@orangecheck/design 0.2.0 → 0.4.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.
@@ -202,4 +202,95 @@ declare function SelectScrollDownButton({ className, ...props }: React.Component
202
202
 
203
203
  declare const Toaster: ({ ...props }: ToasterProps) => react_jsx_runtime.JSX.Element;
204
204
 
205
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertTitle, AlertWithAction, type AlertWithActionProps, AlertWithCountdown, type AlertWithCountdownProps, Badge, Button, ConfirmHost, type ConfirmOptions, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, ErrorBoundary, Input, Label, Modal, type ModalProps, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PromptHost, type PromptOptions, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetContent, SheetDescription, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Skeleton, type SkeletonProps, Textarea, ThemeToggle, ThemeToggleLink, Toaster, Working, WorkingPanel, type WorkingProps, badgeVariants, buttonVariants, confirm, prompt };
205
+ interface CardProps {
206
+ id?: string;
207
+ title: string;
208
+ subtitle?: string;
209
+ refreshedAt?: string | null;
210
+ actions?: ReactNode;
211
+ children: ReactNode;
212
+ className?: string;
213
+ tone?: 'default' | 'warning' | 'destructive' | 'muted';
214
+ expandable?: boolean;
215
+ }
216
+ declare function Card({ id, title, subtitle, refreshedAt, actions, children, className, tone, expandable, }: CardProps): react_jsx_runtime.JSX.Element;
217
+
218
+ interface SparklineProps {
219
+ data: ReadonlyArray<number>;
220
+ width?: number;
221
+ height?: number;
222
+ className?: string;
223
+ area?: boolean;
224
+ }
225
+ declare function Sparkline({ data, width, height, area, className }: SparklineProps): react_jsx_runtime.JSX.Element;
226
+
227
+ interface StatBlockProps {
228
+ label: string;
229
+ value: ReactNode;
230
+ sub?: ReactNode;
231
+ delta?: {
232
+ value: number;
233
+ suffix?: string;
234
+ } | null;
235
+ tone?: 'default' | 'success' | 'warning' | 'destructive';
236
+ className?: string;
237
+ }
238
+ declare function StatBlock({ label, value, sub, delta, tone, className }: StatBlockProps): react_jsx_runtime.JSX.Element;
239
+
240
+ interface BarProps {
241
+ pct: number;
242
+ accent?: 'primary' | 'warning' | 'destructive' | 'success';
243
+ label?: string;
244
+ right?: string;
245
+ className?: string;
246
+ }
247
+ declare function Bar({ pct, accent, label, right, className }: BarProps): react_jsx_runtime.JSX.Element;
248
+
249
+ type StatusTone = 'success' | 'warning' | 'destructive' | 'muted' | 'primary' | 'info';
250
+ interface StatusPillProps {
251
+ label: string;
252
+ tone?: StatusTone;
253
+ icon?: ReactNode;
254
+ className?: string;
255
+ }
256
+ declare function StatusPill({ label, tone, icon, className }: StatusPillProps): react_jsx_runtime.JSX.Element;
257
+
258
+ interface SatsAmountProps {
259
+ sats: number;
260
+ unit?: boolean;
261
+ usd?: boolean;
262
+ btcUsd?: number;
263
+ className?: string;
264
+ }
265
+ declare function SatsAmount({ sats, unit, usd, btcUsd, className }: SatsAmountProps): react_jsx_runtime.JSX.Element;
266
+
267
+ interface BitcoinAddressProps {
268
+ address: string;
269
+ head?: number;
270
+ tail?: number;
271
+ copyable?: boolean;
272
+ full?: boolean;
273
+ className?: string;
274
+ }
275
+ declare function BitcoinAddress({ address, head, tail, copyable, full, className, }: BitcoinAddressProps): react_jsx_runtime.JSX.Element;
276
+
277
+ interface CopyButtonProps {
278
+ value: string;
279
+ label?: string;
280
+ clearAfter?: number;
281
+ size?: 'sm' | 'md';
282
+ className?: string;
283
+ title?: string;
284
+ onCopied?: () => void;
285
+ }
286
+ declare function CopyButton({ value, label, clearAfter, size, className, title, onCopied, }: CopyButtonProps): react_jsx_runtime.JSX.Element;
287
+
288
+ interface QrCodeProps {
289
+ value: string;
290
+ size?: number;
291
+ label?: string;
292
+ className?: string;
293
+ }
294
+ declare function QrCode({ value, size, label, className }: QrCodeProps): react_jsx_runtime.JSX.Element;
295
+
296
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertTitle, AlertWithAction, type AlertWithActionProps, AlertWithCountdown, type AlertWithCountdownProps, Badge, Bar, type BarProps, BitcoinAddress, type BitcoinAddressProps, Button, Card, type CardProps, ConfirmHost, type ConfirmOptions, CopyButton, type CopyButtonProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, ErrorBoundary, Input, Label, Modal, type ModalProps, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PromptHost, type PromptOptions, QrCode, type QrCodeProps, SatsAmount, type SatsAmountProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetContent, SheetDescription, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Skeleton, type SkeletonProps, Sparkline, type SparklineProps, StatBlock, type StatBlockProps, StatusPill, type StatusPillProps, type StatusTone, Textarea, ThemeToggle, ThemeToggleLink, Toaster, Working, WorkingPanel, type WorkingProps, badgeVariants, buttonVariants, confirm, prompt };
@@ -202,4 +202,95 @@ declare function SelectScrollDownButton({ className, ...props }: React.Component
202
202
 
203
203
  declare const Toaster: ({ ...props }: ToasterProps) => react_jsx_runtime.JSX.Element;
204
204
 
205
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertTitle, AlertWithAction, type AlertWithActionProps, AlertWithCountdown, type AlertWithCountdownProps, Badge, Button, ConfirmHost, type ConfirmOptions, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, ErrorBoundary, Input, Label, Modal, type ModalProps, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PromptHost, type PromptOptions, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetContent, SheetDescription, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Skeleton, type SkeletonProps, Textarea, ThemeToggle, ThemeToggleLink, Toaster, Working, WorkingPanel, type WorkingProps, badgeVariants, buttonVariants, confirm, prompt };
205
+ interface CardProps {
206
+ id?: string;
207
+ title: string;
208
+ subtitle?: string;
209
+ refreshedAt?: string | null;
210
+ actions?: ReactNode;
211
+ children: ReactNode;
212
+ className?: string;
213
+ tone?: 'default' | 'warning' | 'destructive' | 'muted';
214
+ expandable?: boolean;
215
+ }
216
+ declare function Card({ id, title, subtitle, refreshedAt, actions, children, className, tone, expandable, }: CardProps): react_jsx_runtime.JSX.Element;
217
+
218
+ interface SparklineProps {
219
+ data: ReadonlyArray<number>;
220
+ width?: number;
221
+ height?: number;
222
+ className?: string;
223
+ area?: boolean;
224
+ }
225
+ declare function Sparkline({ data, width, height, area, className }: SparklineProps): react_jsx_runtime.JSX.Element;
226
+
227
+ interface StatBlockProps {
228
+ label: string;
229
+ value: ReactNode;
230
+ sub?: ReactNode;
231
+ delta?: {
232
+ value: number;
233
+ suffix?: string;
234
+ } | null;
235
+ tone?: 'default' | 'success' | 'warning' | 'destructive';
236
+ className?: string;
237
+ }
238
+ declare function StatBlock({ label, value, sub, delta, tone, className }: StatBlockProps): react_jsx_runtime.JSX.Element;
239
+
240
+ interface BarProps {
241
+ pct: number;
242
+ accent?: 'primary' | 'warning' | 'destructive' | 'success';
243
+ label?: string;
244
+ right?: string;
245
+ className?: string;
246
+ }
247
+ declare function Bar({ pct, accent, label, right, className }: BarProps): react_jsx_runtime.JSX.Element;
248
+
249
+ type StatusTone = 'success' | 'warning' | 'destructive' | 'muted' | 'primary' | 'info';
250
+ interface StatusPillProps {
251
+ label: string;
252
+ tone?: StatusTone;
253
+ icon?: ReactNode;
254
+ className?: string;
255
+ }
256
+ declare function StatusPill({ label, tone, icon, className }: StatusPillProps): react_jsx_runtime.JSX.Element;
257
+
258
+ interface SatsAmountProps {
259
+ sats: number;
260
+ unit?: boolean;
261
+ usd?: boolean;
262
+ btcUsd?: number;
263
+ className?: string;
264
+ }
265
+ declare function SatsAmount({ sats, unit, usd, btcUsd, className }: SatsAmountProps): react_jsx_runtime.JSX.Element;
266
+
267
+ interface BitcoinAddressProps {
268
+ address: string;
269
+ head?: number;
270
+ tail?: number;
271
+ copyable?: boolean;
272
+ full?: boolean;
273
+ className?: string;
274
+ }
275
+ declare function BitcoinAddress({ address, head, tail, copyable, full, className, }: BitcoinAddressProps): react_jsx_runtime.JSX.Element;
276
+
277
+ interface CopyButtonProps {
278
+ value: string;
279
+ label?: string;
280
+ clearAfter?: number;
281
+ size?: 'sm' | 'md';
282
+ className?: string;
283
+ title?: string;
284
+ onCopied?: () => void;
285
+ }
286
+ declare function CopyButton({ value, label, clearAfter, size, className, title, onCopied, }: CopyButtonProps): react_jsx_runtime.JSX.Element;
287
+
288
+ interface QrCodeProps {
289
+ value: string;
290
+ size?: number;
291
+ label?: string;
292
+ className?: string;
293
+ }
294
+ declare function QrCode({ value, size, label, className }: QrCodeProps): react_jsx_runtime.JSX.Element;
295
+
296
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertTitle, AlertWithAction, type AlertWithActionProps, AlertWithCountdown, type AlertWithCountdownProps, Badge, Bar, type BarProps, BitcoinAddress, type BitcoinAddressProps, Button, Card, type CardProps, ConfirmHost, type ConfirmOptions, CopyButton, type CopyButtonProps, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, ErrorBoundary, Input, Label, Modal, type ModalProps, Pagination, type PaginationProps, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, PromptHost, type PromptOptions, QrCode, type QrCodeProps, SatsAmount, type SatsAmountProps, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetContent, SheetDescription, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Skeleton, type SkeletonProps, Sparkline, type SparklineProps, StatBlock, type StatBlockProps, StatusPill, type StatusPillProps, type StatusTone, Textarea, ThemeToggle, ThemeToggleLink, Toaster, Working, WorkingPanel, type WorkingProps, badgeVariants, buttonVariants, confirm, prompt };
@@ -15,6 +15,9 @@ var PopoverPrimitive = require('@radix-ui/react-popover');
15
15
  var AccordionPrimitive = require('@radix-ui/react-accordion');
16
16
  var SelectPrimitive = require('@radix-ui/react-select');
17
17
  var sonner = require('sonner');
18
+ var QRCode = require('qrcode');
19
+
20
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
21
 
19
22
  function _interopNamespace(e) {
20
23
  if (e && e.__esModule) return e;
@@ -41,6 +44,7 @@ var DialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(DialogPrimitive)
41
44
  var PopoverPrimitive__namespace = /*#__PURE__*/_interopNamespace(PopoverPrimitive);
42
45
  var AccordionPrimitive__namespace = /*#__PURE__*/_interopNamespace(AccordionPrimitive);
43
46
  var SelectPrimitive__namespace = /*#__PURE__*/_interopNamespace(SelectPrimitive);
47
+ var QRCode__default = /*#__PURE__*/_interopDefault(QRCode);
44
48
 
45
49
  // src/primitives/button.tsx
46
50
  function cn(...inputs) {
@@ -1168,6 +1172,312 @@ var Toaster = ({ ...props }) => {
1168
1172
  }
1169
1173
  );
1170
1174
  };
1175
+ function Card({
1176
+ id,
1177
+ title,
1178
+ subtitle,
1179
+ refreshedAt,
1180
+ actions,
1181
+ children,
1182
+ className,
1183
+ tone = "default",
1184
+ expandable = true
1185
+ }) {
1186
+ const [expanded, setExpanded] = React2.useState(false);
1187
+ const toneClass = tone === "warning" ? "border-warning/60" : tone === "destructive" ? "border-destructive/60" : tone === "muted" ? "border-border opacity-90" : "border-border";
1188
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1189
+ /* @__PURE__ */ jsxRuntime.jsxs(
1190
+ "section",
1191
+ {
1192
+ id,
1193
+ className: cn("terminal flex flex-col rounded-sm border scroll-mt-16", toneClass, className),
1194
+ children: [
1195
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "terminal-title flex items-center justify-between gap-3", children: [
1196
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1197
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "terminal-dot", "aria-hidden": true }),
1198
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "terminal-dot opacity-70", "aria-hidden": true }),
1199
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "terminal-dot opacity-50", "aria-hidden": true }),
1200
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-2 truncate", children: title }),
1201
+ subtitle && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground/70 ml-1 truncate text-[10px] tracking-normal normal-case", children: [
1202
+ "\xB7 ",
1203
+ subtitle
1204
+ ] })
1205
+ ] }),
1206
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1207
+ refreshedAt && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/60 hidden text-[10px] tracking-normal normal-case sm:inline", children: refreshedAt }),
1208
+ actions,
1209
+ expandable && /* @__PURE__ */ jsxRuntime.jsx(
1210
+ "button",
1211
+ {
1212
+ type: "button",
1213
+ onClick: () => setExpanded(true),
1214
+ "aria-label": `expand ${title}`,
1215
+ title: "expand to fullscreen",
1216
+ className: "text-muted-foreground/70 hover:text-foreground inline-flex h-6 w-6 items-center justify-center rounded-sm",
1217
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Maximize2, { className: "size-3" })
1218
+ }
1219
+ )
1220
+ ] })
1221
+ ] }),
1222
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 p-4 sm:p-5", children })
1223
+ ]
1224
+ }
1225
+ ),
1226
+ expandable && /* @__PURE__ */ jsxRuntime.jsx(
1227
+ Modal,
1228
+ {
1229
+ open: expanded,
1230
+ onOpenChange: setExpanded,
1231
+ title,
1232
+ ...subtitle ? { subtitle } : {},
1233
+ actions: refreshedAt ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/60 hidden text-[10px] tracking-normal normal-case sm:inline", children: refreshedAt }) : null,
1234
+ children
1235
+ }
1236
+ )
1237
+ ] });
1238
+ }
1239
+ function Sparkline({ data, width = 96, height = 24, area = false, className }) {
1240
+ if (data.length === 0) {
1241
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width, height, className: cn(className), "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx(
1242
+ "line",
1243
+ {
1244
+ x1: 0,
1245
+ x2: width,
1246
+ y1: height / 2,
1247
+ y2: height / 2,
1248
+ stroke: "currentColor",
1249
+ strokeWidth: 1,
1250
+ opacity: 0.25
1251
+ }
1252
+ ) });
1253
+ }
1254
+ const min = Math.min(...data);
1255
+ const max = Math.max(...data);
1256
+ const range = max - min || 1;
1257
+ const step = data.length === 1 ? width : width / (data.length - 1);
1258
+ const points = data.map((v, i) => {
1259
+ const x = i * step;
1260
+ const y = height - (v - min) / range * (height - 2) - 1;
1261
+ return [x, y];
1262
+ });
1263
+ const linePath = points.map(([x, y], i) => `${i === 0 ? "M" : "L"}${x.toFixed(2)},${y.toFixed(2)}`).join(" ");
1264
+ const areaPath = area ? `${linePath} L${width.toFixed(2)},${height.toFixed(2)} L0,${height.toFixed(2)} Z` : null;
1265
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1266
+ "svg",
1267
+ {
1268
+ width,
1269
+ height,
1270
+ viewBox: `0 0 ${width} ${height}`,
1271
+ className: cn("text-primary", className),
1272
+ "aria-hidden": true,
1273
+ children: [
1274
+ areaPath && /* @__PURE__ */ jsxRuntime.jsx("path", { d: areaPath, fill: "currentColor", opacity: 0.12 }),
1275
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: linePath, fill: "none", stroke: "currentColor", strokeWidth: 1.25 })
1276
+ ]
1277
+ }
1278
+ );
1279
+ }
1280
+ function StatBlock({ label, value, sub, delta, tone = "default", className }) {
1281
+ const toneClass = tone === "success" ? "text-success" : tone === "warning" ? "text-warning" : tone === "destructive" ? "text-destructive" : "text-foreground";
1282
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col gap-1", className), children: [
1283
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "label-mono text-[10px]", children: label }),
1284
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("font-display text-2xl leading-none", toneClass), children: value }),
1285
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [
1286
+ sub && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground font-mono text-[11px] tracking-wide lowercase", children: sub }),
1287
+ delta && /* @__PURE__ */ jsxRuntime.jsxs(
1288
+ "span",
1289
+ {
1290
+ className: cn(
1291
+ "font-mono text-[11px] tracking-wide",
1292
+ delta.value > 0 ? "text-success" : delta.value < 0 ? "text-destructive" : "text-muted-foreground"
1293
+ ),
1294
+ children: [
1295
+ delta.value > 0 ? "\u25B2" : delta.value < 0 ? "\u25BC" : "\xB7",
1296
+ " ",
1297
+ Math.abs(delta.value).toFixed(1),
1298
+ delta.suffix ?? "%"
1299
+ ]
1300
+ }
1301
+ )
1302
+ ] })
1303
+ ] });
1304
+ }
1305
+ var ACCENT = {
1306
+ primary: "bg-primary",
1307
+ warning: "bg-warning",
1308
+ destructive: "bg-destructive",
1309
+ success: "bg-success"
1310
+ };
1311
+ function Bar({ pct, accent = "primary", label, right, className }) {
1312
+ const clamped = Math.max(0, Math.min(100, pct));
1313
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(className), children: [
1314
+ (label || right) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline justify-between font-mono text-[10.5px] tracking-widest uppercase", children: [
1315
+ label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: label }),
1316
+ right && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-foreground tabular-nums", children: right })
1317
+ ] }),
1318
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-muted mt-1.5 h-1.5 w-full overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("h-full", ACCENT[accent]), style: { width: `${clamped}%` } }) })
1319
+ ] });
1320
+ }
1321
+ var TONE = {
1322
+ success: "text-success",
1323
+ warning: "text-warning",
1324
+ destructive: "text-destructive",
1325
+ muted: "text-muted-foreground",
1326
+ primary: "text-primary",
1327
+ info: "text-info"
1328
+ };
1329
+ function StatusPill({ label, tone = "muted", icon, className }) {
1330
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1331
+ "span",
1332
+ {
1333
+ className: cn(
1334
+ "inline-flex items-center gap-1.5 font-mono text-[11px] tracking-wider lowercase",
1335
+ TONE[tone],
1336
+ className
1337
+ ),
1338
+ children: [
1339
+ icon,
1340
+ label
1341
+ ]
1342
+ }
1343
+ );
1344
+ }
1345
+
1346
+ // src/format/index.ts
1347
+ var REFERENCE_BTC_USD = 95e3;
1348
+ function formatSats(sats) {
1349
+ return new Intl.NumberFormat("en-US").format(Math.round(sats));
1350
+ }
1351
+ function satsToUsd(sats, btcUsd = REFERENCE_BTC_USD) {
1352
+ const usd = sats / 1e8 * btcUsd;
1353
+ if (usd > 0 && usd < 0.01) return "< $0.01";
1354
+ const fractionDigits = usd >= 100 ? 0 : 2;
1355
+ return new Intl.NumberFormat("en-US", {
1356
+ style: "currency",
1357
+ currency: "USD",
1358
+ minimumFractionDigits: fractionDigits,
1359
+ maximumFractionDigits: fractionDigits
1360
+ }).format(usd);
1361
+ }
1362
+ function shortenAddress(value, head = 8, tail = 4) {
1363
+ if (!value) return "";
1364
+ if (value.length <= head + tail + 1) return value;
1365
+ return `${value.slice(0, head)}\u2026${value.slice(-tail)}`;
1366
+ }
1367
+ function SatsAmount({ sats, unit = true, usd = false, btcUsd, className }) {
1368
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("font-mono tabular-nums", className), children: [
1369
+ formatSats(sats),
1370
+ unit && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: " sats" }),
1371
+ usd && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
1372
+ " \xB7 ",
1373
+ satsToUsd(sats, btcUsd)
1374
+ ] })
1375
+ ] });
1376
+ }
1377
+ function CopyButton({
1378
+ value,
1379
+ label,
1380
+ clearAfter,
1381
+ size = "md",
1382
+ className,
1383
+ title,
1384
+ onCopied
1385
+ }) {
1386
+ const [copied, setCopied] = React2.useState(false);
1387
+ const resetRef = React2.useRef(null);
1388
+ const copy = React2.useCallback(async () => {
1389
+ try {
1390
+ await navigator.clipboard.writeText(value);
1391
+ setCopied(true);
1392
+ onCopied?.();
1393
+ if (resetRef.current) clearTimeout(resetRef.current);
1394
+ resetRef.current = setTimeout(() => setCopied(false), 1500);
1395
+ if (clearAfter && clearAfter > 0) {
1396
+ setTimeout(() => void wipeIfUnchanged(value), clearAfter * 1e3);
1397
+ }
1398
+ } catch {
1399
+ }
1400
+ }, [value, clearAfter, onCopied]);
1401
+ const iconSize = size === "sm" ? "size-3" : "size-3.5";
1402
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1403
+ "button",
1404
+ {
1405
+ type: "button",
1406
+ onClick: copy,
1407
+ "aria-label": title ?? (copied ? "copied" : "copy"),
1408
+ title: title ?? "copy",
1409
+ className: cn(
1410
+ "text-muted-foreground hover:text-foreground inline-flex items-center gap-1.5 rounded-sm transition-colors outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50",
1411
+ size === "sm" ? "text-[11px]" : "text-xs",
1412
+ className
1413
+ ),
1414
+ children: [
1415
+ copied ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: cn(iconSize, "text-success"), "aria-hidden": true }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Copy, { className: iconSize, "aria-hidden": true }),
1416
+ label && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", children: copied ? "copied" : label })
1417
+ ]
1418
+ }
1419
+ );
1420
+ }
1421
+ async function wipeIfUnchanged(expected) {
1422
+ try {
1423
+ const current = await navigator.clipboard.readText();
1424
+ if (current !== expected) return;
1425
+ await navigator.clipboard.writeText("");
1426
+ } catch {
1427
+ try {
1428
+ await navigator.clipboard.writeText("");
1429
+ } catch {
1430
+ }
1431
+ }
1432
+ }
1433
+ function BitcoinAddress({
1434
+ address,
1435
+ head = 8,
1436
+ tail = 4,
1437
+ copyable = true,
1438
+ full = false,
1439
+ className
1440
+ }) {
1441
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("inline-flex items-center gap-1.5 font-mono text-xs", className), children: [
1442
+ /* @__PURE__ */ jsxRuntime.jsx("span", { title: address, children: full ? address : shortenAddress(address, head, tail) }),
1443
+ copyable && /* @__PURE__ */ jsxRuntime.jsx(CopyButton, { value: address, size: "sm", title: "copy address" })
1444
+ ] });
1445
+ }
1446
+ function QrCode({ value, size = 200, label, className }) {
1447
+ const [svg, setSvg] = React2.useState(null);
1448
+ React2.useEffect(() => {
1449
+ let cancelled = false;
1450
+ QRCode__default.default.toString(value, {
1451
+ type: "svg",
1452
+ errorCorrectionLevel: "M",
1453
+ margin: 1,
1454
+ color: { dark: "#000000", light: "#ffffff" }
1455
+ }).then((out) => {
1456
+ if (!cancelled) setSvg(out);
1457
+ }).catch(() => {
1458
+ if (!cancelled) setSvg(null);
1459
+ });
1460
+ return () => {
1461
+ cancelled = true;
1462
+ };
1463
+ }, [value]);
1464
+ return /* @__PURE__ */ jsxRuntime.jsx(
1465
+ "div",
1466
+ {
1467
+ role: "img",
1468
+ "aria-label": label ?? `QR code for ${value.slice(0, 24)}\u2026`,
1469
+ className: "inline-block overflow-hidden rounded-md bg-white p-3 " + (className ?? ""),
1470
+ style: { width: size, height: size },
1471
+ children: svg ? /* @__PURE__ */ jsxRuntime.jsx(
1472
+ "div",
1473
+ {
1474
+ className: "size-full [&_svg]:block [&_svg]:size-full",
1475
+ dangerouslySetInnerHTML: { __html: svg }
1476
+ }
1477
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-muted/40 size-full animate-pulse rounded", "aria-hidden": true })
1478
+ }
1479
+ );
1480
+ }
1171
1481
 
1172
1482
  exports.Accordion = Accordion;
1173
1483
  exports.AccordionContent = AccordionContent;
@@ -1179,8 +1489,12 @@ exports.AlertTitle = AlertTitle;
1179
1489
  exports.AlertWithAction = AlertWithAction;
1180
1490
  exports.AlertWithCountdown = AlertWithCountdown;
1181
1491
  exports.Badge = Badge;
1492
+ exports.Bar = Bar;
1493
+ exports.BitcoinAddress = BitcoinAddress;
1182
1494
  exports.Button = Button;
1495
+ exports.Card = Card;
1183
1496
  exports.ConfirmHost = ConfirmHost;
1497
+ exports.CopyButton = CopyButton;
1184
1498
  exports.Dialog = Dialog;
1185
1499
  exports.DialogClose = DialogClose;
1186
1500
  exports.DialogContent = DialogContent;
@@ -1201,6 +1515,8 @@ exports.PopoverAnchor = PopoverAnchor;
1201
1515
  exports.PopoverContent = PopoverContent;
1202
1516
  exports.PopoverTrigger = PopoverTrigger;
1203
1517
  exports.PromptHost = PromptHost;
1518
+ exports.QrCode = QrCode;
1519
+ exports.SatsAmount = SatsAmount;
1204
1520
  exports.Select = Select;
1205
1521
  exports.SelectContent = SelectContent;
1206
1522
  exports.SelectGroup = SelectGroup;
@@ -1221,6 +1537,9 @@ exports.SheetPortal = SheetPortal;
1221
1537
  exports.SheetTitle = SheetTitle;
1222
1538
  exports.SheetTrigger = SheetTrigger;
1223
1539
  exports.Skeleton = Skeleton;
1540
+ exports.Sparkline = Sparkline;
1541
+ exports.StatBlock = StatBlock;
1542
+ exports.StatusPill = StatusPill;
1224
1543
  exports.Textarea = Textarea;
1225
1544
  exports.ThemeToggle = ThemeToggle;
1226
1545
  exports.ThemeToggleLink = ThemeToggleLink;