ebade 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +49 -38
  3. package/ROADMAP.md +19 -14
  4. package/cli/scaffold.js +502 -186
  5. package/cli/simulate.js +102 -0
  6. package/cli/templates/feature-grid.tsx +80 -0
  7. package/cli/templates/footer.tsx +121 -0
  8. package/cli/templates/hero-section.tsx +34 -0
  9. package/cli/templates/login-form.tsx +124 -0
  10. package/cli/templates/navbar.tsx +53 -0
  11. package/cli/templates/pricing-table.tsx +140 -0
  12. package/cli/templates/signup-form.tsx +111 -0
  13. package/demo.tape +2 -2
  14. package/examples/saas-dashboard.ebade.yaml +2 -0
  15. package/netlify.toml +7 -0
  16. package/package.json +3 -1
  17. package/packages/mcp-server/README.md +3 -3
  18. package/packages/mcp-server/package.json +2 -2
  19. package/packages/mcp-server/src/index.ts +12 -16
  20. package/packages/mcp-server/src/tools/scaffold.ts +153 -404
  21. package/packages/vscode-extension/README.md +45 -0
  22. package/packages/vscode-extension/ebade-0.1.0.vsix +0 -0
  23. package/packages/vscode-extension/ebade-0.3.0.vsix +0 -0
  24. package/packages/vscode-extension/ebade-0.3.1.vsix +0 -0
  25. package/packages/vscode-extension/ebade-0.3.2.vsix +0 -0
  26. package/packages/vscode-extension/images/icon.png +0 -0
  27. package/packages/vscode-extension/language-configuration.json +24 -0
  28. package/packages/vscode-extension/package.json +54 -0
  29. package/packages/vscode-extension/snippets/ebade.json +86 -0
  30. package/packages/vscode-extension/syntaxes/ebade.tmLanguage.json +54 -0
  31. package/www/README.md +36 -0
  32. package/www/app/favicon.ico +0 -0
  33. package/www/app/globals.css +1256 -0
  34. package/www/app/layout.tsx +66 -0
  35. package/www/app/page.tsx +374 -0
  36. package/www/app/playground/page.tsx +627 -0
  37. package/www/components/ThreeCanvas.tsx +156 -0
  38. package/www/next.config.ts +19 -0
  39. package/www/package-lock.json +1779 -0
  40. package/www/package.json +27 -0
  41. package/www/postcss.config.mjs +7 -0
  42. package/www/public/logo.png +0 -0
  43. package/www/tsconfig.json +42 -0
  44. package/landing/index.html +0 -237
  45. package/landing/main.js +0 -147
  46. package/landing/style.css +0 -616
  47. /package/{demo.gif → assets/demo.gif} +0 -0
  48. /package/{demo.mp4 → assets/demo.mp4} +0 -0
  49. /package/{landing → www/public}/_headers +0 -0
  50. /package/{landing → www/public}/favicon.svg +0 -0
  51. /package/{landing → www/public}/og-image.png +0 -0
  52. /package/{landing → www/public}/og-readme.png +0 -0
@@ -0,0 +1,627 @@
1
+ "use client";
2
+
3
+ import Link from "next/link";
4
+ import { Zap, Leaf, Clock, ArrowLeft, Play } from "lucide-react";
5
+ import { useState, useRef, useEffect } from "react";
6
+
7
+ export default function PlaygroundPage() {
8
+ const [isRunning, setIsRunning] = useState(false);
9
+ const [phase, setPhase] = useState(0);
10
+ const [legacyTokens, setLegacyTokens] = useState(0);
11
+ const [legacyTime, setLegacyTime] = useState(0);
12
+ const [ebadeTime, setEbadeTime] = useState<number | null>(null);
13
+ const startTimeRef = useRef<number>(0);
14
+
15
+ // Refs for auto-scroll
16
+ const processingRef = useRef<HTMLDivElement>(null);
17
+ const statsRef = useRef<HTMLDivElement>(null);
18
+
19
+ // Auto-scroll on phase change
20
+ useEffect(() => {
21
+ if (phase === 1 && processingRef.current) {
22
+ processingRef.current.scrollIntoView({
23
+ behavior: "smooth",
24
+ block: "center",
25
+ });
26
+ }
27
+ if (phase === 3 && statsRef.current) {
28
+ setTimeout(() => {
29
+ statsRef.current?.scrollIntoView({
30
+ behavior: "smooth",
31
+ block: "start",
32
+ });
33
+ }, 300);
34
+ }
35
+ }, [phase]);
36
+
37
+ const runSimulation = () => {
38
+ setIsRunning(true);
39
+ setPhase(1);
40
+ setLegacyTokens(0);
41
+ setLegacyTime(0);
42
+ setEbadeTime(null);
43
+ startTimeRef.current = Date.now();
44
+
45
+ // ebade finishes almost instantly (500ms)
46
+ setTimeout(() => {
47
+ setEbadeTime(1.51);
48
+ }, 500);
49
+
50
+ // Legacy AI burns tokens slowly + track time
51
+ let tokens = 0;
52
+ const legacyInterval = setInterval(() => {
53
+ tokens += Math.floor(Math.random() * 150) + 50;
54
+ setLegacyTokens(tokens);
55
+ setLegacyTime((Date.now() - startTimeRef.current) / 1000);
56
+ if (tokens >= 4200) {
57
+ clearInterval(legacyInterval);
58
+ setPhase(3);
59
+ setIsRunning(false);
60
+ }
61
+ }, 100);
62
+ };
63
+
64
+ return (
65
+ <div
66
+ className="page-wrapper"
67
+ style={{ background: "#000", minHeight: "100vh" }}
68
+ >
69
+ <nav className="full-nav" style={{ background: "rgba(0,0,0,0.8)" }}>
70
+ <Link href="/" className="logo" style={{ color: "#fff" }}>
71
+ ebade<span>.dev</span>
72
+ </Link>
73
+ <Link
74
+ href="/"
75
+ style={{
76
+ color: "var(--text-dim)",
77
+ display: "flex",
78
+ alignItems: "center",
79
+ gap: "0.5rem",
80
+ textDecoration: "none",
81
+ }}
82
+ >
83
+ <ArrowLeft size={16} /> Back to Home
84
+ </Link>
85
+ </nav>
86
+
87
+ <main style={{ paddingTop: "8rem" }}>
88
+ <section
89
+ className="section-container"
90
+ style={{ textAlign: "center", paddingBottom: "4rem" }}
91
+ >
92
+ <div className="badge-accent">Agentic Simulation</div>
93
+ <h2
94
+ style={{
95
+ color: "#fff",
96
+ fontSize: "3rem",
97
+ fontWeight: 900,
98
+ letterSpacing: "-2px",
99
+ marginBottom: "1rem",
100
+ }}
101
+ >
102
+ One Prompt,{" "}
103
+ <span style={{ color: "var(--primary)" }}>Two Paths</span>
104
+ </h2>
105
+ <p
106
+ style={{
107
+ color: "rgba(255,255,255,0.5)",
108
+ maxWidth: "600px",
109
+ margin: "0 auto 2rem",
110
+ }}
111
+ >
112
+ You ask an AI agent to build something. Watch how{" "}
113
+ <strong style={{ color: "#ef4444" }}>Legacy AI</strong> and{" "}
114
+ <strong style={{ color: "var(--accent-emerald)" }}>
115
+ ebade-powered Agent
116
+ </strong>{" "}
117
+ handle the same request.
118
+ </p>
119
+
120
+ {/* The Single Prompt */}
121
+ <div
122
+ style={{
123
+ background:
124
+ "linear-gradient(135deg, rgba(79,70,229,0.15), rgba(16,185,129,0.15))",
125
+ border: "1px solid rgba(255,255,255,0.2)",
126
+ borderRadius: "16px",
127
+ padding: "1.5rem 2rem",
128
+ maxWidth: "600px",
129
+ margin: "0 auto 3rem",
130
+ }}
131
+ >
132
+ <div
133
+ style={{
134
+ fontFamily: "var(--font-mono)",
135
+ fontSize: "0.7rem",
136
+ color: "rgba(255,255,255,0.4)",
137
+ marginBottom: "0.75rem",
138
+ textTransform: "uppercase",
139
+ letterSpacing: "1px",
140
+ }}
141
+ >
142
+ 👤 User Prompt to AI Agent
143
+ </div>
144
+ <div
145
+ style={{
146
+ color: "#fff",
147
+ fontSize: "1.3rem",
148
+ fontWeight: 600,
149
+ fontStyle: "italic",
150
+ }}
151
+ >
152
+ "Build me a premium pricing table with 3 tiers"
153
+ </div>
154
+ </div>
155
+
156
+ {!isRunning && phase === 0 && (
157
+ <button
158
+ onClick={runSimulation}
159
+ className="btn-glow"
160
+ style={{
161
+ cursor: "pointer",
162
+ border: "none",
163
+ display: "inline-flex",
164
+ alignItems: "center",
165
+ gap: "0.5rem",
166
+ }}
167
+ >
168
+ <Play size={18} /> Run Both Agents
169
+ </button>
170
+ )}
171
+ </section>
172
+
173
+ {/* Two Paths Diverge */}
174
+ <section
175
+ ref={processingRef}
176
+ className="section-container"
177
+ style={{ paddingTop: "4rem", paddingBottom: "4rem" }}
178
+ >
179
+ <div style={{ textAlign: "center", marginBottom: "1.5rem" }}>
180
+ <span
181
+ style={{
182
+ fontFamily: "var(--font-mono)",
183
+ fontSize: "1rem",
184
+ fontWeight: 600,
185
+ color: "rgba(255,255,255,0.6)",
186
+ textTransform: "uppercase",
187
+ letterSpacing: "3px",
188
+ }}
189
+ >
190
+ ⚡ Agent Processing
191
+ </span>
192
+ </div>
193
+
194
+ <div className="comparison-modern">
195
+ {/* Legacy AI Path */}
196
+ <div
197
+ className="code-card"
198
+ style={{
199
+ borderColor:
200
+ phase >= 1 ? "rgba(239,68,68,0.5)" : "rgba(239,68,68,0.2)",
201
+ }}
202
+ >
203
+ <div className="card-header">
204
+ <div className="window-controls">
205
+ <span className="control close"></span>
206
+ <span className="control minimize"></span>
207
+ <span className="control maximize"></span>
208
+ </div>
209
+ <div className="card-tag" style={{ color: "#ef4444" }}>
210
+ Legacy AI Agent
211
+ </div>
212
+ </div>
213
+ <pre style={{ padding: "1.5rem", minHeight: "200px" }}>
214
+ {phase === 0 && (
215
+ <code
216
+ style={{
217
+ color: "rgba(255,255,255,0.3)",
218
+ fontSize: "0.8rem",
219
+ }}
220
+ >
221
+ {`// Waiting for prompt...
222
+ // Will generate code directly
223
+ // Token-intensive approach`}
224
+ </code>
225
+ )}
226
+ {phase >= 1 && (
227
+ <div>
228
+ <code
229
+ style={{
230
+ color: "rgba(255,255,255,0.5)",
231
+ fontSize: "0.7rem",
232
+ display: "block",
233
+ marginBottom: "1rem",
234
+ }}
235
+ >
236
+ {`// Generating 140 lines of React...
237
+ // Burning tokens on every character...`}
238
+ </code>
239
+ <div style={{ textAlign: "center", marginTop: "1rem" }}>
240
+ <div
241
+ style={{
242
+ fontSize: "2.5rem",
243
+ fontWeight: 900,
244
+ color: "#ef4444",
245
+ fontFamily: "var(--font-mono)",
246
+ }}
247
+ >
248
+ {legacyTokens.toLocaleString()}
249
+ </div>
250
+ <div
251
+ style={{
252
+ fontSize: "0.75rem",
253
+ color: "rgba(255,255,255,0.5)",
254
+ marginTop: "0.25rem",
255
+ }}
256
+ >
257
+ tokens burned
258
+ </div>
259
+ <div
260
+ style={{
261
+ fontSize: "1.25rem",
262
+ fontWeight: 700,
263
+ color: "#f97316",
264
+ fontFamily: "var(--font-mono)",
265
+ marginTop: "0.75rem",
266
+ }}
267
+ >
268
+ {legacyTime.toFixed(1)}s elapsed
269
+ </div>
270
+ {phase === 1 && (
271
+ <div
272
+ style={{
273
+ marginTop: "0.75rem",
274
+ color: "#ef4444",
275
+ fontSize: "0.7rem",
276
+ }}
277
+ >
278
+ ⏳ Still generating...
279
+ </div>
280
+ )}
281
+ {phase >= 2 && (
282
+ <div
283
+ style={{
284
+ marginTop: "0.75rem",
285
+ color: "#ef4444",
286
+ fontSize: "0.7rem",
287
+ }}
288
+ >
289
+ ⚠️ Done. Risk of hallucinations.
290
+ </div>
291
+ )}
292
+ </div>
293
+ </div>
294
+ )}
295
+ </pre>
296
+ </div>
297
+
298
+ <div className="transform-arrow">
299
+ <Zap size={24} />
300
+ </div>
301
+
302
+ {/* ebade Agent Path */}
303
+ <div className="code-card ebade">
304
+ <div className="card-header">
305
+ <div className="window-controls">
306
+ <span className="control close"></span>
307
+ <span className="control minimize"></span>
308
+ <span className="control maximize"></span>
309
+ </div>
310
+ <div
311
+ className="card-tag"
312
+ style={{ color: "var(--accent-emerald)" }}
313
+ >
314
+ ebade-Powered Agent
315
+ </div>
316
+ </div>
317
+ <pre style={{ padding: "1.5rem", minHeight: "200px" }}>
318
+ {phase === 0 && (
319
+ <code
320
+ style={{
321
+ color: "rgba(255,255,255,0.3)",
322
+ fontSize: "0.8rem",
323
+ }}
324
+ >
325
+ {`// Waiting for prompt...
326
+ // Will map intent → YAML → Code
327
+ // Zero-token code generation`}
328
+ </code>
329
+ )}
330
+ {phase >= 1 && ebadeTime === null && (
331
+ <code
332
+ style={{
333
+ color: "var(--accent-emerald)",
334
+ fontSize: "0.8rem",
335
+ }}
336
+ >
337
+ {`// Mapping intent...
338
+ components:
339
+ - pricing-table`}
340
+ </code>
341
+ )}
342
+ {ebadeTime !== null && (
343
+ <div>
344
+ <code
345
+ style={{
346
+ color: "var(--accent-emerald)",
347
+ fontSize: "0.7rem",
348
+ display: "block",
349
+ marginBottom: "1rem",
350
+ }}
351
+ >
352
+ {`// Intent: pricing-table ✓
353
+ // Template loaded from disk`}
354
+ </code>
355
+ <div style={{ textAlign: "center", marginTop: "1rem" }}>
356
+ <div
357
+ style={{
358
+ fontSize: "2.5rem",
359
+ fontWeight: 900,
360
+ color: "var(--accent-emerald)",
361
+ fontFamily: "var(--font-mono)",
362
+ }}
363
+ >
364
+ {ebadeTime}ms
365
+ </div>
366
+ <div
367
+ style={{
368
+ fontSize: "0.75rem",
369
+ color: "rgba(255,255,255,0.5)",
370
+ marginTop: "0.25rem",
371
+ }}
372
+ >
373
+ ✓ Done!
374
+ </div>
375
+ <div
376
+ style={{
377
+ marginTop: "0.75rem",
378
+ color: "var(--accent-emerald)",
379
+ fontSize: "0.7rem",
380
+ }}
381
+ >
382
+ 0 tokens. 100% deterministic.
383
+ </div>
384
+ </div>
385
+ </div>
386
+ )}
387
+ </pre>
388
+ </div>
389
+ </div>
390
+ </section>
391
+
392
+ {/* Stats */}
393
+ {phase === 3 && (
394
+ <section
395
+ ref={statsRef}
396
+ className="section-container"
397
+ style={{ paddingTop: "4rem", paddingBottom: "8rem" }}
398
+ >
399
+ {/* Output Comparison - First */}
400
+ <div>
401
+ <div style={{ textAlign: "center", marginBottom: "1.5rem" }}>
402
+ <span
403
+ style={{
404
+ fontFamily: "var(--font-mono)",
405
+ fontSize: "1rem",
406
+ fontWeight: 600,
407
+ color: "rgba(255,255,255,0.6)",
408
+ textTransform: "uppercase",
409
+ letterSpacing: "3px",
410
+ }}
411
+ >
412
+ 📦 Compare the Generated Code
413
+ </span>
414
+ </div>
415
+
416
+ <div className="comparison-modern">
417
+ {/* Legacy AI Output */}
418
+ <div
419
+ className="code-card"
420
+ style={{ borderColor: "rgba(239,68,68,0.3)" }}
421
+ >
422
+ <div className="card-header">
423
+ <div className="window-controls">
424
+ <span className="control close"></span>
425
+ <span className="control minimize"></span>
426
+ <span className="control maximize"></span>
427
+ </div>
428
+ <div className="card-tag" style={{ color: "#ef4444" }}>
429
+ Legacy AI Output
430
+ </div>
431
+ </div>
432
+ <pre
433
+ style={{
434
+ padding: "1rem",
435
+ fontSize: "0.65rem",
436
+ lineHeight: "1.4",
437
+ maxHeight: "180px",
438
+ overflow: "auto",
439
+ }}
440
+ >
441
+ <code style={{ color: "rgba(255,255,255,0.6)" }}>
442
+ {`function Pricing() {
443
+ return (
444
+ <div style={{display: 'flex'}}>
445
+ <div style={{border: '1px solid gray'}}>
446
+ <h3>Free</h3>
447
+ <p>$0/mo</p>
448
+ <button>Sign Up</button>
449
+ </div>
450
+ <div style={{border: '1px solid blue'}}>
451
+ <h3>Pro</h3>
452
+ <p>$29/mo</p>
453
+ <button>Get Started</button>
454
+ </div>
455
+ <div style={{border: '1px solid gray'}}>
456
+ <h3>Enterprise</h3>
457
+ <p>Custom</p>
458
+ <button>Contact Us</button>
459
+ </div>
460
+ </div>
461
+ )
462
+ }`}
463
+ </code>
464
+ </pre>
465
+ <div
466
+ style={{
467
+ padding: "0.75rem 1rem",
468
+ borderTop: "1px solid rgba(255,255,255,0.05)",
469
+ fontSize: "0.65rem",
470
+ color: "#ef4444",
471
+ }}
472
+ >
473
+ ⚠️ No design system · Inline styles · No types · No
474
+ accessibility
475
+ </div>
476
+ </div>
477
+
478
+ <div className="transform-arrow">
479
+ <span
480
+ style={{
481
+ fontSize: "1.2rem",
482
+ fontWeight: 700,
483
+ color: "rgba(255,255,255,0.5)",
484
+ }}
485
+ >
486
+ vs
487
+ </span>
488
+ </div>
489
+
490
+ {/* ebade Output */}
491
+ <div className="code-card ebade">
492
+ <div className="card-header">
493
+ <div className="window-controls">
494
+ <span className="control close"></span>
495
+ <span className="control minimize"></span>
496
+ <span className="control maximize"></span>
497
+ </div>
498
+ <div
499
+ className="card-tag"
500
+ style={{ color: "var(--accent-emerald)" }}
501
+ >
502
+ ebade Output (Shadcn)
503
+ </div>
504
+ </div>
505
+ <pre
506
+ style={{
507
+ padding: "1rem",
508
+ fontSize: "0.65rem",
509
+ lineHeight: "1.4",
510
+ maxHeight: "180px",
511
+ overflow: "auto",
512
+ }}
513
+ >
514
+ <code style={{ color: "rgba(255,255,255,0.8)" }}>
515
+ {`import { cn } from "@/lib/utils";
516
+
517
+ export function PricingTable() {
518
+ const plans = [
519
+ { name: "Starter", price: "$0", popular: false },
520
+ { name: "Pro", price: "$29", popular: true },
521
+ ];
522
+
523
+ return (
524
+ <div className="grid md:grid-cols-3 gap-8">
525
+ {plans.map((plan) => (
526
+ <div className={cn(
527
+ "p-8 rounded-2xl border transition-all",
528
+ plan.popular && "border-primary"
529
+ )}>
530
+ ...
531
+ </div>
532
+ ))}
533
+ </div>
534
+ );
535
+ }`}
536
+ </code>
537
+ </pre>
538
+ <div
539
+ style={{
540
+ padding: "0.75rem 1rem",
541
+ borderTop: "1px solid rgba(16,185,129,0.2)",
542
+ fontSize: "0.65rem",
543
+ color: "var(--accent-emerald)",
544
+ }}
545
+ >
546
+ ✓ Shadcn tokens · Tailwind CSS · TypeScript · Responsive ·
547
+ Accessible
548
+ </div>
549
+ </div>
550
+ </div>
551
+ </div>
552
+
553
+ {/* Stats - Final Takeaway */}
554
+ <div
555
+ className="stats-box"
556
+ style={{
557
+ background: "rgba(255,255,255,0.03)",
558
+ border: "1px solid rgba(255,255,255,0.1)",
559
+ marginTop: "3rem",
560
+ }}
561
+ >
562
+ <div className="stat">
563
+ <div className="stat-icon">
564
+ <Zap size={32} />
565
+ </div>
566
+ <span className="val" style={{ color: "#fff" }}>
567
+ ~{legacyTokens.toLocaleString()}
568
+ </span>
569
+ <span className="label">Tokens Saved</span>
570
+ </div>
571
+ <div
572
+ className="stat-divider"
573
+ style={{ background: "rgba(255,255,255,0.1)" }}
574
+ ></div>
575
+ <div className="stat">
576
+ <div className="stat-icon">
577
+ <Clock size={32} />
578
+ </div>
579
+ <span className="val" style={{ color: "#fff" }}>
580
+ {ebadeTime && legacyTime
581
+ ? `${Math.round((legacyTime * 1000) / ebadeTime)}x`
582
+ : "—"}
583
+ </span>
584
+ <span className="label">Faster</span>
585
+ </div>
586
+ <div
587
+ className="stat-divider"
588
+ style={{ background: "rgba(255,255,255,0.1)" }}
589
+ ></div>
590
+ <div className="stat">
591
+ <div
592
+ className="stat-icon"
593
+ style={{ color: "var(--accent-emerald)" }}
594
+ >
595
+ <Leaf size={32} />
596
+ </div>
597
+ <span className="val" style={{ color: "#fff" }}>
598
+ ~99%
599
+ </span>
600
+ <span className="label">Less Energy</span>
601
+ </div>
602
+ </div>
603
+
604
+ <div style={{ textAlign: "center", marginTop: "3rem" }}>
605
+ <button
606
+ onClick={() => {
607
+ setPhase(0);
608
+ setLegacyTokens(0);
609
+ setLegacyTime(0);
610
+ setEbadeTime(null);
611
+ }}
612
+ className="btn-minimal"
613
+ style={{
614
+ cursor: "pointer",
615
+ background: "rgba(255,255,255,0.05)",
616
+ color: "#fff",
617
+ }}
618
+ >
619
+ Run Again
620
+ </button>
621
+ </div>
622
+ </section>
623
+ )}
624
+ </main>
625
+ </div>
626
+ );
627
+ }