@agent-native/dispatch 0.12.1 → 0.12.3
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/components/dispatch-shell.d.ts.map +1 -1
- package/dist/components/dispatch-shell.js +3 -1
- package/dist/components/dispatch-shell.js.map +1 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +39 -20
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/routes/pages/$appId.d.ts.map +1 -1
- package/dist/routes/pages/$appId.js +3 -2
- package/dist/routes/pages/$appId.js.map +1 -1
- package/dist/routes/pages/agents.d.ts.map +1 -1
- package/dist/routes/pages/agents.js +14 -8
- package/dist/routes/pages/agents.js.map +1 -1
- package/dist/routes/pages/apps.$appId.d.ts.map +1 -1
- package/dist/routes/pages/apps.$appId.js +3 -2
- package/dist/routes/pages/apps.$appId.js.map +1 -1
- package/dist/routes/pages/apps.d.ts.map +1 -1
- package/dist/routes/pages/apps.js +29 -11
- package/dist/routes/pages/apps.js.map +1 -1
- package/dist/routes/pages/destinations.d.ts.map +1 -1
- package/dist/routes/pages/destinations.js +18 -12
- package/dist/routes/pages/destinations.js.map +1 -1
- package/dist/routes/pages/metrics.d.ts.map +1 -1
- package/dist/routes/pages/metrics.js +8 -5
- package/dist/routes/pages/metrics.js.map +1 -1
- package/dist/routes/pages/new-app.d.ts.map +1 -1
- package/dist/routes/pages/new-app.js +3 -2
- package/dist/routes/pages/new-app.js.map +1 -1
- package/package.json +2 -2
- package/src/components/dispatch-shell.tsx +3 -1
- package/src/components/layout/Layout.tsx +63 -28
- package/src/routes/pages/$appId.tsx +15 -12
- package/src/routes/pages/agents.tsx +21 -14
- package/src/routes/pages/apps.$appId.tsx +15 -14
- package/src/routes/pages/apps.tsx +41 -20
- package/src/routes/pages/destinations.tsx +35 -24
- package/src/routes/pages/metrics.tsx +13 -10
- package/src/routes/pages/new-app.tsx +4 -3
|
@@ -19,6 +19,8 @@ import {
|
|
|
19
19
|
useAgentChatHomeHandoff,
|
|
20
20
|
useAgentChatHomeHandoffLinks,
|
|
21
21
|
useChatThreads,
|
|
22
|
+
useFormatters,
|
|
23
|
+
useT,
|
|
22
24
|
type ChatThreadSummary,
|
|
23
25
|
} from "@agent-native/core/client";
|
|
24
26
|
import { ExtensionsSidebarSection } from "@agent-native/core/client/extensions";
|
|
@@ -222,12 +224,6 @@ const OPERATIONS_NAV_ITEMS = [
|
|
|
222
224
|
|
|
223
225
|
const EMPTY_NAV_ITEMS: readonly DispatchNavItem[] = [];
|
|
224
226
|
|
|
225
|
-
const SIDEBAR_SUGGESTIONS = [
|
|
226
|
-
"Build a workspace app for X",
|
|
227
|
-
"Route Slack mentions to my analytics app",
|
|
228
|
-
"Grant my OpenAI key to this app",
|
|
229
|
-
];
|
|
230
|
-
|
|
231
227
|
const CHROMELESS_PATHS = ["/approval"];
|
|
232
228
|
|
|
233
229
|
// Routes whose page renders its own toolbar (with NotificationsBell + AgentToggleButton).
|
|
@@ -309,7 +305,10 @@ function threadIdFromPath(pathname: string): string | null {
|
|
|
309
305
|
}
|
|
310
306
|
}
|
|
311
307
|
|
|
312
|
-
function formatThreadAge(
|
|
308
|
+
function formatThreadAge(
|
|
309
|
+
updatedAt: number,
|
|
310
|
+
formatDate: ReturnType<typeof useFormatters>["formatDate"],
|
|
311
|
+
) {
|
|
313
312
|
const diffMs = Math.max(0, Date.now() - updatedAt);
|
|
314
313
|
const minutes = Math.floor(diffMs / 60_000);
|
|
315
314
|
if (minutes < 1) return "now";
|
|
@@ -318,14 +317,14 @@ function formatThreadAge(updatedAt: number) {
|
|
|
318
317
|
if (hours < 24) return `${hours}h`;
|
|
319
318
|
const days = Math.floor(hours / 24);
|
|
320
319
|
if (days < 7) return `${days}d`;
|
|
321
|
-
return
|
|
320
|
+
return formatDate(updatedAt, {
|
|
322
321
|
month: "short",
|
|
323
322
|
day: "numeric",
|
|
324
323
|
});
|
|
325
324
|
}
|
|
326
325
|
|
|
327
|
-
function threadTitle(thread: ChatThreadSummary) {
|
|
328
|
-
return thread.title || thread.preview ||
|
|
326
|
+
function threadTitle(thread: ChatThreadSummary, fallback: string) {
|
|
327
|
+
return thread.title || thread.preview || fallback;
|
|
329
328
|
}
|
|
330
329
|
|
|
331
330
|
function threadUpdatedAt(thread: ChatThreadSummary) {
|
|
@@ -337,6 +336,8 @@ function threadUpdatedAt(thread: ChatThreadSummary) {
|
|
|
337
336
|
}
|
|
338
337
|
|
|
339
338
|
function DispatchChatsSection({ onNavigate }: { onNavigate?: () => void }) {
|
|
339
|
+
const t = useT();
|
|
340
|
+
const { formatDate } = useFormatters();
|
|
340
341
|
const navigate = useNavigate();
|
|
341
342
|
const location = useLocation();
|
|
342
343
|
const {
|
|
@@ -415,7 +416,7 @@ function DispatchChatsSection({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
415
416
|
|
|
416
417
|
function startRenameThread(thread: ChatThreadSummary) {
|
|
417
418
|
committingRenameRef.current = false;
|
|
418
|
-
setRenameDraft(threadTitle(thread));
|
|
419
|
+
setRenameDraft(threadTitle(thread, t("dispatch.sidebar.newChat")));
|
|
419
420
|
setRenamingThreadId(thread.id);
|
|
420
421
|
}
|
|
421
422
|
|
|
@@ -446,7 +447,7 @@ function DispatchChatsSection({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
446
447
|
<div className="mt-2 border-s border-sidebar-border/70 ps-3">
|
|
447
448
|
<div className="mb-1 flex h-7 items-center gap-2 pe-1">
|
|
448
449
|
<div className="min-w-0 flex-1 text-xs font-medium text-sidebar-foreground/70">
|
|
449
|
-
|
|
450
|
+
{t("dispatch.sidebar.chats")}
|
|
450
451
|
</div>
|
|
451
452
|
<Tooltip>
|
|
452
453
|
<TooltipTrigger asChild>
|
|
@@ -454,12 +455,12 @@ function DispatchChatsSection({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
454
455
|
type="button"
|
|
455
456
|
onClick={handleNewChat}
|
|
456
457
|
className="flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md text-sidebar-foreground/65 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
|
|
457
|
-
aria-label="
|
|
458
|
+
aria-label={t("dispatch.sidebar.newDispatchChat")}
|
|
458
459
|
>
|
|
459
460
|
<IconPlus className="size-3.5" />
|
|
460
461
|
</button>
|
|
461
462
|
</TooltipTrigger>
|
|
462
|
-
<TooltipContent>
|
|
463
|
+
<TooltipContent>{t("dispatch.sidebar.newChat")}</TooltipContent>
|
|
463
464
|
</Tooltip>
|
|
464
465
|
</div>
|
|
465
466
|
<div className="grid gap-0.5">
|
|
@@ -498,7 +499,12 @@ function DispatchChatsSection({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
498
499
|
}
|
|
499
500
|
}}
|
|
500
501
|
maxLength={160}
|
|
501
|
-
aria-label={
|
|
502
|
+
aria-label={t("dispatch.sidebar.renameThread", {
|
|
503
|
+
title: threadTitle(
|
|
504
|
+
thread,
|
|
505
|
+
t("dispatch.sidebar.newChat"),
|
|
506
|
+
),
|
|
507
|
+
})}
|
|
502
508
|
className="h-6 min-w-0 rounded-sm border-sidebar-border bg-background px-1.5 text-xs"
|
|
503
509
|
/>
|
|
504
510
|
</form>
|
|
@@ -510,20 +516,28 @@ function DispatchChatsSection({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
510
516
|
className="flex h-full min-w-0 flex-1 cursor-pointer items-center px-2 text-start outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
511
517
|
>
|
|
512
518
|
<span className="min-w-0 flex-1 truncate">
|
|
513
|
-
{threadTitle(thread)}
|
|
519
|
+
{threadTitle(thread, t("dispatch.sidebar.newChat"))}
|
|
514
520
|
</span>
|
|
515
521
|
</button>
|
|
516
522
|
<div className="relative flex size-7 shrink-0 items-center justify-end pe-1">
|
|
517
523
|
<span className="text-[11px] text-sidebar-foreground/50 transition-opacity group-hover:opacity-0 group-focus-within:opacity-0">
|
|
518
524
|
{isActive
|
|
519
525
|
? ""
|
|
520
|
-
: formatThreadAge(
|
|
526
|
+
: formatThreadAge(
|
|
527
|
+
threadUpdatedAt(thread),
|
|
528
|
+
formatDate,
|
|
529
|
+
)}
|
|
521
530
|
</span>
|
|
522
531
|
<DropdownMenu>
|
|
523
532
|
<DropdownMenuTrigger asChild>
|
|
524
533
|
<button
|
|
525
534
|
type="button"
|
|
526
|
-
aria-label={
|
|
535
|
+
aria-label={t("dispatch.sidebar.chatOptions", {
|
|
536
|
+
title: threadTitle(
|
|
537
|
+
thread,
|
|
538
|
+
t("dispatch.sidebar.newChat"),
|
|
539
|
+
),
|
|
540
|
+
})}
|
|
527
541
|
className="absolute end-1 flex size-6 cursor-pointer items-center justify-center rounded-md text-sidebar-foreground/65 opacity-0 transition-opacity hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring group-hover:opacity-100 group-focus-within:opacity-100 data-[state=open]:opacity-100"
|
|
528
542
|
>
|
|
529
543
|
<IconDots className="size-4" />
|
|
@@ -538,7 +552,7 @@ function DispatchChatsSection({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
538
552
|
onSelect={() => startRenameThread(thread)}
|
|
539
553
|
>
|
|
540
554
|
<IconEdit className="size-4" />
|
|
541
|
-
|
|
555
|
+
{t("dispatch.sidebar.renameChat")}
|
|
542
556
|
</DropdownMenuItem>
|
|
543
557
|
</DropdownMenuContent>
|
|
544
558
|
</DropdownMenu>
|
|
@@ -554,7 +568,7 @@ function DispatchChatsSection({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
554
568
|
onClick={handleNewChat}
|
|
555
569
|
className="flex h-8 cursor-pointer items-center rounded-md px-2 text-start text-sm text-sidebar-foreground/70 transition-colors hover:bg-sidebar-accent/65 hover:text-sidebar-accent-foreground"
|
|
556
570
|
>
|
|
557
|
-
<span className="truncate">
|
|
571
|
+
<span className="truncate">{t("dispatch.sidebar.newChat")}</span>
|
|
558
572
|
</button>
|
|
559
573
|
)}
|
|
560
574
|
</div>
|
|
@@ -569,6 +583,7 @@ export function NavContent({
|
|
|
569
583
|
onNavigate?: () => void;
|
|
570
584
|
extensions?: DispatchExtensionConfig;
|
|
571
585
|
}) {
|
|
586
|
+
const t = useT();
|
|
572
587
|
const location = useLocation();
|
|
573
588
|
const navigate = useNavigate();
|
|
574
589
|
const { data: workspace } = useActionQuery(
|
|
@@ -591,10 +606,20 @@ export function NavContent({
|
|
|
591
606
|
const operationsOpen = operationsNavItems.some((item) =>
|
|
592
607
|
navItemMatchesPath(item, localPathname),
|
|
593
608
|
);
|
|
609
|
+
const navLabel = (item: DispatchNavItem) => {
|
|
610
|
+
const key =
|
|
611
|
+
item.id === "thread-debug"
|
|
612
|
+
? "threadDebug"
|
|
613
|
+
: item.id === "workspace"
|
|
614
|
+
? "resources"
|
|
615
|
+
: item.id;
|
|
616
|
+
return t(`dispatch.nav.${key}`, { defaultValue: item.label });
|
|
617
|
+
};
|
|
594
618
|
|
|
595
619
|
const renderNavItem = (item: DispatchNavItem) => {
|
|
596
620
|
const Icon = item.icon;
|
|
597
621
|
const itemMatchesLocalPath = navItemMatchesPath(item, localPathname);
|
|
622
|
+
const label = navLabel(item);
|
|
598
623
|
return (
|
|
599
624
|
<li key={item.id}>
|
|
600
625
|
<NavLink
|
|
@@ -633,7 +658,7 @@ export function NavContent({
|
|
|
633
658
|
) : (
|
|
634
659
|
<span className="h-4 w-4 shrink-0" aria-hidden="true" />
|
|
635
660
|
)}
|
|
636
|
-
<span className="truncate">{
|
|
661
|
+
<span className="truncate">{label}</span>
|
|
637
662
|
</NavLink>
|
|
638
663
|
{item.id === "chat" && itemMatchesLocalPath ? (
|
|
639
664
|
<DispatchChatsSection onNavigate={onNavigate} />
|
|
@@ -666,8 +691,10 @@ export function NavContent({
|
|
|
666
691
|
</div>
|
|
667
692
|
<div className="truncate text-xs text-muted-foreground">
|
|
668
693
|
{workspaceLabel
|
|
669
|
-
?
|
|
670
|
-
|
|
694
|
+
? t("dispatch.sidebar.workspaceSubtitle", {
|
|
695
|
+
count: ws?.appCount ?? 0,
|
|
696
|
+
})
|
|
697
|
+
: t("dispatch.sidebar.workspaceControlPlane")}
|
|
671
698
|
</div>
|
|
672
699
|
</div>
|
|
673
700
|
</div>
|
|
@@ -682,7 +709,7 @@ export function NavContent({
|
|
|
682
709
|
<div className="border-t px-2 py-2">
|
|
683
710
|
<details className="group" open={operationsOpen}>
|
|
684
711
|
<summary className="flex h-8 cursor-pointer list-none items-center justify-between rounded-md px-2 text-xs font-medium uppercase text-sidebar-foreground/50 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground [&::-webkit-details-marker]:hidden">
|
|
685
|
-
<span>
|
|
712
|
+
<span>{t("dispatch.nav.operations")}</span>
|
|
686
713
|
<IconChevronDown
|
|
687
714
|
size={14}
|
|
688
715
|
className="transition-transform group-open:rotate-180"
|
|
@@ -718,6 +745,7 @@ export function Layout({
|
|
|
718
745
|
children: ReactNode;
|
|
719
746
|
extensions?: DispatchExtensionConfig;
|
|
720
747
|
}) {
|
|
748
|
+
const t = useT();
|
|
721
749
|
const location = useLocation();
|
|
722
750
|
const navigate = useNavigate();
|
|
723
751
|
const [mobileOpen, setMobileOpen] = useState(false);
|
|
@@ -748,6 +776,11 @@ export function Layout({
|
|
|
748
776
|
dispatchNavLinkTarget("/chat"),
|
|
749
777
|
);
|
|
750
778
|
}
|
|
779
|
+
const sidebarSuggestions = [
|
|
780
|
+
t("dispatch.sidebar.suggestionBuildApp"),
|
|
781
|
+
t("dispatch.sidebar.suggestionRouteSlack"),
|
|
782
|
+
t("dispatch.sidebar.suggestionGrantKey"),
|
|
783
|
+
];
|
|
751
784
|
const appContent = (
|
|
752
785
|
<div className="flex h-full min-w-0 flex-1 flex-col overflow-hidden">
|
|
753
786
|
{showHeader ? <Header onOpenMobile={() => setMobileOpen(true)} /> : null}
|
|
@@ -778,8 +811,8 @@ export function Layout({
|
|
|
778
811
|
storageKey="dispatch"
|
|
779
812
|
openOnChatRunning={chatHomeHandoffActive}
|
|
780
813
|
onFullscreenRequest={openAskAgentFullscreen}
|
|
781
|
-
emptyStateText="
|
|
782
|
-
suggestions={
|
|
814
|
+
emptyStateText={t("dispatch.sidebar.emptyAgentText")}
|
|
815
|
+
suggestions={sidebarSuggestions}
|
|
783
816
|
>
|
|
784
817
|
{appContent}
|
|
785
818
|
</AgentSidebar>
|
|
@@ -797,9 +830,11 @@ export function Layout({
|
|
|
797
830
|
side="left"
|
|
798
831
|
className="w-72 p-0 bg-sidebar text-sidebar-foreground [&>button]:hidden"
|
|
799
832
|
>
|
|
800
|
-
<SheetTitle className="sr-only">
|
|
833
|
+
<SheetTitle className="sr-only">
|
|
834
|
+
{t("dispatch.nav.navigation")}
|
|
835
|
+
</SheetTitle>
|
|
801
836
|
<SheetDescription className="sr-only">
|
|
802
|
-
|
|
837
|
+
{t("dispatch.nav.navigationDescription")}
|
|
803
838
|
</SheetDescription>
|
|
804
839
|
<div className="flex h-full w-full flex-col">
|
|
805
840
|
<NavContent
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type ClientLoaderFunctionArgs,
|
|
8
8
|
type LoaderFunctionArgs,
|
|
9
9
|
} from "react-router";
|
|
10
|
-
import { useActionQuery, appPath } from "@agent-native/core/client";
|
|
10
|
+
import { useActionQuery, appPath, useT } from "@agent-native/core/client";
|
|
11
11
|
import {
|
|
12
12
|
IconArrowLeft,
|
|
13
13
|
IconArrowUpRight,
|
|
@@ -93,6 +93,7 @@ export async function clientLoader({
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
export default function WorkspaceAppCatchAllRoute() {
|
|
96
|
+
const t = useT();
|
|
96
97
|
const { appId } = useParams();
|
|
97
98
|
const { data: apps = [], isLoading } = useActionQuery(
|
|
98
99
|
"list-workspace-apps",
|
|
@@ -127,14 +128,14 @@ export default function WorkspaceAppCatchAllRoute() {
|
|
|
127
128
|
|
|
128
129
|
return (
|
|
129
130
|
<DispatchShell
|
|
130
|
-
title={app?.name || "
|
|
131
|
-
description="
|
|
131
|
+
title={app?.name || t("dispatch.pages.pageNotFound")}
|
|
132
|
+
description={t("dispatch.pages.pageNotFoundDescription")}
|
|
132
133
|
>
|
|
133
134
|
<div className="max-w-2xl rounded-lg border bg-card p-5">
|
|
134
135
|
<Button asChild size="sm" variant="ghost" className="-ml-2 mb-4">
|
|
135
136
|
<Link to={appPath("/overview")}>
|
|
136
137
|
<IconArrowLeft size={15} className="mr-1.5" />
|
|
137
|
-
|
|
138
|
+
{t("dispatch.nav.overview")}
|
|
138
139
|
</Link>
|
|
139
140
|
</Button>
|
|
140
141
|
|
|
@@ -149,23 +150,23 @@ export default function WorkspaceAppCatchAllRoute() {
|
|
|
149
150
|
className="gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300"
|
|
150
151
|
>
|
|
151
152
|
<IconClockHour4 size={12} />
|
|
152
|
-
|
|
153
|
+
{t("dispatch.pages.building")}
|
|
153
154
|
</Badge>
|
|
154
155
|
</div>
|
|
155
156
|
<p className="text-sm text-muted-foreground">
|
|
156
|
-
|
|
157
|
+
{t("dispatch.pages.appBuildingPrefix")}{" "}
|
|
157
158
|
<span className="font-mono text-foreground">{app.path}</span>{" "}
|
|
158
|
-
|
|
159
|
+
{t("dispatch.pages.appBuildingSuffix")}
|
|
159
160
|
</p>
|
|
160
161
|
{app.branchName ? (
|
|
161
162
|
<p className="text-xs text-muted-foreground">
|
|
162
|
-
|
|
163
|
+
{t("dispatch.pages.branch", { branch: app.branchName })}
|
|
163
164
|
</p>
|
|
164
165
|
) : null}
|
|
165
166
|
{app.builderUrl ? (
|
|
166
167
|
<Button asChild>
|
|
167
168
|
<a href={app.builderUrl} target="_blank" rel="noreferrer">
|
|
168
|
-
|
|
169
|
+
{t("dispatch.pages.openBuilderBranch")}
|
|
169
170
|
<IconArrowUpRight size={15} className="ml-1.5" />
|
|
170
171
|
</a>
|
|
171
172
|
</Button>
|
|
@@ -174,14 +175,16 @@ export default function WorkspaceAppCatchAllRoute() {
|
|
|
174
175
|
) : (
|
|
175
176
|
<div className="space-y-3">
|
|
176
177
|
<h2 className="text-base font-semibold text-foreground">
|
|
177
|
-
|
|
178
|
+
{t("dispatch.pages.pageNotFound")}
|
|
178
179
|
</h2>
|
|
179
180
|
<p className="text-sm text-muted-foreground">
|
|
180
181
|
<span className="font-mono text-foreground">/{appId}</span> isn't
|
|
181
|
-
|
|
182
|
+
{t("dispatch.pages.notDispatchOrWorkspaceApp")}
|
|
182
183
|
</p>
|
|
183
184
|
<Button asChild>
|
|
184
|
-
<Link to={appPath("/apps")}>
|
|
185
|
+
<Link to={appPath("/apps")}>
|
|
186
|
+
{t("dispatch.pages.browseApps")}
|
|
187
|
+
</Link>
|
|
185
188
|
</Button>
|
|
186
189
|
</div>
|
|
187
190
|
)}
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
agentNativePath,
|
|
4
4
|
useActionMutation,
|
|
5
5
|
useActionQuery,
|
|
6
|
+
useT,
|
|
6
7
|
} from "@agent-native/core/client";
|
|
7
8
|
import { AgentsPanel, type ConnectedAgent } from "@/components/agents-panel";
|
|
8
9
|
import { DispatchShell } from "@/components/dispatch-shell";
|
|
@@ -39,12 +40,13 @@ function dispatchMcpUrl(): string {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
function DispatchMcpAccessPanel() {
|
|
43
|
+
const t = useT();
|
|
42
44
|
const { data, isLoading } = useActionQuery("list-mcp-app-access", {});
|
|
43
45
|
const [optimistic, setOptimistic] = useState<McpAccessState | null>(null);
|
|
44
46
|
const saveAccess = useActionMutation("set-mcp-app-access", {
|
|
45
47
|
onSuccess: () => {
|
|
46
48
|
setOptimistic(null);
|
|
47
|
-
toast.success("
|
|
49
|
+
toast.success(t("dispatch.pages.mcpAccessUpdated"));
|
|
48
50
|
},
|
|
49
51
|
onError: (error) => {
|
|
50
52
|
setOptimistic(null);
|
|
@@ -73,7 +75,7 @@ function DispatchMcpAccessPanel() {
|
|
|
73
75
|
|
|
74
76
|
function persist(next: McpAccessState) {
|
|
75
77
|
if (next.mode === "selected-apps" && next.selectedAppIds.length === 0) {
|
|
76
|
-
toast.error("
|
|
78
|
+
toast.error(t("dispatch.pages.selectAppForMcp"));
|
|
77
79
|
return;
|
|
78
80
|
}
|
|
79
81
|
setOptimistic(next);
|
|
@@ -90,9 +92,9 @@ function DispatchMcpAccessPanel() {
|
|
|
90
92
|
async function copyUrl() {
|
|
91
93
|
try {
|
|
92
94
|
await navigator.clipboard.writeText(mcpUrl);
|
|
93
|
-
toast.success("
|
|
95
|
+
toast.success(t("dispatch.pages.mcpUrlCopied"));
|
|
94
96
|
} catch {
|
|
95
|
-
toast.error("
|
|
97
|
+
toast.error(t("dispatch.pages.mcpUrlCopyFailed"));
|
|
96
98
|
}
|
|
97
99
|
}
|
|
98
100
|
|
|
@@ -102,21 +104,25 @@ function DispatchMcpAccessPanel() {
|
|
|
102
104
|
<div className="min-w-0">
|
|
103
105
|
<div className="flex items-center gap-2 text-sm font-medium text-foreground">
|
|
104
106
|
<IconPlugConnected size={16} />
|
|
105
|
-
|
|
107
|
+
{t("dispatch.pages.unifiedMcpGateway")}
|
|
106
108
|
</div>
|
|
107
109
|
<div className="mt-1 max-w-2xl text-sm text-muted-foreground">
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
{t("dispatch.pages.unifiedMcpGatewayDescription")}{" "}
|
|
111
|
+
<code>list_apps</code>, <code>ask_app</code>, and{" "}
|
|
112
|
+
<code>open_app</code>.
|
|
111
113
|
</div>
|
|
112
114
|
</div>
|
|
113
115
|
<div className="flex items-center gap-3 rounded-xl border px-3 py-2">
|
|
114
116
|
<div>
|
|
115
117
|
<div className="text-xs font-medium text-foreground">
|
|
116
|
-
{access.mode === "all-apps"
|
|
118
|
+
{access.mode === "all-apps"
|
|
119
|
+
? t("dispatch.pages.allApps")
|
|
120
|
+
: t("dispatch.pages.selectedApps")}
|
|
117
121
|
</div>
|
|
118
122
|
<div className="text-xs text-muted-foreground">
|
|
119
|
-
{isLoading
|
|
123
|
+
{isLoading
|
|
124
|
+
? t("dispatch.pages.loading")
|
|
125
|
+
: t("dispatch.pages.grantedCount", { count: grantedCount })}
|
|
120
126
|
</div>
|
|
121
127
|
</div>
|
|
122
128
|
<Switch
|
|
@@ -130,7 +136,7 @@ function DispatchMcpAccessPanel() {
|
|
|
130
136
|
: apps.map((app) => app.id),
|
|
131
137
|
})
|
|
132
138
|
}
|
|
133
|
-
aria-label="
|
|
139
|
+
aria-label={t("dispatch.pages.exposeAllAppsMcp")}
|
|
134
140
|
/>
|
|
135
141
|
</div>
|
|
136
142
|
</div>
|
|
@@ -139,7 +145,7 @@ function DispatchMcpAccessPanel() {
|
|
|
139
145
|
<Input readOnly value={mcpUrl} className="font-mono text-xs" />
|
|
140
146
|
<Button type="button" variant="outline" onClick={copyUrl}>
|
|
141
147
|
<IconCopy size={15} />
|
|
142
|
-
|
|
148
|
+
{t("dispatch.pages.copyUrl")}
|
|
143
149
|
</Button>
|
|
144
150
|
</div>
|
|
145
151
|
|
|
@@ -186,12 +192,13 @@ function DispatchMcpAccessPanel() {
|
|
|
186
192
|
}
|
|
187
193
|
|
|
188
194
|
export default function AgentsRoute() {
|
|
195
|
+
const t = useT();
|
|
189
196
|
const { data, refetch } = useActionQuery("list-connected-agents", {});
|
|
190
197
|
|
|
191
198
|
return (
|
|
192
199
|
<DispatchShell
|
|
193
|
-
title="
|
|
194
|
-
description="
|
|
200
|
+
title={t("dispatch.nav.agents")}
|
|
201
|
+
description={t("dispatch.pages.agentsDescription")}
|
|
195
202
|
>
|
|
196
203
|
<div className="space-y-4">
|
|
197
204
|
<DispatchMcpAccessPanel />
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useMemo } from "react";
|
|
2
2
|
import { Link, useParams } from "react-router";
|
|
3
|
-
import { useActionQuery } from "@agent-native/core/client";
|
|
3
|
+
import { useActionQuery, useT } from "@agent-native/core/client";
|
|
4
4
|
import {
|
|
5
5
|
IconArrowLeft,
|
|
6
6
|
IconArrowUpRight,
|
|
@@ -20,6 +20,7 @@ export function meta() {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export default function WorkspaceAppRoute() {
|
|
23
|
+
const t = useT();
|
|
23
24
|
const { appId } = useParams();
|
|
24
25
|
const { data: apps = [], isLoading } = useActionQuery(
|
|
25
26
|
"list-workspace-apps",
|
|
@@ -42,14 +43,14 @@ export default function WorkspaceAppRoute() {
|
|
|
42
43
|
|
|
43
44
|
return (
|
|
44
45
|
<DispatchShell
|
|
45
|
-
title={app?.name || "
|
|
46
|
-
description="
|
|
46
|
+
title={app?.name || t("dispatch.pages.workspaceAppFallback")}
|
|
47
|
+
description={t("dispatch.pages.workspaceAppDescription")}
|
|
47
48
|
>
|
|
48
49
|
<div className="max-w-2xl rounded-lg border bg-card p-5">
|
|
49
50
|
<Button asChild size="sm" variant="ghost" className="-ml-2 mb-4">
|
|
50
51
|
<Link to="/apps">
|
|
51
52
|
<IconArrowLeft size={15} className="mr-1.5" />
|
|
52
|
-
|
|
53
|
+
{t("dispatch.nav.apps")}
|
|
53
54
|
</Link>
|
|
54
55
|
</Button>
|
|
55
56
|
|
|
@@ -62,10 +63,10 @@ export default function WorkspaceAppRoute() {
|
|
|
62
63
|
) : !app ? (
|
|
63
64
|
<div className="space-y-3">
|
|
64
65
|
<h2 className="text-base font-semibold text-foreground">
|
|
65
|
-
|
|
66
|
+
{t("dispatch.pages.appNotFound")}
|
|
66
67
|
</h2>
|
|
67
68
|
<p className="text-sm text-muted-foreground">
|
|
68
|
-
|
|
69
|
+
{t("dispatch.pages.pageNotFoundDescription")}
|
|
69
70
|
</p>
|
|
70
71
|
</div>
|
|
71
72
|
) : app.status === "pending" ? (
|
|
@@ -79,23 +80,23 @@ export default function WorkspaceAppRoute() {
|
|
|
79
80
|
className="gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300"
|
|
80
81
|
>
|
|
81
82
|
<IconClockHour4 size={12} />
|
|
82
|
-
|
|
83
|
+
{t("dispatch.pages.building")}
|
|
83
84
|
</Badge>
|
|
84
85
|
</div>
|
|
85
86
|
<p className="text-sm text-muted-foreground">
|
|
86
|
-
|
|
87
|
+
{t("dispatch.pages.appBuildingPrefix")}{" "}
|
|
87
88
|
<span className="font-mono text-foreground">{app.path}</span>{" "}
|
|
88
|
-
|
|
89
|
+
{t("dispatch.pages.appBuildingSuffix")}
|
|
89
90
|
</p>
|
|
90
91
|
{app.branchName ? (
|
|
91
92
|
<p className="text-xs text-muted-foreground">
|
|
92
|
-
|
|
93
|
+
{t("dispatch.pages.branch", { branch: app.branchName })}
|
|
93
94
|
</p>
|
|
94
95
|
) : null}
|
|
95
96
|
{app.builderUrl ? (
|
|
96
97
|
<Button asChild>
|
|
97
98
|
<a href={app.builderUrl} target="_blank" rel="noreferrer">
|
|
98
|
-
|
|
99
|
+
{t("dispatch.pages.openBuilderBranch")}
|
|
99
100
|
<IconArrowUpRight size={15} className="ml-1.5" />
|
|
100
101
|
</a>
|
|
101
102
|
</Button>
|
|
@@ -104,16 +105,16 @@ export default function WorkspaceAppRoute() {
|
|
|
104
105
|
) : (
|
|
105
106
|
<div className="space-y-3">
|
|
106
107
|
<h2 className="text-base font-semibold text-foreground">
|
|
107
|
-
|
|
108
|
+
{t("dispatch.pages.openingApp", { name: app.name })}
|
|
108
109
|
</h2>
|
|
109
110
|
<p className="text-sm text-muted-foreground">
|
|
110
|
-
|
|
111
|
+
{t("dispatch.pages.redirectingTo")}{" "}
|
|
111
112
|
<span className="font-mono text-foreground">{app.path}</span>.
|
|
112
113
|
</p>
|
|
113
114
|
{href ? (
|
|
114
115
|
<Button asChild>
|
|
115
116
|
<a href={href}>
|
|
116
|
-
|
|
117
|
+
{t("dispatch.pages.openApp")}
|
|
117
118
|
<IconArrowUpRight size={15} className="ml-1.5" />
|
|
118
119
|
</a>
|
|
119
120
|
</Button>
|