@emberkit/cli 0.6.0 → 0.6.1-alpha.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.
@@ -0,0 +1,331 @@
1
+ import { buildPackageJson, buildTsConfig, buildViteConfig, buildIndexHtml, buildEntryFile, GITIGNORE, } from "../_shared/base.js";
2
+ export const dashboardTemplate = {
3
+ "package.json": buildPackageJson({ hasTailwind: true, hasUI: true }),
4
+ "tsconfig.json": buildTsConfig(),
5
+ "vite.config.ts": buildViteConfig(true),
6
+ "index.html": buildIndexHtml({
7
+ fonts: [
8
+ "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap",
9
+ ],
10
+ }),
11
+ ".gitignore": GITIGNORE,
12
+ "src/index.tsx": buildEntryFile({ hasLayout: true, hasCss: true }),
13
+ "src/styles.css": `@import "tailwindcss";
14
+
15
+ @theme {
16
+ --color-brand-50: #f0fdf4;
17
+ --color-brand-100: #dcfce7;
18
+ --color-brand-200: #bbf7d0;
19
+ --color-brand-300: #86efac;
20
+ --color-brand-400: #4ade80;
21
+ --color-brand-500: #22c55e;
22
+ --color-brand-600: #16a34a;
23
+ --color-brand-700: #15803d;
24
+ --font-sans: 'Inter', system-ui, sans-serif;
25
+ }
26
+
27
+ body {
28
+ @apply bg-gray-50 text-gray-900 font-sans;
29
+ }`,
30
+ "src/routes/_layout.tsx": `import type { RouteComponent } from '@emberkit/core';
31
+ import { signal } from '@emberkit/core';
32
+ import { Sidebar, Header } from '@emberkit/ui';
33
+
34
+ const Layout: RouteComponent = ({ children }) => {
35
+ const sidebarOpen = signal(true);
36
+
37
+ const sidebarItems = [
38
+ { label: 'Dashboard', href: '/dashboard', icon: 'grid' },
39
+ { label: 'Analytics', href: '/analytics', icon: 'chart' },
40
+ { label: 'Users', href: '/users', icon: 'users' },
41
+ { label: 'Projects', href: '/projects', icon: 'folder' },
42
+ { label: 'Settings', href: '/settings', icon: 'settings' },
43
+ ];
44
+
45
+ return (
46
+ <div className="min-h-screen flex">
47
+ <Sidebar
48
+ logo={<span className="font-bold text-lg">&#9889; {{name}}</span>}
49
+ items={sidebarItems}
50
+ collapsed={!sidebarOpen.value}
51
+ onToggle={() => { sidebarOpen.value = !sidebarOpen.value; }}
52
+ className="w-64"
53
+ />
54
+ <div className="flex-1 flex flex-col min-w-0">
55
+ <Header
56
+ title="Dashboard"
57
+ user={{ name: 'User', avatar: '' }}
58
+ onMenuClick={() => { sidebarOpen.value = !sidebarOpen.value; }}
59
+ />
60
+ <main className="flex-1 p-6 overflow-auto">
61
+ {children}
62
+ </main>
63
+ </div>
64
+ </div>
65
+ );
66
+ };
67
+
68
+ export default Layout;`,
69
+ "src/routes/index.tsx": `import type { RouteComponent } from '@emberkit/core';
70
+ import { Card, Badge } from '@emberkit/ui';
71
+
72
+ const DashboardPage: RouteComponent = () => {
73
+ const stats = [
74
+ { label: 'Total Revenue', value: '$45,231', change: '+20.1%', positive: true },
75
+ { label: 'Active Users', value: '2,338', change: '+15.3%', positive: true },
76
+ { label: 'New Signups', value: '1,247', change: '+8.2%', positive: true },
77
+ { label: 'Churn Rate', value: '2.4%', change: '-0.5%', positive: true },
78
+ ];
79
+
80
+ const recentActivity = [
81
+ { user: 'John Doe', action: 'created a new project', time: '2 minutes ago' },
82
+ { user: 'Jane Smith', action: 'upgraded to Pro plan', time: '15 minutes ago' },
83
+ { user: 'Mike Johnson', action: 'invited 3 team members', time: '1 hour ago' },
84
+ { user: 'Sarah Williams', action: 'published a new post', time: '2 hours ago' },
85
+ { user: 'Alex Brown', action: 'deleted an old project', time: '3 hours ago' },
86
+ ];
87
+
88
+ return (
89
+ <div className="space-y-6">
90
+ <div>
91
+ <h1 className="text-2xl font-bold">Welcome back</h1>
92
+ <p className="text-gray-600 mt-1">Here's what's happening with your account today.</p>
93
+ </div>
94
+
95
+ {/* Stats Grid */}
96
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
97
+ {stats.map((stat) => (
98
+ <Card key={stat.label} padding="lg">
99
+ <p className="text-sm text-gray-600">{stat.label}</p>
100
+ <p className="text-2xl font-bold mt-1">{stat.value}</p>
101
+ <Badge variant={stat.positive ? 'success' : 'danger'} size="sm" className="mt-2">
102
+ {stat.change}
103
+ </Badge>
104
+ </Card>
105
+ ))}
106
+ </div>
107
+
108
+ <div className="grid lg:grid-cols-2 gap-6">
109
+ {/* Chart Placeholder */}
110
+ <Card padding="lg">
111
+ <h3 className="font-semibold mb-4">Revenue Overview</h3>
112
+ <div className="h-64 bg-gray-100 rounded-lg flex items-center justify-center text-gray-500">
113
+ Chart placeholder - integrate your preferred charting library
114
+ </div>
115
+ </Card>
116
+
117
+ {/* Recent Activity */}
118
+ <Card padding="lg">
119
+ <h3 className="font-semibold mb-4">Recent Activity</h3>
120
+ <div className="space-y-4">
121
+ {recentActivity.map((item, i) => (
122
+ <div key={i} className="flex items-start gap-3 pb-4 border-b border-gray-100 last:border-0 last:pb-0">
123
+ <div className="w-8 h-8 rounded-full bg-brand-100 flex items-center justify-center text-brand-700 font-medium text-sm">
124
+ {item.user.charAt(0)}
125
+ </div>
126
+ <div className="flex-1 min-w-0">
127
+ <p className="text-sm">
128
+ <span className="font-medium">{item.user}</span> {item.action}
129
+ </p>
130
+ <p className="text-xs text-gray-500 mt-0.5">{item.time}</p>
131
+ </div>
132
+ </div>
133
+ ))}
134
+ </div>
135
+ </Card>
136
+ </div>
137
+
138
+ {/* Quick Actions */}
139
+ <Card padding="lg">
140
+ <h3 className="font-semibold mb-4">Quick Actions</h3>
141
+ <div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
142
+ {[
143
+ { label: 'New Project', icon: '+' },
144
+ { label: 'Invite User', icon: '&#128100;' },
145
+ { label: 'View Reports', icon: '&#128202;' },
146
+ { label: 'Settings', icon: '&#9881;' },
147
+ ].map((action) => (
148
+ <button
149
+ key={action.label}
150
+ className="p-4 rounded-lg border border-gray-200 hover:border-brand-300 hover:bg-brand-50 transition-colors text-center"
151
+ >
152
+ <span className="text-2xl block mb-2">{action.icon}</span>
153
+ <span className="text-sm font-medium">{action.label}</span>
154
+ </button>
155
+ ))}
156
+ </div>
157
+ </Card>
158
+ </div>
159
+ );
160
+ };
161
+
162
+ export default DashboardPage;`,
163
+ "src/routes/users.tsx": `import type { RouteComponent } from '@emberkit/core';
164
+ import { Card, Badge, Button, Input } from '@emberkit/ui';
165
+ import { signal } from '@emberkit/core';
166
+
167
+ interface User {
168
+ id: number;
169
+ name: string;
170
+ email: string;
171
+ role: string;
172
+ status: 'active' | 'inactive' | 'pending';
173
+ lastActive: string;
174
+ }
175
+
176
+ const users: User[] = [
177
+ { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'active', lastActive: '2 min ago' },
178
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'Editor', status: 'active', lastActive: '1 hour ago' },
179
+ { id: 3, name: 'Mike Johnson', email: 'mike@example.com', role: 'Viewer', status: 'inactive', lastActive: '3 days ago' },
180
+ { id: 4, name: 'Sarah Williams', email: 'sarah@example.com', role: 'Editor', status: 'active', lastActive: '5 min ago' },
181
+ { id: 5, name: 'Alex Brown', email: 'alex@example.com', role: 'Viewer', status: 'pending', lastActive: 'Never' },
182
+ ];
183
+
184
+ const UsersPage: RouteComponent = () => {
185
+ const search = signal('');
186
+
187
+ const filteredUsers = users.filter(
188
+ (u) =>
189
+ u.name.toLowerCase().includes(search.value.toLowerCase()) ||
190
+ u.email.toLowerCase().includes(search.value.toLowerCase())
191
+ );
192
+
193
+ const statusVariant = (status: User['status']) => {
194
+ switch (status) {
195
+ case 'active': return 'success';
196
+ case 'inactive': return 'default';
197
+ case 'pending': return 'warning';
198
+ }
199
+ };
200
+
201
+ return (
202
+ <div className="space-y-6">
203
+ <div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
204
+ <div>
205
+ <h1 className="text-2xl font-bold">Users</h1>
206
+ <p className="text-gray-600 mt-1">Manage your team members and their roles.</p>
207
+ </div>
208
+ <Button variant="primary">Add User</Button>
209
+ </div>
210
+
211
+ <Card padding="lg">
212
+ <div className="mb-4">
213
+ <Input
214
+ placeholder="Search users..."
215
+ value={search.value}
216
+ onInput={(e) => { search.value = e.currentTarget.value; }}
217
+ className="max-w-sm"
218
+ />
219
+ </div>
220
+ <div className="overflow-x-auto">
221
+ <table className="w-full text-sm">
222
+ <thead>
223
+ <tr className="border-b border-gray-200">
224
+ <th className="text-left py-3 px-4 font-medium text-gray-600">Name</th>
225
+ <th className="text-left py-3 px-4 font-medium text-gray-600">Role</th>
226
+ <th className="text-left py-3 px-4 font-medium text-gray-600">Status</th>
227
+ <th className="text-left py-3 px-4 font-medium text-gray-600">Last Active</th>
228
+ <th className="text-right py-3 px-4 font-medium text-gray-600">Actions</th>
229
+ </tr>
230
+ </thead>
231
+ <tbody>
232
+ {filteredUsers.map((user) => (
233
+ <tr key={user.id} className="border-b border-gray-100 hover:bg-gray-50">
234
+ <td className="py-3 px-4">
235
+ <div>
236
+ <p className="font-medium">{user.name}</p>
237
+ <p className="text-gray-500 text-xs">{user.email}</p>
238
+ </div>
239
+ </td>
240
+ <td className="py-3 px-4">{user.role}</td>
241
+ <td className="py-3 px-4">
242
+ <Badge variant={statusVariant(user.status)} size="sm">
243
+ {user.status}
244
+ </Badge>
245
+ </td>
246
+ <td className="py-3 px-4 text-gray-500">{user.lastActive}</td>
247
+ <td className="py-3 px-4 text-right">
248
+ <button className="text-brand-600 hover:underline text-sm">Edit</button>
249
+ </td>
250
+ </tr>
251
+ ))}
252
+ </tbody>
253
+ </table>
254
+ </div>
255
+ </Card>
256
+ </div>
257
+ );
258
+ };
259
+
260
+ export default UsersPage;`,
261
+ "src/routes/settings.tsx": `import type { RouteComponent } from '@emberkit/core';
262
+ import { Card, Input, Button, Alert } from '@emberkit/ui';
263
+ import { signal } from '@emberkit/core';
264
+
265
+ const SettingsPage: RouteComponent = () => {
266
+ const name = signal('John Doe');
267
+ const email = signal('john@example.com');
268
+ const saved = signal(false);
269
+
270
+ const handleSave = (e: Event) => {
271
+ e.preventDefault();
272
+ saved.value = true;
273
+ setTimeout(() => { saved.value = false; }, 3000);
274
+ };
275
+
276
+ return (
277
+ <div className="space-y-6 max-w-2xl">
278
+ <div>
279
+ <h1 className="text-2xl font-bold">Settings</h1>
280
+ <p className="text-gray-600 mt-1">Manage your account preferences.</p>
281
+ </div>
282
+
283
+ {saved.value && (
284
+ <Alert variant="success">Settings saved successfully!</Alert>
285
+ )}
286
+
287
+ <Card padding="lg">
288
+ <h3 className="font-semibold mb-6">Profile</h3>
289
+ <form onSubmit={handleSave} className="space-y-4">
290
+ <Input
291
+ label="Full Name"
292
+ value={name.value}
293
+ onInput={(e) => { name.value = e.currentTarget.value; }}
294
+ />
295
+ <Input
296
+ label="Email"
297
+ type="email"
298
+ value={email.value}
299
+ onInput={(e) => { email.value = e.currentTarget.value; }}
300
+ />
301
+ <div className="flex justify-end">
302
+ <Button variant="primary" type="submit">Save Changes</Button>
303
+ </div>
304
+ </form>
305
+ </Card>
306
+
307
+ <Card padding="lg">
308
+ <h3 className="font-semibold mb-6">Password</h3>
309
+ <form className="space-y-4">
310
+ <Input label="Current Password" type="password" />
311
+ <Input label="New Password" type="password" />
312
+ <Input label="Confirm Password" type="password" />
313
+ <div className="flex justify-end">
314
+ <Button variant="primary">Update Password</Button>
315
+ </div>
316
+ </form>
317
+ </Card>
318
+
319
+ <Card padding="lg" className="border-red-200">
320
+ <h3 className="font-semibold mb-2 text-red-600">Danger Zone</h3>
321
+ <p className="text-sm text-gray-600 mb-4">
322
+ Once you delete your account, there is no going back.
323
+ </p>
324
+ <Button variant="danger">Delete Account</Button>
325
+ </Card>
326
+ </div>
327
+ );
328
+ };
329
+
330
+ export default SettingsPage;`,
331
+ };
@@ -0,0 +1,21 @@
1
+ import { buildPackageJson, buildTsConfig, buildViteConfig, buildIndexHtml, buildEntryFile, GITIGNORE, } from "../_shared/base.js";
2
+ export const minimalTemplate = {
3
+ "package.json": buildPackageJson(),
4
+ "tsconfig.json": buildTsConfig(false),
5
+ "vite.config.ts": buildViteConfig(false),
6
+ "index.html": buildIndexHtml(),
7
+ ".gitignore": GITIGNORE,
8
+ "src/index.tsx": buildEntryFile(),
9
+ "src/routes/index.tsx": `import type { RouteComponent } from '@emberkit/core';
10
+
11
+ const Home: RouteComponent = () => {
12
+ return (
13
+ <div style={{ fontFamily: 'system-ui, sans-serif', maxWidth: '600px', margin: '2rem auto', padding: '0 1rem' }}>
14
+ <h1>{{name}}</h1>
15
+ <p>Built with EmberKit</p>
16
+ </div>
17
+ );
18
+ };
19
+
20
+ export default Home;`,
21
+ };