fse-theme-default 0.3.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.
Files changed (47) hide show
  1. package/README.md +55 -0
  2. package/astro.config.mjs +22 -0
  3. package/package.json +38 -0
  4. package/public/favicon.svg +4 -0
  5. package/src/assets/logo.svg +5 -0
  6. package/src/components/Card.astro +31 -0
  7. package/src/components/Head.astro +9 -0
  8. package/src/components/Header.astro +55 -0
  9. package/src/components/Input.astro +56 -0
  10. package/src/components/Logo.astro +17 -0
  11. package/src/components/Modal.astro +78 -0
  12. package/src/components/NavLink.astro +23 -0
  13. package/src/components/Pagination.astro +81 -0
  14. package/src/components/SearchBar.astro +65 -0
  15. package/src/components/Select.astro +60 -0
  16. package/src/components/Sidebar.astro +207 -0
  17. package/src/components/SidebarLinks.astro +10 -0
  18. package/src/components/Table.astro +157 -0
  19. package/src/components/TableFilters.astro +137 -0
  20. package/src/env.d.ts +2 -0
  21. package/src/layouts/EmailLayout.astro +45 -0
  22. package/src/layouts/Layout.astro +95 -0
  23. package/src/pages/emails/password-reset.astro +39 -0
  24. package/src/pages/emails/verify-email-change.astro +39 -0
  25. package/src/pages/emails/verify.astro +39 -0
  26. package/src/pages/error.astro +26 -0
  27. package/src/pages/forgot-password.astro +67 -0
  28. package/src/pages/fse/form.astro +169 -0
  29. package/src/pages/fse/links.astro +104 -0
  30. package/src/pages/fse/list.astro +166 -0
  31. package/src/pages/fse/public-detail.astro +29 -0
  32. package/src/pages/fse/public-list.astro +54 -0
  33. package/src/pages/index.astro +41 -0
  34. package/src/pages/login.astro +88 -0
  35. package/src/pages/public/error.astro +29 -0
  36. package/src/pages/register-success.astro +31 -0
  37. package/src/pages/register.astro +99 -0
  38. package/src/pages/reset-password.astro +117 -0
  39. package/src/pages/settings.astro +188 -0
  40. package/src/pages/user.astro +65 -0
  41. package/src/pages/users.astro +52 -0
  42. package/src/styles/global.css +11 -0
  43. package/src/styles/theme.css +408 -0
  44. package/src/types/pages.ts +58 -0
  45. package/src/types.ts +154 -0
  46. package/theme.json +5 -0
  47. package/tsconfig.json +5 -0
@@ -0,0 +1,408 @@
1
+ /*
2
+ The theme's design layer: tokens, utilities and dark mode. Everything but
3
+ the `@import "tailwindcss"` itself, so a child theme's own Tailwind root
4
+ can import it and override tokens afterwards. `@source` makes Tailwind scan
5
+ this theme's sources wherever it is imported from.
6
+ */
7
+ @source "../";
8
+
9
+ @custom-variant dark (&:is(.dark *));
10
+
11
+ @theme {
12
+ --font-sans:
13
+ ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial,
14
+ sans-serif;
15
+
16
+ --color-primary-50: #f4f9ee;
17
+ --color-primary-100: #e6f2d6;
18
+ --color-primary-200: #cce5ad;
19
+ --color-primary-300: #aad27e;
20
+ --color-primary-400: #8bc358;
21
+ --color-primary-500: #92c355;
22
+ --color-primary-600: #6a9b3c;
23
+ --color-primary-700: #527830;
24
+ --color-primary-800: #3b5622;
25
+ --color-primary-900: #263715;
26
+ --color-primary-950: #17220c;
27
+
28
+ --color-secondary-50: #f8f5f4;
29
+ --color-secondary-100: #f1ece8;
30
+ --color-secondary-200: #e2ddd8;
31
+ --color-secondary-300: #cfc8c1;
32
+ --color-secondary-400: #a8a29a;
33
+ --color-secondary-500: #6b6b67;
34
+ --color-secondary-600: #57564f;
35
+ --color-secondary-700: #44433d;
36
+ --color-secondary-800: #33332e;
37
+ --color-secondary-900: #282925;
38
+ --color-secondary-950: #1c1c19;
39
+ }
40
+
41
+ /*
42
+ The default border color has changed to `currentcolor` in Tailwind CSS v4,
43
+ so we've added these compatibility styles to make sure everything still
44
+ looks the same as it did with Tailwind CSS v3.
45
+
46
+ If we ever want to remove these styles, we need to add an explicit border
47
+ color utility to any element that depends on these defaults.
48
+ */
49
+ @layer base {
50
+ *,
51
+ ::after,
52
+ ::before,
53
+ ::backdrop,
54
+ ::file-selector-button {
55
+ border-color: var(--color-gray-200, currentcolor);
56
+ }
57
+ }
58
+
59
+ @utility section-title {
60
+ @apply font-extrabold uppercase;
61
+ font-size: clamp(26px, 3.5vw, 30px);
62
+ line-height: 1.15;
63
+ letter-spacing: -0.01em;
64
+ }
65
+
66
+ @utility btn-primary {
67
+ @apply px-4 py-2 inline-flex items-center gap-2 justify-center text-sm font-medium text-white bg-primary-600 rounded-lg hover:bg-primary-500 active:bg-primary-700 transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2;
68
+ }
69
+
70
+ @utility btn-secondary {
71
+ @apply px-4 py-2 inline-flex items-center gap-2 justify-center text-sm font-medium text-secondary-700 bg-secondary-100 border border-secondary-200 rounded-lg hover:bg-secondary-200 transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-secondary-300 focus-visible:ring-offset-2;
72
+ }
73
+
74
+ @utility btn-ghost {
75
+ @apply px-4 py-2 inline-flex items-center gap-2 justify-center text-sm font-medium text-secondary-600 rounded-lg hover:bg-secondary-100 hover:text-secondary-900 transition-colors focus-visible:outline-hidden;
76
+ }
77
+
78
+ @utility btn-danger {
79
+ @apply px-4 py-2 inline-flex items-center gap-2 justify-center text-sm font-medium text-red-600 bg-red-50 border border-red-200 rounded-lg hover:bg-red-100 transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-red-400 focus-visible:ring-offset-2;
80
+ }
81
+
82
+ @utility sidebar-closed {
83
+ /* Mobile: fully hide sidebar off-screen */
84
+ & aside {
85
+ @apply -translate-x-full;
86
+ }
87
+ }
88
+
89
+ @layer base {
90
+ /* Light mode tokens — swap these in .dark for dark mode */
91
+ :root {
92
+ --color-bg: 248 245 244; /* secondary-50 (offwhite) */
93
+ --color-surface: 255 255 255; /* white */
94
+ --color-border: 226 221 216; /* secondary-200 (warm border) */
95
+ --color-fg: 40 41 37; /* secondary-900 (charcoal) */
96
+ --color-fg-muted: 107 107 103; /* secondary-500 */
97
+ --color-primary: 111 160 58; /* green-dark */
98
+ }
99
+
100
+ .dark {
101
+ --color-bg: 28 28 25; /* secondary-950 */
102
+ --color-surface: 40 41 37; /* secondary-900 (charcoal) */
103
+ --color-border: 59 59 53; /* raised border */
104
+ --color-fg: 248 245 244; /* secondary-50 (offwhite) */
105
+ --color-fg-muted: 168 168 162; /* secondary-400 */
106
+ --color-primary: 146 195 85; /* primary-500 */
107
+ }
108
+
109
+ body {
110
+ @apply bg-secondary-50 text-secondary-900 antialiased;
111
+ }
112
+
113
+ button:not(:disabled),
114
+ [role="button"]:not(:disabled) {
115
+ cursor: pointer;
116
+ }
117
+ }
118
+
119
+ @layer utilities {
120
+ .dark {
121
+ color-scheme: dark;
122
+ /* Match the focus-ring offset gap to the dark page instead of white. */
123
+ --tw-ring-offset-color: #1c1c19;
124
+ }
125
+
126
+ .dark body {
127
+ background-color: #1c1c19;
128
+ color: #f8f5f4;
129
+ }
130
+
131
+ .dark .btn-primary {
132
+ background-color: #92c355;
133
+ color: #10210a;
134
+ }
135
+ .dark .btn-primary:hover {
136
+ background-color: #a7d176;
137
+ }
138
+ .dark .btn-primary:active {
139
+ background-color: #7fb544;
140
+ }
141
+
142
+ .dark .btn-secondary {
143
+ background-color: #33342f;
144
+ border-color: #44443d;
145
+ color: #ece7e2;
146
+ }
147
+ .dark .btn-secondary:hover {
148
+ background-color: #44443d;
149
+ }
150
+
151
+ .dark .btn-ghost {
152
+ color: #d4cfc8;
153
+ }
154
+ .dark .btn-ghost:hover {
155
+ background-color: #33342f;
156
+ color: #f8f5f4;
157
+ }
158
+
159
+ .dark .btn-danger {
160
+ background-color: #2a1416;
161
+ border-color: #7f1d1d;
162
+ color: #fca5a5;
163
+ }
164
+ .dark .btn-danger:hover {
165
+ background-color: #3a1a1c;
166
+ }
167
+
168
+ /* ---- Backgrounds ---- */
169
+ .dark .bg-white {
170
+ background-color: #282925;
171
+ }
172
+ .dark .bg-secondary-50 {
173
+ background-color: #1c1c19;
174
+ }
175
+ .dark .bg-secondary-100 {
176
+ background-color: #33342f;
177
+ }
178
+ .dark .bg-secondary-200 {
179
+ background-color: #44443d;
180
+ }
181
+ .dark .hover\:bg-secondary-100:hover {
182
+ background-color: #33342f;
183
+ }
184
+ .dark .hover\:bg-secondary-50:hover {
185
+ background-color: #33342f;
186
+ }
187
+ .dark .disabled\:bg-secondary-50:disabled {
188
+ background-color: #232320;
189
+ }
190
+
191
+ .dark .bg-primary-50 {
192
+ background-color: #1b2913;
193
+ }
194
+ .dark .bg-primary-100 {
195
+ background-color: #25360f;
196
+ }
197
+ .dark .aria-\[current\=page\]\:bg-primary-50[aria-current="page"] {
198
+ background-color: #1b2913;
199
+ }
200
+
201
+ .dark .bg-red-50 {
202
+ background-color: #2a1416;
203
+ }
204
+ .dark .hover\:bg-red-50:hover {
205
+ background-color: #2a1416;
206
+ }
207
+ .dark .bg-green-50 {
208
+ background-color: #14271a;
209
+ }
210
+ .dark .bg-amber-50 {
211
+ background-color: #2a2213;
212
+ }
213
+ .dark .bg-pink-50 {
214
+ background-color: #2a1420;
215
+ }
216
+
217
+ /* ---- Text ---- */
218
+ .dark .text-secondary-900,
219
+ .dark .text-black {
220
+ color: #f8f5f4;
221
+ }
222
+ .dark .text-secondary-800 {
223
+ color: #ece7e2;
224
+ }
225
+ .dark .text-secondary-700 {
226
+ color: #d4cfc8;
227
+ }
228
+ .dark .text-secondary-600 {
229
+ color: #d4cfc8;
230
+ }
231
+ .dark .text-secondary-500 {
232
+ color: #b8b3ab;
233
+ }
234
+ .dark .text-secondary-400 {
235
+ color: #a8a29a;
236
+ }
237
+ .dark .text-secondary-300 {
238
+ color: #57564f;
239
+ }
240
+ .dark .text-secondary-200 {
241
+ color: #44443d;
242
+ }
243
+ .dark .hover\:text-secondary-900:hover {
244
+ color: #f8f5f4;
245
+ }
246
+ .dark .hover\:text-secondary-800:hover {
247
+ color: #ece7e2;
248
+ }
249
+ .dark .hover\:text-secondary-700:hover {
250
+ color: #d4cfc8;
251
+ }
252
+ .dark .peer-disabled\:text-secondary-400:disabled {
253
+ color: #57564f;
254
+ }
255
+
256
+ .dark .text-primary-900,
257
+ .dark .text-primary-800,
258
+ .dark .text-primary-700 {
259
+ color: #aad27e;
260
+ }
261
+ .dark .text-primary-600 {
262
+ color: #8bc358;
263
+ }
264
+ .dark .hover\:text-primary-700:hover {
265
+ color: #aad27e;
266
+ }
267
+ .dark .hover\:text-primary-600:hover {
268
+ color: #8bc358;
269
+ }
270
+ .dark .aria-\[current\=page\]\:text-primary-800[aria-current="page"] {
271
+ color: #aad27e;
272
+ }
273
+
274
+ .dark .text-red-700,
275
+ .dark .text-red-600 {
276
+ color: #fca5a5;
277
+ }
278
+ .dark .text-red-500 {
279
+ color: #f87171;
280
+ }
281
+ .dark .hover\:text-red-600:hover {
282
+ color: #fca5a5;
283
+ }
284
+ .dark .text-green-800,
285
+ .dark .text-green-700,
286
+ .dark .text-green-600 {
287
+ color: #86efac;
288
+ }
289
+ .dark .text-amber-900,
290
+ .dark .text-amber-800,
291
+ .dark .text-amber-700 {
292
+ color: #fcd34d;
293
+ }
294
+ .dark .text-amber-500 {
295
+ color: #fbbf24;
296
+ }
297
+ .dark .text-pink-500 {
298
+ color: #f9a8d4;
299
+ }
300
+
301
+ /* ---- Borders ---- */
302
+ .dark .border-secondary-100 {
303
+ border-color: #33342f;
304
+ }
305
+ .dark .border-secondary-200 {
306
+ border-color: #3b3b35;
307
+ }
308
+ .dark .border-secondary-300 {
309
+ border-color: #44443d;
310
+ }
311
+ .dark .border-black {
312
+ border-color: #3b3b35;
313
+ }
314
+ .dark .hover\:border-secondary-300:hover {
315
+ border-color: #57564f;
316
+ }
317
+ .dark .disabled\:border-secondary-200:disabled {
318
+ border-color: #3b3b35;
319
+ }
320
+ .dark .divide-secondary-100 > :not(:last-child) {
321
+ border-color: #33342f;
322
+ }
323
+ .dark .divide-secondary-200 > :not(:last-child) {
324
+ border-color: #3b3b35;
325
+ }
326
+
327
+ .dark .border-primary-100 {
328
+ border-color: #25360f;
329
+ }
330
+ .dark .border-primary-200 {
331
+ border-color: #3b5622;
332
+ }
333
+ .dark .border-primary-300 {
334
+ border-color: #527830;
335
+ }
336
+ .dark .hover\:border-primary-200:hover {
337
+ border-color: #3b5622;
338
+ }
339
+
340
+ .dark .border-red-200 {
341
+ border-color: #7f1d1d;
342
+ }
343
+ .dark .border-green-100,
344
+ .dark .border-green-200 {
345
+ border-color: #166534;
346
+ }
347
+ .dark .border-amber-200 {
348
+ border-color: #78350f;
349
+ }
350
+ .dark .border-pink-100 {
351
+ border-color: #831843;
352
+ }
353
+
354
+ /* ---- Form fields ---- */
355
+ .dark input,
356
+ .dark select,
357
+ .dark textarea {
358
+ color: #f4f4f5;
359
+ }
360
+ /* Floating-label mask sits over the input border; match the surface colour. */
361
+ .dark .before\:bg-white::before {
362
+ background-color: #282925;
363
+ }
364
+ /* Override the browser's white autofill background. */
365
+ .dark input:-webkit-autofill,
366
+ .dark input:-webkit-autofill:hover,
367
+ .dark input:-webkit-autofill:focus {
368
+ -webkit-box-shadow: 0 0 0 1000px #282925 inset;
369
+ -webkit-text-fill-color: #f8f5f4;
370
+ caret-color: #f8f5f4;
371
+ }
372
+ }
373
+
374
+ html.sidebar-mini #nav-menu-1 {
375
+ width: 4rem;
376
+ }
377
+ html.sidebar-mini #nav-menu-1 .sidebar-full-logo {
378
+ display: none;
379
+ }
380
+ html.sidebar-mini #nav-menu-1 .sidebar-label {
381
+ display: none;
382
+ }
383
+ /* Center icons when labels are hidden */
384
+ html.sidebar-mini #nav-menu-1 nav,
385
+ html.sidebar-mini #nav-menu-1 .border-t > div,
386
+ html.sidebar-mini #nav-menu-1 .border-t {
387
+ padding-left: 0.375rem;
388
+ padding-right: 0.375rem;
389
+ }
390
+ html.sidebar-mini #nav-menu-1 nav a,
391
+ html.sidebar-mini #nav-menu-1 nav button,
392
+ html.sidebar-mini #nav-menu-1 .border-t a,
393
+ html.sidebar-mini #nav-menu-1 .border-t button,
394
+ html.sidebar-mini #nav-menu-1 .border-t form button {
395
+ justify-content: center;
396
+ padding-left: 0.625rem;
397
+ padding-right: 0.625rem;
398
+ }
399
+ /* Mini header: pull toggle out of absolute so flexbox centers it */
400
+ html.sidebar-mini #nav-menu-1 #sidebar-header {
401
+ padding-left: 0.375rem;
402
+ padding-right: 0.375rem;
403
+ }
404
+ html.sidebar-mini #nav-menu-1 #toggle-sidebar-mini {
405
+ position: static;
406
+ padding-left: 0.625rem;
407
+ padding-right: 0.625rem;
408
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Shared page-context types for the theme's auth/admin pages —
3
+ * mirrors what the framework's auth module renders.
4
+ */
5
+
6
+ export interface TableHeader {
7
+ /** Column heading shown to the user. */
8
+ title: string;
9
+ /** Row field this column renders. */
10
+ key: string;
11
+ format?: "number" | "delete" | "image";
12
+ }
13
+
14
+ export interface UserRow {
15
+ id: number;
16
+ email: string;
17
+ role: string;
18
+ created_at: string;
19
+ link: string;
20
+ delete_url: string;
21
+ }
22
+
23
+ export interface Role {
24
+ value: string;
25
+ label: string;
26
+ }
27
+
28
+ export interface UsersPage {
29
+ rows: UserRow[];
30
+ search: string;
31
+ filter_role: string;
32
+ roles: Role[];
33
+ page: number;
34
+ total_pages: number;
35
+ total_count: number;
36
+ per_page: number;
37
+ }
38
+
39
+ export interface UserPage {
40
+ id: number;
41
+ email: string;
42
+ role: string;
43
+ roles: Role[];
44
+ }
45
+
46
+ export interface SettingsPage {
47
+ current_email: string;
48
+ first_name: string;
49
+ last_name: string;
50
+ success?: string;
51
+ error?: string;
52
+ email_success?: string;
53
+ email_error?: string;
54
+ }
55
+
56
+ export interface ErrorPage {
57
+ error: string;
58
+ }
package/src/types.ts ADDED
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Render-context shapes for the generic `fse/*` pages — these mirror the
3
+ * JSON the framework's generated handlers build (framework/src/models/
4
+ * routes.rs). Child themes import them via `@parent/types` (or
5
+ * `fse-theme-default/src/types`).
6
+ */
7
+
8
+ export interface ModelColumn {
9
+ name: string;
10
+ widget:
11
+ | "text"
12
+ | "textarea"
13
+ | "number"
14
+ | "checkbox"
15
+ | "datetime"
16
+ | "date"
17
+ | "select"
18
+ | "relation"
19
+ | "email"
20
+ | "url"
21
+ | "json";
22
+ /** Enum values for `select`; `{ id, title }` rows the user may pick for
23
+ * `relation` (null when they may not read the related model). */
24
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
+ options: any[] | null;
26
+ required: boolean;
27
+ readonly: boolean;
28
+ nullable: boolean;
29
+ /** `#[ui(filter)]` kind, `null` when the column isn't a filter. */
30
+ filter: "exact" | "contains" | "range" | null;
31
+ /** Query params the filter reads: `[name]`, or `[name_from, name_to]`. */
32
+ filter_params: string[];
33
+ /** `#[ui(format = ...)]` kind, if any. */
34
+ format: string | null;
35
+ /** The row key to render: `{col}_display` when formatted, else `name`. */
36
+ display: string;
37
+ /** For a foreign key with `#[ui(show)]`: the row key holding `{ id, title }`. */
38
+ relation: string | null;
39
+ }
40
+
41
+ /** A nested model or link table under a row (`{base_path}/{id}/{segment}`). */
42
+ export interface ModelChild {
43
+ segment: string;
44
+ table: string;
45
+ }
46
+
47
+ /** The parent row of a nested page. */
48
+ export interface ParentContext {
49
+ meta: { table: string; base_path: string; title_field: string };
50
+ row: ModelRow;
51
+ }
52
+
53
+ export interface ModelMetaContext {
54
+ table: string;
55
+ base_path: string;
56
+ can_write: boolean;
57
+ no_create: boolean;
58
+ no_edit: boolean;
59
+ no_delete: boolean;
60
+ public_read: string | null;
61
+ title_field: string;
62
+ /** `#[model(owner = ...)]` column — filled by the server, never a form field. */
63
+ owner: string | null;
64
+ /** `#[model(parent = ...)]` column of a nested model. */
65
+ parent_column: string | null;
66
+ /** Row actions (`#[model(actions(...))]`). */
67
+ actions: string[];
68
+ /** Nested models under each row. */
69
+ children: ModelChild[];
70
+ /** `children` or `links` non-empty. */
71
+ has_subpages: boolean;
72
+ /** Many-to-many link tables under each row. */
73
+ links: ModelChild[];
74
+ list_columns: ModelColumn[];
75
+ form_columns: ModelColumn[];
76
+ search_columns: string[];
77
+ filter_columns: ModelColumn[];
78
+ }
79
+
80
+ /** One row — column values are only known at runtime, hence `any`. */
81
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
82
+ export type ModelRow = Record<string, any>;
83
+
84
+ export interface ModelListPage {
85
+ meta: ModelMetaContext;
86
+ rows: ModelRow[];
87
+ total: number;
88
+ page: number;
89
+ per_page: number;
90
+ total_pages: number;
91
+ has_prev: boolean;
92
+ has_next: boolean;
93
+ prev_page: number;
94
+ next_page: number;
95
+ search: string | null;
96
+ sort: string | null;
97
+ desc: boolean;
98
+ /** Current value per filter param (`""` = not filtered). */
99
+ filters: Record<string, string>;
100
+ /** Write permission, `can_create` hook and `no_create`, combined. */
101
+ can_create: boolean;
102
+ /** The parent row, for a nested model (else null). */
103
+ parent: ParentContext | null;
104
+ }
105
+
106
+ export interface ModelFormPage {
107
+ meta: ModelMetaContext;
108
+ row: ModelRow;
109
+ errors: { field: string; code: string }[];
110
+ is_new: boolean;
111
+ /** Write permission, `no_edit` and the model's `can_edit` hook, combined. */
112
+ can_edit: boolean;
113
+ /** Write permission, `no_delete` and the model's `can_delete` hook, combined. */
114
+ can_delete: boolean;
115
+ /** Row actions allowed on this row (`can_act`). */
116
+ actions: string[];
117
+ /** The parent row, for a nested model (else null). */
118
+ parent: ParentContext | null;
119
+ }
120
+
121
+ /** `fse/links` — the rows of another model linked to one row. */
122
+ export interface ModelLinksPage {
123
+ meta: ModelMetaContext;
124
+ parent: ParentContext;
125
+ link: { table: string; segment: string; other_table: string; base_path: string };
126
+ linked: { id: number; title: string }[];
127
+ can_write: boolean;
128
+ }
129
+
130
+ export interface ModelDetailPage {
131
+ meta: ModelMetaContext;
132
+ row: ModelRow;
133
+ }
134
+
135
+ /** One entry of the `nav` list the framework injects into every page. */
136
+ export interface NavItem {
137
+ table: string;
138
+ href: string;
139
+ }
140
+
141
+ /** The signed-in user as injected by the framework (absent when signed out). */
142
+ export interface SessionUser {
143
+ id: number;
144
+ role: string;
145
+ is_admin: boolean;
146
+ can_read_users: boolean;
147
+ }
148
+
149
+ /** Context keys every page receives, whatever renders it. */
150
+ export interface ShellContext {
151
+ nav: NavItem[];
152
+ user?: SessionUser;
153
+ lang_prefix: string;
154
+ }
package/theme.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "fse-theme-default",
3
+ "version": "0.2.1",
4
+ "description": "Default full_stack_engine theme: app shell, design system, auth pages, emails and the generic model CRUD pages."
5
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "astro/tsconfigs/strict",
3
+ "include": [".astro/types.d.ts", "**/*"],
4
+ "exclude": ["dist", "target"]
5
+ }