fix(brain): fix companion page not rendering — schema version + query robustness

- Bump companion tables to db.version(14) so the schema upgrade
  runs even on browsers that already saw v10 with only _events
- Add try/catch to useConversations() and useMessages() queries
  to prevent silent crashes if tables don't exist yet
- Use db.table() directly in chat store instead of collections.ts
  module-level references (avoids eager table resolution issues)
- Add min-height to empty state and companion page container

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-13 22:19:59 +02:00
parent 0847ec86e8
commit e1884cfbd1
4 changed files with 42 additions and 21 deletions

View file

@ -426,13 +426,17 @@ db.version(9).stores({
db.version(10).stores({
_events:
'++seq, type, meta.appId, meta.timestamp, meta.recordId, [meta.appId+meta.timestamp], [type+meta.timestamp]',
// Companion Brain: Goals, Memory, Feedback, Chat
});
// v14 — Companion Brain: Goals, Memory, Feedback, Chat, Rituals.
// Bumped to v14 (past mail/stretch/meditate/sleep) to ensure schema
// upgrade runs even if the browser already saw an earlier v10.
db.version(14).stores({
companionGoals: 'id, moduleId, status, [moduleId+status]',
_memory: 'id, category, confidence, lastConfirmed, [category+confidence]',
_nudgeOutcomes: '++id, nudgeId, nudgeType, outcome, timestamp, [nudgeType+outcome]',
companionConversations: 'id, createdAt',
companionMessages: 'id, conversationId, role, createdAt, [conversationId+createdAt]',
// Rituals
rituals: 'id, status, createdAt',
ritualSteps: 'id, ritualId, order, [ritualId+order]',
ritualLogs: '++id, ritualId, date, [ritualId+date]',

View file

@ -3,23 +3,31 @@
*/
import { useLiveQueryWithDefault } from '@mana/local-store/svelte';
import { conversationTable, messageTable } from './collections';
import { db } from '$lib/data/database';
import type { LocalConversation, LocalMessage } from './types';
export function useConversations() {
return useLiveQueryWithDefault<LocalConversation[]>(async () => {
const all = await conversationTable.toArray();
return all.filter((c) => !c.deletedAt).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
try {
const all = await db.table<LocalConversation>('companionConversations').toArray();
return all.filter((c) => !c.deletedAt).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
} catch {
return [];
}
}, []);
}
export function useMessages(conversationId: string) {
return useLiveQueryWithDefault<LocalMessage[]>(async () => {
if (!conversationId) return [];
const msgs = await messageTable
.where('conversationId')
.equals(conversationId)
.sortBy('createdAt');
return msgs;
try {
return await db
.table<LocalMessage>('companionMessages')
.where('conversationId')
.equals(conversationId)
.sortBy('createdAt');
} catch {
return [];
}
}, []);
}

View file

@ -6,9 +6,12 @@
* Can be upgraded to the LLM orchestrator for multi-tier support.
*/
import { conversationTable, messageTable } from '../collections';
import { db } from '$lib/data/database';
import type { LocalConversation, LocalMessage } from '../types';
const CONV_TABLE = 'companionConversations';
const MSG_TABLE = 'companionMessages';
// ── Conversation CRUD ───────────────────────────────
export const chatStore = {
@ -20,19 +23,19 @@ export const chatStore = {
createdAt: now,
updatedAt: now,
};
await conversationTable.add(conv);
await db.table<LocalConversation>(CONV_TABLE).add(conv);
return conv;
},
async renameConversation(id: string, title: string): Promise<void> {
await conversationTable.update(id, {
await db.table<LocalConversation>(CONV_TABLE).update(id, {
title,
updatedAt: new Date().toISOString(),
});
},
async deleteConversation(id: string): Promise<void> {
await conversationTable.update(id, {
await db.table<LocalConversation>(CONV_TABLE).update(id, {
deletedAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
@ -58,10 +61,10 @@ export const chatStore = {
toolResult: extra?.toolResult,
createdAt: new Date().toISOString(),
};
await messageTable.add(msg);
await db.table<LocalMessage>(MSG_TABLE).add(msg);
// Touch conversation updatedAt
await conversationTable.update(conversationId, {
await db.table<LocalConversation>(CONV_TABLE).update(conversationId, {
updatedAt: msg.createdAt,
});
@ -69,10 +72,14 @@ export const chatStore = {
},
async updateMessageContent(id: string, content: string): Promise<void> {
await messageTable.update(id, { content });
await db.table<LocalMessage>(MSG_TABLE).update(id, { content });
},
async getMessages(conversationId: string): Promise<LocalMessage[]> {
return messageTable.where('conversationId').equals(conversationId).sortBy('createdAt');
return db
.table<LocalMessage>(MSG_TABLE)
.where('conversationId')
.equals(conversationId)
.sortBy('createdAt');
},
};

View file

@ -113,11 +113,12 @@
<style>
.companion-page {
display: flex;
height: calc(100dvh - var(--bottom-chrome-height, 80px) - 4rem);
gap: 1px;
background: hsl(var(--color-border));
min-height: 400px;
height: calc(100dvh - var(--bottom-chrome-height, 80px) - 6rem);
border: 1px solid hsl(var(--color-border));
border-radius: 1rem;
overflow: hidden;
background: hsl(var(--color-background));
}
.sidebar {
@ -243,6 +244,7 @@
.empty-state {
flex: 1;
min-height: 300px;
display: flex;
flex-direction: column;
align-items: center;