1<script module lang="ts">
2 import { defineMeta } from '@storybook/addon-svelte-csf';
3 import ChatMessage from '$lib/components/app/chat/ChatMessages/ChatMessage.svelte';
4
5 const { Story } = defineMeta({
6 title: 'Components/ChatScreen/ChatMessage',
7 component: ChatMessage,
8 parameters: {
9 layout: 'centered'
10 }
11 });
12
13 // Mock messages for different scenarios
14 const userMessage: DatabaseMessage = {
15 id: '1',
16 convId: 'conv-1',
17 type: 'message',
18 timestamp: Date.now() - 1000 * 60 * 5,
19 role: 'user',
20 content: 'What is the meaning of life, the universe, and everything?',
21 parent: '',
22 thinking: '',
23 children: []
24 };
25
26 const assistantMessage: DatabaseMessage = {
27 id: '2',
28 convId: 'conv-1',
29 type: 'message',
30 timestamp: Date.now() - 1000 * 60 * 3,
31 role: 'assistant',
32 content:
33 'The answer to the ultimate question of life, the universe, and everything is **42**.\n\nThis comes from Douglas Adams\' "The Hitchhiker\'s Guide to the Galaxy," where a supercomputer named Deep Thought calculated this answer over 7.5 million years. However, the question itself was never properly formulated, which is why the answer seems meaningless without context.',
34 parent: '1',
35 thinking: '',
36 children: []
37 };
38
39 const assistantWithReasoning: DatabaseMessage = {
40 id: '3',
41 convId: 'conv-1',
42 type: 'message',
43 timestamp: Date.now() - 1000 * 60 * 2,
44 role: 'assistant',
45 content: "Here's the concise answer, now that I've thought it through carefully for you.",
46 parent: '1',
47 thinking:
48 "Let's consider the user's question step by step:\\n\\n1. Identify the core problem\\n2. Evaluate relevant information\\n3. Formulate a clear answer\\n\\nFollowing this process ensures the final response stays focused and accurate.",
49 children: []
50 };
51 const rawOutputMessage: DatabaseMessage = {
52 id: '6',
53 convId: 'conv-1',
54 type: 'message',
55 timestamp: Date.now() - 1000 * 60,
56 role: 'assistant',
57 content:
58 '<|channel|>analysis<|message|>User greeted me. Initiating overcomplicated analysis: Is this a trap? No, just a normal hello. Respond calmly, act like a helpful assistant, and do not start explaining quantum physics again. Confidence 0.73. Engaging socially acceptable greeting protocol...<|end|>Hello there! How can I help you today?',
59 parent: '1',
60 thinking: '',
61 children: []
62 };
63
64 let processingMessage = $state({
65 id: '4',
66 convId: 'conv-1',
67 type: 'message',
68 timestamp: 0, // No timestamp = processing
69 role: 'assistant',
70 content: '',
71 parent: '1',
72 thinking: '',
73 children: []
74 });
75
76 let streamingMessage = $state({
77 id: '5',
78 convId: 'conv-1',
79 type: 'message',
80 timestamp: 0, // No timestamp = streaming
81 role: 'assistant',
82 content: '',
83 parent: '1',
84 thinking: '',
85 children: []
86 });
87</script>
88
89<Story
90 name="User"
91 args={{
92 message: userMessage
93 }}
94 play={async () => {
95 const { settingsStore } = await import('$lib/stores/settings.svelte');
96 settingsStore.updateConfig('disableReasoningFormat', false);
97 }}
98/>
99
100<Story
101 name="Assistant"
102 args={{
103 class: 'max-w-[56rem] w-[calc(100vw-2rem)]',
104 message: assistantMessage
105 }}
106 play={async () => {
107 const { settingsStore } = await import('$lib/stores/settings.svelte');
108 settingsStore.updateConfig('disableReasoningFormat', false);
109 }}
110/>
111
112<Story
113 name="AssistantWithReasoning"
114 args={{
115 class: 'max-w-[56rem] w-[calc(100vw-2rem)]',
116 message: assistantWithReasoning
117 }}
118 play={async () => {
119 const { settingsStore } = await import('$lib/stores/settings.svelte');
120 settingsStore.updateConfig('disableReasoningFormat', false);
121 }}
122/>
123
124<Story
125 name="RawLlmOutput"
126 args={{
127 class: 'max-w-[56rem] w-[calc(100vw-2rem)]',
128 message: rawOutputMessage
129 }}
130 play={async () => {
131 const { settingsStore } = await import('$lib/stores/settings.svelte');
132 settingsStore.updateConfig('disableReasoningFormat', true);
133 }}
134/>
135
136<Story
137 name="WithReasoningContent"
138 args={{
139 message: streamingMessage
140 }}
141 asChild
142 play={async () => {
143 const { settingsStore } = await import('$lib/stores/settings.svelte');
144 settingsStore.updateConfig('disableReasoningFormat', false);
145 // Phase 1: Stream reasoning content in chunks
146 let reasoningText =
147 'I need to think about this carefully. Let me break down the problem:\n\n1. The user is asking for help with something complex\n2. I should provide a thorough and helpful response\n3. I need to consider multiple approaches\n4. The best solution would be to explain step by step\n\nThis approach will ensure clarity and understanding.';
148
149 let reasoningChunk = 'I';
150 let i = 0;
151 while (i < reasoningText.length) {
152 const chunkSize = Math.floor(Math.random() * 5) + 3; // Random 3-7 characters
153 const chunk = reasoningText.slice(i, i + chunkSize);
154 reasoningChunk += chunk;
155
156 // Update the reactive state directly
157 streamingMessage.thinking = reasoningChunk;
158
159 i += chunkSize;
160 await new Promise((resolve) => setTimeout(resolve, 50));
161 }
162
163 const regularText =
164 "Based on my analysis, here's the solution:\n\n**Step 1:** First, we need to understand the requirements clearly.\n\n**Step 2:** Then we can implement the solution systematically.\n\n**Step 3:** Finally, we test and validate the results.\n\nThis approach ensures we cover all aspects of the problem effectively.";
165
166 let contentChunk = '';
167 i = 0;
168
169 while (i < regularText.length) {
170 const chunkSize = Math.floor(Math.random() * 5) + 3; // Random 3-7 characters
171 const chunk = regularText.slice(i, i + chunkSize);
172 contentChunk += chunk;
173
174 // Update the reactive state directly
175 streamingMessage.content = contentChunk;
176
177 i += chunkSize;
178 await new Promise((resolve) => setTimeout(resolve, 50));
179 }
180
181 streamingMessage.timestamp = Date.now();
182 }}
183>
184 <div class="w-[56rem]">
185 <ChatMessage message={streamingMessage} />
186 </div>
187</Story>
188
189<Story
190 name="Processing"
191 args={{
192 message: processingMessage
193 }}
194 play={async () => {
195 const { settingsStore } = await import('$lib/stores/settings.svelte');
196 settingsStore.updateConfig('disableReasoningFormat', false);
197 // Import the chat store to simulate loading state
198 const { chatStore } = await import('$lib/stores/chat.svelte');
199
200 // Set loading state to true to trigger the processing UI
201 chatStore.isLoading = true;
202
203 // Simulate the processing state hook behavior
204 // This will show the "Generating..." text and parameter details
205 await new Promise((resolve) => setTimeout(resolve, 100));
206 }}
207/>