blob: 971048985366ae9c61a90ccecd311bb43e4f4a3a [file] [log] [blame]
bankseane59a2e12025-06-28 01:38:19 +00001/**
2 * Demo module for sketch-timeline component
3 */
4
5import { DemoModule } from "./demo-framework/types";
6import { demoUtils } from "./demo-fixtures/index";
7import type { AgentMessage } from "../../types";
8
9// Mock messages for demo
10function createMockMessage(props: Partial<AgentMessage> = {}): AgentMessage {
11 return {
12 idx: props.idx || 0,
13 type: props.type || "agent",
14 content: props.content || "Hello world",
15 timestamp: props.timestamp || "2023-05-15T12:00:00Z",
16 elapsed: props.elapsed || 1500000000, // 1.5 seconds in nanoseconds
17 end_of_turn: props.end_of_turn || false,
18 conversation_id: props.conversation_id || "conv123",
19 tool_calls: props.tool_calls || [],
20 commits: props.commits || undefined,
21 usage: props.usage,
22 hide_output: props.hide_output || false,
23 ...props,
24 };
25}
26
27function createMockMessages(count: number): AgentMessage[] {
28 return Array.from({ length: count }, (_, i) =>
29 createMockMessage({
30 idx: i,
31 content: `Message ${i + 1}: This is a sample message to demonstrate the timeline component.`,
32 type: i % 3 === 0 ? "user" : "agent",
33 timestamp: new Date(Date.now() - (count - i) * 60000).toISOString(),
34 tool_calls:
35 i % 4 === 0
36 ? [
37 {
38 name: "bash",
39 input: `echo "Tool call example ${i}"`,
40 tool_call_id: `call_${i}`,
philip.zeyliger26bc6592025-06-30 20:15:30 -070041 args: `{"command": "echo 'Tool call example ${i}'"}`,
bankseane59a2e12025-06-28 01:38:19 +000042 result: `Tool call example ${i}`,
43 },
44 ]
45 : [],
46 usage:
47 i % 5 === 0
48 ? {
49 input_tokens: 10 + i,
50 cache_creation_input_tokens: 0,
51 cache_read_input_tokens: 0,
52 output_tokens: 50 + i * 2,
53 cost_usd: 0.001 * (i + 1),
54 }
55 : undefined,
56 }),
57 );
58}
59
60const demo: DemoModule = {
61 title: "Timeline Demo",
62 description:
63 "Interactive timeline component for displaying conversation messages with various states",
64 imports: ["../sketch-timeline"],
65 styles: ["/dist/tailwind.css"],
66
67 setup: async (container: HTMLElement) => {
68 // Create demo sections
69 const basicSection = demoUtils.createDemoSection(
70 "Basic Timeline",
71 "Timeline with a few sample messages",
72 );
73
74 const statesSection = demoUtils.createDemoSection(
75 "Timeline States",
76 "Different loading and thinking states",
77 );
78
79 const interactiveSection = demoUtils.createDemoSection(
80 "Interactive Demo",
81 "Add messages and control timeline behavior",
82 );
83
84 // Basic timeline with sample messages
85 const basicMessages = createMockMessages(5);
86 const basicTimeline = document.createElement("sketch-timeline") as any;
87 basicTimeline.messages = basicMessages;
88 basicTimeline.style.cssText =
89 "height: 400px; border: 1px solid #e1e5e9; border-radius: 6px; margin: 10px 0;";
90
91 // Create a scroll container for the basic timeline
92 const basicScrollContainer = document.createElement("div");
93 basicScrollContainer.style.cssText = "height: 400px; overflow-y: auto;";
94 basicScrollContainer.appendChild(basicTimeline);
95 basicTimeline.scrollContainer = { value: basicScrollContainer };
96
97 basicSection.appendChild(basicScrollContainer);
98
99 // Timeline with loading state
100 const loadingTimeline = document.createElement("sketch-timeline") as any;
101 loadingTimeline.messages = [];
102 loadingTimeline.isLoadingOlderMessages = false;
103 loadingTimeline.style.cssText =
104 "height: 200px; border: 1px solid #e1e5e9; border-radius: 6px; margin: 10px 0;";
105
106 const loadingWrapper = document.createElement("div");
107 loadingWrapper.style.cssText = "margin: 15px 0;";
108
109 const loadingLabel = document.createElement("h4");
110 loadingLabel.textContent = "Loading State (No messages)";
111 loadingLabel.style.cssText =
banksean1ee0bc62025-07-22 23:24:18 +0000112 "margin: 0 0 10px 0; color: var(--demo-label-color); font-size: 14px; font-weight: 600;";
bankseane59a2e12025-06-28 01:38:19 +0000113
114 loadingWrapper.appendChild(loadingLabel);
115 loadingWrapper.appendChild(loadingTimeline);
116 statesSection.appendChild(loadingWrapper);
117
118 // Timeline with thinking state
119 const thinkingMessages = createMockMessages(3);
120 const thinkingTimeline = document.createElement("sketch-timeline") as any;
121 thinkingTimeline.messages = thinkingMessages;
122 thinkingTimeline.llmCalls = 2;
123 thinkingTimeline.toolCalls = ["bash", "patch"];
124 thinkingTimeline.agentState = "thinking";
125 thinkingTimeline.style.cssText =
126 "height: 300px; border: 1px solid #e1e5e9; border-radius: 6px; margin: 10px 0;";
127
128 // Set initial load complete for thinking timeline
129 setTimeout(() => {
130 (thinkingTimeline as any).isInitialLoadComplete = true;
131 thinkingTimeline.requestUpdate();
132 }, 100);
133
134 const thinkingWrapper = document.createElement("div");
135 thinkingWrapper.style.cssText = "margin: 15px 0;";
136
137 const thinkingLabel = document.createElement("h4");
138 thinkingLabel.textContent = "Thinking State (Agent is active)";
139 thinkingLabel.style.cssText =
banksean1ee0bc62025-07-22 23:24:18 +0000140 "margin: 0 0 10px 0; color: var(--demo-label-color); font-size: 14px; font-weight: 600;";
bankseane59a2e12025-06-28 01:38:19 +0000141
142 const thinkingScrollContainer = document.createElement("div");
143 thinkingScrollContainer.style.cssText = "height: 300px; overflow-y: auto;";
144 thinkingScrollContainer.appendChild(thinkingTimeline);
145 thinkingTimeline.scrollContainer = { value: thinkingScrollContainer };
146
147 thinkingWrapper.appendChild(thinkingLabel);
148 thinkingWrapper.appendChild(thinkingScrollContainer);
149 statesSection.appendChild(thinkingWrapper);
150
151 // Interactive timeline
152 const interactiveMessages = createMockMessages(8);
153 const interactiveTimeline = document.createElement(
154 "sketch-timeline",
155 ) as any;
156 interactiveTimeline.messages = interactiveMessages;
157 interactiveTimeline.style.cssText =
158 "height: 400px; border: 1px solid #e1e5e9; border-radius: 6px; margin: 10px 0;";
159
160 // Set initial load complete for interactive timeline
161 setTimeout(() => {
162 (interactiveTimeline as any).isInitialLoadComplete = true;
163 interactiveTimeline.requestUpdate();
164 }, 100);
165
166 const interactiveScrollContainer = document.createElement("div");
167 interactiveScrollContainer.style.cssText =
168 "height: 400px; overflow-y: auto;";
169 interactiveScrollContainer.appendChild(interactiveTimeline);
170 interactiveTimeline.scrollContainer = { value: interactiveScrollContainer };
171
172 // Control buttons for interactive demo
173 const controlsDiv = document.createElement("div");
174 controlsDiv.style.cssText =
175 "margin-top: 20px; display: flex; flex-wrap: wrap; gap: 10px;";
176
177 const addMessageButton = demoUtils.createButton("Add User Message", () => {
178 const newMessage = createMockMessage({
179 idx: interactiveMessages.length,
180 content: `New user message added at ${new Date().toLocaleTimeString()}`,
181 type: "user",
182 timestamp: new Date().toISOString(),
183 });
184 interactiveMessages.push(newMessage);
185 interactiveTimeline.messages = [...interactiveMessages];
186 });
187
188 const addAgentMessageButton = demoUtils.createButton(
189 "Add Agent Message",
190 () => {
191 const newMessage = createMockMessage({
192 idx: interactiveMessages.length,
193 content: `New agent response added at ${new Date().toLocaleTimeString()}`,
194 type: "agent",
195 timestamp: new Date().toISOString(),
196 tool_calls:
197 Math.random() > 0.5
198 ? [
199 {
200 name: "bash",
201 input: "date",
202 tool_call_id: `call_${Date.now()}`,
203 args: '{"command": "date"}',
204 result: new Date().toString(),
205 },
206 ]
207 : [],
208 });
209 interactiveMessages.push(newMessage);
210 interactiveTimeline.messages = [...interactiveMessages];
211 },
212 );
213
214 const toggleThinkingButton = demoUtils.createButton(
215 "Toggle Thinking",
216 () => {
217 if (
218 interactiveTimeline.llmCalls > 0 ||
219 interactiveTimeline.toolCalls.length > 0
220 ) {
221 interactiveTimeline.llmCalls = 0;
222 interactiveTimeline.toolCalls = [];
223 } else {
224 interactiveTimeline.llmCalls = 1;
225 interactiveTimeline.toolCalls = ["bash"];
226 }
227 },
228 );
229
230 const toggleCompactButton = demoUtils.createButton(
231 "Toggle Compact Padding",
232 () => {
233 interactiveTimeline.compactPadding =
234 !interactiveTimeline.compactPadding;
235 },
236 );
237
238 const clearMessagesButton = demoUtils.createButton("Clear Messages", () => {
239 interactiveMessages.length = 0;
240 interactiveTimeline.messages = [];
241 });
242
243 const resetDemoButton = demoUtils.createButton("Reset Demo", () => {
244 interactiveMessages.length = 0;
245 interactiveMessages.push(...createMockMessages(8));
246 interactiveTimeline.messages = [...interactiveMessages];
247 interactiveTimeline.llmCalls = 0;
248 interactiveTimeline.toolCalls = [];
249 interactiveTimeline.compactPadding = false;
250 });
251
252 controlsDiv.appendChild(addMessageButton);
253 controlsDiv.appendChild(addAgentMessageButton);
254 controlsDiv.appendChild(toggleThinkingButton);
255 controlsDiv.appendChild(toggleCompactButton);
256 controlsDiv.appendChild(clearMessagesButton);
257 controlsDiv.appendChild(resetDemoButton);
258
259 const interactiveWrapper = document.createElement("div");
260 interactiveWrapper.style.cssText =
261 "padding: 10px; border: 1px solid #e1e5e9; border-radius: 6px; background: white;";
262 interactiveWrapper.appendChild(interactiveScrollContainer);
263 interactiveWrapper.appendChild(controlsDiv);
264 interactiveSection.appendChild(interactiveWrapper);
265
266 // Set initial load complete for basic timeline
267 setTimeout(() => {
268 (basicTimeline as any).isInitialLoadComplete = true;
269 basicTimeline.requestUpdate();
270 }, 100);
271
272 // Assemble the demo
273 container.appendChild(basicSection);
274 container.appendChild(statesSection);
275 container.appendChild(interactiveSection);
276
277 // Store references for cleanup
278 (container as any).timelines = [
279 basicTimeline,
280 loadingTimeline,
281 thinkingTimeline,
282 interactiveTimeline,
283 ];
284 },
285
286 cleanup: async () => {
287 // Clean up any timers or listeners if needed
288 const container = document.getElementById("demo-container");
289 if (container && (container as any).timelines) {
290 const timelines = (container as any).timelines;
291 timelines.forEach((timeline: any) => {
292 // Reset timeline state
293 timeline.llmCalls = 0;
294 timeline.toolCalls = [];
295 timeline.messages = [];
296 });
297 delete (container as any).timelines;
298 }
299 },
300};
301
302export default demo;