@geminilight/mindos 0.2.1 → 0.4.0

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 (82) hide show
  1. package/app/app/api/init/route.ts +7 -41
  2. package/app/app/api/mcp/agents/route.ts +72 -0
  3. package/app/app/api/mcp/install/route.ts +95 -0
  4. package/app/app/api/mcp/status/route.ts +47 -0
  5. package/app/app/api/settings/route.ts +3 -0
  6. package/app/app/api/setup/generate-token/route.ts +23 -0
  7. package/app/app/api/setup/route.ts +81 -0
  8. package/app/app/api/skills/route.ts +208 -0
  9. package/app/app/api/sync/route.ts +54 -3
  10. package/app/app/api/update-check/route.ts +52 -0
  11. package/app/app/globals.css +12 -0
  12. package/app/app/layout.tsx +4 -2
  13. package/app/app/login/page.tsx +20 -13
  14. package/app/app/page.tsx +22 -2
  15. package/app/app/setup/page.tsx +9 -0
  16. package/app/app/view/[...path]/ViewPageClient.tsx +47 -21
  17. package/app/app/view/[...path]/loading.tsx +1 -1
  18. package/app/app/view/[...path]/not-found.tsx +101 -0
  19. package/app/components/AskFab.tsx +1 -1
  20. package/app/components/AskModal.tsx +1 -1
  21. package/app/components/Backlinks.tsx +1 -1
  22. package/app/components/Breadcrumb.tsx +13 -3
  23. package/app/components/CsvView.tsx +5 -6
  24. package/app/components/DirView.tsx +42 -21
  25. package/app/components/FindInPage.tsx +211 -0
  26. package/app/components/HomeContent.tsx +97 -44
  27. package/app/components/JsonView.tsx +1 -2
  28. package/app/components/MarkdownEditor.tsx +1 -2
  29. package/app/components/OnboardingView.tsx +6 -7
  30. package/app/components/SettingsModal.tsx +5 -2
  31. package/app/components/SetupWizard.tsx +479 -0
  32. package/app/components/Sidebar.tsx +1 -1
  33. package/app/components/UpdateBanner.tsx +101 -0
  34. package/app/components/renderers/{AgentInspectorRenderer.tsx → agent-inspector/AgentInspectorRenderer.tsx} +13 -11
  35. package/app/components/renderers/agent-inspector/manifest.ts +14 -0
  36. package/app/components/renderers/{BacklinksRenderer.tsx → backlinks/BacklinksRenderer.tsx} +6 -6
  37. package/app/components/renderers/backlinks/manifest.ts +14 -0
  38. package/app/components/renderers/config/manifest.ts +14 -0
  39. package/app/components/renderers/csv/BoardView.tsx +12 -12
  40. package/app/components/renderers/csv/ConfigPanel.tsx +7 -8
  41. package/app/components/renderers/{CsvRenderer.tsx → csv/CsvRenderer.tsx} +8 -9
  42. package/app/components/renderers/csv/GalleryView.tsx +3 -3
  43. package/app/components/renderers/csv/TableView.tsx +4 -5
  44. package/app/components/renderers/csv/manifest.ts +14 -0
  45. package/app/components/renderers/{DiffRenderer.tsx → diff/DiffRenderer.tsx} +10 -9
  46. package/app/components/renderers/diff/manifest.ts +14 -0
  47. package/app/components/renderers/{GraphRenderer.tsx → graph/GraphRenderer.tsx} +4 -5
  48. package/app/components/renderers/graph/manifest.ts +14 -0
  49. package/app/components/renderers/{SummaryRenderer.tsx → summary/SummaryRenderer.tsx} +6 -6
  50. package/app/components/renderers/summary/manifest.ts +14 -0
  51. package/app/components/renderers/{TimelineRenderer.tsx → timeline/TimelineRenderer.tsx} +6 -6
  52. package/app/components/renderers/timeline/manifest.ts +14 -0
  53. package/app/components/renderers/{TodoRenderer.tsx → todo/TodoRenderer.tsx} +2 -2
  54. package/app/components/renderers/todo/manifest.ts +14 -0
  55. package/app/components/renderers/{WorkflowRenderer.tsx → workflow/WorkflowRenderer.tsx} +13 -13
  56. package/app/components/renderers/workflow/manifest.ts +14 -0
  57. package/app/components/settings/McpTab.tsx +549 -0
  58. package/app/components/settings/SyncTab.tsx +139 -50
  59. package/app/components/settings/types.ts +1 -1
  60. package/app/data/pages/home.png +0 -0
  61. package/app/lib/i18n.ts +270 -10
  62. package/app/lib/renderers/index.ts +20 -89
  63. package/app/lib/renderers/registry.ts +4 -1
  64. package/app/lib/settings.ts +15 -1
  65. package/app/lib/template.ts +45 -0
  66. package/app/package.json +1 -0
  67. package/app/types/semver.d.ts +8 -0
  68. package/bin/cli.js +137 -24
  69. package/bin/lib/build.js +53 -18
  70. package/bin/lib/colors.js +3 -1
  71. package/bin/lib/config.js +4 -0
  72. package/bin/lib/constants.js +2 -0
  73. package/bin/lib/debug.js +10 -0
  74. package/bin/lib/startup.js +21 -20
  75. package/bin/lib/stop.js +41 -3
  76. package/bin/lib/sync.js +65 -53
  77. package/bin/lib/update-check.js +94 -0
  78. package/bin/lib/utils.js +2 -2
  79. package/package.json +1 -1
  80. package/scripts/gen-renderer-index.js +57 -0
  81. package/scripts/setup.js +117 -1
  82. /package/app/components/renderers/{ConfigRenderer.tsx → config/ConfigRenderer.tsx} +0 -0
@@ -0,0 +1,14 @@
1
+ import type { RendererDefinition } from '@/lib/renderers/registry';
2
+
3
+ export const manifest: RendererDefinition = {
4
+ id: 'summary',
5
+ name: 'AI Briefing',
6
+ description: 'Streams an AI-generated daily briefing summarizing your most recently modified files — key changes, recurring themes, and suggested next actions.',
7
+ author: 'MindOS',
8
+ icon: '✨',
9
+ tags: ['ai', 'summary', 'briefing', 'daily'],
10
+ builtin: true,
11
+ entryPath: 'DAILY.md',
12
+ match: ({ filePath }) => /\b(SUMMARY|summary|Summary|BRIEFING|briefing|Briefing|DAILY|daily|Daily)\b.*\.md$/i.test(filePath),
13
+ load: () => import('./SummaryRenderer').then(m => ({ default: m.SummaryRenderer })),
14
+ };
@@ -71,7 +71,7 @@ function renderInline(text: string): string {
71
71
  return text
72
72
  .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
73
73
  .replace(/\*(.+?)\*/g, '<em>$1</em>')
74
- .replace(/`(.+?)`/g, '<code style="font-family:\'IBM Plex Mono\',monospace;font-size:0.85em;padding:1px 5px;border-radius:4px;background:var(--muted)">$1</code>')
74
+ .replace(/`(.+?)`/g, '<code class="font-display" style="font-size:0.85em;padding:1px 5px;border-radius:4px;background:var(--muted)">$1</code>')
75
75
  .replace(/\[\[([^\]|]+)(?:\|([^\]]+))?\]\]/g, (_, target, alias) =>
76
76
  `<span style="color:var(--amber);cursor:pointer" title="${target}">${alias ?? target}</span>`)
77
77
  .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" style="color:var(--amber)">$1</a>');
@@ -131,7 +131,7 @@ export function TimelineRenderer({ content }: RendererContext) {
131
131
 
132
132
  if (entries.length === 0) {
133
133
  return (
134
- <div style={{ padding: '3rem 1rem', textAlign: 'center', color: 'var(--muted-foreground)', fontFamily: "'IBM Plex Mono',monospace", fontSize: 13 }}>
134
+ <div className="font-display" style={{ padding: '3rem 1rem', textAlign: 'center', color: 'var(--muted-foreground)', fontSize: 13 }}>
135
135
  No timeline entries found. Add <code style={{ background: 'var(--muted)', padding: '1px 6px', borderRadius: 4 }}>## 2025-01-15</code> headings to create entries.
136
136
  </div>
137
137
  );
@@ -141,7 +141,7 @@ export function TimelineRenderer({ content }: RendererContext) {
141
141
  <div style={{ maxWidth: 720, margin: '0 auto', padding: '1.5rem 0' }}>
142
142
  {/* count pill */}
143
143
  <div style={{ marginBottom: '1.5rem', display: 'flex', alignItems: 'center', gap: 8 }}>
144
- <span style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: 11, color: 'var(--muted-foreground)' }}>
144
+ <span className="font-display" style={{ fontSize: 11, color: 'var(--muted-foreground)' }}>
145
145
  {entries.length} {entries.length === 1 ? 'entry' : 'entries'}
146
146
  </span>
147
147
  </div>
@@ -176,11 +176,11 @@ export function TimelineRenderer({ content }: RendererContext) {
176
176
  }}>
177
177
  {/* header */}
178
178
  <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap', marginBottom: 8 }}>
179
- <span style={{ fontFamily: "'IBM Plex Sans',sans-serif", fontWeight: 600, fontSize: '0.9rem', color: 'var(--foreground)' }}>
179
+ <span style={{ fontWeight: 600, fontSize: '0.9rem', color: 'var(--foreground)' }}>
180
180
  {entry.heading}
181
181
  </span>
182
182
  {entry.date && (
183
- <span style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: '0.7rem', color: 'var(--muted-foreground)', opacity: 0.7, flexShrink: 0 }}>
183
+ <span className="font-display" style={{ fontSize: '0.7rem', color: 'var(--muted-foreground)', opacity: 0.7, flexShrink: 0 }}>
184
184
  {formatDate(entry.date)}
185
185
  </span>
186
186
  )}
@@ -197,7 +197,7 @@ export function TimelineRenderer({ content }: RendererContext) {
197
197
  {entry.tags.map(tag => {
198
198
  const c = tagColor(tag);
199
199
  return (
200
- <span key={tag} style={{ fontSize: '0.68rem', padding: '1px 8px', borderRadius: 999, fontFamily: "'IBM Plex Mono',monospace", background: c.bg, color: c.text }}>
200
+ <span key={tag} className="font-display" style={{ fontSize: '0.68rem', padding: '1px 8px', borderRadius: 999, background: c.bg, color: c.text }}>
201
201
  #{tag}
202
202
  </span>
203
203
  );
@@ -0,0 +1,14 @@
1
+ import type { RendererDefinition } from '@/lib/renderers/registry';
2
+
3
+ export const manifest: RendererDefinition = {
4
+ id: 'timeline',
5
+ name: 'Timeline',
6
+ description: 'Renders changelog and journal files as a vertical timeline. Any markdown with ## date headings (e.g. ## 2025-01-15) becomes a card in the feed.',
7
+ author: 'MindOS',
8
+ icon: '📅',
9
+ tags: ['timeline', 'changelog', 'journal', 'history'],
10
+ builtin: true,
11
+ entryPath: 'CHANGELOG.md',
12
+ match: ({ filePath }) => /\b(CHANGELOG|changelog|TIMELINE|timeline|journal|Journal|diary|Diary)\b.*\.md$/i.test(filePath),
13
+ load: () => import('./TimelineRenderer').then(m => ({ default: m.TimelineRenderer })),
14
+ };
@@ -358,7 +358,7 @@ function SectionCard({
358
358
  >
359
359
  <div className="flex items-center gap-2">
360
360
  <span className={`w-2 h-2 rounded-full shrink-0 ${style.dot}`} />
361
- <span className={`text-xs font-semibold uppercase tracking-wider ${style.label}`} style={{ fontFamily: "'IBM Plex Mono', monospace" }}>
361
+ <span className={`text-xs font-semibold uppercase tracking-wider ${style.label} font-display`}>
362
362
  {name}
363
363
  </span>
364
364
  <span className="text-xs text-muted-foreground">{done}/{total}</span>
@@ -441,7 +441,7 @@ export function TodoRenderer({ content, saveAction }: RendererContext) {
441
441
  <div className="max-w-[900px] mx-auto xl:mr-[220px] px-0 py-2">
442
442
  {/* Summary header */}
443
443
  <div className="mb-6">
444
- <p className="text-xs text-muted-foreground" style={{ fontFamily: "'IBM Plex Mono', monospace" }}>
444
+ <p className="text-xs text-muted-foreground font-display">
445
445
  {totalDone} / {totalItems} completed
446
446
  </p>
447
447
  <div className="mt-1.5 w-48 h-1.5 bg-muted rounded-full overflow-hidden">
@@ -0,0 +1,14 @@
1
+ import type { RendererDefinition } from '@/lib/renderers/registry';
2
+
3
+ export const manifest: RendererDefinition = {
4
+ id: 'todo',
5
+ name: 'TODO Board',
6
+ description: 'Renders TODO.md/TODO.csv as an interactive kanban board grouped by section. Check items off directly — changes are written back to the source file.',
7
+ author: 'MindOS',
8
+ icon: '✅',
9
+ tags: ['productivity', 'tasks', 'markdown'],
10
+ builtin: true,
11
+ entryPath: 'TODO.md',
12
+ match: ({ filePath }) => /\bTODO\b.*\.(md|csv)$/i.test(filePath),
13
+ load: () => import('./TodoRenderer').then(m => ({ default: m.TodoRenderer })),
14
+ };
@@ -75,7 +75,7 @@ function parseWorkflow(content: string): { meta: WorkflowMeta; steps: WorkflowSt
75
75
  function renderInline(text: string): string {
76
76
  return text
77
77
  .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
78
- .replace(/`(.+?)`/g, `<code style="font-family:'IBM Plex Mono',monospace;font-size:.82em;padding:1px 5px;border-radius:4px;background:var(--muted)">$1</code>`)
78
+ .replace(/`(.+?)`/g, `<code class="font-display" style="font-size:.82em;padding:1px 5px;border-radius:4px;background:var(--muted)">$1</code>`)
79
79
  .replace(/\*(.+?)\*/g, '<em>$1</em>');
80
80
  }
81
81
 
@@ -188,7 +188,7 @@ function StepCard({
188
188
  <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '11px 14px' }}>
189
189
  <StatusIcon status={step.status} />
190
190
  <span
191
- style={{ flex: 1, fontFamily: "'IBM Plex Sans',sans-serif", fontWeight: 600, fontSize: '.88rem', color: 'var(--foreground)', cursor: hasBody || hasOutput ? 'pointer' : 'default' }}
191
+ style={{ flex: 1, fontWeight: 600, fontSize: '.88rem', color: 'var(--foreground)', cursor: hasBody || hasOutput ? 'pointer' : 'default' }}
192
192
  onClick={() => (hasBody || hasOutput) && setExpanded(v => !v)}
193
193
  >
194
194
  {step.heading}
@@ -204,7 +204,7 @@ function StepCard({
204
204
  style={{
205
205
  display: 'flex', alignItems: 'center', gap: 4,
206
206
  padding: '3px 10px', borderRadius: 6, fontSize: '0.72rem',
207
- fontFamily: "'IBM Plex Mono',monospace", cursor: canRun ? 'pointer' : 'not-allowed',
207
+ cursor: canRun ? 'pointer' : 'not-allowed',
208
208
  border: 'none', background: canRun ? 'var(--amber)' : 'var(--muted)',
209
209
  color: canRun ? '#131210' : 'var(--muted-foreground)',
210
210
  opacity: canRun ? 1 : 0.5,
@@ -216,7 +216,7 @@ function StepCard({
216
216
  onClick={onSkip}
217
217
  style={{
218
218
  padding: '3px 8px', borderRadius: 6, fontSize: '0.72rem',
219
- fontFamily: "'IBM Plex Mono',monospace", cursor: 'pointer',
219
+ cursor: 'pointer',
220
220
  border: '1px solid var(--border)', background: 'transparent',
221
221
  color: 'var(--muted-foreground)',
222
222
  }}
@@ -226,12 +226,12 @@ function StepCard({
226
226
  </>
227
227
  )}
228
228
  {step.status === 'running' && (
229
- <span style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: '0.7rem', color: 'var(--amber)' }}>executing…</span>
229
+ <span className="font-display" style={{ fontSize: '0.7rem', color: 'var(--amber)' }}>executing…</span>
230
230
  )}
231
231
  {(step.status === 'done' || step.status === 'error') && (
232
232
  <button
233
233
  onClick={() => setExpanded(v => !v)}
234
- style={{ padding: '3px 8px', borderRadius: 6, fontSize: '0.72rem', fontFamily: "'IBM Plex Mono',monospace", cursor: 'pointer', border: '1px solid var(--border)', background: 'transparent', color: 'var(--muted-foreground)' }}
234
+ style={{ padding: '3px 8px', borderRadius: 6, fontSize: '0.72rem', cursor: 'pointer', border: '1px solid var(--border)', background: 'transparent', color: 'var(--muted-foreground)' }}
235
235
  >
236
236
  <ChevronDown size={11} style={{ display: 'inline', transform: expanded ? 'rotate(180deg)' : 'none', transition: 'transform .15s' }} />
237
237
  </button>
@@ -251,10 +251,10 @@ function StepCard({
251
251
  <div style={{ padding: '10px 14px', background: 'var(--background)', position: 'relative' }}>
252
252
  <div style={{ display: 'flex', alignItems: 'center', gap: 5, marginBottom: 6 }}>
253
253
  <Sparkles size={11} style={{ color: 'var(--amber)' }} />
254
- <span style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: '0.68rem', color: 'var(--muted-foreground)', textTransform: 'uppercase', letterSpacing: '.06em' }}>AI Output</span>
254
+ <span className="font-display" style={{ fontSize: '0.68rem', color: 'var(--muted-foreground)', textTransform: 'uppercase', letterSpacing: '.06em' }}>AI Output</span>
255
255
  {step.status === 'running' && <span style={{ width: 5, height: 5, borderRadius: '50%', background: 'var(--amber)', animation: 'pulse 1.2s ease-in-out infinite', marginLeft: 4 }} />}
256
256
  </div>
257
- <div style={{ fontFamily: "'IBM Plex Sans',sans-serif", fontSize: '.82rem', lineHeight: 1.7, color: 'var(--foreground)', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
257
+ <div style={{ fontSize: '.82rem', lineHeight: 1.7, color: 'var(--foreground)', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
258
258
  {step.output}
259
259
  </div>
260
260
  </div>
@@ -326,7 +326,7 @@ export function WorkflowRenderer({ filePath, content }: RendererContext) {
326
326
 
327
327
  if (steps.length === 0) {
328
328
  return (
329
- <div style={{ padding: '3rem 1rem', textAlign: 'center', color: 'var(--muted-foreground)', fontFamily: "'IBM Plex Mono',monospace", fontSize: 12 }}>
329
+ <div className="font-display" style={{ padding: '3rem 1rem', textAlign: 'center', color: 'var(--muted-foreground)', fontSize: 12 }}>
330
330
  No steps found. Add <code style={{ background: 'var(--muted)', padding: '1px 5px', borderRadius: 4 }}>## Step N: …</code> headings to define workflow steps.
331
331
  </div>
332
332
  );
@@ -337,7 +337,7 @@ export function WorkflowRenderer({ filePath, content }: RendererContext) {
337
337
  {/* header */}
338
338
  <div style={{ marginBottom: '1.2rem' }}>
339
339
  {parsed.meta.description && (
340
- <p style={{ fontFamily: "'IBM Plex Sans',sans-serif", fontSize: '.82rem', color: 'var(--muted-foreground)', lineHeight: 1.6, marginBottom: 12 }}>
340
+ <p style={{ fontSize: '.82rem', color: 'var(--muted-foreground)', lineHeight: 1.6, marginBottom: 12 }}>
341
341
  {parsed.meta.description}
342
342
  </p>
343
343
  )}
@@ -348,7 +348,7 @@ export function WorkflowRenderer({ filePath, content }: RendererContext) {
348
348
  <div style={{ flex: 1, minWidth: 120, height: 4, borderRadius: 999, background: 'var(--border)', overflow: 'hidden' }}>
349
349
  <div style={{ height: '100%', width: `${progress}%`, background: 'var(--amber)', borderRadius: 999, transition: 'width .3s' }} />
350
350
  </div>
351
- <span style={{ fontFamily: "'IBM Plex Mono',monospace", fontSize: '0.7rem', color: 'var(--muted-foreground)', flexShrink: 0 }}>
351
+ <span className="font-display" style={{ fontSize: '0.7rem', color: 'var(--muted-foreground)', flexShrink: 0 }}>
352
352
  {doneCount}/{steps.length} done
353
353
  </span>
354
354
 
@@ -360,7 +360,7 @@ export function WorkflowRenderer({ filePath, content }: RendererContext) {
360
360
  style={{
361
361
  display: 'flex', alignItems: 'center', gap: 5,
362
362
  padding: '4px 12px', borderRadius: 7, fontSize: '0.75rem',
363
- fontFamily: "'IBM Plex Mono',monospace", cursor: running ? 'not-allowed' : 'pointer',
363
+ cursor: running ? 'not-allowed' : 'pointer',
364
364
  border: 'none', background: running ? 'var(--muted)' : 'var(--amber)',
365
365
  color: running ? 'var(--muted-foreground)' : '#131210',
366
366
  opacity: running ? 0.7 : 1,
@@ -374,7 +374,7 @@ export function WorkflowRenderer({ filePath, content }: RendererContext) {
374
374
  {/* reset */}
375
375
  <button
376
376
  onClick={reset}
377
- style={{ padding: '4px 10px', borderRadius: 7, fontSize: '0.75rem', fontFamily: "'IBM Plex Mono',monospace", cursor: 'pointer', border: '1px solid var(--border)', background: 'transparent', color: 'var(--muted-foreground)', display: 'flex', alignItems: 'center', gap: 4 }}
377
+ style={{ padding: '4px 10px', borderRadius: 7, fontSize: '0.75rem', cursor: 'pointer', border: '1px solid var(--border)', background: 'transparent', color: 'var(--muted-foreground)', display: 'flex', alignItems: 'center', gap: 4 }}
378
378
  >
379
379
  <RotateCcw size={11} /> Reset
380
380
  </button>
@@ -0,0 +1,14 @@
1
+ import type { RendererDefinition } from '@/lib/renderers/registry';
2
+
3
+ export const manifest: RendererDefinition = {
4
+ id: 'workflow',
5
+ name: 'Workflow Runner',
6
+ description: 'Parses step-by-step workflow markdown into an interactive runner. Execute steps sequentially with AI assistance.',
7
+ author: 'MindOS',
8
+ icon: '⚡',
9
+ tags: ['workflow', 'automation', 'steps', 'ai'],
10
+ builtin: true,
11
+ entryPath: 'Workflow.md',
12
+ match: ({ filePath }) => /\b(Workflow|workflow|WORKFLOW)\b.*\.md$/i.test(filePath),
13
+ load: () => import('./WorkflowRenderer').then(m => ({ default: m.WorkflowRenderer })),
14
+ };