remove multiple wallets + monero auto-open + agents rules
This commit is contained in:
@@ -2,3 +2,5 @@
|
||||
|
||||
- Never commit changes unless explicitly told to do so. Always ask first or wait for the user's command.
|
||||
- Never manually edit `.router/router.ts` — sv-router auto-generates it from the filesystem. Just create the route file in the correct filesystem location.
|
||||
- Only commit when explicitly told to do so. When asked to commit, always write a single simple line commit message.
|
||||
- At the end of every response, always surface a "Pending from you" section listing anything you need from the user (decisions, permissions, credentials, confirming a plan, etc.). If nothing is pending, say "Nothing pending."
|
||||
|
||||
@@ -82,6 +82,10 @@ export const MoneroWallet = () => {
|
||||
await checkStatus()
|
||||
}
|
||||
await listWallets()
|
||||
if (wallets.length > 0) {
|
||||
const pw = await moneroGetStoredPassword(wallets[0])
|
||||
if (pw) await openExistingWallet(wallets[0], pw)
|
||||
}
|
||||
}
|
||||
|
||||
const logout = async () => {
|
||||
|
||||
@@ -80,7 +80,7 @@ export const XrpWallet = () => {
|
||||
)
|
||||
if (walletList) {
|
||||
wallets = walletList
|
||||
if (walletList.length === 1 && !currentWalletId) {
|
||||
if (walletList.length > 0 && !currentWalletId) {
|
||||
currentWalletId = walletList[0].id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,39 +269,6 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if xrpWallet.wallets.length > 1}
|
||||
<Card>
|
||||
<CardHeader class="pb-3">
|
||||
<CardTitle class="text-sm">XRP Wallets</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="space-y-1">
|
||||
{#each xrpWallet.wallets as wal}
|
||||
<button
|
||||
onclick={() => {
|
||||
xrpWallet.selectAndUnlockWallet(wal.id)
|
||||
navigate('/xrp')
|
||||
}}
|
||||
class="flex w-full items-center gap-3 rounded-lg p-2 text-left hover:bg-muted transition-colors">
|
||||
<WalletIcon size={16} class="shrink-0 text-muted-foreground" />
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-sm font-medium">{wal.name}</p>
|
||||
<p class="text-xs text-muted-foreground truncate font-mono">{wal.address}</p>
|
||||
</div>
|
||||
{#if xrpBalances[wal.address]?.funded === false}
|
||||
<span class="text-xs text-muted-foreground">unfunded</span>
|
||||
{:else if xrpBalances[wal.address]}
|
||||
<span class="font-mono text-sm tabular-nums">
|
||||
{fmtBal(xrpBalances[wal.address].balance)}
|
||||
</span>
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/if}
|
||||
|
||||
<div class="flex gap-3">
|
||||
<Button
|
||||
onclick={() => navigate('/xrp')}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { moneroWallet } from '$lib/states/monero-wallet.svelte.js'
|
||||
import { session } from '$lib/states/session.svelte.js'
|
||||
import { Button } from '$lib/components/ui/button/index.js'
|
||||
import { Textarea } from '$lib/components/ui/textarea/index.js'
|
||||
import { Input } from '$lib/components/ui/input/index.js'
|
||||
@@ -11,28 +10,21 @@
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from '$lib/components/ui/card/index.js'
|
||||
import Fingerprint from '@lucide/svelte/icons/fingerprint'
|
||||
import Loader from '$lib/components/loader.svelte'
|
||||
import { navigate } from 'sv-router/generated'
|
||||
|
||||
const w = moneroWallet
|
||||
|
||||
let moneroWalletName = $state('')
|
||||
let moneroWalletPass = $state('')
|
||||
let moneroMnemonic = $state('')
|
||||
let moneroRestoreHeight = $state<number | undefined>(undefined)
|
||||
let moneroSelectedWallet = $state('')
|
||||
let moneroSelectedWalletPass = $state('')
|
||||
let moneroUseBiometric = $state(false)
|
||||
|
||||
let initStarted = false
|
||||
|
||||
$effect(() => {
|
||||
if (w.walletOpen && w.walletName) {
|
||||
navigate('/monero/wallet/:name', {
|
||||
params: {
|
||||
name: w.walletName,
|
||||
},
|
||||
params: { name: w.walletName },
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -46,6 +38,12 @@
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
if (w.running && !w.walletOpen && w.wallets.length > 0 && !w.loading) {
|
||||
w.autoUnlock(w.wallets[0])
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="mx-auto max-w-xl">
|
||||
@@ -101,123 +99,41 @@
|
||||
</CardContent>
|
||||
</Card>
|
||||
{:else if !w.walletOpen}
|
||||
{#if w.wallets.length > 0}
|
||||
<Card>
|
||||
<CardContent class="flex items-center justify-center py-8">
|
||||
<Loader />
|
||||
<span class="ml-2 text-sm text-muted-foreground">
|
||||
Opening wallet...
|
||||
</span>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{:else}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Monero Wallet</CardTitle>
|
||||
<CardDescription>
|
||||
{w.wallets.length > 0
|
||||
? `${w.wallets.length} wallet${w.wallets.length > 1 ? 's' : ''} found on disk`
|
||||
: 'No existing wallets found'}
|
||||
Create a new wallet or restore from seed
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="flex flex-col gap-3">
|
||||
{#if w.wallets.length > 0}
|
||||
<p class="text-xs font-medium">
|
||||
Select a wallet to open:
|
||||
</p>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#each w.wallets as name}
|
||||
<Button
|
||||
variant={moneroSelectedWallet === name
|
||||
? 'default'
|
||||
: 'outline'}
|
||||
onclick={() => {
|
||||
moneroSelectedWallet = name
|
||||
moneroSelectedWalletPass = ''
|
||||
}}>
|
||||
{name}
|
||||
</Button>
|
||||
{/each}
|
||||
</div>
|
||||
{#if moneroSelectedWallet}
|
||||
<div class="flex flex-col gap-2">
|
||||
{#if session.unlocked}
|
||||
<Button
|
||||
onclick={() =>
|
||||
w.autoUnlock(
|
||||
moneroSelectedWallet,
|
||||
)}
|
||||
disabled={w.loading}>
|
||||
{#if w.loading}
|
||||
<Loader />
|
||||
{/if}
|
||||
Open
|
||||
</Button>
|
||||
{:else if w.biometricAvailable}
|
||||
<Button
|
||||
onclick={async () => {
|
||||
const ok = await w.biometricAuth()
|
||||
if (ok) {
|
||||
await w.openExistingWallet(
|
||||
moneroSelectedWallet,
|
||||
)
|
||||
}
|
||||
}}
|
||||
disabled={w.loading}>
|
||||
<Fingerprint size={14} />
|
||||
Open with Touch ID
|
||||
</Button>
|
||||
{/if}
|
||||
<div class="flex gap-2 items-end">
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Enter password"
|
||||
bind:value={moneroSelectedWalletPass} />
|
||||
<Button
|
||||
onclick={() =>
|
||||
w.openExistingWallet(
|
||||
moneroSelectedWallet,
|
||||
moneroSelectedWalletPass,
|
||||
)}
|
||||
disabled={w.loading ||
|
||||
!moneroSelectedWalletPass}>
|
||||
Open
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<hr class="border-muted" />
|
||||
{/if}
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Or create a new wallet:
|
||||
Create a new wallet:
|
||||
</p>
|
||||
<Input
|
||||
placeholder="Wallet name"
|
||||
bind:value={moneroWalletName} />
|
||||
{#if w.biometricAvailable}
|
||||
<label
|
||||
class="flex items-center gap-2 text-xs cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={moneroUseBiometric} />
|
||||
Save password with Touch ID
|
||||
</label>
|
||||
{/if}
|
||||
{#if !moneroUseBiometric}
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
bind:value={moneroWalletPass} />
|
||||
{/if}
|
||||
<div class="flex gap-2">
|
||||
<Button
|
||||
onclick={async () => {
|
||||
const pw = moneroUseBiometric
|
||||
? crypto.randomUUID()
|
||||
: moneroWalletPass
|
||||
await w.createWallet(
|
||||
onclick={() =>
|
||||
w.createWallet(
|
||||
moneroWalletName,
|
||||
pw,
|
||||
moneroUseBiometric,
|
||||
)
|
||||
}}
|
||||
disabled={w.loading ||
|
||||
!moneroWalletName ||
|
||||
(!moneroUseBiometric && !moneroWalletPass)}>
|
||||
crypto.randomUUID(),
|
||||
true,
|
||||
)}
|
||||
disabled={w.loading || !moneroWalletName}>
|
||||
Create Wallet
|
||||
</Button>
|
||||
</div>
|
||||
<hr class="border-muted" />
|
||||
<p class="text-xs text-muted-foreground">
|
||||
Restore from seed
|
||||
@@ -229,27 +145,17 @@
|
||||
type="number"
|
||||
placeholder="Restore height (optional)"
|
||||
bind:value={moneroRestoreHeight} />
|
||||
{#if !moneroUseBiometric}
|
||||
<Input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
bind:value={moneroWalletPass} />
|
||||
{/if}
|
||||
<Button
|
||||
onclick={async () => {
|
||||
await w.restoreWallet(
|
||||
onclick={() =>
|
||||
w.restoreWallet(
|
||||
moneroWalletName,
|
||||
moneroUseBiometric
|
||||
? crypto.randomUUID()
|
||||
: moneroWalletPass,
|
||||
crypto.randomUUID(),
|
||||
moneroMnemonic,
|
||||
moneroRestoreHeight,
|
||||
moneroUseBiometric,
|
||||
)
|
||||
}}
|
||||
true,
|
||||
)}
|
||||
disabled={w.loading ||
|
||||
!moneroWalletName ||
|
||||
(!moneroUseBiometric && !moneroWalletPass) ||
|
||||
!moneroMnemonic.trim()}>
|
||||
Restore Wallet
|
||||
</Button>
|
||||
@@ -258,5 +164,6 @@
|
||||
</Card>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -9,14 +9,10 @@
|
||||
CardTitle,
|
||||
} from '$lib/components/ui/card/index.js'
|
||||
import { navigate } from 'sv-router/generated'
|
||||
import ArrowLeft from '@lucide/svelte/icons/arrow-left'
|
||||
import Trash2 from '@lucide/svelte/icons/trash-2'
|
||||
import WalletIcon from '@lucide/svelte/icons/wallet'
|
||||
|
||||
const w = xrpWallet
|
||||
let confirm = $state(false)
|
||||
let resetting = $state(false)
|
||||
let deletingWalletId = $state<string | null>(null)
|
||||
|
||||
async function handleReset() {
|
||||
resetting = true
|
||||
@@ -25,53 +21,9 @@
|
||||
resetting = false
|
||||
confirm = false
|
||||
}
|
||||
|
||||
async function handleDeleteWallet(id: string) {
|
||||
await w.deleteWallet(id)
|
||||
deletingWalletId = null
|
||||
if (w.wallets.length === 0) navigate('/xrp')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="mx-auto max-w-xl">
|
||||
<div class="flex flex-col gap-4">
|
||||
{#if w.wallets.length > 0}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Wallets</CardTitle>
|
||||
<CardDescription>Manage your XRP wallets</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="flex flex-col gap-2">
|
||||
{#each w.wallets as wal}
|
||||
<div class="flex items-center gap-3 rounded-md border border-input p-3">
|
||||
<WalletIcon size={18} class="shrink-0 text-muted-foreground" />
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="font-medium text-sm">{wal.name}</p>
|
||||
<p class="text-xs text-muted-foreground truncate font-mono">
|
||||
{wal.address}
|
||||
</p>
|
||||
</div>
|
||||
{#if deletingWalletId === wal.id}
|
||||
<div class="flex items-center gap-1.5">
|
||||
<Button size="sm" variant="destructive" onclick={() => handleDeleteWallet(wal.id)}>
|
||||
Delete
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={() => (deletingWalletId = null)}>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
{:else}
|
||||
<Button size="sm" variant="outline" onclick={() => (deletingWalletId = wal.id)}>
|
||||
<Trash2 size={14} />
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/if}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Settings</CardTitle>
|
||||
@@ -81,8 +33,8 @@
|
||||
<div>
|
||||
<p class="font-medium text-xs">Reset App</p>
|
||||
<p class="text-muted-foreground text-xs mt-1">
|
||||
Removes all XRP wallets and seeds from the keychain. You
|
||||
can then set up the wallet fresh.
|
||||
Removes all wallets and seeds from the keychain. You can
|
||||
then set up the wallet fresh.
|
||||
</p>
|
||||
<div class="mt-3">
|
||||
{#if confirm}
|
||||
@@ -104,5 +56,4 @@
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { xrpWallet } from '$lib/states/xrp-wallet.svelte.js'
|
||||
import { session } from '$lib/states/session.svelte.js'
|
||||
import { Button } from '$lib/components/ui/button/index.js'
|
||||
import { Input } from '$lib/components/ui/input/index.js'
|
||||
import { Textarea } from '$lib/components/ui/textarea/index.js'
|
||||
@@ -12,9 +11,6 @@
|
||||
CardTitle,
|
||||
} from '$lib/components/ui/card/index.js'
|
||||
import Fingerprint from '@lucide/svelte/icons/fingerprint'
|
||||
import LockKeyhole from '@lucide/svelte/icons/lock-keyhole'
|
||||
import Plus from '@lucide/svelte/icons/plus'
|
||||
import WalletIcon from '@lucide/svelte/icons/wallet'
|
||||
import Loader from '$lib/components/loader.svelte'
|
||||
import { navigate } from 'sv-router/generated'
|
||||
|
||||
@@ -24,7 +20,6 @@
|
||||
let inputSeed = $state('')
|
||||
let inputPassword = $state('')
|
||||
let inputUnlockPassword = $state('')
|
||||
let showCreateForm = $state(false)
|
||||
let newSeed = $state<string | null>(null)
|
||||
|
||||
let initStarted = false
|
||||
@@ -108,12 +103,10 @@
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{:else if w.wallets.length === 0 || showCreateForm}
|
||||
{:else if w.wallets.length === 0}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>
|
||||
{showCreateForm ? 'Add Another Wallet' : 'Create / Import Wallet'}
|
||||
</CardTitle>
|
||||
<CardTitle>Create / Import Wallet</CardTitle>
|
||||
<CardDescription>
|
||||
Generate a new XRP wallet or import an existing seed
|
||||
</CardDescription>
|
||||
@@ -139,7 +132,6 @@
|
||||
type="password"
|
||||
placeholder="Set a password (optional)"
|
||||
bind:value={inputPassword} />
|
||||
<div class="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
onclick={handleImport}
|
||||
@@ -148,68 +140,12 @@
|
||||
!inputWalletName.trim()}>
|
||||
{w.loading ? 'Importing...' : 'Import Wallet'}
|
||||
</Button>
|
||||
{#if showCreateForm}
|
||||
<Button
|
||||
variant="outline"
|
||||
onclick={() => (showCreateForm = false)}>
|
||||
Cancel
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if w.error}
|
||||
<p class="mt-3 text-xs text-red-500">{w.error}</p>
|
||||
{/if}
|
||||
</CardContent>
|
||||
</Card>
|
||||
{:else if !w.currentWalletId}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Wallets</CardTitle>
|
||||
<CardDescription>Select a wallet to use</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div class="flex flex-col gap-2">
|
||||
{#each w.wallets as wal}
|
||||
<button
|
||||
onclick={async () => {
|
||||
if (session.unlocked) {
|
||||
await w.autoUnlock(wal.id)
|
||||
} else if (wal.hasPassword) {
|
||||
await w.selectWallet(wal.id)
|
||||
} else {
|
||||
await w.selectAndUnlockWallet(wal.id)
|
||||
}
|
||||
}}
|
||||
class="flex w-full cursor-pointer items-center gap-3 rounded-md border border-input p-3 text-left hover:bg-muted transition-colors">
|
||||
<WalletIcon
|
||||
size={20}
|
||||
class="shrink-0 text-muted-foreground" />
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="font-medium text-sm">{wal.name}</p>
|
||||
<p class="text-xs text-muted-foreground truncate font-mono">
|
||||
{wal.address}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
{#if wal.hasPassword}
|
||||
<LockKeyhole
|
||||
size={14}
|
||||
class="text-muted-foreground" />
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
<Button
|
||||
variant="outline"
|
||||
onclick={() => (showCreateForm = true)}
|
||||
class="mt-2">
|
||||
<Plus size={16} />
|
||||
Add Wallet
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{:else if w.isLocked}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
@@ -254,11 +190,6 @@
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
<Button
|
||||
variant="outline"
|
||||
onclick={() => w.clearSelection()}>
|
||||
Pick another wallet
|
||||
</Button>
|
||||
</div>
|
||||
{#if w.error}
|
||||
<p class="mt-3 text-xs text-red-500">{w.error}</p>
|
||||
|
||||
Reference in New Issue
Block a user