@agent-native/dispatch 0.4.0 → 0.5.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.
@@ -9,6 +9,7 @@ export interface WorkspaceAppSummary {
9
9
  statusLabel?: string;
10
10
  builderUrl?: string | null;
11
11
  branchName?: string | null;
12
+ archived?: boolean;
12
13
  }
13
14
 
14
15
  export function workspaceAppHref(app: WorkspaceAppSummary): string | null {
@@ -1,5 +1,23 @@
1
- import { useActionQuery } from "@agent-native/core/client";
2
- import { IconApps, IconPlus } from "@tabler/icons-react";
1
+ import { useState } from "react";
2
+ import { useActionMutation, useActionQuery } from "@agent-native/core/client";
3
+ import {
4
+ IconApps,
5
+ IconBrush,
6
+ IconCalendarMonth,
7
+ IconChartBar,
8
+ IconClipboardList,
9
+ IconEyeOff,
10
+ IconFileText,
11
+ IconLoader2,
12
+ IconMail,
13
+ IconPlus,
14
+ IconPresentation,
15
+ IconScreenShare,
16
+ IconSparkles,
17
+ IconStack3,
18
+ IconVideo,
19
+ } from "@tabler/icons-react";
20
+ import { toast } from "sonner";
3
21
  import { CreateAppPopover } from "@/components/create-app-popover";
4
22
  import { DispatchShell } from "@/components/dispatch-shell";
5
23
  import { WorkspaceAppCard } from "@/components/workspace-app-card";
@@ -16,10 +34,33 @@ interface WorkspaceInfo {
16
34
  appCount: number;
17
35
  }
18
36
 
37
+ interface AvailableTemplate {
38
+ name: string;
39
+ label: string;
40
+ hint: string;
41
+ icon: string;
42
+ color: string;
43
+ colorRgb: string;
44
+ core: boolean;
45
+ }
46
+
47
+ const TEMPLATE_ICONS: Record<string, typeof IconMail> = {
48
+ Mail: IconMail,
49
+ CalendarMonth: IconCalendarMonth,
50
+ FileText: IconFileText,
51
+ Presentation: IconPresentation,
52
+ ScreenShare: IconScreenShare,
53
+ ChartBar: IconChartBar,
54
+ ClipboardList: IconClipboardList,
55
+ Brush: IconBrush,
56
+ Video: IconVideo,
57
+ };
58
+
19
59
  export default function AppsRoute() {
60
+ const [showHidden, setShowHidden] = useState(false);
20
61
  const { data: apps = [] } = useActionQuery(
21
62
  "list-workspace-apps",
22
- { includeAgentCards: false },
63
+ { includeAgentCards: false, includeArchived: true },
23
64
  {
24
65
  refetchInterval: 2_000,
25
66
  },
@@ -29,11 +70,20 @@ export default function AppsRoute() {
29
70
  {},
30
71
  { staleTime: 60_000 },
31
72
  );
73
+ const { data: templates = [] } = useActionQuery(
74
+ "list-available-workspace-templates",
75
+ {},
76
+ { refetchInterval: 5_000 },
77
+ );
78
+
32
79
  const ws = workspace as WorkspaceInfo | undefined;
33
80
  const workspaceLabel = ws?.displayName ?? ws?.name ?? null;
34
- const typedApps = (apps as WorkspaceAppSummary[]).filter(
81
+ const allApps = (apps as WorkspaceAppSummary[]).filter(
35
82
  (app) => !app.isDispatch,
36
83
  );
84
+ const visibleApps = allApps.filter((app) => !app.archived);
85
+ const archivedApps = allApps.filter((app) => app.archived);
86
+ const typedTemplates = templates as AvailableTemplate[];
37
87
 
38
88
  return (
39
89
  <DispatchShell
@@ -44,7 +94,7 @@ export default function AppsRoute() {
44
94
  : "Open workspace apps and start new app creation from Dispatch."
45
95
  }
46
96
  >
47
- <div className="space-y-4">
97
+ <div className="space-y-6">
48
98
  <section className="space-y-3">
49
99
  <div className="flex items-center justify-between gap-3">
50
100
  <div className="flex items-center gap-2">
@@ -67,14 +117,117 @@ export default function AppsRoute() {
67
117
  </div>
68
118
 
69
119
  <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
70
- {typedApps.map((app) => (
120
+ {visibleApps.map((app) => (
71
121
  <WorkspaceAppCard key={app.id} app={app} />
72
122
  ))}
73
123
 
74
124
  <CreateAppPopover />
75
125
  </div>
76
126
  </section>
127
+
128
+ {typedTemplates.length > 0 ? (
129
+ <section className="space-y-3">
130
+ <div className="flex items-center gap-2">
131
+ <IconStack3 size={16} className="text-muted-foreground" />
132
+ <h2 className="text-sm font-semibold text-foreground">
133
+ Add a template
134
+ </h2>
135
+ <span className="text-xs text-muted-foreground">
136
+ Scaffold a first-party app into{" "}
137
+ <code className="font-mono text-[11px]">apps/</code>.
138
+ </span>
139
+ </div>
140
+ <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
141
+ {typedTemplates.map((template) => (
142
+ <AddTemplateCard key={template.name} template={template} />
143
+ ))}
144
+ </div>
145
+ </section>
146
+ ) : null}
147
+
148
+ {archivedApps.length > 0 ? (
149
+ <section className="space-y-3">
150
+ <button
151
+ type="button"
152
+ onClick={() => setShowHidden((cur) => !cur)}
153
+ className="inline-flex cursor-pointer items-center gap-2 text-xs text-muted-foreground hover:text-foreground"
154
+ >
155
+ <IconEyeOff size={14} />
156
+ {showHidden ? "Hide" : "Show"} {archivedApps.length} hidden{" "}
157
+ {archivedApps.length === 1 ? "app" : "apps"}
158
+ </button>
159
+ {showHidden ? (
160
+ <div className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
161
+ {archivedApps.map((app) => (
162
+ <WorkspaceAppCard key={app.id} app={app} />
163
+ ))}
164
+ </div>
165
+ ) : null}
166
+ </section>
167
+ ) : null}
77
168
  </div>
78
169
  </DispatchShell>
79
170
  );
80
171
  }
172
+
173
+ function AddTemplateCard({ template }: { template: AvailableTemplate }) {
174
+ const Icon = TEMPLATE_ICONS[template.icon] ?? IconSparkles;
175
+ const scaffold = useActionMutation("scaffold-workspace-app", {
176
+ onSuccess: (result: any) => {
177
+ toast.success(
178
+ `Scaffolded apps/${result?.appId || template.name}. The gateway will pick it up shortly.`,
179
+ );
180
+ },
181
+ onError: (err) => {
182
+ toast.error(
183
+ `Could not scaffold ${template.label}: ${
184
+ err instanceof Error ? err.message : String(err)
185
+ }`,
186
+ );
187
+ },
188
+ });
189
+
190
+ return (
191
+ <div className="group relative flex items-start gap-3 rounded-lg border bg-card p-4 transition hover:border-foreground/30">
192
+ <div
193
+ className="flex h-9 w-9 shrink-0 items-center justify-center rounded-md"
194
+ style={{
195
+ backgroundColor: `rgb(${template.colorRgb} / 0.12)`,
196
+ color: template.color,
197
+ }}
198
+ >
199
+ <Icon size={18} />
200
+ </div>
201
+ <div className="min-w-0 flex-1">
202
+ <div className="flex min-w-0 items-center gap-2">
203
+ <h3 className="truncate text-sm font-semibold text-foreground">
204
+ {template.label}
205
+ </h3>
206
+ </div>
207
+ <p className="mt-1 line-clamp-2 text-xs leading-relaxed text-muted-foreground">
208
+ {template.hint}
209
+ </p>
210
+ <div className="mt-3">
211
+ <Button
212
+ size="sm"
213
+ variant="outline"
214
+ disabled={scaffold.isPending}
215
+ onClick={() => scaffold.mutate({ template: template.name })}
216
+ >
217
+ {scaffold.isPending ? (
218
+ <>
219
+ <IconLoader2 size={14} className="mr-1.5 animate-spin" />
220
+ Adding…
221
+ </>
222
+ ) : (
223
+ <>
224
+ <IconPlus size={14} className="mr-1.5" />
225
+ Add to workspace
226
+ </>
227
+ )}
228
+ </Button>
229
+ </div>
230
+ </div>
231
+ </div>
232
+ );
233
+ }
@@ -782,7 +782,7 @@ const ADDABLE_TEMPLATES: AvailableWorkspaceTemplate[] = [
782
782
  name: "calendar",
783
783
  label: "Calendar",
784
784
  hint: "Manage events, sync, and public booking",
785
- icon: "CalendarDays",
785
+ icon: "CalendarMonth",
786
786
  color: "#8B5CF6",
787
787
  colorRgb: "139 92 246",
788
788
  core: true,
@@ -800,7 +800,7 @@ const ADDABLE_TEMPLATES: AvailableWorkspaceTemplate[] = [
800
800
  name: "slides",
801
801
  label: "Slides",
802
802
  hint: "Generate and edit React presentations",
803
- icon: "GalleryHorizontal",
803
+ icon: "Presentation",
804
804
  color: "#EC4899",
805
805
  colorRgb: "236 72 153",
806
806
  core: true,
@@ -818,7 +818,7 @@ const ADDABLE_TEMPLATES: AvailableWorkspaceTemplate[] = [
818
818
  name: "analytics",
819
819
  label: "Analytics",
820
820
  hint: "Connect data sources, prompt for charts",
821
- icon: "BarChart2",
821
+ icon: "ChartBar",
822
822
  color: "#F59E0B",
823
823
  colorRgb: "245 158 11",
824
824
  core: true,