@agentprojectcontext/apx 1.55.1 → 1.56.0
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/package.json +1 -1
- package/src/interfaces/web/dist/assets/{index-BwnMDZaD.css → index-CAUezTBY.css} +1 -1
- package/src/interfaces/web/dist/assets/index-nL0K1Vt_.js +651 -0
- package/src/interfaces/web/dist/assets/index-nL0K1Vt_.js.map +1 -0
- package/src/interfaces/web/dist/index.html +2 -2
- package/src/interfaces/web/src/App.tsx +1 -0
- package/src/interfaces/web/src/components/chat/ChatList.tsx +6 -3
- package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +24 -15
- package/src/interfaces/web/src/hooks/useChat.ts +22 -0
- package/src/interfaces/web/src/i18n/en.ts +7 -0
- package/src/interfaces/web/src/i18n/es.ts +7 -0
- package/src/interfaces/web/src/screens/ProjectScreen.tsx +15 -11
- package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +24 -0
- package/src/interfaces/web/src/screens/project/ArtifactsTab.tsx +32 -0
- package/src/interfaces/web/src/screens/project/ChatTab.tsx +5 -2
- package/src/interfaces/web/src/screens/project/Overview.tsx +4 -2
- package/src/interfaces/web/dist/assets/index-BxS9xYHB.js +0 -651
- package/src/interfaces/web/dist/assets/index-BxS9xYHB.js.map +0 -1
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
<link rel="apple-touch-icon" href="/favicon/dark/apple-touch-icon.png" media="(prefers-color-scheme: dark)" />
|
|
19
19
|
<link rel="manifest" href="/favicon/white/site.webmanifest" media="(prefers-color-scheme: light)" />
|
|
20
20
|
<link rel="manifest" href="/favicon/dark/site.webmanifest" media="(prefers-color-scheme: dark)" />
|
|
21
|
-
<script type="module" crossorigin src="/assets/index-
|
|
22
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
21
|
+
<script type="module" crossorigin src="/assets/index-nL0K1Vt_.js"></script>
|
|
22
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CAUezTBY.css">
|
|
23
23
|
</head>
|
|
24
24
|
<body class="bg-background text-foreground antialiased">
|
|
25
25
|
<div id="root"></div>
|
|
@@ -235,6 +235,7 @@ function projectLabel(key?: string) {
|
|
|
235
235
|
case "routines": return t("project.nav.routines");
|
|
236
236
|
case "tasks": return t("project.nav.tasks");
|
|
237
237
|
case "mcps": return t("project.nav.mcps");
|
|
238
|
+
case "artifacts": return t("project.nav.artifacts");
|
|
238
239
|
case "config": return t("project.nav.config");
|
|
239
240
|
case "workspaces": return t("base.workspaces_title");
|
|
240
241
|
case "models": return t("settings.tabs.engines");
|
|
@@ -126,10 +126,13 @@ export function ChatList({
|
|
|
126
126
|
const [collapsed, setCollapsed] = useState<Partial<Record<ChannelGroupKey, boolean>>>({});
|
|
127
127
|
const [byAgent, setByAgent] = useState<Record<string, ConversationListEntry[]>>({});
|
|
128
128
|
|
|
129
|
-
// Super-agent channel threads (telegram, web quick-chat, desktop …) from
|
|
130
|
-
// global message ledger
|
|
129
|
+
// Super-agent channel threads (telegram, web quick-chat, desktop …) come from
|
|
130
|
+
// the global message ledger, which is daemon-level and NOT project-scoped.
|
|
131
|
+
// Only surface them in the Base workspace (pid "0"); inside a real project the
|
|
132
|
+
// sidebar shows just that project's own agent conversations.
|
|
133
|
+
const isBase = String(pid) === "0";
|
|
131
134
|
const threadsQ = useSWR(
|
|
132
|
-
`/projects/${pid}/super-agent/threads
|
|
135
|
+
isBase ? `/projects/${pid}/super-agent/threads` : null,
|
|
133
136
|
() => Conversations.threads(pid),
|
|
134
137
|
{ revalidateOnFocus: false },
|
|
135
138
|
);
|
|
@@ -197,27 +197,36 @@ function ArtifactRow({
|
|
|
197
197
|
</DialogContent>
|
|
198
198
|
</Dialog>
|
|
199
199
|
|
|
200
|
-
{/* Editar — opens as a file tab in the main panel
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
200
|
+
{/* Editar — opens as a file tab in the main panel. Only available when
|
|
201
|
+
a host editor (the Code screen) is wired in. */}
|
|
202
|
+
{onEditArtifact && (
|
|
203
|
+
<Tip content={t("code_module.artifacts_edit")}>
|
|
204
|
+
<button
|
|
205
|
+
type="button"
|
|
206
|
+
onClick={() => onEditArtifact(entry.name)}
|
|
207
|
+
className="inline-flex items-center gap-1 rounded px-1.5 py-1 text-[10px] font-medium bg-violet-500/15 text-violet-700 hover:bg-violet-500/25 dark:text-violet-300"
|
|
208
|
+
>
|
|
209
|
+
<SquarePen className="size-3" />
|
|
210
|
+
{t("modules_ui.code_artifact_edit_short")}
|
|
211
|
+
</button>
|
|
212
|
+
</Tip>
|
|
213
|
+
)}
|
|
211
214
|
|
|
212
|
-
{/* Run button
|
|
215
|
+
{/* Run button — hand off to the terminal when there is one (Code
|
|
216
|
+
screen), otherwise execute in place and show the captured output. */}
|
|
213
217
|
{looksRunnable && (
|
|
214
218
|
<Tip content={t("code_module.artifacts_run")}>
|
|
215
219
|
<button
|
|
216
220
|
type="button"
|
|
217
|
-
|
|
218
|
-
|
|
221
|
+
disabled={running}
|
|
222
|
+
onClick={() =>
|
|
223
|
+
onRunInTerminal
|
|
224
|
+
? onRunInTerminal(`apx artifact run ${entry.name}`)
|
|
225
|
+
: void run()
|
|
226
|
+
}
|
|
227
|
+
className="inline-flex items-center gap-1 rounded px-1.5 py-1 text-[10px] font-medium bg-emerald-500/15 text-emerald-700 hover:bg-emerald-500/25 disabled:opacity-60 dark:text-emerald-300"
|
|
219
228
|
>
|
|
220
|
-
<Play className="size-3" />
|
|
229
|
+
{running ? <Spinner size={10} /> : <Play className="size-3" />}
|
|
221
230
|
{t("code_module.artifacts_run")}
|
|
222
231
|
</button>
|
|
223
232
|
</Tip>
|
|
@@ -250,6 +250,11 @@ export function useChat(pid: string, onError?: (msg: string) => void): UseChatRe
|
|
|
250
250
|
const [conversationId, setConversationId] = useState<string | undefined>(undefined);
|
|
251
251
|
const abortRef = useRef<AbortController | null>(null);
|
|
252
252
|
const convoRef = useRef<string | undefined>(undefined);
|
|
253
|
+
// Monotonic token guarding async history loads. Every load()/loadThread()/
|
|
254
|
+
// clear() bumps it; a load only applies its result if it's still the latest.
|
|
255
|
+
// Without this, clicking chat A then B could land A's (slower) response last
|
|
256
|
+
// and paint A's messages under B's header.
|
|
257
|
+
const loadSeqRef = useRef(0);
|
|
253
258
|
|
|
254
259
|
// Mutate the trailing assistant turn in place.
|
|
255
260
|
const patchLast = useCallback((fn: (m: ChatMsg) => ChatMsg) => {
|
|
@@ -348,6 +353,7 @@ export function useChat(pid: string, onError?: (msg: string) => void): UseChatRe
|
|
|
348
353
|
const stop = useCallback(() => abortRef.current?.abort(), []);
|
|
349
354
|
const clear = useCallback(() => {
|
|
350
355
|
if (streaming) return;
|
|
356
|
+
loadSeqRef.current++; // cancel any in-flight history load
|
|
351
357
|
convoRef.current = undefined;
|
|
352
358
|
setConversationId(undefined);
|
|
353
359
|
setMsgs([]);
|
|
@@ -356,8 +362,13 @@ export function useChat(pid: string, onError?: (msg: string) => void): UseChatRe
|
|
|
356
362
|
const load = useCallback(
|
|
357
363
|
async (agentSlug: string, conversationId: string) => {
|
|
358
364
|
if (streaming) return;
|
|
365
|
+
const seq = ++loadSeqRef.current;
|
|
366
|
+
// Blank the pane up front so it never shows the previous chat under the
|
|
367
|
+
// new header while the fetch is in flight.
|
|
368
|
+
setMsgs([]);
|
|
359
369
|
try {
|
|
360
370
|
const detail = await Conversations.get(pid, agentSlug, conversationId);
|
|
371
|
+
if (seq !== loadSeqRef.current) return; // superseded by a newer pick
|
|
361
372
|
const loaded: ChatMsg[] = (detail.messages ?? [])
|
|
362
373
|
.filter((m) => m.role === "user" || m.role === "assistant")
|
|
363
374
|
.map((m) => ({
|
|
@@ -369,6 +380,10 @@ export function useChat(pid: string, onError?: (msg: string) => void): UseChatRe
|
|
|
369
380
|
setConversationId(conversationId);
|
|
370
381
|
setMsgs(loaded);
|
|
371
382
|
} catch (e) {
|
|
383
|
+
if (seq !== loadSeqRef.current) return;
|
|
384
|
+
convoRef.current = undefined;
|
|
385
|
+
setConversationId(undefined);
|
|
386
|
+
setMsgs([]);
|
|
372
387
|
onError?.((e as Error)?.message || t("shared_ui.err_load_conversation"));
|
|
373
388
|
}
|
|
374
389
|
},
|
|
@@ -378,8 +393,11 @@ export function useChat(pid: string, onError?: (msg: string) => void): UseChatRe
|
|
|
378
393
|
const loadThread = useCallback(
|
|
379
394
|
async (channel: string, threadId: string) => {
|
|
380
395
|
if (streaming) return;
|
|
396
|
+
const seq = ++loadSeqRef.current;
|
|
397
|
+
setMsgs([]);
|
|
381
398
|
try {
|
|
382
399
|
const detail = await Conversations.thread(pid, channel, threadId);
|
|
400
|
+
if (seq !== loadSeqRef.current) return; // superseded by a newer pick
|
|
383
401
|
const loaded: ChatMsg[] = (detail.messages ?? [])
|
|
384
402
|
.filter((m) => m.role === "user" || m.role === "assistant")
|
|
385
403
|
.map((m) => ({
|
|
@@ -393,6 +411,10 @@ export function useChat(pid: string, onError?: (msg: string) => void): UseChatRe
|
|
|
393
411
|
setConversationId(undefined);
|
|
394
412
|
setMsgs(loaded);
|
|
395
413
|
} catch (e) {
|
|
414
|
+
if (seq !== loadSeqRef.current) return;
|
|
415
|
+
convoRef.current = undefined;
|
|
416
|
+
setConversationId(undefined);
|
|
417
|
+
setMsgs([]);
|
|
396
418
|
onError?.((e as Error)?.message || t("shared_ui.err_load_conversation"));
|
|
397
419
|
}
|
|
398
420
|
},
|
|
@@ -288,6 +288,7 @@ export const en = {
|
|
|
288
288
|
routines: "Routines",
|
|
289
289
|
tasks: "Tasks",
|
|
290
290
|
mcps: "MCPs",
|
|
291
|
+
artifacts: "Artifacts",
|
|
291
292
|
vars: "Variables",
|
|
292
293
|
logs: "Logs",
|
|
293
294
|
memories: "Memories",
|
|
@@ -305,10 +306,16 @@ export const en = {
|
|
|
305
306
|
routines: "Routines",
|
|
306
307
|
agents: "Agents",
|
|
307
308
|
mcps: "MCPs",
|
|
309
|
+
artifacts: "Artifacts",
|
|
308
310
|
chat: "Chat (super-agent)",
|
|
309
311
|
chat_value: "open",
|
|
310
312
|
},
|
|
311
313
|
|
|
314
|
+
artifacts: {
|
|
315
|
+
title: "Artifacts",
|
|
316
|
+
subtitle: "Reusable scripts and files stored under the project. Agents create them; you can view, run, rename or delete them.",
|
|
317
|
+
},
|
|
318
|
+
|
|
312
319
|
chat: {
|
|
313
320
|
title: "Chat with agent",
|
|
314
321
|
subtitle: "Direct conversations with project agents. The super-agent does not intervene.",
|
|
@@ -289,6 +289,7 @@ export const es = {
|
|
|
289
289
|
routines: "Rutinas",
|
|
290
290
|
tasks: "Tasks",
|
|
291
291
|
mcps: "MCPs",
|
|
292
|
+
artifacts: "Artifacts",
|
|
292
293
|
vars: "Variables",
|
|
293
294
|
logs: "Logs",
|
|
294
295
|
memories: "Memorias",
|
|
@@ -306,10 +307,16 @@ export const es = {
|
|
|
306
307
|
routines: "Rutinas",
|
|
307
308
|
agents: "Agents",
|
|
308
309
|
mcps: "MCPs",
|
|
310
|
+
artifacts: "Artifacts",
|
|
309
311
|
chat: "Chat (super-agent)",
|
|
310
312
|
chat_value: "abrir",
|
|
311
313
|
},
|
|
312
314
|
|
|
315
|
+
artifacts: {
|
|
316
|
+
title: "Artifacts",
|
|
317
|
+
subtitle: "Scripts y archivos reutilizables guardados en el proyecto. Los crean los agentes; podés verlos, ejecutarlos, renombrarlos o eliminarlos.",
|
|
318
|
+
},
|
|
319
|
+
|
|
313
320
|
chat: {
|
|
314
321
|
title: "Chat con agente",
|
|
315
322
|
subtitle: "Chat directo con el agente del proyecto.",
|
|
@@ -3,7 +3,7 @@ import { useParams, Routes, Route, Navigate, useLocation, useNavigate } from "re
|
|
|
3
3
|
import {
|
|
4
4
|
Bot, Heart, Zap, Puzzle, FolderKanban, Settings,
|
|
5
5
|
MessagesSquare, Send, KeyRound,
|
|
6
|
-
LayoutDashboard, Boxes, Cpu, ScrollText, History, Brain,
|
|
6
|
+
LayoutDashboard, Boxes, Cpu, ScrollText, History, Brain, FileCode2,
|
|
7
7
|
} from "lucide-react";
|
|
8
8
|
import { useNavCollapse, type TabSection } from "../components/common/TabNav";
|
|
9
9
|
import { TabLayout } from "../components/common/TabLayout";
|
|
@@ -28,11 +28,12 @@ import { VarsTab } from "./project/VarsTab";
|
|
|
28
28
|
import { ChatTab } from "./project/ChatTab";
|
|
29
29
|
import { TelegramTab } from "./project/TelegramTab";
|
|
30
30
|
import { MemoriesTab } from "./project/MemoriesTab";
|
|
31
|
+
import { ArtifactsTab } from "./project/ArtifactsTab";
|
|
31
32
|
import { AgentDetailScreen } from "./project/AgentDetailScreen";
|
|
32
33
|
|
|
33
34
|
type NavKey =
|
|
34
35
|
| "" | "chat" | "config" | "telegram"
|
|
35
|
-
| "agents" | "routines" | "tasks" | "mcps" | "vars" | "logs" | "memories";
|
|
36
|
+
| "agents" | "routines" | "tasks" | "mcps" | "vars" | "logs" | "memories" | "artifacts";
|
|
36
37
|
|
|
37
38
|
export function ProjectScreen() {
|
|
38
39
|
const navigate = useNavigate();
|
|
@@ -69,10 +70,11 @@ export function ProjectScreen() {
|
|
|
69
70
|
items: [
|
|
70
71
|
{ key: "agents", label: t("project.nav.agents"), icon: Bot },
|
|
71
72
|
{ key: "memories", label: t("project.nav.memories"), icon: Brain },
|
|
72
|
-
{ key: "routines",
|
|
73
|
-
{ key: "mcps",
|
|
74
|
-
{ key: "
|
|
75
|
-
{ key: "
|
|
73
|
+
{ key: "routines", label: t("project.nav.routines"), icon: Heart },
|
|
74
|
+
{ key: "mcps", label: t("project.nav.mcps"), icon: Puzzle },
|
|
75
|
+
{ key: "artifacts", label: t("project.nav.artifacts"), icon: FileCode2 },
|
|
76
|
+
{ key: "vars", label: t("project.nav.vars"), icon: KeyRound },
|
|
77
|
+
{ key: "config", label: t("project.nav.config"), icon: Settings },
|
|
76
78
|
],
|
|
77
79
|
},
|
|
78
80
|
];
|
|
@@ -91,11 +93,12 @@ export function ProjectScreen() {
|
|
|
91
93
|
{
|
|
92
94
|
title: t("project.sections.automation"),
|
|
93
95
|
items: [
|
|
94
|
-
{ key: "routines",
|
|
95
|
-
{ key: "tasks",
|
|
96
|
-
{ key: "mcps",
|
|
97
|
-
{ key: "
|
|
98
|
-
{ key: "
|
|
96
|
+
{ key: "routines", label: t("project.nav.routines"), icon: Heart },
|
|
97
|
+
{ key: "tasks", label: t("project.nav.tasks"), icon: Zap },
|
|
98
|
+
{ key: "mcps", label: t("project.nav.mcps"), icon: Puzzle },
|
|
99
|
+
{ key: "artifacts", label: t("project.nav.artifacts"), icon: FileCode2 },
|
|
100
|
+
{ key: "vars", label: t("project.nav.vars"), icon: KeyRound },
|
|
101
|
+
{ key: "logs", label: t("project.nav.logs"), icon: ScrollText },
|
|
99
102
|
],
|
|
100
103
|
},
|
|
101
104
|
{
|
|
@@ -156,6 +159,7 @@ export function ProjectScreen() {
|
|
|
156
159
|
<Route path="routines" element={<RoutinesTab pid={pid} />} />
|
|
157
160
|
<Route path="tasks" element={isBase ? <GlobalTasksTab /> : <TasksTab pid={pid} />} />
|
|
158
161
|
<Route path="mcps" element={<McpsTab pid={pid} />} />
|
|
162
|
+
<Route path="artifacts" element={<ArtifactsTab pid={pid} />} />
|
|
159
163
|
<Route path="vars" element={<VarsTab pid={pid} />} />
|
|
160
164
|
<Route path="threads" element={<Navigate to={`/p/${pid}/chat`} replace />} />
|
|
161
165
|
<Route path="chat" element={<ChatTab pid={pid} />} />
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
+
import { useSearchParams } from "react-router-dom";
|
|
2
3
|
import useSWR from "swr";
|
|
3
4
|
import { Bot, FolderTree, MessageSquare, PanelLeft, PanelRight, Terminal, X } from "lucide-react";
|
|
4
5
|
import { Group as PanelGroup, Panel, Separator as PanelResizeHandle } from "react-resizable-panels";
|
|
@@ -64,6 +65,8 @@ export function CodeScreen() {
|
|
|
64
65
|
const [termInitCmd, setTermInitCmd] = useState("");
|
|
65
66
|
const [worktreeOpen, setWorktreeOpen] = useState(false);
|
|
66
67
|
const abortRef = useRef<AbortController | null>(null);
|
|
68
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
69
|
+
const deepLinkDone = useRef(false);
|
|
67
70
|
|
|
68
71
|
// Open file tabs. `artifactName` marks an artifact opened for editing;
|
|
69
72
|
// saves route through Artifacts.write instead of being read-only.
|
|
@@ -346,6 +349,27 @@ export function CodeScreen() {
|
|
|
346
349
|
[pid],
|
|
347
350
|
);
|
|
348
351
|
|
|
352
|
+
// Deep-link from the project Artifacts tab (/m/code?pid=..&cmd=.. or &edit=..).
|
|
353
|
+
// Select the requested project, then prefill the terminal with the artifact
|
|
354
|
+
// command (so args like a URL can be typed) or open the file for editing.
|
|
355
|
+
useEffect(() => {
|
|
356
|
+
if (deepLinkDone.current) return;
|
|
357
|
+
const wantPid = searchParams.get("pid");
|
|
358
|
+
const cmd = searchParams.get("cmd");
|
|
359
|
+
const edit = searchParams.get("edit");
|
|
360
|
+
if (!wantPid || (!cmd && !edit)) return;
|
|
361
|
+
// Wait until the requested project is active so the command/edit targets it.
|
|
362
|
+
if (String(pid) !== String(wantPid)) {
|
|
363
|
+
setPid(String(wantPid));
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
deepLinkDone.current = true;
|
|
367
|
+
if (edit) openArtifact(edit);
|
|
368
|
+
if (cmd) runInTerminal(cmd.endsWith(" ") ? cmd : cmd + " ");
|
|
369
|
+
// Clear params so a refresh/back doesn't retrigger the handoff.
|
|
370
|
+
setSearchParams({}, { replace: true });
|
|
371
|
+
}, [searchParams, pid, openArtifact, runInTerminal, setSearchParams]);
|
|
372
|
+
|
|
349
373
|
const saveOpenFile = useCallback(
|
|
350
374
|
async (path: string, content: string) => {
|
|
351
375
|
const file = openFiles.find((f) => f.path === path);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useNavigate } from "react-router-dom";
|
|
2
|
+
import { Section } from "../../components/Section";
|
|
3
|
+
import { CodeArtifactsTab } from "../../components/code/CodeArtifactsTab";
|
|
4
|
+
import { t } from "../../i18n";
|
|
5
|
+
|
|
6
|
+
// Project-level view of the artifacts stored under <project>/artifacts/. Reuses
|
|
7
|
+
// the same list/row UI as the Code screen. Run and Edit hand off to the Code
|
|
8
|
+
// module — the terminal there lets you pass args (e.g. a URL) and the file
|
|
9
|
+
// editor lets you edit — instead of running headless in place.
|
|
10
|
+
export function ArtifactsTab({ pid }: { pid: string }) {
|
|
11
|
+
const navigate = useNavigate();
|
|
12
|
+
|
|
13
|
+
const toCode = (params: Record<string, string>) => {
|
|
14
|
+
const qs = new URLSearchParams({ pid, ...params }).toString();
|
|
15
|
+
navigate(`/m/code?${qs}`);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<Section
|
|
20
|
+
title={t("project.artifacts.title")}
|
|
21
|
+
description={t("project.artifacts.subtitle")}
|
|
22
|
+
fullHeight
|
|
23
|
+
className="min-h-[24rem]"
|
|
24
|
+
>
|
|
25
|
+
<CodeArtifactsTab
|
|
26
|
+
pid={pid}
|
|
27
|
+
onRunInTerminal={(cmd) => toCode({ cmd })}
|
|
28
|
+
onEditArtifact={(name) => toCode({ edit: name })}
|
|
29
|
+
/>
|
|
30
|
+
</Section>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -65,8 +65,11 @@ export function ChatTab({ pid }: { pid: string }) {
|
|
|
65
65
|
} else if (selected.kind === "thread") {
|
|
66
66
|
void loadThread(selected.channel, selected.threadId);
|
|
67
67
|
} else {
|
|
68
|
-
//
|
|
69
|
-
if (conversationId)
|
|
68
|
+
// Live session selected → always start from a clean slate. (Threads leave
|
|
69
|
+
// conversationId undefined, so an `if (conversationId)` guard would skip
|
|
70
|
+
// clearing and the previous chat's messages would linger under the new
|
|
71
|
+
// header — the "title changes but content stays" bug.)
|
|
72
|
+
clear();
|
|
70
73
|
}
|
|
71
74
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
72
75
|
}, [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import useSWR from "swr";
|
|
2
2
|
import { NavLink } from "react-router-dom";
|
|
3
|
-
import { Bot, Heart, MessagesSquare, Puzzle, Zap } from "lucide-react";
|
|
4
|
-
import { Agents, Mcps, Routines, Tasks } from "../../lib/api";
|
|
3
|
+
import { Bot, FileCode2, Heart, MessagesSquare, Puzzle, Zap } from "lucide-react";
|
|
4
|
+
import { Agents, Artifacts, Mcps, Routines, Tasks } from "../../lib/api";
|
|
5
5
|
import { t } from "../../i18n";
|
|
6
6
|
|
|
7
7
|
export function Overview({ pid }: { pid: string }) {
|
|
@@ -9,12 +9,14 @@ export function Overview({ pid }: { pid: string }) {
|
|
|
9
9
|
const routines = useSWR(`/projects/${pid}/routines`, () => Routines.list(pid));
|
|
10
10
|
const agents = useSWR(`/projects/${pid}/agents`, () => Agents.list(pid));
|
|
11
11
|
const mcps = useSWR(`/projects/${pid}/mcps`, () => Mcps.list(pid));
|
|
12
|
+
const artifacts = useSWR(`/projects/${pid}/artifacts`, () => Artifacts.list(pid));
|
|
12
13
|
return (
|
|
13
14
|
<div className="grid grid-cols-2 gap-4 md:grid-cols-3">
|
|
14
15
|
<Card title={t("project.overview.tasks_open")} value={tasks.data?.length ?? "…"} href={`/p/${pid}/tasks`} icon={Zap} />
|
|
15
16
|
<Card title={t("project.overview.routines")} value={routines.data?.length ?? "…"} href={`/p/${pid}/routines`} icon={Heart} />
|
|
16
17
|
<Card title={t("project.overview.agents")} value={agents.data?.length ?? "…"} href={`/p/${pid}/agents`} icon={Bot} />
|
|
17
18
|
<Card title={t("project.overview.mcps")} value={mcps.data?.length ?? "…"} href={`/p/${pid}/mcps`} icon={Puzzle} />
|
|
19
|
+
<Card title={t("project.overview.artifacts")} value={artifacts.data?.length ?? "…"} href={`/p/${pid}/artifacts`} icon={FileCode2} />
|
|
18
20
|
<Card title={t("project.overview.chat")} value={t("project.overview.chat_value")} href={`/p/${pid}/chat`} icon={MessagesSquare} />
|
|
19
21
|
</div>
|
|
20
22
|
);
|