The ManaCore→Mana rename converted both `manaCoreSvg` and the existing
`manaSvg` to the same identifier, leaving two `const manaSvg = ...`
declarations and two `mana:` keys in APP_ICONS. This broke any consumer
of the package with a duplicate-symbol error at SSR build time.
Removed the legacy ManaCore icon (4-circle quartet) and kept the
current Mana brand icon (single droplet). Removed the duplicate
APP_ICONS entry as well.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New SyncChange / FieldChange / SyncOp types replace `any[]` in
applyServerChanges. The wire format is now self-documenting and
TypeScript catches malformed callsites at compile time.
- isValidSyncChange() validates incoming server payloads at the boundary:
malformed entries are dropped with a single warn log, valid ones are
applied. A bad row from the server can no longer corrupt IndexedDB.
Hand-rolled type guards keep us free of a runtime-validation dep.
- applyServerChanges() and readFieldTimestamps() are now top-level
exports (extracted out of createUnifiedSync's closure) so they can be
imported directly by tests. Behaviour is unchanged — the closure
variant inside the sync manager just resolves the module-level
symbol now.
- New sync.test.ts covers:
* pure isValidSyncChange and readFieldTimestamps cases
* field-level LWW: server-newer wins, split outcome when local-newer
on one field and server-newer on another
* insert with __fieldTimestamps stamping
* soft-delete LWW guard
* malformed-entry drop with valid entries surviving
* sync-loop guard: server-applied writes don't generate _pendingChanges
- fake-indexeddb added as devDependency for the integration tests.
Note: the monorepo's vitest install is currently tangled across mixed
@vitest/* package versions in the lockfile, so `pnpm test` fails before
reaching this file. The tests are written to pass on any vitest 4.x once
that's untangled — needs its own dedicated cleanup pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- calendar/types: replace duplicate recurrenceRule with recurrenceDate on CalendarEvent; map it in timeBlockToCalendarEvent
- recurrence: drop stale Record casts now that LocalTimeBlock types isRecurrenceException and recurrenceDate
- todo: route recurrenceRule through TimeBlock in createTask/updateTask, load it from block in useTaskForm; accept labelIds via metadata; remove stale projectId casts
- calendar/events: include linkedBlockId/parentBlockId/recurrenceDate in createDraftEvent
- habits: drop unused db / LocalTimeBlock imports
- eslint-config: disable consistent-type-imports (parser conflict with .svelte.ts files)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous startup.sh checked colima status via `colima status | grep running`
and, if that failed, ran `colima stop --force` unconditionally before starting.
This is destructive: a transient status mis-detection can kill a healthy running
VM, and the subsequent start often hangs because of leftover locks/processes.
Triggered today during the ManaCore→Mana rename: reloading the docker-startup
LaunchAgent ran the script, which falsely concluded colima was down, killed the
running VM, and left 12 zombie limactl processes plus a stale disk lock symlink.
The whole production stack (incl. Forgejo) was offline until manual cleanup.
Changes:
- Use `docker info` as the readiness check instead of `colima status` —
it directly tests the thing we care about (docker socket reachable)
- Only do cleanup work when we actually need to start; never SIGKILL a
running VM as a "precaution"
- When we do need to start: reap any zombie limactl/colima processes from
prior failed runs, and clear the stale disk-in-use lock if no process
actually holds it
- Verify successful start with `docker info`, not `colima status`
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Defense-in-depth on top of the existing application-level WHERE clauses:
- Migrate() now ENABLE + FORCE row level security on sync_changes and
installs a policy that gates rows on current_setting('app.current_user_id').
FORCE makes the policy apply to the table owner too, so the application
role used by mana-sync cannot bypass it regardless of grants.
- New withUser(ctx, userID, fn) helper opens a transaction and calls
set_config('app.current_user_id', userID, true) before running fn.
Empty userIDs are rejected up-front so an unauthenticated request can
never reach the database with an empty RLS scope (which would match
every row).
- RecordChange / GetChangesSince / GetAllChangesSince all run inside
withUser. WITH CHECK on the policy double-validates the user_id column
on insert against the active session, so a future code path that
forgets the WHERE clause cannot leak data.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Single source of truth for the active user via data/current-user.ts;
layout pushes authStore.user.id into it on every auth state change.
- Dexie creating-hook auto-stamps userId from getEffectiveUserId(); the
updating-hook strips userId from modifications so records are
effectively user-immutable after creation.
- BaseRecord gains an optional userId so module types inherit it without
per-module declarations. All hardcoded 'guest'/'local' fallbacks in
module type-converters and session timer stores are deleted; the dead
userId field is removed from the public view types where it was
unused (Task, Conversation, Template, Deck, Plant, Contact, etc.).
- New guest-migration.ts: on first authenticated session, walks every
sync-tracked table, deletes guest-owned records and re-adds them so
the creating-hook re-stamps with the real user id and produces fresh
insert pending-changes with the full payload. Stale guest pending-
changes are cleared up-front.
- Drive-by: root onMount now returns its cleanup synchronously; the
previous async form silently dropped the cleanup callback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 1 (PWA) fully implemented and verified:
- All 29 ListViews, 17 DetailViews, 14 Modals responsive
- Build successful, no new type errors
- Checkboxes marked done, completion date added
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ListViews (25 remaining modules):
- All module ListViews now have responsive container padding (p-3 sm:p-4)
- All interactive items have min-h-[44px] touch targets on mobile
- Picture/Moodlit grids: grid-cols-2 on mobile, grid-cols-3 on desktop
DetailViews (17 modules):
- All DetailViews have reduced padding on mobile (0.75rem vs 1rem)
- All buttons, inputs, selects have min-height: 44px on mobile
Modals (14 components):
- Shared Modal.svelte: bottom-sheet pattern on mobile (slides up from bottom)
- 13 app-specific modals: same bottom-sheet treatment
- Reduced padding, larger close buttons, max-h-[95vh] on mobile
Shared UI components:
- GlobalSpotlight: bottom-sheet on mobile, prevents iOS zoom, hides keyboard hints
- PillDropdown: full-width bottom-sheet on mobile with backdrop
- AppDrawer: 44px touch targets on buttons and search
- TagStrip: 44px min-height on all pill buttons
- ToastContainer: larger touch targets, safe-area positioning
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cross-Module Drag & Drop:
- WeekView day columns accept 'task' and 'habit' drops
- Dropping a task auto-schedules it on that day (creates TimeBlock)
- Dropping a habit creates a logged block on that day
- HabitTile now has dragSource (long-press to drag)
Activity Feed:
- ActivityFeedWidget shows the 10 most recently updated timeBlocks
- Shows type icon, title, action label (running/completed/planned), time ago
- Registered as 'activity-feed' dashboard widget
- i18n keys added (de + en)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Generate PWA icons (192x192, 512x512, apple-touch-icon) from favicon
- Add Apple PWA meta tags, theme-color, and viewport-fit=cover to app.html
- Upgrade caching preset to 'full' (adds font + CDN caching)
- Add manifest shortcuts for Dashboard, Todo, Calendar, Chat
- Switch registerType to 'prompt' for user-controlled updates
- Add OfflineIndicator component (offline banner + sync status badge)
- Add PwaUpdatePrompt component (detects waiting SW, skip-waiting on confirm)
- Add networkStore for online/offline + sync status tracking
- Wire sync manager status into networkStore for pending change counts
- Update offline page text to reflect local-first architecture
- Add mobile/desktop app strategy doc and Tauri v2 implementation plan
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Drag & drop: external timeBlocks (tasks, habits, timeEntries) can now be
dragged and resized directly in calendar views via updateBlock()
- Conflict detection: ConflictWarning component shows overlapping timeBlocks
in EventForm and QuickEventPopover in real-time
- Plan vs Reality: startFromScheduled() creates linked logged blocks from
scheduled blocks, EventCard shows checkmark badge for linked blocks,
linkBlocks() now validates kind compatibility
- Timeline view: full-page /timeline route with chronological day view,
day navigation, type filters, duration stats, live indicators, and
connected dot+line visualization
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Habits:
- Add defaultDuration field to Habit type and domain model
- HabitForm: duration input (minutes) alongside target-per-day
- Logged habits with defaultDuration auto-set endDate on their TimeBlock
Todo:
- Task DetailView: "Kalender planen" button to schedule tasks on calendar
- Creates/updates/removes TimeBlock via scheduledBlockId
- Date + time inputs with one-click unschedule
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CalendarEventsWidget now shows all timeBlock types with type icons
and live indicators (was only showing calendar events before)
- Added day_timeline i18n keys in all 5 languages (de/en/es/fr/it)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename the music module from "Mukke" to "Music" across the entire
codebase: API routes, web app module, shared packages, search provider,
dashboard widgets, i18n keys, app registry, and route paths.
Add POST /api/v1/music/cover/upload endpoint that uploads cover art
images through mana-media for deduplication, thumbnails, and Photos
gallery visibility.
Dexie table names (mukkePlaylists, mukkeProjects) kept unchanged to
preserve existing IndexedDB data.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Chronological day timeline showing all timeBlocks for today across all
modules (events, tasks, habits, time entries). Shows summary stats
(total time, counts per type), live indicators for running timers,
and habit icons. Links to calendar for full view.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces a central `timeBlocks` table that owns the time dimension
(start, end, recurrence, live status) for all modules. Calendar, times,
habits, and todo modules keep only domain-specific data with a
timeBlockId reference. The calendar becomes a universal time view
showing events, tasks, habits, and time entries from all modules.
Key changes:
- New `$lib/data/time-blocks/` module (types, service, queries, collections)
- Dexie schema v3 with timeBlocks table + migration from existing data
- Calendar events store creates TimeBlock + LocalEvent pairs
- Times timer uses TimeBlock.isLive instead of LocalTimeEntry.isRunning
- Habits logHabit creates point-event TimeBlocks (with optional duration)
- Todo scheduled tasks create TimeBlock via scheduledBlockId
- Calendar views filter by blockType, show items from all modules
- All calendar views use getItemColor() for cross-module color support
Also includes mukke → music module rename.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace $effect + liveQuery().subscribe() with useLiveQueryWithDefault
in 6 dashboard modules (todo, calendar, contacts, habits, notes, finance)
to prevent cascading $state writes exceeding Svelte 5 effect depth limit
- Defer checkInlineSuggestion in Dexie hooks via setTimeout to avoid
cross-table reads within a single-table transaction scope
- Add 5s timeout to trySSO fetch calls so app loads in guest mode when
mana-auth is unreachable
- Fix guestMode reactivity by declaring with $state()
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Picture, Contacts, Planta, Storage, and NutriPhi image uploads now go
through mana-media instead of directly to S3. This enables SHA-256
deduplication, automatic thumbnail generation, EXIF extraction, and
makes all images visible in the Photos gallery. Non-image files (PDFs,
audio, docs) continue to use shared-storage directly. SVG avatars in
Contacts also stay on shared-storage since Sharp can't process SVGs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add curated icon registry (73 Phosphor icons, 8 categories) in shared-icons
- Add DynamicIcon atom and IconPicker molecule in shared-ui
- Migrate habits module from emoji strings to Phosphor icon names
- Add Dexie version(2) migration for emoji→icon field rename
- Replace inline SVGs in habits with Phosphor components
- Add drag-and-drop photo upload to Photos workbench ListView
- Add blob: to CSP img-src for upload previews
- Add dev:media script and include mana-media in dev:manacore:servers
- Add ./toast export to shared-ui package.json
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Card-based layout for each automation with clear visual hierarchy
- Colored flow chips (WENN=blue, FILTER=amber, DANN=green) replacing
hard-to-read inline text
- Clearer step-based create form with badge labels per section
- Better suggestion cards with icon, accept/dismiss buttons
- Toggle switch, name, and flow on separate lines
- Delete button only visible on hover
- Consistent spacing and typography
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds right-click context menus to workbench cards, minimized tabs, PillNavigation,
and item-level context menus for todo, calendar, contacts, habits, notes, places,
and moodlit modules. Uses a shared builder pattern with app-specific actions
registered via AppDescriptor.contextMenuActions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two suggestion systems for cross-module automations:
1. Discovery: Automations ListView shows suggestion cards when it
detects name overlaps between modules (e.g. "Basketball" habit +
calendar events containing "Basketball"). Accept or dismiss.
2. Inline: When creating an event/task whose title matches a habit name,
a toast appears: "Log habit 'Basketball' automatically?" with
one-click activation. Auto-dismisses after 8s.
Both use simple case-insensitive string matching (min 4 chars).
Dismissed suggestions are persisted in localStorage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New workbench module for creating and managing cross-module automations.
Users can configure rules like "when calendar event with title containing
'Basketball' is created, log habit 'Basketball'".
UI includes: source picker (app/collection/op), condition builder
(field/op/value), action picker with dynamic params (e.g. habit select
dropdown), toggle enable/disable, delete.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New trigger system that listens to Dexie write operations and fires
configurable actions in other modules. Automations are stored in
IndexedDB and loaded at app startup.
Example: calendar event "Basketball" created → habit "Basketball" logged.
Architecture: Dexie hooks → fire() → registry → condition check → action
Actions are async fire-and-forget with loop prevention.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Complete documentation for the self-hosted email infrastructure:
architecture, Stalwart config, DNS records, account management,
Fritz!Box port forwarding TODO, troubleshooting, and Brevo fallback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Gmail rejects emails without a valid Message-ID header (RFC 5322).
Add Message-ID and Date headers to all outgoing emails.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stalwart requires username without domain for auth and the 'user' role
for SMTP access. Update SMTP_USER from admin to noreply.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- traces: remove migrateLegacyLocationData() and legacy timestamp field
- context: remove legacy @[Title](ID) mention format support
No live users exist, so backwards compatibility is unnecessary.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The noreply account lacks SMTP auth permissions in Stalwart. Use the
admin account for now — SMTP_FROM still sends as noreply@mana.how.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
No live users exist yet, so migrating from old per-app databases
(manacore-todo, manacore-calendar, etc.) to the unified DB is
unnecessary. Removes legacy-migration.ts and its invocation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Go's smtp.PlainAuth refuses to send credentials when the hostname
doesn't match the TLS cert (internal Docker hostname 'stalwart' vs
cert CN 'localhost'). Replace with custom LOGIN auth that works with
any SMTP server. Add detailed error logging at each SMTP stage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Umami database was re-initialized with empty website table. Created
new ManaCore Web website in Umami and updated the ID in docker-compose
and .env.development. Fixes stats.mana.how 400 errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pages now adapt to the available screen width on mobile (<640px) instead
of overflowing at the fixed 480px default. Resize handle remains functional.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add SMTP_INSECURE_TLS env var to skip certificate verification for
internal Docker-network SMTP connections. Stalwart's self-signed cert
uses 'localhost' as CN which doesn't match the 'stalwart' hostname.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set SMTP defaults to use internal Stalwart server (stalwart:587) with
noreply@mana.how credentials. Add stalwart as dependency for mana-notify.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The unified web app calls auth.mana.how/api/v1/settings to sync theme,
nav, locale, and device settings — but the endpoint was missing, causing
404 errors in production. Implements all 7 CRUD routes against the
existing auth.user_settings table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Map host 8443 to container 8080 (HTTP admin UI). Use wget for
healthcheck since curl is not available in the Stalwart image.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>