review-lens-react 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,97 +1,97 @@
1
- import { jsx as a, jsxs as S, Fragment as G } from "react/jsx-runtime";
2
- import { createContext as q, useMemo as $, useState as N, useCallback as F, useEffect as P, useContext as H } from "react";
3
- const D = [
1
+ import { jsx as i, jsxs as 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 = [
4
4
  "https://www.googleapis.com/auth/spreadsheets",
5
5
  "https://www.googleapis.com/auth/userinfo.email"
6
- ].join(" "), Q = "https://www.googleapis.com/oauth2/v3/userinfo";
7
- function X(e) {
8
- const o = e.feedbackSheetName ?? "Feedback", t = e.usersSheetName ?? "Users";
9
- let n, r;
10
- async function i() {
11
- return n ?? (n = te(e.googleClientId)), n;
6
+ ].join(" "), 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;
12
12
  }
13
- async function m(d, c) {
14
- const s = await i(), l = await fetch(
15
- `https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${d}`,
13
+ async function h(a, d) {
14
+ const l = await c(), u = await fetch(
15
+ `https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${a}`,
16
16
  {
17
- ...c,
17
+ ...d,
18
18
  headers: {
19
- Authorization: `Bearer ${s}`,
19
+ Authorization: `Bearer ${l}`,
20
20
  "Content-Type": "application/json",
21
- ...c == null ? void 0 : c.headers
21
+ ...d == null ? void 0 : d.headers
22
22
  }
23
23
  }
24
24
  );
25
- if (!l.ok)
26
- throw new Error(`Google Sheets request failed with ${l.status}`);
27
- return l.json();
25
+ if (!u.ok)
26
+ throw new Error(`Google Sheets request failed with ${u.status}`);
27
+ return u.json();
28
28
  }
29
- async function v(d) {
30
- return (await m(
31
- `/values/${encodeURIComponent(d)}`
29
+ async function w(a) {
30
+ return (await h(
31
+ `/values/${encodeURIComponent(a)}`
32
32
  )).values ?? [];
33
33
  }
34
34
  return {
35
35
  async getCurrentUser() {
36
- if (!r) {
37
- const d = await i(), c = await fetch(Q, {
38
- headers: { Authorization: `Bearer ${d}` }
36
+ if (!o) {
37
+ const a = await c(), d = await fetch(oe, {
38
+ headers: { Authorization: `Bearer ${a}` }
39
39
  });
40
- if (!c.ok)
41
- throw new Error(`Google userinfo request failed with ${c.status}`);
42
- r = (await c.json()).email;
40
+ if (!d.ok)
41
+ throw new Error(`Google userinfo request failed with ${d.status}`);
42
+ o = (await d.json()).email;
43
43
  }
44
- if (!r)
44
+ if (!o)
45
45
  throw new Error("Google account did not return an email address");
46
- return { email: r };
46
+ return { email: o };
47
47
  },
48
- async getPermissions(d) {
49
- const [{ email: c }, s] = await Promise.all([this.getCurrentUser(), v(t)]), l = W(s), k = c.toLowerCase(), h = l.find(
50
- (f) => {
51
- var p;
52
- return ((p = f.email) == null ? void 0 : p.toLowerCase()) === k && f.active !== "false" && (!f.projectKey || f.projectKey === d);
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);
53
53
  }
54
54
  );
55
- return ee((h == null ? void 0 : h.role) ?? "designer");
55
+ return de((p == null ? void 0 : p.role) ?? "designer");
56
56
  },
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));
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));
61
61
  },
62
- async createFeedback(d) {
63
- const c = (/* @__PURE__ */ new Date()).toISOString(), s = {
64
- ...d,
62
+ async createFeedback(a) {
63
+ const d = (/* @__PURE__ */ new Date()).toISOString(), l = {
64
+ ...a,
65
65
  id: crypto.randomUUID(),
66
66
  status: "open",
67
- createdAt: c,
68
- updatedAt: c
67
+ createdAt: d,
68
+ updatedAt: d
69
69
  };
70
- return await m(`/values/${encodeURIComponent(o)}:append?valueInputOption=RAW`, {
70
+ return await h(`/values/${encodeURIComponent(n)}:append?valueInputOption=RAW`, {
71
71
  method: "POST",
72
- body: JSON.stringify({ values: [V(s)] })
73
- }), s;
72
+ body: JSON.stringify({ values: [se(l)] })
73
+ }), l;
74
74
  },
75
- async resolveFeedback(d, c) {
76
- const s = await v(o), l = s[0] ?? Y, k = l.indexOf("id"), h = l.indexOf("status"), f = l.indexOf("updatedAt"), p = l.indexOf("resolvedAt"), w = l.indexOf("resolvedBy"), g = s.findIndex((R, x) => x > 0 && R[k] === d);
77
- if (g < 1)
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`,
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`,
82
82
  {
83
83
  method: "PUT",
84
- body: JSON.stringify({ values: [b] })
84
+ body: JSON.stringify({ values: [S] })
85
85
  }
86
86
  );
87
- const I = j(O(l, b));
88
- if (!I)
89
- throw new Error(`Feedback ${d} could not be parsed after resolving`);
90
- return I;
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;
91
91
  }
92
92
  };
93
93
  }
94
- const Y = [
94
+ const ie = [
95
95
  "id",
96
96
  "projectKey",
97
97
  "contentId",
@@ -109,7 +109,7 @@ const Y = [
109
109
  "resolvedAt",
110
110
  "resolvedBy"
111
111
  ];
112
- function V(e) {
112
+ function se(e) {
113
113
  return [
114
114
  e.id,
115
115
  e.projectKey,
@@ -129,14 +129,14 @@ function V(e) {
129
129
  e.resolvedBy ?? ""
130
130
  ];
131
131
  }
132
- function W(e) {
133
- const [o, ...t] = e;
134
- return o ? t.map((n) => O(o, n)) : [];
132
+ function z(e) {
133
+ const [n, ...t] = e;
134
+ return n ? t.map((r) => q(n, r)) : [];
135
135
  }
136
- function O(e, o) {
137
- return Object.fromEntries(e.map((t, n) => [t, o[n] ?? ""]));
136
+ function q(e, n) {
137
+ return Object.fromEntries(e.map((t, r) => [t, n[r] ?? ""]));
138
138
  }
139
- function j(e) {
139
+ function K(e) {
140
140
  return e.id ? {
141
141
  id: e.id,
142
142
  projectKey: e.projectKey,
@@ -145,12 +145,12 @@ function j(e) {
145
145
  originalUrl: e.originalUrl,
146
146
  selector: e.selector,
147
147
  selectorStrategy: e.selectorStrategy === "stable-attribute" ? "stable-attribute" : "css-path",
148
- elementFingerprint: K(e.elementFingerprintJson, {
148
+ elementFingerprint: G(e.elementFingerprintJson, {
149
149
  tagName: "",
150
150
  width: 0,
151
151
  height: 0
152
152
  }),
153
- cssSnapshot: Z(e.cssSnapshotJson),
153
+ cssSnapshot: ae(e.cssSnapshotJson),
154
154
  comment: e.comment,
155
155
  status: e.status === "resolved" ? "resolved" : "open",
156
156
  authorEmail: e.authorEmail,
@@ -160,202 +160,202 @@ function j(e) {
160
160
  resolvedBy: e.resolvedBy || void 0
161
161
  } : null;
162
162
  }
163
- function K(e, o) {
163
+ function G(e, n) {
164
164
  try {
165
- return e ? JSON.parse(e) : o;
165
+ return e ? JSON.parse(e) : n;
166
166
  } catch {
167
- return o;
167
+ return n;
168
168
  }
169
169
  }
170
- function Z(e) {
171
- const o = K(e, {});
170
+ function ae(e) {
171
+ const n = G(e, {});
172
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
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
195
195
  };
196
196
  }
197
- function ee(e) {
197
+ function de(e) {
198
198
  return e === "admin" ? ["create", "read", "resolve"] : e === "developer" ? ["read", "resolve"] : ["create", "read"];
199
199
  }
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({
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({
204
204
  client_id: e,
205
- scope: D,
206
- callback: (i) => {
207
- if (i.error || !i.access_token) {
208
- t(new Error(i.error ?? "Google OAuth did not return an access token"));
205
+ scope: ne,
206
+ callback: (c) => {
207
+ if (c.error || !c.access_token) {
208
+ t(new Error(c.error ?? "Google OAuth did not return an access token"));
209
209
  return;
210
210
  }
211
- o(i.access_token);
211
+ n(c.access_token);
212
212
  }
213
213
  });
214
- n == null || n.requestAccessToken({ prompt: "" });
214
+ r == null || r.requestAccessToken({ prompt: "" });
215
215
  });
216
216
  }
217
- function oe() {
217
+ function le() {
218
218
  var e;
219
- return (e = window.google) != null && e.accounts.oauth2 ? Promise.resolve() : new Promise((o, t) => {
220
- const n = document.querySelector(
219
+ return (e = window.google) != null && e.accounts.oauth2 ? Promise.resolve() : new Promise((n, t) => {
220
+ const r = document.querySelector(
221
221
  'script[src="https://accounts.google.com/gsi/client"]'
222
222
  );
223
- if (n) {
224
- n.addEventListener("load", () => o(), { once: !0 }), n.addEventListener("error", () => t(new Error("Google Identity failed to load")), {
223
+ if (r) {
224
+ r.addEventListener("load", () => n(), { once: !0 }), r.addEventListener("error", () => t(new Error("Google Identity failed to load")), {
225
225
  once: !0
226
226
  });
227
227
  return;
228
228
  }
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);
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);
231
231
  });
232
232
  }
233
- function ne(e) {
233
+ function he(e) {
234
234
  return new URL(e, window.location.href).pathname.replace(/\/+$/, "") || "/";
235
235
  }
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"),
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"),
241
241
  feedbackSheetName: e.sheetName ?? "Feedback"
242
- }), [e.adapter, e.googleClientId, e.sheetName, e.spreadsheetId]), n = e.currentUrl ?? window.location.href, r = (e.normalizeUrl ?? ne)(n), [i, m] = N(), [v, d] = N([]), [c, s] = N([]), l = F(async () => {
243
- const p = await t.listFeedback({
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({
244
244
  projectKey: e.projectKey,
245
245
  contentId: e.contentId,
246
- normalizedPath: r
246
+ normalizedPath: o
247
247
  });
248
- s(p);
249
- }, [t, e.contentId, e.projectKey, r]);
250
- P(() => {
251
- let p = !0;
252
- async function w() {
253
- const [g, b] = await Promise.all([
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
254
  t.getCurrentUser(),
255
255
  t.getPermissions(e.projectKey)
256
256
  ]);
257
- p && (m(g), d(b), await l());
257
+ m && (h(v), a(S), await u());
258
258
  }
259
- return w(), () => {
260
- p = !1;
259
+ return k(), () => {
260
+ m = !1;
261
261
  };
262
- }, [t, e.projectKey, l]);
263
- const k = F(
264
- async (p) => {
265
- const w = await t.createFeedback(p);
266
- return s((g) => [w, ...g]), w;
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;
267
267
  },
268
268
  [t]
269
- ), h = F(
270
- async (p) => {
271
- const w = await t.resolveFeedback(p, (i == null ? void 0 : i.email) ?? "");
272
- return s(
273
- (g) => g.map((b) => b.id === p ? w : b)
274
- ), w;
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;
275
275
  },
276
- [t, i == null ? void 0 : i.email]
277
- ), f = $(
276
+ [t, c == null ? void 0 : c.email]
277
+ ), b = M(
278
278
  () => ({
279
279
  config: e,
280
280
  adapter: t,
281
- currentUser: i,
282
- permissions: v,
283
- feedback: c,
284
- normalizedPath: r,
285
- refreshFeedback: l,
286
- createFeedback: k,
287
- resolveFeedback: h
281
+ currentUser: c,
282
+ permissions: w,
283
+ feedback: d,
284
+ normalizedPath: o,
285
+ refreshFeedback: u,
286
+ createFeedback: y,
287
+ resolveFeedback: p
288
288
  }),
289
289
  [
290
290
  t,
291
291
  e,
292
- k,
293
- i,
292
+ y,
294
293
  c,
295
- r,
296
- v,
297
- l,
298
- h
294
+ d,
295
+ o,
296
+ w,
297
+ u,
298
+ p
299
299
  ]
300
300
  );
301
- return /* @__PURE__ */ a(_.Provider, { value: f, children: o });
301
+ return /* @__PURE__ */ i(J.Provider, { value: b, children: n });
302
302
  }
303
- function re() {
304
- const e = H(_);
303
+ function ue() {
304
+ const e = Z(J);
305
305
  if (!e)
306
306
  throw new Error("useReviewLens must be used inside ReviewLensProvider");
307
307
  return e;
308
308
  }
309
- function U(e, o) {
309
+ function D(e, n) {
310
310
  if (!e)
311
- throw new Error(`review-lens-react requires config.${o} when no adapter is provided`);
311
+ throw new Error(`review-lens-react requires config.${n} when no adapter is provided`);
312
312
  return e;
313
313
  }
314
- const ie = [
314
+ const pe = [
315
315
  "data-review-id",
316
316
  "data-testid",
317
317
  "data-test-id",
318
318
  "aria-label",
319
319
  "name"
320
320
  ];
321
- function M(e) {
322
- const o = e.getBoundingClientRect(), t = se(e);
321
+ function _(e) {
322
+ const n = e.getBoundingClientRect(), t = me(e);
323
323
  return {
324
324
  selector: t.selector,
325
325
  selectorStrategy: t.strategy,
326
- fingerprint: de(e, o),
327
- cssSnapshot: ce(e, o),
328
- rect: o
326
+ fingerprint: fe(e, n),
327
+ cssSnapshot: we(e, n),
328
+ rect: n
329
329
  };
330
330
  }
331
- function se(e) {
332
- for (const o of ie) {
333
- const t = e.getAttribute(o);
331
+ function me(e) {
332
+ for (const n of pe) {
333
+ const t = e.getAttribute(n);
334
334
  if (t)
335
335
  return {
336
- selector: `[${o}="${z(t)}"]`,
336
+ selector: `[${n}="${O(t)}"]`,
337
337
  strategy: "stable-attribute"
338
338
  };
339
339
  }
340
- return e.id ? { selector: `#${z(e.id)}`, strategy: "stable-attribute" } : { selector: ae(e), strategy: "css-path" };
340
+ return e.id ? { selector: `#${O(e.id)}`, strategy: "stable-attribute" } : { selector: ge(e), strategy: "css-path" };
341
341
  }
342
- function ae(e) {
343
- const o = [];
342
+ function ge(e) {
343
+ const n = [];
344
344
  let t = e;
345
345
  for (; t && t.nodeType === Node.ELEMENT_NODE && t !== document.body; ) {
346
- const n = t.parentElement, r = t.tagName.toLowerCase();
347
- if (!n) {
348
- o.unshift(r);
346
+ const r = t.parentElement, o = t.tagName.toLowerCase();
347
+ if (!r) {
348
+ n.unshift(o);
349
349
  break;
350
350
  }
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;
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;
355
355
  }
356
- return o.join(" > ");
356
+ return n.join(" > ");
357
357
  }
358
- function de(e, o) {
358
+ function fe(e, n) {
359
359
  var t;
360
360
  return {
361
361
  tagName: e.tagName.toLowerCase(),
@@ -363,19 +363,19 @@ function de(e, o) {
363
363
  className: e.getAttribute("class") || void 0,
364
364
  textSnippet: ((t = e.textContent) == null ? void 0 : t.trim().slice(0, 80)) || void 0,
365
365
  ariaLabel: e.getAttribute("aria-label") || void 0,
366
- width: Math.round(o.width),
367
- height: Math.round(o.height)
366
+ width: Math.round(n.width),
367
+ height: Math.round(n.height)
368
368
  };
369
369
  }
370
- function ce(e, o) {
370
+ function we(e, n) {
371
371
  const t = window.getComputedStyle(e);
372
372
  return {
373
- margin: A(t.marginTop, t.marginRight, t.marginBottom, t.marginLeft),
373
+ margin: j(t.marginTop, t.marginRight, t.marginBottom, t.marginLeft),
374
374
  marginTop: t.marginTop,
375
375
  marginRight: t.marginRight,
376
376
  marginBottom: t.marginBottom,
377
377
  marginLeft: t.marginLeft,
378
- padding: A(
378
+ padding: j(
379
379
  t.paddingTop,
380
380
  t.paddingRight,
381
381
  t.paddingBottom,
@@ -385,7 +385,7 @@ function ce(e, o) {
385
385
  paddingRight: t.paddingRight,
386
386
  paddingBottom: t.paddingBottom,
387
387
  paddingLeft: t.paddingLeft,
388
- border: A(
388
+ border: j(
389
389
  t.borderTopWidth,
390
390
  t.borderRightWidth,
391
391
  t.borderBottomWidth,
@@ -400,145 +400,223 @@ function ce(e, o) {
400
400
  lineHeight: t.lineHeight,
401
401
  color: t.color,
402
402
  backgroundColor: t.backgroundColor,
403
- width: Math.round(o.width),
404
- height: Math.round(o.height)
403
+ width: Math.round(n.width),
404
+ height: Math.round(n.height)
405
405
  };
406
406
  }
407
- function A(e, o, t, n) {
408
- return e === o && o === t && t === n ? e : `${e} ${o} ${t} ${n}`;
407
+ function j(e, n, t, r) {
408
+ return e === n && n === t && t === r ? e : `${e} ${n} ${t} ${r}`;
409
409
  }
410
- function z(e) {
410
+ function O(e) {
411
411
  return typeof CSS < "u" && typeof CSS.escape == "function" ? CSS.escape(e) : e.replace(/["\\]/g, "\\$&");
412
412
  }
413
- function be({
413
+ function Le({
414
414
  open: e,
415
- onOpenChange: o,
415
+ onOpenChange: n,
416
416
  placement: t = "top-right",
417
- showResolved: n = !1
417
+ showResolved: r = !1
418
418
  }) {
419
419
  const {
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]
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
430
  );
431
- P(() => {
432
- e || (k(void 0), f(void 0), w(""));
433
- }, [e]);
434
- const x = F((u) => {
435
- const E = u.target instanceof Element ? u.target : null;
436
- if (E)
437
- return E.closest("[data-review-lens-ui]") ? null : E;
438
- const C = document.elementFromPoint(u.clientX, u.clientY);
439
- return !C || C.closest("[data-review-lens-ui]") ? null : C;
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(() => {
436
+ if (!e)
437
+ return;
438
+ function s(g) {
439
+ g.key === "Escape" && (g.preventDefault(), n == null || n(!1));
440
+ }
441
+ return window.addEventListener("keydown", s), () => {
442
+ window.removeEventListener("keydown", s);
443
+ };
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;
440
451
  }, []);
441
- if (P(() => {
442
- if (!e || h)
452
+ if (T(() => {
453
+ if (!e || !N)
443
454
  return;
444
- function u(C) {
445
- const B = x(C);
446
- k(B ? M(B) : void 0);
455
+ function s(L) {
456
+ const A = $(L);
457
+ y(A ? _(A) : void 0);
447
458
  }
448
- function E(C) {
449
- const B = x(C);
450
- B && (C.preventDefault(), C.stopPropagation(), f(M(B)));
459
+ function g(L) {
460
+ const A = $(L);
461
+ A && (L.preventDefault(), L.stopPropagation(), b(_(A)), C("review"));
451
462
  }
452
- return window.addEventListener("mousemove", u, !0), window.addEventListener("click", E, !0), () => {
453
- window.removeEventListener("mousemove", u, !0), window.removeEventListener("click", E, !0);
463
+ return window.addEventListener("mousemove", s, !0), window.addEventListener("click", g, !0), () => {
464
+ window.removeEventListener("mousemove", s, !0), window.removeEventListener("click", g, !0);
454
465
  };
455
- }, [x, h, e]), !e)
466
+ }, [N, $, p, e]), !e)
456
467
  return null;
457
- const T = h ?? l;
458
- async function J() {
459
- !h || !p.trim() || !i || !L || (await c({
460
- projectKey: r.projectKey,
461
- contentId: r.contentId,
462
- normalizedPath: v,
463
- originalUrl: r.currentUrl ?? window.location.href,
464
- selector: h.selector,
465
- selectorStrategy: h.selectorStrategy,
466
- elementFingerprint: h.fingerprint,
467
- cssSnapshot: h.cssSnapshot,
468
- comment: p.trim(),
469
- authorEmail: i.email
470
- }), w(""), f(void 0));
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
+ }));
471
475
  }
472
- return /* @__PURE__ */ S("div", { className: "review-lens-root", "data-review-lens-ui": !0, children: [
473
- T ? /* @__PURE__ */ a(le, { target: T, locked: !!h }) : null,
474
- /* @__PURE__ */ a(
475
- he,
476
+ 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"));
489
+ }
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,
476
494
  {
477
- feedback: R,
478
- selectedFeedback: g,
479
- onSelect: b
495
+ feedback: B,
496
+ selectedFeedback: v,
497
+ onSelect: W
480
498
  }
481
- ),
482
- /* @__PURE__ */ S("aside", { className: `review-lens-panel review-lens-panel--${t}`, "data-review-lens-ui": !0, children: [
483
- /* @__PURE__ */ S("header", { className: "review-lens-panel__header", children: [
484
- /* @__PURE__ */ S("div", { children: [
485
- /* @__PURE__ */ a("p", { className: "review-lens-kicker", children: "Review Lens" }),
486
- /* @__PURE__ */ a("h2", { children: h ? "Element locked" : "Inspecting" })
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: [
503
+ /* @__PURE__ */ i("p", { className: "review-lens-kicker", children: "Review Lens" }),
504
+ /* @__PURE__ */ i("h2", { children: F === "feedback" ? "Feedback" : p ? "Element locked" : "Inspecting" })
487
505
  ] }),
488
- /* @__PURE__ */ a("button", { type: "button", onClick: () => o == null ? void 0 : o(!1), children: "Close" })
506
+ /* @__PURE__ */ i("button", { type: "button", onClick: () => n == null ? void 0 : n(!1), children: "Close" })
489
507
  ] }),
490
- T ? /* @__PURE__ */ a(ue, { target: T }) : /* @__PURE__ */ a("p", { children: "Move over the app to inspect." }),
491
- h ? /* @__PURE__ */ S(
492
- "form",
493
- {
494
- className: "review-lens-feedback-form",
495
- onSubmit: (u) => {
496
- u.preventDefault(), J();
497
- },
498
- children: [
499
- /* @__PURE__ */ a("label", { htmlFor: "review-lens-comment", children: "Feedback" }),
500
- /* @__PURE__ */ a(
501
- "textarea",
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: [
510
+ /* @__PURE__ */ i(
511
+ "button",
512
+ {
513
+ type: "button",
514
+ role: "tab",
515
+ "aria-selected": F === "review",
516
+ onClick: () => C("review"),
517
+ children: "Review"
518
+ }
519
+ ),
520
+ /* @__PURE__ */ f(
521
+ "button",
522
+ {
523
+ type: "button",
524
+ role: "tab",
525
+ "aria-selected": F === "feedback",
526
+ onClick: () => C("feedback"),
527
+ children: [
528
+ "Feedback ",
529
+ /* @__PURE__ */ i("span", { children: B.length })
530
+ ]
531
+ }
532
+ )
533
+ ] }),
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
539
+ ] }),
540
+ X ? /* @__PURE__ */ f(
541
+ "form",
542
+ {
543
+ className: "review-lens-feedback-form",
544
+ onSubmit: (s) => {
545
+ s.preventDefault(), U();
546
+ },
547
+ children: [
548
+ /* @__PURE__ */ i("label", { htmlFor: "review-lens-comment", children: "New feedback" }),
549
+ /* @__PURE__ */ i(
550
+ "textarea",
551
+ {
552
+ 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());
558
+ },
559
+ placeholder: x ? "Describe the UX issue..." : "You do not have permission to comment."
560
+ }
561
+ ),
562
+ x ? /* @__PURE__ */ f("p", { className: "review-lens-feedback-form__hint", children: [
563
+ "Press ",
564
+ /* @__PURE__ */ i("kbd", { children: "Command" }),
565
+ " + ",
566
+ /* @__PURE__ */ i("kbd", { children: "Enter" }),
567
+ " to submit."
568
+ ] }) : null,
569
+ /* @__PURE__ */ i("div", { className: "review-lens-actions", children: /* @__PURE__ */ i("button", { type: "submit", disabled: !m.trim() || !x, children: "Save feedback" }) })
570
+ ]
571
+ }
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 })
577
+ ] }),
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",
502
582
  {
503
- id: "review-lens-comment",
504
- value: p,
505
- disabled: !L,
506
- onChange: (u) => w(u.target.value),
507
- placeholder: L ? "Describe the UX issue..." : "You do not have permission to comment."
508
- }
509
- ),
510
- /* @__PURE__ */ S("div", { className: "review-lens-actions", children: [
511
- /* @__PURE__ */ a("button", { type: "button", onClick: () => f(void 0), children: "Unlock" }),
512
- /* @__PURE__ */ a("button", { type: "submit", disabled: !p.trim() || !L, children: "Save feedback" })
513
- ] })
514
- ]
515
- }
516
- ) : null,
517
- /* @__PURE__ */ S("section", { className: "review-lens-comments", children: [
518
- /* @__PURE__ */ a("h3", { children: "Page feedback" }),
519
- R.length === 0 ? /* @__PURE__ */ a("p", { children: "No feedback for this view." }) : null,
520
- R.map((u) => /* @__PURE__ */ S(
521
- "article",
522
- {
523
- className: (g == null ? void 0 : g.id) === u.id ? "review-lens-comment review-lens-comment--selected" : "review-lens-comment",
524
- children: [
525
- /* @__PURE__ */ a("p", { children: u.comment }),
526
- /* @__PURE__ */ a("span", { children: u.authorEmail }),
527
- u.status === "open" && I ? /* @__PURE__ */ a("button", { type: "button", onClick: () => void s(u.id), children: "Resolve" }) : null
528
- ]
529
- },
530
- u.id
531
- ))
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
+ ]
605
+ },
606
+ s.id
607
+ ))
608
+ ] })
609
+ ] })
532
610
  ] })
533
611
  ] })
534
612
  ] });
535
613
  }
536
- function le({ target: e, locked: o }) {
537
- const t = me(e);
538
- return /* @__PURE__ */ S(
614
+ function ve({ target: e, locked: n }) {
615
+ const t = Se(e);
616
+ return /* @__PURE__ */ f(
539
617
  "div",
540
618
  {
541
- className: o ? "review-lens-highlight review-lens-highlight--locked" : "review-lens-highlight",
619
+ className: n ? "review-lens-highlight review-lens-highlight--locked" : "review-lens-highlight",
542
620
  style: {
543
621
  top: t.margin.top,
544
622
  left: t.margin.left,
@@ -546,7 +624,7 @@ function le({ target: e, locked: o }) {
546
624
  height: t.margin.height
547
625
  },
548
626
  children: [
549
- /* @__PURE__ */ a(
627
+ /* @__PURE__ */ i(
550
628
  "div",
551
629
  {
552
630
  className: "review-lens-highlight__border",
@@ -558,7 +636,7 @@ function le({ target: e, locked: o }) {
558
636
  }
559
637
  }
560
638
  ),
561
- /* @__PURE__ */ a(
639
+ /* @__PURE__ */ i(
562
640
  "div",
563
641
  {
564
642
  className: "review-lens-highlight__padding",
@@ -570,7 +648,7 @@ function le({ target: e, locked: o }) {
570
648
  }
571
649
  }
572
650
  ),
573
- /* @__PURE__ */ a(
651
+ /* @__PURE__ */ i(
574
652
  "div",
575
653
  {
576
654
  className: "review-lens-highlight__content",
@@ -582,7 +660,7 @@ function le({ target: e, locked: o }) {
582
660
  }
583
661
  }
584
662
  ),
585
- /* @__PURE__ */ S("div", { className: "review-lens-highlight__label", children: [
663
+ /* @__PURE__ */ f("div", { className: "review-lens-highlight__label", children: [
586
664
  Math.round(e.rect.width),
587
665
  " x ",
588
666
  Math.round(e.rect.height)
@@ -591,31 +669,52 @@ function le({ target: e, locked: o }) {
591
669
  }
592
670
  );
593
671
  }
594
- function he({
672
+ function be({
595
673
  feedback: e,
596
- selectedFeedback: o,
674
+ selectedFeedback: n,
597
675
  onSelect: t
598
676
  }) {
599
- return /* @__PURE__ */ a(G, { children: e.map((n) => {
600
- const r = pe(n.selector), i = r == null ? void 0 : r.getBoundingClientRect();
601
- return i ? /* @__PURE__ */ a(
602
- "button",
603
- {
604
- type: "button",
605
- className: (o == null ? void 0 : o.id) === n.id ? "review-lens-marker review-lens-marker--selected" : "review-lens-marker",
606
- style: {
607
- top: i.top,
608
- left: i.left + i.width
609
- },
610
- onClick: () => t(n),
611
- "aria-label": `Open feedback from ${n.authorEmail}`
612
- },
613
- n.id
614
- ) : null;
615
- }) });
677
+ return /* @__PURE__ */ i(Y, { children: e.map((r) => /* @__PURE__ */ i(
678
+ ye,
679
+ {
680
+ feedback: r,
681
+ selected: (n == null ? void 0 : n.id) === r.id,
682
+ onSelect: t
683
+ },
684
+ r.id
685
+ )) });
616
686
  }
617
- function ue({ target: e }) {
618
- const o = [
687
+ function ye({
688
+ feedback: e,
689
+ selected: n,
690
+ onSelect: t
691
+ }) {
692
+ const r = ee(null);
693
+ return te(() => {
694
+ let o = 0;
695
+ 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));
701
+ };
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);
704
+ };
705
+ }, [e.selector]), /* @__PURE__ */ i(
706
+ "button",
707
+ {
708
+ ref: r,
709
+ type: "button",
710
+ className: n ? "review-lens-marker review-lens-marker--selected" : "review-lens-marker",
711
+ onClick: () => t(e),
712
+ "aria-label": `Open feedback from ${e.authorEmail}`
713
+ }
714
+ );
715
+ }
716
+ function ke({ target: e }) {
717
+ const n = [
619
718
  ["Selector", e.selector],
620
719
  ["Size", `${e.cssSnapshot.width} x ${e.cssSnapshot.height}`],
621
720
  ["Margin", e.cssSnapshot.margin],
@@ -626,71 +725,71 @@ function ue({ target: e }) {
626
725
  ["Color", e.cssSnapshot.color],
627
726
  ["Background", e.cssSnapshot.backgroundColor]
628
727
  ];
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 })
728
+ return /* @__PURE__ */ i("dl", { className: "review-lens-metrics", children: n.map(([t, r]) => /* @__PURE__ */ f("div", { children: [
729
+ /* @__PURE__ */ i("dt", { children: t }),
730
+ /* @__PURE__ */ i("dd", { children: r })
632
731
  ] }, t)) });
633
732
  }
634
- function pe(e) {
733
+ function H(e) {
635
734
  try {
636
735
  return document.querySelector(e);
637
736
  } catch {
638
737
  return null;
639
738
  }
640
739
  }
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)
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)
647
746
  }, 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)
747
+ top: E(e.cssSnapshot.borderTopWidth),
748
+ right: E(e.cssSnapshot.borderRightWidth),
749
+ bottom: E(e.cssSnapshot.borderBottomWidth),
750
+ left: E(e.cssSnapshot.borderLeftWidth)
657
751
  }, 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 = {
658
757
  top: e.rect.top,
659
758
  left: e.rect.left,
660
759
  width: Math.max(e.rect.width, 0),
661
760
  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)
761
+ }, 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)
677
776
  };
678
777
  return {
679
- margin: i,
680
- border: r,
681
- padding: m,
682
- content: v
778
+ margin: c,
779
+ border: o,
780
+ padding: h,
781
+ content: w
683
782
  };
684
783
  }
685
- function y(e) {
686
- const o = Number.parseFloat(e || "0");
687
- return Number.isFinite(o) ? o : 0;
784
+ function E(e) {
785
+ const n = Number.parseFloat(e || "0");
786
+ return Number.isFinite(n) ? n : 0;
688
787
  }
689
788
  export {
690
- be as ReviewLensOverlay,
691
- we as ReviewLensProvider,
692
- M as buildElementTarget,
693
- X as createGoogleSheetsAdapter,
694
- ne as normalizeReviewUrl,
695
- re as useReviewLens
789
+ Le as ReviewLensOverlay,
790
+ Ce as ReviewLensProvider,
791
+ _ as buildElementTarget,
792
+ re as createGoogleSheetsAdapter,
793
+ he as normalizeReviewUrl,
794
+ ue as useReviewLens
696
795
  };
@@ -1 +1 @@
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"})}));
1
+ (function(y,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):(y=typeof globalThis<"u"?globalThis:y||self,n(y.ReviewLensReact={},y.jsxRuntime,y.React))})(this,(function(y,n,h){"use strict";const X=["https://www.googleapis.com/auth/spreadsheets","https://www.googleapis.com/auth/userinfo.email"].join(" "),Y="https://www.googleapis.com/oauth2/v3/userinfo";function W(e){const o=e.feedbackSheetName??"Feedback",t=e.usersSheetName??"Users";let s,r;async function c(){return s??(s=te(e.googleClientId)),s}async function u(a,d){const l=await c(),p=await fetch(`https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${a}`,{...d,headers:{Authorization:`Bearer ${l}`,"Content-Type":"application/json",...d==null?void 0:d.headers}});if(!p.ok)throw new Error(`Google Sheets request failed with ${p.status}`);return p.json()}async function w(a){return(await u(`/values/${encodeURIComponent(a)}`)).values??[]}return{async getCurrentUser(){if(!r){const a=await c(),d=await fetch(Y,{headers:{Authorization:`Bearer ${a}`}});if(!d.ok)throw new Error(`Google userinfo request failed with ${d.status}`);r=(await d.json()).email}if(!r)throw new Error("Google account did not return an email address");return{email:r}},async getPermissions(a){const[{email:d},l]=await Promise.all([this.getCurrentUser(),w(t)]),p=_(l),k=d.toLowerCase(),f=p.find(b=>{var g;return((g=b.email)==null?void 0:g.toLowerCase())===k&&b.active!=="false"&&(!b.projectKey||b.projectKey===a)});return ee((f==null?void 0:f.role)??"designer")},async listFeedback(a){return _(await w(o)).map(z).filter(l=>l!==null).filter(l=>l.projectKey===a.projectKey&&l.contentId===a.contentId&&l.normalizedPath===a.normalizedPath).sort((l,p)=>p.createdAt.localeCompare(l.createdAt))},async createFeedback(a){const d=new Date().toISOString(),l={...a,id:crypto.randomUUID(),status:"open",createdAt:d,updatedAt:d};return await u(`/values/${encodeURIComponent(o)}:append?valueInputOption=RAW`,{method:"POST",body:JSON.stringify({values:[Z(l)]})}),l},async resolveFeedback(a,d){const l=await w(o),p=l[0]??V,k=p.indexOf("id"),f=p.indexOf("status"),b=p.indexOf("updatedAt"),g=p.indexOf("resolvedAt"),S=p.indexOf("resolvedBy"),v=l.findIndex((C,B)=>B>0&&C[k]===a);if(v<1)throw new Error(`Feedback ${a} was not found`);const E=[...l[v]],T=new Date().toISOString();E[f]="resolved",E[b]=T,E[g]=T,E[S]=d,await u(`/values/${encodeURIComponent(o)}!A${v+1}:Q${v+1}?valueInputOption=RAW`,{method:"PUT",body:JSON.stringify({values:[E]})});const N=z(U(p,E));if(!N)throw new Error(`Feedback ${a} could not be parsed after resolving`);return N}}}const V=["id","projectKey","contentId","normalizedPath","originalUrl","selector","selectorStrategy","elementFingerprintJson","cssSnapshotJson","comment","status","authorEmail","createdAt","updatedAt","resolvedAt","resolvedBy"];function Z(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 _(e){const[o,...t]=e;return o?t.map(s=>U(o,s)):[]}function U(e,o){return Object.fromEntries(e.map((t,s)=>[t,o[s]??""]))}function z(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:K(e.elementFingerprintJson,{tagName:"",width:0,height:0}),cssSnapshot:R(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 K(e,o){try{return e?JSON.parse(e):o}catch{return o}}function R(e){const o=K(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 ee(e){return e==="admin"?["create","read","resolve"]:e==="developer"?["read","resolve"]:["create","read"]}async function te(e){return await oe(),new Promise((o,t)=>{var r;const s=(r=window.google)==null?void 0:r.accounts.oauth2.initTokenClient({client_id:e,scope:X,callback:c=>{if(c.error||!c.access_token){t(new Error(c.error??"Google OAuth did not return an access token"));return}o(c.access_token)}});s==null||s.requestAccessToken({prompt:""})})}function oe(){var e;return(e=window.google)!=null&&e.accounts.oauth2?Promise.resolve():new Promise((o,t)=>{const s=document.querySelector('script[src="https://accounts.google.com/gsi/client"]');if(s){s.addEventListener("load",()=>o(),{once:!0}),s.addEventListener("error",()=>t(new Error("Google Identity failed to load")),{once:!0});return}const r=document.createElement("script");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)})}function O(e){return new URL(e,window.location.href).pathname.replace(/\/+$/,"")||"/"}const D=h.createContext(null);function ne({config:e,children:o}){const t=h.useMemo(()=>e.adapter?e.adapter:W({googleClientId:G(e.googleClientId,"googleClientId"),spreadsheetId:G(e.spreadsheetId,"spreadsheetId"),feedbackSheetName:e.sheetName??"Feedback"}),[e.adapter,e.googleClientId,e.sheetName,e.spreadsheetId]),s=e.currentUrl??window.location.href,r=(e.normalizeUrl??O)(s),[c,u]=h.useState(),[w,a]=h.useState([]),[d,l]=h.useState([]),p=h.useCallback(async()=>{const g=await t.listFeedback({projectKey:e.projectKey,contentId:e.contentId,normalizedPath:r});l(g)},[t,e.contentId,e.projectKey,r]);h.useEffect(()=>{let g=!0;async function S(){const[v,E]=await Promise.all([t.getCurrentUser(),t.getPermissions(e.projectKey)]);g&&(u(v),a(E),await p())}return S(),()=>{g=!1}},[t,e.projectKey,p]);const k=h.useCallback(async g=>{const S=await t.createFeedback(g);return l(v=>[S,...v]),S},[t]),f=h.useCallback(async g=>{const S=await t.resolveFeedback(g,(c==null?void 0:c.email)??"");return l(v=>v.map(E=>E.id===g?S:E)),S},[t,c==null?void 0:c.email]),b=h.useMemo(()=>({config:e,adapter:t,currentUser:c,permissions:w,feedback:d,normalizedPath:r,refreshFeedback:p,createFeedback:k,resolveFeedback:f}),[t,e,k,c,d,r,w,p,f]);return n.jsx(D.Provider,{value:b,children:o})}function q(){const e=h.useContext(D);if(!e)throw new Error("useReviewLens must be used inside ReviewLensProvider");return e}function G(e,o){if(!e)throw new Error(`review-lens-react requires config.${o} when no adapter is provided`);return e}const re=["data-review-id","data-testid","data-test-id","aria-label","name"];function P(e){const o=e.getBoundingClientRect(),t=se(e);return{selector:t.selector,selectorStrategy:t.strategy,fingerprint:ae(e,o),cssSnapshot:de(e,o),rect:o}}function se(e){for(const o of re){const t=e.getAttribute(o);if(t)return{selector:`[${o}="${J(t)}"]`,strategy:"stable-attribute"}}return e.id?{selector:`#${J(e.id)}`,strategy:"stable-attribute"}:{selector:ie(e),strategy:"css-path"}}function ie(e){const o=[];let t=e;for(;t&&t.nodeType===Node.ELEMENT_NODE&&t!==document.body;){const s=t.parentElement,r=t.tagName.toLowerCase();if(!s){o.unshift(r);break}const c=t.tagName,u=Array.from(s.children).filter(a=>a.tagName===c),w=u.indexOf(t)+1;o.unshift(u.length>1?`${r}:nth-of-type(${w})`:r),t=s}return o.join(" > ")}function ae(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 de(e,o){const t=window.getComputedStyle(e);return{margin:$(t.marginTop,t.marginRight,t.marginBottom,t.marginLeft),marginTop:t.marginTop,marginRight:t.marginRight,marginBottom:t.marginBottom,marginLeft:t.marginLeft,padding:$(t.paddingTop,t.paddingRight,t.paddingBottom,t.paddingLeft),paddingTop:t.paddingTop,paddingRight:t.paddingRight,paddingBottom:t.paddingBottom,paddingLeft:t.paddingLeft,border:$(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 $(e,o,t,s){return e===o&&o===t&&t===s?e:`${e} ${o} ${t} ${s}`}function J(e){return typeof CSS<"u"&&typeof CSS.escape=="function"?CSS.escape(e):e.replace(/["\\]/g,"\\$&")}function ce({open:e,onOpenChange:o,placement:t="top-right",showResolved:s=!1}){const{config:r,currentUser:c,feedback:u,normalizedPath:w,permissions:a,createFeedback:d,resolveFeedback:l}=q(),[p,k]=h.useState(),[f,b]=h.useState(),[g,S]=h.useState(""),[v,E]=h.useState(),[T,N]=h.useState("review"),C=!!c,B=a.includes("create"),ge=a.includes("resolve"),I=h.useMemo(()=>u.filter(i=>s||i.status!=="resolved"),[u,s]);h.useEffect(()=>{e||(k(void 0),b(void 0),S(""),N("review"))},[e]),h.useEffect(()=>{C||(k(void 0),b(void 0))},[C]),h.useEffect(()=>{if(!e)return;function i(m){m.key==="Escape"&&(m.preventDefault(),o==null||o(!1))}return window.addEventListener("keydown",i),()=>{window.removeEventListener("keydown",i)}},[o,e]);const j=h.useCallback(i=>{const m=i.target instanceof Element?i.target:null;if(m)return m.closest("[data-review-lens-ui]")?null:m;const F=document.elementFromPoint(i.clientX,i.clientY);return!F||F.closest("[data-review-lens-ui]")?null:F},[]);if(h.useEffect(()=>{if(!e||!C)return;function i(F){const x=j(F);k(x?P(x):void 0)}function m(F){const x=j(F);x&&(F.preventDefault(),F.stopPropagation(),b(P(x)),N("review"))}return window.addEventListener("mousemove",i,!0),window.addEventListener("click",m,!0),()=>{window.removeEventListener("mousemove",i,!0),window.removeEventListener("click",m,!0)}},[C,j,f,e]),!e)return null;const A=p??f,me=!!f;function M(i){E(i),b(void 0),N("feedback");const m=H(i.selector);m&&(m.scrollIntoView({behavior:"smooth",block:"center",inline:"center"}),window.requestAnimationFrame(()=>{k(P(m))}))}async function Q(){!f||!g.trim()||!c||!B||(await d({projectKey:r.projectKey,contentId:r.contentId,normalizedPath:w,originalUrl:r.currentUrl??window.location.href,selector:f.selector,selectorStrategy:f.selectorStrategy,elementFingerprint:f.fingerprint,cssSnapshot:f.cssSnapshot,comment:g.trim(),authorEmail:c.email}),S(""),b(void 0),k(void 0),N("feedback"))}return n.jsxs("div",{className:"review-lens-root","data-review-lens-ui":!0,children:[C&&A?n.jsx(le,{target:A,locked:!!f}):null,C?n.jsx(he,{feedback:I,selectedFeedback:v,onSelect:M}):null,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:T==="feedback"?"Feedback":f?"Element locked":"Inspecting"})]}),n.jsx("button",{type:"button",onClick:()=>o==null?void 0:o(!1),children:"Close"})]}),n.jsxs("div",{className:"review-lens-panel__body",children:[n.jsxs("div",{className:"review-lens-mode-switch",role:"tablist","aria-label":"Review Lens mode",children:[n.jsx("button",{type:"button",role:"tab","aria-selected":T==="review",onClick:()=>N("review"),children:"Review"}),n.jsxs("button",{type:"button",role:"tab","aria-selected":T==="feedback",onClick:()=>N("feedback"),children:["Feedback ",n.jsx("span",{children:I.length})]})]}),T==="review"?n.jsxs("div",{className:"review-lens-review-pane",role:"tabpanel",children:[n.jsxs("div",{className:"review-lens-inspection",children:[C?null:n.jsx("p",{children:"Authenticate with Google to inspect this page."}),C&&A?n.jsx(pe,{target:A}):null,C&&!A?n.jsx("p",{children:"Move over the app to inspect."}):null]}),me?n.jsxs("form",{className:"review-lens-feedback-form",onSubmit:i=>{i.preventDefault(),Q()},children:[n.jsx("label",{htmlFor:"review-lens-comment",children:"New feedback"}),n.jsx("textarea",{id:"review-lens-comment",value:g,disabled:!B,onChange:i=>S(i.target.value),onKeyDown:i=>{i.key==="Enter"&&i.metaKey&&(i.preventDefault(),Q())},placeholder:B?"Describe the UX issue...":"You do not have permission to comment."}),B?n.jsxs("p",{className:"review-lens-feedback-form__hint",children:["Press ",n.jsx("kbd",{children:"Command"})," + ",n.jsx("kbd",{children:"Enter"})," to submit."]}):null,n.jsx("div",{className:"review-lens-actions",children:n.jsx("button",{type:"submit",disabled:!g.trim()||!B,children:"Save feedback"})})]}):null]}):n.jsxs("div",{className:"review-lens-comments",children:[n.jsxs("div",{className:"review-lens-comments__header",children:[n.jsx("h3",{children:"Page feedback"}),n.jsx("span",{children:I.length})]}),n.jsxs("div",{className:"review-lens-comments__list",children:[I.length===0?n.jsx("p",{children:"No feedback for this view."}):null,I.map(i=>n.jsxs("article",{tabIndex:0,className:(v==null?void 0:v.id)===i.id?"review-lens-comment review-lens-comment--selected":"review-lens-comment",onClick:()=>M(i),onKeyDown:m=>{(m.key==="Enter"||m.key===" ")&&(m.preventDefault(),M(i))},children:[n.jsxs("div",{className:"review-lens-comment__content",children:[n.jsx("p",{children:i.comment}),n.jsx("span",{children:i.authorEmail})]}),i.status==="open"&&ge?n.jsx("div",{className:"review-lens-comment__actions",children:n.jsx("button",{type:"button",onClick:m=>{m.stopPropagation(),l(i.id)},children:"Resolve"})}):null]},i.id))]})]})]})]})]})}function le({target:e,locked:o}){const t=fe(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 he({feedback:e,selectedFeedback:o,onSelect:t}){return n.jsx(n.Fragment,{children:e.map(s=>n.jsx(ue,{feedback:s,selected:(o==null?void 0:o.id)===s.id,onSelect:t},s.id))})}function ue({feedback:e,selected:o,onSelect:t}){const s=h.useRef(null);return h.useLayoutEffect(()=>{let r=0;const c=()=>{r=0;const w=s.current,a=H(e.selector),d=a==null?void 0:a.getBoundingClientRect();!w||!d||(w.style.top=`${d.top}px`,w.style.left=`${d.right}px`,w.hidden=d.bottom<0||d.top>window.innerHeight)},u=()=>{r||(r=window.requestAnimationFrame(c))};return c(),window.addEventListener("scroll",u,!0),window.addEventListener("resize",u),()=>{r&&window.cancelAnimationFrame(r),window.removeEventListener("scroll",u,!0),window.removeEventListener("resize",u)}},[e.selector]),n.jsx("button",{ref:s,type:"button",className:o?"review-lens-marker review-lens-marker--selected":"review-lens-marker",onClick:()=>t(e),"aria-label":`Open feedback from ${e.authorEmail}`})}function pe({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,s])=>n.jsxs("div",{children:[n.jsx("dt",{children:t}),n.jsx("dd",{children:s})]},t))})}function H(e){try{return document.querySelector(e)}catch{return null}}function fe(e){const o={top:L(e.cssSnapshot.marginTop),right:L(e.cssSnapshot.marginRight),bottom:L(e.cssSnapshot.marginBottom),left:L(e.cssSnapshot.marginLeft)},t={top:L(e.cssSnapshot.borderTopWidth),right:L(e.cssSnapshot.borderRightWidth),bottom:L(e.cssSnapshot.borderBottomWidth),left:L(e.cssSnapshot.borderLeftWidth)},s={top:L(e.cssSnapshot.paddingTop),right:L(e.cssSnapshot.paddingRight),bottom:L(e.cssSnapshot.paddingBottom),left:L(e.cssSnapshot.paddingLeft)},r={top:e.rect.top,left:e.rect.left,width:Math.max(e.rect.width,0),height:Math.max(e.rect.height,0)},c={top:r.top-o.top,left:r.left-o.left,width:r.width+o.left+o.right,height:r.height+o.top+o.bottom},u={top:r.top+t.top,left:r.left+t.left,width:Math.max(r.width-t.left-t.right,0),height:Math.max(r.height-t.top-t.bottom,0)},w={top:u.top+s.top,left:u.left+s.left,width:Math.max(u.width-s.left-s.right,0),height:Math.max(u.height-s.top-s.bottom,0)};return{margin:c,border:r,padding:u,content:w}}function L(e){const o=Number.parseFloat(e||"0");return Number.isFinite(o)?o:0}y.ReviewLensOverlay=ce,y.ReviewLensProvider=ne,y.buildElementTarget=P,y.createGoogleSheetsAdapter=W,y.normalizeReviewUrl=O,y.useReviewLens=q,Object.defineProperty(y,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;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}
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;display:flex;flex-direction:column;height:min(680px,calc(100vh - 32px));min-height:min(520px,calc(100vh - 32px));overflow:hidden;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{flex:0 0 auto;align-items:flex-start;display:flex;gap:16px;justify-content:space-between;margin-bottom:12px}.review-lens-panel__body{display:flex;flex:1 1 auto;flex-direction:column;gap:16px;min-height:0;overflow:hidden}.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;transition:transform .14s cubic-bezier(.23,1,.32,1)}.review-lens-panel button:active{transform:scale(.97)}.review-lens-panel button:disabled{cursor:not-allowed;opacity:.45}.review-lens-mode-switch{background:#eee;border:1px solid #d4d4d4;border-radius:7px;display:grid;flex:0 0 auto;gap:2px;grid-template-columns:repeat(2,minmax(0,1fr));padding:2px}.review-lens-mode-switch button{background:transparent;border:0;color:#525252;min-height:30px}.review-lens-mode-switch button[aria-selected=true]{background:#fff;box-shadow:0 1px 4px #0f172a1f;color:#171717}.review-lens-mode-switch span{color:#737373;font-size:11px}.review-lens-metrics{border:1px solid #e5e5e5;border-radius:8px;display:grid;gap:0;margin: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:11px;margin:0;min-width:0;padding:6px 8px}.review-lens-metrics dt{background:#f5f5f5;color:#525252;font-weight:700}.review-lens-metrics dd{overflow-wrap:anywhere}.review-lens-inspection{display:grid;flex:0 0 auto;gap:16px}.review-lens-review-pane{display:flex;flex:1 1 auto;flex-direction:column;gap:16px;min-height:0}.review-lens-feedback-form{display:grid;flex:1 1 auto;gap:8px;grid-template-rows:auto minmax(96px,1fr) auto auto;margin-top:0;min-height:0}.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;flex:1 1 auto;font:inherit;min-height:96px;padding:10px;resize:vertical;width:100%}.review-lens-feedback-form__hint{color:#737373;font-size:12px;line-height:1.4}.review-lens-feedback-form__hint kbd{background:#f5f5f5;border:1px solid #d4d4d4;border-radius:4px;color:#404040;font-family:inherit;font-size:11px;padding:1px 4px}.review-lens-actions{display:flex;gap:8px;justify-content:flex-end}.review-lens-comments{border-top:1px solid #e5e5e5;display:flex;flex:1 1 auto;flex-direction:column;gap:8px;min-height:0;padding-top:12px}.review-lens-comments__header{align-items:center;color:#171717;display:flex;gap:8px;justify-content:space-between}.review-lens-comments__header h3{font-size:13px;margin:0}.review-lens-comments__header span{align-items:center;background:#e5e5e5;border-radius:999px;color:#404040;display:inline-flex;font-size:11px;justify-content:center;min-width:22px;padding:2px 7px}.review-lens-comments__list{align-content:start;display:grid;gap:10px;min-height:0;overflow:auto;padding-right:4px}.review-lens-comment{background:linear-gradient(180deg,#fffffffa,#fafafafa);border:1px solid #e5e5e5;border-radius:8px;box-shadow:0 1px 2px #0f172a0a;cursor:pointer;display:grid;gap:10px;padding:12px;position:relative;text-align:left}.review-lens-comment:focus-visible{outline:2px solid #2563eb;outline-offset:2px}.review-lens-comment--selected{background:#eff6ff;border-color:#2563eb;box-shadow:inset 3px 0 #2563eb,0 8px 18px #2563eb1f}.review-lens-comment__content{display:grid;gap:6px}.review-lens-comment__content p{color:#171717;font-size:14px;line-height:1.35}.review-lens-comment__content span{color:#525252;font-size:11px;line-height:1}.review-lens-comment__actions{display:flex;justify-content:flex-end}.review-lens-comment__actions button{background:transparent;border-color:#d4d4d4;color:#404040;min-height:28px;padding:4px 10px}.review-lens-comment__actions button:hover{background:#171717;border-color:#171717;color:#fff}.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "review-lens-react",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
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",