@joewinke/jatui 0.43.0 → 0.44.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/CLAUDE.md CHANGED
@@ -92,6 +92,7 @@ Then import:
92
92
  | `LoadingSpinner` | Spinner animation | `size`, `color` |
93
93
  | `Sparkline` | Inline mini chart | `data: number[]`, `width`, `height`, `color` |
94
94
  | `LazyImage` | Image with lazy loading + placeholder | `src`, `alt`, `placeholder` |
95
+ | `ImageReveal` | Holds an image slot while the image doesn't exist yet (a logo lookup, an AI generation): a token-tinted ripple (`role="status"`, `aria-busy`) while `src` is null and while the bytes decode, then blur-to-sharp on `--motion-*` + `onReveal`. `src=''` or a failed load → calm initials from `alt` (or an image glyph) — never a broken `<img>`. Ripple is `.jatui-motion-ambient`: calm / reduced motion = static tint, instant swap. Clean-room (BoardUI's image-gen effect is Pro). Pure helpers: `imageRevealPhase`, `imageRevealInitials`. | `src` (`string \| null`), `alt`, `shape` (`square\|rounded\|circle`), `size` (`xs…xl` \| px \| CSS \| `fill`), `tone` (semantic or CSS colour), `label`, `initials?`, `fit?`, `phase` (bindable, read-only), `onReveal?(src)`, `onError?(src)`, `fallback?` (snippet) |
95
96
  | `Empty` | Zero-state surface — v1 thin copy/children block still works; pass `title` for the v2 hero layout (upstreamed from the IDE's EmptyState): named `glyph` (11 curated Heroicons), `size` (sm/md/lg), `pulse` (reduced-motion gated, tinted with `--color-primary`), `icon` snippet override, CTA `actions` snippet, `description`, extra `children` below. | `title` (opts into v2), `description`, `glyph`, `size`, `pulse`, `icon`/`actions` (snippets), `copy`, `class`, `children` |
96
97
  | `ErrorState` | Standard "this failed" surface (upstreamed from the IDE's ErrorState) — error-token themed icon, optional Retry button, extra `actions`, collapsible `details` block for raw error output. Centered card mirroring `Empty` v2. | `title`, `message`, `details`, `retryLabel`, `onRetry`, `size`, `actions` (snippet) |
97
98
  | `HunkDiffView` | Side-by-side diff with line numbers, red/green backgrounds, strikethrough on removed lines, inline word-level highlights via the `diff` lib. Used for reviewing YAML/text changes per hunk. | `oldText: string \| null`, `newText: string \| null`, `unified?: boolean` |
@@ -0,0 +1,353 @@
1
+ <!--
2
+ ImageReveal — a frame that shows a "generating" ripple until its image
3
+ exists, then brings the image in blur-to-sharp. Built for slots whose image
4
+ arrives late: a logo being looked up, an AI generation, a thumbnail still
5
+ rendering.
6
+
7
+ src null / undefined → generating: ripple on the tone, role="status",
8
+ aria-busy
9
+ src set, not decoded → still the ripple (no half-loaded image is shown)
10
+ src loaded → blur + slight scale → sharp, ripple fades; onReveal
11
+ src '' or onerror → calm initials (from alt) or an image glyph — never a
12
+ broken image
13
+
14
+ Colours are one tone mixed with color-mix (no hex). The ripple is a
15
+ decorative loop: .jatui-motion-ambient, and under data-motion="calm" or
16
+ prefers-reduced-motion it becomes a static tint and the image swaps in
17
+ without the blur.
18
+
19
+ Clean-room (jatui-vui9q): BoardUI's image-generation effect is a Pro item;
20
+ nothing from it was read or copied.
21
+ -->
22
+ <script lang="ts">
23
+ import type { Snippet } from 'svelte';
24
+ import {
25
+ IMAGE_REVEAL_RADIUS,
26
+ imageRevealBusy,
27
+ imageRevealInitials,
28
+ imageRevealPhase,
29
+ imageRevealSize,
30
+ imageRevealTone,
31
+ type ImageRevealPhase,
32
+ type ImageRevealShape,
33
+ type ImageRevealSizeName,
34
+ type ImageRevealToneName
35
+ } from './imageReveal';
36
+
37
+ interface Props {
38
+ /** null / undefined = still generating. '' = nothing to show (fallback). */
39
+ src?: string | null;
40
+ /** Alt text for the image; also the source of the fallback initials. */
41
+ alt?: string;
42
+ shape?: ImageRevealShape;
43
+ /** Named size, px number, any CSS length, or 'fill' (100% width). Always square. */
44
+ size?: ImageRevealSizeName | number | (string & {});
45
+ /** DaisyUI semantic name or any CSS colour. */
46
+ tone?: ImageRevealToneName | (string & {});
47
+ /** Screen-reader status while generating. */
48
+ label?: string;
49
+ /** Override the letters derived from `alt`. '' forces the glyph. */
50
+ initials?: string;
51
+ /** object-fit for the image. Logos usually want 'contain'. */
52
+ fit?: 'cover' | 'contain';
53
+ /** Read-only: the current phase. */
54
+ phase?: ImageRevealPhase;
55
+ /** Fires once per src, when that image has loaded. */
56
+ onReveal?: (src: string) => void;
57
+ /** Fires once per src, when that image failed to load. */
58
+ onError?: (src: string) => void;
59
+ /** Replaces the initials / glyph fallback. */
60
+ fallback?: Snippet;
61
+ class?: string;
62
+ }
63
+
64
+ let {
65
+ src = null,
66
+ alt = '',
67
+ shape = 'rounded',
68
+ size = 'md',
69
+ tone = 'primary',
70
+ label = 'Generating image',
71
+ initials,
72
+ fit = 'cover',
73
+ phase = $bindable('generating'),
74
+ onReveal,
75
+ onError,
76
+ fallback,
77
+ class: className = ''
78
+ }: Props = $props();
79
+
80
+ let loadedSrc = $state<string | null>(null);
81
+ let failedSrc = $state<string | null>(null);
82
+ let img = $state<HTMLImageElement | null>(null);
83
+
84
+ const current = $derived(imageRevealPhase({ src, loadedSrc, failedSrc }));
85
+ const busy = $derived(imageRevealBusy(current));
86
+ const letters = $derived(initials ?? imageRevealInitials(alt));
87
+
88
+ $effect.pre(() => {
89
+ phase = current;
90
+ });
91
+
92
+ function loaded(from: string) {
93
+ if (from !== src || loadedSrc === from) return;
94
+ loadedSrc = from;
95
+ onReveal?.(from);
96
+ }
97
+
98
+ function failed(from: string) {
99
+ if (from !== src || failedSrc === from) return;
100
+ failedSrc = from;
101
+ onError?.(from);
102
+ }
103
+
104
+ // An image served from cache (or loaded before hydration) can finish before
105
+ // the load listener exists, so read its state once it is in the DOM.
106
+ $effect(() => {
107
+ if (!img || !src) return;
108
+ const el = img;
109
+ const from = src;
110
+ if (el.complete) {
111
+ if (el.naturalWidth > 0) loaded(from);
112
+ else if (el.currentSrc || el.getAttribute('src')) failed(from);
113
+ }
114
+ });
115
+ </script>
116
+
117
+ <div
118
+ class="jatui-image-reveal is-{current} {className}"
119
+ style:--jatui-ir-tone={imageRevealTone(tone)}
120
+ style:--jatui-ir-size={imageRevealSize(size)}
121
+ style:--jatui-ir-radius={IMAGE_REVEAL_RADIUS[shape]}
122
+ aria-busy={busy}
123
+ data-phase={current}
124
+ >
125
+ <span class="jatui-ir-status" role="status">{busy ? label : ''}</span>
126
+
127
+ {#if current !== 'fallback'}
128
+ <div class="jatui-ir-ripple" class:jatui-motion-ambient={busy} aria-hidden="true">
129
+ <span class="jatui-ir-ring"></span>
130
+ <span class="jatui-ir-ring"></span>
131
+ <span class="jatui-ir-ring"></span>
132
+ <span class="jatui-ir-core"></span>
133
+ </div>
134
+ {/if}
135
+
136
+ {#if src && current !== 'fallback'}
137
+ {#key src}
138
+ <img
139
+ bind:this={img}
140
+ class="jatui-ir-img"
141
+ class:is-shown={current === 'revealed'}
142
+ style:object-fit={fit}
143
+ {src}
144
+ {alt}
145
+ decoding="async"
146
+ onload={() => loaded(src!)}
147
+ onerror={() => failed(src!)}
148
+ />
149
+ {/key}
150
+ {:else if current === 'fallback'}
151
+ {#if fallback}
152
+ <div class="jatui-ir-fallback">{@render fallback()}</div>
153
+ {:else if letters}
154
+ <div class="jatui-ir-fallback" role={alt ? 'img' : undefined} aria-label={alt || undefined}>
155
+ <span class="jatui-ir-initials" aria-hidden="true">{letters}</span>
156
+ </div>
157
+ {:else}
158
+ <div class="jatui-ir-fallback" role={alt ? 'img' : undefined} aria-label={alt || undefined}>
159
+ <svg class="jatui-ir-glyph" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true">
160
+ <rect x="3" y="4" width="18" height="16" rx="2" />
161
+ <circle cx="9" cy="10" r="1.75" />
162
+ <path d="m21 16-5-5-9 9" stroke-linecap="round" stroke-linejoin="round" />
163
+ </svg>
164
+ </div>
165
+ {/if}
166
+ {/if}
167
+ </div>
168
+
169
+ <style>
170
+ .jatui-image-reveal {
171
+ --jatui-ir-reveal: calc(var(--motion-move, 300ms) * 2);
172
+ position: relative;
173
+ display: inline-grid;
174
+ place-items: center;
175
+ width: var(--jatui-ir-size);
176
+ max-width: 100%;
177
+ aspect-ratio: 1;
178
+ overflow: hidden;
179
+ isolation: isolate;
180
+ container-type: inline-size;
181
+ border-radius: var(--jatui-ir-radius);
182
+ background: color-mix(in oklab, var(--jatui-ir-tone) 7%, var(--color-base-100, Canvas));
183
+ box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--jatui-ir-tone) 14%, transparent);
184
+ vertical-align: middle;
185
+ }
186
+
187
+ .jatui-ir-status {
188
+ position: absolute;
189
+ width: 1px;
190
+ height: 1px;
191
+ margin: -1px;
192
+ overflow: hidden;
193
+ clip-path: inset(50%);
194
+ white-space: nowrap;
195
+ }
196
+
197
+ /* ---- generating: ripple ---- */
198
+ .jatui-ir-ripple {
199
+ position: absolute;
200
+ inset: 0;
201
+ display: grid;
202
+ place-items: center;
203
+ background: radial-gradient(
204
+ circle at 50% 50%,
205
+ color-mix(in oklab, var(--jatui-ir-tone) 16%, transparent) 0%,
206
+ transparent 70%
207
+ );
208
+ transition: opacity var(--jatui-ir-reveal) var(--motion-glide, ease-out);
209
+ }
210
+
211
+ .jatui-ir-ring,
212
+ .jatui-ir-core {
213
+ grid-area: 1 / 1;
214
+ border-radius: 9999px;
215
+ }
216
+
217
+ .jatui-ir-ring {
218
+ width: 90%;
219
+ aspect-ratio: 1;
220
+ border: max(1px, 2cqi) solid
221
+ color-mix(in oklab, var(--jatui-ir-tone) 45%, transparent);
222
+ opacity: 0;
223
+ transform: scale(0.2);
224
+ animation: jatui-ir-ripple 2400ms var(--motion-glide, ease-out) infinite;
225
+ }
226
+
227
+ .jatui-ir-ring:nth-child(2) {
228
+ animation-delay: 800ms;
229
+ }
230
+
231
+ .jatui-ir-ring:nth-child(3) {
232
+ animation-delay: 1600ms;
233
+ }
234
+
235
+ .jatui-ir-core {
236
+ width: 22%;
237
+ aspect-ratio: 1;
238
+ background: color-mix(in oklab, var(--jatui-ir-tone) 55%, transparent);
239
+ filter: blur(4cqi);
240
+ animation: jatui-ir-breathe 2400ms ease-in-out infinite;
241
+ }
242
+
243
+ @keyframes jatui-ir-ripple {
244
+ 0% {
245
+ opacity: 0;
246
+ transform: scale(0.2);
247
+ }
248
+ 15% {
249
+ opacity: 1;
250
+ }
251
+ 100% {
252
+ opacity: 0;
253
+ transform: scale(1.35);
254
+ }
255
+ }
256
+
257
+ @keyframes jatui-ir-breathe {
258
+ 0%,
259
+ 100% {
260
+ opacity: 0.55;
261
+ transform: scale(0.85);
262
+ }
263
+ 50% {
264
+ opacity: 1;
265
+ transform: scale(1.1);
266
+ }
267
+ }
268
+
269
+ /* ---- reveal ---- */
270
+ .jatui-ir-img {
271
+ position: absolute;
272
+ inset: 0;
273
+ width: 100%;
274
+ height: 100%;
275
+ opacity: 0;
276
+ filter: blur(calc(12cqi + 4px));
277
+ transform: scale(1.06);
278
+ transition:
279
+ opacity var(--jatui-ir-reveal) var(--motion-glide, ease-out),
280
+ filter var(--jatui-ir-reveal) var(--motion-glide, ease-out),
281
+ transform var(--jatui-ir-reveal) var(--motion-glide, ease-out);
282
+ }
283
+
284
+ .jatui-ir-img.is-shown {
285
+ opacity: 1;
286
+ filter: none;
287
+ transform: none;
288
+ }
289
+
290
+ .is-revealed .jatui-ir-ripple {
291
+ opacity: 0;
292
+ visibility: hidden;
293
+ transition:
294
+ opacity var(--jatui-ir-reveal) var(--motion-glide, ease-out),
295
+ visibility 0s linear var(--jatui-ir-reveal);
296
+ }
297
+
298
+ .is-revealed {
299
+ background: transparent;
300
+ box-shadow: none;
301
+ }
302
+
303
+ /* ---- fallback ---- */
304
+ .jatui-ir-fallback {
305
+ position: absolute;
306
+ inset: 0;
307
+ display: grid;
308
+ place-items: center;
309
+ color: color-mix(in oklab, var(--jatui-ir-tone) 75%, var(--color-base-content, CanvasText));
310
+ }
311
+
312
+ .jatui-ir-initials {
313
+ font-weight: 600;
314
+ font-size: 36cqi;
315
+ line-height: 1;
316
+ letter-spacing: 0.02em;
317
+ }
318
+
319
+ .jatui-ir-glyph {
320
+ width: 42%;
321
+ height: 42%;
322
+ opacity: 0.7;
323
+ }
324
+
325
+ /* ---- calm / reduced motion: static tint, instant swap ---- */
326
+ :global([data-motion='calm']) .jatui-ir-ring,
327
+ :global([data-motion='calm']) .jatui-ir-core,
328
+ .jatui-image-reveal:global([data-motion='calm']) .jatui-ir-ring,
329
+ .jatui-image-reveal:global([data-motion='calm']) .jatui-ir-core {
330
+ animation: none;
331
+ }
332
+ :global([data-motion='calm']) .jatui-ir-ring,
333
+ .jatui-image-reveal:global([data-motion='calm']) .jatui-ir-ring {
334
+ opacity: 0;
335
+ }
336
+ :global([data-motion='calm']) .jatui-image-reveal,
337
+ .jatui-image-reveal:global([data-motion='calm']) {
338
+ --jatui-ir-reveal: 0s;
339
+ }
340
+
341
+ @media (prefers-reduced-motion: reduce) {
342
+ .jatui-image-reveal {
343
+ --jatui-ir-reveal: 0s;
344
+ }
345
+ .jatui-ir-ring,
346
+ .jatui-ir-core {
347
+ animation: none;
348
+ }
349
+ .jatui-ir-ring {
350
+ opacity: 0;
351
+ }
352
+ }
353
+ </style>
@@ -0,0 +1,31 @@
1
+ import type { Snippet } from 'svelte';
2
+ import { type ImageRevealPhase, type ImageRevealShape, type ImageRevealSizeName, type ImageRevealToneName } from './imageReveal';
3
+ interface Props {
4
+ /** null / undefined = still generating. '' = nothing to show (fallback). */
5
+ src?: string | null;
6
+ /** Alt text for the image; also the source of the fallback initials. */
7
+ alt?: string;
8
+ shape?: ImageRevealShape;
9
+ /** Named size, px number, any CSS length, or 'fill' (100% width). Always square. */
10
+ size?: ImageRevealSizeName | number | (string & {});
11
+ /** DaisyUI semantic name or any CSS colour. */
12
+ tone?: ImageRevealToneName | (string & {});
13
+ /** Screen-reader status while generating. */
14
+ label?: string;
15
+ /** Override the letters derived from `alt`. '' forces the glyph. */
16
+ initials?: string;
17
+ /** object-fit for the image. Logos usually want 'contain'. */
18
+ fit?: 'cover' | 'contain';
19
+ /** Read-only: the current phase. */
20
+ phase?: ImageRevealPhase;
21
+ /** Fires once per src, when that image has loaded. */
22
+ onReveal?: (src: string) => void;
23
+ /** Fires once per src, when that image failed to load. */
24
+ onError?: (src: string) => void;
25
+ /** Replaces the initials / glyph fallback. */
26
+ fallback?: Snippet;
27
+ class?: string;
28
+ }
29
+ declare const ImageReveal: import("svelte").Component<Props, {}, "phase">;
30
+ type ImageReveal = ReturnType<typeof ImageReveal>;
31
+ export default ImageReveal;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Pure helpers behind ImageReveal: which phase the frame is in, the initials
3
+ * fallback, and the size / shape / tone lookups. No DOM, so
4
+ * tests/unit/imageReveal.test.ts runs them in node.
5
+ *
6
+ * Clean-room (jatui-vui9q): BoardUI's "AI Image Generation" item is in the
7
+ * Pro tier, so no BoardUI source was read or copied.
8
+ */
9
+ /**
10
+ * generating — no src yet (`null`/`undefined`): the ripple runs.
11
+ * loading — a src arrived but the bytes haven't decoded: the ripple keeps
12
+ * running, the image waits underneath at blur + opacity 0.
13
+ * revealed — the image loaded: blur-to-sharp, the ripple fades out.
14
+ * fallback — `src === ''` or the image failed: calm initials / empty glyph.
15
+ */
16
+ export type ImageRevealPhase = 'generating' | 'loading' | 'revealed' | 'fallback';
17
+ export type ImageRevealShape = 'square' | 'rounded' | 'circle';
18
+ export type ImageRevealSizeName = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'fill';
19
+ /** DaisyUI semantic colour names accepted as a `tone`. */
20
+ export type ImageRevealToneName = 'primary' | 'secondary' | 'accent' | 'neutral' | 'info' | 'success' | 'warning' | 'error';
21
+ export interface ImageRevealState {
22
+ src: string | null | undefined;
23
+ /** The src whose load event fired (null if none has). */
24
+ loadedSrc: string | null;
25
+ /** The src whose error event fired (null if none has). */
26
+ failedSrc: string | null;
27
+ }
28
+ /**
29
+ * Phase from the current src plus what the browser reported for it. Load and
30
+ * error are keyed by src, so a new src starts over at `loading` without the
31
+ * component having to reset flags.
32
+ */
33
+ export declare function imageRevealPhase({ src, loadedSrc, failedSrc }: ImageRevealState): ImageRevealPhase;
34
+ /** True while the frame is still waiting for something to show. */
35
+ export declare function imageRevealBusy(phase: ImageRevealPhase): boolean;
36
+ /**
37
+ * Up to two letters from the alt text: "Acme Corp" → "AC", "acme" → "A",
38
+ * "the-north-face" → "TN". Symbols and digits-only words are skipped, so a
39
+ * logo alt like "Acme logo" gives "AL" — callers who want "A" pass
40
+ * `initials` explicitly.
41
+ */
42
+ export declare function imageRevealInitials(alt: string | null | undefined): string;
43
+ export declare const IMAGE_REVEAL_SIZES: Record<Exclude<ImageRevealSizeName, 'fill'>, number>;
44
+ /**
45
+ * A CSS length for the frame's width. Named sizes and numbers are px; any
46
+ * other string (`'10rem'`, `'50%'`) passes through; `'fill'` is 100%.
47
+ */
48
+ export declare function imageRevealSize(size: ImageRevealSizeName | number | (string & {})): string;
49
+ export declare const IMAGE_REVEAL_RADIUS: Record<ImageRevealShape, string>;
50
+ /** A semantic name → its DaisyUI variable; any other string is a CSS colour. */
51
+ export declare function imageRevealTone(tone: ImageRevealToneName | (string & {})): string;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Pure helpers behind ImageReveal: which phase the frame is in, the initials
3
+ * fallback, and the size / shape / tone lookups. No DOM, so
4
+ * tests/unit/imageReveal.test.ts runs them in node.
5
+ *
6
+ * Clean-room (jatui-vui9q): BoardUI's "AI Image Generation" item is in the
7
+ * Pro tier, so no BoardUI source was read or copied.
8
+ */
9
+ /**
10
+ * Phase from the current src plus what the browser reported for it. Load and
11
+ * error are keyed by src, so a new src starts over at `loading` without the
12
+ * component having to reset flags.
13
+ */
14
+ export function imageRevealPhase({ src, loadedSrc, failedSrc }) {
15
+ if (src == null)
16
+ return 'generating';
17
+ if (src.trim() === '' || failedSrc === src)
18
+ return 'fallback';
19
+ if (loadedSrc === src)
20
+ return 'revealed';
21
+ return 'loading';
22
+ }
23
+ /** True while the frame is still waiting for something to show. */
24
+ export function imageRevealBusy(phase) {
25
+ return phase === 'generating' || phase === 'loading';
26
+ }
27
+ /**
28
+ * Up to two letters from the alt text: "Acme Corp" → "AC", "acme" → "A",
29
+ * "the-north-face" → "TN". Symbols and digits-only words are skipped, so a
30
+ * logo alt like "Acme logo" gives "AL" — callers who want "A" pass
31
+ * `initials` explicitly.
32
+ */
33
+ export function imageRevealInitials(alt) {
34
+ if (!alt)
35
+ return '';
36
+ const words = alt
37
+ .split(/[\s\-_./]+/)
38
+ .map((w) => w.match(/\p{L}/u)?.[0] ?? '')
39
+ .filter(Boolean);
40
+ return words
41
+ .slice(0, 2)
42
+ .join('')
43
+ .toLocaleUpperCase();
44
+ }
45
+ export const IMAGE_REVEAL_SIZES = {
46
+ xs: 32,
47
+ sm: 48,
48
+ md: 64,
49
+ lg: 96,
50
+ xl: 128
51
+ };
52
+ /**
53
+ * A CSS length for the frame's width. Named sizes and numbers are px; any
54
+ * other string (`'10rem'`, `'50%'`) passes through; `'fill'` is 100%.
55
+ */
56
+ export function imageRevealSize(size) {
57
+ if (typeof size === 'number')
58
+ return `${size}px`;
59
+ if (size === 'fill')
60
+ return '100%';
61
+ if (size in IMAGE_REVEAL_SIZES)
62
+ return `${IMAGE_REVEAL_SIZES[size]}px`;
63
+ return size;
64
+ }
65
+ export const IMAGE_REVEAL_RADIUS = {
66
+ square: '0',
67
+ rounded: 'var(--jatui-radius-box, var(--radius-box, 0.5rem))',
68
+ circle: '9999px'
69
+ };
70
+ const TONE_NAMES = new Set([
71
+ 'primary',
72
+ 'secondary',
73
+ 'accent',
74
+ 'neutral',
75
+ 'info',
76
+ 'success',
77
+ 'warning',
78
+ 'error'
79
+ ]);
80
+ /** A semantic name → its DaisyUI variable; any other string is a CSS colour. */
81
+ export function imageRevealTone(tone) {
82
+ return TONE_NAMES.has(tone) ? `var(--color-${tone})` : tone;
83
+ }
package/dist/index.d.ts CHANGED
@@ -400,3 +400,6 @@ export { default as SettingsSection } from './components/settings/SettingsSectio
400
400
  export { default as SettingsRow } from './components/settings/SettingsRow.svelte';
401
401
  export type { SettingsRowContext } from './components/settings/SettingsRow.svelte';
402
402
  export * from './data/grid/index';
403
+ export { default as ImageReveal } from './components/ImageReveal.svelte';
404
+ export { imageRevealPhase, imageRevealBusy, imageRevealInitials, imageRevealSize, imageRevealTone, IMAGE_REVEAL_SIZES, IMAGE_REVEAL_RADIUS } from './components/imageReveal';
405
+ export type { ImageRevealPhase, ImageRevealShape, ImageRevealSizeName, ImageRevealToneName, ImageRevealState } from './components/imageReveal';
package/dist/index.js CHANGED
@@ -435,3 +435,5 @@ export { default as SegmentedControl } from './components/SegmentedControl.svelt
435
435
  export { default as SettingsSection } from './components/settings/SettingsSection.svelte';
436
436
  export { default as SettingsRow } from './components/settings/SettingsRow.svelte';
437
437
  export * from './data/grid/index';
438
+ export { default as ImageReveal } from './components/ImageReveal.svelte';
439
+ export { imageRevealPhase, imageRevealBusy, imageRevealInitials, imageRevealSize, imageRevealTone, IMAGE_REVEAL_SIZES, IMAGE_REVEAL_RADIUS } from './components/imageReveal';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joewinke/jatui",
3
- "version": "0.43.0",
3
+ "version": "0.44.0",
4
4
  "private": false,
5
5
  "description": "Shared Svelte 5 component library for JAT projects",
6
6
  "type": "module",