@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.
- package/dist/manifest.json +2 -2
- package/dist/smrt-knowledge.json +4 -4
- package/dist/svelte/components/AssetsGallery.svelte +31 -53
- package/dist/svelte/components/AssetsGallery.svelte.d.ts.map +1 -1
- package/dist/svelte/components/ImageEditor.svelte +62 -78
- package/dist/svelte/components/ImageEditor.svelte.d.ts.map +1 -1
- package/dist/svelte/components/ImageUploader.svelte +110 -139
- 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": 1782290139382,
|
|
4
4
|
"packageName": "@happyvertical/smrt-images",
|
|
5
|
-
"packageVersion": "0.34.
|
|
5
|
+
"packageVersion": "0.34.7",
|
|
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-24T08:35:39.740Z",
|
|
4
4
|
"packageName": "@happyvertical/smrt-images",
|
|
5
|
-
"packageVersion": "0.34.
|
|
5
|
+
"packageVersion": "0.34.7",
|
|
6
6
|
"sourceManifestPath": "dist/manifest.json",
|
|
7
7
|
"agentDocPath": "AGENTS.md",
|
|
8
8
|
"sourceHashes": {
|
|
9
|
-
"manifest": "
|
|
10
|
-
"packageJson": "
|
|
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
|
-
<
|
|
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
|
-
</
|
|
174
|
-
|
|
175
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
237
|
-
|
|
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
|
-
</
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
|
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
|
-
.
|
|
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":"
|
|
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
|
-
<
|
|
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
|
-
<
|
|
198
|
-
|
|
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 <
|
|
214
|
-
<label>Height <
|
|
215
|
-
<
|
|
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"><
|
|
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 <
|
|
226
|
-
<label>Y <
|
|
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 <
|
|
230
|
-
<label>H <
|
|
231
|
-
<
|
|
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"><
|
|
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
|
-
</
|
|
246
|
-
<
|
|
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
|
-
<
|
|
264
|
+
<Textarea
|
|
265
|
+
class="ai-prompt"
|
|
255
266
|
bind:value={prompt}
|
|
256
267
|
placeholder={t(M['images.image_editor.ai_prompt_placeholder'])}
|
|
257
|
-
rows=
|
|
258
|
-
|
|
259
|
-
<
|
|
260
|
-
|
|
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
|
-
</
|
|
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
|
-
|
|
301
|
-
|
|
302
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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":"
|
|
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
|
-
<
|
|
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
|
-
</
|
|
286
|
+
</Button>
|
|
285
287
|
{#if onCancel}
|
|
286
|
-
<
|
|
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
|
-
<
|
|
303
|
+
<Button variant="primary" class="primary-btn--pill" onclick={handleConfirmOriginal}>
|
|
302
304
|
{t(M['images.image_uploader.select_image'])}
|
|
303
|
-
</
|
|
304
|
-
<
|
|
305
|
-
|
|
306
|
-
class=
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
</
|
|
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
|
-
<
|
|
324
|
+
<Textarea
|
|
322
325
|
bind:value={variationPrompt}
|
|
323
326
|
placeholder={t(M['images.image_uploader.variation_prompt_placeholder'])}
|
|
324
|
-
rows=
|
|
327
|
+
rows={3}
|
|
325
328
|
disabled={isGenerating}
|
|
326
|
-
|
|
329
|
+
/>
|
|
327
330
|
{#if variationError}
|
|
328
331
|
<div class="variation-error">{variationError}</div>
|
|
329
332
|
{/if}
|
|
330
|
-
<
|
|
331
|
-
|
|
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
|
-
</
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
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
|
-
<
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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
|
-
<
|
|
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
|
-
<
|
|
452
|
+
<Button variant="primary" class="capture-btn--pill" disabled={!isCameraActive} onclick={takePicture}>
|
|
433
453
|
{t(M['images.image_uploader.take_picture'])}
|
|
434
|
-
</
|
|
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
|
-
<
|
|
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
|
-
<
|
|
450
|
-
|
|
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
|
-
</
|
|
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
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
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
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
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
|
-
.
|
|
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
|
-
|
|
818
|
-
|
|
819
|
-
|
|
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
|
|
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
|
-
|
|
906
|
-
|
|
907
|
-
|
|
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":"
|
|
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.
|
|
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.
|
|
42
|
-
"@happyvertical/smrt-core": "0.34.
|
|
43
|
-
"@happyvertical/smrt-
|
|
44
|
-
"@happyvertical/smrt-
|
|
45
|
-
"@happyvertical/smrt-types": "0.34.
|
|
46
|
-
"@happyvertical/smrt-ui": "0.34.
|
|
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.
|
|
63
|
-
"@happyvertical/smrt-vitest": "0.34.
|
|
63
|
+
"@happyvertical/smrt-playground": "0.34.7",
|
|
64
|
+
"@happyvertical/smrt-vitest": "0.34.7"
|
|
64
65
|
},
|
|
65
66
|
"keywords": [
|
|
66
67
|
"ai",
|