Commit graph

2905 commits

Author SHA1 Message Date
Till JS
56a9811263 fix(status-page): replace multi-line awk with shell loop for ash compatibility
The TIER_JSON generator used a multi-line awk script embedded in a
\$() command substitution with escaped double quotes inside a
single-quoted awk program. Alpine's ash shell refused to parse this,
reporting "syntax error: unterminated quoted string". Under set -e
the syntax error killed the script BEFORE the jq call that writes
status.json, so the file stopped updating after our monitoring changes
triggered a full re-parse cycle.

Replace the awk block with a portable while-read shell loop that ash
handles cleanly. Verified with both `bash -n` and `alpine:3.20 sh -n`.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 17:23:29 +02:00
Till JS
1c94234996 fix(web): point credits/sync API client at credits.mana.how
The frontend was calling /api/v1/credits/* and /api/v1/sync/* on
auth.mana.how, but those routes live on credits.mana.how (mana-credits
service). Add getManaCreditsUrl() helper, inject the URL via
hooks.server.ts, allow it in the CSP connect-src, and update both API
clients (credits.ts + sync.ts) to use it.

Also: pass MANA_CREDITS_URL + MANA_SERVICE_KEY to mana-sync so its
billing middleware can reach mana-credits at http://mana-credits:3002.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 17:12:36 +02:00
Till JS
c47ce83e83 fix(geocoding): proxy Pelias health through wrapper for monitoring
blackbox-exporter can't resolve host.docker.internal on Colima, so
probes of host.docker.internal:4000 and :9200 always fail. Instead,
add a /health/pelias endpoint on the Hono wrapper that proxies to
the Pelias API, and update prometheus.yml to probe the wrapper's
proxied health endpoint.

Also simplifies the status page friendly_name() now that we don't
need to display the host.docker.internal targets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:45:43 +02:00
Till JS
6977d189ab fix(geocoding): don't bind libpostal to host port 4400
Port 4400 collides with mana-infra-landings (status.mana.how nginx)
on the production mac mini. libpostal is only reached internally by
pelias-api over the pelias compose network anyway — no host binding
needed. Use expose instead of ports to drop the host mapping.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:41:26 +02:00
Till JS
21360d9c18 feat(mana/web): redesign settings page + pill-nav compute selector
Settings page now uses a sidebar layout with category buttons (Profil,
Allgemein, KI, Sicherheit, Credits, Daten & Sync), an inline search field
that jumps to the matching section, and componentized sections under
lib/components/settings/. Each section owns its own data loading; the
+page.svelte shrinks from 617 to ~85 lines as a thin orchestrator.

The pill-nav AI tier dropdown now renders an icon per option (cpu, server,
cloud) and a power icon for the off state, and the "KI-Einstellungen"
shortcut deep-links to /settings#ai-options which auto-selects the KI tab
and scrolls to the panel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:40:57 +02:00
Till JS
3a93c56fe5 fix(mana-credits): multi-stage Dockerfile with node+pnpm installer
Use node:22-alpine + pnpm to install workspace dependencies, then copy
node_modules into the bun runtime stage. This resolves @mana/shared-hono
which depends on @mana/shared-logger (transitive workspace dep).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:35:36 +02:00
Till JS
fa7bfd33b4 fix(mana-credits): use pnpm in Dockerfile to handle workspace deps
bun install doesn't read pnpm-workspace.yaml, so workspace dependencies
like @mana/shared-hono can't be resolved. Switch to pnpm install with
--filter to install only mana-credits and its workspace deps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:32:05 +02:00
Till JS
c9a3c8c989 fix(mana-credits): rewrite Dockerfile to use WORKDIR instead of cd
The previous version chained cd + bun install with || fallback, which
left CWD in services/mana-credits after the first attempt and caused the
fallback cd to fail. Use WORKDIR directives instead — each step starts
from a known absolute path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:30:34 +02:00
Till JS
12bbe29a47 fix(workbench): move @const out of {#if} in AppPagePicker snippet
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:27:12 +02:00
Till JS
957060ca55 feat(monitoring): add mana-geocoding + Pelias to prod compose, Prometheus, Grafana, and status.mana.how
Production deployment + observability for the self-hosted geocoding stack:

**docker-compose.macmini.yml**
- New mana-geocoding container (port 3018, internal-only — no traefik
  labels, no Cloudflare route). Uses host.docker.internal to reach the
  Pelias API on the host's pelias compose stack. Dockerfile added under
  services/mana-geocoding/ using the same Bun/Hono pattern as mana-events.

**Prometheus**
- New blackbox-internal job probing mana-geocoding:3018/health, the
  Pelias API on host.docker.internal:4000/v1/status, and Elasticsearch
  at host.docker.internal:9200/_cluster/health. Kept separate from
  blackbox-api which is reserved for public HTTPS endpoints.

**status.mana.how (generate-status-page.sh)**
- Include blackbox-internal in the metric query and add an "Interne
  Dienste" section with its own summary card, right between Infrastruktur
  and GPU Dienste. Summary grid goes from 4 to 5 columns with a
  900px breakpoint.
- friendly_name() now handles http:// URLs and rewrites container-name
  hosts like mana-geocoding:3018/health → "Mana Geocoding",
  host.docker.internal:4000 → "Pelias API",
  host.docker.internal:9200 → "Pelias Elasticsearch".

**Grafana uptime dashboard**
- Add an "Internal" series to the "Alle Dienste — Uptime-Verlauf" panel
- New "Interne Dienste Status" table panel showing per-instance up/down
- New "Geocoding Ø Latenz" stat panel for probe_duration_seconds

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:11:01 +02:00
Till JS
0ba97672b1 feat: extend geocoding to events, contacts, photos
Extract the geocoding client from the places module into a shared
lib at $lib/geocoding so all modules can use it, then wire it into
three new consumers:

- **Events** — Address autocomplete in the edit form. When a
  suggestion is picked, locationLat/locationLon are stored alongside
  the plaintext location string. The view mode now shows an embedded
  OpenStreetMap iframe centered on the event location. Coordinates
  are plaintext for map rendering; the location text stays encrypted.

- **Contacts** — Adds a secondary "Adresse suchen…" input above the
  existing street/PLZ/city/country fields. Picking a suggestion
  fills all four fields at once and captures plaintext lat/lon on
  the contact. Enables future "contacts near me" features.

- **Photos** — Replaces the static "Auf Karte anzeigen" Google Maps
  link with a reverse-geocoded human label ("Konzil Restaurant,
  Konstanz") computed from EXIF gpsLatitude/gpsLongitude on the
  fly. Falls back to "Wird ermittelt…" during the lookup and keeps
  the OpenStreetMap link as a secondary action.

All three modules import from $lib/geocoding; the places module's
internal geocoding.ts is deleted in favor of the shared location.
Type-check: 0 errors across 6514 files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 16:01:20 +02:00
Till JS
f7835471f4 feat(workbench): match AppPagePicker design to module pages
- Bump PickerOverlay border-radius from 0.375rem to 1.25rem to match
  PageShell — picker now visually aligns with the page cards next to it
- Replace colored dots in AppPagePicker with monochrome app icons
  (uses each app's icon component from the registry, currentColor)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 15:56:00 +02:00
Till JS
f7de9fdf2d docs(geocoding): document the Pelias category patch + import gotchas
Expand services/mana-geocoding/CLAUDE.md with:
- The Pelias API patch (geojsonify_place_details.js) that forces the
  category field to always be returned, with regeneration instructions
- The priority-ordered Pelias→PlaceCategory mapping and verified
  example mappings from the DACH index
- A full initial-import walkthrough covering the non-obvious gotchas
  (analysis-icu plugin, dach-latest → planet-latest rename, adminLookup
  disabled, leveldbpath, libpostal config object form, boundary.country
  single-value constraint)

Also register mana-geocoding in the root services list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 15:50:40 +02:00
Till JS
3717f42cb8 fix(mana-sync): update Dockerfile to copy workspace shared-go dependency
The Dockerfile only copied services/mana-sync, but go.mod has a replace
directive pointing to ../../packages/shared-go which needs to be in the
build context. Switch context to repo root and copy both packages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 15:49:01 +02:00
Till JS
e82b5c1449 feat(geocoding): auto-categorize places via Pelias taxonomy
Pelias hides the 'category' field from API responses unless the
caller filters by categories=... explicitly — a default intended for
keyword search that strips category metadata from address queries.

Patch the Pelias API's geojsonify_place_details.js so the category
array is returned on every feature (food, retail, transport, …),
mounted into the container as a read-only volume override.

Rewrite category-map.ts to map Pelias' OSM taxonomy to our 7
PlaceCategories using a priority-ordered list so a restaurant
tagged ['food','retail','nightlife'] resolves to 'food' (the most
specific), not 'shopping'.

Verified with Konstanz test queries:
  Konzil Restaurant        → food
  Bahnhof Konstanz         → transit
  Physiotherapie-Schule    → work
  MX-Park                  → leisure

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 15:48:24 +02:00
Till JS
1293756bbf fix(mana-sync): bump Go base image to 1.25 to match go.mod
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 15:47:29 +02:00
Till JS
1943a1d13c fix(geocoding): Pelias config for DACH-only import + single-country filter
After importing 22M OSM objects for the DACH extract:
- Disable adminLookup (no WOF data needed for address search)
- Configure leveldb path inside the data volume
- Specify planet-latest.osm.pbf as the import filename
- Convert libpostal service config from string to object form
- Drop boundary.country default — Pelias only accepts a single
  country value, and our index only contains DACH data anyway

Verified forward + reverse geocoding work end-to-end for Konstanz
test queries via the mana-geocoding wrapper on port 3018.

Known limitation: OSM category/type (amenity:restaurant etc.) is
not yet populated in Pelias responses — will require whitelisting
those tags in the importer config and re-running the import.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 04:58:55 +02:00
Till JS
68c59c84b1 fix(docker): fix mana-credits Dockerfile to resolve workspace deps
Some checks are pending
CI / Build mana-api-gateway (push) Blocked by required conditions
CI / Build mana-crawler (push) Blocked by required conditions
CI / Build mana-media (push) Blocked by required conditions
CI / Build mana-credits (push) Blocked by required conditions
CI / Build mana-web (push) Blocked by required conditions
CI / Build chat-backend (push) Blocked by required conditions
CI / Build chat-web (push) Blocked by required conditions
CI / Build todo-backend (push) Blocked by required conditions
CI / Build todo-web (push) Blocked by required conditions
CI / Build calendar-backend (push) Blocked by required conditions
CI / Build calendar-web (push) Blocked by required conditions
CI / Build clock-web (push) Blocked by required conditions
CI / Build contacts-backend (push) Blocked by required conditions
CI / Build contacts-web (push) Blocked by required conditions
CI / Build presi-web (push) Blocked by required conditions
CI / Build storage-backend (push) Blocked by required conditions
CI / Build storage-web (push) Blocked by required conditions
CI / Build telegram-stats-bot (push) Blocked by required conditions
CI / Build nutriphi-backend (push) Blocked by required conditions
CI / Build nutriphi-web (push) Blocked by required conditions
CI / Build skilltree-web (push) Blocked by required conditions
Docker Validate / Validate Dockerfiles (push) Waiting to run
Docker Validate / Build calendar-web (push) Blocked by required conditions
Docker Validate / Build todo-backend (push) Blocked by required conditions
Docker Validate / Build todo-web (push) Blocked by required conditions
Docker Validate / Build zitare-web (push) Blocked by required conditions
Docker Validate / Build mana-auth (push) Blocked by required conditions
Docker Validate / Build mana-sync (push) Blocked by required conditions
Docker Validate / Build mana-media (push) Blocked by required conditions
Mirror to Forgejo / Push to Forgejo (push) Waiting to run
The Dockerfile copied only its own package.json, causing bun install to
fail on @mana/shared-hono workspace dependency. Now copies workspace root
package.json and shared-hono/shared-types packages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:14:07 +02:00
Till JS
2a177ba032 fix(monitoring): add 10 missing modules to blackbox probes + geocoding to status
Blackbox web probes were missing: body, journal, dreams, firsts,
cycles, events, finance, places, who, news, mail. These modules
exist in mana-apps.ts and are deployed but were never added to
prometheus.yml — so they didn't show on status.mana.how.

Also adds mana-geocoding and mana-events to the internal SvelteKit
status page health checks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:13:07 +02:00
Till JS
ecfb267280 fix(firsts): remove invalid JS comment from CSS block
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:09:05 +02:00
Till JS
82f58e44fa A11y 2026-04-10 23:04:39 +02:00
Till JS
a47a7bfdba feat(places): add self-hosted geocoding with Pelias (DACH)
New mana-geocoding service (port 3018) wraps a self-hosted Pelias
instance with LRU caching and OSM→PlaceCategory auto-mapping.
All geocoding queries stay within our infrastructure — no user
location data leaves the network.

Places module integration:
- Address autocomplete search in ListView (creates place with
  name, coords, address, category in one step)
- Address search + reverse geocoding button in DetailView
- Auto-fill address via reverse geocoding during tracking
- OSM category mapping (amenity:restaurant→food, shop:*→shopping, etc.)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:02:25 +02:00
Till JS
f5ad492371 refactor(workbench): redesign page cards — rounder corners, unified header, remove DnD
- Increase border-radius to 1.25rem for page cards and add button
- Merge toolbar bar and header into single row (title left, actions right)
- Remove drag-and-drop reorder in favor of arrow buttons
- Make window action icons larger (24px bold) with more spacing
- Title icon monochrome with reduced opacity
- Remove onReorder prop and handleReorder from all carousel consumers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:02:10 +02:00
Till JS
d2c9795405 feat(sync): add sync status PillNav dropdown + onboarding step
PillNavigation sync dropdown:
- New cloud icon pill showing sync status (Lokal/Sync/Pausiert)
- Dropdown with contextual actions: activate, top up credits, settings
- Shows next charge date when active
- Only visible for authenticated users

Onboarding wizard:
- New SyncStep between AI tier and Credits steps
- Explains local-first model: data always stays local, sync is optional
- Interval selection (monthly 30 / quarterly 90 / yearly 360 credits)
- Activate button with balance check and error handling
- Also fixed missing AiTierStep rendering in wizard template

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:51:00 +02:00
Till JS
b8cd33df7a fix(a11y): replace 215 suppression comments with real fixes
Comprehensive a11y sweep that replaces svelte-ignore comments with
proper semantic HTML. Three parallel work streams:

Labels (68 instances, 22 files):
  - 36 labels associated with controls via for/id pairs
  - 32 non-labeling <label> elements changed to <span>/<p>
  Files: LandingEditor (13), todo/settings (7), times/alarms (4),
  inventory/items (4), ViewEditorModal (3), uload (3), plus 16 more.

Div-click + click-keyboard (124 instances, ~67 files):
  - Modal backdrops: added role="presentation", tabindex="-1",
    onkeydown Escape handlers (~30 modals across the codebase)
  - Clickable cards: <div onclick> → <button type="button"> with
    text-left reset (~10 instances)
  - Stop-propagation wrappers: added role="none" (~5 instances)
  - Drag containers: added role="application"/"list"/"toolbar"
  - Contenteditable spans: added role="textbox" + tabindex="0"

Icon buttons (23 instances, 12 files):
  - Color swatches: aria-label="Farbe wählen"
  - Delete buttons: aria-label="Löschen"
  - Edit buttons: aria-label="Bearbeiten"
  - Toggle buttons: aria-label="Umschalten"
  - Other actions: contextual German labels

38 remaining warnings from edge cases (SVG event handlers, nested
roles needing tabindex, drag-drop zones) are suppressed with
comments — these have no clean HTML-semantic fix.

Net: 215 suppressions removed, 38 remain (from 215 → 38 = 82%
real fixes). Zero new warnings introduced.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:43:05 +02:00
Till JS
56d7f9a4de docs(mana-sync): document billing middleware, new env vars, project structure
- Add MANA_CREDITS_URL and MANA_SERVICE_KEY to configuration table
- Document billing gate on sync endpoints (402 behavior, 5min cache, fail-open)
- Add billing/check.go to project structure
- Add stream endpoint to API table

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:38:23 +02:00
Till JS
ed76f53b00 feat(sync): Phase 2 — server-side billing gate, cron charging, email notifications
Server-side gating (mana-sync Go):
- New billing.Checker with 5-minute cache per user
- Middleware wraps POST/GET /sync/{appId} endpoints
- Returns 402 Payment Required when sync subscription inactive
- Fail-open: if mana-credits is unreachable, sync is allowed
- Config: MANA_CREDITS_URL + MANA_SERVICE_KEY env vars

Recurring charge cron (mana-credits):
- Hourly setInterval checks for due sync subscriptions
- Calls chargeRecurring() which debits credits and advances nextChargeAt
- On insufficient credits: pauses subscription, sends email via mana-notify

Email notifications:
- Sends "Cloud Sync pausiert" email via mana-notify when subscription paused
- Uses POST /api/v1/notifications/send with X-Service-Key auth

Client-side 402 handling:
- sync.ts detects 402 from push/pull, fires onBillingRequired callback
- Layout wires callback to reload syncBilling store → shows pause banner

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:28:57 +02:00
Till JS
7102063afc fix(calendar): add timezone fallback in test mock to match Calendar type
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:26:30 +02:00
Till JS
ab62157a98 feat(firsts): add first-times module with dream-to-lived tracking
New module for tracking first-time experiences with two phases:
- Dream: bucket-list items with priority and motivation
- Lived: documented moments with expectation-vs-reality, rating,
  people, places, and media

Includes:
- Full module scaffold (types, collections, queries, store, config)
- ListView with 3 tabs (Timeline, Dreams, People)
- Inline editor + dream-to-lived conversion sheet
- Encryption for all user-typed content
- Dexie schema v6, app-registry, DragType registration
- App icon (amber-rose sparkle) and branding entry

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:23:32 +02:00
Till JS
e943ac9d94 docs: add firsts module design document
Design spec for the "Erste Male / First Times" module — combining
bucket-list dreams, social/people tracking, and rich-media experience
journaling with expectation-vs-reality as the emotional core.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:23:21 +02:00
Till JS
5c2ea614cd feat(credits): add sync billing — monthly credit subscription for cloud sync
Cloud Sync is now a paid feature: 30 credits/month (90/quarter, 360/year).
Users start in local-only mode and opt-in via Settings > Cloud Sync.
1 Credit = 1 Cent, so sync costs ~0.30€/month.

When credits run out, sync is paused (not deleted) and an in-app banner
prompts the user to top up. Local data is always preserved.

Backend (mana-credits):
- New sync_subscriptions table in credits schema
- SyncBillingService with activate/deactivate/chargeRecurring
- User-facing routes: GET/POST /api/v1/sync/{status,activate,deactivate,change-interval}
- Internal routes for server-side checks and cron triggers

Frontend (mana web):
- Sync API client + reactive sync-billing store
- syncEnabled parameter gates createUnifiedSync() — sync only starts when active
- Settings sync page with interval selection and activate/deactivate
- Pause banner in app layout when credits insufficient

Also: removed CALDAV_SYNC/GOOGLE_SYNC operations (not needed),
updated CLOUD_SYNC cost from 5 to 30 credits/month.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:21:58 +02:00
Till JS
f9b6720d15 feat: E2E smoke test, lazy widget loading, typed module context
Three medium-sized improvements:

1. E2E Smoke Test (e2e/smoke.spec.ts)
   Two Playwright tests that exercise the critical happy path:
   - "boot → dashboard → navigate → verify": opens /, /todo,
     /notes, /habits, /calc in sequence, verifies each renders
     content, checks for console errors.
   - "module routing: all core routes respond": iterates 11 core
     routes (/todo, /calendar, /contacts, /notes, /habits, /calc,
     /chat, /body, /dreams, /finance, /moodlit) and asserts no
     SvelteKit error page or crash. Runs in guest mode using the
     existing dismissWelcomeModal helper.

2. Lazy Widget Loading (WidgetContainer.svelte)
   Dashboard widgets are now lazy-mounted via IntersectionObserver.
   Offscreen widgets render a small pulse placeholder until they
   scroll into the viewport (with 200px rootMargin for pre-loading).
   Once visible, the widget stays mounted permanently. This defers
   liveQuery subscriptions for the ~13 dashboard widgets so only
   the ~3-4 above-the-fold widgets fire IndexedDB reads on initial
   mount — the rest activate as the user scrolls.

3. Typed Module Context (lib/data/module-context.ts)
   `createModuleContext<T>(key)` returns a `{ provide, consume }`
   pair that wraps Svelte's setContext/getContext with compile-time
   type safety. Replaces the manual
   `getContext<{readonly value: T[]}>('key')` pattern that was
   duplicated across every layout/page boundary with a fragile
   inline type annotation. Example usage added for the body module
   (body/context.ts) — other modules can adopt incrementally.

Turborepo type-check task was already in place (turbo.json + root
package.json). No changes needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:17:57 +02:00
Till JS
30440f37b0 chore(branding): set all module tiers to guest for testing
All 37 MANA_APPS requiredTier set to 'guest' so every user
can access every module during the current testing phase.
Also resolves merge conflict in who/+page.svelte (formatting).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 22:15:37 +02:00
Till JS
4d133fa430 fix(mana/web): resolve 14 pre-existing svelte-check type errors
Sweep of type errors accumulated from parallel development that
were blocking the production build's pre-push svelte-check gate.
None of these are behavioral changes — just type annotations,
missing exports, and prop mismatches.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 19:28:37 +02:00
Till JS
e42968203d feat(journal): add journal module with voice capture, mood tracking, and encryption
New module at modules/journal/ with daily freeform entries, 8 mood states
(emoji picker), tag system, "on this day" historical recaps, streak tracking,
word count, favorites, and STT voice capture via VoiceCaptureBar. Title and
content encrypted at rest (AES-GCM-256). Registered in module-registry,
crypto registry, seed-registry, app-registry, and shared-branding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 19:23:19 +02:00
Till JS
0f634b2540 refactor(workbench): replace minimize tabs + scene tabs with unified bottom bar
Removes the minimize/restore system entirely (scenes make it redundant)
and merges the top-level SceneTabs into a single inline bottom bar that
renders inside the layout's bottom-stack.

Chrome tab-group style: active scene shows its app tabs inline after it,
inactive scenes appear as compact pills. App tabs show module icons
instead of color dots, no fullscreen/close buttons (use context menu).

Architecture:
- New bottomBarStore (svelte $state) lets pages inject a component into
  the layout's bottom-stack without a Svelte slot mechanism
- SceneAppBar component extracted for clean separation
- PageCarousel stripped to pure carousel (no scene/bar responsibilities)
- bottomChromeHeight accounts for the bar when present (+36px)

Removed: minimized field from WorkbenchSceneApp/CarouselPage, Minus
button from PageShell, minimizeApp/restoreApp from store, onMinimize/
onRestore from context menu builder, SceneTabs component usage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 19:22:35 +02:00
Till JS
cbfe995f7b feat(timeblocks): integrate music, moodlit, presi modules
Extend the unified TimeBlock system to 3 more modules.

New TimeBlockTypes: listening, mood, rehearsal
New SourceModules: music, moodlit, presi

- music: incrementPlayCount() creates 'listening' block with song title,
  artist, and duration-based endDate
- moodlit: add startMoodSession/endMoodSession with live 'mood' blocks
  using the mood's primary color
- presi: add startRehearsal/endRehearsal with live 'rehearsal' blocks
  for presentation practice sessions
- Update analytics colors/labels, calendar filters, dashboard widgets

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 19:19:54 +02:00
Till JS
e068335dd4 refactor(credits): simplify credit system — remove productivity credits, guild pools, complex gift types
The credit system was overengineered for the local-first architecture:
- Productivity micro-credits (task/event/contact creation at 0.02 credits) made no sense
  since these operations happen locally in IndexedDB with zero server cost and were never enforced
- Guild pool system (6 DB tables, spending limits, membership checks) had no active users
- Gift system had 5 types (simple/personalized/split/first_come/riddle) when 2 suffice

Now credits are only charged for operations that actually cost money: AI API calls and
premium features (sync, exports). This makes the value proposition clear to users.

Changes:
- Remove 8 productivity operations + CreditCategory.PRODUCTIVITY from @mana/credits
- Delete guild pool service, routes, schema (3 files); remove guild refs from 8 backend files
- Simplify gifts to simple + personalized only; remove bcrypt/riddle/portions logic
- Update all frontend pages (credits dashboard, gift create/redeem, public gift page)
- Update shared-hono consumeCredits() to remove creditSource parameter
- Update mana-credits CLAUDE.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 19:08:42 +02:00
Till JS
29ad31c4ed feat(timeblocks): integrate guides, places, cards modules
Extend the unified TimeBlock system to 3 more modules.

New TimeBlockTypes: guide, visit, study
New SourceModules: guides, places, cards

- guides: startRun() creates 'guide' block, completeRun() stamps endDate
- places: recordVisit() + auto-visit tracking create 'visit' point-events
- cards: add startStudySession/endStudySession with live 'study' blocks
- Update analytics colors/labels, calendar filters, dashboard widgets

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 19:07:59 +02:00
Till JS
6ee1df390e feat(timeblocks): integrate planta, dreams, skilltree, cycles modules
Extend the unified TimeBlock system to 5 additional modules so their
time-based activities appear in Calendar and Timeline views automatically.

New TimeBlockTypes: body, watering, sleep, practice, cycle
New SourceModules: body, planta, dreams, skilltree, cycles

- planta: logWatering() creates a 'watering' block with plant name
- dreams: createDream/updateDream creates 'sleep' block from bedtime→wakeTime
- skilltree: addXp() creates 'practice' block when duration is provided
- cycles: createCycle() creates allDay 'cycle' block, setPeriodEnd stamps endDate
- Update analytics colors/labels, calendar filters, dashboard widgets

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:54:04 +02:00
Till JS
3e812e8da7 fix(guides): add stub GUIDES export so build passes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:51:01 +02:00
Till JS
a8da25c931 fix(guides): move {@const} out of <div> to fix Svelte 5 build error
Svelte 5 requires {@const} to be a direct child of block elements
({#snippet}, {:else}, {#each}, etc.), not inside plain HTML elements
like <div>. The guides DetailView had it inside <div class="meta">,
which broke the production build.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:45:37 +02:00
Till JS
97610a08f8 feat(sync): batched push with PUSH_BATCH_SIZE = 200
Caps each push request to 200 pending changes so a user who was
offline for weeks doesn't send a single multi-MB payload. After
each successful batch, schedulePush re-triggers to drain the
remaining rows in subsequent chunks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:43:33 +02:00
Till JS
7d18adadf7 fix: as-any cast cleanup + spiral-db prepare + locale typing
Remaining cast cleanups that got lost during the lint-staged stash
cycle and were re-applied:

- citycorners: added createdBy to LocalLocation type, removed 6
  `as any` casts in getCityStats/getPlatformStats
- picture/images: removed toggleField double-cast (now unnecessary
  after the IndexableType widening in shared-stores)
- contacts/[id]: tagIds exists on Contact — removed the
  `as unknown as Record<...>` cast
- calendar/EventForm: same tagIds fix — read directly from event
- +layout.svelte: import SupportedLocale type, use it for locale
  casts instead of `as any`
- spiral-db: added prepare + prepublishOnly scripts so dist/ is
  built on fresh clones

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:43:01 +02:00
Till JS
d7dc5388b9 fix(dx): shortcut mana-media dev startup chain
Skip two intermediate pnpm invocations — go directly to
bun run --hot in the inner apps/api directory.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:41:44 +02:00
Till JS
5647b2f8ae fix(dx): suppress AZURE_OPENAI_API_KEY warning, honest db:push reporting
- docker-compose: add empty default for AZURE_OPENAI_API_KEY to suppress
  Docker Compose "variable is not set" warning
- setup-databases.sh: detect when pnpm filter matches no packages and
  report "Skipped" instead of false "Schema pushed" success

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:36:35 +02:00
Till JS
7df515434e fix: revert tier test patch, widen toggleField, add spiral-db prepare
Three independent fixes grouped because they're each one-line changes:

1. Revert MANA_APPS requiredTier test patch
   Commit e52b6e29f flipped all 36+ apps to requiredTier='guest' for
   local testing. Restored original tiers from before the flip:
   guest-accessible (contacts, calendar, todo), public (who),
   beta (zitare, calc, guides, arcade), alpha (most modules),
   founder (memoro, nutriphi, mail, habits, notes, dreams, cycles,
   events, finance, places, news). Body stays at 'guest' (new module,
   intentional). The memory note "REVERT BEFORE RELEASE" is now done.

2. Widen toggleField to accept IndexableType keys
   `toggleField<T>(table: Table<T, string>, ...)` rejected Dexie
   tables keyed by IndexableType (the default). Changed the second
   generic to IndexableType so callers like images.svelte.ts don't
   need the `as unknown as Parameters<...>[0]` double-cast.

3. Add prepare script to spiral-db
   `"prepare": "pnpm build"` ensures `dist/` is rebuilt after
   `pnpm install` on a fresh clone. Without this, the 209 cascading
   type errors from stale/missing dist files return on every new
   checkout. Also added `prepublishOnly` as a safety net.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:35:20 +02:00
Till JS
3e81a6ebef fix: dev startup — Redis eviction policy, mana-media port crash, Svelte warnings
- Redis: allkeys-lru → noeviction to prevent silent data loss when memory full
- mana-media: --watch → --hot to fix EADDRINUSE crash on Bun HMR reload
- Svelte: build initial values before $state() to avoid state_referenced_locally warnings
  in create-app-onboarding.svelte.ts and shared-llm/store.svelte.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:33:41 +02:00
Till JS
a9956c0009 feat(mana/web): AI tier selector dropdown in PillNavigation
Quick-access dropdown in the bottom navigation bar for toggling LLM
tiers without navigating to the full Settings page. Follows the same
PillDropdown pattern as the existing theme variant selector.

Three files changed:

  packages/shared-ui/src/navigation/types.ts
    Add showAiTierSelector, aiTierItems, currentAiTierLabel to
    PillNavigationProps. Same shape as the existing theme variant
    and language switcher props.

  packages/shared-ui/src/navigation/PillNavigation.svelte
    Destructure the three new props (defaults: false, [], 'KI').
    Render a PillDropdown with icon="cpu" between the theme
    variant selector and the theme toggle button.

  apps/mana/apps/web/src/routes/(app)/+layout.svelte
    Import llmSettingsState, updateLlmSettings, tierLabel, type
    LlmTier from @mana/shared-llm. Import isLocalLlmSupported,
    getLocalLlmStatus, loadLocalLlm from @mana/local-llm.

    Build aiTierItems as a $derived array of PillDropdownItem:
      - Three tier toggles: Browser (Gemma 4), Server (Gemma 4),
        Cloud (Gemini). Each shows active checkmark when enabled.
        Clicking toggles the tier in/out of allowedTiers. Browser
        toggle hidden when WebGPU isn't available.
      - Browser model status line: "✓ Modell geladen" (disabled,
        green) or "Lade... X%" (disabled, progress) or "Modell
        laden (~500 MB)" (clickable, triggers loadLocalLlm).
        Only shown when browser tier is enabled.
      - Divider + "KI-Einstellungen" link to /settings for the
        full configuration (cloud consent, behavior toggles, etc.)

    Build currentAiTierLabel as privacy-sorted first-active-tier
    short name: "Browser" or "Server" or "Cloud" or "Aus".

    Wire all three to PillNavigation via showAiTierSelector={true}
    + {aiTierItems} + {currentAiTierLabel}.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:32:05 +02:00
Till JS
0f7ab60397 feat: top-5 ROI improvements — CI gate, auth fields, body×timeblocks, sync pull, tests
Five high-impact improvements across the stack:

1. Pre-push hook: svelte-check gate (.husky/pre-push)
   Runs `pnpm check --fail-on-warnings` before every `git push`.
   Blocks pushes with type errors or warnings so we never drift
   back to 418 errors. Takes ~15s on warm cache — acceptable for
   push frequency. Skip with `--no-verify` if needed.

2. getUserFromToken: map name/image/twoFactorEnabled
   The JWT payload carries these three fields (from Better Auth's
   user profile + 2FA enrollment) but getUserFromToken() only
   extracted sub/email/role/tier. The Settings page, onboarding
   ProfileStep, and TwoFactorSetup all read these via
   `authStore.user?.name` etc. and got undefined. Now mapped from
   both top-level claims and user_metadata (legacy layout).
   DecodedToken type extended to match.

3. Body × TimeBlocks integration
   startWorkout() now creates a TimeBlock (kind='logged',
   type='body', sourceModule='body') so workouts appear in the
   calendar, timeline page, and DayTimelineWidget. finishWorkout()
   stamps the TimeBlock's endDate so the calendar shows duration.
   deleteWorkout() cascades the TimeBlock deletion. Added
   `timeBlockId?: string` to LocalBodyWorkout.

4. Sync pull() silent-failure surfacing
   Symmetric with the push() fix from the SYNC_DEBUG commit:
   pull() now logs a console.warn + emits telemetry for both
   the unknown-appid and no-token failure paths instead of
   silently returning. Same diagnostic value as the push fix —
   the SYNC_DEBUG runbook's Schritt C now surfaces pull failures
   too.

5. Unit tests for contacts, chat, calendar (3 new test files)
   Same fake-indexeddb + MemoryKeyProvider harness as body/nutriphi.
   - contacts: create+encrypt PII, soft-delete, toggleFavorite (4)
   - chat: create+encrypt title, archive, pin/unpin, delete (4)
   - calendar: create with defaults, soft-delete, setAsDefault (3)
   Total test count: 37 passing across 5 suites.

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