review-lens-react 0.1.1 → 0.2.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,97 +1,119 @@
1
- import { jsx as a, jsxs as S, Fragment as G } from "react/jsx-runtime";
2
- import { createContext as q, useMemo as $, useState as N, useCallback as F, useEffect as P, useContext as H } from "react";
3
- const D = [
1
+ import { jsx as i, jsxs as o, Fragment as ie } from "react/jsx-runtime";
2
+ import { createContext as st, useMemo as re, useState as k, useCallback as W, useEffect as _, useContext as ot, useRef as We, useLayoutEffect as lt } from "react";
3
+ const ct = [
4
4
  "https://www.googleapis.com/auth/spreadsheets",
5
5
  "https://www.googleapis.com/auth/userinfo.email"
6
- ].join(" "), Q = "https://www.googleapis.com/oauth2/v3/userinfo";
7
- function X(e) {
8
- const o = e.feedbackSheetName ?? "Feedback", t = e.usersSheetName ?? "Users";
9
- let n, r;
10
- async function i() {
11
- return n ?? (n = te(e.googleClientId)), n;
6
+ ].join(" "), dt = "https://www.googleapis.com/oauth2/v3/userinfo";
7
+ function ht(e) {
8
+ const t = e.feedbackSheetName ?? "Feedback", n = e.messagesSheetName ?? "Messages", a = e.usersSheetName ?? "Users";
9
+ let s, c;
10
+ async function g() {
11
+ return s ?? (s = St(e.googleClientId)), s;
12
12
  }
13
- async function m(d, c) {
14
- const s = await i(), l = await fetch(
13
+ async function f(d, v) {
14
+ const p = await g(), b = await fetch(
15
15
  `https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${d}`,
16
16
  {
17
- ...c,
17
+ ...v,
18
18
  headers: {
19
- Authorization: `Bearer ${s}`,
19
+ Authorization: `Bearer ${p}`,
20
20
  "Content-Type": "application/json",
21
- ...c == null ? void 0 : c.headers
21
+ ...v == null ? void 0 : v.headers
22
22
  }
23
23
  }
24
24
  );
25
- if (!l.ok)
26
- throw new Error(`Google Sheets request failed with ${l.status}`);
27
- return l.json();
25
+ if (!b.ok)
26
+ throw new Error(`Google Sheets request failed with ${b.status}`);
27
+ return b.json();
28
28
  }
29
- async function v(d) {
30
- return (await m(
29
+ async function u(d) {
30
+ return (await f(
31
31
  `/values/${encodeURIComponent(d)}`
32
32
  )).values ?? [];
33
33
  }
34
34
  return {
35
35
  async getCurrentUser() {
36
- if (!r) {
37
- const d = await i(), c = await fetch(Q, {
36
+ if (!c) {
37
+ const d = await g(), v = await fetch(dt, {
38
38
  headers: { Authorization: `Bearer ${d}` }
39
39
  });
40
- if (!c.ok)
41
- throw new Error(`Google userinfo request failed with ${c.status}`);
42
- r = (await c.json()).email;
40
+ if (!v.ok)
41
+ throw new Error(`Google userinfo request failed with ${v.status}`);
42
+ c = (await v.json()).email;
43
43
  }
44
- if (!r)
44
+ if (!c)
45
45
  throw new Error("Google account did not return an email address");
46
- return { email: r };
46
+ return { email: c };
47
47
  },
48
48
  async getPermissions(d) {
49
- const [{ email: c }, s] = await Promise.all([this.getCurrentUser(), v(t)]), l = W(s), k = c.toLowerCase(), h = l.find(
50
- (f) => {
51
- var p;
52
- return ((p = f.email) == null ? void 0 : p.toLowerCase()) === k && f.active !== "false" && (!f.projectKey || f.projectKey === d);
49
+ const [{ email: v }, p] = await Promise.all([this.getCurrentUser(), u(a)]), b = ce(p), E = v.toLowerCase(), x = b.find(
50
+ (A) => {
51
+ var h;
52
+ return ((h = A.email) == null ? void 0 : h.toLowerCase()) === E && A.active !== "false" && (!A.projectKey || A.projectKey === d);
53
53
  }
54
54
  );
55
- return ee((h == null ? void 0 : h.role) ?? "designer");
55
+ return gt((x == null ? void 0 : x.role) ?? "designer");
56
56
  },
57
57
  async listFeedback(d) {
58
- return W(await v(o)).map(j).filter((s) => s !== null).filter(
59
- (s) => s.projectKey === d.projectKey && s.contentId === d.contentId && s.normalizedPath === d.normalizedPath
60
- ).sort((s, l) => l.createdAt.localeCompare(s.createdAt));
58
+ return ce(await u(t)).map(Te).filter((p) => p !== null).filter(
59
+ (p) => p.projectKey === d.projectKey && p.contentId === d.contentId && p.normalizedPath === d.normalizedPath
60
+ ).sort((p, b) => b.createdAt.localeCompare(p.createdAt));
61
61
  },
62
62
  async createFeedback(d) {
63
- const c = (/* @__PURE__ */ new Date()).toISOString(), s = {
63
+ const v = (/* @__PURE__ */ new Date()).toISOString(), p = {
64
64
  ...d,
65
65
  id: crypto.randomUUID(),
66
- status: "open",
67
- createdAt: c,
68
- updatedAt: c
66
+ attachments: [],
67
+ createdAt: v,
68
+ updatedAt: v
69
69
  };
70
- return await m(`/values/${encodeURIComponent(o)}:append?valueInputOption=RAW`, {
70
+ return await f(`/values/${encodeURIComponent(t)}:append?valueInputOption=RAW`, {
71
71
  method: "POST",
72
- body: JSON.stringify({ values: [V(s)] })
73
- }), s;
72
+ body: JSON.stringify({ values: [Ie(p)] })
73
+ }), p;
74
74
  },
75
- async resolveFeedback(d, c) {
76
- const s = await v(o), l = s[0] ?? Y, k = l.indexOf("id"), h = l.indexOf("status"), f = l.indexOf("updatedAt"), p = l.indexOf("resolvedAt"), w = l.indexOf("resolvedBy"), g = s.findIndex((R, x) => x > 0 && R[k] === d);
77
- if (g < 1)
75
+ async updateFeedback(d, v) {
76
+ const p = await u(t), b = p[0] ?? Me, E = b.indexOf("id");
77
+ if (E === -1)
78
+ throw new Error(`Sheet ${t} is missing an id column`);
79
+ const x = p.findIndex((w, C) => C > 0 && w[E] === d);
80
+ if (x < 1)
78
81
  throw new Error(`Feedback ${d} was not found`);
79
- const b = [...s[g]], L = (/* @__PURE__ */ new Date()).toISOString();
80
- b[h] = "resolved", b[f] = L, b[p] = L, b[w] = c, await m(
81
- `/values/${encodeURIComponent(o)}!A${g + 1}:Q${g + 1}?valueInputOption=RAW`,
82
+ const A = (/* @__PURE__ */ new Date()).toISOString(), h = Te(De(b, p[x]));
83
+ if (!h)
84
+ throw new Error(`Feedback ${d} could not be parsed before updating`);
85
+ const R = {
86
+ ...h,
87
+ ...v,
88
+ updatedAt: A
89
+ }, y = Ie(R);
90
+ return await f(
91
+ `/values/${encodeURIComponent(t)}!A${x + 1}:${yt(
92
+ Me.length
93
+ )}${x + 1}?valueInputOption=RAW`,
82
94
  {
83
95
  method: "PUT",
84
- body: JSON.stringify({ values: [b] })
96
+ body: JSON.stringify({ values: [y] })
85
97
  }
86
- );
87
- const I = j(O(l, b));
88
- if (!I)
89
- throw new Error(`Feedback ${d} could not be parsed after resolving`);
90
- return I;
98
+ ), R;
99
+ },
100
+ async listMessages(d) {
101
+ return ce(await u(n)).map(mt).filter((p) => p !== null).filter((p) => p.feedbackId === d).sort((p, b) => p.createdAt.localeCompare(b.createdAt));
102
+ },
103
+ async createMessage(d) {
104
+ const v = {
105
+ ...d,
106
+ id: crypto.randomUUID(),
107
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
108
+ };
109
+ return await f(`/values/${encodeURIComponent(n)}:append?valueInputOption=RAW`, {
110
+ method: "POST",
111
+ body: JSON.stringify({ values: [pt(v)] })
112
+ }), v;
91
113
  }
92
114
  };
93
115
  }
94
- const Y = [
116
+ const Me = [
95
117
  "id",
96
118
  "projectKey",
97
119
  "contentId",
@@ -100,16 +122,28 @@ const Y = [
100
122
  "selector",
101
123
  "selectorStrategy",
102
124
  "elementFingerprintJson",
103
- "cssSnapshotJson",
125
+ "createdCssSnapshotJson",
104
126
  "comment",
105
127
  "status",
128
+ "severity",
129
+ "category",
130
+ "assigneeEmail",
131
+ "viewportWidth",
132
+ "viewportHeight",
133
+ "viewportPreset",
134
+ "screenshotUrl",
135
+ "screenshotThumbnailUrl",
136
+ "attachmentJson",
106
137
  "authorEmail",
107
138
  "createdAt",
108
139
  "updatedAt",
140
+ "fixedCssSnapshotJson",
141
+ "fixedAt",
142
+ "fixedBy",
109
143
  "resolvedAt",
110
144
  "resolvedBy"
111
- ];
112
- function V(e) {
145
+ ], ut = ["id", "feedbackId", "body", "authorEmail", "createdAt"];
146
+ function Ie(e) {
113
147
  return [
114
148
  e.id,
115
149
  e.projectKey,
@@ -119,24 +153,39 @@ function V(e) {
119
153
  e.selector,
120
154
  e.selectorStrategy,
121
155
  JSON.stringify(e.elementFingerprint),
122
- JSON.stringify(e.cssSnapshot),
156
+ JSON.stringify(e.createdCssSnapshot),
123
157
  e.comment,
124
158
  e.status,
159
+ e.severity,
160
+ e.category,
161
+ e.assigneeEmail ?? "",
162
+ String(e.viewportWidth),
163
+ String(e.viewportHeight),
164
+ e.viewportPreset,
165
+ e.screenshotUrl ?? "",
166
+ e.screenshotThumbnailUrl ?? "",
167
+ JSON.stringify(e.attachments),
125
168
  e.authorEmail,
126
169
  e.createdAt,
127
170
  e.updatedAt,
171
+ e.fixedCssSnapshot ? JSON.stringify(e.fixedCssSnapshot) : "",
172
+ e.fixedAt ?? "",
173
+ e.fixedBy ?? "",
128
174
  e.resolvedAt ?? "",
129
175
  e.resolvedBy ?? ""
130
176
  ];
131
177
  }
132
- function W(e) {
133
- const [o, ...t] = e;
134
- return o ? t.map((n) => O(o, n)) : [];
178
+ function pt(e) {
179
+ return ut.map((t) => e[t]);
135
180
  }
136
- function O(e, o) {
137
- return Object.fromEntries(e.map((t, n) => [t, o[n] ?? ""]));
181
+ function ce(e) {
182
+ const [t, ...n] = e;
183
+ return t ? n.map((a) => De(t, a)) : [];
138
184
  }
139
- function j(e) {
185
+ function De(e, t) {
186
+ return Object.fromEntries(e.map((n, a) => [n, t[a] ?? ""]));
187
+ }
188
+ function Te(e) {
140
189
  return e.id ? {
141
190
  id: e.id,
142
191
  projectKey: e.projectKey,
@@ -145,552 +194,1405 @@ function j(e) {
145
194
  originalUrl: e.originalUrl,
146
195
  selector: e.selector,
147
196
  selectorStrategy: e.selectorStrategy === "stable-attribute" ? "stable-attribute" : "css-path",
148
- elementFingerprint: K(e.elementFingerprintJson, {
197
+ elementFingerprint: he(e.elementFingerprintJson, {
149
198
  tagName: "",
150
199
  width: 0,
151
200
  height: 0
152
201
  }),
153
- cssSnapshot: Z(e.cssSnapshotJson),
202
+ createdCssSnapshot: Re(e.createdCssSnapshotJson),
203
+ fixedCssSnapshot: e.fixedCssSnapshotJson ? Re(e.fixedCssSnapshotJson) : void 0,
154
204
  comment: e.comment,
155
- status: e.status === "resolved" ? "resolved" : "open",
205
+ status: ft(e.status),
206
+ severity: bt(e.severity),
207
+ category: vt(e.category),
208
+ assigneeEmail: e.assigneeEmail || void 0,
209
+ viewportWidth: Number(e.viewportWidth) || 0,
210
+ viewportHeight: Number(e.viewportHeight) || 0,
211
+ viewportPreset: wt(e.viewportPreset),
212
+ screenshotUrl: e.screenshotUrl || void 0,
213
+ screenshotThumbnailUrl: e.screenshotThumbnailUrl || void 0,
214
+ attachments: he(e.attachmentJson, []),
156
215
  authorEmail: e.authorEmail,
157
216
  createdAt: e.createdAt,
158
217
  updatedAt: e.updatedAt,
218
+ fixedAt: e.fixedAt || void 0,
219
+ fixedBy: e.fixedBy || void 0,
159
220
  resolvedAt: e.resolvedAt || void 0,
160
221
  resolvedBy: e.resolvedBy || void 0
161
222
  } : null;
162
223
  }
163
- function K(e, o) {
224
+ function mt(e) {
225
+ return !e.id || !e.feedbackId ? null : {
226
+ id: e.id,
227
+ feedbackId: e.feedbackId,
228
+ body: e.body,
229
+ authorEmail: e.authorEmail,
230
+ createdAt: e.createdAt
231
+ };
232
+ }
233
+ function he(e, t) {
164
234
  try {
165
- return e ? JSON.parse(e) : o;
235
+ return e ? JSON.parse(e) : t;
166
236
  } catch {
167
- return o;
237
+ return t;
168
238
  }
169
239
  }
170
- function Z(e) {
171
- const o = K(e, {});
240
+ function Re(e) {
241
+ const t = he(e, {});
172
242
  return {
173
- margin: o.margin ?? "",
174
- marginTop: o.marginTop ?? "",
175
- marginRight: o.marginRight ?? "",
176
- marginBottom: o.marginBottom ?? "",
177
- marginLeft: o.marginLeft ?? "",
178
- padding: o.padding ?? "",
179
- paddingTop: o.paddingTop ?? "",
180
- paddingRight: o.paddingRight ?? "",
181
- paddingBottom: o.paddingBottom ?? "",
182
- paddingLeft: o.paddingLeft ?? "",
183
- border: o.border ?? "",
184
- borderTopWidth: o.borderTopWidth ?? "",
185
- borderRightWidth: o.borderRightWidth ?? "",
186
- borderBottomWidth: o.borderBottomWidth ?? "",
187
- borderLeftWidth: o.borderLeftWidth ?? "",
188
- fontFamily: o.fontFamily ?? "",
189
- fontSize: o.fontSize ?? "",
190
- lineHeight: o.lineHeight ?? "",
191
- color: o.color ?? "",
192
- backgroundColor: o.backgroundColor ?? "",
193
- width: o.width ?? 0,
194
- height: o.height ?? 0
243
+ margin: t.margin ?? "",
244
+ marginTop: t.marginTop ?? "",
245
+ marginRight: t.marginRight ?? "",
246
+ marginBottom: t.marginBottom ?? "",
247
+ marginLeft: t.marginLeft ?? "",
248
+ padding: t.padding ?? "",
249
+ paddingTop: t.paddingTop ?? "",
250
+ paddingRight: t.paddingRight ?? "",
251
+ paddingBottom: t.paddingBottom ?? "",
252
+ paddingLeft: t.paddingLeft ?? "",
253
+ border: t.border ?? "",
254
+ borderTopWidth: t.borderTopWidth ?? "",
255
+ borderRightWidth: t.borderRightWidth ?? "",
256
+ borderBottomWidth: t.borderBottomWidth ?? "",
257
+ borderLeftWidth: t.borderLeftWidth ?? "",
258
+ fontFamily: t.fontFamily ?? "",
259
+ fontSize: t.fontSize ?? "",
260
+ lineHeight: t.lineHeight ?? "",
261
+ color: t.color ?? "",
262
+ backgroundColor: t.backgroundColor ?? "",
263
+ borderRadius: t.borderRadius ?? "",
264
+ width: t.width ?? 0,
265
+ height: t.height ?? 0
195
266
  };
196
267
  }
197
- function ee(e) {
198
- return e === "admin" ? ["create", "read", "resolve"] : e === "developer" ? ["read", "resolve"] : ["create", "read"];
268
+ function gt(e) {
269
+ return e === "admin" ? ["create", "read", "reply", "update", "assign"] : e === "developer" ? ["read", "reply", "update", "assign"] : ["create", "read", "reply"];
270
+ }
271
+ function ft(e) {
272
+ return e === "in_progress" || e === "needs_clarification" || e === "fixed" || e === "wontfix" || e === "resolved" ? e : "open";
273
+ }
274
+ function bt(e) {
275
+ return e === "low" || e === "high" ? e : "medium";
276
+ }
277
+ function vt(e) {
278
+ return e === "visual" || e === "copy" || e === "accessibility" || e === "responsive" ? e : "bug";
279
+ }
280
+ function wt(e) {
281
+ return e === "mobile" || e === "tablet" || e === "desktop" ? e : "custom";
199
282
  }
200
- async function te(e) {
201
- return await oe(), new Promise((o, t) => {
202
- var r;
203
- const n = (r = window.google) == null ? void 0 : r.accounts.oauth2.initTokenClient({
283
+ function yt(e) {
284
+ let t = e, n = "";
285
+ for (; t > 0; ) {
286
+ const a = (t - 1) % 26;
287
+ n = String.fromCharCode(65 + a) + n, t = Math.floor((t - a) / 26);
288
+ }
289
+ return n;
290
+ }
291
+ async function St(e) {
292
+ return await kt(), new Promise((t, n) => {
293
+ var s;
294
+ const a = (s = window.google) == null ? void 0 : s.accounts.oauth2.initTokenClient({
204
295
  client_id: e,
205
- scope: D,
206
- callback: (i) => {
207
- if (i.error || !i.access_token) {
208
- t(new Error(i.error ?? "Google OAuth did not return an access token"));
296
+ scope: ct,
297
+ callback: (c) => {
298
+ if (c.error || !c.access_token) {
299
+ n(new Error(c.error ?? "Google OAuth did not return an access token"));
209
300
  return;
210
301
  }
211
- o(i.access_token);
302
+ t(c.access_token);
212
303
  }
213
304
  });
214
- n == null || n.requestAccessToken({ prompt: "" });
305
+ a == null || a.requestAccessToken({ prompt: "" });
215
306
  });
216
307
  }
217
- function oe() {
308
+ function kt() {
218
309
  var e;
219
- return (e = window.google) != null && e.accounts.oauth2 ? Promise.resolve() : new Promise((o, t) => {
220
- const n = document.querySelector(
310
+ return (e = window.google) != null && e.accounts.oauth2 ? Promise.resolve() : new Promise((t, n) => {
311
+ const a = document.querySelector(
221
312
  'script[src="https://accounts.google.com/gsi/client"]'
222
313
  );
223
- if (n) {
224
- n.addEventListener("load", () => o(), { once: !0 }), n.addEventListener("error", () => t(new Error("Google Identity failed to load")), {
314
+ if (a) {
315
+ a.addEventListener("load", () => t(), { once: !0 }), a.addEventListener("error", () => n(new Error("Google Identity failed to load")), {
225
316
  once: !0
226
317
  });
227
318
  return;
228
319
  }
229
- const r = document.createElement("script");
230
- r.src = "https://accounts.google.com/gsi/client", r.async = !0, r.defer = !0, r.onload = () => o(), r.onerror = () => t(new Error("Google Identity failed to load")), document.head.append(r);
320
+ const s = document.createElement("script");
321
+ s.src = "https://accounts.google.com/gsi/client", s.async = !0, s.defer = !0, s.onload = () => t(), s.onerror = () => n(new Error("Google Identity failed to load")), document.head.append(s);
231
322
  });
232
323
  }
233
- function ne(e) {
324
+ function Ct(e) {
234
325
  return new URL(e, window.location.href).pathname.replace(/\/+$/, "") || "/";
235
326
  }
236
- const _ = q(null);
237
- function we({ config: e, children: o }) {
238
- const t = $(() => e.adapter ? e.adapter : X({
239
- googleClientId: U(e.googleClientId, "googleClientId"),
240
- spreadsheetId: U(e.spreadsheetId, "spreadsheetId"),
327
+ const ze = st(null);
328
+ function Xt({ config: e, children: t }) {
329
+ const n = re(() => e.adapter ? e.adapter : ht({
330
+ googleClientId: Be(e.googleClientId, "googleClientId"),
331
+ spreadsheetId: Be(e.spreadsheetId, "spreadsheetId"),
241
332
  feedbackSheetName: e.sheetName ?? "Feedback"
242
- }), [e.adapter, e.googleClientId, e.sheetName, e.spreadsheetId]), n = e.currentUrl ?? window.location.href, r = (e.normalizeUrl ?? ne)(n), [i, m] = N(), [v, d] = N([]), [c, s] = N([]), l = F(async () => {
243
- const p = await t.listFeedback({
333
+ }), [e.adapter, e.googleClientId, e.sheetName, e.spreadsheetId]), a = e.currentUrl ?? window.location.href, s = (e.normalizeUrl ?? Ct)(a), [c, g] = k(), [f, u] = k([]), [d, v] = k([]), p = W(async () => {
334
+ const y = await n.listFeedback({
244
335
  projectKey: e.projectKey,
245
336
  contentId: e.contentId,
246
- normalizedPath: r
337
+ normalizedPath: s
247
338
  });
248
- s(p);
249
- }, [t, e.contentId, e.projectKey, r]);
250
- P(() => {
251
- let p = !0;
339
+ v(y);
340
+ }, [n, e.contentId, e.projectKey, s]);
341
+ _(() => {
342
+ let y = !0;
252
343
  async function w() {
253
- const [g, b] = await Promise.all([
254
- t.getCurrentUser(),
255
- t.getPermissions(e.projectKey)
344
+ const [C, B] = await Promise.all([
345
+ n.getCurrentUser(),
346
+ n.getPermissions(e.projectKey)
256
347
  ]);
257
- p && (m(g), d(b), await l());
348
+ y && (g(C), u(B), await p());
258
349
  }
259
350
  return w(), () => {
260
- p = !1;
351
+ y = !1;
261
352
  };
262
- }, [t, e.projectKey, l]);
263
- const k = F(
264
- async (p) => {
265
- const w = await t.createFeedback(p);
266
- return s((g) => [w, ...g]), w;
353
+ }, [n, e.projectKey, p]);
354
+ const b = W(
355
+ async (y) => {
356
+ const w = await n.createFeedback(y);
357
+ return v((C) => [w, ...C]), w;
267
358
  },
268
- [t]
269
- ), h = F(
270
- async (p) => {
271
- const w = await t.resolveFeedback(p, (i == null ? void 0 : i.email) ?? "");
272
- return s(
273
- (g) => g.map((b) => b.id === p ? w : b)
274
- ), w;
359
+ [n]
360
+ ), E = W(
361
+ async (y, w) => {
362
+ const C = await n.updateFeedback(y, w);
363
+ return v(
364
+ (B) => B.map((D) => D.id === y ? C : D)
365
+ ), C;
275
366
  },
276
- [t, i == null ? void 0 : i.email]
277
- ), f = $(
367
+ [n]
368
+ ), x = W(
369
+ (y) => n.listMessages(y),
370
+ [n]
371
+ ), A = W(
372
+ (y) => n.createMessage(y),
373
+ [n]
374
+ ), h = W(
375
+ async (y, w) => {
376
+ const C = e.uploadAttachment ?? n.uploadAttachment;
377
+ if (!C)
378
+ throw new Error("Review Lens attachment upload is not configured");
379
+ return C(y, w);
380
+ },
381
+ [n, e]
382
+ ), R = re(
278
383
  () => ({
279
384
  config: e,
280
- adapter: t,
281
- currentUser: i,
282
- permissions: v,
283
- feedback: c,
284
- normalizedPath: r,
285
- refreshFeedback: l,
286
- createFeedback: k,
287
- resolveFeedback: h
385
+ adapter: n,
386
+ currentUser: c,
387
+ permissions: f,
388
+ feedback: d,
389
+ normalizedPath: s,
390
+ refreshFeedback: p,
391
+ createFeedback: b,
392
+ updateFeedback: E,
393
+ listMessages: x,
394
+ createMessage: A,
395
+ uploadAttachment: h
288
396
  }),
289
397
  [
290
- t,
398
+ n,
291
399
  e,
292
- k,
293
- i,
400
+ b,
294
401
  c,
295
- r,
296
- v,
297
- l,
402
+ d,
403
+ s,
404
+ f,
405
+ p,
406
+ E,
407
+ x,
408
+ A,
298
409
  h
299
410
  ]
300
411
  );
301
- return /* @__PURE__ */ a(_.Provider, { value: f, children: o });
412
+ return /* @__PURE__ */ i(ze.Provider, { value: R, children: t });
302
413
  }
303
- function re() {
304
- const e = H(_);
414
+ function Nt() {
415
+ const e = ot(ze);
305
416
  if (!e)
306
417
  throw new Error("useReviewLens must be used inside ReviewLensProvider");
307
418
  return e;
308
419
  }
309
- function U(e, o) {
420
+ function Be(e, t) {
310
421
  if (!e)
311
- throw new Error(`review-lens-react requires config.${o} when no adapter is provided`);
422
+ throw new Error(`review-lens-react requires config.${t} when no adapter is provided`);
312
423
  return e;
313
424
  }
314
- const ie = [
425
+ const xt = [
315
426
  "data-review-id",
316
427
  "data-testid",
317
428
  "data-test-id",
318
429
  "aria-label",
319
430
  "name"
320
431
  ];
321
- function M(e) {
322
- const o = e.getBoundingClientRect(), t = se(e);
432
+ function X(e) {
433
+ const t = e.getBoundingClientRect(), n = Et(e);
323
434
  return {
324
- selector: t.selector,
325
- selectorStrategy: t.strategy,
326
- fingerprint: de(e, o),
327
- cssSnapshot: ce(e, o),
328
- rect: o
435
+ selector: n.selector,
436
+ selectorStrategy: n.strategy,
437
+ fingerprint: Lt(e, t),
438
+ cssSnapshot: Ft(e, t),
439
+ rect: t
329
440
  };
330
441
  }
331
- function se(e) {
332
- for (const o of ie) {
333
- const t = e.getAttribute(o);
334
- if (t)
442
+ function Et(e) {
443
+ for (const t of xt) {
444
+ const n = e.getAttribute(t);
445
+ if (n)
335
446
  return {
336
- selector: `[${o}="${z(t)}"]`,
447
+ selector: `[${t}="${$e(n)}"]`,
337
448
  strategy: "stable-attribute"
338
449
  };
339
450
  }
340
- return e.id ? { selector: `#${z(e.id)}`, strategy: "stable-attribute" } : { selector: ae(e), strategy: "css-path" };
341
- }
342
- function ae(e) {
343
- const o = [];
344
- let t = e;
345
- for (; t && t.nodeType === Node.ELEMENT_NODE && t !== document.body; ) {
346
- const n = t.parentElement, r = t.tagName.toLowerCase();
347
- if (!n) {
348
- o.unshift(r);
451
+ return e.id ? { selector: `#${$e(e.id)}`, strategy: "stable-attribute" } : { selector: At(e), strategy: "css-path" };
452
+ }
453
+ function At(e) {
454
+ const t = [];
455
+ let n = e;
456
+ for (; n && n.nodeType === Node.ELEMENT_NODE && n !== document.body; ) {
457
+ const a = n.parentElement, s = n.tagName.toLowerCase();
458
+ if (!a) {
459
+ t.unshift(s);
349
460
  break;
350
461
  }
351
- const i = t.tagName, m = Array.from(n.children).filter(
352
- (d) => d.tagName === i
353
- ), v = m.indexOf(t) + 1;
354
- o.unshift(m.length > 1 ? `${r}:nth-of-type(${v})` : r), t = n;
462
+ const c = n.tagName, g = Array.from(a.children).filter(
463
+ (u) => u.tagName === c
464
+ ), f = g.indexOf(n) + 1;
465
+ t.unshift(g.length > 1 ? `${s}:nth-of-type(${f})` : s), n = a;
355
466
  }
356
- return o.join(" > ");
467
+ return t.join(" > ");
357
468
  }
358
- function de(e, o) {
359
- var t;
469
+ function Lt(e, t) {
470
+ var n;
360
471
  return {
361
472
  tagName: e.tagName.toLowerCase(),
362
473
  id: e.id || void 0,
363
474
  className: e.getAttribute("class") || void 0,
364
- textSnippet: ((t = e.textContent) == null ? void 0 : t.trim().slice(0, 80)) || void 0,
475
+ textSnippet: ((n = e.textContent) == null ? void 0 : n.trim().slice(0, 80)) || void 0,
365
476
  ariaLabel: e.getAttribute("aria-label") || void 0,
366
- width: Math.round(o.width),
367
- height: Math.round(o.height)
477
+ width: Math.round(t.width),
478
+ height: Math.round(t.height)
368
479
  };
369
480
  }
370
- function ce(e, o) {
371
- const t = window.getComputedStyle(e);
481
+ function Ft(e, t) {
482
+ const n = window.getComputedStyle(e);
372
483
  return {
373
- margin: A(t.marginTop, t.marginRight, t.marginBottom, t.marginLeft),
374
- marginTop: t.marginTop,
375
- marginRight: t.marginRight,
376
- marginBottom: t.marginBottom,
377
- marginLeft: t.marginLeft,
378
- padding: A(
379
- t.paddingTop,
380
- t.paddingRight,
381
- t.paddingBottom,
382
- t.paddingLeft
484
+ margin: de(n.marginTop, n.marginRight, n.marginBottom, n.marginLeft),
485
+ marginTop: n.marginTop,
486
+ marginRight: n.marginRight,
487
+ marginBottom: n.marginBottom,
488
+ marginLeft: n.marginLeft,
489
+ padding: de(
490
+ n.paddingTop,
491
+ n.paddingRight,
492
+ n.paddingBottom,
493
+ n.paddingLeft
383
494
  ),
384
- paddingTop: t.paddingTop,
385
- paddingRight: t.paddingRight,
386
- paddingBottom: t.paddingBottom,
387
- paddingLeft: t.paddingLeft,
388
- border: A(
389
- t.borderTopWidth,
390
- t.borderRightWidth,
391
- t.borderBottomWidth,
392
- t.borderLeftWidth
495
+ paddingTop: n.paddingTop,
496
+ paddingRight: n.paddingRight,
497
+ paddingBottom: n.paddingBottom,
498
+ paddingLeft: n.paddingLeft,
499
+ border: de(
500
+ n.borderTopWidth,
501
+ n.borderRightWidth,
502
+ n.borderBottomWidth,
503
+ n.borderLeftWidth
393
504
  ),
394
- borderTopWidth: t.borderTopWidth,
395
- borderRightWidth: t.borderRightWidth,
396
- borderBottomWidth: t.borderBottomWidth,
397
- borderLeftWidth: t.borderLeftWidth,
398
- fontFamily: t.fontFamily,
399
- fontSize: t.fontSize,
400
- lineHeight: t.lineHeight,
401
- color: t.color,
402
- backgroundColor: t.backgroundColor,
403
- width: Math.round(o.width),
404
- height: Math.round(o.height)
505
+ borderTopWidth: n.borderTopWidth,
506
+ borderRightWidth: n.borderRightWidth,
507
+ borderBottomWidth: n.borderBottomWidth,
508
+ borderLeftWidth: n.borderLeftWidth,
509
+ fontFamily: n.fontFamily,
510
+ fontSize: n.fontSize,
511
+ lineHeight: n.lineHeight,
512
+ color: n.color,
513
+ backgroundColor: n.backgroundColor,
514
+ borderRadius: n.borderRadius,
515
+ width: Math.round(t.width),
516
+ height: Math.round(t.height)
405
517
  };
406
518
  }
407
- function A(e, o, t, n) {
408
- return e === o && o === t && t === n ? e : `${e} ${o} ${t} ${n}`;
519
+ function de(e, t, n, a) {
520
+ return e === t && t === n && n === a ? e : `${e} ${t} ${n} ${a}`;
409
521
  }
410
- function z(e) {
522
+ function $e(e) {
411
523
  return typeof CSS < "u" && typeof CSS.escape == "function" ? CSS.escape(e) : e.replace(/["\\]/g, "\\$&");
412
524
  }
413
- function be({
525
+ const Mt = [
526
+ { label: "Desktop", value: "desktop" },
527
+ { label: "Tablet", value: "tablet" },
528
+ { label: "Mobile", value: "mobile" }
529
+ ], He = [
530
+ "open",
531
+ "in_progress",
532
+ "needs_clarification",
533
+ "fixed",
534
+ "wontfix",
535
+ "resolved"
536
+ ], je = ["low", "medium", "high"], Oe = ["bug", "visual", "copy", "accessibility", "responsive"], ae = {
537
+ open: "Open",
538
+ in_progress: "In progress",
539
+ needs_clarification: "Needs clarification",
540
+ fixed: "Fixed",
541
+ wontfix: "Won't fix",
542
+ resolved: "Resolved"
543
+ }, Q = {
544
+ low: "Low",
545
+ medium: "Medium",
546
+ high: "High"
547
+ }, Z = {
548
+ bug: "Bug",
549
+ visual: "Visual",
550
+ copy: "Copy",
551
+ accessibility: "Accessibility",
552
+ responsive: "Responsive"
553
+ };
554
+ function Qt({
414
555
  open: e,
415
- onOpenChange: o,
416
- placement: t = "top-right",
417
- showResolved: n = !1
556
+ onOpenChange: t,
557
+ placement: n = "top-right",
558
+ showResolved: a = !1,
559
+ syncSelectionToUrl: s = !1,
560
+ responsivePresets: c = Mt
418
561
  }) {
562
+ var Fe;
419
563
  const {
420
- config: r,
421
- currentUser: i,
422
- feedback: m,
564
+ adapter: g,
565
+ config: f,
566
+ currentUser: u,
567
+ feedback: d,
423
568
  normalizedPath: v,
424
- permissions: d,
425
- createFeedback: c,
426
- resolveFeedback: s
427
- } = re(), [l, k] = N(), [h, f] = N(), [p, w] = N(""), [g, b] = N(), L = d.includes("create"), I = d.includes("resolve"), R = $(
428
- () => m.filter((u) => n || u.status !== "resolved"),
429
- [m, n]
430
- );
431
- P(() => {
432
- e || (k(void 0), f(void 0), w(""));
433
- }, [e]);
434
- const x = F((u) => {
435
- const E = u.target instanceof Element ? u.target : null;
436
- if (E)
437
- return E.closest("[data-review-lens-ui]") ? null : E;
438
- const C = document.elementFromPoint(u.clientX, u.clientY);
439
- return !C || C.closest("[data-review-lens-ui]") ? null : C;
569
+ permissions: p,
570
+ createFeedback: b,
571
+ updateFeedback: E,
572
+ listMessages: x,
573
+ createMessage: A,
574
+ uploadAttachment: h
575
+ } = Nt(), [R, y] = k(), [w, C] = k(), [B, D] = k(""), [ue, Je] = k("medium"), [pe, qe] = k("visual"), [me, ge] = k(""), [fe, Ve] = k(
576
+ ((Fe = c[0]) == null ? void 0 : Fe.value) ?? "desktop"
577
+ ), [S, H] = k(), [M, $] = k("review"), [Ge, be] = k(!1), [j, ve] = k("all"), [O, we] = k("all"), [K, ye] = k("all"), [J, Se] = k("all"), [q, ke] = k("all"), [Ye, Xe] = k(!1), [Qe, Ce] = k({}), [se, oe] = k(""), ee = We(null), I = !!u, T = p.includes("create"), Ne = p.includes("reply"), xe = p.includes("update"), Ze = p.includes("assign"), P = R ?? w, et = !!w, tt = !!(f.captureScreenshot && (f.uploadAttachment || g.uploadAttachment)), nt = re(() => {
578
+ const r = d.map((m) => m.assigneeEmail).filter((m) => !!m);
579
+ return u != null && u.email && r.push(u.email), Array.from(new Set(r)).sort((m, l) => m.localeCompare(l));
580
+ }, [u == null ? void 0 : u.email, d]), L = re(
581
+ () => d.filter((r) => a || r.status !== "resolved").filter((r) => j === "all" || r.status === j).filter((r) => O === "all" || r.severity === O).filter((r) => K === "all" || r.category === K).filter((r) => J === "all" || r.assigneeEmail === J).filter((r) => q === "all" || r.viewportPreset === q),
582
+ [
583
+ J,
584
+ K,
585
+ d,
586
+ O,
587
+ a,
588
+ j,
589
+ q
590
+ ]
591
+ ), it = [
592
+ j,
593
+ O,
594
+ K,
595
+ J,
596
+ q
597
+ ].filter((r) => r !== "all").length;
598
+ _(() => {
599
+ e || (y(void 0), C(void 0), D(""), oe(""), $("review"));
600
+ }, [e]), _(() => {
601
+ I || (y(void 0), C(void 0));
602
+ }, [I]), _(() => {
603
+ !w || M !== "review" || window.requestAnimationFrame(() => {
604
+ var r, m, l;
605
+ (m = (r = ee.current) == null ? void 0 : r.scrollIntoView) == null || m.call(r, { block: "nearest" }), (l = ee.current) == null || l.focus();
606
+ });
607
+ }, [w, M]), _(() => {
608
+ if (!S)
609
+ return;
610
+ let r = !0;
611
+ return x(S.id).then((m) => {
612
+ r && Ce((l) => ({ ...l, [S.id]: m }));
613
+ }), () => {
614
+ r = !1;
615
+ };
616
+ }, [x, S]), _(() => {
617
+ if (!e || !s || S || d.length === 0)
618
+ return;
619
+ const r = new URL(window.location.href).searchParams.get("reviewLensFeedback"), m = d.find((l) => l.id === r);
620
+ m && V(m, { syncUrl: !1 });
621
+ }, [d, e, S, s]), _(() => {
622
+ if (!e)
623
+ return;
624
+ function r(l) {
625
+ var N;
626
+ if (l.key === "Shift" && be(!0), l.key === "Escape") {
627
+ l.preventDefault(), t == null || t(!1);
628
+ return;
629
+ }
630
+ Kt(l.target) || ((l.key === "n" || l.key === "ArrowDown") && (l.preventDefault(), Ee(1)), (l.key === "p" || l.key === "ArrowUp") && (l.preventDefault(), Ee(-1)), l.key === "c" && (l.preventDefault(), $("review"), (N = ee.current) == null || N.focus()), l.key === "f" && S && xe && (l.preventDefault(), Le(S)));
631
+ }
632
+ function m(l) {
633
+ l.key === "Shift" && be(!1);
634
+ }
635
+ return window.addEventListener("keydown", r), window.addEventListener("keyup", m), () => {
636
+ window.removeEventListener("keydown", r), window.removeEventListener("keyup", m);
637
+ };
638
+ });
639
+ const le = W((r) => {
640
+ const m = r.target instanceof Element ? r.target : null;
641
+ if (m)
642
+ return m.closest("[data-review-lens-ui]") ? null : m;
643
+ const l = document.elementFromPoint(r.clientX, r.clientY);
644
+ return !l || l.closest("[data-review-lens-ui]") ? null : l;
440
645
  }, []);
441
- if (P(() => {
442
- if (!e || h)
646
+ if (_(() => {
647
+ if (!e || !I)
443
648
  return;
444
- function u(C) {
445
- const B = x(C);
446
- k(B ? M(B) : void 0);
649
+ function r(l) {
650
+ const N = le(l);
651
+ y(N ? X(N) : void 0);
447
652
  }
448
- function E(C) {
449
- const B = x(C);
450
- B && (C.preventDefault(), C.stopPropagation(), f(M(B)));
653
+ function m(l) {
654
+ const N = le(l);
655
+ N && (l.preventDefault(), l.stopPropagation(), C(X(N)), $("review"));
451
656
  }
452
- return window.addEventListener("mousemove", u, !0), window.addEventListener("click", E, !0), () => {
453
- window.removeEventListener("mousemove", u, !0), window.removeEventListener("click", E, !0);
657
+ return window.addEventListener("mousemove", r, !0), window.addEventListener("click", m, !0), () => {
658
+ window.removeEventListener("mousemove", r, !0), window.removeEventListener("click", m, !0);
454
659
  };
455
- }, [x, h, e]), !e)
660
+ }, [I, le, e]), !e)
456
661
  return null;
457
- const T = h ?? l;
458
- async function J() {
459
- !h || !p.trim() || !i || !L || (await c({
460
- projectKey: r.projectKey,
461
- contentId: r.contentId,
462
- normalizedPath: v,
463
- originalUrl: r.currentUrl ?? window.location.href,
464
- selector: h.selector,
465
- selectorStrategy: h.selectorStrategy,
466
- elementFingerprint: h.fingerprint,
467
- cssSnapshot: h.cssSnapshot,
468
- comment: p.trim(),
469
- authorEmail: i.email
470
- }), w(""), f(void 0));
662
+ function V(r, m = { syncUrl: !0 }) {
663
+ var N;
664
+ if (H(r), C(void 0), $("feedback"), s && m.syncUrl !== !1) {
665
+ const te = new URL(window.location.href);
666
+ te.searchParams.set("reviewLensFeedback", r.id), window.history.replaceState({}, "", te);
667
+ }
668
+ const l = z(r.selector);
669
+ if (!l) {
670
+ y(void 0);
671
+ return;
672
+ }
673
+ (N = l.scrollIntoView) == null || N.call(l, { behavior: "smooth", block: "center", inline: "center" }), window.requestAnimationFrame(() => {
674
+ y(X(l));
675
+ });
471
676
  }
472
- return /* @__PURE__ */ S("div", { className: "review-lens-root", "data-review-lens-ui": !0, children: [
473
- T ? /* @__PURE__ */ a(le, { target: T, locked: !!h }) : null,
474
- /* @__PURE__ */ a(
475
- he,
476
- {
477
- feedback: R,
478
- selectedFeedback: g,
479
- onSelect: b
677
+ function Ee(r) {
678
+ if (L.length === 0)
679
+ return;
680
+ const m = S ? L.findIndex((N) => N.id === S.id) : -1, l = m < 0 ? r > 0 ? 0 : L.length - 1 : (m + r + L.length) % L.length;
681
+ V(L[l]);
682
+ }
683
+ async function Ae() {
684
+ if (!w || !B.trim() || !u || !T)
685
+ return;
686
+ let r = await b({
687
+ projectKey: f.projectKey,
688
+ contentId: f.contentId,
689
+ normalizedPath: v,
690
+ originalUrl: f.currentUrl ?? window.location.href,
691
+ selector: w.selector,
692
+ selectorStrategy: w.selectorStrategy,
693
+ elementFingerprint: w.fingerprint,
694
+ createdCssSnapshot: w.cssSnapshot,
695
+ comment: B.trim(),
696
+ status: "open",
697
+ severity: ue,
698
+ category: pe,
699
+ assigneeEmail: me.trim() || void 0,
700
+ viewportWidth: window.innerWidth,
701
+ viewportHeight: window.innerHeight,
702
+ viewportPreset: fe,
703
+ screenshotUrl: void 0,
704
+ screenshotThumbnailUrl: void 0,
705
+ authorEmail: u.email
706
+ });
707
+ if (f.captureScreenshot)
708
+ try {
709
+ const m = await f.captureScreenshot(w), l = await h(r.id, {
710
+ type: "screenshot",
711
+ data: m,
712
+ createdBy: u.email
713
+ });
714
+ r = await E(r.id, {
715
+ attachments: [l],
716
+ screenshotUrl: l.url,
717
+ screenshotThumbnailUrl: l.thumbnailUrl
718
+ });
719
+ } catch {
480
720
  }
481
- ),
482
- /* @__PURE__ */ S("aside", { className: `review-lens-panel review-lens-panel--${t}`, "data-review-lens-ui": !0, children: [
483
- /* @__PURE__ */ S("header", { className: "review-lens-panel__header", children: [
484
- /* @__PURE__ */ S("div", { children: [
485
- /* @__PURE__ */ a("p", { className: "review-lens-kicker", children: "Review Lens" }),
486
- /* @__PURE__ */ a("h2", { children: h ? "Element locked" : "Inspecting" })
721
+ D(""), ge(""), C(void 0), y(void 0), $("feedback"), H(r);
722
+ }
723
+ async function rt(r, m) {
724
+ const l = (/* @__PURE__ */ new Date()).toISOString(), N = m === "resolved" ? { status: m, resolvedAt: l, resolvedBy: u == null ? void 0 : u.email } : { status: m }, te = await E(r.id, N);
725
+ H(te);
726
+ }
727
+ async function Le(r) {
728
+ const m = z(r.selector);
729
+ if (!m || !u)
730
+ return;
731
+ const l = X(m), N = await E(r.id, {
732
+ status: "fixed",
733
+ fixedCssSnapshot: l.cssSnapshot,
734
+ fixedAt: (/* @__PURE__ */ new Date()).toISOString(),
735
+ fixedBy: u.email
736
+ });
737
+ H(N);
738
+ }
739
+ async function at(r) {
740
+ if (!se.trim() || !u || !Ne)
741
+ return;
742
+ const m = await A({
743
+ feedbackId: r.id,
744
+ body: se.trim(),
745
+ authorEmail: u.email
746
+ });
747
+ Ce((l) => ({
748
+ ...l,
749
+ [r.id]: [...l[r.id] ?? [], m]
750
+ })), oe("");
751
+ }
752
+ return /* @__PURE__ */ o("div", { className: "review-lens-root", "data-review-lens-ui": !0, children: [
753
+ I && P ? /* @__PURE__ */ i($t, { target: P, locked: !!w }) : null,
754
+ I && w && R && Ge ? /* @__PURE__ */ i(_t, { from: w, to: R }) : null,
755
+ I ? /* @__PURE__ */ o(ie, { children: [
756
+ /* @__PURE__ */ i(
757
+ Pt,
758
+ {
759
+ feedback: L,
760
+ selectedFeedback: S,
761
+ onSelect: V
762
+ }
763
+ ),
764
+ /* @__PURE__ */ i(
765
+ Wt,
766
+ {
767
+ feedback: L,
768
+ selectedFeedback: S,
769
+ onSelect: V
770
+ }
771
+ )
772
+ ] }) : null,
773
+ /* @__PURE__ */ o("aside", { className: `review-lens-panel review-lens-panel--${n}`, "data-review-lens-ui": !0, children: [
774
+ /* @__PURE__ */ o("header", { className: "review-lens-panel__header", children: [
775
+ /* @__PURE__ */ o("div", { children: [
776
+ /* @__PURE__ */ i("p", { className: "review-lens-kicker", children: "Review Lens" }),
777
+ /* @__PURE__ */ i("h2", { children: M === "summary" ? "Summary" : M === "feedback" ? "Feedback" : w ? "Element locked" : "Inspecting" })
487
778
  ] }),
488
- /* @__PURE__ */ a("button", { type: "button", onClick: () => o == null ? void 0 : o(!1), children: "Close" })
779
+ /* @__PURE__ */ i("button", { type: "button", onClick: () => t == null ? void 0 : t(!1), children: "Close" })
489
780
  ] }),
490
- T ? /* @__PURE__ */ a(ue, { target: T }) : /* @__PURE__ */ a("p", { children: "Move over the app to inspect." }),
491
- h ? /* @__PURE__ */ S(
492
- "form",
493
- {
494
- className: "review-lens-feedback-form",
495
- onSubmit: (u) => {
496
- u.preventDefault(), J();
497
- },
498
- children: [
499
- /* @__PURE__ */ a("label", { htmlFor: "review-lens-comment", children: "Feedback" }),
500
- /* @__PURE__ */ a(
501
- "textarea",
502
- {
503
- id: "review-lens-comment",
504
- value: p,
505
- disabled: !L,
506
- onChange: (u) => w(u.target.value),
507
- placeholder: L ? "Describe the UX issue..." : "You do not have permission to comment."
781
+ /* @__PURE__ */ o("div", { className: "review-lens-panel__body", children: [
782
+ /* @__PURE__ */ o("div", { className: "review-lens-mode-switch", role: "tablist", "aria-label": "Review Lens mode", children: [
783
+ /* @__PURE__ */ i(
784
+ "button",
785
+ {
786
+ type: "button",
787
+ role: "tab",
788
+ "aria-selected": M === "review",
789
+ onClick: () => $("review"),
790
+ children: "Review"
791
+ }
792
+ ),
793
+ /* @__PURE__ */ o(
794
+ "button",
795
+ {
796
+ type: "button",
797
+ role: "tab",
798
+ "aria-selected": M === "feedback",
799
+ onClick: () => $("feedback"),
800
+ children: [
801
+ "Feedback ",
802
+ /* @__PURE__ */ i("span", { children: L.length })
803
+ ]
804
+ }
805
+ ),
806
+ /* @__PURE__ */ i(
807
+ "button",
808
+ {
809
+ type: "button",
810
+ role: "tab",
811
+ "aria-selected": M === "summary",
812
+ onClick: () => $("summary"),
813
+ children: "Summary"
814
+ }
815
+ )
816
+ ] }),
817
+ M === "review" ? /* @__PURE__ */ o("div", { className: "review-lens-review-pane", role: "tabpanel", children: [
818
+ /* @__PURE__ */ o("div", { className: "review-lens-inspection", children: [
819
+ I ? null : /* @__PURE__ */ i("p", { children: "Authenticate with Google to inspect this page." }),
820
+ I && P ? /* @__PURE__ */ o(ie, { children: [
821
+ /* @__PURE__ */ i(Dt, { target: P }),
822
+ /* @__PURE__ */ i(_e, { title: "Accessibility", items: zt(P) }),
823
+ /* @__PURE__ */ i(
824
+ _e,
825
+ {
826
+ title: "Design tokens",
827
+ items: Ht(P.cssSnapshot, f.designTokens)
828
+ }
829
+ )
830
+ ] }) : null,
831
+ I && !P ? /* @__PURE__ */ i("p", { children: "Move over the app to inspect." }) : null
832
+ ] }),
833
+ et ? /* @__PURE__ */ i("div", { className: "review-lens-composer-panel", children: /* @__PURE__ */ o(
834
+ "form",
835
+ {
836
+ className: "review-lens-feedback-form",
837
+ onSubmit: (r) => {
838
+ r.preventDefault(), Ae();
839
+ },
840
+ children: [
841
+ /* @__PURE__ */ i("label", { htmlFor: "review-lens-comment", children: "New feedback" }),
842
+ /* @__PURE__ */ i(
843
+ "textarea",
844
+ {
845
+ ref: ee,
846
+ id: "review-lens-comment",
847
+ value: B,
848
+ disabled: !T,
849
+ onChange: (r) => D(r.target.value),
850
+ onKeyDown: (r) => {
851
+ r.key === "Enter" && r.metaKey && (r.preventDefault(), Ae());
852
+ },
853
+ placeholder: T ? "Describe the UX issue..." : "You do not have permission to comment."
854
+ }
855
+ ),
856
+ /* @__PURE__ */ o("div", { className: "review-lens-form-grid", children: [
857
+ /* @__PURE__ */ o("label", { children: [
858
+ "Severity",
859
+ /* @__PURE__ */ i(
860
+ "select",
861
+ {
862
+ value: ue,
863
+ onChange: (r) => Je(r.target.value),
864
+ disabled: !T,
865
+ children: je.map((r) => /* @__PURE__ */ i("option", { value: r, children: Q[r] }, r))
866
+ }
867
+ )
868
+ ] }),
869
+ /* @__PURE__ */ o("label", { children: [
870
+ "Type",
871
+ /* @__PURE__ */ i(
872
+ "select",
873
+ {
874
+ value: pe,
875
+ onChange: (r) => qe(r.target.value),
876
+ disabled: !T,
877
+ children: Oe.map((r) => /* @__PURE__ */ i("option", { value: r, children: Z[r] }, r))
878
+ }
879
+ )
880
+ ] }),
881
+ /* @__PURE__ */ o("label", { children: [
882
+ "Assignee",
883
+ /* @__PURE__ */ i(
884
+ "input",
885
+ {
886
+ value: me,
887
+ onChange: (r) => ge(r.target.value),
888
+ disabled: !T,
889
+ placeholder: "optional@email.com"
890
+ }
891
+ )
892
+ ] }),
893
+ /* @__PURE__ */ o("label", { children: [
894
+ "Viewport",
895
+ /* @__PURE__ */ i(
896
+ "select",
897
+ {
898
+ value: fe,
899
+ onChange: (r) => Ve(r.target.value),
900
+ disabled: !T,
901
+ children: c.map((r) => /* @__PURE__ */ i("option", { value: r.value, children: r.label }, r.value))
902
+ }
903
+ )
904
+ ] })
905
+ ] }),
906
+ T ? /* @__PURE__ */ o("p", { className: "review-lens-feedback-form__hint", children: [
907
+ "Press ",
908
+ /* @__PURE__ */ i("kbd", { children: "Command" }),
909
+ " + ",
910
+ /* @__PURE__ */ i("kbd", { children: "Enter" }),
911
+ " to submit.",
912
+ tt ? " Screenshot capture runs after save." : ""
913
+ ] }) : null,
914
+ /* @__PURE__ */ i("div", { className: "review-lens-actions", children: /* @__PURE__ */ i("button", { type: "submit", disabled: !B.trim() || !T, children: "Save feedback" }) })
915
+ ]
916
+ }
917
+ ) }) : null
918
+ ] }) : null,
919
+ M === "feedback" ? /* @__PURE__ */ o("div", { className: "review-lens-comments", children: [
920
+ /* @__PURE__ */ i(
921
+ It,
922
+ {
923
+ open: Ye,
924
+ activeCount: it,
925
+ statusFilter: j,
926
+ severityFilter: O,
927
+ categoryFilter: K,
928
+ assigneeFilter: J,
929
+ viewportFilter: q,
930
+ assignees: nt,
931
+ responsivePresets: c,
932
+ onStatusChange: ve,
933
+ onSeverityChange: we,
934
+ onCategoryChange: ye,
935
+ onAssigneeChange: Se,
936
+ onViewportChange: ke,
937
+ onToggle: () => Xe((r) => !r),
938
+ onClear: () => {
939
+ ve("all"), we("all"), ye("all"), Se("all"), ke("all");
508
940
  }
509
- ),
510
- /* @__PURE__ */ S("div", { className: "review-lens-actions", children: [
511
- /* @__PURE__ */ a("button", { type: "button", onClick: () => f(void 0), children: "Unlock" }),
512
- /* @__PURE__ */ a("button", { type: "submit", disabled: !p.trim() || !L, children: "Save feedback" })
941
+ }
942
+ ),
943
+ /* @__PURE__ */ o("div", { className: "review-lens-list-panel", children: [
944
+ /* @__PURE__ */ o("div", { className: "review-lens-comments__header", children: [
945
+ /* @__PURE__ */ i("h3", { children: "All feedback" }),
946
+ /* @__PURE__ */ i("span", { children: L.length })
947
+ ] }),
948
+ /* @__PURE__ */ o("div", { className: "review-lens-comments__list", children: [
949
+ L.length === 0 ? /* @__PURE__ */ i("p", { children: "No feedback for this view." }) : null,
950
+ L.map((r) => /* @__PURE__ */ i(
951
+ Tt,
952
+ {
953
+ item: r,
954
+ selected: (S == null ? void 0 : S.id) === r.id,
955
+ onSelect: V
956
+ },
957
+ r.id
958
+ ))
513
959
  ] })
514
- ]
515
- }
516
- ) : null,
517
- /* @__PURE__ */ S("section", { className: "review-lens-comments", children: [
518
- /* @__PURE__ */ a("h3", { children: "Page feedback" }),
519
- R.length === 0 ? /* @__PURE__ */ a("p", { children: "No feedback for this view." }) : null,
520
- R.map((u) => /* @__PURE__ */ S(
521
- "article",
960
+ ] }),
961
+ S ? /* @__PURE__ */ o("div", { className: "review-lens-selected-panel", children: [
962
+ /* @__PURE__ */ i("div", { className: "review-lens-selected-panel__label", children: "Selected feedback" }),
963
+ /* @__PURE__ */ i(
964
+ Rt,
965
+ {
966
+ item: S,
967
+ messages: Qe[S.id] ?? [],
968
+ messageDraft: se,
969
+ canReply: Ne,
970
+ canUpdate: xe,
971
+ canAssign: Ze,
972
+ onMessageDraftChange: oe,
973
+ onSubmitMessage: () => void at(S),
974
+ onStatusChange: (r) => void rt(S, r),
975
+ onAssigneeChange: (r) => void E(S.id, {
976
+ assigneeEmail: r.trim() || void 0
977
+ }).then(H),
978
+ onMarkFixed: () => void Le(S)
979
+ },
980
+ S.id
981
+ )
982
+ ] }) : /* @__PURE__ */ o("div", { className: "review-lens-selected-panel review-lens-selected-panel--empty", children: [
983
+ /* @__PURE__ */ i("div", { className: "review-lens-selected-panel__label", children: "Selected feedback" }),
984
+ /* @__PURE__ */ i("p", { children: "Select a feedback item above to review status, assignment, drift, and replies." })
985
+ ] })
986
+ ] }) : null,
987
+ M === "summary" ? /* @__PURE__ */ i(Bt, { feedback: d }) : null
988
+ ] })
989
+ ] })
990
+ ] });
991
+ }
992
+ function It({
993
+ open: e,
994
+ activeCount: t,
995
+ statusFilter: n,
996
+ severityFilter: a,
997
+ categoryFilter: s,
998
+ assigneeFilter: c,
999
+ viewportFilter: g,
1000
+ assignees: f,
1001
+ responsivePresets: u,
1002
+ onStatusChange: d,
1003
+ onSeverityChange: v,
1004
+ onCategoryChange: p,
1005
+ onAssigneeChange: b,
1006
+ onViewportChange: E,
1007
+ onToggle: x,
1008
+ onClear: A
1009
+ }) {
1010
+ return /* @__PURE__ */ o("div", { className: "review-lens-filter-shell", children: [
1011
+ /* @__PURE__ */ o("div", { className: "review-lens-filter-bar", children: [
1012
+ /* @__PURE__ */ o("button", { type: "button", "aria-expanded": e, onClick: x, children: [
1013
+ "Filters",
1014
+ t > 0 ? /* @__PURE__ */ i("span", { children: t }) : null
1015
+ ] }),
1016
+ t > 0 ? /* @__PURE__ */ i("button", { type: "button", onClick: A, children: "Clear" }) : null
1017
+ ] }),
1018
+ e ? /* @__PURE__ */ o("div", { className: "review-lens-filters", children: [
1019
+ /* @__PURE__ */ o("label", { children: [
1020
+ "Status",
1021
+ /* @__PURE__ */ o(
1022
+ "select",
1023
+ {
1024
+ "aria-label": "Filter status",
1025
+ value: n,
1026
+ onChange: (h) => d(h.target.value),
1027
+ children: [
1028
+ /* @__PURE__ */ i("option", { value: "all", children: "All statuses" }),
1029
+ He.map((h) => /* @__PURE__ */ i("option", { value: h, children: ae[h] }, h))
1030
+ ]
1031
+ }
1032
+ )
1033
+ ] }),
1034
+ /* @__PURE__ */ o("label", { children: [
1035
+ "Priority",
1036
+ /* @__PURE__ */ o(
1037
+ "select",
1038
+ {
1039
+ "aria-label": "Filter severity",
1040
+ value: a,
1041
+ onChange: (h) => v(h.target.value),
1042
+ children: [
1043
+ /* @__PURE__ */ i("option", { value: "all", children: "All priorities" }),
1044
+ je.map((h) => /* @__PURE__ */ i("option", { value: h, children: Q[h] }, h))
1045
+ ]
1046
+ }
1047
+ )
1048
+ ] }),
1049
+ /* @__PURE__ */ o("label", { children: [
1050
+ "Type",
1051
+ /* @__PURE__ */ o(
1052
+ "select",
1053
+ {
1054
+ "aria-label": "Filter type",
1055
+ value: s,
1056
+ onChange: (h) => p(h.target.value),
1057
+ children: [
1058
+ /* @__PURE__ */ i("option", { value: "all", children: "All types" }),
1059
+ Oe.map((h) => /* @__PURE__ */ i("option", { value: h, children: Z[h] }, h))
1060
+ ]
1061
+ }
1062
+ )
1063
+ ] }),
1064
+ /* @__PURE__ */ o("label", { children: [
1065
+ "Assignee",
1066
+ /* @__PURE__ */ o(
1067
+ "select",
1068
+ {
1069
+ "aria-label": "Filter assignee",
1070
+ value: c,
1071
+ onChange: (h) => b(h.target.value),
1072
+ children: [
1073
+ /* @__PURE__ */ i("option", { value: "all", children: "All assignees" }),
1074
+ f.map((h) => /* @__PURE__ */ i("option", { value: h, children: h }, h))
1075
+ ]
1076
+ }
1077
+ )
1078
+ ] }),
1079
+ /* @__PURE__ */ o("label", { children: [
1080
+ "Viewport",
1081
+ /* @__PURE__ */ o(
1082
+ "select",
522
1083
  {
523
- className: (g == null ? void 0 : g.id) === u.id ? "review-lens-comment review-lens-comment--selected" : "review-lens-comment",
1084
+ "aria-label": "Filter viewport",
1085
+ value: g,
1086
+ onChange: (h) => E(h.target.value),
524
1087
  children: [
525
- /* @__PURE__ */ a("p", { children: u.comment }),
526
- /* @__PURE__ */ a("span", { children: u.authorEmail }),
527
- u.status === "open" && I ? /* @__PURE__ */ a("button", { type: "button", onClick: () => void s(u.id), children: "Resolve" }) : null
1088
+ /* @__PURE__ */ i("option", { value: "all", children: "All viewports" }),
1089
+ u.map((h) => /* @__PURE__ */ i("option", { value: h.value, children: h.label }, h.value))
528
1090
  ]
529
- },
530
- u.id
531
- ))
1091
+ }
1092
+ )
532
1093
  ] })
1094
+ ] }) : null
1095
+ ] });
1096
+ }
1097
+ function Tt({
1098
+ item: e,
1099
+ selected: t,
1100
+ onSelect: n
1101
+ }) {
1102
+ const a = Ke(e);
1103
+ return /* @__PURE__ */ o(
1104
+ "article",
1105
+ {
1106
+ tabIndex: 0,
1107
+ className: [
1108
+ "review-lens-comment",
1109
+ `review-lens-comment--${e.severity}`,
1110
+ t ? "review-lens-comment--selected" : ""
1111
+ ].filter(Boolean).join(" "),
1112
+ onClick: () => n(e),
1113
+ onKeyDown: (s) => {
1114
+ (s.key === "Enter" || s.key === " ") && (s.preventDefault(), n(e));
1115
+ },
1116
+ children: [
1117
+ /* @__PURE__ */ o("div", { className: "review-lens-comment__header", children: [
1118
+ /* @__PURE__ */ i("span", { children: ae[e.status] }),
1119
+ /* @__PURE__ */ i("strong", { children: Q[e.severity] })
1120
+ ] }),
1121
+ /* @__PURE__ */ o("div", { className: "review-lens-comment__content", children: [
1122
+ /* @__PURE__ */ i("p", { children: e.comment }),
1123
+ /* @__PURE__ */ o("span", { children: [
1124
+ e.authorEmail,
1125
+ e.assigneeEmail ? ` -> ${e.assigneeEmail}` : ""
1126
+ ] })
1127
+ ] }),
1128
+ /* @__PURE__ */ o("div", { className: "review-lens-tags", children: [
1129
+ /* @__PURE__ */ i("span", { children: Z[e.category] }),
1130
+ /* @__PURE__ */ i("span", { children: e.viewportPreset }),
1131
+ /* @__PURE__ */ i("span", { children: a.label })
1132
+ ] })
1133
+ ]
1134
+ }
1135
+ );
1136
+ }
1137
+ function Rt({
1138
+ item: e,
1139
+ messages: t,
1140
+ messageDraft: n,
1141
+ canReply: a,
1142
+ canUpdate: s,
1143
+ canAssign: c,
1144
+ onMessageDraftChange: g,
1145
+ onSubmitMessage: f,
1146
+ onStatusChange: u,
1147
+ onAssigneeChange: d,
1148
+ onMarkFixed: v
1149
+ }) {
1150
+ const p = Ke(e);
1151
+ return /* @__PURE__ */ o("section", { className: "review-lens-detail", "aria-label": "Selected feedback detail", children: [
1152
+ /* @__PURE__ */ o("div", { className: "review-lens-detail__header", children: [
1153
+ /* @__PURE__ */ o("h3", { children: [
1154
+ Z[e.category],
1155
+ " feedback"
1156
+ ] }),
1157
+ /* @__PURE__ */ i("strong", { children: Q[e.severity] })
1158
+ ] }),
1159
+ /* @__PURE__ */ i("blockquote", { children: e.comment }),
1160
+ /* @__PURE__ */ o("dl", { className: "review-lens-detail-meta", children: [
1161
+ /* @__PURE__ */ o("div", { children: [
1162
+ /* @__PURE__ */ i("dt", { children: "Target" }),
1163
+ /* @__PURE__ */ i("dd", { children: p.label })
1164
+ ] }),
1165
+ /* @__PURE__ */ o("div", { children: [
1166
+ /* @__PURE__ */ i("dt", { children: "Viewport" }),
1167
+ /* @__PURE__ */ i("dd", { children: e.viewportPreset })
1168
+ ] }),
1169
+ e.screenshotUrl ? /* @__PURE__ */ o("div", { children: [
1170
+ /* @__PURE__ */ i("dt", { children: "Evidence" }),
1171
+ /* @__PURE__ */ i("dd", { children: /* @__PURE__ */ i("a", { href: e.screenshotUrl, target: "_blank", rel: "noreferrer", children: "Screenshot" }) })
1172
+ ] }) : null
1173
+ ] }),
1174
+ /* @__PURE__ */ o("div", { className: "review-lens-form-grid", children: [
1175
+ /* @__PURE__ */ o("label", { children: [
1176
+ "Status",
1177
+ /* @__PURE__ */ i(
1178
+ "select",
1179
+ {
1180
+ value: e.status,
1181
+ disabled: !s,
1182
+ onChange: (b) => u(b.target.value),
1183
+ children: He.map((b) => /* @__PURE__ */ i("option", { value: b, children: ae[b] }, b))
1184
+ }
1185
+ )
1186
+ ] }),
1187
+ /* @__PURE__ */ o("label", { children: [
1188
+ "Assignee",
1189
+ /* @__PURE__ */ i(
1190
+ "input",
1191
+ {
1192
+ defaultValue: e.assigneeEmail ?? "",
1193
+ disabled: !c,
1194
+ onBlur: (b) => d(b.target.value),
1195
+ placeholder: "optional@email.com"
1196
+ }
1197
+ )
1198
+ ] })
1199
+ ] }),
1200
+ /* @__PURE__ */ o("div", { className: "review-lens-status-actions", children: [
1201
+ /* @__PURE__ */ i(
1202
+ "button",
1203
+ {
1204
+ type: "button",
1205
+ className: "review-lens-button-secondary",
1206
+ disabled: !s,
1207
+ onClick: v,
1208
+ children: "Mark fixed"
1209
+ }
1210
+ ),
1211
+ /* @__PURE__ */ i(
1212
+ "button",
1213
+ {
1214
+ type: "button",
1215
+ className: "review-lens-button-primary",
1216
+ disabled: !s,
1217
+ onClick: () => u("resolved"),
1218
+ children: "Resolve"
1219
+ }
1220
+ )
1221
+ ] }),
1222
+ /* @__PURE__ */ o("div", { className: "review-lens-thread", children: [
1223
+ /* @__PURE__ */ o("div", { className: "review-lens-thread__header", children: [
1224
+ /* @__PURE__ */ i("h3", { children: "Thread" }),
1225
+ /* @__PURE__ */ i("span", { children: t.length })
1226
+ ] }),
1227
+ t.length === 0 ? /* @__PURE__ */ i("p", { children: "No replies yet." }) : null,
1228
+ t.map((b) => /* @__PURE__ */ o("div", { className: "review-lens-thread__message", children: [
1229
+ /* @__PURE__ */ i("p", { children: b.body }),
1230
+ /* @__PURE__ */ i("span", { children: b.authorEmail })
1231
+ ] }, b.id)),
1232
+ /* @__PURE__ */ i(
1233
+ "textarea",
1234
+ {
1235
+ "aria-label": "Reply",
1236
+ value: n,
1237
+ disabled: !a,
1238
+ onChange: (b) => g(b.target.value),
1239
+ placeholder: a ? "Reply..." : "You do not have permission to reply."
1240
+ }
1241
+ ),
1242
+ /* @__PURE__ */ i("div", { className: "review-lens-actions", children: /* @__PURE__ */ i("button", { type: "button", disabled: !n.trim() || !a, onClick: f, children: "Reply" }) })
533
1243
  ] })
534
1244
  ] });
535
1245
  }
536
- function le({ target: e, locked: o }) {
537
- const t = me(e);
538
- return /* @__PURE__ */ S(
1246
+ function Bt({ feedback: e }) {
1247
+ return /* @__PURE__ */ o("div", { className: "review-lens-summary", role: "tabpanel", children: [
1248
+ /* @__PURE__ */ i(G, { title: "Status", values: Y(e, (t) => ae[t.status]) }),
1249
+ /* @__PURE__ */ i(G, { title: "Severity", values: Y(e, (t) => Q[t.severity]) }),
1250
+ /* @__PURE__ */ i(G, { title: "Type", values: Y(e, (t) => Z[t.category]) }),
1251
+ /* @__PURE__ */ i(G, { title: "Assignee", values: Y(e, (t) => t.assigneeEmail ?? "Unassigned") }),
1252
+ /* @__PURE__ */ i(G, { title: "Viewport", values: Y(e, (t) => t.viewportPreset) })
1253
+ ] });
1254
+ }
1255
+ function G({ title: e, values: t }) {
1256
+ return /* @__PURE__ */ o("section", { children: [
1257
+ /* @__PURE__ */ i("h3", { children: e }),
1258
+ /* @__PURE__ */ i("dl", { children: t.map(([n, a]) => /* @__PURE__ */ o("div", { children: [
1259
+ /* @__PURE__ */ i("dt", { children: n }),
1260
+ /* @__PURE__ */ i("dd", { children: a })
1261
+ ] }, n)) })
1262
+ ] });
1263
+ }
1264
+ function $t({ target: e, locked: t }) {
1265
+ const n = Vt(e), a = jt(e.fingerprint);
1266
+ return /* @__PURE__ */ o(
539
1267
  "div",
540
1268
  {
541
- className: o ? "review-lens-highlight review-lens-highlight--locked" : "review-lens-highlight",
1269
+ className: t ? "review-lens-highlight review-lens-highlight--locked" : "review-lens-highlight",
542
1270
  style: {
543
- top: t.margin.top,
544
- left: t.margin.left,
545
- width: t.margin.width,
546
- height: t.margin.height
1271
+ top: n.margin.top,
1272
+ left: n.margin.left,
1273
+ width: n.margin.width,
1274
+ height: n.margin.height
547
1275
  },
548
1276
  children: [
549
- /* @__PURE__ */ a(
1277
+ /* @__PURE__ */ i(
550
1278
  "div",
551
1279
  {
552
1280
  className: "review-lens-highlight__border",
553
1281
  style: {
554
- top: t.border.top - t.margin.top,
555
- left: t.border.left - t.margin.left,
556
- width: t.border.width,
557
- height: t.border.height
1282
+ top: n.border.top - n.margin.top,
1283
+ left: n.border.left - n.margin.left,
1284
+ width: n.border.width,
1285
+ height: n.border.height
558
1286
  }
559
1287
  }
560
1288
  ),
561
- /* @__PURE__ */ a(
1289
+ /* @__PURE__ */ i(
562
1290
  "div",
563
1291
  {
564
1292
  className: "review-lens-highlight__padding",
565
1293
  style: {
566
- top: t.padding.top - t.margin.top,
567
- left: t.padding.left - t.margin.left,
568
- width: t.padding.width,
569
- height: t.padding.height
1294
+ top: n.padding.top - n.margin.top,
1295
+ left: n.padding.left - n.margin.left,
1296
+ width: n.padding.width,
1297
+ height: n.padding.height
570
1298
  }
571
1299
  }
572
1300
  ),
573
- /* @__PURE__ */ a(
1301
+ /* @__PURE__ */ i(
574
1302
  "div",
575
1303
  {
576
1304
  className: "review-lens-highlight__content",
577
1305
  style: {
578
- top: t.content.top - t.margin.top,
579
- left: t.content.left - t.margin.left,
580
- width: t.content.width,
581
- height: t.content.height
1306
+ top: n.content.top - n.margin.top,
1307
+ left: n.content.left - n.margin.left,
1308
+ width: n.content.width,
1309
+ height: n.content.height
582
1310
  }
583
1311
  }
584
1312
  ),
585
- /* @__PURE__ */ S("div", { className: "review-lens-highlight__label", children: [
586
- Math.round(e.rect.width),
587
- " x ",
588
- Math.round(e.rect.height)
1313
+ /* @__PURE__ */ o("div", { className: "review-lens-highlight__label", children: [
1314
+ /* @__PURE__ */ i("strong", { children: a }),
1315
+ /* @__PURE__ */ o("span", { children: [
1316
+ Math.round(e.rect.width),
1317
+ " x ",
1318
+ Math.round(e.rect.height)
1319
+ ] })
589
1320
  ] })
590
1321
  ]
591
1322
  }
592
1323
  );
593
1324
  }
594
- function he({
1325
+ function _t({ from: e, to: t }) {
1326
+ const n = Ot(e.rect, t.rect);
1327
+ return n.length === 0 ? null : /* @__PURE__ */ i(ie, { children: n.map((a) => /* @__PURE__ */ i(
1328
+ "div",
1329
+ {
1330
+ className: `review-lens-distance review-lens-distance--${a.axis}`,
1331
+ style: {
1332
+ top: a.top,
1333
+ left: a.left,
1334
+ width: a.width,
1335
+ height: a.height
1336
+ },
1337
+ children: /* @__PURE__ */ i("span", { children: a.label })
1338
+ },
1339
+ a.key
1340
+ )) });
1341
+ }
1342
+ function Pt({
1343
+ feedback: e,
1344
+ selectedFeedback: t,
1345
+ onSelect: n
1346
+ }) {
1347
+ return /* @__PURE__ */ i(ie, { children: e.map((a) => /* @__PURE__ */ i(
1348
+ Ut,
1349
+ {
1350
+ feedback: a,
1351
+ selected: (t == null ? void 0 : t.id) === a.id,
1352
+ onSelect: n
1353
+ },
1354
+ a.id
1355
+ )) });
1356
+ }
1357
+ function Ut({
1358
+ feedback: e,
1359
+ selected: t,
1360
+ onSelect: n
1361
+ }) {
1362
+ const a = We(null);
1363
+ return lt(() => {
1364
+ let s = 0;
1365
+ const c = () => {
1366
+ s = 0;
1367
+ const f = a.current, u = z(e.selector), d = u == null ? void 0 : u.getBoundingClientRect();
1368
+ !f || !d || (f.style.top = `${d.top}px`, f.style.left = `${d.right}px`, f.hidden = d.bottom < 0 || d.top > window.innerHeight);
1369
+ }, g = () => {
1370
+ s || (s = window.requestAnimationFrame(c));
1371
+ };
1372
+ return c(), window.addEventListener("scroll", g, !0), window.addEventListener("resize", g), () => {
1373
+ s && window.cancelAnimationFrame(s), window.removeEventListener("scroll", g, !0), window.removeEventListener("resize", g);
1374
+ };
1375
+ }, [e.selector]), /* @__PURE__ */ i(
1376
+ "button",
1377
+ {
1378
+ ref: a,
1379
+ type: "button",
1380
+ className: t ? "review-lens-marker review-lens-marker--selected" : "review-lens-marker",
1381
+ onClick: () => n(e),
1382
+ "aria-label": `Open feedback from ${e.authorEmail}`
1383
+ }
1384
+ );
1385
+ }
1386
+ function Wt({
595
1387
  feedback: e,
596
- selectedFeedback: o,
597
- onSelect: t
1388
+ selectedFeedback: t,
1389
+ onSelect: n
598
1390
  }) {
599
- return /* @__PURE__ */ a(G, { children: e.map((n) => {
600
- const r = pe(n.selector), i = r == null ? void 0 : r.getBoundingClientRect();
601
- return i ? /* @__PURE__ */ a(
602
- "button",
603
- {
604
- type: "button",
605
- className: (o == null ? void 0 : o.id) === n.id ? "review-lens-marker review-lens-marker--selected" : "review-lens-marker",
606
- style: {
607
- top: i.top,
608
- left: i.left + i.width
609
- },
610
- onClick: () => t(n),
611
- "aria-label": `Open feedback from ${n.authorEmail}`
612
- },
613
- n.id
614
- ) : null;
615
- }) });
1391
+ const a = e.map((s) => {
1392
+ const c = z(s.selector), g = c == null ? void 0 : c.getBoundingClientRect(), f = Math.max(
1393
+ document.documentElement.scrollHeight,
1394
+ document.body.scrollHeight,
1395
+ window.innerHeight
1396
+ );
1397
+ return !g || f <= 0 ? null : {
1398
+ item: s,
1399
+ top: Math.min(100, Math.max(0, (g.top + window.scrollY) / f * 100))
1400
+ };
1401
+ }).filter((s) => s !== null);
1402
+ return a.length === 0 ? null : /* @__PURE__ */ i("div", { className: "review-lens-minimap", "data-review-lens-ui": !0, "aria-label": "Feedback map", children: a.map((s) => /* @__PURE__ */ i(
1403
+ "button",
1404
+ {
1405
+ type: "button",
1406
+ className: (t == null ? void 0 : t.id) === s.item.id ? "review-lens-minimap__point review-lens-minimap__point--selected" : "review-lens-minimap__point",
1407
+ style: { top: `${s.top}%` },
1408
+ onClick: () => n(s.item),
1409
+ "aria-label": `Jump to feedback from ${s.item.authorEmail}`
1410
+ },
1411
+ s.item.id
1412
+ )) });
616
1413
  }
617
- function ue({ target: e }) {
618
- const o = [
1414
+ function Dt({ target: e }) {
1415
+ const t = [
619
1416
  ["Selector", e.selector],
620
1417
  ["Size", `${e.cssSnapshot.width} x ${e.cssSnapshot.height}`],
621
1418
  ["Margin", e.cssSnapshot.margin],
622
1419
  ["Padding", e.cssSnapshot.padding],
623
1420
  ["Border", e.cssSnapshot.border],
1421
+ ["Radius", e.cssSnapshot.borderRadius],
624
1422
  ["Font", `${e.cssSnapshot.fontSize} / ${e.cssSnapshot.lineHeight}`],
625
1423
  ["Family", e.cssSnapshot.fontFamily],
626
1424
  ["Color", e.cssSnapshot.color],
627
1425
  ["Background", e.cssSnapshot.backgroundColor]
628
1426
  ];
629
- return /* @__PURE__ */ a("dl", { className: "review-lens-metrics", children: o.map(([t, n]) => /* @__PURE__ */ S("div", { children: [
630
- /* @__PURE__ */ a("dt", { children: t }),
631
- /* @__PURE__ */ a("dd", { children: n })
632
- ] }, t)) });
1427
+ return /* @__PURE__ */ i("dl", { className: "review-lens-metrics", children: t.map(([n, a]) => /* @__PURE__ */ o("div", { children: [
1428
+ /* @__PURE__ */ i("dt", { children: n }),
1429
+ /* @__PURE__ */ i("dd", { children: a })
1430
+ ] }, n)) });
633
1431
  }
634
- function pe(e) {
1432
+ function _e({ title: e, items: t }) {
1433
+ return /* @__PURE__ */ o("section", { className: "review-lens-insights", children: [
1434
+ /* @__PURE__ */ i("h3", { children: e }),
1435
+ t.length === 0 ? /* @__PURE__ */ i("p", { children: "No issues detected." }) : null,
1436
+ t.length > 0 ? /* @__PURE__ */ i("ul", { children: t.map((n) => /* @__PURE__ */ i("li", { children: n }, n)) }) : null
1437
+ ] });
1438
+ }
1439
+ function z(e) {
635
1440
  try {
636
1441
  return document.querySelector(e);
637
1442
  } catch {
638
1443
  return null;
639
1444
  }
640
1445
  }
641
- function me(e) {
642
- const o = {
643
- top: y(e.cssSnapshot.marginTop),
644
- right: y(e.cssSnapshot.marginRight),
645
- bottom: y(e.cssSnapshot.marginBottom),
646
- left: y(e.cssSnapshot.marginLeft)
647
- }, t = {
648
- top: y(e.cssSnapshot.borderTopWidth),
649
- right: y(e.cssSnapshot.borderRightWidth),
650
- bottom: y(e.cssSnapshot.borderBottomWidth),
651
- left: y(e.cssSnapshot.borderLeftWidth)
1446
+ function Ke(e) {
1447
+ const t = z(e.selector);
1448
+ if (!t)
1449
+ return { label: "Target missing", level: "warning" };
1450
+ const n = X(t);
1451
+ return n.fingerprint.tagName !== e.elementFingerprint.tagName ? { label: "Element changed", level: "warning" } : Math.abs(n.fingerprint.width - e.elementFingerprint.width) > 2 || Math.abs(n.fingerprint.height - e.elementFingerprint.height) > 2 ? { label: "Size changed", level: "warning" } : n.cssSnapshot.fontSize !== e.createdCssSnapshot.fontSize || n.cssSnapshot.color !== e.createdCssSnapshot.color || n.cssSnapshot.padding !== e.createdCssSnapshot.padding ? { label: "Style changed", level: "warning" } : { label: "Target unchanged", level: "ok" };
1452
+ }
1453
+ function zt(e) {
1454
+ var u;
1455
+ const t = z(e.selector);
1456
+ if (!t)
1457
+ return ["Selected element is no longer available."];
1458
+ const n = [], a = t.tagName.toLowerCase(), s = t.getAttribute("role"), c = ["button", "a", "input", "select", "textarea"].includes(a) || s === "button" || s === "link", g = t.getAttribute("aria-label") || t.getAttribute("title") || ((u = t.textContent) == null ? void 0 : u.trim());
1459
+ c && !g && n.push("Interactive element has no accessible name."), c && (e.rect.width < 44 || e.rect.height < 44) && n.push("Tap target is smaller than 44 x 44."), a === "img" && !t.getAttribute("alt") && n.push("Image is missing alt text.");
1460
+ const f = /^h[1-6]$/.test(a) ? Number(a.slice(1)) : 0;
1461
+ return f > 1 && !document.querySelector(`h${f - 1}`) && n.push("Heading may skip the previous level."), Jt(e.cssSnapshot.color, e.cssSnapshot.backgroundColor) && n.push("Text contrast may be low."), n;
1462
+ }
1463
+ function Ht(e, t = {}) {
1464
+ const n = [];
1465
+ return U("Padding", e.padding, t.spacing, n), U("Margin", e.margin, t.spacing, n), U("Font size", e.fontSize, t.fontSize, n), U("Line height", e.lineHeight, t.lineHeight, n), U("Text color", e.color, t.color, n), U("Background", e.backgroundColor, t.color, n), U("Radius", e.borderRadius, t.radius, n), n;
1466
+ }
1467
+ function U(e, t, n, a) {
1468
+ !n || n.length === 0 || !t || n.includes(t) || a.push(`${e} ${t} is outside configured tokens.`);
1469
+ }
1470
+ function jt(e) {
1471
+ const t = e.id ? `#${e.id}` : "", n = e.className ? `.${e.className.split(/\s+/).filter(Boolean).slice(0, 2).join(".")}` : "", a = e.ariaLabel ? `[aria-label="${e.ariaLabel}"]` : "";
1472
+ return `${e.tagName}${t}${n}${a}` || e.tagName;
1473
+ }
1474
+ function Ot(e, t) {
1475
+ const n = [], a = (Math.max(e.left, t.left) + Math.min(e.right, t.right)) / 2, s = (Math.max(e.top, t.top) + Math.min(e.bottom, t.bottom)) / 2;
1476
+ if (e.right <= t.left || t.right <= e.left) {
1477
+ const c = e.right <= t.left ? e.right : t.right, g = e.right <= t.left ? t.left : e.left;
1478
+ n.push({
1479
+ key: "horizontal",
1480
+ axis: "horizontal",
1481
+ top: Pe(s, 0, window.innerHeight),
1482
+ left: c,
1483
+ width: Math.max(g - c, 1),
1484
+ height: 1,
1485
+ label: `${Math.round(g - c)}px`
1486
+ });
1487
+ }
1488
+ if (e.bottom <= t.top || t.bottom <= e.top) {
1489
+ const c = e.bottom <= t.top ? e.bottom : t.bottom, g = e.bottom <= t.top ? t.top : e.top;
1490
+ n.push({
1491
+ key: "vertical",
1492
+ axis: "vertical",
1493
+ top: c,
1494
+ left: Pe(a, 0, window.innerWidth),
1495
+ width: 1,
1496
+ height: Math.max(g - c, 1),
1497
+ label: `${Math.round(g - c)}px`
1498
+ });
1499
+ }
1500
+ return n;
1501
+ }
1502
+ function Pe(e, t, n) {
1503
+ return Math.min(Math.max(e, t), n);
1504
+ }
1505
+ function Y(e, t) {
1506
+ const n = /* @__PURE__ */ new Map();
1507
+ for (const a of e) {
1508
+ const s = t(a);
1509
+ n.set(s, (n.get(s) ?? 0) + 1);
1510
+ }
1511
+ return Array.from(n.entries()).sort((a, s) => s[1] - a[1] || a[0].localeCompare(s[0]));
1512
+ }
1513
+ function Kt(e) {
1514
+ return e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement || e instanceof HTMLSelectElement || e instanceof HTMLElement && e.isContentEditable;
1515
+ }
1516
+ function Jt(e, t) {
1517
+ const n = Ue(e), a = Ue(t);
1518
+ return !n || !a || a.alpha === 0 ? !1 : qt(n, a) < 4.5;
1519
+ }
1520
+ function Ue(e) {
1521
+ const t = e.match(/rgba?\(([^)]+)\)/);
1522
+ if (!t)
1523
+ return null;
1524
+ const [n, a, s, c = "1"] = t[1].split(",").map((g) => g.trim());
1525
+ return {
1526
+ red: Number(n),
1527
+ green: Number(a),
1528
+ blue: Number(s),
1529
+ alpha: Number(c)
1530
+ };
1531
+ }
1532
+ function qt(e, t) {
1533
+ const n = Math.max(ne(e), ne(t)), a = Math.min(ne(e), ne(t));
1534
+ return (n + 0.05) / (a + 0.05);
1535
+ }
1536
+ function ne(e) {
1537
+ const t = [e.red, e.green, e.blue].map((n) => {
1538
+ const a = n / 255;
1539
+ return a <= 0.03928 ? a / 12.92 : ((a + 0.055) / 1.055) ** 2.4;
1540
+ });
1541
+ return t[0] * 0.2126 + t[1] * 0.7152 + t[2] * 0.0722;
1542
+ }
1543
+ function Vt(e) {
1544
+ const t = {
1545
+ top: F(e.cssSnapshot.marginTop),
1546
+ right: F(e.cssSnapshot.marginRight),
1547
+ bottom: F(e.cssSnapshot.marginBottom),
1548
+ left: F(e.cssSnapshot.marginLeft)
652
1549
  }, n = {
653
- top: y(e.cssSnapshot.paddingTop),
654
- right: y(e.cssSnapshot.paddingRight),
655
- bottom: y(e.cssSnapshot.paddingBottom),
656
- left: y(e.cssSnapshot.paddingLeft)
657
- }, r = {
1550
+ top: F(e.cssSnapshot.borderTopWidth),
1551
+ right: F(e.cssSnapshot.borderRightWidth),
1552
+ bottom: F(e.cssSnapshot.borderBottomWidth),
1553
+ left: F(e.cssSnapshot.borderLeftWidth)
1554
+ }, a = {
1555
+ top: F(e.cssSnapshot.paddingTop),
1556
+ right: F(e.cssSnapshot.paddingRight),
1557
+ bottom: F(e.cssSnapshot.paddingBottom),
1558
+ left: F(e.cssSnapshot.paddingLeft)
1559
+ }, s = {
658
1560
  top: e.rect.top,
659
1561
  left: e.rect.left,
660
1562
  width: Math.max(e.rect.width, 0),
661
1563
  height: Math.max(e.rect.height, 0)
662
- }, i = {
663
- top: r.top - o.top,
664
- left: r.left - o.left,
665
- width: r.width + o.left + o.right,
666
- height: r.height + o.top + o.bottom
667
- }, m = {
668
- top: r.top + t.top,
669
- left: r.left + t.left,
670
- width: Math.max(r.width - t.left - t.right, 0),
671
- height: Math.max(r.height - t.top - t.bottom, 0)
672
- }, v = {
673
- top: m.top + n.top,
674
- left: m.left + n.left,
675
- width: Math.max(m.width - n.left - n.right, 0),
676
- height: Math.max(m.height - n.top - n.bottom, 0)
1564
+ }, c = {
1565
+ top: s.top - t.top,
1566
+ left: s.left - t.left,
1567
+ width: s.width + t.left + t.right,
1568
+ height: s.height + t.top + t.bottom
1569
+ }, g = {
1570
+ top: s.top + n.top,
1571
+ left: s.left + n.left,
1572
+ width: Math.max(s.width - n.left - n.right, 0),
1573
+ height: Math.max(s.height - n.top - n.bottom, 0)
1574
+ }, f = {
1575
+ top: g.top + a.top,
1576
+ left: g.left + a.left,
1577
+ width: Math.max(g.width - a.left - a.right, 0),
1578
+ height: Math.max(g.height - a.top - a.bottom, 0)
677
1579
  };
678
1580
  return {
679
- margin: i,
680
- border: r,
681
- padding: m,
682
- content: v
1581
+ margin: c,
1582
+ border: s,
1583
+ padding: g,
1584
+ content: f
683
1585
  };
684
1586
  }
685
- function y(e) {
686
- const o = Number.parseFloat(e || "0");
687
- return Number.isFinite(o) ? o : 0;
1587
+ function F(e) {
1588
+ const t = Number.parseFloat(e);
1589
+ return Number.isFinite(t) ? t : 0;
688
1590
  }
689
1591
  export {
690
- be as ReviewLensOverlay,
691
- we as ReviewLensProvider,
692
- M as buildElementTarget,
693
- X as createGoogleSheetsAdapter,
694
- ne as normalizeReviewUrl,
695
- re as useReviewLens
1592
+ Qt as ReviewLensOverlay,
1593
+ Xt as ReviewLensProvider,
1594
+ X as buildElementTarget,
1595
+ ht as createGoogleSheetsAdapter,
1596
+ Ct as normalizeReviewUrl,
1597
+ Nt as useReviewLens
696
1598
  };