create-fluxstack 1.22.0 → 1.22.1

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.
Files changed (38) hide show
  1. package/app/client/src/components/AppLayout.tsx +290 -290
  2. package/app/client/src/components/BackButton.tsx +16 -16
  3. package/app/client/src/components/DemoPage.tsx +135 -135
  4. package/app/client/src/framework/ClientOnly.tsx +14 -14
  5. package/app/client/src/framework/LivePage.tsx +55 -55
  6. package/app/client/src/framework/RscHomePage.tsx +125 -125
  7. package/app/client/src/framework/RscLink.tsx +31 -31
  8. package/app/client/src/framework/RscNav.tsx +42 -42
  9. package/app/client/src/framework/RscRoot.tsx +54 -54
  10. package/app/client/src/framework/csrf.ts +22 -22
  11. package/app/client/src/framework/entry.browser.tsx +51 -51
  12. package/app/client/src/framework/entry.rsc.tsx +41 -41
  13. package/app/client/src/framework/entry.ssr.tsx +12 -12
  14. package/app/client/src/framework/navigation.ts +29 -29
  15. package/app/client/src/framework/params.tsx +22 -22
  16. package/app/client/src/framework/routes.ts +143 -143
  17. package/app/client/src/framework/vite-rsc.d.ts +21 -21
  18. package/app/client/src/live/AuthDemo.tsx +270 -270
  19. package/app/client/src/live/CounterDemo.tsx +152 -152
  20. package/app/client/src/live/FormDemo.tsx +140 -140
  21. package/app/client/src/live/PingPongDemo.tsx +180 -180
  22. package/app/client/src/live/RoomChatDemo.tsx +397 -397
  23. package/app/client/src/pages/auth.tsx +13 -13
  24. package/app/client/src/pages/blog/[slug].tsx +23 -23
  25. package/app/client/src/pages/counter.tsx +15 -15
  26. package/app/client/src/pages/form.tsx +13 -13
  27. package/app/client/src/pages/index.tsx +9 -9
  28. package/app/client/src/pages/ping-pong.tsx +13 -13
  29. package/app/client/src/pages/room-chat.tsx +13 -13
  30. package/app/client/src/pages/shared-counter.tsx +13 -13
  31. package/app/client/src/pages/sobre.tsx +19 -19
  32. package/app/server/live/rooms/index.ts +14 -14
  33. package/core/plugins/built-in/rsc/index.ts +156 -156
  34. package/core/plugins/built-in/ssr/bun-asset-loader.ts +46 -46
  35. package/core/plugins/built-in/ssr/index.ts +143 -143
  36. package/core/plugins/built-in/ssr/registry.ts +38 -38
  37. package/core/utils/version.ts +1 -1
  38. package/package.json +1 -1
@@ -1,291 +1,291 @@
1
1
  'use client'
2
- import { useState, useEffect } from 'react'
3
- import type { ReactNode } from 'react'
4
- import { Link, Outlet, useLocation } from 'react-router'
5
- import { FaBars, FaBook, FaChevronDown, FaExternalLinkAlt, FaGithub, FaTimes } from 'react-icons/fa'
6
- import FluxStackLogo from '@client/src/assets/fluxstack.svg'
7
- import faviconSvg from '@client/src/assets/fluxstack-static.svg?raw'
8
- import { useThemeClock } from '../hooks/useThemeClock'
9
- import { ThemePicker } from './ThemePicker'
10
- import type { ColorPalette } from '../lib/theme-clock'
11
- import { themeConfig } from '../config/theme.config'
12
-
13
- const navItems = [
14
- { to: '/', label: 'Home' },
15
- ]
16
-
17
- const demoItems = [
18
- { to: '/counter', label: 'Counter' },
19
- { to: '/form', label: 'Form' },
20
-
21
- { to: '/room-chat', label: 'Room Chat' },
22
- { to: '/auth', label: 'Auth' },
23
- { to: '/ping-pong', label: 'Ping Pong' },
24
- ]
25
-
26
- const allRouteItems = [...navItems, ...demoItems]
27
-
28
- const MAX_FAVICON_CACHE = 20
29
- const faviconUrlCache = new Map<string, string>()
30
-
31
- function HeaderLink({
32
- href,
33
- children,
34
- subtle = false,
35
- }: {
36
- href: string
37
- children: ReactNode
38
- subtle?: boolean
39
- }) {
40
- return (
41
- <a
42
- href={href}
43
- target="_blank"
44
- rel="noopener noreferrer"
45
- className={`hidden h-9 items-center gap-2 rounded-lg border px-3 text-sm transition sm:inline-flex ${
46
- subtle
47
- ? 'border-white/10 bg-white/[0.025] text-gray-400 hover:border-white/20 hover:bg-white/[0.05] hover:text-white'
48
- : 'border-theme-active bg-theme-muted text-theme hover:shadow-theme'
49
- }`}
50
- >
51
- {children}
52
- </a>
53
- )
54
- }
55
-
56
- export function AppLayout() {
57
- const location = useLocation()
58
- const [menuOpen, setMenuOpen] = useState(false)
59
- const [demosOpen, setDemosOpen] = useState(false)
60
- const autoTheme = useThemeClock()
61
- const [overrideTheme, setOverrideTheme] = useState<ColorPalette | null>(null)
62
- const theme = overrideTheme || autoTheme
63
-
64
- useEffect(() => {
65
- setDemosOpen(false)
66
- setMenuOpen(false)
67
- }, [location.pathname])
68
-
69
- useEffect(() => {
70
- const current = allRouteItems.find(item => item.to === location.pathname)
71
- document.title = current ? `${current.label} - FluxStack` : 'FluxStack'
72
-
73
- const hue = `${Math.round(theme.baseHue - 270)}deg`
74
- let url = faviconUrlCache.get(hue)
75
- if (!url) {
76
- if (faviconUrlCache.size >= MAX_FAVICON_CACHE) {
77
- const oldestKey = faviconUrlCache.keys().next().value!
78
- const oldestUrl = faviconUrlCache.get(oldestKey)!
79
- URL.revokeObjectURL(oldestUrl)
80
- faviconUrlCache.delete(oldestKey)
81
- }
82
- const colored = faviconSvg.replace(
83
- '<svg ',
84
- `<svg style="filter: hue-rotate(${hue})" `
85
- )
86
- const blob = new Blob([colored], { type: 'image/svg+xml' })
87
- url = URL.createObjectURL(blob)
88
- faviconUrlCache.set(hue, url)
89
- }
90
- let link = document.querySelector<HTMLLinkElement>('link[rel="icon"]')
91
- if (!link) {
92
- link = document.createElement('link')
93
- link.rel = 'icon'
94
- document.head.appendChild(link)
95
- }
96
- link.type = 'image/svg+xml'
97
- link.href = url
98
- }, [location.pathname, theme.baseHue])
99
-
100
- const activeDemo = demoItems.some(item => item.to === location.pathname)
101
-
102
- return (
103
- <div
104
- className="min-h-screen text-white"
105
- style={{ backgroundColor: `oklch(7% 0.018 ${theme.baseHue})` }}
106
- >
107
- <header className="sticky top-0 z-50 border-b border-white/10 bg-black/45 backdrop-blur-2xl">
108
- <div className="mx-auto flex h-16 max-w-7xl items-center justify-between gap-4 px-4 sm:px-6 lg:px-8">
109
- <Link to="/" className="group flex min-w-0 items-center gap-3">
110
- <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-white/10 bg-white/[0.04]">
111
- <img
112
- src={FluxStackLogo}
113
- alt="FluxStack"
114
- className="h-6 w-6 transition-[filter] duration-500"
115
- style={{
116
- filter: `hue-rotate(${theme.baseHue - 270}deg) drop-shadow(0 0 8px ${theme.primaryGlow})`,
117
- }}
118
- />
119
- </span>
120
- <span className="truncate text-sm font-semibold tracking-tight text-white">
121
- FluxStack
122
- </span>
123
- </Link>
124
-
125
- <nav className="hidden min-w-0 flex-1 items-center justify-center md:flex">
126
- <div className="relative flex max-w-full items-center gap-1 rounded-lg border border-white/10 bg-white/[0.025] p-1">
127
- {navItems.map((item) => {
128
- const active = location.pathname === item.to
129
- return (
130
- <Link
131
- key={item.to}
132
- to={item.to}
133
- className={`whitespace-nowrap rounded-md px-3 py-1.5 text-sm transition ${
134
- active
135
- ? 'bg-white text-black shadow-sm'
136
- : 'text-gray-400 hover:bg-white/[0.06] hover:text-white'
137
- }`}
138
- >
139
- {item.label}
140
- </Link>
141
- )
142
- })}
143
- <div className="relative">
144
- <button
145
- type="button"
146
- onClick={() => setDemosOpen(open => !open)}
147
- className={`inline-flex items-center gap-2 whitespace-nowrap rounded-md px-3 py-1.5 text-sm transition ${
148
- activeDemo
149
- ? 'bg-white text-black shadow-sm'
150
- : 'text-gray-400 hover:bg-white/[0.06] hover:text-white'
151
- }`}
152
- aria-expanded={demosOpen}
153
- >
154
- Live Demos
155
- <FaChevronDown className={`h-2.5 w-2.5 transition ${demosOpen ? 'rotate-180' : ''}`} />
156
- </button>
157
-
158
- {demosOpen && (
159
- <div className="absolute left-1/2 top-11 z-50 w-72 -translate-x-1/2 rounded-lg border border-white/10 bg-[#07070b]/95 p-2 shadow-2xl shadow-black/40 backdrop-blur-2xl">
160
- <div className="px-3 py-2">
161
- <p className="text-xs font-semibold uppercase tracking-[0.18em] text-gray-500">Live Components</p>
162
- <p className="mt-1 text-xs leading-5 text-gray-400">Demos de estado no servidor, salas e auth.</p>
163
- </div>
164
- <div className="mt-1 grid gap-1">
165
- {demoItems.map((item) => {
166
- const active = location.pathname === item.to
167
- return (
168
- <Link
169
- key={item.to}
170
- to={item.to}
171
- className={`rounded-md px-3 py-2 text-sm transition ${
172
- active
173
- ? 'bg-white text-black'
174
- : 'text-gray-300 hover:bg-white/[0.06] hover:text-white'
175
- }`}
176
- >
177
- {item.label}
178
- </Link>
179
- )
180
- })}
181
- </div>
182
- </div>
183
- )}
184
- </div>
185
- </div>
186
- </nav>
187
-
188
- <div className="flex shrink-0 items-center gap-2">
189
- <HeaderLink href="https://live-docs.marcosbrendon.com/">
190
- <FaBook className="h-3.5 w-3.5" />
191
- Live Docs
192
- <FaExternalLinkAlt className="h-2.5 w-2.5 opacity-60" />
193
- </HeaderLink>
194
- <HeaderLink href="/swagger" subtle>
195
- <FaBook className="h-3.5 w-3.5" />
196
- API
197
- </HeaderLink>
198
- <HeaderLink href="https://github.com/MarcosBrendonDePaula/FluxStack" subtle>
199
- <FaGithub className="h-3.5 w-3.5" />
200
- GitHub
201
- </HeaderLink>
202
-
203
- <button
204
- onClick={() => setMenuOpen(!menuOpen)}
205
- className="inline-flex h-9 w-9 items-center justify-center rounded-lg border border-white/10 bg-white/[0.03] text-gray-300 transition hover:bg-white/[0.06] hover:text-white md:hidden"
206
- aria-label="Toggle menu"
207
- >
208
- {menuOpen ? <FaTimes size={16} /> : <FaBars size={16} />}
209
- </button>
210
- </div>
211
- </div>
212
-
213
- {menuOpen && (
214
- <div className="border-t border-white/10 bg-black/70 backdrop-blur-2xl md:hidden">
215
- <nav className="mx-auto grid max-w-7xl gap-1 px-4 py-3 sm:px-6">
216
- {navItems.map((item) => {
217
- const active = location.pathname === item.to
218
- return (
219
- <Link
220
- key={item.to}
221
- to={item.to}
222
- onClick={() => setMenuOpen(false)}
223
- className={`rounded-lg px-3 py-2 text-sm transition ${
224
- active
225
- ? 'bg-white text-black'
226
- : 'text-gray-400 hover:bg-white/[0.06] hover:text-white'
227
- }`}
228
- >
229
- {item.label}
230
- </Link>
231
- )
232
- })}
233
- <div className="mt-2 border-t border-white/10 pt-3">
234
- <p className="px-3 pb-2 text-xs font-semibold uppercase tracking-[0.18em] text-gray-500">
235
- Live Demos
236
- </p>
237
- <div className="grid gap-1">
238
- {demoItems.map((item) => {
239
- const active = location.pathname === item.to
240
- return (
241
- <Link
242
- key={item.to}
243
- to={item.to}
244
- onClick={() => setMenuOpen(false)}
245
- className={`rounded-lg px-3 py-2 text-sm transition ${
246
- active
247
- ? 'bg-white text-black'
248
- : 'text-gray-400 hover:bg-white/[0.06] hover:text-white'
249
- }`}
250
- >
251
- {item.label}
252
- </Link>
253
- )
254
- })}
255
- </div>
256
- </div>
257
- <div className="mt-2 grid gap-2 border-t border-white/10 pt-3 sm:grid-cols-3">
258
- <a href="https://live-docs.marcosbrendon.com/" target="_blank" rel="noopener noreferrer" className="rounded-lg border border-theme-active bg-theme-muted px-3 py-2 text-sm text-theme">
259
- Live Docs
260
- </a>
261
- <a href="/swagger" target="_blank" rel="noopener noreferrer" className="rounded-lg border border-white/10 bg-white/[0.03] px-3 py-2 text-sm text-gray-300">
262
- API Docs
263
- </a>
264
- <a href="https://github.com/MarcosBrendonDePaula/FluxStack" target="_blank" rel="noopener noreferrer" className="rounded-lg border border-white/10 bg-white/[0.03] px-3 py-2 text-sm text-gray-300">
265
- GitHub
266
- </a>
267
- </div>
268
- </nav>
269
- </div>
270
- )}
271
- </header>
272
-
273
- <main className="min-h-[calc(100vh-128px)]">
274
- <Outlet />
275
- </main>
276
-
277
- <footer className="border-t border-white/10 bg-black/20 py-5">
278
- <div className="mx-auto flex max-w-7xl flex-col items-center justify-between gap-2 px-4 text-center sm:flex-row sm:px-6 lg:px-8">
279
- <p className="text-xs text-gray-500">
280
- Built with <span style={{ color: theme.primary }}>FluxStack</span> - Bun + Elysia + React
281
- </p>
282
- <p className="text-xs text-gray-600">
283
- <span style={{ color: theme.primary }}>{theme.period}</span> palette
284
- </p>
285
- </div>
286
- </footer>
287
-
288
- {themeConfig.showPicker && <ThemePicker palette={theme} onOverride={setOverrideTheme} />}
289
- </div>
290
- )
291
- }
2
+ import { useState, useEffect } from 'react'
3
+ import type { ReactNode } from 'react'
4
+ import { Link, Outlet, useLocation } from 'react-router'
5
+ import { FaBars, FaBook, FaChevronDown, FaExternalLinkAlt, FaGithub, FaTimes } from 'react-icons/fa'
6
+ import FluxStackLogo from '@client/src/assets/fluxstack.svg'
7
+ import faviconSvg from '@client/src/assets/fluxstack-static.svg?raw'
8
+ import { useThemeClock } from '../hooks/useThemeClock'
9
+ import { ThemePicker } from './ThemePicker'
10
+ import type { ColorPalette } from '../lib/theme-clock'
11
+ import { themeConfig } from '../config/theme.config'
12
+
13
+ const navItems = [
14
+ { to: '/', label: 'Home' },
15
+ ]
16
+
17
+ const demoItems = [
18
+ { to: '/counter', label: 'Counter' },
19
+ { to: '/form', label: 'Form' },
20
+
21
+ { to: '/room-chat', label: 'Room Chat' },
22
+ { to: '/auth', label: 'Auth' },
23
+ { to: '/ping-pong', label: 'Ping Pong' },
24
+ ]
25
+
26
+ const allRouteItems = [...navItems, ...demoItems]
27
+
28
+ const MAX_FAVICON_CACHE = 20
29
+ const faviconUrlCache = new Map<string, string>()
30
+
31
+ function HeaderLink({
32
+ href,
33
+ children,
34
+ subtle = false,
35
+ }: {
36
+ href: string
37
+ children: ReactNode
38
+ subtle?: boolean
39
+ }) {
40
+ return (
41
+ <a
42
+ href={href}
43
+ target="_blank"
44
+ rel="noopener noreferrer"
45
+ className={`hidden h-9 items-center gap-2 rounded-lg border px-3 text-sm transition sm:inline-flex ${
46
+ subtle
47
+ ? 'border-white/10 bg-white/[0.025] text-gray-400 hover:border-white/20 hover:bg-white/[0.05] hover:text-white'
48
+ : 'border-theme-active bg-theme-muted text-theme hover:shadow-theme'
49
+ }`}
50
+ >
51
+ {children}
52
+ </a>
53
+ )
54
+ }
55
+
56
+ export function AppLayout() {
57
+ const location = useLocation()
58
+ const [menuOpen, setMenuOpen] = useState(false)
59
+ const [demosOpen, setDemosOpen] = useState(false)
60
+ const autoTheme = useThemeClock()
61
+ const [overrideTheme, setOverrideTheme] = useState<ColorPalette | null>(null)
62
+ const theme = overrideTheme || autoTheme
63
+
64
+ useEffect(() => {
65
+ setDemosOpen(false)
66
+ setMenuOpen(false)
67
+ }, [location.pathname])
68
+
69
+ useEffect(() => {
70
+ const current = allRouteItems.find(item => item.to === location.pathname)
71
+ document.title = current ? `${current.label} - FluxStack` : 'FluxStack'
72
+
73
+ const hue = `${Math.round(theme.baseHue - 270)}deg`
74
+ let url = faviconUrlCache.get(hue)
75
+ if (!url) {
76
+ if (faviconUrlCache.size >= MAX_FAVICON_CACHE) {
77
+ const oldestKey = faviconUrlCache.keys().next().value!
78
+ const oldestUrl = faviconUrlCache.get(oldestKey)!
79
+ URL.revokeObjectURL(oldestUrl)
80
+ faviconUrlCache.delete(oldestKey)
81
+ }
82
+ const colored = faviconSvg.replace(
83
+ '<svg ',
84
+ `<svg style="filter: hue-rotate(${hue})" `
85
+ )
86
+ const blob = new Blob([colored], { type: 'image/svg+xml' })
87
+ url = URL.createObjectURL(blob)
88
+ faviconUrlCache.set(hue, url)
89
+ }
90
+ let link = document.querySelector<HTMLLinkElement>('link[rel="icon"]')
91
+ if (!link) {
92
+ link = document.createElement('link')
93
+ link.rel = 'icon'
94
+ document.head.appendChild(link)
95
+ }
96
+ link.type = 'image/svg+xml'
97
+ link.href = url
98
+ }, [location.pathname, theme.baseHue])
99
+
100
+ const activeDemo = demoItems.some(item => item.to === location.pathname)
101
+
102
+ return (
103
+ <div
104
+ className="min-h-screen text-white"
105
+ style={{ backgroundColor: `oklch(7% 0.018 ${theme.baseHue})` }}
106
+ >
107
+ <header className="sticky top-0 z-50 border-b border-white/10 bg-black/45 backdrop-blur-2xl">
108
+ <div className="mx-auto flex h-16 max-w-7xl items-center justify-between gap-4 px-4 sm:px-6 lg:px-8">
109
+ <Link to="/" className="group flex min-w-0 items-center gap-3">
110
+ <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-white/10 bg-white/[0.04]">
111
+ <img
112
+ src={FluxStackLogo}
113
+ alt="FluxStack"
114
+ className="h-6 w-6 transition-[filter] duration-500"
115
+ style={{
116
+ filter: `hue-rotate(${theme.baseHue - 270}deg) drop-shadow(0 0 8px ${theme.primaryGlow})`,
117
+ }}
118
+ />
119
+ </span>
120
+ <span className="truncate text-sm font-semibold tracking-tight text-white">
121
+ FluxStack
122
+ </span>
123
+ </Link>
124
+
125
+ <nav className="hidden min-w-0 flex-1 items-center justify-center md:flex">
126
+ <div className="relative flex max-w-full items-center gap-1 rounded-lg border border-white/10 bg-white/[0.025] p-1">
127
+ {navItems.map((item) => {
128
+ const active = location.pathname === item.to
129
+ return (
130
+ <Link
131
+ key={item.to}
132
+ to={item.to}
133
+ className={`whitespace-nowrap rounded-md px-3 py-1.5 text-sm transition ${
134
+ active
135
+ ? 'bg-white text-black shadow-sm'
136
+ : 'text-gray-400 hover:bg-white/[0.06] hover:text-white'
137
+ }`}
138
+ >
139
+ {item.label}
140
+ </Link>
141
+ )
142
+ })}
143
+ <div className="relative">
144
+ <button
145
+ type="button"
146
+ onClick={() => setDemosOpen(open => !open)}
147
+ className={`inline-flex items-center gap-2 whitespace-nowrap rounded-md px-3 py-1.5 text-sm transition ${
148
+ activeDemo
149
+ ? 'bg-white text-black shadow-sm'
150
+ : 'text-gray-400 hover:bg-white/[0.06] hover:text-white'
151
+ }`}
152
+ aria-expanded={demosOpen}
153
+ >
154
+ Live Demos
155
+ <FaChevronDown className={`h-2.5 w-2.5 transition ${demosOpen ? 'rotate-180' : ''}`} />
156
+ </button>
157
+
158
+ {demosOpen && (
159
+ <div className="absolute left-1/2 top-11 z-50 w-72 -translate-x-1/2 rounded-lg border border-white/10 bg-[#07070b]/95 p-2 shadow-2xl shadow-black/40 backdrop-blur-2xl">
160
+ <div className="px-3 py-2">
161
+ <p className="text-xs font-semibold uppercase tracking-[0.18em] text-gray-500">Live Components</p>
162
+ <p className="mt-1 text-xs leading-5 text-gray-400">Demos de estado no servidor, salas e auth.</p>
163
+ </div>
164
+ <div className="mt-1 grid gap-1">
165
+ {demoItems.map((item) => {
166
+ const active = location.pathname === item.to
167
+ return (
168
+ <Link
169
+ key={item.to}
170
+ to={item.to}
171
+ className={`rounded-md px-3 py-2 text-sm transition ${
172
+ active
173
+ ? 'bg-white text-black'
174
+ : 'text-gray-300 hover:bg-white/[0.06] hover:text-white'
175
+ }`}
176
+ >
177
+ {item.label}
178
+ </Link>
179
+ )
180
+ })}
181
+ </div>
182
+ </div>
183
+ )}
184
+ </div>
185
+ </div>
186
+ </nav>
187
+
188
+ <div className="flex shrink-0 items-center gap-2">
189
+ <HeaderLink href="https://live-docs.marcosbrendon.com/">
190
+ <FaBook className="h-3.5 w-3.5" />
191
+ Live Docs
192
+ <FaExternalLinkAlt className="h-2.5 w-2.5 opacity-60" />
193
+ </HeaderLink>
194
+ <HeaderLink href="/swagger" subtle>
195
+ <FaBook className="h-3.5 w-3.5" />
196
+ API
197
+ </HeaderLink>
198
+ <HeaderLink href="https://github.com/MarcosBrendonDePaula/FluxStack" subtle>
199
+ <FaGithub className="h-3.5 w-3.5" />
200
+ GitHub
201
+ </HeaderLink>
202
+
203
+ <button
204
+ onClick={() => setMenuOpen(!menuOpen)}
205
+ className="inline-flex h-9 w-9 items-center justify-center rounded-lg border border-white/10 bg-white/[0.03] text-gray-300 transition hover:bg-white/[0.06] hover:text-white md:hidden"
206
+ aria-label="Toggle menu"
207
+ >
208
+ {menuOpen ? <FaTimes size={16} /> : <FaBars size={16} />}
209
+ </button>
210
+ </div>
211
+ </div>
212
+
213
+ {menuOpen && (
214
+ <div className="border-t border-white/10 bg-black/70 backdrop-blur-2xl md:hidden">
215
+ <nav className="mx-auto grid max-w-7xl gap-1 px-4 py-3 sm:px-6">
216
+ {navItems.map((item) => {
217
+ const active = location.pathname === item.to
218
+ return (
219
+ <Link
220
+ key={item.to}
221
+ to={item.to}
222
+ onClick={() => setMenuOpen(false)}
223
+ className={`rounded-lg px-3 py-2 text-sm transition ${
224
+ active
225
+ ? 'bg-white text-black'
226
+ : 'text-gray-400 hover:bg-white/[0.06] hover:text-white'
227
+ }`}
228
+ >
229
+ {item.label}
230
+ </Link>
231
+ )
232
+ })}
233
+ <div className="mt-2 border-t border-white/10 pt-3">
234
+ <p className="px-3 pb-2 text-xs font-semibold uppercase tracking-[0.18em] text-gray-500">
235
+ Live Demos
236
+ </p>
237
+ <div className="grid gap-1">
238
+ {demoItems.map((item) => {
239
+ const active = location.pathname === item.to
240
+ return (
241
+ <Link
242
+ key={item.to}
243
+ to={item.to}
244
+ onClick={() => setMenuOpen(false)}
245
+ className={`rounded-lg px-3 py-2 text-sm transition ${
246
+ active
247
+ ? 'bg-white text-black'
248
+ : 'text-gray-400 hover:bg-white/[0.06] hover:text-white'
249
+ }`}
250
+ >
251
+ {item.label}
252
+ </Link>
253
+ )
254
+ })}
255
+ </div>
256
+ </div>
257
+ <div className="mt-2 grid gap-2 border-t border-white/10 pt-3 sm:grid-cols-3">
258
+ <a href="https://live-docs.marcosbrendon.com/" target="_blank" rel="noopener noreferrer" className="rounded-lg border border-theme-active bg-theme-muted px-3 py-2 text-sm text-theme">
259
+ Live Docs
260
+ </a>
261
+ <a href="/swagger" target="_blank" rel="noopener noreferrer" className="rounded-lg border border-white/10 bg-white/[0.03] px-3 py-2 text-sm text-gray-300">
262
+ API Docs
263
+ </a>
264
+ <a href="https://github.com/MarcosBrendonDePaula/FluxStack" target="_blank" rel="noopener noreferrer" className="rounded-lg border border-white/10 bg-white/[0.03] px-3 py-2 text-sm text-gray-300">
265
+ GitHub
266
+ </a>
267
+ </div>
268
+ </nav>
269
+ </div>
270
+ )}
271
+ </header>
272
+
273
+ <main className="min-h-[calc(100vh-128px)]">
274
+ <Outlet />
275
+ </main>
276
+
277
+ <footer className="border-t border-white/10 bg-black/20 py-5">
278
+ <div className="mx-auto flex max-w-7xl flex-col items-center justify-between gap-2 px-4 text-center sm:flex-row sm:px-6 lg:px-8">
279
+ <p className="text-xs text-gray-500">
280
+ Built with <span style={{ color: theme.primary }}>FluxStack</span> - Bun + Elysia + React
281
+ </p>
282
+ <p className="text-xs text-gray-600">
283
+ <span style={{ color: theme.primary }}>{theme.period}</span> palette
284
+ </p>
285
+ </div>
286
+ </footer>
287
+
288
+ {themeConfig.showPicker && <ThemePicker palette={theme} onOverride={setOverrideTheme} />}
289
+ </div>
290
+ )
291
+ }
@@ -1,17 +1,17 @@
1
1
  'use client'
2
- import { useNavigate } from 'react-router'
3
- import { FaArrowLeft } from 'react-icons/fa6'
4
-
5
- export function BackButton() {
6
- const navigate = useNavigate()
7
-
8
- return (
9
- <button
10
- onClick={() => navigate(-1)}
11
- className="inline-flex items-center gap-2 rounded-lg border border-white/10 bg-white/[0.03] px-3 py-2 text-sm font-medium text-gray-300 transition hover:border-white/20 hover:bg-white/[0.06] hover:text-white"
12
- >
13
- <FaArrowLeft className="h-3 w-3" />
14
- Back
15
- </button>
16
- )
17
- }
2
+ import { useNavigate } from 'react-router'
3
+ import { FaArrowLeft } from 'react-icons/fa6'
4
+
5
+ export function BackButton() {
6
+ const navigate = useNavigate()
7
+
8
+ return (
9
+ <button
10
+ onClick={() => navigate(-1)}
11
+ className="inline-flex items-center gap-2 rounded-lg border border-white/10 bg-white/[0.03] px-3 py-2 text-sm font-medium text-gray-300 transition hover:border-white/20 hover:bg-white/[0.06] hover:text-white"
12
+ >
13
+ <FaArrowLeft className="h-3 w-3" />
14
+ Back
15
+ </button>
16
+ )
17
+ }