@agent-native/dispatch 0.13.2 → 0.13.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/components/layout/Layout.d.ts +4 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +55 -10
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/server/lib/dreams-store.js +1 -1
- package/dist/server/lib/dreams-store.js.map +1 -1
- package/package.json +2 -2
- package/src/components/layout/Layout.tsx +229 -94
- package/src/server/lib/dreams-store.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/dispatch",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.4",
|
|
4
4
|
"description": "Dispatch — workspace control plane for agent-native apps. Vault, integrations, destinations, scheduled jobs, and cross-app delegation, shipped as a single drop-in package.",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"typescript": "^6.0.3",
|
|
95
95
|
"vite": "8.1.0",
|
|
96
96
|
"vitest": "^4.1.5",
|
|
97
|
-
"@agent-native/core": "0.
|
|
97
|
+
"@agent-native/core": "0.80.1"
|
|
98
98
|
},
|
|
99
99
|
"peerDependencies": {
|
|
100
100
|
"@agent-native/core": ">=0.8.0",
|
|
@@ -33,6 +33,8 @@ import {
|
|
|
33
33
|
IconBroadcast,
|
|
34
34
|
IconFingerprint,
|
|
35
35
|
IconHistory,
|
|
36
|
+
IconLayoutSidebarLeftCollapse,
|
|
37
|
+
IconLayoutSidebarLeftExpand,
|
|
36
38
|
IconPuzzle,
|
|
37
39
|
IconSettings,
|
|
38
40
|
IconShieldCheck,
|
|
@@ -219,6 +221,7 @@ const OPERATIONS_NAV_ITEMS = [
|
|
|
219
221
|
const EMPTY_NAV_ITEMS: readonly DispatchNavItem[] = [];
|
|
220
222
|
|
|
221
223
|
const CHROMELESS_PATHS = ["/approval"];
|
|
224
|
+
const SIDEBAR_COLLAPSE_KEY = "dispatch.sidebar.collapsed";
|
|
222
225
|
|
|
223
226
|
// Routes whose page renders its own toolbar (with NotificationsBell + AgentToggleButton).
|
|
224
227
|
// Layout still mounts the sidebar + AgentSidebar, but skips its own Header so
|
|
@@ -573,9 +576,15 @@ function DispatchChatsSection({ onNavigate }: { onNavigate?: () => void }) {
|
|
|
573
576
|
export function NavContent({
|
|
574
577
|
onNavigate,
|
|
575
578
|
extensions,
|
|
579
|
+
collapsed = false,
|
|
580
|
+
collapsible = false,
|
|
581
|
+
onCollapsedChange,
|
|
576
582
|
}: {
|
|
577
583
|
onNavigate?: () => void;
|
|
578
584
|
extensions?: DispatchExtensionConfig;
|
|
585
|
+
collapsed?: boolean;
|
|
586
|
+
collapsible?: boolean;
|
|
587
|
+
onCollapsedChange?: (collapsed: boolean) => void;
|
|
579
588
|
}) {
|
|
580
589
|
const t = useT();
|
|
581
590
|
const location = useLocation();
|
|
@@ -616,45 +625,91 @@ export function NavContent({
|
|
|
616
625
|
const label = navLabel(item);
|
|
617
626
|
return (
|
|
618
627
|
<li key={item.id}>
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
628
|
+
{collapsed ? (
|
|
629
|
+
<Tooltip>
|
|
630
|
+
<TooltipTrigger asChild>
|
|
631
|
+
<NavLink
|
|
632
|
+
to={dispatchNavLinkTarget(item.to)}
|
|
633
|
+
onClick={(event) => {
|
|
634
|
+
if (
|
|
635
|
+
item.id === "chat" &&
|
|
636
|
+
localPathname !== "/chat" &&
|
|
637
|
+
!event.metaKey &&
|
|
638
|
+
!event.ctrlKey &&
|
|
639
|
+
!event.shiftKey &&
|
|
640
|
+
!event.altKey
|
|
641
|
+
) {
|
|
642
|
+
event.preventDefault();
|
|
643
|
+
navigateWithAgentChatViewTransition(
|
|
644
|
+
navigate,
|
|
645
|
+
dispatchNavLinkTarget("/chat"),
|
|
646
|
+
);
|
|
647
|
+
onNavigate?.();
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
onNavigate?.();
|
|
651
|
+
}}
|
|
652
|
+
aria-label={label}
|
|
653
|
+
className={({ isActive }) => {
|
|
654
|
+
const active = isActive || itemMatchesLocalPath;
|
|
655
|
+
return cn(
|
|
656
|
+
"flex h-10 w-10 items-center justify-center rounded-md text-sm",
|
|
657
|
+
active
|
|
658
|
+
? "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
|
|
659
|
+
: "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
660
|
+
);
|
|
661
|
+
}}
|
|
662
|
+
>
|
|
663
|
+
{Icon ? (
|
|
664
|
+
<Icon size={16} className="shrink-0" />
|
|
665
|
+
) : (
|
|
666
|
+
<span className="h-4 w-4 shrink-0" aria-hidden="true" />
|
|
667
|
+
)}
|
|
668
|
+
</NavLink>
|
|
669
|
+
</TooltipTrigger>
|
|
670
|
+
<TooltipContent side="right">{label}</TooltipContent>
|
|
671
|
+
</Tooltip>
|
|
672
|
+
) : (
|
|
673
|
+
<NavLink
|
|
674
|
+
to={dispatchNavLinkTarget(item.to)}
|
|
675
|
+
onClick={(event) => {
|
|
676
|
+
if (
|
|
677
|
+
item.id === "chat" &&
|
|
678
|
+
localPathname !== "/chat" &&
|
|
679
|
+
!event.metaKey &&
|
|
680
|
+
!event.ctrlKey &&
|
|
681
|
+
!event.shiftKey &&
|
|
682
|
+
!event.altKey
|
|
683
|
+
) {
|
|
684
|
+
event.preventDefault();
|
|
685
|
+
navigateWithAgentChatViewTransition(
|
|
686
|
+
navigate,
|
|
687
|
+
dispatchNavLinkTarget("/chat"),
|
|
688
|
+
);
|
|
689
|
+
onNavigate?.();
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
635
692
|
onNavigate?.();
|
|
636
|
-
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
</NavLink>
|
|
657
|
-
{item.id === "chat" && itemMatchesLocalPath ? (
|
|
693
|
+
}}
|
|
694
|
+
className={({ isActive }) => {
|
|
695
|
+
const active = isActive || itemMatchesLocalPath;
|
|
696
|
+
return cn(
|
|
697
|
+
"flex h-8 w-full items-center gap-2 rounded-md px-2 text-sm",
|
|
698
|
+
active
|
|
699
|
+
? "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
|
|
700
|
+
: "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
|
|
701
|
+
);
|
|
702
|
+
}}
|
|
703
|
+
>
|
|
704
|
+
{Icon ? (
|
|
705
|
+
<Icon size={16} className="shrink-0" />
|
|
706
|
+
) : (
|
|
707
|
+
<span className="h-4 w-4 shrink-0" aria-hidden="true" />
|
|
708
|
+
)}
|
|
709
|
+
<span className="truncate">{label}</span>
|
|
710
|
+
</NavLink>
|
|
711
|
+
)}
|
|
712
|
+
{!collapsed && item.id === "chat" && itemMatchesLocalPath ? (
|
|
658
713
|
<DispatchChatsSection onNavigate={onNavigate} />
|
|
659
714
|
) : null}
|
|
660
715
|
</li>
|
|
@@ -663,70 +718,118 @@ export function NavContent({
|
|
|
663
718
|
|
|
664
719
|
return (
|
|
665
720
|
<>
|
|
666
|
-
<div className="border-b px-
|
|
667
|
-
<div
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
721
|
+
<div className={cn("border-b py-3", collapsed ? "px-1" : "px-4")}>
|
|
722
|
+
<div
|
|
723
|
+
className={cn(
|
|
724
|
+
"flex items-center",
|
|
725
|
+
collapsed ? "justify-center" : "gap-3",
|
|
726
|
+
)}
|
|
727
|
+
>
|
|
728
|
+
{!collapsed && (
|
|
729
|
+
<>
|
|
730
|
+
<div className="flex h-9 w-9 items-center justify-center rounded-xl border bg-card text-foreground">
|
|
731
|
+
<img
|
|
732
|
+
src={appPath("/agent-native-icon-light.svg")}
|
|
733
|
+
alt=""
|
|
734
|
+
aria-hidden="true"
|
|
735
|
+
className="block h-4 w-auto shrink-0 dark:hidden"
|
|
736
|
+
/>
|
|
737
|
+
<img
|
|
738
|
+
src={appPath("/agent-native-icon-dark.svg")}
|
|
739
|
+
alt=""
|
|
740
|
+
aria-hidden="true"
|
|
741
|
+
className="hidden h-4 w-auto shrink-0 dark:block"
|
|
742
|
+
/>
|
|
743
|
+
</div>
|
|
744
|
+
<div className="min-w-0 flex-1">
|
|
745
|
+
<div className="truncate text-sm font-semibold text-foreground">
|
|
746
|
+
{workspaceLabel ?? "Dispatch"}
|
|
747
|
+
</div>
|
|
748
|
+
<div className="truncate text-xs text-muted-foreground">
|
|
749
|
+
{workspaceLabel
|
|
750
|
+
? t("dispatch.sidebar.workspaceSubtitle", {
|
|
751
|
+
count: ws?.appCount ?? 0,
|
|
752
|
+
})
|
|
753
|
+
: t("dispatch.sidebar.workspaceControlPlane")}
|
|
754
|
+
</div>
|
|
755
|
+
</div>
|
|
756
|
+
</>
|
|
757
|
+
)}
|
|
758
|
+
{collapsible ? (
|
|
759
|
+
<Tooltip>
|
|
760
|
+
<TooltipTrigger asChild>
|
|
761
|
+
<button
|
|
762
|
+
type="button"
|
|
763
|
+
onClick={() => onCollapsedChange?.(!collapsed)}
|
|
764
|
+
aria-label={
|
|
765
|
+
collapsed
|
|
766
|
+
? t("sidebar.expandSidebar")
|
|
767
|
+
: t("sidebar.collapseSidebar")
|
|
768
|
+
}
|
|
769
|
+
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-sidebar-foreground/65 transition-colors hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
|
|
770
|
+
>
|
|
771
|
+
{collapsed ? (
|
|
772
|
+
<IconLayoutSidebarLeftExpand className="h-4 w-4 rtl:-scale-x-100" />
|
|
773
|
+
) : (
|
|
774
|
+
<IconLayoutSidebarLeftCollapse className="h-4 w-4 rtl:-scale-x-100" />
|
|
775
|
+
)}
|
|
776
|
+
</button>
|
|
777
|
+
</TooltipTrigger>
|
|
778
|
+
<TooltipContent side="right">
|
|
779
|
+
{collapsed
|
|
780
|
+
? t("sidebar.expandSidebar")
|
|
781
|
+
: t("sidebar.collapseSidebar")}
|
|
782
|
+
</TooltipContent>
|
|
783
|
+
</Tooltip>
|
|
784
|
+
) : null}
|
|
694
785
|
</div>
|
|
695
786
|
</div>
|
|
696
787
|
|
|
697
788
|
<div className="flex min-h-0 flex-1 flex-col overflow-y-auto">
|
|
698
|
-
<nav className="px-
|
|
699
|
-
<ul
|
|
789
|
+
<nav className={cn("py-3", collapsed ? "px-1" : "px-2")}>
|
|
790
|
+
<ul
|
|
791
|
+
className={cn(
|
|
792
|
+
"space-y-0.5",
|
|
793
|
+
collapsed && "flex flex-col items-center",
|
|
794
|
+
)}
|
|
795
|
+
>
|
|
796
|
+
{(collapsed
|
|
797
|
+
? [...primaryNavItems, ...operationsNavItems]
|
|
798
|
+
: primaryNavItems
|
|
799
|
+
).map(renderNavItem)}
|
|
800
|
+
</ul>
|
|
700
801
|
</nav>
|
|
701
802
|
|
|
702
|
-
|
|
703
|
-
<div className="
|
|
704
|
-
<
|
|
705
|
-
<
|
|
706
|
-
<
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
803
|
+
{!collapsed ? (
|
|
804
|
+
<div className="mt-auto shrink-0">
|
|
805
|
+
<div className="px-2 py-2">
|
|
806
|
+
<details className="group" open={operationsOpen}>
|
|
807
|
+
<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">
|
|
808
|
+
<span>{t("dispatch.nav.operations")}</span>
|
|
809
|
+
<IconChevronDown
|
|
810
|
+
size={14}
|
|
811
|
+
className="transition-transform group-open:rotate-180"
|
|
812
|
+
/>
|
|
813
|
+
</summary>
|
|
814
|
+
<ul className="mt-1 space-y-0.5">
|
|
815
|
+
{operationsNavItems.map(renderNavItem)}
|
|
816
|
+
</ul>
|
|
817
|
+
</details>
|
|
818
|
+
</div>
|
|
717
819
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
820
|
+
<div className="px-2 py-1">
|
|
821
|
+
<ExtensionsSidebarSection />
|
|
822
|
+
</div>
|
|
721
823
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
824
|
+
<div className="px-3 py-2">
|
|
825
|
+
<OrgSwitcher />
|
|
826
|
+
</div>
|
|
725
827
|
|
|
726
|
-
|
|
727
|
-
|
|
828
|
+
<div className="px-3 py-2">
|
|
829
|
+
<FeedbackButton />
|
|
830
|
+
</div>
|
|
728
831
|
</div>
|
|
729
|
-
|
|
832
|
+
) : null}
|
|
730
833
|
</div>
|
|
731
834
|
</>
|
|
732
835
|
);
|
|
@@ -743,6 +846,14 @@ export function Layout({
|
|
|
743
846
|
const location = useLocation();
|
|
744
847
|
const navigate = useNavigate();
|
|
745
848
|
const [mobileOpen, setMobileOpen] = useState(false);
|
|
849
|
+
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => {
|
|
850
|
+
if (typeof window === "undefined") return false;
|
|
851
|
+
try {
|
|
852
|
+
return window.localStorage.getItem(SIDEBAR_COLLAPSE_KEY) === "1";
|
|
853
|
+
} catch {
|
|
854
|
+
return false;
|
|
855
|
+
}
|
|
856
|
+
});
|
|
746
857
|
const localPathname = localDispatchPath(location.pathname);
|
|
747
858
|
const isChatRoute =
|
|
748
859
|
localPathname === "/chat" || localPathname.startsWith("/chat/");
|
|
@@ -758,6 +869,18 @@ export function Layout({
|
|
|
758
869
|
};
|
|
759
870
|
useAgentChatHomeHandoffLinks(chatHandoffLinkOptions);
|
|
760
871
|
|
|
872
|
+
useEffect(() => {
|
|
873
|
+
if (typeof window === "undefined") return;
|
|
874
|
+
try {
|
|
875
|
+
window.localStorage.setItem(
|
|
876
|
+
SIDEBAR_COLLAPSE_KEY,
|
|
877
|
+
sidebarCollapsed ? "1" : "0",
|
|
878
|
+
);
|
|
879
|
+
} catch {
|
|
880
|
+
// Ignore storage failures; the in-memory preference still works.
|
|
881
|
+
}
|
|
882
|
+
}, [sidebarCollapsed]);
|
|
883
|
+
|
|
761
884
|
if (CHROMELESS_PATHS.some((path) => localPathname === path)) {
|
|
762
885
|
return <>{children}</>;
|
|
763
886
|
}
|
|
@@ -817,8 +940,19 @@ export function Layout({
|
|
|
817
940
|
return (
|
|
818
941
|
<HeaderActionsProvider>
|
|
819
942
|
<div className="agent-layout-shell flex h-screen w-full overflow-hidden bg-background">
|
|
820
|
-
<aside
|
|
821
|
-
|
|
943
|
+
<aside
|
|
944
|
+
data-collapsed={sidebarCollapsed ? "true" : "false"}
|
|
945
|
+
className={cn(
|
|
946
|
+
"agent-layout-left-drawer hidden shrink-0 flex-col border-e bg-sidebar text-sidebar-foreground transition-[width] duration-200 ease-out lg:flex",
|
|
947
|
+
sidebarCollapsed ? "w-12" : "w-64",
|
|
948
|
+
)}
|
|
949
|
+
>
|
|
950
|
+
<NavContent
|
|
951
|
+
extensions={extensions}
|
|
952
|
+
collapsed={sidebarCollapsed}
|
|
953
|
+
collapsible
|
|
954
|
+
onCollapsedChange={setSidebarCollapsed}
|
|
955
|
+
/>
|
|
822
956
|
</aside>
|
|
823
957
|
|
|
824
958
|
<Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
|
|
@@ -835,6 +969,7 @@ export function Layout({
|
|
|
835
969
|
<div className="flex h-full w-full flex-col">
|
|
836
970
|
<NavContent
|
|
837
971
|
extensions={extensions}
|
|
972
|
+
collapsed={false}
|
|
838
973
|
onNavigate={() => setMobileOpen(false)}
|
|
839
974
|
/>
|
|
840
975
|
</div>
|
|
@@ -3061,7 +3061,7 @@ export async function rejectDreamProposal(
|
|
|
3061
3061
|
function cronLooksValid(schedule: string): boolean {
|
|
3062
3062
|
const parts = schedule.trim().split(/\s+/);
|
|
3063
3063
|
if (parts.length !== 5) return false;
|
|
3064
|
-
return parts.every((part) => /^[\d
|
|
3064
|
+
return parts.every((part) => /^[\d*/,-]+$/.test(part));
|
|
3065
3065
|
}
|
|
3066
3066
|
|
|
3067
3067
|
function dreamJobBody(settings: Omit<DreamSettings, "scope" | "scopeId">) {
|