@agent-native/dispatch 0.22.1 → 0.23.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/dist/actions/provider-api-register.d.ts +1 -1
- package/dist/components/app-icon.d.ts +2 -1
- package/dist/components/app-icon.d.ts.map +1 -1
- package/dist/components/app-icon.js +51 -29
- package/dist/components/app-icon.js.map +1 -1
- package/dist/components/app-list-row.d.ts +1 -1
- package/dist/components/app-list-row.d.ts.map +1 -1
- package/dist/components/app-list-row.js +1 -1
- package/dist/components/app-list-row.js.map +1 -1
- package/dist/components/app-open-actions.d.ts +2 -1
- package/dist/components/app-open-actions.d.ts.map +1 -1
- package/dist/components/app-open-actions.js +2 -2
- package/dist/components/app-open-actions.js.map +1 -1
- package/dist/components/dispatch-control-plane.d.ts.map +1 -1
- package/dist/components/dispatch-control-plane.js +16 -17
- package/dist/components/dispatch-control-plane.js.map +1 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +137 -66
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/layout/workspace-apps-rail.d.ts.map +1 -1
- package/dist/components/layout/workspace-apps-rail.js +1 -1
- package/dist/components/layout/workspace-apps-rail.js.map +1 -1
- package/dist/components/other-apps-section.d.ts +3 -1
- package/dist/components/other-apps-section.d.ts.map +1 -1
- package/dist/components/other-apps-section.js +9 -6
- package/dist/components/other-apps-section.js.map +1 -1
- package/dist/components/workspace-app-card.d.ts.map +1 -1
- package/dist/components/workspace-app-card.js +7 -2
- package/dist/components/workspace-app-card.js.map +1 -1
- package/dist/components/workspace-template-card.d.ts.map +1 -1
- package/dist/components/workspace-template-card.js +2 -5
- package/dist/components/workspace-template-card.js.map +1 -1
- package/dist/lib/overview-chat.d.ts.map +1 -1
- package/dist/lib/overview-chat.js +0 -8
- package/dist/lib/overview-chat.js.map +1 -1
- package/dist/routes/pages/chat.js +1 -1
- package/dist/routes/pages/chat.js.map +1 -1
- package/dist/server/lib/mcp-gateway.d.ts.map +1 -1
- package/dist/server/lib/mcp-gateway.js +14 -1
- package/dist/server/lib/mcp-gateway.js.map +1 -1
- package/package.json +3 -3
- package/src/components/app-icon.tsx +71 -43
- package/src/components/app-list-row.tsx +1 -1
- package/src/components/app-open-actions.tsx +4 -2
- package/src/components/dispatch-control-plane.spec.tsx +45 -7
- package/src/components/dispatch-control-plane.tsx +56 -75
- package/src/components/layout/Layout.spec.tsx +53 -0
- package/src/components/layout/Layout.tsx +227 -99
- package/src/components/layout/workspace-apps-rail.tsx +1 -0
- package/src/components/other-apps-section.tsx +61 -40
- package/src/components/workspace-app-card.spec.tsx +63 -66
- package/src/components/workspace-app-card.tsx +10 -3
- package/src/components/workspace-template-card.spec.tsx +7 -8
- package/src/components/workspace-template-card.tsx +21 -11
- package/src/lib/overview-chat.spec.ts +0 -19
- package/src/lib/overview-chat.ts +0 -9
- package/src/routes/pages/chat.tsx +1 -1
- package/src/server/lib/mcp-gateway.spec.ts +64 -2
- package/src/server/lib/mcp-gateway.ts +19 -1
- package/src/styles/dispatch-css.spec.ts +11 -0
- package/src/styles/dispatch.css +2 -2
|
@@ -3,7 +3,7 @@ import type { ReactNode } from "react";
|
|
|
3
3
|
import { cn } from "../lib/utils";
|
|
4
4
|
|
|
5
5
|
export const APP_LIST_GRID_CLASS =
|
|
6
|
-
"xl:grid xl:grid-cols-2 xl:gap-3 xl:overflow-visible xl:rounded-none xl:bg-transparent";
|
|
6
|
+
"mx-auto w-full max-w-[1000px] xl:grid xl:grid-cols-2 xl:gap-3 xl:overflow-visible xl:rounded-none xl:bg-transparent";
|
|
7
7
|
export const APP_LIST_GRID_ROW_CLASS = "xl:rounded-2xl xl:border-0 xl:bg-card";
|
|
8
8
|
|
|
9
9
|
export function AppList({
|
|
@@ -29,6 +29,7 @@ export function AppOpenActions({
|
|
|
29
29
|
rel,
|
|
30
30
|
labels: labelOverrides,
|
|
31
31
|
onAddApp,
|
|
32
|
+
onOpenInline,
|
|
32
33
|
showInlineOption = false,
|
|
33
34
|
showNewTabOption = false,
|
|
34
35
|
}: {
|
|
@@ -38,6 +39,7 @@ export function AppOpenActions({
|
|
|
38
39
|
rel?: string;
|
|
39
40
|
labels?: Partial<AppOpenActionLabels>;
|
|
40
41
|
onAddApp?: () => void;
|
|
42
|
+
onOpenInline?: () => void;
|
|
41
43
|
showInlineOption?: boolean;
|
|
42
44
|
showNewTabOption?: boolean;
|
|
43
45
|
}) {
|
|
@@ -92,8 +94,8 @@ export function AppOpenActions({
|
|
|
92
94
|
</DropdownMenuItem>
|
|
93
95
|
) : null}
|
|
94
96
|
{showInlineOption && href ? (
|
|
95
|
-
<DropdownMenuItem
|
|
96
|
-
|
|
97
|
+
<DropdownMenuItem onSelect={onOpenInline}>
|
|
98
|
+
{labels.openInline}
|
|
97
99
|
</DropdownMenuItem>
|
|
98
100
|
) : null}
|
|
99
101
|
{showNewTabOption && href ? (
|
|
@@ -8,6 +8,7 @@ import { DispatchControlPlane } from "./dispatch-control-plane";
|
|
|
8
8
|
import { TooltipProvider } from "./ui/tooltip";
|
|
9
9
|
|
|
10
10
|
const clientState = vi.hoisted(() => ({
|
|
11
|
+
inBuilderFrame: false,
|
|
11
12
|
navigateWithTransition: vi.fn(),
|
|
12
13
|
promptComposerProps: null as Record<string, unknown> | null,
|
|
13
14
|
workspaceApps: [] as Array<Record<string, unknown>>,
|
|
@@ -69,7 +70,7 @@ vi.mock("@agent-native/core/client/hooks", () => ({
|
|
|
69
70
|
}));
|
|
70
71
|
|
|
71
72
|
vi.mock("@agent-native/core/client/host", () => ({
|
|
72
|
-
isInBuilderFrame: () =>
|
|
73
|
+
isInBuilderFrame: () => clientState.inBuilderFrame,
|
|
73
74
|
}));
|
|
74
75
|
|
|
75
76
|
vi.mock("@agent-native/core/client/i18n", () => ({
|
|
@@ -79,7 +80,12 @@ vi.mock("@agent-native/core/client/i18n", () => ({
|
|
|
79
80
|
}));
|
|
80
81
|
|
|
81
82
|
vi.mock("./create-app-popover", () => ({
|
|
82
|
-
CreateAppPopover: () =>
|
|
83
|
+
CreateAppPopover: ({ trigger }: { trigger?: React.ReactNode }) => (
|
|
84
|
+
<div>
|
|
85
|
+
{trigger}
|
|
86
|
+
<span>Create app</span>
|
|
87
|
+
</div>
|
|
88
|
+
),
|
|
83
89
|
}));
|
|
84
90
|
|
|
85
91
|
describe("DispatchControlPlane", () => {
|
|
@@ -88,6 +94,7 @@ describe("DispatchControlPlane", () => {
|
|
|
88
94
|
|
|
89
95
|
beforeEach(() => {
|
|
90
96
|
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
|
|
97
|
+
clientState.inBuilderFrame = false;
|
|
91
98
|
clientState.navigateWithTransition.mockReset();
|
|
92
99
|
clientState.promptComposerProps = null;
|
|
93
100
|
clientState.workspaceApps = [];
|
|
@@ -117,6 +124,8 @@ describe("DispatchControlPlane", () => {
|
|
|
117
124
|
});
|
|
118
125
|
|
|
119
126
|
expect(container.textContent).toContain("Chat across your apps");
|
|
127
|
+
expect(container.querySelector('[class*="max-w-[750px]"]')).not.toBeNull();
|
|
128
|
+
expect(container.querySelector('[class*="max-w-[1000px]"]')).not.toBeNull();
|
|
120
129
|
expect(container.textContent).not.toContain("Open chat");
|
|
121
130
|
expect(container.textContent).not.toContain("Also");
|
|
122
131
|
expect(container.textContent).not.toContain("active");
|
|
@@ -155,6 +164,36 @@ describe("DispatchControlPlane", () => {
|
|
|
155
164
|
);
|
|
156
165
|
});
|
|
157
166
|
|
|
167
|
+
it("keeps overview submissions on Dispatch Chat inside Builder frames", async () => {
|
|
168
|
+
clientState.inBuilderFrame = true;
|
|
169
|
+
|
|
170
|
+
await act(async () => {
|
|
171
|
+
root.render(
|
|
172
|
+
<MemoryRouter initialEntries={["/overview"]}>
|
|
173
|
+
<TooltipProvider>
|
|
174
|
+
<DispatchControlPlane />
|
|
175
|
+
</TooltipProvider>
|
|
176
|
+
</MemoryRouter>,
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
await act(async () => {
|
|
181
|
+
container.querySelector<HTMLButtonElement>("[data-placeholder]")?.click();
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
expect(clientState.navigateWithTransition).toHaveBeenCalledWith(
|
|
185
|
+
expect.any(Function),
|
|
186
|
+
"/chat",
|
|
187
|
+
expect.objectContaining({
|
|
188
|
+
state: {
|
|
189
|
+
dispatchPrompt: expect.objectContaining({
|
|
190
|
+
message: "Route onboarding work",
|
|
191
|
+
}),
|
|
192
|
+
},
|
|
193
|
+
}),
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
|
|
158
197
|
it("shows mounted and connected apps together without duplicates", async () => {
|
|
159
198
|
clientState.workspaceApps = [
|
|
160
199
|
{
|
|
@@ -230,11 +269,10 @@ describe("DispatchControlPlane", () => {
|
|
|
230
269
|
expect(container.textContent).toContain("Mail");
|
|
231
270
|
expect(container.textContent).toContain("Clips");
|
|
232
271
|
expect(container.textContent).toContain("Analytics");
|
|
233
|
-
expect(container.textContent).toContain("
|
|
234
|
-
expect(container.textContent).toContain("
|
|
235
|
-
expect(container.textContent
|
|
236
|
-
|
|
237
|
-
);
|
|
272
|
+
expect(container.textContent).toContain("Apps");
|
|
273
|
+
expect(container.textContent).toContain("New");
|
|
274
|
+
expect(container.textContent).not.toContain("Other apps");
|
|
275
|
+
expect(container.textContent).not.toContain("available");
|
|
238
276
|
expect(container.textContent).not.toContain("Archived app");
|
|
239
277
|
expect(container.textContent).not.toContain("Duplicate onboarding");
|
|
240
278
|
expect(container.textContent).not.toContain("CRM");
|
|
@@ -4,20 +4,13 @@ import {
|
|
|
4
4
|
} from "@agent-native/core/client/agent-chat";
|
|
5
5
|
import { PromptComposer } from "@agent-native/core/client/composer";
|
|
6
6
|
import { useActionQuery } from "@agent-native/core/client/hooks";
|
|
7
|
-
import { isInBuilderFrame } from "@agent-native/core/client/host";
|
|
8
7
|
import { useT } from "@agent-native/core/client/i18n";
|
|
9
|
-
import {
|
|
10
|
-
IconArrowUpRight,
|
|
11
|
-
IconChevronDown,
|
|
12
|
-
IconClockHour4,
|
|
13
|
-
IconApps,
|
|
14
|
-
} from "@tabler/icons-react";
|
|
8
|
+
import { IconChevronDown, IconClockHour4, IconPlus } from "@tabler/icons-react";
|
|
15
9
|
import type { ReactNode } from "react";
|
|
16
10
|
import { useState } from "react";
|
|
17
11
|
import { Link, useNavigate } from "react-router";
|
|
18
12
|
|
|
19
13
|
import type { ConnectedAppSummary } from "../lib/other-apps";
|
|
20
|
-
import { submitOverviewPrompt } from "../lib/overview-chat";
|
|
21
14
|
import { cn } from "../lib/utils";
|
|
22
15
|
import type { WorkspaceAppSummary } from "../lib/workspace-apps";
|
|
23
16
|
import { ActionQueryError } from "./action-query-error";
|
|
@@ -28,7 +21,7 @@ import {
|
|
|
28
21
|
} from "./app-list-row";
|
|
29
22
|
import { CreateAppPopover } from "./create-app-popover";
|
|
30
23
|
import { useSetPageTitle } from "./layout/HeaderActions";
|
|
31
|
-
import { OtherAppsSection } from "./other-apps-section";
|
|
24
|
+
import { mergeOtherAppEntries, OtherAppsSection } from "./other-apps-section";
|
|
32
25
|
import { Button } from "./ui/button";
|
|
33
26
|
import {
|
|
34
27
|
Collapsible,
|
|
@@ -56,26 +49,6 @@ function SectionHeader({
|
|
|
56
49
|
);
|
|
57
50
|
}
|
|
58
51
|
|
|
59
|
-
function AppGroupHeader({
|
|
60
|
-
title,
|
|
61
|
-
description,
|
|
62
|
-
}: {
|
|
63
|
-
title: string;
|
|
64
|
-
description: string;
|
|
65
|
-
}) {
|
|
66
|
-
return (
|
|
67
|
-
<div className="flex min-w-0 items-start gap-2">
|
|
68
|
-
<IconApps size={16} className="mt-0.5 shrink-0 text-muted-foreground" />
|
|
69
|
-
<div className="min-w-0">
|
|
70
|
-
<h3 className="truncate text-sm font-semibold text-foreground">
|
|
71
|
-
{title}
|
|
72
|
-
</h3>
|
|
73
|
-
<p className="mt-0.5 text-xs text-muted-foreground">{description}</p>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
52
|
function CommandPanel() {
|
|
80
53
|
const t = useT();
|
|
81
54
|
const {
|
|
@@ -104,11 +77,6 @@ function CommandPanel() {
|
|
|
104
77
|
const trimmed = message.trim();
|
|
105
78
|
if (!trimmed) return;
|
|
106
79
|
|
|
107
|
-
if (isInBuilderFrame()) {
|
|
108
|
-
submitOverviewPrompt(trimmed, selectedModel);
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
80
|
navigateWithAgentChatViewTransition(navigate, "/chat", {
|
|
113
81
|
state: {
|
|
114
82
|
dispatchPrompt: {
|
|
@@ -124,7 +92,7 @@ function CommandPanel() {
|
|
|
124
92
|
|
|
125
93
|
return (
|
|
126
94
|
<section className="flex flex-col">
|
|
127
|
-
<div className="mx-auto flex w-full max-w-
|
|
95
|
+
<div className="mx-auto flex w-full max-w-[750px] flex-col pt-4 sm:pt-6">
|
|
128
96
|
<div className="mb-5 text-center">
|
|
129
97
|
<h2 className="text-2xl font-semibold tracking-tight text-foreground sm:text-3xl">
|
|
130
98
|
{t("dispatch.pages.chatAcrossApps", {
|
|
@@ -196,60 +164,73 @@ function AppsPanel({
|
|
|
196
164
|
const visibleApps = apps.filter((app) => !app.isDispatch && !app.archived);
|
|
197
165
|
const activeApps = visibleApps.filter((app) => app.status !== "pending");
|
|
198
166
|
const pendingApps = visibleApps.filter((app) => app.status === "pending");
|
|
167
|
+
const otherAppEntries = mergeOtherAppEntries({
|
|
168
|
+
templates: curatedTemplates,
|
|
169
|
+
connectedApps,
|
|
170
|
+
workspaceApps: apps,
|
|
171
|
+
});
|
|
199
172
|
const showSkeletons =
|
|
200
173
|
isLoading && activeApps.length === 0 && pendingApps.length === 0;
|
|
201
174
|
|
|
202
175
|
return (
|
|
203
|
-
<section className="flex flex-col gap-3">
|
|
176
|
+
<section className="mx-auto flex w-full max-w-[1000px] flex-col gap-3">
|
|
204
177
|
<SectionHeader
|
|
205
178
|
title="Apps"
|
|
206
179
|
action={
|
|
207
|
-
<
|
|
208
|
-
<
|
|
209
|
-
View all
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
180
|
+
<div className="flex items-center gap-1.5">
|
|
181
|
+
<Button variant="outline" size="sm" asChild>
|
|
182
|
+
<Link to="/apps">View all</Link>
|
|
183
|
+
</Button>
|
|
184
|
+
<CreateAppPopover
|
|
185
|
+
align="end"
|
|
186
|
+
trigger={
|
|
187
|
+
<Button variant="ghost" size="sm" className="gap-1.5">
|
|
188
|
+
<IconPlus size={14} />
|
|
189
|
+
{t("dispatch.pages.newApp", { defaultValue: "New" })}
|
|
190
|
+
</Button>
|
|
191
|
+
}
|
|
192
|
+
/>
|
|
193
|
+
</div>
|
|
213
194
|
}
|
|
214
195
|
/>
|
|
215
196
|
{showSkeletons ? (
|
|
216
197
|
<OverviewAppsSkeleton />
|
|
217
198
|
) : (
|
|
218
199
|
<>
|
|
219
|
-
{
|
|
220
|
-
|
|
221
|
-
<
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
description={t("dispatch.pages.activeCount", {
|
|
226
|
-
count: activeApps.length,
|
|
227
|
-
})}
|
|
200
|
+
<AppList className={APP_LIST_GRID_CLASS}>
|
|
201
|
+
{activeApps.map((app) => (
|
|
202
|
+
<WorkspaceAppCard
|
|
203
|
+
key={app.id}
|
|
204
|
+
app={app}
|
|
205
|
+
className={APP_LIST_GRID_ROW_CLASS}
|
|
228
206
|
/>
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
207
|
+
))}
|
|
208
|
+
{activeApps.length === 0 &&
|
|
209
|
+
otherAppEntries.length === 0 &&
|
|
210
|
+
!curatedTemplatesLoading &&
|
|
211
|
+
!connectedAppsLoading &&
|
|
212
|
+
!curatedTemplatesError &&
|
|
213
|
+
!connectedAppsError ? (
|
|
214
|
+
<p className="px-4 py-3 text-sm text-muted-foreground">
|
|
215
|
+
{t("dispatch.pages.noApps", {
|
|
216
|
+
defaultValue: "No apps yet.",
|
|
217
|
+
})}
|
|
218
|
+
</p>
|
|
219
|
+
) : null}
|
|
220
|
+
<OtherAppsSection
|
|
221
|
+
templates={curatedTemplates}
|
|
222
|
+
connectedApps={connectedApps}
|
|
223
|
+
workspaceApps={apps}
|
|
224
|
+
templatesLoading={curatedTemplatesLoading}
|
|
225
|
+
connectedAppsLoading={connectedAppsLoading}
|
|
226
|
+
templatesError={curatedTemplatesError}
|
|
227
|
+
connectedAppsError={connectedAppsError}
|
|
228
|
+
onRetryTemplates={onRetryCuratedTemplates}
|
|
229
|
+
onRetryConnectedApps={onRetryConnectedApps}
|
|
230
|
+
heading={null}
|
|
231
|
+
embeddedInList
|
|
232
|
+
/>
|
|
233
|
+
</AppList>
|
|
253
234
|
</>
|
|
254
235
|
)}
|
|
255
236
|
{pendingApps.length > 0 ? (
|
|
@@ -384,6 +384,59 @@ describe("Dispatch NavContent", () => {
|
|
|
384
384
|
expect(paths.at(-1)).toBe("/apps");
|
|
385
385
|
});
|
|
386
386
|
|
|
387
|
+
it("omits the Chats section when chat-first has no chats", async () => {
|
|
388
|
+
clientState.threads = [];
|
|
389
|
+
await act(async () => {
|
|
390
|
+
root.render(
|
|
391
|
+
<MemoryRouter initialEntries={["/chat"]}>
|
|
392
|
+
<TooltipProvider>
|
|
393
|
+
<NavContent
|
|
394
|
+
chatFirstMode
|
|
395
|
+
collapsible
|
|
396
|
+
chatFirstApps={[{ id: "mail", name: "Mail" }]}
|
|
397
|
+
/>
|
|
398
|
+
</TooltipProvider>
|
|
399
|
+
</MemoryRouter>,
|
|
400
|
+
);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
expect(container.textContent).not.toContain("Chats");
|
|
404
|
+
expect(container.querySelector("[data-chat-first-app]")).not.toBeNull();
|
|
405
|
+
expect(
|
|
406
|
+
container.querySelector("[data-chat-first-app] span[style]"),
|
|
407
|
+
).not.toBeNull();
|
|
408
|
+
expect(
|
|
409
|
+
container.querySelector("[data-sidebar-footer-feedback]"),
|
|
410
|
+
).not.toBeNull();
|
|
411
|
+
expect(
|
|
412
|
+
container.querySelector("[data-sidebar-footer-collapse]"),
|
|
413
|
+
).not.toBeNull();
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it("opens a workspace app in the main app route from the left rail", async () => {
|
|
417
|
+
const paths: string[] = [];
|
|
418
|
+
await act(async () => {
|
|
419
|
+
root.render(
|
|
420
|
+
<MemoryRouter initialEntries={["/chat"]}>
|
|
421
|
+
<TooltipProvider>
|
|
422
|
+
<NavContent
|
|
423
|
+
chatFirstMode
|
|
424
|
+
chatFirstApps={[{ id: "mail", name: "Mail" }]}
|
|
425
|
+
onChatFirstAppOpen={(app) => paths.push(`/apps/${app.id}`)}
|
|
426
|
+
/>
|
|
427
|
+
</TooltipProvider>
|
|
428
|
+
</MemoryRouter>,
|
|
429
|
+
);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
await act(async () => {
|
|
433
|
+
container
|
|
434
|
+
.querySelector<HTMLButtonElement>("[data-chat-first-app] button")
|
|
435
|
+
?.click();
|
|
436
|
+
});
|
|
437
|
+
expect(paths).toEqual(["/apps/mail"]);
|
|
438
|
+
});
|
|
439
|
+
|
|
387
440
|
it("uses the shared chat history rail and retains thread actions", async () => {
|
|
388
441
|
await act(async () => {
|
|
389
442
|
root.render(
|