fix(ci): build shared packages before tests and fix formatting

- Add build:packages step to all test.yml jobs (fixes @manacore/shared-nestjs-auth not found)
- Handle missing coverage artifacts gracefully in test-coverage.yml
- Update .prettierignore to exclude apps-archived/ and problematic files
- Format all source files to pass CI checks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Wuesteon 2025-12-01 23:15:00 +01:00
parent 5282f5545b
commit 0ebfde0851
163 changed files with 15247 additions and 14677 deletions

View file

@ -114,11 +114,11 @@ EXPO_PUBLIC_BACKEND_URL=http://localhost:3001
## AI Models Available
| Model ID | Name | Description | Default |
| ------------------------------------ | ----------------- | ------------------------------ | ------- |
| 550e8400-e29b-41d4-a716-446655440101 | Gemini 2.5 Flash | Fast, efficient responses | Yes |
| 550e8400-e29b-41d4-a716-446655440102 | Gemini 2.0 Flash-Lite | Ultra-lightweight model | No |
| 550e8400-e29b-41d4-a716-446655440103 | Gemini 2.5 Pro | Most capable model | No |
| Model ID | Name | Description | Default |
| ------------------------------------ | --------------------- | ------------------------- | ------- |
| 550e8400-e29b-41d4-a716-446655440101 | Gemini 2.5 Flash | Fast, efficient responses | Yes |
| 550e8400-e29b-41d4-a716-446655440102 | Gemini 2.0 Flash-Lite | Ultra-lightweight model | No |
| 550e8400-e29b-41d4-a716-446655440103 | Gemini 2.5 Pro | Most capable model | No |
## Important Notes

View file

@ -134,8 +134,7 @@ export default function SettingsScreen() {
style={[
styles.themeOption,
{
borderColor:
selectedTheme === theme.id ? colors.primary : colors.border,
borderColor: selectedTheme === theme.id ? colors.primary : colors.border,
backgroundColor:
selectedTheme === theme.id ? colors.primary + '10' : 'transparent',
},
@ -272,9 +271,7 @@ export default function SettingsScreen() {
style={styles.settingIcon}
/>
<View>
<Text style={[styles.settingLabel, { color: colors.text }]}>
Passwort ändern
</Text>
<Text style={[styles.settingLabel, { color: colors.text }]}>Passwort ändern</Text>
<Text style={[styles.settingDescription, { color: colors.text + '70' }]}>
Aktualisiere dein Passwort regelmäßig
</Text>
@ -285,12 +282,7 @@ export default function SettingsScreen() {
<TouchableOpacity style={styles.actionRowLast} onPress={handleDeleteChatHistory}>
<View style={styles.settingInfo}>
<Ionicons
name="trash-outline"
size={22}
color="#FF3B30"
style={styles.settingIcon}
/>
<Ionicons name="trash-outline" size={22} color="#FF3B30" style={styles.settingIcon} />
<View>
<Text style={[styles.settingLabel, { color: '#FF3B30' }]}>
Chat-Verlauf löschen

View file

@ -28,7 +28,9 @@
{#each toasts as toast (toast.id)}
{@const Icon = icons[toast.type]}
<div
class="flex items-start gap-3 px-4 py-3 rounded-xl shadow-lg backdrop-blur-xl border border-white/20 animate-slide-in {colors[toast.type]}"
class="flex items-start gap-3 px-4 py-3 rounded-xl shadow-lg backdrop-blur-xl border border-white/20 animate-slide-in {colors[
toast.type
]}"
role="alert"
>
<Icon size={20} weight="fill" class="flex-shrink-0 mt-0.5" />

View file

@ -4,7 +4,15 @@
import { conversationsStore } from '$lib/stores/conversations.svelte';
import { authStore } from '$lib/stores/auth.svelte';
import { isSidebarMode, isNavCollapsed } from '$lib/stores/navigation';
import { MagnifyingGlass, X, Plus, ChatCircle, Archive, Trash, PushPin } from '@manacore/shared-icons';
import {
MagnifyingGlass,
X,
Plus,
ChatCircle,
Archive,
Trash,
PushPin,
} from '@manacore/shared-icons';
import { ConfirmationModal } from '@manacore/shared-ui';
import { goto } from '$app/navigation';
import type { Snippet } from 'svelte';
@ -72,7 +80,7 @@
yesterday: 'Gestern',
thisWeek: 'Diese Woche',
thisMonth: 'Dieser Monat',
older: 'Älter'
older: 'Älter',
};
function getDateSection(dateString: string): DateSection {
@ -117,7 +125,7 @@
yesterday: [],
thisWeek: [],
thisMonth: [],
older: []
older: [],
};
for (const conv of unpinnedConversations) {
@ -331,7 +339,9 @@
<!-- Pinned Section -->
{#if pinnedConversations.length > 0}
<div class="mb-5">
<h4 class="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2 flex items-center gap-1.5">
<h4
class="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2 flex items-center gap-1.5"
>
<PushPin size={12} weight="fill" class="text-primary" />
Angepinnt
</h4>
@ -339,17 +349,11 @@
<a
href="/chat/{conv.id}"
class="group block w-full rounded-xl bg-white/60 dark:bg-white/5 backdrop-blur-sm border border-black/10 dark:border-white/20 p-4 text-left transition-all mb-3 hover:shadow-md hover:bg-white/80 dark:hover:bg-white/10
{isActive(conv.id)
? 'bg-white/90 dark:bg-white/15 shadow-md border-primary/30'
: ''}"
{isActive(conv.id) ? 'bg-white/90 dark:bg-white/15 shadow-md border-primary/30' : ''}"
>
<!-- Title Row -->
<div class="mb-1.5 flex items-center gap-2">
<PushPin
size={16}
weight="fill"
class="flex-shrink-0 text-primary"
/>
<PushPin size={16} weight="fill" class="flex-shrink-0 text-primary" />
<h3 class="text-sm font-semibold line-clamp-1 text-foreground flex-1">
{conv.title || 'Neue Konversation'}
</h3>
@ -367,14 +371,14 @@
</span>
<div class="flex items-center gap-1">
{#if conv.documentMode}
<span
class="text-xs text-primary bg-primary/10 px-2 py-0.5 rounded-full"
>
<span class="text-xs text-primary bg-primary/10 px-2 py-0.5 rounded-full">
Dokument
</span>
{/if}
<!-- Action Buttons (visible on hover) -->
<div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
<div
class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity"
>
<button
onclick={(e) => handleTogglePin(e, conv.id, true)}
class="p-1.5 text-primary hover:text-primary hover:bg-primary/10 rounded-lg transition-colors"
@ -409,16 +413,16 @@
{@const convs = groupedConversations()[section]}
{#if convs.length > 0}
<div class="mb-5">
<h4 class="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2">
<h4
class="text-xs font-semibold text-muted-foreground uppercase tracking-wider mb-2"
>
{sectionLabels[section]}
</h4>
{#each convs as conv (conv.id)}
<a
href="/chat/{conv.id}"
class="group block w-full rounded-xl bg-white/60 dark:bg-white/5 backdrop-blur-sm border border-black/10 dark:border-white/20 p-4 text-left transition-all mb-3 hover:shadow-md hover:bg-white/80 dark:hover:bg-white/10
{isActive(conv.id)
? 'bg-white/90 dark:bg-white/15 shadow-md border-primary/30'
: ''}"
{isActive(conv.id) ? 'bg-white/90 dark:bg-white/15 shadow-md border-primary/30' : ''}"
>
<!-- Title Row -->
<div class="mb-1.5 flex items-center gap-2">
@ -446,14 +450,14 @@
</span>
<div class="flex items-center gap-1">
{#if conv.documentMode}
<span
class="text-xs text-primary bg-primary/10 px-2 py-0.5 rounded-full"
>
<span class="text-xs text-primary bg-primary/10 px-2 py-0.5 rounded-full">
Dokument
</span>
{/if}
<!-- Action Buttons (visible on hover) -->
<div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
<div
class="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity"
>
<button
onclick={(e) => handleTogglePin(e, conv.id, false)}
class="p-1.5 text-muted-foreground hover:text-primary hover:bg-primary/10 rounded-lg transition-colors"

View file

@ -138,9 +138,7 @@
<a
href="/chat/{conv.id}"
class="block px-3 py-2 mx-2 rounded-lg transition-colors
{isActive
? 'bg-primary/10 text-primary'
: 'hover:bg-muted text-foreground'}"
{isActive ? 'bg-primary/10 text-primary' : 'hover:bg-muted text-foreground'}"
>
<div class="flex items-center justify-between gap-2">
<span class="text-sm font-medium truncate pr-6">

View file

@ -52,9 +52,7 @@
{space.name}
</h3>
{#if isOwner}
<span
class="px-2 py-0.5 text-xs font-medium bg-primary/10 text-primary rounded"
>
<span class="px-2 py-0.5 text-xs font-medium bg-primary/10 text-primary rounded">
Besitzer
</span>
{/if}

View file

@ -50,9 +50,7 @@
>
<!-- Name -->
<div>
<label for="name" class="block text-sm font-medium text-foreground mb-1">
Name *
</label>
<label for="name" class="block text-sm font-medium text-foreground mb-1"> Name * </label>
<input
type="text"
id="name"
@ -71,10 +69,7 @@
<!-- Description -->
<div>
<label
for="description"
class="block text-sm font-medium text-foreground mb-1"
>
<label for="description" class="block text-sm font-medium text-foreground mb-1">
Beschreibung (optional)
</label>
<textarea

View file

@ -20,9 +20,7 @@
<div
class="group relative flex rounded-xl overflow-hidden bg-surface shadow-sm hover:shadow-md transition-all
{template.isDefault
? 'ring-2 ring-primary'
: 'border border-border'}"
{template.isDefault ? 'ring-2 ring-primary' : 'border border-border'}"
>
<!-- Color Indicator -->
<div class="w-2 flex-shrink-0" style="background-color: {template.color}"></div>
@ -36,7 +34,9 @@
{template.name}
</h3>
{#if template.isDefault}
<span class="px-2 py-0.5 text-xs font-medium bg-primary text-primary-foreground rounded">
<span
class="px-2 py-0.5 text-xs font-medium bg-primary text-primary-foreground rounded"
>
Standard
</span>
{/if}

View file

@ -90,9 +90,7 @@
>
<!-- Name -->
<div>
<label for="name" class="block text-sm font-medium text-foreground mb-1">
Name *
</label>
<label for="name" class="block text-sm font-medium text-foreground mb-1"> Name * </label>
<input
type="text"
id="name"
@ -111,10 +109,7 @@
<!-- Description -->
<div>
<label
for="description"
class="block text-sm font-medium text-foreground mb-1"
>
<label for="description" class="block text-sm font-medium text-foreground mb-1">
Beschreibung (optional)
</label>
<textarea
@ -131,10 +126,7 @@
<!-- System Prompt -->
<div>
<label
for="systemPrompt"
class="block text-sm font-medium text-foreground mb-1"
>
<label for="systemPrompt" class="block text-sm font-medium text-foreground mb-1">
System-Prompt *
</label>
<textarea
@ -158,10 +150,7 @@
<!-- Initial Question -->
<div>
<label
for="initialQuestion"
class="block text-sm font-medium text-foreground mb-1"
>
<label for="initialQuestion" class="block text-sm font-medium text-foreground mb-1">
Beispielfrage (optional)
</label>
<textarea
@ -236,9 +225,7 @@
type="button"
onclick={() => (documentMode = !documentMode)}
class="w-full flex items-center justify-between p-4 border rounded-lg transition-colors
{documentMode
? 'border-primary bg-primary/10'
: 'border-border bg-muted'}"
{documentMode ? 'border-primary bg-primary/10' : 'border-border bg-muted'}"
>
<div class="text-left">
<p class="font-medium text-foreground">Dokumentmodus aktivieren</p>

View file

@ -48,7 +48,8 @@ export const conversationsStore = {
try {
conversations = await conversationService.getConversations(spaceId);
} catch (e) {
const message = e instanceof Error ? e.message : 'Konversationen konnten nicht geladen werden';
const message =
e instanceof Error ? e.message : 'Konversationen konnten nicht geladen werden';
error = message;
toastStore.error(message);
conversations = [];
@ -67,7 +68,8 @@ export const conversationsStore = {
try {
archivedConversations = await conversationService.getArchivedConversations();
} catch (e) {
const message = e instanceof Error ? e.message : 'Archivierte Konversationen konnten nicht geladen werden';
const message =
e instanceof Error ? e.message : 'Archivierte Konversationen konnten nicht geladen werden';
error = message;
toastStore.error(message);
archivedConversations = [];
@ -131,7 +133,10 @@ export const conversationsStore = {
const conversation = archivedConversations.find((c) => c.id === conversationId);
if (conversation) {
archivedConversations = archivedConversations.filter((c) => c.id !== conversationId);
conversations = sortConversations([{ ...conversation, isArchived: false }, ...conversations]);
conversations = sortConversations([
{ ...conversation, isArchived: false },
...conversations,
]);
}
toastStore.success('Konversation wiederhergestellt');
} else {

View file

@ -93,7 +93,10 @@ export const toastStore = {
* Helper function for API error handling
* Use this in services/stores to show user-friendly error messages
*/
export function handleApiError(error: unknown, fallbackMessage: string = 'Ein Fehler ist aufgetreten'): string {
export function handleApiError(
error: unknown,
fallbackMessage: string = 'Ein Fehler ist aufgetreten'
): string {
const message = error instanceof Error ? error.message : fallbackMessage;
toastStore.error(message);
return message;

View file

@ -81,9 +81,7 @@
d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"
/>
</svg>
<h3 class="text-lg font-medium text-foreground mb-1">
Keine archivierten Konversationen
</h3>
<h3 class="text-lg font-medium text-foreground mb-1">Keine archivierten Konversationen</h3>
<p class="text-muted-foreground">Archivierte Gespräche erscheinen hier.</p>
</div>
{:else}
@ -128,9 +126,7 @@
</button>
<!-- Actions -->
<div
class="flex justify-end gap-2 px-4 py-2 border-t border-border bg-muted/50"
>
<div class="flex justify-end gap-2 px-4 py-2 border-t border-border bg-muted/50">
<button
onclick={() => handleUnarchive(conv.id)}
class="flex items-center gap-1.5 px-3 py-1.5 text-sm text-muted-foreground

View file

@ -148,7 +148,9 @@
</div>
<!-- Title -->
<h3 class="text-2xl font-semibold text-foreground mb-3">Worüber möchtest du reden?</h3>
<h3 class="text-2xl font-semibold text-foreground mb-3">
Worüber möchtest du reden?
</h3>
<p class="text-muted-foreground mb-8">
Stelle eine Frage, bitte um Hilfe bei einem Projekt oder starte einfach eine
Unterhaltung.

View file

@ -10,12 +10,7 @@
import ChatInput from '$lib/components/chat/ChatInput.svelte';
import ChatLayout from '$lib/components/chat/ChatLayout.svelte';
import type { Conversation, Message, AIModel, Document } from '@chat/types';
import {
FileText,
ClockCounterClockwise,
X,
FloppyDisk,
} from '@manacore/shared-icons';
import { FileText, ClockCounterClockwise, X, FloppyDisk } from '@manacore/shared-icons';
let conversation = $state<Conversation | null>(null);
let messages = $state<Message[]>([]);
@ -247,9 +242,7 @@
<FileText size={18} weight="bold" class="text-primary" />
<span class="font-medium text-foreground">Dokument</span>
{#if document}
<span
class="text-xs text-muted-foreground bg-muted px-2 py-0.5 rounded-lg"
>
<span class="text-xs text-muted-foreground bg-muted px-2 py-0.5 rounded-lg">
v{document.version}
</span>
{/if}

View file

@ -127,9 +127,7 @@
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<h3 class="text-lg font-medium text-foreground mb-1">
Keine Dokumente gefunden
</h3>
<h3 class="text-lg font-medium text-foreground mb-1">Keine Dokumente gefunden</h3>
<p class="text-muted-foreground max-w-sm mx-auto">
Erstelle ein neues Dokument in einer Konversation mit aktiviertem Dokumentmodus.
</p>

View file

@ -4,8 +4,4 @@
import { authStore } from '$lib/stores/auth.svelte';
</script>
<FeedbackPage
{feedbackService}
appName="ManaChat"
currentUserId={authStore.user?.id}
/>
<FeedbackPage {feedbackService} appName="ManaChat" currentUserId={authStore.user?.id} />

View file

@ -31,7 +31,11 @@
}
function handleDeleteAccount() {
if (confirm('Bist du sicher, dass du dein Konto löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.')) {
if (
confirm(
'Bist du sicher, dass du dein Konto löschen möchtest? Diese Aktion kann nicht rückgängig gemacht werden.'
)
) {
alert('Konto löschen wird noch implementiert.');
}
}

View file

@ -136,9 +136,7 @@
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
/>
</svg>
<h3 class="text-lg font-medium text-foreground mb-1">
Keine Spaces gefunden
</h3>
<h3 class="text-lg font-medium text-foreground mb-1">Keine Spaces gefunden</h3>
<p class="text-muted-foreground mb-4">
Erstelle einen neuen Space oder frage nach einer Einladung
</p>

View file

@ -98,17 +98,10 @@
{:else if space}
<div class="min-h-[calc(100vh-4rem)] bg-background py-8">
<div class="max-w-4xl mx-auto px-4">
<PageHeader
title={space.name}
description={space.description}
backHref="/spaces"
size="lg"
/>
<PageHeader title={space.name} description={space.description} backHref="/spaces" size="lg" />
<!-- New Chat Section -->
<div
class="mb-8 p-4 bg-surface rounded-xl border border-border"
>
<div class="mb-8 p-4 bg-surface rounded-xl border border-border">
<h2 class="text-lg font-semibold text-foreground mb-3">Neuen Chat starten</h2>
<div class="flex items-center gap-3">
<select
@ -133,14 +126,10 @@
<!-- Conversations List -->
<div>
<h2 class="text-lg font-semibold text-foreground mb-4">
Konversationen in diesem Space
</h2>
<h2 class="text-lg font-semibold text-foreground mb-4">Konversationen in diesem Space</h2>
{#if conversations.length === 0}
<div
class="text-center py-12 bg-surface rounded-xl border border-border"
>
<div class="text-center py-12 bg-surface rounded-xl border border-border">
<svg
class="w-12 h-12 text-muted-foreground mx-auto mb-3"
fill="none"
@ -154,9 +143,7 @@
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
/>
</svg>
<p class="text-muted-foreground">
Noch keine Konversationen in diesem Space.
</p>
<p class="text-muted-foreground">Noch keine Konversationen in diesem Space.</p>
</div>
{:else}
<div class="space-y-3">

View file

@ -154,12 +154,8 @@
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
<h3 class="text-lg font-medium text-foreground mb-1">
Keine Vorlagen vorhanden
</h3>
<p class="text-muted-foreground mb-4">
Erstelle deine erste Vorlage, um loszulegen
</p>
<h3 class="text-lg font-medium text-foreground mb-1">Keine Vorlagen vorhanden</h3>
<p class="text-muted-foreground mb-4">Erstelle deine erste Vorlage, um loszulegen</p>
<button
onclick={handleCreateNew}
class="inline-flex items-center gap-2 px-4 py-2 bg-primary text-primary-foreground rounded-lg font-medium

View file

@ -83,9 +83,7 @@ export interface Template {
}
export type TemplateCreate = Omit<Template, 'id' | 'createdAt' | 'updatedAt'>;
export type TemplateUpdate = Partial<
Omit<Template, 'id' | 'userId' | 'createdAt' | 'updatedAt'>
>;
export type TemplateUpdate = Partial<Omit<Template, 'id' | 'userId' | 'createdAt' | 'updatedAt'>>;
// Space Types
export interface Space {