@qrkit/react 0.1.0 → 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.cjs +109 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +118 -48
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -60,8 +60,7 @@ function Modal({ title, onClose, children, className }) {
|
|
|
60
60
|
(0, import_react.useEffect)(() => {
|
|
61
61
|
const el = containerRef.current;
|
|
62
62
|
const trap = el ? (0, import_focus_trap.createFocusTrap)(el, {
|
|
63
|
-
escapeDeactivates:
|
|
64
|
-
onDeactivate: onClose,
|
|
63
|
+
escapeDeactivates: false,
|
|
65
64
|
allowOutsideClick: true
|
|
66
65
|
}) : null;
|
|
67
66
|
trap?.activate();
|
|
@@ -76,37 +75,46 @@ function Modal({ title, onClose, children, className }) {
|
|
|
76
75
|
document.addEventListener("keydown", handleKey);
|
|
77
76
|
return () => document.removeEventListener("keydown", handleKey);
|
|
78
77
|
}, [onClose]);
|
|
79
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
78
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
80
79
|
"div",
|
|
81
80
|
{
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
className: "qrkit qrkit-backdrop",
|
|
82
|
+
onClick: onClose,
|
|
83
|
+
role: "dialog",
|
|
84
|
+
"aria-modal": "true",
|
|
85
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
86
|
+
"div",
|
|
87
|
+
{
|
|
88
|
+
ref: containerRef,
|
|
89
|
+
className: `qrkit-modal${className ? ` ${className}` : ""}`,
|
|
90
|
+
onClick: (e) => e.stopPropagation(),
|
|
91
|
+
children: [
|
|
92
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "qrkit-modal-header", children: [
|
|
93
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "qrkit-modal-title", children: title }),
|
|
94
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "qrkit-close-btn", onClick: onClose, "aria-label": "Close", children: "\u2715" })
|
|
95
|
+
] }),
|
|
96
|
+
children
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
)
|
|
92
100
|
}
|
|
93
|
-
)
|
|
101
|
+
);
|
|
94
102
|
}
|
|
95
103
|
|
|
96
104
|
// src/hooks/useQRScanner.ts
|
|
97
105
|
var import_react3 = require("react");
|
|
98
|
-
var
|
|
106
|
+
var import_jsqr = __toESM(require("jsqr"), 1);
|
|
99
107
|
|
|
100
108
|
// src/hooks/useURDecoder.ts
|
|
101
109
|
var import_react2 = require("react");
|
|
102
|
-
var import_bc_ur = require("@
|
|
110
|
+
var import_bc_ur = require("@qrkit/bc-ur");
|
|
103
111
|
function useURDecoder({ onScan }) {
|
|
104
|
-
const decoderRef = (0, import_react2.useRef)(new import_bc_ur.
|
|
112
|
+
const decoderRef = (0, import_react2.useRef)(new import_bc_ur.UrFountainDecoder());
|
|
105
113
|
const onScanRef = (0, import_react2.useRef)(onScan);
|
|
106
114
|
const [progress, setProgress] = (0, import_react2.useState)(null);
|
|
107
115
|
onScanRef.current = onScan;
|
|
108
116
|
const reset = (0, import_react2.useCallback)(() => {
|
|
109
|
-
decoderRef.current = new import_bc_ur.
|
|
117
|
+
decoderRef.current = new import_bc_ur.UrFountainDecoder();
|
|
110
118
|
setProgress(null);
|
|
111
119
|
}, []);
|
|
112
120
|
const receivePart = (0, import_react2.useCallback)(
|
|
@@ -114,11 +122,11 @@ function useURDecoder({ onScan }) {
|
|
|
114
122
|
if (!data.toLowerCase().startsWith("ur:")) {
|
|
115
123
|
return onScanRef.current(data) !== false;
|
|
116
124
|
}
|
|
117
|
-
decoderRef.current.
|
|
125
|
+
decoderRef.current.receivePartUr(data.toLowerCase());
|
|
118
126
|
setProgress(Math.round(decoderRef.current.estimatedPercentComplete() * 100));
|
|
119
127
|
if (!decoderRef.current.isComplete()) return false;
|
|
120
|
-
const ur = decoderRef.current.
|
|
121
|
-
const scanned = { type: ur.type, cbor:
|
|
128
|
+
const ur = decoderRef.current.resultUr;
|
|
129
|
+
const scanned = { type: ur.type, cbor: ur.getPayloadCbor() };
|
|
122
130
|
if (onScanRef.current(scanned) !== false) return true;
|
|
123
131
|
reset();
|
|
124
132
|
return false;
|
|
@@ -134,37 +142,67 @@ function useQRScanner({
|
|
|
134
142
|
enabled = true
|
|
135
143
|
}) {
|
|
136
144
|
const videoRef = (0, import_react3.useRef)(null);
|
|
137
|
-
const
|
|
145
|
+
const rafRef = (0, import_react3.useRef)(null);
|
|
146
|
+
const canvasRef = (0, import_react3.useRef)(null);
|
|
147
|
+
const doneRef = (0, import_react3.useRef)(false);
|
|
138
148
|
const [error, setError] = (0, import_react3.useState)(null);
|
|
139
149
|
const { receivePart, progress } = useURDecoder({ onScan });
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
150
|
+
const receivePartRef = (0, import_react3.useRef)(receivePart);
|
|
151
|
+
receivePartRef.current = receivePart;
|
|
152
|
+
const processFrame = (0, import_react3.useCallback)(() => {
|
|
153
|
+
const video = videoRef.current;
|
|
154
|
+
const canvas = canvasRef.current;
|
|
155
|
+
if (!video || !canvas || doneRef.current) return;
|
|
156
|
+
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
|
157
|
+
if (!ctx) return;
|
|
158
|
+
if (video.readyState === video.HAVE_ENOUGH_DATA) {
|
|
159
|
+
canvas.width = video.videoWidth;
|
|
160
|
+
canvas.height = video.videoHeight;
|
|
161
|
+
ctx.drawImage(video, 0, 0);
|
|
162
|
+
let imageData;
|
|
163
|
+
try {
|
|
164
|
+
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
165
|
+
} catch {
|
|
166
|
+
setError(
|
|
167
|
+
"Canvas access blocked. Disable fingerprinting protection for this site to scan QR codes."
|
|
168
|
+
);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
{
|
|
172
|
+
const code = (0, import_jsqr.default)(imageData.data, imageData.width, imageData.height);
|
|
173
|
+
if (code) {
|
|
174
|
+
const done = receivePartRef.current(code.data);
|
|
175
|
+
if (done) {
|
|
176
|
+
doneRef.current = true;
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
rafRef.current = requestAnimationFrame(processFrame);
|
|
183
|
+
}, []);
|
|
147
184
|
(0, import_react3.useEffect)(() => {
|
|
148
185
|
if (!enabled || !videoRef.current) return;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
186
|
+
doneRef.current = false;
|
|
187
|
+
canvasRef.current = document.createElement("canvas");
|
|
188
|
+
let stream = null;
|
|
189
|
+
navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } }).then((s) => {
|
|
190
|
+
stream = s;
|
|
191
|
+
if (videoRef.current) {
|
|
192
|
+
videoRef.current.srcObject = stream;
|
|
193
|
+
videoRef.current.play().catch(() => {
|
|
194
|
+
});
|
|
195
|
+
rafRef.current = requestAnimationFrame(processFrame);
|
|
156
196
|
}
|
|
157
|
-
)
|
|
158
|
-
scannerRef.current = scanner;
|
|
159
|
-
scanner.start().catch(() => {
|
|
197
|
+
}).catch(() => {
|
|
160
198
|
setError("Camera access denied. Please allow camera permissions.");
|
|
161
199
|
});
|
|
162
200
|
return () => {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
201
|
+
if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
|
|
202
|
+
stream?.getTracks().forEach((t) => t.stop());
|
|
203
|
+
canvasRef.current = null;
|
|
166
204
|
};
|
|
167
|
-
}, [enabled,
|
|
205
|
+
}, [enabled, processFrame]);
|
|
168
206
|
return { videoRef, progress, error };
|
|
169
207
|
}
|
|
170
208
|
|
|
@@ -187,7 +225,14 @@ function QRScanner({ onScan, hint, enabled = true, className }) {
|
|
|
187
225
|
progress,
|
|
188
226
|
"%"
|
|
189
227
|
] }),
|
|
190
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
228
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
229
|
+
"p",
|
|
230
|
+
{
|
|
231
|
+
className: "qrkit-hint",
|
|
232
|
+
style: { position: "absolute", bottom: 8, left: 0, right: 0 },
|
|
233
|
+
children: progress !== null && progress < 100 ? "Keep scanning \u2014 animated QR in progress\u2026" : hint ?? "Point camera at the QR code"
|
|
234
|
+
}
|
|
235
|
+
)
|
|
191
236
|
] });
|
|
192
237
|
}
|
|
193
238
|
|
|
@@ -324,7 +369,14 @@ function SignModal({ request, appName, onSign, onReject }) {
|
|
|
324
369
|
step === "scan" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
|
|
325
370
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "qrkit-step", children: "On your hardware wallet, show the signature QR and point it at this camera." }),
|
|
326
371
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(QRScanner, { onScan: handleScan, hint: "Scan the wallet's signature QR code" }),
|
|
327
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
372
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
373
|
+
"button",
|
|
374
|
+
{
|
|
375
|
+
className: "qrkit-btn qrkit-btn-ghost",
|
|
376
|
+
onClick: () => setStep("display"),
|
|
377
|
+
children: "\u2190 Back"
|
|
378
|
+
}
|
|
379
|
+
)
|
|
328
380
|
] })
|
|
329
381
|
]
|
|
330
382
|
}
|
|
@@ -349,7 +401,11 @@ function buildThemeStyle(theme) {
|
|
|
349
401
|
${declarations}
|
|
350
402
|
}` : "";
|
|
351
403
|
}
|
|
352
|
-
function QRKitProvider({
|
|
404
|
+
function QRKitProvider({
|
|
405
|
+
children,
|
|
406
|
+
theme = {},
|
|
407
|
+
appName = "qrkit"
|
|
408
|
+
}) {
|
|
353
409
|
const [account, setAccount] = (0, import_react8.useState)(null);
|
|
354
410
|
const [connectOpen, setConnectOpen] = (0, import_react8.useState)(false);
|
|
355
411
|
const [pendingSign, setPendingSign] = (0, import_react8.useState)(null);
|
|
@@ -393,7 +449,13 @@ function QRKitProvider({ children, theme = {}, appName = "qrkit" }) {
|
|
|
393
449
|
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(QRKitContext.Provider, { value, children: [
|
|
394
450
|
children,
|
|
395
451
|
connectOpen && (0, import_react_dom.createPortal)(
|
|
396
|
-
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
452
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
453
|
+
ConnectModal,
|
|
454
|
+
{
|
|
455
|
+
onConnect: handleConnect,
|
|
456
|
+
onClose: () => setConnectOpen(false)
|
|
457
|
+
}
|
|
458
|
+
),
|
|
397
459
|
document.body
|
|
398
460
|
),
|
|
399
461
|
pendingSign && (0, import_react_dom.createPortal)(
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../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":["// Context + provider\nexport { QRKitProvider, useQRKit } from \"./context.js\";\n\n// Modals\nexport { ConnectModal } from \"./components/ConnectModal.js\";\nexport { SignModal } from \"./components/SignModal.js\";\n\n// Primitive components\nexport { QRScanner } from \"./components/QRScanner.js\";\nexport { QRDisplay } from \"./components/QRDisplay.js\";\n\n// Low-level hooks\nexport { useQRScanner } from \"./hooks/useQRScanner.js\";\nexport { useQRDisplay } from \"./hooks/useQRDisplay.js\";\nexport { useURDecoder } from \"./hooks/useURDecoder.js\";\nexport { useQRParts } from \"./hooks/useQRParts.js\";\n\n// Types\nexport type {\n QRKitTheme,\n SignRequest,\n QRKitContextValue,\n QRKitProviderProps,\n} from \"./types.js\";\nexport type { QRScannerProps } from \"./components/QRScanner.js\";\nexport type { QRDisplayProps } from \"./components/QRDisplay.js\";\nexport type { ConnectModalProps } from \"./components/ConnectModal.js\";\nexport type { SignModalProps } from \"./components/SignModal.js\";\nexport type { UseQRScannerOptions, UseQRScannerResult } from \"./hooks/useQRScanner.js\";\nexport type { UseQRDisplayOptions, UseQRDisplayResult } from \"./hooks/useQRDisplay.js\";\nexport type { UseURDecoderOptions, UseURDecoderResult } from \"./hooks/useURDecoder.js\";\nexport type { UseQRPartsOptions, UseQRPartsResult } from \"./hooks/useQRParts.js\";\n","import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } 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 { QRKitContextValue, QRKitProviderProps, QRKitTheme, SignRequest } 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({ children, theme = {}, appName = \"qrkit\" }: 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 onConnect={handleConnect} onClose={() => setConnectOpen(false)} />,\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 the\n 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\";\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: true,\n onDeactivate: onClose,\n allowOutsideClick: true,\n })\n : null;\n\n trap?.activate();\n return () => { trap?.deactivate(); };\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 className=\"qrkit qrkit-backdrop\" onClick={onClose} role=\"dialog\" aria-modal=\"true\">\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 QrScanner from \"qr-scanner\";\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 scannerRef = useRef<QrScanner | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const { receivePart, progress } = useURDecoder({ onScan });\n\n const processResult = useCallback(\n (data: string, scanner: QrScanner): void => {\n const done = receivePart(data);\n if (done) scanner.stop();\n },\n [receivePart],\n );\n\n useEffect(() => {\n if (!enabled || !videoRef.current) return;\n\n const scanner = new QrScanner(\n videoRef.current,\n (result) => processResult(result.data, scanner),\n {\n preferredCamera: \"environment\",\n highlightScanRegion: false,\n highlightCodeOutline: false,\n },\n );\n\n scannerRef.current = scanner;\n\n scanner.start().catch(() => {\n setError(\"Camera access denied. Please allow camera permissions.\");\n });\n\n return () => {\n scanner.stop();\n scanner.destroy();\n scannerRef.current = null;\n };\n }, [enabled, processResult]);\n\n return { videoRef, progress, error };\n}\n","import { useCallback, useRef, useState } from \"react\";\n\nimport { URDecoder } from \"@ngraveio/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<URDecoder>(new URDecoder());\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 URDecoder();\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.receivePart(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: new Uint8Array(ur.cbor) };\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 <div className={`qrkit-scanner-error${className ? ` ${className}` : \"\"}`}>{error}</div>;\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 className=\"qrkit-hint\" style={{ position: \"absolute\", bottom: 8, left: 0, right: 0 }}>\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 className=\"qrkit-btn qrkit-btn-ghost\" onClick={() => setStep(\"display\")}>\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":["import_react","import_react","import_react","import_react","QrScanner","import_jsx_runtime","import_jsx_runtime","import_react","import_core","import_react","import_react","QRCode","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../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":["// Context + provider\nexport { QRKitProvider, useQRKit } from \"./context.js\";\n\n// Modals\nexport { ConnectModal } from \"./components/ConnectModal.js\";\nexport { SignModal } from \"./components/SignModal.js\";\n\n// Primitive components\nexport { QRScanner } from \"./components/QRScanner.js\";\nexport { QRDisplay } from \"./components/QRDisplay.js\";\n\n// Low-level hooks\nexport { useQRScanner } from \"./hooks/useQRScanner.js\";\nexport { useQRDisplay } from \"./hooks/useQRDisplay.js\";\nexport { useURDecoder } from \"./hooks/useURDecoder.js\";\nexport { useQRParts } from \"./hooks/useQRParts.js\";\n\n// Types\nexport type {\n QRKitTheme,\n SignRequest,\n QRKitContextValue,\n QRKitProviderProps,\n} from \"./types.js\";\nexport type { QRScannerProps } from \"./components/QRScanner.js\";\nexport type { QRDisplayProps } from \"./components/QRDisplay.js\";\nexport type { ConnectModalProps } from \"./components/ConnectModal.js\";\nexport type { SignModalProps } from \"./components/SignModal.js\";\nexport type { UseQRScannerOptions, UseQRScannerResult } from \"./hooks/useQRScanner.js\";\nexport type { UseQRDisplayOptions, UseQRDisplayResult } from \"./hooks/useQRDisplay.js\";\nexport type { UseURDecoderOptions, UseURDecoderResult } from \"./hooks/useURDecoder.js\";\nexport type { UseQRPartsOptions, UseQRPartsResult } from \"./hooks/useQRParts.js\";\n","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":["import_react","import_react","import_react","import_react","jsQR","import_jsx_runtime","import_jsx_runtime","import_react","import_core","import_react","import_react","QRCode","import_jsx_runtime","import_jsx_runtime","import_jsx_runtime"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -36,7 +36,7 @@ interface QRKitProviderProps {
|
|
|
36
36
|
appName?: string;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
declare function QRKitProvider({ children, theme, appName }: QRKitProviderProps): react_jsx_runtime.JSX.Element;
|
|
39
|
+
declare function QRKitProvider({ children, theme, appName, }: QRKitProviderProps): react_jsx_runtime.JSX.Element;
|
|
40
40
|
declare function useQRKit(): QRKitContextValue;
|
|
41
41
|
|
|
42
42
|
interface ConnectModalProps {
|
package/dist/index.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ interface QRKitProviderProps {
|
|
|
36
36
|
appName?: string;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
declare function QRKitProvider({ children, theme, appName }: QRKitProviderProps): react_jsx_runtime.JSX.Element;
|
|
39
|
+
declare function QRKitProvider({ children, theme, appName, }: QRKitProviderProps): react_jsx_runtime.JSX.Element;
|
|
40
40
|
declare function useQRKit(): QRKitContextValue;
|
|
41
41
|
|
|
42
42
|
interface ConnectModalProps {
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
// src/context.tsx
|
|
2
|
-
import {
|
|
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";
|
|
3
11
|
import { createPortal } from "react-dom";
|
|
4
12
|
|
|
5
13
|
// src/components/ConnectModal.tsx
|
|
@@ -15,8 +23,7 @@ function Modal({ title, onClose, children, className }) {
|
|
|
15
23
|
useEffect(() => {
|
|
16
24
|
const el = containerRef.current;
|
|
17
25
|
const trap = el ? createFocusTrap(el, {
|
|
18
|
-
escapeDeactivates:
|
|
19
|
-
onDeactivate: onClose,
|
|
26
|
+
escapeDeactivates: false,
|
|
20
27
|
allowOutsideClick: true
|
|
21
28
|
}) : null;
|
|
22
29
|
trap?.activate();
|
|
@@ -31,37 +38,46 @@ function Modal({ title, onClose, children, className }) {
|
|
|
31
38
|
document.addEventListener("keydown", handleKey);
|
|
32
39
|
return () => document.removeEventListener("keydown", handleKey);
|
|
33
40
|
}, [onClose]);
|
|
34
|
-
return /* @__PURE__ */ jsx(
|
|
41
|
+
return /* @__PURE__ */ jsx(
|
|
35
42
|
"div",
|
|
36
43
|
{
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
+
)
|
|
47
63
|
}
|
|
48
|
-
)
|
|
64
|
+
);
|
|
49
65
|
}
|
|
50
66
|
|
|
51
67
|
// src/hooks/useQRScanner.ts
|
|
52
68
|
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef3, useState as useState2 } from "react";
|
|
53
|
-
import
|
|
69
|
+
import jsQR from "jsqr";
|
|
54
70
|
|
|
55
71
|
// src/hooks/useURDecoder.ts
|
|
56
72
|
import { useCallback, useRef as useRef2, useState } from "react";
|
|
57
|
-
import {
|
|
73
|
+
import { UrFountainDecoder } from "@qrkit/bc-ur";
|
|
58
74
|
function useURDecoder({ onScan }) {
|
|
59
|
-
const decoderRef = useRef2(new
|
|
75
|
+
const decoderRef = useRef2(new UrFountainDecoder());
|
|
60
76
|
const onScanRef = useRef2(onScan);
|
|
61
77
|
const [progress, setProgress] = useState(null);
|
|
62
78
|
onScanRef.current = onScan;
|
|
63
79
|
const reset = useCallback(() => {
|
|
64
|
-
decoderRef.current = new
|
|
80
|
+
decoderRef.current = new UrFountainDecoder();
|
|
65
81
|
setProgress(null);
|
|
66
82
|
}, []);
|
|
67
83
|
const receivePart = useCallback(
|
|
@@ -69,11 +85,11 @@ function useURDecoder({ onScan }) {
|
|
|
69
85
|
if (!data.toLowerCase().startsWith("ur:")) {
|
|
70
86
|
return onScanRef.current(data) !== false;
|
|
71
87
|
}
|
|
72
|
-
decoderRef.current.
|
|
88
|
+
decoderRef.current.receivePartUr(data.toLowerCase());
|
|
73
89
|
setProgress(Math.round(decoderRef.current.estimatedPercentComplete() * 100));
|
|
74
90
|
if (!decoderRef.current.isComplete()) return false;
|
|
75
|
-
const ur = decoderRef.current.
|
|
76
|
-
const scanned = { type: ur.type, cbor:
|
|
91
|
+
const ur = decoderRef.current.resultUr;
|
|
92
|
+
const scanned = { type: ur.type, cbor: ur.getPayloadCbor() };
|
|
77
93
|
if (onScanRef.current(scanned) !== false) return true;
|
|
78
94
|
reset();
|
|
79
95
|
return false;
|
|
@@ -89,37 +105,67 @@ function useQRScanner({
|
|
|
89
105
|
enabled = true
|
|
90
106
|
}) {
|
|
91
107
|
const videoRef = useRef3(null);
|
|
92
|
-
const
|
|
108
|
+
const rafRef = useRef3(null);
|
|
109
|
+
const canvasRef = useRef3(null);
|
|
110
|
+
const doneRef = useRef3(false);
|
|
93
111
|
const [error, setError] = useState2(null);
|
|
94
112
|
const { receivePart, progress } = useURDecoder({ onScan });
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
+
}, []);
|
|
102
147
|
useEffect2(() => {
|
|
103
148
|
if (!enabled || !videoRef.current) return;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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);
|
|
111
159
|
}
|
|
112
|
-
)
|
|
113
|
-
scannerRef.current = scanner;
|
|
114
|
-
scanner.start().catch(() => {
|
|
160
|
+
}).catch(() => {
|
|
115
161
|
setError("Camera access denied. Please allow camera permissions.");
|
|
116
162
|
});
|
|
117
163
|
return () => {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
164
|
+
if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
|
|
165
|
+
stream?.getTracks().forEach((t) => t.stop());
|
|
166
|
+
canvasRef.current = null;
|
|
121
167
|
};
|
|
122
|
-
}, [enabled,
|
|
168
|
+
}, [enabled, processFrame]);
|
|
123
169
|
return { videoRef, progress, error };
|
|
124
170
|
}
|
|
125
171
|
|
|
@@ -142,7 +188,14 @@ function QRScanner({ onScan, hint, enabled = true, className }) {
|
|
|
142
188
|
progress,
|
|
143
189
|
"%"
|
|
144
190
|
] }),
|
|
145
|
-
/* @__PURE__ */ jsx2(
|
|
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
|
+
)
|
|
146
199
|
] });
|
|
147
200
|
}
|
|
148
201
|
|
|
@@ -279,7 +332,14 @@ function SignModal({ request, appName, onSign, onReject }) {
|
|
|
279
332
|
step === "scan" && /* @__PURE__ */ jsxs5(Fragment, { children: [
|
|
280
333
|
/* @__PURE__ */ jsx5("p", { className: "qrkit-step", children: "On your hardware wallet, show the signature QR and point it at this camera." }),
|
|
281
334
|
/* @__PURE__ */ jsx5(QRScanner, { onScan: handleScan, hint: "Scan the wallet's signature QR code" }),
|
|
282
|
-
/* @__PURE__ */ jsx5(
|
|
335
|
+
/* @__PURE__ */ jsx5(
|
|
336
|
+
"button",
|
|
337
|
+
{
|
|
338
|
+
className: "qrkit-btn qrkit-btn-ghost",
|
|
339
|
+
onClick: () => setStep("display"),
|
|
340
|
+
children: "\u2190 Back"
|
|
341
|
+
}
|
|
342
|
+
)
|
|
283
343
|
] })
|
|
284
344
|
]
|
|
285
345
|
}
|
|
@@ -304,7 +364,11 @@ function buildThemeStyle(theme) {
|
|
|
304
364
|
${declarations}
|
|
305
365
|
}` : "";
|
|
306
366
|
}
|
|
307
|
-
function QRKitProvider({
|
|
367
|
+
function QRKitProvider({
|
|
368
|
+
children,
|
|
369
|
+
theme = {},
|
|
370
|
+
appName = "qrkit"
|
|
371
|
+
}) {
|
|
308
372
|
const [account, setAccount] = useState5(null);
|
|
309
373
|
const [connectOpen, setConnectOpen] = useState5(false);
|
|
310
374
|
const [pendingSign, setPendingSign] = useState5(null);
|
|
@@ -348,7 +412,13 @@ function QRKitProvider({ children, theme = {}, appName = "qrkit" }) {
|
|
|
348
412
|
return /* @__PURE__ */ jsxs6(QRKitContext.Provider, { value, children: [
|
|
349
413
|
children,
|
|
350
414
|
connectOpen && createPortal(
|
|
351
|
-
/* @__PURE__ */ jsx6(
|
|
415
|
+
/* @__PURE__ */ jsx6(
|
|
416
|
+
ConnectModal,
|
|
417
|
+
{
|
|
418
|
+
onConnect: handleConnect,
|
|
419
|
+
onClose: () => setConnectOpen(false)
|
|
420
|
+
}
|
|
421
|
+
),
|
|
352
422
|
document.body
|
|
353
423
|
),
|
|
354
424
|
pendingSign && createPortal(
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } 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 { QRKitContextValue, QRKitProviderProps, QRKitTheme, SignRequest } 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({ children, theme = {}, appName = \"qrkit\" }: 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 onConnect={handleConnect} onClose={() => setConnectOpen(false)} />,\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 the\n 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\";\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: true,\n onDeactivate: onClose,\n allowOutsideClick: true,\n })\n : null;\n\n trap?.activate();\n return () => { trap?.deactivate(); };\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 className=\"qrkit qrkit-backdrop\" onClick={onClose} role=\"dialog\" aria-modal=\"true\">\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 QrScanner from \"qr-scanner\";\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 scannerRef = useRef<QrScanner | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const { receivePart, progress } = useURDecoder({ onScan });\n\n const processResult = useCallback(\n (data: string, scanner: QrScanner): void => {\n const done = receivePart(data);\n if (done) scanner.stop();\n },\n [receivePart],\n );\n\n useEffect(() => {\n if (!enabled || !videoRef.current) return;\n\n const scanner = new QrScanner(\n videoRef.current,\n (result) => processResult(result.data, scanner),\n {\n preferredCamera: \"environment\",\n highlightScanRegion: false,\n highlightCodeOutline: false,\n },\n );\n\n scannerRef.current = scanner;\n\n scanner.start().catch(() => {\n setError(\"Camera access denied. Please allow camera permissions.\");\n });\n\n return () => {\n scanner.stop();\n scanner.destroy();\n scannerRef.current = null;\n };\n }, [enabled, processResult]);\n\n return { videoRef, progress, error };\n}\n","import { useCallback, useRef, useState } from \"react\";\n\nimport { URDecoder } from \"@ngraveio/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<URDecoder>(new URDecoder());\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 URDecoder();\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.receivePart(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: new Uint8Array(ur.cbor) };\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 <div className={`qrkit-scanner-error${className ? ` ${className}` : \"\"}`}>{error}</div>;\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 className=\"qrkit-hint\" style={{ position: \"absolute\", bottom: 8, left: 0, right: 0 }}>\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 className=\"qrkit-btn qrkit-btn-ghost\" onClick={() => setStep(\"display\")}>\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"]}
|
|
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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qrkit/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "React context, hooks, and drop-in components for QR-based airgapped wallet flows.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@
|
|
22
|
+
"@qrkit/bc-ur": "2.0.0-beta.9-qrkit.2",
|
|
23
23
|
"focus-trap": "^8.0.1",
|
|
24
|
-
"
|
|
24
|
+
"jsqr": "^1.4.0",
|
|
25
25
|
"qrcode": "^1.5.4",
|
|
26
|
-
"@qrkit/core": "0.
|
|
26
|
+
"@qrkit/core": "0.2.0"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"react": ">=18",
|