@mindbowser_inc/ui-feedback-plugin 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
- var K = Object.defineProperty;
2
- var J = (e, t, r) => t in e ? K(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r;
3
- var H = (e, t, r) => J(e, typeof t != "symbol" ? t + "" : t, r);
4
- const O = /* @__PURE__ */ new Set();
5
- let T = {
1
+ var me = Object.defineProperty;
2
+ var ge = (e, t, r) => t in e ? me(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r;
3
+ var O = (e, t, r) => ge(e, typeof t != "symbol" ? t + "" : t, r);
4
+ const I = /* @__PURE__ */ new Set();
5
+ let R = {
6
6
  active: !1,
7
7
  userName: null,
8
8
  comments: [],
@@ -12,24 +12,24 @@ let T = {
12
12
  filter: "open",
13
13
  config: {}
14
14
  };
15
- function L() {
16
- return T;
15
+ function A() {
16
+ return R;
17
17
  }
18
- function y(e) {
19
- T = { ...T, ...e }, O.forEach((t) => t(T));
18
+ function k(e) {
19
+ R = { ...R, ...e }, I.forEach((t) => t(R));
20
20
  }
21
- function _(e) {
22
- return O.add(e), () => O.delete(e);
21
+ function oe(e) {
22
+ return I.add(e), () => I.delete(e);
23
23
  }
24
- function X() {
25
- if (T.userName) return T.userName;
24
+ function re() {
25
+ if (R.userName) return R.userName;
26
26
  const e = localStorage.getItem("ufp_user_name");
27
- return e ? (y({ userName: e }), e) : null;
27
+ return e ? (k({ userName: e }), e) : null;
28
28
  }
29
- function Y(e) {
30
- localStorage.setItem("ufp_user_name", e), y({ userName: e });
29
+ function ie(e) {
30
+ localStorage.setItem("ufp_user_name", e), k({ userName: e });
31
31
  }
32
- function I(e) {
32
+ function Q(e) {
33
33
  try {
34
34
  const t = new URL(e);
35
35
  return t.origin + t.pathname;
@@ -37,7 +37,7 @@ function I(e) {
37
37
  return e;
38
38
  }
39
39
  }
40
- function Q(e) {
40
+ function xe(e) {
41
41
  return `
42
42
  :host {
43
43
  --ufp-primary: ${e};
@@ -592,13 +592,13 @@ function Q(e) {
592
592
  }
593
593
  `;
594
594
  }
595
- const Z = {
595
+ const ye = {
596
596
  "bottom-right": { bottom: "24px", right: "24px" },
597
597
  "bottom-left": { bottom: "24px", left: "24px" },
598
598
  "top-right": { top: "24px", right: "24px" },
599
599
  "top-left": { top: "24px", left: "24px" }
600
- }, $ = "#6366f1", U = "#4f46e5", j = "#4338ca";
601
- function ee(e, t, r, n) {
600
+ }, q = "#6366f1", Z = "#4f46e5", ee = "#4338ca";
601
+ function ve(e, t, r, n) {
602
602
  const o = document.createElement("div");
603
603
  Object.assign(o.style, {
604
604
  position: "fixed",
@@ -609,43 +609,43 @@ function ee(e, t, r, n) {
609
609
  boxShadow: "0 4px 20px rgba(0,0,0,0.28), 0 1px 4px rgba(0,0,0,0.16)",
610
610
  overflow: "hidden",
611
611
  userSelect: "none",
612
- ...Z[t]
612
+ ...ye[t]
613
613
  });
614
614
  const a = document.createElement("button");
615
- a.id = "ufp-btn-annotate", a.title = "Add feedback annotation", D(a), a.style.padding = "10px 16px 10px 14px", a.innerHTML = `
615
+ a.id = "ufp-btn-annotate", a.title = "Add feedback annotation", te(a), a.style.padding = "10px 16px 10px 14px", a.innerHTML = `
616
616
  <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" style="flex-shrink:0">
617
617
  <path d="M12 20h9"/>
618
618
  <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/>
619
619
  </svg>
620
620
  <span id="ufp-annotate-label" style="font-size:14px;font-weight:600;">Feedback</span>
621
621
  `;
622
- const d = document.createElement("div");
623
- d.style.cssText = "width:1px;background:rgba(255,255,255,0.3);flex-shrink:0;";
624
- const l = document.createElement("button");
625
- l.id = "ufp-btn-panel", l.title = "View all comments", D(l), l.style.padding = "10px 14px", l.innerHTML = `
622
+ const p = document.createElement("div");
623
+ p.style.cssText = "width:1px;background:rgba(255,255,255,0.3);flex-shrink:0;";
624
+ const c = document.createElement("button");
625
+ c.id = "ufp-btn-panel", c.title = "View all comments", te(c), c.style.padding = "10px 14px", c.innerHTML = `
626
626
  <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" style="flex-shrink:0">
627
627
  <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
628
628
  </svg>
629
629
  <span id="ufp-panel-count" style="font-size:13px;font-weight:600;min-width:8px;text-align:center;"></span>
630
- `, o.appendChild(a), o.appendChild(d), o.appendChild(l), e.appendChild(o);
631
- const p = o.querySelector("#ufp-annotate-label"), s = o.querySelector("#ufp-panel-count");
632
- return a.addEventListener("click", (i) => {
633
- if (i.stopPropagation(), o._dragged) return;
634
- const u = L();
635
- u.active || (u.panelOpen ? (n(), setTimeout(r, 220)) : r());
636
- }), l.addEventListener("click", (i) => {
637
- i.stopPropagation(), !o._dragged && (L().active || n());
638
- }), P(a, () => (L().panelOpen, $), U), P(l, () => L().panelOpen ? j : $, U), _((i) => {
639
- const u = i.comments.filter((w) => !w.resolved).length;
640
- s.textContent = u > 0 ? String(u) : "", i.active ? (p.textContent = "Drawing…", a.style.opacity = "0.65", l.style.opacity = "0.65", a.style.pointerEvents = "none", l.style.pointerEvents = "none") : (p.textContent = "Feedback", a.style.opacity = "1", l.style.opacity = "1", a.style.pointerEvents = "auto", l.style.pointerEvents = "auto"), l.style.background = i.panelOpen ? j : $;
641
- }), te(o), o;
630
+ `, o.appendChild(a), o.appendChild(p), o.appendChild(c), e.appendChild(o);
631
+ const i = o.querySelector("#ufp-annotate-label"), l = o.querySelector("#ufp-panel-count");
632
+ return a.addEventListener("click", (s) => {
633
+ if (s.stopPropagation(), o._dragged) return;
634
+ const h = A();
635
+ h.active || (h.panelOpen ? (n(), setTimeout(r, 220)) : r());
636
+ }), c.addEventListener("click", (s) => {
637
+ s.stopPropagation(), !o._dragged && (A().active || n());
638
+ }), ne(a, () => (A().panelOpen, q), Z), ne(c, () => A().panelOpen ? ee : q, Z), oe((s) => {
639
+ const h = s.comments.filter((v) => !v.resolved).length;
640
+ l.textContent = h > 0 ? String(h) : "", s.active ? (i.textContent = "Drawing…", a.style.opacity = "0.65", c.style.opacity = "0.65", a.style.pointerEvents = "none", c.style.pointerEvents = "none") : (i.textContent = "Feedback", a.style.opacity = "1", c.style.opacity = "1", a.style.pointerEvents = "auto", c.style.pointerEvents = "auto"), c.style.background = s.panelOpen ? ee : q;
641
+ }), be(o), o;
642
642
  }
643
- function D(e) {
643
+ function te(e) {
644
644
  Object.assign(e.style, {
645
645
  display: "flex",
646
646
  alignItems: "center",
647
647
  gap: "7px",
648
- background: $,
648
+ background: q,
649
649
  color: "#ffffff",
650
650
  border: "none",
651
651
  cursor: "pointer",
@@ -654,32 +654,32 @@ function D(e) {
654
654
  transition: "background 0.12s"
655
655
  });
656
656
  }
657
- function P(e, t, r) {
657
+ function ne(e, t, r) {
658
658
  e.addEventListener("mouseenter", () => {
659
659
  e.style.background = r;
660
660
  }), e.addEventListener("mouseleave", () => {
661
661
  e.style.background = t();
662
662
  });
663
663
  }
664
- function te(e) {
664
+ function be(e) {
665
665
  let t = 0, r = 0, n = 0, o = 0;
666
666
  e.addEventListener("pointerdown", (a) => {
667
667
  if (a.button !== 0) return;
668
668
  t = a.clientX, r = a.clientY;
669
- const d = e.getBoundingClientRect();
670
- n = d.left, o = d.top, e._dragged = !1;
671
- const l = (s) => {
672
- const i = s.clientX - t, u = s.clientY - r;
673
- !e._dragged && Math.sqrt(i * i + u * u) < 5 || (e._dragged = !0, e.style.left = `${Math.max(0, Math.min(window.innerWidth - e.offsetWidth, n + i))}px`, e.style.top = `${Math.max(0, Math.min(window.innerHeight - e.offsetHeight, o + u))}px`, e.style.right = "auto", e.style.bottom = "auto");
674
- }, p = () => {
675
- document.removeEventListener("pointermove", l), document.removeEventListener("pointerup", p), requestAnimationFrame(() => {
669
+ const p = e.getBoundingClientRect();
670
+ n = p.left, o = p.top, e._dragged = !1;
671
+ const c = (l) => {
672
+ const s = l.clientX - t, h = l.clientY - r;
673
+ !e._dragged && Math.sqrt(s * s + h * h) < 5 || (e._dragged = !0, e.style.left = `${Math.max(0, Math.min(window.innerWidth - e.offsetWidth, n + s))}px`, e.style.top = `${Math.max(0, Math.min(window.innerHeight - e.offsetHeight, o + h))}px`, e.style.right = "auto", e.style.bottom = "auto");
674
+ }, i = () => {
675
+ document.removeEventListener("pointermove", c), document.removeEventListener("pointerup", i), requestAnimationFrame(() => {
676
676
  e._dragged = !1;
677
677
  });
678
678
  };
679
- document.addEventListener("pointermove", l), document.addEventListener("pointerup", p);
679
+ document.addEventListener("pointermove", c), document.addEventListener("pointerup", i);
680
680
  });
681
681
  }
682
- function ne(e) {
682
+ function we(e) {
683
683
  return new Promise((t) => {
684
684
  const r = document.createElement("div");
685
685
  r.className = "ufp-modal-backdrop", r.innerHTML = `
@@ -704,20 +704,20 @@ function ne(e) {
704
704
  o.disabled = n.value.trim().length === 0;
705
705
  });
706
706
  const a = () => {
707
- const d = n.value.trim();
708
- d && (Y(d), r.remove(), t(d));
707
+ const p = n.value.trim();
708
+ p && (ie(p), r.remove(), t(p));
709
709
  };
710
- o.addEventListener("click", a), n.addEventListener("keydown", (d) => {
711
- d.key === "Enter" && a();
710
+ o.addEventListener("click", a), n.addEventListener("keydown", (p) => {
711
+ p.key === "Enter" && a();
712
712
  }), requestAnimationFrame(() => n.focus());
713
713
  });
714
714
  }
715
- const C = "#6366f1", M = 3;
716
- function z(e, t = 1, r = 1) {
715
+ const L = "#6366f1", z = 3;
716
+ function B(e, t = 1, r = 1) {
717
717
  switch (e.type) {
718
718
  case "pen": {
719
719
  const n = document.createElementNS("http://www.w3.org/2000/svg", "path");
720
- return n.setAttribute("d", oe(e.points, t, r)), n.setAttribute("stroke", e.color), n.setAttribute("stroke-width", String(e.width * Math.min(t, r))), n.setAttribute("fill", "none"), n.setAttribute("stroke-linecap", "round"), n.setAttribute("stroke-linejoin", "round"), n;
720
+ return n.setAttribute("d", Ee(e.points, t, r)), n.setAttribute("stroke", e.color), n.setAttribute("stroke-width", String(e.width * Math.min(t, r))), n.setAttribute("fill", "none"), n.setAttribute("stroke-linecap", "round"), n.setAttribute("stroke-linejoin", "round"), n;
721
721
  }
722
722
  case "rect": {
723
723
  const n = document.createElementNS("http://www.w3.org/2000/svg", "rect");
@@ -733,19 +733,19 @@ function z(e, t = 1, r = 1) {
733
733
  }
734
734
  }
735
735
  }
736
- function re(e, t, r, n) {
736
+ function ke(e, t, r, n) {
737
737
  const o = r / t.pageWidth, a = n / t.pageHeight;
738
738
  e.innerHTML = "";
739
- const d = document.createElementNS("http://www.w3.org/2000/svg", "defs");
740
- d.innerHTML = `
739
+ const p = document.createElementNS("http://www.w3.org/2000/svg", "defs");
740
+ p.innerHTML = `
741
741
  <marker id="ufp-arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
742
- <polygon points="0 0, 10 3.5, 0 7" fill="${C}" />
742
+ <polygon points="0 0, 10 3.5, 0 7" fill="${L}" />
743
743
  </marker>
744
- `, e.appendChild(d), t.shapes.forEach((l) => {
745
- e.appendChild(z(l, o, a));
744
+ `, e.appendChild(p), t.shapes.forEach((c) => {
745
+ e.appendChild(B(c, o, a));
746
746
  });
747
747
  }
748
- function oe(e, t, r) {
748
+ function Ee(e, t, r) {
749
749
  if (e.length === 0) return "";
750
750
  if (e.length === 1) {
751
751
  const o = e[0];
@@ -756,54 +756,95 @@ function oe(e, t, r) {
756
756
  n += ` L ${e[o].x * t} ${e[o].y * r}`;
757
757
  return n;
758
758
  }
759
- function ie(e) {
759
+ function Se(e, t, r) {
760
+ if (window.scrollX === 0 && window.scrollY === 0) return null;
761
+ const n = window.innerWidth, o = window.innerHeight, a = document.elementsFromPoint(e, t).find((p) => p !== r);
762
+ for (let p = a ?? null; p && p !== document.documentElement; p = p.parentElement) {
763
+ const c = getComputedStyle(p);
764
+ if (c.position !== "fixed" && c.position !== "sticky") continue;
765
+ const i = p.getBoundingClientRect();
766
+ if (!(i.width >= n * 0.9 && i.height >= o * 0.9)) {
767
+ if (c.position === "sticky") {
768
+ if (i.top < o * 0.25) return "top";
769
+ continue;
770
+ }
771
+ return o - i.bottom < i.top ? "bottom" : "top";
772
+ }
773
+ }
774
+ return null;
775
+ }
776
+ function Ce(e, t, r) {
777
+ if (!t && !r) return e;
778
+ switch (e.type) {
779
+ case "pen":
780
+ return { ...e, points: e.points.map((n) => ({ x: n.x + t, y: n.y + r })) };
781
+ case "rect":
782
+ return { ...e, x: e.x + t, y: e.y + r };
783
+ case "circle":
784
+ return { ...e, cx: e.cx + t, cy: e.cy + r };
785
+ case "arrow":
786
+ return { ...e, x1: e.x1 + t, y1: e.y1 + r, x2: e.x2 + t, y2: e.y2 + r };
787
+ }
788
+ }
789
+ function Le(e) {
760
790
  return new Promise((t) => {
761
791
  let r = "pen";
762
792
  const n = [];
763
- let o = !1, a = 0, d = 0, l = [], p = null, s = null;
764
- const i = document.createElement("div");
765
- i.style.cssText = "position:fixed;inset:0;z-index:2147483645;cursor:crosshair;background:rgba(99,102,241,0.04);";
793
+ let o = !1, a = 0, p = 0, c = [], i = null, l = null, s = !1, h = 0;
794
+ const v = [];
795
+ let m = null;
796
+ const b = () => m ? { x: 0, y: 0 } : { x: window.scrollX, y: window.scrollY }, x = document.createElement("div");
797
+ x.style.cssText = "position:fixed;inset:0;z-index:2147483645;cursor:crosshair;background:rgba(99,102,241,0.04);touch-action:none;user-select:none;-webkit-user-select:none;";
766
798
  const u = document.createElementNS("http://www.w3.org/2000/svg", "svg");
767
799
  u.style.cssText = "position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible;", u.innerHTML = `
768
800
  <defs>
769
801
  <marker id="ufp-arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
770
- <polygon points="0 0, 10 3.5, 0 7" fill="${C}" />
802
+ <polygon points="0 0, 10 3.5, 0 7" fill="${L}" />
771
803
  </marker>
772
804
  </defs>
773
- `, i.appendChild(u);
774
- function w() {
775
- i.remove(), b.remove(), s == null || s.remove();
776
- }
777
- function h() {
778
- const m = u.querySelector("defs");
779
- u.innerHTML = "", u.appendChild(m), n.forEach((x) => u.appendChild(z(x)));
780
- }
781
- function k() {
782
- let m = 0, x = 0, g = 0;
783
- return n.forEach((c) => {
784
- if (c.type === "pen" && c.points.length) {
785
- const v = c.points[c.points.length - 1];
786
- m += v.x, x += v.y, g++;
787
- } else c.type === "rect" ? (m += c.x + c.width, x += c.y + c.height, g++) : c.type === "circle" ? (m += c.cx, x += c.cy, g++) : c.type === "arrow" && (m += c.x2, x += c.y2, g++);
805
+ `, x.appendChild(u);
806
+ const w = document.createElementNS("http://www.w3.org/2000/svg", "g");
807
+ u.appendChild(w);
808
+ const C = document.createElementNS("http://www.w3.org/2000/svg", "g");
809
+ u.appendChild(C);
810
+ const N = () => w.setAttribute("transform", `translate(${-window.scrollX} ${-window.scrollY})`);
811
+ N(), window.addEventListener("scroll", N, { passive: !0 });
812
+ function H() {
813
+ window.removeEventListener("scroll", N);
814
+ for (const d of X) window.removeEventListener(d, K, !0);
815
+ for (const d of Y) window.removeEventListener(d, G, !0);
816
+ x.remove(), P.remove(), l == null || l.remove();
817
+ }
818
+ function j() {
819
+ w.innerHTML = "", C.innerHTML = "", n.forEach((d, g) => (v[g] ? C : w).appendChild(B(d)));
820
+ }
821
+ function _() {
822
+ let d = 0, g = 0, y = 0;
823
+ return n.forEach((f, E) => {
824
+ const S = v[E] ? 0 : window.scrollX, M = v[E] ? 0 : window.scrollY;
825
+ if (f.type === "pen" && f.points.length) {
826
+ const $ = f.points[f.points.length - 1];
827
+ d += $.x - S, g += $.y - M, y++;
828
+ } else f.type === "rect" ? (d += f.x + f.width - S, g += f.y + f.height - M, y++) : f.type === "circle" ? (d += f.cx - S, g += f.cy - M, y++) : f.type === "arrow" && (d += f.x2 - S, g += f.y2 - M, y++);
788
829
  }), {
789
- anchorX: g > 0 ? m / g - window.scrollX : window.innerWidth / 2,
790
- anchorY: g > 0 ? x / g - window.scrollY : window.innerHeight / 2
830
+ anchorX: y > 0 ? d / y : window.innerWidth / 2,
831
+ anchorY: y > 0 ? g / y : window.innerHeight / 2
791
832
  };
792
833
  }
793
- function E() {
794
- i.style.pointerEvents = "none", b.style.pointerEvents = "none", f();
834
+ function ae() {
835
+ s = !0, o = !1, x.style.pointerEvents = "none", P.style.pointerEvents = "none", se();
795
836
  }
796
- function f() {
797
- s && s.remove();
798
- const { anchorX: m, anchorY: x } = k(), g = 300, c = 12;
799
- let v = m + c, S = x + c;
800
- v + g > window.innerWidth - c && (v = m - g - c), v < c && (v = c), S + 210 > window.innerHeight - c && (S = window.innerHeight - 210 - c), S < c && (S = c), s = document.createElement("div"), s.style.cssText = `
801
- position:fixed;left:${v}px;top:${S}px;z-index:2147483647;
802
- background:#fff;border-radius:12px;padding:16px;width:${g}px;
837
+ function se() {
838
+ l && l.remove();
839
+ const { anchorX: d, anchorY: g } = _(), y = 300, f = 12;
840
+ let E = d + f, S = g + f;
841
+ E + y > window.innerWidth - f && (E = d - y - f), E < f && (E = f), S + 210 > window.innerHeight - f && (S = window.innerHeight - 210 - f), S < f && (S = f), l = document.createElement("div"), l.style.cssText = `
842
+ position:fixed;left:${E}px;top:${S}px;z-index:2147483647;
843
+ background:#fff;border-radius:12px;padding:16px;width:${y}px;
803
844
  box-shadow:0 4px 24px rgba(0,0,0,0.18);
804
845
  display:flex;flex-direction:column;gap:10px;
805
846
  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
806
- `, s.innerHTML = `
847
+ `, l.innerHTML = `
807
848
  <div style="font-size:13px;font-weight:600;color:#374151;">Add your comment</div>
808
849
  <textarea id="ufp-inline-text" placeholder="Describe your feedback…" rows="3"
809
850
  style="width:100%;padding:9px 12px;border:1.5px solid #e5e7eb;border-radius:8px;
@@ -819,66 +860,94 @@ function ie(e) {
819
860
  font-family:inherit;cursor:pointer;background:#6366f1;border:none;
820
861
  color:#fff;opacity:0.45;transition:opacity 0.15s;">Submit</button>
821
862
  </div>
822
- `, e.appendChild(s);
823
- const B = s.querySelector("#ufp-inline-text"), R = s.querySelector("#ufp-popup-submit"), G = s.querySelector("#ufp-popup-cancel");
824
- B.addEventListener("input", () => {
825
- const N = B.value.trim().length > 0;
826
- R.disabled = !N, R.style.opacity = N ? "1" : "0.45";
827
- }), R.addEventListener("click", () => {
828
- const N = B.value.trim();
829
- if (!N) return;
830
- const V = k();
831
- w(), t({ shapes: [...n], text: N, ...V });
832
- }), G.addEventListener("click", () => {
833
- w(), t(null);
834
- }), requestAnimationFrame(() => B.focus());
835
- }
836
- const b = ae(
863
+ `, e.appendChild(l);
864
+ const M = l.querySelector("#ufp-inline-text"), $ = l.querySelector("#ufp-popup-submit"), de = l.querySelector("#ufp-popup-cancel");
865
+ M.addEventListener("input", () => {
866
+ const W = M.value.trim().length > 0;
867
+ $.disabled = !W, $.style.opacity = W ? "1" : "0.45";
868
+ }), $.addEventListener("click", () => {
869
+ const W = M.value.trim();
870
+ if (!W) return;
871
+ const ue = _();
872
+ H();
873
+ const fe = n.map((J, he) => v[he] ? J : Ce(J, -window.scrollX, -window.scrollY));
874
+ t({ shapes: fe, text: W, ...ue });
875
+ }), de.addEventListener("click", () => {
876
+ H(), t(null);
877
+ }), requestAnimationFrame(() => M.focus());
878
+ }
879
+ const P = Me(
837
880
  e,
838
- (m) => {
839
- r = m;
881
+ (d) => {
882
+ r = d;
840
883
  },
841
884
  () => {
842
- w(), t(null);
885
+ H(), t(null);
843
886
  },
844
887
  () => {
845
- n.length !== 0 && (s && (s.remove(), s = null, i.style.pointerEvents = "auto", b.style.pointerEvents = "auto"), n.pop(), h());
888
+ n.length !== 0 && (l && (l.remove(), l = null, x.style.pointerEvents = "auto", P.style.pointerEvents = "auto", s = !1), n.pop(), v.pop(), j());
846
889
  }
847
890
  );
848
- e.appendChild(i), i.addEventListener("pointerdown", (m) => {
849
- m.button === 0 && (i.setPointerCapture(m.pointerId), o = !0, a = m.clientX + window.scrollX, d = m.clientY + window.scrollY, r === "pen" && (l = [{ x: a, y: d }]));
850
- }), i.addEventListener("pointermove", (m) => {
891
+ e.appendChild(x);
892
+ const X = ["pointerdown", "pointermove", "pointerup", "pointercancel"], Y = ["mousedown", "mouseup", "click", "dblclick", "auxclick", "touchstart", "touchmove", "contextmenu", "dragstart", "selectstart"];
893
+ function V(d) {
894
+ const g = d.composedPath();
895
+ return g.includes(e.host) && !g.includes(x);
896
+ }
897
+ function G(d) {
898
+ V(d) || s && performance.now() > h || (d.preventDefault(), d.stopPropagation());
899
+ }
900
+ function K(d) {
901
+ if (s || V(d)) return;
902
+ d.stopPropagation();
903
+ const g = d;
904
+ d.type === "pointerdown" ? le(g) : d.type === "pointermove" ? pe(g) : d.type === "pointerup" ? ce(g) : d.type === "pointercancel" && (o = !1, i && (i.remove(), i = null));
905
+ }
906
+ function le(d) {
907
+ if (d.button === 0) {
908
+ d.preventDefault();
909
+ try {
910
+ x.setPointerCapture(d.pointerId);
911
+ } catch {
912
+ }
913
+ o = !0, m = Se(d.clientX, d.clientY, e.host), a = d.clientX + b().x, p = d.clientY + b().y, r === "pen" && (c = [{ x: a, y: p }]);
914
+ }
915
+ }
916
+ function pe(d) {
851
917
  if (!o) return;
852
- const x = m.clientX + window.scrollX, g = m.clientY + window.scrollY;
853
- if (p && p.remove(), r === "pen")
854
- l.push({ x, y: g }), p = z({ type: "pen", points: [...l], color: C, width: M });
918
+ const g = d.clientX + b().x, y = d.clientY + b().y;
919
+ if (i && i.remove(), r === "pen")
920
+ c.push({ x: g, y }), i = B({ type: "pen", points: [...c], color: L, width: z });
855
921
  else if (r === "rect") {
856
- const c = { type: "rect", x: Math.min(a, x), y: Math.min(d, g), width: Math.abs(x - a), height: Math.abs(g - d), color: C, strokeWidth: M };
857
- p = z(c);
922
+ const f = { type: "rect", x: Math.min(a, g), y: Math.min(p, y), width: Math.abs(g - a), height: Math.abs(y - p), color: L, strokeWidth: z };
923
+ i = B(f);
858
924
  } else if (r === "circle") {
859
- const c = Math.abs(x - a) / 2, v = Math.abs(g - d) / 2, S = { type: "circle", cx: Math.min(a, x) + c, cy: Math.min(d, g) + v, rx: c, ry: v, color: C, strokeWidth: M };
860
- p = z(S);
861
- } else r === "arrow" && (p = z({ type: "arrow", x1: a, y1: d, x2: x, y2: g, color: C, width: M }));
862
- p && u.appendChild(p);
863
- }), i.addEventListener("pointerup", (m) => {
925
+ const f = Math.abs(g - a) / 2, E = Math.abs(y - p) / 2, S = { type: "circle", cx: Math.min(a, g) + f, cy: Math.min(p, y) + E, rx: f, ry: E, color: L, strokeWidth: z };
926
+ i = B(S);
927
+ } else r === "arrow" && (i = B({ type: "arrow", x1: a, y1: p, x2: g, y2: y, color: L, width: z }));
928
+ i && (m ? C : w).appendChild(i);
929
+ }
930
+ function ce(d) {
864
931
  if (!o) return;
865
- o = !1, p && (p.remove(), p = null);
866
- const x = m.clientX + window.scrollX, g = m.clientY + window.scrollY;
867
- let c = !1;
868
- if (r === "pen" && l.length > 1)
869
- n.push({ type: "pen", points: [...l], color: C, width: M }), l = [], c = !0;
932
+ h = performance.now() + 400, o = !1, i && (i.remove(), i = null);
933
+ const g = d.clientX + b().x, y = d.clientY + b().y;
934
+ let f = !1;
935
+ if (r === "pen" && c.length > 1)
936
+ n.push({ type: "pen", points: [...c], color: L, width: z }), c = [], f = !0;
870
937
  else if (r === "rect") {
871
- const v = Math.abs(x - a), S = Math.abs(g - d);
872
- v > 4 && S > 4 && (n.push({ type: "rect", x: Math.min(a, x), y: Math.min(d, g), width: v, height: S, color: C, strokeWidth: M }), c = !0);
938
+ const E = Math.abs(g - a), S = Math.abs(y - p);
939
+ E > 4 && S > 4 && (n.push({ type: "rect", x: Math.min(a, g), y: Math.min(p, y), width: E, height: S, color: L, strokeWidth: z }), f = !0);
873
940
  } else if (r === "circle") {
874
- const v = Math.abs(x - a) / 2, S = Math.abs(g - d) / 2;
875
- v > 4 && S > 4 && (n.push({ type: "circle", cx: Math.min(a, x) + v, cy: Math.min(d, g) + S, rx: v, ry: S, color: C, strokeWidth: M }), c = !0);
876
- } else r === "arrow" && Math.sqrt((x - a) ** 2 + (g - d) ** 2) > 10 && (n.push({ type: "arrow", x1: a, y1: d, x2: x, y2: g, color: C, width: M }), c = !0);
877
- c && (h(), E());
878
- });
941
+ const E = Math.abs(g - a) / 2, S = Math.abs(y - p) / 2;
942
+ E > 4 && S > 4 && (n.push({ type: "circle", cx: Math.min(a, g) + E, cy: Math.min(p, y) + S, rx: E, ry: S, color: L, strokeWidth: z }), f = !0);
943
+ } else r === "arrow" && Math.sqrt((g - a) ** 2 + (y - p) ** 2) > 10 && (n.push({ type: "arrow", x1: a, y1: p, x2: g, y2: y, color: L, width: z }), f = !0);
944
+ f && (v.push(m), j(), ae());
945
+ }
946
+ for (const d of X) window.addEventListener(d, K, { capture: !0, passive: !1 });
947
+ for (const d of Y) window.addEventListener(d, G, { capture: !0, passive: !1 });
879
948
  });
880
949
  }
881
- function ae(e, t, r, n) {
950
+ function Me(e, t, r, n) {
882
951
  const o = document.createElement("div");
883
952
  return o.className = "ufp-toolbar", o.innerHTML = `
884
953
  <span class="ufp-toolbar-label">Draw to annotate</span>
@@ -909,11 +978,11 @@ function ae(e, t, r, n) {
909
978
  <button class="ufp-btn ufp-btn-ghost" id="ufp-cancel-btn">Cancel</button>
910
979
  `, o.querySelectorAll("[data-tool]").forEach((a) => {
911
980
  a.addEventListener("click", () => {
912
- o.querySelectorAll("[data-tool]").forEach((d) => d.classList.remove("active")), a.classList.add("active"), t(a.dataset.tool);
981
+ o.querySelectorAll("[data-tool]").forEach((p) => p.classList.remove("active")), a.classList.add("active"), t(a.dataset.tool);
913
982
  });
914
983
  }), o.querySelector("#ufp-undo-btn").addEventListener("click", n), o.querySelector("#ufp-cancel-btn").addEventListener("click", r), e.appendChild(o), o;
915
984
  }
916
- function W(e) {
985
+ function U(e) {
917
986
  const t = Date.now() - e, r = Math.floor(t / 1e3);
918
987
  if (r < 60) return "just now";
919
988
  const n = Math.floor(r / 60);
@@ -923,39 +992,39 @@ function W(e) {
923
992
  const a = Math.floor(o / 24);
924
993
  return a < 7 ? `${a}d ago` : new Date(e).toLocaleDateString();
925
994
  }
926
- function F(e) {
995
+ function D(e) {
927
996
  return e.split(" ").filter(Boolean).slice(0, 2).map((t) => t[0].toUpperCase()).join("");
928
997
  }
929
- function A(e) {
998
+ function T(e) {
930
999
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
931
1000
  }
932
- function se(e, t, r, n) {
1001
+ function Ae(e, t, r, n) {
933
1002
  let o = [...t];
934
1003
  const a = document.createElement("div");
935
1004
  a.className = "ufp-replies";
936
- const d = (h) => {
937
- const k = a.querySelector(".ufp-replies-list");
938
- k && k.remove();
939
- const E = document.createElement("div");
940
- if (E.className = "ufp-replies-list", E.style.cssText = "display:flex;flex-direction:column;gap:12px;", h.length > 0) {
941
- const f = document.createElement("div");
942
- f.className = "ufp-replies-title", f.textContent = `${h.length} ${h.length === 1 ? "Reply" : "Replies"}`, E.appendChild(f), h.forEach((b) => {
943
- const m = document.createElement("div");
944
- m.className = "ufp-reply", m.innerHTML = `
1005
+ const p = (m) => {
1006
+ const b = a.querySelector(".ufp-replies-list");
1007
+ b && b.remove();
1008
+ const x = document.createElement("div");
1009
+ if (x.className = "ufp-replies-list", x.style.cssText = "display:flex;flex-direction:column;gap:12px;", m.length > 0) {
1010
+ const u = document.createElement("div");
1011
+ u.className = "ufp-replies-title", u.textContent = `${m.length} ${m.length === 1 ? "Reply" : "Replies"}`, x.appendChild(u), m.forEach((w) => {
1012
+ const C = document.createElement("div");
1013
+ C.className = "ufp-reply", C.innerHTML = `
945
1014
  <div class="ufp-reply-meta">
946
- <div class="ufp-avatar" style="width:22px;height:22px;font-size:10px;">${F(b.authorName)}</div>
947
- <span class="ufp-reply-author">${A(b.authorName)}</span>
948
- <span class="ufp-reply-time">${W(b.createdAt)}</span>
1015
+ <div class="ufp-avatar" style="width:22px;height:22px;font-size:10px;">${D(w.authorName)}</div>
1016
+ <span class="ufp-reply-author">${T(w.authorName)}</span>
1017
+ <span class="ufp-reply-time">${U(w.createdAt)}</span>
949
1018
  </div>
950
- <div class="ufp-reply-text">${A(b.text)}</div>
951
- `, E.appendChild(m);
1019
+ <div class="ufp-reply-text">${T(w.text)}</div>
1020
+ `, x.appendChild(C);
952
1021
  });
953
1022
  }
954
- a.insertBefore(E, a.querySelector(".ufp-reply-input-area"));
955
- }, l = document.createElement("div");
956
- l.className = "ufp-reply-input-area";
957
- const p = X();
958
- l.innerHTML = `
1023
+ a.insertBefore(x, a.querySelector(".ufp-reply-input-area"));
1024
+ }, c = document.createElement("div");
1025
+ c.className = "ufp-reply-input-area";
1026
+ const i = re();
1027
+ c.innerHTML = `
959
1028
  <div class="ufp-reply-name-row">
960
1029
  <span class="ufp-reply-name-label">Replying as:</span>
961
1030
  <input
@@ -963,7 +1032,7 @@ function se(e, t, r, n) {
963
1032
  id="ufp-reply-name"
964
1033
  type="text"
965
1034
  placeholder="Your name"
966
- value="${A(p ?? "")}"
1035
+ value="${T(i ?? "")}"
967
1036
  style="flex:1;"
968
1037
  />
969
1038
  </div>
@@ -978,32 +1047,32 @@ function se(e, t, r, n) {
978
1047
  <button class="ufp-btn ufp-btn-primary" id="ufp-reply-submit" disabled style="font-size:13px;padding:7px 14px;">Reply</button>
979
1048
  </div>
980
1049
  `;
981
- const s = l.querySelector("#ufp-reply-name"), i = l.querySelector("#ufp-reply-text"), u = l.querySelector("#ufp-reply-submit"), w = () => {
982
- u.disabled = s.value.trim().length === 0 || i.value.trim().length === 0;
1050
+ const l = c.querySelector("#ufp-reply-name"), s = c.querySelector("#ufp-reply-text"), h = c.querySelector("#ufp-reply-submit"), v = () => {
1051
+ h.disabled = l.value.trim().length === 0 || s.value.trim().length === 0;
983
1052
  };
984
- return s.addEventListener("input", w), i.addEventListener("input", w), u.addEventListener("click", async () => {
985
- const h = s.value.trim(), k = i.value.trim();
986
- if (!h || !k) return;
987
- u.disabled = !0, u.textContent = "Sending…", Y(h);
988
- const E = {
1053
+ return l.addEventListener("input", v), s.addEventListener("input", v), h.addEventListener("click", async () => {
1054
+ const m = l.value.trim(), b = s.value.trim();
1055
+ if (!m || !b) return;
1056
+ h.disabled = !0, h.textContent = "Sending…", ie(m);
1057
+ const x = {
989
1058
  commentId: e,
990
- authorName: h,
991
- text: k,
1059
+ authorName: m,
1060
+ text: b,
992
1061
  createdAt: Date.now()
993
1062
  };
994
1063
  try {
995
- const b = { id: await r.addReply(E), ...E };
996
- i.value = "", u.textContent = "Reply", u.disabled = !0, o = [...o, b], d(o), n(b);
997
- } catch (f) {
998
- console.error("[ui-feedback-plugin] Failed to save reply:", f), u.disabled = !1, u.textContent = "Reply";
1064
+ const w = { id: await r.addReply(x), ...x };
1065
+ s.value = "", h.textContent = "Reply", h.disabled = !0, o = [...o, w], p(o), n(w);
1066
+ } catch (u) {
1067
+ console.error("[ui-feedback-plugin] Failed to save reply:", u), h.disabled = !1, h.textContent = "Reply";
999
1068
  }
1000
- }), a.appendChild(l), d(o), a.addReply = (h) => {
1001
- o = [...o, h], d(o);
1069
+ }), a.appendChild(c), p(o), a.addReply = (m) => {
1070
+ o = [...o, m], p(o);
1002
1071
  }, a;
1003
1072
  }
1004
- function le(e, t, r, n, o, a, d, l) {
1005
- const p = document.createElement("div");
1006
- p.className = "ufp-detail", p.innerHTML = `
1073
+ function ze(e, t, r, n, o, a, p, c) {
1074
+ const i = document.createElement("div");
1075
+ i.className = "ufp-detail", i.innerHTML = `
1007
1076
  <div class="ufp-detail-image-area">
1008
1077
  <div class="ufp-detail-image-wrap">
1009
1078
  <img class="ufp-detail-screenshot" src="${t.screenshotUrl}" alt="Page screenshot" />
@@ -1034,59 +1103,59 @@ function le(e, t, r, n, o, a, d, l) {
1034
1103
  <div class="ufp-detail-body">
1035
1104
  <div class="ufp-detail-comment">
1036
1105
  <div class="ufp-comment-meta">
1037
- <div class="ufp-avatar">${F(t.authorName)}</div>
1106
+ <div class="ufp-avatar">${D(t.authorName)}</div>
1038
1107
  <div>
1039
- <div class="ufp-comment-author">${A(t.authorName)}</div>
1040
- <div class="ufp-comment-time">${W(t.createdAt)}</div>
1108
+ <div class="ufp-comment-author">${T(t.authorName)}</div>
1109
+ <div class="ufp-comment-time">${U(t.createdAt)}</div>
1041
1110
  </div>
1042
1111
  </div>
1043
- <div class="ufp-detail-comment-text">${A(t.text)}</div>
1112
+ <div class="ufp-detail-comment-text">${T(t.text)}</div>
1044
1113
  <div class="ufp-detail-actions" id="ufp-detail-actions"></div>
1045
1114
  </div>
1046
1115
  <div id="ufp-detail-replies"></div>
1047
1116
  </div>
1048
1117
  </div>
1049
- `, e.appendChild(p), p.querySelector("#ufp-detail-back").addEventListener("click", () => {
1050
- p.remove(), y({ panelOpen: !0 });
1118
+ `, e.appendChild(i), i.querySelector("#ufp-detail-back").addEventListener("click", () => {
1119
+ i.remove(), k({ panelOpen: !0 });
1051
1120
  });
1052
- const s = p.querySelector("#ufp-detail-actions");
1121
+ const l = i.querySelector("#ufp-detail-actions");
1053
1122
  if (t.resolved) {
1054
- const f = document.createElement("button");
1055
- f.className = "ufp-btn ufp-btn-ghost", f.style.fontSize = "13px", f.innerHTML = `
1123
+ const u = document.createElement("button");
1124
+ u.className = "ufp-btn ufp-btn-ghost", u.style.fontSize = "13px", u.innerHTML = `
1056
1125
  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
1057
1126
  style="vertical-align:middle;margin-right:4px"><polyline points="9 14 4 9 9 4"/><path d="M20 20v-7a4 4 0 0 0-4-4H4"/></svg>
1058
1127
  Reopen
1059
- `, f.addEventListener("click", async () => {
1060
- f.disabled = !0, f.textContent = "Reopening…", await n.updateComment(t.id, { resolved: !1 }), a(t.id), p.remove(), y({ panelOpen: !0 });
1061
- }), s.appendChild(f);
1128
+ `, u.addEventListener("click", async () => {
1129
+ u.disabled = !0, u.textContent = "Reopening…", await n.updateComment(t.id, { resolved: !1 }), a(t.id), i.remove(), k({ panelOpen: !0 });
1130
+ }), l.appendChild(u);
1062
1131
  } else {
1063
- const f = document.createElement("button");
1064
- f.className = "ufp-btn ufp-btn-primary", f.style.fontSize = "13px", f.innerHTML = `
1132
+ const u = document.createElement("button");
1133
+ u.className = "ufp-btn ufp-btn-primary", u.style.fontSize = "13px", u.innerHTML = `
1065
1134
  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"
1066
1135
  style="vertical-align:middle;margin-right:4px"><polyline points="20 6 9 17 4 12"/></svg>
1067
1136
  Mark Resolved
1068
- `, f.addEventListener("click", async () => {
1069
- f.disabled = !0, f.textContent = "Resolving…", await n.updateComment(t.id, { resolved: !0 }), o(t.id), p.remove(), y({ panelOpen: !0 });
1070
- }), s.appendChild(f);
1137
+ `, u.addEventListener("click", async () => {
1138
+ u.disabled = !0, u.textContent = "Resolving…", await n.updateComment(t.id, { resolved: !0 }), o(t.id), i.remove(), k({ panelOpen: !0 });
1139
+ }), l.appendChild(u);
1071
1140
  }
1072
- const i = document.createElement("button");
1073
- i.className = "ufp-btn ufp-btn-ghost", i.style.fontSize = "13px", i.textContent = "Delete", i.addEventListener("click", async () => {
1074
- confirm("Delete this comment and all its replies?") && (i.disabled = !0, await n.deleteComment(t.id), d(t.id), p.remove(), y({ panelOpen: !0 }));
1075
- }), s.appendChild(i);
1076
- const u = p.querySelector("#ufp-detail-replies"), w = se(t.id, r, n, (f) => {
1077
- var b;
1078
- (b = w.addReply) == null || b.call(w, f), l == null || l(f);
1141
+ const s = document.createElement("button");
1142
+ s.className = "ufp-btn ufp-btn-ghost", s.style.fontSize = "13px", s.textContent = "Delete", s.addEventListener("click", async () => {
1143
+ confirm("Delete this comment and all its replies?") && (s.disabled = !0, await n.deleteComment(t.id), p(t.id), i.remove(), k({ panelOpen: !0 }));
1144
+ }), l.appendChild(s);
1145
+ const h = i.querySelector("#ufp-detail-replies"), v = Ae(t.id, r, n, (u) => {
1146
+ var w;
1147
+ (w = v.addReply) == null || w.call(v, u), c == null || c(u);
1079
1148
  });
1080
- u.appendChild(w);
1081
- const h = p.querySelector(".ufp-detail-screenshot"), k = p.querySelector(".ufp-detail-annotation-svg"), E = () => {
1082
- const f = h.clientWidth, b = h.clientHeight;
1083
- k.setAttribute("viewBox", `0 0 ${f} ${b}`), re(k, t.annotation, f, b), k.querySelectorAll("[marker-end]").forEach((m) => {
1084
- m.setAttribute("marker-end", "url(#ufp-detail-arrowhead)");
1149
+ h.appendChild(v);
1150
+ const m = i.querySelector(".ufp-detail-screenshot"), b = i.querySelector(".ufp-detail-annotation-svg"), x = () => {
1151
+ const u = m.clientWidth, w = m.clientHeight;
1152
+ b.setAttribute("viewBox", `0 0 ${u} ${w}`), ke(b, t.annotation, u, w), b.querySelectorAll("[marker-end]").forEach((C) => {
1153
+ C.setAttribute("marker-end", "url(#ufp-detail-arrowhead)");
1085
1154
  });
1086
1155
  };
1087
- h.complete && h.naturalWidth > 0 ? E() : h.addEventListener("load", E);
1156
+ m.complete && m.naturalWidth > 0 ? x() : m.addEventListener("load", x);
1088
1157
  }
1089
- function pe(e, t, r) {
1158
+ function Te(e, t, r) {
1090
1159
  const n = document.createElement("div");
1091
1160
  n.className = "ufp-panel", n.innerHTML = `
1092
1161
  <div class="ufp-panel-header">
@@ -1109,138 +1178,171 @@ function pe(e, t, r) {
1109
1178
  </div>
1110
1179
  <div class="ufp-panel-list" id="ufp-comment-list"></div>
1111
1180
  `, e.appendChild(n), n.querySelector("#ufp-panel-close").addEventListener("click", () => {
1112
- y({ panelOpen: !1 });
1181
+ k({ panelOpen: !1 });
1113
1182
  }), n.querySelector("#ufp-new-feedback").addEventListener("click", () => {
1114
- y({ panelOpen: !1 }), r();
1115
- }), n.querySelectorAll(".ufp-filter-tab").forEach((l) => {
1116
- l.addEventListener("click", () => {
1117
- n.querySelectorAll(".ufp-filter-tab").forEach((p) => p.classList.remove("active")), l.classList.add("active"), y({ filter: l.dataset.filter });
1183
+ k({ panelOpen: !1 }), r();
1184
+ }), n.querySelectorAll(".ufp-filter-tab").forEach((c) => {
1185
+ c.addEventListener("click", () => {
1186
+ n.querySelectorAll(".ufp-filter-tab").forEach((i) => i.classList.remove("active")), c.classList.add("active"), k({ filter: c.dataset.filter });
1118
1187
  });
1119
1188
  });
1120
- const o = n.querySelector("#ufp-comment-list"), a = /* @__PURE__ */ new Map(), d = async (l, p) => {
1121
- const s = l.filter((i) => p === "open" ? !i.resolved : i.resolved);
1122
- if (o.innerHTML = "", s.length === 0) {
1189
+ const o = n.querySelector("#ufp-comment-list"), a = /* @__PURE__ */ new Map(), p = async (c, i) => {
1190
+ const l = c.filter((s) => i === "open" ? !s.resolved : s.resolved);
1191
+ if (o.innerHTML = "", l.length === 0) {
1123
1192
  o.innerHTML = `
1124
1193
  <div class="ufp-panel-empty">
1125
1194
  <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" opacity="0.4">
1126
1195
  <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
1127
1196
  </svg>
1128
- <span>No ${p} comments yet</span>
1129
- ${p === "open" ? '<span style="font-size:12px;color:#9ca3af;">Click <strong>New</strong> to add feedback</span>' : ""}
1197
+ <span>No ${i} comments yet</span>
1198
+ ${i === "open" ? '<span style="font-size:12px;color:#9ca3af;">Click <strong>New</strong> to add feedback</span>' : ""}
1130
1199
  </div>
1131
1200
  `;
1132
1201
  return;
1133
1202
  }
1134
- for (const i of s) {
1135
- if (!a.has(i.id))
1203
+ for (const s of l) {
1204
+ if (!a.has(s.id))
1136
1205
  try {
1137
- a.set(i.id, await t.getReplies(i.id));
1206
+ a.set(s.id, await t.getReplies(s.id));
1138
1207
  } catch {
1139
- a.set(i.id, []);
1208
+ a.set(s.id, []);
1140
1209
  }
1141
1210
  o.appendChild(
1142
- de(i, a, e, t)
1211
+ Ne(s, a, e, t)
1143
1212
  );
1144
1213
  }
1145
1214
  };
1146
- return _(async (l) => {
1147
- n.classList.toggle("open", l.panelOpen), l.panelOpen && await d(l.comments, l.filter);
1215
+ return oe(async (c) => {
1216
+ n.classList.toggle("open", c.panelOpen), c.panelOpen && await p(c.comments, c.filter);
1148
1217
  }), n;
1149
1218
  }
1150
- function de(e, t, r, n, o) {
1151
- const a = t.get(e.id) ?? [], d = document.createElement("div");
1152
- return d.className = "ufp-comment-card", d.dataset.id = e.id, d.innerHTML = `
1219
+ function Ne(e, t, r, n, o) {
1220
+ const a = t.get(e.id) ?? [], p = document.createElement("div");
1221
+ return p.className = "ufp-comment-card", p.dataset.id = e.id, p.innerHTML = `
1153
1222
  <div class="ufp-comment-card-header">
1154
1223
  <div class="ufp-comment-meta">
1155
- <div class="ufp-avatar">${F(e.authorName)}</div>
1224
+ <div class="ufp-avatar">${D(e.authorName)}</div>
1156
1225
  <div>
1157
- <div class="ufp-comment-author">${A(e.authorName)}</div>
1158
- <div class="ufp-comment-time">${W(e.createdAt)}</div>
1226
+ <div class="ufp-comment-author">${T(e.authorName)}</div>
1227
+ <div class="ufp-comment-time">${U(e.createdAt)}</div>
1159
1228
  </div>
1160
1229
  </div>
1161
1230
  ${e.resolved ? '<span class="ufp-resolved-badge">Resolved</span>' : ""}
1162
1231
  </div>
1163
1232
  <img class="ufp-comment-thumbnail" src="${e.screenshotUrl}" alt="Screenshot" loading="lazy" />
1164
- <div class="ufp-comment-text">${A(e.text)}</div>
1233
+ <div class="ufp-comment-text">${T(e.text)}</div>
1165
1234
  <div class="ufp-comment-footer">
1166
1235
  <span class="ufp-reply-count">
1167
1236
  ${a.length > 0 ? `${a.length} ${a.length === 1 ? "reply" : "replies"}` : "No replies yet — click to reply"}
1168
1237
  </span>
1169
1238
  </div>
1170
- `, d.addEventListener("click", async () => {
1171
- let l = t.get(e.id) ?? [];
1239
+ `, p.addEventListener("click", async () => {
1240
+ let c = t.get(e.id) ?? [];
1172
1241
  try {
1173
- l = await n.getReplies(e.id), t.set(e.id, l);
1174
- } catch (p) {
1175
- console.error("[ui-feedback-plugin] Failed to fetch replies:", p);
1242
+ c = await n.getReplies(e.id), t.set(e.id, c);
1243
+ } catch (i) {
1244
+ console.error("[ui-feedback-plugin] Failed to fetch replies:", i);
1176
1245
  }
1177
- le(
1246
+ ze(
1178
1247
  r,
1179
1248
  e,
1180
- l,
1249
+ c,
1181
1250
  n,
1182
- (p) => {
1183
- const s = L();
1184
- y({ comments: s.comments.map((i) => i.id === p ? { ...i, resolved: !0 } : i) });
1251
+ (i) => {
1252
+ const l = A();
1253
+ k({ comments: l.comments.map((s) => s.id === i ? { ...s, resolved: !0 } : s) });
1185
1254
  },
1186
- (p) => {
1187
- const s = L();
1188
- y({ comments: s.comments.map((i) => i.id === p ? { ...i, resolved: !1 } : i) });
1255
+ (i) => {
1256
+ const l = A();
1257
+ k({ comments: l.comments.map((s) => s.id === i ? { ...s, resolved: !1 } : s) });
1189
1258
  },
1190
- (p) => {
1191
- const s = L();
1192
- y({ comments: s.comments.filter((i) => i.id !== p) }), t.delete(p);
1259
+ (i) => {
1260
+ const l = A();
1261
+ k({ comments: l.comments.filter((s) => s.id !== i) }), t.delete(i);
1193
1262
  },
1194
1263
  // When a reply is added in the detail view, update badge count immediately
1195
- (p) => {
1196
- const s = t.get(e.id) ?? [];
1197
- t.set(e.id, [...s, p]);
1198
- const i = L();
1199
- y({ comments: [...i.comments] });
1264
+ (i) => {
1265
+ const l = t.get(e.id) ?? [];
1266
+ t.set(e.id, [...l, i]);
1267
+ const s = A();
1268
+ k({ comments: [...s.comments] });
1200
1269
  }
1201
1270
  );
1202
- }), d;
1271
+ }), p;
1203
1272
  }
1204
- async function ce() {
1273
+ async function He() {
1205
1274
  try {
1206
- const e = await ue();
1207
- return await fe(e);
1275
+ const e = await $e();
1276
+ return await Be(e);
1208
1277
  } catch (e) {
1209
- return console.error("[ui-feedback-plugin] capturePageScreenshot failed:", e), me();
1278
+ return console.error("[ui-feedback-plugin] capturePageScreenshot failed:", e), Re();
1210
1279
  }
1211
1280
  }
1212
- async function ue() {
1281
+ async function $e() {
1213
1282
  return (await import("./html2canvas-pro.esm-QETbiPYt.mjs")).default;
1214
1283
  }
1215
- async function fe(e) {
1216
- const t = window.scrollX, r = window.scrollY;
1217
- window.scrollTo(0, 0);
1218
- try {
1219
- return (await e(document.body, {
1220
- allowTaint: !1,
1221
- useCORS: !0,
1222
- logging: !1,
1223
- scale: Math.min(window.devicePixelRatio, 2),
1224
- width: document.documentElement.scrollWidth,
1225
- height: document.documentElement.scrollHeight,
1226
- windowWidth: document.documentElement.scrollWidth,
1227
- windowHeight: document.documentElement.scrollHeight,
1228
- x: 0,
1229
- y: 0,
1230
- ignoreElements: (o) => o.tagName === "UI-FEEDBACK-PLUGIN"
1231
- })).toDataURL("image/png");
1232
- } finally {
1233
- window.scrollTo(t, r);
1234
- }
1284
+ async function Be(e) {
1285
+ const t = document.documentElement.clientWidth, r = window.innerHeight;
1286
+ return (await e(document.body, {
1287
+ allowTaint: !1,
1288
+ useCORS: !0,
1289
+ logging: !1,
1290
+ scale: Math.min(window.devicePixelRatio, 2),
1291
+ x: window.scrollX,
1292
+ y: window.scrollY,
1293
+ width: t,
1294
+ height: r,
1295
+ windowWidth: t,
1296
+ windowHeight: r,
1297
+ ignoreElements: (o) => o.tagName === "UI-FEEDBACK-PLUGIN"
1298
+ })).toDataURL("image/png");
1235
1299
  }
1236
- function me() {
1300
+ function Re() {
1237
1301
  const e = document.createElement("canvas");
1238
1302
  e.width = window.innerWidth, e.height = window.innerHeight;
1239
1303
  const t = e.getContext("2d");
1240
1304
  return t.fillStyle = "#f3f4f6", t.fillRect(0, 0, e.width, e.height), t.fillStyle = "#9ca3af", t.font = "16px -apple-system, sans-serif", t.textAlign = "center", t.fillText("Screenshot unavailable", e.width / 2, e.height / 2), e.toDataURL("image/png");
1241
1305
  }
1242
- const q = "ui-feedback-plugin";
1243
- async function he(e) {
1306
+ async function We(e, t) {
1307
+ try {
1308
+ const r = await new Promise((i, l) => {
1309
+ const s = new Image();
1310
+ s.onload = () => i(s), s.onerror = () => l(new Error("screenshot failed to load")), s.src = e;
1311
+ }), n = document.createElement("canvas");
1312
+ n.width = r.naturalWidth, n.height = r.naturalHeight;
1313
+ const o = n.getContext("2d");
1314
+ o.drawImage(r, 0, 0);
1315
+ const a = n.width / t.pageWidth, p = n.height / t.pageHeight, c = Math.min(a, p);
1316
+ o.lineCap = "round", o.lineJoin = "round";
1317
+ for (const i of t.shapes)
1318
+ switch (o.strokeStyle = i.color, o.fillStyle = i.color, o.beginPath(), i.type) {
1319
+ case "pen": {
1320
+ o.lineWidth = i.width * c, i.points.forEach((l, s) => s === 0 ? o.moveTo(l.x * a, l.y * p) : o.lineTo(l.x * a, l.y * p)), o.stroke();
1321
+ break;
1322
+ }
1323
+ case "rect": {
1324
+ o.lineWidth = i.strokeWidth * c, o.roundRect(i.x * a, i.y * p, i.width * a, i.height * p, 3 * c), o.stroke();
1325
+ break;
1326
+ }
1327
+ case "circle": {
1328
+ o.lineWidth = i.strokeWidth * c, o.ellipse(i.cx * a, i.cy * p, i.rx * a, i.ry * p, 0, 0, Math.PI * 2), o.stroke();
1329
+ break;
1330
+ }
1331
+ case "arrow": {
1332
+ const l = i.width * c, s = i.x1 * a, h = i.y1 * p, v = i.x2 * a, m = i.y2 * p;
1333
+ o.lineWidth = l, o.lineCap = "butt", o.moveTo(s, h), o.lineTo(v, m), o.stroke(), o.lineCap = "round";
1334
+ const b = Math.hypot(v - s, m - h) || 1, x = (v - s) / b, u = (m - h) / b, w = v + x * l, C = m + u * l, N = w - x * 10 * l, H = C - u * 10 * l;
1335
+ o.beginPath(), o.moveTo(w, C), o.lineTo(N - u * 3.5 * l, H + x * 3.5 * l), o.lineTo(N + u * 3.5 * l, H - x * 3.5 * l), o.closePath(), o.fill();
1336
+ break;
1337
+ }
1338
+ }
1339
+ return n.toDataURL("image/png");
1340
+ } catch (r) {
1341
+ return console.error("[ui-feedback-plugin] drawAnnotationsOnScreenshot failed:", r), e;
1342
+ }
1343
+ }
1344
+ const F = "ui-feedback-plugin";
1345
+ async function Oe(e) {
1244
1346
  const { backend: t } = e;
1245
1347
  if (t.provider === "firebase") {
1246
1348
  const { FirebaseAdapter: r } = await import("./firebase-BGU5KpRU.mjs"), n = new r(t);
@@ -1260,112 +1362,114 @@ async function he(e) {
1260
1362
  }
1261
1363
  throw new Error("[ui-feedback-plugin] Unknown backend provider");
1262
1364
  }
1263
- function xe(e) {
1264
- var d;
1265
- if (customElements.get(q))
1365
+ function Pe(e) {
1366
+ var p;
1367
+ if (customElements.get(F))
1266
1368
  return console.warn("[ui-feedback-plugin] Already initialized."), () => {
1267
1369
  };
1268
- const t = ((d = e.theme) == null ? void 0 : d.primaryColor) ?? "#6366f1", r = e.projectKey ?? "default", n = e.position ?? "bottom-right";
1269
- y({ config: e });
1370
+ const t = ((p = e.theme) == null ? void 0 : p.primaryColor) ?? "#6366f1", r = e.projectKey ?? "default", n = e.position ?? "bottom-right";
1371
+ k({ config: e });
1270
1372
  class o extends HTMLElement {
1271
1373
  constructor() {
1272
1374
  super();
1273
- H(this, "shadow");
1274
- H(this, "db");
1275
- H(this, "screenshot");
1276
- H(this, "unsubscribe", null);
1375
+ O(this, "shadow");
1376
+ O(this, "db");
1377
+ O(this, "screenshot");
1378
+ O(this, "unsubscribe", null);
1277
1379
  this.shadow = this.attachShadow({ mode: "open" });
1278
1380
  }
1279
1381
  async connectedCallback() {
1280
- const s = document.createElement("style");
1281
- s.textContent = Q(t), this.shadow.appendChild(s);
1382
+ const l = document.createElement("style");
1383
+ l.textContent = xe(t), this.shadow.appendChild(l);
1282
1384
  try {
1283
- const i = await he(e);
1284
- this.db = i.db, this.screenshot = i.screenshot;
1285
- } catch (i) {
1286
- console.error("[ui-feedback-plugin] Failed to initialise backend:", i);
1385
+ const s = await Oe(e);
1386
+ this.db = s.db, this.screenshot = s.screenshot;
1387
+ } catch (s) {
1388
+ console.error("[ui-feedback-plugin] Failed to initialise backend:", s);
1287
1389
  return;
1288
1390
  }
1289
1391
  this.mount();
1290
1392
  }
1291
1393
  disconnectedCallback() {
1292
- var s;
1293
- (s = this.unsubscribe) == null || s.call(this);
1394
+ var l;
1395
+ (l = this.unsubscribe) == null || l.call(this);
1294
1396
  }
1295
1397
  mount() {
1296
- const s = I(window.location.href);
1297
- pe(this.shadow, this.db, () => this.startAnnotation()), ee(
1398
+ const l = Q(window.location.href);
1399
+ Te(this.shadow, this.db, () => this.startAnnotation()), ve(
1298
1400
  this.shadow,
1299
1401
  n,
1300
1402
  () => this.startAnnotation(),
1301
- () => y({ panelOpen: !L().panelOpen })
1302
- ), this.unsubscribe = this.db.subscribeToComments(s, r, (i) => {
1303
- y({ comments: i });
1403
+ () => k({ panelOpen: !A().panelOpen })
1404
+ ), this.unsubscribe = this.db.subscribeToComments(l, r, (s) => {
1405
+ k({ comments: s });
1304
1406
  });
1305
1407
  }
1306
1408
  async startAnnotation() {
1307
- let s = X();
1308
- if (!s && (s = await ne(this.shadow), !s))
1409
+ let l = re();
1410
+ if (!l && (l = await we(this.shadow), !l))
1309
1411
  return;
1310
- y({ active: !0, panelOpen: !1 });
1311
- const i = await ie(this.shadow);
1312
- if (!i) {
1313
- y({ active: !1 });
1412
+ k({ active: !0, panelOpen: !1 });
1413
+ const s = await Le(this.shadow);
1414
+ if (!s) {
1415
+ k({ active: !1 });
1314
1416
  return;
1315
1417
  }
1316
- y({ active: !1 }), this.showToast("Saving comment…", "info");
1418
+ k({ active: !1 }), this.showToast("Saving comment…", "info");
1317
1419
  try {
1318
- await this.saveComment(s, i.text, i.shapes), this.showToast("Comment saved!", "success");
1319
- } catch (u) {
1320
- console.error("[ui-feedback-plugin] Failed to save comment:", u), this.showToast("Failed to save — check the browser console for details.", "error");
1420
+ await this.saveComment(l, s.text, s.shapes), this.showToast("Comment saved!", "success");
1421
+ } catch (h) {
1422
+ console.error("[ui-feedback-plugin] Failed to save comment:", h), this.showToast("Failed to save — check the browser console for details.", "error");
1321
1423
  return;
1322
1424
  }
1323
- y({ panelOpen: !0 });
1324
- }
1325
- async saveComment(s, i, u) {
1326
- const w = I(window.location.href), h = {
1327
- shapes: u,
1328
- pageWidth: document.documentElement.scrollWidth,
1329
- pageHeight: document.documentElement.scrollHeight
1425
+ k({ panelOpen: !0 });
1426
+ }
1427
+ async saveComment(l, s, h) {
1428
+ const v = Q(window.location.href), m = {
1429
+ shapes: h,
1430
+ // Shapes are in viewport coordinates; the screenshot covers just the visible viewport.
1431
+ pageWidth: document.documentElement.clientWidth,
1432
+ pageHeight: window.innerHeight
1330
1433
  };
1331
1434
  this.style.visibility = "hidden";
1332
- let k;
1435
+ let b;
1333
1436
  try {
1334
- k = await ce();
1437
+ b = await He();
1335
1438
  } finally {
1336
1439
  this.style.visibility = "";
1337
1440
  }
1338
- const E = {
1339
- pageUrl: w,
1441
+ b = await We(b, m);
1442
+ const x = {
1443
+ pageUrl: v,
1340
1444
  projectKey: r,
1341
- authorName: s,
1342
- text: i,
1445
+ authorName: l,
1446
+ text: s,
1343
1447
  screenshotUrl: "",
1344
- annotation: h,
1448
+ annotation: m,
1345
1449
  resolved: !1,
1346
1450
  createdAt: Date.now()
1347
- }, f = await this.db.addComment(E), b = await this.screenshot.uploadScreenshot(f, k);
1348
- await this.db.updateComment(f, { screenshotUrl: b });
1349
- }
1350
- showToast(s, i) {
1351
- const u = this.shadow.querySelector(".ufp-toast");
1352
- u == null || u.remove();
1353
- const w = { info: "#6366f1", success: "#10b981", error: "#ef4444" }, h = document.createElement("div");
1354
- h.className = "ufp-toast", h.style.cssText = `
1451
+ }, u = await this.db.addComment(x), w = await this.screenshot.uploadScreenshot(u, b);
1452
+ await this.db.updateComment(u, { screenshotUrl: w });
1453
+ }
1454
+ showToast(l, s) {
1455
+ const h = this.shadow.querySelector(".ufp-toast");
1456
+ h == null || h.remove();
1457
+ const v = { info: "#6366f1", success: "#10b981", error: "#ef4444" }, m = document.createElement("div");
1458
+ m.className = "ufp-toast", m.style.cssText = `
1355
1459
  position:fixed;bottom:80px;left:50%;transform:translateX(-50%);
1356
- background:${w[i]};color:white;padding:10px 20px;border-radius:8px;
1460
+ background:${v[s]};color:white;padding:10px 20px;border-radius:8px;
1357
1461
  font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
1358
1462
  font-size:13px;font-weight:500;z-index:2147483647;
1359
1463
  box-shadow:0 4px 12px rgba(0,0,0,0.2);pointer-events:none;
1360
1464
  max-width:360px;text-align:center;
1361
- `, h.textContent = s, this.shadow.appendChild(h), setTimeout(() => h.remove(), i === "error" ? 6e3 : 2500);
1465
+ `, m.textContent = l, this.shadow.appendChild(m), setTimeout(() => m.remove(), s === "error" ? 6e3 : 2500);
1362
1466
  }
1363
1467
  }
1364
- customElements.define(q, o);
1365
- const a = document.createElement(q);
1468
+ customElements.define(F, o);
1469
+ const a = document.createElement(F);
1366
1470
  return document.body.appendChild(a), () => a.remove();
1367
1471
  }
1368
1472
  export {
1369
- xe as initFeedbackPlugin
1473
+ Pe as initFeedbackPlugin
1370
1474
  };
1371
1475
  //# sourceMappingURL=ui-feedback-plugin.es.js.map