@svadmin/ui 0.75.0 → 0.77.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 (53) hide show
  1. package/dist/app.css +383 -22
  2. package/dist/app.theme.css +383 -22
  3. package/dist/component-registry.d.ts +300 -0
  4. package/dist/component-registry.js +2 -0
  5. package/dist/components/AppFooter.svelte +137 -0
  6. package/dist/components/AppFooter.svelte.d.ts +25 -0
  7. package/dist/components/CodeEditor.svelte +145 -0
  8. package/dist/components/CodeEditor.svelte.d.ts +26 -0
  9. package/dist/components/HighlightText.svelte +46 -0
  10. package/dist/components/HighlightText.svelte.d.ts +13 -0
  11. package/dist/components/JsonEditor.svelte +182 -0
  12. package/dist/components/JsonEditor.svelte.d.ts +22 -0
  13. package/dist/components/Layout.svelte +38 -5
  14. package/dist/components/Layout.svelte.d.ts +4 -3
  15. package/dist/components/Marquee.svelte +104 -0
  16. package/dist/components/Marquee.svelte.d.ts +20 -0
  17. package/dist/components/MediaPlayer.svelte +104 -0
  18. package/dist/components/MediaPlayer.svelte.d.ts +21 -0
  19. package/dist/components/PhoneInput.svelte +157 -0
  20. package/dist/components/PhoneInput.svelte.d.ts +26 -0
  21. package/dist/components/QRCode.svelte +88 -0
  22. package/dist/components/QRCode.svelte.d.ts +22 -0
  23. package/dist/components/ResizableGrid.svelte +252 -0
  24. package/dist/components/ResizableGrid.svelte.d.ts +34 -0
  25. package/dist/components/Sidebar.svelte +1 -1
  26. package/dist/components/TopProgressBar.svelte +90 -0
  27. package/dist/components/TopProgressBar.svelte.d.ts +15 -0
  28. package/dist/components/browser.svelte.d.ts +65 -0
  29. package/dist/components/browser.svelte.js +228 -0
  30. package/dist/components/charts/AreaChart.svelte +78 -0
  31. package/dist/components/charts/AreaChart.svelte.d.ts +17 -0
  32. package/dist/components/charts/ScatterChart.svelte +82 -0
  33. package/dist/components/charts/ScatterChart.svelte.d.ts +17 -0
  34. package/dist/components/charts/index.d.ts +2 -0
  35. package/dist/components/charts/index.js +2 -0
  36. package/dist/components/code-editor-presets.d.ts +14 -0
  37. package/dist/components/code-editor-presets.js +14 -0
  38. package/dist/components/content/ContentPageHeader.svelte +17 -3
  39. package/dist/components/content/ContentPageHeader.svelte.d.ts +1 -0
  40. package/dist/components/content/ContentPageShell.svelte +12 -3
  41. package/dist/components/content/ContentPageShell.svelte.d.ts +1 -0
  42. package/dist/components/content/MetricBlock.svelte +1 -1
  43. package/dist/components/highlight.d.ts +16 -0
  44. package/dist/components/highlight.js +68 -0
  45. package/dist/components/media.svelte.d.ts +27 -0
  46. package/dist/components/media.svelte.js +60 -0
  47. package/dist/components/phone-dial-codes.d.ts +19 -0
  48. package/dist/components/phone-dial-codes.js +112 -0
  49. package/dist/index.d.ts +19 -1
  50. package/dist/index.js +14 -1
  51. package/dist/recipes.d.ts +77 -38
  52. package/dist/recipes.js +33 -8
  53. package/package.json +64 -4
@@ -22,6 +22,7 @@ const registryEntries = [
22
22
  description: { kind: "string" },
23
23
  pageId: { kind: "string" },
24
24
  width: variant("contentPageRecipe", "width"),
25
+ density: variant("contentPageRecipe", "density"),
25
26
  class: { kind: "string" },
26
27
  },
27
28
  slots: { children: { kind: "snippet", required: true }, actions: { kind: "snippet" } },
@@ -37,6 +38,7 @@ const registryEntries = [
37
38
  eyebrow: { kind: "string" },
38
39
  description: { kind: "string" },
39
40
  breadcrumbs: { kind: "string[]" },
41
+ density: variant("contentHeaderRecipe", "density"),
40
42
  class: { kind: "string" },
41
43
  },
42
44
  slots: { actions: { kind: "snippet" } },
@@ -0,0 +1,137 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { cn } from '../utils.js';
4
+
5
+ export interface FooterLink {
6
+ label: string;
7
+ href?: string;
8
+ external?: boolean;
9
+ onclick?: () => void;
10
+ }
11
+
12
+ export interface FooterColumn {
13
+ title: string;
14
+ links: FooterLink[];
15
+ }
16
+
17
+ interface Props {
18
+ /** Bottom-line copyright text. */
19
+ copyright?: string;
20
+ /** Flat links rendered in the bottom bar. */
21
+ links?: FooterLink[];
22
+ /** Link columns rendered above the bottom bar. */
23
+ columns?: FooterColumn[];
24
+ ariaLabel?: string;
25
+ class?: string;
26
+ children?: Snippet;
27
+ }
28
+
29
+ let {
30
+ copyright,
31
+ links = [],
32
+ columns = [],
33
+ ariaLabel = 'Footer',
34
+ class: className = '',
35
+ children,
36
+ }: Props = $props();
37
+ </script>
38
+
39
+ <footer class={cn('svadmin-app-footer', className)} data-slot="app-footer" aria-label={ariaLabel}>
40
+ {#if columns.length}
41
+ <div class="svadmin-app-footer__columns">
42
+ {#each columns as column (column.title)}
43
+ <nav class="svadmin-app-footer__column" aria-label={column.title}>
44
+ <h2 class="svadmin-app-footer__heading">{column.title}</h2>
45
+ <ul class="svadmin-app-footer__list">
46
+ {#each column.links as link (link.label)}
47
+ <li>
48
+ {#if link.href}
49
+ <a
50
+ class="svadmin-app-footer__link"
51
+ href={link.href}
52
+ rel={link.external ? 'noreferrer noopener' : undefined}
53
+ target={link.external ? '_blank' : undefined}
54
+ >{link.label}</a>
55
+ {:else}
56
+ <button class="svadmin-app-footer__link" type="button" onclick={() => link.onclick?.()}>{link.label}</button>
57
+ {/if}
58
+ </li>
59
+ {/each}
60
+ </ul>
61
+ </nav>
62
+ {/each}
63
+ </div>
64
+ {/if}
65
+
66
+ {#if children}<div class="svadmin-app-footer__content">{@render children()}</div>{/if}
67
+
68
+ {#if copyright || links.length}
69
+ <div class="svadmin-app-footer__bar">
70
+ {#if copyright}<span class="svadmin-app-footer__copyright">{copyright}</span>{/if}
71
+ {#if links.length}
72
+ <ul class="svadmin-app-footer__list svadmin-app-footer__list--inline">
73
+ {#each links as link (link.label)}
74
+ <li>
75
+ {#if link.href}
76
+ <a
77
+ class="svadmin-app-footer__link"
78
+ href={link.href}
79
+ rel={link.external ? 'noreferrer noopener' : undefined}
80
+ target={link.external ? '_blank' : undefined}
81
+ >{link.label}</a>
82
+ {:else}
83
+ <button class="svadmin-app-footer__link" type="button" onclick={() => link.onclick?.()}>{link.label}</button>
84
+ {/if}
85
+ </li>
86
+ {/each}
87
+ </ul>
88
+ {/if}
89
+ </div>
90
+ {/if}
91
+ </footer>
92
+
93
+ <style>
94
+ .svadmin-app-footer {
95
+ display: grid;
96
+ gap: 1.5rem;
97
+ border-block-start: 1px solid var(--border, currentColor);
98
+ padding: 1.5rem;
99
+ background: var(--card, var(--background, transparent));
100
+ color: var(--muted-foreground, currentColor);
101
+ font-size: 0.8125rem;
102
+ }
103
+
104
+ .svadmin-app-footer__columns {
105
+ display: grid;
106
+ grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
107
+ gap: 1.5rem;
108
+ }
109
+
110
+ .svadmin-app-footer__column { display: grid; gap: 0.625rem; }
111
+ .svadmin-app-footer__heading { margin: 0; color: var(--foreground, currentColor); font-size: 0.8125rem; font-weight: 600; }
112
+ .svadmin-app-footer__list { display: grid; gap: 0.375rem; margin: 0; padding: 0; list-style: none; }
113
+ .svadmin-app-footer__list--inline { display: flex; flex-wrap: wrap; gap: 1rem; }
114
+
115
+ .svadmin-app-footer__link {
116
+ border: 0;
117
+ padding: 0;
118
+ background: transparent;
119
+ color: inherit;
120
+ font: inherit;
121
+ text-decoration: none;
122
+ cursor: pointer;
123
+ }
124
+ .svadmin-app-footer__link:hover { color: var(--foreground, currentColor); text-decoration: underline; text-underline-offset: 0.2em; }
125
+ .svadmin-app-footer__link:focus-visible { outline: 2px solid var(--ring, currentColor); outline-offset: 2px; }
126
+
127
+ .svadmin-app-footer__bar {
128
+ display: flex;
129
+ flex-wrap: wrap;
130
+ align-items: center;
131
+ justify-content: space-between;
132
+ gap: 0.75rem;
133
+ border-block-start: 1px solid var(--border, currentColor);
134
+ padding-block-start: 1rem;
135
+ color: var(--muted-foreground, currentColor);
136
+ }
137
+ </style>
@@ -0,0 +1,25 @@
1
+ import type { Snippet } from 'svelte';
2
+ export interface FooterLink {
3
+ label: string;
4
+ href?: string;
5
+ external?: boolean;
6
+ onclick?: () => void;
7
+ }
8
+ export interface FooterColumn {
9
+ title: string;
10
+ links: FooterLink[];
11
+ }
12
+ interface Props {
13
+ /** Bottom-line copyright text. */
14
+ copyright?: string;
15
+ /** Flat links rendered in the bottom bar. */
16
+ links?: FooterLink[];
17
+ /** Link columns rendered above the bottom bar. */
18
+ columns?: FooterColumn[];
19
+ ariaLabel?: string;
20
+ class?: string;
21
+ children?: Snippet;
22
+ }
23
+ declare const AppFooter: import("svelte").Component<Props, {}, "">;
24
+ type AppFooter = ReturnType<typeof AppFooter>;
25
+ export default AppFooter;
@@ -0,0 +1,145 @@
1
+ <script lang="ts">
2
+ import type { Component } from 'svelte';
3
+ import { onMount } from 'svelte';
4
+ import { cn } from '../utils.js';
5
+
6
+ /**
7
+ * Thin adapter over the Svelte ecosystem `svelte-codemirror-editor` package.
8
+ * The dependency is optional: if it cannot be loaded, an accessible
9
+ * `<textarea>` fallback keeps the field usable.
10
+ */
11
+ interface Props {
12
+ value?: string;
13
+ placeholder?: string;
14
+ readonly?: boolean;
15
+ editable?: boolean;
16
+ lineNumbers?: boolean;
17
+ lineWrapping?: boolean;
18
+ tabSize?: number;
19
+ /** CodeMirror extensions supplied by the host (languages, themes, …). */
20
+ extensions?: readonly unknown[];
21
+ lang?: unknown;
22
+ theme?: unknown;
23
+ ariaLabel?: string;
24
+ class?: string;
25
+ onchange?: (value: string) => void;
26
+ onready?: (view: unknown) => void;
27
+ }
28
+
29
+ let {
30
+ value = $bindable(''),
31
+ placeholder = 'Write code…',
32
+ readonly = false,
33
+ editable = true,
34
+ lineNumbers = true,
35
+ lineWrapping = true,
36
+ tabSize = 2,
37
+ extensions,
38
+ lang,
39
+ theme,
40
+ ariaLabel = 'Code editor',
41
+ class: className = '',
42
+ onchange,
43
+ onready,
44
+ }: Props = $props();
45
+
46
+ let Editor = $state<Component<Record<string, unknown>> | null>(null);
47
+ let unavailable = $state(false);
48
+
49
+ onMount(() => {
50
+ let active = true;
51
+ import('svelte-codemirror-editor')
52
+ .then((module) => {
53
+ if (active) Editor = module.default as unknown as Component<Record<string, unknown>>;
54
+ })
55
+ .catch(() => {
56
+ if (active) unavailable = true;
57
+ });
58
+ return () => {
59
+ active = false;
60
+ };
61
+ });
62
+
63
+ const editorProps = $derived({
64
+ value,
65
+ placeholder,
66
+ readonly,
67
+ editable,
68
+ lineNumbers,
69
+ lineWrapping,
70
+ tabSize,
71
+ ...(extensions ? { extensions: [...extensions] } : {}),
72
+ ...(lang ? { lang } : {}),
73
+ ...(theme ? { theme } : {}),
74
+ ...(onready ? { onready } : {}),
75
+ onchange: (next: string) => {
76
+ value = next;
77
+ onchange?.(next);
78
+ },
79
+ });
80
+
81
+ const editorState = $derived(Editor ? 'ready' : unavailable ? 'unavailable' : 'loading');
82
+ </script>
83
+
84
+ <div
85
+ class={cn('svadmin-code-editor', className)}
86
+ data-slot="code-editor"
87
+ data-state={editorState}
88
+ >
89
+ {#if Editor}
90
+ <Editor {...editorProps} />
91
+ {:else}
92
+ <textarea
93
+ class="svadmin-code-editor__fallback"
94
+ bind:value
95
+ {placeholder}
96
+ {readonly}
97
+ disabled={!editable || readonly}
98
+ aria-label={ariaLabel}
99
+ spellcheck="false"
100
+ autocapitalize="off"
101
+ autocomplete="off"
102
+ wrap={lineWrapping ? 'soft' : 'off'}
103
+ oninput={(event) => onchange?.((event.currentTarget as HTMLTextAreaElement).value)}
104
+ ></textarea>
105
+ {#if unavailable}
106
+ <p class="svadmin-code-editor__hint" role="status">
107
+ Install <code>svelte-codemirror-editor</code> and <code>codemirror</code> to enable syntax highlighting.
108
+ </p>
109
+ {/if}
110
+ {/if}
111
+ </div>
112
+
113
+ <style>
114
+ .svadmin-code-editor {
115
+ overflow: hidden;
116
+ border: 1px solid var(--border, currentColor);
117
+ border-radius: min(var(--radius, 0.5rem), 0.5rem);
118
+ background: var(--card, var(--background, transparent));
119
+ color: var(--card-foreground, var(--foreground, currentColor));
120
+ }
121
+
122
+ .svadmin-code-editor:focus-within { outline: 2px solid var(--ring, currentColor); outline-offset: 1px; }
123
+ .svadmin-code-editor__fallback {
124
+ display: block;
125
+ width: 100%;
126
+ min-height: 8rem;
127
+ border: 0;
128
+ padding: 0.75rem;
129
+ background: transparent;
130
+ color: inherit;
131
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
132
+ font-size: 0.8125rem;
133
+ line-height: 1.6;
134
+ resize: vertical;
135
+ }
136
+ .svadmin-code-editor__fallback:focus { outline: none; }
137
+ .svadmin-code-editor__hint {
138
+ margin: 0;
139
+ border-top: 1px solid var(--border, currentColor);
140
+ padding: 0.5rem 0.75rem;
141
+ color: var(--muted-foreground, currentColor);
142
+ font-size: 0.75rem;
143
+ }
144
+ .svadmin-code-editor__hint code { color: var(--foreground, currentColor); }
145
+ </style>
@@ -0,0 +1,26 @@
1
+ import type { Component } from 'svelte';
2
+ /**
3
+ * Thin adapter over the Svelte ecosystem `svelte-codemirror-editor` package.
4
+ * The dependency is optional: if it cannot be loaded, an accessible
5
+ * `<textarea>` fallback keeps the field usable.
6
+ */
7
+ interface Props {
8
+ value?: string;
9
+ placeholder?: string;
10
+ readonly?: boolean;
11
+ editable?: boolean;
12
+ lineNumbers?: boolean;
13
+ lineWrapping?: boolean;
14
+ tabSize?: number;
15
+ /** CodeMirror extensions supplied by the host (languages, themes, …). */
16
+ extensions?: readonly unknown[];
17
+ lang?: unknown;
18
+ theme?: unknown;
19
+ ariaLabel?: string;
20
+ class?: string;
21
+ onchange?: (value: string) => void;
22
+ onready?: (view: unknown) => void;
23
+ }
24
+ declare const CodeEditor: Component<Props, {}, "value">;
25
+ type CodeEditor = ReturnType<typeof CodeEditor>;
26
+ export default CodeEditor;
@@ -0,0 +1,46 @@
1
+ <script lang="ts">
2
+ import { cn } from '../utils.js';
3
+ import { splitHighlights } from './highlight.js';
4
+
5
+ interface Props {
6
+ text: string;
7
+ /** One term or a list of terms to highlight. */
8
+ query?: string | readonly string[];
9
+ caseSensitive?: boolean;
10
+ wholeWord?: boolean;
11
+ class?: string;
12
+ /** Overrides the `<mark>` styling without leaving the semantic token system. */
13
+ highlightClass?: string;
14
+ }
15
+
16
+ let {
17
+ text,
18
+ query,
19
+ caseSensitive = false,
20
+ wholeWord = false,
21
+ class: className = '',
22
+ highlightClass = '',
23
+ }: Props = $props();
24
+
25
+ const segments = $derived(
26
+ splitHighlights(text, query, { caseSensitive, wholeWord }),
27
+ );
28
+ </script>
29
+
30
+ <span class={cn('svadmin-highlight-text', className)} data-slot="highlight-text">
31
+ {#each segments as segment, index (index)}{#if segment.match}<mark class={cn('svadmin-highlight-text__mark', highlightClass)}>{segment.text}</mark>{:else}{segment.text}{/if}{/each}
32
+ </span>
33
+
34
+ <style>
35
+ .svadmin-highlight-text {
36
+ color: inherit;
37
+ overflow-wrap: anywhere;
38
+ }
39
+
40
+ .svadmin-highlight-text__mark {
41
+ border-radius: 0.2rem;
42
+ padding-inline: 0.1rem;
43
+ background: color-mix(in oklch, var(--warning, var(--primary, currentColor)) 28%, transparent);
44
+ color: var(--foreground, currentColor);
45
+ }
46
+ </style>
@@ -0,0 +1,13 @@
1
+ interface Props {
2
+ text: string;
3
+ /** One term or a list of terms to highlight. */
4
+ query?: string | readonly string[];
5
+ caseSensitive?: boolean;
6
+ wholeWord?: boolean;
7
+ class?: string;
8
+ /** Overrides the `<mark>` styling without leaving the semantic token system. */
9
+ highlightClass?: string;
10
+ }
11
+ declare const HighlightText: import("svelte").Component<Props, {}, "">;
12
+ type HighlightText = ReturnType<typeof HighlightText>;
13
+ export default HighlightText;
@@ -0,0 +1,182 @@
1
+ <script lang="ts">
2
+ import type { Component } from 'svelte';
3
+ import { onMount } from 'svelte';
4
+ import { cn } from '../utils.js';
5
+
6
+ /**
7
+ * Thin adapter over the Svelte ecosystem `svelte-jsoneditor` package. The
8
+ * dependency is optional: if it cannot be loaded, a validating `<textarea>`
9
+ * fallback keeps the field usable.
10
+ */
11
+ type JsonMode = 'tree' | 'text' | 'table';
12
+
13
+ interface Props {
14
+ /** Parsed JSON value. */
15
+ value?: unknown;
16
+ mode?: JsonMode;
17
+ readonly?: boolean;
18
+ /** Height of the editor surface (CSS length). */
19
+ height?: string;
20
+ ariaLabel?: string;
21
+ class?: string;
22
+ onchange?: (value: unknown) => void;
23
+ onerror?: (message: string) => void;
24
+ }
25
+
26
+ let {
27
+ value = $bindable(undefined),
28
+ mode = 'tree',
29
+ readonly = false,
30
+ height = '22rem',
31
+ ariaLabel = 'JSON editor',
32
+ class: className = '',
33
+ onchange,
34
+ onerror,
35
+ }: Props = $props();
36
+
37
+ const generatedId = $props.id();
38
+ const errorId = `${generatedId}-error`;
39
+
40
+ let Editor = $state<Component<Record<string, unknown>> | null>(null);
41
+ let unavailable = $state(false);
42
+ let error = $state('');
43
+
44
+ function format(input: unknown): string {
45
+ if (input === undefined) return '';
46
+ try {
47
+ return JSON.stringify(input, null, 2) ?? '';
48
+ } catch {
49
+ return '';
50
+ }
51
+ }
52
+
53
+ let text = $state(format(value));
54
+
55
+ onMount(() => {
56
+ let active = true;
57
+ import('svelte-jsoneditor')
58
+ .then((module) => {
59
+ if (active) Editor = module.JSONEditor as unknown as Component<Record<string, unknown>>;
60
+ })
61
+ .catch(() => {
62
+ if (active) unavailable = true;
63
+ });
64
+ return () => {
65
+ active = false;
66
+ };
67
+ });
68
+
69
+ function handleEditorChange(updated: { content?: { json?: unknown; text?: string } }): void {
70
+ const content = updated?.content ?? {};
71
+ const next = 'json' in content ? content.json : content.text;
72
+ value = next;
73
+ error = '';
74
+ onchange?.(next);
75
+ }
76
+
77
+ function handleFallbackInput(next: string): void {
78
+ text = next;
79
+ if (next.trim() === '') {
80
+ error = '';
81
+ value = undefined;
82
+ onchange?.(undefined);
83
+ return;
84
+ }
85
+ try {
86
+ const parsed: unknown = JSON.parse(next);
87
+ error = '';
88
+ value = parsed;
89
+ onchange?.(parsed);
90
+ } catch (cause) {
91
+ const message = cause instanceof Error ? cause.message : 'Invalid JSON';
92
+ error = message;
93
+ onerror?.(message);
94
+ }
95
+ }
96
+
97
+ const editorProps = $derived({
98
+ content: { json: value },
99
+ mode,
100
+ readOnly: readonly,
101
+ ariaLabel,
102
+ mainMenuBar: !readonly,
103
+ navigationBar: !readonly,
104
+ statusBar: true,
105
+ onChange: handleEditorChange,
106
+ });
107
+
108
+ const editorState = $derived(Editor ? 'ready' : unavailable ? 'unavailable' : 'loading');
109
+ </script>
110
+
111
+ <div
112
+ class={cn('svadmin-json-editor', className)}
113
+ data-slot="json-editor"
114
+ data-state={editorState}
115
+ >
116
+ {#if Editor}
117
+ <div class="svadmin-json-editor__surface" style={`height: ${height};`}>
118
+ <Editor {...editorProps} />
119
+ </div>
120
+ {:else}
121
+ <textarea
122
+ class="svadmin-json-editor__fallback"
123
+ style={`min-height: ${height};`}
124
+ bind:value={text}
125
+ {readonly}
126
+ aria-label={ariaLabel}
127
+ spellcheck="false"
128
+ autocapitalize="off"
129
+ autocomplete="off"
130
+ aria-invalid={error ? 'true' : undefined}
131
+ aria-describedby={error ? errorId : undefined}
132
+ oninput={(event) => handleFallbackInput((event.currentTarget as HTMLTextAreaElement).value)}
133
+ ></textarea>
134
+ {#if error}<p id={errorId} class="svadmin-json-editor__error" role="alert">{error}</p>{/if}
135
+ {#if unavailable}
136
+ <p class="svadmin-json-editor__hint" role="status">
137
+ Install <code>svelte-jsoneditor</code> to enable the structured editor.
138
+ </p>
139
+ {/if}
140
+ {/if}
141
+ </div>
142
+
143
+ <style>
144
+ .svadmin-json-editor {
145
+ overflow: hidden;
146
+ border: 1px solid var(--border, currentColor);
147
+ border-radius: min(var(--radius, 0.5rem), 0.5rem);
148
+ background: var(--card, var(--background, transparent));
149
+ color: var(--card-foreground, var(--foreground, currentColor));
150
+ }
151
+
152
+ .svadmin-json-editor:focus-within { outline: 2px solid var(--ring, currentColor); outline-offset: 1px; }
153
+ .svadmin-json-editor__surface { width: 100%; }
154
+ .svadmin-json-editor__fallback {
155
+ display: block;
156
+ width: 100%;
157
+ border: 0;
158
+ padding: 0.75rem;
159
+ background: transparent;
160
+ color: inherit;
161
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
162
+ font-size: 0.8125rem;
163
+ line-height: 1.6;
164
+ resize: vertical;
165
+ }
166
+ .svadmin-json-editor__fallback:focus { outline: none; }
167
+ .svadmin-json-editor__error {
168
+ margin: 0;
169
+ border-top: 1px solid var(--destructive, currentColor);
170
+ padding: 0.5rem 0.75rem;
171
+ color: var(--destructive, currentColor);
172
+ font-size: 0.75rem;
173
+ }
174
+ .svadmin-json-editor__hint {
175
+ margin: 0;
176
+ border-top: 1px solid var(--border, currentColor);
177
+ padding: 0.5rem 0.75rem;
178
+ color: var(--muted-foreground, currentColor);
179
+ font-size: 0.75rem;
180
+ }
181
+ .svadmin-json-editor__hint code { color: var(--foreground, currentColor); }
182
+ </style>
@@ -0,0 +1,22 @@
1
+ import type { Component } from 'svelte';
2
+ /**
3
+ * Thin adapter over the Svelte ecosystem `svelte-jsoneditor` package. The
4
+ * dependency is optional: if it cannot be loaded, a validating `<textarea>`
5
+ * fallback keeps the field usable.
6
+ */
7
+ type JsonMode = 'tree' | 'text' | 'table';
8
+ interface Props {
9
+ /** Parsed JSON value. */
10
+ value?: unknown;
11
+ mode?: JsonMode;
12
+ readonly?: boolean;
13
+ /** Height of the editor surface (CSS length). */
14
+ height?: string;
15
+ ariaLabel?: string;
16
+ class?: string;
17
+ onchange?: (value: unknown) => void;
18
+ onerror?: (message: string) => void;
19
+ }
20
+ declare const JsonEditor: Component<Props, {}, "value">;
21
+ type JsonEditor = ReturnType<typeof JsonEditor>;
22
+ export default JsonEditor;