@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,253 +0,0 @@
1
- "use client";
2
-
3
- /**
4
- * CopilotLayout — wraps the dashboard main content and docks a global,
5
- * context-aware Builder panel BESIDE it (push, not overlay): when open the
6
- * content shrinks and the panel takes its place on the right.
7
- *
8
- * The Meta Agent is the same one the Agent Studio uses; here it picks up the
9
- * page CONTEXT (current agent / skill / section) from the route so it acts
10
- * on what you're looking at.
11
- *
12
- * The panel is ALWAYS mounted (it collapses by WIDTH, never unmounts), so
13
- * the conversation survives open/close and navigation between project pages.
14
- * It's keyed by projectId so switching projects starts a fresh session.
15
- *
16
- * Phase 1: additive. It coexists with the Studio's docked builder;
17
- * convergence (one surface) is a later decision.
18
- */
19
-
20
- import {
21
- createContext,
22
- useCallback,
23
- useContext,
24
- useEffect,
25
- useMemo,
26
- useRef,
27
- useState,
28
- type ReactNode,
29
- } from "react";
30
- import { useRouter } from "next/navigation";
31
- import { useQuery, useQueryClient } from "@tanstack/react-query";
32
- import { SquarePen, X } from "lucide-react";
33
- import { BuilderChat } from "../../components/dashboard/builder-chat";
34
- import { useBuilderContext, type NavigateTarget } from "../../lib/builder-context";
35
- import { fetchControlPlane } from "../../lib/data-client";
36
-
37
- const API_URL = process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:4000";
38
-
39
- /** localStorage key for persisted copilot UI preferences. */
40
- const COPILOT_PREFS_KEY = "polpo.copilot.prefs";
41
-
42
- interface CopilotContextValue {
43
- open: boolean;
44
- setOpen: (open: boolean) => void;
45
- toggle: () => void;
46
- }
47
-
48
- const CopilotContext = createContext<CopilotContextValue | null>(null);
49
-
50
- /** Open/close the global Copilot panel from anywhere under the dashboard
51
- * layout (e.g. the Agent Studio "Edit" button). Returns null outside it. */
52
- export function useCopilot(): CopilotContextValue | null {
53
- return useContext(CopilotContext);
54
- }
55
-
56
- /** The Polpo mark — two offset squares (no wordmark). Inherits color. */
57
- function PolpoMark({ className = "" }: { className?: string }) {
58
- return (
59
- <span className={`relative inline-block ${className}`} aria-hidden>
60
- <span className="absolute left-0 top-0 h-[45%] w-[45%] bg-current" />
61
- <span className="absolute bottom-0 right-0 h-[45%] w-[45%] bg-current" />
62
- </span>
63
- );
64
- }
65
-
66
- export function CopilotLayout({ children }: { children: ReactNode }) {
67
- const router = useRouter();
68
- const queryClient = useQueryClient();
69
- const [open, setOpen] = useState(false);
70
- // Bumping this remounts <BuilderChat>, resetting its in-memory state.
71
- const [sessionNonce, setSessionNonce] = useState(0);
72
- const { projectId, context } = useBuilderContext();
73
-
74
- // Persist UI preferences (currently: was the panel open) across reloads.
75
- const prefsHydrated = useRef(false);
76
- useEffect(() => {
77
- if (typeof window === "undefined") return;
78
- try {
79
- const raw = window.localStorage.getItem(COPILOT_PREFS_KEY);
80
- if (raw) {
81
- const prefs = JSON.parse(raw);
82
- if (typeof prefs?.open === "boolean") setOpen(prefs.open);
83
- }
84
- } catch {
85
- /* ignore */
86
- }
87
- }, []);
88
- useEffect(() => {
89
- if (typeof window === "undefined") return;
90
- if (!prefsHydrated.current) {
91
- prefsHydrated.current = true;
92
- return;
93
- }
94
- try {
95
- window.localStorage.setItem(COPILOT_PREFS_KEY, JSON.stringify({ open }));
96
- } catch {
97
- /* ignore */
98
- }
99
- }, [open]);
100
-
101
- const copilotCtx = useMemo<CopilotContextValue>(
102
- () => ({ open, setOpen, toggle: () => setOpen((v) => !v) }),
103
- [open],
104
- );
105
-
106
- // Clear the locally-persisted conversation and start fresh.
107
- const clearConversation = useCallback(() => {
108
- if (projectId && typeof window !== "undefined") {
109
- try {
110
- window.localStorage.removeItem(`polpo.builder.${projectId}`);
111
- } catch {
112
- /* ignore */
113
- }
114
- }
115
- setSessionNonce((n) => n + 1);
116
- }, [projectId]);
117
-
118
- // When the Meta Agent mutates something, refresh only the relevant data:
119
- // router.refresh() re-fetches server components (e.g. the Studio agent
120
- // detail), and we invalidate the matching client queries so lists update
121
- // live too. Keyed by which tool ran.
122
- const handleMutation = useCallback(
123
- (tool: string) => {
124
- router.refresh();
125
- const inv = (key: unknown[]) => queryClient.invalidateQueries({ queryKey: key });
126
- // Tool name is `polpo_<resource>_<action>` — invalidate that resource's
127
- // queries, plus the cross-referenced ones (team ↔ agent membership).
128
- const resource = tool.split("_")[1] ?? "";
129
- inv([resource, projectId]);
130
- if (resource === "agents") inv(["teams", projectId]);
131
- if (resource === "skills" || resource === "teams") inv(["agents", projectId]);
132
- },
133
- [router, queryClient, projectId],
134
- );
135
-
136
- // Client-side navigation requested by the Meta Agent (navigate tool):
137
- // build the URL and push it. The panel is global, so it stays open and
138
- // its context auto-updates to the new page.
139
- const handleNavigate = useCallback(
140
- (t: NavigateTarget) => {
141
- if (!projectId) return;
142
- let url = `/projects/${projectId}`;
143
- if (t.section && t.section !== "overview") url += `/${t.section}`;
144
- if (t.name) url += `/${encodeURIComponent(t.name)}`;
145
- if (t.tab) url += `?tab=${encodeURIComponent(t.tab)}`;
146
- router.push(url);
147
- },
148
- [router, projectId],
149
- );
150
-
151
- // Only meaningful inside a project (the Meta Agent operates on one).
152
- const show = !!projectId;
153
-
154
- // Project name for the context line — shares the ["project", id] query
155
- // with sidebar/top-header (cache hit, no extra fetch).
156
- const { data: project } = useQuery({
157
- queryKey: ["project", projectId],
158
- queryFn: () =>
159
- fetchControlPlane<{ name: string; orgId: string }>(`/v1/projects/${projectId}`),
160
- enabled: show,
161
- staleTime: 60_000,
162
- });
163
-
164
- // Dynamic, human explanation of what the Meta Agent is scoped to —
165
- // always names the specific thing (which agent / skill / project).
166
- const projectName = project?.name ?? "this project";
167
- const contextDetail =
168
- context.type === "agent"
169
- ? `Agent ${context.agentName}`
170
- : context.type === "skill"
171
- ? `Skill ${context.skill}`
172
- : context.type === "section"
173
- ? context.label
174
- : "Overview";
175
- const contextExplain = `${contextDetail} · ${projectName}`;
176
-
177
- return (
178
- <CopilotContext.Provider value={copilotCtx}>
179
- <div className="flex min-h-0 flex-1 overflow-hidden">
180
- <main className="min-w-0 flex-1 overflow-y-auto">{children}</main>
181
-
182
- {show && (
183
- <>
184
- {/* Launcher — Intercom-style, bottom-right; only visible when closed. */}
185
- {!open && (
186
- <button
187
- type="button"
188
- onClick={() => setOpen(true)}
189
- className="group fixed bottom-5 right-5 z-40 inline-flex items-center gap-2.5 border border-border bg-card px-3 py-2.5 shadow-lg transition-colors hover:border-foreground/40 hover:bg-card"
190
- >
191
- <PolpoMark className="h-3.5 w-3.5 text-foreground" />
192
- <span className="text-xs font-semibold text-foreground">Polpo AI</span>
193
- </button>
194
- )}
195
-
196
- {/* Panel — docked beside content; collapses by width, stays mounted. */}
197
- <aside
198
- className={`shrink-0 overflow-hidden border-l border-border transition-[width] duration-200 ease-out ${
199
- open ? "w-[500px]" : "w-0"
200
- }`}
201
- >
202
- <div className="flex h-full w-[500px] flex-col overflow-hidden">
203
- {/* Header — title + dynamic context on one row. The context
204
- names the specific thing the agent acts on. */}
205
- <div className="flex shrink-0 items-center gap-2 border-b border-border px-4 py-3">
206
- <PolpoMark className="h-3.5 w-3.5 shrink-0 text-foreground" />
207
- <span className="shrink-0 text-sm font-semibold text-foreground">Polpo AI</span>
208
- <span className="ml-1 flex min-w-0 items-center gap-1.5 border-l border-border pl-2">
209
- <span className="shrink-0 font-mono text-[10px] font-bold uppercase tracking-[0.14em] text-muted-foreground/55">
210
- Context
211
- </span>
212
- <span className="min-w-0 truncate text-xs text-foreground" title={contextExplain}>
213
- {contextExplain}
214
- </span>
215
- </span>
216
- <button
217
- type="button"
218
- onClick={clearConversation}
219
- aria-label="New conversation"
220
- title="New conversation"
221
- className="ml-auto shrink-0 text-muted-foreground transition-colors hover:text-foreground"
222
- >
223
- <SquarePen className="h-4 w-4" strokeWidth={1.5} />
224
- </button>
225
- <button
226
- type="button"
227
- onClick={() => setOpen(false)}
228
- aria-label="Close"
229
- className="shrink-0 text-muted-foreground transition-colors hover:text-foreground"
230
- >
231
- <X className="h-4 w-4" strokeWidth={1.5} />
232
- </button>
233
- </div>
234
-
235
- <div className="copilot-chat-dense flex min-h-0 flex-1 flex-col overflow-hidden">
236
- <BuilderChat
237
- key={`${projectId ?? "none"}:${sessionNonce}`}
238
- projectId={projectId as string}
239
- apiUrl={API_URL}
240
- context={context}
241
- onMutation={handleMutation}
242
- onNavigate={handleNavigate}
243
- persistKey={`polpo.builder.${projectId}`}
244
- />
245
- </div>
246
- </div>
247
- </aside>
248
- </>
249
- )}
250
- </div>
251
- </CopilotContext.Provider>
252
- );
253
- }
@@ -1,194 +0,0 @@
1
- "use client";
2
-
3
- import { useCallback, useMemo, useState } from "react";
4
- import { CopyCard } from "../../components/dashboard/copy-card";
5
- import { AgentPickerCard } from "../../components/dashboard/agent-picker-card";
6
- import { TabToggle } from "../../components/dashboard/tab-toggle";
7
- import { AgentModelSelector } from "../../components/dashboard/agent-model-selector";
8
- import { PolpoProvider } from "@polpo-ai/react";
9
- import { Chat } from "@polpo-ai/chat";
10
- import type { AgentConfig } from "@polpo-ai/core";
11
-
12
- type SnippetTab = "curl" | "ts" | "playground";
13
-
14
- /**
15
- * SDK snippet panel — agent picker + language toggle (curl / TS /
16
- * Playground) + copy-able code snippet. Shared between ConnectDialog
17
- * SDK tab and OnboardingChecklist "Chat with your agent" step.
18
- *
19
- * When `projectId` and `apiUrl` are provided, a third "Playground"
20
- * tab appears that renders the same <Chat> UI against the session-
21
- * authenticated proxy — the user can fire a real chat completion
22
- * without leaving the panel.
23
- */
24
- export function SdkSnippetPanel({
25
- baseUrl,
26
- agents,
27
- agentConfigs,
28
- defaultAgent,
29
- sdkKey = "$POLPO_API_KEY",
30
- compact = false,
31
- projectId,
32
- apiUrl,
33
- }: {
34
- baseUrl: string;
35
- agents: string[] | null;
36
- /** Full agent configs — enables the Playground tab to show model info
37
- * next to the agent name. When omitted, Playground shows name only. */
38
- agentConfigs?: AgentConfig[];
39
- defaultAgent: string;
40
- sdkKey?: string;
41
- compact?: boolean;
42
- projectId?: string;
43
- apiUrl?: string;
44
- }) {
45
- const [agent, setAgent] = useState(defaultAgent);
46
- const canUsePlayground = !!projectId && !!apiUrl;
47
- const [lang, setLang] = useState<SnippetTab>(
48
- canUsePlayground ? "playground" : "curl",
49
- );
50
-
51
- // If `sdkKey` looks like a real key (starts with sk_), inline it in the
52
- // snippet so a freshly-generated key is immediately copy-pasteable.
53
- // Otherwise fall back to the $POLPO_API_KEY env-var placeholder.
54
- const isRealKey = sdkKey.startsWith("sk_");
55
- const tsKeyExpr = isRealKey ? `"${sdkKey}"` : "process.env.POLPO_API_KEY!";
56
- const curlAuthKey = isRealKey ? sdkKey : "$POLPO_API_KEY";
57
-
58
- const tsSnippet = `import { PolpoClient } from "@polpo-ai/sdk";
59
-
60
- const polpo = new PolpoClient({
61
- apiKey: ${tsKeyExpr},
62
- baseUrl: "${baseUrl}",
63
- });
64
-
65
- const res = await polpo.chatCompletions({
66
- model: "polpo",
67
- agent: "${agent}",
68
- stream: true,
69
- messages: [{ role: "user", content: "Hello" }],
70
- });`;
71
-
72
- const curlPreamble = isRealKey
73
- ? ""
74
- : `# Load your API key from .env.local (created by polpo link)
75
- export POLPO_API_KEY=$(grep POLPO_API_KEY .env.local | cut -d= -f2)
76
-
77
- `;
78
-
79
- const curlSnippet = `${curlPreamble}curl ${baseUrl}/v1/chat/completions \\
80
- -H "Authorization: Bearer ${curlAuthKey}" \\
81
- -H "Content-Type: application/json" \\
82
- -d '{
83
- "model": "polpo",
84
- "agent": "${agent}",
85
- "stream": true,
86
- "messages": [{"role":"user","content":"Hello"}]
87
- }'`;
88
-
89
- const options: { value: SnippetTab; label: string }[] = [
90
- ...(canUsePlayground
91
- ? [{ value: "playground" as const, label: "Playground" }]
92
- : []),
93
- { value: "curl", label: "curl" },
94
- { value: "ts", label: "TypeScript" },
95
- ];
96
-
97
- return (
98
- <div className="flex flex-col gap-3">
99
- <TabToggle value={lang} onChange={setLang} options={options} />
100
-
101
- {!compact && lang !== "playground" && (
102
- <AgentPickerCard
103
- agents={agents}
104
- agent={agent}
105
- onChange={setAgent}
106
- show={agents !== null && agents.length > 0}
107
- />
108
- )}
109
-
110
- {lang === "playground" && canUsePlayground && (
111
- <InlinePlayground
112
- projectId={projectId!}
113
- apiUrl={apiUrl!}
114
- agent={agent}
115
- onAgentChange={setAgent}
116
- agentConfigs={agentConfigs}
117
- />
118
- )}
119
-
120
- {lang !== "playground" && (
121
- <CopyCard
122
- label={lang === "ts" ? "@polpo-ai/sdk" : "curl"}
123
- value={lang === "ts" ? tsSnippet : curlSnippet}
124
- lang={lang}
125
- />
126
- )}
127
- </div>
128
- );
129
- }
130
-
131
- /* ------------------------------------------------------------------ */
132
- /* Inline playground — same wiring as the /playground page, but */
133
- /* constrained to a small panel for use inside onboarding / dialogs. */
134
- /* ------------------------------------------------------------------ */
135
-
136
- function InlinePlayground({
137
- projectId,
138
- apiUrl,
139
- agent,
140
- onAgentChange,
141
- agentConfigs,
142
- }: {
143
- projectId: string;
144
- apiUrl: string;
145
- agent: string;
146
- onAgentChange: (name: string) => void;
147
- agentConfigs?: AgentConfig[];
148
- }) {
149
- const baseUrl = `${apiUrl}/v1/projects/${projectId}/data`;
150
- const [sessionState, setSessionState] = useState<{ agent: string; id: string } | null>(null);
151
- const sessionId = sessionState?.agent === agent ? sessionState.id : undefined;
152
- const setCurrentSessionId = useCallback((id: string) => setSessionState({ agent, id }), [agent]);
153
-
154
- const cookieFetch = useMemo<typeof globalThis.fetch>(
155
- () =>
156
- (async (input, init) => {
157
- const res = await globalThis.fetch(input, {
158
- ...init,
159
- credentials: "include",
160
- });
161
- const id = res.headers.get("x-session-id");
162
- if (id) setCurrentSessionId(id);
163
- return res;
164
- }) as typeof globalThis.fetch,
165
- [setCurrentSessionId],
166
- );
167
-
168
- return (
169
- <div className="flex flex-col gap-2">
170
- {agentConfigs && agentConfigs.length > 0 && (
171
- <div className="flex items-center justify-between">
172
- <span className="text-[10px] font-mono uppercase tracking-[0.18em] text-muted-foreground/60">
173
- Chatting with
174
- </span>
175
- <AgentModelSelector
176
- agents={agentConfigs}
177
- selected={agent}
178
- onSelect={onAgentChange}
179
- />
180
- </div>
181
- )}
182
- <div className="flex h-[420px] flex-col border border-border bg-card">
183
- <PolpoProvider
184
- baseUrl={baseUrl}
185
- apiPrefix="/v1"
186
- fetch={cookieFetch}
187
- autoConnect={false}
188
- >
189
- <Chat key={agent} sessionId={sessionId} agent={agent} onSessionCreated={setCurrentSessionId} className="flex-1" />
190
- </PolpoProvider>
191
- </div>
192
- </div>
193
- );
194
- }
@@ -1,37 +0,0 @@
1
- import type { ReactNode } from "react";
2
-
3
- /**
4
- * Shared section header for the agent tab pages (Capabilities, Tools,
5
- * Skills, …) so every internal section reads the same: a title with an
6
- * optional count, and a one-line muted description. Keeps the tabs
7
- * visually coherent.
8
- */
9
- export function SectionHeader({
10
- title,
11
- count,
12
- description,
13
- }: {
14
- /** Optional — omit when a tab already labels the section (avoids a
15
- * redundant heading). Description-only is rendered then. */
16
- title?: string;
17
- count?: number;
18
- description?: ReactNode;
19
- }) {
20
- return (
21
- <div>
22
- {title && (
23
- <h3 className="text-sm font-semibold">
24
- {title}
25
- {count !== undefined && (
26
- <span className="ml-1 font-mono text-muted-foreground/60">({count})</span>
27
- )}
28
- </h3>
29
- )}
30
- {description && (
31
- <p className={`text-xs text-muted-foreground ${title ? "mt-1" : ""}`}>
32
- {description}
33
- </p>
34
- )}
35
- </div>
36
- );
37
- }