feat(ui): various ui/ux improvements for desktop and mobile screens

This commit is contained in:
Ivan committed 2026-04-12 18:29:12 -05:00
1 parent 4fc7826904
commit 4ca9b97350
8 files changed
+154 -82

No files matched your search

@@ -2,15 +2,18 @@
<v-dialog
v-if="!isPage"
v-model="visible"
:fullscreen="isMobile"
:fullscreen="dialogFullscreen"
max-width="800"
scrollable
transition="dialog-bottom-transition"
class="changelog-dialog"
@update:model-value="onVisibleUpdate"
>
<v-card class="flex flex-col h-full bg-white dark:bg-zinc-900 border-0 overflow-hidden">
<v-card
class="flex min-h-0 flex-1 flex-col bg-white dark:bg-zinc-900 border-0 overflow-hidden h-full max-h-[100dvh]"
>
<!-- Header -->
<v-toolbar flat color="transparent" class="px-4 border-b dark:border-zinc-800">
<v-toolbar flat color="transparent" class="px-3 sm:px-4 border-b dark:border-zinc-800 shrink-0">
<div class="flex items-center">
<div class="p-1 mr-3">
<img src="../public/favicons/favicon-512x512.png" class="w-8 h-8 object-contain" alt="Logo" />
@@ -36,7 +39,7 @@
</v-toolbar>
<!-- Content -->
<v-card-text class="flex-1 overflow-y-auto px-6 py-8">
<v-card-text class="flex-1 min-h-0 overflow-y-auto overscroll-contain px-4 py-6 sm:px-6 sm:py-8">
<div v-if="loading" class="flex flex-col items-center justify-center h-full space-y-4">
<v-progress-circular indeterminate color="blue" size="64"></v-progress-circular>
<div class="text-gray-500 dark:text-zinc-400 font-medium">Loading changelog...</div>
@@ -59,7 +62,9 @@
<!-- Footer -->
<v-divider class="dark:border-zinc-800"></v-divider>
<v-card-actions class="px-6 py-4 bg-gray-50 dark:bg-zinc-950/50 flex-wrap gap-y-2">
<v-card-actions
class="px-4 py-3 sm:px-6 sm:py-4 bg-gray-50 dark:bg-zinc-950/50 flex-wrap gap-y-2 shrink-0 pb-[max(0.75rem,env(safe-area-inset-bottom))]"
>
<div class="flex flex-col">
<v-checkbox
v-model="dontShowAgain"
@@ -145,6 +150,7 @@ export default {
version: "",
dontShowAgain: false,
dontShowEver: false,
windowWidth: typeof window !== "undefined" ? window.innerWidth : 1024,
};
},
computed: {
@@ -154,15 +160,24 @@ export default {
isPage() {
return this.$route?.meta?.isPage === true;
},
isMobile() {
return window.innerWidth < 640;
dialogFullscreen() {
return this.windowWidth < 768;
},
},
mounted() {
this.onWindowResize = () => {
this.windowWidth = window.innerWidth;
};
window.addEventListener("resize", this.onWindowResize, { passive: true });
if (this.isPage) {
this.fetchChangelog();
}
},
beforeUnmount() {
if (this.onWindowResize) {
window.removeEventListener("resize", this.onWindowResize);
}
},
methods: {
async show() {
this.visible = true;
@@ -238,6 +253,15 @@ export default {
overflow: hidden;
}
@media (max-width: 767px) {
.changelog-dialog .v-overlay__content {
border-radius: 0 !important;
max-height: 100dvh !important;
margin: 0 !important;
width: 100% !important;
}
}
.changelog-content {
@apply leading-relaxed !important;
}
@@ -2,14 +2,17 @@
<v-dialog
v-if="!isPage"
v-model="visible"
:fullscreen="isMobile"
:fullscreen="dialogFullscreen"
max-width="800"
scrollable
transition="dialog-bottom-transition"
class="tutorial-dialog"
persistent
@update:model-value="onVisibleUpdate"
>
<v-card class="flex flex-col h-full bg-white dark:bg-zinc-950 border-0 overflow-hidden relative">
<v-card
class="flex min-h-0 flex-1 flex-col bg-white dark:bg-zinc-950 border-0 overflow-hidden relative h-full max-h-[100dvh]"
>
<!-- Settings Controls -->
<div class="absolute top-4 left-4 z-50 flex items-center gap-1">
<LanguageSelector @language-change="onLanguageChange" />
@@ -41,7 +44,9 @@
</div>
<!-- Content Area -->
<v-card-text class="flex-1 overflow-y-auto px-6 md:px-12 py-10 relative">
<v-card-text
class="relative min-h-0 flex-1 overflow-y-auto overscroll-contain px-4 py-6 sm:px-6 md:px-12 md:py-10"
>
<transition name="fade-slide" mode="out-in">
<!-- Step 1: Welcome -->
<div v-if="currentStep === 1" key="step1" class="flex flex-col items-center text-center space-y-6">
@@ -619,7 +624,9 @@
<!-- Footer -->
<v-divider class="dark:border-zinc-900"></v-divider>
<v-card-actions class="px-6 py-6 bg-gray-50 dark:bg-zinc-950/50 flex justify-between">
<v-card-actions
class="shrink-0 flex justify-between bg-gray-50 px-4 py-4 pb-[max(1rem,env(safe-area-inset-bottom))] dark:bg-zinc-950/50 sm:px-6 sm:py-6"
>
<button
v-if="currentStep > 1 && currentStep < totalSteps"
type="button"
@@ -1421,14 +1428,15 @@ export default {
savingPropagation: false,
discoveryInterval: null,
markingSeen: false,
windowWidth: typeof window !== "undefined" ? window.innerWidth : 1024,
};
},
computed: {
isPage() {
return this.$route?.meta?.isPage === true;
},
isMobile() {
return window.innerWidth < 640;
dialogFullscreen() {
return this.windowWidth < 768;
},
config() {
return GlobalState.config;
@@ -1441,11 +1449,18 @@ export default {
},
},
beforeUnmount() {
if (this.onWindowResize) {
window.removeEventListener("resize", this.onWindowResize);
}
if (this.discoveryInterval) {
clearInterval(this.discoveryInterval);
}
},
mounted() {
this.onWindowResize = () => {
this.windowWidth = window.innerWidth;
};
window.addEventListener("resize", this.onWindowResize, { passive: true });
if (this.isPage) {
this.loadCommunityInterfaces();
this.loadDiscoveredInterfaces();
@@ -1735,6 +1750,15 @@ export default {
overflow: hidden;
}
@media (max-width: 767px) {
.tutorial-dialog .v-overlay__content {
border-radius: 0 !important;
max-height: 100dvh !important;
margin: 0 !important;
width: 100% !important;
}
}
.fade-slide-enter-active,
.fade-slide-leave-active {
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
@@ -5,7 +5,7 @@
'opacity-60 grayscale-[0.5]': !isInterfaceEnabled(iface) || iface._restart_required || !isReticulumRunning,
}"
>
<div class="flex flex-col sm:flex-row gap-4 sm:items-start relative">
<div class="flex flex-col sm:flex-row gap-4 sm:items-start relative pt-11 sm:pt-0">
<!-- Offline Overlay -->
<div
v-if="!isReticulumRunning"
@@ -82,20 +82,27 @@
</div>
</div>
<div
class="flex flex-col sm:flex-row gap-2 sm:items-center items-end sm:shrink-0 justify-end sm:justify-end relative"
class="absolute top-2 right-2 z-20 flex flex-row items-center gap-1 sm:static sm:z-auto sm:flex sm:flex-row sm:gap-2 sm:items-center sm:shrink-0 sm:justify-end"
>
<button
v-if="isInterfaceEnabled(iface)"
type="button"
class="secondary-chip text-xs shrink-0"
class="inline-flex items-center justify-center rounded-full p-2 min-w-[44px] min-h-[44px] sm:min-w-0 sm:min-h-0 shrink-0 border-0 bg-transparent text-gray-600 dark:text-zinc-300 hover:bg-gray-100 dark:hover:bg-zinc-800 transition-colors"
:title="$t('interface.disable')"
@click="disableInterface"
>
<MaterialDesignIcon icon-name="power" class="w-4 h-4" />
{{ $t("interface.disable") }}
<MaterialDesignIcon icon-name="power" class="w-5 h-5 sm:w-4 sm:h-4" />
<span class="hidden sm:inline sm:ml-1.5 text-xs font-semibold">{{ $t("interface.disable") }}</span>
</button>
<button v-else type="button" class="primary-chip text-xs shrink-0" @click="enableInterface">
<MaterialDesignIcon icon-name="power" class="w-4 h-4" />
{{ $t("interface.enable") }}
<button
v-else
type="button"
class="inline-flex items-center justify-center rounded-full p-2 min-w-[44px] min-h-[44px] sm:min-w-0 sm:min-h-0 shrink-0 border-0 bg-transparent text-green-600 dark:text-green-400 hover:bg-gray-100 dark:hover:bg-zinc-800 transition-colors"
:title="$t('interface.enable')"
@click="enableInterface"
>
<MaterialDesignIcon icon-name="power" class="w-5 h-5 sm:w-4 sm:h-4" />
<span class="hidden sm:inline sm:ml-1.5 text-xs font-semibold">{{ $t("interface.enable") }}</span>
</button>
<div class="relative z-50 shrink-0">
<DropDownMenu>
@@ -2,8 +2,8 @@
<div
class="flex flex-col flex-1 overflow-hidden min-w-0 bg-gradient-to-br from-slate-50 via-slate-100 to-white dark:from-zinc-950 dark:via-zinc-900 dark:to-zinc-900"
>
<div class="flex-1 overflow-y-auto w-full px-3 sm:px-5 md:px-8 py-4 sm:py-6">
<div class="space-y-0 w-full max-w-6xl xl:max-w-7xl 2xl:max-w-[90rem] mx-auto flex-1">
<div class="flex-1 overflow-y-auto overflow-x-hidden w-full px-3 sm:px-5 md:px-8 py-4 sm:py-6">
<div class="space-y-0 w-full min-w-0 max-w-6xl xl:max-w-7xl 2xl:max-w-[90rem] mx-auto flex-1">
<div
v-if="showRestartReminder"
class="bg-amber-600 text-white border border-amber-500/30 p-4 sm:rounded-xl flex flex-wrap gap-3 items-center mb-4 sm:mb-6"
@@ -176,8 +176,8 @@
</div>
<div class="interfaces-subpanel space-y-3">
<div class="flex items-center gap-3">
<div class="flex-1">
<div class="flex flex-col gap-3 min-w-0 lg:flex-row lg:items-start lg:justify-between">
<div class="min-w-0 flex-1">
<div class="text-xs uppercase tracking-wide text-gray-500 dark:text-gray-400">
Discovered Interfaces
</div>
@@ -194,7 +194,7 @@
a green pill; disconnected entries are dimmed with a red label.
</div>
</div>
<div class="flex gap-2 flex-wrap items-center">
<div class="flex flex-wrap gap-2 items-center shrink-0 min-w-0">
<div class="flex gap-1.5 mr-2">
<button
type="button"
@@ -242,15 +242,17 @@
<div
v-else
class="grid gap-4 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 3xl:grid-cols-5 4xl:grid-cols-6"
class="grid gap-4 min-w-0 grid-cols-1 sm:grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 3xl:grid-cols-5 4xl:grid-cols-6"
>
<div
v-for="iface in sortedDiscoveredInterfaces"
:key="iface.discovery_hash || iface.name"
class="interface-card group transition-all duration-300"
class="interface-card group transition-all duration-300 min-w-0"
:class="{ 'opacity-70 grayscale-[0.3]': !isDiscoveredConnected(iface) }"
>
<div class="flex gap-4 items-start relative">
<div
class="flex flex-col gap-3 sm:flex-row sm:gap-4 sm:items-start relative min-w-0"
>
<!-- Disconnected Overlay -->
<div
v-if="!isDiscoveredConnected(iface)"
@@ -395,7 +397,9 @@
</div>
</div>
<div class="flex flex-col gap-2 shrink-0">
<div
class="flex flex-row sm:flex-col gap-2 shrink-0 self-end sm:self-auto justify-end"
>
<div class="relative">
<button
type="button"
@@ -463,8 +467,8 @@
Configure Per-Interface
</RouterLink>
</div>
<div class="grid gap-4 md:grid-cols-2">
<div class="space-y-2 text-sm text-gray-700 dark:text-gray-300">
<div class="grid gap-4 min-w-0 md:grid-cols-2">
<div class="space-y-2 text-sm text-gray-700 dark:text-gray-300 min-w-0">
<div class="font-semibold text-gray-900 dark:text-white">Publish (Server)</div>
<div>
Enable discovery while adding or editing an interface to broadcast reachable
@@ -475,9 +479,9 @@
but usually recommended so peers can connect back.
</div>
</div>
<div class="space-y-3">
<div class="flex items-center">
<div class="flex flex-col mr-auto">
<div class="space-y-3 min-w-0">
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div class="flex flex-col min-w-0 pr-0 sm:pr-4">
<div class="text-sm font-semibold text-gray-900 dark:text-white">
Discover Interfaces (Peer)
</div>
@@ -486,9 +490,12 @@
interfaces.
</div>
</div>
<Toggle v-model="discoveryConfig.discover_interfaces" class="my-auto mx-2" />
<Toggle
v-model="discoveryConfig.discover_interfaces"
class="shrink-0 sm:my-auto"
/>
</div>
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2 min-w-0">
<div>
<div class="text-xs font-semibold text-gray-700 dark:text-gray-200">
Allowed Sources
@@ -2,32 +2,28 @@
<div class="flex flex-col h-full w-full bg-white dark:bg-zinc-950 overflow-hidden">
<!-- header -->
<div
class="flex items-center px-4 py-2 border-b border-gray-200 dark:border-zinc-800 bg-white/80 dark:bg-zinc-900/80 backdrop-blur z-10 relative"
class="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-0 px-3 py-2 sm:px-4 border-b border-gray-200 dark:border-zinc-800 bg-white/80 dark:bg-zinc-900/80 backdrop-blur z-10 relative"
>
<div class="flex items-center space-x-2">
<v-icon icon="mdi-map" class="text-blue-500 dark:text-blue-400" size="24"></v-icon>
<h1 class="text-xl font-black text-gray-900 dark:text-white">{{ $t("map.title") }}</h1>
<div class="flex items-center min-w-0 gap-2">
<v-icon icon="mdi-map" class="text-blue-500 dark:text-blue-400 shrink-0" size="24"></v-icon>
<h1 class="text-lg sm:text-xl font-black text-gray-900 dark:text-white truncate">
{{ $t("map.title") }}
</h1>
</div>
<div class="ml-auto flex items-center space-x-2">
<button
v-if="!isPopoutMode"
type="button"
class="hidden sm:flex items-center gap-1.5 px-2.5 py-1.5 text-sm font-medium text-gray-600 dark:text-zinc-300 hover:bg-gray-100 dark:hover:bg-zinc-800 rounded-lg transition-colors"
:title="$t('map.pop_out')"
@click="openMapPopout"
>
<MaterialDesignIcon icon-name="open-in-new" class="size-4" />
<span>{{ $t("map.pop_out") }}</span>
</button>
<div
class="flex flex-wrap items-center gap-x-1.5 gap-y-2 sm:ml-auto sm:flex-nowrap sm:gap-2 sm:justify-end min-w-0"
>
<!-- offline/online toggle -->
<div class="flex items-center bg-gray-100 dark:bg-zinc-800 rounded-lg p-1">
<div
class="flex items-center bg-gray-100 dark:bg-zinc-800 rounded-lg p-0.5 sm:p-1 min-w-0 max-w-full"
>
<button
class="p-2 rounded-lg text-gray-500 hover:bg-gray-200 dark:hover:bg-zinc-700 transition-colors mr-1"
class="p-1.5 sm:p-2 rounded-lg text-gray-500 hover:bg-gray-200 dark:hover:bg-zinc-700 transition-colors shrink-0"
title="Map Discovered Interfaces"
@click="mapDiscoveredNodes"
>
<MaterialDesignIcon icon-name="map-marker-radius" class="size-5" />
<MaterialDesignIcon icon-name="map-marker-radius" class="size-[18px] sm:size-5" />
</button>
<button
:class="
@@ -35,7 +31,7 @@
? 'bg-white dark:bg-zinc-700 shadow-sm text-blue-600 dark:text-blue-400'
: 'text-gray-500 dark:text-gray-300'
"
class="px-3 py-1 text-sm font-medium rounded-md transition-all"
class="px-2 py-1 text-xs sm:px-3 sm:text-sm font-medium rounded-md transition-all shrink-0"
@click="toggleOffline(false)"
>
{{ $t("map.online_mode") }}
@@ -46,7 +42,7 @@
? 'bg-white dark:bg-zinc-700 shadow-sm text-blue-600 dark:text-blue-400'
: 'text-gray-500 dark:text-gray-300'
"
class="px-3 py-1 text-sm font-medium rounded-md transition-all"
class="px-2 py-1 text-xs sm:px-3 sm:text-sm font-medium rounded-md transition-all shrink-0"
:disabled="!hasOfflineMap"
@click="toggleOffline(true)"
>
@@ -54,22 +50,34 @@
</button>
</div>
<!-- upload button (desktop only) -->
<!-- upload: icon on mobile, full label from sm -->
<button
class="hidden sm:flex items-center space-x-1 px-3 py-1.5 bg-blue-500 hover:bg-blue-600 text-white rounded-lg shadow-sm transition-colors text-sm font-medium"
type="button"
class="inline-flex items-center justify-center sm:gap-1 p-2 sm:px-3 sm:py-1.5 bg-blue-500 hover:bg-blue-600 text-white rounded-lg shadow-sm transition-colors text-sm font-medium shrink-0"
:title="$t('map.upload_mbtiles')"
@click="$refs.fileInput.click()"
>
<MaterialDesignIcon icon-name="upload" class="size-4" />
<span>{{ $t("map.upload_mbtiles") }}</span>
<MaterialDesignIcon icon-name="upload" class="size-[18px] sm:size-4" />
<span class="hidden sm:inline">{{ $t("map.upload_mbtiles") }}</span>
</button>
<input ref="fileInput" type="file" accept=".mbtiles" class="hidden" @change="onFileSelected" />
<button
v-if="!isPopoutMode"
type="button"
class="flex p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-zinc-800 rounded-full transition-colors shrink-0"
:title="$t('map.pop_out')"
@click="openMapPopout"
>
<MaterialDesignIcon icon-name="open-in-new" class="size-[18px] sm:size-5" />
</button>
<!-- settings button -->
<button
class="p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-zinc-800 rounded-full transition-colors"
type="button"
class="p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-zinc-800 rounded-full transition-colors shrink-0"
@click="isSettingsOpen = !isSettingsOpen"
>
<MaterialDesignIcon icon-name="cog" class="size-5" />
<MaterialDesignIcon icon-name="cog" class="size-[18px] sm:size-5" />
</button>
</div>
</div>
@@ -1545,13 +1545,13 @@
</div>
<!-- text input + send -->
<div class="flex items-end gap-2">
<div class="flex items-center gap-2">
<textarea
id="message-input"
ref="message-input"
v-model="newMessageText"
:readonly="isTranslatingMessage"
class="bg-white dark:bg-zinc-900 border border-gray-200 dark:border-zinc-800 text-gray-900 dark:text-zinc-100 text-sm rounded-xl focus:ring-2 focus:ring-blue-500/50 focus:border-blue-500 block w-full min-w-0 flex-1 px-3 sm:px-4 py-2 resize-none shadow-sm transition-all placeholder:text-gray-400 dark:placeholder:text-zinc-500 min-h-[44px] max-h-[200px] overflow-y-auto"
class="bg-white dark:bg-zinc-900 border border-gray-200 dark:border-zinc-800 text-gray-900 dark:text-zinc-100 text-sm rounded-xl focus:ring-2 focus:ring-blue-500/50 focus:border-blue-500 block w-full min-w-0 flex-1 px-3 sm:px-4 py-2.5 resize-none shadow-sm transition-all placeholder:text-gray-400 dark:placeholder:text-zinc-500 min-h-[44px] max-h-[200px] overflow-y-auto leading-snug"
rows="1"
spellcheck="true"
:placeholder="$t('messages.send_placeholder')"
@@ -1559,7 +1559,7 @@
@keydown.enter.shift.exact.prevent="onShiftEnterPressed"
@paste="onMessagePaste"
></textarea>
<div class="shrink-0 pb-0.5">
<div class="shrink-0 flex items-center">
<SendMessageButton
:is-sending-message="false"
:can-send-message="canSendMessage"
@@ -5,8 +5,8 @@
<div class="flex-1 overflow-y-auto w-full px-4 md:px-8 py-6">
<div class="space-y-6 w-full max-w-4xl mx-auto">
<!-- header -->
<div class="flex items-center justify-between flex-wrap gap-4">
<div>
<div class="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
<div class="min-w-0">
<h1 class="text-3xl font-bold text-gray-900 dark:text-white tracking-tight">
{{ $t("identities.title") }}
</h1>
@@ -14,31 +14,33 @@
{{ $t("identities.manage") }}
</p>
</div>
<div class="flex items-center gap-2">
<div
class="flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:flex-wrap sm:items-stretch sm:justify-end"
>
<button
type="button"
class="inline-flex items-center gap-x-2 rounded-2xl bg-gradient-to-r from-blue-500 via-indigo-500 to-purple-500 px-5 py-2.5 text-sm font-semibold text-white shadow-lg hover:shadow-indigo-500/25 transition-all active:scale-95"
class="inline-flex w-full items-center justify-center gap-x-2 min-h-[44px] rounded-xl bg-blue-600 px-4 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 transition-all active:scale-[0.98] sm:w-auto sm:min-h-0 sm:rounded-2xl"
@click="showCreateModal = true"
>
<MaterialDesignIcon icon-name="plus" class="w-5 h-5" />
{{ $t("identities.new_identity") }}
<MaterialDesignIcon icon-name="plus" class="w-5 h-5 shrink-0" />
<span class="truncate">{{ $t("identities.new_identity") }}</span>
</button>
<button
type="button"
class="inline-flex items-center gap-x-2 rounded-2xl border border-gray-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 px-5 py-2.5 text-sm font-semibold text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-zinc-700 transition-all active:scale-95"
class="inline-flex w-full items-center justify-center gap-x-2 min-h-[44px] rounded-xl border border-gray-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 px-4 py-2.5 text-sm font-semibold text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-zinc-700 transition-all active:scale-[0.98] sm:w-auto sm:min-h-0 sm:rounded-2xl"
@click="showImportModal = true"
>
<MaterialDesignIcon icon-name="upload" class="w-5 h-5" />
{{ $t("identities.import") }}
<MaterialDesignIcon icon-name="upload" class="w-5 h-5 shrink-0" />
<span class="truncate">{{ $t("identities.import") }}</span>
</button>
<button
type="button"
class="inline-flex items-center gap-x-2 rounded-2xl border border-gray-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 px-5 py-2.5 text-sm font-semibold text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-zinc-700 transition-all active:scale-95"
class="inline-flex w-full items-center justify-center gap-x-2 min-h-[44px] rounded-xl border border-gray-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 px-4 py-2.5 text-sm font-semibold text-gray-700 dark:text-gray-200 hover:bg-gray-50 dark:hover:bg-zinc-700 transition-all active:scale-[0.98] disabled:opacity-50 sm:w-auto sm:min-h-0 sm:rounded-2xl"
:disabled="identities.length === 0"
@click="downloadAllIdentities"
>
<MaterialDesignIcon icon-name="file-export" class="w-5 h-5" />
{{ $t("identities.export_all") }}
<MaterialDesignIcon icon-name="file-export" class="w-5 h-5 shrink-0" />
<span class="truncate">{{ $t("identities.export_all") }}</span>
</button>
</div>
</div>
@@ -141,21 +143,21 @@
</span>
</div>
<div
class="text-xs font-mono text-gray-500 dark:text-zinc-500 truncate mt-0.5 tracking-tight"
class="text-xs font-mono text-gray-500 dark:text-zinc-500 mt-0.5 tracking-tight break-all sm:truncate"
:title="'RNS: ' + identity.hash"
>
ID: {{ identity.hash }}
</div>
<div
v-if="identity.lxmf_address"
class="text-[10px] font-mono text-gray-400 dark:text-zinc-600 truncate mt-0.5 tracking-tighter"
class="text-[10px] font-mono text-gray-400 dark:text-zinc-600 mt-0.5 tracking-tighter break-all sm:truncate"
:title="'LXMF: ' + identity.lxmf_address"
>
LXMF: {{ identity.lxmf_address }}
</div>
<div
v-if="identity.lxst_address"
class="text-[10px] font-mono text-gray-400 dark:text-zinc-600 truncate mt-0.5 tracking-tighter"
class="text-[10px] font-mono text-gray-400 dark:text-zinc-600 mt-0.5 tracking-tighter break-all sm:truncate"
:title="'LXST: ' + identity.lxst_address"
>
LXST: {{ identity.lxst_address }}
@@ -98,7 +98,7 @@
<!-- search bar -->
<div
class="sticky top-0 z-10 py-3 sm:py-4 -mx-3 sm:mx-0 px-3 sm:px-0 mb-2 bg-gradient-to-br from-slate-50/95 via-slate-100/95 to-white/95 dark:from-zinc-950/95 dark:via-zinc-900/95 dark:to-zinc-900/95 backdrop-blur-sm border-b border-gray-200/50 dark:border-zinc-800/50"
class="sticky top-0 z-10 py-3 sm:py-4 -mx-3 sm:mx-0 px-3 sm:px-0 mb-2 border-b border-gray-200/50 dark:border-zinc-800/50 bg-gradient-to-br from-slate-50 via-slate-100 to-white dark:from-zinc-950 dark:via-zinc-900 dark:to-zinc-900 [box-shadow:0_1px_0_0_rgb(255_255_255_/_0.6)_inset] dark:[box-shadow:0_1px_0_0_rgb(39_39_42_/_0.5)_inset]"
>
<div class="relative w-full max-w-6xl xl:max-w-7xl 2xl:max-w-[90rem] mx-auto">
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">