implement logic
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user