diff --git a/apps/manacore/apps/web/src/lib/components/dashboard/widgets/ZitareQuoteWidget.svelte b/apps/manacore/apps/web/src/lib/components/dashboard/widgets/ZitareQuoteWidget.svelte
index f223f0ba5..0477f9abd 100644
--- a/apps/manacore/apps/web/src/lib/components/dashboard/widgets/ZitareQuoteWidget.svelte
+++ b/apps/manacore/apps/web/src/lib/components/dashboard/widgets/ZitareQuoteWidget.svelte
@@ -1,117 +1,51 @@
-
diff --git a/apps/manacore/apps/web/src/lib/data/cross-app-queries.ts b/apps/manacore/apps/web/src/lib/data/cross-app-queries.ts
index 3205c919f..7d6f066e0 100644
--- a/apps/manacore/apps/web/src/lib/data/cross-app-queries.ts
+++ b/apps/manacore/apps/web/src/lib/data/cross-app-queries.ts
@@ -11,9 +11,35 @@ import {
crossTaskCollection,
crossEventCollection,
crossContactCollection,
+ crossConversationCollection,
+ crossFavoriteCollection,
+ crossImageCollection,
+ crossAlarmCollection,
+ crossTimerCollection,
+ crossFileCollection,
+ crossSongCollection,
+ crossPlaylistCollection,
+ crossPresiDeckCollection,
+ crossSpaceCollection,
+ crossDocumentCollection,
+ crossManadeckDeckCollection,
+ crossManadeckCardCollection,
type CrossAppTask,
type CrossAppEvent,
type CrossAppContact,
+ type CrossAppConversation,
+ type CrossAppFavorite,
+ type CrossAppImage,
+ type CrossAppAlarm,
+ type CrossAppTimer,
+ type CrossAppFile,
+ type CrossAppSong,
+ type CrossAppPlaylist,
+ type CrossAppDeck,
+ type CrossAppSpace,
+ type CrossAppDocument,
+ type CrossAppManadeckDeck,
+ type CrossAppManadeckCard,
} from './cross-app-stores';
// βββ Todo Queries βββββββββββββββββββββββββββββββββββββββββββ
@@ -112,3 +138,170 @@ export function useFavoriteContacts(limit = 5) {
return all.filter((c) => c.isFavorite && !c.isArchived && !c.deletedAt).slice(0, limit);
}, [] as CrossAppContact[]);
}
+
+// βββ Chat Queries βββββββββββββββββββββββββββββββββββββββββββ
+
+/** Recent conversations, sorted by updatedAt desc. */
+export function useRecentConversations(limit = 5) {
+ return useLiveQueryWithDefault(async () => {
+ const all = await crossConversationCollection.getAll(undefined, {
+ sortBy: 'updatedAt',
+ sortDirection: 'desc',
+ });
+ return all.filter((c) => !c.isArchived && !c.deletedAt).slice(0, limit);
+ }, [] as CrossAppConversation[]);
+}
+
+// βββ Zitare Queries βββββββββββββββββββββββββββββββββββββββββ
+
+/** A random favorite quote. */
+export function useRandomFavorite() {
+ return useLiveQueryWithDefault(
+ async () => {
+ const all = await crossFavoriteCollection.getAll();
+ const active = all.filter((f) => !f.deletedAt);
+ if (active.length === 0) return null;
+ return active[Math.floor(Math.random() * active.length)];
+ },
+ null as CrossAppFavorite | null
+ );
+}
+
+// βββ Picture Queries ββββββββββββββββββββββββββββββββββββββββ
+
+/** Recent generated images. */
+export function useRecentImages(limit = 6) {
+ return useLiveQueryWithDefault(async () => {
+ const all = await crossImageCollection.getAll(undefined, {
+ sortBy: 'createdAt',
+ sortDirection: 'desc',
+ });
+ return all.filter((i) => !i.archivedAt && !i.deletedAt).slice(0, limit);
+ }, [] as CrossAppImage[]);
+}
+
+// βββ Clock Queries ββββββββββββββββββββββββββββββββββββββββββ
+
+/** Enabled alarms. */
+export function useEnabledAlarms() {
+ return useLiveQueryWithDefault(async () => {
+ const all = await crossAlarmCollection.getAll();
+ return all.filter((a) => a.enabled && !a.deletedAt);
+ }, [] as CrossAppAlarm[]);
+}
+
+/** Active/running timers. */
+export function useActiveTimers() {
+ return useLiveQueryWithDefault(async () => {
+ const all = await crossTimerCollection.getAll();
+ return all.filter((t) => (t.status === 'running' || t.status === 'paused') && !t.deletedAt);
+ }, [] as CrossAppTimer[]);
+}
+
+// βββ Storage Queries ββββββββββββββββββββββββββββββββββββββββ
+
+/** Storage stats: total files and total size. */
+export function useStorageStats() {
+ return useLiveQueryWithDefault(
+ async () => {
+ const files = await crossFileCollection.getAll();
+ const active = files.filter((f) => !f.isDeleted && !f.deletedAt);
+ const totalSize = active.reduce((sum, f) => sum + (f.size || 0), 0);
+ const recent = active
+ .sort((a, b) => (b.updatedAt ?? '').localeCompare(a.updatedAt ?? ''))
+ .slice(0, 5);
+ return { totalFiles: active.length, totalSize, recentFiles: recent };
+ },
+ { totalFiles: 0, totalSize: 0, recentFiles: [] as CrossAppFile[] }
+ );
+}
+
+// βββ Mukke Queries ββββββββββββββββββββββββββββββββββββββββββ
+
+/** Mukke library stats + recent songs. */
+export function useMukkeStats() {
+ return useLiveQueryWithDefault(
+ async () => {
+ const songs = await crossSongCollection.getAll();
+ const playlists = await crossPlaylistCollection.getAll();
+ const activeSongs = songs.filter((s) => !s.deletedAt);
+ const activePlaylists = playlists.filter((p) => !p.deletedAt);
+ const recent = activeSongs
+ .sort((a, b) => (b.updatedAt ?? '').localeCompare(a.updatedAt ?? ''))
+ .slice(0, 5);
+ return {
+ totalSongs: activeSongs.length,
+ totalPlaylists: activePlaylists.length,
+ favoriteCount: activeSongs.filter((s) => s.favorite).length,
+ recentSongs: recent,
+ };
+ },
+ { totalSongs: 0, totalPlaylists: 0, favoriteCount: 0, recentSongs: [] as CrossAppSong[] }
+ );
+}
+
+// βββ Presi Queries ββββββββββββββββββββββββββββββββββββββββββ
+
+/** Recent presentation decks. */
+export function useRecentDecks(limit = 5) {
+ return useLiveQueryWithDefault(async () => {
+ const all = await crossPresiDeckCollection.getAll(undefined, {
+ sortBy: 'updatedAt',
+ sortDirection: 'desc',
+ });
+ return all.filter((d) => !d.deletedAt).slice(0, limit);
+ }, [] as CrossAppDeck[]);
+}
+
+// βββ Context Queries ββββββββββββββββββββββββββββββββββββββββ
+
+/** Recent documents + spaces. */
+export function useRecentDocuments(limit = 5) {
+ return useLiveQueryWithDefault(async () => {
+ const all = await crossDocumentCollection.getAll(undefined, {
+ sortBy: 'updatedAt',
+ sortDirection: 'desc',
+ });
+ return all.filter((d) => !d.deletedAt).slice(0, limit);
+ }, [] as CrossAppDocument[]);
+}
+
+export function useSpaces() {
+ return useLiveQueryWithDefault(async () => {
+ const all = await crossSpaceCollection.getAll(undefined, {
+ sortBy: 'pinned',
+ sortDirection: 'desc',
+ });
+ return all.filter((s) => !s.deletedAt);
+ }, [] as CrossAppSpace[]);
+}
+
+// βββ ManaDeck Queries βββββββββββββββββββββββββββββββββββββββ
+
+/** ManaDeck learning progress. */
+export function useManadeckProgress() {
+ return useLiveQueryWithDefault(
+ async () => {
+ const decks = await crossManadeckDeckCollection.getAll();
+ const cards = await crossManadeckCardCollection.getAll();
+ const activeDecks = decks.filter((d) => !d.deletedAt);
+ const activeCards = cards.filter((c) => !c.deletedAt);
+ const now = new Date().toISOString();
+ const dueCards = activeCards.filter((c) => c.nextReview && c.nextReview <= now);
+ return {
+ totalDecks: activeDecks.length,
+ totalCards: activeCards.length,
+ cardsLearned: activeCards.filter((c) => (c.reviewCount ?? 0) > 0).length,
+ dueForReview: dueCards.length,
+ decks: activeDecks,
+ };
+ },
+ {
+ totalDecks: 0,
+ totalCards: 0,
+ cardsLearned: 0,
+ dueForReview: 0,
+ decks: [] as CrossAppManadeckDeck[],
+ }
+ );
+}
diff --git a/apps/manacore/apps/web/src/lib/data/cross-app-stores.ts b/apps/manacore/apps/web/src/lib/data/cross-app-stores.ts
index e9ad9fc74..eade3c346 100644
--- a/apps/manacore/apps/web/src/lib/data/cross-app-stores.ts
+++ b/apps/manacore/apps/web/src/lib/data/cross-app-stores.ts
@@ -76,6 +76,144 @@ export interface CrossAppContact extends BaseRecord {
isArchived?: boolean;
}
+// βββ Chat Types βββββββββββββββββββββββββββββββββββββββββββββ
+
+export interface CrossAppConversation extends BaseRecord {
+ title?: string;
+ modelId?: string;
+ isArchived?: boolean;
+ isPinned?: boolean;
+ spaceId?: string;
+}
+
+export interface CrossAppMessage extends BaseRecord {
+ conversationId: string;
+ sender: 'user' | 'assistant' | 'system';
+ messageText: string;
+}
+
+// βββ Zitare Types βββββββββββββββββββββββββββββββββββββββββββ
+
+export interface CrossAppFavorite extends BaseRecord {
+ quoteId: string;
+}
+
+// βββ Picture Types ββββββββββββββββββββββββββββββββββββββββββ
+
+export interface CrossAppImage extends BaseRecord {
+ prompt?: string;
+ publicUrl?: string;
+ storagePath?: string;
+ filename?: string;
+ width?: number;
+ height?: number;
+ isFavorite?: boolean;
+ isPublic?: boolean;
+ archivedAt?: string | null;
+}
+
+// βββ Clock Types ββββββββββββββββββββββββββββββββββββββββββββ
+
+export interface CrossAppAlarm extends BaseRecord {
+ label?: string;
+ time: string;
+ enabled: boolean;
+ repeatDays?: number[];
+}
+
+export interface CrossAppTimer extends BaseRecord {
+ label?: string;
+ durationSeconds: number;
+ remainingSeconds: number;
+ status: 'idle' | 'running' | 'paused' | 'finished';
+ startedAt?: string;
+}
+
+// βββ Storage Types ββββββββββββββββββββββββββββββββββββββββββ
+
+export interface CrossAppFile extends BaseRecord {
+ name: string;
+ originalName?: string;
+ mimeType?: string;
+ size?: number;
+ parentFolderId?: string | null;
+ isFavorite?: boolean;
+ isDeleted?: boolean;
+}
+
+export interface CrossAppFolder extends BaseRecord {
+ name: string;
+ parentFolderId?: string | null;
+ path?: string;
+ depth?: number;
+ isFavorite?: boolean;
+ isDeleted?: boolean;
+}
+
+// βββ Mukke Types ββββββββββββββββββββββββββββββββββββββββββββ
+
+export interface CrossAppSong extends BaseRecord {
+ title: string;
+ artist?: string;
+ album?: string;
+ duration?: number;
+ favorite?: boolean;
+}
+
+export interface CrossAppPlaylist extends BaseRecord {
+ name: string;
+ description?: string;
+}
+
+// βββ Presi Types ββββββββββββββββββββββββββββββββββββββββββββ
+
+export interface CrossAppDeck extends BaseRecord {
+ title: string;
+ description?: string;
+ isPublic?: boolean;
+}
+
+export interface CrossAppSlide extends BaseRecord {
+ deckId: string;
+ order: number;
+ content?: unknown;
+}
+
+// βββ Context Types ββββββββββββββββββββββββββββββββββββββββββ
+
+export interface CrossAppSpace extends BaseRecord {
+ name: string;
+ description?: string;
+ pinned?: boolean;
+}
+
+export interface CrossAppDocument extends BaseRecord {
+ spaceId: string;
+ title: string;
+ type?: 'text' | 'context' | 'prompt';
+ pinned?: boolean;
+}
+
+// βββ ManaDeck Types βββββββββββββββββββββββββββββββββββββββββ
+
+export interface CrossAppManadeckDeck extends BaseRecord {
+ name: string;
+ description?: string;
+ color?: string;
+ cardCount?: number;
+ lastStudied?: string;
+ isPublic?: boolean;
+}
+
+export interface CrossAppManadeckCard extends BaseRecord {
+ deckId: string;
+ front: string;
+ back: string;
+ difficulty?: number;
+ nextReview?: string;
+ reviewCount?: number;
+}
+
// βββ Store Instances ββββββββββββββββββββββββββββββββββββββββ
// These open existing IndexedDB databases created by other apps.
// No sync config β ManaCore only reads, the owning app handles sync.
@@ -126,9 +264,119 @@ export const contactsReader = createLocalStore({
],
});
-// Typed collection accessors
+export const chatReader = createLocalStore({
+ appId: 'chat',
+ collections: [
+ { name: 'conversations', indexes: ['isArchived', 'isPinned', 'spaceId'] },
+ { name: 'messages', indexes: ['conversationId', 'sender', '[conversationId+sender]'] },
+ ],
+});
+
+export const zitareReader = createLocalStore({
+ appId: 'zitare',
+ collections: [{ name: 'favorites', indexes: ['quoteId'] }],
+});
+
+export const pictureReader = createLocalStore({
+ appId: 'picture',
+ collections: [{ name: 'images', indexes: ['isFavorite', 'isPublic', 'archivedAt', 'prompt'] }],
+});
+
+export const clockReader = createLocalStore({
+ appId: 'clock',
+ collections: [
+ { name: 'alarms', indexes: ['enabled', 'time'] },
+ { name: 'timers', indexes: ['status'] },
+ ],
+});
+
+export const storageReader = createLocalStore({
+ appId: 'storage',
+ collections: [
+ {
+ name: 'files',
+ indexes: ['parentFolderId', 'mimeType', 'isFavorite', 'isDeleted', 'name'],
+ },
+ { name: 'folders', indexes: ['parentFolderId', 'path', 'depth', 'isFavorite', 'isDeleted'] },
+ ],
+});
+
+export const mukkeReader = createLocalStore({
+ appId: 'mukke',
+ collections: [
+ { name: 'songs', indexes: ['artist', 'album', 'genre', 'favorite', 'title'] },
+ { name: 'playlists', indexes: ['name'] },
+ ],
+});
+
+export const presiReader = createLocalStore({
+ appId: 'presi',
+ collections: [
+ { name: 'decks', indexes: ['isPublic'] },
+ { name: 'slides', indexes: ['deckId', 'order', '[deckId+order]'] },
+ ],
+});
+
+export const contextReader = createLocalStore({
+ appId: 'context',
+ collections: [
+ { name: 'spaces', indexes: ['pinned', 'prefix'] },
+ { name: 'documents', indexes: ['spaceId', 'type', 'pinned', 'title', '[spaceId+type]'] },
+ ],
+});
+
+export const manadeckReader = createLocalStore({
+ appId: 'manadeck',
+ collections: [
+ { name: 'decks', indexes: ['isPublic'] },
+ { name: 'cards', indexes: ['deckId', 'difficulty', 'nextReview', 'order', '[deckId+order]'] },
+ ],
+});
+
+// βββ Typed Collection Accessors βββββββββββββββββββββββββββββ
+
+// Todo
export const crossTaskCollection = todoReader.collection
('tasks');
export const crossProjectCollection = todoReader.collection('projects');
+
+// Calendar
export const crossEventCollection = calendarReader.collection('events');
export const crossCalendarCollection = calendarReader.collection('calendars');
+
+// Contacts
export const crossContactCollection = contactsReader.collection('contacts');
+
+// Chat
+export const crossConversationCollection =
+ chatReader.collection('conversations');
+export const crossMessageCollection = chatReader.collection('messages');
+
+// Zitare
+export const crossFavoriteCollection = zitareReader.collection('favorites');
+
+// Picture
+export const crossImageCollection = pictureReader.collection('images');
+
+// Clock
+export const crossAlarmCollection = clockReader.collection('alarms');
+export const crossTimerCollection = clockReader.collection('timers');
+
+// Storage
+export const crossFileCollection = storageReader.collection('files');
+export const crossFolderCollection = storageReader.collection('folders');
+
+// Mukke
+export const crossSongCollection = mukkeReader.collection('songs');
+export const crossPlaylistCollection = mukkeReader.collection('playlists');
+
+// Presi
+export const crossPresiDeckCollection = presiReader.collection('decks');
+export const crossSlideCollection = presiReader.collection('slides');
+
+// Context
+export const crossSpaceCollection = contextReader.collection('spaces');
+export const crossDocumentCollection = contextReader.collection('documents');
+
+// ManaDeck
+export const crossManadeckDeckCollection = manadeckReader.collection('decks');
+export const crossManadeckCardCollection = manadeckReader.collection('cards');
diff --git a/apps/manacore/apps/web/src/routes/(app)/+layout.svelte b/apps/manacore/apps/web/src/routes/(app)/+layout.svelte
index 21a205ae5..678b564d8 100644
--- a/apps/manacore/apps/web/src/routes/(app)/+layout.svelte
+++ b/apps/manacore/apps/web/src/routes/(app)/+layout.svelte
@@ -10,7 +10,20 @@
import type { PillNavItem, PillDropdownItem } from '@manacore/shared-ui';
import { tagLocalStore, tagMutations, useAllTags } from '$lib/stores/tags.svelte';
import { manacoreStore } from '$lib/data/local-store';
- import { todoReader, calendarReader, contactsReader } from '$lib/data/cross-app-stores';
+ import {
+ todoReader,
+ calendarReader,
+ contactsReader,
+ chatReader,
+ zitareReader,
+ pictureReader,
+ clockReader,
+ storageReader,
+ mukkeReader,
+ presiReader,
+ contextReader,
+ manadeckReader,
+ } from '$lib/data/cross-app-stores';
import { dashboardStore } from '$lib/stores/dashboard.svelte';
import {
THEME_DEFINITIONS,
@@ -211,6 +224,15 @@
todoReader.initialize(),
calendarReader.initialize(),
contactsReader.initialize(),
+ chatReader.initialize(),
+ zitareReader.initialize(),
+ pictureReader.initialize(),
+ clockReader.initialize(),
+ storageReader.initialize(),
+ mukkeReader.initialize(),
+ presiReader.initialize(),
+ contextReader.initialize(),
+ manadeckReader.initialize(),
]);
// Start syncing to server