@geminilight/mindos 0.6.29 → 0.6.31

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 (110) hide show
  1. package/README.md +10 -4
  2. package/README_zh.md +10 -4
  3. package/app/app/api/acp/config/route.ts +82 -0
  4. package/app/app/api/acp/detect/route.ts +71 -48
  5. package/app/app/api/acp/install/route.ts +51 -0
  6. package/app/app/api/acp/session/route.ts +141 -11
  7. package/app/app/api/ask/route.ts +126 -18
  8. package/app/app/api/export/route.ts +105 -0
  9. package/app/app/api/workflows/route.ts +156 -0
  10. package/app/app/globals.css +2 -2
  11. package/app/app/page.tsx +7 -2
  12. package/app/app/trash/page.tsx +7 -0
  13. package/app/app/view/[...path]/ViewPageClient.tsx +234 -2
  14. package/app/components/ActivityBar.tsx +12 -4
  15. package/app/components/AskModal.tsx +4 -1
  16. package/app/components/ExportModal.tsx +220 -0
  17. package/app/components/FileTree.tsx +42 -11
  18. package/app/components/HomeContent.tsx +92 -20
  19. package/app/components/MarkdownView.tsx +45 -10
  20. package/app/components/Panel.tsx +1 -0
  21. package/app/components/RightAskPanel.tsx +5 -1
  22. package/app/components/Sidebar.tsx +10 -1
  23. package/app/components/SidebarLayout.tsx +6 -0
  24. package/app/components/TrashPageClient.tsx +263 -0
  25. package/app/components/agents/AgentDetailContent.tsx +263 -47
  26. package/app/components/agents/AgentsContentPage.tsx +11 -0
  27. package/app/components/agents/AgentsPanelA2aTab.tsx +285 -46
  28. package/app/components/agents/AgentsPanelSessionsTab.tsx +166 -0
  29. package/app/components/agents/agents-content-model.ts +2 -2
  30. package/app/components/ask/AgentSelectorCapsule.tsx +218 -0
  31. package/app/components/ask/AskContent.tsx +197 -239
  32. package/app/components/ask/FileChip.tsx +82 -17
  33. package/app/components/ask/MentionPopover.tsx +21 -3
  34. package/app/components/ask/MessageList.tsx +30 -9
  35. package/app/components/ask/SlashCommandPopover.tsx +21 -3
  36. package/app/components/ask/ToolCallBlock.tsx +102 -18
  37. package/app/components/changes/ChangesContentPage.tsx +58 -14
  38. package/app/components/explore/ExploreContent.tsx +4 -7
  39. package/app/components/explore/UseCaseCard.tsx +18 -1
  40. package/app/components/explore/use-cases.generated.ts +76 -0
  41. package/app/components/explore/use-cases.yaml +185 -0
  42. package/app/components/panels/AgentsPanel.tsx +1 -0
  43. package/app/components/panels/AgentsPanelHubNav.tsx +9 -2
  44. package/app/components/panels/DiscoverPanel.tsx +1 -1
  45. package/app/components/panels/WorkflowsPanel.tsx +206 -0
  46. package/app/components/renderers/workflow-yaml/StepEditor.tsx +164 -0
  47. package/app/components/renderers/workflow-yaml/WorkflowEditor.tsx +211 -0
  48. package/app/components/renderers/workflow-yaml/WorkflowRunner.tsx +269 -0
  49. package/app/components/renderers/workflow-yaml/WorkflowYamlRenderer.tsx +126 -0
  50. package/app/components/renderers/workflow-yaml/execution.ts +229 -0
  51. package/app/components/renderers/workflow-yaml/index.ts +6 -0
  52. package/app/components/renderers/workflow-yaml/manifest.ts +21 -0
  53. package/app/components/renderers/workflow-yaml/parser.ts +172 -0
  54. package/app/components/renderers/workflow-yaml/selectors.tsx +574 -0
  55. package/app/components/renderers/workflow-yaml/serializer.ts +56 -0
  56. package/app/components/renderers/workflow-yaml/types.ts +46 -0
  57. package/app/components/settings/AiTab.tsx +191 -174
  58. package/app/components/settings/AppearanceTab.tsx +168 -77
  59. package/app/components/settings/KnowledgeTab.tsx +131 -136
  60. package/app/components/settings/McpTab.tsx +11 -11
  61. package/app/components/settings/Primitives.tsx +60 -0
  62. package/app/components/settings/SettingsContent.tsx +15 -8
  63. package/app/components/settings/SyncTab.tsx +12 -12
  64. package/app/components/settings/UninstallTab.tsx +8 -18
  65. package/app/components/settings/UpdateTab.tsx +82 -82
  66. package/app/components/settings/types.ts +17 -8
  67. package/app/hooks/useAcpConfig.ts +96 -0
  68. package/app/hooks/useAcpDetection.ts +69 -14
  69. package/app/hooks/useAcpRegistry.ts +46 -11
  70. package/app/hooks/useAskModal.ts +12 -5
  71. package/app/hooks/useAskPanel.ts +8 -5
  72. package/app/hooks/useAskSession.ts +19 -2
  73. package/app/hooks/useImageUpload.ts +152 -0
  74. package/app/lib/acp/acp-tools.ts +3 -1
  75. package/app/lib/acp/agent-descriptors.ts +274 -0
  76. package/app/lib/acp/bridge.ts +6 -0
  77. package/app/lib/acp/index.ts +20 -4
  78. package/app/lib/acp/registry.ts +74 -7
  79. package/app/lib/acp/session.ts +490 -28
  80. package/app/lib/acp/subprocess.ts +307 -21
  81. package/app/lib/acp/types.ts +158 -20
  82. package/app/lib/actions.ts +57 -3
  83. package/app/lib/agent/model.ts +18 -3
  84. package/app/lib/agent/stream-consumer.ts +18 -0
  85. package/app/lib/agent/to-agent-messages.ts +25 -2
  86. package/app/lib/agent/tools.ts +56 -9
  87. package/app/lib/core/export.ts +116 -0
  88. package/app/lib/core/trash.ts +241 -0
  89. package/app/lib/fs.ts +47 -0
  90. package/app/lib/hooks/usePinnedFiles.ts +90 -0
  91. package/app/lib/i18n/generated/explore-i18n.generated.ts +138 -0
  92. package/app/lib/i18n/index.ts +3 -0
  93. package/app/lib/i18n/modules/knowledge.ts +124 -6
  94. package/app/lib/i18n/modules/navigation.ts +2 -0
  95. package/app/lib/i18n/modules/onboarding.ts +2 -134
  96. package/app/lib/i18n/modules/panels.ts +146 -2
  97. package/app/lib/i18n/modules/settings.ts +12 -0
  98. package/app/lib/pi-integration/skills.ts +21 -6
  99. package/app/lib/renderers/index.ts +2 -2
  100. package/app/lib/settings.ts +10 -0
  101. package/app/lib/types.ts +12 -1
  102. package/app/next-env.d.ts +1 -1
  103. package/app/package.json +11 -3
  104. package/app/scripts/generate-explore.ts +145 -0
  105. package/package.json +1 -1
  106. package/templates/en/.mindos/workflows/Sprint Release.flow.yaml +130 -0
  107. package/templates/zh/.mindos/workflows//345/221/250/350/277/255/344/273/243/346/243/200/346/237/245.flow.yaml +84 -0
  108. package/app/components/explore/use-cases.ts +0 -58
  109. package/app/components/renderers/workflow/WorkflowRenderer.tsx +0 -409
  110. package/app/components/renderers/workflow/manifest.ts +0 -14
@@ -1,409 +0,0 @@
1
- 'use client';
2
-
3
- import { useMemo, useState, useRef, useCallback } from 'react';
4
- import { Play, SkipForward, RotateCcw, CheckCircle2, Circle, Loader2, AlertCircle, ChevronDown, Sparkles } from 'lucide-react';
5
- import type { RendererContext } from '@/lib/renderers/registry';
6
- import { useLocale } from '@/lib/LocaleContext';
7
-
8
- // ─── Types ────────────────────────────────────────────────────────────────────
9
-
10
- type StepStatus = 'pending' | 'running' | 'done' | 'skipped' | 'error';
11
-
12
- interface WorkflowStep {
13
- index: number;
14
- heading: string; // full heading text e.g. "Step 1: Gather requirements"
15
- body: string; // body text below heading
16
- status: StepStatus;
17
- output: string; // AI output for this step
18
- }
19
-
20
- interface WorkflowMeta {
21
- title: string;
22
- description: string;
23
- }
24
-
25
- // ─── Parser ───────────────────────────────────────────────────────────────────
26
-
27
- function parseWorkflow(content: string): { meta: WorkflowMeta; steps: WorkflowStep[] } {
28
- const lines = content.split('\n');
29
- let title = '';
30
- let description = '';
31
- const steps: WorkflowStep[] = [];
32
- let currentStep: { heading: string; bodyLines: string[] } | null = null;
33
- let inMeta = true;
34
- const metaLines: string[] = [];
35
-
36
- const flushStep = () => {
37
- if (!currentStep) return;
38
- steps.push({
39
- index: steps.length,
40
- heading: currentStep.heading,
41
- body: currentStep.bodyLines.join('\n').trim(),
42
- status: 'pending',
43
- output: '',
44
- });
45
- currentStep = null;
46
- };
47
-
48
- for (const line of lines) {
49
- if (/^# /.test(line)) {
50
- title = line.slice(2).trim();
51
- inMeta = true;
52
- continue;
53
- }
54
- // H2 = step
55
- if (/^## /.test(line)) {
56
- flushStep();
57
- inMeta = false;
58
- currentStep = { heading: line.slice(3).trim(), bodyLines: [] };
59
- continue;
60
- }
61
- if (currentStep) {
62
- currentStep.bodyLines.push(line);
63
- } else if (inMeta) {
64
- metaLines.push(line);
65
- }
66
- }
67
- flushStep();
68
-
69
- description = metaLines.filter(l => l.trim() && !/^#/.test(l)).join(' ').trim().slice(0, 200);
70
-
71
- return { meta: { title, description }, steps };
72
- }
73
-
74
- // ─── Inline markdown renderer ─────────────────────────────────────────────────
75
-
76
- function renderInline(text: string): string {
77
- return text
78
- .replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
79
- .replace(/`(.+?)`/g, `<code class="font-display" style="font-size:.82em;padding:1px 5px;border-radius:4px;background:var(--muted)">$1</code>`)
80
- .replace(/\*(.+?)\*/g, '<em>$1</em>');
81
- }
82
-
83
- function renderBody(body: string): string {
84
- return body.split('\n').map(line => {
85
- if (!line.trim()) return '';
86
- if (/^- /.test(line)) return `<li style="margin:.2em 0;font-size:.82rem;color:var(--muted-foreground)">${renderInline(line.slice(2))}</li>`;
87
- return `<p style="margin:.3em 0;font-size:.82rem;line-height:1.6;color:var(--muted-foreground)">${renderInline(line)}</p>`;
88
- }).join('');
89
- }
90
-
91
- // ─── Status icon ─────────────────────────────────────────────────────────────
92
-
93
- function StatusIcon({ status }: { status: StepStatus }) {
94
- if (status === 'pending') return <Circle size={15} style={{ color: 'var(--border)' }} />;
95
- if (status === 'running') return <Loader2 size={15} style={{ color: 'var(--amber)', animation: 'spin 1s linear infinite' }} />;
96
- if (status === 'done') return <CheckCircle2 size={15} style={{ color: 'var(--success)' }} />;
97
- if (status === 'skipped') return <SkipForward size={15} style={{ color: 'var(--muted-foreground)', opacity: .5 }} />;
98
- return <AlertCircle size={15} style={{ color: 'var(--error)' }} />;
99
- }
100
-
101
- const STATUS_BORDER: Record<StepStatus, string> = {
102
- pending: 'var(--border)',
103
- running: 'rgba(200,135,58,0.5)',
104
- done: 'rgba(122,173,128,0.4)',
105
- skipped: 'var(--border)',
106
- error: 'rgba(200,80,80,0.4)',
107
- };
108
-
109
- // ─── AI execution ─────────────────────────────────────────────────────────────
110
-
111
- async function runStepWithAI(
112
- step: WorkflowStep,
113
- filePath: string,
114
- allStepsSummary: string,
115
- onChunk: (chunk: string) => void,
116
- signal: AbortSignal,
117
- ): Promise<void> {
118
- const prompt = `You are executing step ${step.index + 1} of a SOP/Workflow: "${step.heading}".
119
-
120
- Context of the full workflow:
121
- ${allStepsSummary}
122
-
123
- Current step instructions:
124
- ${step.body || '(No specific instructions — use common sense for this step.)'}
125
-
126
- Execute this step concisely. Provide:
127
- 1. What you did / what the output is
128
- 2. Any decisions made
129
- 3. What the next step should watch out for
130
-
131
- Be specific and actionable. Format in Markdown.`;
132
-
133
- const res = await fetch('/api/ask', {
134
- method: 'POST',
135
- headers: { 'Content-Type': 'application/json' },
136
- body: JSON.stringify({
137
- messages: [{ role: 'user', content: prompt }],
138
- currentFile: filePath,
139
- }),
140
- signal,
141
- });
142
-
143
- if (!res.ok) throw new Error(`HTTP ${res.status}`);
144
- if (!res.body) throw new Error('No response body');
145
-
146
- const reader = res.body.getReader();
147
- const decoder = new TextDecoder();
148
- let acc = '';
149
-
150
- while (true) {
151
- const { done, value } = await reader.read();
152
- if (done) break;
153
- const raw = decoder.decode(value, { stream: true });
154
- for (const line of raw.split('\n')) {
155
- const m = line.match(/^0:"((?:[^"\\]|\\.)*)"$/);
156
- if (m) {
157
- acc += m[1].replace(/\\n/g, '\n').replace(/\\"/g, '"').replace(/\\\\/g, '\\');
158
- onChunk(acc);
159
- }
160
- }
161
- }
162
- }
163
-
164
- // ─── Step card ────────────────────────────────────────────────────────────────
165
-
166
- function StepCard({
167
- step, isActive, onRun, onSkip, canRun,
168
- }: {
169
- step: WorkflowStep;
170
- isActive: boolean;
171
- onRun: () => void;
172
- onSkip: () => void;
173
- canRun: boolean;
174
- }) {
175
- const { t } = useLocale();
176
- const [expanded, setExpanded] = useState(false);
177
- const hasBody = step.body.trim().length > 0;
178
- const hasOutput = step.output.length > 0;
179
-
180
- return (
181
- <div style={{
182
- border: `1px solid ${STATUS_BORDER[step.status]}`,
183
- borderRadius: 10,
184
- overflow: 'hidden',
185
- background: 'var(--card)',
186
- opacity: step.status === 'skipped' ? 0.6 : 1,
187
- transition: 'border-color .2s, opacity .2s',
188
- }}>
189
- {/* header */}
190
- <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '11px 14px' }}>
191
- <StatusIcon status={step.status} />
192
- <span
193
- style={{ flex: 1, fontWeight: 600, fontSize: '.88rem', color: 'var(--foreground)', cursor: hasBody || hasOutput ? 'pointer' : 'default' }}
194
- onClick={() => (hasBody || hasOutput) && setExpanded(v => !v)}
195
- >
196
- {step.heading}
197
- </span>
198
-
199
- {/* action buttons */}
200
- <div style={{ display: 'flex', gap: 5, flexShrink: 0 }}>
201
- {step.status === 'pending' && (
202
- <>
203
- <button
204
- onClick={onRun}
205
- disabled={!canRun}
206
- title={!canRun ? t.hints.workflowStepRunning : undefined}
207
- style={{
208
- display: 'flex', alignItems: 'center', gap: 4,
209
- padding: '3px 10px', borderRadius: 6, fontSize: '0.72rem',
210
- cursor: canRun ? 'pointer' : 'not-allowed',
211
- border: 'none', background: canRun ? 'var(--amber)' : 'var(--muted)',
212
- color: canRun ? 'var(--amber-foreground)' : 'var(--muted-foreground)',
213
- opacity: canRun ? 1 : 0.5,
214
- }}
215
- >
216
- <Play size={10} /> Run
217
- </button>
218
- <button
219
- onClick={onSkip}
220
- style={{
221
- padding: '3px 8px', borderRadius: 6, fontSize: '0.72rem',
222
- cursor: 'pointer',
223
- border: '1px solid var(--border)', background: 'transparent',
224
- color: 'var(--muted-foreground)',
225
- }}
226
- >
227
- Skip
228
- </button>
229
- </>
230
- )}
231
- {step.status === 'running' && (
232
- <span className="font-display" style={{ fontSize: '0.7rem', color: 'var(--amber)' }}>executing…</span>
233
- )}
234
- {(step.status === 'done' || step.status === 'error') && (
235
- <button
236
- onClick={() => setExpanded(v => !v)}
237
- style={{ padding: '3px 8px', borderRadius: 6, fontSize: '0.72rem', cursor: 'pointer', border: '1px solid var(--border)', background: 'transparent', color: 'var(--muted-foreground)' }}
238
- >
239
- <ChevronDown size={11} style={{ display: 'inline', transform: expanded ? 'rotate(180deg)' : 'none', transition: 'transform .15s' }} />
240
- </button>
241
- )}
242
- </div>
243
- </div>
244
-
245
- {/* body / output */}
246
- {(expanded || step.status === 'running') && (hasBody || hasOutput) && (
247
- <div style={{ borderTop: '1px solid var(--border)' }}>
248
- {hasBody && (
249
- <div style={{ padding: '10px 14px', borderBottom: hasOutput ? '1px solid var(--border)' : 'none' }}>
250
- <div dangerouslySetInnerHTML={{ __html: renderBody(step.body) }} />
251
- </div>
252
- )}
253
- {hasOutput && (
254
- <div style={{ padding: '10px 14px', background: 'var(--background)', position: 'relative' }}>
255
- <div style={{ display: 'flex', alignItems: 'center', gap: 5, marginBottom: 6 }}>
256
- <Sparkles size={11} style={{ color: 'var(--amber)' }} />
257
- <span className="font-display" style={{ fontSize: '0.68rem', color: 'var(--muted-foreground)', textTransform: 'uppercase', letterSpacing: '.06em' }}>AI Output</span>
258
- {step.status === 'running' && <span style={{ width: 5, height: 5, borderRadius: '50%', background: 'var(--amber)', animation: 'pulse 1.2s ease-in-out infinite', marginLeft: 4 }} />}
259
- </div>
260
- <div style={{ fontSize: '.82rem', lineHeight: 1.7, color: 'var(--foreground)', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
261
- {step.output}
262
- </div>
263
- </div>
264
- )}
265
- </div>
266
- )}
267
- </div>
268
- );
269
- }
270
-
271
- // ─── Main renderer ────────────────────────────────────────────────────────────
272
-
273
- export function WorkflowRenderer({ filePath, content }: RendererContext) {
274
- const { t } = useLocale();
275
- const parsed = useMemo(() => parseWorkflow(content), [content]);
276
- const [steps, setSteps] = useState<WorkflowStep[]>(() => parsed.steps);
277
- const [running, setRunning] = useState(false);
278
- const abortRef = useRef<AbortController | null>(null);
279
-
280
- // Reset when content changes externally
281
- useMemo(() => { setSteps(parsed.steps.map(s => ({ ...s, status: 'pending' as StepStatus, output: '' }))); }, [parsed]);
282
-
283
- const allStepsSummary = useMemo(() =>
284
- parsed.steps.map((s, i) => `${i + 1}. ${s.heading}`).join('\n'),
285
- [parsed]);
286
-
287
- const runStep = useCallback(async (idx: number) => {
288
- if (running) return;
289
- abortRef.current?.abort();
290
- const ctrl = new AbortController();
291
- abortRef.current = ctrl;
292
- setRunning(true);
293
-
294
- setSteps(prev => prev.map((s, i) =>
295
- i === idx ? { ...s, status: 'running', output: '' } : s));
296
-
297
- try {
298
- await runStepWithAI(
299
- steps[idx], filePath, allStepsSummary,
300
- (chunk) => setSteps(prev => prev.map((s, i) =>
301
- i === idx ? { ...s, output: chunk } : s)),
302
- ctrl.signal,
303
- );
304
- setSteps(prev => prev.map((s, i) =>
305
- i === idx ? { ...s, status: 'done' } : s));
306
- } catch (err: unknown) {
307
- if (err instanceof Error && err.name === 'AbortError') return;
308
- setSteps(prev => prev.map((s, i) =>
309
- i === idx ? { ...s, status: 'error', output: (err instanceof Error ? err.message : String(err)) } : s));
310
- } finally {
311
- setRunning(false);
312
- }
313
- }, [running, steps, filePath, allStepsSummary]);
314
-
315
- const skipStep = useCallback((idx: number) => {
316
- setSteps(prev => prev.map((s, i) =>
317
- i === idx ? { ...s, status: 'skipped' } : s));
318
- }, []);
319
-
320
- const reset = useCallback(() => {
321
- abortRef.current?.abort();
322
- setRunning(false);
323
- setSteps(parsed.steps.map(s => ({ ...s, status: 'pending' as StepStatus, output: '' })));
324
- }, [parsed]);
325
-
326
- // Next runnable step = first pending step
327
- const nextPendingIdx = steps.findIndex(s => s.status === 'pending');
328
- const doneCount = steps.filter(s => s.status === 'done').length;
329
- const progress = steps.length > 0 ? Math.round((doneCount / steps.length) * 100) : 0;
330
-
331
- if (steps.length === 0) {
332
- return (
333
- <div className="font-display" style={{ padding: '3rem 1rem', textAlign: 'center', color: 'var(--muted-foreground)', fontSize: 12 }}>
334
- No steps found. Add <code style={{ background: 'var(--muted)', padding: '1px 5px', borderRadius: 4 }}>## Step N: …</code> headings to define workflow steps.
335
- </div>
336
- );
337
- }
338
-
339
- return (
340
- <div style={{ maxWidth: 720, margin: '0 auto', padding: '1.5rem 0' }}>
341
- {/* header */}
342
- <div style={{ marginBottom: '1.2rem' }}>
343
- {parsed.meta.description && (
344
- <p style={{ fontSize: '.82rem', color: 'var(--muted-foreground)', lineHeight: 1.6, marginBottom: 12 }}>
345
- {parsed.meta.description}
346
- </p>
347
- )}
348
-
349
- {/* progress + actions */}
350
- <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
351
- {/* progress bar */}
352
- <div style={{ flex: 1, minWidth: 120, height: 4, borderRadius: 999, background: 'var(--border)', overflow: 'hidden' }}>
353
- <div style={{ height: '100%', width: `${progress}%`, background: 'var(--amber)', borderRadius: 999, transition: 'width .3s' }} />
354
- </div>
355
- <span className="font-display" style={{ fontSize: '0.7rem', color: 'var(--muted-foreground)', flexShrink: 0 }}>
356
- {doneCount}/{steps.length} done
357
- </span>
358
-
359
- {/* run next */}
360
- {nextPendingIdx >= 0 && (
361
- <button
362
- onClick={() => runStep(nextPendingIdx)}
363
- disabled={running}
364
- title={running ? t.hints.workflowRunning : undefined}
365
- style={{
366
- display: 'flex', alignItems: 'center', gap: 5,
367
- padding: '4px 12px', borderRadius: 7, fontSize: '0.75rem',
368
- cursor: running ? 'not-allowed' : 'pointer',
369
- border: 'none', background: running ? 'var(--muted)' : 'var(--amber)',
370
- color: running ? 'var(--muted-foreground)' : 'var(--amber-foreground)',
371
- opacity: running ? 0.7 : 1,
372
- }}
373
- >
374
- {running ? <Loader2 size={11} style={{ animation: 'spin 1s linear infinite' }} /> : <Play size={11} />}
375
- Run next
376
- </button>
377
- )}
378
-
379
- {/* reset */}
380
- <button
381
- onClick={reset}
382
- 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 }}
383
- >
384
- <RotateCcw size={11} /> Reset
385
- </button>
386
- </div>
387
- </div>
388
-
389
- {/* step list */}
390
- <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
391
- {steps.map((step, i) => (
392
- <StepCard
393
- key={i}
394
- step={step}
395
- isActive={i === nextPendingIdx}
396
- canRun={!running}
397
- onRun={() => runStep(i)}
398
- onSkip={() => skipStep(i)}
399
- />
400
- ))}
401
- </div>
402
-
403
- <style>{`
404
- @keyframes spin { to { transform: rotate(360deg); } }
405
- @keyframes pulse { 0%,100% { opacity:1; } 50% { opacity:.3; } }
406
- `}</style>
407
- </div>
408
- );
409
- }
@@ -1,14 +0,0 @@
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
- };