@smartspace/chat-ui 1.13.1-pr.258.6c80b75 → 1.13.1-pr.260.2e65c1d
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/dist/index.d.ts +8 -14
- package/dist/index.js +59 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -238,6 +238,14 @@ type MessageThread = {
|
|
|
238
238
|
totalMessages: number;
|
|
239
239
|
pinned: boolean;
|
|
240
240
|
workSpaceId: string;
|
|
241
|
+
/**
|
|
242
|
+
* Monotonic version of when this summary was emitted (epoch ms). Used by
|
|
243
|
+
* `applyThreadToCache` to reject stale writes — e.g. a SignalR summary
|
|
244
|
+
* landing after a fresher SSE thread frame because the server's DB write
|
|
245
|
+
* lagged Redis. Mappers derive this from `lastUpdatedAt`; client-side
|
|
246
|
+
* writers like `ensureDraftThread` use `Date.now()`.
|
|
247
|
+
*/
|
|
248
|
+
summaryEmittedAt: number;
|
|
241
249
|
};
|
|
242
250
|
type ThreadsResponse = {
|
|
243
251
|
data: MessageThread[];
|
|
@@ -969,20 +977,6 @@ declare function mapMentionUserDtoToModel(dto: MentionUserDto): MentionUser;
|
|
|
969
977
|
declare function mapWorkspaceDtoToModel(dto: WorkspaceDto): Workspace;
|
|
970
978
|
declare const mapWorkspacesDtoToModels: (arr: WorkspacesListItemDto[]) => Workspace[];
|
|
971
979
|
|
|
972
|
-
/**
|
|
973
|
-
* Write a freshly-observed thread (from SignalR or an SSE thread frame)
|
|
974
|
-
* directly into the relevant query caches so subscribers paint without a
|
|
975
|
-
* refetch roundtrip.
|
|
976
|
-
*
|
|
977
|
-
* - Merges into `threadsKeys.detail(workspaceId, thread.id)`.
|
|
978
|
-
* - Splices into every threads-list cache for the workspace, handling both
|
|
979
|
-
* finite `ThreadsResponse` and infinite `{ pages, pageParams }` shapes.
|
|
980
|
-
*
|
|
981
|
-
* Returns `true` when the thread was found in at least one list cache.
|
|
982
|
-
* Callers that need to surface brand-new threads (e.g. another user just
|
|
983
|
-
* created one) can fall back to invalidating the list queries when this
|
|
984
|
-
* returns `false`.
|
|
985
|
-
*/
|
|
986
980
|
declare function applyThreadToCache(qc: QueryClient, thread: MessageThread): boolean;
|
|
987
981
|
/**
|
|
988
982
|
* Invalidate every threads-list cache for a workspace. Use as a fallback when
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Loader2, Check, X, Paperclip, ArrowBigUp, Minimize2, AlertTriangle, Fil
|
|
|
4
4
|
import * as React8 from 'react';
|
|
5
5
|
import { createContext, forwardRef, useImperativeHandle, useRef, useState, useEffect, useMemo, useCallback, createElement, useContext } from 'react';
|
|
6
6
|
import { createPortal } from 'react-dom';
|
|
7
|
-
import { useQuery, queryOptions, useQueryClient, useMutation } from '@tanstack/react-query';
|
|
7
|
+
import { useQuery, queryOptions, useQueryClient, useMutation, skipToken } from '@tanstack/react-query';
|
|
8
8
|
import { toast } from 'sonner';
|
|
9
9
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
10
10
|
import { Editor, rootCtx, defaultValueCtx, editorViewOptionsCtx, editorViewCtx, serializerCtx, SchemaReady, nodeViewCtx, markViewCtx, schemaCtx, prosePluginsCtx, nodesCtx } from '@milkdown/core';
|
|
@@ -3233,7 +3233,20 @@ var threadsKeys = {
|
|
|
3233
3233
|
};
|
|
3234
3234
|
|
|
3235
3235
|
// src/domains/threads/cache.ts
|
|
3236
|
+
function isStaleSummary(incoming, existing) {
|
|
3237
|
+
if (!existing) return false;
|
|
3238
|
+
if (typeof existing.summaryEmittedAt !== "number") return false;
|
|
3239
|
+
if (typeof incoming.summaryEmittedAt !== "number") return false;
|
|
3240
|
+
if (incoming.summaryEmittedAt >= existing.summaryEmittedAt) return false;
|
|
3241
|
+
return existing.isFlowRunning === false && incoming.isFlowRunning === true;
|
|
3242
|
+
}
|
|
3236
3243
|
function applyThreadToCache(qc, thread) {
|
|
3244
|
+
const existingDetail = qc.getQueryData(
|
|
3245
|
+
threadsKeys.detail(thread.workSpaceId, thread.id)
|
|
3246
|
+
);
|
|
3247
|
+
if (isStaleSummary(thread, existingDetail)) {
|
|
3248
|
+
return false;
|
|
3249
|
+
}
|
|
3237
3250
|
qc.setQueryData(
|
|
3238
3251
|
threadsKeys.detail(thread.workSpaceId, thread.id),
|
|
3239
3252
|
(old) => ({ ...old ?? thread, ...thread })
|
|
@@ -3254,6 +3267,7 @@ function applyThreadToCache(qc, thread) {
|
|
|
3254
3267
|
if (!page?.data) return page;
|
|
3255
3268
|
const idx2 = page.data.findIndex((t) => t.id === thread.id);
|
|
3256
3269
|
if (idx2 === -1) return page;
|
|
3270
|
+
if (isStaleSummary(thread, page.data[idx2])) return page;
|
|
3257
3271
|
changed = true;
|
|
3258
3272
|
foundInList = true;
|
|
3259
3273
|
const nextData2 = page.data.slice();
|
|
@@ -3266,6 +3280,7 @@ function applyThreadToCache(qc, thread) {
|
|
|
3266
3280
|
if (!list2.data) return old;
|
|
3267
3281
|
const idx = list2.data.findIndex((t) => t.id === thread.id);
|
|
3268
3282
|
if (idx === -1) return old;
|
|
3283
|
+
if (isStaleSummary(thread, list2.data[idx])) return old;
|
|
3269
3284
|
foundInList = true;
|
|
3270
3285
|
const nextData = list2.data.slice();
|
|
3271
3286
|
nextData[idx] = { ...nextData[idx], ...thread };
|
|
@@ -3335,36 +3350,40 @@ var {
|
|
|
3335
3350
|
messageThreadsGetMessageThreadWorkspacesWorkspaceIdMessagethreadsIdResponse: threadResponseSchema
|
|
3336
3351
|
} = ChatZod;
|
|
3337
3352
|
function mapThreadDtoToModel(dto) {
|
|
3353
|
+
const lastUpdatedAt = utcDate(dto.lastUpdatedAt);
|
|
3338
3354
|
return {
|
|
3339
3355
|
id: dto.id,
|
|
3340
3356
|
createdAt: utcDate(dto.createdAt),
|
|
3341
3357
|
createdBy: dto.createdBy ?? "",
|
|
3342
3358
|
createdByUserId: dto.createdByUserId,
|
|
3343
3359
|
isFlowRunning: dto.isFlowRunning,
|
|
3344
|
-
lastUpdatedAt
|
|
3360
|
+
lastUpdatedAt,
|
|
3345
3361
|
lastUpdatedByUserId: dto.lastUpdatedByUserId,
|
|
3346
3362
|
name: dto.name ?? "",
|
|
3347
3363
|
totalMessages: dto.totalMessages,
|
|
3348
3364
|
pinned: dto.favorited,
|
|
3349
|
-
workSpaceId: dto.workSpaceId
|
|
3365
|
+
workSpaceId: dto.workSpaceId,
|
|
3366
|
+
summaryEmittedAt: lastUpdatedAt.getTime()
|
|
3350
3367
|
};
|
|
3351
3368
|
}
|
|
3352
3369
|
function mapThreadsResponseDtoToModel(dto) {
|
|
3353
3370
|
return { data: dto.data.map(mapThreadDtoToModel), total: dto.total };
|
|
3354
3371
|
}
|
|
3355
3372
|
function mapSignalRThreadSummaryToModel(summary) {
|
|
3373
|
+
const lastUpdatedAt = utcDate(summary.lastUpdatedAt);
|
|
3356
3374
|
return {
|
|
3357
3375
|
id: summary.id,
|
|
3358
3376
|
createdAt: utcDate(summary.createdAt),
|
|
3359
3377
|
createdBy: summary.createdBy ?? "",
|
|
3360
3378
|
createdByUserId: summary.createdByUserId,
|
|
3361
3379
|
isFlowRunning: summary.isFlowRunning,
|
|
3362
|
-
lastUpdatedAt
|
|
3380
|
+
lastUpdatedAt,
|
|
3363
3381
|
lastUpdatedByUserId: summary.lastUpdatedByUserId,
|
|
3364
3382
|
name: summary.name ?? "",
|
|
3365
3383
|
totalMessages: summary.totalMessages,
|
|
3366
3384
|
pinned: summary.favorited,
|
|
3367
|
-
workSpaceId: summary.workSpaceId
|
|
3385
|
+
workSpaceId: summary.workSpaceId,
|
|
3386
|
+
summaryEmittedAt: lastUpdatedAt.getTime()
|
|
3368
3387
|
};
|
|
3369
3388
|
}
|
|
3370
3389
|
var threadDetailOptions = ({
|
|
@@ -3418,11 +3437,12 @@ var useThread = ({
|
|
|
3418
3437
|
});
|
|
3419
3438
|
};
|
|
3420
3439
|
var useThreadIsRunning = (workspaceId, threadId) => {
|
|
3421
|
-
const
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3440
|
+
const queryClient = useQueryClient();
|
|
3441
|
+
const { data: detailThread } = useQuery({
|
|
3442
|
+
queryKey: threadsKeys.detail(workspaceId ?? "", threadId ?? ""),
|
|
3443
|
+
queryFn: skipToken
|
|
3425
3444
|
});
|
|
3445
|
+
const listThread = workspaceId && threadId ? getThreadPlaceholderFromListCache(queryClient, workspaceId, threadId) : void 0;
|
|
3426
3446
|
const { data: optimistic } = useQuery({
|
|
3427
3447
|
queryKey: threadsKeys.optimisticRunning(threadId ?? ""),
|
|
3428
3448
|
queryFn: () => false,
|
|
@@ -3430,7 +3450,7 @@ var useThreadIsRunning = (workspaceId, threadId) => {
|
|
|
3430
3450
|
staleTime: Infinity,
|
|
3431
3451
|
enabled: !!threadId
|
|
3432
3452
|
});
|
|
3433
|
-
return !!optimistic || !!
|
|
3453
|
+
return !!optimistic || !!(detailThread ?? listThread)?.isFlowRunning;
|
|
3434
3454
|
};
|
|
3435
3455
|
|
|
3436
3456
|
// src/domains/messages/enums.ts
|
|
@@ -3539,7 +3559,13 @@ function useSendMessage() {
|
|
|
3539
3559
|
toast.error("There was an error posting your message");
|
|
3540
3560
|
throw err;
|
|
3541
3561
|
}
|
|
3542
|
-
qc.setQueryData(messagesKeys.list(threadId), [
|
|
3562
|
+
qc.setQueryData(messagesKeys.list(threadId), (old = []) => {
|
|
3563
|
+
const withoutOptimistic = old.filter((m) => !m.optimistic);
|
|
3564
|
+
if (withoutOptimistic.some((m) => m.id === realMessage.id)) {
|
|
3565
|
+
return withoutOptimistic;
|
|
3566
|
+
}
|
|
3567
|
+
return [...withoutOptimistic, realMessage];
|
|
3568
|
+
});
|
|
3543
3569
|
qc.setQueryData(
|
|
3544
3570
|
threadsKeys.detail(workspaceId, threadId),
|
|
3545
3571
|
(old) => old ? { ...old, isFlowRunning: true } : old
|
|
@@ -19803,6 +19829,10 @@ function MessageList({
|
|
|
19803
19829
|
const messagesEndRef = useRef(null);
|
|
19804
19830
|
const prevMessageCountRef = useRef(0);
|
|
19805
19831
|
const hasInitialScrollRef = useRef(false);
|
|
19832
|
+
const everHadMessagesRef = useRef({
|
|
19833
|
+
threadId: "",
|
|
19834
|
+
had: false
|
|
19835
|
+
});
|
|
19806
19836
|
const isMobile = useIsMobile();
|
|
19807
19837
|
const { data: activeWorkspace } = useWorkspace(workspaceId);
|
|
19808
19838
|
const [isAtBottom, setIsAtBottom] = useState(true);
|
|
@@ -19874,8 +19904,15 @@ function MessageList({
|
|
|
19874
19904
|
ro.observe(content);
|
|
19875
19905
|
return () => ro.disconnect();
|
|
19876
19906
|
}, [isAtBottom, scrollToBottom]);
|
|
19907
|
+
const safeMessages = messages ?? [];
|
|
19908
|
+
if (everHadMessagesRef.current.threadId !== threadId) {
|
|
19909
|
+
everHadMessagesRef.current = { threadId, had: safeMessages.length > 0 };
|
|
19910
|
+
} else if (safeMessages.length > 0) {
|
|
19911
|
+
everHadMessagesRef.current.had = true;
|
|
19912
|
+
}
|
|
19913
|
+
const hadMessagesBefore = everHadMessagesRef.current.had;
|
|
19877
19914
|
const isLoading = isChoosingThread || (threadPending || threadFetching) && !thread || (messagesPending || messagesFetching) && messages === void 0;
|
|
19878
|
-
if (isLoading) {
|
|
19915
|
+
if (isLoading && !hadMessagesBefore) {
|
|
19879
19916
|
return /* @__PURE__ */ jsx(
|
|
19880
19917
|
"div",
|
|
19881
19918
|
{
|
|
@@ -19891,7 +19928,7 @@ function MessageList({
|
|
|
19891
19928
|
}
|
|
19892
19929
|
);
|
|
19893
19930
|
}
|
|
19894
|
-
if (threadError || messagesError) {
|
|
19931
|
+
if ((threadError || messagesError) && !hadMessagesBefore) {
|
|
19895
19932
|
return /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center p-6", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-md space-y-3", children: [
|
|
19896
19933
|
threadError && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-destructive", children: [
|
|
19897
19934
|
/* @__PURE__ */ jsx(AlertTriangle, { className: "h-4 w-4" }),
|
|
@@ -19903,8 +19940,16 @@ function MessageList({
|
|
|
19903
19940
|
] })
|
|
19904
19941
|
] }) });
|
|
19905
19942
|
}
|
|
19906
|
-
const safeMessages = messages ?? [];
|
|
19907
19943
|
if (safeMessages.length === 0) {
|
|
19944
|
+
if (hadMessagesBefore) {
|
|
19945
|
+
return /* @__PURE__ */ jsx(
|
|
19946
|
+
"div",
|
|
19947
|
+
{
|
|
19948
|
+
className: `ss-chat__body flex-shrink-10 flex-1 overflow-y-auto ${hostBg}`,
|
|
19949
|
+
"data-ss-layer": "message-list"
|
|
19950
|
+
}
|
|
19951
|
+
);
|
|
19952
|
+
}
|
|
19908
19953
|
return /* @__PURE__ */ jsxs("div", { className: "flex overflow-auto flex-shrink-10 flex-col p-8 text-center", children: [
|
|
19909
19954
|
/* @__PURE__ */ jsx("h3", { className: "text-lg font-medium mb-2", children: activeWorkspace?.name ?? "No messages yet" }),
|
|
19910
19955
|
activeWorkspace?.firstPrompt && /* @__PURE__ */ jsx("div", { className: "max-w-3xl mx-auto p-4", children: /* @__PURE__ */ jsx(MessageMarkdown, { value: activeWorkspace.firstPrompt }) })
|