@widgetic/editor 4.0.6 → 4.0.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.
@@ -51,6 +51,10 @@
51
51
  */
52
52
  $: open = $openDropdowns?.has(controllerId) || false;
53
53
 
54
+ /** Timestamp of the last open — used to ignore bits-ui's own opening scrolls. */
55
+ let lastOpenTimestamp = 0;
56
+ $: if (open) lastOpenTimestamp = performance.now();
57
+
54
58
  /**
55
59
  * Closes the dropdown if it's currently open
56
60
  * Used for cleanup and external close triggers
@@ -63,9 +67,25 @@
63
67
 
64
68
  /**
65
69
  * Scroll Event Handler
66
- * Closes dropdown when scrolling occurs to prevent floating UI issues
70
+ * Closes dropdown when scrolling occurs to prevent floating UI issues.
71
+ * Guarded: bits-ui may scroll the viewport/list right after opening, and the
72
+ * capture-phase listener would otherwise close the dropdown in the same frame
73
+ * (which looked like "the dropdown never opens" in the Embed overlay).
67
74
  */
68
- function handleScroll() {
75
+ function handleScroll(event?: Event) {
76
+ if (!open) return;
77
+ // Grace period after opening — ignore self-inflicted scrolls from bits-ui.
78
+ if (performance.now() - lastOpenTimestamp < 300) return;
79
+ // Never close on scrolls that happen inside the dropdown itself or its portal.
80
+ const target = event?.target as Node | null;
81
+ if (
82
+ target instanceof Element &&
83
+ (target.closest('[data-bits-floating-content-wrapper]') ||
84
+ target.closest('[role="listbox"]') ||
85
+ target.closest('.dropdown-ic-content'))
86
+ ) {
87
+ return;
88
+ }
69
89
  closeDropdown();
70
90
  }
71
91
 
@@ -104,7 +124,8 @@
104
124
  }
105
125
  }
106
126
 
107
- $: triggerContent = options.find((o) => o.value === (value.value || value))?.label ?? defaultSelectedLabel;
127
+ $: selectedValue = typeof value === 'string' ? value : value?.value ?? '';
128
+ $: triggerContent = options.find((option) => option.value === selectedValue)?.label ?? defaultSelectedLabel;
108
129
  </script>
109
130
 
110
131
  <!-- Dropdown Container -->
@@ -122,7 +143,7 @@
122
143
  >
123
144
  <Select.Root
124
145
  type="single"
125
- value={value.value || value}
146
+ value={selectedValue}
126
147
  onValueChange={handleValueChange}
127
148
  open={open}
128
149
  onOpenChange={handleOpenChange}
@@ -218,9 +239,13 @@
218
239
  display: none;
219
240
  }
220
241
 
221
- /* Override bits-ui floating wrapper z-index (default 50 is too low).
222
- The wrapper is portaled to body by bits-ui, so we target it globally. */
242
+ /* bits-ui portals the Select content to <body> and wraps it in
243
+ [data-bits-floating-content-wrapper]. The floating layer's default z-index
244
+ (50) sat below page overlays — in the composition Embed overlay
245
+ (z-index 100000) the dropdown opened but was painted behind the dark
246
+ backdrop, so it looked like it never opened. Lift the wrapper above any
247
+ app overlay. */
223
248
  :global([data-bits-floating-content-wrapper]) {
224
- z-index: 99999 !important;
249
+ z-index: 1000001 !important;
225
250
  }
226
251
  </style>
@@ -95,6 +95,7 @@
95
95
  // Font weight state - remove localStorage check on init
96
96
  let selectedWeight = `${controllerId}-Regular`;
97
97
  let availableWeights: string[] = ['Regular', 'Medium', 'SemiBold', 'Bold'];
98
+ let fontCatalogFallbackWarned = false;
98
99
 
99
100
  // Font search and filter state
100
101
  let allFonts: FontItem[] = [];
@@ -625,7 +626,7 @@
625
626
 
626
627
  // try {
627
628
  // // Get the current font's ID if it exists in our custom fonts
628
- // const selectedId = value?.family ?
629
+ // const selectedId = resolvedFont?.family ?
629
630
  // fontManagerInstance?.getCachedFontList()
630
631
  // .find(f => f.family === value.family && f.category === 'Mine')?.id
631
632
  // : undefined;
@@ -1360,6 +1361,9 @@
1360
1361
  */
1361
1362
  async function fetchFontWeights(fontFamily: string) {
1362
1363
  try {
1364
+ // Wait for FontManager init — reactive weight fetch can race ahead of onMount.
1365
+ fontManagerInstance = await FontManager.getInstance(appConfig.GOOGLE_FONTS_API_KEY);
1366
+
1363
1367
  // First check if it's a custom font using FontManager
1364
1368
  if (fontManagerInstance?.isCustomFont(fontFamily)) {
1365
1369
  // Get custom font data
@@ -1397,7 +1401,16 @@
1397
1401
  }
1398
1402
 
1399
1403
  // Google font: use catalog cached by FontManager (no extra API call per font)
1400
- const variantIds = fontManagerInstance?.getGoogleFontVariantIds(fontFamily);
1404
+ let variantIds = fontManagerInstance?.getGoogleFontVariantIds(fontFamily);
1405
+ if (!variantIds?.length) {
1406
+ // Catalog may still be empty right after init edge cases — force list load once.
1407
+ try {
1408
+ await fontManagerInstance.getInitialFontList();
1409
+ } catch (catalogError) {
1410
+ console.warn('[FontInput] Catalog refresh failed before weight lookup:', catalogError);
1411
+ }
1412
+ variantIds = fontManagerInstance?.getGoogleFontVariantIds(fontFamily);
1413
+ }
1401
1414
  if (variantIds?.length) {
1402
1415
  availableWeights = mapGoogleVariantIdsToWeightNames(variantIds);
1403
1416
  if (!availableWeights.includes(selectedWeight.replace(`${controllerId}-`, ''))) {
@@ -1407,7 +1420,11 @@
1407
1420
  }
1408
1421
 
1409
1422
  // API unavailable (403 / no catalog): sensible defaults, still loads via fonts.googleapis.com CSS
1410
- console.warn('[FontInput] Using default weights for font (Google catalog not loaded):', fontFamily);
1423
+ // Warn once — not on every reactive re-run during catalog race.
1424
+ if (!fontCatalogFallbackWarned) {
1425
+ fontCatalogFallbackWarned = true;
1426
+ console.warn('[FontInput] Using default weights (Google catalog not loaded). Later fonts will use the same fallback silently.');
1427
+ }
1411
1428
  availableWeights = ['Regular', 'Bold'];
1412
1429
  selectedWeight = `${controllerId}-Regular`;
1413
1430
  } catch (error) {
@@ -1431,7 +1448,7 @@
1431
1448
  */
1432
1449
  function highlightMatch(name: string, term: string) {
1433
1450
  // Check if this font is the selected one
1434
- const isSelected = value?.family === name;
1451
+ const isSelected = resolvedFont?.family === name;
1435
1452
 
1436
1453
  if (!term) {
1437
1454
  // If no search term, just return the name with appropriate color
@@ -1558,7 +1575,7 @@
1558
1575
  await fontManagerInstance?.removeCustomFont(fontFamily);
1559
1576
 
1560
1577
  // If the removed font was selected, clear the selection
1561
- if (value?.family === fontFamily && onChange) {
1578
+ if (resolvedFont?.family === fontFamily && onChange) {
1562
1579
  onChange({
1563
1580
  family: '',
1564
1581
  weight: 'Regular',
@@ -1719,7 +1736,7 @@
1719
1736
  }
1720
1737
  }}
1721
1738
  >
1722
- <Popover.Trigger tabindex="-1" class="w-full h-full flex items-center border-0 bg-transparent hover:bg-transparent focus:bg-transparent pl-3 pr-[16px] group"
1739
+ <Popover.Trigger tabindex={-1} class="w-full h-full flex items-center border-0 bg-transparent hover:bg-transparent focus:bg-transparent pl-3 pr-[16px] group"
1723
1740
  onmouseenter={() => {
1724
1741
  // Preload fonts when hovering over the button
1725
1742
  // This reduces the delay when actually clicking to open
@@ -1896,7 +1913,7 @@
1896
1913
  <div
1897
1914
  role="button"
1898
1915
  tabindex="-1"
1899
- class="ml-auto transition-opacity {value?.family === font.family ? 'opacity-0 pointer-events-none' : 'opacity-0 group-hover:opacity-100'}"
1916
+ class="ml-auto transition-opacity {resolvedFont?.family === font.family ? 'opacity-0 pointer-events-none' : 'opacity-0 group-hover:opacity-100'}"
1900
1917
  onclick={(e) => {
1901
1918
  e.stopPropagation();
1902
1919
  e.preventDefault();
@@ -1939,7 +1956,7 @@
1939
1956
  }
1940
1957
  }}
1941
1958
  >
1942
- <Popover.Trigger tabindex="-1" class="w-full h-full flex items-center border-0 bg-transparent hover:bg-transparent focus:bg-transparent pl-3 pr-[16px] group">
1959
+ <Popover.Trigger tabindex={-1} class="w-full h-full flex items-center border-0 bg-transparent hover:bg-transparent focus:bg-transparent pl-3 pr-[16px] group">
1943
1960
  <span class="truncate flex-1 text-left subtext text-grey7">
1944
1961
  {displayWeight || 'Regular'}
1945
1962
  </span>
@@ -0,0 +1,98 @@
1
+ <script module lang="ts">
2
+ import type { ComponentProps } from 'svelte';
3
+ import {
4
+ TextInputController, TextAreaPreviewInputController, NumberInputController,
5
+ SliderInputController, SliderRangeInputController, DropdownInputController,
6
+ DatePickerInputController, DateFieldInputController, RangeCalendarInputController,
7
+ SwitchInputController, ColorPickerInputController, CheckboxInputController,
8
+ RadioGroupInputController, FontInputController, PositionInputController,
9
+ BrowserInputController, FixedPositionInputController, VectorInputController,
10
+ AngleInputController, TimeInputController, RoundedCornersInputController, TagsInputController
11
+ } from './index';
12
+
13
+ type ControllerComponents = {
14
+ textinput: typeof TextInputController;
15
+ textarea: typeof TextAreaPreviewInputController;
16
+ number: typeof NumberInputController;
17
+ slider: typeof SliderInputController;
18
+ sliderrange: typeof SliderRangeInputController;
19
+ dropdown: typeof DropdownInputController;
20
+ datepicker: typeof DatePickerInputController;
21
+ datefield: typeof DateFieldInputController;
22
+ rangecalendar: typeof RangeCalendarInputController;
23
+ switch: typeof SwitchInputController;
24
+ colorpicker: typeof ColorPickerInputController;
25
+ checkbox: typeof CheckboxInputController;
26
+ radiogroup: typeof RadioGroupInputController;
27
+ font: typeof FontInputController;
28
+ position: typeof PositionInputController;
29
+ browser: typeof BrowserInputController;
30
+ fixedposition: typeof FixedPositionInputController;
31
+ vector: typeof VectorInputController;
32
+ angle: typeof AngleInputController;
33
+ time: typeof TimeInputController;
34
+ roundedcorners: typeof RoundedCornersInputController;
35
+ tags: typeof TagsInputController;
36
+ };
37
+
38
+ // Each branch preserves the relationship between a controller and its props.
39
+ export type ControllerDescriptor = {
40
+ [Kind in keyof ControllerComponents]: { kind: Kind; props: ComponentProps<ControllerComponents[Kind]> }
41
+ }[keyof ControllerComponents];
42
+ </script>
43
+
44
+ <script lang="ts">
45
+ let { controller, class: className = '', style = '' }: {
46
+ controller: ControllerDescriptor | null;
47
+ class?: string;
48
+ style?: string;
49
+ } = $props();
50
+ </script>
51
+
52
+ <div class="input-controller-renderer contents {className}" {style}>
53
+ {#if controller?.kind === 'textinput'}
54
+ <TextInputController {...controller.props} />
55
+ {:else if controller?.kind === 'textarea'}
56
+ <TextAreaPreviewInputController {...controller.props} />
57
+ {:else if controller?.kind === 'number'}
58
+ <NumberInputController {...controller.props} />
59
+ {:else if controller?.kind === 'slider'}
60
+ <SliderInputController {...controller.props} />
61
+ {:else if controller?.kind === 'sliderrange'}
62
+ <SliderRangeInputController {...controller.props} />
63
+ {:else if controller?.kind === 'dropdown'}
64
+ <DropdownInputController {...controller.props} />
65
+ {:else if controller?.kind === 'datepicker'}
66
+ <DatePickerInputController {...controller.props} />
67
+ {:else if controller?.kind === 'datefield'}
68
+ <DateFieldInputController {...controller.props} />
69
+ {:else if controller?.kind === 'rangecalendar'}
70
+ <RangeCalendarInputController {...controller.props} />
71
+ {:else if controller?.kind === 'switch'}
72
+ <SwitchInputController {...controller.props} />
73
+ {:else if controller?.kind === 'colorpicker'}
74
+ <ColorPickerInputController {...controller.props} />
75
+ {:else if controller?.kind === 'checkbox'}
76
+ <CheckboxInputController {...controller.props} />
77
+ {:else if controller?.kind === 'radiogroup'}
78
+ <RadioGroupInputController {...controller.props} />
79
+ {:else if controller?.kind === 'font'}
80
+ <FontInputController {...controller.props} />
81
+ {:else if controller?.kind === 'position'}
82
+ <PositionInputController {...controller.props} />
83
+ {:else if controller?.kind === 'browser'}
84
+ <BrowserInputController {...controller.props} />
85
+ {:else if controller?.kind === 'fixedposition'}
86
+ <FixedPositionInputController {...controller.props} />
87
+ {:else if controller?.kind === 'vector'}
88
+ <VectorInputController {...controller.props} />
89
+ {:else if controller?.kind === 'angle'}
90
+ <AngleInputController {...controller.props} />
91
+ {:else if controller?.kind === 'time'}
92
+ <TimeInputController {...controller.props} />
93
+ {:else if controller?.kind === 'roundedcorners'}
94
+ <RoundedCornersInputController {...controller.props} />
95
+ {:else if controller?.kind === 'tags'}
96
+ <TagsInputController {...controller.props} />
97
+ {/if}
98
+ </div>
@@ -0,0 +1,40 @@
1
+ import type { ComponentProps } from 'svelte';
2
+ import { TextInputController, TextAreaPreviewInputController, NumberInputController, SliderInputController, SliderRangeInputController, DropdownInputController, DatePickerInputController, DateFieldInputController, RangeCalendarInputController, SwitchInputController, ColorPickerInputController, CheckboxInputController, RadioGroupInputController, FontInputController, PositionInputController, BrowserInputController, FixedPositionInputController, VectorInputController, AngleInputController, TimeInputController, RoundedCornersInputController, TagsInputController } from './index';
3
+ type ControllerComponents = {
4
+ textinput: typeof TextInputController;
5
+ textarea: typeof TextAreaPreviewInputController;
6
+ number: typeof NumberInputController;
7
+ slider: typeof SliderInputController;
8
+ sliderrange: typeof SliderRangeInputController;
9
+ dropdown: typeof DropdownInputController;
10
+ datepicker: typeof DatePickerInputController;
11
+ datefield: typeof DateFieldInputController;
12
+ rangecalendar: typeof RangeCalendarInputController;
13
+ switch: typeof SwitchInputController;
14
+ colorpicker: typeof ColorPickerInputController;
15
+ checkbox: typeof CheckboxInputController;
16
+ radiogroup: typeof RadioGroupInputController;
17
+ font: typeof FontInputController;
18
+ position: typeof PositionInputController;
19
+ browser: typeof BrowserInputController;
20
+ fixedposition: typeof FixedPositionInputController;
21
+ vector: typeof VectorInputController;
22
+ angle: typeof AngleInputController;
23
+ time: typeof TimeInputController;
24
+ roundedcorners: typeof RoundedCornersInputController;
25
+ tags: typeof TagsInputController;
26
+ };
27
+ export type ControllerDescriptor = {
28
+ [Kind in keyof ControllerComponents]: {
29
+ kind: Kind;
30
+ props: ComponentProps<ControllerComponents[Kind]>;
31
+ };
32
+ }[keyof ControllerComponents];
33
+ type $$ComponentProps = {
34
+ controller: ControllerDescriptor | null;
35
+ class?: string;
36
+ style?: string;
37
+ };
38
+ declare const InputControllerRenderer: import("svelte").Component<$$ComponentProps, {}, "">;
39
+ type InputControllerRenderer = ReturnType<typeof InputControllerRenderer>;
40
+ export default InputControllerRenderer;
@@ -26,7 +26,6 @@
26
26
  export let label: string;
27
27
  export let value: { topLeft: number; topRight: number; bottomRight: number; bottomLeft: number };
28
28
  export let onChange: (newValue: { topLeft: number; topRight: number; bottomRight: number; bottomLeft: number }) => void;
29
- export let config: InputControllerConfig = {};
30
29
  export let helpText: string | undefined = undefined;
31
30
  export let onFocus: () => void = () => {};
32
31
 
@@ -269,7 +268,7 @@
269
268
  </script>
270
269
 
271
270
  <!-- Main corner radius grid component -->
272
- <div class="corners-container {className}"
271
+ <div class="corners-container {className}" role="group" aria-label={label || 'Corner radius'} title={helpText}
273
272
  on:mouseenter={handleMouseEnter}
274
273
  on:mouseleave={handleMouseLeave}
275
274
  on:focusin={onFocus}>
@@ -360,9 +359,9 @@
360
359
  aria-pressed={isLocked}
361
360
  >
362
361
  <div class="flex items-center justify-center w-full h-full {isLocked ? 'text-sage-green' : 'text-grey7'}">
363
- <svelte:component
364
- this={isLocked ? Lock : Unlock}
365
- class="w-5 h-5"
362
+ <svelte:component
363
+ this={isLocked ? Lock : Unlock}
364
+ class="w-5 h-5"
366
365
  />
367
366
  </div>
368
367
  </button>
@@ -372,19 +371,10 @@
372
371
  <div
373
372
  class="border-overlay {isHovering ? 'hovered' : ''}"
374
373
  aria-hidden="true"
375
- />
374
+ ></div>
376
375
  </div>
377
376
 
378
377
  <style>
379
- /* Hide number input spinners */
380
- /* svelte-ignore css_unused_selector */
381
- input[type="number"]::-webkit-inner-spin-button,
382
- /* svelte-ignore css_unused_selector */
383
- input[type="number"]::-webkit-outer-spin-button {
384
- -webkit-appearance: none;
385
- margin: 0;
386
- }
387
-
388
378
  input {
389
379
  -moz-appearance: textfield;
390
380
  appearance: textfield;
@@ -1,4 +1,3 @@
1
- import type { InputControllerConfig } from './index';
2
1
  interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
2
  new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
3
  $$bindings?: Bindings;
@@ -26,7 +25,6 @@ declare const RoundedCornersInputController: $$__sveltets_2_IsomorphicComponent<
26
25
  bottomRight: number;
27
26
  bottomLeft: number;
28
27
  }) => void;
29
- config?: InputControllerConfig;
30
28
  helpText?: string | undefined;
31
29
  onFocus?: () => void;
32
30
  class?: string;
@@ -83,7 +83,7 @@
83
83
  }>();
84
84
 
85
85
  // Use config values with fallbacks
86
- const textColor = config.textColor || '#1a1a1a';
86
+ const textColor = $derived(config.textColor || '#1a1a1a');
87
87
 
88
88
  /**
89
89
  * State Variables
@@ -121,7 +121,7 @@
121
121
  let currentTextStyle = $state('text'); // Track current text style
122
122
 
123
123
  let showColorPicker = $state(false);
124
- let currentColor = $state(textColor);
124
+ let currentColor = $derived(textColor);
125
125
  let selectedTextRange: { from: number, to: number } | null = $state(null);
126
126
 
127
127
  const textOptions = [
@@ -159,16 +159,16 @@
159
159
 
160
160
 
161
161
 
162
- // Update to use $derived for reactive statement
163
- const editorStyle = $derived(`
162
+ // Static styles — avoid $derived so teardown does not leave inert derived reads.
163
+ const editorStyle = `
164
164
  --editor-min-height: auto;
165
165
  --editor-max-height: none;
166
166
  --transition-duration: 0ms;
167
167
  --transition-timing: ease-out;
168
- `);
168
+ `;
169
169
 
170
170
  // Add this with other state declarations
171
- let editorClass = $derived(`border-none overflow-auto editor-height ${''}`);
171
+ let editorClass = 'border-none overflow-auto editor-height';
172
172
 
173
173
  // Add state to track if any dropdown is open
174
174
  let isDropdownOpen = $state(false);
@@ -191,6 +191,7 @@
191
191
  // Add these new state variables after the other state declarations
192
192
  let lastKnownContent = $state('');
193
193
  let wasVisible = $state(true);
194
+ let isDestroyed = false;
194
195
 
195
196
  function handleEditorMount(e: any) {
196
197
  editorInstance = e.detail.editor;
@@ -361,6 +362,7 @@
361
362
  // Only update if there's a change to prevent unnecessary rerenders
362
363
  if (JSON.stringify(newFormats) !== JSON.stringify($state.snapshot(activeFormats))) {
363
364
  activeFormats = newFormats;
365
+ syncToggleValues();
364
366
  // console.log('Active formats updated:', $state.snapshot(activeFormats));
365
367
  }
366
368
  }
@@ -440,11 +442,20 @@
440
442
  }
441
443
  }
442
444
 
443
- // Update derived toggleValues with proper typing
444
- const toggleValues = $derived(Object.entries(activeFormats)
445
- .filter(([_, isActive]) => isActive)
446
- .map(([format]) => formatMapping[format as FormatType].value)
447
- );
445
+ // Compute toggle values without $derived — TipTap callbacks can run after unmount
446
+ // and reading an inert derived triggers Svelte's derived_inert warning.
447
+ function getToggleValues(): string[] {
448
+ if (isDestroyed) return [];
449
+ return Object.entries(activeFormats)
450
+ .filter(([_, isActive]) => isActive)
451
+ .map(([format]) => formatMapping[format as FormatType].value);
452
+ }
453
+
454
+ let toggleValues = $state<string[]>([]);
455
+ function syncToggleValues() {
456
+ if (isDestroyed) return;
457
+ toggleValues = getToggleValues();
458
+ }
448
459
 
449
460
  // Restore handleTextStyleChange for other style changes
450
461
  function handleTextStyleChange(value: string) {
@@ -823,7 +834,9 @@
823
834
 
824
835
  // Add effect to handle clicks outside Color Select
825
836
  $effect(() => {
837
+ if (isDestroyed) return;
826
838
  const handleClickOutside = (event: MouseEvent) => {
839
+ if (isDestroyed) return;
827
840
  const colorSelectModal = document.querySelector('[data-radix-popper-content-wrapper]');
828
841
  const isClickingColorSelect = colorSelectModal?.contains(event.target as Node);
829
842
 
@@ -896,6 +909,7 @@
896
909
 
897
910
  // Update the effect that maintains selection during color picker interactions
898
911
  $effect(() => {
912
+ if (isDestroyed) return;
899
913
  if (showColorPicker && selectedTextRange && editorInstance) {
900
914
  const { from, to } = selectedTextRange;
901
915
  editorInstance.chain()
@@ -1020,6 +1034,7 @@
1020
1034
 
1021
1035
  // Add cleanup function
1022
1036
  onDestroy(() => {
1037
+ isDestroyed = true;
1023
1038
  if (editorInstance) {
1024
1039
  // Store the current content before unmounting
1025
1040
  lastKnownContent = editorInstance.getHTML();
@@ -1336,8 +1351,6 @@
1336
1351
  handleColorChange(color);
1337
1352
  }}
1338
1353
  controllerId="text-color-picker"
1339
- {openDropdowns}
1340
- {toggleDropdown}
1341
1354
  saveRecentColors={true}
1342
1355
  showOpacityControls={false}
1343
1356
  showRing={false}
@@ -1,4 +1,8 @@
1
- <svg width="12" height="22" viewBox="0 0 12 22" fill="none" xmlns="http://www.w3.org/2000/svg">
1
+ <script>
2
+ let { class: className = '', style = '' } = $props();
3
+ </script>
4
+
5
+ <svg class="locked-icon {className}" {style} width="12" height="22" viewBox="0 0 12 22" fill="none" xmlns="http://www.w3.org/2000/svg">
2
6
  <path d="M1 6C1 4.67392 1.52678 3.40215 2.46447 2.46447C3.40215 1.52678 4.67392 1 6 1C7.32608 1 8.59785 1.52678 9.53553 2.46447C10.4732 3.40215 11 4.67392 11 6V16C11 17.3261 10.4732 18.5979 9.53553 19.5355C8.59785 20.4732 7.32608 21 6 21C4.67392 21 3.40215 20.4732 2.46447 19.5355C1.52678 18.5979 1 17.3261 1 16V6Z" stroke="#7BA668" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
3
7
  <rect x="5" y="6" width="2" height="10" fill="#7BA668"/>
4
8
  </svg>
@@ -1,26 +1,13 @@
1
1
  export default Locked;
2
- type Locked = SvelteComponent<{
3
- [x: string]: never;
4
- }, {
5
- [evt: string]: CustomEvent<any>;
6
- }, {}> & {
7
- $$bindings?: string;
2
+ type Locked = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const Locked: import("svelte").Component<{
7
+ class?: string;
8
+ style?: string;
9
+ }, {}, "">;
10
+ type $$ComponentProps = {
11
+ class?: string;
12
+ style?: string;
8
13
  };
9
- declare const Locked: $$__sveltets_2_IsomorphicComponent<{
10
- [x: string]: never;
11
- }, {
12
- [evt: string]: CustomEvent<any>;
13
- }, {}, {}, string>;
14
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
15
- new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
16
- $$bindings?: Bindings;
17
- } & Exports;
18
- (internal: unknown, props: {
19
- $$events?: Events;
20
- $$slots?: Slots;
21
- }): Exports & {
22
- $set?: any;
23
- $on?: any;
24
- };
25
- z_$$bindings?: Bindings;
26
- }
@@ -1,4 +1,8 @@
1
- <svg width="12" height="22" viewBox="0 0 12 22" fill="none" xmlns="http://www.w3.org/2000/svg">
1
+ <script>
2
+ let { class: className = '', style = '' } = $props();
3
+ </script>
4
+
5
+ <svg class="unlocked-icon {className}" {style} width="12" height="22" viewBox="0 0 12 22" fill="none" xmlns="http://www.w3.org/2000/svg">
2
6
  <path d="M3.17157 3.17157C3.92172 2.42143 4.93913 2 6 2C7.06087 2 8.07828 2.42143 8.82843 3.17157C9.57857 3.92172 10 4.93913 10 6V10H12V6C12 4.4087 11.3679 2.88258 10.2426 1.75736C9.11742 0.632141 7.5913 0 6 0C4.4087 0 2.88258 0.632141 1.75736 1.75736C0.632141 2.88258 0 4.4087 0 6V10H2V6C2 4.93913 2.42143 3.92172 3.17157 3.17157Z" fill="#666666"/>
3
7
  <path d="M2 12H0V16C0 17.5913 0.632141 19.1174 1.75736 20.2426C2.88258 21.3679 4.4087 22 6 22C7.5913 22 9.11742 21.3679 10.2426 20.2426C11.3679 19.1174 12 17.5913 12 16V12H10V16C10 17.0609 9.57857 18.0783 8.82843 18.8284C8.07828 19.5786 7.06087 20 6 20C4.93913 20 3.92172 19.5786 3.17157 18.8284C2.42143 18.0783 2 17.0609 2 16V12Z" fill="#666666"/>
4
8
  </svg>
@@ -1,26 +1,13 @@
1
1
  export default Unlocked;
2
- type Unlocked = SvelteComponent<{
3
- [x: string]: never;
4
- }, {
5
- [evt: string]: CustomEvent<any>;
6
- }, {}> & {
7
- $$bindings?: string;
2
+ type Unlocked = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<$$ComponentProps>): void;
5
+ };
6
+ declare const Unlocked: import("svelte").Component<{
7
+ class?: string;
8
+ style?: string;
9
+ }, {}, "">;
10
+ type $$ComponentProps = {
11
+ class?: string;
12
+ style?: string;
8
13
  };
9
- declare const Unlocked: $$__sveltets_2_IsomorphicComponent<{
10
- [x: string]: never;
11
- }, {
12
- [evt: string]: CustomEvent<any>;
13
- }, {}, {}, string>;
14
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
15
- new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
16
- $$bindings?: Bindings;
17
- } & Exports;
18
- (internal: unknown, props: {
19
- $$events?: Events;
20
- $$slots?: Slots;
21
- }): Exports & {
22
- $set?: any;
23
- $on?: any;
24
- };
25
- z_$$bindings?: Bindings;
26
- }
@@ -245,13 +245,20 @@ class FontManager {
245
245
  * Gets or creates the singleton instance and initializes it
246
246
  */
247
247
  static async getInstance(apiKey) {
248
+ const trimmedKey = String(apiKey || '').trim();
248
249
  if (!FontManager.instance) {
249
- FontManager.instance = new FontManager(apiKey);
250
- // Initialize only once
250
+ FontManager.instance = new FontManager(trimmedKey);
251
251
  if (!FontManager.initializationPromise) {
252
252
  FontManager.initializationPromise = FontManager.instance.initialize();
253
253
  }
254
- // Wait for initialization to complete
254
+ }
255
+ else if (trimmedKey && !FontManager.instance.apiKey?.trim()) {
256
+ // Key arrived after a premature empty-key construction (race with setGoogleFontsApiKey).
257
+ FontManager.instance.apiKey = trimmedKey;
258
+ FontManager.instance.catalogFetchPromise = null;
259
+ }
260
+ // Always await in-flight init — concurrent callers used to return mid-initialize.
261
+ if (FontManager.initializationPromise) {
255
262
  await FontManager.initializationPromise;
256
263
  }
257
264
  return FontManager.instance;