@svadmin/ui 0.29.0 → 0.29.2

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 (224) hide show
  1. package/package.json +20 -9
  2. package/src/README.md +0 -18
  3. package/src/actions.ts +0 -110
  4. package/src/app.css +0 -519
  5. package/src/component-registry.svelte.ts +0 -60
  6. package/src/components/AICommandBar.svelte +0 -180
  7. package/src/components/AboutSettings.svelte +0 -106
  8. package/src/components/AdminApp.svelte +0 -199
  9. package/src/components/AnimatedCounter.svelte +0 -46
  10. package/src/components/AnomalyBadge.svelte +0 -65
  11. package/src/components/AppearanceSettings.svelte +0 -183
  12. package/src/components/AuditLogDrawer.svelte +0 -93
  13. package/src/components/AuditLogViewer.svelte +0 -226
  14. package/src/components/Authenticated.svelte +0 -51
  15. package/src/components/AutoForm.svelte +0 -242
  16. package/src/components/AutoSaveIndicator.svelte +0 -35
  17. package/src/components/AutoTable.svelte +0 -839
  18. package/src/components/Breadcrumbs.svelte +0 -58
  19. package/src/components/CanAccess.svelte +0 -23
  20. package/src/components/CatchAllNavigate.svelte +0 -21
  21. package/src/components/ChatDialog.svelte +0 -535
  22. package/src/components/ComboboxField.svelte +0 -129
  23. package/src/components/CommandPalette.svelte +0 -135
  24. package/src/components/ConfigErrorScreen.svelte +0 -126
  25. package/src/components/ConfirmDialog.svelte +0 -53
  26. package/src/components/CopilotPanel.svelte +0 -163
  27. package/src/components/CreatePage.svelte +0 -39
  28. package/src/components/DevTools.svelte +0 -149
  29. package/src/components/DraggableGrid.svelte +0 -86
  30. package/src/components/DraggableHeader.svelte +0 -112
  31. package/src/components/DrawerForm.svelte +0 -35
  32. package/src/components/EditPage.svelte +0 -52
  33. package/src/components/EmptyState.svelte +0 -31
  34. package/src/components/ErrorBoundary.svelte +0 -49
  35. package/src/components/ErrorComponent.svelte +0 -36
  36. package/src/components/FieldRenderer.svelte +0 -357
  37. package/src/components/ForgotPasswordPage.svelte +0 -112
  38. package/src/components/Header.svelte +0 -94
  39. package/src/components/InferencerPanel.svelte +0 -192
  40. package/src/components/InfiniteList.svelte +0 -93
  41. package/src/components/InlineEdit.svelte +0 -88
  42. package/src/components/InsightCard.svelte +0 -98
  43. package/src/components/KeyboardShortcuts.svelte +0 -61
  44. package/src/components/Layout.svelte +0 -167
  45. package/src/components/ListPage.svelte +0 -71
  46. package/src/components/LiveIndicator.svelte +0 -58
  47. package/src/components/LoginPage.svelte +0 -163
  48. package/src/components/MarkdownRenderer.svelte +0 -127
  49. package/src/components/ModalForm.svelte +0 -38
  50. package/src/components/NavigateToResource.svelte +0 -20
  51. package/src/components/PageHeader.svelte +0 -39
  52. package/src/components/PageSkeleton.svelte +0 -71
  53. package/src/components/PasswordInput.svelte +0 -96
  54. package/src/components/PermissionMatrix.svelte +0 -185
  55. package/src/components/ProfilePage.svelte +0 -284
  56. package/src/components/RegisterPage.svelte +0 -112
  57. package/src/components/RolesSettings.svelte +0 -146
  58. package/src/components/SettingsPage.svelte +0 -113
  59. package/src/components/ShowPage.svelte +0 -73
  60. package/src/components/Sidebar.svelte +0 -417
  61. package/src/components/SidebarItem.svelte +0 -129
  62. package/src/components/SmartSuggest.svelte +0 -158
  63. package/src/components/StatsCard.svelte +0 -79
  64. package/src/components/StepsForm.svelte +0 -192
  65. package/src/components/TaskQueueDrawer.svelte +0 -158
  66. package/src/components/TenantSwitcher.svelte +0 -84
  67. package/src/components/ThemedTitle.svelte +0 -32
  68. package/src/components/Toast.svelte +0 -46
  69. package/src/components/TooltipButton.svelte +0 -42
  70. package/src/components/UndoableNotification.svelte +0 -69
  71. package/src/components/UpdatePasswordPage.svelte +0 -89
  72. package/src/components/VirtualTable.svelte +0 -76
  73. package/src/components/VoiceInput.svelte +0 -120
  74. package/src/components/buttons/CloneButton.svelte +0 -30
  75. package/src/components/buttons/CreateButton.svelte +0 -29
  76. package/src/components/buttons/DeleteButton.svelte +0 -59
  77. package/src/components/buttons/EditButton.svelte +0 -30
  78. package/src/components/buttons/ExportButton.svelte +0 -30
  79. package/src/components/buttons/ImportButton.svelte +0 -56
  80. package/src/components/buttons/ListButton.svelte +0 -23
  81. package/src/components/buttons/RefreshButton.svelte +0 -36
  82. package/src/components/buttons/SaveButton.svelte +0 -27
  83. package/src/components/buttons/ShowButton.svelte +0 -31
  84. package/src/components/buttons/index.ts +0 -10
  85. package/src/components/charts/BarChart.svelte +0 -69
  86. package/src/components/charts/LineChart.svelte +0 -115
  87. package/src/components/charts/PieChart.svelte +0 -95
  88. package/src/components/charts/index.ts +0 -3
  89. package/src/components/fieldComponentMap.ts +0 -79
  90. package/src/components/fields/ArrayField.svelte +0 -86
  91. package/src/components/fields/BooleanField.svelte +0 -13
  92. package/src/components/fields/DateField.svelte +0 -6
  93. package/src/components/fields/EmailField.svelte +0 -9
  94. package/src/components/fields/FileField.svelte +0 -14
  95. package/src/components/fields/ImageField.svelte +0 -9
  96. package/src/components/fields/JsonField.svelte +0 -72
  97. package/src/components/fields/MarkdownField.svelte +0 -9
  98. package/src/components/fields/MultiSelectField.svelte +0 -21
  99. package/src/components/fields/NumberField.svelte +0 -6
  100. package/src/components/fields/RelationField.svelte +0 -24
  101. package/src/components/fields/RichTextField.svelte +0 -35
  102. package/src/components/fields/SelectField.svelte +0 -14
  103. package/src/components/fields/TagField.svelte +0 -12
  104. package/src/components/fields/TextField.svelte +0 -11
  105. package/src/components/fields/UrlField.svelte +0 -14
  106. package/src/components/ui/alert/alert-description.svelte +0 -17
  107. package/src/components/ui/alert/alert-title.svelte +0 -17
  108. package/src/components/ui/alert/alert.svelte +0 -44
  109. package/src/components/ui/alert/index.ts +0 -15
  110. package/src/components/ui/alert-dialog/alert-dialog-content.svelte +0 -29
  111. package/src/components/ui/alert-dialog/alert-dialog-description.svelte +0 -16
  112. package/src/components/ui/alert-dialog/alert-dialog-footer.svelte +0 -11
  113. package/src/components/ui/alert-dialog/alert-dialog-header.svelte +0 -11
  114. package/src/components/ui/alert-dialog/alert-dialog-overlay.svelte +0 -16
  115. package/src/components/ui/alert-dialog/alert-dialog-portal.svelte +0 -6
  116. package/src/components/ui/alert-dialog/alert-dialog-title.svelte +0 -16
  117. package/src/components/ui/alert-dialog/index.ts +0 -20
  118. package/src/components/ui/avatar/avatar.svelte +0 -63
  119. package/src/components/ui/avatar/index.ts +0 -2
  120. package/src/components/ui/badge/badge.svelte +0 -49
  121. package/src/components/ui/badge/index.ts +0 -3
  122. package/src/components/ui/breadcrumb/breadcrumb-ellipsis.svelte +0 -18
  123. package/src/components/ui/breadcrumb/breadcrumb-item.svelte +0 -15
  124. package/src/components/ui/breadcrumb/breadcrumb-link.svelte +0 -15
  125. package/src/components/ui/breadcrumb/breadcrumb-list.svelte +0 -15
  126. package/src/components/ui/breadcrumb/breadcrumb-page.svelte +0 -18
  127. package/src/components/ui/breadcrumb/breadcrumb-separator.svelte +0 -22
  128. package/src/components/ui/breadcrumb/breadcrumb.svelte +0 -11
  129. package/src/components/ui/breadcrumb/index.ts +0 -7
  130. package/src/components/ui/button/button.svelte +0 -90
  131. package/src/components/ui/button/index.ts +0 -18
  132. package/src/components/ui/card/card-action.svelte +0 -23
  133. package/src/components/ui/card/card-content.svelte +0 -20
  134. package/src/components/ui/card/card-description.svelte +0 -20
  135. package/src/components/ui/card/card-footer.svelte +0 -20
  136. package/src/components/ui/card/card-header.svelte +0 -23
  137. package/src/components/ui/card/card-title.svelte +0 -15
  138. package/src/components/ui/card/card.svelte +0 -22
  139. package/src/components/ui/card/index.ts +0 -25
  140. package/src/components/ui/checkbox/checkbox.svelte +0 -39
  141. package/src/components/ui/checkbox/index.ts +0 -6
  142. package/src/components/ui/collapsible/index.ts +0 -7
  143. package/src/components/ui/command/command-dialog.svelte +0 -31
  144. package/src/components/ui/command/index.ts +0 -2
  145. package/src/components/ui/context-menu/index.ts +0 -11
  146. package/src/components/ui/dialog/dialog-close.svelte +0 -11
  147. package/src/components/ui/dialog/dialog-content.svelte +0 -48
  148. package/src/components/ui/dialog/dialog-description.svelte +0 -17
  149. package/src/components/ui/dialog/dialog-footer.svelte +0 -32
  150. package/src/components/ui/dialog/dialog-header.svelte +0 -20
  151. package/src/components/ui/dialog/dialog-overlay.svelte +0 -17
  152. package/src/components/ui/dialog/dialog-portal.svelte +0 -7
  153. package/src/components/ui/dialog/dialog-title.svelte +0 -17
  154. package/src/components/ui/dialog/dialog-trigger.svelte +0 -11
  155. package/src/components/ui/dialog/dialog.svelte +0 -7
  156. package/src/components/ui/dialog/index.ts +0 -34
  157. package/src/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +0 -43
  158. package/src/components/ui/dropdown-menu/dropdown-menu-content.svelte +0 -35
  159. package/src/components/ui/dropdown-menu/dropdown-menu-item.svelte +0 -36
  160. package/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte +0 -10
  161. package/src/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +0 -27
  162. package/src/components/ui/dropdown-menu/dropdown-menu.svelte +0 -45
  163. package/src/components/ui/dropdown-menu/index.ts +0 -21
  164. package/src/components/ui/input/index.ts +0 -7
  165. package/src/components/ui/input/input.svelte +0 -48
  166. package/src/components/ui/label/index.ts +0 -1
  167. package/src/components/ui/label/label.svelte +0 -18
  168. package/src/components/ui/pagination/index.ts +0 -7
  169. package/src/components/ui/pagination/pagination-content.svelte +0 -11
  170. package/src/components/ui/pagination/pagination-ellipsis.svelte +0 -12
  171. package/src/components/ui/pagination/pagination-item.svelte +0 -11
  172. package/src/components/ui/pagination/pagination-link.svelte +0 -26
  173. package/src/components/ui/pagination/pagination-next.svelte +0 -22
  174. package/src/components/ui/pagination/pagination-previous.svelte +0 -22
  175. package/src/components/ui/pagination/pagination.svelte +0 -11
  176. package/src/components/ui/popover/index.ts +0 -12
  177. package/src/components/ui/popover/popover-content.svelte +0 -25
  178. package/src/components/ui/progress/index.ts +0 -1
  179. package/src/components/ui/progress/progress.svelte +0 -22
  180. package/src/components/ui/scroll-area/index.ts +0 -1
  181. package/src/components/ui/scroll-area/scroll-area.svelte +0 -20
  182. package/src/components/ui/select/index.ts +0 -2
  183. package/src/components/ui/select/select.svelte +0 -36
  184. package/src/components/ui/separator/index.ts +0 -7
  185. package/src/components/ui/separator/separator.svelte +0 -23
  186. package/src/components/ui/sheet/index.ts +0 -16
  187. package/src/components/ui/sheet/sheet-content.svelte +0 -28
  188. package/src/components/ui/sheet/sheet-description.svelte +0 -22
  189. package/src/components/ui/sheet/sheet-footer.svelte +0 -22
  190. package/src/components/ui/sheet/sheet-header.svelte +0 -22
  191. package/src/components/ui/sheet/sheet-title.svelte +0 -22
  192. package/src/components/ui/sheet/sheet.svelte +0 -78
  193. package/src/components/ui/skeleton/index.ts +0 -2
  194. package/src/components/ui/skeleton/skeleton.svelte +0 -21
  195. package/src/components/ui/switch/index.ts +0 -2
  196. package/src/components/ui/switch/switch.svelte +0 -49
  197. package/src/components/ui/table/index.ts +0 -28
  198. package/src/components/ui/table/table-body.svelte +0 -15
  199. package/src/components/ui/table/table-caption.svelte +0 -20
  200. package/src/components/ui/table/table-cell.svelte +0 -15
  201. package/src/components/ui/table/table-footer.svelte +0 -20
  202. package/src/components/ui/table/table-head.svelte +0 -15
  203. package/src/components/ui/table/table-header.svelte +0 -20
  204. package/src/components/ui/table/table-row.svelte +0 -15
  205. package/src/components/ui/table/table.svelte +0 -17
  206. package/src/components/ui/tabs/index.ts +0 -15
  207. package/src/components/ui/tabs/tabs-content.svelte +0 -30
  208. package/src/components/ui/tabs/tabs-list.svelte +0 -26
  209. package/src/components/ui/tabs/tabs-trigger.svelte +0 -37
  210. package/src/components/ui/tabs/tabs.svelte +0 -27
  211. package/src/components/ui/textarea/index.ts +0 -2
  212. package/src/components/ui/textarea/textarea.svelte +0 -24
  213. package/src/components/ui/tooltip/index.ts +0 -19
  214. package/src/components/ui/tooltip/tooltip-content.svelte +0 -52
  215. package/src/components/ui/tooltip/tooltip-portal.svelte +0 -7
  216. package/src/components/ui/tooltip/tooltip-provider.svelte +0 -7
  217. package/src/components/ui/tooltip/tooltip-trigger.svelte +0 -7
  218. package/src/components/ui/tooltip/tooltip.svelte +0 -10
  219. package/src/editor-config.svelte.ts +0 -23
  220. package/src/index.ts +0 -155
  221. package/src/markdown-deps.d.ts +0 -21
  222. package/src/router-state.svelte.ts +0 -65
  223. package/src/types.ts +0 -33
  224. package/src/utils.ts +0 -28
@@ -1,60 +0,0 @@
1
- /**
2
- * Component Registry — Svelte Context-based DI for component injection.
3
- *
4
- * Allows users to override any registered component via the `components`
5
- * prop on `AdminApp`, while providing sensible defaults.
6
- */
7
- import { getContext, setContext, type Component } from 'svelte';
8
-
9
- // ─── Overridable component slots ────────────────────────────────
10
- export interface ComponentRegistry {
11
- // Layout primitives
12
- Layout: Component<any>;
13
- Sidebar: Component<any>;
14
- Header: Component<any>;
15
-
16
- // Pages
17
- LoginPage: Component<any>;
18
- AutoTable: Component<any>;
19
- AutoForm: Component<any>;
20
- ShowPage: Component<any>;
21
-
22
- // Shadcn primitives
23
- Button: Component<any>;
24
- Input: Component<any>;
25
- Badge: Component<any>;
26
- Skeleton: Component<any>;
27
-
28
- // ─── Extended slots (optional) ──────────────────────────────
29
- // These allow deeper customization without replacing entire Layout/Header.
30
-
31
- /** Custom dashboard page component (replaces default welcome message) */
32
- DashboardPage?: Component<any>;
33
- /** Custom breadcrumbs component (replaces built-in Breadcrumbs) */
34
- Breadcrumbs?: Component<any>;
35
- /** Custom theme toggle button (replaces built-in Sun/Moon toggle) */
36
- ThemeToggle?: Component<any>;
37
- /** Custom user menu / avatar dropdown in the header */
38
- UserMenu?: Component<any>;
39
- /** Custom notification panel / bell icon in the header */
40
- NotificationPanel?: Component<any>;
41
- }
42
-
43
- const REGISTRY_KEY = 'svadmin:components';
44
-
45
- /** Set the component registry in context (called by AdminApp). */
46
- export function setComponentRegistry(registry: ComponentRegistry): void {
47
- setContext(REGISTRY_KEY, registry);
48
- }
49
-
50
- /** Retrieve the full component registry from context. */
51
- export function getComponentRegistry(): ComponentRegistry {
52
- return getContext<ComponentRegistry>(REGISTRY_KEY);
53
- }
54
-
55
- /** Retrieve a single component by name from the registry. */
56
- export function useComponent<K extends keyof ComponentRegistry>(
57
- name: K,
58
- ): ComponentRegistry[K] {
59
- return getComponentRegistry()[name];
60
- }
@@ -1,180 +0,0 @@
1
- <script lang="ts">
2
- import { getResources, getChatProvider } from '@svadmin/core';
3
- import { navigate } from '@svadmin/core/router';
4
- import { t } from '@svadmin/core/i18n';
5
- import { toggleTheme } from '@svadmin/core';
6
- import { Command } from 'cmdk-sv';
7
- import * as Dialog from './ui/dialog/index.js';
8
- import { Search, LayoutDashboard, Plus, Sun, FileText, Sparkles, Loader2, Send } from '@lucide/svelte';
9
- import MarkdownRenderer from './MarkdownRenderer.svelte';
10
-
11
- let { open = $bindable(false) } = $props<{ open?: boolean }>();
12
- let searchValue = $state('');
13
-
14
- const resources = getResources();
15
- const provider = $derived(getChatProvider());
16
-
17
- // AI State
18
- let aiMode = $state(false);
19
- let aiResponse = $state('');
20
- let isPredicting = $state(false);
21
- let abortController: AbortController | null = null;
22
-
23
- function close() {
24
- open = false;
25
- searchValue = '';
26
- aiMode = false;
27
- aiResponse = '';
28
- if (abortController) {
29
- abortController.abort();
30
- abortController = null;
31
- }
32
- }
33
-
34
- function act(fn: () => void) {
35
- fn();
36
- close();
37
- }
38
-
39
- async function askAI() {
40
- if (!provider || !searchValue.trim()) return;
41
-
42
- aiMode = true;
43
- isPredicting = true;
44
- aiResponse = '';
45
-
46
- if (abortController) abortController.abort();
47
- abortController = new AbortController();
48
-
49
- try {
50
- const result = provider.sendMessage(
51
- [{ id: 'cmd', role: 'user', content: searchValue, timestamp: Date.now() }],
52
- { signal: abortController.signal }
53
- );
54
-
55
- if (Symbol.asyncIterator in result) {
56
- for await (const chunk of result as AsyncIterable<string>) {
57
- aiResponse += chunk;
58
- }
59
- } else {
60
- aiResponse = await result;
61
- }
62
- } catch (err: unknown) {
63
- if (err instanceof Error && err.name !== 'AbortError') {
64
- aiResponse = 'Failed to get response from AI.';
65
- }
66
- } finally {
67
- isPredicting = false;
68
- }
69
- }
70
-
71
- function handleKeyDown(e: KeyboardEvent) {
72
- if (e.key === 'Enter' && searchValue.trim() && !aiMode) {
73
- // If pressing enter on an active search, cmdk usually handles selection.
74
- // But if there's no selection or we want to force AI, we can intercept.
75
- // For simplicity, we trigger AI on Ctrl+Enter
76
- if (e.ctrlKey || e.metaKey) {
77
- e.preventDefault();
78
- askAI();
79
- }
80
- }
81
- }
82
-
83
- const itemClass = 'relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50';
84
- </script>
85
-
86
- <Dialog.Dialog bind:open onOpenChange={(v: boolean) => !v && close()}>
87
- <Dialog.DialogContent class="overflow-hidden p-0 sm:max-w-[600px] border-none shadow-2xl">
88
- <Command.Root
89
- shouldFilter={!aiMode}
90
- class="flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground [&_[data-cmdk-group-heading]]:text-muted-foreground [&_[data-cmdk-group-heading]]:px-2 [&_[data-cmdk-group-heading]]:py-1.5 [&_[data-cmdk-group-heading]]:text-xs [&_[data-cmdk-group-heading]]:font-semibold [&_[data-cmdk-group-heading]]:uppercase [&_[data-cmdk-group-heading]]:tracking-wider"
91
- >
92
- <div class="flex items-center border-b px-3 h-12 relative" data-cmdk-input-wrapper="">
93
- {#if aiMode}
94
- <Sparkles class="mr-2 h-4 w-4 shrink-0 text-warning animate-pulse" />
95
- {:else}
96
- <Search class="mr-2 h-4 w-4 shrink-0 opacity-50" />
97
- {/if}
98
- <Command.Input
99
- bind:value={searchValue}
100
- onkeydown={handleKeyDown}
101
- placeholder={aiMode ? "Ask AI anything..." : "Search commands or ask AI (Ctrl+Enter)..."}
102
- class="flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50"
103
- />
104
- {#if searchValue.trim() && !aiMode}
105
- <button
106
- class="absolute right-3 flex items-center gap-1 text-[10px] font-medium text-warning bg-warning/10 hover:bg-warning/20 px-2 py-1 rounded transition-colors"
107
- onclick={askAI}
108
- >
109
- <Sparkles class="h-3 w-3" />
110
- <kbd class="font-sans">Ctrl+Enter</kbd> to Ask AI
111
- </button>
112
- {/if}
113
- </div>
114
-
115
- {#if !aiMode}
116
- <Command.List class="max-h-[360px] overflow-y-auto overflow-x-hidden p-1">
117
- <Command.Empty class="py-6 text-center text-sm text-muted-foreground">
118
- No standard commands found. Press <kbd class="px-1 py-0.5 rounded bg-muted border font-sans text-[10px]">Ctrl+Enter</kbd> to ask AI.
119
- </Command.Empty>
120
-
121
- <!-- Navigation -->
122
- <Command.Group heading={t('common.home')}>
123
- <Command.Item value="home" onSelect={() => act(() => navigate('/'))} class={itemClass}>
124
- <LayoutDashboard class="h-4 w-4 text-muted-foreground" />
125
- {t('common.home')}
126
- </Command.Item>
127
- {#each resources as r}
128
- <Command.Item value={r.name} onSelect={() => act(() => navigate(`/${r.name}`))} class={itemClass}>
129
- <FileText class="h-4 w-4 text-muted-foreground" />
130
- {r.label}
131
- </Command.Item>
132
- {/each}
133
- </Command.Group>
134
-
135
- <Command.Separator class="mx-1 my-1 h-px bg-border" />
136
-
137
- <!-- Actions -->
138
- <Command.Group heading={t('common.actions')}>
139
- {#each resources as r}
140
- <Command.Item value={`create-${r.name}`} onSelect={() => act(() => navigate(`/${r.name}/create`))} class={itemClass}>
141
- <Plus class="h-4 w-4 text-muted-foreground" />
142
- {t('common.create')} {r.label}
143
- </Command.Item>
144
- {/each}
145
- <Command.Item value="toggle-theme" onSelect={() => act(() => toggleTheme())} class={itemClass}>
146
- <Sun class="h-4 w-4 text-muted-foreground" />
147
- {t('common.toggleTheme')}
148
- </Command.Item>
149
- </Command.Group>
150
- </Command.List>
151
- {:else}
152
- <!-- AI Response Area -->
153
- <div class="h-[360px] max-h-[360px] overflow-y-auto p-4 bg-muted/20">
154
- <div class="flex items-start gap-3">
155
- <div class="mt-0.5 w-8 h-8 rounded-full bg-warning/10 text-warning flex items-center justify-center shrink-0">
156
- <Sparkles class="h-4 w-4" />
157
- </div>
158
- <div class="flex-1 w-full text-sm">
159
- {#if !aiResponse && isPredicting}
160
- <div class="flex items-center gap-2 text-muted-foreground mt-1.5">
161
- <Loader2 class="h-4 w-4 animate-spin" /> Thinking...
162
- </div>
163
- {:else}
164
- <MarkdownRenderer content={aiResponse} streaming={isPredicting} />
165
- {/if}
166
- </div>
167
- </div>
168
- </div>
169
- <div class="p-2 border-t flex justify-end gap-2 bg-muted/10">
170
- <button
171
- class="text-xs px-3 py-1.5 rounded-md hover:bg-muted transition-colors text-muted-foreground"
172
- onclick={() => { aiMode = false; searchValue = ''; }}
173
- >
174
- Clear & Return
175
- </button>
176
- </div>
177
- {/if}
178
- </Command.Root>
179
- </Dialog.DialogContent>
180
- </Dialog.Dialog>
@@ -1,106 +0,0 @@
1
- <script lang="ts">
2
- import { t } from '@svadmin/core/i18n';
3
- import { getResources, getDataProviderNames } from '@svadmin/core';
4
- import { getLocale } from '@svadmin/core/i18n';
5
- import { getResolvedTheme, getColorTheme } from '@svadmin/core';
6
- import * as Card from './ui/card/index.js';
7
- import { Badge } from './ui/badge/index.js';
8
- import { Info, Database, Globe, Palette, Layers } from '@lucide/svelte';
9
-
10
- const resources = getResources();
11
- const providerNames = getDataProviderNames();
12
-
13
- const version = '__SVADMIN_VERSION__'; // replaced at build time or shown as-is
14
- </script>
15
-
16
- <div class="space-y-6">
17
- <div>
18
- <h2 class="text-xl font-semibold text-foreground">{t('settings.about')}</h2>
19
- </div>
20
-
21
- <!-- Version -->
22
- <Card.Card>
23
- <Card.CardHeader class="pb-3">
24
- <Card.CardTitle class="text-base flex items-center gap-2">
25
- <Info class="h-4 w-4 text-muted-foreground" />
26
- svadmin
27
- </Card.CardTitle>
28
- </Card.CardHeader>
29
- <Card.CardContent>
30
- <div class="grid gap-4 text-sm">
31
- <div class="flex items-center justify-between">
32
- <span class="text-muted-foreground">{t('settings.version')}</span>
33
- <Badge variant="secondary" class="font-mono text-xs">{version}</Badge>
34
- </div>
35
- </div>
36
- </Card.CardContent>
37
- </Card.Card>
38
-
39
- <!-- Registered Resources -->
40
- <Card.Card>
41
- <Card.CardHeader class="pb-3">
42
- <Card.CardTitle class="text-base flex items-center gap-2">
43
- <Layers class="h-4 w-4 text-muted-foreground" />
44
- {t('settings.registeredResources')}
45
- </Card.CardTitle>
46
- </Card.CardHeader>
47
- <Card.CardContent>
48
- <div class="flex flex-wrap gap-2">
49
- {#each resources as resource}
50
- <Badge variant="outline" class="font-mono text-xs">{resource.name}</Badge>
51
- {/each}
52
- {#if resources.length === 0}
53
- <p class="text-sm text-muted-foreground">—</p>
54
- {/if}
55
- </div>
56
- </Card.CardContent>
57
- </Card.Card>
58
-
59
- <!-- Data Providers -->
60
- <Card.Card>
61
- <Card.CardHeader class="pb-3">
62
- <Card.CardTitle class="text-base flex items-center gap-2">
63
- <Database class="h-4 w-4 text-muted-foreground" />
64
- {t('settings.dataProvider')}
65
- </Card.CardTitle>
66
- </Card.CardHeader>
67
- <Card.CardContent>
68
- <div class="flex flex-wrap gap-2">
69
- {#each providerNames as name}
70
- <Badge variant="outline" class="font-mono text-xs">{name}</Badge>
71
- {/each}
72
- </div>
73
- </Card.CardContent>
74
- </Card.Card>
75
-
76
- <!-- Current Environment -->
77
- <Card.Card>
78
- <Card.CardHeader class="pb-3">
79
- <Card.CardTitle class="text-base flex items-center gap-2">
80
- <Globe class="h-4 w-4 text-muted-foreground" />
81
- {t('settings.currentEnvironment')}
82
- </Card.CardTitle>
83
- </Card.CardHeader>
84
- <Card.CardContent>
85
- <div class="grid gap-3 text-sm">
86
- <div class="flex items-center justify-between">
87
- <span class="text-muted-foreground">{t('settings.language')}</span>
88
- <span class="font-mono text-xs">{getLocale()}</span>
89
- </div>
90
- <div class="h-px bg-border"></div>
91
- <div class="flex items-center justify-between">
92
- <span class="text-muted-foreground">{t('settings.themeMode')}</span>
93
- <span class="font-mono text-xs">{getResolvedTheme()}</span>
94
- </div>
95
- <div class="h-px bg-border"></div>
96
- <div class="flex items-center justify-between">
97
- <span class="text-muted-foreground flex items-center gap-1.5">
98
- <Palette class="h-3.5 w-3.5" />
99
- {t('settings.colorAccent')}
100
- </span>
101
- <span class="font-mono text-xs">{getColorTheme()}</span>
102
- </div>
103
- </div>
104
- </Card.CardContent>
105
- </Card.Card>
106
- </div>
@@ -1,199 +0,0 @@
1
- <script lang="ts">
2
- // Auto-inject design tokens so consumers don't need to manually import
3
- import '../app.css';
4
- import type { Snippet } from 'svelte';
5
- import type { DataProvider, AuthProvider, ResourceDefinition, ThemeMode, RouterProvider, ThemeConfig, MenuItem } from '@svadmin/core';
6
- import { setDataProvider, setAuthProvider, setResources, setLocale, setTheme, setRouterProvider, getAuthProvider, createHashRouterProvider, configureTheme } from '@svadmin/core';
7
- import { t } from '@svadmin/core/i18n';
8
- import { navigate } from '@svadmin/core/router';
9
- import { currentPath } from '@svadmin/core/router';
10
- import { QueryClient, QueryClientProvider } from '@tanstack/svelte-query';
11
- import { setComponentRegistry, getComponentRegistry, type ComponentRegistry } from '../component-registry.svelte.js';
12
- import Layout from './Layout.svelte';
13
- import AutoTable from './AutoTable.svelte';
14
- import AutoForm from './AutoForm.svelte';
15
- import ShowPage from './ShowPage.svelte';
16
- import Toast from './Toast.svelte';
17
- import LoginPage from './LoginPage.svelte';
18
- import RegisterPage from './RegisterPage.svelte';
19
- import ForgotPasswordPage from './ForgotPasswordPage.svelte';
20
- import UpdatePasswordPage from './UpdatePasswordPage.svelte';
21
- import ConfigErrorScreen from './ConfigErrorScreen.svelte';
22
- import DevTools from './DevTools.svelte';
23
- import Sidebar from './Sidebar.svelte';
24
- import Header from './Header.svelte';
25
- import SettingsPage from './SettingsPage.svelte';
26
- import { Button } from './ui/button/index.js';
27
- import { Input } from './ui/input/index.js';
28
- import { Badge } from './ui/badge/index.js';
29
- import { Skeleton } from './ui/skeleton/index.js';
30
- import { initRouter, getRoute, getParams } from '../router-state.svelte.js';
31
-
32
- interface Props {
33
- dataProvider: DataProvider;
34
- authProvider?: AuthProvider;
35
- routerProvider?: RouterProvider;
36
- resources: ResourceDefinition[];
37
- locale?: string;
38
- title?: string;
39
- defaultTheme?: ThemeMode;
40
- /** Theme configuration for dark-first mode, CSS overrides, etc. */
41
- themeConfig?: ThemeConfig;
42
- dashboard?: Snippet;
43
- loginPage?: Snippet;
44
- /** Override default components via DI */
45
- components?: Partial<ComponentRegistry>;
46
- /** Custom multi-level menu configuration */
47
- menu?: MenuItem[];
48
- /** External URL to the main application or workspace (renders a shortcut in the header) */
49
- siteUrl?: string;
50
- }
51
-
52
- let {
53
- dataProvider,
54
- authProvider,
55
- routerProvider,
56
- resources,
57
- locale,
58
- title = 'Admin',
59
- defaultTheme,
60
- themeConfig: userThemeConfig,
61
- dashboard,
62
- loginPage,
63
- components: userComponents,
64
- menu,
65
- siteUrl,
66
- }: Props = $props();
67
-
68
- // Default component registry
69
- const defaultComponents: ComponentRegistry = {
70
- Layout, Sidebar, Header,
71
- LoginPage, AutoTable, AutoForm, ShowPage,
72
- Button: Button as unknown as ComponentRegistry['Button'],
73
- Input: Input as unknown as ComponentRegistry['Input'],
74
- Badge: Badge as unknown as ComponentRegistry['Badge'],
75
- Skeleton: Skeleton as unknown as ComponentRegistry['Skeleton'],
76
- };
77
-
78
- // Merge user overrides and set context
79
- setComponentRegistry({ ...defaultComponents, ...userComponents });
80
-
81
- // Resolve router provider (default to hash)
82
- const resolvedRouter = $derived(routerProvider ?? createHashRouterProvider());
83
-
84
- // Set up context — use $effect so prop changes are tracked
85
- $effect.pre(() => {
86
- setDataProvider(dataProvider);
87
- if (authProvider) setAuthProvider(authProvider);
88
- setResources(resources);
89
- setRouterProvider(resolvedRouter);
90
- if (locale) setLocale(locale);
91
- if (userThemeConfig) configureTheme(userThemeConfig);
92
- if (defaultTheme) setTheme(defaultTheme);
93
- });
94
-
95
- const queryClient = new QueryClient({
96
- defaultOptions: {
97
- queries: { staleTime: 30_000, retry: 1 },
98
- },
99
- });
100
-
101
- // Initialize router with provider
102
- $effect.pre(() => {
103
- initRouter(resolvedRouter);
104
- });
105
-
106
- // Reactive getters for route state
107
- const route = $derived(getRoute());
108
- const params = $derived(getParams());
109
-
110
- // Auth check
111
- let isAuthenticated = $state(false);
112
- let authChecked = $state(false);
113
-
114
- $effect(() => {
115
- // Explicitly track route so auth is re-checked on navigation
116
- const _route = route;
117
-
118
- if (!authProvider) {
119
- isAuthenticated = true;
120
- authChecked = true;
121
- return;
122
- }
123
- authProvider.check().then(result => {
124
- isAuthenticated = result.authenticated;
125
- authChecked = true;
126
- if (!result.authenticated && _route !== '/login' && _route !== '/register' && _route !== '/forgot-password' && _route !== '/update-password') {
127
- navigate(result.redirectTo ?? '/login');
128
- }
129
- });
130
- });
131
- </script>
132
-
133
- <QueryClientProvider client={queryClient}>
134
- {#if !authChecked}
135
- <div class="flex h-screen items-center justify-center">
136
- <div class="space-y-4 text-center">
137
- <div class="h-8 w-8 animate-spin rounded-full border-4 border-muted border-t-primary mx-auto"></div>
138
- <p class="text-sm text-muted-foreground">Loading...</p>
139
- </div>
140
- </div>
141
- {:else if route === '/login' && loginPage}
142
- {@render loginPage()}
143
- {:else if route === '/login' && authProvider}
144
- <LoginPage {title} onSuccess={() => { isAuthenticated = true; navigate('/'); }} />
145
- {:else if route === '/register' && authProvider?.register}
146
- <RegisterPage {title} />
147
- {:else if route === '/forgot-password' && authProvider?.forgotPassword}
148
- <ForgotPasswordPage {title} />
149
- {:else if route === '/update-password' && authProvider?.updatePassword}
150
- <UpdatePasswordPage {title} />
151
- {:else if route === '/login' || route === '/register' || route === '/forgot-password' || route === '/update-password'}
152
- <ConfigErrorScreen title="{title} — {t('common.configRequired')}" />
153
- {:else if isAuthenticated || !authProvider}
154
- <Layout {title} {menu} {siteUrl}>
155
- {#key route + (params.resource ?? '') + (params.id ?? '')}
156
- <div class="svadmin-page-enter">
157
- {#if currentPath().startsWith('/settings')}
158
- <SettingsPage />
159
- {:else if route === '/' || route === ''}
160
- {#if dashboard}
161
- {@render dashboard()}
162
- {:else}
163
- <div class="space-y-4">
164
- <h1 class="text-xl font-semibold text-foreground">{t('common.welcome', { title })}</h1>
165
- <p class="text-muted-foreground">{t('common.dashboardHint')}</p>
166
- </div>
167
- {/if}
168
- {:else if route === '/:resource'}
169
- {#key params.resource}
170
- {@const Comp = getComponentRegistry().AutoTable}
171
- <Comp resourceName={params.resource} />
172
- {/key}
173
- {:else if route === '/:resource/create'}
174
- {#key params.resource}
175
- {@const Comp = getComponentRegistry().AutoForm}
176
- <Comp resourceName={params.resource} mode="create" />
177
- {/key}
178
- {:else if route === '/:resource/edit/:id'}
179
- {#key `${params.resource}-${params.id}`}
180
- {@const Comp = getComponentRegistry().AutoForm}
181
- <Comp resourceName={params.resource} mode="edit" id={params.id} />
182
- {/key}
183
- {:else if route === '/:resource/show/:id'}
184
- {#key `${params.resource}-${params.id}`}
185
- {@const Comp = getComponentRegistry().ShowPage}
186
- <Comp resourceName={params.resource} id={params.id} />
187
- {/key}
188
- {/if}
189
- </div>
190
- {/key}
191
- </Layout>
192
- {:else}
193
- <div class="flex h-screen items-center justify-center">
194
- <p class="text-muted-foreground">{t('common.redirecting')}</p>
195
- </div>
196
- {/if}
197
- <Toast />
198
- <DevTools />
199
- </QueryClientProvider>
@@ -1,46 +0,0 @@
1
- <script lang="ts">
2
- import { untrack } from 'svelte';
3
-
4
- interface Props {
5
- /** The target number to animate to */
6
- value: number | string;
7
- /** Animation duration in milliseconds */
8
- duration?: number;
9
- /** CSS class for the container */
10
- class?: string;
11
- }
12
-
13
- let { value, duration = 600, class: className = '' }: Props = $props();
14
-
15
- let displayValue = $state('0');
16
- let prevTarget = 0;
17
-
18
- $effect(() => {
19
- const target = typeof value === 'number' ? value : parseInt(value, 10);
20
- if (isNaN(target)) {
21
- displayValue = String(value);
22
- return;
23
- }
24
-
25
- const start = untrack(() => prevTarget);
26
- prevTarget = target;
27
- const startTime = performance.now();
28
-
29
- function tick() {
30
- const elapsed = performance.now() - startTime;
31
- const progress = Math.min(elapsed / duration, 1);
32
- // Ease-out cubic
33
- const eased = 1 - Math.pow(1 - progress, 3);
34
- const current = Math.round(start + (target - start) * eased);
35
- displayValue = current.toLocaleString();
36
-
37
- if (progress < 1) {
38
- requestAnimationFrame(tick);
39
- }
40
- }
41
-
42
- requestAnimationFrame(tick);
43
- });
44
- </script>
45
-
46
- <span class={className}>{displayValue}</span>
@@ -1,65 +0,0 @@
1
- <script lang="ts">
2
- import { Badge } from './ui/badge/index.js';
3
- import { TrendingUp, TrendingDown, AlertTriangle } from '@lucide/svelte';
4
- import TooltipButton from './TooltipButton.svelte';
5
-
6
- interface Props {
7
- /** The actual current value */
8
- value: number;
9
- /** The baseline/expected value to compare against */
10
- baseline: number;
11
- /** Threshold percentage to trigger an anomaly (e.g. 0.2 means 20% diff) */
12
- threshold?: number;
13
- /** Optional label formatting */
14
- formatter?: (val: number) => string;
15
- /** If true, lower is better (e.g. latency). Default false (higher is better e.g. revenue). */
16
- lowerIsBetter?: boolean;
17
- class?: string;
18
- }
19
-
20
- let {
21
- value,
22
- baseline,
23
- threshold = 0.2,
24
- formatter = (v) => v.toString(),
25
- lowerIsBetter = false,
26
- class: className = ''
27
- }: Props = $props();
28
-
29
- // Calculate percentage difference
30
- const diff = $derived(baseline === 0 ? 0 : (value - baseline) / Math.abs(baseline));
31
- const isAnomaly = $derived(Math.abs(diff) >= threshold);
32
-
33
- // Determine sentiment (good, bad, neutral)
34
- const isGood = $derived(
35
- !isAnomaly ? null :
36
- (lowerIsBetter ? diff < 0 : diff > 0)
37
- );
38
-
39
- const percentLabel = $derived(`${Math.abs(diff * 100).toFixed(1)}%`);
40
- </script>
41
-
42
- {#if !isAnomaly}
43
- <span class="text-muted-foreground text-sm font-medium {className}">
44
- {formatter(value)}
45
- </span>
46
- {:else}
47
- <TooltipButton
48
- tooltip={`Baseline: ${formatter(baseline)} (${diff > 0 ? '+' : '-'}${percentLabel})`}
49
- variant="ghost"
50
- size="sm"
51
- class="p-0 h-auto hover:bg-transparent"
52
- >
53
- <Badge
54
- variant={isGood ? 'default' : 'destructive'}
55
- class="flex items-center gap-1 font-mono text-xs px-1.5 py-0 {isGood ? 'bg-success hover:bg-success/90 text-success-foreground' : ''} {className}"
56
- >
57
- {#if diff > 0}
58
- <TrendingUp class="h-3 w-3" />
59
- {:else}
60
- <TrendingDown class="h-3 w-3" />
61
- {/if}
62
- {formatter(value)}
63
- </Badge>
64
- </TooltipButton>
65
- {/if}