@geminilight/mindos 0.5.22 → 0.5.24
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 +7 -14
- package/app/app/api/bootstrap/route.ts +1 -0
- package/app/app/globals.css +14 -0
- package/app/app/setup/page.tsx +3 -2
- package/app/components/ActivityBar.tsx +183 -0
- package/app/components/AskFab.tsx +39 -97
- package/app/components/AskModal.tsx +13 -371
- package/app/components/Breadcrumb.tsx +4 -4
- package/app/components/FileTree.tsx +21 -4
- package/app/components/Logo.tsx +39 -0
- package/app/components/Panel.tsx +152 -0
- package/app/components/RightAskPanel.tsx +72 -0
- package/app/components/SettingsModal.tsx +9 -241
- package/app/components/SidebarLayout.tsx +426 -12
- package/app/components/SyncStatusBar.tsx +74 -53
- package/app/components/TableOfContents.tsx +4 -2
- package/app/components/ask/AskContent.tsx +418 -0
- package/app/components/ask/MessageList.tsx +2 -2
- package/app/components/panels/AgentsPanel.tsx +231 -0
- package/app/components/panels/PanelHeader.tsx +35 -0
- package/app/components/panels/PluginsPanel.tsx +106 -0
- package/app/components/panels/SearchPanel.tsx +178 -0
- package/app/components/panels/SyncPopover.tsx +105 -0
- package/app/components/renderers/csv/TableView.tsx +4 -4
- package/app/components/settings/AiTab.tsx +39 -1
- package/app/components/settings/KnowledgeTab.tsx +116 -2
- package/app/components/settings/McpTab.tsx +6 -6
- package/app/components/settings/SettingsContent.tsx +343 -0
- package/app/components/settings/types.ts +1 -1
- package/app/components/setup/index.tsx +2 -23
- package/app/hooks/useResizeDrag.ts +78 -0
- package/app/lib/agent/index.ts +0 -1
- package/app/lib/agent/model.ts +33 -10
- package/app/lib/format.ts +19 -0
- package/app/lib/i18n-en.ts +6 -6
- package/app/lib/i18n-zh.ts +5 -5
- package/app/next-env.d.ts +1 -1
- package/app/next.config.ts +1 -1
- package/bin/cli.js +27 -97
- package/package.json +4 -2
- package/scripts/setup.js +2 -12
- package/skills/mindos/SKILL.md +226 -8
- package/skills/mindos-zh/SKILL.md +226 -8
- package/app/lib/agent/skill-rules.ts +0 -70
- package/app/package-lock.json +0 -15736
package/app/app/api/ask/route.ts
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
createTransformContext,
|
|
14
14
|
} from '@/lib/agent/context';
|
|
15
15
|
import { logAgentOp } from '@/lib/agent/log';
|
|
16
|
-
import { loadSkillRules } from '@/lib/agent/skill-rules';
|
|
17
16
|
import { readSettings } from '@/lib/settings';
|
|
18
17
|
import { MindOSError, apiError, ErrorCodes } from '@/lib/errors';
|
|
19
18
|
import { metrics } from '@/lib/metrics';
|
|
@@ -179,17 +178,15 @@ export async function POST(req: NextRequest) {
|
|
|
179
178
|
const contextStrategy = agentConfig.contextStrategy ?? 'auto';
|
|
180
179
|
|
|
181
180
|
// Auto-load skill + bootstrap context for each request.
|
|
182
|
-
// 1. SKILL.md —
|
|
183
|
-
// 2. skill-rules.md — user's
|
|
184
|
-
// 3. user-rules.md — user's personalized rules (if exists)
|
|
181
|
+
// 1. SKILL.md — complete skill with operating rules (always loaded)
|
|
182
|
+
// 2. user-skill-rules.md — user's personalized rules from KB root (if exists)
|
|
185
183
|
const isZh = serverSettings.disabledSkills?.includes('mindos') ?? false;
|
|
186
184
|
const skillDirName = isZh ? 'mindos-zh' : 'mindos';
|
|
187
185
|
const skillPath = path.resolve(process.cwd(), `data/skills/${skillDirName}/SKILL.md`);
|
|
188
186
|
const skill = readAbsoluteFile(skillPath);
|
|
189
187
|
|
|
190
|
-
// Progressive skill loading: read skill-rules + user-rules from knowledge base
|
|
191
188
|
const mindRoot = getMindRoot();
|
|
192
|
-
const
|
|
189
|
+
const userSkillRules = readKnowledgeFile('user-skill-rules.md');
|
|
193
190
|
|
|
194
191
|
const targetDir = dirnameOf(currentFile);
|
|
195
192
|
const bootstrap = {
|
|
@@ -208,8 +205,7 @@ export async function POST(req: NextRequest) {
|
|
|
208
205
|
const truncationWarnings: string[] = [];
|
|
209
206
|
if (!skill.ok) initFailures.push(`skill.mindos: failed (${skill.error})`);
|
|
210
207
|
if (skill.ok && skill.truncated) truncationWarnings.push('skill.mindos was truncated');
|
|
211
|
-
if (
|
|
212
|
-
if (userRules.ok && userRules.truncated) truncationWarnings.push('user-rules.md was truncated');
|
|
208
|
+
if (userSkillRules.ok && userSkillRules.truncated) truncationWarnings.push('user-skill-rules.md was truncated');
|
|
213
209
|
if (!bootstrap.instruction.ok) initFailures.push(`bootstrap.instruction: failed (${bootstrap.instruction.error})`);
|
|
214
210
|
if (bootstrap.instruction.ok && bootstrap.instruction.truncated) truncationWarnings.push('bootstrap.instruction was truncated');
|
|
215
211
|
if (!bootstrap.index.ok) initFailures.push(`bootstrap.index: failed (${bootstrap.index.error})`);
|
|
@@ -233,12 +229,9 @@ export async function POST(req: NextRequest) {
|
|
|
233
229
|
|
|
234
230
|
const initContextBlocks: string[] = [];
|
|
235
231
|
if (skill.ok) initContextBlocks.push(`## mindos_skill_md\n\n${skill.content}`);
|
|
236
|
-
//
|
|
237
|
-
if (
|
|
238
|
-
initContextBlocks.push(`##
|
|
239
|
-
}
|
|
240
|
-
if (userRules.ok && !userRules.empty) {
|
|
241
|
-
initContextBlocks.push(`## user_rules\n\nUser personalization rules (.agents/skills/${skillDirName}/user-rules.md):\n\n${userRules.content}`);
|
|
232
|
+
// User personalization rules (from knowledge base root)
|
|
233
|
+
if (userSkillRules.ok && !userSkillRules.truncated && userSkillRules.content.trim()) {
|
|
234
|
+
initContextBlocks.push(`## user_skill_rules\n\nUser personalization rules (user-skill-rules.md):\n\n${userSkillRules.content}`);
|
|
242
235
|
}
|
|
243
236
|
if (bootstrap.instruction.ok) initContextBlocks.push(`## bootstrap_instruction\n\n${bootstrap.instruction.content}`);
|
|
244
237
|
if (bootstrap.index.ok) initContextBlocks.push(`## bootstrap_index\n\n${bootstrap.index.content}`);
|
package/app/app/globals.css
CHANGED
|
@@ -212,6 +212,20 @@ body {
|
|
|
212
212
|
.prose img { max-width: 100%; border-radius: 6px; border: 1px solid var(--prose-code-border); }
|
|
213
213
|
.prose hr { border: none; border-top: 1px solid var(--prose-border); margin: 2.5em 0; }
|
|
214
214
|
|
|
215
|
+
/* Ask Panel — compact prose for side panel chat bubbles */
|
|
216
|
+
.prose.prose-panel {
|
|
217
|
+
font-size: 0.8125rem !important;
|
|
218
|
+
line-height: 1.6;
|
|
219
|
+
}
|
|
220
|
+
.prose.prose-panel h1, .prose.prose-panel h2, .prose.prose-panel h3, .prose.prose-panel h4 {
|
|
221
|
+
font-size: 0.8125rem !important;
|
|
222
|
+
margin-top: 1em;
|
|
223
|
+
margin-bottom: 0.4em;
|
|
224
|
+
}
|
|
225
|
+
.prose.prose-panel p { margin-bottom: 0.6em; }
|
|
226
|
+
.prose.prose-panel pre { padding: 0.6em 0.8em; margin: 0.8em 0; }
|
|
227
|
+
.prose.prose-panel pre code { font-size: 0.78em; }
|
|
228
|
+
|
|
215
229
|
:root {
|
|
216
230
|
--prose-body: #3a3730;
|
|
217
231
|
--prose-heading: #1c1a17;
|
package/app/app/setup/page.tsx
CHANGED
|
@@ -4,9 +4,10 @@ import SetupWizard from '@/components/SetupWizard';
|
|
|
4
4
|
|
|
5
5
|
export const dynamic = 'force-dynamic';
|
|
6
6
|
|
|
7
|
-
export default function SetupPage({ searchParams }: { searchParams: { force?: string } }) {
|
|
7
|
+
export default async function SetupPage({ searchParams }: { searchParams: Promise<{ force?: string }> }) {
|
|
8
8
|
const settings = readSettings();
|
|
9
|
-
const force = searchParams
|
|
9
|
+
const { force: forceParam } = await searchParams;
|
|
10
|
+
const force = forceParam === '1';
|
|
10
11
|
if (!settings.setupPending && !force) redirect('/');
|
|
11
12
|
return <SetupWizard />;
|
|
12
13
|
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useRef, useCallback } from 'react';
|
|
4
|
+
import Link from 'next/link';
|
|
5
|
+
import { FolderTree, Search, Settings, RefreshCw, Blocks, Bot, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
6
|
+
import { DOT_COLORS, getStatusLevel } from './SyncStatusBar';
|
|
7
|
+
import type { SyncStatus } from './settings/SyncTab';
|
|
8
|
+
import Logo from './Logo';
|
|
9
|
+
|
|
10
|
+
export type PanelId = 'files' | 'search' | 'plugins' | 'agents';
|
|
11
|
+
|
|
12
|
+
export const RAIL_WIDTH_COLLAPSED = 48;
|
|
13
|
+
export const RAIL_WIDTH_EXPANDED = 180;
|
|
14
|
+
|
|
15
|
+
interface ActivityBarProps {
|
|
16
|
+
activePanel: PanelId | null;
|
|
17
|
+
onPanelChange: (id: PanelId | null) => void;
|
|
18
|
+
syncStatus: SyncStatus | null;
|
|
19
|
+
expanded: boolean;
|
|
20
|
+
onExpandedChange: (expanded: boolean) => void;
|
|
21
|
+
onSettingsClick: () => void;
|
|
22
|
+
onSyncClick: (rect: DOMRect) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface RailButtonProps {
|
|
26
|
+
icon: React.ReactNode;
|
|
27
|
+
label: string;
|
|
28
|
+
shortcut?: string;
|
|
29
|
+
active?: boolean;
|
|
30
|
+
expanded: boolean;
|
|
31
|
+
onClick: () => void;
|
|
32
|
+
buttonRef?: React.Ref<HTMLButtonElement>;
|
|
33
|
+
/** Optional overlay badge (e.g. status dot) rendered inside the button */
|
|
34
|
+
badge?: React.ReactNode;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function RailButton({ icon, label, shortcut, active = false, expanded, onClick, buttonRef, badge }: RailButtonProps) {
|
|
38
|
+
return (
|
|
39
|
+
<button
|
|
40
|
+
ref={buttonRef}
|
|
41
|
+
onClick={onClick}
|
|
42
|
+
aria-pressed={active}
|
|
43
|
+
aria-label={label}
|
|
44
|
+
title={expanded ? undefined : (shortcut ? `${label} (${shortcut})` : label)}
|
|
45
|
+
className={`
|
|
46
|
+
relative flex items-center ${expanded ? 'justify-start px-3 w-full' : 'justify-center w-10'} h-10 rounded-md transition-colors
|
|
47
|
+
${active
|
|
48
|
+
? 'text-[var(--amber)] bg-[var(--amber-dim)]'
|
|
49
|
+
: 'text-muted-foreground hover:text-foreground hover:bg-muted'
|
|
50
|
+
}
|
|
51
|
+
focus-visible:ring-2 focus-visible:ring-ring
|
|
52
|
+
`}
|
|
53
|
+
>
|
|
54
|
+
{active && (
|
|
55
|
+
<span className="absolute left-0 top-1/2 -translate-y-1/2 w-[2px] h-[18px] rounded-r-full" style={{ background: 'var(--amber)' }} />
|
|
56
|
+
)}
|
|
57
|
+
<span className="shrink-0 flex items-center justify-center w-[18px]">{icon}</span>
|
|
58
|
+
{badge}
|
|
59
|
+
{expanded && (
|
|
60
|
+
<>
|
|
61
|
+
<span className="ml-2.5 text-sm whitespace-nowrap">{label}</span>
|
|
62
|
+
{shortcut && (
|
|
63
|
+
<span className="ml-auto text-2xs text-muted-foreground/60 font-mono shrink-0">{shortcut}</span>
|
|
64
|
+
)}
|
|
65
|
+
</>
|
|
66
|
+
)}
|
|
67
|
+
</button>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export default function ActivityBar({
|
|
72
|
+
activePanel,
|
|
73
|
+
onPanelChange,
|
|
74
|
+
syncStatus,
|
|
75
|
+
expanded,
|
|
76
|
+
onExpandedChange,
|
|
77
|
+
onSettingsClick,
|
|
78
|
+
onSyncClick,
|
|
79
|
+
}: ActivityBarProps) {
|
|
80
|
+
const lastClickRef = useRef(0);
|
|
81
|
+
const syncBtnRef = useRef<HTMLButtonElement>(null);
|
|
82
|
+
|
|
83
|
+
/** Debounce rapid clicks (300ms) — shared across all Rail buttons */
|
|
84
|
+
const debounced = useCallback((fn: () => void) => {
|
|
85
|
+
const now = Date.now();
|
|
86
|
+
if (now - lastClickRef.current < 300) return;
|
|
87
|
+
lastClickRef.current = now;
|
|
88
|
+
fn();
|
|
89
|
+
}, []);
|
|
90
|
+
|
|
91
|
+
const toggle = useCallback((id: PanelId) => {
|
|
92
|
+
debounced(() => onPanelChange(activePanel === id ? null : id));
|
|
93
|
+
}, [activePanel, onPanelChange, debounced]);
|
|
94
|
+
|
|
95
|
+
const syncLevel = getStatusLevel(syncStatus, false);
|
|
96
|
+
const showSyncDot = syncLevel !== 'off' && syncLevel !== 'synced';
|
|
97
|
+
|
|
98
|
+
const railWidth = expanded ? RAIL_WIDTH_EXPANDED : RAIL_WIDTH_COLLAPSED;
|
|
99
|
+
|
|
100
|
+
// Sync dot badge — positioned differently in collapsed vs expanded
|
|
101
|
+
const syncBadge = showSyncDot ? (
|
|
102
|
+
<span className={`absolute ${expanded ? 'left-[26px] top-1.5' : 'top-1.5 right-1.5'} w-2 h-2 rounded-full ${DOT_COLORS[syncLevel]} ${syncLevel === 'error' || syncLevel === 'conflicts' ? 'animate-pulse' : ''}`} />
|
|
103
|
+
) : undefined;
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<aside
|
|
107
|
+
className="group hidden md:flex fixed top-0 left-0 h-screen z-[31] flex-col bg-background border-r border-border transition-[width] duration-200 ease-out"
|
|
108
|
+
style={{ width: `${railWidth}px` }}
|
|
109
|
+
role="toolbar"
|
|
110
|
+
aria-label="Navigation"
|
|
111
|
+
aria-orientation="vertical"
|
|
112
|
+
>
|
|
113
|
+
{/* Content wrapper — overflow-hidden prevents text flash during width transitions */}
|
|
114
|
+
<div className="flex flex-col h-full w-full overflow-hidden">
|
|
115
|
+
{/* ── Top: Logo ── */}
|
|
116
|
+
<Link
|
|
117
|
+
href="/"
|
|
118
|
+
className={`flex items-center ${expanded ? 'px-3 gap-2' : 'justify-center'} w-full py-3 hover:opacity-80 transition-opacity`}
|
|
119
|
+
aria-label="MindOS Home"
|
|
120
|
+
>
|
|
121
|
+
<Logo id="rail" className="w-7 h-3.5 shrink-0" />
|
|
122
|
+
{expanded && <span className="text-sm font-semibold text-foreground font-display whitespace-nowrap">MindOS</span>}
|
|
123
|
+
</Link>
|
|
124
|
+
|
|
125
|
+
<div className={`${expanded ? 'mx-3' : 'mx-auto w-6'} border-t border-border`} />
|
|
126
|
+
|
|
127
|
+
{/* ── Middle: Core panel toggles ── */}
|
|
128
|
+
<div className={`flex flex-col ${expanded ? 'px-1.5' : 'items-center'} gap-1 py-2`}>
|
|
129
|
+
<RailButton icon={<FolderTree size={18} />} label="Files" active={activePanel === 'files'} expanded={expanded} onClick={() => toggle('files')} />
|
|
130
|
+
<RailButton icon={<Search size={18} />} label="Search" shortcut="⌘K" active={activePanel === 'search'} expanded={expanded} onClick={() => toggle('search')} />
|
|
131
|
+
<RailButton icon={<Blocks size={18} />} label="Plugins" active={activePanel === 'plugins'} expanded={expanded} onClick={() => toggle('plugins')} />
|
|
132
|
+
<RailButton icon={<Bot size={18} />} label="Agents" active={activePanel === 'agents'} expanded={expanded} onClick={() => toggle('agents')} />
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
{/* ── Spacer ── */}
|
|
136
|
+
<div className="flex-1" />
|
|
137
|
+
|
|
138
|
+
{/* ── Bottom: Action buttons (not panel toggles) ── */}
|
|
139
|
+
<div className={`${expanded ? 'mx-3' : 'mx-auto w-6'} border-t border-border`} />
|
|
140
|
+
<div className={`flex flex-col ${expanded ? 'px-1.5' : 'items-center'} gap-1 py-2`}>
|
|
141
|
+
<RailButton
|
|
142
|
+
icon={<Settings size={18} />}
|
|
143
|
+
label="Settings"
|
|
144
|
+
shortcut="⌘,"
|
|
145
|
+
expanded={expanded}
|
|
146
|
+
onClick={() => debounced(onSettingsClick)}
|
|
147
|
+
/>
|
|
148
|
+
<RailButton
|
|
149
|
+
icon={<RefreshCw size={18} />}
|
|
150
|
+
label="Sync"
|
|
151
|
+
expanded={expanded}
|
|
152
|
+
buttonRef={syncBtnRef}
|
|
153
|
+
badge={syncBadge}
|
|
154
|
+
onClick={() => debounced(() => {
|
|
155
|
+
const rect = syncBtnRef.current?.getBoundingClientRect();
|
|
156
|
+
if (rect) onSyncClick(rect);
|
|
157
|
+
})}
|
|
158
|
+
/>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
{/* ── Hover expand/collapse button — vertically centered on right edge ── */}
|
|
163
|
+
{/* z-[32] ensures it paints above Panel (z-30). Shows on Rail hover OR self-hover. */}
|
|
164
|
+
<button
|
|
165
|
+
onClick={() => onExpandedChange(!expanded)}
|
|
166
|
+
className="
|
|
167
|
+
absolute right-0 top-1/2 -translate-y-1/2 translate-x-1/2 z-[32]
|
|
168
|
+
w-5 h-5 rounded-full
|
|
169
|
+
bg-card border border-border shadow-sm
|
|
170
|
+
flex items-center justify-center
|
|
171
|
+
opacity-0 group-hover:opacity-100 hover:!opacity-100
|
|
172
|
+
transition-opacity duration-200
|
|
173
|
+
text-muted-foreground hover:text-foreground hover:bg-muted
|
|
174
|
+
focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring
|
|
175
|
+
"
|
|
176
|
+
aria-label={expanded ? 'Collapse sidebar' : 'Expand sidebar'}
|
|
177
|
+
title={expanded ? 'Collapse' : 'Expand'}
|
|
178
|
+
>
|
|
179
|
+
{expanded ? <ChevronLeft size={10} /> : <ChevronRight size={10} />}
|
|
180
|
+
</button>
|
|
181
|
+
</aside>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
@@ -1,105 +1,47 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useState, useEffect, useCallback } from 'react';
|
|
4
|
-
import { usePathname } from 'next/navigation';
|
|
5
3
|
import { Sparkles } from 'lucide-react';
|
|
6
|
-
import AskModal from './AskModal';
|
|
7
|
-
import { useAskModal } from '@/hooks/useAskModal';
|
|
8
4
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
// Listen to useAskModal store for cross-component open requests (e.g. from GuideCard)
|
|
17
|
-
const askModal = useAskModal();
|
|
18
|
-
const [initialMessage, setInitialMessage] = useState('');
|
|
19
|
-
const [openSource, setOpenSource] = useState<'user' | 'guide' | 'guide-next'>('user');
|
|
20
|
-
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
if (askModal.open) {
|
|
23
|
-
setInitialMessage(askModal.initialMessage);
|
|
24
|
-
setOpenSource(askModal.source);
|
|
25
|
-
setOpen(true);
|
|
26
|
-
askModal.close(); // Reset store state after consuming
|
|
27
|
-
}
|
|
28
|
-
}, [askModal.open, askModal.initialMessage, askModal.source, askModal.close]);
|
|
29
|
-
|
|
30
|
-
const handleClose = useCallback(() => {
|
|
31
|
-
setOpen(false);
|
|
32
|
-
setInitialMessage('');
|
|
33
|
-
setOpenSource('user');
|
|
34
|
-
}, []);
|
|
35
|
-
|
|
36
|
-
// Dispatch correct PATCH based on how the modal was opened
|
|
37
|
-
const handleFirstMessage = useCallback(() => {
|
|
38
|
-
const notifyGuide = () => window.dispatchEvent(new Event('guide-state-updated'));
|
|
39
|
-
|
|
40
|
-
if (openSource === 'guide') {
|
|
41
|
-
// Task ② completion: mark askedAI
|
|
42
|
-
fetch('/api/setup', {
|
|
43
|
-
method: 'PATCH',
|
|
44
|
-
headers: { 'Content-Type': 'application/json' },
|
|
45
|
-
body: JSON.stringify({ guideState: { askedAI: true } }),
|
|
46
|
-
}).then(notifyGuide).catch(() => {});
|
|
47
|
-
} else if (openSource === 'guide-next') {
|
|
48
|
-
// Next-step advancement: GuideCard already PATCHed nextStepIndex optimistically.
|
|
49
|
-
// Just notify GuideCard to re-fetch for consistency; no additional PATCH needed.
|
|
50
|
-
notifyGuide();
|
|
51
|
-
}
|
|
52
|
-
// For 'user' source: no guide action needed
|
|
53
|
-
}, [openSource]);
|
|
5
|
+
interface AskFabProps {
|
|
6
|
+
/** Toggle the right-side Ask AI panel */
|
|
7
|
+
onToggle: () => void;
|
|
8
|
+
/** Whether the right panel is currently open (FAB hides when open) */
|
|
9
|
+
askPanelOpen: boolean;
|
|
10
|
+
}
|
|
54
11
|
|
|
12
|
+
export default function AskFab({ onToggle, askPanelOpen }: AskFabProps) {
|
|
55
13
|
return (
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
opacity-0 group-hover:opacity-100
|
|
89
|
-
transition-all duration-200 ease-out
|
|
90
|
-
whitespace-nowrap overflow-hidden
|
|
91
|
-
">
|
|
92
|
-
MindOS Agent
|
|
93
|
-
</span>
|
|
94
|
-
</button>
|
|
95
|
-
|
|
96
|
-
<AskModal
|
|
97
|
-
open={open}
|
|
98
|
-
onClose={handleClose}
|
|
99
|
-
currentFile={currentFile}
|
|
100
|
-
initialMessage={initialMessage}
|
|
101
|
-
onFirstMessage={handleFirstMessage}
|
|
102
|
-
/>
|
|
103
|
-
</>
|
|
14
|
+
<button
|
|
15
|
+
onClick={onToggle}
|
|
16
|
+
className={`
|
|
17
|
+
group hidden md:flex
|
|
18
|
+
fixed z-40 bottom-5 right-5
|
|
19
|
+
items-center justify-center
|
|
20
|
+
gap-0 hover:gap-2
|
|
21
|
+
p-[11px] rounded-xl
|
|
22
|
+
text-white font-medium text-[13px]
|
|
23
|
+
shadow-md shadow-amber-900/15
|
|
24
|
+
transition-all duration-200 ease-out
|
|
25
|
+
hover:shadow-lg hover:shadow-amber-800/25
|
|
26
|
+
active:scale-95 cursor-pointer overflow-hidden font-display
|
|
27
|
+
${askPanelOpen ? 'opacity-0 pointer-events-none translate-y-2' : 'opacity-100 translate-y-0'}
|
|
28
|
+
`}
|
|
29
|
+
style={{
|
|
30
|
+
background: 'linear-gradient(135deg, #b07c2e 0%, #c8873a 50%, #d4943f 100%)',
|
|
31
|
+
}}
|
|
32
|
+
title="MindOS Agent (⌘/)"
|
|
33
|
+
aria-label="MindOS Agent"
|
|
34
|
+
>
|
|
35
|
+
<Sparkles size={16} className="relative z-10 shrink-0" />
|
|
36
|
+
<span className="
|
|
37
|
+
relative z-10
|
|
38
|
+
max-w-0 group-hover:max-w-[120px]
|
|
39
|
+
opacity-0 group-hover:opacity-100
|
|
40
|
+
transition-all duration-200 ease-out
|
|
41
|
+
whitespace-nowrap overflow-hidden
|
|
42
|
+
">
|
|
43
|
+
MindOS Agent
|
|
44
|
+
</span>
|
|
45
|
+
</button>
|
|
104
46
|
);
|
|
105
47
|
}
|