@geminilight/mindos 0.5.64 → 0.5.66
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/file/route.ts +9 -0
- package/app/app/api/mcp/agents/route.ts +27 -1
- package/app/app/api/skills/route.ts +18 -2
- package/app/app/api/tree-version/route.ts +8 -0
- 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 +6 -11
- 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 +1 -1
- package/app/components/RightAskPanel.tsx +1 -1
- package/app/components/SearchModal.tsx +10 -2
- package/app/components/SidebarLayout.tsx +35 -10
- package/app/components/ThemeToggle.tsx +1 -1
- package/app/components/agents/AgentDetailContent.tsx +454 -59
- package/app/components/agents/AgentsContentPage.tsx +70 -5
- package/app/components/agents/AgentsMcpSection.tsx +474 -159
- package/app/components/agents/AgentsOverviewSection.tsx +418 -59
- package/app/components/agents/AgentsPrimitives.tsx +335 -0
- package/app/components/agents/AgentsSkillsSection.tsx +739 -121
- package/app/components/agents/SkillDetailPopover.tsx +416 -0
- package/app/components/agents/agents-content-model.ts +292 -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 +1 -2
- 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 +12 -104
- package/app/components/panels/AgentsPanelAgentDetail.tsx +2 -2
- package/app/components/panels/AgentsPanelAgentGroups.tsx +3 -7
- package/app/components/panels/AgentsPanelAgentListRow.tsx +9 -11
- package/app/components/panels/EchoPanel.tsx +8 -10
- package/app/components/panels/PanelNavRow.tsx +9 -2
- package/app/components/panels/PluginsPanel.tsx +2 -2
- package/app/components/renderers/agent-inspector/AgentInspectorRenderer.tsx +30 -8
- package/app/components/renderers/agent-inspector/manifest.ts +3 -3
- 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 +2 -2
- 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 +4 -2
- package/app/hooks/useMention.ts +25 -8
- package/app/lib/agent/log.ts +15 -18
- package/app/lib/agent/prompt.ts +17 -29
- 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/index.ts +11 -0
- package/app/lib/fs.ts +9 -0
- package/app/lib/i18n-en.ts +259 -33
- package/app/lib/i18n-zh.ts +258 -32
- package/app/lib/mcp-agents.ts +231 -2
- package/app/lib/types.ts +2 -0
- package/package.json +1 -1
- package/scripts/migrate-agent-audit-log.js +170 -0
|
@@ -51,7 +51,7 @@ export default function SkillRow({
|
|
|
51
51
|
{expanded ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
|
|
52
52
|
<span className="text-xs font-medium flex-1">{skill.name}</span>
|
|
53
53
|
<span className={`text-2xs px-1.5 py-0.5 rounded ${
|
|
54
|
-
skill.source === 'builtin' ? 'bg-
|
|
54
|
+
skill.source === 'builtin' ? 'bg-muted text-muted-foreground' : 'bg-[var(--amber-subtle)] text-[var(--amber)]'
|
|
55
55
|
}`}>
|
|
56
56
|
{skill.source === 'builtin' ? (m?.skillBuiltin ?? 'Built-in') : (m?.skillUser ?? 'Custom')}
|
|
57
57
|
</span>
|
|
@@ -106,8 +106,7 @@ export default function SkillRow({
|
|
|
106
106
|
<button
|
|
107
107
|
onClick={() => onEditSave(skill.name)}
|
|
108
108
|
disabled={saving}
|
|
109
|
-
className="flex items-center gap-1 px-2.5 py-1 text-xs rounded-md disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
|
|
110
|
-
style={{ background: 'var(--amber)', color: 'var(--amber-foreground)' }}
|
|
109
|
+
className="flex items-center gap-1 px-2.5 py-1 text-xs rounded-md disabled:opacity-40 disabled:cursor-not-allowed transition-colors bg-[var(--amber)] text-[var(--amber-foreground)]"
|
|
111
110
|
>
|
|
112
111
|
{saving && <Loader2 size={10} className="animate-spin" />}
|
|
113
112
|
{m?.saveSkill ?? 'Save'}
|
|
@@ -239,7 +239,7 @@ export default function SkillsSection({ t }: McpSkillsSectionProps) {
|
|
|
239
239
|
onClick={() => handleLangSwitch('en')}
|
|
240
240
|
disabled={switchingLang}
|
|
241
241
|
className={`px-2.5 py-1 text-xs transition-colors disabled:opacity-50 disabled:cursor-not-allowed ${
|
|
242
|
-
currentLang === 'en' ? 'bg-amber-
|
|
242
|
+
currentLang === 'en' ? 'bg-[var(--amber-subtle)] text-[var(--amber)] font-medium' : 'text-muted-foreground hover:bg-muted'
|
|
243
243
|
}`}
|
|
244
244
|
>
|
|
245
245
|
{m?.skillLangEn ?? 'English'}
|
|
@@ -248,7 +248,7 @@ export default function SkillsSection({ t }: McpSkillsSectionProps) {
|
|
|
248
248
|
onClick={() => handleLangSwitch('zh')}
|
|
249
249
|
disabled={switchingLang}
|
|
250
250
|
className={`px-2.5 py-1 text-xs transition-colors disabled:opacity-50 disabled:cursor-not-allowed border-l border-border ${
|
|
251
|
-
currentLang === 'zh' ? 'bg-amber-
|
|
251
|
+
currentLang === 'zh' ? 'bg-[var(--amber-subtle)] text-[var(--amber)] font-medium' : 'text-muted-foreground hover:bg-muted'
|
|
252
252
|
}`}
|
|
253
253
|
>
|
|
254
254
|
{m?.skillLangZh ?? '中文'}
|
|
@@ -68,7 +68,7 @@ export function McpTab({ t }: McpTabProps) {
|
|
|
68
68
|
{/* MCP Config Viewer */}
|
|
69
69
|
{mcp.agents.length > 0 && (
|
|
70
70
|
<div>
|
|
71
|
-
<
|
|
71
|
+
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-3">MCP</p>
|
|
72
72
|
<AgentConfigViewer
|
|
73
73
|
connectedAgents={connectedAgents}
|
|
74
74
|
detectedAgents={detectedAgents}
|
|
@@ -91,13 +91,13 @@ export function McpTab({ t }: McpTabProps) {
|
|
|
91
91
|
|
|
92
92
|
{/* Skills */}
|
|
93
93
|
<div>
|
|
94
|
-
<
|
|
94
|
+
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-3">{m?.skillsTitle ?? 'Skills'}</p>
|
|
95
95
|
<SkillsSection t={t} />
|
|
96
96
|
</div>
|
|
97
97
|
|
|
98
98
|
{/* Batch Agent Install */}
|
|
99
99
|
<div>
|
|
100
|
-
<
|
|
100
|
+
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wider mb-3">{m?.agentsTitle ?? 'Agent Configuration'}</p>
|
|
101
101
|
<AgentInstall agents={mcp.agents} t={t} onRefresh={mcp.refresh} />
|
|
102
102
|
</div>
|
|
103
103
|
</div>
|
|
@@ -119,8 +119,8 @@ function McpStatusCard({ status, restarting, onRestart, onRefresh, m }: {
|
|
|
119
119
|
<div className="flex items-center gap-2.5 text-xs">
|
|
120
120
|
{restarting ? (
|
|
121
121
|
<>
|
|
122
|
-
<Loader2 size={12} className="animate-spin
|
|
123
|
-
<span
|
|
122
|
+
<Loader2 size={12} className="animate-spin text-[var(--amber)]" />
|
|
123
|
+
<span className="text-[var(--amber)]">{m?.restarting ?? 'Restarting...'}</span>
|
|
124
124
|
</>
|
|
125
125
|
) : (
|
|
126
126
|
<>
|
|
@@ -142,8 +142,7 @@ function McpStatusCard({ status, restarting, onRestart, onRefresh, m }: {
|
|
|
142
142
|
<div className="flex items-center gap-2">
|
|
143
143
|
{!status.running && !restarting && (
|
|
144
144
|
<button onClick={onRestart}
|
|
145
|
-
className="flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-lg font-medium text-
|
|
146
|
-
style={{ background: 'var(--amber)' }}>
|
|
145
|
+
className="flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors">
|
|
147
146
|
<RotateCcw size={12} /> {m?.restart ?? 'Restart'}
|
|
148
147
|
</button>
|
|
149
148
|
)}
|
|
@@ -222,18 +221,18 @@ function AgentConfigViewer({ connectedAgents, detectedAgents, notFoundAgents, cu
|
|
|
222
221
|
{/* Agent status badge */}
|
|
223
222
|
<div className="flex items-center gap-2">
|
|
224
223
|
{currentAgent.present && currentAgent.installed ? (
|
|
225
|
-
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-2xs font-medium bg-
|
|
226
|
-
<span className="w-1.5 h-1.5 rounded-full bg-
|
|
224
|
+
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-2xs font-medium bg-success/10 text-success">
|
|
225
|
+
<span className="w-1.5 h-1.5 rounded-full bg-success inline-block" />
|
|
227
226
|
{m?.tagConnected ?? 'Connected'}
|
|
228
227
|
</span>
|
|
229
228
|
) : currentAgent.present && !currentAgent.installed ? (
|
|
230
|
-
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-2xs font-medium
|
|
231
|
-
<span className="w-1.5 h-1.5 rounded-full
|
|
229
|
+
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-2xs font-medium bg-[var(--amber-subtle)] text-[var(--amber)]">
|
|
230
|
+
<span className="w-1.5 h-1.5 rounded-full bg-[var(--amber)] inline-block" />
|
|
232
231
|
{m?.tagDetected ?? 'Detected — not configured'}
|
|
233
232
|
</span>
|
|
234
233
|
) : (
|
|
235
234
|
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-2xs font-medium bg-muted text-muted-foreground">
|
|
236
|
-
<span className="w-1.5 h-1.5 rounded-full bg-
|
|
235
|
+
<span className="w-1.5 h-1.5 rounded-full bg-muted-foreground inline-block" />
|
|
237
236
|
{m?.tagNotInstalled ?? 'Not installed'}
|
|
238
237
|
</span>
|
|
239
238
|
)}
|
|
@@ -267,7 +266,7 @@ function AgentConfigViewer({ connectedAgents, detectedAgents, notFoundAgents, cu
|
|
|
267
266
|
|
|
268
267
|
{/* Auth warning */}
|
|
269
268
|
{transport === 'http' && mcpStatus && !mcpStatus.authConfigured && (
|
|
270
|
-
<p className="flex items-center gap-1.5 text-xs
|
|
269
|
+
<p className="flex items-center gap-1.5 text-xs text-[var(--amber)]">
|
|
271
270
|
<AlertCircle size={12} />
|
|
272
271
|
{m?.noAuthWarning ?? 'Auth not configured. Run `mindos token` to set up.'}
|
|
273
272
|
</p>
|
|
@@ -52,7 +52,7 @@ function ProgressBar({ value, max, className }: { value: number; max: number; cl
|
|
|
52
52
|
return (
|
|
53
53
|
<div className={`h-2 w-full rounded-full bg-muted ${className ?? ''}`}>
|
|
54
54
|
<div
|
|
55
|
-
className={`h-full rounded-full transition-all duration-300 ${pct > 85 ? 'bg-destructive' : 'bg-amber
|
|
55
|
+
className={`h-full rounded-full transition-all duration-300 ${pct > 85 ? 'bg-destructive' : 'bg-[var(--amber)]'}`}
|
|
56
56
|
style={{ width: `${pct}%` }}
|
|
57
57
|
/>
|
|
58
58
|
</div>
|
|
@@ -125,10 +125,10 @@ export function MonitoringTab({ t }: MonitoringTabProps) {
|
|
|
125
125
|
<div className="space-y-6">
|
|
126
126
|
{/* System */}
|
|
127
127
|
<section>
|
|
128
|
-
<
|
|
129
|
-
<Cpu size={13}
|
|
128
|
+
<p className="flex items-center gap-1.5 text-xs font-medium text-muted-foreground uppercase tracking-wider mb-3">
|
|
129
|
+
<Cpu size={13} />
|
|
130
130
|
{mon.system || 'System'}
|
|
131
|
-
</
|
|
131
|
+
</p>
|
|
132
132
|
<div className="space-y-3">
|
|
133
133
|
<div>
|
|
134
134
|
<div className="flex justify-between text-xs mb-1">
|
|
@@ -147,10 +147,10 @@ export function MonitoringTab({ t }: MonitoringTabProps) {
|
|
|
147
147
|
|
|
148
148
|
{/* Application */}
|
|
149
149
|
<section>
|
|
150
|
-
<
|
|
151
|
-
<Zap size={13}
|
|
150
|
+
<p className="flex items-center gap-1.5 text-xs font-medium text-muted-foreground uppercase tracking-wider mb-3">
|
|
151
|
+
<Zap size={13} />
|
|
152
152
|
{mon.application || 'Application'}
|
|
153
|
-
</
|
|
153
|
+
</p>
|
|
154
154
|
<div className="grid grid-cols-3 gap-4">
|
|
155
155
|
<StatCard label={mon.requests || 'Requests'} value={application.agentRequests} />
|
|
156
156
|
<StatCard label={mon.toolCalls || 'Tool Calls'} value={application.toolExecutions} />
|
|
@@ -166,10 +166,10 @@ export function MonitoringTab({ t }: MonitoringTabProps) {
|
|
|
166
166
|
|
|
167
167
|
{/* Knowledge Base */}
|
|
168
168
|
<section>
|
|
169
|
-
<
|
|
170
|
-
<Database size={13}
|
|
169
|
+
<p className="flex items-center gap-1.5 text-xs font-medium text-muted-foreground uppercase tracking-wider mb-3">
|
|
170
|
+
<Database size={13} />
|
|
171
171
|
{mon.knowledgeBase || 'Knowledge Base'}
|
|
172
|
-
</
|
|
172
|
+
</p>
|
|
173
173
|
<div className="grid grid-cols-3 gap-4">
|
|
174
174
|
<StatCard label={mon.files || 'Files'} value={knowledgeBase.fileCount} />
|
|
175
175
|
<StatCard label={mon.totalSize || 'Total Size'} value={formatBytes(knowledgeBase.totalSizeBytes)} />
|
|
@@ -179,10 +179,10 @@ export function MonitoringTab({ t }: MonitoringTabProps) {
|
|
|
179
179
|
|
|
180
180
|
{/* MCP */}
|
|
181
181
|
<section>
|
|
182
|
-
<
|
|
183
|
-
<HardDrive size={13}
|
|
182
|
+
<p className="flex items-center gap-1.5 text-xs font-medium text-muted-foreground uppercase tracking-wider mb-3">
|
|
183
|
+
<HardDrive size={13} />
|
|
184
184
|
MCP
|
|
185
|
-
</
|
|
185
|
+
</p>
|
|
186
186
|
<div className="grid grid-cols-3 gap-4">
|
|
187
187
|
<StatCard
|
|
188
188
|
label={mon.mcpStatus || 'Status'}
|
|
@@ -8,7 +8,7 @@ import type { PluginsTabProps } from './types';
|
|
|
8
8
|
export function PluginsTab({ pluginStates, setPluginStates, t }: PluginsTabProps) {
|
|
9
9
|
const renderers = getPluginRenderers();
|
|
10
10
|
return (
|
|
11
|
-
<div className="space-y-
|
|
11
|
+
<div className="space-y-6">
|
|
12
12
|
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wider">{t.settings.plugins.title}</p>
|
|
13
13
|
|
|
14
14
|
{renderers.length === 0 ? (
|
|
@@ -30,7 +30,7 @@ export function PluginsTab({ pluginStates, setPluginStates, t }: PluginsTabProps
|
|
|
30
30
|
<div className="flex items-center gap-2 flex-wrap">
|
|
31
31
|
<span className="text-sm font-medium text-foreground">{renderer.name}</span>
|
|
32
32
|
{isCore && (
|
|
33
|
-
<span className="text-2xs px-1.5 py-0.5 rounded bg-amber-
|
|
33
|
+
<span className="text-2xs px-1.5 py-0.5 rounded bg-[var(--amber-subtle)] text-[var(--amber)] font-mono">
|
|
34
34
|
core
|
|
35
35
|
</span>
|
|
36
36
|
)}
|
|
@@ -35,7 +35,7 @@ export function Select({ className = '', ...props }: React.SelectHTMLAttributes<
|
|
|
35
35
|
export function EnvBadge({ overridden }: { overridden: boolean }) {
|
|
36
36
|
if (!overridden) return null;
|
|
37
37
|
return (
|
|
38
|
-
<span className="text-2xs px-1.5 py-0.5 rounded bg-amber-
|
|
38
|
+
<span className="text-2xs px-1.5 py-0.5 rounded bg-[var(--amber-subtle)] text-[var(--amber)] font-mono ml-1.5">env</span>
|
|
39
39
|
);
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -72,7 +72,7 @@ export function Toggle({ checked, onChange, size = 'md', disabled, title, onClic
|
|
|
72
72
|
onClick={onClick ?? (() => onChange?.(!checked))}
|
|
73
73
|
className={`relative inline-flex shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-60 ${
|
|
74
74
|
sm ? 'h-4 w-7' : 'h-5 w-9'
|
|
75
|
-
} ${checked ? 'bg-amber
|
|
75
|
+
} ${checked ? 'bg-[var(--amber)]' : 'bg-muted'}`}
|
|
76
76
|
>
|
|
77
77
|
<span
|
|
78
78
|
className={`pointer-events-none inline-block rounded-full bg-white shadow-sm transition-transform ${
|
|
@@ -119,8 +119,7 @@ export function PrimaryButton({ children, disabled, onClick, type = 'button', cl
|
|
|
119
119
|
type={type}
|
|
120
120
|
onClick={onClick}
|
|
121
121
|
disabled={disabled}
|
|
122
|
-
className={`px-4 py-2 text-sm font-medium rounded-lg transition-colors disabled:opacity-40 disabled:cursor-not-allowed ${className}`}
|
|
123
|
-
style={{ background: 'var(--amber)', color: 'var(--amber-foreground)' }}
|
|
122
|
+
className={`px-4 py-2 text-sm font-medium rounded-lg bg-[var(--amber)] text-[var(--amber-foreground)] transition-colors disabled:opacity-40 disabled:cursor-not-allowed ${className}`}
|
|
124
123
|
{...props}
|
|
125
124
|
>
|
|
126
125
|
{children}
|
|
@@ -245,7 +245,7 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
|
|
|
245
245
|
onClick={() => setTab(tabItem.id)}
|
|
246
246
|
className={`flex items-center gap-1 px-2 py-2 text-[11px] font-medium transition-colors border-b-2 -mb-px whitespace-nowrap ${
|
|
247
247
|
tab === tabItem.id
|
|
248
|
-
? 'border-amber
|
|
248
|
+
? 'border-[var(--amber)] text-foreground'
|
|
249
249
|
: 'border-transparent text-muted-foreground hover:text-foreground'
|
|
250
250
|
}`}
|
|
251
251
|
>
|
|
@@ -296,7 +296,7 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
|
|
|
296
296
|
onClick={() => setTab(tabItem.id)}
|
|
297
297
|
className={`flex items-center gap-1.5 px-3 py-2.5 text-xs font-medium transition-colors border-b-2 -mb-px whitespace-nowrap ${
|
|
298
298
|
tab === tabItem.id
|
|
299
|
-
? 'border-amber
|
|
299
|
+
? 'border-[var(--amber)] text-foreground'
|
|
300
300
|
: 'border-transparent text-muted-foreground hover:text-foreground'
|
|
301
301
|
}`}
|
|
302
302
|
>
|
|
@@ -330,7 +330,7 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
|
|
|
330
330
|
}`}
|
|
331
331
|
>
|
|
332
332
|
{tab === tabItem.id && (
|
|
333
|
-
<div className="absolute left-0 top-1 bottom-1 w-[3px] rounded-r-full bg-amber
|
|
333
|
+
<div className="absolute left-0 top-1 bottom-1 w-[3px] rounded-r-full bg-[var(--amber)]" />
|
|
334
334
|
)}
|
|
335
335
|
{tabItem.icon}
|
|
336
336
|
{tabItem.label}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useCallback } from 'react';
|
|
4
4
|
import { RefreshCw, AlertCircle, CheckCircle2, Loader2, GitBranch, ExternalLink, Eye, EyeOff } from 'lucide-react';
|
|
5
|
-
import { SectionLabel, PrimaryButton } from './Primitives';
|
|
5
|
+
import { SectionLabel, PrimaryButton, Input } from './Primitives';
|
|
6
6
|
import { apiFetch } from '@/lib/api';
|
|
7
7
|
import type { SyncStatus, SyncTabProps } from './types';
|
|
8
8
|
import type { Messages } from '@/lib/i18n';
|
|
@@ -64,7 +64,7 @@ function SyncEmptyState({ t, onInitComplete }: { t: Messages; onInitComplete: ()
|
|
|
64
64
|
};
|
|
65
65
|
|
|
66
66
|
return (
|
|
67
|
-
<div className="space-y-
|
|
67
|
+
<div className="space-y-6">
|
|
68
68
|
{/* Header */}
|
|
69
69
|
<div className="flex items-center gap-3">
|
|
70
70
|
<div className="w-9 h-9 rounded-lg bg-muted flex items-center justify-center shrink-0">
|
|
@@ -85,19 +85,15 @@ function SyncEmptyState({ t, onInitComplete }: { t: Messages; onInitComplete: ()
|
|
|
85
85
|
<label className="text-xs font-medium text-foreground block">
|
|
86
86
|
{syncT?.remoteUrl ?? 'Git Remote URL'}
|
|
87
87
|
</label>
|
|
88
|
-
<
|
|
88
|
+
<Input
|
|
89
89
|
type="text"
|
|
90
90
|
value={remoteUrl}
|
|
91
91
|
onChange={e => { setRemoteUrl(e.target.value); setError(''); }}
|
|
92
92
|
placeholder="https://github.com/user/my-mind.git"
|
|
93
|
-
className=
|
|
94
|
-
style={{
|
|
95
|
-
borderColor: remoteUrl.trim() && !isValid ? 'var(--destructive, red)' : 'var(--border)',
|
|
96
|
-
color: 'var(--foreground)',
|
|
97
|
-
}}
|
|
93
|
+
className={`font-mono ${remoteUrl.trim() && !isValid ? 'border-destructive' : ''}`}
|
|
98
94
|
/>
|
|
99
95
|
{remoteUrl.trim() && !isValid && (
|
|
100
|
-
<p className="text-xs
|
|
96
|
+
<p className="text-xs text-destructive">
|
|
101
97
|
{syncT?.invalidUrl ?? 'Invalid Git URL — use HTTPS (https://...) or SSH (git@...)'}
|
|
102
98
|
</p>
|
|
103
99
|
)}
|
|
@@ -117,13 +113,12 @@ function SyncEmptyState({ t, onInitComplete }: { t: Messages; onInitComplete: ()
|
|
|
117
113
|
<span className="text-muted-foreground font-normal">{syncT?.optional ?? '(optional, for private repos)'}</span>
|
|
118
114
|
</label>
|
|
119
115
|
<div className="relative">
|
|
120
|
-
<
|
|
116
|
+
<Input
|
|
121
117
|
type={showToken ? 'text' : 'password'}
|
|
122
118
|
value={token}
|
|
123
119
|
onChange={e => setToken(e.target.value)}
|
|
124
120
|
placeholder="ghp_xxxxxxxxxxxx"
|
|
125
|
-
className="
|
|
126
|
-
style={{ borderColor: 'var(--border)', color: 'var(--foreground)' }}
|
|
121
|
+
className="pr-9 font-mono"
|
|
127
122
|
/>
|
|
128
123
|
<button
|
|
129
124
|
type="button"
|
|
@@ -144,13 +139,12 @@ function SyncEmptyState({ t, onInitComplete }: { t: Messages; onInitComplete: ()
|
|
|
144
139
|
<label className="text-xs font-medium text-foreground block">
|
|
145
140
|
{syncT?.branchLabel ?? 'Branch'}
|
|
146
141
|
</label>
|
|
147
|
-
<
|
|
142
|
+
<Input
|
|
148
143
|
type="text"
|
|
149
144
|
value={branch}
|
|
150
145
|
onChange={e => setBranch(e.target.value)}
|
|
151
146
|
placeholder="main"
|
|
152
|
-
className="
|
|
153
|
-
style={{ borderColor: 'var(--border)', color: 'var(--foreground)' }}
|
|
147
|
+
className="max-w-[200px] font-mono"
|
|
154
148
|
/>
|
|
155
149
|
</div>
|
|
156
150
|
|
|
@@ -168,7 +162,7 @@ function SyncEmptyState({ t, onInitComplete }: { t: Messages; onInitComplete: ()
|
|
|
168
162
|
|
|
169
163
|
{/* Error */}
|
|
170
164
|
{error && (
|
|
171
|
-
<div className="flex items-start gap-2 text-xs p-3 rounded-lg" role="alert" aria-live="polite"
|
|
165
|
+
<div className="flex items-start gap-2 text-xs p-3 rounded-lg bg-destructive/10 text-destructive" role="alert" aria-live="polite">
|
|
172
166
|
<AlertCircle size={13} className="shrink-0 mt-0.5" />
|
|
173
167
|
<span>{error}</span>
|
|
174
168
|
</div>
|
|
@@ -269,7 +263,7 @@ export function SyncTab({ t }: SyncTabProps) {
|
|
|
269
263
|
const conflicts = status.conflicts || [];
|
|
270
264
|
|
|
271
265
|
return (
|
|
272
|
-
<div className="space-y-
|
|
266
|
+
<div className="space-y-6">
|
|
273
267
|
<SectionLabel>Sync</SectionLabel>
|
|
274
268
|
|
|
275
269
|
{/* Status overview */}
|
|
@@ -57,7 +57,7 @@ function StageIcon({ status }: { status: string }) {
|
|
|
57
57
|
case 'done':
|
|
58
58
|
return <CheckCircle2 size={14} className="text-success shrink-0" />;
|
|
59
59
|
case 'running':
|
|
60
|
-
return <Loader2 size={14} className="animate-spin shrink-0
|
|
60
|
+
return <Loader2 size={14} className="animate-spin shrink-0 text-[var(--amber)]" />;
|
|
61
61
|
case 'failed':
|
|
62
62
|
return <AlertCircle size={14} className="text-destructive shrink-0" />;
|
|
63
63
|
default:
|
|
@@ -119,7 +119,7 @@ function DesktopUpdateTab() {
|
|
|
119
119
|
};
|
|
120
120
|
|
|
121
121
|
return (
|
|
122
|
-
<div className="space-y-
|
|
122
|
+
<div className="space-y-6">
|
|
123
123
|
<div className="rounded-xl border border-border bg-card p-4 space-y-3">
|
|
124
124
|
<div className="flex items-center justify-between">
|
|
125
125
|
<div className="flex items-center gap-2">
|
|
@@ -137,14 +137,14 @@ function DesktopUpdateTab() {
|
|
|
137
137
|
)}
|
|
138
138
|
|
|
139
139
|
{state === 'idle' && !available && (
|
|
140
|
-
<div className="flex items-center gap-2 text-xs text-
|
|
140
|
+
<div className="flex items-center gap-2 text-xs text-success">
|
|
141
141
|
<CheckCircle2 size={13} />
|
|
142
142
|
{u?.upToDate ?? "You're up to date"}
|
|
143
143
|
</div>
|
|
144
144
|
)}
|
|
145
145
|
|
|
146
146
|
{state === 'idle' && available && (
|
|
147
|
-
<div className="flex items-center gap-2 text-xs
|
|
147
|
+
<div className="flex items-center gap-2 text-xs text-[var(--amber)]">
|
|
148
148
|
<Download size={13} />
|
|
149
149
|
{version ? `Update available: v${version}` : 'Update available'}
|
|
150
150
|
</div>
|
|
@@ -153,20 +153,20 @@ function DesktopUpdateTab() {
|
|
|
153
153
|
{state === 'downloading' && (
|
|
154
154
|
<div className="space-y-2">
|
|
155
155
|
<div className="flex items-center gap-2 text-xs text-foreground">
|
|
156
|
-
<Loader2 size={13} className="animate-spin
|
|
156
|
+
<Loader2 size={13} className="animate-spin text-[var(--amber)]" />
|
|
157
157
|
{u?.desktopDownloading ?? 'Downloading update...'}
|
|
158
158
|
</div>
|
|
159
159
|
<div className="h-1 rounded-full bg-muted overflow-hidden">
|
|
160
160
|
<div
|
|
161
|
-
className="h-full rounded-full transition-all duration-300"
|
|
162
|
-
style={{ width: `${Math.max(progress, 3)}
|
|
161
|
+
className="h-full rounded-full bg-[var(--amber)] transition-all duration-300"
|
|
162
|
+
style={{ width: `${Math.max(progress, 3)}%` }}
|
|
163
163
|
/>
|
|
164
164
|
</div>
|
|
165
165
|
</div>
|
|
166
166
|
)}
|
|
167
167
|
|
|
168
168
|
{state === 'ready' && (
|
|
169
|
-
<div className="flex items-center gap-2 text-xs text-
|
|
169
|
+
<div className="flex items-center gap-2 text-xs text-success">
|
|
170
170
|
<CheckCircle2 size={13} />
|
|
171
171
|
{u?.desktopReady ?? 'Update downloaded. Restart to apply.'}
|
|
172
172
|
</div>
|
|
@@ -193,8 +193,7 @@ function DesktopUpdateTab() {
|
|
|
193
193
|
{state === 'idle' && available && (
|
|
194
194
|
<button
|
|
195
195
|
onClick={handleInstall}
|
|
196
|
-
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg font-medium text-
|
|
197
|
-
style={{ background: 'var(--amber)' }}
|
|
196
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors"
|
|
198
197
|
>
|
|
199
198
|
<Download size={12} />
|
|
200
199
|
{version ? `Update to v${version}` : 'Update'}
|
|
@@ -204,8 +203,7 @@ function DesktopUpdateTab() {
|
|
|
204
203
|
{state === 'ready' && (
|
|
205
204
|
<button
|
|
206
205
|
onClick={() => bridge.installUpdate()}
|
|
207
|
-
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg font-medium text-
|
|
208
|
-
style={{ background: 'var(--amber)' }}
|
|
206
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors"
|
|
209
207
|
>
|
|
210
208
|
<RefreshCw size={12} />
|
|
211
209
|
{u?.desktopRestart ?? 'Restart Now'}
|
|
@@ -401,7 +399,7 @@ function BrowserUpdateTab() {
|
|
|
401
399
|
const progress = stages.length > 0 ? Math.round((doneCount / stages.length) * 100) : 0;
|
|
402
400
|
|
|
403
401
|
return (
|
|
404
|
-
<div className="space-y-
|
|
402
|
+
<div className="space-y-6">
|
|
405
403
|
{/* Version Card */}
|
|
406
404
|
<div className="rounded-xl border border-border bg-card p-4 space-y-3">
|
|
407
405
|
<div className="flex items-center justify-between">
|
|
@@ -419,14 +417,14 @@ function BrowserUpdateTab() {
|
|
|
419
417
|
)}
|
|
420
418
|
|
|
421
419
|
{state === 'idle' && info && !info.hasUpdate && (
|
|
422
|
-
<div className="flex items-center gap-2 text-xs text-
|
|
420
|
+
<div className="flex items-center gap-2 text-xs text-success">
|
|
423
421
|
<CheckCircle2 size={13} />
|
|
424
422
|
{u?.upToDate ?? "You're up to date"}
|
|
425
423
|
</div>
|
|
426
424
|
)}
|
|
427
425
|
|
|
428
426
|
{state === 'idle' && info?.hasUpdate && (
|
|
429
|
-
<div className="flex items-center gap-2 text-xs
|
|
427
|
+
<div className="flex items-center gap-2 text-xs text-[var(--amber)]">
|
|
430
428
|
<Download size={13} />
|
|
431
429
|
{u?.available ? u.available(info.current, info.latest) : `Update available: v${info.current} → v${info.latest}`}
|
|
432
430
|
</div>
|
|
@@ -449,8 +447,8 @@ function BrowserUpdateTab() {
|
|
|
449
447
|
{/* Progress bar */}
|
|
450
448
|
<div className="h-1 rounded-full bg-muted overflow-hidden">
|
|
451
449
|
<div
|
|
452
|
-
className="h-full rounded-full transition-all duration-500 ease-out"
|
|
453
|
-
style={{ width: `${Math.max(progress, 5)}
|
|
450
|
+
className="h-full rounded-full bg-[var(--amber)] transition-all duration-500 ease-out"
|
|
451
|
+
style={{ width: `${Math.max(progress, 5)}%` }}
|
|
454
452
|
/>
|
|
455
453
|
</div>
|
|
456
454
|
|
|
@@ -463,7 +461,7 @@ function BrowserUpdateTab() {
|
|
|
463
461
|
)}
|
|
464
462
|
|
|
465
463
|
{state === 'updated' && (
|
|
466
|
-
<div className="flex items-center gap-2 text-xs text-
|
|
464
|
+
<div className="flex items-center gap-2 text-xs text-success">
|
|
467
465
|
<CheckCircle2 size={13} />
|
|
468
466
|
{u?.updated ?? 'Updated successfully! Reloading...'}
|
|
469
467
|
</div>
|
|
@@ -471,7 +469,7 @@ function BrowserUpdateTab() {
|
|
|
471
469
|
|
|
472
470
|
{state === 'timeout' && (
|
|
473
471
|
<div className="space-y-2">
|
|
474
|
-
<div className="flex items-center gap-2 text-xs text-amber
|
|
472
|
+
<div className="flex items-center gap-2 text-xs text-[var(--amber)]">
|
|
475
473
|
<AlertCircle size={13} />
|
|
476
474
|
{u?.timeout ?? 'Update may still be in progress.'}
|
|
477
475
|
</div>
|
|
@@ -521,8 +519,7 @@ function BrowserUpdateTab() {
|
|
|
521
519
|
{info?.hasUpdate && state !== 'updating' && state !== 'updated' && (
|
|
522
520
|
<button
|
|
523
521
|
onClick={handleUpdate}
|
|
524
|
-
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg font-medium text-
|
|
525
|
-
style={{ background: 'var(--amber)' }}
|
|
522
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors"
|
|
526
523
|
>
|
|
527
524
|
<Download size={12} />
|
|
528
525
|
{u?.updateButton ? u.updateButton(info.latest) : `Update to v${info.latest}`}
|
|
@@ -79,6 +79,20 @@ export interface AgentInfo {
|
|
|
79
79
|
globalNestedKey?: string;
|
|
80
80
|
globalPath: string;
|
|
81
81
|
projectPath?: string | null;
|
|
82
|
+
skillMode?: 'universal' | 'additional' | 'unsupported';
|
|
83
|
+
skillAgentName?: string;
|
|
84
|
+
skillWorkspacePath?: string;
|
|
85
|
+
hiddenRootPath?: string;
|
|
86
|
+
hiddenRootPresent?: boolean;
|
|
87
|
+
runtimeConversationSignal?: boolean;
|
|
88
|
+
runtimeUsageSignal?: boolean;
|
|
89
|
+
runtimeLastActivityAt?: string;
|
|
90
|
+
configuredMcpServers?: string[];
|
|
91
|
+
configuredMcpServerCount?: number;
|
|
92
|
+
configuredMcpSources?: string[];
|
|
93
|
+
installedSkillNames?: string[];
|
|
94
|
+
installedSkillCount?: number;
|
|
95
|
+
installedSkillSourcePath?: string;
|
|
82
96
|
}
|
|
83
97
|
|
|
84
98
|
export interface SkillInfo {
|
|
@@ -61,7 +61,7 @@ export default function StepKB({ state, update, t, homeDir }: StepKBProps) {
|
|
|
61
61
|
const full: string[] = (d.dirs as string[]).map((dir: string) => parentNorm + dir);
|
|
62
62
|
const endsWithAnySep = typed.endsWith('/') || typed.endsWith('\\');
|
|
63
63
|
const filtered = endsWithAnySep ? full : full.filter(f => f.startsWith(typed));
|
|
64
|
-
setSuggestions(filtered.slice(0,
|
|
64
|
+
setSuggestions(filtered.slice(0, 20));
|
|
65
65
|
setShowSuggestions(filtered.length > 0);
|
|
66
66
|
setActiveSuggestion(-1);
|
|
67
67
|
})
|
package/app/hooks/useMcpData.tsx
CHANGED
|
@@ -12,7 +12,7 @@ export interface McpContextValue {
|
|
|
12
12
|
skills: SkillInfo[];
|
|
13
13
|
loading: boolean;
|
|
14
14
|
refresh: () => Promise<void>;
|
|
15
|
-
toggleSkill: (name: string, enabled: boolean) => Promise<
|
|
15
|
+
toggleSkill: (name: string, enabled: boolean) => Promise<boolean>;
|
|
16
16
|
installAgent: (key: string, opts?: { scope?: string; transport?: string }) => Promise<boolean>;
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -108,7 +108,7 @@ export default function McpProvider({ children }: { children: ReactNode }) {
|
|
|
108
108
|
await fetchAll();
|
|
109
109
|
}, [fetchAll]);
|
|
110
110
|
|
|
111
|
-
const toggleSkill = useCallback(async (name: string, enabled: boolean) => {
|
|
111
|
+
const toggleSkill = useCallback(async (name: string, enabled: boolean): Promise<boolean> => {
|
|
112
112
|
// Optimistic update
|
|
113
113
|
setSkills(prev => prev.map(s => s.name === name ? { ...s, enabled } : s));
|
|
114
114
|
try {
|
|
@@ -117,9 +117,11 @@ export default function McpProvider({ children }: { children: ReactNode }) {
|
|
|
117
117
|
headers: { 'Content-Type': 'application/json' },
|
|
118
118
|
body: JSON.stringify({ action: 'toggle', name, enabled }),
|
|
119
119
|
});
|
|
120
|
+
return true;
|
|
120
121
|
} catch {
|
|
121
122
|
// Revert on failure
|
|
122
123
|
setSkills(prev => prev.map(s => s.name === name ? { ...s, enabled: !enabled } : s));
|
|
124
|
+
return false;
|
|
123
125
|
}
|
|
124
126
|
}, []);
|
|
125
127
|
|
package/app/hooks/useMention.ts
CHANGED
|
@@ -2,21 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
import { useState, useCallback, useEffect } from 'react';
|
|
4
4
|
|
|
5
|
+
function safeFetchFiles(): Promise<string[]> {
|
|
6
|
+
return fetch('/api/files')
|
|
7
|
+
.then((r) => (r.ok ? r.json() : []))
|
|
8
|
+
.then((data) => (Array.isArray(data) ? data : []))
|
|
9
|
+
.catch(() => [] as string[]);
|
|
10
|
+
}
|
|
11
|
+
|
|
5
12
|
export function useMention() {
|
|
6
13
|
const [allFiles, setAllFiles] = useState<string[]>([]);
|
|
7
14
|
const [mentionQuery, setMentionQuery] = useState<string | null>(null);
|
|
8
15
|
const [mentionResults, setMentionResults] = useState<string[]>([]);
|
|
9
16
|
const [mentionIndex, setMentionIndex] = useState(0);
|
|
10
17
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
fetch('/api/files')
|
|
14
|
-
.then((r) => r.json())
|
|
15
|
-
.then(setAllFiles)
|
|
16
|
-
.catch(() => {});
|
|
18
|
+
const loadFiles = useCallback(() => {
|
|
19
|
+
safeFetchFiles().then(setAllFiles);
|
|
17
20
|
}, []);
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
loadFiles();
|
|
24
|
+
const handler = () => loadFiles();
|
|
25
|
+
window.addEventListener('mindos:files-changed', handler);
|
|
26
|
+
return () => window.removeEventListener('mindos:files-changed', handler);
|
|
27
|
+
}, [loadFiles]);
|
|
28
|
+
|
|
20
29
|
const updateMentionFromInput = useCallback(
|
|
21
30
|
(val: string) => {
|
|
22
31
|
const atIdx = val.lastIndexOf('@');
|
|
@@ -30,8 +39,15 @@ export function useMention() {
|
|
|
30
39
|
return;
|
|
31
40
|
}
|
|
32
41
|
const query = val.slice(atIdx + 1).toLowerCase();
|
|
42
|
+
const filtered = allFiles.filter((f) => f.toLowerCase().includes(query)).slice(0, 30);
|
|
43
|
+
if (filtered.length === 0) {
|
|
44
|
+
setMentionQuery(null);
|
|
45
|
+
setMentionResults([]);
|
|
46
|
+
setMentionIndex(0);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
33
49
|
setMentionQuery(query);
|
|
34
|
-
setMentionResults(
|
|
50
|
+
setMentionResults(filtered);
|
|
35
51
|
setMentionIndex(0);
|
|
36
52
|
},
|
|
37
53
|
[allFiles],
|
|
@@ -39,6 +55,7 @@ export function useMention() {
|
|
|
39
55
|
|
|
40
56
|
const navigateMention = useCallback(
|
|
41
57
|
(direction: 'up' | 'down') => {
|
|
58
|
+
if (mentionResults.length === 0) return;
|
|
42
59
|
if (direction === 'down') {
|
|
43
60
|
setMentionIndex((i) => Math.min(i + 1, mentionResults.length - 1));
|
|
44
61
|
} else {
|