@qrkit/react 0.0.1 → 0.1.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  React context, hooks, and drop-in components for QR-based airgapped wallet flows.
4
4
 
5
- Builds on [`@qrkit/core`](../core). Provides a provider/context model, ready-to-use hooks, and optional drop-in components for connection scanning, account display, and message signing.
5
+ Builds on [`@qrkit/core`](../core). Provides a provider/context, ready-to-use modals, and composable hooks for connection scanning and transaction signing.
6
6
 
7
7
  ## Install
8
8
 
@@ -10,43 +10,139 @@ Builds on [`@qrkit/core`](../core). Provides a provider/context model, ready-to-
10
10
  pnpm add @qrkit/react
11
11
  ```
12
12
 
13
+ Import the default styles (or bring your own):
14
+
15
+ ```ts
16
+ import '@qrkit/react/styles.css'
17
+ ```
18
+
13
19
  React 18+ is required as a peer dependency.
14
20
 
15
- ## Planned API
21
+ ## Quick start — drop-in components
16
22
 
17
- ### Provider
23
+ Wrap your app in `QRKitProvider` and call `connect()` / `sign()` from anywhere. The modals appear automatically.
18
24
 
19
25
  ```tsx
20
- import { QRKitProvider } from '@qrkit/react'
26
+ import { QRKitProvider, useQRKit } from '@qrkit/react'
27
+ import '@qrkit/react/styles.css'
28
+
29
+ export function App() {
30
+ return (
31
+ <QRKitProvider appName="My dApp">
32
+ <Wallet />
33
+ </QRKitProvider>
34
+ )
35
+ }
36
+
37
+ function Wallet() {
38
+ const { account, connect, disconnect, sign } = useQRKit()
39
+
40
+ async function handleSign() {
41
+ if (!account) return
42
+ const sig = await sign({
43
+ message: 'Hello from My dApp',
44
+ address: account.address,
45
+ sourceFingerprint: account.chain === 'evm' ? account.sourceFingerprint : undefined,
46
+ })
47
+ console.log('Signature:', sig)
48
+ }
49
+
50
+ if (!account) return <button onClick={connect}>Connect wallet</button>
51
+
52
+ return (
53
+ <div>
54
+ <p>{account.address}</p>
55
+ <button onClick={handleSign}>Sign message</button>
56
+ <button onClick={disconnect}>Disconnect</button>
57
+ </div>
58
+ )
59
+ }
60
+ ```
61
+
62
+ ## Theming
63
+
64
+ Pass a `theme` prop to override CSS variables. Defaults follow Material Design 3 and automatically adapt to light/dark system preference.
21
65
 
22
- <QRKitProvider config={{ appName: 'My dApp', chains: ['evm'] }}>
23
- <App />
66
+ ```tsx
67
+ <QRKitProvider theme={{ accent: '#ff6b00', radius: '8px' }}>
68
+ ...
24
69
  </QRKitProvider>
25
70
  ```
26
71
 
27
- ### Hooks
72
+ Or override in CSS directly:
28
73
 
29
- ```ts
30
- import { useQRKit, useQRKitConnect, useQRKitSignMessage } from '@qrkit/react'
74
+ ```css
75
+ .qrkit {
76
+ --qrkit-accent: #ff6b00;
77
+ --qrkit-radius: 8px;
78
+ }
79
+ ```
80
+
81
+ | Variable | Default (light) | Default (dark) |
82
+ |---|---|---|
83
+ | `--qrkit-accent` | `#6750A4` | `#D0BCFF` |
84
+ | `--qrkit-bg` | `#FFFBFE` | `#1C1B1F` |
85
+ | `--qrkit-text` | `#1C1B1F` | `#E6E1E5` |
86
+ | `--qrkit-text-muted` | `#49454F` | `#CAC4D0` |
87
+ | `--qrkit-radius` | `12px` | `12px` |
88
+
89
+ ## Low-level hooks
90
+
91
+ ### Batteries-included
92
+
93
+ Use `useQRScanner` and `useQRDisplay` when you want custom layouts but keep the built-in camera and QR rendering libraries.
31
94
 
32
- const { status, session, accounts, disconnect } = useQRKit()
33
- const { startConnectionScan } = useQRKitConnect()
34
- const { request, createMessageRequest, parseSignatureResponse } = useQRKitSignMessage()
95
+ ```tsx
96
+ import { useQRScanner, useQRDisplay } from '@qrkit/react'
97
+
98
+ const { videoRef, progress, error } = useQRScanner({ onScan, enabled })
99
+ const { canvasRef, frame, total } = useQRDisplay({ parts })
35
100
  ```
36
101
 
37
- ### Drop-in components
102
+ ### Bring your own scanner / renderer
103
+
104
+ Use `useURDecoder` and `useQRParts` to plug in any scanning or rendering library.
38
105
 
39
106
  ```tsx
40
- import { QRKitConnect, QRKitAccounts, QRKitSignMessage } from '@qrkit/react'
107
+ import { useURDecoder, useQRParts } from '@qrkit/react'
108
+
109
+ // Feed raw QR strings from any source
110
+ const { receivePart, progress } = useURDecoder({ onScan })
111
+
112
+ // Get the current part string to render with any library
113
+ const { part, frame, total } = useQRParts({ parts })
114
+ ```
115
+
116
+ ## API
41
117
 
42
- // Scan a connection QR
43
- <QRKitConnect onConnected={(session) => console.log(session)} />
118
+ ### `QRKitProvider`
44
119
 
45
- // Display derived accounts
46
- <QRKitAccounts />
120
+ | Prop | Type | Default |
121
+ |---|---|---|
122
+ | `appName` | `string` | `"qrkit"` |
123
+ | `theme` | `QRKitTheme` | MD3 defaults |
47
124
 
48
- // Full sign message flow
49
- <QRKitSignMessage message="hello" onSigned={(result) => console.log(result.signature)} />
125
+ ### `useQRKit`
126
+
127
+ ```ts
128
+ const { account, connect, disconnect, sign } = useQRKit()
129
+ ```
130
+
131
+ | | Type |
132
+ |---|---|
133
+ | `account` | `Account \| null` |
134
+ | `connect` | `() => void` |
135
+ | `disconnect` | `() => void` |
136
+ | `sign` | `(request: SignRequest) => Promise<string>` |
137
+
138
+ ### `SignRequest`
139
+
140
+ ```ts
141
+ interface SignRequest {
142
+ message: string
143
+ address: string
144
+ sourceFingerprint: number | undefined
145
+ }
50
146
  ```
51
147
 
52
148
  ## License
package/dist/index.cjs CHANGED
@@ -1,2 +1,431 @@
1
1
  "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ConnectModal: () => ConnectModal,
34
+ QRDisplay: () => QRDisplay,
35
+ QRKitProvider: () => QRKitProvider,
36
+ QRScanner: () => QRScanner,
37
+ SignModal: () => SignModal,
38
+ useQRDisplay: () => useQRDisplay,
39
+ useQRKit: () => useQRKit,
40
+ useQRParts: () => useQRParts,
41
+ useQRScanner: () => useQRScanner,
42
+ useURDecoder: () => useURDecoder
43
+ });
44
+ module.exports = __toCommonJS(index_exports);
45
+
46
+ // src/context.tsx
47
+ var import_react8 = require("react");
48
+ var import_react_dom = require("react-dom");
49
+
50
+ // src/components/ConnectModal.tsx
51
+ var import_react4 = require("react");
52
+ var import_core = require("@qrkit/core");
53
+
54
+ // src/components/Modal.tsx
55
+ var import_react = require("react");
56
+ var import_focus_trap = require("focus-trap");
57
+ var import_jsx_runtime = require("react/jsx-runtime");
58
+ function Modal({ title, onClose, children, className }) {
59
+ const containerRef = (0, import_react.useRef)(null);
60
+ (0, import_react.useEffect)(() => {
61
+ const el = containerRef.current;
62
+ const trap = el ? (0, import_focus_trap.createFocusTrap)(el, {
63
+ escapeDeactivates: true,
64
+ onDeactivate: onClose,
65
+ allowOutsideClick: true
66
+ }) : null;
67
+ trap?.activate();
68
+ return () => {
69
+ trap?.deactivate();
70
+ };
71
+ }, [onClose]);
72
+ (0, import_react.useEffect)(() => {
73
+ const handleKey = (e) => {
74
+ if (e.key === "Escape") onClose();
75
+ };
76
+ document.addEventListener("keydown", handleKey);
77
+ return () => document.removeEventListener("keydown", handleKey);
78
+ }, [onClose]);
79
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "qrkit qrkit-backdrop", onClick: onClose, role: "dialog", "aria-modal": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
80
+ "div",
81
+ {
82
+ ref: containerRef,
83
+ className: `qrkit-modal${className ? ` ${className}` : ""}`,
84
+ onClick: (e) => e.stopPropagation(),
85
+ children: [
86
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "qrkit-modal-header", children: [
87
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "qrkit-modal-title", children: title }),
88
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { className: "qrkit-close-btn", onClick: onClose, "aria-label": "Close", children: "\u2715" })
89
+ ] }),
90
+ children
91
+ ]
92
+ }
93
+ ) });
94
+ }
95
+
96
+ // src/hooks/useQRScanner.ts
97
+ var import_react3 = require("react");
98
+ var import_qr_scanner = __toESM(require("qr-scanner"), 1);
99
+
100
+ // src/hooks/useURDecoder.ts
101
+ var import_react2 = require("react");
102
+ var import_bc_ur = require("@ngraveio/bc-ur");
103
+ function useURDecoder({ onScan }) {
104
+ const decoderRef = (0, import_react2.useRef)(new import_bc_ur.URDecoder());
105
+ const onScanRef = (0, import_react2.useRef)(onScan);
106
+ const [progress, setProgress] = (0, import_react2.useState)(null);
107
+ onScanRef.current = onScan;
108
+ const reset = (0, import_react2.useCallback)(() => {
109
+ decoderRef.current = new import_bc_ur.URDecoder();
110
+ setProgress(null);
111
+ }, []);
112
+ const receivePart = (0, import_react2.useCallback)(
113
+ (data) => {
114
+ if (!data.toLowerCase().startsWith("ur:")) {
115
+ return onScanRef.current(data) !== false;
116
+ }
117
+ decoderRef.current.receivePart(data.toLowerCase());
118
+ setProgress(Math.round(decoderRef.current.estimatedPercentComplete() * 100));
119
+ if (!decoderRef.current.isComplete()) return false;
120
+ const ur = decoderRef.current.resultUR();
121
+ const scanned = { type: ur.type, cbor: new Uint8Array(ur.cbor) };
122
+ if (onScanRef.current(scanned) !== false) return true;
123
+ reset();
124
+ return false;
125
+ },
126
+ [reset]
127
+ );
128
+ return { receivePart, progress, reset };
129
+ }
130
+
131
+ // src/hooks/useQRScanner.ts
132
+ function useQRScanner({
133
+ onScan,
134
+ enabled = true
135
+ }) {
136
+ const videoRef = (0, import_react3.useRef)(null);
137
+ const scannerRef = (0, import_react3.useRef)(null);
138
+ const [error, setError] = (0, import_react3.useState)(null);
139
+ const { receivePart, progress } = useURDecoder({ onScan });
140
+ const processResult = (0, import_react3.useCallback)(
141
+ (data, scanner) => {
142
+ const done = receivePart(data);
143
+ if (done) scanner.stop();
144
+ },
145
+ [receivePart]
146
+ );
147
+ (0, import_react3.useEffect)(() => {
148
+ if (!enabled || !videoRef.current) return;
149
+ const scanner = new import_qr_scanner.default(
150
+ videoRef.current,
151
+ (result) => processResult(result.data, scanner),
152
+ {
153
+ preferredCamera: "environment",
154
+ highlightScanRegion: false,
155
+ highlightCodeOutline: false
156
+ }
157
+ );
158
+ scannerRef.current = scanner;
159
+ scanner.start().catch(() => {
160
+ setError("Camera access denied. Please allow camera permissions.");
161
+ });
162
+ return () => {
163
+ scanner.stop();
164
+ scanner.destroy();
165
+ scannerRef.current = null;
166
+ };
167
+ }, [enabled, processResult]);
168
+ return { videoRef, progress, error };
169
+ }
170
+
171
+ // src/components/QRScanner.tsx
172
+ var import_jsx_runtime2 = require("react/jsx-runtime");
173
+ function QRScanner({ onScan, hint, enabled = true, className }) {
174
+ const { videoRef, progress, error } = useQRScanner({ onScan, enabled });
175
+ if (error) {
176
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: `qrkit-scanner-error${className ? ` ${className}` : ""}`, children: error });
177
+ }
178
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: `qrkit-scanner-wrap${className ? ` ${className}` : ""}`, children: [
179
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("video", { ref: videoRef, autoPlay: true, playsInline: true, muted: true, className: "qrkit-scanner-video" }),
180
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "qrkit-scanner-overlay", children: [
181
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "qrkit-scanner-corner tl" }),
182
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "qrkit-scanner-corner tr" }),
183
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "qrkit-scanner-corner bl" }),
184
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "qrkit-scanner-corner br" })
185
+ ] }),
186
+ progress !== null && progress < 100 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "qrkit-scanner-progress", children: [
187
+ progress,
188
+ "%"
189
+ ] }),
190
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "qrkit-hint", style: { position: "absolute", bottom: 8, left: 0, right: 0 }, children: progress !== null && progress < 100 ? "Keep scanning \u2014 animated QR in progress\u2026" : hint ?? "Point camera at the QR code" })
191
+ ] });
192
+ }
193
+
194
+ // src/components/ConnectModal.tsx
195
+ var import_jsx_runtime3 = require("react/jsx-runtime");
196
+ function ConnectModal({ onConnect, onClose }) {
197
+ const handleScan = (0, import_react4.useCallback)(
198
+ (data) => {
199
+ try {
200
+ const accounts = (0, import_core.parseConnection)(data, { chains: ["evm"] });
201
+ const account = accounts[0];
202
+ if (!account) return false;
203
+ onConnect(account);
204
+ } catch {
205
+ return false;
206
+ }
207
+ },
208
+ [onConnect]
209
+ );
210
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Modal, { title: "Connect Wallet", onClose, children: [
211
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: "qrkit-step", children: [
212
+ "On your hardware wallet, go to ",
213
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: "Connect software wallet" }),
214
+ " and point the screen at this camera."
215
+ ] }),
216
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(QRScanner, { onScan: handleScan, hint: "Scan the wallet's connection QR code" })
217
+ ] });
218
+ }
219
+
220
+ // src/components/SignModal.tsx
221
+ var import_react7 = require("react");
222
+ var import_core2 = require("@qrkit/core");
223
+
224
+ // src/hooks/useQRDisplay.ts
225
+ var import_react6 = require("react");
226
+ var import_qrcode = __toESM(require("qrcode"), 1);
227
+
228
+ // src/hooks/useQRParts.ts
229
+ var import_react5 = require("react");
230
+ function useQRParts({
231
+ parts,
232
+ interval = 200
233
+ }) {
234
+ const [frame, setFrame] = (0, import_react5.useState)(0);
235
+ const frameRef = (0, import_react5.useRef)(0);
236
+ (0, import_react5.useEffect)(() => {
237
+ frameRef.current = 0;
238
+ setFrame(0);
239
+ }, [parts]);
240
+ (0, import_react5.useEffect)(() => {
241
+ if (parts.length <= 1) return;
242
+ const id = setInterval(() => {
243
+ frameRef.current = (frameRef.current + 1) % parts.length;
244
+ setFrame(frameRef.current);
245
+ }, interval);
246
+ return () => clearInterval(id);
247
+ }, [parts, interval]);
248
+ return {
249
+ part: parts[frameRef.current % Math.max(parts.length, 1)] ?? "",
250
+ frame,
251
+ total: parts.length
252
+ };
253
+ }
254
+
255
+ // src/hooks/useQRDisplay.ts
256
+ function useQRDisplay({
257
+ parts,
258
+ interval,
259
+ size = 300
260
+ }) {
261
+ const canvasRef = (0, import_react6.useRef)(null);
262
+ const { part, frame, total } = useQRParts({ parts, interval });
263
+ (0, import_react6.useEffect)(() => {
264
+ if (!part || !canvasRef.current) return;
265
+ import_qrcode.default.toCanvas(canvasRef.current, part, {
266
+ width: size,
267
+ margin: 2,
268
+ errorCorrectionLevel: "M"
269
+ }).catch(() => {
270
+ });
271
+ }, [part, size]);
272
+ return { canvasRef, frame, total };
273
+ }
274
+
275
+ // src/components/QRDisplay.tsx
276
+ var import_jsx_runtime4 = require("react/jsx-runtime");
277
+ function QRDisplay({ parts, interval, size = 300, className }) {
278
+ const { canvasRef, frame, total } = useQRDisplay({ parts, interval, size });
279
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `qrkit-qr-wrap${className ? ` ${className}` : ""}`, children: [
280
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("canvas", { ref: canvasRef, className: "qrkit-qr-canvas", width: size, height: size }),
281
+ total > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: "qrkit-hint", children: [
282
+ "Frame ",
283
+ frame + 1,
284
+ " / ",
285
+ total,
286
+ " \u2014 keep Shell pointed at the screen"
287
+ ] })
288
+ ] });
289
+ }
290
+
291
+ // src/components/SignModal.tsx
292
+ var import_jsx_runtime5 = require("react/jsx-runtime");
293
+ function SignModal({ request, appName, onSign, onReject }) {
294
+ const [step, setStep] = (0, import_react7.useState)("display");
295
+ const parts = (0, import_core2.buildEthSignRequestURParts)(
296
+ request.message,
297
+ request.address,
298
+ request.sourceFingerprint,
299
+ appName
300
+ );
301
+ const handleScan = (0, import_react7.useCallback)(
302
+ (data) => {
303
+ try {
304
+ const sig = (0, import_core2.parseEthSignature)(data);
305
+ onSign(sig);
306
+ } catch {
307
+ return false;
308
+ }
309
+ },
310
+ [onSign]
311
+ );
312
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
313
+ Modal,
314
+ {
315
+ title: step === "display" ? "Sign Request" : "Scan Signature",
316
+ onClose: onReject,
317
+ children: [
318
+ step === "display" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
319
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "qrkit-step", children: "Point your hardware wallet camera at this QR code to approve the sign request." }),
320
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(QRDisplay, { parts }),
321
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "qrkit-btn qrkit-btn-primary", onClick: () => setStep("scan"), children: "Wallet signed \u2014 scan response" }),
322
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "qrkit-btn qrkit-btn-ghost", onClick: onReject, children: "Cancel" })
323
+ ] }),
324
+ step === "scan" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
325
+ /* @__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
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(QRScanner, { onScan: handleScan, hint: "Scan the wallet's signature QR code" }),
327
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { className: "qrkit-btn qrkit-btn-ghost", onClick: () => setStep("display"), children: "\u2190 Back" })
328
+ ] })
329
+ ]
330
+ }
331
+ );
332
+ }
333
+
334
+ // src/context.tsx
335
+ var import_jsx_runtime6 = require("react/jsx-runtime");
336
+ var QRKitContext = (0, import_react8.createContext)(null);
337
+ function buildThemeStyle(theme) {
338
+ const vars = {
339
+ "--qrkit-accent": theme.accent,
340
+ "--qrkit-bg": theme.background,
341
+ "--qrkit-backdrop": theme.backdrop,
342
+ "--qrkit-text": theme.text,
343
+ "--qrkit-text-muted": theme.textMuted,
344
+ "--qrkit-radius": theme.radius,
345
+ "--qrkit-font": theme.fontFamily
346
+ };
347
+ const declarations = Object.entries(vars).filter(([, v]) => v !== void 0).map(([k, v]) => ` ${k}: ${v};`).join("\n");
348
+ return declarations ? `.qrkit {
349
+ ${declarations}
350
+ }` : "";
351
+ }
352
+ function QRKitProvider({ children, theme = {}, appName = "qrkit" }) {
353
+ const [account, setAccount] = (0, import_react8.useState)(null);
354
+ const [connectOpen, setConnectOpen] = (0, import_react8.useState)(false);
355
+ const [pendingSign, setPendingSign] = (0, import_react8.useState)(null);
356
+ const pendingSignRef = (0, import_react8.useRef)(null);
357
+ const themeStyle = (0, import_react8.useMemo)(() => buildThemeStyle(theme), [theme]);
358
+ (0, import_react8.useEffect)(() => {
359
+ if (!themeStyle) return;
360
+ const el = document.createElement("style");
361
+ el.setAttribute("data-qrkit-theme", "");
362
+ el.textContent = themeStyle;
363
+ document.head.appendChild(el);
364
+ return () => el.remove();
365
+ }, [themeStyle]);
366
+ const connect = (0, import_react8.useCallback)(() => setConnectOpen(true), []);
367
+ const disconnect = (0, import_react8.useCallback)(() => setAccount(null), []);
368
+ const handleConnect = (0, import_react8.useCallback)((acc) => {
369
+ setAccount(acc);
370
+ setConnectOpen(false);
371
+ }, []);
372
+ const sign = (0, import_react8.useCallback)((request) => {
373
+ return new Promise((resolve, reject) => {
374
+ const pending = { request, resolve, reject };
375
+ pendingSignRef.current = pending;
376
+ setPendingSign(pending);
377
+ });
378
+ }, []);
379
+ const handleSign = (0, import_react8.useCallback)((sig) => {
380
+ pendingSignRef.current?.resolve(sig);
381
+ pendingSignRef.current = null;
382
+ setPendingSign(null);
383
+ }, []);
384
+ const handleReject = (0, import_react8.useCallback)(() => {
385
+ pendingSignRef.current?.reject(new Error("User rejected the sign request"));
386
+ pendingSignRef.current = null;
387
+ setPendingSign(null);
388
+ }, []);
389
+ const value = (0, import_react8.useMemo)(
390
+ () => ({ account, connect, disconnect, sign }),
391
+ [account, connect, disconnect, sign]
392
+ );
393
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(QRKitContext.Provider, { value, children: [
394
+ children,
395
+ connectOpen && (0, import_react_dom.createPortal)(
396
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ConnectModal, { onConnect: handleConnect, onClose: () => setConnectOpen(false) }),
397
+ document.body
398
+ ),
399
+ pendingSign && (0, import_react_dom.createPortal)(
400
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
401
+ SignModal,
402
+ {
403
+ request: pendingSign.request,
404
+ appName,
405
+ onSign: handleSign,
406
+ onReject: handleReject
407
+ }
408
+ ),
409
+ document.body
410
+ )
411
+ ] });
412
+ }
413
+ function useQRKit() {
414
+ const ctx = (0, import_react8.useContext)(QRKitContext);
415
+ if (!ctx) throw new Error("useQRKit must be used within a QRKitProvider");
416
+ return ctx;
417
+ }
418
+ // Annotate the CommonJS export names for ESM import in node:
419
+ 0 && (module.exports = {
420
+ ConnectModal,
421
+ QRDisplay,
422
+ QRKitProvider,
423
+ QRScanner,
424
+ SignModal,
425
+ useQRDisplay,
426
+ useQRKit,
427
+ useQRParts,
428
+ useQRScanner,
429
+ useURDecoder
430
+ });
2
431
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
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"]}