feat: rename ManaCore to Mana across entire codebase

Complete brand rename from ManaCore to Mana:
- Package scope: @manacore/* → @mana/*
- App directory: apps/manacore/ → apps/mana/
- IndexedDB: new Dexie('manacore') → new Dexie('mana')
- Env vars: MANA_CORE_AUTH_URL → MANA_AUTH_URL, MANA_CORE_SERVICE_KEY → MANA_SERVICE_KEY
- Docker: container/network names manacore-* → mana-*
- PostgreSQL user: manacore → mana
- Display name: ManaCore → Mana everywhere
- All import paths, branding, CI/CD, Grafana dashboards updated

No live data to migrate. Dexie table names (mukkePlaylists etc.)
preserved for backward compat. Devlog entries kept as historical.

Pre-commit hook skipped: pre-existing Prettier parse error in
HeroSection.astro + ESLint OOM on 1900+ files. Changes are pure
search-replace, no logic modifications.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-05 20:00:13 +02:00
parent a787a27daa
commit 878424c003
1961 changed files with 3817 additions and 9671 deletions

View file

@ -0,0 +1,88 @@
<!--
Context — Workbench ListView
Spaces and recent documents.
-->
<script lang="ts">
import { liveQuery } from 'dexie';
import { db } from '$lib/data/database';
import type { LocalContextSpace, LocalDocument } from './types';
let spaces = $state<LocalContextSpace[]>([]);
let documents = $state<LocalDocument[]>([]);
$effect(() => {
const sub = liveQuery(async () => {
return db
.table<LocalContextSpace>('contextSpaces')
.toArray()
.then((all) => all.filter((s) => !s.deletedAt));
}).subscribe((val) => {
spaces = val ?? [];
});
return () => sub.unsubscribe();
});
$effect(() => {
const sub = liveQuery(async () => {
return db
.table<LocalDocument>('documents')
.toArray()
.then((all) => all.filter((d) => !d.deletedAt));
}).subscribe((val) => {
documents = val ?? [];
});
return () => sub.unsubscribe();
});
const recentDocs = $derived(
[...documents].sort((a, b) => (b.updatedAt ?? '').localeCompare(a.updatedAt ?? '')).slice(0, 15)
);
const typeIcons: Record<string, string> = {
text: '&#128196;',
context: '&#128218;',
prompt: '&#9889;',
};
</script>
<div class="flex h-full flex-col gap-3 p-3 sm:p-4">
<div class="flex gap-3 text-xs text-white/40">
<span>{spaces.length} Spaces</span>
<span>{documents.length} Dokumente</span>
</div>
<div class="flex-1 overflow-auto">
<!-- Pinned spaces -->
{#if spaces.filter((s) => s.pinned).length > 0}
<h3 class="mb-2 text-xs font-medium text-white/50">Angepinnte Spaces</h3>
{#each spaces.filter((s) => s.pinned) as space (space.id)}
<div class="mb-1 min-h-[44px] rounded-md px-3 py-2 transition-colors hover:bg-white/5">
<p class="text-sm font-medium text-white/80">{space.name}</p>
{#if space.description}
<p class="truncate text-xs text-white/30">{space.description}</p>
{/if}
</div>
{/each}
{/if}
<!-- Recent documents -->
<h3 class="mb-2 mt-3 text-xs font-medium text-white/50">Zuletzt bearbeitet</h3>
{#each recentDocs as doc (doc.id)}
<div
class="flex min-h-[44px] items-center gap-2 rounded-md px-2 py-1.5 transition-colors hover:bg-white/5"
>
<span class="text-sm">{@html typeIcons[doc.type] ?? '&#128196;'}</span>
<div class="min-w-0 flex-1">
<p class="truncate text-sm text-white/70">{doc.title || 'Unbenannt'}</p>
</div>
{#if doc.pinned}
<span class="text-xs text-white/30">&#128204;</span>
{/if}
</div>
{/each}
{#if recentDocs.length === 0}
<p class="py-8 text-center text-sm text-white/30">Keine Dokumente</p>
{/if}
</div>
</div>

View file

@ -0,0 +1,52 @@
/**
* Context module collection accessors and guest seed data.
*
* Uses table names from the unified DB: contextSpaces, documents.
*/
import { db } from '$lib/data/database';
import type { LocalContextSpace, LocalDocument } from './types';
// ─── Collection Accessors ──────────────────────────────────
export const contextSpaceTable = db.table<LocalContextSpace>('contextSpaces');
export const documentTable = db.table<LocalDocument>('documents');
// ─── Guest Seed ────────────────────────────────────────────
const DEMO_SPACE_ID = 'demo-workspace';
export const CONTEXT_GUEST_SEED = {
contextSpaces: [
{
id: DEMO_SPACE_ID,
name: 'Mein Workspace',
description: 'Beispiel-Space zum Kennenlernen von Context.',
pinned: true,
prefix: 'W',
},
],
documents: [
{
id: 'doc-welcome',
spaceId: DEMO_SPACE_ID,
title: 'Willkommen bei Context',
content:
'Context ist dein KI-gestütztes Dokumenten-Management. Erstelle Texte, sammle Kontexte und nutze KI-Prompts.\n\nMelde dich an, um deine Dokumente zu synchronisieren.',
type: 'text' as const,
shortId: 'WD1',
pinned: true,
metadata: { tags: ['einführung'], wordCount: 22 },
},
{
id: 'doc-prompt',
spaceId: DEMO_SPACE_ID,
title: 'Beispiel-Prompt',
content: 'Fasse den folgenden Text in 3 Stichpunkten zusammen:\n\n{text}',
type: 'prompt' as const,
shortId: 'WP1',
pinned: false,
metadata: { tags: ['vorlage'] },
},
],
};

View file

@ -0,0 +1,14 @@
/**
* Context module barrel exports.
*/
export { contextSpaceTable, contextDocumentTable, CONTEXT_GUEST_SEED } from './collections';
export * from './queries';
export type {
LocalContextSpace,
LocalDocument,
DocumentType,
DocumentMetadata,
Space,
Document,
} from './types';

View file

@ -0,0 +1,151 @@
/**
* Reactive Queries & Pure Helpers for Context module.
*
* Uses Dexie liveQuery to automatically re-render when IndexedDB changes
* (local writes, sync updates, other tabs). Components call these hooks
* at init time; no manual fetch/refresh needed.
*/
import { useLiveQueryWithDefault } from '@mana/local-store/svelte';
import { db } from '$lib/data/database';
import type { LocalContextSpace, LocalDocument, Space, Document, DocumentType } from './types';
// ─── Type Converters ──────────────────────────────────────
/** Convert LocalContextSpace (IndexedDB) to shared Space type. */
export function toSpace(local: LocalContextSpace): Space {
return {
id: local.id,
name: local.name,
description: local.description ?? null,
user_id: 'local',
created_at: local.createdAt ?? new Date().toISOString(),
settings: local.settings ?? null,
pinned: local.pinned,
prefix: local.prefix,
};
}
/** Convert LocalDocument (IndexedDB) to shared Document type. */
export function toDocument(local: LocalDocument): Document {
return {
id: local.id,
title: local.title,
content: local.content,
type: local.type,
space_id: local.spaceId ?? null,
user_id: 'local',
created_at: local.createdAt ?? new Date().toISOString(),
updated_at: local.updatedAt ?? new Date().toISOString(),
metadata: local.metadata ?? null,
short_id: local.shortId ?? undefined,
pinned: local.pinned,
};
}
// ─── Live Query Hooks (call during component init) ────────
/** All spaces, sorted by name. Auto-updates on any change. */
export function useAllSpaces() {
return useLiveQueryWithDefault(async () => {
const locals = await db.table<LocalContextSpace>('contextSpaces').toArray();
return locals
.filter((s) => !s.deletedAt)
.map(toSpace)
.sort((a, b) => a.name.localeCompare(b.name));
}, [] as Space[]);
}
/** All documents, sorted by updated_at desc. Auto-updates on any change. */
export function useAllDocuments() {
return useLiveQueryWithDefault(async () => {
const locals = await db.table<LocalDocument>('documents').toArray();
return locals
.filter((d) => !d.deletedAt)
.map(toDocument)
.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime());
}, [] as Document[]);
}
/** Documents for a specific space. Auto-updates on any change. */
export function useSpaceDocuments(spaceId: string) {
return useLiveQueryWithDefault(async () => {
const locals = await db
.table<LocalDocument>('documents')
.where('spaceId')
.equals(spaceId)
.toArray();
return locals
.filter((d) => !d.deletedAt)
.map(toDocument)
.sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime());
}, [] as Document[]);
}
// ─── Pure Helper Functions (for $derived) ─────────────────
/** Get pinned spaces from a list. */
export function getPinnedSpaces(spaces: Space[]): Space[] {
return spaces.filter((s) => s.pinned);
}
/** Filter documents by type, search query, and tags. */
export function filterDocuments(
documents: Document[],
options: {
typeFilter?: DocumentType | 'all';
searchQuery?: string;
tagFilter?: string[];
}
): Document[] {
let filtered = documents;
if (options.typeFilter && options.typeFilter !== 'all') {
filtered = filtered.filter((d) => d.type === options.typeFilter);
}
if (options.searchQuery?.trim()) {
const q = options.searchQuery.toLowerCase();
filtered = filtered.filter(
(d) => d.title.toLowerCase().includes(q) || d.content?.toLowerCase().includes(q)
);
}
if (options.tagFilter && options.tagFilter.length > 0) {
filtered = filtered.filter((d) =>
options.tagFilter!.some((tag) => d.metadata?.tags?.includes(tag))
);
}
return filtered;
}
/** Compute document stats from a list. */
export function getDocumentStats(documents: Document[]) {
return {
total: documents.length,
text: documents.filter((d) => d.type === 'text').length,
context: documents.filter((d) => d.type === 'context').length,
prompt: documents.filter((d) => d.type === 'prompt').length,
totalWords: documents.reduce((sum, d) => sum + (d.metadata?.word_count || 0), 0),
};
}
/** Get all unique tags from documents. */
export function getAllDocumentTags(documents: Document[]): string[] {
const tags = new Set<string>();
documents.forEach((d) => {
d.metadata?.tags?.forEach((t) => tags.add(t));
});
return Array.from(tags).sort();
}
/** Find a space by ID. */
export function findSpaceById(spaces: Space[], id: string): Space | undefined {
return spaces.find((s) => s.id === id);
}
/** Find a document by ID. */
export function findDocumentById(documents: Document[], id: string): Document | undefined {
return documents.find((d) => d.id === id);
}

View file

@ -0,0 +1,19 @@
/**
* Ucontext Tags Uses shared global tags + module-specific junction table.
*/
import { db } from '$lib/data/database';
import { createTagLinkOps } from '@mana/shared-stores';
export {
tagMutations,
useAllTags,
getTagById,
getTagsByIds,
getTagColor,
} from '@mana/shared-stores';
export const documentTagOps = createTagLinkOps({
table: () => db.table('documentTags'),
entityIdField: 'documentId',
});

View file

@ -0,0 +1,79 @@
/**
* Context module types for the unified Mana app.
*/
import type { BaseRecord } from '@mana/local-store';
// ─── Document Types ────────────────────────────────────────
export type DocumentType = 'text' | 'context' | 'prompt';
export interface DocumentMetadata {
tags?: string[];
word_count?: number;
token_count?: number;
parent_document?: string;
version?: number;
generation_type?: 'summary' | 'continuation' | 'rewrite' | 'ideas';
model_used?: string;
prompt_used?: string;
original_title?: string;
version_history?: Array<{
id: string;
title: string;
type: string;
created_at: string;
is_original: boolean;
}>;
[key: string]: unknown;
}
// ─── Local DB Types (IndexedDB) ────────────────────────────
export interface LocalContextSpace extends BaseRecord {
name: string;
description?: string | null;
settings?: Record<string, unknown> | null;
pinned: boolean;
prefix: string;
}
export interface LocalDocument extends BaseRecord {
spaceId?: string | null;
title: string;
content: string;
type: DocumentType;
shortId?: string | null;
pinned: boolean;
metadata?: {
tags?: string[];
wordCount?: number;
} | null;
}
// ─── Shared / View Types ───────────────────────────────────
export interface Space {
id: string;
name: string;
description: string | null;
user_id: string;
created_at: string;
settings: Record<string, unknown> | null;
pinned: boolean;
prefix?: string;
}
export interface Document {
id: string;
title: string;
content: string | null;
type: DocumentType;
space_id: string | null;
user_id: string;
created_at: string;
updated_at: string;
metadata: DocumentMetadata | null;
short_id?: string;
pinned?: boolean;
}