brustjs 0.1.28-alpha → 0.1.30-alpha
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/example/pokedex/app.css +8 -1712
- package/example/pokedex/components/AddToTeamButton.tsx +36 -19
- package/example/pokedex/components/AppLayout.tsx +48 -50
- package/example/pokedex/components/Breadcrumb.tsx +49 -0
- package/example/pokedex/components/DexFilter.tsx +121 -0
- package/example/pokedex/components/HeroSearch.tsx +51 -0
- package/example/pokedex/components/NavLink.tsx +16 -23
- package/example/pokedex/components/NavPreloader.tsx +7 -3
- package/example/pokedex/components/TeamBuilder.tsx +48 -131
- package/example/pokedex/components/ThemeToggle.tsx +22 -11
- package/example/pokedex/lib/loaders.ts +125 -115
- package/example/pokedex/lib/pokeapi.ts +21 -21
- package/example/pokedex/lib/team-store.ts +1 -1
- package/example/pokedex/lib/types.ts +73 -94
- package/example/pokedex/pages/BrowsePage.tsx +31 -0
- package/example/pokedex/pages/DetailPage.tsx +176 -91
- package/example/pokedex/pages/HomePage.tsx +229 -0
- package/example/pokedex/pages/TypeChart.tsx +46 -27
- package/example/pokedex/routes.tsx +9 -20
- package/example/pokedex/stores/team.ts +1 -1
- package/package.json +8 -7
- package/runtime/cli/native-routes-emit.ts +223 -63
- package/runtime/cli/templates.ts +7 -4
- package/runtime/index.js +52 -52
- package/runtime/native/runtime.ts +160 -16
- package/example/pokedex/pages/ListPage.tsx +0 -76
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// Native leaf route "/" — the landing page. Renders into AppLayout's <Outlet/>
|
|
2
|
+
// slot (chrome lives in AppLayout). SINGLE return, NO local bindings — a local
|
|
3
|
+
// `const` would soft-fall-back to an SSR component and lose the document shell.
|
|
4
|
+
//
|
|
5
|
+
// Everything dynamic is precomputed in homeLoader: `featured` cards, `typeTiles`
|
|
6
|
+
// (each carrying its own hex `color` for an inline style, since Tailwind's
|
|
7
|
+
// scanner can't see runtime-built type-color classes). The hero search box is
|
|
8
|
+
// the HeroSearch native directive (imperative navigate() dogfood).
|
|
9
|
+
import {
|
|
10
|
+
ArrowRight,
|
|
11
|
+
Box,
|
|
12
|
+
Component,
|
|
13
|
+
Cookie,
|
|
14
|
+
Database,
|
|
15
|
+
Layout,
|
|
16
|
+
List,
|
|
17
|
+
Navigation,
|
|
18
|
+
Server,
|
|
19
|
+
Table,
|
|
20
|
+
Zap,
|
|
21
|
+
} from 'lucide-react'
|
|
22
|
+
import HeroSearch from '../components/HeroSearch'
|
|
23
|
+
import type { HomeData } from '../lib/types'
|
|
24
|
+
|
|
25
|
+
export default function HomePage({ featured, typeTiles }: HomeData) {
|
|
26
|
+
return (
|
|
27
|
+
<div className="space-y-16 py-4">
|
|
28
|
+
<section className="relative overflow-hidden rounded-3xl bg-gradient-to-br from-brand-500 via-brand-600 to-indigo-700 px-6 py-16 text-center shadow-xl sm:px-12">
|
|
29
|
+
<span className="inline-flex items-center rounded-full bg-white/15 px-3 py-1 text-xs font-semibold uppercase tracking-wider text-white/90 ring-1 ring-white/20">
|
|
30
|
+
Built with brust
|
|
31
|
+
</span>
|
|
32
|
+
<h1 className="mx-auto mt-5 max-w-2xl text-4xl font-extrabold tracking-tight text-white sm:text-5xl">
|
|
33
|
+
Every Pokémon, one fast native-rendered Pokédex.
|
|
34
|
+
</h1>
|
|
35
|
+
<p className="mx-auto mt-4 max-w-xl text-base text-white/85 sm:text-lg">
|
|
36
|
+
Browse the National Dex, study the type chart, and build your dream team — server-rendered
|
|
37
|
+
in Rust, hydrated only where it counts.
|
|
38
|
+
</p>
|
|
39
|
+
<HeroSearch native />
|
|
40
|
+
<div className="mt-6 flex flex-wrap items-center justify-center gap-3">
|
|
41
|
+
<a
|
|
42
|
+
href="/pokedex"
|
|
43
|
+
className="inline-flex items-center gap-2 rounded-xl bg-white px-5 py-2.5 text-sm font-semibold text-brand-700 no-underline shadow-sm transition-transform hover:-translate-y-0.5"
|
|
44
|
+
>
|
|
45
|
+
Browse Pokédex
|
|
46
|
+
<ArrowRight size={16} isr={{ key: 'LcIconArrowRight' }} />
|
|
47
|
+
</a>
|
|
48
|
+
<a
|
|
49
|
+
href="/type-chart"
|
|
50
|
+
className="inline-flex items-center gap-2 rounded-xl bg-white/15 px-5 py-2.5 text-sm font-semibold text-white no-underline ring-1 ring-white/30 transition-colors hover:bg-white/25"
|
|
51
|
+
>
|
|
52
|
+
<Table size={16} isr={{ key: 'LcIconTable' }} />
|
|
53
|
+
Type chart
|
|
54
|
+
</a>
|
|
55
|
+
</div>
|
|
56
|
+
</section>
|
|
57
|
+
|
|
58
|
+
<section>
|
|
59
|
+
<div className="mb-5 flex items-end justify-between">
|
|
60
|
+
<h2 className="text-2xl font-extrabold tracking-tight text-slate-900 dark:text-white">
|
|
61
|
+
Featured
|
|
62
|
+
</h2>
|
|
63
|
+
<a
|
|
64
|
+
href="/pokedex"
|
|
65
|
+
className="inline-flex items-center gap-1 text-sm font-semibold text-brand-600 no-underline hover:underline dark:text-brand-50"
|
|
66
|
+
>
|
|
67
|
+
View all
|
|
68
|
+
<ArrowRight size={14} isr={{ key: 'LcIconArrowRight14' }} />
|
|
69
|
+
</a>
|
|
70
|
+
</div>
|
|
71
|
+
<div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
|
|
72
|
+
{featured.map((p) => (
|
|
73
|
+
<a
|
|
74
|
+
key={p.id}
|
|
75
|
+
href={p.detailHref}
|
|
76
|
+
className="group flex flex-col items-center rounded-2xl border border-slate-200 bg-white p-4 no-underline shadow-sm transition-all hover:-translate-y-0.5 hover:border-brand-500/50 hover:shadow-md dark:border-slate-800 dark:bg-slate-900"
|
|
77
|
+
>
|
|
78
|
+
<span className="self-start text-[11px] font-semibold tabular-nums text-slate-400">
|
|
79
|
+
{p.num}
|
|
80
|
+
</span>
|
|
81
|
+
<img
|
|
82
|
+
src={p.artwork}
|
|
83
|
+
alt={p.displayName}
|
|
84
|
+
loading="lazy"
|
|
85
|
+
className="h-28 w-28 object-contain transition-transform group-hover:scale-110"
|
|
86
|
+
/>
|
|
87
|
+
<div className="mt-1 text-sm font-semibold text-slate-800 dark:text-slate-100">
|
|
88
|
+
{p.displayName}
|
|
89
|
+
</div>
|
|
90
|
+
</a>
|
|
91
|
+
))}
|
|
92
|
+
</div>
|
|
93
|
+
</section>
|
|
94
|
+
|
|
95
|
+
<section>
|
|
96
|
+
<h2 className="mb-5 text-2xl font-extrabold tracking-tight text-slate-900 dark:text-white">
|
|
97
|
+
Browse by type
|
|
98
|
+
</h2>
|
|
99
|
+
<div className="grid grid-cols-3 gap-2.5 sm:grid-cols-6">
|
|
100
|
+
{typeTiles.map((t) => (
|
|
101
|
+
<a
|
|
102
|
+
key={t.name}
|
|
103
|
+
href={t.href}
|
|
104
|
+
style={{ background: t.color }}
|
|
105
|
+
className="flex items-center justify-center rounded-xl px-3 py-2.5 text-sm font-semibold text-white no-underline shadow-sm transition-transform hover:-translate-y-0.5"
|
|
106
|
+
>
|
|
107
|
+
{t.label}
|
|
108
|
+
</a>
|
|
109
|
+
))}
|
|
110
|
+
</div>
|
|
111
|
+
</section>
|
|
112
|
+
|
|
113
|
+
<section className="overflow-hidden rounded-3xl border border-slate-200 bg-white p-8 shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
114
|
+
<div className="flex flex-col items-start gap-4 sm:flex-row sm:items-center sm:justify-between">
|
|
115
|
+
<div>
|
|
116
|
+
<h2 className="text-2xl font-extrabold tracking-tight text-slate-900 dark:text-white">
|
|
117
|
+
Build your team
|
|
118
|
+
</h2>
|
|
119
|
+
<p className="mt-2 max-w-md text-slate-500 dark:text-slate-400">
|
|
120
|
+
Add up to six Pokémon from any detail page. Your team lives in the floating dock and
|
|
121
|
+
follows you across the whole site.
|
|
122
|
+
</p>
|
|
123
|
+
</div>
|
|
124
|
+
<a
|
|
125
|
+
href="/pokedex"
|
|
126
|
+
className="inline-flex shrink-0 items-center gap-2 rounded-xl bg-brand-500 px-5 py-2.5 text-sm font-semibold text-white no-underline shadow-sm transition-colors hover:bg-brand-600"
|
|
127
|
+
>
|
|
128
|
+
Start picking
|
|
129
|
+
<ArrowRight size={16} isr={{ key: 'LcIconArrowRight' }} />
|
|
130
|
+
</a>
|
|
131
|
+
</div>
|
|
132
|
+
</section>
|
|
133
|
+
|
|
134
|
+
<section>
|
|
135
|
+
<h2 className="mb-1 text-2xl font-extrabold tracking-tight text-slate-900 dark:text-white">
|
|
136
|
+
Built with brust
|
|
137
|
+
</h2>
|
|
138
|
+
<p className="mb-6 max-w-2xl text-slate-500 dark:text-slate-400">
|
|
139
|
+
This whole site is one brust app. Every box below is a real framework feature dogfooded
|
|
140
|
+
right here.
|
|
141
|
+
</p>
|
|
142
|
+
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
|
143
|
+
<div className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
144
|
+
<div className="flex items-center gap-2 text-sm font-bold text-slate-900 dark:text-white">
|
|
145
|
+
<Server size={16} className="text-brand-500" isr={{ key: 'LcIconServer' }} />
|
|
146
|
+
Native SSR routes
|
|
147
|
+
</div>
|
|
148
|
+
<div className="mt-1 text-sm text-slate-500 dark:text-slate-400">
|
|
149
|
+
Every page is JSX compiled to minijinja and rendered in Rust.
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
<div className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
153
|
+
<div className="flex items-center gap-2 text-sm font-bold text-slate-900 dark:text-white">
|
|
154
|
+
<Database size={16} className="text-brand-500" isr={{ key: 'LcIconDatabase' }} />
|
|
155
|
+
Loaders + ISR
|
|
156
|
+
</div>
|
|
157
|
+
<div className="mt-1 text-sm text-slate-500 dark:text-slate-400">
|
|
158
|
+
Loaders fetch PokeAPI and precompute the view-model; cached responses stay fast.
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
<div className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
162
|
+
<div className="flex items-center gap-2 text-sm font-bold text-slate-900 dark:text-white">
|
|
163
|
+
<Cookie size={16} className="text-brand-500" isr={{ key: 'LcIconCookie' }} />
|
|
164
|
+
Cookies / request-context
|
|
165
|
+
</div>
|
|
166
|
+
<div className="mt-1 text-sm text-slate-500 dark:text-slate-400">
|
|
167
|
+
The theme toggle reads and writes the <code>mode</code> cookie per request.
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
<div className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
171
|
+
<div className="flex items-center gap-2 text-sm font-bold text-slate-900 dark:text-white">
|
|
172
|
+
<Box size={16} className="text-brand-500" isr={{ key: 'LcIconBox' }} />
|
|
173
|
+
defineStore
|
|
174
|
+
</div>
|
|
175
|
+
<div className="mt-1 text-sm text-slate-500 dark:text-slate-400">
|
|
176
|
+
The team dock is a window-singleton store shared across islands.
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
<div className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
180
|
+
<div className="flex items-center gap-2 text-sm font-bold text-slate-900 dark:text-white">
|
|
181
|
+
<Navigation size={16} className="text-brand-500" isr={{ key: 'LcIconNavigation' }} />
|
|
182
|
+
Client navigation
|
|
183
|
+
</div>
|
|
184
|
+
<div className="mt-1 text-sm text-slate-500 dark:text-slate-400">
|
|
185
|
+
NavLink active state, NavPreloader prefetch, and imperative navigate() in the hero
|
|
186
|
+
search.
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
<div className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
190
|
+
<div className="flex items-center gap-2 text-sm font-bold text-slate-900 dark:text-white">
|
|
191
|
+
<Component size={16} className="text-brand-500" isr={{ key: 'LcIconComponent' }} />
|
|
192
|
+
React islands
|
|
193
|
+
</div>
|
|
194
|
+
<div className="mt-1 text-sm text-slate-500 dark:text-slate-400">
|
|
195
|
+
The team dock hydrates as a React island while the page stays native.
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
<div className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
199
|
+
<div className="flex items-center gap-2 text-sm font-bold text-slate-900 dark:text-white">
|
|
200
|
+
<List size={16} className="text-brand-500" isr={{ key: 'LcIconList' }} />
|
|
201
|
+
Keyed x-for
|
|
202
|
+
</div>
|
|
203
|
+
<div className="mt-1 text-sm text-slate-500 dark:text-slate-400">
|
|
204
|
+
The browse grid filters and sorts live with a keyed, react-free reconcile.
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
<div className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
208
|
+
<div className="flex items-center gap-2 text-sm font-bold text-slate-900 dark:text-white">
|
|
209
|
+
<Layout size={16} className="text-brand-500" isr={{ key: 'LcIconLayout' }} />
|
|
210
|
+
Nested Outlet layout
|
|
211
|
+
</div>
|
|
212
|
+
<div className="mt-1 text-sm text-slate-500 dark:text-slate-400">
|
|
213
|
+
The navbar, breadcrumb, and footer are written once and nested over every route.
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
<div className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
217
|
+
<div className="flex items-center gap-2 text-sm font-bold text-slate-900 dark:text-white">
|
|
218
|
+
<Zap size={16} className="text-brand-500" isr={{ key: 'LcIconZap' }} />
|
|
219
|
+
Treaty actions
|
|
220
|
+
</div>
|
|
221
|
+
<div className="mt-1 text-sm text-slate-500 dark:text-slate-400">
|
|
222
|
+
Add-to-team and theme persistence call type-safe server actions over treaty.
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
</section>
|
|
227
|
+
</div>
|
|
228
|
+
)
|
|
229
|
+
}
|
|
@@ -1,42 +1,61 @@
|
|
|
1
|
-
//
|
|
2
|
-
// slot (chrome lives in AppLayout).
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// r.cells.map(c => …)) into a
|
|
1
|
+
// Native leaf route "/type-chart" — the 18×18 type-effectiveness matrix.
|
|
2
|
+
// Renders into AppLayout's <Outlet/> slot (chrome lives in AppLayout). SINGLE
|
|
3
|
+
// return, NO local bindings. Pure read-only data → the ideal native page
|
|
4
|
+
// (compiled to jinja, rendered in Rust, zero React on the server).
|
|
5
|
+
//
|
|
6
|
+
// The 19×19 grid uses nested `.map()`: rows.map(r => r.cells.map(c => …)) into a
|
|
7
|
+
// CSS grid. Each row wrapper is `display:contents` (Tailwind `contents`) so
|
|
8
|
+
// every cell is a direct grid item — the row div leaves no box. Cell coloring
|
|
9
|
+
// is a static Tailwind utility class string the loader picks per effectiveness
|
|
10
|
+
// (literals in a .ts file, so `@source` scans them). The header row + row heads
|
|
11
|
+
// are `sticky` (also set in the loader class strings).
|
|
7
12
|
import type { TypeChartData } from '../lib/types'
|
|
8
13
|
|
|
9
14
|
export default function TypeChart({ rows }: TypeChartData) {
|
|
10
15
|
return (
|
|
11
|
-
|
|
12
|
-
<div className="
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
</
|
|
16
|
+
<section className="py-2">
|
|
17
|
+
<div className="mb-5">
|
|
18
|
+
<h1 className="text-3xl font-extrabold tracking-tight text-slate-900 dark:text-white">
|
|
19
|
+
Type chart
|
|
20
|
+
</h1>
|
|
21
|
+
<p className="mt-2 max-w-2xl text-slate-500 dark:text-slate-400">
|
|
22
|
+
Damage relations — rows attack, columns defend. Rendered server-side in Rust from jinja
|
|
23
|
+
(native:true · no React runtime in the payload).
|
|
24
|
+
</p>
|
|
20
25
|
</div>
|
|
21
26
|
|
|
22
|
-
<div className="
|
|
23
|
-
<span className="
|
|
24
|
-
<span className="
|
|
27
|
+
<div className="mb-4 flex flex-wrap items-center gap-4 text-sm text-slate-500 dark:text-slate-400">
|
|
28
|
+
<span className="inline-flex items-center gap-1.5">
|
|
29
|
+
<span className="flex h-5 w-5 items-center justify-center rounded bg-green-500/25 text-xs font-bold text-green-700 dark:text-green-300">
|
|
30
|
+
2
|
|
31
|
+
</span>
|
|
32
|
+
super effective
|
|
25
33
|
</span>
|
|
26
|
-
<span className="
|
|
27
|
-
<span className="
|
|
34
|
+
<span className="inline-flex items-center gap-1.5">
|
|
35
|
+
<span className="flex h-5 w-5 items-center justify-center rounded bg-red-500/20 text-xs font-bold text-red-700 dark:text-red-300">
|
|
36
|
+
½
|
|
37
|
+
</span>
|
|
38
|
+
not very effective
|
|
28
39
|
</span>
|
|
29
|
-
<span className="
|
|
30
|
-
<span className="
|
|
40
|
+
<span className="inline-flex items-center gap-1.5">
|
|
41
|
+
<span className="flex h-5 w-5 items-center justify-center rounded bg-slate-800/80 text-xs font-bold text-slate-200">
|
|
42
|
+
0
|
|
43
|
+
</span>
|
|
44
|
+
no effect
|
|
31
45
|
</span>
|
|
32
46
|
</div>
|
|
33
47
|
|
|
34
|
-
<div className="
|
|
35
|
-
<div className="
|
|
48
|
+
<div className="overflow-x-auto rounded-2xl border border-slate-200 bg-white p-2 shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
49
|
+
<div className="grid w-max grid-cols-[2.75rem_repeat(18,2.25rem)] gap-px">
|
|
36
50
|
{rows.map((r) => (
|
|
37
|
-
<div key={r.id} className="
|
|
51
|
+
<div key={r.id} className="contents">
|
|
38
52
|
{r.cells.map((c) => (
|
|
39
|
-
<div
|
|
53
|
+
<div
|
|
54
|
+
key={c.id}
|
|
55
|
+
className={c.className}
|
|
56
|
+
title={c.title}
|
|
57
|
+
style={{ background: c.bg }}
|
|
58
|
+
>
|
|
40
59
|
{c.content}
|
|
41
60
|
</div>
|
|
42
61
|
))}
|
|
@@ -44,6 +63,6 @@ export default function TypeChart({ rows }: TypeChartData) {
|
|
|
44
63
|
))}
|
|
45
64
|
</div>
|
|
46
65
|
</div>
|
|
47
|
-
|
|
66
|
+
</section>
|
|
48
67
|
)
|
|
49
68
|
}
|
|
@@ -1,35 +1,24 @@
|
|
|
1
1
|
import { defineRoutes } from 'brustjs/routes'
|
|
2
2
|
import AppLayout from './components/AppLayout'
|
|
3
|
-
import { detailLoader,
|
|
3
|
+
import { browseLoader, detailLoader, homeLoader, typeChartLoader } from './lib/loaders'
|
|
4
|
+
import HomePage from './pages/HomePage'
|
|
5
|
+
import BrowsePage from './pages/BrowsePage'
|
|
4
6
|
import DetailPage from './pages/DetailPage'
|
|
5
|
-
import ListPage from './pages/ListPage'
|
|
6
7
|
import TypeChart from './pages/TypeChart'
|
|
7
8
|
|
|
8
9
|
// Every route is `native: true` — each page is compiled from JSX to a minijinja
|
|
9
|
-
// template at build time and rendered in Rust
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// ROUTER-LEVEL LAYOUT (Approach a): the chrome (sidebar / topbar / team-dock) is
|
|
15
|
-
// written ONCE in AppLayout, nested as the parent of the three leaf routes. Each
|
|
16
|
-
// leaf renders into AppLayout's <Outlet/> slot. The compiler builds the synth
|
|
17
|
-
// wrapper `<AppLayout native><Leaf native/></AppLayout>` per leaf and runs the
|
|
18
|
-
// chain loaders top-down, shallow-merging into one flat jinja context. The leaf
|
|
19
|
-
// loaders therefore also return the chrome fields (title/active/crumb/teamProps)
|
|
20
|
-
// that AppLayout reads — see lib/loaders.ts and components/AppLayout.tsx.
|
|
10
|
+
// template at build time and rendered in Rust. The loader runs in a Bun worker
|
|
11
|
+
// and its return value becomes the template scope. The chrome (navbar / footer /
|
|
12
|
+
// team-dock) is written ONCE in AppLayout, nested as the parent of the four leaf
|
|
13
|
+
// routes; each leaf renders into AppLayout's <Outlet/> slot.
|
|
21
14
|
export const routes = defineRoutes([
|
|
22
15
|
{
|
|
23
16
|
Component: AppLayout,
|
|
24
17
|
native: true,
|
|
25
18
|
children: [
|
|
26
|
-
|
|
27
|
-
{ path: '/', Component:
|
|
28
|
-
|
|
29
|
-
// Dynamic param {name} + a (non-streamed) evolution chain loaded in the loader.
|
|
19
|
+
{ path: '/', Component: HomePage, native: true, loader: homeLoader },
|
|
20
|
+
{ path: '/pokedex', Component: BrowsePage, native: true, loader: browseLoader },
|
|
30
21
|
{ path: '/pokemon/{name}', Component: DetailPage, native: true, loader: detailLoader },
|
|
31
|
-
|
|
32
|
-
// Static 18×18 effectiveness matrix — the ideal native page.
|
|
33
22
|
{ path: '/type-chart', Component: TypeChart, native: true, loader: typeChartLoader },
|
|
34
23
|
],
|
|
35
24
|
},
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// module-scope store imported by both would be duplicated into two instances
|
|
5
5
|
// that never sync. `defineStore` resolves both bundles to ONE instance on
|
|
6
6
|
// `window.__BRUST_STORES__['pokedex.team']`, so a write in one island is seen by
|
|
7
|
-
// the other — no hand-rolled CustomEvent bus.
|
|
7
|
+
// the other — no hand-rolled CustomEvent bus.
|
|
8
8
|
import { defineStore, signal } from 'brustjs/store'
|
|
9
9
|
import type { TeamMember } from '../lib/types'
|
|
10
10
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brustjs",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30-alpha",
|
|
4
4
|
"description": "Bun + Rust SSR framework — React on the server, Rust everywhere else (napi cdylib + per-worker SharedArrayBuffer).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -40,12 +40,12 @@
|
|
|
40
40
|
"typescript": "^6.0.3"
|
|
41
41
|
},
|
|
42
42
|
"optionalDependencies": {
|
|
43
|
-
"brustjs-darwin-x64": "0.1.
|
|
44
|
-
"brustjs-darwin-arm64": "0.1.
|
|
45
|
-
"brustjs-linux-x64-gnu": "0.1.
|
|
46
|
-
"brustjs-linux-arm64-gnu": "0.1.
|
|
47
|
-
"brustjs-linux-x64-musl": "0.1.
|
|
48
|
-
"brustjs-linux-arm64-musl": "0.1.
|
|
43
|
+
"brustjs-darwin-x64": "0.1.30-alpha",
|
|
44
|
+
"brustjs-darwin-arm64": "0.1.30-alpha",
|
|
45
|
+
"brustjs-linux-x64-gnu": "0.1.30-alpha",
|
|
46
|
+
"brustjs-linux-arm64-gnu": "0.1.30-alpha",
|
|
47
|
+
"brustjs-linux-x64-musl": "0.1.30-alpha",
|
|
48
|
+
"brustjs-linux-arm64-musl": "0.1.30-alpha"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"react": "^19.2.6",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"@types/react": "^19.2.15",
|
|
58
58
|
"@types/react-dom": "^19.2.3",
|
|
59
59
|
"happy-dom": "^20.9.0",
|
|
60
|
+
"lucide-react": "^1.17.0",
|
|
60
61
|
"react": "^19.2.6",
|
|
61
62
|
"react-dom": "^19.2.6",
|
|
62
63
|
"zod": "^4.4.3"
|