@getcatalystiq/agent-plane-ui 0.1.12 → 0.1.13

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.cjs CHANGED
@@ -1,2 +1,553 @@
1
1
  'use strict';
2
2
 
3
+ var chunkTZGIXZON_cjs = require('./chunk-TZGIXZON.cjs');
4
+ var react = require('react');
5
+ var CodeMirror = require('@uiw/react-codemirror');
6
+ var langMarkdown = require('@codemirror/lang-markdown');
7
+ var langJavascript = require('@codemirror/lang-javascript');
8
+ var langJson = require('@codemirror/lang-json');
9
+ var themeOneDark = require('@codemirror/theme-one-dark');
10
+ var jsxRuntime = require('react/jsx-runtime');
11
+
12
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
13
+
14
+ var CodeMirror__default = /*#__PURE__*/_interopDefault(CodeMirror);
15
+
16
+ function getLanguageExtension(filename) {
17
+ const ext = filename.split(".").pop()?.toLowerCase();
18
+ switch (ext) {
19
+ case "md":
20
+ return [langMarkdown.markdown()];
21
+ case "json":
22
+ return [langJson.json()];
23
+ case "js":
24
+ case "ts":
25
+ case "jsx":
26
+ case "tsx":
27
+ return [langJavascript.javascript({ typescript: ext === "ts" || ext === "tsx", jsx: ext === "jsx" || ext === "tsx" })];
28
+ default:
29
+ return [];
30
+ }
31
+ }
32
+ function buildTree(files) {
33
+ const rootFiles = [];
34
+ const dirMap = /* @__PURE__ */ new Map();
35
+ function ensureDir(dirPath) {
36
+ const existing = dirMap.get(dirPath);
37
+ if (existing) return existing;
38
+ const parts = dirPath.split("/");
39
+ const node = {
40
+ name: parts[parts.length - 1] ?? dirPath,
41
+ fullPath: dirPath,
42
+ children: [],
43
+ files: []
44
+ };
45
+ dirMap.set(dirPath, node);
46
+ if (parts.length > 1) {
47
+ const parentPath = parts.slice(0, -1).join("/");
48
+ const parent = ensureDir(parentPath);
49
+ if (!parent.children.some((c) => c.fullPath === dirPath)) {
50
+ parent.children.push(node);
51
+ }
52
+ }
53
+ return node;
54
+ }
55
+ for (const file of files) {
56
+ const slashIdx = file.path.lastIndexOf("/");
57
+ if (slashIdx === -1) {
58
+ rootFiles.push(file);
59
+ } else {
60
+ const dirPath = file.path.slice(0, slashIdx);
61
+ const dir = ensureDir(dirPath);
62
+ dir.files.push(file);
63
+ }
64
+ }
65
+ const topLevel = [];
66
+ for (const node of dirMap.values()) {
67
+ if (!node.fullPath.includes("/")) {
68
+ topLevel.push(node);
69
+ }
70
+ }
71
+ function sortNode(node) {
72
+ node.children.sort((a, b) => a.name.localeCompare(b.name));
73
+ node.files.sort((a, b) => a.path.localeCompare(b.path));
74
+ node.children.forEach(sortNode);
75
+ }
76
+ topLevel.forEach(sortNode);
77
+ topLevel.sort((a, b) => a.name.localeCompare(b.name));
78
+ rootFiles.sort((a, b) => a.path.localeCompare(b.path));
79
+ return { rootFiles, rootDirs: topLevel };
80
+ }
81
+ function collectAllDirPaths(nodes) {
82
+ const paths = /* @__PURE__ */ new Set();
83
+ function walk(node) {
84
+ paths.add(node.fullPath);
85
+ node.children.forEach(walk);
86
+ }
87
+ nodes.forEach(walk);
88
+ return paths;
89
+ }
90
+ function FileTreeEditor({
91
+ initialFiles,
92
+ onSave,
93
+ onChange,
94
+ readOnly = false,
95
+ hideSave = false,
96
+ title = "Files",
97
+ saveLabel = "Save",
98
+ addFolderLabel = "Folder",
99
+ newFileTemplate = { filename: "SKILL.md", content: "# New\n\nDescribe this...\n" },
100
+ savedVersion
101
+ }) {
102
+ const [files, setFiles] = react.useState(initialFiles);
103
+ const [selectedPath, setSelectedPath] = react.useState(
104
+ initialFiles.length > 0 ? initialFiles[0]?.path ?? null : null
105
+ );
106
+ const [saving, setSaving] = react.useState(false);
107
+ const [expanded, setExpanded] = react.useState(() => {
108
+ const { rootDirs } = buildTree(initialFiles);
109
+ return collectAllDirPaths(rootDirs);
110
+ });
111
+ const [showAddFolder, setShowAddFolder] = react.useState(false);
112
+ const [newFolderName, setNewFolderName] = react.useState("");
113
+ const [addingFileInDir, setAddingFileInDir] = react.useState(null);
114
+ const [newFileName, setNewFileName] = react.useState("");
115
+ const [savedSnapshot, setSavedSnapshot] = react.useState(() => JSON.stringify(initialFiles));
116
+ react.useEffect(() => {
117
+ const snap = JSON.stringify(initialFiles);
118
+ setSavedSnapshot(snap);
119
+ setFiles(initialFiles);
120
+ const { rootDirs } = buildTree(initialFiles);
121
+ setExpanded(collectAllDirPaths(rootDirs));
122
+ }, [initialFiles]);
123
+ react.useEffect(() => {
124
+ if (savedVersion !== void 0 && savedVersion > 0) {
125
+ setSavedSnapshot(JSON.stringify(files));
126
+ }
127
+ }, [savedVersion]);
128
+ const onChangeRef = react.useRef(onChange);
129
+ onChangeRef.current = onChange;
130
+ react.useEffect(() => {
131
+ if (onChangeRef.current && JSON.stringify(files) !== savedSnapshot) {
132
+ onChangeRef.current(files);
133
+ }
134
+ }, [files, savedSnapshot]);
135
+ const isDirty = react.useMemo(
136
+ () => JSON.stringify(files) !== savedSnapshot,
137
+ [files, savedSnapshot]
138
+ );
139
+ const tree = react.useMemo(() => buildTree(files), [files]);
140
+ const activeFile = react.useMemo(
141
+ () => selectedPath ? files.find((f) => f.path === selectedPath) ?? null : null,
142
+ [files, selectedPath]
143
+ );
144
+ const handleEditorChange = react.useCallback((value) => {
145
+ if (readOnly || !selectedPath) return;
146
+ setFiles((prev) => prev.map((f) => f.path === selectedPath ? { ...f, content: value } : f));
147
+ }, [readOnly, selectedPath]);
148
+ function toggleExpand(dirPath) {
149
+ setExpanded((prev) => {
150
+ const next = new Set(prev);
151
+ if (next.has(dirPath)) {
152
+ next.delete(dirPath);
153
+ } else {
154
+ next.add(dirPath);
155
+ }
156
+ return next;
157
+ });
158
+ }
159
+ function addFolder() {
160
+ const name = newFolderName.trim();
161
+ if (!name) return;
162
+ const filePath = `${name}/${newFileTemplate.filename}`;
163
+ if (files.some((f) => f.path === filePath)) return;
164
+ const content = newFileTemplate.content.replace("# New", `# ${name}`);
165
+ setFiles((prev) => [...prev, { path: filePath, content }]);
166
+ setSelectedPath(filePath);
167
+ setExpanded((prev) => /* @__PURE__ */ new Set([...prev, name]));
168
+ setNewFolderName("");
169
+ setShowAddFolder(false);
170
+ }
171
+ function removeDir(dirPath) {
172
+ const prefix = dirPath + "/";
173
+ const affectedFiles = files.filter((f) => f.path.startsWith(prefix));
174
+ if (affectedFiles.length === 0) return;
175
+ if (!confirm(`Remove "${dirPath}" and all ${affectedFiles.length} file(s)?`)) return;
176
+ setFiles((prev) => prev.filter((f) => !f.path.startsWith(prefix)));
177
+ if (selectedPath && selectedPath.startsWith(prefix)) {
178
+ setSelectedPath(null);
179
+ }
180
+ }
181
+ function removeFile(filePath) {
182
+ const fileName = filePath.split("/").pop() ?? filePath;
183
+ if (!confirm(`Remove file "${fileName}"?`)) return;
184
+ setFiles((prev) => prev.filter((f) => f.path !== filePath));
185
+ if (selectedPath === filePath) {
186
+ setSelectedPath(null);
187
+ }
188
+ }
189
+ function addFileInDir(dirPath) {
190
+ const name = newFileName.trim();
191
+ if (!name) return;
192
+ const filePath = dirPath ? `${dirPath}/${name}` : name;
193
+ if (files.some((f) => f.path === filePath)) return;
194
+ setFiles((prev) => [...prev, { path: filePath, content: "" }]);
195
+ setSelectedPath(filePath);
196
+ setNewFileName("");
197
+ setAddingFileInDir(null);
198
+ }
199
+ async function handleSave() {
200
+ setSaving(true);
201
+ try {
202
+ await onSave(files);
203
+ } finally {
204
+ setSaving(false);
205
+ }
206
+ }
207
+ function renderTreeNode(node, depth) {
208
+ const isExpanded = expanded.has(node.fullPath);
209
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
210
+ /* @__PURE__ */ jsxRuntime.jsxs(
211
+ "div",
212
+ {
213
+ className: `flex items-center justify-between cursor-pointer hover:bg-muted/50 py-1 pr-2`,
214
+ style: { paddingLeft: `${depth * 16 + 8}px` },
215
+ onClick: () => toggleExpand(node.fullPath),
216
+ children: [
217
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-medium text-xs truncate flex items-center gap-1", children: [
218
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: isExpanded ? "\u25BE" : "\u25B8" }),
219
+ node.name,
220
+ "/"
221
+ ] }),
222
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
223
+ "button",
224
+ {
225
+ onClick: (e) => {
226
+ e.stopPropagation();
227
+ removeDir(node.fullPath);
228
+ },
229
+ className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
230
+ children: "\xD7"
231
+ }
232
+ )
233
+ ]
234
+ }
235
+ ),
236
+ isExpanded && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
237
+ node.children.map((child) => renderTreeNode(child, depth + 1)),
238
+ node.files.map((file) => {
239
+ const fileName = file.path.split("/").pop() ?? file.path;
240
+ return /* @__PURE__ */ jsxRuntime.jsxs(
241
+ "div",
242
+ {
243
+ className: `flex items-center justify-between cursor-pointer hover:bg-muted/30 py-1 pr-2 ${selectedPath === file.path ? "bg-primary/10 text-primary" : ""}`,
244
+ style: { paddingLeft: `${(depth + 1) * 16 + 8}px` },
245
+ onClick: () => setSelectedPath(file.path),
246
+ children: [
247
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs truncate", children: fileName }),
248
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
249
+ "button",
250
+ {
251
+ onClick: (e) => {
252
+ e.stopPropagation();
253
+ removeFile(file.path);
254
+ },
255
+ className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
256
+ children: "\xD7"
257
+ }
258
+ )
259
+ ]
260
+ },
261
+ file.path
262
+ );
263
+ }),
264
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { paddingLeft: `${(depth + 1) * 16 + 8}px` }, className: "py-1 pr-2", children: addingFileInDir === node.fullPath ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1", children: [
265
+ /* @__PURE__ */ jsxRuntime.jsx(
266
+ chunkTZGIXZON_cjs.Input,
267
+ {
268
+ value: newFileName,
269
+ onChange: (e) => setNewFileName(e.target.value),
270
+ placeholder: "file.md",
271
+ className: "h-6 text-xs",
272
+ onKeyDown: (e) => e.key === "Enter" && addFileInDir(node.fullPath),
273
+ autoFocus: true
274
+ }
275
+ ),
276
+ /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.Button, { onClick: () => addFileInDir(node.fullPath), size: "sm", className: "h-6 text-xs px-2", children: "+" })
277
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
278
+ "button",
279
+ {
280
+ onClick: () => {
281
+ setAddingFileInDir(node.fullPath);
282
+ setNewFileName("");
283
+ },
284
+ className: "text-xs text-primary hover:underline",
285
+ children: "+ File"
286
+ }
287
+ ) })
288
+ ] })
289
+ ] }, node.fullPath);
290
+ }
291
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
292
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-3", children: [
293
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
294
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-lg font-semibold", children: title }),
295
+ isDirty && !readOnly && /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.Badge, { variant: "destructive", className: "text-xs", children: "Unsaved changes" }),
296
+ readOnly && /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.Badge, { variant: "secondary", className: "text-xs", children: "Read-only" })
297
+ ] }),
298
+ !readOnly && !hideSave && /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.Button, { onClick: handleSave, disabled: saving || !isDirty, size: "sm", children: saving ? "Saving..." : saveLabel })
299
+ ] }),
300
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4 min-h-[500px]", children: [
301
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-64 shrink-0 border border-border rounded-md overflow-hidden", children: [
302
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 bg-muted/50 border-b border-border flex items-center justify-between", children: [
303
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-muted-foreground", children: title }),
304
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs(
305
+ "button",
306
+ {
307
+ onClick: () => setShowAddFolder(!showAddFolder),
308
+ className: "text-xs text-primary hover:underline",
309
+ children: [
310
+ "+ ",
311
+ addFolderLabel
312
+ ]
313
+ }
314
+ )
315
+ ] }),
316
+ showAddFolder && !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-2 border-b border-border flex gap-1", children: [
317
+ /* @__PURE__ */ jsxRuntime.jsx(
318
+ chunkTZGIXZON_cjs.Input,
319
+ {
320
+ value: newFolderName,
321
+ onChange: (e) => setNewFolderName(e.target.value),
322
+ placeholder: "folder-name",
323
+ className: "h-7 text-xs",
324
+ onKeyDown: (e) => e.key === "Enter" && addFolder(),
325
+ autoFocus: true
326
+ }
327
+ ),
328
+ /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.Button, { onClick: addFolder, size: "sm", className: "h-7 text-xs px-2", children: "Add" })
329
+ ] }),
330
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm overflow-y-auto", children: [
331
+ tree.rootFiles.map((file) => /* @__PURE__ */ jsxRuntime.jsxs(
332
+ "div",
333
+ {
334
+ className: `flex items-center justify-between cursor-pointer hover:bg-muted/30 py-1 pr-2 ${selectedPath === file.path ? "bg-primary/10 text-primary" : ""}`,
335
+ style: { paddingLeft: "8px" },
336
+ onClick: () => setSelectedPath(file.path),
337
+ children: [
338
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs truncate", children: file.path }),
339
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsx(
340
+ "button",
341
+ {
342
+ onClick: (e) => {
343
+ e.stopPropagation();
344
+ removeFile(file.path);
345
+ },
346
+ className: "text-muted-foreground hover:text-destructive text-xs ml-1 shrink-0",
347
+ children: "\xD7"
348
+ }
349
+ )
350
+ ]
351
+ },
352
+ file.path
353
+ )),
354
+ tree.rootDirs.map((node) => renderTreeNode(node, 0)),
355
+ files.length === 0 && /* @__PURE__ */ jsxRuntime.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.` })
356
+ ] })
357
+ ] }),
358
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 border border-border rounded-md overflow-hidden", children: activeFile ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex flex-col", children: [
359
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-1.5 bg-muted/50 border-b border-border text-xs text-muted-foreground", children: activeFile.path }),
360
+ /* @__PURE__ */ jsxRuntime.jsx(
361
+ CodeMirror__default.default,
362
+ {
363
+ value: activeFile.content,
364
+ onChange: handleEditorChange,
365
+ readOnly,
366
+ theme: themeOneDark.oneDark,
367
+ extensions: getLanguageExtension(activeFile.path),
368
+ height: "100%",
369
+ className: "flex-1 overflow-auto",
370
+ basicSetup: {
371
+ lineNumbers: true,
372
+ foldGutter: true,
373
+ bracketMatching: true
374
+ }
375
+ }
376
+ )
377
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "h-full flex items-center justify-center text-muted-foreground text-sm", children: [
378
+ "Select a file to ",
379
+ readOnly ? "view" : "edit"
380
+ ] }) })
381
+ ] }) })
382
+ ] });
383
+ }
384
+ function PluginEditorPage({ marketplaceId, pluginName }) {
385
+ const { LinkComponent, basePath } = chunkTZGIXZON_cjs.useNavigation();
386
+ const client = chunkTZGIXZON_cjs.useAgentPlaneClient();
387
+ const { data: pluginFiles, error, isLoading } = chunkTZGIXZON_cjs.useApi(
388
+ `marketplace-${marketplaceId}-plugin-files-${pluginName}`,
389
+ (c) => c.pluginMarketplaces.getPluginFiles(marketplaceId, pluginName)
390
+ );
391
+ const [skills, setSkills] = react.useState([]);
392
+ const [agents, setAgents] = react.useState([]);
393
+ const [mcpJson, setMcpJson] = react.useState("");
394
+ const [activeTab, setActiveTab] = react.useState("agents");
395
+ const [saving, setSaving] = react.useState(false);
396
+ const [saveError, setSaveError] = react.useState("");
397
+ const [success, setSuccess] = react.useState("");
398
+ const [savedVersion, setSavedVersion] = react.useState(0);
399
+ const [initialized, setInitialized] = react.useState(false);
400
+ if (pluginFiles && !initialized) {
401
+ setSkills(pluginFiles.skills);
402
+ setAgents(pluginFiles.agents);
403
+ setMcpJson(pluginFiles.mcpJson ?? "");
404
+ setInitialized(true);
405
+ }
406
+ const readOnly = pluginFiles ? !pluginFiles.isOwned : true;
407
+ const handleSkillsChange = react.useCallback((updated) => {
408
+ setSkills(updated);
409
+ }, []);
410
+ const handleAgentsChange = react.useCallback((updated) => {
411
+ setAgents(updated);
412
+ }, []);
413
+ const noopSave = react.useCallback(async () => {
414
+ }, []);
415
+ async function handleSaveAll() {
416
+ setSaving(true);
417
+ setSaveError("");
418
+ setSuccess("");
419
+ try {
420
+ const result = await client.pluginMarketplaces.savePluginFiles(
421
+ marketplaceId,
422
+ pluginName,
423
+ {
424
+ skills,
425
+ agents,
426
+ mcpJson: mcpJson || null
427
+ }
428
+ );
429
+ setSuccess(`Saved (commit ${result.commitSha.slice(0, 7)})`);
430
+ setSavedVersion((v) => v + 1);
431
+ } catch (err) {
432
+ setSaveError(err instanceof Error ? err.message : "Unknown error");
433
+ } finally {
434
+ setSaving(false);
435
+ }
436
+ }
437
+ if (error) {
438
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center min-h-[40vh]", children: /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-destructive", children: [
439
+ "Failed to load plugin files: ",
440
+ error.message
441
+ ] }) });
442
+ }
443
+ if (isLoading || !pluginFiles) {
444
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-6", children: [
445
+ /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.Skeleton, { className: "h-8 w-48" }),
446
+ /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.Skeleton, { className: "h-4 w-96" }),
447
+ /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.Skeleton, { className: "h-[500px] rounded-lg" })
448
+ ] });
449
+ }
450
+ const tabs = [
451
+ { id: "agents", label: "Agents", count: agents.length },
452
+ { id: "skills", label: "Skills", count: skills.length },
453
+ { id: "connectors", label: "Connectors", count: mcpJson ? 1 : 0 }
454
+ ];
455
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
456
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
457
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3 mb-1", children: /* @__PURE__ */ jsxRuntime.jsx(
458
+ LinkComponent,
459
+ {
460
+ href: `${basePath}/plugin-marketplaces/${marketplaceId}`,
461
+ className: "text-muted-foreground hover:text-foreground text-sm",
462
+ children: "\u2190 Back to marketplace"
463
+ }
464
+ ) }),
465
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
466
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-semibold", children: pluginName }),
467
+ readOnly ? /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.Badge, { variant: "outline", children: "Read-only" }) : /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.Badge, { variant: "secondary", children: "Editable" })
468
+ ] })
469
+ ] }),
470
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-end border-b border-border", children: [
471
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-1", children: tabs.map((tab) => /* @__PURE__ */ jsxRuntime.jsxs(
472
+ "button",
473
+ {
474
+ onClick: () => setActiveTab(tab.id),
475
+ className: `px-4 py-2 text-sm font-medium border-b-2 transition-colors ${activeTab === tab.id ? "border-primary text-foreground" : "border-transparent text-muted-foreground hover:text-foreground hover:border-muted-foreground/50"}`,
476
+ children: [
477
+ tab.label,
478
+ tab.count > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-1.5 text-xs text-muted-foreground", children: [
479
+ "(",
480
+ tab.count,
481
+ ")"
482
+ ] })
483
+ ]
484
+ },
485
+ tab.id
486
+ )) }),
487
+ !readOnly && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 ml-auto pb-2", children: [
488
+ saveError && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-destructive", children: saveError }),
489
+ success && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-green-500", children: success }),
490
+ /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.Button, { size: "sm", onClick: handleSaveAll, disabled: saving, children: saving ? "Pushing to GitHub..." : "Save All to GitHub" })
491
+ ] })
492
+ ] }),
493
+ activeTab === "agents" && /* @__PURE__ */ jsxRuntime.jsx(
494
+ FileTreeEditor,
495
+ {
496
+ initialFiles: pluginFiles.agents,
497
+ onSave: noopSave,
498
+ onChange: readOnly ? void 0 : handleAgentsChange,
499
+ readOnly,
500
+ hideSave: !readOnly,
501
+ title: "Agents",
502
+ addFolderLabel: "Agent",
503
+ newFileTemplate: { filename: "agent.md", content: "---\nname: new-agent\ndescription: Describe what this agent does\n---\n\nYou are a specialized agent.\n" },
504
+ savedVersion
505
+ }
506
+ ),
507
+ activeTab === "skills" && /* @__PURE__ */ jsxRuntime.jsx(
508
+ FileTreeEditor,
509
+ {
510
+ initialFiles: pluginFiles.skills,
511
+ onSave: noopSave,
512
+ onChange: readOnly ? void 0 : handleSkillsChange,
513
+ readOnly,
514
+ hideSave: !readOnly,
515
+ title: "Skills",
516
+ addFolderLabel: "Skill",
517
+ newFileTemplate: { filename: "SKILL.md", content: "# New\n\nDescribe this skill...\n" },
518
+ savedVersion
519
+ }
520
+ ),
521
+ activeTab === "connectors" && /* @__PURE__ */ jsxRuntime.jsxs(chunkTZGIXZON_cjs.Card, { children: [
522
+ /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.CardHeader, { className: "flex flex-row items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
523
+ /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.CardTitle, { className: "text-base", children: "Connectors (.mcp.json)" }),
524
+ readOnly && /* @__PURE__ */ jsxRuntime.jsx(chunkTZGIXZON_cjs.Badge, { variant: "secondary", className: "text-xs", children: "Read-only" })
525
+ ] }) }),
526
+ /* @__PURE__ */ jsxRuntime.jsxs(chunkTZGIXZON_cjs.CardContent, { children: [
527
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border border-border rounded-md overflow-hidden", children: [
528
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-1.5 bg-muted/50 border-b border-border text-xs text-muted-foreground", children: ".mcp.json" }),
529
+ /* @__PURE__ */ jsxRuntime.jsx(
530
+ CodeMirror__default.default,
531
+ {
532
+ value: mcpJson,
533
+ onChange: (val) => !readOnly && setMcpJson(val),
534
+ readOnly,
535
+ theme: themeOneDark.oneDark,
536
+ extensions: [langJson.json()],
537
+ height: "200px",
538
+ basicSetup: {
539
+ lineNumbers: true,
540
+ foldGutter: true,
541
+ bracketMatching: true
542
+ }
543
+ }
544
+ )
545
+ ] }),
546
+ !mcpJson && !readOnly && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-2", children: "No .mcp.json found. Add connector definitions to suggest MCP servers for agents using this plugin." })
547
+ ] })
548
+ ] })
549
+ ] });
550
+ }
551
+
552
+ exports.FileTreeEditor = FileTreeEditor;
553
+ exports.PluginEditorPage = PluginEditorPage;
package/dist/editor.d.cts CHANGED
@@ -1,2 +1,31 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
1
2
 
2
- export { }
3
+ interface FlatFile {
4
+ path: string;
5
+ content: string;
6
+ }
7
+ interface FileTreeEditorProps {
8
+ initialFiles: FlatFile[];
9
+ onChange?: ((files: FlatFile[]) => void) | undefined;
10
+ onSave: (files: FlatFile[]) => Promise<void>;
11
+ readOnly?: boolean;
12
+ hideSave?: boolean;
13
+ title?: string;
14
+ saveLabel?: string;
15
+ addFolderLabel?: string;
16
+ newFileTemplate?: {
17
+ filename: string;
18
+ content: string;
19
+ };
20
+ /** Increment to mark current files as saved (resets dirty state) */
21
+ savedVersion?: number;
22
+ }
23
+ declare function FileTreeEditor({ initialFiles, onSave, onChange, readOnly, hideSave, title, saveLabel, addFolderLabel, newFileTemplate, savedVersion, }: FileTreeEditorProps): react_jsx_runtime.JSX.Element;
24
+
25
+ interface PluginEditorPageProps {
26
+ marketplaceId: string;
27
+ pluginName: string;
28
+ }
29
+ declare function PluginEditorPage({ marketplaceId, pluginName }: PluginEditorPageProps): react_jsx_runtime.JSX.Element;
30
+
31
+ export { FileTreeEditor, type FileTreeEditorProps, type FlatFile, PluginEditorPage, type PluginEditorPageProps };
package/dist/editor.d.ts CHANGED
@@ -1,2 +1,31 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
1
2
 
2
- export { }
3
+ interface FlatFile {
4
+ path: string;
5
+ content: string;
6
+ }
7
+ interface FileTreeEditorProps {
8
+ initialFiles: FlatFile[];
9
+ onChange?: ((files: FlatFile[]) => void) | undefined;
10
+ onSave: (files: FlatFile[]) => Promise<void>;
11
+ readOnly?: boolean;
12
+ hideSave?: boolean;
13
+ title?: string;
14
+ saveLabel?: string;
15
+ addFolderLabel?: string;
16
+ newFileTemplate?: {
17
+ filename: string;
18
+ content: string;
19
+ };
20
+ /** Increment to mark current files as saved (resets dirty state) */
21
+ savedVersion?: number;
22
+ }
23
+ declare function FileTreeEditor({ initialFiles, onSave, onChange, readOnly, hideSave, title, saveLabel, addFolderLabel, newFileTemplate, savedVersion, }: FileTreeEditorProps): react_jsx_runtime.JSX.Element;
24
+
25
+ interface PluginEditorPageProps {
26
+ marketplaceId: string;
27
+ pluginName: string;
28
+ }
29
+ declare function PluginEditorPage({ marketplaceId, pluginName }: PluginEditorPageProps): react_jsx_runtime.JSX.Element;
30
+
31
+ export { FileTreeEditor, type FileTreeEditorProps, type FlatFile, PluginEditorPage, type PluginEditorPageProps };