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.
- package/dist/review-lens-react.js +473 -374
- package/dist/review-lens-react.umd.cjs +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -1,97 +1,97 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { createContext as
|
|
3
|
-
const
|
|
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(" "),
|
|
7
|
-
function
|
|
8
|
-
const
|
|
9
|
-
let
|
|
10
|
-
async function
|
|
11
|
-
return
|
|
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
|
|
14
|
-
const
|
|
15
|
-
`https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${
|
|
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
|
-
...
|
|
17
|
+
...d,
|
|
18
18
|
headers: {
|
|
19
|
-
Authorization: `Bearer ${
|
|
19
|
+
Authorization: `Bearer ${l}`,
|
|
20
20
|
"Content-Type": "application/json",
|
|
21
|
-
...
|
|
21
|
+
...d == null ? void 0 : d.headers
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
);
|
|
25
|
-
if (!
|
|
26
|
-
throw new Error(`Google Sheets request failed with ${
|
|
27
|
-
return
|
|
25
|
+
if (!u.ok)
|
|
26
|
+
throw new Error(`Google Sheets request failed with ${u.status}`);
|
|
27
|
+
return u.json();
|
|
28
28
|
}
|
|
29
|
-
async function
|
|
30
|
-
return (await
|
|
31
|
-
`/values/${encodeURIComponent(
|
|
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 (!
|
|
37
|
-
const
|
|
38
|
-
headers: { Authorization: `Bearer ${
|
|
36
|
+
if (!o) {
|
|
37
|
+
const a = await c(), d = await fetch(oe, {
|
|
38
|
+
headers: { Authorization: `Bearer ${a}` }
|
|
39
39
|
});
|
|
40
|
-
if (!
|
|
41
|
-
throw new Error(`Google userinfo request failed with ${
|
|
42
|
-
|
|
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 (!
|
|
44
|
+
if (!o)
|
|
45
45
|
throw new Error("Google account did not return an email address");
|
|
46
|
-
return { email:
|
|
46
|
+
return { email: o };
|
|
47
47
|
},
|
|
48
|
-
async getPermissions(
|
|
49
|
-
const [{ email:
|
|
50
|
-
(
|
|
51
|
-
var
|
|
52
|
-
return ((
|
|
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
|
|
55
|
+
return de((p == null ? void 0 : p.role) ?? "designer");
|
|
56
56
|
},
|
|
57
|
-
async listFeedback(
|
|
58
|
-
return
|
|
59
|
-
(
|
|
60
|
-
).sort((
|
|
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(
|
|
63
|
-
const
|
|
64
|
-
...
|
|
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:
|
|
68
|
-
updatedAt:
|
|
67
|
+
createdAt: d,
|
|
68
|
+
updatedAt: d
|
|
69
69
|
};
|
|
70
|
-
return await
|
|
70
|
+
return await h(`/values/${encodeURIComponent(n)}:append?valueInputOption=RAW`, {
|
|
71
71
|
method: "POST",
|
|
72
|
-
body: JSON.stringify({ values: [
|
|
73
|
-
}),
|
|
72
|
+
body: JSON.stringify({ values: [se(l)] })
|
|
73
|
+
}), l;
|
|
74
74
|
},
|
|
75
|
-
async resolveFeedback(
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
78
|
-
throw new Error(`Feedback ${
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
`/values/${encodeURIComponent(
|
|
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: [
|
|
84
|
+
body: JSON.stringify({ values: [S] })
|
|
85
85
|
}
|
|
86
86
|
);
|
|
87
|
-
const
|
|
88
|
-
if (!
|
|
89
|
-
throw new Error(`Feedback ${
|
|
90
|
-
return
|
|
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
|
|
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
|
|
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
|
|
133
|
-
const [
|
|
134
|
-
return
|
|
132
|
+
function z(e) {
|
|
133
|
+
const [n, ...t] = e;
|
|
134
|
+
return n ? t.map((r) => q(n, r)) : [];
|
|
135
135
|
}
|
|
136
|
-
function
|
|
137
|
-
return Object.fromEntries(e.map((t,
|
|
136
|
+
function q(e, n) {
|
|
137
|
+
return Object.fromEntries(e.map((t, r) => [t, n[r] ?? ""]));
|
|
138
138
|
}
|
|
139
|
-
function
|
|
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:
|
|
148
|
+
elementFingerprint: G(e.elementFingerprintJson, {
|
|
149
149
|
tagName: "",
|
|
150
150
|
width: 0,
|
|
151
151
|
height: 0
|
|
152
152
|
}),
|
|
153
|
-
cssSnapshot:
|
|
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
|
|
163
|
+
function G(e, n) {
|
|
164
164
|
try {
|
|
165
|
-
return e ? JSON.parse(e) :
|
|
165
|
+
return e ? JSON.parse(e) : n;
|
|
166
166
|
} catch {
|
|
167
|
-
return
|
|
167
|
+
return n;
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
-
function
|
|
171
|
-
const
|
|
170
|
+
function ae(e) {
|
|
171
|
+
const n = G(e, {});
|
|
172
172
|
return {
|
|
173
|
-
margin:
|
|
174
|
-
marginTop:
|
|
175
|
-
marginRight:
|
|
176
|
-
marginBottom:
|
|
177
|
-
marginLeft:
|
|
178
|
-
padding:
|
|
179
|
-
paddingTop:
|
|
180
|
-
paddingRight:
|
|
181
|
-
paddingBottom:
|
|
182
|
-
paddingLeft:
|
|
183
|
-
border:
|
|
184
|
-
borderTopWidth:
|
|
185
|
-
borderRightWidth:
|
|
186
|
-
borderBottomWidth:
|
|
187
|
-
borderLeftWidth:
|
|
188
|
-
fontFamily:
|
|
189
|
-
fontSize:
|
|
190
|
-
lineHeight:
|
|
191
|
-
color:
|
|
192
|
-
backgroundColor:
|
|
193
|
-
width:
|
|
194
|
-
height:
|
|
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
|
|
197
|
+
function de(e) {
|
|
198
198
|
return e === "admin" ? ["create", "read", "resolve"] : e === "developer" ? ["read", "resolve"] : ["create", "read"];
|
|
199
199
|
}
|
|
200
|
-
async function
|
|
201
|
-
return await
|
|
202
|
-
var
|
|
203
|
-
const
|
|
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:
|
|
206
|
-
callback: (
|
|
207
|
-
if (
|
|
208
|
-
t(new Error(
|
|
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
|
-
|
|
211
|
+
n(c.access_token);
|
|
212
212
|
}
|
|
213
213
|
});
|
|
214
|
-
|
|
214
|
+
r == null || r.requestAccessToken({ prompt: "" });
|
|
215
215
|
});
|
|
216
216
|
}
|
|
217
|
-
function
|
|
217
|
+
function le() {
|
|
218
218
|
var e;
|
|
219
|
-
return (e = window.google) != null && e.accounts.oauth2 ? Promise.resolve() : new Promise((
|
|
220
|
-
const
|
|
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 (
|
|
224
|
-
|
|
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
|
|
230
|
-
|
|
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
|
|
233
|
+
function he(e) {
|
|
234
234
|
return new URL(e, window.location.href).pathname.replace(/\/+$/, "") || "/";
|
|
235
235
|
}
|
|
236
|
-
const
|
|
237
|
-
function
|
|
238
|
-
const t =
|
|
239
|
-
googleClientId:
|
|
240
|
-
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]),
|
|
243
|
-
const
|
|
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:
|
|
246
|
+
normalizedPath: o
|
|
247
247
|
});
|
|
248
|
-
|
|
249
|
-
}, [t, e.contentId, e.projectKey,
|
|
250
|
-
|
|
251
|
-
let
|
|
252
|
-
async function
|
|
253
|
-
const [
|
|
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
|
-
|
|
257
|
+
m && (h(v), a(S), await u());
|
|
258
258
|
}
|
|
259
|
-
return
|
|
260
|
-
|
|
259
|
+
return k(), () => {
|
|
260
|
+
m = !1;
|
|
261
261
|
};
|
|
262
|
-
}, [t, e.projectKey,
|
|
263
|
-
const
|
|
264
|
-
async (
|
|
265
|
-
const
|
|
266
|
-
return
|
|
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
|
-
),
|
|
270
|
-
async (
|
|
271
|
-
const
|
|
272
|
-
return
|
|
273
|
-
(
|
|
274
|
-
),
|
|
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,
|
|
277
|
-
),
|
|
276
|
+
[t, c == null ? void 0 : c.email]
|
|
277
|
+
), b = M(
|
|
278
278
|
() => ({
|
|
279
279
|
config: e,
|
|
280
280
|
adapter: t,
|
|
281
|
-
currentUser:
|
|
282
|
-
permissions:
|
|
283
|
-
feedback:
|
|
284
|
-
normalizedPath:
|
|
285
|
-
refreshFeedback:
|
|
286
|
-
createFeedback:
|
|
287
|
-
resolveFeedback:
|
|
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
|
-
|
|
293
|
-
i,
|
|
292
|
+
y,
|
|
294
293
|
c,
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
294
|
+
d,
|
|
295
|
+
o,
|
|
296
|
+
w,
|
|
297
|
+
u,
|
|
298
|
+
p
|
|
299
299
|
]
|
|
300
300
|
);
|
|
301
|
-
return /* @__PURE__ */
|
|
301
|
+
return /* @__PURE__ */ i(J.Provider, { value: b, children: n });
|
|
302
302
|
}
|
|
303
|
-
function
|
|
304
|
-
const e =
|
|
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
|
|
309
|
+
function D(e, n) {
|
|
310
310
|
if (!e)
|
|
311
|
-
throw new Error(`review-lens-react requires config.${
|
|
311
|
+
throw new Error(`review-lens-react requires config.${n} when no adapter is provided`);
|
|
312
312
|
return e;
|
|
313
313
|
}
|
|
314
|
-
const
|
|
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
|
|
322
|
-
const
|
|
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:
|
|
327
|
-
cssSnapshot:
|
|
328
|
-
rect:
|
|
326
|
+
fingerprint: fe(e, n),
|
|
327
|
+
cssSnapshot: we(e, n),
|
|
328
|
+
rect: n
|
|
329
329
|
};
|
|
330
330
|
}
|
|
331
|
-
function
|
|
332
|
-
for (const
|
|
333
|
-
const t = e.getAttribute(
|
|
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: `[${
|
|
336
|
+
selector: `[${n}="${O(t)}"]`,
|
|
337
337
|
strategy: "stable-attribute"
|
|
338
338
|
};
|
|
339
339
|
}
|
|
340
|
-
return e.id ? { selector: `#${
|
|
340
|
+
return e.id ? { selector: `#${O(e.id)}`, strategy: "stable-attribute" } : { selector: ge(e), strategy: "css-path" };
|
|
341
341
|
}
|
|
342
|
-
function
|
|
343
|
-
const
|
|
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
|
|
347
|
-
if (!
|
|
348
|
-
|
|
346
|
+
const r = t.parentElement, o = t.tagName.toLowerCase();
|
|
347
|
+
if (!r) {
|
|
348
|
+
n.unshift(o);
|
|
349
349
|
break;
|
|
350
350
|
}
|
|
351
|
-
const
|
|
352
|
-
(
|
|
353
|
-
),
|
|
354
|
-
|
|
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
|
|
356
|
+
return n.join(" > ");
|
|
357
357
|
}
|
|
358
|
-
function
|
|
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(
|
|
367
|
-
height: Math.round(
|
|
366
|
+
width: Math.round(n.width),
|
|
367
|
+
height: Math.round(n.height)
|
|
368
368
|
};
|
|
369
369
|
}
|
|
370
|
-
function
|
|
370
|
+
function we(e, n) {
|
|
371
371
|
const t = window.getComputedStyle(e);
|
|
372
372
|
return {
|
|
373
|
-
margin:
|
|
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:
|
|
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:
|
|
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(
|
|
404
|
-
height: Math.round(
|
|
403
|
+
width: Math.round(n.width),
|
|
404
|
+
height: Math.round(n.height)
|
|
405
405
|
};
|
|
406
406
|
}
|
|
407
|
-
function
|
|
408
|
-
return e ===
|
|
407
|
+
function j(e, n, t, r) {
|
|
408
|
+
return e === n && n === t && t === r ? e : `${e} ${n} ${t} ${r}`;
|
|
409
409
|
}
|
|
410
|
-
function
|
|
410
|
+
function O(e) {
|
|
411
411
|
return typeof CSS < "u" && typeof CSS.escape == "function" ? CSS.escape(e) : e.replace(/["\\]/g, "\\$&");
|
|
412
412
|
}
|
|
413
|
-
function
|
|
413
|
+
function Le({
|
|
414
414
|
open: e,
|
|
415
|
-
onOpenChange:
|
|
415
|
+
onOpenChange: n,
|
|
416
416
|
placement: t = "top-right",
|
|
417
|
-
showResolved:
|
|
417
|
+
showResolved: r = !1
|
|
418
418
|
}) {
|
|
419
419
|
const {
|
|
420
|
-
config:
|
|
421
|
-
currentUser:
|
|
422
|
-
feedback:
|
|
423
|
-
normalizedPath:
|
|
424
|
-
permissions:
|
|
425
|
-
createFeedback:
|
|
426
|
-
resolveFeedback:
|
|
427
|
-
} =
|
|
428
|
-
() =>
|
|
429
|
-
[
|
|
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
|
-
|
|
432
|
-
e || (
|
|
433
|
-
}, [e])
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if (
|
|
437
|
-
return
|
|
438
|
-
|
|
439
|
-
|
|
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 (
|
|
442
|
-
if (!e ||
|
|
452
|
+
if (T(() => {
|
|
453
|
+
if (!e || !N)
|
|
443
454
|
return;
|
|
444
|
-
function
|
|
445
|
-
const
|
|
446
|
-
|
|
455
|
+
function s(L) {
|
|
456
|
+
const A = $(L);
|
|
457
|
+
y(A ? _(A) : void 0);
|
|
447
458
|
}
|
|
448
|
-
function
|
|
449
|
-
const
|
|
450
|
-
|
|
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",
|
|
453
|
-
window.removeEventListener("mousemove",
|
|
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
|
-
}, [
|
|
466
|
+
}, [N, $, p, e]), !e)
|
|
456
467
|
return null;
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
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:
|
|
478
|
-
selectedFeedback:
|
|
479
|
-
onSelect:
|
|
495
|
+
feedback: B,
|
|
496
|
+
selectedFeedback: v,
|
|
497
|
+
onSelect: W
|
|
480
498
|
}
|
|
481
|
-
),
|
|
482
|
-
/* @__PURE__ */
|
|
483
|
-
/* @__PURE__ */
|
|
484
|
-
/* @__PURE__ */
|
|
485
|
-
/* @__PURE__ */
|
|
486
|
-
/* @__PURE__ */
|
|
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__ */
|
|
506
|
+
/* @__PURE__ */ i("button", { type: "button", onClick: () => n == null ? void 0 : n(!1), children: "Close" })
|
|
489
507
|
] }),
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
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
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
|
537
|
-
const t =
|
|
538
|
-
return /* @__PURE__ */
|
|
614
|
+
function ve({ target: e, locked: n }) {
|
|
615
|
+
const t = Se(e);
|
|
616
|
+
return /* @__PURE__ */ f(
|
|
539
617
|
"div",
|
|
540
618
|
{
|
|
541
|
-
className:
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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
|
|
672
|
+
function be({
|
|
595
673
|
feedback: e,
|
|
596
|
-
selectedFeedback:
|
|
674
|
+
selectedFeedback: n,
|
|
597
675
|
onSelect: t
|
|
598
676
|
}) {
|
|
599
|
-
return /* @__PURE__ */
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
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
|
|
618
|
-
|
|
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__ */
|
|
630
|
-
/* @__PURE__ */
|
|
631
|
-
/* @__PURE__ */
|
|
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
|
|
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
|
|
642
|
-
const
|
|
643
|
-
top:
|
|
644
|
-
right:
|
|
645
|
-
bottom:
|
|
646
|
-
left:
|
|
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:
|
|
649
|
-
right:
|
|
650
|
-
bottom:
|
|
651
|
-
left:
|
|
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
|
-
},
|
|
663
|
-
top:
|
|
664
|
-
left:
|
|
665
|
-
width:
|
|
666
|
-
height:
|
|
667
|
-
},
|
|
668
|
-
top:
|
|
669
|
-
left:
|
|
670
|
-
width: Math.max(
|
|
671
|
-
height: Math.max(
|
|
672
|
-
},
|
|
673
|
-
top:
|
|
674
|
-
left:
|
|
675
|
-
width: Math.max(
|
|
676
|
-
height: Math.max(
|
|
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:
|
|
680
|
-
border:
|
|
681
|
-
padding:
|
|
682
|
-
content:
|
|
778
|
+
margin: c,
|
|
779
|
+
border: o,
|
|
780
|
+
padding: h,
|
|
781
|
+
content: w
|
|
683
782
|
};
|
|
684
783
|
}
|
|
685
|
-
function
|
|
686
|
-
const
|
|
687
|
-
return Number.isFinite(
|
|
784
|
+
function E(e) {
|
|
785
|
+
const n = Number.parseFloat(e || "0");
|
|
786
|
+
return Number.isFinite(n) ? n : 0;
|
|
688
787
|
}
|
|
689
788
|
export {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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;
|
|
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}
|