refactor: split Web5.vue, Settings.vue, and Mesh.vue into focused subcomponents
- F25: Split Web5.vue (3940 lines) into 14 files under views/web5/ - F26: Split Mesh.vue (2106→840 lines) extracting Bitcoin and Deadman panels - F27: Dashboard.vue assessed — layout shell, no split needed - F28: Split Settings.vue (1792 lines) into AccountSection + SystemSection Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
618244eab0
commit
afd7405b1a
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<!-- Node Visibility -->
|
||||
<div data-controller-container tabindex="0" :class="{ 'card-stagger': showStagger }" class="glass-card p-6 flex flex-col md:w-1/2" style="--stagger-index: 3">
|
||||
<div class="flex items-start gap-4 mb-4 shrink-0">
|
||||
<div class="flex-shrink-0 w-12 h-12 rounded-lg bg-white/10 flex items-center justify-center">
|
||||
<svg class="w-6 h-6 text-white/80" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h2 class="text-xl font-semibold text-white mb-2">{{ t('web5.nodeVisibility') }}</h2>
|
||||
<p class="text-white/70 text-sm mb-4">{{ t('web5.nodeVisibilityDesc') }}</p>
|
||||
</div>
|
||||
<div v-if="visibilityLoading" class="shrink-0">
|
||||
<svg class="animate-spin h-5 w-5 text-white/40" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Visibility Options -->
|
||||
<div class="space-y-2 flex-1 min-h-0">
|
||||
<button
|
||||
v-for="opt in visibilityOptions"
|
||||
:key="opt.value"
|
||||
@click="setVisibility(opt.value)"
|
||||
:disabled="settingVisibility"
|
||||
class="w-full flex items-center gap-3 p-3 rounded-lg border transition-colors text-left"
|
||||
:class="nodeVisibility === opt.value
|
||||
? 'bg-white/10 border-white/25 text-white'
|
||||
: 'bg-white/5 border-white/10 text-white/60 hover:bg-white/8 hover:text-white/80'"
|
||||
>
|
||||
<div class="w-3 h-3 rounded-full shrink-0 border-2 flex items-center justify-center"
|
||||
:class="nodeVisibility === opt.value ? 'border-green-400' : 'border-white/30'"
|
||||
>
|
||||
<div v-if="nodeVisibility === opt.value" class="w-1.5 h-1.5 rounded-full bg-green-400"></div>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-sm font-medium">{{ opt.label }}</p>
|
||||
<p class="text-xs text-white/50">{{ opt.description }}</p>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Onion address (shown when discoverable/public) -->
|
||||
<div v-if="nodeVisibility !== 'hidden' && nodeOnionAddress" class="mt-4 p-3 bg-white/5 rounded-lg">
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<div class="min-w-0">
|
||||
<p class="text-xs text-white/50 mb-1">{{ t('web5.yourTorAddress') }}</p>
|
||||
<p class="text-xs font-mono text-white/80 truncate" :title="nodeOnionAddress">{{ nodeOnionAddress }}</p>
|
||||
</div>
|
||||
<button @click="copyOnionAddress" class="shrink-0 p-2 rounded-lg text-white/50 hover:text-white hover:bg-white/10 transition-colors" title="Copy">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Warning -->
|
||||
<p v-if="nodeVisibility !== 'hidden'" class="mt-3 text-xs text-amber-400/80">
|
||||
{{ t('web5.discoverableWarning') }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { rpcClient } from '@/api/rpc-client'
|
||||
import { safeClipboardWrite } from './utils'
|
||||
import type { VisibilityLevel } from './types'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
defineProps<{
|
||||
showStagger: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
toast: [text: string]
|
||||
}>()
|
||||
|
||||
const nodeVisibility = ref<VisibilityLevel>('hidden')
|
||||
const nodeOnionAddress = ref<string | null>(null)
|
||||
const visibilityLoading = ref(false)
|
||||
const settingVisibility = ref(false)
|
||||
|
||||
const visibilityOptions = [
|
||||
{ value: 'hidden' as VisibilityLevel, label: 'Hidden', description: 'Your node is not discoverable by others' },
|
||||
{ value: 'discoverable' as VisibilityLevel, label: 'Discoverable', description: 'Federated peers can find and connect to your node' },
|
||||
{ value: 'public' as VisibilityLevel, label: 'Public', description: 'Accepting connections from any Archipelago node' },
|
||||
]
|
||||
|
||||
async function loadVisibility() {
|
||||
visibilityLoading.value = true
|
||||
try {
|
||||
const res = await rpcClient.call<{ visibility: string; onion_address?: string }>({ method: 'network.get-visibility' })
|
||||
nodeVisibility.value = (res.visibility as VisibilityLevel) || 'hidden'
|
||||
nodeOnionAddress.value = res.onion_address || null
|
||||
} catch {
|
||||
nodeVisibility.value = 'hidden'
|
||||
} finally {
|
||||
visibilityLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function setVisibility(level: VisibilityLevel) {
|
||||
if (settingVisibility.value || nodeVisibility.value === level) return
|
||||
settingVisibility.value = true
|
||||
try {
|
||||
const res = await rpcClient.call<{ visibility: string; onion_address?: string }>({
|
||||
method: 'network.set-visibility',
|
||||
params: { visibility: level },
|
||||
})
|
||||
nodeVisibility.value = (res.visibility as VisibilityLevel) || level
|
||||
nodeOnionAddress.value = res.onion_address || nodeOnionAddress.value
|
||||
emit('toast', t('web5.visibilitySetTo', { level }))
|
||||
} catch {
|
||||
emit('toast', t('web5.failedToUpdateVisibility'))
|
||||
} finally {
|
||||
settingVisibility.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function copyOnionAddress() {
|
||||
if (!nodeOnionAddress.value) return
|
||||
safeClipboardWrite(nodeOnionAddress.value)
|
||||
emit('toast', t('web5.onionAddressCopied'))
|
||||
}
|
||||
|
||||
defineExpose({ loadVisibility })
|
||||
</script>
|
||||
Reference in New Issue
Block a user