ebade 0.4.1 β 0.4.5
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 +29 -1
- package/README.md +1 -1
- package/cli/scaffold.js +147 -85
- package/cli/templates/activity-chart.tsx +50 -0
- package/cli/templates/cta-banner.tsx +39 -0
- package/cli/templates/faq-accordion.tsx +53 -0
- package/cli/templates/recent-events.tsx +80 -0
- package/cli/templates/sidebar-navigation.tsx +68 -0
- package/cli/templates/stats-grid.tsx +80 -0
- package/cli/templates/testimonials.tsx +69 -0
- package/package.json +1 -1
- package/packages/mcp-server/README.md +4 -6
- package/packages/mcp-server/package-lock.json +4 -4
- package/packages/mcp-server/package.json +3 -3
- package/www/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,34 @@ All notable changes to **ebade** will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.5] - 2025-01-10
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- UI/UX Overhaul: Premium dark-mode aesthetic with ambient glow and glassmorphism by default.
|
|
12
|
+
- Dynamic Color Support: Real-time Hex-to-HSL conversion to apply user's primary color choice to CSS variables.
|
|
13
|
+
- Component "Intents": Placeholders are now high-quality "Glass Cards" that look like part of a finished UI.
|
|
14
|
+
- Test Organization: All unit tests are now generated in a centralized `tests/` directory instead of being cluttered with components.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- API Pathing: Fixed a bug that caused double-nesting (e.g., `/api/api/...`) in generated routes.
|
|
18
|
+
- Removed debug information (headers, route labels) from generated pages for a "turnkey" production feel.
|
|
19
|
+
|
|
20
|
+
## [0.4.4] - 2025-01-10
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
- Added `postcss.config.js` generation to enable Tailwind styling in scaffolded projects.
|
|
24
|
+
- Fixed double-nesting issues when initializing projects (removed redundant `projectName/projectName` folder creation).
|
|
25
|
+
- Refined Tailwind config to ensure all component paths are correctly watched.
|
|
26
|
+
|
|
27
|
+
## [0.4.3] - 2025-01-10
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
- Critical bug where templates were not found when running via `npx` (fix using `import.meta.url`).
|
|
31
|
+
- Added missing component templates for SaaS Dashboard and Landing Pages (`testimonials`, `faq-accordion`, `cta-banner`, `activity-chart`, `stats-grid`, `recent-events`).
|
|
32
|
+
- Updated `tsconfig.json` generation to use `jsx: preserve` and added path aliases (`@/*`).
|
|
33
|
+
- Added slugification for `package.json` names to ensure valid naming conventions.
|
|
34
|
+
- Synced framework versions across all packages.
|
|
35
|
+
|
|
8
36
|
## [0.4.1] - 2025-01-10
|
|
9
37
|
|
|
10
38
|
### Added
|
|
@@ -28,7 +56,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
28
56
|
### Added
|
|
29
57
|
|
|
30
58
|
- π Initial alpha release of **ebade**
|
|
31
|
-
- MCP Server package (
|
|
59
|
+
- MCP Server package (`ebade-mcp-server`) with 4 tools:
|
|
32
60
|
- `ebade_scaffold` - Create full projects from ebade definitions
|
|
33
61
|
- `ebade_validate` - Validate ebade files against schema
|
|
34
62
|
- `ebade_compile` - Compile single ebade to code
|
package/README.md
CHANGED
package/cli/scaffold.js
CHANGED
|
@@ -14,6 +14,10 @@ import ora from "ora";
|
|
|
14
14
|
import prompts from "prompts";
|
|
15
15
|
import chokidar from "chokidar";
|
|
16
16
|
import { execSync } from "child_process";
|
|
17
|
+
import { fileURLToPath } from "url";
|
|
18
|
+
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = path.dirname(__filename);
|
|
17
21
|
|
|
18
22
|
// ============================================
|
|
19
23
|
// ANSI Renk KodlarΔ± (Terminal Γ§Δ±ktΔ±sΔ± iΓ§in)
|
|
@@ -40,7 +44,7 @@ ${colors.magenta} ββββββ ${colors.cyan}ββββββββ$
|
|
|
40
44
|
${colors.magenta} ββββββββ${colors.cyan}ββββββββ${colors.magenta}βββ βββ${colors.cyan}ββββββββ${colors.magenta}ββββββββ
|
|
41
45
|
${colors.magenta} ββββββββ${colors.cyan}βββββββ ${colors.magenta}βββ βββ${colors.cyan}βββββββ ${colors.magenta}ββββββββ${colors.reset}
|
|
42
46
|
|
|
43
|
-
${colors.dim}β¨ Agent-First Framework ${colors.yellow}v0.4.
|
|
47
|
+
${colors.dim}β¨ Agent-First Framework ${colors.yellow}v0.4.5${colors.reset}
|
|
44
48
|
`;
|
|
45
49
|
|
|
46
50
|
const log = {
|
|
@@ -70,8 +74,8 @@ function parseEbade(ebadePath) {
|
|
|
70
74
|
// ============================================
|
|
71
75
|
function getComponentTemplate(componentName, design) {
|
|
72
76
|
const templatePath = path.join(
|
|
73
|
-
|
|
74
|
-
"
|
|
77
|
+
__dirname,
|
|
78
|
+
"templates",
|
|
75
79
|
`${componentName}.tsx`
|
|
76
80
|
);
|
|
77
81
|
|
|
@@ -92,17 +96,19 @@ import { cn } from "@/lib/utils";
|
|
|
92
96
|
/**
|
|
93
97
|
* π§ Generated via ebade
|
|
94
98
|
* Component: ${toPascalCase(componentName)}
|
|
95
|
-
* Status:
|
|
99
|
+
* Status: Intent needs implementation
|
|
96
100
|
*/
|
|
97
101
|
export function ${toPascalCase(componentName)}() {
|
|
98
102
|
return (
|
|
99
|
-
<div className="p-12
|
|
100
|
-
<div className="w-
|
|
101
|
-
<span className="text-
|
|
103
|
+
<div className="p-12 glass-card rounded-[2.5rem] text-center min-h-[300px] flex flex-col items-center justify-center group hover:border-primary/50 transition-all">
|
|
104
|
+
<div className="w-20 h-20 bg-primary/10 rounded-[2rem] flex items-center justify-center mb-6 group-hover:scale-110 transition-transform">
|
|
105
|
+
<span className="text-3xl">β¨</span>
|
|
102
106
|
</div>
|
|
103
|
-
<h3 className="text-
|
|
104
|
-
|
|
105
|
-
|
|
107
|
+
<h3 className="text-2xl font-bold mb-3 text-white">${toPascalCase(
|
|
108
|
+
componentName
|
|
109
|
+
)}</h3>
|
|
110
|
+
<p className="text-slate-400 max-w-sm mx-auto leading-relaxed">
|
|
111
|
+
This intent is defined for your AI agent. To customize, edit <code>components/${componentName}.tsx</code> or use the ebade compiler.
|
|
106
112
|
</p>
|
|
107
113
|
</div>
|
|
108
114
|
);
|
|
@@ -172,19 +178,15 @@ ${componentImports}
|
|
|
172
178
|
*/
|
|
173
179
|
export default function ${toPascalCase(page.intent)}Page() {
|
|
174
180
|
return (
|
|
175
|
-
<div className="min-h-screen bg-slate-950 text-white">
|
|
176
|
-
<
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
page.intent
|
|
180
|
-
)}</h1>
|
|
181
|
-
<p className="text-sm opacity-40 mt-1">Route: ${page.path}</p>
|
|
182
|
-
</header>
|
|
181
|
+
<div className="min-h-screen bg-slate-950 text-white selection:bg-indigo-500/30 selection:text-indigo-200">
|
|
182
|
+
<main className="relative overflow-hidden">
|
|
183
|
+
{/* Ambient background glow */}
|
|
184
|
+
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-full h-[500px] bg-indigo-600/10 blur-[120px] rounded-full pointer-events-none" />
|
|
183
185
|
|
|
184
|
-
<
|
|
186
|
+
<div className="relative z-10">
|
|
185
187
|
${componentUsage}
|
|
186
|
-
</
|
|
187
|
-
</
|
|
188
|
+
</div>
|
|
189
|
+
</main>
|
|
188
190
|
</div>
|
|
189
191
|
);
|
|
190
192
|
}
|
|
@@ -252,7 +254,7 @@ function generateDatabaseSchema(data) {
|
|
|
252
254
|
function generatePackageJson(config) {
|
|
253
255
|
return JSON.stringify(
|
|
254
256
|
{
|
|
255
|
-
name: config.name,
|
|
257
|
+
name: config.name.toLowerCase().replace(/[^a-z0-9]/g, "-"),
|
|
256
258
|
version: "0.1.0",
|
|
257
259
|
private: true,
|
|
258
260
|
scripts: {
|
|
@@ -263,10 +265,10 @@ function generatePackageJson(config) {
|
|
|
263
265
|
test: "vitest",
|
|
264
266
|
},
|
|
265
267
|
dependencies: {
|
|
266
|
-
next: "^14.
|
|
267
|
-
react: "^18.
|
|
268
|
-
"react-dom": "^18.
|
|
269
|
-
"lucide-react": "^0.
|
|
268
|
+
next: "^14.2.0",
|
|
269
|
+
react: "^18.3.0",
|
|
270
|
+
"react-dom": "^18.3.0",
|
|
271
|
+
"lucide-react": "^0.400.0",
|
|
270
272
|
clsx: "^2.1.0",
|
|
271
273
|
"tailwind-merge": "^2.2.0",
|
|
272
274
|
"class-variance-authority": "^0.7.0",
|
|
@@ -380,6 +382,16 @@ export default nextConfig;
|
|
|
380
382
|
`;
|
|
381
383
|
}
|
|
382
384
|
|
|
385
|
+
function generatePostcssConfig() {
|
|
386
|
+
return `module.exports = {
|
|
387
|
+
plugins: {
|
|
388
|
+
tailwindcss: {},
|
|
389
|
+
autoprefixer: {},
|
|
390
|
+
},
|
|
391
|
+
}
|
|
392
|
+
`;
|
|
393
|
+
}
|
|
394
|
+
|
|
383
395
|
function generateTsConfig() {
|
|
384
396
|
return JSON.stringify(
|
|
385
397
|
{
|
|
@@ -395,10 +407,12 @@ function generateTsConfig() {
|
|
|
395
407
|
moduleResolution: "node",
|
|
396
408
|
resolveJsonModule: true,
|
|
397
409
|
isolatedModules: true,
|
|
398
|
-
jsx: "
|
|
410
|
+
jsx: "preserve",
|
|
399
411
|
incremental: true,
|
|
400
412
|
plugins: [{ name: "next" }],
|
|
401
|
-
paths: {
|
|
413
|
+
paths: {
|
|
414
|
+
"@/*": ["./*"],
|
|
415
|
+
},
|
|
402
416
|
},
|
|
403
417
|
include: ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
404
418
|
exclude: ["node_modules"],
|
|
@@ -453,65 +467,35 @@ function generateGlobalsCss(design) {
|
|
|
453
467
|
|
|
454
468
|
@layer base {
|
|
455
469
|
:root {
|
|
456
|
-
--background:
|
|
457
|
-
--foreground:
|
|
458
|
-
|
|
459
|
-
--card: 0 0% 100%;
|
|
460
|
-
--card-foreground: 222.2 84% 4.9%;
|
|
461
|
-
|
|
462
|
-
--popover: 0 0% 100%;
|
|
463
|
-
--popover-foreground: 222.2 84% 4.9%;
|
|
464
|
-
|
|
465
|
-
--primary: 221.2 83.2% 53.3%;
|
|
466
|
-
--primary-foreground: 210 40% 98%;
|
|
467
|
-
|
|
468
|
-
--secondary: 210 40% 96.1%;
|
|
469
|
-
--secondary-foreground: 222.2 47.4% 11.2%;
|
|
470
|
-
|
|
471
|
-
--muted: 210 40% 96.1%;
|
|
472
|
-
--muted-foreground: 215.4 16.3% 46.9%;
|
|
473
|
-
|
|
474
|
-
--accent: 210 40% 96.1%;
|
|
475
|
-
--accent-foreground: 222.2 47.4% 11.2%;
|
|
476
|
-
|
|
477
|
-
--destructive: 0 84.2% 60.2%;
|
|
478
|
-
--destructive-foreground: 210 40% 98%;
|
|
479
|
-
|
|
480
|
-
--border: 214.3 31.8% 91.4%;
|
|
481
|
-
--input: 214.3 31.8% 91.4%;
|
|
482
|
-
--ring: 221.2 83.2% 53.3%;
|
|
483
|
-
|
|
484
|
-
--radius: 0.5rem;
|
|
485
|
-
}
|
|
470
|
+
--background: 222 47% 4%;
|
|
471
|
+
--foreground: 213 31% 91%;
|
|
486
472
|
|
|
487
|
-
|
|
488
|
-
--
|
|
489
|
-
--foreground: 210 40% 98%;
|
|
473
|
+
--card: 222 47% 4%;
|
|
474
|
+
--card-foreground: 213 31% 91%;
|
|
490
475
|
|
|
491
|
-
--
|
|
492
|
-
--
|
|
476
|
+
--popover: 222 47% 4%;
|
|
477
|
+
--popover-foreground: 213 31% 91%;
|
|
493
478
|
|
|
494
|
-
--
|
|
495
|
-
--
|
|
479
|
+
--primary: ${hexToHsl(primary)};
|
|
480
|
+
--primary-foreground: 0 0% 100%;
|
|
496
481
|
|
|
497
|
-
--
|
|
498
|
-
--
|
|
482
|
+
--secondary: 222 47% 11%;
|
|
483
|
+
--secondary-foreground: 213 31% 91%;
|
|
499
484
|
|
|
500
|
-
--
|
|
501
|
-
--
|
|
485
|
+
--muted: 223 47% 11%;
|
|
486
|
+
--muted-foreground: 215.4 16.3% 56.9%;
|
|
502
487
|
|
|
503
|
-
--
|
|
504
|
-
--muted-foreground: 215 20.2% 65.1%;
|
|
505
|
-
|
|
506
|
-
--accent: 217.2 32.6% 17.5%;
|
|
488
|
+
--accent: 216 34% 17%;
|
|
507
489
|
--accent-foreground: 210 40% 98%;
|
|
508
490
|
|
|
509
|
-
--destructive: 0
|
|
491
|
+
--destructive: 0 63% 31%;
|
|
510
492
|
--destructive-foreground: 210 40% 98%;
|
|
511
493
|
|
|
512
|
-
--border:
|
|
513
|
-
--input:
|
|
514
|
-
--ring:
|
|
494
|
+
--border: 216 34% 17%;
|
|
495
|
+
--input: 216 34% 17%;
|
|
496
|
+
--ring: ${hexToHsl(primary)};
|
|
497
|
+
|
|
498
|
+
--radius: 1rem;
|
|
515
499
|
}
|
|
516
500
|
}
|
|
517
501
|
|
|
@@ -520,9 +504,25 @@ function generateGlobalsCss(design) {
|
|
|
520
504
|
@apply border-border;
|
|
521
505
|
}
|
|
522
506
|
body {
|
|
523
|
-
@apply bg-background text-foreground;
|
|
507
|
+
@apply bg-background text-foreground antialiased;
|
|
508
|
+
font-feature-settings: "cv11", "ss01";
|
|
524
509
|
}
|
|
525
510
|
}
|
|
511
|
+
|
|
512
|
+
/* Premium Animations */
|
|
513
|
+
@keyframes float {
|
|
514
|
+
0% { transform: translateY(0px); }
|
|
515
|
+
50% { transform: translateY(-10px); }
|
|
516
|
+
100% { transform: translateY(0px); }
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.animate-float {
|
|
520
|
+
animation: float 6s ease-in-out infinite;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.glass-card {
|
|
524
|
+
@apply bg-white/[0.03] border border-white/10 backdrop-blur-xl;
|
|
525
|
+
}
|
|
526
526
|
`;
|
|
527
527
|
}
|
|
528
528
|
|
|
@@ -630,11 +630,54 @@ function toPascalCase(str) {
|
|
|
630
630
|
|
|
631
631
|
function toSnakeCase(str) {
|
|
632
632
|
return str
|
|
633
|
-
.replace(/
|
|
633
|
+
.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`)
|
|
634
634
|
.toLowerCase()
|
|
635
635
|
.replace(/^_/, "");
|
|
636
636
|
}
|
|
637
637
|
|
|
638
|
+
function hexToHsl(hex) {
|
|
639
|
+
let r = 0,
|
|
640
|
+
g = 0,
|
|
641
|
+
b = 0;
|
|
642
|
+
if (hex.length === 4) {
|
|
643
|
+
r = parseInt(hex[1] + hex[1], 16);
|
|
644
|
+
g = parseInt(hex[2] + hex[2], 16);
|
|
645
|
+
b = parseInt(hex[3] + hex[3], 16);
|
|
646
|
+
} else if (hex.length === 7) {
|
|
647
|
+
r = parseInt(hex.slice(1, 3), 16);
|
|
648
|
+
g = parseInt(hex.slice(3, 5), 16);
|
|
649
|
+
b = parseInt(hex.slice(5, 7), 16);
|
|
650
|
+
}
|
|
651
|
+
r /= 255;
|
|
652
|
+
g /= 255;
|
|
653
|
+
b /= 255;
|
|
654
|
+
let max = Math.max(r, g, b),
|
|
655
|
+
min = Math.min(r, g, b);
|
|
656
|
+
let h,
|
|
657
|
+
s,
|
|
658
|
+
l = (max + min) / 2;
|
|
659
|
+
if (max === min) h = s = 0;
|
|
660
|
+
else {
|
|
661
|
+
let d = max - min;
|
|
662
|
+
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
663
|
+
switch (max) {
|
|
664
|
+
case r:
|
|
665
|
+
h = (g - b) / d + (g < b ? 6 : 0);
|
|
666
|
+
break;
|
|
667
|
+
case g:
|
|
668
|
+
h = (b - r) / d + 2;
|
|
669
|
+
break;
|
|
670
|
+
case b:
|
|
671
|
+
h = (r - g) / d + 4;
|
|
672
|
+
break;
|
|
673
|
+
}
|
|
674
|
+
h /= 6;
|
|
675
|
+
}
|
|
676
|
+
return `${Math.round(h * 360)} ${Math.round(s * 100)}% ${Math.round(
|
|
677
|
+
l * 100
|
|
678
|
+
)}%`;
|
|
679
|
+
}
|
|
680
|
+
|
|
638
681
|
function mapToSqlType(type) {
|
|
639
682
|
const typeMap = {
|
|
640
683
|
uuid: "UUID PRIMARY KEY DEFAULT gen_random_uuid()",
|
|
@@ -680,7 +723,10 @@ async function scaffold(ebadePath, outputDir) {
|
|
|
680
723
|
);
|
|
681
724
|
|
|
682
725
|
// Create output directory structure
|
|
683
|
-
|
|
726
|
+
// If outputDir already ends with config.name, don't nest again
|
|
727
|
+
const projectDir = outputDir.endsWith(config.name)
|
|
728
|
+
? outputDir
|
|
729
|
+
: path.join(outputDir, config.name);
|
|
684
730
|
ensureDir(projectDir);
|
|
685
731
|
|
|
686
732
|
// ========== Generate Structure ==========
|
|
@@ -745,8 +791,11 @@ async function scaffold(ebadePath, outputDir) {
|
|
|
745
791
|
|
|
746
792
|
fs.writeFileSync(path.join(projectDir, componentPath), content.trim());
|
|
747
793
|
|
|
748
|
-
// Generate unit test
|
|
749
|
-
const testPath = `components/${component}.test.tsx`;
|
|
794
|
+
// Generate unit test in tests/ directory
|
|
795
|
+
const testPath = `tests/components/${component}.test.tsx`;
|
|
796
|
+
const testDir = path.dirname(path.join(projectDir, testPath));
|
|
797
|
+
ensureDir(testDir);
|
|
798
|
+
|
|
750
799
|
fs.writeFileSync(
|
|
751
800
|
path.join(projectDir, testPath),
|
|
752
801
|
generateComponentTest(component).trim()
|
|
@@ -765,7 +814,12 @@ async function scaffold(ebadePath, outputDir) {
|
|
|
765
814
|
if (config.api) {
|
|
766
815
|
const spinner3 = ora("Generating API routes...").start();
|
|
767
816
|
config.api.forEach((endpoint) => {
|
|
768
|
-
|
|
817
|
+
// Fix potential /api/api double nesting
|
|
818
|
+
const cleanPath = endpoint.path.startsWith("/api")
|
|
819
|
+
? endpoint.path.slice(4)
|
|
820
|
+
: endpoint.path;
|
|
821
|
+
|
|
822
|
+
const apiPath = `app/api${cleanPath}/route.ts`;
|
|
769
823
|
const apiDir = path.dirname(path.join(projectDir, apiPath));
|
|
770
824
|
ensureDir(apiDir);
|
|
771
825
|
|
|
@@ -814,6 +868,13 @@ async function scaffold(ebadePath, outputDir) {
|
|
|
814
868
|
);
|
|
815
869
|
log.file("tailwind.config.js");
|
|
816
870
|
|
|
871
|
+
// postcss.config.js
|
|
872
|
+
fs.writeFileSync(
|
|
873
|
+
path.join(projectDir, "postcss.config.js"),
|
|
874
|
+
generatePostcssConfig()
|
|
875
|
+
);
|
|
876
|
+
log.file("postcss.config.js");
|
|
877
|
+
|
|
817
878
|
// vitest.config.ts
|
|
818
879
|
fs.writeFileSync(
|
|
819
880
|
path.join(projectDir, "vitest.config.ts"),
|
|
@@ -984,13 +1045,14 @@ async function verifyOutput(projectDir, config) {
|
|
|
984
1045
|
}
|
|
985
1046
|
|
|
986
1047
|
// 3. Test Coverage Check
|
|
987
|
-
// Ensure every component has a matching test file
|
|
1048
|
+
// Ensure every component has a matching test file in tests/components/
|
|
988
1049
|
const components = fs
|
|
989
1050
|
.readdirSync(path.join(projectDir, "components"))
|
|
990
1051
|
.filter((f) => f.endsWith(".tsx") && !f.endsWith(".test.tsx"));
|
|
991
1052
|
components.forEach((comp) => {
|
|
992
1053
|
const testFile = comp.replace(".tsx", ".test.tsx");
|
|
993
|
-
|
|
1054
|
+
const testPath = path.join(projectDir, "tests/components", testFile);
|
|
1055
|
+
if (!fs.existsSync(testPath)) {
|
|
994
1056
|
results.tests = false;
|
|
995
1057
|
results.issues.push(`Missing test for component: ${comp}`);
|
|
996
1058
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* π§ Generated via ebade
|
|
6
|
+
* Component: ActivityChart
|
|
7
|
+
*/
|
|
8
|
+
export function ActivityChart() {
|
|
9
|
+
const data = [40, 70, 45, 90, 65, 80, 50, 85, 95, 60, 75, 55];
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div className="p-6 rounded-3xl bg-slate-900 border border-slate-800 shadow-sm">
|
|
13
|
+
<div className="flex items-center justify-between mb-8">
|
|
14
|
+
<div>
|
|
15
|
+
<h3 className="text-lg font-bold text-white">Activity Overview</h3>
|
|
16
|
+
<p className="text-sm text-slate-500">
|
|
17
|
+
System throughput over the last 12 hours
|
|
18
|
+
</p>
|
|
19
|
+
</div>
|
|
20
|
+
<div className="flex gap-2">
|
|
21
|
+
<div className="flex items-center gap-1.5">
|
|
22
|
+
<div className="w-2.5 h-2.5 rounded-full bg-indigo-500"></div>
|
|
23
|
+
<span className="text-xs text-slate-400">Requests</span>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
<div className="flex items-end justify-between h-48 gap-2 px-2">
|
|
29
|
+
{data.map((val, i) => (
|
|
30
|
+
<div
|
|
31
|
+
key={i}
|
|
32
|
+
className="flex-1 flex flex-col items-center gap-2 group"
|
|
33
|
+
>
|
|
34
|
+
<div
|
|
35
|
+
className="w-full bg-indigo-500/20 rounded-t-lg group-hover:bg-indigo-500/40 transition-all relative"
|
|
36
|
+
style={{ height: `${val}%` }}
|
|
37
|
+
>
|
|
38
|
+
<div className="absolute -top-8 left-1/2 -translate-x-1/2 bg-slate-800 text-white text-[10px] px-2 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity">
|
|
39
|
+
{val}k
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<span className="text-[10px] text-slate-600 font-mono">
|
|
43
|
+
H{i + 1}
|
|
44
|
+
</span>
|
|
45
|
+
</div>
|
|
46
|
+
))}
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import { ArrowRight } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* π§ Generated via ebade
|
|
7
|
+
* Component: CtaBanner
|
|
8
|
+
*/
|
|
9
|
+
export function CtaBanner() {
|
|
10
|
+
return (
|
|
11
|
+
<section className="py-24 px-4">
|
|
12
|
+
<div className="max-w-5xl mx-auto p-12 rounded-[2.5rem] bg-gradient-to-br from-indigo-600 to-indigo-700 text-center relative overflow-hidden shadow-2xl">
|
|
13
|
+
<div className="absolute top-0 right-0 -mr-20 -mt-20 w-80 h-80 bg-white/10 rounded-full blur-3xl"></div>
|
|
14
|
+
<div className="absolute bottom-0 left-0 -ml-20 -mb-20 w-60 h-60 bg-black/10 rounded-full blur-3xl"></div>
|
|
15
|
+
|
|
16
|
+
<h2 className="text-3xl md:text-5xl font-bold text-white mb-6 relative z-10">
|
|
17
|
+
Ready to build with Intent?
|
|
18
|
+
</h2>
|
|
19
|
+
<p className="text-indigo-100 text-lg mb-10 max-w-2xl mx-auto relative z-10 italic">
|
|
20
|
+
Join the revolution of Agent-First engineering. Transform your YAML
|
|
21
|
+
into production-ready software in seconds.
|
|
22
|
+
</p>
|
|
23
|
+
|
|
24
|
+
<div className="flex flex-col sm:flex-row gap-4 justify-center items-center relative z-10">
|
|
25
|
+
<button className="px-8 py-4 bg-white text-indigo-600 font-bold rounded-2xl hover:bg-slate-50 transition-all flex items-center gap-2 group shadow-xl">
|
|
26
|
+
Get Started Free
|
|
27
|
+
<ArrowRight
|
|
28
|
+
size={20}
|
|
29
|
+
className="group-hover:translate-x-1 transition-transform"
|
|
30
|
+
/>
|
|
31
|
+
</button>
|
|
32
|
+
<button className="px-8 py-4 bg-transparent text-white border border-white/20 font-semibold rounded-2xl hover:bg-white/10 transition-all">
|
|
33
|
+
Contact Sales
|
|
34
|
+
</button>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
</section>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import { ChevronDown } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* π§ Generated via ebade
|
|
7
|
+
* Component: FaqAccordion
|
|
8
|
+
*/
|
|
9
|
+
export function FaqAccordion() {
|
|
10
|
+
const faqs = [
|
|
11
|
+
{
|
|
12
|
+
q: "What is ebade?",
|
|
13
|
+
a: "ebade is an Agent-First Framework designed to help AI agents build production-ready applications with deterministic outcomes.",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
q: "How does it reduce tokens?",
|
|
17
|
+
a: "By using YAML-based intent definitions, we provide only the essential context to the model, reducing entropy and token consumption.",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
q: "Is it compatible with Next.js?",
|
|
21
|
+
a: "Yes, ebade scaffolds native Next.js projects using the App Router and Tailwind CSS by default.",
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<section className="py-24 bg-slate-950">
|
|
27
|
+
<div className="max-w-3xl mx-auto px-4">
|
|
28
|
+
<h2 className="text-3xl font-bold text-white text-center mb-12">
|
|
29
|
+
Frequently Asked Questions
|
|
30
|
+
</h2>
|
|
31
|
+
<div className="space-y-4">
|
|
32
|
+
{faqs.map((f, i) => (
|
|
33
|
+
<div
|
|
34
|
+
key={i}
|
|
35
|
+
className="group rounded-2xl border border-slate-800 bg-slate-900/30 overflow-hidden transition-all"
|
|
36
|
+
>
|
|
37
|
+
<button className="flex items-center justify-between w-full p-6 text-left text-white hover:bg-white/5 transition-colors">
|
|
38
|
+
<span className="font-medium">{f.q}</span>
|
|
39
|
+
<ChevronDown
|
|
40
|
+
size={20}
|
|
41
|
+
className="text-slate-500 group-focus:rotate-180 transition-transform"
|
|
42
|
+
/>
|
|
43
|
+
</button>
|
|
44
|
+
<div className="p-6 pt-0 text-slate-400 text-sm leading-relaxed border-t border-slate-800/50">
|
|
45
|
+
{f.a}
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
))}
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
</section>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import { CheckCircle2, Clock, Info, ShieldAlert } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* π§ Generated via ebade
|
|
7
|
+
* Component: RecentEvents
|
|
8
|
+
*/
|
|
9
|
+
export function RecentEvents() {
|
|
10
|
+
const events = [
|
|
11
|
+
{
|
|
12
|
+
title: "Deployment Successful",
|
|
13
|
+
time: "2m ago",
|
|
14
|
+
type: "success",
|
|
15
|
+
icon: CheckCircle2,
|
|
16
|
+
desc: "Production environment updated to v1.2.4",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
title: "Security Alert",
|
|
20
|
+
time: "15m ago",
|
|
21
|
+
type: "error",
|
|
22
|
+
icon: ShieldAlert,
|
|
23
|
+
desc: "Anomalous login attempt from unknown IP",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
title: "Backup Completed",
|
|
27
|
+
time: "1h ago",
|
|
28
|
+
type: "info",
|
|
29
|
+
icon: Clock,
|
|
30
|
+
desc: "Global database backup finished successfully",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
title: "New Team Member",
|
|
34
|
+
time: "3h ago",
|
|
35
|
+
type: "info",
|
|
36
|
+
icon: Info,
|
|
37
|
+
desc: "Sarah Chen joined the Engineering team",
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="p-6 rounded-3xl bg-slate-900 border border-slate-800">
|
|
43
|
+
<h3 className="text-lg font-bold text-white mb-6">Recent Events</h3>
|
|
44
|
+
<div className="space-y-6">
|
|
45
|
+
{events.map((e, i) => {
|
|
46
|
+
const Icon = e.icon;
|
|
47
|
+
return (
|
|
48
|
+
<div key={i} className="flex gap-4 group">
|
|
49
|
+
<div
|
|
50
|
+
className={cn(
|
|
51
|
+
"p-2 rounded-xl h-fit",
|
|
52
|
+
e.type === "success"
|
|
53
|
+
? "bg-emerald-500/10 text-emerald-500"
|
|
54
|
+
: e.type === "error"
|
|
55
|
+
? "bg-red-500/10 text-red-500"
|
|
56
|
+
: "bg-blue-500/10 text-blue-500"
|
|
57
|
+
)}
|
|
58
|
+
>
|
|
59
|
+
<Icon size={18} />
|
|
60
|
+
</div>
|
|
61
|
+
<div className="flex-1 pb-6 border-b border-slate-800/50 group-last:border-0 group-last:pb-0">
|
|
62
|
+
<div className="flex justify-between mb-1">
|
|
63
|
+
<h4 className="text-sm font-semibold text-slate-200">
|
|
64
|
+
{e.title}
|
|
65
|
+
</h4>
|
|
66
|
+
<span className="text-[10px] text-slate-500 uppercase font-bold tracking-wider">
|
|
67
|
+
{e.time}
|
|
68
|
+
</span>
|
|
69
|
+
</div>
|
|
70
|
+
<p className="text-xs text-slate-500 leading-relaxed">
|
|
71
|
+
{e.desc}
|
|
72
|
+
</p>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
})}
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import {
|
|
4
|
+
LayoutDashboard,
|
|
5
|
+
BarChart3,
|
|
6
|
+
Users2,
|
|
7
|
+
Settings,
|
|
8
|
+
CreditCard,
|
|
9
|
+
Key,
|
|
10
|
+
} from "lucide-react";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* π§ Generated via ebade
|
|
14
|
+
* Component: SidebarNavigation
|
|
15
|
+
*/
|
|
16
|
+
export function SidebarNavigation() {
|
|
17
|
+
const menuItems = [
|
|
18
|
+
{ label: "Dashboard", icon: LayoutDashboard, active: true },
|
|
19
|
+
{ label: "Analytics", icon: BarChart3 },
|
|
20
|
+
{ label: "Team", icon: Users2 },
|
|
21
|
+
{ label: "Billing", icon: CreditCard },
|
|
22
|
+
{ label: "API Keys", icon: Key },
|
|
23
|
+
{ label: "Settings", icon: Settings },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="w-64 h-full bg-slate-900 border-r border-slate-800 flex flex-col p-6">
|
|
28
|
+
<div className="flex items-center gap-3 mb-12">
|
|
29
|
+
<div className="w-8 h-8 bg-indigo-500 rounded-lg flex items-center justify-center text-white font-bold">
|
|
30
|
+
A
|
|
31
|
+
</div>
|
|
32
|
+
<span className="text-white font-bold tracking-tight">AnalyticsHQ</span>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div className="flex-1 space-y-2">
|
|
36
|
+
{menuItems.map((item, i) => {
|
|
37
|
+
const Icon = item.icon;
|
|
38
|
+
return (
|
|
39
|
+
<button
|
|
40
|
+
key={i}
|
|
41
|
+
className={cn(
|
|
42
|
+
"flex items-center gap-3 w-full px-4 py-3 rounded-2xl transition-all",
|
|
43
|
+
item.active
|
|
44
|
+
? "bg-indigo-500 text-white"
|
|
45
|
+
: "text-slate-400 hover:bg-slate-800 hover:text-white"
|
|
46
|
+
)}
|
|
47
|
+
>
|
|
48
|
+
<Icon size={20} />
|
|
49
|
+
<span className="text-sm font-medium">{item.label}</span>
|
|
50
|
+
</button>
|
|
51
|
+
);
|
|
52
|
+
})}
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div className="mt-auto p-4 rounded-2xl bg-indigo-500/10 border border-indigo-500/20">
|
|
56
|
+
<p className="text-xs text-indigo-300 font-bold mb-1 italic">
|
|
57
|
+
PRO PLAN
|
|
58
|
+
</p>
|
|
59
|
+
<p className="text-[10px] text-indigo-400/70 mb-3">
|
|
60
|
+
Upgrade to Enterprise for unlimited seats.
|
|
61
|
+
</p>
|
|
62
|
+
<div className="w-full bg-slate-800 h-1 rounded-full overflow-hidden">
|
|
63
|
+
<div className="bg-indigo-500 h-full w-[65%]"></div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import { Users, Zap, Shield, TrendingUp } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* π§ Generated via ebade
|
|
7
|
+
* Component: StatsGrid
|
|
8
|
+
*/
|
|
9
|
+
export function StatsGrid() {
|
|
10
|
+
const stats = [
|
|
11
|
+
{
|
|
12
|
+
label: "Active Users",
|
|
13
|
+
value: "12,402",
|
|
14
|
+
growth: "+12.5%",
|
|
15
|
+
icon: Users,
|
|
16
|
+
color: "text-blue-500",
|
|
17
|
+
bg: "bg-blue-500/10",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
label: "Throughput",
|
|
21
|
+
value: "84.2k",
|
|
22
|
+
growth: "+8.1%",
|
|
23
|
+
icon: Zap,
|
|
24
|
+
color: "text-indigo-500",
|
|
25
|
+
bg: "bg-indigo-500/10",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
label: "Security Score",
|
|
29
|
+
value: "98/100",
|
|
30
|
+
growth: "Stable",
|
|
31
|
+
icon: Shield,
|
|
32
|
+
color: "text-emerald-500",
|
|
33
|
+
bg: "bg-emerald-500/10",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
label: "Revenue",
|
|
37
|
+
value: "$42.1k",
|
|
38
|
+
growth: "+24.3%",
|
|
39
|
+
icon: TrendingUp,
|
|
40
|
+
color: "text-violet-500",
|
|
41
|
+
bg: "bg-violet-500/10",
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
47
|
+
{stats.map((s, i) => {
|
|
48
|
+
const Icon = s.icon;
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
key={i}
|
|
52
|
+
className="p-6 rounded-3xl bg-slate-900 border border-slate-800 hover:border-slate-700 transition-colors"
|
|
53
|
+
>
|
|
54
|
+
<div className="flex items-start justify-between mb-4">
|
|
55
|
+
<div className={cn("p-3 rounded-2xl", s.bg)}>
|
|
56
|
+
<Icon size={24} className={s.color} />
|
|
57
|
+
</div>
|
|
58
|
+
<span
|
|
59
|
+
className={cn(
|
|
60
|
+
"text-xs font-bold px-2 py-1 rounded-full",
|
|
61
|
+
s.growth.startsWith("+")
|
|
62
|
+
? "bg-emerald-500/10 text-emerald-500"
|
|
63
|
+
: "bg-slate-800 text-slate-400"
|
|
64
|
+
)}
|
|
65
|
+
>
|
|
66
|
+
{s.growth}
|
|
67
|
+
</span>
|
|
68
|
+
</div>
|
|
69
|
+
<div>
|
|
70
|
+
<p className="text-sm text-slate-500 font-medium mb-1">
|
|
71
|
+
{s.label}
|
|
72
|
+
</p>
|
|
73
|
+
<h4 className="text-2xl font-bold text-white">{s.value}</h4>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
})}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import { Star } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* π§ Generated via ebade
|
|
7
|
+
* Component: Testimonials
|
|
8
|
+
*/
|
|
9
|
+
export function Testimonials() {
|
|
10
|
+
const reviews = [
|
|
11
|
+
{
|
|
12
|
+
name: "Alex Rivera",
|
|
13
|
+
role: "CTO at TechFlow",
|
|
14
|
+
content:
|
|
15
|
+
"ebade changed how we bridge the gap between AI intent and production-ready code. It's a game changer.",
|
|
16
|
+
rating: 5,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "Sarah Chen",
|
|
20
|
+
role: "Lead Developer",
|
|
21
|
+
content:
|
|
22
|
+
"The most efficient way to maintain deterministic outputs with LLMs. Low entropy, high quality.",
|
|
23
|
+
rating: 5,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "Marcus Thorne",
|
|
27
|
+
role: "Product Designer",
|
|
28
|
+
content:
|
|
29
|
+
"Finally, a framework that speaks the language of agents without sacrificing developer experience.",
|
|
30
|
+
rating: 5,
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<section className="py-24 bg-slate-950">
|
|
36
|
+
<div className="max-w-7xl mx-auto px-4">
|
|
37
|
+
<div className="text-center mb-16">
|
|
38
|
+
<h2 className="text-3xl md:text-4xl font-bold text-white mb-4">
|
|
39
|
+
Trusted by the Best
|
|
40
|
+
</h2>
|
|
41
|
+
<p className="text-slate-400">
|
|
42
|
+
Join thousands of developers building the future of AI-Native
|
|
43
|
+
software.
|
|
44
|
+
</p>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
|
48
|
+
{reviews.map((s, i) => (
|
|
49
|
+
<div
|
|
50
|
+
key={i}
|
|
51
|
+
className="p-8 rounded-3xl bg-slate-900/50 border border-slate-800 hover:border-slate-700 transition-all"
|
|
52
|
+
>
|
|
53
|
+
<div className="flex gap-1 mb-4 text-yellow-500">
|
|
54
|
+
{[...Array(s.rating)].map((_, i) => (
|
|
55
|
+
<Star key={i} size={16} fill="currentColor" />
|
|
56
|
+
))}
|
|
57
|
+
</div>
|
|
58
|
+
<p className="text-slate-300 mb-6 italic">"{s.content}"</p>
|
|
59
|
+
<div>
|
|
60
|
+
<p className="text-white font-semibold">{s.name}</p>
|
|
61
|
+
<p className="text-slate-500 text-sm">{s.role}</p>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
))}
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</section>
|
|
68
|
+
);
|
|
69
|
+
}
|
package/package.json
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
# ebade MCP Server
|
|
1
|
+
# ebade MCP Server π£
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
This MCP (Model Context Protocol) server allows AI agents like Claude, GPT, and others to use ebade for building web applications.
|
|
3
|
+
Model Context Protocol (MCP) server for the **ebade** Agent-First Framework.
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
|
-
|
|
8
|
+
npx -y ebade-mcp-server
|
|
11
9
|
```
|
|
12
10
|
|
|
13
11
|
Or locally:
|
|
14
12
|
|
|
15
13
|
```bash
|
|
16
|
-
npm install
|
|
14
|
+
npm install ebade-mcp-server
|
|
17
15
|
```
|
|
18
16
|
|
|
19
17
|
## Configuration
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
3
|
-
"version": "0.1
|
|
2
|
+
"name": "ebade-mcp-server",
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
|
-
"name": "
|
|
9
|
-
"version": "0.1
|
|
8
|
+
"name": "ebade-mcp-server",
|
|
9
|
+
"version": "0.4.1",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "MCP Server for ebade v0.4.
|
|
2
|
+
"name": "ebade-mcp-server",
|
|
3
|
+
"version": "0.4.5",
|
|
4
|
+
"description": "MCP Server for ebade v0.4.4 - The Agent-First Framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|