optimized webchat

This commit is contained in:
2025-10-05 17:00:46 +00:00
parent 8ae3c914d8
commit 740cb3e1e6
6 changed files with 397 additions and 74 deletions

View File

@@ -1,43 +1,91 @@
<script lang="ts">
import { mdiSendVariantOutline } from "@mdi/js"
import Icon from "../widgets/Icon.svelte"
import type { WerkChatSession } from "@kontextwerk/web-sdk"
let { chat }: { chat: WerkChatSession } = $props()
import { tick } from "svelte"
let {
onSend,
disabled = false,
quickMessages = [
{
title: "Erzähl mir was über",
highlight: "Kontextwerk",
message: "Erzähl mir bitte mehr über Kontextwerk.",
},
{
title: "Ich möchte euch",
highlight: "kontaktieren",
message: "Ich möchte euch kontaktieren. Welche Möglichkeiten gibt es?",
},
],
}: {
onSend: (message: string) => void
disabled?: boolean
quickMessages?: Array<{ title: string; highlight: string; message: string }>
} = $props()
let value = $state("")
let textareaEl: HTMLTextAreaElement = $state(null)
let textareaEl: HTMLTextAreaElement | null = $state(null)
function autoResize() {
if (textareaEl) {
textareaEl.style.height = "auto"
textareaEl.style.height = textareaEl.scrollHeight + "px"
}
}
let submittedOnce = $state(false)
const submit = async () => {
submittedOnce = true
const text = value.trim()
if (!text || disabled) return
onSend(text)
value = ""
await tick()
autoResize()
}
$effect(() => {
void tick().then(autoResize)
})
</script>
<div class="input-row">
{#if quickMessages.length && !submittedOnce}
<div class="quick-actions">
{#each quickMessages as quick, index (index)}
<button
class="quick-chip"
type="button"
{disabled}
onclick={() => {
if (!disabled) {
onSend(quick.message)
}
}}
>
<span class="title">{quick.title}</span>
<span class="highlight">{quick.highlight}</span>
</button>
{/each}
</div>
{/if}
<textarea
placeholder="Schreibe eine Nachricht..."
bind:value
rows="1"
bind:this={textareaEl}
oninput={autoResize}
{disabled}
onkeydown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault()
if (value.trim()) {
chat.generateResponse(value.trim())
value = ""
}
void submit()
}
}}
></textarea>
<button
class="btn"
onclick={() => {
if (value.trim()) {
chat.generateResponse(value.trim())
value = ""
}
}}
onclick={() => void submit()}
disabled={disabled || !value.trim()}
aria-label="Nachricht senden"
>
<Icon
@@ -54,6 +102,59 @@
flex-direction: column;
align-items: flex-end;
position: relative;
gap: 0.75rem;
.quick-actions {
width: 100%;
display: grid;
gap: 0.65rem;
flex-wrap: wrap;
justify-content: flex-start;
padding: 0px 0.6rem;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
.quick-chip {
display: inline-flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
padding: 0.65rem 0.95rem;
border-radius: 4px;
background: var(--bg-200);
color: white;
border: 1px solid var(--bg-100);
cursor: pointer;
gap: 0.25rem;
transition:
background 0.3s ease,
transform 0.3s ease;
&:hover:not(:disabled) {
background: var(--bg-100);
border: 1px solid var(--bg-200);
transform: translateY(-1px);
}
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.title {
font-size: 0.8rem;
letter-spacing: 0.01em;
opacity: 0.8;
}
.highlight {
font-size: 0.95rem;
font-weight: 600;
color: var(--neutral-white);
text-transform: capitalize;
}
}
}
textarea {
width: 100%;
min-height: 3.6rem;
@@ -65,6 +166,8 @@
resize: none;
overflow: hidden;
color: white;
border-top: 1px solid white;
transition: border-top 0.3s ease;
&:focus {
outline: none;
border-top: 1px solid var(--primary-100);
@@ -79,6 +182,10 @@
min-width: unset;
background-color: var(--primary-100);
border-radius: 4px;
&:disabled {
opacity: 0.6;
cursor: not-allowed;
}
}
}
</style>