diff --git a/src/lib/components/ChatPanel.svelte b/src/lib/components/ChatPanel.svelte index efa55ef..0a29f99 100644 --- a/src/lib/components/ChatPanel.svelte +++ b/src/lib/components/ChatPanel.svelte @@ -2,9 +2,12 @@ import { invoke } from '@tauri-apps/api/core'; import { messages, currentInput, isProcessing, addMessage, currentSessionId, messageToDb, type Message } from '$lib/stores/app'; import { marked, type Tokens } from 'marked'; - import { tick, onDestroy } from 'svelte'; + import { tick, onDestroy, onMount } from 'svelte'; import { get } from 'svelte/store'; + // Input-Referenz für Focus-Shortcuts + let inputTextarea: HTMLTextAreaElement; + // Custom Renderer für Code-Blöcke mit Wrapper const renderer = new marked.Renderer(); renderer.code = function ({ text, lang }: Tokens.Code): string { @@ -129,6 +132,31 @@ unsubscribe(); }); + // Globale Keyboard Shortcuts + function handleGlobalKeydown(event: KeyboardEvent) { + // Ctrl+K: Focus auf Input + if (event.ctrlKey && event.key === 'k') { + event.preventDefault(); + inputTextarea?.focus(); + return; + } + + // Ctrl+Shift+K: Input leeren und Focus + if (event.ctrlKey && event.shiftKey && event.key === 'K') { + event.preventDefault(); + $currentInput = ''; + inputTextarea?.focus(); + return; + } + } + + onMount(() => { + window.addEventListener('keydown', handleGlobalKeydown); + return () => { + window.removeEventListener('keydown', handleGlobalKeydown); + }; + }); + async function sendMessage() { const text = $currentInput.trim(); if (!text || $isProcessing) return; @@ -159,6 +187,13 @@ } function handleKeydown(event: KeyboardEvent) { + // Ctrl+Enter: Immer senden (auch bei mehrzeiligem Text) + if (event.key === 'Enter' && event.ctrlKey) { + event.preventDefault(); + sendMessage(); + return; + } + // Enter (ohne Shift): Senden if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); sendMessage(); @@ -265,6 +300,74 @@ } return false; } + + // "Das merken" - Speichern in Wissensbasis + let rememberDialogOpen = $state(false); + let rememberContent = $state(''); + let rememberEntry = $state({ + category: 'pattern', + title: '', + tags: '', + priority: 2, + }); + let rememberSaving = $state(false); + let rememberError = $state(''); + + const knowledgeCategories = [ + { id: 'dolibarr', label: 'Dolibarr' }, + { id: 'fints', label: 'FinTS/Banking' }, + { id: 'pattern', label: 'Pattern/Code' }, + { id: 'projekt', label: 'Projekt' }, + { id: 'setup', label: 'Setup/Config' }, + { id: 'ids', label: 'IDs/Referenzen' }, + ]; + + function openRememberDialog(message: Message) { + rememberContent = message.content; + rememberEntry = { + category: 'pattern', + title: '', + tags: '', + priority: 2, + }; + rememberError = ''; + rememberDialogOpen = true; + } + + function closeRememberDialog() { + rememberDialogOpen = false; + rememberContent = ''; + } + + async function saveToKnowledge() { + if (!rememberEntry.title.trim() || !rememberContent.trim()) { + rememberError = 'Titel und Inhalt sind erforderlich'; + return; + } + + rememberSaving = true; + rememberError = ''; + + try { + await invoke('save_knowledge', { + entry: { + category: rememberEntry.category, + title: rememberEntry.title.trim(), + content: rememberContent.trim(), + tags: rememberEntry.tags.trim() || null, + priority: rememberEntry.priority, + status: 'active', + source: 'chat', + related_ids: null, + } + }); + closeRememberDialog(); + } catch (err) { + rememberError = `Fehler: ${err}`; + } finally { + rememberSaving = false; + } + }
@@ -278,7 +381,7 @@
🤖

Starte eine Konversation mit Claude.

-

Enter = Senden, Shift+Enter = Neue Zeile, Escape = Stopp

+

Enter/Ctrl+Enter = Senden, Shift+Enter = Neue Zeile, Ctrl+K = Focus, Escape = Stopp

{:else} {#each $messages as message, index} @@ -300,6 +403,9 @@ {#if message.role === 'assistant' && !$isProcessing && isLastAssistantMessage(index) && message.content} {/if} + {#if message.content && message.role !== 'system'} + + {/if} {message.timestamp.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })} @@ -348,9 +454,10 @@
@@ -368,6 +475,83 @@
+ +{#if rememberDialogOpen} + +{/if} +