@svadmin/lite 0.3.27 → 0.6.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 (43) hide show
  1. package/README.md +106 -1
  2. package/dist/compatibility.d.ts +23 -0
  3. package/dist/compatibility.js +95 -0
  4. package/dist/components/LiteForm.svelte +1 -1
  5. package/dist/components/LiteForm.svelte.d.ts +1 -1
  6. package/dist/components/LiteShowField.svelte +32 -22
  7. package/dist/components/LiteShowField.svelte.d.ts +3 -2
  8. package/dist/components/LiteTable.svelte +90 -37
  9. package/dist/components/LiteTable.svelte.d.ts +3 -2
  10. package/dist/components/compatibility/LiteCapabilityBoundary.svelte +51 -0
  11. package/dist/components/compatibility/LiteCapabilityBoundary.svelte.d.ts +13 -0
  12. package/dist/components/compatibility/LiteClipboardFallback.svelte +16 -0
  13. package/dist/components/compatibility/LiteClipboardFallback.svelte.d.ts +8 -0
  14. package/dist/components/compatibility/LiteComputeFallback.svelte +50 -0
  15. package/dist/components/compatibility/LiteComputeFallback.svelte.d.ts +14 -0
  16. package/dist/components/compatibility/LiteDirectoryUpload.svelte +44 -0
  17. package/dist/components/compatibility/LiteDirectoryUpload.svelte.d.ts +11 -0
  18. package/dist/components/compatibility/LiteOrderedList.svelte +47 -0
  19. package/dist/components/compatibility/LiteOrderedList.svelte.d.ts +14 -0
  20. package/dist/components/compatibility/LiteRealtimeStatus.svelte +43 -0
  21. package/dist/components/compatibility/LiteRealtimeStatus.svelte.d.ts +11 -0
  22. package/dist/components/compatibility/LiteVisualFallback.svelte +80 -0
  23. package/dist/components/compatibility/LiteVisualFallback.svelte.d.ts +18 -0
  24. package/dist/components/compatibility/index.d.ts +7 -0
  25. package/dist/components/compatibility/index.js +7 -0
  26. package/dist/components/pages/LiteCreatePage.svelte +15 -6
  27. package/dist/components/pages/LiteCreatePage.svelte.d.ts +1 -1
  28. package/dist/components/pages/LiteEditPage.svelte +18 -9
  29. package/dist/components/pages/LiteEditPage.svelte.d.ts +1 -1
  30. package/dist/components/pages/LiteListPage.svelte +86 -16
  31. package/dist/components/pages/LiteListPage.svelte.d.ts +4 -2
  32. package/dist/components/pages/LiteShowPage.svelte +19 -10
  33. package/dist/components/pages/LiteShowPage.svelte.d.ts +1 -1
  34. package/dist/index.d.ts +7 -3
  35. package/dist/index.js +8 -3
  36. package/dist/lite.css +269 -0
  37. package/dist/schema-generator.d.ts +49 -13
  38. package/dist/schema-generator.js +381 -199
  39. package/dist/server-adapter.d.ts +25 -1
  40. package/dist/server-adapter.js +132 -33
  41. package/dist/value-normalization.d.ts +1 -0
  42. package/dist/value-normalization.js +18 -0
  43. package/package.json +10 -10
@@ -1,11 +1,11 @@
1
1
  <script lang="ts">
2
- import type { ResourceDefinition } from '@svadmin/core';
3
- import { t } from '@svadmin/core/i18n';
4
- import LiteTable from '../LiteTable.svelte';
5
- import LitePagination from '../LitePagination.svelte';
6
- import LiteSearch from '../LiteSearch.svelte';
7
- import LiteCreateButton from '../buttons/LiteCreateButton.svelte';
8
- import LiteRefreshButton from '../buttons/LiteRefreshButton.svelte';
2
+ import type { ResourceDefinition } from "@svadmin/core";
3
+ import { t } from "@svadmin/core/i18n";
4
+ import LiteTable from "../LiteTable.svelte";
5
+ import LitePagination from "../LitePagination.svelte";
6
+ import LiteBreadcrumbs from "../LiteBreadcrumbs.svelte";
7
+ import LiteCreateButton from "../buttons/LiteCreateButton.svelte";
8
+ import LiteRefreshButton from "../buttons/LiteRefreshButton.svelte";
9
9
 
10
10
  interface Props {
11
11
  resource: ResourceDefinition;
@@ -13,13 +13,15 @@
13
13
  total: number;
14
14
  pagination: { page: number; perPage: number };
15
15
  currentSort?: string;
16
- currentOrder?: 'asc' | 'desc';
16
+ currentOrder?: "asc" | "desc";
17
17
  currentSearch?: string;
18
+ currentFilters?: Record<string, string>;
18
19
  basePath?: string;
19
20
  canCreate?: boolean;
20
21
  canShow?: boolean;
21
22
  canEdit?: boolean;
22
23
  canDelete?: boolean;
24
+ enableBatch?: boolean;
23
25
  }
24
26
 
25
27
  let {
@@ -28,24 +30,43 @@
28
30
  total,
29
31
  pagination,
30
32
  currentSort,
31
- currentOrder = 'asc',
33
+ currentOrder = "asc",
32
34
  currentSearch,
33
- basePath = '/lite',
35
+ currentFilters = {},
36
+ basePath = "/lite",
34
37
  canCreate,
35
38
  canShow,
36
39
  canEdit,
37
40
  canDelete,
41
+ enableBatch = true,
38
42
  }: Props = $props();
39
43
 
40
44
  const showCreate = $derived(canCreate ?? resource.canCreate !== false);
41
45
  const showView = $derived(canShow ?? resource.canShow !== false);
42
46
  const showEdit = $derived(canEdit ?? resource.canEdit !== false);
43
47
  const showDelete = $derived(canDelete ?? resource.canDelete !== false);
48
+
49
+ const filterableFields = $derived(
50
+ resource.fields
51
+ .filter((f) => f.type === "select" && f.options && f.options.length > 0 && f.filterable !== false)
52
+ .slice(0, 3)
53
+ );
54
+
55
+ const hasActiveFilters = $derived(
56
+ Boolean(currentSearch) || Object.values(currentFilters).some((v) => Boolean(v))
57
+ );
44
58
  </script>
45
59
 
46
60
  <div class="lite-page">
61
+ <LiteBreadcrumbs
62
+ items={[
63
+ { label: t("common.dashboard") || "Dashboard", href: basePath },
64
+ { label: resource.label || resource.name },
65
+ ]}
66
+ />
67
+
47
68
  <div class="lite-page-header">
48
- <h1 class="lite-page-title">{resource.label || resource.name} {t('common.list') || 'List'}</h1>
69
+ <h1 class="lite-page-title">{resource.label || resource.name} {t("common.list") || "List"}</h1>
49
70
  <div class="lite-page-actions">
50
71
  {#if showCreate}
51
72
  <LiteCreateButton resource={resource.name} {basePath} />
@@ -55,10 +76,57 @@
55
76
  </div>
56
77
 
57
78
  <div class="lite-card" style="margin-bottom: 20px;">
58
- <div style="padding: 16px; border-bottom: 1px solid #e2e8f0; display: flex; justify-content: space-between; align-items: center;">
59
- <LiteSearch value={currentSearch} placeholder={t('common.search') || 'Search...'} />
79
+ <div style="padding: 16px; border-bottom: 1px solid #e2e8f0; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap;">
80
+ <form method="GET" class="lite-filter-bar" style="margin: 0;">
81
+ <input
82
+ type="text"
83
+ name="q"
84
+ value={currentSearch ?? ""}
85
+ placeholder={t("common.search") || "Search..."}
86
+ class="lite-input"
87
+ style="width: 200px; display: inline-block;"
88
+ />
89
+
90
+ {#each filterableFields as field (field.key)}
91
+ <select
92
+ name={field.key}
93
+ class="lite-select"
94
+ style="width: 150px; display: inline-block;"
95
+ >
96
+ <option value="">All {field.label}</option>
97
+ {#if field.options}
98
+ {#each field.options as opt (opt.value)}
99
+ <option
100
+ value={String(opt.value)}
101
+ selected={String(currentFilters[field.key] ?? "") === String(opt.value)}
102
+ >
103
+ {opt.label}
104
+ </option>
105
+ {/each}
106
+ {/if}
107
+ </select>
108
+ {/each}
109
+
110
+ {#if currentSort}
111
+ <input type="hidden" name="sort" value={currentSort} />
112
+ {/if}
113
+ {#if currentOrder}
114
+ <input type="hidden" name="order" value={currentOrder} />
115
+ {/if}
116
+
117
+ <button type="submit" class="lite-btn lite-btn-sm lite-btn-primary">
118
+ {t("common.filter") || "Filter"}
119
+ </button>
120
+
121
+ {#if hasActiveFilters}
122
+ <a href={basePath + "/" + resource.name} class="lite-btn lite-btn-sm">
123
+ {t("common.reset") || "Reset"}
124
+ </a>
125
+ {/if}
126
+ </form>
127
+
60
128
  <span style="font-size: 13px; color: #64748b;">
61
- {t('common.total') || 'Total'}: {total}
129
+ {t("common.total") || "Total"}: <strong>{total}</strong>
62
130
  </span>
63
131
  </div>
64
132
 
@@ -72,8 +140,9 @@
72
140
  canShow={showView}
73
141
  canEdit={showEdit}
74
142
  canDelete={showDelete}
143
+ {enableBatch}
75
144
  />
76
-
145
+
77
146
  {#if total > pagination.perPage}
78
147
  <LitePagination
79
148
  page={pagination.page}
@@ -81,7 +150,8 @@
81
150
  preserveParams={{
82
151
  ...(currentSort ? { sort: currentSort } : {}),
83
152
  ...(currentOrder ? { order: currentOrder } : {}),
84
- ...(currentSearch ? { q: currentSearch } : {})
153
+ ...(currentSearch ? { q: currentSearch } : {}),
154
+ ...currentFilters,
85
155
  }}
86
156
  />
87
157
  {/if}
@@ -1,4 +1,4 @@
1
- import type { ResourceDefinition } from '@svadmin/core';
1
+ import type { ResourceDefinition } from "@svadmin/core";
2
2
  interface Props {
3
3
  resource: ResourceDefinition;
4
4
  records: Record<string, unknown>[];
@@ -8,13 +8,15 @@ interface Props {
8
8
  perPage: number;
9
9
  };
10
10
  currentSort?: string;
11
- currentOrder?: 'asc' | 'desc';
11
+ currentOrder?: "asc" | "desc";
12
12
  currentSearch?: string;
13
+ currentFilters?: Record<string, string>;
13
14
  basePath?: string;
14
15
  canCreate?: boolean;
15
16
  canShow?: boolean;
16
17
  canEdit?: boolean;
17
18
  canDelete?: boolean;
19
+ enableBatch?: boolean;
18
20
  }
19
21
  declare const LiteListPage: import("svelte").Component<Props, {}, "">;
20
22
  type LiteListPage = ReturnType<typeof LiteListPage>;
@@ -1,10 +1,11 @@
1
1
  <script lang="ts">
2
- import type { ResourceDefinition } from '@svadmin/core';
3
- import { t } from '@svadmin/core/i18n';
4
- import LiteShowField from '../LiteShowField.svelte';
5
- import LiteListButton from '../buttons/LiteListButton.svelte';
6
- import LiteEditButton from '../buttons/LiteEditButton.svelte';
7
- import LiteDeleteButton from '../buttons/LiteDeleteButton.svelte';
2
+ import type { ResourceDefinition } from "@svadmin/core";
3
+ import { t } from "@svadmin/core/i18n";
4
+ import LiteShowField from "../LiteShowField.svelte";
5
+ import LiteBreadcrumbs from "../LiteBreadcrumbs.svelte";
6
+ import LiteListButton from "../buttons/LiteListButton.svelte";
7
+ import LiteEditButton from "../buttons/LiteEditButton.svelte";
8
+ import LiteDeleteButton from "../buttons/LiteDeleteButton.svelte";
8
9
 
9
10
  interface Props {
10
11
  resource: ResourceDefinition;
@@ -17,12 +18,12 @@
17
18
  let {
18
19
  resource,
19
20
  record,
20
- basePath = '/lite',
21
+ basePath = "/lite",
21
22
  canEdit,
22
23
  canDelete,
23
24
  }: Props = $props();
24
25
 
25
- let pk = $derived(resource.primaryKey ?? 'id');
26
+ let pk = $derived(resource.primaryKey ?? "id");
26
27
  let idStr = $derived(String(record[pk]));
27
28
  const showEdit = $derived(canEdit ?? resource.canEdit !== false);
28
29
  const showDelete = $derived(canDelete ?? resource.canDelete !== false);
@@ -33,8 +34,16 @@
33
34
  </script>
34
35
 
35
36
  <div class="lite-page">
37
+ <LiteBreadcrumbs
38
+ items={[
39
+ { label: t("common.dashboard") || "Dashboard", href: basePath },
40
+ { label: resource.label || resource.name, href: `${basePath}/${resource.name}` },
41
+ { label: `#${idStr}` },
42
+ ]}
43
+ />
44
+
36
45
  <div class="lite-page-header">
37
- <h1 class="lite-page-title">{t('common.show') || 'Show'} {resource.label || resource.name} #{idStr}</h1>
46
+ <h1 class="lite-page-title">{t("common.show") || "Show"} {resource.label || resource.name} #{idStr}</h1>
38
47
  <div class="lite-page-actions">
39
48
  {#if showEdit}
40
49
  <LiteEditButton resource={resource.name} recordItemId={idStr} {basePath} />
@@ -54,7 +63,7 @@
54
63
  {field.label}
55
64
  </div>
56
65
  <div style="color: #0f172a; font-size: 14px;">
57
- <LiteShowField {field} value={record[field.key]} />
66
+ <LiteShowField {field} value={record[field.key]} {basePath} />
58
67
  </div>
59
68
  </div>
60
69
  {/each}
@@ -1,4 +1,4 @@
1
- import type { ResourceDefinition } from '@svadmin/core';
1
+ import type { ResourceDefinition } from "@svadmin/core";
2
2
  interface Props {
3
3
  resource: ResourceDefinition;
4
4
  record: Record<string, unknown>;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,9 @@
1
- export { createListLoader, createDetailLoader, createCrudActions, createAuthGuard, createAuthActions, createLegacyRedirectHook, isLegacyBrowser, } from './server-adapter';
2
- export type { ListLoaderResult } from './server-adapter';
3
- export { fieldsToZodSchema, resourceToZodSchema, fieldToInputType, fieldToPlaceholder, } from './schema-generator';
1
+ export { createListLoader, createDetailLoader, createCrudActions, createAuthGuard, createAuthActions, createLegacyRedirectHook, getLegacyRedirectLocation, isLegacyBrowser, } from './server-adapter';
2
+ export type { LegacyRedirectOptions, ListLoaderResult } from './server-adapter';
3
+ export { LITE_COMPATIBILITY_CATALOG, detectLiteCapabilities, resolveLiteCompatibility, } from './compatibility';
4
+ export type { LiteCapability, LiteCapabilitySupport, LiteCompatibilityDescriptor, LiteCompatibilityResolution, LiteFallbackKind, } from './compatibility';
5
+ export { fieldsToTypeBoxSchema, resourceToTypeBoxSchema, fieldsToZodSchema, resourceToZodSchema, fieldToInputType, fieldToPlaceholder, } from './schema-generator';
6
+ export { getStatusBadgeClass, parseExplicitBoolean, isExplicitBooleanTrue } from './value-normalization';
4
7
  export { default as LiteLayout } from './components/LiteLayout.svelte';
5
8
  export { default as LiteTable } from './components/LiteTable.svelte';
6
9
  export { default as LitePagination } from './components/LitePagination.svelte';
@@ -20,6 +23,7 @@ export { default as LiteTabs } from './components/LiteTabs.svelte';
20
23
  export { default as LiteShowField } from './components/LiteShowField.svelte';
21
24
  export { default as LiteMediaThumbnail } from './components/LiteMediaThumbnail.svelte';
22
25
  export { default as LiteChatDialog } from './components/LiteChatDialog.svelte';
26
+ export * from './components/compatibility/index';
23
27
  export * from './components/buttons/index';
24
28
  export * from './components/fields/index';
25
29
  export * from './components/pages/index';
package/dist/index.js CHANGED
@@ -1,10 +1,13 @@
1
1
  // @svadmin/lite — Lightweight SSR Admin UI
2
2
  // Server-rendered by default, with optional progressive enhancement.
3
3
  // Server utilities (use in +page.server.ts / hooks.server.ts)
4
- export { createListLoader, createDetailLoader, createCrudActions, createAuthGuard, createAuthActions, createLegacyRedirectHook, isLegacyBrowser, } from './server-adapter';
5
- // Schema generator (used by Lite actions; also reusable by other Zod consumers)
6
- export { fieldsToZodSchema, resourceToZodSchema, fieldToInputType, fieldToPlaceholder, } from './schema-generator';
4
+ export { createListLoader, createDetailLoader, createCrudActions, createAuthGuard, createAuthActions, createLegacyRedirectHook, getLegacyRedirectLocation, isLegacyBrowser, } from './server-adapter';
5
+ // Optional browser capabilities. The SSR baseline does not import browser globals.
6
+ export { LITE_COMPATIBILITY_CATALOG, detectLiteCapabilities, resolveLiteCompatibility, } from './compatibility';
7
+ // Schema generator (TypeBox schemas used by Lite actions and client forms)
8
+ export { fieldsToTypeBoxSchema, resourceToTypeBoxSchema, fieldsToZodSchema, resourceToZodSchema, fieldToInputType, fieldToPlaceholder, } from './schema-generator';
7
9
  // UI Components (use in +page.svelte with csr = false)
10
+ export { getStatusBadgeClass, parseExplicitBoolean, isExplicitBooleanTrue } from './value-normalization';
8
11
  export { default as LiteLayout } from './components/LiteLayout.svelte';
9
12
  export { default as LiteTable } from './components/LiteTable.svelte';
10
13
  export { default as LitePagination } from './components/LitePagination.svelte';
@@ -24,6 +27,8 @@ export { default as LiteTabs } from './components/LiteTabs.svelte';
24
27
  export { default as LiteShowField } from './components/LiteShowField.svelte';
25
28
  export { default as LiteMediaThumbnail } from './components/LiteMediaThumbnail.svelte';
26
29
  export { default as LiteChatDialog } from './components/LiteChatDialog.svelte';
30
+ // Compatibility fallbacks for browser-only and third-party UI capabilities.
31
+ export * from './components/compatibility/index';
27
32
  // Action Buttons
28
33
  export * from './components/buttons/index';
29
34
  // Fields
package/dist/lite.css CHANGED
@@ -174,6 +174,21 @@ a:hover {
174
174
  background: #f8fafc;
175
175
  }
176
176
 
177
+ .lite-page-header {
178
+ display: flex;
179
+ align-items: flex-start;
180
+ justify-content: space-between;
181
+ margin-bottom: 20px;
182
+ }
183
+
184
+ .lite-page-title {
185
+ margin: 0;
186
+ font-size: 24px;
187
+ line-height: 1.25;
188
+ font-weight: 600;
189
+ color: #0f172a;
190
+ }
191
+
177
192
  .lite-header {
178
193
  display: flex;
179
194
  align-items: center;
@@ -189,6 +204,40 @@ a:hover {
189
204
  letter-spacing: -0.025em;
190
205
  }
191
206
 
207
+
208
+ /* ─── Breadcrumbs ──────────────────────────────────────────── */
209
+ .lite-breadcrumbs {
210
+ margin-bottom: 16px;
211
+ }
212
+ .lite-breadcrumbs ol {
213
+ display: flex;
214
+ align-items: center;
215
+ flex-wrap: wrap;
216
+ list-style: none;
217
+ padding: 0;
218
+ margin: 0;
219
+ }
220
+ .lite-breadcrumbs li {
221
+ display: flex;
222
+ align-items: center;
223
+ font-size: 13px;
224
+ color: #64748b;
225
+ }
226
+ .lite-breadcrumbs a {
227
+ color: #64748b;
228
+ }
229
+ .lite-breadcrumbs a:hover {
230
+ color: #0f172a;
231
+ }
232
+ .lite-breadcrumbs .current {
233
+ color: #0f172a;
234
+ font-weight: 500;
235
+ }
236
+ .lite-breadcrumb-sep {
237
+ margin: 0 8px;
238
+ color: #cbd5e1;
239
+ }
240
+
192
241
  /* ─── Cards ────────────────────────────────────────────────── */
193
242
  .lite-card {
194
243
  background: #fff;
@@ -747,6 +796,174 @@ textarea.lite-input {
747
796
  justify-content: flex-end;
748
797
  }
749
798
 
799
+ /* ─── Compatibility Fallbacks ─────────────────────────────── */
800
+ .lite-compatibility {
801
+ border: 1px solid #e2e8f0;
802
+ border-radius: 8px;
803
+ background: #fff;
804
+ margin-bottom: 16px;
805
+ }
806
+
807
+ .lite-compatibility-header,
808
+ .lite-section-header,
809
+ .lite-realtime-status {
810
+ display: flex;
811
+ align-items: flex-start;
812
+ justify-content: space-between;
813
+ }
814
+
815
+ .lite-compatibility-header {
816
+ padding: 14px 16px;
817
+ border-bottom: 1px solid #e2e8f0;
818
+ }
819
+
820
+ .lite-compatibility-header h2,
821
+ .lite-section-header h2,
822
+ .lite-ordered-list h2 {
823
+ margin: 0;
824
+ font-size: 15px;
825
+ font-weight: 600;
826
+ color: #0f172a;
827
+ }
828
+
829
+ .lite-compatibility-header p,
830
+ .lite-section-header p {
831
+ margin: 4px 0 0;
832
+ color: #64748b;
833
+ font-size: 12px;
834
+ }
835
+
836
+ .lite-compatibility-body {
837
+ padding: 16px;
838
+ }
839
+
840
+ .lite-visual-fallback {
841
+ padding: 0;
842
+ }
843
+
844
+ .lite-visual-fallback .lite-section-header {
845
+ padding: 14px 16px;
846
+ border-bottom: 1px solid #e2e8f0;
847
+ }
848
+
849
+ .lite-visual-snapshot {
850
+ padding: 16px;
851
+ border-bottom: 1px solid #e2e8f0;
852
+ text-align: center;
853
+ }
854
+
855
+ .lite-visual-snapshot img {
856
+ display: inline-block;
857
+ max-width: 100%;
858
+ height: auto;
859
+ }
860
+
861
+ .lite-table-scroll {
862
+ overflow-x: auto;
863
+ }
864
+
865
+ .lite-muted {
866
+ color: #64748b;
867
+ font-size: 12px;
868
+ }
869
+
870
+ .lite-realtime-status {
871
+ align-items: center;
872
+ padding: 10px 12px;
873
+ border: 1px solid #e2e8f0;
874
+ border-radius: 6px;
875
+ background: #fff;
876
+ }
877
+
878
+ .lite-realtime-status > div > * + * {
879
+ margin-left: 8px;
880
+ }
881
+
882
+ .lite-directory-upload {
883
+ border: 1px solid #e2e8f0;
884
+ border-radius: 8px;
885
+ padding: 16px;
886
+ background: #fff;
887
+ }
888
+
889
+ .lite-directory-upload legend {
890
+ padding: 0 6px;
891
+ font-size: 14px;
892
+ font-weight: 600;
893
+ }
894
+
895
+ .lite-ordered-list {
896
+ padding: 16px;
897
+ }
898
+
899
+ .lite-ordered-list ol {
900
+ margin: 12px 0 0;
901
+ padding-left: 24px;
902
+ }
903
+
904
+ .lite-ordered-list li {
905
+ padding: 10px 0 10px 4px;
906
+ border-bottom: 1px solid #f1f5f9;
907
+ }
908
+
909
+ .lite-ordered-list li > div:first-child {
910
+ margin-bottom: 8px;
911
+ }
912
+
913
+ .lite-ordered-list li span {
914
+ display: block;
915
+ margin-top: 2px;
916
+ color: #64748b;
917
+ font-size: 12px;
918
+ }
919
+
920
+ .lite-compute-fallback {
921
+ padding: 16px;
922
+ }
923
+
924
+ .lite-clipboard-fallback {
925
+ margin-bottom: 0;
926
+ }
927
+
928
+ .lite-dashboard-stats,
929
+ .lite-dashboard-split,
930
+ .lite-resource-links {
931
+ display: flex;
932
+ flex-wrap: wrap;
933
+ margin-left: -8px;
934
+ margin-right: -8px;
935
+ margin-bottom: 24px;
936
+ }
937
+
938
+ .lite-dashboard-stats > *,
939
+ .lite-dashboard-split > *,
940
+ .lite-resource-links > * {
941
+ margin-left: 8px;
942
+ margin-right: 8px;
943
+ margin-bottom: 16px;
944
+ }
945
+
946
+ .lite-dashboard-stats > * {
947
+ flex: 1 1 220px;
948
+ }
949
+
950
+ .lite-dashboard-split > *:first-child {
951
+ flex: 2 1 480px;
952
+ }
953
+
954
+ .lite-dashboard-split > *:last-child {
955
+ flex: 1 1 240px;
956
+ }
957
+
958
+ .lite-resource-links {
959
+ padding: 16px 8px 0;
960
+ margin-bottom: 0;
961
+ }
962
+
963
+ .lite-resource-links > * {
964
+ flex: 1 1 200px;
965
+ }
966
+
750
967
  /* ─── Login Page ───────────────────────────────────────────── */
751
968
  .lite-login-wrapper {
752
969
  display: flex;
@@ -791,6 +1008,36 @@ textarea.lite-input {
791
1008
  border-color: #fecaca;
792
1009
  }
793
1010
 
1011
+ .lite-badge-info {
1012
+ background: #eff6ff;
1013
+ color: #1d4ed8;
1014
+ border-color: #bfdbfe;
1015
+ }
1016
+
1017
+ .lite-badge-indigo {
1018
+ background: #eef2ff;
1019
+ color: #4338ca;
1020
+ border-color: #c7d2fe;
1021
+ }
1022
+
1023
+ .lite-filter-bar {
1024
+ display: flex;
1025
+ align-items: center;
1026
+ flex-wrap: wrap;
1027
+ }
1028
+ .lite-filter-bar > * + * {
1029
+ margin-left: 8px;
1030
+ }
1031
+
1032
+ .lite-batch-bar {
1033
+ display: flex;
1034
+ align-items: center;
1035
+ justify-content: space-between;
1036
+ padding: 10px 16px;
1037
+ background: #f8fafc;
1038
+ border-top: 1px solid #e2e8f0;
1039
+ }
1040
+
794
1041
  .lite-badge-warning {
795
1042
  background: #fffbeb;
796
1043
  color: #92400e;
@@ -830,6 +1077,11 @@ textarea.lite-input {
830
1077
  .lite-mobile-nav {
831
1078
  display: block;
832
1079
  }
1080
+ .lite-mobile-nav-links {
1081
+ max-height: 240px;
1082
+ overflow-y: auto;
1083
+ border-top: 1px solid #f1f5f9;
1084
+ }
833
1085
  .lite-main {
834
1086
  padding: 16px;
835
1087
  }
@@ -843,6 +1095,23 @@ textarea.lite-input {
843
1095
  .lite-header h1 {
844
1096
  margin-bottom: 12px;
845
1097
  }
1098
+ .lite-page-header {
1099
+ display: block;
1100
+ }
1101
+ .lite-page-header > .lite-btn {
1102
+ margin-top: 12px;
1103
+ }
1104
+ .lite-compatibility-header,
1105
+ .lite-section-header,
1106
+ .lite-realtime-status {
1107
+ display: block;
1108
+ }
1109
+ .lite-compatibility-header .lite-badge,
1110
+ .lite-section-header > .lite-badge,
1111
+ .lite-section-header > .lite-btn,
1112
+ .lite-realtime-status > .lite-btn {
1113
+ margin-top: 10px;
1114
+ }
846
1115
  }
847
1116
 
848
1117
  /* ─── Print ────────────────────────────────────────────────── */
@@ -1,26 +1,62 @@
1
1
  /**
2
2
  * @svadmin/lite — Schema Generator
3
3
  *
4
- * Auto-generates Zod schemas from @svadmin/core FieldDefinitions.
5
- * Used by the Lite server actions and compatible with other Zod consumers.
4
+ * Auto-generates TypeBox schemas from @svadmin/core FieldDefinitions.
5
+ * Used by Lite server actions and high-speed JIT form validation.
6
6
  */
7
- import { z } from 'zod';
8
- import type { FieldDefinition, ResourceDefinition } from '@svadmin/core';
7
+ import { type TObject } from "@sinclair/typebox";
8
+ import type { FieldDefinition, ResourceDefinition } from "@svadmin/core";
9
+ export interface SchemaValidationIssue {
10
+ path: (string | number)[];
11
+ message: string;
12
+ }
13
+ export type SchemaValidationResult<T = Record<string, unknown>> = {
14
+ success: true;
15
+ data: T;
16
+ error?: never;
17
+ } | {
18
+ success: false;
19
+ error: {
20
+ issues: SchemaValidationIssue[];
21
+ };
22
+ data?: never;
23
+ };
24
+ export type TypeBoxEnhancedSchema<T = Record<string, unknown>> = TObject & {
25
+ parse: (values: unknown) => T;
26
+ safeParse: (values: unknown) => SchemaValidationResult<T>;
27
+ Check: (values: unknown) => boolean;
28
+ "~standard": {
29
+ version: 1;
30
+ vendor: "svadmin";
31
+ validate: (values: unknown) => {
32
+ value: T;
33
+ } | {
34
+ issues: Array<{
35
+ message: string;
36
+ path?: (string | number)[];
37
+ }>;
38
+ };
39
+ };
40
+ };
9
41
  /**
10
- * Generate a Zod object schema from a ResourceDefinition's fields.
11
- * Only includes fields that are relevant for form rendering.
12
- *
13
- * @param mode - 'create' | 'edit' to filter fields by showInCreate / showInEdit
42
+ * Generate a TypeBox object schema from a list of FieldDefinitions.
43
+ */
44
+ export declare function fieldsToTypeBoxSchema(fields: FieldDefinition[], mode?: "create" | "edit"): TypeBoxEnhancedSchema;
45
+ /**
46
+ * Generate a TypeBox schema from a ResourceDefinition.
47
+ * Convenience wrapper around fieldsToTypeBoxSchema.
48
+ */
49
+ export declare function resourceToTypeBoxSchema(resource: ResourceDefinition, mode?: "create" | "edit"): TypeBoxEnhancedSchema;
50
+ /**
51
+ * Backward compatibility alias for fieldsToTypeBoxSchema
14
52
  */
15
- export declare function fieldsToZodSchema(fields: FieldDefinition[], mode?: 'create' | 'edit'): z.ZodObject<Record<string, z.ZodTypeAny>>;
53
+ export declare const fieldsToZodSchema: typeof fieldsToTypeBoxSchema;
16
54
  /**
17
- * Generate a Zod schema from a ResourceDefinition.
18
- * Convenience wrapper around fieldsToZodSchema.
55
+ * Backward compatibility alias for resourceToTypeBoxSchema
19
56
  */
20
- export declare function resourceToZodSchema(resource: ResourceDefinition, mode?: 'create' | 'edit'): z.ZodObject<Record<string, z.ZodTypeAny>>;
57
+ export declare const resourceToZodSchema: typeof resourceToTypeBoxSchema;
21
58
  /**
22
59
  * Determine a conservative HTML input type for server-rendered forms.
23
- * Text fallbacks avoid inconsistent native validation and date widgets.
24
60
  */
25
61
  export declare function fieldToInputType(field: FieldDefinition): string;
26
62
  /**