claude-ws 0.3.97 → 0.3.99
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/locales/de.json +374 -12
- package/locales/en.json +374 -12
- package/locales/es.json +398 -11
- package/locales/fr.json +398 -11
- package/locales/ja.json +398 -11
- package/locales/ko.json +398 -11
- package/locales/vi.json +374 -12
- package/locales/zh.json +398 -11
- package/package.json +1 -1
- package/server.ts +283 -6
- package/src/app/[locale]/not-found.tsx +6 -3
- package/src/app/[locale]/page.tsx +14 -4
- package/src/app/api/attempts/[id]/workflow/route.ts +76 -0
- package/src/app/api/questions/answer/route.ts +58 -0
- package/src/app/api/questions/route.ts +68 -0
- package/src/app/api/tasks/[id]/compact/route.ts +62 -0
- package/src/components/access-anywhere/api-access-key-setup-modal.tsx +2 -2
- package/src/components/access-anywhere/tunnel-settings-dialog.tsx +6 -6
- package/src/components/access-anywhere/wizard-step-ctunnel.tsx +8 -8
- package/src/components/agent-factory/dependency-tree.tsx +5 -3
- package/src/components/agent-factory/discovery-dialog.tsx +26 -22
- package/src/components/agent-factory/plugin-detail-dialog.tsx +41 -38
- package/src/components/agent-factory/plugin-form-dialog.tsx +23 -20
- package/src/components/agent-factory/plugin-list.tsx +20 -17
- package/src/components/agent-factory/upload-dialog.tsx +17 -14
- package/src/components/auth/agent-provider-dialog.tsx +67 -65
- package/src/components/auth/api-key-dialog.tsx +14 -11
- package/src/components/auth/auth-error-message.tsx +6 -3
- package/src/components/editor/code-editor-with-inline-edit.tsx +4 -2
- package/src/components/editor/file-diff-resolver-modal.tsx +31 -26
- package/src/components/editor/inline-edit-dialog.tsx +9 -6
- package/src/components/editor/selection-mention-popup.tsx +3 -1
- package/src/components/header/project-selector.tsx +7 -4
- package/src/components/header.tsx +70 -4
- package/src/components/kanban/column.tsx +11 -0
- package/src/components/kanban/task-card.tsx +70 -4
- package/src/components/project-settings/component-selector.tsx +3 -1
- package/src/components/project-settings/plugin-upload-dialog.tsx +7 -5
- package/src/components/project-settings/project-settings-dialog.tsx +5 -3
- package/src/components/questions/questions-panel.tsx +136 -0
- package/src/components/settings/folder-browser-dialog.tsx +29 -25
- package/src/components/settings/settings-page.tsx +64 -18
- package/src/components/settings/setup-dialog.tsx +26 -23
- package/src/components/setup/unified-setup-wizard.tsx +12 -9
- package/src/components/sidebar/file-browser/file-create-buttons.tsx +7 -3
- package/src/components/sidebar/file-browser/file-tab-content.tsx +19 -15
- package/src/components/sidebar/file-browser/file-tabs-panel.tsx +7 -4
- package/src/components/sidebar/file-browser/file-tree.tsx +3 -1
- package/src/components/sidebar/git-changes/branch-checkout-modal.tsx +6 -4
- package/src/components/sidebar/git-changes/commit-details-modal.tsx +5 -3
- package/src/components/sidebar/git-changes/diff-tabs-panel.tsx +3 -1
- package/src/components/sidebar/git-changes/git-file-item.tsx +8 -6
- package/src/components/sidebar/git-changes/git-graph.tsx +8 -5
- package/src/components/sidebar/git-changes/git-panel.tsx +28 -27
- package/src/components/sidebar/git-changes/git-section.tsx +5 -3
- package/src/components/sidebar/shells/shell-panel.tsx +3 -1
- package/src/components/task/attachment-bar.tsx +4 -1
- package/src/components/task/attempt-item.tsx +7 -5
- package/src/components/task/conversation-view.tsx +21 -13
- package/src/components/task/floating-chat-window.tsx +14 -5
- package/src/components/task/interactive-command/checkpoint-list.tsx +5 -3
- package/src/components/task/interactive-command/confirm-dialog.tsx +9 -4
- package/src/components/task/interactive-command/interactive-command-overlay.tsx +23 -9
- package/src/components/task/interactive-command/question-prompt.tsx +12 -8
- package/src/components/task/pending-question-indicator.tsx +5 -3
- package/src/components/task/prompt-input.tsx +1 -1
- package/src/components/task/shell-log-view.tsx +3 -1
- package/src/components/task/status-line.tsx +84 -23
- package/src/components/task/task-detail-panel.tsx +27 -27
- package/src/components/task/task-shell-indicator.tsx +10 -6
- package/src/components/terminal/terminal-context-menu.tsx +6 -4
- package/src/components/terminal/terminal-instance.tsx +11 -3
- package/src/components/terminal/terminal-panel.tsx +6 -3
- package/src/components/terminal/terminal-shortcut-bar.tsx +3 -1
- package/src/components/terminal/terminal-tab-bar.tsx +5 -3
- package/src/components/workflow/workflow-panel.tsx +181 -0
- package/src/hooks/use-attempt-stream.ts +96 -3
- package/src/lib/agent-manager.ts +89 -3
- package/src/lib/db/index.ts +18 -0
- package/src/lib/db/schema.ts +29 -0
- package/src/lib/process-manager.ts +28 -7
- package/src/lib/session-manager.ts +60 -0
- package/src/lib/usage-tracker.ts +19 -19
- package/src/lib/workflow-tracker.ts +118 -20
- package/src/stores/questions-store.ts +76 -0
- package/src/stores/workflow-store.ts +71 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useRef } from 'react';
|
|
4
|
+
import { useTranslations } from 'next-intl';
|
|
4
5
|
import {
|
|
5
6
|
Dialog,
|
|
6
7
|
DialogContent,
|
|
@@ -80,6 +81,8 @@ export function PluginDetailDialog({
|
|
|
80
81
|
open,
|
|
81
82
|
onOpenChange,
|
|
82
83
|
}: PluginDetailDialogProps) {
|
|
84
|
+
const t = useTranslations('agentFactory');
|
|
85
|
+
const tCommon = useTranslations('common');
|
|
83
86
|
const isImported = isImportedPlugin(plugin);
|
|
84
87
|
const [activeTab, setActiveTab] = useState<'details' | 'files' | 'dependencies'>('details');
|
|
85
88
|
const [files, setFiles] = useState<FileNode[]>([]);
|
|
@@ -170,7 +173,7 @@ export function PluginDetailDialog({
|
|
|
170
173
|
let fileData;
|
|
171
174
|
if (isImported) {
|
|
172
175
|
const res = await fetch(`/api/agent-factory/plugins/${plugin.id}/files`);
|
|
173
|
-
if (!res.ok) throw new Error('
|
|
176
|
+
if (!res.ok) throw new Error(t('failedToLoadFiles'));
|
|
174
177
|
fileData = await res.json();
|
|
175
178
|
} else {
|
|
176
179
|
// For discovered plugins, use a different endpoint that reads from sourcePath
|
|
@@ -179,12 +182,12 @@ export function PluginDetailDialog({
|
|
|
179
182
|
headers: { 'Content-Type': 'application/json' },
|
|
180
183
|
body: JSON.stringify({ sourcePath: plugin.sourcePath, type: plugin.type }),
|
|
181
184
|
});
|
|
182
|
-
if (!res.ok) throw new Error('
|
|
185
|
+
if (!res.ok) throw new Error(t('failedToLoadFiles'));
|
|
183
186
|
fileData = await res.json();
|
|
184
187
|
}
|
|
185
188
|
setFiles(fileData.files || []);
|
|
186
189
|
} catch (err) {
|
|
187
|
-
setError(err instanceof Error ? err.message : '
|
|
190
|
+
setError(err instanceof Error ? err.message : t('failedToLoadFiles'));
|
|
188
191
|
} finally {
|
|
189
192
|
setLoadingFiles(false);
|
|
190
193
|
}
|
|
@@ -198,7 +201,7 @@ export function PluginDetailDialog({
|
|
|
198
201
|
if (isImported) {
|
|
199
202
|
const encodedPath = filePath.split('/').map(encodeURIComponent).join('/');
|
|
200
203
|
const res = await fetch(`/api/agent-factory/plugins/${plugin.id}/files/${encodedPath}`);
|
|
201
|
-
if (!res.ok) throw new Error('
|
|
204
|
+
if (!res.ok) throw new Error(t('failedToLoadFile'));
|
|
202
205
|
data = await res.json();
|
|
203
206
|
} else {
|
|
204
207
|
// For discovered plugins
|
|
@@ -210,13 +213,13 @@ export function PluginDetailDialog({
|
|
|
210
213
|
filePath,
|
|
211
214
|
}),
|
|
212
215
|
});
|
|
213
|
-
if (!res.ok) throw new Error('
|
|
216
|
+
if (!res.ok) throw new Error(t('failedToLoadFile'));
|
|
214
217
|
data = await res.json();
|
|
215
218
|
}
|
|
216
219
|
setFileContent(data);
|
|
217
220
|
setMobileFileModalOpen(true);
|
|
218
221
|
} catch (err) {
|
|
219
|
-
setError(err instanceof Error ? err.message : '
|
|
222
|
+
setError(err instanceof Error ? err.message : t('failedToLoadFile'));
|
|
220
223
|
} finally {
|
|
221
224
|
setLoadingContent(false);
|
|
222
225
|
}
|
|
@@ -229,7 +232,7 @@ export function PluginDetailDialog({
|
|
|
229
232
|
let data;
|
|
230
233
|
if (isImported) {
|
|
231
234
|
const res = await fetch(`/api/agent-factory/plugins/${plugin.id}/dependencies`);
|
|
232
|
-
if (!res.ok) throw new Error('
|
|
235
|
+
if (!res.ok) throw new Error(t('failedToLoadDependencies'));
|
|
233
236
|
data = await res.json();
|
|
234
237
|
} else {
|
|
235
238
|
// For discovered plugins
|
|
@@ -238,12 +241,12 @@ export function PluginDetailDialog({
|
|
|
238
241
|
headers: { 'Content-Type': 'application/json' },
|
|
239
242
|
body: JSON.stringify({ sourcePath: plugin.sourcePath, type: plugin.type }),
|
|
240
243
|
});
|
|
241
|
-
if (!res.ok) throw new Error('
|
|
244
|
+
if (!res.ok) throw new Error(t('failedToLoadDependencies'));
|
|
242
245
|
data = await res.json();
|
|
243
246
|
}
|
|
244
247
|
setDependencies(data);
|
|
245
248
|
} catch (err) {
|
|
246
|
-
setError(err instanceof Error ? err.message : '
|
|
249
|
+
setError(err instanceof Error ? err.message : t('failedToLoadDependencies'));
|
|
247
250
|
} finally {
|
|
248
251
|
setLoadingDeps(false);
|
|
249
252
|
}
|
|
@@ -260,7 +263,7 @@ export function PluginDetailDialog({
|
|
|
260
263
|
headers: { 'Content-Type': 'application/json' },
|
|
261
264
|
body: JSON.stringify({ useClaude: true }),
|
|
262
265
|
});
|
|
263
|
-
if (!res.ok) throw new Error('
|
|
266
|
+
if (!res.ok) throw new Error(t('failedToReResolveDependencies'));
|
|
264
267
|
const data = await res.json();
|
|
265
268
|
setDependencies(data);
|
|
266
269
|
} else {
|
|
@@ -274,12 +277,12 @@ export function PluginDetailDialog({
|
|
|
274
277
|
useClaude: true,
|
|
275
278
|
}),
|
|
276
279
|
});
|
|
277
|
-
if (!res.ok) throw new Error('
|
|
280
|
+
if (!res.ok) throw new Error(t('failedToAnalyzeDependencies'));
|
|
278
281
|
const data = await res.json();
|
|
279
282
|
setDependencies(data);
|
|
280
283
|
}
|
|
281
284
|
} catch (err) {
|
|
282
|
-
setError(err instanceof Error ? err.message : '
|
|
285
|
+
setError(err instanceof Error ? err.message : t('failedToReResolveDependencies'));
|
|
283
286
|
} finally {
|
|
284
287
|
setReResolvingDeps(false);
|
|
285
288
|
}
|
|
@@ -466,19 +469,19 @@ export function PluginDetailDialog({
|
|
|
466
469
|
<Package className="w-6 h-6" />
|
|
467
470
|
{plugin.name}
|
|
468
471
|
{!isImported && (
|
|
469
|
-
<Badge variant="outline" className="text-xs">
|
|
472
|
+
<Badge variant="outline" className="text-xs">{t('discovered')}</Badge>
|
|
470
473
|
)}
|
|
471
474
|
</DialogTitle>
|
|
472
475
|
<DialogDescription>
|
|
473
|
-
{isImported ? '
|
|
476
|
+
{isImported ? t('pluginDetails') : t('discoveredPluginDetails')}
|
|
474
477
|
</DialogDescription>
|
|
475
478
|
</DialogHeader>
|
|
476
479
|
|
|
477
480
|
<Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as 'details' | 'files' | 'dependencies')} className="flex-1 flex flex-col overflow-hidden">
|
|
478
481
|
<TabsList>
|
|
479
|
-
<TabsTrigger value="details">
|
|
480
|
-
<TabsTrigger value="files">
|
|
481
|
-
<TabsTrigger value="dependencies">
|
|
482
|
+
<TabsTrigger value="details">{t('details')}</TabsTrigger>
|
|
483
|
+
<TabsTrigger value="files">{t('files')}</TabsTrigger>
|
|
484
|
+
<TabsTrigger value="dependencies">{t('dependencies')}</TabsTrigger>
|
|
482
485
|
</TabsList>
|
|
483
486
|
|
|
484
487
|
<TabsContent value="details" className="flex-1 overflow-y-auto mt-4">
|
|
@@ -496,7 +499,7 @@ export function PluginDetailDialog({
|
|
|
496
499
|
<div>
|
|
497
500
|
<div className="flex items-center gap-2 mb-2">
|
|
498
501
|
<FileText className="w-4 h-4 text-muted-foreground" />
|
|
499
|
-
<span className="text-sm font-medium">
|
|
502
|
+
<span className="text-sm font-medium">{t('pluginDescription')}</span>
|
|
500
503
|
</div>
|
|
501
504
|
<p className="text-sm text-muted-foreground pl-6">
|
|
502
505
|
{plugin.description}
|
|
@@ -508,7 +511,7 @@ export function PluginDetailDialog({
|
|
|
508
511
|
<div>
|
|
509
512
|
<div className="flex items-center gap-2 mb-2">
|
|
510
513
|
<Folder className="w-4 h-4 text-muted-foreground" />
|
|
511
|
-
<span className="text-sm font-medium">
|
|
514
|
+
<span className="text-sm font-medium">{t('sourcePath')}</span>
|
|
512
515
|
</div>
|
|
513
516
|
<code className="text-xs bg-muted px-2 py-1 rounded block pl-6 break-all">
|
|
514
517
|
{plugin.sourcePath}
|
|
@@ -518,7 +521,7 @@ export function PluginDetailDialog({
|
|
|
518
521
|
{/* Storage Type - only for imported */}
|
|
519
522
|
{isImported && (
|
|
520
523
|
<div className="flex items-center gap-2">
|
|
521
|
-
<span className="text-sm text-muted-foreground">
|
|
524
|
+
<span className="text-sm text-muted-foreground">{t('storage')}</span>
|
|
522
525
|
<Badge variant="secondary">{plugin.storageType}</Badge>
|
|
523
526
|
</div>
|
|
524
527
|
)}
|
|
@@ -528,7 +531,7 @@ export function PluginDetailDialog({
|
|
|
528
531
|
<div>
|
|
529
532
|
<div className="flex items-center gap-2 mb-2">
|
|
530
533
|
<FileText className="w-4 h-4 text-muted-foreground" />
|
|
531
|
-
<span className="text-sm font-medium">
|
|
534
|
+
<span className="text-sm font-medium">{t('metadata')}</span>
|
|
532
535
|
</div>
|
|
533
536
|
<pre className="text-xs bg-muted p-3 rounded overflow-x-auto pl-6">
|
|
534
537
|
{metadataStr}
|
|
@@ -555,7 +558,7 @@ export function PluginDetailDialog({
|
|
|
555
558
|
{/* File Tree */}
|
|
556
559
|
<div className="border rounded-lg overflow-hidden">
|
|
557
560
|
<div className="p-2 border-b bg-muted/50 text-sm font-medium">
|
|
558
|
-
|
|
561
|
+
{t('files')}
|
|
559
562
|
</div>
|
|
560
563
|
<div className="p-2 max-h-[400px] overflow-y-auto">
|
|
561
564
|
{loadingFiles ? (
|
|
@@ -565,7 +568,7 @@ export function PluginDetailDialog({
|
|
|
565
568
|
) : error ? (
|
|
566
569
|
<div className="text-sm text-destructive py-4">{error}</div>
|
|
567
570
|
) : files.length === 0 ? (
|
|
568
|
-
<div className="text-sm text-muted-foreground py-4">
|
|
571
|
+
<div className="text-sm text-muted-foreground py-4">{t('noFilesFound')}</div>
|
|
569
572
|
) : (
|
|
570
573
|
renderFileTree(files)
|
|
571
574
|
)}
|
|
@@ -580,7 +583,7 @@ export function PluginDetailDialog({
|
|
|
580
583
|
<div className="text-sm text-muted-foreground">
|
|
581
584
|
{dependencies && dependencies.resolvedAt
|
|
582
585
|
? `Last resolved: ${new Date(dependencies.resolvedAt).toLocaleString()}`
|
|
583
|
-
: '
|
|
586
|
+
: t('dependencies')}
|
|
584
587
|
</div>
|
|
585
588
|
<Button
|
|
586
589
|
size="sm"
|
|
@@ -592,12 +595,12 @@ export function PluginDetailDialog({
|
|
|
592
595
|
{reResolvingDeps ? (
|
|
593
596
|
<>
|
|
594
597
|
<Loader2 className="w-3 h-3 animate-spin" />
|
|
595
|
-
|
|
598
|
+
{t('reResolving')}
|
|
596
599
|
</>
|
|
597
600
|
) : (
|
|
598
601
|
<>
|
|
599
602
|
<RefreshCw className="w-3 h-3" />
|
|
600
|
-
|
|
603
|
+
{t('reResolve')}
|
|
601
604
|
</>
|
|
602
605
|
)}
|
|
603
606
|
</Button>
|
|
@@ -610,18 +613,18 @@ export function PluginDetailDialog({
|
|
|
610
613
|
) : error ? (
|
|
611
614
|
<div className="text-sm text-destructive py-4">{error}</div>
|
|
612
615
|
) : !dependencies ? (
|
|
613
|
-
<div className="text-sm text-muted-foreground py-4">
|
|
616
|
+
<div className="text-sm text-muted-foreground py-4">{t('noDependenciesFound')}</div>
|
|
614
617
|
) : (
|
|
615
618
|
<>
|
|
616
619
|
{/* Library Dependencies */}
|
|
617
620
|
<div>
|
|
618
621
|
<div className="flex items-center gap-2 mb-3">
|
|
619
622
|
<PackageSearch className="w-4 h-4 text-muted-foreground" />
|
|
620
|
-
<h3 className="text-sm font-medium">
|
|
623
|
+
<h3 className="text-sm font-medium">{t('libraryDependencies')}</h3>
|
|
621
624
|
<Badge variant="secondary">{dependencies.libraries.length}</Badge>
|
|
622
625
|
</div>
|
|
623
626
|
{dependencies.libraries.length === 0 ? (
|
|
624
|
-
<p className="text-sm text-muted-foreground pl-6">
|
|
627
|
+
<p className="text-sm text-muted-foreground pl-6">{t('noExternalLibraries')}</p>
|
|
625
628
|
) : (
|
|
626
629
|
<div className="pl-6 space-y-4">
|
|
627
630
|
{/* Library badges */}
|
|
@@ -642,7 +645,7 @@ export function PluginDetailDialog({
|
|
|
642
645
|
<div className="mt-4">
|
|
643
646
|
<div className="flex items-center gap-2 mb-2">
|
|
644
647
|
<Terminal className="w-4 h-4 text-muted-foreground" />
|
|
645
|
-
<h4 className="text-sm font-medium">
|
|
648
|
+
<h4 className="text-sm font-medium">{t('installScripts')}</h4>
|
|
646
649
|
</div>
|
|
647
650
|
|
|
648
651
|
<div className="border rounded-lg overflow-hidden">
|
|
@@ -790,13 +793,13 @@ export function PluginDetailDialog({
|
|
|
790
793
|
<div>
|
|
791
794
|
<div className="flex items-center gap-2 mb-3">
|
|
792
795
|
<Package className="w-4 h-4 text-muted-foreground" />
|
|
793
|
-
<h3 className="text-sm font-medium">
|
|
796
|
+
<h3 className="text-sm font-medium">{t('pluginDependencies')}</h3>
|
|
794
797
|
<Badge variant="secondary">
|
|
795
798
|
{dependencies.dependencyTree ? countPlugins(dependencies.dependencyTree) : dependencies.plugins.length}
|
|
796
799
|
</Badge>
|
|
797
800
|
</div>
|
|
798
801
|
{(!dependencies.dependencyTree || dependencies.dependencyTree.length === 0) && dependencies.plugins.length === 0 ? (
|
|
799
|
-
<p className="text-sm text-muted-foreground pl-6">
|
|
802
|
+
<p className="text-sm text-muted-foreground pl-6">{t('noPluginDependencies')}</p>
|
|
800
803
|
) : (
|
|
801
804
|
<div className="pl-6">
|
|
802
805
|
{dependencies.dependencyTree ? (
|
|
@@ -824,7 +827,7 @@ export function PluginDetailDialog({
|
|
|
824
827
|
{dependencies.hasCycles && (
|
|
825
828
|
<div className="text-orange-500">
|
|
826
829
|
<AlertTriangle className="w-3 h-3 inline mr-1" />
|
|
827
|
-
|
|
830
|
+
{t('circularDependencies')}
|
|
828
831
|
</div>
|
|
829
832
|
)}
|
|
830
833
|
{dependencies.resolvedAt && (
|
|
@@ -839,7 +842,7 @@ export function PluginDetailDialog({
|
|
|
839
842
|
</Tabs>
|
|
840
843
|
|
|
841
844
|
<div className="flex justify-end pt-4 border-t">
|
|
842
|
-
<Button onClick={() => onOpenChange(false)}>
|
|
845
|
+
<Button onClick={() => onOpenChange(false)}>{tCommon('close')}</Button>
|
|
843
846
|
</div>
|
|
844
847
|
</DialogContent>
|
|
845
848
|
</Dialog>
|
|
@@ -868,7 +871,7 @@ export function PluginDetailDialog({
|
|
|
868
871
|
onClick={handleStartEdit}
|
|
869
872
|
>
|
|
870
873
|
<Edit3 className="w-3 h-3" />
|
|
871
|
-
|
|
874
|
+
{tCommon('edit')}
|
|
872
875
|
</Button>
|
|
873
876
|
)}
|
|
874
877
|
<Button
|
|
@@ -925,7 +928,7 @@ export function PluginDetailDialog({
|
|
|
925
928
|
disabled={saving}
|
|
926
929
|
>
|
|
927
930
|
<XIcon className="w-3 h-3 mr-1" />
|
|
928
|
-
|
|
931
|
+
{tCommon('cancel')}
|
|
929
932
|
</Button>
|
|
930
933
|
<Button
|
|
931
934
|
size="sm"
|
|
@@ -935,12 +938,12 @@ export function PluginDetailDialog({
|
|
|
935
938
|
{saving ? (
|
|
936
939
|
<>
|
|
937
940
|
<Loader2 className="w-3 h-3 mr-1 animate-spin" />
|
|
938
|
-
|
|
941
|
+
{tCommon('saving')}
|
|
939
942
|
</>
|
|
940
943
|
) : (
|
|
941
944
|
<>
|
|
942
945
|
<Save className="w-3 h-3 mr-1" />
|
|
943
|
-
|
|
946
|
+
{tCommon('save')}
|
|
944
947
|
</>
|
|
945
948
|
)}
|
|
946
949
|
</Button>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useMemo } from 'react';
|
|
4
|
+
import { useTranslations } from 'next-intl';
|
|
4
5
|
import {
|
|
5
6
|
Dialog,
|
|
6
7
|
DialogContent,
|
|
@@ -31,6 +32,8 @@ export function PluginFormDialog({
|
|
|
31
32
|
onOpenChange,
|
|
32
33
|
plugin,
|
|
33
34
|
}: PluginFormDialogProps) {
|
|
35
|
+
const t = useTranslations('agentFactory');
|
|
36
|
+
const tCommon = useTranslations('common');
|
|
34
37
|
const { createPlugin, updatePlugin, error } = useAgentFactoryStore();
|
|
35
38
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
36
39
|
|
|
@@ -110,12 +113,12 @@ export function PluginFormDialog({
|
|
|
110
113
|
<DialogContent className="sm:max-w-[500px]">
|
|
111
114
|
<DialogHeader>
|
|
112
115
|
<DialogTitle>
|
|
113
|
-
{plugin ? '
|
|
116
|
+
{plugin ? t('editPlugin') : t('createNewPlugin')}
|
|
114
117
|
</DialogTitle>
|
|
115
118
|
<DialogDescription>
|
|
116
119
|
{plugin
|
|
117
|
-
? '
|
|
118
|
-
: '
|
|
120
|
+
? t('updatePluginDescription')
|
|
121
|
+
: t('addPluginDescription')}
|
|
119
122
|
</DialogDescription>
|
|
120
123
|
</DialogHeader>
|
|
121
124
|
|
|
@@ -123,27 +126,27 @@ export function PluginFormDialog({
|
|
|
123
126
|
{/* Plugin Type (only for new plugins) */}
|
|
124
127
|
{!plugin && (
|
|
125
128
|
<div className="space-y-2">
|
|
126
|
-
<label className="text-sm font-medium">
|
|
129
|
+
<label className="text-sm font-medium">{t('type')}</label>
|
|
127
130
|
<select
|
|
128
131
|
value={type}
|
|
129
132
|
onChange={(e) => setType(e.target.value as 'skill' | 'command' | 'agent')}
|
|
130
133
|
className="w-full border rounded-md p-2 bg-background"
|
|
131
134
|
disabled={isSubmitting}
|
|
132
135
|
>
|
|
133
|
-
<option value="skill">
|
|
134
|
-
<option value="command">
|
|
135
|
-
<option value="agent">
|
|
136
|
+
<option value="skill">{t('skill')}</option>
|
|
137
|
+
<option value="command">{t('command')}</option>
|
|
138
|
+
<option value="agent">{t('agent')}</option>
|
|
136
139
|
</select>
|
|
137
140
|
</div>
|
|
138
141
|
)}
|
|
139
142
|
|
|
140
143
|
{/* Name */}
|
|
141
144
|
<div className="space-y-2">
|
|
142
|
-
<label className="text-sm font-medium">
|
|
145
|
+
<label className="text-sm font-medium">{t('name')} *</label>
|
|
143
146
|
<Input
|
|
144
147
|
value={name}
|
|
145
148
|
onChange={(e) => setName(e.target.value)}
|
|
146
|
-
placeholder=
|
|
149
|
+
placeholder={t('pluginName')}
|
|
147
150
|
disabled={isSubmitting}
|
|
148
151
|
required
|
|
149
152
|
/>
|
|
@@ -151,11 +154,11 @@ export function PluginFormDialog({
|
|
|
151
154
|
|
|
152
155
|
{/* Description */}
|
|
153
156
|
<div className="space-y-2">
|
|
154
|
-
<label className="text-sm font-medium">
|
|
157
|
+
<label className="text-sm font-medium">{t('pluginDescription')}</label>
|
|
155
158
|
<Input
|
|
156
159
|
value={description}
|
|
157
160
|
onChange={(e) => setDescription(e.target.value)}
|
|
158
|
-
placeholder=
|
|
161
|
+
placeholder={t('pluginDescription')}
|
|
159
162
|
disabled={isSubmitting}
|
|
160
163
|
/>
|
|
161
164
|
</div>
|
|
@@ -163,7 +166,7 @@ export function PluginFormDialog({
|
|
|
163
166
|
{/* Source Path - show for editing, read-only preview for new */}
|
|
164
167
|
{plugin ? (
|
|
165
168
|
<div className="space-y-2">
|
|
166
|
-
<label className="text-sm font-medium">
|
|
169
|
+
<label className="text-sm font-medium">{t('sourcePath')} *</label>
|
|
167
170
|
<Input
|
|
168
171
|
value={sourcePath}
|
|
169
172
|
onChange={(e) => setSourcePath(e.target.value)}
|
|
@@ -174,27 +177,27 @@ export function PluginFormDialog({
|
|
|
174
177
|
</div>
|
|
175
178
|
) : (
|
|
176
179
|
<div className="space-y-2">
|
|
177
|
-
<label className="text-sm font-medium">
|
|
180
|
+
<label className="text-sm font-medium">{t('sourcePath')}</label>
|
|
178
181
|
<div className="text-sm text-muted-foreground bg-muted/50 p-2 rounded border">
|
|
179
182
|
{previewPath}
|
|
180
183
|
</div>
|
|
181
|
-
<p className="text-xs text-muted-foreground">
|
|
184
|
+
<p className="text-xs text-muted-foreground">{t('pathAutoGenerated')}</p>
|
|
182
185
|
</div>
|
|
183
186
|
)}
|
|
184
187
|
|
|
185
188
|
{/* Storage Type (only for new plugins) */}
|
|
186
189
|
{!plugin && (
|
|
187
190
|
<div className="space-y-2">
|
|
188
|
-
<label className="text-sm font-medium">
|
|
191
|
+
<label className="text-sm font-medium">{t('storageType')}</label>
|
|
189
192
|
<select
|
|
190
193
|
value={storageType}
|
|
191
194
|
onChange={(e) => setStorageType(e.target.value as 'local' | 'imported' | 'external')}
|
|
192
195
|
className="w-full border rounded-md p-2 bg-background"
|
|
193
196
|
disabled={isSubmitting}
|
|
194
197
|
>
|
|
195
|
-
<option value="local">
|
|
196
|
-
<option value="imported">
|
|
197
|
-
<option value="external">
|
|
198
|
+
<option value="local">{t('local')}</option>
|
|
199
|
+
<option value="imported">{t('imported')}</option>
|
|
200
|
+
<option value="external">{t('external')}</option>
|
|
198
201
|
</select>
|
|
199
202
|
</div>
|
|
200
203
|
)}
|
|
@@ -214,10 +217,10 @@ export function PluginFormDialog({
|
|
|
214
217
|
onClick={() => onOpenChange(false)}
|
|
215
218
|
disabled={isSubmitting}
|
|
216
219
|
>
|
|
217
|
-
|
|
220
|
+
{tCommon('cancel')}
|
|
218
221
|
</Button>
|
|
219
222
|
<Button type="submit" disabled={isSubmitting || !name.trim()}>
|
|
220
|
-
{isSubmitting ? '
|
|
223
|
+
{isSubmitting ? tCommon('saving') : plugin ? t('update') : tCommon('create')}
|
|
221
224
|
</Button>
|
|
222
225
|
</div>
|
|
223
226
|
</form>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useEffect, useState } from 'react';
|
|
4
|
+
import { useTranslations } from 'next-intl';
|
|
4
5
|
import { Package, Plus, RefreshCw, Search, Trash2, Edit, X, Upload } from 'lucide-react';
|
|
5
6
|
import { useAgentFactoryStore } from '@/stores/agent-factory-store';
|
|
6
7
|
import { useAgentFactoryUIStore } from '@/stores/agent-factory-ui-store';
|
|
@@ -13,6 +14,8 @@ import { DiscoveryDialog } from './discovery-dialog';
|
|
|
13
14
|
import { UploadDialog } from './upload-dialog';
|
|
14
15
|
|
|
15
16
|
export function PluginList() {
|
|
17
|
+
const t = useTranslations('agentFactory');
|
|
18
|
+
const tCommon = useTranslations('common');
|
|
16
19
|
const { plugins, loading, error, fetchPlugins, deletePlugin } = useAgentFactoryStore();
|
|
17
20
|
const { setOpen: setAgentFactoryOpen } = useAgentFactoryUIStore();
|
|
18
21
|
const [filter, setFilter] = useState<'all' | 'skill' | 'command' | 'agent' | 'agent_set'>('all');
|
|
@@ -43,7 +46,7 @@ export function PluginList() {
|
|
|
43
46
|
});
|
|
44
47
|
|
|
45
48
|
const handleDelete = async (id: string) => {
|
|
46
|
-
if (!confirm('
|
|
49
|
+
if (!confirm(t('deletePluginConfirm'))) return;
|
|
47
50
|
try {
|
|
48
51
|
await deletePlugin(id);
|
|
49
52
|
} catch (error) {
|
|
@@ -82,7 +85,7 @@ export function PluginList() {
|
|
|
82
85
|
<div className="flex items-center justify-between sm:justify-normal gap-3">
|
|
83
86
|
<div className="flex items-center gap-3">
|
|
84
87
|
<Package className="w-6 h-6" />
|
|
85
|
-
<h1 className="text-2xl font-bold">
|
|
88
|
+
<h1 className="text-2xl font-bold">{t('title')}</h1>
|
|
86
89
|
</div>
|
|
87
90
|
<Button variant="ghost" size="icon" className="sm:hidden" onClick={() => setAgentFactoryOpen(false)}>
|
|
88
91
|
<X className="w-5 h-5" />
|
|
@@ -91,19 +94,19 @@ export function PluginList() {
|
|
|
91
94
|
<div className="flex flex-wrap gap-2 justify-end">
|
|
92
95
|
<Button variant="outline" size="sm" onClick={() => fetchPlugins()}>
|
|
93
96
|
<RefreshCw className="w-4 h-4 mr-2" />
|
|
94
|
-
|
|
97
|
+
{tCommon('refresh')}
|
|
95
98
|
</Button>
|
|
96
99
|
<Button variant="outline" size="sm" onClick={() => setDiscoveryOpen(true)}>
|
|
97
100
|
<Package className="w-4 h-4 mr-2" />
|
|
98
|
-
|
|
101
|
+
{tCommon('discover')}
|
|
99
102
|
</Button>
|
|
100
103
|
<Button variant="outline" size="sm" onClick={() => setUploadOpen(true)}>
|
|
101
104
|
<Upload className="w-4 h-4 mr-2" />
|
|
102
|
-
|
|
105
|
+
{tCommon('upload')}
|
|
103
106
|
</Button>
|
|
104
107
|
<Button size="sm" onClick={() => setCreateFormOpen(true)}>
|
|
105
108
|
<Plus className="w-4 h-4 mr-2" />
|
|
106
|
-
|
|
109
|
+
{tCommon('new')}
|
|
107
110
|
</Button>
|
|
108
111
|
<Button variant="ghost" size="icon" className="hidden sm:flex" onClick={() => setAgentFactoryOpen(false)}>
|
|
109
112
|
<X className="w-5 h-5" />
|
|
@@ -113,7 +116,7 @@ export function PluginList() {
|
|
|
113
116
|
|
|
114
117
|
{/* Description */}
|
|
115
118
|
<p className="text-sm text-muted-foreground mb-6">
|
|
116
|
-
|
|
119
|
+
{t('description')}
|
|
117
120
|
</p>
|
|
118
121
|
|
|
119
122
|
{/* Filters and Search */}
|
|
@@ -129,14 +132,14 @@ export function PluginList() {
|
|
|
129
132
|
: 'bg-secondary text-secondary-foreground hover:bg-secondary/80'
|
|
130
133
|
}`}
|
|
131
134
|
>
|
|
132
|
-
{type === 'all' ? '
|
|
135
|
+
{type === 'all' ? t('all') : type === 'agent_set' ? t('agentSets') : type === 'skill' ? t('skills') : type === 'command' ? t('commands') : t('agents')}
|
|
133
136
|
</button>
|
|
134
137
|
))}
|
|
135
138
|
</div>
|
|
136
139
|
<div className="flex-1 relative">
|
|
137
140
|
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
|
138
141
|
<Input
|
|
139
|
-
placeholder=
|
|
142
|
+
placeholder={t('searchPlugins')}
|
|
140
143
|
value={searchQuery}
|
|
141
144
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
142
145
|
className="pl-10"
|
|
@@ -154,7 +157,7 @@ export function PluginList() {
|
|
|
154
157
|
{/* Loading */}
|
|
155
158
|
{loading && (
|
|
156
159
|
<div className="text-center py-12 text-muted-foreground">
|
|
157
|
-
|
|
160
|
+
{t('loadingPlugins')}
|
|
158
161
|
</div>
|
|
159
162
|
)}
|
|
160
163
|
|
|
@@ -178,7 +181,7 @@ export function PluginList() {
|
|
|
178
181
|
</span>
|
|
179
182
|
</div>
|
|
180
183
|
<p className="text-sm text-muted-foreground line-clamp-2 mb-4">
|
|
181
|
-
{plugin.description || '
|
|
184
|
+
{plugin.description || t('noDescription')}
|
|
182
185
|
</p>
|
|
183
186
|
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
184
187
|
<span className="capitalize">{plugin.storageType}</span>
|
|
@@ -215,25 +218,25 @@ export function PluginList() {
|
|
|
215
218
|
{!loading && filteredPlugins.length === 0 && (
|
|
216
219
|
<div className="text-center py-12">
|
|
217
220
|
<Package className="w-12 h-12 mx-auto mb-4 text-muted-foreground" />
|
|
218
|
-
<h3 className="text-lg font-semibold mb-2">
|
|
221
|
+
<h3 className="text-lg font-semibold mb-2">{t('noPluginsFound')}</h3>
|
|
219
222
|
<p className="text-muted-foreground mb-4">
|
|
220
223
|
{searchQuery || filter !== 'all'
|
|
221
|
-
? '
|
|
222
|
-
: '
|
|
224
|
+
? t('adjustFilters')
|
|
225
|
+
: t('getStartedDiscover')}
|
|
223
226
|
</p>
|
|
224
227
|
{!searchQuery && filter === 'all' && (
|
|
225
228
|
<div className="flex justify-center gap-2">
|
|
226
229
|
<Button variant="outline" onClick={() => setDiscoveryOpen(true)}>
|
|
227
230
|
<Package className="w-4 h-4 mr-2" />
|
|
228
|
-
|
|
231
|
+
{tCommon('discover')}
|
|
229
232
|
</Button>
|
|
230
233
|
<Button variant="outline" onClick={() => setUploadOpen(true)}>
|
|
231
234
|
<Upload className="w-4 h-4 mr-2" />
|
|
232
|
-
|
|
235
|
+
{tCommon('upload')}
|
|
233
236
|
</Button>
|
|
234
237
|
<Button onClick={() => setCreateFormOpen(true)}>
|
|
235
238
|
<Plus className="w-4 h-4 mr-2" />
|
|
236
|
-
|
|
239
|
+
{tCommon('new')}
|
|
237
240
|
</Button>
|
|
238
241
|
</div>
|
|
239
242
|
)}
|