@svadmin/ui 0.11.1 → 0.12.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.
package/package.json
CHANGED
package/src/app.css
CHANGED
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
@layer base {
|
|
11
|
+
/* Global border-color reset — ensures Tailwind border utilities
|
|
12
|
+
* (border-r, border-t, etc.) use the theme color instead of currentColor */
|
|
13
|
+
*,
|
|
14
|
+
::after,
|
|
15
|
+
::before {
|
|
16
|
+
border-color: var(--border);
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
:root {
|
|
12
20
|
--radius: 0.5rem;
|
|
13
21
|
--background: oklch(0.98 0.002 264);
|
|
@@ -169,3 +177,59 @@ body {
|
|
|
169
177
|
--radius-lg: var(--radius);
|
|
170
178
|
--radius-xl: calc(var(--radius) + 4px);
|
|
171
179
|
}
|
|
180
|
+
|
|
181
|
+
/* ─── svadmin UI Components CSS ─────────────────────────────── */
|
|
182
|
+
/* These rules were moved from .svelte files to eliminate <style> tags */
|
|
183
|
+
|
|
184
|
+
/* MarkdownRenderer Styles */
|
|
185
|
+
.prose {
|
|
186
|
+
line-height: 1.6;
|
|
187
|
+
}
|
|
188
|
+
.prose p {
|
|
189
|
+
margin-top: 0.5em;
|
|
190
|
+
margin-bottom: 0.5em;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* Streaming cursor effect */
|
|
194
|
+
.streaming > *:last-child::after {
|
|
195
|
+
content: '▋';
|
|
196
|
+
display: inline-block;
|
|
197
|
+
animation: svadmin-blink 1s step-start infinite;
|
|
198
|
+
margin-left: 2px;
|
|
199
|
+
vertical-align: baseline;
|
|
200
|
+
color: currentColor;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
@keyframes svadmin-blink {
|
|
204
|
+
50% { opacity: 0; }
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* Handle copied state for dynamically injected buttons */
|
|
208
|
+
.copy-btn[data-copied-id] {
|
|
209
|
+
color: #10b981; /* Tailwind emerald-500 */
|
|
210
|
+
}
|
|
211
|
+
.copy-btn[data-copied-id] svg {
|
|
212
|
+
/* Swap to Check icon */
|
|
213
|
+
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="%2310b981" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>');
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* ChatDialog Styles */
|
|
217
|
+
.chat-markdown pre.chat-code-block {
|
|
218
|
+
background: var(--color-muted, hsl(var(--muted)));
|
|
219
|
+
border-radius: 0.5rem;
|
|
220
|
+
padding: 0.75rem;
|
|
221
|
+
margin: 0.5rem 0;
|
|
222
|
+
overflow-x: auto;
|
|
223
|
+
font-size: 0.75rem;
|
|
224
|
+
line-height: 1.5;
|
|
225
|
+
}
|
|
226
|
+
.chat-markdown code.chat-inline-code {
|
|
227
|
+
background: var(--color-muted, hsl(var(--muted)));
|
|
228
|
+
padding: 0.125rem 0.375rem;
|
|
229
|
+
border-radius: 0.25rem;
|
|
230
|
+
font-size: 0.8125em;
|
|
231
|
+
font-family: ui-monospace, monospace;
|
|
232
|
+
}
|
|
233
|
+
.chat-markdown strong {
|
|
234
|
+
font-weight: 600;
|
|
235
|
+
}
|
|
@@ -24,6 +24,20 @@ export interface ComponentRegistry {
|
|
|
24
24
|
Input: Component<any>;
|
|
25
25
|
Badge: Component<any>;
|
|
26
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>;
|
|
27
41
|
}
|
|
28
42
|
|
|
29
43
|
const REGISTRY_KEY = 'svadmin:components';
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Auto-inject design tokens so consumers don't need to manually import
|
|
3
3
|
import '../app.css';
|
|
4
4
|
import type { Snippet } from 'svelte';
|
|
5
|
-
import type { DataProvider, AuthProvider, ResourceDefinition, ThemeMode, RouterProvider } from '@svadmin/core';
|
|
6
|
-
import { setDataProvider, setAuthProvider, setResources, setLocale, setTheme, setRouterProvider, getAuthProvider, createHashRouterProvider } from '@svadmin/core';
|
|
5
|
+
import type { DataProvider, AuthProvider, ResourceDefinition, ThemeMode, RouterProvider, ThemeConfig } from '@svadmin/core';
|
|
6
|
+
import { setDataProvider, setAuthProvider, setResources, setLocale, setTheme, setRouterProvider, getAuthProvider, createHashRouterProvider, configureTheme } from '@svadmin/core';
|
|
7
7
|
import { t } from '@svadmin/core/i18n';
|
|
8
8
|
import { navigate } from '@svadmin/core/router';
|
|
9
9
|
import { QueryClient, QueryClientProvider } from '@tanstack/svelte-query';
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
locale?: string;
|
|
36
36
|
title?: string;
|
|
37
37
|
defaultTheme?: ThemeMode;
|
|
38
|
+
/** Theme configuration for dark-first mode, CSS overrides, etc. */
|
|
39
|
+
themeConfig?: ThemeConfig;
|
|
38
40
|
dashboard?: Snippet;
|
|
39
41
|
loginPage?: Snippet;
|
|
40
42
|
/** Override default components via DI */
|
|
@@ -49,6 +51,7 @@
|
|
|
49
51
|
locale,
|
|
50
52
|
title = 'Admin',
|
|
51
53
|
defaultTheme,
|
|
54
|
+
themeConfig: userThemeConfig,
|
|
52
55
|
dashboard,
|
|
53
56
|
loginPage,
|
|
54
57
|
components: userComponents,
|
|
@@ -77,6 +80,7 @@
|
|
|
77
80
|
setResources(resources);
|
|
78
81
|
setRouterProvider(resolvedRouter);
|
|
79
82
|
if (locale) setLocale(locale);
|
|
83
|
+
if (userThemeConfig) configureTheme(userThemeConfig);
|
|
80
84
|
if (defaultTheme) setTheme(defaultTheme);
|
|
81
85
|
});
|
|
82
86
|
|
|
@@ -446,24 +446,4 @@
|
|
|
446
446
|
{/if}
|
|
447
447
|
{/if}
|
|
448
448
|
|
|
449
|
-
|
|
450
|
-
:global(.chat-markdown pre.chat-code-block) {
|
|
451
|
-
background: hsl(var(--muted));
|
|
452
|
-
border-radius: 0.5rem;
|
|
453
|
-
padding: 0.75rem;
|
|
454
|
-
margin: 0.5rem 0;
|
|
455
|
-
overflow-x: auto;
|
|
456
|
-
font-size: 0.75rem;
|
|
457
|
-
line-height: 1.5;
|
|
458
|
-
}
|
|
459
|
-
:global(.chat-markdown code.chat-inline-code) {
|
|
460
|
-
background: hsl(var(--muted));
|
|
461
|
-
padding: 0.125rem 0.375rem;
|
|
462
|
-
border-radius: 0.25rem;
|
|
463
|
-
font-size: 0.8125em;
|
|
464
|
-
font-family: ui-monospace, monospace;
|
|
465
|
-
}
|
|
466
|
-
:global(.chat-markdown strong) {
|
|
467
|
-
font-weight: 600;
|
|
468
|
-
}
|
|
469
|
-
</style>
|
|
449
|
+
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { getResolvedTheme, toggleTheme } from '@svadmin/core';
|
|
6
6
|
import Breadcrumbs from './Breadcrumbs.svelte';
|
|
7
7
|
import { t } from '@svadmin/core/i18n';
|
|
8
|
+
import { getComponentRegistry } from '../component-registry.svelte.js';
|
|
8
9
|
|
|
9
10
|
let {
|
|
10
11
|
showThemeToggle = false,
|
|
@@ -12,13 +13,23 @@
|
|
|
12
13
|
showSearch = true,
|
|
13
14
|
onSearchClick,
|
|
14
15
|
children,
|
|
16
|
+
/** Slot for additional actions on the right side of the header */
|
|
17
|
+
rightActions,
|
|
15
18
|
} = $props<{
|
|
16
19
|
showThemeToggle?: boolean;
|
|
17
20
|
showBreadcrumbs?: boolean;
|
|
18
21
|
showSearch?: boolean;
|
|
19
22
|
onSearchClick?: () => void;
|
|
20
23
|
children?: import('svelte').Snippet;
|
|
24
|
+
rightActions?: import('svelte').Snippet;
|
|
21
25
|
}>();
|
|
26
|
+
|
|
27
|
+
// Retrieve optional component overrides from registry
|
|
28
|
+
const registry = getComponentRegistry();
|
|
29
|
+
const CustomBreadcrumbs = registry.Breadcrumbs;
|
|
30
|
+
const CustomThemeToggle = registry.ThemeToggle;
|
|
31
|
+
const CustomUserMenu = registry.UserMenu;
|
|
32
|
+
const CustomNotificationPanel = registry.NotificationPanel;
|
|
22
33
|
</script>
|
|
23
34
|
|
|
24
35
|
<header class="sticky top-0 z-30 flex h-14 w-full shrink-0 items-center justify-between bg-card/60 backdrop-blur-md px-4 md:px-6">
|
|
@@ -27,10 +38,22 @@
|
|
|
27
38
|
{@render children()}
|
|
28
39
|
{/if}
|
|
29
40
|
{#if showBreadcrumbs}
|
|
30
|
-
|
|
41
|
+
{#if CustomBreadcrumbs}
|
|
42
|
+
<CustomBreadcrumbs />
|
|
43
|
+
{:else}
|
|
44
|
+
<Breadcrumbs />
|
|
45
|
+
{/if}
|
|
31
46
|
{/if}
|
|
32
47
|
</div>
|
|
33
48
|
<div class="ml-auto flex items-center gap-2">
|
|
49
|
+
{#if rightActions}
|
|
50
|
+
{@render rightActions()}
|
|
51
|
+
{/if}
|
|
52
|
+
|
|
53
|
+
{#if CustomNotificationPanel}
|
|
54
|
+
<CustomNotificationPanel />
|
|
55
|
+
{/if}
|
|
56
|
+
|
|
34
57
|
{#if showSearch && onSearchClick}
|
|
35
58
|
<Button variant="outline" size="sm" onclick={onSearchClick} class="gap-2 text-muted-foreground h-8 px-3">
|
|
36
59
|
<Search class="h-3.5 w-3.5" />
|
|
@@ -42,13 +65,22 @@
|
|
|
42
65
|
{/if}
|
|
43
66
|
|
|
44
67
|
{#if showThemeToggle}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
68
|
+
{#if CustomThemeToggle}
|
|
69
|
+
<CustomThemeToggle />
|
|
70
|
+
{:else}
|
|
71
|
+
<TooltipButton tooltip={t('common.toggleTheme')} onclick={() => toggleTheme()} class="rounded-full">
|
|
72
|
+
{#if getResolvedTheme() === 'dark'}
|
|
73
|
+
<Moon class="h-4 w-4 transition-all" />
|
|
74
|
+
{:else}
|
|
75
|
+
<Sun class="h-4 w-4 transition-all" />
|
|
76
|
+
{/if}
|
|
77
|
+
</TooltipButton>
|
|
78
|
+
{/if}
|
|
79
|
+
{/if}
|
|
80
|
+
|
|
81
|
+
{#if CustomUserMenu}
|
|
82
|
+
<CustomUserMenu />
|
|
52
83
|
{/if}
|
|
53
84
|
</div>
|
|
54
85
|
</header>
|
|
86
|
+
|
|
@@ -109,36 +109,4 @@
|
|
|
109
109
|
{@html html}
|
|
110
110
|
</div>
|
|
111
111
|
|
|
112
|
-
<style>
|
|
113
|
-
/* Base Markdown Styles */
|
|
114
|
-
:global(.prose) {
|
|
115
|
-
line-height: 1.6;
|
|
116
|
-
}
|
|
117
|
-
:global(.prose p) {
|
|
118
|
-
margin-top: 0.5em;
|
|
119
|
-
margin-bottom: 0.5em;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/* Streaming cursor effect */
|
|
123
|
-
.streaming > :global(*:last-child)::after {
|
|
124
|
-
content: '▋';
|
|
125
|
-
display: inline-block;
|
|
126
|
-
animation: blink 1s step-start infinite;
|
|
127
|
-
margin-left: 2px;
|
|
128
|
-
vertical-align: baseline;
|
|
129
|
-
color: currentColor;
|
|
130
|
-
}
|
|
131
112
|
|
|
132
|
-
@keyframes blink {
|
|
133
|
-
50% { opacity: 0; }
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/* Handle copied state for dynamically injected buttons */
|
|
137
|
-
:global(.copy-btn[data-copied-id]) {
|
|
138
|
-
color: #10b981; /* Tailwind emerald-500 */
|
|
139
|
-
}
|
|
140
|
-
:global(.copy-btn[data-copied-id] svg) {
|
|
141
|
-
/* Swap to Check icon */
|
|
142
|
-
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="%2310b981" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>');
|
|
143
|
-
}
|
|
144
|
-
</style>
|