review-lens-react 0.1.0 → 0.1.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,93 +1,93 @@
1
- import { jsx as c, jsxs as k, Fragment as q } from "react/jsx-runtime";
2
- import { createContext as D, useMemo as x, useState as E, useCallback as L, useEffect as R, useContext as H } from "react";
3
- const W = [
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 = [
4
4
  "https://www.googleapis.com/auth/spreadsheets",
5
5
  "https://www.googleapis.com/auth/userinfo.email"
6
- ].join(" "), _ = "https://www.googleapis.com/oauth2/v3/userinfo";
6
+ ].join(" "), Q = "https://www.googleapis.com/oauth2/v3/userinfo";
7
7
  function X(e) {
8
- const n = e.feedbackSheetName ?? "Feedback", t = e.usersSheetName ?? "Users";
9
- let r, o;
10
- async function s() {
11
- return r ?? (r = Z(e.googleClientId)), r;
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;
12
12
  }
13
- async function v(i, l) {
14
- const a = await s(), d = await fetch(
15
- `https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${i}`,
13
+ async function m(d, c) {
14
+ const s = await i(), l = await fetch(
15
+ `https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${d}`,
16
16
  {
17
- ...l,
17
+ ...c,
18
18
  headers: {
19
- Authorization: `Bearer ${a}`,
19
+ Authorization: `Bearer ${s}`,
20
20
  "Content-Type": "application/json",
21
- ...l == null ? void 0 : l.headers
21
+ ...c == null ? void 0 : c.headers
22
22
  }
23
23
  }
24
24
  );
25
- if (!d.ok)
26
- throw new Error(`Google Sheets request failed with ${d.status}`);
27
- return d.json();
25
+ if (!l.ok)
26
+ throw new Error(`Google Sheets request failed with ${l.status}`);
27
+ return l.json();
28
28
  }
29
- async function b(i) {
30
- return (await v(
31
- `/values/${encodeURIComponent(i)}`
29
+ async function v(d) {
30
+ return (await m(
31
+ `/values/${encodeURIComponent(d)}`
32
32
  )).values ?? [];
33
33
  }
34
34
  return {
35
35
  async getCurrentUser() {
36
- if (!o) {
37
- const i = await s(), l = await fetch(_, {
38
- headers: { Authorization: `Bearer ${i}` }
36
+ if (!r) {
37
+ const d = await i(), c = await fetch(Q, {
38
+ headers: { Authorization: `Bearer ${d}` }
39
39
  });
40
- if (!l.ok)
41
- throw new Error(`Google userinfo request failed with ${l.status}`);
42
- o = (await l.json()).email;
40
+ if (!c.ok)
41
+ throw new Error(`Google userinfo request failed with ${c.status}`);
42
+ r = (await c.json()).email;
43
43
  }
44
- if (!o)
44
+ if (!r)
45
45
  throw new Error("Google account did not return an email address");
46
- return { email: o };
46
+ return { email: r };
47
47
  },
48
- async getPermissions(i) {
49
- const [{ email: l }, a] = await Promise.all([this.getCurrentUser(), b(t)]), d = U(a), y = l.toLowerCase(), u = d.find(
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
50
  (f) => {
51
51
  var p;
52
- return ((p = f.email) == null ? void 0 : p.toLowerCase()) === y && f.active !== "false" && (!f.projectKey || f.projectKey === i);
52
+ return ((p = f.email) == null ? void 0 : p.toLowerCase()) === k && f.active !== "false" && (!f.projectKey || f.projectKey === d);
53
53
  }
54
54
  );
55
- return V((u == null ? void 0 : u.role) ?? "designer");
55
+ return ee((h == null ? void 0 : h.role) ?? "designer");
56
56
  },
57
- async listFeedback(i) {
58
- return U(await b(n)).map(T).filter((a) => a !== null).filter(
59
- (a) => a.projectKey === i.projectKey && a.contentId === i.contentId && a.normalizedPath === i.normalizedPath
60
- ).sort((a, d) => d.createdAt.localeCompare(a.createdAt));
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));
61
61
  },
62
- async createFeedback(i) {
63
- const l = (/* @__PURE__ */ new Date()).toISOString(), a = {
64
- ...i,
62
+ async createFeedback(d) {
63
+ const c = (/* @__PURE__ */ new Date()).toISOString(), s = {
64
+ ...d,
65
65
  id: crypto.randomUUID(),
66
66
  status: "open",
67
- createdAt: l,
68
- updatedAt: l
67
+ createdAt: c,
68
+ updatedAt: c
69
69
  };
70
- return await v(`/values/${encodeURIComponent(n)}:append?valueInputOption=RAW`, {
70
+ return await m(`/values/${encodeURIComponent(o)}:append?valueInputOption=RAW`, {
71
71
  method: "POST",
72
- body: JSON.stringify({ values: [Q(a)] })
73
- }), a;
72
+ body: JSON.stringify({ values: [V(s)] })
73
+ }), s;
74
74
  },
75
- async resolveFeedback(i, l) {
76
- const a = await b(n), d = a[0] ?? Y, y = d.indexOf("id"), u = d.indexOf("status"), f = d.indexOf("updatedAt"), p = d.indexOf("resolvedAt"), w = d.indexOf("resolvedBy"), m = a.findIndex((N, A) => A > 0 && N[y] === i);
77
- if (m < 1)
78
- throw new Error(`Feedback ${i} was not found`);
79
- const g = [...a[m]], C = (/* @__PURE__ */ new Date()).toISOString();
80
- g[u] = "resolved", g[f] = C, g[p] = C, g[w] = l, await v(
81
- `/values/${encodeURIComponent(n)}!A${m + 1}:Q${m + 1}?valueInputOption=RAW`,
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)
78
+ 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
82
  {
83
83
  method: "PUT",
84
- body: JSON.stringify({ values: [g] })
84
+ body: JSON.stringify({ values: [b] })
85
85
  }
86
86
  );
87
- const $ = T(M(d, g));
88
- if (!$)
89
- throw new Error(`Feedback ${i} could not be parsed after resolving`);
90
- return $;
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;
91
91
  }
92
92
  };
93
93
  }
@@ -109,7 +109,7 @@ const Y = [
109
109
  "resolvedAt",
110
110
  "resolvedBy"
111
111
  ];
112
- function Q(e) {
112
+ function V(e) {
113
113
  return [
114
114
  e.id,
115
115
  e.projectKey,
@@ -129,14 +129,14 @@ function Q(e) {
129
129
  e.resolvedBy ?? ""
130
130
  ];
131
131
  }
132
- function U(e) {
133
- const [n, ...t] = e;
134
- return n ? t.map((r) => M(n, r)) : [];
132
+ function W(e) {
133
+ const [o, ...t] = e;
134
+ return o ? t.map((n) => O(o, n)) : [];
135
135
  }
136
- function M(e, n) {
137
- return Object.fromEntries(e.map((t, r) => [t, n[r] ?? ""]));
136
+ function O(e, o) {
137
+ return Object.fromEntries(e.map((t, n) => [t, o[n] ?? ""]));
138
138
  }
139
- function T(e) {
139
+ function j(e) {
140
140
  return e.id ? {
141
141
  id: e.id,
142
142
  projectKey: e.projectKey,
@@ -145,23 +145,12 @@ function T(e) {
145
145
  originalUrl: e.originalUrl,
146
146
  selector: e.selector,
147
147
  selectorStrategy: e.selectorStrategy === "stable-attribute" ? "stable-attribute" : "css-path",
148
- elementFingerprint: O(e.elementFingerprintJson, {
148
+ elementFingerprint: K(e.elementFingerprintJson, {
149
149
  tagName: "",
150
150
  width: 0,
151
151
  height: 0
152
152
  }),
153
- cssSnapshot: O(e.cssSnapshotJson, {
154
- margin: "",
155
- padding: "",
156
- border: "",
157
- fontFamily: "",
158
- fontSize: "",
159
- lineHeight: "",
160
- color: "",
161
- backgroundColor: "",
162
- width: 0,
163
- height: 0
164
- }),
153
+ cssSnapshot: Z(e.cssSnapshotJson),
165
154
  comment: e.comment,
166
155
  status: e.status === "resolved" ? "resolved" : "open",
167
156
  authorEmail: e.authorEmail,
@@ -171,175 +160,202 @@ function T(e) {
171
160
  resolvedBy: e.resolvedBy || void 0
172
161
  } : null;
173
162
  }
174
- function O(e, n) {
163
+ function K(e, o) {
175
164
  try {
176
- return e ? JSON.parse(e) : n;
165
+ return e ? JSON.parse(e) : o;
177
166
  } catch {
178
- return n;
167
+ return o;
179
168
  }
180
169
  }
181
- function V(e) {
170
+ function Z(e) {
171
+ const o = K(e, {});
172
+ 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
195
+ };
196
+ }
197
+ function ee(e) {
182
198
  return e === "admin" ? ["create", "read", "resolve"] : e === "developer" ? ["read", "resolve"] : ["create", "read"];
183
199
  }
184
- async function Z(e) {
185
- return await ee(), new Promise((n, t) => {
186
- var o;
187
- const r = (o = window.google) == null ? void 0 : o.accounts.oauth2.initTokenClient({
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({
188
204
  client_id: e,
189
- scope: W,
190
- callback: (s) => {
191
- if (s.error || !s.access_token) {
192
- t(new Error(s.error ?? "Google OAuth did not return an access token"));
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"));
193
209
  return;
194
210
  }
195
- n(s.access_token);
211
+ o(i.access_token);
196
212
  }
197
213
  });
198
- r == null || r.requestAccessToken({ prompt: "" });
214
+ n == null || n.requestAccessToken({ prompt: "" });
199
215
  });
200
216
  }
201
- function ee() {
217
+ function oe() {
202
218
  var e;
203
- return (e = window.google) != null && e.accounts.oauth2 ? Promise.resolve() : new Promise((n, t) => {
204
- const r = document.querySelector(
219
+ return (e = window.google) != null && e.accounts.oauth2 ? Promise.resolve() : new Promise((o, t) => {
220
+ const n = document.querySelector(
205
221
  'script[src="https://accounts.google.com/gsi/client"]'
206
222
  );
207
- if (r) {
208
- r.addEventListener("load", () => n(), { once: !0 }), r.addEventListener("error", () => t(new Error("Google Identity failed to load")), {
223
+ if (n) {
224
+ n.addEventListener("load", () => o(), { once: !0 }), n.addEventListener("error", () => t(new Error("Google Identity failed to load")), {
209
225
  once: !0
210
226
  });
211
227
  return;
212
228
  }
213
- const o = document.createElement("script");
214
- 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);
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);
215
231
  });
216
232
  }
217
- function te(e) {
233
+ function ne(e) {
218
234
  return new URL(e, window.location.href).pathname.replace(/\/+$/, "") || "/";
219
235
  }
220
- const J = D(null);
221
- function me({ config: e, children: n }) {
222
- const t = x(() => e.adapter ? e.adapter : X({
223
- googleClientId: z(e.googleClientId, "googleClientId"),
224
- spreadsheetId: z(e.spreadsheetId, "spreadsheetId"),
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"),
225
241
  feedbackSheetName: e.sheetName ?? "Feedback"
226
- }), [e.adapter, e.googleClientId, e.sheetName, e.spreadsheetId]), r = e.currentUrl ?? window.location.href, o = (e.normalizeUrl ?? te)(r), [s, v] = E(), [b, i] = E([]), [l, a] = E([]), d = L(async () => {
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 () => {
227
243
  const p = await t.listFeedback({
228
244
  projectKey: e.projectKey,
229
245
  contentId: e.contentId,
230
- normalizedPath: o
246
+ normalizedPath: r
231
247
  });
232
- a(p);
233
- }, [t, e.contentId, e.projectKey, o]);
234
- R(() => {
248
+ s(p);
249
+ }, [t, e.contentId, e.projectKey, r]);
250
+ P(() => {
235
251
  let p = !0;
236
252
  async function w() {
237
- const [m, g] = await Promise.all([
253
+ const [g, b] = await Promise.all([
238
254
  t.getCurrentUser(),
239
255
  t.getPermissions(e.projectKey)
240
256
  ]);
241
- p && (v(m), i(g), await d());
257
+ p && (m(g), d(b), await l());
242
258
  }
243
259
  return w(), () => {
244
260
  p = !1;
245
261
  };
246
- }, [t, e.projectKey, d]);
247
- const y = L(
262
+ }, [t, e.projectKey, l]);
263
+ const k = F(
248
264
  async (p) => {
249
265
  const w = await t.createFeedback(p);
250
- return a((m) => [w, ...m]), w;
266
+ return s((g) => [w, ...g]), w;
251
267
  },
252
268
  [t]
253
- ), u = L(
269
+ ), h = F(
254
270
  async (p) => {
255
- const w = await t.resolveFeedback(p, (s == null ? void 0 : s.email) ?? "");
256
- return a(
257
- (m) => m.map((g) => g.id === p ? w : g)
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)
258
274
  ), w;
259
275
  },
260
- [t, s == null ? void 0 : s.email]
261
- ), f = x(
276
+ [t, i == null ? void 0 : i.email]
277
+ ), f = $(
262
278
  () => ({
263
279
  config: e,
264
280
  adapter: t,
265
- currentUser: s,
266
- permissions: b,
267
- feedback: l,
268
- normalizedPath: o,
269
- refreshFeedback: d,
270
- createFeedback: y,
271
- resolveFeedback: u
281
+ currentUser: i,
282
+ permissions: v,
283
+ feedback: c,
284
+ normalizedPath: r,
285
+ refreshFeedback: l,
286
+ createFeedback: k,
287
+ resolveFeedback: h
272
288
  }),
273
289
  [
274
290
  t,
275
291
  e,
276
- y,
277
- s,
292
+ k,
293
+ i,
294
+ c,
295
+ r,
296
+ v,
278
297
  l,
279
- o,
280
- b,
281
- d,
282
- u
298
+ h
283
299
  ]
284
300
  );
285
- return /* @__PURE__ */ c(J.Provider, { value: f, children: n });
301
+ return /* @__PURE__ */ a(_.Provider, { value: f, children: o });
286
302
  }
287
- function ne() {
288
- const e = H(J);
303
+ function re() {
304
+ const e = H(_);
289
305
  if (!e)
290
306
  throw new Error("useReviewLens must be used inside ReviewLensProvider");
291
307
  return e;
292
308
  }
293
- function z(e, n) {
309
+ function U(e, o) {
294
310
  if (!e)
295
- throw new Error(`review-lens-react requires config.${n} when no adapter is provided`);
311
+ throw new Error(`review-lens-react requires config.${o} when no adapter is provided`);
296
312
  return e;
297
313
  }
298
- const re = [
314
+ const ie = [
299
315
  "data-review-id",
300
316
  "data-testid",
301
317
  "data-test-id",
302
318
  "aria-label",
303
319
  "name"
304
320
  ];
305
- function B(e) {
306
- const n = e.getBoundingClientRect(), t = oe(e);
321
+ function M(e) {
322
+ const o = e.getBoundingClientRect(), t = se(e);
307
323
  return {
308
324
  selector: t.selector,
309
325
  selectorStrategy: t.strategy,
310
- fingerprint: ae(e, n),
311
- cssSnapshot: ie(e, n),
312
- rect: n
326
+ fingerprint: de(e, o),
327
+ cssSnapshot: ce(e, o),
328
+ rect: o
313
329
  };
314
330
  }
315
- function oe(e) {
316
- for (const n of re) {
317
- const t = e.getAttribute(n);
331
+ function se(e) {
332
+ for (const o of ie) {
333
+ const t = e.getAttribute(o);
318
334
  if (t)
319
335
  return {
320
- selector: `[${n}="${K(t)}"]`,
336
+ selector: `[${o}="${z(t)}"]`,
321
337
  strategy: "stable-attribute"
322
338
  };
323
339
  }
324
- return e.id ? { selector: `#${K(e.id)}`, strategy: "stable-attribute" } : { selector: se(e), strategy: "css-path" };
340
+ return e.id ? { selector: `#${z(e.id)}`, strategy: "stable-attribute" } : { selector: ae(e), strategy: "css-path" };
325
341
  }
326
- function se(e) {
327
- const n = [];
342
+ function ae(e) {
343
+ const o = [];
328
344
  let t = e;
329
345
  for (; t && t.nodeType === Node.ELEMENT_NODE && t !== document.body; ) {
330
- const r = t.parentElement, o = t.tagName.toLowerCase();
331
- if (!r) {
332
- n.unshift(o);
346
+ const n = t.parentElement, r = t.tagName.toLowerCase();
347
+ if (!n) {
348
+ o.unshift(r);
333
349
  break;
334
350
  }
335
- const s = t.tagName, v = Array.from(r.children).filter(
336
- (i) => i.tagName === s
337
- ), b = v.indexOf(t) + 1;
338
- n.unshift(v.length > 1 ? `${o}:nth-of-type(${b})` : o), t = r;
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;
339
355
  }
340
- return n.join(" > ");
356
+ return o.join(" > ");
341
357
  }
342
- function ae(e, n) {
358
+ function de(e, o) {
343
359
  var t;
344
360
  return {
345
361
  tagName: e.tagName.toLowerCase(),
@@ -347,203 +363,259 @@ function ae(e, n) {
347
363
  className: e.getAttribute("class") || void 0,
348
364
  textSnippet: ((t = e.textContent) == null ? void 0 : t.trim().slice(0, 80)) || void 0,
349
365
  ariaLabel: e.getAttribute("aria-label") || void 0,
350
- width: Math.round(n.width),
351
- height: Math.round(n.height)
366
+ width: Math.round(o.width),
367
+ height: Math.round(o.height)
352
368
  };
353
369
  }
354
- function ie(e, n) {
370
+ function ce(e, o) {
355
371
  const t = window.getComputedStyle(e);
356
372
  return {
357
- margin: j(t.marginTop, t.marginRight, t.marginBottom, t.marginLeft),
358
- padding: j(
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(
359
379
  t.paddingTop,
360
380
  t.paddingRight,
361
381
  t.paddingBottom,
362
382
  t.paddingLeft
363
383
  ),
364
- border: j(
384
+ paddingTop: t.paddingTop,
385
+ paddingRight: t.paddingRight,
386
+ paddingBottom: t.paddingBottom,
387
+ paddingLeft: t.paddingLeft,
388
+ border: A(
365
389
  t.borderTopWidth,
366
390
  t.borderRightWidth,
367
391
  t.borderBottomWidth,
368
392
  t.borderLeftWidth
369
393
  ),
394
+ borderTopWidth: t.borderTopWidth,
395
+ borderRightWidth: t.borderRightWidth,
396
+ borderBottomWidth: t.borderBottomWidth,
397
+ borderLeftWidth: t.borderLeftWidth,
370
398
  fontFamily: t.fontFamily,
371
399
  fontSize: t.fontSize,
372
400
  lineHeight: t.lineHeight,
373
401
  color: t.color,
374
402
  backgroundColor: t.backgroundColor,
375
- width: Math.round(n.width),
376
- height: Math.round(n.height)
403
+ width: Math.round(o.width),
404
+ height: Math.round(o.height)
377
405
  };
378
406
  }
379
- function j(e, n, t, r) {
380
- return e === n && n === t && t === r ? e : `${e} ${n} ${t} ${r}`;
407
+ function A(e, o, t, n) {
408
+ return e === o && o === t && t === n ? e : `${e} ${o} ${t} ${n}`;
381
409
  }
382
- function K(e) {
410
+ function z(e) {
383
411
  return typeof CSS < "u" && typeof CSS.escape == "function" ? CSS.escape(e) : e.replace(/["\\]/g, "\\$&");
384
412
  }
385
- function fe({
413
+ function be({
386
414
  open: e,
387
- onOpenChange: n,
415
+ onOpenChange: o,
388
416
  placement: t = "top-right",
389
- showResolved: r = !1
417
+ showResolved: n = !1
390
418
  }) {
391
419
  const {
392
- config: o,
393
- currentUser: s,
394
- feedback: v,
395
- normalizedPath: b,
396
- permissions: i,
397
- createFeedback: l,
398
- resolveFeedback: a
399
- } = ne(), [d, y] = E(), [u, f] = E(), [p, w] = E(""), [m, g] = E(), C = i.includes("create"), $ = i.includes("resolve"), N = x(
400
- () => v.filter((h) => r || h.status !== "resolved"),
401
- [v, r]
420
+ config: r,
421
+ currentUser: i,
422
+ feedback: m,
423
+ 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]
402
430
  );
403
- R(() => {
404
- e || (y(void 0), f(void 0), w(""));
431
+ P(() => {
432
+ e || (k(void 0), f(void 0), w(""));
405
433
  }, [e]);
406
- const A = L((h) => {
407
- const I = h.target instanceof Element ? h.target : null;
408
- if (I)
409
- return I.closest("[data-review-lens-ui]") ? null : I;
410
- const S = document.elementFromPoint(h.clientX, h.clientY);
411
- return !S || S.closest("[data-review-lens-ui]") ? null : S;
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;
412
440
  }, []);
413
- if (R(() => {
414
- if (!e || u)
441
+ if (P(() => {
442
+ if (!e || h)
415
443
  return;
416
- function h(S) {
417
- const F = A(S);
418
- F && y(B(F));
444
+ function u(C) {
445
+ const B = x(C);
446
+ k(B ? M(B) : void 0);
419
447
  }
420
- function I(S) {
421
- const F = A(S);
422
- F && (S.preventDefault(), S.stopPropagation(), f(B(F)));
448
+ function E(C) {
449
+ const B = x(C);
450
+ B && (C.preventDefault(), C.stopPropagation(), f(M(B)));
423
451
  }
424
- return window.addEventListener("mousemove", h, !0), window.addEventListener("click", I, !0), () => {
425
- window.removeEventListener("mousemove", h, !0), window.removeEventListener("click", I, !0);
452
+ return window.addEventListener("mousemove", u, !0), window.addEventListener("click", E, !0), () => {
453
+ window.removeEventListener("mousemove", u, !0), window.removeEventListener("click", E, !0);
426
454
  };
427
- }, [A, u, e]), !e)
455
+ }, [x, h, e]), !e)
428
456
  return null;
429
- const P = u ?? d;
430
- async function G() {
431
- !u || !p.trim() || !s || !C || (await l({
432
- projectKey: o.projectKey,
433
- contentId: o.contentId,
434
- normalizedPath: b,
435
- originalUrl: o.currentUrl ?? window.location.href,
436
- selector: u.selector,
437
- selectorStrategy: u.selectorStrategy,
438
- elementFingerprint: u.fingerprint,
439
- cssSnapshot: u.cssSnapshot,
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,
440
468
  comment: p.trim(),
441
- authorEmail: s.email
469
+ authorEmail: i.email
442
470
  }), w(""), f(void 0));
443
471
  }
444
- return /* @__PURE__ */ k("div", { className: "review-lens-root", "data-review-lens-ui": !0, children: [
445
- P ? /* @__PURE__ */ c(ce, { target: P, locked: !!u }) : null,
446
- /* @__PURE__ */ c(
447
- le,
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,
448
476
  {
449
- feedback: N,
450
- selectedFeedback: m,
451
- onSelect: g
477
+ feedback: R,
478
+ selectedFeedback: g,
479
+ onSelect: b
452
480
  }
453
481
  ),
454
- /* @__PURE__ */ k("aside", { className: `review-lens-panel review-lens-panel--${t}`, "data-review-lens-ui": !0, children: [
455
- /* @__PURE__ */ k("header", { className: "review-lens-panel__header", children: [
456
- /* @__PURE__ */ k("div", { children: [
457
- /* @__PURE__ */ c("p", { className: "review-lens-kicker", children: "Review Lens" }),
458
- /* @__PURE__ */ c("h2", { children: u ? "Element locked" : "Inspecting" })
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" })
459
487
  ] }),
460
- /* @__PURE__ */ c("button", { type: "button", onClick: () => n == null ? void 0 : n(!1), children: "Close" })
488
+ /* @__PURE__ */ a("button", { type: "button", onClick: () => o == null ? void 0 : o(!1), children: "Close" })
461
489
  ] }),
462
- P ? /* @__PURE__ */ c(de, { target: P }) : /* @__PURE__ */ c("p", { children: "Move over the app to inspect." }),
463
- u ? /* @__PURE__ */ k(
490
+ T ? /* @__PURE__ */ a(ue, { target: T }) : /* @__PURE__ */ a("p", { children: "Move over the app to inspect." }),
491
+ h ? /* @__PURE__ */ S(
464
492
  "form",
465
493
  {
466
494
  className: "review-lens-feedback-form",
467
- onSubmit: (h) => {
468
- h.preventDefault(), G();
495
+ onSubmit: (u) => {
496
+ u.preventDefault(), J();
469
497
  },
470
498
  children: [
471
- /* @__PURE__ */ c("label", { htmlFor: "review-lens-comment", children: "Feedback" }),
472
- /* @__PURE__ */ c(
499
+ /* @__PURE__ */ a("label", { htmlFor: "review-lens-comment", children: "Feedback" }),
500
+ /* @__PURE__ */ a(
473
501
  "textarea",
474
502
  {
475
503
  id: "review-lens-comment",
476
504
  value: p,
477
- disabled: !C,
478
- onChange: (h) => w(h.target.value),
479
- placeholder: C ? "Describe the UX issue..." : "You do not have permission to comment."
505
+ disabled: !L,
506
+ onChange: (u) => w(u.target.value),
507
+ placeholder: L ? "Describe the UX issue..." : "You do not have permission to comment."
480
508
  }
481
509
  ),
482
- /* @__PURE__ */ k("div", { className: "review-lens-actions", children: [
483
- /* @__PURE__ */ c("button", { type: "button", onClick: () => f(void 0), children: "Unlock" }),
484
- /* @__PURE__ */ c("button", { type: "submit", disabled: !p.trim() || !C, children: "Save feedback" })
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" })
485
513
  ] })
486
514
  ]
487
515
  }
488
516
  ) : null,
489
- /* @__PURE__ */ k("section", { className: "review-lens-comments", children: [
490
- /* @__PURE__ */ c("h3", { children: "Page feedback" }),
491
- N.length === 0 ? /* @__PURE__ */ c("p", { children: "No feedback for this view." }) : null,
492
- N.map((h) => /* @__PURE__ */ k(
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(
493
521
  "article",
494
522
  {
495
- className: (m == null ? void 0 : m.id) === h.id ? "review-lens-comment review-lens-comment--selected" : "review-lens-comment",
523
+ className: (g == null ? void 0 : g.id) === u.id ? "review-lens-comment review-lens-comment--selected" : "review-lens-comment",
496
524
  children: [
497
- /* @__PURE__ */ c("p", { children: h.comment }),
498
- /* @__PURE__ */ c("span", { children: h.authorEmail }),
499
- h.status === "open" && $ ? /* @__PURE__ */ c("button", { type: "button", onClick: () => void a(h.id), children: "Resolve" }) : null
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
500
528
  ]
501
529
  },
502
- h.id
530
+ u.id
503
531
  ))
504
532
  ] })
505
533
  ] })
506
534
  ] });
507
535
  }
508
- function ce({ target: e, locked: n }) {
509
- return /* @__PURE__ */ c(
536
+ function le({ target: e, locked: o }) {
537
+ const t = me(e);
538
+ return /* @__PURE__ */ S(
510
539
  "div",
511
540
  {
512
- className: n ? "review-lens-highlight review-lens-highlight--locked" : "review-lens-highlight",
541
+ className: o ? "review-lens-highlight review-lens-highlight--locked" : "review-lens-highlight",
513
542
  style: {
514
- top: e.rect.top + window.scrollY,
515
- left: e.rect.left + window.scrollX,
516
- width: e.rect.width,
517
- height: e.rect.height
518
- }
543
+ top: t.margin.top,
544
+ left: t.margin.left,
545
+ width: t.margin.width,
546
+ height: t.margin.height
547
+ },
548
+ children: [
549
+ /* @__PURE__ */ a(
550
+ "div",
551
+ {
552
+ className: "review-lens-highlight__border",
553
+ 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
558
+ }
559
+ }
560
+ ),
561
+ /* @__PURE__ */ a(
562
+ "div",
563
+ {
564
+ className: "review-lens-highlight__padding",
565
+ 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
570
+ }
571
+ }
572
+ ),
573
+ /* @__PURE__ */ a(
574
+ "div",
575
+ {
576
+ className: "review-lens-highlight__content",
577
+ 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
582
+ }
583
+ }
584
+ ),
585
+ /* @__PURE__ */ S("div", { className: "review-lens-highlight__label", children: [
586
+ Math.round(e.rect.width),
587
+ " x ",
588
+ Math.round(e.rect.height)
589
+ ] })
590
+ ]
519
591
  }
520
592
  );
521
593
  }
522
- function le({
594
+ function he({
523
595
  feedback: e,
524
- selectedFeedback: n,
596
+ selectedFeedback: o,
525
597
  onSelect: t
526
598
  }) {
527
- return /* @__PURE__ */ c(q, { children: e.map((r) => {
528
- const o = ue(r.selector), s = o == null ? void 0 : o.getBoundingClientRect();
529
- return s ? /* @__PURE__ */ c(
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(
530
602
  "button",
531
603
  {
532
604
  type: "button",
533
- className: (n == null ? void 0 : n.id) === r.id ? "review-lens-marker review-lens-marker--selected" : "review-lens-marker",
605
+ className: (o == null ? void 0 : o.id) === n.id ? "review-lens-marker review-lens-marker--selected" : "review-lens-marker",
534
606
  style: {
535
- top: s.top + window.scrollY,
536
- left: s.left + window.scrollX + s.width
607
+ top: i.top,
608
+ left: i.left + i.width
537
609
  },
538
- onClick: () => t(r),
539
- "aria-label": `Open feedback from ${r.authorEmail}`
610
+ onClick: () => t(n),
611
+ "aria-label": `Open feedback from ${n.authorEmail}`
540
612
  },
541
- r.id
613
+ n.id
542
614
  ) : null;
543
615
  }) });
544
616
  }
545
- function de({ target: e }) {
546
- const n = [
617
+ function ue({ target: e }) {
618
+ const o = [
547
619
  ["Selector", e.selector],
548
620
  ["Size", `${e.cssSnapshot.width} x ${e.cssSnapshot.height}`],
549
621
  ["Margin", e.cssSnapshot.margin],
@@ -554,23 +626,71 @@ function de({ target: e }) {
554
626
  ["Color", e.cssSnapshot.color],
555
627
  ["Background", e.cssSnapshot.backgroundColor]
556
628
  ];
557
- return /* @__PURE__ */ c("dl", { className: "review-lens-metrics", children: n.map(([t, r]) => /* @__PURE__ */ k("div", { children: [
558
- /* @__PURE__ */ c("dt", { children: t }),
559
- /* @__PURE__ */ c("dd", { children: r })
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 })
560
632
  ] }, t)) });
561
633
  }
562
- function ue(e) {
634
+ function pe(e) {
563
635
  try {
564
636
  return document.querySelector(e);
565
637
  } catch {
566
638
  return null;
567
639
  }
568
640
  }
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)
652
+ }, 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 = {
658
+ top: e.rect.top,
659
+ left: e.rect.left,
660
+ width: Math.max(e.rect.width, 0),
661
+ 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)
677
+ };
678
+ return {
679
+ margin: i,
680
+ border: r,
681
+ padding: m,
682
+ content: v
683
+ };
684
+ }
685
+ function y(e) {
686
+ const o = Number.parseFloat(e || "0");
687
+ return Number.isFinite(o) ? o : 0;
688
+ }
569
689
  export {
570
- fe as ReviewLensOverlay,
571
- me as ReviewLensProvider,
572
- B as buildElementTarget,
690
+ be as ReviewLensOverlay,
691
+ we as ReviewLensProvider,
692
+ M as buildElementTarget,
573
693
  X as createGoogleSheetsAdapter,
574
- te as normalizeReviewUrl,
575
- ne as useReviewLens
694
+ ne as normalizeReviewUrl,
695
+ re as useReviewLens
576
696
  };
@@ -1 +1 @@
1
- (function(w,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],r):(w=typeof globalThis<"u"?globalThis:w||self,r(w.ReviewLensReact={},w.jsxRuntime,w.React))})(this,(function(w,r,p){"use strict";const J=["https://www.googleapis.com/auth/spreadsheets","https://www.googleapis.com/auth/userinfo.email"].join(" "),D="https://www.googleapis.com/oauth2/v3/userinfo";function T(e){const n=e.feedbackSheetName??"Feedback",t=e.usersSheetName??"Users";let o,s;async function a(){return o??(o=X(e.googleClientId)),o}async function y(c,l){const i=await a(),d=await fetch(`https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${c}`,{...l,headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json",...l==null?void 0:l.headers}});if(!d.ok)throw new Error(`Google Sheets request failed with ${d.status}`);return d.json()}async function S(c){return(await y(`/values/${encodeURIComponent(c)}`)).values??[]}return{async getCurrentUser(){if(!s){const c=await a(),l=await fetch(D,{headers:{Authorization:`Bearer ${c}`}});if(!l.ok)throw new Error(`Google userinfo request failed with ${l.status}`);s=(await l.json()).email}if(!s)throw new Error("Google account did not return an email address");return{email:s}},async getPermissions(c){const[{email:l},i]=await Promise.all([this.getCurrentUser(),S(t)]),d=U(i),k=l.toLowerCase(),u=d.find(g=>{var f;return((f=g.email)==null?void 0:f.toLowerCase())===k&&g.active!=="false"&&(!g.projectKey||g.projectKey===c)});return _((u==null?void 0:u.role)??"designer")},async listFeedback(c){return U(await S(n)).map(x).filter(i=>i!==null).filter(i=>i.projectKey===c.projectKey&&i.contentId===c.contentId&&i.normalizedPath===c.normalizedPath).sort((i,d)=>d.createdAt.localeCompare(i.createdAt))},async createFeedback(c){const l=new Date().toISOString(),i={...c,id:crypto.randomUUID(),status:"open",createdAt:l,updatedAt:l};return await y(`/values/${encodeURIComponent(n)}:append?valueInputOption=RAW`,{method:"POST",body:JSON.stringify({values:[W(i)]})}),i},async resolveFeedback(c,l){const i=await S(n),d=i[0]??H,k=d.indexOf("id"),u=d.indexOf("status"),g=d.indexOf("updatedAt"),f=d.indexOf("resolvedAt"),v=d.indexOf("resolvedBy"),m=i.findIndex((A,N)=>N>0&&A[k]===c);if(m<1)throw new Error(`Feedback ${c} was not found`);const b=[...i[m]],E=new Date().toISOString();b[u]="resolved",b[g]=E,b[f]=E,b[v]=l,await y(`/values/${encodeURIComponent(n)}!A${m+1}:Q${m+1}?valueInputOption=RAW`,{method:"PUT",body:JSON.stringify({values:[b]})});const P=x(O(d,b));if(!P)throw new Error(`Feedback ${c} could not be parsed after resolving`);return P}}}const H=["id","projectKey","contentId","normalizedPath","originalUrl","selector","selectorStrategy","elementFingerprintJson","cssSnapshotJson","comment","status","authorEmail","createdAt","updatedAt","resolvedAt","resolvedBy"];function W(e){return[e.id,e.projectKey,e.contentId,e.normalizedPath,e.originalUrl,e.selector,e.selectorStrategy,JSON.stringify(e.elementFingerprint),JSON.stringify(e.cssSnapshot),e.comment,e.status,e.authorEmail,e.createdAt,e.updatedAt,e.resolvedAt??"",e.resolvedBy??""]}function U(e){const[n,...t]=e;return n?t.map(o=>O(n,o)):[]}function O(e,n){return Object.fromEntries(e.map((t,o)=>[t,n[o]??""]))}function x(e){return e.id?{id:e.id,projectKey:e.projectKey,contentId:e.contentId,normalizedPath:e.normalizedPath,originalUrl:e.originalUrl,selector:e.selector,selectorStrategy:e.selectorStrategy==="stable-attribute"?"stable-attribute":"css-path",elementFingerprint:z(e.elementFingerprintJson,{tagName:"",width:0,height:0}),cssSnapshot:z(e.cssSnapshotJson,{margin:"",padding:"",border:"",fontFamily:"",fontSize:"",lineHeight:"",color:"",backgroundColor:"",width:0,height:0}),comment:e.comment,status:e.status==="resolved"?"resolved":"open",authorEmail:e.authorEmail,createdAt:e.createdAt,updatedAt:e.updatedAt,resolvedAt:e.resolvedAt||void 0,resolvedBy:e.resolvedBy||void 0}:null}function z(e,n){try{return e?JSON.parse(e):n}catch{return n}}function _(e){return e==="admin"?["create","read","resolve"]:e==="developer"?["read","resolve"]:["create","read"]}async function X(e){return await Y(),new Promise((n,t)=>{var s;const o=(s=window.google)==null?void 0:s.accounts.oauth2.initTokenClient({client_id:e,scope:J,callback:a=>{if(a.error||!a.access_token){t(new Error(a.error??"Google OAuth did not return an access token"));return}n(a.access_token)}});o==null||o.requestAccessToken({prompt:""})})}function Y(){var e;return(e=window.google)!=null&&e.accounts.oauth2?Promise.resolve():new Promise((n,t)=>{const o=document.querySelector('script[src="https://accounts.google.com/gsi/client"]');if(o){o.addEventListener("load",()=>n(),{once:!0}),o.addEventListener("error",()=>t(new Error("Google Identity failed to load")),{once:!0});return}const s=document.createElement("script");s.src="https://accounts.google.com/gsi/client",s.async=!0,s.defer=!0,s.onload=()=>n(),s.onerror=()=>t(new Error("Google Identity failed to load")),document.head.append(s)})}function B(e){return new URL(e,window.location.href).pathname.replace(/\/+$/,"")||"/"}const K=p.createContext(null);function Q({config:e,children:n}){const t=p.useMemo(()=>e.adapter?e.adapter:T({googleClientId:q(e.googleClientId,"googleClientId"),spreadsheetId:q(e.spreadsheetId,"spreadsheetId"),feedbackSheetName:e.sheetName??"Feedback"}),[e.adapter,e.googleClientId,e.sheetName,e.spreadsheetId]),o=e.currentUrl??window.location.href,s=(e.normalizeUrl??B)(o),[a,y]=p.useState(),[S,c]=p.useState([]),[l,i]=p.useState([]),d=p.useCallback(async()=>{const f=await t.listFeedback({projectKey:e.projectKey,contentId:e.contentId,normalizedPath:s});i(f)},[t,e.contentId,e.projectKey,s]);p.useEffect(()=>{let f=!0;async function v(){const[m,b]=await Promise.all([t.getCurrentUser(),t.getPermissions(e.projectKey)]);f&&(y(m),c(b),await d())}return v(),()=>{f=!1}},[t,e.projectKey,d]);const k=p.useCallback(async f=>{const v=await t.createFeedback(f);return i(m=>[v,...m]),v},[t]),u=p.useCallback(async f=>{const v=await t.resolveFeedback(f,(a==null?void 0:a.email)??"");return i(m=>m.map(b=>b.id===f?v:b)),v},[t,a==null?void 0:a.email]),g=p.useMemo(()=>({config:e,adapter:t,currentUser:a,permissions:S,feedback:l,normalizedPath:s,refreshFeedback:d,createFeedback:k,resolveFeedback:u}),[t,e,k,a,l,s,S,d,u]);return r.jsx(K.Provider,{value:g,children:n})}function M(){const e=p.useContext(K);if(!e)throw new Error("useReviewLens must be used inside ReviewLensProvider");return e}function q(e,n){if(!e)throw new Error(`review-lens-react requires config.${n} when no adapter is provided`);return e}const V=["data-review-id","data-testid","data-test-id","aria-label","name"];function L(e){const n=e.getBoundingClientRect(),t=Z(e);return{selector:t.selector,selectorStrategy:t.strategy,fingerprint:ee(e,n),cssSnapshot:te(e,n),rect:n}}function Z(e){for(const n of V){const t=e.getAttribute(n);if(t)return{selector:`[${n}="${G(t)}"]`,strategy:"stable-attribute"}}return e.id?{selector:`#${G(e.id)}`,strategy:"stable-attribute"}:{selector:R(e),strategy:"css-path"}}function R(e){const n=[];let t=e;for(;t&&t.nodeType===Node.ELEMENT_NODE&&t!==document.body;){const o=t.parentElement,s=t.tagName.toLowerCase();if(!o){n.unshift(s);break}const a=t.tagName,y=Array.from(o.children).filter(c=>c.tagName===a),S=y.indexOf(t)+1;n.unshift(y.length>1?`${s}:nth-of-type(${S})`:s),t=o}return n.join(" > ")}function ee(e,n){var t;return{tagName:e.tagName.toLowerCase(),id:e.id||void 0,className:e.getAttribute("class")||void 0,textSnippet:((t=e.textContent)==null?void 0:t.trim().slice(0,80))||void 0,ariaLabel:e.getAttribute("aria-label")||void 0,width:Math.round(n.width),height:Math.round(n.height)}}function te(e,n){const t=window.getComputedStyle(e);return{margin:j(t.marginTop,t.marginRight,t.marginBottom,t.marginLeft),padding:j(t.paddingTop,t.paddingRight,t.paddingBottom,t.paddingLeft),border:j(t.borderTopWidth,t.borderRightWidth,t.borderBottomWidth,t.borderLeftWidth),fontFamily:t.fontFamily,fontSize:t.fontSize,lineHeight:t.lineHeight,color:t.color,backgroundColor:t.backgroundColor,width:Math.round(n.width),height:Math.round(n.height)}}function j(e,n,t,o){return e===n&&n===t&&t===o?e:`${e} ${n} ${t} ${o}`}function G(e){return typeof CSS<"u"&&typeof CSS.escape=="function"?CSS.escape(e):e.replace(/["\\]/g,"\\$&")}function ne({open:e,onOpenChange:n,placement:t="top-right",showResolved:o=!1}){const{config:s,currentUser:a,feedback:y,normalizedPath:S,permissions:c,createFeedback:l,resolveFeedback:i}=M(),[d,k]=p.useState(),[u,g]=p.useState(),[f,v]=p.useState(""),[m,b]=p.useState(),E=c.includes("create"),P=c.includes("resolve"),A=p.useMemo(()=>y.filter(h=>o||h.status!=="resolved"),[y,o]);p.useEffect(()=>{e||(k(void 0),g(void 0),v(""))},[e]);const N=p.useCallback(h=>{const I=h.target instanceof Element?h.target:null;if(I)return I.closest("[data-review-lens-ui]")?null:I;const C=document.elementFromPoint(h.clientX,h.clientY);return!C||C.closest("[data-review-lens-ui]")?null:C},[]);if(p.useEffect(()=>{if(!e||u)return;function h(C){const F=N(C);F&&k(L(F))}function I(C){const F=N(C);F&&(C.preventDefault(),C.stopPropagation(),g(L(F)))}return window.addEventListener("mousemove",h,!0),window.addEventListener("click",I,!0),()=>{window.removeEventListener("mousemove",h,!0),window.removeEventListener("click",I,!0)}},[N,u,e]),!e)return null;const $=u??d;async function ie(){!u||!f.trim()||!a||!E||(await l({projectKey:s.projectKey,contentId:s.contentId,normalizedPath:S,originalUrl:s.currentUrl??window.location.href,selector:u.selector,selectorStrategy:u.selectorStrategy,elementFingerprint:u.fingerprint,cssSnapshot:u.cssSnapshot,comment:f.trim(),authorEmail:a.email}),v(""),g(void 0))}return r.jsxs("div",{className:"review-lens-root","data-review-lens-ui":!0,children:[$?r.jsx(re,{target:$,locked:!!u}):null,r.jsx(oe,{feedback:A,selectedFeedback:m,onSelect:b}),r.jsxs("aside",{className:`review-lens-panel review-lens-panel--${t}`,"data-review-lens-ui":!0,children:[r.jsxs("header",{className:"review-lens-panel__header",children:[r.jsxs("div",{children:[r.jsx("p",{className:"review-lens-kicker",children:"Review Lens"}),r.jsx("h2",{children:u?"Element locked":"Inspecting"})]}),r.jsx("button",{type:"button",onClick:()=>n==null?void 0:n(!1),children:"Close"})]}),$?r.jsx(se,{target:$}):r.jsx("p",{children:"Move over the app to inspect."}),u?r.jsxs("form",{className:"review-lens-feedback-form",onSubmit:h=>{h.preventDefault(),ie()},children:[r.jsx("label",{htmlFor:"review-lens-comment",children:"Feedback"}),r.jsx("textarea",{id:"review-lens-comment",value:f,disabled:!E,onChange:h=>v(h.target.value),placeholder:E?"Describe the UX issue...":"You do not have permission to comment."}),r.jsxs("div",{className:"review-lens-actions",children:[r.jsx("button",{type:"button",onClick:()=>g(void 0),children:"Unlock"}),r.jsx("button",{type:"submit",disabled:!f.trim()||!E,children:"Save feedback"})]})]}):null,r.jsxs("section",{className:"review-lens-comments",children:[r.jsx("h3",{children:"Page feedback"}),A.length===0?r.jsx("p",{children:"No feedback for this view."}):null,A.map(h=>r.jsxs("article",{className:(m==null?void 0:m.id)===h.id?"review-lens-comment review-lens-comment--selected":"review-lens-comment",children:[r.jsx("p",{children:h.comment}),r.jsx("span",{children:h.authorEmail}),h.status==="open"&&P?r.jsx("button",{type:"button",onClick:()=>void i(h.id),children:"Resolve"}):null]},h.id))]})]})]})}function re({target:e,locked:n}){return r.jsx("div",{className:n?"review-lens-highlight review-lens-highlight--locked":"review-lens-highlight",style:{top:e.rect.top+window.scrollY,left:e.rect.left+window.scrollX,width:e.rect.width,height:e.rect.height}})}function oe({feedback:e,selectedFeedback:n,onSelect:t}){return r.jsx(r.Fragment,{children:e.map(o=>{const s=ae(o.selector),a=s==null?void 0:s.getBoundingClientRect();return a?r.jsx("button",{type:"button",className:(n==null?void 0:n.id)===o.id?"review-lens-marker review-lens-marker--selected":"review-lens-marker",style:{top:a.top+window.scrollY,left:a.left+window.scrollX+a.width},onClick:()=>t(o),"aria-label":`Open feedback from ${o.authorEmail}`},o.id):null})})}function se({target:e}){const n=[["Selector",e.selector],["Size",`${e.cssSnapshot.width} x ${e.cssSnapshot.height}`],["Margin",e.cssSnapshot.margin],["Padding",e.cssSnapshot.padding],["Border",e.cssSnapshot.border],["Font",`${e.cssSnapshot.fontSize} / ${e.cssSnapshot.lineHeight}`],["Family",e.cssSnapshot.fontFamily],["Color",e.cssSnapshot.color],["Background",e.cssSnapshot.backgroundColor]];return r.jsx("dl",{className:"review-lens-metrics",children:n.map(([t,o])=>r.jsxs("div",{children:[r.jsx("dt",{children:t}),r.jsx("dd",{children:o})]},t))})}function ae(e){try{return document.querySelector(e)}catch{return null}}w.ReviewLensOverlay=ne,w.ReviewLensProvider=Q,w.buildElementTarget=L,w.createGoogleSheetsAdapter=T,w.normalizeReviewUrl=B,w.useReviewLens=M,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(w,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],n):(w=typeof globalThis<"u"?globalThis:w||self,n(w.ReviewLensReact={},w.jsxRuntime,w.React))})(this,(function(w,n,p){"use strict";const G=["https://www.googleapis.com/auth/spreadsheets","https://www.googleapis.com/auth/userinfo.email"].join(" "),J="https://www.googleapis.com/oauth2/v3/userinfo";function $(e){const o=e.feedbackSheetName??"Feedback",t=e.usersSheetName??"Users";let r,s;async function i(){return r??(r=X(e.googleClientId)),r}async function m(d,c){const a=await i(),l=await fetch(`https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${d}`,{...c,headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json",...c==null?void 0:c.headers}});if(!l.ok)throw new Error(`Google Sheets request failed with ${l.status}`);return l.json()}async function k(d){return(await m(`/values/${encodeURIComponent(d)}`)).values??[]}return{async getCurrentUser(){if(!s){const d=await i(),c=await fetch(J,{headers:{Authorization:`Bearer ${d}`}});if(!c.ok)throw new Error(`Google userinfo request failed with ${c.status}`);s=(await c.json()).email}if(!s)throw new Error("Google account did not return an email address");return{email:s}},async getPermissions(d){const[{email:c},a]=await Promise.all([this.getCurrentUser(),k(t)]),l=j(a),C=c.toLowerCase(),h=l.find(b=>{var g;return((g=b.email)==null?void 0:g.toLowerCase())===C&&b.active!=="false"&&(!b.projectKey||b.projectKey===d)});return Q((h==null?void 0:h.role)??"designer")},async listFeedback(d){return j(await k(o)).map(M).filter(a=>a!==null).filter(a=>a.projectKey===d.projectKey&&a.contentId===d.contentId&&a.normalizedPath===d.normalizedPath).sort((a,l)=>l.createdAt.localeCompare(a.createdAt))},async createFeedback(d){const c=new Date().toISOString(),a={...d,id:crypto.randomUUID(),status:"open",createdAt:c,updatedAt:c};return await m(`/values/${encodeURIComponent(o)}:append?valueInputOption=RAW`,{method:"POST",body:JSON.stringify({values:[H(a)]})}),a},async resolveFeedback(d,c){const a=await k(o),l=a[0]??R,C=l.indexOf("id"),h=l.indexOf("status"),b=l.indexOf("updatedAt"),g=l.indexOf("resolvedAt"),v=l.indexOf("resolvedBy"),f=a.findIndex((T,B)=>B>0&&T[C]===d);if(f<1)throw new Error(`Feedback ${d} was not found`);const y=[...a[f]],E=new Date().toISOString();y[h]="resolved",y[b]=E,y[g]=E,y[v]=c,await m(`/values/${encodeURIComponent(o)}!A${f+1}:Q${f+1}?valueInputOption=RAW`,{method:"PUT",body:JSON.stringify({values:[y]})});const F=M(W(l,y));if(!F)throw new Error(`Feedback ${d} could not be parsed after resolving`);return F}}}const R=["id","projectKey","contentId","normalizedPath","originalUrl","selector","selectorStrategy","elementFingerprintJson","cssSnapshotJson","comment","status","authorEmail","createdAt","updatedAt","resolvedAt","resolvedBy"];function H(e){return[e.id,e.projectKey,e.contentId,e.normalizedPath,e.originalUrl,e.selector,e.selectorStrategy,JSON.stringify(e.elementFingerprint),JSON.stringify(e.cssSnapshot),e.comment,e.status,e.authorEmail,e.createdAt,e.updatedAt,e.resolvedAt??"",e.resolvedBy??""]}function j(e){const[o,...t]=e;return o?t.map(r=>W(o,r)):[]}function W(e,o){return Object.fromEntries(e.map((t,r)=>[t,o[r]??""]))}function M(e){return e.id?{id:e.id,projectKey:e.projectKey,contentId:e.contentId,normalizedPath:e.normalizedPath,originalUrl:e.originalUrl,selector:e.selector,selectorStrategy:e.selectorStrategy==="stable-attribute"?"stable-attribute":"css-path",elementFingerprint:U(e.elementFingerprintJson,{tagName:"",width:0,height:0}),cssSnapshot:D(e.cssSnapshotJson),comment:e.comment,status:e.status==="resolved"?"resolved":"open",authorEmail:e.authorEmail,createdAt:e.createdAt,updatedAt:e.updatedAt,resolvedAt:e.resolvedAt||void 0,resolvedBy:e.resolvedBy||void 0}:null}function U(e,o){try{return e?JSON.parse(e):o}catch{return o}}function D(e){const o=U(e,{});return{margin:o.margin??"",marginTop:o.marginTop??"",marginRight:o.marginRight??"",marginBottom:o.marginBottom??"",marginLeft:o.marginLeft??"",padding:o.padding??"",paddingTop:o.paddingTop??"",paddingRight:o.paddingRight??"",paddingBottom:o.paddingBottom??"",paddingLeft:o.paddingLeft??"",border:o.border??"",borderTopWidth:o.borderTopWidth??"",borderRightWidth:o.borderRightWidth??"",borderBottomWidth:o.borderBottomWidth??"",borderLeftWidth:o.borderLeftWidth??"",fontFamily:o.fontFamily??"",fontSize:o.fontSize??"",lineHeight:o.lineHeight??"",color:o.color??"",backgroundColor:o.backgroundColor??"",width:o.width??0,height:o.height??0}}function Q(e){return e==="admin"?["create","read","resolve"]:e==="developer"?["read","resolve"]:["create","read"]}async function X(e){return await Y(),new Promise((o,t)=>{var s;const r=(s=window.google)==null?void 0:s.accounts.oauth2.initTokenClient({client_id:e,scope:G,callback:i=>{if(i.error||!i.access_token){t(new Error(i.error??"Google OAuth did not return an access token"));return}o(i.access_token)}});r==null||r.requestAccessToken({prompt:""})})}function Y(){var e;return(e=window.google)!=null&&e.accounts.oauth2?Promise.resolve():new Promise((o,t)=>{const r=document.querySelector('script[src="https://accounts.google.com/gsi/client"]');if(r){r.addEventListener("load",()=>o(),{once:!0}),r.addEventListener("error",()=>t(new Error("Google Identity failed to load")),{once:!0});return}const s=document.createElement("script");s.src="https://accounts.google.com/gsi/client",s.async=!0,s.defer=!0,s.onload=()=>o(),s.onerror=()=>t(new Error("Google Identity failed to load")),document.head.append(s)})}function O(e){return new URL(e,window.location.href).pathname.replace(/\/+$/,"")||"/"}const z=p.createContext(null);function V({config:e,children:o}){const t=p.useMemo(()=>e.adapter?e.adapter:$({googleClientId:_(e.googleClientId,"googleClientId"),spreadsheetId:_(e.spreadsheetId,"spreadsheetId"),feedbackSheetName:e.sheetName??"Feedback"}),[e.adapter,e.googleClientId,e.sheetName,e.spreadsheetId]),r=e.currentUrl??window.location.href,s=(e.normalizeUrl??O)(r),[i,m]=p.useState(),[k,d]=p.useState([]),[c,a]=p.useState([]),l=p.useCallback(async()=>{const g=await t.listFeedback({projectKey:e.projectKey,contentId:e.contentId,normalizedPath:s});a(g)},[t,e.contentId,e.projectKey,s]);p.useEffect(()=>{let g=!0;async function v(){const[f,y]=await Promise.all([t.getCurrentUser(),t.getPermissions(e.projectKey)]);g&&(m(f),d(y),await l())}return v(),()=>{g=!1}},[t,e.projectKey,l]);const C=p.useCallback(async g=>{const v=await t.createFeedback(g);return a(f=>[v,...f]),v},[t]),h=p.useCallback(async g=>{const v=await t.resolveFeedback(g,(i==null?void 0:i.email)??"");return a(f=>f.map(y=>y.id===g?v:y)),v},[t,i==null?void 0:i.email]),b=p.useMemo(()=>({config:e,adapter:t,currentUser:i,permissions:k,feedback:c,normalizedPath:s,refreshFeedback:l,createFeedback:C,resolveFeedback:h}),[t,e,C,i,c,s,k,l,h]);return n.jsx(z.Provider,{value:b,children:o})}function K(){const e=p.useContext(z);if(!e)throw new Error("useReviewLens must be used inside ReviewLensProvider");return e}function _(e,o){if(!e)throw new Error(`review-lens-react requires config.${o} when no adapter is provided`);return e}const Z=["data-review-id","data-testid","data-test-id","aria-label","name"];function A(e){const o=e.getBoundingClientRect(),t=ee(e);return{selector:t.selector,selectorStrategy:t.strategy,fingerprint:oe(e,o),cssSnapshot:ne(e,o),rect:o}}function ee(e){for(const o of Z){const t=e.getAttribute(o);if(t)return{selector:`[${o}="${q(t)}"]`,strategy:"stable-attribute"}}return e.id?{selector:`#${q(e.id)}`,strategy:"stable-attribute"}:{selector:te(e),strategy:"css-path"}}function te(e){const o=[];let t=e;for(;t&&t.nodeType===Node.ELEMENT_NODE&&t!==document.body;){const r=t.parentElement,s=t.tagName.toLowerCase();if(!r){o.unshift(s);break}const i=t.tagName,m=Array.from(r.children).filter(d=>d.tagName===i),k=m.indexOf(t)+1;o.unshift(m.length>1?`${s}:nth-of-type(${k})`:s),t=r}return o.join(" > ")}function oe(e,o){var t;return{tagName:e.tagName.toLowerCase(),id:e.id||void 0,className:e.getAttribute("class")||void 0,textSnippet:((t=e.textContent)==null?void 0:t.trim().slice(0,80))||void 0,ariaLabel:e.getAttribute("aria-label")||void 0,width:Math.round(o.width),height:Math.round(o.height)}}function ne(e,o){const t=window.getComputedStyle(e);return{margin:P(t.marginTop,t.marginRight,t.marginBottom,t.marginLeft),marginTop:t.marginTop,marginRight:t.marginRight,marginBottom:t.marginBottom,marginLeft:t.marginLeft,padding:P(t.paddingTop,t.paddingRight,t.paddingBottom,t.paddingLeft),paddingTop:t.paddingTop,paddingRight:t.paddingRight,paddingBottom:t.paddingBottom,paddingLeft:t.paddingLeft,border:P(t.borderTopWidth,t.borderRightWidth,t.borderBottomWidth,t.borderLeftWidth),borderTopWidth:t.borderTopWidth,borderRightWidth:t.borderRightWidth,borderBottomWidth:t.borderBottomWidth,borderLeftWidth:t.borderLeftWidth,fontFamily:t.fontFamily,fontSize:t.fontSize,lineHeight:t.lineHeight,color:t.color,backgroundColor:t.backgroundColor,width:Math.round(o.width),height:Math.round(o.height)}}function P(e,o,t,r){return e===o&&o===t&&t===r?e:`${e} ${o} ${t} ${r}`}function q(e){return typeof CSS<"u"&&typeof CSS.escape=="function"?CSS.escape(e):e.replace(/["\\]/g,"\\$&")}function re({open:e,onOpenChange:o,placement:t="top-right",showResolved:r=!1}){const{config:s,currentUser:i,feedback:m,normalizedPath:k,permissions:d,createFeedback:c,resolveFeedback:a}=K(),[l,C]=p.useState(),[h,b]=p.useState(),[g,v]=p.useState(""),[f,y]=p.useState(),E=d.includes("create"),F=d.includes("resolve"),T=p.useMemo(()=>m.filter(u=>r||u.status!=="resolved"),[m,r]);p.useEffect(()=>{e||(C(void 0),b(void 0),v(""))},[e]);const B=p.useCallback(u=>{const N=u.target instanceof Element?u.target:null;if(N)return N.closest("[data-review-lens-ui]")?null:N;const L=document.elementFromPoint(u.clientX,u.clientY);return!L||L.closest("[data-review-lens-ui]")?null:L},[]);if(p.useEffect(()=>{if(!e||h)return;function u(L){const I=B(L);C(I?A(I):void 0)}function N(L){const I=B(L);I&&(L.preventDefault(),L.stopPropagation(),b(A(I)))}return window.addEventListener("mousemove",u,!0),window.addEventListener("click",N,!0),()=>{window.removeEventListener("mousemove",u,!0),window.removeEventListener("click",N,!0)}},[B,h,e]),!e)return null;const x=h??l;async function le(){!h||!g.trim()||!i||!E||(await c({projectKey:s.projectKey,contentId:s.contentId,normalizedPath:k,originalUrl:s.currentUrl??window.location.href,selector:h.selector,selectorStrategy:h.selectorStrategy,elementFingerprint:h.fingerprint,cssSnapshot:h.cssSnapshot,comment:g.trim(),authorEmail:i.email}),v(""),b(void 0))}return n.jsxs("div",{className:"review-lens-root","data-review-lens-ui":!0,children:[x?n.jsx(se,{target:x,locked:!!h}):null,n.jsx(ie,{feedback:T,selectedFeedback:f,onSelect:y}),n.jsxs("aside",{className:`review-lens-panel review-lens-panel--${t}`,"data-review-lens-ui":!0,children:[n.jsxs("header",{className:"review-lens-panel__header",children:[n.jsxs("div",{children:[n.jsx("p",{className:"review-lens-kicker",children:"Review Lens"}),n.jsx("h2",{children:h?"Element locked":"Inspecting"})]}),n.jsx("button",{type:"button",onClick:()=>o==null?void 0:o(!1),children:"Close"})]}),x?n.jsx(ae,{target:x}):n.jsx("p",{children:"Move over the app to inspect."}),h?n.jsxs("form",{className:"review-lens-feedback-form",onSubmit:u=>{u.preventDefault(),le()},children:[n.jsx("label",{htmlFor:"review-lens-comment",children:"Feedback"}),n.jsx("textarea",{id:"review-lens-comment",value:g,disabled:!E,onChange:u=>v(u.target.value),placeholder:E?"Describe the UX issue...":"You do not have permission to comment."}),n.jsxs("div",{className:"review-lens-actions",children:[n.jsx("button",{type:"button",onClick:()=>b(void 0),children:"Unlock"}),n.jsx("button",{type:"submit",disabled:!g.trim()||!E,children:"Save feedback"})]})]}):null,n.jsxs("section",{className:"review-lens-comments",children:[n.jsx("h3",{children:"Page feedback"}),T.length===0?n.jsx("p",{children:"No feedback for this view."}):null,T.map(u=>n.jsxs("article",{className:(f==null?void 0:f.id)===u.id?"review-lens-comment review-lens-comment--selected":"review-lens-comment",children:[n.jsx("p",{children:u.comment}),n.jsx("span",{children:u.authorEmail}),u.status==="open"&&F?n.jsx("button",{type:"button",onClick:()=>void a(u.id),children:"Resolve"}):null]},u.id))]})]})]})}function se({target:e,locked:o}){const t=ce(e);return n.jsxs("div",{className:o?"review-lens-highlight review-lens-highlight--locked":"review-lens-highlight",style:{top:t.margin.top,left:t.margin.left,width:t.margin.width,height:t.margin.height},children:[n.jsx("div",{className:"review-lens-highlight__border",style:{top:t.border.top-t.margin.top,left:t.border.left-t.margin.left,width:t.border.width,height:t.border.height}}),n.jsx("div",{className:"review-lens-highlight__padding",style:{top:t.padding.top-t.margin.top,left:t.padding.left-t.margin.left,width:t.padding.width,height:t.padding.height}}),n.jsx("div",{className:"review-lens-highlight__content",style:{top:t.content.top-t.margin.top,left:t.content.left-t.margin.left,width:t.content.width,height:t.content.height}}),n.jsxs("div",{className:"review-lens-highlight__label",children:[Math.round(e.rect.width)," x ",Math.round(e.rect.height)]})]})}function ie({feedback:e,selectedFeedback:o,onSelect:t}){return n.jsx(n.Fragment,{children:e.map(r=>{const s=de(r.selector),i=s==null?void 0:s.getBoundingClientRect();return i?n.jsx("button",{type:"button",className:(o==null?void 0:o.id)===r.id?"review-lens-marker review-lens-marker--selected":"review-lens-marker",style:{top:i.top,left:i.left+i.width},onClick:()=>t(r),"aria-label":`Open feedback from ${r.authorEmail}`},r.id):null})})}function ae({target:e}){const o=[["Selector",e.selector],["Size",`${e.cssSnapshot.width} x ${e.cssSnapshot.height}`],["Margin",e.cssSnapshot.margin],["Padding",e.cssSnapshot.padding],["Border",e.cssSnapshot.border],["Font",`${e.cssSnapshot.fontSize} / ${e.cssSnapshot.lineHeight}`],["Family",e.cssSnapshot.fontFamily],["Color",e.cssSnapshot.color],["Background",e.cssSnapshot.backgroundColor]];return n.jsx("dl",{className:"review-lens-metrics",children:o.map(([t,r])=>n.jsxs("div",{children:[n.jsx("dt",{children:t}),n.jsx("dd",{children:r})]},t))})}function de(e){try{return document.querySelector(e)}catch{return null}}function ce(e){const o={top:S(e.cssSnapshot.marginTop),right:S(e.cssSnapshot.marginRight),bottom:S(e.cssSnapshot.marginBottom),left:S(e.cssSnapshot.marginLeft)},t={top:S(e.cssSnapshot.borderTopWidth),right:S(e.cssSnapshot.borderRightWidth),bottom:S(e.cssSnapshot.borderBottomWidth),left:S(e.cssSnapshot.borderLeftWidth)},r={top:S(e.cssSnapshot.paddingTop),right:S(e.cssSnapshot.paddingRight),bottom:S(e.cssSnapshot.paddingBottom),left:S(e.cssSnapshot.paddingLeft)},s={top:e.rect.top,left:e.rect.left,width:Math.max(e.rect.width,0),height:Math.max(e.rect.height,0)},i={top:s.top-o.top,left:s.left-o.left,width:s.width+o.left+o.right,height:s.height+o.top+o.bottom},m={top:s.top+t.top,left:s.left+t.left,width:Math.max(s.width-t.left-t.right,0),height:Math.max(s.height-t.top-t.bottom,0)},k={top:m.top+r.top,left:m.left+r.left,width:Math.max(m.width-r.left-r.right,0),height:Math.max(m.height-r.top-r.bottom,0)};return{margin:i,border:s,padding:m,content:k}}function S(e){const o=Number.parseFloat(e||"0");return Number.isFinite(o)?o:0}w.ReviewLensOverlay=re,w.ReviewLensProvider=V,w.buildElementTarget=A,w.createGoogleSheetsAdapter=$,w.normalizeReviewUrl=O,w.useReviewLens=K,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"})}));
package/dist/styles.css CHANGED
@@ -1 +1 @@
1
- .review-lens-root{color:#171717;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;position:fixed;z-index:2147483647}.review-lens-highlight{border:2px solid #2563eb;box-shadow:0 0 0 9999px #0f172a14,0 0 0 4px #2563eb2e;pointer-events:none;position:absolute}.review-lens-highlight--locked{border-color:#f97316;box-shadow:0 0 0 9999px #0f172a1f,0 0 0 4px #f9731638}.review-lens-panel{background:#fafafa;border:1px solid #d4d4d4;border-radius:8px;box-shadow:0 24px 70px #0f172a38;box-sizing:border-box;max-height:calc(100vh - 32px);overflow:auto;padding:16px;position:fixed;width:min(380px,calc(100vw - 32px))}.review-lens-panel--top-left{left:16px;top:16px}.review-lens-panel--top-right{right:16px;top:16px}.review-lens-panel--bottom-left{bottom:16px;left:16px}.review-lens-panel--bottom-right{bottom:16px;right:16px}.review-lens-panel__header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.review-lens-panel h2,.review-lens-panel h3,.review-lens-panel p{margin:0}.review-lens-panel h2{font-size:18px;line-height:1.25}.review-lens-panel h3{font-size:14px;margin-top:18px}.review-lens-kicker{color:#525252;font-size:11px;font-weight:700;letter-spacing:0;text-transform:uppercase}.review-lens-panel button{background:#171717;border:1px solid #171717;border-radius:6px;color:#fff;cursor:pointer;font:inherit;font-size:13px;min-height:32px;padding:6px 10px}.review-lens-panel button:disabled{cursor:not-allowed;opacity:.45}.review-lens-metrics{border:1px solid #e5e5e5;border-radius:8px;display:grid;gap:0;margin:16px 0 0;overflow:hidden}.review-lens-metrics div{display:grid;grid-template-columns:96px minmax(0,1fr)}.review-lens-metrics dt,.review-lens-metrics dd{border-bottom:1px solid #e5e5e5;font-size:12px;margin:0;min-width:0;padding:8px}.review-lens-metrics dt{background:#f5f5f5;color:#525252;font-weight:700}.review-lens-metrics dd{overflow-wrap:anywhere}.review-lens-feedback-form{display:grid;gap:8px;margin-top:16px}.review-lens-feedback-form label{font-size:13px;font-weight:700}.review-lens-feedback-form textarea{border:1px solid #d4d4d4;border-radius:8px;box-sizing:border-box;font:inherit;min-height:96px;padding:10px;resize:vertical;width:100%}.review-lens-actions{display:flex;gap:8px;justify-content:flex-end}.review-lens-comments{display:grid;gap:8px}.review-lens-comment{border:1px solid #e5e5e5;border-radius:8px;display:grid;gap:6px;padding:10px}.review-lens-comment--selected{border-color:#2563eb}.review-lens-comment span{color:#525252;font-size:12px}.review-lens-marker{background:#f97316;border:2px solid #ffffff;border-radius:999px;box-shadow:0 8px 20px #0f172a3d;cursor:pointer;height:18px;position:absolute;transform:translate(-50%,-50%);width:18px}.review-lens-marker--selected{background:#2563eb}
1
+ .review-lens-root{color:#171717;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;top:0;right:0;bottom:0;left:0;pointer-events:none;position:fixed;z-index:2147483647}.review-lens-highlight{background:#f9731633;box-shadow:0 0 0 9999px #0f172a14;box-sizing:border-box;pointer-events:none;position:fixed}.review-lens-highlight--locked{box-shadow:0 0 0 9999px #0f172a1f}.review-lens-highlight__border,.review-lens-highlight__padding,.review-lens-highlight__content{box-sizing:border-box;position:absolute}.review-lens-highlight__border{border:2px solid #facc15;background:#facc1538}.review-lens-highlight__padding{border:2px solid #22c55e;background:#22c55e38}.review-lens-highlight__content{border:2px solid #2563eb;background:#2563eb29}.review-lens-highlight--locked .review-lens-highlight__content{border-color:#f97316}.review-lens-highlight__label{background:#171717;border-radius:4px;color:#fff;font-size:11px;font-weight:700;left:0;line-height:1;padding:4px 6px;position:absolute;top:-24px;white-space:nowrap}.review-lens-panel{background:#fafafa;border:1px solid #d4d4d4;border-radius:8px;box-shadow:0 24px 70px #0f172a38;box-sizing:border-box;max-height:calc(100vh - 32px);overflow:auto;padding:16px;pointer-events:auto;position:fixed;width:min(380px,calc(100vw - 32px))}.review-lens-panel--top-left{left:16px;top:16px}.review-lens-panel--top-right{right:16px;top:16px}.review-lens-panel--bottom-left{bottom:16px;left:16px}.review-lens-panel--bottom-right{bottom:16px;right:16px}.review-lens-panel__header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.review-lens-panel h2,.review-lens-panel h3,.review-lens-panel p{margin:0}.review-lens-panel h2{font-size:18px;line-height:1.25}.review-lens-panel h3{font-size:14px;margin-top:18px}.review-lens-kicker{color:#525252;font-size:11px;font-weight:700;letter-spacing:0;text-transform:uppercase}.review-lens-panel button{background:#171717;border:1px solid #171717;border-radius:6px;color:#fff;cursor:pointer;font:inherit;font-size:13px;min-height:32px;padding:6px 10px}.review-lens-panel button:disabled{cursor:not-allowed;opacity:.45}.review-lens-metrics{border:1px solid #e5e5e5;border-radius:8px;display:grid;gap:0;margin:16px 0 0;overflow:hidden}.review-lens-metrics div{display:grid;grid-template-columns:96px minmax(0,1fr)}.review-lens-metrics dt,.review-lens-metrics dd{border-bottom:1px solid #e5e5e5;font-size:12px;margin:0;min-width:0;padding:8px}.review-lens-metrics dt{background:#f5f5f5;color:#525252;font-weight:700}.review-lens-metrics dd{overflow-wrap:anywhere}.review-lens-feedback-form{display:grid;gap:8px;margin-top:16px}.review-lens-feedback-form label{font-size:13px;font-weight:700}.review-lens-feedback-form textarea{border:1px solid #d4d4d4;border-radius:8px;box-sizing:border-box;font:inherit;min-height:96px;padding:10px;resize:vertical;width:100%}.review-lens-actions{display:flex;gap:8px;justify-content:flex-end}.review-lens-comments{display:grid;gap:8px}.review-lens-comment{border:1px solid #e5e5e5;border-radius:8px;display:grid;gap:6px;padding:10px}.review-lens-comment--selected{border-color:#2563eb}.review-lens-comment span{color:#525252;font-size:12px}.review-lens-marker{background:#f97316;border:2px solid #ffffff;border-radius:999px;box-shadow:0 8px 20px #0f172a3d;cursor:pointer;height:18px;pointer-events:auto;position:fixed;transform:translate(-50%,-50%);width:18px}.review-lens-marker--selected{background:#2563eb}
package/dist/types.d.ts CHANGED
@@ -4,8 +4,20 @@ export type ReviewLensRole = "designer" | "developer" | "admin";
4
4
  export type ReviewLensPermission = "create" | "read" | "resolve";
5
5
  export type CssSnapshot = {
6
6
  margin: string;
7
+ marginTop: string;
8
+ marginRight: string;
9
+ marginBottom: string;
10
+ marginLeft: string;
7
11
  padding: string;
12
+ paddingTop: string;
13
+ paddingRight: string;
14
+ paddingBottom: string;
15
+ paddingLeft: string;
8
16
  border: string;
17
+ borderTopWidth: string;
18
+ borderRightWidth: string;
19
+ borderBottomWidth: string;
20
+ borderLeftWidth: string;
9
21
  fontFamily: string;
10
22
  fontSize: string;
11
23
  lineHeight: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "review-lens-react",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "React overlay for UX review feedback backed by Google Sheets.",
5
5
  "type": "module",
6
6
  "main": "./dist/review-lens-react.umd.cjs",