@happyvertical/smrt-images 0.34.4 → 0.34.6
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/dist/manifest.json +2 -2
- package/dist/smrt-knowledge.json +4 -4
- package/dist/svelte/components/AssetsGallery.svelte +12 -12
- package/dist/svelte/components/AssetsGallery.svelte.d.ts.map +1 -1
- package/dist/svelte/components/ImageEditor.svelte +38 -48
- package/dist/svelte/components/ImageEditor.svelte.d.ts.map +1 -1
- package/dist/svelte/components/ImageUploader.svelte +92 -91
- package/dist/svelte/components/ImageUploader.svelte.d.ts.map +1 -1
- package/package.json +10 -9
package/dist/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "1.0.0",
|
|
3
|
-
"timestamp":
|
|
3
|
+
"timestamp": 1782278454905,
|
|
4
4
|
"packageName": "@happyvertical/smrt-images",
|
|
5
|
-
"packageVersion": "0.34.
|
|
5
|
+
"packageVersion": "0.34.6",
|
|
6
6
|
"objects": {
|
|
7
7
|
"@happyvertical/smrt-images:Image": {
|
|
8
8
|
"name": "image",
|
package/dist/smrt-knowledge.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-06-
|
|
3
|
+
"generatedAt": "2026-06-24T05:20:55.298Z",
|
|
4
4
|
"packageName": "@happyvertical/smrt-images",
|
|
5
|
-
"packageVersion": "0.34.
|
|
5
|
+
"packageVersion": "0.34.6",
|
|
6
6
|
"sourceManifestPath": "dist/manifest.json",
|
|
7
7
|
"agentDocPath": "AGENTS.md",
|
|
8
8
|
"sourceHashes": {
|
|
9
|
-
"manifest": "
|
|
10
|
-
"packageJson": "
|
|
9
|
+
"manifest": "515005d7f498f29b64950227f55bf717187170d8804964551ce0d4d801ae2edf",
|
|
10
|
+
"packageJson": "92a1d613d753d7e30b192816fede1a97398d170365b5b4c1ca8d23c6a2912678",
|
|
11
11
|
"agents": "b0cf63bd78f00cc1729ea7f1425291f8e23ab5591161cbbf0d08f4b16ace07fd"
|
|
12
12
|
},
|
|
13
13
|
"exports": [
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
3
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
3
4
|
import { onMount } from 'svelte';
|
|
4
5
|
import { M } from '../i18n.js';
|
|
5
6
|
import type {
|
|
@@ -195,6 +196,7 @@ function handleDragStart(event: DragEvent, image: ImageLike) {
|
|
|
195
196
|
<div class="gallery-grid">
|
|
196
197
|
{#each images as image (image.id)}
|
|
197
198
|
{#if onSelect}
|
|
199
|
+
<!-- raw-primitive-allow: large draggable selection card wrapping rich image content, not a standard action button -->
|
|
198
200
|
<button
|
|
199
201
|
type="button"
|
|
200
202
|
class="gallery-item"
|
|
@@ -233,13 +235,14 @@ function handleDragStart(event: DragEvent, image: ImageLike) {
|
|
|
233
235
|
|
|
234
236
|
{#if hasMore}
|
|
235
237
|
<div class="load-more">
|
|
236
|
-
<
|
|
237
|
-
|
|
238
|
+
<Button
|
|
239
|
+
variant="ghost"
|
|
240
|
+
class="load-more-btn--pill"
|
|
241
|
+
onclick={() => loadImages(false)}
|
|
238
242
|
disabled={isLoading}
|
|
239
|
-
class="load-more-btn"
|
|
240
243
|
>
|
|
241
244
|
{isLoading ? 'Loading...' : 'Load More'}
|
|
242
|
-
</
|
|
245
|
+
</Button>
|
|
243
246
|
</div>
|
|
244
247
|
{/if}
|
|
245
248
|
</div>
|
|
@@ -421,23 +424,20 @@ function handleDragStart(event: DragEvent, image: ImageLike) {
|
|
|
421
424
|
padding: 1rem 0;
|
|
422
425
|
}
|
|
423
426
|
|
|
424
|
-
.
|
|
427
|
+
/* Bespoke pill look for the migrated Load More Button. :global() pierces into
|
|
428
|
+
the Button child's rendered <button>, which carries its own scope hash
|
|
429
|
+
(see #1589 scoping rule). */
|
|
430
|
+
.load-more :global(.load-more-btn--pill) {
|
|
425
431
|
padding: 0.75rem 2rem;
|
|
426
432
|
background: var(--smrt-color-surface-container-high, #242424);
|
|
427
433
|
color: var(--smrt-color-primary, #3b82f6);
|
|
428
434
|
border: 1px solid var(--smrt-color-outline-variant, #444);
|
|
429
435
|
border-radius: var(--smrt-radius-full, 9999px);
|
|
430
436
|
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
431
|
-
cursor: pointer;
|
|
432
437
|
transition: background 0.2s;
|
|
433
438
|
}
|
|
434
439
|
|
|
435
|
-
.load-more-btn:hover:not(:disabled) {
|
|
440
|
+
.load-more :global(.load-more-btn--pill:hover:not(:disabled)) {
|
|
436
441
|
background: var(--smrt-color-surface-container-highest, #333);
|
|
437
442
|
}
|
|
438
|
-
|
|
439
|
-
.load-more-btn:disabled {
|
|
440
|
-
opacity: 0.6;
|
|
441
|
-
cursor: not-allowed;
|
|
442
|
-
}
|
|
443
443
|
</style>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetsGallery.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/AssetsGallery.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AssetsGallery.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/AssetsGallery.svelte.ts"],"names":[],"mappings":"AAOA,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;AA2NF,QAAA,MAAM,aAAa,sDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
3
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
3
4
|
import { M } from '../i18n.js';
|
|
4
5
|
import type {
|
|
5
6
|
ImageConvertRequest,
|
|
@@ -180,7 +181,7 @@ async function handleAIEdit() {
|
|
|
180
181
|
<div class="header">
|
|
181
182
|
<h3>{t(M['images.image_editor.title'])}</h3>
|
|
182
183
|
{#if onCancel}
|
|
183
|
-
<
|
|
184
|
+
<Button variant="ghost" size="sm" class="close-btn--x" onclick={onCancel}>×</Button>
|
|
184
185
|
{/if}
|
|
185
186
|
</div>
|
|
186
187
|
|
|
@@ -194,8 +195,16 @@ async function handleAIEdit() {
|
|
|
194
195
|
|
|
195
196
|
<div class="editor-controls">
|
|
196
197
|
<div class="mode-selector">
|
|
197
|
-
<
|
|
198
|
-
|
|
198
|
+
<Button
|
|
199
|
+
variant={mode === 'standard' ? 'primary' : 'ghost'}
|
|
200
|
+
class="mode-tab"
|
|
201
|
+
onclick={() => (mode = 'standard')}
|
|
202
|
+
>{t(M['images.image_editor.standard_tools'])}</Button>
|
|
203
|
+
<Button
|
|
204
|
+
variant={mode === 'ai' ? 'primary' : 'ghost'}
|
|
205
|
+
class="mode-tab"
|
|
206
|
+
onclick={() => (mode = 'ai')}
|
|
207
|
+
>{t(M['images.image_editor.ai_edit'])}</Button>
|
|
199
208
|
</div>
|
|
200
209
|
|
|
201
210
|
{#if error}
|
|
@@ -212,10 +221,10 @@ async function handleAIEdit() {
|
|
|
212
221
|
<div class="row">
|
|
213
222
|
<label>Width <input type="number" bind:value={width} onfocus={() => isEditingDimensions = true} /></label>
|
|
214
223
|
<label>Height <input type="number" bind:value={height} onfocus={() => isEditingDimensions = true} /></label>
|
|
215
|
-
<
|
|
224
|
+
<Button variant="ghost" class="tonal-btn--filled" disabled={isProcessing} onclick={handleResize}>{t(M['images.image_editor.apply_resize'])}</Button>
|
|
216
225
|
</div>
|
|
217
226
|
{#if isEditingDimensions}
|
|
218
|
-
<div class="row"><
|
|
227
|
+
<div class="row"><Button variant="ghost" size="sm" class="text-btn--link" onclick={resetDimensions}>{t(M['images.image_editor.reset_dimensions'])}</Button></div>
|
|
219
228
|
{/if}
|
|
220
229
|
</div>
|
|
221
230
|
|
|
@@ -228,10 +237,10 @@ async function handleAIEdit() {
|
|
|
228
237
|
<div class="row">
|
|
229
238
|
<label>W <input type="number" bind:value={cropW} onfocus={() => isCropping = true} /></label>
|
|
230
239
|
<label>H <input type="number" bind:value={cropH} onfocus={() => isCropping = true} /></label>
|
|
231
|
-
<
|
|
240
|
+
<Button variant="ghost" class="tonal-btn--filled" disabled={isProcessing} onclick={handleCrop}>{t(M['images.image_editor.apply_crop'])}</Button>
|
|
232
241
|
</div>
|
|
233
242
|
{#if isCropping}
|
|
234
|
-
<div class="row"><
|
|
243
|
+
<div class="row"><Button variant="ghost" size="sm" class="text-btn--link" onclick={resetDimensions}>{t(M['images.image_editor.reset_dimensions'])}</Button></div>
|
|
235
244
|
{/if}
|
|
236
245
|
</div>
|
|
237
246
|
|
|
@@ -243,7 +252,7 @@ async function handleAIEdit() {
|
|
|
243
252
|
<option value="jpeg">JPEG</option>
|
|
244
253
|
<option value="png">PNG</option>
|
|
245
254
|
</select>
|
|
246
|
-
<
|
|
255
|
+
<Button variant="ghost" class="tonal-btn--filled" disabled={isProcessing} onclick={handleConvert}>Convert</Button>
|
|
247
256
|
</div>
|
|
248
257
|
</div>
|
|
249
258
|
{:else}
|
|
@@ -256,13 +265,14 @@ async function handleAIEdit() {
|
|
|
256
265
|
placeholder={t(M['images.image_editor.ai_prompt_placeholder'])}
|
|
257
266
|
rows="4"
|
|
258
267
|
></textarea>
|
|
259
|
-
<
|
|
260
|
-
|
|
268
|
+
<Button
|
|
269
|
+
variant="primary"
|
|
270
|
+
class="primary-btn--pill"
|
|
271
|
+
disabled={isProcessing || !prompt.trim()}
|
|
261
272
|
onclick={handleAIEdit}
|
|
262
|
-
class="primary-btn"
|
|
263
273
|
>
|
|
264
274
|
{isProcessing ? 'Generating...' : 'Apply AI Edit'}
|
|
265
|
-
</
|
|
275
|
+
</Button>
|
|
266
276
|
</div>
|
|
267
277
|
{/if}
|
|
268
278
|
</div>
|
|
@@ -297,12 +307,12 @@ async function handleAIEdit() {
|
|
|
297
307
|
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
298
308
|
}
|
|
299
309
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
310
|
+
/* The migrated close Button keeps ghost styling; only bump the × glyph size.
|
|
311
|
+
:global() pierces into the Button child's rendered <button> (see #1589). */
|
|
312
|
+
.header :global(.close-btn--x) {
|
|
303
313
|
color: inherit;
|
|
304
314
|
font-size: var(--smrt-typography-headline-small-size, 1.5rem);
|
|
305
|
-
|
|
315
|
+
line-height: 1;
|
|
306
316
|
}
|
|
307
317
|
|
|
308
318
|
.empty-state {
|
|
@@ -346,22 +356,14 @@ async function handleAIEdit() {
|
|
|
346
356
|
gap: 0.25rem;
|
|
347
357
|
}
|
|
348
358
|
|
|
349
|
-
|
|
359
|
+
/* The mode toggles are migrated Buttons (variant primary = active, ghost =
|
|
360
|
+
inactive). Keep the segmented-pill sizing by piercing into each Button's
|
|
361
|
+
rendered <button> (see #1589 scoping rule). */
|
|
362
|
+
.mode-selector :global(.mode-tab) {
|
|
350
363
|
flex: 1;
|
|
351
|
-
background: transparent;
|
|
352
|
-
border: none;
|
|
353
364
|
padding: 0.6rem 1rem;
|
|
354
|
-
color: var(--smrt-color-outline, #666);
|
|
355
|
-
cursor: pointer;
|
|
356
365
|
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
357
366
|
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
367
|
}
|
|
366
368
|
|
|
367
369
|
.tool-section h4 {
|
|
@@ -414,53 +416,41 @@ async function handleAIEdit() {
|
|
|
414
416
|
font-family: inherit;
|
|
415
417
|
}
|
|
416
418
|
|
|
417
|
-
|
|
419
|
+
/* Migrated Buttons keep their bespoke filled-tonal / text-link / pill looks.
|
|
420
|
+
:global() pierces into each Button's rendered <button> (see #1589). */
|
|
421
|
+
.tool-section :global(.tonal-btn--filled) {
|
|
418
422
|
background: var(--smrt-color-surface-container-highest, #333);
|
|
419
423
|
color: var(--smrt-color-primary, #3b82f6);
|
|
420
424
|
border: 1px solid var(--smrt-color-outline-variant, #444);
|
|
421
425
|
padding: 0.6rem 1.2rem;
|
|
422
426
|
border-radius: var(--smrt-radius-full, 9999px);
|
|
423
427
|
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
424
|
-
cursor: pointer;
|
|
425
428
|
transition: background 0.2s;
|
|
426
429
|
margin-top: 1.25rem;
|
|
427
430
|
}
|
|
428
431
|
|
|
429
|
-
.tonal-btn:hover:not(:disabled) {
|
|
432
|
+
.tool-section :global(.tonal-btn--filled:hover:not(:disabled)) {
|
|
430
433
|
background: var(--smrt-color-surface-container-high, #3f3f3f);
|
|
431
434
|
}
|
|
432
435
|
|
|
433
|
-
.text-btn {
|
|
434
|
-
background: transparent;
|
|
435
|
-
border: none;
|
|
436
|
-
cursor: pointer;
|
|
436
|
+
.tool-section :global(.text-btn--link) {
|
|
437
437
|
text-decoration: underline;
|
|
438
438
|
padding: 0;
|
|
439
439
|
}
|
|
440
|
-
.text-btn:hover {
|
|
440
|
+
.tool-section :global(.text-btn--link:hover) {
|
|
441
441
|
color: var(--smrt-color-on-surface, #fff);
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
-
.primary-btn {
|
|
445
|
-
background: var(--smrt-color-primary, #3b82f6);
|
|
446
|
-
color: white;
|
|
447
|
-
border: none;
|
|
444
|
+
.tool-section :global(.primary-btn--pill) {
|
|
448
445
|
padding: 0.6rem 1.5rem;
|
|
449
446
|
border-radius: var(--smrt-radius-full, 9999px);
|
|
450
447
|
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
451
|
-
cursor: pointer;
|
|
452
|
-
transition: background 0.2s, opacity 0.2s;
|
|
453
448
|
}
|
|
454
449
|
|
|
455
|
-
.primary-btn:hover:not(:disabled) {
|
|
450
|
+
.tool-section :global(.primary-btn--pill:hover:not(:disabled)) {
|
|
456
451
|
filter: brightness(1.1);
|
|
457
452
|
}
|
|
458
453
|
|
|
459
|
-
button:disabled {
|
|
460
|
-
opacity: 0.5;
|
|
461
|
-
cursor: not-allowed;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
454
|
.hint {
|
|
465
455
|
font-size: var(--smrt-typography-body-small-size, 0.8rem);
|
|
466
456
|
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":"
|
|
1
|
+
{"version":3,"file":"ImageEditor.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ImageEditor.svelte.ts"],"names":[],"mappings":"AAMA,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;AAkQF,QAAA,MAAM,WAAW,sDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
3
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
3
4
|
import { onDestroy } from 'svelte';
|
|
4
5
|
import { M } from '../i18n.js';
|
|
5
6
|
import type {
|
|
@@ -276,14 +277,14 @@ onDestroy(() => {
|
|
|
276
277
|
{#if selectedImage}
|
|
277
278
|
<!-- Gallery Confirmation Step -->
|
|
278
279
|
<div class="header">
|
|
279
|
-
<
|
|
280
|
+
<Button variant="ghost" size="sm" class="back-btn--link" onclick={handleBackToChooser}>
|
|
280
281
|
<svg viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
281
282
|
<polyline points="15 18 9 12 15 6"></polyline>
|
|
282
283
|
</svg>
|
|
283
284
|
Back
|
|
284
|
-
</
|
|
285
|
+
</Button>
|
|
285
286
|
{#if onCancel}
|
|
286
|
-
<
|
|
287
|
+
<Button variant="ghost" size="sm" class="close-btn--x" onclick={onCancel}>×</Button>
|
|
287
288
|
{/if}
|
|
288
289
|
</div>
|
|
289
290
|
|
|
@@ -298,21 +299,22 @@ onDestroy(() => {
|
|
|
298
299
|
</div>
|
|
299
300
|
|
|
300
301
|
<div class="confirm-actions">
|
|
301
|
-
<
|
|
302
|
+
<Button variant="primary" class="primary-btn--pill" onclick={handleConfirmOriginal}>
|
|
302
303
|
{t(M['images.image_uploader.select_image'])}
|
|
303
|
-
</
|
|
304
|
-
<
|
|
305
|
-
|
|
306
|
-
class=
|
|
307
|
-
|
|
308
|
-
|
|
304
|
+
</Button>
|
|
305
|
+
<Button
|
|
306
|
+
variant="ghost"
|
|
307
|
+
class={showVariation
|
|
308
|
+
? 'variation-toggle--chip variation-toggle--active'
|
|
309
|
+
: 'variation-toggle--chip'}
|
|
310
|
+
onclick={() => (showVariation = !showVariation)}
|
|
309
311
|
>
|
|
310
312
|
<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
311
313
|
<path d="M12 20h9"></path>
|
|
312
314
|
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
|
|
313
315
|
</svg>
|
|
314
316
|
{t(M['images.image_uploader.create_variation'])}
|
|
315
|
-
</
|
|
317
|
+
</Button>
|
|
316
318
|
</div>
|
|
317
319
|
|
|
318
320
|
{#if showVariation}
|
|
@@ -327,10 +329,10 @@ onDestroy(() => {
|
|
|
327
329
|
{#if variationError}
|
|
328
330
|
<div class="variation-error">{variationError}</div>
|
|
329
331
|
{/if}
|
|
330
|
-
<
|
|
331
|
-
|
|
332
|
-
class="generate-btn"
|
|
333
|
-
disabled={isGenerating || !variationPrompt.trim()}
|
|
332
|
+
<Button
|
|
333
|
+
variant="primary"
|
|
334
|
+
class="generate-btn--pill"
|
|
335
|
+
disabled={isGenerating || !variationPrompt.trim()}
|
|
334
336
|
onclick={handleGenerateVariation}
|
|
335
337
|
>
|
|
336
338
|
{#if isGenerating}
|
|
@@ -339,7 +341,7 @@ onDestroy(() => {
|
|
|
339
341
|
{:else}
|
|
340
342
|
{t(M['images.image_uploader.generate_variation'])}
|
|
341
343
|
{/if}
|
|
342
|
-
</
|
|
344
|
+
</Button>
|
|
343
345
|
</div>
|
|
344
346
|
{/if}
|
|
345
347
|
</div>
|
|
@@ -349,22 +351,38 @@ onDestroy(() => {
|
|
|
349
351
|
<div class="header">
|
|
350
352
|
<h3>{t(M['images.image_uploader.choose_image'])}</h3>
|
|
351
353
|
{#if onCancel}
|
|
352
|
-
<
|
|
354
|
+
<Button variant="ghost" size="sm" class="close-btn--x" onclick={onCancel}>×</Button>
|
|
353
355
|
{/if}
|
|
354
356
|
</div>
|
|
355
357
|
|
|
356
358
|
<div class="tabs">
|
|
357
359
|
{#if allowedTabs.includes('gallery')}
|
|
358
|
-
<
|
|
360
|
+
<Button
|
|
361
|
+
variant="ghost"
|
|
362
|
+
class={activeTab === 'gallery' ? 'uploader-tab uploader-tab--active' : 'uploader-tab'}
|
|
363
|
+
onclick={() => (activeTab = 'gallery')}
|
|
364
|
+
>Gallery</Button>
|
|
359
365
|
{/if}
|
|
360
366
|
{#if allowedTabs.includes('upload')}
|
|
361
|
-
<
|
|
367
|
+
<Button
|
|
368
|
+
variant="ghost"
|
|
369
|
+
class={activeTab === 'upload' ? 'uploader-tab uploader-tab--active' : 'uploader-tab'}
|
|
370
|
+
onclick={() => (activeTab = 'upload')}
|
|
371
|
+
>Upload</Button>
|
|
362
372
|
{/if}
|
|
363
373
|
{#if allowedTabs.includes('camera')}
|
|
364
|
-
<
|
|
374
|
+
<Button
|
|
375
|
+
variant="ghost"
|
|
376
|
+
class={activeTab === 'camera' ? 'uploader-tab uploader-tab--active' : 'uploader-tab'}
|
|
377
|
+
onclick={() => (activeTab = 'camera')}
|
|
378
|
+
>Camera</Button>
|
|
365
379
|
{/if}
|
|
366
380
|
{#if allowedTabs.includes('external')}
|
|
367
|
-
<
|
|
381
|
+
<Button
|
|
382
|
+
variant="ghost"
|
|
383
|
+
class={activeTab === 'external' ? 'uploader-tab uploader-tab--active' : 'uploader-tab'}
|
|
384
|
+
onclick={() => (activeTab = 'external')}
|
|
385
|
+
>{t(M['images.image_uploader.external_url'])}</Button>
|
|
368
386
|
{/if}
|
|
369
387
|
</div>
|
|
370
388
|
|
|
@@ -401,7 +419,7 @@ onDestroy(() => {
|
|
|
401
419
|
</div>
|
|
402
420
|
<p>{t(M['images.image_uploader.drag_and_drop'])}</p>
|
|
403
421
|
<span class="divider">or</span>
|
|
404
|
-
<
|
|
422
|
+
<Button variant="primary" class="browse-btn--decorative">{t(M['images.image_uploader.browse_files'])}</Button>
|
|
405
423
|
<input
|
|
406
424
|
type="file"
|
|
407
425
|
accept="image/*"
|
|
@@ -419,7 +437,7 @@ onDestroy(() => {
|
|
|
419
437
|
{#if cameraError}
|
|
420
438
|
<div class="error-panel">
|
|
421
439
|
<p>{cameraError}</p>
|
|
422
|
-
<
|
|
440
|
+
<Button variant="secondary" class="try-again-btn" onclick={startCamera}>{t(M['images.image_uploader.try_again'])}</Button>
|
|
423
441
|
</div>
|
|
424
442
|
{:else}
|
|
425
443
|
<div class="video-container">
|
|
@@ -429,9 +447,9 @@ onDestroy(() => {
|
|
|
429
447
|
<div class="loading-overlay">{t(M['images.image_uploader.starting_camera'])}</div>
|
|
430
448
|
{/if}
|
|
431
449
|
</div>
|
|
432
|
-
<
|
|
450
|
+
<Button variant="primary" class="capture-btn--pill" disabled={!isCameraActive} onclick={takePicture}>
|
|
433
451
|
{t(M['images.image_uploader.take_picture'])}
|
|
434
|
-
</
|
|
452
|
+
</Button>
|
|
435
453
|
<canvas bind:this={canvasElement} style="display: none;"></canvas>
|
|
436
454
|
{/if}
|
|
437
455
|
</div>
|
|
@@ -446,14 +464,14 @@ onDestroy(() => {
|
|
|
446
464
|
placeholder={t(M['images.image_uploader.external_url_placeholder'])}
|
|
447
465
|
onkeydown={(e) => e.key === 'Enter' && handleExternalSubmit()}
|
|
448
466
|
/>
|
|
449
|
-
<
|
|
450
|
-
|
|
451
|
-
class="submit-btn"
|
|
452
|
-
disabled={!externalUrl.trim()}
|
|
467
|
+
<Button
|
|
468
|
+
variant="primary"
|
|
469
|
+
class="submit-btn--inline"
|
|
470
|
+
disabled={!externalUrl.trim()}
|
|
453
471
|
onclick={handleExternalSubmit}
|
|
454
472
|
>
|
|
455
473
|
Add
|
|
456
|
-
</
|
|
474
|
+
</Button>
|
|
457
475
|
</div>
|
|
458
476
|
</div>
|
|
459
477
|
{/if}
|
|
@@ -488,16 +506,16 @@ onDestroy(() => {
|
|
|
488
506
|
font-size: var(--smrt-typography-title-medium-size, 1.15rem);
|
|
489
507
|
}
|
|
490
508
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
509
|
+
/* Migrated close Button keeps its muted ghost look; only the × glyph sizing
|
|
510
|
+
is custom. :global() pierces into the Button child's rendered <button>
|
|
511
|
+
(see #1589 scoping rule). */
|
|
512
|
+
.header :global(.close-btn--x) {
|
|
494
513
|
color: var(--smrt-color-outline, #888);
|
|
495
514
|
font-size: var(--smrt-typography-headline-small-size, 1.5rem);
|
|
496
515
|
line-height: 1;
|
|
497
|
-
cursor: pointer;
|
|
498
516
|
}
|
|
499
|
-
|
|
500
|
-
.close-btn:hover {
|
|
517
|
+
|
|
518
|
+
.header :global(.close-btn--x:hover) {
|
|
501
519
|
color: var(--smrt-color-on-surface, #fff);
|
|
502
520
|
}
|
|
503
521
|
|
|
@@ -507,27 +525,29 @@ onDestroy(() => {
|
|
|
507
525
|
border-bottom: 1px solid var(--smrt-color-outline-variant, #333);
|
|
508
526
|
}
|
|
509
527
|
|
|
510
|
-
|
|
528
|
+
/* The tab strip is migrated to ghost Buttons; the active state is the bottom
|
|
529
|
+
underline rather than a filled variant. :global() pierces into each
|
|
530
|
+
Button's rendered <button> (see #1589 scoping rule). */
|
|
531
|
+
.tabs :global(.uploader-tab) {
|
|
511
532
|
flex: 1;
|
|
512
|
-
background: transparent;
|
|
513
533
|
border: none;
|
|
514
534
|
border-bottom: 2px solid var(--smrt-color-outline-variant, #333);
|
|
515
535
|
padding: 1rem;
|
|
516
536
|
color: var(--smrt-color-outline, #888);
|
|
517
537
|
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
518
|
-
|
|
538
|
+
border-radius: 0;
|
|
519
539
|
transition: all 0.2s;
|
|
520
540
|
text-transform: uppercase;
|
|
521
541
|
font-size: var(--smrt-typography-label-large-size, 0.85rem);
|
|
522
542
|
letter-spacing: var(--smrt-typography-label-large-tracking, 0.5px);
|
|
523
543
|
}
|
|
524
544
|
|
|
525
|
-
.tabs
|
|
545
|
+
.tabs :global(.uploader-tab:hover) {
|
|
526
546
|
color: var(--smrt-color-on-surface-variant, #ccc);
|
|
527
547
|
background: color-mix(in srgb, var(--smrt-color-on-surface, #fff) 2%, transparent);
|
|
528
548
|
}
|
|
529
549
|
|
|
530
|
-
.tabs
|
|
550
|
+
.tabs :global(.uploader-tab--active) {
|
|
531
551
|
color: var(--smrt-color-primary, #3b82f6);
|
|
532
552
|
border-bottom: 2px solid var(--smrt-color-primary, #3b82f6);
|
|
533
553
|
background: transparent;
|
|
@@ -594,10 +614,10 @@ onDestroy(() => {
|
|
|
594
614
|
margin-bottom: 1rem;
|
|
595
615
|
}
|
|
596
616
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
617
|
+
/* Decorative label inside the click-catching upload area; the parent handles
|
|
618
|
+
clicks, so this migrated Button stays inert. :global() pierces into the
|
|
619
|
+
Button child's rendered <button> (see #1589 scoping rule). */
|
|
620
|
+
.upload-area :global(.browse-btn--decorative) {
|
|
601
621
|
padding: 0.5rem 1.5rem;
|
|
602
622
|
border-radius: var(--smrt-radius-full, 9999px);
|
|
603
623
|
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
@@ -648,21 +668,18 @@ onDestroy(() => {
|
|
|
648
668
|
color: white;
|
|
649
669
|
}
|
|
650
670
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
border: none;
|
|
671
|
+
/* Large primary capture action; keeps its oversized pill sizing over the
|
|
672
|
+
primary variant. :global() pierces into the Button child (see #1589). */
|
|
673
|
+
.camera-area :global(.capture-btn--pill) {
|
|
655
674
|
padding: 1rem 3rem;
|
|
656
675
|
border-radius: var(--smrt-radius-full, 9999px);
|
|
657
676
|
font-size: var(--smrt-typography-body-large-size, 1.1rem);
|
|
658
677
|
font-weight: var(--smrt-typography-weight-semibold, 600);
|
|
659
|
-
cursor: pointer;
|
|
660
678
|
}
|
|
661
679
|
|
|
662
|
-
.capture-btn:disabled {
|
|
680
|
+
.camera-area :global(.capture-btn--pill:disabled) {
|
|
663
681
|
background: var(--smrt-color-outline-variant, #444);
|
|
664
682
|
color: var(--smrt-color-outline, #888);
|
|
665
|
-
cursor: not-allowed;
|
|
666
683
|
}
|
|
667
684
|
|
|
668
685
|
.error-panel {
|
|
@@ -677,13 +694,14 @@ onDestroy(() => {
|
|
|
677
694
|
margin-bottom: 1rem;
|
|
678
695
|
}
|
|
679
696
|
|
|
680
|
-
|
|
697
|
+
/* Migrated "try again" Button keeps its surface-tinted look over the
|
|
698
|
+
secondary variant. :global() pierces into the Button child (see #1589). */
|
|
699
|
+
.error-panel :global(.try-again-btn) {
|
|
681
700
|
background: var(--smrt-color-surface-container-high, #242424);
|
|
682
701
|
color: white;
|
|
683
702
|
border: 1px solid var(--smrt-color-outline-variant, #444);
|
|
684
703
|
padding: 0.5rem 1rem;
|
|
685
704
|
border-radius: var(--smrt-radius-sm, 4px);
|
|
686
|
-
cursor: pointer;
|
|
687
705
|
}
|
|
688
706
|
|
|
689
707
|
/* External Tab */
|
|
@@ -725,38 +743,35 @@ onDestroy(() => {
|
|
|
725
743
|
box-shadow: inset 0 0 0 1px var(--smrt-color-primary, #3b82f6);
|
|
726
744
|
}
|
|
727
745
|
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
746
|
+
/* Migrated Add Button sits in the URL input group; keeps square-ish radius
|
|
747
|
+
and zero vertical padding. :global() pierces into the Button child
|
|
748
|
+
(see #1589 scoping rule). */
|
|
749
|
+
.input-group :global(.submit-btn--inline) {
|
|
732
750
|
padding: 0 1.5rem;
|
|
733
751
|
border-radius: var(--smrt-radius-md, 6px);
|
|
734
752
|
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
735
|
-
cursor: pointer;
|
|
736
753
|
}
|
|
737
754
|
|
|
738
|
-
.submit-btn:disabled {
|
|
755
|
+
.input-group :global(.submit-btn--inline:disabled) {
|
|
739
756
|
background: var(--smrt-color-surface-container-highest, #333);
|
|
740
757
|
color: var(--smrt-color-outline, #666);
|
|
741
|
-
cursor: not-allowed;
|
|
742
758
|
}
|
|
743
759
|
|
|
744
760
|
/* --- Back Button --- */
|
|
745
|
-
.
|
|
761
|
+
/* Migrated ghost Button keeps the icon+label inline layout and tinted hover.
|
|
762
|
+
:global() pierces into the Button child (see #1589 scoping rule). */
|
|
763
|
+
.header :global(.back-btn--link) {
|
|
746
764
|
display: flex;
|
|
747
765
|
align-items: center;
|
|
748
766
|
gap: 0.35rem;
|
|
749
|
-
background: transparent;
|
|
750
|
-
border: none;
|
|
751
767
|
color: var(--smrt-color-primary, #3b82f6);
|
|
752
768
|
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
753
|
-
cursor: pointer;
|
|
754
769
|
padding: 0.25rem 0.5rem;
|
|
755
770
|
border-radius: var(--smrt-radius-sm, 4px);
|
|
756
771
|
transition: background 0.15s;
|
|
757
772
|
}
|
|
758
773
|
|
|
759
|
-
.back-btn:hover {
|
|
774
|
+
.header :global(.back-btn--link:hover) {
|
|
760
775
|
background: color-mix(in srgb, var(--smrt-color-primary) 8%, transparent);
|
|
761
776
|
}
|
|
762
777
|
|
|
@@ -814,22 +829,20 @@ onDestroy(() => {
|
|
|
814
829
|
margin-top: 0.5rem;
|
|
815
830
|
}
|
|
816
831
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
border: none;
|
|
832
|
+
/* Migrated confirm-action Buttons keep their pill / chip looks. :global()
|
|
833
|
+
pierces into each Button's rendered <button> (see #1589 scoping rule). */
|
|
834
|
+
.confirm-actions :global(.primary-btn--pill) {
|
|
821
835
|
padding: 0.65rem 1.5rem;
|
|
822
836
|
border-radius: var(--smrt-radius-full, 9999px);
|
|
823
837
|
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
824
|
-
cursor: pointer;
|
|
825
838
|
transition: filter 0.15s;
|
|
826
839
|
}
|
|
827
840
|
|
|
828
|
-
.primary-btn:hover {
|
|
841
|
+
.confirm-actions :global(.primary-btn--pill:hover) {
|
|
829
842
|
filter: brightness(1.1);
|
|
830
843
|
}
|
|
831
844
|
|
|
832
|
-
.variation-toggle {
|
|
845
|
+
.confirm-actions :global(.variation-toggle--chip) {
|
|
833
846
|
display: flex;
|
|
834
847
|
align-items: center;
|
|
835
848
|
gap: 0.4rem;
|
|
@@ -839,15 +852,14 @@ onDestroy(() => {
|
|
|
839
852
|
padding: 0.65rem 1.25rem;
|
|
840
853
|
border-radius: var(--smrt-radius-full, 9999px);
|
|
841
854
|
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
842
|
-
cursor: pointer;
|
|
843
855
|
transition: all 0.2s;
|
|
844
856
|
}
|
|
845
857
|
|
|
846
|
-
.variation-toggle:hover {
|
|
858
|
+
.confirm-actions :global(.variation-toggle--chip:hover) {
|
|
847
859
|
background: var(--smrt-color-surface-container-high, #3f3f3f);
|
|
848
860
|
}
|
|
849
861
|
|
|
850
|
-
.variation-toggle
|
|
862
|
+
.confirm-actions :global(.variation-toggle--active) {
|
|
851
863
|
color: var(--smrt-color-primary, #3b82f6);
|
|
852
864
|
border-color: var(--smrt-color-primary, #3b82f6);
|
|
853
865
|
background: color-mix(in srgb, var(--smrt-color-primary) 8%, transparent);
|
|
@@ -902,31 +914,20 @@ onDestroy(() => {
|
|
|
902
914
|
font-size: var(--smrt-typography-body-medium-size, 0.85rem);
|
|
903
915
|
}
|
|
904
916
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
justify-content: center;
|
|
909
|
-
gap: 0.5rem;
|
|
917
|
+
/* Migrated generate Button keeps its left-aligned pill look and inline
|
|
918
|
+
spinner. :global() pierces into the Button child (see #1589 scoping rule). */
|
|
919
|
+
.variation-form :global(.generate-btn--pill) {
|
|
910
920
|
align-self: flex-start;
|
|
911
|
-
background: var(--smrt-color-primary, #3b82f6);
|
|
912
|
-
color: white;
|
|
913
|
-
border: none;
|
|
914
921
|
padding: 0.65rem 1.5rem;
|
|
915
922
|
border-radius: var(--smrt-radius-full, 9999px);
|
|
916
923
|
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
917
|
-
cursor: pointer;
|
|
918
924
|
transition: filter 0.15s, opacity 0.15s;
|
|
919
925
|
}
|
|
920
926
|
|
|
921
|
-
.generate-btn:hover:not(:disabled) {
|
|
927
|
+
.variation-form :global(.generate-btn--pill:hover:not(:disabled)) {
|
|
922
928
|
filter: brightness(1.1);
|
|
923
929
|
}
|
|
924
930
|
|
|
925
|
-
.generate-btn:disabled {
|
|
926
|
-
opacity: 0.5;
|
|
927
|
-
cursor: not-allowed;
|
|
928
|
-
}
|
|
929
|
-
|
|
930
931
|
@keyframes spin {
|
|
931
932
|
to { transform: rotate(360deg); }
|
|
932
933
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ImageUploader.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ImageUploader.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ImageUploader.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ImageUploader.svelte.ts"],"names":[],"mappings":"AAOA,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;AA6ZF,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.
|
|
3
|
+
"version": "0.34.6",
|
|
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-buttons",
|
|
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.
|
|
42
|
-
"@happyvertical/smrt-core": "0.34.
|
|
43
|
-
"@happyvertical/smrt-
|
|
44
|
-
"@happyvertical/smrt-
|
|
45
|
-
"@happyvertical/smrt-
|
|
46
|
-
"@happyvertical/smrt-ui": "0.34.
|
|
42
|
+
"@happyvertical/smrt-assets": "0.34.6",
|
|
43
|
+
"@happyvertical/smrt-core": "0.34.6",
|
|
44
|
+
"@happyvertical/smrt-tenancy": "0.34.6",
|
|
45
|
+
"@happyvertical/smrt-types": "0.34.6",
|
|
46
|
+
"@happyvertical/smrt-prompts": "0.34.6",
|
|
47
|
+
"@happyvertical/smrt-ui": "0.34.6"
|
|
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-
|
|
63
|
-
"@happyvertical/smrt-
|
|
63
|
+
"@happyvertical/smrt-playground": "0.34.6",
|
|
64
|
+
"@happyvertical/smrt-vitest": "0.34.6"
|
|
64
65
|
},
|
|
65
66
|
"keywords": [
|
|
66
67
|
"ai",
|