diff --git a/apps/mana/apps/web/src/lib/app-registry/apps.ts b/apps/mana/apps/web/src/lib/app-registry/apps.ts index 3bf59c0ab..cbe1247bc 100644 --- a/apps/mana/apps/web/src/lib/app-registry/apps.ts +++ b/apps/mana/apps/web/src/lib/app-registry/apps.ts @@ -1041,12 +1041,12 @@ registerApp({ }); registerApp({ - id: 'ai-rituals', - name: 'AI Rituale', + id: 'rituals', + name: 'Rituale', color: '#EC4899', icon: Lightning, views: { - list: { load: () => import('$lib/modules/ai-rituals/ListView.svelte') }, + list: { load: () => import('$lib/modules/rituals/ListView.svelte') }, }, }); diff --git a/apps/mana/apps/web/src/lib/app-registry/categories.ts b/apps/mana/apps/web/src/lib/app-registry/categories.ts index d3a9a3eb6..17b1ae0cd 100644 --- a/apps/mana/apps/web/src/lib/app-registry/categories.ts +++ b/apps/mana/apps/web/src/lib/app-registry/categories.ts @@ -47,7 +47,6 @@ export const APP_CATEGORY_MAP: Record = { 'ai-missions': 'ai', 'ai-agents': 'ai', 'ai-workbench': 'ai', - 'ai-rituals': 'ai', 'ai-policy': 'ai', 'ai-insights': 'ai', 'ai-health': 'ai', @@ -67,6 +66,7 @@ export const APP_CATEGORY_MAP: Record = { dreams: 'life', drink: 'life', meditate: 'life', + rituals: 'life', journal: 'life', food: 'life', recipes: 'life', diff --git a/apps/mana/apps/web/src/lib/companion/rituals/store.ts b/apps/mana/apps/web/src/lib/companion/rituals/store.ts index 2c7661c73..8c20d9068 100644 --- a/apps/mana/apps/web/src/lib/companion/rituals/store.ts +++ b/apps/mana/apps/web/src/lib/companion/rituals/store.ts @@ -17,6 +17,7 @@ export const ritualStore = { title: template.title, description: template.description, trigger: template.trigger, + category: template.category ?? 'utility', status: 'active', createdAt: now, updatedAt: now, @@ -44,6 +45,7 @@ export const ritualStore = { title: string; description?: string; trigger: LocalRitual['trigger']; + category?: LocalRitual['category']; }): Promise { const now = new Date().toISOString(); const ritual: LocalRitual = { @@ -51,6 +53,7 @@ export const ritualStore = { title: input.title, description: input.description, trigger: input.trigger, + category: input.category ?? 'utility', status: 'active', createdAt: now, updatedAt: now, diff --git a/apps/mana/apps/web/src/lib/companion/rituals/types.ts b/apps/mana/apps/web/src/lib/companion/rituals/types.ts index d7091ed85..76ea69c4d 100644 --- a/apps/mana/apps/web/src/lib/companion/rituals/types.ts +++ b/apps/mana/apps/web/src/lib/companion/rituals/types.ts @@ -1,17 +1,35 @@ /** - * Ritual types — Guided routines that write data into modules. + * Ritual types — Guided sequences, either utility (data capture) or + * ceremony (presence/meaning) or a mix of both. * - * A ritual is a sequence of steps. Each step either executes a tool - * (e.g. log_drink), collects user input (free text, mood picker, - * number), or displays information (DaySnapshot data). + * A ritual is an ordered list of steps. Steps come in two flavours: + * - Utility: tool_call / number_input / text_input / mood_picker / + * info_display / checklist — each writes data into other + * modules (drink, food, tasks, …) via the AI tool layer. + * - Ceremony: presence / breath / media — no tool calls, no data + * written. They exist to hold the user's attention on + * something for a moment (a timer, a breath pattern, an + * image). Useful for morning coffee, Sunday reset, + * before-bed shutdown. + * + * `category` on the ritual itself is a hint for filtering and UI intent, + * not a hard constraint — a ritual can mix utility + ceremony steps. */ +export type RitualCategory = 'utility' | 'ceremony' | 'mixed'; + export interface LocalRitual { id: string; title: string; description?: string; /** When this ritual should trigger ('morning', 'evening', 'manual') */ trigger: 'morning' | 'evening' | 'manual'; + /** + * UI hint: is this more "log my state" (utility) or "hold my attention" + * (ceremony)? Defaults to 'utility' for backward compatibility with the + * original ai-rituals design where every ritual was tool-driven. + */ + category?: RitualCategory; status: 'active' | 'paused' | 'archived'; createdAt: string; updatedAt: string; @@ -37,7 +55,10 @@ export type RitualStepType = | 'text_input' // User enters text → tool call | 'mood_picker' // User picks mood (1-5) → tool call | 'info_display' // Show data from projections (read-only) - | 'checklist'; // Multiple items to check off + | 'checklist' // Multiple items to check off + | 'presence' // Markdown text + optional timer — hold attention + | 'breath' // Guided breathing pattern (box / 4-7-8 / custom) + | 'media'; // Image + caption — e.g. mantra, photo, quote export type RitualStepConfig = | ToolCallStepConfig @@ -45,7 +66,10 @@ export type RitualStepConfig = | TextInputStepConfig | MoodPickerStepConfig | InfoDisplayStepConfig - | ChecklistStepConfig; + | ChecklistStepConfig + | PresenceStepConfig + | BreathStepConfig + | MediaStepConfig; export interface ToolCallStepConfig { type: 'tool_call'; @@ -91,6 +115,41 @@ export interface ChecklistStepConfig { items: { label: string; toolName?: string; toolParams?: Record }[]; } +// ── Ceremony steps (no tool calls) ─────────────────── + +export interface PresenceStepConfig { + type: 'presence'; + /** Markdown-ish body text shown while the timer runs. */ + body?: string; + /** Optional countdown timer in seconds. Step can be advanced manually at any time. */ + durationSec?: number; +} + +export type BreathPattern = + | 'box' // 4-4-4-4 + | '4-7-8' // inhale 4, hold 7, exhale 8 + | 'coherent' // 5-0-5-0 (slow resonant breathing) + | 'custom'; + +export interface BreathStepConfig { + type: 'breath'; + pattern: BreathPattern; + /** Number of full cycles. */ + cycles: number; + /** Required when pattern='custom'; otherwise derived from the preset. */ + timings?: { inhaleSec: number; hold1Sec: number; exhaleSec: number; hold2Sec: number }; +} + +export interface MediaStepConfig { + type: 'media'; + /** URL or data-URL of the image. */ + imageUrl?: string; + /** Caption / mantra / quote shown below the image. */ + caption?: string; + /** Optional linger timer before the user can advance. */ + durationSec?: number; +} + export interface LocalRitualLog { id?: number; ritualId: string; @@ -108,6 +167,7 @@ export interface RitualTemplate { title: string; description: string; trigger: LocalRitual['trigger']; + category?: RitualCategory; steps: Omit[]; } @@ -117,6 +177,7 @@ export const RITUAL_TEMPLATES: RitualTemplate[] = [ title: 'Morgenroutine', description: 'Starte den Tag mit Wasser, Tagesueberblick und Prioritaeten', trigger: 'morning', + category: 'utility', steps: [ { order: 0, @@ -153,6 +214,7 @@ export const RITUAL_TEMPLATES: RitualTemplate[] = [ title: 'Abendroutine', description: 'Reflektiere den Tag und plane morgen', trigger: 'evening', + category: 'utility', steps: [ { order: 0, @@ -185,6 +247,7 @@ export const RITUAL_TEMPLATES: RitualTemplate[] = [ title: 'Trink-Check', description: 'Schneller Wasser-Check und Nachloggen', trigger: 'manual', + category: 'utility', steps: [ { order: 0, @@ -209,4 +272,153 @@ export const RITUAL_TEMPLATES: RitualTemplate[] = [ }, ], }, + + // ── Ceremony templates ───────────────────────────── + + { + id: 'tpl-morning-coffee', + title: 'Morgenkaffee', + description: 'Einen Moment für den ersten Kaffee des Tages — ohne Handy, ohne Eile.', + trigger: 'morning', + category: 'ceremony', + steps: [ + { + order: 0, + type: 'presence', + label: 'Wasser aufsetzen', + config: { + type: 'presence', + body: 'Setz das Wasser auf. Atme dabei bewusst. Das Geräusch des Kochers ist dein erster Anker im Tag.', + durationSec: 180, + }, + }, + { + order: 1, + type: 'presence', + label: 'Aufbrühen', + config: { + type: 'presence', + body: 'Gieß langsam. Riech den Kaffee, bevor du ihn trinkst. Nichts tun außer brühen.', + durationSec: 240, + }, + }, + { + order: 2, + type: 'breath', + label: 'Drei tiefe Atemzüge', + config: { type: 'breath', pattern: 'coherent', cycles: 3 }, + }, + { + order: 3, + type: 'presence', + label: 'Fünf Minuten still trinken', + config: { + type: 'presence', + body: 'Kein Handy. Kein Podcast. Nur der Kaffee und der Morgen. Wie fühlst du dich heute?', + durationSec: 300, + }, + }, + ], + }, + + { + id: 'tpl-sunday-reset', + title: 'Sonntag-Reset', + description: 'Die Woche abschließen, die nächste vorbereiten — ohne Hetze.', + trigger: 'manual', + category: 'mixed', + steps: [ + { + order: 0, + type: 'presence', + label: 'Ankommen', + config: { + type: 'presence', + body: 'Mach dir einen Tee. Setz dich hin. Die Woche ist vorbei.', + durationSec: 300, + }, + }, + { + order: 1, + type: 'info_display', + label: 'Was diese Woche gelaufen ist', + config: { type: 'info_display', source: 'streaks' }, + }, + { + order: 2, + type: 'text_input', + label: 'Was nehme ich mit?', + config: { + type: 'text_input', + toolName: 'create_task', + paramName: 'title', + baseParams: {}, + placeholder: 'Ein Moment, eine Lektion, ein Gefühl...', + }, + }, + { + order: 3, + type: 'info_display', + label: 'Nächste Woche', + config: { type: 'info_display', source: 'events_today' }, + }, + { + order: 4, + type: 'presence', + label: 'Handy weg', + config: { + type: 'presence', + body: 'Für die nächsten zwei Stunden: Handy in den anderen Raum. Buch, Kochen, Spaziergang — was auch immer dir gut tut.', + }, + }, + ], + }, + + { + id: 'tpl-bedtime', + title: 'Vor dem Schlaf', + description: 'Herunterfahren. Gedanken loslassen. Bereit für die Nacht.', + trigger: 'evening', + category: 'ceremony', + steps: [ + { + order: 0, + type: 'presence', + label: 'Bildschirme aus', + config: { + type: 'presence', + body: 'Schalte Fernseher, Laptop und Handy aus. Leg das Handy nicht neben das Bett.', + durationSec: 60, + }, + }, + { + order: 1, + type: 'breath', + label: '4-7-8 Atmung', + config: { type: 'breath', pattern: '4-7-8', cycles: 4 }, + }, + { + order: 2, + type: 'text_input', + label: 'Was hat mich heute bewegt?', + config: { + type: 'text_input', + toolName: 'create_journal_entry', + paramName: 'content', + baseParams: {}, + placeholder: 'Ein Satz reicht.', + }, + }, + { + order: 3, + type: 'presence', + label: 'Loslassen', + config: { + type: 'presence', + body: 'Was heute offen geblieben ist, bleibt offen. Morgen ist ein neuer Tag.', + durationSec: 120, + }, + }, + ], + }, ]; diff --git a/apps/mana/apps/web/src/lib/modules/ai-rituals/ListView.svelte b/apps/mana/apps/web/src/lib/modules/ai-rituals/ListView.svelte deleted file mode 100644 index 6440944b7..000000000 --- a/apps/mana/apps/web/src/lib/modules/ai-rituals/ListView.svelte +++ /dev/null @@ -1,170 +0,0 @@ - - - -
- {#if activeRitual} - - (activeRitual = null)} - onClose={() => (activeRitual = null)} - /> - {:else} -
- -
- - {#if showTemplates} -
- {#each RITUAL_TEMPLATES as t} - - {/each} -
- {/if} - -
    - {#each rituals.value as r (r.id)} -
  • - - -
  • - {/each} - {#if rituals.value.length === 0 && !showTemplates} -
  • Noch keine Rituale — erstelle eines aus einer Vorlage oben.
  • - {/if} -
- {/if} -
- - diff --git a/apps/mana/apps/web/src/lib/modules/companion/components/RitualRunner.svelte b/apps/mana/apps/web/src/lib/modules/companion/components/RitualRunner.svelte index 89aca1512..b14c4617f 100644 --- a/apps/mana/apps/web/src/lib/modules/companion/components/RitualRunner.svelte +++ b/apps/mana/apps/web/src/lib/modules/companion/components/RitualRunner.svelte @@ -5,13 +5,13 @@ displays projection data. Tracks progress and logs completion. --> + +
+ {#if activeRitual} + + (activeRitual = null)} + onClose={() => (activeRitual = null)} + /> + {:else} +
+
+ + + +
+ +
+ + {#if showTemplates} +
+ {#each ['ceremony', 'utility', 'mixed'] as const as cat (cat)} + {#if templatesByCategory[cat].length > 0} +
+
{CATEGORY_LABELS[cat]}
+ {#each templatesByCategory[cat] as t (t.id)} + + {/each} +
+ {/if} + {/each} +
+ {/if} + +
    + {#each filteredRituals as r (r.id)} +
  • + + +
  • + {/each} + {#if filteredRituals.length === 0 && !showTemplates} +
  • + {filter === 'all' + ? 'Noch keine Rituale — erstelle eines aus einer Vorlage oben.' + : 'Keine Rituale in dieser Kategorie.'} +
  • + {/if} +
+ {/if} +
+ + diff --git a/docs/MODULE_REGISTRY.md b/docs/MODULE_REGISTRY.md index 5450b66d2..5df3cfdab 100644 --- a/docs/MODULE_REGISTRY.md +++ b/docs/MODULE_REGISTRY.md @@ -18,7 +18,7 @@ Alle 73 Module der Mana-App (`apps/mana/apps/web/src/lib/modules/`). | `inventory` | Inventory | Besitz verwalten mit Fotos, Quittungen, Garantien | | `calc` | Calc | Taschenrechner (Standard, Scientific, Programmer, Unit-Conversion) | -## Gesundheit & Wellness (10) +## Gesundheit & Wellness (11) | Modul | Name | Beschreibung | |---|---|---| @@ -30,6 +30,7 @@ Alle 73 Module der Mana-App (`apps/mana/apps/web/src/lib/modules/`). | `period` | Periode | Menstruations-Tracking mit Vorhersagen und Phasen-Erkennung | | `stretch` | Stretch | Mobility-Assessments und geführte Dehn-Routinen | | `meditate` | Meditate | Meditations-Timer, Atemübungen, Body-Scans | +| `rituals` | Rituale | Geführte Sequenzen — utility (Daten-Erfassung) + ceremony (Präsenz) | | `mood` | Mood | Stimmungs-Tracking mehrmals täglich mit Kontext und Mustern | | `moodlit` | Moodlit | Beruhigende Ambient-Beleuchtung mit animierten Farbverläufen | @@ -100,7 +101,7 @@ Alle 73 Module der Mana-App (`apps/mana/apps/web/src/lib/modules/`). | `myday` | Mein Tag | Tagesübersicht: Tasks, Events, Wasser, Ernährung, Streaks | | `activity` | Aktivität | Live-Activity-Stream über alle Module | -## AI-System (9) +## AI-System (8) | Modul | Name | Beschreibung | |---|---|---| @@ -109,7 +110,6 @@ Alle 73 Module der Mana-App (`apps/mana/apps/web/src/lib/modules/`). | `ai-missions` | AI Missions | Langlebige autonome AI-Aufträge | | `ai-agents` | AI Agents | AI-Agenten erstellen und verwalten | | `ai-policy` | AI Policy | Tool-Execution-Policies konfigurieren | -| `ai-rituals` | AI Rituals | AI-gesteuerte Routinen und Rituale | | `ai-health` | AI Health | AI-System-Monitoring und Metriken | | `ai-insights` | AI Insights | AI-generierte Insights aus User-Daten | | `news-research` | News Research | RSS-Feed-Discovery und Keyword-Suche |