563716fa76
- Add $state-based entity cache layer with 30s TTL, request deduplication, and local mutation helpers (entity-cache.svelte.ts + caches.svelte.ts) - Wire all 10 page components to use shared caches for cross-page data - Add slide animation for nav tree expand/collapse with rotating chevron - Remove aggregate count badges from container nav nodes (keep on leaves) - Convert Targets from flat leaf to group with per-type children (Telegram, Webhook, Email, Discord, Slack, ntfy, Matrix) - Add URL-based type filtering on Targets page with per-type descriptions - Add Bots group children for Email and Matrix alongside Telegram - Tab-based routing for bots page (?tab=telegram/email/matrix) - Add per-type target counts and email/matrix bot counts to /status/counts - Split CLAUDE.md into focused context files under .claude/docs/ - Fix .gitignore: scope lib/ to root, allow .claude/docs/ tracking - Clear all caches on logout - Reset form state when switching target type tabs
2.5 KiB
2.5 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()