@geminilight/mindos 0.5.63 → 0.5.65
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/README.md +4 -0
- package/README_zh.md +4 -0
- package/app/app/api/ask/route.ts +12 -0
- package/app/app/api/changes/route.ts +7 -1
- package/app/app/api/file/route.ts +9 -0
- package/app/app/api/mcp/agents/route.ts +27 -1
- package/app/app/api/mcp/install-skill/route.ts +9 -24
- package/app/app/api/skills/route.ts +18 -2
- package/app/app/api/tree-version/route.ts +8 -0
- package/app/app/layout.tsx +1 -0
- package/app/app/page.tsx +1 -2
- package/app/app/view/[...path]/ViewPageClient.tsx +0 -1
- package/app/components/ActivityBar.tsx +2 -2
- package/app/components/Backlinks.tsx +5 -5
- package/app/components/CreateSpaceModal.tsx +3 -2
- package/app/components/DirPicker.tsx +1 -1
- package/app/components/DirView.tsx +2 -3
- package/app/components/EditorWrapper.tsx +3 -3
- package/app/components/FileTree.tsx +25 -10
- package/app/components/GuideCard.tsx +4 -4
- package/app/components/HomeContent.tsx +44 -14
- package/app/components/MarkdownView.tsx +2 -2
- package/app/components/OnboardingView.tsx +1 -1
- package/app/components/Panel.tsx +1 -1
- package/app/components/RightAgentDetailPanel.tsx +2 -1
- package/app/components/RightAskPanel.tsx +1 -1
- package/app/components/SearchModal.tsx +10 -2
- package/app/components/SidebarLayout.tsx +36 -10
- package/app/components/ThemeToggle.tsx +1 -1
- package/app/components/agents/AgentDetailContent.tsx +454 -59
- package/app/components/agents/AgentsContentPage.tsx +89 -20
- package/app/components/agents/AgentsMcpSection.tsx +513 -85
- package/app/components/agents/AgentsOverviewSection.tsx +418 -59
- package/app/components/agents/AgentsPrimitives.tsx +335 -0
- package/app/components/agents/AgentsSkillsSection.tsx +746 -105
- package/app/components/agents/SkillDetailPopover.tsx +416 -0
- package/app/components/agents/agents-content-model.ts +308 -10
- package/app/components/ask/AskContent.tsx +34 -5
- package/app/components/ask/FileChip.tsx +1 -0
- package/app/components/ask/MentionPopover.tsx +13 -1
- package/app/components/ask/MessageList.tsx +5 -7
- package/app/components/ask/ToolCallBlock.tsx +4 -4
- package/app/components/changes/ChangesBanner.tsx +89 -13
- package/app/components/changes/ChangesContentPage.tsx +134 -51
- package/app/components/echo/EchoHero.tsx +10 -24
- package/app/components/echo/EchoInsightCollapsible.tsx +52 -43
- package/app/components/echo/EchoPageSections.tsx +13 -9
- package/app/components/echo/EchoSegmentNav.tsx +14 -11
- package/app/components/echo/EchoSegmentPageClient.tsx +64 -43
- package/app/components/explore/ExploreContent.tsx +3 -7
- package/app/components/explore/UseCaseCard.tsx +4 -15
- package/app/components/panels/AgentsPanel.tsx +22 -128
- package/app/components/panels/AgentsPanelAgentDetail.tsx +7 -6
- package/app/components/panels/AgentsPanelAgentGroups.tsx +8 -13
- package/app/components/panels/AgentsPanelAgentListRow.tsx +39 -16
- package/app/components/panels/AgentsPanelHubNav.tsx +12 -12
- package/app/components/panels/EchoPanel.tsx +8 -10
- package/app/components/panels/PanelNavRow.tsx +9 -2
- package/app/components/panels/PluginsPanel.tsx +5 -5
- package/app/components/renderers/agent-inspector/AgentInspectorRenderer.tsx +30 -8
- package/app/components/renderers/agent-inspector/manifest.ts +5 -3
- package/app/components/renderers/config/manifest.ts +1 -0
- package/app/components/renderers/csv/manifest.ts +1 -0
- package/app/components/renderers/todo/manifest.ts +1 -0
- package/app/components/settings/AiTab.tsx +3 -3
- package/app/components/settings/AppearanceTab.tsx +2 -2
- package/app/components/settings/KnowledgeTab.tsx +3 -3
- package/app/components/settings/McpAgentInstall.tsx +3 -6
- package/app/components/settings/McpSkillCreateForm.tsx +2 -3
- package/app/components/settings/McpSkillRow.tsx +2 -3
- package/app/components/settings/McpSkillsSection.tsx +2 -2
- package/app/components/settings/McpTab.tsx +12 -13
- package/app/components/settings/MonitoringTab.tsx +13 -13
- package/app/components/settings/PluginsTab.tsx +6 -5
- package/app/components/settings/Primitives.tsx +3 -4
- package/app/components/settings/SettingsContent.tsx +3 -3
- package/app/components/settings/SyncTab.tsx +11 -17
- package/app/components/settings/UpdateTab.tsx +18 -21
- package/app/components/settings/types.ts +14 -0
- package/app/components/setup/StepKB.tsx +1 -1
- package/app/hooks/useMcpData.tsx +7 -4
- package/app/hooks/useMention.ts +25 -8
- package/app/lib/agent/log.ts +15 -18
- package/app/lib/agent/stream-consumer.ts +3 -0
- package/app/lib/agent/to-agent-messages.ts +6 -4
- package/app/lib/core/agent-audit-log.ts +280 -0
- package/app/lib/core/content-changes.ts +148 -8
- package/app/lib/core/index.ts +11 -0
- package/app/lib/fs.ts +16 -1
- package/app/lib/i18n-en.ts +317 -36
- package/app/lib/i18n-zh.ts +316 -35
- package/app/lib/mcp-agents.ts +273 -2
- package/app/lib/renderers/index.ts +1 -2
- package/app/lib/renderers/registry.ts +10 -0
- package/app/lib/types.ts +2 -0
- package/app/next-env.d.ts +1 -1
- package/bin/lib/mcp-agents.js +38 -13
- package/package.json +1 -1
- package/scripts/migrate-agent-audit-log.js +170 -0
- package/scripts/migrate-agent-diff.js +146 -0
- package/scripts/setup.js +12 -17
- package/skills/plugin-core-builtin-migration/SKILL.md +178 -0
- package/app/components/renderers/diff/DiffRenderer.tsx +0 -311
- package/app/components/renderers/diff/manifest.ts +0 -14
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
5
|
+
import { ArrowUpRight, Bookmark, Brain, Check, History, Sun, UserRound } from 'lucide-react';
|
|
4
6
|
import type { EchoSegment } from '@/lib/echo-segments';
|
|
5
7
|
import { buildEchoInsightUserPrompt } from '@/lib/echo-insight-prompt';
|
|
6
8
|
import type { Locale, Messages } from '@/lib/i18n';
|
|
@@ -44,12 +46,20 @@ function segmentLead(segment: EchoSegment, p: ReturnType<typeof useLocale>['t'][
|
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
const SEGMENT_ICON: Record<EchoSegment, ReactNode> = {
|
|
50
|
+
'about-you': <UserRound size={18} strokeWidth={1.75} />,
|
|
51
|
+
continued: <Bookmark size={18} strokeWidth={1.75} />,
|
|
52
|
+
daily: <Sun size={18} strokeWidth={1.75} />,
|
|
53
|
+
'past-you': <History size={18} strokeWidth={1.75} />,
|
|
54
|
+
growth: <Brain size={18} strokeWidth={1.75} />,
|
|
55
|
+
};
|
|
56
|
+
|
|
47
57
|
const fieldLabelClass =
|
|
48
58
|
'block font-sans text-2xs font-semibold uppercase tracking-wide text-muted-foreground';
|
|
49
59
|
const inputClass =
|
|
50
|
-
'mt-2 w-full min-h-[5rem] resize-y rounded-lg border border-border bg-background px-3 py-2.5 font-sans text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring';
|
|
60
|
+
'mt-2 w-full min-h-[5rem] resize-y rounded-lg border border-border bg-background px-3 py-2.5 font-sans text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:border-[var(--amber)]/40';
|
|
51
61
|
const cardSectionClass =
|
|
52
|
-
'rounded-xl border border-border bg-card p-5 shadow-sm transition-[border-color,box-shadow] duration-150 ease-out hover:border-[var(--amber)]/20 hover:shadow
|
|
62
|
+
'rounded-xl border border-border bg-card p-5 shadow-sm transition-[border-color,box-shadow] duration-150 ease-out hover:border-[var(--amber)]/20 hover:shadow sm:p-6';
|
|
53
63
|
|
|
54
64
|
function echoSnapshotCopy(segment: EchoSegment, p: Messages['echoPages']): { title: string; body: string } {
|
|
55
65
|
switch (segment) {
|
|
@@ -77,6 +87,15 @@ export default function EchoSegmentPageClient({ segment }: { segment: EchoSegmen
|
|
|
77
87
|
|
|
78
88
|
const [dailyLine, setDailyLine] = useState('');
|
|
79
89
|
const [growthIntent, setGrowthIntent] = useState('');
|
|
90
|
+
const [dailySaved, setDailySaved] = useState(false);
|
|
91
|
+
const [growthSaved, setGrowthSaved] = useState(false);
|
|
92
|
+
const dailySavedTimer = useRef<ReturnType<typeof setTimeout>>(undefined);
|
|
93
|
+
const growthSavedTimer = useRef<ReturnType<typeof setTimeout>>(undefined);
|
|
94
|
+
|
|
95
|
+
useEffect(() => () => {
|
|
96
|
+
clearTimeout(dailySavedTimer.current);
|
|
97
|
+
clearTimeout(growthSavedTimer.current);
|
|
98
|
+
}, []);
|
|
80
99
|
|
|
81
100
|
const snapshot = useMemo(() => echoSnapshotCopy(segment, p), [segment, p]);
|
|
82
101
|
|
|
@@ -97,6 +116,9 @@ export default function EchoSegmentPageClient({ segment }: { segment: EchoSegmen
|
|
|
97
116
|
} catch {
|
|
98
117
|
/* ignore */
|
|
99
118
|
}
|
|
119
|
+
clearTimeout(dailySavedTimer.current);
|
|
120
|
+
setDailySaved(true);
|
|
121
|
+
dailySavedTimer.current = setTimeout(() => setDailySaved(false), 1800);
|
|
100
122
|
}, [dailyLine]);
|
|
101
123
|
|
|
102
124
|
const persistGrowth = useCallback(() => {
|
|
@@ -105,6 +127,9 @@ export default function EchoSegmentPageClient({ segment }: { segment: EchoSegmen
|
|
|
105
127
|
} catch {
|
|
106
128
|
/* ignore */
|
|
107
129
|
}
|
|
130
|
+
clearTimeout(growthSavedTimer.current);
|
|
131
|
+
setGrowthSaved(true);
|
|
132
|
+
growthSavedTimer.current = setTimeout(() => setGrowthSaved(false), 1800);
|
|
108
133
|
}, [growthIntent]);
|
|
109
134
|
|
|
110
135
|
const openDailyAsk = useCallback(() => {
|
|
@@ -150,7 +175,14 @@ export default function EchoSegmentPageClient({ segment }: { segment: EchoSegmen
|
|
|
150
175
|
);
|
|
151
176
|
|
|
152
177
|
const secondaryBtnClass =
|
|
153
|
-
'inline-flex items-center rounded-lg border border-border bg-background px-4 py-2.5 font-sans text-sm font-medium text-foreground transition-colors duration-150 hover:border-[var(--amber)]/35 hover:bg-[var(--amber-dim)]/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring';
|
|
178
|
+
'inline-flex items-center gap-1.5 rounded-lg border border-border bg-background px-4 py-2.5 font-sans text-sm font-medium text-foreground transition-colors duration-150 hover:border-[var(--amber)]/35 hover:bg-[var(--amber-dim)]/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring';
|
|
179
|
+
|
|
180
|
+
const agentBtn = (onClick: () => void) => (
|
|
181
|
+
<button type="button" onClick={onClick} className={secondaryBtnClass}>
|
|
182
|
+
{p.continueAgent}
|
|
183
|
+
<ArrowUpRight size={14} className="shrink-0 text-muted-foreground" aria-hidden />
|
|
184
|
+
</button>
|
|
185
|
+
);
|
|
154
186
|
|
|
155
187
|
return (
|
|
156
188
|
<article
|
|
@@ -158,16 +190,13 @@ export default function EchoSegmentPageClient({ segment }: { segment: EchoSegmen
|
|
|
158
190
|
aria-labelledby={pageTitleId}
|
|
159
191
|
>
|
|
160
192
|
<EchoHero
|
|
161
|
-
breadcrumbNav={p.breadcrumbNav}
|
|
162
|
-
parentHref="/echo/about-you"
|
|
163
|
-
parent={p.parent}
|
|
164
193
|
heroKicker={p.heroKicker}
|
|
165
194
|
pageTitle={title}
|
|
166
195
|
lead={lead}
|
|
167
196
|
titleId={pageTitleId}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
197
|
+
>
|
|
198
|
+
<EchoSegmentNav activeSegment={segment} />
|
|
199
|
+
</EchoHero>
|
|
171
200
|
|
|
172
201
|
<div className="mt-6 space-y-6 sm:mt-8">
|
|
173
202
|
<EchoFactSnapshot
|
|
@@ -176,24 +205,15 @@ export default function EchoSegmentPageClient({ segment }: { segment: EchoSegmen
|
|
|
176
205
|
snapshotBadge={p.snapshotBadge}
|
|
177
206
|
emptyTitle={snapshot.title}
|
|
178
207
|
emptyBody={snapshot.body}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
<button type="button" onClick={openSegmentAsk} className={secondaryBtnClass}>
|
|
182
|
-
{p.continueAgent}
|
|
183
|
-
</button>
|
|
184
|
-
) : undefined
|
|
185
|
-
}
|
|
208
|
+
icon={SEGMENT_ICON[segment]}
|
|
209
|
+
actions={segment === 'about-you' ? agentBtn(openSegmentAsk) : undefined}
|
|
186
210
|
/>
|
|
187
211
|
{segment === 'continued' ? (
|
|
188
212
|
<EchoContinuedGroups
|
|
189
213
|
draftsLabel={p.continuedDrafts}
|
|
190
214
|
todosLabel={p.continuedTodos}
|
|
191
215
|
subEmptyHint={p.subEmptyHint}
|
|
192
|
-
footer={
|
|
193
|
-
<button type="button" onClick={openSegmentAsk} className={secondaryBtnClass}>
|
|
194
|
-
{p.continueAgent}
|
|
195
|
-
</button>
|
|
196
|
-
}
|
|
216
|
+
footer={agentBtn(openSegmentAsk)}
|
|
197
217
|
/>
|
|
198
218
|
) : null}
|
|
199
219
|
</div>
|
|
@@ -212,11 +232,14 @@ export default function EchoSegmentPageClient({ segment }: { segment: EchoSegmen
|
|
|
212
232
|
placeholder={p.dailyLinePlaceholder}
|
|
213
233
|
className={inputClass}
|
|
214
234
|
/>
|
|
215
|
-
<p className="mt-3 font-sans text-2xs text-muted-foreground">
|
|
235
|
+
<p className="mt-3 flex items-center gap-2 font-sans text-2xs text-muted-foreground">
|
|
236
|
+
<span>{p.dailySavedNote}</span>
|
|
237
|
+
<span className="inline-flex items-center gap-1 text-[var(--success)]" aria-live="polite">
|
|
238
|
+
{dailySaved ? <><Check size={12} aria-hidden /> {p.savedFlash}</> : null}
|
|
239
|
+
</span>
|
|
240
|
+
</p>
|
|
216
241
|
<div className="mt-4">
|
|
217
|
-
|
|
218
|
-
{p.continueAgent}
|
|
219
|
-
</button>
|
|
242
|
+
{agentBtn(openDailyAsk)}
|
|
220
243
|
</div>
|
|
221
244
|
</section>
|
|
222
245
|
) : null}
|
|
@@ -235,31 +258,29 @@ export default function EchoSegmentPageClient({ segment }: { segment: EchoSegmen
|
|
|
235
258
|
placeholder={p.growthIntentPlaceholder}
|
|
236
259
|
className={`${inputClass} min-h-[6.5rem]`}
|
|
237
260
|
/>
|
|
238
|
-
<p className="mt-3 font-sans text-2xs text-muted-foreground">
|
|
261
|
+
<p className="mt-3 flex items-center gap-2 font-sans text-2xs text-muted-foreground">
|
|
262
|
+
<span>{p.growthSavedNote}</span>
|
|
263
|
+
<span className="inline-flex items-center gap-1 text-[var(--success)]" aria-live="polite">
|
|
264
|
+
{growthSaved ? <><Check size={12} aria-hidden /> {p.savedFlash}</> : null}
|
|
265
|
+
</span>
|
|
266
|
+
</p>
|
|
239
267
|
<div className="mt-4 border-t border-border/60 pt-4">
|
|
240
|
-
|
|
241
|
-
{p.continueAgent}
|
|
242
|
-
</button>
|
|
268
|
+
{agentBtn(openSegmentAsk)}
|
|
243
269
|
</div>
|
|
244
270
|
</section>
|
|
245
271
|
) : null}
|
|
246
272
|
|
|
247
273
|
{segment === 'past-you' ? (
|
|
248
274
|
<section className={`${cardSectionClass} mt-6`}>
|
|
249
|
-
<
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
>
|
|
256
|
-
{p.pastYouAnother}
|
|
257
|
-
</button>
|
|
258
|
-
<p className="mt-3 font-sans text-2xs text-muted-foreground">{p.pastYouDisabledHint}</p>
|
|
275
|
+
<div className="flex items-center gap-3">
|
|
276
|
+
<span className={fieldLabelClass}>{p.pastYouDrawLabel}</span>
|
|
277
|
+
<span className="rounded-full bg-muted px-2 py-0.5 font-sans text-2xs font-medium text-muted-foreground">
|
|
278
|
+
{p.pastYouComingSoon}
|
|
279
|
+
</span>
|
|
280
|
+
</div>
|
|
281
|
+
<p className="mt-3 font-sans text-sm leading-relaxed text-muted-foreground">{p.pastYouDisabledHint}</p>
|
|
259
282
|
<div className="mt-4 border-t border-border/60 pt-4">
|
|
260
|
-
|
|
261
|
-
{p.continueAgent}
|
|
262
|
-
</button>
|
|
283
|
+
{agentBtn(openSegmentAsk)}
|
|
263
284
|
</div>
|
|
264
285
|
</section>
|
|
265
286
|
) : null}
|
|
@@ -31,17 +31,13 @@ export default function ExploreContent() {
|
|
|
31
31
|
{/* Header */}
|
|
32
32
|
<div className="mb-8">
|
|
33
33
|
<div className="flex items-center gap-2 mb-3">
|
|
34
|
-
<div className="w-1 h-5 rounded-full
|
|
35
|
-
<h1
|
|
36
|
-
className="text-2xl font-semibold tracking-tight font-display"
|
|
37
|
-
style={{ color: 'var(--foreground)' }}
|
|
38
|
-
>
|
|
34
|
+
<div className="w-1 h-5 rounded-full bg-[var(--amber)]" />
|
|
35
|
+
<h1 className="text-2xl font-semibold tracking-tight font-display text-foreground">
|
|
39
36
|
{e.title}
|
|
40
37
|
</h1>
|
|
41
38
|
</div>
|
|
42
39
|
<p
|
|
43
|
-
className="text-sm leading-relaxed"
|
|
44
|
-
style={{ color: 'var(--muted-foreground)', paddingLeft: '1rem' }}
|
|
40
|
+
className="text-sm leading-relaxed text-muted-foreground pl-4"
|
|
45
41
|
>
|
|
46
42
|
{e.subtitle}
|
|
47
43
|
</p>
|
|
@@ -13,35 +13,24 @@ interface UseCaseCardProps {
|
|
|
13
13
|
export default function UseCaseCard({ icon, title, description, prompt, tryItLabel }: UseCaseCardProps) {
|
|
14
14
|
return (
|
|
15
15
|
<div
|
|
16
|
-
className="group flex flex-col gap-3 p-4 rounded-xl border transition-all duration-150 hover:border-amber
|
|
17
|
-
style={{ borderColor: 'var(--border)', background: 'var(--card)' }}
|
|
16
|
+
className="group flex flex-col gap-3 p-4 rounded-xl border border-border bg-card transition-all duration-150 hover:border-[var(--amber)]/30 hover:bg-muted/50"
|
|
18
17
|
>
|
|
19
18
|
<div className="flex items-start gap-3">
|
|
20
19
|
<span className="text-xl leading-none shrink-0 mt-0.5" suppressHydrationWarning>
|
|
21
20
|
{icon}
|
|
22
21
|
</span>
|
|
23
22
|
<div className="flex-1 min-w-0">
|
|
24
|
-
<h3
|
|
25
|
-
className="text-sm font-semibold font-display truncate"
|
|
26
|
-
style={{ color: 'var(--foreground)' }}
|
|
27
|
-
>
|
|
23
|
+
<h3 className="text-sm font-semibold font-display truncate text-foreground">
|
|
28
24
|
{title}
|
|
29
25
|
</h3>
|
|
30
|
-
<p
|
|
31
|
-
className="text-xs leading-relaxed mt-1 line-clamp-2"
|
|
32
|
-
style={{ color: 'var(--muted-foreground)' }}
|
|
33
|
-
>
|
|
26
|
+
<p className="text-xs leading-relaxed mt-1 line-clamp-2 text-muted-foreground">
|
|
34
27
|
{description}
|
|
35
28
|
</p>
|
|
36
29
|
</div>
|
|
37
30
|
</div>
|
|
38
31
|
<button
|
|
39
32
|
onClick={() => openAskModal(prompt, 'user')}
|
|
40
|
-
className="self-start inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium transition-all duration-150 hover:opacity-80 cursor-pointer"
|
|
41
|
-
style={{
|
|
42
|
-
background: 'var(--amber-dim)',
|
|
43
|
-
color: 'var(--amber)',
|
|
44
|
-
}}
|
|
33
|
+
className="self-start inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium transition-all duration-150 hover:opacity-80 cursor-pointer bg-[var(--amber-dim)] text-[var(--amber)]"
|
|
45
34
|
>
|
|
46
35
|
{tryItLabel} →
|
|
47
36
|
</button>
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useState
|
|
4
|
-
import {
|
|
5
|
-
import { Loader2, RefreshCw,
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { usePathname } from 'next/navigation';
|
|
5
|
+
import { Loader2, RefreshCw, Settings } from 'lucide-react';
|
|
6
6
|
import { useMcpData } from '@/hooks/useMcpData';
|
|
7
7
|
import { useLocale } from '@/lib/LocaleContext';
|
|
8
|
-
import { Toggle } from '../settings/Primitives';
|
|
9
|
-
import type { SkillInfo } from '../settings/types';
|
|
10
8
|
import PanelHeader from './PanelHeader';
|
|
11
9
|
import { AgentsPanelHubNav } from './AgentsPanelHubNav';
|
|
12
10
|
import { AgentsPanelAgentGroups } from './AgentsPanelAgentGroups';
|
|
@@ -15,9 +13,7 @@ interface AgentsPanelProps {
|
|
|
15
13
|
active: boolean;
|
|
16
14
|
maximized?: boolean;
|
|
17
15
|
onMaximize?: () => void;
|
|
18
|
-
/** Highlights the row for the agent whose detail is open in the right dock. */
|
|
19
16
|
selectedAgentKey?: string | null;
|
|
20
|
-
onOpenAgentDetail?: (key: string) => void;
|
|
21
17
|
}
|
|
22
18
|
|
|
23
19
|
export default function AgentsPanel({
|
|
@@ -25,18 +21,13 @@ export default function AgentsPanel({
|
|
|
25
21
|
maximized,
|
|
26
22
|
onMaximize,
|
|
27
23
|
selectedAgentKey = null,
|
|
28
|
-
onOpenAgentDetail,
|
|
29
24
|
}: AgentsPanelProps) {
|
|
30
25
|
const { t } = useLocale();
|
|
31
|
-
const router = useRouter();
|
|
32
26
|
const p = t.panels.agents;
|
|
33
27
|
const mcp = useMcpData();
|
|
28
|
+
const pathname = usePathname();
|
|
34
29
|
const [refreshing, setRefreshing] = useState(false);
|
|
35
30
|
const [showNotDetected, setShowNotDetected] = useState(false);
|
|
36
|
-
const [showBuiltinSkills, setShowBuiltinSkills] = useState(false);
|
|
37
|
-
|
|
38
|
-
const overviewRef = useRef<HTMLDivElement>(null);
|
|
39
|
-
const skillsRef = useRef<HTMLDivElement>(null);
|
|
40
31
|
|
|
41
32
|
const handleRefresh = async () => {
|
|
42
33
|
setRefreshing(true);
|
|
@@ -44,10 +35,6 @@ export default function AgentsPanel({
|
|
|
44
35
|
setRefreshing(false);
|
|
45
36
|
};
|
|
46
37
|
|
|
47
|
-
const scrollTo = useCallback((el: HTMLElement | null) => {
|
|
48
|
-
el?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
49
|
-
}, []);
|
|
50
|
-
|
|
51
38
|
const openAdvancedConfig = () => {
|
|
52
39
|
window.dispatchEvent(new CustomEvent('mindos:open-settings', { detail: { tab: 'mcp' } }));
|
|
53
40
|
};
|
|
@@ -56,13 +43,23 @@ export default function AgentsPanel({
|
|
|
56
43
|
const detected = mcp.agents.filter(a => a.present && !a.installed);
|
|
57
44
|
const notFound = mcp.agents.filter(a => !a.present);
|
|
58
45
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
46
|
+
const installAgentWithRefresh = async (key: string) => {
|
|
47
|
+
const ok = await mcp.installAgent(key);
|
|
48
|
+
if (ok) await mcp.refresh();
|
|
49
|
+
return ok;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const routeSelectedAgentKey = pathname?.startsWith('/agents/')
|
|
53
|
+
? decodeURIComponent(pathname.slice('/agents/'.length))
|
|
54
|
+
: null;
|
|
55
|
+
const effectiveSelectedAgentKey = routeSelectedAgentKey ?? selectedAgentKey;
|
|
62
56
|
|
|
63
57
|
const listCopy = {
|
|
64
58
|
installing: p.installing,
|
|
65
59
|
install: p.install,
|
|
60
|
+
installSuccess: p.installSuccess,
|
|
61
|
+
installFailed: p.installFailed,
|
|
62
|
+
retryInstall: p.retryInstall,
|
|
66
63
|
};
|
|
67
64
|
|
|
68
65
|
const hubCopy = {
|
|
@@ -75,10 +72,6 @@ export default function AgentsPanel({
|
|
|
75
72
|
<AgentsPanelHubNav
|
|
76
73
|
copy={hubCopy}
|
|
77
74
|
connectedCount={connected.length}
|
|
78
|
-
overviewRef={overviewRef}
|
|
79
|
-
skillsRef={skillsRef}
|
|
80
|
-
scrollTo={scrollTo}
|
|
81
|
-
openAdvancedConfig={openAdvancedConfig}
|
|
82
75
|
/>
|
|
83
76
|
);
|
|
84
77
|
|
|
@@ -91,15 +84,6 @@ export default function AgentsPanel({
|
|
|
91
84
|
{connected.length} {p.connected}
|
|
92
85
|
</span>
|
|
93
86
|
)}
|
|
94
|
-
<button
|
|
95
|
-
onClick={() => router.push('/agents')}
|
|
96
|
-
className="px-2 py-1 rounded border border-border text-2xs text-muted-foreground hover:text-foreground hover:bg-muted transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
97
|
-
aria-label={p.openDashboard}
|
|
98
|
-
title={p.openDashboard}
|
|
99
|
-
type="button"
|
|
100
|
-
>
|
|
101
|
-
{p.openDashboard}
|
|
102
|
-
</button>
|
|
103
87
|
<button
|
|
104
88
|
onClick={handleRefresh}
|
|
105
89
|
disabled={refreshing}
|
|
@@ -122,28 +106,13 @@ export default function AgentsPanel({
|
|
|
122
106
|
<div className="flex flex-col gap-2 py-4 px-0">
|
|
123
107
|
{hub}
|
|
124
108
|
<div className="mx-4 border-t border-border" />
|
|
125
|
-
<div
|
|
126
|
-
<
|
|
127
|
-
{mcp.status?.running ? (
|
|
128
|
-
<span className="flex items-center gap-1.5 text-[11px]">
|
|
129
|
-
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500 inline-block" />
|
|
130
|
-
<span className="text-emerald-600 dark:text-emerald-400">:{mcp.status.port}</span>
|
|
131
|
-
<span className="text-muted-foreground">· {mcp.status.toolCount} tools</span>
|
|
132
|
-
</span>
|
|
133
|
-
) : (
|
|
134
|
-
<span className="flex items-center gap-1.5 text-[11px]">
|
|
135
|
-
<span className="w-1.5 h-1.5 rounded-full bg-zinc-400 inline-block" />
|
|
136
|
-
<span className="text-muted-foreground">{p.stopped}</span>
|
|
137
|
-
</span>
|
|
138
|
-
)}
|
|
139
|
-
</div>
|
|
140
|
-
<div ref={skillsRef} className="mx-3 scroll-mt-2 rounded-lg border border-dashed border-border px-3 py-3 text-center">
|
|
141
|
-
<p className="text-xs text-muted-foreground mb-2">{p.noAgents}</p>
|
|
109
|
+
<div className="mx-3 rounded-lg border border-dashed border-border px-3 py-4 text-center">
|
|
110
|
+
<p className="text-xs text-muted-foreground mb-1.5">{p.noAgents}</p>
|
|
142
111
|
<p className="text-2xs text-muted-foreground mb-3">{p.skillsEmptyHint}</p>
|
|
143
112
|
<button
|
|
144
113
|
onClick={handleRefresh}
|
|
145
114
|
type="button"
|
|
146
|
-
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
115
|
+
className="inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
147
116
|
>
|
|
148
117
|
<RefreshCw size={11} /> {p.retry}
|
|
149
118
|
</button>
|
|
@@ -156,30 +125,13 @@ export default function AgentsPanel({
|
|
|
156
125
|
<div className="mx-4 border-t border-border" />
|
|
157
126
|
|
|
158
127
|
<div className="px-3 py-3 space-y-4">
|
|
159
|
-
<div ref={overviewRef} className="rounded-lg border border-border bg-card/50 px-3 py-2.5 flex items-center justify-between scroll-mt-2">
|
|
160
|
-
<span className="text-xs font-medium text-foreground">{p.mcpServer}</span>
|
|
161
|
-
{mcp.status?.running ? (
|
|
162
|
-
<span className="flex items-center gap-1.5 text-[11px]">
|
|
163
|
-
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500 inline-block" />
|
|
164
|
-
<span className="text-emerald-600 dark:text-emerald-400">:{mcp.status.port}</span>
|
|
165
|
-
<span className="text-muted-foreground">· {mcp.status.toolCount} tools</span>
|
|
166
|
-
</span>
|
|
167
|
-
) : (
|
|
168
|
-
<span className="flex items-center gap-1.5 text-[11px]">
|
|
169
|
-
<span className="w-1.5 h-1.5 rounded-full bg-zinc-400 inline-block" />
|
|
170
|
-
<span className="text-muted-foreground">{p.stopped}</span>
|
|
171
|
-
</span>
|
|
172
|
-
)}
|
|
173
|
-
</div>
|
|
174
|
-
|
|
175
128
|
<AgentsPanelAgentGroups
|
|
176
129
|
connected={connected}
|
|
177
130
|
detected={detected}
|
|
178
131
|
notFound={notFound}
|
|
179
|
-
|
|
180
|
-
selectedAgentKey={selectedAgentKey}
|
|
181
|
-
mcp={mcp}
|
|
132
|
+
selectedAgentKey={effectiveSelectedAgentKey}
|
|
182
133
|
listCopy={listCopy}
|
|
134
|
+
onInstallAgent={installAgentWithRefresh}
|
|
183
135
|
showNotDetected={showNotDetected}
|
|
184
136
|
setShowNotDetected={setShowNotDetected}
|
|
185
137
|
p={{
|
|
@@ -189,55 +141,6 @@ export default function AgentsPanel({
|
|
|
189
141
|
sectionNotDetected: p.sectionNotDetected,
|
|
190
142
|
}}
|
|
191
143
|
/>
|
|
192
|
-
|
|
193
|
-
<section ref={skillsRef} className="scroll-mt-2">
|
|
194
|
-
{mcp.skills.length > 0 ? (
|
|
195
|
-
<>
|
|
196
|
-
<div className="flex items-center justify-between mb-2">
|
|
197
|
-
<h3 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wider">
|
|
198
|
-
{p.skillsTitle} <span className="normal-case font-normal">{activeSkillCount} {p.skillsActive}</span>
|
|
199
|
-
</h3>
|
|
200
|
-
<button
|
|
201
|
-
type="button"
|
|
202
|
-
onClick={openAdvancedConfig}
|
|
203
|
-
className="text-2xs text-muted-foreground hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
|
|
204
|
-
>
|
|
205
|
-
{p.newSkill}
|
|
206
|
-
</button>
|
|
207
|
-
</div>
|
|
208
|
-
|
|
209
|
-
{customSkills.length > 0 && (
|
|
210
|
-
<div className="space-y-0.5 mb-2">
|
|
211
|
-
{customSkills.map(skill => (
|
|
212
|
-
<SkillRow key={skill.name} skill={skill} onToggle={mcp.toggleSkill} />
|
|
213
|
-
))}
|
|
214
|
-
</div>
|
|
215
|
-
)}
|
|
216
|
-
|
|
217
|
-
{builtinSkills.length > 0 && (
|
|
218
|
-
<>
|
|
219
|
-
<button
|
|
220
|
-
type="button"
|
|
221
|
-
onClick={() => setShowBuiltinSkills(!showBuiltinSkills)}
|
|
222
|
-
className="flex items-center gap-1 text-2xs text-muted-foreground hover:text-foreground transition-colors mb-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
|
|
223
|
-
>
|
|
224
|
-
{showBuiltinSkills ? <ChevronDown size={10} /> : <ChevronRight size={10} />}
|
|
225
|
-
{p.builtinSkills} ({builtinSkills.length})
|
|
226
|
-
</button>
|
|
227
|
-
{showBuiltinSkills && (
|
|
228
|
-
<div className="space-y-0.5">
|
|
229
|
-
{builtinSkills.map(skill => (
|
|
230
|
-
<SkillRow key={skill.name} skill={skill} onToggle={mcp.toggleSkill} />
|
|
231
|
-
))}
|
|
232
|
-
</div>
|
|
233
|
-
)}
|
|
234
|
-
</>
|
|
235
|
-
)}
|
|
236
|
-
</>
|
|
237
|
-
) : (
|
|
238
|
-
<p className="text-2xs text-muted-foreground py-1">{p.skillsEmptyHint}</p>
|
|
239
|
-
)}
|
|
240
|
-
</section>
|
|
241
144
|
</div>
|
|
242
145
|
</div>
|
|
243
146
|
)}
|
|
@@ -256,12 +159,3 @@ export default function AgentsPanel({
|
|
|
256
159
|
</div>
|
|
257
160
|
);
|
|
258
161
|
}
|
|
259
|
-
|
|
260
|
-
function SkillRow({ skill, onToggle }: { skill: SkillInfo; onToggle: (name: string, enabled: boolean) => void }) {
|
|
261
|
-
return (
|
|
262
|
-
<div className="flex items-center justify-between gap-2 px-2 py-1.5 rounded-md hover:bg-muted/30 transition-colors">
|
|
263
|
-
<span className="text-xs text-foreground truncate">{skill.name}</span>
|
|
264
|
-
<Toggle size="sm" checked={skill.enabled} onChange={v => onToggle(skill.name, v)} />
|
|
265
|
-
</div>
|
|
266
|
-
);
|
|
267
|
-
}
|
|
@@ -12,7 +12,8 @@ export type { AgentsPanelAgentDetailStatus };
|
|
|
12
12
|
export interface AgentsPanelAgentDetailCopy {
|
|
13
13
|
connected: string;
|
|
14
14
|
installing: string;
|
|
15
|
-
install:
|
|
15
|
+
install: string;
|
|
16
|
+
installFailed: string;
|
|
16
17
|
copyConfig: string;
|
|
17
18
|
copied: string;
|
|
18
19
|
transportLocal: string;
|
|
@@ -61,7 +62,7 @@ export default function AgentsPanelAgentDetail({
|
|
|
61
62
|
setResult(
|
|
62
63
|
ok
|
|
63
64
|
? { type: 'success', text: `${agent.name} ${copy.connected}` }
|
|
64
|
-
: { type: 'error', text:
|
|
65
|
+
: { type: 'error', text: copy.installFailed },
|
|
65
66
|
);
|
|
66
67
|
setInstalling(false);
|
|
67
68
|
};
|
|
@@ -76,7 +77,7 @@ export default function AgentsPanelAgentDetail({
|
|
|
76
77
|
};
|
|
77
78
|
|
|
78
79
|
const dot =
|
|
79
|
-
agentStatus === 'connected' ? 'bg-
|
|
80
|
+
agentStatus === 'connected' ? 'bg-[var(--success)]' : agentStatus === 'detected' ? 'bg-[var(--amber)]' : 'bg-muted-foreground';
|
|
80
81
|
|
|
81
82
|
return (
|
|
82
83
|
<div className="flex flex-col h-full min-h-0">
|
|
@@ -117,14 +118,14 @@ export default function AgentsPanelAgentDetail({
|
|
|
117
118
|
type="button"
|
|
118
119
|
onClick={handleInstall}
|
|
119
120
|
disabled={installing}
|
|
120
|
-
className="inline-flex items-center gap-1.5 px-3 py-2 text-xs rounded-lg font-medium text-[var(--amber
|
|
121
|
+
className="inline-flex items-center gap-1.5 px-3 py-2 text-xs rounded-lg font-medium text-white disabled:opacity-50 bg-[var(--amber)] hover:bg-[var(--amber)]/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
121
122
|
>
|
|
122
123
|
{installing ? <Loader2 size={14} className="animate-spin" /> : null}
|
|
123
|
-
{installing ? copy.installing : copy.install
|
|
124
|
+
{installing ? copy.installing : copy.install}
|
|
124
125
|
</button>
|
|
125
126
|
{result && (
|
|
126
127
|
<span
|
|
127
|
-
className={`flex items-center gap-1 text-2xs ${result.type === 'success' ? 'text-
|
|
128
|
+
className={`flex items-center gap-1 text-2xs ${result.type === 'success' ? 'text-success' : 'text-destructive'}`}
|
|
128
129
|
>
|
|
129
130
|
{result.type === 'success' ? <CheckCircle2 size={12} /> : <AlertCircle size={12} />}
|
|
130
131
|
{result.text}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { ChevronDown, ChevronRight } from 'lucide-react';
|
|
4
4
|
import type { AgentInfo } from '../settings/types';
|
|
5
|
-
import type { McpContextValue } from '@/hooks/useMcpData';
|
|
6
5
|
import AgentsPanelAgentListRow, { type AgentsPanelAgentListRowCopy } from './AgentsPanelAgentListRow';
|
|
7
6
|
|
|
8
7
|
type AgentsCopy = {
|
|
@@ -16,9 +15,8 @@ export function AgentsPanelAgentGroups({
|
|
|
16
15
|
connected,
|
|
17
16
|
detected,
|
|
18
17
|
notFound,
|
|
19
|
-
onOpenDetail,
|
|
20
18
|
selectedAgentKey,
|
|
21
|
-
|
|
19
|
+
onInstallAgent,
|
|
22
20
|
listCopy,
|
|
23
21
|
showNotDetected,
|
|
24
22
|
setShowNotDetected,
|
|
@@ -27,16 +25,13 @@ export function AgentsPanelAgentGroups({
|
|
|
27
25
|
connected: AgentInfo[];
|
|
28
26
|
detected: AgentInfo[];
|
|
29
27
|
notFound: AgentInfo[];
|
|
30
|
-
onOpenDetail?: (key: string) => void;
|
|
31
28
|
selectedAgentKey?: string | null;
|
|
32
|
-
|
|
29
|
+
onInstallAgent: (key: string) => Promise<boolean>;
|
|
33
30
|
listCopy: AgentsPanelAgentListRowCopy;
|
|
34
31
|
showNotDetected: boolean;
|
|
35
32
|
setShowNotDetected: (v: boolean | ((prev: boolean) => boolean)) => void;
|
|
36
33
|
p: AgentsCopy;
|
|
37
34
|
}) {
|
|
38
|
-
const open = onOpenDetail ?? (() => {});
|
|
39
|
-
|
|
40
35
|
return (
|
|
41
36
|
<div>
|
|
42
37
|
<div className="px-0 py-1 mb-0.5">
|
|
@@ -54,8 +49,8 @@ export function AgentsPanelAgentGroups({
|
|
|
54
49
|
agent={agent}
|
|
55
50
|
agentStatus="connected"
|
|
56
51
|
selected={selectedAgentKey === agent.key}
|
|
57
|
-
|
|
58
|
-
onInstallAgent={
|
|
52
|
+
detailHref={`/agents/${encodeURIComponent(agent.key)}`}
|
|
53
|
+
onInstallAgent={onInstallAgent}
|
|
59
54
|
copy={listCopy}
|
|
60
55
|
/>
|
|
61
56
|
))}
|
|
@@ -75,8 +70,8 @@ export function AgentsPanelAgentGroups({
|
|
|
75
70
|
agent={agent}
|
|
76
71
|
agentStatus="detected"
|
|
77
72
|
selected={selectedAgentKey === agent.key}
|
|
78
|
-
|
|
79
|
-
onInstallAgent={
|
|
73
|
+
detailHref={`/agents/${encodeURIComponent(agent.key)}`}
|
|
74
|
+
onInstallAgent={onInstallAgent}
|
|
80
75
|
copy={listCopy}
|
|
81
76
|
/>
|
|
82
77
|
))}
|
|
@@ -102,8 +97,8 @@ export function AgentsPanelAgentGroups({
|
|
|
102
97
|
agent={agent}
|
|
103
98
|
agentStatus="notFound"
|
|
104
99
|
selected={selectedAgentKey === agent.key}
|
|
105
|
-
|
|
106
|
-
onInstallAgent={
|
|
100
|
+
detailHref={`/agents/${encodeURIComponent(agent.key)}`}
|
|
101
|
+
onInstallAgent={onInstallAgent}
|
|
107
102
|
copy={listCopy}
|
|
108
103
|
/>
|
|
109
104
|
))}
|