@geminilight/mindos 0.5.63 → 0.5.65

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.
Files changed (104) hide show
  1. package/README.md +4 -0
  2. package/README_zh.md +4 -0
  3. package/app/app/api/ask/route.ts +12 -0
  4. package/app/app/api/changes/route.ts +7 -1
  5. package/app/app/api/file/route.ts +9 -0
  6. package/app/app/api/mcp/agents/route.ts +27 -1
  7. package/app/app/api/mcp/install-skill/route.ts +9 -24
  8. package/app/app/api/skills/route.ts +18 -2
  9. package/app/app/api/tree-version/route.ts +8 -0
  10. package/app/app/layout.tsx +1 -0
  11. package/app/app/page.tsx +1 -2
  12. package/app/app/view/[...path]/ViewPageClient.tsx +0 -1
  13. package/app/components/ActivityBar.tsx +2 -2
  14. package/app/components/Backlinks.tsx +5 -5
  15. package/app/components/CreateSpaceModal.tsx +3 -2
  16. package/app/components/DirPicker.tsx +1 -1
  17. package/app/components/DirView.tsx +2 -3
  18. package/app/components/EditorWrapper.tsx +3 -3
  19. package/app/components/FileTree.tsx +25 -10
  20. package/app/components/GuideCard.tsx +4 -4
  21. package/app/components/HomeContent.tsx +44 -14
  22. package/app/components/MarkdownView.tsx +2 -2
  23. package/app/components/OnboardingView.tsx +1 -1
  24. package/app/components/Panel.tsx +1 -1
  25. package/app/components/RightAgentDetailPanel.tsx +2 -1
  26. package/app/components/RightAskPanel.tsx +1 -1
  27. package/app/components/SearchModal.tsx +10 -2
  28. package/app/components/SidebarLayout.tsx +36 -10
  29. package/app/components/ThemeToggle.tsx +1 -1
  30. package/app/components/agents/AgentDetailContent.tsx +454 -59
  31. package/app/components/agents/AgentsContentPage.tsx +89 -20
  32. package/app/components/agents/AgentsMcpSection.tsx +513 -85
  33. package/app/components/agents/AgentsOverviewSection.tsx +418 -59
  34. package/app/components/agents/AgentsPrimitives.tsx +335 -0
  35. package/app/components/agents/AgentsSkillsSection.tsx +746 -105
  36. package/app/components/agents/SkillDetailPopover.tsx +416 -0
  37. package/app/components/agents/agents-content-model.ts +308 -10
  38. package/app/components/ask/AskContent.tsx +34 -5
  39. package/app/components/ask/FileChip.tsx +1 -0
  40. package/app/components/ask/MentionPopover.tsx +13 -1
  41. package/app/components/ask/MessageList.tsx +5 -7
  42. package/app/components/ask/ToolCallBlock.tsx +4 -4
  43. package/app/components/changes/ChangesBanner.tsx +89 -13
  44. package/app/components/changes/ChangesContentPage.tsx +134 -51
  45. package/app/components/echo/EchoHero.tsx +10 -24
  46. package/app/components/echo/EchoInsightCollapsible.tsx +52 -43
  47. package/app/components/echo/EchoPageSections.tsx +13 -9
  48. package/app/components/echo/EchoSegmentNav.tsx +14 -11
  49. package/app/components/echo/EchoSegmentPageClient.tsx +64 -43
  50. package/app/components/explore/ExploreContent.tsx +3 -7
  51. package/app/components/explore/UseCaseCard.tsx +4 -15
  52. package/app/components/panels/AgentsPanel.tsx +22 -128
  53. package/app/components/panels/AgentsPanelAgentDetail.tsx +7 -6
  54. package/app/components/panels/AgentsPanelAgentGroups.tsx +8 -13
  55. package/app/components/panels/AgentsPanelAgentListRow.tsx +39 -16
  56. package/app/components/panels/AgentsPanelHubNav.tsx +12 -12
  57. package/app/components/panels/EchoPanel.tsx +8 -10
  58. package/app/components/panels/PanelNavRow.tsx +9 -2
  59. package/app/components/panels/PluginsPanel.tsx +5 -5
  60. package/app/components/renderers/agent-inspector/AgentInspectorRenderer.tsx +30 -8
  61. package/app/components/renderers/agent-inspector/manifest.ts +5 -3
  62. package/app/components/renderers/config/manifest.ts +1 -0
  63. package/app/components/renderers/csv/manifest.ts +1 -0
  64. package/app/components/renderers/todo/manifest.ts +1 -0
  65. package/app/components/settings/AiTab.tsx +3 -3
  66. package/app/components/settings/AppearanceTab.tsx +2 -2
  67. package/app/components/settings/KnowledgeTab.tsx +3 -3
  68. package/app/components/settings/McpAgentInstall.tsx +3 -6
  69. package/app/components/settings/McpSkillCreateForm.tsx +2 -3
  70. package/app/components/settings/McpSkillRow.tsx +2 -3
  71. package/app/components/settings/McpSkillsSection.tsx +2 -2
  72. package/app/components/settings/McpTab.tsx +12 -13
  73. package/app/components/settings/MonitoringTab.tsx +13 -13
  74. package/app/components/settings/PluginsTab.tsx +6 -5
  75. package/app/components/settings/Primitives.tsx +3 -4
  76. package/app/components/settings/SettingsContent.tsx +3 -3
  77. package/app/components/settings/SyncTab.tsx +11 -17
  78. package/app/components/settings/UpdateTab.tsx +18 -21
  79. package/app/components/settings/types.ts +14 -0
  80. package/app/components/setup/StepKB.tsx +1 -1
  81. package/app/hooks/useMcpData.tsx +7 -4
  82. package/app/hooks/useMention.ts +25 -8
  83. package/app/lib/agent/log.ts +15 -18
  84. package/app/lib/agent/stream-consumer.ts +3 -0
  85. package/app/lib/agent/to-agent-messages.ts +6 -4
  86. package/app/lib/core/agent-audit-log.ts +280 -0
  87. package/app/lib/core/content-changes.ts +148 -8
  88. package/app/lib/core/index.ts +11 -0
  89. package/app/lib/fs.ts +16 -1
  90. package/app/lib/i18n-en.ts +317 -36
  91. package/app/lib/i18n-zh.ts +316 -35
  92. package/app/lib/mcp-agents.ts +273 -2
  93. package/app/lib/renderers/index.ts +1 -2
  94. package/app/lib/renderers/registry.ts +10 -0
  95. package/app/lib/types.ts +2 -0
  96. package/app/next-env.d.ts +1 -1
  97. package/bin/lib/mcp-agents.js +38 -13
  98. package/package.json +1 -1
  99. package/scripts/migrate-agent-audit-log.js +170 -0
  100. package/scripts/migrate-agent-diff.js +146 -0
  101. package/scripts/setup.js +12 -17
  102. package/skills/plugin-core-builtin-migration/SKILL.md +178 -0
  103. package/app/components/renderers/diff/DiffRenderer.tsx +0 -311
  104. package/app/components/renderers/diff/manifest.ts +0 -14
@@ -5,8 +5,7 @@ import { FileText, Table, Clock, Sparkles, ArrowRight, FilePlus, Search, Chevron
5
5
  import { useState, useEffect, useMemo } from 'react';
6
6
  import { useLocale } from '@/lib/LocaleContext';
7
7
  import { encodePath, relativeTime, extractEmoji, stripEmoji } from '@/lib/utils';
8
- import { getAllRenderers } from '@/lib/renderers/registry';
9
- import '@/lib/renderers/index'; // registers all renderers
8
+ import { getAllRenderers, getPluginRenderers } from '@/lib/renderers/registry';
10
9
  import OnboardingView from './OnboardingView';
11
10
  import GuideCard from './GuideCard';
12
11
  import CreateSpaceModal from './CreateSpaceModal';
@@ -116,23 +115,20 @@ export default function HomeContent({ recent, existingFiles, spaces, dirPaths }:
116
115
  }, [suggestions.length]);
117
116
 
118
117
  const existingSet = new Set(existingFiles ?? []);
118
+ const spaceList = spaces ?? [];
119
+ const { groups, rootFiles } = useMemo(() => groupBySpace(recent, spaceList), [recent, spaceList]);
119
120
 
120
- // Empty knowledge base → show onboarding
121
121
  if (recent.length === 0) {
122
122
  return <OnboardingView />;
123
123
  }
124
124
 
125
125
  const formatTime = (mtime: number) => relativeTime(mtime, t.home.relativeTime);
126
126
 
127
- // Only show renderers that are available (have entryPath + file exists) as quick-access chips
128
- const availablePlugins = getAllRenderers().filter(r => r.entryPath && existingSet.has(r.entryPath));
127
+ const availablePlugins = getPluginRenderers().filter(r => r.entryPath && existingSet.has(r.entryPath));
128
+ const builtinFeatures = getAllRenderers().filter((r) => r.appBuiltinFeature && r.id !== 'csv');
129
129
 
130
130
  const lastFile = recent[0];
131
131
 
132
- // Group recent files by Space
133
- const spaceList = spaces ?? [];
134
- const { groups, rootFiles } = useMemo(() => groupBySpace(recent, spaceList), [recent, spaceList]);
135
-
136
132
  return (
137
133
  <div className="content-width px-4 md:px-6 py-8 md:py-12">
138
134
  <GuideCard
@@ -159,7 +155,7 @@ export default function HomeContent({ recent, existingFiles, spaces, dirPaths }:
159
155
  onClick={triggerAsk}
160
156
  title="⌘/"
161
157
  data-walkthrough="ask-button"
162
- 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"
158
+ 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-[var(--amber)]/50 hover:bg-[var(--amber)]/8"
163
159
  >
164
160
  <Sparkles size={15} className="shrink-0 text-[var(--amber)]" />
165
161
  <span className="text-sm flex-1 text-left text-foreground">
@@ -236,7 +232,7 @@ export default function HomeContent({ recent, existingFiles, spaces, dirPaths }:
236
232
  className={`flex items-start gap-3 px-3.5 py-3 rounded-xl border transition-all duration-150 hover:translate-x-0.5 ${
237
233
  isEmpty
238
234
  ? 'border-dashed border-border/50 opacity-50 hover:opacity-70'
239
- : 'border-border hover:border-amber-500/30 hover:bg-muted/40'
235
+ : 'border-border hover:border-[var(--amber)]/30 hover:bg-muted/40'
240
236
  }`}
241
237
  >
242
238
  {emoji ? (
@@ -275,7 +271,41 @@ export default function HomeContent({ recent, existingFiles, spaces, dirPaths }:
275
271
  <CreateSpaceModal t={t} dirPaths={dirPaths ?? []} />
276
272
  </section>
277
273
 
278
- {/* ── Section 2: Extensions ── */}
274
+ {/* ── Section 2: Built-in capabilities ── */}
275
+ {builtinFeatures.length > 0 && (
276
+ <section className="mb-8">
277
+ <SectionTitle icon={<Puzzle size={13} />} count={builtinFeatures.length}>
278
+ {t.home.builtinFeatures}
279
+ </SectionTitle>
280
+ <div className="flex flex-wrap gap-2">
281
+ {builtinFeatures.map((r) => {
282
+ const active = !!r.entryPath && existingSet.has(r.entryPath);
283
+ if (active && r.entryPath) {
284
+ return (
285
+ <Link key={r.id} href={`/view/${encodePath(r.entryPath)}`}>
286
+ <span className="inline-flex items-center gap-2 px-3 py-2 rounded-lg border border-border text-xs transition-all duration-150 hover:border-[var(--amber)]/30 hover:bg-muted/60">
287
+ <span className="text-sm leading-none" suppressHydrationWarning>{r.icon}</span>
288
+ <span className="font-medium text-foreground">{r.name}</span>
289
+ </span>
290
+ </Link>
291
+ );
292
+ }
293
+ return (
294
+ <span
295
+ key={r.id}
296
+ className="inline-flex items-center gap-2 px-3 py-2 rounded-lg border border-dashed border-border text-xs text-muted-foreground opacity-70"
297
+ title={r.entryPath ? t.home.createToActivate.replace('{file}', r.entryPath) : t.home.builtinInactive}
298
+ >
299
+ <span className="text-sm leading-none" suppressHydrationWarning>{r.icon}</span>
300
+ <span className="font-medium">{r.name}</span>
301
+ </span>
302
+ );
303
+ })}
304
+ </div>
305
+ </section>
306
+ )}
307
+
308
+ {/* ── Section 3: Extensions ── */}
279
309
  {availablePlugins.length > 0 && (
280
310
  <section className="mb-8">
281
311
  <SectionTitle
@@ -300,7 +330,7 @@ export default function HomeContent({ recent, existingFiles, spaces, dirPaths }:
300
330
  <Link
301
331
  key={r.id}
302
332
  href={`/view/${encodePath(r.entryPath!)}`}
303
- className="inline-flex items-center gap-2 px-3 py-2 rounded-lg border border-border text-xs transition-all duration-150 hover:border-amber-500/30 hover:bg-muted/60"
333
+ className="inline-flex items-center gap-2 px-3 py-2 rounded-lg border border-border text-xs transition-all duration-150 hover:border-[var(--amber)]/30 hover:bg-muted/60"
304
334
  >
305
335
  <span className="text-sm leading-none" suppressHydrationWarning>{r.icon}</span>
306
336
  <span className="font-medium text-foreground">{r.name}</span>
@@ -310,7 +340,7 @@ export default function HomeContent({ recent, existingFiles, spaces, dirPaths }:
310
340
  </section>
311
341
  )}
312
342
 
313
- {/* ── Section 3: Recently Edited ── */}
343
+ {/* ── Section 4: Recently Edited ── */}
314
344
  {recent.length > 0 && (
315
345
  <section className="mb-12">
316
346
  <SectionTitle icon={<Clock size={13} />} count={recent.length}>{t.home.recentlyEdited}</SectionTitle>
@@ -31,8 +31,8 @@ function CopyButton({ code }: { code: string }) {
31
31
  className="
32
32
  absolute top-2.5 right-2.5
33
33
  p-1.5 rounded-md
34
- bg-zinc-700 hover:bg-zinc-600
35
- text-zinc-400 hover:text-zinc-200
34
+ bg-muted hover:bg-accent
35
+ text-muted-foreground hover:text-foreground
36
36
  transition-colors duration-100
37
37
  opacity-0 group-hover:opacity-100
38
38
  "
@@ -100,7 +100,7 @@ export default function OnboardingView() {
100
100
  key={tpl.id}
101
101
  disabled={isDisabled}
102
102
  onClick={() => handleSelect(tpl.id)}
103
- className="group relative flex flex-col items-start gap-3 p-5 rounded-xl border border-border bg-card text-left transition-all duration-150 hover:border-amber-500/50 hover:bg-amber-500/5 disabled:opacity-60 disabled:cursor-not-allowed"
103
+ className="group relative flex flex-col items-start gap-3 p-5 rounded-xl border border-border bg-card text-left transition-all duration-150 hover:border-[var(--amber)]/50 hover:bg-[var(--amber)]/5 disabled:opacity-60 disabled:cursor-not-allowed"
104
104
  >
105
105
  {/* Icon + title */}
106
106
  <div className="flex items-center gap-2.5 w-full">
@@ -149,7 +149,7 @@ export default function Panel({
149
149
  className="absolute top-0 -right-[3px] w-[6px] h-full cursor-col-resize z-40 group hidden md:block"
150
150
  onMouseDown={handleMouseDown}
151
151
  >
152
- <div className="absolute right-[2px] top-0 w-[2px] h-full opacity-0 group-hover:opacity-100 bg-amber-500/60 transition-opacity" />
152
+ <div className="absolute right-[2px] top-0 w-[2px] h-full opacity-0 group-hover:opacity-100 bg-[var(--amber)]/60 transition-opacity" />
153
153
  </div>
154
154
  )}
155
155
  </aside>
@@ -70,6 +70,7 @@ export default function RightAgentDetailPanel({
70
70
  connected: p.connected,
71
71
  installing: p.installing,
72
72
  install: p.install,
73
+ installFailed: p.installFailed,
73
74
  copyConfig: p.copyConfig,
74
75
  copied: p.copied,
75
76
  transportLocal: p.transportLocal,
@@ -95,7 +96,7 @@ export default function RightAgentDetailPanel({
95
96
  aria-hidden={!open || !resolved}
96
97
  >
97
98
  {resolved && (
98
- <div className="flex flex-col flex-1 min-h-0 overflow-hidden">
99
+ <div className="flex flex-col flex-1 min-h-0 overflow-y-auto">
99
100
  <AgentsPanelAgentDetail
100
101
  agent={resolved.agent}
101
102
  agentStatus={resolved.status}
@@ -81,7 +81,7 @@ export default function RightAskPanel({
81
81
  className="absolute top-0 -left-[3px] w-[6px] h-full cursor-col-resize z-40 group hidden md:block"
82
82
  onMouseDown={handleMouseDown}
83
83
  >
84
- <div className="absolute left-[2px] top-0 w-[2px] h-full opacity-0 group-hover:opacity-100 bg-amber-500/60 transition-opacity" />
84
+ <div className="absolute left-[2px] top-0 w-[2px] h-full opacity-0 group-hover:opacity-100 bg-[var(--amber)]/60 transition-opacity" />
85
85
  </div>
86
86
  </aside>
87
87
  );
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useState, useEffect, useCallback, useRef } from 'react';
3
+ import { useState, useEffect, useCallback, useRef, useLayoutEffect } from 'react';
4
4
  import { useRouter } from 'next/navigation';
5
5
  import { Search, X, FileText, Table } from 'lucide-react';
6
6
  import { SearchResult } from '@/lib/types';
@@ -32,6 +32,7 @@ export default function SearchModal({ open, onClose }: SearchModalProps) {
32
32
  const [loading, setLoading] = useState(false);
33
33
  const [selectedIndex, setSelectedIndex] = useState(0);
34
34
  const inputRef = useRef<HTMLInputElement>(null);
35
+ const resultsRef = useRef<HTMLDivElement>(null);
35
36
  const router = useRouter();
36
37
  const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
37
38
  const { t } = useLocale();
@@ -99,6 +100,13 @@ export default function SearchModal({ open, onClose }: SearchModalProps) {
99
100
  return () => window.removeEventListener('keydown', handler);
100
101
  }, [open, onClose, results, selectedIndex, navigate]);
101
102
 
103
+ useLayoutEffect(() => {
104
+ const container = resultsRef.current;
105
+ if (!container) return;
106
+ const selected = container.children[selectedIndex] as HTMLElement | undefined;
107
+ selected?.scrollIntoView({ block: 'nearest' });
108
+ }, [selectedIndex]);
109
+
102
110
  if (!open) return null;
103
111
 
104
112
  return (
@@ -134,7 +142,7 @@ export default function SearchModal({ open, onClose }: SearchModalProps) {
134
142
  </div>
135
143
 
136
144
  {/* Results */}
137
- <div className="max-h-[50vh] md:max-h-80 overflow-y-auto flex-1">
145
+ <div ref={resultsRef} className="max-h-[50vh] md:max-h-80 overflow-y-auto flex-1">
138
146
  {results.length === 0 && query && !loading && (
139
147
  <div className="px-4 py-8 text-center text-sm text-muted-foreground">{t.search.noResults}</div>
140
148
  )}
@@ -31,6 +31,7 @@ import { FileNode } from '@/lib/types';
31
31
  import { useLocale } from '@/lib/LocaleContext';
32
32
  import { WalkthroughProvider } from './walkthrough';
33
33
  import McpProvider from '@/hooks/useMcpData';
34
+ import '@/lib/renderers/index'; // client-side renderer registration source of truth
34
35
  import { useLeftPanel } from '@/hooks/useLeftPanel';
35
36
  import { useAskPanel } from '@/hooks/useAskPanel';
36
37
  import type { Tab } from './settings/types';
@@ -136,18 +137,43 @@ export default function SidebarLayout({ fileTree, children }: SidebarLayoutProps
136
137
 
137
138
  const agentDockOpen = agentDetailKey !== null && lp.activePanel === 'agents';
138
139
 
139
- // Refresh file tree periodically
140
+ // Refresh file tree when server-side tree version changes.
141
+ // Polls a lightweight version counter every 3s — only calls router.refresh()
142
+ // (which rebuilds the full tree) when the version actually changes.
140
143
  useEffect(() => {
144
+ let lastVersion = -1;
145
+ let stopped = false;
146
+
147
+ const checkVersion = async () => {
148
+ if (stopped || document.visibilityState === 'hidden') return;
149
+ try {
150
+ const res = await fetch('/api/tree-version');
151
+ if (!res.ok) return;
152
+ const { v } = (await res.json()) as { v: number };
153
+ if (lastVersion === -1) {
154
+ lastVersion = v;
155
+ return;
156
+ }
157
+ if (v !== lastVersion) {
158
+ lastVersion = v;
159
+ router.refresh();
160
+ window.dispatchEvent(new Event('mindos:files-changed'));
161
+ }
162
+ } catch (err) { console.debug('[tree-version] poll failed', err); }
163
+ };
164
+
141
165
  const onVisible = () => {
142
- if (document.visibilityState === 'visible') router.refresh();
166
+ if (document.visibilityState === 'visible') void checkVersion();
143
167
  };
168
+
169
+ void checkVersion();
170
+ const interval = setInterval(() => void checkVersion(), 3_000);
144
171
  document.addEventListener('visibilitychange', onVisible);
145
- const interval = setInterval(() => {
146
- if (document.visibilityState === 'visible') router.refresh();
147
- }, 30_000);
172
+
148
173
  return () => {
149
- document.removeEventListener('visibilitychange', onVisible);
174
+ stopped = true;
150
175
  clearInterval(interval);
176
+ document.removeEventListener('visibilitychange', onVisible);
151
177
  };
152
178
  }, [router]);
153
179
 
@@ -223,8 +249,7 @@ export default function SidebarLayout({ fileTree, children }: SidebarLayoutProps
223
249
  {/* Skip link */}
224
250
  <a
225
251
  href="#main-content"
226
- className="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-[60] focus:px-4 focus:py-2 focus:rounded-lg focus:text-sm focus:font-medium focus:font-display"
227
- style={{ background: 'var(--amber)', color: 'var(--amber-foreground)' }}
252
+ className="sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-[60] focus:px-4 focus:py-2 focus:rounded-lg focus:text-sm focus:font-medium focus:font-display bg-[var(--amber)] text-[var(--amber-foreground)]"
228
253
  >
229
254
  Skip to main content
230
255
  </a>
@@ -234,7 +259,9 @@ export default function SidebarLayout({ fileTree, children }: SidebarLayoutProps
234
259
  activePanel={railActivePanel}
235
260
  onPanelChange={lp.setActivePanel}
236
261
  onAgentsClick={() => {
237
- lp.setActivePanel((current) => (current === 'agents' ? null : 'agents'));
262
+ const wasActive = lp.activePanel === 'agents';
263
+ lp.setActivePanel(wasActive ? null : 'agents');
264
+ if (!wasActive) router.push('/agents');
238
265
  setAgentDetailKey(null);
239
266
  }}
240
267
  syncStatus={syncStatus}
@@ -272,7 +299,6 @@ export default function SidebarLayout({ fileTree, children }: SidebarLayoutProps
272
299
  maximized={lp.panelMaximized}
273
300
  onMaximize={lp.handlePanelMaximize}
274
301
  selectedAgentKey={agentDockOpen ? agentDetailKey : null}
275
- onOpenAgentDetail={setAgentDetailKey}
276
302
  />
277
303
  </div>
278
304
  <div className={`flex flex-col h-full ${lp.activePanel === 'discover' ? '' : 'hidden'}`}>
@@ -27,7 +27,7 @@ export default function ThemeToggle() {
27
27
  return (
28
28
  <button
29
29
  onClick={toggle}
30
- className="p-1.5 rounded-lg hover:bg-zinc-800 text-zinc-500 hover:text-zinc-300 transition-colors"
30
+ className="p-1.5 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-colors"
31
31
  title={dark ? 'Switch to light mode' : 'Switch to dark mode'}
32
32
  >
33
33
  {dark ? <Sun size={15} /> : <Moon size={15} />}