@sylergydigital/issue-pin-sdk 0.6.4 → 0.6.6
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/CHANGELOG.md +22 -0
- package/dist/index.cjs +197 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +200 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -157,6 +157,7 @@ interface FeedbackContextType {
|
|
|
157
157
|
submitThread: (body: string, visibility: "public" | "internal") => Promise<void>;
|
|
158
158
|
submitScreenshotThread: (body: string, visibility: "public" | "internal") => Promise<void>;
|
|
159
159
|
refreshThreads: () => Promise<void>;
|
|
160
|
+
openThreadInDashboard: (threadId: string) => void;
|
|
160
161
|
}
|
|
161
162
|
/**
|
|
162
163
|
* SDK Entry Point — wrap your app to enable in-app feedback.
|
package/dist/index.d.ts
CHANGED
|
@@ -157,6 +157,7 @@ interface FeedbackContextType {
|
|
|
157
157
|
submitThread: (body: string, visibility: "public" | "internal") => Promise<void>;
|
|
158
158
|
submitScreenshotThread: (body: string, visibility: "public" | "internal") => Promise<void>;
|
|
159
159
|
refreshThreads: () => Promise<void>;
|
|
160
|
+
openThreadInDashboard: (threadId: string) => void;
|
|
160
161
|
}
|
|
161
162
|
/**
|
|
162
163
|
* SDK Entry Point — wrap your app to enable in-app feedback.
|
package/dist/index.js
CHANGED
|
@@ -437,6 +437,34 @@ function FeedbackProvider({
|
|
|
437
437
|
const onModeChangeUnified = config.onModeChange ?? (config.onFeedbackActiveChange ? ((m) => config.onFeedbackActiveChange(m === "annotate")) : void 0);
|
|
438
438
|
const controlledModeFromProps = config.mode !== void 0 ? config.mode : config.feedbackActive !== void 0 ? config.feedbackActive ? "annotate" : "view" : void 0;
|
|
439
439
|
const initialModeUncontrolled = config.mode ?? (config.feedbackActive !== void 0 ? config.feedbackActive ? "annotate" : "view" : "view");
|
|
440
|
+
const openThreadInDashboard = useCallback((threadId) => {
|
|
441
|
+
const threadPath = `/threads/${threadId}`;
|
|
442
|
+
const baseUrl = resolved.siteUrl?.replace(/\/+$/, "") || window.location.origin;
|
|
443
|
+
if (config.apiKey && autoIdentity.accessToken) {
|
|
444
|
+
const functionsBaseUrl = getFunctionsBaseUrl(resolved.supabaseUrl);
|
|
445
|
+
fetch(`${functionsBaseUrl}/external-sso-launch`, {
|
|
446
|
+
method: "POST",
|
|
447
|
+
headers: {
|
|
448
|
+
"Content-Type": "application/json",
|
|
449
|
+
Authorization: `Bearer ${autoIdentity.accessToken}`
|
|
450
|
+
},
|
|
451
|
+
body: JSON.stringify({
|
|
452
|
+
apiKey: config.apiKey,
|
|
453
|
+
nextPath: threadPath
|
|
454
|
+
})
|
|
455
|
+
}).then((res) => res.ok ? res.json() : null).then((data) => {
|
|
456
|
+
if (data?.redirect_url) {
|
|
457
|
+
window.open(data.redirect_url, "_blank", "noopener,noreferrer");
|
|
458
|
+
} else {
|
|
459
|
+
window.open(`${baseUrl}${threadPath}`, "_blank", "noopener,noreferrer");
|
|
460
|
+
}
|
|
461
|
+
}).catch(() => {
|
|
462
|
+
window.open(`${baseUrl}${threadPath}`, "_blank", "noopener,noreferrer");
|
|
463
|
+
});
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
window.open(`${baseUrl}${threadPath}`, "_blank", "noopener,noreferrer");
|
|
467
|
+
}, [resolved.supabaseUrl, resolved.siteUrl, config.apiKey, autoIdentity.accessToken]);
|
|
440
468
|
return /* @__PURE__ */ jsx(
|
|
441
469
|
FeedbackProviderInner,
|
|
442
470
|
{
|
|
@@ -463,6 +491,7 @@ function FeedbackProvider({
|
|
|
463
491
|
userId: effectiveUserId,
|
|
464
492
|
userEmail: effectiveEmail,
|
|
465
493
|
userDisplayName: effectiveDisplayName,
|
|
494
|
+
openThreadInDashboard,
|
|
466
495
|
children
|
|
467
496
|
}
|
|
468
497
|
);
|
|
@@ -491,7 +520,8 @@ function FeedbackProviderInner({
|
|
|
491
520
|
debug,
|
|
492
521
|
userId,
|
|
493
522
|
userEmail,
|
|
494
|
-
userDisplayName
|
|
523
|
+
userDisplayName,
|
|
524
|
+
openThreadInDashboard
|
|
495
525
|
}) {
|
|
496
526
|
const debugLog = useCallback((message, extra) => {
|
|
497
527
|
if (!debug) return;
|
|
@@ -882,7 +912,8 @@ function FeedbackProviderInner({
|
|
|
882
912
|
setPendingScreenshotPin,
|
|
883
913
|
submitThread,
|
|
884
914
|
submitScreenshotThread,
|
|
885
|
-
refreshThreads: fetchThreads
|
|
915
|
+
refreshThreads: fetchThreads,
|
|
916
|
+
openThreadInDashboard
|
|
886
917
|
},
|
|
887
918
|
children: [
|
|
888
919
|
children,
|
|
@@ -1242,7 +1273,7 @@ function ThreadPins() {
|
|
|
1242
1273
|
const signedUrlCache = useRef2({});
|
|
1243
1274
|
const threads = ctx?.threads ?? EMPTY_THREADS;
|
|
1244
1275
|
const client = ctx?.client;
|
|
1245
|
-
const
|
|
1276
|
+
const openThreadInDashboard = ctx?.openThreadInDashboard;
|
|
1246
1277
|
const scrollContainer = ctx?.scrollContainer;
|
|
1247
1278
|
const container = scrollContainer?.ref.current ?? null;
|
|
1248
1279
|
const getSignedUrl = useCallback4(async (path) => {
|
|
@@ -1366,8 +1397,8 @@ function ThreadPins() {
|
|
|
1366
1397
|
});
|
|
1367
1398
|
return;
|
|
1368
1399
|
}
|
|
1369
|
-
|
|
1370
|
-
}, [getSignedUrl,
|
|
1400
|
+
openThreadInDashboard?.(pin.threadId);
|
|
1401
|
+
}, [getSignedUrl, openThreadInDashboard]);
|
|
1371
1402
|
const containerLayer = useMemo3(() => {
|
|
1372
1403
|
if (!container || containerPositions.length === 0) return null;
|
|
1373
1404
|
const { width, height } = getContainerContentSize(container);
|
|
@@ -1494,7 +1525,7 @@ function ReviewSurfaceOverlay() {
|
|
|
1494
1525
|
const mode = ctx?.mode ?? "view";
|
|
1495
1526
|
const reviewUrl = ctx?.reviewUrl ?? null;
|
|
1496
1527
|
const threads = ctx?.threads ?? EMPTY_THREADS2;
|
|
1497
|
-
const
|
|
1528
|
+
const openThreadInDashboard = ctx?.openThreadInDashboard;
|
|
1498
1529
|
const updateMetrics = useCallback5(() => {
|
|
1499
1530
|
const iframe = iframeRef.current;
|
|
1500
1531
|
if (!iframe) return;
|
|
@@ -1672,7 +1703,7 @@ function ReviewSurfaceOverlay() {
|
|
|
1672
1703
|
index: index + 1,
|
|
1673
1704
|
left,
|
|
1674
1705
|
top,
|
|
1675
|
-
onClick: () =>
|
|
1706
|
+
onClick: () => openThreadInDashboard?.(thread.id)
|
|
1676
1707
|
},
|
|
1677
1708
|
thread.id
|
|
1678
1709
|
);
|
|
@@ -2096,7 +2127,7 @@ function ScreenshotFeedback() {
|
|
|
2096
2127
|
}
|
|
2097
2128
|
|
|
2098
2129
|
// src/FeedbackButton.tsx
|
|
2099
|
-
import { useRef as useRef5, useEffect as useEffect6, useState as useState7 } from "react";
|
|
2130
|
+
import { useRef as useRef5, useEffect as useEffect6, useState as useState7, useCallback as useCallback8 } from "react";
|
|
2100
2131
|
import { MessageSquarePlus, MapPin, Camera as Camera2 } from "lucide-react";
|
|
2101
2132
|
|
|
2102
2133
|
// src/launcher.ts
|
|
@@ -2118,10 +2149,52 @@ function getLauncherCapabilities({
|
|
|
2118
2149
|
|
|
2119
2150
|
// src/FeedbackButton.tsx
|
|
2120
2151
|
import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2152
|
+
var LAUNCHER_POS_KEY = "issue-pin:launcher-pos";
|
|
2153
|
+
var DRAG_THRESHOLD = 5;
|
|
2154
|
+
var EDGE_MARGIN = 20;
|
|
2155
|
+
var BTN_SIZE = 48;
|
|
2156
|
+
var SNAP_EASING = "cubic-bezier(0.2, 0, 0, 1)";
|
|
2157
|
+
var SNAP_TRANSITION = `left 0.3s ${SNAP_EASING}, top 0.3s ${SNAP_EASING}`;
|
|
2158
|
+
function loadPos(initialEdge) {
|
|
2159
|
+
try {
|
|
2160
|
+
const raw = localStorage.getItem(LAUNCHER_POS_KEY);
|
|
2161
|
+
if (raw) {
|
|
2162
|
+
const parsed = JSON.parse(raw);
|
|
2163
|
+
if ((parsed.edge === "left" || parsed.edge === "right") && typeof parsed.y === "number") {
|
|
2164
|
+
return { edge: parsed.edge, y: parsed.y };
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
} catch {
|
|
2168
|
+
}
|
|
2169
|
+
return {
|
|
2170
|
+
edge: initialEdge === "bottom-left" ? "left" : "right",
|
|
2171
|
+
y: window.innerHeight - BTN_SIZE - EDGE_MARGIN
|
|
2172
|
+
};
|
|
2173
|
+
}
|
|
2174
|
+
function savePos(pos) {
|
|
2175
|
+
try {
|
|
2176
|
+
localStorage.setItem(LAUNCHER_POS_KEY, JSON.stringify(pos));
|
|
2177
|
+
} catch {
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
function clampY(y) {
|
|
2181
|
+
return Math.min(
|
|
2182
|
+
Math.max(y, EDGE_MARGIN),
|
|
2183
|
+
window.innerHeight - BTN_SIZE - EDGE_MARGIN
|
|
2184
|
+
);
|
|
2185
|
+
}
|
|
2186
|
+
function snapXForEdge(edge) {
|
|
2187
|
+
return edge === "left" ? EDGE_MARGIN : window.innerWidth - BTN_SIZE - EDGE_MARGIN;
|
|
2188
|
+
}
|
|
2121
2189
|
function FeedbackButton({ position = "bottom-right" }) {
|
|
2122
2190
|
const ctx = useFeedbackSafe();
|
|
2123
2191
|
const menuRef = useRef5(null);
|
|
2124
2192
|
const [hintDismissed, setHintDismissed] = useState7(false);
|
|
2193
|
+
const [pos, setPos] = useState7(() => loadPos(position));
|
|
2194
|
+
const [dragXY, setDragXY] = useState7(null);
|
|
2195
|
+
const [snapping, setSnapping] = useState7(false);
|
|
2196
|
+
const dragStateRef = useRef5(null);
|
|
2197
|
+
const snapTimerRef = useRef5(null);
|
|
2125
2198
|
const menuOpenState = ctx?.menuOpen ?? false;
|
|
2126
2199
|
const debug = ctx?.debug ?? false;
|
|
2127
2200
|
useEffect6(() => {
|
|
@@ -2139,6 +2212,79 @@ function FeedbackButton({ position = "bottom-right" }) {
|
|
|
2139
2212
|
console.log("[EW SDK] FeedbackButton mounted");
|
|
2140
2213
|
return () => console.log("[EW SDK] FeedbackButton unmounted");
|
|
2141
2214
|
}, [debug]);
|
|
2215
|
+
useEffect6(() => {
|
|
2216
|
+
const handleResize = () => {
|
|
2217
|
+
setPos((prev) => {
|
|
2218
|
+
const newPos = {
|
|
2219
|
+
edge: prev.edge,
|
|
2220
|
+
y: clampY(prev.y)
|
|
2221
|
+
};
|
|
2222
|
+
savePos(newPos);
|
|
2223
|
+
return newPos;
|
|
2224
|
+
});
|
|
2225
|
+
};
|
|
2226
|
+
window.addEventListener("resize", handleResize);
|
|
2227
|
+
return () => window.removeEventListener("resize", handleResize);
|
|
2228
|
+
}, []);
|
|
2229
|
+
useEffect6(() => {
|
|
2230
|
+
return () => {
|
|
2231
|
+
if (snapTimerRef.current) clearTimeout(snapTimerRef.current);
|
|
2232
|
+
};
|
|
2233
|
+
}, []);
|
|
2234
|
+
const handlePointerDown = useCallback8((e) => {
|
|
2235
|
+
if (e.button != null && e.button !== 0) return;
|
|
2236
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
2237
|
+
dragStateRef.current = {
|
|
2238
|
+
active: false,
|
|
2239
|
+
startX: e.clientX,
|
|
2240
|
+
startY: e.clientY,
|
|
2241
|
+
currentX: e.clientX,
|
|
2242
|
+
currentY: e.clientY,
|
|
2243
|
+
didDrag: false
|
|
2244
|
+
};
|
|
2245
|
+
}, []);
|
|
2246
|
+
const handlePointerMove = useCallback8((e) => {
|
|
2247
|
+
const ds = dragStateRef.current;
|
|
2248
|
+
if (!ds) return;
|
|
2249
|
+
const dx = e.clientX - ds.startX;
|
|
2250
|
+
const dy = e.clientY - ds.startY;
|
|
2251
|
+
const distance = Math.sqrt(dx * dx + dy * dy);
|
|
2252
|
+
if (!ds.active && distance >= DRAG_THRESHOLD) {
|
|
2253
|
+
ds.active = true;
|
|
2254
|
+
ds.didDrag = true;
|
|
2255
|
+
}
|
|
2256
|
+
if (ds.active) {
|
|
2257
|
+
ds.currentX = e.clientX;
|
|
2258
|
+
ds.currentY = e.clientY;
|
|
2259
|
+
const x = e.clientX - BTN_SIZE / 2;
|
|
2260
|
+
const y = clampY(e.clientY - BTN_SIZE / 2);
|
|
2261
|
+
setDragXY({ x, y });
|
|
2262
|
+
}
|
|
2263
|
+
}, []);
|
|
2264
|
+
const handlePointerUp = useCallback8((e) => {
|
|
2265
|
+
try {
|
|
2266
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
2267
|
+
} catch {
|
|
2268
|
+
}
|
|
2269
|
+
const ds = dragStateRef.current;
|
|
2270
|
+
if (!ds || !ds.didDrag) {
|
|
2271
|
+
dragStateRef.current = null;
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
const centerX = ds.currentX;
|
|
2275
|
+
const edge = centerX < window.innerWidth / 2 ? "left" : "right";
|
|
2276
|
+
const y = clampY(ds.currentY - BTN_SIZE / 2);
|
|
2277
|
+
const newPos = { edge, y };
|
|
2278
|
+
setSnapping(true);
|
|
2279
|
+
setPos(newPos);
|
|
2280
|
+
savePos(newPos);
|
|
2281
|
+
setDragXY(null);
|
|
2282
|
+
if (snapTimerRef.current) clearTimeout(snapTimerRef.current);
|
|
2283
|
+
snapTimerRef.current = setTimeout(() => {
|
|
2284
|
+
setSnapping(false);
|
|
2285
|
+
snapTimerRef.current = null;
|
|
2286
|
+
}, 300);
|
|
2287
|
+
}, []);
|
|
2142
2288
|
if (!ctx) return null;
|
|
2143
2289
|
const {
|
|
2144
2290
|
mode,
|
|
@@ -2154,9 +2300,13 @@ function FeedbackButton({ position = "bottom-right" }) {
|
|
|
2154
2300
|
} = ctx;
|
|
2155
2301
|
const annotate = mode === "annotate";
|
|
2156
2302
|
const capabilities = getLauncherCapabilities({ allowPinOnPage: canPinOnPage, allowScreenshot: canScreenshot });
|
|
2157
|
-
const posStyle = position === "bottom-left" ? { left: 20, bottom: 20 } : { right: 20, bottom: 20 };
|
|
2158
|
-
const hintStyle = position === "bottom-left" ? { left: 56, bottom: 8 } : { right: 56, bottom: 8 };
|
|
2159
2303
|
const handleToggle = () => {
|
|
2304
|
+
if (dragStateRef.current?.didDrag) {
|
|
2305
|
+
dragStateRef.current.didDrag = false;
|
|
2306
|
+
dragStateRef.current = null;
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
dragStateRef.current = null;
|
|
2160
2310
|
if (debug) console.log("[EW SDK] handleToggle", { mode, menuOpen });
|
|
2161
2311
|
setHintDismissed(true);
|
|
2162
2312
|
if (annotate) {
|
|
@@ -2176,12 +2326,44 @@ function FeedbackButton({ position = "bottom-right" }) {
|
|
|
2176
2326
|
const capturing = screenshotCapturing;
|
|
2177
2327
|
const showLauncherHint = showHints && !hintDismissed && !annotate && !menuOpen;
|
|
2178
2328
|
if (capabilities.actionCount === 0) return null;
|
|
2329
|
+
const restX = snapXForEdge(pos.edge);
|
|
2330
|
+
let wrapperStyle;
|
|
2331
|
+
if (dragXY !== null) {
|
|
2332
|
+
wrapperStyle = {
|
|
2333
|
+
position: "fixed",
|
|
2334
|
+
left: dragXY.x,
|
|
2335
|
+
top: dragXY.y,
|
|
2336
|
+
transition: "none",
|
|
2337
|
+
zIndex: Z.launcher
|
|
2338
|
+
};
|
|
2339
|
+
} else if (snapping) {
|
|
2340
|
+
wrapperStyle = {
|
|
2341
|
+
position: "fixed",
|
|
2342
|
+
left: restX,
|
|
2343
|
+
top: pos.y,
|
|
2344
|
+
transition: SNAP_TRANSITION,
|
|
2345
|
+
zIndex: Z.launcher
|
|
2346
|
+
};
|
|
2347
|
+
} else {
|
|
2348
|
+
wrapperStyle = {
|
|
2349
|
+
position: "fixed",
|
|
2350
|
+
left: restX,
|
|
2351
|
+
top: pos.y,
|
|
2352
|
+
transition: "none",
|
|
2353
|
+
zIndex: Z.launcher
|
|
2354
|
+
};
|
|
2355
|
+
}
|
|
2356
|
+
const hintStyle = pos.edge === "right" ? { right: BTN_SIZE + 8, top: "50%", transform: "translateY(-50%)" } : { left: BTN_SIZE + 8, top: "50%", transform: "translateY(-50%)" };
|
|
2357
|
+
const menuPositionStyle = pos.edge === "right" ? { right: 0, bottom: BTN_SIZE + 4 } : { left: 0, bottom: BTN_SIZE + 4 };
|
|
2179
2358
|
return /* @__PURE__ */ jsxs6(
|
|
2180
2359
|
"div",
|
|
2181
2360
|
{
|
|
2182
2361
|
ref: menuRef,
|
|
2183
2362
|
"data-ew-feedback-interactive": "true",
|
|
2184
|
-
style: {
|
|
2363
|
+
style: { ...wrapperStyle, touchAction: "none" },
|
|
2364
|
+
onPointerDown: handlePointerDown,
|
|
2365
|
+
onPointerMove: handlePointerMove,
|
|
2366
|
+
onPointerUp: handlePointerUp,
|
|
2185
2367
|
children: [
|
|
2186
2368
|
showLauncherHint && /* @__PURE__ */ jsxs6(
|
|
2187
2369
|
"div",
|
|
@@ -2208,8 +2390,7 @@ function FeedbackButton({ position = "bottom-right" }) {
|
|
|
2208
2390
|
{
|
|
2209
2391
|
style: {
|
|
2210
2392
|
position: "absolute",
|
|
2211
|
-
|
|
2212
|
-
right: 0,
|
|
2393
|
+
...menuPositionStyle,
|
|
2213
2394
|
marginBottom: 4,
|
|
2214
2395
|
width: 176,
|
|
2215
2396
|
borderRadius: 8,
|
|
@@ -2274,7 +2455,7 @@ function FeedbackButton({ position = "bottom-right" }) {
|
|
|
2274
2455
|
children: [
|
|
2275
2456
|
/* @__PURE__ */ jsx7(Camera2, { style: { width: 16, height: 16, color: T.primary, flexShrink: 0 } }),
|
|
2276
2457
|
/* @__PURE__ */ jsxs6("div", { children: [
|
|
2277
|
-
/* @__PURE__ */ jsx7("div", { style: { fontWeight: 500 }, children: capturing ? "Capturing
|
|
2458
|
+
/* @__PURE__ */ jsx7("div", { style: { fontWeight: 500 }, children: capturing ? "Capturing..." : "Screenshot" }),
|
|
2278
2459
|
/* @__PURE__ */ jsx7("div", { style: { fontSize: 10, color: T.muted }, children: "Capture current view" })
|
|
2279
2460
|
] })
|
|
2280
2461
|
]
|
|
@@ -2289,8 +2470,8 @@ function FeedbackButton({ position = "bottom-right" }) {
|
|
|
2289
2470
|
onClick: handleToggle,
|
|
2290
2471
|
style: {
|
|
2291
2472
|
display: "flex",
|
|
2292
|
-
height:
|
|
2293
|
-
width:
|
|
2473
|
+
height: BTN_SIZE,
|
|
2474
|
+
width: BTN_SIZE,
|
|
2294
2475
|
alignItems: "center",
|
|
2295
2476
|
justifyContent: "center",
|
|
2296
2477
|
borderRadius: "50%",
|
|
@@ -2299,7 +2480,7 @@ function FeedbackButton({ position = "bottom-right" }) {
|
|
|
2299
2480
|
border: annotate ? "none" : `1px solid ${T.border}`,
|
|
2300
2481
|
background: annotate ? T.primary : T.card,
|
|
2301
2482
|
color: annotate ? T.primaryFg : T.fg,
|
|
2302
|
-
cursor: "pointer",
|
|
2483
|
+
cursor: dragXY !== null ? "grabbing" : "pointer",
|
|
2303
2484
|
transform: annotate ? "scale(1.1)" : "scale(1)"
|
|
2304
2485
|
},
|
|
2305
2486
|
title: annotate ? "Exit feedback mode" : "Open feedback menu",
|
|
@@ -2313,7 +2494,7 @@ function FeedbackButton({ position = "bottom-right" }) {
|
|
|
2313
2494
|
}
|
|
2314
2495
|
|
|
2315
2496
|
// src/ModalFeedbackInjector.tsx
|
|
2316
|
-
import { useState as useState8, useEffect as useEffect7, useCallback as
|
|
2497
|
+
import { useState as useState8, useEffect as useEffect7, useCallback as useCallback9 } from "react";
|
|
2317
2498
|
import { createPortal as createPortal3 } from "react-dom";
|
|
2318
2499
|
import { Camera as Camera3 } from "lucide-react";
|
|
2319
2500
|
import { Fragment as Fragment4, jsx as jsx8 } from "react/jsx-runtime";
|
|
@@ -2332,7 +2513,7 @@ function ModalFeedbackInjector({
|
|
|
2332
2513
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
2333
2514
|
return () => observer.disconnect();
|
|
2334
2515
|
}, []);
|
|
2335
|
-
const handleCapture =
|
|
2516
|
+
const handleCapture = useCallback9(async () => {
|
|
2336
2517
|
if (!ctx) return;
|
|
2337
2518
|
await ctx.startScreenshotCapture();
|
|
2338
2519
|
}, [ctx]);
|