review-lens-react 0.1.2 → 0.2.1

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 i, jsxs as f, Fragment as Y } from "react/jsx-runtime";
2
- import { createContext as V, useMemo as M, useState as R, useCallback as P, useEffect as T, useContext as Z, useRef as ee, useLayoutEffect as te } from "react";
3
- const ne = [
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(" "), oe = "https://www.googleapis.com/oauth2/v3/userinfo";
7
- function re(e) {
8
- const n = e.feedbackSheetName ?? "Feedback", t = e.usersSheetName ?? "Users";
9
- let r, o;
10
- async function c() {
11
- return r ?? (r = ce(e.googleClientId)), r;
6
+ ].join(" "), dt = "https://www.googleapis.com/oauth2/v3/userinfo";
7
+ function ht(e) {
8
+ const t = e.feedbackSheetName ?? "Feedback", n = e.messagesSheetName ?? "Messages", r = e.usersSheetName ?? "Users";
9
+ let s, c;
10
+ async function g() {
11
+ return s ?? (s = St(e.googleClientId)), s;
12
12
  }
13
- async function h(a, d) {
14
- const l = await c(), u = await fetch(
15
- `https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${a}`,
13
+ async function f(d, v) {
14
+ const p = await g(), b = await fetch(
15
+ `https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${d}`,
16
16
  {
17
- ...d,
17
+ ...v,
18
18
  headers: {
19
- Authorization: `Bearer ${l}`,
19
+ Authorization: `Bearer ${p}`,
20
20
  "Content-Type": "application/json",
21
- ...d == null ? void 0 : d.headers
21
+ ...v == null ? void 0 : v.headers
22
22
  }
23
23
  }
24
24
  );
25
- if (!u.ok)
26
- throw new Error(`Google Sheets request failed with ${u.status}`);
27
- return u.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 w(a) {
30
- return (await h(
31
- `/values/${encodeURIComponent(a)}`
29
+ async function u(d) {
30
+ return (await f(
31
+ `/values/${encodeURIComponent(d)}`
32
32
  )).values ?? [];
33
33
  }
34
34
  return {
35
35
  async getCurrentUser() {
36
- if (!o) {
37
- const a = await c(), d = await fetch(oe, {
38
- headers: { Authorization: `Bearer ${a}` }
36
+ if (!c) {
37
+ const d = await g(), v = await fetch(dt, {
38
+ headers: { Authorization: `Bearer ${d}` }
39
39
  });
40
- if (!d.ok)
41
- throw new Error(`Google userinfo request failed with ${d.status}`);
42
- o = (await d.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 (!o)
44
+ if (!c)
45
45
  throw new Error("Google account did not return an email address");
46
- return { email: o };
46
+ return { email: c };
47
47
  },
48
- async getPermissions(a) {
49
- const [{ email: d }, l] = await Promise.all([this.getCurrentUser(), w(t)]), u = z(l), y = d.toLowerCase(), p = u.find(
50
- (b) => {
51
- var m;
52
- return ((m = b.email) == null ? void 0 : m.toLowerCase()) === y && b.active !== "false" && (!b.projectKey || b.projectKey === a);
48
+ async getPermissions(d) {
49
+ const [{ email: v }, p] = await Promise.all([this.getCurrentUser(), u(r)]), 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 de((p == null ? void 0 : p.role) ?? "designer");
55
+ return gt((x == null ? void 0 : x.role) ?? "designer");
56
56
  },
57
- async listFeedback(a) {
58
- return z(await w(n)).map(K).filter((l) => l !== null).filter(
59
- (l) => l.projectKey === a.projectKey && l.contentId === a.contentId && l.normalizedPath === a.normalizedPath
60
- ).sort((l, u) => u.createdAt.localeCompare(l.createdAt));
57
+ async listFeedback(d) {
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
- async createFeedback(a) {
63
- const d = (/* @__PURE__ */ new Date()).toISOString(), l = {
64
- ...a,
62
+ async createFeedback(d) {
63
+ const v = (/* @__PURE__ */ new Date()).toISOString(), p = {
64
+ ...d,
65
65
  id: crypto.randomUUID(),
66
- status: "open",
67
- createdAt: d,
68
- updatedAt: d
66
+ attachments: [],
67
+ createdAt: v,
68
+ updatedAt: v
69
69
  };
70
- return await h(`/values/${encodeURIComponent(n)}:append?valueInputOption=RAW`, {
70
+ return await f(`/values/${encodeURIComponent(t)}:append?valueInputOption=RAW`, {
71
71
  method: "POST",
72
- body: JSON.stringify({ values: [se(l)] })
73
- }), l;
72
+ body: JSON.stringify({ values: [Ie(p)] })
73
+ }), p;
74
74
  },
75
- async resolveFeedback(a, d) {
76
- const l = await w(n), u = l[0] ?? ie, y = u.indexOf("id"), p = u.indexOf("status"), b = u.indexOf("updatedAt"), m = u.indexOf("resolvedAt"), k = u.indexOf("resolvedBy"), v = l.findIndex((N, x) => x > 0 && N[y] === a);
77
- if (v < 1)
78
- throw new Error(`Feedback ${a} was not found`);
79
- const S = [...l[v]], F = (/* @__PURE__ */ new Date()).toISOString();
80
- S[p] = "resolved", S[b] = F, S[m] = F, S[k] = d, await h(
81
- `/values/${encodeURIComponent(n)}!A${v + 1}:Q${v + 1}?valueInputOption=RAW`,
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)
81
+ throw new Error(`Feedback ${d} was not found`);
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: [S] })
96
+ body: JSON.stringify({ values: [y] })
85
97
  }
86
- );
87
- const C = K(q(u, S));
88
- if (!C)
89
- throw new Error(`Feedback ${a} could not be parsed after resolving`);
90
- return C;
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 ie = [
116
+ const Me = [
95
117
  "id",
96
118
  "projectKey",
97
119
  "contentId",
@@ -100,16 +122,28 @@ const ie = [
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 se(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 se(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 z(e) {
133
- const [n, ...t] = e;
134
- return n ? t.map((r) => q(n, r)) : [];
178
+ function pt(e) {
179
+ return ut.map((t) => e[t]);
135
180
  }
136
- function q(e, n) {
137
- return Object.fromEntries(e.map((t, r) => [t, n[r] ?? ""]));
181
+ function ce(e) {
182
+ const [t, ...n] = e;
183
+ return t ? n.map((r) => De(t, r)) : [];
138
184
  }
139
- function K(e) {
185
+ function De(e, t) {
186
+ return Object.fromEntries(e.map((n, r) => [n, t[r] ?? ""]));
187
+ }
188
+ function Te(e) {
140
189
  return e.id ? {
141
190
  id: e.id,
142
191
  projectKey: e.projectKey,
@@ -145,483 +194,1084 @@ function K(e) {
145
194
  originalUrl: e.originalUrl,
146
195
  selector: e.selector,
147
196
  selectorStrategy: e.selectorStrategy === "stable-attribute" ? "stable-attribute" : "css-path",
148
- elementFingerprint: G(e.elementFingerprintJson, {
197
+ elementFingerprint: he(e.elementFingerprintJson, {
149
198
  tagName: "",
150
199
  width: 0,
151
200
  height: 0
152
201
  }),
153
- cssSnapshot: ae(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 G(e, n) {
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) : n;
235
+ return e ? JSON.parse(e) : t;
166
236
  } catch {
167
- return n;
237
+ return t;
168
238
  }
169
239
  }
170
- function ae(e) {
171
- const n = G(e, {});
240
+ function Re(e) {
241
+ const t = he(e, {});
172
242
  return {
173
- margin: n.margin ?? "",
174
- marginTop: n.marginTop ?? "",
175
- marginRight: n.marginRight ?? "",
176
- marginBottom: n.marginBottom ?? "",
177
- marginLeft: n.marginLeft ?? "",
178
- padding: n.padding ?? "",
179
- paddingTop: n.paddingTop ?? "",
180
- paddingRight: n.paddingRight ?? "",
181
- paddingBottom: n.paddingBottom ?? "",
182
- paddingLeft: n.paddingLeft ?? "",
183
- border: n.border ?? "",
184
- borderTopWidth: n.borderTopWidth ?? "",
185
- borderRightWidth: n.borderRightWidth ?? "",
186
- borderBottomWidth: n.borderBottomWidth ?? "",
187
- borderLeftWidth: n.borderLeftWidth ?? "",
188
- fontFamily: n.fontFamily ?? "",
189
- fontSize: n.fontSize ?? "",
190
- lineHeight: n.lineHeight ?? "",
191
- color: n.color ?? "",
192
- backgroundColor: n.backgroundColor ?? "",
193
- width: n.width ?? 0,
194
- height: n.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 de(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";
282
+ }
283
+ function yt(e) {
284
+ let t = e, n = "";
285
+ for (; t > 0; ) {
286
+ const r = (t - 1) % 26;
287
+ n = String.fromCharCode(65 + r) + n, t = Math.floor((t - r) / 26);
288
+ }
289
+ return n;
199
290
  }
200
- async function ce(e) {
201
- return await le(), new Promise((n, t) => {
202
- var o;
203
- const r = (o = window.google) == null ? void 0 : o.accounts.oauth2.initTokenClient({
291
+ async function St(e) {
292
+ return await kt(), new Promise((t, n) => {
293
+ var s;
294
+ const r = (s = window.google) == null ? void 0 : s.accounts.oauth2.initTokenClient({
204
295
  client_id: e,
205
- scope: ne,
296
+ scope: ct,
206
297
  callback: (c) => {
207
298
  if (c.error || !c.access_token) {
208
- t(new Error(c.error ?? "Google OAuth did not return an access token"));
299
+ n(new Error(c.error ?? "Google OAuth did not return an access token"));
209
300
  return;
210
301
  }
211
- n(c.access_token);
302
+ t(c.access_token);
212
303
  }
213
304
  });
214
305
  r == null || r.requestAccessToken({ prompt: "" });
215
306
  });
216
307
  }
217
- function le() {
308
+ function kt() {
218
309
  var e;
219
- return (e = window.google) != null && e.accounts.oauth2 ? Promise.resolve() : new Promise((n, t) => {
310
+ return (e = window.google) != null && e.accounts.oauth2 ? Promise.resolve() : new Promise((t, n) => {
220
311
  const r = document.querySelector(
221
312
  'script[src="https://accounts.google.com/gsi/client"]'
222
313
  );
223
314
  if (r) {
224
- r.addEventListener("load", () => n(), { once: !0 }), r.addEventListener("error", () => t(new Error("Google Identity failed to load")), {
315
+ r.addEventListener("load", () => t(), { once: !0 }), r.addEventListener("error", () => n(new Error("Google Identity failed to load")), {
225
316
  once: !0
226
317
  });
227
318
  return;
228
319
  }
229
- const o = document.createElement("script");
230
- o.src = "https://accounts.google.com/gsi/client", o.async = !0, o.defer = !0, o.onload = () => n(), o.onerror = () => t(new Error("Google Identity failed to load")), document.head.append(o);
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 he(e) {
324
+ function Ct(e) {
234
325
  return new URL(e, window.location.href).pathname.replace(/\/+$/, "") || "/";
235
326
  }
236
- const J = V(null);
237
- function Ce({ config: e, children: n }) {
238
- const t = M(() => e.adapter ? e.adapter : re({
239
- googleClientId: D(e.googleClientId, "googleClientId"),
240
- spreadsheetId: D(e.spreadsheetId, "spreadsheetId"),
327
+ const ze = st(null);
328
+ function Qt({ 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]), r = e.currentUrl ?? window.location.href, o = (e.normalizeUrl ?? he)(r), [c, h] = R(), [w, a] = R([]), [d, l] = R([]), u = P(async () => {
243
- const m = await t.listFeedback({
333
+ }), [e.adapter, e.googleClientId, e.sheetName, e.spreadsheetId]), r = e.currentUrl ?? window.location.href, s = (e.normalizeUrl ?? Ct)(r), [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: o
337
+ normalizedPath: s
247
338
  });
248
- l(m);
249
- }, [t, e.contentId, e.projectKey, o]);
250
- T(() => {
251
- let m = !0;
252
- async function k() {
253
- const [v, S] = await Promise.all([
254
- t.getCurrentUser(),
255
- t.getPermissions(e.projectKey)
339
+ v(y);
340
+ }, [n, e.contentId, e.projectKey, s]);
341
+ _(() => {
342
+ let y = !0;
343
+ async function w() {
344
+ const [C, B] = await Promise.all([
345
+ n.getCurrentUser(),
346
+ n.getPermissions(e.projectKey)
256
347
  ]);
257
- m && (h(v), a(S), await u());
348
+ y && (g(C), u(B), await p());
258
349
  }
259
- return k(), () => {
260
- m = !1;
350
+ return w(), () => {
351
+ y = !1;
261
352
  };
262
- }, [t, e.projectKey, u]);
263
- const y = P(
264
- async (m) => {
265
- const k = await t.createFeedback(m);
266
- return l((v) => [k, ...v]), k;
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
- ), p = P(
270
- async (m) => {
271
- const k = await t.resolveFeedback(m, (c == null ? void 0 : c.email) ?? "");
272
- return l(
273
- (v) => v.map((S) => S.id === m ? k : S)
274
- ), k;
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, c == null ? void 0 : c.email]
277
- ), b = M(
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,
385
+ adapter: n,
281
386
  currentUser: c,
282
- permissions: w,
387
+ permissions: f,
283
388
  feedback: d,
284
- normalizedPath: o,
285
- refreshFeedback: u,
286
- createFeedback: y,
287
- resolveFeedback: p
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
- y,
400
+ b,
293
401
  c,
294
402
  d,
295
- o,
296
- w,
297
- u,
298
- p
403
+ s,
404
+ f,
405
+ p,
406
+ E,
407
+ x,
408
+ A,
409
+ h
299
410
  ]
300
411
  );
301
- return /* @__PURE__ */ i(J.Provider, { value: b, children: n });
412
+ return /* @__PURE__ */ i(ze.Provider, { value: R, children: t });
302
413
  }
303
- function ue() {
304
- const e = Z(J);
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 D(e, n) {
420
+ function Be(e, t) {
310
421
  if (!e)
311
- throw new Error(`review-lens-react requires config.${n} 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 pe = [
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 _(e) {
322
- const n = e.getBoundingClientRect(), t = me(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: fe(e, n),
327
- cssSnapshot: we(e, n),
328
- rect: n
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 me(e) {
332
- for (const n of pe) {
333
- const t = e.getAttribute(n);
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: `[${n}="${O(t)}"]`,
447
+ selector: `[${t}="${$e(n)}"]`,
337
448
  strategy: "stable-attribute"
338
449
  };
339
450
  }
340
- return e.id ? { selector: `#${O(e.id)}`, strategy: "stable-attribute" } : { selector: ge(e), strategy: "css-path" };
451
+ return e.id ? { selector: `#${$e(e.id)}`, strategy: "stable-attribute" } : { selector: At(e), strategy: "css-path" };
341
452
  }
342
- function ge(e) {
343
- const n = [];
344
- let t = e;
345
- for (; t && t.nodeType === Node.ELEMENT_NODE && t !== document.body; ) {
346
- const r = t.parentElement, o = t.tagName.toLowerCase();
453
+ function At(e) {
454
+ const t = [];
455
+ let n = e;
456
+ for (; n && n.nodeType === Node.ELEMENT_NODE && n !== document.body; ) {
457
+ const r = n.parentElement, s = n.tagName.toLowerCase();
347
458
  if (!r) {
348
- n.unshift(o);
459
+ t.unshift(s);
349
460
  break;
350
461
  }
351
- const c = t.tagName, h = Array.from(r.children).filter(
352
- (a) => a.tagName === c
353
- ), w = h.indexOf(t) + 1;
354
- n.unshift(h.length > 1 ? `${o}:nth-of-type(${w})` : o), t = r;
462
+ const c = n.tagName, g = Array.from(r.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 = r;
355
466
  }
356
- return n.join(" > ");
467
+ return t.join(" > ");
357
468
  }
358
- function fe(e, n) {
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(n.width),
367
- height: Math.round(n.height)
477
+ width: Math.round(t.width),
478
+ height: Math.round(t.height)
368
479
  };
369
480
  }
370
- function we(e, n) {
371
- const t = window.getComputedStyle(e);
481
+ function Ft(e, t) {
482
+ const n = window.getComputedStyle(e);
372
483
  return {
373
- margin: j(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: j(
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: j(
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(n.width),
404
- height: Math.round(n.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 j(e, n, t, r) {
408
- return e === n && n === t && t === r ? e : `${e} ${n} ${t} ${r}`;
519
+ function de(e, t, n, r) {
520
+ return e === t && t === n && n === r ? e : `${e} ${t} ${n} ${r}`;
409
521
  }
410
- function O(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 Le({
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 Zt({
414
555
  open: e,
415
- onOpenChange: n,
416
- placement: t = "top-right",
417
- showResolved: r = !1
556
+ onOpenChange: t,
557
+ placement: n = "top-right",
558
+ showResolved: r = !1,
559
+ syncSelectionToUrl: s = !1,
560
+ responsivePresets: c = Mt
418
561
  }) {
562
+ var Fe;
419
563
  const {
420
- config: o,
421
- currentUser: c,
422
- feedback: h,
423
- normalizedPath: w,
424
- permissions: a,
425
- createFeedback: d,
426
- resolveFeedback: l
427
- } = ue(), [u, y] = R(), [p, b] = R(), [m, k] = R(""), [v, S] = R(), [F, C] = R("review"), N = !!c, x = a.includes("create"), Q = a.includes("resolve"), B = M(
428
- () => h.filter((s) => r || s.status !== "resolved"),
429
- [h, r]
430
- );
431
- T(() => {
432
- e || (y(void 0), b(void 0), k(""), C("review"));
433
- }, [e]), T(() => {
434
- N || (y(void 0), b(void 0));
435
- }, [N]), T(() => {
564
+ adapter: g,
565
+ config: f,
566
+ currentUser: u,
567
+ feedback: d,
568
+ normalizedPath: v,
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, Ve] = k("visual"), [me, ge] = k(""), [fe, qe] = 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"), [V, 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 a = d.map((m) => m.assigneeEmail).filter((m) => !!m);
579
+ return u != null && u.email && a.push(u.email), Array.from(new Set(a)).sort((m, l) => m.localeCompare(l));
580
+ }, [u == null ? void 0 : u.email, d]), L = re(
581
+ () => d.filter((a) => r || a.status !== "resolved").filter((a) => j === "all" || a.status === j).filter((a) => O === "all" || a.severity === O).filter((a) => K === "all" || a.category === K).filter((a) => J === "all" || a.assigneeEmail === J).filter((a) => V === "all" || a.viewportPreset === V),
582
+ [
583
+ J,
584
+ K,
585
+ d,
586
+ O,
587
+ r,
588
+ j,
589
+ V
590
+ ]
591
+ ), it = [
592
+ j,
593
+ O,
594
+ K,
595
+ J,
596
+ V
597
+ ].filter((a) => a !== "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 a, m, l;
605
+ (m = (a = ee.current) == null ? void 0 : a.scrollIntoView) == null || m.call(a, { block: "nearest" }), (l = ee.current) == null || l.focus();
606
+ });
607
+ }, [w, M]), _(() => {
608
+ if (!S)
609
+ return;
610
+ let a = !0;
611
+ return x(S.id).then((m) => {
612
+ a && Ce((l) => ({ ...l, [S.id]: m }));
613
+ }), () => {
614
+ a = !1;
615
+ };
616
+ }, [x, S]), _(() => {
617
+ if (!e || !s || S || d.length === 0)
618
+ return;
619
+ const a = new URL(window.location.href).searchParams.get("reviewLensFeedback"), m = d.find((l) => l.id === a);
620
+ m && q(m, { syncUrl: !1 });
621
+ }, [d, e, S, s]), _(() => {
436
622
  if (!e)
437
623
  return;
438
- function s(g) {
439
- g.key === "Escape" && (g.preventDefault(), n == null || n(!1));
624
+ function a(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
+ Jt(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);
440
634
  }
441
- return window.addEventListener("keydown", s), () => {
442
- window.removeEventListener("keydown", s);
635
+ return window.addEventListener("keydown", a), window.addEventListener("keyup", m), () => {
636
+ window.removeEventListener("keydown", a), window.removeEventListener("keyup", m);
443
637
  };
444
- }, [n, e]);
445
- const $ = P((s) => {
446
- const g = s.target instanceof Element ? s.target : null;
447
- if (g)
448
- return g.closest("[data-review-lens-ui]") ? null : g;
449
- const L = document.elementFromPoint(s.clientX, s.clientY);
450
- return !L || L.closest("[data-review-lens-ui]") ? null : L;
638
+ });
639
+ const le = W((a) => {
640
+ const m = a.target instanceof Element ? a.target : null;
641
+ if (m)
642
+ return m.closest("[data-review-lens-ui]") ? null : m;
643
+ const l = document.elementFromPoint(a.clientX, a.clientY);
644
+ return !l || l.closest("[data-review-lens-ui]") ? null : l;
451
645
  }, []);
452
- if (T(() => {
453
- if (!e || !N)
646
+ if (_(() => {
647
+ if (!e || !I)
454
648
  return;
455
- function s(L) {
456
- const A = $(L);
457
- y(A ? _(A) : void 0);
649
+ function a(l) {
650
+ const N = le(l);
651
+ y(N ? X(N) : void 0);
458
652
  }
459
- function g(L) {
460
- const A = $(L);
461
- A && (L.preventDefault(), L.stopPropagation(), b(_(A)), C("review"));
653
+ function m(l) {
654
+ const N = le(l);
655
+ N && (l.preventDefault(), l.stopPropagation(), C(X(N)), $("review"));
462
656
  }
463
- return window.addEventListener("mousemove", s, !0), window.addEventListener("click", g, !0), () => {
464
- window.removeEventListener("mousemove", s, !0), window.removeEventListener("click", g, !0);
657
+ return window.addEventListener("mousemove", a, !0), window.addEventListener("click", m, !0), () => {
658
+ window.removeEventListener("mousemove", a, !0), window.removeEventListener("click", m, !0);
465
659
  };
466
- }, [N, $, p, e]), !e)
660
+ }, [I, le, e]), !e)
467
661
  return null;
468
- const I = u ?? p, X = !!p;
469
- function W(s) {
470
- S(s), b(void 0), C("feedback");
471
- const g = H(s.selector);
472
- g && (g.scrollIntoView({ behavior: "smooth", block: "center", inline: "center" }), window.requestAnimationFrame(() => {
473
- y(_(g));
474
- }));
662
+ function q(a, m = { syncUrl: !0 }) {
663
+ var N;
664
+ if (H(a), C(void 0), $("feedback"), s && m.syncUrl !== !1) {
665
+ const te = new URL(window.location.href);
666
+ te.searchParams.set("reviewLensFeedback", a.id), window.history.replaceState({}, "", te);
667
+ }
668
+ const l = z(a.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
+ });
475
676
  }
476
- async function U() {
477
- !p || !m.trim() || !c || !x || (await d({
478
- projectKey: o.projectKey,
479
- contentId: o.contentId,
480
- normalizedPath: w,
481
- originalUrl: o.currentUrl ?? window.location.href,
482
- selector: p.selector,
483
- selectorStrategy: p.selectorStrategy,
484
- elementFingerprint: p.fingerprint,
485
- cssSnapshot: p.cssSnapshot,
486
- comment: m.trim(),
487
- authorEmail: c.email
488
- }), k(""), b(void 0), y(void 0), C("feedback"));
677
+ function Ee(a) {
678
+ if (L.length === 0)
679
+ return;
680
+ const m = S ? L.findIndex((N) => N.id === S.id) : -1, l = m < 0 ? a > 0 ? 0 : L.length - 1 : (m + a + L.length) % L.length;
681
+ q(L[l]);
489
682
  }
490
- return /* @__PURE__ */ f("div", { className: "review-lens-root", "data-review-lens-ui": !0, children: [
491
- N && I ? /* @__PURE__ */ i(ve, { target: I, locked: !!p }) : null,
492
- N ? /* @__PURE__ */ i(
493
- be,
494
- {
495
- feedback: B,
496
- selectedFeedback: v,
497
- onSelect: W
683
+ async function Ae() {
684
+ if (!w || !B.trim() || !u || !T)
685
+ return;
686
+ let a = 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(a.id, {
710
+ type: "screenshot",
711
+ data: m,
712
+ createdBy: u.email
713
+ });
714
+ a = await E(a.id, {
715
+ attachments: [l],
716
+ screenshotUrl: l.url,
717
+ screenshotThumbnailUrl: l.thumbnailUrl
718
+ });
719
+ } catch {
498
720
  }
499
- ) : null,
500
- /* @__PURE__ */ f("aside", { className: `review-lens-panel review-lens-panel--${t}`, "data-review-lens-ui": !0, children: [
501
- /* @__PURE__ */ f("header", { className: "review-lens-panel__header", children: [
502
- /* @__PURE__ */ f("div", { children: [
721
+ D(""), ge(""), C(void 0), y(void 0), $("feedback"), H(a);
722
+ }
723
+ async function rt(a, 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(a.id, N);
725
+ H(te);
726
+ }
727
+ async function Le(a) {
728
+ const m = z(a.selector);
729
+ if (!m || !u)
730
+ return;
731
+ const l = X(m), N = await E(a.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(a) {
740
+ if (!se.trim() || !u || !Ne)
741
+ return;
742
+ const m = await A({
743
+ feedbackId: a.id,
744
+ body: se.trim(),
745
+ authorEmail: u.email
746
+ });
747
+ Ce((l) => ({
748
+ ...l,
749
+ [a.id]: [...l[a.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: q
762
+ }
763
+ ),
764
+ /* @__PURE__ */ i(
765
+ Wt,
766
+ {
767
+ feedback: L,
768
+ selectedFeedback: S,
769
+ onSelect: q
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: [
503
776
  /* @__PURE__ */ i("p", { className: "review-lens-kicker", children: "Review Lens" }),
504
- /* @__PURE__ */ i("h2", { children: F === "feedback" ? "Feedback" : p ? "Element locked" : "Inspecting" })
777
+ /* @__PURE__ */ i("h2", { children: M === "summary" ? "Summary" : M === "feedback" ? "Feedback" : w ? "Element locked" : "Inspecting" })
505
778
  ] }),
506
- /* @__PURE__ */ i("button", { type: "button", onClick: () => n == null ? void 0 : n(!1), children: "Close" })
779
+ /* @__PURE__ */ i("button", { type: "button", onClick: () => t == null ? void 0 : t(!1), children: "Close" })
507
780
  ] }),
508
- /* @__PURE__ */ f("div", { className: "review-lens-panel__body", children: [
509
- /* @__PURE__ */ f("div", { className: "review-lens-mode-switch", role: "tablist", "aria-label": "Review Lens mode", children: [
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: [
510
783
  /* @__PURE__ */ i(
511
784
  "button",
512
785
  {
513
786
  type: "button",
514
787
  role: "tab",
515
- "aria-selected": F === "review",
516
- onClick: () => C("review"),
788
+ "aria-selected": M === "review",
789
+ onClick: () => $("review"),
517
790
  children: "Review"
518
791
  }
519
792
  ),
520
- /* @__PURE__ */ f(
793
+ /* @__PURE__ */ o(
521
794
  "button",
522
795
  {
523
796
  type: "button",
524
797
  role: "tab",
525
- "aria-selected": F === "feedback",
526
- onClick: () => C("feedback"),
798
+ "aria-selected": M === "feedback",
799
+ onClick: () => $("feedback"),
527
800
  children: [
528
801
  "Feedback ",
529
- /* @__PURE__ */ i("span", { children: B.length })
802
+ /* @__PURE__ */ i("span", { children: L.length })
530
803
  ]
531
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
+ }
532
815
  )
533
816
  ] }),
534
- F === "review" ? /* @__PURE__ */ f("div", { className: "review-lens-review-pane", role: "tabpanel", children: [
535
- /* @__PURE__ */ f("div", { className: "review-lens-inspection", children: [
536
- N ? null : /* @__PURE__ */ i("p", { children: "Authenticate with Google to inspect this page." }),
537
- N && I ? /* @__PURE__ */ i(ke, { target: I }) : null,
538
- N && !I ? /* @__PURE__ */ i("p", { children: "Move over the app to inspect." }) : null
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
539
832
  ] }),
540
- X ? /* @__PURE__ */ f(
833
+ et ? /* @__PURE__ */ i("div", { className: "review-lens-composer-panel", children: /* @__PURE__ */ o(
541
834
  "form",
542
835
  {
543
836
  className: "review-lens-feedback-form",
544
- onSubmit: (s) => {
545
- s.preventDefault(), U();
837
+ onSubmit: (a) => {
838
+ a.preventDefault(), Ae();
546
839
  },
547
840
  children: [
548
841
  /* @__PURE__ */ i("label", { htmlFor: "review-lens-comment", children: "New feedback" }),
549
842
  /* @__PURE__ */ i(
550
843
  "textarea",
551
844
  {
845
+ ref: ee,
552
846
  id: "review-lens-comment",
553
- value: m,
554
- disabled: !x,
555
- onChange: (s) => k(s.target.value),
556
- onKeyDown: (s) => {
557
- s.key === "Enter" && s.metaKey && (s.preventDefault(), U());
847
+ value: B,
848
+ disabled: !T,
849
+ onChange: (a) => D(a.target.value),
850
+ onKeyDown: (a) => {
851
+ a.key === "Enter" && a.metaKey && (a.preventDefault(), Ae());
558
852
  },
559
- placeholder: x ? "Describe the UX issue..." : "You do not have permission to comment."
853
+ placeholder: T ? "Describe the UX issue..." : "You do not have permission to comment."
560
854
  }
561
855
  ),
562
- x ? /* @__PURE__ */ f("p", { className: "review-lens-feedback-form__hint", children: [
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: (a) => Je(a.target.value),
864
+ disabled: !T,
865
+ children: je.map((a) => /* @__PURE__ */ i("option", { value: a, children: Q[a] }, a))
866
+ }
867
+ )
868
+ ] }),
869
+ /* @__PURE__ */ o("label", { children: [
870
+ "Type",
871
+ /* @__PURE__ */ i(
872
+ "select",
873
+ {
874
+ value: pe,
875
+ onChange: (a) => Ve(a.target.value),
876
+ disabled: !T,
877
+ children: Oe.map((a) => /* @__PURE__ */ i("option", { value: a, children: Z[a] }, a))
878
+ }
879
+ )
880
+ ] }),
881
+ /* @__PURE__ */ o("label", { children: [
882
+ "Assignee",
883
+ /* @__PURE__ */ i(
884
+ "input",
885
+ {
886
+ value: me,
887
+ onChange: (a) => ge(a.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: (a) => qe(a.target.value),
900
+ disabled: !T,
901
+ children: c.map((a) => /* @__PURE__ */ i("option", { value: a.value, children: a.label }, a.value))
902
+ }
903
+ )
904
+ ] })
905
+ ] }),
906
+ T ? /* @__PURE__ */ o("p", { className: "review-lens-feedback-form__hint", children: [
563
907
  "Press ",
564
908
  /* @__PURE__ */ i("kbd", { children: "Command" }),
565
909
  " + ",
566
910
  /* @__PURE__ */ i("kbd", { children: "Enter" }),
567
- " to submit."
911
+ " to submit.",
912
+ tt ? " Screenshot capture runs after save." : ""
568
913
  ] }) : null,
569
- /* @__PURE__ */ i("div", { className: "review-lens-actions", children: /* @__PURE__ */ i("button", { type: "submit", disabled: !m.trim() || !x, children: "Save feedback" }) })
914
+ /* @__PURE__ */ i("div", { className: "review-lens-actions", children: /* @__PURE__ */ i("button", { type: "submit", disabled: !B.trim() || !T, children: "Save feedback" }) })
570
915
  ]
571
916
  }
572
- ) : null
573
- ] }) : /* @__PURE__ */ f("div", { className: "review-lens-comments", children: [
574
- /* @__PURE__ */ f("div", { className: "review-lens-comments__header", children: [
575
- /* @__PURE__ */ i("h3", { children: "Page feedback" }),
576
- /* @__PURE__ */ i("span", { children: B.length })
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: V,
930
+ assignees: nt,
931
+ responsivePresets: c,
932
+ onStatusChange: ve,
933
+ onSeverityChange: we,
934
+ onCategoryChange: ye,
935
+ onAssigneeChange: Se,
936
+ onViewportChange: ke,
937
+ onToggle: () => Xe((a) => !a),
938
+ onClear: () => {
939
+ ve("all"), we("all"), ye("all"), Se("all"), ke("all");
940
+ }
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((a) => /* @__PURE__ */ i(
951
+ Tt,
952
+ {
953
+ item: a,
954
+ selected: (S == null ? void 0 : S.id) === a.id,
955
+ onSelect: q
956
+ },
957
+ a.id
958
+ ))
959
+ ] })
577
960
  ] }),
578
- /* @__PURE__ */ f("div", { className: "review-lens-comments__list", children: [
579
- B.length === 0 ? /* @__PURE__ */ i("p", { children: "No feedback for this view." }) : null,
580
- B.map((s) => /* @__PURE__ */ f(
581
- "article",
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,
582
965
  {
583
- tabIndex: 0,
584
- className: (v == null ? void 0 : v.id) === s.id ? "review-lens-comment review-lens-comment--selected" : "review-lens-comment",
585
- onClick: () => W(s),
586
- onKeyDown: (g) => {
587
- (g.key === "Enter" || g.key === " ") && (g.preventDefault(), W(s));
588
- },
589
- children: [
590
- /* @__PURE__ */ f("div", { className: "review-lens-comment__content", children: [
591
- /* @__PURE__ */ i("p", { children: s.comment }),
592
- /* @__PURE__ */ i("span", { children: s.authorEmail })
593
- ] }),
594
- s.status === "open" && Q ? /* @__PURE__ */ i("div", { className: "review-lens-comment__actions", children: /* @__PURE__ */ i(
595
- "button",
596
- {
597
- type: "button",
598
- onClick: (g) => {
599
- g.stopPropagation(), l(s.id);
600
- },
601
- children: "Resolve"
602
- }
603
- ) }) : null
604
- ]
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: (a) => void rt(S, a),
975
+ onAssigneeChange: (a) => void E(S.id, {
976
+ assigneeEmail: a.trim() || void 0
977
+ }).then(H),
978
+ onMarkFixed: () => void Le(S)
605
979
  },
606
- s.id
607
- ))
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." })
608
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: r,
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: r,
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",
1083
+ {
1084
+ "aria-label": "Filter viewport",
1085
+ value: g,
1086
+ onChange: (h) => E(h.target.value),
1087
+ children: [
1088
+ /* @__PURE__ */ i("option", { value: "all", children: "All viewports" }),
1089
+ u.map((h) => /* @__PURE__ */ i("option", { value: h.value, children: h.label }, h.value))
1090
+ ]
1091
+ }
1092
+ )
1093
+ ] })
1094
+ ] }) : null
1095
+ ] });
1096
+ }
1097
+ function Tt({
1098
+ item: e,
1099
+ selected: t,
1100
+ onSelect: n
1101
+ }) {
1102
+ const r = 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: r.label })
609
1132
  ] })
1133
+ ]
1134
+ }
1135
+ );
1136
+ }
1137
+ function Rt({
1138
+ item: e,
1139
+ messages: t,
1140
+ messageDraft: n,
1141
+ canReply: r,
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
+ )
610
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: !r,
1238
+ onChange: (b) => g(b.target.value),
1239
+ placeholder: r ? "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() || !r, onClick: f, children: "Reply" }) })
611
1243
  ] })
612
1244
  ] });
613
1245
  }
614
- function ve({ target: e, locked: n }) {
615
- const t = Se(e);
616
- return /* @__PURE__ */ f(
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, r]) => /* @__PURE__ */ o("div", { children: [
1259
+ /* @__PURE__ */ i("dt", { children: n }),
1260
+ /* @__PURE__ */ i("dd", { children: r })
1261
+ ] }, n)) })
1262
+ ] });
1263
+ }
1264
+ function $t({ target: e, locked: t }) {
1265
+ const n = Gt(e), r = Ot(e.fingerprint);
1266
+ return /* @__PURE__ */ o(
617
1267
  "div",
618
1268
  {
619
- className: n ? "review-lens-highlight review-lens-highlight--locked" : "review-lens-highlight",
1269
+ className: t ? "review-lens-highlight review-lens-highlight--locked" : "review-lens-highlight",
620
1270
  style: {
621
- top: t.margin.top,
622
- left: t.margin.left,
623
- width: t.margin.width,
624
- height: t.margin.height
1271
+ top: n.margin.top,
1272
+ left: n.margin.left,
1273
+ width: n.margin.width,
1274
+ height: n.margin.height
625
1275
  },
626
1276
  children: [
627
1277
  /* @__PURE__ */ i(
@@ -629,10 +1279,10 @@ function ve({ target: e, locked: n }) {
629
1279
  {
630
1280
  className: "review-lens-highlight__border",
631
1281
  style: {
632
- top: t.border.top - t.margin.top,
633
- left: t.border.left - t.margin.left,
634
- width: t.border.width,
635
- 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
636
1286
  }
637
1287
  }
638
1288
  ),
@@ -641,10 +1291,10 @@ function ve({ target: e, locked: n }) {
641
1291
  {
642
1292
  className: "review-lens-highlight__padding",
643
1293
  style: {
644
- top: t.padding.top - t.margin.top,
645
- left: t.padding.left - t.margin.left,
646
- width: t.padding.width,
647
- 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
648
1298
  }
649
1299
  }
650
1300
  ),
@@ -653,143 +1303,304 @@ function ve({ target: e, locked: n }) {
653
1303
  {
654
1304
  className: "review-lens-highlight__content",
655
1305
  style: {
656
- top: t.content.top - t.margin.top,
657
- left: t.content.left - t.margin.left,
658
- width: t.content.width,
659
- 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
660
1310
  }
661
1311
  }
662
1312
  ),
663
- /* @__PURE__ */ f("div", { className: "review-lens-highlight__label", children: [
664
- Math.round(e.rect.width),
665
- " x ",
666
- Math.round(e.rect.height)
1313
+ /* @__PURE__ */ o("div", { className: "review-lens-highlight__label", children: [
1314
+ /* @__PURE__ */ i("strong", { children: r }),
1315
+ /* @__PURE__ */ o("span", { children: [
1316
+ Math.round(e.rect.width),
1317
+ " x ",
1318
+ Math.round(e.rect.height)
1319
+ ] })
667
1320
  ] })
668
1321
  ]
669
1322
  }
670
1323
  );
671
1324
  }
672
- function be({
1325
+ function _t({ from: e, to: t }) {
1326
+ const n = Kt(e.rect, t.rect);
1327
+ return n.length === 0 ? null : /* @__PURE__ */ i(ie, { children: n.map((r) => /* @__PURE__ */ i(
1328
+ "div",
1329
+ {
1330
+ className: `review-lens-distance review-lens-distance--${r.axis}`,
1331
+ style: {
1332
+ top: r.top,
1333
+ left: r.left,
1334
+ width: r.width,
1335
+ height: r.height
1336
+ },
1337
+ children: /* @__PURE__ */ i("span", { children: r.label })
1338
+ },
1339
+ r.key
1340
+ )) });
1341
+ }
1342
+ function Pt({
673
1343
  feedback: e,
674
- selectedFeedback: n,
675
- onSelect: t
1344
+ selectedFeedback: t,
1345
+ onSelect: n
676
1346
  }) {
677
- return /* @__PURE__ */ i(Y, { children: e.map((r) => /* @__PURE__ */ i(
678
- ye,
1347
+ return /* @__PURE__ */ i(ie, { children: e.map((r) => /* @__PURE__ */ i(
1348
+ Ut,
679
1349
  {
680
1350
  feedback: r,
681
- selected: (n == null ? void 0 : n.id) === r.id,
682
- onSelect: t
1351
+ selected: (t == null ? void 0 : t.id) === r.id,
1352
+ onSelect: n
683
1353
  },
684
1354
  r.id
685
1355
  )) });
686
1356
  }
687
- function ye({
1357
+ function Ut({
688
1358
  feedback: e,
689
- selected: n,
690
- onSelect: t
1359
+ selected: t,
1360
+ onSelect: n
691
1361
  }) {
692
- const r = ee(null);
693
- return te(() => {
694
- let o = 0;
1362
+ const r = We(null);
1363
+ return lt(() => {
1364
+ let s = 0;
695
1365
  const c = () => {
696
- o = 0;
697
- const w = r.current, a = H(e.selector), d = a == null ? void 0 : a.getBoundingClientRect();
698
- !w || !d || (w.style.top = `${d.top}px`, w.style.left = `${d.right}px`, w.hidden = d.bottom < 0 || d.top > window.innerHeight);
699
- }, h = () => {
700
- o || (o = window.requestAnimationFrame(c));
1366
+ s = 0;
1367
+ const f = r.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));
701
1371
  };
702
- return c(), window.addEventListener("scroll", h, !0), window.addEventListener("resize", h), () => {
703
- o && window.cancelAnimationFrame(o), window.removeEventListener("scroll", h, !0), window.removeEventListener("resize", h);
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);
704
1374
  };
705
1375
  }, [e.selector]), /* @__PURE__ */ i(
706
1376
  "button",
707
1377
  {
708
1378
  ref: r,
709
1379
  type: "button",
710
- className: n ? "review-lens-marker review-lens-marker--selected" : "review-lens-marker",
711
- onClick: () => t(e),
1380
+ className: t ? "review-lens-marker review-lens-marker--selected" : "review-lens-marker",
1381
+ onClick: () => n(e),
712
1382
  "aria-label": `Open feedback from ${e.authorEmail}`
713
1383
  }
714
1384
  );
715
1385
  }
716
- function ke({ target: e }) {
717
- const n = [
1386
+ function Wt({
1387
+ feedback: e,
1388
+ selectedFeedback: t,
1389
+ onSelect: n
1390
+ }) {
1391
+ const r = 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 r.length === 0 ? null : /* @__PURE__ */ i("div", { className: "review-lens-minimap", "data-review-lens-ui": !0, "aria-label": "Feedback map", children: r.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
+ )) });
1413
+ }
1414
+ function Dt({ target: e }) {
1415
+ const t = [
718
1416
  ["Selector", e.selector],
719
1417
  ["Size", `${e.cssSnapshot.width} x ${e.cssSnapshot.height}`],
720
1418
  ["Margin", e.cssSnapshot.margin],
721
1419
  ["Padding", e.cssSnapshot.padding],
722
1420
  ["Border", e.cssSnapshot.border],
1421
+ ["Radius", e.cssSnapshot.borderRadius],
723
1422
  ["Font", `${e.cssSnapshot.fontSize} / ${e.cssSnapshot.lineHeight}`],
724
1423
  ["Family", e.cssSnapshot.fontFamily],
725
1424
  ["Color", e.cssSnapshot.color],
726
1425
  ["Background", e.cssSnapshot.backgroundColor]
727
1426
  ];
728
- return /* @__PURE__ */ i("dl", { className: "review-lens-metrics", children: n.map(([t, r]) => /* @__PURE__ */ f("div", { children: [
729
- /* @__PURE__ */ i("dt", { children: t }),
1427
+ return /* @__PURE__ */ i("dl", { className: "review-lens-metrics", children: t.map(([n, r]) => /* @__PURE__ */ o("div", { children: [
1428
+ /* @__PURE__ */ i("dt", { children: n }),
730
1429
  /* @__PURE__ */ i("dd", { children: r })
731
- ] }, t)) });
1430
+ ] }, n)) });
732
1431
  }
733
- function H(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) {
734
1440
  try {
735
1441
  return document.querySelector(e);
736
1442
  } catch {
737
1443
  return null;
738
1444
  }
739
1445
  }
740
- function Se(e) {
741
- const n = {
742
- top: E(e.cssSnapshot.marginTop),
743
- right: E(e.cssSnapshot.marginRight),
744
- bottom: E(e.cssSnapshot.marginBottom),
745
- left: E(e.cssSnapshot.marginLeft)
746
- }, t = {
747
- top: E(e.cssSnapshot.borderTopWidth),
748
- right: E(e.cssSnapshot.borderRightWidth),
749
- bottom: E(e.cssSnapshot.borderBottomWidth),
750
- left: E(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 = [], r = t.tagName.toLowerCase(), s = t.getAttribute("role"), c = ["button", "a", "input", "select", "textarea"].includes(r) || 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."), r === "img" && !t.getAttribute("alt") && n.push("Image is missing alt text.");
1460
+ const f = /^h[1-6]$/.test(r) ? Number(r.slice(1)) : 0;
1461
+ return f > 1 && !document.querySelector(`h${f - 1}`) && n.push("Heading may skip the previous level."), Vt(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, { allowComposite: !0 }), U("Margin", e.margin, t.spacing, n, { allowComposite: !0 }), 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, { allowComposite: !0 }), n;
1466
+ }
1467
+ function U(e, t, n, r, s = {}) {
1468
+ !n || n.length === 0 || !t || jt(t, n, s) || r.push(`${e} ${t} is outside configured tokens.`);
1469
+ }
1470
+ function jt(e, t, n = {}) {
1471
+ if (t.includes(e))
1472
+ return !0;
1473
+ if (!n.allowComposite)
1474
+ return !1;
1475
+ const r = e.trim().split(/\s+/);
1476
+ return r.length > 1 && r.every((s) => t.includes(s));
1477
+ }
1478
+ function Ot(e) {
1479
+ const t = e.id ? `#${e.id}` : "", n = e.className ? `.${e.className.split(/\s+/).filter(Boolean).slice(0, 2).join(".")}` : "", r = e.ariaLabel ? `[aria-label="${e.ariaLabel}"]` : "";
1480
+ return `${e.tagName}${t}${n}${r}` || e.tagName;
1481
+ }
1482
+ function Kt(e, t) {
1483
+ const n = [], r = (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;
1484
+ if (e.right <= t.left || t.right <= e.left) {
1485
+ const c = e.right <= t.left ? e.right : t.right, g = e.right <= t.left ? t.left : e.left;
1486
+ n.push({
1487
+ key: "horizontal",
1488
+ axis: "horizontal",
1489
+ top: Pe(s, 0, window.innerHeight),
1490
+ left: c,
1491
+ width: Math.max(g - c, 1),
1492
+ height: 1,
1493
+ label: `${Math.round(g - c)}px`
1494
+ });
1495
+ }
1496
+ if (e.bottom <= t.top || t.bottom <= e.top) {
1497
+ const c = e.bottom <= t.top ? e.bottom : t.bottom, g = e.bottom <= t.top ? t.top : e.top;
1498
+ n.push({
1499
+ key: "vertical",
1500
+ axis: "vertical",
1501
+ top: c,
1502
+ left: Pe(r, 0, window.innerWidth),
1503
+ width: 1,
1504
+ height: Math.max(g - c, 1),
1505
+ label: `${Math.round(g - c)}px`
1506
+ });
1507
+ }
1508
+ return n;
1509
+ }
1510
+ function Pe(e, t, n) {
1511
+ return Math.min(Math.max(e, t), n);
1512
+ }
1513
+ function Y(e, t) {
1514
+ const n = /* @__PURE__ */ new Map();
1515
+ for (const r of e) {
1516
+ const s = t(r);
1517
+ n.set(s, (n.get(s) ?? 0) + 1);
1518
+ }
1519
+ return Array.from(n.entries()).sort((r, s) => s[1] - r[1] || r[0].localeCompare(s[0]));
1520
+ }
1521
+ function Jt(e) {
1522
+ return e instanceof HTMLInputElement || e instanceof HTMLTextAreaElement || e instanceof HTMLSelectElement || e instanceof HTMLElement && e.isContentEditable;
1523
+ }
1524
+ function Vt(e, t) {
1525
+ const n = Ue(e), r = Ue(t);
1526
+ return !n || !r || r.alpha === 0 ? !1 : qt(n, r) < 4.5;
1527
+ }
1528
+ function Ue(e) {
1529
+ const t = e.match(/rgba?\(([^)]+)\)/);
1530
+ if (!t)
1531
+ return null;
1532
+ const [n, r, s, c = "1"] = t[1].split(",").map((g) => g.trim());
1533
+ return {
1534
+ red: Number(n),
1535
+ green: Number(r),
1536
+ blue: Number(s),
1537
+ alpha: Number(c)
1538
+ };
1539
+ }
1540
+ function qt(e, t) {
1541
+ const n = Math.max(ne(e), ne(t)), r = Math.min(ne(e), ne(t));
1542
+ return (n + 0.05) / (r + 0.05);
1543
+ }
1544
+ function ne(e) {
1545
+ const t = [e.red, e.green, e.blue].map((n) => {
1546
+ const r = n / 255;
1547
+ return r <= 0.03928 ? r / 12.92 : ((r + 0.055) / 1.055) ** 2.4;
1548
+ });
1549
+ return t[0] * 0.2126 + t[1] * 0.7152 + t[2] * 0.0722;
1550
+ }
1551
+ function Gt(e) {
1552
+ const t = {
1553
+ top: F(e.cssSnapshot.marginTop),
1554
+ right: F(e.cssSnapshot.marginRight),
1555
+ bottom: F(e.cssSnapshot.marginBottom),
1556
+ left: F(e.cssSnapshot.marginLeft)
1557
+ }, n = {
1558
+ top: F(e.cssSnapshot.borderTopWidth),
1559
+ right: F(e.cssSnapshot.borderRightWidth),
1560
+ bottom: F(e.cssSnapshot.borderBottomWidth),
1561
+ left: F(e.cssSnapshot.borderLeftWidth)
751
1562
  }, r = {
752
- top: E(e.cssSnapshot.paddingTop),
753
- right: E(e.cssSnapshot.paddingRight),
754
- bottom: E(e.cssSnapshot.paddingBottom),
755
- left: E(e.cssSnapshot.paddingLeft)
756
- }, o = {
1563
+ top: F(e.cssSnapshot.paddingTop),
1564
+ right: F(e.cssSnapshot.paddingRight),
1565
+ bottom: F(e.cssSnapshot.paddingBottom),
1566
+ left: F(e.cssSnapshot.paddingLeft)
1567
+ }, s = {
757
1568
  top: e.rect.top,
758
1569
  left: e.rect.left,
759
1570
  width: Math.max(e.rect.width, 0),
760
1571
  height: Math.max(e.rect.height, 0)
761
1572
  }, c = {
762
- top: o.top - n.top,
763
- left: o.left - n.left,
764
- width: o.width + n.left + n.right,
765
- height: o.height + n.top + n.bottom
766
- }, h = {
767
- top: o.top + t.top,
768
- left: o.left + t.left,
769
- width: Math.max(o.width - t.left - t.right, 0),
770
- height: Math.max(o.height - t.top - t.bottom, 0)
771
- }, w = {
772
- top: h.top + r.top,
773
- left: h.left + r.left,
774
- width: Math.max(h.width - r.left - r.right, 0),
775
- height: Math.max(h.height - r.top - r.bottom, 0)
1573
+ top: s.top - t.top,
1574
+ left: s.left - t.left,
1575
+ width: s.width + t.left + t.right,
1576
+ height: s.height + t.top + t.bottom
1577
+ }, g = {
1578
+ top: s.top + n.top,
1579
+ left: s.left + n.left,
1580
+ width: Math.max(s.width - n.left - n.right, 0),
1581
+ height: Math.max(s.height - n.top - n.bottom, 0)
1582
+ }, f = {
1583
+ top: g.top + r.top,
1584
+ left: g.left + r.left,
1585
+ width: Math.max(g.width - r.left - r.right, 0),
1586
+ height: Math.max(g.height - r.top - r.bottom, 0)
776
1587
  };
777
1588
  return {
778
1589
  margin: c,
779
- border: o,
780
- padding: h,
781
- content: w
1590
+ border: s,
1591
+ padding: g,
1592
+ content: f
782
1593
  };
783
1594
  }
784
- function E(e) {
785
- const n = Number.parseFloat(e || "0");
786
- return Number.isFinite(n) ? n : 0;
1595
+ function F(e) {
1596
+ const t = Number.parseFloat(e);
1597
+ return Number.isFinite(t) ? t : 0;
787
1598
  }
788
1599
  export {
789
- Le as ReviewLensOverlay,
790
- Ce as ReviewLensProvider,
791
- _ as buildElementTarget,
792
- re as createGoogleSheetsAdapter,
793
- he as normalizeReviewUrl,
794
- ue as useReviewLens
1600
+ Zt as ReviewLensOverlay,
1601
+ Qt as ReviewLensProvider,
1602
+ X as buildElementTarget,
1603
+ ht as createGoogleSheetsAdapter,
1604
+ Ct as normalizeReviewUrl,
1605
+ Nt as useReviewLens
795
1606
  };