Commit graph

2433 commits

Author SHA1 Message Date
Till JS
f7ee9ead44 fix(branding): rename ManaContacts to Kontakte
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 11:38:53 +02:00
Till JS
019f3eb9fd feat(manacore/web): show tags in detail views with click-to-remove
Display assigned tags as colored pills in todo, calendar, contacts
DetailViews. Clicking a tag pill removes it from the item. Hover turns
the X icon red for clear affordance.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 11:37:41 +02:00
Till JS
794424d6ad fix(ui): open all AppDrawer apps in new tab
All app clicks in the PillNav app drawer now open in a new tab via
window.open('_blank'). Previously internal URLs used window.location.href
(same tab navigation) which was confusing in the unified app context.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 11:36:26 +02:00
Till JS
0d142efa3c feat(manacore/web): show tags as labeled pills instead of dots
Replace tiny 6px tag dots with readable pills showing colored dot +
tag name. Uses color-mix for subtle tinted background per tag color.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 00:30:44 +02:00
Till JS
e9d4cbfdba fix(manacore/web): fix tag drag-and-drop — use reactive .value instead of .subscribe()
useAllTags() returns a Svelte 5 runes object with .value, not an
Observable with .subscribe(). Also fix getTagsByIds() calls to pass
the allTags array as first argument.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 00:26:22 +02:00
Till JS
976fb5fdf8 fix(ui): move nav toggle to right side of InputBar, make it larger
- Add rightAction snippet prop to QuickInputBar (InputBar.svelte)
- Move toggle from leftAction to rightAction (renders after submit button)
- Increase toggle size from 28px to 36px for better tap target

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 00:21:47 +02:00
Till JS
06ebc6271d feat(manacore/web): add tag drag-and-drop to workbench pages
- Mount DragPreview + draggable tag bar on workbench homepage
- Add dropTarget on list items in todo, calendar, contacts ListViews
- Show assigned tags as colored dots on list items
- Tags can be dragged from the tag bar onto any item to assign them
- Drop target highlights with module-colored outline on hover

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 00:20:54 +02:00
Till JS
fb5271acc8 fix(ui): move PillNav toggle inside QuickInputBar via leftAction snippet
Toggle button now renders inside the InputBar pill (left side) instead of
as a separate element next to it. Uses the existing leftAction snippet prop.

- Button is 28px circle, subtle background, no border/shadow (lives inside pill)
- Remove bottom-stack-row wrapper (no longer needed)
- Cleaner visual: toggle is part of the input bar, not floating beside it

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 00:15:29 +02:00
Till JS
81f781c133 fix(ui): move Tags to leftmost in PillNav, match toggle button to InputBar style
- Tags pill moved to first position in nav items (leftmost)
- Toggle button: 44px round (matches InputBar height), same border-radius
  (9999px), same backdrop-filter blur, same box-shadow, theme-aware colors
- Button sits directly next to QuickInputBar with no gap, vertically centered

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 00:08:24 +02:00
Till JS
b3dd8cded9 fix(ui): dynamic bottom-chrome-height for tabs, notifications, main content
The bottom-stack container now exports its height as --bottom-chrome-height
CSS custom property (via bind:clientHeight + $effect on :root). All elements
that need to position above the bottom chrome read this variable:

- PageCarousel minimized-tabs: bottom: var(--bottom-chrome-height)
- NotificationBar: bottom: var(--bottom-chrome-height)
- Main content padding: calc(var(--bottom-chrome-height) + 2rem)

This ensures tabs, notifications, and content never overlap with the
bottom stack regardless of PillNav collapse state or TagStrip visibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 23:59:16 +02:00
Till JS
b415567dfa refactor(ui): unified bottom-stack container for PillNav, QuickInput, TagStrip
Replace 4 independent position:fixed elements with one flex container that
stacks them naturally from bottom to top. Elements push each other
automatically — no more hardcoded offsets or z-index conflicts.

Stack order (bottom → top):
1. PillNavigation (collapsible)
2. TagStrip (togglable)
3. QuickInputBar + toggle button row

Shared-UI changes:
- PillNavigation: add positioning='fixed'|'static' prop
- QuickInputBar: add positioning='fixed'|'static' prop
- TagStrip: add positioning='fixed'|'static' prop
- All default to 'fixed' for backward compatibility

Layout changes:
- Wrap all bottom elements in .bottom-stack (position:fixed, flex-column)
- Remove hardcoded bottomOffset calculations
- Toggle button is now inline next to QuickInputBar (not separately positioned)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 23:52:40 +02:00
Till JS
bed2060ba5 fix(manacore/web): fix height resize using element's current height as start value
When heightPx is not yet set, use the element's actual offsetHeight
instead of 0 so the first drag correctly adjusts height.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 23:51:54 +02:00
Till JS
b66a26810f feat(manacore/web): add 2D resize (width + height) to workbench pages
Extend PageShell resize handle to support diagonal drag for both
width and height. Height is persisted per page in workbench settings.
Resize cursor changed from ew-resize to nwse-resize.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 23:49:39 +02:00
Till JS
ef0c834a2b feat(ui): add PillNav toggle button next to QuickInputBar
Small floating button (▼/▲) at the bottom-right that toggles PillNav
visibility. QuickInputBar's bottomOffset adjusts dynamically when nav
is collapsed (70px → 12px), reclaiming vertical space.

- Button uses existing handleCollapsedChange() with localStorage persistence
- Smooth transitions on position and rotation
- Glass-morphism style (blur + semi-transparent background)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 23:45:43 +02:00
Till JS
9ea7e482f0 refactor(manacore/web): rename AppView → ListView across all 24 modules
Consistent naming: ListView.svelte + DetailView.svelte as a pair.
Update app-registry.ts and splitscreen/registry.ts imports.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 23:45:11 +02:00
Till JS
fed38efb8b fix(sync): fix SSE live updates — 2 bugs found during E2E testing
Bug 1: NotifyUser() early-returned when no WebSocket clients existed,
skipping SSE subscriber notifications entirely. Fixed by restructuring
to check WS clients and SSE subscribers independently.

Bug 2: SSE stream cursor defaulted to client's `since` parameter when
no initial data existed. If `since` was in the future (or very recent),
live updates had created_at < cursor and were silently filtered out.
Fixed by defaulting cursor to now() when no initial data is returned.

Bug 3: NotifyUser used original sseSubs slice instead of sseSubsCopy
after releasing the read lock (race condition).

Verified E2E: Push from client A → SSE stream on client B receives
live change event with correct data within ~1 second.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 23:39:46 +02:00
Till JS
4cb1bda852 feat(manacore/web): add overlay detail views for cards, storage, presi
Add inline-editable DetailViews with auto-save for:
- cards: deck details with color, description, public toggle
- storage: file details with rename, favorite, size/type info
- presi: presentation deck details with slide count

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 23:26:57 +02:00
Till JS
650dea5e1d feat(manacore/web): add overlay detail views for 8 more modules
Add inline-editable DetailViews with auto-save for:
planta, inventar, skilltree, memoro, questions, uload, mukke, citycorners

Wire AppView list items to open overlay via navigate() with sibling
navigation support. Fix citycorners table names (cityLocations→ccLocations).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:57:09 +02:00
Till JS
a08f1501f2 feat(manacore/web): add overlay detail views with inline editing, consolidate routes
- Remove /dashboard (redundant), merge /home into / (app root)
- Update all redirects and navigation references accordingly
- Add panel navigation stack with overlay detail views for workbench
- Implement inline-editable DetailViews for todo, calendar, contacts
- Auto-save on blur, prev/next navigation with sibling arrows
- Fix minimized tabs z-index behind quick input bar
- Fix showNewEvent undefined error in calendar AppView

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:43:05 +02:00
Till JS
c8daa443fc feat(sync): replace WebSocket with SSE client for real-time sync
Client now connects to GET /sync/{appId}/stream via fetch + ReadableStream
instead of WebSocket + HTTP pull. Each app gets its own SSE connection that
delivers initial sync data + live updates in one persistent stream.

Changes:
- Remove WebSocket connection (connectUnifiedWs)
- Add connectSSE() per app using fetch + ReadableStream
- Parse SSE events (changes, heartbeat) from streamed response
- Auto-reconnect on disconnect with WS_RECONNECT_DELAY
- Fallback to polling if SSE endpoint not available
- ensureAppSynced() connects SSE for lazy apps on first visit
- handleOnline() reconnects all active SSE streams
- handleOffline() aborts all SSE connections

Benefits: 1 connection instead of 2 (WS + HTTP), data delivered instantly
without notification → pull round-trip, works through HTTP proxies/CDN.

Push (POST /sync/{appId}) remains unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:27:30 +02:00
Till JS
068a64b275 feat(sync): add SSE streaming endpoint for real-time sync
New endpoint GET /sync/{appId}/stream sends Server-Sent Events with
change data directly, replacing the WebSocket notification + HTTP pull
round-trip pattern.

Server (Go):
- HandleStream() in handler.go: SSE endpoint with initial sync + live streaming
- Hub.Subscribe()/Unsubscribe() in hub.go: channel-based SSE subscriber system
- Notification type for type-safe SSE events
- convertChanges() helper extracted from duplicated code
- WriteTimeout set to 0 for SSE long-lived connections

Protocol: Client connects to /sync/{appId}/stream?collections=a,b&since=...
Server sends initial changes, then streams live changes as other clients sync.
Heartbeat every 30s keeps connection alive. Push still uses POST /sync/{appId}.

WebSocket remains available as fallback (not removed).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:24:10 +02:00
Till JS
8ba3c4c10d feat(sync): partial sync — lazy collection loading on module visit
Only sync eager apps at startup (manacore, todo, calendar, contacts,
tags, links — needed for dashboard widgets). All other apps are lazy:
their collections sync on first module route visit.

Reduces startup pull requests from ~108 to ~20-30. Lazy apps get
synced when the user navigates to their module via ensureAppSynced().

- Add EAGER_APPS config set in sync.ts
- startAll() only starts pull for eager apps
- ensureAppSynced() starts pull + periodic sync for lazy apps
- Route-based trigger in +layout.svelte $effect

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:19:20 +02:00
Till JS
f7f5c9eb3a feat(sync): add pull pagination with hasMore flag
Server now returns hasMore: true when there are more than 1000 changes
pending for a collection. Client continues pulling in a loop until
hasMore is false, using the last row's timestamp as cursor.

Prevents data loss after long offline periods where >1000 changes
accumulated for a single collection.

Server changes (Go):
- GetChangesSince() accepts limit parameter
- HandlePull() fetches limit+1, trims, sets hasMore
- SyncedUntil uses last row's timestamp when paginating

Client changes (TypeScript):
- Pull loop: while (hasMore) { fetch → apply → advance cursor }
- Cursor only persisted after all pages fetched

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:17:20 +02:00
Till JS
03434c2802 refactor(auth): absorb shared-auth-stores into shared-auth-ui
Merge the auth store factories (createManaAuthStore, createAuthStore) from
@manacore/shared-auth-stores into @manacore/shared-auth-ui, reducing
from 3 auth packages to 2.

- Copy store files into shared-auth-ui/src/stores/
- Re-export store factories and types from shared-auth-ui
- Update imports in manacore/web and arcade/web
- Remove shared-auth-stores from active package.json dependencies

Result: @manacore/shared-auth (core, platform-agnostic) +
        @manacore/shared-auth-ui (Svelte components + stores)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:43:42 +02:00
Till JS
baca70155a fix(contacts): create self-contact in guest mode too
ensureSelfContact now works without profile data — creates a minimal
"Ich" contact in guest/local mode, syncs with profile when authenticated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:42:46 +02:00
Till JS
31d168c02b feat(contacts): add self-contact with profile sync and "Mein Profil" page
- Auto-create a self-contact (fixed ID) from the user's auth profile
  on first visit to /contacts, synced on each load
- Pin self-contact to top of all contact lists with "Du" badge
- Add "Mein Profil" carousel page showing a profile card view
- Add page option to ContactPagePicker

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:39:54 +02:00
Till JS
3556fc18be chore: archive 17 standalone app servers (replaced by unified API)
All app compute servers have been consolidated into apps/api/ (unified
Hono/Bun server). Old servers moved to apps/*/apps/server-archived/.

Archived: cards, chat, contacts, context, calendar, guides, moodlit,
mukke, news, nutriphi, picture, planta, presi, questions, storage, todo, traces

Still active: uload (separate domain), memoro (Supabase-based)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:37:45 +02:00
Till JS
9363063cd7 feat(api): port remaining 12 modules to unified API server
Complete consolidation of all 15 app servers into one Hono/Bun process.

Modules added: chat, context, picture, storage, todo, planta, nutriphi,
guides, moodlit, news, traces, presi

Total: 15 modules, one server, one port (3050), ~2400 LOC.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:34:08 +02:00
Till JS
eb97378438 feat(manacore/web): add page carousel to contacts module
Add ContactPage and ContactPagePicker components using the shared
PageCarousel/PageShell system. Available pages: Alle Kontakte,
Favoriten, Bald Geburtstag, Mit E-Mail, Mit Telefon, Mit Unternehmen,
Mit Adresse, Kürzlich hinzugefügt. Default opens "Alle" + "Favoriten".

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:21:07 +02:00
Till JS
aa93c54391 feat(api): create unified API server with first 3 modules
New consolidated Hono/Bun API server at apps/api/ that replaces individual
app servers. One process, one port, one auth middleware, one container.

Modules ported:
- calendar: RRULE expansion, ICS import, Google Calendar (stub)
- contacts: avatar upload (S3), vCard import/parsing
- mukke: audio upload/download presigned URLs, batch cover art

Architecture: each module registers routes under /api/v1/{module}/*
using the shared-hono middleware stack (auth, rate limit, error handler).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:12:15 +02:00
Till JS
079015ade7 refactor(manacore/web): unify page carousel system, remove edit mode
Extract shared PageShell and PageCarousel components from duplicated
workbench/todo code. Remove edit mode (FAB, width pills, move buttons)
from both routes. Add per-page resize handle (drag bottom-right corner).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 20:55:54 +02:00
Till JS
9e078492a5 docs: update CLAUDE.md for unified app architecture
- Rewrite monorepo overview to describe unified web app as primary interface
- Restructure projects table: unified modules vs standalone vs archived
- Replace Supabase reference with Mana Core Auth + local-first + i18n
- Add wisekeep to archived projects, document web-archived pattern

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 20:16:27 +02:00
Till JS
4fba777b9b chore: add web-archived to eslint ignore pattern in lint-staged
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 20:14:49 +02:00
Till JS
2eb1a0cd76 chore: archive 25 standalone web apps, move wisekeep to apps-archived
All standalone SvelteKit web apps have been superseded by the unified
ManaCore app (apps/manacore/apps/web). Moved to web-archived/ within
each project to preserve history while removing from active workspace.

Archived: calc, cards, chat, citycorners, contacts, context, guides,
inventar, moodlit, mukke, news, nutriphi, photos, picture, planta,
presi, questions, skilltree, storage, times, zitare, todo, calendar,
uload, memoro

Moved to apps-archived/: wisekeep (not integrated, inactive)

Kept active: manacore (unified), matrix, manavoxel, arcade (separate containers)

Server, landing, and package directories remain active for each project.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 20:14:29 +02:00
Till JS
373976a11b feat(i18n): migrate help content to locale files (Phase 5)
Replace hardcoded isDE ternaries in help/index.ts with svelte-i18n lookups.
FAQ questions, answers (HTML), features, highlights, and contact info are
now in locales/help/{de,en,es,fr,it}.json — supporting all 5 languages
instead of only German and English.

32 locale modules registered, 160 JSON files total.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:12:45 +02:00
Till JS
033d070362 feat(i18n): replace 126 hardcoded German strings with $_() calls (Phase 4)
Systematically replace hardcoded UI strings across 48 files with svelte-i18n
$_('common.*') calls. Added 14 new common translation keys (saving, close,
create, creating, edit, add, search, settings, upload, uploading,
error_saving, error_loading, error_deleting, click_to_close) in all 5 languages.

Covers: profile modals, onboarding, workbench, calendar, cards, contacts,
context, credits, gifts, inventar, memoro, moodlit, mukke, nutriphi, photos,
picture, planta, questions, settings, skilltree, storage, subscription,
times, todo, uload — all common button/action/error strings now i18n-ready.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:09:10 +02:00
Till JS
2e44a717d3 feat(i18n): consolidate all 22 module translations (Phase 3)
Port i18n strings from 22 standalone apps into unified per-module locale
structure. Each module now has 5 language files (de, en, es, fr, it).

Modules added: calendar, contacts, memoro, citycorners, times, zitare,
inventar, photos, skilltree, questions, uload, chat, cards, picture,
moodlit, storage, context, presi, nutriphi, planta, calc, matrix, guides

Total: 31 locale modules, 155 JSON files, all 5 languages complete.
German and English ported from standalone apps, ES/FR/IT generated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:53:00 +02:00
Till JS
7650a83e6e feat(manacore/web): wire reminder scheduler into app layout
Start createReminderScheduler with todoReminderSource on auth ready.
Request notification permission on app mount. Stop scheduler on destroy.
Todo reminders now fire browser notifications when dueDate - minutesBefore
is reached.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:25:31 +02:00
Till JS
034b67825a fix(manacore/web): fix TagField prop name (selectedIds, not selectedTagIds)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:24:24 +02:00
Till JS
7908995a29 feat(monitoring): structured logging, Promtail alignment, GlitchTip config, status page
- Upgrade shared-logger to dual-mode: JSON lines in production, console
  in dev. Adds configureLogger() for service name + request ID.
- Add requestLogger middleware to shared-hono with request ID generation
  and structured request/response logging.
- Align Promtail config with new JSON field names (requestId, ts, service).
- Add PUBLIC_GLITCHTIP_DSN + PUBLIC_UMAMI_WEBSITE_ID to mana-web docker config.
- Add /status page that polls all backend /health endpoints server-side.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:23:52 +02:00
Till JS
7ba82472b2 feat(manacore/web): wire TagField, FavoriteButton, ColorPicker into module UIs
Add shared TagField component (ID-based wrapper for TagSelector).
Wire TagField into: calendar EventForm, times EntryForm, cards
CreateDeckModal, contacts detail page. Wire FavoriteButton into
contacts list (replaces inline Star toggle). Add ColorPicker to
cards CreateDeckModal for deck color selection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:20:46 +02:00
Till JS
39af8f8480 chore(manacore/web): remove dead code from todo module refactor
Delete components and stores that are no longer imported after the
pages system and workbench refactor:
- MinimizedTabs.svelte (replaced by inline tabs in todo +page)
- TagStrip.svelte (old filter strip, unused)
- TodoToolbar.svelte (old Inbox/Today/Upcoming tabs, unused)
- minimized-pages.svelte.ts store (unused)
- view.svelte.ts store (only used by deleted components)
- AppRow.svelte (old home page grid, replaced by workbench)
- ActivityFeed.svelte (old home page feed, replaced by workbench)
- Remove viewStore + minimizedPagesStore from todo/index.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:18:18 +02:00
Till JS
e7ae444b18 feat(analytics): add event tracking to remaining 12 module stores
Add analytics events to inventar, storage, memoro, mukke, presi,
moodlit, picture, calc, citycorners, and zitare stores. Also adds
new event helpers for calc, inventar, moodlit, and citycorners.

All 31 module store files now have analytics instrumentation,
up from 4 at the start of this session.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:17:07 +02:00
Till JS
5a7bc5e10d feat(manacore/web): expand EventDetailModal with full feature set
Complete rewrite of the calendar event detail modal:
- Color accent bar matching calendar color
- Date with weekday + time range + duration display
- Recurrence-aware delete dialog (this event / entire series)
- Tags display
- Copy to clipboard button
- Created/updated metadata
- Cleaner layout with better typography

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:15:49 +02:00
Till JS
8ece7d31b8 feat(analytics): add event tracking to 7 core module stores
Add analytics events to todo, calendar, contacts, chat, cards, and
photos module stores. All mutations (create, update, delete, complete,
favorite, archive) now fire the corresponding typed event helpers
with automatic module context.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:10:30 +02:00
Till JS
bfe11d91ed feat(i18n): wire user settings locale, add nav translations
- Sync svelte-i18n locale from userSettings.locale (backend) via $effect
- Persist language changes to backend via userSettings.updateGlobal
- Add nav/ locale module with navigation labels in 5 languages
- Replace 6 hardcoded German strings in app layout with $_() calls:
  "Alle Themes", "Menü", "Geschenke", "Profil", "Einstellungen", etc.
- Make baseNavItems reactive ($derived) so labels update on language change

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:08:11 +02:00
Till JS
e152098829 refactor(i18n): split monolithic locale files into per-module structure
Split 5 monolithic JSON files (de/en/es/fr/it) into 7 per-module folders
(common, dashboard, credits, profile, subscription, todo, app_slider)
with 5 language files each = 35 files total.

- Rewrite i18n/index.ts with merge-registration pattern that dynamically
  imports and merges per-module locale files at registration time
- Complete missing translations: IT (was 7% → now 100%), ES/FR (were 43% → now 100%)
- Generated missing IT translations for dashboard, credits, profile, subscription, todo
- Generated missing ES/FR translations for dashboard, credits, profile, subscription
- All 5 languages now have identical key structures across all modules

This enables scalable i18n: adding a new module = new folder + 5 JSON files + 1 line in registration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:05:14 +02:00
Till JS
8495a0d12f feat(shared-stores): add centralized keyboard shortcuts registry
createKeyboardShortcuts with register/unregister, Ctrl/Shift/Alt
modifiers, input-focus awareness, Mac Cmd support, case-insensitive
matching. Singleton keyboardShortcuts instance for app-wide use.
11 tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:03:38 +02:00
Till JS
f2d6573fa7 feat(analytics): add Web Vitals tracking, GlitchTip user context, and funnel events
- Add web-vitals package with LCP/CLS/INP/FCP/TTFB → Umami tracking
- Set GlitchTip user context on login, clear on logout
- Add funnel events: first_content_created, user_return_visit,
  second_module_used, guest_converted
- Track first content via Dexie creating hook (fires once per user)
- Track module usage via route navigation effect
- Track guest→registered conversion on signup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:03:06 +02:00
Till JS
198720ca38 feat(shared-stores): add generic data export/import utilities
exportToJSON, exportToCSV (with BOM for Excel), importFromJSON,
downloadFile, timestampedFilename. Works with any Dexie table.
Supports filtering, column selection, custom formatters, ID
regeneration, and transform functions. 16 tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:01:53 +02:00