@meshagent/meshagent-tailwind 0.38.2 → 0.38.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 (61) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/cjs/Chat.d.ts +11 -3
  3. package/dist/cjs/Chat.js +376 -29
  4. package/dist/cjs/ChatBotView.d.ts +29 -0
  5. package/dist/cjs/ChatBotView.js +491 -0
  6. package/dist/cjs/ChatInput.d.ts +12 -3
  7. package/dist/cjs/ChatInput.js +143 -44
  8. package/dist/cjs/ChatThread.d.ts +17 -3
  9. package/dist/cjs/ChatThread.js +646 -90
  10. package/dist/cjs/ChatTypingIndicator.d.ts +12 -5
  11. package/dist/cjs/ChatTypingIndicator.js +104 -13
  12. package/dist/cjs/FileUploader.d.ts +3 -2
  13. package/dist/cjs/FileUploader.js +35 -11
  14. package/dist/cjs/UploadPill.d.ts +2 -2
  15. package/dist/cjs/UploadPill.js +70 -32
  16. package/dist/cjs/chat-hooks.d.ts +38 -0
  17. package/dist/cjs/chat-hooks.js +390 -0
  18. package/dist/cjs/chat-message.d.ts +11 -0
  19. package/dist/cjs/chat-message.js +33 -0
  20. package/dist/cjs/components/ui/button.d.ts +1 -1
  21. package/dist/cjs/conversation-descriptor.d.ts +59 -0
  22. package/dist/cjs/conversation-descriptor.js +300 -0
  23. package/dist/cjs/file-attachment.d.ts +45 -0
  24. package/dist/cjs/file-attachment.js +171 -0
  25. package/dist/cjs/index.d.ts +5 -0
  26. package/dist/cjs/index.js +5 -0
  27. package/dist/cjs/multi-thread-view.d.ts +18 -0
  28. package/dist/cjs/multi-thread-view.js +88 -0
  29. package/dist/cjs/tools/ui-toolkit.d.ts +1 -1
  30. package/dist/cjs/tools/ui-toolkit.js +2 -1
  31. package/dist/esm/Chat.d.ts +11 -3
  32. package/dist/esm/Chat.js +378 -31
  33. package/dist/esm/ChatBotView.d.ts +29 -0
  34. package/dist/esm/ChatBotView.js +486 -0
  35. package/dist/esm/ChatInput.d.ts +12 -3
  36. package/dist/esm/ChatInput.js +143 -34
  37. package/dist/esm/ChatThread.d.ts +17 -3
  38. package/dist/esm/ChatThread.js +648 -92
  39. package/dist/esm/ChatTypingIndicator.d.ts +12 -5
  40. package/dist/esm/ChatTypingIndicator.js +94 -13
  41. package/dist/esm/FileUploader.d.ts +3 -2
  42. package/dist/esm/FileUploader.js +26 -12
  43. package/dist/esm/UploadPill.d.ts +2 -2
  44. package/dist/esm/UploadPill.js +60 -32
  45. package/dist/esm/chat-hooks.d.ts +38 -0
  46. package/dist/esm/chat-hooks.js +372 -0
  47. package/dist/esm/chat-message.d.ts +11 -0
  48. package/dist/esm/chat-message.js +13 -0
  49. package/dist/esm/components/ui/button.d.ts +1 -1
  50. package/dist/esm/conversation-descriptor.d.ts +59 -0
  51. package/dist/esm/conversation-descriptor.js +280 -0
  52. package/dist/esm/file-attachment.d.ts +45 -0
  53. package/dist/esm/file-attachment.js +151 -0
  54. package/dist/esm/index.d.ts +5 -0
  55. package/dist/esm/index.js +5 -0
  56. package/dist/esm/multi-thread-view.d.ts +18 -0
  57. package/dist/esm/multi-thread-view.js +68 -0
  58. package/dist/esm/tools/ui-toolkit.d.ts +1 -1
  59. package/dist/esm/tools/ui-toolkit.js +2 -1
  60. package/dist/index.css +1 -1
  61. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [0.38.3]
2
+ - Breaking: TypeScript container image summaries now use `references`/`preferredRef` and metadata fields (timestamps/media type), and `inspectImage` returns manifests/layers/content size; `tags`/`size` are removed.
3
+ - `getUsage` now supports filters for users, room, provider, model, and usage type.
4
+ - Room connection and errors improved: `RoomServerException` now carries `statusCode` and `retryable`, and `RoomClient` can route OAuth/secret requests via handler options.
5
+ - React hooks add robust connection retry/backoff, new authorization helpers, and optional secret/OAuth handlers; document connection now supports schema/initial JSON and improved cleanup.
6
+ - New Livekit support (client + protocol channel) and room participant hook; hosted toolkits are now shared per room to avoid duplicate starts.
7
+ - Breaking: `meshagent-react` no longer exports the legacy chat and file-upload modules.
8
+ - Tailwind chat UI is rebuilt with multi-thread support, new thread creation via agent tools, and file attachment utilities, with new exports for thread and conversation helpers.
9
+
1
10
  ## [0.38.2]
2
11
  - Stability
3
12
 
@@ -1,7 +1,15 @@
1
- import { RoomClient, Participant } from "@meshagent/meshagent";
1
+ import type { ReactElement } from "react";
2
+ import { Participant, RoomClient } from "@meshagent/meshagent";
2
3
  export interface ChatProps {
3
4
  room: RoomClient;
4
- path: string;
5
+ path?: string;
5
6
  participants?: Participant[];
7
+ agentName?: string;
8
+ toolkit?: string;
9
+ tool?: string;
10
+ centerComposer?: boolean;
11
+ emptyStateTitle?: string;
12
+ emptyStateDescription?: string;
13
+ onThreadResolved?: (path: string | null, displayName: string | null) => void;
6
14
  }
7
- export declare function Chat({ room, path, participants }: ChatProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare function Chat({ room, path, participants, agentName, toolkit, tool, centerComposer, emptyStateTitle, emptyStateDescription, onThreadResolved, }: ChatProps): ReactElement;
package/dist/cjs/Chat.js CHANGED
@@ -23,55 +23,193 @@ __export(Chat_exports, {
23
23
  module.exports = __toCommonJS(Chat_exports);
24
24
  var import_jsx_runtime = require("react/jsx-runtime");
25
25
  var import_react = require("react");
26
+ var import_meshagent = require("@meshagent/meshagent");
26
27
  var import_meshagent_react = require("@meshagent/meshagent-react");
27
- var import_ChatThread = require("./ChatThread");
28
+ var import_lucide_react = require("lucide-react");
28
29
  var import_ChatInput = require("./ChatInput");
29
- var import_ChatTypingIndicator = require("./ChatTypingIndicator");
30
- var import_ui_toolkit = require("./tools/ui-toolkit");
30
+ var import_ChatThread = require("./ChatThread");
31
+ var import_button = require("./components/ui/button");
31
32
  var import_sonner = require("./components/ui/sonner");
33
+ var import_file_attachment = require("./file-attachment");
34
+ var import_chat_hooks = require("./chat-hooks");
35
+ class NewThreadCancelledError extends Error {
36
+ constructor() {
37
+ super("new thread creation cancelled");
38
+ this.name = "NewThreadCancelledError";
39
+ }
40
+ }
41
+ function normalizeThreadPath(path) {
42
+ const normalizedPath = path?.trim();
43
+ return normalizedPath ? normalizedPath : null;
44
+ }
32
45
  function getParticipantName(participant) {
33
- const name = participant?.getAttribute("name");
34
- return typeof name === "string" ? name : "";
46
+ const name = participant.getAttribute("name");
47
+ return typeof name === "string" ? name.trim() : "";
48
+ }
49
+ function displayParticipantName(name) {
50
+ const normalizedName = name?.trim();
51
+ if (!normalizedName) {
52
+ return "agent";
53
+ }
54
+ return normalizedName.split("@")[0]?.trim() || normalizedName;
55
+ }
56
+ function isAgentParticipant(participant) {
57
+ return participant.role === "agent" || participant.getAttribute("supports_agent_messages") === true;
58
+ }
59
+ function findTargetAgent(room, agentName) {
60
+ const normalizedAgentName = agentName?.trim();
61
+ for (const participant of room.messaging.remoteParticipants) {
62
+ if (!isAgentParticipant(participant)) {
63
+ continue;
64
+ }
65
+ if (normalizedAgentName && getParticipantName(participant) !== normalizedAgentName) {
66
+ continue;
67
+ }
68
+ return participant;
69
+ }
70
+ return null;
71
+ }
72
+ function ensureOperationActive(operationId, activeOperationRef) {
73
+ if (activeOperationRef.current !== operationId) {
74
+ throw new NewThreadCancelledError();
75
+ }
76
+ }
77
+ function delay(milliseconds) {
78
+ return new Promise((resolve) => {
79
+ window.setTimeout(resolve, milliseconds);
80
+ });
35
81
  }
36
- function Chat({ room, path, participants }) {
82
+ async function waitForTargetAgent(params) {
83
+ const { room, agentName, operationId, activeOperationRef } = params;
84
+ while (true) {
85
+ ensureOperationActive(operationId, activeOperationRef);
86
+ const targetAgent = findTargetAgent(room, agentName);
87
+ if (targetAgent) {
88
+ return targetAgent;
89
+ }
90
+ await delay(250);
91
+ }
92
+ }
93
+ async function waitForToolkitAvailable(params) {
37
94
  const {
95
+ room,
96
+ participantId,
97
+ toolkit,
98
+ operationId,
99
+ activeOperationRef
100
+ } = params;
101
+ while (true) {
102
+ ensureOperationActive(operationId, activeOperationRef);
103
+ try {
104
+ const toolkits = await room.agents.listToolkits({ participantId, timeout: 1e3 });
105
+ if (toolkits.some((toolkitDescription) => toolkitDescription.name === toolkit)) {
106
+ return;
107
+ }
108
+ } catch {
109
+ }
110
+ await delay(250);
111
+ }
112
+ }
113
+ function getStringField(record, key) {
114
+ const value = record[key];
115
+ return typeof value === "string" && value.trim() !== "" ? value.trim() : null;
116
+ }
117
+ function parseThreadToolResult(toolkit, tool, content) {
118
+ const path = getStringField(content.json, "path");
119
+ if (!path) {
120
+ throw new Error(`${toolkit}.${tool} response missing path`);
121
+ }
122
+ return {
123
+ path,
124
+ displayName: getStringField(content.json, "name")
125
+ };
126
+ }
127
+ function describeError(error) {
128
+ if (error instanceof Error && error.message.trim() !== "") {
129
+ return error.message;
130
+ }
131
+ return `${error}`;
132
+ }
133
+ function EmptyState({
134
+ title,
135
+ description
136
+ }) {
137
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mx-auto flex max-w-2xl flex-col items-center justify-center px-6 py-20 text-center", children: [
138
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-4xl font-semibold tracking-tight text-foreground sm:text-5xl", children: title }),
139
+ description?.trim() ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "mt-3 max-w-xl text-sm leading-6 text-muted-foreground sm:text-base", children: description }) : null
140
+ ] });
141
+ }
142
+ function ErrorBanner({ message }) {
143
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mx-auto w-full max-w-[912px] whitespace-pre-wrap rounded-3xl border border-destructive/30 bg-destructive/5 px-6 py-5 text-sm text-destructive", children: message });
144
+ }
145
+ function ResolvedChatView({
146
+ room,
147
+ path,
148
+ participants,
149
+ agentName,
150
+ emptyStateTitle,
151
+ emptyStateDescription,
152
+ showNewThreadButton = false,
153
+ onStartNewThread
154
+ }) {
155
+ const {
156
+ document,
38
157
  messages,
39
158
  sendMessage,
40
159
  selectAttachments,
41
160
  attachments,
42
161
  setAttachments,
43
- schemaFileExists,
162
+ onlineParticipants,
163
+ localParticipantName,
44
164
  cancelRequest
45
- } = (0, import_meshagent_react.useChat)({ room, path, participants });
46
- const { thinking } = (0, import_meshagent_react.useRoomIndicators)({ room, path });
47
- const toolkits = (0, import_react.useMemo)(() => [
48
- new import_ui_toolkit.UIToolkit()
49
- ], []);
50
- (0, import_meshagent_react.useClientToolkits)({ room, toolkits, public: false });
51
- const onTextChange = (0, import_react.useCallback)((_) => {
52
- const removeParticipant = room.messaging.remoteParticipants;
53
- for (const part of removeParticipant) {
165
+ } = (0, import_chat_hooks.useChatThread)({ room, path, participants, agentName });
166
+ const { typing, thinking } = (0, import_meshagent_react.useRoomIndicators)({ room, path });
167
+ const threadStatus = (0, import_chat_hooks.useThreadStatus)({ room, path, agentName });
168
+ const [showCompletedToolCalls, setShowCompletedToolCalls] = (0, import_react.useState)(false);
169
+ const onTextChange = (0, import_react.useCallback)(() => {
170
+ for (const participant of onlineParticipants) {
54
171
  room.messaging.sendMessage({
55
- to: part,
172
+ to: participant,
56
173
  type: "typing",
57
174
  message: { path }
58
175
  });
59
176
  }
60
- }, [room, path]);
61
- const localParticipantName = getParticipantName(room?.localParticipant);
62
- if (schemaFileExists === false) {
63
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-col flex-1 min-h-0 gap-2 p-4", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-red-500", children: 'No AI agent found in this room. Run `meshagent chatbot join --room [room-name] --agent-name "Chat Agent" --name "Chat Friend"` and try again.' }) });
64
- }
65
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col flex-1 min-h-0 gap-2 p-0", children: [
177
+ }, [onlineParticipants, path, room]);
178
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex min-h-0 flex-1 flex-col", children: [
179
+ showNewThreadButton && onStartNewThread ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-4 pt-3", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mx-auto flex w-full max-w-[912px] justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
180
+ import_button.Button,
181
+ {
182
+ type: "button",
183
+ variant: "outline",
184
+ size: "sm",
185
+ className: "rounded-full shadow-xs",
186
+ onClick: onStartNewThread,
187
+ children: [
188
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Plus, { className: "mr-2 h-4 w-4" }),
189
+ "New thread"
190
+ ]
191
+ }
192
+ ) }) }) : null,
66
193
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
67
194
  import_ChatThread.ChatThread,
68
195
  {
69
196
  room,
197
+ path,
70
198
  messages,
71
- localParticipantName
199
+ isLoading: document === null,
200
+ localParticipantName,
201
+ showCompletedToolCalls,
202
+ onShowCompletedToolCallsChanged: setShowCompletedToolCalls,
203
+ typing,
204
+ thinking,
205
+ threadStatusText: threadStatus.text,
206
+ threadStatusStartedAt: threadStatus.startedAt,
207
+ threadStatusMode: threadStatus.mode,
208
+ onCancelRequest: cancelRequest,
209
+ emptyStateTitle,
210
+ emptyStateDescription
72
211
  }
73
212
  ),
74
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_ChatTypingIndicator.ChatTypingIndicator, { room, path }),
75
213
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
76
214
  import_ChatInput.ChatInput,
77
215
  {
@@ -79,11 +217,220 @@ function Chat({ room, path, participants }) {
79
217
  attachments,
80
218
  onFilesSelected: selectAttachments,
81
219
  setAttachments,
82
- onTextChange,
83
- onCancelRequest: cancelRequest,
84
- showCancelButton: thinking
220
+ onTextChange
85
221
  }
86
- ),
222
+ )
223
+ ] });
224
+ }
225
+ function Chat({
226
+ room,
227
+ path,
228
+ participants,
229
+ agentName,
230
+ toolkit = "chat",
231
+ tool = "new_thread",
232
+ centerComposer = true,
233
+ emptyStateTitle,
234
+ emptyStateDescription,
235
+ onThreadResolved
236
+ }) {
237
+ const [internalThreadPath, setInternalThreadPath] = (0, import_react.useState)(null);
238
+ const [newThreadDraft, setNewThreadDraft] = (0, import_react.useState)("");
239
+ const [newThreadAttachments, setNewThreadAttachments] = (0, import_react.useState)([]);
240
+ const [newThreadError, setNewThreadError] = (0, import_react.useState)(null);
241
+ const [creatingNewThread, setCreatingNewThread] = (0, import_react.useState)(false);
242
+ const [waitingForAgent, setWaitingForAgent] = (0, import_react.useState)(false);
243
+ const activeOperationRef = (0, import_react.useRef)(0);
244
+ const controlledPath = (0, import_react.useMemo)(() => normalizeThreadPath(path), [path]);
245
+ const managesOwnThread = controlledPath === null;
246
+ const activePath = controlledPath ?? internalThreadPath;
247
+ (0, import_react.useEffect)(() => {
248
+ return () => {
249
+ activeOperationRef.current += 1;
250
+ };
251
+ }, []);
252
+ (0, import_react.useEffect)(() => {
253
+ if (controlledPath !== null) {
254
+ setInternalThreadPath(null);
255
+ }
256
+ }, [controlledPath]);
257
+ (0, import_react.useEffect)(() => {
258
+ activeOperationRef.current += 1;
259
+ setInternalThreadPath(null);
260
+ setNewThreadDraft("");
261
+ setNewThreadAttachments([]);
262
+ setNewThreadError(null);
263
+ setCreatingNewThread(false);
264
+ setWaitingForAgent(false);
265
+ }, [agentName, managesOwnThread, room]);
266
+ const selectNewThreadAttachments = (0, import_react.useCallback)((files) => {
267
+ const nextAttachments = files.map((file) => new import_file_attachment.MeshagentFileUpload(
268
+ room,
269
+ `uploaded-files/${file.name}`,
270
+ (0, import_file_attachment.fileToAsyncIterable)(file),
271
+ file.size
272
+ ));
273
+ setNewThreadAttachments((currentAttachments) => [...currentAttachments, ...nextAttachments]);
274
+ }, [room]);
275
+ const cancelPendingNewThread = (0, import_react.useCallback)(() => {
276
+ activeOperationRef.current += 1;
277
+ setCreatingNewThread(false);
278
+ setWaitingForAgent(false);
279
+ setNewThreadError(null);
280
+ }, []);
281
+ const openNewThreadComposer = (0, import_react.useCallback)(() => {
282
+ activeOperationRef.current += 1;
283
+ setInternalThreadPath(null);
284
+ setNewThreadDraft("");
285
+ setNewThreadAttachments([]);
286
+ setNewThreadError(null);
287
+ setCreatingNewThread(false);
288
+ setWaitingForAgent(false);
289
+ }, []);
290
+ const handleCreateThread = (0, import_react.useCallback)(async () => {
291
+ const text = newThreadDraft.trim();
292
+ const hasDraft = text !== "" || newThreadAttachments.length > 0;
293
+ if (!hasDraft || creatingNewThread || waitingForAgent) {
294
+ return;
295
+ }
296
+ const operationId = activeOperationRef.current + 1;
297
+ activeOperationRef.current = operationId;
298
+ const initialTargetAgent = findTargetAgent(room, agentName);
299
+ setWaitingForAgent(initialTargetAgent === null);
300
+ setCreatingNewThread(initialTargetAgent !== null);
301
+ setNewThreadError(null);
302
+ try {
303
+ const targetAgent = initialTargetAgent ?? await waitForTargetAgent({
304
+ room,
305
+ agentName,
306
+ operationId,
307
+ activeOperationRef
308
+ });
309
+ ensureOperationActive(operationId, activeOperationRef);
310
+ if (initialTargetAgent === null) {
311
+ setWaitingForAgent(false);
312
+ setCreatingNewThread(true);
313
+ }
314
+ await waitForToolkitAvailable({
315
+ room,
316
+ participantId: targetAgent.id,
317
+ toolkit,
318
+ operationId,
319
+ activeOperationRef
320
+ });
321
+ ensureOperationActive(operationId, activeOperationRef);
322
+ const response = await room.agents.invokeTool({
323
+ toolkit,
324
+ tool,
325
+ arguments: {
326
+ message: {
327
+ text,
328
+ attachments: newThreadAttachments.map((attachment) => ({ path: attachment.path }))
329
+ }
330
+ }
331
+ });
332
+ ensureOperationActive(operationId, activeOperationRef);
333
+ if (!(response instanceof import_meshagent.JsonContent)) {
334
+ throw new Error(`${toolkit}.${tool} returned non-JSON content`);
335
+ }
336
+ const result = parseThreadToolResult(toolkit, tool, response);
337
+ if (controlledPath === null) {
338
+ setInternalThreadPath(result.path);
339
+ }
340
+ setNewThreadDraft("");
341
+ setNewThreadAttachments([]);
342
+ setNewThreadError(null);
343
+ setCreatingNewThread(false);
344
+ setWaitingForAgent(false);
345
+ onThreadResolved?.(result.path, result.displayName);
346
+ } catch (error) {
347
+ if (error instanceof NewThreadCancelledError) {
348
+ return;
349
+ }
350
+ setCreatingNewThread(false);
351
+ setWaitingForAgent(false);
352
+ setNewThreadError(describeError(error));
353
+ }
354
+ }, [
355
+ agentName,
356
+ controlledPath,
357
+ creatingNewThread,
358
+ newThreadAttachments,
359
+ newThreadDraft,
360
+ onThreadResolved,
361
+ room,
362
+ toolkit,
363
+ tool,
364
+ waitingForAgent
365
+ ]);
366
+ (0, import_react.useEffect)(() => {
367
+ if (!managesOwnThread) {
368
+ return;
369
+ }
370
+ const handleKeyDown = (event) => {
371
+ if (!(event.ctrlKey || event.metaKey) || event.key.toLowerCase() !== "n") {
372
+ return;
373
+ }
374
+ event.preventDefault();
375
+ openNewThreadComposer();
376
+ };
377
+ window.addEventListener("keydown", handleKeyDown);
378
+ return () => {
379
+ window.removeEventListener("keydown", handleKeyDown);
380
+ };
381
+ }, [managesOwnThread, openNewThreadComposer]);
382
+ const targetAgentLabel = (0, import_react.useMemo)(() => {
383
+ const knownAgentName = agentName?.trim();
384
+ if (knownAgentName) {
385
+ return displayParticipantName(knownAgentName);
386
+ }
387
+ const targetAgent = findTargetAgent(room);
388
+ return displayParticipantName(targetAgent ? getParticipantName(targetAgent) : null);
389
+ }, [agentName, room]);
390
+ const pendingStatusText = waitingForAgent ? `Waiting for ${targetAgentLabel} to be ready.` : creatingNewThread ? `Starting a thread with ${targetAgentLabel}.` : null;
391
+ const composer = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
392
+ import_ChatInput.ChatInput,
393
+ {
394
+ onSubmit: handleCreateThread,
395
+ attachments: newThreadAttachments,
396
+ onFilesSelected: selectNewThreadAttachments,
397
+ setAttachments: setNewThreadAttachments,
398
+ value: newThreadDraft,
399
+ onValueChange: setNewThreadDraft,
400
+ clearOnSubmit: false,
401
+ showCancelButton: creatingNewThread || waitingForAgent,
402
+ onCancelRequest: cancelPendingNewThread,
403
+ disabled: creatingNewThread || waitingForAgent,
404
+ placeholder: agentName?.trim() ? `Type a message or @${displayParticipantName(agentName)}` : "Type a message"
405
+ }
406
+ );
407
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex min-h-0 flex-1 flex-col", children: [
408
+ activePath ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
409
+ ResolvedChatView,
410
+ {
411
+ room,
412
+ path: activePath,
413
+ participants,
414
+ agentName,
415
+ emptyStateTitle,
416
+ emptyStateDescription,
417
+ showNewThreadButton: managesOwnThread,
418
+ onStartNewThread: openNewThreadComposer
419
+ },
420
+ activePath
421
+ ) : centerComposer ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex min-h-0 flex-1 items-center justify-center px-4 py-6", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "w-full max-w-[912px] space-y-5", children: [
422
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "space-y-2 text-center", children: [
423
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: "text-4xl font-semibold tracking-tight text-foreground sm:text-5xl", children: "Start a new thread" }),
424
+ pendingStatusText ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm text-muted-foreground sm:text-base", children: pendingStatusText }) : null
425
+ ] }),
426
+ composer,
427
+ newThreadError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorBanner, { message: newThreadError }) : null
428
+ ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex min-h-0 flex-1 flex-col", children: [
429
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex-1", children: emptyStateTitle ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmptyState, { title: emptyStateTitle, description: emptyStateDescription }) : null }),
430
+ pendingStatusText ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-4 pb-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "mx-auto w-full max-w-[912px] text-sm text-muted-foreground", children: pendingStatusText }) }) : null,
431
+ newThreadError ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "px-4 pb-2", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorBanner, { message: newThreadError }) }) : null,
432
+ composer
433
+ ] }),
87
434
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_sonner.Toaster, {})
88
435
  ] });
89
436
  }
@@ -0,0 +1,29 @@
1
+ import type { ReactElement } from "react";
2
+ import { Participant, RoomClient } from "@meshagent/meshagent";
3
+ import { ChatThreadDisplayMode } from "./conversation-descriptor";
4
+ export { ChatThreadDisplayMode, chatDocumentPath, resolvedThreadListPath, } from "./conversation-descriptor";
5
+ export interface ChatBotViewProps {
6
+ room: RoomClient;
7
+ path?: string;
8
+ documentPath?: string;
9
+ participants?: Participant[];
10
+ agentName?: string;
11
+ threadDisplayMode?: ChatThreadDisplayMode;
12
+ threadDir?: string;
13
+ threadListPath?: string;
14
+ toolkit?: string;
15
+ tool?: string;
16
+ centerComposer?: boolean;
17
+ emptyStateTitle?: string;
18
+ emptyStateDescription?: string;
19
+ selectedThreadPath?: string | null;
20
+ selectedThreadDisplayName?: string | null;
21
+ onSelectedThreadPathChanged?: (path: string | null) => void;
22
+ onSelectedThreadResolved?: (path: string | null, displayName: string | null) => void;
23
+ onThreadResolved?: (path: string | null, displayName: string | null) => void;
24
+ newThreadResetVersion?: number;
25
+ showThreadList?: boolean;
26
+ threadListWidth?: number;
27
+ threadListCollapsedHeight?: number;
28
+ }
29
+ export declare function ChatBotView({ room, path, documentPath, participants, agentName, threadDisplayMode, threadDir, threadListPath, toolkit, tool, centerComposer, emptyStateTitle, emptyStateDescription, selectedThreadPath, onSelectedThreadPathChanged, onSelectedThreadResolved, onThreadResolved, newThreadResetVersion, showThreadList, threadListWidth, threadListCollapsedHeight, }: ChatBotViewProps): ReactElement;