@geminilight/mindos 0.5.20 → 0.5.21
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 +308 -172
- package/app/components/SettingsModal.tsx +52 -58
- package/app/components/settings/AiTab.tsx +4 -25
- package/app/components/settings/AppearanceTab.tsx +31 -13
- package/app/components/settings/KnowledgeTab.tsx +13 -28
- package/app/components/settings/McpAgentInstall.tsx +227 -0
- package/app/components/settings/McpServerStatus.tsx +172 -0
- package/app/components/settings/McpSkillsSection.tsx +583 -0
- package/app/components/settings/McpTab.tsx +17 -959
- package/app/components/settings/PluginsTab.tsx +4 -27
- package/app/components/settings/Primitives.tsx +69 -0
- package/app/components/settings/ShortcutsTab.tsx +2 -4
- package/app/components/settings/SyncTab.tsx +8 -24
- package/app/components/settings/types.ts +116 -2
- package/app/lib/agent/context.ts +151 -87
- package/app/lib/agent/index.ts +4 -3
- package/app/lib/agent/model.ts +76 -10
- package/app/lib/agent/stream-consumer.ts +73 -77
- package/app/lib/agent/to-agent-messages.ts +106 -0
- package/app/lib/agent/tools.ts +260 -266
- package/app/lib/i18n-en.ts +480 -0
- package/app/lib/i18n-zh.ts +505 -0
- package/app/lib/i18n.ts +4 -963
- package/app/next-env.d.ts +1 -1
- package/app/package-lock.json +3258 -3093
- package/app/package.json +6 -3
- package/bin/cli.js +7 -4
- package/package.json +4 -1
|
@@ -2,12 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { Puzzle } from 'lucide-react';
|
|
4
4
|
import { getAllRenderers, setRendererEnabled } from '@/lib/renderers/registry';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
pluginStates: Record<string, boolean>;
|
|
8
|
-
setPluginStates: React.Dispatch<React.SetStateAction<Record<string, boolean>>>;
|
|
9
|
-
t: any;
|
|
10
|
-
}
|
|
5
|
+
import { Toggle } from './Primitives';
|
|
6
|
+
import type { PluginsTabProps } from './types';
|
|
11
7
|
|
|
12
8
|
export function PluginsTab({ pluginStates, setPluginStates, t }: PluginsTabProps) {
|
|
13
9
|
return (
|
|
@@ -58,28 +54,9 @@ export function PluginsTab({ pluginStates, setPluginStates, t }: PluginsTabProps
|
|
|
58
54
|
</div>
|
|
59
55
|
|
|
60
56
|
{isCore ? (
|
|
61
|
-
<
|
|
62
|
-
className="shrink-0 w-9 h-5 rounded-full bg-amber-600 relative cursor-not-allowed opacity-60"
|
|
63
|
-
title={t.settings.plugins.coreHint ?? 'Core renderer — always enabled'}
|
|
64
|
-
>
|
|
65
|
-
<span className="absolute top-[3px] left-[18px] w-3.5 h-3.5 rounded-full shadow-sm bg-white" />
|
|
66
|
-
</span>
|
|
57
|
+
<Toggle checked={true} disabled />
|
|
67
58
|
) : (
|
|
68
|
-
<
|
|
69
|
-
onClick={() => {
|
|
70
|
-
const next = !enabled;
|
|
71
|
-
setRendererEnabled(renderer.id, next);
|
|
72
|
-
setPluginStates(s => ({ ...s, [renderer.id]: next }));
|
|
73
|
-
}}
|
|
74
|
-
role="switch"
|
|
75
|
-
aria-checked={enabled}
|
|
76
|
-
className={`shrink-0 w-9 h-5 rounded-full transition-colors relative ${enabled ? 'bg-amber-600' : 'bg-muted border border-border'}`}
|
|
77
|
-
title={enabled ? t.settings.plugins.enabled : t.settings.plugins.disabled}
|
|
78
|
-
>
|
|
79
|
-
<span
|
|
80
|
-
className={`absolute top-[3px] w-3.5 h-3.5 rounded-full shadow-sm transition-all ${enabled ? 'left-[18px] bg-white' : 'left-[3px] bg-muted-foreground/50'}`}
|
|
81
|
-
/>
|
|
82
|
-
</button>
|
|
59
|
+
<Toggle checked={enabled} onChange={(next) => { setRendererEnabled(renderer.id, next); setPluginStates(s => ({ ...s, [renderer.id]: next })); }} title={enabled ? t.settings.plugins.enabled : t.settings.plugins.disabled} />
|
|
83
60
|
)}
|
|
84
61
|
</div>
|
|
85
62
|
</div>
|
|
@@ -39,6 +39,50 @@ export function EnvBadge({ overridden }: { overridden: boolean }) {
|
|
|
39
39
|
);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* 🟢 MINOR #6: Toggle component with aria accessibility
|
|
44
|
+
* @param {boolean} checked - Toggle state
|
|
45
|
+
* @param {function} onChange - Called when toggle state changes (if no onClick provided)
|
|
46
|
+
* @param {string} size - 'sm' (h-4 w-7) or 'md' (h-5 w-9)
|
|
47
|
+
* @param {boolean} disabled - Disable toggle
|
|
48
|
+
* @param {string} title - Tooltip text
|
|
49
|
+
* @param {function} onClick - Custom click handler (takes priority over onChange). Call onChange directly if needed.
|
|
50
|
+
*
|
|
51
|
+
* Usage:
|
|
52
|
+
* - Basic: `<Toggle checked={x} onChange={setX} />`
|
|
53
|
+
* - With custom handler: `<Toggle checked={x} onClick={(e) => { e.stopPropagation(); await save(); }} />`
|
|
54
|
+
* - In lists: Use `onClick` to prevent event bubbling; manually call `onChange` for state sync
|
|
55
|
+
*/
|
|
56
|
+
export function Toggle({ checked, onChange, size = 'md', disabled, title, onClick }: {
|
|
57
|
+
checked: boolean;
|
|
58
|
+
onChange?: (checked: boolean) => void;
|
|
59
|
+
size?: 'sm' | 'md';
|
|
60
|
+
disabled?: boolean;
|
|
61
|
+
title?: string;
|
|
62
|
+
onClick?: (e: React.MouseEvent) => void;
|
|
63
|
+
}) {
|
|
64
|
+
const sm = size === 'sm';
|
|
65
|
+
return (
|
|
66
|
+
<button
|
|
67
|
+
type="button"
|
|
68
|
+
role="switch"
|
|
69
|
+
aria-checked={checked}
|
|
70
|
+
disabled={disabled}
|
|
71
|
+
title={title}
|
|
72
|
+
onClick={onClick ?? (() => onChange?.(!checked))}
|
|
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
|
+
sm ? 'h-4 w-7' : 'h-5 w-9'
|
|
75
|
+
} ${checked ? 'bg-amber-500' : 'bg-muted'}`}
|
|
76
|
+
>
|
|
77
|
+
<span
|
|
78
|
+
className={`pointer-events-none inline-block rounded-full bg-white shadow-sm transition-transform ${
|
|
79
|
+
sm ? 'h-3 w-3' : 'h-4 w-4'
|
|
80
|
+
} ${checked ? (sm ? 'translate-x-3' : 'translate-x-4') : 'translate-x-0'}`}
|
|
81
|
+
/>
|
|
82
|
+
</button>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
42
86
|
export function ApiKeyInput({ value, onChange, placeholder, disabled }: {
|
|
43
87
|
value: string;
|
|
44
88
|
onChange: (v: string) => void;
|
|
@@ -58,3 +102,28 @@ export function ApiKeyInput({ value, onChange, placeholder, disabled }: {
|
|
|
58
102
|
/>
|
|
59
103
|
);
|
|
60
104
|
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 💡 SUGGESTION #10: Unified primary button primitive for amber actions
|
|
108
|
+
* Replaces inline `style={{ background: 'var(--amber)', color: 'var(--amber-foreground)' }}` pattern
|
|
109
|
+
*/
|
|
110
|
+
export function PrimaryButton({ children, disabled, onClick, type = 'button', className = '', ...props }: {
|
|
111
|
+
children: React.ReactNode;
|
|
112
|
+
disabled?: boolean;
|
|
113
|
+
onClick?: () => void;
|
|
114
|
+
type?: 'button' | 'submit';
|
|
115
|
+
className?: string;
|
|
116
|
+
} & React.ButtonHTMLAttributes<HTMLButtonElement>) {
|
|
117
|
+
return (
|
|
118
|
+
<button
|
|
119
|
+
type={type}
|
|
120
|
+
onClick={onClick}
|
|
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)' }}
|
|
124
|
+
{...props}
|
|
125
|
+
>
|
|
126
|
+
{children}
|
|
127
|
+
</button>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
t: any;
|
|
5
|
-
}
|
|
3
|
+
import type { ShortcutsTabProps } from './types';
|
|
6
4
|
|
|
7
5
|
export function ShortcutsTab({ t }: ShortcutsTabProps) {
|
|
8
6
|
return (
|
|
9
7
|
<div className="space-y-1">
|
|
10
|
-
{t.shortcuts.map((s: { description: string; keys: string[] }, i: number) => (
|
|
8
|
+
{t.shortcuts.map((s: { readonly description: string; readonly keys: readonly string[] }, i: number) => (
|
|
11
9
|
<div key={i} className="flex items-center justify-between py-2.5 border-b border-border last:border-0">
|
|
12
10
|
<span className="text-sm text-foreground">{s.description}</span>
|
|
13
11
|
<div className="flex items-center gap-1">
|
|
@@ -2,26 +2,12 @@
|
|
|
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 } from './Primitives';
|
|
5
|
+
import { SectionLabel, PrimaryButton } from './Primitives';
|
|
6
6
|
import { apiFetch } from '@/lib/api';
|
|
7
|
+
import type { SyncStatus, SyncTabProps } from './types';
|
|
8
|
+
import type { Messages } from '@/lib/i18n';
|
|
7
9
|
|
|
8
|
-
export
|
|
9
|
-
enabled: boolean;
|
|
10
|
-
provider?: string;
|
|
11
|
-
remote?: string;
|
|
12
|
-
branch?: string;
|
|
13
|
-
lastSync?: string | null;
|
|
14
|
-
lastPull?: string | null;
|
|
15
|
-
unpushed?: string;
|
|
16
|
-
conflicts?: Array<{ file: string; time: string }>;
|
|
17
|
-
lastError?: string | null;
|
|
18
|
-
autoCommitInterval?: number;
|
|
19
|
-
autoPullInterval?: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface SyncTabProps {
|
|
23
|
-
t: any;
|
|
24
|
-
}
|
|
10
|
+
export { SyncStatus }; // Re-export for backward compatibility
|
|
25
11
|
|
|
26
12
|
export function timeAgo(iso: string | null | undefined): string {
|
|
27
13
|
if (!iso) return 'never';
|
|
@@ -40,7 +26,7 @@ function isValidGitUrl(url: string): 'https' | 'ssh' | false {
|
|
|
40
26
|
return false;
|
|
41
27
|
}
|
|
42
28
|
|
|
43
|
-
function SyncEmptyState({ t, onInitComplete }: { t:
|
|
29
|
+
function SyncEmptyState({ t, onInitComplete }: { t: Messages; onInitComplete: () => void }) {
|
|
44
30
|
const syncT = t.settings?.sync;
|
|
45
31
|
|
|
46
32
|
const [remoteUrl, setRemoteUrl] = useState('');
|
|
@@ -169,18 +155,16 @@ function SyncEmptyState({ t, onInitComplete }: { t: any; onInitComplete: () => v
|
|
|
169
155
|
</div>
|
|
170
156
|
|
|
171
157
|
{/* Connect button */}
|
|
172
|
-
<
|
|
173
|
-
type="button"
|
|
158
|
+
<PrimaryButton
|
|
174
159
|
onClick={handleConnect}
|
|
175
160
|
disabled={!isValid || connecting}
|
|
176
|
-
className="flex items-center gap-2
|
|
177
|
-
style={{ background: 'var(--amber)', color: 'var(--amber-foreground)' }}
|
|
161
|
+
className="flex items-center gap-2"
|
|
178
162
|
>
|
|
179
163
|
{connecting && <Loader2 size={14} className="animate-spin" />}
|
|
180
164
|
{connecting
|
|
181
165
|
? (syncT?.connecting ?? 'Connecting...')
|
|
182
166
|
: (syncT?.connectButton ?? 'Connect & Start Sync')}
|
|
183
|
-
</
|
|
167
|
+
</PrimaryButton>
|
|
184
168
|
|
|
185
169
|
{/* Error */}
|
|
186
170
|
{error && (
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Locale } from '@/lib/i18n';
|
|
1
|
+
import type { Locale, Messages } from '@/lib/i18n';
|
|
2
|
+
import type React from 'react';
|
|
2
3
|
|
|
3
4
|
export interface ProviderConfig {
|
|
4
5
|
apiKey: string;
|
|
@@ -32,7 +33,7 @@ export interface SettingsData {
|
|
|
32
33
|
envValues?: Record<string, string>;
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
export type Tab = 'ai' | 'appearance' | 'knowledge' | 'mcp' | 'plugins' | '
|
|
36
|
+
export type Tab = 'ai' | 'appearance' | 'knowledge' | 'mcp' | 'plugins' | 'sync';
|
|
36
37
|
|
|
37
38
|
export const CONTENT_WIDTHS = [
|
|
38
39
|
{ value: '680px', label: 'Narrow (680px)' },
|
|
@@ -47,3 +48,116 @@ export const FONTS = [
|
|
|
47
48
|
{ value: 'geist', label: 'Geist', style: { fontFamily: 'var(--font-geist-sans), sans-serif' } },
|
|
48
49
|
{ value: 'ibm-plex-mono', label: 'IBM Plex Mono (mono)', style: { fontFamily: "'IBM Plex Mono', monospace" } },
|
|
49
50
|
];
|
|
51
|
+
|
|
52
|
+
/* ── MCP Types ────────────────────────────────────────────────── */
|
|
53
|
+
|
|
54
|
+
export interface McpStatus {
|
|
55
|
+
running: boolean;
|
|
56
|
+
transport: string;
|
|
57
|
+
endpoint: string;
|
|
58
|
+
port: number;
|
|
59
|
+
toolCount: number;
|
|
60
|
+
authConfigured: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface AgentInfo {
|
|
64
|
+
key: string;
|
|
65
|
+
name: string;
|
|
66
|
+
present: boolean;
|
|
67
|
+
installed: boolean;
|
|
68
|
+
scope?: string;
|
|
69
|
+
transport?: string;
|
|
70
|
+
configPath?: string;
|
|
71
|
+
hasProjectScope: boolean;
|
|
72
|
+
hasGlobalScope: boolean;
|
|
73
|
+
preferredTransport: 'stdio' | 'http';
|
|
74
|
+
// Snippet generation fields
|
|
75
|
+
format: 'json' | 'toml';
|
|
76
|
+
configKey: string;
|
|
77
|
+
globalNestedKey?: string;
|
|
78
|
+
globalPath: string;
|
|
79
|
+
projectPath?: string | null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface SkillInfo {
|
|
83
|
+
name: string;
|
|
84
|
+
description: string;
|
|
85
|
+
path: string;
|
|
86
|
+
source: 'builtin' | 'user';
|
|
87
|
+
enabled: boolean;
|
|
88
|
+
editable: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** 🟢 MINOR #7: Moved from SyncTab.tsx for consistency */
|
|
92
|
+
export interface SyncStatus {
|
|
93
|
+
enabled: boolean;
|
|
94
|
+
provider?: string;
|
|
95
|
+
remote?: string;
|
|
96
|
+
branch?: string;
|
|
97
|
+
lastSync?: string | null;
|
|
98
|
+
lastPull?: string | null;
|
|
99
|
+
unpushed?: string;
|
|
100
|
+
conflicts?: Array<{ file: string; time: string }>;
|
|
101
|
+
lastError?: string | null;
|
|
102
|
+
autoCommitInterval?: number;
|
|
103
|
+
autoPullInterval?: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface McpTabProps {
|
|
107
|
+
t: Messages;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface AppearanceTabProps {
|
|
111
|
+
font: string;
|
|
112
|
+
setFont: (v: string) => void;
|
|
113
|
+
contentWidth: string;
|
|
114
|
+
setContentWidth: (v: string) => void;
|
|
115
|
+
dark: boolean;
|
|
116
|
+
setDark: (v: boolean) => void;
|
|
117
|
+
locale: Locale;
|
|
118
|
+
setLocale: (v: Locale) => void;
|
|
119
|
+
t: Messages;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface AiTabProps {
|
|
123
|
+
data: SettingsData;
|
|
124
|
+
updateAi: (patch: Partial<AiSettings>) => void;
|
|
125
|
+
updateAgent: (patch: Partial<AgentSettings>) => void;
|
|
126
|
+
t: Messages;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface KnowledgeTabProps {
|
|
130
|
+
data: SettingsData;
|
|
131
|
+
setData: React.Dispatch<React.SetStateAction<SettingsData | null>>;
|
|
132
|
+
t: Messages;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface PluginsTabProps {
|
|
136
|
+
pluginStates: Record<string, boolean>;
|
|
137
|
+
setPluginStates: React.Dispatch<React.SetStateAction<Record<string, boolean>>>;
|
|
138
|
+
t: Messages;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface SyncTabProps {
|
|
142
|
+
t: Messages;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface McpServerStatusProps {
|
|
146
|
+
status: McpStatus | null;
|
|
147
|
+
agents: AgentInfo[];
|
|
148
|
+
t: Messages;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface McpAgentInstallProps {
|
|
152
|
+
agents: AgentInfo[];
|
|
153
|
+
t: Messages;
|
|
154
|
+
onRefresh: () => void;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface McpSkillsSectionProps {
|
|
158
|
+
t: Messages;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export interface ShortcutsTabProps {
|
|
162
|
+
t: Messages;
|
|
163
|
+
}
|