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.
- package/CHANGELOG.md +5 -0
- package/README.md +49 -38
- package/ROADMAP.md +19 -14
- package/cli/scaffold.js +502 -186
- package/cli/simulate.js +102 -0
- package/cli/templates/feature-grid.tsx +80 -0
- package/cli/templates/footer.tsx +121 -0
- package/cli/templates/hero-section.tsx +34 -0
- package/cli/templates/login-form.tsx +124 -0
- package/cli/templates/navbar.tsx +53 -0
- package/cli/templates/pricing-table.tsx +140 -0
- package/cli/templates/signup-form.tsx +111 -0
- package/demo.tape +2 -2
- package/examples/saas-dashboard.ebade.yaml +2 -0
- package/netlify.toml +7 -0
- package/package.json +3 -1
- package/packages/mcp-server/README.md +3 -3
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/index.ts +12 -16
- package/packages/mcp-server/src/tools/scaffold.ts +153 -404
- package/packages/vscode-extension/README.md +45 -0
- package/packages/vscode-extension/ebade-0.1.0.vsix +0 -0
- package/packages/vscode-extension/ebade-0.3.0.vsix +0 -0
- package/packages/vscode-extension/ebade-0.3.1.vsix +0 -0
- package/packages/vscode-extension/ebade-0.3.2.vsix +0 -0
- package/packages/vscode-extension/images/icon.png +0 -0
- package/packages/vscode-extension/language-configuration.json +24 -0
- package/packages/vscode-extension/package.json +54 -0
- package/packages/vscode-extension/snippets/ebade.json +86 -0
- package/packages/vscode-extension/syntaxes/ebade.tmLanguage.json +54 -0
- package/www/README.md +36 -0
- package/www/app/favicon.ico +0 -0
- package/www/app/globals.css +1256 -0
- package/www/app/layout.tsx +66 -0
- package/www/app/page.tsx +374 -0
- package/www/app/playground/page.tsx +627 -0
- package/www/components/ThreeCanvas.tsx +156 -0
- package/www/next.config.ts +19 -0
- package/www/package-lock.json +1779 -0
- package/www/package.json +27 -0
- package/www/postcss.config.mjs +7 -0
- package/www/public/logo.png +0 -0
- package/www/tsconfig.json +42 -0
- package/landing/index.html +0 -237
- package/landing/main.js +0 -147
- package/landing/style.css +0 -616
- /package/{demo.gif → assets/demo.gif} +0 -0
- /package/{demo.mp4 → assets/demo.mp4} +0 -0
- /package/{landing → www/public}/_headers +0 -0
- /package/{landing → www/public}/favicon.svg +0 -0
- /package/{landing → www/public}/og-image.png +0 -0
- /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
|
+
}
|