@medialane/ui 0.152.0 → 0.154.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/collection-hero-banner.cjs.map +1 -1
- package/dist/components/collection-hero-banner.d.cts +0 -2
- package/dist/components/collection-hero-banner.d.ts +0 -2
- package/dist/components/collection-hero-banner.js.map +1 -1
- package/dist/components/drop-preview-card.test.d.cts +3 -2
- package/dist/components/drop-preview-card.test.d.ts +3 -2
- package/dist/components/dual-price.cjs +0 -2
- package/dist/components/dual-price.cjs.map +1 -1
- package/dist/components/dual-price.d.cts +0 -2
- package/dist/components/dual-price.d.ts +0 -2
- package/dist/components/dual-price.js +0 -2
- package/dist/components/dual-price.js.map +1 -1
- package/dist/components/fast-mint/fast-mint.d.cts +2 -2
- package/dist/components/fast-mint/fast-mint.d.ts +2 -2
- package/dist/components/image.cjs.map +1 -1
- package/dist/components/image.js.map +1 -1
- package/dist/components/ip-type-fields.cjs.map +1 -1
- package/dist/components/ip-type-fields.d.cts +0 -6
- package/dist/components/ip-type-fields.d.ts +0 -6
- package/dist/components/ip-type-fields.js.map +1 -1
- package/dist/components/launchpad-cta-banner.cjs.map +1 -1
- package/dist/components/launchpad-cta-banner.d.cts +0 -4
- package/dist/components/launchpad-cta-banner.d.ts +0 -4
- package/dist/components/launchpad-cta-banner.js.map +1 -1
- package/dist/components/launchpad-services.cjs.map +1 -1
- package/dist/components/launchpad-services.d.cts +0 -3
- package/dist/components/launchpad-services.d.ts +0 -3
- package/dist/components/launchpad-services.js.map +1 -1
- package/dist/components/link.cjs.map +1 -1
- package/dist/components/link.d.cts +0 -2
- package/dist/components/link.d.ts +0 -2
- package/dist/components/link.js.map +1 -1
- package/dist/components/medialane-collection-card.cjs.map +1 -1
- package/dist/components/medialane-collection-card.d.cts +0 -1
- package/dist/components/medialane-collection-card.d.ts +0 -1
- package/dist/components/medialane-collection-card.js.map +1 -1
- package/dist/components/portfolio-section.cjs.map +1 -1
- package/dist/components/portfolio-section.d.cts +0 -4
- package/dist/components/portfolio-section.d.ts +0 -4
- package/dist/components/portfolio-section.js.map +1 -1
- package/dist/components/router.cjs.map +1 -1
- package/dist/components/router.js.map +1 -1
- package/dist/data/coins.cjs.map +1 -1
- package/dist/data/coins.d.cts +0 -2
- package/dist/data/coins.d.ts +0 -2
- package/dist/data/coins.js.map +1 -1
- package/dist/medialane.css +1 -47
- package/dist/utils/ipfs.cjs.map +1 -1
- package/dist/utils/ipfs.d.cts +0 -7
- package/dist/utils/ipfs.d.ts +0 -7
- package/dist/utils/ipfs.js.map +1 -1
- package/dist/utils/profile-identity.cjs +1 -1
- package/dist/utils/profile-identity.cjs.map +1 -1
- package/dist/utils/profile-identity.d.cts +1 -13
- package/dist/utils/profile-identity.d.ts +1 -13
- package/dist/utils/profile-identity.js +1 -1
- package/dist/utils/profile-identity.js.map +1 -1
- package/dist/utils/use-usd-prices.cjs.map +1 -1
- package/dist/utils/use-usd-prices.d.cts +0 -2
- package/dist/utils/use-usd-prices.d.ts +0 -2
- package/dist/utils/use-usd-prices.js.map +1 -1
- package/package.json +1 -1
package/dist/medialane.css
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/* Medialane Design System — shared CSS
|
|
2
|
-
Imported by apps via: import "@medialane/ui/styles"
|
|
3
|
-
Do NOT import @tailwind directives here — apps handle that.
|
|
4
|
-
All classes here are pure CSS, no @apply. */
|
|
5
|
-
|
|
6
|
-
/* ── Scrollbars ───────────────────────────────────────────────────────── */
|
|
7
1
|
::-webkit-scrollbar { width: 4px; height: 4px; }
|
|
8
2
|
::-webkit-scrollbar-track { background: transparent; }
|
|
9
3
|
::-webkit-scrollbar-thumb { background: hsl(var(--border)); border-radius: 4px; }
|
|
@@ -12,33 +6,24 @@
|
|
|
12
6
|
.scrollbar-hide { scrollbar-width: none; }
|
|
13
7
|
.scrollbar-hide::-webkit-scrollbar { display: none; }
|
|
14
8
|
|
|
15
|
-
/* ── Safe-area insets (notched/home-indicator devices) ──────────────────── */
|
|
16
9
|
.pb-safe { padding-bottom: max(env(safe-area-inset-bottom), 0px); }
|
|
17
10
|
.pt-safe { padding-top: max(env(safe-area-inset-top), 0px); }
|
|
18
11
|
|
|
19
|
-
/* ── Glass ────────────────────────────────────────────────────────────── */
|
|
20
12
|
.glass {
|
|
21
13
|
background: rgba(10, 14, 30, 0.60);
|
|
22
14
|
backdrop-filter: blur(16px);
|
|
23
|
-
-webkit-backdrop-filter: blur(16px);
|
|
24
15
|
border: 1px solid rgba(255, 255, 255, 0.06);
|
|
25
16
|
}
|
|
26
17
|
.glass-light {
|
|
27
18
|
background: rgba(255, 255, 255, 0.80);
|
|
28
19
|
backdrop-filter: blur(16px);
|
|
29
|
-
-webkit-backdrop-filter: blur(16px);
|
|
30
20
|
border: 1px solid rgba(255, 255, 255, 0.5);
|
|
31
21
|
}
|
|
32
22
|
|
|
33
|
-
/* ── Display face ─────────────────────────────────────────────────────── */
|
|
34
|
-
/* Only the page main header renders in the brand display face (Urbanist);
|
|
35
|
-
everything else stays on the body face. Apps set --font-display via
|
|
36
|
-
next/font; unset, h1 inherits the body face. */
|
|
37
23
|
h1 {
|
|
38
24
|
font-family: var(--font-display, inherit);
|
|
39
25
|
}
|
|
40
26
|
|
|
41
|
-
/* ── Typography gradients ─────────────────────────────────────────────── */
|
|
42
27
|
.gradient-text {
|
|
43
28
|
background: linear-gradient(135deg, #8a5cf6 0%, #5b4ce6 40%, #3b7bff 100%);
|
|
44
29
|
-webkit-background-clip: text;
|
|
@@ -64,14 +49,12 @@ h1 {
|
|
|
64
49
|
background-clip: text;
|
|
65
50
|
}
|
|
66
51
|
|
|
67
|
-
/* ── Price ────────────────────────────────────────────────────────────── */
|
|
68
52
|
.price-value {
|
|
69
53
|
color: hsl(var(--price));
|
|
70
54
|
font-weight: 700;
|
|
71
55
|
font-variant-numeric: tabular-nums;
|
|
72
56
|
}
|
|
73
57
|
|
|
74
|
-
/* ── Section label ────────────────────────────────────────────────────── */
|
|
75
58
|
.section-label {
|
|
76
59
|
font-size: 0.625rem;
|
|
77
60
|
font-weight: 700;
|
|
@@ -80,7 +63,6 @@ h1 {
|
|
|
80
63
|
color: hsl(var(--muted-foreground));
|
|
81
64
|
}
|
|
82
65
|
|
|
83
|
-
/* ── Pill badge ───────────────────────────────────────────────────────── */
|
|
84
66
|
.pill-badge {
|
|
85
67
|
display: inline-flex;
|
|
86
68
|
align-items: center;
|
|
@@ -96,15 +78,13 @@ h1 {
|
|
|
96
78
|
color: hsl(var(--primary));
|
|
97
79
|
}
|
|
98
80
|
|
|
99
|
-
/* Navigation canvas */
|
|
100
81
|
.nav-canvas-overlay {
|
|
101
82
|
position: fixed;
|
|
102
83
|
inset: 0;
|
|
103
84
|
z-index: 100;
|
|
104
85
|
backdrop-filter: blur(48px) saturate(1.5);
|
|
105
|
-
-webkit-backdrop-filter: blur(48px) saturate(1.5);
|
|
106
86
|
}
|
|
107
|
-
|
|
87
|
+
|
|
108
88
|
.card-base {
|
|
109
89
|
border-radius: calc(var(--radius) * 1.25);
|
|
110
90
|
border: 1px solid hsl(var(--border));
|
|
@@ -120,7 +100,6 @@ h1 {
|
|
|
120
100
|
position: relative;
|
|
121
101
|
}
|
|
122
102
|
|
|
123
|
-
/* ── Background grid ──────────────────────────────────────────────────── */
|
|
124
103
|
.bg-grid {
|
|
125
104
|
background-image:
|
|
126
105
|
linear-gradient(hsl(var(--border) / 0.4) 1px, transparent 1px),
|
|
@@ -128,11 +107,9 @@ h1 {
|
|
|
128
107
|
background-size: 48px 48px;
|
|
129
108
|
}
|
|
130
109
|
|
|
131
|
-
/* ── Snap scroll ──────────────────────────────────────────────────────── */
|
|
132
110
|
.snap-x-mandatory { scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; }
|
|
133
111
|
.snap-start { scroll-snap-align: start; }
|
|
134
112
|
|
|
135
|
-
/* ── Keyframes ────────────────────────────────────────────────────────── */
|
|
136
113
|
@keyframes float {
|
|
137
114
|
0%, 100% { transform: translateY(0px); }
|
|
138
115
|
50% { transform: translateY(-10px); }
|
|
@@ -163,39 +140,32 @@ h1 {
|
|
|
163
140
|
50% { background-position: 100% 50%; }
|
|
164
141
|
}
|
|
165
142
|
|
|
166
|
-
/* ── Animation utilities ──────────────────────────────────────────────── */
|
|
167
143
|
.animate-float { animation: float 5s ease-in-out infinite; }
|
|
168
144
|
.animate-pulse-glow { animation: pulse-glow 2s ease-in-out infinite; }
|
|
169
145
|
.animate-spin-slow { animation: spin-slow 24s linear infinite; }
|
|
170
146
|
.animate-sparkle { animation: pulse-glow 2.5s ease-in-out infinite; }
|
|
171
147
|
.animate-kenburns { animation: kenburns 8s ease-in-out infinite alternate; transform-origin: center center; }
|
|
172
148
|
|
|
173
|
-
/* ── Reduced motion ───────────────────────────────────────────────────── */
|
|
174
149
|
@media (prefers-reduced-motion: reduce) {
|
|
175
150
|
.animate-float, .animate-pulse-glow, .animate-spin-slow,
|
|
176
151
|
.animate-sparkle, .animate-kenburns { animation: none; }
|
|
177
152
|
}
|
|
178
153
|
|
|
179
|
-
/* ── Input spinners (price inputs use custom suffix) ──────────────────── */
|
|
180
154
|
input[type="number"]::-webkit-outer-spin-button,
|
|
181
155
|
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
|
|
182
156
|
input[type="number"] { -moz-appearance: textfield; }
|
|
183
157
|
|
|
184
|
-
/* ── Wrapper that adds a 2px animated gradient border around a .card-base card ── */
|
|
185
158
|
.card-exclusive-wrapper {
|
|
186
159
|
padding: 2px;
|
|
187
160
|
border-radius: calc(var(--radius) * 1.25 + 2px);
|
|
188
161
|
}
|
|
189
162
|
|
|
190
|
-
/* ── Animated gradient border (marketplace buy button) ────────────────── */
|
|
191
163
|
.btn-border-animated {
|
|
192
164
|
background: linear-gradient(270deg, #3b7bff, #8a5cf6, #f6608f, #fb8b46, #3b7bff);
|
|
193
165
|
background-size: 300% 300%;
|
|
194
166
|
animation: border-flow 5s ease infinite;
|
|
195
167
|
}
|
|
196
168
|
|
|
197
|
-
/* ── Action button — animated gradient border via ::before mask ────────── */
|
|
198
|
-
/* Set --ml-grad on the element to choose the border gradient. */
|
|
199
169
|
.ml-gbtn { position: relative; }
|
|
200
170
|
.ml-gbtn::before {
|
|
201
171
|
content: '';
|
|
@@ -213,10 +183,6 @@ input[type="number"] { -moz-appearance: textfield; }
|
|
|
213
183
|
}
|
|
214
184
|
.ml-gbtn:hover::before { padding: 2px; animation-duration: 1.6s; }
|
|
215
185
|
|
|
216
|
-
/* ── Nav wallet trigger — rotating brand-gradient ring ──────────────────── */
|
|
217
|
-
/* Not connected: the ring rotates continuously (ambient "connect here" cue, */
|
|
218
|
-
/* no icon inside). Connected: the ring stops and settles to a quiet static */
|
|
219
|
-
/* accent, the trigger's own User glyph renders in the center instead. */
|
|
220
186
|
.ml-nav-wallet-ring {
|
|
221
187
|
position: absolute;
|
|
222
188
|
inset: 0;
|
|
@@ -233,10 +199,6 @@ input[type="number"] { -moz-appearance: textfield; }
|
|
|
233
199
|
animation: none;
|
|
234
200
|
}
|
|
235
201
|
|
|
236
|
-
/* ── Static two-stop signature ring — Collections/Creators surfaces ────── */
|
|
237
|
-
/* Same masked-ring technique as .ml-nav-wallet-ring, but static (no spin) and
|
|
238
|
-
scoped to the two colors this section owns (brand-blue -> brand-purple) —
|
|
239
|
-
see medialane-core reference_brand_colors_and_gradients section-theming map. */
|
|
240
202
|
.ml-collection-ring {
|
|
241
203
|
position: absolute;
|
|
242
204
|
inset: 0;
|
|
@@ -249,10 +211,6 @@ input[type="number"] { -moz-appearance: textfield; }
|
|
|
249
211
|
pointer-events: none;
|
|
250
212
|
}
|
|
251
213
|
|
|
252
|
-
/* ── Hover-only branded gradient border (launchpad cards) ───────────────── */
|
|
253
|
-
/* The resting state keeps the card's own hairline; on hover-capable devices */
|
|
254
|
-
/* the full-spectrum ring fades in over it. Decoration only — never carries */
|
|
255
|
-
/* information (mobile users lose nothing). */
|
|
256
214
|
.ml-grad-hover-border { position: relative; }
|
|
257
215
|
.ml-grad-hover-border::before {
|
|
258
216
|
content: '';
|
|
@@ -276,9 +234,6 @@ input[type="number"] { -moz-appearance: textfield; }
|
|
|
276
234
|
100% { background-position: 220% 50%; }
|
|
277
235
|
}
|
|
278
236
|
|
|
279
|
-
/* ── Collector card material (MedialaneCollectionCard) ────────────────── */
|
|
280
|
-
/* Light by default (matches the app's light-theme root, no .dark ancestor);
|
|
281
|
-
.dark overrides restore the original always-dark foil-on-navy look. */
|
|
282
237
|
.ml-card-material {
|
|
283
238
|
background:
|
|
284
239
|
radial-gradient(120% 90% at 0% 0%, rgba(59,123,255,0.07), transparent 55%),
|
|
@@ -292,7 +247,6 @@ input[type="number"] { -moz-appearance: textfield; }
|
|
|
292
247
|
#0a0e1f;
|
|
293
248
|
}
|
|
294
249
|
|
|
295
|
-
/* ── Action dialog keyframes ──────────────────────────────────────────── */
|
|
296
250
|
@keyframes mlFade {
|
|
297
251
|
from { opacity: 0; }
|
|
298
252
|
to { opacity: 1; }
|
package/dist/utils/ipfs.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/ipfs.ts"],"sourcesContent":["export const DEFAULT_GATEWAY = \"https://gateway.pinata.cloud/ipfs/\";\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\", \"ipfs:\"]);\n\nconst KNOWN_IPFS_GATEWAY_HOSTS = /(^|\\.)(mypinata\\.cloud|pinata\\.cloud|ipfs\\.io|dweb\\.link|cloudflare-ipfs\\.com|nftstorage\\.link|w3s\\.link)$/i;\n\nconst DEFAULT_WIDTH = 1200;\n\nfunction withSize(url: string, gateway: string, width: number | null): string {\n if (!width || !url.startsWith(gateway)) return url;\n const sep = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${sep}img-width=${width}&img-format=webp&img-quality=80`;\n}\n\nexport function ipfsToHttp(\n uri: string | null | undefined,\n opts: { gateway?: string; width?: number | null } = {}\n): string {\n const gateway = opts.gateway ?? DEFAULT_GATEWAY;\n const width = opts.width === undefined ? DEFAULT_WIDTH : opts.width;\n\n if (!uri) return \"\";\n if (uri.startsWith(\"ipfs://\")) {\n return withSize(uri.replace(\"ipfs://\", gateway), gateway, width);\n }\n if (uri.startsWith(\"data:image/\")) {\n return uri;\n }\n try {\n const parsed = new URL(uri);\n if (!ALLOWED_PROTOCOLS.has(parsed.protocol)) return \"\";\n\n if (KNOWN_IPFS_GATEWAY_HOSTS.test(parsed.hostname)) {\n const match = parsed.pathname.match(/\\/ipfs\\/(.+)$/);\n if (match) return withSize(`${gateway}${match[1]}`, gateway, width);\n }\n } catch {\n return \"\";\n }\n return uri;\n}\n\nexport interface DisplayUrlOptions {\n gateway?: string;\n proxyPath?: string;\n placeholder?: string;\n}\n\nexport function toDisplayUrl(\n uri: string | null | undefined,\n opts: DisplayUrlOptions = {},\n): string {\n const gateway = opts.gateway ?? DEFAULT_GATEWAY;\n const placeholder = opts.placeholder ?? \"/placeholder.svg\";\n const proxyPath = opts.proxyPath ?? \"/api/img\";\n\n if (!uri) return placeholder;\n if (uri.startsWith(\"data:image/\")) return uri;\n\n const resolved = ipfsToHttp(uri, { gateway });\n if (resolved.startsWith(gateway)) return resolved;\n\n if (uri.startsWith(\"https://\") || uri.startsWith(\"http://\")) {\n return `${proxyPath}?url=${encodeURIComponent(uri)}`;\n }\n\n return placeholder;\n}\n\nexport function toDisplayUrlOrNull(\n raw: string | null | undefined,\n opts: DisplayUrlOptions = {},\n): string | null {\n if (!raw) return null;\n if (raw.startsWith(\"/\")) return raw;\n return toDisplayUrl(raw, opts);\n}\n\
|
|
1
|
+
{"version":3,"sources":["../../src/utils/ipfs.ts"],"sourcesContent":["export const DEFAULT_GATEWAY = \"https://gateway.pinata.cloud/ipfs/\";\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\", \"ipfs:\"]);\n\nconst KNOWN_IPFS_GATEWAY_HOSTS = /(^|\\.)(mypinata\\.cloud|pinata\\.cloud|ipfs\\.io|dweb\\.link|cloudflare-ipfs\\.com|nftstorage\\.link|w3s\\.link)$/i;\n\nconst DEFAULT_WIDTH = 1200;\n\nfunction withSize(url: string, gateway: string, width: number | null): string {\n if (!width || !url.startsWith(gateway)) return url;\n const sep = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${sep}img-width=${width}&img-format=webp&img-quality=80`;\n}\n\nexport function ipfsToHttp(\n uri: string | null | undefined,\n opts: { gateway?: string; width?: number | null } = {}\n): string {\n const gateway = opts.gateway ?? DEFAULT_GATEWAY;\n const width = opts.width === undefined ? DEFAULT_WIDTH : opts.width;\n\n if (!uri) return \"\";\n if (uri.startsWith(\"ipfs://\")) {\n return withSize(uri.replace(\"ipfs://\", gateway), gateway, width);\n }\n if (uri.startsWith(\"data:image/\")) {\n return uri;\n }\n try {\n const parsed = new URL(uri);\n if (!ALLOWED_PROTOCOLS.has(parsed.protocol)) return \"\";\n\n if (KNOWN_IPFS_GATEWAY_HOSTS.test(parsed.hostname)) {\n const match = parsed.pathname.match(/\\/ipfs\\/(.+)$/);\n if (match) return withSize(`${gateway}${match[1]}`, gateway, width);\n }\n } catch {\n return \"\";\n }\n return uri;\n}\n\nexport interface DisplayUrlOptions {\n gateway?: string;\n proxyPath?: string;\n placeholder?: string;\n}\n\nexport function toDisplayUrl(\n uri: string | null | undefined,\n opts: DisplayUrlOptions = {},\n): string {\n const gateway = opts.gateway ?? DEFAULT_GATEWAY;\n const placeholder = opts.placeholder ?? \"/placeholder.svg\";\n const proxyPath = opts.proxyPath ?? \"/api/img\";\n\n if (!uri) return placeholder;\n if (uri.startsWith(\"data:image/\")) return uri;\n\n const resolved = ipfsToHttp(uri, { gateway });\n if (resolved.startsWith(gateway)) return resolved;\n\n if (uri.startsWith(\"https://\") || uri.startsWith(\"http://\")) {\n return `${proxyPath}?url=${encodeURIComponent(uri)}`;\n }\n\n return placeholder;\n}\n\nexport function toDisplayUrlOrNull(\n raw: string | null | undefined,\n opts: DisplayUrlOptions = {},\n): string | null {\n if (!raw) return null;\n if (raw.startsWith(\"/\")) return raw;\n return toDisplayUrl(raw, opts);\n}\n\nexport function toAbsoluteImageUrl(uri: string | null | undefined): string {\n if (!uri) return \"\";\n return ipfsToHttp(uri) || \"\";\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAO,MAAM,kBAAkB;AAE/B,MAAM,oBAAoB,oBAAI,IAAI,CAAC,SAAS,UAAU,OAAO,CAAC;AAE9D,MAAM,2BAA2B;AAEjC,MAAM,gBAAgB;AAEtB,SAAS,SAAS,KAAa,SAAiB,OAA8B;AAC5E,MAAI,CAAC,SAAS,CAAC,IAAI,WAAW,OAAO,EAAG,QAAO;AAC/C,QAAM,MAAM,IAAI,SAAS,GAAG,IAAI,MAAM;AACtC,SAAO,GAAG,GAAG,GAAG,GAAG,aAAa,KAAK;AACvC;AAEO,SAAS,WACd,KACA,OAAoD,CAAC,GAC7C;AACR,QAAM,UAAU,KAAK,WAAW;AAChC,QAAM,QAAQ,KAAK,UAAU,SAAY,gBAAgB,KAAK;AAE9D,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,WAAW,SAAS,GAAG;AAC7B,WAAO,SAAS,IAAI,QAAQ,WAAW,OAAO,GAAG,SAAS,KAAK;AAAA,EACjE;AACA,MAAI,IAAI,WAAW,aAAa,GAAG;AACjC,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,QAAI,CAAC,kBAAkB,IAAI,OAAO,QAAQ,EAAG,QAAO;AAEpD,QAAI,yBAAyB,KAAK,OAAO,QAAQ,GAAG;AAClD,YAAM,QAAQ,OAAO,SAAS,MAAM,eAAe;AACnD,UAAI,MAAO,QAAO,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC,IAAI,SAAS,KAAK;AAAA,IACpE;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAQO,SAAS,aACd,KACA,OAA0B,CAAC,GACnB;AACR,QAAM,UAAU,KAAK,WAAW;AAChC,QAAM,cAAc,KAAK,eAAe;AACxC,QAAM,YAAY,KAAK,aAAa;AAEpC,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,WAAW,aAAa,EAAG,QAAO;AAE1C,QAAM,WAAW,WAAW,KAAK,EAAE,QAAQ,CAAC;AAC5C,MAAI,SAAS,WAAW,OAAO,EAAG,QAAO;AAEzC,MAAI,IAAI,WAAW,UAAU,KAAK,IAAI,WAAW,SAAS,GAAG;AAC3D,WAAO,GAAG,SAAS,QAAQ,mBAAmB,GAAG,CAAC;AAAA,EACpD;AAEA,SAAO;AACT;AAEO,SAAS,mBACd,KACA,OAA0B,CAAC,GACZ;AACf,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,WAAW,GAAG,EAAG,QAAO;AAChC,SAAO,aAAa,KAAK,IAAI;AAC/B;AAEO,SAAS,mBAAmB,KAAwC;AACzE,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,WAAW,GAAG,KAAK;AAC5B;","names":[]}
|
package/dist/utils/ipfs.d.cts
CHANGED
|
@@ -10,13 +10,6 @@ interface DisplayUrlOptions {
|
|
|
10
10
|
}
|
|
11
11
|
declare function toDisplayUrl(uri: string | null | undefined, opts?: DisplayUrlOptions): string;
|
|
12
12
|
declare function toDisplayUrlOrNull(raw: string | null | undefined, opts?: DisplayUrlOptions): string | null;
|
|
13
|
-
/**
|
|
14
|
-
* Absolute URL for a crawler: og:image and twitter:image are fetched by
|
|
15
|
-
* Facebook, X and Slack with our app nowhere in the path, so the result must
|
|
16
|
-
* resolve on its own. Pointing these at an app route is how every social
|
|
17
|
-
* preview once 404'd. Empty string means "no image", which is what the
|
|
18
|
-
* metadata APIs expect in place of a broken URL.
|
|
19
|
-
*/
|
|
20
13
|
declare function toAbsoluteImageUrl(uri: string | null | undefined): string;
|
|
21
14
|
|
|
22
15
|
export { DEFAULT_GATEWAY, type DisplayUrlOptions, ipfsToHttp, toAbsoluteImageUrl, toDisplayUrl, toDisplayUrlOrNull };
|
package/dist/utils/ipfs.d.ts
CHANGED
|
@@ -10,13 +10,6 @@ interface DisplayUrlOptions {
|
|
|
10
10
|
}
|
|
11
11
|
declare function toDisplayUrl(uri: string | null | undefined, opts?: DisplayUrlOptions): string;
|
|
12
12
|
declare function toDisplayUrlOrNull(raw: string | null | undefined, opts?: DisplayUrlOptions): string | null;
|
|
13
|
-
/**
|
|
14
|
-
* Absolute URL for a crawler: og:image and twitter:image are fetched by
|
|
15
|
-
* Facebook, X and Slack with our app nowhere in the path, so the result must
|
|
16
|
-
* resolve on its own. Pointing these at an app route is how every social
|
|
17
|
-
* preview once 404'd. Empty string means "no image", which is what the
|
|
18
|
-
* metadata APIs expect in place of a broken URL.
|
|
19
|
-
*/
|
|
20
13
|
declare function toAbsoluteImageUrl(uri: string | null | undefined): string;
|
|
21
14
|
|
|
22
15
|
export { DEFAULT_GATEWAY, type DisplayUrlOptions, ipfsToHttp, toAbsoluteImageUrl, toDisplayUrl, toDisplayUrlOrNull };
|
package/dist/utils/ipfs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/ipfs.ts"],"sourcesContent":["export const DEFAULT_GATEWAY = \"https://gateway.pinata.cloud/ipfs/\";\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\", \"ipfs:\"]);\n\nconst KNOWN_IPFS_GATEWAY_HOSTS = /(^|\\.)(mypinata\\.cloud|pinata\\.cloud|ipfs\\.io|dweb\\.link|cloudflare-ipfs\\.com|nftstorage\\.link|w3s\\.link)$/i;\n\nconst DEFAULT_WIDTH = 1200;\n\nfunction withSize(url: string, gateway: string, width: number | null): string {\n if (!width || !url.startsWith(gateway)) return url;\n const sep = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${sep}img-width=${width}&img-format=webp&img-quality=80`;\n}\n\nexport function ipfsToHttp(\n uri: string | null | undefined,\n opts: { gateway?: string; width?: number | null } = {}\n): string {\n const gateway = opts.gateway ?? DEFAULT_GATEWAY;\n const width = opts.width === undefined ? DEFAULT_WIDTH : opts.width;\n\n if (!uri) return \"\";\n if (uri.startsWith(\"ipfs://\")) {\n return withSize(uri.replace(\"ipfs://\", gateway), gateway, width);\n }\n if (uri.startsWith(\"data:image/\")) {\n return uri;\n }\n try {\n const parsed = new URL(uri);\n if (!ALLOWED_PROTOCOLS.has(parsed.protocol)) return \"\";\n\n if (KNOWN_IPFS_GATEWAY_HOSTS.test(parsed.hostname)) {\n const match = parsed.pathname.match(/\\/ipfs\\/(.+)$/);\n if (match) return withSize(`${gateway}${match[1]}`, gateway, width);\n }\n } catch {\n return \"\";\n }\n return uri;\n}\n\nexport interface DisplayUrlOptions {\n gateway?: string;\n proxyPath?: string;\n placeholder?: string;\n}\n\nexport function toDisplayUrl(\n uri: string | null | undefined,\n opts: DisplayUrlOptions = {},\n): string {\n const gateway = opts.gateway ?? DEFAULT_GATEWAY;\n const placeholder = opts.placeholder ?? \"/placeholder.svg\";\n const proxyPath = opts.proxyPath ?? \"/api/img\";\n\n if (!uri) return placeholder;\n if (uri.startsWith(\"data:image/\")) return uri;\n\n const resolved = ipfsToHttp(uri, { gateway });\n if (resolved.startsWith(gateway)) return resolved;\n\n if (uri.startsWith(\"https://\") || uri.startsWith(\"http://\")) {\n return `${proxyPath}?url=${encodeURIComponent(uri)}`;\n }\n\n return placeholder;\n}\n\nexport function toDisplayUrlOrNull(\n raw: string | null | undefined,\n opts: DisplayUrlOptions = {},\n): string | null {\n if (!raw) return null;\n if (raw.startsWith(\"/\")) return raw;\n return toDisplayUrl(raw, opts);\n}\n\
|
|
1
|
+
{"version":3,"sources":["../../src/utils/ipfs.ts"],"sourcesContent":["export const DEFAULT_GATEWAY = \"https://gateway.pinata.cloud/ipfs/\";\n\nconst ALLOWED_PROTOCOLS = new Set([\"http:\", \"https:\", \"ipfs:\"]);\n\nconst KNOWN_IPFS_GATEWAY_HOSTS = /(^|\\.)(mypinata\\.cloud|pinata\\.cloud|ipfs\\.io|dweb\\.link|cloudflare-ipfs\\.com|nftstorage\\.link|w3s\\.link)$/i;\n\nconst DEFAULT_WIDTH = 1200;\n\nfunction withSize(url: string, gateway: string, width: number | null): string {\n if (!width || !url.startsWith(gateway)) return url;\n const sep = url.includes(\"?\") ? \"&\" : \"?\";\n return `${url}${sep}img-width=${width}&img-format=webp&img-quality=80`;\n}\n\nexport function ipfsToHttp(\n uri: string | null | undefined,\n opts: { gateway?: string; width?: number | null } = {}\n): string {\n const gateway = opts.gateway ?? DEFAULT_GATEWAY;\n const width = opts.width === undefined ? DEFAULT_WIDTH : opts.width;\n\n if (!uri) return \"\";\n if (uri.startsWith(\"ipfs://\")) {\n return withSize(uri.replace(\"ipfs://\", gateway), gateway, width);\n }\n if (uri.startsWith(\"data:image/\")) {\n return uri;\n }\n try {\n const parsed = new URL(uri);\n if (!ALLOWED_PROTOCOLS.has(parsed.protocol)) return \"\";\n\n if (KNOWN_IPFS_GATEWAY_HOSTS.test(parsed.hostname)) {\n const match = parsed.pathname.match(/\\/ipfs\\/(.+)$/);\n if (match) return withSize(`${gateway}${match[1]}`, gateway, width);\n }\n } catch {\n return \"\";\n }\n return uri;\n}\n\nexport interface DisplayUrlOptions {\n gateway?: string;\n proxyPath?: string;\n placeholder?: string;\n}\n\nexport function toDisplayUrl(\n uri: string | null | undefined,\n opts: DisplayUrlOptions = {},\n): string {\n const gateway = opts.gateway ?? DEFAULT_GATEWAY;\n const placeholder = opts.placeholder ?? \"/placeholder.svg\";\n const proxyPath = opts.proxyPath ?? \"/api/img\";\n\n if (!uri) return placeholder;\n if (uri.startsWith(\"data:image/\")) return uri;\n\n const resolved = ipfsToHttp(uri, { gateway });\n if (resolved.startsWith(gateway)) return resolved;\n\n if (uri.startsWith(\"https://\") || uri.startsWith(\"http://\")) {\n return `${proxyPath}?url=${encodeURIComponent(uri)}`;\n }\n\n return placeholder;\n}\n\nexport function toDisplayUrlOrNull(\n raw: string | null | undefined,\n opts: DisplayUrlOptions = {},\n): string | null {\n if (!raw) return null;\n if (raw.startsWith(\"/\")) return raw;\n return toDisplayUrl(raw, opts);\n}\n\nexport function toAbsoluteImageUrl(uri: string | null | undefined): string {\n if (!uri) return \"\";\n return ipfsToHttp(uri) || \"\";\n}\n"],"mappings":"AAAO,MAAM,kBAAkB;AAE/B,MAAM,oBAAoB,oBAAI,IAAI,CAAC,SAAS,UAAU,OAAO,CAAC;AAE9D,MAAM,2BAA2B;AAEjC,MAAM,gBAAgB;AAEtB,SAAS,SAAS,KAAa,SAAiB,OAA8B;AAC5E,MAAI,CAAC,SAAS,CAAC,IAAI,WAAW,OAAO,EAAG,QAAO;AAC/C,QAAM,MAAM,IAAI,SAAS,GAAG,IAAI,MAAM;AACtC,SAAO,GAAG,GAAG,GAAG,GAAG,aAAa,KAAK;AACvC;AAEO,SAAS,WACd,KACA,OAAoD,CAAC,GAC7C;AACR,QAAM,UAAU,KAAK,WAAW;AAChC,QAAM,QAAQ,KAAK,UAAU,SAAY,gBAAgB,KAAK;AAE9D,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,WAAW,SAAS,GAAG;AAC7B,WAAO,SAAS,IAAI,QAAQ,WAAW,OAAO,GAAG,SAAS,KAAK;AAAA,EACjE;AACA,MAAI,IAAI,WAAW,aAAa,GAAG;AACjC,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,QAAI,CAAC,kBAAkB,IAAI,OAAO,QAAQ,EAAG,QAAO;AAEpD,QAAI,yBAAyB,KAAK,OAAO,QAAQ,GAAG;AAClD,YAAM,QAAQ,OAAO,SAAS,MAAM,eAAe;AACnD,UAAI,MAAO,QAAO,SAAS,GAAG,OAAO,GAAG,MAAM,CAAC,CAAC,IAAI,SAAS,KAAK;AAAA,IACpE;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAQO,SAAS,aACd,KACA,OAA0B,CAAC,GACnB;AACR,QAAM,UAAU,KAAK,WAAW;AAChC,QAAM,cAAc,KAAK,eAAe;AACxC,QAAM,YAAY,KAAK,aAAa;AAEpC,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,WAAW,aAAa,EAAG,QAAO;AAE1C,QAAM,WAAW,WAAW,KAAK,EAAE,QAAQ,CAAC;AAC5C,MAAI,SAAS,WAAW,OAAO,EAAG,QAAO;AAEzC,MAAI,IAAI,WAAW,UAAU,KAAK,IAAI,WAAW,SAAS,GAAG;AAC3D,WAAO,GAAG,SAAS,QAAQ,mBAAmB,GAAG,CAAC;AAAA,EACpD;AAEA,SAAO;AACT;AAEO,SAAS,mBACd,KACA,OAA0B,CAAC,GACZ;AACf,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,WAAW,GAAG,EAAG,QAAO;AAChC,SAAO,aAAa,KAAK,IAAI;AAC/B;AAEO,SAAS,mBAAmB,KAAwC;AACzE,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,WAAW,GAAG,KAAK;AAC5B;","names":[]}
|
|
@@ -24,7 +24,7 @@ module.exports = __toCommonJS(profile_identity_exports);
|
|
|
24
24
|
var import_address = require("./address.js");
|
|
25
25
|
function profileIdentity(source) {
|
|
26
26
|
const username = source.username?.trim() || null;
|
|
27
|
-
const name = source.
|
|
27
|
+
const name = source.name?.trim() || null;
|
|
28
28
|
const address = source.walletAddress?.trim() || null;
|
|
29
29
|
return {
|
|
30
30
|
identity: username ? `@${username}` : address ? (0, import_address.shortenAddress)(address) : "Unknown account",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/profile-identity.ts"],"sourcesContent":["import { shortenAddress } from \"./address.js\";\n\nexport interface ProfileIdentitySource {\n username?: string | null;\n
|
|
1
|
+
{"version":3,"sources":["../../src/utils/profile-identity.ts"],"sourcesContent":["import { shortenAddress } from \"./address.js\";\n\nexport interface ProfileIdentitySource {\n username?: string | null;\n name?: string | null;\n walletAddress?: string | null;\n}\n\nexport interface ProfileIdentity {\n identity: string;\n name: string | null;\n hasUsername: boolean;\n}\n\nexport function profileIdentity(source: ProfileIdentitySource): ProfileIdentity {\n const username = source.username?.trim() || null;\n const name = source.name?.trim() || null;\n const address = source.walletAddress?.trim() || null;\n\n return {\n identity: username ? `@${username}` : address ? shortenAddress(address) : \"Unknown account\",\n name,\n hasUsername: Boolean(username),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAA+B;AAcxB,SAAS,gBAAgB,QAAgD;AAC9E,QAAM,WAAW,OAAO,UAAU,KAAK,KAAK;AAC5C,QAAM,OAAO,OAAO,MAAM,KAAK,KAAK;AACpC,QAAM,UAAU,OAAO,eAAe,KAAK,KAAK;AAEhD,SAAO;AAAA,IACL,UAAU,WAAW,IAAI,QAAQ,KAAK,cAAU,+BAAe,OAAO,IAAI;AAAA,IAC1E;AAAA,IACA,aAAa,QAAQ,QAAQ;AAAA,EAC/B;AACF;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
interface ProfileIdentitySource {
|
|
2
2
|
username?: string | null;
|
|
3
|
-
|
|
3
|
+
name?: string | null;
|
|
4
4
|
walletAddress?: string | null;
|
|
5
5
|
}
|
|
6
6
|
interface ProfileIdentity {
|
|
@@ -8,18 +8,6 @@ interface ProfileIdentity {
|
|
|
8
8
|
name: string | null;
|
|
9
9
|
hasUsername: boolean;
|
|
10
10
|
}
|
|
11
|
-
/**
|
|
12
|
-
* Splits a profile into what identifies an account and what merely labels it.
|
|
13
|
-
*
|
|
14
|
-
* `identity` only ever comes from a claimed username or the wallet address.
|
|
15
|
-
* Both are governed: a username is unique, charset-restricted, blocked against
|
|
16
|
-
* a reserved list and reviewed before approval; an address is the account
|
|
17
|
-
* itself. `name` is free text the account typed, so it can say anything and
|
|
18
|
-
* cannot identify anyone.
|
|
19
|
-
*
|
|
20
|
-
* Keeping them apart is the point. A profile that set its name to a platform
|
|
21
|
-
* or a rival's handle rendered as that handle everywhere the two were merged.
|
|
22
|
-
*/
|
|
23
11
|
declare function profileIdentity(source: ProfileIdentitySource): ProfileIdentity;
|
|
24
12
|
|
|
25
13
|
export { type ProfileIdentity, type ProfileIdentitySource, profileIdentity };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
interface ProfileIdentitySource {
|
|
2
2
|
username?: string | null;
|
|
3
|
-
|
|
3
|
+
name?: string | null;
|
|
4
4
|
walletAddress?: string | null;
|
|
5
5
|
}
|
|
6
6
|
interface ProfileIdentity {
|
|
@@ -8,18 +8,6 @@ interface ProfileIdentity {
|
|
|
8
8
|
name: string | null;
|
|
9
9
|
hasUsername: boolean;
|
|
10
10
|
}
|
|
11
|
-
/**
|
|
12
|
-
* Splits a profile into what identifies an account and what merely labels it.
|
|
13
|
-
*
|
|
14
|
-
* `identity` only ever comes from a claimed username or the wallet address.
|
|
15
|
-
* Both are governed: a username is unique, charset-restricted, blocked against
|
|
16
|
-
* a reserved list and reviewed before approval; an address is the account
|
|
17
|
-
* itself. `name` is free text the account typed, so it can say anything and
|
|
18
|
-
* cannot identify anyone.
|
|
19
|
-
*
|
|
20
|
-
* Keeping them apart is the point. A profile that set its name to a platform
|
|
21
|
-
* or a rival's handle rendered as that handle everywhere the two were merged.
|
|
22
|
-
*/
|
|
23
11
|
declare function profileIdentity(source: ProfileIdentitySource): ProfileIdentity;
|
|
24
12
|
|
|
25
13
|
export { type ProfileIdentity, type ProfileIdentitySource, profileIdentity };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { shortenAddress } from "./address.js";
|
|
2
2
|
function profileIdentity(source) {
|
|
3
3
|
const username = source.username?.trim() || null;
|
|
4
|
-
const name = source.
|
|
4
|
+
const name = source.name?.trim() || null;
|
|
5
5
|
const address = source.walletAddress?.trim() || null;
|
|
6
6
|
return {
|
|
7
7
|
identity: username ? `@${username}` : address ? shortenAddress(address) : "Unknown account",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/profile-identity.ts"],"sourcesContent":["import { shortenAddress } from \"./address.js\";\n\nexport interface ProfileIdentitySource {\n username?: string | null;\n
|
|
1
|
+
{"version":3,"sources":["../../src/utils/profile-identity.ts"],"sourcesContent":["import { shortenAddress } from \"./address.js\";\n\nexport interface ProfileIdentitySource {\n username?: string | null;\n name?: string | null;\n walletAddress?: string | null;\n}\n\nexport interface ProfileIdentity {\n identity: string;\n name: string | null;\n hasUsername: boolean;\n}\n\nexport function profileIdentity(source: ProfileIdentitySource): ProfileIdentity {\n const username = source.username?.trim() || null;\n const name = source.name?.trim() || null;\n const address = source.walletAddress?.trim() || null;\n\n return {\n identity: username ? `@${username}` : address ? shortenAddress(address) : \"Unknown account\",\n name,\n hasUsername: Boolean(username),\n };\n}\n"],"mappings":"AAAA,SAAS,sBAAsB;AAcxB,SAAS,gBAAgB,QAAgD;AAC9E,QAAM,WAAW,OAAO,UAAU,KAAK,KAAK;AAC5C,QAAM,OAAO,OAAO,MAAM,KAAK,KAAK;AACpC,QAAM,UAAU,OAAO,eAAe,KAAK,KAAK;AAEhD,SAAO;AAAA,IACL,UAAU,WAAW,IAAI,QAAQ,KAAK,UAAU,eAAe,OAAO,IAAI;AAAA,IAC1E;AAAA,IACA,aAAa,QAAQ,QAAQ;AAAA,EAC/B;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/use-usd-prices.ts"],"sourcesContent":["\"use client\";\n\nimport useSWR from \"swr\";\n\nexport type UsdPrices = Partial<Record<\"STRK\" | \"ETH\" | \"USDC\" | \"USDT\" | \"WBTC\", number>>;\n\
|
|
1
|
+
{"version":3,"sources":["../../src/utils/use-usd-prices.ts"],"sourcesContent":["\"use client\";\n\nimport useSWR from \"swr\";\n\nexport type UsdPrices = Partial<Record<\"STRK\" | \"ETH\" | \"USDC\" | \"USDT\" | \"WBTC\", number>>;\n\nexport function usdPriceFor(prices: UsdPrices | null, symbol: string): number | undefined {\n return (prices as Record<string, number | undefined> | null)?.[symbol];\n}\n\nconst REFRESH_MS = 60_000;\nconst PRICES_KEY = \"usd-prices\";\nconst PRICES_URL = \"/api/proxy/v1/prices\";\n\nasync function fetchUsdPrices(): Promise<UsdPrices | null> {\n const res = await fetch(PRICES_URL);\n const body = (await res.json()) as { data?: { usd?: UsdPrices } };\n return body.data?.usd ?? null;\n}\n\nexport function useUsdPrices(): UsdPrices | null {\n const { data } = useSWR(PRICES_KEY, fetchUsdPrices, {\n refreshInterval: REFRESH_MS,\n dedupingInterval: REFRESH_MS,\n revalidateOnFocus: false,\n revalidateOnReconnect: true,\n keepPreviousData: true,\n onError: () => {},\n });\n return data ?? null;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,iBAAmB;AAIZ,SAAS,YAAY,QAA0B,QAAoC;AACxF,SAAQ,SAAuD,MAAM;AACvE;AAEA,MAAM,aAAa;AACnB,MAAM,aAAa;AACnB,MAAM,aAAa;AAEnB,eAAe,iBAA4C;AACzD,QAAM,MAAM,MAAM,MAAM,UAAU;AAClC,QAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,SAAO,KAAK,MAAM,OAAO;AAC3B;AAEO,SAAS,eAAiC;AAC/C,QAAM,EAAE,KAAK,QAAI,WAAAA,SAAO,YAAY,gBAAgB;AAAA,IAClD,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,uBAAuB;AAAA,IACvB,kBAAkB;AAAA,IAClB,SAAS,MAAM;AAAA,IAAC;AAAA,EAClB,CAAC;AACD,SAAO,QAAQ;AACjB;","names":["useSWR"]}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
type UsdPrices = Partial<Record<"STRK" | "ETH" | "USDC" | "USDT" | "WBTC", number>>;
|
|
2
|
-
/** Looks up a USD price by an arbitrary token symbol (not just the pinned
|
|
3
|
-
* set UsdPrices is keyed on) — a coin/memecoin symbol simply has no entry. */
|
|
4
2
|
declare function usdPriceFor(prices: UsdPrices | null, symbol: string): number | undefined;
|
|
5
3
|
declare function useUsdPrices(): UsdPrices | null;
|
|
6
4
|
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
type UsdPrices = Partial<Record<"STRK" | "ETH" | "USDC" | "USDT" | "WBTC", number>>;
|
|
2
|
-
/** Looks up a USD price by an arbitrary token symbol (not just the pinned
|
|
3
|
-
* set UsdPrices is keyed on) — a coin/memecoin symbol simply has no entry. */
|
|
4
2
|
declare function usdPriceFor(prices: UsdPrices | null, symbol: string): number | undefined;
|
|
5
3
|
declare function useUsdPrices(): UsdPrices | null;
|
|
6
4
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/use-usd-prices.ts"],"sourcesContent":["\"use client\";\n\nimport useSWR from \"swr\";\n\nexport type UsdPrices = Partial<Record<\"STRK\" | \"ETH\" | \"USDC\" | \"USDT\" | \"WBTC\", number>>;\n\
|
|
1
|
+
{"version":3,"sources":["../../src/utils/use-usd-prices.ts"],"sourcesContent":["\"use client\";\n\nimport useSWR from \"swr\";\n\nexport type UsdPrices = Partial<Record<\"STRK\" | \"ETH\" | \"USDC\" | \"USDT\" | \"WBTC\", number>>;\n\nexport function usdPriceFor(prices: UsdPrices | null, symbol: string): number | undefined {\n return (prices as Record<string, number | undefined> | null)?.[symbol];\n}\n\nconst REFRESH_MS = 60_000;\nconst PRICES_KEY = \"usd-prices\";\nconst PRICES_URL = \"/api/proxy/v1/prices\";\n\nasync function fetchUsdPrices(): Promise<UsdPrices | null> {\n const res = await fetch(PRICES_URL);\n const body = (await res.json()) as { data?: { usd?: UsdPrices } };\n return body.data?.usd ?? null;\n}\n\nexport function useUsdPrices(): UsdPrices | null {\n const { data } = useSWR(PRICES_KEY, fetchUsdPrices, {\n refreshInterval: REFRESH_MS,\n dedupingInterval: REFRESH_MS,\n revalidateOnFocus: false,\n revalidateOnReconnect: true,\n keepPreviousData: true,\n onError: () => {},\n });\n return data ?? null;\n}\n"],"mappings":";AAEA,OAAO,YAAY;AAIZ,SAAS,YAAY,QAA0B,QAAoC;AACxF,SAAQ,SAAuD,MAAM;AACvE;AAEA,MAAM,aAAa;AACnB,MAAM,aAAa;AACnB,MAAM,aAAa;AAEnB,eAAe,iBAA4C;AACzD,QAAM,MAAM,MAAM,MAAM,UAAU;AAClC,QAAM,OAAQ,MAAM,IAAI,KAAK;AAC7B,SAAO,KAAK,MAAM,OAAO;AAC3B;AAEO,SAAS,eAAiC;AAC/C,QAAM,EAAE,KAAK,IAAI,OAAO,YAAY,gBAAgB;AAAA,IAClD,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,uBAAuB;AAAA,IACvB,kBAAkB;AAAA,IAClB,SAAS,MAAM;AAAA,IAAC;AAAA,EAClB,CAAC;AACD,SAAO,QAAQ;AACjB;","names":[]}
|