2025-09-16 16:14:37 +00:00
|
|
|
<script lang="ts">
|
|
|
|
|
import { Resolve } from "Services/DependencyService";
|
|
|
|
|
import { Services } from "Services/Services";
|
2025-09-27 12:23:28 +00:00
|
|
|
import type { StreamingMarkdownService } from "Services/StreamingMarkdownService";
|
2025-12-04 23:04:20 +00:00
|
|
|
import ThoughtIndicator from "./ThoughtIndicator.svelte";
|
2025-09-28 22:31:35 +00:00
|
|
|
import StreamingIndicator from "./StreamingIndicator.svelte";
|
2025-10-02 08:34:54 +00:00
|
|
|
import { Greeting } from "Enums/Greeting";
|
2025-10-04 12:10:22 +00:00
|
|
|
import { Role } from "Enums/Role";
|
|
|
|
|
import type { ConversationContent } from "Conversations/ConversationContent";
|
2025-10-11 11:28:42 +00:00
|
|
|
import { tick } from "svelte";
|
2026-07-10 21:57:25 +00:00
|
|
|
import { setElementIcon } from "Helpers/ElementHelper";
|
2026-05-25 18:42:30 +00:00
|
|
|
import { setIcon } from "obsidian";
|
|
|
|
|
import { fade } from "svelte/transition";
|
2026-07-06 13:22:51 +00:00
|
|
|
import GraphAnimation from "./GraphAnimation.svelte";
|
2026-07-11 12:49:44 +00:00
|
|
|
import { ArtifactAction, artifactActionToCopy } from "Enums/ArtifactAction";
|
|
|
|
|
import { basename } from "path-browserify";
|
2025-09-16 16:14:37 +00:00
|
|
|
|
2025-10-04 12:10:22 +00:00
|
|
|
export let messages: ConversationContent[] = [];
|
2025-10-07 21:27:51 +00:00
|
|
|
export let currentThought: string | null = null;
|
2025-10-06 23:57:08 +00:00
|
|
|
export let isSubmitting: boolean = false;
|
2025-09-28 16:40:05 +00:00
|
|
|
export let chatContainer: HTMLDivElement;
|
2025-10-02 19:52:43 +00:00
|
|
|
|
2025-10-24 16:25:19 +00:00
|
|
|
export function resetChatArea() {
|
|
|
|
|
chatContainer.scroll({ top: 0, behavior: "instant" });
|
2026-06-27 13:44:55 +00:00
|
|
|
tick().then(updateScrolledState);
|
2025-10-24 16:25:19 +00:00
|
|
|
}
|
|
|
|
|
|
2026-07-10 21:57:25 +00:00
|
|
|
export async function updateChatAreaLayout(behavior: ScrollBehavior | undefined = undefined) {
|
2026-01-30 19:36:52 +00:00
|
|
|
await tick();
|
2025-10-24 16:25:19 +00:00
|
|
|
|
2026-01-30 19:36:52 +00:00
|
|
|
requestAnimationFrame(() => {
|
2026-07-10 21:57:25 +00:00
|
|
|
if (behavior) {
|
|
|
|
|
scrollToLatestTurn(behavior);
|
|
|
|
|
}
|
|
|
|
|
tick().then(updateScrolledState);
|
2026-01-30 19:36:52 +00:00
|
|
|
});
|
2026-01-29 17:01:50 +00:00
|
|
|
}
|
|
|
|
|
|
2026-07-10 21:57:25 +00:00
|
|
|
function scrollToLatestTurn(behavior: ScrollBehavior) {
|
|
|
|
|
const latestTurn = chatContainer.querySelector<HTMLElement>(".message-group.latest");
|
|
|
|
|
if (!latestTurn) {
|
2026-01-29 17:01:50 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2026-07-10 21:57:25 +00:00
|
|
|
const paddingTop = parseFloat(getComputedStyle(chatContainer).paddingTop) || 0;
|
|
|
|
|
chatContainer.scroll({ top: latestTurn.offsetTop - paddingTop, behavior });
|
2025-10-11 23:33:04 +00:00
|
|
|
}
|
|
|
|
|
|
2026-07-10 21:57:25 +00:00
|
|
|
function scrollToBottom(behavior: ScrollBehavior) {
|
|
|
|
|
chatContainer.scroll({ top: chatContainer.scrollHeight, behavior });
|
2026-04-19 22:53:14 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-25 18:42:30 +00:00
|
|
|
function updateScrolledState() {
|
2026-06-27 13:44:55 +00:00
|
|
|
if (!chatContainer) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const isScrollable = chatContainer.scrollHeight > chatContainer.clientHeight;
|
|
|
|
|
scrolledToBottom = !isScrollable || Math.abs(chatContainer.scrollHeight - chatContainer.scrollTop - chatContainer.clientHeight) < 100;
|
2026-05-25 18:42:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let scrolledToBottom: boolean = true;
|
|
|
|
|
|
|
|
|
|
let scrollToBottomButton: HTMLButtonElement;
|
2025-10-11 11:28:42 +00:00
|
|
|
|
2025-10-12 20:26:01 +00:00
|
|
|
let streamingMarkdownService: StreamingMarkdownService = Resolve<StreamingMarkdownService>(Services.StreamingMarkdownService);
|
2025-09-28 13:57:58 +00:00
|
|
|
|
2026-07-10 21:57:25 +00:00
|
|
|
type Turn = { id: number, messages: ConversationContent[] };
|
|
|
|
|
|
|
|
|
|
$: turns = groupTurns(messages);
|
|
|
|
|
|
|
|
|
|
// A turn starts at each visible user message; hidden contents (tool
|
|
|
|
|
// responses, planning notices, attachment stubs) stay attached to the
|
|
|
|
|
// turn they belong to instead of starting phantom turns
|
|
|
|
|
function groupTurns(messages: ConversationContent[]): Turn[] {
|
|
|
|
|
const turns: Turn[] = [];
|
|
|
|
|
for (const message of messages) {
|
|
|
|
|
if ((message.role === Role.User && message.shouldDisplayContent) || turns.length === 0) {
|
|
|
|
|
turns.push({ id: message.id, messages: [] });
|
|
|
|
|
}
|
|
|
|
|
turns[turns.length - 1].messages.push(message);
|
|
|
|
|
}
|
|
|
|
|
return turns;
|
|
|
|
|
}
|
2025-10-05 17:43:48 +00:00
|
|
|
|
2025-10-02 08:34:54 +00:00
|
|
|
function getGreetingByTime(): string {
|
|
|
|
|
const hour = new Date().getHours();
|
|
|
|
|
|
|
|
|
|
// Morning: 5am - 11:59am
|
|
|
|
|
if (hour >= 5 && hour < 12) {
|
|
|
|
|
return Greeting.Morning;
|
|
|
|
|
}
|
|
|
|
|
// Midday: 12pm - 4:59pm
|
|
|
|
|
else if (hour >= 12 && hour < 17) {
|
|
|
|
|
return Greeting.Midday;
|
|
|
|
|
}
|
|
|
|
|
// Evening: 5pm - 8:59pm
|
|
|
|
|
else if (hour >= 17 && hour < 21) {
|
|
|
|
|
return Greeting.Evening;
|
|
|
|
|
}
|
|
|
|
|
// Night: 9pm - 4:59am
|
|
|
|
|
else {
|
|
|
|
|
return Greeting.Night;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 15:25:22 +00:00
|
|
|
function messageRenderAction(element: HTMLElement, message: ConversationContent) {
|
|
|
|
|
streamingMarkdownService.render(message.getDisplayContent(), element);
|
|
|
|
|
return {
|
|
|
|
|
update(newMessage: ConversationContent) {
|
|
|
|
|
streamingMarkdownService.render(newMessage.getDisplayContent(), element, !isSubmitting);
|
2025-09-27 12:23:28 +00:00
|
|
|
}
|
2026-05-25 15:25:22 +00:00
|
|
|
};
|
2025-10-11 11:28:42 +00:00
|
|
|
}
|
|
|
|
|
|
2026-07-11 12:49:44 +00:00
|
|
|
function tallyArtifactsByAction(artifacts: { action: ArtifactAction }[]): Partial<Record<ArtifactAction, number>> {
|
|
|
|
|
const tally: Partial<Record<ArtifactAction, number>> = {};
|
|
|
|
|
for (const artifact of artifacts) {
|
|
|
|
|
tally[artifact.action] = (tally[artifact.action] ?? 0) + 1;
|
|
|
|
|
}
|
|
|
|
|
return tally;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 18:42:30 +00:00
|
|
|
$: if (scrollToBottomButton) {
|
|
|
|
|
setIcon(scrollToBottomButton, "arrow-down");
|
|
|
|
|
}
|
2025-09-16 16:14:37 +00:00
|
|
|
</script>
|
|
|
|
|
|
2025-12-20 11:20:43 +00:00
|
|
|
<div class="chat-area-wrapper">
|
|
|
|
|
{#if messages.length > 0}
|
2025-12-22 20:02:02 +00:00
|
|
|
<div class="top-fade"></div>
|
2025-12-20 11:20:43 +00:00
|
|
|
{/if}
|
2026-05-25 18:42:30 +00:00
|
|
|
<div class="chat-area" bind:this={chatContainer} on:scroll={updateScrolledState}>
|
2026-07-10 21:57:25 +00:00
|
|
|
{#each turns as turn, turnIndex (turn.id)}
|
|
|
|
|
{@const isLatestTurn = turnIndex === turns.length - 1}
|
|
|
|
|
<div class="message-group" class:latest={isLatestTurn}>
|
|
|
|
|
{#each turn.messages as message (message.id)}
|
|
|
|
|
{@const content = message.getDisplayContent()}
|
|
|
|
|
{#if message.shouldDisplayContent && content.trim() !== ""}
|
|
|
|
|
{#if message.role === Role.User}
|
|
|
|
|
<div class="message-container {Role.User}">
|
|
|
|
|
<div class="message-bubble {Role.User}">
|
|
|
|
|
<div class="message-text-user-container" contenteditable="false">
|
|
|
|
|
<div class="message-text-user">
|
|
|
|
|
{@html content}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{#if message.references.length > 0}
|
|
|
|
|
<hr class="message-attachment-break"/>
|
|
|
|
|
<div class="message-attachments-container">
|
|
|
|
|
{#each message.references as reference}
|
|
|
|
|
<div class="message-attachmanet" aria-label="{reference.fileName}">
|
|
|
|
|
<div
|
|
|
|
|
class="message-attachment-icon"
|
|
|
|
|
use:setElementIcon={reference.getIconName()}
|
|
|
|
|
></div>
|
|
|
|
|
<div class="message-attachment-info">
|
|
|
|
|
<div class="message-attachment-name">{reference.fileName}</div>
|
|
|
|
|
<div class="message-attachment-size">{reference.size}MB</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
2026-03-15 19:48:28 +00:00
|
|
|
</div>
|
2025-12-20 11:20:43 +00:00
|
|
|
</div>
|
2026-07-10 21:57:25 +00:00
|
|
|
{:else}
|
|
|
|
|
<div class="message-container {Role.Assistant}">
|
|
|
|
|
<div class="message-bubble {Role.Assistant}">
|
|
|
|
|
<div class="markdown-content">
|
|
|
|
|
<div use:messageRenderAction={message} class="streaming-content"></div>
|
|
|
|
|
</div>
|
2026-07-11 12:49:44 +00:00
|
|
|
{#if message.artifacts.length > 0}
|
|
|
|
|
{@const artifactTally = tallyArtifactsByAction(message.artifacts)}
|
|
|
|
|
<div class="artifacts-container" in:fade={{ duration: 300 }}>
|
|
|
|
|
<span class="artifacts-container-title">{message.artifacts.length} FILES CHANGED</span>
|
|
|
|
|
<div class="artifacts-tally">
|
|
|
|
|
{#each Object.values(ArtifactAction) as action}
|
|
|
|
|
{#if artifactTally[action]}
|
|
|
|
|
<div class="artifact-tally-container">
|
|
|
|
|
<span class="artifact-tally-ellipse artifact-{action}"></span>
|
|
|
|
|
<span class="artifact-tally-count">{artifactTally[action]}</span>
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="artifacts-list-container">
|
|
|
|
|
{#each message.artifacts as artifact}
|
|
|
|
|
<div class="artifact-card" aria-label="{artifact.filePath}">
|
|
|
|
|
<span class="artifact-ellipse artifact-{artifact.action}"></span>
|
|
|
|
|
<div
|
|
|
|
|
class="artifact-icon"
|
|
|
|
|
use:setElementIcon={artifact.getIconName()}
|
|
|
|
|
></div>
|
|
|
|
|
<span class="artifact-name">{basename(artifact.filePath)}</span>
|
|
|
|
|
<span class="artifact-action artifact-action-{artifact.action}">{artifactActionToCopy(artifact.action)}</span>
|
|
|
|
|
</div>
|
|
|
|
|
{/each}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
2025-12-23 12:04:29 +00:00
|
|
|
</div>
|
2025-12-20 11:20:43 +00:00
|
|
|
</div>
|
2026-07-10 21:57:25 +00:00
|
|
|
{/if}
|
|
|
|
|
{/if}
|
|
|
|
|
{/each}
|
|
|
|
|
|
|
|
|
|
{#if isLatestTurn}
|
|
|
|
|
<ThoughtIndicator thought={currentThought}/>
|
|
|
|
|
{#if isSubmitting}
|
|
|
|
|
<div transition:fade={{ duration: 300 }}>
|
|
|
|
|
<StreamingIndicator/>
|
2025-10-06 23:57:08 +00:00
|
|
|
</div>
|
2026-07-10 21:57:25 +00:00
|
|
|
{/if}
|
2025-12-20 11:20:43 +00:00
|
|
|
{/if}
|
2026-07-06 18:05:19 +00:00
|
|
|
</div>
|
2026-07-10 21:57:25 +00:00
|
|
|
{/each}
|
2025-12-04 23:04:20 +00:00
|
|
|
|
2025-12-20 11:20:43 +00:00
|
|
|
{#if messages.length === 0}
|
2026-07-06 13:22:51 +00:00
|
|
|
<div class="conversation-empty-container">
|
|
|
|
|
<div class="conversation-empty-animation">
|
|
|
|
|
<GraphAnimation/>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="conversation-empty-greeting">
|
|
|
|
|
<div class="conversation-empty-greeting-backdrop"></div>
|
|
|
|
|
<div class="conversation-empty-greeting-label">{getGreetingByTime()}</div>
|
|
|
|
|
</div>
|
2025-12-20 11:20:43 +00:00
|
|
|
</div>
|
|
|
|
|
{/if}
|
2026-05-25 18:42:30 +00:00
|
|
|
|
2025-12-20 11:20:43 +00:00
|
|
|
</div>
|
2026-05-25 18:42:30 +00:00
|
|
|
|
2025-12-20 11:20:43 +00:00
|
|
|
{#if messages.length > 0}
|
2025-12-22 20:02:02 +00:00
|
|
|
<div class="bottom-fade"></div>
|
2025-09-16 16:14:37 +00:00
|
|
|
{/if}
|
2026-05-25 18:42:30 +00:00
|
|
|
|
|
|
|
|
{#if !scrolledToBottom}
|
|
|
|
|
<div class="scroll-to-bottom-container" transition:fade>
|
|
|
|
|
<button
|
|
|
|
|
id="scroll-to-bottom-button"
|
|
|
|
|
bind:this={scrollToBottomButton}
|
2026-07-10 21:57:25 +00:00
|
|
|
on:click={() => scrollToBottom("smooth")}
|
2026-05-25 18:42:30 +00:00
|
|
|
aria-label="Scroll to bottom">
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
{/if}
|
2025-09-16 16:14:37 +00:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<style>
|
2026-05-25 18:42:30 +00:00
|
|
|
.scroll-to-bottom-container {
|
|
|
|
|
background-color: color-mix(in srgb, var(--background-primary) 70%, transparent);
|
|
|
|
|
border-radius: var(--radius-l);
|
|
|
|
|
padding: var(--size-4-2);
|
|
|
|
|
position: absolute;
|
|
|
|
|
bottom: var(--size-4-2);
|
|
|
|
|
right: 0px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#scroll-to-bottom-button {
|
|
|
|
|
background-color: var(--interactive-accent);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 11:20:43 +00:00
|
|
|
.chat-area-wrapper {
|
|
|
|
|
position: relative;
|
|
|
|
|
height: 100%;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-20 01:34:41 +00:00
|
|
|
.top-fade {
|
|
|
|
|
position: absolute;
|
2025-12-20 11:20:43 +00:00
|
|
|
top: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
2025-12-20 01:34:41 +00:00
|
|
|
height: var(--size-4-4);
|
|
|
|
|
background-image: linear-gradient(to bottom, var(--background-secondary), transparent);
|
2025-12-20 11:20:43 +00:00
|
|
|
z-index: 10;
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.bottom-fade {
|
|
|
|
|
position: absolute;
|
|
|
|
|
bottom: 0;
|
|
|
|
|
left: 0;
|
|
|
|
|
right: 0;
|
|
|
|
|
height: var(--size-4-4);
|
|
|
|
|
background-image: linear-gradient(to top, var(--background-secondary), transparent);
|
|
|
|
|
z-index: 10;
|
|
|
|
|
pointer-events: none;
|
2025-12-20 01:34:41 +00:00
|
|
|
}
|
|
|
|
|
|
2025-09-16 16:14:37 +00:00
|
|
|
.chat-area {
|
2025-12-20 11:20:43 +00:00
|
|
|
position: relative;
|
2025-09-16 16:14:37 +00:00
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
height: 100%;
|
2025-10-01 21:12:39 +00:00
|
|
|
overflow: auto;
|
2025-12-20 01:34:41 +00:00
|
|
|
padding: var(--size-4-4) var(--size-4-3) var(--size-4-3) var(--size-4-3);
|
2025-10-01 21:12:39 +00:00
|
|
|
gap: var(--size-4-2);
|
|
|
|
|
scroll-behavior: smooth;
|
2025-09-16 16:14:37 +00:00
|
|
|
}
|
2025-09-28 23:09:10 +00:00
|
|
|
|
|
|
|
|
.chat-area::-webkit-scrollbar {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
2025-09-29 10:09:22 +00:00
|
|
|
|
2026-07-10 21:57:25 +00:00
|
|
|
.message-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
gap: var(--size-4-2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-group.latest {
|
2026-07-11 12:49:44 +00:00
|
|
|
margin-top: var(--size-4-2);
|
2026-07-10 21:57:25 +00:00
|
|
|
min-height: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-16 16:14:37 +00:00
|
|
|
.message-container {
|
|
|
|
|
display: flex;
|
2025-11-04 22:51:22 +00:00
|
|
|
text-align: left;
|
2025-10-01 21:12:39 +00:00
|
|
|
margin: 0;
|
2025-09-16 16:14:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-container.user {
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-container.assistant {
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
}
|
2026-05-25 17:37:31 +00:00
|
|
|
|
|
|
|
|
.message-container {
|
2026-05-25 18:42:30 +00:00
|
|
|
animation: fadeIn 0.5s ease-out forwards;
|
2026-05-25 17:37:31 +00:00
|
|
|
}
|
|
|
|
|
|
2026-05-25 18:42:30 +00:00
|
|
|
@keyframes fadeIn {
|
2026-05-25 17:37:31 +00:00
|
|
|
from {
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transform: translateY(5px);
|
|
|
|
|
}
|
|
|
|
|
to {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-16 16:14:37 +00:00
|
|
|
|
|
|
|
|
.message-bubble {
|
|
|
|
|
word-wrap: break-word;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-bubble.user {
|
|
|
|
|
word-wrap: break-word;
|
|
|
|
|
max-width: 70%;
|
|
|
|
|
border: var(--border-width) solid var(--background-modifier-border);
|
|
|
|
|
border-radius: var(--radius-m);
|
|
|
|
|
padding: 0px var(--size-4-2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-bubble.assistant {
|
|
|
|
|
word-wrap: break-word;
|
2026-07-11 12:49:44 +00:00
|
|
|
width: 100%;
|
2025-09-16 16:14:37 +00:00
|
|
|
}
|
2025-10-01 21:12:39 +00:00
|
|
|
|
2026-03-15 19:48:28 +00:00
|
|
|
.message-text-user-container {
|
2026-03-15 12:21:41 +00:00
|
|
|
max-height: 15vh;
|
|
|
|
|
overflow: scroll;
|
|
|
|
|
padding-top: var(--size-4-2);
|
2025-12-23 12:04:29 +00:00
|
|
|
white-space: pre-wrap;
|
2025-10-01 21:12:39 +00:00
|
|
|
}
|
2026-03-15 12:21:41 +00:00
|
|
|
|
2026-03-15 19:48:28 +00:00
|
|
|
.message-text-user-container::-webkit-scrollbar {
|
2026-03-15 12:21:41 +00:00
|
|
|
display: none;
|
|
|
|
|
}
|
2026-03-15 19:48:28 +00:00
|
|
|
|
|
|
|
|
.message-text-user-container {
|
|
|
|
|
padding-bottom: var(--size-4-2);
|
|
|
|
|
}
|
2026-07-11 12:49:44 +00:00
|
|
|
|
|
|
|
|
.artifacts-container {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-rows: auto auto;
|
|
|
|
|
grid-template-columns: auto auto;
|
|
|
|
|
width: 100%;
|
|
|
|
|
margin-top: var(--size-4-3);
|
|
|
|
|
gap: var(--size-4-4);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifacts-container-title {
|
|
|
|
|
grid-row: 1;
|
|
|
|
|
grid-column: 1;
|
|
|
|
|
font-size: var(--font-smallest);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifacts-tally {
|
|
|
|
|
grid-row: 1;
|
|
|
|
|
grid-column: 2;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-tally-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
margin-left: auto;
|
|
|
|
|
align-items: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-tally-ellipse {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
width: 6px;
|
|
|
|
|
height: 6px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
margin-left: var(--size-4-2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-create {
|
|
|
|
|
background: var(--color-green);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-modify {
|
|
|
|
|
background: var(--color-blue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-delete {
|
|
|
|
|
background: var(--color-red);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-tally-count {
|
|
|
|
|
font-size: var(--font-smallest);
|
|
|
|
|
margin-left: var(--size-4-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifacts-list-container {
|
|
|
|
|
grid-row: 2;
|
|
|
|
|
grid-column: 1 / 3;
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
overflow: scroll;
|
|
|
|
|
width: 100%;
|
|
|
|
|
max-height: 200px;
|
|
|
|
|
gap: var(--size-4-1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifacts-list-container::-webkit-scrollbar {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-card {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-rows: auto;
|
|
|
|
|
grid-template-columns: auto auto 1fr auto;
|
|
|
|
|
gap: var(--size-4-3);
|
|
|
|
|
align-items: center;
|
|
|
|
|
height: 30px;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
background-color: var(--background-secondary-alt);
|
|
|
|
|
border-radius: var(--size-4-2);
|
|
|
|
|
padding: 0 var(--size-4-1) 0 var(--size-4-2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-ellipse {
|
|
|
|
|
grid-row: 1;
|
|
|
|
|
grid-column: 1;
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
width: 10px;
|
|
|
|
|
height: 10px;
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
align-self: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-ellipse-create {
|
|
|
|
|
background: var(--color-green);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-ellipse-modify {
|
|
|
|
|
background: var(--color-blue);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-ellipse-delete {
|
|
|
|
|
background: var(--color-red);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-icon {
|
|
|
|
|
grid-row: 1;
|
|
|
|
|
grid-column: 2;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-name {
|
|
|
|
|
grid-row: 1;
|
|
|
|
|
grid-column: 3;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: start;
|
|
|
|
|
font-size: var(--font-smaller);
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-action {
|
|
|
|
|
grid-row: 1;
|
|
|
|
|
grid-column: 4;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
font-size: var(--font-smallest);
|
|
|
|
|
font-weight: var(--font-semibold);
|
|
|
|
|
border-radius: var(--size-4-2);
|
|
|
|
|
padding: var(--size-2-1) var(--size-4-2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-action-create {
|
|
|
|
|
background-color: color-mix(
|
|
|
|
|
in srgb,
|
|
|
|
|
var(--color-green) 25%,
|
|
|
|
|
black 20%
|
|
|
|
|
);
|
|
|
|
|
color: color-mix(
|
|
|
|
|
in srgb,
|
|
|
|
|
var(--color-green) 100%,
|
|
|
|
|
white 10%
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-action-modify {
|
|
|
|
|
background-color: color-mix(
|
|
|
|
|
in srgb,
|
|
|
|
|
var(--color-blue) 25%,
|
|
|
|
|
black 20%
|
|
|
|
|
);
|
|
|
|
|
color: color-mix(
|
|
|
|
|
in srgb,
|
|
|
|
|
var(--color-blue) 100%,
|
|
|
|
|
white 10%
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.artifact-action-delete {
|
|
|
|
|
background-color: color-mix(
|
|
|
|
|
in srgb,
|
|
|
|
|
var(--color-red) 25%,
|
|
|
|
|
black 20%
|
|
|
|
|
);
|
|
|
|
|
color: color-mix(
|
|
|
|
|
in srgb,
|
|
|
|
|
var(--color-red) 100%,
|
|
|
|
|
white 10%
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-06 13:22:51 +00:00
|
|
|
.conversation-empty-container {
|
|
|
|
|
display: grid;
|
|
|
|
|
height: 100%;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.conversation-empty-animation {
|
|
|
|
|
grid-row: 1;
|
|
|
|
|
grid-column: 1;
|
|
|
|
|
height: 100%;
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.conversation-empty-greeting-backdrop {
|
|
|
|
|
position: absolute;
|
|
|
|
|
inset: -48px;
|
|
|
|
|
background: radial-gradient(
|
|
|
|
|
ellipse closest-side,
|
|
|
|
|
var(--background-secondary) 0%,
|
|
|
|
|
color-mix(in srgb, var(--background-secondary) 80%, transparent) 65%,
|
|
|
|
|
transparent 100%
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.conversation-empty-greeting {
|
|
|
|
|
grid-row: 1;
|
|
|
|
|
grid-column: 1;
|
|
|
|
|
position: relative;
|
2025-10-05 12:06:05 +00:00
|
|
|
margin: auto;
|
2025-09-16 16:14:37 +00:00
|
|
|
font-style: italic;
|
2025-10-08 21:06:25 +00:00
|
|
|
font-size: var(--font-ui-medium);
|
2025-09-16 16:14:37 +00:00
|
|
|
color: var(--text-muted);
|
|
|
|
|
pointer-events: none;
|
|
|
|
|
}
|
2025-09-26 19:48:05 +00:00
|
|
|
|
2026-07-06 13:22:51 +00:00
|
|
|
.conversation-empty-greeting-label {
|
|
|
|
|
position: relative;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
padding: var(--size-2-2);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-27 12:23:28 +00:00
|
|
|
.streaming-content {
|
2025-11-04 22:51:22 +00:00
|
|
|
justify-content: left;
|
2025-09-27 12:23:28 +00:00
|
|
|
min-height: 1em; /* Ensure the element exists for binding */
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-02 11:49:40 +00:00
|
|
|
/* Streaming message styles */
|
2025-12-23 12:04:29 +00:00
|
|
|
.content-fade-in {
|
2025-10-02 11:49:40 +00:00
|
|
|
animation: reveal-fade 0.5s ease-in-out forwards;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@keyframes reveal-fade {
|
|
|
|
|
0% {
|
|
|
|
|
opacity: 0;
|
|
|
|
|
transform: translateY(10px);
|
|
|
|
|
}
|
|
|
|
|
100% {
|
|
|
|
|
opacity: 1;
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-23 12:04:29 +00:00
|
|
|
|
|
|
|
|
/* Message attachments styles */
|
2025-12-23 20:18:27 +00:00
|
|
|
.message-attachment-break {
|
|
|
|
|
color: var(--background-secondary-alt);
|
|
|
|
|
margin: 0 0 var(--size-4-2) 0;
|
|
|
|
|
opacity: 0.5;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-23 12:04:29 +00:00
|
|
|
.message-attachments-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
overflow-x: auto;
|
|
|
|
|
overflow-y: hidden;
|
|
|
|
|
scroll-behavior: smooth;
|
2025-12-23 20:18:27 +00:00
|
|
|
gap: var(--size-4-2);
|
2025-12-23 12:04:29 +00:00
|
|
|
margin-bottom: var(--size-4-2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-attachments-container::-webkit-scrollbar {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-attachmanet {
|
|
|
|
|
display: grid;
|
2025-12-23 20:18:27 +00:00
|
|
|
grid-template-rows: var(--size-4-2) auto var(--size-4-2);
|
|
|
|
|
grid-template-columns: var(--size-4-2) auto var(--size-4-2) auto var(--size-4-2);
|
|
|
|
|
background-color: var(--background-secondary-alt);
|
2025-12-23 12:04:29 +00:00
|
|
|
border: var(--border-width) solid var(--background-modifier-border);
|
|
|
|
|
border-radius: var(--radius-m);
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-attachment-icon {
|
|
|
|
|
grid-row: 2;
|
|
|
|
|
grid-column: 2;
|
2025-12-23 20:18:27 +00:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
2025-12-23 12:04:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-attachment-info {
|
|
|
|
|
grid-row: 2;
|
|
|
|
|
grid-column: 4;
|
|
|
|
|
min-width: 40px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-attachment-name {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
width: 100%;
|
|
|
|
|
padding: 0;
|
|
|
|
|
font-size: var(--font-smaller);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.message-attachment-size {
|
|
|
|
|
padding: 0;
|
|
|
|
|
font-size: var(--font-smallest);
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
}
|
2025-09-16 16:14:37 +00:00
|
|
|
</style>
|