@mindbowser_inc/ui-feedback-plugin 0.4.1 → 0.5.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
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
const
|
|
5
|
-
let
|
|
1
|
+
var ge = Object.defineProperty;
|
|
2
|
+
var xe = (e, t, r) => t in e ? ge(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r;
|
|
3
|
+
var O = (e, t, r) => xe(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
|
|
16
|
-
return
|
|
15
|
+
function A() {
|
|
16
|
+
return R;
|
|
17
17
|
}
|
|
18
|
-
function
|
|
19
|
-
|
|
18
|
+
function k(e) {
|
|
19
|
+
R = { ...R, ...e }, I.forEach((t) => t(R));
|
|
20
20
|
}
|
|
21
|
-
function
|
|
22
|
-
return
|
|
21
|
+
function ne(e) {
|
|
22
|
+
return I.add(e), () => I.delete(e);
|
|
23
23
|
}
|
|
24
|
-
function
|
|
25
|
-
if (
|
|
24
|
+
function oe() {
|
|
25
|
+
if (R.userName) return R.userName;
|
|
26
26
|
const e = localStorage.getItem("ufp_user_name");
|
|
27
|
-
return e ? (
|
|
27
|
+
return e ? (k({ userName: e }), e) : null;
|
|
28
28
|
}
|
|
29
|
-
function
|
|
30
|
-
localStorage.setItem("ufp_user_name", e),
|
|
29
|
+
function re(e) {
|
|
30
|
+
localStorage.setItem("ufp_user_name", e), k({ userName: e });
|
|
31
31
|
}
|
|
32
|
-
function
|
|
32
|
+
function J(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
|
|
40
|
+
function ye(e) {
|
|
41
41
|
return `
|
|
42
42
|
:host {
|
|
43
43
|
--ufp-primary: ${e};
|
|
@@ -592,15 +592,15 @@ function Q(e) {
|
|
|
592
592
|
}
|
|
593
593
|
`;
|
|
594
594
|
}
|
|
595
|
-
const
|
|
595
|
+
const ve = {
|
|
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
|
-
},
|
|
601
|
-
function
|
|
602
|
-
const
|
|
603
|
-
Object.assign(
|
|
600
|
+
}, q = "#6366f1", Q = "#4f46e5", Z = "#4338ca";
|
|
601
|
+
function be(e, t, r, o) {
|
|
602
|
+
const n = document.createElement("div");
|
|
603
|
+
Object.assign(n.style, {
|
|
604
604
|
position: "fixed",
|
|
605
605
|
zIndex: "2147483647",
|
|
606
606
|
display: "flex",
|
|
@@ -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
|
-
...
|
|
612
|
+
...ve[t]
|
|
613
613
|
});
|
|
614
614
|
const a = document.createElement("button");
|
|
615
|
-
a.id = "ufp-btn-annotate", a.title = "Add feedback annotation",
|
|
615
|
+
a.id = "ufp-btn-annotate", a.title = "Add feedback annotation", ee(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
|
|
623
|
-
|
|
624
|
-
const
|
|
625
|
-
|
|
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", ee(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
|
-
`,
|
|
631
|
-
const
|
|
632
|
-
return a.addEventListener("click", (
|
|
633
|
-
if (
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
}),
|
|
637
|
-
|
|
638
|
-
}),
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
}),
|
|
630
|
+
`, n.appendChild(a), n.appendChild(p), n.appendChild(c), e.appendChild(n);
|
|
631
|
+
const i = n.querySelector("#ufp-annotate-label"), l = n.querySelector("#ufp-panel-count");
|
|
632
|
+
return a.addEventListener("click", (s) => {
|
|
633
|
+
if (s.stopPropagation(), n._dragged) return;
|
|
634
|
+
const m = A();
|
|
635
|
+
m.active || (m.panelOpen ? (o(), setTimeout(r, 220)) : r());
|
|
636
|
+
}), c.addEventListener("click", (s) => {
|
|
637
|
+
s.stopPropagation(), !n._dragged && (A().active || o());
|
|
638
|
+
}), te(a, () => (A().panelOpen, q), Q), te(c, () => A().panelOpen ? Z : q, Q), ne((s) => {
|
|
639
|
+
const m = s.comments.filter((v) => !v.resolved).length;
|
|
640
|
+
l.textContent = m > 0 ? String(m) : "", 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 ? Z : q;
|
|
641
|
+
}), we(n), n;
|
|
642
642
|
}
|
|
643
|
-
function
|
|
643
|
+
function ee(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
|
|
657
|
+
function te(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
|
|
665
|
-
let t = 0, r = 0,
|
|
664
|
+
function we(e) {
|
|
665
|
+
let t = 0, r = 0, o = 0, n = 0;
|
|
666
666
|
e.addEventListener("pointerdown", (a) => {
|
|
667
667
|
if (a.button !== 0) return;
|
|
668
668
|
t = a.clientX, r = a.clientY;
|
|
669
|
-
const
|
|
670
|
-
|
|
671
|
-
const
|
|
672
|
-
const
|
|
673
|
-
!e._dragged && Math.sqrt(
|
|
674
|
-
},
|
|
675
|
-
document.removeEventListener("pointermove",
|
|
669
|
+
const p = e.getBoundingClientRect();
|
|
670
|
+
o = p.left, n = p.top, e._dragged = !1;
|
|
671
|
+
const c = (l) => {
|
|
672
|
+
const s = l.clientX - t, m = l.clientY - r;
|
|
673
|
+
!e._dragged && Math.sqrt(s * s + m * m) < 5 || (e._dragged = !0, e.style.left = `${Math.max(0, Math.min(window.innerWidth - e.offsetWidth, o + s))}px`, e.style.top = `${Math.max(0, Math.min(window.innerHeight - e.offsetHeight, n + m))}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",
|
|
679
|
+
document.addEventListener("pointermove", c), document.addEventListener("pointerup", i);
|
|
680
680
|
});
|
|
681
681
|
}
|
|
682
|
-
function
|
|
682
|
+
function ke(e) {
|
|
683
683
|
return new Promise((t) => {
|
|
684
684
|
const r = document.createElement("div");
|
|
685
685
|
r.className = "ufp-modal-backdrop", r.innerHTML = `
|
|
@@ -699,111 +699,152 @@ function ne(e) {
|
|
|
699
699
|
</div>
|
|
700
700
|
</div>
|
|
701
701
|
`, e.appendChild(r);
|
|
702
|
-
const
|
|
703
|
-
|
|
704
|
-
|
|
702
|
+
const o = r.querySelector("#ufp-name-input"), n = r.querySelector("#ufp-name-submit");
|
|
703
|
+
o.addEventListener("input", () => {
|
|
704
|
+
n.disabled = o.value.trim().length === 0;
|
|
705
705
|
});
|
|
706
706
|
const a = () => {
|
|
707
|
-
const
|
|
708
|
-
|
|
707
|
+
const p = o.value.trim();
|
|
708
|
+
p && (re(p), r.remove(), t(p));
|
|
709
709
|
};
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
}), requestAnimationFrame(() =>
|
|
710
|
+
n.addEventListener("click", a), o.addEventListener("keydown", (p) => {
|
|
711
|
+
p.key === "Enter" && a();
|
|
712
|
+
}), requestAnimationFrame(() => o.focus());
|
|
713
713
|
});
|
|
714
714
|
}
|
|
715
|
-
const
|
|
716
|
-
function
|
|
715
|
+
const L = "#6366f1", T = 3;
|
|
716
|
+
function B(e, t = 1, r = 1) {
|
|
717
717
|
switch (e.type) {
|
|
718
718
|
case "pen": {
|
|
719
|
-
const
|
|
720
|
-
return
|
|
719
|
+
const o = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
720
|
+
return o.setAttribute("d", Se(e.points, t, r)), o.setAttribute("stroke", e.color), o.setAttribute("stroke-width", String(e.width * Math.min(t, r))), o.setAttribute("fill", "none"), o.setAttribute("stroke-linecap", "round"), o.setAttribute("stroke-linejoin", "round"), o;
|
|
721
721
|
}
|
|
722
722
|
case "rect": {
|
|
723
|
-
const
|
|
724
|
-
return
|
|
723
|
+
const o = document.createElementNS("http://www.w3.org/2000/svg", "rect");
|
|
724
|
+
return o.setAttribute("x", String(e.x * t)), o.setAttribute("y", String(e.y * r)), o.setAttribute("width", String(e.width * t)), o.setAttribute("height", String(e.height * r)), o.setAttribute("stroke", e.color), o.setAttribute("stroke-width", String(e.strokeWidth * Math.min(t, r))), o.setAttribute("fill", "none"), o.setAttribute("rx", "3"), o;
|
|
725
725
|
}
|
|
726
726
|
case "circle": {
|
|
727
|
-
const
|
|
728
|
-
return
|
|
727
|
+
const o = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");
|
|
728
|
+
return o.setAttribute("cx", String(e.cx * t)), o.setAttribute("cy", String(e.cy * r)), o.setAttribute("rx", String(e.rx * t)), o.setAttribute("ry", String(e.ry * r)), o.setAttribute("stroke", e.color), o.setAttribute("stroke-width", String(e.strokeWidth * Math.min(t, r))), o.setAttribute("fill", "none"), o;
|
|
729
729
|
}
|
|
730
730
|
case "arrow": {
|
|
731
|
-
const
|
|
732
|
-
return
|
|
731
|
+
const o = document.createElementNS("http://www.w3.org/2000/svg", "g"), n = document.createElementNS("http://www.w3.org/2000/svg", "line");
|
|
732
|
+
return n.setAttribute("x1", String(e.x1 * t)), n.setAttribute("y1", String(e.y1 * r)), n.setAttribute("x2", String(e.x2 * t)), n.setAttribute("y2", String(e.y2 * r)), n.setAttribute("stroke", e.color), n.setAttribute("stroke-width", String(e.width * Math.min(t, r))), n.setAttribute("marker-end", "url(#ufp-arrowhead)"), o.appendChild(n), o;
|
|
733
733
|
}
|
|
734
734
|
}
|
|
735
735
|
}
|
|
736
|
-
function
|
|
737
|
-
const
|
|
736
|
+
function Ee(e, t, r, o) {
|
|
737
|
+
const n = r / t.pageWidth, a = o / t.pageHeight;
|
|
738
738
|
e.innerHTML = "";
|
|
739
|
-
const
|
|
740
|
-
|
|
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="${
|
|
742
|
+
<polygon points="0 0, 10 3.5, 0 7" fill="${L}" />
|
|
743
743
|
</marker>
|
|
744
|
-
`, e.appendChild(
|
|
745
|
-
e.appendChild(
|
|
744
|
+
`, e.appendChild(p), t.shapes.forEach((c) => {
|
|
745
|
+
e.appendChild(B(c, n, a));
|
|
746
746
|
});
|
|
747
747
|
}
|
|
748
|
-
function
|
|
748
|
+
function Se(e, t, r) {
|
|
749
749
|
if (e.length === 0) return "";
|
|
750
750
|
if (e.length === 1) {
|
|
751
|
-
const
|
|
752
|
-
return `M ${
|
|
751
|
+
const n = e[0];
|
|
752
|
+
return `M ${n.x * t} ${n.y * r}`;
|
|
753
753
|
}
|
|
754
|
-
let
|
|
755
|
-
for (let
|
|
756
|
-
|
|
757
|
-
return
|
|
754
|
+
let o = `M ${e[0].x * t} ${e[0].y * r}`;
|
|
755
|
+
for (let n = 1; n < e.length; n++)
|
|
756
|
+
o += ` L ${e[n].x * t} ${e[n].y * r}`;
|
|
757
|
+
return o;
|
|
758
758
|
}
|
|
759
|
-
function
|
|
759
|
+
function Ce(e, t, r) {
|
|
760
|
+
if (window.scrollY === 0) return null;
|
|
761
|
+
const o = window.innerWidth, n = 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 >= o * 0.9 && i.height >= n * 0.9)) {
|
|
767
|
+
if (c.position === "sticky") {
|
|
768
|
+
if (i.top < n * 0.25) return "top";
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
return n - i.bottom < i.top ? "bottom" : "top";
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
return null;
|
|
775
|
+
}
|
|
776
|
+
function Le(e, t) {
|
|
777
|
+
if (!t) return e;
|
|
778
|
+
switch (e.type) {
|
|
779
|
+
case "pen":
|
|
780
|
+
return { ...e, points: e.points.map((r) => ({ x: r.x, y: r.y + t })) };
|
|
781
|
+
case "rect":
|
|
782
|
+
return { ...e, y: e.y + t };
|
|
783
|
+
case "circle":
|
|
784
|
+
return { ...e, cy: e.cy + t };
|
|
785
|
+
case "arrow":
|
|
786
|
+
return { ...e, y1: e.y1 + t, y2: e.y2 + t };
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
function Me(e) {
|
|
760
790
|
return new Promise((t) => {
|
|
761
791
|
let r = "pen";
|
|
762
|
-
const
|
|
763
|
-
let
|
|
764
|
-
const
|
|
765
|
-
|
|
792
|
+
const o = [];
|
|
793
|
+
let n = !1, a = 0, p = 0, c = [], i = null, l = null, s = !1, m = 0;
|
|
794
|
+
const v = [];
|
|
795
|
+
let h = null;
|
|
796
|
+
const b = () => h ? { 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="${
|
|
802
|
+
<polygon points="0 0, 10 3.5, 0 7" fill="${L}" />
|
|
771
803
|
</marker>
|
|
772
804
|
</defs>
|
|
773
|
-
`,
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
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 Y) window.removeEventListener(d, K, !0);
|
|
815
|
+
for (const d of X) window.removeEventListener(d, G, !0);
|
|
816
|
+
x.remove(), P.remove(), l == null || l.remove();
|
|
817
|
+
}
|
|
818
|
+
function j() {
|
|
819
|
+
w.innerHTML = "", C.innerHTML = "", o.forEach((d, g) => (v[g] ? C : w).appendChild(B(d)));
|
|
820
|
+
}
|
|
821
|
+
function _() {
|
|
822
|
+
let d = 0, g = 0, y = 0;
|
|
823
|
+
return o.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:
|
|
790
|
-
anchorY:
|
|
830
|
+
anchorX: y > 0 ? d / y : window.innerWidth / 2,
|
|
831
|
+
anchorY: y > 0 ? g / y : window.innerHeight / 2
|
|
791
832
|
};
|
|
792
833
|
}
|
|
793
|
-
function
|
|
794
|
-
|
|
834
|
+
function ie() {
|
|
835
|
+
s = !0, n = !1, x.style.pointerEvents = "none", P.style.pointerEvents = "none", ae();
|
|
795
836
|
}
|
|
796
|
-
function
|
|
797
|
-
|
|
798
|
-
const { anchorX:
|
|
799
|
-
let
|
|
800
|
-
|
|
801
|
-
position:fixed;left:${
|
|
802
|
-
background:#fff;border-radius:12px;padding:16px;width:${
|
|
837
|
+
function ae() {
|
|
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
|
-
`,
|
|
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,68 +860,96 @@ 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(
|
|
823
|
-
const
|
|
824
|
-
|
|
825
|
-
const
|
|
826
|
-
|
|
827
|
-
}),
|
|
828
|
-
const
|
|
829
|
-
if (!
|
|
830
|
-
const
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
}),
|
|
835
|
-
|
|
836
|
-
|
|
863
|
+
`, e.appendChild(l);
|
|
864
|
+
const M = l.querySelector("#ufp-inline-text"), $ = l.querySelector("#ufp-popup-submit"), ce = 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 de = _();
|
|
872
|
+
H();
|
|
873
|
+
const ue = Math.max(0, document.documentElement.scrollHeight - window.innerHeight), fe = o.map((me, he) => Le(me, v[he] === "bottom" ? ue : 0));
|
|
874
|
+
t({ shapes: fe, text: W, ...de });
|
|
875
|
+
}), ce.addEventListener("click", () => {
|
|
876
|
+
H(), t(null);
|
|
877
|
+
}), requestAnimationFrame(() => M.focus());
|
|
878
|
+
}
|
|
879
|
+
const P = Ae(
|
|
837
880
|
e,
|
|
838
|
-
(
|
|
839
|
-
r =
|
|
881
|
+
(d) => {
|
|
882
|
+
r = d;
|
|
840
883
|
},
|
|
841
884
|
() => {
|
|
842
|
-
|
|
885
|
+
H(), t(null);
|
|
843
886
|
},
|
|
844
887
|
() => {
|
|
845
|
-
|
|
888
|
+
o.length !== 0 && (l && (l.remove(), l = null, x.style.pointerEvents = "auto", P.style.pointerEvents = "auto", s = !1), o.pop(), v.pop(), j());
|
|
846
889
|
}
|
|
847
890
|
);
|
|
848
|
-
e.appendChild(
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
891
|
+
e.appendChild(x);
|
|
892
|
+
const Y = ["pointerdown", "pointermove", "pointerup", "pointercancel"], X = ["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() > m || (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" ? se(g) : d.type === "pointermove" ? le(g) : d.type === "pointerup" ? pe(g) : d.type === "pointercancel" && (n = !1, i && (i.remove(), i = null));
|
|
905
|
+
}
|
|
906
|
+
function se(d) {
|
|
907
|
+
if (d.button === 0) {
|
|
908
|
+
d.preventDefault();
|
|
909
|
+
try {
|
|
910
|
+
x.setPointerCapture(d.pointerId);
|
|
911
|
+
} catch {
|
|
912
|
+
}
|
|
913
|
+
n = !0, h = Ce(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 le(d) {
|
|
917
|
+
if (!n) return;
|
|
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: T });
|
|
855
921
|
else if (r === "rect") {
|
|
856
|
-
const
|
|
857
|
-
|
|
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: T };
|
|
923
|
+
i = B(f);
|
|
858
924
|
} else if (r === "circle") {
|
|
859
|
-
const
|
|
860
|
-
|
|
861
|
-
} else r === "arrow" && (
|
|
862
|
-
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
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: T };
|
|
926
|
+
i = B(S);
|
|
927
|
+
} else r === "arrow" && (i = B({ type: "arrow", x1: a, y1: p, x2: g, y2: y, color: L, width: T }));
|
|
928
|
+
i && (h ? C : w).appendChild(i);
|
|
929
|
+
}
|
|
930
|
+
function pe(d) {
|
|
931
|
+
if (!n) return;
|
|
932
|
+
m = performance.now() + 400, n = !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
|
+
o.push({ type: "pen", points: [...c], color: L, width: T }), c = [], f = !0;
|
|
870
937
|
else if (r === "rect") {
|
|
871
|
-
const
|
|
872
|
-
|
|
938
|
+
const E = Math.abs(g - a), S = Math.abs(y - p);
|
|
939
|
+
E > 4 && S > 4 && (o.push({ type: "rect", x: Math.min(a, g), y: Math.min(p, y), width: E, height: S, color: L, strokeWidth: T }), f = !0);
|
|
873
940
|
} else if (r === "circle") {
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
} else r === "arrow" && Math.sqrt((
|
|
877
|
-
|
|
878
|
-
}
|
|
941
|
+
const E = Math.abs(g - a) / 2, S = Math.abs(y - p) / 2;
|
|
942
|
+
E > 4 && S > 4 && (o.push({ type: "circle", cx: Math.min(a, g) + E, cy: Math.min(p, y) + S, rx: E, ry: S, color: L, strokeWidth: T }), f = !0);
|
|
943
|
+
} else r === "arrow" && Math.sqrt((g - a) ** 2 + (y - p) ** 2) > 10 && (o.push({ type: "arrow", x1: a, y1: p, x2: g, y2: y, color: L, width: T }), f = !0);
|
|
944
|
+
f && (v.push(h), j(), ie());
|
|
945
|
+
}
|
|
946
|
+
for (const d of Y) window.addEventListener(d, K, { capture: !0, passive: !1 });
|
|
947
|
+
for (const d of X) window.addEventListener(d, G, { capture: !0, passive: !1 });
|
|
879
948
|
});
|
|
880
949
|
}
|
|
881
|
-
function
|
|
882
|
-
const
|
|
883
|
-
return
|
|
950
|
+
function Ae(e, t, r, o) {
|
|
951
|
+
const n = document.createElement("div");
|
|
952
|
+
return n.className = "ufp-toolbar", n.innerHTML = `
|
|
884
953
|
<span class="ufp-toolbar-label">Draw to annotate</span>
|
|
885
954
|
<div class="ufp-toolbar-sep"></div>
|
|
886
955
|
<button class="ufp-btn-icon active" data-tool="pen" title="Pen">
|
|
@@ -907,55 +976,55 @@ function ae(e, t, r, n) {
|
|
|
907
976
|
</button>
|
|
908
977
|
<div class="ufp-toolbar-sep"></div>
|
|
909
978
|
<button class="ufp-btn ufp-btn-ghost" id="ufp-cancel-btn">Cancel</button>
|
|
910
|
-
`,
|
|
979
|
+
`, n.querySelectorAll("[data-tool]").forEach((a) => {
|
|
911
980
|
a.addEventListener("click", () => {
|
|
912
|
-
|
|
981
|
+
n.querySelectorAll("[data-tool]").forEach((p) => p.classList.remove("active")), a.classList.add("active"), t(a.dataset.tool);
|
|
913
982
|
});
|
|
914
|
-
}),
|
|
983
|
+
}), n.querySelector("#ufp-undo-btn").addEventListener("click", o), n.querySelector("#ufp-cancel-btn").addEventListener("click", r), e.appendChild(n), n;
|
|
915
984
|
}
|
|
916
|
-
function
|
|
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
|
-
const
|
|
920
|
-
if (
|
|
921
|
-
const
|
|
922
|
-
if (
|
|
923
|
-
const a = Math.floor(
|
|
988
|
+
const o = Math.floor(r / 60);
|
|
989
|
+
if (o < 60) return `${o}m ago`;
|
|
990
|
+
const n = Math.floor(o / 60);
|
|
991
|
+
if (n < 24) return `${n}h ago`;
|
|
992
|
+
const a = Math.floor(n / 24);
|
|
924
993
|
return a < 7 ? `${a}d ago` : new Date(e).toLocaleDateString();
|
|
925
994
|
}
|
|
926
|
-
function
|
|
995
|
+
function D(e) {
|
|
927
996
|
return e.split(" ").filter(Boolean).slice(0, 2).map((t) => t[0].toUpperCase()).join("");
|
|
928
997
|
}
|
|
929
|
-
function
|
|
998
|
+
function z(e) {
|
|
930
999
|
return e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
931
1000
|
}
|
|
932
|
-
function
|
|
933
|
-
let
|
|
1001
|
+
function Te(e, t, r, o) {
|
|
1002
|
+
let n = [...t];
|
|
934
1003
|
const a = document.createElement("div");
|
|
935
1004
|
a.className = "ufp-replies";
|
|
936
|
-
const
|
|
937
|
-
const
|
|
938
|
-
|
|
939
|
-
const
|
|
940
|
-
if (
|
|
941
|
-
const
|
|
942
|
-
|
|
943
|
-
const
|
|
944
|
-
|
|
1005
|
+
const p = (h) => {
|
|
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;", h.length > 0) {
|
|
1010
|
+
const u = document.createElement("div");
|
|
1011
|
+
u.className = "ufp-replies-title", u.textContent = `${h.length} ${h.length === 1 ? "Reply" : "Replies"}`, x.appendChild(u), h.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;">${
|
|
947
|
-
<span class="ufp-reply-author">${
|
|
948
|
-
<span class="ufp-reply-time">${
|
|
1015
|
+
<div class="ufp-avatar" style="width:22px;height:22px;font-size:10px;">${D(w.authorName)}</div>
|
|
1016
|
+
<span class="ufp-reply-author">${z(w.authorName)}</span>
|
|
1017
|
+
<span class="ufp-reply-time">${U(w.createdAt)}</span>
|
|
949
1018
|
</div>
|
|
950
|
-
<div class="ufp-reply-text">${
|
|
951
|
-
`,
|
|
1019
|
+
<div class="ufp-reply-text">${z(w.text)}</div>
|
|
1020
|
+
`, x.appendChild(C);
|
|
952
1021
|
});
|
|
953
1022
|
}
|
|
954
|
-
a.insertBefore(
|
|
955
|
-
},
|
|
956
|
-
|
|
957
|
-
const
|
|
958
|
-
|
|
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 = oe();
|
|
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="${
|
|
1035
|
+
value="${z(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
|
|
982
|
-
|
|
1050
|
+
const l = c.querySelector("#ufp-reply-name"), s = c.querySelector("#ufp-reply-text"), m = c.querySelector("#ufp-reply-submit"), v = () => {
|
|
1051
|
+
m.disabled = l.value.trim().length === 0 || s.value.trim().length === 0;
|
|
983
1052
|
};
|
|
984
|
-
return
|
|
985
|
-
const h =
|
|
986
|
-
if (!h || !
|
|
987
|
-
|
|
988
|
-
const
|
|
1053
|
+
return l.addEventListener("input", v), s.addEventListener("input", v), m.addEventListener("click", async () => {
|
|
1054
|
+
const h = l.value.trim(), b = s.value.trim();
|
|
1055
|
+
if (!h || !b) return;
|
|
1056
|
+
m.disabled = !0, m.textContent = "Sending…", re(h);
|
|
1057
|
+
const x = {
|
|
989
1058
|
commentId: e,
|
|
990
1059
|
authorName: h,
|
|
991
|
-
text:
|
|
1060
|
+
text: b,
|
|
992
1061
|
createdAt: Date.now()
|
|
993
1062
|
};
|
|
994
1063
|
try {
|
|
995
|
-
const
|
|
996
|
-
|
|
997
|
-
} catch (
|
|
998
|
-
console.error("[ui-feedback-plugin] Failed to save reply:",
|
|
1064
|
+
const w = { id: await r.addReply(x), ...x };
|
|
1065
|
+
s.value = "", m.textContent = "Reply", m.disabled = !0, n = [...n, w], p(n), o(w);
|
|
1066
|
+
} catch (u) {
|
|
1067
|
+
console.error("[ui-feedback-plugin] Failed to save reply:", u), m.disabled = !1, m.textContent = "Reply";
|
|
999
1068
|
}
|
|
1000
|
-
}), a.appendChild(
|
|
1001
|
-
|
|
1069
|
+
}), a.appendChild(c), p(n), a.addReply = (h) => {
|
|
1070
|
+
n = [...n, h], p(n);
|
|
1002
1071
|
}, a;
|
|
1003
1072
|
}
|
|
1004
|
-
function
|
|
1005
|
-
const
|
|
1006
|
-
|
|
1073
|
+
function ze(e, t, r, o, n, 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,61 +1103,61 @@ 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">${
|
|
1106
|
+
<div class="ufp-avatar">${D(t.authorName)}</div>
|
|
1038
1107
|
<div>
|
|
1039
|
-
<div class="ufp-comment-author">${
|
|
1040
|
-
<div class="ufp-comment-time">${
|
|
1108
|
+
<div class="ufp-comment-author">${z(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">${
|
|
1112
|
+
<div class="ufp-detail-comment-text">${z(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(
|
|
1050
|
-
|
|
1118
|
+
`, e.appendChild(i), i.querySelector("#ufp-detail-back").addEventListener("click", () => {
|
|
1119
|
+
i.remove(), k({ panelOpen: !0 });
|
|
1051
1120
|
});
|
|
1052
|
-
const
|
|
1121
|
+
const l = i.querySelector("#ufp-detail-actions");
|
|
1053
1122
|
if (t.resolved) {
|
|
1054
|
-
const
|
|
1055
|
-
|
|
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
|
-
`,
|
|
1060
|
-
|
|
1061
|
-
}),
|
|
1128
|
+
`, u.addEventListener("click", async () => {
|
|
1129
|
+
u.disabled = !0, u.textContent = "Reopening…", await o.updateComment(t.id, { resolved: !1 }), a(t.id), i.remove(), k({ panelOpen: !0 });
|
|
1130
|
+
}), l.appendChild(u);
|
|
1062
1131
|
} else {
|
|
1063
|
-
const
|
|
1064
|
-
|
|
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
|
-
`,
|
|
1069
|
-
|
|
1070
|
-
}),
|
|
1137
|
+
`, u.addEventListener("click", async () => {
|
|
1138
|
+
u.disabled = !0, u.textContent = "Resolving…", await o.updateComment(t.id, { resolved: !0 }), n(t.id), i.remove(), k({ panelOpen: !0 });
|
|
1139
|
+
}), l.appendChild(u);
|
|
1071
1140
|
}
|
|
1072
|
-
const
|
|
1073
|
-
|
|
1074
|
-
confirm("Delete this comment and all its replies?") && (
|
|
1075
|
-
}),
|
|
1076
|
-
const
|
|
1077
|
-
var
|
|
1078
|
-
(
|
|
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 o.deleteComment(t.id), p(t.id), i.remove(), k({ panelOpen: !0 }));
|
|
1144
|
+
}), l.appendChild(s);
|
|
1145
|
+
const m = i.querySelector("#ufp-detail-replies"), v = Te(t.id, r, o, (u) => {
|
|
1146
|
+
var w;
|
|
1147
|
+
(w = v.addReply) == null || w.call(v, u), c == null || c(u);
|
|
1079
1148
|
});
|
|
1080
|
-
|
|
1081
|
-
const h =
|
|
1082
|
-
const
|
|
1083
|
-
|
|
1084
|
-
|
|
1149
|
+
m.appendChild(v);
|
|
1150
|
+
const h = i.querySelector(".ufp-detail-screenshot"), b = i.querySelector(".ufp-detail-annotation-svg"), x = () => {
|
|
1151
|
+
const u = h.clientWidth, w = h.clientHeight;
|
|
1152
|
+
b.setAttribute("viewBox", `0 0 ${u} ${w}`), Ee(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 ?
|
|
1156
|
+
h.complete && h.naturalWidth > 0 ? x() : h.addEventListener("load", x);
|
|
1088
1157
|
}
|
|
1089
|
-
function
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1158
|
+
function Ne(e, t, r) {
|
|
1159
|
+
const o = document.createElement("div");
|
|
1160
|
+
o.className = "ufp-panel", o.innerHTML = `
|
|
1092
1161
|
<div class="ufp-panel-header">
|
|
1093
1162
|
<span class="ufp-panel-title">Feedback</span>
|
|
1094
1163
|
<div style="display:flex;gap:8px;align-items:center;">
|
|
@@ -1108,111 +1177,111 @@ function pe(e, t, r) {
|
|
|
1108
1177
|
<button class="ufp-filter-tab" data-filter="resolved">Resolved</button>
|
|
1109
1178
|
</div>
|
|
1110
1179
|
<div class="ufp-panel-list" id="ufp-comment-list"></div>
|
|
1111
|
-
`, e.appendChild(
|
|
1112
|
-
|
|
1113
|
-
}),
|
|
1114
|
-
|
|
1115
|
-
}),
|
|
1116
|
-
|
|
1117
|
-
|
|
1180
|
+
`, e.appendChild(o), o.querySelector("#ufp-panel-close").addEventListener("click", () => {
|
|
1181
|
+
k({ panelOpen: !1 });
|
|
1182
|
+
}), o.querySelector("#ufp-new-feedback").addEventListener("click", () => {
|
|
1183
|
+
k({ panelOpen: !1 }), r();
|
|
1184
|
+
}), o.querySelectorAll(".ufp-filter-tab").forEach((c) => {
|
|
1185
|
+
c.addEventListener("click", () => {
|
|
1186
|
+
o.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
|
|
1121
|
-
const
|
|
1122
|
-
if (
|
|
1123
|
-
|
|
1189
|
+
const n = o.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 (n.innerHTML = "", l.length === 0) {
|
|
1192
|
+
n.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 ${
|
|
1129
|
-
${
|
|
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
|
|
1135
|
-
if (!a.has(
|
|
1203
|
+
for (const s of l) {
|
|
1204
|
+
if (!a.has(s.id))
|
|
1136
1205
|
try {
|
|
1137
|
-
a.set(
|
|
1206
|
+
a.set(s.id, await t.getReplies(s.id));
|
|
1138
1207
|
} catch {
|
|
1139
|
-
a.set(
|
|
1208
|
+
a.set(s.id, []);
|
|
1140
1209
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1210
|
+
n.appendChild(
|
|
1211
|
+
He(s, a, e, t)
|
|
1143
1212
|
);
|
|
1144
1213
|
}
|
|
1145
1214
|
};
|
|
1146
|
-
return
|
|
1147
|
-
|
|
1148
|
-
}),
|
|
1215
|
+
return ne(async (c) => {
|
|
1216
|
+
o.classList.toggle("open", c.panelOpen), c.panelOpen && await p(c.comments, c.filter);
|
|
1217
|
+
}), o;
|
|
1149
1218
|
}
|
|
1150
|
-
function
|
|
1151
|
-
const a = t.get(e.id) ?? [],
|
|
1152
|
-
return
|
|
1219
|
+
function He(e, t, r, o, n) {
|
|
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">${
|
|
1224
|
+
<div class="ufp-avatar">${D(e.authorName)}</div>
|
|
1156
1225
|
<div>
|
|
1157
|
-
<div class="ufp-comment-author">${
|
|
1158
|
-
<div class="ufp-comment-time">${
|
|
1226
|
+
<div class="ufp-comment-author">${z(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">${
|
|
1233
|
+
<div class="ufp-comment-text">${z(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
|
-
`,
|
|
1171
|
-
let
|
|
1239
|
+
`, p.addEventListener("click", async () => {
|
|
1240
|
+
let c = t.get(e.id) ?? [];
|
|
1172
1241
|
try {
|
|
1173
|
-
|
|
1174
|
-
} catch (
|
|
1175
|
-
console.error("[ui-feedback-plugin] Failed to fetch replies:",
|
|
1242
|
+
c = await o.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
|
-
|
|
1246
|
+
ze(
|
|
1178
1247
|
r,
|
|
1179
1248
|
e,
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
(
|
|
1183
|
-
const
|
|
1184
|
-
|
|
1249
|
+
c,
|
|
1250
|
+
o,
|
|
1251
|
+
(i) => {
|
|
1252
|
+
const l = A();
|
|
1253
|
+
k({ comments: l.comments.map((s) => s.id === i ? { ...s, resolved: !0 } : s) });
|
|
1185
1254
|
},
|
|
1186
|
-
(
|
|
1187
|
-
const
|
|
1188
|
-
|
|
1255
|
+
(i) => {
|
|
1256
|
+
const l = A();
|
|
1257
|
+
k({ comments: l.comments.map((s) => s.id === i ? { ...s, resolved: !1 } : s) });
|
|
1189
1258
|
},
|
|
1190
|
-
(
|
|
1191
|
-
const
|
|
1192
|
-
|
|
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
|
-
(
|
|
1196
|
-
const
|
|
1197
|
-
t.set(e.id, [...
|
|
1198
|
-
const
|
|
1199
|
-
|
|
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
|
-
}),
|
|
1271
|
+
}), p;
|
|
1203
1272
|
}
|
|
1204
|
-
async function
|
|
1273
|
+
async function $e() {
|
|
1205
1274
|
try {
|
|
1206
|
-
const e = await
|
|
1207
|
-
return await
|
|
1275
|
+
const e = await Be();
|
|
1276
|
+
return await Re(e);
|
|
1208
1277
|
} catch (e) {
|
|
1209
|
-
return console.error("[ui-feedback-plugin] capturePageScreenshot failed:", e),
|
|
1278
|
+
return console.error("[ui-feedback-plugin] capturePageScreenshot failed:", e), We();
|
|
1210
1279
|
}
|
|
1211
1280
|
}
|
|
1212
|
-
async function
|
|
1281
|
+
async function Be() {
|
|
1213
1282
|
return (await import("./html2canvas-pro.esm-QETbiPYt.mjs")).default;
|
|
1214
1283
|
}
|
|
1215
|
-
async function
|
|
1284
|
+
async function Re(e) {
|
|
1216
1285
|
const t = window.scrollX, r = window.scrollY;
|
|
1217
1286
|
window.scrollTo(0, 0);
|
|
1218
1287
|
try {
|
|
@@ -1227,145 +1296,184 @@ async function fe(e) {
|
|
|
1227
1296
|
windowHeight: document.documentElement.scrollHeight,
|
|
1228
1297
|
x: 0,
|
|
1229
1298
|
y: 0,
|
|
1230
|
-
ignoreElements: (
|
|
1299
|
+
ignoreElements: (n) => n.tagName === "UI-FEEDBACK-PLUGIN"
|
|
1231
1300
|
})).toDataURL("image/png");
|
|
1232
1301
|
} finally {
|
|
1233
1302
|
window.scrollTo(t, r);
|
|
1234
1303
|
}
|
|
1235
1304
|
}
|
|
1236
|
-
function
|
|
1305
|
+
function We() {
|
|
1237
1306
|
const e = document.createElement("canvas");
|
|
1238
1307
|
e.width = window.innerWidth, e.height = window.innerHeight;
|
|
1239
1308
|
const t = e.getContext("2d");
|
|
1240
1309
|
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
1310
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1311
|
+
async function Oe(e, t) {
|
|
1312
|
+
try {
|
|
1313
|
+
const r = await new Promise((i, l) => {
|
|
1314
|
+
const s = new Image();
|
|
1315
|
+
s.onload = () => i(s), s.onerror = () => l(new Error("screenshot failed to load")), s.src = e;
|
|
1316
|
+
}), o = document.createElement("canvas");
|
|
1317
|
+
o.width = r.naturalWidth, o.height = r.naturalHeight;
|
|
1318
|
+
const n = o.getContext("2d");
|
|
1319
|
+
n.drawImage(r, 0, 0);
|
|
1320
|
+
const a = o.width / t.pageWidth, p = o.height / t.pageHeight, c = Math.min(a, p);
|
|
1321
|
+
n.lineCap = "round", n.lineJoin = "round";
|
|
1322
|
+
for (const i of t.shapes)
|
|
1323
|
+
switch (n.strokeStyle = i.color, n.fillStyle = i.color, n.beginPath(), i.type) {
|
|
1324
|
+
case "pen": {
|
|
1325
|
+
n.lineWidth = i.width * c, i.points.forEach((l, s) => s === 0 ? n.moveTo(l.x * a, l.y * p) : n.lineTo(l.x * a, l.y * p)), n.stroke();
|
|
1326
|
+
break;
|
|
1327
|
+
}
|
|
1328
|
+
case "rect": {
|
|
1329
|
+
n.lineWidth = i.strokeWidth * c, n.roundRect(i.x * a, i.y * p, i.width * a, i.height * p, 3 * c), n.stroke();
|
|
1330
|
+
break;
|
|
1331
|
+
}
|
|
1332
|
+
case "circle": {
|
|
1333
|
+
n.lineWidth = i.strokeWidth * c, n.ellipse(i.cx * a, i.cy * p, i.rx * a, i.ry * p, 0, 0, Math.PI * 2), n.stroke();
|
|
1334
|
+
break;
|
|
1335
|
+
}
|
|
1336
|
+
case "arrow": {
|
|
1337
|
+
const l = i.width * c, s = i.x1 * a, m = i.y1 * p, v = i.x2 * a, h = i.y2 * p;
|
|
1338
|
+
n.lineWidth = l, n.lineCap = "butt", n.moveTo(s, m), n.lineTo(v, h), n.stroke(), n.lineCap = "round";
|
|
1339
|
+
const b = Math.hypot(v - s, h - m) || 1, x = (v - s) / b, u = (h - m) / b, w = v + x * l, C = h + u * l, N = w - x * 10 * l, H = C - u * 10 * l;
|
|
1340
|
+
n.beginPath(), n.moveTo(w, C), n.lineTo(N - u * 3.5 * l, H + x * 3.5 * l), n.lineTo(N + u * 3.5 * l, H - x * 3.5 * l), n.closePath(), n.fill();
|
|
1341
|
+
break;
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
return o.toDataURL("image/png");
|
|
1345
|
+
} catch (r) {
|
|
1346
|
+
return console.error("[ui-feedback-plugin] drawAnnotationsOnScreenshot failed:", r), e;
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
const F = "ui-feedback-plugin";
|
|
1350
|
+
async function qe(e) {
|
|
1244
1351
|
const { backend: t } = e;
|
|
1245
1352
|
if (t.provider === "firebase") {
|
|
1246
|
-
const { FirebaseAdapter: r } = await import("./firebase-BGU5KpRU.mjs"),
|
|
1247
|
-
return { db:
|
|
1353
|
+
const { FirebaseAdapter: r } = await import("./firebase-BGU5KpRU.mjs"), o = new r(t);
|
|
1354
|
+
return { db: o, screenshot: o };
|
|
1248
1355
|
}
|
|
1249
1356
|
if (t.provider === "supabase") {
|
|
1250
|
-
const { SupabaseAdapter: r } = await import("./supabase-BUHP2OkK.mjs"),
|
|
1251
|
-
return { db:
|
|
1357
|
+
const { SupabaseAdapter: r } = await import("./supabase-BUHP2OkK.mjs"), o = new r(t.url, t.anonKey);
|
|
1358
|
+
return { db: o, screenshot: o };
|
|
1252
1359
|
}
|
|
1253
1360
|
if (t.provider === "aws") {
|
|
1254
|
-
const { AWSAdapter: r } = await import("./aws-DyQmIBI9.mjs"),
|
|
1255
|
-
return { db:
|
|
1361
|
+
const { AWSAdapter: r } = await import("./aws-DyQmIBI9.mjs"), o = new r(t);
|
|
1362
|
+
return { db: o, screenshot: o };
|
|
1256
1363
|
}
|
|
1257
1364
|
if (t.provider === "local") {
|
|
1258
|
-
const { LocalAdapter: r } = await import("./local-CqZPr-Eb.mjs"),
|
|
1259
|
-
return { db:
|
|
1365
|
+
const { LocalAdapter: r } = await import("./local-CqZPr-Eb.mjs"), o = new r();
|
|
1366
|
+
return { db: o, screenshot: o };
|
|
1260
1367
|
}
|
|
1261
1368
|
throw new Error("[ui-feedback-plugin] Unknown backend provider");
|
|
1262
1369
|
}
|
|
1263
|
-
function
|
|
1264
|
-
var
|
|
1265
|
-
if (customElements.get(
|
|
1370
|
+
function Fe(e) {
|
|
1371
|
+
var p;
|
|
1372
|
+
if (customElements.get(F))
|
|
1266
1373
|
return console.warn("[ui-feedback-plugin] Already initialized."), () => {
|
|
1267
1374
|
};
|
|
1268
|
-
const t = ((
|
|
1269
|
-
|
|
1270
|
-
class
|
|
1375
|
+
const t = ((p = e.theme) == null ? void 0 : p.primaryColor) ?? "#6366f1", r = e.projectKey ?? "default", o = e.position ?? "bottom-right";
|
|
1376
|
+
k({ config: e });
|
|
1377
|
+
class n extends HTMLElement {
|
|
1271
1378
|
constructor() {
|
|
1272
1379
|
super();
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1380
|
+
O(this, "shadow");
|
|
1381
|
+
O(this, "db");
|
|
1382
|
+
O(this, "screenshot");
|
|
1383
|
+
O(this, "unsubscribe", null);
|
|
1277
1384
|
this.shadow = this.attachShadow({ mode: "open" });
|
|
1278
1385
|
}
|
|
1279
1386
|
async connectedCallback() {
|
|
1280
|
-
const
|
|
1281
|
-
|
|
1387
|
+
const l = document.createElement("style");
|
|
1388
|
+
l.textContent = ye(t), this.shadow.appendChild(l);
|
|
1282
1389
|
try {
|
|
1283
|
-
const
|
|
1284
|
-
this.db =
|
|
1285
|
-
} catch (
|
|
1286
|
-
console.error("[ui-feedback-plugin] Failed to initialise backend:",
|
|
1390
|
+
const s = await qe(e);
|
|
1391
|
+
this.db = s.db, this.screenshot = s.screenshot;
|
|
1392
|
+
} catch (s) {
|
|
1393
|
+
console.error("[ui-feedback-plugin] Failed to initialise backend:", s);
|
|
1287
1394
|
return;
|
|
1288
1395
|
}
|
|
1289
1396
|
this.mount();
|
|
1290
1397
|
}
|
|
1291
1398
|
disconnectedCallback() {
|
|
1292
|
-
var
|
|
1293
|
-
(
|
|
1399
|
+
var l;
|
|
1400
|
+
(l = this.unsubscribe) == null || l.call(this);
|
|
1294
1401
|
}
|
|
1295
1402
|
mount() {
|
|
1296
|
-
const
|
|
1297
|
-
|
|
1403
|
+
const l = J(window.location.href);
|
|
1404
|
+
Ne(this.shadow, this.db, () => this.startAnnotation()), be(
|
|
1298
1405
|
this.shadow,
|
|
1299
|
-
|
|
1406
|
+
o,
|
|
1300
1407
|
() => this.startAnnotation(),
|
|
1301
|
-
() =>
|
|
1302
|
-
), this.unsubscribe = this.db.subscribeToComments(
|
|
1303
|
-
|
|
1408
|
+
() => k({ panelOpen: !A().panelOpen })
|
|
1409
|
+
), this.unsubscribe = this.db.subscribeToComments(l, r, (s) => {
|
|
1410
|
+
k({ comments: s });
|
|
1304
1411
|
});
|
|
1305
1412
|
}
|
|
1306
1413
|
async startAnnotation() {
|
|
1307
|
-
let
|
|
1308
|
-
if (!
|
|
1414
|
+
let l = oe();
|
|
1415
|
+
if (!l && (l = await ke(this.shadow), !l))
|
|
1309
1416
|
return;
|
|
1310
|
-
|
|
1311
|
-
const
|
|
1312
|
-
if (!
|
|
1313
|
-
|
|
1417
|
+
k({ active: !0, panelOpen: !1 });
|
|
1418
|
+
const s = await Me(this.shadow);
|
|
1419
|
+
if (!s) {
|
|
1420
|
+
k({ active: !1 });
|
|
1314
1421
|
return;
|
|
1315
1422
|
}
|
|
1316
|
-
|
|
1423
|
+
k({ active: !1 }), this.showToast("Saving comment…", "info");
|
|
1317
1424
|
try {
|
|
1318
|
-
await this.saveComment(
|
|
1319
|
-
} catch (
|
|
1320
|
-
console.error("[ui-feedback-plugin] Failed to save comment:",
|
|
1425
|
+
await this.saveComment(l, s.text, s.shapes), this.showToast("Comment saved!", "success");
|
|
1426
|
+
} catch (m) {
|
|
1427
|
+
console.error("[ui-feedback-plugin] Failed to save comment:", m), this.showToast("Failed to save — check the browser console for details.", "error");
|
|
1321
1428
|
return;
|
|
1322
1429
|
}
|
|
1323
|
-
|
|
1430
|
+
k({ panelOpen: !0 });
|
|
1324
1431
|
}
|
|
1325
|
-
async saveComment(
|
|
1326
|
-
const
|
|
1327
|
-
shapes:
|
|
1432
|
+
async saveComment(l, s, m) {
|
|
1433
|
+
const v = J(window.location.href), h = {
|
|
1434
|
+
shapes: m,
|
|
1328
1435
|
pageWidth: document.documentElement.scrollWidth,
|
|
1329
1436
|
pageHeight: document.documentElement.scrollHeight
|
|
1330
1437
|
};
|
|
1331
1438
|
this.style.visibility = "hidden";
|
|
1332
|
-
let
|
|
1439
|
+
let b;
|
|
1333
1440
|
try {
|
|
1334
|
-
|
|
1441
|
+
b = await $e();
|
|
1335
1442
|
} finally {
|
|
1336
1443
|
this.style.visibility = "";
|
|
1337
1444
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1445
|
+
b = await Oe(b, h);
|
|
1446
|
+
const x = {
|
|
1447
|
+
pageUrl: v,
|
|
1340
1448
|
projectKey: r,
|
|
1341
|
-
authorName:
|
|
1342
|
-
text:
|
|
1449
|
+
authorName: l,
|
|
1450
|
+
text: s,
|
|
1343
1451
|
screenshotUrl: "",
|
|
1344
1452
|
annotation: h,
|
|
1345
1453
|
resolved: !1,
|
|
1346
1454
|
createdAt: Date.now()
|
|
1347
|
-
},
|
|
1348
|
-
await this.db.updateComment(
|
|
1455
|
+
}, u = await this.db.addComment(x), w = await this.screenshot.uploadScreenshot(u, b);
|
|
1456
|
+
await this.db.updateComment(u, { screenshotUrl: w });
|
|
1349
1457
|
}
|
|
1350
|
-
showToast(
|
|
1351
|
-
const
|
|
1352
|
-
|
|
1353
|
-
const
|
|
1458
|
+
showToast(l, s) {
|
|
1459
|
+
const m = this.shadow.querySelector(".ufp-toast");
|
|
1460
|
+
m == null || m.remove();
|
|
1461
|
+
const v = { info: "#6366f1", success: "#10b981", error: "#ef4444" }, h = document.createElement("div");
|
|
1354
1462
|
h.className = "ufp-toast", h.style.cssText = `
|
|
1355
1463
|
position:fixed;bottom:80px;left:50%;transform:translateX(-50%);
|
|
1356
|
-
background:${
|
|
1464
|
+
background:${v[s]};color:white;padding:10px 20px;border-radius:8px;
|
|
1357
1465
|
font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
|
|
1358
1466
|
font-size:13px;font-weight:500;z-index:2147483647;
|
|
1359
1467
|
box-shadow:0 4px 12px rgba(0,0,0,0.2);pointer-events:none;
|
|
1360
1468
|
max-width:360px;text-align:center;
|
|
1361
|
-
`, h.textContent =
|
|
1469
|
+
`, h.textContent = l, this.shadow.appendChild(h), setTimeout(() => h.remove(), s === "error" ? 6e3 : 2500);
|
|
1362
1470
|
}
|
|
1363
1471
|
}
|
|
1364
|
-
customElements.define(
|
|
1365
|
-
const a = document.createElement(
|
|
1472
|
+
customElements.define(F, n);
|
|
1473
|
+
const a = document.createElement(F);
|
|
1366
1474
|
return document.body.appendChild(a), () => a.remove();
|
|
1367
1475
|
}
|
|
1368
1476
|
export {
|
|
1369
|
-
|
|
1477
|
+
Fe as initFeedbackPlugin
|
|
1370
1478
|
};
|
|
1371
1479
|
//# sourceMappingURL=ui-feedback-plugin.es.js.map
|