- Fix bot card header overflow by replacing "Sync with Telegram" text button with icon button, add flex-wrap - Rename sync button label to "Sync Commands" - Remove decorative dashes from selector placeholders (— X — → X) - Show selected provider name/icon in dashboard stat card when global provider filter is active - Add selector placeholder convention to frontend-architecture.md
2.9 KiB
Frontend Architecture Notes
Stack
- SvelteKit 2 + Svelte 5 + Tailwind CSS v4 — Static adapter with SPA fallback. Dev proxy to :8420.
- Tailwind CSS v4: Uses
@themedirective inapp.cssfor CSS variables.
Svelte 5 Runes
$stateonly works in.svelteand.svelte.tsfiles. Regular.tsfiles cannot use runes — use plain variables instead.
i18n
- Uses
$staterune in.svelte.tsfile. Locale auto-detects from localStorage. t()is reactive via$state.setLocale()updates immediately without page reload.
Auth Flow
- After login/setup, use
window.location.href = '/'(hard redirect), NOTgoto('/').
Overlays
IMPORTANT: Overlays (modals, dropdowns, pickers) MUST use position: fixed with inline styles and z-index: 9999. Tailwind CSS v4 fixed/absolute classes do NOT work reliably inside flex/overflow containers in this project. Always calculate position from getBoundingClientRect() for dropdowns, or use top:0;left:0;right:0;bottom:0 for full-screen backdrops.
Entity Cache System
Shared entities use a $state-based cache layer in frontend/src/lib/stores/:
entity-cache.svelte.ts— Generic cache factory with 30s TTL, request deduplication, and local mutation helpers (upsert,remove,set).caches.svelte.ts— Singleton caches for:providersCache,targetsCache,trackingConfigsCache,templateConfigsCache,telegramBotsCache,emailBotsCache,matrixBotsCache,commandConfigsCache,commandTemplateConfigsCache.
How pages use caches
- Cross-page references (e.g. providers on the Trackers page): Use
$derived(providersCache.items)and callprovidersCache.fetch()inload(). Cached data is returned instantly if fresh (<30s). - Owning pages (e.g. Providers page itself): Use
$derived(providersCache.items)and callprovidersCache.fetch(true)(force refresh) inload(). - After mutations (create/update/delete): The owning page calls
cache.invalidate()thenload()to force-refresh. Other pages pick up changes on next navigation via TTL expiry. - On logout:
clearAllCaches()is called to wipe all cached data.
Adding a new cached entity
- Add a type to
frontend/src/lib/types.ts - Add
export const fooCache = createEntityCache<Foo>('/foo');tocaches.svelte.ts - Add
fooCache.clear()toclearAllCaches() - In page components: replace
let foo = $state<Foo[]>([])withlet foo = $derived(fooCache.items)and replaceapi('/foo')withfooCache.fetch()
UI Conventions
Selector Placeholders
IMPORTANT: Selector/dropdown placeholder text must be plain and simple — no decorative dashes or special characters. Use Select provider... or Tracking Configs, NOT — Select provider — or -- Tracking Configs --. The i18n keys already follow this convention; never wrap them with '— ' + t('key') + ' —' in Svelte templates.