@agent-native/toolkit 0.217.0 → 0.219.0
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/agent-native.eject.json +21 -0
- package/dist/app/agent-page/AgentContextTab.d.ts.map +1 -1
- package/dist/app/agent-page/AgentContextTab.js +1 -0
- package/dist/app/agent-page/AgentContextTab.js.map +1 -1
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +1 -1
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/context.d.ts +4 -1
- package/dist/app/agentkit/react/context.d.ts.map +1 -1
- package/dist/app/agentkit/react/context.js +3 -1
- package/dist/app/agentkit/react/context.js.map +1 -1
- package/dist/app/agentkit/react/data-chart-layout.d.ts +30 -0
- package/dist/app/agentkit/react/data-chart-layout.d.ts.map +1 -0
- package/dist/app/agentkit/react/data-chart-layout.js +2 -0
- package/dist/app/agentkit/react/data-chart-layout.js.map +1 -0
- package/dist/app/agentkit/react/index.d.ts +1 -0
- package/dist/app/agentkit/react/index.d.ts.map +1 -1
- package/dist/app/agentkit/react/index.js +1 -0
- package/dist/app/agentkit/react/index.js.map +1 -1
- package/dist/app/agentkit/react/root.d.ts +3 -1
- package/dist/app/agentkit/react/root.d.ts.map +1 -1
- package/dist/app/agentkit/react/root.js +2 -2
- package/dist/app/agentkit/react/root.js.map +1 -1
- package/dist/app/agentkit/react/styles.css +31 -6
- package/dist/app/agentkit/react/transcript-layout.d.ts +2 -0
- package/dist/app/agentkit/react/transcript-layout.d.ts.map +1 -1
- package/dist/app/agentkit/react/transcript-layout.js.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +2 -2
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/AgentPanel.js +6 -6
- package/dist/app/chat/AgentPanel.js.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.js +53 -95
- package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
- package/dist/app/chat/agentkit-chat/action-widget.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/action-widget.js +4 -1
- package/dist/app/chat/agentkit-chat/action-widget.js.map +1 -1
- package/dist/app/chat/agentkit-chat/rail.d.ts +1 -1
- package/dist/app/chat/agentkit-chat/rail.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/rail.js +1 -1
- package/dist/app/chat/agentkit-chat/rail.js.map +1 -1
- package/dist/app/chat/chat/index.d.ts +2 -1
- package/dist/app/chat/chat/index.d.ts.map +1 -1
- package/dist/app/chat/chat/index.js +1 -1
- package/dist/app/chat/chat/index.js.map +1 -1
- package/dist/app/chat/chat/surface-types.d.ts +3 -0
- package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
- package/dist/app/chat/chat/surface-types.js.map +1 -1
- package/dist/app/chat/chat/tool-call-display.d.ts +5 -1
- package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
- package/dist/app/chat/chat/tool-call-display.js +6 -4
- package/dist/app/chat/chat/tool-call-display.js.map +1 -1
- package/dist/app/chat/chat/tool-render-registry.d.ts +4 -0
- package/dist/app/chat/chat/tool-render-registry.d.ts.map +1 -1
- package/dist/app/chat/chat/tool-render-registry.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts +3 -1
- package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartRenderer.js +116 -26
- package/dist/app/chat/chat/widgets/DataChartRenderer.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartWidget.d.ts +3 -1
- package/dist/app/chat/chat/widgets/DataChartWidget.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartWidget.js +21 -2
- package/dist/app/chat/chat/widgets/DataChartWidget.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataInsightsWidget.d.ts +3 -1
- package/dist/app/chat/chat/widgets/DataInsightsWidget.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataInsightsWidget.js +2 -2
- package/dist/app/chat/chat/widgets/DataInsightsWidget.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts +1 -1
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js +3 -1
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js.map +1 -1
- package/dist/app/chat/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/builtin-tool-renderers.js +2 -2
- package/dist/app/chat/chat/widgets/builtin-tool-renderers.js.map +1 -1
- package/dist/app/chat/chat-first/chat-history.d.ts +13 -2
- package/dist/app/chat/chat-first/chat-history.d.ts.map +1 -1
- package/dist/app/chat/chat-first/chat-history.js +24 -3
- package/dist/app/chat/chat-first/chat-history.js.map +1 -1
- package/dist/app/chat/conversation/AgentConversation.d.ts.map +1 -1
- package/dist/app/chat/conversation/AgentConversation.js +6 -1
- package/dist/app/chat/conversation/AgentConversation.js.map +1 -1
- package/dist/app/chat/ownership-history/ChatOwnerAvatar.d.ts +6 -0
- package/dist/app/chat/ownership-history/ChatOwnerAvatar.d.ts.map +1 -0
- package/dist/app/chat/ownership-history/ChatOwnerAvatar.js +9 -0
- package/dist/app/chat/ownership-history/ChatOwnerAvatar.js.map +1 -0
- package/dist/app/chat/ownership-history/index.d.ts +11 -0
- package/dist/app/chat/ownership-history/index.d.ts.map +1 -0
- package/dist/app/chat/ownership-history/index.js +22 -0
- package/dist/app/chat/ownership-history/index.js.map +1 -0
- package/dist/app/chat/ownership-history/rows.d.ts +27 -0
- package/dist/app/chat/ownership-history/rows.d.ts.map +1 -0
- package/dist/app/chat/ownership-history/rows.js +101 -0
- package/dist/app/chat/ownership-history/rows.js.map +1 -0
- package/dist/app/chat/ownership-history/useOwnershipChatHistory.d.ts +52 -0
- package/dist/app/chat/ownership-history/useOwnershipChatHistory.d.ts.map +1 -0
- package/dist/app/chat/ownership-history/useOwnershipChatHistory.js +497 -0
- package/dist/app/chat/ownership-history/useOwnershipChatHistory.js.map +1 -0
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +3 -0
- package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.js +3 -0
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +3 -0
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +3 -0
- package/dist/app/i18n/catalogs/en-US.js.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.js +3 -0
- package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.js +3 -0
- package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.js +3 -0
- package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.js +3 -0
- package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.js +3 -0
- package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.js +3 -0
- package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.js +3 -0
- package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.js +3 -0
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/shared/CommandMenu.d.ts +2 -0
- package/dist/app/shared/CommandMenu.d.ts.map +1 -1
- package/dist/app/shared/CommandMenu.js +11 -6
- package/dist/app/shared/CommandMenu.js.map +1 -1
- package/dist/app/sharing/ShareButton.d.ts.map +1 -1
- package/dist/app/sharing/ShareButton.js +9 -8
- package/dist/app/sharing/ShareButton.js.map +1 -1
- package/dist/chat-history/ChatHistoryList.d.ts +16 -3
- package/dist/chat-history/ChatHistoryList.d.ts.map +1 -1
- package/dist/chat-history/ChatHistoryList.js +25 -15
- package/dist/chat-history/ChatHistoryList.js.map +1 -1
- package/dist/chat-history.css +109 -9
- package/dist/composer/TiptapComposer.js +1 -1
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/package.json +10 -3
- package/src/app/agent-page/AgentContextTab.spec.ts +50 -2
- package/src/app/agent-page/AgentContextTab.tsx +1 -0
- package/src/app/agentkit/react/components.interaction.spec.tsx +2 -0
- package/src/app/agentkit/react/components.spec.tsx +11 -6
- package/src/app/agentkit/react/components.tsx +3 -0
- package/src/app/agentkit/react/context.tsx +6 -0
- package/src/app/agentkit/react/data-chart-layout.ts +25 -0
- package/src/app/agentkit/react/index.ts +1 -0
- package/src/app/agentkit/react/root.tsx +4 -0
- package/src/app/agentkit/react/styles.css +31 -6
- package/src/app/agentkit/react/transcript-layout.ts +2 -0
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +201 -27
- package/src/app/chat/AgentKitAssistantChat.tsx +3 -0
- package/src/app/chat/AgentPanel.tsx +8 -8
- package/src/app/chat/MultiTabAssistantChat.spec.tsx +270 -3
- package/src/app/chat/MultiTabAssistantChat.tsx +81 -158
- package/src/app/chat/agentkit-chat/action-widget.spec.tsx +62 -0
- package/src/app/chat/agentkit-chat/action-widget.tsx +4 -0
- package/src/app/chat/agentkit-chat/rail.ts +11 -0
- package/src/app/chat/chat/index.ts +10 -0
- package/src/app/chat/chat/surface-types.ts +3 -0
- package/src/app/chat/chat/tool-call-display.spec.tsx +29 -0
- package/src/app/chat/chat/tool-call-display.tsx +17 -1
- package/src/app/chat/chat/tool-render-registry.tsx +5 -0
- package/src/app/chat/chat/widgets/DataChartRenderer.spec.tsx +377 -17
- package/src/app/chat/chat/widgets/DataChartRenderer.tsx +232 -63
- package/src/app/chat/chat/widgets/DataChartWidget.spec.tsx +67 -0
- package/src/app/chat/chat/widgets/DataChartWidget.tsx +39 -5
- package/src/app/chat/chat/widgets/DataInsightsWidget.tsx +13 -2
- package/src/app/chat/chat/widgets/DataWidgetDisplayControls.tsx +6 -4
- package/src/app/chat/chat/widgets/builtin-tool-renderers.tsx +5 -1
- package/src/app/chat/chat-first/chat-history.spec.tsx +79 -0
- package/src/app/chat/chat-first/chat-history.tsx +75 -22
- package/src/app/chat/conversation/AgentConversation.spec.tsx +51 -1
- package/src/app/chat/conversation/AgentConversation.tsx +11 -2
- package/src/app/chat/ownership-history/ChatOwnerAvatar.tsx +27 -0
- package/src/app/chat/ownership-history/index.tsx +67 -0
- package/src/app/chat/ownership-history/ownership-history.spec.tsx +3146 -0
- package/src/app/chat/ownership-history/rows.ts +156 -0
- package/src/app/chat/ownership-history/useOwnershipChatHistory.ts +709 -0
- package/src/app/i18n/catalogs/ar-SA.ts +3 -0
- package/src/app/i18n/catalogs/de-DE.ts +3 -0
- package/src/app/i18n/catalogs/en-US.ts +3 -0
- package/src/app/i18n/catalogs/es-ES.ts +3 -0
- package/src/app/i18n/catalogs/fr-FR.ts +3 -0
- package/src/app/i18n/catalogs/hi-IN.ts +3 -0
- package/src/app/i18n/catalogs/ja-JP.ts +3 -0
- package/src/app/i18n/catalogs/ko-KR.ts +3 -0
- package/src/app/i18n/catalogs/pt-BR.ts +3 -0
- package/src/app/i18n/catalogs/zh-CN.ts +3 -0
- package/src/app/i18n/catalogs/zh-TW.ts +3 -0
- package/src/app/shared/CommandMenu.spec.tsx +175 -6
- package/src/app/shared/CommandMenu.tsx +24 -7
- package/src/app/sharing/ShareButton.tsx +12 -10
- package/src/chat-history/ChatHistoryList.spec.tsx +51 -0
- package/src/chat-history/ChatHistoryList.tsx +87 -26
- package/src/chat-history/ChatHistoryRailSections.spec.tsx +12 -4
- package/src/chat-history.css +109 -9
- package/src/composer/TiptapComposer.tsx +1 -1
|
@@ -34,6 +34,7 @@ import React, { act } from "react";
|
|
|
34
34
|
import { flushSync } from "react-dom";
|
|
35
35
|
import { createRoot, type Root } from "react-dom/client";
|
|
36
36
|
import { MemoryRouter } from "react-router";
|
|
37
|
+
import { toast } from "sonner";
|
|
37
38
|
import {
|
|
38
39
|
afterEach,
|
|
39
40
|
beforeEach,
|
|
@@ -7874,6 +7875,8 @@ describe("MultiTabAssistantChat page overlay", () => {
|
|
|
7874
7875
|
describe("MultiTabAssistantChat history popover", () => {
|
|
7875
7876
|
let container: HTMLDivElement;
|
|
7876
7877
|
let root: Root;
|
|
7878
|
+
let sharedHistoryRows: ChatThreadSummary[];
|
|
7879
|
+
let mineHistoryRows: ChatThreadSummary[] | undefined;
|
|
7877
7880
|
|
|
7878
7881
|
beforeEach(() => {
|
|
7879
7882
|
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
|
|
@@ -7882,6 +7885,8 @@ describe("MultiTabAssistantChat history popover", () => {
|
|
|
7882
7885
|
return 0;
|
|
7883
7886
|
});
|
|
7884
7887
|
resetThreadMocks();
|
|
7888
|
+
sharedHistoryRows = [];
|
|
7889
|
+
mineHistoryRows = undefined;
|
|
7885
7890
|
const now = Date.now();
|
|
7886
7891
|
threadMocks.activeThreadId = "thread-1";
|
|
7887
7892
|
const historyThreads: ChatThreadSummary[] = [
|
|
@@ -7917,7 +7922,32 @@ describe("MultiTabAssistantChat history popover", () => {
|
|
|
7917
7922
|
threadMocks.threads = historyThreads;
|
|
7918
7923
|
vi.stubGlobal(
|
|
7919
7924
|
"fetch",
|
|
7920
|
-
vi.fn(async () =>
|
|
7925
|
+
vi.fn(async (input: RequestInfo | URL) => {
|
|
7926
|
+
const url = new URL(String(input), "http://test.local");
|
|
7927
|
+
const ownership = url.searchParams.get("ownership");
|
|
7928
|
+
if (ownership) {
|
|
7929
|
+
const source =
|
|
7930
|
+
ownership === "mine"
|
|
7931
|
+
? (mineHistoryRows ?? threadMocks.threads).map((thread) => ({
|
|
7932
|
+
...thread,
|
|
7933
|
+
ownerEmail: "me@example.com",
|
|
7934
|
+
accessBasis: "owner",
|
|
7935
|
+
visibility: "private",
|
|
7936
|
+
}))
|
|
7937
|
+
: sharedHistoryRows;
|
|
7938
|
+
const query = url.searchParams.get("q") ?? "";
|
|
7939
|
+
const matching = source.filter((row) =>
|
|
7940
|
+
row.title.toLowerCase().includes(query.toLowerCase()),
|
|
7941
|
+
);
|
|
7942
|
+
const offset = Number(url.searchParams.get("offset") ?? 0);
|
|
7943
|
+
const limit = Number(url.searchParams.get("limit") ?? 50);
|
|
7944
|
+
return Response.json({
|
|
7945
|
+
threads: matching.slice(offset, offset + limit),
|
|
7946
|
+
hasMore: matching.length > offset + limit,
|
|
7947
|
+
});
|
|
7948
|
+
}
|
|
7949
|
+
return Response.json({ value: null });
|
|
7950
|
+
}),
|
|
7921
7951
|
);
|
|
7922
7952
|
window.localStorage.clear();
|
|
7923
7953
|
window.localStorage.setItem(
|
|
@@ -7947,7 +7977,7 @@ describe("MultiTabAssistantChat history popover", () => {
|
|
|
7947
7977
|
'button[aria-label="All chats"]',
|
|
7948
7978
|
);
|
|
7949
7979
|
expect(historyButton).not.toBeNull();
|
|
7950
|
-
act(() => {
|
|
7980
|
+
await act(async () => {
|
|
7951
7981
|
historyButton!.click();
|
|
7952
7982
|
});
|
|
7953
7983
|
}
|
|
@@ -7965,13 +7995,224 @@ describe("MultiTabAssistantChat history popover", () => {
|
|
|
7965
7995
|
});
|
|
7966
7996
|
}
|
|
7967
7997
|
|
|
7998
|
+
it.each(["local", "owner"] as const)(
|
|
7999
|
+
"keeps the active proven-%s empty chat before persistence and hides inactive blanks",
|
|
8000
|
+
async (proof) => {
|
|
8001
|
+
mineHistoryRows = [];
|
|
8002
|
+
threadMocks.isNewThread.mockImplementation(() => proof === "local");
|
|
8003
|
+
threadMocks.threads = threadMocks.threads.slice(0, 2).map((thread) => ({
|
|
8004
|
+
...thread,
|
|
8005
|
+
messageCount: 0,
|
|
8006
|
+
...(proof === "owner"
|
|
8007
|
+
? {
|
|
8008
|
+
accessBasis: "owner" as const,
|
|
8009
|
+
ownerEmail: "me@example.com",
|
|
8010
|
+
visibility: "private" as const,
|
|
8011
|
+
}
|
|
8012
|
+
: {}),
|
|
8013
|
+
}));
|
|
8014
|
+
await openHistory();
|
|
8015
|
+
|
|
8016
|
+
const mine = container.querySelector(
|
|
8017
|
+
'.an-chat-history__section[data-section-id="mine"]',
|
|
8018
|
+
)!;
|
|
8019
|
+
expect(
|
|
8020
|
+
Array.from(mine.querySelectorAll(".an-chat-history-row__title")).map(
|
|
8021
|
+
(element) => element.textContent,
|
|
8022
|
+
),
|
|
8023
|
+
).toEqual(["Active chat"]);
|
|
8024
|
+
expect(mine.querySelector(".an-chat-history-row--active")).not.toBeNull();
|
|
8025
|
+
expect(
|
|
8026
|
+
mine.querySelector(".an-chat-history-row__timestamp")?.textContent,
|
|
8027
|
+
).toBe("Active");
|
|
8028
|
+
expect(threadMocks.saveThreadData).not.toHaveBeenCalled();
|
|
8029
|
+
},
|
|
8030
|
+
);
|
|
8031
|
+
|
|
8032
|
+
it.each([false, true])(
|
|
8033
|
+
"never recasts an active shared empty chat as Mine (server row loaded: %s)",
|
|
8034
|
+
async (loaded) => {
|
|
8035
|
+
mineHistoryRows = [];
|
|
8036
|
+
threadMocks.isNewThread.mockReturnValue(true);
|
|
8037
|
+
const shared: ChatThreadSummary = {
|
|
8038
|
+
...threadMocks.threads[0],
|
|
8039
|
+
messageCount: 0,
|
|
8040
|
+
accessBasis: "direct",
|
|
8041
|
+
ownerEmail: "pat@example.com",
|
|
8042
|
+
visibility: "private",
|
|
8043
|
+
};
|
|
8044
|
+
threadMocks.threads = [shared];
|
|
8045
|
+
sharedHistoryRows = loaded ? [shared] : [];
|
|
8046
|
+
await openHistory();
|
|
8047
|
+
|
|
8048
|
+
expect(
|
|
8049
|
+
container.querySelector(
|
|
8050
|
+
'.an-chat-history__section[data-section-id="mine"] .an-chat-history-row',
|
|
8051
|
+
),
|
|
8052
|
+
).toBeNull();
|
|
8053
|
+
const sharedSection = container.querySelector(
|
|
8054
|
+
'.an-chat-history__section[data-section-id="shared"]',
|
|
8055
|
+
);
|
|
8056
|
+
expect(
|
|
8057
|
+
sharedSection?.querySelectorAll(".an-chat-history-row").length ?? 0,
|
|
8058
|
+
).toBe(loaded ? 1 : 0);
|
|
8059
|
+
const trigger =
|
|
8060
|
+
sharedSection?.querySelector<HTMLButtonElement>(
|
|
8061
|
+
".an-chat-history-row__menu-trigger",
|
|
8062
|
+
) ?? null;
|
|
8063
|
+
if (!loaded) {
|
|
8064
|
+
expect(trigger).toBeNull();
|
|
8065
|
+
return;
|
|
8066
|
+
}
|
|
8067
|
+
await openRowMenu(trigger!);
|
|
8068
|
+
expect(
|
|
8069
|
+
Array.from(
|
|
8070
|
+
document.body.querySelectorAll(".an-chat-history-row__menu-item"),
|
|
8071
|
+
(element) => element.textContent,
|
|
8072
|
+
),
|
|
8073
|
+
).toEqual(["Copy link"]);
|
|
8074
|
+
},
|
|
8075
|
+
);
|
|
8076
|
+
|
|
8077
|
+
it("loads both ownership lists only while open and debounces search to both", async () => {
|
|
8078
|
+
await act(async () => {
|
|
8079
|
+
root.render(<MultiTabAssistantChat storageKey="history-test" />);
|
|
8080
|
+
});
|
|
8081
|
+
const fetchMock = vi.mocked(fetch);
|
|
8082
|
+
const ownershipRequests = () =>
|
|
8083
|
+
fetchMock.mock.calls
|
|
8084
|
+
.map(([input]) => new URL(String(input), "http://test.local"))
|
|
8085
|
+
.filter((url) => url.searchParams.has("ownership"));
|
|
8086
|
+
expect(ownershipRequests()).toHaveLength(0);
|
|
8087
|
+
await openHistory();
|
|
8088
|
+
expect(
|
|
8089
|
+
ownershipRequests().map((url) => url.searchParams.get("ownership")),
|
|
8090
|
+
).toEqual(["mine", "shared"]);
|
|
8091
|
+
vi.useFakeTimers();
|
|
8092
|
+
try {
|
|
8093
|
+
const input = container.querySelector<HTMLInputElement>(
|
|
8094
|
+
".an-chat-history__search-input",
|
|
8095
|
+
)!;
|
|
8096
|
+
act(() => {
|
|
8097
|
+
Object.getOwnPropertyDescriptor(
|
|
8098
|
+
window.HTMLInputElement.prototype,
|
|
8099
|
+
"value",
|
|
8100
|
+
)!.set!.call(input, "Other");
|
|
8101
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
8102
|
+
});
|
|
8103
|
+
expect(ownershipRequests()).toHaveLength(2);
|
|
8104
|
+
await act(async () => {
|
|
8105
|
+
await vi.advanceTimersByTimeAsync(250);
|
|
8106
|
+
});
|
|
8107
|
+
expect(
|
|
8108
|
+
ownershipRequests()
|
|
8109
|
+
.slice(-2)
|
|
8110
|
+
.map((url) => [
|
|
8111
|
+
url.searchParams.get("ownership"),
|
|
8112
|
+
url.searchParams.get("q"),
|
|
8113
|
+
]),
|
|
8114
|
+
).toEqual([
|
|
8115
|
+
["mine", "Other"],
|
|
8116
|
+
["shared", "Other"],
|
|
8117
|
+
]);
|
|
8118
|
+
expect(threadMocks.searchThreads).not.toHaveBeenCalled();
|
|
8119
|
+
} finally {
|
|
8120
|
+
vi.useRealTimers();
|
|
8121
|
+
}
|
|
8122
|
+
});
|
|
8123
|
+
|
|
8124
|
+
it("previews five shared rows without owner pins or actions and switches through history", async () => {
|
|
8125
|
+
sharedHistoryRows = Array.from({ length: 51 }, (_, index) => ({
|
|
8126
|
+
id: `shared-${index}`,
|
|
8127
|
+
title: `Shared ${index}`,
|
|
8128
|
+
preview: "",
|
|
8129
|
+
messageCount: 1,
|
|
8130
|
+
createdAt: 1,
|
|
8131
|
+
updatedAt: 1,
|
|
8132
|
+
scope: null,
|
|
8133
|
+
accessBasis: index === 0 ? "organization" : "direct",
|
|
8134
|
+
ownerEmail: "pat@example.com",
|
|
8135
|
+
visibility: "org",
|
|
8136
|
+
pinnedAt: 10,
|
|
8137
|
+
}));
|
|
8138
|
+
await openHistory();
|
|
8139
|
+
const sharedSection = container.querySelector(
|
|
8140
|
+
'.an-chat-history__section[data-section-id="shared"]',
|
|
8141
|
+
)!;
|
|
8142
|
+
expect(sharedSection.querySelectorAll(".an-chat-history-row")).toHaveLength(
|
|
8143
|
+
5,
|
|
8144
|
+
);
|
|
8145
|
+
expect(sharedSection.textContent).toContain("pat@example.com");
|
|
8146
|
+
expect(sharedSection.textContent).toContain(
|
|
8147
|
+
"Shared with your organization",
|
|
8148
|
+
);
|
|
8149
|
+
await openRowMenu(
|
|
8150
|
+
sharedSection.querySelector<HTMLButtonElement>(
|
|
8151
|
+
".an-chat-history-row__menu-trigger",
|
|
8152
|
+
)!,
|
|
8153
|
+
);
|
|
8154
|
+
expect(
|
|
8155
|
+
Array.from(
|
|
8156
|
+
document.body.querySelectorAll(".an-chat-history-row__menu-item"),
|
|
8157
|
+
(element) => element.textContent,
|
|
8158
|
+
),
|
|
8159
|
+
).toEqual(["Copy link"]);
|
|
8160
|
+
await act(async () => {
|
|
8161
|
+
document.dispatchEvent(
|
|
8162
|
+
new KeyboardEvent("keydown", { key: "Escape", bubbles: true }),
|
|
8163
|
+
);
|
|
8164
|
+
});
|
|
8165
|
+
expect(
|
|
8166
|
+
sharedSection.querySelector(".an-chat-history-row--pinned"),
|
|
8167
|
+
).toBeNull();
|
|
8168
|
+
expect(container.querySelector(".an-chat-history-rail__footer")).toBeNull();
|
|
8169
|
+
await act(async () => {
|
|
8170
|
+
sharedSection
|
|
8171
|
+
.querySelector<HTMLButtonElement>(
|
|
8172
|
+
".an-chat-history-rail__section-toggle",
|
|
8173
|
+
)!
|
|
8174
|
+
.click();
|
|
8175
|
+
});
|
|
8176
|
+
expect(sharedSection.querySelectorAll(".an-chat-history-row")).toHaveLength(
|
|
8177
|
+
50,
|
|
8178
|
+
);
|
|
8179
|
+
await act(async () => {
|
|
8180
|
+
sharedSection
|
|
8181
|
+
.querySelector<HTMLButtonElement>(".an-chat-history-rail__load-more")!
|
|
8182
|
+
.click();
|
|
8183
|
+
});
|
|
8184
|
+
expect(sharedSection.querySelectorAll(".an-chat-history-row")).toHaveLength(
|
|
8185
|
+
51,
|
|
8186
|
+
);
|
|
8187
|
+
await act(async () => {
|
|
8188
|
+
sharedSection
|
|
8189
|
+
.querySelector<HTMLButtonElement>(
|
|
8190
|
+
".an-chat-history-rail__section-toggle",
|
|
8191
|
+
)!
|
|
8192
|
+
.click();
|
|
8193
|
+
});
|
|
8194
|
+
expect(sharedSection.querySelectorAll(".an-chat-history-row")).toHaveLength(
|
|
8195
|
+
5,
|
|
8196
|
+
);
|
|
8197
|
+
await act(async () => {
|
|
8198
|
+
sharedSection
|
|
8199
|
+
.querySelector<HTMLButtonElement>(".an-chat-history-row__button")!
|
|
8200
|
+
.click();
|
|
8201
|
+
});
|
|
8202
|
+
expect(threadMocks.switchThread).toHaveBeenCalledWith("shared-0");
|
|
8203
|
+
expect(container.querySelector(".an-chat-history")).toBeNull();
|
|
8204
|
+
expect(
|
|
8205
|
+
chatThreadHookMocks.useChatThreads.mock.calls.at(-1)?.[3]?.ownership,
|
|
8206
|
+
).toBeUndefined();
|
|
8207
|
+
});
|
|
8208
|
+
|
|
7968
8209
|
it("groups pinned threads into a dedicated section, sorted ahead of the rest", async () => {
|
|
7969
8210
|
await openHistory();
|
|
7970
8211
|
|
|
7971
8212
|
const labels = Array.from(
|
|
7972
8213
|
container.querySelectorAll(".an-chat-history__section-label"),
|
|
7973
8214
|
).map((el) => el.textContent);
|
|
7974
|
-
expect(labels).toEqual(["Pinned"]);
|
|
8215
|
+
expect(labels).toEqual(["Pinned", "My chats"]);
|
|
7975
8216
|
|
|
7976
8217
|
const titles = Array.from(
|
|
7977
8218
|
container.querySelectorAll(".an-chat-history-row__title"),
|
|
@@ -8055,6 +8296,32 @@ describe("MultiTabAssistantChat history popover", () => {
|
|
|
8055
8296
|
expect(threadMocks.pinThread).toHaveBeenCalledWith("thread-3", true);
|
|
8056
8297
|
});
|
|
8057
8298
|
|
|
8299
|
+
it("shows the localized save failure when a history pin fails", async () => {
|
|
8300
|
+
const toastError = vi
|
|
8301
|
+
.spyOn(toast, "error")
|
|
8302
|
+
.mockReturnValue("failure-toast");
|
|
8303
|
+
threadMocks.pinThread.mockResolvedValue(false);
|
|
8304
|
+
try {
|
|
8305
|
+
await openHistory();
|
|
8306
|
+
const row = container.querySelectorAll(".an-chat-history-row")[2];
|
|
8307
|
+
await openRowMenu(
|
|
8308
|
+
row.querySelector<HTMLButtonElement>(
|
|
8309
|
+
".an-chat-history-row__menu-trigger",
|
|
8310
|
+
)!,
|
|
8311
|
+
);
|
|
8312
|
+
const pinItem = Array.from(
|
|
8313
|
+
document.body.querySelectorAll<HTMLButtonElement>(
|
|
8314
|
+
".an-chat-history-row__menu-item",
|
|
8315
|
+
),
|
|
8316
|
+
).find((element) => element.textContent?.includes("Pin to top"))!;
|
|
8317
|
+
await act(async () => pinItem.click());
|
|
8318
|
+
expect(threadMocks.pinThread).toHaveBeenCalledWith("thread-3", true);
|
|
8319
|
+
expect(toastError).toHaveBeenCalledExactlyOnceWith("Save failed");
|
|
8320
|
+
} finally {
|
|
8321
|
+
toastError.mockRestore();
|
|
8322
|
+
}
|
|
8323
|
+
});
|
|
8324
|
+
|
|
8058
8325
|
it("unpins an already-pinned thread via the row action menu", async () => {
|
|
8059
8326
|
await openHistory();
|
|
8060
8327
|
|
|
@@ -33,7 +33,6 @@ import {
|
|
|
33
33
|
import {
|
|
34
34
|
useChatThreads,
|
|
35
35
|
type ChatThreadScope,
|
|
36
|
-
type ChatThreadSummary,
|
|
37
36
|
} from "@agent-native/core/client/agent-chat";
|
|
38
37
|
import { agentNativePath, appPath } from "@agent-native/core/client/api-path";
|
|
39
38
|
import { getBrowserTabId } from "@agent-native/core/client/hooks";
|
|
@@ -80,17 +79,13 @@ import React, {
|
|
|
80
79
|
useLayoutEffect,
|
|
81
80
|
useCallback,
|
|
82
81
|
} from "react";
|
|
82
|
+
import { toast } from "sonner";
|
|
83
83
|
|
|
84
84
|
import {
|
|
85
85
|
getAgentChatNavigationKey,
|
|
86
86
|
type AgentChatNavigationKey,
|
|
87
87
|
} from "./agent-sidebar-events.js";
|
|
88
88
|
import { AgentKitAssistantChat } from "./AgentKitAssistantChat.js";
|
|
89
|
-
import {
|
|
90
|
-
ChatHistoryList,
|
|
91
|
-
type ChatHistoryItem,
|
|
92
|
-
type ChatHistorySection,
|
|
93
|
-
} from "./chat/ChatHistoryList.js";
|
|
94
89
|
import type {
|
|
95
90
|
AssistantChatProps,
|
|
96
91
|
AssistantChatHandle,
|
|
@@ -98,6 +93,12 @@ import type {
|
|
|
98
93
|
AssistantChatSendOptions,
|
|
99
94
|
} from "./chat/surface-types.js";
|
|
100
95
|
import { fallbackChatTitle } from "./fallback-chat-title.js";
|
|
96
|
+
import {
|
|
97
|
+
OwnershipChatHistoryRail,
|
|
98
|
+
ownDraftRows,
|
|
99
|
+
useOwnershipChatHistory,
|
|
100
|
+
type OwnDraftRow,
|
|
101
|
+
} from "./ownership-history/index.js";
|
|
101
102
|
|
|
102
103
|
type AgentActionScope = NonNullable<AgentChatMessage["actionScope"]>;
|
|
103
104
|
const PREFILL_CONTEXT_KEY = "agent-chat-prefill-context";
|
|
@@ -488,139 +489,75 @@ function formatThreadTime(
|
|
|
488
489
|
}
|
|
489
490
|
|
|
490
491
|
function HistoryPopover({
|
|
491
|
-
|
|
492
|
+
apiUrl,
|
|
493
|
+
scope,
|
|
494
|
+
localMineRows,
|
|
492
495
|
openTabIds,
|
|
493
496
|
activeThreadId,
|
|
494
|
-
hasMoreThreads = false,
|
|
495
|
-
isLoadingMoreThreads = false,
|
|
496
|
-
loadError,
|
|
497
497
|
onSelect,
|
|
498
|
+
onNewChat,
|
|
498
499
|
onClose,
|
|
499
|
-
onLoadMore,
|
|
500
|
-
onSearch,
|
|
501
500
|
onTogglePin,
|
|
502
501
|
onRename,
|
|
502
|
+
getThreadUrl,
|
|
503
503
|
popoverAlign = "end",
|
|
504
504
|
}: {
|
|
505
|
-
|
|
505
|
+
apiUrl?: string;
|
|
506
|
+
scope?: ChatThreadScope;
|
|
507
|
+
localMineRows: OwnDraftRow[];
|
|
506
508
|
openTabIds: Set<string>;
|
|
507
509
|
activeThreadId: string | null;
|
|
508
|
-
hasMoreThreads?: boolean;
|
|
509
|
-
isLoadingMoreThreads?: boolean;
|
|
510
|
-
loadError?: string | null;
|
|
511
510
|
onSelect: (id: string) => void;
|
|
511
|
+
onNewChat: () => void;
|
|
512
512
|
onClose: () => void;
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
* current pinned state so the caller can flip it. */
|
|
517
|
-
onTogglePin?: (id: string, pinned: boolean) => void;
|
|
518
|
-
/** Presence enables the inline rename row action. */
|
|
519
|
-
onRename?: (id: string, nextTitle: string) => void;
|
|
513
|
+
onTogglePin: (id: string, pinned: boolean) => Promise<boolean>;
|
|
514
|
+
onRename: (id: string, nextTitle: string) => Promise<boolean>;
|
|
515
|
+
getThreadUrl: (id: string) => string;
|
|
520
516
|
popoverAlign?: "start" | "end";
|
|
521
517
|
}) {
|
|
522
518
|
const t = useT();
|
|
523
519
|
const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
|
|
524
520
|
const [search, setSearch] = useState("");
|
|
525
|
-
const [
|
|
526
|
-
ChatThreadSummary[] | null
|
|
527
|
-
>(null);
|
|
528
|
-
const [isSearching, setIsSearching] = useState(false);
|
|
521
|
+
const [query, setQuery] = useState("");
|
|
529
522
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
530
|
-
const debounceRef = useRef<ReturnType<typeof setTimeout>>(undefined);
|
|
531
523
|
|
|
532
524
|
useEffect(() => {
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
setSearchResults(results);
|
|
554
|
-
} else {
|
|
555
|
-
// Fallback to client-side filtering
|
|
556
|
-
setSearchResults(null);
|
|
557
|
-
}
|
|
558
|
-
setIsSearching(false);
|
|
559
|
-
}, 250);
|
|
560
|
-
return () => {
|
|
561
|
-
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
562
|
-
};
|
|
563
|
-
}, [search, onSearch]);
|
|
564
|
-
|
|
565
|
-
// Hide empty threads from the history list — except the currently-active
|
|
566
|
-
// one. The active thread always belongs in the list so the user can see
|
|
567
|
-
// they're in it (the previous filter dropped a brand-new chat the user
|
|
568
|
-
// had just opened, making them think their chat had vanished).
|
|
569
|
-
const visibleThreads = threads.filter(
|
|
570
|
-
(t) => t.messageCount > 0 || t.id === activeThreadId,
|
|
571
|
-
);
|
|
572
|
-
|
|
573
|
-
const filtered = search.trim()
|
|
574
|
-
? (searchResults ?? visibleThreads).filter(
|
|
575
|
-
(t) => t.messageCount > 0 || t.id === activeThreadId,
|
|
576
|
-
)
|
|
577
|
-
: visibleThreads;
|
|
578
|
-
|
|
579
|
-
// Pinned threads always float to the top of `threads` already (see
|
|
580
|
-
// `sortThreadSummaries` in use-chat-threads.ts and the matching server
|
|
581
|
-
// ORDER BY), but pulling them into their own labeled section — instead of
|
|
582
|
-
// just relying on sort order within "All chats" — makes the pin affordance
|
|
583
|
-
// visible at a glance, matching common chat-history UX.
|
|
584
|
-
const pinnedThreads = filtered.filter((t) => t.pinnedAt != null);
|
|
585
|
-
const unpinnedThreads = filtered.filter((t) => t.pinnedAt == null);
|
|
586
|
-
|
|
587
|
-
const toHistoryItem = (thread: ChatThreadSummary): ChatHistoryItem => {
|
|
588
|
-
const isActive = thread.id === activeThreadId;
|
|
589
|
-
const hasTitle = Boolean(thread.title);
|
|
590
|
-
const title = thread.title || t("agentChat.history.untitledChat");
|
|
591
|
-
return {
|
|
592
|
-
id: thread.id,
|
|
593
|
-
title,
|
|
594
|
-
titleText: title,
|
|
525
|
+
const timer = setTimeout(() => setQuery(search.trim()), 250);
|
|
526
|
+
return () => clearTimeout(timer);
|
|
527
|
+
}, [search]);
|
|
528
|
+
|
|
529
|
+
// This component is mounted only while the popover is open.
|
|
530
|
+
const history = useOwnershipChatHistory({
|
|
531
|
+
apiUrl,
|
|
532
|
+
scope,
|
|
533
|
+
enabled: true,
|
|
534
|
+
query,
|
|
535
|
+
activeId: activeThreadId,
|
|
536
|
+
pinned: true,
|
|
537
|
+
localMineRows,
|
|
538
|
+
mutations: { pin: onTogglePin, rename: onRename },
|
|
539
|
+
getThreadUrl,
|
|
540
|
+
onMutationFailed: () => toast.error(t("agentChat.common.saveFailed")),
|
|
541
|
+
decorateItem: (item, row) => ({
|
|
542
|
+
...item,
|
|
543
|
+
title: row.title || t("agentChat.history.untitledChat"),
|
|
544
|
+
titleText: row.title || t("agentChat.history.untitledChat"),
|
|
595
545
|
subtitle:
|
|
596
|
-
|
|
597
|
-
?
|
|
546
|
+
row.title && row.preview && row.title !== row.preview
|
|
547
|
+
? row.preview
|
|
598
548
|
: undefined,
|
|
599
|
-
timestamp:
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
}
|
|
610
|
-
};
|
|
611
|
-
|
|
612
|
-
const historySections: ChatHistorySection[] = [
|
|
613
|
-
...(pinnedThreads.length > 0
|
|
614
|
-
? [
|
|
615
|
-
{
|
|
616
|
-
id: "pinned",
|
|
617
|
-
label: t("agentChat.history.pinned"),
|
|
618
|
-
items: pinnedThreads.map(toHistoryItem),
|
|
619
|
-
},
|
|
620
|
-
]
|
|
621
|
-
: []),
|
|
622
|
-
{ id: "all", items: unpinnedThreads.map(toHistoryItem) },
|
|
623
|
-
];
|
|
549
|
+
timestamp:
|
|
550
|
+
row.id === activeThreadId
|
|
551
|
+
? t("agentChat.history.active")
|
|
552
|
+
: openTabIds.has(row.id)
|
|
553
|
+
? t("agentChat.history.open")
|
|
554
|
+
: formatThreadTime(
|
|
555
|
+
row.updatedAt,
|
|
556
|
+
locale,
|
|
557
|
+
t("agentChat.history.yesterday"),
|
|
558
|
+
),
|
|
559
|
+
}),
|
|
560
|
+
});
|
|
624
561
|
|
|
625
562
|
return (
|
|
626
563
|
<Popover open onOpenChange={(open) => !open && onClose()}>
|
|
@@ -640,46 +577,23 @@ function HistoryPopover({
|
|
|
640
577
|
}}
|
|
641
578
|
className="w-72 rounded-lg p-0"
|
|
642
579
|
>
|
|
643
|
-
<
|
|
644
|
-
|
|
580
|
+
<OwnershipChatHistoryRail
|
|
581
|
+
{...history}
|
|
645
582
|
activeId={activeThreadId}
|
|
583
|
+
onNewChat={onNewChat}
|
|
584
|
+
showNewChat={false}
|
|
646
585
|
onSelect={(id) => {
|
|
647
586
|
onSelect(id);
|
|
648
587
|
onClose();
|
|
649
588
|
}}
|
|
650
|
-
onTogglePin={
|
|
651
|
-
onTogglePin
|
|
652
|
-
? (id) => {
|
|
653
|
-
const isPinned =
|
|
654
|
-
threads.find((t) => t.id === id)?.pinnedAt != null;
|
|
655
|
-
onTogglePin(id, !isPinned);
|
|
656
|
-
}
|
|
657
|
-
: undefined
|
|
658
|
-
}
|
|
659
|
-
onRename={onRename}
|
|
660
589
|
searchValue={search}
|
|
661
590
|
onSearchChange={setSearch}
|
|
662
591
|
searchPlaceholder={t("agentChat.history.search")}
|
|
663
592
|
searchInputRef={inputRef}
|
|
664
|
-
loading={isSearching}
|
|
665
593
|
loadingLabel={t("agentChat.history.searching")}
|
|
666
|
-
error={loadError && !search.trim() ? loadError : undefined}
|
|
667
594
|
emptyLabel={t("agentChat.history.empty")}
|
|
668
595
|
emptySearchLabel={t("agentChat.history.noMatches")}
|
|
669
|
-
|
|
670
|
-
!search.trim() && hasMoreThreads ? (
|
|
671
|
-
<button
|
|
672
|
-
type="button"
|
|
673
|
-
onClick={() => onLoadMore?.()}
|
|
674
|
-
disabled={isLoadingMoreThreads}
|
|
675
|
-
className="mx-1 mt-1 flex w-[calc(100%-0.5rem)] items-center justify-center rounded-md px-3 py-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground disabled:cursor-default disabled:opacity-60"
|
|
676
|
-
>
|
|
677
|
-
{isLoadingMoreThreads
|
|
678
|
-
? t("agentChat.common.loading")
|
|
679
|
-
: t("agentChat.history.loadOlder")}
|
|
680
|
-
</button>
|
|
681
|
-
) : undefined
|
|
682
|
-
}
|
|
596
|
+
className="an-chat-history--capped"
|
|
683
597
|
/>
|
|
684
598
|
</PopoverContent>
|
|
685
599
|
</Popover>
|
|
@@ -1284,12 +1198,7 @@ export function MultiTabAssistantChat({
|
|
|
1284
1198
|
forkThread,
|
|
1285
1199
|
saveThreadData,
|
|
1286
1200
|
generateTitle,
|
|
1287
|
-
searchThreads,
|
|
1288
|
-
loadMoreThreads,
|
|
1289
1201
|
refreshThreads,
|
|
1290
|
-
hasMoreThreads,
|
|
1291
|
-
isLoadingMoreThreads,
|
|
1292
|
-
threadsLoadError,
|
|
1293
1202
|
restoredThreadIdOnListFailure,
|
|
1294
1203
|
evictedThreadIds,
|
|
1295
1204
|
isNewThread,
|
|
@@ -1333,6 +1242,7 @@ export function MultiTabAssistantChat({
|
|
|
1333
1242
|
const [showHistory, setShowHistory] = useState(false);
|
|
1334
1243
|
const [pageOverlayScrolled, setPageOverlayScrolled] = useState(false);
|
|
1335
1244
|
const newThreadIds = useRef<Set<string>>(new Set());
|
|
1245
|
+
const observedThreadStarts = useRef(new Map<string, number>());
|
|
1336
1246
|
const [, setThreadPersistenceVersion] = useState(0);
|
|
1337
1247
|
const handleThreadSnapshotPersisted = useCallback(
|
|
1338
1248
|
(threadId: string, messageCount: number) => {
|
|
@@ -2732,6 +2642,7 @@ export function MultiTabAssistantChat({
|
|
|
2732
2642
|
if (!detail) return;
|
|
2733
2643
|
const { isRunning, tabId } = detail;
|
|
2734
2644
|
if (!tabId) return;
|
|
2645
|
+
if (isRunning) observedThreadStarts.current.set(tabId, Date.now());
|
|
2735
2646
|
|
|
2736
2647
|
setRunningThreads((prev) => {
|
|
2737
2648
|
const next = new Set(prev);
|
|
@@ -3769,7 +3680,7 @@ export function MultiTabAssistantChat({
|
|
|
3769
3680
|
key={tab.id}
|
|
3770
3681
|
ref={isActive ? activeTabRefCb : undefined}
|
|
3771
3682
|
className={cn(
|
|
3772
|
-
"agent-tab agent-tab-group relative flex items-center rounded-md text-[11px] font-medium shrink
|
|
3683
|
+
"agent-tab agent-tab-group relative flex items-center rounded-md text-[11px] font-medium shrink min-w-[56px] max-w-[130px]",
|
|
3773
3684
|
isActive
|
|
3774
3685
|
? "bg-accent text-foreground ring-1 ring-inset ring-border/60 shadow-sm"
|
|
3775
3686
|
: "text-muted-foreground hover:text-foreground hover:bg-accent/50",
|
|
@@ -3871,7 +3782,7 @@ export function MultiTabAssistantChat({
|
|
|
3871
3782
|
: undefined
|
|
3872
3783
|
}
|
|
3873
3784
|
className={cn(
|
|
3874
|
-
"agent-tab agent-tab-group relative flex shrink
|
|
3785
|
+
"agent-tab agent-tab-group relative flex shrink items-center rounded-md text-[10px] font-medium min-w-[48px] max-w-[130px]",
|
|
3875
3786
|
tab.id === activeThreadId
|
|
3876
3787
|
? "bg-accent text-foreground"
|
|
3877
3788
|
: "text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
@@ -3939,18 +3850,30 @@ export function MultiTabAssistantChat({
|
|
|
3939
3850
|
{/* History popover — rendered inside relative container so positioning works */}
|
|
3940
3851
|
{showHistory && (
|
|
3941
3852
|
<HistoryPopover
|
|
3942
|
-
|
|
3853
|
+
apiUrl={apiUrl}
|
|
3854
|
+
scope={isolateHistoryByScope ? (scope ?? undefined) : undefined}
|
|
3855
|
+
localMineRows={ownDraftRows({
|
|
3856
|
+
threads: threads.map((thread) => ({
|
|
3857
|
+
...thread,
|
|
3858
|
+
messageCount: messageCounts[thread.id] ?? thread.messageCount,
|
|
3859
|
+
})),
|
|
3860
|
+
isNewThread,
|
|
3861
|
+
activeId: activeThreadId,
|
|
3862
|
+
observedThreadStarts: observedThreadStarts.current,
|
|
3863
|
+
})}
|
|
3943
3864
|
openTabIds={new Set(openTabIds)}
|
|
3944
3865
|
activeThreadId={activeThreadId}
|
|
3945
|
-
hasMoreThreads={hasMoreThreads}
|
|
3946
|
-
isLoadingMoreThreads={isLoadingMoreThreads}
|
|
3947
|
-
loadError={threadsLoadError}
|
|
3948
3866
|
onSelect={openFromHistory}
|
|
3867
|
+
onNewChat={addTab}
|
|
3949
3868
|
onClose={() => setShowHistory(false)}
|
|
3950
|
-
onLoadMore={loadMoreThreads}
|
|
3951
|
-
onSearch={searchThreads}
|
|
3952
3869
|
onTogglePin={pinThread}
|
|
3953
3870
|
onRename={renameThread}
|
|
3871
|
+
getThreadUrl={(threadId) => {
|
|
3872
|
+
if (getThreadPath) return appPath(getThreadPath(threadId));
|
|
3873
|
+
const url = new URL(window.location.href);
|
|
3874
|
+
url.searchParams.set(threadUrlParamName, threadId);
|
|
3875
|
+
return url.toString();
|
|
3876
|
+
}}
|
|
3954
3877
|
popoverAlign={renderOverlay ? "start" : "end"}
|
|
3955
3878
|
/>
|
|
3956
3879
|
)}
|