Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

<script setup lang="ts">
import { invoke } from '@tauri-apps/api/core'
import { useLocale } from '~/composables/useLocale'
import { useWallet } from '~/composables/useWallet'
import { useTheme } from '~/composables/useTheme'
import { useSettingsStore } from '~/stores/settings'
Expand All @@ -31,8 +32,14 @@ const nodesStore = useNodesStore()
const filesStore = useFilesStore()
const connectionStore = useConnectionStore()

const { init: initLocale } = useLocale()

onMounted(async () => {
// Config first — useLocale.init() reads the persisted choice from
// settingsStore.i18nLocale, so the store must be hydrated before we resolve.
await settingsStore.loadConfig()
await initLocale()

await settingsStore.loadDevnetManifest()
nodesStore.init()
filesStore.loadHistory()
Expand Down
32 changes: 16 additions & 16 deletions components/AppHeader.vue
Original file line number Diff line number Diff line change
@@ -1,43 +1,43 @@
<template>
<header class="flex h-12 items-center justify-between border-b border-autonomi-border bg-autonomi-surface px-4">
<!-- Page title -->
<h1 class="text-sm font-medium text-autonomi-text">{{ pageTitle }}</h1>
<h1 class="text-sm font-medium text-autonomi-text">{{ $t(pageTitleKey) }}</h1>

<!-- Right side: status indicators + wallet -->
<div class="flex items-center gap-4">
<!-- Active transfers indicator -->
<div v-if="filesStore.hasActiveTransfers" class="flex items-center gap-2 text-xs">
<span class="text-autonomi-blue">↑↓</span>
<span class="text-autonomi-muted">
{{ filesStore.pinnedFiles.length }} active
{{ $t('header.active_transfers', { count: filesStore.pinnedFiles.length }) }}
</span>
</div>

<!-- Network connection indicator -->
<div
v-if="connectionStore.isConnecting"
class="flex items-center gap-2 rounded-md border border-autonomi-border px-2.5 py-1 text-xs text-autonomi-muted"
title="Connecting to the Autonomi network"
:title="$t('header.connecting_tooltip')"
>
<div class="h-2.5 w-2.5 animate-spin rounded-full border-2 border-yellow-500 border-t-transparent" />
<span>Connecting</span>
<span>{{ $t('header.connecting') }}</span>
</div>
<div
v-else-if="connectionStore.isConnected"
class="flex items-center gap-2 rounded-md border border-autonomi-border px-2.5 py-1 text-xs text-autonomi-text"
title="Connected to the Autonomi network"
:title="$t('header.connected_tooltip')"
>
<span class="text-autonomi-success">●</span>
<span>Network</span>
<span>{{ $t('header.connected') }}</span>
</div>
<button
v-else-if="connectionStore.hasFailed"
class="flex items-center gap-2 rounded-md border border-red-500/30 bg-red-500/5 px-2.5 py-1 text-xs text-red-400 hover:bg-red-500/10"
title="Connection failed — click to retry"
:title="$t('header.offline_tooltip')"
@click="connectionStore.retry()"
>
<span>●</span>
<span>Offline · Retry</span>
<span>{{ $t('header.offline') }}</span>
</button>

<!-- Indelible indicator (replaces wallet when connected) -->
Expand All @@ -64,7 +64,7 @@
class="rounded-md bg-autonomi-blue px-2.5 py-1 text-xs font-medium text-white hover:opacity-90"
@click="openModal"
>
Connect Wallet
{{ $t('header.connect_wallet') }}
</button>
</template>
</div>
Expand Down Expand Up @@ -98,14 +98,14 @@ function openModal() {
}
}

const pageTitle = computed(() => {
const titles: Record<string, string> = {
'/': 'Nodes',
'/files': 'Files',
'/wallet': 'Wallet',
'/settings': 'Settings',
const pageTitleKey = computed(() => {
const keys: Record<string, string> = {
'/': 'nav.nodes',
'/files': 'nav.files',
'/wallet': 'nav.wallet',
'/settings': 'nav.settings',
}
return titles[route.path] ?? 'Autonomi'
return keys[route.path] ?? 'header.title'
})

</script>
26 changes: 13 additions & 13 deletions components/AppSidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
v-if="isPrereleaseBuild"
class="mb-2 rounded-md bg-teal-500/10 border border-teal-500/20 px-3 py-1.5 text-center text-xs font-medium text-teal-400"
>
PRE-RELEASE
{{ $t('sidebar.pre_release') }}
</div>

<!-- Network mode indicator -->
<div
v-if="settingsStore.devnetActive && networkLabel"
v-if="settingsStore.devnetActive && networkLabelKey"
class="mb-2 rounded-md bg-amber-500/10 border border-amber-500/20 px-3 py-1.5 text-center text-xs font-medium text-amber-400"
>
{{ networkLabel }}
{{ $t(networkLabelKey) }}
</div>

<!-- Update banner -->
Expand All @@ -42,9 +42,9 @@
v-if="updaterStore.isPrerelease"
class="text-[10px] font-bold uppercase tracking-wider opacity-70"
>
Pre-Release
{{ $t('sidebar.update_pre_release_tag') }}
</div>
<div class="text-xs font-bold uppercase tracking-wide">Update Available</div>
<div class="text-xs font-bold uppercase tracking-wide">{{ $t('sidebar.update_available') }}</div>
<div class="truncate text-xs font-medium opacity-80">v{{ updaterStore.version }}</div>
</div>
<span v-if="updaterStore.installing" class="h-4 w-4 animate-spin rounded-full border-2 border-gray-900/30 border-t-gray-900" />
Expand All @@ -60,7 +60,7 @@
: 'text-autonomi-muted hover:bg-autonomi-border/50 hover:text-autonomi-text'"
>
<span class="text-base">{{ item.icon }}</span>
<span>{{ item.label }}</span>
<span>{{ $t(item.labelKey) }}</span>
</NuxtLink>
</nav>

Expand All @@ -74,7 +74,7 @@
: 'text-autonomi-muted hover:bg-autonomi-border/50 hover:text-autonomi-text'"
>
<span class="text-base">⚙</span>
<span>Settings</span>
<span>{{ $t('nav.settings') }}</span>
</NuxtLink>
</div>
</aside>
Expand Down Expand Up @@ -109,18 +109,18 @@ const isPrereleaseBuild = computed(() =>
/-(?:rc|beta|alpha)\./.test(currentVersion.value ?? ''),
)

const networkLabel = computed<string | null>(() => {
const networkLabelKey = computed<string | null>(() => {
switch (settingsStore.devnetChainId) {
case arbitrumSepolia.id: return 'SEPOLIA TESTNET'
case ANVIL_CHAIN_ID: return 'DEVNET'
case arbitrumSepolia.id: return 'sidebar.network_sepolia'
case ANVIL_CHAIN_ID: return 'sidebar.network_devnet'
default: return null // mainnet or unset — no badge
}
})

const mainNav = computed(() => [
{ path: '/', label: 'Nodes', icon: '⬡' },
{ path: '/files', label: 'Files', icon: '◫' },
{ path: '/wallet', label: 'Wallet', icon: '◎' },
{ path: '/', labelKey: 'nav.nodes', icon: '⬡' },
{ path: '/files', labelKey: 'nav.files', icon: '◫' },
{ path: '/wallet', labelKey: 'nav.wallet', icon: '◎' },
])

function isActive(path: string) {
Expand Down
60 changes: 34 additions & 26 deletions components/StatusBadge.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,43 @@
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n'

const props = defineProps<{ status: string }>()
const { t } = useI18n()

const statusMap: Record<string, { dot: string; label: string; color: string }> = {
// Static-status entries keyed by the backend's status string. `labelKey`
// resolves to the localized label; dynamic statuses (percentages, "Failed:",
// "Quote:") bypass this map and surface their raw text — those are
// untranslated by design until the source emits structured tokens we can
// interpolate.
const statusMap: Record<string, { dot: string; labelKey: string; color: string }> = {
// Node statuses (lowercase, matching ant-core)
running: { dot: '●', label: 'Running', color: 'text-autonomi-success' },
stopped: { dot: '○', label: 'Stopped', color: 'text-autonomi-muted' },
starting: { dot: '◐', label: 'Starting', color: 'text-autonomi-warning' },
stopping: { dot: '◐', label: 'Stopping', color: 'text-autonomi-warning' },
adding: { dot: '◐', label: 'Adding...', color: 'text-autonomi-warning' },
errored: { dot: '●', label: 'Error', color: 'text-autonomi-error' },
upgrade_scheduled: { dot: '◐', label: 'Upgrading', color: 'text-autonomi-blue' },
running: { dot: '●', labelKey: 'status.node.running', color: 'text-autonomi-success' },
stopped: { dot: '○', labelKey: 'status.node.stopped', color: 'text-autonomi-muted' },
starting: { dot: '◐', labelKey: 'status.node.starting', color: 'text-autonomi-warning' },
stopping: { dot: '◐', labelKey: 'status.node.stopping', color: 'text-autonomi-warning' },
adding: { dot: '◐', labelKey: 'status.node.adding', color: 'text-autonomi-warning' },
errored: { dot: '●', labelKey: 'status.node.errored', color: 'text-autonomi-error' },
upgrade_scheduled: { dot: '◐', labelKey: 'status.node.upgrade_scheduled', color: 'text-autonomi-blue' },
// File transfer statuses
Pending: { dot: '○', label: 'Pending', color: 'text-autonomi-muted' },
Quoting: { dot: '◐', label: 'Quoting', color: 'text-autonomi-warning' },
'Encrypting…': { dot: '◐', label: 'Encrypting…', color: 'text-autonomi-warning' },
'Resolving datamap': { dot: '◐', label: 'Resolving datamap', color: 'text-autonomi-warning' },
'Queued: quoting': { dot: '○', label: 'Queued: quoting', color: 'text-autonomi-muted' },
'Queued: uploading': { dot: '○', label: 'Queued: uploading', color: 'text-autonomi-muted' },
'Connecting to network…': { dot: '◐', label: 'Connecting to network…', color: 'text-autonomi-warning' },
'Obtaining quote…': { dot: '◐', label: 'Obtaining quote…', color: 'text-autonomi-warning' },
'Saving datamap…': { dot: '◐', label: 'Saving datamap…', color: 'text-autonomi-warning' },
'Network unavailable': { dot: '✖', label: 'Network unavailable', color: 'text-autonomi-error' },
'Ready to approve': { dot: '●', label: 'Ready to approve', color: 'text-autonomi-blue' },
'Awaiting approval': { dot: '◐', label: 'Awaiting approval', color: 'text-autonomi-warning' },
Uploading: { dot: '●', label: 'Uploading', color: 'text-autonomi-blue' },
Downloading: { dot: '●', label: 'Downloading', color: 'text-autonomi-blue' },
Complete: { dot: '●', label: 'Done', color: 'text-autonomi-success' },
Done: { dot: '●', label: 'Done', color: 'text-autonomi-success' },
Downloaded: { dot: '↓', label: 'Downloaded — click to open', color: 'text-autonomi-blue' },
Pending: { dot: '○', labelKey: 'status.file.pending', color: 'text-autonomi-muted' },
Quoting: { dot: '◐', labelKey: 'status.file.quoting', color: 'text-autonomi-warning' },
'Encrypting…': { dot: '◐', labelKey: 'status.file.encrypting', color: 'text-autonomi-warning' },
'Resolving datamap': { dot: '◐', labelKey: 'status.file.resolving_datamap', color: 'text-autonomi-warning' },
'Queued: quoting': { dot: '○', labelKey: 'status.file.queued_quoting', color: 'text-autonomi-muted' },
'Queued: uploading': { dot: '○', labelKey: 'status.file.queued_uploading', color: 'text-autonomi-muted' },
'Connecting to network…': { dot: '◐', labelKey: 'status.file.connecting_to_network', color: 'text-autonomi-warning' },
'Obtaining quote…': { dot: '◐', labelKey: 'status.file.obtaining_quote', color: 'text-autonomi-warning' },
'Saving datamap…': { dot: '◐', labelKey: 'status.file.saving_datamap', color: 'text-autonomi-warning' },
'Network unavailable': { dot: '✖', labelKey: 'status.file.network_unavailable', color: 'text-autonomi-error' },
'Ready to approve': { dot: '●', labelKey: 'status.file.ready_to_approve', color: 'text-autonomi-blue' },
'Awaiting approval': { dot: '◐', labelKey: 'status.file.awaiting_approval', color: 'text-autonomi-warning' },
Uploading: { dot: '●', labelKey: 'status.file.uploading', color: 'text-autonomi-blue' },
Downloading: { dot: '●', labelKey: 'status.file.downloading', color: 'text-autonomi-blue' },
Complete: { dot: '●', labelKey: 'status.file.done', color: 'text-autonomi-success' },
Done: { dot: '●', labelKey: 'status.file.done', color: 'text-autonomi-success' },
Downloaded: { dot: '↓', labelKey: 'status.file.downloaded', color: 'text-autonomi-blue' },
}

const entry = computed(() => {
Expand All @@ -58,7 +66,7 @@ const entry = computed(() => {
return { dot: '●', label: props.status, color: 'text-autonomi-blue', error: false }
}
const mapped = statusMap[props.status]
if (mapped) return { ...mapped, error: false }
if (mapped) return { dot: mapped.dot, label: t(mapped.labelKey), color: mapped.color, error: false }
return { dot: '?', label: props.status, color: 'text-autonomi-muted', error: false }
})

Expand Down
24 changes: 12 additions & 12 deletions components/UpdateDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,37 @@
>
<!-- Header -->
<h2 id="update-dialog-title" class="text-lg font-medium">
Update Available
{{ $t('updater.dialog.title') }}
</h2>
<p class="mt-1 flex items-center gap-2 text-sm text-autonomi-muted">
<span>v{{ updaterStore.version }} is ready to install</span>
<span>{{ $t('updater.dialog.version_ready', { version: updaterStore.version }) }}</span>
<span
v-if="updaterStore.isPrerelease"
class="rounded bg-teal-500/10 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wider text-teal-400"
>
Pre-release
{{ $t('updater.dialog.pre_release_badge') }}
</span>
</p>

<!-- Download size (shown during download if available) -->
<p v-if="updaterStore.downloadTotal" class="mt-1 text-xs text-autonomi-muted">
Download size: {{ formatBytes(updaterStore.downloadTotal) }}
{{ $t('updater.dialog.download_size', { size: formatBytes(updaterStore.downloadTotal) }) }}
</p>

<!-- Release notes -->
<div
v-if="!updaterStore.installing"
class="mt-4 max-h-56 overflow-auto rounded-md bg-autonomi-surface p-3"
>
<h3 class="mb-2 text-xs font-semibold uppercase tracking-wider text-autonomi-muted">Release Notes</h3>
<h3 class="mb-2 text-xs font-semibold uppercase tracking-wider text-autonomi-muted">{{ $t('updater.dialog.release_notes') }}</h3>
<div v-if="updaterStore.body" class="prose-sm text-xs leading-relaxed text-autonomi-text" v-html="renderMarkdown(updaterStore.body)" />
<p v-else class="text-xs text-autonomi-muted">No release notes available for this version.</p>
<p v-else class="text-xs text-autonomi-muted">{{ $t('updater.dialog.no_release_notes') }}</p>
</div>

<!-- Download progress -->
<div v-if="updaterStore.installing" class="mt-4">
<div class="flex items-center justify-between text-xs text-autonomi-muted">
<span>{{ downloadComplete ? 'Download succeeded, the app will restart shortly' : 'Downloading...' }}</span>
<span>{{ downloadComplete ? $t('updater.dialog.download_complete') : $t('updater.dialog.downloading') }}</span>
<span v-if="!downloadComplete && updaterStore.downloadProgress !== null">{{ updaterStore.downloadProgress }}%</span>
</div>
<div class="mt-1.5 h-2 overflow-hidden rounded-full bg-autonomi-surface">
Expand All @@ -56,7 +56,7 @@
{{ formatBytes(updaterStore.downloadedBytes) }} / {{ formatBytes(updaterStore.downloadTotal) }}
</p>
<p v-if="!downloadComplete" class="mt-2 text-xs text-autonomi-muted">
The app will restart automatically when complete.
{{ $t('updater.dialog.auto_restart_hint') }}
</p>
</div>

Expand All @@ -69,10 +69,10 @@
v-if="updaterStore.installing"
class="rounded-md border border-autonomi-error/50 px-3 py-1.5 text-sm text-autonomi-error hover:bg-autonomi-error/10 disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:bg-transparent"
:disabled="downloadComplete"
:title="downloadComplete ? 'Installing — please wait' : undefined"
:title="downloadComplete ? $t('updater.dialog.installing_tooltip') : undefined"
@click="cancelDownload"
>
Cancel Download
{{ $t('updater.dialog.cancel_download') }}
</button>
<span v-else />

Expand All @@ -82,14 +82,14 @@
class="rounded-md border border-autonomi-border px-3 py-1.5 text-sm text-autonomi-muted hover:text-autonomi-text"
@click="close"
>
Not Now
{{ $t('updater.dialog.not_now') }}
</button>
<button
v-if="!updaterStore.installing"
class="rounded-md bg-autonomi-blue px-3 py-1.5 text-sm font-medium text-white hover:opacity-90"
@click="confirm"
>
Update &amp; Restart
{{ $t('updater.dialog.update_restart') }}
</button>
</div>
</div>
Expand Down
Loading