paytaca-cli 0.8.1 → 0.8.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../src/web/page.ts"],"names":[],"mappings":"AAWA,wBAAgB,UAAU,IAAI,MAAM,CAsnBnC"}
1
+ {"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../../src/web/page.ts"],"names":[],"mappings":"AAWA,wBAAgB,UAAU,IAAI,MAAM,CAwpBnC"}
package/dist/web/page.js CHANGED
@@ -18,6 +18,7 @@ export function renderPage() {
18
18
  <style>
19
19
  *,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
20
20
  :root{
21
+ color-scheme:light;
21
22
  --sans:'Rubik',system-ui,-apple-system,'Segoe UI',sans-serif;
22
23
  --mono:'JetBrains Mono',ui-monospace,'SF Mono','Cascadia Code',monospace;
23
24
  --bg:#ecf3f3;
@@ -45,6 +46,7 @@ export function renderPage() {
45
46
  --on-ink:#fff;
46
47
  }
47
48
  [data-theme="dark"]{
49
+ color-scheme:dark;
48
50
  --bg:#273746;
49
51
  --surface:rgba(28,40,51,0.7);
50
52
  --surface-2:#1c2833;
@@ -102,9 +104,10 @@ body{font-family:var(--sans);background:var(--bg);color:var(--ink);font-size:14p
102
104
  .tx-toast a{font-family:var(--mono);font-size:10.5px;color:var(--accent);text-decoration:underline;text-underline-offset:2px}
103
105
  .tx-toast a:hover{color:var(--accent-strong)}
104
106
  .content{width:100%;max-width:1400px;margin:0 auto;padding:26px 30px 56px}
105
- .grid{display:grid;grid-template-columns:repeat(12,1fr);gap:16px}
107
+ .grid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:16px}
106
108
  .span-4{grid-column:span 4}.span-5{grid-column:span 5}.span-6{grid-column:span 6}.span-7{grid-column:span 7}.span-8{grid-column:span 8}.span-12{grid-column:span 12}
107
- .grid>div{display:flex;flex-direction:column}.grid>div>.card{flex:1}
109
+ .grid>div{display:flex;flex-direction:column;min-width:0}.grid>div>.card{flex:1;min-width:0}
110
+ .tbl-scroll{overflow-x:auto}
108
111
  .card{background:var(--surface);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid var(--line);border-radius:var(--r);padding:20px;box-shadow:var(--shadow),var(--glass-hi)}
109
112
  .card-head{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:18px}
110
113
  .card-title{font-family:var(--mono);font-size:10.5px;font-weight:600;letter-spacing:0.13em;text-transform:uppercase;color:var(--ink-2)}
@@ -172,7 +175,8 @@ input[type="number"]{-moz-appearance:textfield}input::-webkit-outer-spin-button,
172
175
  .tbl tbody tr:hover{background:var(--surface-2)}
173
176
  .tbl tr:last-child td{border-bottom:none}
174
177
  .tbl-num{font-family:var(--mono);font-variant-numeric:tabular-nums;font-size:12px}
175
- .hist-date a{color:var(--ink-2);text-decoration:none;border-bottom:1px dotted var(--line-2);font-family:var(--mono);font-size:11px}
178
+ .hist-date-text{font-family:var(--mono);font-size:11px;color:var(--ink-2)}
179
+ .hist-date a{color:var(--ink-3);text-decoration:none;border-bottom:1px dotted var(--line-2);font-family:var(--mono);font-size:10px}
176
180
  .hist-date a:hover{color:var(--accent-strong);border-bottom-color:var(--accent)}.hist-addr{max-width:210px}
177
181
  .hist-sub{font-family:var(--mono);font-size:10px;color:var(--ink-3);margin-top:2px}
178
182
  .hist-amt{font-family:var(--mono);font-size:12px;font-variant-numeric:tabular-nums}
@@ -187,6 +191,11 @@ input[type="number"]{-moz-appearance:textfield}input::-webkit-outer-spin-button,
187
191
  .pill-wait{background:rgba(255,193,7,0.12);color:#d4a017}
188
192
  [data-theme="dark"] .pill-ok{background:rgba(40,167,69,0.18);color:#4ade80}
189
193
  [data-theme="dark"] .pill-wait{background:rgba(255,193,7,0.18);color:#fbbf24}
194
+ [data-theme="dark"] .field-input{background:rgba(255,255,255,0.06);border-color:rgba(255,255,255,0.2)}
195
+ [data-theme="dark"] .field-input:focus{background:rgba(255,255,255,0.09)}
196
+ [data-theme="dark"] .segmented,[data-theme="dark"] .seg{border-color:rgba(255,255,255,0.2)}
197
+ [data-theme="dark"] .segmented-opt.active{background:rgba(255,255,255,0.12)}
198
+ [data-theme="dark"] .seg button{background:rgba(255,255,255,0.08)}
190
199
  .pagination{display:flex;align-items:center;justify-content:center;gap:14px;margin-top:18px}
191
200
  .pagination .page-info{font-family:var(--mono);font-size:11px;color:var(--ink-3)}
192
201
  .empty{padding:30px 12px;text-align:center;font-family:var(--mono);font-size:11.5px;letter-spacing:0.02em;color:var(--ink-3)}
@@ -200,11 +209,23 @@ input[type="number"]{-moz-appearance:textfield}input::-webkit-outer-spin-button,
200
209
  .plan-group:first-child{padding-top:2px}
201
210
  .plan-group:last-child{border-bottom:0;padding-bottom:2px}
202
211
  .plan-model{font-size:13.5px;font-weight:600;margin-bottom:12px}
203
- .plan-tiers{display:flex;gap:10px;flex-wrap:wrap}
204
- .plan-tier{display:flex;align-items:center;gap:14px;border:1px solid var(--line);border-radius:var(--r-sm);padding:9px 14px;background:var(--surface-3)}
205
- .plan-tier:hover{border-color:var(--accent);transform:translateY(-1px);transition:all .2s ease}
212
+ .plan-slider{margin:2px 0 0}
213
+ .plan-slider input[type="range"]{-webkit-appearance:none;appearance:none;display:block;width:100%;height:4px;border-radius:2px;background:var(--line);outline:none;cursor:pointer;margin:0}
214
+ .plan-slider input[type="range"]::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:16px;height:16px;border-radius:50%;background:var(--accent);border:2px solid var(--surface);box-shadow:0 1px 4px rgba(0,0,0,0.25);cursor:pointer}
215
+ .plan-slider input[type="range"]::-moz-range-thumb{width:12px;height:12px;border-radius:50%;background:var(--accent);border:2px solid var(--surface);box-shadow:0 1px 4px rgba(0,0,0,0.25);cursor:pointer}
216
+ .plan-slider input[type="range"]::-moz-range-track{background:transparent}
217
+ .plan-ticks{display:flex;justify-content:space-between;padding:7px 8px 0}
218
+ .plan-ticks span{width:0;display:flex;flex-direction:column;align-items:center;white-space:nowrap;font-family:var(--mono);font-size:9.5px;letter-spacing:0.04em;color:var(--ink-3)}
219
+ .plan-ticks span::before{content:'';width:1px;height:4px;background:var(--line);margin-bottom:3px}
220
+ .plan-selection{display:flex;align-items:center;gap:12px;margin-top:12px;min-height:29px}
221
+ .plan-selection .grow{flex:1}
222
+ .plan-hint{font-family:var(--mono);font-size:10.5px;letter-spacing:0.04em;color:var(--ink-3)}
223
+ .plan-insufficient{font-family:var(--mono);font-size:10px;letter-spacing:0.04em;color:var(--err)}
206
224
  .plan-duration{font-family:var(--mono);font-size:10.5px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;color:var(--ink-2)}
207
225
  .plan-price{font-size:13px;font-weight:600}
226
+ .refill-model{margin-bottom:14px}
227
+ .refill-pay{display:flex;align-items:center;gap:12px;margin-top:14px}
228
+ .refill-pay .field-label{margin:0}
208
229
  .seg{display:inline-flex;border:1px solid var(--line);border-radius:var(--r-sm);overflow:hidden}
209
230
  .seg button{appearance:none;background:var(--surface-3);border:0;padding:8px 20px;font-family:var(--mono);font-size:11px;font-weight:600;letter-spacing:0.08em;color:var(--ink-3);cursor:pointer}
210
231
  .seg button+.seg button{border-left:1px solid var(--line)}
@@ -319,7 +340,7 @@ input[type="number"]{-moz-appearance:textfield}input::-webkit-outer-spin-button,
319
340
  <button class="action-btn" :class="{active: walletAction==='receive'}" @click="walletAction = walletAction==='receive' ? null : 'receive'"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 7 7 17"/><path d="M16 17H7V8"/></svg>Receive</button>
320
341
  <button class="action-btn" :class="{active: walletAction==='swap'}" @click="walletAction = walletAction==='swap' ? null : 'swap'"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m16 3 4 4-4 4"/><path d="M20 7H4"/><path d="m8 21-4-4 4-4"/><path d="M4 17h16"/></svg>Swap</button>
321
342
  </div>
322
- <div class="grid" x-show="walletAction">
343
+ <div class="grid" x-show="walletAction" style="margin-top:16px">
323
344
  <div class="span-12">
324
345
  <div class="card">
325
346
  <div class="card-head"><div class="card-title" x-text="walletAction==='send' ? 'Send' : walletAction==='receive' ? 'Receive' : 'Cauldron Swap'"></div><button class="btn-close" @click="walletAction=null" title="Close">✕</button></div>
@@ -343,14 +364,10 @@ input[type="number"]{-moz-appearance:textfield}input::-webkit-outer-spin-button,
343
364
  <div class="segmented-opt" :class="{active: sendType==='bch'}" @click="sendType='bch'; sendError=''; sendSuccess=''">BCH</div>
344
365
  <div class="segmented-opt" :class="{active: sendType==='token'}" @click="sendType='token'; sendError=''; sendSuccess=''">CashToken</div>
345
366
  </div>
346
- <div x-show="sendType==='token'">
347
- <div class="field"><label class="field-label">Token Category ID</label><select class="field-input" x-model="sendTokenPreset" @change="if(sendTokenPreset==='__custom__')sendCategory='';else sendCategory=sendTokenPreset"><option value="__custom__">Enter token category</option><option value="5932b2fd4915d6a75d3ec53282cd49118149a2176ee67ed68b1111ff0786f7fc">LIFT</option><option value="2469acc5afa4b10cb5b5c04afb89c3a3ffd61c5da9c01e26d00951cae2a02544">PUSD</option></select></div>
348
- <div class="field" x-show="sendTokenPreset === '__custom__'"><input class="field-input" type="text" x-model="sendCategory" placeholder="Paste 64-char hex category ID"></div>
349
- <div class="field"><label class="field-label">Token Amount (base units)</label><input class="field-input" type="text" x-model="sendTokenAmount" placeholder="e.g. 1000"></div>
350
- </div>
351
- <div x-show="sendType==='bch'">
352
- <div class="field"><label class="field-label">Amount</label><div class="inline-row"><input class="field-input" type="number" x-model="sendAmount" placeholder="0.0" step="any" min="0"><select class="field-input shrink" x-model="sendCurrency" style="width:104px"><option value="bch">BCH</option><option value="sats">sats</option><option value="usd">USD</option></select></div></div>
353
- </div>
367
+ <div class="field" x-show="sendType==='token'"><label class="field-label">Token Category ID</label><select class="field-input" x-model="sendTokenPreset" @change="if(sendTokenPreset==='__custom__')sendCategory='';else sendCategory=sendTokenPreset"><option value="__custom__">Enter token category</option><option value="5932b2fd4915d6a75d3ec53282cd49118149a2176ee67ed68b1111ff0786f7fc">LIFT</option><option value="2469acc5afa4b10cb5b5c04afb89c3a3ffd61c5da9c01e26d00951cae2a02544">PUSD</option></select></div>
368
+ <div class="field" x-show="sendType==='token' && sendTokenPreset === '__custom__'"><input class="field-input" type="text" x-model="sendCategory" placeholder="Paste 64-char hex category ID"></div>
369
+ <div class="field" x-show="sendType==='token'"><label class="field-label">Token Amount (base units)</label><input class="field-input" type="text" x-model="sendTokenAmount" placeholder="e.g. 1000"></div>
370
+ <div class="field" x-show="sendType==='bch'"><div class="field-label" style="display:flex;justify-content:space-between;align-items:baseline">Amount<a href="#" @click.prevent="fillMaxSend()" style="font-family:var(--mono);font-size:9.5px;font-weight:500;letter-spacing:0.12em;text-transform:uppercase;color:var(--accent);text-decoration:underline;text-underline-offset:2px;cursor:pointer">Max</a></div><div class="inline-row"><input class="field-input" type="number" x-model="sendAmount" placeholder="0.0" step="any" min="0"><select class="field-input shrink" x-model="sendCurrency" style="width:104px"><option value="bch">BCH</option><option value="sats">sats</option><option value="usd">USD</option></select></div></div>
354
371
  <div class="field"><label class="field-label">Recipient Address</label><input class="field-input" type="text" x-model="sendAddress" placeholder="bitcoincash:q..."></div>
355
372
  <template x-if="sendType==='token' && sendAddress && !isTokenAddr(sendAddress)"><div class="warning-bar">Address is not token-aware (z-prefix). Tokens may be lost.</div></template>
356
373
  <div x-show="sendError" class="field-error" x-text="sendError"></div>
@@ -362,10 +379,10 @@ input[type="number"]{-moz-appearance:textfield}input::-webkit-outer-spin-button,
362
379
  <div class="field"><label class="field-label">Token</label><select class="field-input" x-model="swapToken"><option value="__custom__">Enter token category</option><template x-for="t in swapTokens" :key="t.category"><option :value="t.category" x-text="t.symbol + ' · ' + t.category.slice(0,10) + '…' + t.category.slice(-6)"></option></template></select></div>
363
380
  <div class="field" x-show="swapToken === '__custom__'"><input class="field-input" type="text" x-model="swapCustomCategory" placeholder="Paste 64-char hex category ID"></div>
364
381
  <div class="segmented" style="margin-bottom:14px">
365
- <div class="segmented-opt" :class="{active: swapDir==='sell'}" @click="swapDir='sell'; swapQuoteText=''; pendingSwap=null">Sell Token → BCH</div>
366
382
  <div class="segmented-opt" :class="{active: swapDir==='buy'}" @click="swapDir='buy'; swapQuoteText=''; pendingSwap=null">Buy Token ← BCH</div>
383
+ <div class="segmented-opt" :class="{active: swapDir==='sell'}" @click="swapDir='sell'; swapQuoteText=''; pendingSwap=null">Sell Token → BCH</div>
367
384
  </div>
368
- <div class="field"><label class="field-label" x-text="swapDir==='sell' ? 'Token Amount' : 'BCH Amount'"></label><input class="field-input" type="number" x-model="swapAmount" placeholder="0.0" step="any" min="0"></div>
385
+ <div class="field"><label class="field-label">Token Amount (human units)</label><input class="field-input" type="number" x-model="swapAmount" placeholder="0.0" step="any" min="0"></div>
369
386
  <div x-show="swapError" class="field-error" x-text="swapError"></div>
370
387
  <button class="btn btn-primary" style="margin-top:14px;min-width:180px" @click="doSwapQuote()" :disabled="swapBusy" x-text="swapBusy ? 'Fetching quote…' : 'Get Quote'"></button>
371
388
  <template x-if="swapQuoteText"><div style="margin-top:16px;padding-top:16px;border-top:1px solid var(--line-2)">
@@ -389,8 +406,8 @@ input[type="number"]{-moz-appearance:textfield}input::-webkit-outer-spin-button,
389
406
  <div class="card-head"><div class="card-title">History</div><select class="field-input" x-model="histType" @change="loadHistory(1)" style="width:auto;padding:5px 9px;font-size:11.5px"><option value="all">All</option><option value="incoming">Incoming</option><option value="outgoing">Outgoing</option></select></div>
390
407
  <template x-if="!histRecords.length && histLoading"><div class="empty">Loading…</div></template>
391
408
  <template x-if="!histRecords.length && !histLoading"><div class="empty">No transactions yet</div></template>
392
- <template x-if="histRecords.length"><div><table class="tbl"><thead><tr><th>Date</th><th>Type</th><th>Counterparty</th><th style="text-align:right">Amount</th></tr></thead><tbody>
393
- <template x-for="(r, ri) in histRecords" :key="ri"><tr><td class="hist-date" style="white-space:nowrap"><a :href="r.explorer" target="_blank" rel="noopener" :title="'View '+r.txid+' on explorer'" x-text="r.dateText"></a></td><td><span class="pill" :class="r.type==='incoming'?'pill-in':'pill-out'" x-text="r.type==='incoming'?'IN':'OUT'"></span></td><td class="hist-addr"><span class="mono-break" :title="r.counterparty" x-text="r.partyShort"></span><div class="hist-sub" x-show="r.type==='outgoing' && r.fee" x-text="'fee '+r.fee+' BCH'"></div></td><td class="tbl-num" style="text-align:right"><div class="hist-amt" :class="r.type==='incoming'?'hist-in':'hist-out'" x-text="(r.type==='incoming'?'+':'-')+r.amountText+' BCH'"></div><div class="hist-sub" x-show="r.usd!=null" x-text="'≈ $'+r.usd+' USD'"></div></td></tr></template>
409
+ <template x-if="histRecords.length"><div class="tbl-scroll"><table class="tbl"><thead><tr><th>Date</th><th>Type</th><th>Counterparty</th><th style="text-align:right">Amount</th></tr></thead><tbody>
410
+ <template x-for="(r, ri) in histRecords" :key="ri"><tr><td class="hist-date" style="white-space:nowrap"><div class="hist-date-text" x-text="r.dateText"></div><a x-show="r.txidShort" :href="r.explorer" target="_blank" rel="noopener" :title="'View '+r.txid+' on explorer'" x-text="r.txidShort"></a></td><td><span class="pill" :class="r.type==='incoming'?'pill-in':'pill-out'" x-text="r.type==='incoming'?'IN':'OUT'"></span></td><td class="hist-addr"><span class="mono-break" :title="r.counterparty" x-text="r.partyShort"></span><div class="hist-sub" x-show="r.type==='outgoing' && r.fee" x-text="'fee '+r.fee+' BCH'"></div></td><td class="tbl-num" style="text-align:right"><div class="hist-amt" :class="r.type==='incoming'?'hist-in':'hist-out'" x-text="(r.type==='incoming'?'+':'-')+r.amountText+' BCH'"></div><div class="hist-sub" x-show="r.usd!=null" x-text="'≈ $'+r.usd+' USD'"></div></td></tr></template>
394
411
  </tbody></table><div class="pagination"><button class="btn btn-sm btn-ghost" @click="loadHistory(histPage - 1)" x-show="histPage > 1">← Prev</button><span class="page-info">Page <span x-text="histPage"></span> / <span x-text="histNumPages"></span></span><button class="btn btn-sm btn-ghost" @click="loadHistory(histPage + 1)" x-show="histHasNext">Next →</button></div></div></template>
395
412
  </div>
396
413
  </div>
@@ -414,9 +431,9 @@ input[type="number"]{-moz-appearance:textfield}input::-webkit-outer-spin-button,
414
431
  <div class="span-8">
415
432
  <div class="card">
416
433
  <div class="card-head"><div class="card-title">Usage</div></div>
417
- <template x-if="state?.usage?.length"><table class="tbl"><thead><tr><th>Model</th><th>Status</th><th>Remaining</th><th>Used</th></tr></thead><tbody>
434
+ <template x-if="state?.usage?.length"><div class="tbl-scroll"><table class="tbl"><thead><tr><th>Model</th><th>Status</th><th>Remaining</th><th>Used</th></tr></thead><tbody>
418
435
  <template x-for="(s, i) in state?.usage || []" :key="i"><tr><td style="font-size:12.5px" x-text="s.displayName || s.model || 'Unknown'"></td><td><span class="pill pill-active" x-show="s.active">ACTIVE</span><span class="pill pill-idle" x-show="!s.active">IDLE</span></td><td class="tbl-num" x-text="fmtDuration(s.remainingSeconds)"></td><td class="tbl-num" x-text="fmtDuration(s.usedSeconds)"></td></tr></template>
419
- </tbody></table></template>
436
+ </tbody></table></div></template>
420
437
  <template x-if="state && !state?.usage?.length"><div class="empty">No usage data</div></template>
421
438
  <template x-if="!state"><div class="empty">Loading…</div></template>
422
439
  </div>
@@ -427,7 +444,7 @@ input[type="number"]{-moz-appearance:textfield}input::-webkit-outer-spin-button,
427
444
  <button class="action-btn" :class="{active: aiAction==='refill'}" @click="aiAction = aiAction==='refill' ? null : 'refill'"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/><path d="M3 21v-5h5"/></svg>Auto-Refill</button>
428
445
  <button class="action-btn" :class="{active: aiAction==='images'}" @click="aiAction = aiAction==='images' ? null : 'images'"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="m21 15-5-5L5 21"/></svg>Generate Images</button>
429
446
  </div>
430
- <div class="grid" x-show="aiAction==='buy' || aiAction==='refill'">
447
+ <div class="grid" x-show="aiAction==='buy' || aiAction==='refill'" style="margin-top:16px">
431
448
  <div class="span-12">
432
449
  <div class="card">
433
450
  <div class="card-head"><div class="card-title" x-text="aiAction==='buy' ? 'Buy Credits' : 'Auto-Refill'"></div><button class="btn-close" @click="aiAction=null" title="Close">✕</button></div>
@@ -437,14 +454,15 @@ input[type="number"]{-moz-appearance:textfield}input::-webkit-outer-spin-button,
437
454
  <template x-for="plan in state?.plans || []" :key="plan.modelId">
438
455
  <div class="plan-group">
439
456
  <div class="plan-model" x-text="plan.displayName || plan.modelId"></div>
440
- <div class="plan-tiers">
441
- <template x-for="tier in plan.tiers || []" :key="tier.minutes">
442
- <div class="plan-tier">
443
- <span class="plan-duration" x-text="tier.durationDisplay"></span>
444
- <span class="plan-price" x-text="tier.priceUsd != null ? '$' + tier.priceUsd.toFixed(2) : (tier.priceSats/1e8).toFixed(8) + ' BCH'"></span>
445
- <button class="btn btn-sm btn-primary" @click="startPurchase(plan.modelId, tier.minutes, tier.priceUsd, tier.priceSats, tier.durationDisplay, plan.displayName || plan.modelId)">Buy</button>
446
- </div>
447
- </template>
457
+ <div class="plan-slider">
458
+ <input type="range" min="0" max="3" step="1" :value="planSliderIdx[plan.modelId] || 0" @input="setPlanSlider(plan.modelId, parseInt($event.target.value))" :aria-label="'Duration for ' + (plan.displayName || plan.modelId)" :style="'background:linear-gradient(to right,var(--accent) 0%,var(--accent) ' + ((planSliderIdx[plan.modelId] || 0) / 3 * 100) + '%,var(--line) ' + ((planSliderIdx[plan.modelId] || 0) / 3 * 100) + '%,var(--line) 100%)'">
459
+ <div class="plan-ticks"><span>0</span><span>15</span><span>30</span><span>60</span></div>
460
+ </div>
461
+ <div class="plan-selection">
462
+ <template x-if="planMinutes(plan.modelId) > 0"><span style="display:flex;align-items:center;gap:12px"><span class="plan-duration" x-text="planTier(plan)?.durationDisplay || ''"></span><span class="plan-price" x-text="tierPriceLabel(planTier(plan))"></span><template x-if="tierInsufficient(planTier(plan))"><span class="plan-insufficient">insufficient balance</span></template></span></template>
463
+ <template x-if="!planMinutes(plan.modelId)"><span class="plan-hint">Slide to select duration</span></template>
464
+ <span class="grow"></span>
465
+ <button class="btn btn-sm btn-primary" :disabled="!planMinutes(plan.modelId) || tierInsufficient(planTier(plan))" @click="buyPlan(plan)">Buy</button>
448
466
  </div>
449
467
  </div>
450
468
  </template>
@@ -454,20 +472,26 @@ input[type="number"]{-moz-appearance:textfield}input::-webkit-outer-spin-button,
454
472
  </div></template>
455
473
  <template x-if="aiAction==='refill'"><div>
456
474
  <template x-if="state && (!state.autoRefill || refillEditing)"><div>
457
- <div style="font-size:12.5px;color:var(--ink-3);margin-bottom:12px">Auto-refill buys a new plan when credits run out. It tops up immediately if this model has no active credits.</div>
458
- <div class="inline-row">
459
- <div class="field"><label class="field-label">Model</label><select class="field-input" x-model="refillModel"><option value="">Select model</option><template x-for="p in state?.plans || []" :key="p.modelId"><option :value="p.modelId" x-text="p.displayName || p.modelId"></option></template></select></div>
460
- <div class="field"><label class="field-label">Top up (minutes)</label><select class="field-input" x-model="refillMinutes"><template x-for="m in [15,30,60]" :key="m"><option :value="String(m)" x-text="m"></option></template><template x-if="refillMinutes && ![15,30,60].includes(parseInt(refillMinutes))"><option :value="refillMinutes" x-text="refillMinutes"></option></template></select></div>
461
- </div>
462
- <div class="inline-row" style="margin-top:12px">
463
- <div class="field"><label class="field-label">Budget cap (minutes, optional)</label><input class="field-input" type="number" min="1" x-model="refillMax" placeholder="Unlimited"></div>
464
- <div class="field"><label class="field-label">Pay with</label><select class="field-input" x-model="refillPayMethod"><option value="bch">BCH</option><option value="lift">LIFT</option></select></div>
465
- </div>
466
- <div style="margin-top:6px;font-size:11.5px;color:var(--ink-3)">Budget cap must be at least twice the top-up and a whole-number multiple of it<span x-text="' (min ' + (parseInt(refillMinutes||'30')*2) + ' min, steps of ' + parseInt(refillMinutes||'30') + ')'"></span>.</div>
467
- <template x-if="refillNotice"><div style="margin-top:12px;font-size:12.5px;color:var(--ink-3)" x-text="refillNotice"></div></template>
468
- <div style="margin-top:16px;display:flex;gap:8px">
469
- <button class="btn btn-sm btn-primary" @click="enableRefill()" x-text="refillEditing ? 'Save & Resume' : 'Enable Auto-Refill'"></button>
470
- <button class="btn btn-sm" x-show="refillEditing" @click="cancelRefillEdit()">Cancel</button>
475
+ <div style="font-size:12.5px;color:var(--ink-3);margin-bottom:14px">Auto-refill buys a new plan when credits run out. It tops up immediately if this model has no active credits.</div>
476
+ <div class="plan-group" style="padding-top:0">
477
+ <div class="refill-model"><label class="field-label">Model</label><select class="field-input" x-model="refillModel"><option value="">Select model</option><template x-for="p in state?.plans || []" :key="p.modelId"><option :value="p.modelId" x-text="p.displayName || p.modelId"></option></template></select></div>
478
+ <div class="plan-slider">
479
+ <input type="range" min="0" max="2" step="1" :value="refillMinutesIdx" @input="refillMinutesIdx = parseInt($event.target.value)" aria-label="Top-up duration" :style="'background:linear-gradient(to right,var(--accent) 0%,var(--accent) ' + (refillMinutesIdx / 2 * 100) + '%,var(--line) ' + (refillMinutesIdx / 2 * 100) + '%,var(--line) 100%)'">
480
+ <div class="plan-ticks"><span>15m</span><span>30m</span><span>1h</span></div>
481
+ </div>
482
+ <div class="plan-slider" style="margin-top:10px">
483
+ <input type="range" min="0" max="3" step="1" :value="refillCapIdx" @input="refillCapIdx = parseInt($event.target.value)" aria-label="Budget cap multiplier" :style="'background:linear-gradient(to right,var(--accent) 0%,var(--accent) ' + (refillCapIdx / 3 * 100) + '%,var(--line) ' + (refillCapIdx / 3 * 100) + '%,var(--line) 100%)'">
484
+ <div class="plan-ticks"><span>2×</span><span>4×</span><span>8×</span><span>∞</span></div>
485
+ </div>
486
+ <div class="refill-pay"><span class="field-label">Pay with</span><div class="seg"><button :class="{active: refillPayMethod==='bch'}" @click="refillPayMethod='bch'">BCH</button><button :class="{active: refillPayMethod==='lift'}" @click="refillPayMethod='lift'">LIFT</button></div></div>
487
+ <div class="plan-selection">
488
+ <template x-if="refillModel"><span style="display:flex;align-items:center;gap:12px"><span class="plan-duration" x-text="refillTopUpMinutes() + 'm top-up'"></span><span class="plan-price" style="font-size:11.5px;color:var(--ink-3)" x-text="refillCapLabel()"></span></span></template>
489
+ <template x-if="!refillModel"><span class="plan-hint">Select a model to configure auto-refill</span></template>
490
+ <span class="grow"></span>
491
+ <button class="btn btn-sm btn-primary" :disabled="!refillModel" @click="enableRefill()" x-text="refillEditing ? 'Save & Resume' : 'Enable'"></button>
492
+ <template x-if="refillEditing"><button class="btn btn-sm" @click="cancelRefillEdit()">Cancel</button></template>
493
+ </div>
494
+ <template x-if="refillNotice"><div style="margin-top:10px;font-size:12px;color:var(--ink-3)" x-text="refillNotice"></div></template>
471
495
  </div>
472
496
  </div></template>
473
497
  <template x-if="state?.autoRefill && !refillEditing"><div>
@@ -495,7 +519,7 @@ input[type="number"]{-moz-appearance:textfield}input::-webkit-outer-spin-button,
495
519
  </div>
496
520
  </div>
497
521
  </div>
498
- <div x-show="aiAction==='images'" class="grid">
522
+ <div x-show="aiAction==='images'" class="grid" style="margin-top:16px">
499
523
  <div class="span-5">
500
524
  <div class="card">
501
525
  <div class="card-head"><div class="card-title">Image Generation</div></div>
@@ -579,10 +603,10 @@ token:(function(){var t=new URLSearchParams(window.location.search).get('token')
579
603
  rcvType:'bch',rcvCategory:'',rcvAmount:'',rcvView:null,rcvQrSvg:'',
580
604
  sendType:'bch',sendAmount:'',sendCurrency:'bch',sendAddress:'',sendCategory:'',sendTokenPreset:'__custom__',sendTokenAmount:'',sendError:'',sendSuccess:'',pendingSend:null,
581
605
  histType:'all',histRecords:[],histPage:1,histNumPages:1,histHasNext:false,histLoading:false,histNetwork:'mainnet',
582
- swapTokens:[{symbol:'LIFT',category:'5932b2fd4915d6a75d3ec53282cd49118149a2176ee67ed68b1111ff0786f7fc'},{symbol:'PUSD',category:'2469acc5afa4b10cb5b5c04afb89c3a3ffd61c5da9c01e26d00951cae2a02544'}],swapToken:'5932b2fd4915d6a75d3ec53282cd49118149a2176ee67ed68b1111ff0786f7fc',swapCustomCategory:'',swapDir:'sell',swapAmount:'',swapError:'',swapQuoteText:'',swapBusy:false,swapExecBusy:false,pendingSwap:null,
583
- pendingPurchase:null,purchaseBusy:false,purchaseMethod:'bch',purchaseError:'',liftQuote:null,liftQuoteBusy:false,liftQuoteError:'',showPurchaseModal:false,showSendModal:false,sendConfirmBusy:false,
606
+ swapTokens:[{symbol:'LIFT',category:'5932b2fd4915d6a75d3ec53282cd49118149a2176ee67ed68b1111ff0786f7fc'},{symbol:'PUSD',category:'2469acc5afa4b10cb5b5c04afb89c3a3ffd61c5da9c01e26d00951cae2a02544'}],swapToken:'5932b2fd4915d6a75d3ec53282cd49118149a2176ee67ed68b1111ff0786f7fc',swapCustomCategory:'',swapDir:'buy',swapAmount:'',swapError:'',swapQuoteText:'',swapBusy:false,swapExecBusy:false,pendingSwap:null,
607
+ pendingPurchase:null,purchaseBusy:false,purchaseMethod:'bch',purchaseError:'',liftQuote:null,liftQuoteBusy:false,liftQuoteError:'',showPurchaseModal:false,showSendModal:false,sendConfirmBusy:false,planSliderIdx:{},PLAN_STEPS:[0,15,30,60],
584
608
  pendingImageQuote:null,showImageQuoteModal:false,imgGenBusy:false,imgQuoteBusy:false,walletAction:null,aiAction:null,lightbox:null,lightboxConfirmDelete:false,imgUrls:{},
585
- refillModel:'',refillMinutes:'30',refillMax:'',refillPayMethod:'bch',refillEditing:false,refillNotice:'',
609
+ refillModel:'',refillMinutesIdx:1,refillCapIdx:0,REFILL_MINUTES:[15,30,60],REFILL_CAPS:[2,4,8,-1],refillPayMethod:'bch',refillEditing:false,refillNotice:'',
586
610
  imgPrompt:'',imgModel:'bytedance-seed/seedream-5-0-pro',imgAspect:'1:1',imgQuality:'auto',
587
611
  toastVisible:false,toastMsg:'',toastOk:true,
588
612
  ws:null,wsStatus:'connecting',wsAddress:null,wsSeen:{},wsReconnectTimer:null,wsPingTimer:null,wsRefreshTimer:null,wsLateTimer:null,notify:null,_ntid:0,
@@ -594,6 +618,15 @@ async api(method,apipath,body){var opts={method:method,headers:{'X-Paytaca-Token
594
618
  fmtDuration(s){if(!s||s<=0)return '0m';var h=Math.floor(s/3600),m=Math.floor((s%3600)/60);return h?h+'h '+m+'m':m+'m'},
595
619
  bchPriceLabel(p){if(!p)return '';return p.priceUsd!=null?'$'+p.priceUsd.toFixed(2)+' USD':((p.priceSats||0)/1e8).toFixed(8)+' BCH'},
596
620
  liftPriceLabel(p){if(!p)return '';var pct=(this.state&&this.state.liftDiscountPercent)||0;var f=1-pct/100;return p.priceUsd!=null?'$'+(p.priceUsd*f).toFixed(2)+' USD':(((p.priceSats||0)*f)/1e8).toFixed(8)+' BCH'},
621
+ planMinutes(modelId){return this.PLAN_STEPS[this.planSliderIdx[modelId]||0]||0},
622
+ setPlanSlider(modelId,val){var next={};var keys=Object.keys(this.planSliderIdx);for(var i=0;i<keys.length;i++){next[keys[i]]=keys[i]===modelId?val:0}next[modelId]=val;this.planSliderIdx=next},
623
+ planTier(plan){var m=this.planMinutes(plan.modelId);if(!m)return null;var tiers=(plan&&plan.tiers)||[],i;for(i=0;i<tiers.length;i++)if(tiers[i].minutes===m)return tiers[i];var sorted=tiers.slice().sort(function(a,b){return a.minutes-b.minutes});for(i=0;i<sorted.length;i++)if(sorted[i].minutes>=m)return sorted[i];return sorted[sorted.length-1]||null},
624
+ tierPriceLabel(tier){if(!tier)return '';return tier.priceUsd!=null?'$'+tier.priceUsd.toFixed(2):(tier.priceSats/1e8).toFixed(8)+' BCH'},
625
+ tierInsufficient(tier){if(!tier)return false;var have=this.state&&this.state.balance?Number(this.state.balance.spendableSats):0;return have<Number(tier.priceSats||0)},
626
+ buyPlan(plan){var t=this.planTier(plan);if(!t)return;this.startPurchase(plan.modelId,t.minutes,t.priceUsd,t.priceSats,t.durationDisplay,plan.displayName||plan.modelId)},
627
+ refillTopUpMinutes(){return this.REFILL_MINUTES[this.refillMinutesIdx]||30},
628
+ refillCapMinutes(){var cap=this.REFILL_CAPS[this.refillCapIdx];return cap>0?cap*this.refillTopUpMinutes():null},
629
+ refillCapLabel(){var cap=this.refillCapMinutes();if(!cap)return 'No cap';return 'Max '+cap+' min ('+this.REFILL_CAPS[this.refillCapIdx]+'×)'},
597
630
 
598
631
  async loadLiftQuote(){var p=this.pendingPurchase;if(!p||!p.priceSats){this.liftQuote=null;this.liftQuoteError='';return}var pct=(this.state&&this.state.liftDiscountPercent)||0;var sats=Math.round(p.priceSats*(1-pct/100));if(sats<1)sats=1;this.liftQuoteBusy=true;this.liftQuoteError='';try{this.liftQuote=await this.api('GET','/api/ai/lift-quote?sats='+sats)}catch(e){this.liftQuote=null;this.liftQuoteError=e.message||'Failed to estimate LIFT needed'}this.liftQuoteBusy=false},
599
632
 
@@ -610,17 +643,18 @@ startWsPing(){var self=this;if(this.wsPingTimer)clearTimeout(this.wsPingTimer);t
610
643
  handleWsUpdate(m){if(!m||!m.txid)return;var key=m.txid+'|'+(m.address||'')+'|'+(m.index==null?'':m.index);if(this.wsSeen[key])return;this.wsSeen[key]=1;if(Object.keys(this.wsSeen).length>400)this.wsSeen={};this.showTxToast(m,Array.isArray(m.senders)&&m.senders.length>0)},
611
644
  showTxToast(m,incoming){var self=this;var sym=(m.token_symbol||'').toLowerCase();var tname=(m.token_name||'').toLowerCase();var isBch=!m.token_symbol||sym==='bch'||tname==='bch'||tname==='bitcoin cash'||m.token==='bch';var amountText;if(isBch){var sats=m.value!=null?Number(m.value):null;var bch=sats!=null&&isFinite(sats)?sats/1e8:null;amountText=bch!=null?bch.toFixed(8).replace(/\.?0+$/,'')+' BCH':'New activity';if(bch!=null&&this.state&&this.state.bchPriceUsd){var usd=bch*this.state.bchPriceUsd;if(usd>0.0001){var usdStr=usd.toFixed(2);if(usdStr.slice(-3)==='.00')usdStr=usdStr.slice(0,-3);amountText+=' ('+usdStr+' USD)'}}}else{amountText=m.amount!=null?(m.amount+' '+m.token_symbol):(m.token_symbol||'Token')+' transfer'}var base=this.state&&this.state.network==='chipnet'?'https://chipnet.bchexplorer.info/tx/':'https://bchexplorer.info/tx/';var id=m.txid||'';var tid=++self._ntid;var n={title:incoming?'Incoming transaction':'Wallet activity',amountText:amountText,txidShort:id.slice(0,10)+'…'+id.slice(-6),explorer:base+id};this.notify=n;if(this.wsRefreshTimer)clearTimeout(this.wsRefreshTimer);this.wsRefreshTimer=setTimeout(function(){self.load()},1200);if(this.wsLateTimer)clearTimeout(this.wsLateTimer);this.wsLateTimer=setTimeout(function(){self.load()},5000);setTimeout(function(){if(self._ntid===tid)self.notify=null},6000)},
612
645
  async onRcvChange(){this.rcvView=null;this.rcvQrSvg='';var params=new URLSearchParams();if(this.rcvType==='token'&&this.rcvCategory){params.set('category',this.rcvCategory);params.set('token','1')}if(this.rcvAmount)params.set('amount',this.rcvAmount);try{var view=await this.api('GET','/api/wallet/receive?'+params.toString());this.rcvView=view;if(view.address){try{this.rcvQrSvg=QR.toSVG(QR.encode(view.address))}catch(ex){}}if(view&&view.address&&this.wsAddress!==view.address)this.connectWatch(view.address)}catch(e){this.rcvView={address:'Error: '+e.message}}},
646
+ fillMaxSend(){var bal=this.state&&this.state.balance;if(!bal)return;if(this.sendCurrency==='bch'){this.sendAmount=String(bal.spendableBch||'')}else if(this.sendCurrency==='sats'){this.sendAmount=String(bal.spendableSats||0)}else{var bch=parseFloat(bal.spendableBch)||0;var price=this.state.bchPriceUsd||0;this.sendAmount=price?(bch*price).toFixed(2):''}},
613
647
  doSend(){this.sendError='';this.sendSuccess='';var address=(this.sendAddress||'').trim();if(!address){this.sendError='Enter a recipient address';return}if(this.sendType==='bch'){var amount=parseFloat(this.sendAmount);var currency=this.sendCurrency;if(!amount||amount<=0){this.sendError='Enter a valid amount';return}this.pendingSend={type:'bch',address:address,amount:amount,currency:currency};this.showSendModal=true}else{var category=(this.sendCategory||'').trim();var tokenAmount=(this.sendTokenAmount||'').trim();if(!category||!/^[a-fA-F0-9]{64}$/.test(category)){this.sendError='Enter a valid 64-char hex category ID';return}if(!tokenAmount){this.sendError='Enter a token amount';return}this.pendingSend={type:'token',category:category,tokenAmount:tokenAmount,address:address};this.showSendModal=true}},
614
648
  async confirmSend(){if(!this.pendingSend)return;this.sendConfirmBusy=true;try{var result;if(this.pendingSend.type==='bch'){result=await this.api('POST','/api/wallet/send',this.pendingSend)}else{result=await this.api('POST','/api/wallet/send-token',{category:this.pendingSend.category,amount:this.pendingSend.tokenAmount,address:this.pendingSend.address})}this.showSendModal=false;if(result.success){this.sendSuccess='Sent! TX: '+(result.txid||'').slice(0,16)+'...';this.toast('Transaction sent');this.load()}else{this.sendError=result.error||'Send failed'}}catch(e){this.sendError=e.message}this.sendConfirmBusy=false;this.pendingSend=null},
615
- async loadHistory(page){this.histPage=page||1;this.histLoading=true;this.histRecords=[];try{var data=await this.api('GET','/api/wallet/history?page='+this.histPage+'&type='+this.histType);var net=data.network||'mainnet';this.histNetwork=net;var base=net==='chipnet'?'https://chipnet.bchexplorer.info/tx/':'https://bchexplorer.info/tx/';this.histRecords=(data.records||[]).map(function(r){var isIn=r.record_type==='incoming';var party=isIn?((r.senders&&r.senders[0]&&r.senders[0][0])||''):((r.recipients&&r.recipients[0]&&r.recipients[0][0])||'');var amt=Number(r.amount)||0;var when=r.tx_timestamp||r.date_created||'';var dt=when?new Date(when):null;return{txid:r.txid,type:isIn?'incoming':'outgoing',dateText:dt?dt.toLocaleString(undefined,{year:'numeric',month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'}):'',counterparty:party,partyShort:party?(party.slice(0,16)+'…'+party.slice(-6)):'—',amount:amt,amountText:amt.toFixed(8),usd:r.usd_price!=null?(amt*Number(r.usd_price)).toFixed(2):null,fee:r.tx_fee!=null?(Number(r.tx_fee)/1e8).toFixed(8):null,explorer:r.txid?base+r.txid:''}});this.histNumPages=data.numPages||1;this.histHasNext=!!data.hasNext}catch(e){this.histRecords=[]}this.histLoading=false},
649
+ async loadHistory(page){this.histPage=page||1;this.histLoading=true;this.histRecords=[];try{var data=await this.api('GET','/api/wallet/history?page='+this.histPage+'&type='+this.histType);var net=data.network||'mainnet';this.histNetwork=net;var base=net==='chipnet'?'https://chipnet.bchexplorer.info/tx/':'https://bchexplorer.info/tx/';this.histRecords=(data.records||[]).map(function(r){var isIn=r.record_type==='incoming';var party=isIn?((r.senders&&r.senders[0]&&r.senders[0][0])||''):((r.recipients&&r.recipients[0]&&r.recipients[0][0])||'');var amt=Number(r.amount)||0;var when=r.tx_timestamp||r.date_created||'';var dt=when?new Date(when):null;return{txid:r.txid,type:isIn?'incoming':'outgoing',dateText:dt?dt.toLocaleString(undefined,{year:'numeric',month:'short',day:'numeric',hour:'2-digit',minute:'2-digit'}):'',txidShort:r.txid?(r.txid.slice(0,10)+'…'+r.txid.slice(-6)):'',counterparty:party,partyShort:party?(party.slice(0,16)+'…'+party.slice(-6)):'—',amount:amt,amountText:amt.toFixed(8),usd:r.usd_price!=null?(amt*Number(r.usd_price)).toFixed(2):null,fee:r.tx_fee!=null?(Number(r.tx_fee)/1e8).toFixed(8):null,explorer:r.txid?base+r.txid:''}});this.histNumPages=data.numPages||1;this.histHasNext=!!data.hasNext}catch(e){this.histRecords=[]}this.histLoading=false},
616
650
  async doSwapQuote(){var tokenId=this.swapToken==='__custom__'?(this.swapCustomCategory||'').trim():(this.swapToken||'').trim();var amount=(this.swapAmount||'').trim();this.swapError='';if(!tokenId||!/^[a-fA-F0-9]{64}$/.test(tokenId)){this.swapError='Enter a valid 64-char hex token ID';return}if(!amount||isNaN(parseFloat(amount))||parseFloat(amount)<=0){this.swapError='Enter a valid amount';return}this.swapBusy=true;try{var q=await this.api('POST','/api/swap/quote',{tokenId:tokenId,direction:this.swapDir,amount:amount});this.pendingSwap={tokenId:tokenId,direction:this.swapDir,amount:amount};var text=q.formatted||'';if(!text){text='Rate: '+(q.rate||'?')+'\\nToken: '+q.tokenAmount+' '+(q.symbol||'')+'\\nBCH: '+(Number(q.bchAmountSats)/1e8).toFixed(8)+' BCH\\nTrade fee: '+(Number(q.tradeFeeSats)/1e8).toFixed(8)+' BCH';if(q.platformFeeSats)text+='\\nPlatform fee: '+(Number(q.platformFeeSats)/1e8).toFixed(8)+' BCH'}this.swapQuoteText=text}catch(e){this.swapError=e.message}this.swapBusy=false},
617
651
  async doSwapExecute(){if(!this.pendingSwap)return;this.swapExecBusy=true;try{var result=await this.api('POST','/api/swap/execute',this.pendingSwap);if(result.success){this.toast('Swap executed! TX: '+(result.txid||'').slice(0,16)+'...');this.swapQuoteText='';this.pendingSwap=null;this.load()}else{this.swapError=result.error||'Swap failed'}}catch(e){this.swapError=e.message}this.swapExecBusy=false},
618
652
  startPurchase(model,minutes,priceUsd,priceSats,durationDisplay,displayName){this.pendingPurchase={model:model,minutes:minutes,priceUsd:priceUsd,priceSats:priceSats,durationDisplay:durationDisplay,displayName:displayName};this.purchaseMethod='bch';this.purchaseError='';this.liftQuote=null;this.liftQuoteError='';this.showPurchaseModal=true},
619
- async confirmPurchase(){if(!this.pendingPurchase)return;this.purchaseBusy=true;this.purchaseError='';try{var r=await this.api('POST','/api/ai/purchase',{model:this.pendingPurchase.model,minutes:this.pendingPurchase.minutes,method:this.purchaseMethod});if(r&&r.success&&r.paid){this.showPurchaseModal=false;this.pendingPurchase=null;this.toast('Plan purchased!');this.load()}else if(r&&r.success&&!r.paid){this.showPurchaseModal=false;this.pendingPurchase=null;this.toast(r.error||'Payment submitted — credits will update shortly',true);this.load()}else{this.purchaseError=(r&&r.error)||'Purchase failed';this.toast(this.purchaseError,true)}}catch(e){this.purchaseError=e.message||'Purchase failed';this.toast(e.message,true)}this.purchaseBusy=false},
620
- async enableRefill(){var model=this.refillModel;var minutes=parseInt(this.refillMinutes)||30;var paymentMethod=this.refillPayMethod||'bch';var maxMinutes;if(!model){this.toast('Select a model',true);return}if(this.refillMax!==''&&this.refillMax!=null){var raw=Number(this.refillMax);if(!Number.isInteger(raw)||raw<=0){this.toast('Budget cap must be a whole number',true);return}if(raw<minutes*2){this.toast('Budget cap must be at least twice the top-up ('+minutes*2+' min)',true);return}if(raw%minutes!==0){this.toast('Budget cap must be a multiple of the top-up ('+minutes+' min)',true);return}maxMinutes=raw}try{var r=await this.api('POST','/api/ai/auto-refill',{enabled:true,model:model,minutes:minutes,maxMinutes:maxMinutes,paymentMethod:paymentMethod});this.refillNotice=this.refillNoticeText(r&&r.initialTick);this.refillEditing=false;this.toast('Auto-refill enabled');this.load()}catch(e){this.toast(e.message,true)}},
653
+ async confirmPurchase(){if(!this.pendingPurchase)return;this.purchaseBusy=true;this.purchaseError='';try{var r=await this.api('POST','/api/ai/purchase',{model:this.pendingPurchase.model,minutes:this.pendingPurchase.minutes,method:this.purchaseMethod});if(r&&r.success&&r.paid){this.showPurchaseModal=false;this.pendingPurchase=null;this.planSliderIdx={};this.toast('Plan purchased!');this.load()}else if(r&&r.success&&!r.paid){this.showPurchaseModal=false;this.pendingPurchase=null;this.toast(r.error||'Payment submitted — credits will update shortly',true);this.load()}else{this.purchaseError=(r&&r.error)||'Purchase failed';this.toast(this.purchaseError,true)}}catch(e){this.purchaseError=e.message||'Purchase failed';this.toast(e.message,true)}this.purchaseBusy=false},
654
+ async enableRefill(){var model=this.refillModel;var minutes=this.refillTopUpMinutes();var maxMinutes=this.refillCapMinutes();var paymentMethod=this.refillPayMethod||'bch';if(!model){this.toast('Select a model',true);return}try{var r=await this.api('POST','/api/ai/auto-refill',{enabled:true,model:model,minutes:minutes,maxMinutes:maxMinutes,paymentMethod:paymentMethod});this.refillNotice=this.refillNoticeText(r&&r.initialTick);this.refillEditing=false;this.toast('Auto-refill enabled');this.load()}catch(e){this.toast(e.message,true)}},
621
655
  async resumeRefill(){try{var r=await this.api('POST','/api/ai/auto-refill',{enabled:true});this.refillNotice=this.refillNoticeText(r&&r.initialTick);this.toast('Auto-refill resumed');this.load()}catch(e){this.toast(e.message,true)}},
622
656
  async pauseRefill(){try{await this.api('POST','/api/ai/auto-refill',{enabled:false});this.refillNotice='';this.toast('Auto-refill paused');this.load()}catch(e){this.toast(e.message,true)}},
623
- startRefillEdit(){if(!this.state||!this.state.autoRefill)return;this.refillModel=this.state.autoRefill.model||'';this.refillMinutes=String(this.state.autoRefill.minutes||30);this.refillMax=this.state.autoRefill.maxMinutes?String(this.state.autoRefill.maxMinutes):'';this.refillPayMethod=this.state.autoRefill.paymentMethod||'bch';this.refillNotice='';this.refillEditing=true},
657
+ startRefillEdit(){if(!this.state||!this.state.autoRefill)return;var ar=this.state.autoRefill;this.refillModel=ar.model||'';var mins=ar.minutes||30;var mi=this.REFILL_MINUTES.indexOf(mins);this.refillMinutesIdx=mi>=0?mi:1;this.refillCapIdx=ar.maxMinutes?Math.round(ar.maxMinutes/mins)<=2?0:Math.round(ar.maxMinutes/mins)<=4?1:2:3;this.refillPayMethod=ar.paymentMethod||'bch';this.refillNotice='';this.refillEditing=true},
624
658
  cancelRefillEdit(){this.refillEditing=false;this.refillNotice=''},
625
659
  refillNoticeText(tick){if(!tick)return '';if(tick.action==='refilled'){var m=(tick.state&&tick.state.minutes)||'';var tx=tick.state&&tick.state.lastRefillTxid;return 'Topped up '+m+' min now'+(tx?' (tx '+String(tx).slice(0,10)+'…)':'')+'.'}if(tick.action==='skipped')return 'Refill skipped: '+tick.reason+'.';if(tick.action==='disarmed')return 'Auto-refill stopped: '+tick.reason+'.';return 'Credits still active — no refill needed.'},
626
660
  async deleteRefill(){if(!confirm('Delete auto-refill configuration?'))return;try{await this.api('POST','/api/ai/auto-refill',{delete:true});this.refillEditing=false;this.refillNotice='';this.toast('Auto-refill deleted');this.load()}catch(e){this.toast(e.message,true)}},
@@ -1 +1 @@
1
- {"version":3,"file":"page.js","sourceRoot":"","sources":["../../src/web/page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE3D,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAA;AACjE,MAAM,SAAS,GAAG,EAAE,CAAC,YAAY,CAC/B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EACvF,MAAM,CACP,CAAA;AAED,MAAM,UAAU,UAAU;IACxB,OAAO;;;;;;0CAMiC,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qCAsQjB,YAAY;;;;;;wGAMuD,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sCAuHjF,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAyOrC,SAAS;;QAEjB,CAAC;AACT,CAAC"}
1
+ {"version":3,"file":"page.js","sourceRoot":"","sources":["../../src/web/page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE3D,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAA;AACjE,MAAM,SAAS,GAAG,EAAE,CAAC,YAAY,CAC/B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,EACvF,MAAM,CACP,CAAA;AAED,MAAM,UAAU,UAAU;IACxB,OAAO;;;;;;0CAMiC,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qCA2RjB,YAAY;;;;;;wGAMuD,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sCAmHjF,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBA0PrC,SAAS;;QAEjB,CAAC;AACT,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paytaca-cli",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "Command-line interface for the Paytaca Bitcoin Cash wallet",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",