feat: add onboarding to 6 new apps and feature intro step to all 16 apps

Add onboarding with feature overview, preference selection, and tips to
Zitare, Mukke, Photos, Planta, SkillTree, and Questions. Insert a new
first "features" info step into all 10 existing onboarding flows so every
app now starts with a core-features overview page.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-23 21:58:14 +01:00
parent 89ca3b0454
commit 250e0b20af
35 changed files with 872 additions and 0 deletions

View file

@ -7,6 +7,20 @@ import type { CalendarViewType } from '@calendar/shared';
* Calendar-specific onboarding steps
*/
const calendarOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Kalender!',
description: 'Das kann Kalender für dich tun:',
emoji: '📅',
gradient: { from: 'blue-500', to: 'blue-700' },
bullets: [
'Termine erstellen & verwalten',
'Wochen-, Monats- & Agenda-Ansicht',
'Schnelleingabe per Text',
'Drag & Drop zum Verschieben',
],
},
{
id: 'weekStart',
type: 'select',

View file

@ -5,6 +5,20 @@ import { userSettings } from './user-settings.svelte';
* Chat-specific onboarding steps
*/
const chatOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Chat!',
description: 'Das kann Chat für dich tun:',
emoji: '💬',
gradient: { from: 'blue-500', to: 'blue-700' },
bullets: [
'KI-Chat mit lokalen & Cloud-Modellen',
'Konversationen organisieren & durchsuchen',
'Dateien & Bilder teilen',
'Code-Highlighting & Markdown',
],
},
{
id: 'defaultModel',
type: 'select',

View file

@ -5,6 +5,20 @@ import { userSettings } from './user-settings.svelte';
* Clock-specific onboarding steps
*/
const clockOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Clock!',
description: 'Das kann Clock für dich tun:',
emoji: '🕐',
gradient: { from: 'blue-500', to: 'blue-700' },
bullets: [
'Flexible Timer & Stoppuhr',
'Pomodoro-Technik für produktives Arbeiten',
'Voreingestellte Timer-Dauern',
'Minimalistisches Design',
],
},
{
id: 'defaultTimer',
type: 'select',

View file

@ -6,6 +6,20 @@ import { contactsFilterStore } from './filter.svelte';
* Contacts-specific onboarding steps
*/
const contactsOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Kontakte!',
description: 'Das kann Kontakte für dich tun:',
emoji: '👥',
gradient: { from: 'blue-500', to: 'blue-700' },
bullets: [
'Kontakte verwalten & organisieren',
'Import von Google oder vCard/CSV',
'Tags & Gruppen für bessere Übersicht',
'Schnellsuche & Filter',
],
},
{
id: 'sortOrder',
type: 'select',

View file

@ -5,6 +5,20 @@ import { userSettings } from './user-settings.svelte';
* Context-specific onboarding steps
*/
const contextOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Context!',
description: 'Das kann Context für dich tun:',
emoji: '📄',
gradient: { from: 'blue-500', to: 'blue-700' },
bullets: [
'Dokumente & Wissen organisieren',
'KI-gestützte Zusammenfassungen',
'Persönliche Wissensdatenbank',
'Schnelle Volltextsuche',
],
},
{
id: 'useCase',
type: 'select',

View file

@ -5,6 +5,20 @@ import { userSettings } from './user-settings.svelte';
* ManaDeck-specific onboarding steps
*/
const manadeckOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei ManaDeck!',
description: 'Das kann ManaDeck für dich tun:',
emoji: '🃏',
gradient: { from: 'blue-500', to: 'blue-700' },
bullets: [
'Kartendecks erstellen & lernen',
'Spaced Repetition für optimales Lernen',
'Fortschritt tracken',
'Verschiedene Kartentypen',
],
},
{
id: 'startAction',
type: 'select',

View file

@ -52,6 +52,7 @@
"@manacore/shared-theme-ui": "workspace:*",
"@manacore/shared-ui": "workspace:*",
"@manacore/shared-utils": "workspace:*",
"@manacore/shared-app-onboarding": "workspace:*",
"@mukke/shared": "workspace:*",
"butterchurn": "^2.6.7",
"butterchurn-presets": "^2.4.7",

View file

@ -0,0 +1,64 @@
import { createAppOnboardingStore, type AppOnboardingStep } from '@manacore/shared-app-onboarding';
import { userSettings } from './user-settings.svelte';
const mukkeOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Mukke!',
description: 'Das kann Mukke für dich tun:',
emoji: '🎵',
gradient: { from: 'purple-500', to: 'purple-700' },
bullets: [
'Musik-Bibliothek mit Alben & Künstlern',
'Playlists erstellen & verwalten',
'Beat-Editor mit Waveform-Visualisierung',
'BPM-Erkennung & Lyrics-Sync',
],
},
{
id: 'viewMode',
type: 'select',
question: 'Wie möchtest du deine Bibliothek sehen?',
description: 'Du kannst die Ansicht jederzeit wechseln.',
emoji: '📚',
gradient: { from: 'indigo-500', to: 'indigo-700' },
options: [
{
id: 'grid',
label: 'Grid-Ansicht',
description: 'Cover-Art im Raster (Empfohlen)',
emoji: '🎨',
},
{
id: 'list',
label: 'Listenansicht',
description: 'Kompakte Liste mit Details',
emoji: '📋',
},
],
defaultValue: 'grid',
},
{
id: 'welcome',
type: 'info',
question: 'Deine Musik wartet!',
description: 'Hier sind einige Tipps:',
emoji: '🎉',
gradient: { from: 'primary', to: 'primary/70' },
bullets: [
'Lade Songs per Drag & Drop hoch',
'ID3-Tags werden automatisch erkannt',
'Erstelle Projekte im Beat-Editor',
'Exportiere synchronisierte Lyrics als LRC oder SRT',
],
},
];
export const mukkeOnboarding = createAppOnboardingStore({
appId: 'mukke',
steps: mukkeOnboardingSteps,
userSettings,
onComplete: async () => {},
onSkip: async () => {},
});

View file

@ -0,0 +1,18 @@
import { browser } from '$app/environment';
import { createUserSettingsStore } from '@manacore/shared-theme';
import { authStore } from './auth.svelte';
function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001';
}
return 'http://localhost:3001';
}
export const userSettings = createUserSettingsStore({
appId: 'mukke',
authUrl: getAuthUrl(),
getAccessToken: () => authStore.getValidToken(),
});

View file

@ -3,6 +3,8 @@
import { onMount, onDestroy } from 'svelte';
import { authStore } from '$lib/stores/auth.svelte';
import { theme } from '$lib/stores/theme.svelte';
import { MiniOnboardingModal } from '@manacore/shared-app-onboarding';
import { mukkeOnboarding } from '$lib/stores/app-onboarding.svelte';
let { children } = $props();
@ -34,4 +36,8 @@
<div class="min-h-screen bg-background text-foreground">
{@render children()}
</div>
{#if mukkeOnboarding.shouldShow}
<MiniOnboardingModal store={mukkeOnboarding} appName="Mukke" appEmoji="🎵" />
{/if}
{/if}

View file

@ -52,6 +52,7 @@
"@manacore/shared-theme-ui": "workspace:*",
"@manacore/shared-ui": "workspace:*",
"@manacore/shared-utils": "workspace:*",
"@manacore/shared-app-onboarding": "workspace:*",
"@photos/shared": "workspace:*",
"date-fns": "^4.1.0",
"svelte-i18n": "^4.0.1"

View file

@ -0,0 +1,70 @@
import { createAppOnboardingStore, type AppOnboardingStep } from '@manacore/shared-app-onboarding';
import { userSettings } from './user-settings.svelte';
const photosOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Photos!',
description: 'Das kann Photos für dich tun:',
emoji: '📸',
gradient: { from: 'emerald-500', to: 'emerald-700' },
bullets: [
'Fotos aus allen ManaCore-Apps an einem Ort',
'Alben erstellen & organisieren',
'Smart-Alben nach Datum, Ort & Kamera',
'EXIF-Daten & Metadaten anzeigen',
],
},
{
id: 'gridSize',
type: 'select',
question: 'Welche Galerie-Größe bevorzugst du?',
description: 'Du kannst die Größe jederzeit anpassen.',
emoji: '🖼️',
gradient: { from: 'indigo-500', to: 'indigo-700' },
options: [
{
id: 'small',
label: 'Klein',
description: 'Viele Fotos auf einen Blick',
emoji: '🔍',
},
{
id: 'medium',
label: 'Mittel',
description: 'Gute Balance (Empfohlen)',
emoji: '📐',
},
{
id: 'large',
label: 'Groß',
description: 'Detailreiche Vorschau',
emoji: '🖼️',
},
],
defaultValue: 'medium',
},
{
id: 'welcome',
type: 'info',
question: 'Deine Galerie ist bereit!',
description: 'Hier sind einige Tipps:',
emoji: '🎉',
gradient: { from: 'primary', to: 'primary/70' },
bullets: [
'Lade neue Fotos direkt per Drag & Drop hoch',
'Markiere Favoriten für schnellen Zugriff',
'Nutze Tags für bessere Organisation',
'Smart-Alben werden automatisch erstellt',
],
},
];
export const photosOnboarding = createAppOnboardingStore({
appId: 'photos',
steps: photosOnboardingSteps,
userSettings,
onComplete: async () => {},
onSkip: async () => {},
});

View file

@ -0,0 +1,18 @@
import { browser } from '$app/environment';
import { createUserSettingsStore } from '@manacore/shared-theme';
import { authStore } from './auth.svelte';
function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001';
}
return 'http://localhost:3001';
}
export const userSettings = createUserSettingsStore({
appId: 'photos',
authUrl: getAuthUrl(),
getAccessToken: () => authStore.getAccessToken(),
});

View file

@ -6,6 +6,8 @@
import { theme } from '$lib/stores/theme';
import { authStore } from '$lib/stores/auth.svelte';
import { ToastContainer, setupGlobalErrorHandler } from '@manacore/shared-ui';
import { MiniOnboardingModal } from '@manacore/shared-app-onboarding';
import { photosOnboarding } from '$lib/stores/app-onboarding.svelte';
let { children } = $props();
@ -32,6 +34,10 @@
<div class="min-h-screen bg-background text-foreground">
{@render children()}
</div>
{#if photosOnboarding.shouldShow}
<MiniOnboardingModal store={photosOnboarding} appName="Photos" appEmoji="📸" />
{/if}
{/if}
<ToastContainer />

View file

@ -5,6 +5,20 @@ import { userSettings } from './user-settings.svelte';
* Picture-specific onboarding steps
*/
const pictureOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Picture!',
description: 'Das kann Picture für dich tun:',
emoji: '🎨',
gradient: { from: 'indigo-500', to: 'indigo-700' },
bullets: [
'KI-Bilder generieren',
'Verschiedene Modelle & Stile',
'Galerie mit Grid-Ansicht',
'Bilder teilen & herunterladen',
],
},
{
id: 'viewMode',
type: 'select',

View file

@ -42,6 +42,7 @@
"@manacore/shared-theme-ui": "workspace:*",
"@manacore/shared-ui": "workspace:*",
"@manacore/shared-utils": "workspace:*",
"@manacore/shared-app-onboarding": "workspace:*",
"@planta/shared": "workspace:*",
"svelte-i18n": "^4.0.1"
},

View file

@ -0,0 +1,70 @@
import { createAppOnboardingStore, type AppOnboardingStep } from '@manacore/shared-app-onboarding';
import { userSettings } from './user-settings.svelte';
const plantaOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Planta!',
description: 'Das kann Planta für dich tun:',
emoji: '🌱',
gradient: { from: 'green-500', to: 'green-700' },
bullets: [
'Pflanzen per Foto identifizieren (KI)',
'Gieß-Erinnerungen & Pflege-Zeitplan',
'Pflege-Tipps & Gesundheits-Tracking',
'Licht-, Wasser- & Feuchtigkeitsempfehlungen',
],
},
{
id: 'experience',
type: 'select',
question: 'Wie erfahren bist du mit Pflanzen?',
description: 'Hilft uns, die Tipps anzupassen.',
emoji: '🌿',
gradient: { from: 'emerald-500', to: 'emerald-700' },
options: [
{
id: 'beginner',
label: 'Anfänger',
description: 'Gerade erst angefangen',
emoji: '🌱',
},
{
id: 'intermediate',
label: 'Fortgeschritten',
description: 'Einige Pflanzen zu Hause',
emoji: '🪴',
},
{
id: 'expert',
label: 'Profi',
description: 'Grüner Daumen seit Jahren',
emoji: '🌳',
},
],
defaultValue: 'beginner',
},
{
id: 'welcome',
type: 'info',
question: 'Dein Pflanzengarten ist bereit!',
description: 'Hier sind einige Tipps:',
emoji: '🎉',
gradient: { from: 'primary', to: 'primary/70' },
bullets: [
'Fotografiere eine Pflanze, um sie zu identifizieren',
'Gieß-Erinnerungen kommen automatisch',
'Prüfe regelmäßig den Gesundheitsstatus',
'Notiere Standort und Lichtverhältnisse',
],
},
];
export const plantaOnboarding = createAppOnboardingStore({
appId: 'planta',
steps: plantaOnboardingSteps,
userSettings,
onComplete: async () => {},
onSkip: async () => {},
});

View file

@ -0,0 +1,18 @@
import { browser } from '$app/environment';
import { createUserSettingsStore } from '@manacore/shared-theme';
import { authStore } from './auth.svelte';
function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001';
}
return 'http://localhost:3001';
}
export const userSettings = createUserSettingsStore({
appId: 'planta',
authUrl: getAuthUrl(),
getAccessToken: () => authStore.getValidToken(),
});

View file

@ -5,6 +5,8 @@
import { isLoading as i18nLoading, _ as t } from 'svelte-i18n';
import { theme } from '$lib/stores/theme';
import { authStore } from '$lib/stores/auth.svelte';
import { MiniOnboardingModal } from '@manacore/shared-app-onboarding';
import { plantaOnboarding } from '$lib/stores/app-onboarding.svelte';
let { children } = $props();
@ -35,4 +37,8 @@
<div class="min-h-screen bg-background text-foreground">
{@render children()}
</div>
{#if plantaOnboarding.shouldShow}
<MiniOnboardingModal store={plantaOnboarding} appName="Planta" appEmoji="🌱" />
{/if}
{/if}

View file

@ -5,6 +5,20 @@ import { userSettings } from './user-settings.svelte';
* Presi-specific onboarding steps
*/
const presiOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Presi!',
description: 'Das kann Presi für dich tun:',
emoji: '🎬',
gradient: { from: 'blue-500', to: 'blue-700' },
bullets: [
'Präsentationen erstellen & bearbeiten',
'Slide-Editor mit verschiedenen Folientypen',
'Presenter-Modus für Vorträge',
'Teilen über öffentliche Links',
],
},
{
id: 'welcome',
type: 'info',

View file

@ -41,6 +41,7 @@
"@manacore/shared-error-tracking": "workspace:*",
"@manacore/shared-i18n": "workspace:*",
"@manacore/shared-icons": "workspace:*",
"@manacore/shared-app-onboarding": "workspace:*",
"@manacore/shared-tailwind": "workspace:*",
"@manacore/shared-theme": "workspace:*",
"@manacore/shared-theme-ui": "workspace:*",

View file

@ -0,0 +1,70 @@
import { createAppOnboardingStore, type AppOnboardingStep } from '@manacore/shared-app-onboarding';
import { userSettings } from './user-settings.svelte';
const questionsOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Questions!',
description: 'Das kann Questions für dich tun:',
emoji: '🔬',
gradient: { from: 'violet-500', to: 'violet-700' },
bullets: [
'Fragen stellen & recherchieren lassen',
'Web-Suche & automatische Quellenextraktion',
'KI-generierte Antworten aus Quellen',
'Sammlungen für bessere Organisation',
],
},
{
id: 'researchDepth',
type: 'select',
question: 'Wie gründlich soll die Recherche sein?',
description: 'Du kannst die Tiefe pro Frage anpassen.',
emoji: '🔍',
gradient: { from: 'indigo-500', to: 'indigo-700' },
options: [
{
id: 'quick',
label: 'Schnell',
description: '5 Quellen, schnelle Antwort',
emoji: '⚡',
},
{
id: 'standard',
label: 'Standard',
description: '15 Quellen mit Extraktion (Empfohlen)',
emoji: '📖',
},
{
id: 'deep',
label: 'Gründlich',
description: '30 Quellen, alle Kategorien',
emoji: '🔬',
},
],
defaultValue: 'standard',
},
{
id: 'welcome',
type: 'info',
question: 'Dein Recherche-Assistent ist bereit!',
description: 'Hier sind einige Tipps:',
emoji: '🎉',
gradient: { from: 'primary', to: 'primary/70' },
bullets: [
'Stelle präzise Fragen für bessere Ergebnisse',
'Nutze Sammlungen, um Themen zu gruppieren',
'Quellen werden automatisch extrahiert',
'Bewerte Antworten, um die Qualität zu verbessern',
],
},
];
export const questionsOnboarding = createAppOnboardingStore({
appId: 'questions',
steps: questionsOnboardingSteps,
userSettings,
onComplete: async () => {},
onSkip: async () => {},
});

View file

@ -0,0 +1,18 @@
import { browser } from '$app/environment';
import { createUserSettingsStore } from '@manacore/shared-theme';
import { authStore } from './auth.svelte';
function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001';
}
return 'http://localhost:3001';
}
export const userSettings = createUserSettingsStore({
appId: 'questions',
authUrl: getAuthUrl(),
getAccessToken: () => authStore.getValidToken(),
});

View file

@ -7,6 +7,8 @@
import { authStore } from '$lib/stores/auth.svelte';
import { apiClient } from '$lib/api/client';
import { AppLoadingSkeleton } from '$lib/components/skeletons';
import { MiniOnboardingModal } from '@manacore/shared-app-onboarding';
import { questionsOnboarding } from '$lib/stores/app-onboarding.svelte';
let { children } = $props();
@ -33,4 +35,8 @@
<div class="min-h-screen bg-background text-foreground">
{@render children()}
</div>
{#if questionsOnboarding.shouldShow}
<MiniOnboardingModal store={questionsOnboarding} appName="Questions" appEmoji="🔬" />
{/if}
{/if}

View file

@ -41,6 +41,7 @@
"@manacore/shared-error-tracking": "workspace:*",
"@manacore/shared-i18n": "workspace:*",
"@manacore/shared-icons": "workspace:*",
"@manacore/shared-app-onboarding": "workspace:*",
"@manacore/shared-tailwind": "workspace:*",
"@manacore/shared-theme": "workspace:*",
"@manacore/shared-ui": "workspace:^",

View file

@ -0,0 +1,88 @@
import { createAppOnboardingStore, type AppOnboardingStep } from '@manacore/shared-app-onboarding';
import { userSettings } from './user-settings.svelte';
const skilltreeOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei SkillTree!',
description: 'Das kann SkillTree für dich tun:',
emoji: '🌳',
gradient: { from: 'emerald-500', to: 'emerald-700' },
bullets: [
'RPG-Skill-Baum für echte Fähigkeiten',
'6 Bereiche: Intellekt, Körper, Kreativität, Sozial, Praktisch, Mindset',
'XP sammeln & Level aufsteigen',
'Aktivitäten loggen & Fortschritt tracken',
],
},
{
id: 'focusBranch',
type: 'select',
question: 'Welcher Bereich interessiert dich am meisten?',
description: 'Du kannst alle Bereiche nutzen — das ist nur dein Startfokus.',
emoji: '🎯',
gradient: { from: 'indigo-500', to: 'indigo-700' },
options: [
{
id: 'intellect',
label: 'Intellekt',
description: 'Wissen, Sprachen, Wissenschaft',
emoji: '🧠',
},
{
id: 'body',
label: 'Körper',
description: 'Fitness, Sport, Gesundheit',
emoji: '💪',
},
{
id: 'creativity',
label: 'Kreativität',
description: 'Kunst, Musik, Schreiben',
emoji: '🎨',
},
{
id: 'social',
label: 'Sozial',
description: 'Kommunikation, Führung',
emoji: '👥',
},
{
id: 'practical',
label: 'Praktisch',
description: 'Handwerk, Kochen, Technik',
emoji: '🔧',
},
{
id: 'mindset',
label: 'Mindset',
description: 'Meditation, Fokus, Resilienz',
emoji: '🧘',
},
],
defaultValue: 'intellect',
},
{
id: 'welcome',
type: 'info',
question: 'Dein Skill-Baum ist bereit!',
description: 'Hier sind einige Tipps:',
emoji: '🎉',
gradient: { from: 'primary', to: 'primary/70' },
bullets: [
'Erstelle Skills in deinem Fokus-Bereich',
'Logge Aktivitäten, um XP zu sammeln',
'Level reichen von Anfänger bis Meister',
'Bleib dran — regelmäßige Aktivitäten geben Bonus-XP',
],
},
];
export const skilltreeOnboarding = createAppOnboardingStore({
appId: 'skilltree',
steps: skilltreeOnboardingSteps,
userSettings,
onComplete: async () => {},
onSkip: async () => {},
});

View file

@ -0,0 +1,18 @@
import { browser } from '$app/environment';
import { createUserSettingsStore } from '@manacore/shared-theme';
import { authStore } from './auth.svelte';
function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001';
}
return 'http://localhost:3001';
}
export const userSettings = createUserSettingsStore({
appId: 'skilltree',
authUrl: getAuthUrl(),
getAccessToken: () => authStore.getAccessToken(),
});

View file

@ -5,6 +5,8 @@
import { isLoading as i18nLoading, _ as t } from 'svelte-i18n';
import { skillStore } from '$lib/stores/skills.svelte';
import { authStore } from '$lib/stores/auth.svelte';
import { MiniOnboardingModal } from '@manacore/shared-app-onboarding';
import { skilltreeOnboarding } from '$lib/stores/app-onboarding.svelte';
let { children } = $props();
@ -33,4 +35,8 @@
<div class="min-h-screen bg-gray-900 text-gray-100">
{@render children()}
</div>
{#if skilltreeOnboarding.shouldShow}
<MiniOnboardingModal store={skilltreeOnboarding} appName="SkillTree" appEmoji="🌳" />
{/if}
{/if}

View file

@ -5,6 +5,20 @@ import { userSettings } from './user-settings.svelte';
* Storage-specific onboarding steps
*/
const storageOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Storage!',
description: 'Das kann Storage für dich tun:',
emoji: '☁️',
gradient: { from: 'blue-500', to: 'blue-700' },
bullets: [
'Dateien hochladen & verwalten',
'Ordnerstruktur & Tags',
'Sichere Links mit Passwortschutz',
'Drag & Drop Upload',
],
},
{
id: 'welcome',
type: 'info',

View file

@ -6,6 +6,20 @@ import { todoSettings } from './settings.svelte';
* Todo-specific onboarding steps
*/
const todoOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Todo!',
description: 'Das kann Todo für dich tun:',
emoji: '✅',
gradient: { from: 'blue-500', to: 'blue-700' },
bullets: [
'Aufgaben erstellen & verwalten',
'Heute-, Inbox- & Kanban-Ansichten',
'Fälligkeiten & Prioritäten',
'Schnelleingabe per Text',
],
},
{
id: 'defaultView',
type: 'select',

View file

@ -49,6 +49,7 @@
"@manacore/shared-theme-ui": "workspace:*",
"@manacore/shared-ui": "workspace:*",
"@manacore/spiral-db": "workspace:^",
"@manacore/shared-app-onboarding": "workspace:*",
"@zitare/content": "workspace:*",
"svelte-i18n": "^4.0.1"
},

View file

@ -0,0 +1,64 @@
import { createAppOnboardingStore, type AppOnboardingStep } from '@manacore/shared-app-onboarding';
import { userSettings } from './user-settings.svelte';
const zitareOnboardingSteps: AppOnboardingStep[] = [
{
id: 'features',
type: 'info',
question: 'Willkommen bei Zitare!',
description: 'Das kann Zitare für dich tun:',
emoji: '✨',
gradient: { from: 'amber-500', to: 'amber-700' },
bullets: [
'Tägliche Inspiration durch ausgewählte Zitate',
'Zitate-Sammlung zum Stöbern & Entdecken',
'Immersiver Lesemodus für ungestörtes Lesen',
'Favoriten & eigene Listen erstellen',
],
},
{
id: 'displayMode',
type: 'select',
question: 'Wie möchtest du Zitate lesen?',
description: 'Du kannst den Modus jederzeit wechseln.',
emoji: '👁️',
gradient: { from: 'indigo-500', to: 'indigo-700' },
options: [
{
id: 'compact',
label: 'Kompakt',
description: 'Übersichtliche Listenansicht',
emoji: '📋',
},
{
id: 'immersive',
label: 'Immersiv',
description: 'Großes Zitat mit Hintergrund (Empfohlen)',
emoji: '🖼️',
},
],
defaultValue: 'immersive',
},
{
id: 'welcome',
type: 'info',
question: 'Deine Zitate sind bereit!',
description: 'Hier sind einige Tipps:',
emoji: '🎉',
gradient: { from: 'primary', to: 'primary/70' },
bullets: [
'Swipe oder klicke für das nächste Zitat',
'Markiere Favoriten mit dem Herz-Symbol',
'Erstelle eigene Sammlungen',
'Teile Zitate mit Freunden',
],
},
];
export const zitareOnboarding = createAppOnboardingStore({
appId: 'zitare',
steps: zitareOnboardingSteps,
userSettings,
onComplete: async () => {},
onSkip: async () => {},
});

View file

@ -0,0 +1,18 @@
import { browser } from '$app/environment';
import { createUserSettingsStore } from '@manacore/shared-theme';
import { authStore } from './auth.svelte';
function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001';
}
return 'http://localhost:3001';
}
export const userSettings = createUserSettingsStore({
appId: 'zitare',
authUrl: getAuthUrl(),
getAccessToken: () => authStore.getAccessToken(),
});

View file

@ -7,6 +7,8 @@
import { quotesStore } from '$lib/stores/quotes.svelte';
import { waitLocale } from '$lib/i18n';
import { ToastContainer, setupGlobalErrorHandler } from '@manacore/shared-ui';
import { MiniOnboardingModal } from '@manacore/shared-app-onboarding';
import { zitareOnboarding } from '$lib/stores/app-onboarding.svelte';
let { children } = $props();
@ -54,4 +56,8 @@
<div class="min-h-screen bg-background text-foreground">
{@render children()}
</div>
{#if zitareOnboarding.shouldShow}
<MiniOnboardingModal store={zitareOnboarding} appName="Zitare" appEmoji="✨" />
{/if}
{/if}

156
pnpm-lock.yaml generated
View file

@ -455,6 +455,9 @@ importers:
'@manacore/shared-errors':
specifier: workspace:*
version: link:../../../../packages/shared-errors
'@manacore/shared-llm':
specifier: workspace:^
version: link:../../../../packages/shared-llm
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
@ -1619,6 +1622,9 @@ importers:
'@manacore/shared-error-tracking':
specifier: workspace:*
version: link:../../../../packages/shared-error-tracking
'@manacore/shared-llm':
specifier: workspace:^
version: link:../../../../packages/shared-llm
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
@ -3143,6 +3149,9 @@ importers:
'@manacore/shared-api-client':
specifier: workspace:*
version: link:../../../../packages/shared-api-client
'@manacore/shared-app-onboarding':
specifier: workspace:*
version: link:../../../../packages/shared-app-onboarding
'@manacore/shared-auth':
specifier: workspace:*
version: link:../../../../packages/shared-auth
@ -3279,6 +3288,9 @@ importers:
'@manacore/shared-error-tracking':
specifier: workspace:*
version: link:../../../../packages/shared-error-tracking
'@manacore/shared-llm':
specifier: workspace:^
version: link:../../../../packages/shared-llm
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
@ -3655,6 +3667,9 @@ importers:
'@manacore/shared-api-client':
specifier: workspace:*
version: link:../../../../packages/shared-api-client
'@manacore/shared-app-onboarding':
specifier: workspace:*
version: link:../../../../packages/shared-app-onboarding
'@manacore/shared-auth':
specifier: workspace:*
version: link:../../../../packages/shared-auth
@ -4380,6 +4395,9 @@ importers:
'@manacore/shared-error-tracking':
specifier: workspace:*
version: link:../../../../packages/shared-error-tracking
'@manacore/shared-llm':
specifier: workspace:^
version: link:../../../../packages/shared-llm
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
@ -4501,6 +4519,9 @@ importers:
'@manacore/shared-api-client':
specifier: workspace:*
version: link:../../../../packages/shared-api-client
'@manacore/shared-app-onboarding':
specifier: workspace:*
version: link:../../../../packages/shared-app-onboarding
'@manacore/shared-auth':
specifier: workspace:*
version: link:../../../../packages/shared-auth
@ -4997,6 +5018,9 @@ importers:
'@manacore/shared-api-client':
specifier: workspace:*
version: link:../../../../packages/shared-api-client
'@manacore/shared-app-onboarding':
specifier: workspace:*
version: link:../../../../packages/shared-app-onboarding
'@manacore/shared-auth':
specifier: workspace:*
version: link:../../../../packages/shared-auth
@ -5185,6 +5209,9 @@ importers:
'@manacore/shared-api-client':
specifier: workspace:*
version: link:../../../../packages/shared-api-client
'@manacore/shared-app-onboarding':
specifier: workspace:*
version: link:../../../../packages/shared-app-onboarding
'@manacore/shared-auth':
specifier: workspace:*
version: link:../../../../packages/shared-auth
@ -5844,6 +5871,9 @@ importers:
'@manacore/shared-error-tracking':
specifier: workspace:*
version: link:../../../../packages/shared-error-tracking
'@manacore/shared-llm':
specifier: workspace:^
version: link:../../../../packages/shared-llm
'@manacore/shared-nestjs-auth':
specifier: workspace:*
version: link:../../../../packages/shared-nestjs-auth
@ -6162,6 +6192,9 @@ importers:
'@manacore/shared-api-client':
specifier: workspace:*
version: link:../../../../packages/shared-api-client
'@manacore/shared-app-onboarding':
specifier: workspace:*
version: link:../../../../packages/shared-app-onboarding
'@manacore/shared-auth':
specifier: workspace:*
version: link:../../../../packages/shared-auth
@ -6854,6 +6887,34 @@ importers:
specifier: ^5.0.0
version: 5.9.3
packages/shared-llm:
dependencies:
'@nestjs/common':
specifier: ^10.0.0 || ^11.0.0
version: 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/config':
specifier: ^3.0.0 || ^4.0.0
version: 3.3.0(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(rxjs@7.8.2)
'@nestjs/core':
specifier: ^10.0.0 || ^11.0.0
version: 10.4.20(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(@nestjs/websockets@10.4.20)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2)
reflect-metadata:
specifier: ^0.1.13 || ^0.2.0
version: 0.2.2
rxjs:
specifier: ^7.0.0
version: 7.8.2
devDependencies:
'@types/node':
specifier: ^20.0.0
version: 20.19.25
typescript:
specifier: ^5.0.0
version: 5.9.3
vitest:
specifier: ^2.0.0
version: 2.1.9(@types/node@20.19.25)(jsdom@27.2.0)(lightningcss@1.30.2)(terser@5.44.1)
packages/shared-logger:
devDependencies:
'@types/node':
@ -7318,6 +7379,9 @@ importers:
'@google/generative-ai':
specifier: ^0.24.1
version: 0.24.1
'@manacore/shared-llm':
specifier: workspace:^
version: link:../../packages/shared-llm
'@manacore/shared-storage':
specifier: workspace:*
version: link:../../packages/shared-storage
@ -33824,6 +33888,23 @@ snapshots:
transitivePeerDependencies:
- encoding
'@nestjs/core@10.4.20(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(@nestjs/websockets@10.4.20)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
dependencies:
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nuxtjs/opencollective': 0.3.2(encoding@0.1.13)
fast-safe-stringify: 2.1.1
iterare: 1.2.1
path-to-regexp: 3.3.0
reflect-metadata: 0.2.2
rxjs: 7.8.2
tslib: 2.8.1
uid: 2.0.2
optionalDependencies:
'@nestjs/platform-express': 10.4.20(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)
'@nestjs/websockets': 10.4.20(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/platform-socket.io@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.2)
transitivePeerDependencies:
- encoding
'@nestjs/core@11.1.9(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.9)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
dependencies:
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
@ -33878,6 +33959,19 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@nestjs/platform-express@10.4.20(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)':
dependencies:
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/core': 10.4.20(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(@nestjs/websockets@10.4.20)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2)
body-parser: 1.20.3
cors: 2.8.5
express: 4.21.2
multer: 2.0.2
tslib: 2.8.1
transitivePeerDependencies:
- supports-color
optional: true
'@nestjs/platform-express@11.1.9(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.9)':
dependencies:
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
@ -33916,6 +34010,19 @@ snapshots:
- utf-8-validate
optional: true
'@nestjs/platform-socket.io@10.4.20(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@10.4.20)(rxjs@7.8.2)':
dependencies:
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/websockets': 10.4.20(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/platform-socket.io@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.2)
rxjs: 7.8.2
socket.io: 4.8.1
tslib: 2.8.1
transitivePeerDependencies:
- bufferutil
- supports-color
- utf-8-validate
optional: true
'@nestjs/schedule@4.1.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)':
dependencies:
'@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2)
@ -34085,6 +34192,19 @@ snapshots:
'@nestjs/platform-socket.io': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@10.4.20)(rxjs@7.8.2)
optional: true
'@nestjs/websockets@10.4.20(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/platform-socket.io@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
dependencies:
'@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)
'@nestjs/core': 10.4.20(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@10.4.20)(@nestjs/websockets@10.4.20)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.2)
iterare: 1.2.1
object-hash: 3.0.0
reflect-metadata: 0.2.2
rxjs: 7.8.2
tslib: 2.8.1
optionalDependencies:
'@nestjs/platform-socket.io': 10.4.20(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@10.4.20)(rxjs@7.8.2)
optional: true
'@netlify/blobs@10.4.1':
dependencies:
'@netlify/dev-utils': 4.3.2
@ -58518,6 +58638,42 @@ snapshots:
- supports-color
- terser
vitest@2.1.9(@types/node@20.19.25)(jsdom@27.2.0)(lightningcss@1.30.2)(terser@5.44.1):
dependencies:
'@vitest/expect': 2.1.9
'@vitest/mocker': 2.1.9(vite@5.4.21(@types/node@20.19.25)(lightningcss@1.30.2)(terser@5.44.1))
'@vitest/pretty-format': 2.1.9
'@vitest/runner': 2.1.9
'@vitest/snapshot': 2.1.9
'@vitest/spy': 2.1.9
'@vitest/utils': 2.1.9
chai: 5.3.3
debug: 4.4.3
expect-type: 1.2.2
magic-string: 0.30.21
pathe: 1.1.2
std-env: 3.10.0
tinybench: 2.9.0
tinyexec: 0.3.2
tinypool: 1.1.1
tinyrainbow: 1.2.0
vite: 5.4.21(@types/node@20.19.25)(lightningcss@1.30.2)(terser@5.44.1)
vite-node: 2.1.9(@types/node@20.19.25)(lightningcss@1.30.2)(terser@5.44.1)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 20.19.25
jsdom: 27.2.0
transitivePeerDependencies:
- less
- lightningcss
- msw
- sass
- sass-embedded
- stylus
- sugarss
- supports-color
- terser
vitest@2.1.9(@types/node@24.10.1)(jsdom@27.2.0)(lightningcss@1.30.2)(terser@5.44.1):
dependencies:
'@vitest/expect': 2.1.9