@theproductguy/create-mission-control 1.0.14 → 1.0.17
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/bin/cli.js +2 -2
- package/package.json +1 -1
- package/src/template/control-center/frontend/src/App.tsx +583 -522
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { useEffect, useState } from 'react';
|
|
1
|
+
import { useEffect, useState, createContext, useContext } from 'react';
|
|
2
2
|
import axios from 'axios';
|
|
3
|
-
import { Layout, Layers, FileText, Code, CheckSquare, RefreshCw, ArrowRight, X, Plus, Trash2, WalletCards, LayoutDashboard, Copy, Package, Play } from 'lucide-react';
|
|
3
|
+
import { Layout, Layers, FileText, Code, CheckSquare, RefreshCw, ArrowRight, X, Plus, Trash2, WalletCards, LayoutDashboard, Copy, Package, Play, Settings } from 'lucide-react';
|
|
4
4
|
import { useToast } from './components/ui/ToastContext';
|
|
5
|
+
// @ts-ignore
|
|
5
6
|
import { ThemeToggle } from '@theproductguy/agent-os-ui';
|
|
6
7
|
import '@theproductguy/agent-os-ui/style.css';
|
|
7
8
|
import ReactMarkdown from 'react-markdown';
|
|
@@ -115,6 +116,8 @@ const Guidance = ({ phase, title, description, prompt, actionLabel, onAction, cl
|
|
|
115
116
|
</div>
|
|
116
117
|
);
|
|
117
118
|
|
|
119
|
+
const IdeContext = createContext({ scheme: 'vscode', setScheme: (_s: string) => { } });
|
|
120
|
+
|
|
118
121
|
function StatusItem({ label, status, icon, small, onClick }: any) {
|
|
119
122
|
if (!status) return null;
|
|
120
123
|
const isComplete = status.exists && (status.total > 0 ? status.completed === status.total : true);
|
|
@@ -145,7 +148,8 @@ function StatusItem({ label, status, icon, small, onClick }: any) {
|
|
|
145
148
|
}
|
|
146
149
|
|
|
147
150
|
function PromptButton({ label, prompt, onClick, small, primary }: any) {
|
|
148
|
-
const
|
|
151
|
+
const { scheme } = useContext(IdeContext);
|
|
152
|
+
const deepLink = `${scheme}://vscode.executeCommand/workbench.action.chat.open?query=${encodeURIComponent(prompt)}`;
|
|
149
153
|
|
|
150
154
|
return (
|
|
151
155
|
<div className={`flex gap-1 items-center ${!small ? 'w-full' : 'flex-1'}`}>
|
|
@@ -167,7 +171,7 @@ function PromptButton({ label, prompt, onClick, small, primary }: any) {
|
|
|
167
171
|
className={`flex items-center justify-center rounded-lg border border-border bg-secondary hover:bg-secondary/80 text-foreground transition
|
|
168
172
|
${small ? 'w-8 h-[30px]' : 'w-10 h-[38px]'}
|
|
169
173
|
`}
|
|
170
|
-
title=
|
|
174
|
+
title={`Run in IDE (${scheme}://)`}
|
|
171
175
|
>
|
|
172
176
|
<Play size={small ? 12 : 14} fill="currentColor" className="opacity-70" />
|
|
173
177
|
</a>
|
|
@@ -290,6 +294,20 @@ function App() {
|
|
|
290
294
|
const { toast } = useToast();
|
|
291
295
|
const [runtimeConfig, setRuntimeConfig] = useState<any>(null);
|
|
292
296
|
|
|
297
|
+
// IDE Settings
|
|
298
|
+
const [showSettings, setShowSettings] = useState(false);
|
|
299
|
+
const [ideScheme, setIdeScheme] = useState('vscode');
|
|
300
|
+
|
|
301
|
+
useEffect(() => {
|
|
302
|
+
const saved = localStorage.getItem('ide_scheme');
|
|
303
|
+
if (saved) setIdeScheme(saved);
|
|
304
|
+
}, []);
|
|
305
|
+
|
|
306
|
+
const updateIdeScheme = (scheme: string) => {
|
|
307
|
+
setIdeScheme(scheme);
|
|
308
|
+
localStorage.setItem('ide_scheme', scheme);
|
|
309
|
+
};
|
|
310
|
+
|
|
293
311
|
// Edit State
|
|
294
312
|
const [isEditing, setIsEditing] = useState(false);
|
|
295
313
|
const [editContent, setEditContent] = useState("");
|
|
@@ -398,606 +416,649 @@ function App() {
|
|
|
398
416
|
}
|
|
399
417
|
|
|
400
418
|
return (
|
|
401
|
-
<
|
|
402
|
-
|
|
403
|
-
|
|
419
|
+
<IdeContext.Provider value={{ scheme: ideScheme, setScheme: updateIdeScheme }}>
|
|
420
|
+
<div className="min-h-screen bg-background text-foreground font-sans flex flex-col h-screen overflow-hidden">
|
|
421
|
+
{showSettings && (
|
|
404
422
|
<div className="fixed inset-0 z-[200] bg-black/50 backdrop-blur-sm flex items-center justify-center p-6 animate-in fade-in">
|
|
405
|
-
<div className="bg-card border border-border w-full max-w-
|
|
406
|
-
<div className="
|
|
407
|
-
<
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
</div>
|
|
416
|
-
<div className="flex items-center gap-2">
|
|
417
|
-
{!isEditing ? (
|
|
418
|
-
<button
|
|
419
|
-
onClick={() => setIsEditing(true)}
|
|
420
|
-
className="px-3 py-1.5 text-sm font-medium bg-secondary hover:bg-secondary/80 rounded-md transition-colors"
|
|
421
|
-
>
|
|
422
|
-
Edit
|
|
423
|
-
</button>
|
|
424
|
-
) : (
|
|
425
|
-
<>
|
|
423
|
+
<div className="bg-card border border-border w-full max-w-sm rounded-xl shadow-2xl flex flex-col overflow-hidden animate-in zoom-in-95 duration-200">
|
|
424
|
+
<div className="p-6 border-b border-border bg-muted/30 flex items-center justify-between">
|
|
425
|
+
<h3 className="font-semibold text-lg flex items-center gap-2"><Settings size={18} /> Settings</h3>
|
|
426
|
+
<button onClick={() => setShowSettings(false)} className="hover:bg-secondary p-1 rounded"><X size={18} /></button>
|
|
427
|
+
</div>
|
|
428
|
+
<div className="p-6 space-y-4">
|
|
429
|
+
<div>
|
|
430
|
+
<label className="text-sm font-medium mb-2 block">IDE / Editor</label>
|
|
431
|
+
<div className="space-y-2">
|
|
432
|
+
{['vscode', 'cursor', 'windsurf', 'antigravity'].map((s) => (
|
|
426
433
|
<button
|
|
427
|
-
|
|
428
|
-
|
|
434
|
+
key={s}
|
|
435
|
+
onClick={() => updateIdeScheme(s)}
|
|
436
|
+
className={`w-full flex items-center justify-between px-4 py-3 rounded-lg border transition-all ${ideScheme === s
|
|
437
|
+
? 'bg-primary/10 border-primary text-primary'
|
|
438
|
+
: 'bg-secondary/20 border-border hover:border-foreground/20'
|
|
439
|
+
}`}
|
|
429
440
|
>
|
|
430
|
-
|
|
441
|
+
<span className="capitalize font-medium">{s}</span>
|
|
442
|
+
{ideScheme === s && <div className="w-2 h-2 rounded-full bg-primary" />}
|
|
431
443
|
</button>
|
|
444
|
+
))}
|
|
445
|
+
</div>
|
|
446
|
+
<p className="text-xs text-muted-foreground mt-3">
|
|
447
|
+
Select the editor to open when clicking "Play".
|
|
448
|
+
<br />Currently using: <code>{ideScheme}://</code>
|
|
449
|
+
</p>
|
|
450
|
+
</div>
|
|
451
|
+
</div>
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
)}
|
|
455
|
+
{[
|
|
456
|
+
viewingFile && (
|
|
457
|
+
<div className="fixed inset-0 z-[200] bg-black/50 backdrop-blur-sm flex items-center justify-center p-6 animate-in fade-in">
|
|
458
|
+
<div className="bg-card border border-border w-full max-w-4xl max-h-[90vh] rounded-xl shadow-2xl flex flex-col overflow-hidden animate-in zoom-in-95 duration-200">
|
|
459
|
+
<div className="flex items-center justify-between px-6 py-4 border-b border-border bg-muted/30">
|
|
460
|
+
<div className="flex items-center gap-3">
|
|
461
|
+
<div className="p-2 bg-primary/10 text-primary rounded-md">
|
|
462
|
+
<FileText size={18} />
|
|
463
|
+
</div>
|
|
464
|
+
<div>
|
|
465
|
+
<h3 className="font-semibold text-lg">{viewingFile.title}</h3>
|
|
466
|
+
<code className="text-xs text-muted-foreground font-mono">{viewingFile.path}</code>
|
|
467
|
+
</div>
|
|
468
|
+
</div>
|
|
469
|
+
<div className="flex items-center gap-2">
|
|
470
|
+
{!isEditing ? (
|
|
432
471
|
<button
|
|
433
|
-
onClick={
|
|
434
|
-
className="px-3 py-1.5 text-sm font-medium bg-
|
|
472
|
+
onClick={() => setIsEditing(true)}
|
|
473
|
+
className="px-3 py-1.5 text-sm font-medium bg-secondary hover:bg-secondary/80 rounded-md transition-colors"
|
|
435
474
|
>
|
|
436
|
-
|
|
475
|
+
Edit
|
|
437
476
|
</button>
|
|
438
|
-
|
|
477
|
+
) : (
|
|
478
|
+
<>
|
|
479
|
+
<button
|
|
480
|
+
onClick={() => setIsEditing(false)}
|
|
481
|
+
className="px-3 py-1.5 text-sm font-medium hover:bg-secondary rounded-md transition-colors"
|
|
482
|
+
>
|
|
483
|
+
Cancel
|
|
484
|
+
</button>
|
|
485
|
+
<button
|
|
486
|
+
onClick={saveFile}
|
|
487
|
+
className="px-3 py-1.5 text-sm font-medium bg-primary text-primary-foreground hover:bg-primary/90 rounded-md transition-colors"
|
|
488
|
+
>
|
|
489
|
+
Save Changes
|
|
490
|
+
</button>
|
|
491
|
+
</>
|
|
492
|
+
)}
|
|
493
|
+
<button onClick={() => setViewingFile(null)} className="p-2 hover:bg-secondary rounded-full transition-colors ml-2">
|
|
494
|
+
<X size={20} />
|
|
495
|
+
</button>
|
|
496
|
+
</div>
|
|
497
|
+
</div>
|
|
498
|
+
<div className="flex-1 overflow-y-auto p-6 bg-background">
|
|
499
|
+
{fileContent === "Loading..." || fileContent === "Error loading file." ? (
|
|
500
|
+
<div className="font-mono text-sm">{fileContent}</div>
|
|
501
|
+
) : isEditing ? (
|
|
502
|
+
<textarea
|
|
503
|
+
value={editContent}
|
|
504
|
+
onChange={(e) => setEditContent(e.target.value)}
|
|
505
|
+
className="w-full h-full min-h-[500px] p-4 font-mono text-sm bg-secondary text-secondary-foreground border border-border rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-primary/20"
|
|
506
|
+
/>
|
|
507
|
+
) : (
|
|
508
|
+
<article className="prose dark:prose-invert max-w-none prose-headings:font-serif prose-headings:font-semibold prose-a:text-primary prose-code:text-primary prose-pre:bg-secondary/50 prose-pre:border prose-pre:border-border prose-p:leading-relaxed prose-p:mb-4">
|
|
509
|
+
<ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]}>
|
|
510
|
+
{fileContent || ''}
|
|
511
|
+
</ReactMarkdown>
|
|
512
|
+
</article>
|
|
439
513
|
)}
|
|
440
|
-
<button onClick={() => setViewingFile(null)} className="p-2 hover:bg-secondary rounded-full transition-colors ml-2">
|
|
441
|
-
<X size={20} />
|
|
442
|
-
</button>
|
|
443
514
|
</div>
|
|
444
515
|
</div>
|
|
445
|
-
<div className="flex-1 overflow-y-auto p-6 bg-background">
|
|
446
|
-
{fileContent === "Loading..." || fileContent === "Error loading file." ? (
|
|
447
|
-
<div className="font-mono text-sm">{fileContent}</div>
|
|
448
|
-
) : isEditing ? (
|
|
449
|
-
<textarea
|
|
450
|
-
value={editContent}
|
|
451
|
-
onChange={(e) => setEditContent(e.target.value)}
|
|
452
|
-
className="w-full h-full min-h-[500px] p-4 font-mono text-sm bg-secondary text-secondary-foreground border border-border rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-primary/20"
|
|
453
|
-
/>
|
|
454
|
-
) : (
|
|
455
|
-
<article className="prose dark:prose-invert max-w-none prose-headings:font-serif prose-headings:font-semibold prose-a:text-primary prose-code:text-primary prose-pre:bg-secondary/50 prose-pre:border prose-pre:border-border prose-p:leading-relaxed prose-p:mb-4">
|
|
456
|
-
<ReactMarkdown remarkPlugins={[remarkGfm, remarkBreaks]}>
|
|
457
|
-
{fileContent || ''}
|
|
458
|
-
</ReactMarkdown>
|
|
459
|
-
</article>
|
|
460
|
-
)}
|
|
461
|
-
</div>
|
|
462
516
|
</div>
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
517
|
+
),
|
|
518
|
+
creatingSpec && (
|
|
519
|
+
<div className="fixed inset-0 z-[200] bg-black/50 backdrop-blur-sm flex items-center justify-center p-6 animate-in fade-in">
|
|
520
|
+
<div className="bg-card border border-border w-full max-w-md rounded-xl shadow-2xl flex flex-col overflow-hidden animate-in zoom-in-95 duration-200">
|
|
521
|
+
<div className="px-6 py-4 border-b border-border bg-muted/30">
|
|
522
|
+
<h3 className="font-semibold text-lg">New Feature Spec</h3>
|
|
523
|
+
<p className="text-sm text-muted-foreground">Shape a new feature for your project.</p>
|
|
524
|
+
</div>
|
|
525
|
+
<div className="p-6">
|
|
526
|
+
<div className="space-y-4">
|
|
527
|
+
<div>
|
|
528
|
+
<label className="text-sm font-medium mb-1.5 block">Feature Name</label>
|
|
529
|
+
<input
|
|
530
|
+
autoFocus
|
|
531
|
+
type="text"
|
|
532
|
+
className="w-full px-3 py-2 rounded-md border border-input bg-secondary text-secondary-foreground text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
533
|
+
placeholder="e.g. user-profile"
|
|
534
|
+
value={newSpecName}
|
|
535
|
+
onChange={(e) => setNewSpecName(e.target.value)}
|
|
536
|
+
onKeyDown={async (e) => {
|
|
537
|
+
if (e.key === 'Enter' && newSpecName.trim()) {
|
|
538
|
+
const name = newSpecName.trim();
|
|
539
|
+
if (runtimeConfig?.api) {
|
|
540
|
+
await axios.post(`${runtimeConfig.api}/api/scaffold/spec`, { name });
|
|
541
|
+
const prompt = `Antigravity, let's shape the spec for '${name}'. Read commands/shape-spec/shape-spec.md.`;
|
|
542
|
+
await copyToClipboard(prompt);
|
|
543
|
+
setNewSpecName("");
|
|
544
|
+
setCreatingSpec(false);
|
|
545
|
+
await fetchStatus();
|
|
546
|
+
openFile(`specs/${name}/spec.md`, `${name} Spec`);
|
|
547
|
+
toast({ title: "Spec created & Prompt copied!", type: 'success' });
|
|
548
|
+
}
|
|
495
549
|
}
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
</
|
|
550
|
+
}}
|
|
551
|
+
/>
|
|
552
|
+
<p className="text-xs text-muted-foreground mt-1.5">
|
|
553
|
+
This will create a new directory in <code>specs/</code> and copy the prompt to your clipboard.
|
|
554
|
+
</p>
|
|
555
|
+
</div>
|
|
556
|
+
<div className="flex justify-end gap-2 pt-2">
|
|
557
|
+
<button onClick={() => { setCreatingSpec(false); setNewSpecName(""); }} className="px-4 py-2 text-sm font-medium rounded-md hover:bg-secondary transition-colors">Cancel</button>
|
|
558
|
+
<button
|
|
559
|
+
disabled={!newSpecName.trim()}
|
|
560
|
+
onClick={async () => {
|
|
561
|
+
if (newSpecName.trim()) {
|
|
562
|
+
const name = newSpecName.trim();
|
|
563
|
+
if (runtimeConfig?.api) {
|
|
564
|
+
await axios.post(`${runtimeConfig.api}/api/scaffold/spec`, { name });
|
|
565
|
+
const prompt = `Antigravity, let's shape the spec for '${name}'. Read commands/shape-spec/shape-spec.md.`;
|
|
566
|
+
await copyToClipboard(prompt);
|
|
567
|
+
setNewSpecName("");
|
|
568
|
+
setCreatingSpec(false);
|
|
569
|
+
await fetchStatus();
|
|
570
|
+
openFile(`specs/${name}/spec.md`, `${name} Spec`);
|
|
571
|
+
toast({ title: "Spec created & Prompt copied!", type: 'success' });
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}}
|
|
575
|
+
className="px-4 py-2 text-sm font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
|
576
|
+
>
|
|
577
|
+
Create Spec
|
|
578
|
+
</button>
|
|
579
|
+
</div>
|
|
502
580
|
</div>
|
|
503
|
-
|
|
504
|
-
|
|
581
|
+
</div>
|
|
582
|
+
</div>
|
|
583
|
+
</div>
|
|
584
|
+
),
|
|
585
|
+
deletingSpec && (
|
|
586
|
+
<div className="fixed inset-0 z-[200] bg-black/50 backdrop-blur-sm flex items-center justify-center p-6 animate-in fade-in">
|
|
587
|
+
<div className="bg-card border border-border w-full max-w-sm rounded-xl shadow-2xl flex flex-col overflow-hidden animate-in zoom-in-95 duration-200">
|
|
588
|
+
<div className="p-6">
|
|
589
|
+
<div className="flex items-center gap-3 mb-4 text-red-500">
|
|
590
|
+
<div className="p-2 bg-red-100 dark:bg-red-900/20 rounded-full">
|
|
591
|
+
<Trash2 size={24} />
|
|
592
|
+
</div>
|
|
593
|
+
<h3 className="font-semibold text-lg text-foreground">Delete Spec?</h3>
|
|
594
|
+
</div>
|
|
595
|
+
<p className="text-muted-foreground mb-6">
|
|
596
|
+
Are you sure you want to delete <strong>{deletingSpec}</strong>? This action cannot be undone and will permanently delete the spec files.
|
|
597
|
+
</p>
|
|
598
|
+
<div className="flex justify-end gap-2">
|
|
599
|
+
<button onClick={() => setDeletingSpec(null)} className="px-4 py-2 text-sm font-medium rounded-md hover:bg-secondary transition-colors">Cancel</button>
|
|
505
600
|
<button
|
|
506
|
-
disabled={!newSpecName.trim()}
|
|
507
601
|
onClick={async () => {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
await axios.post(`${runtimeConfig.api}/api/scaffold/spec`, { name });
|
|
512
|
-
const prompt = `Antigravity, let's shape the spec for '${name}'. Read commands/shape-spec/shape-spec.md.`;
|
|
513
|
-
await copyToClipboard(prompt);
|
|
514
|
-
setNewSpecName("");
|
|
515
|
-
setCreatingSpec(false);
|
|
516
|
-
await fetchStatus();
|
|
517
|
-
openFile(`specs/${name}/spec.md`, `${name} Spec`);
|
|
518
|
-
toast({ title: "Spec created & Prompt copied!", type: 'success' });
|
|
519
|
-
}
|
|
520
|
-
}
|
|
602
|
+
await axios.delete(`${runtimeConfig.api}/api/specs/${deletingSpec}`);
|
|
603
|
+
setDeletingSpec(null);
|
|
604
|
+
fetchStatus();
|
|
521
605
|
}}
|
|
522
|
-
className="px-4 py-2 text-sm font-medium rounded-md bg-
|
|
606
|
+
className="px-4 py-2 text-sm font-medium rounded-md bg-red-500 hover:bg-red-600 text-white transition-colors"
|
|
523
607
|
>
|
|
524
|
-
|
|
608
|
+
Delete Spec
|
|
525
609
|
</button>
|
|
526
610
|
</div>
|
|
527
611
|
</div>
|
|
528
612
|
</div>
|
|
529
613
|
</div>
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
</div>
|
|
540
|
-
<h3 className="font-semibold text-lg text-foreground">Delete Spec?</h3>
|
|
541
|
-
</div>
|
|
542
|
-
<p className="text-muted-foreground mb-6">
|
|
543
|
-
Are you sure you want to delete <strong>{deletingSpec}</strong>? This action cannot be undone and will permanently delete the spec files.
|
|
544
|
-
</p>
|
|
545
|
-
<div className="flex justify-end gap-2">
|
|
546
|
-
<button onClick={() => setDeletingSpec(null)} className="px-4 py-2 text-sm font-medium rounded-md hover:bg-secondary transition-colors">Cancel</button>
|
|
547
|
-
<button
|
|
548
|
-
onClick={async () => {
|
|
549
|
-
await axios.delete(`${runtimeConfig.api}/api/specs/${deletingSpec}`);
|
|
550
|
-
setDeletingSpec(null);
|
|
551
|
-
fetchStatus();
|
|
552
|
-
}}
|
|
553
|
-
className="px-4 py-2 text-sm font-medium rounded-md bg-red-500 hover:bg-red-600 text-white transition-colors"
|
|
554
|
-
>
|
|
555
|
-
Delete Spec
|
|
556
|
-
</button>
|
|
614
|
+
)
|
|
615
|
+
]}
|
|
616
|
+
|
|
617
|
+
{showDesignOS && (
|
|
618
|
+
<div className="fixed inset-0 z-[100] bg-background flex flex-col animate-fade-in w-screen h-screen">
|
|
619
|
+
<div className="h-14 border-b border-border flex items-center justify-between px-6 bg-card shrink-0 shadow-sm z-50">
|
|
620
|
+
<div className="flex items-center gap-3">
|
|
621
|
+
<div className="p-1.5 bg-sidebar-primary/10 text-sidebar-primary rounded-md">
|
|
622
|
+
<Layers size={20} />
|
|
557
623
|
</div>
|
|
624
|
+
<h2 className="font-semibold text-lg text-foreground">Design OS</h2>
|
|
625
|
+
<span className="text-muted-foreground text-sm border-l border-border pl-3">Design System & Handoff</span>
|
|
558
626
|
</div>
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
<div className="flex items-center gap-3">
|
|
568
|
-
<div className="p-1.5 bg-sidebar-primary/10 text-sidebar-primary rounded-md">
|
|
569
|
-
<Layers size={20} />
|
|
627
|
+
<div className="flex items-center gap-3">
|
|
628
|
+
<button
|
|
629
|
+
onClick={() => setShowDesignOS(false)}
|
|
630
|
+
className="p-2 text-muted-foreground hover:text-foreground hover:bg-secondary rounded-md transition-colors"
|
|
631
|
+
title="Close Design OS"
|
|
632
|
+
>
|
|
633
|
+
<X size={20} />
|
|
634
|
+
</button>
|
|
570
635
|
</div>
|
|
571
|
-
<h2 className="font-semibold text-lg text-foreground">Design OS</h2>
|
|
572
|
-
<span className="text-muted-foreground text-sm border-l border-border pl-3">Design System & Handoff</span>
|
|
573
636
|
</div>
|
|
574
|
-
<div className="flex
|
|
575
|
-
<
|
|
576
|
-
|
|
577
|
-
className="
|
|
578
|
-
title="
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
</button>
|
|
637
|
+
<div className="flex-1 bg-background relative">
|
|
638
|
+
<iframe
|
|
639
|
+
src={`http://localhost:5400?theme=${localStorage.getItem('theme') || 'system'}`}
|
|
640
|
+
className="absolute inset-0 w-full h-full border-none"
|
|
641
|
+
title="Design OS"
|
|
642
|
+
allow="clipboard-read; clipboard-write"
|
|
643
|
+
/>
|
|
582
644
|
</div>
|
|
583
|
-
</div>
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
src={`http://localhost:5400?theme=${localStorage.getItem('theme') || 'system'}`}
|
|
587
|
-
className="absolute inset-0 w-full h-full border-none"
|
|
588
|
-
title="Design OS"
|
|
589
|
-
allow="clipboard-read; clipboard-write"
|
|
590
|
-
/>
|
|
591
|
-
</div>
|
|
592
|
-
</div >
|
|
593
|
-
)
|
|
594
|
-
}
|
|
645
|
+
</div >
|
|
646
|
+
)
|
|
647
|
+
}
|
|
595
648
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
</div>
|
|
604
|
-
Mission Control
|
|
605
|
-
</h1>
|
|
606
|
-
<p className="text-muted-foreground font-mono text-xs opacity-70">Project: {state?.projectRoot}</p>
|
|
607
|
-
</div>
|
|
608
|
-
<div className="flex gap-3 items-center">
|
|
609
|
-
<ThemeToggle onThemeChange={(theme: string) => {
|
|
610
|
-
const iframe = document.querySelector('iframe[title="Design OS"]') as HTMLIFrameElement;
|
|
611
|
-
if (iframe?.contentWindow) {
|
|
612
|
-
iframe.contentWindow.postMessage({ type: 'THEME_CHANGE', theme }, '*');
|
|
613
|
-
}
|
|
614
|
-
}} />
|
|
615
|
-
<button className={`px-4 py-2 border rounded-lg flex items-center gap-2 transition text-sm font-medium ${showDesignOS ? 'bg-primary text-primary-foreground border-primary' : 'bg-background hover:bg-secondary border-border text-foreground'}`}
|
|
616
|
-
onClick={() => setShowDesignOS(!showDesignOS)}>
|
|
617
|
-
<Layers size={16} /> {showDesignOS ? 'Close Design OS' : 'Open Design OS'}
|
|
618
|
-
</button>
|
|
619
|
-
</div>
|
|
620
|
-
</header>
|
|
621
|
-
|
|
622
|
-
<div className="p-8 overflow-y-auto flex-1 bg-background/50">
|
|
623
|
-
<div className="max-w-7xl mx-auto">
|
|
624
|
-
{state && <NextStepCard state={state} onOpenDesign={() => setShowDesignOS(true)} />}
|
|
625
|
-
|
|
626
|
-
<main className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
627
|
-
{/* Phase 1: Product Strategy */}
|
|
628
|
-
<section className="bg-card border border-border rounded-xl p-5 shadow-sm h-full flex flex-col transition-all hover:shadow-md hover:border-border/80">
|
|
629
|
-
<div className="flex items-center gap-3 mb-5 pb-4 border-b border-border/50">
|
|
630
|
-
<div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
|
|
631
|
-
<LayoutDashboard size={20} />
|
|
632
|
-
</div>
|
|
633
|
-
<h2 className="text-lg font-semibold">1. Product Strategy</h2>
|
|
649
|
+
<header className="px-8 py-6 flex justify-between items-end border-b border-border bg-card/50 backdrop-blur-sm sticky top-0 z-10 shrink-0">
|
|
650
|
+
<div className="flex flex-col gap-4">
|
|
651
|
+
<h1 className="text-2xl font-semibold text-foreground tracking-tight flex items-center gap-3">
|
|
652
|
+
<div className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center text-primary-foreground">
|
|
653
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" className="w-5 h-5">
|
|
654
|
+
<path d="M50 20 L75 80 L50 65 L25 80 Z" fill="currentColor" stroke="none" />
|
|
655
|
+
</svg>
|
|
634
656
|
</div>
|
|
657
|
+
Mission Control
|
|
658
|
+
</h1>
|
|
659
|
+
<p className="text-muted-foreground font-mono text-xs opacity-70">Project: {state?.projectRoot}</p>
|
|
660
|
+
</div>
|
|
661
|
+
<div className="flex gap-3 items-center">
|
|
662
|
+
<ThemeToggle onThemeChange={(theme: string) => {
|
|
663
|
+
const iframe = document.querySelector('iframe[title="Design OS"]') as HTMLIFrameElement;
|
|
664
|
+
if (iframe?.contentWindow) {
|
|
665
|
+
iframe.contentWindow.postMessage({ type: 'THEME_CHANGE', theme }, '*');
|
|
666
|
+
}
|
|
667
|
+
}} />
|
|
668
|
+
<button className={`px-4 py-2 border rounded-lg flex items-center gap-2 transition text-sm font-medium ${showDesignOS ? 'bg-primary text-primary-foreground border-primary' : 'bg-background hover:bg-secondary border-border text-foreground'}`}
|
|
669
|
+
onClick={() => setShowDesignOS(!showDesignOS)}>
|
|
670
|
+
<Layers size={16} /> {showDesignOS ? 'Close Design OS' : 'Open Design OS'}
|
|
671
|
+
</button>
|
|
672
|
+
<button
|
|
673
|
+
onClick={() => setShowSettings(true)}
|
|
674
|
+
className="p-2 bg-background hover:bg-secondary border border-border rounded-lg text-foreground transition-colors"
|
|
675
|
+
title="Settings"
|
|
676
|
+
>
|
|
677
|
+
<Settings size={20} />
|
|
678
|
+
</button>
|
|
679
|
+
</div>
|
|
680
|
+
</header>
|
|
635
681
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
<StatusItem label="Tech Stack" status={state?.product?.techStack} icon={<Code size={16} />} onClick={() => openFile('product/tech-stack.md', 'Tech Stack')} />
|
|
640
|
-
</div>
|
|
682
|
+
<div className="p-8 overflow-y-auto flex-1 bg-background/50">
|
|
683
|
+
<div className="max-w-7xl mx-auto">
|
|
684
|
+
{state && <NextStepCard state={state} onOpenDesign={() => setShowDesignOS(true)} />}
|
|
641
685
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
686
|
+
<main className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
687
|
+
{/* Phase 1: Product Strategy */}
|
|
688
|
+
<section className="bg-card border border-border rounded-xl p-5 shadow-sm h-full flex flex-col transition-all hover:shadow-md hover:border-border/80">
|
|
689
|
+
<div className="flex items-center gap-3 mb-5 pb-4 border-b border-border/50">
|
|
690
|
+
<div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
|
|
691
|
+
<LayoutDashboard size={20} />
|
|
692
|
+
</div>
|
|
693
|
+
<h2 className="text-lg font-semibold">1. Product Strategy</h2>
|
|
694
|
+
</div>
|
|
651
695
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
<Layers size={20} />
|
|
696
|
+
<div className="space-y-3 flex-1">
|
|
697
|
+
<StatusItem label="Mission" status={state?.product?.mission} icon={<FileText size={16} />} onClick={() => openFile('product/mission.md', 'Product Mission')} />
|
|
698
|
+
<StatusItem label="Roadmap" status={state?.product?.roadmap} icon={<CheckSquare size={16} />} onClick={() => openFile('product/roadmap.md', 'Product Roadmap')} />
|
|
699
|
+
<StatusItem label="Tech Stack" status={state?.product?.techStack} icon={<Code size={16} />} onClick={() => openFile('product/tech-stack.md', 'Tech Stack')} />
|
|
657
700
|
</div>
|
|
658
|
-
<h2 className="text-lg font-semibold">2. Design System</h2>
|
|
659
|
-
</div>
|
|
660
701
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
)}
|
|
702
|
+
<div className="mt-6">
|
|
703
|
+
<PromptButton
|
|
704
|
+
label={state?.product?.mission?.exists && !state?.product?.mission?.isBoilerplate && state?.product?.roadmap?.exists && !state?.product?.roadmap?.isBoilerplate && state?.product?.techStack?.exists && !state?.product?.techStack?.isBoilerplate ? "Next: Design System" : "Plan Product"}
|
|
705
|
+
prompt={state?.product?.mission?.exists && !state?.product?.mission?.isBoilerplate && state?.product?.roadmap?.exists && !state?.product?.roadmap?.isBoilerplate && state?.product?.techStack?.exists && !state?.product?.techStack?.isBoilerplate ? "Antigravity, please sync my product plan to Design OS. Read 'agent-os/commands/initialize-design/initialize-design.md'." : "Antigravity, let's start Phase 1: Product Planning. Please read 'agent-os/commands/plan-product/plan-product.md' and guide me."}
|
|
706
|
+
onClick={copyToClipboard}
|
|
707
|
+
primary={state?.product?.mission?.exists && !state?.product?.mission?.isBoilerplate && state?.product?.roadmap?.exists && !state?.product?.roadmap?.isBoilerplate && state?.product?.techStack?.exists && !state?.product?.techStack?.isBoilerplate}
|
|
708
|
+
/>
|
|
669
709
|
</div>
|
|
710
|
+
</section>
|
|
670
711
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
<div className={`p-2 rounded-lg border text-center ${state?.design?.shell ? 'bg-emerald-50/50 border-emerald-200 text-emerald-700 dark:bg-emerald-900/20 dark:border-emerald-800 dark:text-emerald-300' : 'bg-secondary/20 border-border/50 text-muted-foreground'}`}>
|
|
677
|
-
<span className="text-xs font-medium block mb-1">App Shell</span>
|
|
678
|
-
{state?.design?.shell ? <CheckSquare size={14} className="mx-auto" /> : <span className="block w-2 h-2 rounded-full bg-stone-300 mx-auto mt-1" />}
|
|
712
|
+
{/* Phase 2: Design */}
|
|
713
|
+
<section className="bg-card border border-border rounded-xl p-5 shadow-sm h-full flex flex-col transition-all hover:shadow-md hover:border-border/80">
|
|
714
|
+
<div className="flex items-center gap-3 mb-5 pb-4 border-b border-border/50">
|
|
715
|
+
<div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
|
|
716
|
+
<Layers size={20} />
|
|
679
717
|
</div>
|
|
718
|
+
<h2 className="text-lg font-semibold">2. Design System</h2>
|
|
680
719
|
</div>
|
|
681
720
|
|
|
682
|
-
<div className="
|
|
683
|
-
<div className=
|
|
684
|
-
<span className="text-
|
|
685
|
-
{state?.design?.
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
721
|
+
<div className="space-y-4 flex-1">
|
|
722
|
+
<div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
|
|
723
|
+
<span className="text-foreground font-medium text-sm flex items-center gap-2"><RefreshCw size={16} className="text-muted-foreground" /> Data Sync</span>
|
|
724
|
+
{state?.design?.initialized ? (
|
|
725
|
+
<span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">Synced</span>
|
|
726
|
+
) : (
|
|
727
|
+
<span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
|
|
728
|
+
)}
|
|
690
729
|
</div>
|
|
691
|
-
</div>
|
|
692
730
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
<
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
</div>
|
|
731
|
+
<div className="grid grid-cols-2 gap-2">
|
|
732
|
+
<div className={`p-2 rounded-lg border text-center ${state?.design?.tokens ? 'bg-emerald-50/50 border-emerald-200 text-emerald-700 dark:bg-emerald-900/20 dark:border-emerald-800 dark:text-emerald-300' : 'bg-secondary/20 border-border/50 text-muted-foreground'}`}>
|
|
733
|
+
<span className="text-xs font-medium block mb-1">Tokens</span>
|
|
734
|
+
{state?.design?.tokens ? <CheckSquare size={14} className="mx-auto" /> : <span className="block w-2 h-2 rounded-full bg-stone-300 mx-auto mt-1" />}
|
|
735
|
+
</div>
|
|
736
|
+
<div className={`p-2 rounded-lg border text-center ${state?.design?.shell ? 'bg-emerald-50/50 border-emerald-200 text-emerald-700 dark:bg-emerald-900/20 dark:border-emerald-800 dark:text-emerald-300' : 'bg-secondary/20 border-border/50 text-muted-foreground'}`}>
|
|
737
|
+
<span className="text-xs font-medium block mb-1">App Shell</span>
|
|
738
|
+
{state?.design?.shell ? <CheckSquare size={14} className="mx-auto" /> : <span className="block w-2 h-2 rounded-full bg-stone-300 mx-auto mt-1" />}
|
|
739
|
+
</div>
|
|
740
|
+
</div>
|
|
704
741
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
{state?.design?.initialized && !state?.design?.exported && (
|
|
715
|
-
<div className="flex gap-2">
|
|
716
|
-
<PromptButton
|
|
717
|
-
label="Run Audit"
|
|
718
|
-
prompt="Antigravity, run an audit on the design system. Read 'design-system/.gemini/commands/impeccable/audit.md'."
|
|
719
|
-
onClick={copyToClipboard}
|
|
720
|
-
small
|
|
721
|
-
/>
|
|
722
|
-
<PromptButton
|
|
723
|
-
label="Run Polish"
|
|
724
|
-
prompt="Antigravity, run a polish pass. Read 'design-system/.gemini/commands/impeccable/polish.md'."
|
|
725
|
-
onClick={copyToClipboard}
|
|
726
|
-
small
|
|
727
|
-
/>
|
|
742
|
+
<div className="grid grid-cols-2 gap-2">
|
|
743
|
+
<div className={`p-2 rounded-lg border text-center ${state?.design?.qa?.audit ? 'bg-emerald-50/50 border-emerald-200 text-emerald-700 dark:bg-emerald-900/20 dark:border-emerald-800 dark:text-emerald-300' : 'bg-secondary/20 border-border/50 text-muted-foreground'}`}>
|
|
744
|
+
<span className="text-xs font-medium block mb-1">Audit</span>
|
|
745
|
+
{state?.design?.qa?.audit ? <CheckSquare size={14} className="mx-auto" /> : <span className="block w-2 h-2 rounded-full bg-stone-300 mx-auto mt-1" />}
|
|
746
|
+
</div>
|
|
747
|
+
<div className={`p-2 rounded-lg border text-center ${state?.design?.qa?.polish ? 'bg-emerald-50/50 border-emerald-200 text-emerald-700 dark:bg-emerald-900/20 dark:border-emerald-800 dark:text-emerald-300' : 'bg-secondary/20 border-border/50 text-muted-foreground'}`}>
|
|
748
|
+
<span className="text-xs font-medium block mb-1">Polish</span>
|
|
749
|
+
{state?.design?.qa?.polish ? <CheckSquare size={14} className="mx-auto" /> : <span className="block w-2 h-2 rounded-full bg-stone-300 mx-auto mt-1" />}
|
|
750
|
+
</div>
|
|
728
751
|
</div>
|
|
729
|
-
)}
|
|
730
|
-
</div>
|
|
731
|
-
</section>
|
|
732
752
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
753
|
+
<div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
|
|
754
|
+
<span className="text-foreground font-medium text-sm flex items-center gap-2"><ArrowRight size={16} className="text-muted-foreground" /> Export</span>
|
|
755
|
+
{state?.design?.exported ? (
|
|
756
|
+
<span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">Done</span>
|
|
757
|
+
) : state?.design?.exists ? (
|
|
758
|
+
<span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">Ready</span>
|
|
759
|
+
) : (
|
|
760
|
+
<span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
|
|
761
|
+
)}
|
|
739
762
|
</div>
|
|
740
|
-
<h2 className="text-lg font-semibold">3. Feature Specs</h2>
|
|
741
763
|
</div>
|
|
742
|
-
<button className="text-xs bg-secondary hover:bg-secondary/80 border border-border px-3 py-2 rounded-md flex items-center gap-1.5 font-medium transition cursor-pointer"
|
|
743
|
-
onClick={() => setCreatingSpec(true)}>
|
|
744
|
-
<Plus size={14} /> New
|
|
745
|
-
</button>
|
|
746
|
-
</div>
|
|
747
764
|
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
Ready to generate specs from roadmap items?
|
|
757
|
-
</p>
|
|
758
|
-
<PromptButton
|
|
759
|
-
label="Generate All Specs"
|
|
760
|
-
prompt={`Antigravity, generate feature specs from the roadmap items. For each item in 'agent-os/product/roadmap.md' that is not yet completed, create a spec folder in 'agent-os/specs/[feature-name]/' with spec.md and tasks.md files. Context: 1. Read 'product-plan/product-overview.md' for data model/flows. 2. Read 'design-system/design-tokens.md' and 'design-system/app-shell.md' for UI/UX constraints. 3. Use the shape-spec command: Read 'agent-os/commands/shape-spec/shape-spec.md'.`}
|
|
761
|
-
onClick={copyToClipboard}
|
|
762
|
-
small
|
|
763
|
-
primary
|
|
764
|
-
/>
|
|
765
|
-
</div>
|
|
765
|
+
<div className="mt-6 flex flex-col gap-2">
|
|
766
|
+
{!state?.design?.initialized && (
|
|
767
|
+
<PromptButton
|
|
768
|
+
label="Sync Data"
|
|
769
|
+
prompt="Antigravity, please sync my product plan to Design OS. Read 'agent-os/commands/initialize-design/initialize-design.md'."
|
|
770
|
+
onClick={copyToClipboard}
|
|
771
|
+
primary
|
|
772
|
+
/>
|
|
766
773
|
)}
|
|
767
|
-
|
|
768
|
-
<div key={spec.name} className="bg-secondary/20 p-4 rounded-lg border border-border/50 hover:border-border transition-colors group relative">
|
|
769
|
-
<button
|
|
770
|
-
onClick={(e) => {
|
|
771
|
-
e.stopPropagation();
|
|
772
|
-
setDeletingSpec(spec.name);
|
|
773
|
-
}}
|
|
774
|
-
className="absolute top-4 right-4 text-muted-foreground hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1"
|
|
775
|
-
title="Delete Spec"
|
|
776
|
-
>
|
|
777
|
-
<Trash2 size={16} />
|
|
778
|
-
</button>
|
|
779
|
-
<h3 className="font-medium text-base mb-3 flex items-center gap-2">
|
|
780
|
-
<WalletCards size={16} className="text-muted-foreground" />
|
|
781
|
-
{spec.name}
|
|
782
|
-
</h3>
|
|
783
|
-
<div className="space-y-2 mb-4">
|
|
784
|
-
<StatusItem label="Spec" status={spec.spec} icon={<FileText size={14} />} small onClick={() => openFile(`specs/${spec.name}/spec.md`, `${spec.name} Spec`)} />
|
|
785
|
-
<StatusItem label="Tasks" status={spec.tasks} icon={<CheckSquare size={14} />} small onClick={() => openFile(`specs/${spec.name}/tasks.md`, `${spec.name} Tasks`)} />
|
|
786
|
-
</div>
|
|
774
|
+
{state?.design?.initialized && !state?.design?.exported && (
|
|
787
775
|
<div className="flex gap-2">
|
|
788
776
|
<PromptButton
|
|
789
|
-
label="
|
|
790
|
-
prompt=
|
|
777
|
+
label="Run Audit"
|
|
778
|
+
prompt="Antigravity, run an audit on the design system. Read 'design-system/.gemini/commands/impeccable/audit.md'."
|
|
791
779
|
onClick={copyToClipboard}
|
|
792
780
|
small
|
|
793
781
|
/>
|
|
794
782
|
<PromptButton
|
|
795
|
-
label="
|
|
796
|
-
prompt=
|
|
783
|
+
label="Run Polish"
|
|
784
|
+
prompt="Antigravity, run a polish pass. Read 'design-system/.gemini/commands/impeccable/polish.md'."
|
|
797
785
|
onClick={copyToClipboard}
|
|
798
786
|
small
|
|
799
|
-
primary
|
|
800
787
|
/>
|
|
801
788
|
</div>
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
<div key={item.name} className="border border-dashed border-border/60 p-4 rounded-lg bg-secondary/5 hover:bg-secondary/10 transition-colors">
|
|
813
|
-
<div className="flex justify-between items-start mb-2">
|
|
814
|
-
<h3 className="font-medium text-base flex items-center gap-2 text-muted-foreground w-full">
|
|
815
|
-
<div className="w-2 h-2 rounded-full bg-stone-300 shrink-0" />
|
|
816
|
-
<span className="truncate" title={item.name}>{item.name}</span>
|
|
817
|
-
</h3>
|
|
818
|
-
<span className="text-[10px] uppercase tracking-wider font-semibold text-muted-foreground bg-secondary px-1.5 py-0.5 rounded shrink-0 ml-2">Planned</span>
|
|
789
|
+
)}
|
|
790
|
+
</div>
|
|
791
|
+
</section>
|
|
792
|
+
|
|
793
|
+
{/* Phase 3: Feature Specs */}
|
|
794
|
+
<section className="bg-card border border-border rounded-xl p-5 shadow-sm col-span-1 md:col-span-2 lg:col-span-1 h-full flex flex-col transition-all hover:shadow-md hover:border-border/80">
|
|
795
|
+
<div className="flex items-center justify-between mb-5 pb-4 border-b border-border/50">
|
|
796
|
+
<div className="flex items-center gap-3">
|
|
797
|
+
<div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
|
|
798
|
+
<Code size={20} />
|
|
819
799
|
</div>
|
|
820
|
-
<
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
800
|
+
<h2 className="text-lg font-semibold">3. Feature Specs</h2>
|
|
801
|
+
</div>
|
|
802
|
+
<button className="text-xs bg-secondary hover:bg-secondary/80 border border-border px-3 py-2 rounded-md flex items-center gap-1.5 font-medium transition cursor-pointer"
|
|
803
|
+
onClick={() => setCreatingSpec(true)}>
|
|
804
|
+
<Plus size={14} /> New
|
|
805
|
+
</button>
|
|
806
|
+
</div>
|
|
807
|
+
|
|
808
|
+
<div className="space-y-3 flex-1 overflow-y-auto max-h-[300px] pr-1">
|
|
809
|
+
{/* Auto-generate from Roadmap after Design Export */}
|
|
810
|
+
{(state?.product?.roadmap?.items?.filter(item =>
|
|
811
|
+
!item.completed &&
|
|
812
|
+
!state?.product?.roadmap?.isBoilerplate
|
|
813
|
+
) ?? []).length > 0 && !state?.specs?.length && (
|
|
814
|
+
<div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 p-4 rounded-lg mb-3">
|
|
815
|
+
<p className="text-sm text-blue-700 dark:text-blue-300 mb-2">
|
|
816
|
+
Ready to generate specs from roadmap items?
|
|
817
|
+
</p>
|
|
818
|
+
<PromptButton
|
|
819
|
+
label="Generate All Specs"
|
|
820
|
+
prompt={`Antigravity, generate feature specs from the roadmap items. For each item in 'agent-os/product/roadmap.md' that is not yet completed, create a spec folder in 'agent-os/specs/[feature-name]/' with spec.md and tasks.md files. Context: 1. Read 'product-plan/product-overview.md' for data model/flows. 2. Read 'design-system/design-tokens.md' and 'design-system/app-shell.md' for UI/UX constraints. 3. Use the shape-spec command: Read 'agent-os/commands/shape-spec/shape-spec.md'.`}
|
|
821
|
+
onClick={copyToClipboard}
|
|
822
|
+
small
|
|
823
|
+
primary
|
|
824
|
+
/>
|
|
825
|
+
</div>
|
|
826
|
+
)}
|
|
827
|
+
{state?.specs?.map(spec => (
|
|
828
|
+
<div key={spec.name} className="bg-secondary/20 p-4 rounded-lg border border-border/50 hover:border-border transition-colors group relative">
|
|
824
829
|
<button
|
|
825
|
-
onClick={() => {
|
|
826
|
-
|
|
827
|
-
|
|
830
|
+
onClick={(e) => {
|
|
831
|
+
e.stopPropagation();
|
|
832
|
+
setDeletingSpec(spec.name);
|
|
828
833
|
}}
|
|
829
|
-
className="
|
|
834
|
+
className="absolute top-4 right-4 text-muted-foreground hover:text-red-500 opacity-0 group-hover:opacity-100 transition-all p-1"
|
|
835
|
+
title="Delete Spec"
|
|
830
836
|
>
|
|
831
|
-
<
|
|
837
|
+
<Trash2 size={16} />
|
|
832
838
|
</button>
|
|
839
|
+
<h3 className="font-medium text-base mb-3 flex items-center gap-2">
|
|
840
|
+
<WalletCards size={16} className="text-muted-foreground" />
|
|
841
|
+
{spec.name}
|
|
842
|
+
</h3>
|
|
843
|
+
<div className="space-y-2 mb-4">
|
|
844
|
+
<StatusItem label="Spec" status={spec.spec} icon={<FileText size={14} />} small onClick={() => openFile(`specs/${spec.name}/spec.md`, `${spec.name} Spec`)} />
|
|
845
|
+
<StatusItem label="Tasks" status={spec.tasks} icon={<CheckSquare size={14} />} small onClick={() => openFile(`specs/${spec.name}/tasks.md`, `${spec.name} Tasks`)} />
|
|
846
|
+
</div>
|
|
847
|
+
<div className="flex gap-2">
|
|
848
|
+
<PromptButton
|
|
849
|
+
label="Shape"
|
|
850
|
+
prompt={`Antigravity, let's shape the spec for '${spec.name}'. Read commands/shape-spec/shape-spec.md.`}
|
|
851
|
+
onClick={copyToClipboard}
|
|
852
|
+
small
|
|
853
|
+
/>
|
|
854
|
+
<PromptButton
|
|
855
|
+
label="Implement"
|
|
856
|
+
prompt={`Antigravity, implement the tasks for '${spec.name}'. Read commands/implement-tasks/implement-tasks.md.`}
|
|
857
|
+
onClick={copyToClipboard}
|
|
858
|
+
small
|
|
859
|
+
primary
|
|
860
|
+
/>
|
|
861
|
+
</div>
|
|
833
862
|
</div>
|
|
834
|
-
|
|
835
|
-
))}
|
|
863
|
+
))}
|
|
836
864
|
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
>
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
865
|
+
{/* Pending Roadmap Items */}
|
|
866
|
+
{/* Pending Roadmap Items */}
|
|
867
|
+
{state?.product?.roadmap?.items?.filter(item =>
|
|
868
|
+
!item.completed &&
|
|
869
|
+
!state?.product?.roadmap?.isBoilerplate &&
|
|
870
|
+
!state?.specs?.some(s => s.name.toLowerCase().includes(item.name.toLowerCase().trim().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '')))
|
|
871
|
+
).map(item => (
|
|
872
|
+
<div key={item.name} className="border border-dashed border-border/60 p-4 rounded-lg bg-secondary/5 hover:bg-secondary/10 transition-colors">
|
|
873
|
+
<div className="flex justify-between items-start mb-2">
|
|
874
|
+
<h3 className="font-medium text-base flex items-center gap-2 text-muted-foreground w-full">
|
|
875
|
+
<div className="w-2 h-2 rounded-full bg-stone-300 shrink-0" />
|
|
876
|
+
<span className="truncate" title={item.name}>{item.name}</span>
|
|
877
|
+
</h3>
|
|
878
|
+
<span className="text-[10px] uppercase tracking-wider font-semibold text-muted-foreground bg-secondary px-1.5 py-0.5 rounded shrink-0 ml-2">Planned</span>
|
|
879
|
+
</div>
|
|
880
|
+
<p className="text-xs text-muted-foreground mb-4 pl-4 opacity-70">
|
|
881
|
+
Defined in Product Roadmap. Ready to spec.
|
|
882
|
+
</p>
|
|
883
|
+
<div className="pl-4">
|
|
884
|
+
<button
|
|
885
|
+
onClick={() => {
|
|
886
|
+
setNewSpecName(item.name.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, ''));
|
|
887
|
+
setCreatingSpec(true);
|
|
888
|
+
}}
|
|
889
|
+
className="text-xs bg-background hover:bg-secondary border border-border px-3 py-1.5 rounded-md flex items-center gap-1.5 font-medium transition cursor-pointer w-full justify-center group"
|
|
890
|
+
>
|
|
891
|
+
<Plus size={12} className="group-hover:text-primary transition-colors" /> Shape Spec
|
|
892
|
+
</button>
|
|
893
|
+
</div>
|
|
894
|
+
</div>
|
|
895
|
+
))}
|
|
849
896
|
|
|
897
|
+
{/* Empty State */}
|
|
898
|
+
{(!state?.specs?.length && (!state?.product?.roadmap?.items?.length || state?.product?.roadmap?.isBoilerplate)) && (
|
|
899
|
+
<div className="border border-dashed border-border/60 p-8 rounded-lg text-center h-40 flex flex-col items-center justify-center">
|
|
900
|
+
<p className="text-muted-foreground text-sm mb-3">No specs found yet.</p>
|
|
901
|
+
<button
|
|
902
|
+
onClick={() => setCreatingSpec(true)}
|
|
903
|
+
className="text-sm px-4 py-2 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors"
|
|
904
|
+
>
|
|
905
|
+
Create your first spec
|
|
906
|
+
</button>
|
|
907
|
+
</div>
|
|
908
|
+
)}
|
|
850
909
|
|
|
851
|
-
</div>
|
|
852
|
-
</section>
|
|
853
910
|
|
|
854
|
-
{/* Phase 4: Implementation */}
|
|
855
|
-
<section className="bg-card border border-border rounded-xl p-5 shadow-sm h-full flex flex-col transition-all hover:shadow-md hover:border-border/80">
|
|
856
|
-
<div className="flex items-center gap-3 mb-5 pb-4 border-b border-border/50">
|
|
857
|
-
<div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
|
|
858
|
-
<Package size={20} />
|
|
859
911
|
</div>
|
|
860
|
-
|
|
861
|
-
</div>
|
|
912
|
+
</section>
|
|
862
913
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
<div className="flex items-center
|
|
866
|
-
<
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
<span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
|
|
871
|
-
)}
|
|
914
|
+
{/* Phase 4: Implementation */}
|
|
915
|
+
<section className="bg-card border border-border rounded-xl p-5 shadow-sm h-full flex flex-col transition-all hover:shadow-md hover:border-border/80">
|
|
916
|
+
<div className="flex items-center gap-3 mb-5 pb-4 border-b border-border/50">
|
|
917
|
+
<div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
|
|
918
|
+
<Package size={20} />
|
|
919
|
+
</div>
|
|
920
|
+
<h2 className="text-lg font-semibold">4. Implementation</h2>
|
|
872
921
|
</div>
|
|
873
922
|
|
|
874
|
-
|
|
875
|
-
|
|
923
|
+
<div className="space-y-3 flex-1">
|
|
924
|
+
{/* Scaffold Status */}
|
|
876
925
|
<div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
|
|
877
|
-
<span className="text-foreground font-medium text-sm flex items-center gap-2">
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
<span className="text-amber-600 bg-amber-50 dark:bg-amber-900/20 px-2 py-0.5 rounded text-xs font-medium border border-amber-200 dark:border-amber-800">
|
|
884
|
-
{state.implementation.git.uncommitted} uncommitted
|
|
885
|
-
</span>
|
|
886
|
-
)}
|
|
887
|
-
{state.implementation.git.uncommitted === 0 && (
|
|
888
|
-
<span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">Clean</span>
|
|
889
|
-
)}
|
|
890
|
-
</div>
|
|
926
|
+
<span className="text-foreground font-medium text-sm flex items-center gap-2"><Layout size={16} className="text-muted-foreground" /> Scaffold</span>
|
|
927
|
+
{state?.implementation?.scaffolded ? (
|
|
928
|
+
<span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">Done</span>
|
|
929
|
+
) : (
|
|
930
|
+
<span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
|
|
931
|
+
)}
|
|
891
932
|
</div>
|
|
892
|
-
)}
|
|
893
933
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
: state.implementation.coverage >= 50
|
|
912
|
-
? 'text-amber-600 bg-amber-50 dark:bg-amber-900/20 border-amber-200 dark:border-amber-800'
|
|
913
|
-
: 'text-red-600 bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800'
|
|
914
|
-
}`}>
|
|
915
|
-
{state.implementation.coverage}% coverage
|
|
916
|
-
</span>
|
|
917
|
-
)}
|
|
934
|
+
{/* Git Status */}
|
|
935
|
+
{state?.implementation?.git?.initialized && (
|
|
936
|
+
<div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
|
|
937
|
+
<span className="text-foreground font-medium text-sm flex items-center gap-2">
|
|
938
|
+
<svg className="w-4 h-4 text-muted-foreground" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" /></svg>
|
|
939
|
+
{state.implementation.git.branch || 'main'}
|
|
940
|
+
</span>
|
|
941
|
+
<div className="flex items-center gap-2">
|
|
942
|
+
{state.implementation.git.uncommitted > 0 && (
|
|
943
|
+
<span className="text-amber-600 bg-amber-50 dark:bg-amber-900/20 px-2 py-0.5 rounded text-xs font-medium border border-amber-200 dark:border-amber-800">
|
|
944
|
+
{state.implementation.git.uncommitted} uncommitted
|
|
945
|
+
</span>
|
|
946
|
+
)}
|
|
947
|
+
{state.implementation.git.uncommitted === 0 && (
|
|
948
|
+
<span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">Clean</span>
|
|
949
|
+
)}
|
|
950
|
+
</div>
|
|
918
951
|
</div>
|
|
919
|
-
|
|
920
|
-
)}
|
|
952
|
+
)}
|
|
921
953
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
954
|
+
{/* Tests & Coverage */}
|
|
955
|
+
{state?.implementation?.scaffolded && (
|
|
956
|
+
<div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
|
|
957
|
+
<span className="text-foreground font-medium text-sm flex items-center gap-2">
|
|
958
|
+
<CheckSquare size={16} className="text-muted-foreground" /> Tests
|
|
959
|
+
</span>
|
|
960
|
+
<div className="flex items-center gap-2">
|
|
961
|
+
{state?.implementation?.tests?.hasTests ? (
|
|
962
|
+
<span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">
|
|
963
|
+
{state.implementation.tests.count} files
|
|
964
|
+
</span>
|
|
965
|
+
) : (
|
|
966
|
+
<span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">No tests</span>
|
|
967
|
+
)}
|
|
968
|
+
{state?.implementation?.coverage !== null && state?.implementation?.coverage !== undefined && (
|
|
969
|
+
<span className={`px-2 py-0.5 rounded text-xs font-medium border ${state.implementation.coverage >= 80
|
|
970
|
+
? 'text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 border-emerald-200 dark:border-emerald-800'
|
|
971
|
+
: state.implementation.coverage >= 50
|
|
972
|
+
? 'text-amber-600 bg-amber-50 dark:bg-amber-900/20 border-amber-200 dark:border-amber-800'
|
|
973
|
+
: 'text-red-600 bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800'
|
|
974
|
+
}`}>
|
|
975
|
+
{state.implementation.coverage}% coverage
|
|
976
|
+
</span>
|
|
977
|
+
)}
|
|
978
|
+
</div>
|
|
979
|
+
</div>
|
|
980
|
+
)}
|
|
937
981
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
982
|
+
{/* Spec Progress */}
|
|
983
|
+
{state?.implementation?.specs && state.implementation.specs.total > 0 && (
|
|
984
|
+
<div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
|
|
985
|
+
<span className="text-foreground font-medium text-sm flex items-center gap-2">
|
|
986
|
+
<FileText size={16} className="text-muted-foreground" /> Specs
|
|
987
|
+
</span>
|
|
988
|
+
<span className={`px-2 py-0.5 rounded text-xs font-medium border ${state.implementation.specs.completed === state.implementation.specs.total
|
|
989
|
+
? 'text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 border-emerald-200 dark:border-emerald-800'
|
|
990
|
+
: 'text-blue-600 bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800'
|
|
991
|
+
}`}>
|
|
992
|
+
{state.implementation.specs.completed}/{state.implementation.specs.total} complete
|
|
993
|
+
</span>
|
|
994
|
+
</div>
|
|
995
|
+
)}
|
|
996
|
+
</div>
|
|
950
997
|
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
<
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
/>
|
|
962
|
-
{state?.design?.exportPrompts?.section && (
|
|
963
|
-
<div className="relative">
|
|
964
|
-
<PromptButton
|
|
965
|
-
label="Option B: Incremental (Section)"
|
|
966
|
-
prompt={`Antigravity, implement a section. Read 'product-plan/prompts/section-prompt.md'. Use Context7 for latest docs if available.`}
|
|
967
|
-
onClick={copyToClipboard}
|
|
968
|
-
small
|
|
969
|
-
/>
|
|
970
|
-
<p className="text-[10px] text-muted-foreground mt-1.5 text-center leading-tight">
|
|
971
|
-
Build by spec from <strong>Feature Specs</strong> ←
|
|
972
|
-
</p>
|
|
973
|
-
</div>
|
|
974
|
-
)}
|
|
998
|
+
<div className="mt-6">
|
|
999
|
+
{!state?.implementation?.scaffolded ? (
|
|
1000
|
+
<PromptButton
|
|
1001
|
+
label="Scaffold App"
|
|
1002
|
+
prompt="Antigravity, scaffold the implementation. Read 'agent-os/commands/scaffold-implementation/scaffold-implementation.md'."
|
|
1003
|
+
onClick={copyToClipboard}
|
|
1004
|
+
/>
|
|
1005
|
+
) : (
|
|
1006
|
+
<div className="p-3 bg-secondary/30 rounded-lg border border-border/50 text-sm text-muted-foreground text-center italic mb-4">
|
|
1007
|
+
App scaffolded. Ready for implementation.
|
|
975
1008
|
</div>
|
|
976
|
-
|
|
977
|
-
)}
|
|
978
|
-
</div>
|
|
979
|
-
</section>
|
|
1009
|
+
)}
|
|
980
1010
|
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1011
|
+
{state?.implementation?.scaffolded && state?.design?.exportPrompts?.oneShot && (
|
|
1012
|
+
<div className="pt-3 border-t border-border/50">
|
|
1013
|
+
<p className="text-xs text-muted-foreground mb-2 font-medium">Implementation Options:</p>
|
|
1014
|
+
<div className="space-y-2">
|
|
1015
|
+
<PromptButton
|
|
1016
|
+
label="Option A: One-Shot (All)"
|
|
1017
|
+
prompt={`Antigravity, implement the app. Read 'product-plan/prompts/one-shot-prompt.md'. If Context7 is configured, use it to verify library documentation.`}
|
|
1018
|
+
onClick={copyToClipboard}
|
|
1019
|
+
small
|
|
1020
|
+
primary
|
|
1021
|
+
/>
|
|
1022
|
+
{state?.design?.exportPrompts?.section && (
|
|
1023
|
+
<div className="relative">
|
|
1024
|
+
<PromptButton
|
|
1025
|
+
label="Option B: Incremental (Section)"
|
|
1026
|
+
prompt={`Antigravity, implement a section. Read 'product-plan/prompts/section-prompt.md'. Use Context7 for latest docs if available.`}
|
|
1027
|
+
onClick={copyToClipboard}
|
|
1028
|
+
small
|
|
1029
|
+
/>
|
|
1030
|
+
<p className="text-[10px] text-muted-foreground mt-1.5 text-center leading-tight">
|
|
1031
|
+
Build by spec from <strong>Feature Specs</strong> ←
|
|
1032
|
+
</p>
|
|
1033
|
+
</div>
|
|
1034
|
+
)}
|
|
1035
|
+
</div>
|
|
1036
|
+
</div>
|
|
1037
|
+
)}
|
|
1038
|
+
</div>
|
|
1039
|
+
</section>
|
|
1040
|
+
|
|
1041
|
+
</main>
|
|
1042
|
+
</div>
|
|
998
1043
|
</div>
|
|
999
|
-
|
|
1000
|
-
|
|
1044
|
+
<footer className="h-8 border-t border-border/40 bg-card px-4 flex items-center gap-6 text-[10px] font-medium text-muted-foreground fixed bottom-0 w-full z-50">
|
|
1045
|
+
<div className="flex items-center gap-2">
|
|
1046
|
+
<div className={`w-1.5 h-1.5 rounded-full ${state?.services?.api ? 'bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.4)]' : 'bg-red-500 animate-pulse'}`} />
|
|
1047
|
+
<span>Control Center API ({runtimeConfig?.ports?.api || 5403})</span>
|
|
1048
|
+
</div>
|
|
1049
|
+
<div className="h-3 w-[1px] bg-border" />
|
|
1050
|
+
<div className="flex items-center gap-2">
|
|
1051
|
+
<div className={`w-1.5 h-1.5 rounded-full ${state?.services?.design ? 'bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.4)]' : 'bg-red-500'}`} />
|
|
1052
|
+
<span>Design OS ({runtimeConfig?.ports?.design || 5400})</span>
|
|
1053
|
+
</div>
|
|
1054
|
+
<div className="h-3 w-[1px] bg-border" />
|
|
1055
|
+
<div className="flex items-center gap-2">
|
|
1056
|
+
<div className={`w-1.5 h-1.5 rounded-full ${state?.services?.app ? 'bg-emerald-500 shadow-[0_0_6px_rgba(16,185,129,0.4)]' : 'bg-red-500'}`} />
|
|
1057
|
+
<span>App ({runtimeConfig?.ports?.app || 5402})</span>
|
|
1058
|
+
</div>
|
|
1059
|
+
</footer>
|
|
1060
|
+
</div >
|
|
1061
|
+
</IdeContext.Provider>
|
|
1001
1062
|
);
|
|
1002
1063
|
}
|
|
1003
1064
|
|