@fiber-pay/react 0.2.3 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,13 +1,39 @@
1
- // src/fiber-pay-quick-card.tsx
2
- import { useEffect as useEffect3, useId, useState as useState3 } from "react";
1
+ // src/index.ts
2
+ import {
3
+ ChannelState,
4
+ ConfigBuilder as ConfigBuilder2,
5
+ ckbHash,
6
+ ckbToShannons,
7
+ derivePublicKey,
8
+ FiberBrowserNode as FiberBrowserNode2,
9
+ FiberRpcError,
10
+ formatShannonsAsCkb as formatShannonsAsCkb2,
11
+ fromHex,
12
+ getLockBalanceShannons as getLockBalanceShannons2,
13
+ PasskeyCredentialProvider as PasskeyCredentialProvider2,
14
+ PasswordCredentialProvider as PasswordCredentialProvider2,
15
+ RawKeyCredentialProvider as RawKeyCredentialProvider2,
16
+ scriptToAddress as scriptToAddress2,
17
+ shannonsToCkb,
18
+ toHex
19
+ } from "@fiber-pay/sdk/browser";
20
+
21
+ // src/connect-button.tsx
22
+ import {
23
+ useCallback as useCallback2,
24
+ useEffect as useEffect2,
25
+ useRef as useRef2,
26
+ useState as useState2
27
+ } from "react";
3
28
 
4
29
  // src/use-fiber-node.ts
5
30
  import {
6
31
  FiberBrowserNode,
7
32
  PasskeyCredentialProvider,
8
- PasswordCredentialProvider
33
+ PasswordCredentialProvider,
34
+ RawKeyCredentialProvider
9
35
  } from "@fiber-pay/sdk/browser";
10
- import { useCallback, useEffect, useRef, useState } from "react";
36
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
11
37
  var PASSKEY_UNAVAILABLE_REASON_TEXT = {
12
38
  "window-unavailable": "Passkey is not available because there is no browser window context.",
13
39
  "insecure-context": "Passkey requires a secure context (HTTPS or localhost).",
@@ -66,6 +92,7 @@ function useFiberNode(options) {
66
92
  [detachNodeListeners]
67
93
  );
68
94
  useEffect(() => {
95
+ if (options.enabled === false) return;
69
96
  let cancelled = false;
70
97
  PasskeyCredentialProvider.getSupportStatus().then((status) => {
71
98
  if (!cancelled) {
@@ -89,7 +116,7 @@ function useFiberNode(options) {
89
116
  return () => {
90
117
  cancelled = true;
91
118
  };
92
- }, [walletId]);
119
+ }, [walletId, options.enabled]);
93
120
  const initNode = useCallback(
94
121
  (credential) => {
95
122
  if (nodeRef.current) {
@@ -217,6 +244,26 @@ function useFiberNode(options) {
217
244
  await cleanupFailedStart(node);
218
245
  }
219
246
  }, [cleanupFailedStart, initNode, walletId]);
247
+ const startWithRawKey = useCallback(
248
+ async (fiberKey, ckbSecretKey) => {
249
+ setError(null);
250
+ let node = null;
251
+ try {
252
+ const credential = new RawKeyCredentialProvider(fiberKey, ckbSecretKey, walletId);
253
+ node = initNode(credential);
254
+ const info = await node.start();
255
+ if (isMountedRef.current) {
256
+ setNodeInfo(info);
257
+ }
258
+ } catch (startError) {
259
+ if (isMountedRef.current) {
260
+ setError(asErrorMessage(startError));
261
+ }
262
+ await cleanupFailedStart(node);
263
+ }
264
+ },
265
+ [cleanupFailedStart, initNode, walletId]
266
+ );
220
267
  const stop = useCallback(async () => {
221
268
  const node = nodeRef.current;
222
269
  if (!node) {
@@ -238,11 +285,15 @@ function useFiberNode(options) {
238
285
  }
239
286
  }
240
287
  }, [detachNodeListeners]);
288
+ const isStarting = useMemo(() => state === "unlocking" || state === "starting", [state]);
289
+ const isRunning = useMemo(() => state === "running", [state]);
241
290
  return {
242
291
  state,
243
292
  node: nodeRef.current,
244
293
  nodeInfo,
245
294
  error,
295
+ isStarting,
296
+ isRunning,
246
297
  isPasskeySupported,
247
298
  passkeySupportReason,
248
299
  passkeyUnavailableReason,
@@ -250,12 +301,390 @@ function useFiberNode(options) {
250
301
  startWithPassword,
251
302
  createPasskeyAndStart,
252
303
  startWithPasskey,
304
+ startWithRawKey,
253
305
  stop
254
306
  };
255
307
  }
256
308
 
309
+ // src/connect-button.tsx
310
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
311
+ function truncateNodeId(id) {
312
+ if (id.length <= 16) return id;
313
+ return `${id.slice(0, 8)}\u2026${id.slice(-4)}`;
314
+ }
315
+ var styles = {
316
+ root: {
317
+ position: "relative",
318
+ display: "inline-flex",
319
+ alignItems: "center",
320
+ gap: "0.5rem",
321
+ fontFamily: "system-ui, -apple-system, sans-serif"
322
+ },
323
+ button: {
324
+ display: "inline-flex",
325
+ alignItems: "center",
326
+ gap: "0.5rem",
327
+ padding: "0.5rem 1.25rem",
328
+ fontSize: "0.875rem",
329
+ fontWeight: 600,
330
+ borderRadius: "9999px",
331
+ border: "none",
332
+ cursor: "pointer",
333
+ transition: "background-color 0.15s, opacity 0.15s",
334
+ lineHeight: 1.4
335
+ },
336
+ connectButton: {
337
+ backgroundColor: "var(--fpay-accent, #6366f1)",
338
+ color: "var(--fpay-accent-fg, #fff)"
339
+ },
340
+ connectedButton: {
341
+ backgroundColor: "var(--fpay-accent-subtle, rgba(99,102,241,0.12))",
342
+ color: "var(--fpay-accent, #6366f1)",
343
+ border: "1px solid var(--fpay-accent-border, rgba(99,102,241,0.35))"
344
+ },
345
+ disabledButton: {
346
+ opacity: 0.6,
347
+ cursor: "not-allowed"
348
+ },
349
+ statusDot: {
350
+ display: "inline-block",
351
+ width: "0.5rem",
352
+ height: "0.5rem",
353
+ borderRadius: "50%",
354
+ backgroundColor: "var(--fpay-accent, #6366f1)"
355
+ },
356
+ errorText: {
357
+ fontSize: "0.75rem",
358
+ color: "var(--fpay-error, #ef4444)",
359
+ maxWidth: "220px",
360
+ overflow: "hidden",
361
+ textOverflow: "ellipsis",
362
+ whiteSpace: "nowrap"
363
+ },
364
+ dropdown: {
365
+ position: "absolute",
366
+ right: 0,
367
+ top: "100%",
368
+ marginTop: "0.5rem",
369
+ width: "280px",
370
+ borderRadius: "0.75rem",
371
+ border: "1px solid var(--fpay-border, #e5e7eb)",
372
+ backgroundColor: "var(--fpay-bg-elevated, #fff)",
373
+ padding: "1rem",
374
+ boxShadow: "0 10px 25px rgba(0,0,0,0.1)",
375
+ zIndex: 100
376
+ },
377
+ infoRow: {
378
+ display: "flex",
379
+ justifyContent: "space-between",
380
+ alignItems: "center",
381
+ padding: "0.375rem 0",
382
+ fontSize: "0.75rem"
383
+ },
384
+ infoLabel: {
385
+ color: "var(--fpay-text-secondary, #6b7280)"
386
+ },
387
+ infoValue: {
388
+ fontFamily: "ui-monospace, monospace",
389
+ color: "var(--fpay-text-primary, #111827)"
390
+ },
391
+ separator: {
392
+ borderTop: "1px solid var(--fpay-border, #e5e7eb)",
393
+ margin: "0.75rem 0"
394
+ },
395
+ disconnectButton: {
396
+ display: "flex",
397
+ width: "100%",
398
+ alignItems: "center",
399
+ justifyContent: "space-between",
400
+ padding: "0.625rem 0.75rem",
401
+ fontSize: "0.875rem",
402
+ fontWeight: 600,
403
+ border: "1px solid var(--fpay-border, #e5e7eb)",
404
+ borderRadius: "0.5rem",
405
+ backgroundColor: "var(--fpay-bg-secondary, #f9fafb)",
406
+ color: "var(--fpay-text-primary, #111827)",
407
+ cursor: "pointer",
408
+ transition: "background-color 0.15s"
409
+ },
410
+ spinner: {
411
+ animation: "fpay-spin 1s linear infinite"
412
+ }
413
+ };
414
+ var KEYFRAMES_ID = "fpay-connect-button-keyframes";
415
+ function ensureKeyframes() {
416
+ if (typeof document === "undefined") return;
417
+ if (document.getElementById(KEYFRAMES_ID)) return;
418
+ const style = document.createElement("style");
419
+ style.id = KEYFRAMES_ID;
420
+ style.textContent = `@keyframes fpay-spin { to { transform: rotate(360deg); } }`;
421
+ document.head.appendChild(style);
422
+ }
423
+ function Spinner({ size = 16 }) {
424
+ return /* @__PURE__ */ jsx(
425
+ "svg",
426
+ {
427
+ width: size,
428
+ height: size,
429
+ viewBox: "0 0 24 24",
430
+ fill: "none",
431
+ stroke: "currentColor",
432
+ strokeWidth: "2",
433
+ strokeLinecap: "round",
434
+ strokeLinejoin: "round",
435
+ style: styles.spinner,
436
+ role: "img",
437
+ "aria-label": "Loading",
438
+ children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
439
+ }
440
+ );
441
+ }
442
+ function Chevron({ open }) {
443
+ return /* @__PURE__ */ jsx(
444
+ "svg",
445
+ {
446
+ width: "12",
447
+ height: "12",
448
+ viewBox: "0 0 24 24",
449
+ fill: "none",
450
+ stroke: "currentColor",
451
+ strokeWidth: "2",
452
+ strokeLinecap: "round",
453
+ strokeLinejoin: "round",
454
+ style: { transition: "transform 0.15s", transform: open ? "rotate(180deg)" : "none" },
455
+ "aria-hidden": "true",
456
+ children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" })
457
+ }
458
+ );
459
+ }
460
+ function ConnectButton(props) {
461
+ const {
462
+ network = "testnet",
463
+ fiber: externalFiber,
464
+ strategy = "auto",
465
+ password,
466
+ rawKey,
467
+ rawCkbKey,
468
+ walletId,
469
+ passkeyUsername = "User",
470
+ wasmFactory,
471
+ nodeConfig,
472
+ className,
473
+ style,
474
+ dropdownStyle,
475
+ renderConnectedDropdown,
476
+ onConnect,
477
+ onDisconnect,
478
+ onError
479
+ } = props;
480
+ const internalFiber = useFiberNode({
481
+ network,
482
+ walletId,
483
+ wasmFactory,
484
+ nodeConfig,
485
+ enabled: !externalFiber
486
+ });
487
+ const fiber = externalFiber ?? internalFiber;
488
+ const {
489
+ state,
490
+ node,
491
+ nodeInfo,
492
+ error,
493
+ isStarting,
494
+ isRunning,
495
+ isPasskeySupported,
496
+ passkeyUnavailableReason,
497
+ hasPasskeyConfigured,
498
+ createPasskeyAndStart,
499
+ startWithPasskey,
500
+ startWithPassword,
501
+ startWithRawKey,
502
+ stop
503
+ } = fiber;
504
+ const [isConnecting, setIsConnecting] = useState2(false);
505
+ const [showDropdown, setShowDropdown] = useState2(false);
506
+ const [localError, setLocalError] = useState2(null);
507
+ const dropdownRef = useRef2(null);
508
+ const effectiveIsStarting = isConnecting || isStarting;
509
+ useEffect2(() => ensureKeyframes(), []);
510
+ useEffect2(() => {
511
+ if (!showDropdown) return;
512
+ function handleClickOutside(e) {
513
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
514
+ setShowDropdown(false);
515
+ }
516
+ }
517
+ document.addEventListener("mousedown", handleClickOutside);
518
+ return () => document.removeEventListener("mousedown", handleClickOutside);
519
+ }, [showDropdown]);
520
+ const prevRunningRef = useRef2(false);
521
+ useEffect2(() => {
522
+ if (isRunning && !prevRunningRef.current && node && nodeInfo) {
523
+ onConnect?.(node, nodeInfo);
524
+ }
525
+ if (!isRunning && prevRunningRef.current) {
526
+ onDisconnect?.();
527
+ }
528
+ prevRunningRef.current = isRunning;
529
+ }, [isRunning, node, nodeInfo, onConnect, onDisconnect]);
530
+ useEffect2(() => {
531
+ if (error) onError?.(error);
532
+ }, [error, onError]);
533
+ const resolvedStrategy = strategy === "auto" ? hasPasskeyConfigured && isPasskeySupported ? "passkey" : password ? "password" : rawKey ? "rawKey" : "passkey" : strategy;
534
+ const handleConnect = useCallback2(async () => {
535
+ setIsConnecting(true);
536
+ setLocalError(null);
537
+ try {
538
+ switch (resolvedStrategy) {
539
+ case "password":
540
+ if (!password) throw new Error('Password is required for "password" strategy');
541
+ await startWithPassword(password);
542
+ break;
543
+ case "rawKey":
544
+ if (!rawKey) throw new Error('rawKey is required for "rawKey" strategy');
545
+ await startWithRawKey(rawKey, rawCkbKey);
546
+ break;
547
+ case "passkey":
548
+ if (hasPasskeyConfigured) {
549
+ await startWithPasskey();
550
+ } else {
551
+ await createPasskeyAndStart(passkeyUsername);
552
+ }
553
+ break;
554
+ }
555
+ } catch (err) {
556
+ const msg = err instanceof Error ? err.message : String(err);
557
+ setLocalError(msg);
558
+ onError?.(msg);
559
+ } finally {
560
+ setIsConnecting(false);
561
+ }
562
+ }, [
563
+ resolvedStrategy,
564
+ password,
565
+ rawKey,
566
+ rawCkbKey,
567
+ passkeyUsername,
568
+ hasPasskeyConfigured,
569
+ startWithPassword,
570
+ startWithPasskey,
571
+ startWithRawKey,
572
+ createPasskeyAndStart,
573
+ onError
574
+ ]);
575
+ const handleDisconnect = useCallback2(async () => {
576
+ try {
577
+ await stop();
578
+ } catch (err) {
579
+ const msg = err instanceof Error ? err.message : String(err);
580
+ setLocalError(msg);
581
+ onError?.(msg);
582
+ } finally {
583
+ setShowDropdown(false);
584
+ }
585
+ }, [stop, onError]);
586
+ const closeDropdown = useCallback2(() => {
587
+ setShowDropdown(false);
588
+ }, []);
589
+ const hasError = !!(error || localError);
590
+ let buttonLabel;
591
+ let buttonOnClick;
592
+ let buttonDisabled = false;
593
+ let buttonStyle;
594
+ if (isRunning) {
595
+ buttonLabel = /* @__PURE__ */ jsxs(Fragment, { children: [
596
+ /* @__PURE__ */ jsx("span", { style: styles.statusDot }),
597
+ /* @__PURE__ */ jsx("span", { style: { fontFamily: "ui-monospace, monospace" }, children: nodeInfo?.pubkey ? truncateNodeId(nodeInfo.pubkey) : "Connected" }),
598
+ /* @__PURE__ */ jsx(Chevron, { open: showDropdown })
599
+ ] });
600
+ buttonOnClick = () => setShowDropdown((s) => !s);
601
+ buttonStyle = { ...styles.button, ...styles.connectedButton };
602
+ } else if (effectiveIsStarting) {
603
+ buttonLabel = /* @__PURE__ */ jsxs(Fragment, { children: [
604
+ /* @__PURE__ */ jsx(Spinner, {}),
605
+ "Connecting\u2026"
606
+ ] });
607
+ buttonDisabled = true;
608
+ buttonStyle = { ...styles.button, ...styles.connectButton, ...styles.disabledButton };
609
+ } else {
610
+ switch (resolvedStrategy) {
611
+ case "passkey":
612
+ buttonLabel = hasPasskeyConfigured ? "Connect with Passkey" : "Connect via Passkey";
613
+ if (!isPasskeySupported) {
614
+ buttonLabel = "Passkey unavailable";
615
+ buttonDisabled = true;
616
+ }
617
+ break;
618
+ case "password":
619
+ buttonLabel = "Connect";
620
+ break;
621
+ case "rawKey":
622
+ buttonLabel = "Connect";
623
+ break;
624
+ }
625
+ buttonOnClick = handleConnect;
626
+ buttonStyle = {
627
+ ...styles.button,
628
+ ...styles.connectButton,
629
+ ...buttonDisabled ? styles.disabledButton : {}
630
+ };
631
+ }
632
+ return /* @__PURE__ */ jsxs("div", { className, style: { ...styles.root, ...style }, "data-fpay-connect-button": "", children: [
633
+ (hasError || !isPasskeySupported && passkeyUnavailableReason && resolvedStrategy === "passkey") && /* @__PURE__ */ jsx("span", { style: styles.errorText, children: error || localError || passkeyUnavailableReason }),
634
+ isRunning ? /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, ref: dropdownRef, children: [
635
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: buttonOnClick, style: buttonStyle, children: buttonLabel }),
636
+ showDropdown && /* @__PURE__ */ jsx("div", { style: { ...styles.dropdown, ...dropdownStyle }, children: renderConnectedDropdown ? renderConnectedDropdown({
637
+ fiber,
638
+ closeDropdown,
639
+ disconnect: handleDisconnect
640
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [
641
+ nodeInfo && /* @__PURE__ */ jsxs(Fragment, { children: [
642
+ /* @__PURE__ */ jsxs("div", { style: styles.infoRow, children: [
643
+ /* @__PURE__ */ jsx("span", { style: styles.infoLabel, children: "Pubkey" }),
644
+ /* @__PURE__ */ jsx("span", { style: styles.infoValue, children: truncateNodeId(nodeInfo.pubkey) })
645
+ ] }),
646
+ /* @__PURE__ */ jsxs("div", { style: styles.infoRow, children: [
647
+ /* @__PURE__ */ jsx("span", { style: styles.infoLabel, children: "State" }),
648
+ /* @__PURE__ */ jsx("span", { style: styles.infoValue, children: state })
649
+ ] })
650
+ ] }),
651
+ /* @__PURE__ */ jsx("div", { style: styles.separator }),
652
+ /* @__PURE__ */ jsxs(
653
+ "button",
654
+ {
655
+ type: "button",
656
+ onClick: () => void handleDisconnect(),
657
+ style: styles.disconnectButton,
658
+ children: [
659
+ /* @__PURE__ */ jsx("span", { children: "Disconnect" }),
660
+ /* @__PURE__ */ jsx(
661
+ "svg",
662
+ {
663
+ width: "14",
664
+ height: "14",
665
+ viewBox: "0 0 24 24",
666
+ fill: "none",
667
+ stroke: "currentColor",
668
+ strokeWidth: "2",
669
+ strokeLinecap: "round",
670
+ strokeLinejoin: "round",
671
+ "aria-hidden": "true",
672
+ children: /* @__PURE__ */ jsx("path", { d: "M9 18l6-6-6-6" })
673
+ }
674
+ )
675
+ ]
676
+ }
677
+ )
678
+ ] }) })
679
+ ] }) : /* @__PURE__ */ jsx("button", { type: "button", onClick: buttonOnClick, disabled: buttonDisabled, style: buttonStyle, children: buttonLabel })
680
+ ] });
681
+ }
682
+
683
+ // src/fiber-pay-quick-card.tsx
684
+ import { useEffect as useEffect4, useId, useState as useState4 } from "react";
685
+
257
686
  // src/use-fiber-payment.ts
258
- import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
687
+ import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
259
688
  function asErrorMessage2(error) {
260
689
  if (error instanceof Error) {
261
690
  return error.message;
@@ -263,17 +692,17 @@ function asErrorMessage2(error) {
263
692
  return String(error);
264
693
  }
265
694
  function useFiberPayment(node) {
266
- const [isPaying, setIsPaying] = useState2(false);
267
- const [paymentResult, setPaymentResult] = useState2(null);
268
- const [error, setError] = useState2(null);
269
- const isMountedRef = useRef2(true);
270
- useEffect2(
695
+ const [isPaying, setIsPaying] = useState3(false);
696
+ const [paymentResult, setPaymentResult] = useState3(null);
697
+ const [error, setError] = useState3(null);
698
+ const isMountedRef = useRef3(true);
699
+ useEffect3(
271
700
  () => () => {
272
701
  isMountedRef.current = false;
273
702
  },
274
703
  []
275
704
  );
276
- const payInvoice = useCallback2(
705
+ const payInvoice = useCallback3(
277
706
  async (invoice) => {
278
707
  if (!node) {
279
708
  if (isMountedRef.current) {
@@ -318,7 +747,7 @@ function useFiberPayment(node) {
318
747
  }
319
748
 
320
749
  // src/fiber-pay-quick-card.tsx
321
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
750
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
322
751
  var ONE_CKB_SHANNONS = "0x5f5e100";
323
752
  var cardStyle = {
324
753
  border: "1px solid #ddd",
@@ -356,22 +785,22 @@ function FiberPayQuickCard(props) {
356
785
  stop
357
786
  } = useFiberNode({ network, walletId: props.walletId });
358
787
  const { payInvoice, isPaying, error: payError, paymentResult } = useFiberPayment(node);
359
- const [password, setPassword] = useState3("");
360
- const [invoiceInput, setInvoiceInput] = useState3("");
361
- const [createdInvoice, setCreatedInvoice] = useState3("");
362
- const [isCreatingInvoice, setIsCreatingInvoice] = useState3(false);
363
- const [invoiceError, setInvoiceError] = useState3(null);
364
- useEffect3(() => {
788
+ const [password, setPassword] = useState4("");
789
+ const [invoiceInput, setInvoiceInput] = useState4("");
790
+ const [createdInvoice, setCreatedInvoice] = useState4("");
791
+ const [isCreatingInvoice, setIsCreatingInvoice] = useState4(false);
792
+ const [invoiceError, setInvoiceError] = useState4(null);
793
+ useEffect4(() => {
365
794
  if (nodeError) {
366
795
  onError?.({ scope: "node", message: nodeError });
367
796
  }
368
797
  }, [nodeError, onError]);
369
- useEffect3(() => {
798
+ useEffect4(() => {
370
799
  if (payError) {
371
800
  onError?.({ scope: "payment", message: payError });
372
801
  }
373
802
  }, [onError, payError]);
374
- useEffect3(() => {
803
+ useEffect4(() => {
375
804
  if (paymentResult) {
376
805
  onPaymentResult?.(paymentResult);
377
806
  }
@@ -398,18 +827,18 @@ function FiberPayQuickCard(props) {
398
827
  setIsCreatingInvoice(false);
399
828
  }
400
829
  };
401
- return /* @__PURE__ */ jsxs("div", { style: { ...cardStyle, ...props.style }, className: props.className, children: [
402
- /* @__PURE__ */ jsxs("h3", { children: [
830
+ return /* @__PURE__ */ jsxs2("div", { style: { ...cardStyle, ...props.style }, className: props.className, children: [
831
+ /* @__PURE__ */ jsxs2("h3", { children: [
403
832
  title,
404
833
  " (",
405
834
  network,
406
835
  ")"
407
836
  ] }),
408
- !nodeInfo ? /* @__PURE__ */ jsxs(Fragment, { children: [
409
- isPasskeySupported ? /* @__PURE__ */ jsx("div", { style: rowWithMarginStyle, children: hasPasskeyConfigured ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void startWithPasskey(), children: "Login with Passkey" }) : /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void createPasskeyAndStart(passkeyUsername), children: "Register Passkey" }) }) : null,
410
- /* @__PURE__ */ jsx("label", { htmlFor: passwordInputId, children: "Password" }),
411
- /* @__PURE__ */ jsxs("div", { style: rowStyle, children: [
412
- /* @__PURE__ */ jsx(
837
+ !nodeInfo ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
838
+ isPasskeySupported ? /* @__PURE__ */ jsx2("div", { style: rowWithMarginStyle, children: hasPasskeyConfigured ? /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => void startWithPasskey(), children: "Login with Passkey" }) : /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => void createPasskeyAndStart(passkeyUsername), children: "Register Passkey" }) }) : null,
839
+ /* @__PURE__ */ jsx2("label", { htmlFor: passwordInputId, children: "Password" }),
840
+ /* @__PURE__ */ jsxs2("div", { style: rowStyle, children: [
841
+ /* @__PURE__ */ jsx2(
413
842
  "input",
414
843
  {
415
844
  id: passwordInputId,
@@ -421,31 +850,31 @@ function FiberPayQuickCard(props) {
421
850
  placeholder: "Password"
422
851
  }
423
852
  ),
424
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void startWithPassword(password), children: "Start with Password" })
853
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => void startWithPassword(password), children: "Start with Password" })
425
854
  ] })
426
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
427
- /* @__PURE__ */ jsxs("p", { children: [
428
- /* @__PURE__ */ jsx("strong", { children: "State:" }),
855
+ ] }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
856
+ /* @__PURE__ */ jsxs2("p", { children: [
857
+ /* @__PURE__ */ jsx2("strong", { children: "State:" }),
429
858
  " ",
430
859
  state
431
860
  ] }),
432
- /* @__PURE__ */ jsxs("p", { children: [
433
- /* @__PURE__ */ jsx("strong", { children: "Pubkey:" }),
861
+ /* @__PURE__ */ jsxs2("p", { children: [
862
+ /* @__PURE__ */ jsx2("strong", { children: "Pubkey:" }),
434
863
  " ",
435
864
  nodeInfo.pubkey
436
865
  ] }),
437
- /* @__PURE__ */ jsxs("div", { style: rowWithMarginStyle, children: [
438
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void createInvoice(), disabled: isCreatingInvoice, children: isCreatingInvoice ? "Creating..." : "Create Invoice (1 CKB)" }),
439
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void stop(), children: "Stop Node" })
866
+ /* @__PURE__ */ jsxs2("div", { style: rowWithMarginStyle, children: [
867
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => void createInvoice(), disabled: isCreatingInvoice, children: isCreatingInvoice ? "Creating..." : "Create Invoice (1 CKB)" }),
868
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => void stop(), children: "Stop Node" })
440
869
  ] }),
441
- createdInvoice ? /* @__PURE__ */ jsxs("p", { children: [
442
- /* @__PURE__ */ jsx("strong", { children: "Created invoice:" }),
870
+ createdInvoice ? /* @__PURE__ */ jsxs2("p", { children: [
871
+ /* @__PURE__ */ jsx2("strong", { children: "Created invoice:" }),
443
872
  " ",
444
873
  createdInvoice
445
874
  ] }) : null,
446
- /* @__PURE__ */ jsx("label", { htmlFor: invoiceInputId, children: "Invoice" }),
447
- /* @__PURE__ */ jsxs("div", { style: rowStyle, children: [
448
- /* @__PURE__ */ jsx(
875
+ /* @__PURE__ */ jsx2("label", { htmlFor: invoiceInputId, children: "Invoice" }),
876
+ /* @__PURE__ */ jsxs2("div", { style: rowStyle, children: [
877
+ /* @__PURE__ */ jsx2(
449
878
  "input",
450
879
  {
451
880
  id: invoiceInputId,
@@ -455,33 +884,439 @@ function FiberPayQuickCard(props) {
455
884
  placeholder: "Paste invoice to pay"
456
885
  }
457
886
  ),
458
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void payInvoice(invoiceInput), disabled: isPaying, children: isPaying ? "Paying..." : "Pay" })
887
+ /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => void payInvoice(invoiceInput), disabled: isPaying, children: isPaying ? "Paying..." : "Pay" })
459
888
  ] }),
460
- paymentResult ? /* @__PURE__ */ jsxs("p", { children: [
461
- /* @__PURE__ */ jsx("strong", { children: "Payment:" }),
889
+ paymentResult ? /* @__PURE__ */ jsxs2("p", { children: [
890
+ /* @__PURE__ */ jsx2("strong", { children: "Payment:" }),
462
891
  " ",
463
892
  paymentResult.status
464
893
  ] }) : null
465
894
  ] }),
466
- nodeError ? /* @__PURE__ */ jsxs("p", { style: { color: "#b91c1c" }, children: [
467
- /* @__PURE__ */ jsx("strong", { children: "Node error:" }),
895
+ nodeError ? /* @__PURE__ */ jsxs2("p", { style: { color: "#b91c1c" }, children: [
896
+ /* @__PURE__ */ jsx2("strong", { children: "Node error:" }),
468
897
  " ",
469
898
  nodeError
470
899
  ] }) : null,
471
- payError ? /* @__PURE__ */ jsxs("p", { style: { color: "#b91c1c" }, children: [
472
- /* @__PURE__ */ jsx("strong", { children: "Payment error:" }),
900
+ payError ? /* @__PURE__ */ jsxs2("p", { style: { color: "#b91c1c" }, children: [
901
+ /* @__PURE__ */ jsx2("strong", { children: "Payment error:" }),
473
902
  " ",
474
903
  payError
475
904
  ] }) : null,
476
- invoiceError ? /* @__PURE__ */ jsxs("p", { style: { color: "#b91c1c" }, children: [
477
- /* @__PURE__ */ jsx("strong", { children: "Invoice error:" }),
905
+ invoiceError ? /* @__PURE__ */ jsxs2("p", { style: { color: "#b91c1c" }, children: [
906
+ /* @__PURE__ */ jsx2("strong", { children: "Invoice error:" }),
478
907
  " ",
479
908
  invoiceError
480
909
  ] }) : null
481
910
  ] });
482
911
  }
912
+
913
+ // src/node-info-panel.tsx
914
+ import {
915
+ ConfigBuilder,
916
+ formatShannonsAsCkb,
917
+ getLockBalanceShannons,
918
+ scriptToAddress
919
+ } from "@fiber-pay/sdk/browser";
920
+ import {
921
+ useCallback as useCallback4,
922
+ useEffect as useEffect5,
923
+ useRef as useRef4,
924
+ useState as useState5
925
+ } from "react";
926
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
927
+ function truncateMiddle(str, left = 8, right = 8) {
928
+ if (str.length <= left + right + 3) return str;
929
+ return `${str.slice(0, left)}\u2026${str.slice(-right)}`;
930
+ }
931
+ function copyToClipboard(text) {
932
+ void navigator.clipboard.writeText(text);
933
+ }
934
+ async function fetchNodeStats(node, network) {
935
+ const [nodeInfo, peers, channels] = await Promise.all([
936
+ node.nodeInfo(),
937
+ node.listPeers(),
938
+ node.listChannels()
939
+ ]);
940
+ const lockScript = nodeInfo.default_funding_lock_script;
941
+ const ckbRpcUrl = ConfigBuilder.getDefaults(network).ckbRpcUrl;
942
+ if (!lockScript || lockScript.args === "0x") {
943
+ return {
944
+ pubkey: nodeInfo.pubkey,
945
+ peers: peers.peers.length,
946
+ channels: channels.channels.length,
947
+ ckbAddress: null,
948
+ balanceCkb: null,
949
+ externalFunding: true
950
+ };
951
+ }
952
+ const ckbAddress = scriptToAddress(lockScript, network);
953
+ const balanceShannons = await getLockBalanceShannons(ckbRpcUrl, lockScript);
954
+ const balanceCkb = formatShannonsAsCkb(balanceShannons, 4);
955
+ return {
956
+ pubkey: nodeInfo.pubkey,
957
+ peers: peers.peers.length,
958
+ channels: channels.channels.length,
959
+ ckbAddress,
960
+ balanceCkb,
961
+ externalFunding: false
962
+ };
963
+ }
964
+ var styles2 = {
965
+ root: {
966
+ fontFamily: "system-ui, -apple-system, sans-serif",
967
+ fontSize: "0.875rem",
968
+ color: "var(--fpay-text-primary, #111827)"
969
+ },
970
+ idle: {
971
+ padding: "1rem",
972
+ textAlign: "center",
973
+ color: "var(--fpay-text-secondary, #6b7280)",
974
+ fontSize: "0.75rem"
975
+ },
976
+ loading: {
977
+ display: "flex",
978
+ alignItems: "center",
979
+ gap: "0.5rem",
980
+ padding: "0.5rem 0",
981
+ fontSize: "0.75rem",
982
+ color: "var(--fpay-text-secondary, #6b7280)"
983
+ },
984
+ errorBox: {
985
+ marginBottom: "0.5rem",
986
+ padding: "0.375rem 0.5rem",
987
+ fontSize: "0.75rem",
988
+ borderRadius: "0.5rem",
989
+ border: "1px solid var(--fpay-error-border, rgba(239,68,68,0.3))",
990
+ backgroundColor: "var(--fpay-error-bg, rgba(239,68,68,0.1))",
991
+ color: "var(--fpay-error, #ef4444)"
992
+ },
993
+ infoRow: {
994
+ display: "flex",
995
+ justifyContent: "space-between",
996
+ alignItems: "center",
997
+ gap: "0.75rem",
998
+ padding: "0.375rem 0"
999
+ },
1000
+ infoLabel: {
1001
+ fontSize: "0.75rem",
1002
+ color: "var(--fpay-text-secondary, #6b7280)"
1003
+ },
1004
+ infoValueWrapper: {
1005
+ display: "flex",
1006
+ alignItems: "center",
1007
+ gap: "0.375rem"
1008
+ },
1009
+ infoValue: {
1010
+ fontFamily: "ui-monospace, monospace",
1011
+ fontSize: "0.75rem",
1012
+ color: "var(--fpay-text-primary, #111827)"
1013
+ },
1014
+ copyButton: {
1015
+ display: "inline-flex",
1016
+ alignItems: "center",
1017
+ justifyContent: "center",
1018
+ padding: "0.25rem",
1019
+ borderRadius: "0.25rem",
1020
+ border: "none",
1021
+ background: "none",
1022
+ cursor: "pointer",
1023
+ color: "var(--fpay-text-secondary, #6b7280)",
1024
+ transition: "color 0.15s"
1025
+ },
1026
+ statsGrid: {
1027
+ display: "grid",
1028
+ gridTemplateColumns: "1fr 1fr",
1029
+ gap: "0.5rem",
1030
+ marginTop: "0.5rem"
1031
+ },
1032
+ statCard: {
1033
+ padding: "0.375rem 0.5rem",
1034
+ textAlign: "center",
1035
+ borderRadius: "0.5rem",
1036
+ border: "1px solid var(--fpay-border, #e5e7eb)",
1037
+ backgroundColor: "var(--fpay-bg-secondary, #f9fafb)"
1038
+ },
1039
+ statLabel: {
1040
+ fontSize: "0.625rem",
1041
+ color: "var(--fpay-text-secondary, #6b7280)"
1042
+ },
1043
+ statValue: {
1044
+ fontSize: "0.875rem",
1045
+ fontWeight: 600,
1046
+ color: "var(--fpay-text-primary, #111827)"
1047
+ },
1048
+ qrContainer: {
1049
+ display: "flex",
1050
+ flexDirection: "column",
1051
+ alignItems: "center",
1052
+ gap: "0.25rem",
1053
+ marginTop: "0.75rem",
1054
+ padding: "0.5rem",
1055
+ borderRadius: "0.5rem",
1056
+ border: "1px solid var(--fpay-border, #e5e7eb)",
1057
+ backgroundColor: "var(--fpay-bg-secondary, #f9fafb)"
1058
+ },
1059
+ qrCaption: {
1060
+ fontSize: "0.625rem",
1061
+ color: "var(--fpay-text-secondary, #6b7280)"
1062
+ },
1063
+ balanceRow: {
1064
+ display: "flex",
1065
+ justifyContent: "space-between",
1066
+ alignItems: "center",
1067
+ width: "100%",
1068
+ marginTop: "0.5rem",
1069
+ paddingTop: "0.5rem",
1070
+ borderTop: "1px solid var(--fpay-border, #e5e7eb)"
1071
+ },
1072
+ statusBadge: {
1073
+ display: "inline-flex",
1074
+ alignItems: "center",
1075
+ gap: "0.375rem",
1076
+ padding: "0.25rem 0.625rem",
1077
+ fontSize: "0.75rem",
1078
+ fontWeight: 500,
1079
+ borderRadius: "9999px",
1080
+ marginBottom: "0.5rem"
1081
+ }
1082
+ };
1083
+ var STATUS_COLORS = {
1084
+ idle: { bg: "rgba(107,114,128,0.1)", fg: "#6b7280", dot: "#9ca3af" },
1085
+ unlocking: { bg: "rgba(245,158,11,0.1)", fg: "#d97706", dot: "#f59e0b" },
1086
+ starting: { bg: "rgba(245,158,11,0.1)", fg: "#d97706", dot: "#f59e0b" },
1087
+ running: { bg: "rgba(34,197,94,0.1)", fg: "#16a34a", dot: "#22c55e" },
1088
+ stopping: { bg: "rgba(245,158,11,0.1)", fg: "#d97706", dot: "#f59e0b" },
1089
+ stopped: { bg: "rgba(107,114,128,0.1)", fg: "#6b7280", dot: "#9ca3af" },
1090
+ error: { bg: "rgba(239,68,68,0.1)", fg: "#dc2626", dot: "#ef4444" }
1091
+ };
1092
+ function CopyIcon() {
1093
+ return /* @__PURE__ */ jsxs3(
1094
+ "svg",
1095
+ {
1096
+ width: "12",
1097
+ height: "12",
1098
+ viewBox: "0 0 24 24",
1099
+ fill: "none",
1100
+ stroke: "currentColor",
1101
+ strokeWidth: "2",
1102
+ strokeLinecap: "round",
1103
+ strokeLinejoin: "round",
1104
+ "aria-hidden": "true",
1105
+ children: [
1106
+ /* @__PURE__ */ jsx3("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
1107
+ /* @__PURE__ */ jsx3("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
1108
+ ]
1109
+ }
1110
+ );
1111
+ }
1112
+ function InfoRow({ label, value, copyable }) {
1113
+ return /* @__PURE__ */ jsxs3("div", { style: styles2.infoRow, children: [
1114
+ /* @__PURE__ */ jsx3("span", { style: styles2.infoLabel, children: label }),
1115
+ /* @__PURE__ */ jsxs3("div", { style: styles2.infoValueWrapper, children: [
1116
+ /* @__PURE__ */ jsx3("span", { style: styles2.infoValue, children: truncateMiddle(value, 6, 6) }),
1117
+ copyable && /* @__PURE__ */ jsx3(
1118
+ "button",
1119
+ {
1120
+ type: "button",
1121
+ onClick: () => copyToClipboard(value),
1122
+ style: styles2.copyButton,
1123
+ title: "Copy to clipboard",
1124
+ "aria-label": `Copy ${label}`,
1125
+ children: /* @__PURE__ */ jsx3(CopyIcon, {})
1126
+ }
1127
+ )
1128
+ ] })
1129
+ ] });
1130
+ }
1131
+ function NodeInfoPanel(props) {
1132
+ const {
1133
+ node,
1134
+ network,
1135
+ pollInterval = 15e3,
1136
+ showQrCode = false,
1137
+ renderQrCode,
1138
+ className,
1139
+ style
1140
+ } = props;
1141
+ const [stats, setStats] = useState5(null);
1142
+ const [statsError, setStatsError] = useState5(null);
1143
+ const [statsLoading, setStatsLoading] = useState5(false);
1144
+ const cancelledRef = useRef4(false);
1145
+ const [QRComponent, setQRComponent] = useState5(null);
1146
+ useEffect5(() => {
1147
+ let cancelled = false;
1148
+ if (!showQrCode || renderQrCode) return;
1149
+ import("qrcode.react").then((mod) => {
1150
+ if (cancelled) return;
1151
+ const Comp = mod.QRCodeSVG ?? mod.default;
1152
+ if (Comp) setQRComponent(() => Comp);
1153
+ }).catch(() => {
1154
+ });
1155
+ return () => {
1156
+ cancelled = true;
1157
+ };
1158
+ }, [showQrCode, renderQrCode]);
1159
+ const loadingRef = useRef4(false);
1160
+ const loadStats = useCallback4(async () => {
1161
+ if (!node || node.state !== "running" || loadingRef.current) return;
1162
+ loadingRef.current = true;
1163
+ setStatsLoading(true);
1164
+ setStatsError(null);
1165
+ try {
1166
+ const data = await fetchNodeStats(node, network);
1167
+ if (!cancelledRef.current) setStats(data);
1168
+ } catch (e) {
1169
+ if (!cancelledRef.current) {
1170
+ setStatsError(e instanceof Error ? e.message : String(e));
1171
+ }
1172
+ } finally {
1173
+ loadingRef.current = false;
1174
+ if (!cancelledRef.current) setStatsLoading(false);
1175
+ }
1176
+ }, [node, network]);
1177
+ useEffect5(() => {
1178
+ cancelledRef.current = false;
1179
+ if (!node || node.state !== "running") {
1180
+ setStats(null);
1181
+ setStatsError(null);
1182
+ return;
1183
+ }
1184
+ void loadStats();
1185
+ const interval = setInterval(() => void loadStats(), pollInterval);
1186
+ return () => {
1187
+ cancelledRef.current = true;
1188
+ clearInterval(interval);
1189
+ };
1190
+ }, [node, node?.state, pollInterval, loadStats]);
1191
+ if (!node) {
1192
+ return /* @__PURE__ */ jsx3("div", { className, style: { ...styles2.root, ...style }, "data-fpay-node-info": "", children: /* @__PURE__ */ jsx3("div", { style: styles2.idle, children: "No node connected" }) });
1193
+ }
1194
+ const nodeState = node.state;
1195
+ const statusColor = STATUS_COLORS[nodeState] ?? STATUS_COLORS.idle;
1196
+ return /* @__PURE__ */ jsxs3("div", { className, style: { ...styles2.root, ...style }, "data-fpay-node-info": "", children: [
1197
+ /* @__PURE__ */ jsxs3(
1198
+ "div",
1199
+ {
1200
+ style: {
1201
+ ...styles2.statusBadge,
1202
+ backgroundColor: statusColor.bg,
1203
+ color: statusColor.fg
1204
+ },
1205
+ children: [
1206
+ /* @__PURE__ */ jsx3(
1207
+ "span",
1208
+ {
1209
+ style: {
1210
+ display: "inline-block",
1211
+ width: "0.5rem",
1212
+ height: "0.5rem",
1213
+ borderRadius: "50%",
1214
+ backgroundColor: statusColor.dot
1215
+ }
1216
+ }
1217
+ ),
1218
+ nodeState
1219
+ ]
1220
+ }
1221
+ ),
1222
+ statsLoading && !stats && /* @__PURE__ */ jsxs3("div", { style: styles2.loading, children: [
1223
+ /* @__PURE__ */ jsxs3(
1224
+ "svg",
1225
+ {
1226
+ width: "12",
1227
+ height: "12",
1228
+ viewBox: "0 0 24 24",
1229
+ fill: "none",
1230
+ stroke: "currentColor",
1231
+ strokeWidth: "2",
1232
+ strokeLinecap: "round",
1233
+ strokeLinejoin: "round",
1234
+ role: "img",
1235
+ "aria-label": "Loading",
1236
+ children: [
1237
+ /* @__PURE__ */ jsx3("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }),
1238
+ /* @__PURE__ */ jsx3(
1239
+ "animateTransform",
1240
+ {
1241
+ attributeName: "transform",
1242
+ type: "rotate",
1243
+ from: "0 12 12",
1244
+ to: "360 12 12",
1245
+ dur: "1s",
1246
+ repeatCount: "indefinite"
1247
+ }
1248
+ )
1249
+ ]
1250
+ }
1251
+ ),
1252
+ "Loading\u2026"
1253
+ ] }),
1254
+ statsError && /* @__PURE__ */ jsx3("div", { style: styles2.errorBox, children: statsError }),
1255
+ stats && /* @__PURE__ */ jsxs3(Fragment3, { children: [
1256
+ /* @__PURE__ */ jsx3(InfoRow, { label: "Pubkey", value: stats.pubkey, copyable: true }),
1257
+ stats.externalFunding ? /* @__PURE__ */ jsx3("div", { style: { padding: "0.25rem 0", fontSize: "0.75rem", color: "#6b7280" }, children: "External funding mode" }) : stats.ckbAddress ? /* @__PURE__ */ jsx3(InfoRow, { label: "CKB Address", value: stats.ckbAddress, copyable: true }) : null,
1258
+ /* @__PURE__ */ jsxs3("div", { style: styles2.statsGrid, children: [
1259
+ /* @__PURE__ */ jsxs3("div", { style: styles2.statCard, children: [
1260
+ /* @__PURE__ */ jsx3("div", { style: styles2.statLabel, children: "Peers" }),
1261
+ /* @__PURE__ */ jsx3("div", { style: styles2.statValue, children: stats.peers })
1262
+ ] }),
1263
+ /* @__PURE__ */ jsxs3("div", { style: styles2.statCard, children: [
1264
+ /* @__PURE__ */ jsx3("div", { style: styles2.statLabel, children: "Channels" }),
1265
+ /* @__PURE__ */ jsx3("div", { style: styles2.statValue, children: stats.channels })
1266
+ ] })
1267
+ ] }),
1268
+ showQrCode && stats.ckbAddress && /* @__PURE__ */ jsxs3("div", { style: styles2.qrContainer, children: [
1269
+ renderQrCode ? renderQrCode(stats.ckbAddress) : QRComponent ? /* @__PURE__ */ jsx3(
1270
+ QRComponent,
1271
+ {
1272
+ value: stats.ckbAddress,
1273
+ size: 120,
1274
+ bgColor: "transparent",
1275
+ fgColor: "currentColor"
1276
+ }
1277
+ ) : /* @__PURE__ */ jsx3("div", { style: { fontSize: "0.625rem", color: "#9ca3af" }, children: "Install qrcode.react for QR code" }),
1278
+ /* @__PURE__ */ jsx3("span", { style: styles2.qrCaption, children: "Scan to deposit CKB" }),
1279
+ /* @__PURE__ */ jsxs3("div", { style: styles2.balanceRow, children: [
1280
+ /* @__PURE__ */ jsx3("span", { style: { fontSize: "0.75rem", color: "#6b7280" }, children: "Balance" }),
1281
+ /* @__PURE__ */ jsxs3(
1282
+ "span",
1283
+ {
1284
+ style: {
1285
+ fontFamily: "ui-monospace, monospace",
1286
+ fontSize: "0.75rem",
1287
+ fontWeight: 500
1288
+ },
1289
+ children: [
1290
+ stats.balanceCkb ?? "\u2014",
1291
+ " CKB"
1292
+ ]
1293
+ }
1294
+ )
1295
+ ] })
1296
+ ] })
1297
+ ] })
1298
+ ] });
1299
+ }
483
1300
  export {
1301
+ ChannelState,
1302
+ ConfigBuilder2 as ConfigBuilder,
1303
+ ConnectButton,
1304
+ FiberBrowserNode2 as FiberBrowserNode,
484
1305
  FiberPayQuickCard,
1306
+ FiberRpcError,
1307
+ NodeInfoPanel,
1308
+ PasskeyCredentialProvider2 as PasskeyCredentialProvider,
1309
+ PasswordCredentialProvider2 as PasswordCredentialProvider,
1310
+ RawKeyCredentialProvider2 as RawKeyCredentialProvider,
1311
+ ckbHash,
1312
+ ckbToShannons,
1313
+ derivePublicKey,
1314
+ formatShannonsAsCkb2 as formatShannonsAsCkb,
1315
+ fromHex,
1316
+ getLockBalanceShannons2 as getLockBalanceShannons,
1317
+ scriptToAddress2 as scriptToAddress,
1318
+ shannonsToCkb,
1319
+ toHex,
485
1320
  useFiberNode,
486
1321
  useFiberPayment
487
1322
  };