@widgetic/creator 0.3.71 → 0.3.73

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.
Files changed (33) hide show
  1. package/dist/CreatorApp.svelte +882 -78
  2. package/dist/components/BrandKitPanel.svelte +556 -0
  3. package/dist/components/BrandKitPanel.svelte.d.ts +32 -0
  4. package/dist/components/DesignQuickEdit.svelte +218 -0
  5. package/dist/components/DesignQuickEdit.svelte.d.ts +33 -0
  6. package/dist/components/FileUrlSourceControl.svelte +69 -0
  7. package/dist/components/FileUrlSourceControl.svelte.d.ts +15 -0
  8. package/dist/components/VersionDropdownRow.svelte +86 -1
  9. package/dist/components/VersionDropdownRow.svelte.d.ts +10 -0
  10. package/dist/components/WidgetDetails.svelte +19 -2
  11. package/dist/components/WidgetShapeLivePreviews.svelte +8 -3
  12. package/dist/retiredSurface.test.d.ts +1 -0
  13. package/dist/retiredSurface.test.js +113 -0
  14. package/dist/stores/websocketStore.d.ts +3 -0
  15. package/dist/stores/websocketStore.js +4 -0
  16. package/dist/utils/canvasShapeSnapClient.d.ts +27 -0
  17. package/dist/utils/canvasShapeSnapClient.js +67 -0
  18. package/dist/utils/designIntentJevClient.d.ts +32 -0
  19. package/dist/utils/designIntentJevClient.js +51 -0
  20. package/dist/utils/designIntentResolver.d.ts +107 -0
  21. package/dist/utils/designIntentResolver.js +509 -0
  22. package/dist/utils/designIntentResolver.test.d.ts +1 -0
  23. package/dist/utils/designIntentResolver.test.js +246 -0
  24. package/dist/utils/designIntentTelemetry.d.ts +176 -0
  25. package/dist/utils/designIntentTelemetry.js +474 -0
  26. package/dist/utils/designIntentTelemetry.test.d.ts +1 -0
  27. package/dist/utils/designIntentTelemetry.test.js +385 -0
  28. package/dist/utils/designQuickEditValues.d.ts +51 -0
  29. package/dist/utils/designQuickEditValues.js +77 -0
  30. package/dist/utils/designQuickEditValues.test.d.ts +1 -0
  31. package/dist/utils/designQuickEditValues.test.js +100 -0
  32. package/dist/utils/pinScrollToBottom.js +9 -2
  33. package/package.json +2 -2
@@ -0,0 +1,556 @@
1
+ <script lang="ts">
2
+ import { writable } from 'svelte/store';
3
+ import { DropdownMenu, Portal } from '@widgetic/design-system';
4
+ import { FontInputController } from '@widgetic/editor';
5
+ import FileUrlSourceControl from './FileUrlSourceControl.svelte';
6
+ import HoverTooltip from './HoverTooltip.svelte';
7
+
8
+ interface FontModel {
9
+ family: string;
10
+ weight: string;
11
+ category: string;
12
+ }
13
+
14
+ interface Props {
15
+ class?: string;
16
+ style?: string;
17
+ part?: 'checks' | 'panel';
18
+ brandKitEnabled?: boolean;
19
+ librariesEnabled?: boolean;
20
+ showPresetLibraries?: boolean;
21
+ suspendPickers?: boolean;
22
+ primary?: string;
23
+ secondary?: string;
24
+ title?: string;
25
+ sub?: string;
26
+ fonts?: string[];
27
+ libraries?: string[];
28
+ libraryGitUrl?: string;
29
+ libraryFileName?: string;
30
+ libraryUploads?: { name: string; content: string; encoding: string }[];
31
+ fontUploads?: { name: string; content: string; encoding: string }[];
32
+ showLanguage?: boolean;
33
+ language?: string;
34
+ }
35
+
36
+ const maxFonts = 3;
37
+
38
+ let {
39
+ class: className = '',
40
+ style = '',
41
+ part = 'checks',
42
+ brandKitEnabled = $bindable(false),
43
+ librariesEnabled = $bindable(false),
44
+ showPresetLibraries = false,
45
+ suspendPickers = false,
46
+ primary = $bindable('#314F2F'),
47
+ secondary = $bindable('#7BA668'),
48
+ title = $bindable('#0F2010'),
49
+ sub = $bindable('#5C6B58'),
50
+ fonts = $bindable(['Inter']),
51
+ libraries = $bindable([] as string[]),
52
+ libraryGitUrl = $bindable(''),
53
+ libraryFileName = $bindable(''),
54
+ libraryUploads = $bindable([] as { name: string; content: string; encoding: string }[]),
55
+ fontUploads = $bindable([] as { name: string; content: string; encoding: string }[]),
56
+ showLanguage = false,
57
+ language = $bindable('svelte'),
58
+ }: Props = $props();
59
+
60
+ const libraryChoices = [
61
+ { id: 'chart.js/auto', label: 'Chart.js' },
62
+ { id: 'd3', label: 'D3' },
63
+ { id: 'gsap', label: 'GSAP' },
64
+ { id: 'three', label: 'Three.js' },
65
+ ];
66
+
67
+ // FontInputController requires a store for its open-dropdown contract.
68
+ const openDropdowns = writable(new Set<string>());
69
+
70
+ let fontModels = $state<FontModel[]>(
71
+ (fonts.length ? fonts : ['Inter']).slice(0, maxFonts).map((family) => ({
72
+ family,
73
+ weight: '400',
74
+ category: 'sans-serif',
75
+ })),
76
+ );
77
+
78
+ function publishFonts() {
79
+ fonts = fontModels.map((model) => model.family);
80
+ }
81
+
82
+ function toggleDropdown(id: string) {
83
+ openDropdowns.update((current) => {
84
+ const next = new Set(current);
85
+ if (next.has(id)) next.delete(id);
86
+ else {
87
+ next.clear();
88
+ next.add(id);
89
+ }
90
+ return next;
91
+ });
92
+ }
93
+
94
+ function updateFont(index: number, next: { family?: string } | null) {
95
+ const family = next?.family?.trim() || 'Inter';
96
+ if (fontModels[index]?.family === family) return;
97
+ fontModels[index] = { family, weight: '400', category: 'sans-serif' };
98
+ publishFonts();
99
+ }
100
+
101
+ function addFont() {
102
+ if (fontModels.length >= maxFonts) return;
103
+ fontModels = [...fontModels, { family: 'Inter', weight: '400', category: 'sans-serif' }];
104
+ publishFonts();
105
+ }
106
+
107
+ function removeFont(index: number) {
108
+ if (fontModels.length <= 1) return;
109
+ fontModels = fontModels.filter((_, entryIndex) => entryIndex !== index);
110
+ publishFonts();
111
+ }
112
+
113
+ function toggleLibrary(id: string) {
114
+ const next = libraries.includes(id)
115
+ ? libraries.filter((entry) => entry !== id)
116
+ : [...libraries, id];
117
+ libraries = next;
118
+ if (next.length > 0) librariesEnabled = true;
119
+ }
120
+
121
+ let iconFileName = $state('');
122
+ let iconUrl = $state('');
123
+
124
+ type SourceRow = { id: string; fileName: string; url: string; content: string; encoding: string };
125
+ const emptySource = (): SourceRow => ({
126
+ id: crypto.randomUUID(),
127
+ fileName: '',
128
+ url: '',
129
+ content: '',
130
+ encoding: 'utf8',
131
+ });
132
+ let libraryRows = $state<SourceRow[]>([emptySource()]);
133
+ let iconRows = $state<SourceRow[]>([emptySource()]);
134
+
135
+ function addSourceRow(kind: 'library' | 'icon') {
136
+ const rows = kind === 'library' ? libraryRows : iconRows;
137
+ if (rows.length >= 4) return;
138
+ const next = [emptySource(), ...rows];
139
+ if (kind === 'library') libraryRows = next;
140
+ else iconRows = next;
141
+ }
142
+
143
+ function removeSourceRow(kind: 'library' | 'icon', id: string) {
144
+ const rows = kind === 'library' ? libraryRows : iconRows;
145
+ const next = rows.filter((row) => row.id !== id);
146
+ const kept = next.length > 0 ? next : [emptySource()];
147
+ if (kind === 'library') libraryRows = kept;
148
+ else iconRows = kept;
149
+ }
150
+
151
+ let fontFileName = $state('');
152
+ let fontFileContent = $state('');
153
+ let fontFileEncoding = $state('base64');
154
+ let fontFileUrl = $state('');
155
+
156
+ function clearFontUpload() {
157
+ fontFileName = '';
158
+ fontFileContent = '';
159
+ fontFileEncoding = 'base64';
160
+ fontFileUrl = '';
161
+ }
162
+
163
+ $effect(() => {
164
+ libraryFileName = libraryRows[0]?.fileName || '';
165
+ libraryGitUrl = libraryRows[0]?.url || '';
166
+ iconFileName = iconRows[0]?.fileName || '';
167
+ iconUrl = iconRows[0]?.url || '';
168
+ libraryUploads = [...libraryRows, ...iconRows]
169
+ .filter((row) => row.fileName && row.content)
170
+ .map((row) => ({ name: row.fileName, content: row.content, encoding: row.encoding }));
171
+ fontUploads = fontFileName && fontFileContent
172
+ ? [{ name: fontFileName, content: fontFileContent, encoding: fontFileEncoding || 'base64' }]
173
+ : [];
174
+ });
175
+
176
+ let brandPanelOpen = $state(false);
177
+ let brandPanelMaxHeight = $state(0);
178
+ let brandButtonEl = $state<HTMLLabelElement | null>(null);
179
+ let brandPopoverEl = $state<HTMLDivElement | null>(null);
180
+ let brandPanelTop = $state(0);
181
+ let brandPanelLeft = $state(0);
182
+
183
+ function placePopover(anchor: HTMLElement | null, panel: HTMLElement | null, width: number) {
184
+ if (!anchor) return { top: 0, left: 8 };
185
+ const rect = anchor.getBoundingClientRect();
186
+ const height = panel?.offsetHeight || 320;
187
+ const left = Math.max(8, Math.min(rect.left, window.innerWidth - width - 8));
188
+ const below = rect.bottom + 6;
189
+ const fitsBelow = below + height <= window.innerHeight - 8;
190
+ const top = fitsBelow ? below : Math.max(8, rect.top - height - 6);
191
+ return { top, left };
192
+ }
193
+
194
+ function closeFontPickers() {
195
+ openDropdowns.set(new Set());
196
+ }
197
+
198
+ function onBrandKitChange(event: Event) {
199
+ brandKitEnabled = (event.currentTarget as HTMLInputElement).checked;
200
+ closeFontPickers();
201
+ brandPanelOpen = brandKitEnabled;
202
+ if (!brandKitEnabled) brandPanelMaxHeight = 0;
203
+ }
204
+
205
+ function onLibrariesChange(event: Event) {
206
+ librariesEnabled = (event.currentTarget as HTMLInputElement).checked;
207
+ }
208
+
209
+ function componentExtension() {
210
+ return language === 'react' ? '.jsx' : '.svelte';
211
+ }
212
+
213
+ function sourceAccept(includeSvg: boolean) {
214
+ const extensions = ['.js', '.ts', componentExtension(), '.zip', '.tar', '.tgz', '.gz'];
215
+ if (includeSvg) extensions.unshift('.svg');
216
+ return extensions.join(',');
217
+ }
218
+
219
+ function uploadTooltip(includeSvg: boolean) {
220
+ return includeSvg ? 'Upload your icon library.' : 'Upload your custom library.';
221
+ }
222
+
223
+ function urlPlaceholder(includeSvg: boolean) {
224
+ const component = componentExtension();
225
+ return includeSvg
226
+ ? `URL (git, archive, .svg, ${component})`
227
+ : `URL (git, archive, .js, .ts, ${component})`;
228
+ }
229
+
230
+ $effect(() => {
231
+ if (!brandPanelOpen) return;
232
+
233
+ function place() {
234
+ const brandPlace = placePopover(brandButtonEl, brandPopoverEl, 320);
235
+ brandPanelTop = brandPlace.top;
236
+ brandPanelLeft = brandPlace.left;
237
+ const openHeight = brandPopoverEl?.offsetHeight || 0;
238
+ if (openHeight > 0 && brandPanelMaxHeight === 0) brandPanelMaxHeight = openHeight;
239
+ }
240
+
241
+ function onPointerDown(event: PointerEvent) {
242
+ const target = event.target as HTMLElement | null;
243
+ if (!target) return;
244
+ if (brandButtonEl?.contains(target)) return;
245
+ if (target.closest('.generation-brand-kit-popover')) return;
246
+ if (target.closest('.font-input-popover')) return;
247
+ if (target.closest('#widgetic-font-picker-portal')) return;
248
+ closeFontPickers();
249
+ brandPanelOpen = false;
250
+ brandPanelMaxHeight = 0;
251
+ }
252
+
253
+ place();
254
+ const frame = requestAnimationFrame(place);
255
+ window.addEventListener('pointerdown', onPointerDown, true);
256
+ window.addEventListener('resize', place);
257
+ window.addEventListener('scroll', place, true);
258
+ return () => {
259
+ cancelAnimationFrame(frame);
260
+ window.removeEventListener('pointerdown', onPointerDown, true);
261
+ window.removeEventListener('resize', place);
262
+ window.removeEventListener('scroll', place, true);
263
+ };
264
+ });
265
+ </script>
266
+
267
+ {#if part === 'checks'}
268
+ <div class="generation-options-checks contents {className}" {style}>
269
+ <label
270
+ class="generation-brand-kit-toggle ml-auto flex h-10 shrink-0 cursor-pointer items-center gap-1.5 rounded-lg border px-2 text-sm {brandKitEnabled ? 'border-[#314F2F] bg-[#314F2F] text-white' : 'border-gray-300 bg-white text-gray-800'}"
271
+ bind:this={brandButtonEl}
272
+ >
273
+ <input
274
+ class="generation-option-check cursor-pointer"
275
+ type="checkbox"
276
+ checked={brandKitEnabled}
277
+ onchange={onBrandKitChange}
278
+ />
279
+ Brand kit
280
+ </label>
281
+ <div class="generation-libraries-row flex w-full flex-col gap-2">
282
+ <div class="generation-libraries-checks flex flex-nowrap items-center gap-1">
283
+ {#if showLanguage}
284
+ <HoverTooltip class="inline-flex shrink-0" text="Select language">
285
+ <DropdownMenu.Root>
286
+ <DropdownMenu.Trigger
287
+ class="generation-language-select flex h-10 w-[4.75rem] shrink-0 cursor-pointer items-center justify-between gap-0.5 rounded-lg border border-gray-300 bg-white px-1.5 text-sm text-gray-900"
288
+ >
289
+ <span class="truncate">{language === 'react' ? 'React' : 'Svelte'}</span>
290
+ <svg class="h-3 w-3 shrink-0 text-gray-500" viewBox="0 0 12 12" fill="none" aria-hidden="true">
291
+ <path d="M3 4.5 6 7.5 9 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
292
+ </svg>
293
+ </DropdownMenu.Trigger>
294
+ <DropdownMenu.Content class="w-[4.75rem] min-w-0">
295
+ <DropdownMenu.Item
296
+ class="cursor-pointer {language === 'svelte' ? 'bg-[#314F2F]/15 text-[#314F2F]' : ''}"
297
+ onSelect={() => (language = 'svelte')}
298
+ >Svelte</DropdownMenu.Item>
299
+ <DropdownMenu.Item
300
+ class="cursor-pointer {language === 'react' ? 'bg-[#314F2F]/15 text-[#314F2F]' : ''}"
301
+ onSelect={() => (language = 'react')}
302
+ >React</DropdownMenu.Item>
303
+ </DropdownMenu.Content>
304
+ </DropdownMenu.Root>
305
+ </HoverTooltip>
306
+ {/if}
307
+ <label class="generation-option-toggle flex h-10 shrink-0 cursor-pointer items-center gap-1.5 rounded-lg border px-2 text-sm {librariesEnabled ? 'border-[#314F2F] bg-[#314F2F] text-white' : 'border-gray-300 bg-white text-gray-800'}">
308
+ <input class="generation-option-check cursor-pointer" type="checkbox" checked={librariesEnabled} onchange={onLibrariesChange} />
309
+ Libraries
310
+ </label>
311
+ {#if librariesEnabled && showPresetLibraries}
312
+ {#each libraryChoices as choice (choice.id)}
313
+ <label class="generation-library flex items-center gap-1 text-xs text-gray-700">
314
+ <input
315
+ class="generation-library-check cursor-pointer"
316
+ type="checkbox"
317
+ checked={libraries.includes(choice.id)}
318
+ onchange={() => toggleLibrary(choice.id)}
319
+ />
320
+ {choice.label}
321
+ </label>
322
+ {/each}
323
+ {/if}
324
+ {#if librariesEnabled}
325
+ <HoverTooltip class="inline-flex shrink-0" text="Add Library">
326
+ <button
327
+ type="button"
328
+ class="generation-library-add flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-md border border-gray-300 bg-white text-sm text-gray-800"
329
+ aria-label="Add Library"
330
+ onclick={() => addSourceRow('library')}
331
+ >+</button>
332
+ </HoverTooltip>
333
+ {/if}
334
+ </div>
335
+ {#if librariesEnabled}
336
+ {#each libraryRows as row (row.id)}
337
+ <div class="generation-source-row flex items-center gap-1">
338
+ <FileUrlSourceControl
339
+ class="min-w-0 flex-1"
340
+ bind:fileName={row.fileName}
341
+ bind:fileContent={row.content}
342
+ bind:fileEncoding={row.encoding}
343
+ bind:url={row.url}
344
+ fileLabel="Upload library"
345
+ fileTitle={uploadTooltip(false)}
346
+ urlPlaceholder={urlPlaceholder(false)}
347
+ accept={sourceAccept(false)}
348
+ />
349
+ {#if libraryRows.length > 1}
350
+ <button
351
+ type="button"
352
+ class="generation-source-remove flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-md border border-gray-300 text-gray-500"
353
+ aria-label="Remove library"
354
+ onclick={() => removeSourceRow('library', row.id)}
355
+ >
356
+ <svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
357
+ <path d="M4 7h16" />
358
+ <path d="M9 7V5h6v2" />
359
+ <path d="M8 7l1 13h6l1-13" />
360
+ </svg>
361
+ </button>
362
+ {/if}
363
+ </div>
364
+ {/each}
365
+ {/if}
366
+ </div>
367
+ </div>
368
+ {#if brandPanelOpen}
369
+ <Portal
370
+ target="generation-brand-kit-portal"
371
+ class="generation-brand-kit-portal-root"
372
+ style="position: fixed; top: {brandPanelTop}px; left: {brandPanelLeft}px; width: 320px; height: auto;"
373
+ >
374
+ <div
375
+ class="generation-brand-kit-popover flex flex-col gap-3 overflow-y-auto rounded-lg border border-gray-200 bg-white p-3 shadow-lg"
376
+ style={brandPanelMaxHeight > 0 ? `max-height: ${brandPanelMaxHeight}px;` : ''}
377
+ bind:this={brandPopoverEl}
378
+ >
379
+ <p class="generation-options-note text-xs text-gray-500">Replaces the Widgetic palette for this generation.</p>
380
+ <section class="generation-color-palette flex flex-col gap-2">
381
+ <h3 class="generation-section-title text-xs font-semibold text-gray-800">Color palette</h3>
382
+ <div class="generation-brand-colors flex flex-wrap items-end gap-3">
383
+ <label class="generation-brand-color flex flex-col gap-1 text-xs text-gray-600">
384
+ Primary
385
+ <input class="generation-brand-color-input h-8 w-14 cursor-pointer border border-gray-200 bg-white" type="color" bind:value={primary} />
386
+ </label>
387
+ <label class="generation-brand-color flex flex-col gap-1 text-xs text-gray-600">
388
+ Secondary
389
+ <input class="generation-brand-color-input h-8 w-14 cursor-pointer border border-gray-200 bg-white" type="color" bind:value={secondary} />
390
+ </label>
391
+ <label class="generation-brand-color flex flex-col gap-1 text-xs text-gray-600">
392
+ Title
393
+ <input class="generation-brand-color-input h-8 w-14 cursor-pointer border border-gray-200 bg-white" type="color" bind:value={title} />
394
+ </label>
395
+ <label class="generation-brand-color flex flex-col gap-1 text-xs text-gray-600">
396
+ Sub
397
+ <input class="generation-brand-color-input h-8 w-14 cursor-pointer border border-gray-200 bg-white" type="color" bind:value={sub} />
398
+ </label>
399
+ </div>
400
+ </section>
401
+ <div class="generation-section-separator border-t border-gray-200"></div>
402
+ <section class="generation-brand-fonts flex flex-col gap-2">
403
+ <div class="generation-fonts-header flex items-center justify-between gap-2">
404
+ <h3 class="generation-section-title text-xs font-semibold text-gray-800">Fonts</h3>
405
+ {#if fontModels.length < maxFonts && !suspendPickers}
406
+ <button
407
+ type="button"
408
+ class="generation-brand-font-add h-7 cursor-pointer rounded-md border border-gray-300 px-2 text-xs text-gray-700"
409
+ onclick={addFont}
410
+ >
411
+ + Font
412
+ </button>
413
+ {/if}
414
+ </div>
415
+ {#each fontModels as model, index (index)}
416
+ <div class="generation-brand-font-row flex items-start gap-2">
417
+ {#if suspendPickers}
418
+ <p class="generation-brand-font-name min-w-0 flex-1 py-2 text-sm text-gray-800">{model.family}</p>
419
+ {:else}
420
+ <div class="generation-brand-font-picker min-w-0 flex-1">
421
+ <FontInputController
422
+ label={index === 0 ? 'Font' : `Font ${index + 1}`}
423
+ controllerId="generation-brand-font-{index}"
424
+ value={model}
425
+ {openDropdowns}
426
+ {toggleDropdown}
427
+ hideTrashIcon={true}
428
+ hideRecentFromList={true}
429
+ onChange={(next) => updateFont(index, next)}
430
+ />
431
+ </div>
432
+ {/if}
433
+ {#if fontModels.length > 1}
434
+ <button
435
+ type="button"
436
+ class="generation-brand-font-remove mt-1 flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-md border border-gray-300 text-gray-500"
437
+ aria-label="Remove font"
438
+ onclick={() => removeFont(index)}
439
+ >
440
+ <svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
441
+ <path d="M4 7h16" />
442
+ <path d="M9 7V5h6v2" />
443
+ <path d="M8 7l1 13h6l1-13" />
444
+ </svg>
445
+ </button>
446
+ {/if}
447
+ </div>
448
+ {/each}
449
+ <div class="generation-font-upload flex items-center gap-1">
450
+ <FileUrlSourceControl
451
+ class="min-w-0 flex-1"
452
+ bind:fileName={fontFileName}
453
+ bind:fileContent={fontFileContent}
454
+ bind:fileEncoding={fontFileEncoding}
455
+ bind:url={fontFileUrl}
456
+ fileLabel="Upload Fonts"
457
+ fileTitle="Upload a font file or an archive."
458
+ urlPlaceholder="URL (archive, .woff2, .ttf, .otf)"
459
+ accept=".woff,.woff2,.ttf,.otf,.eot,.zip,.tar,.tgz,.gz"
460
+ />
461
+ {#if fontFileName || fontFileUrl}
462
+ <button
463
+ type="button"
464
+ class="generation-source-remove flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-md border border-gray-300 text-gray-500"
465
+ aria-label="Remove uploaded font"
466
+ onclick={clearFontUpload}
467
+ >
468
+ <svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
469
+ <path d="M4 7h16" />
470
+ <path d="M9 7V5h6v2" />
471
+ <path d="M8 7l1 13h6l1-13" />
472
+ </svg>
473
+ </button>
474
+ {/if}
475
+ </div>
476
+ </section>
477
+ <div class="generation-section-separator border-t border-gray-200"></div>
478
+ <section class="generation-brand-icons flex flex-col gap-2">
479
+ <div class="generation-icons-header flex items-center justify-between gap-2">
480
+ <h3 class="generation-section-title text-xs font-semibold text-gray-800">Icons</h3>
481
+ <HoverTooltip text="Add icon library">
482
+ <button
483
+ type="button"
484
+ class="generation-icon-add flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border border-gray-300 text-sm text-gray-700"
485
+ aria-label="Add icon library"
486
+ onclick={() => addSourceRow('icon')}
487
+ >+</button>
488
+ </HoverTooltip>
489
+ </div>
490
+ {#each iconRows as row (row.id)}
491
+ <div class="generation-source-row flex items-center gap-1">
492
+ <FileUrlSourceControl
493
+ class="min-w-0 flex-1"
494
+ bind:fileName={row.fileName}
495
+ bind:fileContent={row.content}
496
+ bind:fileEncoding={row.encoding}
497
+ bind:url={row.url}
498
+ fileLabel="Upload library"
499
+ fileTitle={uploadTooltip(true)}
500
+ urlPlaceholder={urlPlaceholder(true)}
501
+ accept={sourceAccept(true)}
502
+ />
503
+ {#if iconRows.length > 1}
504
+ <button
505
+ type="button"
506
+ class="generation-source-remove flex h-7 w-7 shrink-0 cursor-pointer items-center justify-center rounded-md border border-gray-300 text-gray-500"
507
+ aria-label="Remove icon library"
508
+ onclick={() => removeSourceRow('icon', row.id)}
509
+ >
510
+ <svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
511
+ <path d="M4 7h16" />
512
+ <path d="M9 7V5h6v2" />
513
+ <path d="M8 7l1 13h6l1-13" />
514
+ </svg>
515
+ </button>
516
+ {/if}
517
+ </div>
518
+ {/each}
519
+ </section>
520
+ </div>
521
+ </Portal>
522
+ {/if}
523
+ {/if}
524
+
525
+ <style>
526
+ .generation-options-checks input:focus,
527
+ .generation-options-checks input:focus-visible,
528
+ .generation-brand-kit-popover button:focus,
529
+ .generation-brand-kit-popover button:focus-visible,
530
+ .generation-brand-kit-popover input:focus,
531
+ .generation-brand-kit-popover input:focus-visible,
532
+ .generation-language-select:focus,
533
+ .generation-language-select:focus-visible {
534
+ outline: none;
535
+ box-shadow: none;
536
+ }
537
+
538
+ .generation-brand-kit-popover {
539
+ scrollbar-width: thin;
540
+ scrollbar-color: rgba(0, 0, 0, 0.2) transparent;
541
+ }
542
+
543
+ .generation-brand-kit-popover::-webkit-scrollbar {
544
+ width: 8px;
545
+ background: transparent;
546
+ }
547
+
548
+ .generation-brand-kit-popover::-webkit-scrollbar-track {
549
+ background: transparent;
550
+ }
551
+
552
+ .generation-brand-kit-popover::-webkit-scrollbar-thumb {
553
+ background: rgba(0, 0, 0, 0.2);
554
+ border-radius: 4px;
555
+ }
556
+ </style>
@@ -0,0 +1,32 @@
1
+ interface Props {
2
+ class?: string;
3
+ style?: string;
4
+ part?: 'checks' | 'panel';
5
+ brandKitEnabled?: boolean;
6
+ librariesEnabled?: boolean;
7
+ showPresetLibraries?: boolean;
8
+ suspendPickers?: boolean;
9
+ primary?: string;
10
+ secondary?: string;
11
+ title?: string;
12
+ sub?: string;
13
+ fonts?: string[];
14
+ libraries?: string[];
15
+ libraryGitUrl?: string;
16
+ libraryFileName?: string;
17
+ libraryUploads?: {
18
+ name: string;
19
+ content: string;
20
+ encoding: string;
21
+ }[];
22
+ fontUploads?: {
23
+ name: string;
24
+ content: string;
25
+ encoding: string;
26
+ }[];
27
+ showLanguage?: boolean;
28
+ language?: string;
29
+ }
30
+ declare const BrandKitPanel: import("svelte").Component<Props, {}, "brandKitEnabled" | "librariesEnabled" | "primary" | "secondary" | "title" | "sub" | "fonts" | "libraries" | "libraryGitUrl" | "libraryFileName" | "libraryUploads" | "fontUploads" | "language">;
31
+ type BrandKitPanel = ReturnType<typeof BrandKitPanel>;
32
+ export default BrandKitPanel;