@getcatalystiq/agent-plane-ui 0.1.19 → 0.1.21

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/editor.js CHANGED
@@ -1,380 +1,13 @@
1
- import { Badge, Button, Input, useNavigation, useAgentPlaneClient, useApi, Skeleton, Card, CardHeader, CardTitle, CardContent, Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogBody, FormField, DialogFooter } from './chunk-OOBDCC6Q.js';
2
- import { useState, useEffect, useRef, useMemo, useCallback } from 'react';
1
+ import { useApi, Skeleton, FileTreeEditor, Card, CardHeader, CardTitle, CardContent } from './chunk-CE2RHDPY.js';
2
+ export { FileTreeEditor } from './chunk-CE2RHDPY.js';
3
+ export { AgentIdentityTab } from './chunk-L45SI32F.js';
4
+ import { useNavigation, useAgentPlaneClient, Badge, Button } from './chunk-XFI227OB.js';
5
+ import { useState, useCallback } from 'react';
3
6
  import CodeMirror from '@uiw/react-codemirror';
4
- import { markdown } from '@codemirror/lang-markdown';
5
- import { javascript } from '@codemirror/lang-javascript';
6
7
  import { json } from '@codemirror/lang-json';
7
8
  import { oneDark } from '@codemirror/theme-one-dark';
8
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
9
+ import { jsx, jsxs } from 'react/jsx-runtime';
9
10
 
10
- function getLanguageExtension(filename) {
11
- const ext = filename.split(".").pop()?.toLowerCase();
12
- switch (ext) {
13
- case "md":
14
- return [markdown()];
15
- case "json":
16
- return [json()];
17
- case "js":
18
- case "ts":
19
- case "jsx":
20
- case "tsx":
21
- return [javascript({ typescript: ext === "ts" || ext === "tsx", jsx: ext === "jsx" || ext === "tsx" })];
22
- default:
23
- return [];
24
- }
25
- }
26
- function buildTree(files) {
27
- const rootFiles = [];
28
- const dirMap = /* @__PURE__ */ new Map();
29
- function ensureDir(dirPath) {
30
- const existing = dirMap.get(dirPath);
31
- if (existing) return existing;
32
- const parts = dirPath.split("/");
33
- const node = {
34
- name: parts[parts.length - 1] ?? dirPath,
35
- fullPath: dirPath,
36
- children: [],
37
- files: []
38
- };
39
- dirMap.set(dirPath, node);
40
- if (parts.length > 1) {
41
- const parentPath = parts.slice(0, -1).join("/");
42
- const parent = ensureDir(parentPath);
43
- if (!parent.children.some((c) => c.fullPath === dirPath)) {
44
- parent.children.push(node);
45
- }
46
- }
47
- return node;
48
- }
49
- for (const file of files) {
50
- const slashIdx = file.path.lastIndexOf("/");
51
- if (slashIdx === -1) {
52
- rootFiles.push(file);
53
- } else {
54
- const dirPath = file.path.slice(0, slashIdx);
55
- const dir = ensureDir(dirPath);
56
- dir.files.push(file);
57
- }
58
- }
59
- const topLevel = [];
60
- for (const node of dirMap.values()) {
61
- if (!node.fullPath.includes("/")) {
62
- topLevel.push(node);
63
- }
64
- }
65
- function sortNode(node) {
66
- node.children.sort((a, b) => a.name.localeCompare(b.name));
67
- node.files.sort((a, b) => a.path.localeCompare(b.path));
68
- node.children.forEach(sortNode);
69
- }
70
- topLevel.forEach(sortNode);
71
- topLevel.sort((a, b) => a.name.localeCompare(b.name));
72
- rootFiles.sort((a, b) => a.path.localeCompare(b.path));
73
- return { rootFiles, rootDirs: topLevel };
74
- }
75
- function collectAllDirPaths(nodes) {
76
- const paths = /* @__PURE__ */ new Set();
77
- function walk(node) {
78
- paths.add(node.fullPath);
79
- node.children.forEach(walk);
80
- }
81
- nodes.forEach(walk);
82
- return paths;
83
- }
84
- function FileTreeEditor({
85
- initialFiles,
86
- onSave,
87
- onChange,
88
- readOnly = false,
89
- hideSave = false,
90
- title = "Files",
91
- saveLabel = "Save",
92
- addFolderLabel = "Folder",
93
- newFileTemplate = { filename: "SKILL.md", content: "# New\n\nDescribe this...\n" },
94
- savedVersion
95
- }) {
96
- const [files, setFiles] = useState(initialFiles);
97
- const [selectedPath, setSelectedPath] = useState(
98
- initialFiles.length > 0 ? initialFiles[0]?.path ?? null : null
99
- );
100
- const [saving, setSaving] = useState(false);
101
- const [expanded, setExpanded] = useState(() => {
102
- const { rootDirs } = buildTree(initialFiles);
103
- return collectAllDirPaths(rootDirs);
104
- });
105
- const [showAddFolder, setShowAddFolder] = useState(false);
106
- const [newFolderName, setNewFolderName] = useState("");
107
- const [addingFileInDir, setAddingFileInDir] = useState(null);
108
- const [newFileName, setNewFileName] = useState("");
109
- const [savedSnapshot, setSavedSnapshot] = useState(() => JSON.stringify(initialFiles));
110
- useEffect(() => {
111
- const snap = JSON.stringify(initialFiles);
112
- setSavedSnapshot(snap);
113
- setFiles(initialFiles);
114
- const { rootDirs } = buildTree(initialFiles);
115
- setExpanded(collectAllDirPaths(rootDirs));
116
- }, [initialFiles]);
117
- useEffect(() => {
118
- if (savedVersion !== void 0 && savedVersion > 0) {
119
- setSavedSnapshot(JSON.stringify(files));
120
- }
121
- }, [savedVersion]);
122
- const onChangeRef = useRef(onChange);
123
- onChangeRef.current = onChange;
124
- useEffect(() => {
125
- if (onChangeRef.current && JSON.stringify(files) !== savedSnapshot) {
126
- onChangeRef.current(files);
127
- }
128
- }, [files, savedSnapshot]);
129
- const isDirty = useMemo(
130
- () => JSON.stringify(files) !== savedSnapshot,
131
- [files, savedSnapshot]
132
- );
133
- const tree = useMemo(() => buildTree(files), [files]);
134
- const activeFile = useMemo(
135
- () => selectedPath ? files.find((f) => f.path === selectedPath) ?? null : null,
136
- [files, selectedPath]
137
- );
138
- const handleEditorChange = useCallback((value) => {
139
- if (readOnly || !selectedPath) return;
140
- setFiles((prev) => prev.map((f) => f.path === selectedPath ? { ...f, content: value } : f));
141
- }, [readOnly, selectedPath]);
142
- function toggleExpand(dirPath) {
143
- setExpanded((prev) => {
144
- const next = new Set(prev);
145
- if (next.has(dirPath)) {
146
- next.delete(dirPath);
147
- } else {
148
- next.add(dirPath);
149
- }
150
- return next;
151
- });
152
- }
153
- function addFolder() {
154
- const name = newFolderName.trim();
155
- if (!name) return;
156
- const filePath = `${name}/${newFileTemplate.filename}`;
157
- if (files.some((f) => f.path === filePath)) return;
158
- const content = newFileTemplate.content.replace("# New", `# ${name}`);
159
- setFiles((prev) => [...prev, { path: filePath, content }]);
160
- setSelectedPath(filePath);
161
- setExpanded((prev) => /* @__PURE__ */ new Set([...prev, name]));
162
- setNewFolderName("");
163
- setShowAddFolder(false);
164
- }
165
- function removeDir(dirPath) {
166
- const prefix = dirPath + "/";
167
- const affectedFiles = files.filter((f) => f.path.startsWith(prefix));
168
- if (affectedFiles.length === 0) return;
169
- if (!confirm(`Remove "${dirPath}" and all ${affectedFiles.length} file(s)?`)) return;
170
- setFiles((prev) => prev.filter((f) => !f.path.startsWith(prefix)));
171
- if (selectedPath && selectedPath.startsWith(prefix)) {
172
- setSelectedPath(null);
173
- }
174
- }
175
- function removeFile(filePath) {
176
- const fileName = filePath.split("/").pop() ?? filePath;
177
- if (!confirm(`Remove file "${fileName}"?`)) return;
178
- setFiles((prev) => prev.filter((f) => f.path !== filePath));
179
- if (selectedPath === filePath) {
180
- setSelectedPath(null);
181
- }
182
- }
183
- function addFileInDir(dirPath) {
184
- const name = newFileName.trim();
185
- if (!name) return;
186
- const filePath = dirPath ? `${dirPath}/${name}` : name;
187
- if (files.some((f) => f.path === filePath)) return;
188
- setFiles((prev) => [...prev, { path: filePath, content: "" }]);
189
- setSelectedPath(filePath);
190
- setNewFileName("");
191
- setAddingFileInDir(null);
192
- }
193
- async function handleSave() {
194
- setSaving(true);
195
- try {
196
- await onSave(files);
197
- } finally {
198
- setSaving(false);
199
- }
200
- }
201
- function renderTreeNode(node, depth) {
202
- const isExpanded = expanded.has(node.fullPath);
203
- return /* @__PURE__ */ jsxs("div", { children: [
204
- /* @__PURE__ */ jsxs(
205
- "div",
206
- {
207
- className: `flex items-center justify-between cursor-pointer hover:bg-muted/50 py-1 pr-2`,
208
- style: { paddingLeft: `${depth * 16 + 8}px` },
209
- onClick: () => toggleExpand(node.fullPath),
210
- children: [
211
- /* @__PURE__ */ jsxs("span", { className: "font-medium text-xs truncate flex items-center gap-1", children: [
212
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: isExpanded ? "\u25BE" : "\u25B8" }),
213
- node.name,
214
- "/"
215
- ] }),
216
- !readOnly && /* @__PURE__ */ jsx(
217
- "button",
218
- {
219
- onClick: (e) => {
220
- e.stopPropagation();
221
- removeDir(node.fullPath);
222
- },
223
- className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
224
- children: "\xD7"
225
- }
226
- )
227
- ]
228
- }
229
- ),
230
- isExpanded && /* @__PURE__ */ jsxs(Fragment, { children: [
231
- node.children.map((child) => renderTreeNode(child, depth + 1)),
232
- node.files.map((file) => {
233
- const fileName = file.path.split("/").pop() ?? file.path;
234
- return /* @__PURE__ */ jsxs(
235
- "div",
236
- {
237
- className: `flex items-center justify-between cursor-pointer hover:bg-muted/30 py-1 pr-2 ${selectedPath === file.path ? "bg-primary/10 text-primary" : ""}`,
238
- style: { paddingLeft: `${(depth + 1) * 16 + 8}px` },
239
- onClick: () => setSelectedPath(file.path),
240
- children: [
241
- /* @__PURE__ */ jsx("span", { className: "text-xs truncate", children: fileName }),
242
- !readOnly && /* @__PURE__ */ jsx(
243
- "button",
244
- {
245
- onClick: (e) => {
246
- e.stopPropagation();
247
- removeFile(file.path);
248
- },
249
- className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
250
- children: "\xD7"
251
- }
252
- )
253
- ]
254
- },
255
- file.path
256
- );
257
- }),
258
- !readOnly && /* @__PURE__ */ jsx("div", { style: { paddingLeft: `${(depth + 1) * 16 + 8}px` }, className: "py-1 pr-2", children: addingFileInDir === node.fullPath ? /* @__PURE__ */ jsxs("div", { className: "flex gap-1", children: [
259
- /* @__PURE__ */ jsx(
260
- Input,
261
- {
262
- value: newFileName,
263
- onChange: (e) => setNewFileName(e.target.value),
264
- placeholder: "file.md",
265
- className: "h-6 text-xs",
266
- onKeyDown: (e) => e.key === "Enter" && addFileInDir(node.fullPath),
267
- autoFocus: true
268
- }
269
- ),
270
- /* @__PURE__ */ jsx(Button, { onClick: () => addFileInDir(node.fullPath), size: "sm", className: "h-6 text-xs px-2", children: "+" })
271
- ] }) : /* @__PURE__ */ jsx(
272
- "button",
273
- {
274
- onClick: () => {
275
- setAddingFileInDir(node.fullPath);
276
- setNewFileName("");
277
- },
278
- className: "text-xs text-primary hover:underline",
279
- children: "+ File"
280
- }
281
- ) })
282
- ] })
283
- ] }, node.fullPath);
284
- }
285
- return /* @__PURE__ */ jsxs("div", { children: [
286
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-3", children: [
287
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
288
- /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: title }),
289
- isDirty && !readOnly && /* @__PURE__ */ jsx(Badge, { variant: "destructive", className: "text-xs", children: "Unsaved changes" }),
290
- readOnly && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: "Read-only" })
291
- ] }),
292
- !readOnly && !hideSave && /* @__PURE__ */ jsx(Button, { onClick: handleSave, disabled: saving || !isDirty, size: "sm", children: saving ? "Saving..." : saveLabel })
293
- ] }),
294
- /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs("div", { className: "flex gap-4 min-h-[500px]", children: [
295
- /* @__PURE__ */ jsxs("div", { className: "w-64 shrink-0 border border-border rounded-md overflow-hidden", children: [
296
- /* @__PURE__ */ jsxs("div", { className: "p-2 bg-muted/50 border-b border-border flex items-center justify-between", children: [
297
- /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-muted-foreground", children: title }),
298
- !readOnly && /* @__PURE__ */ jsxs(
299
- "button",
300
- {
301
- onClick: () => setShowAddFolder(!showAddFolder),
302
- className: "text-xs text-primary hover:underline",
303
- children: [
304
- "+ ",
305
- addFolderLabel
306
- ]
307
- }
308
- )
309
- ] }),
310
- showAddFolder && !readOnly && /* @__PURE__ */ jsxs("div", { className: "p-2 border-b border-border flex gap-1", children: [
311
- /* @__PURE__ */ jsx(
312
- Input,
313
- {
314
- value: newFolderName,
315
- onChange: (e) => setNewFolderName(e.target.value),
316
- placeholder: "folder-name",
317
- className: "h-7 text-xs",
318
- onKeyDown: (e) => e.key === "Enter" && addFolder(),
319
- autoFocus: true
320
- }
321
- ),
322
- /* @__PURE__ */ jsx(Button, { onClick: addFolder, size: "sm", className: "h-7 text-xs px-2", children: "Add" })
323
- ] }),
324
- /* @__PURE__ */ jsxs("div", { className: "text-sm overflow-y-auto", children: [
325
- tree.rootFiles.map((file) => /* @__PURE__ */ jsxs(
326
- "div",
327
- {
328
- className: `flex items-center justify-between cursor-pointer hover:bg-muted/30 py-1 pr-2 ${selectedPath === file.path ? "bg-primary/10 text-primary" : ""}`,
329
- style: { paddingLeft: "8px" },
330
- onClick: () => setSelectedPath(file.path),
331
- children: [
332
- /* @__PURE__ */ jsx("span", { className: "text-xs truncate", children: file.path }),
333
- !readOnly && /* @__PURE__ */ jsx(
334
- "button",
335
- {
336
- onClick: (e) => {
337
- e.stopPropagation();
338
- removeFile(file.path);
339
- },
340
- className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
341
- children: "\xD7"
342
- }
343
- )
344
- ]
345
- },
346
- file.path
347
- )),
348
- tree.rootDirs.map((node) => renderTreeNode(node, 0)),
349
- files.length === 0 && /* @__PURE__ */ jsx("p", { className: "p-3 text-xs text-muted-foreground", children: readOnly ? "No files." : `No ${title.toLowerCase()} yet. Add a ${addFolderLabel.toLowerCase()} to get started.` })
350
- ] })
351
- ] }),
352
- /* @__PURE__ */ jsx("div", { className: "flex-1 border border-border rounded-md overflow-hidden", children: activeFile ? /* @__PURE__ */ jsxs("div", { className: "h-full flex flex-col", children: [
353
- /* @__PURE__ */ jsx("div", { className: "px-3 py-1.5 bg-muted/50 border-b border-border text-xs text-muted-foreground", children: activeFile.path }),
354
- /* @__PURE__ */ jsx(
355
- CodeMirror,
356
- {
357
- value: activeFile.content,
358
- onChange: handleEditorChange,
359
- readOnly,
360
- theme: oneDark,
361
- extensions: getLanguageExtension(activeFile.path),
362
- height: "100%",
363
- className: "flex-1 overflow-auto",
364
- basicSetup: {
365
- lineNumbers: true,
366
- foldGutter: true,
367
- bracketMatching: true
368
- }
369
- }
370
- )
371
- ] }) : /* @__PURE__ */ jsxs("div", { className: "h-full flex items-center justify-center text-muted-foreground text-sm", children: [
372
- "Select a file to ",
373
- readOnly ? "view" : "edit"
374
- ] }) })
375
- ] }) })
376
- ] });
377
- }
378
11
  function PluginEditorPage({ marketplaceId, pluginName }) {
379
12
  const { LinkComponent, basePath } = useNavigation();
380
13
  const client = useAgentPlaneClient();
@@ -542,274 +175,5 @@ function PluginEditorPage({ marketplaceId, pluginName }) {
542
175
  ] })
543
176
  ] });
544
177
  }
545
- var FILE_MAP = [
546
- { path: "SOUL.md", field: "soul_md" },
547
- { path: "IDENTITY.md", field: "identity_md" },
548
- { path: "STYLE.md", field: "style_md" },
549
- { path: "AGENTS.md", field: "agents_md" },
550
- { path: "HEARTBEAT.md", field: "heartbeat_md" },
551
- { path: "USER_TEMPLATE.md", field: "user_template_md" },
552
- { path: "examples/good-outputs.md", field: "examples_good_md" },
553
- { path: "examples/bad-outputs.md", field: "examples_bad_md" }
554
- ];
555
- function agentToFiles(agent) {
556
- const files = [];
557
- for (const { path, field } of FILE_MAP) {
558
- const value = agent[field];
559
- if (typeof value === "string" && value.length > 0) {
560
- files.push({ path, content: value });
561
- }
562
- }
563
- return files;
564
- }
565
- function filesToPayload(files) {
566
- const payload = {};
567
- for (const { path, field } of FILE_MAP) {
568
- const file = files.find((f) => f.path === path);
569
- payload[field] = file ? file.content : null;
570
- }
571
- return payload;
572
- }
573
- function AgentIdentityTab({
574
- agent,
575
- FileTreeEditor: FileTreeEditor2,
576
- onSaved,
577
- onGenerateSoul,
578
- onImportSoul,
579
- onExportSoul,
580
- onPublishSoul
581
- }) {
582
- const client = useAgentPlaneClient();
583
- const [saving, setSaving] = useState(false);
584
- const [error, setError] = useState("");
585
- const [generating, setGenerating] = useState(false);
586
- const [publishing, setPublishing] = useState(false);
587
- const [importOpen, setImportOpen] = useState(false);
588
- const [savedVersion, setSavedVersion] = useState(0);
589
- const [overrideFiles, setOverrideFiles] = useState(null);
590
- const initialFiles = useMemo(() => agentToFiles(agent), [agent]);
591
- const editorFiles = overrideFiles ?? initialFiles;
592
- const handleSave = useCallback(
593
- async (files) => {
594
- setSaving(true);
595
- setError("");
596
- try {
597
- const payload = filesToPayload(files);
598
- await client.agents.update(agent.id, payload);
599
- setOverrideFiles(null);
600
- setSavedVersion((v) => v + 1);
601
- onSaved?.();
602
- } catch (err) {
603
- setError(err instanceof Error ? err.message : "Failed to save");
604
- throw err;
605
- } finally {
606
- setSaving(false);
607
- }
608
- },
609
- [agent.id, client, onSaved]
610
- );
611
- function applyFilesFromResponse(responseFiles) {
612
- const newFiles = [];
613
- for (const { path, field } of FILE_MAP) {
614
- const content = responseFiles[field] ?? responseFiles[path];
615
- if (content) {
616
- newFiles.push({ path, content });
617
- }
618
- }
619
- if (newFiles.length > 0) {
620
- setOverrideFiles(newFiles);
621
- }
622
- }
623
- async function handleGenerate() {
624
- if (!onGenerateSoul) return;
625
- setGenerating(true);
626
- setError("");
627
- try {
628
- const data = await onGenerateSoul();
629
- applyFilesFromResponse(data.files);
630
- } catch (err) {
631
- setError(err instanceof Error ? err.message : "Failed to generate");
632
- } finally {
633
- setGenerating(false);
634
- }
635
- }
636
- function handleImported(responseFiles) {
637
- applyFilesFromResponse(responseFiles);
638
- }
639
- async function handleExport() {
640
- if (!onExportSoul) return;
641
- setError("");
642
- try {
643
- const data = await onExportSoul();
644
- const blob = new Blob([JSON.stringify(data, null, 2)], {
645
- type: "application/json"
646
- });
647
- const url = URL.createObjectURL(blob);
648
- const a = document.createElement("a");
649
- a.href = url;
650
- a.download = `${data.name || "soulspec"}.json`;
651
- a.click();
652
- URL.revokeObjectURL(url);
653
- } catch (err) {
654
- setError(err instanceof Error ? err.message : "Failed to export");
655
- }
656
- }
657
- async function handlePublish() {
658
- if (!onPublishSoul) return;
659
- const owner = prompt("Enter owner name for publishing:");
660
- if (!owner?.trim()) return;
661
- setPublishing(true);
662
- setError("");
663
- try {
664
- await onPublishSoul(owner.trim());
665
- } catch (err) {
666
- setError(err instanceof Error ? err.message : "Failed to publish");
667
- } finally {
668
- setPublishing(false);
669
- }
670
- }
671
- const warnings = [];
672
- const hasSoul = editorFiles.some(
673
- (f) => f.path === "SOUL.md" && f.content.trim().length > 0
674
- );
675
- const hasIdentity = editorFiles.some(
676
- (f) => f.path === "IDENTITY.md" && f.content.trim().length > 0
677
- );
678
- if (!hasSoul) warnings.push("SOUL.md is empty -- this is the core identity file");
679
- if (!hasIdentity)
680
- warnings.push("IDENTITY.md is empty -- consider adding behavioral traits");
681
- return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
682
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
683
- onGenerateSoul && /* @__PURE__ */ jsx(
684
- Button,
685
- {
686
- variant: "outline",
687
- size: "sm",
688
- onClick: handleGenerate,
689
- disabled: generating,
690
- children: generating ? "Generating..." : "Generate Soul"
691
- }
692
- ),
693
- onImportSoul && /* @__PURE__ */ jsx(
694
- Button,
695
- {
696
- variant: "outline",
697
- size: "sm",
698
- onClick: () => setImportOpen(true),
699
- children: "Import"
700
- }
701
- ),
702
- onExportSoul && /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", onClick: handleExport, children: "Export" }),
703
- onPublishSoul && /* @__PURE__ */ jsx(
704
- Button,
705
- {
706
- variant: "outline",
707
- size: "sm",
708
- onClick: handlePublish,
709
- disabled: publishing,
710
- children: publishing ? "Publishing..." : "Publish"
711
- }
712
- ),
713
- overrideFiles && /* @__PURE__ */ jsx(Badge, { variant: "destructive", className: "text-xs", children: "Unsaved generated content" })
714
- ] }),
715
- error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }),
716
- /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-muted-foreground/25 p-5", children: /* @__PURE__ */ jsx(
717
- FileTreeEditor2,
718
- {
719
- initialFiles: editorFiles,
720
- onSave: handleSave,
721
- title: "SoulSpec",
722
- saveLabel: saving ? "Saving..." : "Save Identity",
723
- addFolderLabel: "Folder",
724
- newFileTemplate: {
725
- filename: "CUSTOM.md",
726
- content: "# Custom\n\nAdd custom identity content...\n"
727
- },
728
- savedVersion
729
- }
730
- ) }),
731
- warnings.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-amber-600/30 bg-amber-950/20 p-3", children: [
732
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-amber-500 mb-1", children: "Validation Warnings" }),
733
- /* @__PURE__ */ jsx("ul", { className: "text-xs text-amber-400/80 space-y-0.5", children: warnings.map((w) => /* @__PURE__ */ jsxs("li", { children: [
734
- "- ",
735
- w
736
- ] }, w)) })
737
- ] }),
738
- onImportSoul && /* @__PURE__ */ jsx(
739
- ImportSoulDialog,
740
- {
741
- open: importOpen,
742
- onOpenChange: setImportOpen,
743
- onImport: onImportSoul,
744
- onImported: handleImported
745
- }
746
- )
747
- ] });
748
- }
749
- function ImportSoulDialog({
750
- open,
751
- onOpenChange,
752
- onImport,
753
- onImported
754
- }) {
755
- const [ref, setRef] = useState("");
756
- const [loading, setLoading] = useState(false);
757
- const [error, setError] = useState("");
758
- async function handleImport() {
759
- if (!ref.trim()) return;
760
- setLoading(true);
761
- setError("");
762
- try {
763
- const data = await onImport(ref.trim());
764
- onImported(data.files);
765
- onOpenChange(false);
766
- setRef("");
767
- } catch (err) {
768
- setError(err instanceof Error ? err.message : "Failed to import");
769
- } finally {
770
- setLoading(false);
771
- }
772
- }
773
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-md", children: [
774
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
775
- /* @__PURE__ */ jsx(DialogTitle, { children: "Import SoulSpec" }),
776
- /* @__PURE__ */ jsx(DialogDescription, { children: "Import a SoulSpec from the ClawSouls registry." })
777
- ] }),
778
- /* @__PURE__ */ jsxs(DialogBody, { children: [
779
- error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive mb-3", children: error }),
780
- /* @__PURE__ */ jsx(FormField, { label: "Registry Reference", children: /* @__PURE__ */ jsx(
781
- Input,
782
- {
783
- value: ref,
784
- onChange: (e) => setRef(e.target.value),
785
- placeholder: "owner/name (e.g. clawsouls/surgical-coder)",
786
- onKeyDown: (e) => e.key === "Enter" && handleImport(),
787
- autoFocus: true
788
- }
789
- ) })
790
- ] }),
791
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
792
- /* @__PURE__ */ jsx(
793
- Button,
794
- {
795
- variant: "outline",
796
- size: "sm",
797
- onClick: () => onOpenChange(false),
798
- disabled: loading,
799
- children: "Cancel"
800
- }
801
- ),
802
- /* @__PURE__ */ jsx(
803
- Button,
804
- {
805
- size: "sm",
806
- onClick: handleImport,
807
- disabled: loading || !ref.trim(),
808
- children: loading ? "Importing..." : "Import"
809
- }
810
- )
811
- ] })
812
- ] }) });
813
- }
814
178
 
815
- export { AgentIdentityTab, FileTreeEditor, PluginEditorPage };
179
+ export { PluginEditorPage };