@mindbowser_inc/ui-feedback-plugin 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -23,7 +23,7 @@ declare interface AWSProviderConfig {
23
23
  pollInterval?: number;
24
24
  }
25
25
 
26
- declare type BackendConfig = FirebaseProviderConfig | SupabaseProviderConfig | AWSProviderConfig;
26
+ declare type BackendConfig = FirebaseProviderConfig | SupabaseProviderConfig | AWSProviderConfig | LocalProviderConfig;
27
27
 
28
28
  /**
29
29
  * Firebase backend — Firestore for comments/replies, Firebase Storage for screenshots.
@@ -42,12 +42,17 @@ declare interface FirebaseProviderConfig {
42
42
 
43
43
  export declare function initFeedbackPlugin(config: PluginConfig): () => void;
44
44
 
45
+ declare interface LocalProviderConfig {
46
+ provider: 'local';
47
+ }
48
+
45
49
  export declare interface PluginConfig {
46
50
  /**
47
51
  * Storage backend. Pick one:
48
52
  * - `firebase` — Firestore + Firebase Storage
49
53
  * - `supabase` — Postgres + Supabase Storage
50
54
  * - `aws` — Everything in AWS S3 via Cognito Identity Pool (no backend needed)
55
+ * - `local` — In-memory / sessionStorage local storage
51
56
  */
52
57
  backend: BackendConfig;
53
58
  /** Namespace comments by project (default: 'default'). */
@@ -0,0 +1,66 @@
1
+ var a = Object.defineProperty;
2
+ var g = (n, e, t) => e in n ? a(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t;
3
+ var i = (n, e, t) => g(n, typeof e != "symbol" ? e + "" : e, t);
4
+ class l {
5
+ constructor() {
6
+ i(this, "listeners", /* @__PURE__ */ new Set());
7
+ }
8
+ getCommentsFromStorage() {
9
+ const e = sessionStorage.getItem("ufp_comments");
10
+ return e ? JSON.parse(e) : [];
11
+ }
12
+ saveCommentsToStorage(e) {
13
+ sessionStorage.setItem("ufp_comments", JSON.stringify(e)), this.notifyListeners();
14
+ }
15
+ getRepliesFromStorage() {
16
+ const e = sessionStorage.getItem("ufp_replies");
17
+ return e ? JSON.parse(e) : [];
18
+ }
19
+ saveRepliesToStorage(e) {
20
+ sessionStorage.setItem("ufp_replies", JSON.stringify(e));
21
+ }
22
+ notifyListeners() {
23
+ const e = this.getCommentsFromStorage();
24
+ for (const t of this.listeners)
25
+ t(e);
26
+ }
27
+ async addComment(e) {
28
+ const t = this.getCommentsFromStorage(), s = Math.random().toString(36).substring(2, 9), o = { ...e, id: s };
29
+ return t.push(o), this.saveCommentsToStorage(t), s;
30
+ }
31
+ async getComments(e, t) {
32
+ return this.getCommentsFromStorage().filter((o) => o.pageUrl === e && o.projectKey === t);
33
+ }
34
+ async updateComment(e, t) {
35
+ const s = this.getCommentsFromStorage(), o = s.findIndex((m) => m.id === e);
36
+ o !== -1 && (s[o] = { ...s[o], ...t }, this.saveCommentsToStorage(s));
37
+ }
38
+ async deleteComment(e) {
39
+ const s = this.getCommentsFromStorage().filter((r) => r.id !== e);
40
+ this.saveCommentsToStorage(s);
41
+ const m = this.getRepliesFromStorage().filter((r) => r.commentId !== e);
42
+ this.saveRepliesToStorage(m);
43
+ }
44
+ async addReply(e) {
45
+ const t = this.getRepliesFromStorage(), s = Math.random().toString(36).substring(2, 9), o = { ...e, id: s };
46
+ return t.push(o), this.saveRepliesToStorage(t), s;
47
+ }
48
+ async getReplies(e) {
49
+ return this.getRepliesFromStorage().filter((s) => s.commentId === e);
50
+ }
51
+ subscribeToComments(e, t, s) {
52
+ const o = (m) => {
53
+ s(m.filter((r) => r.pageUrl === e && r.projectKey === t));
54
+ };
55
+ return this.listeners.add(o), o(this.getCommentsFromStorage()), () => {
56
+ this.listeners.delete(o);
57
+ };
58
+ }
59
+ async uploadScreenshot(e, t) {
60
+ return t;
61
+ }
62
+ }
63
+ export {
64
+ l as LocalAdapter
65
+ };
66
+ //# sourceMappingURL=local-CqZPr-Eb.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-CqZPr-Eb.mjs","sources":["../src/adapters/local.ts"],"sourcesContent":["import type {\n FeedbackComment,\n Reply,\n DatabaseAdapter,\n ScreenshotAdapter,\n} from '../core/types'\n\nexport class LocalAdapter implements DatabaseAdapter, ScreenshotAdapter {\n private listeners: Set<(comments: FeedbackComment[]) => void> = new Set()\n\n private getCommentsFromStorage(): FeedbackComment[] {\n const data = sessionStorage.getItem('ufp_comments')\n return data ? JSON.parse(data) : []\n }\n\n private saveCommentsToStorage(comments: FeedbackComment[]) {\n sessionStorage.setItem('ufp_comments', JSON.stringify(comments))\n this.notifyListeners()\n }\n\n private getRepliesFromStorage(): Reply[] {\n const data = sessionStorage.getItem('ufp_replies')\n return data ? JSON.parse(data) : []\n }\n\n private saveRepliesToStorage(replies: Reply[]) {\n sessionStorage.setItem('ufp_replies', JSON.stringify(replies))\n }\n\n private notifyListeners() {\n const comments = this.getCommentsFromStorage()\n for (const listener of this.listeners) {\n listener(comments)\n }\n }\n\n async addComment(comment: Omit<FeedbackComment, 'id'>): Promise<string> {\n const comments = this.getCommentsFromStorage()\n const id = Math.random().toString(36).substring(2, 9)\n const newComment: FeedbackComment = { ...comment, id }\n comments.push(newComment)\n this.saveCommentsToStorage(comments)\n return id\n }\n\n async getComments(pageUrl: string, projectKey: string): Promise<FeedbackComment[]> {\n const comments = this.getCommentsFromStorage()\n return comments.filter(c => c.pageUrl === pageUrl && c.projectKey === projectKey)\n }\n\n async updateComment(id: string, data: Partial<FeedbackComment>): Promise<void> {\n const comments = this.getCommentsFromStorage()\n const index = comments.findIndex(c => c.id === id)\n if (index !== -1) {\n comments[index] = { ...comments[index], ...data }\n this.saveCommentsToStorage(comments)\n }\n }\n\n async deleteComment(id: string): Promise<void> {\n const comments = this.getCommentsFromStorage()\n const filtered = comments.filter(c => c.id !== id)\n this.saveCommentsToStorage(filtered)\n\n const replies = this.getRepliesFromStorage()\n const filteredReplies = replies.filter(r => r.commentId !== id)\n this.saveRepliesToStorage(filteredReplies)\n }\n\n async addReply(reply: Omit<Reply, 'id'>): Promise<string> {\n const replies = this.getRepliesFromStorage()\n const id = Math.random().toString(36).substring(2, 9)\n const newReply: Reply = { ...reply, id }\n replies.push(newReply)\n this.saveRepliesToStorage(replies)\n return id\n }\n\n async getReplies(commentId: string): Promise<Reply[]> {\n const replies = this.getRepliesFromStorage()\n return replies.filter(r => r.commentId === commentId)\n }\n\n subscribeToComments(\n pageUrl: string,\n projectKey: string,\n callback: (comments: FeedbackComment[]) => void\n ): () => void {\n const wrapper = (comments: FeedbackComment[]) => {\n callback(comments.filter(c => c.pageUrl === pageUrl && c.projectKey === projectKey))\n }\n this.listeners.add(wrapper)\n wrapper(this.getCommentsFromStorage())\n\n return () => {\n this.listeners.delete(wrapper)\n }\n }\n\n async uploadScreenshot(commentId: string, dataUrl: string): Promise<string> {\n // Return base64 URL directly, which gets stored in the comment's screenshotUrl.\n return dataUrl\n }\n}\n"],"names":["LocalAdapter","__publicField","data","comments","replies","listener","comment","id","newComment","pageUrl","projectKey","c","index","filtered","filteredReplies","reply","newReply","commentId","r","callback","wrapper","dataUrl"],"mappings":";;;AAOO,MAAMA,EAA2D;AAAA,EAAjE;AACG,IAAAC,EAAA,uCAA4D,IAAA;AAAA;AAAA,EAE5D,yBAA4C;AAClD,UAAMC,IAAO,eAAe,QAAQ,cAAc;AAClD,WAAOA,IAAO,KAAK,MAAMA,CAAI,IAAI,CAAA;AAAA,EACnC;AAAA,EAEQ,sBAAsBC,GAA6B;AACzD,mBAAe,QAAQ,gBAAgB,KAAK,UAAUA,CAAQ,CAAC,GAC/D,KAAK,gBAAA;AAAA,EACP;AAAA,EAEQ,wBAAiC;AACvC,UAAMD,IAAO,eAAe,QAAQ,aAAa;AACjD,WAAOA,IAAO,KAAK,MAAMA,CAAI,IAAI,CAAA;AAAA,EACnC;AAAA,EAEQ,qBAAqBE,GAAkB;AAC7C,mBAAe,QAAQ,eAAe,KAAK,UAAUA,CAAO,CAAC;AAAA,EAC/D;AAAA,EAEQ,kBAAkB;AACxB,UAAMD,IAAW,KAAK,uBAAA;AACtB,eAAWE,KAAY,KAAK;AAC1B,MAAAA,EAASF,CAAQ;AAAA,EAErB;AAAA,EAEA,MAAM,WAAWG,GAAuD;AACtE,UAAMH,IAAW,KAAK,uBAAA,GAChBI,IAAK,KAAK,SAAS,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC,GAC9CC,IAA8B,EAAE,GAAGF,GAAS,IAAAC,EAAA;AAClD,WAAAJ,EAAS,KAAKK,CAAU,GACxB,KAAK,sBAAsBL,CAAQ,GAC5BI;AAAA,EACT;AAAA,EAEA,MAAM,YAAYE,GAAiBC,GAAgD;AAEjF,WADiB,KAAK,uBAAA,EACN,OAAO,CAAAC,MAAKA,EAAE,YAAYF,KAAWE,EAAE,eAAeD,CAAU;AAAA,EAClF;AAAA,EAEA,MAAM,cAAcH,GAAYL,GAA+C;AAC7E,UAAMC,IAAW,KAAK,uBAAA,GAChBS,IAAQT,EAAS,UAAU,CAAAQ,MAAKA,EAAE,OAAOJ,CAAE;AACjD,IAAIK,MAAU,OACZT,EAASS,CAAK,IAAI,EAAE,GAAGT,EAASS,CAAK,GAAG,GAAGV,EAAA,GAC3C,KAAK,sBAAsBC,CAAQ;AAAA,EAEvC;AAAA,EAEA,MAAM,cAAcI,GAA2B;AAE7C,UAAMM,IADW,KAAK,uBAAA,EACI,OAAO,CAAAF,MAAKA,EAAE,OAAOJ,CAAE;AACjD,SAAK,sBAAsBM,CAAQ;AAGnC,UAAMC,IADU,KAAK,sBAAA,EACW,OAAO,CAAA,MAAK,EAAE,cAAcP,CAAE;AAC9D,SAAK,qBAAqBO,CAAe;AAAA,EAC3C;AAAA,EAEA,MAAM,SAASC,GAA2C;AACxD,UAAMX,IAAU,KAAK,sBAAA,GACfG,IAAK,KAAK,SAAS,SAAS,EAAE,EAAE,UAAU,GAAG,CAAC,GAC9CS,IAAkB,EAAE,GAAGD,GAAO,IAAAR,EAAA;AACpC,WAAAH,EAAQ,KAAKY,CAAQ,GACrB,KAAK,qBAAqBZ,CAAO,GAC1BG;AAAA,EACT;AAAA,EAEA,MAAM,WAAWU,GAAqC;AAEpD,WADgB,KAAK,sBAAA,EACN,OAAO,CAAAC,MAAKA,EAAE,cAAcD,CAAS;AAAA,EACtD;AAAA,EAEA,oBACER,GACAC,GACAS,GACY;AACZ,UAAMC,IAAU,CAACjB,MAAgC;AAC/C,MAAAgB,EAAShB,EAAS,OAAO,CAAAQ,MAAKA,EAAE,YAAYF,KAAWE,EAAE,eAAeD,CAAU,CAAC;AAAA,IACrF;AACA,gBAAK,UAAU,IAAIU,CAAO,GAC1BA,EAAQ,KAAK,wBAAwB,GAE9B,MAAM;AACX,WAAK,UAAU,OAAOA,CAAO;AAAA,IAC/B;AAAA,EACF;AAAA,EAEA,MAAM,iBAAiBH,GAAmBI,GAAkC;AAE1E,WAAOA;AAAA,EACT;AACF;"}
@@ -1,6 +1,6 @@
1
1
  var K = Object.defineProperty;
2
- var J = (e, t, o) => t in e ? K(e, t, { enumerable: !0, configurable: !0, writable: !0, value: o }) : e[t] = o;
3
- var H = (e, t, o) => J(e, typeof t != "symbol" ? t + "" : t, o);
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
4
  const O = /* @__PURE__ */ new Set();
5
5
  let T = {
6
6
  active: !1,
@@ -18,7 +18,7 @@ function L() {
18
18
  function y(e) {
19
19
  T = { ...T, ...e }, O.forEach((t) => t(T));
20
20
  }
21
- function P(e) {
21
+ function _(e) {
22
22
  return O.add(e), () => O.delete(e);
23
23
  }
24
24
  function X() {
@@ -598,9 +598,9 @@ const Z = {
598
598
  "top-right": { top: "24px", right: "24px" },
599
599
  "top-left": { top: "24px", left: "24px" }
600
600
  }, $ = "#6366f1", U = "#4f46e5", j = "#4338ca";
601
- function ee(e, t, o, n) {
602
- const r = document.createElement("div");
603
- Object.assign(r.style, {
601
+ function ee(e, t, r, n) {
602
+ const o = document.createElement("div");
603
+ Object.assign(o.style, {
604
604
  position: "fixed",
605
605
  zIndex: "2147483647",
606
606
  display: "flex",
@@ -627,18 +627,18 @@ function ee(e, t, o, n) {
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
- `, r.appendChild(a), r.appendChild(d), r.appendChild(l), e.appendChild(r);
631
- const p = r.querySelector("#ufp-annotate-label"), s = r.querySelector("#ufp-panel-count");
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
632
  return a.addEventListener("click", (i) => {
633
- if (i.stopPropagation(), r._dragged) return;
633
+ if (i.stopPropagation(), o._dragged) return;
634
634
  const u = L();
635
- u.active || (u.panelOpen ? (n(), setTimeout(o, 220)) : o());
635
+ u.active || (u.panelOpen ? (n(), setTimeout(r, 220)) : r());
636
636
  }), l.addEventListener("click", (i) => {
637
- i.stopPropagation(), !r._dragged && (L().active || n());
638
- }), _(a, () => (L().panelOpen, $), U), _(l, () => L().panelOpen ? j : $, U), P((i) => {
637
+ i.stopPropagation(), !o._dragged && (L().active || n());
638
+ }), P(a, () => (L().panelOpen, $), U), P(l, () => L().panelOpen ? j : $, U), _((i) => {
639
639
  const u = i.comments.filter((w) => !w.resolved).length;
640
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(r), r;
641
+ }), te(o), o;
642
642
  }
643
643
  function D(e) {
644
644
  Object.assign(e.style, {
@@ -654,23 +654,23 @@ function D(e) {
654
654
  transition: "background 0.12s"
655
655
  });
656
656
  }
657
- function _(e, t, o) {
657
+ function P(e, t, r) {
658
658
  e.addEventListener("mouseenter", () => {
659
- e.style.background = o;
659
+ e.style.background = r;
660
660
  }), e.addEventListener("mouseleave", () => {
661
661
  e.style.background = t();
662
662
  });
663
663
  }
664
664
  function te(e) {
665
- let t = 0, o = 0, n = 0, r = 0;
665
+ let t = 0, r = 0, n = 0, o = 0;
666
666
  e.addEventListener("pointerdown", (a) => {
667
667
  if (a.button !== 0) return;
668
- t = a.clientX, o = a.clientY;
668
+ t = a.clientX, r = a.clientY;
669
669
  const d = e.getBoundingClientRect();
670
- n = d.left, r = d.top, e._dragged = !1;
670
+ n = d.left, o = d.top, e._dragged = !1;
671
671
  const l = (s) => {
672
- const i = s.clientX - t, u = s.clientY - o;
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, r + u))}px`, e.style.right = "auto", e.style.bottom = "auto");
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
674
  }, p = () => {
675
675
  document.removeEventListener("pointermove", l), document.removeEventListener("pointerup", p), requestAnimationFrame(() => {
676
676
  e._dragged = !1;
@@ -681,8 +681,8 @@ function te(e) {
681
681
  }
682
682
  function ne(e) {
683
683
  return new Promise((t) => {
684
- const o = document.createElement("div");
685
- o.className = "ufp-modal-backdrop", o.innerHTML = `
684
+ const r = document.createElement("div");
685
+ r.className = "ufp-modal-backdrop", r.innerHTML = `
686
686
  <div class="ufp-modal" role="dialog" aria-modal="true" aria-labelledby="ufp-modal-title">
687
687
  <h2 id="ufp-modal-title">Welcome to Feedback Mode</h2>
688
688
  <p>Enter your name so others can see who left the comment.</p>
@@ -698,43 +698,43 @@ function ne(e) {
698
698
  <button class="ufp-btn ufp-btn-primary" id="ufp-name-submit" disabled>Continue</button>
699
699
  </div>
700
700
  </div>
701
- `, e.appendChild(o);
702
- const n = o.querySelector("#ufp-name-input"), r = o.querySelector("#ufp-name-submit");
701
+ `, e.appendChild(r);
702
+ const n = r.querySelector("#ufp-name-input"), o = r.querySelector("#ufp-name-submit");
703
703
  n.addEventListener("input", () => {
704
- r.disabled = n.value.trim().length === 0;
704
+ o.disabled = n.value.trim().length === 0;
705
705
  });
706
706
  const a = () => {
707
707
  const d = n.value.trim();
708
- d && (Y(d), o.remove(), t(d));
708
+ d && (Y(d), r.remove(), t(d));
709
709
  };
710
- r.addEventListener("click", a), n.addEventListener("keydown", (d) => {
710
+ o.addEventListener("click", a), n.addEventListener("keydown", (d) => {
711
711
  d.key === "Enter" && a();
712
712
  }), requestAnimationFrame(() => n.focus());
713
713
  });
714
714
  }
715
715
  const C = "#6366f1", M = 3;
716
- function z(e, t = 1, o = 1) {
716
+ function z(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, o)), n.setAttribute("stroke", e.color), n.setAttribute("stroke-width", String(e.width * Math.min(t, o))), n.setAttribute("fill", "none"), n.setAttribute("stroke-linecap", "round"), n.setAttribute("stroke-linejoin", "round"), n;
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;
721
721
  }
722
722
  case "rect": {
723
723
  const n = document.createElementNS("http://www.w3.org/2000/svg", "rect");
724
- return n.setAttribute("x", String(e.x * t)), n.setAttribute("y", String(e.y * o)), n.setAttribute("width", String(e.width * t)), n.setAttribute("height", String(e.height * o)), n.setAttribute("stroke", e.color), n.setAttribute("stroke-width", String(e.strokeWidth * Math.min(t, o))), n.setAttribute("fill", "none"), n.setAttribute("rx", "3"), n;
724
+ return n.setAttribute("x", String(e.x * t)), n.setAttribute("y", String(e.y * r)), n.setAttribute("width", String(e.width * t)), n.setAttribute("height", String(e.height * r)), n.setAttribute("stroke", e.color), n.setAttribute("stroke-width", String(e.strokeWidth * Math.min(t, r))), n.setAttribute("fill", "none"), n.setAttribute("rx", "3"), n;
725
725
  }
726
726
  case "circle": {
727
727
  const n = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");
728
- return n.setAttribute("cx", String(e.cx * t)), n.setAttribute("cy", String(e.cy * o)), n.setAttribute("rx", String(e.rx * t)), n.setAttribute("ry", String(e.ry * o)), n.setAttribute("stroke", e.color), n.setAttribute("stroke-width", String(e.strokeWidth * Math.min(t, o))), n.setAttribute("fill", "none"), n;
728
+ return n.setAttribute("cx", String(e.cx * t)), n.setAttribute("cy", String(e.cy * r)), n.setAttribute("rx", String(e.rx * t)), n.setAttribute("ry", String(e.ry * r)), n.setAttribute("stroke", e.color), n.setAttribute("stroke-width", String(e.strokeWidth * Math.min(t, r))), n.setAttribute("fill", "none"), n;
729
729
  }
730
730
  case "arrow": {
731
- const n = document.createElementNS("http://www.w3.org/2000/svg", "g"), r = document.createElementNS("http://www.w3.org/2000/svg", "line");
732
- return r.setAttribute("x1", String(e.x1 * t)), r.setAttribute("y1", String(e.y1 * o)), r.setAttribute("x2", String(e.x2 * t)), r.setAttribute("y2", String(e.y2 * o)), r.setAttribute("stroke", e.color), r.setAttribute("stroke-width", String(e.width * Math.min(t, o))), r.setAttribute("marker-end", "url(#ufp-arrowhead)"), n.appendChild(r), n;
731
+ const n = document.createElementNS("http://www.w3.org/2000/svg", "g"), o = document.createElementNS("http://www.w3.org/2000/svg", "line");
732
+ return o.setAttribute("x1", String(e.x1 * t)), o.setAttribute("y1", String(e.y1 * r)), o.setAttribute("x2", String(e.x2 * t)), o.setAttribute("y2", String(e.y2 * r)), o.setAttribute("stroke", e.color), o.setAttribute("stroke-width", String(e.width * Math.min(t, r))), o.setAttribute("marker-end", "url(#ufp-arrowhead)"), n.appendChild(o), n;
733
733
  }
734
734
  }
735
735
  }
736
- function re(e, t, o, n) {
737
- const r = o / t.pageWidth, a = n / t.pageHeight;
736
+ function re(e, t, r, n) {
737
+ const o = r / t.pageWidth, a = n / t.pageHeight;
738
738
  e.innerHTML = "";
739
739
  const d = document.createElementNS("http://www.w3.org/2000/svg", "defs");
740
740
  d.innerHTML = `
@@ -742,25 +742,25 @@ function re(e, t, o, n) {
742
742
  <polygon points="0 0, 10 3.5, 0 7" fill="${C}" />
743
743
  </marker>
744
744
  `, e.appendChild(d), t.shapes.forEach((l) => {
745
- e.appendChild(z(l, r, a));
745
+ e.appendChild(z(l, o, a));
746
746
  });
747
747
  }
748
- function oe(e, t, o) {
748
+ function oe(e, t, r) {
749
749
  if (e.length === 0) return "";
750
750
  if (e.length === 1) {
751
- const r = e[0];
752
- return `M ${r.x * t} ${r.y * o}`;
751
+ const o = e[0];
752
+ return `M ${o.x * t} ${o.y * r}`;
753
753
  }
754
- let n = `M ${e[0].x * t} ${e[0].y * o}`;
755
- for (let r = 1; r < e.length; r++)
756
- n += ` L ${e[r].x * t} ${e[r].y * o}`;
754
+ let n = `M ${e[0].x * t} ${e[0].y * r}`;
755
+ for (let o = 1; o < e.length; o++)
756
+ n += ` L ${e[o].x * t} ${e[o].y * r}`;
757
757
  return n;
758
758
  }
759
759
  function ie(e) {
760
760
  return new Promise((t) => {
761
- let o = "pen";
761
+ let r = "pen";
762
762
  const n = [];
763
- let r = !1, a = 0, d = 0, l = [], p = null, s = null;
763
+ let o = !1, a = 0, d = 0, l = [], p = null, s = null;
764
764
  const i = document.createElement("div");
765
765
  i.style.cssText = "position:fixed;inset:0;z-index:2147483645;cursor:crosshair;background:rgba(99,102,241,0.04);";
766
766
  const u = document.createElementNS("http://www.w3.org/2000/svg", "svg");
@@ -836,7 +836,7 @@ function ie(e) {
836
836
  const b = ae(
837
837
  e,
838
838
  (m) => {
839
- o = m;
839
+ r = m;
840
840
  },
841
841
  () => {
842
842
  w(), t(null);
@@ -846,41 +846,41 @@ function ie(e) {
846
846
  }
847
847
  );
848
848
  e.appendChild(i), i.addEventListener("pointerdown", (m) => {
849
- m.button === 0 && (i.setPointerCapture(m.pointerId), r = !0, a = m.clientX + window.scrollX, d = m.clientY + window.scrollY, o === "pen" && (l = [{ x: a, y: d }]));
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
850
  }), i.addEventListener("pointermove", (m) => {
851
- if (!r) return;
851
+ if (!o) return;
852
852
  const x = m.clientX + window.scrollX, g = m.clientY + window.scrollY;
853
- if (p && p.remove(), o === "pen")
853
+ if (p && p.remove(), r === "pen")
854
854
  l.push({ x, y: g }), p = z({ type: "pen", points: [...l], color: C, width: M });
855
- else if (o === "rect") {
855
+ else if (r === "rect") {
856
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
857
  p = z(c);
858
- } else if (o === "circle") {
858
+ } else if (r === "circle") {
859
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
860
  p = z(S);
861
- } else o === "arrow" && (p = z({ type: "arrow", x1: a, y1: d, x2: x, y2: g, color: C, width: M }));
861
+ } else r === "arrow" && (p = z({ type: "arrow", x1: a, y1: d, x2: x, y2: g, color: C, width: M }));
862
862
  p && u.appendChild(p);
863
863
  }), i.addEventListener("pointerup", (m) => {
864
- if (!r) return;
865
- r = !1, p && (p.remove(), p = null);
864
+ if (!o) return;
865
+ o = !1, p && (p.remove(), p = null);
866
866
  const x = m.clientX + window.scrollX, g = m.clientY + window.scrollY;
867
867
  let c = !1;
868
- if (o === "pen" && l.length > 1)
868
+ if (r === "pen" && l.length > 1)
869
869
  n.push({ type: "pen", points: [...l], color: C, width: M }), l = [], c = !0;
870
- else if (o === "rect") {
870
+ else if (r === "rect") {
871
871
  const v = Math.abs(x - a), S = Math.abs(g - d);
872
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);
873
- } else if (o === "circle") {
873
+ } else if (r === "circle") {
874
874
  const v = Math.abs(x - a) / 2, S = Math.abs(g - d) / 2;
875
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 o === "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);
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
877
  c && (h(), E());
878
878
  });
879
879
  });
880
880
  }
881
- function ae(e, t, o, n) {
882
- const r = document.createElement("div");
883
- return r.className = "ufp-toolbar", r.innerHTML = `
881
+ function ae(e, t, r, n) {
882
+ const o = document.createElement("div");
883
+ return o.className = "ufp-toolbar", o.innerHTML = `
884
884
  <span class="ufp-toolbar-label">Draw to annotate</span>
885
885
  <div class="ufp-toolbar-sep"></div>
886
886
  <button class="ufp-btn-icon active" data-tool="pen" title="Pen">
@@ -907,20 +907,20 @@ function ae(e, t, o, n) {
907
907
  </button>
908
908
  <div class="ufp-toolbar-sep"></div>
909
909
  <button class="ufp-btn ufp-btn-ghost" id="ufp-cancel-btn">Cancel</button>
910
- `, r.querySelectorAll("[data-tool]").forEach((a) => {
910
+ `, o.querySelectorAll("[data-tool]").forEach((a) => {
911
911
  a.addEventListener("click", () => {
912
- r.querySelectorAll("[data-tool]").forEach((d) => d.classList.remove("active")), a.classList.add("active"), t(a.dataset.tool);
912
+ o.querySelectorAll("[data-tool]").forEach((d) => d.classList.remove("active")), a.classList.add("active"), t(a.dataset.tool);
913
913
  });
914
- }), r.querySelector("#ufp-undo-btn").addEventListener("click", n), r.querySelector("#ufp-cancel-btn").addEventListener("click", o), e.appendChild(r), r;
914
+ }), o.querySelector("#ufp-undo-btn").addEventListener("click", n), o.querySelector("#ufp-cancel-btn").addEventListener("click", r), e.appendChild(o), o;
915
915
  }
916
916
  function W(e) {
917
- const t = Date.now() - e, o = Math.floor(t / 1e3);
918
- if (o < 60) return "just now";
919
- const n = Math.floor(o / 60);
917
+ const t = Date.now() - e, r = Math.floor(t / 1e3);
918
+ if (r < 60) return "just now";
919
+ const n = Math.floor(r / 60);
920
920
  if (n < 60) return `${n}m ago`;
921
- const r = Math.floor(n / 60);
922
- if (r < 24) return `${r}h ago`;
923
- const a = Math.floor(r / 24);
921
+ const o = Math.floor(n / 60);
922
+ if (o < 24) return `${o}h ago`;
923
+ const a = Math.floor(o / 24);
924
924
  return a < 7 ? `${a}d ago` : new Date(e).toLocaleDateString();
925
925
  }
926
926
  function F(e) {
@@ -929,8 +929,8 @@ function F(e) {
929
929
  function A(e) {
930
930
  return e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
931
931
  }
932
- function se(e, t, o, n) {
933
- let r = [...t];
932
+ function se(e, t, r, n) {
933
+ let o = [...t];
934
934
  const a = document.createElement("div");
935
935
  a.className = "ufp-replies";
936
936
  const d = (h) => {
@@ -992,16 +992,16 @@ function se(e, t, o, n) {
992
992
  createdAt: Date.now()
993
993
  };
994
994
  try {
995
- const b = { id: await o.addReply(E), ...E };
996
- i.value = "", u.textContent = "Reply", u.disabled = !0, r = [...r, b], d(r), n(b);
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
997
  } catch (f) {
998
998
  console.error("[ui-feedback-plugin] Failed to save reply:", f), u.disabled = !1, u.textContent = "Reply";
999
999
  }
1000
- }), a.appendChild(l), d(r), a.addReply = (h) => {
1001
- r = [...r, h], d(r);
1000
+ }), a.appendChild(l), d(o), a.addReply = (h) => {
1001
+ o = [...o, h], d(o);
1002
1002
  }, a;
1003
1003
  }
1004
- function le(e, t, o, n, r, a, d, l) {
1004
+ function le(e, t, r, n, o, a, d, l) {
1005
1005
  const p = document.createElement("div");
1006
1006
  p.className = "ufp-detail", p.innerHTML = `
1007
1007
  <div class="ufp-detail-image-area">
@@ -1066,14 +1066,14 @@ function le(e, t, o, n, r, a, d, l) {
1066
1066
  style="vertical-align:middle;margin-right:4px"><polyline points="20 6 9 17 4 12"/></svg>
1067
1067
  Mark Resolved
1068
1068
  `, f.addEventListener("click", async () => {
1069
- f.disabled = !0, f.textContent = "Resolving…", await n.updateComment(t.id, { resolved: !0 }), r(t.id), p.remove(), y({ panelOpen: !0 });
1069
+ f.disabled = !0, f.textContent = "Resolving…", await n.updateComment(t.id, { resolved: !0 }), o(t.id), p.remove(), y({ panelOpen: !0 });
1070
1070
  }), s.appendChild(f);
1071
1071
  }
1072
1072
  const i = document.createElement("button");
1073
1073
  i.className = "ufp-btn ufp-btn-ghost", i.style.fontSize = "13px", i.textContent = "Delete", i.addEventListener("click", async () => {
1074
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
1075
  }), s.appendChild(i);
1076
- const u = p.querySelector("#ufp-detail-replies"), w = se(t.id, o, n, (f) => {
1076
+ const u = p.querySelector("#ufp-detail-replies"), w = se(t.id, r, n, (f) => {
1077
1077
  var b;
1078
1078
  (b = w.addReply) == null || b.call(w, f), l == null || l(f);
1079
1079
  });
@@ -1086,7 +1086,7 @@ function le(e, t, o, n, r, a, d, l) {
1086
1086
  };
1087
1087
  h.complete && h.naturalWidth > 0 ? E() : h.addEventListener("load", E);
1088
1088
  }
1089
- function pe(e, t, o) {
1089
+ function pe(e, t, r) {
1090
1090
  const n = document.createElement("div");
1091
1091
  n.className = "ufp-panel", n.innerHTML = `
1092
1092
  <div class="ufp-panel-header">
@@ -1111,16 +1111,16 @@ function pe(e, t, o) {
1111
1111
  `, e.appendChild(n), n.querySelector("#ufp-panel-close").addEventListener("click", () => {
1112
1112
  y({ panelOpen: !1 });
1113
1113
  }), n.querySelector("#ufp-new-feedback").addEventListener("click", () => {
1114
- y({ panelOpen: !1 }), o();
1114
+ y({ panelOpen: !1 }), r();
1115
1115
  }), n.querySelectorAll(".ufp-filter-tab").forEach((l) => {
1116
1116
  l.addEventListener("click", () => {
1117
1117
  n.querySelectorAll(".ufp-filter-tab").forEach((p) => p.classList.remove("active")), l.classList.add("active"), y({ filter: l.dataset.filter });
1118
1118
  });
1119
1119
  });
1120
- const r = n.querySelector("#ufp-comment-list"), a = /* @__PURE__ */ new Map(), d = async (l, p) => {
1120
+ const o = n.querySelector("#ufp-comment-list"), a = /* @__PURE__ */ new Map(), d = async (l, p) => {
1121
1121
  const s = l.filter((i) => p === "open" ? !i.resolved : i.resolved);
1122
- if (r.innerHTML = "", s.length === 0) {
1123
- r.innerHTML = `
1122
+ if (o.innerHTML = "", s.length === 0) {
1123
+ o.innerHTML = `
1124
1124
  <div class="ufp-panel-empty">
1125
1125
  <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" opacity="0.4">
1126
1126
  <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
@@ -1138,16 +1138,16 @@ function pe(e, t, o) {
1138
1138
  } catch {
1139
1139
  a.set(i.id, []);
1140
1140
  }
1141
- r.appendChild(
1141
+ o.appendChild(
1142
1142
  de(i, a, e, t)
1143
1143
  );
1144
1144
  }
1145
1145
  };
1146
- return P(async (l) => {
1146
+ return _(async (l) => {
1147
1147
  n.classList.toggle("open", l.panelOpen), l.panelOpen && await d(l.comments, l.filter);
1148
1148
  }), n;
1149
1149
  }
1150
- function de(e, t, o, n, r) {
1150
+ function de(e, t, r, n, o) {
1151
1151
  const a = t.get(e.id) ?? [], d = document.createElement("div");
1152
1152
  return d.className = "ufp-comment-card", d.dataset.id = e.id, d.innerHTML = `
1153
1153
  <div class="ufp-comment-card-header">
@@ -1175,7 +1175,7 @@ function de(e, t, o, n, r) {
1175
1175
  console.error("[ui-feedback-plugin] Failed to fetch replies:", p);
1176
1176
  }
1177
1177
  le(
1178
- o,
1178
+ r,
1179
1179
  e,
1180
1180
  l,
1181
1181
  n,
@@ -1205,19 +1205,19 @@ async function ce() {
1205
1205
  try {
1206
1206
  const e = await ue();
1207
1207
  return await fe(e);
1208
- } catch {
1209
- return me();
1208
+ } catch (e) {
1209
+ return console.error("[ui-feedback-plugin] capturePageScreenshot failed:", e), me();
1210
1210
  }
1211
1211
  }
1212
1212
  async function ue() {
1213
- return (await import("./html2canvas.esm-d2sM-0Wm.mjs")).default;
1213
+ return (await import("./html2canvas-pro.esm-QETbiPYt.mjs")).default;
1214
1214
  }
1215
1215
  async function fe(e) {
1216
- const t = window.scrollX, o = window.scrollY;
1216
+ const t = window.scrollX, r = window.scrollY;
1217
1217
  window.scrollTo(0, 0);
1218
1218
  try {
1219
1219
  return (await e(document.body, {
1220
- allowTaint: !0,
1220
+ allowTaint: !1,
1221
1221
  useCORS: !0,
1222
1222
  logging: !1,
1223
1223
  scale: Math.min(window.devicePixelRatio, 2),
@@ -1227,10 +1227,10 @@ async function fe(e) {
1227
1227
  windowHeight: document.documentElement.scrollHeight,
1228
1228
  x: 0,
1229
1229
  y: 0,
1230
- ignoreElements: (r) => r.tagName === "UI-FEEDBACK-PLUGIN"
1230
+ ignoreElements: (o) => o.tagName === "UI-FEEDBACK-PLUGIN"
1231
1231
  })).toDataURL("image/png");
1232
1232
  } finally {
1233
- window.scrollTo(t, o);
1233
+ window.scrollTo(t, r);
1234
1234
  }
1235
1235
  }
1236
1236
  function me() {
@@ -1243,15 +1243,19 @@ const q = "ui-feedback-plugin";
1243
1243
  async function he(e) {
1244
1244
  const { backend: t } = e;
1245
1245
  if (t.provider === "firebase") {
1246
- const { FirebaseAdapter: o } = await import("./firebase-BGU5KpRU.mjs"), n = new o(t);
1246
+ const { FirebaseAdapter: r } = await import("./firebase-BGU5KpRU.mjs"), n = new r(t);
1247
1247
  return { db: n, screenshot: n };
1248
1248
  }
1249
1249
  if (t.provider === "supabase") {
1250
- const { SupabaseAdapter: o } = await import("./supabase-CbB80UVb.mjs"), n = new o(t.url, t.anonKey);
1250
+ const { SupabaseAdapter: r } = await import("./supabase-CbB80UVb.mjs"), n = new r(t.url, t.anonKey);
1251
1251
  return { db: n, screenshot: n };
1252
1252
  }
1253
1253
  if (t.provider === "aws") {
1254
- const { AWSAdapter: o } = await import("./aws-CkWYvUhs.mjs"), n = new o(t);
1254
+ const { AWSAdapter: r } = await import("./aws-CkWYvUhs.mjs"), n = new r(t);
1255
+ return { db: n, screenshot: n };
1256
+ }
1257
+ if (t.provider === "local") {
1258
+ const { LocalAdapter: r } = await import("./local-CqZPr-Eb.mjs"), n = new r();
1255
1259
  return { db: n, screenshot: n };
1256
1260
  }
1257
1261
  throw new Error("[ui-feedback-plugin] Unknown backend provider");
@@ -1261,9 +1265,9 @@ function xe(e) {
1261
1265
  if (customElements.get(q))
1262
1266
  return console.warn("[ui-feedback-plugin] Already initialized."), () => {
1263
1267
  };
1264
- const t = ((d = e.theme) == null ? void 0 : d.primaryColor) ?? "#6366f1", o = e.projectKey ?? "default", n = e.position ?? "bottom-right";
1268
+ const t = ((d = e.theme) == null ? void 0 : d.primaryColor) ?? "#6366f1", r = e.projectKey ?? "default", n = e.position ?? "bottom-right";
1265
1269
  y({ config: e });
1266
- class r extends HTMLElement {
1270
+ class o extends HTMLElement {
1267
1271
  constructor() {
1268
1272
  super();
1269
1273
  H(this, "shadow");
@@ -1295,7 +1299,7 @@ function xe(e) {
1295
1299
  n,
1296
1300
  () => this.startAnnotation(),
1297
1301
  () => y({ panelOpen: !L().panelOpen })
1298
- ), this.unsubscribe = this.db.subscribeToComments(s, o, (i) => {
1302
+ ), this.unsubscribe = this.db.subscribeToComments(s, r, (i) => {
1299
1303
  y({ comments: i });
1300
1304
  });
1301
1305
  }
@@ -1333,7 +1337,7 @@ function xe(e) {
1333
1337
  }
1334
1338
  const E = {
1335
1339
  pageUrl: w,
1336
- projectKey: o,
1340
+ projectKey: r,
1337
1341
  authorName: s,
1338
1342
  text: i,
1339
1343
  screenshotUrl: "",
@@ -1357,7 +1361,7 @@ function xe(e) {
1357
1361
  `, h.textContent = s, this.shadow.appendChild(h), setTimeout(() => h.remove(), i === "error" ? 6e3 : 2500);
1358
1362
  }
1359
1363
  }
1360
- customElements.define(q, r);
1364
+ customElements.define(q, o);
1361
1365
  const a = document.createElement(q);
1362
1366
  return document.body.appendChild(a), () => a.remove();
1363
1367
  }