@geminilight/mindos 0.5.52 → 0.5.54
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 +7 -7
- package/README_zh.md +5 -5
- package/app/components/ActivityBar.tsx +3 -2
- package/app/components/Panel.tsx +1 -0
- package/app/components/RightAgentDetailPanel.tsx +121 -0
- package/app/components/SidebarLayout.tsx +62 -5
- package/app/components/panels/AgentsPanel.tsx +156 -178
- package/app/components/panels/AgentsPanelAgentDetail.tsx +193 -0
- package/app/components/panels/AgentsPanelAgentGroups.tsx +116 -0
- package/app/components/panels/AgentsPanelAgentListRow.tsx +101 -0
- package/app/components/panels/AgentsPanelHubNav.tsx +48 -0
- package/app/components/panels/DiscoverPanel.tsx +6 -46
- package/app/components/panels/EchoPanel.tsx +82 -0
- package/app/components/panels/PanelNavRow.tsx +51 -0
- package/app/components/panels/agents-panel-resolve-status.ts +13 -0
- package/app/lib/i18n-en.ts +29 -2
- package/app/lib/i18n-zh.ts +28 -2
- package/package.json +1 -1
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ChevronDown, ChevronRight } from 'lucide-react';
|
|
4
|
+
import type { AgentInfo } from '../settings/types';
|
|
5
|
+
import type { McpContextValue } from '@/hooks/useMcpData';
|
|
6
|
+
import AgentsPanelAgentListRow, { type AgentsPanelAgentListRowCopy } from './AgentsPanelAgentListRow';
|
|
7
|
+
|
|
8
|
+
type AgentsCopy = {
|
|
9
|
+
rosterLabel: string;
|
|
10
|
+
sectionConnected: string;
|
|
11
|
+
sectionDetected: string;
|
|
12
|
+
sectionNotDetected: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function AgentsPanelAgentGroups({
|
|
16
|
+
connected,
|
|
17
|
+
detected,
|
|
18
|
+
notFound,
|
|
19
|
+
onOpenDetail,
|
|
20
|
+
selectedAgentKey,
|
|
21
|
+
mcp,
|
|
22
|
+
listCopy,
|
|
23
|
+
showNotDetected,
|
|
24
|
+
setShowNotDetected,
|
|
25
|
+
p,
|
|
26
|
+
}: {
|
|
27
|
+
connected: AgentInfo[];
|
|
28
|
+
detected: AgentInfo[];
|
|
29
|
+
notFound: AgentInfo[];
|
|
30
|
+
onOpenDetail?: (key: string) => void;
|
|
31
|
+
selectedAgentKey?: string | null;
|
|
32
|
+
mcp: Pick<McpContextValue, 'installAgent'>;
|
|
33
|
+
listCopy: AgentsPanelAgentListRowCopy;
|
|
34
|
+
showNotDetected: boolean;
|
|
35
|
+
setShowNotDetected: (v: boolean | ((prev: boolean) => boolean)) => void;
|
|
36
|
+
p: AgentsCopy;
|
|
37
|
+
}) {
|
|
38
|
+
const open = onOpenDetail ?? (() => {});
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div>
|
|
42
|
+
<div className="px-0 py-1 mb-0.5">
|
|
43
|
+
<span className="text-2xs font-semibold text-muted-foreground uppercase tracking-wider">{p.rosterLabel}</span>
|
|
44
|
+
</div>
|
|
45
|
+
{connected.length > 0 && (
|
|
46
|
+
<section className="mb-3">
|
|
47
|
+
<h3 className="text-[11px] font-medium text-muted-foreground/90 uppercase tracking-wider mb-2 pl-0.5">
|
|
48
|
+
{p.sectionConnected} ({connected.length})
|
|
49
|
+
</h3>
|
|
50
|
+
<div className="space-y-1.5">
|
|
51
|
+
{connected.map(agent => (
|
|
52
|
+
<AgentsPanelAgentListRow
|
|
53
|
+
key={agent.key}
|
|
54
|
+
agent={agent}
|
|
55
|
+
agentStatus="connected"
|
|
56
|
+
selected={selectedAgentKey === agent.key}
|
|
57
|
+
onOpenDetail={() => open(agent.key)}
|
|
58
|
+
onInstallAgent={mcp.installAgent}
|
|
59
|
+
copy={listCopy}
|
|
60
|
+
/>
|
|
61
|
+
))}
|
|
62
|
+
</div>
|
|
63
|
+
</section>
|
|
64
|
+
)}
|
|
65
|
+
|
|
66
|
+
{detected.length > 0 && (
|
|
67
|
+
<section className="mb-3">
|
|
68
|
+
<h3 className="text-[11px] font-medium text-muted-foreground/90 uppercase tracking-wider mb-2 pl-0.5">
|
|
69
|
+
{p.sectionDetected} ({detected.length})
|
|
70
|
+
</h3>
|
|
71
|
+
<div className="space-y-1.5">
|
|
72
|
+
{detected.map(agent => (
|
|
73
|
+
<AgentsPanelAgentListRow
|
|
74
|
+
key={agent.key}
|
|
75
|
+
agent={agent}
|
|
76
|
+
agentStatus="detected"
|
|
77
|
+
selected={selectedAgentKey === agent.key}
|
|
78
|
+
onOpenDetail={() => open(agent.key)}
|
|
79
|
+
onInstallAgent={mcp.installAgent}
|
|
80
|
+
copy={listCopy}
|
|
81
|
+
/>
|
|
82
|
+
))}
|
|
83
|
+
</div>
|
|
84
|
+
</section>
|
|
85
|
+
)}
|
|
86
|
+
|
|
87
|
+
{notFound.length > 0 && (
|
|
88
|
+
<section>
|
|
89
|
+
<button
|
|
90
|
+
type="button"
|
|
91
|
+
onClick={() => setShowNotDetected(!showNotDetected)}
|
|
92
|
+
className="flex items-center gap-1 text-[11px] font-medium text-muted-foreground uppercase tracking-wider mb-2 hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm pl-0.5"
|
|
93
|
+
>
|
|
94
|
+
{showNotDetected ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
|
|
95
|
+
{p.sectionNotDetected} ({notFound.length})
|
|
96
|
+
</button>
|
|
97
|
+
{showNotDetected && (
|
|
98
|
+
<div className="space-y-1.5">
|
|
99
|
+
{notFound.map(agent => (
|
|
100
|
+
<AgentsPanelAgentListRow
|
|
101
|
+
key={agent.key}
|
|
102
|
+
agent={agent}
|
|
103
|
+
agentStatus="notFound"
|
|
104
|
+
selected={selectedAgentKey === agent.key}
|
|
105
|
+
onOpenDetail={() => open(agent.key)}
|
|
106
|
+
onInstallAgent={mcp.installAgent}
|
|
107
|
+
copy={listCopy}
|
|
108
|
+
/>
|
|
109
|
+
))}
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
</section>
|
|
113
|
+
)}
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { ChevronRight, Loader2 } from 'lucide-react';
|
|
5
|
+
import type { AgentInfo } from '../settings/types';
|
|
6
|
+
|
|
7
|
+
export type AgentsPanelAgentListStatus = 'connected' | 'detected' | 'notFound';
|
|
8
|
+
|
|
9
|
+
export interface AgentsPanelAgentListRowCopy {
|
|
10
|
+
installing: string;
|
|
11
|
+
install: (name: string) => string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function AgentsPanelAgentListRow({
|
|
15
|
+
agent,
|
|
16
|
+
agentStatus,
|
|
17
|
+
selected = false,
|
|
18
|
+
onOpenDetail,
|
|
19
|
+
onInstallAgent,
|
|
20
|
+
copy,
|
|
21
|
+
}: {
|
|
22
|
+
agent: AgentInfo;
|
|
23
|
+
agentStatus: AgentsPanelAgentListStatus;
|
|
24
|
+
selected?: boolean;
|
|
25
|
+
onOpenDetail: () => void;
|
|
26
|
+
onInstallAgent: (key: string) => Promise<boolean>;
|
|
27
|
+
copy: AgentsPanelAgentListRowCopy;
|
|
28
|
+
}) {
|
|
29
|
+
const dot =
|
|
30
|
+
agentStatus === 'connected' ? 'bg-emerald-500' : agentStatus === 'detected' ? 'bg-amber-500' : 'bg-zinc-400';
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div
|
|
34
|
+
className={`
|
|
35
|
+
group flex items-center gap-0 rounded-xl border transition-all duration-150
|
|
36
|
+
${selected
|
|
37
|
+
? 'border-border ring-2 ring-ring/50 bg-[var(--amber-dim)]/45'
|
|
38
|
+
: 'border-border/70 bg-card/50 hover:border-border hover:bg-muted/25'}
|
|
39
|
+
`}
|
|
40
|
+
>
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
43
|
+
onClick={onOpenDetail}
|
|
44
|
+
className="flex flex-1 min-w-0 items-center gap-2.5 text-left rounded-xl pl-3 pr-2 py-2.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
45
|
+
>
|
|
46
|
+
<span className={`w-2 h-2 rounded-full shrink-0 ring-2 ring-background ${dot}`} />
|
|
47
|
+
<span className="text-sm font-medium text-foreground truncate leading-tight">{agent.name}</span>
|
|
48
|
+
{agentStatus === 'connected' && agent.transport && (
|
|
49
|
+
<span className="text-2xs font-mono tabular-nums px-1.5 py-0.5 rounded-md bg-muted/90 text-muted-foreground shrink-0 border border-border/50">
|
|
50
|
+
{agent.transport}
|
|
51
|
+
</span>
|
|
52
|
+
)}
|
|
53
|
+
<span className="flex-1 min-w-[4px]" />
|
|
54
|
+
<ChevronRight
|
|
55
|
+
size={15}
|
|
56
|
+
className={`shrink-0 transition-opacity duration-150 ${selected ? 'text-[var(--amber)] opacity-90' : 'text-muted-foreground/45 group-hover:text-muted-foreground/80'}`}
|
|
57
|
+
aria-hidden
|
|
58
|
+
/>
|
|
59
|
+
</button>
|
|
60
|
+
|
|
61
|
+
{agentStatus === 'detected' && (
|
|
62
|
+
<div className="pr-2 py-2 shrink-0">
|
|
63
|
+
<AgentInstallButton agentKey={agent.key} agentName={agent.name} onInstallAgent={onInstallAgent} copy={copy} />
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function AgentInstallButton({
|
|
71
|
+
agentKey,
|
|
72
|
+
agentName,
|
|
73
|
+
onInstallAgent,
|
|
74
|
+
copy,
|
|
75
|
+
}: {
|
|
76
|
+
agentKey: string;
|
|
77
|
+
agentName: string;
|
|
78
|
+
onInstallAgent: (key: string) => Promise<boolean>;
|
|
79
|
+
copy: AgentsPanelAgentListRowCopy;
|
|
80
|
+
}) {
|
|
81
|
+
const [installing, setInstalling] = useState(false);
|
|
82
|
+
|
|
83
|
+
const handleInstall = async (e: React.MouseEvent) => {
|
|
84
|
+
e.stopPropagation();
|
|
85
|
+
setInstalling(true);
|
|
86
|
+
await onInstallAgent(agentKey);
|
|
87
|
+
setInstalling(false);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<button
|
|
92
|
+
type="button"
|
|
93
|
+
onClick={handleInstall}
|
|
94
|
+
disabled={installing}
|
|
95
|
+
className="flex items-center gap-1 px-2 py-1.5 text-2xs rounded-lg font-medium text-[var(--amber-foreground)] disabled:opacity-50 transition-colors shrink-0 bg-[var(--amber)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
96
|
+
>
|
|
97
|
+
{installing ? <Loader2 size={10} className="animate-spin" /> : null}
|
|
98
|
+
{installing ? copy.installing : copy.install(agentName)}
|
|
99
|
+
</button>
|
|
100
|
+
);
|
|
101
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { type RefObject } from 'react';
|
|
4
|
+
import { LayoutDashboard, Server, Zap } from 'lucide-react';
|
|
5
|
+
import { PanelNavRow } from './PanelNavRow';
|
|
6
|
+
|
|
7
|
+
type HubCopy = {
|
|
8
|
+
navOverview: string;
|
|
9
|
+
navMcp: string;
|
|
10
|
+
navSkills: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function AgentsPanelHubNav({
|
|
14
|
+
copy,
|
|
15
|
+
connectedCount,
|
|
16
|
+
overviewRef,
|
|
17
|
+
skillsRef,
|
|
18
|
+
scrollTo,
|
|
19
|
+
openAdvancedConfig,
|
|
20
|
+
}: {
|
|
21
|
+
copy: HubCopy;
|
|
22
|
+
connectedCount: number;
|
|
23
|
+
overviewRef: RefObject<HTMLDivElement | null>;
|
|
24
|
+
skillsRef: RefObject<HTMLDivElement | null>;
|
|
25
|
+
scrollTo: (el: HTMLElement | null) => void;
|
|
26
|
+
openAdvancedConfig: () => void;
|
|
27
|
+
}) {
|
|
28
|
+
return (
|
|
29
|
+
<div className="py-2">
|
|
30
|
+
<PanelNavRow
|
|
31
|
+
icon={<LayoutDashboard size={14} className="text-[var(--amber)]" />}
|
|
32
|
+
title={copy.navOverview}
|
|
33
|
+
badge={<span className="text-2xs tabular-nums text-muted-foreground">{connectedCount}</span>}
|
|
34
|
+
onClick={() => scrollTo(overviewRef.current)}
|
|
35
|
+
/>
|
|
36
|
+
<PanelNavRow
|
|
37
|
+
icon={<Server size={14} className="text-muted-foreground" />}
|
|
38
|
+
title={copy.navMcp}
|
|
39
|
+
onClick={openAdvancedConfig}
|
|
40
|
+
/>
|
|
41
|
+
<PanelNavRow
|
|
42
|
+
icon={<Zap size={14} className="text-muted-foreground" />}
|
|
43
|
+
title={copy.navSkills}
|
|
44
|
+
onClick={() => scrollTo(skillsRef.current)}
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { Lightbulb, Blocks, Zap, LayoutTemplate, ChevronRight, User, Download, RefreshCw, Repeat, Rocket, Search, Handshake, ShieldCheck } from 'lucide-react';
|
|
3
|
+
import { Lightbulb, Blocks, Zap, LayoutTemplate, User, Download, RefreshCw, Repeat, Rocket, Search, Handshake, ShieldCheck } from 'lucide-react';
|
|
5
4
|
import PanelHeader from './PanelHeader';
|
|
5
|
+
import { PanelNavRow, ComingSoonBadge } from './PanelNavRow';
|
|
6
6
|
import { useLocale } from '@/lib/LocaleContext';
|
|
7
7
|
import { useCases } from '@/components/explore/use-cases';
|
|
8
8
|
import { openAskModal } from '@/hooks/useAskModal';
|
|
@@ -13,46 +13,6 @@ interface DiscoverPanelProps {
|
|
|
13
13
|
onMaximize?: () => void;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
/** Navigation entry — clickable row linking to a page or showing coming soon */
|
|
17
|
-
function NavEntry({
|
|
18
|
-
icon,
|
|
19
|
-
title,
|
|
20
|
-
badge,
|
|
21
|
-
href,
|
|
22
|
-
onClick,
|
|
23
|
-
}: {
|
|
24
|
-
icon: React.ReactNode;
|
|
25
|
-
title: string;
|
|
26
|
-
badge?: React.ReactNode;
|
|
27
|
-
href?: string;
|
|
28
|
-
onClick?: () => void;
|
|
29
|
-
}) {
|
|
30
|
-
const content = (
|
|
31
|
-
<>
|
|
32
|
-
<span className="flex items-center justify-center w-7 h-7 rounded-md bg-muted shrink-0">{icon}</span>
|
|
33
|
-
<span className="text-sm font-medium text-foreground flex-1">{title}</span>
|
|
34
|
-
{badge}
|
|
35
|
-
<ChevronRight size={14} className="text-muted-foreground shrink-0" />
|
|
36
|
-
</>
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
const className = "flex items-center gap-3 px-4 py-2.5 hover:bg-muted/50 transition-colors cursor-pointer";
|
|
40
|
-
|
|
41
|
-
if (href) {
|
|
42
|
-
return <Link href={href} className={className}>{content}</Link>;
|
|
43
|
-
}
|
|
44
|
-
return <button onClick={onClick} className={`${className} w-full`}>{content}</button>;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Coming soon badge */
|
|
48
|
-
function ComingSoonBadge({ label }: { label: string }) {
|
|
49
|
-
return (
|
|
50
|
-
<span className="text-2xs px-2 py-0.5 rounded-full bg-muted text-muted-foreground shrink-0">
|
|
51
|
-
{label}
|
|
52
|
-
</span>
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
16
|
/** Compact use case row */
|
|
57
17
|
function UseCaseRow({
|
|
58
18
|
icon,
|
|
@@ -112,23 +72,23 @@ export default function DiscoverPanel({ active, maximized, onMaximize }: Discove
|
|
|
112
72
|
<div className="flex-1 overflow-y-auto min-h-0">
|
|
113
73
|
{/* Navigation entries */}
|
|
114
74
|
<div className="py-2">
|
|
115
|
-
<
|
|
75
|
+
<PanelNavRow
|
|
116
76
|
icon={<Lightbulb size={14} className="text-[var(--amber)]" />}
|
|
117
77
|
title={d.useCases}
|
|
118
78
|
badge={<span className="text-2xs tabular-nums text-muted-foreground">{useCases.length}</span>}
|
|
119
79
|
href="/explore"
|
|
120
80
|
/>
|
|
121
|
-
<
|
|
81
|
+
<PanelNavRow
|
|
122
82
|
icon={<Blocks size={14} className="text-muted-foreground" />}
|
|
123
83
|
title={d.pluginMarket}
|
|
124
84
|
badge={<ComingSoonBadge label={d.comingSoon} />}
|
|
125
85
|
/>
|
|
126
|
-
<
|
|
86
|
+
<PanelNavRow
|
|
127
87
|
icon={<Zap size={14} className="text-muted-foreground" />}
|
|
128
88
|
title={d.skillMarket}
|
|
129
89
|
badge={<ComingSoonBadge label={d.comingSoon} />}
|
|
130
90
|
/>
|
|
131
|
-
<
|
|
91
|
+
<PanelNavRow
|
|
132
92
|
icon={<LayoutTemplate size={14} className="text-muted-foreground" />}
|
|
133
93
|
title={d.spaceTemplates}
|
|
134
94
|
badge={<ComingSoonBadge label={d.comingSoon} />}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
import { UserRound, Bookmark, Sun, History, Brain } from 'lucide-react';
|
|
5
|
+
import PanelHeader from './PanelHeader';
|
|
6
|
+
import { ComingSoonBadge } from './PanelNavRow';
|
|
7
|
+
import { useLocale } from '@/lib/LocaleContext';
|
|
8
|
+
|
|
9
|
+
interface EchoPanelProps {
|
|
10
|
+
active: boolean;
|
|
11
|
+
maximized?: boolean;
|
|
12
|
+
onMaximize?: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function EchoPlaceholdSection({
|
|
16
|
+
icon,
|
|
17
|
+
title,
|
|
18
|
+
hint,
|
|
19
|
+
comingSoonLabel,
|
|
20
|
+
}: {
|
|
21
|
+
icon: ReactNode;
|
|
22
|
+
title: string;
|
|
23
|
+
hint: string;
|
|
24
|
+
comingSoonLabel: string;
|
|
25
|
+
}) {
|
|
26
|
+
return (
|
|
27
|
+
<div className="px-4 py-2.5 border-b border-border/60 last:border-b-0">
|
|
28
|
+
<div className="flex items-center gap-2.5">
|
|
29
|
+
<span className="flex items-center justify-center w-7 h-7 rounded-md bg-muted shrink-0 text-muted-foreground">{icon}</span>
|
|
30
|
+
<span className="text-sm font-medium text-foreground flex-1 text-left">{title}</span>
|
|
31
|
+
<ComingSoonBadge label={comingSoonLabel} />
|
|
32
|
+
</div>
|
|
33
|
+
<p className="text-2xs text-muted-foreground leading-relaxed mt-1.5 pl-9">{hint}</p>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export default function EchoPanel({ active, maximized, onMaximize }: EchoPanelProps) {
|
|
39
|
+
const { t } = useLocale();
|
|
40
|
+
const e = t.panels.echo;
|
|
41
|
+
const soon = e.comingSoon;
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div className={`flex flex-col h-full ${active ? '' : 'hidden'}`}>
|
|
45
|
+
<PanelHeader title={e.title} maximized={maximized} onMaximize={onMaximize} />
|
|
46
|
+
<div className="flex-1 overflow-y-auto min-h-0">
|
|
47
|
+
<div className="py-1">
|
|
48
|
+
<EchoPlaceholdSection
|
|
49
|
+
icon={<UserRound size={14} />}
|
|
50
|
+
title={e.aboutYouTitle}
|
|
51
|
+
hint={e.aboutYouHint}
|
|
52
|
+
comingSoonLabel={soon}
|
|
53
|
+
/>
|
|
54
|
+
<EchoPlaceholdSection
|
|
55
|
+
icon={<Bookmark size={14} />}
|
|
56
|
+
title={e.continuedTitle}
|
|
57
|
+
hint={e.continuedHint}
|
|
58
|
+
comingSoonLabel={soon}
|
|
59
|
+
/>
|
|
60
|
+
<EchoPlaceholdSection
|
|
61
|
+
icon={<Sun size={14} />}
|
|
62
|
+
title={e.dailyEchoTitle}
|
|
63
|
+
hint={e.dailyEchoHint}
|
|
64
|
+
comingSoonLabel={soon}
|
|
65
|
+
/>
|
|
66
|
+
<EchoPlaceholdSection
|
|
67
|
+
icon={<History size={14} />}
|
|
68
|
+
title={e.pastYouTitle}
|
|
69
|
+
hint={e.pastYouHint}
|
|
70
|
+
comingSoonLabel={soon}
|
|
71
|
+
/>
|
|
72
|
+
<EchoPlaceholdSection
|
|
73
|
+
icon={<Brain size={14} />}
|
|
74
|
+
title={e.intentGrowthTitle}
|
|
75
|
+
hint={e.intentGrowthHint}
|
|
76
|
+
comingSoonLabel={soon}
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from 'react';
|
|
4
|
+
import Link from 'next/link';
|
|
5
|
+
import { ChevronRight } from 'lucide-react';
|
|
6
|
+
|
|
7
|
+
/** Row matching Discover panel nav: icon tile, title, optional badge, chevron. */
|
|
8
|
+
export function PanelNavRow({
|
|
9
|
+
icon,
|
|
10
|
+
title,
|
|
11
|
+
badge,
|
|
12
|
+
href,
|
|
13
|
+
onClick,
|
|
14
|
+
}: {
|
|
15
|
+
icon: ReactNode;
|
|
16
|
+
title: string;
|
|
17
|
+
badge?: React.ReactNode;
|
|
18
|
+
href?: string;
|
|
19
|
+
onClick?: () => void;
|
|
20
|
+
}) {
|
|
21
|
+
const content = (
|
|
22
|
+
<>
|
|
23
|
+
<span className="flex items-center justify-center w-7 h-7 rounded-md bg-muted shrink-0">{icon}</span>
|
|
24
|
+
<span className="text-sm font-medium text-foreground flex-1 text-left">{title}</span>
|
|
25
|
+
{badge}
|
|
26
|
+
<ChevronRight size={14} className="text-muted-foreground shrink-0" />
|
|
27
|
+
</>
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const className =
|
|
31
|
+
'flex items-center gap-3 px-4 py-2.5 hover:bg-muted/50 transition-colors cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm';
|
|
32
|
+
|
|
33
|
+
if (href) {
|
|
34
|
+
return (
|
|
35
|
+
<Link href={href} className={className}>
|
|
36
|
+
{content}
|
|
37
|
+
</Link>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return (
|
|
41
|
+
<button type="button" onClick={onClick} className={`${className} w-full`}>
|
|
42
|
+
{content}
|
|
43
|
+
</button>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function ComingSoonBadge({ label }: { label: string }) {
|
|
48
|
+
return (
|
|
49
|
+
<span className="text-2xs px-2 py-0.5 rounded-full bg-muted text-muted-foreground shrink-0">{label}</span>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type AgentsPanelAgentDetailStatus = 'connected' | 'detected' | 'notFound';
|
|
2
|
+
|
|
3
|
+
export function resolveAgentDetailStatus(
|
|
4
|
+
key: string,
|
|
5
|
+
connected: { key: string }[],
|
|
6
|
+
detected: { key: string }[],
|
|
7
|
+
notFound: { key: string }[],
|
|
8
|
+
): AgentsPanelAgentDetailStatus | null {
|
|
9
|
+
if (connected.some(a => a.key === key)) return 'connected';
|
|
10
|
+
if (detected.some(a => a.key === key)) return 'detected';
|
|
11
|
+
if (notFound.some(a => a.key === key)) return 'notFound';
|
|
12
|
+
return null;
|
|
13
|
+
}
|
package/app/lib/i18n-en.ts
CHANGED
|
@@ -59,6 +59,7 @@ export const en = {
|
|
|
59
59
|
settingsTitle: 'Settings',
|
|
60
60
|
plugins: 'Plugins',
|
|
61
61
|
agents: 'Agents',
|
|
62
|
+
echo: 'Echo',
|
|
62
63
|
discover: 'Discover',
|
|
63
64
|
help: 'Help',
|
|
64
65
|
syncLabel: 'Sync',
|
|
@@ -148,6 +149,18 @@ export const en = {
|
|
|
148
149
|
newSkill: '+ New',
|
|
149
150
|
// Footer
|
|
150
151
|
advancedConfig: 'Advanced Config →',
|
|
152
|
+
// Hub nav (aligned with Discover panel rows)
|
|
153
|
+
navOverview: 'Overview',
|
|
154
|
+
navMcp: 'MCP',
|
|
155
|
+
navSkills: 'Skills',
|
|
156
|
+
rosterLabel: 'Your setup',
|
|
157
|
+
notFoundDetail: 'This agent app was not detected on this machine. Install it, then refresh the list.',
|
|
158
|
+
skillsEmptyHint: 'Enable and edit skills in MCP & Skills settings.',
|
|
159
|
+
backToList: 'Back',
|
|
160
|
+
closeAgentDetail: 'Close',
|
|
161
|
+
agentDetailPanelAria: 'Agent connection configuration',
|
|
162
|
+
agentDetailTransport: 'Transport',
|
|
163
|
+
agentDetailSnippet: 'Config snippet',
|
|
151
164
|
},
|
|
152
165
|
plugins: {
|
|
153
166
|
title: 'Plugins',
|
|
@@ -161,6 +174,20 @@ export const en = {
|
|
|
161
174
|
noFile: 'Entry file not found',
|
|
162
175
|
createFile: 'Create {file} to activate',
|
|
163
176
|
},
|
|
177
|
+
echo: {
|
|
178
|
+
title: 'Echo',
|
|
179
|
+
aboutYouTitle: 'Tied to you',
|
|
180
|
+
aboutYouHint: 'Paths and links in your library that curve back to your name.',
|
|
181
|
+
continuedTitle: 'Still open',
|
|
182
|
+
continuedHint: 'Drafts, half-written lines, todos you never closed.',
|
|
183
|
+
dailyEchoTitle: 'Daily echo',
|
|
184
|
+
dailyEchoHint: 'One quiet line for today—no need to open a full chat.',
|
|
185
|
+
pastYouTitle: 'Who you were',
|
|
186
|
+
pastYouHint: 'Choices and moods you set down at another point on the timeline.',
|
|
187
|
+
intentGrowthTitle: 'Heart & growth',
|
|
188
|
+
intentGrowthHint: 'What you are steering toward, and how it slowly shifts. Stays on device; you can wipe it anytime.',
|
|
189
|
+
comingSoon: 'On its way',
|
|
190
|
+
},
|
|
164
191
|
discover: {
|
|
165
192
|
title: 'Discover',
|
|
166
193
|
useCases: 'Use Cases',
|
|
@@ -702,7 +729,7 @@ export const en = {
|
|
|
702
729
|
c1: {
|
|
703
730
|
title: 'Inject Your Identity',
|
|
704
731
|
desc: 'Tell all AI agents who you are — preferences, tech stack, communication style — in one shot.',
|
|
705
|
-
|
|
732
|
+
prompt: "Here's my resume, read it and organize my info into MindOS.",
|
|
706
733
|
},
|
|
707
734
|
c2: {
|
|
708
735
|
title: 'Save Information',
|
|
@@ -805,7 +832,7 @@ export const en = {
|
|
|
805
832
|
title: 'Using MindOS with AI Agents',
|
|
806
833
|
intro: 'Once you connect an agent (Claude Code, Cursor, Windsurf, etc.) via MCP, just talk to it naturally. The agent can read and write your knowledge base directly — no special commands needed. Here are the most common scenarios:',
|
|
807
834
|
scenarios: [
|
|
808
|
-
{ emoji: '🪪', title: 'Inject Your Identity', desc: 'Tell all AI agents who you are — preferences, tech stack, communication style — in one shot.', prompt:
|
|
835
|
+
{ emoji: '🪪', title: 'Inject Your Identity', desc: 'Tell all AI agents who you are — preferences, tech stack, communication style — in one shot.', prompt: "\"Here's my resume, read it and organize my info into MindOS.\"" },
|
|
809
836
|
{ emoji: '🔄', title: 'Cross-Agent Handoff', desc: 'Brainstorm ideas in GPT, then execute in Claude Code — zero context loss.', prompt: '"Save this conversation to MindOS."\n"Read the plan in MindOS and help me start coding."' },
|
|
810
837
|
{ emoji: '📋', title: 'Experience → SOP', desc: 'Turn hard-won debugging sessions into reusable workflows that prevent future mistakes.', prompt: '"Help me distill this conversation into a reusable workflow in MindOS."' },
|
|
811
838
|
{ emoji: '🚀', title: 'Project Cold Start', desc: 'Spin up a new project in minutes — your profile and SOPs guide the scaffolding automatically.', prompt: '"Help me start a new project following the Startup SOP in MindOS."' },
|
package/app/lib/i18n-zh.ts
CHANGED
|
@@ -84,6 +84,7 @@ export const zh = {
|
|
|
84
84
|
settingsTitle: '设置',
|
|
85
85
|
plugins: '插件',
|
|
86
86
|
agents: '智能体',
|
|
87
|
+
echo: '回响',
|
|
87
88
|
discover: '探索',
|
|
88
89
|
help: '帮助',
|
|
89
90
|
syncLabel: '同步',
|
|
@@ -173,6 +174,17 @@ export const zh = {
|
|
|
173
174
|
newSkill: '+ 新建',
|
|
174
175
|
// Footer
|
|
175
176
|
advancedConfig: '高级配置 →',
|
|
177
|
+
navOverview: '总览',
|
|
178
|
+
navMcp: 'MCP',
|
|
179
|
+
navSkills: '技能',
|
|
180
|
+
rosterLabel: '当前环境',
|
|
181
|
+
notFoundDetail: '本机未检测到该智能体客户端。请安装后刷新列表。',
|
|
182
|
+
skillsEmptyHint: '请在「MCP 与技能」设置中启用或编辑 Skills。',
|
|
183
|
+
backToList: '返回',
|
|
184
|
+
closeAgentDetail: '关闭',
|
|
185
|
+
agentDetailPanelAria: '智能体连接配置',
|
|
186
|
+
agentDetailTransport: '传输方式',
|
|
187
|
+
agentDetailSnippet: '配置片段',
|
|
176
188
|
},
|
|
177
189
|
plugins: {
|
|
178
190
|
title: '插件',
|
|
@@ -186,6 +198,20 @@ export const zh = {
|
|
|
186
198
|
noFile: '入口文件不存在',
|
|
187
199
|
createFile: '创建 {file} 以激活',
|
|
188
200
|
},
|
|
201
|
+
echo: {
|
|
202
|
+
title: '回响',
|
|
203
|
+
aboutYouTitle: '与你有关',
|
|
204
|
+
aboutYouHint: '路径与链接里,绕回你名下的那几笔。',
|
|
205
|
+
continuedTitle: '未完待续',
|
|
206
|
+
continuedHint: '草稿、写到一半的句子、没收口的待办。',
|
|
207
|
+
dailyEchoTitle: '每日回响',
|
|
208
|
+
dailyEchoHint: '给今天留一行空白;轻到不必为此开一场对话。',
|
|
209
|
+
pastYouTitle: '历史的你',
|
|
210
|
+
pastYouHint: '在另一个时间刻度上,你写下的选择与心情。',
|
|
211
|
+
intentGrowthTitle: '心向生长',
|
|
212
|
+
intentGrowthHint: '你在推的方向,以及它怎样慢慢偏转。只存本机,可随时清空。',
|
|
213
|
+
comingSoon: '随后到来',
|
|
214
|
+
},
|
|
189
215
|
discover: {
|
|
190
216
|
title: '探索',
|
|
191
217
|
useCases: '使用案例',
|
|
@@ -727,7 +753,7 @@ export const zh = {
|
|
|
727
753
|
c1: {
|
|
728
754
|
title: '注入身份',
|
|
729
755
|
desc: '让所有 AI Agent 一次认识你 — 偏好、技术栈、沟通风格。',
|
|
730
|
-
|
|
756
|
+
prompt: '这是我的简历,读一下,把我的信息整理到 MindOS 里。',
|
|
731
757
|
},
|
|
732
758
|
c2: {
|
|
733
759
|
title: '注入信息',
|
|
@@ -830,7 +856,7 @@ export const zh = {
|
|
|
830
856
|
title: '在 AI Agent 中使用 MindOS',
|
|
831
857
|
intro: '通过 MCP 连接 Agent(Claude Code、Cursor、Windsurf 等)后,直接用自然语言对话即可。Agent 能自动读写你的知识库,不需要特殊指令。以下是最常见的使用场景:',
|
|
832
858
|
scenarios: [
|
|
833
|
-
{ emoji: '🪪', title: '注入身份', desc: '让所有 AI Agent 一次认识你——偏好、技术栈、沟通风格。', prompt: '"
|
|
859
|
+
{ emoji: '🪪', title: '注入身份', desc: '让所有 AI Agent 一次认识你——偏好、技术栈、沟通风格。', prompt: '"这是我的简历,读一下,把我的信息整理到 MindOS 里。"' },
|
|
834
860
|
{ emoji: '🔄', title: '跨 Agent 切换', desc: '在 GPT 里聊想法,到 Claude Code 去执行——上下文零丢失。', prompt: '"帮我把刚才的对话整理到 MindOS。"\n"读一下 MindOS 里的方案,帮我开始写代码。"' },
|
|
835
861
|
{ emoji: '📋', title: '经验→SOP', desc: '把踩坑经验沉淀为可复用的工作流,下次 3 分钟搞定。', prompt: '"帮我把这次对话的经验沉淀到 MindOS,形成可复用的工作流。"' },
|
|
836
862
|
{ emoji: '🚀', title: '项目冷启动', desc: '几分钟搭建新项目——Profile 和 SOP 自动引导脚手架。', prompt: '"帮我按 MindOS 里的 Startup SOP 启动一个新项目。"' },
|
package/package.json
CHANGED