blumenjs 0.1.4 → 0.1.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.
@@ -0,0 +1,546 @@
1
+ import React, { useState } from "react";
2
+
3
+ const DashboardPage: React.FC = () => {
4
+ const [activeNav, setActiveNav] = useState("overview");
5
+
6
+ const stats = [
7
+ { label: "Total Users", value: "12,847", change: "+12.5%", up: true, icon: "◎" },
8
+ { label: "Revenue", value: "$48,290", change: "+8.2%", up: true, icon: "◆" },
9
+ { label: "Active Now", value: "1,429", change: "+3.1%", up: true, icon: "●" },
10
+ { label: "Bounce Rate", value: "24.3%", change: "-2.4%", up: false, icon: "◇" },
11
+ ];
12
+
13
+ const navItems = [
14
+ { id: "overview", label: "Overview", icon: "◎" },
15
+ { id: "analytics", label: "Analytics", icon: "◆" },
16
+ { id: "users", label: "Users", icon: "◈" },
17
+ { id: "settings", label: "Settings", icon: "⬡" },
18
+ ];
19
+
20
+ const recentActivity = [
21
+ { user: "Sarah Chen", action: "Updated billing info", time: "2 min ago", avatar: "SC" },
22
+ { user: "Alex Rivera", action: "Created new project", time: "15 min ago", avatar: "AR" },
23
+ { user: "Jamie Park", action: "Deployed to production", time: "1 hr ago", avatar: "JP" },
24
+ { user: "Morgan Ellis", action: "Invited team member", time: "3 hrs ago", avatar: "ME" },
25
+ { user: "Taylor Kim", action: "Upgraded to Pro plan", time: "5 hrs ago", avatar: "TK" },
26
+ ];
27
+
28
+ return (
29
+ <div className="bl-dash">
30
+ <style
31
+ dangerouslySetInnerHTML={{
32
+ __html: `
33
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
34
+
35
+ @keyframes fade-up {
36
+ from { opacity: 0; transform: translateY(16px); }
37
+ to { opacity: 1; transform: translateY(0); }
38
+ }
39
+
40
+ @keyframes shimmer {
41
+ 0% { background-position: -200% 0; }
42
+ 100% { background-position: 200% 0; }
43
+ }
44
+
45
+ @keyframes pulse-ring {
46
+ 0% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.3); }
47
+ 70% { box-shadow: 0 0 0 8px rgba(99, 102, 241, 0); }
48
+ 100% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0); }
49
+ }
50
+
51
+ .bl-dash * { box-sizing: border-box; margin: 0; padding: 0; }
52
+
53
+ .bl-dash {
54
+ min-height: 100vh;
55
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
56
+ background: #09090b;
57
+ color: #fafafa;
58
+ display: flex;
59
+ }
60
+
61
+ /* ── Sidebar ── */
62
+ .bl-sidebar {
63
+ width: 260px;
64
+ background: #0f0f12;
65
+ border-right: 1px solid rgba(255,255,255,0.06);
66
+ padding: 24px 16px;
67
+ display: flex;
68
+ flex-direction: column;
69
+ position: fixed;
70
+ top: 0;
71
+ bottom: 0;
72
+ left: 0;
73
+ z-index: 10;
74
+ }
75
+
76
+ .bl-sidebar-brand {
77
+ display: flex;
78
+ align-items: center;
79
+ gap: 12px;
80
+ padding: 8px 12px;
81
+ margin-bottom: 32px;
82
+ }
83
+
84
+ .bl-sidebar-logo {
85
+ width: 36px; height: 36px;
86
+ border-radius: 10px;
87
+ background: linear-gradient(135deg, #6366f1, #8b5cf6);
88
+ display: flex;
89
+ align-items: center;
90
+ justify-content: center;
91
+ font-size: 1.1rem;
92
+ }
93
+
94
+ .bl-sidebar-name {
95
+ font-size: 1.1rem;
96
+ font-weight: 700;
97
+ letter-spacing: -0.02em;
98
+ }
99
+
100
+ .bl-sidebar-name span {
101
+ color: #6b7280;
102
+ font-weight: 400;
103
+ font-size: 0.75rem;
104
+ display: block;
105
+ margin-top: 2px;
106
+ }
107
+
108
+ .bl-nav { list-style: none; flex: 1; }
109
+
110
+ .bl-nav-item {
111
+ display: flex;
112
+ align-items: center;
113
+ gap: 12px;
114
+ padding: 10px 14px;
115
+ border-radius: 10px;
116
+ font-size: 0.88rem;
117
+ font-weight: 500;
118
+ color: #6b7280;
119
+ cursor: pointer;
120
+ transition: all 0.2s;
121
+ margin-bottom: 4px;
122
+ border: none;
123
+ background: none;
124
+ width: 100%;
125
+ font-family: inherit;
126
+ text-align: left;
127
+ }
128
+
129
+ .bl-nav-item:hover {
130
+ color: #d1d5db;
131
+ background: rgba(255,255,255,0.04);
132
+ }
133
+
134
+ .bl-nav-item.active {
135
+ color: #fff;
136
+ background: rgba(99, 102, 241, 0.12);
137
+ }
138
+
139
+ .bl-nav-icon {
140
+ width: 20px;
141
+ text-align: center;
142
+ font-size: 0.9rem;
143
+ }
144
+
145
+ .bl-sidebar-footer {
146
+ padding: 16px 14px;
147
+ border-top: 1px solid rgba(255,255,255,0.06);
148
+ display: flex;
149
+ align-items: center;
150
+ gap: 12px;
151
+ }
152
+
153
+ .bl-avatar {
154
+ width: 34px; height: 34px;
155
+ border-radius: 8px;
156
+ background: linear-gradient(135deg, #1e1b4b, #312e81);
157
+ display: flex;
158
+ align-items: center;
159
+ justify-content: center;
160
+ font-size: 0.7rem;
161
+ font-weight: 600;
162
+ color: #a5b4fc;
163
+ }
164
+
165
+ .bl-user-name {
166
+ font-size: 0.82rem;
167
+ font-weight: 600;
168
+ color: #e5e7eb;
169
+ }
170
+
171
+ .bl-user-role {
172
+ font-size: 0.72rem;
173
+ color: #6b7280;
174
+ }
175
+
176
+ /* ── Main ── */
177
+ .bl-main {
178
+ flex: 1;
179
+ margin-left: 260px;
180
+ padding: 32px 40px;
181
+ }
182
+
183
+ .bl-header {
184
+ display: flex;
185
+ justify-content: space-between;
186
+ align-items: center;
187
+ margin-bottom: 36px;
188
+ animation: fade-up 0.6s ease-out both;
189
+ }
190
+
191
+ .bl-header-title {
192
+ font-size: 1.6rem;
193
+ font-weight: 800;
194
+ letter-spacing: -0.03em;
195
+ }
196
+
197
+ .bl-header-sub {
198
+ font-size: 0.88rem;
199
+ color: #6b7280;
200
+ margin-top: 4px;
201
+ }
202
+
203
+ .bl-header-actions {
204
+ display: flex;
205
+ gap: 10px;
206
+ }
207
+
208
+ .bl-btn {
209
+ padding: 9px 20px;
210
+ border-radius: 10px;
211
+ font-family: inherit;
212
+ font-size: 0.82rem;
213
+ font-weight: 600;
214
+ border: none;
215
+ cursor: pointer;
216
+ transition: all 0.2s;
217
+ }
218
+
219
+ .bl-btn-primary {
220
+ background: linear-gradient(135deg, #6366f1, #818cf8);
221
+ color: #fff;
222
+ }
223
+
224
+ .bl-btn-primary:hover {
225
+ transform: translateY(-1px);
226
+ box-shadow: 0 4px 20px rgba(99, 102, 241, 0.3);
227
+ }
228
+
229
+ .bl-btn-ghost {
230
+ background: rgba(255,255,255,0.05);
231
+ color: #9ca3af;
232
+ border: 1px solid rgba(255,255,255,0.08);
233
+ }
234
+
235
+ .bl-btn-ghost:hover {
236
+ background: rgba(255,255,255,0.08);
237
+ color: #e5e7eb;
238
+ }
239
+
240
+ /* ── Stats Grid ── */
241
+ .bl-stats {
242
+ display: grid;
243
+ grid-template-columns: repeat(4, 1fr);
244
+ gap: 16px;
245
+ margin-bottom: 32px;
246
+ }
247
+
248
+ .bl-stat-card {
249
+ padding: 24px;
250
+ border-radius: 16px;
251
+ background: #111114;
252
+ border: 1px solid rgba(255,255,255,0.06);
253
+ transition: all 0.3s;
254
+ animation: fade-up 0.6s ease-out both;
255
+ }
256
+
257
+ .bl-stat-card:nth-child(1) { animation-delay: 0.05s; }
258
+ .bl-stat-card:nth-child(2) { animation-delay: 0.1s; }
259
+ .bl-stat-card:nth-child(3) { animation-delay: 0.15s; }
260
+ .bl-stat-card:nth-child(4) { animation-delay: 0.2s; }
261
+
262
+ .bl-stat-card:hover {
263
+ border-color: rgba(99, 102, 241, 0.2);
264
+ transform: translateY(-2px);
265
+ box-shadow: 0 8px 32px rgba(0,0,0,0.3);
266
+ }
267
+
268
+ .bl-stat-top {
269
+ display: flex;
270
+ justify-content: space-between;
271
+ align-items: center;
272
+ margin-bottom: 16px;
273
+ }
274
+
275
+ .bl-stat-icon {
276
+ width: 40px; height: 40px;
277
+ border-radius: 10px;
278
+ background: rgba(99, 102, 241, 0.1);
279
+ display: flex;
280
+ align-items: center;
281
+ justify-content: center;
282
+ font-size: 1rem;
283
+ color: #818cf8;
284
+ }
285
+
286
+ .bl-stat-change {
287
+ font-size: 0.75rem;
288
+ font-weight: 600;
289
+ padding: 3px 8px;
290
+ border-radius: 6px;
291
+ }
292
+
293
+ .bl-stat-change.up {
294
+ background: rgba(34, 197, 94, 0.1);
295
+ color: #4ade80;
296
+ }
297
+
298
+ .bl-stat-change.down {
299
+ background: rgba(239, 68, 68, 0.1);
300
+ color: #f87171;
301
+ }
302
+
303
+ .bl-stat-value {
304
+ font-size: 1.8rem;
305
+ font-weight: 800;
306
+ letter-spacing: -0.02em;
307
+ margin-bottom: 4px;
308
+ }
309
+
310
+ .bl-stat-label {
311
+ font-size: 0.82rem;
312
+ color: #6b7280;
313
+ font-weight: 500;
314
+ }
315
+
316
+ /* ── Content Grid ── */
317
+ .bl-grid {
318
+ display: grid;
319
+ grid-template-columns: 1fr 380px;
320
+ gap: 20px;
321
+ }
322
+
323
+ .bl-card {
324
+ border-radius: 16px;
325
+ background: #111114;
326
+ border: 1px solid rgba(255,255,255,0.06);
327
+ padding: 24px;
328
+ animation: fade-up 0.6s ease-out 0.25s both;
329
+ }
330
+
331
+ .bl-card-title {
332
+ font-size: 1rem;
333
+ font-weight: 700;
334
+ margin-bottom: 20px;
335
+ display: flex;
336
+ justify-content: space-between;
337
+ align-items: center;
338
+ }
339
+
340
+ .bl-card-badge {
341
+ font-size: 0.7rem;
342
+ padding: 4px 10px;
343
+ border-radius: 6px;
344
+ background: rgba(99, 102, 241, 0.1);
345
+ color: #818cf8;
346
+ font-weight: 600;
347
+ }
348
+
349
+ /* ── Chart placeholder ── */
350
+ .bl-chart {
351
+ height: 200px;
352
+ border-radius: 12px;
353
+ background: linear-gradient(180deg, rgba(99, 102, 241, 0.08) 0%, transparent 100%);
354
+ border: 1px dashed rgba(99, 102, 241, 0.15);
355
+ display: flex;
356
+ align-items: flex-end;
357
+ justify-content: space-around;
358
+ padding: 20px 16px;
359
+ gap: 8px;
360
+ }
361
+
362
+ .bl-bar {
363
+ flex: 1;
364
+ border-radius: 6px 6px 0 0;
365
+ background: linear-gradient(180deg, #6366f1 0%, #4338ca 100%);
366
+ transition: all 0.3s;
367
+ min-height: 10px;
368
+ position: relative;
369
+ }
370
+
371
+ .bl-bar:hover {
372
+ filter: brightness(1.3);
373
+ transform: scaleY(1.05);
374
+ transform-origin: bottom;
375
+ }
376
+
377
+ /* ── Activity List ── */
378
+ .bl-activity-item {
379
+ display: flex;
380
+ align-items: center;
381
+ gap: 14px;
382
+ padding: 14px 0;
383
+ border-bottom: 1px solid rgba(255,255,255,0.04);
384
+ }
385
+
386
+ .bl-activity-item:last-child { border-bottom: none; }
387
+
388
+ .bl-activity-avatar {
389
+ width: 36px; height: 36px;
390
+ border-radius: 10px;
391
+ background: linear-gradient(135deg, #1e1b4b, #312e81);
392
+ display: flex;
393
+ align-items: center;
394
+ justify-content: center;
395
+ font-size: 0.65rem;
396
+ font-weight: 700;
397
+ color: #a5b4fc;
398
+ flex-shrink: 0;
399
+ }
400
+
401
+ .bl-activity-info { flex: 1; }
402
+
403
+ .bl-activity-user {
404
+ font-size: 0.82rem;
405
+ font-weight: 600;
406
+ color: #e5e7eb;
407
+ }
408
+
409
+ .bl-activity-action {
410
+ font-size: 0.78rem;
411
+ color: #6b7280;
412
+ margin-top: 2px;
413
+ }
414
+
415
+ .bl-activity-time {
416
+ font-size: 0.72rem;
417
+ color: #4b5563;
418
+ white-space: nowrap;
419
+ }
420
+
421
+ .bl-online-dot {
422
+ width: 8px; height: 8px;
423
+ border-radius: 50%;
424
+ background: #22c55e;
425
+ animation: pulse-ring 2s infinite;
426
+ }
427
+
428
+ @media (max-width: 1024px) {
429
+ .bl-sidebar { display: none; }
430
+ .bl-main { margin-left: 0; }
431
+ .bl-stats { grid-template-columns: repeat(2, 1fr); }
432
+ .bl-grid { grid-template-columns: 1fr; }
433
+ }
434
+
435
+ @media (max-width: 640px) {
436
+ .bl-stats { grid-template-columns: 1fr; }
437
+ .bl-main { padding: 20px 16px; }
438
+ }
439
+ `,
440
+ }}
441
+ />
442
+
443
+ {/* Sidebar */}
444
+ <aside className="bl-sidebar">
445
+ <div className="bl-sidebar-brand">
446
+ <div className="bl-sidebar-logo">🌸</div>
447
+ <div className="bl-sidebar-name">
448
+ Blumen
449
+ <span>Dashboard</span>
450
+ </div>
451
+ </div>
452
+
453
+ <nav className="bl-nav">
454
+ {navItems.map((item) => (
455
+ <button
456
+ key={item.id}
457
+ className={`bl-nav-item ${activeNav === item.id ? "active" : ""}`}
458
+ onClick={() => setActiveNav(item.id)}
459
+ >
460
+ <span className="bl-nav-icon">{item.icon}</span>
461
+ {item.label}
462
+ </button>
463
+ ))}
464
+ </nav>
465
+
466
+ <div className="bl-sidebar-footer">
467
+ <div className="bl-avatar">JD</div>
468
+ <div>
469
+ <div className="bl-user-name">Jane Doe</div>
470
+ <div className="bl-user-role">Admin</div>
471
+ </div>
472
+ </div>
473
+ </aside>
474
+
475
+ {/* Main Content */}
476
+ <main className="bl-main">
477
+ <div className="bl-header">
478
+ <div>
479
+ <h1 className="bl-header-title">Dashboard</h1>
480
+ <p className="bl-header-sub">Welcome back. Here's what's happening today.</p>
481
+ </div>
482
+ <div className="bl-header-actions">
483
+ <button className="bl-btn bl-btn-ghost">Export</button>
484
+ <button className="bl-btn bl-btn-primary">+ New Project</button>
485
+ </div>
486
+ </div>
487
+
488
+ {/* Stats Row */}
489
+ <div className="bl-stats">
490
+ {stats.map((stat) => (
491
+ <div key={stat.label} className="bl-stat-card">
492
+ <div className="bl-stat-top">
493
+ <div className="bl-stat-icon">{stat.icon}</div>
494
+ <span className={`bl-stat-change ${stat.up ? "up" : "down"}`}>
495
+ {stat.change}
496
+ </span>
497
+ </div>
498
+ <div className="bl-stat-value">{stat.value}</div>
499
+ <div className="bl-stat-label">{stat.label}</div>
500
+ </div>
501
+ ))}
502
+ </div>
503
+
504
+ {/* Content Grid */}
505
+ <div className="bl-grid">
506
+ {/* Chart Card */}
507
+ <div className="bl-card">
508
+ <div className="bl-card-title">
509
+ Revenue Overview
510
+ <span className="bl-card-badge">Last 7 days</span>
511
+ </div>
512
+ <div className="bl-chart">
513
+ {[65, 45, 80, 55, 90, 70, 85].map((h, i) => (
514
+ <div
515
+ key={i}
516
+ className="bl-bar"
517
+ style={{ height: `${h}%`, opacity: 0.6 + (h / 300) }}
518
+ />
519
+ ))}
520
+ </div>
521
+ </div>
522
+
523
+ {/* Activity Feed */}
524
+ <div className="bl-card">
525
+ <div className="bl-card-title">
526
+ Recent Activity
527
+ <div className="bl-online-dot" />
528
+ </div>
529
+ {recentActivity.map((item) => (
530
+ <div key={item.user} className="bl-activity-item">
531
+ <div className="bl-activity-avatar">{item.avatar}</div>
532
+ <div className="bl-activity-info">
533
+ <div className="bl-activity-user">{item.user}</div>
534
+ <div className="bl-activity-action">{item.action}</div>
535
+ </div>
536
+ <div className="bl-activity-time">{item.time}</div>
537
+ </div>
538
+ ))}
539
+ </div>
540
+ </div>
541
+ </main>
542
+ </div>
543
+ );
544
+ };
545
+
546
+ export default DashboardPage;
@@ -0,0 +1,128 @@
1
+ import React from "react";
2
+
3
+ const EmptyPage: React.FC = () => {
4
+ return (
5
+ <div className="blumen-empty">
6
+ <style
7
+ dangerouslySetInnerHTML={{
8
+ __html: `
9
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap');
10
+
11
+ @keyframes fade-in {
12
+ from { opacity: 0; transform: translateY(20px); }
13
+ to { opacity: 1; transform: translateY(0); }
14
+ }
15
+
16
+ @keyframes float {
17
+ 0%, 100% { transform: translateY(0); }
18
+ 50% { transform: translateY(-8px); }
19
+ }
20
+
21
+ @keyframes pulse {
22
+ 0%, 100% { opacity: 0.4; }
23
+ 50% { opacity: 1; }
24
+ }
25
+
26
+ .blumen-empty * { box-sizing: border-box; margin: 0; padding: 0; }
27
+
28
+ .blumen-empty {
29
+ min-height: 100vh;
30
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
31
+ background: #0c0c0c;
32
+ color: #fafafa;
33
+ display: flex;
34
+ align-items: center;
35
+ justify-content: center;
36
+ position: relative;
37
+ overflow: hidden;
38
+ }
39
+
40
+ .blumen-empty::before {
41
+ content: '';
42
+ position: absolute;
43
+ top: 50%; left: 50%;
44
+ width: 600px; height: 600px;
45
+ transform: translate(-50%, -50%);
46
+ background: radial-gradient(circle, rgba(99, 102, 241, 0.08) 0%, transparent 70%);
47
+ pointer-events: none;
48
+ }
49
+
50
+ .blumen-empty-center {
51
+ position: relative;
52
+ z-index: 1;
53
+ text-align: center;
54
+ animation: fade-in 1s ease-out both;
55
+ }
56
+
57
+ .blumen-empty-flower {
58
+ font-size: 3rem;
59
+ animation: float 3s ease-in-out infinite;
60
+ margin-bottom: 2rem;
61
+ }
62
+
63
+ .blumen-empty-title {
64
+ font-size: 2.5rem;
65
+ font-weight: 800;
66
+ letter-spacing: -0.03em;
67
+ margin-bottom: 1rem;
68
+ background: linear-gradient(135deg, #fff 0%, #a5b4fc 100%);
69
+ -webkit-background-clip: text;
70
+ -webkit-text-fill-color: transparent;
71
+ background-clip: text;
72
+ }
73
+
74
+ .blumen-empty-sub {
75
+ font-size: 1rem;
76
+ color: #6b7280;
77
+ font-weight: 400;
78
+ margin-bottom: 2.5rem;
79
+ line-height: 1.6;
80
+ }
81
+
82
+ .blumen-empty-sub code {
83
+ background: rgba(99, 102, 241, 0.1);
84
+ border: 1px solid rgba(99, 102, 241, 0.2);
85
+ padding: 2px 10px;
86
+ border-radius: 6px;
87
+ color: #a5b4fc;
88
+ font-family: 'SF Mono', 'Fira Code', monospace;
89
+ font-size: 0.88rem;
90
+ }
91
+
92
+ .blumen-empty-hint {
93
+ display: inline-flex;
94
+ align-items: center;
95
+ gap: 8px;
96
+ padding: 10px 20px;
97
+ border-radius: 10px;
98
+ background: rgba(255,255,255,0.04);
99
+ border: 1px solid rgba(255,255,255,0.08);
100
+ font-size: 0.82rem;
101
+ color: #9ca3af;
102
+ }
103
+
104
+ .blumen-empty-hint-dot {
105
+ width: 6px; height: 6px;
106
+ border-radius: 50%;
107
+ background: #6366f1;
108
+ animation: pulse 2s ease-in-out infinite;
109
+ }
110
+ `,
111
+ }}
112
+ />
113
+ <div className="blumen-empty-center">
114
+ <div className="blumen-empty-flower">🌸</div>
115
+ <h1 className="blumen-empty-title">Ready to build.</h1>
116
+ <p className="blumen-empty-sub">
117
+ Edit <code>app/pages/Home.tsx</code> to get started.
118
+ </p>
119
+ <div className="blumen-empty-hint">
120
+ <span className="blumen-empty-hint-dot" />
121
+ Server-side rendered with Go + React
122
+ </div>
123
+ </div>
124
+ </div>
125
+ );
126
+ };
127
+
128
+ export default EmptyPage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blumenjs",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "The React framework powered by Go. Lightning-fast SSR with the DX you deserve.",
5
5
  "type": "module",
6
6
  "bin": {