aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/features/ark.js41
-rw-r--r--src/style.css11
2 files changed, 44 insertions, 8 deletions
diff --git a/src/features/ark.js b/src/features/ark.js
index 5209de8..23e158a 100644
--- a/src/features/ark.js
+++ b/src/features/ark.js
@@ -1351,16 +1351,43 @@ export function arkFeature(ctx) {
// Moving money between the two balances, in plain terms. This is the only
// place the two rails meet, and it never names them: Saving is on-chain,
// Spending is instant, and the wallet handles the rest.
+ // Amount field with an inline Max — a text affordance inside the input's
+ // right edge rather than a button eating a third of the row.
+ function amountField({ value, onInput, maxSat, placeholder, onMax }) {
+ return h('div', { class: 'amt-field' },
+ h('input', {
+ type: 'number', inputmode: 'numeric', min: '0', placeholder,
+ value: value || '',
+ onInput,
+ }),
+ maxSat > 0
+ ? h('button', { type: 'button', class: 'amt-max', onClick: onMax }, t('max'))
+ : null);
+ }
+
+ // Boarding drains an on-chain balance, so Max must leave room for the
+ // mining fee: ask the tx builder what a sweep would actually deliver.
+ function maxBoardSat() {
+ try {
+ const feeRate = (wallet.feeRates && wallet.feeRates.halfHourFee) || 5;
+ const draft = wallet.buildTx({ recipients: [{ address: wallet.freshChange().address, amount: 0 }], feeRate, sendMax: true });
+ return Math.max(0, (draft.outputs[0]?.amount || 0));
+ } catch {
+ return 0;
+ }
+ }
+
function boardForm() {
const minBoard = (ark && ark.info && ark.info.minBoardAmountSat) || 0;
const canBoard = wallet.spendable >= minBoard;
return h('div', { class: 'col', style: 'width:100%;gap:8px' },
h('div', { class: 'input-group' },
- h('input', {
- type: 'number', inputmode: 'numeric', min: '0',
+ amountField({
+ value: ui.arkBoardAmt,
placeholder: t('arkBoardPlaceholder', { n: minBoard.toLocaleString() }),
- value: ui.arkBoardAmt || '',
onInput: (e) => { ui.arkBoardAmt = e.target.value; render(); },
+ maxSat: canBoard ? wallet.spendable : 0,
+ onMax: () => { ui.arkBoardAmt = String(maxBoardSat()); render(); },
}),
h('button', { class: 'btn-primary', disabled: !!ui.arkBusy || !canBoard, onClick: doArkBoard },
ui.arkBusy === 'board' ? h('span', { class: 'spinner sm' }) : t('arkBoardBtn'))),
@@ -1402,13 +1429,13 @@ export function arkFeature(ctx) {
const valid = sats > 0 && sats <= spendable;
return h('div', { class: 'col', style: 'gap:8px' },
h('div', { class: 'input-group' },
- h('input', {
- type: 'number', inputmode: 'numeric', min: '0',
+ amountField({
+ value: ui.arkOffboardAmt,
placeholder: t('arkOffboardAmtPlaceholder'),
- value: ui.arkOffboardAmt || '',
onInput: (e) => { ui.arkOffboardAmt = e.target.value; render(); },
+ maxSat: spendable,
+ onMax: () => { ui.arkOffboardAmt = String(spendable); render(); },
}),
- h('button', { class: 'btn-ghost', onClick: () => { ui.arkOffboardAmt = String(spendable); render(); } }, t('max')),
h('button', {
class: 'btn-primary', disabled: !!ui.arkBusy || !valid,
onClick: () => doArkOffboard(sats),
diff --git a/src/style.css b/src/style.css
index 0131556..f151bf5 100644
--- a/src/style.css
+++ b/src/style.css
@@ -513,9 +513,18 @@ body::after {
@media (prefers-reduced-motion: reduce) { .onb-mark { animation: none; } }
.onb-addr { color: var(--muted); font-family: var(--mono); font-size: 13px; }
/* balance card: one account at a time, dots to flip */
-.balance-face { min-height: 118px; }
+.balance-face { min-height: 96px; }
.balance-note { margin-top: 2px; }
.balance-dots { display: flex; gap: 7px; align-items: center; }
.bdot { width: 8px; height: 8px; padding: 0; border-radius: 50%; border: 0; background: var(--line-strong); cursor: pointer; transition: background .18s, transform .18s; }
.bdot:hover { transform: scale(1.25); background: var(--muted); }
.bdot.on { background: var(--accent); transform: scale(1.15); }
+/* amount input with an inline Max affordance */
+.amt-field { position: relative; display: flex; flex: 1; min-width: 0; }
+.amt-field input { width: 100%; padding-right: 52px; }
+.amt-max {
+ position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
+ padding: 4px 8px; font-size: 12px; font-weight: 600; line-height: 1;
+ border: 0; border-radius: 7px; background: var(--accent-soft); color: var(--muted); cursor: pointer;
+}
+.amt-max:hover { background: var(--surface2); color: var(--ink); }