remove multiple wallets + monero auto-open + agents rules

This commit is contained in:
2026-08-01 19:12:32 +08:00
parent bd8ba8e3e5
commit 5ac8c93027
7 changed files with 114 additions and 352 deletions
+2
View File
@@ -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
}
}
-33
View File
@@ -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')}
+31 -124
View File
@@ -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>
+2 -51
View File
@@ -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>
+2 -71
View File
@@ -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>