@geminilight/mindos 0.6.30 → 0.6.31
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_zh.md +10 -4
- package/app/app/api/ask/route.ts +12 -7
- package/app/app/api/export/route.ts +105 -0
- package/app/app/globals.css +2 -2
- package/app/app/trash/page.tsx +7 -0
- package/app/app/view/[...path]/ViewPageClient.tsx +234 -2
- package/app/components/ExportModal.tsx +220 -0
- package/app/components/FileTree.tsx +22 -2
- package/app/components/HomeContent.tsx +91 -20
- package/app/components/MarkdownView.tsx +45 -10
- package/app/components/Sidebar.tsx +10 -1
- package/app/components/TrashPageClient.tsx +263 -0
- package/app/components/ask/ToolCallBlock.tsx +102 -18
- package/app/components/changes/ChangesContentPage.tsx +58 -14
- package/app/components/explore/ExploreContent.tsx +4 -7
- package/app/components/explore/UseCaseCard.tsx +18 -1
- package/app/components/explore/use-cases.generated.ts +76 -0
- package/app/components/explore/use-cases.yaml +185 -0
- package/app/components/panels/DiscoverPanel.tsx +1 -1
- package/app/components/renderers/workflow-yaml/StepEditor.tsx +98 -91
- package/app/components/renderers/workflow-yaml/WorkflowEditor.tsx +82 -72
- package/app/components/renderers/workflow-yaml/WorkflowRunner.tsx +163 -120
- package/app/components/renderers/workflow-yaml/WorkflowYamlRenderer.tsx +61 -61
- package/app/components/renderers/workflow-yaml/execution.ts +64 -12
- package/app/components/renderers/workflow-yaml/selectors.tsx +64 -12
- package/app/components/settings/AiTab.tsx +191 -174
- package/app/components/settings/AppearanceTab.tsx +168 -77
- package/app/components/settings/KnowledgeTab.tsx +131 -136
- package/app/components/settings/McpTab.tsx +11 -11
- package/app/components/settings/Primitives.tsx +60 -0
- package/app/components/settings/SettingsContent.tsx +15 -8
- package/app/components/settings/SyncTab.tsx +12 -12
- package/app/components/settings/UninstallTab.tsx +8 -18
- package/app/components/settings/UpdateTab.tsx +82 -82
- package/app/components/settings/types.ts +17 -8
- package/app/lib/acp/session.ts +12 -3
- package/app/lib/actions.ts +57 -3
- package/app/lib/agent/stream-consumer.ts +18 -0
- package/app/lib/agent/tools.ts +56 -9
- package/app/lib/core/export.ts +116 -0
- package/app/lib/core/trash.ts +241 -0
- package/app/lib/fs.ts +47 -0
- package/app/lib/hooks/usePinnedFiles.ts +90 -0
- package/app/lib/i18n/generated/explore-i18n.generated.ts +138 -0
- package/app/lib/i18n/index.ts +3 -0
- package/app/lib/i18n/modules/knowledge.ts +120 -6
- package/app/lib/i18n/modules/onboarding.ts +2 -134
- package/app/lib/i18n/modules/settings.ts +12 -0
- package/app/package.json +8 -2
- package/app/scripts/generate-explore.ts +145 -0
- package/package.json +1 -1
- package/app/components/explore/use-cases.ts +0 -58
|
@@ -135,7 +135,7 @@ function McpStatusCard({ status, restarting, onRestart, onRefresh, m }: {
|
|
|
135
135
|
if (!status) return null;
|
|
136
136
|
return (
|
|
137
137
|
<div className="rounded-xl border border-border bg-card p-4 flex items-center justify-between">
|
|
138
|
-
<div className="flex items-center gap-2.5 text-
|
|
138
|
+
<div className="flex items-center gap-2.5 text-sm">
|
|
139
139
|
{restarting ? (
|
|
140
140
|
<>
|
|
141
141
|
<Loader2 size={12} className="animate-spin text-[var(--amber)]" />
|
|
@@ -161,13 +161,13 @@ function McpStatusCard({ status, restarting, onRestart, onRefresh, m }: {
|
|
|
161
161
|
<div className="flex items-center gap-2">
|
|
162
162
|
{!status.running && !restarting && (
|
|
163
163
|
<button onClick={onRestart}
|
|
164
|
-
className="flex items-center gap-1.5 px-
|
|
165
|
-
<RotateCcw size={
|
|
164
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors">
|
|
165
|
+
<RotateCcw size={14} /> {m?.restart ?? 'Restart'}
|
|
166
166
|
</button>
|
|
167
167
|
)}
|
|
168
168
|
<button onClick={onRefresh}
|
|
169
169
|
className="p-1.5 rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors">
|
|
170
|
-
<RefreshCw size={
|
|
170
|
+
<RefreshCw size={14} />
|
|
171
171
|
</button>
|
|
172
172
|
</div>
|
|
173
173
|
</div>
|
|
@@ -257,19 +257,19 @@ function AgentConfigViewer({ connectedAgents, detectedAgents, notFoundAgents, cu
|
|
|
257
257
|
<div className="flex items-center rounded-lg border border-border overflow-hidden w-fit">
|
|
258
258
|
<button
|
|
259
259
|
onClick={() => onTransportChange('stdio')}
|
|
260
|
-
className={`flex items-center gap-1.5 px-3 py-1.5 text-
|
|
260
|
+
className={`flex items-center gap-1.5 px-3 py-1.5 text-sm transition-colors ${
|
|
261
261
|
transport === 'stdio' ? 'bg-muted text-foreground font-medium' : 'text-muted-foreground hover:text-foreground'
|
|
262
262
|
}`}
|
|
263
263
|
>
|
|
264
|
-
<Monitor size={
|
|
264
|
+
<Monitor size={14} /> {m?.transportLocal ?? 'Local (stdio)'}
|
|
265
265
|
</button>
|
|
266
266
|
<button
|
|
267
267
|
onClick={() => onTransportChange('http')}
|
|
268
|
-
className={`flex items-center gap-1.5 px-3 py-1.5 text-
|
|
268
|
+
className={`flex items-center gap-1.5 px-3 py-1.5 text-sm transition-colors ${
|
|
269
269
|
transport === 'http' ? 'bg-muted text-foreground font-medium' : 'text-muted-foreground hover:text-foreground'
|
|
270
270
|
}`}
|
|
271
271
|
>
|
|
272
|
-
<Globe size={
|
|
272
|
+
<Globe size={14} /> {m?.transportRemote ?? 'Remote (HTTP)'}
|
|
273
273
|
</button>
|
|
274
274
|
</div>
|
|
275
275
|
|
|
@@ -287,10 +287,10 @@ function AgentConfigViewer({ connectedAgents, detectedAgents, notFoundAgents, cu
|
|
|
287
287
|
<pre className="text-[11px] font-mono bg-muted/50 border border-border rounded-lg p-3 overflow-x-auto whitespace-pre select-all max-h-[240px] overflow-y-auto">
|
|
288
288
|
{snippet.displaySnippet}
|
|
289
289
|
</pre>
|
|
290
|
-
<div className="flex items-center gap-3 text-
|
|
290
|
+
<div className="flex items-center gap-3 text-sm">
|
|
291
291
|
<button onClick={() => onCopy(snippet.snippet)}
|
|
292
|
-
className="inline-flex items-center gap-1.5 px-
|
|
293
|
-
<Copy size={
|
|
292
|
+
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors shrink-0">
|
|
293
|
+
<Copy size={14} />
|
|
294
294
|
{m?.copyConfig ?? 'Copy config'}
|
|
295
295
|
</button>
|
|
296
296
|
<span className="text-muted-foreground">→</span>
|
|
@@ -238,3 +238,63 @@ export function PrimaryButton({ children, disabled, onClick, type = 'button', cl
|
|
|
238
238
|
</button>
|
|
239
239
|
);
|
|
240
240
|
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* SettingCard — groups related settings into a visually distinct card.
|
|
244
|
+
* Provides the "breathing room" and grouping that raw Field stacks lack.
|
|
245
|
+
*
|
|
246
|
+
* Usage:
|
|
247
|
+
* <SettingCard icon={<Sparkles size={15} />} title="AI Provider" description="Choose your model">
|
|
248
|
+
* <Field label="Model"> ... </Field>
|
|
249
|
+
* </SettingCard>
|
|
250
|
+
*/
|
|
251
|
+
export function SettingCard({ icon, title, description, badge, children, className = '' }: {
|
|
252
|
+
icon: React.ReactNode;
|
|
253
|
+
title: string;
|
|
254
|
+
description?: string;
|
|
255
|
+
badge?: React.ReactNode;
|
|
256
|
+
children: React.ReactNode;
|
|
257
|
+
className?: string;
|
|
258
|
+
}) {
|
|
259
|
+
return (
|
|
260
|
+
<div className={`rounded-xl border border-border/50 bg-card/50 p-5 ${className}`}>
|
|
261
|
+
<div className="flex items-start gap-3 mb-4">
|
|
262
|
+
<div className="w-8 h-8 rounded-lg bg-muted/50 flex items-center justify-center shrink-0 mt-0.5">
|
|
263
|
+
<span className="text-muted-foreground">{icon}</span>
|
|
264
|
+
</div>
|
|
265
|
+
<div className="flex-1 min-w-0">
|
|
266
|
+
<div className="flex items-center gap-2">
|
|
267
|
+
<h3 className="text-sm font-semibold text-foreground">{title}</h3>
|
|
268
|
+
{badge}
|
|
269
|
+
</div>
|
|
270
|
+
{description && (
|
|
271
|
+
<p className="text-xs text-muted-foreground mt-0.5 leading-relaxed">{description}</p>
|
|
272
|
+
)}
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
<div className="space-y-4 pl-11">
|
|
276
|
+
{children}
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* SettingRow — inline label + control on one line.
|
|
284
|
+
* Replaces verbose Field + vertical stacking for simple toggle/select rows.
|
|
285
|
+
*/
|
|
286
|
+
export function SettingRow({ label, hint, children }: {
|
|
287
|
+
label: string;
|
|
288
|
+
hint?: string;
|
|
289
|
+
children: React.ReactNode;
|
|
290
|
+
}) {
|
|
291
|
+
return (
|
|
292
|
+
<div className="flex items-center justify-between gap-4">
|
|
293
|
+
<div className="flex-1 min-w-0">
|
|
294
|
+
<div className="text-sm text-foreground">{label}</div>
|
|
295
|
+
{hint && <div className="text-xs text-muted-foreground mt-0.5">{hint}</div>}
|
|
296
|
+
</div>
|
|
297
|
+
<div className="shrink-0">{children}</div>
|
|
298
|
+
</div>
|
|
299
|
+
);
|
|
300
|
+
}
|
|
@@ -30,6 +30,7 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
|
|
|
30
30
|
const dataLoaded = useRef(false);
|
|
31
31
|
|
|
32
32
|
const [font, setFont] = useState('lora');
|
|
33
|
+
const [fontSize, setFontSize] = useState('15px');
|
|
33
34
|
const [contentWidth, setContentWidth] = useState('780px');
|
|
34
35
|
const [dark, setDark] = useState(true);
|
|
35
36
|
|
|
@@ -63,6 +64,7 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
|
|
|
63
64
|
if (justOpened) {
|
|
64
65
|
apiFetch<SettingsData>('/api/settings').then(d => { setData(d); dataLoaded.current = true; }).catch(() => setStatus('load-error'));
|
|
65
66
|
setFont(localStorage.getItem('prose-font') ?? 'lora');
|
|
67
|
+
setFontSize(localStorage.getItem('prose-font-size') ?? '15px');
|
|
66
68
|
setContentWidth(localStorage.getItem('content-width') ?? '780px');
|
|
67
69
|
const stored = localStorage.getItem('theme');
|
|
68
70
|
setDark(stored ? stored === 'dark' : window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
@@ -87,6 +89,11 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
|
|
|
87
89
|
localStorage.setItem('prose-font', font);
|
|
88
90
|
}, [font]);
|
|
89
91
|
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
document.documentElement.style.setProperty('--prose-font-size-override', fontSize);
|
|
94
|
+
localStorage.setItem('prose-font-size', fontSize);
|
|
95
|
+
}, [fontSize]);
|
|
96
|
+
|
|
90
97
|
useEffect(() => {
|
|
91
98
|
document.documentElement.style.setProperty('--content-width-override', contentWidth);
|
|
92
99
|
localStorage.setItem('content-width', contentWidth);
|
|
@@ -178,7 +185,7 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
|
|
|
178
185
|
) : (
|
|
179
186
|
<>
|
|
180
187
|
{tab === 'ai' && data?.ai && <AiTab data={data} updateAi={updateAi} updateAgent={updateAgent} t={t} />}
|
|
181
|
-
{tab === 'appearance' && <AppearanceTab font={font} setFont={setFont} contentWidth={contentWidth} setContentWidth={setContentWidth} dark={dark} setDark={setDark} locale={locale} setLocale={setLocale} t={t} />}
|
|
188
|
+
{tab === 'appearance' && <AppearanceTab font={font} setFont={setFont} fontSize={fontSize} setFontSize={setFontSize} contentWidth={contentWidth} setContentWidth={setContentWidth} dark={dark} setDark={setDark} locale={locale} setLocale={setLocale} t={t} />}
|
|
182
189
|
{tab === 'knowledge' && data && <KnowledgeTab data={data} setData={setData} t={t} />}
|
|
183
190
|
{tab === 'sync' && <SyncTab t={t} />}
|
|
184
191
|
{tab === 'mcp' && <McpTab t={t} />}
|
|
@@ -197,18 +204,18 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
|
|
|
197
204
|
<button
|
|
198
205
|
onClick={restoreFromEnv}
|
|
199
206
|
disabled={saving || !data}
|
|
200
|
-
className={`flex items-center gap-1.5 ${isPanel ? 'px-2.5 py-1 text-
|
|
207
|
+
className={`flex items-center gap-1.5 ${isPanel ? 'px-2.5 py-1 text-xs rounded-md' : 'px-3 py-1.5 text-sm rounded-lg'} border border-border text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors`}
|
|
201
208
|
>
|
|
202
|
-
<RotateCcw size={isPanel ?
|
|
209
|
+
<RotateCcw size={isPanel ? 12 : 13} />
|
|
203
210
|
{t.settings.ai.restoreFromEnv}
|
|
204
211
|
</button>
|
|
205
212
|
)}
|
|
206
213
|
{tab === 'knowledge' && (
|
|
207
214
|
<a
|
|
208
215
|
href="/setup?force=1"
|
|
209
|
-
className={`flex items-center gap-1.5 ${isPanel ? 'px-2.5 py-1 text-
|
|
216
|
+
className={`flex items-center gap-1.5 ${isPanel ? 'px-2.5 py-1 text-xs rounded-md' : 'px-3 py-1.5 text-sm rounded-lg'} border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors`}
|
|
210
217
|
>
|
|
211
|
-
<RotateCcw size={isPanel ?
|
|
218
|
+
<RotateCcw size={isPanel ? 12 : 13} />
|
|
212
219
|
{t.settings.reconfigure}
|
|
213
220
|
</a>
|
|
214
221
|
)}
|
|
@@ -246,7 +253,7 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
|
|
|
246
253
|
<button
|
|
247
254
|
key={tabItem.id}
|
|
248
255
|
onClick={() => setTab(tabItem.id)}
|
|
249
|
-
className={`flex items-center gap-1 px-2 py-2 text-
|
|
256
|
+
className={`flex items-center gap-1 px-2 py-2 text-xs font-medium transition-colors border-b-2 -mb-px whitespace-nowrap ${
|
|
250
257
|
tab === tabItem.id
|
|
251
258
|
? 'border-[var(--amber)] text-foreground'
|
|
252
259
|
: 'border-transparent text-muted-foreground hover:text-foreground'
|
|
@@ -297,7 +304,7 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
|
|
|
297
304
|
<button
|
|
298
305
|
key={tabItem.id}
|
|
299
306
|
onClick={() => setTab(tabItem.id)}
|
|
300
|
-
className={`flex items-center gap-1.5 px-3 py-2.5 text-
|
|
307
|
+
className={`flex items-center gap-1.5 px-3 py-2.5 text-sm font-medium transition-colors border-b-2 -mb-px whitespace-nowrap ${
|
|
301
308
|
tab === tabItem.id
|
|
302
309
|
? 'border-[var(--amber)] text-foreground'
|
|
303
310
|
: 'border-transparent text-muted-foreground hover:text-foreground'
|
|
@@ -326,7 +333,7 @@ export default function SettingsContent({ visible, initialTab, variant, onClose
|
|
|
326
333
|
<button
|
|
327
334
|
key={tabItem.id}
|
|
328
335
|
onClick={() => setTab(tabItem.id)}
|
|
329
|
-
className={`flex items-center gap-2 w-full px-4 py-2 text-
|
|
336
|
+
className={`flex items-center gap-2 w-full px-4 py-2 text-sm font-medium transition-colors relative ${
|
|
330
337
|
tab === tabItem.id
|
|
331
338
|
? 'text-foreground bg-muted'
|
|
332
339
|
: 'text-muted-foreground hover:text-foreground hover:bg-muted/50'
|
|
@@ -82,7 +82,7 @@ function SyncEmptyState({ t, onInitComplete }: { t: Messages; onInitComplete: ()
|
|
|
82
82
|
|
|
83
83
|
{/* Git Remote URL */}
|
|
84
84
|
<div className="space-y-1.5">
|
|
85
|
-
<label className="text-
|
|
85
|
+
<label className="text-sm font-medium text-foreground block">
|
|
86
86
|
{syncT?.remoteUrl ?? 'Git Remote URL'}
|
|
87
87
|
</label>
|
|
88
88
|
<Input
|
|
@@ -108,7 +108,7 @@ function SyncEmptyState({ t, onInitComplete }: { t: Messages; onInitComplete: ()
|
|
|
108
108
|
{/* Access Token (HTTPS only) */}
|
|
109
109
|
{showTokenField && (
|
|
110
110
|
<div className="space-y-1.5">
|
|
111
|
-
<label className="text-
|
|
111
|
+
<label className="text-sm font-medium text-foreground block">
|
|
112
112
|
{syncT?.accessToken ?? 'Access Token'}{' '}
|
|
113
113
|
<span className="text-muted-foreground font-normal">{syncT?.optional ?? '(optional, for private repos)'}</span>
|
|
114
114
|
</label>
|
|
@@ -136,7 +136,7 @@ function SyncEmptyState({ t, onInitComplete }: { t: Messages; onInitComplete: ()
|
|
|
136
136
|
|
|
137
137
|
{/* Branch */}
|
|
138
138
|
<div className="space-y-1.5">
|
|
139
|
-
<label className="text-
|
|
139
|
+
<label className="text-sm font-medium text-foreground block">
|
|
140
140
|
{syncT?.branchLabel ?? 'Branch'}
|
|
141
141
|
</label>
|
|
142
142
|
<Input
|
|
@@ -267,30 +267,30 @@ export function SyncTab({ t }: SyncTabProps) {
|
|
|
267
267
|
<SectionLabel>Sync</SectionLabel>
|
|
268
268
|
|
|
269
269
|
{/* Status overview */}
|
|
270
|
-
<div className="space-y-2 text-sm">
|
|
270
|
+
<div className="space-y-2.5 text-sm">
|
|
271
271
|
<div className="flex items-center gap-2">
|
|
272
272
|
<span className="text-muted-foreground w-24 shrink-0">Provider</span>
|
|
273
|
-
<span className="font-mono text-
|
|
273
|
+
<span className="font-mono text-sm">{status.provider}</span>
|
|
274
274
|
</div>
|
|
275
275
|
<div className="flex items-center gap-2">
|
|
276
276
|
<span className="text-muted-foreground w-24 shrink-0">Remote</span>
|
|
277
|
-
<span className="font-mono text-
|
|
277
|
+
<span className="font-mono text-sm truncate" title={status.remote}>{status.remote}</span>
|
|
278
278
|
</div>
|
|
279
279
|
<div className="flex items-center gap-2">
|
|
280
280
|
<span className="text-muted-foreground w-24 shrink-0">Branch</span>
|
|
281
|
-
<span className="font-mono text-
|
|
281
|
+
<span className="font-mono text-sm">{status.branch}</span>
|
|
282
282
|
</div>
|
|
283
283
|
<div className="flex items-center gap-2">
|
|
284
284
|
<span className="text-muted-foreground w-24 shrink-0">Last sync</span>
|
|
285
|
-
<span className="text-
|
|
285
|
+
<span className="text-sm">{timeAgo(status.lastSync)}</span>
|
|
286
286
|
</div>
|
|
287
287
|
<div className="flex items-center gap-2">
|
|
288
288
|
<span className="text-muted-foreground w-24 shrink-0">Unpushed</span>
|
|
289
|
-
<span className="text-
|
|
289
|
+
<span className="text-sm">{status.unpushed} commits</span>
|
|
290
290
|
</div>
|
|
291
291
|
<div className="flex items-center gap-2">
|
|
292
292
|
<span className="text-muted-foreground w-24 shrink-0">Auto-sync</span>
|
|
293
|
-
<span className="text-
|
|
293
|
+
<span className="text-sm">
|
|
294
294
|
commit: {status.autoCommitInterval}s, pull: {Math.floor((status.autoPullInterval || 300) / 60)}min
|
|
295
295
|
</span>
|
|
296
296
|
</div>
|
|
@@ -303,7 +303,7 @@ export function SyncTab({ t }: SyncTabProps) {
|
|
|
303
303
|
onClick={handleSyncNow}
|
|
304
304
|
disabled={syncing}
|
|
305
305
|
title={syncing ? t.hints.syncInProgress : undefined}
|
|
306
|
-
className="flex items-center gap-1.5 px-3 py-1.5 text-
|
|
306
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
|
|
307
307
|
>
|
|
308
308
|
<RefreshCw size={12} className={syncing ? 'animate-spin' : ''} />
|
|
309
309
|
Sync Now
|
|
@@ -313,7 +313,7 @@ export function SyncTab({ t }: SyncTabProps) {
|
|
|
313
313
|
onClick={handleToggle}
|
|
314
314
|
disabled={toggling}
|
|
315
315
|
title={toggling ? t.hints.toggleInProgress : undefined}
|
|
316
|
-
className={`flex items-center gap-1.5 px-3 py-1.5 text-
|
|
316
|
+
className={`flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg border transition-colors disabled:opacity-40 disabled:cursor-not-allowed ${
|
|
317
317
|
status.enabled
|
|
318
318
|
? 'border-border text-muted-foreground hover:text-destructive hover:border-destructive/50'
|
|
319
319
|
: 'border-success/30 text-success hover:bg-success/10'
|
|
@@ -4,6 +4,7 @@ import { useState } from 'react';
|
|
|
4
4
|
import { Trash2, AlertTriangle, CheckCircle2, Loader2, ShieldCheck } from 'lucide-react';
|
|
5
5
|
import { apiFetch } from '@/lib/api';
|
|
6
6
|
import { useLocale } from '@/lib/LocaleContext';
|
|
7
|
+
import { SettingCard } from './Primitives';
|
|
7
8
|
|
|
8
9
|
type Phase = 'idle' | 'confirming' | 'running' | 'success' | 'error';
|
|
9
10
|
|
|
@@ -73,25 +74,14 @@ export function UninstallTab() {
|
|
|
73
74
|
className="mt-0.5 form-check"
|
|
74
75
|
/>
|
|
75
76
|
<div>
|
|
76
|
-
<p className="text-
|
|
77
|
-
<p className="text-
|
|
77
|
+
<p className="text-sm font-medium text-foreground">{label}</p>
|
|
78
|
+
<p className="text-xs text-muted-foreground">{desc}</p>
|
|
78
79
|
</div>
|
|
79
80
|
</label>
|
|
80
81
|
);
|
|
81
82
|
|
|
82
83
|
return (
|
|
83
|
-
<
|
|
84
|
-
{/* Header */}
|
|
85
|
-
<div>
|
|
86
|
-
<h3 className="text-sm font-medium text-foreground flex items-center gap-2">
|
|
87
|
-
<Trash2 size={14} className="text-muted-foreground" />
|
|
88
|
-
{u.title}
|
|
89
|
-
</h3>
|
|
90
|
-
<p className="text-xs text-muted-foreground mt-1">
|
|
91
|
-
{isDesktop ? u.descDesktop : u.descCli}
|
|
92
|
-
</p>
|
|
93
|
-
</div>
|
|
94
|
-
|
|
84
|
+
<SettingCard icon={<Trash2 size={15} />} title={u.title} description={isDesktop ? u.descDesktop : u.descCli}>
|
|
95
85
|
{/* Knowledge base safety note */}
|
|
96
86
|
<div className="flex gap-2.5 p-3 rounded-md bg-muted/50 border border-border">
|
|
97
87
|
<ShieldCheck size={14} className="text-success shrink-0 mt-0.5" />
|
|
@@ -116,7 +106,7 @@ export function UninstallTab() {
|
|
|
116
106
|
{phase === 'idle' && (
|
|
117
107
|
<button
|
|
118
108
|
onClick={() => setPhase('confirming')}
|
|
119
|
-
className="px-3 py-
|
|
109
|
+
className="px-3.5 py-2 text-sm font-medium rounded-lg bg-error/10 text-error border border-error/20 hover:bg-error/20 transition-colors focus-visible:ring-1 focus-visible:ring-ring disabled:opacity-40 disabled:cursor-not-allowed"
|
|
120
110
|
>
|
|
121
111
|
<Trash2 size={12} className="inline mr-1.5 -mt-px" />
|
|
122
112
|
{u.confirmButton}
|
|
@@ -129,13 +119,13 @@ export function UninstallTab() {
|
|
|
129
119
|
<div className="flex gap-2">
|
|
130
120
|
<button
|
|
131
121
|
onClick={handleUninstall}
|
|
132
|
-
className="px-3 py-
|
|
122
|
+
className="px-3.5 py-2 text-sm font-medium rounded-lg bg-error text-white hover:bg-error/90 transition-colors focus-visible:ring-1 focus-visible:ring-ring"
|
|
133
123
|
>
|
|
134
124
|
{u.confirmButton}
|
|
135
125
|
</button>
|
|
136
126
|
<button
|
|
137
127
|
onClick={() => setPhase('idle')}
|
|
138
|
-
className="px-3 py-
|
|
128
|
+
className="px-3.5 py-2 text-sm font-medium rounded-lg bg-muted text-foreground hover:bg-muted/80 transition-colors focus-visible:ring-1 focus-visible:ring-ring"
|
|
139
129
|
>
|
|
140
130
|
{u.cancelButton}
|
|
141
131
|
</button>
|
|
@@ -174,6 +164,6 @@ export function UninstallTab() {
|
|
|
174
164
|
</button>
|
|
175
165
|
</div>
|
|
176
166
|
)}
|
|
177
|
-
</
|
|
167
|
+
</SettingCard>
|
|
178
168
|
);
|
|
179
169
|
}
|
|
@@ -178,59 +178,60 @@ function DesktopUpdateTab() {
|
|
|
178
178
|
{errorMsg}
|
|
179
179
|
</div>
|
|
180
180
|
)}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
<div className="flex items-center gap-2">
|
|
184
|
-
<button
|
|
185
|
-
onClick={handleCheck}
|
|
186
|
-
disabled={state === 'checking' || state === 'downloading'}
|
|
187
|
-
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
|
|
188
|
-
>
|
|
189
|
-
<RefreshCw size={12} className={state === 'checking' ? 'animate-spin' : ''} />
|
|
190
|
-
{u?.checkButton ?? 'Check for Updates'}
|
|
191
|
-
</button>
|
|
192
|
-
|
|
193
|
-
{state === 'idle' && available && (
|
|
181
|
+
{/* Actions */}
|
|
182
|
+
<div className="flex items-center gap-2 pt-1">
|
|
194
183
|
<button
|
|
195
|
-
onClick={
|
|
196
|
-
|
|
184
|
+
onClick={handleCheck}
|
|
185
|
+
disabled={state === 'checking' || state === 'downloading'}
|
|
186
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
|
|
197
187
|
>
|
|
198
|
-
<
|
|
199
|
-
{
|
|
188
|
+
<RefreshCw size={14} className={state === 'checking' ? 'animate-spin' : ''} />
|
|
189
|
+
{u?.checkButton ?? 'Check for Updates'}
|
|
200
190
|
</button>
|
|
201
|
-
)}
|
|
202
191
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}}
|
|
213
|
-
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"
|
|
214
|
-
>
|
|
215
|
-
<RefreshCw size={12} />
|
|
216
|
-
{u?.desktopRestart ?? 'Restart Now'}
|
|
217
|
-
</button>
|
|
218
|
-
)}
|
|
219
|
-
</div>
|
|
192
|
+
{state === 'idle' && available && (
|
|
193
|
+
<button
|
|
194
|
+
onClick={handleInstall}
|
|
195
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors"
|
|
196
|
+
>
|
|
197
|
+
<Download size={14} />
|
|
198
|
+
{version ? `Update to v${version}` : 'Update'}
|
|
199
|
+
</button>
|
|
200
|
+
)}
|
|
220
201
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
202
|
+
{state === 'ready' && (
|
|
203
|
+
<button
|
|
204
|
+
onClick={async () => {
|
|
205
|
+
try {
|
|
206
|
+
await bridge.installUpdate();
|
|
207
|
+
} catch {
|
|
208
|
+
setState('error');
|
|
209
|
+
setErrorMsg(u?.error ?? 'Failed to install update. Please try again.');
|
|
210
|
+
}
|
|
211
|
+
}}
|
|
212
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors"
|
|
213
|
+
>
|
|
214
|
+
<RefreshCw size={14} />
|
|
215
|
+
{u?.desktopRestart ?? 'Restart Now'}
|
|
216
|
+
</button>
|
|
217
|
+
)}
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
{/* Info */}
|
|
221
|
+
<div className="border-t border-border/50 pt-3 space-y-2">
|
|
222
|
+
<a
|
|
223
|
+
href={CHANGELOG_URL}
|
|
224
|
+
target="_blank"
|
|
225
|
+
rel="noopener noreferrer"
|
|
226
|
+
className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
227
|
+
>
|
|
228
|
+
<ExternalLink size={12} />
|
|
229
|
+
{u?.releaseNotes ?? 'View release notes'}
|
|
230
|
+
</a>
|
|
231
|
+
<p className="text-2xs text-muted-foreground/60">
|
|
232
|
+
{u?.desktopHint ?? 'Updates are delivered through the Desktop app auto-updater.'}
|
|
233
|
+
</p>
|
|
234
|
+
</div>
|
|
234
235
|
</div>
|
|
235
236
|
</div>
|
|
236
237
|
);
|
|
@@ -527,44 +528,43 @@ function BrowserUpdateTab() {
|
|
|
527
528
|
)}
|
|
528
529
|
</div>
|
|
529
530
|
)}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
{/* Actions */}
|
|
533
|
-
<div className="flex items-center gap-2">
|
|
534
|
-
<button
|
|
535
|
-
onClick={checkUpdate}
|
|
536
|
-
disabled={state === 'checking' || state === 'updating'}
|
|
537
|
-
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
|
|
538
|
-
>
|
|
539
|
-
<RefreshCw size={12} className={state === 'checking' ? 'animate-spin' : ''} />
|
|
540
|
-
{u?.checkButton ?? 'Check for Updates'}
|
|
541
|
-
</button>
|
|
542
|
-
|
|
543
|
-
{info?.hasUpdate && state !== 'updating' && state !== 'updated' && (
|
|
531
|
+
{/* Actions */}
|
|
532
|
+
<div className="flex items-center gap-2 pt-1">
|
|
544
533
|
<button
|
|
545
|
-
onClick={
|
|
546
|
-
|
|
534
|
+
onClick={checkUpdate}
|
|
535
|
+
disabled={state === 'checking' || state === 'updating'}
|
|
536
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
|
|
547
537
|
>
|
|
548
|
-
<
|
|
549
|
-
{u?.
|
|
538
|
+
<RefreshCw size={14} className={state === 'checking' ? 'animate-spin' : ''} />
|
|
539
|
+
{u?.checkButton ?? 'Check for Updates'}
|
|
550
540
|
</button>
|
|
551
|
-
)}
|
|
552
|
-
</div>
|
|
553
541
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
542
|
+
{info?.hasUpdate && state !== 'updating' && state !== 'updated' && (
|
|
543
|
+
<button
|
|
544
|
+
onClick={handleUpdate}
|
|
545
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-sm rounded-lg font-medium text-[var(--amber-foreground)] bg-[var(--amber)] transition-colors"
|
|
546
|
+
>
|
|
547
|
+
<Download size={14} />
|
|
548
|
+
{u?.updateButton ? u.updateButton(info.latest) : `Update to v${info.latest}`}
|
|
549
|
+
</button>
|
|
550
|
+
)}
|
|
551
|
+
</div>
|
|
552
|
+
|
|
553
|
+
{/* Info */}
|
|
554
|
+
<div className="border-t border-border/50 pt-3 space-y-2">
|
|
555
|
+
<a
|
|
556
|
+
href={CHANGELOG_URL}
|
|
557
|
+
target="_blank"
|
|
558
|
+
rel="noopener noreferrer"
|
|
559
|
+
className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
560
|
+
>
|
|
561
|
+
<ExternalLink size={12} />
|
|
562
|
+
{u?.releaseNotes ?? 'View release notes'}
|
|
563
|
+
</a>
|
|
564
|
+
<p className="text-2xs text-muted-foreground/60">
|
|
565
|
+
{u?.hint ?? 'Updates are installed via npm. Equivalent to running'} <code className="font-mono bg-muted px-1 py-0.5 rounded">mindos update</code> {u?.inTerminal ?? 'in your terminal.'}
|
|
566
|
+
</p>
|
|
567
|
+
</div>
|
|
568
568
|
</div>
|
|
569
569
|
</div>
|
|
570
570
|
);
|
|
@@ -37,17 +37,24 @@ export interface SettingsData {
|
|
|
37
37
|
export type Tab = 'ai' | 'appearance' | 'knowledge' | 'mcp' | 'sync' | 'update' | 'uninstall';
|
|
38
38
|
|
|
39
39
|
export const CONTENT_WIDTHS = [
|
|
40
|
-
{ value: '680px', label: 'Narrow
|
|
41
|
-
{ value: '780px', label: 'Default
|
|
42
|
-
{ value: '960px', label: 'Wide
|
|
43
|
-
{ value: '100%', label: 'Full width
|
|
40
|
+
{ value: '680px', label: 'Narrow', width: 42 },
|
|
41
|
+
{ value: '780px', label: 'Default', width: 56 },
|
|
42
|
+
{ value: '960px', label: 'Wide', width: 75 },
|
|
43
|
+
{ value: '100%', label: 'Full', width: 100 },
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
export const FONT_SIZES = [
|
|
47
|
+
{ value: '14px', label: '14', numericValue: 14 },
|
|
48
|
+
{ value: '15px', label: '15', numericValue: 15, isDefault: true },
|
|
49
|
+
{ value: '16px', label: '16', numericValue: 16 },
|
|
50
|
+
{ value: '17px', label: '17', numericValue: 17 },
|
|
44
51
|
];
|
|
45
52
|
|
|
46
53
|
export const FONTS = [
|
|
47
|
-
{ value: 'lora', label: 'Lora
|
|
48
|
-
{ value: 'ibm-plex-sans', label: 'IBM Plex Sans', style: { fontFamily: "'IBM Plex Sans', sans-serif" } },
|
|
49
|
-
{ value: 'geist', label: 'Geist', style: { fontFamily: 'var(--font-geist-sans), sans-serif' } },
|
|
50
|
-
{ value: 'ibm-plex-mono', label: 'IBM Plex Mono
|
|
54
|
+
{ value: 'lora', label: 'Lora', category: 'Serif', style: { fontFamily: 'Lora, Georgia, serif' } },
|
|
55
|
+
{ value: 'ibm-plex-sans', label: 'IBM Plex Sans', category: 'Sans', style: { fontFamily: "'IBM Plex Sans', sans-serif" } },
|
|
56
|
+
{ value: 'geist', label: 'Geist', category: 'Sans', style: { fontFamily: 'var(--font-geist-sans), sans-serif' } },
|
|
57
|
+
{ value: 'ibm-plex-mono', label: 'IBM Plex Mono', category: 'Mono', style: { fontFamily: "'IBM Plex Mono', monospace" } },
|
|
51
58
|
];
|
|
52
59
|
|
|
53
60
|
/* ── MCP Types ────────────────────────────────────────────────── */
|
|
@@ -127,6 +134,8 @@ export interface McpTabProps {
|
|
|
127
134
|
export interface AppearanceTabProps {
|
|
128
135
|
font: string;
|
|
129
136
|
setFont: (v: string) => void;
|
|
137
|
+
fontSize: string;
|
|
138
|
+
setFontSize: (v: string) => void;
|
|
130
139
|
contentWidth: string;
|
|
131
140
|
setContentWidth: (v: string) => void;
|
|
132
141
|
dark: boolean;
|
package/app/lib/acp/session.ts
CHANGED
|
@@ -125,8 +125,15 @@ export async function createSessionFromEntry(
|
|
|
125
125
|
}, 15_000);
|
|
126
126
|
|
|
127
127
|
if (newResponse.error) {
|
|
128
|
-
|
|
129
|
-
|
|
128
|
+
const errMsg = newResponse.error.message ?? 'session/new failed';
|
|
129
|
+
// Authentication errors are fatal — agent cannot proceed without auth
|
|
130
|
+
if (/auth/i.test(errMsg)) {
|
|
131
|
+
unsubApproval();
|
|
132
|
+
killAgent(proc);
|
|
133
|
+
throw new Error(`${entry.id}: ${errMsg}`);
|
|
134
|
+
}
|
|
135
|
+
// Other errors are non-fatal: some agents may not support explicit session/new
|
|
136
|
+
console.warn(`ACP session/new warning for ${entry.id}: ${errMsg}`);
|
|
130
137
|
} else {
|
|
131
138
|
const newResult = newResponse.result as Record<string, unknown> | undefined;
|
|
132
139
|
if (newResult) {
|
|
@@ -134,7 +141,9 @@ export async function createSessionFromEntry(
|
|
|
134
141
|
configOptions = parseConfigOptions(newResult.configOptions);
|
|
135
142
|
}
|
|
136
143
|
}
|
|
137
|
-
} catch {
|
|
144
|
+
} catch (sessionErr) {
|
|
145
|
+
// Re-throw auth errors — they are fatal
|
|
146
|
+
if (sessionErr instanceof Error && /auth/i.test(sessionErr.message)) throw sessionErr;
|
|
138
147
|
// Non-fatal: agent may not support explicit session/new (backwards compat)
|
|
139
148
|
}
|
|
140
149
|
|