implement logic

This commit is contained in:
centdix
2025-05-27 12:14:35 +02:00
parent db99f557f4
commit 4ac46eeccf
6 changed files with 58 additions and 223 deletions

View File

@@ -1,163 +1,72 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte'
import { writable } from 'svelte/store'
import { twMerge } from 'tailwind-merge'
import Button from '$lib/components/common/button/Button.svelte'
import { Send, X, Loader2, MessageCircle } from 'lucide-svelte'
import { sendUserToast } from '$lib/toast'
import { Send, Loader2 } from 'lucide-svelte'
import { chatRequest, prepareSystemMessage } from './core'
import type { ChatCompletionMessageParam } from 'openai/resources/index.mjs'
export let open = false
const dispatch = createEventDispatcher<{
close: null
}>()
let message = ''
let messages: Array<{ role: 'user' | 'assistant'; content: string; timestamp: Date }> = []
let loading = writable(false)
let chatContainer: HTMLDivElement
// Placeholder chat history for demo
let chatHistory = [
{
role: 'assistant' as const,
content: 'Hello! I\'m your global assistant. How can I help you today?',
timestamp: new Date(Date.now() - 60000)
role: 'assistant',
content: "Hello! I'm your global assistant. How can I help you today?"
}
]
] as ChatCompletionMessageParam[]
$: messages = [...chatHistory, ...messages]
let inputValue = $state('')
let isSubmitting = $state(false)
let currentReply = $state('')
let messages = $state(chatHistory)
function scrollToBottom() {
if (chatContainer) {
setTimeout(() => {
chatContainer.scrollTop = chatContainer.scrollHeight
}, 10)
}
}
let abortController = new AbortController()
let chatContainer: HTMLDivElement
async function sendMessage() {
if (!message.trim() || $loading) return
async function handleSubmit() {
if (!inputValue.trim()) return
const userMessage = message.trim()
message = ''
isSubmitting = true
currentReply = ''
// Add user message
messages = [...messages, {
role: 'user',
content: userMessage,
timestamp: new Date()
}]
const userMessage = inputValue
const systemMessage = prepareSystemMessage()
scrollToBottom()
// Add user message to chat
messages = [...messages, { role: 'user', content: userMessage }]
// Simulate API call with placeholder response
loading.set(true)
try {
// Simulate processing delay
await new Promise(resolve => setTimeout(resolve, 1500))
// Add placeholder assistant response
const responses = [
"I understand you're asking about: " + userMessage + ". This is a placeholder response for the global chat feature.",
"That's an interesting question! The global chat functionality is currently being developed with placeholder responses.",
"Thanks for your message: \"" + userMessage + "\". In the full implementation, this would connect to the AI system.",
"I see you mentioned: " + userMessage + ". This global chat drawer is now functional with placeholder logic as requested."
]
const response = responses[Math.floor(Math.random() * responses.length)]
messages = [...messages, {
role: 'assistant',
content: response,
timestamp: new Date()
}]
// Create message array for API request
const apiMessages = [systemMessage, ...messages]
scrollToBottom()
} catch (error) {
sendUserToast('Error sending message', true)
} finally {
loading.set(false)
}
}
await chatRequest(apiMessages, abortController, (token) => {
currentReply = currentReply + token
})
function handleKeyPress(event: KeyboardEvent) {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault()
sendMessage()
}
}
// Add assistant's response to chat
messages = [...messages, { role: 'assistant', content: currentReply }]
function formatTime(date: Date) {
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
}
function clearChat() {
messages = []
chatHistory = [
{
role: 'assistant' as const,
content: 'Chat cleared! How can I help you now?',
timestamp: new Date()
}
]
scrollToBottom()
// Reset the input field
inputValue = ''
isSubmitting = false
}
</script>
<div class="flex flex-col h-full bg-surface">
<!-- Header -->
<div class="flex items-center justify-between p-4 border-b border-gray-200 dark:border-gray-600">
<div class="flex items-center gap-2">
<MessageCircle size={18} class="text-primary" />
<h2 class="text-lg font-semibold">Global Chat</h2>
</div>
<div class="flex items-center gap-2">
<Button
size="sm"
variant="border"
color="light"
on:click={clearChat}
>
Clear
</Button>
<Button
size="sm"
variant="border"
color="light"
iconOnly
startIcon={{ icon: X }}
on:click={() => dispatch('close')}
/>
</div>
</div>
<!-- Chat Messages -->
<div
bind:this={chatContainer}
class="flex-1 overflow-y-auto p-4 space-y-4"
>
{#each messages as msg (msg.timestamp.getTime())}
<div class={twMerge(
"flex flex-col",
msg.role === 'user' ? "items-end" : "items-start"
)}>
<div class={twMerge(
"max-w-[80%] p-3 rounded-lg text-sm",
msg.role === 'user'
? "bg-blue-500 text-white rounded-br-sm"
: "bg-gray-100 dark:bg-gray-700 text-primary rounded-bl-sm"
)}>
<div bind:this={chatContainer} class="flex-1 overflow-y-auto p-4 space-y-4">
{#each messages as msg}
<div class={twMerge('flex flex-col', msg.role === 'user' ? 'items-end' : 'items-start')}>
<div
class={twMerge(
'max-w-[80%] p-3 rounded-lg text-sm',
msg.role === 'user'
? 'bg-blue-500 text-white rounded-br-sm'
: 'bg-gray-100 dark:bg-gray-700 text-primary rounded-bl-sm'
)}
>
<p class="whitespace-pre-wrap">{msg.content}</p>
</div>
<span class="text-xs text-secondary mt-1">
{formatTime(msg.timestamp)}
</span>
</div>
{/each}
{#if $loading}
{#if isSubmitting}
<div class="flex items-start">
<div class="bg-gray-100 dark:bg-gray-700 p-3 rounded-lg rounded-bl-sm">
<div class="flex items-center gap-2 text-secondary">
@@ -173,23 +82,25 @@
<div class="p-4 border-t border-gray-200 dark:border-gray-600">
<div class="flex gap-2">
<textarea
bind:value={message}
on:keydown={handleKeyPress}
placeholder="Type your message... (Enter to send, Shift+Enter for new line)"
bind:value={inputValue}
onkeydown={(e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
handleSubmit()
}
}}
placeholder="Type your message..."
class="flex-1 resize-none border border-gray-300 dark:border-gray-600 rounded-lg p-3 text-sm bg-surface text-primary focus:outline-none focus:ring-2 focus:ring-blue-500 min-h-[44px] max-h-32"
rows="1"
disabled={$loading}
disabled={isSubmitting}
></textarea>
<Button
size="md"
disabled={!message.trim() || $loading}
disabled={!inputValue.trim() || isSubmitting}
iconOnly
startIcon={{ icon: Send }}
on:click={sendMessage}
on:click={handleSubmit}
/>
</div>
<p class="text-xs text-secondary mt-2">
Global chat with placeholder functionality - ready for AI integration
</p>
</div>
</div>
</div>

View File

@@ -10,12 +10,8 @@
}
</script>
<Drawer
bind:open
size="500px"
placement="right"
>
<Drawer bind:open size="500px" placement="right">
<DrawerContent title="Global Chat" on:close={closeDrawer}>
<GlobalChat {open} on:close={closeDrawer} />
<GlobalChat />
</DrawerContent>
</Drawer>
</Drawer>

View File

@@ -1,65 +0,0 @@
<script lang="ts">
import { chatRequest, prepareUserMessage, prepareSystemMessage } from './core'
// Using Svelte 5 runes for reactivity
let inputValue = $state('')
let isSubmitting = $state(false)
let currentReply = $state('')
let abortController = new AbortController()
// Props definition using $props
let { placeholder = 'Type a message...', buttonText = 'Send' } = $props()
async function handleSubmit() {
if (!inputValue.trim()) return
isSubmitting = true
currentReply = ''
const userMessage = prepareUserMessage(inputValue)
const systemMessage = prepareSystemMessage()
let messages = [systemMessage]
messages.push({ role: 'user', content: userMessage })
const result = await chatRequest(messages, abortController, (token) => {
currentReply = currentReply + token
})
console.log(result)
console.log(currentReply)
// Reset the input field
inputValue = ''
isSubmitting = false
}
</script>
<div class="flex flex-col gap-2">
<form
class="flex w-full gap-2"
onsubmit={(e) => {
e.preventDefault()
handleSubmit()
}}
>
<input
type="text"
class="flex-1 px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
bind:value={inputValue}
{placeholder}
disabled={isSubmitting}
/>
<button
type="submit"
class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50"
disabled={isSubmitting || !inputValue.trim()}
>
{buttonText}
</button>
</form>
<div class="flex flex-row border rounded-md p-2">
<p>{currentReply}</p>
</div>
</div>

View File

@@ -2,16 +2,11 @@
import { Code2, Plus } from 'lucide-svelte'
import Button from '../common/button/Button.svelte'
import { base } from '$lib/base'
import { goto } from '$app/navigation'
// Reference to the button component
let buttonComponent: { click: () => void } | undefined = undefined
export function triggerClick() {
// Navigate to the script creation page directly
// goto(`${base}/scripts/add`)
// Focus the button for visual feedback
if (buttonComponent) {
buttonComponent.click()
}

View File

@@ -54,7 +54,6 @@
import { base } from '$app/paths'
import { Menubar } from '$lib/components/meltComponents'
import GlobalChatDrawer from '$lib/components/chat/GlobalChatDrawer.svelte'
import Input from '$lib/components/globalchat/Input.svelte'
OpenAPI.WITH_CREDENTIALS = true
let menuOpen = false
@@ -661,7 +660,6 @@
>
<main class="min-h-screen">
<div class="relative w-full h-full">
<Input />
<div
class={classNames(
'py-2 px-2 sm:px-4 md:px-8 flex justify-between items-center shadow-sm max-w-7xl mx-auto md:hidden',