@geminilight/mindos 0.5.35 → 0.5.37
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/globals.css +8 -2
- package/app/components/GuideCard.tsx +3 -10
- package/app/components/HomeContent.tsx +2 -3
- package/app/components/OnboardingView.tsx +1 -1
- package/app/components/RightAskPanel.tsx +25 -10
- package/app/components/panels/PluginsPanel.tsx +16 -14
- package/app/components/settings/KnowledgeTab.tsx +29 -1
- package/app/lib/i18n-en.ts +1 -0
- package/app/lib/i18n-zh.ts +1 -0
- package/app/next-env.d.ts +1 -1
- package/package.json +1 -1
- package/app/components/WelcomeBanner.tsx +0 -63
package/app/app/globals.css
CHANGED
|
@@ -75,7 +75,8 @@ body {
|
|
|
75
75
|
--ring: var(--amber);
|
|
76
76
|
--radius: 0.5rem;
|
|
77
77
|
--amber: #c8873a;
|
|
78
|
-
--amber-dim: rgba(200, 135, 58, 0.
|
|
78
|
+
--amber-dim: rgba(200, 135, 58, 0.18);
|
|
79
|
+
--amber-subtle: rgba(200, 135, 30, 0.08);
|
|
79
80
|
--amber-foreground: #131210;
|
|
80
81
|
--success: #7aad80;
|
|
81
82
|
--error: #c85050;
|
|
@@ -109,7 +110,8 @@ body {
|
|
|
109
110
|
--input: rgba(232, 228, 220, 0.1);
|
|
110
111
|
--ring: var(--amber);
|
|
111
112
|
--amber: #d4954a;
|
|
112
|
-
--amber-dim: rgba(212, 149, 74, 0.
|
|
113
|
+
--amber-dim: rgba(212, 149, 74, 0.20);
|
|
114
|
+
--amber-subtle: rgba(212, 149, 74, 0.10);
|
|
113
115
|
--amber-foreground: #131210;
|
|
114
116
|
--success: #7aad80;
|
|
115
117
|
--error: #c85050;
|
|
@@ -298,6 +300,10 @@ body {
|
|
|
298
300
|
backdrop-filter: blur(8px);
|
|
299
301
|
-webkit-backdrop-filter: blur(8px);
|
|
300
302
|
}
|
|
303
|
+
.dark .modal-backdrop {
|
|
304
|
+
background: rgba(0, 0, 0, 0.65);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
301
307
|
|
|
302
308
|
/* Micro type scale: text-2xs = 10px (between nothing and text-xs 12px) */
|
|
303
309
|
@layer utilities {
|
|
@@ -15,10 +15,6 @@ const DIR_ICONS: Record<string, string> = {
|
|
|
15
15
|
|
|
16
16
|
const EMPTY_FILES = ['INSTRUCTION.md', 'README.md', 'CONFIG.json'];
|
|
17
17
|
|
|
18
|
-
/* Shared amber-subtle background — used as inline style because Tailwind can't handle
|
|
19
|
-
CSS var() with fallback in arbitrary values: bg-[var(--amber-subtle,rgba(...))] breaks. */
|
|
20
|
-
const amberSubtleBg = 'var(--amber-subtle, rgba(200,135,30,0.08))';
|
|
21
|
-
|
|
22
18
|
interface GuideCardProps {
|
|
23
19
|
/** Called when user clicks a file/dir to open it in FileView */
|
|
24
20
|
onNavigate?: (path: string) => void;
|
|
@@ -155,8 +151,7 @@ export default function GuideCard({ onNavigate }: GuideCardProps) {
|
|
|
155
151
|
// After all next-steps done → final state (auto-dismisses after 8s)
|
|
156
152
|
if (allDone && allNextDone) {
|
|
157
153
|
return (
|
|
158
|
-
<div className="mb-6 rounded-xl border border-[var(--amber)] px-5 py-4 flex items-center gap-3 animate-in fade-in duration-300"
|
|
159
|
-
style={{ background: amberSubtleBg }}>
|
|
154
|
+
<div className="mb-6 rounded-xl border border-[var(--amber)] px-5 py-4 flex items-center gap-3 animate-in fade-in duration-300 bg-[var(--amber-subtle)]">
|
|
160
155
|
<Sparkles size={16} className="animate-spin-slow text-[var(--amber)]" />
|
|
161
156
|
<span className="text-sm font-semibold flex-1 text-foreground">
|
|
162
157
|
✨ {g.done.titleFinal}
|
|
@@ -178,8 +173,7 @@ export default function GuideCard({ onNavigate }: GuideCardProps) {
|
|
|
178
173
|
if (allDone) {
|
|
179
174
|
const step = nextSteps[nextIdx];
|
|
180
175
|
return (
|
|
181
|
-
<div className="mb-6 rounded-xl border border-[var(--amber)] px-5 py-4 animate-in fade-in duration-300"
|
|
182
|
-
style={{ background: amberSubtleBg }}>
|
|
176
|
+
<div className="mb-6 rounded-xl border border-[var(--amber)] px-5 py-4 animate-in fade-in duration-300 bg-[var(--amber-subtle)]">
|
|
183
177
|
<div className="flex items-center gap-3">
|
|
184
178
|
<Sparkles size={16} className="text-[var(--amber)]" />
|
|
185
179
|
<span className="text-sm font-semibold flex-1 text-foreground">
|
|
@@ -204,8 +198,7 @@ export default function GuideCard({ onNavigate }: GuideCardProps) {
|
|
|
204
198
|
|
|
205
199
|
// Main guide card with 3 tasks
|
|
206
200
|
return (
|
|
207
|
-
<div className="mb-6 rounded-xl border border-[var(--amber)] overflow-hidden"
|
|
208
|
-
style={{ background: amberSubtleBg }}>
|
|
201
|
+
<div className="mb-6 rounded-xl border border-[var(--amber)] overflow-hidden bg-[var(--amber-subtle)]">
|
|
209
202
|
|
|
210
203
|
{/* Header */}
|
|
211
204
|
<div className="flex items-center gap-3 px-5 pt-4 pb-2">
|
|
@@ -81,7 +81,7 @@ export default function HomeContent({ recent, existingFiles }: { recent: RecentF
|
|
|
81
81
|
className="flex-1 flex items-center gap-3 px-4 py-3 rounded-xl border border-border bg-card transition-all duration-150 hover:border-amber-500/50 hover:bg-amber-500/8"
|
|
82
82
|
>
|
|
83
83
|
<Sparkles size={15} className="shrink-0 text-[var(--amber)]" />
|
|
84
|
-
<span className="text-sm flex-1 text-left text-foreground"
|
|
84
|
+
<span className="text-sm flex-1 text-left text-foreground">
|
|
85
85
|
{suggestions[suggestionIdx]}
|
|
86
86
|
</span>
|
|
87
87
|
<kbd className="hidden sm:inline-flex items-center gap-0.5 px-2 py-0.5 rounded text-xs font-mono font-medium bg-[var(--amber-dim)] text-[var(--amber)]">
|
|
@@ -215,8 +215,7 @@ export default function HomeContent({ recent, existingFiles }: { recent: RecentF
|
|
|
215
215
|
>
|
|
216
216
|
<ChevronDown
|
|
217
217
|
size={12}
|
|
218
|
-
className=
|
|
219
|
-
style={{ transform: showAll ? 'rotate(180deg)' : undefined }}
|
|
218
|
+
className={`transition-transform duration-200 ${showAll ? 'rotate-180' : ''}`}
|
|
220
219
|
/>
|
|
221
220
|
<span>{showAll ? t.home.showLess : t.home.showMore}</span>
|
|
222
221
|
</button>
|
|
@@ -80,7 +80,7 @@ export default function OnboardingView() {
|
|
|
80
80
|
className="max-w-2xl mx-auto mb-6 flex items-center gap-2.5 px-4 py-3 rounded-lg border border-destructive/30 bg-destructive/5 text-sm text-destructive"
|
|
81
81
|
>
|
|
82
82
|
<AlertCircle size={16} className="shrink-0" />
|
|
83
|
-
<span className="flex-1">{ob.initError ?? 'Initialization failed. Please try again.'}
|
|
83
|
+
<span className="flex-1">{ob.initError ?? 'Initialization failed. Please try again.'}</span>
|
|
84
84
|
<button
|
|
85
85
|
onClick={() => setError(null)}
|
|
86
86
|
className="text-xs underline shrink-0 hover:opacity-80"
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { AlertCircle } from 'lucide-react';
|
|
3
4
|
import AskContent from '@/components/ask/AskContent';
|
|
5
|
+
import ErrorBoundary from '@/components/ErrorBoundary';
|
|
4
6
|
import { useResizeDrag } from '@/hooks/useResizeDrag';
|
|
5
7
|
|
|
6
8
|
const DEFAULT_WIDTH = 380;
|
|
@@ -47,16 +49,29 @@ export default function RightAskPanel({
|
|
|
47
49
|
role="complementary"
|
|
48
50
|
aria-label="MindOS Agent panel"
|
|
49
51
|
>
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
<ErrorBoundary fallback={
|
|
53
|
+
<div className="flex flex-col items-center justify-center h-full gap-3 px-6 text-center">
|
|
54
|
+
<AlertCircle size={20} className="text-muted-foreground" />
|
|
55
|
+
<p className="text-sm text-muted-foreground">AI panel encountered an error.</p>
|
|
56
|
+
<button
|
|
57
|
+
onClick={() => window.location.reload()}
|
|
58
|
+
className="text-xs px-3 py-1.5 rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
|
|
59
|
+
>
|
|
60
|
+
Reload page
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
63
|
+
}>
|
|
64
|
+
<AskContent
|
|
65
|
+
visible={open}
|
|
66
|
+
variant="panel"
|
|
67
|
+
currentFile={open ? currentFile : undefined}
|
|
68
|
+
initialMessage={initialMessage}
|
|
69
|
+
onFirstMessage={onFirstMessage}
|
|
70
|
+
onClose={onClose}
|
|
71
|
+
askMode={askMode}
|
|
72
|
+
onModeSwitch={onModeSwitch}
|
|
73
|
+
/>
|
|
74
|
+
</ErrorBoundary>
|
|
60
75
|
|
|
61
76
|
{/* Drag resize handle — LEFT edge */}
|
|
62
77
|
<div
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useState, useEffect, useCallback } from 'react';
|
|
3
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
4
4
|
import { useRouter } from 'next/navigation';
|
|
5
5
|
import { getAllRenderers, isRendererEnabled, setRendererEnabled, loadDisabledState } from '@/lib/renderers/registry';
|
|
6
6
|
import { Toggle } from '../settings/Primitives';
|
|
@@ -27,25 +27,25 @@ export default function PluginsPanel({ active, maximized, onMaximize }: PluginsP
|
|
|
27
27
|
setMounted(true);
|
|
28
28
|
}, []);
|
|
29
29
|
|
|
30
|
-
// Check which entry files exist
|
|
30
|
+
// Check which entry files exist — fetch once on mount, cache result
|
|
31
|
+
const fetchedRef = useRef(false);
|
|
31
32
|
useEffect(() => {
|
|
32
|
-
if (!mounted ||
|
|
33
|
+
if (!mounted || fetchedRef.current) return;
|
|
34
|
+
fetchedRef.current = true;
|
|
33
35
|
const entryPaths = getAllRenderers()
|
|
34
36
|
.map(r => r.entryPath)
|
|
35
37
|
.filter((p): p is string => !!p);
|
|
36
38
|
if (entryPaths.length === 0) return;
|
|
37
39
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
});
|
|
48
|
-
}, [mounted, active]);
|
|
40
|
+
// Single request: fetch all file paths and check which entry paths exist
|
|
41
|
+
fetch('/api/files')
|
|
42
|
+
.then(r => r.ok ? r.json() : [])
|
|
43
|
+
.then((allPaths: string[]) => {
|
|
44
|
+
const pathSet = new Set(allPaths);
|
|
45
|
+
setExistingFiles(new Set(entryPaths.filter(p => pathSet.has(p))));
|
|
46
|
+
})
|
|
47
|
+
.catch(() => {});
|
|
48
|
+
}, [mounted]);
|
|
49
49
|
|
|
50
50
|
const renderers = mounted ? getAllRenderers() : [];
|
|
51
51
|
const enabledCount = mounted ? renderers.filter(r => isRendererEnabled(r.id)).length : 0;
|
|
@@ -86,7 +86,9 @@ export default function PluginsPanel({ active, maximized, onMaximize }: PluginsP
|
|
|
86
86
|
${!enabled ? 'opacity-50' : ''}
|
|
87
87
|
`}
|
|
88
88
|
onClick={canOpen ? () => handleOpen(r.entryPath!) : undefined}
|
|
89
|
+
onKeyDown={canOpen ? (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleOpen(r.entryPath!); } } : undefined}
|
|
89
90
|
role={canOpen ? 'link' : undefined}
|
|
91
|
+
tabIndex={canOpen ? 0 : undefined}
|
|
90
92
|
>
|
|
91
93
|
{/* Top row: status dot + icon + name + toggle */}
|
|
92
94
|
<div className="flex items-center justify-between gap-2">
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState, useEffect, useCallback, useSyncExternalStore, useRef } from 'react';
|
|
4
|
-
import { Copy, Check, RefreshCw, Trash2, Sparkles, ChevronDown, ChevronRight, Loader2, Cpu, Zap, Database as DatabaseIcon, HardDrive } from 'lucide-react';
|
|
4
|
+
import { Copy, Check, RefreshCw, Trash2, Sparkles, ChevronDown, ChevronRight, Loader2, Cpu, Zap, Database as DatabaseIcon, HardDrive, RotateCcw } from 'lucide-react';
|
|
5
5
|
import type { KnowledgeTabProps } from './types';
|
|
6
6
|
import { Field, Input, EnvBadge, SectionLabel, Toggle } from './Primitives';
|
|
7
7
|
import { apiFetch } from '@/lib/api';
|
|
@@ -49,6 +49,27 @@ export function KnowledgeTab({ data, setData, t }: KnowledgeTabProps) {
|
|
|
49
49
|
});
|
|
50
50
|
}, [guideDismissed]);
|
|
51
51
|
|
|
52
|
+
const handleRestartWalkthrough = useCallback(() => {
|
|
53
|
+
apiFetch('/api/setup', {
|
|
54
|
+
method: 'PATCH',
|
|
55
|
+
headers: { 'Content-Type': 'application/json' },
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
guideState: {
|
|
58
|
+
active: true,
|
|
59
|
+
dismissed: false,
|
|
60
|
+
walkthroughStep: 0,
|
|
61
|
+
walkthroughDismissed: false,
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
64
|
+
})
|
|
65
|
+
.then(() => {
|
|
66
|
+
setGuideActive(true);
|
|
67
|
+
setGuideDismissed(false);
|
|
68
|
+
window.dispatchEvent(new Event('guide-state-updated'));
|
|
69
|
+
})
|
|
70
|
+
.catch(err => console.error('Failed to restart walkthrough:', err));
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
52
73
|
const origin = useSyncExternalStore(
|
|
53
74
|
() => () => {},
|
|
54
75
|
() => `${window.location.protocol}//${window.location.hostname}`,
|
|
@@ -207,6 +228,13 @@ export function KnowledgeTab({ data, setData, t }: KnowledgeTabProps) {
|
|
|
207
228
|
</div>
|
|
208
229
|
<Toggle checked={!guideDismissed} onChange={() => handleGuideToggle()} />
|
|
209
230
|
</div>
|
|
231
|
+
<button
|
|
232
|
+
onClick={handleRestartWalkthrough}
|
|
233
|
+
className="flex items-center gap-1.5 mt-2 px-3 py-1.5 text-xs rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors"
|
|
234
|
+
>
|
|
235
|
+
<RotateCcw size={12} />
|
|
236
|
+
{k.restartWalkthrough ?? 'Restart walkthrough'}
|
|
237
|
+
</button>
|
|
210
238
|
</div>
|
|
211
239
|
)}
|
|
212
240
|
|
package/app/lib/i18n-en.ts
CHANGED
|
@@ -234,6 +234,7 @@ export const en = {
|
|
|
234
234
|
authTokenClear: 'Clear token',
|
|
235
235
|
authTokenResetConfirm: 'Regenerate token? All existing MCP clients will need to update their config.',
|
|
236
236
|
authTokenMcpPort: 'MCP port',
|
|
237
|
+
restartWalkthrough: 'Restart walkthrough',
|
|
237
238
|
},
|
|
238
239
|
sync: {
|
|
239
240
|
emptyTitle: 'Cross-device Sync',
|
package/app/lib/i18n-zh.ts
CHANGED
package/app/next-env.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="next" />
|
|
2
2
|
/// <reference types="next/image-types/global" />
|
|
3
|
-
import "./.next/
|
|
3
|
+
import "./.next/types/routes.d.ts";
|
|
4
4
|
|
|
5
5
|
// NOTE: This file should not be edited
|
|
6
6
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
package/package.json
CHANGED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useState, useEffect } from 'react';
|
|
4
|
-
import { X, Sparkles } from 'lucide-react';
|
|
5
|
-
import { useLocale } from '@/lib/LocaleContext';
|
|
6
|
-
|
|
7
|
-
export default function WelcomeBanner() {
|
|
8
|
-
const { t } = useLocale();
|
|
9
|
-
const s = t.setup;
|
|
10
|
-
const [visible, setVisible] = useState(false);
|
|
11
|
-
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
// Show banner if ?welcome=1 is in the URL
|
|
14
|
-
const params = new URLSearchParams(window.location.search);
|
|
15
|
-
if (params.get('welcome') === '1') {
|
|
16
|
-
setVisible(true);
|
|
17
|
-
// Remove ?welcome=1 from URL without reloading
|
|
18
|
-
const url = new URL(window.location.href);
|
|
19
|
-
url.searchParams.delete('welcome');
|
|
20
|
-
const newUrl = url.pathname + (url.searchParams.size > 0 ? '?' + url.searchParams.toString() : '');
|
|
21
|
-
window.history.replaceState({}, '', newUrl);
|
|
22
|
-
}
|
|
23
|
-
}, []);
|
|
24
|
-
|
|
25
|
-
if (!visible) return null;
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<div className="mb-6 rounded-xl border px-5 py-4 flex items-start gap-4"
|
|
29
|
-
style={{ background: 'var(--amber-subtle, rgba(200,135,30,0.08))', borderColor: 'var(--amber)' }}>
|
|
30
|
-
<Sparkles size={18} className="mt-0.5 shrink-0" style={{ color: 'var(--amber)' }} />
|
|
31
|
-
<div className="flex-1 min-w-0">
|
|
32
|
-
<p className="text-sm font-semibold mb-1" style={{ color: 'var(--foreground)' }}>
|
|
33
|
-
{s.welcomeTitle}
|
|
34
|
-
</p>
|
|
35
|
-
<p className="text-xs leading-relaxed mb-3" style={{ color: 'var(--muted-foreground)' }}>
|
|
36
|
-
{s.welcomeDesc}
|
|
37
|
-
</p>
|
|
38
|
-
<div className="flex flex-wrap gap-2">
|
|
39
|
-
<a href="/setup?force=1" className="text-xs px-3 py-1.5 rounded-lg border transition-colors"
|
|
40
|
-
style={{ borderColor: 'var(--amber)', color: 'var(--amber)' }}>
|
|
41
|
-
{s.welcomeLinkReconfigure}
|
|
42
|
-
</a>
|
|
43
|
-
<button onClick={() => window.dispatchEvent(new KeyboardEvent('keydown', { key: '/', metaKey: true, bubbles: true }))}
|
|
44
|
-
className="text-xs px-3 py-1.5 rounded-lg border transition-colors"
|
|
45
|
-
style={{ borderColor: 'var(--border)', color: 'var(--muted-foreground)' }}>
|
|
46
|
-
{s.welcomeLinkAskAI}
|
|
47
|
-
</button>
|
|
48
|
-
<button
|
|
49
|
-
onClick={() => window.dispatchEvent(new KeyboardEvent('keydown', { key: ',', metaKey: true, bubbles: true }))}
|
|
50
|
-
className="text-xs px-3 py-1.5 rounded-lg border transition-colors"
|
|
51
|
-
style={{ borderColor: 'var(--border)', color: 'var(--muted-foreground)' }}>
|
|
52
|
-
{s.welcomeLinkMCP}
|
|
53
|
-
</button>
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
<button onClick={() => setVisible(false)}
|
|
57
|
-
className="p-1 rounded hover:bg-muted transition-colors shrink-0"
|
|
58
|
-
style={{ color: 'var(--muted-foreground)' }}>
|
|
59
|
-
<X size={14} />
|
|
60
|
-
</button>
|
|
61
|
-
</div>
|
|
62
|
-
);
|
|
63
|
-
}
|