Removes all star ratings from the features page. Replaces the card
grid summary with a flowing paragraph of clickable keyword links
that scroll to each feature section. Both simple and tech views
updated.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rewrites all USP texts for non-technical users (no jargon like
IndexedDB, tables, AES-GCM-256). Adds a "Für alle / Technik" toggle
switch that swaps between user-friendly marketing copy and deep
technical architecture details. Toggle state persists in localStorage.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a comprehensive features page listing all 12 USPs of the Mana
platform with usefulness ratings (3-5 stars), detail bullet points,
and a cost comparison section (Mana vs individual tools). Also fixes
a devlog content schema error (category 'fix' -> 'bugfix') and adds
a footer link to the new page.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace inline PillDropdownBar for user menu with a centered overlay
panel (UserMenuPanel). Move AI tier, theme, and language selectors
into the panel. Make app switcher and user pill icon-only. AI section
split into "Textgenerierung" and "Spracherkennung" subsections.
- AppDrawer trigger: icon-only (no label/chevron)
- User pill: icon-only, opens overlay panel instead of bar
- Theme + AI pills removed from nav bar (now in user panel)
- UserMenuPanel: centered on desktop, bottom-sheet on mobile
- Login button in footer, structured sections with subsection headers
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
getAccessToken() reads the stored JWT without checking expiry, causing
401s when the token ages out — especially for services not covered by
the fetch interceptor (credits, events, api, etc.). getValidToken()
checks validity first and refreshes automatically when needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The workbench ListView was using Function() (effectively eval) with
aggressive sanitizing that stripped valid characters, causing every
calculation to fail with a generic "Fehler". Now uses the same safe
parser as the standard calculator page. Also adds console.error
logging and shows result before persisting to DB.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clicking a mood now opens it immediately in fullscreen (browser
Fullscreen API, z-index above all UI). Preview step removed.
Cards redesigned with full gradient backgrounds, live animations,
gradient overlays, and hover border highlight.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New module for tracking all beverages (water, coffee, tea, juice, alcohol, etc.)
with daily progress bar, quick-tap presets, and inline editing of quantity/date/time.
Includes: module config, types, collections with guest seed (5 presets),
queries, store, ListView with context menus, route, app-registry registration,
Dexie schema v7, encryption registry, shared-branding icon/app entry.
Also extends docs/future/MODULE_IDEAS.md with additional module ideas.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Docker build failed because @mana/local-stt was added as a
workspace dependency but not COPYed into the build context.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
transcribeAudio() now checks localSTT.isReady before falling back to
the server-side mana-stt proxy. When local STT is active, audio blobs
are decoded to Float32Array via AudioContext.decodeAudioData() and
transcribed entirely on-device. The returned model field shows
"Whisper Tiny (lokal)" or similar so every module (dreams, memoro,
habits) displays which backend was used — no module code changed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract the floating pill-shaped input bar (text + optional voice)
into a shared component at $lib/components/FloatingInputBar.svelte.
Migrate todo, calendar, dreams, notes, journal, memoro and contacts
from inline forms / VoiceCaptureBar to the unified bottom bar.
Calendar now shows all upcoming events with relative date labels
(Heute, Morgen, weekday name, or short date).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stripped stats counters, filter tabs, and VoiceCaptureBar. Now shows
a flat list with round monochrome checkboxes, inline due-date badges
(Überfällig/Heute/date), completed tasks below a divider with
completion timestamp, and a pill-shaped FloatingInputBar at the
bottom with integrated voice input.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PageShell header icon/title had opacity: 0.5 — removed for full
visibility. Moodlit, Zitare, Skilltree and BaseListView used
text-white/* classes that were invisible in light mode — migrated
to hsl(var(--color-foreground/muted-foreground)) tokens.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PillNav overhaul:
- Dropdown-as-bar: theme/AI/sync/user menus render as horizontal
bars in the bottom stack (PillDropdownBar) instead of floating
popovers. New onOpenBar/activeBarId props on PillNavigation.
- iconOnly pills: tags/search/workbench-tabs pills show only icons.
Home pill removed. New iconOnly flag on PillNavItem.
- Segmented toggle groups: items sharing a `group` id render as a
single segmented pill (e.g. Light/Dark/System triple).
- Fullscreen mode: press "f" to hide all bottom chrome, Esc to exit.
- QuickInputBar + bottom bar visibility toggles via new pills.
- Progress ring on AI trigger pill during model download
(conic-gradient ::after, follows pill border-radius).
@mana/local-stt — new package for browser-local speech-to-text:
- Whisper models via transformers.js v4 (WebGPU + WASM fallback)
- Same Web Worker architecture as @mana/local-llm
- Two models: Whisper Tiny (150 MB) and Whisper Small (950 MB)
- Reactive Svelte 5 bindings (getLocalSttStatus, loadLocalStt, transcribe)
Voice-to-text integration:
- useLocalStt() composable: mic capture via AudioContext +
ScriptProcessor, resample to 16kHz mono, feed into Whisper worker
- Mic button in QuickInputBar (leftAction slot) with
recording/loading/transcribing states + pulse animation
- Transcribed text injected into InputBar via new injectedText prop
- STT model selector in AI bar alongside LLM tier controls
Also: vite.config.ts server.fs.allow expanded to monorepo root
so workspace package workers resolve in dev.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Wallpaper system with four sources (predefined images, CSS gradients,
custom uploads via mana-media, and theme default). Configurable per-scene
or globally, with overlay controls (blur + opacity) and hover preview.
Adds sticky prop to shared PageHeader component and applies it across
themes, settings, credits, subscription, help, and profile pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The presi module's schema was defined inline in routes.ts but had no
working db:push mechanism — the old references to @presi/server and
@presi/backend no longer exist after consolidation. Extracts schema
into its own file, adds a dedicated drizzle config, and updates the
setup script so tables are actually created.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PageShell cards now fill the available viewport between the workbench
top padding and the bottom chrome instead of using a static 60vh.
Height is calculated via two CSS vars published by the layout <main>:
height: calc(100dvh - var(--bottom-chrome-height) - var(--workbench-reserved-y))
--bottom-chrome-height reacts to pill-nav collapse, tag strip toggle
and bottom-bar mount state. --workbench-reserved-y (2.5rem) folds the
wrapper padding + buffer into a single non-chrome offset. dvh handles
Safari's retractable address bar. Inline height from resize-drag still
overrides as before.
Bottom-stack bars now use a uniform `gap: 0.25rem` instead of ad-hoc
per-child padding-bottom, giving consistent 4px spacing between all
bars. Wrapper vertical padding reduced from py-4/py-8 to py-2/py-3
and main's bottom buffer from +32px to +8px — cards gain ~72px of
usable vertical space on a typical viewport.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switch PageShell's per-theme paper overlay from a ::before +
mix-blend-mode + opacity stack to direct background-blend-mode on the
element itself. The old approach had invisibility issues in dark mode
and stacking-context quirks that made the grain disappear entirely.
background-blend-mode against background-color is the simpler, more
reliable primitive.
utils.ts auto-switches multiply → overlay in dark mode (dark × dark is
essentially invisible) while leaving other blend modes as-is. The
opacityLight/opacityDark knobs are gone from the paper config since
background-blend-mode has no opacity slot — tune via blendMode choice
instead.
Visual tuning pass:
- Card border bumped from 1px box-shadow ring to a real 2px border
with background-clip: border-box so the paper texture reads
continuously across the edge. Alpha 0.12 light / 0.28 dark (black).
- Drop shadow deepened (0 8px 24px + 0 3px 8px) for more card lift.
- Stone theme cooled toward real slate-blue: hue 200 → 212, saturation
bumped ~10pts across the palette. Stone was reading as warm-neutral
grey, now it's a proper cold blue.
- Texture remap: Lume → paper-004 (strongest grain, 480px tile for
coarser fiber), Stone → cardboard-002 (linen), Lavender → paper-001
(freed up after Stone claimed cardboard-002).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When users type 'mana.how' (no scheme), Safari and other browsers default
to HTTP. Cloudflare/cloudflared serves the page over HTTP without
rewriting the scheme. The browser then sends 'Origin: http://mana.how'
on every fetch, but mana-auth CORS only allows 'https://mana.how'.
Result: every auth request fails, the SSO check throws, AuthGate hangs
on the loading spinner forever, and the page never finishes loading.
Fix: detect HTTP requests in hooks.server.ts via cf-visitor /
x-forwarded-proto / event.url.protocol and 301-redirect to HTTPS before
serving any content. Localhost is exempted for dev.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three related fixes for the workbench tracking overlay:
1. **Same-origin proxy at /api/v1/geocode/[...path]/+server.ts.**
mana-geocoding is intentionally NOT exposed via Cloudflare, so the
browser can't reach it directly — localhost:3018 is unreachable from
a visitor's device. Same-origin proxy fixes this: the browser talks
to https://mana.how/api/v1/geocode/*, SvelteKit forwards to
http://mana-geocoding:3018 over the docker network. Pattern copied
from the existing /api/v1/who/[...path] proxy.
2. **`formatFullAddress()` in $lib/geocoding** builds a compact line
with street+housenumber, postal code, city, and 2-letter country
code (DE/AT/CH) — e.g. "Hafenstraße 2, 78462 Konstanz, DE". Maps
German and English OSM country names to ISO 3166-1 alpha-2.
3. **Clickable, inline-editable tracking label in ListView.** The
tracking overlay used to show "47.6630, 9.1750" while tracking was
active. Now it shows the venue name + full address with ISO country
code, tapping it switches to an autocomplete input so the user can
fix the location when GPS snaps to the wrong building. Debounced
reverse-geocode on position change (1.5 s + 10 m precision), edits
are kept local — the current tracking position drives the label
but user corrections override until the next significant move.
The client lib now uses relative URLs in the browser (same-origin
proxy) and absolute URLs only from Node/SSR (via env var or localhost
fallback). geocoding unit tests still pass (42/42 green).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When tracking is active the workbench ListView used to show only raw
coordinates ("47.6630, 9.1750"). Now a human-readable location label
appears above the coords ("Münster Café" or "Konstanz, Germany"),
fed from the shared reverse-geocoding endpoint.
To avoid hammering the geocoding service while the user is stationary
and their GPS jitters by a few metres, the effect debounces to 1.5 s
and rounds coordinates to 4 decimal places (~10 m) before checking
whether a new reverse lookup is needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
**Unit tests (`bun test`, 42 checks, 0 deps)**
- `src/lib/__tests__/category-map.test.ts` locks in the Pelias→
PlaceCategory priority resolution. Covers the ambiguous multi-category
case (food beats retail for restaurants, transit beats professional
for car rentals, transport:rail still maps to transit, …), the simple
single-category paths, the layer-hint fallback, and regression cases
from real Konstanz/Stuttgart/Köln venues observed during deploy
verification.
- `src/lib/__tests__/cache.test.ts` covers LRU eviction order, TTL
expiry, move-to-end on get (so frequently-read entries survive
eviction), size tracking, and typed-value storage.
**Smoke test (`./scripts/smoke-test.sh` or `bun run test:smoke`)**
End-to-end curls against a running service, aimed at post-deploy
verification. Health endpoints, forward (venue + street fallback),
focus biasing, reverse geocoding, cache hit. 9 checks total.
Wired up as `test:smoke` in package.json so it runs alongside the
unit tests. Verified working: 42/42 unit tests green locally, 9/9
smoke checks green against the live Mac Mini deployment.
CLAUDE.md Testing section rewritten to reflect the new test layers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After the 2026-04-11 production deploy, several non-obvious gotchas
surfaced that needed documenting:
- Forward search: autocomplete→search fallback explained, so future-me
knows why the handler hits two Pelias endpoints for address-style
queries.
- Pelias infra: corrected object counts (13.4M actual, not 22M), noted
the libpostal RAM surprise (~1.9 GB, much larger than Pelias docs
suggest), and added real per-container RAM numbers from production.
- pelias.json: document that we dropped placeholder/pip/interpolation
(not just how to run them) and why the cleaner degradation matters.
- Wrapper gotchas section: Bun idleTimeout, Colima bind-mount cache
staleness, and the host.docker.internal-from-blackbox workaround.
- /health/pelias endpoint is now listed in the API table since it's
the integration point with blackbox monitoring.
- Testing section added — explicitly "no automated tests yet", with a
curl-based manual smoke test set a human can run after changes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pelias /autocomplete deliberately excludes the address layer as a
performance optimization, so queries like "Marktstätte Konstanz"
(street + locality) return 0 venue matches even though they're clearly
in the index. /search covers all layers including addresses and streets.
Query /autocomplete first (fast, fuzzy, great for venue names), and if
it returns nothing, try /search. Best of both worlds: quick matches for
"Konzil Restaurant" plus reliable matches for street addresses.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two production follow-ups surfaced after the deploy:
1. Pelias API was emitting continuous `ENOTFOUND placeholder`, `pip`,
`interpolation` errors because we declared those services in
pelias.json but never actually run them (we don't need WOF
admin lookup or street interpolation for the DACH use case).
Removed the stale entries — Pelias degrades cleanly to
libpostal-only parsing, which is what we want.
2. Bun.serve's default idleTimeout is 10s, which is too tight for
cold Pelias queries hitting Elasticsearch. Raise to 60s so
first-query-after-idle doesn't get cut off.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Profile/Settings/Spiral/Credits move out of the standalone nav pills and
into the user-menu dropdown so the bottom bar stays compact. The dropdown
now also renders for guests (login users) — auth-only items (Profil,
Mana, Feedback, Logout) get filtered out, and a primary-styled "Anmelden"
entry replaces Logout. Themes is dropped from the dropdown since it
already has its own theme-variant pill.
New PillNavigation props: creditsHref, guestMenuLabel. New PillDropdown
icon paths: creditCard, spiral. New PillDropdownItem flag: primary
(prominent CTA styling), used for the guest Anmelden item.
All .glass-pill classes across PillNavigation, PillDropdown, PillTabGroup,
PillTagSelector, PillViewSwitcher, PillTimeRangeSelector, PillToolbar,
AppDrawer and ExpandableToolbar move from rgba+backdrop-blur to solid
theme tokens (hsl(var(--color-card)) / --color-border / --color-foreground)
so pills are fully opaque and follow the active theme variant instead of
having a frosted look that varied by background.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>