@polpo-ai/dashboard 0.1.3 → 0.15.8

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.
Files changed (184) hide show
  1. package/LICENSE +3 -1
  2. package/README.md +13 -40
  3. package/dist/components.d.ts +53 -0
  4. package/dist/components.d.ts.map +1 -0
  5. package/dist/components.js +81 -0
  6. package/dist/components.js.map +1 -0
  7. package/dist/host.d.ts +24 -0
  8. package/dist/host.d.ts.map +1 -0
  9. package/dist/host.js +18 -0
  10. package/dist/host.js.map +1 -0
  11. package/dist/index.d.ts +13 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +11 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/test/setup.d.ts +2 -0
  16. package/dist/test/setup.d.ts.map +1 -0
  17. package/dist/test/setup.js +2 -0
  18. package/dist/test/setup.js.map +1 -0
  19. package/dist/views/agent-detail.d.ts +4 -0
  20. package/dist/views/agent-detail.d.ts.map +1 -0
  21. package/dist/views/agent-detail.js +31 -0
  22. package/dist/views/agent-detail.js.map +1 -0
  23. package/dist/views/agents.d.ts +2 -0
  24. package/dist/views/agents.d.ts.map +1 -0
  25. package/dist/views/agents.js +81 -0
  26. package/dist/views/agents.js.map +1 -0
  27. package/dist/views/files.d.ts +2 -0
  28. package/dist/views/files.d.ts.map +1 -0
  29. package/dist/views/files.js +30 -0
  30. package/dist/views/files.js.map +1 -0
  31. package/dist/views/memory.d.ts +2 -0
  32. package/dist/views/memory.d.ts.map +1 -0
  33. package/dist/views/memory.js +19 -0
  34. package/dist/views/memory.js.map +1 -0
  35. package/dist/views/playground.d.ts +4 -0
  36. package/dist/views/playground.d.ts.map +1 -0
  37. package/dist/views/playground.js +43 -0
  38. package/dist/views/playground.js.map +1 -0
  39. package/dist/views/session-detail.d.ts +4 -0
  40. package/dist/views/session-detail.d.ts.map +1 -0
  41. package/dist/views/session-detail.js +23 -0
  42. package/dist/views/session-detail.js.map +1 -0
  43. package/dist/views/sessions.d.ts +2 -0
  44. package/dist/views/sessions.d.ts.map +1 -0
  45. package/dist/views/sessions.js +17 -0
  46. package/dist/views/sessions.js.map +1 -0
  47. package/dist/views/skills.d.ts +2 -0
  48. package/dist/views/skills.d.ts.map +1 -0
  49. package/dist/views/skills.js +16 -0
  50. package/dist/views/skills.js.map +1 -0
  51. package/package.json +47 -83
  52. package/src/styles.css +169 -0
  53. package/app/(dashboard)/error.tsx +0 -47
  54. package/app/(dashboard)/layout.tsx +0 -41
  55. package/app/(dashboard)/loading.tsx +0 -30
  56. package/app/(dashboard)/projects/[id]/agents/[name]/memory/view.tsx +0 -36
  57. package/app/(dashboard)/projects/[id]/agents/[name]/models-view.tsx +0 -30
  58. package/app/(dashboard)/projects/[id]/agents/[name]/page.tsx +0 -51
  59. package/app/(dashboard)/projects/[id]/agents/[name]/prompt/view.tsx +0 -211
  60. package/app/(dashboard)/projects/[id]/agents/[name]/skills/view.tsx +0 -84
  61. package/app/(dashboard)/projects/[id]/agents/[name]/tools/view.tsx +0 -525
  62. package/app/(dashboard)/projects/[id]/agents/[name]/vault/view.tsx +0 -61
  63. package/app/(dashboard)/projects/[id]/agents/page.tsx +0 -39
  64. package/app/(dashboard)/projects/[id]/agents/view.tsx +0 -283
  65. package/app/(dashboard)/projects/[id]/layout.tsx +0 -11
  66. package/app/(dashboard)/projects/[id]/logs/page.tsx +0 -27
  67. package/app/(dashboard)/projects/[id]/logs/view.tsx +0 -184
  68. package/app/(dashboard)/projects/[id]/memory/page.tsx +0 -40
  69. package/app/(dashboard)/projects/[id]/memory/view.tsx +0 -17
  70. package/app/(dashboard)/projects/[id]/missions/[missionId]/page.tsx +0 -88
  71. package/app/(dashboard)/projects/[id]/missions/[missionId]/view.tsx +0 -569
  72. package/app/(dashboard)/projects/[id]/missions/page.tsx +0 -32
  73. package/app/(dashboard)/projects/[id]/missions/view.tsx +0 -282
  74. package/app/(dashboard)/projects/[id]/onboarding-checklist.tsx +0 -359
  75. package/app/(dashboard)/projects/[id]/page.tsx +0 -68
  76. package/app/(dashboard)/projects/[id]/playbooks/[name]/page.tsx +0 -5
  77. package/app/(dashboard)/projects/[id]/playbooks/[name]/view.tsx +0 -433
  78. package/app/(dashboard)/projects/[id]/playbooks/page.tsx +0 -5
  79. package/app/(dashboard)/projects/[id]/playbooks/view.tsx +0 -222
  80. package/app/(dashboard)/projects/[id]/schedules/page.tsx +0 -44
  81. package/app/(dashboard)/projects/[id]/schedules/view.tsx +0 -385
  82. package/app/(dashboard)/projects/[id]/sessions/[sessionId]/page.tsx +0 -55
  83. package/app/(dashboard)/projects/[id]/sessions/[sessionId]/raw-view.tsx +0 -373
  84. package/app/(dashboard)/projects/[id]/sessions/[sessionId]/view.tsx +0 -124
  85. package/app/(dashboard)/projects/[id]/sessions/page.tsx +0 -31
  86. package/app/(dashboard)/projects/[id]/sessions/view.tsx +0 -242
  87. package/app/(dashboard)/projects/[id]/settings/page.tsx +0 -52
  88. package/app/(dashboard)/projects/[id]/skills/[name]/page.tsx +0 -65
  89. package/app/(dashboard)/projects/[id]/skills/[name]/view.tsx +0 -227
  90. package/app/(dashboard)/projects/[id]/skills/page.tsx +0 -31
  91. package/app/(dashboard)/projects/[id]/skills/view.tsx +0 -243
  92. package/app/(dashboard)/projects/[id]/storage/page.tsx +0 -5
  93. package/app/(dashboard)/projects/[id]/storage/view.tsx +0 -385
  94. package/app/(dashboard)/projects/[id]/tasks/[taskId]/activity/view.tsx +0 -336
  95. package/app/(dashboard)/projects/[id]/tasks/[taskId]/assessment/view.tsx +0 -113
  96. package/app/(dashboard)/projects/[id]/tasks/[taskId]/blueprint.ts +0 -72
  97. package/app/(dashboard)/projects/[id]/tasks/[taskId]/output/view.tsx +0 -90
  98. package/app/(dashboard)/projects/[id]/tasks/[taskId]/page.tsx +0 -63
  99. package/app/(dashboard)/projects/[id]/tasks/[taskId]/view.tsx +0 -137
  100. package/app/(dashboard)/projects/[id]/tasks/page.tsx +0 -40
  101. package/app/(dashboard)/projects/[id]/tasks/view.tsx +0 -421
  102. package/app/(dashboard)/projects/[id]/view.tsx +0 -421
  103. package/app/(dashboard)/projects/[id]/welcome-banner.tsx +0 -156
  104. package/app/(dashboard)/projects/projects-list.tsx +0 -126
  105. package/app/(playground)/error.tsx +0 -42
  106. package/app/(playground)/layout.tsx +0 -27
  107. package/app/(playground)/projects/[id]/playground/page.tsx +0 -57
  108. package/app/(playground)/projects/[id]/playground/view.tsx +0 -167
  109. package/app/(playground)/projects/[id]/playground-legacy/page.tsx +0 -47
  110. package/app/(playground)/projects/[id]/playground-legacy/skeleton.tsx +0 -63
  111. package/app/(playground)/projects/[id]/playground-legacy/view.tsx +0 -237
  112. package/app/favicon.ico +0 -0
  113. package/app/globals.css +0 -385
  114. package/app/icon.svg +0 -6
  115. package/app/layout.tsx +0 -39
  116. package/app/page.tsx +0 -11
  117. package/components/ai-elements/code-block.tsx +0 -562
  118. package/components/dashboard/agent-capabilities.tsx +0 -326
  119. package/components/dashboard/agent-identity-header.tsx +0 -50
  120. package/components/dashboard/agent-model-picker.tsx +0 -195
  121. package/components/dashboard/agent-model-selector.tsx +0 -121
  122. package/components/dashboard/agent-picker-card.tsx +0 -46
  123. package/components/dashboard/agent-studio.tsx +0 -242
  124. package/components/dashboard/agents-table.tsx +0 -48
  125. package/components/dashboard/breadcrumb.tsx +0 -70
  126. package/components/dashboard/builder-chat.tsx +0 -418
  127. package/components/dashboard/chat-shell.tsx +0 -40
  128. package/components/dashboard/client-picker.tsx +0 -63
  129. package/components/dashboard/command-snippet.tsx +0 -77
  130. package/components/dashboard/connect-dialog.tsx +0 -680
  131. package/components/dashboard/copy-button.tsx +0 -45
  132. package/components/dashboard/copy-card.tsx +0 -48
  133. package/components/dashboard/file-browser.tsx +0 -161
  134. package/components/dashboard/hint.tsx +0 -22
  135. package/components/dashboard/inference-mode.tsx +0 -15
  136. package/components/dashboard/manual-refresh-button.tsx +0 -52
  137. package/components/dashboard/markdown.tsx +0 -31
  138. package/components/dashboard/mcp-install-panel.tsx +0 -196
  139. package/components/dashboard/mission-graph.tsx +0 -262
  140. package/components/dashboard/nav-tabs.tsx +0 -86
  141. package/components/dashboard/polpo-chat.tsx +0 -468
  142. package/components/dashboard/project-copilot.tsx +0 -253
  143. package/components/dashboard/sdk-snippet-panel.tsx +0 -194
  144. package/components/dashboard/section-header.tsx +0 -37
  145. package/components/dashboard/settings-form.tsx +0 -2477
  146. package/components/dashboard/sidebar.tsx +0 -385
  147. package/components/dashboard/skeletons.tsx +0 -1054
  148. package/components/dashboard/skills-install-wizard.tsx +0 -294
  149. package/components/dashboard/swarm-runs.tsx +0 -316
  150. package/components/dashboard/tab-toggle.tsx +0 -35
  151. package/components/dashboard/task-studio.tsx +0 -224
  152. package/components/dashboard/top-header.tsx +0 -203
  153. package/components/dashboard/webhook-deliveries.tsx +0 -234
  154. package/components/icons/coding-agents.tsx +0 -151
  155. package/components/json-ld.tsx +0 -8
  156. package/components/providers.tsx +0 -25
  157. package/components/ui/badge.tsx +0 -52
  158. package/components/ui/button.tsx +0 -60
  159. package/components/ui/card.tsx +0 -103
  160. package/components/ui/chart.tsx +0 -356
  161. package/components/ui/command.tsx +0 -196
  162. package/components/ui/dialog.tsx +0 -157
  163. package/components/ui/input-group.tsx +0 -158
  164. package/components/ui/input.tsx +0 -20
  165. package/components/ui/multi-select.tsx +0 -161
  166. package/components/ui/popover.tsx +0 -90
  167. package/components/ui/select.tsx +0 -201
  168. package/components/ui/separator.tsx +0 -25
  169. package/components/ui/sheet.tsx +0 -135
  170. package/components/ui/skeleton.tsx +0 -13
  171. package/components/ui/tabs.tsx +0 -56
  172. package/components/ui/textarea.tsx +0 -18
  173. package/components/ui/tooltip.tsx +0 -66
  174. package/hooks/use-desktop-sidebar.tsx +0 -50
  175. package/hooks/use-mobile-sidebar.tsx +0 -37
  176. package/index.ts +0 -17
  177. package/lib/api.ts +0 -194
  178. package/lib/builder-context.ts +0 -60
  179. package/lib/data-client.ts +0 -68
  180. package/lib/get-query-client.ts +0 -25
  181. package/lib/polpo-client.tsx +0 -49
  182. package/lib/tool-catalog.ts +0 -234
  183. package/lib/use-event-catalog.ts +0 -28
  184. package/lib/utils.ts +0 -6
@@ -1,68 +0,0 @@
1
- import { Suspense } from "react";
2
- import { api, dataApi } from "../../../../lib/api";
3
- import type { Project } from "../../../../lib/api";
4
- import type { AgentConfig, Task, Mission } from "@polpo-ai/core";
5
- import { ProjectOverviewSkeleton } from "../../../../components/dashboard/skeletons";
6
- import ProjectOverviewView from "./view";
7
- import WelcomeBanner from "./welcome-banner";
8
-
9
- async function OverviewData({ id }: { id: string }) {
10
- let agents: AgentConfig[] = [];
11
- let tasks: Task[] = [];
12
- let missions: Mission[] = [];
13
- let project: Project | null = null;
14
-
15
- try {
16
- const [agentsRes, tasksRes, missionsRes, projectRes] = await Promise.all([
17
- dataApi<{ ok: boolean; data: AgentConfig[] }>(id, "/v1/agents").catch(
18
- () => ({ ok: false, data: [] as AgentConfig[] }),
19
- ),
20
- dataApi<{ ok: boolean; data: Task[] }>(id, "/v1/tasks").catch(() => ({
21
- ok: false,
22
- data: [] as Task[],
23
- })),
24
- dataApi<{ ok: boolean; data: Mission[] }>(id, "/v1/missions").catch(
25
- () => ({ ok: false, data: [] as Mission[] }),
26
- ),
27
- api<Project>(`/v1/projects/${id}`).catch(() => null),
28
- ]);
29
- agents = agentsRes.data ?? [];
30
- tasks = tasksRes.data ?? [];
31
- missions = missionsRes.data ?? [];
32
- project = projectRes;
33
- } catch {}
34
-
35
- return (
36
- <ProjectOverviewView
37
- initialAgents={agents}
38
- initialTasks={tasks}
39
- initialMissions={missions}
40
- projectName={project?.name ?? "Your project"}
41
- projectSlug={project?.slug}
42
- initialChecklist={project?.onboardingChecklist ?? {}}
43
- welcomeBanner={
44
- <WelcomeBanner
45
- key={`welcome-${id}`}
46
- projectId={id}
47
- projectName={project?.name ?? "Your project"}
48
- projectSlug={project?.slug}
49
- agentName={agents[0]?.name ?? null}
50
- />
51
- }
52
- />
53
- );
54
- }
55
-
56
- export default async function ProjectOverviewPage({
57
- params,
58
- }: {
59
- params: Promise<{ id: string }>;
60
- }) {
61
- const { id } = await params;
62
-
63
- return (
64
- <Suspense fallback={<ProjectOverviewSkeleton />}>
65
- <OverviewData id={id} />
66
- </Suspense>
67
- );
68
- }
@@ -1,5 +0,0 @@
1
- import PlaybookDetailView from "./view";
2
-
3
- export default function PlaybookDetailPage() {
4
- return <PlaybookDetailView />;
5
- }
@@ -1,433 +0,0 @@
1
- "use client";
2
-
3
- import { useState } from "react";
4
- import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
5
- import { useParams, useRouter } from "next/navigation";
6
- import Link from "next/link";
7
- import { ArrowLeft, Play, Loader2, AlertTriangle, Workflow, Braces, Copy, Check, GitBranch, List } from "lucide-react";
8
- import type { Task } from "@polpo-ai/core";
9
- import { usePolpoClient } from "../../../../../../lib/polpo-client";
10
- import { MissionGraph } from "../../../../../../components/dashboard/mission-graph";
11
- import { ManualRefreshButton } from "../../../../../../components/dashboard/manual-refresh-button";
12
- import {
13
- Dialog,
14
- DialogContent,
15
- DialogHeader,
16
- DialogTitle,
17
- DialogDescription,
18
- DialogFooter,
19
- DialogClose,
20
- } from "../../../../../../components/ui/dialog";
21
-
22
- interface PlaybookParam {
23
- name: string;
24
- type?: string;
25
- description?: string;
26
- required?: boolean;
27
- default?: unknown;
28
- enum?: unknown[];
29
- }
30
-
31
- interface PlaybookTask {
32
- title: string;
33
- assignTo: string;
34
- description?: string;
35
- dependsOn?: string[];
36
- }
37
-
38
- interface Playbook {
39
- name: string;
40
- description: string;
41
- mission: {
42
- prompt?: string;
43
- tasks: PlaybookTask[];
44
- };
45
- parameters?: PlaybookParam[];
46
- version?: string;
47
- author?: string;
48
- tags?: string[];
49
- }
50
-
51
- function highlightJson(json: string): string {
52
- return json.replace(
53
- /("(?:\\.|[^"\\])*")\s*(:)?|(\b(?:true|false|null)\b)|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g,
54
- (match, str, colon, bool, num) => {
55
- if (str) {
56
- if (colon) return `<span style="color:oklch(0.75 0.1 250)">${str}</span>:`;
57
- return `<span style="color:oklch(0.75 0.15 155)">${str}</span>`;
58
- }
59
- if (bool) return `<span style="color:oklch(0.7 0.15 30)">${bool}</span>`;
60
- if (num) return `<span style="color:oklch(0.8 0.12 80)">${num}</span>`;
61
- return match;
62
- },
63
- );
64
- }
65
-
66
- export default function PlaybookDetailView() {
67
- const { id, name } = useParams<{ id: string; name: string }>();
68
- const decodedName = decodeURIComponent(name);
69
- const router = useRouter();
70
- const queryClient = useQueryClient();
71
-
72
- const [view, setView] = useState<"graph" | "tasks" | "json">("graph");
73
- const [showRun, setShowRun] = useState(false);
74
- const [showDelete, setShowDelete] = useState(false);
75
- const [paramValues, setParamValues] = useState<Record<string, string>>({});
76
- const [copied, setCopied] = useState(false);
77
-
78
- const polpo = usePolpoClient(id);
79
- const { data: playbook = null, isLoading, isFetching, refetch } = useQuery({
80
- queryKey: ["playbook", id, decodedName],
81
- queryFn: () => polpo.getPlaybook(decodedName) as unknown as Promise<Playbook | null>,
82
- });
83
-
84
- const runMutation = useMutation({
85
- mutationFn: (params: Record<string, string | number | boolean>) =>
86
- polpo.runPlaybook(decodedName, params),
87
- onSuccess: (data) => {
88
- queryClient.invalidateQueries({ queryKey: ["missions", id] });
89
- setShowRun(false);
90
- if (data?.mission?.id) {
91
- router.push(`/projects/${id}/missions/${data.mission.id}`);
92
- }
93
- },
94
- });
95
-
96
- const deleteMutation = useMutation({
97
- mutationFn: () => polpo.deletePlaybook(decodedName),
98
- onSuccess: () => {
99
- router.push(`/projects/${id}/playbooks`);
100
- },
101
- });
102
-
103
- function handleRun() {
104
- if (!playbook) return;
105
- const params: Record<string, string | number | boolean> = {};
106
- for (const p of playbook.parameters ?? []) {
107
- const raw = paramValues[p.name];
108
- if (!raw && !p.required) continue;
109
- if (p.type === "number") params[p.name] = Number(raw);
110
- else if (p.type === "boolean") params[p.name] = raw === "true";
111
- else params[p.name] = raw;
112
- }
113
- runMutation.mutate(params);
114
- }
115
-
116
- const missingRequired = playbook
117
- ? (playbook.parameters ?? []).filter(p => p.required && !paramValues[p.name]?.trim())
118
- : [];
119
-
120
- if (isLoading) {
121
- return (
122
- <div className="space-y-3">
123
- {[1, 2, 3].map(i => <div key={i} className="h-16 rounded-lg bg-secondary/30 animate-pulse" />)}
124
- </div>
125
- );
126
- }
127
-
128
- if (!playbook) {
129
- return (
130
- <div className="mt-4 border border-border p-8 text-center text-sm text-muted-foreground">
131
- Playbook not found.
132
- </div>
133
- );
134
- }
135
-
136
- const params = playbook.parameters ?? [];
137
- const tasks = playbook.mission?.tasks ?? [];
138
-
139
- // Build fake Task objects for the graph
140
- const fakeTasks = tasks.map((t, i) => ({
141
- id: `pb-${i}`,
142
- title: t.title,
143
- assignTo: t.assignTo,
144
- description: t.description ?? "",
145
- status: "draft" as const,
146
- dependsOn: (t.dependsOn ?? []).map(dep => {
147
- const depIdx = tasks.findIndex(tt => tt.title === dep);
148
- return depIdx >= 0 ? `pb-${depIdx}` : dep;
149
- }),
150
- retries: 0,
151
- maxRetries: 3,
152
- expectations: [],
153
- metrics: [],
154
- createdAt: "",
155
- updatedAt: "",
156
- }));
157
-
158
- return (
159
- <div>
160
- {/* Back */}
161
- <Link
162
- href={`/projects/${id}/playbooks`}
163
- className="inline-flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors"
164
- >
165
- <ArrowLeft className="h-3 w-3" />
166
- Playbooks
167
- </Link>
168
-
169
- {/* Header */}
170
- <div className="mt-4 flex items-start justify-between gap-4">
171
- <div>
172
- <div className="flex items-center gap-2.5">
173
- <Workflow className="h-4 w-4 text-muted-foreground" />
174
- <h2 className="font-mono text-lg font-extrabold tracking-tight">{playbook.name}</h2>
175
- {playbook.version && (
176
- <span className="rounded bg-secondary px-1.5 py-0.5 text-[10px] font-mono text-muted-foreground">
177
- v{playbook.version}
178
- </span>
179
- )}
180
- </div>
181
- <p className="mt-1 text-sm text-muted-foreground">{playbook.description}</p>
182
-
183
- {/* Meta */}
184
- <div className="mt-3 flex items-center gap-3 text-xs text-muted-foreground">
185
- <span>{tasks.length} task{tasks.length !== 1 ? "s" : ""}</span>
186
- <span>{params.length} param{params.length !== 1 ? "s" : ""}</span>
187
- {playbook.author && <span>by {playbook.author}</span>}
188
- </div>
189
-
190
- {/* Tags */}
191
- {playbook.tags && playbook.tags.length > 0 && (
192
- <div className="mt-2 flex gap-1">
193
- {playbook.tags.map(t => (
194
- <span key={t} className="rounded bg-foreground/5 px-1.5 py-0.5 text-[9px] text-muted-foreground">
195
- {t}
196
- </span>
197
- ))}
198
- </div>
199
- )}
200
- </div>
201
- <ManualRefreshButton onRefresh={() => refetch()} isRefreshing={isFetching} className="mt-1 shrink-0" />
202
- </div>
203
-
204
- {/* Parameters */}
205
- {params.length > 0 && (
206
- <section className="mt-6">
207
- <h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-3">Parameters</h3>
208
- <div className="border border-border overflow-hidden">
209
- <table className="w-full text-xs">
210
- <thead>
211
- <tr className="border-b border-border bg-muted/40 text-muted-foreground">
212
- <th className="px-4 py-2 text-left font-medium">Name</th>
213
- <th className="px-4 py-2 text-left font-medium w-20">Type</th>
214
- <th className="px-4 py-2 text-left font-medium w-16">Required</th>
215
- <th className="px-4 py-2 text-left font-medium w-24">Default</th>
216
- <th className="px-4 py-2 text-left font-medium hidden sm:table-cell">Description</th>
217
- </tr>
218
- </thead>
219
- <tbody className="font-mono">
220
- {params.map(p => (
221
- <tr key={p.name} className="border-b border-border last:border-0">
222
- <td className="px-4 py-2.5 font-medium">
223
- {p.name}
224
- {p.enum && (
225
- <span className="text-muted-foreground/40 font-normal ml-1">
226
- [{p.enum.join(", ")}]
227
- </span>
228
- )}
229
- </td>
230
- <td className="px-4 py-2.5 text-muted-foreground">{p.type ?? "string"}</td>
231
- <td className="px-4 py-2.5">
232
- {p.required ? <span className="text-red-400">yes</span> : <span className="text-muted-foreground/30">no</span>}
233
- </td>
234
- <td className="px-4 py-2.5 text-muted-foreground/60">
235
- {p.default !== undefined ? String(p.default) : "\u2014"}
236
- </td>
237
- <td className="px-4 py-2.5 text-muted-foreground font-sans hidden sm:table-cell">{p.description ?? "\u2014"}</td>
238
- </tr>
239
- ))}
240
- </tbody>
241
- </table>
242
- </div>
243
- </section>
244
- )}
245
-
246
- {/* Task view */}
247
- <section className="mt-6">
248
- <div className="flex items-center justify-between mb-3">
249
- <h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">Mission template</h3>
250
- <div className="flex items-center gap-1 rounded-lg bg-muted/50 p-1">
251
- {([
252
- { key: "graph" as const, icon: GitBranch, label: "Graph" },
253
- { key: "tasks" as const, icon: List, label: "Tasks" },
254
- { key: "json" as const, icon: Braces, label: "JSON" },
255
- ]).map(tab => (
256
- <button
257
- key={tab.key}
258
- onClick={() => setView(tab.key)}
259
- className={`inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-xs font-medium transition-colors ${
260
- view === tab.key ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"
261
- }`}
262
- >
263
- <tab.icon className="h-3.5 w-3.5" />
264
- {tab.label}
265
- </button>
266
- ))}
267
- </div>
268
- </div>
269
-
270
- {view === "graph" ? (
271
- <MissionGraph tasks={fakeTasks as unknown as Task[]} projectId={id} readonly />
272
- ) : view === "json" ? (
273
- <div className="border border-border bg-card overflow-hidden">
274
- <div className="flex items-center justify-between px-4 py-2 border-b border-border bg-muted/40">
275
- <span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">Playbook JSON</span>
276
- <button
277
- onClick={() => {
278
- navigator.clipboard.writeText(JSON.stringify(playbook, null, 2));
279
- setCopied(true);
280
- setTimeout(() => setCopied(false), 2000);
281
- }}
282
- className="inline-flex items-center gap-1.5 text-[10px] text-muted-foreground hover:text-foreground transition-colors"
283
- >
284
- {copied ? <Check className="h-3 w-3 text-emerald-500" /> : <Copy className="h-3 w-3" />}
285
- {copied ? "Copied" : "Copy"}
286
- </button>
287
- </div>
288
- <pre
289
- className="p-4 text-xs font-mono overflow-y-auto max-h-[560px] whitespace-pre-wrap break-all"
290
- dangerouslySetInnerHTML={{ __html: highlightJson(JSON.stringify(playbook, null, 2)) }}
291
- />
292
- </div>
293
- ) : (
294
- <div className="border border-border overflow-hidden">
295
- <table className="w-full text-xs">
296
- <thead>
297
- <tr className="border-b border-border bg-muted/40 text-muted-foreground">
298
- <th className="px-4 py-2.5 text-left font-medium w-8">#</th>
299
- <th className="px-4 py-2.5 text-left font-medium">Task</th>
300
- <th className="px-4 py-2.5 text-left font-medium w-24">Agent</th>
301
- <th className="px-4 py-2.5 text-left font-medium hidden sm:table-cell">Dependencies</th>
302
- </tr>
303
- </thead>
304
- <tbody className="font-mono">
305
- {tasks.map((task, i) => (
306
- <tr key={i} className="border-b border-border last:border-0">
307
- <td className="px-4 py-3 text-muted-foreground/40">{i + 1}</td>
308
- <td className="px-4 py-3">
309
- <span className="font-medium">{task.title}</span>
310
- {task.description && (
311
- <p className="text-[10px] text-muted-foreground mt-0.5 font-sans">{task.description}</p>
312
- )}
313
- </td>
314
- <td className="px-4 py-3 text-muted-foreground">{task.assignTo}</td>
315
- <td className="px-4 py-3 text-muted-foreground/50 hidden sm:table-cell">
316
- {task.dependsOn?.join(", ") ?? "\u2014"}
317
- </td>
318
- </tr>
319
- ))}
320
- </tbody>
321
- </table>
322
- </div>
323
- )}
324
- </section>
325
-
326
- {/* Run dialog */}
327
- <Dialog open={showRun} onOpenChange={setShowRun}>
328
- <DialogContent showCloseButton={false}>
329
- <DialogHeader>
330
- <div className="flex items-center gap-2">
331
- <Play className="h-4 w-4" />
332
- <DialogTitle>Run {playbook.name}</DialogTitle>
333
- </div>
334
- <DialogDescription>{playbook.description}</DialogDescription>
335
- </DialogHeader>
336
-
337
- {params.length === 0 ? (
338
- <p className="text-xs text-muted-foreground py-2">This playbook takes no parameters.</p>
339
- ) : (
340
- <div className="space-y-3 py-2">
341
- {params.map(p => (
342
- <div key={p.name}>
343
- <label className="flex items-center gap-1 text-[10px] uppercase tracking-wider text-muted-foreground font-medium mb-1">
344
- {p.name}
345
- {p.required && <span className="text-red-400">*</span>}
346
- <span className="text-muted-foreground/40 normal-case tracking-normal ml-1">{p.type ?? "string"}</span>
347
- </label>
348
- {p.type === "boolean" ? (
349
- <select
350
- value={paramValues[p.name] ?? ""}
351
- onChange={(e) => setParamValues(prev => ({ ...prev, [p.name]: e.target.value }))}
352
- className="w-full h-8 rounded-md border border-border bg-background px-3 text-xs font-mono focus:border-foreground/30 focus:outline-none"
353
- >
354
- <option value="">Select...</option>
355
- <option value="true">true</option>
356
- <option value="false">false</option>
357
- </select>
358
- ) : p.enum && p.enum.length > 0 ? (
359
- <select
360
- value={paramValues[p.name] ?? ""}
361
- onChange={(e) => setParamValues(prev => ({ ...prev, [p.name]: e.target.value }))}
362
- className="w-full h-8 rounded-md border border-border bg-background px-3 text-xs font-mono focus:border-foreground/30 focus:outline-none"
363
- >
364
- <option value="">Select...</option>
365
- {p.enum.map(v => (
366
- <option key={String(v)} value={String(v)}>{String(v)}</option>
367
- ))}
368
- </select>
369
- ) : (
370
- <input
371
- type={p.type === "number" ? "number" : "text"}
372
- value={paramValues[p.name] ?? ""}
373
- onChange={(e) => setParamValues(prev => ({ ...prev, [p.name]: e.target.value }))}
374
- placeholder={p.default !== undefined ? `Default: ${p.default}` : p.description ?? p.name}
375
- className="w-full h-8 rounded-md border border-border bg-transparent px-3 text-xs font-mono placeholder:text-muted-foreground/30 focus:border-foreground/30 focus:outline-none"
376
- />
377
- )}
378
- {p.description && <p className="text-[10px] text-muted-foreground mt-0.5">{p.description}</p>}
379
- </div>
380
- ))}
381
- </div>
382
- )}
383
-
384
- <DialogFooter>
385
- {missingRequired.length > 0 && (
386
- <span className="text-[10px] text-red-400 mr-auto">
387
- {missingRequired.length} required param{missingRequired.length > 1 ? "s" : ""} missing
388
- </span>
389
- )}
390
- <DialogClose render={<button className="px-4 py-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors" />}>
391
- Cancel
392
- </DialogClose>
393
- <button
394
- onClick={handleRun}
395
- disabled={missingRequired.length > 0 || runMutation.isPending}
396
- className="inline-flex items-center gap-2 bg-foreground text-background px-4 py-1.5 text-xs font-medium transition-all hover:opacity-90 disabled:opacity-30 rounded-md"
397
- >
398
- {runMutation.isPending ? <Loader2 className="h-3 w-3 animate-spin" /> : <Play className="h-3 w-3" />}
399
- Run
400
- </button>
401
- </DialogFooter>
402
- </DialogContent>
403
- </Dialog>
404
-
405
- {/* Delete dialog */}
406
- <Dialog open={showDelete} onOpenChange={setShowDelete}>
407
- <DialogContent showCloseButton={false}>
408
- <DialogHeader>
409
- <div className="flex items-center gap-2">
410
- <AlertTriangle className="h-4 w-4 text-destructive" />
411
- <DialogTitle>Delete playbook</DialogTitle>
412
- </div>
413
- <DialogDescription>
414
- Are you sure you want to delete <span className="font-mono font-medium text-foreground">{playbook.name}</span>? This cannot be undone.
415
- </DialogDescription>
416
- </DialogHeader>
417
- <DialogFooter>
418
- <DialogClose render={<button className="px-4 py-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors" />}>
419
- Cancel
420
- </DialogClose>
421
- <button
422
- onClick={() => deleteMutation.mutate()}
423
- disabled={deleteMutation.isPending}
424
- className="inline-flex items-center gap-2 bg-destructive text-destructive-foreground px-4 py-1.5 text-xs font-medium transition-all hover:opacity-90 disabled:opacity-50 rounded-md"
425
- >
426
- {deleteMutation.isPending ? <Loader2 className="h-3 w-3 animate-spin" /> : "Delete"}
427
- </button>
428
- </DialogFooter>
429
- </DialogContent>
430
- </Dialog>
431
- </div>
432
- );
433
- }
@@ -1,5 +0,0 @@
1
- import PlaybooksView from "./view";
2
-
3
- export default function PlaybooksPage() {
4
- return <PlaybooksView />;
5
- }