blumenjs 0.1.4 → 0.1.6

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,344 @@
1
+ import React, { useState } from "react";
2
+
3
+ const DashboardPage: React.FC = () => {
4
+ const [activeNav, setActiveNav] = useState("dashboard");
5
+ const nav = [
6
+ { id: "dashboard", label: "Dashboard" }, { id: "payments", label: "Payments" },
7
+ { id: "transactions", label: "Transactions" }, { id: "invoices", label: "Invoices" },
8
+ { id: "cards", label: "Cards" }, { id: "savings", label: "Saving Plans" },
9
+ { id: "investments", label: "Investments" }, { id: "inbox", label: "Inbox" },
10
+ ];
11
+ const stats = [
12
+ { label: "Total Income", value: "$78,000", change: "+17.5%", up: true },
13
+ { label: "Total Expense", value: "$43,000", change: "-12.4%", up: false },
14
+ { label: "Total Savings", value: "$56,000", change: "+24.5%", up: true },
15
+ ];
16
+ const bars = [
17
+ { m: "Jan", i: 50, e: 35 }, { m: "Feb", i: 55, e: 40 }, { m: "Mar", i: 48, e: 45 },
18
+ { m: "Apr", i: 62, e: 38 }, { m: "May", i: 58, e: 48 }, { m: "Jun", i: 72, e: 42 },
19
+ { m: "Jul", i: 60, e: 38 }, { m: "Aug", i: 68, e: 52 }, { m: "Sep", i: 75, e: 50 },
20
+ { m: "Oct", i: 82, e: 58 }, { m: "Nov", i: 70, e: 45 }, { m: "Dec", i: 78, e: 52 },
21
+ ];
22
+ const txns = [
23
+ { name: "Electricity Bill", cat: "Payments", date: "2028-03-01", amt: "$295.81", note: "Monthly electricity bill", status: "Failed" },
24
+ { name: "Weekly Groceries", cat: "Shopping", date: "2028-03-04", amt: "$204.07", note: "Local supermarket", status: "Completed" },
25
+ { name: "Movie Night", cat: "Entertainment", date: "2028-02-27", amt: "$97.84", note: "Tickets and snacks", status: "Pending" },
26
+ { name: "Medical Check-up", cat: "Healthcare", date: "2028-02-07", amt: "$323.33", note: "Routine health check", status: "Pending" },
27
+ ];
28
+ const funds = [
29
+ { name: "Emergency Fund", saved: "$5,000", pct: 50, target: "$10,000" },
30
+ { name: "Vacation Fund", saved: "$3,000", pct: 60, target: "$5,000" },
31
+ { name: "Home Down Payment", saved: "$7,250", pct: 36, target: "$20,000" },
32
+ ];
33
+ const expenses = [
34
+ { label: "Rent & Living", pct: "60%", amt: "$2,100", c: "#16a34a" },
35
+ { label: "Investment", pct: "15%", amt: "$525", c: "#22c55e" },
36
+ { label: "Education", pct: "12%", amt: "$420", c: "#4ade80" },
37
+ { label: "Food & Drink", pct: "8%", amt: "$280", c: "#86efac" },
38
+ { label: "Entertainment", pct: "5%", amt: "$175", c: "#bbf7d0" },
39
+ ];
40
+ const acts = [
41
+ { u: "Jamie Smith", a: "updated account settings", t: "16:05", c: "#22c55e" },
42
+ { u: "Alex Johnson", a: "logged in", t: "13:05", c: "#3b82f6" },
43
+ { u: "Morgan Lee", a: "added a new savings goal", t: "02:05", c: "#f59e0b" },
44
+ ];
45
+ const actsY = [
46
+ { u: "Taylor Green", a: "reviewed recent transactions", t: "21:05", c: "#8b5cf6" },
47
+ { u: "Wilson Baptista", a: "transferred funds", t: "09:06", c: "#ef4444" },
48
+ ];
49
+ const sc: Record<string, { bg: string; fg: string }> = {
50
+ Failed: { bg: "#fef2f2", fg: "#dc2626" },
51
+ Completed: { bg: "#f0fdf4", fg: "#16a34a" },
52
+ Pending: { bg: "#fffbeb", fg: "#d97706" },
53
+ };
54
+
55
+ return (
56
+ <div className="fd">
57
+ <style dangerouslySetInnerHTML={{ __html: `
58
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap');
59
+ @keyframes fu{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
60
+ .fd *{box-sizing:border-box;margin:0;padding:0;font-family:'Inter',sans-serif}
61
+ .fd{min-height:100vh;background:#f0f2f0;display:flex;color:#1a1a2e}
62
+
63
+ /* Sidebar */
64
+ .sb{width:200px;background:#fff;border-right:1px solid #e5e7eb;padding:16px 10px;display:flex;flex-direction:column;position:fixed;top:0;bottom:0;left:0;z-index:10}
65
+ .sb-brand{display:flex;align-items:center;gap:8px;padding:8px 10px;margin-bottom:20px}
66
+ .sb-logo{width:28px;height:28px;border-radius:7px;background:linear-gradient(135deg,#16a34a,#22c55e);display:flex;align-items:center;justify-content:center;font-size:0.75rem;color:#fff;font-weight:800}
67
+ .sb-name{font-size:0.9rem;font-weight:800;letter-spacing:-0.01em}
68
+ .sb-nav{list-style:none;flex:1}
69
+ .sb-ni{display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:8px;font-size:0.78rem;font-weight:500;color:#6b7280;cursor:pointer;transition:all 0.15s;margin-bottom:1px;border:none;background:none;width:100%;text-align:left}
70
+ .sb-ni:hover{color:#16a34a;background:#f0fdf4}
71
+ .sb-ni.on{color:#fff;background:#16a34a;font-weight:600}
72
+ .sb-cta{margin-top:auto;padding:16px;border-radius:12px;background:linear-gradient(135deg,#0f3d1e,#14532d);color:#fff;text-align:center}
73
+ .sb-cta p{font-size:0.65rem;color:#86efac;margin:6px 0 10px;line-height:1.4}
74
+ .sb-cta-btn{padding:6px 16px;border-radius:6px;background:#22c55e;color:#fff;font-size:0.72rem;font-weight:600;border:none;cursor:pointer}
75
+
76
+ /* Layout */
77
+ .mn{margin-left:200px;display:grid;grid-template-columns:240px 1fr 260px;min-height:100vh}
78
+ .lp{padding:16px 12px;background:#fff;border-right:1px solid #e8ebe8}
79
+ .cp{padding:16px}
80
+ .rp{padding:16px 12px;background:#fff;border-left:1px solid #e8ebe8}
81
+
82
+ /* Components */
83
+ .cd{background:#fff;border-radius:12px;border:1px solid #e8ebe8;padding:14px;margin-bottom:12px;animation:fu 0.4s ease-out both}
84
+ .hdr{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px}
85
+ .hdr h1{font-size:1.15rem;font-weight:800}
86
+ .search{padding:6px 14px;border-radius:8px;border:1px solid #e5e7eb;background:#f9fafb;font-size:0.78rem;width:200px}
87
+ .uav{width:32px;height:32px;border-radius:50%;background:linear-gradient(135deg,#16a34a,#4ade80);display:flex;align-items:center;justify-content:center;font-size:0.65rem;font-weight:700;color:#fff}
88
+
89
+ /* User Card */
90
+ .uc{background:linear-gradient(135deg,#14532d,#166534);border-radius:14px;padding:16px;color:#fff;margin-bottom:12px;position:relative;overflow:hidden}
91
+ .uc::after{content:'';position:absolute;top:-25px;right:-25px;width:100px;height:100px;border-radius:50%;background:rgba(255,255,255,0.05)}
92
+ .uc-n{font-size:0.9rem;font-weight:700;margin:8px 0 12px}
93
+ .uc-b{font-size:0.65rem;color:#86efac}
94
+ .uc-a{font-size:1.3rem;font-weight:800;margin:2px 0}
95
+ .uc-m{display:flex;gap:16px;margin-top:10px;font-size:0.65rem;color:#86efac}
96
+
97
+ /* Quick Actions */
98
+ .qa{display:grid;grid-template-columns:repeat(4,1fr);gap:6px;margin-bottom:12px}
99
+ .qa-i{text-align:center;padding:8px 2px;border-radius:8px;background:#f0fdf4;border:1px solid #dcfce7;cursor:pointer;transition:all 0.15s}
100
+ .qa-i:hover{background:#dcfce7;transform:translateY(-1px)}
101
+ .qa-ic{font-size:0.9rem;margin-bottom:2px}
102
+ .qa-lb{font-size:0.6rem;color:#6b7280;font-weight:500}
103
+
104
+ /* Progress bars */
105
+ .pb{height:5px;border-radius:3px;background:#e5e7eb;margin-top:6px;overflow:hidden}
106
+ .pf{height:100%;border-radius:3px;background:linear-gradient(90deg,#16a34a,#22c55e)}
107
+
108
+ /* Savings */
109
+ .sv{padding:10px 0;border-bottom:1px solid #f3f4f6}
110
+ .sv:last-child{border-bottom:none}
111
+ .sv-n{font-size:0.78rem;font-weight:600;margin-bottom:4px}
112
+ .sv-b{height:5px;border-radius:3px;background:#e5e7eb;margin:4px 0;overflow:hidden}
113
+ .sv-f{height:100%;border-radius:3px;background:#22c55e}
114
+ .sv-m{display:flex;justify-content:space-between;font-size:0.65rem;color:#9ca3af}
115
+
116
+ /* Stats */
117
+ .sts{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:14px}
118
+ .st{background:#fff;border-radius:12px;border:1px solid #e8ebe8;padding:14px;animation:fu 0.35s ease-out both}
119
+ .st:nth-child(2){animation-delay:0.05s}
120
+ .st:nth-child(3){animation-delay:0.1s}
121
+ .st-ic{width:32px;height:32px;border-radius:8px;background:#f0fdf4;display:flex;align-items:center;justify-content:center;font-size:0.8rem;margin-bottom:10px}
122
+ .st-v{font-size:1.3rem;font-weight:800;letter-spacing:-0.02em}
123
+ .st-l{font-size:0.72rem;color:#9ca3af;margin-top:2px}
124
+ .st-c{font-size:0.65rem;font-weight:600;padding:2px 6px;border-radius:4px;margin-top:6px;display:inline-block}
125
+ .st-c.up{background:#f0fdf4;color:#16a34a}
126
+ .st-c.dn{background:#fef2f2;color:#dc2626}
127
+
128
+ /* Chart */
129
+ .ch{display:flex;align-items:flex-end;gap:4px;height:140px;padding-top:6px}
130
+ .ch-c{flex:1;display:flex;flex-direction:column;align-items:center;gap:2px}
131
+ .ch-bw{flex:1;width:100%;display:flex;gap:2px;align-items:flex-end;justify-content:center}
132
+ .ch-b{border-radius:2px 2px 0 0;min-width:6px;transition:all 0.2s}
133
+ .ch-b:hover{filter:brightness(1.15)}
134
+ .ch-b.inc{background:#16a34a}
135
+ .ch-b.exp{background:#bbf7d0}
136
+ .ch-m{font-size:0.55rem;color:#9ca3af;margin-top:3px}
137
+ .lg{display:flex;gap:12px;font-size:0.68rem;color:#6b7280}
138
+ .lg-d{width:7px;height:7px;border-radius:2px;display:inline-block;margin-right:3px}
139
+
140
+ /* Table */
141
+ .tb{width:100%;border-collapse:collapse;font-size:0.72rem}
142
+ .tb th{text-align:left;padding:6px 8px;color:#9ca3af;font-weight:500;font-size:0.65rem;border-bottom:1px solid #f3f4f6}
143
+ .tb td{padding:8px;border-bottom:1px solid #f9fafb;color:#374151}
144
+ .tb-n{font-weight:600;color:#1a1a2e;font-size:0.75rem}
145
+ .tb-c{font-size:0.65rem;color:#9ca3af}
146
+ .badge{padding:2px 7px;border-radius:5px;font-size:0.62rem;font-weight:600}
147
+
148
+ /* Donut */
149
+ .dn-w{display:flex;justify-content:center;margin:12px 0}
150
+
151
+ /* Expense list */
152
+ .ex{display:flex;align-items:center;padding:5px 0}
153
+ .ex-d{width:7px;height:7px;border-radius:50%;margin-right:7px;flex-shrink:0}
154
+ .ex-p{font-size:0.65rem;color:#9ca3af;margin-right:10px;min-width:24px;font-weight:600}
155
+ .ex-l{flex:1;font-size:0.75rem;color:#374151}
156
+ .ex-a{font-size:0.78rem;font-weight:600;text-align:right}
157
+
158
+ /* Activity */
159
+ .ac{display:flex;gap:8px;padding:6px 0}
160
+ .ac-d{width:7px;height:7px;border-radius:50%;margin-top:5px;flex-shrink:0}
161
+ .ac-t{flex:1;font-size:0.72rem;color:#374151;line-height:1.4}
162
+ .ac-u{font-weight:600;color:#1a1a2e}
163
+ .ac-tm{font-size:0.62rem;color:#9ca3af;margin-top:1px}
164
+
165
+ .stl{font-size:0.82rem;font-weight:700;margin-bottom:10px;display:flex;justify-content:space-between;align-items:center}
166
+ .stl span{font-size:0.65rem;color:#16a34a;font-weight:500;cursor:pointer}
167
+ .sub{font-size:0.65rem;color:#9ca3af}
168
+ .big{font-size:1.15rem;font-weight:800;margin-bottom:8px}
169
+
170
+ @media(max-width:1100px){.mn{grid-template-columns:1fr}.lp,.rp{display:none}}
171
+ @media(max-width:768px){.sb{display:none}.mn{margin-left:0}.sts{grid-template-columns:1fr}}
172
+ `}} />
173
+
174
+ {/* Sidebar */}
175
+ <aside className="sb">
176
+ <div className="sb-brand">
177
+ <div className="sb-logo">B</div>
178
+ <div className="sb-name">BLUMEN</div>
179
+ </div>
180
+ <nav className="sb-nav">
181
+ {nav.map(n => (
182
+ <button key={n.id} className={`sb-ni ${activeNav === n.id ? "on" : ""}`} onClick={() => setActiveNav(n.id)}>
183
+ {n.label}
184
+ </button>
185
+ ))}
186
+ </nav>
187
+ <div className="sb-cta">
188
+ <div style={{ fontSize: "1.5rem" }}>🌸</div>
189
+ <p>Gain full access to your finances with detailed analytics</p>
190
+ <button className="sb-cta-btn">Get Pro</button>
191
+ </div>
192
+ </aside>
193
+
194
+ <div className="mn">
195
+ {/* Left Panel */}
196
+ <div className="lp">
197
+ <div className="uc">
198
+ <div style={{ fontSize: "1.2rem" }}>🌸</div>
199
+ <div className="uc-n">Andrew Forbist</div>
200
+ <div className="uc-b">Balance Amount</div>
201
+ <div className="uc-a">$562,000</div>
202
+ <div className="uc-m"><span>EXP 11/29</span><span>CVV 323</span></div>
203
+ </div>
204
+
205
+ <div className="qa">
206
+ {[["⊕","Top Up"],["⇄","Transfer"],["↗","Request"],["◷","History"]].map(([ic,lb]) => (
207
+ <div key={lb} className="qa-i"><div className="qa-ic">{ic}</div><div className="qa-lb">{lb}</div></div>
208
+ ))}
209
+ </div>
210
+
211
+ <div className="cd">
212
+ <div className="stl">Daily Limit</div>
213
+ <div style={{ fontSize: "0.78rem", fontWeight: 600 }}>$2,500.00 <span className="sub">spent of $20,000.00</span></div>
214
+ <div className="pb"><div className="pf" style={{ width: "12.5%" }} /></div>
215
+ <div style={{ textAlign: "right", fontSize: "0.62rem", color: "#9ca3af", marginTop: "3px" }}>12.5%</div>
216
+ </div>
217
+
218
+ <div className="cd">
219
+ <div className="stl">Saving Plans <span>+ Add Plan</span></div>
220
+ <div className="sub">Total Savings</div>
221
+ <div className="big">$84,500</div>
222
+ {funds.map(f => (
223
+ <div key={f.name} className="sv">
224
+ <div className="sv-n">{f.name}</div>
225
+ <div className="sv-b"><div className="sv-f" style={{ width: `${f.pct}%` }} /></div>
226
+ <div className="sv-m"><span>{f.saved} {f.pct}%</span><span>Target: {f.target}</span></div>
227
+ </div>
228
+ ))}
229
+ </div>
230
+ </div>
231
+
232
+ {/* Center */}
233
+ <div className="cp">
234
+ <div className="hdr">
235
+ <h1>Dashboard</h1>
236
+ <div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
237
+ <input className="search" placeholder="Search placeholder" />
238
+ <div className="uav">AF</div>
239
+ <span style={{ fontSize: "0.82rem", fontWeight: 600 }}>Andrew Forbist</span>
240
+ </div>
241
+ </div>
242
+
243
+ <div className="sts">
244
+ {stats.map(s => (
245
+ <div key={s.label} className="st">
246
+ <div className="st-ic">💰</div>
247
+ <div className="st-v">{s.value}</div>
248
+ <div className="st-l">{s.label}</div>
249
+ <span className={`st-c ${s.up ? "up" : "dn"}`}>{s.change}</span>
250
+ </div>
251
+ ))}
252
+ </div>
253
+
254
+ <div className="cd" style={{ animationDelay: "0.12s" }}>
255
+ <div style={{ display: "flex", justifyContent: "space-between", alignItems: "flex-start" }}>
256
+ <div>
257
+ <div className="stl" style={{ marginBottom: 4 }}>Cashflow</div>
258
+ <div className="sub">Total Balance</div>
259
+ <div style={{ fontSize: "1.2rem", fontWeight: 800, margin: "2px 0 10px" }}>$562,000</div>
260
+ </div>
261
+ <div className="lg">
262
+ <span><span className="lg-d" style={{ background: "#16a34a" }} />Income</span>
263
+ <span><span className="lg-d" style={{ background: "#bbf7d0" }} />Expense</span>
264
+ </div>
265
+ </div>
266
+ <div className="ch">
267
+ {bars.map(d => (
268
+ <div key={d.m} className="ch-c">
269
+ <div className="ch-bw">
270
+ <div className="ch-b inc" style={{ height: `${d.i * 1.6}px` }} />
271
+ <div className="ch-b exp" style={{ height: `${d.e * 1.6}px` }} />
272
+ </div>
273
+ <div className="ch-m">{d.m}</div>
274
+ </div>
275
+ ))}
276
+ </div>
277
+ </div>
278
+
279
+ <div className="cd" style={{ animationDelay: "0.2s" }}>
280
+ <div className="stl">Recent Transactions</div>
281
+ <table className="tb">
282
+ <thead><tr><th>Transaction Name</th><th>Date</th><th>Amount</th><th>Note</th><th>Status</th></tr></thead>
283
+ <tbody>
284
+ {txns.map(t => (
285
+ <tr key={t.name}>
286
+ <td><div className="tb-n">{t.name}</div><div className="tb-c">{t.cat}</div></td>
287
+ <td>{t.date}</td>
288
+ <td>{t.amt}</td>
289
+ <td style={{ color: "#9ca3af", fontSize: "0.65rem" }}>{t.note}</td>
290
+ <td><span className="badge" style={{ background: sc[t.status].bg, color: sc[t.status].fg }}>{t.status}</span></td>
291
+ </tr>
292
+ ))}
293
+ </tbody>
294
+ </table>
295
+ </div>
296
+ </div>
297
+
298
+ {/* Right Panel */}
299
+ <div className="rp">
300
+ <div className="cd">
301
+ <div className="stl">Statistic</div>
302
+ <div className="dn-w">
303
+ <svg width="120" height="120" viewBox="0 0 120 120">
304
+ <circle cx="60" cy="60" r="46" fill="none" stroke="#e5e7eb" strokeWidth="12" />
305
+ <circle cx="60" cy="60" r="46" fill="none" stroke="#16a34a" strokeWidth="12" strokeDasharray="289" strokeDashoffset="72" strokeLinecap="round" transform="rotate(-90 60 60)" />
306
+ <circle cx="60" cy="60" r="46" fill="none" stroke="#4ade80" strokeWidth="12" strokeDasharray="289" strokeDashoffset="246" strokeLinecap="round" transform="rotate(150 60 60)" />
307
+ <text x="60" y="55" textAnchor="middle" fill="#9ca3af" fontSize="7" fontFamily="Inter">Total Expense</text>
308
+ <text x="60" y="70" textAnchor="middle" fill="#1a1a2e" fontSize="14" fontWeight="800" fontFamily="Inter">$3,500</text>
309
+ </svg>
310
+ </div>
311
+ {expenses.map(e => (
312
+ <div key={e.label} className="ex">
313
+ <span className="ex-d" style={{ background: e.c }} />
314
+ <span className="ex-p">{e.pct}</span>
315
+ <span className="ex-l">{e.label}</span>
316
+ <span className="ex-a">{e.amt}</span>
317
+ </div>
318
+ ))}
319
+ </div>
320
+
321
+ <div className="cd" style={{ animationDelay: "0.1s" }}>
322
+ <div className="stl">Recent Activity</div>
323
+ <div style={{ fontSize: "0.68rem", fontWeight: 600, color: "#9ca3af", marginBottom: "6px" }}>Today</div>
324
+ {acts.map(a => (
325
+ <div key={a.u} className="ac">
326
+ <span className="ac-d" style={{ background: a.c }} />
327
+ <div><div className="ac-t"><span className="ac-u">{a.u}</span> {a.a}</div><div className="ac-tm">{a.t}</div></div>
328
+ </div>
329
+ ))}
330
+ <div style={{ fontSize: "0.68rem", fontWeight: 600, color: "#9ca3af", margin: "10px 0 6px" }}>Yesterday</div>
331
+ {actsY.map(a => (
332
+ <div key={a.u} className="ac">
333
+ <span className="ac-d" style={{ background: a.c }} />
334
+ <div><div className="ac-t"><span className="ac-u">{a.u}</span> {a.a}</div><div className="ac-tm">{a.t}</div></div>
335
+ </div>
336
+ ))}
337
+ </div>
338
+ </div>
339
+ </div>
340
+ </div>
341
+ );
342
+ };
343
+
344
+ 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.6",
4
4
  "description": "The React framework powered by Go. Lightning-fast SSR with the DX you deserve.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,6 +10,7 @@
10
10
  "files": [
11
11
  "dist/cli/**/*",
12
12
  "dist/templates/**/*",
13
+ "cli/postinstall.cjs",
13
14
  "README.md",
14
15
  "LICENSE"
15
16
  ],
@@ -20,6 +21,7 @@
20
21
  "start": "tsx cli/blumen.ts start",
21
22
  "create": "tsx cli/blumen.ts create",
22
23
  "build:cli": "tsx cli/build-cli.ts",
24
+ "postinstall": "node cli/postinstall.cjs",
23
25
  "prepublishOnly": "npm run build:cli",
24
26
  "dev:legacy": "npm run routes && concurrently \"npm run dev:client\" \"npm run dev:ssr\" \"npm run dev:go\"",
25
27
  "dev:client": "webpack serve --mode development",