WIP: Restyling Chat page

This commit is contained in:
John
2024-12-27 02:13:49 -05:00
parent 84e3ff9386
commit 9d8d5ca924
5 changed files with 178 additions and 166 deletions

View File

@@ -4,56 +4,52 @@
import ModelConfig from "./ModelConfig.svelte";
import Models from "./Models.svelte";
import Patterns from "./Patterns.svelte";
import NoteDrawer from '$lib/components/ui/noteDrawer/NoteDrawer.svelte';
import { getDrawerStore } from '@skeletonlabs/skeleton';
import { page } from '$app/stores';
import { beforeNavigate } from '$app/navigation';
const drawerStore = getDrawerStore();
function openDrawer() {
drawerStore.open({});
}
beforeNavigate(() => {
drawerStore.close();
});
$: isVisible = $page.url.pathname.startsWith('/chat');
//import NoteDrawer from '$lib/components/ui/noteDrawer/NoteDrawer.svelte';
//import { getDrawerStore } from '@skeletonlabs/skeleton';
//import { Button } from '$lib/components/ui/button';
//import { page } from '$app/stores';
//import { beforeNavigate } from '$app/navigation';
//
//const drawerStore = getDrawerStore();
//function openDrawer() {
// drawerStore.open({});
//}
//
//beforeNavigate(() => {
// drawerStore.close();
//});
//
//$: isVisible = $page.url.pathname.startsWith('/chat');
</script>
<div class="flex-1 mx-auto p-4 min-h-screen">
<div class="grid grid-cols-1 auto-fit lg:grid-cols-[250px_minmax(250px,_1.5fr)_minmax(250px,_1.5fr)] gap-4 h-[calc(100vh-2rem)]">
<div class="flex flex-col space-y-1 order-3 lg:order-1">
<div class="space-y-2 max-w-full">
<div class="flex flex-col gap-2">
<div content="width=device-width, height=device-height, initial-scale=1.0">
<div class="h-screen overflow-auto">
<div class="h-full flex flex-col">
<div class="flex-1 overflow-hidden mx-auto p-2">
<!-- <div class="flex flex-col columns-3 m-2 p-1 h-screen"> -->
<div class="h-full flex gap-2">
<aside class="w-1/5 overflow-y-auto">
<!-- <div class="space-y-2"> -->
<!-- <div class="flex flex-col gap-2"> -->
<Patterns />
<Models />
<ModelConfig />
<!-- </div> -->
<!-- </div> -->
</aside>
<div class="w-1/2">
<!-- <div class="flex flex-col space-y-4 order-2 lg:order-2 h-screen"> -->
<ChatInput />
</div>
<div class="flex flex-col gap-2">
{#if isVisible}
<div class="flex justify-start mt-2">
<button type="button"
class="btn btn-sm border variant-filled-primary"
on:click={openDrawer}
>Open Drawer
</button>
</div>
<NoteDrawer />
{/if}
<br>
<div class="w-1/2">
<!-- <div class="flex flex-col rounded-lg order-1 lg:order-3 h-screen w-full"> -->
<ChatMessages />
</div>
</div>
<!-- <button class="primary" on:click={openDrawer}>Open Drawer</button> -->
</div>
<div class="flex flex-col space-y-4 order-2 lg:order-2">
<ChatInput />
</div>
<div class="flex flex-col border rounded-lg bg-muted/50 p-4 order-1 lg:order-3 max-h-[695px]">
<ChatMessages />
<!-- </div> -->
</div>
</div>
</div>
</div>

View File

@@ -22,7 +22,7 @@
// Clear input before sending to improve perceived performance
userInput = "";
await sendMessage(trimmedSystemPrompt + trimmedInput);
await sendMessage(trimmedSystemPrompt + '\n' + trimmedInput);
} catch (error) {
console.error('Chat submission error:', error);
toastStore.trigger({
@@ -45,42 +45,45 @@
});
</script>
<div class="flex flex-col gap-2">
<div class="flex-none">
<Textarea
bind:value={$systemPrompt}
on:input={(e) => $systemPrompt || ''}
placeholder="Enter system instructions..."
class="min-h-[330px] resize-none bg-background"
/>
<div class="h-full">
<div class="flex flex-col gap-2 h-screen">
<div class="flex-1 rounded-lg border-current">
<Textarea
bind:value={$systemPrompt}
on:input={(e) => $systemPrompt || ''}
placeholder="Enter system instructions..."
class="w-full h-full resize-none bg-primary-800/30 rounded-lg border-none"
/>
</div>
<div class="flex-1 py-2 relative">
<Textarea
bind:value={userInput}
on:input={(e) => userInput}
on:keydown={handleKeydown}
placeholder="Enter your message..."
class="min-h-[350px] resize-none bg-background"
/>
<div class="absolute bottom-5 right-2 gap-2 flex justify-end end-7">
<FileButton
name="file-upload"
button="btn btn-sm variant-soft-surface"
bind:files={files}
on:change={(e) => {
// Workin on the file selection
// Check out `https://www.skeleton.dev/components/file-buttons` for more info
}}
>
<Paperclip class="w-4" />
</FileButton>
<Button type="button" name="submit" variant="secondary" on:click={handleSubmit}>
<Send class="w-4 h-4" />
</Button>
</div>
<Textarea
bind:value={userInput}
on:input={(e) => userInput}
on:keydown={handleKeydown}
placeholder="Enter your message..."
class="w-full h-full resize-none bg-primary-800/30 rounded-lg border-none"
/>
<div class="absolute bottom-5 right-2 gap-2 flex justify-end end-7">
<FileButton
name="file-upload"
button="btn btn-sm variant-soft-surface"
bind:files={files}
on:change={(e) => {
// Workin on the file selection
// Check out `https://www.skeleton.dev/components/file-buttons` for more info
// Check 24-12-08 for half-baked implementation
}}
>
<Paperclip class="w-4" />
</FileButton>
<Button type="button" name="submit" variant="secondary" on:click={handleSubmit}>
<Send class="w-4 h-4" />
</Button>
</div>
</div>
</div>
</div>
<style>

View File

@@ -1,6 +1,7 @@
<script lang="ts">
import { chatState, errorStore, streamingStore } from '$lib/store/chat';
import { afterUpdate } from 'svelte';
import { toastStore } from '$lib/store/toast-store';
import { marked } from 'marked';
import SessionManager from './SessionManager.svelte';
import { fade, slide } from 'svelte/transition';
@@ -29,111 +30,123 @@
}
</script>
<div class="chat-messages-wrapper flex flex-col h-full">
<div class="flex justify-between items-center mb-4 flex-none">
<span class="text-sm font-medium">Chat History</span>
<SessionManager />
<div class="bg-primary-800/30 rounded-lg flex flex-col h-full shadow-lg">
<div class="flex justify-between items-center mb-1 mt-1 flex-none">
<div class="flex items-center gap-2 pl-4">
<b class="text-sm text-muted-foreground font-bold">Chat History</b>
</div>
<SessionManager />
</div>
{#if $errorStore}
<div class="error-message" transition:slide>
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-4" role="alert">
<p>{$errorStore}</p>
{#if $errorStore}
<div class="error-message" transition:slide>
<div class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-4" role="alert">
<p>{$errorStore}</p>
</div>
</div>
{/if}
<!-- Check 24-12-08 chat-interface for more updates here -->
<div class="messages-container p-4 flex-1 overflow-y-auto max-h-dvh" bind:this={messagesContainer}>
<div class="messages-content flex flex-col gap-4">
{#each $chatState.messages as message}
<div
class="message-item {message.role === 'assistant' ? 'pl-4 bg-primary/5 rounded-lg p-2' : 'pr-4 ml-auto'}"
transition:fade
>
<div class="message-header flex items-center gap-2 mb-1 {message.role === 'assistant' ? '' : 'justify-end'}">
<span class="text-xs text-muted-foreground rounded-lg p-1 variant-glass-secondary font-bold uppercase">
{message.role === 'assistant' ? 'AI' : 'You'}
</span>
{#if message.role === 'assistant' && $streamingStore}
<span class="loading-indicator flex gap-1">
<span class="dot animate-bounce">.</span>
<span class="dot animate-bounce delay-100">.</span>
<span class="dot animate-bounce delay-200">.</span>
</span>
{/if}
</div>
{#if message.role === 'assistant'}
<div class="prose prose-slate dark:prose-invert text-inherit prose-headings:text-inherit prose-pre:bg-primary/10 prose-pre:text-inherit text-sm max-w-none">
{@html renderMarkdown(message.content, true)}
</div>
{:else}
<div class="whitespace-pre-wrap text-sm">
{message.content}
</div>
{/if}
</div>
{/if}
<div class="messages-container" bind:this={messagesContainer}>
<div class="messages-content">
{#each $chatState.messages as message}
<div
class="message-item {message.role === 'assistant' ? 'pl-4' : 'font-medium'}"
transition:fade
>
<div class="message-header flex items-center gap-2 mb-1">
<span class="text-xs border rounded-lg p-1 variant-glass-secondary font-bold uppercase">{message.role}</span>
{#if message.role === 'assistant' && $streamingStore}
<span class="loading-indicator">
<span class="dot">.</span>
<span class="dot">.</span>
<span class="dot">.</span>
</span>
{/if}
</div>
{#if message.role === 'assistant'}
<div class="prose prose-sm text-inherit max-w-none">
{@html renderMarkdown(message.content, true)}
</div>
{:else}
<div class="whitespace-pre-wrap text-sm">
{message.content}
</div>
{/if}
</div>
{/each}
</div>
{/each}
</div>
</div>
</div>
<style>
.chat-messages-wrapper {
display: flex;
flex-direction: column;
min-height: 0;
}
/*.chat-messages-wrapper {*/
/* display: flex;*/
/* flex-direction: column;*/
/* min-height: 0;*/
/*}*/
.messages-container {
flex: 1;
overflow-y: auto;
padding-right: 1rem;
}
.messages-container {
flex: 1;
overflow-y: auto;
scrollbar-width: thin;
-ms-overflow-style: thin;
}
.messages-content {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.messages-content {
display: flex;
flex-direction: column;
gap: 2rem;
}
.message-item {
position: relative;
}
.message-header {
display: flex;
gap: 0.5rem;
}
.loading-indicator {
display: inline-flex;
gap: 2px;
}
.message-item {
position: relative;
}
.dot {
animation: blink 1.4s infinite;
opacity: 0;
}
.loading-indicator {
display: inline-flex;
gap: 2px;
}
.dot:nth-child(2) {
animation-delay: 0.2s;
}
.dot {
animation: blink 1.4s infinite;
opacity: 0;
}
.dot:nth-child(3) {
animation-delay: 0.4s;
}
.dot:nth-child(2) {
animation-delay: 0.2s;
}
@keyframes blink {
0%, 100% { opacity: 0; }
50% { opacity: 1; }
}
.dot:nth-child(3) {
animation-delay: 0.4s;
}
:global(.prose pre) {
background-color: rgb(40, 44, 52);
color: rgb(171, 178, 191);
padding: 1rem;
border-radius: 0.375rem;
margin: 1rem 0;
}
@keyframes blink {
0%, 100% { opacity: 0; }
50% { opacity: 1; }
}
:global(.prose code) {
color: rgb(171, 178, 191);
background-color: rgba(40, 44, 52, 0.1);
padding: 0.2em 0.4em;
border-radius: 0.25rem;
}
:global(.prose pre) {
background-color: rgb(40, 44, 52);
color: rgb(171, 178, 191);
padding: 1rem;
border-radius: 0.375rem;
margin: 1rem 0;
}
:global(.prose code) {
color: rgb(171, 178, 191);
background-color: rgba(40, 44, 52, 0.1);
padding: 0.2em 0.4em;
border-radius: 0.25rem;
}
</style>

View File

@@ -47,7 +47,7 @@
}
</script>
<div class="space-y-4">
<div class="p-1 m-1 mr-2">
<div class="flex gap-2">
<Button variant="outline" size="icon" aria-label="Revert Last Message" on:click={revertLastMessage}>
<RotateCcw class="h-4 w-4" />

View File

@@ -42,7 +42,7 @@
<div
in:fly={{ duration: 500, delay: 100, y: 100 }}
>
<main class="m-auto p-4">
<main class="main m-auto">
<slot />
</main>
</div>