svelora 2.1.1 → 3.0.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/README.md CHANGED
@@ -16,8 +16,8 @@
16
16
  </p>
17
17
 
18
18
  <p align="center">
19
- <a href="https://svelora.vercel.app"><strong>Live Demo</strong></a> &middot;
20
- <a href="https://svelora.vercel.app/getting-started"><strong>Getting Started</strong></a> &middot;
19
+ <a href="https://svelora-ui.vercel.app/"><strong>Live Demo</strong></a> &middot;
20
+ <a href="https://svelora-ui.vercel.app//getting-started"><strong>Getting Started</strong></a> &middot;
21
21
  <a href="https://github.com/asphum/svelora/blob/main/CHANGELOG.md"><strong>Changelog</strong></a>
22
22
  </p>
23
23
 
@@ -131,15 +131,14 @@ Release checklist: [RELEASE_CHECKLIST.md](./RELEASE_CHECKLIST.md)
131
131
  bun run release:verify
132
132
  ```
133
133
 
134
- One command release:
134
+ One command publish:
135
135
 
136
136
  ```bash
137
- bun run release:patch
137
+ bun run public
138
138
  ```
139
139
 
140
- The release command generates `CHANGELOG.md` automatically from unreleased git commits.
141
- It groups commit messages into `Added`, `Changed`, and `Fixed`, writes the next version section, and resets `## [Unreleased]` back to the template automatically.
142
- If you still have local changes that are not committed yet, pass a work commit message and the script will create that commit before publishing.
140
+ The publish command generates `CHANGELOG.md` automatically from unreleased git commits.
141
+ It groups commit messages into `Added`, `Changed`, and `Fixed`, writes the next version section, resets `## [Unreleased]` back to the template automatically, commits local changes if needed, publishes to npm, tags the release, and pushes everything.
143
142
 
144
143
  Refresh the template manually if needed:
145
144
 
@@ -147,29 +146,28 @@ Refresh the template manually if needed:
147
146
  bun run changelog:template
148
147
  ```
149
148
 
150
- Release with a work commit message for uncommitted changes:
151
-
152
- ```bash
153
- bun run release:patch -- "feat(button): improve loading state"
154
- ```
155
-
156
149
  Other version bumps:
157
150
 
158
151
  ```bash
159
- bun run release:minor
160
- # bun run release:major
152
+ bun run public:minor
153
+ # bun run public:major
161
154
  ```
162
155
 
163
156
  Full verification before publish:
164
157
 
165
158
  ```bash
166
- bun run release:patch:full
159
+ bun run public:full
167
160
  ```
168
161
 
169
162
  Dry run:
170
163
 
171
164
  ```bash
172
- bun run release:patch:dry
165
+ bun run public:dry
166
+ ```
167
+
168
+ ```bash
169
+ bun run public:minor:dry
170
+ # bun run public:major:dry
173
171
  ```
174
172
 
175
173
  ```bash
@@ -0,0 +1,83 @@
1
+ <script lang="ts" module>
2
+ import type { CodeBlockProps } from './code-block.types.js'
3
+
4
+ export type Props = CodeBlockProps
5
+ </script>
6
+
7
+ <script lang="ts">
8
+ import { getComponentConfig } from '../config.js'
9
+ import { codeBlockDefaults, codeBlockVariants } from './code-block.variants.js'
10
+
11
+ const config = getComponentConfig('codeBlock', codeBlockDefaults)
12
+
13
+ let {
14
+ title = 'Code',
15
+ code = '',
16
+ copyText,
17
+ copyable = true,
18
+ html,
19
+ variant = config.defaultVariants.variant,
20
+ size = config.defaultVariants.size,
21
+ class: className,
22
+ ui,
23
+ children
24
+ }: Props = $props()
25
+
26
+ let copyLabel = $state('Copy')
27
+
28
+ const resolvedCopyText = $derived((copyText ?? code).trim())
29
+ const canCopy = $derived(copyable && resolvedCopyText.length > 0)
30
+
31
+ const classes = $derived.by(() => {
32
+ const slots = codeBlockVariants({ variant, size })
33
+ return {
34
+ root: slots.root({ class: [config.slots.root, className, ui?.root] }),
35
+ header: slots.header({ class: [config.slots.header, ui?.header] }),
36
+ title: slots.title({ class: [config.slots.title, ui?.title] }),
37
+ button: slots.button({ class: [config.slots.button, ui?.button] }),
38
+ body: slots.body({ class: [config.slots.body, ui?.body] })
39
+ }
40
+ })
41
+
42
+ function scheduleLabelReset(resetLabel: string): void {
43
+ window.setTimeout(() => {
44
+ copyLabel = resetLabel
45
+ }, 1200)
46
+ }
47
+
48
+ async function handleCopy(): Promise<void> {
49
+ if (!canCopy) return
50
+ if (typeof navigator === 'undefined' || !navigator.clipboard) return
51
+
52
+ const resetLabel = copyLabel
53
+ try {
54
+ await navigator.clipboard.writeText(resolvedCopyText)
55
+ copyLabel = 'Copied'
56
+ } catch {
57
+ copyLabel = 'Failed'
58
+ }
59
+
60
+ scheduleLabelReset(resetLabel)
61
+ }
62
+ </script>
63
+
64
+ <div class={classes.root}>
65
+ <div class={classes.header}>
66
+ <p class={classes.title}>{title}</p>
67
+ <button type="button" class={classes.button} disabled={!canCopy} onclick={handleCopy}>
68
+ {copyLabel}
69
+ </button>
70
+ </div>
71
+
72
+ <div class={classes.body}>
73
+ {#if html}
74
+ {@html html}
75
+ {:else if children}
76
+ {@render children()}
77
+ {:else}
78
+ <pre class="shiki">
79
+ <code>{code}</code>
80
+ </pre>
81
+ {/if}
82
+ </div>
83
+ </div>
@@ -0,0 +1,5 @@
1
+ import type { CodeBlockProps } from './code-block.types.js';
2
+ export type Props = CodeBlockProps;
3
+ declare const CodeBlock: import("svelte").Component<CodeBlockProps, {}, "">;
4
+ type CodeBlock = ReturnType<typeof CodeBlock>;
5
+ export default CodeBlock;
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ClassNameValue } from 'tailwind-merge';
3
+ import type { CodeBlockVariantProps, CodeBlockSlots } from './code-block.variants.js';
4
+ export type CodeBlockProps = {
5
+ title?: string;
6
+ code?: string;
7
+ copyText?: string;
8
+ copyable?: boolean;
9
+ html?: string;
10
+ variant?: NonNullable<CodeBlockVariantProps['variant']>;
11
+ size?: NonNullable<CodeBlockVariantProps['size']>;
12
+ class?: ClassNameValue;
13
+ ui?: Partial<Record<CodeBlockSlots, ClassNameValue>>;
14
+ children?: Snippet;
15
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,288 @@
1
+ import { type VariantProps } from 'tailwind-variants';
2
+ export declare const codeBlockVariants: import("tailwind-variants").TVReturnType<{
3
+ variant: {
4
+ outline: {
5
+ root: string;
6
+ header: string;
7
+ button: string;
8
+ };
9
+ soft: {
10
+ root: string;
11
+ header: string;
12
+ button: string;
13
+ };
14
+ subtle: {
15
+ root: string;
16
+ header: string;
17
+ button: string;
18
+ };
19
+ solid: {
20
+ root: string;
21
+ header: string;
22
+ button: string;
23
+ };
24
+ ghost: {
25
+ root: string;
26
+ header: string;
27
+ button: string;
28
+ };
29
+ none: {
30
+ root: string;
31
+ header: string;
32
+ button: string;
33
+ };
34
+ };
35
+ size: {
36
+ sm: {
37
+ header: string;
38
+ button: string;
39
+ body: string;
40
+ };
41
+ md: {
42
+ header: string;
43
+ button: string;
44
+ body: string;
45
+ };
46
+ lg: {
47
+ header: string;
48
+ button: string;
49
+ body: string;
50
+ };
51
+ };
52
+ }, {
53
+ root: string;
54
+ header: string;
55
+ title: string;
56
+ button: string;
57
+ body: string;
58
+ }, undefined, {
59
+ variant: {
60
+ outline: {
61
+ root: string;
62
+ header: string;
63
+ button: string;
64
+ };
65
+ soft: {
66
+ root: string;
67
+ header: string;
68
+ button: string;
69
+ };
70
+ subtle: {
71
+ root: string;
72
+ header: string;
73
+ button: string;
74
+ };
75
+ solid: {
76
+ root: string;
77
+ header: string;
78
+ button: string;
79
+ };
80
+ ghost: {
81
+ root: string;
82
+ header: string;
83
+ button: string;
84
+ };
85
+ none: {
86
+ root: string;
87
+ header: string;
88
+ button: string;
89
+ };
90
+ };
91
+ size: {
92
+ sm: {
93
+ header: string;
94
+ button: string;
95
+ body: string;
96
+ };
97
+ md: {
98
+ header: string;
99
+ button: string;
100
+ body: string;
101
+ };
102
+ lg: {
103
+ header: string;
104
+ button: string;
105
+ body: string;
106
+ };
107
+ };
108
+ }, {
109
+ root: string;
110
+ header: string;
111
+ title: string;
112
+ button: string;
113
+ body: string;
114
+ }, import("tailwind-variants").TVReturnType<{
115
+ variant: {
116
+ outline: {
117
+ root: string;
118
+ header: string;
119
+ button: string;
120
+ };
121
+ soft: {
122
+ root: string;
123
+ header: string;
124
+ button: string;
125
+ };
126
+ subtle: {
127
+ root: string;
128
+ header: string;
129
+ button: string;
130
+ };
131
+ solid: {
132
+ root: string;
133
+ header: string;
134
+ button: string;
135
+ };
136
+ ghost: {
137
+ root: string;
138
+ header: string;
139
+ button: string;
140
+ };
141
+ none: {
142
+ root: string;
143
+ header: string;
144
+ button: string;
145
+ };
146
+ };
147
+ size: {
148
+ sm: {
149
+ header: string;
150
+ button: string;
151
+ body: string;
152
+ };
153
+ md: {
154
+ header: string;
155
+ button: string;
156
+ body: string;
157
+ };
158
+ lg: {
159
+ header: string;
160
+ button: string;
161
+ body: string;
162
+ };
163
+ };
164
+ }, {
165
+ root: string;
166
+ header: string;
167
+ title: string;
168
+ button: string;
169
+ body: string;
170
+ }, undefined, unknown, unknown, undefined>>;
171
+ export type CodeBlockVariantProps = VariantProps<typeof codeBlockVariants>;
172
+ export type CodeBlockSlots = keyof ReturnType<typeof codeBlockVariants>;
173
+ export declare const codeBlockDefaults: {
174
+ defaultVariants: import("tailwind-variants").TVDefaultVariants<{
175
+ variant: {
176
+ outline: {
177
+ root: string;
178
+ header: string;
179
+ button: string;
180
+ };
181
+ soft: {
182
+ root: string;
183
+ header: string;
184
+ button: string;
185
+ };
186
+ subtle: {
187
+ root: string;
188
+ header: string;
189
+ button: string;
190
+ };
191
+ solid: {
192
+ root: string;
193
+ header: string;
194
+ button: string;
195
+ };
196
+ ghost: {
197
+ root: string;
198
+ header: string;
199
+ button: string;
200
+ };
201
+ none: {
202
+ root: string;
203
+ header: string;
204
+ button: string;
205
+ };
206
+ };
207
+ size: {
208
+ sm: {
209
+ header: string;
210
+ button: string;
211
+ body: string;
212
+ };
213
+ md: {
214
+ header: string;
215
+ button: string;
216
+ body: string;
217
+ };
218
+ lg: {
219
+ header: string;
220
+ button: string;
221
+ body: string;
222
+ };
223
+ };
224
+ }, {
225
+ root: string;
226
+ header: string;
227
+ title: string;
228
+ button: string;
229
+ body: string;
230
+ }, {
231
+ variant: {
232
+ outline: {
233
+ root: string;
234
+ header: string;
235
+ button: string;
236
+ };
237
+ soft: {
238
+ root: string;
239
+ header: string;
240
+ button: string;
241
+ };
242
+ subtle: {
243
+ root: string;
244
+ header: string;
245
+ button: string;
246
+ };
247
+ solid: {
248
+ root: string;
249
+ header: string;
250
+ button: string;
251
+ };
252
+ ghost: {
253
+ root: string;
254
+ header: string;
255
+ button: string;
256
+ };
257
+ none: {
258
+ root: string;
259
+ header: string;
260
+ button: string;
261
+ };
262
+ };
263
+ size: {
264
+ sm: {
265
+ header: string;
266
+ button: string;
267
+ body: string;
268
+ };
269
+ md: {
270
+ header: string;
271
+ button: string;
272
+ body: string;
273
+ };
274
+ lg: {
275
+ header: string;
276
+ button: string;
277
+ body: string;
278
+ };
279
+ };
280
+ }, {
281
+ root: string;
282
+ header: string;
283
+ title: string;
284
+ button: string;
285
+ body: string;
286
+ }>;
287
+ slots: Partial<Record<CodeBlockSlots, string>>;
288
+ };
@@ -0,0 +1,69 @@
1
+ import { tv } from 'tailwind-variants';
2
+ export const codeBlockVariants = tv({
3
+ slots: {
4
+ root: 'mt-4 overflow-hidden rounded-2xl border border-outline-variant bg-surface-container',
5
+ header: 'flex items-center justify-between border-b border-outline-variant',
6
+ title: 'text-sm font-medium text-on-surface-variant',
7
+ button: 'rounded-md border border-outline-variant text-xs font-medium text-on-surface-variant transition-colors hover:bg-surface-container-highest disabled:cursor-not-allowed disabled:opacity-50',
8
+ body: 'bg-surface-container-highest [&_.shiki]:rounded-none [&_.shiki]:overflow-x-auto [&_.shiki]:m-0'
9
+ },
10
+ variants: {
11
+ variant: {
12
+ outline: {
13
+ root: 'border border-outline-variant bg-surface-container',
14
+ header: 'border-outline-variant',
15
+ button: 'border-outline-variant'
16
+ },
17
+ soft: {
18
+ root: 'border border-outline-variant/60 bg-surface-container/40',
19
+ header: 'border-outline-variant/60',
20
+ button: 'border-outline-variant/60'
21
+ },
22
+ subtle: {
23
+ root: 'border border-outline-variant/40 bg-surface-container/20',
24
+ header: 'border-outline-variant/40',
25
+ button: 'border-outline-variant/40'
26
+ },
27
+ solid: {
28
+ root: 'border border-outline-variant/60 bg-surface-container-high',
29
+ header: 'border-outline-variant/60',
30
+ button: 'border-outline-variant/60'
31
+ },
32
+ ghost: {
33
+ root: 'border border-transparent bg-transparent',
34
+ header: 'border-outline-variant/30',
35
+ button: 'border-outline-variant/30'
36
+ },
37
+ none: {
38
+ root: 'border-0 bg-transparent rounded-none',
39
+ header: 'border-0 px-0',
40
+ button: 'border-outline-variant/40'
41
+ }
42
+ },
43
+ size: {
44
+ sm: {
45
+ header: 'px-3 py-2',
46
+ button: 'px-2 py-1',
47
+ body: '[&_.shiki]:p-3 [&_.shiki]:text-xs [&_.shiki]:leading-5'
48
+ },
49
+ md: {
50
+ header: 'px-4 py-3',
51
+ button: 'px-2.5 py-1',
52
+ body: '[&_.shiki]:p-4 [&_.shiki]:text-sm [&_.shiki]:leading-6'
53
+ },
54
+ lg: {
55
+ header: 'px-5 py-4',
56
+ button: 'px-3 py-1.5',
57
+ body: '[&_.shiki]:p-5 [&_.shiki]:text-sm [&_.shiki]:leading-6'
58
+ }
59
+ }
60
+ },
61
+ defaultVariants: {
62
+ variant: 'outline',
63
+ size: 'md'
64
+ }
65
+ });
66
+ export const codeBlockDefaults = {
67
+ defaultVariants: codeBlockVariants.defaultVariants,
68
+ slots: {}
69
+ };
@@ -0,0 +1,2 @@
1
+ export { default as CodeBlock } from './CodeBlock.svelte';
2
+ export type { CodeBlockProps } from './code-block.types.js';
@@ -0,0 +1 @@
1
+ export { default as CodeBlock } from './CodeBlock.svelte';
@@ -81,7 +81,7 @@
81
81
  }
82
82
 
83
83
  const rootProps = $derived.by(() => {
84
- const base = {
84
+ const base: Record<string, unknown> = {
85
85
  ...rest,
86
86
  open,
87
87
  onOpenChange: handleOpenChange,
@@ -99,6 +99,9 @@
99
99
  onRelease,
100
100
  onClose
101
101
  }
102
+ if (rest.container) {
103
+ base.container = rest.container
104
+ }
102
105
  if (snapPoints && fadeFromIndex !== null) {
103
106
  return { ...base, snapPoints, fadeFromIndex }
104
107
  }
@@ -182,9 +185,16 @@
182
185
  bits-ui v0 internally, which is incompatible with Svelte 5 snippets
183
186
  and yields an empty <button> in the DOM. We pass an `onclick` handler
184
187
  to the user-supplied trigger snippet so the user's element opens the
185
- drawer when activated.
188
+ drawer when activated, and we manually expose `data-state` so callers
189
+ can style/assert the trigger element.
186
190
  -->
187
- {@render children({ props: { type: 'button', onclick: () => (open = true) } })}
191
+ {@render children({
192
+ props: {
193
+ type: 'button',
194
+ onclick: () => (open = true),
195
+ 'data-state': open ? 'open' : 'closed'
196
+ }
197
+ })}
188
198
  {/if}
189
199
 
190
200
  {#if portal}
@@ -467,6 +467,9 @@ export type Props = EditorProps;
467
467
  try {
468
468
  const url = await onImageUpload(file)
469
469
  if (!isSafeImageSrc(url)) {
470
+ console.warn(
471
+ `[svelora] Blocked unsafe image src returned by onImageUpload: ${url}`
472
+ )
470
473
  return
471
474
  }
472
475
  editor.chain().focus().setImage({ src: url }).run()
@@ -0,0 +1,54 @@
1
+ <script lang="ts" module>
2
+ import type { FontsProps } from './fonts.types.js'
3
+
4
+ export type Props = FontsProps
5
+ </script>
6
+
7
+ <script lang="ts">
8
+ import { getConfig } from '../config.js'
9
+ import {
10
+ buildFontVariablesCss,
11
+ buildGoogleFontsUrl,
12
+ buildLocalFontFaceCss,
13
+ fontsDefaults,
14
+ resolveFontsOptions
15
+ } from './fonts.js'
16
+
17
+ const globalConfig = getConfig()
18
+ const config = resolveFontsOptions(globalConfig.fonts)
19
+
20
+ let {
21
+ fonts,
22
+ families,
23
+ display,
24
+ preconnect
25
+ }: Props = $props()
26
+
27
+ const resolvedFamilies = $derived(families ?? fonts ?? (config === false ? [] : config.families ?? []))
28
+ const resolvedDisplay = $derived(display ?? (config === false ? fontsDefaults.display : config.display))
29
+ const resolvedPreconnect = $derived(
30
+ preconnect ?? (config === false ? fontsDefaults.preconnect : config.preconnect)
31
+ )
32
+
33
+ const href = $derived(buildGoogleFontsUrl(resolvedFamilies, resolvedDisplay))
34
+ const localFontFaceCss = $derived(buildLocalFontFaceCss(resolvedFamilies, resolvedDisplay))
35
+ const variableCss = $derived(buildFontVariablesCss(resolvedFamilies))
36
+ const inlineCss = $derived([localFontFaceCss, variableCss].filter((value) => value.length > 0).join('\n\n'))
37
+ const hasFonts = $derived(href.length > 0 || inlineCss.length > 0)
38
+ </script>
39
+
40
+ <svelte:head>
41
+ {#if hasFonts}
42
+ {#if resolvedPreconnect && href}
43
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
44
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
45
+ {/if}
46
+ {#if href}
47
+ <link rel="stylesheet" href={href} />
48
+ {/if}
49
+ {#if inlineCss}
50
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
51
+ {@html `<style>${inlineCss}</style>`}
52
+ {/if}
53
+ {/if}
54
+ </svelte:head>
@@ -0,0 +1,5 @@
1
+ import type { FontsProps } from './fonts.types.js';
2
+ export type Props = FontsProps;
3
+ declare const Fonts: import("svelte").Component<FontsProps, {}, "">;
4
+ type Fonts = ReturnType<typeof Fonts>;
5
+ export default Fonts;
@@ -0,0 +1,12 @@
1
+ import type { FontDefinition, GoogleFontDisplay, FontsConfig, FontsOptions } from './fonts.types.js';
2
+ export declare const defaultFontFamilies: FontDefinition[];
3
+ export declare const fontsDefaults: {
4
+ families: FontDefinition[];
5
+ display: GoogleFontDisplay;
6
+ preconnect: boolean;
7
+ };
8
+ export declare function buildGoogleFontsUrl(fonts: FontDefinition[], display?: GoogleFontDisplay): string;
9
+ export declare function buildFontFamily(font: FontDefinition): string;
10
+ export declare function buildLocalFontFaceCss(fonts: FontDefinition[], defaultDisplay?: GoogleFontDisplay): string;
11
+ export declare function buildFontVariablesCss(fonts: FontDefinition[]): string;
12
+ export declare function resolveFontsOptions(config?: FontsConfig): FontsOptions | false;