1<script lang="ts">
2 import { ChatMessage } from '$lib/components/app';
3 import { chatStore } from '$lib/stores/chat.svelte';
4 import { conversationsStore, activeConversation } from '$lib/stores/conversations.svelte';
5 import { config } from '$lib/stores/settings.svelte';
6 import { getMessageSiblings } from '$lib/utils';
7
8 interface Props {
9 class?: string;
10 messages?: DatabaseMessage[];
11 onUserAction?: () => void;
12 }
13
14 let { class: className, messages = [], onUserAction }: Props = $props();
15
16 let allConversationMessages = $state<DatabaseMessage[]>([]);
17 const currentConfig = config();
18
19 function refreshAllMessages() {
20 const conversation = activeConversation();
21
22 if (conversation) {
23 conversationsStore.getConversationMessages(conversation.id).then((messages) => {
24 allConversationMessages = messages;
25 });
26 } else {
27 allConversationMessages = [];
28 }
29 }
30
31 // Single effect that tracks both conversation and message changes
32 $effect(() => {
33 const conversation = activeConversation();
34
35 if (conversation) {
36 refreshAllMessages();
37 }
38 });
39
40 let displayMessages = $derived.by(() => {
41 if (!messages.length) {
42 return [];
43 }
44
45 // Filter out system messages if showSystemMessage is false
46 const filteredMessages = currentConfig.showSystemMessage
47 ? messages
48 : messages.filter((msg) => msg.type !== 'system');
49
50 return filteredMessages.map((message) => {
51 const siblingInfo = getMessageSiblings(allConversationMessages, message.id);
52
53 return {
54 message,
55 siblingInfo: siblingInfo || {
56 message,
57 siblingIds: [message.id],
58 currentIndex: 0,
59 totalSiblings: 1
60 }
61 };
62 });
63 });
64
65 async function handleNavigateToSibling(siblingId: string) {
66 await conversationsStore.navigateToSibling(siblingId);
67 }
68
69 async function handleEditWithBranching(
70 message: DatabaseMessage,
71 newContent: string,
72 newExtras?: DatabaseMessageExtra[]
73 ) {
74 onUserAction?.();
75
76 await chatStore.editMessageWithBranching(message.id, newContent, newExtras);
77
78 refreshAllMessages();
79 }
80
81 async function handleEditWithReplacement(
82 message: DatabaseMessage,
83 newContent: string,
84 shouldBranch: boolean
85 ) {
86 onUserAction?.();
87
88 await chatStore.editAssistantMessage(message.id, newContent, shouldBranch);
89
90 refreshAllMessages();
91 }
92
93 async function handleRegenerateWithBranching(message: DatabaseMessage, modelOverride?: string) {
94 onUserAction?.();
95
96 await chatStore.regenerateMessageWithBranching(message.id, modelOverride);
97
98 refreshAllMessages();
99 }
100
101 async function handleContinueAssistantMessage(message: DatabaseMessage) {
102 onUserAction?.();
103
104 await chatStore.continueAssistantMessage(message.id);
105
106 refreshAllMessages();
107 }
108
109 async function handleEditUserMessagePreserveResponses(
110 message: DatabaseMessage,
111 newContent: string,
112 newExtras?: DatabaseMessageExtra[]
113 ) {
114 onUserAction?.();
115
116 await chatStore.editUserMessagePreserveResponses(message.id, newContent, newExtras);
117
118 refreshAllMessages();
119 }
120
121 async function handleDeleteMessage(message: DatabaseMessage) {
122 await chatStore.deleteMessage(message.id);
123
124 refreshAllMessages();
125 }
126</script>
127
128<div class="flex h-full flex-col space-y-10 pt-16 md:pt-24 {className}" style="height: auto; ">
129 {#each displayMessages as { message, siblingInfo } (message.id)}
130 <ChatMessage
131 class="mx-auto w-full max-w-[48rem]"
132 {message}
133 {siblingInfo}
134 onDelete={handleDeleteMessage}
135 onNavigateToSibling={handleNavigateToSibling}
136 onEditWithBranching={handleEditWithBranching}
137 onEditWithReplacement={handleEditWithReplacement}
138 onEditUserMessagePreserveResponses={handleEditUserMessagePreserveResponses}
139 onRegenerateWithBranching={handleRegenerateWithBranching}
140 onContinueAssistantMessage={handleContinueAssistantMessage}
141 />
142 {/each}
143</div>