@happyvertical/smrt-images 0.34.5 → 0.34.7

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": "1.0.0",
3
- "timestamp": 1782261289124,
3
+ "timestamp": 1782290139382,
4
4
  "packageName": "@happyvertical/smrt-images",
5
- "packageVersion": "0.34.5",
5
+ "packageVersion": "0.34.7",
6
6
  "objects": {
7
7
  "@happyvertical/smrt-images:Image": {
8
8
  "name": "image",
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-06-24T00:34:49.879Z",
3
+ "generatedAt": "2026-06-24T08:35:39.740Z",
4
4
  "packageName": "@happyvertical/smrt-images",
5
- "packageVersion": "0.34.5",
5
+ "packageVersion": "0.34.7",
6
6
  "sourceManifestPath": "dist/manifest.json",
7
7
  "agentDocPath": "AGENTS.md",
8
8
  "sourceHashes": {
9
- "manifest": "3b5de61f9f6239f9835e1a662bcb1adf49eee164215d3f85f270d5765d88530a",
10
- "packageJson": "ac8c71a2bd295c90b4fdf0f037010607948e8494883cd05b7b42b9d1f09a80f8",
9
+ "manifest": "3ae226f3faecb697f69b0c201fecaeeecce675946c85b543fdadb4ea43801249",
10
+ "packageJson": "d9aaa3aecde993a2efa52b9353e09a0176555dbab0a70de0466fad78755968c9",
11
11
  "agents": "b0cf63bd78f00cc1729ea7f1425291f8e23ab5591161cbbf0d08f4b16ace07fd"
12
12
  },
13
13
  "exports": [
@@ -1,5 +1,7 @@
1
1
  <script lang="ts">
2
+ import { Input, Select } from '@happyvertical/smrt-ui/forms';
2
3
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
4
+ import { Button } from '@happyvertical/smrt-ui/ui';
3
5
  import { onMount } from 'svelte';
4
6
  import { M } from '../i18n.js';
5
7
  import type {
@@ -157,29 +159,29 @@ function handleDragStart(event: DragEvent, image: ImageLike) {
157
159
 
158
160
  <div class="toolbar">
159
161
  <div class="search-box">
160
- <input
161
- type="search"
162
+ <Input
163
+ type="search"
162
164
  bind:value={searchQuery}
163
165
  placeholder={t(M['images.assets_gallery.search_placeholder'])}
164
166
  />
165
167
  </div>
166
-
168
+
167
169
  <div class="filters">
168
- <select bind:value={orientationFilter}>
170
+ <Select class="orientation-select" bind:value={orientationFilter}>
169
171
  <option value="all">{t(M['images.assets_gallery.any_orientation'])}</option>
170
172
  <option value="landscape">Landscape</option>
171
173
  <option value="portrait">Portrait</option>
172
174
  <option value="square">Square</option>
173
- </select>
174
-
175
- <input
176
- type="number"
175
+ </Select>
176
+
177
+ <Input
178
+ type="number"
177
179
  bind:value={minWidth}
178
180
  placeholder={t(M['images.assets_gallery.min_width_placeholder'])}
179
181
  class="size-input"
180
182
  />
181
- <input
182
- type="number"
183
+ <Input
184
+ type="number"
183
185
  bind:value={minHeight}
184
186
  placeholder={t(M['images.assets_gallery.min_height_placeholder'])}
185
187
  class="size-input"
@@ -195,6 +197,7 @@ function handleDragStart(event: DragEvent, image: ImageLike) {
195
197
  <div class="gallery-grid">
196
198
  {#each images as image (image.id)}
197
199
  {#if onSelect}
200
+ <!-- raw-primitive-allow: large draggable selection card wrapping rich image content, not a standard action button -->
198
201
  <button
199
202
  type="button"
200
203
  class="gallery-item"
@@ -233,13 +236,14 @@ function handleDragStart(event: DragEvent, image: ImageLike) {
233
236
 
234
237
  {#if hasMore}
235
238
  <div class="load-more">
236
- <button
237
- onclick={() => loadImages(false)}
239
+ <Button
240
+ variant="ghost"
241
+ class="load-more-btn--pill"
242
+ onclick={() => loadImages(false)}
238
243
  disabled={isLoading}
239
- class="load-more-btn"
240
244
  >
241
245
  {isLoading ? 'Loading...' : 'Load More'}
242
- </button>
246
+ </Button>
243
247
  </div>
244
248
  {/if}
245
249
  </div>
@@ -283,45 +287,22 @@ function handleDragStart(event: DragEvent, image: ImageLike) {
283
287
  min-width: 250px;
284
288
  }
285
289
 
286
- .search-box input {
287
- width: 100%;
288
- padding: 0.75rem 1.25rem;
289
- background: var(--smrt-color-surface-container-highest, #333);
290
- border: 1px solid var(--smrt-color-outline-variant, #444);
291
- border-radius: var(--smrt-radius-full, 9999px);
292
- color: inherit;
293
- font-size: var(--smrt-typography-body-large-size, 0.95rem);
294
- transition: box-shadow 0.2s, border-color 0.2s;
295
- }
296
-
297
- .search-box input:focus {
298
- outline: none;
299
- border-color: var(--smrt-color-primary, #3b82f6);
300
- box-shadow: inset 0 0 0 1px var(--smrt-color-primary, #3b82f6);
301
- }
302
-
303
290
  .filters {
304
291
  display: flex;
305
292
  gap: 0.75rem;
306
293
  flex-wrap: wrap;
307
294
  }
308
295
 
309
- .filters select, .filters input {
310
- padding: 0.75rem 1rem;
311
- background: var(--smrt-color-surface-container-high, #242424);
312
- border: 1px solid var(--smrt-color-outline-variant, #444);
313
- border-radius: var(--smrt-radius-sm, 4px);
314
- color: inherit;
315
- transition: box-shadow 0.2s, border-color 0.2s;
296
+ /* Layout-only overrides for the migrated filter primitives. The Input / Select
297
+ primitives own the visual styling; these :global() rules only pierce their
298
+ rendered controls to size the boxes within the flex row (see #1589 scoping
299
+ rule). The orientation Select sizes to its content instead of the primitive's
300
+ full-width default; the min-width/min-height Inputs are capped. */
301
+ .filters :global(.orientation-select) {
302
+ width: auto;
316
303
  }
317
304
 
318
- .filters select:focus, .filters input:focus {
319
- outline: none;
320
- border-color: var(--smrt-color-primary, #3b82f6);
321
- box-shadow: inset 0 0 0 1px var(--smrt-color-primary, #3b82f6);
322
- }
323
-
324
- .size-input {
305
+ .filters :global(.size-input) {
325
306
  width: 120px;
326
307
  }
327
308
 
@@ -421,23 +402,20 @@ function handleDragStart(event: DragEvent, image: ImageLike) {
421
402
  padding: 1rem 0;
422
403
  }
423
404
 
424
- .load-more-btn {
405
+ /* Bespoke pill look for the migrated Load More Button. :global() pierces into
406
+ the Button child's rendered <button>, which carries its own scope hash
407
+ (see #1589 scoping rule). */
408
+ .load-more :global(.load-more-btn--pill) {
425
409
  padding: 0.75rem 2rem;
426
410
  background: var(--smrt-color-surface-container-high, #242424);
427
411
  color: var(--smrt-color-primary, #3b82f6);
428
412
  border: 1px solid var(--smrt-color-outline-variant, #444);
429
413
  border-radius: var(--smrt-radius-full, 9999px);
430
414
  font-weight: var(--smrt-typography-weight-medium, 500);
431
- cursor: pointer;
432
415
  transition: background 0.2s;
433
416
  }
434
417
 
435
- .load-more-btn:hover:not(:disabled) {
418
+ .load-more :global(.load-more-btn--pill:hover:not(:disabled)) {
436
419
  background: var(--smrt-color-surface-container-highest, #333);
437
420
  }
438
-
439
- .load-more-btn:disabled {
440
- opacity: 0.6;
441
- cursor: not-allowed;
442
- }
443
421
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"AssetsGallery.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/AssetsGallery.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,SAAS,EACT,mBAAmB,EAEpB,MAAM,kBAAkB,CAAC;AAEzB,KAAK,gBAAgB,GAAI;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACtC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAyNF,QAAA,MAAM,aAAa,sDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"AssetsGallery.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/AssetsGallery.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,SAAS,EACT,mBAAmB,EAEpB,MAAM,kBAAkB,CAAC;AAEzB,KAAK,gBAAgB,GAAI;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACtC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AA4NF,QAAA,MAAM,aAAa,sDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -1,5 +1,7 @@
1
1
  <script lang="ts">
2
+ import { Input, Select, Textarea } from '@happyvertical/smrt-ui/forms';
2
3
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
4
+ import { Button } from '@happyvertical/smrt-ui/ui';
3
5
  import { M } from '../i18n.js';
4
6
  import type {
5
7
  ImageConvertRequest,
@@ -180,7 +182,7 @@ async function handleAIEdit() {
180
182
  <div class="header">
181
183
  <h3>{t(M['images.image_editor.title'])}</h3>
182
184
  {#if onCancel}
183
- <button class="close-btn" onclick={onCancel}>×</button>
185
+ <Button variant="ghost" size="sm" class="close-btn--x" onclick={onCancel}>×</Button>
184
186
  {/if}
185
187
  </div>
186
188
 
@@ -194,8 +196,16 @@ async function handleAIEdit() {
194
196
 
195
197
  <div class="editor-controls">
196
198
  <div class="mode-selector">
197
- <button class:active={mode === 'standard'} onclick={() => mode = 'standard'}>{t(M['images.image_editor.standard_tools'])}</button>
198
- <button class:active={mode === 'ai'} onclick={() => mode = 'ai'}>{t(M['images.image_editor.ai_edit'])}</button>
199
+ <Button
200
+ variant={mode === 'standard' ? 'primary' : 'ghost'}
201
+ class="mode-tab"
202
+ onclick={() => (mode = 'standard')}
203
+ >{t(M['images.image_editor.standard_tools'])}</Button>
204
+ <Button
205
+ variant={mode === 'ai' ? 'primary' : 'ghost'}
206
+ class="mode-tab"
207
+ onclick={() => (mode = 'ai')}
208
+ >{t(M['images.image_editor.ai_edit'])}</Button>
199
209
  </div>
200
210
 
201
211
  {#if error}
@@ -210,40 +220,40 @@ async function handleAIEdit() {
210
220
  <div class="tool-section">
211
221
  <h4>Resize</h4>
212
222
  <div class="row">
213
- <label>Width <input type="number" bind:value={width} onfocus={() => isEditingDimensions = true} /></label>
214
- <label>Height <input type="number" bind:value={height} onfocus={() => isEditingDimensions = true} /></label>
215
- <button disabled={isProcessing} onclick={handleResize} class="tonal-btn">{t(M['images.image_editor.apply_resize'])}</button>
223
+ <label>Width <Input type="number" class="dim-input" bind:value={width} onfocus={() => isEditingDimensions = true} /></label>
224
+ <label>Height <Input type="number" class="dim-input" bind:value={height} onfocus={() => isEditingDimensions = true} /></label>
225
+ <Button variant="ghost" class="tonal-btn--filled" disabled={isProcessing} onclick={handleResize}>{t(M['images.image_editor.apply_resize'])}</Button>
216
226
  </div>
217
227
  {#if isEditingDimensions}
218
- <div class="row"><button class="text-btn hint" onclick={resetDimensions}>{t(M['images.image_editor.reset_dimensions'])}</button></div>
228
+ <div class="row"><Button variant="ghost" size="sm" class="text-btn--link" onclick={resetDimensions}>{t(M['images.image_editor.reset_dimensions'])}</Button></div>
219
229
  {/if}
220
230
  </div>
221
231
 
222
232
  <div class="tool-section">
223
233
  <h4>Crop</h4>
224
234
  <div class="row">
225
- <label>X <input type="number" bind:value={cropX} onfocus={() => isCropping = true} /></label>
226
- <label>Y <input type="number" bind:value={cropY} onfocus={() => isCropping = true} /></label>
235
+ <label>X <Input type="number" class="dim-input" bind:value={cropX} onfocus={() => isCropping = true} /></label>
236
+ <label>Y <Input type="number" class="dim-input" bind:value={cropY} onfocus={() => isCropping = true} /></label>
227
237
  </div>
228
238
  <div class="row">
229
- <label>W <input type="number" bind:value={cropW} onfocus={() => isCropping = true} /></label>
230
- <label>H <input type="number" bind:value={cropH} onfocus={() => isCropping = true} /></label>
231
- <button disabled={isProcessing} onclick={handleCrop} class="tonal-btn">{t(M['images.image_editor.apply_crop'])}</button>
239
+ <label>W <Input type="number" class="dim-input" bind:value={cropW} onfocus={() => isCropping = true} /></label>
240
+ <label>H <Input type="number" class="dim-input" bind:value={cropH} onfocus={() => isCropping = true} /></label>
241
+ <Button variant="ghost" class="tonal-btn--filled" disabled={isProcessing} onclick={handleCrop}>{t(M['images.image_editor.apply_crop'])}</Button>
232
242
  </div>
233
243
  {#if isCropping}
234
- <div class="row"><button class="text-btn hint" onclick={resetDimensions}>{t(M['images.image_editor.reset_dimensions'])}</button></div>
244
+ <div class="row"><Button variant="ghost" size="sm" class="text-btn--link" onclick={resetDimensions}>{t(M['images.image_editor.reset_dimensions'])}</Button></div>
235
245
  {/if}
236
246
  </div>
237
247
 
238
248
  <div class="tool-section">
239
249
  <h4>{t(M['images.image_editor.convert_format'])}</h4>
240
250
  <div class="row">
241
- <select bind:value={format}>
251
+ <Select class="format-select" bind:value={format}>
242
252
  <option value="webp">WebP</option>
243
253
  <option value="jpeg">JPEG</option>
244
254
  <option value="png">PNG</option>
245
- </select>
246
- <button disabled={isProcessing} onclick={handleConvert} class="tonal-btn">Convert</button>
255
+ </Select>
256
+ <Button variant="ghost" class="tonal-btn--filled" disabled={isProcessing} onclick={handleConvert}>Convert</Button>
247
257
  </div>
248
258
  </div>
249
259
  {:else}
@@ -251,18 +261,20 @@ async function handleAIEdit() {
251
261
  <div class="tool-section">
252
262
  <h4>{t(M['images.image_editor.ai_powered_edit'])}</h4>
253
263
  <p class="hint">{t(M['images.image_editor.ai_powered_edit_hint'])}</p>
254
- <textarea
264
+ <Textarea
265
+ class="ai-prompt"
255
266
  bind:value={prompt}
256
267
  placeholder={t(M['images.image_editor.ai_prompt_placeholder'])}
257
- rows="4"
258
- ></textarea>
259
- <button
260
- disabled={isProcessing || !prompt.trim()}
268
+ rows={4}
269
+ />
270
+ <Button
271
+ variant="primary"
272
+ class="primary-btn--pill"
273
+ disabled={isProcessing || !prompt.trim()}
261
274
  onclick={handleAIEdit}
262
- class="primary-btn"
263
275
  >
264
276
  {isProcessing ? 'Generating...' : 'Apply AI Edit'}
265
- </button>
277
+ </Button>
266
278
  </div>
267
279
  {/if}
268
280
  </div>
@@ -297,12 +309,12 @@ async function handleAIEdit() {
297
309
  font-weight: var(--smrt-typography-weight-medium, 500);
298
310
  }
299
311
 
300
- .close-btn {
301
- background: transparent;
302
- border: none;
312
+ /* The migrated close Button keeps ghost styling; only bump the × glyph size.
313
+ :global() pierces into the Button child's rendered <button> (see #1589). */
314
+ .header :global(.close-btn--x) {
303
315
  color: inherit;
304
316
  font-size: var(--smrt-typography-headline-small-size, 1.5rem);
305
- cursor: pointer;
317
+ line-height: 1;
306
318
  }
307
319
 
308
320
  .empty-state {
@@ -346,22 +358,14 @@ async function handleAIEdit() {
346
358
  gap: 0.25rem;
347
359
  }
348
360
 
349
- .mode-selector button {
361
+ /* The mode toggles are migrated Buttons (variant primary = active, ghost =
362
+ inactive). Keep the segmented-pill sizing by piercing into each Button's
363
+ rendered <button> (see #1589 scoping rule). */
364
+ .mode-selector :global(.mode-tab) {
350
365
  flex: 1;
351
- background: transparent;
352
- border: none;
353
366
  padding: 0.6rem 1rem;
354
- color: var(--smrt-color-outline, #666);
355
- cursor: pointer;
356
367
  font-weight: var(--smrt-typography-weight-medium, 500);
357
368
  border-radius: var(--smrt-radius-full, 9999px);
358
- transition: all 0.2s;
359
- }
360
-
361
- .mode-selector button.active {
362
- background: var(--smrt-color-surface-container, #1a1a1a);
363
- color: var(--smrt-color-on-surface, #fff);
364
- box-shadow: var(--smrt-elevation-1, 0 1px 3px color-mix(in srgb, var(--smrt-color-shadow) 20%, transparent));
365
369
  }
366
370
 
367
371
  .tool-section h4 {
@@ -388,79 +392,59 @@ async function handleAIEdit() {
388
392
  gap: 0.25rem;
389
393
  }
390
394
 
391
- input, select, textarea {
392
- background: var(--smrt-color-surface-container-high, #242424);
393
- border: 1px solid var(--smrt-color-outline-variant, #444);
394
- color: inherit;
395
- padding: 0.6rem 0.75rem;
396
- border-radius: var(--smrt-radius-sm, 4px);
397
- transition: box-shadow 0.2s, border-color 0.2s;
395
+ /* Layout-only overrides for the migrated form primitives. The Input / Select /
396
+ Textarea primitives own the visual styling (background, border, focus ring);
397
+ these :global() rules only pierce their rendered controls to constrain box
398
+ size and spacing (see #1589 scoping rule). */
399
+ .row label :global(.dim-input) {
400
+ width: 90px;
398
401
  }
399
402
 
400
- input:focus, select:focus, textarea:focus {
401
- outline: none;
402
- border-color: var(--smrt-color-primary, #3b82f6);
403
- box-shadow: inset 0 0 0 1px var(--smrt-color-primary, #3b82f6);
403
+ /* Size the format Select to its content instead of the primitive's full-width
404
+ default so it sits inline beside the Convert button in the flex row. */
405
+ .row :global(.format-select) {
406
+ width: auto;
404
407
  }
405
408
 
406
- input[type="number"] {
407
- width: 90px;
408
- }
409
-
410
- textarea {
411
- width: 100%;
412
- resize: vertical;
409
+ .tool-section :global(.ai-prompt) {
413
410
  margin-bottom: 1rem;
414
- font-family: inherit;
415
411
  }
416
412
 
417
- .tonal-btn {
413
+ /* Migrated Buttons keep their bespoke filled-tonal / text-link / pill looks.
414
+ :global() pierces into each Button's rendered <button> (see #1589). */
415
+ .tool-section :global(.tonal-btn--filled) {
418
416
  background: var(--smrt-color-surface-container-highest, #333);
419
417
  color: var(--smrt-color-primary, #3b82f6);
420
418
  border: 1px solid var(--smrt-color-outline-variant, #444);
421
419
  padding: 0.6rem 1.2rem;
422
420
  border-radius: var(--smrt-radius-full, 9999px);
423
421
  font-weight: var(--smrt-typography-weight-medium, 500);
424
- cursor: pointer;
425
422
  transition: background 0.2s;
426
423
  margin-top: 1.25rem;
427
424
  }
428
425
 
429
- .tonal-btn:hover:not(:disabled) {
426
+ .tool-section :global(.tonal-btn--filled:hover:not(:disabled)) {
430
427
  background: var(--smrt-color-surface-container-high, #3f3f3f);
431
428
  }
432
429
 
433
- .text-btn {
434
- background: transparent;
435
- border: none;
436
- cursor: pointer;
430
+ .tool-section :global(.text-btn--link) {
437
431
  text-decoration: underline;
438
432
  padding: 0;
439
433
  }
440
- .text-btn:hover {
434
+ .tool-section :global(.text-btn--link:hover) {
441
435
  color: var(--smrt-color-on-surface, #fff);
442
436
  }
443
437
 
444
- .primary-btn {
445
- background: var(--smrt-color-primary, #3b82f6);
446
- color: white;
447
- border: none;
438
+ .tool-section :global(.primary-btn--pill) {
448
439
  padding: 0.6rem 1.5rem;
449
440
  border-radius: var(--smrt-radius-full, 9999px);
450
441
  font-weight: var(--smrt-typography-weight-medium, 500);
451
- cursor: pointer;
452
- transition: background 0.2s, opacity 0.2s;
453
442
  }
454
443
 
455
- .primary-btn:hover:not(:disabled) {
444
+ .tool-section :global(.primary-btn--pill:hover:not(:disabled)) {
456
445
  filter: brightness(1.1);
457
446
  }
458
447
 
459
- button:disabled {
460
- opacity: 0.5;
461
- cursor: not-allowed;
462
- }
463
-
464
448
  .hint {
465
449
  font-size: var(--smrt-typography-body-small-size, 0.8rem);
466
450
  color: var(--smrt-color-outline, #888);
@@ -1 +1 @@
1
- {"version":3,"file":"ImageEditor.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ImageEditor.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,iBAAiB,EACjB,SAAS,EAEV,MAAM,kBAAkB,CAAC;AAEzB,KAAK,gBAAgB,GAAI;IACxB,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAiQF,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ImageEditor.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ImageEditor.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAGV,iBAAiB,EACjB,SAAS,EAEV,MAAM,kBAAkB,CAAC;AAEzB,KAAK,gBAAgB,GAAI;IACxB,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACpC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB,CAAC;AAmQF,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -1,5 +1,7 @@
1
1
  <script lang="ts">
2
+ import { Input, Textarea } from '@happyvertical/smrt-ui/forms';
2
3
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
4
+ import { Button } from '@happyvertical/smrt-ui/ui';
3
5
  import { onDestroy } from 'svelte';
4
6
  import { M } from '../i18n.js';
5
7
  import type {
@@ -276,14 +278,14 @@ onDestroy(() => {
276
278
  {#if selectedImage}
277
279
  <!-- Gallery Confirmation Step -->
278
280
  <div class="header">
279
- <button type="button" class="back-btn" onclick={handleBackToChooser}>
281
+ <Button variant="ghost" size="sm" class="back-btn--link" onclick={handleBackToChooser}>
280
282
  <svg viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
281
283
  <polyline points="15 18 9 12 15 6"></polyline>
282
284
  </svg>
283
285
  Back
284
- </button>
286
+ </Button>
285
287
  {#if onCancel}
286
- <button type="button" class="close-btn" onclick={onCancel}>×</button>
288
+ <Button variant="ghost" size="sm" class="close-btn--x" onclick={onCancel}>×</Button>
287
289
  {/if}
288
290
  </div>
289
291
 
@@ -298,39 +300,40 @@ onDestroy(() => {
298
300
  </div>
299
301
 
300
302
  <div class="confirm-actions">
301
- <button type="button" class="primary-btn" onclick={handleConfirmOriginal}>
303
+ <Button variant="primary" class="primary-btn--pill" onclick={handleConfirmOriginal}>
302
304
  {t(M['images.image_uploader.select_image'])}
303
- </button>
304
- <button
305
- type="button"
306
- class="variation-toggle"
307
- class:active={showVariation}
308
- onclick={() => showVariation = !showVariation}
305
+ </Button>
306
+ <Button
307
+ variant="ghost"
308
+ class={showVariation
309
+ ? 'variation-toggle--chip variation-toggle--active'
310
+ : 'variation-toggle--chip'}
311
+ onclick={() => (showVariation = !showVariation)}
309
312
  >
310
313
  <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
311
314
  <path d="M12 20h9"></path>
312
315
  <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
313
316
  </svg>
314
317
  {t(M['images.image_uploader.create_variation'])}
315
- </button>
318
+ </Button>
316
319
  </div>
317
320
 
318
321
  {#if showVariation}
319
322
  <div class="variation-form">
320
323
  <p class="variation-hint">{t(M['images.image_uploader.variation_hint'])}</p>
321
- <textarea
324
+ <Textarea
322
325
  bind:value={variationPrompt}
323
326
  placeholder={t(M['images.image_uploader.variation_prompt_placeholder'])}
324
- rows="3"
327
+ rows={3}
325
328
  disabled={isGenerating}
326
- ></textarea>
329
+ />
327
330
  {#if variationError}
328
331
  <div class="variation-error">{variationError}</div>
329
332
  {/if}
330
- <button
331
- type="button"
332
- class="generate-btn"
333
- disabled={isGenerating || !variationPrompt.trim()}
333
+ <Button
334
+ variant="primary"
335
+ class="generate-btn--pill"
336
+ disabled={isGenerating || !variationPrompt.trim()}
334
337
  onclick={handleGenerateVariation}
335
338
  >
336
339
  {#if isGenerating}
@@ -339,7 +342,7 @@ onDestroy(() => {
339
342
  {:else}
340
343
  {t(M['images.image_uploader.generate_variation'])}
341
344
  {/if}
342
- </button>
345
+ </Button>
343
346
  </div>
344
347
  {/if}
345
348
  </div>
@@ -349,22 +352,38 @@ onDestroy(() => {
349
352
  <div class="header">
350
353
  <h3>{t(M['images.image_uploader.choose_image'])}</h3>
351
354
  {#if onCancel}
352
- <button type="button" class="close-btn" onclick={onCancel}>×</button>
355
+ <Button variant="ghost" size="sm" class="close-btn--x" onclick={onCancel}>×</Button>
353
356
  {/if}
354
357
  </div>
355
358
 
356
359
  <div class="tabs">
357
360
  {#if allowedTabs.includes('gallery')}
358
- <button type="button" class:active={activeTab === 'gallery'} onclick={() => activeTab = 'gallery'}>Gallery</button>
361
+ <Button
362
+ variant="ghost"
363
+ class={activeTab === 'gallery' ? 'uploader-tab uploader-tab--active' : 'uploader-tab'}
364
+ onclick={() => (activeTab = 'gallery')}
365
+ >Gallery</Button>
359
366
  {/if}
360
367
  {#if allowedTabs.includes('upload')}
361
- <button type="button" class:active={activeTab === 'upload'} onclick={() => activeTab = 'upload'}>Upload</button>
368
+ <Button
369
+ variant="ghost"
370
+ class={activeTab === 'upload' ? 'uploader-tab uploader-tab--active' : 'uploader-tab'}
371
+ onclick={() => (activeTab = 'upload')}
372
+ >Upload</Button>
362
373
  {/if}
363
374
  {#if allowedTabs.includes('camera')}
364
- <button type="button" class:active={activeTab === 'camera'} onclick={() => activeTab = 'camera'}>Camera</button>
375
+ <Button
376
+ variant="ghost"
377
+ class={activeTab === 'camera' ? 'uploader-tab uploader-tab--active' : 'uploader-tab'}
378
+ onclick={() => (activeTab = 'camera')}
379
+ >Camera</Button>
365
380
  {/if}
366
381
  {#if allowedTabs.includes('external')}
367
- <button type="button" class:active={activeTab === 'external'} onclick={() => activeTab = 'external'}>{t(M['images.image_uploader.external_url'])}</button>
382
+ <Button
383
+ variant="ghost"
384
+ class={activeTab === 'external' ? 'uploader-tab uploader-tab--active' : 'uploader-tab'}
385
+ onclick={() => (activeTab = 'external')}
386
+ >{t(M['images.image_uploader.external_url'])}</Button>
368
387
  {/if}
369
388
  </div>
370
389
 
@@ -401,13 +420,14 @@ onDestroy(() => {
401
420
  </div>
402
421
  <p>{t(M['images.image_uploader.drag_and_drop'])}</p>
403
422
  <span class="divider">or</span>
404
- <button type="button" class="browse-btn">{t(M['images.image_uploader.browse_files'])}</button>
405
- <input
406
- type="file"
407
- accept="image/*"
408
- bind:this={uploadInput}
409
- onchange={handleFileSelect}
410
- style="display: none;"
423
+ <Button variant="primary" class="browse-btn--decorative">{t(M['images.image_uploader.browse_files'])}</Button>
424
+ <!-- raw-primitive-allow: hidden file input driven imperatively via a DOM ref (bind:this gives uploadInput so the parent can call .click() and onchange reads target.files); the Input primitive exposes a component instance, not the underlying HTMLInputElement -->
425
+ <input
426
+ type="file"
427
+ accept="image/*"
428
+ bind:this={uploadInput}
429
+ onchange={handleFileSelect}
430
+ style="display: none;"
411
431
  />
412
432
  {#if uploadError}
413
433
  <p class="error">{uploadError}</p>
@@ -419,7 +439,7 @@ onDestroy(() => {
419
439
  {#if cameraError}
420
440
  <div class="error-panel">
421
441
  <p>{cameraError}</p>
422
- <button type="button" onclick={startCamera}>{t(M['images.image_uploader.try_again'])}</button>
442
+ <Button variant="secondary" class="try-again-btn" onclick={startCamera}>{t(M['images.image_uploader.try_again'])}</Button>
423
443
  </div>
424
444
  {:else}
425
445
  <div class="video-container">
@@ -429,9 +449,9 @@ onDestroy(() => {
429
449
  <div class="loading-overlay">{t(M['images.image_uploader.starting_camera'])}</div>
430
450
  {/if}
431
451
  </div>
432
- <button type="button" class="capture-btn" disabled={!isCameraActive} onclick={takePicture}>
452
+ <Button variant="primary" class="capture-btn--pill" disabled={!isCameraActive} onclick={takePicture}>
433
453
  {t(M['images.image_uploader.take_picture'])}
434
- </button>
454
+ </Button>
435
455
  <canvas bind:this={canvasElement} style="display: none;"></canvas>
436
456
  {/if}
437
457
  </div>
@@ -440,20 +460,21 @@ onDestroy(() => {
440
460
  <div class="external-area">
441
461
  <p class="hint">{t(M['images.image_uploader.external_hint'])}</p>
442
462
  <div class="input-group">
443
- <input
444
- type="url"
463
+ <Input
464
+ type="url"
465
+ class="external-url-input"
445
466
  bind:value={externalUrl}
446
467
  placeholder={t(M['images.image_uploader.external_url_placeholder'])}
447
468
  onkeydown={(e) => e.key === 'Enter' && handleExternalSubmit()}
448
469
  />
449
- <button
450
- type="button"
451
- class="submit-btn"
452
- disabled={!externalUrl.trim()}
470
+ <Button
471
+ variant="primary"
472
+ class="submit-btn--inline"
473
+ disabled={!externalUrl.trim()}
453
474
  onclick={handleExternalSubmit}
454
475
  >
455
476
  Add
456
- </button>
477
+ </Button>
457
478
  </div>
458
479
  </div>
459
480
  {/if}
@@ -488,16 +509,16 @@ onDestroy(() => {
488
509
  font-size: var(--smrt-typography-title-medium-size, 1.15rem);
489
510
  }
490
511
 
491
- .close-btn {
492
- background: transparent;
493
- border: none;
512
+ /* Migrated close Button keeps its muted ghost look; only the × glyph sizing
513
+ is custom. :global() pierces into the Button child's rendered <button>
514
+ (see #1589 scoping rule). */
515
+ .header :global(.close-btn--x) {
494
516
  color: var(--smrt-color-outline, #888);
495
517
  font-size: var(--smrt-typography-headline-small-size, 1.5rem);
496
518
  line-height: 1;
497
- cursor: pointer;
498
519
  }
499
-
500
- .close-btn:hover {
520
+
521
+ .header :global(.close-btn--x:hover) {
501
522
  color: var(--smrt-color-on-surface, #fff);
502
523
  }
503
524
 
@@ -507,27 +528,29 @@ onDestroy(() => {
507
528
  border-bottom: 1px solid var(--smrt-color-outline-variant, #333);
508
529
  }
509
530
 
510
- .tabs button {
531
+ /* The tab strip is migrated to ghost Buttons; the active state is the bottom
532
+ underline rather than a filled variant. :global() pierces into each
533
+ Button's rendered <button> (see #1589 scoping rule). */
534
+ .tabs :global(.uploader-tab) {
511
535
  flex: 1;
512
- background: transparent;
513
536
  border: none;
514
537
  border-bottom: 2px solid var(--smrt-color-outline-variant, #333);
515
538
  padding: 1rem;
516
539
  color: var(--smrt-color-outline, #888);
517
540
  font-weight: var(--smrt-typography-weight-medium, 500);
518
- cursor: pointer;
541
+ border-radius: 0;
519
542
  transition: all 0.2s;
520
543
  text-transform: uppercase;
521
544
  font-size: var(--smrt-typography-label-large-size, 0.85rem);
522
545
  letter-spacing: var(--smrt-typography-label-large-tracking, 0.5px);
523
546
  }
524
547
 
525
- .tabs button:hover {
548
+ .tabs :global(.uploader-tab:hover) {
526
549
  color: var(--smrt-color-on-surface-variant, #ccc);
527
550
  background: color-mix(in srgb, var(--smrt-color-on-surface, #fff) 2%, transparent);
528
551
  }
529
552
 
530
- .tabs button.active {
553
+ .tabs :global(.uploader-tab--active) {
531
554
  color: var(--smrt-color-primary, #3b82f6);
532
555
  border-bottom: 2px solid var(--smrt-color-primary, #3b82f6);
533
556
  background: transparent;
@@ -594,10 +617,10 @@ onDestroy(() => {
594
617
  margin-bottom: 1rem;
595
618
  }
596
619
 
597
- .browse-btn {
598
- background: var(--smrt-color-primary, #3b82f6);
599
- color: white;
600
- border: none;
620
+ /* Decorative label inside the click-catching upload area; the parent handles
621
+ clicks, so this migrated Button stays inert. :global() pierces into the
622
+ Button child's rendered <button> (see #1589 scoping rule). */
623
+ .upload-area :global(.browse-btn--decorative) {
601
624
  padding: 0.5rem 1.5rem;
602
625
  border-radius: var(--smrt-radius-full, 9999px);
603
626
  font-weight: var(--smrt-typography-weight-medium, 500);
@@ -648,21 +671,18 @@ onDestroy(() => {
648
671
  color: white;
649
672
  }
650
673
 
651
- .capture-btn {
652
- background: var(--smrt-color-primary, #3b82f6);
653
- color: white;
654
- border: none;
674
+ /* Large primary capture action; keeps its oversized pill sizing over the
675
+ primary variant. :global() pierces into the Button child (see #1589). */
676
+ .camera-area :global(.capture-btn--pill) {
655
677
  padding: 1rem 3rem;
656
678
  border-radius: var(--smrt-radius-full, 9999px);
657
679
  font-size: var(--smrt-typography-body-large-size, 1.1rem);
658
680
  font-weight: var(--smrt-typography-weight-semibold, 600);
659
- cursor: pointer;
660
681
  }
661
682
 
662
- .capture-btn:disabled {
683
+ .camera-area :global(.capture-btn--pill:disabled) {
663
684
  background: var(--smrt-color-outline-variant, #444);
664
685
  color: var(--smrt-color-outline, #888);
665
- cursor: not-allowed;
666
686
  }
667
687
 
668
688
  .error-panel {
@@ -677,13 +697,14 @@ onDestroy(() => {
677
697
  margin-bottom: 1rem;
678
698
  }
679
699
 
680
- .error-panel button {
700
+ /* Migrated "try again" Button keeps its surface-tinted look over the
701
+ secondary variant. :global() pierces into the Button child (see #1589). */
702
+ .error-panel :global(.try-again-btn) {
681
703
  background: var(--smrt-color-surface-container-high, #242424);
682
704
  color: white;
683
705
  border: 1px solid var(--smrt-color-outline-variant, #444);
684
706
  padding: 0.5rem 1rem;
685
707
  border-radius: var(--smrt-radius-sm, 4px);
686
- cursor: pointer;
687
708
  }
688
709
 
689
710
  /* External Tab */
@@ -708,55 +729,42 @@ onDestroy(() => {
708
729
  gap: 0.5rem;
709
730
  }
710
731
 
711
- .input-group input {
732
+ /* Layout-only override: let the migrated URL Input flex-grow to fill the row
733
+ beside the Add button. The Input primitive owns the visual styling; this
734
+ :global() rule only pierces its rendered <input> (see #1589 scoping rule). */
735
+ .input-group :global(.external-url-input) {
712
736
  flex: 1;
713
- padding: 1rem 1.25rem;
714
- background: var(--smrt-color-surface-container-high, #242424);
715
- border: 1px solid var(--smrt-color-outline-variant, #444);
716
- border-radius: var(--smrt-radius-sm, 4px);
717
- color: var(--smrt-color-on-surface, #fff);
718
- font-size: var(--smrt-typography-body-large-size, 1rem);
719
- transition: border-color 0.2s, box-shadow 0.2s;
720
737
  }
721
738
 
722
- .input-group input:focus {
723
- outline: none;
724
- border-color: var(--smrt-color-primary, #3b82f6);
725
- box-shadow: inset 0 0 0 1px var(--smrt-color-primary, #3b82f6);
726
- }
727
-
728
- .submit-btn {
729
- background: var(--smrt-color-primary, #3b82f6);
730
- color: white;
731
- border: none;
739
+ /* Migrated Add Button sits in the URL input group; keeps square-ish radius
740
+ and zero vertical padding. :global() pierces into the Button child
741
+ (see #1589 scoping rule). */
742
+ .input-group :global(.submit-btn--inline) {
732
743
  padding: 0 1.5rem;
733
744
  border-radius: var(--smrt-radius-md, 6px);
734
745
  font-weight: var(--smrt-typography-weight-medium, 500);
735
- cursor: pointer;
736
746
  }
737
747
 
738
- .submit-btn:disabled {
748
+ .input-group :global(.submit-btn--inline:disabled) {
739
749
  background: var(--smrt-color-surface-container-highest, #333);
740
750
  color: var(--smrt-color-outline, #666);
741
- cursor: not-allowed;
742
751
  }
743
752
 
744
753
  /* --- Back Button --- */
745
- .back-btn {
754
+ /* Migrated ghost Button keeps the icon+label inline layout and tinted hover.
755
+ :global() pierces into the Button child (see #1589 scoping rule). */
756
+ .header :global(.back-btn--link) {
746
757
  display: flex;
747
758
  align-items: center;
748
759
  gap: 0.35rem;
749
- background: transparent;
750
- border: none;
751
760
  color: var(--smrt-color-primary, #3b82f6);
752
761
  font-weight: var(--smrt-typography-weight-medium, 500);
753
- cursor: pointer;
754
762
  padding: 0.25rem 0.5rem;
755
763
  border-radius: var(--smrt-radius-sm, 4px);
756
764
  transition: background 0.15s;
757
765
  }
758
766
 
759
- .back-btn:hover {
767
+ .header :global(.back-btn--link:hover) {
760
768
  background: color-mix(in srgb, var(--smrt-color-primary) 8%, transparent);
761
769
  }
762
770
 
@@ -814,22 +822,20 @@ onDestroy(() => {
814
822
  margin-top: 0.5rem;
815
823
  }
816
824
 
817
- .primary-btn {
818
- background: var(--smrt-color-primary, #3b82f6);
819
- color: white;
820
- border: none;
825
+ /* Migrated confirm-action Buttons keep their pill / chip looks. :global()
826
+ pierces into each Button's rendered <button> (see #1589 scoping rule). */
827
+ .confirm-actions :global(.primary-btn--pill) {
821
828
  padding: 0.65rem 1.5rem;
822
829
  border-radius: var(--smrt-radius-full, 9999px);
823
830
  font-weight: var(--smrt-typography-weight-medium, 500);
824
- cursor: pointer;
825
831
  transition: filter 0.15s;
826
832
  }
827
833
 
828
- .primary-btn:hover {
834
+ .confirm-actions :global(.primary-btn--pill:hover) {
829
835
  filter: brightness(1.1);
830
836
  }
831
837
 
832
- .variation-toggle {
838
+ .confirm-actions :global(.variation-toggle--chip) {
833
839
  display: flex;
834
840
  align-items: center;
835
841
  gap: 0.4rem;
@@ -839,15 +845,14 @@ onDestroy(() => {
839
845
  padding: 0.65rem 1.25rem;
840
846
  border-radius: var(--smrt-radius-full, 9999px);
841
847
  font-weight: var(--smrt-typography-weight-medium, 500);
842
- cursor: pointer;
843
848
  transition: all 0.2s;
844
849
  }
845
850
 
846
- .variation-toggle:hover {
851
+ .confirm-actions :global(.variation-toggle--chip:hover) {
847
852
  background: var(--smrt-color-surface-container-high, #3f3f3f);
848
853
  }
849
854
 
850
- .variation-toggle.active {
855
+ .confirm-actions :global(.variation-toggle--active) {
851
856
  color: var(--smrt-color-primary, #3b82f6);
852
857
  border-color: var(--smrt-color-primary, #3b82f6);
853
858
  background: color-mix(in srgb, var(--smrt-color-primary) 8%, transparent);
@@ -871,29 +876,6 @@ onDestroy(() => {
871
876
  margin: 0;
872
877
  }
873
878
 
874
- .variation-form textarea {
875
- width: 100%;
876
- padding: 0.75rem;
877
- background: var(--smrt-color-surface-container, #1a1a1a);
878
- border: 1px solid var(--smrt-color-outline-variant, #444);
879
- border-radius: var(--smrt-radius-sm, 4px);
880
- color: inherit;
881
- font-family: inherit;
882
- font-size: var(--smrt-typography-body-large-size, 0.95rem);
883
- resize: vertical;
884
- transition: border-color 0.2s, box-shadow 0.2s;
885
- }
886
-
887
- .variation-form textarea:focus {
888
- outline: none;
889
- border-color: var(--smrt-color-primary, #3b82f6);
890
- box-shadow: inset 0 0 0 1px var(--smrt-color-primary, #3b82f6);
891
- }
892
-
893
- .variation-form textarea:disabled {
894
- opacity: 0.6;
895
- }
896
-
897
879
  .variation-error {
898
880
  color: var(--smrt-color-error, #ef4444);
899
881
  background: color-mix(in srgb, var(--smrt-color-error) 10%, transparent);
@@ -902,31 +884,20 @@ onDestroy(() => {
902
884
  font-size: var(--smrt-typography-body-medium-size, 0.85rem);
903
885
  }
904
886
 
905
- .generate-btn {
906
- display: flex;
907
- align-items: center;
908
- justify-content: center;
909
- gap: 0.5rem;
887
+ /* Migrated generate Button keeps its left-aligned pill look and inline
888
+ spinner. :global() pierces into the Button child (see #1589 scoping rule). */
889
+ .variation-form :global(.generate-btn--pill) {
910
890
  align-self: flex-start;
911
- background: var(--smrt-color-primary, #3b82f6);
912
- color: white;
913
- border: none;
914
891
  padding: 0.65rem 1.5rem;
915
892
  border-radius: var(--smrt-radius-full, 9999px);
916
893
  font-weight: var(--smrt-typography-weight-medium, 500);
917
- cursor: pointer;
918
894
  transition: filter 0.15s, opacity 0.15s;
919
895
  }
920
896
 
921
- .generate-btn:hover:not(:disabled) {
897
+ .variation-form :global(.generate-btn--pill:hover:not(:disabled)) {
922
898
  filter: brightness(1.1);
923
899
  }
924
900
 
925
- .generate-btn:disabled {
926
- opacity: 0.5;
927
- cursor: not-allowed;
928
- }
929
-
930
901
  @keyframes spin {
931
902
  to { transform: rotate(360deg); }
932
903
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ImageUploader.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ImageUploader.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,iBAAiB,EACjB,SAAS,EACT,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;AAGzB,KAAK,gBAAgB,GAAI;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,mDAAmD;IACnD,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC;IACrD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,CAAC,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC,EAAE,CAAC;IAC/D,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AA0ZF,QAAA,MAAM,aAAa,sDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"ImageUploader.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ImageUploader.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,iBAAiB,EACjB,SAAS,EACT,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;AAGzB,KAAK,gBAAgB,GAAI;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,iBAAiB,CAAC;IACjC,aAAa,CAAC,EAAE,mBAAmB,CAAC;IACpC,mDAAmD;IACnD,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC;IACrD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,CAAC,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC,EAAE,CAAC;IAC/D,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AA+ZF,QAAA,MAAM,aAAa,sDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-images",
3
- "version": "0.34.5",
3
+ "version": "0.34.7",
4
4
  "description": "Image asset management with AI-powered categorization, search, editing, and metadata extraction for SMRT framework",
5
5
  "type": "module",
6
+ "smrtRawPrimitives": "strict",
6
7
  "main": "./dist/index.js",
7
8
  "types": "./dist/index.d.ts",
8
9
  "files": [
@@ -38,12 +39,12 @@
38
39
  "@happyvertical/utils": "^0.74.7",
39
40
  "jimp": "^1.6.1",
40
41
  "sharp": "^0.34.5",
41
- "@happyvertical/smrt-assets": "0.34.5",
42
- "@happyvertical/smrt-core": "0.34.5",
43
- "@happyvertical/smrt-prompts": "0.34.5",
44
- "@happyvertical/smrt-tenancy": "0.34.5",
45
- "@happyvertical/smrt-types": "0.34.5",
46
- "@happyvertical/smrt-ui": "0.34.5"
42
+ "@happyvertical/smrt-assets": "0.34.7",
43
+ "@happyvertical/smrt-core": "0.34.7",
44
+ "@happyvertical/smrt-tenancy": "0.34.7",
45
+ "@happyvertical/smrt-prompts": "0.34.7",
46
+ "@happyvertical/smrt-types": "0.34.7",
47
+ "@happyvertical/smrt-ui": "0.34.7"
47
48
  },
48
49
  "peerDependencies": {
49
50
  "svelte": "^5.18.0"
@@ -59,8 +60,8 @@
59
60
  "typescript": "^5.9.3",
60
61
  "vite": "^7.3.1",
61
62
  "vitest": "^4.0.17",
62
- "@happyvertical/smrt-playground": "0.34.5",
63
- "@happyvertical/smrt-vitest": "0.34.5"
63
+ "@happyvertical/smrt-playground": "0.34.7",
64
+ "@happyvertical/smrt-vitest": "0.34.7"
64
65
  },
65
66
  "keywords": [
66
67
  "ai",