@tanstack/ai-devtools-core 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/README.md +1 -0
  2. package/dist/esm/components/ConversationDetails.js +74 -1
  3. package/dist/esm/components/ConversationDetails.js.map +1 -1
  4. package/dist/esm/components/conversation/ActivityEventsTab.d.ts +8 -0
  5. package/dist/esm/components/conversation/ActivityEventsTab.js +72 -0
  6. package/dist/esm/components/conversation/ActivityEventsTab.js.map +1 -0
  7. package/dist/esm/components/conversation/ConversationHeader.js +109 -66
  8. package/dist/esm/components/conversation/ConversationHeader.js.map +1 -1
  9. package/dist/esm/components/conversation/ConversationTabs.d.ts +1 -1
  10. package/dist/esm/components/conversation/ConversationTabs.js +107 -3
  11. package/dist/esm/components/conversation/ConversationTabs.js.map +1 -1
  12. package/dist/esm/components/conversation/MessageCard.js +83 -28
  13. package/dist/esm/components/conversation/MessageCard.js.map +1 -1
  14. package/dist/esm/components/conversation/index.d.ts +1 -0
  15. package/dist/esm/store/ai-context.d.ts +19 -2
  16. package/dist/esm/store/ai-context.js +528 -262
  17. package/dist/esm/store/ai-context.js.map +1 -1
  18. package/dist/esm/styles/use-styles.d.ts +7 -0
  19. package/dist/esm/styles/use-styles.js +65 -1
  20. package/dist/esm/styles/use-styles.js.map +1 -1
  21. package/package.json +2 -2
  22. package/src/components/ConversationDetails.tsx +70 -1
  23. package/src/components/conversation/ActivityEventsTab.tsx +57 -0
  24. package/src/components/conversation/ConversationHeader.tsx +52 -36
  25. package/src/components/conversation/ConversationTabs.tsx +121 -2
  26. package/src/components/conversation/MessageCard.tsx +45 -10
  27. package/src/components/conversation/index.ts +1 -0
  28. package/src/store/ai-context.tsx +783 -418
  29. package/src/styles/use-styles.ts +65 -1
@@ -1,27 +1,79 @@
1
1
  import { template, insert, createComponent, effect, className, memo, delegateEvents } from "solid-js/web";
2
- import { Show } from "solid-js";
2
+ import { createSignal, createEffect, Show } from "solid-js";
3
3
  import { useStyles } from "../../styles/use-styles.js";
4
- var _tmpl$ = /* @__PURE__ */ template(`<button>💬 Messages (<!>)`), _tmpl$2 = /* @__PURE__ */ template(`<button>📦 Chunks (<!>)`), _tmpl$3 = /* @__PURE__ */ template(`<button>📝 Summaries (<!>)`), _tmpl$4 = /* @__PURE__ */ template(`<div>`);
4
+ var _tmpl$ = /* @__PURE__ */ template(`<button>💬 Messages (<!>)`), _tmpl$2 = /* @__PURE__ */ template(`<button>📦 Chunks (<!>)`), _tmpl$3 = /* @__PURE__ */ template(`<button>📝 Summaries (<!>)`), _tmpl$4 = /* @__PURE__ */ template(`<button>🖼️ Image (<!>)`), _tmpl$5 = /* @__PURE__ */ template(`<button>🔊 Speech (<!>)`), _tmpl$6 = /* @__PURE__ */ template(`<button>📝 Transcription (<!>)`), _tmpl$7 = /* @__PURE__ */ template(`<button>🎬 Video (<!>)`), _tmpl$8 = /* @__PURE__ */ template(`<div>`);
5
5
  const ConversationTabs = (props) => {
6
6
  const styles = useStyles();
7
7
  const conv = () => props.conversation;
8
8
  const totalRawChunks = () => conv().chunks.reduce((sum, c) => sum + (c.chunkCount || 1), 0);
9
9
  const summariesCount = () => conv().summaries?.length ?? 0;
10
+ const imageCount = () => conv().imageEvents?.length ?? 0;
11
+ const speechCount = () => conv().speechEvents?.length ?? 0;
12
+ const transcriptionCount = () => conv().transcriptionEvents?.length ?? 0;
13
+ const videoCount = () => conv().videoEvents?.length ?? 0;
14
+ const [imagePulse, setImagePulse] = createSignal(false);
15
+ const [speechPulse, setSpeechPulse] = createSignal(false);
16
+ const [transcriptionPulse, setTranscriptionPulse] = createSignal(false);
17
+ const [videoPulse, setVideoPulse] = createSignal(false);
18
+ const triggerPulse = (setter) => {
19
+ setter(true);
20
+ setTimeout(() => setter(false), 2e3);
21
+ };
22
+ let previousImageCount = 0;
23
+ let previousSpeechCount = 0;
24
+ let previousTranscriptionCount = 0;
25
+ let previousVideoCount = 0;
26
+ createEffect(() => {
27
+ const count = imageCount();
28
+ if (count > 0 && previousImageCount === 0) {
29
+ triggerPulse(setImagePulse);
30
+ }
31
+ previousImageCount = count;
32
+ });
33
+ createEffect(() => {
34
+ const count = speechCount();
35
+ if (count > 0 && previousSpeechCount === 0) {
36
+ triggerPulse(setSpeechPulse);
37
+ }
38
+ previousSpeechCount = count;
39
+ });
40
+ createEffect(() => {
41
+ const count = transcriptionCount();
42
+ if (count > 0 && previousTranscriptionCount === 0) {
43
+ triggerPulse(setTranscriptionPulse);
44
+ }
45
+ previousTranscriptionCount = count;
46
+ });
47
+ createEffect(() => {
48
+ const count = videoCount();
49
+ if (count > 0 && previousVideoCount === 0) {
50
+ triggerPulse(setVideoPulse);
51
+ }
52
+ previousVideoCount = count;
53
+ });
10
54
  const hasMessages = () => conv().type === "client" && conv().messages.length > 0;
11
55
  const hasChunks = () => conv().chunks.length > 0 || conv().type === "server";
12
56
  const hasSummaries = () => conv().hasSummarize || summariesCount() > 0;
57
+ const hasImage = () => conv().hasImage || imageCount() > 0;
58
+ const hasSpeech = () => conv().hasSpeech || speechCount() > 0;
59
+ const hasTranscription = () => conv().hasTranscription || transcriptionCount() > 0;
60
+ const hasVideo = () => conv().hasVideo || videoCount() > 0;
13
61
  const visibleTabCount = () => {
14
62
  let count = 0;
15
63
  if (hasMessages()) count++;
16
64
  if (hasChunks() && conv().type === "server") count++;
17
65
  if (hasSummaries()) count++;
66
+ if (hasImage()) count++;
67
+ if (hasSpeech()) count++;
68
+ if (hasTranscription()) count++;
69
+ if (hasVideo()) count++;
18
70
  return count;
19
71
  };
20
72
  if (visibleTabCount() <= 1) {
21
73
  return null;
22
74
  }
23
75
  return (() => {
24
- var _el$ = _tmpl$4();
76
+ var _el$ = _tmpl$8();
25
77
  insert(_el$, createComponent(Show, {
26
78
  get when() {
27
79
  return hasMessages();
@@ -61,6 +113,58 @@ const ConversationTabs = (props) => {
61
113
  return _el$0;
62
114
  }
63
115
  }), null);
116
+ insert(_el$, createComponent(Show, {
117
+ get when() {
118
+ return hasImage();
119
+ },
120
+ get children() {
121
+ var _el$12 = _tmpl$4(), _el$13 = _el$12.firstChild, _el$15 = _el$13.nextSibling;
122
+ _el$15.nextSibling;
123
+ _el$12.$$click = () => props.onTabChange("image");
124
+ insert(_el$12, imageCount, _el$15);
125
+ effect(() => className(_el$12, `${styles().actionButton} ${props.activeTab === "image" ? styles().conversationDetails.tabButtonActive : ""} ${imagePulse() ? styles().conversationDetails.tabButtonPulse : ""}`));
126
+ return _el$12;
127
+ }
128
+ }), null);
129
+ insert(_el$, createComponent(Show, {
130
+ get when() {
131
+ return hasSpeech();
132
+ },
133
+ get children() {
134
+ var _el$16 = _tmpl$5(), _el$17 = _el$16.firstChild, _el$19 = _el$17.nextSibling;
135
+ _el$19.nextSibling;
136
+ _el$16.$$click = () => props.onTabChange("speech");
137
+ insert(_el$16, speechCount, _el$19);
138
+ effect(() => className(_el$16, `${styles().actionButton} ${props.activeTab === "speech" ? styles().conversationDetails.tabButtonActive : ""} ${speechPulse() ? styles().conversationDetails.tabButtonPulse : ""}`));
139
+ return _el$16;
140
+ }
141
+ }), null);
142
+ insert(_el$, createComponent(Show, {
143
+ get when() {
144
+ return hasTranscription();
145
+ },
146
+ get children() {
147
+ var _el$20 = _tmpl$6(), _el$21 = _el$20.firstChild, _el$23 = _el$21.nextSibling;
148
+ _el$23.nextSibling;
149
+ _el$20.$$click = () => props.onTabChange("transcription");
150
+ insert(_el$20, transcriptionCount, _el$23);
151
+ effect(() => className(_el$20, `${styles().actionButton} ${props.activeTab === "transcription" ? styles().conversationDetails.tabButtonActive : ""} ${transcriptionPulse() ? styles().conversationDetails.tabButtonPulse : ""}`));
152
+ return _el$20;
153
+ }
154
+ }), null);
155
+ insert(_el$, createComponent(Show, {
156
+ get when() {
157
+ return hasVideo();
158
+ },
159
+ get children() {
160
+ var _el$24 = _tmpl$7(), _el$25 = _el$24.firstChild, _el$27 = _el$25.nextSibling;
161
+ _el$27.nextSibling;
162
+ _el$24.$$click = () => props.onTabChange("video");
163
+ insert(_el$24, videoCount, _el$27);
164
+ effect(() => className(_el$24, `${styles().actionButton} ${props.activeTab === "video" ? styles().conversationDetails.tabButtonActive : ""} ${videoPulse() ? styles().conversationDetails.tabButtonPulse : ""}`));
165
+ return _el$24;
166
+ }
167
+ }), null);
64
168
  effect(() => className(_el$, styles().conversationDetails.tabsContainer));
65
169
  return _el$;
66
170
  })();
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationTabs.js","sources":["../../../../src/components/conversation/ConversationTabs.tsx"],"sourcesContent":["import { Show } from 'solid-js'\nimport { useStyles } from '../../styles/use-styles'\nimport type { Component } from 'solid-js'\nimport type { Conversation } from '../../store/ai-context'\n\nexport type TabType = 'messages' | 'chunks' | 'summaries'\n\ninterface ConversationTabsProps {\n conversation: Conversation\n activeTab: TabType\n onTabChange: (tab: TabType) => void\n}\n\nexport const ConversationTabs: Component<ConversationTabsProps> = (props) => {\n const styles = useStyles()\n const conv = () => props.conversation\n\n // Total raw chunks = sum of all chunkCounts\n const totalRawChunks = () =>\n conv().chunks.reduce((sum, c) => sum + (c.chunkCount || 1), 0)\n\n const summariesCount = () => conv().summaries?.length ?? 0\n\n // Determine if we should show any chat-related tabs\n // For server conversations, don't show messages tab - only chunks\n const hasMessages = () =>\n conv().type === 'client' && conv().messages.length > 0\n const hasChunks = () => conv().chunks.length > 0 || conv().type === 'server'\n const hasSummaries = () => conv().hasSummarize || summariesCount() > 0\n\n // Count how many tabs would be visible\n const visibleTabCount = () => {\n let count = 0\n if (hasMessages()) count++\n if (hasChunks() && conv().type === 'server') count++\n if (hasSummaries()) count++\n return count\n }\n\n // Don't render tabs if only one tab would be visible\n if (visibleTabCount() <= 1) {\n return null\n }\n\n return (\n <div class={styles().conversationDetails.tabsContainer}>\n {/* Show messages tab for client conversations or when there are messages */}\n <Show when={hasMessages()}>\n <button\n class={`${styles().actionButton} ${\n props.activeTab === 'messages'\n ? styles().conversationDetails.tabButtonActive\n : ''\n }`}\n onClick={() => props.onTabChange('messages')}\n >\n 💬 Messages ({conv().messages.length})\n </button>\n </Show>\n {/* Show chunks tab for server conversations or when there are chunks */}\n <Show when={hasChunks() && conv().type === 'server'}>\n <button\n class={`${styles().actionButton} ${\n props.activeTab === 'chunks'\n ? styles().conversationDetails.tabButtonActive\n : ''\n }`}\n onClick={() => props.onTabChange('chunks')}\n >\n 📦 Chunks ({totalRawChunks()})\n </button>\n </Show>\n {/* Show summaries tab if there are summarize operations */}\n <Show when={hasSummaries()}>\n <button\n class={`${styles().actionButton} ${\n props.activeTab === 'summaries'\n ? styles().conversationDetails.tabButtonActive\n : ''\n }`}\n onClick={() => props.onTabChange('summaries')}\n >\n 📝 Summaries ({summariesCount()})\n </button>\n </Show>\n </div>\n )\n}\n"],"names":["ConversationTabs","props","styles","useStyles","conv","conversation","totalRawChunks","chunks","reduce","sum","c","chunkCount","summariesCount","summaries","length","hasMessages","type","messages","hasChunks","hasSummaries","hasSummarize","visibleTabCount","count","_el$","_tmpl$4","_$insert","_$createComponent","Show","when","children","_el$2","_tmpl$","_el$3","firstChild","_el$5","nextSibling","$$click","onTabChange","_$effect","_$className","actionButton","activeTab","conversationDetails","tabButtonActive","_$memo","_el$6","_tmpl$2","_el$7","_el$9","_el$0","_tmpl$3","_el$1","_el$11","tabsContainer","_$delegateEvents"],"mappings":"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"}
1
+ {"version":3,"file":"ConversationTabs.js","sources":["../../../../src/components/conversation/ConversationTabs.tsx"],"sourcesContent":["import { Show, createEffect, createSignal } from 'solid-js'\nimport { useStyles } from '../../styles/use-styles'\nimport type { Component } from 'solid-js'\nimport type { Conversation } from '../../store/ai-context'\n\nexport type TabType =\n | 'messages'\n | 'chunks'\n | 'summaries'\n | 'image'\n | 'speech'\n | 'transcription'\n | 'video'\n\ninterface ConversationTabsProps {\n conversation: Conversation\n activeTab: TabType\n onTabChange: (tab: TabType) => void\n}\n\nexport const ConversationTabs: Component<ConversationTabsProps> = (props) => {\n const styles = useStyles()\n const conv = () => props.conversation\n\n // Total raw chunks = sum of all chunkCounts\n const totalRawChunks = () =>\n conv().chunks.reduce((sum, c) => sum + (c.chunkCount || 1), 0)\n\n const summariesCount = () => conv().summaries?.length ?? 0\n const imageCount = () => conv().imageEvents?.length ?? 0\n const speechCount = () => conv().speechEvents?.length ?? 0\n const transcriptionCount = () => conv().transcriptionEvents?.length ?? 0\n const videoCount = () => conv().videoEvents?.length ?? 0\n\n const [imagePulse, setImagePulse] = createSignal(false)\n const [speechPulse, setSpeechPulse] = createSignal(false)\n const [transcriptionPulse, setTranscriptionPulse] = createSignal(false)\n const [videoPulse, setVideoPulse] = createSignal(false)\n\n const triggerPulse = (setter: (value: boolean) => void) => {\n setter(true)\n setTimeout(() => setter(false), 2000)\n }\n\n let previousImageCount = 0\n let previousSpeechCount = 0\n let previousTranscriptionCount = 0\n let previousVideoCount = 0\n\n createEffect(() => {\n const count = imageCount()\n if (count > 0 && previousImageCount === 0) {\n triggerPulse(setImagePulse)\n }\n previousImageCount = count\n })\n\n createEffect(() => {\n const count = speechCount()\n if (count > 0 && previousSpeechCount === 0) {\n triggerPulse(setSpeechPulse)\n }\n previousSpeechCount = count\n })\n\n createEffect(() => {\n const count = transcriptionCount()\n if (count > 0 && previousTranscriptionCount === 0) {\n triggerPulse(setTranscriptionPulse)\n }\n previousTranscriptionCount = count\n })\n\n createEffect(() => {\n const count = videoCount()\n if (count > 0 && previousVideoCount === 0) {\n triggerPulse(setVideoPulse)\n }\n previousVideoCount = count\n })\n\n // Determine if we should show any chat-related tabs\n // For server conversations, don't show messages tab - only chunks\n const hasMessages = () =>\n conv().type === 'client' && conv().messages.length > 0\n const hasChunks = () => conv().chunks.length > 0 || conv().type === 'server'\n const hasSummaries = () => conv().hasSummarize || summariesCount() > 0\n const hasImage = () => conv().hasImage || imageCount() > 0\n const hasSpeech = () => conv().hasSpeech || speechCount() > 0\n const hasTranscription = () =>\n conv().hasTranscription || transcriptionCount() > 0\n const hasVideo = () => conv().hasVideo || videoCount() > 0\n\n // Count how many tabs would be visible\n const visibleTabCount = () => {\n let count = 0\n if (hasMessages()) count++\n if (hasChunks() && conv().type === 'server') count++\n if (hasSummaries()) count++\n if (hasImage()) count++\n if (hasSpeech()) count++\n if (hasTranscription()) count++\n if (hasVideo()) count++\n return count\n }\n\n // Don't render tabs if only one tab would be visible\n if (visibleTabCount() <= 1) {\n return null\n }\n\n return (\n <div class={styles().conversationDetails.tabsContainer}>\n {/* Show messages tab for client conversations or when there are messages */}\n <Show when={hasMessages()}>\n <button\n class={`${styles().actionButton} ${\n props.activeTab === 'messages'\n ? styles().conversationDetails.tabButtonActive\n : ''\n }`}\n onClick={() => props.onTabChange('messages')}\n >\n 💬 Messages ({conv().messages.length})\n </button>\n </Show>\n {/* Show chunks tab for server conversations or when there are chunks */}\n <Show when={hasChunks() && conv().type === 'server'}>\n <button\n class={`${styles().actionButton} ${\n props.activeTab === 'chunks'\n ? styles().conversationDetails.tabButtonActive\n : ''\n }`}\n onClick={() => props.onTabChange('chunks')}\n >\n 📦 Chunks ({totalRawChunks()})\n </button>\n </Show>\n {/* Show summaries tab if there are summarize operations */}\n <Show when={hasSummaries()}>\n <button\n class={`${styles().actionButton} ${\n props.activeTab === 'summaries'\n ? styles().conversationDetails.tabButtonActive\n : ''\n }`}\n onClick={() => props.onTabChange('summaries')}\n >\n 📝 Summaries ({summariesCount()})\n </button>\n </Show>\n <Show when={hasImage()}>\n <button\n class={`${styles().actionButton} ${\n props.activeTab === 'image'\n ? styles().conversationDetails.tabButtonActive\n : ''\n } ${imagePulse() ? styles().conversationDetails.tabButtonPulse : ''}`}\n onClick={() => props.onTabChange('image')}\n >\n 🖼️ Image ({imageCount()})\n </button>\n </Show>\n <Show when={hasSpeech()}>\n <button\n class={`${styles().actionButton} ${\n props.activeTab === 'speech'\n ? styles().conversationDetails.tabButtonActive\n : ''\n } ${speechPulse() ? styles().conversationDetails.tabButtonPulse : ''}`}\n onClick={() => props.onTabChange('speech')}\n >\n 🔊 Speech ({speechCount()})\n </button>\n </Show>\n <Show when={hasTranscription()}>\n <button\n class={`${styles().actionButton} ${\n props.activeTab === 'transcription'\n ? styles().conversationDetails.tabButtonActive\n : ''\n } ${\n transcriptionPulse()\n ? styles().conversationDetails.tabButtonPulse\n : ''\n }`}\n onClick={() => props.onTabChange('transcription')}\n >\n 📝 Transcription ({transcriptionCount()})\n </button>\n </Show>\n <Show when={hasVideo()}>\n <button\n class={`${styles().actionButton} ${\n props.activeTab === 'video'\n ? styles().conversationDetails.tabButtonActive\n : ''\n } ${videoPulse() ? styles().conversationDetails.tabButtonPulse : ''}`}\n onClick={() => props.onTabChange('video')}\n >\n 🎬 Video ({videoCount()})\n </button>\n </Show>\n </div>\n )\n}\n"],"names":["ConversationTabs","props","styles","useStyles","conv","conversation","totalRawChunks","chunks","reduce","sum","c","chunkCount","summariesCount","summaries","length","imageCount","imageEvents","speechCount","speechEvents","transcriptionCount","transcriptionEvents","videoCount","videoEvents","imagePulse","setImagePulse","createSignal","speechPulse","setSpeechPulse","transcriptionPulse","setTranscriptionPulse","videoPulse","setVideoPulse","triggerPulse","setter","setTimeout","previousImageCount","previousSpeechCount","previousTranscriptionCount","previousVideoCount","createEffect","count","hasMessages","type","messages","hasChunks","hasSummaries","hasSummarize","hasImage","hasSpeech","hasTranscription","hasVideo","visibleTabCount","_el$","_tmpl$8","_$insert","_$createComponent","Show","when","children","_el$2","_tmpl$","_el$3","firstChild","_el$5","nextSibling","$$click","onTabChange","_$effect","_$className","actionButton","activeTab","conversationDetails","tabButtonActive","_$memo","_el$6","_tmpl$2","_el$7","_el$9","_el$0","_tmpl$3","_el$1","_el$11","_el$12","_tmpl$4","_el$13","_el$15","tabButtonPulse","_el$16","_tmpl$5","_el$17","_el$19","_el$20","_tmpl$6","_el$21","_el$23","_el$24","_tmpl$7","_el$25","_el$27","tabsContainer","_$delegateEvents"],"mappings":"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"}
@@ -1,10 +1,11 @@
1
1
  import { template, insert, createComponent, effect, className, memo } from "solid-js/web";
2
2
  import { Show, For } from "solid-js";
3
+ import { JsonTree } from "@tanstack/devtools-ui";
3
4
  import { useStyles } from "../../styles/use-styles.js";
4
- import { formatTimestamp } from "../utils/format.js";
5
+ import { formatTimestamp, formatDuration } from "../utils/format.js";
5
6
  import { ToolCallDisplay } from "./ToolCallDisplay.js";
6
7
  import { ChunksCollapsible } from "./ChunksCollapsible.js";
7
- var _tmpl$ = /* @__PURE__ */ template(`<div><span></span><span>`), _tmpl$2 = /* @__PURE__ */ template(`<div><span>🎯</span><span> in</span><span>•</span><span> out`), _tmpl$3 = /* @__PURE__ */ template(`<details><summary>💭 Thinking...</summary><div>`), _tmpl$4 = /* @__PURE__ */ template(`<div>`), _tmpl$5 = /* @__PURE__ */ template(`<div><div><div><div></div><div><div></div></div><div></div></div><div>`);
8
+ var _tmpl$ = /* @__PURE__ */ template(`<div><span></span><span>`), _tmpl$2 = /* @__PURE__ */ template(`<div><span>🎯</span><span> in</span><span>•</span><span> out`), _tmpl$3 = /* @__PURE__ */ template(`<div><span>⏱️</span><span>`), _tmpl$4 = /* @__PURE__ */ template(`<details><summary>💭 Thinking...</summary><div>`), _tmpl$5 = /* @__PURE__ */ template(`<div>`), _tmpl$6 = /* @__PURE__ */ template(`<div><div><div><div></div><div><div></div></div><div>`);
8
9
  const MessageCard = (props) => {
9
10
  const styles = useStyles();
10
11
  const msg = () => props.message;
@@ -19,12 +20,27 @@ const MessageCard = (props) => {
19
20
  }
20
21
  return `${base} ${styles().conversationDetails.messageCardServer}`;
21
22
  };
22
- const isSourcedMessage = () => msg().role !== "user";
23
+ const parseJsonContent = () => {
24
+ const content = msg().content;
25
+ if (typeof content !== "string") return null;
26
+ const trimmed = content.trim();
27
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) return null;
28
+ try {
29
+ return JSON.parse(trimmed);
30
+ } catch {
31
+ return null;
32
+ }
33
+ };
34
+ const toolDuration = () => {
35
+ const durations = msg().toolCalls?.map((tool) => tool.duration || 0).filter((duration) => duration > 0);
36
+ if (!durations || durations.length === 0) return 0;
37
+ return durations.reduce((total, duration) => total + duration, 0);
38
+ };
23
39
  return (() => {
24
- var _el$ = _tmpl$5(), _el$5 = _el$.firstChild, _el$6 = _el$5.firstChild, _el$7 = _el$6.firstChild, _el$8 = _el$7.nextSibling, _el$9 = _el$8.firstChild, _el$0 = _el$8.nextSibling, _el$19 = _el$6.nextSibling;
40
+ var _el$ = _tmpl$6(), _el$5 = _el$.firstChild, _el$6 = _el$5.firstChild, _el$7 = _el$6.firstChild, _el$8 = _el$7.nextSibling, _el$9 = _el$8.firstChild, _el$0 = _el$8.nextSibling;
25
41
  insert(_el$, createComponent(Show, {
26
42
  get when() {
27
- return isSourcedMessage();
43
+ return msg().role === "assistant";
28
44
  },
29
45
  get children() {
30
46
  var _el$2 = _tmpl$(), _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling;
@@ -70,35 +86,76 @@ const MessageCard = (props) => {
70
86
  return _el$1;
71
87
  }
72
88
  }), null);
73
- insert(_el$5, createComponent(Show, {
89
+ insert(_el$6, createComponent(Show, {
74
90
  get when() {
75
- return msg().thinkingContent;
91
+ return toolDuration() > 0;
76
92
  },
77
93
  get children() {
78
94
  var _el$16 = _tmpl$3(), _el$17 = _el$16.firstChild, _el$18 = _el$17.nextSibling;
79
- insert(_el$18, () => msg().thinkingContent);
95
+ insert(_el$18, () => formatDuration(toolDuration()));
80
96
  effect((_p$) => {
81
- var _v$6 = styles().conversationDetails.thinkingDetails, _v$7 = styles().conversationDetails.thinkingSummary, _v$8 = styles().conversationDetails.thinkingContent;
97
+ var _v$6 = styles().conversationDetails.messageUsage, _v$7 = styles().conversationDetails.messageUsageIcon;
82
98
  _v$6 !== _p$.e && className(_el$16, _p$.e = _v$6);
83
99
  _v$7 !== _p$.t && className(_el$17, _p$.t = _v$7);
84
- _v$8 !== _p$.a && className(_el$18, _p$.a = _v$8);
100
+ return _p$;
101
+ }, {
102
+ e: void 0,
103
+ t: void 0
104
+ });
105
+ return _el$16;
106
+ }
107
+ }), null);
108
+ insert(_el$5, createComponent(Show, {
109
+ get when() {
110
+ return msg().thinkingContent;
111
+ },
112
+ get children() {
113
+ var _el$19 = _tmpl$4(), _el$20 = _el$19.firstChild, _el$21 = _el$20.nextSibling;
114
+ insert(_el$21, () => msg().thinkingContent);
115
+ effect((_p$) => {
116
+ var _v$8 = styles().conversationDetails.thinkingDetails, _v$9 = styles().conversationDetails.thinkingSummary, _v$0 = styles().conversationDetails.thinkingContent;
117
+ _v$8 !== _p$.e && className(_el$19, _p$.e = _v$8);
118
+ _v$9 !== _p$.t && className(_el$20, _p$.t = _v$9);
119
+ _v$0 !== _p$.a && className(_el$21, _p$.a = _v$0);
85
120
  return _p$;
86
121
  }, {
87
122
  e: void 0,
88
123
  t: void 0,
89
124
  a: void 0
90
125
  });
91
- return _el$16;
126
+ return _el$19;
92
127
  }
93
- }), _el$19);
94
- insert(_el$19, () => msg().content);
128
+ }), null);
129
+ insert(_el$5, createComponent(Show, {
130
+ get when() {
131
+ return memo(() => msg().role === "tool")() && parseJsonContent() !== null;
132
+ },
133
+ get fallback() {
134
+ return (() => {
135
+ var _el$24 = _tmpl$5();
136
+ insert(_el$24, () => msg().content);
137
+ effect(() => className(_el$24, styles().conversationDetails.messageContent));
138
+ return _el$24;
139
+ })();
140
+ },
141
+ get children() {
142
+ var _el$22 = _tmpl$5();
143
+ insert(_el$22, createComponent(JsonTree, {
144
+ get value() {
145
+ return parseJsonContent();
146
+ }
147
+ }));
148
+ effect(() => className(_el$22, styles().conversationDetails.toolJsonContainer));
149
+ return _el$22;
150
+ }
151
+ }), null);
95
152
  insert(_el$5, createComponent(Show, {
96
153
  get when() {
97
154
  return memo(() => !!msg().toolCalls)() && msg().toolCalls.length > 0;
98
155
  },
99
156
  get children() {
100
- var _el$20 = _tmpl$4();
101
- insert(_el$20, createComponent(For, {
157
+ var _el$23 = _tmpl$5();
158
+ insert(_el$23, createComponent(For, {
102
159
  get each() {
103
160
  return msg().toolCalls;
104
161
  },
@@ -106,8 +163,8 @@ const MessageCard = (props) => {
106
163
  tool
107
164
  })
108
165
  }));
109
- effect(() => className(_el$20, styles().conversationDetails.toolCallsContainer));
110
- return _el$20;
166
+ effect(() => className(_el$23, styles().conversationDetails.toolCallsContainer));
167
+ return _el$23;
111
168
  }
112
169
  }), null);
113
170
  insert(_el$5, createComponent(Show, {
@@ -123,15 +180,14 @@ const MessageCard = (props) => {
123
180
  }
124
181
  }), null);
125
182
  effect((_p$) => {
126
- var _v$9 = getCardClass(), _v$0 = isSourcedMessage() ? styles().conversationDetails.messageCardContent : "", _v$1 = styles().conversationDetails.messageHeader, _v$10 = msg().role === "user" ? styles().conversationDetails.avatarUser : isClientMessage() ? styles().conversationDetails.avatarClient : styles().conversationDetails.avatarServer, _v$11 = styles().conversationDetails.roleLabel, _v$12 = msg().role === "user" ? styles().conversationDetails.roleLabelUser : isClientMessage() ? styles().conversationDetails.roleLabelClient : styles().conversationDetails.roleLabelServer, _v$13 = styles().conversationDetails.timestamp, _v$14 = styles().conversationDetails.messageContent;
127
- _v$9 !== _p$.e && className(_el$, _p$.e = _v$9);
128
- _v$0 !== _p$.t && className(_el$5, _p$.t = _v$0);
129
- _v$1 !== _p$.a && className(_el$6, _p$.a = _v$1);
130
- _v$10 !== _p$.o && className(_el$7, _p$.o = _v$10);
131
- _v$11 !== _p$.i && className(_el$8, _p$.i = _v$11);
132
- _v$12 !== _p$.n && className(_el$9, _p$.n = _v$12);
133
- _v$13 !== _p$.s && className(_el$0, _p$.s = _v$13);
134
- _v$14 !== _p$.h && className(_el$19, _p$.h = _v$14);
183
+ var _v$1 = getCardClass(), _v$10 = msg().role === "user" ? "" : styles().conversationDetails.messageCardContent, _v$11 = styles().conversationDetails.messageHeader, _v$12 = msg().role === "user" ? styles().conversationDetails.avatarUser : isClientMessage() ? styles().conversationDetails.avatarClient : styles().conversationDetails.avatarServer, _v$13 = styles().conversationDetails.roleLabel, _v$14 = msg().role === "user" ? styles().conversationDetails.roleLabelUser : isClientMessage() ? styles().conversationDetails.roleLabelClient : styles().conversationDetails.roleLabelServer, _v$15 = styles().conversationDetails.timestamp;
184
+ _v$1 !== _p$.e && className(_el$, _p$.e = _v$1);
185
+ _v$10 !== _p$.t && className(_el$5, _p$.t = _v$10);
186
+ _v$11 !== _p$.a && className(_el$6, _p$.a = _v$11);
187
+ _v$12 !== _p$.o && className(_el$7, _p$.o = _v$12);
188
+ _v$13 !== _p$.i && className(_el$8, _p$.i = _v$13);
189
+ _v$14 !== _p$.n && className(_el$9, _p$.n = _v$14);
190
+ _v$15 !== _p$.s && className(_el$0, _p$.s = _v$15);
135
191
  return _p$;
136
192
  }, {
137
193
  e: void 0,
@@ -140,8 +196,7 @@ const MessageCard = (props) => {
140
196
  o: void 0,
141
197
  i: void 0,
142
198
  n: void 0,
143
- s: void 0,
144
- h: void 0
199
+ s: void 0
145
200
  });
146
201
  return _el$;
147
202
  })();
@@ -1 +1 @@
1
- {"version":3,"file":"MessageCard.js","sources":["../../../../src/components/conversation/MessageCard.tsx"],"sourcesContent":["import { For, Show } from 'solid-js'\nimport { useStyles } from '../../styles/use-styles'\nimport { formatTimestamp } from '../utils'\nimport { ToolCallDisplay } from './ToolCallDisplay'\nimport { ChunksCollapsible } from './ChunksCollapsible'\nimport type { Message } from '../../store/ai-store'\nimport type { Component } from 'solid-js'\n\ninterface MessageCardProps {\n message: Message\n}\n\nexport const MessageCard: Component<MessageCardProps> = (props) => {\n const styles = useStyles()\n // Access message through props directly for proper SolidJS reactivity\n const msg = () => props.message\n\n // Check if message is from client (explicitly marked) or server (default)\n const isClientMessage = () => msg().source === 'client'\n\n // Determine card class based on role and source\n const getCardClass = () => {\n const base = styles().conversationDetails.messageCard\n if (msg().role === 'user') {\n return `${base} ${styles().conversationDetails.messageCardUser}`\n }\n // For assistant messages: client if explicitly marked, otherwise server\n if (isClientMessage()) {\n return `${base} ${styles().conversationDetails.messageCardClient}`\n }\n return `${base} ${styles().conversationDetails.messageCardServer}`\n }\n\n // Check if this is a non-user message (needs source banner and content wrapper)\n const isSourcedMessage = () => msg().role !== 'user'\n\n return (\n <div class={getCardClass()}>\n {/* Source indicator banner at top of card */}\n <Show when={isSourcedMessage()}>\n <div\n class={`${styles().conversationDetails.sourceBanner} ${\n isClientMessage()\n ? styles().conversationDetails.sourceBannerClient\n : styles().conversationDetails.sourceBannerServer\n }`}\n >\n <span class={styles().conversationDetails.sourceBannerIcon}>\n {isClientMessage() ? '📱' : '☁️'}\n </span>\n <span class={styles().conversationDetails.sourceBannerText}>\n {isClientMessage() ? 'Client Message' : 'Server Message'}\n </span>\n </div>\n </Show>\n {/* Content wrapper with padding */}\n <div\n class={\n isSourcedMessage()\n ? styles().conversationDetails.messageCardContent\n : ''\n }\n >\n <div class={styles().conversationDetails.messageHeader}>\n <div\n class={\n msg().role === 'user'\n ? styles().conversationDetails.avatarUser\n : isClientMessage()\n ? styles().conversationDetails.avatarClient\n : styles().conversationDetails.avatarServer\n }\n >\n {msg().role === 'user' ? 'U' : msg().role === 'tool' ? '🔧' : '🤖'}\n </div>\n <div class={styles().conversationDetails.roleLabel}>\n <div\n class={\n msg().role === 'user'\n ? styles().conversationDetails.roleLabelUser\n : isClientMessage()\n ? styles().conversationDetails.roleLabelClient\n : styles().conversationDetails.roleLabelServer\n }\n >\n {msg().role}\n </div>\n </div>\n <div class={styles().conversationDetails.timestamp}>\n {formatTimestamp(msg().timestamp)}\n </div>\n {/* Per-message token usage */}\n <Show when={msg().usage}>\n <div class={styles().conversationDetails.messageUsage}>\n <span class={styles().conversationDetails.messageUsageIcon}>\n 🎯\n </span>\n <span>{msg().usage?.promptTokens.toLocaleString()} in</span>\n <span>•</span>\n <span>{msg().usage?.completionTokens.toLocaleString()} out</span>\n </div>\n </Show>\n </div>\n\n {/* Thinking content (for extended thinking models) */}\n <Show when={msg().thinkingContent}>\n <details class={styles().conversationDetails.thinkingDetails}>\n <summary class={styles().conversationDetails.thinkingSummary}>\n 💭 Thinking...\n </summary>\n <div class={styles().conversationDetails.thinkingContent}>\n {msg().thinkingContent}\n </div>\n </details>\n </Show>\n\n <div class={styles().conversationDetails.messageContent}>\n {msg().content}\n </div>\n\n {/* Tool Calls Display */}\n <Show when={msg().toolCalls && msg().toolCalls!.length > 0}>\n <div class={styles().conversationDetails.toolCallsContainer}>\n <For each={msg().toolCalls}>\n {(tool) => <ToolCallDisplay tool={tool} />}\n </For>\n </div>\n </Show>\n\n {/* Chunks Display (for client conversations with server chunks) */}\n <Show when={msg().chunks && msg().chunks!.length > 0}>\n <ChunksCollapsible chunks={msg().chunks!} />\n </Show>\n </div>\n </div>\n )\n}\n"],"names":["MessageCard","props","styles","useStyles","msg","message","isClientMessage","source","getCardClass","base","conversationDetails","messageCard","role","messageCardUser","messageCardClient","messageCardServer","isSourcedMessage","_el$","_tmpl$5","_el$5","firstChild","_el$6","_el$7","_el$8","nextSibling","_el$9","_el$0","_el$19","_$insert","_$createComponent","Show","when","children","_el$2","_tmpl$","_el$3","_el$4","_$effect","_p$","_v$","sourceBanner","sourceBannerClient","sourceBannerServer","_v$2","sourceBannerIcon","_v$3","sourceBannerText","e","_$className","t","a","undefined","_c$","_$memo","formatTimestamp","timestamp","usage","_el$1","_tmpl$2","_el$10","_el$11","_el$12","_el$13","_el$14","_el$15","promptTokens","toLocaleString","completionTokens","_v$4","messageUsage","_v$5","messageUsageIcon","thinkingContent","_el$16","_tmpl$3","_el$17","_el$18","_v$6","thinkingDetails","_v$7","thinkingSummary","_v$8","content","toolCalls","length","_el$20","_tmpl$4","For","each","tool","ToolCallDisplay","toolCallsContainer","chunks","ChunksCollapsible","_v$9","_v$0","messageCardContent","_v$1","messageHeader","_v$10","avatarUser","avatarClient","avatarServer","_v$11","roleLabel","_v$12","roleLabelUser","roleLabelClient","roleLabelServer","_v$13","_v$14","messageContent","o","i","n","s","h"],"mappings":"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"}
1
+ {"version":3,"file":"MessageCard.js","sources":["../../../../src/components/conversation/MessageCard.tsx"],"sourcesContent":["import { For, Show } from 'solid-js'\nimport { JsonTree } from '@tanstack/devtools-ui'\nimport { useStyles } from '../../styles/use-styles'\nimport { formatDuration, formatTimestamp } from '../utils'\nimport { ToolCallDisplay } from './ToolCallDisplay'\nimport { ChunksCollapsible } from './ChunksCollapsible'\nimport type { Message } from '../../store/ai-store'\nimport type { Component } from 'solid-js'\n\ninterface MessageCardProps {\n message: Message\n}\n\nexport const MessageCard: Component<MessageCardProps> = (props) => {\n const styles = useStyles()\n // Access message through props directly for proper SolidJS reactivity\n const msg = () => props.message\n\n // Check if message is from client (explicitly marked) or server (default)\n const isClientMessage = () => msg().source === 'client'\n\n // Determine card class based on role and source\n const getCardClass = () => {\n const base = styles().conversationDetails.messageCard\n if (msg().role === 'user') {\n return `${base} ${styles().conversationDetails.messageCardUser}`\n }\n // For assistant messages: client if explicitly marked, otherwise server\n if (isClientMessage()) {\n return `${base} ${styles().conversationDetails.messageCardClient}`\n }\n return `${base} ${styles().conversationDetails.messageCardServer}`\n }\n\n const parseJsonContent = () => {\n const content = msg().content\n if (typeof content !== 'string') return null\n const trimmed = content.trim()\n if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) return null\n try {\n return JSON.parse(trimmed) as Record<string, unknown> | Array<unknown>\n } catch {\n return null\n }\n }\n\n const toolDuration = () => {\n const durations = msg()\n .toolCalls?.map((tool) => tool.duration || 0)\n .filter((duration) => duration > 0)\n if (!durations || durations.length === 0) return 0\n return durations.reduce((total, duration) => total + duration, 0)\n }\n\n return (\n <div class={getCardClass()}>\n {/* Source indicator banner at top of card */}\n <Show when={msg().role === 'assistant'}>\n <div\n class={`${styles().conversationDetails.sourceBanner} ${\n isClientMessage()\n ? styles().conversationDetails.sourceBannerClient\n : styles().conversationDetails.sourceBannerServer\n }`}\n >\n <span class={styles().conversationDetails.sourceBannerIcon}>\n {isClientMessage() ? '📱' : '☁️'}\n </span>\n <span class={styles().conversationDetails.sourceBannerText}>\n {isClientMessage() ? 'Client Message' : 'Server Message'}\n </span>\n </div>\n </Show>\n {/* Content wrapper with padding */}\n <div\n class={\n msg().role === 'user'\n ? ''\n : styles().conversationDetails.messageCardContent\n }\n >\n <div class={styles().conversationDetails.messageHeader}>\n <div\n class={\n msg().role === 'user'\n ? styles().conversationDetails.avatarUser\n : isClientMessage()\n ? styles().conversationDetails.avatarClient\n : styles().conversationDetails.avatarServer\n }\n >\n {msg().role === 'user' ? 'U' : msg().role === 'tool' ? '🔧' : '🤖'}\n </div>\n <div class={styles().conversationDetails.roleLabel}>\n <div\n class={\n msg().role === 'user'\n ? styles().conversationDetails.roleLabelUser\n : isClientMessage()\n ? styles().conversationDetails.roleLabelClient\n : styles().conversationDetails.roleLabelServer\n }\n >\n {msg().role}\n </div>\n </div>\n <div class={styles().conversationDetails.timestamp}>\n {formatTimestamp(msg().timestamp)}\n </div>\n {/* Per-message token usage */}\n <Show when={msg().usage}>\n <div class={styles().conversationDetails.messageUsage}>\n <span class={styles().conversationDetails.messageUsageIcon}>\n 🎯\n </span>\n <span>{msg().usage?.promptTokens.toLocaleString()} in</span>\n <span>•</span>\n <span>{msg().usage?.completionTokens.toLocaleString()} out</span>\n </div>\n </Show>\n <Show when={toolDuration() > 0}>\n <div class={styles().conversationDetails.messageUsage}>\n <span class={styles().conversationDetails.messageUsageIcon}>\n ⏱️\n </span>\n <span>{formatDuration(toolDuration())}</span>\n </div>\n </Show>\n </div>\n\n {/* Thinking content (for extended thinking models) */}\n <Show when={msg().thinkingContent}>\n <details class={styles().conversationDetails.thinkingDetails}>\n <summary class={styles().conversationDetails.thinkingSummary}>\n 💭 Thinking...\n </summary>\n <div class={styles().conversationDetails.thinkingContent}>\n {msg().thinkingContent}\n </div>\n </details>\n </Show>\n\n <Show\n when={msg().role === 'tool' && parseJsonContent() !== null}\n fallback={\n <div class={styles().conversationDetails.messageContent}>\n {msg().content}\n </div>\n }\n >\n <div class={styles().conversationDetails.toolJsonContainer}>\n <JsonTree value={parseJsonContent()!} />\n </div>\n </Show>\n\n {/* Tool Calls Display */}\n <Show when={msg().toolCalls && msg().toolCalls!.length > 0}>\n <div class={styles().conversationDetails.toolCallsContainer}>\n <For each={msg().toolCalls}>\n {(tool) => <ToolCallDisplay tool={tool} />}\n </For>\n </div>\n </Show>\n\n {/* Chunks Display (for client conversations with server chunks) */}\n <Show when={msg().chunks && msg().chunks!.length > 0}>\n <ChunksCollapsible chunks={msg().chunks!} />\n </Show>\n </div>\n </div>\n )\n}\n"],"names":["MessageCard","props","styles","useStyles","msg","message","isClientMessage","source","getCardClass","base","conversationDetails","messageCard","role","messageCardUser","messageCardClient","messageCardServer","parseJsonContent","content","trimmed","trim","startsWith","JSON","parse","toolDuration","durations","toolCalls","map","tool","duration","filter","length","reduce","total","_el$","_tmpl$6","_el$5","firstChild","_el$6","_el$7","_el$8","nextSibling","_el$9","_el$0","_$insert","_$createComponent","Show","when","children","_el$2","_tmpl$","_el$3","_el$4","_$effect","_p$","_v$","sourceBanner","sourceBannerClient","sourceBannerServer","_v$2","sourceBannerIcon","_v$3","sourceBannerText","e","_$className","t","a","undefined","_c$","_$memo","formatTimestamp","timestamp","usage","_el$1","_tmpl$2","_el$10","_el$11","_el$12","_el$13","_el$14","_el$15","promptTokens","toLocaleString","completionTokens","_v$4","messageUsage","_v$5","messageUsageIcon","_el$16","_tmpl$3","_el$17","_el$18","formatDuration","_v$6","_v$7","thinkingContent","_el$19","_tmpl$4","_el$20","_el$21","_v$8","thinkingDetails","_v$9","thinkingSummary","_v$0","fallback","_el$24","_tmpl$5","messageContent","_el$22","JsonTree","value","toolJsonContainer","_el$23","For","each","ToolCallDisplay","toolCallsContainer","chunks","ChunksCollapsible","_v$1","_v$10","messageCardContent","_v$11","messageHeader","_v$12","avatarUser","avatarClient","avatarServer","_v$13","roleLabel","_v$14","roleLabelUser","roleLabelClient","roleLabelServer","_v$15","o","i","n","s"],"mappings":"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"}
@@ -4,3 +4,4 @@ export type { TabType } from './ConversationTabs.js';
4
4
  export { MessagesTab } from './MessagesTab.js';
5
5
  export { ChunksTab } from './ChunksTab.js';
6
6
  export { SummariesTab } from './SummariesTab.js';
7
+ export { ActivityEventsTab } from './ActivityEventsTab.js';
@@ -1,6 +1,6 @@
1
1
  import { ParentComponent } from 'solid-js';
2
2
  interface MessagePart {
3
- type: 'text' | 'tool-call' | 'tool-result';
3
+ type: 'text' | 'tool-call' | 'tool-result' | 'thinking';
4
4
  content?: string;
5
5
  toolCallId?: string;
6
6
  toolName?: string;
@@ -69,6 +69,8 @@ export interface Chunk {
69
69
  duration?: number;
70
70
  /** Number of raw chunks that were merged into this consolidated chunk */
71
71
  chunkCount: number;
72
+ /** Whether this is a client-side tool execution */
73
+ isClientTool?: boolean;
72
74
  }
73
75
  export interface SummarizeOperation {
74
76
  id: string;
@@ -79,6 +81,12 @@ export interface SummarizeOperation {
79
81
  timestamp: number;
80
82
  status: 'started' | 'completed';
81
83
  }
84
+ export interface ActivityEvent {
85
+ id: string;
86
+ name: string;
87
+ timestamp: number;
88
+ payload: unknown;
89
+ }
82
90
  export interface Conversation {
83
91
  id: string;
84
92
  type: 'client' | 'server';
@@ -94,12 +102,21 @@ export interface Conversation {
94
102
  iterationCount?: number;
95
103
  toolNames?: Array<string>;
96
104
  options?: Record<string, unknown>;
97
- providerOptions?: Record<string, unknown>;
105
+ modelOptions?: Record<string, unknown>;
106
+ systemPrompts?: Array<string>;
98
107
  /** Flags for which operation types this conversation has */
99
108
  hasChat?: boolean;
100
109
  hasSummarize?: boolean;
110
+ hasImage?: boolean;
111
+ hasSpeech?: boolean;
112
+ hasTranscription?: boolean;
113
+ hasVideo?: boolean;
101
114
  /** Summarize operations in this conversation */
102
115
  summaries?: Array<SummarizeOperation>;
116
+ imageEvents?: Array<ActivityEvent>;
117
+ speechEvents?: Array<ActivityEvent>;
118
+ transcriptionEvents?: Array<ActivityEvent>;
119
+ videoEvents?: Array<ActivityEvent>;
103
120
  }
104
121
  interface AIStoreState {
105
122
  conversations: Record<string, Conversation>;