From 5c35939eab49ed822aa51836cba7bed48b4fdc36 Mon Sep 17 00:00:00 2001 From: team3 Date: Fri, 12 Jun 2026 08:12:11 +0200 Subject: [PATCH] Frontend: Composables useConfirm/useChat/usePolling; Guide-Chat abbrechbar Co-Authored-By: Claude Opus 4.8 (1M context) --- frontend/src/App.vue | 41 +++----------- frontend/src/components/TopicDetail.vue | 67 +++++++--------------- frontend/src/components/TopicSidebar.vue | 17 +----- frontend/src/composables/useChat.js | 71 ++++++++++++++++++++++++ frontend/src/composables/useConfirm.js | 32 +++++++++++ frontend/src/composables/usePolling.js | 44 +++++++++++++++ 6 files changed, 178 insertions(+), 94 deletions(-) create mode 100644 frontend/src/composables/useChat.js create mode 100644 frontend/src/composables/useConfirm.js create mode 100644 frontend/src/composables/usePolling.js diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 587d7dc..ba6ab1e 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,6 +1,7 @@ diff --git a/frontend/src/components/TopicDetail.vue b/frontend/src/components/TopicDetail.vue index 416cba3..be4abfe 100644 --- a/frontend/src/components/TopicDetail.vue +++ b/frontend/src/components/TopicDetail.vue @@ -2,6 +2,7 @@ import { computed, ref, watch, nextTick, onMounted, onUnmounted } from 'vue' import { fetchGuideContent, chatGuide, fetchProgress, setProgress } from '../api.js' import { renderMarkdown } from '../markdown.js' +import { useChat } from '../composables/useChat.js' const props = defineProps({ previewGuide: { type: Object, default: null }, @@ -72,13 +73,16 @@ async function toggleChapter(title) { } } -// --- Chat --- +// --- Chat (Mechanik in useChat; Kontext-Extraktion bleibt hier) --- +const chat = useChat((msgs) => { + const { section, outline } = extractContext() + return chatGuide(props.previewGuide.id, { + section, outline, messages: msgs, provider: props.provider, + }) +}) +const { messages, input, loading, messagesEl, inputEl, send } = chat +const autoGrow = () => chat.autoGrow() const chatOpen = ref(false) -const messages = ref([]) -const input = ref('') -const loading = ref(false) -const messagesEl = ref(null) -const inputEl = ref(null) const panelEl = ref(null) function openChat() { @@ -88,8 +92,7 @@ function openChat() { function closeChat() { chatOpen.value = false - messages.value = [] - input.value = '' + chat.reset() } function onDocMouseDown(e) { @@ -141,43 +144,6 @@ function extractContext() { return { section, outline } } -async function scrollToBottom() { - await nextTick() - if (messagesEl.value) messagesEl.value.scrollTop = messagesEl.value.scrollHeight -} - -function autoGrow() { - const el = inputEl.value - if (!el) return - el.style.height = 'auto' - el.style.height = Math.min(el.scrollHeight, 140) + 'px' -} - -async function send() { - const text = input.value.trim() - if (!text || loading.value || !props.previewGuide) return - messages.value.push({ role: 'user', content: text }) - input.value = '' - nextTick(autoGrow) - loading.value = true - scrollToBottom() - try { - const { section, outline } = extractContext() - const res = await chatGuide(props.previewGuide.id, { - section, - outline, - messages: messages.value, - provider: props.provider, - }) - messages.value.push({ role: 'assistant', content: res.reply || '…' }) - } catch { - messages.value.push({ role: 'assistant', content: 'Fehler bei der Anfrage.' }) - } finally { - loading.value = false - scrollToBottom() - nextTick(() => inputEl.value?.focus()) - } -}