@agent-native/toolkit 0.198.3 → 0.198.4
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/app/agentkit/react/components.d.ts +4 -1
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +12 -4
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +43 -21
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/AgentSidebar.d.ts.map +1 -1
- package/dist/app/chat/AgentSidebar.js +2 -0
- package/dist/app/chat/AgentSidebar.js.map +1 -1
- package/dist/app/chat/FileStorageSetupPopover.js +2 -2
- package/dist/app/chat/FileStorageSetupPopover.js.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.js +1 -1
- package/dist/app/chat/agentkit-chat/connections.d.ts +1 -4
- package/dist/app/chat/agentkit-chat/connections.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/connections.js +1 -26
- package/dist/app/chat/agentkit-chat/connections.js.map +1 -1
- package/dist/app/chat/agentkit-chat/history.d.ts +4 -1
- package/dist/app/chat/agentkit-chat/history.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/history.js +17 -5
- package/dist/app/chat/agentkit-chat/history.js.map +1 -1
- package/dist/app/chat/chat/history-types.d.ts +1 -1
- package/dist/app/chat/chat/history-types.d.ts.map +1 -1
- package/dist/app/chat/chat-first/copy.d.ts +1 -2
- package/dist/app/chat/chat-first/copy.d.ts.map +1 -1
- package/dist/app/chat/chat-first/copy.js +1 -121
- package/dist/app/chat/chat-first/copy.js.map +1 -1
- package/dist/app/chat/chat-first-copy.d.ts +3 -0
- package/dist/app/chat/chat-first-copy.d.ts.map +1 -0
- package/dist/app/chat/chat-first-copy.js +122 -0
- package/dist/app/chat/chat-first-copy.js.map +1 -0
- package/dist/app/feature-flags/FeatureFlagsPanel.d.ts.map +1 -1
- package/dist/app/feature-flags/FeatureFlagsPanel.js +1 -0
- package/dist/app/feature-flags/FeatureFlagsPanel.js.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +2 -1
- 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 +2 -1
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +2 -1
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +2 -1
- 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 +2 -1
- 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 +2 -1
- 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 +2 -1
- 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 +2 -1
- 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 +2 -1
- 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 +2 -1
- 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 +2 -1
- 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 +2 -1
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/labs/LabsSettings.d.ts.map +1 -1
- package/dist/app/labs/LabsSettings.js +7 -3
- package/dist/app/labs/LabsSettings.js.map +1 -1
- package/dist/app/org/OrgGeneralSection.d.ts.map +1 -1
- package/dist/app/org/OrgGeneralSection.js +21 -18
- package/dist/app/org/OrgGeneralSection.js.map +1 -1
- package/dist/app/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/app/org/OrgSwitcher.js +3 -2
- package/dist/app/org/OrgSwitcher.js.map +1 -1
- package/dist/app/org/workspace-app-links.d.ts +1 -0
- package/dist/app/org/workspace-app-links.d.ts.map +1 -1
- package/dist/app/org/workspace-app-links.js +3 -0
- package/dist/app/org/workspace-app-links.js.map +1 -1
- package/dist/app/resources/McpAgentKitConnectionRequest.d.ts +7 -2
- package/dist/app/resources/McpAgentKitConnectionRequest.d.ts.map +1 -1
- package/dist/app/resources/McpAgentKitConnectionRequest.js +132 -13
- package/dist/app/resources/McpAgentKitConnectionRequest.js.map +1 -1
- package/dist/icons/ResourceIconPicker.d.ts +6 -1
- package/dist/icons/ResourceIconPicker.d.ts.map +1 -1
- package/dist/icons/ResourceIconPicker.js +45 -6
- package/dist/icons/ResourceIconPicker.js.map +1 -1
- package/package.json +6 -4
- package/src/app/agentkit/react/components.spec.tsx +3 -3
- package/src/app/agentkit/react/components.tsx +22 -4
- package/src/app/agentkit/react/lifecycle.spec.tsx +42 -0
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +114 -11
- package/src/app/chat/AgentKitAssistantChat.tsx +78 -17
- package/src/app/chat/AgentPanel.header.spec.ts +59 -22
- package/src/app/chat/AgentSidebar.tsx +2 -0
- package/src/app/chat/FileStorageSetupPopover.tsx +2 -2
- package/src/app/chat/MultiTabAssistantChat.messageCount.spec.tsx +3 -1
- package/src/app/chat/MultiTabAssistantChat.spec.tsx +8 -4
- package/src/app/chat/MultiTabAssistantChat.tsx +1 -1
- package/src/app/chat/agentkit-chat/action-widget.spec.tsx +5 -17
- package/src/app/chat/agentkit-chat/connections.spec.tsx +137 -0
- package/src/app/chat/agentkit-chat/connections.ts +2 -37
- package/src/app/chat/agentkit-chat/history.tsx +23 -8
- package/src/app/chat/chat/history-types.ts +1 -1
- package/src/app/chat/chat/tool-call-display.spec.tsx +2 -2
- package/src/app/chat/chat/widgets/record-change-widget.spec.tsx +20 -29
- package/src/app/chat/chat-first/copy.ts +1 -133
- package/src/app/chat/chat-first-copy.ts +133 -0
- package/src/app/chat/composer/use-agentkit-capabilities.hardening.spec.tsx +3 -2
- package/src/app/feature-flags/FeatureFlagsPanel.tsx +1 -0
- package/src/app/i18n/catalogs/ar-SA.ts +2 -1
- package/src/app/i18n/catalogs/de-DE.ts +3 -1
- package/src/app/i18n/catalogs/en-US.ts +2 -1
- package/src/app/i18n/catalogs/es-ES.ts +2 -1
- package/src/app/i18n/catalogs/fr-FR.ts +3 -1
- package/src/app/i18n/catalogs/hi-IN.ts +2 -1
- package/src/app/i18n/catalogs/ja-JP.ts +2 -1
- package/src/app/i18n/catalogs/ko-KR.ts +2 -1
- package/src/app/i18n/catalogs/pt-BR.ts +3 -1
- package/src/app/i18n/catalogs/zh-CN.ts +2 -1
- package/src/app/i18n/catalogs/zh-TW.ts +2 -1
- package/src/app/integrations/IntegrationDetailPage.spec.tsx +1 -1
- package/src/app/labs/LabsSettings.tsx +46 -10
- package/src/app/observability/ThumbsFeedback.spec.tsx +15 -0
- package/src/app/onboarding/FirstRunOnboarding.spec.tsx +4 -0
- package/src/app/org/OrgGeneralSection.tsx +32 -22
- package/src/app/org/OrgSwitcher.tsx +3 -2
- package/src/app/org/workspace-app-links.spec.ts +4 -0
- package/src/app/org/workspace-app-links.ts +8 -1
- package/src/app/resources/McpAgentKitConnectionRequest.spec.tsx +371 -2
- package/src/app/resources/McpAgentKitConnectionRequest.tsx +171 -11
- package/src/app/settings/SettingsTabsPage.spec.tsx +4 -2
- package/src/app/settings/shell/SettingsShell.spec.tsx +14 -8
- package/src/app/settings/shell/pages/app-group-pages.spec.tsx +35 -5
- package/src/app/settings/shell/pages/resource-pages.spec.tsx +3 -0
- package/src/app/settings/usage/UsagePage.spec.tsx +3 -3
- package/src/app/shared/lazy-chunk-error-boundary.spec.tsx +2 -0
- package/src/app-shell/sidebar-overflow.spec.tsx +4 -1
- package/src/design-tweaks/visual-style-controls.spec.tsx +38 -0
- package/src/editor/useCollabReconcile.concurrent.spec.ts +2 -1
- package/src/icons/ResourceIconPicker.spec.tsx +270 -0
- package/src/icons/ResourceIconPicker.tsx +72 -4
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
import type { AgentConnectionRequest } from "@agent-native/agentkit/protocol";
|
|
1
2
|
import {
|
|
2
|
-
|
|
3
|
+
agentNativePath,
|
|
4
|
+
appBasePath,
|
|
5
|
+
} from "@agent-native/core/client/api-path";
|
|
6
|
+
import { useT } from "@agent-native/core/client/i18n";
|
|
7
|
+
import {
|
|
8
|
+
getWorkspaceConnectionProvider,
|
|
9
|
+
workspaceProviderOAuthUrl,
|
|
10
|
+
} from "@agent-native/core/client/integrations";
|
|
11
|
+
import { openOAuthPopup } from "@agent-native/core/client/oauth-popup";
|
|
12
|
+
import {
|
|
13
|
+
addMcpConnectionCompleteListener,
|
|
14
|
+
clearMcpConnectionResume,
|
|
15
|
+
getPendingMcpConnectionResume,
|
|
3
16
|
saveMcpConnectionResume,
|
|
4
17
|
type McpConnectionResumeRequest,
|
|
5
18
|
} from "@agent-native/core/client/resources/mcp-connection-resume";
|
|
@@ -7,8 +20,14 @@ import {
|
|
|
7
20
|
getDefaultMcpIntegrations,
|
|
8
21
|
navigateToMcpOAuthStart,
|
|
9
22
|
} from "@agent-native/core/client/resources/mcp-integration-catalog";
|
|
10
|
-
import {
|
|
23
|
+
import { IconAlertCircle } from "@tabler/icons-react";
|
|
24
|
+
import { useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
|
11
25
|
|
|
26
|
+
import { AgentConnectionRequestCard } from "../agentkit/react/components.js";
|
|
27
|
+
import {
|
|
28
|
+
dispatchIntegrationsHref,
|
|
29
|
+
useOrgSwitcherAppLinks,
|
|
30
|
+
} from "../org/workspace-app-links.js";
|
|
12
31
|
import { McpConnectionSuggestion } from "./McpConnectionSuggestion.js";
|
|
13
32
|
|
|
14
33
|
export interface McpAgentKitConnectionTarget {
|
|
@@ -20,6 +39,10 @@ export interface McpAgentKitConnectionTarget {
|
|
|
20
39
|
export interface McpAgentKitConnectionRequestCardProps {
|
|
21
40
|
provider: string;
|
|
22
41
|
detail?: string;
|
|
42
|
+
reason?: AgentConnectionRequest["reason"];
|
|
43
|
+
status?: AgentConnectionRequest["status"];
|
|
44
|
+
appId?: string;
|
|
45
|
+
source?: AgentConnectionRequest["source"];
|
|
23
46
|
target: McpAgentKitConnectionTarget;
|
|
24
47
|
onConnected: () => void | Promise<void>;
|
|
25
48
|
onDeclined: () => void | Promise<void>;
|
|
@@ -29,18 +52,35 @@ export interface McpAgentKitConnectionRequestCardProps {
|
|
|
29
52
|
export function McpAgentKitConnectionRequestCard({
|
|
30
53
|
provider,
|
|
31
54
|
detail,
|
|
55
|
+
reason,
|
|
56
|
+
status = "requested",
|
|
57
|
+
appId,
|
|
58
|
+
source,
|
|
32
59
|
target,
|
|
33
60
|
onConnected,
|
|
34
61
|
onDeclined,
|
|
35
62
|
fallback = null,
|
|
36
63
|
}: McpAgentKitConnectionRequestCardProps) {
|
|
37
64
|
const settledRef = useRef(false);
|
|
65
|
+
const [workspaceSetupOpened, setWorkspaceSetupOpened] = useState(false);
|
|
38
66
|
const integrations = useMemo(() => getDefaultMcpIntegrations(), []);
|
|
39
67
|
const integration = integrations.find(
|
|
40
68
|
(candidate) =>
|
|
41
69
|
candidate.id.toLowerCase() === provider.trim().toLowerCase() ||
|
|
42
70
|
candidate.provider.toLowerCase() === provider.trim().toLowerCase(),
|
|
43
71
|
);
|
|
72
|
+
const workspaceProvider =
|
|
73
|
+
source?.kind === "workspace_connection" && source.id === provider
|
|
74
|
+
? getWorkspaceConnectionProvider(source.id)
|
|
75
|
+
: null;
|
|
76
|
+
const needsWorkspaceSetup =
|
|
77
|
+
source?.kind === "workspace_connection" &&
|
|
78
|
+
source.id === provider &&
|
|
79
|
+
(reason === "grant" || !workspaceProvider?.oauth);
|
|
80
|
+
const { apps: workspaceApps } = useOrgSwitcherAppLinks(needsWorkspaceSetup);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (status === "failed") setWorkspaceSetupOpened(false);
|
|
83
|
+
}, [status]);
|
|
44
84
|
const settle = async (callback: () => void | Promise<void>) => {
|
|
45
85
|
if (settledRef.current) return;
|
|
46
86
|
settledRef.current = true;
|
|
@@ -51,6 +91,86 @@ export function McpAgentKitConnectionRequestCard({
|
|
|
51
91
|
throw error;
|
|
52
92
|
}
|
|
53
93
|
};
|
|
94
|
+
if (source?.kind === "workspace_connection") {
|
|
95
|
+
if (!workspaceProvider || !appId) return fallback;
|
|
96
|
+
const request: AgentConnectionRequest = {
|
|
97
|
+
id: target.requestId,
|
|
98
|
+
provider,
|
|
99
|
+
reason: reason ?? "connect",
|
|
100
|
+
status,
|
|
101
|
+
appId,
|
|
102
|
+
detail,
|
|
103
|
+
source,
|
|
104
|
+
};
|
|
105
|
+
if (reason === "grant" || !workspaceProvider.oauth) {
|
|
106
|
+
return (
|
|
107
|
+
<AgentConnectionRequestCard
|
|
108
|
+
request={request}
|
|
109
|
+
runId={target.runId}
|
|
110
|
+
providerLabel={source.label}
|
|
111
|
+
retry={workspaceSetupOpened}
|
|
112
|
+
onConnect={() => {
|
|
113
|
+
if (workspaceSetupOpened) return onConnected();
|
|
114
|
+
const setupWindow = window.open("", "_blank");
|
|
115
|
+
if (!setupWindow) return false;
|
|
116
|
+
try {
|
|
117
|
+
setupWindow.opener = null;
|
|
118
|
+
setupWindow.location.assign(
|
|
119
|
+
dispatchIntegrationsHref(workspaceApps),
|
|
120
|
+
);
|
|
121
|
+
} catch {
|
|
122
|
+
setupWindow.close();
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
setWorkspaceSetupOpened(true);
|
|
126
|
+
}}
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return (
|
|
131
|
+
<AgentConnectionRequestCard
|
|
132
|
+
request={request}
|
|
133
|
+
runId={target.runId}
|
|
134
|
+
providerLabel={source.label}
|
|
135
|
+
onConnect={() => {
|
|
136
|
+
const popup = openOAuthPopup({ features: "width=640,height=760" });
|
|
137
|
+
if (!popup) return false;
|
|
138
|
+
const completionId = crypto.randomUUID();
|
|
139
|
+
const message = detail ?? `Continue after connecting ${provider}.`;
|
|
140
|
+
if (!saveMcpConnectionResume(message, target, completionId)) {
|
|
141
|
+
popup.close();
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
const returnUrl = new URL(
|
|
145
|
+
agentNativePath("/_agent-native/oauth/popup"),
|
|
146
|
+
window.location.href,
|
|
147
|
+
);
|
|
148
|
+
returnUrl.searchParams.set("complete", "workspace-connection");
|
|
149
|
+
returnUrl.searchParams.set("resume", completionId);
|
|
150
|
+
const basePath = appBasePath();
|
|
151
|
+
const hasBasePath =
|
|
152
|
+
basePath && returnUrl.pathname.startsWith(basePath + "/");
|
|
153
|
+
const returnPath =
|
|
154
|
+
(hasBasePath
|
|
155
|
+
? returnUrl.pathname.slice(basePath.length)
|
|
156
|
+
: returnUrl.pathname) + returnUrl.search;
|
|
157
|
+
try {
|
|
158
|
+
popup.location.assign(
|
|
159
|
+
workspaceProviderOAuthUrl(source.id, {
|
|
160
|
+
appId,
|
|
161
|
+
scope: "user",
|
|
162
|
+
returnPath,
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
165
|
+
} catch (error) {
|
|
166
|
+
clearMcpConnectionResume(completionId);
|
|
167
|
+
popup.close();
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}}
|
|
171
|
+
/>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
54
174
|
if (!integration) return fallback;
|
|
55
175
|
return (
|
|
56
176
|
<McpConnectionSuggestion
|
|
@@ -87,14 +207,54 @@ export function McpAgentKitConnectionResume({
|
|
|
87
207
|
onResume,
|
|
88
208
|
onMessageResume,
|
|
89
209
|
}: McpAgentKitConnectionResumeProps) {
|
|
210
|
+
const t = useT();
|
|
211
|
+
const onResumeRef = useRef(onResume);
|
|
212
|
+
const onMessageResumeRef = useRef(onMessageResume);
|
|
213
|
+
const processingRef = useRef(false);
|
|
214
|
+
const [failed, setFailed] = useState(false);
|
|
215
|
+
onResumeRef.current = onResume;
|
|
216
|
+
onMessageResumeRef.current = onMessageResume;
|
|
217
|
+
|
|
90
218
|
useEffect(() => {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
219
|
+
const resumePending = async () => {
|
|
220
|
+
if (processingRef.current) return;
|
|
221
|
+
const pending = getPendingMcpConnectionResume();
|
|
222
|
+
if (!pending) return;
|
|
223
|
+
processingRef.current = true;
|
|
224
|
+
try {
|
|
225
|
+
if (pending.agentKit) {
|
|
226
|
+
try {
|
|
227
|
+
await onResumeRef.current(pending.agentKit, pending);
|
|
228
|
+
} catch (error) {
|
|
229
|
+
if (
|
|
230
|
+
!(error instanceof Error) ||
|
|
231
|
+
!error.message.startsWith("Unknown AgentKit run:") ||
|
|
232
|
+
!onMessageResumeRef.current
|
|
233
|
+
) {
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
await onMessageResumeRef.current(pending);
|
|
237
|
+
}
|
|
238
|
+
} else {
|
|
239
|
+
if (!onMessageResumeRef.current) return;
|
|
240
|
+
await onMessageResumeRef.current(pending);
|
|
241
|
+
}
|
|
242
|
+
clearMcpConnectionResume(pending);
|
|
243
|
+
setFailed(false);
|
|
244
|
+
} finally {
|
|
245
|
+
processingRef.current = false;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
const resume = () => {
|
|
249
|
+
void resumePending().catch(() => setFailed(true));
|
|
250
|
+
};
|
|
251
|
+
resume();
|
|
252
|
+
return addMcpConnectionCompleteListener(resume);
|
|
253
|
+
}, []);
|
|
254
|
+
return failed ? (
|
|
255
|
+
<div className="agentkit-command-error" role="alert">
|
|
256
|
+
<IconAlertCircle aria-hidden="true" className="agentkit-icon" />
|
|
257
|
+
<span>{t("agentChat.connection.failed")}</span>
|
|
258
|
+
</div>
|
|
259
|
+
) : null;
|
|
100
260
|
}
|
|
@@ -518,6 +518,7 @@ describe("SettingsTabsPage", () => {
|
|
|
518
518
|
});
|
|
519
519
|
|
|
520
520
|
it("renders What's new markdown in the legacy settings fallback", async () => {
|
|
521
|
+
await import("../changelog/Changelog.js");
|
|
521
522
|
act(() => {
|
|
522
523
|
root.render(
|
|
523
524
|
<SettingsTabsPage
|
|
@@ -607,11 +608,11 @@ describe("SettingsTabsPage", () => {
|
|
|
607
608
|
]);
|
|
608
609
|
});
|
|
609
610
|
|
|
610
|
-
it("keeps shell-only app groups out of the tabbed page", () => {
|
|
611
|
+
it("keeps shell-only app groups out of the tabbed page with legacy General content", () => {
|
|
611
612
|
act(() => {
|
|
612
613
|
root.render(
|
|
613
614
|
<SettingsTabsPage
|
|
614
|
-
general={<div>General content</div>}
|
|
615
|
+
general={<div>Legacy General content</div>}
|
|
615
616
|
generalGroups={<div>App groups</div>}
|
|
616
617
|
notifications={<div>Email settings</div>}
|
|
617
618
|
notificationsLabel="Notifications"
|
|
@@ -644,6 +645,7 @@ describe("SettingsTabsPage", () => {
|
|
|
644
645
|
]);
|
|
645
646
|
// `generalGroups` may call useSettingsShell(), which throws here.
|
|
646
647
|
expect(container.textContent).not.toContain("App groups");
|
|
648
|
+
expect(container.textContent).toContain("Legacy General content");
|
|
647
649
|
});
|
|
648
650
|
|
|
649
651
|
it("visually separates app, agent, and workspace tabs", () => {
|
|
@@ -35,10 +35,14 @@ const appState = vi.hoisted(() => ({
|
|
|
35
35
|
write: vi.fn(async (_key: string, value: unknown) => value),
|
|
36
36
|
remove: vi.fn(async () => undefined),
|
|
37
37
|
}));
|
|
38
|
-
vi.mock(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
vi.mock(
|
|
39
|
+
"@agent-native/core/client/application-state",
|
|
40
|
+
async (importOriginal) => ({
|
|
41
|
+
...(await importOriginal<Record<string, unknown>>()),
|
|
42
|
+
writeClientAppState: appState.write,
|
|
43
|
+
deleteClientAppState: appState.remove,
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
42
46
|
|
|
43
47
|
import { AgentNativeI18nProvider } from "@agent-native/core/client/i18n";
|
|
44
48
|
|
|
@@ -728,10 +732,12 @@ describe("SettingsShell", () => {
|
|
|
728
732
|
// the router is still committing the previous navigation.
|
|
729
733
|
const [, setTick] = useState(0);
|
|
730
734
|
React.useEffect(() => {
|
|
731
|
-
//
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
+
// Re-render during the 60ms loader without keeping act() busy forever.
|
|
736
|
+
let ticks = 0;
|
|
737
|
+
const timer = setInterval(() => {
|
|
738
|
+
setTick((tick) => tick + 1);
|
|
739
|
+
if (++ticks === 5) clearInterval(timer);
|
|
740
|
+
}, 10);
|
|
735
741
|
return () => clearInterval(timer);
|
|
736
742
|
}, []);
|
|
737
743
|
const current = useLocation();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// @vitest-environment happy-dom
|
|
2
2
|
|
|
3
|
+
import type { LabStates } from "@agent-native/core/client/labs/use-lab";
|
|
3
4
|
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
4
5
|
import React, { act, Suspense } from "react";
|
|
5
6
|
import { createRoot, type Root } from "react-dom/client";
|
|
@@ -32,17 +33,17 @@ vi.mock("@agent-native/core/client/app-model-default", () => ({
|
|
|
32
33
|
}));
|
|
33
34
|
|
|
34
35
|
const labsActions = vi.hoisted(() => ({
|
|
35
|
-
query: { data: undefined as
|
|
36
|
+
query: { data: undefined as LabStates | undefined },
|
|
36
37
|
isError: false,
|
|
37
38
|
mutate: vi.fn(),
|
|
38
39
|
}));
|
|
39
40
|
vi.mock("@agent-native/core/client/hooks", () => ({
|
|
40
|
-
useChangeVersions: () => 0,
|
|
41
41
|
useDemoModeStatus: () => ({
|
|
42
42
|
enabled: false,
|
|
43
43
|
forced: false,
|
|
44
44
|
isLoading: false,
|
|
45
45
|
}),
|
|
46
|
+
useChangeVersions: () => 0,
|
|
46
47
|
}));
|
|
47
48
|
vi.mock("@agent-native/core/client/use-action", () => ({
|
|
48
49
|
useActionQuery: () => ({
|
|
@@ -422,9 +423,13 @@ describe("app group pages", () => {
|
|
|
422
423
|
|
|
423
424
|
it("hides labs the server didn't register once it answers", async () => {
|
|
424
425
|
labsActions.query.data = {
|
|
425
|
-
"chatgpt-subscription":
|
|
426
|
-
|
|
427
|
-
|
|
426
|
+
"chatgpt-subscription": {
|
|
427
|
+
enabled: false,
|
|
428
|
+
source: "default",
|
|
429
|
+
mixed: false,
|
|
430
|
+
},
|
|
431
|
+
"clips.wisprflow": { enabled: false, source: "choice", mixed: false },
|
|
432
|
+
"clips.meetings": { enabled: true, source: "choice", mixed: false },
|
|
428
433
|
};
|
|
429
434
|
await renderPage(LabsSettingsPage, { input: { labs } });
|
|
430
435
|
expect(switchFor("Retired lab")).toBeNull();
|
|
@@ -436,6 +441,31 @@ describe("app group pages", () => {
|
|
|
436
441
|
).toBe("true");
|
|
437
442
|
expect(switchFor("Voice dictation")?.disabled).toBe(false);
|
|
438
443
|
});
|
|
444
|
+
|
|
445
|
+
it("offers explicit choices for inherited mixed settings", async () => {
|
|
446
|
+
labsActions.query.data = {
|
|
447
|
+
"clips.meetings": {
|
|
448
|
+
enabled: false,
|
|
449
|
+
source: "legacy",
|
|
450
|
+
mixed: true,
|
|
451
|
+
legacyValues: { capture: false, recovery: true },
|
|
452
|
+
},
|
|
453
|
+
};
|
|
454
|
+
await renderPage(LabsSettingsPage, { input: { labs } });
|
|
455
|
+
expect(switchFor("Meetings and transcription")).toBeNull();
|
|
456
|
+
const on = container.querySelector<HTMLButtonElement>(
|
|
457
|
+
'[aria-label="Meetings and transcription: On"]',
|
|
458
|
+
);
|
|
459
|
+
expect(on?.disabled).toBe(false);
|
|
460
|
+
act(() => on?.click());
|
|
461
|
+
expect(labsActions.mutate).toHaveBeenCalledWith(
|
|
462
|
+
{ key: "clips.meetings", enabled: true },
|
|
463
|
+
expect.any(Object),
|
|
464
|
+
);
|
|
465
|
+
expect(
|
|
466
|
+
switchFor("Meetings and transcription")?.getAttribute("aria-checked"),
|
|
467
|
+
).toBe("true");
|
|
468
|
+
});
|
|
439
469
|
});
|
|
440
470
|
|
|
441
471
|
describe("Automations", () => {
|
|
@@ -10,6 +10,9 @@ const orgState = vi.hoisted(() => ({
|
|
|
10
10
|
value: { orgId: "org-1", orgName: "Acme", role: "member" as string },
|
|
11
11
|
}));
|
|
12
12
|
|
|
13
|
+
vi.mock("@agent-native/core/client/org/hooks", () => ({
|
|
14
|
+
useOrg: () => ({ data: orgState.value, isLoading: false }),
|
|
15
|
+
}));
|
|
13
16
|
vi.mock("@agent-native/core/client/org/hooks", () => ({
|
|
14
17
|
useOrg: () => ({ data: orgState.value, isLoading: false }),
|
|
15
18
|
}));
|
|
@@ -54,9 +54,9 @@ vi.mock("@agent-native/core/client/hooks", () => ({
|
|
|
54
54
|
}),
|
|
55
55
|
}));
|
|
56
56
|
|
|
57
|
-
vi.mock("@agent-native/core/client/use-action", () =>
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
vi.mock("@agent-native/core/client/use-action", async () =>
|
|
58
|
+
vi.importMock("@agent-native/core/client/hooks"),
|
|
59
|
+
);
|
|
60
60
|
|
|
61
61
|
vi.mock("@agent-native/core/client/i18n", () => ({
|
|
62
62
|
useT:
|
|
@@ -12,6 +12,7 @@ vi.mock("@agent-native/core/client/route-chunk-recovery", () => ({
|
|
|
12
12
|
|
|
13
13
|
import { AgentNativeI18nProvider } from "@agent-native/core/client/i18n";
|
|
14
14
|
|
|
15
|
+
import { createToolkitI18nCatalog } from "../i18n.js";
|
|
15
16
|
import { LazyChunkErrorBoundary } from "./LazyChunkErrorBoundary.js";
|
|
16
17
|
import { LazyChunkRetryFallback } from "./LazyChunkRetryFallback.js";
|
|
17
18
|
|
|
@@ -42,6 +43,7 @@ describe("LazyChunkErrorBoundary", () => {
|
|
|
42
43
|
await act(async () => {
|
|
43
44
|
root.render(
|
|
44
45
|
<AgentNativeI18nProvider
|
|
46
|
+
catalog={createToolkitI18nCatalog({ messages: {} })}
|
|
45
47
|
initialLocale="en-US"
|
|
46
48
|
initialPreference="en-US"
|
|
47
49
|
persistPreference={false}
|
|
@@ -32,7 +32,10 @@ describe("AppSidebar overflow affordances", () => {
|
|
|
32
32
|
root = createRoot(container);
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
afterEach(() => {
|
|
35
|
+
afterEach(async () => {
|
|
36
|
+
await act(async () => {
|
|
37
|
+
await new Promise((resolve) => setTimeout(resolve, 20));
|
|
38
|
+
});
|
|
36
39
|
act(() => root.unmount());
|
|
37
40
|
container.remove();
|
|
38
41
|
vi.unstubAllGlobals();
|
|
@@ -378,6 +378,44 @@ describe("visual style controls", () => {
|
|
|
378
378
|
expect(input.getAttribute("data-design-history-hotkeys")).toBe("true");
|
|
379
379
|
});
|
|
380
380
|
|
|
381
|
+
it("commits a zero pixel value with leading zeros", () => {
|
|
382
|
+
const onChange = vi.fn();
|
|
383
|
+
act(() => {
|
|
384
|
+
root.render(
|
|
385
|
+
<VisualScrubInput
|
|
386
|
+
label="Border width"
|
|
387
|
+
value={1}
|
|
388
|
+
min={0}
|
|
389
|
+
unit="px"
|
|
390
|
+
onChange={onChange}
|
|
391
|
+
/>,
|
|
392
|
+
);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
const input = container.querySelector<HTMLInputElement>("input")!;
|
|
396
|
+
act(() => input.focus());
|
|
397
|
+
act(() => {
|
|
398
|
+
const setter = Object.getOwnPropertyDescriptor(
|
|
399
|
+
HTMLInputElement.prototype,
|
|
400
|
+
"value",
|
|
401
|
+
)!.set!;
|
|
402
|
+
setter.call(input, "00px");
|
|
403
|
+
input.dispatchEvent(new Event("input", { bubbles: true }));
|
|
404
|
+
});
|
|
405
|
+
act(() => {
|
|
406
|
+
input.dispatchEvent(
|
|
407
|
+
new KeyboardEvent("keydown", { key: "Enter", bubbles: true }),
|
|
408
|
+
);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
expect(onChange).toHaveBeenCalledWith(
|
|
412
|
+
0,
|
|
413
|
+
expect.objectContaining({ source: "commit", expression: "00px" }),
|
|
414
|
+
);
|
|
415
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
416
|
+
expect(input.value).toBe("0px");
|
|
417
|
+
});
|
|
418
|
+
|
|
381
419
|
it("releases focus on Enter when the consumer opts in", () => {
|
|
382
420
|
const onChange = vi.fn();
|
|
383
421
|
act(() => {
|
|
@@ -1786,6 +1786,7 @@ describe("useCollabReconcile — concurrent edit / lost-update guards", () => {
|
|
|
1786
1786
|
});
|
|
1787
1787
|
|
|
1788
1788
|
it("still clears a stale collab value when there is no local emission", async () => {
|
|
1789
|
+
vi.useFakeTimers();
|
|
1789
1790
|
const { captured, Harness } = makeCollabSeedHarness("stale persisted body");
|
|
1790
1791
|
act(() =>
|
|
1791
1792
|
root.render(
|
|
@@ -1799,7 +1800,7 @@ describe("useCollabReconcile — concurrent edit / lost-update guards", () => {
|
|
|
1799
1800
|
),
|
|
1800
1801
|
);
|
|
1801
1802
|
expect(getEditorMarkdown(captured.editor!)).toBe("stale persisted body");
|
|
1802
|
-
await act(async () =>
|
|
1803
|
+
await act(async () => vi.advanceTimersByTimeAsync(30));
|
|
1803
1804
|
|
|
1804
1805
|
expect(getEditorMarkdown(captured.editor!)).toBe("canonical SQL body");
|
|
1805
1806
|
expect(captured.emitted).toEqual([]);
|