@svadmin/lite 0.5.0 → 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 (37) hide show
  1. package/README.md +105 -0
  2. package/dist/compatibility.d.ts +23 -0
  3. package/dist/compatibility.js +95 -0
  4. package/dist/components/LiteShowField.svelte +30 -22
  5. package/dist/components/LiteShowField.svelte.d.ts +3 -2
  6. package/dist/components/LiteTable.svelte +89 -36
  7. package/dist/components/LiteTable.svelte.d.ts +3 -2
  8. package/dist/components/compatibility/LiteCapabilityBoundary.svelte +51 -0
  9. package/dist/components/compatibility/LiteCapabilityBoundary.svelte.d.ts +13 -0
  10. package/dist/components/compatibility/LiteClipboardFallback.svelte +16 -0
  11. package/dist/components/compatibility/LiteClipboardFallback.svelte.d.ts +8 -0
  12. package/dist/components/compatibility/LiteComputeFallback.svelte +50 -0
  13. package/dist/components/compatibility/LiteComputeFallback.svelte.d.ts +14 -0
  14. package/dist/components/compatibility/LiteDirectoryUpload.svelte +44 -0
  15. package/dist/components/compatibility/LiteDirectoryUpload.svelte.d.ts +11 -0
  16. package/dist/components/compatibility/LiteOrderedList.svelte +47 -0
  17. package/dist/components/compatibility/LiteOrderedList.svelte.d.ts +14 -0
  18. package/dist/components/compatibility/LiteRealtimeStatus.svelte +43 -0
  19. package/dist/components/compatibility/LiteRealtimeStatus.svelte.d.ts +11 -0
  20. package/dist/components/compatibility/LiteVisualFallback.svelte +80 -0
  21. package/dist/components/compatibility/LiteVisualFallback.svelte.d.ts +18 -0
  22. package/dist/components/compatibility/index.d.ts +7 -0
  23. package/dist/components/compatibility/index.js +7 -0
  24. package/dist/components/pages/LiteCreatePage.svelte +15 -6
  25. package/dist/components/pages/LiteCreatePage.svelte.d.ts +1 -1
  26. package/dist/components/pages/LiteEditPage.svelte +18 -9
  27. package/dist/components/pages/LiteEditPage.svelte.d.ts +1 -1
  28. package/dist/components/pages/LiteListPage.svelte +86 -16
  29. package/dist/components/pages/LiteListPage.svelte.d.ts +4 -2
  30. package/dist/components/pages/LiteShowPage.svelte +19 -10
  31. package/dist/components/pages/LiteShowPage.svelte.d.ts +1 -1
  32. package/dist/index.d.ts +5 -2
  33. package/dist/index.js +5 -1
  34. package/dist/lite.css +239 -0
  35. package/dist/server-adapter.d.ts +18 -1
  36. package/dist/server-adapter.js +73 -16
  37. package/package.json +2 -2
@@ -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,5 +1,7 @@
1
- export { createListLoader, createDetailLoader, createCrudActions, createAuthGuard, createAuthActions, createLegacyRedirectHook, isLegacyBrowser, } from './server-adapter';
2
- export type { ListLoaderResult } from './server-adapter';
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';
3
5
  export { fieldsToTypeBoxSchema, resourceToTypeBoxSchema, fieldsToZodSchema, resourceToZodSchema, fieldToInputType, fieldToPlaceholder, } from './schema-generator';
4
6
  export { getStatusBadgeClass, parseExplicitBoolean, isExplicitBooleanTrue } from './value-normalization';
5
7
  export { default as LiteLayout } from './components/LiteLayout.svelte';
@@ -21,6 +23,7 @@ export { default as LiteTabs } from './components/LiteTabs.svelte';
21
23
  export { default as LiteShowField } from './components/LiteShowField.svelte';
22
24
  export { default as LiteMediaThumbnail } from './components/LiteMediaThumbnail.svelte';
23
25
  export { default as LiteChatDialog } from './components/LiteChatDialog.svelte';
26
+ export * from './components/compatibility/index';
24
27
  export * from './components/buttons/index';
25
28
  export * from './components/fields/index';
26
29
  export * from './components/pages/index';
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
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';
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';
5
7
  // Schema generator (TypeBox schemas used by Lite actions and client forms)
6
8
  export { fieldsToTypeBoxSchema, resourceToTypeBoxSchema, fieldsToZodSchema, resourceToZodSchema, fieldToInputType, fieldToPlaceholder, } from './schema-generator';
7
9
  // UI Components (use in +page.svelte with csr = false)
@@ -25,6 +27,8 @@ export { default as LiteTabs } from './components/LiteTabs.svelte';
25
27
  export { default as LiteShowField } from './components/LiteShowField.svelte';
26
28
  export { default as LiteMediaThumbnail } from './components/LiteMediaThumbnail.svelte';
27
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';
28
32
  // Action Buttons
29
33
  export * from './components/buttons/index';
30
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;
@@ -860,6 +1077,11 @@ textarea.lite-input {
860
1077
  .lite-mobile-nav {
861
1078
  display: block;
862
1079
  }
1080
+ .lite-mobile-nav-links {
1081
+ max-height: 240px;
1082
+ overflow-y: auto;
1083
+ border-top: 1px solid #f1f5f9;
1084
+ }
863
1085
  .lite-main {
864
1086
  padding: 16px;
865
1087
  }
@@ -873,6 +1095,23 @@ textarea.lite-input {
873
1095
  .lite-header h1 {
874
1096
  margin-bottom: 12px;
875
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
+ }
876
1115
  }
877
1116
 
878
1117
  /* ─── Print ────────────────────────────────────────────────── */
@@ -24,6 +24,7 @@ export interface ListLoaderResult {
24
24
  currentSort?: string;
25
25
  currentOrder?: 'asc' | 'desc';
26
26
  currentSearch?: string;
27
+ currentFilters?: Record<string, string>;
27
28
  resource: ResourceDefinition;
28
29
  }
29
30
  /**
@@ -157,11 +158,27 @@ export declare function createAuthActions(authProvider: AuthProvider): {
157
158
  * This helper does not imply that Svelte 5 or the consuming app supports IE11.
158
159
  */
159
160
  export declare function isLegacyBrowser(userAgent: string): boolean;
161
+ export interface LegacyRedirectOptions {
162
+ /** Route prefix that contains the standalone SSR Lite application. */
163
+ litePrefix?: string;
164
+ /** Optional route prefix occupied by the modern SPA, for example `/admin`. */
165
+ spaPrefix?: string;
166
+ /** Paths that must remain outside automatic legacy routing. */
167
+ exclude?: string[];
168
+ /** Override the SPA-to-Lite path mapping without changing SPA code. */
169
+ mapPath?: (pathname: string) => string;
170
+ status?: 302 | 307;
171
+ }
172
+ /**
173
+ * Computes a legacy redirect without importing or modifying the modern SPA.
174
+ * This pure helper can be used from non-SvelteKit server middleware.
175
+ */
176
+ export declare function getLegacyRedirectLocation(request: Pick<Request, 'method' | 'headers'>, url: URL, options?: string | LegacyRedirectOptions): string | undefined;
160
177
  /**
161
178
  * Creates an opt-in SvelteKit hook that redirects detected IE11 user agents.
162
179
  * Consumers remain responsible for their own transpilation and browser support.
163
180
  */
164
- export declare function createLegacyRedirectHook(litePrefix?: string): ({ event, resolve }: {
181
+ export declare function createLegacyRedirectHook(options?: string | LegacyRedirectOptions): ({ event, resolve }: {
165
182
  event: RequestEvent;
166
183
  resolve: (event: RequestEvent) => Promise<Response>;
167
184
  }) => Promise<Response>;