@stacknet/keyutils 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,132 @@
1
+ # @stacknet/keyutils
2
+
3
+ Reusable React components and hooks for buying, managing, and selling StackNet Node Keys.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add @stacknet/keyutils
9
+ ```
10
+
11
+ Peer dependencies: `react >= 18`, `react-dom >= 18`
12
+
13
+ ## Quick Start
14
+
15
+ ```tsx
16
+ import { BuyKeyWidget } from '@stacknet/keyutils/components';
17
+
18
+ <BuyKeyWidget
19
+ apiBaseUrl=""
20
+ stackId="stk_your_stack_id"
21
+ stackName="My App"
22
+ paymentMethods={['stripe', 'solana']}
23
+ merchantWallet="YOUR_SOLANA_WALLET_ADDRESS"
24
+ maxQuantity={5}
25
+ keyImage="/key-art.png"
26
+ aispImage="/aisp-chip.png"
27
+ onMintSuccess={(result) => console.log('Key generated:', result)}
28
+ />
29
+ ```
30
+
31
+ The `BuyKeyWidget` is fully self-contained — it wraps its own `KeyUtilsProvider` and manages all state internally. Zero setup.
32
+
33
+ ## Components
34
+
35
+ ### `BuyKeyWidget`
36
+
37
+ Main embeddable widget with three tabs: **Buy**, **Use**, **Sell**.
38
+
39
+ | Prop | Type | Description |
40
+ |------|------|-------------|
41
+ | `apiBaseUrl` | `string` | Base URL for API calls (use `""` for same-origin) |
42
+ | `stackId` | `string` | Stack identifier for payment config |
43
+ | `stackName` | `string` | Display name for the originating stack |
44
+ | `paymentMethods` | `('stripe' \| 'solana')[]` | Enabled payment methods |
45
+ | `merchantWallet` | `string` | Solana wallet address for crypto payments |
46
+ | `maxQuantity` | `number` | Max keys per purchase (default: 5) |
47
+ | `keyImage` | `string` | Image URL shown above price on Buy tab |
48
+ | `keyImageClass` | `string` | CSS class for key image (default: `w-1/2 mx-auto`) |
49
+ | `aispImage` | `string` | Image URL shown on Use tab |
50
+ | `termsUrl` | `string` | URL for terms link (default: `/terms`) |
51
+ | `solanaRpcUrl` | `string` | Solana RPC endpoint (default: mainnet) |
52
+ | `defaultTab` | `'buy' \| 'use' \| 'sell'` | Initial active tab |
53
+ | `onMintSuccess` | `(result) => void` | Called after successful key generation |
54
+ | `onMintError` | `(error) => void` | Called on generation failure |
55
+ | `onListingCreated` | `(listing) => void` | Called after marketplace listing |
56
+
57
+ ### `KeyUtilsProvider`
58
+
59
+ Context provider for using individual components and hooks outside of `BuyKeyWidget`.
60
+
61
+ ```tsx
62
+ import { KeyUtilsProvider, KeyList } from '@stacknet/keyutils/components';
63
+
64
+ <KeyUtilsProvider config={{ apiBaseUrl: '' }}>
65
+ <KeyList getKeyHref="/keys/get" />
66
+ </KeyUtilsProvider>
67
+ ```
68
+
69
+ ### Other Components
70
+
71
+ | Component | Description |
72
+ |-----------|-------------|
73
+ | `BuyPanel` | Purchase flow with pricing, quantity, and payment |
74
+ | `UsePanel` | Download links with platform detection |
75
+ | `SellPanel` | Marketplace listing interface |
76
+ | `KeyList` | Displays user's keys with balances |
77
+ | `KeyCard` | Single key with expandable ledger |
78
+ | `KeyBalanceBar` | Token balance progress bar |
79
+ | `SolanaPayButton` | Solana payment via Phantom wallet |
80
+ | `MintSuccess` | Post-purchase success display |
81
+
82
+ All components except `BuyKeyWidget` require a parent `KeyUtilsProvider`.
83
+
84
+ ## Hooks
85
+
86
+ ```tsx
87
+ import { usePricing, useKeys, useMintKey, useListKey } from '@stacknet/keyutils/hooks';
88
+ ```
89
+
90
+ | Hook | Returns | Description |
91
+ |------|---------|-------------|
92
+ | `usePricing()` | `{ pricing, loading, error, refresh }` | Live pricing with 60s auto-refresh |
93
+ | `useKeys()` | `{ keys, totalBalance, loading, error, refresh }` | User's node keys |
94
+ | `useKeyLedger(keyId)` | `{ entries, loading, error, refresh }` | Ledger for a specific key |
95
+ | `useMintKey()` | `{ mint, minting, result, error }` | Key generation after payment |
96
+ | `useListKey()` | `{ listKey, delistKey, listing, result, error }` | Marketplace list/delist |
97
+ | `useStripeCheckout()` | `{ createSession, loading, error }` | Stripe Checkout Session creation |
98
+
99
+ All hooks require a parent `KeyUtilsProvider`.
100
+
101
+ ## API Proxy Routes
102
+
103
+ The widget expects these proxy routes on the host app:
104
+
105
+ | Route | Method | Proxies To |
106
+ |-------|--------|------------|
107
+ | `/api/keys` | GET | `/api/v2/node-keys` |
108
+ | `/api/keys/pricing` | GET | `/api/v2/node-keys/pricing` |
109
+ | `/api/keys/mint` | POST | `/api/v2/node-keys/mint` |
110
+ | `/api/keys/:id` | GET | `/api/v2/node-keys/:id` |
111
+ | `/api/keys/:id/ledger` | GET | `/api/v2/node-keys/:id/ledger` |
112
+ | `/api/keys/:id/list` | POST/DELETE | `/api/v2/node-keys/:id/list` |
113
+ | `/api/keys/stripe-session` | POST | `/api/v2/node-keys/stripe-session` |
114
+
115
+ ## Sub-path Exports
116
+
117
+ ```ts
118
+ import { BuyKeyWidget } from '@stacknet/keyutils/components';
119
+ import { usePricing } from '@stacknet/keyutils/hooks';
120
+ import type { NodeKeyInfo } from '@stacknet/keyutils/types';
121
+ import { cn, formatTokens } from '@stacknet/keyutils/utils';
122
+ ```
123
+
124
+ ## Build
125
+
126
+ ```bash
127
+ pnpm --filter @stacknet/keyutils build
128
+ ```
129
+
130
+ ## License
131
+
132
+ MIT
@@ -0,0 +1,2 @@
1
+ 'use strict';var at=require('react'),jsxRuntime=require('react/jsx-runtime'),clsx=require('clsx'),tailwindMerge=require('tailwind-merge'),lucideReact=require('lucide-react');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var at__default=/*#__PURE__*/_interopDefault(at);var he=at.createContext(null);function N(){let e=at.useContext(he);if(!e)throw new Error("useKeyUtilsContext must be used within a KeyUtilsProvider");return e}function ee({config:e,callbacks:t={},children:r}){typeof e.apiBaseUrl!="string"&&console.error("[KeyUtils] apiBaseUrl must be a string");let s=at.useMemo(()=>({config:e,callbacks:t}),[e,t]);return jsxRuntime.jsx(he.Provider,{value:s,children:r})}function m(...e){return tailwindMerge.twMerge(clsx.clsx(e))}function L(e){return e==null?"0":e>=1e9?`${(e/1e9).toFixed(1)}B`:e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:e.toLocaleString()}function Ne(e,t){return t<=0?0:Math.min(100,Math.round(e/t*100))}function ve(e){return e>=70?"bg-green-500":e>=30?"bg-yellow-500":"bg-red-500"}function F(e){let t=typeof e=="string"&&/^\d+$/.test(e)?Number(e):e,r=new Date(t);return isNaN(r.getTime())?String(e):r.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}function ke(){if(typeof navigator>"u")return "mac";let e=navigator.userAgent.toLowerCase();return /iphone|ipad|ipod/.test(e)?"ios":/android/.test(e)?"android":/win/.test(e)?"windows":/linux/.test(e)?"linux":"mac"}function Pe(){let{config:e}=N(),[t,r]=at.useState(null),[s,g]=at.useState(true),[c,f]=at.useState(null),a=at.useRef(true),n=at.useRef(e.apiBaseUrl);n.current=e.apiBaseUrl;let d=async()=>{try{a.current&&(g(!0),f(null));let p=await fetch(`${n.current}/api/keys/pricing`);if(!p.ok){let i=`Pricing unavailable (${p.status})`;try{let o=await p.json();o.error&&(i=o.error);}catch{}throw new Error(i)}let u=await p.json();a.current&&r(u);}catch(p){a.current&&f(p instanceof Error?p.message:"Failed to fetch pricing");}finally{a.current&&g(false);}};return at.useEffect(()=>{a.current=true,d();let p=setInterval(d,6e4);return ()=>{a.current=false,clearInterval(p);}},[]),{pricing:t,loading:s,error:c,refresh:d}}function Ce(){let{config:e,callbacks:t}=N(),[r,s]=at.useState(false),[g,c]=at.useState(null),[f,a]=at.useState(null);return {mint:at.useCallback(async(d,p,u=1)=>{try{s(!0),a(null),t.onPaymentComplete?.(d,p);let i=await fetch(`${e.apiBaseUrl}/api/keys/mint`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paymentMethod:d,paymentRef:p,quantity:u})});if(!i.ok){let P="Key generation failed";try{let w=await i.json();w.error&&(P=w.error);}catch{}throw new Error(P)}let o=await i.json(),l=o.keys?.[0]||o,v={success:!0,key:{id:l.keyId||l.id||"",key:l.key||"",userId:l.userId||"",status:"active",tokenBalance:l.tokenBalance??l.currentTokenBalance??0,maxTokens:l.initialTokenBalance??l.tokenBalance??0,createdAt:String(l.createdAt||Date.now())},transactionId:p};return c(v),t.onMintSuccess?.(v),v}catch(i){let o=i instanceof Error?i:new Error("Key generation failed");return a(o.message),t.onMintError?.(o),{success:false,error:o.message}}finally{s(false);}},[e.apiBaseUrl,t]),minting:r,result:g,error:f}}function Ke(){let{config:e}=N(),[t,r]=at.useState(false),[s,g]=at.useState(null),c=at.useRef(true);return {createSession:at.useCallback(async(a,n)=>{try{r(!0),g(null);let d=await fetch(`${e.apiBaseUrl}/api/keys/stripe-session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({priceCents:a,quantity:n,stackId:e.stackId})});if(!d.ok){let u=`Checkout failed (${d.status})`;try{let i=await d.json();i.error&&(u=i.error);}catch{}throw new Error(u)}let{url:p}=await d.json();if(p){try{sessionStorage.setItem("keyutils_stripe_pending","1");}catch{}window.location.href=p;}else throw new Error("No checkout URL returned")}catch(d){if(c.current){let p=d instanceof Error?d.message:"Checkout failed";g(p),r(false);}}},[e.apiBaseUrl,e.stackId]),loading:t,error:s}}var Ze=/^[1-9A-HJ-NP-Za-km-z]{32,44}$/;function oe({amountSol:e,onSuccess:t,onError:r,disabled:s,className:g,style:c}){let{config:f}=N(),[a,n]=at.useState(false),[d,p]=at.useState(null),u=!!(f.merchantWallet&&Ze.test(f.merchantWallet)),i=at.useCallback(async()=>{if(u)try{n(!0),p(null);let{Connection:o,PublicKey:l,Transaction:v,SystemProgram:P,LAMPORTS_PER_SOL:w}=await import('@solana/web3.js'),R=typeof window<"u"?window.solana:void 0;if(!R||typeof R!="object"||!R.isPhantom)throw new Error("Phantom wallet not found. Please install Phantom.");let B=R,M;try{M=new l(f.merchantWallet);}catch{throw new Error("Invalid merchant wallet address.")}let _=await B.connect(),k=new l(_.publicKey.toString()),W=new o(f.solanaRpcUrl||"https://api.mainnet-beta.solana.com"),{blockhash:A}=await W.getLatestBlockhash(),Z=BigInt(Math.floor(e*1e9)),X=new v({recentBlockhash:A,feePayer:k}).add(P.transfer({fromPubkey:k,toPubkey:M,lamports:Number(Z)})),{signature:K}=await B.signAndSendTransaction(X);t(K);}catch(o){let l=o instanceof Error?o:new Error("Payment failed");p(l.message),r?.(l);}finally{n(false);}},[e,f.merchantWallet,u,t,r]);return jsxRuntime.jsxs("div",{children:[jsxRuntime.jsx("button",{onClick:i,disabled:s||a||!u,"aria-label":`Pay ${e.toFixed(4)} SOL`,className:m("flex w-full items-center justify-center gap-2 rounded-lg bg-purple-600 px-4 py-3 font-medium text-white transition-colors hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed",g),style:c,children:a?jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(lucideReact.Loader2,{className:"h-4 w-4 animate-spin"}),"Processing..."]}):jsxRuntime.jsxs(jsxRuntime.Fragment,{children:["Pay ",e.toFixed(4)," SOL"]})}),d&&jsxRuntime.jsx("p",{className:"mt-2 text-center text-xs text-red-500",children:d})]})}function ae({result:e,className:t,style:r}){let[s,g]=at.useState(false),c=at.useRef(null);at.useEffect(()=>()=>{c.current&&clearTimeout(c.current);},[]);let f=at.useCallback(()=>{e.key?.key&&(navigator.clipboard.writeText(e.key.key),g(true),c.current&&clearTimeout(c.current),c.current=setTimeout(()=>g(false),2e3));},[e.key?.key]);return !e.success||!e.key?null:jsxRuntime.jsxs("div",{className:m("rounded-lg border border-green-500/20 bg-green-500/10 p-6 text-center",t),style:r,children:[jsxRuntime.jsx(lucideReact.CheckCircle,{className:"mx-auto mb-3 h-12 w-12 text-green-500"}),jsxRuntime.jsx("h3",{className:"mb-2 text-lg font-semibold text-foreground",children:"Key Generated!"}),jsxRuntime.jsx("p",{className:"mb-4 text-sm text-muted-foreground",children:"Your node key has been successfully generated."}),jsxRuntime.jsxs("div",{className:"mx-auto flex max-w-md items-center gap-2 rounded-md border bg-background p-3",children:[jsxRuntime.jsx("code",{className:"flex-1 truncate text-xs text-foreground",children:e.key.key}),jsxRuntime.jsx("button",{onClick:f,"aria-label":"Copy key to clipboard",className:"shrink-0 rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",children:s?jsxRuntime.jsx(lucideReact.Check,{className:"h-4 w-4 text-green-500"}):jsxRuntime.jsx(lucideReact.Copy,{className:"h-4 w-4"})})]})]})}function I({className:e}){return jsxRuntime.jsx("div",{className:m("animate-pulse rounded-xl bg-muted",e)})}function ie(){return jsxRuntime.jsxs("div",{className:"space-y-6",children:[jsxRuntime.jsxs("div",{className:"px-6 py-10 flex flex-col items-center gap-3",children:[jsxRuntime.jsx(I,{className:"h-14 w-48"}),jsxRuntime.jsx(I,{className:"h-5 w-64"})]}),jsxRuntime.jsx(I,{className:"h-[72px] w-full rounded-2xl"}),jsxRuntime.jsx(I,{className:"h-[68px] w-full rounded-2xl"}),jsxRuntime.jsx(I,{className:"h-[60px] w-full rounded-2xl"})]})}function Y({count:e=3}){return jsxRuntime.jsx("div",{className:"space-y-3",children:Array.from({length:e}).map((t,r)=>jsxRuntime.jsxs("div",{className:"rounded-xl border p-4 space-y-3",children:[jsxRuntime.jsxs("div",{className:"flex items-center justify-between",children:[jsxRuntime.jsx(I,{className:"h-4 w-32"}),jsxRuntime.jsx(I,{className:"h-4 w-16"})]}),jsxRuntime.jsx(I,{className:"h-2 w-full rounded-full"}),jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx(I,{className:"h-3 w-24"}),jsxRuntime.jsx(I,{className:"h-3 w-20"})]})]},r))})}function pt({quantity:e,max:t,onChange:r}){return jsxRuntime.jsxs("div",{className:"flex items-center justify-between rounded-2xl border bg-muted/20 px-6 py-5",children:[jsxRuntime.jsx("span",{className:"text-sm font-medium text-foreground",children:"Quantity"}),jsxRuntime.jsxs("div",{className:"flex items-center gap-0 rounded-xl border bg-background",children:[jsxRuntime.jsx("button",{onClick:()=>r(Math.max(1,e-1)),disabled:e<=1,"aria-label":"Decrease quantity",className:"flex h-12 w-12 items-center justify-center rounded-l-xl text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-30",children:jsxRuntime.jsx(lucideReact.Minus,{className:"h-5 w-5"})}),jsxRuntime.jsx("span",{className:"flex h-12 w-14 items-center justify-center border-x text-xl font-bold text-foreground","aria-live":"polite",children:e}),jsxRuntime.jsx("button",{onClick:()=>r(Math.min(t,e+1)),disabled:e>=t,"aria-label":"Increase quantity",className:"flex h-12 w-12 items-center justify-center rounded-r-xl text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-30",children:jsxRuntime.jsx(lucideReact.Plus,{className:"h-5 w-5"})})]})]})}function Te({pricing:e,quantity:t}){let[r,s]=at.useState(false),g=e.priceUsd*t;return jsxRuntime.jsxs("div",{className:"rounded-2xl border bg-background",children:[jsxRuntime.jsxs("button",{onClick:()=>s(!r),"aria-expanded":r,className:"flex w-full items-center justify-between px-6 py-5 text-left",children:[jsxRuntime.jsxs("span",{className:"text-foreground",children:[jsxRuntime.jsxs("span",{className:"text-xl font-bold",children:["$",g.toFixed(2)]})," ",jsxRuntime.jsx("span",{className:"text-sm text-muted-foreground",children:"total fees included"})]}),r?jsxRuntime.jsx(lucideReact.ChevronUp,{className:"h-5 w-5 text-muted-foreground"}):jsxRuntime.jsx(lucideReact.ChevronDown,{className:"h-5 w-5 text-muted-foreground"})]}),r&&jsxRuntime.jsxs("div",{className:"border-t px-6 pb-8 pt-4 space-y-3 text-sm",children:[jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsxs("span",{className:"text-muted-foreground",children:["Node Key \xD7 ",t]}),jsxRuntime.jsxs("span",{className:"text-foreground",children:["$",(e.priceUsd*t).toFixed(2)]})]}),jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx("span",{className:"text-muted-foreground",children:"Keys sold"}),jsxRuntime.jsx("span",{className:"text-foreground",children:e.keysSold})]}),e.daysUntilHalving>0&&jsxRuntime.jsxs("div",{className:"flex justify-between text-muted-foreground",children:[jsxRuntime.jsx("span",{children:"Next refresh"}),jsxRuntime.jsxs("span",{children:[e.daysUntilHalving," day",e.daysUntilHalving!==1?"s":""]})]}),jsxRuntime.jsxs("div",{className:"flex justify-between border-t pt-3 font-semibold",children:[jsxRuntime.jsx("span",{className:"text-foreground",children:"Total"}),jsxRuntime.jsxs("span",{className:"text-foreground",children:["$",g.toFixed(2)]})]})]})]})}function le({className:e,style:t}){let{config:r}=N(),{pricing:s,loading:g,error:c,refresh:f}=Pe(),{mint:a,minting:n,result:d,error:p}=Ce(),{createSession:u,loading:i,error:o}=Ke(),[l,v]=at.useState(1),[P,w]=at.useState(r.paymentMethods?.includes("stripe")?"stripe":"solana"),[R,B]=at.useState(0),[M,_]=at.useState(false),k=at__default.default.useRef(l);k.current=l;let W=Math.max(1,r.maxQuantity||5),A=[...r.paymentMethods||["solana"]].sort((K,G)=>K==="stripe"?-1:G==="stripe"?1:0);if(at.useEffect(()=>{if(typeof window>"u")return;let G=new URLSearchParams(window.location.search).get("session_id");if(G&&/^cs_(test|live)_/.test(G)&&!M&&!d?.success){let ye=false;try{ye=sessionStorage.getItem("keyutils_stripe_pending")==="1";}catch{}if(!ye)return;try{sessionStorage.removeItem("keyutils_stripe_pending");}catch{}_(true);let be=new URL(window.location.href);be.searchParams.delete("session_id"),window.history.replaceState({},"",be.toString()),a("stripe",G,k.current);}},[M,d,a]),d?.success)return jsxRuntime.jsx("div",{className:m("flex flex-col items-center justify-center",e),style:t,children:jsxRuntime.jsx(ae,{result:d})});if(M||n)return jsxRuntime.jsx("div",{className:m("flex flex-col",e),style:t,children:jsxRuntime.jsx(ie,{})});if(g)return jsxRuntime.jsx("div",{className:m("flex flex-col",e),style:t,children:jsxRuntime.jsx(ie,{})});if(c||!s)return jsxRuntime.jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 py-24",e),style:t,children:[jsxRuntime.jsxs("div",{className:"rounded-xl border border-red-500/20 bg-red-500/5 px-6 py-5 text-center max-w-sm",children:[jsxRuntime.jsx("p",{className:"text-sm font-medium text-foreground",children:"Unable to load pricing"}),jsxRuntime.jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:c||"Please check your connection and try again."})]}),jsxRuntime.jsx("button",{onClick:()=>f(),className:"text-sm text-muted-foreground hover:text-foreground transition-colors",children:"Try again"})]});let Z=async K=>{B(2),await a(P,K,l);},X=()=>{u(s.priceCents,l);};return jsxRuntime.jsx("div",{className:m("flex flex-col",e),style:t,children:jsxRuntime.jsxs("div",{className:"space-y-6",children:[R===0&&jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[r.keyImage&&jsxRuntime.jsx("img",{src:r.keyImage,alt:"Node Key",className:r.keyImageClass||"w-1/2 mx-auto"}),jsxRuntime.jsxs("div",{className:"px-6 py-10 text-center",children:[jsxRuntime.jsxs("p",{className:"text-5xl font-bold tracking-tight text-foreground md:text-6xl",children:["$",s.priceUsd.toFixed(2)]}),jsxRuntime.jsxs("p",{className:"mt-2 text-base text-muted-foreground",children:["per key \xB7 ",s.tokenAllocationFormatted||L(s.tokenAllocation)," tokens included"]})]}),jsxRuntime.jsx(pt,{quantity:l,max:W,onChange:v}),jsxRuntime.jsx(Te,{pricing:s,quantity:l}),jsxRuntime.jsx("button",{onClick:()=>B(1),className:"w-full rounded-2xl bg-foreground py-5 text-center text-base font-semibold text-background transition-colors hover:bg-foreground/90",children:"Continue to Payment"})]}),R===1&&jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[A.length>1&&jsxRuntime.jsx("div",{role:"tablist",className:"flex gap-1 rounded-2xl bg-muted/50 p-1.5",children:A.map(K=>jsxRuntime.jsxs("button",{role:"tab","aria-selected":P===K,onClick:()=>w(K),className:m("flex flex-1 items-center justify-center gap-2.5 rounded-xl py-4 text-sm font-medium transition-colors",P===K?"bg-background text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:[K==="stripe"?jsxRuntime.jsx(lucideReact.CreditCard,{className:"h-5 w-5"}):jsxRuntime.jsx(lucideReact.Wallet,{className:"h-5 w-5"}),K==="stripe"?"Credit Card":"Crypto"]},K))}),jsxRuntime.jsx(Te,{pricing:s,quantity:l}),P==="stripe"?jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx("button",{onClick:X,disabled:i,className:"flex w-full items-center justify-center gap-2 rounded-2xl bg-foreground py-5 text-base font-semibold text-background transition-colors hover:bg-foreground/90 disabled:opacity-50",children:i?jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(lucideReact.Loader2,{className:"h-5 w-5 animate-spin"}),"Redirecting to Stripe..."]}):jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(lucideReact.CreditCard,{className:"h-5 w-5"}),"Pay with Card"]})}),o&&jsxRuntime.jsx("p",{className:"text-center text-sm text-red-500",children:o})]}):P==="solana"?jsxRuntime.jsx(oe,{amountSol:s.priceSol??0,onSuccess:Z,className:"rounded-2xl py-5 text-base"}):null,p&&jsxRuntime.jsx("p",{className:"text-center text-sm text-red-500",children:p}),jsxRuntime.jsx("button",{onClick:()=>B(0),className:"w-full py-3 text-center text-sm text-muted-foreground hover:text-foreground transition-colors",children:"\u2190 Back to Configure"})]})]})})}var bt={ios:"iOS",android:"Android",windows:"Windows",mac:"macOS",linux:"Linux"},ht=["mac","windows","linux","ios","android"];function ce({downloads:e,className:t,style:r}){let{config:s}=N(),g=at.useMemo(()=>ke(),[]),c=e||ht.map(n=>({platform:n,label:bt[n],url:"#",compatible:n===g})),f=c.find(n=>n.compatible)||c[0],a=c.filter(n=>n.platform!==f.platform);return jsxRuntime.jsxs("div",{className:m("space-y-8",t),style:r,children:[jsxRuntime.jsxs("div",{className:"flex flex-col items-center gap-6 pt-4 mt-4",children:[s.aispImage&&jsxRuntime.jsx("img",{src:s.aispImage,alt:"aiSP",className:"w-full sm:w-1/2 mx-auto px-4 sm:px-0"}),jsxRuntime.jsxs("div",{className:"text-center",children:[jsxRuntime.jsx("p",{className:"text-lg sm:text-2xl font-medium text-foreground",children:"Download the AI Service Provider App"}),jsxRuntime.jsx("p",{className:"mt-1 text-sm sm:text-base text-muted-foreground",children:"Load your keys and run your node."})]}),jsxRuntime.jsxs("a",{href:f.url,className:"inline-flex items-center gap-3 rounded-full px-10 py-4 text-lg font-semibold text-white transition-colors hover:opacity-90",style:{backgroundColor:"#0900f4"},children:[jsxRuntime.jsx(lucideReact.Download,{className:"h-5 w-5"}),"Get aiSP for ",f.label]})]}),jsxRuntime.jsxs("p",{className:"text-center text-sm text-muted-foreground",children:["aiSP is also available on"," ",a.map((n,d)=>jsxRuntime.jsxs(at__default.default.Fragment,{children:[d>0&&(d===a.length-1?" and ":", "),jsxRuntime.jsx("a",{href:n.url,className:"font-medium underline transition-colors hover:text-foreground",style:{color:"#0900f4"},children:n.label})]},n.platform)),"."]}),jsxRuntime.jsxs("div",{className:"flex flex-col items-center gap-2 mt-12",children:[jsxRuntime.jsxs("p",{className:"text-center text-xs text-muted-foreground flex justify-center gap-0.5 flex-wrap",children:["Source code is available on",jsxRuntime.jsx("a",{href:"https://github.com/stack-net",target:"_blank",rel:"noopener noreferrer",className:"text-foreground underline hover:text-foreground/80 color-[#0900f4]",children:"Github"})]}),jsxRuntime.jsxs("p",{className:"text-center text-xs text-muted-foreground flex justify-center gap-0.5 flex-wrap",children:["By downloading, you agree to the",jsxRuntime.jsx("a",{href:s.termsUrl||"/terms",className:"text-foreground underline hover:text-foreground/80",children:"Open Source Applications Terms"})]})]})]})}function wt(e){return {id:e.id||"",key:e.key||e.keyPrefix||"",userId:e.userId||e.user_id||"",status:e.status||"active",tokenBalance:e.tokenBalance??e.currentTokenBalance??e.current_token_balance??0,maxTokens:e.maxTokens??e.initialTokenBalance??e.initial_token_balance??0,createdAt:String(e.createdAt??e.created_at??""),expiresAt:e.expiresAt,label:e.label||e.keyPrefix||e.key_prefix||void 0,paperWork:e.paperWork||e.paper_work||void 0,stackId:e.stackId,stackName:e.stackName}}function J(){let{config:e}=N(),[t,r]=at.useState([]),[s,g]=at.useState(true),[c,f]=at.useState(null),a=at.useRef(true),n=e.apiBaseUrl,d=at.useCallback(async()=>{try{g(!0),f(null);let u=await fetch(`${n}/api/keys`);if(!u.ok){let l=`Failed to fetch keys (${u.status})`;try{let v=await u.json();v.error&&(l=v.error);}catch{}throw new Error(l)}let i=await u.json(),o=Array.isArray(i)?i:i.keys??[];a.current&&r(o.map(wt));}catch(u){a.current&&f(u instanceof Error?u.message:"Failed to fetch keys");}finally{a.current&&g(false);}},[n]);at.useEffect(()=>(a.current=true,d(),()=>{a.current=false;}),[d]);let p=t.reduce((u,i)=>u+i.tokenBalance,0);return {keys:t,totalBalance:p,loading:s,error:c,refresh:d}}function Ie(){let{config:e,callbacks:t}=N(),[r,s]=at.useState(false),[g,c]=at.useState(null),[f,a]=at.useState(null),n=at.useRef(true),d=at.useCallback(async(u,i)=>{try{s(!0),a(null);let o=await fetch(`${e.apiBaseUrl}/api/keys/${u}/list`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({askPriceCents:i,stackId:e.stackId,stackName:e.stackName})});if(!o.ok){let v=`Listing failed (${o.status})`;try{let P=await o.json();P.error&&(v=P.error);}catch{}throw new Error(v)}let l=await o.json();return n.current&&(c(l),t.onListingCreated?.(l)),l}catch(o){let l=o instanceof Error?o.message:"Listing failed";return n.current&&a(l),null}finally{n.current&&s(false);}},[e.apiBaseUrl,e.stackId,e.stackName,t]),p=at.useCallback(async u=>{try{s(!0),a(null);let i=await fetch(`${e.apiBaseUrl}/api/keys/${u}/list`,{method:"DELETE"});if(!i.ok){let o=`Delist failed (${i.status})`;try{let l=await i.json();l.error&&(o=l.error);}catch{}throw new Error(o)}return n.current&&c(null),!0}catch(i){let o=i instanceof Error?i.message:"Delist failed";return n.current&&a(o),false}finally{n.current&&s(false);}},[e.apiBaseUrl]);return {listKey:d,delistKey:p,listing:r,result:g,error:f}}function z({current:e,max:t,className:r,style:s,showLabel:g=true}){let c=Ne(e,t),f=ve(c);return jsxRuntime.jsxs("div",{className:m("w-full",r),style:s,children:[g&&jsxRuntime.jsxs("div",{className:"mb-1 flex items-center justify-between text-xs text-muted-foreground",children:[jsxRuntime.jsxs("span",{children:[L(e)," / ",L(t)]}),jsxRuntime.jsxs("span",{children:[c,"%"]})]}),jsxRuntime.jsx("div",{className:"h-2 w-full overflow-hidden rounded-full bg-muted",children:jsxRuntime.jsx("div",{className:m("h-full rounded-full transition-all duration-500",f),style:{width:`${c}%`}})})]})}function Ut({nodeKey:e,selected:t,onSelect:r}){return jsxRuntime.jsxs("button",{onClick:r,className:m("flex w-full items-start gap-3 rounded-xl border p-4 text-left transition-all",t?"border-foreground/30 bg-foreground/5 ring-1 ring-foreground/10":"border-border hover:border-foreground/20"),children:[jsxRuntime.jsx("div",{className:m("mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full border-2 transition-colors",t?"border-foreground bg-foreground":"border-muted-foreground/40"),children:t&&jsxRuntime.jsx(lucideReact.Check,{className:"h-3 w-3 text-background"})}),jsxRuntime.jsxs("div",{className:"min-w-0 flex-1",children:[jsxRuntime.jsxs("div",{className:"flex items-center justify-between",children:[jsxRuntime.jsx("span",{className:"text-sm font-medium text-foreground",children:e.label||e.id.slice(0,16)}),jsxRuntime.jsx("span",{className:m("text-xs font-medium capitalize",e.status==="active"?"text-green-500":"text-muted-foreground"),children:e.status})]}),jsxRuntime.jsx(z,{current:e.tokenBalance,max:e.maxTokens,className:"mt-2"})]})]})}function fe({className:e,style:t}){let{config:r}=N(),{keys:s,loading:g,error:c,refresh:f}=J(),{listKey:a,listing:n,result:d,error:p}=Ie(),[u,i]=at.useState(null),[o,l]=at.useState(""),[v,P]=at.useState(false),w=s.find(k=>k.id===u),R=parseInt(o.replace(/[^0-9]/g,""),10)||0,B=w&&R>0&&v&&!n,M=async()=>{if(!B||!w)return;let k=R*100;await a(w.id,k)&&f();};if(d)return jsxRuntime.jsxs("div",{className:m("flex flex-col items-center gap-4 rounded-xl border border-green-500/20 bg-green-500/10 py-12",e),style:t,children:[jsxRuntime.jsx(lucideReact.CheckCircle,{className:"h-12 w-12 text-green-500"}),jsxRuntime.jsx("h3",{className:"text-lg font-semibold text-foreground",children:"Key Listed!"}),jsxRuntime.jsx("p",{className:"text-sm text-muted-foreground",children:"Your key is now on the Global Open AI Network marketplace."}),jsxRuntime.jsxs("p",{className:"text-xs text-muted-foreground",children:["Listing ID: ",jsxRuntime.jsx("code",{children:d.listingId})]})]});if(g)return jsxRuntime.jsxs("div",{className:m("space-y-5 px-1",e),style:t,children:[jsxRuntime.jsx("h3",{className:"text-2xl mt-3 mb-2 font-semibold text-foreground",children:"Your Keys"}),jsxRuntime.jsx(Y,{count:2})]});if(c)return jsxRuntime.jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 py-16",e),style:t,children:[jsxRuntime.jsxs("div",{className:"rounded-xl border border-red-500/20 bg-red-500/5 px-6 py-5 text-center max-w-sm",children:[jsxRuntime.jsx("p",{className:"text-sm font-medium text-foreground",children:"Unable to load your keys"}),jsxRuntime.jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:c})]}),jsxRuntime.jsx("button",{onClick:()=>f(),className:"text-sm text-muted-foreground hover:text-foreground transition-colors",children:"Try again"})]});let _=s.filter(k=>k.status==="active");return _.length===0?jsxRuntime.jsxs("div",{className:m("flex flex-col items-center justify-center gap-3 rounded-xl border border-dashed py-16",e),style:t,children:[jsxRuntime.jsx(lucideReact.AlertTriangle,{className:"h-8 w-8 text-muted-foreground"}),jsxRuntime.jsx("p",{className:"text-sm text-muted-foreground",children:"No active keys to sell."})]}):jsxRuntime.jsxs("div",{className:m("space-y-5",e),style:t,children:[jsxRuntime.jsxs("div",{children:[jsxRuntime.jsx("h3",{className:"text-2xl mt-3 mb-2 font-semibold text-foreground",children:"Your Keys"}),jsxRuntime.jsx("div",{className:"space-y-2",children:_.map(k=>jsxRuntime.jsx(Ut,{nodeKey:k,selected:u===k.id,onSelect:()=>i(k.id)},k.id))})]}),w&&jsxRuntime.jsxs("div",{className:"rounded-xl border bg-muted/30 p-5 space-y-2 text-sm",children:[jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx("span",{className:"text-muted-foreground",children:"Key ID"}),jsxRuntime.jsx("code",{className:"text-xs text-foreground",children:w.id})]}),jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx("span",{className:"text-muted-foreground",children:"Originating Stack"}),jsxRuntime.jsx("span",{className:"text-foreground",children:r.stackName||"Unknown"})]}),jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx("span",{className:"text-muted-foreground",children:"Tokens (remaining / total)"}),jsxRuntime.jsxs("span",{className:"text-foreground",children:[L(w.tokenBalance)," / ",L(w.maxTokens)]})]}),w.paperWork&&jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx("span",{className:"text-muted-foreground",children:"Paper earned"}),jsxRuntime.jsx("span",{className:"text-foreground",children:w.paperWork})]}),jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx("span",{className:"text-muted-foreground",children:"Created"}),jsxRuntime.jsx("span",{className:"text-foreground",children:F(w.createdAt)})]})]}),jsxRuntime.jsxs("div",{children:[jsxRuntime.jsx("label",{htmlFor:"listing-price",className:"mb-2 block text-sm font-semibold text-foreground",children:"Listing Price (USD)"}),jsxRuntime.jsxs("div",{className:"relative",children:[jsxRuntime.jsx("span",{className:"absolute left-4 top-1/2 -translate-y-1/2 text-2xl font-bold text-muted-foreground",children:"$"}),jsxRuntime.jsx("input",{id:"listing-price",type:"text",inputMode:"numeric",value:o,onChange:k=>{let W=k.target.value.replace(/[^0-9]/g,""),A=parseInt(W,10);l(isNaN(A)?"":A.toLocaleString("en-US"));},placeholder:"0",className:"w-full rounded-xl border bg-background py-5 pl-10 pr-4 text-3xl font-bold text-foreground placeholder:text-muted-foreground/30 focus:outline-none focus:ring-2 focus:ring-foreground/20"})]})]}),jsxRuntime.jsxs("label",{className:"flex items-start gap-3 cursor-pointer",children:[jsxRuntime.jsx("input",{type:"checkbox",checked:v,onChange:k=>P(k.target.checked),className:"sr-only"}),jsxRuntime.jsx("div",{"aria-hidden":"true",className:m("mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded border-2 transition-colors",v?"border-foreground bg-foreground":"border-muted-foreground/40"),children:v&&jsxRuntime.jsx(lucideReact.Check,{className:"h-3 w-3 text-background"})}),jsxRuntime.jsxs("span",{className:"text-xs text-muted-foreground leading-relaxed",children:["I agree to list this key on the Global Open AI Network marketplace. The key will be deactivated upon sale and transferred to the buyer. Listing fees may apply."," ",jsxRuntime.jsx("a",{href:r.termsUrl||"/terms",className:"text-foreground underline",children:"Terms"})]})]}),p&&jsxRuntime.jsx("p",{className:"text-sm text-red-500",children:p}),jsxRuntime.jsxs("button",{onClick:M,disabled:!B,className:m("flex w-full items-center justify-center gap-2 rounded-xl py-4 text-sm font-semibold transition-colors",B?"bg-foreground text-background hover:bg-foreground/90":"bg-muted text-muted-foreground cursor-not-allowed"),children:[jsxRuntime.jsx(lucideReact.ExternalLink,{className:"h-4 w-4"}),n?"Listing...":"List on Marketplace"]})]})}var Bt=[{id:"buy",label:"Buy"},{id:"use",label:"Use"},{id:"sell",label:"Sell"}];function Lt({active:e,onChange:t}){return jsxRuntime.jsx("div",{role:"tablist","aria-label":"Key actions",className:"flex gap-1 rounded-2xl bg-muted/50 p-1.5",children:Bt.map(({id:r,label:s})=>jsxRuntime.jsx("button",{role:"tab","aria-selected":e===r,"aria-controls":`panel-${r}`,id:`tab-${r}`,onClick:()=>t(r),className:m("flex flex-1 items-center justify-center gap-2.5 rounded-xl py-3.5 text-sm font-medium transition-all",e===r?"bg-background text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:jsxRuntime.jsx("span",{className:"inline",children:s})},r))})}function Tt({defaultTab:e="buy",className:t,style:r}){let[s,g]=at.useState(e);return jsxRuntime.jsxs("div",{className:m("flex flex-col",t),style:r,children:[jsxRuntime.jsx("div",{className:"sticky top-0 z-10 bg-background/80 px-5 pb-3 pt-2 sm:pt-5 backdrop-blur-sm sm:px-10 md:px-16 lg:px-20",children:jsxRuntime.jsx("div",{className:"mx-auto max-w-xl",children:jsxRuntime.jsx(Lt,{active:s,onChange:g})})}),jsxRuntime.jsx("div",{className:"flex flex-1 flex-col px-5 pb-8 sm:px-10 md:px-16 md:pb-12 lg:px-20",children:jsxRuntime.jsx("div",{className:"mx-auto w-full max-w-xl flex-1",children:jsxRuntime.jsxs("div",{role:"tabpanel",id:`panel-${s}`,"aria-labelledby":`tab-${s}`,children:[s==="buy"&&jsxRuntime.jsx(le,{className:"flex-1"}),s==="use"&&jsxRuntime.jsx(ce,{className:"flex-1"}),s==="sell"&&jsxRuntime.jsx(fe,{className:"flex-1"})]})})})]})}function Et({apiBaseUrl:e,stackId:t,stackName:r,paymentMethods:s,merchantWallet:g,stripePublicKey:c,theme:f,maxQuantity:a,keyImage:n,keyImageClass:d,aispImage:p,termsUrl:u,onMintSuccess:i,onMintError:o,onPaymentStart:l,onPaymentComplete:v,onListingCreated:P,defaultTab:w,className:R,style:B}){return jsxRuntime.jsx(ee,{config:{apiBaseUrl:e,stackId:t,stackName:r,paymentMethods:s,merchantWallet:g,stripePublicKey:c,theme:f,maxQuantity:a,keyImage:n,keyImageClass:d,aispImage:p,termsUrl:u},callbacks:{onMintSuccess:i,onMintError:o,onPaymentStart:l,onPaymentComplete:v,onListingCreated:P},children:jsxRuntime.jsx(Tt,{defaultTab:w,className:R,style:B})})}function Ae(e){let{config:t}=N(),[r,s]=at.useState([]),[g,c]=at.useState(false),[f,a]=at.useState(null),n=at.useRef(true),d=t.apiBaseUrl,p=at.useCallback(async()=>{if(e)try{c(!0),a(null);let u=await fetch(`${d}/api/keys/${e}/ledger`);if(!u.ok){let o=`Failed to fetch ledger (${u.status})`;try{let l=await u.json();l.error&&(o=l.error);}catch{}throw new Error(o)}let i=await u.json();n.current&&s(Array.isArray(i)?i:i.entries??[]);}catch(u){n.current&&a(u instanceof Error?u.message:"Failed to fetch ledger");}finally{n.current&&c(false);}},[d,e]);return at.useEffect(()=>(n.current=true,p(),()=>{n.current=false;}),[p]),{entries:r,loading:g,error:f,refresh:p}}var ge=20;function xe({nodeKey:e,className:t,style:r}){let[s,g]=at.useState(false),[c,f]=at.useState(false),a=at.useRef(null),{entries:n,loading:d}=Ae(s?e.id:null);at.useEffect(()=>()=>{a.current&&clearTimeout(a.current);},[]);let p=at.useCallback(()=>{e.key&&(navigator.clipboard.writeText(e.key),f(true),a.current&&clearTimeout(a.current),a.current=setTimeout(()=>f(false),2e3));},[e.key]),u=e.status==="active"?"text-green-500":e.status==="expired"?"text-red-500":"text-yellow-500",i=n.slice(0,ge);return jsxRuntime.jsxs("div",{className:m("rounded-lg border bg-background",t),style:r,children:[jsxRuntime.jsxs("div",{className:"p-4",children:[jsxRuntime.jsxs("div",{className:"mb-3 flex items-center justify-between",children:[jsxRuntime.jsxs("div",{className:"flex items-center gap-2",children:[jsxRuntime.jsx("button",{onClick:()=>g(!s),"aria-expanded":s,"aria-label":s?"Collapse ledger":"Expand ledger",className:"text-muted-foreground hover:text-foreground",children:s?jsxRuntime.jsx(lucideReact.ChevronDown,{className:"h-4 w-4"}):jsxRuntime.jsx(lucideReact.ChevronRight,{className:"h-4 w-4"})}),jsxRuntime.jsx("span",{className:"text-sm font-medium text-foreground",children:e.label||`Key ${e.id.slice(0,8)}`})]}),jsxRuntime.jsx("span",{className:m("text-xs font-medium capitalize",u),children:e.status})]}),e.key&&jsxRuntime.jsxs("div",{className:"mb-3 flex items-center gap-2",children:[jsxRuntime.jsx("code",{className:"flex-1 truncate rounded bg-muted px-2 py-1 text-xs text-muted-foreground",children:e.key}),jsxRuntime.jsx("button",{onClick:p,"aria-label":"Copy key to clipboard",className:"shrink-0 rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",children:c?jsxRuntime.jsx(lucideReact.Check,{className:"h-3.5 w-3.5 text-green-500"}):jsxRuntime.jsx(lucideReact.Copy,{className:"h-3.5 w-3.5"})})]}),jsxRuntime.jsx(z,{current:e.tokenBalance,max:e.maxTokens}),jsxRuntime.jsxs("div",{className:"mt-2 flex items-center justify-between text-xs text-muted-foreground",children:[jsxRuntime.jsxs("span",{children:["Created ",F(e.createdAt)]}),e.expiresAt&&jsxRuntime.jsxs("span",{children:["Expires ",F(e.expiresAt)]})]})]}),s&&jsxRuntime.jsxs("div",{className:"border-t px-4 py-3",children:[jsxRuntime.jsx("h4",{className:"mb-2 text-xs font-medium text-muted-foreground",children:"Ledger"}),d?jsxRuntime.jsx("p",{className:"text-xs text-muted-foreground",children:"Loading..."}):i.length===0?jsxRuntime.jsx("p",{className:"text-xs text-muted-foreground",children:"No ledger entries."}):jsxRuntime.jsxs("div",{className:"space-y-1.5",children:[i.map(o=>jsxRuntime.jsxs("div",{className:"flex items-center justify-between text-xs",children:[jsxRuntime.jsxs("div",{className:"flex items-center gap-2",children:[jsxRuntime.jsxs("span",{className:m("font-medium",o.type==="credit"||o.type==="reward"?"text-green-500":"text-red-500"),children:[o.type==="credit"||o.type==="reward"?"+":"-",L(Math.abs(o.amount))]}),jsxRuntime.jsx("span",{className:"text-muted-foreground",children:o.description})]}),jsxRuntime.jsx("span",{className:"text-muted-foreground",children:F(o.createdAt)})]},o.id)),n.length>ge&&jsxRuntime.jsxs("p",{className:"pt-1 text-xs text-muted-foreground",children:["Showing ",ge," of ",n.length," entries"]})]})]})]})}function Qt({getKeyHref:e="/keys/get",className:t,style:r}){let{keys:s,totalBalance:g,loading:c,error:f,refresh:a}=J();return c?jsxRuntime.jsx("div",{className:m("space-y-4",t),style:r,children:jsxRuntime.jsx(Y,{count:3})}):f?jsxRuntime.jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 py-12",t),style:r,children:[jsxRuntime.jsxs("div",{className:"rounded-xl border border-red-500/20 bg-red-500/5 px-6 py-5 text-center max-w-sm",children:[jsxRuntime.jsx("p",{className:"text-sm font-medium text-foreground",children:"Unable to load your keys"}),jsxRuntime.jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:f})]}),jsxRuntime.jsx("button",{onClick:()=>a(),className:"text-sm text-muted-foreground hover:text-foreground transition-colors",children:"Try again"})]}):s.length===0?jsxRuntime.jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 rounded-lg border border-dashed py-12",t),style:r,children:[jsxRuntime.jsx(lucideReact.KeyRound,{className:"h-10 w-10 text-muted-foreground"}),jsxRuntime.jsxs("div",{className:"text-center",children:[jsxRuntime.jsx("p",{className:"text-sm font-medium text-foreground",children:"No keys yet"}),jsxRuntime.jsx("p",{className:"text-xs text-muted-foreground",children:"Get your first node key to start earning."})]}),e&&jsxRuntime.jsx("a",{href:e,className:"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background transition-colors hover:bg-foreground/90",children:"Get a Key"})]}):jsxRuntime.jsxs("div",{className:m("space-y-4",t),style:r,children:[jsxRuntime.jsxs("div",{className:"flex items-center justify-between",children:[jsxRuntime.jsxs("p",{className:"text-sm text-muted-foreground",children:[s.length," key",s.length!==1?"s":""," \xB7 Total balance: ",L(g)]}),e&&jsxRuntime.jsx("a",{href:e,className:"rounded-md bg-foreground px-3 py-1.5 text-xs font-medium text-background transition-colors hover:bg-foreground/90",children:"Get a Key"})]}),s.map(n=>jsxRuntime.jsx(xe,{nodeKey:n},n.id))]})}
2
+ exports.BuyKeyWidget=Et;exports.BuyPanel=le;exports.KeyBalanceBar=z;exports.KeyCard=xe;exports.KeyList=Qt;exports.KeyUtilsProvider=ee;exports.MintSuccess=ae;exports.SellPanel=fe;exports.SolanaPayButton=oe;exports.UsePanel=ce;exports.useKeyUtilsContext=N;
@@ -0,0 +1,83 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React from 'react';
3
+ import { KeyUtilsConfig, KeyUtilsCallbacks, KeyWidgetTab, PlatformDownload, NodeKeyInfo, MintResult } from '../types/index.cjs';
4
+
5
+ interface KeyUtilsContextValue {
6
+ config: KeyUtilsConfig;
7
+ callbacks: KeyUtilsCallbacks;
8
+ }
9
+ declare function useKeyUtilsContext(): KeyUtilsContextValue;
10
+ interface KeyUtilsProviderProps {
11
+ config: KeyUtilsConfig;
12
+ callbacks?: KeyUtilsCallbacks;
13
+ children: React.ReactNode;
14
+ }
15
+ declare function KeyUtilsProvider({ config, callbacks, children, }: KeyUtilsProviderProps): react_jsx_runtime.JSX.Element;
16
+
17
+ interface BuyKeyWidgetProps extends KeyUtilsConfig, KeyUtilsCallbacks {
18
+ defaultTab?: KeyWidgetTab;
19
+ className?: string;
20
+ style?: React.CSSProperties;
21
+ }
22
+ declare function BuyKeyWidget({ apiBaseUrl, stackId, stackName, paymentMethods, merchantWallet, stripePublicKey, theme, maxQuantity, keyImage, keyImageClass, aispImage, termsUrl, onMintSuccess, onMintError, onPaymentStart, onPaymentComplete, onListingCreated, defaultTab, className, style, }: BuyKeyWidgetProps): react_jsx_runtime.JSX.Element;
23
+
24
+ interface BuyPanelProps {
25
+ className?: string;
26
+ style?: React.CSSProperties;
27
+ }
28
+ declare function BuyPanel({ className, style }: BuyPanelProps): react_jsx_runtime.JSX.Element;
29
+
30
+ interface UsePanelProps {
31
+ downloads?: PlatformDownload[];
32
+ className?: string;
33
+ style?: React.CSSProperties;
34
+ }
35
+ declare function UsePanel({ downloads, className, style }: UsePanelProps): react_jsx_runtime.JSX.Element;
36
+
37
+ interface SellPanelProps {
38
+ className?: string;
39
+ style?: React.CSSProperties;
40
+ }
41
+ declare function SellPanel({ className, style }: SellPanelProps): react_jsx_runtime.JSX.Element;
42
+
43
+ interface KeyListProps {
44
+ getKeyHref?: string;
45
+ className?: string;
46
+ style?: React.CSSProperties;
47
+ }
48
+ declare function KeyList({ getKeyHref, className, style }: KeyListProps): react_jsx_runtime.JSX.Element;
49
+
50
+ interface KeyCardProps {
51
+ nodeKey: NodeKeyInfo;
52
+ className?: string;
53
+ style?: React.CSSProperties;
54
+ }
55
+ declare function KeyCard({ nodeKey, className, style }: KeyCardProps): react_jsx_runtime.JSX.Element;
56
+
57
+ interface KeyBalanceBarProps {
58
+ current: number;
59
+ max: number;
60
+ className?: string;
61
+ style?: React.CSSProperties;
62
+ showLabel?: boolean;
63
+ }
64
+ declare function KeyBalanceBar({ current, max, className, style, showLabel, }: KeyBalanceBarProps): react_jsx_runtime.JSX.Element;
65
+
66
+ interface SolanaPayButtonProps {
67
+ amountSol: number;
68
+ onSuccess: (transactionId: string) => void;
69
+ onError?: (error: Error) => void;
70
+ disabled?: boolean;
71
+ className?: string;
72
+ style?: React.CSSProperties;
73
+ }
74
+ declare function SolanaPayButton({ amountSol, onSuccess, onError, disabled, className, style, }: SolanaPayButtonProps): react_jsx_runtime.JSX.Element;
75
+
76
+ interface MintSuccessProps {
77
+ result: MintResult;
78
+ className?: string;
79
+ style?: React.CSSProperties;
80
+ }
81
+ declare function MintSuccess({ result, className, style }: MintSuccessProps): react_jsx_runtime.JSX.Element | null;
82
+
83
+ export { BuyKeyWidget, type BuyKeyWidgetProps, BuyPanel, type BuyPanelProps, KeyBalanceBar, type KeyBalanceBarProps, KeyCard, type KeyCardProps, KeyList, type KeyListProps, KeyUtilsProvider, type KeyUtilsProviderProps, MintSuccess, type MintSuccessProps, SellPanel, type SellPanelProps, SolanaPayButton, type SolanaPayButtonProps, UsePanel, type UsePanelProps, useKeyUtilsContext };
@@ -0,0 +1,83 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React from 'react';
3
+ import { KeyUtilsConfig, KeyUtilsCallbacks, KeyWidgetTab, PlatformDownload, NodeKeyInfo, MintResult } from '../types/index.js';
4
+
5
+ interface KeyUtilsContextValue {
6
+ config: KeyUtilsConfig;
7
+ callbacks: KeyUtilsCallbacks;
8
+ }
9
+ declare function useKeyUtilsContext(): KeyUtilsContextValue;
10
+ interface KeyUtilsProviderProps {
11
+ config: KeyUtilsConfig;
12
+ callbacks?: KeyUtilsCallbacks;
13
+ children: React.ReactNode;
14
+ }
15
+ declare function KeyUtilsProvider({ config, callbacks, children, }: KeyUtilsProviderProps): react_jsx_runtime.JSX.Element;
16
+
17
+ interface BuyKeyWidgetProps extends KeyUtilsConfig, KeyUtilsCallbacks {
18
+ defaultTab?: KeyWidgetTab;
19
+ className?: string;
20
+ style?: React.CSSProperties;
21
+ }
22
+ declare function BuyKeyWidget({ apiBaseUrl, stackId, stackName, paymentMethods, merchantWallet, stripePublicKey, theme, maxQuantity, keyImage, keyImageClass, aispImage, termsUrl, onMintSuccess, onMintError, onPaymentStart, onPaymentComplete, onListingCreated, defaultTab, className, style, }: BuyKeyWidgetProps): react_jsx_runtime.JSX.Element;
23
+
24
+ interface BuyPanelProps {
25
+ className?: string;
26
+ style?: React.CSSProperties;
27
+ }
28
+ declare function BuyPanel({ className, style }: BuyPanelProps): react_jsx_runtime.JSX.Element;
29
+
30
+ interface UsePanelProps {
31
+ downloads?: PlatformDownload[];
32
+ className?: string;
33
+ style?: React.CSSProperties;
34
+ }
35
+ declare function UsePanel({ downloads, className, style }: UsePanelProps): react_jsx_runtime.JSX.Element;
36
+
37
+ interface SellPanelProps {
38
+ className?: string;
39
+ style?: React.CSSProperties;
40
+ }
41
+ declare function SellPanel({ className, style }: SellPanelProps): react_jsx_runtime.JSX.Element;
42
+
43
+ interface KeyListProps {
44
+ getKeyHref?: string;
45
+ className?: string;
46
+ style?: React.CSSProperties;
47
+ }
48
+ declare function KeyList({ getKeyHref, className, style }: KeyListProps): react_jsx_runtime.JSX.Element;
49
+
50
+ interface KeyCardProps {
51
+ nodeKey: NodeKeyInfo;
52
+ className?: string;
53
+ style?: React.CSSProperties;
54
+ }
55
+ declare function KeyCard({ nodeKey, className, style }: KeyCardProps): react_jsx_runtime.JSX.Element;
56
+
57
+ interface KeyBalanceBarProps {
58
+ current: number;
59
+ max: number;
60
+ className?: string;
61
+ style?: React.CSSProperties;
62
+ showLabel?: boolean;
63
+ }
64
+ declare function KeyBalanceBar({ current, max, className, style, showLabel, }: KeyBalanceBarProps): react_jsx_runtime.JSX.Element;
65
+
66
+ interface SolanaPayButtonProps {
67
+ amountSol: number;
68
+ onSuccess: (transactionId: string) => void;
69
+ onError?: (error: Error) => void;
70
+ disabled?: boolean;
71
+ className?: string;
72
+ style?: React.CSSProperties;
73
+ }
74
+ declare function SolanaPayButton({ amountSol, onSuccess, onError, disabled, className, style, }: SolanaPayButtonProps): react_jsx_runtime.JSX.Element;
75
+
76
+ interface MintSuccessProps {
77
+ result: MintResult;
78
+ className?: string;
79
+ style?: React.CSSProperties;
80
+ }
81
+ declare function MintSuccess({ result, className, style }: MintSuccessProps): react_jsx_runtime.JSX.Element | null;
82
+
83
+ export { BuyKeyWidget, type BuyKeyWidgetProps, BuyPanel, type BuyPanelProps, KeyBalanceBar, type KeyBalanceBarProps, KeyCard, type KeyCardProps, KeyList, type KeyListProps, KeyUtilsProvider, type KeyUtilsProviderProps, MintSuccess, type MintSuccessProps, SellPanel, type SellPanelProps, SolanaPayButton, type SolanaPayButtonProps, UsePanel, type UsePanelProps, useKeyUtilsContext };
@@ -0,0 +1,2 @@
1
+ import at,{createContext,useContext,useMemo,useState,useCallback,useRef,useEffect}from'react';import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {clsx}from'clsx';import {twMerge}from'tailwind-merge';import {Loader2,CheckCircle,Check,Copy,CreditCard,Wallet,Download,AlertTriangle,ExternalLink,ChevronDown,ChevronRight,KeyRound,Minus,Plus,ChevronUp}from'lucide-react';var he=createContext(null);function N(){let e=useContext(he);if(!e)throw new Error("useKeyUtilsContext must be used within a KeyUtilsProvider");return e}function ee({config:e,callbacks:t={},children:r}){typeof e.apiBaseUrl!="string"&&console.error("[KeyUtils] apiBaseUrl must be a string");let s=useMemo(()=>({config:e,callbacks:t}),[e,t]);return jsx(he.Provider,{value:s,children:r})}function m(...e){return twMerge(clsx(e))}function L(e){return e==null?"0":e>=1e9?`${(e/1e9).toFixed(1)}B`:e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:e.toLocaleString()}function Ne(e,t){return t<=0?0:Math.min(100,Math.round(e/t*100))}function ve(e){return e>=70?"bg-green-500":e>=30?"bg-yellow-500":"bg-red-500"}function F(e){let t=typeof e=="string"&&/^\d+$/.test(e)?Number(e):e,r=new Date(t);return isNaN(r.getTime())?String(e):r.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}function ke(){if(typeof navigator>"u")return "mac";let e=navigator.userAgent.toLowerCase();return /iphone|ipad|ipod/.test(e)?"ios":/android/.test(e)?"android":/win/.test(e)?"windows":/linux/.test(e)?"linux":"mac"}function Pe(){let{config:e}=N(),[t,r]=useState(null),[s,g]=useState(true),[c,f]=useState(null),a=useRef(true),n=useRef(e.apiBaseUrl);n.current=e.apiBaseUrl;let d=async()=>{try{a.current&&(g(!0),f(null));let p=await fetch(`${n.current}/api/keys/pricing`);if(!p.ok){let i=`Pricing unavailable (${p.status})`;try{let o=await p.json();o.error&&(i=o.error);}catch{}throw new Error(i)}let u=await p.json();a.current&&r(u);}catch(p){a.current&&f(p instanceof Error?p.message:"Failed to fetch pricing");}finally{a.current&&g(false);}};return useEffect(()=>{a.current=true,d();let p=setInterval(d,6e4);return ()=>{a.current=false,clearInterval(p);}},[]),{pricing:t,loading:s,error:c,refresh:d}}function Ce(){let{config:e,callbacks:t}=N(),[r,s]=useState(false),[g,c]=useState(null),[f,a]=useState(null);return {mint:useCallback(async(d,p,u=1)=>{try{s(!0),a(null),t.onPaymentComplete?.(d,p);let i=await fetch(`${e.apiBaseUrl}/api/keys/mint`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paymentMethod:d,paymentRef:p,quantity:u})});if(!i.ok){let P="Key generation failed";try{let w=await i.json();w.error&&(P=w.error);}catch{}throw new Error(P)}let o=await i.json(),l=o.keys?.[0]||o,v={success:!0,key:{id:l.keyId||l.id||"",key:l.key||"",userId:l.userId||"",status:"active",tokenBalance:l.tokenBalance??l.currentTokenBalance??0,maxTokens:l.initialTokenBalance??l.tokenBalance??0,createdAt:String(l.createdAt||Date.now())},transactionId:p};return c(v),t.onMintSuccess?.(v),v}catch(i){let o=i instanceof Error?i:new Error("Key generation failed");return a(o.message),t.onMintError?.(o),{success:false,error:o.message}}finally{s(false);}},[e.apiBaseUrl,t]),minting:r,result:g,error:f}}function Ke(){let{config:e}=N(),[t,r]=useState(false),[s,g]=useState(null),c=useRef(true);return {createSession:useCallback(async(a,n)=>{try{r(!0),g(null);let d=await fetch(`${e.apiBaseUrl}/api/keys/stripe-session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({priceCents:a,quantity:n,stackId:e.stackId})});if(!d.ok){let u=`Checkout failed (${d.status})`;try{let i=await d.json();i.error&&(u=i.error);}catch{}throw new Error(u)}let{url:p}=await d.json();if(p){try{sessionStorage.setItem("keyutils_stripe_pending","1");}catch{}window.location.href=p;}else throw new Error("No checkout URL returned")}catch(d){if(c.current){let p=d instanceof Error?d.message:"Checkout failed";g(p),r(false);}}},[e.apiBaseUrl,e.stackId]),loading:t,error:s}}var Ze=/^[1-9A-HJ-NP-Za-km-z]{32,44}$/;function oe({amountSol:e,onSuccess:t,onError:r,disabled:s,className:g,style:c}){let{config:f}=N(),[a,n]=useState(false),[d,p]=useState(null),u=!!(f.merchantWallet&&Ze.test(f.merchantWallet)),i=useCallback(async()=>{if(u)try{n(!0),p(null);let{Connection:o,PublicKey:l,Transaction:v,SystemProgram:P,LAMPORTS_PER_SOL:w}=await import('@solana/web3.js'),R=typeof window<"u"?window.solana:void 0;if(!R||typeof R!="object"||!R.isPhantom)throw new Error("Phantom wallet not found. Please install Phantom.");let B=R,M;try{M=new l(f.merchantWallet);}catch{throw new Error("Invalid merchant wallet address.")}let _=await B.connect(),k=new l(_.publicKey.toString()),W=new o(f.solanaRpcUrl||"https://api.mainnet-beta.solana.com"),{blockhash:A}=await W.getLatestBlockhash(),Z=BigInt(Math.floor(e*1e9)),X=new v({recentBlockhash:A,feePayer:k}).add(P.transfer({fromPubkey:k,toPubkey:M,lamports:Number(Z)})),{signature:K}=await B.signAndSendTransaction(X);t(K);}catch(o){let l=o instanceof Error?o:new Error("Payment failed");p(l.message),r?.(l);}finally{n(false);}},[e,f.merchantWallet,u,t,r]);return jsxs("div",{children:[jsx("button",{onClick:i,disabled:s||a||!u,"aria-label":`Pay ${e.toFixed(4)} SOL`,className:m("flex w-full items-center justify-center gap-2 rounded-lg bg-purple-600 px-4 py-3 font-medium text-white transition-colors hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed",g),style:c,children:a?jsxs(Fragment,{children:[jsx(Loader2,{className:"h-4 w-4 animate-spin"}),"Processing..."]}):jsxs(Fragment,{children:["Pay ",e.toFixed(4)," SOL"]})}),d&&jsx("p",{className:"mt-2 text-center text-xs text-red-500",children:d})]})}function ae({result:e,className:t,style:r}){let[s,g]=useState(false),c=useRef(null);useEffect(()=>()=>{c.current&&clearTimeout(c.current);},[]);let f=useCallback(()=>{e.key?.key&&(navigator.clipboard.writeText(e.key.key),g(true),c.current&&clearTimeout(c.current),c.current=setTimeout(()=>g(false),2e3));},[e.key?.key]);return !e.success||!e.key?null:jsxs("div",{className:m("rounded-lg border border-green-500/20 bg-green-500/10 p-6 text-center",t),style:r,children:[jsx(CheckCircle,{className:"mx-auto mb-3 h-12 w-12 text-green-500"}),jsx("h3",{className:"mb-2 text-lg font-semibold text-foreground",children:"Key Generated!"}),jsx("p",{className:"mb-4 text-sm text-muted-foreground",children:"Your node key has been successfully generated."}),jsxs("div",{className:"mx-auto flex max-w-md items-center gap-2 rounded-md border bg-background p-3",children:[jsx("code",{className:"flex-1 truncate text-xs text-foreground",children:e.key.key}),jsx("button",{onClick:f,"aria-label":"Copy key to clipboard",className:"shrink-0 rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",children:s?jsx(Check,{className:"h-4 w-4 text-green-500"}):jsx(Copy,{className:"h-4 w-4"})})]})]})}function I({className:e}){return jsx("div",{className:m("animate-pulse rounded-xl bg-muted",e)})}function ie(){return jsxs("div",{className:"space-y-6",children:[jsxs("div",{className:"px-6 py-10 flex flex-col items-center gap-3",children:[jsx(I,{className:"h-14 w-48"}),jsx(I,{className:"h-5 w-64"})]}),jsx(I,{className:"h-[72px] w-full rounded-2xl"}),jsx(I,{className:"h-[68px] w-full rounded-2xl"}),jsx(I,{className:"h-[60px] w-full rounded-2xl"})]})}function Y({count:e=3}){return jsx("div",{className:"space-y-3",children:Array.from({length:e}).map((t,r)=>jsxs("div",{className:"rounded-xl border p-4 space-y-3",children:[jsxs("div",{className:"flex items-center justify-between",children:[jsx(I,{className:"h-4 w-32"}),jsx(I,{className:"h-4 w-16"})]}),jsx(I,{className:"h-2 w-full rounded-full"}),jsxs("div",{className:"flex justify-between",children:[jsx(I,{className:"h-3 w-24"}),jsx(I,{className:"h-3 w-20"})]})]},r))})}function pt({quantity:e,max:t,onChange:r}){return jsxs("div",{className:"flex items-center justify-between rounded-2xl border bg-muted/20 px-6 py-5",children:[jsx("span",{className:"text-sm font-medium text-foreground",children:"Quantity"}),jsxs("div",{className:"flex items-center gap-0 rounded-xl border bg-background",children:[jsx("button",{onClick:()=>r(Math.max(1,e-1)),disabled:e<=1,"aria-label":"Decrease quantity",className:"flex h-12 w-12 items-center justify-center rounded-l-xl text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-30",children:jsx(Minus,{className:"h-5 w-5"})}),jsx("span",{className:"flex h-12 w-14 items-center justify-center border-x text-xl font-bold text-foreground","aria-live":"polite",children:e}),jsx("button",{onClick:()=>r(Math.min(t,e+1)),disabled:e>=t,"aria-label":"Increase quantity",className:"flex h-12 w-12 items-center justify-center rounded-r-xl text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-30",children:jsx(Plus,{className:"h-5 w-5"})})]})]})}function Te({pricing:e,quantity:t}){let[r,s]=useState(false),g=e.priceUsd*t;return jsxs("div",{className:"rounded-2xl border bg-background",children:[jsxs("button",{onClick:()=>s(!r),"aria-expanded":r,className:"flex w-full items-center justify-between px-6 py-5 text-left",children:[jsxs("span",{className:"text-foreground",children:[jsxs("span",{className:"text-xl font-bold",children:["$",g.toFixed(2)]})," ",jsx("span",{className:"text-sm text-muted-foreground",children:"total fees included"})]}),r?jsx(ChevronUp,{className:"h-5 w-5 text-muted-foreground"}):jsx(ChevronDown,{className:"h-5 w-5 text-muted-foreground"})]}),r&&jsxs("div",{className:"border-t px-6 pb-8 pt-4 space-y-3 text-sm",children:[jsxs("div",{className:"flex justify-between",children:[jsxs("span",{className:"text-muted-foreground",children:["Node Key \xD7 ",t]}),jsxs("span",{className:"text-foreground",children:["$",(e.priceUsd*t).toFixed(2)]})]}),jsxs("div",{className:"flex justify-between",children:[jsx("span",{className:"text-muted-foreground",children:"Keys sold"}),jsx("span",{className:"text-foreground",children:e.keysSold})]}),e.daysUntilHalving>0&&jsxs("div",{className:"flex justify-between text-muted-foreground",children:[jsx("span",{children:"Next refresh"}),jsxs("span",{children:[e.daysUntilHalving," day",e.daysUntilHalving!==1?"s":""]})]}),jsxs("div",{className:"flex justify-between border-t pt-3 font-semibold",children:[jsx("span",{className:"text-foreground",children:"Total"}),jsxs("span",{className:"text-foreground",children:["$",g.toFixed(2)]})]})]})]})}function le({className:e,style:t}){let{config:r}=N(),{pricing:s,loading:g,error:c,refresh:f}=Pe(),{mint:a,minting:n,result:d,error:p}=Ce(),{createSession:u,loading:i,error:o}=Ke(),[l,v]=useState(1),[P,w]=useState(r.paymentMethods?.includes("stripe")?"stripe":"solana"),[R,B]=useState(0),[M,_]=useState(false),k=at.useRef(l);k.current=l;let W=Math.max(1,r.maxQuantity||5),A=[...r.paymentMethods||["solana"]].sort((K,G)=>K==="stripe"?-1:G==="stripe"?1:0);if(useEffect(()=>{if(typeof window>"u")return;let G=new URLSearchParams(window.location.search).get("session_id");if(G&&/^cs_(test|live)_/.test(G)&&!M&&!d?.success){let ye=false;try{ye=sessionStorage.getItem("keyutils_stripe_pending")==="1";}catch{}if(!ye)return;try{sessionStorage.removeItem("keyutils_stripe_pending");}catch{}_(true);let be=new URL(window.location.href);be.searchParams.delete("session_id"),window.history.replaceState({},"",be.toString()),a("stripe",G,k.current);}},[M,d,a]),d?.success)return jsx("div",{className:m("flex flex-col items-center justify-center",e),style:t,children:jsx(ae,{result:d})});if(M||n)return jsx("div",{className:m("flex flex-col",e),style:t,children:jsx(ie,{})});if(g)return jsx("div",{className:m("flex flex-col",e),style:t,children:jsx(ie,{})});if(c||!s)return jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 py-24",e),style:t,children:[jsxs("div",{className:"rounded-xl border border-red-500/20 bg-red-500/5 px-6 py-5 text-center max-w-sm",children:[jsx("p",{className:"text-sm font-medium text-foreground",children:"Unable to load pricing"}),jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:c||"Please check your connection and try again."})]}),jsx("button",{onClick:()=>f(),className:"text-sm text-muted-foreground hover:text-foreground transition-colors",children:"Try again"})]});let Z=async K=>{B(2),await a(P,K,l);},X=()=>{u(s.priceCents,l);};return jsx("div",{className:m("flex flex-col",e),style:t,children:jsxs("div",{className:"space-y-6",children:[R===0&&jsxs(Fragment,{children:[r.keyImage&&jsx("img",{src:r.keyImage,alt:"Node Key",className:r.keyImageClass||"w-1/2 mx-auto"}),jsxs("div",{className:"px-6 py-10 text-center",children:[jsxs("p",{className:"text-5xl font-bold tracking-tight text-foreground md:text-6xl",children:["$",s.priceUsd.toFixed(2)]}),jsxs("p",{className:"mt-2 text-base text-muted-foreground",children:["per key \xB7 ",s.tokenAllocationFormatted||L(s.tokenAllocation)," tokens included"]})]}),jsx(pt,{quantity:l,max:W,onChange:v}),jsx(Te,{pricing:s,quantity:l}),jsx("button",{onClick:()=>B(1),className:"w-full rounded-2xl bg-foreground py-5 text-center text-base font-semibold text-background transition-colors hover:bg-foreground/90",children:"Continue to Payment"})]}),R===1&&jsxs(Fragment,{children:[A.length>1&&jsx("div",{role:"tablist",className:"flex gap-1 rounded-2xl bg-muted/50 p-1.5",children:A.map(K=>jsxs("button",{role:"tab","aria-selected":P===K,onClick:()=>w(K),className:m("flex flex-1 items-center justify-center gap-2.5 rounded-xl py-4 text-sm font-medium transition-colors",P===K?"bg-background text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:[K==="stripe"?jsx(CreditCard,{className:"h-5 w-5"}):jsx(Wallet,{className:"h-5 w-5"}),K==="stripe"?"Credit Card":"Crypto"]},K))}),jsx(Te,{pricing:s,quantity:l}),P==="stripe"?jsxs(Fragment,{children:[jsx("button",{onClick:X,disabled:i,className:"flex w-full items-center justify-center gap-2 rounded-2xl bg-foreground py-5 text-base font-semibold text-background transition-colors hover:bg-foreground/90 disabled:opacity-50",children:i?jsxs(Fragment,{children:[jsx(Loader2,{className:"h-5 w-5 animate-spin"}),"Redirecting to Stripe..."]}):jsxs(Fragment,{children:[jsx(CreditCard,{className:"h-5 w-5"}),"Pay with Card"]})}),o&&jsx("p",{className:"text-center text-sm text-red-500",children:o})]}):P==="solana"?jsx(oe,{amountSol:s.priceSol??0,onSuccess:Z,className:"rounded-2xl py-5 text-base"}):null,p&&jsx("p",{className:"text-center text-sm text-red-500",children:p}),jsx("button",{onClick:()=>B(0),className:"w-full py-3 text-center text-sm text-muted-foreground hover:text-foreground transition-colors",children:"\u2190 Back to Configure"})]})]})})}var bt={ios:"iOS",android:"Android",windows:"Windows",mac:"macOS",linux:"Linux"},ht=["mac","windows","linux","ios","android"];function ce({downloads:e,className:t,style:r}){let{config:s}=N(),g=useMemo(()=>ke(),[]),c=e||ht.map(n=>({platform:n,label:bt[n],url:"#",compatible:n===g})),f=c.find(n=>n.compatible)||c[0],a=c.filter(n=>n.platform!==f.platform);return jsxs("div",{className:m("space-y-8",t),style:r,children:[jsxs("div",{className:"flex flex-col items-center gap-6 pt-4 mt-4",children:[s.aispImage&&jsx("img",{src:s.aispImage,alt:"aiSP",className:"w-full sm:w-1/2 mx-auto px-4 sm:px-0"}),jsxs("div",{className:"text-center",children:[jsx("p",{className:"text-lg sm:text-2xl font-medium text-foreground",children:"Download the AI Service Provider App"}),jsx("p",{className:"mt-1 text-sm sm:text-base text-muted-foreground",children:"Load your keys and run your node."})]}),jsxs("a",{href:f.url,className:"inline-flex items-center gap-3 rounded-full px-10 py-4 text-lg font-semibold text-white transition-colors hover:opacity-90",style:{backgroundColor:"#0900f4"},children:[jsx(Download,{className:"h-5 w-5"}),"Get aiSP for ",f.label]})]}),jsxs("p",{className:"text-center text-sm text-muted-foreground",children:["aiSP is also available on"," ",a.map((n,d)=>jsxs(at.Fragment,{children:[d>0&&(d===a.length-1?" and ":", "),jsx("a",{href:n.url,className:"font-medium underline transition-colors hover:text-foreground",style:{color:"#0900f4"},children:n.label})]},n.platform)),"."]}),jsxs("div",{className:"flex flex-col items-center gap-2 mt-12",children:[jsxs("p",{className:"text-center text-xs text-muted-foreground flex justify-center gap-0.5 flex-wrap",children:["Source code is available on",jsx("a",{href:"https://github.com/stack-net",target:"_blank",rel:"noopener noreferrer",className:"text-foreground underline hover:text-foreground/80 color-[#0900f4]",children:"Github"})]}),jsxs("p",{className:"text-center text-xs text-muted-foreground flex justify-center gap-0.5 flex-wrap",children:["By downloading, you agree to the",jsx("a",{href:s.termsUrl||"/terms",className:"text-foreground underline hover:text-foreground/80",children:"Open Source Applications Terms"})]})]})]})}function wt(e){return {id:e.id||"",key:e.key||e.keyPrefix||"",userId:e.userId||e.user_id||"",status:e.status||"active",tokenBalance:e.tokenBalance??e.currentTokenBalance??e.current_token_balance??0,maxTokens:e.maxTokens??e.initialTokenBalance??e.initial_token_balance??0,createdAt:String(e.createdAt??e.created_at??""),expiresAt:e.expiresAt,label:e.label||e.keyPrefix||e.key_prefix||void 0,paperWork:e.paperWork||e.paper_work||void 0,stackId:e.stackId,stackName:e.stackName}}function J(){let{config:e}=N(),[t,r]=useState([]),[s,g]=useState(true),[c,f]=useState(null),a=useRef(true),n=e.apiBaseUrl,d=useCallback(async()=>{try{g(!0),f(null);let u=await fetch(`${n}/api/keys`);if(!u.ok){let l=`Failed to fetch keys (${u.status})`;try{let v=await u.json();v.error&&(l=v.error);}catch{}throw new Error(l)}let i=await u.json(),o=Array.isArray(i)?i:i.keys??[];a.current&&r(o.map(wt));}catch(u){a.current&&f(u instanceof Error?u.message:"Failed to fetch keys");}finally{a.current&&g(false);}},[n]);useEffect(()=>(a.current=true,d(),()=>{a.current=false;}),[d]);let p=t.reduce((u,i)=>u+i.tokenBalance,0);return {keys:t,totalBalance:p,loading:s,error:c,refresh:d}}function Ie(){let{config:e,callbacks:t}=N(),[r,s]=useState(false),[g,c]=useState(null),[f,a]=useState(null),n=useRef(true),d=useCallback(async(u,i)=>{try{s(!0),a(null);let o=await fetch(`${e.apiBaseUrl}/api/keys/${u}/list`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({askPriceCents:i,stackId:e.stackId,stackName:e.stackName})});if(!o.ok){let v=`Listing failed (${o.status})`;try{let P=await o.json();P.error&&(v=P.error);}catch{}throw new Error(v)}let l=await o.json();return n.current&&(c(l),t.onListingCreated?.(l)),l}catch(o){let l=o instanceof Error?o.message:"Listing failed";return n.current&&a(l),null}finally{n.current&&s(false);}},[e.apiBaseUrl,e.stackId,e.stackName,t]),p=useCallback(async u=>{try{s(!0),a(null);let i=await fetch(`${e.apiBaseUrl}/api/keys/${u}/list`,{method:"DELETE"});if(!i.ok){let o=`Delist failed (${i.status})`;try{let l=await i.json();l.error&&(o=l.error);}catch{}throw new Error(o)}return n.current&&c(null),!0}catch(i){let o=i instanceof Error?i.message:"Delist failed";return n.current&&a(o),false}finally{n.current&&s(false);}},[e.apiBaseUrl]);return {listKey:d,delistKey:p,listing:r,result:g,error:f}}function z({current:e,max:t,className:r,style:s,showLabel:g=true}){let c=Ne(e,t),f=ve(c);return jsxs("div",{className:m("w-full",r),style:s,children:[g&&jsxs("div",{className:"mb-1 flex items-center justify-between text-xs text-muted-foreground",children:[jsxs("span",{children:[L(e)," / ",L(t)]}),jsxs("span",{children:[c,"%"]})]}),jsx("div",{className:"h-2 w-full overflow-hidden rounded-full bg-muted",children:jsx("div",{className:m("h-full rounded-full transition-all duration-500",f),style:{width:`${c}%`}})})]})}function Ut({nodeKey:e,selected:t,onSelect:r}){return jsxs("button",{onClick:r,className:m("flex w-full items-start gap-3 rounded-xl border p-4 text-left transition-all",t?"border-foreground/30 bg-foreground/5 ring-1 ring-foreground/10":"border-border hover:border-foreground/20"),children:[jsx("div",{className:m("mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full border-2 transition-colors",t?"border-foreground bg-foreground":"border-muted-foreground/40"),children:t&&jsx(Check,{className:"h-3 w-3 text-background"})}),jsxs("div",{className:"min-w-0 flex-1",children:[jsxs("div",{className:"flex items-center justify-between",children:[jsx("span",{className:"text-sm font-medium text-foreground",children:e.label||e.id.slice(0,16)}),jsx("span",{className:m("text-xs font-medium capitalize",e.status==="active"?"text-green-500":"text-muted-foreground"),children:e.status})]}),jsx(z,{current:e.tokenBalance,max:e.maxTokens,className:"mt-2"})]})]})}function fe({className:e,style:t}){let{config:r}=N(),{keys:s,loading:g,error:c,refresh:f}=J(),{listKey:a,listing:n,result:d,error:p}=Ie(),[u,i]=useState(null),[o,l]=useState(""),[v,P]=useState(false),w=s.find(k=>k.id===u),R=parseInt(o.replace(/[^0-9]/g,""),10)||0,B=w&&R>0&&v&&!n,M=async()=>{if(!B||!w)return;let k=R*100;await a(w.id,k)&&f();};if(d)return jsxs("div",{className:m("flex flex-col items-center gap-4 rounded-xl border border-green-500/20 bg-green-500/10 py-12",e),style:t,children:[jsx(CheckCircle,{className:"h-12 w-12 text-green-500"}),jsx("h3",{className:"text-lg font-semibold text-foreground",children:"Key Listed!"}),jsx("p",{className:"text-sm text-muted-foreground",children:"Your key is now on the Global Open AI Network marketplace."}),jsxs("p",{className:"text-xs text-muted-foreground",children:["Listing ID: ",jsx("code",{children:d.listingId})]})]});if(g)return jsxs("div",{className:m("space-y-5 px-1",e),style:t,children:[jsx("h3",{className:"text-2xl mt-3 mb-2 font-semibold text-foreground",children:"Your Keys"}),jsx(Y,{count:2})]});if(c)return jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 py-16",e),style:t,children:[jsxs("div",{className:"rounded-xl border border-red-500/20 bg-red-500/5 px-6 py-5 text-center max-w-sm",children:[jsx("p",{className:"text-sm font-medium text-foreground",children:"Unable to load your keys"}),jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:c})]}),jsx("button",{onClick:()=>f(),className:"text-sm text-muted-foreground hover:text-foreground transition-colors",children:"Try again"})]});let _=s.filter(k=>k.status==="active");return _.length===0?jsxs("div",{className:m("flex flex-col items-center justify-center gap-3 rounded-xl border border-dashed py-16",e),style:t,children:[jsx(AlertTriangle,{className:"h-8 w-8 text-muted-foreground"}),jsx("p",{className:"text-sm text-muted-foreground",children:"No active keys to sell."})]}):jsxs("div",{className:m("space-y-5",e),style:t,children:[jsxs("div",{children:[jsx("h3",{className:"text-2xl mt-3 mb-2 font-semibold text-foreground",children:"Your Keys"}),jsx("div",{className:"space-y-2",children:_.map(k=>jsx(Ut,{nodeKey:k,selected:u===k.id,onSelect:()=>i(k.id)},k.id))})]}),w&&jsxs("div",{className:"rounded-xl border bg-muted/30 p-5 space-y-2 text-sm",children:[jsxs("div",{className:"flex justify-between",children:[jsx("span",{className:"text-muted-foreground",children:"Key ID"}),jsx("code",{className:"text-xs text-foreground",children:w.id})]}),jsxs("div",{className:"flex justify-between",children:[jsx("span",{className:"text-muted-foreground",children:"Originating Stack"}),jsx("span",{className:"text-foreground",children:r.stackName||"Unknown"})]}),jsxs("div",{className:"flex justify-between",children:[jsx("span",{className:"text-muted-foreground",children:"Tokens (remaining / total)"}),jsxs("span",{className:"text-foreground",children:[L(w.tokenBalance)," / ",L(w.maxTokens)]})]}),w.paperWork&&jsxs("div",{className:"flex justify-between",children:[jsx("span",{className:"text-muted-foreground",children:"Paper earned"}),jsx("span",{className:"text-foreground",children:w.paperWork})]}),jsxs("div",{className:"flex justify-between",children:[jsx("span",{className:"text-muted-foreground",children:"Created"}),jsx("span",{className:"text-foreground",children:F(w.createdAt)})]})]}),jsxs("div",{children:[jsx("label",{htmlFor:"listing-price",className:"mb-2 block text-sm font-semibold text-foreground",children:"Listing Price (USD)"}),jsxs("div",{className:"relative",children:[jsx("span",{className:"absolute left-4 top-1/2 -translate-y-1/2 text-2xl font-bold text-muted-foreground",children:"$"}),jsx("input",{id:"listing-price",type:"text",inputMode:"numeric",value:o,onChange:k=>{let W=k.target.value.replace(/[^0-9]/g,""),A=parseInt(W,10);l(isNaN(A)?"":A.toLocaleString("en-US"));},placeholder:"0",className:"w-full rounded-xl border bg-background py-5 pl-10 pr-4 text-3xl font-bold text-foreground placeholder:text-muted-foreground/30 focus:outline-none focus:ring-2 focus:ring-foreground/20"})]})]}),jsxs("label",{className:"flex items-start gap-3 cursor-pointer",children:[jsx("input",{type:"checkbox",checked:v,onChange:k=>P(k.target.checked),className:"sr-only"}),jsx("div",{"aria-hidden":"true",className:m("mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded border-2 transition-colors",v?"border-foreground bg-foreground":"border-muted-foreground/40"),children:v&&jsx(Check,{className:"h-3 w-3 text-background"})}),jsxs("span",{className:"text-xs text-muted-foreground leading-relaxed",children:["I agree to list this key on the Global Open AI Network marketplace. The key will be deactivated upon sale and transferred to the buyer. Listing fees may apply."," ",jsx("a",{href:r.termsUrl||"/terms",className:"text-foreground underline",children:"Terms"})]})]}),p&&jsx("p",{className:"text-sm text-red-500",children:p}),jsxs("button",{onClick:M,disabled:!B,className:m("flex w-full items-center justify-center gap-2 rounded-xl py-4 text-sm font-semibold transition-colors",B?"bg-foreground text-background hover:bg-foreground/90":"bg-muted text-muted-foreground cursor-not-allowed"),children:[jsx(ExternalLink,{className:"h-4 w-4"}),n?"Listing...":"List on Marketplace"]})]})}var Bt=[{id:"buy",label:"Buy"},{id:"use",label:"Use"},{id:"sell",label:"Sell"}];function Lt({active:e,onChange:t}){return jsx("div",{role:"tablist","aria-label":"Key actions",className:"flex gap-1 rounded-2xl bg-muted/50 p-1.5",children:Bt.map(({id:r,label:s})=>jsx("button",{role:"tab","aria-selected":e===r,"aria-controls":`panel-${r}`,id:`tab-${r}`,onClick:()=>t(r),className:m("flex flex-1 items-center justify-center gap-2.5 rounded-xl py-3.5 text-sm font-medium transition-all",e===r?"bg-background text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:jsx("span",{className:"inline",children:s})},r))})}function Tt({defaultTab:e="buy",className:t,style:r}){let[s,g]=useState(e);return jsxs("div",{className:m("flex flex-col",t),style:r,children:[jsx("div",{className:"sticky top-0 z-10 bg-background/80 px-5 pb-3 pt-2 sm:pt-5 backdrop-blur-sm sm:px-10 md:px-16 lg:px-20",children:jsx("div",{className:"mx-auto max-w-xl",children:jsx(Lt,{active:s,onChange:g})})}),jsx("div",{className:"flex flex-1 flex-col px-5 pb-8 sm:px-10 md:px-16 md:pb-12 lg:px-20",children:jsx("div",{className:"mx-auto w-full max-w-xl flex-1",children:jsxs("div",{role:"tabpanel",id:`panel-${s}`,"aria-labelledby":`tab-${s}`,children:[s==="buy"&&jsx(le,{className:"flex-1"}),s==="use"&&jsx(ce,{className:"flex-1"}),s==="sell"&&jsx(fe,{className:"flex-1"})]})})})]})}function Et({apiBaseUrl:e,stackId:t,stackName:r,paymentMethods:s,merchantWallet:g,stripePublicKey:c,theme:f,maxQuantity:a,keyImage:n,keyImageClass:d,aispImage:p,termsUrl:u,onMintSuccess:i,onMintError:o,onPaymentStart:l,onPaymentComplete:v,onListingCreated:P,defaultTab:w,className:R,style:B}){return jsx(ee,{config:{apiBaseUrl:e,stackId:t,stackName:r,paymentMethods:s,merchantWallet:g,stripePublicKey:c,theme:f,maxQuantity:a,keyImage:n,keyImageClass:d,aispImage:p,termsUrl:u},callbacks:{onMintSuccess:i,onMintError:o,onPaymentStart:l,onPaymentComplete:v,onListingCreated:P},children:jsx(Tt,{defaultTab:w,className:R,style:B})})}function Ae(e){let{config:t}=N(),[r,s]=useState([]),[g,c]=useState(false),[f,a]=useState(null),n=useRef(true),d=t.apiBaseUrl,p=useCallback(async()=>{if(e)try{c(!0),a(null);let u=await fetch(`${d}/api/keys/${e}/ledger`);if(!u.ok){let o=`Failed to fetch ledger (${u.status})`;try{let l=await u.json();l.error&&(o=l.error);}catch{}throw new Error(o)}let i=await u.json();n.current&&s(Array.isArray(i)?i:i.entries??[]);}catch(u){n.current&&a(u instanceof Error?u.message:"Failed to fetch ledger");}finally{n.current&&c(false);}},[d,e]);return useEffect(()=>(n.current=true,p(),()=>{n.current=false;}),[p]),{entries:r,loading:g,error:f,refresh:p}}var ge=20;function xe({nodeKey:e,className:t,style:r}){let[s,g]=useState(false),[c,f]=useState(false),a=useRef(null),{entries:n,loading:d}=Ae(s?e.id:null);useEffect(()=>()=>{a.current&&clearTimeout(a.current);},[]);let p=useCallback(()=>{e.key&&(navigator.clipboard.writeText(e.key),f(true),a.current&&clearTimeout(a.current),a.current=setTimeout(()=>f(false),2e3));},[e.key]),u=e.status==="active"?"text-green-500":e.status==="expired"?"text-red-500":"text-yellow-500",i=n.slice(0,ge);return jsxs("div",{className:m("rounded-lg border bg-background",t),style:r,children:[jsxs("div",{className:"p-4",children:[jsxs("div",{className:"mb-3 flex items-center justify-between",children:[jsxs("div",{className:"flex items-center gap-2",children:[jsx("button",{onClick:()=>g(!s),"aria-expanded":s,"aria-label":s?"Collapse ledger":"Expand ledger",className:"text-muted-foreground hover:text-foreground",children:s?jsx(ChevronDown,{className:"h-4 w-4"}):jsx(ChevronRight,{className:"h-4 w-4"})}),jsx("span",{className:"text-sm font-medium text-foreground",children:e.label||`Key ${e.id.slice(0,8)}`})]}),jsx("span",{className:m("text-xs font-medium capitalize",u),children:e.status})]}),e.key&&jsxs("div",{className:"mb-3 flex items-center gap-2",children:[jsx("code",{className:"flex-1 truncate rounded bg-muted px-2 py-1 text-xs text-muted-foreground",children:e.key}),jsx("button",{onClick:p,"aria-label":"Copy key to clipboard",className:"shrink-0 rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",children:c?jsx(Check,{className:"h-3.5 w-3.5 text-green-500"}):jsx(Copy,{className:"h-3.5 w-3.5"})})]}),jsx(z,{current:e.tokenBalance,max:e.maxTokens}),jsxs("div",{className:"mt-2 flex items-center justify-between text-xs text-muted-foreground",children:[jsxs("span",{children:["Created ",F(e.createdAt)]}),e.expiresAt&&jsxs("span",{children:["Expires ",F(e.expiresAt)]})]})]}),s&&jsxs("div",{className:"border-t px-4 py-3",children:[jsx("h4",{className:"mb-2 text-xs font-medium text-muted-foreground",children:"Ledger"}),d?jsx("p",{className:"text-xs text-muted-foreground",children:"Loading..."}):i.length===0?jsx("p",{className:"text-xs text-muted-foreground",children:"No ledger entries."}):jsxs("div",{className:"space-y-1.5",children:[i.map(o=>jsxs("div",{className:"flex items-center justify-between text-xs",children:[jsxs("div",{className:"flex items-center gap-2",children:[jsxs("span",{className:m("font-medium",o.type==="credit"||o.type==="reward"?"text-green-500":"text-red-500"),children:[o.type==="credit"||o.type==="reward"?"+":"-",L(Math.abs(o.amount))]}),jsx("span",{className:"text-muted-foreground",children:o.description})]}),jsx("span",{className:"text-muted-foreground",children:F(o.createdAt)})]},o.id)),n.length>ge&&jsxs("p",{className:"pt-1 text-xs text-muted-foreground",children:["Showing ",ge," of ",n.length," entries"]})]})]})]})}function Qt({getKeyHref:e="/keys/get",className:t,style:r}){let{keys:s,totalBalance:g,loading:c,error:f,refresh:a}=J();return c?jsx("div",{className:m("space-y-4",t),style:r,children:jsx(Y,{count:3})}):f?jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 py-12",t),style:r,children:[jsxs("div",{className:"rounded-xl border border-red-500/20 bg-red-500/5 px-6 py-5 text-center max-w-sm",children:[jsx("p",{className:"text-sm font-medium text-foreground",children:"Unable to load your keys"}),jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:f})]}),jsx("button",{onClick:()=>a(),className:"text-sm text-muted-foreground hover:text-foreground transition-colors",children:"Try again"})]}):s.length===0?jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 rounded-lg border border-dashed py-12",t),style:r,children:[jsx(KeyRound,{className:"h-10 w-10 text-muted-foreground"}),jsxs("div",{className:"text-center",children:[jsx("p",{className:"text-sm font-medium text-foreground",children:"No keys yet"}),jsx("p",{className:"text-xs text-muted-foreground",children:"Get your first node key to start earning."})]}),e&&jsx("a",{href:e,className:"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background transition-colors hover:bg-foreground/90",children:"Get a Key"})]}):jsxs("div",{className:m("space-y-4",t),style:r,children:[jsxs("div",{className:"flex items-center justify-between",children:[jsxs("p",{className:"text-sm text-muted-foreground",children:[s.length," key",s.length!==1?"s":""," \xB7 Total balance: ",L(g)]}),e&&jsx("a",{href:e,className:"rounded-md bg-foreground px-3 py-1.5 text-xs font-medium text-background transition-colors hover:bg-foreground/90",children:"Get a Key"})]}),s.map(n=>jsx(xe,{nodeKey:n},n.id))]})}
2
+ export{Et as BuyKeyWidget,le as BuyPanel,z as KeyBalanceBar,xe as KeyCard,Qt as KeyList,ee as KeyUtilsProvider,ae as MintSuccess,fe as SellPanel,oe as SolanaPayButton,ce as UsePanel,N as useKeyUtilsContext};
@@ -0,0 +1 @@
1
+ 'use strict';var react=require('react');require('react/jsx-runtime');var w=react.createContext(null);function m(){let e=react.useContext(w);if(!e)throw new Error("useKeyUtilsContext must be used within a KeyUtilsProvider");return e}function S(){let{config:e}=m(),[c,g]=react.useState(null),[u,f]=react.useState(true),[y,d]=react.useState(null),i=react.useRef(true),l=react.useRef(e.apiBaseUrl);l.current=e.apiBaseUrl;let a=async()=>{try{i.current&&(f(!0),d(null));let s=await fetch(`${l.current}/api/keys/pricing`);if(!s.ok){let t=`Pricing unavailable (${s.status})`;try{let n=await s.json();n.error&&(t=n.error);}catch{}throw new Error(t)}let r=await s.json();i.current&&g(r);}catch(s){i.current&&d(s instanceof Error?s.message:"Failed to fetch pricing");}finally{i.current&&f(false);}};return react.useEffect(()=>{i.current=true,a();let s=setInterval(a,6e4);return ()=>{i.current=false,clearInterval(s);}},[]),{pricing:c,loading:u,error:y,refresh:a}}function j(e){return {id:e.id||"",key:e.key||e.keyPrefix||"",userId:e.userId||e.user_id||"",status:e.status||"active",tokenBalance:e.tokenBalance??e.currentTokenBalance??e.current_token_balance??0,maxTokens:e.maxTokens??e.initialTokenBalance??e.initial_token_balance??0,createdAt:String(e.createdAt??e.created_at??""),expiresAt:e.expiresAt,label:e.label||e.keyPrefix||e.key_prefix||void 0,paperWork:e.paperWork||e.paper_work||void 0,stackId:e.stackId,stackName:e.stackName}}function N(){let{config:e}=m(),[c,g]=react.useState([]),[u,f]=react.useState(true),[y,d]=react.useState(null),i=react.useRef(true),l=e.apiBaseUrl,a=react.useCallback(async()=>{try{f(!0),d(null);let r=await fetch(`${l}/api/keys`);if(!r.ok){let o=`Failed to fetch keys (${r.status})`;try{let p=await r.json();p.error&&(o=p.error);}catch{}throw new Error(o)}let t=await r.json(),n=Array.isArray(t)?t:t.keys??[];i.current&&g(n.map(j));}catch(r){i.current&&d(r instanceof Error?r.message:"Failed to fetch keys");}finally{i.current&&f(false);}},[l]);react.useEffect(()=>(i.current=true,a(),()=>{i.current=false;}),[a]);let s=c.reduce((r,t)=>r+t.tokenBalance,0);return {keys:c,totalBalance:s,loading:u,error:y,refresh:a}}function D(e){let{config:c}=m(),[g,u]=react.useState([]),[f,y]=react.useState(false),[d,i]=react.useState(null),l=react.useRef(true),a=c.apiBaseUrl,s=react.useCallback(async()=>{if(e)try{y(!0),i(null);let r=await fetch(`${a}/api/keys/${e}/ledger`);if(!r.ok){let n=`Failed to fetch ledger (${r.status})`;try{let o=await r.json();o.error&&(n=o.error);}catch{}throw new Error(n)}let t=await r.json();l.current&&u(Array.isArray(t)?t:t.entries??[]);}catch(r){l.current&&i(r instanceof Error?r.message:"Failed to fetch ledger");}finally{l.current&&y(false);}},[a,e]);return react.useEffect(()=>(l.current=true,s(),()=>{l.current=false;}),[s]),{entries:g,loading:f,error:d,refresh:s}}function F(){let{config:e,callbacks:c}=m(),[g,u]=react.useState(false),[f,y]=react.useState(null),[d,i]=react.useState(null);return {mint:react.useCallback(async(a,s,r=1)=>{try{u(!0),i(null),c.onPaymentComplete?.(a,s);let t=await fetch(`${e.apiBaseUrl}/api/keys/mint`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paymentMethod:a,paymentRef:s,quantity:r})});if(!t.ok){let k="Key generation failed";try{let C=await t.json();C.error&&(k=C.error);}catch{}throw new Error(k)}let n=await t.json(),o=n.keys?.[0]||n,p={success:!0,key:{id:o.keyId||o.id||"",key:o.key||"",userId:o.userId||"",status:"active",tokenBalance:o.tokenBalance??o.currentTokenBalance??0,maxTokens:o.initialTokenBalance??o.tokenBalance??0,createdAt:String(o.createdAt||Date.now())},transactionId:s};return y(p),c.onMintSuccess?.(p),p}catch(t){let n=t instanceof Error?t:new Error("Key generation failed");return i(n.message),c.onMintError?.(n),{success:false,error:n.message}}finally{u(false);}},[e.apiBaseUrl,c]),minting:g,result:f,error:d}}function V(){let{config:e,callbacks:c}=m(),[g,u]=react.useState(false),[f,y]=react.useState(null),[d,i]=react.useState(null),l=react.useRef(true),a=react.useCallback(async(r,t)=>{try{u(!0),i(null);let n=await fetch(`${e.apiBaseUrl}/api/keys/${r}/list`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({askPriceCents:t,stackId:e.stackId,stackName:e.stackName})});if(!n.ok){let p=`Listing failed (${n.status})`;try{let k=await n.json();k.error&&(p=k.error);}catch{}throw new Error(p)}let o=await n.json();return l.current&&(y(o),c.onListingCreated?.(o)),o}catch(n){let o=n instanceof Error?n.message:"Listing failed";return l.current&&i(o),null}finally{l.current&&u(false);}},[e.apiBaseUrl,e.stackId,e.stackName,c]),s=react.useCallback(async r=>{try{u(!0),i(null);let t=await fetch(`${e.apiBaseUrl}/api/keys/${r}/list`,{method:"DELETE"});if(!t.ok){let n=`Delist failed (${t.status})`;try{let o=await t.json();o.error&&(n=o.error);}catch{}throw new Error(n)}return l.current&&y(null),!0}catch(t){let n=t instanceof Error?t.message:"Delist failed";return l.current&&i(n),false}finally{l.current&&u(false);}},[e.apiBaseUrl]);return {listKey:a,delistKey:s,listing:g,result:f,error:d}}function z(){let{config:e}=m(),[c,g]=react.useState(false),[u,f]=react.useState(null),y=react.useRef(true);return {createSession:react.useCallback(async(i,l)=>{try{g(!0),f(null);let a=await fetch(`${e.apiBaseUrl}/api/keys/stripe-session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({priceCents:i,quantity:l,stackId:e.stackId})});if(!a.ok){let r=`Checkout failed (${a.status})`;try{let t=await a.json();t.error&&(r=t.error);}catch{}throw new Error(r)}let{url:s}=await a.json();if(s){try{sessionStorage.setItem("keyutils_stripe_pending","1");}catch{}window.location.href=s;}else throw new Error("No checkout URL returned")}catch(a){if(y.current){let s=a instanceof Error?a.message:"Checkout failed";f(s),g(false);}}},[e.apiBaseUrl,e.stackId]),loading:c,error:u}}exports.useKeyLedger=D;exports.useKeys=N;exports.useListKey=V;exports.useMintKey=F;exports.usePricing=S;exports.useStripeCheckout=z;
@@ -0,0 +1,52 @@
1
+ import { PricingInfo, NodeKeyInfo, LedgerEntry, PaymentMethod, MintResult, ListingResult } from '../types/index.cjs';
2
+
3
+ interface UsePricingReturn {
4
+ pricing: PricingInfo | null;
5
+ loading: boolean;
6
+ error: string | null;
7
+ refresh: () => Promise<void>;
8
+ }
9
+ declare function usePricing(): UsePricingReturn;
10
+
11
+ interface UseKeysReturn {
12
+ keys: NodeKeyInfo[];
13
+ totalBalance: number;
14
+ loading: boolean;
15
+ error: string | null;
16
+ refresh: () => Promise<void>;
17
+ }
18
+ declare function useKeys(): UseKeysReturn;
19
+
20
+ interface UseKeyLedgerReturn {
21
+ entries: LedgerEntry[];
22
+ loading: boolean;
23
+ error: string | null;
24
+ refresh: () => Promise<void>;
25
+ }
26
+ declare function useKeyLedger(keyId: string | null): UseKeyLedgerReturn;
27
+
28
+ interface UseMintKeyReturn {
29
+ mint: (paymentMethod: PaymentMethod, transactionId: string, quantity?: number) => Promise<MintResult>;
30
+ minting: boolean;
31
+ result: MintResult | null;
32
+ error: string | null;
33
+ }
34
+ declare function useMintKey(): UseMintKeyReturn;
35
+
36
+ interface UseListKeyReturn {
37
+ listKey: (keyId: string, askPriceCents: number) => Promise<ListingResult | null>;
38
+ delistKey: (keyId: string) => Promise<boolean>;
39
+ listing: boolean;
40
+ result: ListingResult | null;
41
+ error: string | null;
42
+ }
43
+ declare function useListKey(): UseListKeyReturn;
44
+
45
+ interface UseStripeCheckoutReturn {
46
+ createSession: (priceCents: number, quantity: number) => Promise<void>;
47
+ loading: boolean;
48
+ error: string | null;
49
+ }
50
+ declare function useStripeCheckout(): UseStripeCheckoutReturn;
51
+
52
+ export { useKeyLedger, useKeys, useListKey, useMintKey, usePricing, useStripeCheckout };
@@ -0,0 +1,52 @@
1
+ import { PricingInfo, NodeKeyInfo, LedgerEntry, PaymentMethod, MintResult, ListingResult } from '../types/index.js';
2
+
3
+ interface UsePricingReturn {
4
+ pricing: PricingInfo | null;
5
+ loading: boolean;
6
+ error: string | null;
7
+ refresh: () => Promise<void>;
8
+ }
9
+ declare function usePricing(): UsePricingReturn;
10
+
11
+ interface UseKeysReturn {
12
+ keys: NodeKeyInfo[];
13
+ totalBalance: number;
14
+ loading: boolean;
15
+ error: string | null;
16
+ refresh: () => Promise<void>;
17
+ }
18
+ declare function useKeys(): UseKeysReturn;
19
+
20
+ interface UseKeyLedgerReturn {
21
+ entries: LedgerEntry[];
22
+ loading: boolean;
23
+ error: string | null;
24
+ refresh: () => Promise<void>;
25
+ }
26
+ declare function useKeyLedger(keyId: string | null): UseKeyLedgerReturn;
27
+
28
+ interface UseMintKeyReturn {
29
+ mint: (paymentMethod: PaymentMethod, transactionId: string, quantity?: number) => Promise<MintResult>;
30
+ minting: boolean;
31
+ result: MintResult | null;
32
+ error: string | null;
33
+ }
34
+ declare function useMintKey(): UseMintKeyReturn;
35
+
36
+ interface UseListKeyReturn {
37
+ listKey: (keyId: string, askPriceCents: number) => Promise<ListingResult | null>;
38
+ delistKey: (keyId: string) => Promise<boolean>;
39
+ listing: boolean;
40
+ result: ListingResult | null;
41
+ error: string | null;
42
+ }
43
+ declare function useListKey(): UseListKeyReturn;
44
+
45
+ interface UseStripeCheckoutReturn {
46
+ createSession: (priceCents: number, quantity: number) => Promise<void>;
47
+ loading: boolean;
48
+ error: string | null;
49
+ }
50
+ declare function useStripeCheckout(): UseStripeCheckoutReturn;
51
+
52
+ export { useKeyLedger, useKeys, useListKey, useMintKey, usePricing, useStripeCheckout };
@@ -0,0 +1 @@
1
+ import {createContext,useState,useRef,useEffect,useCallback,useContext}from'react';import'react/jsx-runtime';var w=createContext(null);function m(){let e=useContext(w);if(!e)throw new Error("useKeyUtilsContext must be used within a KeyUtilsProvider");return e}function S(){let{config:e}=m(),[c,g]=useState(null),[u,f]=useState(true),[y,d]=useState(null),i=useRef(true),l=useRef(e.apiBaseUrl);l.current=e.apiBaseUrl;let a=async()=>{try{i.current&&(f(!0),d(null));let s=await fetch(`${l.current}/api/keys/pricing`);if(!s.ok){let t=`Pricing unavailable (${s.status})`;try{let n=await s.json();n.error&&(t=n.error);}catch{}throw new Error(t)}let r=await s.json();i.current&&g(r);}catch(s){i.current&&d(s instanceof Error?s.message:"Failed to fetch pricing");}finally{i.current&&f(false);}};return useEffect(()=>{i.current=true,a();let s=setInterval(a,6e4);return ()=>{i.current=false,clearInterval(s);}},[]),{pricing:c,loading:u,error:y,refresh:a}}function j(e){return {id:e.id||"",key:e.key||e.keyPrefix||"",userId:e.userId||e.user_id||"",status:e.status||"active",tokenBalance:e.tokenBalance??e.currentTokenBalance??e.current_token_balance??0,maxTokens:e.maxTokens??e.initialTokenBalance??e.initial_token_balance??0,createdAt:String(e.createdAt??e.created_at??""),expiresAt:e.expiresAt,label:e.label||e.keyPrefix||e.key_prefix||void 0,paperWork:e.paperWork||e.paper_work||void 0,stackId:e.stackId,stackName:e.stackName}}function N(){let{config:e}=m(),[c,g]=useState([]),[u,f]=useState(true),[y,d]=useState(null),i=useRef(true),l=e.apiBaseUrl,a=useCallback(async()=>{try{f(!0),d(null);let r=await fetch(`${l}/api/keys`);if(!r.ok){let o=`Failed to fetch keys (${r.status})`;try{let p=await r.json();p.error&&(o=p.error);}catch{}throw new Error(o)}let t=await r.json(),n=Array.isArray(t)?t:t.keys??[];i.current&&g(n.map(j));}catch(r){i.current&&d(r instanceof Error?r.message:"Failed to fetch keys");}finally{i.current&&f(false);}},[l]);useEffect(()=>(i.current=true,a(),()=>{i.current=false;}),[a]);let s=c.reduce((r,t)=>r+t.tokenBalance,0);return {keys:c,totalBalance:s,loading:u,error:y,refresh:a}}function D(e){let{config:c}=m(),[g,u]=useState([]),[f,y]=useState(false),[d,i]=useState(null),l=useRef(true),a=c.apiBaseUrl,s=useCallback(async()=>{if(e)try{y(!0),i(null);let r=await fetch(`${a}/api/keys/${e}/ledger`);if(!r.ok){let n=`Failed to fetch ledger (${r.status})`;try{let o=await r.json();o.error&&(n=o.error);}catch{}throw new Error(n)}let t=await r.json();l.current&&u(Array.isArray(t)?t:t.entries??[]);}catch(r){l.current&&i(r instanceof Error?r.message:"Failed to fetch ledger");}finally{l.current&&y(false);}},[a,e]);return useEffect(()=>(l.current=true,s(),()=>{l.current=false;}),[s]),{entries:g,loading:f,error:d,refresh:s}}function F(){let{config:e,callbacks:c}=m(),[g,u]=useState(false),[f,y]=useState(null),[d,i]=useState(null);return {mint:useCallback(async(a,s,r=1)=>{try{u(!0),i(null),c.onPaymentComplete?.(a,s);let t=await fetch(`${e.apiBaseUrl}/api/keys/mint`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paymentMethod:a,paymentRef:s,quantity:r})});if(!t.ok){let k="Key generation failed";try{let C=await t.json();C.error&&(k=C.error);}catch{}throw new Error(k)}let n=await t.json(),o=n.keys?.[0]||n,p={success:!0,key:{id:o.keyId||o.id||"",key:o.key||"",userId:o.userId||"",status:"active",tokenBalance:o.tokenBalance??o.currentTokenBalance??0,maxTokens:o.initialTokenBalance??o.tokenBalance??0,createdAt:String(o.createdAt||Date.now())},transactionId:s};return y(p),c.onMintSuccess?.(p),p}catch(t){let n=t instanceof Error?t:new Error("Key generation failed");return i(n.message),c.onMintError?.(n),{success:false,error:n.message}}finally{u(false);}},[e.apiBaseUrl,c]),minting:g,result:f,error:d}}function V(){let{config:e,callbacks:c}=m(),[g,u]=useState(false),[f,y]=useState(null),[d,i]=useState(null),l=useRef(true),a=useCallback(async(r,t)=>{try{u(!0),i(null);let n=await fetch(`${e.apiBaseUrl}/api/keys/${r}/list`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({askPriceCents:t,stackId:e.stackId,stackName:e.stackName})});if(!n.ok){let p=`Listing failed (${n.status})`;try{let k=await n.json();k.error&&(p=k.error);}catch{}throw new Error(p)}let o=await n.json();return l.current&&(y(o),c.onListingCreated?.(o)),o}catch(n){let o=n instanceof Error?n.message:"Listing failed";return l.current&&i(o),null}finally{l.current&&u(false);}},[e.apiBaseUrl,e.stackId,e.stackName,c]),s=useCallback(async r=>{try{u(!0),i(null);let t=await fetch(`${e.apiBaseUrl}/api/keys/${r}/list`,{method:"DELETE"});if(!t.ok){let n=`Delist failed (${t.status})`;try{let o=await t.json();o.error&&(n=o.error);}catch{}throw new Error(n)}return l.current&&y(null),!0}catch(t){let n=t instanceof Error?t.message:"Delist failed";return l.current&&i(n),false}finally{l.current&&u(false);}},[e.apiBaseUrl]);return {listKey:a,delistKey:s,listing:g,result:f,error:d}}function z(){let{config:e}=m(),[c,g]=useState(false),[u,f]=useState(null),y=useRef(true);return {createSession:useCallback(async(i,l)=>{try{g(!0),f(null);let a=await fetch(`${e.apiBaseUrl}/api/keys/stripe-session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({priceCents:i,quantity:l,stackId:e.stackId})});if(!a.ok){let r=`Checkout failed (${a.status})`;try{let t=await a.json();t.error&&(r=t.error);}catch{}throw new Error(r)}let{url:s}=await a.json();if(s){try{sessionStorage.setItem("keyutils_stripe_pending","1");}catch{}window.location.href=s;}else throw new Error("No checkout URL returned")}catch(a){if(y.current){let s=a instanceof Error?a.message:"Checkout failed";f(s),g(false);}}},[e.apiBaseUrl,e.stackId]),loading:c,error:u}}export{D as useKeyLedger,N as useKeys,V as useListKey,F as useMintKey,S as usePricing,z as useStripeCheckout};
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';var clsx=require('clsx'),tailwindMerge=require('tailwind-merge'),pt=require('react'),jsxRuntime=require('react/jsx-runtime'),lucideReact=require('lucide-react');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var pt__default=/*#__PURE__*/_interopDefault(pt);function m(...e){return tailwindMerge.twMerge(clsx.clsx(e))}function L(e){return e==null?"0":e>=1e9?`${(e/1e9).toFixed(1)}B`:e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:e.toLocaleString()}function Pe(e,t){return t<=0?0:Math.min(100,Math.round(e/t*100))}function Ce(e){return e>=70?"bg-green-500":e>=30?"bg-yellow-500":"bg-red-500"}function F(e){let t=typeof e=="string"&&/^\d+$/.test(e)?Number(e):e,r=new Date(t);return isNaN(r.getTime())?String(e):r.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}function Se(){if(typeof navigator>"u")return "mac";let e=navigator.userAgent.toLowerCase();return /iphone|ipad|ipod/.test(e)?"ios":/android/.test(e)?"android":/win/.test(e)?"windows":/linux/.test(e)?"linux":"mac"}var Ke=pt.createContext(null);function N(){let e=pt.useContext(Ke);if(!e)throw new Error("useKeyUtilsContext must be used within a KeyUtilsProvider");return e}function ee({config:e,callbacks:t={},children:r}){typeof e.apiBaseUrl!="string"&&console.error("[KeyUtils] apiBaseUrl must be a string");let s=pt.useMemo(()=>({config:e,callbacks:t}),[e,t]);return jsxRuntime.jsx(Ke.Provider,{value:s,children:r})}function re(){let{config:e}=N(),[t,r]=pt.useState(null),[s,g]=pt.useState(true),[c,f]=pt.useState(null),a=pt.useRef(true),o=pt.useRef(e.apiBaseUrl);o.current=e.apiBaseUrl;let d=async()=>{try{a.current&&(g(!0),f(null));let p=await fetch(`${o.current}/api/keys/pricing`);if(!p.ok){let i=`Pricing unavailable (${p.status})`;try{let n=await p.json();n.error&&(i=n.error);}catch{}throw new Error(i)}let u=await p.json();a.current&&r(u);}catch(p){a.current&&f(p instanceof Error?p.message:"Failed to fetch pricing");}finally{a.current&&g(false);}};return pt.useEffect(()=>{a.current=true,d();let p=setInterval(d,6e4);return ()=>{a.current=false,clearInterval(p);}},[]),{pricing:t,loading:s,error:c,refresh:d}}function Je(e){return {id:e.id||"",key:e.key||e.keyPrefix||"",userId:e.userId||e.user_id||"",status:e.status||"active",tokenBalance:e.tokenBalance??e.currentTokenBalance??e.current_token_balance??0,maxTokens:e.maxTokens??e.initialTokenBalance??e.initial_token_balance??0,createdAt:String(e.createdAt??e.created_at??""),expiresAt:e.expiresAt,label:e.label||e.keyPrefix||e.key_prefix||void 0,paperWork:e.paperWork||e.paper_work||void 0,stackId:e.stackId,stackName:e.stackName}}function Q(){let{config:e}=N(),[t,r]=pt.useState([]),[s,g]=pt.useState(true),[c,f]=pt.useState(null),a=pt.useRef(true),o=e.apiBaseUrl,d=pt.useCallback(async()=>{try{g(!0),f(null);let u=await fetch(`${o}/api/keys`);if(!u.ok){let l=`Failed to fetch keys (${u.status})`;try{let v=await u.json();v.error&&(l=v.error);}catch{}throw new Error(l)}let i=await u.json(),n=Array.isArray(i)?i:i.keys??[];a.current&&r(n.map(Je));}catch(u){a.current&&f(u instanceof Error?u.message:"Failed to fetch keys");}finally{a.current&&g(false);}},[o]);pt.useEffect(()=>(a.current=true,d(),()=>{a.current=false;}),[d]);let p=t.reduce((u,i)=>u+i.tokenBalance,0);return {keys:t,totalBalance:p,loading:s,error:c,refresh:d}}function ne(e){let{config:t}=N(),[r,s]=pt.useState([]),[g,c]=pt.useState(false),[f,a]=pt.useState(null),o=pt.useRef(true),d=t.apiBaseUrl,p=pt.useCallback(async()=>{if(e)try{c(!0),a(null);let u=await fetch(`${d}/api/keys/${e}/ledger`);if(!u.ok){let n=`Failed to fetch ledger (${u.status})`;try{let l=await u.json();l.error&&(n=l.error);}catch{}throw new Error(n)}let i=await u.json();o.current&&s(Array.isArray(i)?i:i.entries??[]);}catch(u){o.current&&a(u instanceof Error?u.message:"Failed to fetch ledger");}finally{o.current&&c(false);}},[d,e]);return pt.useEffect(()=>(o.current=true,p(),()=>{o.current=false;}),[p]),{entries:r,loading:g,error:f,refresh:p}}function ie(){let{config:e,callbacks:t}=N(),[r,s]=pt.useState(false),[g,c]=pt.useState(null),[f,a]=pt.useState(null);return {mint:pt.useCallback(async(d,p,u=1)=>{try{s(!0),a(null),t.onPaymentComplete?.(d,p);let i=await fetch(`${e.apiBaseUrl}/api/keys/mint`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paymentMethod:d,paymentRef:p,quantity:u})});if(!i.ok){let P="Key generation failed";try{let w=await i.json();w.error&&(P=w.error);}catch{}throw new Error(P)}let n=await i.json(),l=n.keys?.[0]||n,v={success:!0,key:{id:l.keyId||l.id||"",key:l.key||"",userId:l.userId||"",status:"active",tokenBalance:l.tokenBalance??l.currentTokenBalance??0,maxTokens:l.initialTokenBalance??l.tokenBalance??0,createdAt:String(l.createdAt||Date.now())},transactionId:p};return c(v),t.onMintSuccess?.(v),v}catch(i){let n=i instanceof Error?i:new Error("Key generation failed");return a(n.message),t.onMintError?.(n),{success:false,error:n.message}}finally{s(false);}},[e.apiBaseUrl,t]),minting:r,result:g,error:f}}function ce(){let{config:e,callbacks:t}=N(),[r,s]=pt.useState(false),[g,c]=pt.useState(null),[f,a]=pt.useState(null),o=pt.useRef(true),d=pt.useCallback(async(u,i)=>{try{s(!0),a(null);let n=await fetch(`${e.apiBaseUrl}/api/keys/${u}/list`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({askPriceCents:i,stackId:e.stackId,stackName:e.stackName})});if(!n.ok){let v=`Listing failed (${n.status})`;try{let P=await n.json();P.error&&(v=P.error);}catch{}throw new Error(v)}let l=await n.json();return o.current&&(c(l),t.onListingCreated?.(l)),l}catch(n){let l=n instanceof Error?n.message:"Listing failed";return o.current&&a(l),null}finally{o.current&&s(false);}},[e.apiBaseUrl,e.stackId,e.stackName,t]),p=pt.useCallback(async u=>{try{s(!0),a(null);let i=await fetch(`${e.apiBaseUrl}/api/keys/${u}/list`,{method:"DELETE"});if(!i.ok){let n=`Delist failed (${i.status})`;try{let l=await i.json();l.error&&(n=l.error);}catch{}throw new Error(n)}return o.current&&c(null),!0}catch(i){let n=i instanceof Error?i.message:"Delist failed";return o.current&&a(n),false}finally{o.current&&s(false);}},[e.apiBaseUrl]);return {listKey:d,delistKey:p,listing:r,result:g,error:f}}function de(){let{config:e}=N(),[t,r]=pt.useState(false),[s,g]=pt.useState(null),c=pt.useRef(true);return {createSession:pt.useCallback(async(a,o)=>{try{r(!0),g(null);let d=await fetch(`${e.apiBaseUrl}/api/keys/stripe-session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({priceCents:a,quantity:o,stackId:e.stackId})});if(!d.ok){let u=`Checkout failed (${d.status})`;try{let i=await d.json();i.error&&(u=i.error);}catch{}throw new Error(u)}let{url:p}=await d.json();if(p){try{sessionStorage.setItem("keyutils_stripe_pending","1");}catch{}window.location.href=p;}else throw new Error("No checkout URL returned")}catch(d){if(c.current){let p=d instanceof Error?d.message:"Checkout failed";g(p),r(false);}}},[e.apiBaseUrl,e.stackId]),loading:t,error:s}}var at=/^[1-9A-HJ-NP-Za-km-z]{32,44}$/;function fe({amountSol:e,onSuccess:t,onError:r,disabled:s,className:g,style:c}){let{config:f}=N(),[a,o]=pt.useState(false),[d,p]=pt.useState(null),u=!!(f.merchantWallet&&at.test(f.merchantWallet)),i=pt.useCallback(async()=>{if(u)try{o(!0),p(null);let{Connection:n,PublicKey:l,Transaction:v,SystemProgram:P,LAMPORTS_PER_SOL:w}=await import('@solana/web3.js'),R=typeof window<"u"?window.solana:void 0;if(!R||typeof R!="object"||!R.isPhantom)throw new Error("Phantom wallet not found. Please install Phantom.");let B=R,M;try{M=new l(f.merchantWallet);}catch{throw new Error("Invalid merchant wallet address.")}let _=await B.connect(),k=new l(_.publicKey.toString()),W=new n(f.solanaRpcUrl||"https://api.mainnet-beta.solana.com"),{blockhash:A}=await W.getLatestBlockhash(),Z=BigInt(Math.floor(e*1e9)),X=new v({recentBlockhash:A,feePayer:k}).add(P.transfer({fromPubkey:k,toPubkey:M,lamports:Number(Z)})),{signature:K}=await B.signAndSendTransaction(X);t(K);}catch(n){let l=n instanceof Error?n:new Error("Payment failed");p(l.message),r?.(l);}finally{o(false);}},[e,f.merchantWallet,u,t,r]);return jsxRuntime.jsxs("div",{children:[jsxRuntime.jsx("button",{onClick:i,disabled:s||a||!u,"aria-label":`Pay ${e.toFixed(4)} SOL`,className:m("flex w-full items-center justify-center gap-2 rounded-lg bg-purple-600 px-4 py-3 font-medium text-white transition-colors hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed",g),style:c,children:a?jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(lucideReact.Loader2,{className:"h-4 w-4 animate-spin"}),"Processing..."]}):jsxRuntime.jsxs(jsxRuntime.Fragment,{children:["Pay ",e.toFixed(4)," SOL"]})}),d&&jsxRuntime.jsx("p",{className:"mt-2 text-center text-xs text-red-500",children:d})]})}function pe({result:e,className:t,style:r}){let[s,g]=pt.useState(false),c=pt.useRef(null);pt.useEffect(()=>()=>{c.current&&clearTimeout(c.current);},[]);let f=pt.useCallback(()=>{e.key?.key&&(navigator.clipboard.writeText(e.key.key),g(true),c.current&&clearTimeout(c.current),c.current=setTimeout(()=>g(false),2e3));},[e.key?.key]);return !e.success||!e.key?null:jsxRuntime.jsxs("div",{className:m("rounded-lg border border-green-500/20 bg-green-500/10 p-6 text-center",t),style:r,children:[jsxRuntime.jsx(lucideReact.CheckCircle,{className:"mx-auto mb-3 h-12 w-12 text-green-500"}),jsxRuntime.jsx("h3",{className:"mb-2 text-lg font-semibold text-foreground",children:"Key Generated!"}),jsxRuntime.jsx("p",{className:"mb-4 text-sm text-muted-foreground",children:"Your node key has been successfully generated."}),jsxRuntime.jsxs("div",{className:"mx-auto flex max-w-md items-center gap-2 rounded-md border bg-background p-3",children:[jsxRuntime.jsx("code",{className:"flex-1 truncate text-xs text-foreground",children:e.key.key}),jsxRuntime.jsx("button",{onClick:f,"aria-label":"Copy key to clipboard",className:"shrink-0 rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",children:s?jsxRuntime.jsx(lucideReact.Check,{className:"h-4 w-4 text-green-500"}):jsxRuntime.jsx(lucideReact.Copy,{className:"h-4 w-4"})})]})]})}function I({className:e}){return jsxRuntime.jsx("div",{className:m("animate-pulse rounded-xl bg-muted",e)})}function ge(){return jsxRuntime.jsxs("div",{className:"space-y-6",children:[jsxRuntime.jsxs("div",{className:"px-6 py-10 flex flex-col items-center gap-3",children:[jsxRuntime.jsx(I,{className:"h-14 w-48"}),jsxRuntime.jsx(I,{className:"h-5 w-64"})]}),jsxRuntime.jsx(I,{className:"h-[72px] w-full rounded-2xl"}),jsxRuntime.jsx(I,{className:"h-[68px] w-full rounded-2xl"}),jsxRuntime.jsx(I,{className:"h-[60px] w-full rounded-2xl"})]})}function J({count:e=3}){return jsxRuntime.jsx("div",{className:"space-y-3",children:Array.from({length:e}).map((t,r)=>jsxRuntime.jsxs("div",{className:"rounded-xl border p-4 space-y-3",children:[jsxRuntime.jsxs("div",{className:"flex items-center justify-between",children:[jsxRuntime.jsx(I,{className:"h-4 w-32"}),jsxRuntime.jsx(I,{className:"h-4 w-16"})]}),jsxRuntime.jsx(I,{className:"h-2 w-full rounded-full"}),jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx(I,{className:"h-3 w-24"}),jsxRuntime.jsx(I,{className:"h-3 w-20"})]})]},r))})}function kt({quantity:e,max:t,onChange:r}){return jsxRuntime.jsxs("div",{className:"flex items-center justify-between rounded-2xl border bg-muted/20 px-6 py-5",children:[jsxRuntime.jsx("span",{className:"text-sm font-medium text-foreground",children:"Quantity"}),jsxRuntime.jsxs("div",{className:"flex items-center gap-0 rounded-xl border bg-background",children:[jsxRuntime.jsx("button",{onClick:()=>r(Math.max(1,e-1)),disabled:e<=1,"aria-label":"Decrease quantity",className:"flex h-12 w-12 items-center justify-center rounded-l-xl text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-30",children:jsxRuntime.jsx(lucideReact.Minus,{className:"h-5 w-5"})}),jsxRuntime.jsx("span",{className:"flex h-12 w-14 items-center justify-center border-x text-xl font-bold text-foreground","aria-live":"polite",children:e}),jsxRuntime.jsx("button",{onClick:()=>r(Math.min(t,e+1)),disabled:e>=t,"aria-label":"Increase quantity",className:"flex h-12 w-12 items-center justify-center rounded-r-xl text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-30",children:jsxRuntime.jsx(lucideReact.Plus,{className:"h-5 w-5"})})]})]})}function Me({pricing:e,quantity:t}){let[r,s]=pt.useState(false),g=e.priceUsd*t;return jsxRuntime.jsxs("div",{className:"rounded-2xl border bg-background",children:[jsxRuntime.jsxs("button",{onClick:()=>s(!r),"aria-expanded":r,className:"flex w-full items-center justify-between px-6 py-5 text-left",children:[jsxRuntime.jsxs("span",{className:"text-foreground",children:[jsxRuntime.jsxs("span",{className:"text-xl font-bold",children:["$",g.toFixed(2)]})," ",jsxRuntime.jsx("span",{className:"text-sm text-muted-foreground",children:"total fees included"})]}),r?jsxRuntime.jsx(lucideReact.ChevronUp,{className:"h-5 w-5 text-muted-foreground"}):jsxRuntime.jsx(lucideReact.ChevronDown,{className:"h-5 w-5 text-muted-foreground"})]}),r&&jsxRuntime.jsxs("div",{className:"border-t px-6 pb-8 pt-4 space-y-3 text-sm",children:[jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsxs("span",{className:"text-muted-foreground",children:["Node Key \xD7 ",t]}),jsxRuntime.jsxs("span",{className:"text-foreground",children:["$",(e.priceUsd*t).toFixed(2)]})]}),jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx("span",{className:"text-muted-foreground",children:"Keys sold"}),jsxRuntime.jsx("span",{className:"text-foreground",children:e.keysSold})]}),e.daysUntilHalving>0&&jsxRuntime.jsxs("div",{className:"flex justify-between text-muted-foreground",children:[jsxRuntime.jsx("span",{children:"Next refresh"}),jsxRuntime.jsxs("span",{children:[e.daysUntilHalving," day",e.daysUntilHalving!==1?"s":""]})]}),jsxRuntime.jsxs("div",{className:"flex justify-between border-t pt-3 font-semibold",children:[jsxRuntime.jsx("span",{className:"text-foreground",children:"Total"}),jsxRuntime.jsxs("span",{className:"text-foreground",children:["$",g.toFixed(2)]})]})]})]})}function xe({className:e,style:t}){let{config:r}=N(),{pricing:s,loading:g,error:c,refresh:f}=re(),{mint:a,minting:o,result:d,error:p}=ie(),{createSession:u,loading:i,error:n}=de(),[l,v]=pt.useState(1),[P,w]=pt.useState(r.paymentMethods?.includes("stripe")?"stripe":"solana"),[R,B]=pt.useState(0),[M,_]=pt.useState(false),k=pt__default.default.useRef(l);k.current=l;let W=Math.max(1,r.maxQuantity||5),A=[...r.paymentMethods||["solana"]].sort((K,G)=>K==="stripe"?-1:G==="stripe"?1:0);if(pt.useEffect(()=>{if(typeof window>"u")return;let G=new URLSearchParams(window.location.search).get("session_id");if(G&&/^cs_(test|live)_/.test(G)&&!M&&!d?.success){let ke=false;try{ke=sessionStorage.getItem("keyutils_stripe_pending")==="1";}catch{}if(!ke)return;try{sessionStorage.removeItem("keyutils_stripe_pending");}catch{}_(true);let we=new URL(window.location.href);we.searchParams.delete("session_id"),window.history.replaceState({},"",we.toString()),a("stripe",G,k.current);}},[M,d,a]),d?.success)return jsxRuntime.jsx("div",{className:m("flex flex-col items-center justify-center",e),style:t,children:jsxRuntime.jsx(pe,{result:d})});if(M||o)return jsxRuntime.jsx("div",{className:m("flex flex-col",e),style:t,children:jsxRuntime.jsx(ge,{})});if(g)return jsxRuntime.jsx("div",{className:m("flex flex-col",e),style:t,children:jsxRuntime.jsx(ge,{})});if(c||!s)return jsxRuntime.jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 py-24",e),style:t,children:[jsxRuntime.jsxs("div",{className:"rounded-xl border border-red-500/20 bg-red-500/5 px-6 py-5 text-center max-w-sm",children:[jsxRuntime.jsx("p",{className:"text-sm font-medium text-foreground",children:"Unable to load pricing"}),jsxRuntime.jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:c||"Please check your connection and try again."})]}),jsxRuntime.jsx("button",{onClick:()=>f(),className:"text-sm text-muted-foreground hover:text-foreground transition-colors",children:"Try again"})]});let Z=async K=>{B(2),await a(P,K,l);},X=()=>{u(s.priceCents,l);};return jsxRuntime.jsx("div",{className:m("flex flex-col",e),style:t,children:jsxRuntime.jsxs("div",{className:"space-y-6",children:[R===0&&jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[r.keyImage&&jsxRuntime.jsx("img",{src:r.keyImage,alt:"Node Key",className:r.keyImageClass||"w-1/2 mx-auto"}),jsxRuntime.jsxs("div",{className:"px-6 py-10 text-center",children:[jsxRuntime.jsxs("p",{className:"text-5xl font-bold tracking-tight text-foreground md:text-6xl",children:["$",s.priceUsd.toFixed(2)]}),jsxRuntime.jsxs("p",{className:"mt-2 text-base text-muted-foreground",children:["per key \xB7 ",s.tokenAllocationFormatted||L(s.tokenAllocation)," tokens included"]})]}),jsxRuntime.jsx(kt,{quantity:l,max:W,onChange:v}),jsxRuntime.jsx(Me,{pricing:s,quantity:l}),jsxRuntime.jsx("button",{onClick:()=>B(1),className:"w-full rounded-2xl bg-foreground py-5 text-center text-base font-semibold text-background transition-colors hover:bg-foreground/90",children:"Continue to Payment"})]}),R===1&&jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[A.length>1&&jsxRuntime.jsx("div",{role:"tablist",className:"flex gap-1 rounded-2xl bg-muted/50 p-1.5",children:A.map(K=>jsxRuntime.jsxs("button",{role:"tab","aria-selected":P===K,onClick:()=>w(K),className:m("flex flex-1 items-center justify-center gap-2.5 rounded-xl py-4 text-sm font-medium transition-colors",P===K?"bg-background text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:[K==="stripe"?jsxRuntime.jsx(lucideReact.CreditCard,{className:"h-5 w-5"}):jsxRuntime.jsx(lucideReact.Wallet,{className:"h-5 w-5"}),K==="stripe"?"Credit Card":"Crypto"]},K))}),jsxRuntime.jsx(Me,{pricing:s,quantity:l}),P==="stripe"?jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx("button",{onClick:X,disabled:i,className:"flex w-full items-center justify-center gap-2 rounded-2xl bg-foreground py-5 text-base font-semibold text-background transition-colors hover:bg-foreground/90 disabled:opacity-50",children:i?jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(lucideReact.Loader2,{className:"h-5 w-5 animate-spin"}),"Redirecting to Stripe..."]}):jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(lucideReact.CreditCard,{className:"h-5 w-5"}),"Pay with Card"]})}),n&&jsxRuntime.jsx("p",{className:"text-center text-sm text-red-500",children:n})]}):P==="solana"?jsxRuntime.jsx(fe,{amountSol:s.priceSol??0,onSuccess:Z,className:"rounded-2xl py-5 text-base"}):null,p&&jsxRuntime.jsx("p",{className:"text-center text-sm text-red-500",children:p}),jsxRuntime.jsx("button",{onClick:()=>B(0),className:"w-full py-3 text-center text-sm text-muted-foreground hover:text-foreground transition-colors",children:"\u2190 Back to Configure"})]})]})})}var St={ios:"iOS",android:"Android",windows:"Windows",mac:"macOS",linux:"Linux"},Kt=["mac","windows","linux","ios","android"];function ye({downloads:e,className:t,style:r}){let{config:s}=N(),g=pt.useMemo(()=>Se(),[]),c=e||Kt.map(o=>({platform:o,label:St[o],url:"#",compatible:o===g})),f=c.find(o=>o.compatible)||c[0],a=c.filter(o=>o.platform!==f.platform);return jsxRuntime.jsxs("div",{className:m("space-y-8",t),style:r,children:[jsxRuntime.jsxs("div",{className:"flex flex-col items-center gap-6 pt-4 mt-4",children:[s.aispImage&&jsxRuntime.jsx("img",{src:s.aispImage,alt:"aiSP",className:"w-full sm:w-1/2 mx-auto px-4 sm:px-0"}),jsxRuntime.jsxs("div",{className:"text-center",children:[jsxRuntime.jsx("p",{className:"text-lg sm:text-2xl font-medium text-foreground",children:"Download the AI Service Provider App"}),jsxRuntime.jsx("p",{className:"mt-1 text-sm sm:text-base text-muted-foreground",children:"Load your keys and run your node."})]}),jsxRuntime.jsxs("a",{href:f.url,className:"inline-flex items-center gap-3 rounded-full px-10 py-4 text-lg font-semibold text-white transition-colors hover:opacity-90",style:{backgroundColor:"#0900f4"},children:[jsxRuntime.jsx(lucideReact.Download,{className:"h-5 w-5"}),"Get aiSP for ",f.label]})]}),jsxRuntime.jsxs("p",{className:"text-center text-sm text-muted-foreground",children:["aiSP is also available on"," ",a.map((o,d)=>jsxRuntime.jsxs(pt__default.default.Fragment,{children:[d>0&&(d===a.length-1?" and ":", "),jsxRuntime.jsx("a",{href:o.url,className:"font-medium underline transition-colors hover:text-foreground",style:{color:"#0900f4"},children:o.label})]},o.platform)),"."]}),jsxRuntime.jsxs("div",{className:"flex flex-col items-center gap-2 mt-12",children:[jsxRuntime.jsxs("p",{className:"text-center text-xs text-muted-foreground flex justify-center gap-0.5 flex-wrap",children:["Source code is available on",jsxRuntime.jsx("a",{href:"https://github.com/stack-net",target:"_blank",rel:"noopener noreferrer",className:"text-foreground underline hover:text-foreground/80 color-[#0900f4]",children:"Github"})]}),jsxRuntime.jsxs("p",{className:"text-center text-xs text-muted-foreground flex justify-center gap-0.5 flex-wrap",children:["By downloading, you agree to the",jsxRuntime.jsx("a",{href:s.termsUrl||"/terms",className:"text-foreground underline hover:text-foreground/80",children:"Open Source Applications Terms"})]})]})]})}function Y({current:e,max:t,className:r,style:s,showLabel:g=true}){let c=Pe(e,t),f=Ce(c);return jsxRuntime.jsxs("div",{className:m("w-full",r),style:s,children:[g&&jsxRuntime.jsxs("div",{className:"mb-1 flex items-center justify-between text-xs text-muted-foreground",children:[jsxRuntime.jsxs("span",{children:[L(e)," / ",L(t)]}),jsxRuntime.jsxs("span",{children:[c,"%"]})]}),jsxRuntime.jsx("div",{className:"h-2 w-full overflow-hidden rounded-full bg-muted",children:jsxRuntime.jsx("div",{className:m("h-full rounded-full transition-all duration-500",f),style:{width:`${c}%`}})})]})}function Lt({nodeKey:e,selected:t,onSelect:r}){return jsxRuntime.jsxs("button",{onClick:r,className:m("flex w-full items-start gap-3 rounded-xl border p-4 text-left transition-all",t?"border-foreground/30 bg-foreground/5 ring-1 ring-foreground/10":"border-border hover:border-foreground/20"),children:[jsxRuntime.jsx("div",{className:m("mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full border-2 transition-colors",t?"border-foreground bg-foreground":"border-muted-foreground/40"),children:t&&jsxRuntime.jsx(lucideReact.Check,{className:"h-3 w-3 text-background"})}),jsxRuntime.jsxs("div",{className:"min-w-0 flex-1",children:[jsxRuntime.jsxs("div",{className:"flex items-center justify-between",children:[jsxRuntime.jsx("span",{className:"text-sm font-medium text-foreground",children:e.label||e.id.slice(0,16)}),jsxRuntime.jsx("span",{className:m("text-xs font-medium capitalize",e.status==="active"?"text-green-500":"text-muted-foreground"),children:e.status})]}),jsxRuntime.jsx(Y,{current:e.tokenBalance,max:e.maxTokens,className:"mt-2"})]})]})}function he({className:e,style:t}){let{config:r}=N(),{keys:s,loading:g,error:c,refresh:f}=Q(),{listKey:a,listing:o,result:d,error:p}=ce(),[u,i]=pt.useState(null),[n,l]=pt.useState(""),[v,P]=pt.useState(false),w=s.find(k=>k.id===u),R=parseInt(n.replace(/[^0-9]/g,""),10)||0,B=w&&R>0&&v&&!o,M=async()=>{if(!B||!w)return;let k=R*100;await a(w.id,k)&&f();};if(d)return jsxRuntime.jsxs("div",{className:m("flex flex-col items-center gap-4 rounded-xl border border-green-500/20 bg-green-500/10 py-12",e),style:t,children:[jsxRuntime.jsx(lucideReact.CheckCircle,{className:"h-12 w-12 text-green-500"}),jsxRuntime.jsx("h3",{className:"text-lg font-semibold text-foreground",children:"Key Listed!"}),jsxRuntime.jsx("p",{className:"text-sm text-muted-foreground",children:"Your key is now on the Global Open AI Network marketplace."}),jsxRuntime.jsxs("p",{className:"text-xs text-muted-foreground",children:["Listing ID: ",jsxRuntime.jsx("code",{children:d.listingId})]})]});if(g)return jsxRuntime.jsxs("div",{className:m("space-y-5 px-1",e),style:t,children:[jsxRuntime.jsx("h3",{className:"text-2xl mt-3 mb-2 font-semibold text-foreground",children:"Your Keys"}),jsxRuntime.jsx(J,{count:2})]});if(c)return jsxRuntime.jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 py-16",e),style:t,children:[jsxRuntime.jsxs("div",{className:"rounded-xl border border-red-500/20 bg-red-500/5 px-6 py-5 text-center max-w-sm",children:[jsxRuntime.jsx("p",{className:"text-sm font-medium text-foreground",children:"Unable to load your keys"}),jsxRuntime.jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:c})]}),jsxRuntime.jsx("button",{onClick:()=>f(),className:"text-sm text-muted-foreground hover:text-foreground transition-colors",children:"Try again"})]});let _=s.filter(k=>k.status==="active");return _.length===0?jsxRuntime.jsxs("div",{className:m("flex flex-col items-center justify-center gap-3 rounded-xl border border-dashed py-16",e),style:t,children:[jsxRuntime.jsx(lucideReact.AlertTriangle,{className:"h-8 w-8 text-muted-foreground"}),jsxRuntime.jsx("p",{className:"text-sm text-muted-foreground",children:"No active keys to sell."})]}):jsxRuntime.jsxs("div",{className:m("space-y-5",e),style:t,children:[jsxRuntime.jsxs("div",{children:[jsxRuntime.jsx("h3",{className:"text-2xl mt-3 mb-2 font-semibold text-foreground",children:"Your Keys"}),jsxRuntime.jsx("div",{className:"space-y-2",children:_.map(k=>jsxRuntime.jsx(Lt,{nodeKey:k,selected:u===k.id,onSelect:()=>i(k.id)},k.id))})]}),w&&jsxRuntime.jsxs("div",{className:"rounded-xl border bg-muted/30 p-5 space-y-2 text-sm",children:[jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx("span",{className:"text-muted-foreground",children:"Key ID"}),jsxRuntime.jsx("code",{className:"text-xs text-foreground",children:w.id})]}),jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx("span",{className:"text-muted-foreground",children:"Originating Stack"}),jsxRuntime.jsx("span",{className:"text-foreground",children:r.stackName||"Unknown"})]}),jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx("span",{className:"text-muted-foreground",children:"Tokens (remaining / total)"}),jsxRuntime.jsxs("span",{className:"text-foreground",children:[L(w.tokenBalance)," / ",L(w.maxTokens)]})]}),w.paperWork&&jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx("span",{className:"text-muted-foreground",children:"Paper earned"}),jsxRuntime.jsx("span",{className:"text-foreground",children:w.paperWork})]}),jsxRuntime.jsxs("div",{className:"flex justify-between",children:[jsxRuntime.jsx("span",{className:"text-muted-foreground",children:"Created"}),jsxRuntime.jsx("span",{className:"text-foreground",children:F(w.createdAt)})]})]}),jsxRuntime.jsxs("div",{children:[jsxRuntime.jsx("label",{htmlFor:"listing-price",className:"mb-2 block text-sm font-semibold text-foreground",children:"Listing Price (USD)"}),jsxRuntime.jsxs("div",{className:"relative",children:[jsxRuntime.jsx("span",{className:"absolute left-4 top-1/2 -translate-y-1/2 text-2xl font-bold text-muted-foreground",children:"$"}),jsxRuntime.jsx("input",{id:"listing-price",type:"text",inputMode:"numeric",value:n,onChange:k=>{let W=k.target.value.replace(/[^0-9]/g,""),A=parseInt(W,10);l(isNaN(A)?"":A.toLocaleString("en-US"));},placeholder:"0",className:"w-full rounded-xl border bg-background py-5 pl-10 pr-4 text-3xl font-bold text-foreground placeholder:text-muted-foreground/30 focus:outline-none focus:ring-2 focus:ring-foreground/20"})]})]}),jsxRuntime.jsxs("label",{className:"flex items-start gap-3 cursor-pointer",children:[jsxRuntime.jsx("input",{type:"checkbox",checked:v,onChange:k=>P(k.target.checked),className:"sr-only"}),jsxRuntime.jsx("div",{"aria-hidden":"true",className:m("mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded border-2 transition-colors",v?"border-foreground bg-foreground":"border-muted-foreground/40"),children:v&&jsxRuntime.jsx(lucideReact.Check,{className:"h-3 w-3 text-background"})}),jsxRuntime.jsxs("span",{className:"text-xs text-muted-foreground leading-relaxed",children:["I agree to list this key on the Global Open AI Network marketplace. The key will be deactivated upon sale and transferred to the buyer. Listing fees may apply."," ",jsxRuntime.jsx("a",{href:r.termsUrl||"/terms",className:"text-foreground underline",children:"Terms"})]})]}),p&&jsxRuntime.jsx("p",{className:"text-sm text-red-500",children:p}),jsxRuntime.jsxs("button",{onClick:M,disabled:!B,className:m("flex w-full items-center justify-center gap-2 rounded-xl py-4 text-sm font-semibold transition-colors",B?"bg-foreground text-background hover:bg-foreground/90":"bg-muted text-muted-foreground cursor-not-allowed"),children:[jsxRuntime.jsx(lucideReact.ExternalLink,{className:"h-4 w-4"}),o?"Listing...":"List on Marketplace"]})]})}var Et=[{id:"buy",label:"Buy"},{id:"use",label:"Use"},{id:"sell",label:"Sell"}];function It({active:e,onChange:t}){return jsxRuntime.jsx("div",{role:"tablist","aria-label":"Key actions",className:"flex gap-1 rounded-2xl bg-muted/50 p-1.5",children:Et.map(({id:r,label:s})=>jsxRuntime.jsx("button",{role:"tab","aria-selected":e===r,"aria-controls":`panel-${r}`,id:`tab-${r}`,onClick:()=>t(r),className:m("flex flex-1 items-center justify-center gap-2.5 rounded-xl py-3.5 text-sm font-medium transition-all",e===r?"bg-background text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:jsxRuntime.jsx("span",{className:"inline",children:s})},r))})}function Mt({defaultTab:e="buy",className:t,style:r}){let[s,g]=pt.useState(e);return jsxRuntime.jsxs("div",{className:m("flex flex-col",t),style:r,children:[jsxRuntime.jsx("div",{className:"sticky top-0 z-10 bg-background/80 px-5 pb-3 pt-2 sm:pt-5 backdrop-blur-sm sm:px-10 md:px-16 lg:px-20",children:jsxRuntime.jsx("div",{className:"mx-auto max-w-xl",children:jsxRuntime.jsx(It,{active:s,onChange:g})})}),jsxRuntime.jsx("div",{className:"flex flex-1 flex-col px-5 pb-8 sm:px-10 md:px-16 md:pb-12 lg:px-20",children:jsxRuntime.jsx("div",{className:"mx-auto w-full max-w-xl flex-1",children:jsxRuntime.jsxs("div",{role:"tabpanel",id:`panel-${s}`,"aria-labelledby":`tab-${s}`,children:[s==="buy"&&jsxRuntime.jsx(xe,{className:"flex-1"}),s==="use"&&jsxRuntime.jsx(ye,{className:"flex-1"}),s==="sell"&&jsxRuntime.jsx(he,{className:"flex-1"})]})})})]})}function jt({apiBaseUrl:e,stackId:t,stackName:r,paymentMethods:s,merchantWallet:g,stripePublicKey:c,theme:f,maxQuantity:a,keyImage:o,keyImageClass:d,aispImage:p,termsUrl:u,onMintSuccess:i,onMintError:n,onPaymentStart:l,onPaymentComplete:v,onListingCreated:P,defaultTab:w,className:R,style:B}){return jsxRuntime.jsx(ee,{config:{apiBaseUrl:e,stackId:t,stackName:r,paymentMethods:s,merchantWallet:g,stripePublicKey:c,theme:f,maxQuantity:a,keyImage:o,keyImageClass:d,aispImage:p,termsUrl:u},callbacks:{onMintSuccess:i,onMintError:n,onPaymentStart:l,onPaymentComplete:v,onListingCreated:P},children:jsxRuntime.jsx(Mt,{defaultTab:w,className:R,style:B})})}var Ne=20;function ve({nodeKey:e,className:t,style:r}){let[s,g]=pt.useState(false),[c,f]=pt.useState(false),a=pt.useRef(null),{entries:o,loading:d}=ne(s?e.id:null);pt.useEffect(()=>()=>{a.current&&clearTimeout(a.current);},[]);let p=pt.useCallback(()=>{e.key&&(navigator.clipboard.writeText(e.key),f(true),a.current&&clearTimeout(a.current),a.current=setTimeout(()=>f(false),2e3));},[e.key]),u=e.status==="active"?"text-green-500":e.status==="expired"?"text-red-500":"text-yellow-500",i=o.slice(0,Ne);return jsxRuntime.jsxs("div",{className:m("rounded-lg border bg-background",t),style:r,children:[jsxRuntime.jsxs("div",{className:"p-4",children:[jsxRuntime.jsxs("div",{className:"mb-3 flex items-center justify-between",children:[jsxRuntime.jsxs("div",{className:"flex items-center gap-2",children:[jsxRuntime.jsx("button",{onClick:()=>g(!s),"aria-expanded":s,"aria-label":s?"Collapse ledger":"Expand ledger",className:"text-muted-foreground hover:text-foreground",children:s?jsxRuntime.jsx(lucideReact.ChevronDown,{className:"h-4 w-4"}):jsxRuntime.jsx(lucideReact.ChevronRight,{className:"h-4 w-4"})}),jsxRuntime.jsx("span",{className:"text-sm font-medium text-foreground",children:e.label||`Key ${e.id.slice(0,8)}`})]}),jsxRuntime.jsx("span",{className:m("text-xs font-medium capitalize",u),children:e.status})]}),e.key&&jsxRuntime.jsxs("div",{className:"mb-3 flex items-center gap-2",children:[jsxRuntime.jsx("code",{className:"flex-1 truncate rounded bg-muted px-2 py-1 text-xs text-muted-foreground",children:e.key}),jsxRuntime.jsx("button",{onClick:p,"aria-label":"Copy key to clipboard",className:"shrink-0 rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",children:c?jsxRuntime.jsx(lucideReact.Check,{className:"h-3.5 w-3.5 text-green-500"}):jsxRuntime.jsx(lucideReact.Copy,{className:"h-3.5 w-3.5"})})]}),jsxRuntime.jsx(Y,{current:e.tokenBalance,max:e.maxTokens}),jsxRuntime.jsxs("div",{className:"mt-2 flex items-center justify-between text-xs text-muted-foreground",children:[jsxRuntime.jsxs("span",{children:["Created ",F(e.createdAt)]}),e.expiresAt&&jsxRuntime.jsxs("span",{children:["Expires ",F(e.expiresAt)]})]})]}),s&&jsxRuntime.jsxs("div",{className:"border-t px-4 py-3",children:[jsxRuntime.jsx("h4",{className:"mb-2 text-xs font-medium text-muted-foreground",children:"Ledger"}),d?jsxRuntime.jsx("p",{className:"text-xs text-muted-foreground",children:"Loading..."}):i.length===0?jsxRuntime.jsx("p",{className:"text-xs text-muted-foreground",children:"No ledger entries."}):jsxRuntime.jsxs("div",{className:"space-y-1.5",children:[i.map(n=>jsxRuntime.jsxs("div",{className:"flex items-center justify-between text-xs",children:[jsxRuntime.jsxs("div",{className:"flex items-center gap-2",children:[jsxRuntime.jsxs("span",{className:m("font-medium",n.type==="credit"||n.type==="reward"?"text-green-500":"text-red-500"),children:[n.type==="credit"||n.type==="reward"?"+":"-",L(Math.abs(n.amount))]}),jsxRuntime.jsx("span",{className:"text-muted-foreground",children:n.description})]}),jsxRuntime.jsx("span",{className:"text-muted-foreground",children:F(n.createdAt)})]},n.id)),o.length>Ne&&jsxRuntime.jsxs("p",{className:"pt-1 text-xs text-muted-foreground",children:["Showing ",Ne," of ",o.length," entries"]})]})]})]})}function Qt({getKeyHref:e="/keys/get",className:t,style:r}){let{keys:s,totalBalance:g,loading:c,error:f,refresh:a}=Q();return c?jsxRuntime.jsx("div",{className:m("space-y-4",t),style:r,children:jsxRuntime.jsx(J,{count:3})}):f?jsxRuntime.jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 py-12",t),style:r,children:[jsxRuntime.jsxs("div",{className:"rounded-xl border border-red-500/20 bg-red-500/5 px-6 py-5 text-center max-w-sm",children:[jsxRuntime.jsx("p",{className:"text-sm font-medium text-foreground",children:"Unable to load your keys"}),jsxRuntime.jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:f})]}),jsxRuntime.jsx("button",{onClick:()=>a(),className:"text-sm text-muted-foreground hover:text-foreground transition-colors",children:"Try again"})]}):s.length===0?jsxRuntime.jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 rounded-lg border border-dashed py-12",t),style:r,children:[jsxRuntime.jsx(lucideReact.KeyRound,{className:"h-10 w-10 text-muted-foreground"}),jsxRuntime.jsxs("div",{className:"text-center",children:[jsxRuntime.jsx("p",{className:"text-sm font-medium text-foreground",children:"No keys yet"}),jsxRuntime.jsx("p",{className:"text-xs text-muted-foreground",children:"Get your first node key to start earning."})]}),e&&jsxRuntime.jsx("a",{href:e,className:"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background transition-colors hover:bg-foreground/90",children:"Get a Key"})]}):jsxRuntime.jsxs("div",{className:m("space-y-4",t),style:r,children:[jsxRuntime.jsxs("div",{className:"flex items-center justify-between",children:[jsxRuntime.jsxs("p",{className:"text-sm text-muted-foreground",children:[s.length," key",s.length!==1?"s":""," \xB7 Total balance: ",L(g)]}),e&&jsxRuntime.jsx("a",{href:e,className:"rounded-md bg-foreground px-3 py-1.5 text-xs font-medium text-background transition-colors hover:bg-foreground/90",children:"Get a Key"})]}),s.map(o=>jsxRuntime.jsx(ve,{nodeKey:o},o.id))]})}
2
+ exports.BuyKeyWidget=jt;exports.BuyPanel=xe;exports.KeyBalanceBar=Y;exports.KeyCard=ve;exports.KeyList=Qt;exports.KeyUtilsProvider=ee;exports.MintSuccess=pe;exports.SellPanel=he;exports.SolanaPayButton=fe;exports.UsePanel=ye;exports.calculateBalancePercentage=Pe;exports.cn=m;exports.detectPlatform=Se;exports.formatDate=F;exports.formatTokens=L;exports.getBalanceColor=Ce;exports.useKeyLedger=ne;exports.useKeyUtilsContext=N;exports.useKeys=Q;exports.useListKey=ce;exports.useMintKey=ie;exports.usePricing=re;exports.useStripeCheckout=de;
@@ -0,0 +1,7 @@
1
+ export { KeyListing, KeyUtilsCallbacks, KeyUtilsConfig, KeyWidgetTab, LedgerEntry, ListingResult, MintResult, NodeKeyInfo, PaymentMethod, Platform, PlatformDownload, PricingInfo } from './types/index.cjs';
2
+ export { calculateBalancePercentage, cn, detectPlatform, formatDate, formatTokens, getBalanceColor } from './utils/index.cjs';
3
+ export { useKeyLedger, useKeys, useListKey, useMintKey, usePricing, useStripeCheckout } from './hooks/index.cjs';
4
+ export { BuyKeyWidget, BuyKeyWidgetProps, BuyPanel, BuyPanelProps, KeyBalanceBar, KeyBalanceBarProps, KeyCard, KeyCardProps, KeyList, KeyListProps, KeyUtilsProvider, KeyUtilsProviderProps, MintSuccess, MintSuccessProps, SellPanel, SellPanelProps, SolanaPayButton, SolanaPayButtonProps, UsePanel, UsePanelProps, useKeyUtilsContext } from './components/index.cjs';
5
+ import 'clsx';
6
+ import 'react/jsx-runtime';
7
+ import 'react';
@@ -0,0 +1,7 @@
1
+ export { KeyListing, KeyUtilsCallbacks, KeyUtilsConfig, KeyWidgetTab, LedgerEntry, ListingResult, MintResult, NodeKeyInfo, PaymentMethod, Platform, PlatformDownload, PricingInfo } from './types/index.js';
2
+ export { calculateBalancePercentage, cn, detectPlatform, formatDate, formatTokens, getBalanceColor } from './utils/index.js';
3
+ export { useKeyLedger, useKeys, useListKey, useMintKey, usePricing, useStripeCheckout } from './hooks/index.js';
4
+ export { BuyKeyWidget, BuyKeyWidgetProps, BuyPanel, BuyPanelProps, KeyBalanceBar, KeyBalanceBarProps, KeyCard, KeyCardProps, KeyList, KeyListProps, KeyUtilsProvider, KeyUtilsProviderProps, MintSuccess, MintSuccessProps, SellPanel, SellPanelProps, SolanaPayButton, SolanaPayButtonProps, UsePanel, UsePanelProps, useKeyUtilsContext } from './components/index.js';
5
+ import 'clsx';
6
+ import 'react/jsx-runtime';
7
+ import 'react';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import {clsx}from'clsx';import {twMerge}from'tailwind-merge';import pt,{createContext,useContext,useMemo,useState,useRef,useEffect,useCallback}from'react';import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {Loader2,CheckCircle,Check,Copy,CreditCard,Wallet,Download,AlertTriangle,ExternalLink,ChevronDown,ChevronRight,KeyRound,Minus,Plus,ChevronUp}from'lucide-react';function m(...e){return twMerge(clsx(e))}function L(e){return e==null?"0":e>=1e9?`${(e/1e9).toFixed(1)}B`:e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}K`:e.toLocaleString()}function Pe(e,t){return t<=0?0:Math.min(100,Math.round(e/t*100))}function Ce(e){return e>=70?"bg-green-500":e>=30?"bg-yellow-500":"bg-red-500"}function F(e){let t=typeof e=="string"&&/^\d+$/.test(e)?Number(e):e,r=new Date(t);return isNaN(r.getTime())?String(e):r.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}function Se(){if(typeof navigator>"u")return "mac";let e=navigator.userAgent.toLowerCase();return /iphone|ipad|ipod/.test(e)?"ios":/android/.test(e)?"android":/win/.test(e)?"windows":/linux/.test(e)?"linux":"mac"}var Ke=createContext(null);function N(){let e=useContext(Ke);if(!e)throw new Error("useKeyUtilsContext must be used within a KeyUtilsProvider");return e}function ee({config:e,callbacks:t={},children:r}){typeof e.apiBaseUrl!="string"&&console.error("[KeyUtils] apiBaseUrl must be a string");let s=useMemo(()=>({config:e,callbacks:t}),[e,t]);return jsx(Ke.Provider,{value:s,children:r})}function re(){let{config:e}=N(),[t,r]=useState(null),[s,g]=useState(true),[c,f]=useState(null),a=useRef(true),o=useRef(e.apiBaseUrl);o.current=e.apiBaseUrl;let d=async()=>{try{a.current&&(g(!0),f(null));let p=await fetch(`${o.current}/api/keys/pricing`);if(!p.ok){let i=`Pricing unavailable (${p.status})`;try{let n=await p.json();n.error&&(i=n.error);}catch{}throw new Error(i)}let u=await p.json();a.current&&r(u);}catch(p){a.current&&f(p instanceof Error?p.message:"Failed to fetch pricing");}finally{a.current&&g(false);}};return useEffect(()=>{a.current=true,d();let p=setInterval(d,6e4);return ()=>{a.current=false,clearInterval(p);}},[]),{pricing:t,loading:s,error:c,refresh:d}}function Je(e){return {id:e.id||"",key:e.key||e.keyPrefix||"",userId:e.userId||e.user_id||"",status:e.status||"active",tokenBalance:e.tokenBalance??e.currentTokenBalance??e.current_token_balance??0,maxTokens:e.maxTokens??e.initialTokenBalance??e.initial_token_balance??0,createdAt:String(e.createdAt??e.created_at??""),expiresAt:e.expiresAt,label:e.label||e.keyPrefix||e.key_prefix||void 0,paperWork:e.paperWork||e.paper_work||void 0,stackId:e.stackId,stackName:e.stackName}}function Q(){let{config:e}=N(),[t,r]=useState([]),[s,g]=useState(true),[c,f]=useState(null),a=useRef(true),o=e.apiBaseUrl,d=useCallback(async()=>{try{g(!0),f(null);let u=await fetch(`${o}/api/keys`);if(!u.ok){let l=`Failed to fetch keys (${u.status})`;try{let v=await u.json();v.error&&(l=v.error);}catch{}throw new Error(l)}let i=await u.json(),n=Array.isArray(i)?i:i.keys??[];a.current&&r(n.map(Je));}catch(u){a.current&&f(u instanceof Error?u.message:"Failed to fetch keys");}finally{a.current&&g(false);}},[o]);useEffect(()=>(a.current=true,d(),()=>{a.current=false;}),[d]);let p=t.reduce((u,i)=>u+i.tokenBalance,0);return {keys:t,totalBalance:p,loading:s,error:c,refresh:d}}function ne(e){let{config:t}=N(),[r,s]=useState([]),[g,c]=useState(false),[f,a]=useState(null),o=useRef(true),d=t.apiBaseUrl,p=useCallback(async()=>{if(e)try{c(!0),a(null);let u=await fetch(`${d}/api/keys/${e}/ledger`);if(!u.ok){let n=`Failed to fetch ledger (${u.status})`;try{let l=await u.json();l.error&&(n=l.error);}catch{}throw new Error(n)}let i=await u.json();o.current&&s(Array.isArray(i)?i:i.entries??[]);}catch(u){o.current&&a(u instanceof Error?u.message:"Failed to fetch ledger");}finally{o.current&&c(false);}},[d,e]);return useEffect(()=>(o.current=true,p(),()=>{o.current=false;}),[p]),{entries:r,loading:g,error:f,refresh:p}}function ie(){let{config:e,callbacks:t}=N(),[r,s]=useState(false),[g,c]=useState(null),[f,a]=useState(null);return {mint:useCallback(async(d,p,u=1)=>{try{s(!0),a(null),t.onPaymentComplete?.(d,p);let i=await fetch(`${e.apiBaseUrl}/api/keys/mint`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paymentMethod:d,paymentRef:p,quantity:u})});if(!i.ok){let P="Key generation failed";try{let w=await i.json();w.error&&(P=w.error);}catch{}throw new Error(P)}let n=await i.json(),l=n.keys?.[0]||n,v={success:!0,key:{id:l.keyId||l.id||"",key:l.key||"",userId:l.userId||"",status:"active",tokenBalance:l.tokenBalance??l.currentTokenBalance??0,maxTokens:l.initialTokenBalance??l.tokenBalance??0,createdAt:String(l.createdAt||Date.now())},transactionId:p};return c(v),t.onMintSuccess?.(v),v}catch(i){let n=i instanceof Error?i:new Error("Key generation failed");return a(n.message),t.onMintError?.(n),{success:false,error:n.message}}finally{s(false);}},[e.apiBaseUrl,t]),minting:r,result:g,error:f}}function ce(){let{config:e,callbacks:t}=N(),[r,s]=useState(false),[g,c]=useState(null),[f,a]=useState(null),o=useRef(true),d=useCallback(async(u,i)=>{try{s(!0),a(null);let n=await fetch(`${e.apiBaseUrl}/api/keys/${u}/list`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({askPriceCents:i,stackId:e.stackId,stackName:e.stackName})});if(!n.ok){let v=`Listing failed (${n.status})`;try{let P=await n.json();P.error&&(v=P.error);}catch{}throw new Error(v)}let l=await n.json();return o.current&&(c(l),t.onListingCreated?.(l)),l}catch(n){let l=n instanceof Error?n.message:"Listing failed";return o.current&&a(l),null}finally{o.current&&s(false);}},[e.apiBaseUrl,e.stackId,e.stackName,t]),p=useCallback(async u=>{try{s(!0),a(null);let i=await fetch(`${e.apiBaseUrl}/api/keys/${u}/list`,{method:"DELETE"});if(!i.ok){let n=`Delist failed (${i.status})`;try{let l=await i.json();l.error&&(n=l.error);}catch{}throw new Error(n)}return o.current&&c(null),!0}catch(i){let n=i instanceof Error?i.message:"Delist failed";return o.current&&a(n),false}finally{o.current&&s(false);}},[e.apiBaseUrl]);return {listKey:d,delistKey:p,listing:r,result:g,error:f}}function de(){let{config:e}=N(),[t,r]=useState(false),[s,g]=useState(null),c=useRef(true);return {createSession:useCallback(async(a,o)=>{try{r(!0),g(null);let d=await fetch(`${e.apiBaseUrl}/api/keys/stripe-session`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({priceCents:a,quantity:o,stackId:e.stackId})});if(!d.ok){let u=`Checkout failed (${d.status})`;try{let i=await d.json();i.error&&(u=i.error);}catch{}throw new Error(u)}let{url:p}=await d.json();if(p){try{sessionStorage.setItem("keyutils_stripe_pending","1");}catch{}window.location.href=p;}else throw new Error("No checkout URL returned")}catch(d){if(c.current){let p=d instanceof Error?d.message:"Checkout failed";g(p),r(false);}}},[e.apiBaseUrl,e.stackId]),loading:t,error:s}}var at=/^[1-9A-HJ-NP-Za-km-z]{32,44}$/;function fe({amountSol:e,onSuccess:t,onError:r,disabled:s,className:g,style:c}){let{config:f}=N(),[a,o]=useState(false),[d,p]=useState(null),u=!!(f.merchantWallet&&at.test(f.merchantWallet)),i=useCallback(async()=>{if(u)try{o(!0),p(null);let{Connection:n,PublicKey:l,Transaction:v,SystemProgram:P,LAMPORTS_PER_SOL:w}=await import('@solana/web3.js'),R=typeof window<"u"?window.solana:void 0;if(!R||typeof R!="object"||!R.isPhantom)throw new Error("Phantom wallet not found. Please install Phantom.");let B=R,M;try{M=new l(f.merchantWallet);}catch{throw new Error("Invalid merchant wallet address.")}let _=await B.connect(),k=new l(_.publicKey.toString()),W=new n(f.solanaRpcUrl||"https://api.mainnet-beta.solana.com"),{blockhash:A}=await W.getLatestBlockhash(),Z=BigInt(Math.floor(e*1e9)),X=new v({recentBlockhash:A,feePayer:k}).add(P.transfer({fromPubkey:k,toPubkey:M,lamports:Number(Z)})),{signature:K}=await B.signAndSendTransaction(X);t(K);}catch(n){let l=n instanceof Error?n:new Error("Payment failed");p(l.message),r?.(l);}finally{o(false);}},[e,f.merchantWallet,u,t,r]);return jsxs("div",{children:[jsx("button",{onClick:i,disabled:s||a||!u,"aria-label":`Pay ${e.toFixed(4)} SOL`,className:m("flex w-full items-center justify-center gap-2 rounded-lg bg-purple-600 px-4 py-3 font-medium text-white transition-colors hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed",g),style:c,children:a?jsxs(Fragment,{children:[jsx(Loader2,{className:"h-4 w-4 animate-spin"}),"Processing..."]}):jsxs(Fragment,{children:["Pay ",e.toFixed(4)," SOL"]})}),d&&jsx("p",{className:"mt-2 text-center text-xs text-red-500",children:d})]})}function pe({result:e,className:t,style:r}){let[s,g]=useState(false),c=useRef(null);useEffect(()=>()=>{c.current&&clearTimeout(c.current);},[]);let f=useCallback(()=>{e.key?.key&&(navigator.clipboard.writeText(e.key.key),g(true),c.current&&clearTimeout(c.current),c.current=setTimeout(()=>g(false),2e3));},[e.key?.key]);return !e.success||!e.key?null:jsxs("div",{className:m("rounded-lg border border-green-500/20 bg-green-500/10 p-6 text-center",t),style:r,children:[jsx(CheckCircle,{className:"mx-auto mb-3 h-12 w-12 text-green-500"}),jsx("h3",{className:"mb-2 text-lg font-semibold text-foreground",children:"Key Generated!"}),jsx("p",{className:"mb-4 text-sm text-muted-foreground",children:"Your node key has been successfully generated."}),jsxs("div",{className:"mx-auto flex max-w-md items-center gap-2 rounded-md border bg-background p-3",children:[jsx("code",{className:"flex-1 truncate text-xs text-foreground",children:e.key.key}),jsx("button",{onClick:f,"aria-label":"Copy key to clipboard",className:"shrink-0 rounded-md p-1.5 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",children:s?jsx(Check,{className:"h-4 w-4 text-green-500"}):jsx(Copy,{className:"h-4 w-4"})})]})]})}function I({className:e}){return jsx("div",{className:m("animate-pulse rounded-xl bg-muted",e)})}function ge(){return jsxs("div",{className:"space-y-6",children:[jsxs("div",{className:"px-6 py-10 flex flex-col items-center gap-3",children:[jsx(I,{className:"h-14 w-48"}),jsx(I,{className:"h-5 w-64"})]}),jsx(I,{className:"h-[72px] w-full rounded-2xl"}),jsx(I,{className:"h-[68px] w-full rounded-2xl"}),jsx(I,{className:"h-[60px] w-full rounded-2xl"})]})}function J({count:e=3}){return jsx("div",{className:"space-y-3",children:Array.from({length:e}).map((t,r)=>jsxs("div",{className:"rounded-xl border p-4 space-y-3",children:[jsxs("div",{className:"flex items-center justify-between",children:[jsx(I,{className:"h-4 w-32"}),jsx(I,{className:"h-4 w-16"})]}),jsx(I,{className:"h-2 w-full rounded-full"}),jsxs("div",{className:"flex justify-between",children:[jsx(I,{className:"h-3 w-24"}),jsx(I,{className:"h-3 w-20"})]})]},r))})}function kt({quantity:e,max:t,onChange:r}){return jsxs("div",{className:"flex items-center justify-between rounded-2xl border bg-muted/20 px-6 py-5",children:[jsx("span",{className:"text-sm font-medium text-foreground",children:"Quantity"}),jsxs("div",{className:"flex items-center gap-0 rounded-xl border bg-background",children:[jsx("button",{onClick:()=>r(Math.max(1,e-1)),disabled:e<=1,"aria-label":"Decrease quantity",className:"flex h-12 w-12 items-center justify-center rounded-l-xl text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-30",children:jsx(Minus,{className:"h-5 w-5"})}),jsx("span",{className:"flex h-12 w-14 items-center justify-center border-x text-xl font-bold text-foreground","aria-live":"polite",children:e}),jsx("button",{onClick:()=>r(Math.min(t,e+1)),disabled:e>=t,"aria-label":"Increase quantity",className:"flex h-12 w-12 items-center justify-center rounded-r-xl text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-30",children:jsx(Plus,{className:"h-5 w-5"})})]})]})}function Me({pricing:e,quantity:t}){let[r,s]=useState(false),g=e.priceUsd*t;return jsxs("div",{className:"rounded-2xl border bg-background",children:[jsxs("button",{onClick:()=>s(!r),"aria-expanded":r,className:"flex w-full items-center justify-between px-6 py-5 text-left",children:[jsxs("span",{className:"text-foreground",children:[jsxs("span",{className:"text-xl font-bold",children:["$",g.toFixed(2)]})," ",jsx("span",{className:"text-sm text-muted-foreground",children:"total fees included"})]}),r?jsx(ChevronUp,{className:"h-5 w-5 text-muted-foreground"}):jsx(ChevronDown,{className:"h-5 w-5 text-muted-foreground"})]}),r&&jsxs("div",{className:"border-t px-6 pb-8 pt-4 space-y-3 text-sm",children:[jsxs("div",{className:"flex justify-between",children:[jsxs("span",{className:"text-muted-foreground",children:["Node Key \xD7 ",t]}),jsxs("span",{className:"text-foreground",children:["$",(e.priceUsd*t).toFixed(2)]})]}),jsxs("div",{className:"flex justify-between",children:[jsx("span",{className:"text-muted-foreground",children:"Keys sold"}),jsx("span",{className:"text-foreground",children:e.keysSold})]}),e.daysUntilHalving>0&&jsxs("div",{className:"flex justify-between text-muted-foreground",children:[jsx("span",{children:"Next refresh"}),jsxs("span",{children:[e.daysUntilHalving," day",e.daysUntilHalving!==1?"s":""]})]}),jsxs("div",{className:"flex justify-between border-t pt-3 font-semibold",children:[jsx("span",{className:"text-foreground",children:"Total"}),jsxs("span",{className:"text-foreground",children:["$",g.toFixed(2)]})]})]})]})}function xe({className:e,style:t}){let{config:r}=N(),{pricing:s,loading:g,error:c,refresh:f}=re(),{mint:a,minting:o,result:d,error:p}=ie(),{createSession:u,loading:i,error:n}=de(),[l,v]=useState(1),[P,w]=useState(r.paymentMethods?.includes("stripe")?"stripe":"solana"),[R,B]=useState(0),[M,_]=useState(false),k=pt.useRef(l);k.current=l;let W=Math.max(1,r.maxQuantity||5),A=[...r.paymentMethods||["solana"]].sort((K,G)=>K==="stripe"?-1:G==="stripe"?1:0);if(useEffect(()=>{if(typeof window>"u")return;let G=new URLSearchParams(window.location.search).get("session_id");if(G&&/^cs_(test|live)_/.test(G)&&!M&&!d?.success){let ke=false;try{ke=sessionStorage.getItem("keyutils_stripe_pending")==="1";}catch{}if(!ke)return;try{sessionStorage.removeItem("keyutils_stripe_pending");}catch{}_(true);let we=new URL(window.location.href);we.searchParams.delete("session_id"),window.history.replaceState({},"",we.toString()),a("stripe",G,k.current);}},[M,d,a]),d?.success)return jsx("div",{className:m("flex flex-col items-center justify-center",e),style:t,children:jsx(pe,{result:d})});if(M||o)return jsx("div",{className:m("flex flex-col",e),style:t,children:jsx(ge,{})});if(g)return jsx("div",{className:m("flex flex-col",e),style:t,children:jsx(ge,{})});if(c||!s)return jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 py-24",e),style:t,children:[jsxs("div",{className:"rounded-xl border border-red-500/20 bg-red-500/5 px-6 py-5 text-center max-w-sm",children:[jsx("p",{className:"text-sm font-medium text-foreground",children:"Unable to load pricing"}),jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:c||"Please check your connection and try again."})]}),jsx("button",{onClick:()=>f(),className:"text-sm text-muted-foreground hover:text-foreground transition-colors",children:"Try again"})]});let Z=async K=>{B(2),await a(P,K,l);},X=()=>{u(s.priceCents,l);};return jsx("div",{className:m("flex flex-col",e),style:t,children:jsxs("div",{className:"space-y-6",children:[R===0&&jsxs(Fragment,{children:[r.keyImage&&jsx("img",{src:r.keyImage,alt:"Node Key",className:r.keyImageClass||"w-1/2 mx-auto"}),jsxs("div",{className:"px-6 py-10 text-center",children:[jsxs("p",{className:"text-5xl font-bold tracking-tight text-foreground md:text-6xl",children:["$",s.priceUsd.toFixed(2)]}),jsxs("p",{className:"mt-2 text-base text-muted-foreground",children:["per key \xB7 ",s.tokenAllocationFormatted||L(s.tokenAllocation)," tokens included"]})]}),jsx(kt,{quantity:l,max:W,onChange:v}),jsx(Me,{pricing:s,quantity:l}),jsx("button",{onClick:()=>B(1),className:"w-full rounded-2xl bg-foreground py-5 text-center text-base font-semibold text-background transition-colors hover:bg-foreground/90",children:"Continue to Payment"})]}),R===1&&jsxs(Fragment,{children:[A.length>1&&jsx("div",{role:"tablist",className:"flex gap-1 rounded-2xl bg-muted/50 p-1.5",children:A.map(K=>jsxs("button",{role:"tab","aria-selected":P===K,onClick:()=>w(K),className:m("flex flex-1 items-center justify-center gap-2.5 rounded-xl py-4 text-sm font-medium transition-colors",P===K?"bg-background text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:[K==="stripe"?jsx(CreditCard,{className:"h-5 w-5"}):jsx(Wallet,{className:"h-5 w-5"}),K==="stripe"?"Credit Card":"Crypto"]},K))}),jsx(Me,{pricing:s,quantity:l}),P==="stripe"?jsxs(Fragment,{children:[jsx("button",{onClick:X,disabled:i,className:"flex w-full items-center justify-center gap-2 rounded-2xl bg-foreground py-5 text-base font-semibold text-background transition-colors hover:bg-foreground/90 disabled:opacity-50",children:i?jsxs(Fragment,{children:[jsx(Loader2,{className:"h-5 w-5 animate-spin"}),"Redirecting to Stripe..."]}):jsxs(Fragment,{children:[jsx(CreditCard,{className:"h-5 w-5"}),"Pay with Card"]})}),n&&jsx("p",{className:"text-center text-sm text-red-500",children:n})]}):P==="solana"?jsx(fe,{amountSol:s.priceSol??0,onSuccess:Z,className:"rounded-2xl py-5 text-base"}):null,p&&jsx("p",{className:"text-center text-sm text-red-500",children:p}),jsx("button",{onClick:()=>B(0),className:"w-full py-3 text-center text-sm text-muted-foreground hover:text-foreground transition-colors",children:"\u2190 Back to Configure"})]})]})})}var St={ios:"iOS",android:"Android",windows:"Windows",mac:"macOS",linux:"Linux"},Kt=["mac","windows","linux","ios","android"];function ye({downloads:e,className:t,style:r}){let{config:s}=N(),g=useMemo(()=>Se(),[]),c=e||Kt.map(o=>({platform:o,label:St[o],url:"#",compatible:o===g})),f=c.find(o=>o.compatible)||c[0],a=c.filter(o=>o.platform!==f.platform);return jsxs("div",{className:m("space-y-8",t),style:r,children:[jsxs("div",{className:"flex flex-col items-center gap-6 pt-4 mt-4",children:[s.aispImage&&jsx("img",{src:s.aispImage,alt:"aiSP",className:"w-full sm:w-1/2 mx-auto px-4 sm:px-0"}),jsxs("div",{className:"text-center",children:[jsx("p",{className:"text-lg sm:text-2xl font-medium text-foreground",children:"Download the AI Service Provider App"}),jsx("p",{className:"mt-1 text-sm sm:text-base text-muted-foreground",children:"Load your keys and run your node."})]}),jsxs("a",{href:f.url,className:"inline-flex items-center gap-3 rounded-full px-10 py-4 text-lg font-semibold text-white transition-colors hover:opacity-90",style:{backgroundColor:"#0900f4"},children:[jsx(Download,{className:"h-5 w-5"}),"Get aiSP for ",f.label]})]}),jsxs("p",{className:"text-center text-sm text-muted-foreground",children:["aiSP is also available on"," ",a.map((o,d)=>jsxs(pt.Fragment,{children:[d>0&&(d===a.length-1?" and ":", "),jsx("a",{href:o.url,className:"font-medium underline transition-colors hover:text-foreground",style:{color:"#0900f4"},children:o.label})]},o.platform)),"."]}),jsxs("div",{className:"flex flex-col items-center gap-2 mt-12",children:[jsxs("p",{className:"text-center text-xs text-muted-foreground flex justify-center gap-0.5 flex-wrap",children:["Source code is available on",jsx("a",{href:"https://github.com/stack-net",target:"_blank",rel:"noopener noreferrer",className:"text-foreground underline hover:text-foreground/80 color-[#0900f4]",children:"Github"})]}),jsxs("p",{className:"text-center text-xs text-muted-foreground flex justify-center gap-0.5 flex-wrap",children:["By downloading, you agree to the",jsx("a",{href:s.termsUrl||"/terms",className:"text-foreground underline hover:text-foreground/80",children:"Open Source Applications Terms"})]})]})]})}function Y({current:e,max:t,className:r,style:s,showLabel:g=true}){let c=Pe(e,t),f=Ce(c);return jsxs("div",{className:m("w-full",r),style:s,children:[g&&jsxs("div",{className:"mb-1 flex items-center justify-between text-xs text-muted-foreground",children:[jsxs("span",{children:[L(e)," / ",L(t)]}),jsxs("span",{children:[c,"%"]})]}),jsx("div",{className:"h-2 w-full overflow-hidden rounded-full bg-muted",children:jsx("div",{className:m("h-full rounded-full transition-all duration-500",f),style:{width:`${c}%`}})})]})}function Lt({nodeKey:e,selected:t,onSelect:r}){return jsxs("button",{onClick:r,className:m("flex w-full items-start gap-3 rounded-xl border p-4 text-left transition-all",t?"border-foreground/30 bg-foreground/5 ring-1 ring-foreground/10":"border-border hover:border-foreground/20"),children:[jsx("div",{className:m("mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full border-2 transition-colors",t?"border-foreground bg-foreground":"border-muted-foreground/40"),children:t&&jsx(Check,{className:"h-3 w-3 text-background"})}),jsxs("div",{className:"min-w-0 flex-1",children:[jsxs("div",{className:"flex items-center justify-between",children:[jsx("span",{className:"text-sm font-medium text-foreground",children:e.label||e.id.slice(0,16)}),jsx("span",{className:m("text-xs font-medium capitalize",e.status==="active"?"text-green-500":"text-muted-foreground"),children:e.status})]}),jsx(Y,{current:e.tokenBalance,max:e.maxTokens,className:"mt-2"})]})]})}function he({className:e,style:t}){let{config:r}=N(),{keys:s,loading:g,error:c,refresh:f}=Q(),{listKey:a,listing:o,result:d,error:p}=ce(),[u,i]=useState(null),[n,l]=useState(""),[v,P]=useState(false),w=s.find(k=>k.id===u),R=parseInt(n.replace(/[^0-9]/g,""),10)||0,B=w&&R>0&&v&&!o,M=async()=>{if(!B||!w)return;let k=R*100;await a(w.id,k)&&f();};if(d)return jsxs("div",{className:m("flex flex-col items-center gap-4 rounded-xl border border-green-500/20 bg-green-500/10 py-12",e),style:t,children:[jsx(CheckCircle,{className:"h-12 w-12 text-green-500"}),jsx("h3",{className:"text-lg font-semibold text-foreground",children:"Key Listed!"}),jsx("p",{className:"text-sm text-muted-foreground",children:"Your key is now on the Global Open AI Network marketplace."}),jsxs("p",{className:"text-xs text-muted-foreground",children:["Listing ID: ",jsx("code",{children:d.listingId})]})]});if(g)return jsxs("div",{className:m("space-y-5 px-1",e),style:t,children:[jsx("h3",{className:"text-2xl mt-3 mb-2 font-semibold text-foreground",children:"Your Keys"}),jsx(J,{count:2})]});if(c)return jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 py-16",e),style:t,children:[jsxs("div",{className:"rounded-xl border border-red-500/20 bg-red-500/5 px-6 py-5 text-center max-w-sm",children:[jsx("p",{className:"text-sm font-medium text-foreground",children:"Unable to load your keys"}),jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:c})]}),jsx("button",{onClick:()=>f(),className:"text-sm text-muted-foreground hover:text-foreground transition-colors",children:"Try again"})]});let _=s.filter(k=>k.status==="active");return _.length===0?jsxs("div",{className:m("flex flex-col items-center justify-center gap-3 rounded-xl border border-dashed py-16",e),style:t,children:[jsx(AlertTriangle,{className:"h-8 w-8 text-muted-foreground"}),jsx("p",{className:"text-sm text-muted-foreground",children:"No active keys to sell."})]}):jsxs("div",{className:m("space-y-5",e),style:t,children:[jsxs("div",{children:[jsx("h3",{className:"text-2xl mt-3 mb-2 font-semibold text-foreground",children:"Your Keys"}),jsx("div",{className:"space-y-2",children:_.map(k=>jsx(Lt,{nodeKey:k,selected:u===k.id,onSelect:()=>i(k.id)},k.id))})]}),w&&jsxs("div",{className:"rounded-xl border bg-muted/30 p-5 space-y-2 text-sm",children:[jsxs("div",{className:"flex justify-between",children:[jsx("span",{className:"text-muted-foreground",children:"Key ID"}),jsx("code",{className:"text-xs text-foreground",children:w.id})]}),jsxs("div",{className:"flex justify-between",children:[jsx("span",{className:"text-muted-foreground",children:"Originating Stack"}),jsx("span",{className:"text-foreground",children:r.stackName||"Unknown"})]}),jsxs("div",{className:"flex justify-between",children:[jsx("span",{className:"text-muted-foreground",children:"Tokens (remaining / total)"}),jsxs("span",{className:"text-foreground",children:[L(w.tokenBalance)," / ",L(w.maxTokens)]})]}),w.paperWork&&jsxs("div",{className:"flex justify-between",children:[jsx("span",{className:"text-muted-foreground",children:"Paper earned"}),jsx("span",{className:"text-foreground",children:w.paperWork})]}),jsxs("div",{className:"flex justify-between",children:[jsx("span",{className:"text-muted-foreground",children:"Created"}),jsx("span",{className:"text-foreground",children:F(w.createdAt)})]})]}),jsxs("div",{children:[jsx("label",{htmlFor:"listing-price",className:"mb-2 block text-sm font-semibold text-foreground",children:"Listing Price (USD)"}),jsxs("div",{className:"relative",children:[jsx("span",{className:"absolute left-4 top-1/2 -translate-y-1/2 text-2xl font-bold text-muted-foreground",children:"$"}),jsx("input",{id:"listing-price",type:"text",inputMode:"numeric",value:n,onChange:k=>{let W=k.target.value.replace(/[^0-9]/g,""),A=parseInt(W,10);l(isNaN(A)?"":A.toLocaleString("en-US"));},placeholder:"0",className:"w-full rounded-xl border bg-background py-5 pl-10 pr-4 text-3xl font-bold text-foreground placeholder:text-muted-foreground/30 focus:outline-none focus:ring-2 focus:ring-foreground/20"})]})]}),jsxs("label",{className:"flex items-start gap-3 cursor-pointer",children:[jsx("input",{type:"checkbox",checked:v,onChange:k=>P(k.target.checked),className:"sr-only"}),jsx("div",{"aria-hidden":"true",className:m("mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded border-2 transition-colors",v?"border-foreground bg-foreground":"border-muted-foreground/40"),children:v&&jsx(Check,{className:"h-3 w-3 text-background"})}),jsxs("span",{className:"text-xs text-muted-foreground leading-relaxed",children:["I agree to list this key on the Global Open AI Network marketplace. The key will be deactivated upon sale and transferred to the buyer. Listing fees may apply."," ",jsx("a",{href:r.termsUrl||"/terms",className:"text-foreground underline",children:"Terms"})]})]}),p&&jsx("p",{className:"text-sm text-red-500",children:p}),jsxs("button",{onClick:M,disabled:!B,className:m("flex w-full items-center justify-center gap-2 rounded-xl py-4 text-sm font-semibold transition-colors",B?"bg-foreground text-background hover:bg-foreground/90":"bg-muted text-muted-foreground cursor-not-allowed"),children:[jsx(ExternalLink,{className:"h-4 w-4"}),o?"Listing...":"List on Marketplace"]})]})}var Et=[{id:"buy",label:"Buy"},{id:"use",label:"Use"},{id:"sell",label:"Sell"}];function It({active:e,onChange:t}){return jsx("div",{role:"tablist","aria-label":"Key actions",className:"flex gap-1 rounded-2xl bg-muted/50 p-1.5",children:Et.map(({id:r,label:s})=>jsx("button",{role:"tab","aria-selected":e===r,"aria-controls":`panel-${r}`,id:`tab-${r}`,onClick:()=>t(r),className:m("flex flex-1 items-center justify-center gap-2.5 rounded-xl py-3.5 text-sm font-medium transition-all",e===r?"bg-background text-foreground shadow-sm":"text-muted-foreground hover:text-foreground"),children:jsx("span",{className:"inline",children:s})},r))})}function Mt({defaultTab:e="buy",className:t,style:r}){let[s,g]=useState(e);return jsxs("div",{className:m("flex flex-col",t),style:r,children:[jsx("div",{className:"sticky top-0 z-10 bg-background/80 px-5 pb-3 pt-2 sm:pt-5 backdrop-blur-sm sm:px-10 md:px-16 lg:px-20",children:jsx("div",{className:"mx-auto max-w-xl",children:jsx(It,{active:s,onChange:g})})}),jsx("div",{className:"flex flex-1 flex-col px-5 pb-8 sm:px-10 md:px-16 md:pb-12 lg:px-20",children:jsx("div",{className:"mx-auto w-full max-w-xl flex-1",children:jsxs("div",{role:"tabpanel",id:`panel-${s}`,"aria-labelledby":`tab-${s}`,children:[s==="buy"&&jsx(xe,{className:"flex-1"}),s==="use"&&jsx(ye,{className:"flex-1"}),s==="sell"&&jsx(he,{className:"flex-1"})]})})})]})}function jt({apiBaseUrl:e,stackId:t,stackName:r,paymentMethods:s,merchantWallet:g,stripePublicKey:c,theme:f,maxQuantity:a,keyImage:o,keyImageClass:d,aispImage:p,termsUrl:u,onMintSuccess:i,onMintError:n,onPaymentStart:l,onPaymentComplete:v,onListingCreated:P,defaultTab:w,className:R,style:B}){return jsx(ee,{config:{apiBaseUrl:e,stackId:t,stackName:r,paymentMethods:s,merchantWallet:g,stripePublicKey:c,theme:f,maxQuantity:a,keyImage:o,keyImageClass:d,aispImage:p,termsUrl:u},callbacks:{onMintSuccess:i,onMintError:n,onPaymentStart:l,onPaymentComplete:v,onListingCreated:P},children:jsx(Mt,{defaultTab:w,className:R,style:B})})}var Ne=20;function ve({nodeKey:e,className:t,style:r}){let[s,g]=useState(false),[c,f]=useState(false),a=useRef(null),{entries:o,loading:d}=ne(s?e.id:null);useEffect(()=>()=>{a.current&&clearTimeout(a.current);},[]);let p=useCallback(()=>{e.key&&(navigator.clipboard.writeText(e.key),f(true),a.current&&clearTimeout(a.current),a.current=setTimeout(()=>f(false),2e3));},[e.key]),u=e.status==="active"?"text-green-500":e.status==="expired"?"text-red-500":"text-yellow-500",i=o.slice(0,Ne);return jsxs("div",{className:m("rounded-lg border bg-background",t),style:r,children:[jsxs("div",{className:"p-4",children:[jsxs("div",{className:"mb-3 flex items-center justify-between",children:[jsxs("div",{className:"flex items-center gap-2",children:[jsx("button",{onClick:()=>g(!s),"aria-expanded":s,"aria-label":s?"Collapse ledger":"Expand ledger",className:"text-muted-foreground hover:text-foreground",children:s?jsx(ChevronDown,{className:"h-4 w-4"}):jsx(ChevronRight,{className:"h-4 w-4"})}),jsx("span",{className:"text-sm font-medium text-foreground",children:e.label||`Key ${e.id.slice(0,8)}`})]}),jsx("span",{className:m("text-xs font-medium capitalize",u),children:e.status})]}),e.key&&jsxs("div",{className:"mb-3 flex items-center gap-2",children:[jsx("code",{className:"flex-1 truncate rounded bg-muted px-2 py-1 text-xs text-muted-foreground",children:e.key}),jsx("button",{onClick:p,"aria-label":"Copy key to clipboard",className:"shrink-0 rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",children:c?jsx(Check,{className:"h-3.5 w-3.5 text-green-500"}):jsx(Copy,{className:"h-3.5 w-3.5"})})]}),jsx(Y,{current:e.tokenBalance,max:e.maxTokens}),jsxs("div",{className:"mt-2 flex items-center justify-between text-xs text-muted-foreground",children:[jsxs("span",{children:["Created ",F(e.createdAt)]}),e.expiresAt&&jsxs("span",{children:["Expires ",F(e.expiresAt)]})]})]}),s&&jsxs("div",{className:"border-t px-4 py-3",children:[jsx("h4",{className:"mb-2 text-xs font-medium text-muted-foreground",children:"Ledger"}),d?jsx("p",{className:"text-xs text-muted-foreground",children:"Loading..."}):i.length===0?jsx("p",{className:"text-xs text-muted-foreground",children:"No ledger entries."}):jsxs("div",{className:"space-y-1.5",children:[i.map(n=>jsxs("div",{className:"flex items-center justify-between text-xs",children:[jsxs("div",{className:"flex items-center gap-2",children:[jsxs("span",{className:m("font-medium",n.type==="credit"||n.type==="reward"?"text-green-500":"text-red-500"),children:[n.type==="credit"||n.type==="reward"?"+":"-",L(Math.abs(n.amount))]}),jsx("span",{className:"text-muted-foreground",children:n.description})]}),jsx("span",{className:"text-muted-foreground",children:F(n.createdAt)})]},n.id)),o.length>Ne&&jsxs("p",{className:"pt-1 text-xs text-muted-foreground",children:["Showing ",Ne," of ",o.length," entries"]})]})]})]})}function Qt({getKeyHref:e="/keys/get",className:t,style:r}){let{keys:s,totalBalance:g,loading:c,error:f,refresh:a}=Q();return c?jsx("div",{className:m("space-y-4",t),style:r,children:jsx(J,{count:3})}):f?jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 py-12",t),style:r,children:[jsxs("div",{className:"rounded-xl border border-red-500/20 bg-red-500/5 px-6 py-5 text-center max-w-sm",children:[jsx("p",{className:"text-sm font-medium text-foreground",children:"Unable to load your keys"}),jsx("p",{className:"mt-1 text-xs text-muted-foreground",children:f})]}),jsx("button",{onClick:()=>a(),className:"text-sm text-muted-foreground hover:text-foreground transition-colors",children:"Try again"})]}):s.length===0?jsxs("div",{className:m("flex flex-col items-center justify-center gap-4 rounded-lg border border-dashed py-12",t),style:r,children:[jsx(KeyRound,{className:"h-10 w-10 text-muted-foreground"}),jsxs("div",{className:"text-center",children:[jsx("p",{className:"text-sm font-medium text-foreground",children:"No keys yet"}),jsx("p",{className:"text-xs text-muted-foreground",children:"Get your first node key to start earning."})]}),e&&jsx("a",{href:e,className:"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background transition-colors hover:bg-foreground/90",children:"Get a Key"})]}):jsxs("div",{className:m("space-y-4",t),style:r,children:[jsxs("div",{className:"flex items-center justify-between",children:[jsxs("p",{className:"text-sm text-muted-foreground",children:[s.length," key",s.length!==1?"s":""," \xB7 Total balance: ",L(g)]}),e&&jsx("a",{href:e,className:"rounded-md bg-foreground px-3 py-1.5 text-xs font-medium text-background transition-colors hover:bg-foreground/90",children:"Get a Key"})]}),s.map(o=>jsx(ve,{nodeKey:o},o.id))]})}
2
+ export{jt as BuyKeyWidget,xe as BuyPanel,Y as KeyBalanceBar,ve as KeyCard,Qt as KeyList,ee as KeyUtilsProvider,pe as MintSuccess,he as SellPanel,fe as SolanaPayButton,ye as UsePanel,Pe as calculateBalancePercentage,m as cn,Se as detectPlatform,F as formatDate,L as formatTokens,Ce as getBalanceColor,ne as useKeyLedger,N as useKeyUtilsContext,Q as useKeys,ce as useListKey,ie as useMintKey,re as usePricing,de as useStripeCheckout};
@@ -0,0 +1 @@
1
+ 'use strict';
@@ -0,0 +1,97 @@
1
+ interface NodeKeyInfo {
2
+ id: string;
3
+ key: string;
4
+ userId: string;
5
+ status: 'active' | 'inactive' | 'expired' | 'closed' | 'listed';
6
+ tokenBalance: number;
7
+ maxTokens: number;
8
+ createdAt: string;
9
+ expiresAt?: string;
10
+ label?: string;
11
+ paperWork?: string;
12
+ stackId?: string;
13
+ stackName?: string;
14
+ }
15
+ interface LedgerEntry {
16
+ id: string;
17
+ keyId: string;
18
+ type: 'credit' | 'debit' | 'mint' | 'reward';
19
+ amount: number;
20
+ description: string;
21
+ createdAt: string;
22
+ metadata?: Record<string, unknown>;
23
+ }
24
+ interface PricingInfo {
25
+ priceUsd: number;
26
+ priceCents: number;
27
+ priceSol?: number;
28
+ tokenAllocation: number;
29
+ tokenAllocationFormatted: string;
30
+ currentDay: number;
31
+ currentEpoch: number;
32
+ nextHalvingDate: number;
33
+ daysUntilHalving: number;
34
+ keysSold: number;
35
+ saleActive: boolean;
36
+ saleStartDate: number;
37
+ }
38
+ interface MintResult {
39
+ success: boolean;
40
+ key?: NodeKeyInfo;
41
+ transactionId?: string;
42
+ error?: string;
43
+ }
44
+ type PaymentMethod = 'solana' | 'stripe';
45
+ interface KeyListing {
46
+ id: string;
47
+ keyId: string;
48
+ sellerId: string;
49
+ askPriceCents: number;
50
+ currency: string;
51
+ tokenBalance: number;
52
+ maxTokens: number;
53
+ paperWork?: string;
54
+ stackId?: string;
55
+ stackName?: string;
56
+ status: 'active' | 'sold' | 'cancelled';
57
+ createdAt: number;
58
+ }
59
+ interface ListingResult {
60
+ listingId: string;
61
+ keyId: string;
62
+ askPriceCents: number;
63
+ currency: string;
64
+ status: string;
65
+ }
66
+ type Platform = 'ios' | 'android' | 'windows' | 'mac' | 'linux';
67
+ interface PlatformDownload {
68
+ platform: Platform;
69
+ label: string;
70
+ url: string;
71
+ compatible: boolean;
72
+ }
73
+ type KeyWidgetTab = 'buy' | 'use' | 'sell';
74
+ interface KeyUtilsConfig {
75
+ apiBaseUrl: string;
76
+ stackId?: string;
77
+ stackName?: string;
78
+ paymentMethods?: PaymentMethod[];
79
+ merchantWallet?: string;
80
+ stripePublicKey?: string;
81
+ theme?: 'light' | 'dark' | 'system';
82
+ maxQuantity?: number;
83
+ keyImage?: string;
84
+ keyImageClass?: string;
85
+ aispImage?: string;
86
+ termsUrl?: string;
87
+ solanaRpcUrl?: string;
88
+ }
89
+ interface KeyUtilsCallbacks {
90
+ onMintSuccess?: (result: MintResult) => void;
91
+ onMintError?: (error: Error) => void;
92
+ onPaymentStart?: (method: PaymentMethod) => void;
93
+ onPaymentComplete?: (method: PaymentMethod, transactionId: string) => void;
94
+ onListingCreated?: (listing: ListingResult) => void;
95
+ }
96
+
97
+ export type { KeyListing, KeyUtilsCallbacks, KeyUtilsConfig, KeyWidgetTab, LedgerEntry, ListingResult, MintResult, NodeKeyInfo, PaymentMethod, Platform, PlatformDownload, PricingInfo };
@@ -0,0 +1,97 @@
1
+ interface NodeKeyInfo {
2
+ id: string;
3
+ key: string;
4
+ userId: string;
5
+ status: 'active' | 'inactive' | 'expired' | 'closed' | 'listed';
6
+ tokenBalance: number;
7
+ maxTokens: number;
8
+ createdAt: string;
9
+ expiresAt?: string;
10
+ label?: string;
11
+ paperWork?: string;
12
+ stackId?: string;
13
+ stackName?: string;
14
+ }
15
+ interface LedgerEntry {
16
+ id: string;
17
+ keyId: string;
18
+ type: 'credit' | 'debit' | 'mint' | 'reward';
19
+ amount: number;
20
+ description: string;
21
+ createdAt: string;
22
+ metadata?: Record<string, unknown>;
23
+ }
24
+ interface PricingInfo {
25
+ priceUsd: number;
26
+ priceCents: number;
27
+ priceSol?: number;
28
+ tokenAllocation: number;
29
+ tokenAllocationFormatted: string;
30
+ currentDay: number;
31
+ currentEpoch: number;
32
+ nextHalvingDate: number;
33
+ daysUntilHalving: number;
34
+ keysSold: number;
35
+ saleActive: boolean;
36
+ saleStartDate: number;
37
+ }
38
+ interface MintResult {
39
+ success: boolean;
40
+ key?: NodeKeyInfo;
41
+ transactionId?: string;
42
+ error?: string;
43
+ }
44
+ type PaymentMethod = 'solana' | 'stripe';
45
+ interface KeyListing {
46
+ id: string;
47
+ keyId: string;
48
+ sellerId: string;
49
+ askPriceCents: number;
50
+ currency: string;
51
+ tokenBalance: number;
52
+ maxTokens: number;
53
+ paperWork?: string;
54
+ stackId?: string;
55
+ stackName?: string;
56
+ status: 'active' | 'sold' | 'cancelled';
57
+ createdAt: number;
58
+ }
59
+ interface ListingResult {
60
+ listingId: string;
61
+ keyId: string;
62
+ askPriceCents: number;
63
+ currency: string;
64
+ status: string;
65
+ }
66
+ type Platform = 'ios' | 'android' | 'windows' | 'mac' | 'linux';
67
+ interface PlatformDownload {
68
+ platform: Platform;
69
+ label: string;
70
+ url: string;
71
+ compatible: boolean;
72
+ }
73
+ type KeyWidgetTab = 'buy' | 'use' | 'sell';
74
+ interface KeyUtilsConfig {
75
+ apiBaseUrl: string;
76
+ stackId?: string;
77
+ stackName?: string;
78
+ paymentMethods?: PaymentMethod[];
79
+ merchantWallet?: string;
80
+ stripePublicKey?: string;
81
+ theme?: 'light' | 'dark' | 'system';
82
+ maxQuantity?: number;
83
+ keyImage?: string;
84
+ keyImageClass?: string;
85
+ aispImage?: string;
86
+ termsUrl?: string;
87
+ solanaRpcUrl?: string;
88
+ }
89
+ interface KeyUtilsCallbacks {
90
+ onMintSuccess?: (result: MintResult) => void;
91
+ onMintError?: (error: Error) => void;
92
+ onPaymentStart?: (method: PaymentMethod) => void;
93
+ onPaymentComplete?: (method: PaymentMethod, transactionId: string) => void;
94
+ onListingCreated?: (listing: ListingResult) => void;
95
+ }
96
+
97
+ export type { KeyListing, KeyUtilsCallbacks, KeyUtilsConfig, KeyWidgetTab, LedgerEntry, ListingResult, MintResult, NodeKeyInfo, PaymentMethod, Platform, PlatformDownload, PricingInfo };
File without changes
@@ -0,0 +1 @@
1
+ 'use strict';var clsx=require('clsx'),tailwindMerge=require('tailwind-merge');function f(...r){return tailwindMerge.twMerge(clsx.clsx(r))}function a(r){return r==null?"0":r>=1e9?`${(r/1e9).toFixed(1)}B`:r>=1e6?`${(r/1e6).toFixed(1)}M`:r>=1e3?`${(r/1e3).toFixed(1)}K`:r.toLocaleString()}function s(r,e){return e<=0?0:Math.min(100,Math.round(r/e*100))}function l(r){return r>=70?"bg-green-500":r>=30?"bg-yellow-500":"bg-red-500"}function c(r){let e=typeof r=="string"&&/^\d+$/.test(r)?Number(r):r,t=new Date(e);return isNaN(t.getTime())?String(r):t.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}function m(){if(typeof navigator>"u")return "mac";let r=navigator.userAgent.toLowerCase();return /iphone|ipad|ipod/.test(r)?"ios":/android/.test(r)?"android":/win/.test(r)?"windows":/linux/.test(r)?"linux":"mac"}exports.calculateBalancePercentage=s;exports.cn=f;exports.detectPlatform=m;exports.formatDate=c;exports.formatTokens=a;exports.getBalanceColor=l;
@@ -0,0 +1,11 @@
1
+ import { ClassValue } from 'clsx';
2
+ import { Platform } from '../types/index.cjs';
3
+
4
+ declare function cn(...inputs: ClassValue[]): string;
5
+ declare function formatTokens(amount: number | undefined | null): string;
6
+ declare function calculateBalancePercentage(current: number, max: number): number;
7
+ declare function getBalanceColor(percentage: number): string;
8
+ declare function formatDate(dateString: string | number): string;
9
+ declare function detectPlatform(): Platform;
10
+
11
+ export { calculateBalancePercentage, cn, detectPlatform, formatDate, formatTokens, getBalanceColor };
@@ -0,0 +1,11 @@
1
+ import { ClassValue } from 'clsx';
2
+ import { Platform } from '../types/index.js';
3
+
4
+ declare function cn(...inputs: ClassValue[]): string;
5
+ declare function formatTokens(amount: number | undefined | null): string;
6
+ declare function calculateBalancePercentage(current: number, max: number): number;
7
+ declare function getBalanceColor(percentage: number): string;
8
+ declare function formatDate(dateString: string | number): string;
9
+ declare function detectPlatform(): Platform;
10
+
11
+ export { calculateBalancePercentage, cn, detectPlatform, formatDate, formatTokens, getBalanceColor };
@@ -0,0 +1 @@
1
+ import {clsx}from'clsx';import {twMerge}from'tailwind-merge';function f(...r){return twMerge(clsx(r))}function a(r){return r==null?"0":r>=1e9?`${(r/1e9).toFixed(1)}B`:r>=1e6?`${(r/1e6).toFixed(1)}M`:r>=1e3?`${(r/1e3).toFixed(1)}K`:r.toLocaleString()}function s(r,e){return e<=0?0:Math.min(100,Math.round(r/e*100))}function l(r){return r>=70?"bg-green-500":r>=30?"bg-yellow-500":"bg-red-500"}function c(r){let e=typeof r=="string"&&/^\d+$/.test(r)?Number(r):r,t=new Date(e);return isNaN(t.getTime())?String(r):t.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}function m(){if(typeof navigator>"u")return "mac";let r=navigator.userAgent.toLowerCase();return /iphone|ipad|ipod/.test(r)?"ios":/android/.test(r)?"android":/win/.test(r)?"windows":/linux/.test(r)?"linux":"mac"}export{s as calculateBalancePercentage,f as cn,m as detectPlatform,c as formatDate,a as formatTokens,l as getBalanceColor};
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "@stacknet/keyutils",
3
+ "version": "0.1.0",
4
+ "description": "Reusable components for buying and managing StackNet node keys",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./components": {
16
+ "types": "./dist/components/index.d.ts",
17
+ "import": "./dist/components/index.js",
18
+ "require": "./dist/components/index.cjs"
19
+ },
20
+ "./hooks": {
21
+ "types": "./dist/hooks/index.d.ts",
22
+ "import": "./dist/hooks/index.js",
23
+ "require": "./dist/hooks/index.cjs"
24
+ },
25
+ "./types": {
26
+ "types": "./dist/types/index.d.ts",
27
+ "import": "./dist/types/index.js",
28
+ "require": "./dist/types/index.cjs"
29
+ },
30
+ "./utils": {
31
+ "types": "./dist/utils/index.d.ts",
32
+ "import": "./dist/utils/index.js",
33
+ "require": "./dist/utils/index.cjs"
34
+ }
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "README.md"
39
+ ],
40
+ "scripts": {
41
+ "build": "tsup",
42
+ "build:publish": "tsup --no-sourcemap --minify",
43
+ "dev": "tsup --watch",
44
+ "clean": "rm -rf dist",
45
+ "typecheck": "tsc --noEmit",
46
+ "prepublishOnly": "pnpm run clean && pnpm run typecheck && pnpm run build:publish",
47
+ "release": "pnpm run prepublishOnly && npm publish --access public",
48
+ "release:dry": "pnpm run prepublishOnly && npm pack --dry-run"
49
+ },
50
+ "dependencies": {
51
+ "class-variance-authority": "^0.7.0",
52
+ "clsx": "^2.1.1",
53
+ "tailwind-merge": "^2.5.4",
54
+ "lucide-react": "^0.460.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/react": "^19.0.0",
58
+ "@types/react-dom": "^19.0.0",
59
+ "react": "^19.0.0",
60
+ "react-dom": "^19.0.0",
61
+ "tsup": "^8.3.5",
62
+ "typescript": "^5.6.3"
63
+ },
64
+ "peerDependencies": {
65
+ "react": ">=18.0.0",
66
+ "react-dom": ">=18.0.0"
67
+ },
68
+ "optionalDependencies": {
69
+ "@solana/web3.js": "^1.95.0"
70
+ },
71
+ "keywords": [
72
+ "react",
73
+ "components",
74
+ "stacknet",
75
+ "node-keys",
76
+ "ui"
77
+ ],
78
+ "license": "MIT",
79
+ "repository": {
80
+ "type": "git"
81
+ }
82
+ }