@zerodev/wallet-react-ui 0.0.1

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.
Files changed (94) hide show
  1. package/README.md +124 -0
  2. package/dist/_types/auth/authStoreSlice.d.ts +33 -0
  3. package/dist/_types/auth/authStoreSlice.d.ts.map +1 -0
  4. package/dist/_types/auth/components/BlobAnimation/index.d.ts +4 -0
  5. package/dist/_types/auth/components/BlobAnimation/index.d.ts.map +1 -0
  6. package/dist/_types/auth/components/CodeInput/index.d.ts +11 -0
  7. package/dist/_types/auth/components/CodeInput/index.d.ts.map +1 -0
  8. package/dist/_types/auth/hooks/useAuth.d.ts +18 -0
  9. package/dist/_types/auth/hooks/useAuth.d.ts.map +1 -0
  10. package/dist/_types/auth/index.d.ts +4 -0
  11. package/dist/_types/auth/index.d.ts.map +1 -0
  12. package/dist/_types/auth/pages/EmailVerification.d.ts +2 -0
  13. package/dist/_types/auth/pages/EmailVerification.d.ts.map +1 -0
  14. package/dist/_types/auth/pages/ErrorScreen.d.ts +9 -0
  15. package/dist/_types/auth/pages/ErrorScreen.d.ts.map +1 -0
  16. package/dist/_types/auth/pages/OtpInput.d.ts +2 -0
  17. package/dist/_types/auth/pages/OtpInput.d.ts.map +1 -0
  18. package/dist/_types/auth/pages/SignUp.d.ts +2 -0
  19. package/dist/_types/auth/pages/SignUp.d.ts.map +1 -0
  20. package/dist/_types/auth/pages/Verifying.d.ts +2 -0
  21. package/dist/_types/auth/pages/Verifying.d.ts.map +1 -0
  22. package/dist/_types/auth/pages/WalletSelection.d.ts +2 -0
  23. package/dist/_types/auth/pages/WalletSelection.d.ts.map +1 -0
  24. package/dist/_types/auth/types.d.ts +12 -0
  25. package/dist/_types/auth/types.d.ts.map +1 -0
  26. package/dist/_types/auth/utils/url.d.ts +3 -0
  27. package/dist/_types/auth/utils/url.d.ts.map +1 -0
  28. package/dist/_types/connector.d.ts +17 -0
  29. package/dist/_types/connector.d.ts.map +1 -0
  30. package/dist/_types/index.d.ts +10 -0
  31. package/dist/_types/index.d.ts.map +1 -0
  32. package/dist/_types/shared/components/PoweredBy/index.d.ts +6 -0
  33. package/dist/_types/shared/components/PoweredBy/index.d.ts.map +1 -0
  34. package/dist/_types/shared/components/Screen/EllipticalRadial.d.ts +23 -0
  35. package/dist/_types/shared/components/Screen/EllipticalRadial.d.ts.map +1 -0
  36. package/dist/_types/shared/components/Screen/MultiRadialBackground.d.ts +4 -0
  37. package/dist/_types/shared/components/Screen/MultiRadialBackground.d.ts.map +1 -0
  38. package/dist/_types/shared/components/Screen/index.d.ts +9 -0
  39. package/dist/_types/shared/components/Screen/index.d.ts.map +1 -0
  40. package/dist/_types/shared/components/SignUpFooter/index.d.ts +8 -0
  41. package/dist/_types/shared/components/SignUpFooter/index.d.ts.map +1 -0
  42. package/dist/_types/shared/components/StatusScreen/index.d.ts +10 -0
  43. package/dist/_types/shared/components/StatusScreen/index.d.ts.map +1 -0
  44. package/dist/_types/shared/components/TopNav/index.d.ts +10 -0
  45. package/dist/_types/shared/components/TopNav/index.d.ts.map +1 -0
  46. package/dist/_types/shared/hooks/useKitStore.d.ts +11 -0
  47. package/dist/_types/shared/hooks/useKitStore.d.ts.map +1 -0
  48. package/dist/_types/shared/utils/common.d.ts +5 -0
  49. package/dist/_types/shared/utils/common.d.ts.map +1 -0
  50. package/dist/_types/shared/utils/store.d.ts +4 -0
  51. package/dist/_types/shared/utils/store.d.ts.map +1 -0
  52. package/dist/_types/store.d.ts +25 -0
  53. package/dist/_types/store.d.ts.map +1 -0
  54. package/dist/_types/types.d.ts +38 -0
  55. package/dist/_types/types.d.ts.map +1 -0
  56. package/dist/blob.webm +0 -0
  57. package/dist/error.webp +0 -0
  58. package/dist/index.cjs +4 -0
  59. package/dist/index.cjs.map +1 -0
  60. package/dist/index.mjs +1374 -0
  61. package/dist/index.mjs.map +1 -0
  62. package/dist/loading.webp +0 -0
  63. package/dist/send.webp +0 -0
  64. package/dist/styles.css +2 -0
  65. package/dist/success.webp +0 -0
  66. package/package.json +96 -0
  67. package/src/assets.d.ts +14 -0
  68. package/src/auth/authStoreSlice.ts +180 -0
  69. package/src/auth/components/BlobAnimation/index.tsx +24 -0
  70. package/src/auth/components/CodeInput/index.tsx +118 -0
  71. package/src/auth/hooks/useAuth.ts +32 -0
  72. package/src/auth/index.tsx +108 -0
  73. package/src/auth/pages/EmailVerification.tsx +76 -0
  74. package/src/auth/pages/ErrorScreen.tsx +53 -0
  75. package/src/auth/pages/OtpInput.tsx +124 -0
  76. package/src/auth/pages/SignUp.tsx +303 -0
  77. package/src/auth/pages/Verifying.tsx +108 -0
  78. package/src/auth/pages/WalletSelection.tsx +53 -0
  79. package/src/auth/types.ts +23 -0
  80. package/src/auth/utils/url.ts +12 -0
  81. package/src/connector.ts +182 -0
  82. package/src/index.ts +25 -0
  83. package/src/shared/components/PoweredBy/index.tsx +26 -0
  84. package/src/shared/components/Screen/EllipticalRadial.tsx +65 -0
  85. package/src/shared/components/Screen/MultiRadialBackground.tsx +323 -0
  86. package/src/shared/components/Screen/index.tsx +65 -0
  87. package/src/shared/components/SignUpFooter/index.tsx +66 -0
  88. package/src/shared/components/StatusScreen/index.tsx +49 -0
  89. package/src/shared/components/TopNav/index.tsx +45 -0
  90. package/src/shared/hooks/useKitStore.ts +20 -0
  91. package/src/shared/utils/common.ts +26 -0
  92. package/src/shared/utils/store.ts +9 -0
  93. package/src/store.ts +44 -0
  94. package/src/types.ts +48 -0
package/src/index.ts ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @zerodev/wallet-react-ui
3
+ * React UI components and enhanced connector for ZeroDev Wallet SDK
4
+ */
5
+
6
+ // Auth
7
+ export { AuthFlow } from './auth'
8
+ export { useAuth } from './auth/hooks/useAuth'
9
+ export type { AuthMethod, AuthStep } from './auth/types'
10
+
11
+ // Connector
12
+ export type {
13
+ // SigningConfig,
14
+ ZeroDevKitConfig,
15
+ ZeroDevKitConnectorParams,
16
+ } from './connector.js'
17
+ export { zeroDevWallet } from './connector.js'
18
+
19
+ // Signing
20
+ // export type { SignatureRequestProps } from './signing'
21
+ // export { SignatureRequest } from './signing'
22
+ // export { usePendingRequest } from './signing/hooks/usePendingRequest.js'
23
+ // export { usePendingRequests } from './signing/hooks/usePendingRequests.js'
24
+ //
25
+ // export type { PendingRequest, Request, RequestMethod } from './types.js'
@@ -0,0 +1,26 @@
1
+ import { cn, Icon, Text } from '@zerodev/react-ui'
2
+ import type { CSSProperties } from 'react'
3
+
4
+ export function PoweredBy({
5
+ className,
6
+ style,
7
+ }: {
8
+ className?: string
9
+ style?: CSSProperties
10
+ }) {
11
+ return (
12
+ <div
13
+ className={cn(
14
+ 'zd:gap-1.5 zd:flex zd:flex-row zd:items-center',
15
+ className,
16
+ )}
17
+ >
18
+ <Text>Powered by</Text>
19
+ <Icon
20
+ name="zerodevLogo"
21
+ className="zd:h-[18px] zd:w-auto"
22
+ style={style}
23
+ />
24
+ </div>
25
+ )
26
+ }
@@ -0,0 +1,65 @@
1
+ interface RadialStop {
2
+ offset: number
3
+ color: string
4
+ opacity: number
5
+ }
6
+
7
+ export interface RadialLayer {
8
+ id: string
9
+ // Figma-exported gradientTransform matrix: the unit circle
10
+ // (center 0,0 radius 1) mapped to a rotated/scaled/translated ellipse.
11
+ matrix: [number, number, number, number, number, number]
12
+ // Opacity of the rect the gradient fills (Figma exports this on the <rect>).
13
+ fillOpacity: number
14
+ stops: RadialStop[]
15
+ }
16
+
17
+ // The rect each gradient fills, in the parent SVG's viewBox coordinates.
18
+ interface RadialRect {
19
+ x: number
20
+ y: number
21
+ width: number
22
+ height: number
23
+ }
24
+
25
+ export function EllipticalRadial({
26
+ layer,
27
+ rect,
28
+ }: {
29
+ layer: RadialLayer
30
+ rect: RadialRect
31
+ }) {
32
+ const { id, matrix, fillOpacity, stops } = layer
33
+
34
+ return (
35
+ <>
36
+ <defs>
37
+ <radialGradient
38
+ id={id}
39
+ cx="0"
40
+ cy="0"
41
+ r="1"
42
+ gradientUnits="userSpaceOnUse"
43
+ gradientTransform={`matrix(${matrix.join(' ')})`}
44
+ >
45
+ {stops.map((s, i) => (
46
+ <stop
47
+ key={s.color + i.toString()}
48
+ offset={s.offset}
49
+ stopColor={s.color}
50
+ stopOpacity={s.opacity}
51
+ />
52
+ ))}
53
+ </radialGradient>
54
+ </defs>
55
+ <rect
56
+ x={rect.x}
57
+ y={rect.y}
58
+ width={rect.width}
59
+ height={rect.height}
60
+ fill={`url(#${id})`}
61
+ fillOpacity={fillOpacity}
62
+ />
63
+ </>
64
+ )
65
+ }
@@ -0,0 +1,323 @@
1
+ import { useId } from 'react'
2
+
3
+ import { EllipticalRadial, type RadialLayer } from './EllipticalRadial'
4
+
5
+ // The design frame: the gradient layers live on a 412×812 rect offset to
6
+ // (-6,-6) so they bleed 6px past the 400×800 card on every side — that 6px
7
+ // reveal is the wrapper's gradient border. The matrices below are the
8
+ // Figma-exported gradientTransform values verbatim, so rendering the SVG with
9
+ // a matching viewBox + preserveAspectRatio="none" reproduces the design 1:1
10
+ // while still scaling to any container size.
11
+ const FRAME_VIEWBOX = '-6 -6 412 812'
12
+
13
+ export function MultiRadialBackground() {
14
+ // The colorful gradient now lives entirely in CardGlow. This base layer is
15
+ // just a flat, soft warm-white fill behind it so CardGlow's colors read
16
+ // bright and clean, with no gray muddiers underneath.
17
+ return (
18
+ <div className="zd:absolute zd:inset-0 zd:z-0 zd:rounded-4xl zd:pointer-events-none">
19
+ <svg
20
+ width="100%"
21
+ height="100%"
22
+ viewBox={FRAME_VIEWBOX}
23
+ preserveAspectRatio="none"
24
+ role="img"
25
+ aria-label="Decorative gradient background"
26
+ >
27
+ <rect x="-6" y="-6" width="412" height="812" fill="#FBF7F2" />
28
+ </svg>
29
+ </div>
30
+ )
31
+ }
32
+
33
+ // On-card overlay (400×800), sitting ON TOP of the translucent card body so its
34
+ // color reads at full strength (the card at 0.8 opacity would otherwise wash a
35
+ // behind-card gradient to ~20%). This reproduces the vivid orange + blue that in
36
+ // the source design came from the decorative "Amorphic" blob raster overlaid on
37
+ // the lower card, plus the design's own white card glow (Figma paint6).
38
+ //
39
+ // The orange + blue lobes are NOT verbatim Figma layers — they reconstruct the
40
+ // blob's measured color footprint (orange across the lower card; blue lobe
41
+ // bottom-left ~x 0-150 / y 600-800) as gradients, per the decision to treat the
42
+ // blob as a background effect rather than ship the raster.
43
+ export function CardGlow() {
44
+ const baseId = useId()
45
+ // VERBATIM from the Figma `--Background-Brand-Gradient` token (6 stacked
46
+ // radial-gradients). CSS lists topmost-first; SVG paints last-on-top, so the
47
+ // array is the CSS list REVERSED. Each matrix is the CSS `rx% ry% at cx% cy%`
48
+ // converted over the 400×800 frame. An upward linear fade is applied on top
49
+ // (see the <rect> below) to soften the gradient toward the top.
50
+ const layers: RadialLayer[] = [
51
+ {
52
+ // (CSS #6) blue → top-right. x-radius widened so the lobe reaches further
53
+ // left; y-radius kept short so it stays up top and doesn't bleed into the
54
+ // orange band.
55
+ id: `${baseId}-blue`,
56
+ matrix: [1850, 0, 0, 645, 381.44, 0],
57
+ fillOpacity: 1,
58
+ stops: [
59
+ { offset: 0, color: '#45ABFB', opacity: 0.66 },
60
+ { offset: 0.4615, color: '#1E9AFB', opacity: 0.35 },
61
+ { offset: 1, color: '#45ABFB', opacity: 0 },
62
+ ],
63
+ },
64
+ {
65
+ // (CSS #5) offWhite → blue, very faint
66
+ id: `${baseId}-offwhite-tr`,
67
+ matrix: [55.28, 0, 0, 773.36, 386.68, 13.28],
68
+ fillOpacity: 1,
69
+ stops: [
70
+ { offset: 0, color: '#F7F5F0', opacity: 0.1 },
71
+ { offset: 1, color: '#45ABFB', opacity: 0 },
72
+ ],
73
+ },
74
+ {
75
+ // (CSS #4) warmGrey
76
+ id: `${baseId}-warmgrey`,
77
+ matrix: [326.72, 0, 0, 855.52, 0, 50.32],
78
+ fillOpacity: 1,
79
+ stops: [
80
+ { offset: 0, color: '#E3CFC3', opacity: 1 },
81
+ { offset: 1, color: '#E3CFC3', opacity: 0 },
82
+ ],
83
+ },
84
+ {
85
+ // (CSS #3) orange — spans the full width across the bottom ~30%, uniform.
86
+ id: `${baseId}-orange`,
87
+ matrix: [580, 0, 0, 380, 200, 835],
88
+ fillOpacity: 1,
89
+ stops: [
90
+ { offset: 0, color: '#EE6C2E', opacity: 0.8 },
91
+ { offset: 0.55, color: '#EE6C2E', opacity: 0.6 },
92
+ { offset: 1, color: '#EE6C2E', opacity: 0.15 },
93
+ ],
94
+ },
95
+ {
96
+ // (CSS #2) peach — spread further up (~30% of the bottom), a bit more vivid
97
+ id: `${baseId}-peach`,
98
+ matrix: [520, 0, 0, 560, 180, 830],
99
+ fillOpacity: 1,
100
+ stops: [
101
+ { offset: 0, color: '#FAC8AC', opacity: 1 },
102
+ { offset: 1, color: '#F27B3E', opacity: 0 },
103
+ ],
104
+ },
105
+ {
106
+ // (CSS #1) white glow (topmost of the colour stack)
107
+ id: `${baseId}-white-bl`,
108
+ matrix: [90.72, 0, 0, 550.48, 38.04, 702.64],
109
+ fillOpacity: 1,
110
+ stops: [
111
+ { offset: 0, color: 'white', opacity: 0.58 },
112
+ { offset: 1, color: 'white', opacity: 0 },
113
+ ],
114
+ },
115
+ ]
116
+
117
+ return (
118
+ <div className="zd:absolute zd:inset-0 zd:z-0 zd:rounded-4xl zd:pointer-events-none zd:overflow-hidden">
119
+ <svg
120
+ width="100%"
121
+ height="100%"
122
+ viewBox="0 0 400 800"
123
+ preserveAspectRatio="none"
124
+ role="presentation"
125
+ >
126
+ {layers.map((layer) => (
127
+ <EllipticalRadial
128
+ key={layer.id}
129
+ layer={layer}
130
+ rect={{ x: 0, y: 0, width: 400, height: 800 }}
131
+ />
132
+ ))}
133
+ {/* Vertical white fade — strongest at the bottom, easing linearly to
134
+ nothing at the top. Softens the lower (most vivid) part of the
135
+ gradient. A gradient layer (not an opaque card) so the frosted
136
+ buttons composite over it cleanly. */}
137
+ <defs>
138
+ <linearGradient
139
+ id={`${baseId}-vfade`}
140
+ x1="0"
141
+ y1="0"
142
+ x2="0"
143
+ y2="800"
144
+ gradientUnits="userSpaceOnUse"
145
+ >
146
+ <stop offset="0" stopColor="white" stopOpacity="0.55" />
147
+ <stop offset="0.45" stopColor="white" stopOpacity="0.52" />
148
+ <stop offset="0.65" stopColor="white" stopOpacity="0.5" />
149
+ <stop offset="0.85" stopColor="white" stopOpacity="0.5" />
150
+ <stop offset="1" stopColor="white" stopOpacity="0.5" />
151
+ </linearGradient>
152
+ </defs>
153
+ <rect
154
+ x="0"
155
+ y="0"
156
+ width="400"
157
+ height="800"
158
+ fill={`url(#${baseId}-vfade)`}
159
+ />
160
+ {/* Blue — a diagonal linear gradient from the bottom-left corner fading
161
+ to transparent toward the top-right, so it blends smoothly into the
162
+ orange with no hard edge. On top of the fade so #A7B8E2 reads true. */}
163
+ <defs>
164
+ <linearGradient
165
+ id={`${baseId}-bluefade`}
166
+ x1="0"
167
+ y1="800"
168
+ x2="120"
169
+ y2="640"
170
+ gradientUnits="userSpaceOnUse"
171
+ >
172
+ <stop offset="0" stopColor="#A7B8E2" stopOpacity="1" />
173
+ <stop offset="0.33" stopColor="#A7B8E2" stopOpacity="0.7" />
174
+ <stop offset="0.66" stopColor="#A7B8E2" stopOpacity="0.35" />
175
+ <stop offset="1" stopColor="#A7B8E2" stopOpacity="0" />
176
+ </linearGradient>
177
+ </defs>
178
+ <rect
179
+ x="0"
180
+ y="0"
181
+ width="400"
182
+ height="800"
183
+ fill={`url(#${baseId}-bluefade)`}
184
+ />
185
+ </svg>
186
+ </div>
187
+ )
188
+ }
189
+
190
+ // The wrapper's gradient BORDER. Renders full-frame behind the inner card; the
191
+ // card sits on top with a 6px margin so this gradient shows as the border ring.
192
+ // Uses normalized cx/cy/rx/ry radial gradients (scale-transform) over a #130E0B
193
+ // base — the original border look.
194
+ type BorderLayer = {
195
+ id: string
196
+ cx: number
197
+ cy: number
198
+ rx: number
199
+ ry: number
200
+ stops: { offset: number; color: string; opacity: number }[]
201
+ }
202
+
203
+ export function WrapperBorder() {
204
+ const baseId = useId()
205
+ // VERBATIM from the Figma `--Background-Brand-Gradient` token (6 stacked
206
+ // radial-gradients). CSS lists topmost-first, SVG paints last-on-top, so the
207
+ // array is the CSS list REVERSED. Each CSS `rx% ry% at cx% cy%` maps to
208
+ // normalized cx/cy/rx/ry (÷100); the inline <radialGradient> below applies
209
+ // the cx=(cx/rx)*100% + scale(rx,ry) convention.
210
+ const layers: BorderLayer[] = [
211
+ {
212
+ id: `${baseId}-g6`, // (CSS #6) blue, top-right
213
+ cx: 0.9536,
214
+ cy: 0.0,
215
+ rx: 0.9385,
216
+ ry: 1.2256,
217
+ stops: [
218
+ { offset: 0, color: '#45ABFB', opacity: 0.6 },
219
+ { offset: 0.4615, color: '#1E9AFB', opacity: 0.32 },
220
+ { offset: 1, color: '#45ABFB', opacity: 0 },
221
+ ],
222
+ },
223
+ {
224
+ id: `${baseId}-g5`, // (CSS #5) faint light, top-right
225
+ cx: 0.9667,
226
+ cy: 0.0166,
227
+ rx: 0.1382,
228
+ ry: 0.9667,
229
+ stops: [
230
+ { offset: 0, color: '#F7F5F0', opacity: 0.1 },
231
+ { offset: 1, color: '#45ABFB', opacity: 0 },
232
+ ],
233
+ },
234
+ {
235
+ id: `${baseId}-g4`, // (CSS #4) warm grey, top-left
236
+ cx: 0.0,
237
+ cy: 0.0629,
238
+ rx: 0.8168,
239
+ ry: 1.0694,
240
+ stops: [
241
+ { offset: 0, color: '#E3CFC3', opacity: 1 },
242
+ { offset: 1, color: '#E3CFC3', opacity: 0 },
243
+ ],
244
+ },
245
+ {
246
+ id: `${baseId}-g3`, // (CSS #3) orange, bottom-left
247
+ cx: 0.0393,
248
+ cy: 1.0,
249
+ rx: 1.0628,
250
+ ry: 1.0303,
251
+ stops: [
252
+ { offset: 0, color: '#FF6B1F', opacity: 1 },
253
+ { offset: 0.476, color: '#E76000', opacity: 0.72 },
254
+ { offset: 1, color: '#E76000', opacity: 0 },
255
+ ],
256
+ },
257
+ {
258
+ id: `${baseId}-g2`, // (CSS #2) peach, bottom-center
259
+ cx: 0.3476,
260
+ cy: 1.0,
261
+ rx: 0.9614,
262
+ ry: 0.338,
263
+ stops: [
264
+ { offset: 0, color: '#FAC8AC', opacity: 0.7 },
265
+ { offset: 1, color: '#F27B3E', opacity: 0 },
266
+ ],
267
+ },
268
+ {
269
+ id: `${baseId}-g1`, // (CSS #1) white glow, bottom-left
270
+ cx: 0.0951,
271
+ cy: 0.8783,
272
+ rx: 0.2268,
273
+ ry: 0.6881,
274
+ stops: [
275
+ { offset: 0, color: '#FFFFFF', opacity: 0.58 },
276
+ { offset: 1, color: '#FFFFFF', opacity: 0 },
277
+ ],
278
+ },
279
+ ]
280
+
281
+ return (
282
+ <div className="zd:absolute zd:inset-0 zd:z-0 zd:rounded-4xl zd:pointer-events-none zd:overflow-hidden">
283
+ <svg
284
+ width="100%"
285
+ height="100%"
286
+ preserveAspectRatio="none"
287
+ role="presentation"
288
+ >
289
+ <rect width="100%" height="100%" fill="#130E0B" />
290
+ <defs>
291
+ {layers.map((layer) => (
292
+ <radialGradient
293
+ key={layer.id}
294
+ id={layer.id}
295
+ cx={`${(layer.cx / layer.rx) * 100}%`}
296
+ cy={`${(layer.cy / layer.ry) * 100}%`}
297
+ r="100%"
298
+ gradientUnits="objectBoundingBox"
299
+ gradientTransform={`scale(${layer.rx}, ${layer.ry})`}
300
+ >
301
+ {layer.stops.map((s, i) => (
302
+ <stop
303
+ key={s.color + i.toString()}
304
+ offset={s.offset}
305
+ stopColor={s.color}
306
+ stopOpacity={s.opacity}
307
+ />
308
+ ))}
309
+ </radialGradient>
310
+ ))}
311
+ </defs>
312
+ {layers.map((layer) => (
313
+ <rect
314
+ key={`${layer.id}-rect`}
315
+ width="100%"
316
+ height="100%"
317
+ fill={`url(#${layer.id})`}
318
+ />
319
+ ))}
320
+ </svg>
321
+ </div>
322
+ )
323
+ }
@@ -0,0 +1,65 @@
1
+ import { cn } from '@zerodev/react-ui'
2
+ import type { CSSProperties, ReactNode } from 'react'
3
+ import { TOP_NAV_HEIGHT } from '../TopNav'
4
+ import {
5
+ CardGlow,
6
+ MultiRadialBackground,
7
+ WrapperBorder,
8
+ } from './MultiRadialBackground'
9
+
10
+ const CONTENT_PADDING_TOP = TOP_NAV_HEIGHT + 16
11
+
12
+ export function Screen({
13
+ children,
14
+ className,
15
+ contentClassName,
16
+ style,
17
+ topNav,
18
+ }: {
19
+ children: ReactNode
20
+ className?: string | undefined
21
+ contentClassName?: string | undefined
22
+ style?: CSSProperties | undefined
23
+ topNav?: ReactNode
24
+ }) {
25
+ return (
26
+ <div
27
+ className={cn(
28
+ 'zd:flex zd:flex-col zd:overflow-hidden zd:text-left',
29
+ 'zd:fixed zd:inset-0 zd:z-50 zd:w-screen zd:h-[100dvh] zd:rounded-[36px]',
30
+ 'zd:sm:relative zd:sm:z-auto zd:sm:w-100 zd:sm:max-w-full zd:sm:h-[810px] zd:sm:max-h-[100dvh]',
31
+ className,
32
+ )}
33
+ style={style}
34
+ >
35
+ {/* Gradient border layer — fills the frame; the card on top has a 6px
36
+ margin so this shows as the border ring. */}
37
+ <WrapperBorder />
38
+ {/* Inner card sits on top with a 6px margin, exposing the border ring.
39
+ Its own gradient (base + CardGlow) fills the card so the colour stays
40
+ vivid and distinct from the border. */}
41
+ <div
42
+ className={cn(
43
+ 'zd:flex zd:flex-1 zd:flex-col zd:m-1.5 zd:px-4 zd:overflow-hidden zd:rounded-4xl zd:relative',
44
+ contentClassName,
45
+ )}
46
+ // clip-path clips backdrop-filter to the rounded corners; plain
47
+ // overflow-hidden does not (WebKit/Chromium leaves scrolled blurred
48
+ // children bleeding past the radius). Must match rounded-4xl (32px).
49
+ style={{ clipPath: 'inset(0 round 32px)' }}
50
+ >
51
+ <MultiRadialBackground />
52
+ <CardGlow />
53
+ <div className="zd:relative zd:z-10 zd:flex zd:flex-1 zd:flex-col zd:min-h-0">
54
+ {topNav}
55
+ <div
56
+ className="zd:flex zd:flex-1 zd:flex-col zd:min-h-0 zd:overflow-y-auto zd:overflow-x-hidden"
57
+ style={{ paddingTop: `${CONTENT_PADDING_TOP}px` }}
58
+ >
59
+ {children}
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ )
65
+ }
@@ -0,0 +1,66 @@
1
+ import { Text } from '@zerodev/react-ui'
2
+ import { PoweredBy } from '../PoweredBy'
3
+
4
+ export function SignUpFooter({
5
+ termsAndConditionsUrl,
6
+ privacyPolicyUrl,
7
+ agreedToTerms,
8
+ setAgreedToTerms,
9
+ highlight = false,
10
+ }: {
11
+ termsAndConditionsUrl?: string | undefined
12
+ privacyPolicyUrl?: string | undefined
13
+ agreedToTerms: boolean
14
+ setAgreedToTerms: (agreed: boolean) => void
15
+ highlight?: boolean
16
+ }) {
17
+ const showAgreement = !!(termsAndConditionsUrl || privacyPolicyUrl)
18
+
19
+ return (
20
+ <div className="zd:flex zd:flex-col zd:items-center zd:gap-5">
21
+ {showAgreement && (
22
+ <div
23
+ className={`zd:flex zd:flex-row zd:items-center zd:gap-2 zd:rounded-md zd:p-2 zd:transition-colors ${
24
+ highlight
25
+ ? 'zd:border zd:border-negative'
26
+ : 'zd:border zd:border-transparent'
27
+ }`}
28
+ >
29
+ <input
30
+ type="checkbox"
31
+ checked={agreedToTerms}
32
+ onChange={(e) => setAgreedToTerms(e.target.checked)}
33
+ className="zd:cursor-pointer zd:[color-scheme:light]"
34
+ />
35
+ <Text className="zd:flex-1">
36
+ I agree to the{' '}
37
+ {termsAndConditionsUrl && (
38
+ <Text
39
+ as="a"
40
+ href={termsAndConditionsUrl}
41
+ target="_blank"
42
+ rel="noopener noreferrer"
43
+ className="zd:underline"
44
+ >
45
+ Terms & Conditions
46
+ </Text>
47
+ )}
48
+ {termsAndConditionsUrl && privacyPolicyUrl && ' and '}
49
+ {privacyPolicyUrl && (
50
+ <Text
51
+ as="a"
52
+ href={privacyPolicyUrl}
53
+ target="_blank"
54
+ rel="noopener noreferrer"
55
+ className="zd:underline"
56
+ >
57
+ Privacy Policy
58
+ </Text>
59
+ )}
60
+ </Text>
61
+ </div>
62
+ )}
63
+ <PoweredBy />
64
+ </div>
65
+ )
66
+ }
@@ -0,0 +1,49 @@
1
+ import { cn, Text } from '@zerodev/react-ui'
2
+ import type { ReactNode } from 'react'
3
+ import errorImg from '../../../../assets/states/error.webp?no-inline'
4
+ import loadingImg from '../../../../assets/states/loading.webp?no-inline'
5
+ import sendImg from '../../../../assets/states/send.webp?no-inline'
6
+ import successImg from '../../../../assets/states/success.webp?no-inline'
7
+
8
+ export type StateImageName = 'error' | 'loading' | 'send' | 'success'
9
+
10
+ const STATE_IMAGES: Record<StateImageName, string> = {
11
+ error: errorImg,
12
+ loading: loadingImg,
13
+ send: sendImg,
14
+ success: successImg,
15
+ }
16
+
17
+ export interface StatusScreenProps {
18
+ imageName: StateImageName
19
+ title: string
20
+ children: ReactNode
21
+ className?: string
22
+ }
23
+
24
+ export function StatusScreen({
25
+ imageName,
26
+ title,
27
+ children,
28
+ className,
29
+ }: StatusScreenProps) {
30
+ return (
31
+ <div
32
+ className={cn('zd:flex zd:flex-col zd:gap-8 zd:items-center', className)}
33
+ >
34
+ <img
35
+ src={STATE_IMAGES[imageName]}
36
+ alt={imageName}
37
+ className="zd:w-[118px] zd:h-[118px] zd:bg-transparent"
38
+ />
39
+ <div className="zd:flex zd:flex-col zd:gap-4 zd:items-center">
40
+ <Text className="zd:text-h2 zd:text-center zd:whitespace-pre-wrap">
41
+ {title}
42
+ </Text>
43
+ <Text className="zd:text-center zd:whitespace-pre-wrap">
44
+ {children}
45
+ </Text>
46
+ </div>
47
+ </div>
48
+ )
49
+ }
@@ -0,0 +1,45 @@
1
+ import { cn, IconButton, Text } from '@zerodev/react-ui'
2
+ import type { ReactNode } from 'react'
3
+
4
+ export const TOP_NAV_HEIGHT = 52
5
+
6
+ export function TopNav({
7
+ onBack,
8
+ onClose,
9
+ title,
10
+ logo,
11
+ className,
12
+ }: {
13
+ onBack?: () => void
14
+ onClose: () => void
15
+ title?: string
16
+ logo?: ReactNode
17
+ className?: string
18
+ }) {
19
+ return (
20
+ <div
21
+ className={cn(
22
+ 'zd:absolute zd:top-4 zd:left-0 zd:right-0 zd:flex zd:flex-row zd:items-center zd:justify-between',
23
+ className,
24
+ )}
25
+ style={{ height: TOP_NAV_HEIGHT }}
26
+ >
27
+ {onBack ? (
28
+ <IconButton iconName="chevronLeft" onClick={onBack} />
29
+ ) : (
30
+ <div className="zd:h-13 zd:w-13" />
31
+ )}
32
+ {logo && (
33
+ <div className="zd:absolute zd:left-0 zd:right-0 zd:flex zd:items-center zd:justify-center zd:pointer-events-none">
34
+ {logo}
35
+ </div>
36
+ )}
37
+ {title && (
38
+ <div className="zd:absolute zd:left-0 zd:right-0 zd:flex zd:items-center zd:justify-center zd:pointer-events-none">
39
+ <Text className="zd:text-body1">{title}</Text>
40
+ </div>
41
+ )}
42
+ <IconButton iconName="x" onClick={onClose} />
43
+ </div>
44
+ )
45
+ }
@@ -0,0 +1,20 @@
1
+ import { useConfig } from 'wagmi'
2
+ import type { createStore } from '../../store'
3
+
4
+ type KitStore = ReturnType<typeof createStore>
5
+
6
+ /**
7
+ * Returns the zustand store attached to the `zeroDevWallet` connector.
8
+ * Throws when the kit connector isn't present on the wagmi config.
9
+ *
10
+ * Read individual fields with `useStore(useKitStore(), (s) => s.field)`.
11
+ */
12
+ export function useKitStore(): KitStore {
13
+ const config = useConfig()
14
+ const connector = config.connectors.find((c) => c.id === 'zerodev-wallet')
15
+ if (!connector || !('getKitStore' in connector)) {
16
+ throw new Error('useKitStore must be used with the zeroDevWallet connector')
17
+ }
18
+ // @ts-expect-error - getKitStore is a custom method on the kit connector
19
+ return connector.getKitStore()
20
+ }