@qrkit/react 0.0.1 → 0.2.0

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/index.js CHANGED
@@ -1 +1,455 @@
1
+ // src/context.tsx
2
+ import {
3
+ createContext,
4
+ useCallback as useCallback5,
5
+ useContext,
6
+ useEffect as useEffect5,
7
+ useMemo,
8
+ useRef as useRef6,
9
+ useState as useState5
10
+ } from "react";
11
+ import { createPortal } from "react-dom";
12
+
13
+ // src/components/ConnectModal.tsx
14
+ import { useCallback as useCallback3 } from "react";
15
+ import { parseConnection } from "@qrkit/core";
16
+
17
+ // src/components/Modal.tsx
18
+ import { useEffect, useRef } from "react";
19
+ import { createFocusTrap } from "focus-trap";
20
+ import { jsx, jsxs } from "react/jsx-runtime";
21
+ function Modal({ title, onClose, children, className }) {
22
+ const containerRef = useRef(null);
23
+ useEffect(() => {
24
+ const el = containerRef.current;
25
+ const trap = el ? createFocusTrap(el, {
26
+ escapeDeactivates: false,
27
+ allowOutsideClick: true
28
+ }) : null;
29
+ trap?.activate();
30
+ return () => {
31
+ trap?.deactivate();
32
+ };
33
+ }, [onClose]);
34
+ useEffect(() => {
35
+ const handleKey = (e) => {
36
+ if (e.key === "Escape") onClose();
37
+ };
38
+ document.addEventListener("keydown", handleKey);
39
+ return () => document.removeEventListener("keydown", handleKey);
40
+ }, [onClose]);
41
+ return /* @__PURE__ */ jsx(
42
+ "div",
43
+ {
44
+ className: "qrkit qrkit-backdrop",
45
+ onClick: onClose,
46
+ role: "dialog",
47
+ "aria-modal": "true",
48
+ children: /* @__PURE__ */ jsxs(
49
+ "div",
50
+ {
51
+ ref: containerRef,
52
+ className: `qrkit-modal${className ? ` ${className}` : ""}`,
53
+ onClick: (e) => e.stopPropagation(),
54
+ children: [
55
+ /* @__PURE__ */ jsxs("div", { className: "qrkit-modal-header", children: [
56
+ /* @__PURE__ */ jsx("h2", { className: "qrkit-modal-title", children: title }),
57
+ /* @__PURE__ */ jsx("button", { className: "qrkit-close-btn", onClick: onClose, "aria-label": "Close", children: "\u2715" })
58
+ ] }),
59
+ children
60
+ ]
61
+ }
62
+ )
63
+ }
64
+ );
65
+ }
66
+
67
+ // src/hooks/useQRScanner.ts
68
+ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef3, useState as useState2 } from "react";
69
+ import jsQR from "jsqr";
70
+
71
+ // src/hooks/useURDecoder.ts
72
+ import { useCallback, useRef as useRef2, useState } from "react";
73
+ import { UrFountainDecoder } from "@qrkit/bc-ur";
74
+ function useURDecoder({ onScan }) {
75
+ const decoderRef = useRef2(new UrFountainDecoder());
76
+ const onScanRef = useRef2(onScan);
77
+ const [progress, setProgress] = useState(null);
78
+ onScanRef.current = onScan;
79
+ const reset = useCallback(() => {
80
+ decoderRef.current = new UrFountainDecoder();
81
+ setProgress(null);
82
+ }, []);
83
+ const receivePart = useCallback(
84
+ (data) => {
85
+ if (!data.toLowerCase().startsWith("ur:")) {
86
+ return onScanRef.current(data) !== false;
87
+ }
88
+ decoderRef.current.receivePartUr(data.toLowerCase());
89
+ setProgress(Math.round(decoderRef.current.estimatedPercentComplete() * 100));
90
+ if (!decoderRef.current.isComplete()) return false;
91
+ const ur = decoderRef.current.resultUr;
92
+ const scanned = { type: ur.type, cbor: ur.getPayloadCbor() };
93
+ if (onScanRef.current(scanned) !== false) return true;
94
+ reset();
95
+ return false;
96
+ },
97
+ [reset]
98
+ );
99
+ return { receivePart, progress, reset };
100
+ }
101
+
102
+ // src/hooks/useQRScanner.ts
103
+ function useQRScanner({
104
+ onScan,
105
+ enabled = true
106
+ }) {
107
+ const videoRef = useRef3(null);
108
+ const rafRef = useRef3(null);
109
+ const canvasRef = useRef3(null);
110
+ const doneRef = useRef3(false);
111
+ const [error, setError] = useState2(null);
112
+ const { receivePart, progress } = useURDecoder({ onScan });
113
+ const receivePartRef = useRef3(receivePart);
114
+ receivePartRef.current = receivePart;
115
+ const processFrame = useCallback2(() => {
116
+ const video = videoRef.current;
117
+ const canvas = canvasRef.current;
118
+ if (!video || !canvas || doneRef.current) return;
119
+ const ctx = canvas.getContext("2d", { willReadFrequently: true });
120
+ if (!ctx) return;
121
+ if (video.readyState === video.HAVE_ENOUGH_DATA) {
122
+ canvas.width = video.videoWidth;
123
+ canvas.height = video.videoHeight;
124
+ ctx.drawImage(video, 0, 0);
125
+ let imageData;
126
+ try {
127
+ imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
128
+ } catch {
129
+ setError(
130
+ "Canvas access blocked. Disable fingerprinting protection for this site to scan QR codes."
131
+ );
132
+ return;
133
+ }
134
+ {
135
+ const code = jsQR(imageData.data, imageData.width, imageData.height);
136
+ if (code) {
137
+ const done = receivePartRef.current(code.data);
138
+ if (done) {
139
+ doneRef.current = true;
140
+ return;
141
+ }
142
+ }
143
+ }
144
+ }
145
+ rafRef.current = requestAnimationFrame(processFrame);
146
+ }, []);
147
+ useEffect2(() => {
148
+ if (!enabled || !videoRef.current) return;
149
+ doneRef.current = false;
150
+ canvasRef.current = document.createElement("canvas");
151
+ let stream = null;
152
+ navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then((s) => {
153
+ stream = s;
154
+ if (videoRef.current) {
155
+ videoRef.current.srcObject = stream;
156
+ videoRef.current.play().catch(() => {
157
+ });
158
+ rafRef.current = requestAnimationFrame(processFrame);
159
+ }
160
+ }).catch(() => {
161
+ setError("Camera access denied. Please allow camera permissions.");
162
+ });
163
+ return () => {
164
+ if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
165
+ stream?.getTracks().forEach((t) => t.stop());
166
+ canvasRef.current = null;
167
+ };
168
+ }, [enabled, processFrame]);
169
+ return { videoRef, progress, error };
170
+ }
171
+
172
+ // src/components/QRScanner.tsx
173
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
174
+ function QRScanner({ onScan, hint, enabled = true, className }) {
175
+ const { videoRef, progress, error } = useQRScanner({ onScan, enabled });
176
+ if (error) {
177
+ return /* @__PURE__ */ jsx2("div", { className: `qrkit-scanner-error${className ? ` ${className}` : ""}`, children: error });
178
+ }
179
+ return /* @__PURE__ */ jsxs2("div", { className: `qrkit-scanner-wrap${className ? ` ${className}` : ""}`, children: [
180
+ /* @__PURE__ */ jsx2("video", { ref: videoRef, autoPlay: true, playsInline: true, muted: true, className: "qrkit-scanner-video" }),
181
+ /* @__PURE__ */ jsxs2("div", { className: "qrkit-scanner-overlay", children: [
182
+ /* @__PURE__ */ jsx2("div", { className: "qrkit-scanner-corner tl" }),
183
+ /* @__PURE__ */ jsx2("div", { className: "qrkit-scanner-corner tr" }),
184
+ /* @__PURE__ */ jsx2("div", { className: "qrkit-scanner-corner bl" }),
185
+ /* @__PURE__ */ jsx2("div", { className: "qrkit-scanner-corner br" })
186
+ ] }),
187
+ progress !== null && progress < 100 && /* @__PURE__ */ jsxs2("div", { className: "qrkit-scanner-progress", children: [
188
+ progress,
189
+ "%"
190
+ ] }),
191
+ /* @__PURE__ */ jsx2(
192
+ "p",
193
+ {
194
+ className: "qrkit-hint",
195
+ style: { position: "absolute", bottom: 8, left: 0, right: 0 },
196
+ children: progress !== null && progress < 100 ? "Keep scanning \u2014 animated QR in progress\u2026" : hint ?? "Point camera at the QR code"
197
+ }
198
+ )
199
+ ] });
200
+ }
201
+
202
+ // src/components/ConnectModal.tsx
203
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
204
+ function ConnectModal({ onConnect, onClose }) {
205
+ const handleScan = useCallback3(
206
+ (data) => {
207
+ try {
208
+ const accounts = parseConnection(data, { chains: ["evm"] });
209
+ const account = accounts[0];
210
+ if (!account) return false;
211
+ onConnect(account);
212
+ } catch {
213
+ return false;
214
+ }
215
+ },
216
+ [onConnect]
217
+ );
218
+ return /* @__PURE__ */ jsxs3(Modal, { title: "Connect Wallet", onClose, children: [
219
+ /* @__PURE__ */ jsxs3("p", { className: "qrkit-step", children: [
220
+ "On your hardware wallet, go to ",
221
+ /* @__PURE__ */ jsx3("strong", { children: "Connect software wallet" }),
222
+ " and point the screen at this camera."
223
+ ] }),
224
+ /* @__PURE__ */ jsx3(QRScanner, { onScan: handleScan, hint: "Scan the wallet's connection QR code" })
225
+ ] });
226
+ }
227
+
228
+ // src/components/SignModal.tsx
229
+ import { useCallback as useCallback4, useState as useState4 } from "react";
230
+ import { buildEthSignRequestURParts, parseEthSignature } from "@qrkit/core";
231
+
232
+ // src/hooks/useQRDisplay.ts
233
+ import { useEffect as useEffect4, useRef as useRef5 } from "react";
234
+ import QRCode from "qrcode";
235
+
236
+ // src/hooks/useQRParts.ts
237
+ import { useEffect as useEffect3, useRef as useRef4, useState as useState3 } from "react";
238
+ function useQRParts({
239
+ parts,
240
+ interval = 200
241
+ }) {
242
+ const [frame, setFrame] = useState3(0);
243
+ const frameRef = useRef4(0);
244
+ useEffect3(() => {
245
+ frameRef.current = 0;
246
+ setFrame(0);
247
+ }, [parts]);
248
+ useEffect3(() => {
249
+ if (parts.length <= 1) return;
250
+ const id = setInterval(() => {
251
+ frameRef.current = (frameRef.current + 1) % parts.length;
252
+ setFrame(frameRef.current);
253
+ }, interval);
254
+ return () => clearInterval(id);
255
+ }, [parts, interval]);
256
+ return {
257
+ part: parts[frameRef.current % Math.max(parts.length, 1)] ?? "",
258
+ frame,
259
+ total: parts.length
260
+ };
261
+ }
262
+
263
+ // src/hooks/useQRDisplay.ts
264
+ function useQRDisplay({
265
+ parts,
266
+ interval,
267
+ size = 300
268
+ }) {
269
+ const canvasRef = useRef5(null);
270
+ const { part, frame, total } = useQRParts({ parts, interval });
271
+ useEffect4(() => {
272
+ if (!part || !canvasRef.current) return;
273
+ QRCode.toCanvas(canvasRef.current, part, {
274
+ width: size,
275
+ margin: 2,
276
+ errorCorrectionLevel: "M"
277
+ }).catch(() => {
278
+ });
279
+ }, [part, size]);
280
+ return { canvasRef, frame, total };
281
+ }
282
+
283
+ // src/components/QRDisplay.tsx
284
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
285
+ function QRDisplay({ parts, interval, size = 300, className }) {
286
+ const { canvasRef, frame, total } = useQRDisplay({ parts, interval, size });
287
+ return /* @__PURE__ */ jsxs4("div", { className: `qrkit-qr-wrap${className ? ` ${className}` : ""}`, children: [
288
+ /* @__PURE__ */ jsx4("canvas", { ref: canvasRef, className: "qrkit-qr-canvas", width: size, height: size }),
289
+ total > 1 && /* @__PURE__ */ jsxs4("p", { className: "qrkit-hint", children: [
290
+ "Frame ",
291
+ frame + 1,
292
+ " / ",
293
+ total,
294
+ " \u2014 keep Shell pointed at the screen"
295
+ ] })
296
+ ] });
297
+ }
298
+
299
+ // src/components/SignModal.tsx
300
+ import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
301
+ function SignModal({ request, appName, onSign, onReject }) {
302
+ const [step, setStep] = useState4("display");
303
+ const parts = buildEthSignRequestURParts(
304
+ request.message,
305
+ request.address,
306
+ request.sourceFingerprint,
307
+ appName
308
+ );
309
+ const handleScan = useCallback4(
310
+ (data) => {
311
+ try {
312
+ const sig = parseEthSignature(data);
313
+ onSign(sig);
314
+ } catch {
315
+ return false;
316
+ }
317
+ },
318
+ [onSign]
319
+ );
320
+ return /* @__PURE__ */ jsxs5(
321
+ Modal,
322
+ {
323
+ title: step === "display" ? "Sign Request" : "Scan Signature",
324
+ onClose: onReject,
325
+ children: [
326
+ step === "display" && /* @__PURE__ */ jsxs5(Fragment, { children: [
327
+ /* @__PURE__ */ jsx5("p", { className: "qrkit-step", children: "Point your hardware wallet camera at this QR code to approve the sign request." }),
328
+ /* @__PURE__ */ jsx5(QRDisplay, { parts }),
329
+ /* @__PURE__ */ jsx5("button", { className: "qrkit-btn qrkit-btn-primary", onClick: () => setStep("scan"), children: "Wallet signed \u2014 scan response" }),
330
+ /* @__PURE__ */ jsx5("button", { className: "qrkit-btn qrkit-btn-ghost", onClick: onReject, children: "Cancel" })
331
+ ] }),
332
+ step === "scan" && /* @__PURE__ */ jsxs5(Fragment, { children: [
333
+ /* @__PURE__ */ jsx5("p", { className: "qrkit-step", children: "On your hardware wallet, show the signature QR and point it at this camera." }),
334
+ /* @__PURE__ */ jsx5(QRScanner, { onScan: handleScan, hint: "Scan the wallet's signature QR code" }),
335
+ /* @__PURE__ */ jsx5(
336
+ "button",
337
+ {
338
+ className: "qrkit-btn qrkit-btn-ghost",
339
+ onClick: () => setStep("display"),
340
+ children: "\u2190 Back"
341
+ }
342
+ )
343
+ ] })
344
+ ]
345
+ }
346
+ );
347
+ }
348
+
349
+ // src/context.tsx
350
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
351
+ var QRKitContext = createContext(null);
352
+ function buildThemeStyle(theme) {
353
+ const vars = {
354
+ "--qrkit-accent": theme.accent,
355
+ "--qrkit-bg": theme.background,
356
+ "--qrkit-backdrop": theme.backdrop,
357
+ "--qrkit-text": theme.text,
358
+ "--qrkit-text-muted": theme.textMuted,
359
+ "--qrkit-radius": theme.radius,
360
+ "--qrkit-font": theme.fontFamily
361
+ };
362
+ const declarations = Object.entries(vars).filter(([, v]) => v !== void 0).map(([k, v]) => ` ${k}: ${v};`).join("\n");
363
+ return declarations ? `.qrkit {
364
+ ${declarations}
365
+ }` : "";
366
+ }
367
+ function QRKitProvider({
368
+ children,
369
+ theme = {},
370
+ appName = "qrkit"
371
+ }) {
372
+ const [account, setAccount] = useState5(null);
373
+ const [connectOpen, setConnectOpen] = useState5(false);
374
+ const [pendingSign, setPendingSign] = useState5(null);
375
+ const pendingSignRef = useRef6(null);
376
+ const themeStyle = useMemo(() => buildThemeStyle(theme), [theme]);
377
+ useEffect5(() => {
378
+ if (!themeStyle) return;
379
+ const el = document.createElement("style");
380
+ el.setAttribute("data-qrkit-theme", "");
381
+ el.textContent = themeStyle;
382
+ document.head.appendChild(el);
383
+ return () => el.remove();
384
+ }, [themeStyle]);
385
+ const connect = useCallback5(() => setConnectOpen(true), []);
386
+ const disconnect = useCallback5(() => setAccount(null), []);
387
+ const handleConnect = useCallback5((acc) => {
388
+ setAccount(acc);
389
+ setConnectOpen(false);
390
+ }, []);
391
+ const sign = useCallback5((request) => {
392
+ return new Promise((resolve, reject) => {
393
+ const pending = { request, resolve, reject };
394
+ pendingSignRef.current = pending;
395
+ setPendingSign(pending);
396
+ });
397
+ }, []);
398
+ const handleSign = useCallback5((sig) => {
399
+ pendingSignRef.current?.resolve(sig);
400
+ pendingSignRef.current = null;
401
+ setPendingSign(null);
402
+ }, []);
403
+ const handleReject = useCallback5(() => {
404
+ pendingSignRef.current?.reject(new Error("User rejected the sign request"));
405
+ pendingSignRef.current = null;
406
+ setPendingSign(null);
407
+ }, []);
408
+ const value = useMemo(
409
+ () => ({ account, connect, disconnect, sign }),
410
+ [account, connect, disconnect, sign]
411
+ );
412
+ return /* @__PURE__ */ jsxs6(QRKitContext.Provider, { value, children: [
413
+ children,
414
+ connectOpen && createPortal(
415
+ /* @__PURE__ */ jsx6(
416
+ ConnectModal,
417
+ {
418
+ onConnect: handleConnect,
419
+ onClose: () => setConnectOpen(false)
420
+ }
421
+ ),
422
+ document.body
423
+ ),
424
+ pendingSign && createPortal(
425
+ /* @__PURE__ */ jsx6(
426
+ SignModal,
427
+ {
428
+ request: pendingSign.request,
429
+ appName,
430
+ onSign: handleSign,
431
+ onReject: handleReject
432
+ }
433
+ ),
434
+ document.body
435
+ )
436
+ ] });
437
+ }
438
+ function useQRKit() {
439
+ const ctx = useContext(QRKitContext);
440
+ if (!ctx) throw new Error("useQRKit must be used within a QRKitProvider");
441
+ return ctx;
442
+ }
443
+ export {
444
+ ConnectModal,
445
+ QRDisplay,
446
+ QRKitProvider,
447
+ QRScanner,
448
+ SignModal,
449
+ useQRDisplay,
450
+ useQRKit,
451
+ useQRParts,
452
+ useQRScanner,
453
+ useURDecoder
454
+ };
1
455
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../src/context.tsx","../src/components/ConnectModal.tsx","../src/components/Modal.tsx","../src/hooks/useQRScanner.ts","../src/hooks/useURDecoder.ts","../src/components/QRScanner.tsx","../src/components/SignModal.tsx","../src/hooks/useQRDisplay.ts","../src/hooks/useQRParts.ts","../src/components/QRDisplay.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\n\nimport { createPortal } from \"react-dom\";\n\nimport type { Account } from \"@qrkit/core\";\n\nimport { ConnectModal } from \"./components/ConnectModal.js\";\nimport { SignModal } from \"./components/SignModal.js\";\nimport type {\n QRKitContextValue,\n QRKitProviderProps,\n QRKitTheme,\n SignRequest,\n} from \"./types.js\";\n\nconst QRKitContext = createContext<QRKitContextValue | null>(null);\n\nfunction buildThemeStyle(theme: QRKitTheme): string {\n const vars: Record<string, string | undefined> = {\n \"--qrkit-accent\": theme.accent,\n \"--qrkit-bg\": theme.background,\n \"--qrkit-backdrop\": theme.backdrop,\n \"--qrkit-text\": theme.text,\n \"--qrkit-text-muted\": theme.textMuted,\n \"--qrkit-radius\": theme.radius,\n \"--qrkit-font\": theme.fontFamily,\n };\n\n const declarations = Object.entries(vars)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => ` ${k}: ${v};`)\n .join(\"\\n\");\n\n return declarations ? `.qrkit {\\n${declarations}\\n}` : \"\";\n}\n\ninterface PendingSign {\n request: SignRequest;\n resolve: (sig: string) => void;\n reject: (err: Error) => void;\n}\n\nexport function QRKitProvider({\n children,\n theme = {},\n appName = \"qrkit\",\n}: QRKitProviderProps) {\n const [account, setAccount] = useState<Account | null>(null);\n const [connectOpen, setConnectOpen] = useState(false);\n const [pendingSign, setPendingSign] = useState<PendingSign | null>(null);\n const pendingSignRef = useRef<PendingSign | null>(null);\n\n const themeStyle = useMemo(() => buildThemeStyle(theme), [theme]);\n\n useEffect(() => {\n if (!themeStyle) return;\n const el = document.createElement(\"style\");\n el.setAttribute(\"data-qrkit-theme\", \"\");\n el.textContent = themeStyle;\n document.head.appendChild(el);\n return () => el.remove();\n }, [themeStyle]);\n\n const connect = useCallback(() => setConnectOpen(true), []);\n const disconnect = useCallback(() => setAccount(null), []);\n\n const handleConnect = useCallback((acc: Account) => {\n setAccount(acc);\n setConnectOpen(false);\n }, []);\n\n const sign = useCallback((request: SignRequest): Promise<string> => {\n return new Promise((resolve, reject) => {\n const pending: PendingSign = { request, resolve, reject };\n pendingSignRef.current = pending;\n setPendingSign(pending);\n });\n }, []);\n\n const handleSign = useCallback((sig: string) => {\n pendingSignRef.current?.resolve(sig);\n pendingSignRef.current = null;\n setPendingSign(null);\n }, []);\n\n const handleReject = useCallback(() => {\n pendingSignRef.current?.reject(new Error(\"User rejected the sign request\"));\n pendingSignRef.current = null;\n setPendingSign(null);\n }, []);\n\n const value = useMemo<QRKitContextValue>(\n () => ({ account, connect, disconnect, sign }),\n [account, connect, disconnect, sign],\n );\n\n return (\n <QRKitContext.Provider value={value}>\n {children}\n {connectOpen &&\n createPortal(\n <ConnectModal\n onConnect={handleConnect}\n onClose={() => setConnectOpen(false)}\n />,\n document.body,\n )}\n {pendingSign &&\n createPortal(\n <SignModal\n request={pendingSign.request}\n appName={appName}\n onSign={handleSign}\n onReject={handleReject}\n />,\n document.body,\n )}\n </QRKitContext.Provider>\n );\n}\n\nexport function useQRKit(): QRKitContextValue {\n const ctx = useContext(QRKitContext);\n if (!ctx) throw new Error(\"useQRKit must be used within a QRKitProvider\");\n return ctx;\n}\n","import { useCallback } from \"react\";\n\nimport { parseConnection } from \"@qrkit/core\";\nimport type { Account, ScannedUR } from \"@qrkit/core\";\n\nimport { Modal } from \"./Modal.js\";\nimport { QRScanner } from \"./QRScanner.js\";\n\nexport interface ConnectModalProps {\n onConnect: (account: Account) => void;\n onClose: () => void;\n}\n\nexport function ConnectModal({ onConnect, onClose }: ConnectModalProps) {\n const handleScan = useCallback(\n (data: ScannedUR | string): boolean | void => {\n try {\n const accounts = parseConnection(data as ScannedUR, { chains: [\"evm\"] });\n const account = accounts[0];\n if (!account) return false;\n onConnect(account);\n } catch {\n return false;\n }\n },\n [onConnect],\n );\n\n return (\n <Modal title=\"Connect Wallet\" onClose={onClose}>\n <p className=\"qrkit-step\">\n On your hardware wallet, go to <strong>Connect software wallet</strong> and point\n the screen at this camera.\n </p>\n <QRScanner onScan={handleScan} hint=\"Scan the wallet's connection QR code\" />\n </Modal>\n );\n}\n","import { useEffect, useRef } from \"react\";\nimport type React from \"react\";\n\nimport { createFocusTrap } from \"focus-trap\";\n\nexport interface ModalProps {\n title: string;\n onClose: () => void;\n children: React.ReactNode;\n className?: string;\n}\n\nexport function Modal({ title, onClose, children, className }: ModalProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const el = containerRef.current;\n const trap = el\n ? createFocusTrap(el, {\n escapeDeactivates: false,\n allowOutsideClick: true,\n })\n : null;\n\n trap?.activate();\n return () => {\n trap?.deactivate();\n };\n }, [onClose]);\n\n useEffect(() => {\n const handleKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") onClose();\n };\n document.addEventListener(\"keydown\", handleKey);\n return () => document.removeEventListener(\"keydown\", handleKey);\n }, [onClose]);\n\n return (\n <div\n className=\"qrkit qrkit-backdrop\"\n onClick={onClose}\n role=\"dialog\"\n aria-modal=\"true\"\n >\n <div\n ref={containerRef}\n className={`qrkit-modal${className ? ` ${className}` : \"\"}`}\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"qrkit-modal-header\">\n <h2 className=\"qrkit-modal-title\">{title}</h2>\n <button className=\"qrkit-close-btn\" onClick={onClose} aria-label=\"Close\">\n ✕\n </button>\n </div>\n {children}\n </div>\n </div>\n );\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport jsQR from \"jsqr\";\n\nimport type { ScannedUR } from \"@qrkit/core\";\n\nimport { useURDecoder } from \"./useURDecoder.js\";\n\nexport interface UseQRScannerOptions {\n /**\n * Called when a QR code is decoded.\n * Return false to keep scanning (e.g. on parse error), void/true to stop.\n */\n onScan: (result: ScannedUR | string) => boolean | void;\n enabled?: boolean;\n}\n\nexport interface UseQRScannerResult {\n videoRef: React.RefObject<HTMLVideoElement | null>;\n /** 0–100 while scanning an animated UR, null otherwise */\n progress: number | null;\n error: string | null;\n}\n\nexport function useQRScanner({\n onScan,\n enabled = true,\n}: UseQRScannerOptions): UseQRScannerResult {\n const videoRef = useRef<HTMLVideoElement>(null);\n const rafRef = useRef<number | null>(null);\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\n const doneRef = useRef(false);\n const [error, setError] = useState<string | null>(null);\n\n const { receivePart, progress } = useURDecoder({ onScan });\n\n const receivePartRef = useRef(receivePart);\n receivePartRef.current = receivePart;\n\n const processFrame = useCallback((): void => {\n const video = videoRef.current;\n const canvas = canvasRef.current;\n if (!video || !canvas || doneRef.current) return;\n\n const ctx = canvas.getContext(\"2d\", { willReadFrequently: true });\n if (!ctx) return;\n\n if (video.readyState === video.HAVE_ENOUGH_DATA) {\n canvas.width = video.videoWidth;\n canvas.height = video.videoHeight;\n ctx.drawImage(video, 0, 0);\n let imageData: ImageData;\n try {\n imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);\n } catch {\n setError(\n \"Canvas access blocked. Disable fingerprinting protection for this site to scan QR codes.\",\n );\n return;\n }\n {\n const code = jsQR(imageData.data, imageData.width, imageData.height);\n if (code) {\n const done = receivePartRef.current(code.data);\n if (done) {\n doneRef.current = true;\n return;\n }\n }\n }\n }\n\n rafRef.current = requestAnimationFrame(processFrame);\n }, []);\n\n useEffect(() => {\n if (!enabled || !videoRef.current) return;\n\n doneRef.current = false;\n canvasRef.current = document.createElement(\"canvas\");\n\n let stream: MediaStream | null = null;\n\n navigator.mediaDevices\n .getUserMedia({ video: { facingMode: \"environment\" } })\n .then((s) => {\n stream = s;\n if (videoRef.current) {\n videoRef.current.srcObject = stream;\n videoRef.current.play().catch(() => {});\n rafRef.current = requestAnimationFrame(processFrame);\n }\n })\n .catch(() => {\n setError(\"Camera access denied. Please allow camera permissions.\");\n });\n\n return () => {\n if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);\n stream?.getTracks().forEach((t) => t.stop());\n canvasRef.current = null;\n };\n }, [enabled, processFrame]);\n\n return { videoRef, progress, error };\n}\n","import { useCallback, useRef, useState } from \"react\";\n\nimport { UrFountainDecoder } from \"@qrkit/bc-ur\";\n\nimport type { ScannedUR } from \"@qrkit/core\";\n\nexport interface UseURDecoderOptions {\n /**\n * Called when a QR string is decoded.\n * Return false to keep scanning (e.g. on parse error), void/true to stop.\n */\n onScan: (result: ScannedUR | string) => boolean | void;\n}\n\nexport interface UseURDecoderResult {\n /** Feed a raw QR string into the decoder. Returns true when scanning is done. */\n receivePart: (data: string) => boolean;\n /** 0–100 while assembling an animated UR, null otherwise */\n progress: number | null;\n /** Reset decoder state (e.g. to start a new scan) */\n reset: () => void;\n}\n\nexport function useURDecoder({ onScan }: UseURDecoderOptions): UseURDecoderResult {\n const decoderRef = useRef<UrFountainDecoder>(new UrFountainDecoder());\n const onScanRef = useRef(onScan);\n const [progress, setProgress] = useState<number | null>(null);\n\n onScanRef.current = onScan;\n\n const reset = useCallback(() => {\n decoderRef.current = new UrFountainDecoder();\n setProgress(null);\n }, []);\n\n const receivePart = useCallback(\n (data: string): boolean => {\n if (!data.toLowerCase().startsWith(\"ur:\")) {\n return onScanRef.current(data) !== false;\n }\n\n decoderRef.current.receivePartUr(data.toLowerCase());\n setProgress(Math.round(decoderRef.current.estimatedPercentComplete() * 100));\n\n if (!decoderRef.current.isComplete()) return false;\n\n const ur = decoderRef.current.resultUr;\n const scanned: ScannedUR = { type: ur.type, cbor: ur.getPayloadCbor() };\n if (onScanRef.current(scanned) !== false) return true;\n\n reset();\n return false;\n },\n [reset],\n );\n\n return { receivePart, progress, reset };\n}\n","import type { ScannedUR } from \"@qrkit/core\";\n\nimport { useQRScanner } from \"../hooks/useQRScanner.js\";\n\nexport interface QRScannerProps {\n onScan: (result: ScannedUR | string) => boolean | void;\n hint?: string;\n enabled?: boolean;\n className?: string;\n}\n\nexport function QRScanner({ onScan, hint, enabled = true, className }: QRScannerProps) {\n const { videoRef, progress, error } = useQRScanner({ onScan, enabled });\n\n if (error) {\n return (\n <div className={`qrkit-scanner-error${className ? ` ${className}` : \"\"}`}>\n {error}\n </div>\n );\n }\n\n return (\n <div className={`qrkit-scanner-wrap${className ? ` ${className}` : \"\"}`}>\n <video ref={videoRef} autoPlay playsInline muted className=\"qrkit-scanner-video\" />\n <div className=\"qrkit-scanner-overlay\">\n <div className=\"qrkit-scanner-corner tl\" />\n <div className=\"qrkit-scanner-corner tr\" />\n <div className=\"qrkit-scanner-corner bl\" />\n <div className=\"qrkit-scanner-corner br\" />\n </div>\n {progress !== null && progress < 100 && (\n <div className=\"qrkit-scanner-progress\">{progress}%</div>\n )}\n <p\n className=\"qrkit-hint\"\n style={{ position: \"absolute\", bottom: 8, left: 0, right: 0 }}\n >\n {progress !== null && progress < 100\n ? \"Keep scanning — animated QR in progress…\"\n : (hint ?? \"Point camera at the QR code\")}\n </p>\n </div>\n );\n}\n","import { useCallback, useState } from \"react\";\n\nimport { buildEthSignRequestURParts, parseEthSignature } from \"@qrkit/core\";\nimport type { ScannedUR } from \"@qrkit/core\";\n\nimport type { SignRequest } from \"../types.js\";\nimport { Modal } from \"./Modal.js\";\nimport { QRDisplay } from \"./QRDisplay.js\";\nimport { QRScanner } from \"./QRScanner.js\";\n\ntype Step = \"display\" | \"scan\";\n\nexport interface SignModalProps {\n request: SignRequest;\n appName: string;\n onSign: (signature: string) => void;\n onReject: () => void;\n}\n\nexport function SignModal({ request, appName, onSign, onReject }: SignModalProps) {\n const [step, setStep] = useState<Step>(\"display\");\n\n const parts = buildEthSignRequestURParts(\n request.message,\n request.address,\n request.sourceFingerprint,\n appName,\n );\n\n const handleScan = useCallback(\n (data: ScannedUR | string): boolean | void => {\n try {\n const sig = parseEthSignature(data as ScannedUR);\n onSign(sig);\n } catch {\n return false;\n }\n },\n [onSign],\n );\n\n return (\n <Modal\n title={step === \"display\" ? \"Sign Request\" : \"Scan Signature\"}\n onClose={onReject}\n >\n {step === \"display\" && (\n <>\n <p className=\"qrkit-step\">\n Point your hardware wallet camera at this QR code to approve the sign request.\n </p>\n <QRDisplay parts={parts} />\n <button className=\"qrkit-btn qrkit-btn-primary\" onClick={() => setStep(\"scan\")}>\n Wallet signed — scan response\n </button>\n <button className=\"qrkit-btn qrkit-btn-ghost\" onClick={onReject}>\n Cancel\n </button>\n </>\n )}\n\n {step === \"scan\" && (\n <>\n <p className=\"qrkit-step\">\n On your hardware wallet, show the signature QR and point it at this camera.\n </p>\n <QRScanner onScan={handleScan} hint=\"Scan the wallet's signature QR code\" />\n <button\n className=\"qrkit-btn qrkit-btn-ghost\"\n onClick={() => setStep(\"display\")}\n >\n ← Back\n </button>\n </>\n )}\n </Modal>\n );\n}\n","import { useEffect, useRef } from \"react\";\n\nimport QRCode from \"qrcode\";\n\nimport { useQRParts } from \"./useQRParts.js\";\n\nexport interface UseQRDisplayOptions {\n /** QR parts to cycle through. Single-frame: pass an array with one element. */\n parts: string[];\n /** Interval between frames in ms. Default: 200 */\n interval?: number;\n /** Canvas size in pixels. Default: 300 */\n size?: number;\n}\n\nexport interface UseQRDisplayResult {\n canvasRef: React.RefObject<HTMLCanvasElement | null>;\n /** Current frame index */\n frame: number;\n total: number;\n}\n\nexport function useQRDisplay({\n parts,\n interval,\n size = 300,\n}: UseQRDisplayOptions): UseQRDisplayResult {\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const { part, frame, total } = useQRParts({ parts, interval });\n\n useEffect(() => {\n if (!part || !canvasRef.current) return;\n\n QRCode.toCanvas(canvasRef.current, part, {\n width: size,\n margin: 2,\n errorCorrectionLevel: \"M\",\n }).catch(() => {\n // ignore render errors\n });\n }, [part, size]);\n\n return { canvasRef, frame, total };\n}\n","import { useEffect, useRef, useState } from \"react\";\n\nexport interface UseQRPartsOptions {\n /** QR parts to cycle through. Single-frame: pass an array with one element. */\n parts: string[];\n /** Interval between frames in ms. Default: 200 */\n interval?: number;\n}\n\nexport interface UseQRPartsResult {\n /** The current QR string to render */\n part: string;\n /** Current frame index (0-based) */\n frame: number;\n total: number;\n}\n\nexport function useQRParts({\n parts,\n interval = 200,\n}: UseQRPartsOptions): UseQRPartsResult {\n const [frame, setFrame] = useState(0);\n const frameRef = useRef(0);\n\n useEffect(() => {\n frameRef.current = 0;\n setFrame(0);\n }, [parts]);\n\n useEffect(() => {\n if (parts.length <= 1) return;\n\n const id = setInterval(() => {\n frameRef.current = (frameRef.current + 1) % parts.length;\n setFrame(frameRef.current);\n }, interval);\n\n return () => clearInterval(id);\n }, [parts, interval]);\n\n return {\n part: parts[frameRef.current % Math.max(parts.length, 1)] ?? \"\",\n frame,\n total: parts.length,\n };\n}\n","import { useQRDisplay } from \"../hooks/useQRDisplay.js\";\n\nexport interface QRDisplayProps {\n parts: string[];\n interval?: number;\n size?: number;\n className?: string;\n}\n\nexport function QRDisplay({ parts, interval, size = 300, className }: QRDisplayProps) {\n const { canvasRef, frame, total } = useQRDisplay({ parts, interval, size });\n\n return (\n <div className={`qrkit-qr-wrap${className ? ` ${className}` : \"\"}`}>\n <canvas ref={canvasRef} className=\"qrkit-qr-canvas\" width={size} height={size} />\n {total > 1 && (\n <p className=\"qrkit-hint\">\n Frame {frame + 1} / {total} — keep Shell pointed at the screen\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":["useCallback","useEffect","useRef","useState","useCallback","useCallback","useEffect","useRef","useState","useRef","useRef","useState","useCallback","useEffect","jsx","jsxs","jsx","jsxs","useCallback","useCallback","useState","useEffect","useRef","useEffect","useRef","useState","useRef","useEffect","jsx","jsxs","jsx","jsxs","useState","useCallback","jsx","jsxs","useState","useRef","useEffect","useCallback"]}