@geminilight/mindos 0.3.0 → 0.5.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.
- package/app/app/api/mcp/agents/route.ts +72 -0
- package/app/app/api/mcp/install/route.ts +95 -0
- package/app/app/api/mcp/status/route.ts +47 -0
- package/app/app/api/setup/check-port/route.ts +41 -0
- package/app/app/api/skills/route.ts +208 -0
- package/app/app/api/sync/route.ts +54 -3
- package/app/app/api/update-check/route.ts +52 -0
- package/app/app/globals.css +12 -0
- package/app/app/layout.tsx +4 -2
- package/app/app/login/page.tsx +20 -13
- package/app/app/page.tsx +19 -2
- package/app/app/setup/page.tsx +2 -0
- package/app/app/view/[...path]/ViewPageClient.tsx +47 -21
- package/app/app/view/[...path]/loading.tsx +1 -1
- package/app/app/view/[...path]/not-found.tsx +101 -0
- package/app/components/AskFab.tsx +1 -1
- package/app/components/AskModal.tsx +1 -1
- package/app/components/Backlinks.tsx +1 -1
- package/app/components/Breadcrumb.tsx +13 -3
- package/app/components/CsvView.tsx +5 -6
- package/app/components/DirView.tsx +42 -21
- package/app/components/FindInPage.tsx +211 -0
- package/app/components/HomeContent.tsx +97 -44
- package/app/components/JsonView.tsx +1 -2
- package/app/components/MarkdownEditor.tsx +1 -2
- package/app/components/OnboardingView.tsx +6 -7
- package/app/components/SettingsModal.tsx +5 -2
- package/app/components/SetupWizard.tsx +499 -172
- package/app/components/Sidebar.tsx +1 -1
- package/app/components/UpdateBanner.tsx +101 -0
- package/app/components/renderers/{AgentInspectorRenderer.tsx → agent-inspector/AgentInspectorRenderer.tsx} +13 -11
- package/app/components/renderers/agent-inspector/manifest.ts +14 -0
- package/app/components/renderers/{BacklinksRenderer.tsx → backlinks/BacklinksRenderer.tsx} +6 -6
- package/app/components/renderers/backlinks/manifest.ts +14 -0
- package/app/components/renderers/config/manifest.ts +14 -0
- package/app/components/renderers/csv/BoardView.tsx +12 -12
- package/app/components/renderers/csv/ConfigPanel.tsx +7 -8
- package/app/components/renderers/{CsvRenderer.tsx → csv/CsvRenderer.tsx} +8 -9
- package/app/components/renderers/csv/GalleryView.tsx +3 -3
- package/app/components/renderers/csv/TableView.tsx +4 -5
- package/app/components/renderers/csv/manifest.ts +14 -0
- package/app/components/renderers/{DiffRenderer.tsx → diff/DiffRenderer.tsx} +10 -9
- package/app/components/renderers/diff/manifest.ts +14 -0
- package/app/components/renderers/{GraphRenderer.tsx → graph/GraphRenderer.tsx} +4 -5
- package/app/components/renderers/graph/manifest.ts +14 -0
- package/app/components/renderers/{SummaryRenderer.tsx → summary/SummaryRenderer.tsx} +6 -6
- package/app/components/renderers/summary/manifest.ts +14 -0
- package/app/components/renderers/{TimelineRenderer.tsx → timeline/TimelineRenderer.tsx} +6 -6
- package/app/components/renderers/timeline/manifest.ts +14 -0
- package/app/components/renderers/{TodoRenderer.tsx → todo/TodoRenderer.tsx} +2 -2
- package/app/components/renderers/todo/manifest.ts +14 -0
- package/app/components/renderers/{WorkflowRenderer.tsx → workflow/WorkflowRenderer.tsx} +13 -13
- package/app/components/renderers/workflow/manifest.ts +14 -0
- package/app/components/settings/McpTab.tsx +549 -0
- package/app/components/settings/SyncTab.tsx +139 -50
- package/app/components/settings/types.ts +1 -1
- package/app/data/pages/home.png +0 -0
- package/app/lib/i18n.ts +226 -19
- package/app/lib/renderers/index.ts +20 -89
- package/app/lib/renderers/registry.ts +4 -1
- package/app/lib/settings.ts +3 -0
- package/app/package.json +1 -0
- package/app/types/semver.d.ts +8 -0
- package/bin/cli.js +137 -24
- package/bin/lib/build.js +53 -18
- package/bin/lib/colors.js +3 -1
- package/bin/lib/config.js +4 -0
- package/bin/lib/constants.js +2 -0
- package/bin/lib/debug.js +10 -0
- package/bin/lib/mcp-install.js +4 -1
- package/bin/lib/port.js +8 -2
- package/bin/lib/startup.js +21 -20
- package/bin/lib/stop.js +41 -3
- package/bin/lib/sync.js +65 -53
- package/bin/lib/update-check.js +94 -0
- package/bin/lib/utils.js +2 -2
- package/package.json +1 -1
- package/scripts/gen-renderer-index.js +57 -0
- package/scripts/setup.js +205 -10
- /package/app/components/renderers/{ConfigRenderer.tsx → config/ConfigRenderer.tsx} +0 -0
|
@@ -136,9 +136,8 @@ const WikiNode = memo(function WikiNode({ data }: NodeProps) {
|
|
|
136
136
|
<div
|
|
137
137
|
onClick={handleClick}
|
|
138
138
|
title={id as string}
|
|
139
|
-
className="group"
|
|
139
|
+
className="group font-display"
|
|
140
140
|
style={{
|
|
141
|
-
fontFamily: "'IBM Plex Mono', monospace",
|
|
142
141
|
fontSize: 10 * scale,
|
|
143
142
|
padding: `${4 * scale}px ${12 * scale}px`,
|
|
144
143
|
borderRadius: 999, // Pill shape
|
|
@@ -326,7 +325,7 @@ export function GraphRenderer({ filePath }: RendererContext) {
|
|
|
326
325
|
justifyContent: 'center',
|
|
327
326
|
}}
|
|
328
327
|
>
|
|
329
|
-
<span style={{ color: 'var(--muted-foreground)',
|
|
328
|
+
<span className="font-display" style={{ color: 'var(--muted-foreground)', fontSize: 12 }}>
|
|
330
329
|
{loading ? 'Building graph…' : 'Loading…'}
|
|
331
330
|
</span>
|
|
332
331
|
</div>
|
|
@@ -346,8 +345,8 @@ export function GraphRenderer({ filePath }: RendererContext) {
|
|
|
346
345
|
}}
|
|
347
346
|
>
|
|
348
347
|
<span
|
|
348
|
+
className="font-display"
|
|
349
349
|
style={{
|
|
350
|
-
fontFamily: "'IBM Plex Mono', monospace",
|
|
351
350
|
fontSize: 11,
|
|
352
351
|
color: 'var(--muted-foreground)',
|
|
353
352
|
}}
|
|
@@ -368,11 +367,11 @@ export function GraphRenderer({ filePath }: RendererContext) {
|
|
|
368
367
|
<button
|
|
369
368
|
key={btn.id}
|
|
370
369
|
onClick={() => setScope(btn.id)}
|
|
370
|
+
className="font-display"
|
|
371
371
|
style={{
|
|
372
372
|
padding: '3px 12px',
|
|
373
373
|
borderRadius: 5,
|
|
374
374
|
fontSize: 11,
|
|
375
|
-
fontFamily: "'IBM Plex Mono', monospace",
|
|
376
375
|
cursor: 'pointer',
|
|
377
376
|
border: 'none',
|
|
378
377
|
outline: 'none',
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RendererDefinition } from '@/lib/renderers/registry';
|
|
2
|
+
|
|
3
|
+
export const manifest: RendererDefinition = {
|
|
4
|
+
id: 'graph',
|
|
5
|
+
name: 'Wiki Graph',
|
|
6
|
+
description: 'Force-directed graph of wikilink references across all markdown files. Supports Global and Local (2-hop) scope filters.',
|
|
7
|
+
author: 'MindOS',
|
|
8
|
+
icon: '🕸️',
|
|
9
|
+
tags: ['graph', 'wiki', 'links', 'visualization'],
|
|
10
|
+
builtin: true,
|
|
11
|
+
entryPath: 'README.md',
|
|
12
|
+
match: ({ extension }) => extension === 'md',
|
|
13
|
+
load: () => import('./GraphRenderer').then(m => ({ default: m.GraphRenderer })),
|
|
14
|
+
};
|
|
@@ -34,7 +34,7 @@ function renderMarkdown(md: string): string {
|
|
|
34
34
|
.replace(/^# (.+)$/gm, '<h1 style="font-size:1rem;font-weight:700;color:var(--foreground);margin:1.2em 0 .4em">$1</h1>')
|
|
35
35
|
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
|
36
36
|
.replace(/\*(.+?)\*/g, '<em>$1</em>')
|
|
37
|
-
.replace(/`(.+?)`/g, '<code
|
|
37
|
+
.replace(/`(.+?)`/g, '<code class="font-display" style="font-size:.82em;padding:1px 5px;border-radius:4px;background:var(--muted)">$1</code>')
|
|
38
38
|
.replace(/^[-*] (.+)$/gm, '<li style="margin:.2em 0;padding-left:.3em">$1</li>')
|
|
39
39
|
.replace(/(<li[^>]*>.*<\/li>\n?)+/g, s => `<ul style="margin:.4em 0;padding-left:1.4em;list-style:disc">${s}</ul>`)
|
|
40
40
|
.replace(/\n{2,}/g, '</p><p style="margin:.5em 0;font-size:.85rem;line-height:1.7;color:var(--foreground)">')
|
|
@@ -136,7 +136,7 @@ Be specific. Reference actual content from the files. Keep the total response un
|
|
|
136
136
|
<div style={{ maxWidth: 720, margin: '0 auto', padding: '1.5rem 0' }}>
|
|
137
137
|
{/* header row */}
|
|
138
138
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: '1.5rem', flexWrap: 'wrap' }}>
|
|
139
|
-
<span style={{
|
|
139
|
+
<span className="font-display" style={{ fontSize: 11, color: 'var(--muted-foreground)' }}>
|
|
140
140
|
{recentFiles.length > 0
|
|
141
141
|
? `${recentFiles.length} recently modified files`
|
|
142
142
|
: 'Loading recent files…'}
|
|
@@ -144,6 +144,7 @@ Be specific. Reference actual content from the files. Keep the total response un
|
|
|
144
144
|
<button
|
|
145
145
|
onClick={generate}
|
|
146
146
|
disabled={streaming || recentFiles.length === 0}
|
|
147
|
+
className="font-display"
|
|
147
148
|
style={{
|
|
148
149
|
display: 'flex',
|
|
149
150
|
alignItems: 'center',
|
|
@@ -151,7 +152,6 @@ Be specific. Reference actual content from the files. Keep the total response un
|
|
|
151
152
|
padding: '5px 14px',
|
|
152
153
|
borderRadius: 7,
|
|
153
154
|
fontSize: 12,
|
|
154
|
-
fontFamily: "'IBM Plex Mono',monospace",
|
|
155
155
|
cursor: streaming || recentFiles.length === 0 ? 'not-allowed' : 'pointer',
|
|
156
156
|
border: 'none',
|
|
157
157
|
background: streaming ? 'var(--muted)' : 'var(--amber)',
|
|
@@ -176,6 +176,7 @@ Be specific. Reference actual content from the files. Keep the total response un
|
|
|
176
176
|
<a
|
|
177
177
|
key={f.path}
|
|
178
178
|
href={`/view/${encodePath(f.path)}`}
|
|
179
|
+
className="font-display"
|
|
179
180
|
style={{
|
|
180
181
|
display: 'inline-flex',
|
|
181
182
|
alignItems: 'center',
|
|
@@ -183,7 +184,6 @@ Be specific. Reference actual content from the files. Keep the total response un
|
|
|
183
184
|
padding: '3px 10px',
|
|
184
185
|
borderRadius: 999,
|
|
185
186
|
fontSize: '0.7rem',
|
|
186
|
-
fontFamily: "'IBM Plex Mono',monospace",
|
|
187
187
|
background: 'var(--muted)',
|
|
188
188
|
color: 'var(--muted-foreground)',
|
|
189
189
|
textDecoration: 'none',
|
|
@@ -207,7 +207,7 @@ Be specific. Reference actual content from the files. Keep the total response un
|
|
|
207
207
|
|
|
208
208
|
{/* error */}
|
|
209
209
|
{error && (
|
|
210
|
-
<div style={{ padding: '10px 14px', borderRadius: 8, background: 'rgba(200,60,60,0.1)', border: '1px solid rgba(200,60,60,0.3)', color: '#c83c3c',
|
|
210
|
+
<div className="font-display" style={{ padding: '10px 14px', borderRadius: 8, background: 'rgba(200,60,60,0.1)', border: '1px solid rgba(200,60,60,0.3)', color: '#c83c3c', fontSize: 12, marginBottom: '1rem' }}>
|
|
211
211
|
{error}
|
|
212
212
|
</div>
|
|
213
213
|
)}
|
|
@@ -235,7 +235,7 @@ Be specific. Reference actual content from the files. Keep the total response un
|
|
|
235
235
|
color: 'var(--muted-foreground)',
|
|
236
236
|
}}>
|
|
237
237
|
<Sparkles size={28} style={{ margin: '0 auto 10px', opacity: 0.3, color: 'var(--amber)' }} />
|
|
238
|
-
<p style={{
|
|
238
|
+
<p className="font-display" style={{ fontSize: 12 }}>
|
|
239
239
|
Click <strong style={{ color: 'var(--foreground)' }}>Generate briefing</strong> to summarize recent changes with AI.
|
|
240
240
|
</p>
|
|
241
241
|
</div>
|
|
@@ -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
|
|
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)',
|
|
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={{
|
|
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={{
|
|
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={{
|
|
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,
|
|
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}`}
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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={{
|
|
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',
|
|
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={{
|
|
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={{
|
|
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)',
|
|
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={{
|
|
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={{
|
|
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
|
-
|
|
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',
|
|
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
|
+
};
|