@widgetic/editor 4.0.1 → 4.0.3

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 (39) hide show
  1. package/dist/components/MockWidget.svelte +1 -1
  2. package/dist/components/WidgetPreview.svelte +36 -52
  3. package/dist/components/editors/EmbedEditor.svelte +1 -1
  4. package/dist/components/editors/PropsEditor.svelte +309 -227
  5. package/dist/components/ic/BrowserInputController.svelte +26 -19
  6. package/dist/components/ic/CheckboxInputController.svelte +1 -1
  7. package/dist/components/ic/ColorPickerInputController.svelte +37 -37
  8. package/dist/components/ic/DatePickerInputController.svelte +6 -6
  9. package/dist/components/ic/DropdownInputController.svelte +1 -1
  10. package/dist/components/ic/FontInputController.svelte +225 -116
  11. package/dist/components/ic/FontInputController.svelte.d.ts +4 -2
  12. package/dist/components/ic/NumberInputController.svelte +37 -37
  13. package/dist/components/ic/RadioGroupInputController.svelte +1 -1
  14. package/dist/components/ic/SliderRangeInputController.svelte +1 -1
  15. package/dist/components/ic/SwitchInputController.svelte +1 -1
  16. package/dist/components/ic/TextAreaInputController.svelte +213 -85
  17. package/dist/components/ic/TextAreaInputController.svelte.d.ts +1 -0
  18. package/dist/components/ic/TextAreaPreviewInputController.svelte +81 -79
  19. package/dist/components/ic/VectorInputController.svelte +34 -34
  20. package/dist/components/ic/shared/CategoryTabs.svelte +1 -0
  21. package/dist/components/ic/shared/RecentItemsSection.svelte +21 -6
  22. package/dist/components/ic/shared/RecentItemsSection.svelte.d.ts +1 -0
  23. package/dist/config.d.ts +6 -0
  24. package/dist/config.js +19 -7
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.js +1 -0
  27. package/dist/stores/userSession.d.ts +1 -1
  28. package/dist/stores/userSession.js +17 -2
  29. package/dist/utils/FontManager.d.ts +3 -1
  30. package/dist/utils/FontManager.js +95 -103
  31. package/dist/utils/VectorManager.d.ts +3 -1
  32. package/dist/utils/VectorManager.js +30 -70
  33. package/dist/utils/colorTransparency.js +3 -3
  34. package/dist/utils/fontUtils.js +1 -1
  35. package/dist/utils/passiveEvents.d.ts +5 -0
  36. package/dist/utils/passiveEvents.js +9 -18
  37. package/dist/utils/recentColors.js +15 -15
  38. package/dist/utils/vectorUtils.js +1 -1
  39. package/package.json +1 -1
@@ -1,11 +1,9 @@
1
1
  <script lang="ts">
2
- // Import Shadcn UI components from Widgetic design-system package
3
- import { Button, Portal } from '@widgetic/design-system/components';
4
- import { IconArrowsDiagonal, IconArrowsMaximize, IconX, IconBrush, IconBold, IconItalic, IconUnderline, IconLink } from '@widgetic/design-system/icons';
2
+ import { Portal } from '@widgetic/design-system/components';
3
+ import { IconArrowsDiagonal } from '@widgetic/design-system/icons';
5
4
  import { onMount, onDestroy } from 'svelte';
6
5
 
7
- // Local components
8
- import { InputControllerHeader, TextAreaInputController } from './index';
6
+ import { TextAreaInputController } from './index';
9
7
  import type { InputControllerConfig } from './index';
10
8
 
11
9
  /**
@@ -50,8 +48,15 @@
50
48
  function createPreviewText(text: string) {
51
49
  if (!text) return '';
52
50
 
51
+ let html = text;
52
+ if (!html.includes('<') && html.includes('&lt;')) {
53
+ const decoded = document.createElement('textarea');
54
+ decoded.innerHTML = html;
55
+ html = decoded.value;
56
+ }
57
+
53
58
  const parser = new DOMParser();
54
- const doc = parser.parseFromString(text, 'text/html');
59
+ const doc = parser.parseFromString(html, 'text/html');
55
60
 
56
61
  function processNode(node: Node): string {
57
62
  if (node.nodeType === Node.TEXT_NODE) {
@@ -60,63 +65,63 @@
60
65
 
61
66
  if (node.nodeType === Node.ELEMENT_NODE) {
62
67
  const element = node as Element;
63
- const tag = element.tagName.toLowerCase();
68
+ let tag = element.tagName.toLowerCase();
64
69
  const attributes: Record<string, string> = {};
70
+ const children = Array.from(node.childNodes)
71
+ .map(child => processNode(child))
72
+ .join('');
73
+
74
+ // Flatten block wrappers so marks render inside the compact clamp box
75
+ if (tag === 'p' || tag === 'div' || tag === 'br') {
76
+ return tag === 'br' ? ' ' : `${children} `;
77
+ }
65
78
 
66
- // Preserve all style attributes
67
79
  const style = element.getAttribute('style');
68
80
  if (style) {
69
- // Extract color value from style
70
81
  const colorMatch = style.match(/color:\s*([^;]+)/);
71
82
  if (colorMatch) {
72
- const color = colorMatch[1];
73
- // Set the color as a CSS variable
74
- attributes.style = `--color: ${color}; ${style}`;
83
+ attributes.style = `--color: ${colorMatch[1]}; ${style}`;
75
84
  } else {
76
85
  attributes.style = style;
77
86
  }
78
87
  }
79
88
 
80
- // Preserve data-text-style attribute
81
89
  const textStyle = element.getAttribute('data-text-style');
82
90
  if (textStyle) {
83
91
  attributes['data-text-style'] = textStyle;
84
- // Add preserve-color class
85
92
  attributes.class = (attributes.class || '') + ' preserve-color';
86
93
  }
87
94
 
88
- // Preserve heading classes
89
95
  if (tag.match(/^h[1-4]$/)) {
96
+ tag = 'span';
90
97
  attributes.class = (attributes.class || '') + ' tiptap-heading';
91
98
  }
92
99
 
93
- // Build start tag with attributes
94
100
  let startTag = `<${tag}`;
95
- for (const [key, value] of Object.entries(attributes)) {
96
- startTag += ` ${key}="${value.trim()}"`;
101
+ for (const [key, attrValue] of Object.entries(attributes)) {
102
+ startTag += ` ${key}="${attrValue.trim()}"`;
97
103
  }
98
104
  startTag += '>';
99
105
 
100
- // Process child nodes
101
- const children = Array.from(node.childNodes)
102
- .map(child => processNode(child))
103
- .join('');
104
-
105
106
  return `${startTag}${children}</${tag}>`;
106
107
  }
107
108
 
108
109
  return '';
109
110
  }
110
111
 
111
- const processedNodes = Array.from(doc.body.childNodes)
112
+ return Array.from(doc.body.childNodes)
112
113
  .map(node => processNode(node))
113
- .join('');
114
+ .join('')
115
+ .trim();
116
+ }
114
117
 
115
- return processedNodes;
118
+ function handleFullEditorChange(html: string) {
119
+ value = html;
120
+ if (typeof onChange === 'function') onChange(html);
116
121
  }
117
122
 
118
123
  function handleClick() {
119
- console.log('Opening text area in portal');
124
+ // console.log('Opening text area in portal');
120
125
  isShowingTextArea = !isShowingTextArea;
121
126
  }
122
127
 
@@ -139,6 +144,10 @@
139
144
  }
140
145
  }
141
146
 
147
+ $: if (value) {
148
+ // console.log('[TextAreaPreview] compact preview html', value.slice(0, 180));
149
+ }
150
+
142
151
  function handlePortalMouseEnter() {
143
152
  // console.log('Portal mouse entered');
144
153
  isPortalHovered = true;
@@ -211,42 +220,31 @@
211
220
  }
212
221
  </script>
213
222
 
214
- <div class="text-area-preview-input-controller flex items-center justify-between w-full widgetic-colors">
215
- <div class="header-container flex-1 min-w-0">
216
- <InputControllerHeader {label} />
217
- </div>
218
-
223
+ <div class="text-area-preview-input-controller w-full min-w-0 overflow-hidden widgetic-colors">
219
224
  <div
220
- class="text-area-preview-main-container w-[185px] flex-shrink-0"
225
+ class="text-area-preview-main-container w-full min-w-0 overflow-hidden"
221
226
  >
222
227
  <div
223
- class="h-[92px] w-full border border-grey2 flex items-start bg-grey1 transition-colors duration-200 hover:border-forest-green cursor-pointer group overflow-hidden"
228
+ class="textarea-ic-preview-box relative h-[92px] w-full min-w-0 border border-grey2 bg-grey1 transition-colors duration-200 hover:border-forest-green cursor-pointer group overflow-hidden"
224
229
  style="border-radius: var(--radius-large);"
225
230
  on:click={handleClick}
226
231
  on:keydown={(e) => e.key === 'Enter' && handleClick()}
227
232
  role="button"
228
233
  tabindex="0"
229
234
  >
230
- <div class="relative w-full h-full">
235
+ <div class="textarea-ic-preview-copy min-w-0 overflow-hidden pr-10 pt-[13px] pb-4 pl-4">
231
236
  <div
232
- class="absolute top-0 left-0 w-full h-full flex flex-col"
237
+ class="textarea-ic-preview-html break-words text-grey7 text-sm leading-5"
238
+ class:text-grey3={isShowingPlaceholder}
233
239
  >
234
- <div class="flex-1" style="padding: 13px 48px 16px 16px;">
235
- <div
236
- class="subtext line-clamp-3 break-words text-ellipsis prose-sm w-[129px] text-grey7"
237
- class:text-grey3={isShowingPlaceholder}
238
- style="display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden;"
239
- >
240
- {@html previewText}
241
- </div>
242
- </div>
243
- <div class="absolute top-[14px] right-[12px]">
244
- <IconArrowsDiagonal
245
- class="w-5 h-5 text-grey5 group-hover:text-grey7 transition-colors duration-200"
246
- />
247
- </div>
240
+ {@html previewText}
248
241
  </div>
249
242
  </div>
243
+ <div class="textarea-ic-expand-icon absolute top-[14px] right-[12px]">
244
+ <IconArrowsDiagonal
245
+ class="w-5 h-5 text-grey5 group-hover:text-grey7 transition-colors duration-200"
246
+ />
247
+ </div>
250
248
  </div>
251
249
  </div>
252
250
 
@@ -280,24 +278,14 @@
280
278
  style="border-radius: var(--radius-large);"
281
279
  ></div>
282
280
 
283
- <Button
284
- variant="ghost"
285
- class="absolute z-[999] p-0 hover:bg-transparent h-[32px] w-[32px] top-[10px] right-[10px]"
286
- onclick={handleClose}
287
- >
288
- <IconX
289
- class="w-[24px] h-[24px] text-grey5 hover:text-grey7 transition-colors duration-200"
290
- style="min-width: 24px; min-height: 24px;"
291
- />
292
- </Button>
293
-
294
281
  <TextAreaInputController
295
282
  class="text-area w-full h-full transition-colors duration-200 {isPortalHovered ? 'border-solid !border-forest-green' : ''}"
296
283
  label={label}
297
- value={value}
298
- onChange={onChange}
284
+ bind:value
285
+ onChange={handleFullEditorChange}
299
286
  config={config}
300
287
  description={description}
288
+ onClose={handleClose}
301
289
  />
302
290
  </div>
303
291
  </Portal>
@@ -318,7 +306,7 @@
318
306
 
319
307
 
320
308
  /* Heading styles matching tiptap editor */
321
- :global(.subtext h1.tiptap-heading) {
309
+ :global(.textarea-ic-preview-html h1.tiptap-heading) {
322
310
  font-size: 2em !important;
323
311
  font-weight: 600 !important;
324
312
  line-height: 1.2 !important;
@@ -327,7 +315,7 @@
327
315
  color: inherit !important;
328
316
  }
329
317
 
330
- :global(.subtext h2.tiptap-heading) {
318
+ :global(.textarea-ic-preview-html h2.tiptap-heading) {
331
319
  font-size: 1.5em !important;
332
320
  font-weight: 600 !important;
333
321
  line-height: 1.3 !important;
@@ -336,7 +324,7 @@
336
324
  color: inherit !important;
337
325
  }
338
326
 
339
- :global(.subtext h3.tiptap-heading) {
327
+ :global(.textarea-ic-preview-html h3.tiptap-heading) {
340
328
  font-size: 1.17em !important;
341
329
  font-weight: 600 !important;
342
330
  line-height: 1.3 !important;
@@ -345,7 +333,7 @@
345
333
  color: inherit !important;
346
334
  }
347
335
 
348
- :global(.subtext h4.tiptap-heading) {
336
+ :global(.textarea-ic-preview-html h4.tiptap-heading) {
349
337
  font-size: 1em !important;
350
338
  font-weight: 600 !important;
351
339
  line-height: 1.4 !important;
@@ -354,36 +342,50 @@
354
342
  color: inherit !important;
355
343
  }
356
344
 
345
+ :global(.textarea-ic-preview-html) {
346
+ display: -webkit-box;
347
+ -webkit-box-orient: vertical;
348
+ -webkit-line-clamp: 3;
349
+ line-clamp: 3;
350
+ overflow: hidden;
351
+ }
352
+
357
353
  /* Color preservation */
358
- :global(.subtext [style*="color"]) {
354
+ :global(.textarea-ic-preview-html [style*="color"]) {
359
355
  color: var(--color) !important;
360
356
  }
361
357
 
362
- /* Text formatting */
363
- :global(.subtext strong),
364
- :global(.subtext em),
365
- :global(.subtext u) {
366
- color: inherit !important;
358
+ /* Text formatting — {@html} children need :global; include b/i as well as strong/em/u */
359
+ :global(.textarea-ic-preview-html :is(strong, b, [style*="font-weight"])) {
360
+ font-weight: 700 !important;
361
+ }
362
+
363
+ :global(.textarea-ic-preview-html :is(em, i)) {
364
+ font-style: italic !important;
365
+ }
366
+
367
+ :global(.textarea-ic-preview-html :is(u, [style*="text-decoration: underline"])) {
368
+ text-decoration: underline !important;
367
369
  }
368
370
 
369
- :global(.subtext strong[style*="color"]),
370
- :global(.subtext em[style*="color"]),
371
- :global(.subtext u[style*="color"]) {
371
+ :global(.textarea-ic-preview-html strong[style*="color"]),
372
+ :global(.textarea-ic-preview-html em[style*="color"]),
373
+ :global(.textarea-ic-preview-html u[style*="color"]) {
372
374
  color: var(--color) !important;
373
375
  }
374
376
 
375
377
  /* Add specific color inheritance for data-text-style */
376
- :global(.subtext [data-text-style*="color"]) {
378
+ :global(.textarea-ic-preview-html [data-text-style*="color"]) {
377
379
  color: var(--color) !important;
378
380
  }
379
381
 
380
382
  /* Ensure all text elements can inherit colors */
381
- :global(.subtext *) {
383
+ :global(.textarea-ic-preview-html *) {
382
384
  color: inherit;
383
385
  }
384
386
 
385
387
  /* Ensure paragraphs have no margins */
386
- :global(.subtext p) {
388
+ :global(.textarea-ic-preview-html p) {
387
389
  margin-top: 0 !important;
388
390
  margin-bottom: 0 !important;
389
391
  }
@@ -329,7 +329,7 @@
329
329
  const testImg = new Image();
330
330
  testImg.onload = () => {
331
331
  // Vector loads successfully
332
- console.log(`✅ [VectorInput] Current vector loads successfully: ${value.name}`);
332
+ // console.log(`✅ [VectorInput] Current vector loads successfully: ${value.name}`);
333
333
  };
334
334
  testImg.onerror = () => {
335
335
  // Mark as error if it fails to load
@@ -420,13 +420,13 @@
420
420
 
421
421
  // Preload the vectors to detect missing files early
422
422
  if (filteredVectors.length > 0) {
423
- console.log(`🔍 [VectorInput] Preloading ${filteredVectors.length} vectors to detect missing files...`);
423
+ // console.log(`🔍 [VectorInput] Preloading ${filteredVectors.length} vectors to detect missing files...`);
424
424
  preloadVectors(filteredVectors);
425
425
  }
426
426
 
427
427
  // Also preload recent vectors
428
428
  if (recentVectors.length > 0) {
429
- console.log(`🔍 [VectorInput] Preloading ${recentVectors.length} recent vectors...`);
429
+ // console.log(`🔍 [VectorInput] Preloading ${recentVectors.length} recent vectors...`);
430
430
  preloadVectors(recentVectors);
431
431
  }
432
432
 
@@ -897,12 +897,12 @@
897
897
  }
898
898
  }, 0);
899
899
 
900
- console.log('📝 [VectorSearch] Loaded next batch:', {
901
- batchSize: nextBatch.length,
902
- totalDisplayed: filteredVectors.length,
903
- remainingVectors: currentSearchResults.length - filteredVectors.length,
904
- nextBatchSize: SEARCH_BATCH_SIZE
905
- });
900
+ // console.log('📝 [VectorSearch] Loaded next batch:', {
901
+ // batchSize: nextBatch.length,
902
+ // totalDisplayed: filteredVectors.length,
903
+ // remainingVectors: currentSearchResults.length - filteredVectors.length,
904
+ // nextBatchSize: SEARCH_BATCH_SIZE
905
+ // });
906
906
  }
907
907
  } else {
908
908
  // Normal vector list scrolling
@@ -971,17 +971,17 @@
971
971
  }
972
972
  showVectorModal = false;
973
973
 
974
- console.log('🧹 [VectorInput] Cleanup complete:', {
975
- controllerId,
976
- lastFilter: currentFilter
977
- });
974
+ // console.log('🧹 [VectorInput] Cleanup complete:', {
975
+ // controllerId,
976
+ // lastFilter: currentFilter
977
+ // });
978
978
  });
979
979
 
980
980
  /**
981
981
  * Opens the file browser for vector selection
982
982
  */
983
983
  async function openFileBrowser() {
984
- console.log('🔍 [VectorInput] Opening file browser for vectors');
984
+ // console.log('🔍 [VectorInput] Opening file browser for vectors');
985
985
 
986
986
  try {
987
987
  // Get the current vector's ID if it exists in our custom vectors
@@ -990,10 +990,10 @@
990
990
  .find(v => v.name === value.name && v.category === 'Mine')?.name
991
991
  : undefined;
992
992
 
993
- console.log('🔍 [VectorInput] Opening file browser:', {
994
- fileTypes: ['svg'],
995
- selectedId
996
- });
993
+ // console.log('🔍 [VectorInput] Opening file browser:', {
994
+ // fileTypes: ['svg'],
995
+ // selectedId
996
+ // });
997
997
 
998
998
  // TODO: Implement file browser functionality
999
999
  // This will be similar to FontInputController but for SVG files
@@ -1167,21 +1167,21 @@
1167
1167
  // Add to VectorInputController.svelte
1168
1168
  function handleToggle() {
1169
1169
  const beforeState = $openDropdowns?.has(controllerId);
1170
- console.log('🟡 [VectorInput] Toggle start:', {
1171
- controllerId,
1172
- beforeState,
1173
- openDropdowns: Array.from($openDropdowns || new Set())
1174
- });
1170
+ // console.log('🟡 [VectorInput] Toggle start:', {
1171
+ // controllerId,
1172
+ // beforeState,
1173
+ // openDropdowns: Array.from($openDropdowns || new Set())
1174
+ // });
1175
1175
 
1176
1176
  toggleDropdown(controllerId);
1177
1177
 
1178
1178
  const afterState = $openDropdowns?.has(controllerId);
1179
- console.log('🟢 [VectorInput] Toggle complete:', {
1180
- controllerId,
1181
- beforeState,
1182
- afterState,
1183
- openDropdowns: Array.from($openDropdowns || new Set())
1184
- });
1179
+ // console.log('🟢 [VectorInput] Toggle complete:', {
1180
+ // controllerId,
1181
+ // beforeState,
1182
+ // afterState,
1183
+ // openDropdowns: Array.from($openDropdowns || new Set())
1184
+ // });
1185
1185
  }
1186
1186
 
1187
1187
  function closeDropdown() {
@@ -1216,7 +1216,7 @@
1216
1216
 
1217
1217
  // If scrolling outside our containers, close dropdowns
1218
1218
  if ($openDropdowns?.has(controllerId)) {
1219
- console.log('🔴 [VectorInput] Closing dropdown due to external scroll');
1219
+ // console.log('🔴 [VectorInput] Closing dropdown due to external scroll');
1220
1220
  toggleDropdown(controllerId);
1221
1221
  }
1222
1222
  }
@@ -1283,7 +1283,7 @@
1283
1283
  // Create a Svelte action to handle image error events
1284
1284
  function handleImageError(node: HTMLElement, vectorName: string) {
1285
1285
  const errorHandler = (event: Event) => {
1286
- console.log(`❌ [VectorInput] Image failed to load: ${vectorName}`);
1286
+ // console.log(`❌ [VectorInput] Image failed to load: ${vectorName}`);
1287
1287
  svgLoadErrors.set(vectorName, true);
1288
1288
  svgLoadErrors = new Map(svgLoadErrors);
1289
1289
 
@@ -1481,7 +1481,7 @@
1481
1481
  if (hasSvgLoadError(value.name)) {
1482
1482
  svgLoadErrors.delete(value.name);
1483
1483
  svgLoadErrors = new Map(svgLoadErrors);
1484
- console.log(`✅ [VectorInput] Vector now loads successfully: ${value.name}`);
1484
+ // console.log(`✅ [VectorInput] Vector now loads successfully: ${value.name}`);
1485
1485
  }
1486
1486
  };
1487
1487
  testImg.onerror = () => {
@@ -1648,10 +1648,10 @@
1648
1648
  // Ensure the observer is set up after the DOM updates
1649
1649
  setTimeout(() => {
1650
1650
  setupVectorObserver();
1651
- console.log('🔄 [VectorSearch] Observer setup after search clear');
1651
+ // console.log('🔄 [VectorSearch] Observer setup after search clear');
1652
1652
  }, 50);
1653
1653
 
1654
- console.log('🔄 [VectorSearch] Search cleared');
1654
+ // console.log('🔄 [VectorSearch] Search cleared');
1655
1655
  }}
1656
1656
  />
1657
1657
  </div>
@@ -25,6 +25,7 @@
25
25
  {@const isActive = getCategoryKey(activeCategory) === getCategoryKey(category)}
26
26
  <button
27
27
  type="button"
28
+ tabindex="-1"
28
29
  class="cursor-pointer caption transition-colors text-center {
29
30
  isActive
30
31
  ? 'text-grey7'
@@ -26,6 +26,7 @@
26
26
  export let displayMode: 'list' | 'grid' = 'list';
27
27
  export let hideTrashIcon: boolean = false;
28
28
  export let itemType: 'font' | 'vector' = 'font';
29
+ export let selectionToken = '';
29
30
 
30
31
  // Helper function to determine if the result has HTML content
31
32
  function hasHtml(result: { html: string } | string): result is { html: string } {
@@ -39,7 +40,7 @@
39
40
 
40
41
  {#if displayMode === 'list'}
41
42
  <div class="grid grid-cols-1">
42
- {#each recentItems as item (getItemKey(item))}
43
+ {#each recentItems as item (getItemKey(item) + '-' + selectionToken)}
43
44
  {@const renderedItem = renderItem(item)}
44
45
  {@const selected = isItemSelected(item)}
45
46
 
@@ -47,9 +48,10 @@
47
48
  <!-- svelte-ignore a11y-click-events-have-key-events -->
48
49
  <!-- svelte-ignore a11y_no_static_element_interactions -->
49
50
  <div
50
- class="w-full px-4 cursor-pointer overflow-hidden relative rounded-small"
51
+ class="font-list-row w-full px-4 cursor-pointer overflow-hidden relative rounded-small"
51
52
  class:bg-sage-green={selected}
52
53
  class:text-white={selected}
54
+ class:text-grey7={!selected}
53
55
  class:hover:bg-light-green={!selected}
54
56
  class:selected-item={selected}
55
57
  onclick={() => onItemSelect(item)}
@@ -69,6 +71,7 @@
69
71
  {#if isCustomItem(item) && !hideTrashIcon}
70
72
  <button
71
73
  type="button"
74
+ tabindex="-1"
72
75
  class="p-1 rounded-full hover:bg-red-100 hover:text-red-500 transition-colors"
73
76
  onclick={(e) => {
74
77
  e.stopPropagation();
@@ -84,9 +87,10 @@
84
87
  <!-- svelte-ignore a11y-click-events-have-key-events -->
85
88
  <!-- svelte-ignore a11y_no_static_element_interactions -->
86
89
  <div
87
- class="w-full px-4 cursor-pointer overflow-hidden relative rounded-small"
90
+ class="font-list-row w-full px-4 cursor-pointer overflow-hidden relative rounded-small"
88
91
  class:bg-sage-green={selected}
89
92
  class:text-white={selected}
93
+ class:text-grey7={!selected}
90
94
  class:hover:bg-light-green={!selected}
91
95
  class:selected-item={selected}
92
96
  onclick={() => onItemSelect(item)}
@@ -105,6 +109,7 @@
105
109
  {#if isCustomItem(item) && !hideTrashIcon}
106
110
  <button
107
111
  type="button"
112
+ tabindex="-1"
108
113
  class="p-1 rounded-full hover:bg-red-100 hover:text-red-500 transition-colors"
109
114
  onclick={(e) => {
110
115
  e.stopPropagation();
@@ -121,7 +126,7 @@
121
126
  </div>
122
127
  {:else}
123
128
  <div class="grid grid-cols-3 gap-x-6 gap-y-4 pt-2 pb-0">
124
- {#each recentItems as item (getItemKey(item))}
129
+ {#each recentItems as item (getItemKey(item) + '-' + selectionToken)}
125
130
  {@const renderedItem = renderItem(item)}
126
131
  {@const selected = isItemSelected(item)}
127
132
 
@@ -132,6 +137,7 @@
132
137
  class="flex-shrink-0 flex items-center justify-center widgetic-radius rounded transition-colors cursor-pointer border-2 border-grey2"
133
138
  class:bg-sage-green={selected}
134
139
  class:text-white={selected}
140
+ class:text-grey7={!selected}
135
141
  class:border-sage-green={selected}
136
142
  class:hover:bg-light-green={!selected}
137
143
  class:selected-item={selected}
@@ -152,6 +158,7 @@
152
158
  <button
153
159
  type="button"
154
160
  class="absolute top-1 right-1 p-1 rounded-full hover:bg-red-100 hover:text-red-500 transition-colors bg-white"
161
+ tabindex="-1"
155
162
  onclick={(e) => {
156
163
  e.stopPropagation();
157
164
  onItemDelete(item, e);
@@ -169,6 +176,7 @@
169
176
  class="flex-shrink-0 flex items-center justify-center widgetic-radius rounded transition-colors cursor-pointer border-2 border-grey2"
170
177
  class:bg-sage-green={selected}
171
178
  class:text-white={selected}
179
+ class:text-grey7={!selected}
172
180
  class:border-sage-green={selected}
173
181
  class:hover:bg-light-green={!selected}
174
182
  class:selected-item={selected}
@@ -188,6 +196,7 @@
188
196
  <button
189
197
  type="button"
190
198
  class="absolute top-1 right-1 p-1 rounded-full hover:bg-red-100 hover:text-red-500 transition-colors bg-white"
199
+ tabindex="-1"
191
200
  onclick={(e) => {
192
201
  e.stopPropagation();
193
202
  onItemDelete(item, e);
@@ -252,9 +261,15 @@
252
261
  background-color: hsl(var(--light-green)) !important;
253
262
  }
254
263
 
255
- /* Prevent hover effect on selected items */
256
- :global(.selected-item:hover) {
264
+ :global(.font-list-row.selected-item:hover),
265
+ :global([data-item].selected-item:hover) {
257
266
  background-color: hsl(var(--sage-green)) !important;
267
+ color: white !important;
268
+ }
269
+
270
+ :global(.font-list-row:not(.selected-item)),
271
+ :global([data-item]:not(.selected-item)) {
272
+ color: hsl(var(--grey7)) !important;
258
273
  }
259
274
 
260
275
  </style>
@@ -28,6 +28,7 @@ declare const RecentItemsSection: $$__sveltets_2_IsomorphicComponent<{
28
28
  displayMode?: "list" | "grid";
29
29
  hideTrashIcon?: boolean;
30
30
  itemType?: "font" | "vector";
31
+ selectionToken?: string;
31
32
  }, {
32
33
  [evt: string]: CustomEvent<any>;
33
34
  }, {}, {}, string>;
package/dist/config.d.ts CHANGED
@@ -1,5 +1,11 @@
1
1
  interface Config {
2
2
  GOOGLE_FONTS_API_KEY: string;
3
3
  }
4
+ export declare function setGoogleFontsApiKey(key: string): void;
5
+ /**
6
+ * Google Fonts Web Fonts API key.
7
+ * Order: runtime setter → PUBLIC_GOOGLE_FONTS_API_KEY → VITE_GOOGLE_FONTS_API_KEY.
8
+ */
9
+ export declare function getGoogleFontsKey(): string;
4
10
  export declare const config: Config;
5
11
  export {};
package/dist/config.js CHANGED
@@ -1,19 +1,31 @@
1
+ /**
2
+ * Set by the consuming app (widget-creator / site) so the published
3
+ * `@widgetic/editor` package never bakes a key into dist at pack time.
4
+ * Vite still inlines PUBLIC_/VITE_ keys when the consumer bundles this source.
5
+ */
6
+ let runtimeGoogleFontsApiKey = '';
7
+ export function setGoogleFontsApiKey(key) {
8
+ runtimeGoogleFontsApiKey = String(key || '').trim();
9
+ }
1
10
  /**
2
11
  * Google Fonts Web Fonts API key.
3
- * Set in consuming app .env as PUBLIC_GOOGLE_FONTS_API_KEY or VITE_GOOGLE_FONTS_API_KEY.
4
- * Avoids $env/static/public import so the library works when embedded in site/creator.
12
+ * Order: runtime setter → PUBLIC_GOOGLE_FONTS_API_KEY → VITE_GOOGLE_FONTS_API_KEY.
5
13
  */
6
- function getGoogleFontsKey() {
14
+ export function getGoogleFontsKey() {
15
+ if (runtimeGoogleFontsApiKey)
16
+ return runtimeGoogleFontsApiKey;
7
17
  if (typeof import.meta !== 'undefined' && import.meta.env) {
8
- const fromVite = import.meta.env.VITE_GOOGLE_FONTS_API_KEY;
9
- if (fromVite && String(fromVite).trim())
10
- return String(fromVite).trim();
11
18
  const fromPublicMeta = import.meta.env.PUBLIC_GOOGLE_FONTS_API_KEY;
12
19
  if (fromPublicMeta && String(fromPublicMeta).trim())
13
20
  return String(fromPublicMeta).trim();
21
+ const fromVite = import.meta.env.VITE_GOOGLE_FONTS_API_KEY;
22
+ if (fromVite && String(fromVite).trim())
23
+ return String(fromVite).trim();
14
24
  }
15
25
  return '';
16
26
  }
17
27
  export const config = {
18
- GOOGLE_FONTS_API_KEY: getGoogleFontsKey(),
28
+ get GOOGLE_FONTS_API_KEY() {
29
+ return getGoogleFontsKey();
30
+ },
19
31
  };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { default as PropsEditor } from './components/editors/PropsEditor.svelte';
2
2
  export { default as WidgetPreview } from './components/WidgetPreview.svelte';
3
3
  export { setFileBrowserUploadConfig } from '@widgetic/file-browser';
4
+ export { setGoogleFontsApiKey, getGoogleFontsKey } from './config';
4
5
  export { default as EmbedEditor } from './components/editors/EmbedEditor.svelte';
package/dist/index.js CHANGED
@@ -4,5 +4,6 @@
4
4
  export { default as PropsEditor } from './components/editors/PropsEditor.svelte';
5
5
  export { default as WidgetPreview } from './components/WidgetPreview.svelte';
6
6
  export { setFileBrowserUploadConfig } from '@widgetic/file-browser';
7
+ export { setGoogleFontsApiKey, getGoogleFontsKey } from './config';
7
8
  // Embed Editor
8
9
  export { default as EmbedEditor } from './components/editors/EmbedEditor.svelte';
@@ -1,6 +1,6 @@
1
1
  export declare const userSession: import("svelte/store").Writable<{
2
2
  user: any;
3
- jwt: any;
3
+ jwt: string | null;
4
4
  }>;
5
5
  export declare function setSession(user: any, jwt: any): void;
6
6
  export declare function clearSession(): void;