@geminilight/mindos 0.5.69 → 0.6.0
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/app/app/api/ask/route.ts +122 -92
- package/app/app/api/file/import/route.ts +197 -0
- package/app/app/api/mcp/agents/route.ts +53 -2
- package/app/app/api/mcp/status/route.ts +1 -1
- package/app/app/api/skills/route.ts +10 -114
- package/app/components/ActivityBar.tsx +5 -7
- package/app/components/CreateSpaceModal.tsx +31 -6
- package/app/components/FileTree.tsx +68 -11
- package/app/components/GuideCard.tsx +197 -131
- package/app/components/HomeContent.tsx +85 -18
- package/app/components/ImportModal.tsx +415 -0
- package/app/components/OnboardingView.tsx +9 -0
- package/app/components/Panel.tsx +4 -2
- package/app/components/SidebarLayout.tsx +96 -8
- package/app/components/SpaceInitToast.tsx +173 -0
- package/app/components/TableOfContents.tsx +1 -0
- package/app/components/agents/AgentDetailContent.tsx +69 -45
- package/app/components/agents/AgentsContentPage.tsx +2 -1
- package/app/components/agents/AgentsMcpSection.tsx +16 -12
- package/app/components/agents/AgentsOverviewSection.tsx +37 -36
- package/app/components/agents/AgentsPrimitives.tsx +41 -20
- package/app/components/agents/AgentsSkillsSection.tsx +16 -7
- package/app/components/agents/SkillDetailPopover.tsx +11 -11
- package/app/components/agents/agents-content-model.ts +16 -8
- package/app/components/ask/AskContent.tsx +148 -50
- package/app/components/ask/MentionPopover.tsx +16 -8
- package/app/components/ask/SlashCommandPopover.tsx +62 -0
- package/app/components/panels/AgentsPanelAgentGroups.tsx +8 -6
- package/app/components/panels/AgentsPanelHubNav.tsx +3 -3
- package/app/components/panels/DiscoverPanel.tsx +88 -2
- package/app/components/settings/KnowledgeTab.tsx +61 -0
- package/app/components/walkthrough/steps.ts +11 -6
- package/app/hooks/useFileImport.ts +191 -0
- package/app/hooks/useFileUpload.ts +11 -0
- package/app/hooks/useMention.ts +14 -6
- package/app/hooks/useSlashCommand.ts +114 -0
- package/app/lib/actions.ts +79 -2
- package/app/lib/agent/index.ts +1 -1
- package/app/lib/agent/prompt.ts +2 -0
- package/app/lib/agent/tools.ts +252 -0
- package/app/lib/core/create-space.ts +11 -4
- package/app/lib/core/file-convert.ts +97 -0
- package/app/lib/core/index.ts +1 -1
- package/app/lib/core/organize.ts +105 -0
- package/app/lib/i18n-en.ts +102 -46
- package/app/lib/i18n-zh.ts +101 -45
- package/app/lib/mcp-agents.ts +8 -0
- package/app/lib/pdf-extract.ts +33 -0
- package/app/lib/pi-integration/extensions.ts +45 -0
- package/app/lib/pi-integration/mcporter.ts +219 -0
- package/app/lib/pi-integration/session-store.ts +62 -0
- package/app/lib/pi-integration/skills.ts +116 -0
- package/app/lib/settings.ts +1 -1
- package/app/next-env.d.ts +1 -1
- package/app/next.config.ts +1 -1
- package/app/package.json +2 -0
- package/mcp/src/index.ts +29 -0
- package/package.json +1 -1
|
@@ -11,8 +11,10 @@ export function PillButton({ active, label, onClick }: { active: boolean; label:
|
|
|
11
11
|
type="button"
|
|
12
12
|
onClick={onClick}
|
|
13
13
|
aria-pressed={active}
|
|
14
|
-
className={`px-2.5 min-h-[28px] rounded text-xs cursor-pointer transition-
|
|
15
|
-
active
|
|
14
|
+
className={`relative px-2.5 min-h-[28px] rounded text-xs cursor-pointer transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${
|
|
15
|
+
active
|
|
16
|
+
? 'bg-[var(--amber-dim)] text-[var(--amber)] font-medium shadow-[0_1px_2px_rgba(200,135,58,0.08)]'
|
|
17
|
+
: 'text-muted-foreground hover:text-foreground hover:bg-muted/60'
|
|
16
18
|
}`}
|
|
17
19
|
>
|
|
18
20
|
{label}
|
|
@@ -21,11 +23,13 @@ export function PillButton({ active, label, onClick }: { active: boolean; label:
|
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export function StatusDot({ tone, label, count }: { tone: 'ok' | 'warn' | 'neutral'; label: string; count: number }) {
|
|
24
|
-
const dotCls = tone === 'ok' ? 'bg-[var(--success)]' : tone === 'warn' ? 'bg-[var(--amber)]' : 'bg-muted-foreground';
|
|
26
|
+
const dotCls = tone === 'ok' ? 'bg-[var(--success)]' : tone === 'warn' ? 'bg-[var(--amber)]' : 'bg-muted-foreground/60';
|
|
27
|
+
const countCls = tone === 'ok' ? 'text-foreground' : tone === 'warn' ? 'text-[var(--amber)]' : 'text-muted-foreground';
|
|
25
28
|
return (
|
|
26
29
|
<span className="inline-flex items-center gap-1.5 text-muted-foreground">
|
|
27
|
-
<span className={`w-
|
|
28
|
-
|
|
30
|
+
<span className={`w-2 h-2 rounded-full ${dotCls} ${tone === 'ok' ? 'ring-2 ring-[var(--success)]/20' : ''}`} aria-hidden="true" />
|
|
31
|
+
<span className="text-xs">{label}</span>
|
|
32
|
+
<span className={`tabular-nums font-medium ${countCls}`}>{count}</span>
|
|
29
33
|
</span>
|
|
30
34
|
);
|
|
31
35
|
}
|
|
@@ -44,23 +48,23 @@ export function SearchInput({
|
|
|
44
48
|
icon: React.ComponentType<{ size?: number; className?: string }>;
|
|
45
49
|
}) {
|
|
46
50
|
return (
|
|
47
|
-
<label className="relative block">
|
|
48
|
-
<Icon size={14} className="absolute left-
|
|
51
|
+
<label className="relative block group/search">
|
|
52
|
+
<Icon size={14} className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground/60 group-focus-within/search:text-[var(--amber)] pointer-events-none transition-colors duration-150" />
|
|
49
53
|
<input
|
|
50
54
|
value={value}
|
|
51
55
|
onChange={(e) => onChange(e.target.value)}
|
|
52
56
|
placeholder={placeholder}
|
|
53
57
|
aria-label={ariaLabel}
|
|
54
|
-
className="w-full h-9 rounded-
|
|
58
|
+
className="w-full h-9 rounded-lg border border-border bg-background pl-9 pr-8 text-sm text-foreground placeholder:text-muted-foreground/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--amber)]/30 focus-visible:border-[var(--amber)]/40 transition-all duration-150"
|
|
55
59
|
/>
|
|
56
60
|
{value.length > 0 && (
|
|
57
61
|
<button
|
|
58
62
|
type="button"
|
|
59
63
|
onClick={() => onChange('')}
|
|
60
64
|
aria-label="Clear search"
|
|
61
|
-
className="absolute right-2 top-1/2 -translate-y-1/2 p-0.5 rounded-
|
|
65
|
+
className="absolute right-2.5 top-1/2 -translate-y-1/2 p-0.5 rounded-full text-muted-foreground hover:text-foreground hover:bg-muted cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors duration-150"
|
|
62
66
|
>
|
|
63
|
-
<X size={
|
|
67
|
+
<X size={13} />
|
|
64
68
|
</button>
|
|
65
69
|
)}
|
|
66
70
|
</label>
|
|
@@ -112,20 +116,35 @@ export function BulkMessage({ message }: { message: string | null }) {
|
|
|
112
116
|
);
|
|
113
117
|
}
|
|
114
118
|
|
|
115
|
-
export function EmptyState({ message, className }: { message: string; className?: string }) {
|
|
119
|
+
export function EmptyState({ message, icon, className }: { message: string; icon?: React.ReactNode; className?: string }) {
|
|
116
120
|
return (
|
|
117
|
-
<div className={`rounded-
|
|
118
|
-
|
|
121
|
+
<div className={`rounded-xl border border-dashed border-border/60 bg-gradient-to-b from-card/80 to-card/40 p-10 text-center ${className ?? ''}`}>
|
|
122
|
+
{icon && (
|
|
123
|
+
<div className="w-10 h-10 rounded-full bg-muted/50 flex items-center justify-center mx-auto mb-3 text-muted-foreground/40">
|
|
124
|
+
{icon}
|
|
125
|
+
</div>
|
|
126
|
+
)}
|
|
127
|
+
<p className="text-sm text-muted-foreground/70 leading-relaxed max-w-xs mx-auto">{message}</p>
|
|
119
128
|
</div>
|
|
120
129
|
);
|
|
121
130
|
}
|
|
122
131
|
|
|
123
132
|
/* ────────── Agent Avatar ────────── */
|
|
124
133
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
'bg-
|
|
128
|
-
'bg-
|
|
134
|
+
/** Soft pastel palette: [bg, border, text] — watercolor aesthetic */
|
|
135
|
+
const AVATAR_PALETTES: [string, string, string][] = [
|
|
136
|
+
['bg-rose-100/70', 'border-rose-300/50', 'text-rose-600/80'],
|
|
137
|
+
['bg-violet-100/70', 'border-violet-300/50', 'text-violet-600/80'],
|
|
138
|
+
['bg-emerald-100/70', 'border-emerald-300/50', 'text-emerald-600/80'],
|
|
139
|
+
['bg-sky-100/70', 'border-sky-300/50', 'text-sky-600/80'],
|
|
140
|
+
['bg-amber-100/70', 'border-amber-300/50', 'text-amber-700/80'],
|
|
141
|
+
['bg-teal-100/70', 'border-teal-300/50', 'text-teal-600/80'],
|
|
142
|
+
['bg-pink-100/70', 'border-pink-300/50', 'text-pink-600/80'],
|
|
143
|
+
['bg-indigo-100/70', 'border-indigo-300/50', 'text-indigo-600/80'],
|
|
144
|
+
['bg-lime-100/70', 'border-lime-300/50', 'text-lime-700/80'],
|
|
145
|
+
['bg-fuchsia-100/70', 'border-fuchsia-300/50', 'text-fuchsia-600/80'],
|
|
146
|
+
['bg-cyan-100/70', 'border-cyan-300/50', 'text-cyan-600/80'],
|
|
147
|
+
['bg-orange-100/70', 'border-orange-300/50', 'text-orange-600/80'],
|
|
129
148
|
];
|
|
130
149
|
|
|
131
150
|
function hashName(str: string): number {
|
|
@@ -153,13 +172,13 @@ export function AgentAvatar({
|
|
|
153
172
|
onRemove?: () => void;
|
|
154
173
|
href?: string;
|
|
155
174
|
}) {
|
|
156
|
-
const
|
|
175
|
+
const [bg, border, text] = AVATAR_PALETTES[hashName(name) % AVATAR_PALETTES.length];
|
|
157
176
|
const sizeClasses = size === 'sm' ? 'w-7 h-7 text-[10px]' : 'w-9 h-9 text-xs';
|
|
158
177
|
const dotColor = status === 'connected' ? 'bg-[var(--success)]' : status === 'detected' ? 'bg-[var(--amber)]' : 'bg-muted-foreground';
|
|
159
178
|
|
|
160
179
|
return (
|
|
161
180
|
<div className="relative group/avatar" title={name}>
|
|
162
|
-
<div className={`${sizeClasses} ${
|
|
181
|
+
<div className={`${sizeClasses} ${bg} ${border} ${text} border rounded-full flex items-center justify-center font-semibold select-none`}>
|
|
163
182
|
{initials(name)}
|
|
164
183
|
</div>
|
|
165
184
|
{status && (
|
|
@@ -322,9 +341,11 @@ export function AgentPickerPopover({
|
|
|
322
341
|
onClick={() => onSelect(agent.key)}
|
|
323
342
|
className="w-full text-left px-3 py-2 text-xs text-foreground hover:bg-muted cursor-pointer flex items-center gap-2 transition-colors duration-100"
|
|
324
343
|
>
|
|
325
|
-
|
|
344
|
+
{(() => { const [bg, bdr, txt] = AVATAR_PALETTES[hashName(agent.name) % AVATAR_PALETTES.length]; return (
|
|
345
|
+
<div className={`w-6 h-6 rounded-full border ${bg} ${bdr} ${txt} flex items-center justify-center text-[9px] font-semibold shrink-0`}>
|
|
326
346
|
{initials(agent.name)}
|
|
327
347
|
</div>
|
|
348
|
+
); })()}
|
|
328
349
|
{agent.name}
|
|
329
350
|
</button>
|
|
330
351
|
))}
|
|
@@ -207,7 +207,12 @@ export default function AgentsSkillsSection({
|
|
|
207
207
|
<section className="space-y-4 overflow-hidden" aria-label={copy.title}>
|
|
208
208
|
{/* Header */}
|
|
209
209
|
<div className="flex items-center justify-between gap-3">
|
|
210
|
-
<h2 className="text-sm font-
|
|
210
|
+
<h2 className="text-sm font-semibold text-foreground flex items-center gap-2">
|
|
211
|
+
<div className="w-6 h-6 rounded-md bg-muted/50 flex items-center justify-center">
|
|
212
|
+
<Zap size={13} className="text-muted-foreground/70" aria-hidden="true" />
|
|
213
|
+
</div>
|
|
214
|
+
{copy.title}
|
|
215
|
+
</h2>
|
|
211
216
|
<div className="flex items-center gap-1 rounded-md border border-border p-0.5 bg-background" role="tablist" aria-label={copy.title}>
|
|
212
217
|
<PillButton active={view === 'bySkill'} label={copy.tabs.bySkill} onClick={() => setView('bySkill')} />
|
|
213
218
|
<PillButton active={view === 'byAgent'} label={copy.tabs.byAgent} onClick={() => setView('byAgent')} />
|
|
@@ -215,8 +220,8 @@ export default function AgentsSkillsSection({
|
|
|
215
220
|
</div>
|
|
216
221
|
|
|
217
222
|
{/* Compact status strip */}
|
|
218
|
-
<div className="rounded-
|
|
219
|
-
<div className="flex flex-wrap items-center gap-x-
|
|
223
|
+
<div className="rounded-xl border border-border/60 bg-gradient-to-r from-card to-card/80 p-3.5">
|
|
224
|
+
<div className="flex flex-wrap items-center gap-x-5 gap-y-2 text-xs">
|
|
220
225
|
<span className="inline-flex items-center gap-1.5 text-muted-foreground">
|
|
221
226
|
<span className="w-1.5 h-1.5 rounded-full bg-[var(--success)]" aria-hidden="true" />
|
|
222
227
|
{copy.summaryEnabled(enabledCount)}
|
|
@@ -446,8 +451,12 @@ function BySkillView({
|
|
|
446
451
|
<div className="space-y-3">
|
|
447
452
|
{sortedGrouped.map(([groupKey, sortedSkills]) => (
|
|
448
453
|
<div key={groupKey}>
|
|
449
|
-
<div className="
|
|
450
|
-
|
|
454
|
+
<div className="flex items-center gap-2 mb-2.5">
|
|
455
|
+
<span className="w-1 h-4 rounded-full bg-[var(--amber)]/40" aria-hidden="true" />
|
|
456
|
+
<span className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
|
457
|
+
{copy.groupLabels[groupKey as keyof typeof copy.groupLabels]}
|
|
458
|
+
</span>
|
|
459
|
+
<span className="text-2xs tabular-nums text-muted-foreground/50 font-medium">({sortedSkills.length})</span>
|
|
451
460
|
</div>
|
|
452
461
|
<div className="space-y-3">
|
|
453
462
|
{sortedSkills.map((skill) => {
|
|
@@ -457,7 +466,7 @@ function BySkillView({
|
|
|
457
466
|
const isUserSkill = skill.kind === 'mindos' && skill.source === 'user';
|
|
458
467
|
|
|
459
468
|
return (
|
|
460
|
-
<div key={skill.name} className="rounded-
|
|
469
|
+
<div key={skill.name} className="rounded-xl border border-border bg-card p-4 hover:shadow-[0_2px_8px_rgba(0,0,0,0.04)] transition-all duration-200">
|
|
461
470
|
{/* Skill header */}
|
|
462
471
|
<div className="flex items-center justify-between gap-2 mb-2">
|
|
463
472
|
<div className="flex items-center gap-2 min-w-0">
|
|
@@ -676,7 +685,7 @@ function AgentCard({
|
|
|
676
685
|
const visibleNative = nativeExpanded ? nativeSkills : nativeSkills.slice(0, NATIVE_COLLAPSE_THRESHOLD);
|
|
677
686
|
|
|
678
687
|
return (
|
|
679
|
-
<div className="rounded-
|
|
688
|
+
<div className="rounded-xl border border-border bg-card hover:shadow-[0_2px_8px_rgba(0,0,0,0.04)] transition-all duration-200 overflow-hidden">
|
|
680
689
|
{/* Card header with avatar */}
|
|
681
690
|
<div className="flex items-center gap-3 p-4 pb-0">
|
|
682
691
|
<AgentAvatar name={name} status={status} />
|
|
@@ -217,14 +217,14 @@ export default function SkillDetailPopover({
|
|
|
217
217
|
className="fixed right-0 top-0 z-50 h-full w-full max-w-md border-l border-border bg-card shadow-2xl flex flex-col animate-in slide-in-from-right duration-200"
|
|
218
218
|
>
|
|
219
219
|
{/* ─── Header ─── */}
|
|
220
|
-
<div className="flex items-center gap-3 px-5 py-4 border-b border-border shrink-0">
|
|
221
|
-
<div className="w-9 h-9 rounded-
|
|
220
|
+
<div className="flex items-center gap-3 px-5 py-4 border-b border-border/60 bg-gradient-to-r from-card to-card/80 shrink-0">
|
|
221
|
+
<div className="w-9 h-9 rounded-xl bg-[var(--amber)]/[0.08] flex items-center justify-center text-[var(--amber)] shrink-0">
|
|
222
222
|
<CapIcon size={18} />
|
|
223
223
|
</div>
|
|
224
224
|
<div className="flex-1 min-w-0">
|
|
225
225
|
<h2 className="text-sm font-semibold text-foreground truncate">{skillName}</h2>
|
|
226
|
-
<div className="flex items-center gap-2 mt-
|
|
227
|
-
<span className={`text-2xs px-
|
|
226
|
+
<div className="flex items-center gap-2 mt-1">
|
|
227
|
+
<span className={`text-2xs px-2 py-0.5 rounded-full font-medium select-none ${
|
|
228
228
|
isNative
|
|
229
229
|
? 'bg-muted text-muted-foreground'
|
|
230
230
|
: skill?.source === 'builtin'
|
|
@@ -233,7 +233,7 @@ export default function SkillDetailPopover({
|
|
|
233
233
|
}`}>
|
|
234
234
|
{sourceLabel}
|
|
235
235
|
</span>
|
|
236
|
-
<span className="text-2xs text-muted-foreground capitalize">{capability}</span>
|
|
236
|
+
<span className="text-2xs text-muted-foreground/60 capitalize">{capability}</span>
|
|
237
237
|
</div>
|
|
238
238
|
</div>
|
|
239
239
|
<button
|
|
@@ -267,9 +267,9 @@ export default function SkillDetailPopover({
|
|
|
267
267
|
|
|
268
268
|
{/* Path */}
|
|
269
269
|
{skillPath && (
|
|
270
|
-
<div className="rounded-
|
|
271
|
-
<span className="text-2xs text-muted-foreground block mb-1">{copy.path}</span>
|
|
272
|
-
<code className="text-xs text-foreground font-mono break-all leading-relaxed">{skillPath}</code>
|
|
270
|
+
<div className="rounded-xl border border-border/50 bg-muted/[0.03] p-3.5">
|
|
271
|
+
<span className="text-2xs text-muted-foreground/60 block mb-1.5 uppercase tracking-wider">{copy.path}</span>
|
|
272
|
+
<code className="text-xs text-foreground/80 font-mono break-all leading-relaxed">{skillPath}</code>
|
|
273
273
|
</div>
|
|
274
274
|
)}
|
|
275
275
|
|
|
@@ -410,9 +410,9 @@ function MetaCard({
|
|
|
410
410
|
: tone === 'muted' ? 'text-muted-foreground'
|
|
411
411
|
: 'text-foreground';
|
|
412
412
|
return (
|
|
413
|
-
<div className="rounded-
|
|
414
|
-
<span className="text-2xs text-muted-foreground block mb-
|
|
415
|
-
<span className={`text-sm font-
|
|
413
|
+
<div className="rounded-xl border border-border/50 bg-muted/[0.03] px-3.5 py-3 hover:bg-muted/[0.06] transition-colors duration-100">
|
|
414
|
+
<span className="text-2xs text-muted-foreground/60 block mb-1 uppercase tracking-wider">{label}</span>
|
|
415
|
+
<span className={`text-sm font-semibold capitalize ${valueColor}`}>{value}</span>
|
|
416
416
|
</div>
|
|
417
417
|
);
|
|
418
418
|
}
|
|
@@ -56,11 +56,10 @@ export function groupSkillsByCapability(skills: SkillInfo[]): Record<SkillCapabi
|
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return items;
|
|
59
|
+
export interface RiskCopy {
|
|
60
|
+
riskMcpStopped: string;
|
|
61
|
+
riskDetected: (n: number) => string;
|
|
62
|
+
riskSkillsDisabled: string;
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
export function buildRiskQueue(args: {
|
|
@@ -68,9 +67,12 @@ export function buildRiskQueue(args: {
|
|
|
68
67
|
detectedCount: number;
|
|
69
68
|
notFoundCount: number;
|
|
70
69
|
allSkillsDisabled: boolean;
|
|
70
|
+
copy: RiskCopy;
|
|
71
71
|
}): RiskItem[] {
|
|
72
|
-
const items =
|
|
73
|
-
if (args.
|
|
72
|
+
const items: RiskItem[] = [];
|
|
73
|
+
if (!args.mcpRunning) items.push({ id: 'mcp-stopped', severity: 'error', title: args.copy.riskMcpStopped });
|
|
74
|
+
if (args.detectedCount > 0) items.push({ id: 'detected-unconfigured', severity: 'warn', title: args.copy.riskDetected(args.detectedCount) });
|
|
75
|
+
if (args.allSkillsDisabled) items.push({ id: 'skills-disabled', severity: 'warn', title: args.copy.riskSkillsDisabled });
|
|
74
76
|
return items;
|
|
75
77
|
}
|
|
76
78
|
|
|
@@ -178,12 +180,18 @@ export function filterAgentsForMcpWorkspace(
|
|
|
178
180
|
});
|
|
179
181
|
}
|
|
180
182
|
|
|
183
|
+
const defaultRiskCopy: RiskCopy = {
|
|
184
|
+
riskMcpStopped: 'MCP server is not running.',
|
|
185
|
+
riskDetected: (n: number) => `${n} detected agent(s) need configuration.`,
|
|
186
|
+
riskSkillsDisabled: 'All skills are disabled.',
|
|
187
|
+
};
|
|
188
|
+
|
|
181
189
|
export function buildMcpRiskQueue(args: {
|
|
182
190
|
mcpRunning: boolean;
|
|
183
191
|
detectedCount: number;
|
|
184
192
|
notFoundCount: number;
|
|
185
193
|
}): RiskItem[] {
|
|
186
|
-
return
|
|
194
|
+
return buildRiskQueue({ ...args, allSkillsDisabled: false, copy: defaultRiskCopy });
|
|
187
195
|
}
|
|
188
196
|
|
|
189
197
|
export interface McpBulkReconnectResult {
|