Skip to content

Commit 0f5a353

Browse files
committed
Add ct-toast and use it for omnibot
1 parent fcd10cf commit 0f5a353

File tree

6 files changed

+882
-4
lines changed

6 files changed

+882
-4
lines changed

packages/patterns/default-app.tsx

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
/// <cts-enable />
22
import {
3+
BuiltInLLMMessage,
34
Cell,
45
cell,
56
derive,
67
handler,
78
ifElse,
9+
lift,
810
NAME,
911
navigateTo,
1012
recipe,
@@ -101,6 +103,46 @@ const toggle = handler<any, { value: Cell<boolean> }>((_, { value }) => {
101103
value.set(!value.get());
102104
});
103105

106+
const messagesToNotifications = lift<
107+
{
108+
messages: BuiltInLLMMessage[];
109+
seen: Cell<number>;
110+
notifications: Cell<{ id: string; text: string; timestamp: number }[]>;
111+
}
112+
>(({ messages, seen, notifications }) => {
113+
if (messages.length > 0) {
114+
if (seen.get() >= messages.length) return;
115+
116+
const latestMessage = messages[messages.length - 1];
117+
if (latestMessage.role === "assistant") {
118+
const contentText = typeof latestMessage.content === "string"
119+
? latestMessage.content
120+
: latestMessage.content.map((part) => {
121+
if (part.type === "text") {
122+
return part.text;
123+
} else if (part.type === "tool-call") {
124+
return `Tool call: ${part.toolName}`;
125+
} else if (part.type === "tool-result") {
126+
return part.output.type === "text"
127+
? part.output.value
128+
: JSON.stringify(part.output.value);
129+
} else if (part.type === "image") {
130+
return "[Image]";
131+
}
132+
return "";
133+
}).join("");
134+
135+
notifications.push({
136+
id: Math.random().toString(36),
137+
text: contentText,
138+
timestamp: Date.now(),
139+
});
140+
141+
seen.set(messages.length);
142+
}
143+
}
144+
});
145+
104146
export default recipe<CharmsListInput, CharmsListOutput>(
105147
"DefaultCharmList",
106148
(_) => {
@@ -110,6 +152,8 @@ export default recipe<CharmsListInput, CharmsListOutput>(
110152
);
111153
const index = BacklinksIndex({ allCharms });
112154
const fabExpanded = cell(false);
155+
const notifications = cell<{ text: string; timestamp: number }[]>([]);
156+
const seen = cell<number>(0);
113157

114158
const omnibot = Chatbot({
115159
messages: [],
@@ -126,6 +170,14 @@ export default recipe<CharmsListInput, CharmsListOutput>(
126170
},
127171
});
128172

173+
messagesToNotifications({
174+
messages: omnibot.messages,
175+
seen: seen as unknown as Cell<number>,
176+
notifications: notifications as unknown as Cell<
177+
{ id: string; text: string; timestamp: number }[]
178+
>,
179+
});
180+
129181
return {
130182
backlinksIndex: index,
131183
[NAME]: str`DefaultCharmList (${allCharms.length})`,
@@ -215,10 +267,20 @@ export default recipe<CharmsListInput, CharmsListOutput>(
215267
{omnibot.ui.chatLog as any}
216268
</div>
217269
),
218-
fabUI: ifElse(
219-
fabExpanded,
220-
omnibot.ui.promptInput,
221-
<ct-button onClick={toggle({ value: fabExpanded })}></ct-button>,
270+
fabUI: (
271+
<>
272+
<ct-toast-stack
273+
$notifications={notifications}
274+
position="top-right"
275+
auto-dismiss={5000}
276+
max-toasts={5}
277+
/>
278+
{ifElse(
279+
fabExpanded,
280+
omnibot.ui.promptInput,
281+
<ct-button onClick={toggle({ value: fabExpanded })}></ct-button>,
282+
)}
283+
</>
222284
),
223285
};
224286
},

0 commit comments

Comments
 (0)