148 lines
3.1 KiB
Vue
148 lines
3.1 KiB
Vue
<script setup>
|
|
import { watch } from 'vue'
|
|
import { chatElement } from '../../api.js'
|
|
import { useChat } from '../../composables/useChat.js'
|
|
|
|
const props = defineProps({
|
|
element: { type: Object, required: true },
|
|
provider: { type: String, default: 'claude' },
|
|
})
|
|
|
|
const emit = defineEmits(['changes'])
|
|
|
|
const chat = useChat((msgs) => chatElement(props.element.id, msgs, props.provider))
|
|
const { messages, input, loading, messagesEl, inputEl } = chat
|
|
|
|
// Anderes Element gewählt → Verlauf verwerfen
|
|
watch(() => props.element.id, () => chat.reset())
|
|
|
|
async function send() {
|
|
const res = await chat.send()
|
|
if (res?.changes?.length) emit('changes', res.changes)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="el-chat">
|
|
<div ref="messagesEl" class="chat-messages">
|
|
<p v-if="!messages.length" class="chat-hint">Schreib, was am Element geändert werden soll.</p>
|
|
<template v-for="(m, i) in messages" :key="i">
|
|
<div :class="['chat-msg', m.role]">{{ m.content }}</div>
|
|
</template>
|
|
<div v-if="loading" class="chat-msg assistant chat-typing">Passt an…</div>
|
|
</div>
|
|
<div class="chat-input">
|
|
<textarea
|
|
ref="inputEl"
|
|
v-model="input"
|
|
placeholder="Element anpassen…"
|
|
@keydown.enter.exact.prevent="send"
|
|
></textarea>
|
|
<button
|
|
:disabled="!input.trim() && !loading"
|
|
:class="{ cancel: loading }"
|
|
:title="loading ? 'Abbrechen' : 'Senden'"
|
|
@click="send"
|
|
>{{ loading ? '✕' : '➤' }}</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.el-chat {
|
|
display: flex;
|
|
flex-direction: column;
|
|
flex: 1;
|
|
min-height: 0;
|
|
}
|
|
|
|
.chat-messages {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 0.6rem 0.75rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
}
|
|
|
|
.chat-hint {
|
|
color: var(--text-faint);
|
|
font-size: 0.78rem;
|
|
text-align: center;
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.chat-msg {
|
|
max-width: 85%;
|
|
padding: 6px 10px;
|
|
border-radius: 12px;
|
|
font-size: 0.82rem;
|
|
line-height: 1.4;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
}
|
|
|
|
.chat-msg.user {
|
|
align-self: flex-end;
|
|
background: var(--accent);
|
|
color: var(--on-accent);
|
|
border-bottom-right-radius: 3px;
|
|
}
|
|
|
|
.chat-msg.assistant {
|
|
align-self: flex-start;
|
|
background: var(--panel-soft);
|
|
color: var(--text);
|
|
border-bottom-left-radius: 3px;
|
|
}
|
|
|
|
.chat-typing {
|
|
color: var(--text-faint);
|
|
font-style: italic;
|
|
}
|
|
|
|
.chat-input {
|
|
display: flex;
|
|
gap: 6px;
|
|
padding: 0.6rem;
|
|
border-top: 1px solid var(--border);
|
|
}
|
|
|
|
.chat-input textarea {
|
|
flex: 1;
|
|
resize: none;
|
|
height: 72px;
|
|
padding: 8px 10px;
|
|
border: 1px solid var(--border-strong);
|
|
border-radius: 8px;
|
|
font-size: 0.85rem;
|
|
font-family: inherit;
|
|
background: var(--panel);
|
|
color: var(--text);
|
|
outline: none;
|
|
}
|
|
|
|
.chat-input textarea:focus {
|
|
border-color: var(--accent);
|
|
}
|
|
|
|
.chat-input button {
|
|
width: 38px;
|
|
border: none;
|
|
border-radius: 8px;
|
|
background: var(--accent);
|
|
color: var(--on-accent);
|
|
font-size: 1rem;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.chat-input button:disabled {
|
|
opacity: 0.4;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.chat-input button.cancel {
|
|
background: var(--danger);
|
|
}
|
|
</style>
|