feat(chat): add auto title generation, inline renaming, and styled delete modal

- Fix missing conversationsStore import for auto title generation
- Make model ID dynamic in generateTitle() with error handling and fallback
- Add inline editing for manual conversation renaming in sidebar
- Add updateConversationTitle API endpoint and store method
- Replace browser confirm() with styled ConfirmationModal for delete
- Update Modal and ConfirmationModal with glassmorphism styling
- Add DEV_BYPASS_AUTH and GOOGLE_GENAI_API_KEY to env generation

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Till-JS 2025-11-29 22:43:41 +01:00
parent a32c4f0a16
commit 05fe8ca5b6
14 changed files with 447 additions and 71 deletions

View file

@ -31,9 +31,9 @@
* ```
*/
import { Warning, WarningCircle, Info } from '@manacore/shared-icons';
import { Warning, WarningCircle, Info, X, Trash, Check } from '@manacore/shared-icons';
import Modal from './Modal.svelte';
import { Text, Button } from '../atoms';
import { Text } from '../atoms';
type ConfirmationVariant = 'danger' | 'warning' | 'info';
@ -72,19 +72,25 @@
const variantConfig: Record<
ConfirmationVariant,
{ iconColor: string; buttonVariant: 'danger' | 'primary' }
{ iconColor: string; iconBg: string; buttonColor: string; buttonHover: string }
> = {
danger: {
iconColor: 'text-red-500',
buttonVariant: 'danger',
iconBg: 'bg-red-500/10',
buttonColor: 'bg-red-500 text-white',
buttonHover: 'hover:bg-red-600 hover:shadow-lg hover:shadow-red-500/25',
},
warning: {
iconColor: 'text-yellow-500',
buttonVariant: 'primary',
iconBg: 'bg-yellow-500/10',
buttonColor: 'bg-yellow-500 text-white',
buttonHover: 'hover:bg-yellow-600 hover:shadow-lg hover:shadow-yellow-500/25',
},
info: {
iconColor: 'text-blue-500',
buttonVariant: 'primary',
iconBg: 'bg-blue-500/10',
buttonColor: 'bg-blue-500 text-white',
buttonHover: 'hover:bg-blue-600 hover:shadow-lg hover:shadow-blue-500/25',
},
};
@ -97,7 +103,7 @@
<Modal {visible} {onClose} {title} maxWidth="sm">
{#snippet icon()}
<div class="p-2 rounded-full bg-menu-hover {config.iconColor}">
<div class="p-2.5 rounded-xl {config.iconBg} {config.iconColor}">
{#if variant === 'danger'}
<Warning size={20} weight="bold" />
{:else if variant === 'warning'}
@ -117,13 +123,46 @@
</div>
{#snippet footer()}
<div class="flex gap-3 justify-end">
<Button variant="ghost" onclick={onClose} disabled={loading}>
{cancelLabel}
</Button>
<Button variant={config.buttonVariant} onclick={handleConfirm} {loading}>
<div class="flex flex-col gap-3">
<!-- Confirm Button -->
<button
onclick={handleConfirm}
disabled={loading}
class="w-full flex items-center justify-center gap-2 px-4 py-3 rounded-xl font-semibold text-sm
{config.buttonColor} {config.buttonHover}
transition-all duration-200 hover:-translate-y-0.5 active:translate-y-0
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:translate-y-0 disabled:hover:shadow-none"
>
{#if loading}
<svg class="h-4 w-4 animate-spin" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
{:else if variant === 'danger'}
<Trash size={18} weight="bold" />
{:else}
<Check size={18} weight="bold" />
{/if}
{confirmLabel}
</Button>
</button>
<!-- Cancel Button -->
<button
onclick={onClose}
disabled={loading}
class="w-full flex items-center justify-center gap-2 px-4 py-3 rounded-xl font-semibold text-sm
bg-black/5 dark:bg-white/10 text-foreground
hover:bg-black/10 dark:hover:bg-white/20 hover:shadow-md
transition-all duration-200 hover:-translate-y-0.5 active:translate-y-0
disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:translate-y-0"
>
<X size={18} weight="bold" />
{cancelLabel}
</button>
</div>
{/snippet}
</Modal>

View file

@ -65,14 +65,14 @@
<div
class="relative flex max-h-[90vh] w-full {maxWidthClasses[
maxWidth
]} flex-col rounded-xl border border-theme bg-menu shadow-xl"
]} flex-col rounded-2xl border border-black/10 dark:border-white/20 bg-white/80 dark:bg-white/10 backdrop-blur-xl shadow-2xl"
onclick={(e) => e.stopPropagation()}
onkeydown={(e) => e.stopPropagation()}
>
{#if showHeader}
<!-- Header -->
<div class="flex items-center justify-between p-6 border-b border-theme">
<div class="flex items-center gap-2 flex-1">
<div class="flex items-center justify-between p-6 border-b border-black/10 dark:border-white/10">
<div class="flex items-center gap-3 flex-1">
{#if icon}
{@render icon()}
{/if}
@ -84,10 +84,10 @@
</div>
<button
onclick={onClose}
class="p-2 rounded-full hover:bg-menu-hover transition-colors"
class="p-2 rounded-xl bg-black/5 dark:bg-white/10 hover:bg-black/10 dark:hover:bg-white/20 transition-all duration-200 hover:scale-105"
aria-label="Close"
>
<X size={20} weight="bold" class="text-theme-muted" />
<X size={18} weight="bold" class="text-muted-foreground" />
</button>
</div>
{/if}
@ -99,7 +99,7 @@
<!-- Footer (optional) -->
{#if footer}
<div class="border-t border-theme p-6">
<div class="border-t border-black/10 dark:border-white/10 p-6">
{@render footer()}
</div>
{/if}