@meshagent/meshagent-tailwind 0.38.4 → 0.39.1
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.
- package/CHANGELOG.md +12 -0
- package/dist/cjs/Chat.js +3 -3
- package/dist/cjs/ChatBotView.js +132 -40
- package/dist/cjs/ChatThread.js +1 -1
- package/dist/cjs/chat-hooks.d.ts +41 -5
- package/dist/cjs/chat-hooks.js +246 -55
- package/dist/esm/Chat.js +3 -3
- package/dist/esm/ChatBotView.js +142 -42
- package/dist/esm/ChatThread.js +1 -1
- package/dist/esm/chat-hooks.d.ts +41 -5
- package/dist/esm/chat-hooks.js +246 -55
- package/dist/index.css +1 -1
- package/package.json +5 -5
package/dist/cjs/chat-hooks.js
CHANGED
|
@@ -18,6 +18,8 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var chat_hooks_exports = {};
|
|
20
20
|
__export(chat_hooks_exports, {
|
|
21
|
+
ChatThreadStatusState: () => ChatThreadStatusState,
|
|
22
|
+
PendingAgentMessage: () => PendingAgentMessage,
|
|
21
23
|
formatThreadStatusText: () => formatThreadStatusText,
|
|
22
24
|
useChatThread: () => useChatThread,
|
|
23
25
|
useThreadStatus: () => useThreadStatus
|
|
@@ -27,6 +29,103 @@ var import_react = require("react");
|
|
|
27
29
|
var import_meshagent = require("@meshagent/meshagent");
|
|
28
30
|
var import_meshagent_react = require("@meshagent/meshagent-react");
|
|
29
31
|
var import_file_attachment = require("./file-attachment");
|
|
32
|
+
const agentTurnSteerType = "meshagent.agent.turn.steer";
|
|
33
|
+
class PendingAgentMessage {
|
|
34
|
+
messageId;
|
|
35
|
+
messageType;
|
|
36
|
+
threadPath;
|
|
37
|
+
text;
|
|
38
|
+
attachments;
|
|
39
|
+
senderName;
|
|
40
|
+
awaitingAcceptance;
|
|
41
|
+
awaitingOnline;
|
|
42
|
+
constructor({
|
|
43
|
+
messageId,
|
|
44
|
+
messageType,
|
|
45
|
+
threadPath,
|
|
46
|
+
text,
|
|
47
|
+
attachments,
|
|
48
|
+
senderName,
|
|
49
|
+
awaitingAcceptance = false,
|
|
50
|
+
awaitingOnline = false
|
|
51
|
+
}) {
|
|
52
|
+
this.messageId = messageId;
|
|
53
|
+
this.messageType = messageType;
|
|
54
|
+
this.threadPath = threadPath;
|
|
55
|
+
this.text = text;
|
|
56
|
+
this.attachments = attachments;
|
|
57
|
+
this.senderName = senderName;
|
|
58
|
+
this.awaitingAcceptance = awaitingAcceptance;
|
|
59
|
+
this.awaitingOnline = awaitingOnline;
|
|
60
|
+
}
|
|
61
|
+
static fromQueueJson(json) {
|
|
62
|
+
const content = json["content"];
|
|
63
|
+
const textParts = [];
|
|
64
|
+
const attachments = [];
|
|
65
|
+
if (Array.isArray(content)) {
|
|
66
|
+
for (const item of content) {
|
|
67
|
+
if (item == null || typeof item !== "object") {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const obj = item;
|
|
71
|
+
const type = obj["type"];
|
|
72
|
+
if (type === "text") {
|
|
73
|
+
const text = obj["text"];
|
|
74
|
+
if (typeof text === "string" && text.trim().length > 0) {
|
|
75
|
+
textParts.push(text);
|
|
76
|
+
}
|
|
77
|
+
} else if (type === "file") {
|
|
78
|
+
const url = obj["url"];
|
|
79
|
+
if (typeof url === "string" && url.trim().length > 0) {
|
|
80
|
+
attachments.push(url);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const senderName = json["sender_name"];
|
|
86
|
+
const messageType = json["message_type"];
|
|
87
|
+
const messageId = json["message_id"];
|
|
88
|
+
const threadPath = json["thread_id"];
|
|
89
|
+
return new PendingAgentMessage({
|
|
90
|
+
messageId: typeof messageId === "string" ? messageId : crypto.randomUUID(),
|
|
91
|
+
messageType: typeof messageType === "string" ? messageType : agentTurnSteerType,
|
|
92
|
+
threadPath: typeof threadPath === "string" ? threadPath : "",
|
|
93
|
+
text: textParts.join("\n\n"),
|
|
94
|
+
attachments,
|
|
95
|
+
senderName: typeof senderName === "string" && senderName.trim().length > 0 ? senderName.trim() : void 0,
|
|
96
|
+
awaitingOnline: false
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
class ChatThreadStatusState {
|
|
101
|
+
text;
|
|
102
|
+
startedAt;
|
|
103
|
+
mode;
|
|
104
|
+
turnId;
|
|
105
|
+
pendingMessages;
|
|
106
|
+
pendingItemId;
|
|
107
|
+
supportsAgentMessages;
|
|
108
|
+
constructor({
|
|
109
|
+
text,
|
|
110
|
+
startedAt,
|
|
111
|
+
mode,
|
|
112
|
+
turnId,
|
|
113
|
+
pendingMessages = [],
|
|
114
|
+
pendingItemId,
|
|
115
|
+
supportsAgentMessages: supportsAgentMessages2 = false
|
|
116
|
+
}) {
|
|
117
|
+
this.text = text;
|
|
118
|
+
this.startedAt = startedAt;
|
|
119
|
+
this.mode = mode;
|
|
120
|
+
this.turnId = turnId;
|
|
121
|
+
this.pendingMessages = pendingMessages;
|
|
122
|
+
this.pendingItemId = pendingItemId;
|
|
123
|
+
this.supportsAgentMessages = supportsAgentMessages2;
|
|
124
|
+
}
|
|
125
|
+
get hasStatus() {
|
|
126
|
+
return this.text != null && this.text.trim().length > 0;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
30
129
|
function getParticipantName(participant) {
|
|
31
130
|
const name = participant.getAttribute("name");
|
|
32
131
|
return typeof name === "string" ? name.trim() : "";
|
|
@@ -109,73 +208,161 @@ function threadStatusAttributeCandidates(path, prefix) {
|
|
|
109
208
|
}
|
|
110
209
|
return [`${prefix}.${path}`, `${prefix}./${path}`];
|
|
111
210
|
}
|
|
112
|
-
function
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
211
|
+
function parsePendingMessagesStatus(participant, path) {
|
|
212
|
+
for (const key of threadStatusAttributeCandidates(
|
|
213
|
+
path,
|
|
214
|
+
"thread.status.pending_messages"
|
|
215
|
+
)) {
|
|
216
|
+
const value = participant.getAttribute(key);
|
|
217
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
const decoded = JSON.parse(value.trim());
|
|
222
|
+
if (decoded != null && typeof decoded === "object" && !Array.isArray(decoded)) {
|
|
223
|
+
return decoded;
|
|
224
|
+
}
|
|
225
|
+
} catch (_) {
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return void 0;
|
|
229
|
+
}
|
|
230
|
+
function resolveThreadStatus({
|
|
231
|
+
room,
|
|
232
|
+
path,
|
|
233
|
+
agentName,
|
|
234
|
+
previous
|
|
235
|
+
}) {
|
|
236
|
+
const keyCandidates = threadStatusAttributeCandidates(path, "thread.status");
|
|
237
|
+
const textKeyCandidates = threadStatusAttributeCandidates(path, "thread.status.text");
|
|
238
|
+
const modeKeyCandidates = threadStatusAttributeCandidates(path, "thread.status.mode");
|
|
239
|
+
const startedAtKeyCandidates = threadStatusAttributeCandidates(path, "thread.status.started_at");
|
|
240
|
+
const pendingItemIdKeyCandidates = threadStatusAttributeCandidates(path, "thread.status.pending_item_id");
|
|
241
|
+
const candidates = agentName != null ? room.messaging.remoteParticipants.filter(
|
|
242
|
+
(participant) => participant.getAttribute("name") === agentName
|
|
243
|
+
) : room.messaging.remoteParticipants.filter(
|
|
244
|
+
(participant) => participant.role === "agent" || supportsAgentMessages(participant)
|
|
245
|
+
);
|
|
246
|
+
let nextStatus;
|
|
247
|
+
let nextMode;
|
|
248
|
+
let nextStartedAt;
|
|
249
|
+
let nextTurnId;
|
|
250
|
+
let nextPendingMessages = [];
|
|
251
|
+
let nextPendingItemId;
|
|
252
|
+
let nextSupportsAgentMessages = false;
|
|
124
253
|
for (const participant of candidates) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
254
|
+
if (supportsAgentMessages(participant)) {
|
|
255
|
+
nextSupportsAgentMessages = true;
|
|
256
|
+
}
|
|
257
|
+
if (nextStatus == null) {
|
|
258
|
+
for (const key of textKeyCandidates) {
|
|
128
259
|
const value = participant.getAttribute(key);
|
|
129
|
-
if (typeof value === "string" && value.trim()
|
|
130
|
-
|
|
260
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
261
|
+
nextStatus = value.trim();
|
|
131
262
|
break;
|
|
132
263
|
}
|
|
133
264
|
}
|
|
134
265
|
}
|
|
135
|
-
if (
|
|
136
|
-
for (const key of
|
|
266
|
+
if (nextStatus == null) {
|
|
267
|
+
for (const key of keyCandidates) {
|
|
137
268
|
const value = participant.getAttribute(key);
|
|
138
|
-
if (typeof value
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
const normalized = value.trim().toLowerCase();
|
|
142
|
-
if (normalized === "busy" || normalized === "steerable") {
|
|
143
|
-
mode = normalized;
|
|
269
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
270
|
+
nextStatus = value.trim();
|
|
144
271
|
break;
|
|
145
272
|
}
|
|
146
273
|
}
|
|
147
274
|
}
|
|
148
|
-
|
|
149
|
-
|
|
275
|
+
const pendingStatus = parsePendingMessagesStatus(participant, path);
|
|
276
|
+
if (pendingStatus != null && typeof pendingStatus === "object") {
|
|
277
|
+
if (nextTurnId == null) {
|
|
278
|
+
const turnId = pendingStatus["turn_id"];
|
|
279
|
+
if (typeof turnId === "string" && turnId.trim().length > 0) {
|
|
280
|
+
nextTurnId = turnId.trim();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (nextPendingMessages.length === 0) {
|
|
284
|
+
const messages = pendingStatus["messages"];
|
|
285
|
+
if (Array.isArray(messages)) {
|
|
286
|
+
nextPendingMessages = messages.flatMap((item) => {
|
|
287
|
+
if (item != null && typeof item === "object") {
|
|
288
|
+
return [
|
|
289
|
+
PendingAgentMessage.fromQueueJson(
|
|
290
|
+
item
|
|
291
|
+
)
|
|
292
|
+
];
|
|
293
|
+
}
|
|
294
|
+
return [];
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (nextMode == null) {
|
|
300
|
+
for (const key of modeKeyCandidates) {
|
|
301
|
+
const value = participant.getAttribute(key);
|
|
302
|
+
if (typeof value === "string") {
|
|
303
|
+
const normalized = value.trim().toLowerCase();
|
|
304
|
+
if (normalized === "busy" || normalized === "steerable") {
|
|
305
|
+
nextMode = normalized;
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
if (nextStartedAt == null) {
|
|
312
|
+
for (const key of startedAtKeyCandidates) {
|
|
150
313
|
const value = participant.getAttribute(key);
|
|
151
|
-
if (typeof value !== "string"
|
|
314
|
+
if (typeof value !== "string") {
|
|
152
315
|
continue;
|
|
153
316
|
}
|
|
154
|
-
const
|
|
317
|
+
const normalized = value.trim();
|
|
318
|
+
if (normalized.length === 0) {
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
const parsed = new Date(normalized);
|
|
155
322
|
if (!Number.isNaN(parsed.getTime())) {
|
|
156
|
-
|
|
323
|
+
nextStartedAt = parsed;
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (nextPendingItemId == null) {
|
|
329
|
+
for (const key of pendingItemIdKeyCandidates) {
|
|
330
|
+
const value = participant.getAttribute(key);
|
|
331
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
332
|
+
nextPendingItemId = value.trim();
|
|
157
333
|
break;
|
|
158
334
|
}
|
|
159
335
|
}
|
|
160
336
|
}
|
|
337
|
+
if (nextStatus != null && nextMode != null && nextStartedAt != null && nextTurnId != null && nextPendingItemId != null) {
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
161
340
|
}
|
|
162
|
-
if (
|
|
163
|
-
return {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
startedAt: null,
|
|
167
|
-
supportsAgentMessages: hasAgentMessageSupport
|
|
168
|
-
};
|
|
341
|
+
if (nextStatus == null) {
|
|
342
|
+
return new ChatThreadStatusState({
|
|
343
|
+
supportsAgentMessages: nextSupportsAgentMessages
|
|
344
|
+
});
|
|
169
345
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
346
|
+
nextMode ??= "busy";
|
|
347
|
+
nextStartedAt ??= previous?.hasStatus === true ? previous.startedAt : /* @__PURE__ */ new Date();
|
|
348
|
+
return new ChatThreadStatusState({
|
|
349
|
+
text: nextStatus,
|
|
350
|
+
startedAt: nextStartedAt,
|
|
351
|
+
mode: nextMode,
|
|
352
|
+
turnId: nextTurnId,
|
|
353
|
+
pendingMessages: nextPendingMessages,
|
|
354
|
+
pendingItemId: nextPendingItemId,
|
|
355
|
+
supportsAgentMessages: nextSupportsAgentMessages
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
function pendingMessagesEqual(left, right) {
|
|
359
|
+
return left.length === right.length && left.every((message, index) => {
|
|
360
|
+
const other = right[index];
|
|
361
|
+
return message.messageId === other.messageId && message.messageType === other.messageType && message.text === other.text && stringArraysEqual(message.attachments, other.attachments) && message.senderName === other.senderName;
|
|
362
|
+
});
|
|
176
363
|
}
|
|
177
364
|
function threadStatusEquals(left, right) {
|
|
178
|
-
return left.text === right.text && left.mode === right.mode && left.startedAt?.getTime() === right.startedAt?.getTime() && left.supportsAgentMessages === right.supportsAgentMessages;
|
|
365
|
+
return left.text === right.text && left.mode === right.mode && left.startedAt?.getTime() === right.startedAt?.getTime() && left.turnId === right.turnId && left.pendingItemId === right.pendingItemId && pendingMessagesEqual(left.pendingMessages, right.pendingMessages) && left.supportsAgentMessages === right.supportsAgentMessages;
|
|
179
366
|
}
|
|
180
367
|
function formatThreadStatusText(text, startedAt) {
|
|
181
368
|
if (!(startedAt instanceof Date) || Number.isNaN(startedAt.getTime())) {
|
|
@@ -360,8 +547,15 @@ function useThreadStatus({ room, path, agentName }) {
|
|
|
360
547
|
const [status, setStatus] = (0, import_react.useState)(() => resolveThreadStatus({ room, path, agentName }));
|
|
361
548
|
(0, import_react.useEffect)(() => {
|
|
362
549
|
const updateStatus = () => {
|
|
363
|
-
|
|
364
|
-
|
|
550
|
+
setStatus((currentStatus) => {
|
|
551
|
+
const nextStatus = resolveThreadStatus({
|
|
552
|
+
room,
|
|
553
|
+
path,
|
|
554
|
+
agentName,
|
|
555
|
+
previous: currentStatus
|
|
556
|
+
});
|
|
557
|
+
return threadStatusEquals(currentStatus, nextStatus) ? currentStatus : nextStatus;
|
|
558
|
+
});
|
|
365
559
|
};
|
|
366
560
|
const roomSubscription = (0, import_meshagent_react.subscribe)(room.listen(), {
|
|
367
561
|
next: (event) => {
|
|
@@ -372,18 +566,15 @@ function useThreadStatus({ room, path, agentName }) {
|
|
|
372
566
|
updateStatus();
|
|
373
567
|
}
|
|
374
568
|
});
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
room.messaging.on("participant_added", handleParticipantsChanged);
|
|
379
|
-
room.messaging.on("participant_removed", handleParticipantsChanged);
|
|
380
|
-
room.messaging.on("messaging_enabled", handleParticipantsChanged);
|
|
569
|
+
room.messaging.on("participant_added", updateStatus);
|
|
570
|
+
room.messaging.on("participant_removed", updateStatus);
|
|
571
|
+
room.messaging.on("messaging_enabled", updateStatus);
|
|
381
572
|
updateStatus();
|
|
382
573
|
return () => {
|
|
383
574
|
roomSubscription.unsubscribe();
|
|
384
|
-
room.messaging.off("participant_added",
|
|
385
|
-
room.messaging.off("participant_removed",
|
|
386
|
-
room.messaging.off("messaging_enabled",
|
|
575
|
+
room.messaging.off("participant_added", updateStatus);
|
|
576
|
+
room.messaging.off("participant_removed", updateStatus);
|
|
577
|
+
room.messaging.off("messaging_enabled", updateStatus);
|
|
387
578
|
};
|
|
388
579
|
}, [agentName, path, room]);
|
|
389
580
|
return status;
|
package/dist/esm/Chat.js
CHANGED
|
@@ -111,7 +111,7 @@ function EmptyState({
|
|
|
111
111
|
title,
|
|
112
112
|
description
|
|
113
113
|
}) {
|
|
114
|
-
return /* @__PURE__ */ jsxs("div", { className: "
|
|
114
|
+
return /* @__PURE__ */ jsxs("div", { className: "h-full max-w-2xl flex flex-col items-center justify-center px-6 py-20 text-center", children: [
|
|
115
115
|
/* @__PURE__ */ jsx("h2", { className: "text-4xl font-semibold tracking-tight text-foreground sm:text-5xl", children: title }),
|
|
116
116
|
description?.trim() ? /* @__PURE__ */ jsx("p", { className: "mt-3 max-w-xl text-sm leading-6 text-muted-foreground sm:text-base", children: description }) : null
|
|
117
117
|
] });
|
|
@@ -381,7 +381,7 @@ function Chat({
|
|
|
381
381
|
placeholder: agentName?.trim() ? `Type a message or @${displayParticipantName(agentName)}` : "Type a message"
|
|
382
382
|
}
|
|
383
383
|
);
|
|
384
|
-
return /* @__PURE__ */ jsxs("div", { className: "
|
|
384
|
+
return /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-1 flex-col", children: [
|
|
385
385
|
activePath ? /* @__PURE__ */ jsx(
|
|
386
386
|
ResolvedChatView,
|
|
387
387
|
{
|
|
@@ -402,7 +402,7 @@ function Chat({
|
|
|
402
402
|
] }),
|
|
403
403
|
composer,
|
|
404
404
|
newThreadError ? /* @__PURE__ */ jsx(ErrorBanner, { message: newThreadError }) : null
|
|
405
|
-
] }) }) : /* @__PURE__ */ jsxs("div", { className: "
|
|
405
|
+
] }) }) : /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-1 flex-col", children: [
|
|
406
406
|
/* @__PURE__ */ jsx("div", { className: "flex-1", children: emptyStateTitle ? /* @__PURE__ */ jsx(EmptyState, { title: emptyStateTitle, description: emptyStateDescription }) : null }),
|
|
407
407
|
pendingStatusText ? /* @__PURE__ */ jsx("div", { className: "px-4 pb-2", children: /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-[912px] text-sm text-muted-foreground", children: pendingStatusText }) }) : null,
|
|
408
408
|
newThreadError ? /* @__PURE__ */ jsx("div", { className: "px-4 pb-2", children: /* @__PURE__ */ jsx(ErrorBanner, { message: newThreadError }) }) : null,
|
package/dist/esm/ChatBotView.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { useDocumentChanged } from "@meshagent/meshagent-react";
|
|
4
4
|
import {
|
|
5
5
|
AlertTriangle,
|
|
@@ -11,6 +11,17 @@ import {
|
|
|
11
11
|
import { useThreadStatus } from "./chat-hooks";
|
|
12
12
|
import { Chat } from "./Chat";
|
|
13
13
|
import { Button } from "./components/ui/button";
|
|
14
|
+
import {
|
|
15
|
+
Dialog,
|
|
16
|
+
DialogClose,
|
|
17
|
+
DialogContent,
|
|
18
|
+
DialogDescription,
|
|
19
|
+
DialogFooter,
|
|
20
|
+
DialogHeader,
|
|
21
|
+
DialogTitle
|
|
22
|
+
} from "./components/ui/dialog";
|
|
23
|
+
import { Input } from "./components/ui/input";
|
|
24
|
+
import { Label } from "./components/ui/label";
|
|
14
25
|
import { Spinner } from "./components/ui/spinner";
|
|
15
26
|
import {
|
|
16
27
|
ChatThreadDisplayMode,
|
|
@@ -110,10 +121,7 @@ function useIsWideLayout(minWidth) {
|
|
|
110
121
|
}, [minWidth]);
|
|
111
122
|
return matches;
|
|
112
123
|
}
|
|
113
|
-
function useThreadListDocument({
|
|
114
|
-
room,
|
|
115
|
-
path
|
|
116
|
-
}) {
|
|
124
|
+
function useThreadListDocument({ room, path }) {
|
|
117
125
|
const [document, setDocument] = useState(null);
|
|
118
126
|
const [entries, setEntries] = useState([]);
|
|
119
127
|
const [loading, setLoading] = useState(path !== null);
|
|
@@ -180,7 +188,7 @@ function ThreadListRow({
|
|
|
180
188
|
icon,
|
|
181
189
|
trailing
|
|
182
190
|
}) {
|
|
183
|
-
return /* @__PURE__ */ jsx("div", { className: "px-2 py-1", children: /* @__PURE__ */ jsxs(
|
|
191
|
+
return /* @__PURE__ */ jsx("div", { className: "px-2 py-1 cursor-pointer", children: /* @__PURE__ */ jsxs(
|
|
184
192
|
"div",
|
|
185
193
|
{
|
|
186
194
|
className: cn(
|
|
@@ -224,14 +232,13 @@ function ThreadListEntryRow({
|
|
|
224
232
|
}) {
|
|
225
233
|
const status = useThreadStatus({ room, path: entry.path, agentName });
|
|
226
234
|
const iconClassName = selected ? "text-accent-foreground" : "text-muted-foreground";
|
|
227
|
-
const hasStatus = status.text?.trim() !== "";
|
|
228
235
|
return /* @__PURE__ */ jsx(
|
|
229
236
|
ThreadListRow,
|
|
230
237
|
{
|
|
231
238
|
title: entry.name,
|
|
232
239
|
selected,
|
|
233
240
|
onClick: () => onSelect(entry),
|
|
234
|
-
icon: hasStatus ? /* @__PURE__ */ jsx(Spinner, { size: "sm", className: iconClassName }) : selected ? /* @__PURE__ */ jsx(Check, { className: cn("h-4 w-4", iconClassName) }) : /* @__PURE__ */ jsx(MessageSquare, { className: cn("h-4 w-4", iconClassName) }),
|
|
241
|
+
icon: status.hasStatus ? /* @__PURE__ */ jsx(Spinner, { size: "sm", className: iconClassName }) : selected ? /* @__PURE__ */ jsx(Check, { className: cn("h-4 w-4", iconClassName) }) : /* @__PURE__ */ jsx(MessageSquare, { className: cn("h-4 w-4", iconClassName) }),
|
|
235
242
|
trailing: /* @__PURE__ */ jsx(
|
|
236
243
|
Button,
|
|
237
244
|
{
|
|
@@ -259,7 +266,7 @@ function ThreadListPanel({
|
|
|
259
266
|
const { entries, error, loading } = threadList;
|
|
260
267
|
const hasSelectedEntry = selectedThreadPath !== null && entries.some((entry) => entry.path === selectedThreadPath);
|
|
261
268
|
const showPendingNewThreadSelection = selectedThreadPath === null || !hasSelectedEntry;
|
|
262
|
-
return /* @__PURE__ */ jsx("div", { className: "
|
|
269
|
+
return /* @__PURE__ */ jsx("div", { className: "h-full flex flex-col rounded-md border", children: loading ? /* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center p-6", children: /* @__PURE__ */ jsx(Spinner, { size: "lg", className: "text-muted-foreground" }) }) : error ? /* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center p-6 text-center text-sm text-muted-foreground", children: `Unable to load threads: ${describeError(error)}` }) : /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-1 flex-col overflow-y-auto py-1", children: [
|
|
263
270
|
/* @__PURE__ */ jsx(
|
|
264
271
|
ThreadListRow,
|
|
265
272
|
{
|
|
@@ -284,6 +291,52 @@ function ThreadListPanel({
|
|
|
284
291
|
))
|
|
285
292
|
] }) });
|
|
286
293
|
}
|
|
294
|
+
function RenameThreadDialog({
|
|
295
|
+
dialogState,
|
|
296
|
+
onNameChange,
|
|
297
|
+
onOpenChange,
|
|
298
|
+
onSubmit
|
|
299
|
+
}) {
|
|
300
|
+
const inputId = useId();
|
|
301
|
+
const inputRef = useRef(null);
|
|
302
|
+
const trimmedName = dialogState?.value.trim() ?? "";
|
|
303
|
+
const saveDisabled = dialogState === null || trimmedName === "" || trimmedName === dialogState.entry.name;
|
|
304
|
+
return /* @__PURE__ */ jsx(Dialog, { open: dialogState !== null, onOpenChange, children: /* @__PURE__ */ jsx(
|
|
305
|
+
DialogContent,
|
|
306
|
+
{
|
|
307
|
+
showCloseButton: false,
|
|
308
|
+
className: "sm:max-w-[425px]",
|
|
309
|
+
onOpenAutoFocus: (event) => {
|
|
310
|
+
event.preventDefault();
|
|
311
|
+
inputRef.current?.focus();
|
|
312
|
+
inputRef.current?.select();
|
|
313
|
+
},
|
|
314
|
+
children: /* @__PURE__ */ jsxs("form", { className: "space-y-4", onSubmit, children: [
|
|
315
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
316
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Rename thread" }),
|
|
317
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "Use a short and descriptive name" })
|
|
318
|
+
] }),
|
|
319
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
320
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: inputId, children: "Name" }),
|
|
321
|
+
/* @__PURE__ */ jsx(
|
|
322
|
+
Input,
|
|
323
|
+
{
|
|
324
|
+
ref: inputRef,
|
|
325
|
+
id: inputId,
|
|
326
|
+
value: dialogState?.value ?? "",
|
|
327
|
+
autoComplete: "off",
|
|
328
|
+
onChange: onNameChange
|
|
329
|
+
}
|
|
330
|
+
)
|
|
331
|
+
] }),
|
|
332
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
333
|
+
/* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", children: "Cancel" }) }),
|
|
334
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", disabled: saveDisabled, children: "Save" })
|
|
335
|
+
] })
|
|
336
|
+
] })
|
|
337
|
+
}
|
|
338
|
+
) });
|
|
339
|
+
}
|
|
287
340
|
function MultiThreadUnavailable() {
|
|
288
341
|
return /* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center px-4 py-8", children: /* @__PURE__ */ jsx("div", { className: "w-full max-w-[912px] rounded-3xl border border-destructive/30 bg-destructive/5 p-6 text-destructive", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
289
342
|
/* @__PURE__ */ jsx(AlertTriangle, { className: "mt-0.5 h-5 w-5 shrink-0" }),
|
|
@@ -330,6 +383,7 @@ function ChatBotView({
|
|
|
330
383
|
const [internalSelectedThreadPath, setInternalSelectedThreadPath] = useState(() => explicitSelectedThreadPath ?? legacySelectedThreadPath ?? null);
|
|
331
384
|
const previousLegacySelectedThreadPathRef = useRef(legacySelectedThreadPath);
|
|
332
385
|
const previousNewThreadResetVersionRef = useRef(newThreadResetVersion);
|
|
386
|
+
const [renameThreadDialog, setRenameThreadDialog] = useState(null);
|
|
333
387
|
const activeSelectedThreadPath = explicitSelectedThreadPath ?? internalSelectedThreadPath;
|
|
334
388
|
const resolvedThreadListDocumentPath = useMemo(
|
|
335
389
|
() => resolvedThreadListPath(threadListPath, { threadDir, agentName }),
|
|
@@ -371,6 +425,9 @@ function ChatBotView({
|
|
|
371
425
|
handleSelectedThreadPathChanged(normalizedNextPath);
|
|
372
426
|
emitResolvedThread(normalizedNextPath, displayName);
|
|
373
427
|
}, [emitResolvedThread, handleSelectedThreadPathChanged]);
|
|
428
|
+
const closeRenameThreadDialog = useCallback(() => {
|
|
429
|
+
setRenameThreadDialog(null);
|
|
430
|
+
}, []);
|
|
374
431
|
useEffect(() => {
|
|
375
432
|
if (threadDisplayMode !== ChatThreadDisplayMode.MultiThreadComposer) {
|
|
376
433
|
previousNewThreadResetVersionRef.current = newThreadResetVersion;
|
|
@@ -382,22 +439,43 @@ function ChatBotView({
|
|
|
382
439
|
previousNewThreadResetVersionRef.current = newThreadResetVersion;
|
|
383
440
|
}, [activeSelectedThreadPath, newThreadResetVersion, setSelectedThread, threadDisplayMode]);
|
|
384
441
|
const handleRenameThread = useCallback((entry) => {
|
|
385
|
-
|
|
386
|
-
|
|
442
|
+
setRenameThreadDialog({
|
|
443
|
+
entry,
|
|
444
|
+
value: entry.name
|
|
445
|
+
});
|
|
446
|
+
}, []);
|
|
447
|
+
const handleRenameThreadNameChanged = useCallback((event) => {
|
|
448
|
+
const nextValue = event.target.value;
|
|
449
|
+
setRenameThreadDialog((currentDialogState) => {
|
|
450
|
+
if (currentDialogState === null) {
|
|
451
|
+
return currentDialogState;
|
|
452
|
+
}
|
|
453
|
+
return {
|
|
454
|
+
...currentDialogState,
|
|
455
|
+
value: nextValue
|
|
456
|
+
};
|
|
457
|
+
});
|
|
458
|
+
}, []);
|
|
459
|
+
const handleRenameThreadDialogOpenChange = useCallback((open) => {
|
|
460
|
+
if (!open) {
|
|
461
|
+
closeRenameThreadDialog();
|
|
387
462
|
}
|
|
388
|
-
|
|
389
|
-
|
|
463
|
+
}, [closeRenameThreadDialog]);
|
|
464
|
+
const handleRenameThreadSubmit = useCallback((event) => {
|
|
465
|
+
event.preventDefault();
|
|
466
|
+
if (renameThreadDialog === null) {
|
|
390
467
|
return;
|
|
391
468
|
}
|
|
392
|
-
const trimmedName =
|
|
393
|
-
if (trimmedName === "" || trimmedName === entry.name) {
|
|
469
|
+
const trimmedName = renameThreadDialog.value.trim();
|
|
470
|
+
if (trimmedName === "" || trimmedName === renameThreadDialog.entry.name) {
|
|
394
471
|
return;
|
|
395
472
|
}
|
|
396
|
-
entry.element.setAttribute("name", trimmedName);
|
|
397
|
-
if (entry.path === activeSelectedThreadPath) {
|
|
398
|
-
emitResolvedThread(entry.path, trimmedName);
|
|
473
|
+
renameThreadDialog.entry.element.setAttribute("name", trimmedName);
|
|
474
|
+
if (renameThreadDialog.entry.path === activeSelectedThreadPath) {
|
|
475
|
+
emitResolvedThread(renameThreadDialog.entry.path, trimmedName);
|
|
399
476
|
}
|
|
400
|
-
|
|
477
|
+
closeRenameThreadDialog();
|
|
478
|
+
}, [activeSelectedThreadPath, closeRenameThreadDialog, emitResolvedThread, renameThreadDialog]);
|
|
401
479
|
if (threadDisplayMode !== ChatThreadDisplayMode.MultiThreadComposer) {
|
|
402
480
|
return /* @__PURE__ */ jsx(
|
|
403
481
|
Chat,
|
|
@@ -449,31 +527,53 @@ function ChatBotView({
|
|
|
449
527
|
}
|
|
450
528
|
);
|
|
451
529
|
if (!showThreadList || resolvedThreadListDocumentPath === null) {
|
|
452
|
-
return
|
|
530
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
531
|
+
content,
|
|
532
|
+
/* @__PURE__ */ jsx(
|
|
533
|
+
RenameThreadDialog,
|
|
534
|
+
{
|
|
535
|
+
dialogState: renameThreadDialog,
|
|
536
|
+
onNameChange: handleRenameThreadNameChanged,
|
|
537
|
+
onOpenChange: handleRenameThreadDialogOpenChange,
|
|
538
|
+
onSubmit: handleRenameThreadSubmit
|
|
539
|
+
}
|
|
540
|
+
)
|
|
541
|
+
] });
|
|
453
542
|
}
|
|
454
|
-
return /* @__PURE__ */ jsxs(
|
|
455
|
-
/* @__PURE__ */
|
|
543
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
544
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex flex-1 h-full", isWideLayout ? "flex-row items-stretch" : "flex-col"), children: [
|
|
545
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col h-full min-h-0 min-w-0 flex-1", children: content }),
|
|
546
|
+
/* @__PURE__ */ jsx(
|
|
547
|
+
"div",
|
|
548
|
+
{
|
|
549
|
+
className: cn("shrink-0 mr-4", isWideLayout ? "ml-3" : "mt-3"),
|
|
550
|
+
style: isWideLayout ? { width: threadListWidth } : { height: threadListCollapsedHeight },
|
|
551
|
+
children: /* @__PURE__ */ jsx(
|
|
552
|
+
ThreadListPanel,
|
|
553
|
+
{
|
|
554
|
+
room,
|
|
555
|
+
threadList,
|
|
556
|
+
selectedThreadPath: activeSelectedThreadPath,
|
|
557
|
+
agentName,
|
|
558
|
+
onSelectThread: (entry) => {
|
|
559
|
+
setSelectedThread(entry.path, entry.name);
|
|
560
|
+
},
|
|
561
|
+
onClearSelection: () => {
|
|
562
|
+
setSelectedThread(null, null);
|
|
563
|
+
},
|
|
564
|
+
onRenameThread: handleRenameThread
|
|
565
|
+
}
|
|
566
|
+
)
|
|
567
|
+
}
|
|
568
|
+
)
|
|
569
|
+
] }),
|
|
456
570
|
/* @__PURE__ */ jsx(
|
|
457
|
-
|
|
571
|
+
RenameThreadDialog,
|
|
458
572
|
{
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
{
|
|
464
|
-
room,
|
|
465
|
-
threadList,
|
|
466
|
-
selectedThreadPath: activeSelectedThreadPath,
|
|
467
|
-
agentName,
|
|
468
|
-
onSelectThread: (entry) => {
|
|
469
|
-
setSelectedThread(entry.path, entry.name);
|
|
470
|
-
},
|
|
471
|
-
onClearSelection: () => {
|
|
472
|
-
setSelectedThread(null, null);
|
|
473
|
-
},
|
|
474
|
-
onRenameThread: handleRenameThread
|
|
475
|
-
}
|
|
476
|
-
)
|
|
573
|
+
dialogState: renameThreadDialog,
|
|
574
|
+
onNameChange: handleRenameThreadNameChanged,
|
|
575
|
+
onOpenChange: handleRenameThreadDialogOpenChange,
|
|
576
|
+
onSubmit: handleRenameThreadSubmit
|
|
477
577
|
}
|
|
478
578
|
)
|
|
479
579
|
] });
|
package/dist/esm/ChatThread.js
CHANGED
|
@@ -505,7 +505,7 @@ function EmptyState({
|
|
|
505
505
|
title,
|
|
506
506
|
description
|
|
507
507
|
}) {
|
|
508
|
-
return /* @__PURE__ */ jsxs("div", { className: "mx-auto flex max-w-2xl flex-col items-center justify-center px-6 py-20 text-center", children: [
|
|
508
|
+
return /* @__PURE__ */ jsxs("div", { className: "h-full mx-auto flex max-w-2xl flex-col items-center justify-center px-6 py-20 text-center", children: [
|
|
509
509
|
/* @__PURE__ */ jsx("h2", { className: "text-4xl font-semibold tracking-tight text-foreground sm:text-5xl", children: title }),
|
|
510
510
|
description?.trim() ? /* @__PURE__ */ jsx("p", { className: "mt-3 max-w-xl text-sm leading-6 text-muted-foreground sm:text-base", children: description }) : null
|
|
511
511
|
] });
|