- Restructure floating input: dominant textarea with compact bottom toolbar (model badge, thinking toggle, attach, send/stop). - Model badge sizes to the current selection (not widest option) via a layered transparent select, with truncate-on-overflow fallback. - Auto-expand the conversation sidebar on desktop and slide chat content right when open instead of overlaying. - Add per-request thinking toggle (brain icon, default on, persisted in localStorage) sending chat_template_kwargs.enable_thinking. - Always disable thinking for title summarization. - Generate chat titles before the main response to keep the SSE stream from staying open past visible completion and to avoid busting the KV cache between turns.
542 lines
21 KiB
HTML
542 lines
21 KiB
HTML
<div x-data="chatManager()">
|
||
<!-- Chat Content -->
|
||
<div
|
||
:class="chatListOpen ? 'md:pl-[23rem]' : ''"
|
||
class="h-dvh pt-16 flex flex-col-reverse pb-36 overflow-scroll mx-auto px-4 md:px-6 max-w-6xl transition-all duration-300 ease-out"
|
||
>
|
||
<template x-for="message in currentChatMessages" :key="message.id">
|
||
<div
|
||
:class="['mb-4', message.role === 'user' ? 'text-right' : 'text-left']"
|
||
>
|
||
<div
|
||
:class="['inline-block px-4 py-3 text-left rounded-lg max-w-[95%] md:max-w-[85%]',
|
||
message.role === 'user'
|
||
? 'bg-primary-100 text-primary-900 rounded-br-none'
|
||
: 'bg-primary-200 text-primary-900 rounded-bl-none'
|
||
]"
|
||
>
|
||
<!-- User Images -->
|
||
<div
|
||
x-show="message.role === 'user' && message.images && message.images.length > 0"
|
||
class="flex gap-1 mb-2 flex-wrap"
|
||
>
|
||
<template x-for="(img, imgIdx) in message.images" :key="imgIdx">
|
||
<img :src="img" class="max-w-full h-auto rounded-lg max-h-48" />
|
||
</template>
|
||
</div>
|
||
|
||
<!-- Thinking Section -->
|
||
<div
|
||
x-show="message.thinking"
|
||
x-data="{ expanded: false }"
|
||
@click="expanded = !expanded"
|
||
>
|
||
<div
|
||
class="cursor-pointer rounded-lg overflow-hidden bg-primary-100 hover:bg-primary-50"
|
||
>
|
||
<div
|
||
class="flex justify-center w-full px-3 py-2 text-xs text-primary-700 flex items-center gap-2 transition-colors"
|
||
>
|
||
<span x-text="expanded ? '▼' : '◀'"></span>
|
||
<span class="font-medium">Reasoning</span>
|
||
<span x-text="expanded ? '▼' : '▶'"></span>
|
||
</div>
|
||
<div
|
||
x-show="expanded"
|
||
class="prose p-4 max-w-none text-sm prose-p:my-1 prose-headings:my-2 prose-ul:my-1 prose-ol:my-1 prose-pre:p-0"
|
||
x-html="renderMarkdown(message.thinking)"
|
||
></div>
|
||
</div>
|
||
</div>
|
||
|
||
<hr x-show="message.thinking" class="my-2 border-primary-400/50" />
|
||
|
||
<!-- Loading Spinner (Streaming with no content yet) -->
|
||
<div
|
||
x-show="message.role === 'assistant' && message.status === 'streaming' && !message.thinking && !message.content"
|
||
class="flex items-center gap-2 py-1"
|
||
>
|
||
<div
|
||
class="h-4 w-4 animate-spin rounded-full border-2 border-primary-500 border-t-transparent"
|
||
></div>
|
||
<span class="text-xs text-primary-600">Thinking...</span>
|
||
</div>
|
||
|
||
<!-- Main Content -->
|
||
<div
|
||
x-show="message.content || message.status !== 'streaming'"
|
||
class="prose max-w-none text-sm prose-p:my-1 prose-headings:my-2 prose-ul:my-1 prose-ol:my-1 prose-pre:p-0"
|
||
x-html="renderMarkdown(message.content)"
|
||
></div>
|
||
|
||
<!-- Message Metadata -->
|
||
<div class="flex items-center justify-between gap-2 mt-2">
|
||
<div
|
||
class="text-[10px] opacity-60"
|
||
x-text="new Date(message.created_at).toLocaleTimeString()"
|
||
></div>
|
||
<div
|
||
x-show="message.role === 'assistant' && ['stopped', 'error', 'failed'].includes(message.status)"
|
||
:class="message.status === 'stopped' ? 'bg-primary-300/50 text-primary-700' : 'bg-tertiary-100 text-tertiary-700'"
|
||
class="px-2 py-0.5 rounded-full text-[10px] font-medium"
|
||
x-text="message.status === 'stopped' ? 'Stopped' : 'Error'"
|
||
></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Stats Badges (Assistant) -->
|
||
<div
|
||
x-show="message.role === 'assistant' && message.stats"
|
||
class="flex items-center gap-1 py-2 flex-wrap justify-start text-primary-700"
|
||
>
|
||
<!-- Cumulative Tokens with Hover Breakdown -->
|
||
<div
|
||
x-show="message.stats?.prompt_tokens || message.stats?.generated_tokens"
|
||
class="group relative px-2 py-0.5 text-[10px] bg-primary-300/50 rounded-full cursor-help"
|
||
>
|
||
<span
|
||
x-text="`${(message.stats?.prompt_tokens || 0) + (message.stats?.generated_tokens || 0)} tokens`"
|
||
></span>
|
||
|
||
<!-- Tokens -->
|
||
<div
|
||
class="invisible group-hover:visible absolute bottom-full left-1/2 -translate-x-1/2 m-2 px-2 py-1 bg-gray-900 text-white text-[10px] rounded whitespace-nowrap pointer-events-none grid grid-cols-[auto_1fr] gap-x-2"
|
||
>
|
||
<div
|
||
x-show="message.stats?.prompt_tokens"
|
||
x-text="message.stats?.prompt_tokens"
|
||
></div>
|
||
<div x-show="message.stats?.prompt_tokens">prompt tokens</div>
|
||
|
||
<div
|
||
x-show="message.stats?.generated_tokens"
|
||
x-text="message.stats?.generated_tokens"
|
||
></div>
|
||
<div x-show="message.stats?.generated_tokens">
|
||
generated tokens
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<span
|
||
x-show="message.stats?.prompt_per_second"
|
||
class="px-2 py-0.5 text-[10px] bg-primary-300/50 rounded-full"
|
||
x-text="`${message.stats?.prompt_per_second.toFixed(1)} ppt/s`"
|
||
></span>
|
||
<span
|
||
x-show="message.stats?.generated_per_second"
|
||
class="px-2 py-0.5 text-[10px] bg-primary-300/50 rounded-full"
|
||
x-text="`${message.stats?.generated_per_second.toFixed(1)} tgt/s`"
|
||
></span>
|
||
<span
|
||
x-show="message.stats?.time_to_first_token"
|
||
class="px-2 py-0.5 text-[10px] bg-primary-300/50 rounded-full"
|
||
x-text="`${(message.stats?.time_to_first_token / 1000).toFixed(2)}s TTFT`"
|
||
></span>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
|
||
<!-- Floating Input and Model Selection -->
|
||
<div
|
||
:class="chatListOpen ? 'md:pl-[23rem]' : ''"
|
||
class="fixed bottom-4 w-full flex justify-center px-4 md:px-6 transition-all duration-300 ease-out"
|
||
>
|
||
<div class="w-full sm:w-[calc(100%-2rem)] max-w-3xl z-10">
|
||
<form
|
||
@submit.prevent="sendMessage"
|
||
class="flex flex-col gap-2 p-3 bg-primary-50/95 backdrop-blur-sm rounded-2xl shadow-2xl border border-primary-200"
|
||
>
|
||
<!-- Hidden File Input -->
|
||
<input
|
||
x-ref="fileInput"
|
||
type="file"
|
||
accept="image/png, image/jpeg, image/webp"
|
||
multiple
|
||
class="hidden"
|
||
@change="
|
||
Array.from($event.target.files).forEach(file => {
|
||
const reader = new FileReader();
|
||
reader.onload = (e) => { selectedImages.push(e.target.result); };
|
||
reader.readAsDataURL(file);
|
||
});
|
||
$event.target.value = '';
|
||
"
|
||
/>
|
||
|
||
<!-- Image Preview Strip -->
|
||
<div
|
||
x-show="selectedImages.length > 0"
|
||
class="flex gap-2 flex-wrap"
|
||
>
|
||
<template x-for="(img, idx) in selectedImages" :key="idx">
|
||
<div class="relative">
|
||
<img :src="img" class="w-20 h-20 object-cover rounded-lg" />
|
||
<button
|
||
type="button"
|
||
@click="selectedImages.splice(idx, 1)"
|
||
class="absolute -top-1 -right-1 w-5 h-5 bg-tertiary-700 text-white rounded-full flex items-center justify-center text-xs hover:bg-tertiary-900"
|
||
>
|
||
×
|
||
</button>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
|
||
<!-- Message Textarea (dominant) -->
|
||
<textarea
|
||
x-model="inputMessage"
|
||
placeholder="Type your message..."
|
||
rows="2"
|
||
class="scrollbar-hide w-full px-2 py-1 bg-transparent border-0 focus:outline-none focus:ring-0 text-primary-900 text-sm resize-none overflow-y-auto max-h-60"
|
||
@keydown.enter="if (!$event.shiftKey) { $event.preventDefault(); sendMessage(); }"
|
||
@input="$el.style.height = 'auto'; $el.style.height = $el.scrollHeight + 'px'"
|
||
></textarea>
|
||
|
||
<!-- Bottom Toolbar: model badge + actions -->
|
||
<div class="flex items-center gap-2">
|
||
<!-- Model Badge Select -->
|
||
<div class="relative min-w-0 flex-shrink inline-flex items-center pl-7 pr-6 py-1 bg-primary-200/70 hover:bg-primary-300 border border-primary-200 rounded-full transition-colors">
|
||
<span
|
||
class="text-primary-900 text-xs font-medium truncate pointer-events-none"
|
||
x-text="(models.find(m => m.id === selectedModel)?.name) || selectedModel || 'Select Model'"
|
||
></span>
|
||
<select
|
||
x-model="selectedModel"
|
||
class="absolute inset-0 w-full h-full opacity-0 cursor-pointer focus:outline-none"
|
||
aria-label="Select model"
|
||
>
|
||
<option value="">Select Model</option>
|
||
<template x-for="model in models" :key="model.id">
|
||
<option
|
||
:value="model.id"
|
||
x-text="model.name || model.id"
|
||
></option>
|
||
</template>
|
||
</select>
|
||
<svg
|
||
class="absolute left-2 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-primary-500 pointer-events-none"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
|
||
/>
|
||
</svg>
|
||
<svg
|
||
class="absolute right-1.5 top-1/2 -translate-y-1/2 h-3 w-3 text-primary-500 pointer-events-none"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M19 9l-7 7-7-7"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
|
||
<!-- Thinking Toggle Badge -->
|
||
<button
|
||
type="button"
|
||
@click="thinkingEnabled = !thinkingEnabled"
|
||
:class="thinkingEnabled
|
||
? 'bg-primary-600 text-white border-primary-600 hover:bg-primary-700'
|
||
: 'bg-transparent text-primary-500 border-primary-300 hover:bg-primary-200/70'"
|
||
class="relative h-7 w-7 rounded-full border transition-colors flex items-center justify-center flex-shrink-0"
|
||
:title="thinkingEnabled ? 'Thinking on' : 'Thinking off'"
|
||
:aria-pressed="thinkingEnabled ? 'true' : 'false'"
|
||
aria-label="Toggle thinking"
|
||
>
|
||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path d="M12 5a3 3 0 0 0-5.997.125 4 4 0 0 0-2.526 5.77 4 4 0 0 0 .556 6.588A4 4 0 0 0 12 21z" />
|
||
<path d="M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.526 5.77 4 4 0 0 1-.556 6.588A4 4 0 0 1 12 21" />
|
||
<path d="M15 13a4.5 4.5 0 0 1-3-4 4.5 4.5 0 0 1-3 4" />
|
||
<path d="M17.599 6.5a3 3 0 0 0 .399-1.375" />
|
||
<path d="M6.003 5.125A3 3 0 0 0 6.401 6.5" />
|
||
<path d="M3.477 10.896a4 4 0 0 1 .585-.396" />
|
||
<path d="M19.938 10.5a4 4 0 0 1 .585.396" />
|
||
<path d="M6 18a4 4 0 0 1-1.967-.516" />
|
||
<path d="M19.967 17.484A4 4 0 0 1 18 18" />
|
||
</svg>
|
||
<svg
|
||
x-show="!thinkingEnabled"
|
||
class="absolute inset-0 h-full w-full text-primary-500"
|
||
viewBox="0 0 24 24"
|
||
fill="none"
|
||
stroke="currentColor"
|
||
aria-hidden="true"
|
||
>
|
||
<line x1="5" y1="19" x2="19" y2="5" stroke-width="2" stroke-linecap="round" />
|
||
</svg>
|
||
</button>
|
||
|
||
<!-- Attach Image Badge Button -->
|
||
<button
|
||
type="button"
|
||
@click="$refs.fileInput.click()"
|
||
class="h-7 w-7 bg-primary-200/70 text-primary-700 rounded-full transition-colors flex items-center justify-center flex-shrink-0 hover:bg-primary-300"
|
||
title="Attach Image"
|
||
aria-label="Attach Image"
|
||
>
|
||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"
|
||
/>
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M15 13a3 3 0 11-6 0 3 3 0 016 0z"
|
||
/>
|
||
</svg>
|
||
</button>
|
||
|
||
|
||
|
||
<!-- Stop Button -->
|
||
<button
|
||
x-show="loading"
|
||
type="button"
|
||
@click="stopResponse()"
|
||
:disabled="!activeStreamChatID"
|
||
:class="!activeStreamChatID ? 'opacity-50 cursor-not-allowed' : 'hover:bg-tertiary-700'"
|
||
class="ml-auto h-7 w-7 bg-tertiary-600 text-white rounded-full transition-colors flex items-center justify-center flex-shrink-0"
|
||
title="Stop response"
|
||
aria-label="Stop response"
|
||
>
|
||
<svg class="h-3 w-3" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||
<path d="M6 6h12v12H6z" />
|
||
</svg>
|
||
</button>
|
||
|
||
<!-- Send Button -->
|
||
<button
|
||
type="submit"
|
||
:disabled="(!inputMessage.trim() && selectedImages.length === 0) || loading"
|
||
:class=" ((!inputMessage.trim() && selectedImages.length === 0) || loading) ? 'opacity-50 cursor-not-allowed' : 'hover:shadow-md'"
|
||
class="ml-auto h-7 w-7 bg-gradient-to-r from-primary-600 to-primary-500 text-white rounded-full transition-all flex items-center justify-center flex-shrink-0"
|
||
title="Send"
|
||
aria-label="Send"
|
||
>
|
||
<template x-if="loading">
|
||
<div class="h-3 w-3 animate-spin rounded-full border-2 border-white border-t-transparent"></div>
|
||
</template>
|
||
<template x-if="!loading">
|
||
<svg class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8"
|
||
/>
|
||
</svg>
|
||
</template>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Error Message -->
|
||
<div
|
||
x-show="error"
|
||
class="bg-tertiary-50 border border-tertiary-200 rounded-lg px-4 py-2"
|
||
>
|
||
<p class="text-sm text-tertiary-700" x-text="error"></p>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Floating Conversation List Toggle -->
|
||
<button
|
||
@click="chatListOpen = !chatListOpen"
|
||
:aria-expanded="chatListOpen ? 'true' : 'false'"
|
||
aria-label="Toggle left navigation"
|
||
class="isolate cursor-pointer fixed z-50 flex justify-between top-4 left-4 md:left-6 p-2 rounded-md text-primary-700 hover:bg-primary-300 transition-colors"
|
||
>
|
||
<svg
|
||
x-show="!chatListOpen"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
class="h-5 w-5"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M4 6h16M4 12h16M4 18h16"
|
||
/>
|
||
</svg>
|
||
<svg
|
||
x-show="chatListOpen"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
class="h-5 w-5"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M6 18L18 6M6 6l12 12"
|
||
/>
|
||
</svg>
|
||
</button>
|
||
|
||
<!-- Floating Conversation List -->
|
||
<div
|
||
x-show="chatListOpen"
|
||
x-transition:enter="transform transition-all duration-300 ease-out"
|
||
x-transition:enter-start="-translate-x-full opacity-0"
|
||
x-transition:enter-end="translate-x-0 opacity-100"
|
||
x-transition:leave="transform transition-all duration-300 ease-in"
|
||
x-transition:leave-start="translate-x-0 opacity-100"
|
||
x-transition:leave-end="-translate-x-full opacity-0"
|
||
class="fixed top-16 left-0 right-0 mx-auto md:left-6 md:right-auto md:mx-0 bottom-4 w-86 bg-primary-100 rounded-xl shadow-lg z-20 overflow-hidden flex flex-col"
|
||
>
|
||
<div class="px-4 py-3 border-b border-primary-200 flex justify-center">
|
||
<h4 class="font-semibold text-primary-900">
|
||
<span>Conversations</span>
|
||
</h4>
|
||
</div>
|
||
|
||
<!-- Conversation List-->
|
||
<div id="left-nav-desktop" class="flex-1 overflow-y-auto p-4">
|
||
<div
|
||
x-show="chats.length === 0"
|
||
class="h-full flex flex-col justify-center text-center py-8 text-primary-600"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
class="h-10 w-10 mx-auto mb-2 text-primary-300"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
|
||
/>
|
||
</svg>
|
||
<p class="text-sm">No chats yet</p>
|
||
</div>
|
||
|
||
<div class="space-y-2">
|
||
<template x-for="chat in chats" :key="chat.id">
|
||
<div
|
||
@click="selectChat(chat.id); if (!window.matchMedia('(min-width: 768px)').matches) chatListOpen = false;"
|
||
:class="[
|
||
'p-3 rounded-lg cursor-pointer transition-all border-l-3',
|
||
selectedChatID === chat.id
|
||
? 'bg-primary-200 border-l-primary-600'
|
||
: 'hover:bg-primary-200 border-l-transparent'
|
||
]"
|
||
:title="chat.title"
|
||
>
|
||
<div class="flex items-start gap-3">
|
||
<!-- Icon -->
|
||
<div class="mt-0.5 shrink-0">
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
:class="[
|
||
'h-4 w-4',
|
||
selectedChatID === chat.id ? 'text-primary-600' : 'text-primary-400'
|
||
]"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
|
||
<div class="flex-1 min-w-0">
|
||
<div class="font-medium text-sm text-primary-900 truncate">
|
||
<span x-text="chat.title || 'New Conversation'"></span>
|
||
</div>
|
||
<div
|
||
class="flex items-center gap-2 mt-1.5 text-xs text-primary-600"
|
||
>
|
||
<span
|
||
x-show="chat.message_count > 0"
|
||
class="shrink-0 bg-primary-300 text-primary-700 px-1.5 py-0.5 rounded text-[10px] font-medium"
|
||
x-text="chat.message_count"
|
||
></span>
|
||
<span class="truncate" x-text="chat.initial_message"></span>
|
||
</div>
|
||
</div>
|
||
|
||
<button
|
||
@click.stop="deleteChat($event, chat.id)"
|
||
class="cursor-pointer shrink-0 p-1 text-primary-400 hover:text-tertiary-600 hover:bg-tertiary-100 rounded transition-colors"
|
||
title="Delete Chat"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
class="h-4 w-4"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||
/>
|
||
</svg>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Left Nav Footer -->
|
||
<div
|
||
x-show="$store.navigation.activeTab === 'chats'"
|
||
class="p-4 border-t border-primary-200 shrink-0"
|
||
>
|
||
<button
|
||
@click="selectChat(null)"
|
||
class="w-full px-4 py-2.5 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors text-sm font-medium flex cursor-pointer items-center justify-center gap-2"
|
||
>
|
||
<svg
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
class="h-4 w-4"
|
||
fill="none"
|
||
viewBox="0 0 24 24"
|
||
stroke="currentColor"
|
||
>
|
||
<path
|
||
stroke-linecap="round"
|
||
stroke-linejoin="round"
|
||
stroke-width="2"
|
||
d="M12 4v16m8-8H4"
|
||
/>
|
||
</svg>
|
||
New Conversation
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|