diff --git a/CLAUDE.md b/CLAUDE.md index 7622abab1..35d10b9cc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -538,6 +538,11 @@ Logged in: App → IndexedDB → UI → SyncEngine → mana-sync (Go) → Postg | NutriPhi | meals, goals, favorites | Done | | Planta | plants, plantPhotos, wateringSchedules, wateringLogs | Done | | Storage | files, folders, tags, fileTags | Done | +| Chat | conversations, messages, templates | Done | +| Questions | collections, questions, answers | Done | +| Mukke | songs, playlists, playlistSongs, projects, markers | Done | +| Context | spaces, documents | Done | +| Photos | albums, albumItems, favorites, tags, photoTags | Done | ### Dev Commands (Local-First Stack) diff --git a/apps/chat/apps/web/package.json b/apps/chat/apps/web/package.json index b834159cd..22c376859 100644 --- a/apps/chat/apps/web/package.json +++ b/apps/chat/apps/web/package.json @@ -37,6 +37,7 @@ "@manacore/shared-app-onboarding": "workspace:*", "@manacore/shared-auth": "workspace:*", "@manacore/shared-auth-ui": "workspace:*", + "@manacore/local-store": "workspace:*", "@manacore/shared-branding": "workspace:*", "@manacore/shared-error-tracking": "workspace:*", "@manacore/shared-feedback-service": "workspace:*", diff --git a/apps/chat/apps/web/src/lib/data/guest-seed.ts b/apps/chat/apps/web/src/lib/data/guest-seed.ts new file mode 100644 index 000000000..65b4ff321 --- /dev/null +++ b/apps/chat/apps/web/src/lib/data/guest-seed.ts @@ -0,0 +1,30 @@ +/** + * Guest seed data for the Chat app. + * + * Provides a demo conversation to showcase the chat experience. + */ + +import type { LocalConversation, LocalMessage } from './local-store'; + +const DEMO_CONVERSATION_ID = 'demo-welcome'; + +export const guestConversations: LocalConversation[] = [ + { + id: DEMO_CONVERSATION_ID, + title: 'Willkommen bei Chat!', + conversationMode: 'free', + documentMode: false, + isArchived: false, + isPinned: true, + }, +]; + +export const guestMessages: LocalMessage[] = [ + { + id: 'msg-1', + conversationId: DEMO_CONVERSATION_ID, + sender: 'assistant', + messageText: + 'Hallo! Ich bin dein KI-Assistent. Du kannst mir Fragen stellen, Texte schreiben lassen oder einfach ein Gespräch führen. Melde dich an, um deine Unterhaltungen zu speichern und zu synchronisieren.', + }, +]; diff --git a/apps/chat/apps/web/src/lib/data/local-store.ts b/apps/chat/apps/web/src/lib/data/local-store.ts new file mode 100644 index 000000000..fb9030ecd --- /dev/null +++ b/apps/chat/apps/web/src/lib/data/local-store.ts @@ -0,0 +1,71 @@ +/** + * Chat — Local-First Data Layer + * + * Conversations, messages, and templates stored locally. + * LLM streaming and model management remain server-side. + */ + +import { createLocalStore, type BaseRecord } from '@manacore/local-store'; +import { guestConversations, guestMessages } from './guest-seed'; + +// ─── Types ────────────────────────────────────────────────── + +export interface LocalConversation extends BaseRecord { + title?: string | null; + modelId?: string | null; + templateId?: string | null; + spaceId?: string | null; + conversationMode: 'free' | 'guided' | 'template'; + documentMode: boolean; + isArchived: boolean; + isPinned: boolean; +} + +export interface LocalMessage extends BaseRecord { + conversationId: string; + sender: 'user' | 'assistant' | 'system'; + messageText: string; +} + +export interface LocalTemplate extends BaseRecord { + name: string; + description: string; + systemPrompt: string; + initialQuestion?: string | null; + modelId?: string | null; + color: string; + isDefault: boolean; + documentMode: boolean; +} + +// ─── Store ────────────────────────────────────────────────── + +const SYNC_SERVER_URL = import.meta.env.PUBLIC_SYNC_SERVER_URL || 'http://localhost:3050'; + +export const chatStore = createLocalStore({ + appId: 'chat', + collections: [ + { + name: 'conversations', + indexes: ['isArchived', 'isPinned', 'spaceId', 'templateId'], + guestSeed: guestConversations, + }, + { + name: 'messages', + indexes: ['conversationId', 'sender', '[conversationId+sender]'], + guestSeed: guestMessages, + }, + { + name: 'templates', + indexes: ['isDefault'], + }, + ], + sync: { + serverUrl: SYNC_SERVER_URL, + }, +}); + +// Typed collection accessors +export const conversationCollection = chatStore.collection('conversations'); +export const messageCollection = chatStore.collection('messages'); +export const templateCollection = chatStore.collection('templates'); diff --git a/apps/chat/apps/web/src/routes/(protected)/+layout.svelte b/apps/chat/apps/web/src/routes/(protected)/+layout.svelte index e8c8556a8..809603e8d 100644 --- a/apps/chat/apps/web/src/routes/(protected)/+layout.svelte +++ b/apps/chat/apps/web/src/routes/(protected)/+layout.svelte @@ -24,7 +24,9 @@ import type { LayoutData } from './$types'; import { chatOnboarding } from '$lib/stores/app-onboarding.svelte'; import { MiniOnboardingModal } from '@manacore/shared-app-onboarding'; - import { SessionExpiredBanner, AuthGate } from '@manacore/shared-auth-ui'; + import { SessionExpiredBanner, AuthGate, GuestWelcomeModal } from '@manacore/shared-auth-ui'; + import { shouldShowGuestWelcome } from '@manacore/shared-auth-ui'; + import { chatStore } from '$lib/data/local-store'; // App switcher items const appItems = getPillAppItems('chat'); @@ -156,7 +158,18 @@ goto('/login'); } + let showGuestWelcome = $state(false); + async function handleAuthReady() { + // Initialize local-first database + await chatStore.initialize(); + if (authStore.isAuthenticated) { + chatStore.startSync(() => authStore.getValidToken()); + } + if (!authStore.isAuthenticated && shouldShowGuestWelcome('chat')) { + showGuestWelcome = true; + } + // Initialize theme theme.initialize(); @@ -167,6 +180,8 @@ collapsedStore.set(true); } + if (!authStore.isAuthenticated) return; + // Load user settings and tags await userSettings.load(); await tagStore.fetchTags(); @@ -186,7 +201,7 @@ - +
@@ -256,7 +271,18 @@ {/if} - + {#if authStore.isAuthenticated} + + {/if} + + (showGuestWelcome = false)} + onLogin={() => goto('/login')} + onRegister={() => goto('/register')} + locale={($locale || 'de') === 'de' ? 'de' : 'en'} + />