diff --git a/frontend/src/app.css b/frontend/src/app.css index f1c1ca1..4bbf0b5 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -183,3 +183,29 @@ a:focus-visible { .font-mono { font-family: var(--font-mono); } + +/* Card highlight for cross-entity navigation */ +@keyframes cardHighlight { + 0%, 100% { box-shadow: none; } + 25%, 75% { box-shadow: 0 0 0 3px var(--color-primary), 0 0 20px color-mix(in srgb, var(--color-primary) 30%, transparent); } +} + +.card-highlight { + animation: cardHighlight 2s ease-in-out; + position: relative; + z-index: 11; +} + +.nav-dim-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.4); + z-index: 10; + pointer-events: none; + opacity: 0; + transition: opacity 0.3s ease-in-out; +} + +.nav-dim-overlay.active { + opacity: 1; +} diff --git a/frontend/src/lib/components/Card.svelte b/frontend/src/lib/components/Card.svelte new file mode 100644 index 0000000..c90c08d --- /dev/null +++ b/frontend/src/lib/components/Card.svelte @@ -0,0 +1,31 @@ + + +
{trk.name}
-{(tracker.collection_ids || []).length} {t('notificationTracker.albums_count')} · {t('notificationTracker.every')} {tracker.scan_interval}s · {(tracker.tracker_targets || []).length} {t('notificationTracker.linkedTargets')} diff --git a/frontend/src/routes/providers/+page.svelte b/frontend/src/routes/providers/+page.svelte index f214d09..761e37c 100644 --- a/frontend/src/routes/providers/+page.svelte +++ b/frontend/src/routes/providers/+page.svelte @@ -13,6 +13,7 @@ import ConfirmModal from '$lib/components/ConfirmModal.svelte'; import IconButton from '$lib/components/IconButton.svelte'; import { snackSuccess, snackError } from '$lib/stores/snackbar.svelte'; + import { highlightFromUrl } from '$lib/highlight'; import type { ServiceProvider } from '$lib/types'; let providers = $derived(providersCache.items); @@ -34,7 +35,7 @@ loadError = ''; } catch (err: any) { loadError = err.message || t('providers.loadError'); - } finally { loaded = true; } + } finally { loaded = true; highlightFromUrl(); } // Ping all providers in background for (const p of providers) { health = { ...health, [p.id]: null }; @@ -151,7 +152,7 @@ {:else}
{target.name}
{#if !activeType}{target.type}{/if} {#if target.receiver_count}{target.receiver_count} receiver(s){/if} - {#if getBotName(target)}{#if target.type === 'telegram'} diff --git a/frontend/src/routes/telegram-bots/+page.svelte b/frontend/src/routes/telegram-bots/+page.svelte index 4e79528..06c2c37 100644 --- a/frontend/src/routes/telegram-bots/+page.svelte +++ b/frontend/src/routes/telegram-bots/+page.svelte @@ -14,6 +14,7 @@ import ConfirmModal from '$lib/components/ConfirmModal.svelte'; import IconButton from '$lib/components/IconButton.svelte'; import { snackSuccess, snackError, snackInfo } from '$lib/stores/snackbar.svelte'; + import { highlightFromUrl } from '$lib/highlight'; import type { TelegramBot, TelegramChat, EmailBot, MatrixBot } from '$lib/types'; type BotTab = 'telegram' | 'email' | 'matrix'; @@ -51,7 +52,7 @@ matrixBotsCache.fetch(true), ]); } catch (err: any) { error = err.message || t('common.loadError'); snackError(error); } - finally { loaded = true; } + finally { loaded = true; highlightFromUrl(); } } function openNew() { form = { name: '', icon: '', token: '' }; editing = null; showForm = true; } @@ -390,7 +391,7 @@ {:else}