@custardui/custardui 1.0.0 → 2.0.0

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 (48) hide show
  1. package/dist/custardui.js +1573 -685
  2. package/dist/custardui.js.map +1 -1
  3. package/dist/custardui.min.js +2 -2
  4. package/dist/custardui.min.js.map +1 -1
  5. package/dist/types/src/lib/app/types.d.ts +36 -0
  6. package/dist/types/src/lib/app/types.d.ts.map +1 -0
  7. package/dist/types/src/lib/app/ui-manager.d.ts +2 -35
  8. package/dist/types/src/lib/app/ui-manager.d.ts.map +1 -1
  9. package/dist/types/src/lib/features/focus/services/focus-service.svelte.d.ts +2 -2
  10. package/dist/types/src/lib/features/focus/services/focus-service.svelte.d.ts.map +1 -1
  11. package/dist/types/src/lib/features/highlight/highlight-logic.d.ts +6 -1
  12. package/dist/types/src/lib/features/highlight/highlight-logic.d.ts.map +1 -1
  13. package/dist/types/src/lib/features/highlight/services/highlight-annotations.d.ts +10 -0
  14. package/dist/types/src/lib/features/highlight/services/highlight-annotations.d.ts.map +1 -0
  15. package/dist/types/src/lib/features/highlight/services/highlight-colors.d.ts +24 -0
  16. package/dist/types/src/lib/features/highlight/services/highlight-colors.d.ts.map +1 -0
  17. package/dist/types/src/lib/features/highlight/services/highlight-service.svelte.d.ts +5 -0
  18. package/dist/types/src/lib/features/highlight/services/highlight-service.svelte.d.ts.map +1 -1
  19. package/dist/types/src/lib/features/highlight/services/highlight-types.d.ts +5 -0
  20. package/dist/types/src/lib/features/highlight/services/highlight-types.d.ts.map +1 -1
  21. package/dist/types/src/lib/features/placeholder/placeholder-binder.d.ts +13 -1
  22. package/dist/types/src/lib/features/placeholder/placeholder-binder.d.ts.map +1 -1
  23. package/dist/types/src/lib/features/placeholder/placeholder-manager.d.ts +6 -0
  24. package/dist/types/src/lib/features/placeholder/placeholder-manager.d.ts.map +1 -1
  25. package/dist/types/src/lib/features/placeholder/types.d.ts +6 -0
  26. package/dist/types/src/lib/features/placeholder/types.d.ts.map +1 -1
  27. package/dist/types/src/lib/features/settings/intro-manager.svelte.d.ts +3 -6
  28. package/dist/types/src/lib/features/settings/intro-manager.svelte.d.ts.map +1 -1
  29. package/dist/types/src/lib/features/settings/stores/icon-settings-store.svelte.d.ts +18 -0
  30. package/dist/types/src/lib/features/settings/stores/icon-settings-store.svelte.d.ts.map +1 -0
  31. package/dist/types/src/lib/features/settings/types.d.ts +1 -1
  32. package/dist/types/src/lib/features/settings/types.d.ts.map +1 -1
  33. package/dist/types/src/lib/features/share/stores/share-store.svelte.d.ts +12 -1
  34. package/dist/types/src/lib/features/share/stores/share-store.svelte.d.ts.map +1 -1
  35. package/dist/types/src/lib/features/url/url-state-manager.d.ts.map +1 -1
  36. package/dist/types/src/lib/runtime.svelte.d.ts +4 -6
  37. package/dist/types/src/lib/runtime.svelte.d.ts.map +1 -1
  38. package/dist/types/src/lib/stores/active-state-store.svelte.d.ts +12 -3
  39. package/dist/types/src/lib/stores/active-state-store.svelte.d.ts.map +1 -1
  40. package/dist/types/src/lib/stores/derived-store.svelte.d.ts +1 -0
  41. package/dist/types/src/lib/stores/derived-store.svelte.d.ts.map +1 -1
  42. package/dist/types/src/lib/utils/dom-element-locator.d.ts +5 -0
  43. package/dist/types/src/lib/utils/dom-element-locator.d.ts.map +1 -1
  44. package/dist/types/src/lib/utils/scroll-utils.d.ts +0 -6
  45. package/dist/types/src/lib/utils/scroll-utils.d.ts.map +1 -1
  46. package/dist/types/tests/lib/features/settings/stores/icon-settings-store.test.d.ts +2 -0
  47. package/dist/types/tests/lib/features/settings/stores/icon-settings-store.test.d.ts.map +1 -0
  48. package/package.json +1 -1
package/dist/custardui.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @custardui/custardui v1.0.0
2
+ * @custardui/custardui v2.0.0
3
3
  * (c) 2026 Chan Ger Teck
4
4
  * Released under the MIT License.
5
5
  */
@@ -559,6 +559,22 @@
559
559
  component_context = context;
560
560
  }
561
561
 
562
+ /**
563
+ * Retrieves the context that belongs to the closest parent component with the specified `key`.
564
+ * Must be called during component initialisation.
565
+ *
566
+ * [`createContext`](https://svelte.dev/docs/svelte/svelte#createContext) is a type-safe alternative.
567
+ *
568
+ * @template T
569
+ * @param {any} key
570
+ * @returns {T}
571
+ */
572
+ function getContext(key) {
573
+ const context_map = get_or_init_context_map();
574
+ const result = /** @type {T} */ (context_map.get(key));
575
+ return result;
576
+ }
577
+
562
578
  /**
563
579
  * @param {Record<string, unknown>} props
564
580
  * @param {any} runes
@@ -610,6 +626,34 @@
610
626
  return !legacy_mode_flag || (component_context !== null && component_context.l === null);
611
627
  }
612
628
 
629
+ /**
630
+ * @param {string} name
631
+ * @returns {Map<unknown, unknown>}
632
+ */
633
+ function get_or_init_context_map(name) {
634
+ if (component_context === null) {
635
+ lifecycle_outside_component();
636
+ }
637
+
638
+ return (component_context.c ??= new Map(get_parent_context(component_context) || undefined));
639
+ }
640
+
641
+ /**
642
+ * @param {ComponentContext} component_context
643
+ * @returns {Map<unknown, unknown> | null}
644
+ */
645
+ function get_parent_context(component_context) {
646
+ let parent = component_context.p;
647
+ while (parent !== null) {
648
+ const context_map = parent.c;
649
+ if (context_map !== null) {
650
+ return context_map;
651
+ }
652
+ parent = parent.p;
653
+ }
654
+ return null;
655
+ }
656
+
613
657
  /** @type {Array<() => void>} */
614
658
  let micro_tasks = [];
615
659
 
@@ -3053,6 +3097,18 @@
3053
3097
  }
3054
3098
  }
3055
3099
 
3100
+ /**
3101
+ * The child of a textarea actually corresponds to the defaultValue property, so we need
3102
+ * to remove it upon hydration to avoid a bug when someone resets the form value.
3103
+ * @param {HTMLTextAreaElement} dom
3104
+ * @returns {void}
3105
+ */
3106
+ function remove_textarea_child(dom) {
3107
+ if (hydrating && get_first_child(dom) !== null) {
3108
+ clear_text_content(dom);
3109
+ }
3110
+ }
3111
+
3056
3112
  let listening_to_form_reset = false;
3057
3113
 
3058
3114
  function add_form_reset_listener() {
@@ -3095,33 +3151,6 @@
3095
3151
  }
3096
3152
  }
3097
3153
 
3098
- /**
3099
- * Listen to the given event, and then instantiate a global form reset listener if not already done,
3100
- * to notify all bindings when the form is reset
3101
- * @param {HTMLElement} element
3102
- * @param {string} event
3103
- * @param {(is_reset?: true) => void} handler
3104
- * @param {(is_reset?: true) => void} [on_reset]
3105
- */
3106
- function listen_to_event_and_reset_event(element, event, handler, on_reset = handler) {
3107
- element.addEventListener(event, () => without_reactive_context(handler));
3108
- // @ts-expect-error
3109
- const prev = element.__on_r;
3110
- if (prev) {
3111
- // special case for checkbox that can have multiple binds (group & checked)
3112
- // @ts-expect-error
3113
- element.__on_r = () => {
3114
- prev();
3115
- on_reset(true);
3116
- };
3117
- } else {
3118
- // @ts-expect-error
3119
- element.__on_r = () => on_reset(true);
3120
- }
3121
-
3122
- add_form_reset_listener();
3123
- }
3124
-
3125
3154
  /** @import { ComponentContext, ComponentContextLegacy, Derived, Effect, TemplateNode, TransitionManager } from '#client' */
3126
3155
 
3127
3156
  /**
@@ -5445,6 +5474,18 @@
5445
5474
  /** @import { EachItem, EachOutroGroup, EachState, Effect, EffectNodes, MaybeSource, Source, TemplateNode, TransitionManager, Value } from '#client' */
5446
5475
  /** @import { Batch } from '../../reactivity/batch.js'; */
5447
5476
 
5477
+ // When making substantive changes to this file, validate them with the each block stress test:
5478
+ // https://svelte.dev/playground/1972b2cf46564476ad8c8c6405b23b7b
5479
+ // This test also exists in this repo, as `packages/svelte/tests/manual/each-stress-test`
5480
+
5481
+ /**
5482
+ * @param {any} _
5483
+ * @param {number} i
5484
+ */
5485
+ function index(_, i) {
5486
+ return i;
5487
+ }
5488
+
5448
5489
  /**
5449
5490
  * Pause multiple effects simultaneously, and coordinate their
5450
5491
  * subsequent destruction. Used in each blocks
@@ -7602,132 +7643,6 @@
7602
7643
  return setters;
7603
7644
  }
7604
7645
 
7605
- /** @import { Batch } from '../../../reactivity/batch.js' */
7606
-
7607
- /** @type {Set<HTMLInputElement[]>} */
7608
- const pending = new Set();
7609
-
7610
- /**
7611
- * @param {HTMLInputElement[]} inputs
7612
- * @param {null | [number]} group_index
7613
- * @param {HTMLInputElement} input
7614
- * @param {() => unknown} get
7615
- * @param {(value: unknown) => void} set
7616
- * @returns {void}
7617
- */
7618
- function bind_group(inputs, group_index, input, get, set = get) {
7619
- var is_checkbox = input.getAttribute('type') === 'checkbox';
7620
- var binding_group = inputs;
7621
-
7622
- // needs to be let or related code isn't treeshaken out if it's always false
7623
- let hydration_mismatch = false;
7624
-
7625
- if (group_index !== null) {
7626
- for (var index of group_index) {
7627
- // @ts-expect-error
7628
- binding_group = binding_group[index] ??= [];
7629
- }
7630
- }
7631
-
7632
- binding_group.push(input);
7633
-
7634
- listen_to_event_and_reset_event(
7635
- input,
7636
- 'change',
7637
- () => {
7638
- // @ts-ignore
7639
- var value = input.__value;
7640
-
7641
- if (is_checkbox) {
7642
- value = get_binding_group_value(binding_group, value, input.checked);
7643
- }
7644
-
7645
- set(value);
7646
- },
7647
- // TODO better default value handling
7648
- () => set(is_checkbox ? [] : null)
7649
- );
7650
-
7651
- render_effect(() => {
7652
- var value = get();
7653
-
7654
- // If we are hydrating and the value has since changed, then use the update value
7655
- // from the input instead.
7656
- if (hydrating && input.defaultChecked !== input.checked) {
7657
- hydration_mismatch = true;
7658
- return;
7659
- }
7660
-
7661
- if (is_checkbox) {
7662
- value = value || [];
7663
- // @ts-ignore
7664
- input.checked = value.includes(input.__value);
7665
- } else {
7666
- // @ts-ignore
7667
- input.checked = is(input.__value, value);
7668
- }
7669
- });
7670
-
7671
- teardown(() => {
7672
- var index = binding_group.indexOf(input);
7673
-
7674
- if (index !== -1) {
7675
- binding_group.splice(index, 1);
7676
- }
7677
- });
7678
-
7679
- if (!pending.has(binding_group)) {
7680
- pending.add(binding_group);
7681
-
7682
- queue_micro_task(() => {
7683
- // necessary to maintain binding group order in all insertion scenarios
7684
- binding_group.sort((a, b) => (a.compareDocumentPosition(b) === 4 ? -1 : 1));
7685
- pending.delete(binding_group);
7686
- });
7687
- }
7688
-
7689
- queue_micro_task(() => {
7690
- if (hydration_mismatch) {
7691
- var value;
7692
-
7693
- if (is_checkbox) {
7694
- value = get_binding_group_value(binding_group, value, input.checked);
7695
- } else {
7696
- var hydration_input = binding_group.find((input) => input.checked);
7697
- // @ts-ignore
7698
- value = hydration_input?.__value;
7699
- }
7700
-
7701
- set(value);
7702
- }
7703
- });
7704
- }
7705
-
7706
- /**
7707
- * @template V
7708
- * @param {Array<HTMLInputElement>} group
7709
- * @param {V} __value
7710
- * @param {boolean} checked
7711
- * @returns {V[]}
7712
- */
7713
- function get_binding_group_value(group, __value, checked) {
7714
- /** @type {Set<V>} */
7715
- var value = new Set();
7716
-
7717
- for (var i = 0; i < group.length; i += 1) {
7718
- if (group[i].checked) {
7719
- // @ts-ignore
7720
- value.add(group[i].__value);
7721
- }
7722
- }
7723
-
7724
- if (!checked) {
7725
- value.delete(__value);
7726
- }
7727
-
7728
- return Array.from(value);
7729
- }
7730
-
7731
7646
  /**
7732
7647
  * Resize observer singleton.
7733
7648
  * One listener per element only!
@@ -8653,6 +8568,104 @@
8653
8568
  return Class;
8654
8569
  }
8655
8570
 
8571
+ const RUNTIME_CALLBACKS_CTX = Symbol('cv-runtime-callbacks');
8572
+
8573
+ /* icon-settings-store.svelte.ts generated by Svelte v5.46.1 */
8574
+
8575
+ const COLLAPSED_KEY = 'cv-settings-icon-collapsed';
8576
+ const OFFSET_KEY = 'cv-settings-icon-offset';
8577
+
8578
+ const ICON_SETTINGS_CTX = Symbol('icon-settings');
8579
+
8580
+ class IconSettingsStore {
8581
+ persistence;
8582
+ #isCollapsed = state(false);
8583
+
8584
+ get isCollapsed() {
8585
+ return get(this.#isCollapsed);
8586
+ }
8587
+
8588
+ set isCollapsed(value) {
8589
+ set(this.#isCollapsed, value, true);
8590
+ }
8591
+
8592
+ #offset = state(0);
8593
+
8594
+ get offset() {
8595
+ return get(this.#offset);
8596
+ }
8597
+
8598
+ set offset(value) {
8599
+ set(this.#offset, value, true);
8600
+ }
8601
+
8602
+ #isDismissed = state(false);
8603
+
8604
+ get isDismissed() {
8605
+ return get(this.#isDismissed);
8606
+ }
8607
+
8608
+ set isDismissed(value) {
8609
+ set(this.#isDismissed, value, true);
8610
+ }
8611
+
8612
+ constructor(persistence) {
8613
+ this.persistence = persistence;
8614
+
8615
+ const savedCollapsed = persistence.getItem(COLLAPSED_KEY);
8616
+
8617
+ if (savedCollapsed !== null) {
8618
+ this.isCollapsed = savedCollapsed === 'true';
8619
+ } else if (typeof window !== 'undefined' && window.innerWidth <= 768) {
8620
+ this.isCollapsed = true;
8621
+ }
8622
+
8623
+ // Parse offset safely
8624
+ const savedOffset = persistence.getItem(OFFSET_KEY);
8625
+
8626
+ if (savedOffset !== null) {
8627
+ const parsedOffset = parseFloat(savedOffset);
8628
+
8629
+ if (Number.isFinite(parsedOffset)) {
8630
+ this.offset = parsedOffset;
8631
+ } else {
8632
+ // Corrupted value; clear it out.
8633
+ this.offset = 0;
8634
+
8635
+ persistence.removeItem(OFFSET_KEY);
8636
+ }
8637
+ }
8638
+ }
8639
+
8640
+ setCollapsed(value) {
8641
+ this.isCollapsed = value;
8642
+ this.persistence.setItem(COLLAPSED_KEY, value.toString());
8643
+ }
8644
+
8645
+ setOffset(value) {
8646
+ this.offset = value;
8647
+ this.persistence.setItem(OFFSET_KEY, value.toString());
8648
+ }
8649
+
8650
+ resetPositionAndCollapseState() {
8651
+ this.offset = 0;
8652
+ this.persistence.removeItem(OFFSET_KEY);
8653
+ this.persistence.removeItem(COLLAPSED_KEY);
8654
+
8655
+ if (typeof window !== 'undefined' && window.innerWidth <= 768) {
8656
+ this.isCollapsed = true;
8657
+ } else {
8658
+ this.isCollapsed = false;
8659
+ }
8660
+
8661
+ this.isDismissed = false;
8662
+ }
8663
+
8664
+ dismiss() {
8665
+ this.isDismissed = true;
8666
+ }
8667
+ }
8668
+
8656
8669
  /** @import { Source } from '#client' */
8657
8670
 
8658
8671
  var read_methods = ['forEach', 'isDisjointFrom', 'isSubsetOf', 'isSupersetOf'];
@@ -9154,6 +9167,8 @@
9154
9167
  config.placeholders.forEach((def) => {
9155
9168
  placeholderRegistryStore.register({
9156
9169
  ...def,
9170
+ // adaptationPlaceholder implies hidden from user-facing settings
9171
+ hiddenFromSettings: def.adaptationPlaceholder ? true : def.hiddenFromSettings,
9157
9172
  source: 'config',
9158
9173
  });
9159
9174
  });
@@ -9211,6 +9226,24 @@
9211
9226
  }
9212
9227
  return valid;
9213
9228
  }
9229
+ /**
9230
+ * Filters a record of incoming placeholders to only those that can be set by users.
9231
+ * Extends filterValidPlaceholders() by also excluding adaptation-only placeholders.
9232
+ * Use this for persistence loads and URL delta application.
9233
+ */
9234
+ filterUserSettablePlaceholders(placeholders = {}) {
9235
+ const valid = this.filterValidPlaceholders(placeholders);
9236
+ const userSettable = {};
9237
+ for (const [key, value] of Object.entries(valid)) {
9238
+ const def = placeholderRegistryStore.get(key);
9239
+ if (def?.adaptationPlaceholder) {
9240
+ // Silently skip — adaptation-only placeholders cannot be user-set
9241
+ continue;
9242
+ }
9243
+ userSettable[key] = value;
9244
+ }
9245
+ return userSettable;
9246
+ }
9214
9247
  // --- Internal Helpers ---
9215
9248
  registerPlaceholderFromTabGroup(groupConfig) {
9216
9249
  if (!groupConfig.placeholderId)
@@ -9346,6 +9379,24 @@
9346
9379
  this.state.peekToggles = peek;
9347
9380
  }
9348
9381
 
9382
+ /**
9383
+ * Update the visibility state of a specific toggle.
9384
+ * Handled internally so UI components (like Modal) don't need to mutate arrays directly.
9385
+ * @param toggleId The ID of the toggle.
9386
+ * @param value The new visibility state.
9387
+ */
9388
+ updateToggleState(toggleId, value) {
9389
+ const currentShown = this.state.shownToggles || [];
9390
+ const currentPeek = this.state.peekToggles || [];
9391
+ const newShown = currentShown.filter((id) => id !== toggleId);
9392
+ const newPeek = currentPeek.filter((id) => id !== toggleId);
9393
+
9394
+ if (value === 'show') newShown.push(toggleId);
9395
+ if (value === 'peek') newPeek.push(toggleId);
9396
+
9397
+ this.setToggles(newShown, newPeek);
9398
+ }
9399
+
9349
9400
  /**
9350
9401
  * Set a specific placeholder value.
9351
9402
  * @param key The ID/name of the placeholder.
@@ -9362,16 +9413,18 @@
9362
9413
  * Replaces the full application state (e.g. from persistence).
9363
9414
  *
9364
9415
  * Precedence model:
9365
- * 1. Start from computed defaults (config-driven).
9366
- * 2. Layer in the incoming `newState`, sanitizing tabs and placeholders.
9367
- * 3. Sync any tab-group-derived placeholders that weren't explicitly set.
9416
+ * 1. Start from computed defaults for toggles and tabs.
9417
+ * 2. Layer in the incoming `newState`, sanitizing toggles, tabs, and user-settable placeholders.
9418
+ * 3. For placeholders, use the CURRENT state as the base (preserving adaptation defaults)
9419
+ * and merge user-persisted values on top.
9420
+ * 4. Sync any tab-group-derived placeholders that weren't explicitly set.
9368
9421
  *
9369
9422
  * @param newState The persisted state to restore.
9370
9423
  */
9371
9424
  applyState(newState) {
9372
9425
  const defaults = this.computeDefaultState();
9373
9426
  const validatedTabs = this.filterValidTabs(newState.tabs ?? {});
9374
- const validatedPlaceholders = placeholderManager.filterValidPlaceholders(newState.placeholders ?? {});
9427
+ const validatedPlaceholders = placeholderManager.filterUserSettablePlaceholders(newState.placeholders ?? {});
9375
9428
  const validatedShownToggles = this.filterValidToggles(newState.shownToggles ?? defaults.shownToggles ?? []);
9376
9429
  const validatedPeekToggles = this.filterValidToggles(newState.peekToggles ?? defaults.peekToggles ?? []);
9377
9430
 
@@ -9379,7 +9432,10 @@
9379
9432
  shownToggles: validatedShownToggles,
9380
9433
  peekToggles: validatedPeekToggles,
9381
9434
  tabs: { ...defaults.tabs ?? {}, ...validatedTabs },
9382
- placeholders: { ...defaults.placeholders ?? {}, ...validatedPlaceholders }
9435
+
9436
+ // Use current state as base so adaptation defaults (layered before this call)
9437
+ // are preserved; user-persisted values still win on top.
9438
+ placeholders: { ...this.state.placeholders ?? {}, ...validatedPlaceholders }
9383
9439
  };
9384
9440
 
9385
9441
  // Sync derived placeholders for any tabs that shifted (and aren't explicitly overridden).
@@ -9584,7 +9640,7 @@
9584
9640
  * Explicit placeholder values override any tab-derived value (winning over syncPlaceholdersFromTabs).
9585
9641
  */
9586
9642
  applyPlaceholdersDelta(deltaPlaceholders) {
9587
- const validatedPlaceholders = placeholderManager.filterValidPlaceholders(deltaPlaceholders);
9643
+ const validatedPlaceholders = placeholderManager.filterUserSettablePlaceholders(deltaPlaceholders);
9588
9644
 
9589
9645
  if (!this.state.placeholders) this.state.placeholders = {};
9590
9646
 
@@ -9881,6 +9937,21 @@
9881
9937
  set(this.#menuTabGroups, value);
9882
9938
  }
9883
9939
 
9940
+ #hiddenToggleIds = user_derived(() => {
9941
+ const shown = activeStateStore.state.shownToggles ?? [];
9942
+ const peek = activeStateStore.state.peekToggles ?? [];
9943
+
9944
+ return [...elementStore.detectedToggles].filter((id) => !shown.includes(id) && !peek.includes(id));
9945
+ });
9946
+
9947
+ get hiddenToggleIds() {
9948
+ return get(this.#hiddenToggleIds);
9949
+ }
9950
+
9951
+ set hiddenToggleIds(value) {
9952
+ set(this.#hiddenToggleIds, value);
9953
+ }
9954
+
9884
9955
  #hasVisiblePlaceholders = user_derived(() => {
9885
9956
  return placeholderRegistryStore.definitions.some((d) => {
9886
9957
  if (d.hiddenFromSettings) return false;
@@ -9920,13 +9991,13 @@
9920
9991
 
9921
9992
  var root$t = from_html(`<div><div role="alert"><button class="close-btn svelte-ysaqmb" aria-label="Dismiss intro">×</button> <p class="text svelte-ysaqmb"> </p></div></div>`);
9922
9993
 
9923
- const $$css$k = {
9994
+ const $$css$m = {
9924
9995
  hash: 'svelte-ysaqmb',
9925
9996
  code: '\n /* Animation */\n @keyframes svelte-ysaqmb-popIn {\n 0% {\n opacity: 0;\n transform: scale(0.9) translateY(-50%);\n }\n 100% {\n opacity: 1;\n transform: scale(1) translateY(-50%);\n }\n }\n\n /* Reset transform for top/bottom positions */\n @keyframes svelte-ysaqmb-popInVertical {\n 0% {\n opacity: 0;\n transform: scale(0.9);\n }\n 100% {\n opacity: 1;\n transform: scale(1);\n }\n }\n\n /* Simplified Pulse Animation - Shadow Only */\n @keyframes svelte-ysaqmb-pulse {\n 0% {\n transform: scale(1);\n box-shadow:\n 0 4px 6px -1px rgba(0, 0, 0, 0.1),\n 0 0 0 0 rgba(62, 132, 244, 0.7);\n }\n 50% {\n transform: scale(1);\n box-shadow:\n 0 4px 6px -1px rgba(0, 0, 0, 0.1),\n 0 0 0 10px rgba(62, 132, 244, 0);\n }\n 100% {\n transform: scale(1);\n box-shadow:\n 0 4px 6px -1px rgba(0, 0, 0, 0.1),\n 0 0 0 0 rgba(62, 132, 244, 0);\n }\n }\n\n /* Wrapper handles Positioning & Entry Animation */.cv-callout-wrapper.svelte-ysaqmb {position:fixed;z-index:9999;\n\n /* Default animation (centered ones) */\n animation: svelte-ysaqmb-popIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;}\n\n /* Inner handles Visuals & Pulse Animation */.cv-callout.svelte-ysaqmb {background:var(--cv-callout-bg, var(--cv-bg));padding:1rem 1.25rem;border-radius:0.5rem;box-shadow:0 4px 6px -1px var(--cv-shadow),\n 0 2px 4px -1px var(--cv-shadow); /* adapt shadow? */max-width:250px;font-size:0.9rem;line-height:1.5;color:var(--cv-callout-text, var(--cv-text));display:flex;align-items:flex-start;gap:0.75rem;font-family:inherit;border:2px solid var(--cv-border);}\n\n /* Apply pulse to inner callout if enabled */.cv-callout.cv-pulse.svelte-ysaqmb {\n animation: svelte-ysaqmb-pulse 2s infinite 0.5s;}\n\n /* Arrow Base */.cv-callout.svelte-ysaqmb::before {content:\'\';position:absolute;width:1rem;height:1rem;background:var(--cv-callout-bg, var(--cv-bg));transform:rotate(45deg);border:2px solid var(--cv-border);z-index:-1;}.close-btn.svelte-ysaqmb {background:transparent;border:none;color:currentColor;opacity:0.7;font-size:1.25rem;line-height:1;cursor:pointer;padding:0;margin:-0.25rem -0.5rem 0 0;transition:opacity 0.15s;flex-shrink:0;}.close-btn.svelte-ysaqmb:hover {color:currentColor;opacity:1;}.text.svelte-ysaqmb {margin:0;flex:1;font-weight:500;}\n\n /* \n Position Specifics (Applied to Wrapper)\n */\n\n /* Right-side positions (Icon on Right -> Callout on Left) */.pos-top-right.svelte-ysaqmb,\n .pos-middle-right.svelte-ysaqmb,\n .pos-bottom-right.svelte-ysaqmb {right:80px;}.pos-top-right.svelte-ysaqmb,\n .pos-bottom-right.svelte-ysaqmb {\n animation-name: svelte-ysaqmb-popInVertical;}\n\n /* X Button Spacing Adjustments */.pos-top-right.svelte-ysaqmb .close-btn:where(.svelte-ysaqmb),\n .pos-middle-right.svelte-ysaqmb .close-btn:where(.svelte-ysaqmb),\n .pos-bottom-right.svelte-ysaqmb .close-btn:where(.svelte-ysaqmb) {margin-right:0;margin-left:-0.5rem;}\n\n /* Left-side positions (Icon on Left -> Callout on Right) */.pos-top-left.svelte-ysaqmb,\n .pos-middle-left.svelte-ysaqmb,\n .pos-bottom-left.svelte-ysaqmb {left:80px;}.pos-top-left.svelte-ysaqmb .close-btn:where(.svelte-ysaqmb),\n .pos-middle-left.svelte-ysaqmb .close-btn:where(.svelte-ysaqmb),\n .pos-bottom-left.svelte-ysaqmb .close-btn:where(.svelte-ysaqmb) {order:2; /* Move to end */margin-right:-0.5rem;margin-left:0;}.pos-top-left.svelte-ysaqmb,\n .pos-bottom-left.svelte-ysaqmb {\n animation-name: svelte-ysaqmb-popInVertical;}\n\n /* Vertical Alignment */.pos-middle-right.svelte-ysaqmb,\n .pos-middle-left.svelte-ysaqmb {top:50%;\n /* transform handled by popIn animation (translateY -50%) */}.pos-top-right.svelte-ysaqmb,\n .pos-top-left.svelte-ysaqmb {top:20px;}.pos-bottom-right.svelte-ysaqmb,\n .pos-bottom-left.svelte-ysaqmb {bottom:20px;}\n\n /* Arrow Positioning (Child of .callout, dependent on Wrapper .pos-*) */\n\n /* Pointing Right */.pos-top-right.svelte-ysaqmb .cv-callout:where(.svelte-ysaqmb)::before,\n .pos-middle-right.svelte-ysaqmb .cv-callout:where(.svelte-ysaqmb)::before,\n .pos-bottom-right.svelte-ysaqmb .cv-callout:where(.svelte-ysaqmb)::before {right:-0.5rem;border-left:none;border-bottom:none;}\n\n /* Pointing Left */.pos-top-left.svelte-ysaqmb .cv-callout:where(.svelte-ysaqmb)::before,\n .pos-middle-left.svelte-ysaqmb .cv-callout:where(.svelte-ysaqmb)::before,\n .pos-bottom-left.svelte-ysaqmb .cv-callout:where(.svelte-ysaqmb)::before {left:-0.5rem;border-right:none;border-top:none;}\n\n /* Vertical placement of arrow */.pos-middle-right.svelte-ysaqmb .cv-callout:where(.svelte-ysaqmb)::before,\n .pos-middle-left.svelte-ysaqmb .cv-callout:where(.svelte-ysaqmb)::before {top:50%;margin-top:-0.5rem;}.pos-top-right.svelte-ysaqmb .cv-callout:where(.svelte-ysaqmb)::before,\n .pos-top-left.svelte-ysaqmb .cv-callout:where(.svelte-ysaqmb)::before {top:1.25rem;}.pos-bottom-right.svelte-ysaqmb .cv-callout:where(.svelte-ysaqmb)::before,\n .pos-bottom-left.svelte-ysaqmb .cv-callout:where(.svelte-ysaqmb)::before {bottom:1.25rem;}\n\n @media print {.cv-callout-wrapper.svelte-ysaqmb {display:none !important;}\n }'
9926
9997
  };
9927
9998
 
9928
9999
  function IntroCallout($$anchor, $$props) {
9929
- append_styles$1($$anchor, $$css$k);
10000
+ append_styles$1($$anchor, $$css$m);
9930
10001
 
9931
10002
  let position = prop($$props, 'position', 3, 'middle-left'),
9932
10003
  message = prop($$props, 'message', 3, 'Customize your reading experience here.'),
@@ -9977,18 +10048,38 @@
9977
10048
 
9978
10049
  delegate(['click']);
9979
10050
 
9980
- var root$s = from_html(`<div role="button" tabindex="0" aria-label="Open Custom Views Settings"><span class="cv-gear svelte-122ln5">⚙</span></div>`);
10051
+ var root$s = from_svg(`<svg><path xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" d="M12 8.00002C9.79085 8.00002 7.99999 9.79088 7.99999 12C7.99999 14.2092 9.79085 16 12 16C14.2091 16 16 14.2092 16 12C16 9.79088 14.2091 8.00002 12 8.00002ZM9.99999 12C9.99999 10.8955 10.8954 10 12 10C13.1046 10 14 10.8955 14 12C14 13.1046 13.1046 14 12 14C10.8954 14 9.99999 13.1046 9.99999 12Z" fill="#0F1729"></path><path xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" d="M10.7673 1.01709C10.9925 0.999829 11.2454 0.99993 11.4516 1.00001L12.5484 1.00001C12.7546 0.99993 13.0075 0.999829 13.2327 1.01709C13.4989 1.03749 13.8678 1.08936 14.2634 1.26937C14.7635 1.49689 15.1915 1.85736 15.5007 2.31147C15.7454 2.67075 15.8592 3.0255 15.9246 3.2843C15.9799 3.50334 16.0228 3.75249 16.0577 3.9557L16.1993 4.77635L16.2021 4.77788C16.2369 4.79712 16.2715 4.81659 16.306 4.8363L16.3086 4.83774L17.2455 4.49865C17.4356 4.42978 17.6693 4.34509 17.8835 4.28543C18.1371 4.2148 18.4954 4.13889 18.9216 4.17026C19.4614 4.20998 19.9803 4.39497 20.4235 4.70563C20.7734 4.95095 21.0029 5.23636 21.1546 5.4515C21.2829 5.63326 21.4103 5.84671 21.514 6.02029L22.0158 6.86003C22.1256 7.04345 22.2594 7.26713 22.3627 7.47527C22.4843 7.7203 22.6328 8.07474 22.6777 8.52067C22.7341 9.08222 22.6311 9.64831 22.3803 10.1539C22.1811 10.5554 21.9171 10.8347 21.7169 11.0212C21.5469 11.1795 21.3428 11.3417 21.1755 11.4746L20.5 12L21.1755 12.5254C21.3428 12.6584 21.5469 12.8205 21.7169 12.9789C21.9171 13.1653 22.1811 13.4446 22.3802 13.8461C22.631 14.3517 22.7341 14.9178 22.6776 15.4794C22.6328 15.9253 22.4842 16.2797 22.3626 16.5248C22.2593 16.7329 22.1255 16.9566 22.0158 17.14L21.5138 17.9799C21.4102 18.1535 21.2828 18.3668 21.1546 18.5485C21.0028 18.7637 20.7734 19.0491 20.4234 19.2944C19.9803 19.6051 19.4613 19.7901 18.9216 19.8298C18.4954 19.8612 18.1371 19.7852 17.8835 19.7146C17.6692 19.6549 17.4355 19.5703 17.2454 19.5014L16.3085 19.1623L16.306 19.1638C16.2715 19.1835 16.2369 19.2029 16.2021 19.2222L16.1993 19.2237L16.0577 20.0443C16.0228 20.2475 15.9799 20.4967 15.9246 20.7157C15.8592 20.9745 15.7454 21.3293 15.5007 21.6886C15.1915 22.1427 14.7635 22.5032 14.2634 22.7307C13.8678 22.9107 13.4989 22.9626 13.2327 22.983C13.0074 23.0002 12.7546 23.0001 12.5484 23H11.4516C11.2454 23.0001 10.9925 23.0002 10.7673 22.983C10.5011 22.9626 10.1322 22.9107 9.73655 22.7307C9.23648 22.5032 8.80849 22.1427 8.49926 21.6886C8.25461 21.3293 8.14077 20.9745 8.07542 20.7157C8.02011 20.4967 7.97723 20.2475 7.94225 20.0443L7.80068 19.2237L7.79791 19.2222C7.7631 19.2029 7.72845 19.1835 7.69396 19.1637L7.69142 19.1623L6.75458 19.5014C6.5645 19.5702 6.33078 19.6549 6.11651 19.7146C5.86288 19.7852 5.50463 19.8611 5.07841 19.8298C4.53866 19.7901 4.01971 19.6051 3.57654 19.2944C3.2266 19.0491 2.99714 18.7637 2.84539 18.5485C2.71718 18.3668 2.58974 18.1534 2.4861 17.9798L1.98418 17.14C1.87447 16.9566 1.74067 16.7329 1.63737 16.5248C1.51575 16.2797 1.36719 15.9253 1.32235 15.4794C1.26588 14.9178 1.36897 14.3517 1.61976 13.8461C1.81892 13.4446 2.08289 13.1653 2.28308 12.9789C2.45312 12.8205 2.65717 12.6584 2.82449 12.5254L3.47844 12.0054V11.9947L2.82445 11.4746C2.65712 11.3417 2.45308 11.1795 2.28304 11.0212C2.08285 10.8347 1.81888 10.5554 1.61972 10.1539C1.36893 9.64832 1.26584 9.08224 1.3223 8.52069C1.36714 8.07476 1.51571 7.72032 1.63732 7.47528C1.74062 7.26715 1.87443 7.04347 1.98414 6.86005L2.48605 6.02026C2.58969 5.84669 2.71714 5.63326 2.84534 5.45151C2.9971 5.23637 3.22655 4.95096 3.5765 4.70565C4.01966 4.39498 4.53862 4.20999 5.07837 4.17027C5.50458 4.1389 5.86284 4.21481 6.11646 4.28544C6.33072 4.34511 6.56444 4.4298 6.75451 4.49867L7.69141 4.83775L7.69394 4.8363C7.72844 4.8166 7.7631 4.79712 7.79791 4.77788L7.80068 4.77635L7.94225 3.95571C7.97723 3.7525 8.02011 3.50334 8.07542 3.2843C8.14077 3.0255 8.25461 2.67075 8.49926 2.31147C8.80849 1.85736 9.23648 1.49689 9.73655 1.26937C10.1322 1.08936 10.5011 1.03749 10.7673 1.01709ZM14.0938 4.3363C14.011 3.85634 13.9696 3.61637 13.8476 3.43717C13.7445 3.2858 13.6019 3.16564 13.4352 3.0898C13.2378 3.00002 12.9943 3.00002 12.5073 3.00002H11.4927C11.0057 3.00002 10.7621 3.00002 10.5648 3.0898C10.3981 3.16564 10.2555 3.2858 10.1524 3.43717C10.0304 3.61637 9.98895 3.85634 9.90615 4.3363L9.75012 5.24064C9.69445 5.56333 9.66662 5.72467 9.60765 5.84869C9.54975 5.97047 9.50241 6.03703 9.40636 6.13166C9.30853 6.22804 9.12753 6.3281 8.76554 6.52822C8.73884 6.54298 8.71227 6.55791 8.68582 6.57302C8.33956 6.77078 8.16643 6.86966 8.03785 6.90314C7.91158 6.93602 7.83293 6.94279 7.70289 6.93196C7.57049 6.92094 7.42216 6.86726 7.12551 6.7599L6.11194 6.39308C5.66271 6.2305 5.43809 6.14921 5.22515 6.16488C5.04524 6.17811 4.87225 6.23978 4.72453 6.34333C4.5497 6.46589 4.42715 6.67094 4.18206 7.08103L3.72269 7.84965C3.46394 8.2826 3.33456 8.49907 3.31227 8.72078C3.29345 8.90796 3.32781 9.09665 3.41141 9.26519C3.51042 9.4648 3.7078 9.62177 4.10256 9.9357L4.82745 10.5122C5.07927 10.7124 5.20518 10.8126 5.28411 10.9199C5.36944 11.036 5.40583 11.1114 5.44354 11.2504C5.47844 11.379 5.47844 11.586 5.47844 12C5.47844 12.414 5.47844 12.621 5.44354 12.7497C5.40582 12.8887 5.36944 12.9641 5.28413 13.0801C5.20518 13.1875 5.07927 13.2876 4.82743 13.4879L4.10261 14.0643C3.70785 14.3783 3.51047 14.5352 3.41145 14.7349C3.32785 14.9034 3.29349 15.0921 3.31231 15.2793C3.33461 15.501 3.46398 15.7174 3.72273 16.1504L4.1821 16.919C4.4272 17.3291 4.54974 17.5342 4.72457 17.6567C4.8723 17.7603 5.04528 17.8219 5.2252 17.8352C5.43813 17.8508 5.66275 17.7695 6.11199 17.607L7.12553 17.2402C7.42216 17.1328 7.5705 17.0791 7.7029 17.0681C7.83294 17.0573 7.91159 17.064 8.03786 17.0969C8.16644 17.1304 8.33956 17.2293 8.68582 17.427C8.71228 17.4421 8.73885 17.4571 8.76554 17.4718C9.12753 17.6719 9.30853 17.772 9.40635 17.8684C9.50241 17.963 9.54975 18.0296 9.60765 18.1514C9.66662 18.2754 9.69445 18.4367 9.75012 18.7594L9.90615 19.6637C9.98895 20.1437 10.0304 20.3837 10.1524 20.5629C10.2555 20.7142 10.3981 20.8344 10.5648 20.9102C10.7621 21 11.0057 21 11.4927 21H12.5073C12.9943 21 13.2378 21 13.4352 20.9102C13.6019 20.8344 13.7445 20.7142 13.8476 20.5629C13.9696 20.3837 14.011 20.1437 14.0938 19.6637L14.2499 18.7594C14.3055 18.4367 14.3334 18.2754 14.3923 18.1514C14.4502 18.0296 14.4976 17.963 14.5936 17.8684C14.6915 17.772 14.8725 17.6719 15.2344 17.4718C15.2611 17.4571 15.2877 17.4421 15.3141 17.427C15.6604 17.2293 15.8335 17.1304 15.9621 17.0969C16.0884 17.064 16.167 17.0573 16.2971 17.0681C16.4295 17.0791 16.5778 17.1328 16.8744 17.2402L17.888 17.607C18.3372 17.7696 18.5619 17.8509 18.7748 17.8352C18.9547 17.8219 19.1277 17.7603 19.2754 17.6567C19.4502 17.5342 19.5728 17.3291 19.8179 16.919L20.2773 16.1504C20.536 15.7175 20.6654 15.501 20.6877 15.2793C20.7065 15.0921 20.6721 14.9034 20.5885 14.7349C20.4895 14.5353 20.2921 14.3783 19.8974 14.0643L19.1726 13.4879C18.9207 13.2876 18.7948 13.1875 18.7159 13.0801C18.6306 12.9641 18.5942 12.8887 18.5564 12.7497C18.5215 12.6211 18.5215 12.414 18.5215 12C18.5215 11.586 18.5215 11.379 18.5564 11.2504C18.5942 11.1114 18.6306 11.036 18.7159 10.9199C18.7948 10.8126 18.9207 10.7124 19.1725 10.5122L19.8974 9.9357C20.2922 9.62176 20.4896 9.46479 20.5886 9.26517C20.6722 9.09664 20.7065 8.90795 20.6877 8.72076C20.6654 8.49906 20.5361 8.28259 20.2773 7.84964L19.8179 7.08102C19.5728 6.67093 19.4503 6.46588 19.2755 6.34332C19.1277 6.23977 18.9548 6.1781 18.7748 6.16486C18.5619 6.14919 18.3373 6.23048 17.888 6.39307L16.8745 6.75989C16.5778 6.86725 16.4295 6.92093 16.2971 6.93195C16.167 6.94278 16.0884 6.93601 15.9621 6.90313C15.8335 6.86965 15.6604 6.77077 15.3142 6.57302C15.2877 6.55791 15.2611 6.54298 15.2345 6.52822C14.8725 6.3281 14.6915 6.22804 14.5936 6.13166C14.4976 6.03703 14.4502 5.97047 14.3923 5.84869C14.3334 5.72467 14.3055 5.56332 14.2499 5.24064L14.0938 4.3363Z" fill="#0F1729"></path></svg>`);
9981
10052
 
9982
- const $$css$j = {
10053
+ function IconGear($$anchor, $$props) {
10054
+ let rest = rest_props($$props, ['$$slots', '$$events', '$$legacy']);
10055
+ var svg = root$s();
10056
+
10057
+ attribute_effect(svg, () => ({
10058
+ class: 'cv-modal-icon-svg',
10059
+ fill: 'currentColor',
10060
+ viewBox: '0 0 24 24',
10061
+ xmlns: 'http://www.w3.org/2000/svg',
10062
+ ...rest
10063
+ }));
10064
+
10065
+ append($$anchor, svg);
10066
+ }
10067
+
10068
+ var root_1$c = from_html(`<button class="cv-dismiss-btn svelte-122ln5" aria-label="Dismiss settings icon">✕</button>`);
10069
+ var root$r = from_html(`<div role="none"><button class="cv-settings-main-btn svelte-122ln5"><span class="cv-gear svelte-122ln5"><!></span></button> <button class="cv-collapse-btn svelte-122ln5" aria-label="Collapse settings icon"> </button> <!></div>`);
10070
+
10071
+ const $$css$l = {
9983
10072
  hash: 'svelte-122ln5',
9984
- code: '.cv-settings-icon.svelte-122ln5 {position:fixed;background:var(--cv-icon-bg, rgba(255, 255, 255, 0.92));color:var(--cv-icon-color, rgba(0, 0, 0, 0.9));opacity:var(--cv-icon-opacity, 0.6);display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:bold;cursor:grab; /* Default cursor */box-shadow:0 4px 12px rgba(0, 0, 0, 0.15);border:2px solid rgba(0, 0, 0, 0.2);z-index:9998;transition:width 0.3s ease,\n background 0.3s ease,\n color 0.3s ease,\n opacity 0.3s ease,\n border-color 0.3s ease; /* Removed transform transition to allow smooth dragging */touch-action:none; /* Crucial for touch dragging */font-family:-apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif;box-sizing:border-box;user-select:none; /* Prevent text selection while dragging */}.cv-settings-icon.svelte-122ln5:active {cursor:grabbing;}.cv-settings-icon.svelte-122ln5:hover {background:var(--cv-icon-bg, rgba(255, 255, 255, 1));color:var(--cv-icon-color, rgba(0, 0, 0, 1));opacity:1;border-color:rgba(0, 0, 0, 0.3);}\n\n /* Top-right */.cv-settings-top-right.svelte-122ln5 {top:20px;right:0;border-radius:18px 0 0 18px;padding-left:6px;justify-content:flex-start;border-right:none;}\n\n /* Top-left */.cv-settings-top-left.svelte-122ln5 {top:20px;left:0;border-radius:0 18px 18px 0;padding-right:6px;justify-content:flex-end;border-left:none;}\n\n /* Bottom-right */.cv-settings-bottom-right.svelte-122ln5 {bottom:20px;right:0;border-radius:18px 0 0 18px;padding-left:6px;justify-content:flex-start;border-right:none;}\n\n /* Bottom-left */.cv-settings-bottom-left.svelte-122ln5 {bottom:20px;left:0;border-radius:0 18px 18px 0;padding-right:6px;justify-content:flex-end;border-left:none;}\n\n /* Middle-left */.cv-settings-middle-left.svelte-122ln5 {top:50%;left:0;\n /* transform handled by inline style now */border-radius:0 18px 18px 0;padding-right:6px;justify-content:flex-end;border-left:none;}\n\n /* Middle-right */.cv-settings-middle-right.svelte-122ln5 {top:50%;right:0;\n /* transform handled by inline style now */border-radius:18px 0 0 18px;padding-left:6px;justify-content:flex-start;border-right:none;}.cv-settings-top-right.svelte-122ln5,\n .cv-settings-middle-right.svelte-122ln5,\n .cv-settings-bottom-right.svelte-122ln5,\n .cv-settings-top-left.svelte-122ln5,\n .cv-settings-middle-left.svelte-122ln5,\n .cv-settings-bottom-left.svelte-122ln5 {height:36px;width:36px;}.cv-settings-middle-right.svelte-122ln5:hover,\n .cv-settings-top-right.svelte-122ln5:hover,\n .cv-settings-bottom-right.svelte-122ln5:hover,\n .cv-settings-top-left.svelte-122ln5:hover,\n .cv-settings-middle-left.svelte-122ln5:hover,\n .cv-settings-bottom-left.svelte-122ln5:hover {width:55px;}.cv-pulse {\n animation: svelte-122ln5-pulse 2s infinite;}\n\n @keyframes svelte-122ln5-pulse {\n 0% {\n box-shadow:\n 0 4px 12px rgba(0, 0, 0, 0.15),\n 0 0 0 0 rgba(62, 132, 244, 0.7);\n }\n 70% {\n box-shadow:\n 0 4px 12px rgba(0, 0, 0, 0.15),\n 0 0 0 10px rgba(62, 132, 244, 0);\n }\n 100% {\n box-shadow:\n 0 4px 12px rgba(0, 0, 0, 0.15),\n 0 0 0 0 rgba(62, 132, 244, 0);\n }\n }\n\n @media (max-width: 768px) {.cv-settings-top-right.svelte-122ln5,\n .cv-settings-top-left.svelte-122ln5 {top:10px;}.cv-settings-bottom-right.svelte-122ln5,\n .cv-settings-bottom-left.svelte-122ln5 {bottom:10px;}.cv-settings-top-right.svelte-122ln5,\n .cv-settings-bottom-right.svelte-122ln5,\n .cv-settings-middle-right.svelte-122ln5 {right:0;}.cv-settings-top-left.svelte-122ln5,\n .cv-settings-bottom-left.svelte-122ln5,\n .cv-settings-middle-left.svelte-122ln5 {left:0;}.cv-settings-icon.svelte-122ln5 {width:60px;height:32px;}.cv-settings-icon.svelte-122ln5:hover {width:75px;}\n }\n\n @media print {.cv-settings-icon.svelte-122ln5 {display:none !important;}\n }'
10073
+ code: '.cv-settings-main-btn.svelte-122ln5 {appearance:none;-webkit-appearance:none;background:transparent;border:none;padding:0;margin:0;flex:1;height:100%;width:100%;display:flex;align-items:inherit;justify-content:inherit;color:inherit;cursor:inherit;border-radius:inherit;}.cv-settings-main-btn.svelte-122ln5:focus-visible {outline:2px solid currentColor;outline-offset:-2px;}.cv-gear.svelte-122ln5 {display:flex;align-items:center;justify-content:center;width:18px;height:18px;}.cv-gear.svelte-122ln5 svg {width:18px;height:18px;}.cv-gear.svelte-122ln5 svg path {fill:currentColor;}.cv-settings-icon.svelte-122ln5 {position:fixed;background:var(--cv-icon-bg, rgba(255, 255, 255, 0.92));color:var(--cv-icon-color, rgba(0, 0, 0, 0.9));opacity:var(--cv-icon-opacity, 0.6);display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:bold;cursor:grab; /* Default cursor */box-shadow:0 4px 12px rgba(0, 0, 0, 0.15);border:2px solid rgba(0, 0, 0, 0.2);z-index:9998;transition:width 0.3s ease,\n background 0.3s ease,\n color 0.3s ease,\n opacity 0.3s ease,\n border-color 0.3s ease,\n transform 0.4s ease; /* transform transition drives the peek slide animation */touch-action:none; /* Crucial for touch dragging */font-family:-apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif;box-sizing:border-box;user-select:none; /* Prevent text selection while dragging */}.cv-settings-icon.svelte-122ln5:active {cursor:grabbing;}.cv-settings-icon.svelte-122ln5:hover {background:var(--cv-icon-bg, rgba(255, 255, 255, 1));color:var(--cv-icon-color, rgba(0, 0, 0, 1));opacity:1;border-color:rgba(0, 0, 0, 0.3);}\n\n /* Remove transform transition during drag so it tracks the pointer without lag */.cv-settings-icon.cv-is-dragging.svelte-122ln5 {transition:width 0.3s ease,\n background 0.3s ease,\n color 0.3s ease,\n opacity 0.3s ease,\n border-color 0.3s ease;}\n\n /* When collapsed, dim the strip */.cv-settings-icon.cv-is-collapsed.svelte-122ln5 {opacity:0.5;}.cv-settings-icon.cv-is-collapsed.svelte-122ln5:hover {opacity:0.85;}.cv-collapse-btn.svelte-122ln5 {position:absolute;top:0;bottom:0;width:16px;display:flex;align-items:center;justify-content:center;background:rgba(0, 0, 0, 0.12);border:none;padding:0;cursor:pointer;font-size:13px;line-height:1;color:inherit;opacity:0.5;transition:opacity 0.15s ease, background 0.15s ease;}.cv-collapse-btn[data-side=\'left\'].svelte-122ln5 {left:0; /* outer = screen-edge side for left icons */border-radius:0 6px 6px 0;}.cv-collapse-btn[data-side=\'right\'].svelte-122ln5 {right:0; /* outer = screen-edge side for right icons */border-radius:6px 0 0 6px;}.cv-collapse-btn.svelte-122ln5:hover {opacity:1;background:rgba(0, 0, 0, 0.22);}\n\n /* Hide collapse tab when already collapsed */.cv-settings-icon.cv-is-collapsed.svelte-122ln5 .cv-collapse-btn:where(.svelte-122ln5) {display:none;}.cv-dismiss-btn.svelte-122ln5 {position:absolute;bottom:calc(100% + 4px);width:16px;height:16px;display:flex;align-items:center;justify-content:center;background:rgba(0, 0, 0, 0.15);border:none;border-radius:50%;padding:0;cursor:pointer;font-size:9px;line-height:1;color:inherit;opacity:0.5;transition:opacity 0.15s ease, background 0.15s ease;}.cv-dismiss-btn[data-side=\'left\'].svelte-122ln5 {left:0;}.cv-dismiss-btn[data-side=\'right\'].svelte-122ln5 {right:0;}.cv-dismiss-btn.svelte-122ln5:hover {opacity:1;background:rgba(0, 0, 0, 0.25);}\n\n\n /* Top-right */.cv-settings-top-right.svelte-122ln5 {top:20px;right:0;border-radius:18px 0 0 18px;padding-left:6px;justify-content:flex-start;border-right:none;}\n\n /* Top-left */.cv-settings-top-left.svelte-122ln5 {top:20px;left:0;border-radius:0 18px 18px 0;padding-right:6px;justify-content:flex-end;border-left:none;}\n\n /* Bottom-right */.cv-settings-bottom-right.svelte-122ln5 {bottom:20px;right:0;border-radius:18px 0 0 18px;padding-left:6px;justify-content:flex-start;border-right:none;}\n\n /* Bottom-left */.cv-settings-bottom-left.svelte-122ln5 {bottom:20px;left:0;border-radius:0 18px 18px 0;padding-right:6px;justify-content:flex-end;border-left:none;}\n\n /* Middle-left */.cv-settings-middle-left.svelte-122ln5 {top:50%;left:0;\n /* transform handled by inline style now */border-radius:0 18px 18px 0;padding-right:6px;justify-content:flex-end;border-left:none;}\n\n /* Middle-right */.cv-settings-middle-right.svelte-122ln5 {top:50%;right:0;\n /* transform handled by inline style now */border-radius:18px 0 0 18px;padding-left:6px;justify-content:flex-start;border-right:none;}.cv-settings-top-right.svelte-122ln5,\n .cv-settings-middle-right.svelte-122ln5,\n .cv-settings-bottom-right.svelte-122ln5,\n .cv-settings-top-left.svelte-122ln5,\n .cv-settings-middle-left.svelte-122ln5,\n .cv-settings-bottom-left.svelte-122ln5 {height:36px;width:36px;}.cv-settings-middle-right.svelte-122ln5:hover,\n .cv-settings-top-right.svelte-122ln5:hover,\n .cv-settings-bottom-right.svelte-122ln5:hover,\n .cv-settings-top-left.svelte-122ln5:hover,\n .cv-settings-middle-left.svelte-122ln5:hover,\n .cv-settings-bottom-left.svelte-122ln5:hover {width:55px;}.cv-pulse {\n animation: svelte-122ln5-pulse 2s infinite;}\n\n @keyframes svelte-122ln5-pulse {\n 0% {\n box-shadow:\n 0 4px 12px rgba(0, 0, 0, 0.15),\n 0 0 0 0 rgba(62, 132, 244, 0.7);\n }\n 70% {\n box-shadow:\n 0 4px 12px rgba(0, 0, 0, 0.15),\n 0 0 0 10px rgba(62, 132, 244, 0);\n }\n 100% {\n box-shadow:\n 0 4px 12px rgba(0, 0, 0, 0.15),\n 0 0 0 0 rgba(62, 132, 244, 0);\n }\n }\n\n @media (max-width: 768px) {.cv-settings-top-right.svelte-122ln5,\n .cv-settings-top-left.svelte-122ln5 {top:10px;}.cv-settings-bottom-right.svelte-122ln5,\n .cv-settings-bottom-left.svelte-122ln5 {bottom:10px;}.cv-settings-top-right.svelte-122ln5,\n .cv-settings-bottom-right.svelte-122ln5,\n .cv-settings-middle-right.svelte-122ln5 {right:0;}.cv-settings-top-left.svelte-122ln5,\n .cv-settings-bottom-left.svelte-122ln5,\n .cv-settings-middle-left.svelte-122ln5 {left:0;}.cv-settings-icon.svelte-122ln5 {width:60px;height:32px;}.cv-settings-icon.svelte-122ln5:hover {width:75px;}\n }\n\n @media print {.cv-settings-icon.svelte-122ln5 {display:none !important;}\n }'
9985
10074
  };
9986
10075
 
9987
10076
  function SettingsIcon($$anchor, $$props) {
9988
10077
  push($$props, true);
9989
- append_styles$1($$anchor, $$css$j);
10078
+ append_styles$1($$anchor, $$css$l);
9990
10079
 
9991
10080
  /* eslint-disable @typescript-eslint/no-explicit-any */
10081
+ const iconSettingsStore = getContext(ICON_SETTINGS_CTX);
10082
+
9992
10083
  let position = prop($$props, 'position', 3, 'middle-left'),
9993
10084
  title = prop($$props, 'title', 3, 'Customize View'),
9994
10085
  pulse = prop($$props, 'pulse', 3, false),
@@ -9996,34 +10087,26 @@
9996
10087
  iconColor = prop($$props, 'iconColor', 3, undefined),
9997
10088
  backgroundColor = prop($$props, 'backgroundColor', 3, undefined),
9998
10089
  opacity = prop($$props, 'opacity', 3, undefined),
9999
- scale = prop($$props, 'scale', 3, undefined),
10000
- getIconPosition = prop($$props, 'getIconPosition', 3, undefined),
10001
- saveIconPosition = prop($$props, 'saveIconPosition', 3, undefined),
10002
- clearIconPosition = prop($$props, 'clearIconPosition', 3, undefined);
10090
+ scale = prop($$props, 'scale', 3, undefined);
10003
10091
 
10004
10092
  // Constants
10005
10093
  const VIEWPORT_MARGIN = 10;
10006
10094
 
10007
10095
  const DRAG_THRESHOLD = 5;
10096
+ const PEEK_WIDTH = 20;
10008
10097
  let isDragging = state(false);
10009
10098
  let dragStartY = 0;
10010
10099
  let dragStartOffset = 0;
10011
- let currentOffset = state(0);
10100
+ let dragOffset = state(0);
10101
+ const currentOffset = user_derived(() => get(isDragging) ? get(dragOffset) : iconSettingsStore.offset);
10012
10102
  let suppressClick = false;
10103
+ const isRight = user_derived(() => position()?.includes('right') ?? false);
10104
+ const isCollapsed = user_derived(() => iconSettingsStore.isCollapsed);
10013
10105
  let minOffset = -Infinity;
10014
10106
  let maxOffset = Infinity;
10015
10107
  let settingsIconElement;
10016
10108
 
10017
10109
  onMount(() => {
10018
- // Load persisted offset
10019
- if (getIconPosition()) {
10020
- const savedOffset = getIconPosition()();
10021
-
10022
- if (savedOffset !== null) {
10023
- set(currentOffset, savedOffset, true);
10024
- }
10025
- }
10026
-
10027
10110
  // Global event listeners to handle drag leaving the element
10028
10111
  window.addEventListener('mousemove', handleDragMove);
10029
10112
 
@@ -10032,8 +10115,8 @@
10032
10115
  window.addEventListener('touchend', endDrag);
10033
10116
  window.addEventListener('resize', constrainPositionToViewport);
10034
10117
 
10035
- // Initial check
10036
- constCheckTimer = setTimeout(constrainPositionToViewport, 0); // Ensure layout is ready
10118
+ // Initial check — defer to ensure layout is ready
10119
+ constCheckTimer = setTimeout(constrainPositionToViewport, 0);
10037
10120
 
10038
10121
  return () => {
10039
10122
  window.removeEventListener('mousemove', handleDragMove);
@@ -10047,10 +10130,9 @@
10047
10130
 
10048
10131
  let constCheckTimer;
10049
10132
 
10050
- function resetPosition() {
10051
- set(currentOffset, 0);
10052
- dragStartOffset = 0;
10053
- clearIconPosition()?.();
10133
+ function handleCollapse(e) {
10134
+ e.stopPropagation(); // don't bubble to the icon's onclick
10135
+ iconSettingsStore.setCollapsed(true);
10054
10136
  }
10055
10137
 
10056
10138
  function constrainPositionToViewport() {
@@ -10065,17 +10147,27 @@
10065
10147
  const min = VIEWPORT_MARGIN - zeroTop;
10066
10148
  const max = window.innerHeight - VIEWPORT_MARGIN - zeroTop - elementHeight;
10067
10149
 
10150
+ // Refresh boundaries during drag so handleDragMove respects the new window size
10151
+ if (get(isDragging)) {
10152
+ minOffset = min;
10153
+ maxOffset = max;
10154
+ }
10155
+
10068
10156
  // Clamp
10069
10157
  const clamped = Math.max(min, Math.min(max, get(currentOffset)));
10070
10158
 
10071
10159
  if (clamped !== get(currentOffset)) {
10072
- set(currentOffset, clamped, true);
10073
- savePosition();
10160
+ if (get(isDragging)) {
10161
+ set(dragOffset, clamped, true);
10162
+ } else {
10163
+ iconSettingsStore.setOffset(clamped);
10164
+ }
10074
10165
  }
10075
10166
  }
10076
10167
 
10077
10168
  function onMouseDown(e) {
10078
10169
  if (e.button !== 0) return;
10170
+ if (get(isCollapsed)) return; // strip click expands via onClick; don't start drag
10079
10171
 
10080
10172
  startDrag(e.clientY);
10081
10173
  }
@@ -10083,13 +10175,23 @@
10083
10175
  function onTouchStart(e) {
10084
10176
  if (e.touches.length !== 1) return;
10085
10177
 
10178
+ // First tap on a peeking icon just reveals it without starting a drag
10179
+ if (get(isCollapsed)) {
10180
+ iconSettingsStore.setCollapsed(false);
10181
+ e.preventDefault();
10182
+
10183
+ return;
10184
+ }
10185
+
10086
10186
  startDrag(e.touches[0].clientY);
10087
10187
  }
10088
10188
 
10089
10189
  function startDrag(clientY) {
10190
+ iconSettingsStore.setCollapsed(false);
10090
10191
  set(isDragging, true);
10091
10192
  dragStartY = clientY;
10092
- dragStartOffset = get(currentOffset);
10193
+ dragStartOffset = iconSettingsStore.offset;
10194
+ set(dragOffset, dragStartOffset, true);
10093
10195
  suppressClick = false;
10094
10196
  calculateDragConstraints();
10095
10197
  }
@@ -10118,7 +10220,7 @@
10118
10220
  const rawOffset = dragStartOffset + deltaY;
10119
10221
 
10120
10222
  // Clamp the offset to keep element on screen
10121
- set(currentOffset, Math.max(minOffset, Math.min(maxOffset, rawOffset)), true);
10223
+ set(dragOffset, Math.max(minOffset, Math.min(maxOffset, rawOffset)), true);
10122
10224
 
10123
10225
  if (Math.abs(deltaY) > DRAG_THRESHOLD) {
10124
10226
  suppressClick = true;
@@ -10139,14 +10241,16 @@
10139
10241
  if (!get(isDragging)) return;
10140
10242
 
10141
10243
  set(isDragging, false);
10142
- savePosition();
10244
+ iconSettingsStore.setOffset(get(dragOffset));
10143
10245
  }
10144
10246
 
10145
- function savePosition() {
10146
- saveIconPosition()?.(get(currentOffset));
10147
- }
10247
+ function handleMainClick(e) {
10248
+ if (get(isCollapsed)) {
10249
+ iconSettingsStore.setCollapsed(false);
10250
+
10251
+ return;
10252
+ }
10148
10253
 
10149
- function onClick(e) {
10150
10254
  if (suppressClick) {
10151
10255
  e.stopImmediatePropagation();
10152
10256
  e.preventDefault();
@@ -10158,24 +10262,22 @@
10158
10262
  if (onclick()) onclick()();
10159
10263
  }
10160
10264
 
10161
- // Key handler for accessibility
10162
- function onKeyDown(e) {
10163
- if (e.key === 'Enter' || e.key === ' ') {
10164
- e.preventDefault();
10165
-
10166
- if (onclick()) onclick()();
10167
- }
10168
- }
10169
-
10170
10265
  // Helper for transforms
10171
- function getTransform(pos, offset, s) {
10266
+ function getTransform(pos, offset, s, collapsed) {
10172
10267
  const isMiddle = pos && pos.includes('middle');
10268
+ const isRight = pos && pos.includes('right');
10173
10269
  let t = '';
10174
10270
 
10271
+ if (collapsed) {
10272
+ t = isRight
10273
+ ? `translateX(calc(100% - ${PEEK_WIDTH}px)) `
10274
+ : `translateX(calc(-100% + ${PEEK_WIDTH}px)) `;
10275
+ }
10276
+
10175
10277
  if (isMiddle) {
10176
- t = `translateY(calc(-50% + ${offset}px))`;
10278
+ t += `translateY(calc(-50% + ${offset}px))`;
10177
10279
  } else {
10178
- t = `translateY(${offset}px)`;
10280
+ t += `translateY(${offset}px)`;
10179
10281
  }
10180
10282
 
10181
10283
  if (s && s !== 1) {
@@ -10185,41 +10287,90 @@
10185
10287
  return t;
10186
10288
  }
10187
10289
 
10188
- var $$exports = { resetPosition };
10189
- var div = root$s();
10290
+ var div = root$r();
10291
+ let classes;
10190
10292
 
10191
10293
  div.__mousedown = onMouseDown;
10192
10294
  div.__touchstart = onTouchStart;
10193
- div.__click = onClick;
10194
- div.__keydown = onKeyDown;
10195
10295
 
10196
10296
  let styles;
10297
+ var button = child(div);
10298
+
10299
+ button.__click = handleMainClick;
10300
+
10301
+ var span = child(button);
10302
+ var node = child(span);
10303
+
10304
+ IconGear(node, {});
10305
+ reset(span);
10306
+ reset(button);
10307
+
10308
+ var button_1 = sibling(button, 2);
10309
+
10310
+ button_1.__click = handleCollapse;
10311
+ button_1.__mousedown = (e) => e.stopPropagation();
10312
+ button_1.__touchstart = (e) => e.stopPropagation();
10313
+ button_1.__pointerdown = (e) => e.stopPropagation();
10314
+
10315
+ var text = child(button_1, true);
10197
10316
 
10317
+ reset(button_1);
10318
+
10319
+ var node_1 = sibling(button_1, 2);
10320
+
10321
+ {
10322
+ var consequent = ($$anchor) => {
10323
+ var button_2 = root_1$c();
10324
+
10325
+ button_2.__click = (e) => {
10326
+ e.stopPropagation();
10327
+ iconSettingsStore.dismiss();
10328
+ };
10329
+
10330
+ button_2.__mousedown = (e) => e.stopPropagation();
10331
+ button_2.__touchstart = (e) => e.stopPropagation();
10332
+ button_2.__pointerdown = (e) => e.stopPropagation();
10333
+ template_effect(() => set_attribute(button_2, 'data-side', get(isRight) ? 'left' : 'right'));
10334
+ append($$anchor, button_2);
10335
+ };
10336
+
10337
+ if_block(node_1, ($$render) => {
10338
+ if (get(isCollapsed)) $$render(consequent);
10339
+ });
10340
+ }
10341
+
10342
+ reset(div);
10198
10343
  bind_this(div, ($$value) => settingsIconElement = $$value, () => settingsIconElement);
10199
10344
 
10200
10345
  template_effect(
10201
10346
  ($0) => {
10202
- set_class(div, 1, `cv-settings-icon cv-settings-${position() ?? ''} ${pulse() ? 'cv-pulse' : ''}`, 'svelte-122ln5');
10203
- set_attribute(div, 'title', title());
10347
+ classes = set_class(div, 1, `cv-settings-icon cv-settings-${position() ?? ''} ${pulse() ? 'cv-pulse' : ''}`, 'svelte-122ln5', classes, {
10348
+ 'cv-is-dragging': get(isDragging),
10349
+ 'cv-is-collapsed': get(isCollapsed)
10350
+ });
10351
+
10204
10352
  styles = set_style(div, '', styles, $0);
10353
+ set_attribute(button, 'title', title());
10354
+ set_attribute(button, 'aria-label', get(isCollapsed) ? 'Expand settings' : 'Open Custom Views Settings');
10355
+ set_attribute(button_1, 'data-side', get(isRight) ? 'right' : 'left');
10356
+ set_text(text, get(isRight) ? '›' : '‹');
10205
10357
  },
10206
10358
  [
10207
10359
  () => ({
10208
10360
  '--cv-icon-color': iconColor(),
10209
10361
  '--cv-icon-bg': backgroundColor(),
10210
10362
  '--cv-icon-opacity': opacity(),
10211
- transform: getTransform(position(), get(currentOffset), scale()),
10212
- cursor: get(isDragging) ? 'grabbing' : 'grab'
10363
+ transform: getTransform(position(), get(currentOffset), scale(), get(isCollapsed)),
10364
+ cursor: get(isDragging) ? 'grabbing' : get(isCollapsed) ? 'pointer' : 'grab'
10213
10365
  })
10214
10366
  ]
10215
10367
  );
10216
10368
 
10217
10369
  append($$anchor, div);
10218
-
10219
- return pop($$exports);
10370
+ pop();
10220
10371
  }
10221
10372
 
10222
- delegate(['mousedown', 'touchstart', 'click', 'keydown']);
10373
+ delegate(['mousedown', 'touchstart', 'click', 'pointerdown']);
10223
10374
 
10224
10375
  /** @import { BlurParams, CrossfadeParams, DrawParams, FadeParams, FlyParams, ScaleParams, SlideParams, TransitionConfig } from './public' */
10225
10376
 
@@ -10357,23 +10508,6 @@
10357
10508
  };
10358
10509
  }
10359
10510
 
10360
- var root$r = from_svg(`<svg><path xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" d="M12 8.00002C9.79085 8.00002 7.99999 9.79088 7.99999 12C7.99999 14.2092 9.79085 16 12 16C14.2091 16 16 14.2092 16 12C16 9.79088 14.2091 8.00002 12 8.00002ZM9.99999 12C9.99999 10.8955 10.8954 10 12 10C13.1046 10 14 10.8955 14 12C14 13.1046 13.1046 14 12 14C10.8954 14 9.99999 13.1046 9.99999 12Z" fill="#0F1729"></path><path xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" d="M10.7673 1.01709C10.9925 0.999829 11.2454 0.99993 11.4516 1.00001L12.5484 1.00001C12.7546 0.99993 13.0075 0.999829 13.2327 1.01709C13.4989 1.03749 13.8678 1.08936 14.2634 1.26937C14.7635 1.49689 15.1915 1.85736 15.5007 2.31147C15.7454 2.67075 15.8592 3.0255 15.9246 3.2843C15.9799 3.50334 16.0228 3.75249 16.0577 3.9557L16.1993 4.77635L16.2021 4.77788C16.2369 4.79712 16.2715 4.81659 16.306 4.8363L16.3086 4.83774L17.2455 4.49865C17.4356 4.42978 17.6693 4.34509 17.8835 4.28543C18.1371 4.2148 18.4954 4.13889 18.9216 4.17026C19.4614 4.20998 19.9803 4.39497 20.4235 4.70563C20.7734 4.95095 21.0029 5.23636 21.1546 5.4515C21.2829 5.63326 21.4103 5.84671 21.514 6.02029L22.0158 6.86003C22.1256 7.04345 22.2594 7.26713 22.3627 7.47527C22.4843 7.7203 22.6328 8.07474 22.6777 8.52067C22.7341 9.08222 22.6311 9.64831 22.3803 10.1539C22.1811 10.5554 21.9171 10.8347 21.7169 11.0212C21.5469 11.1795 21.3428 11.3417 21.1755 11.4746L20.5 12L21.1755 12.5254C21.3428 12.6584 21.5469 12.8205 21.7169 12.9789C21.9171 13.1653 22.1811 13.4446 22.3802 13.8461C22.631 14.3517 22.7341 14.9178 22.6776 15.4794C22.6328 15.9253 22.4842 16.2797 22.3626 16.5248C22.2593 16.7329 22.1255 16.9566 22.0158 17.14L21.5138 17.9799C21.4102 18.1535 21.2828 18.3668 21.1546 18.5485C21.0028 18.7637 20.7734 19.0491 20.4234 19.2944C19.9803 19.6051 19.4613 19.7901 18.9216 19.8298C18.4954 19.8612 18.1371 19.7852 17.8835 19.7146C17.6692 19.6549 17.4355 19.5703 17.2454 19.5014L16.3085 19.1623L16.306 19.1638C16.2715 19.1835 16.2369 19.2029 16.2021 19.2222L16.1993 19.2237L16.0577 20.0443C16.0228 20.2475 15.9799 20.4967 15.9246 20.7157C15.8592 20.9745 15.7454 21.3293 15.5007 21.6886C15.1915 22.1427 14.7635 22.5032 14.2634 22.7307C13.8678 22.9107 13.4989 22.9626 13.2327 22.983C13.0074 23.0002 12.7546 23.0001 12.5484 23H11.4516C11.2454 23.0001 10.9925 23.0002 10.7673 22.983C10.5011 22.9626 10.1322 22.9107 9.73655 22.7307C9.23648 22.5032 8.80849 22.1427 8.49926 21.6886C8.25461 21.3293 8.14077 20.9745 8.07542 20.7157C8.02011 20.4967 7.97723 20.2475 7.94225 20.0443L7.80068 19.2237L7.79791 19.2222C7.7631 19.2029 7.72845 19.1835 7.69396 19.1637L7.69142 19.1623L6.75458 19.5014C6.5645 19.5702 6.33078 19.6549 6.11651 19.7146C5.86288 19.7852 5.50463 19.8611 5.07841 19.8298C4.53866 19.7901 4.01971 19.6051 3.57654 19.2944C3.2266 19.0491 2.99714 18.7637 2.84539 18.5485C2.71718 18.3668 2.58974 18.1534 2.4861 17.9798L1.98418 17.14C1.87447 16.9566 1.74067 16.7329 1.63737 16.5248C1.51575 16.2797 1.36719 15.9253 1.32235 15.4794C1.26588 14.9178 1.36897 14.3517 1.61976 13.8461C1.81892 13.4446 2.08289 13.1653 2.28308 12.9789C2.45312 12.8205 2.65717 12.6584 2.82449 12.5254L3.47844 12.0054V11.9947L2.82445 11.4746C2.65712 11.3417 2.45308 11.1795 2.28304 11.0212C2.08285 10.8347 1.81888 10.5554 1.61972 10.1539C1.36893 9.64832 1.26584 9.08224 1.3223 8.52069C1.36714 8.07476 1.51571 7.72032 1.63732 7.47528C1.74062 7.26715 1.87443 7.04347 1.98414 6.86005L2.48605 6.02026C2.58969 5.84669 2.71714 5.63326 2.84534 5.45151C2.9971 5.23637 3.22655 4.95096 3.5765 4.70565C4.01966 4.39498 4.53862 4.20999 5.07837 4.17027C5.50458 4.1389 5.86284 4.21481 6.11646 4.28544C6.33072 4.34511 6.56444 4.4298 6.75451 4.49867L7.69141 4.83775L7.69394 4.8363C7.72844 4.8166 7.7631 4.79712 7.79791 4.77788L7.80068 4.77635L7.94225 3.95571C7.97723 3.7525 8.02011 3.50334 8.07542 3.2843C8.14077 3.0255 8.25461 2.67075 8.49926 2.31147C8.80849 1.85736 9.23648 1.49689 9.73655 1.26937C10.1322 1.08936 10.5011 1.03749 10.7673 1.01709ZM14.0938 4.3363C14.011 3.85634 13.9696 3.61637 13.8476 3.43717C13.7445 3.2858 13.6019 3.16564 13.4352 3.0898C13.2378 3.00002 12.9943 3.00002 12.5073 3.00002H11.4927C11.0057 3.00002 10.7621 3.00002 10.5648 3.0898C10.3981 3.16564 10.2555 3.2858 10.1524 3.43717C10.0304 3.61637 9.98895 3.85634 9.90615 4.3363L9.75012 5.24064C9.69445 5.56333 9.66662 5.72467 9.60765 5.84869C9.54975 5.97047 9.50241 6.03703 9.40636 6.13166C9.30853 6.22804 9.12753 6.3281 8.76554 6.52822C8.73884 6.54298 8.71227 6.55791 8.68582 6.57302C8.33956 6.77078 8.16643 6.86966 8.03785 6.90314C7.91158 6.93602 7.83293 6.94279 7.70289 6.93196C7.57049 6.92094 7.42216 6.86726 7.12551 6.7599L6.11194 6.39308C5.66271 6.2305 5.43809 6.14921 5.22515 6.16488C5.04524 6.17811 4.87225 6.23978 4.72453 6.34333C4.5497 6.46589 4.42715 6.67094 4.18206 7.08103L3.72269 7.84965C3.46394 8.2826 3.33456 8.49907 3.31227 8.72078C3.29345 8.90796 3.32781 9.09665 3.41141 9.26519C3.51042 9.4648 3.7078 9.62177 4.10256 9.9357L4.82745 10.5122C5.07927 10.7124 5.20518 10.8126 5.28411 10.9199C5.36944 11.036 5.40583 11.1114 5.44354 11.2504C5.47844 11.379 5.47844 11.586 5.47844 12C5.47844 12.414 5.47844 12.621 5.44354 12.7497C5.40582 12.8887 5.36944 12.9641 5.28413 13.0801C5.20518 13.1875 5.07927 13.2876 4.82743 13.4879L4.10261 14.0643C3.70785 14.3783 3.51047 14.5352 3.41145 14.7349C3.32785 14.9034 3.29349 15.0921 3.31231 15.2793C3.33461 15.501 3.46398 15.7174 3.72273 16.1504L4.1821 16.919C4.4272 17.3291 4.54974 17.5342 4.72457 17.6567C4.8723 17.7603 5.04528 17.8219 5.2252 17.8352C5.43813 17.8508 5.66275 17.7695 6.11199 17.607L7.12553 17.2402C7.42216 17.1328 7.5705 17.0791 7.7029 17.0681C7.83294 17.0573 7.91159 17.064 8.03786 17.0969C8.16644 17.1304 8.33956 17.2293 8.68582 17.427C8.71228 17.4421 8.73885 17.4571 8.76554 17.4718C9.12753 17.6719 9.30853 17.772 9.40635 17.8684C9.50241 17.963 9.54975 18.0296 9.60765 18.1514C9.66662 18.2754 9.69445 18.4367 9.75012 18.7594L9.90615 19.6637C9.98895 20.1437 10.0304 20.3837 10.1524 20.5629C10.2555 20.7142 10.3981 20.8344 10.5648 20.9102C10.7621 21 11.0057 21 11.4927 21H12.5073C12.9943 21 13.2378 21 13.4352 20.9102C13.6019 20.8344 13.7445 20.7142 13.8476 20.5629C13.9696 20.3837 14.011 20.1437 14.0938 19.6637L14.2499 18.7594C14.3055 18.4367 14.3334 18.2754 14.3923 18.1514C14.4502 18.0296 14.4976 17.963 14.5936 17.8684C14.6915 17.772 14.8725 17.6719 15.2344 17.4718C15.2611 17.4571 15.2877 17.4421 15.3141 17.427C15.6604 17.2293 15.8335 17.1304 15.9621 17.0969C16.0884 17.064 16.167 17.0573 16.2971 17.0681C16.4295 17.0791 16.5778 17.1328 16.8744 17.2402L17.888 17.607C18.3372 17.7696 18.5619 17.8509 18.7748 17.8352C18.9547 17.8219 19.1277 17.7603 19.2754 17.6567C19.4502 17.5342 19.5728 17.3291 19.8179 16.919L20.2773 16.1504C20.536 15.7175 20.6654 15.501 20.6877 15.2793C20.7065 15.0921 20.6721 14.9034 20.5885 14.7349C20.4895 14.5353 20.2921 14.3783 19.8974 14.0643L19.1726 13.4879C18.9207 13.2876 18.7948 13.1875 18.7159 13.0801C18.6306 12.9641 18.5942 12.8887 18.5564 12.7497C18.5215 12.6211 18.5215 12.414 18.5215 12C18.5215 11.586 18.5215 11.379 18.5564 11.2504C18.5942 11.1114 18.6306 11.036 18.7159 10.9199C18.7948 10.8126 18.9207 10.7124 19.1725 10.5122L19.8974 9.9357C20.2922 9.62176 20.4896 9.46479 20.5886 9.26517C20.6722 9.09664 20.7065 8.90795 20.6877 8.72076C20.6654 8.49906 20.5361 8.28259 20.2773 7.84964L19.8179 7.08102C19.5728 6.67093 19.4503 6.46588 19.2755 6.34332C19.1277 6.23977 18.9548 6.1781 18.7748 6.16486C18.5619 6.14919 18.3373 6.23048 17.888 6.39307L16.8745 6.75989C16.5778 6.86725 16.4295 6.92093 16.2971 6.93195C16.167 6.94278 16.0884 6.93601 15.9621 6.90313C15.8335 6.86965 15.6604 6.77077 15.3142 6.57302C15.2877 6.55791 15.2611 6.54298 15.2345 6.52822C14.8725 6.3281 14.6915 6.22804 14.5936 6.13166C14.4976 6.03703 14.4502 5.97047 14.3923 5.84869C14.3334 5.72467 14.3055 5.56332 14.2499 5.24064L14.0938 4.3363Z" fill="#0F1729"></path></svg>`);
10361
-
10362
- function IconGear($$anchor, $$props) {
10363
- let rest = rest_props($$props, ['$$slots', '$$events', '$$legacy']);
10364
- var svg = root$r();
10365
-
10366
- attribute_effect(svg, () => ({
10367
- class: 'cv-modal-icon-svg',
10368
- fill: 'currentColor',
10369
- viewBox: '0 0 24 24',
10370
- xmlns: 'http://www.w3.org/2000/svg',
10371
- ...rest
10372
- }));
10373
-
10374
- append($$anchor, svg);
10375
- }
10376
-
10377
10511
  var root$q = from_svg(`<svg><path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"></path></svg>`);
10378
10512
 
10379
10513
  function IconClose($$anchor, $$props) {
@@ -10500,41 +10634,6 @@
10500
10634
  append($$anchor, svg);
10501
10635
  }
10502
10636
 
10503
- var root$j = from_svg(`<svg><path d="M22.719 12A10.719 10.719 0 0 1 1.28 12h.838a9.916 9.916 0 1 0 1.373-5H8v1H2V2h1v4.2A10.71 10.71 0 0 1 22.719 12z"></path><path fill="none" d="M0 0h24v24H0z"></path></svg>`);
10504
-
10505
- function IconReset($$anchor, $$props) {
10506
- let rest = rest_props($$props, ['$$slots', '$$events', '$$legacy']);
10507
- var svg = root$j();
10508
-
10509
- attribute_effect(svg, () => ({
10510
- class: 'cv-btn-icon',
10511
- fill: 'currentColor',
10512
- viewBox: '0 0 24 24',
10513
- xmlns: 'http://www.w3.org/2000/svg',
10514
- ...rest
10515
- }));
10516
-
10517
- append($$anchor, svg);
10518
- }
10519
-
10520
- var root$i = from_svg(`<svg><path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"></path></svg>`);
10521
-
10522
- function IconGitHub($$anchor, $$props) {
10523
- let rest = rest_props($$props, ['$$slots', '$$events', '$$legacy']);
10524
- var svg = root$i();
10525
-
10526
- attribute_effect(svg, () => ({
10527
- viewBox: '0 0 98 96',
10528
- width: '16',
10529
- height: '16',
10530
- xmlns: 'http://www.w3.org/2000/svg',
10531
- fill: 'currentColor',
10532
- ...rest
10533
- }));
10534
-
10535
- append($$anchor, svg);
10536
- }
10537
-
10538
10637
  const PARAM_SHOW_TOGGLE = 't-show';
10539
10638
  const PARAM_PEEK_TOGGLE = 't-peek';
10540
10639
  const PARAM_HIDE_TOGGLE = 't-hide';
@@ -10688,20 +10787,18 @@
10688
10787
  return url.origin + url.pathname + (search ? `?${search}` : '') + (url.hash || '');
10689
10788
  }
10690
10789
  /**
10691
- * Strips placeholder entries whose value is derived from a tab group (source: 'tabgroup').
10692
- *
10693
- * These placeholders should NOT be encoded in the shared URL because their value
10694
- * is already implied by the `?tabs=` param. Encoding both would create two sources
10695
- * of truth and risk drift when decoded on the recipient's side.
10790
+ * Strips placeholder entries that should not appear in shareable URLs:
10791
+ * - Tab-group-derived placeholders (source: 'tabgroup') — implied by ?tabs=
10792
+ * - Adaptation-only placeholders (adaptationPlaceholder: true) author-controlled, not shareable
10696
10793
  */
10697
- function stripTabDerivedPlaceholders(placeholders) {
10794
+ function stripNonShareablePlaceholders(placeholders) {
10698
10795
  const shareable = {};
10699
10796
  for (const [key, value] of Object.entries(placeholders)) {
10700
10797
  const definition = placeholderRegistryStore.get(key);
10701
- if (definition?.source === 'tabgroup') {
10702
- // Omit the tab selection in ?tabs= is the source of truth for this placeholder.
10703
- continue;
10704
- }
10798
+ if (definition?.source === 'tabgroup')
10799
+ continue; // implied by ?tabs=
10800
+ if (definition?.adaptationPlaceholder)
10801
+ continue; // author-only, not shareable
10705
10802
  shareable[key] = value;
10706
10803
  }
10707
10804
  return shareable;
@@ -10759,7 +10856,7 @@
10759
10856
  }
10760
10857
  // 3. Filter placeholders to only those present on the page
10761
10858
  if (currentState.placeholders) {
10762
- const shareablePlaceholders = stripTabDerivedPlaceholders(currentState.placeholders);
10859
+ const shareablePlaceholders = stripNonShareablePlaceholders(currentState.placeholders);
10763
10860
  const pagePlaceholders = {};
10764
10861
  for (const [key, value] of Object.entries(shareablePlaceholders)) {
10765
10862
  if (pagePlaceholdersSet.has(key)) {
@@ -10972,40 +11069,44 @@
10972
11069
  return highestVisibleEl;
10973
11070
  }
10974
11071
  /**
10975
- * Scrolls the page to align the element to the top of the viewport,
10976
- * accounting for fixed/sticky headers and adding some padding.
10977
- * @param element The element to scroll to.
11072
+ * Adjusts the scroll position to keep a specific element in the same visual location.
11073
+ * Useful when content additions/removals above might cause jumps.
10978
11074
  */
10979
- function scrollToElement(element) {
10980
- const headerOffset = getScrollTopOffset();
10981
- const PADDING_BELOW_HEADER = 20;
10982
- const targetElementRect = element.getBoundingClientRect();
10983
- const scrollTargetY = targetElementRect.top + window.scrollY;
10984
- const finalScrollY = scrollTargetY - headerOffset - PADDING_BELOW_HEADER;
10985
- window.scrollTo({
10986
- top: finalScrollY,
10987
- behavior: 'smooth',
11075
+ function handleScrollAnchor(scrollAnchor) {
11076
+ requestAnimationFrame(() => {
11077
+ const { element, top: initialTop } = scrollAnchor;
11078
+ // Check if element is still in document
11079
+ if (!element || !document.contains(element))
11080
+ return;
11081
+ const newTop = element.getBoundingClientRect().top;
11082
+ const scrollDelta = newTop - initialTop;
11083
+ // Only scroll if there's a noticeable change
11084
+ if (Math.abs(scrollDelta) > 1) {
11085
+ window.scrollBy({
11086
+ top: scrollDelta,
11087
+ behavior: 'instant',
11088
+ });
11089
+ }
10988
11090
  });
10989
11091
  }
10990
11092
 
10991
- var root_1$a = from_html(`<p class="description svelte-gwkhja"> </p>`);
10992
- var root$h = from_html(`<div class="card svelte-gwkhja"><div class="content svelte-gwkhja"><div><p class="title svelte-gwkhja"> </p> <!></div> <div class="radios svelte-gwkhja"><label class="radio-label svelte-gwkhja" title="Hide"><input class="toggle-input svelte-gwkhja" type="radio"/> <span>Hide</span></label> <label class="radio-label svelte-gwkhja" title="Peek"><input class="toggle-input svelte-gwkhja" type="radio"/> <span>Peek</span></label> <label class="radio-label svelte-gwkhja" title="Show"><input class="toggle-input svelte-gwkhja" type="radio"/> <span>Show</span></label></div></div></div>`);
11093
+ var root_1$b = from_html(`<p class="description svelte-gwkhja"> </p>`);
11094
+ var root_2$9 = from_html(`<button> </button>`);
11095
+ var root$j = from_html(`<div class="card svelte-gwkhja"><div class="content svelte-gwkhja"><div><p class="title svelte-gwkhja"> </p> <!></div> <div class="segmented svelte-gwkhja" role="group" aria-label="Visibility"></div></div></div>`);
10993
11096
 
10994
- const $$css$i = {
11097
+ const $$css$k = {
10995
11098
  hash: 'svelte-gwkhja',
10996
- code: '.card.svelte-gwkhja {background:var(--cv-bg);border:1px solid var(--cv-border);border-radius:0.5rem;}.content.svelte-gwkhja {display:flex;align-items:center;justify-content:space-between;padding:0.75rem;}.title.svelte-gwkhja {font-weight:500;font-size:0.875rem;color:var(--cv-text);margin:0;}.description.svelte-gwkhja {font-size:0.75rem;color:var(--cv-text-secondary);margin:0.125rem 0 0 0;}.radios.svelte-gwkhja {display:flex;gap:8px;}.radio-label.svelte-gwkhja {display:flex;align-items:center;gap:4px;font-size:0.85rem;cursor:pointer;color:var(--cv-text);}.toggle-input.svelte-gwkhja {margin:0;opacity:1;width:auto;height:auto;}'
11099
+ code: '.card.svelte-gwkhja {background:var(--cv-bg);border:1px solid var(--cv-border);border-radius:var(--cv-card-radius, 0.5rem);transition:background 0.15s ease;}.card.svelte-gwkhja:hover {background:var(--cv-bg-hover);}.content.svelte-gwkhja {display:flex;align-items:center;justify-content:space-between;padding:0.75rem;}.title.svelte-gwkhja {font-weight:500;font-size:0.875rem;color:var(--cv-text);margin:0;}.description.svelte-gwkhja {font-size:0.75rem;color:var(--cv-text-secondary);margin:0.125rem 0 0 0;}.segmented.svelte-gwkhja {display:flex;border:1px solid var(--cv-border);border-radius:0.375rem;overflow:hidden;flex-shrink:0;}.segment-btn.svelte-gwkhja {background:transparent;border:none;border-left:1px solid var(--cv-border);padding:0.3rem 0.6rem;font-size:0.8rem;font-weight:500;color:var(--cv-text-secondary);cursor:pointer;transition:background 0.15s ease, color 0.15s ease;font-family:inherit;line-height:1;}.segment-btn.svelte-gwkhja:first-child {border-left:none;}.segment-btn.svelte-gwkhja:hover:not(.active) {background:var(--cv-bg-hover);color:var(--cv-text);}.segment-btn.active.svelte-gwkhja {background:var(--cv-primary);color:white;box-shadow:0 1px 3px rgba(0, 0, 0, 0.15);}'
10997
11100
  };
10998
11101
 
10999
11102
  function ToggleItem($$anchor, $$props) {
11000
11103
  push($$props, true);
11001
- append_styles$1($$anchor, $$css$i);
11002
-
11003
- const binding_group = [];
11104
+ append_styles$1($$anchor, $$css$k);
11004
11105
 
11005
11106
  let value = prop($$props, 'value', 15, 'show'),
11006
11107
  onchange = prop($$props, 'onchange', 3, () => {});
11007
11108
 
11008
- var div = root$h();
11109
+ var div = root$j();
11009
11110
  var div_1 = child(div);
11010
11111
  var div_2 = child(div_1);
11011
11112
  var p = child(div_2);
@@ -11017,7 +11118,7 @@
11017
11118
 
11018
11119
  {
11019
11120
  var consequent = ($$anchor) => {
11020
- var p_1 = root_1$a();
11121
+ var p_1 = root_1$b();
11021
11122
  var text_1 = child(p_1, true);
11022
11123
 
11023
11124
  reset(p_1);
@@ -11033,64 +11134,53 @@
11033
11134
  reset(div_2);
11034
11135
 
11035
11136
  var div_3 = sibling(div_2, 2);
11036
- var label = child(div_3);
11037
- var input = child(label);
11038
11137
 
11039
- remove_input_defaults(input);
11040
- input.__change = () => onchange()({ toggleId: $$props.toggle.toggleId, value: 'hide' });
11041
- input.value = input.__value = 'hide';
11042
- next(2);
11043
- reset(label);
11138
+ each(div_3, 20, () => ['hide', 'peek', 'show'], (option) => option, ($$anchor, option) => {
11139
+ var button = root_2$9();
11044
11140
 
11045
- var label_1 = sibling(label, 2);
11046
- var input_1 = child(label_1);
11141
+ button.__click = () => {
11142
+ value(option);
11143
+ onchange()({ toggleId: $$props.toggle.toggleId, value: option });
11144
+ };
11047
11145
 
11048
- remove_input_defaults(input_1);
11049
- input_1.__change = () => onchange()({ toggleId: $$props.toggle.toggleId, value: 'peek' });
11050
- input_1.value = input_1.__value = 'peek';
11051
- next(2);
11052
- reset(label_1);
11146
+ var text_2 = child(button, true);
11053
11147
 
11054
- var label_2 = sibling(label_1, 2);
11055
- var input_2 = child(label_2);
11148
+ reset(button);
11056
11149
 
11057
- remove_input_defaults(input_2);
11058
- input_2.__change = () => onchange()({ toggleId: $$props.toggle.toggleId, value: 'show' });
11059
- input_2.value = input_2.__value = 'show';
11060
- next(2);
11061
- reset(label_2);
11062
- reset(div_3);
11063
- reset(div_1);
11064
- reset(div);
11150
+ template_effect(
11151
+ ($0) => {
11152
+ set_class(button, 1, `segment-btn ${value() === option ? 'active' : ''}`, 'svelte-gwkhja');
11153
+ set_attribute(button, 'aria-pressed', value() === option);
11154
+ set_text(text_2, $0);
11155
+ },
11156
+ [() => option.charAt(0).toUpperCase() + option.slice(1)]
11157
+ );
11065
11158
 
11066
- template_effect(() => {
11067
- set_text(text, $$props.toggle.label || $$props.toggle.toggleId);
11068
- set_attribute(input, 'name', `cv-toggle-${$$props.toggle.toggleId ?? ''}`);
11069
- set_attribute(input_1, 'name', `cv-toggle-${$$props.toggle.toggleId ?? ''}`);
11070
- set_attribute(input_2, 'name', `cv-toggle-${$$props.toggle.toggleId ?? ''}`);
11159
+ append($$anchor, button);
11071
11160
  });
11072
11161
 
11073
- bind_group(binding_group, [], input, value, value);
11074
- bind_group(binding_group, [], input_1, value, value);
11075
- bind_group(binding_group, [], input_2, value, value);
11162
+ reset(div_3);
11163
+ reset(div_1);
11164
+ reset(div);
11165
+ template_effect(() => set_text(text, $$props.toggle.label || $$props.toggle.toggleId));
11076
11166
  append($$anchor, div);
11077
11167
  pop();
11078
11168
  }
11079
11169
 
11080
- delegate(['change']);
11170
+ delegate(['click']);
11081
11171
 
11082
- var root_1$9 = from_html(`<p class="description svelte-uub3h8"> </p>`);
11083
- var root_2$4 = from_html(`<option> </option>`);
11084
- var root$g = from_html(`<div class="root svelte-uub3h8"><div class="header svelte-uub3h8"><label class="label svelte-uub3h8"> </label> <!></div> <select class="select svelte-uub3h8"></select></div>`);
11172
+ var root_1$a = from_html(`<p class="description svelte-uub3h8"> </p>`);
11173
+ var root_2$8 = from_html(`<option> </option>`);
11174
+ var root$i = from_html(`<div class="root svelte-uub3h8"><div class="header svelte-uub3h8"><label class="label svelte-uub3h8"> </label> <!></div> <select class="select svelte-uub3h8"></select></div>`);
11085
11175
 
11086
- const $$css$h = {
11176
+ const $$css$j = {
11087
11177
  hash: 'svelte-uub3h8',
11088
- code: '.root.svelte-uub3h8 {display:flex;flex-direction:column;gap:0.75rem;padding:0.75rem;background:var(--cv-bg);border:1px solid var(--cv-border);border-radius:0.5rem;}\n\n /* Remove special handling for last child since they are now separate cards */.root.svelte-uub3h8:last-child {border-bottom:1px solid var(--cv-border);}.header.svelte-uub3h8 {display:flex;flex-direction:column;gap:0.25rem;}.label.svelte-uub3h8 {font-size:0.875rem;color:var(--cv-text);margin:0;line-height:1.4;font-weight:500;display:block;cursor:pointer;}.description.svelte-uub3h8 {font-size:0.75rem;color:var(--cv-text-secondary);margin:0;line-height:1.4;}.select.svelte-uub3h8 {width:100%;border-radius:0.5rem;background:var(--cv-input-bg);border:1px solid var(--cv-input-border);color:var(--cv-text);padding:0.5rem 0.75rem;font-size:0.875rem;cursor:pointer;transition:all 0.15s ease;font-family:inherit;}.select.svelte-uub3h8:hover {border-color:var(--cv-text-secondary);}.select.svelte-uub3h8:focus {outline:none;border-color:var(--cv-primary);box-shadow:0 0 0 2px var(--cv-focus-ring);}'
11178
+ code: '.root.svelte-uub3h8 {display:flex;flex-direction:column;gap:0.5rem;padding:0.75rem;background:var(--cv-bg);border:1px solid var(--cv-border);border-radius:var(--cv-card-radius, 0.5rem);transition:background 0.15s ease;}.root.svelte-uub3h8:hover {background:var(--cv-bg-hover);}\n\n /* Remove special handling for last child since they are now separate cards */.root.svelte-uub3h8:last-child {border-bottom:1px solid var(--cv-border);}.header.svelte-uub3h8 {display:flex;flex-direction:column;gap:0.25rem;}.label.svelte-uub3h8 {font-size:0.875rem;color:var(--cv-text);margin:0;line-height:1.4;font-weight:500;display:block;cursor:pointer;}.description.svelte-uub3h8 {font-size:0.75rem;color:var(--cv-text-secondary);margin:0;line-height:1.4;}.select.svelte-uub3h8 {width:100%;border-radius:0.5rem;background:var(--cv-input-bg);border:1px solid var(--cv-input-border);color:var(--cv-text);padding:0.5rem 0.75rem;font-size:0.875rem;cursor:pointer;transition:all 0.15s ease;font-family:inherit;}.select.svelte-uub3h8:hover {border-color:var(--cv-text-secondary);}.select.svelte-uub3h8:focus {outline:none;border-color:var(--cv-primary);box-shadow:0 0 0 2px var(--cv-focus-ring);}'
11089
11179
  };
11090
11180
 
11091
11181
  function TabGroupItem($$anchor, $$props) {
11092
11182
  push($$props, true);
11093
- append_styles$1($$anchor, $$css$h);
11183
+ append_styles$1($$anchor, $$css$j);
11094
11184
 
11095
11185
  let activeTabId = prop($$props, 'activeTabId', 15, ''),
11096
11186
  onchange = prop($$props, 'onchange', 3, () => {});
@@ -11102,7 +11192,7 @@
11102
11192
  onchange()({ groupId: $$props.group.groupId, tabId: activeTabId() });
11103
11193
  }
11104
11194
 
11105
- var div = root$g();
11195
+ var div = root$i();
11106
11196
  var div_1 = child(div);
11107
11197
  var label = child(div_1);
11108
11198
  var text = child(label, true);
@@ -11113,7 +11203,7 @@
11113
11203
 
11114
11204
  {
11115
11205
  var consequent = ($$anchor) => {
11116
- var p = root_1$9();
11206
+ var p = root_1$a();
11117
11207
  var text_1 = child(p, true);
11118
11208
 
11119
11209
  reset(p);
@@ -11133,7 +11223,7 @@
11133
11223
  select.__change = onChange;
11134
11224
 
11135
11225
  each(select, 21, () => $$props.group.tabs, (tab) => tab.tabId, ($$anchor, tab) => {
11136
- var option = root_2$4();
11226
+ var option = root_2$8();
11137
11227
  var text_2 = child(option, true);
11138
11228
 
11139
11229
  reset(option);
@@ -11177,16 +11267,16 @@
11177
11267
 
11178
11268
  delegate(['change']);
11179
11269
 
11180
- var root$f = from_html(`<div class="placeholder-item svelte-1vp05mb"><label class="placeholder-label svelte-1vp05mb"> </label> <input class="placeholder-input svelte-1vp05mb" type="text"/></div>`);
11270
+ var root$h = from_html(`<div class="placeholder-item svelte-1vp05mb"><label class="placeholder-label svelte-1vp05mb"> </label> <input class="placeholder-input svelte-1vp05mb" type="text"/></div>`);
11181
11271
 
11182
- const $$css$g = {
11272
+ const $$css$i = {
11183
11273
  hash: 'svelte-1vp05mb',
11184
- code: '.placeholder-item.svelte-1vp05mb {display:flex;flex-direction:column;gap:0.25rem;}.placeholder-label.svelte-1vp05mb {font-size:0.85rem;font-weight:500;color:var(--cv-text);}.placeholder-input.svelte-1vp05mb {padding:0.5rem 0.75rem;border:1px solid var(--cv-input-border);border-radius:0.375rem;font-size:0.9rem;transition:border-color 0.2s;background:var(--cv-input-bg);color:var(--cv-text);}.placeholder-input.svelte-1vp05mb:focus {outline:none;border-color:var(--cv-primary);box-shadow:0 0 0 2px var(--cv-focus-ring);}'
11274
+ code: '.placeholder-item.svelte-1vp05mb {display:flex;flex-direction:column;gap:0.25rem;}.placeholder-label.svelte-1vp05mb {font-size:0.85rem;font-weight:500;color:var(--cv-text);}.placeholder-input.svelte-1vp05mb {padding:0.5rem 0.75rem;border:1px solid var(--cv-input-border);border-radius:var(--cv-card-radius, 0.5rem);font-size:0.9rem;transition:border-color 0.2s;background:var(--cv-input-bg);color:var(--cv-text);}.placeholder-input.svelte-1vp05mb:focus {outline:none;border-color:var(--cv-primary);box-shadow:0 0 0 2px var(--cv-focus-ring);}'
11185
11275
  };
11186
11276
 
11187
11277
  function PlaceholderItem($$anchor, $$props) {
11188
11278
  push($$props, true);
11189
- append_styles$1($$anchor, $$css$g);
11279
+ append_styles$1($$anchor, $$css$i);
11190
11280
 
11191
11281
  let value = prop($$props, 'value', 15, ''),
11192
11282
  onchange = prop($$props, 'onchange', 3, () => {});
@@ -11199,7 +11289,7 @@
11199
11289
  }
11200
11290
 
11201
11291
  const sanitizedId = user_derived(() => `cv-placeholder-${$$props.definition.name.replace(/[^a-zA-Z0-9-_]/g, '-')}`);
11202
- var div = root$f();
11292
+ var div = root$h();
11203
11293
  var label = child(div);
11204
11294
  var text = child(label, true);
11205
11295
 
@@ -11266,34 +11356,33 @@
11266
11356
  }
11267
11357
  }
11268
11358
 
11269
- var root_1$8 = from_html(`<button>Customize</button>`);
11270
- var root_3$2 = from_html(`<p class="description svelte-16uy9h6"> </p>`);
11271
- var root_5 = from_html(`<div class="section svelte-16uy9h6"><div class="section-heading svelte-16uy9h6">Toggles</div> <div class="toggles-container svelte-16uy9h6"></div></div>`);
11359
+ var root_1$9 = from_html(`<button>Customize</button>`);
11360
+ var root_3$5 = from_html(`<p class="description svelte-16uy9h6"> </p>`);
11361
+ var root_5$1 = from_html(`<div class="section svelte-16uy9h6"><div class="section-heading svelte-16uy9h6">Toggles</div> <div class="toggles-container svelte-16uy9h6"></div></div>`);
11272
11362
  var root_7 = from_html(`<div class="section svelte-16uy9h6"><div class="section-heading svelte-16uy9h6">Placeholders</div> <div class="placeholders-container svelte-16uy9h6"></div></div>`);
11273
11363
  var root_9 = from_html(`<div class="section svelte-16uy9h6"><div class="section-heading svelte-16uy9h6">Tab Groups</div> <div class="tabgroups-container svelte-16uy9h6"><div class="tabgroup-card header-card svelte-16uy9h6" role="group"><div class="tabgroup-row svelte-16uy9h6"><div class="logo-box svelte-16uy9h6" id="cv-nav-icon-box"><div class="nav-icon svelte-16uy9h6"><!></div></div> <div class="tabgroup-info svelte-16uy9h6"><div class="tabgroup-title-container"><p class="tabgroup-title svelte-16uy9h6">Show only the selected tab</p></div> <p class="tabgroup-description svelte-16uy9h6">Hide the navigation headers</p></div> <label class="toggle-switch nav-toggle svelte-16uy9h6"><input class="nav-pref-input svelte-16uy9h6" type="checkbox" aria-label="Show only the selected tab"/> <span class="switch-bg svelte-16uy9h6"></span> <span class="switch-knob svelte-16uy9h6"></span></label></div></div> <div class="tab-groups-list svelte-16uy9h6"></div></div></div>`);
11274
- var root_4 = from_html(`<!> <!> <!>`, 1);
11275
- var root_2$3 = from_html(`<div class="tab-content active svelte-16uy9h6"><!> <!></div>`);
11364
+ var root_4$2 = from_html(`<!> <!> <!>`, 1);
11365
+ var root_2$7 = from_html(`<div class="tab-content active svelte-16uy9h6"><!> <!></div>`);
11276
11366
  var root_16 = from_html(`<button class="share-action-btn copy-url-btn svelte-16uy9h6"><span class="btn-icon svelte-16uy9h6"><!></span> <span><!></span></button>`);
11277
11367
 
11278
11368
  var root_15 = from_html(`<div class="tab-content active svelte-16uy9h6"><div class="share-content svelte-16uy9h6"><div class="share-instruction svelte-16uy9h6">Create a shareable link for your current customization, or select specific parts of
11279
11369
  the page to share.</div> <button class="share-action-btn primary start-share-btn svelte-16uy9h6"><span class="btn-icon svelte-16uy9h6"><!></span> <span>Select elements to share</span></button> <!></div></div>`);
11280
11370
 
11281
- var root_21 = from_html(`<button class="reset-btn svelte-16uy9h6" title="Reset to Default"><span><!></span> <span>Reset</span></button>`);
11371
+ var root_21 = from_html(`<button class="reset-btn svelte-16uy9h6" title="Reset to Default">Reset</button>`);
11282
11372
  var root_22 = from_html(`<div></div>`);
11283
- var root$e = from_html(`<div class="modal-overlay svelte-16uy9h6" role="presentation"><div class="modal-box cv-custom-state-modal svelte-16uy9h6" role="dialog" aria-modal="true"><header class="header svelte-16uy9h6"><div class="header-content svelte-16uy9h6"><div class="modal-icon svelte-16uy9h6"><!></div> <div class="title svelte-16uy9h6"> </div></div> <button class="close-btn svelte-16uy9h6" aria-label="Close modal"><!></button></header> <main class="main svelte-16uy9h6"><div class="tabs svelte-16uy9h6"><!> <button>Share</button></div> <!></main> <footer class="footer svelte-16uy9h6"><!> <a href="https://github.com/custardui/custardui" target="_blank" class="footer-link svelte-16uy9h6"><!> <span>View on GitHub</span></a> <button class="done-btn svelte-16uy9h6">Done</button></footer></div></div>`);
11373
+ var root$g = from_html(`<div class="modal-overlay svelte-16uy9h6" role="presentation"><div class="modal-box cv-custom-state-modal svelte-16uy9h6" role="dialog" aria-modal="true"><header class="header svelte-16uy9h6"><div class="header-content svelte-16uy9h6"><div class="modal-icon svelte-16uy9h6"><!></div> <div class="title svelte-16uy9h6"> </div></div> <button class="close-btn svelte-16uy9h6" aria-label="Close modal"><!></button></header> <main class="main svelte-16uy9h6"><div class="tabs svelte-16uy9h6"><!> <button>Share</button></div> <!></main> <footer class="footer svelte-16uy9h6"><!> <a href="https://custardui.js.org" target="_blank" rel="noopener noreferrer" class="footer-link svelte-16uy9h6">custardui.js.org</a> <button class="done-btn svelte-16uy9h6">Done</button></footer></div></div>`);
11284
11374
 
11285
- const $$css$f = {
11375
+ const $$css$h = {
11286
11376
  hash: 'svelte-16uy9h6',
11287
- code: '\n /* Modal Overlay & Modal Frame */.modal-overlay.svelte-16uy9h6 {position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0, 0, 0, 0.5);display:flex;align-items:center;justify-content:center;z-index:10002;}.modal-box.svelte-16uy9h6 {background:var(--cv-bg);border-radius:0.75rem;box-shadow:0 25px 50px -12px var(--cv-shadow);max-width:32rem;width:90vw;max-height:80vh;display:flex;flex-direction:column;}.header.svelte-16uy9h6 {display:flex;align-items:center;justify-content:space-between;padding:0.5rem 1rem;border-bottom:1px solid var(--cv-border);}.header-content.svelte-16uy9h6 {display:flex;align-items:center;gap:0.75rem;}.modal-icon.svelte-16uy9h6 {position:relative;width:1rem;height:1rem;display:flex;align-items:center;justify-content:center;border-radius:9999px;color:var(--cv-text);}.modal-icon.svelte-16uy9h6 svg {fill:currentColor;}.title.svelte-16uy9h6 {font-size:1.125rem;font-weight:bold;color:var(--cv-text);margin:0;}.close-btn.svelte-16uy9h6 {width:2rem;height:2rem;display:flex;align-items:center;justify-content:center;border-radius:9999px;background:transparent;border:none;color:var(--cv-text-secondary);cursor:pointer;transition:all 0.2s ease;}.close-btn.svelte-16uy9h6:hover {background:rgba(62, 132, 244, 0.1);color:var(--cv-primary);}.main.svelte-16uy9h6 {padding:1rem;flex:1;display:flex;flex-direction:column;overflow-y:auto;max-height:calc(80vh - 8rem);min-height:var(--cv-modal-min-height, 20rem);}.description.svelte-16uy9h6 {font-size:0.875rem;color:var(--cv-text);margin:0 0 1rem 0;line-height:1.4;}\n\n /* Tabs */.tabs.svelte-16uy9h6 {display:flex;margin-bottom:1rem;border-bottom:2px solid var(--cv-border);}.tab.svelte-16uy9h6 {background:transparent;border:none;padding:0.5rem 1rem;font-size:0.9rem;font-weight:600;color:var(--cv-text-secondary);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-2px;}.tab.active.svelte-16uy9h6 {color:var(--cv-primary);border-bottom-color:var(--cv-primary);}.tab-content.svelte-16uy9h6 {display:none;}.tab-content.active.svelte-16uy9h6 {display:block;}\n\n /* Section Styling */.section.svelte-16uy9h6 {display:flex;flex-direction:column;gap:0.75rem;margin-bottom:1.5rem;}.section-heading.svelte-16uy9h6 {font-size:1rem;font-weight:bold;color:var(--cv-text);margin:0;}.toggles-container.svelte-16uy9h6 {display:flex;flex-direction:column;gap:0.5rem;overflow:hidden;}\n\n /* Tab Groups Section specific */.tabgroups-container.svelte-16uy9h6 {border-radius:0.5rem;}\n\n /* Nav Toggle Card */.tabgroup-card.svelte-16uy9h6 {background:var(--cv-bg);border-bottom:1px solid var(--cv-border);}.tabgroup-card.header-card.svelte-16uy9h6 {display:flex;align-items:center;justify-content:space-between;padding:0.75rem;border:1px solid var(--cv-border);border-radius:0.5rem;margin-bottom:0.75rem;}.tabgroup-row.svelte-16uy9h6 {display:flex;align-items:center;justify-content:space-between;width:100%;gap:1rem;}.logo-box.svelte-16uy9h6 {width:3rem;height:3rem;background:var(--cv-modal-icon-bg);border-radius:0.5rem;display:flex;align-items:center;justify-content:center;flex-shrink:0;}.nav-icon.svelte-16uy9h6 {width:2rem;height:2rem;color:var(--cv-text);display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:color 0.2s ease;}.tabgroup-info.svelte-16uy9h6 {flex:1;display:flex;flex-direction:column;gap:0.25rem;}.tabgroup-title.svelte-16uy9h6 {font-weight:500;font-size:0.875rem;color:var(--cv-text);margin:0 0 0 0;}.tabgroup-description.svelte-16uy9h6 {font-size:0.75rem;color:var(--cv-text-secondary);margin:0;line-height:1.3;}\n\n /* Toggle Switch */.toggle-switch.svelte-16uy9h6 {position:relative;display:inline-flex;align-items:center;width:44px;height:24px;background:var(--cv-switch-bg);border-radius:9999px;padding:2px;box-sizing:border-box;cursor:pointer;transition:background-color 0.2s ease;border:none;}.toggle-switch.svelte-16uy9h6 input:where(.svelte-16uy9h6) {display:none;}.toggle-switch.svelte-16uy9h6 .switch-bg:where(.svelte-16uy9h6) {position:absolute;inset:0;border-radius:9999px;background:var(--cv-switch-bg);transition:background-color 0.2s ease;pointer-events:none;}.toggle-switch.svelte-16uy9h6 .switch-knob:where(.svelte-16uy9h6) {position:relative;width:20px;height:20px;background:var(--cv-switch-knob);border-radius:50%;box-shadow:0 1px 2px rgba(0, 0, 0, 0.1);transition:transform 0.2s ease;transform:translateX(0);}.toggle-switch.svelte-16uy9h6 input:where(.svelte-16uy9h6):checked ~ .switch-knob:where(.svelte-16uy9h6) {transform:translateX(20px);}.toggle-switch.svelte-16uy9h6 input:where(.svelte-16uy9h6):checked ~ .switch-bg:where(.svelte-16uy9h6) {background:var(--cv-primary);}\n\n /* Tab Groups List */.tab-groups-list.svelte-16uy9h6 {display:flex;flex-direction:column;gap:0.75rem;}\n\n /* Footer */.footer.svelte-16uy9h6 {padding:1rem;border-top:1px solid var(--cv-border);display:flex;align-items:center;justify-content:space-between;background:var(--cv-bg);border-bottom-left-radius:0.75rem;border-bottom-right-radius:0.75rem;}.footer-link.svelte-16uy9h6 {display:flex;align-items:center;gap:0.5rem;color:var(--cv-text-secondary);text-decoration:none;font-size:0.875rem;font-weight:500;transition:color 0.15s ease;}.footer-link.svelte-16uy9h6:hover {color:var(--cv-text);}.reset-btn.svelte-16uy9h6 {display:flex;align-items:center;gap:0.5rem;background:var(--cv-bg);border:1px solid var(--cv-border);font-size:0.875rem;font-weight:500;color:var(--cv-danger);cursor:pointer;padding:0.5rem 0.75rem;border-radius:0.5rem;transition:all 0.2s ease;box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);}.reset-btn.svelte-16uy9h6:hover {background:var(--cv-danger-bg);border-color:var(--cv-danger);box-shadow:0 2px 4px rgba(0, 0, 0, 0.05);}.done-btn.svelte-16uy9h6 {background:var(--cv-primary);color:white;border:none;padding:0.5rem 1rem;border-radius:0.375rem;font-weight:600;font-size:0.875rem;cursor:pointer;transition:background-color 0.15s ease;}.done-btn.svelte-16uy9h6:hover {background:var(--cv-primary-hover);}.reset-btn-icon.svelte-16uy9h6 {display:flex;align-items:center;width:1.25rem;height:1.25rem;}.spinning {\n animation: svelte-16uy9h6-spin 1s linear infinite;}\n\n @keyframes svelte-16uy9h6-spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }\n\n /* Share Tab Styles */.share-content.svelte-16uy9h6 {display:flex;flex-direction:column;gap:1rem;align-items:center;text-align:center;padding:1rem 0;}.share-instruction.svelte-16uy9h6 {font-size:0.95rem;color:var(--cv-text-secondary);margin-bottom:0.5rem;}.share-action-btn.svelte-16uy9h6 {display:flex;align-items:center;justify-content:center;gap:0.75rem;width:100%;max-width:320px;padding:0.75rem 1rem;border-radius:0.5rem;font-weight:500;font-size:0.95rem;cursor:pointer;transition:all 0.2s ease;border:1px solid var(--cv-border);background:var(--cv-bg);color:var(--cv-text);}.share-action-btn.svelte-16uy9h6:hover {border-color:var(--cv-primary);color:var(--cv-primary);background:var(--cv-bg-hover);}.share-action-btn.primary.svelte-16uy9h6 {background:var(--cv-primary);border-color:var(--cv-primary);color:white;}.share-action-btn.primary.svelte-16uy9h6:hover {background:var(--cv-primary-hover);border-color:var(--cv-primary-hover);}.btn-icon.svelte-16uy9h6 {display:flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;}\n\n /* Placeholder Inputs */.placeholders-container.svelte-16uy9h6 {display:flex;flex-direction:column;gap:0.75rem;}'
11377
+ code: '\n /* Modal Overlay & Modal Frame */.modal-overlay.svelte-16uy9h6 {position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0, 0, 0, 0.5);display:flex;align-items:center;justify-content:center;z-index:10002;}.modal-box.svelte-16uy9h6 {background:var(--cv-bg);border-radius:var(--cv-modal-radius, 0.75rem);box-shadow:0 25px 50px -12px var(--cv-shadow);max-width:32rem;width:90vw;max-height:80vh;display:flex;flex-direction:column;}.header.svelte-16uy9h6 {display:flex;align-items:center;justify-content:space-between;padding:0.5rem 1rem;border-bottom:1px solid var(--cv-border);}.header-content.svelte-16uy9h6 {display:flex;align-items:center;gap:0.75rem;}.modal-icon.svelte-16uy9h6 {position:relative;width:1rem;height:1rem;display:flex;align-items:center;justify-content:center;border-radius:9999px;color:var(--cv-text);}.modal-icon.svelte-16uy9h6 svg {fill:currentColor;}.title.svelte-16uy9h6 {font-size:1.125rem;font-weight:bold;color:var(--cv-text);margin:0;}.close-btn.svelte-16uy9h6 {width:2rem;height:2rem;display:flex;align-items:center;justify-content:center;border-radius:9999px;background:transparent;border:none;color:var(--cv-text-secondary);cursor:pointer;transition:all 0.2s ease;}.close-btn.svelte-16uy9h6:hover {background:rgba(62, 132, 244, 0.1);color:var(--cv-primary);}.main.svelte-16uy9h6 {padding:1rem;flex:1;display:flex;flex-direction:column;overflow-y:auto;max-height:calc(80vh - 8rem);min-height:var(--cv-modal-min-height, 20rem);}.description.svelte-16uy9h6 {font-size:0.875rem;color:var(--cv-text);margin:0 0 1rem 0;line-height:1.4;}\n\n /* Tabs */.tabs.svelte-16uy9h6 {display:flex;margin-bottom:1rem;border-bottom:2px solid var(--cv-border);}.tab.svelte-16uy9h6 {background:transparent;border:none;padding:0.5rem 1rem;font-size:0.9rem;font-weight:600;color:var(--cv-text-secondary);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-2px;}.tab.active.svelte-16uy9h6 {color:var(--cv-primary);border-bottom-color:var(--cv-primary);}.tab-content.svelte-16uy9h6 {display:none;}.tab-content.active.svelte-16uy9h6 {display:block;}\n\n /* Section Styling */.section.svelte-16uy9h6 {display:flex;flex-direction:column;gap:0.75rem;margin-bottom:1.5rem;}.section-heading.svelte-16uy9h6 {font-size:0.7rem;font-weight:600;color:var(--cv-text-secondary);text-transform:var(--cv-section-label-transform, uppercase);letter-spacing:0.08em;margin:0;}.toggles-container.svelte-16uy9h6 {display:flex;flex-direction:column;gap:0.5rem;overflow:hidden;}\n\n /* Tab Groups Section specific */.tabgroups-container.svelte-16uy9h6 {border-radius:0.5rem;}\n\n /* Nav Toggle Card */.tabgroup-card.svelte-16uy9h6 {background:var(--cv-bg);border-bottom:1px solid var(--cv-border);}.tabgroup-card.header-card.svelte-16uy9h6 {display:flex;align-items:center;justify-content:space-between;padding:0.75rem;border:1px solid var(--cv-border);border-radius:0.5rem;margin-bottom:0.75rem;}.tabgroup-row.svelte-16uy9h6 {display:flex;align-items:center;justify-content:space-between;width:100%;gap:1rem;}.logo-box.svelte-16uy9h6 {width:3rem;height:3rem;background:var(--cv-modal-icon-bg);border-radius:0.5rem;display:flex;align-items:center;justify-content:center;flex-shrink:0;}.nav-icon.svelte-16uy9h6 {width:2rem;height:2rem;color:var(--cv-text);display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:color 0.2s ease;}.tabgroup-info.svelte-16uy9h6 {flex:1;display:flex;flex-direction:column;gap:0.25rem;}.tabgroup-title.svelte-16uy9h6 {font-weight:500;font-size:0.875rem;color:var(--cv-text);margin:0 0 0 0;}.tabgroup-description.svelte-16uy9h6 {font-size:0.75rem;color:var(--cv-text-secondary);margin:0;line-height:1.3;}\n\n /* Toggle Switch */.toggle-switch.svelte-16uy9h6 {position:relative;display:inline-flex;align-items:center;width:44px;height:24px;background:var(--cv-switch-bg);border-radius:9999px;padding:2px;box-sizing:border-box;cursor:pointer;transition:background-color 0.2s ease;border:none;}.toggle-switch.svelte-16uy9h6 input:where(.svelte-16uy9h6) {display:none;}.toggle-switch.svelte-16uy9h6 .switch-bg:where(.svelte-16uy9h6) {position:absolute;inset:0;border-radius:9999px;background:var(--cv-switch-bg);transition:background-color 0.2s ease;pointer-events:none;}.toggle-switch.svelte-16uy9h6 .switch-knob:where(.svelte-16uy9h6) {position:relative;width:20px;height:20px;background:var(--cv-switch-knob);border-radius:50%;box-shadow:0 1px 2px rgba(0, 0, 0, 0.1);transition:transform 0.2s ease;transform:translateX(0);}.toggle-switch.svelte-16uy9h6 input:where(.svelte-16uy9h6):checked ~ .switch-knob:where(.svelte-16uy9h6) {transform:translateX(20px);}.toggle-switch.svelte-16uy9h6 input:where(.svelte-16uy9h6):checked ~ .switch-bg:where(.svelte-16uy9h6) {background:var(--cv-primary);}\n\n /* Tab Groups List */.tab-groups-list.svelte-16uy9h6 {display:flex;flex-direction:column;gap:0.75rem;}\n\n /* Footer */.footer.svelte-16uy9h6 {padding:0.75rem 1rem;border-top:1px solid var(--cv-border);display:flex;align-items:center;justify-content:space-between;background:var(--cv-bg);border-bottom-left-radius:var(--cv-modal-radius, 0.75rem);border-bottom-right-radius:var(--cv-modal-radius, 0.75rem);}.footer-link.svelte-16uy9h6 {align-self:flex-end;color:var(--cv-text-secondary);text-decoration:none;font-size:0.68rem;font-weight:500;letter-spacing:0.08em;opacity:0.5;transition:color 0.15s ease, opacity 0.15s ease;}.footer-link.svelte-16uy9h6:hover {color:var(--cv-primary);opacity:1;}.reset-btn.svelte-16uy9h6 {display:flex;align-items:center;gap:0.4rem;background:transparent;border:none;font-size:0.875rem;font-weight:500;color:var(--cv-text-secondary);cursor:pointer;padding:0.4rem 0.5rem;border-radius:0.5rem;transition:all 0.2s ease;}.reset-btn.svelte-16uy9h6:hover {background:var(--cv-danger-bg);color:var(--cv-danger);}.done-btn.svelte-16uy9h6 {background:var(--cv-primary);color:white;border:none;padding:0.5rem 1.1rem;border-radius:0.5rem;font-weight:600;font-size:0.875rem;cursor:pointer;box-shadow:0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.08);transition:background-color 0.15s ease, box-shadow 0.15s ease;}.done-btn.svelte-16uy9h6:hover {background:var(--cv-primary-hover);box-shadow:0 3px 6px rgba(0, 0, 0, 0.12), 0 2px 4px rgba(0, 0, 0, 0.08);}\n\n /* Share Tab Styles */.share-content.svelte-16uy9h6 {display:flex;flex-direction:column;gap:1rem;align-items:center;text-align:center;padding:1rem 0;}.share-instruction.svelte-16uy9h6 {font-size:0.95rem;color:var(--cv-text-secondary);margin-bottom:0.5rem;}.share-action-btn.svelte-16uy9h6 {display:flex;align-items:center;justify-content:center;gap:0.75rem;width:100%;max-width:320px;padding:0.75rem 1rem;border-radius:0.5rem;font-weight:500;font-size:0.95rem;cursor:pointer;transition:all 0.2s ease;border:1px solid var(--cv-border);background:var(--cv-bg);color:var(--cv-text);}.share-action-btn.svelte-16uy9h6:hover {border-color:var(--cv-primary);color:var(--cv-primary);background:var(--cv-bg-hover);}.share-action-btn.primary.svelte-16uy9h6 {background:var(--cv-primary);border-color:var(--cv-primary);color:white;}.share-action-btn.primary.svelte-16uy9h6:hover {background:var(--cv-primary-hover);border-color:var(--cv-primary-hover);}.btn-icon.svelte-16uy9h6 {display:flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;}\n\n /* Placeholder Inputs */.placeholders-container.svelte-16uy9h6 {display:flex;flex-direction:column;gap:0.75rem;}'
11288
11378
  };
11289
11379
 
11290
11380
  function Modal($$anchor, $$props) {
11291
11381
  push($$props, true);
11292
- append_styles$1($$anchor, $$css$f);
11382
+ append_styles$1($$anchor, $$css$h);
11293
11383
 
11294
11384
  /* eslint-disable @typescript-eslint/no-explicit-any */
11295
- let isResetting = prop($$props, 'isResetting', 3, false),
11296
- onclose = prop($$props, 'onclose', 3, () => {}),
11385
+ let onclose = prop($$props, 'onclose', 3, () => {}),
11297
11386
  onreset = prop($$props, 'onreset', 3, () => {}),
11298
11387
  onstartShare = prop($$props, 'onstartShare', 3, () => {});
11299
11388
 
@@ -11368,30 +11457,25 @@
11368
11457
  // --- Core Actions ---
11369
11458
  function handleToggleChange(detail) {
11370
11459
  const { toggleId, value } = detail;
11371
- const currentShown = activeStateStore.state.shownToggles || [];
11372
- const currentPeek = activeStateStore.state.peekToggles || [];
11373
- const newShown = currentShown.filter((id) => id !== toggleId);
11374
- const newPeek = currentPeek.filter((id) => id !== toggleId);
11375
-
11376
- if (value === 'show') newShown.push(toggleId);
11377
- if (value === 'peek') newPeek.push(toggleId);
11378
11460
 
11379
- activeStateStore.setToggles(newShown, newPeek);
11461
+ activeStateStore.updateToggleState(toggleId, value);
11380
11462
  }
11381
11463
 
11382
11464
  function handleTabGroupChange(detail) {
11383
11465
  const { groupId, tabId } = detail;
11384
11466
 
11385
- // Scroll Logic: Capture target before state update
11386
- const groupToScrollTo = findHighestVisibleElement('cv-tabgroup');
11467
+ // Capture element and its current visual position before state change
11468
+ const anchorEl = findHighestVisibleElement('cv-tabgroup');
11469
+
11470
+ const scrollAnchor = anchorEl
11471
+ ? { element: anchorEl, top: anchorEl.getBoundingClientRect().top }
11472
+ : null;
11387
11473
 
11388
11474
  activeStateStore.setPinnedTab(groupId, tabId);
11389
11475
 
11390
- // Restore scroll after update
11391
- if (groupToScrollTo) {
11392
- queueMicrotask(() => {
11393
- scrollToElement(groupToScrollTo);
11394
- });
11476
+ // Restore visual position after layout shift
11477
+ if (scrollAnchor) {
11478
+ handleScrollAnchor(scrollAnchor);
11395
11479
  }
11396
11480
  }
11397
11481
 
@@ -11430,7 +11514,7 @@
11430
11514
  return 'hide';
11431
11515
  }
11432
11516
 
11433
- var div = root$e();
11517
+ var div = root$g();
11434
11518
 
11435
11519
  div.__click = (e) => {
11436
11520
  if (e.target === e.currentTarget) onclose()();
@@ -11470,7 +11554,7 @@
11470
11554
 
11471
11555
  {
11472
11556
  var consequent = ($$anchor) => {
11473
- var button_1 = root_1$8();
11557
+ var button_1 = root_1$9();
11474
11558
 
11475
11559
  button_1.__click = () => set(activeTab, 'customize');
11476
11560
  template_effect(() => set_class(button_1, 1, `tab ${get(activeTab) === 'customize' ? 'active' : ''}`, 'svelte-16uy9h6'));
@@ -11491,12 +11575,12 @@
11491
11575
 
11492
11576
  {
11493
11577
  var consequent_7 = ($$anchor) => {
11494
- var div_6 = root_2$3();
11578
+ var div_6 = root_2$7();
11495
11579
  var node_4 = child(div_6);
11496
11580
 
11497
11581
  {
11498
11582
  var consequent_1 = ($$anchor) => {
11499
- var p = root_3$2();
11583
+ var p = root_3$5();
11500
11584
  var text_1 = child(p, true);
11501
11585
 
11502
11586
  reset(p);
@@ -11512,12 +11596,12 @@
11512
11596
  var node_5 = sibling(node_4, 2);
11513
11597
 
11514
11598
  each(node_5, 16, () => get(sectionOrder), (section) => section, ($$anchor, section) => {
11515
- var fragment = root_4();
11599
+ var fragment = root_4$2();
11516
11600
  var node_6 = first_child(fragment);
11517
11601
 
11518
11602
  {
11519
11603
  var consequent_2 = ($$anchor) => {
11520
- var div_7 = root_5();
11604
+ var div_7 = root_5$1();
11521
11605
  var div_8 = sibling(child(div_7), 2);
11522
11606
 
11523
11607
  each(div_8, 21, () => get(toggles), (toggle) => toggle.toggleId, ($$anchor, toggle) => {
@@ -11782,14 +11866,6 @@
11782
11866
  onreset()?.apply(this, $$args);
11783
11867
  };
11784
11868
 
11785
- var span_3 = child(button_5);
11786
- var node_16 = child(span_3);
11787
-
11788
- IconReset(node_16, {});
11789
- reset(span_3);
11790
- next(2);
11791
- reset(button_5);
11792
- template_effect(() => set_class(span_3, 1, `reset-btn-icon ${isResetting() ? 'spinning' : ''}`, 'svelte-16uy9h6'));
11793
11869
  append($$anchor, button_5);
11794
11870
  };
11795
11871
 
@@ -11804,14 +11880,7 @@
11804
11880
  });
11805
11881
  }
11806
11882
 
11807
- var a = sibling(node_15, 2);
11808
- var node_17 = child(a);
11809
-
11810
- IconGitHub(node_17, {});
11811
- next(2);
11812
- reset(a);
11813
-
11814
- var button_6 = sibling(a, 2);
11883
+ var button_6 = sibling(node_15, 4);
11815
11884
 
11816
11885
  button_6.__click = function (...$$args) {
11817
11886
  onclose()?.apply(this, $$args);
@@ -11840,6 +11909,27 @@
11840
11909
 
11841
11910
  delegate(['click', 'change']);
11842
11911
 
11912
+ const HIGHLIGHT_COLORS = [
11913
+ { key: 'yellow', label: 'Yellow', hex: '#f5f521' }, // default
11914
+ { key: 'blue', label: 'Blue', hex: '#3b82f6' },
11915
+ { key: 'red', label: 'Red', hex: '#ef4444' },
11916
+ { key: 'black', label: 'Black', hex: '#1a1a1a' },
11917
+ { key: 'green', label: 'Green', hex: '#22c55e' },
11918
+ ];
11919
+ const DEFAULT_COLOR_KEY = 'yellow';
11920
+
11921
+ const ANNOTATION_CORNERS = ['tl', 'tr', 'bl', 'br'];
11922
+ const CORNER_ICONS = [
11923
+ { key: 'tl', icon: '◤' },
11924
+ { key: 'tr', icon: '◥' },
11925
+ { key: 'bl', icon: '◣' },
11926
+ { key: 'br', icon: '◢' },
11927
+ ];
11928
+ const DEFAULT_ANNOTATION_CORNER = 'tl';
11929
+ const MAX_ANNOTATION_LENGTH = 280;
11930
+ const ANNOTATION_PREVIEW_LENGTH = 40;
11931
+
11932
+ /* eslint-disable @typescript-eslint/no-explicit-any */
11843
11933
  /**
11844
11934
  * Generates a simple hash code for a string.
11845
11935
  */
@@ -11906,11 +11996,31 @@
11906
11996
  s: d.textSnippet,
11907
11997
  h: d.textHash,
11908
11998
  id: d.elementId,
11999
+ ...(d.color && d.color !== DEFAULT_COLOR_KEY ? { c: d.color } : {}),
12000
+ ...(d.annotation ? { n: d.annotation } : {}),
12001
+ ...(d.annotation && d.annotationCorner && d.annotationCorner !== DEFAULT_ANNOTATION_CORNER
12002
+ ? { nc: d.annotationCorner }
12003
+ : {}),
11909
12004
  }));
11910
- // Check if all have IDs, use human-readable format
12005
+ // When all elements have stable IDs, use a human-readable format.
12006
+ // Annotations are encoded via encodeURIComponent so the format stays URL-readable.
12007
+ // Format per element:
12008
+ // no color, no note → "id"
12009
+ // color only → "id:color"
12010
+ // color + note → "id:color:corner:encodedNote" (always 4 parts)
12011
+ // note, no color → "id::corner:encodedNote"
11911
12012
  const allHaveIds = minified.every((m) => !!m.id);
11912
12013
  if (allHaveIds) {
11913
- return minified.map((m) => m.id).join(',');
12014
+ return minified.map((m) => {
12015
+ const id = m.id;
12016
+ const c = m.c ?? '';
12017
+ const n = m.n;
12018
+ const nc = m.nc ?? DEFAULT_ANNOTATION_CORNER;
12019
+ if (n) {
12020
+ return `${id}:${c}:${nc}:${encodeURIComponent(n)}`;
12021
+ }
12022
+ return c ? `${id}:${c}` : id;
12023
+ }).join(',');
11914
12024
  }
11915
12025
  const json = JSON.stringify(minified);
11916
12026
  // Modern UTF-8 safe Base64 encoding
@@ -11954,7 +12064,7 @@
11954
12064
  // Robustness: Ensure item is an object
11955
12065
  if (typeof m !== 'object' || m === null)
11956
12066
  throw new Error('Item is not an object');
11957
- return {
12067
+ const descriptor = {
11958
12068
  tag: m.t,
11959
12069
  index: m.i,
11960
12070
  parentId: m.p,
@@ -11962,6 +12072,13 @@
11962
12072
  textHash: m.h,
11963
12073
  elementId: m.id,
11964
12074
  };
12075
+ if (m.c)
12076
+ descriptor.color = m.c;
12077
+ if (m.n) {
12078
+ descriptor.annotation = m.n;
12079
+ descriptor.annotationCorner = (m.nc ?? DEFAULT_ANNOTATION_CORNER);
12080
+ }
12081
+ return descriptor;
11965
12082
  });
11966
12083
  }
11967
12084
  catch {
@@ -11969,18 +12086,67 @@
11969
12086
  return parseIds(encoded);
11970
12087
  }
11971
12088
  }
12089
+ const COLOR_KEYS = new Set(HIGHLIGHT_COLORS.map((c) => c.key));
12090
+ const CORNER_KEYS = new Set(ANNOTATION_CORNERS);
11972
12091
  /**
11973
12092
  * Parses a space-separated, plus-separated, or comma-separated list of IDs into a list of AnchorDescriptors.
12093
+ * Supports:
12094
+ * "id" — ID only
12095
+ * "id:color" — with color
12096
+ * "id:color:corner:note" — with color + annotation (note is percent-encoded)
12097
+ * "id::corner:note" — annotation, no color
11974
12098
  */
11975
12099
  function parseIds(encoded) {
11976
12100
  const parts = encoded.split(/[ +,]+/).filter((p) => p.length > 0);
11977
- return parts.map((id) => ({
11978
- tag: 'ANY',
11979
- index: 0,
11980
- textSnippet: '',
11981
- textHash: 0,
11982
- elementId: id,
11983
- }));
12101
+ return parts.map((part) => {
12102
+ // Split into at most 4 segments on ':'. The note segment (4th) is the
12103
+ // remainder so it can itself contain encoded colons (%3A).
12104
+ const segs = part.split(':');
12105
+ let id = part;
12106
+ let color;
12107
+ let annotation;
12108
+ let annotationCorner;
12109
+ if (segs.length >= 4) {
12110
+ // "id:color:corner:encodedNote..."
12111
+ id = segs[0];
12112
+ const colorSeg = segs[1];
12113
+ const cornerSeg = segs[2];
12114
+ const noteSeg = segs.slice(3).join(':'); // re-join in case of extra colons
12115
+ if (COLOR_KEYS.has(colorSeg))
12116
+ color = colorSeg;
12117
+ annotationCorner = CORNER_KEYS.has(cornerSeg)
12118
+ ? cornerSeg
12119
+ : DEFAULT_ANNOTATION_CORNER;
12120
+ try {
12121
+ annotation = decodeURIComponent(noteSeg);
12122
+ }
12123
+ catch {
12124
+ annotation = noteSeg;
12125
+ }
12126
+ }
12127
+ else if (segs.length === 2) {
12128
+ // "id:color"
12129
+ const colorSeg = segs[1];
12130
+ if (COLOR_KEYS.has(colorSeg)) {
12131
+ id = segs[0];
12132
+ color = colorSeg;
12133
+ }
12134
+ }
12135
+ const descriptor = {
12136
+ tag: 'ANY',
12137
+ index: 0,
12138
+ textSnippet: '',
12139
+ textHash: 0,
12140
+ elementId: id,
12141
+ };
12142
+ if (color !== undefined)
12143
+ descriptor.color = color;
12144
+ if (annotation !== undefined && annotationCorner !== undefined) {
12145
+ descriptor.annotation = annotation;
12146
+ descriptor.annotationCorner = annotationCorner;
12147
+ }
12148
+ return descriptor;
12149
+ });
11984
12150
  }
11985
12151
  const SCORE_EXACT_HASH = 50;
11986
12152
  const SCORE_SNIPPET_START = 30;
@@ -12213,6 +12379,8 @@
12213
12379
  set(this.#currentHoverTarget, value, true);
12214
12380
  }
12215
12381
 
12382
+ highlightColors = new SvelteMap();
12383
+ highlightAnnotations = new SvelteMap();
12216
12384
  #shareCount = user_derived(() => this.selectedElements.size);
12217
12385
 
12218
12386
  get shareCount() {
@@ -12289,6 +12457,8 @@
12289
12457
  this.selectedElements.forEach((oldEl) => {
12290
12458
  if (!updatedSelection.has(oldEl)) {
12291
12459
  this._removeSelectionClass(oldEl);
12460
+ this.highlightColors.delete(oldEl);
12461
+ this.highlightAnnotations.delete(oldEl);
12292
12462
  }
12293
12463
  });
12294
12464
 
@@ -12314,6 +12484,30 @@
12314
12484
  clearAllSelections() {
12315
12485
  this.selectedElements.forEach((el) => this._removeSelectionClass(el));
12316
12486
  this.selectedElements.clear();
12487
+ this.highlightColors.clear();
12488
+ this.highlightAnnotations.clear();
12489
+ }
12490
+
12491
+ setAnnotation(el, text, corner) {
12492
+ const trimmed = text.trim();
12493
+
12494
+ if (trimmed.length === 0) {
12495
+ this.highlightAnnotations.delete(el);
12496
+ } else {
12497
+ const validatedText = trimmed.length > MAX_ANNOTATION_LENGTH ? trimmed.substring(0, MAX_ANNOTATION_LENGTH) : trimmed;
12498
+
12499
+ this.highlightAnnotations.set(el, { text: validatedText, corner });
12500
+ }
12501
+ }
12502
+
12503
+ setHighlightColor(el, color) {
12504
+ this.highlightColors.set(el, color);
12505
+ }
12506
+
12507
+ setAllHighlightColors(color) {
12508
+ this.selectedElements.forEach((el) => {
12509
+ this.highlightColors.set(el, color);
12510
+ });
12317
12511
  }
12318
12512
 
12319
12513
  _addHighlightClass(el) {
@@ -12351,7 +12545,7 @@
12351
12545
  return;
12352
12546
  }
12353
12547
 
12354
- const descriptors = Array.from(this.selectedElements).map((el) => createDescriptor(el));
12548
+ const descriptors = this._buildDescriptors();
12355
12549
  let serialized;
12356
12550
 
12357
12551
  try {
@@ -12394,7 +12588,7 @@
12394
12588
  return;
12395
12589
  }
12396
12590
 
12397
- const descriptors = Array.from(this.selectedElements).map((el) => createDescriptor(el));
12591
+ const descriptors = this._buildDescriptors();
12398
12592
  const serialized = serialize(descriptors);
12399
12593
 
12400
12594
  // eslint-disable-next-line svelte/prefer-svelte-reactivity
@@ -12414,10 +12608,59 @@
12414
12608
 
12415
12609
  window.open(url.toString(), '_blank');
12416
12610
  }
12611
+
12612
+ _buildDescriptors() {
12613
+ return Array.from(this.selectedElements).map((el) => {
12614
+ const desc = createDescriptor(el);
12615
+
12616
+ if (this.selectionMode === 'highlight') {
12617
+ const color = this.highlightColors.get(el);
12618
+
12619
+ if (color !== undefined) desc.color = color;
12620
+
12621
+ const annotation = this.highlightAnnotations.get(el);
12622
+
12623
+ if (annotation !== undefined) {
12624
+ desc.annotation = annotation.text;
12625
+ desc.annotationCorner = annotation.corner;
12626
+ }
12627
+ }
12628
+
12629
+ return desc;
12630
+ });
12631
+ }
12417
12632
  }
12418
12633
 
12419
12634
  const shareStore = new ShareStore();
12420
12635
 
12636
+ /* focus-store.svelte.ts generated by Svelte v5.46.1 */
12637
+
12638
+ class FocusStore {
12639
+ #isActive = state(false);
12640
+
12641
+ get isActive() {
12642
+ return get(this.#isActive);
12643
+ }
12644
+
12645
+ set isActive(value) {
12646
+ set(this.#isActive, value, true);
12647
+ }
12648
+
12649
+ setIsActive(isActive) {
12650
+ this.isActive = isActive;
12651
+ }
12652
+
12653
+ /**
12654
+ * Signals intent to exit focus mode.
12655
+ * Logic is handled by the service observing this state.
12656
+ */
12657
+ exit() {
12658
+ this.isActive = false;
12659
+ }
12660
+ }
12661
+
12662
+ const focusStore = new FocusStore();
12663
+
12421
12664
  enable_legacy_mode_flag();
12422
12665
 
12423
12666
  /*
@@ -12513,23 +12756,23 @@
12513
12756
  return zoom;
12514
12757
  }
12515
12758
 
12516
- var root_1$7 = from_html(`<div role="alert" aria-live="polite"> </div>`);
12517
- var root$d = from_html(`<div class="toast-container svelte-14irt8g"></div>`);
12759
+ var root_1$8 = from_html(`<div role="alert" aria-live="polite"> </div>`);
12760
+ var root$f = from_html(`<div class="toast-container svelte-14irt8g"></div>`);
12518
12761
 
12519
- const $$css$e = {
12762
+ const $$css$g = {
12520
12763
  hash: 'svelte-14irt8g',
12521
12764
  code: '.toast-container.svelte-14irt8g {position:fixed;top:20px;left:50%;transform:translateX(-50%);z-index:20000;display:flex;flex-direction:column;align-items:center;gap:10px;pointer-events:none; /* Let clicks pass through container */}.toast-item.svelte-14irt8g {background:rgba(0, 0, 0, 0.85);color:white;padding:10px 20px;border-radius:4px;font-size:14px;box-shadow:0 4px 6px rgba(0, 0, 0, 0.1);pointer-events:auto; /* Re-enable clicks on toasts */max-width:300px;text-align:center;}'
12522
12765
  };
12523
12766
 
12524
12767
  function Toast($$anchor, $$props) {
12525
12768
  push($$props, false);
12526
- append_styles$1($$anchor, $$css$e);
12769
+ append_styles$1($$anchor, $$css$g);
12527
12770
  init();
12528
12771
 
12529
- var div = root$d();
12772
+ var div = root$f();
12530
12773
 
12531
12774
  each(div, 13, () => toast.items, (t) => t.id, ($$anchor, t) => {
12532
- var div_1 = root_1$7();
12775
+ var div_1 = root_1$8();
12533
12776
  var text = child(div_1, true);
12534
12777
 
12535
12778
  reset(div_1);
@@ -12550,16 +12793,16 @@
12550
12793
  pop();
12551
12794
  }
12552
12795
 
12553
- var root$c = from_html(`<div class="floating-bar svelte-bs8cbd"><div class="mode-toggle svelte-bs8cbd"><button title="Show only selected elements">Show</button> <button title="Hide selected elements">Hide</button> <button title="Highlight selected elements">Highlight</button></div> <span class="divider svelte-bs8cbd"></span> <span class="count svelte-bs8cbd"> </span> <button class="btn clear svelte-bs8cbd">Clear</button> <button class="btn preview svelte-bs8cbd">Preview</button> <button class="btn generate svelte-bs8cbd">Copy Link</button> <button class="btn exit svelte-bs8cbd">Exit</button></div>`);
12796
+ var root$e = from_html(`<div class="floating-bar svelte-bs8cbd"><div class="mode-toggle svelte-bs8cbd"><button title="Show only selected elements">Show</button> <button title="Hide selected elements">Hide</button> <button title="Highlight selected elements">Highlight</button></div> <span class="divider svelte-bs8cbd"></span> <span class="count svelte-bs8cbd"> </span> <button class="btn clear svelte-bs8cbd">Clear</button> <button class="btn preview svelte-bs8cbd">Preview</button> <button class="btn generate svelte-bs8cbd">Copy Link</button> <button class="btn exit svelte-bs8cbd">Exit</button></div>`);
12554
12797
 
12555
- const $$css$d = {
12798
+ const $$css$f = {
12556
12799
  hash: 'svelte-bs8cbd',
12557
12800
  code: '.floating-bar.svelte-bs8cbd {position:fixed;bottom:20px;left:50%;transform:translateX(-50%);background-color:#2c2c2c;color:#f1f1f1;border-radius:8px;padding:8px 12px;box-shadow:0 8px 20px rgba(0, 0, 0, 0.4);display:grid;grid-template-columns:auto auto 1fr auto auto auto auto;align-items:center;gap:12px;z-index:99999;font-family:system-ui,\n -apple-system,\n sans-serif;font-size:14px;border:1px solid #4a4a4a;pointer-events:auto;white-space:nowrap;min-width:500px;}.mode-toggle.svelte-bs8cbd {display:flex;background:#1a1a1a;border-radius:6px;padding:2px;border:1px solid #4a4a4a;}.mode-btn.svelte-bs8cbd {background:transparent;color:#aeaeae;border:none;padding:4px 10px;border-radius:4px;cursor:pointer;font-weight:500;font-size:13px;transition:all 0.2s;}.mode-btn.svelte-bs8cbd:hover {color:#fff;}.mode-btn.active.svelte-bs8cbd {background:#4a4a4a;color:#fff;box-shadow:0 1px 3px rgba(0, 0, 0, 0.2);}.divider.svelte-bs8cbd {width:1px;height:20px;background:#4a4a4a;margin:0 4px;}.count.svelte-bs8cbd {font-weight:500;min-width:120px;text-align:center;font-size:13px;color:#ccc;}.btn.svelte-bs8cbd {background-color:#0078d4;color:white;border:none;padding:6px 12px;border-radius:5px;cursor:pointer;font-weight:500;transition:background-color 0.2s;font-size:13px;}.btn.svelte-bs8cbd:hover {background-color:#005a9e;}.btn.clear.svelte-bs8cbd {background-color:transparent;border:1px solid #5a5a5a;color:#dadada;}.btn.clear.svelte-bs8cbd:hover {background-color:#3a3a3a;color:white;}.btn.preview.svelte-bs8cbd {background-color:#333;border:1px solid #555;}.btn.preview.svelte-bs8cbd:hover {background-color:#444;}.btn.exit.svelte-bs8cbd {background-color:transparent;color:#ff6b6b;padding:6px 10px;}.btn.exit.svelte-bs8cbd:hover {background-color:rgba(255, 107, 107, 0.1);}\n\n @media (max-width: 600px) {.floating-bar.svelte-bs8cbd {display:flex;flex-wrap:wrap;min-width:unset;width:90%;max-width:400px;height:auto;padding:12px;gap:10px;bottom:30px;}.mode-toggle.svelte-bs8cbd {margin-right:auto;order:1;}.btn.exit.svelte-bs8cbd {margin-left:auto;order:2;}.divider.svelte-bs8cbd {display:none;}.count.svelte-bs8cbd {width:100%;text-align:center;order:3;padding:8px 0;border-top:1px solid #3a3a3a;border-bottom:1px solid #3a3a3a;margin:4px 0;}.btn.clear.svelte-bs8cbd,\n .btn.preview.svelte-bs8cbd,\n .btn.generate.svelte-bs8cbd {flex:1;text-align:center;font-size:12px;padding:8px 4px;order:4;}.btn.generate.svelte-bs8cbd {flex:1.5;}\n }'
12558
12801
  };
12559
12802
 
12560
12803
  function ShareToolbar($$anchor, $$props) {
12561
12804
  push($$props, false);
12562
- append_styles$1($$anchor, $$css$d);
12805
+ append_styles$1($$anchor, $$css$f);
12563
12806
 
12564
12807
  function handleClear() {
12565
12808
  shareStore.clearAllSelections();
@@ -12579,7 +12822,7 @@
12579
12822
 
12580
12823
  init();
12581
12824
 
12582
- var div = root$c();
12825
+ var div = root$e();
12583
12826
  var div_1 = child(div);
12584
12827
  var button = child(div_1);
12585
12828
 
@@ -12637,18 +12880,18 @@
12637
12880
 
12638
12881
  delegate(['click']);
12639
12882
 
12640
- var root_2$2 = from_html(`<span class="id-badge svelte-64gpkh" title="ID detection active"> </span>`);
12641
- var root_3$1 = from_html(`<button class="action-btn up svelte-64gpkh" title="Select Parent">↰</button>`);
12642
- var root_1$6 = from_html(`<div class="hover-helper svelte-64gpkh"><div class="info svelte-64gpkh"><span class="tag svelte-64gpkh"> </span> <!></div> <button> </button> <!></div>`);
12883
+ var root_2$6 = from_html(`<span class="id-badge svelte-64gpkh" title="ID detection active"> </span>`);
12884
+ var root_3$4 = from_html(`<button class="action-btn up svelte-64gpkh" title="Select Parent">↰</button>`);
12885
+ var root_1$7 = from_html(`<div class="hover-helper svelte-64gpkh"><div class="info svelte-64gpkh"><span class="tag svelte-64gpkh"> </span> <!></div> <button> </button> <!></div>`);
12643
12886
 
12644
- const $$css$c = {
12887
+ const $$css$e = {
12645
12888
  hash: 'svelte-64gpkh',
12646
12889
  code: '.hover-helper.svelte-64gpkh {position:fixed;z-index:99999;background-color:#333;color:white;padding:4px 8px;border-radius:4px;display:flex;align-items:center;gap:8px;box-shadow:0 2px 5px rgba(0, 0, 0, 0.2);font-family:monospace;pointer-events:auto;}.info.svelte-64gpkh {display:flex;flex-direction:column;align-items:flex-start;line-height:1;gap:2px;}.tag.svelte-64gpkh {font-size:12px;font-weight:bold;color:#aeaeae;}.id-badge.svelte-64gpkh {font-size:10px;color:#64d2ff;max-width:100px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}.action-btn.svelte-64gpkh {background:#555;border:none;color:white;border-radius:3px;cursor:pointer;padding:2px 6px;font-size:14px;line-height:1;display:flex;align-items:center;justify-content:center;transition:background-color 0.1s;}.action-btn.svelte-64gpkh:hover {background:#777;}.action-btn.deselect.svelte-64gpkh {background-color:#d13438;}.action-btn.deselect.svelte-64gpkh:hover {background-color:#a42628;}'
12647
12890
  };
12648
12891
 
12649
12892
  function HoverHelper($$anchor, $$props) {
12650
12893
  push($$props, true);
12651
- append_styles$1($$anchor, $$css$c);
12894
+ append_styles$1($$anchor, $$css$e);
12652
12895
 
12653
12896
  // Derived state for easier access
12654
12897
  let target = user_derived(() => shareStore.currentHoverTarget);
@@ -12747,7 +12990,7 @@
12747
12990
 
12748
12991
  {
12749
12992
  var consequent_2 = ($$anchor) => {
12750
- var div = root_1$6();
12993
+ var div = root_1$7();
12751
12994
  var div_1 = child(div);
12752
12995
  var span = child(div_1);
12753
12996
  var text = child(span, true);
@@ -12758,7 +13001,7 @@
12758
13001
 
12759
13002
  {
12760
13003
  var consequent = ($$anchor) => {
12761
- var span_1 = root_2$2();
13004
+ var span_1 = root_2$6();
12762
13005
  var text_1 = child(span_1);
12763
13006
 
12764
13007
  reset(span_1);
@@ -12785,7 +13028,7 @@
12785
13028
 
12786
13029
  {
12787
13030
  var consequent_1 = ($$anchor) => {
12788
- var button_1 = root_3$1();
13031
+ var button_1 = root_3$4();
12789
13032
 
12790
13033
  button_1.__click = handleSelectParent;
12791
13034
  append($$anchor, button_1);
@@ -12822,8 +13065,338 @@
12822
13065
 
12823
13066
  delegate(['click']);
12824
13067
 
12825
- var root_1$5 = from_html(`<div><span class="selection-label svelte-1dbf58w"> </span></div>`);
12826
- var root$b = from_html(`<div class="share-overlay-ui"><!> <!> <!></div>`);
13068
+ var root_2$5 = from_html(`<button></button>`);
13069
+ var root_1$6 = from_html(`<div class="cv-color-swatches svelte-1r78n4c" role="none"></div>`);
13070
+ var root$d = from_html(`<div class="cv-color-picker svelte-1r78n4c" role="none"><button class="cv-color-trigger svelte-1r78n4c" title="Choose highlight color" aria-label="Choose highlight color"><span class="cv-color-dot svelte-1r78n4c"></span></button> <!></div>`);
13071
+
13072
+ const $$css$d = {
13073
+ hash: 'svelte-1r78n4c',
13074
+ code: '.cv-color-picker.svelte-1r78n4c {position:fixed;transform:translateX(-50%) translateY(-100%);display:flex;flex-direction:column;align-items:center;gap:4px;pointer-events:auto;z-index:9500;\n /* Nudge down so the trigger peeks above the element edge */margin-top:8px;}.cv-color-trigger.svelte-1r78n4c {width:22px;height:16px;border-radius:100px;border:1.5px solid rgba(0, 0, 0, 0.18);background:white;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;box-shadow:0 2px 8px rgba(0, 0, 0, 0.15);transition:box-shadow 0.15s;}.cv-color-trigger.svelte-1r78n4c:hover {box-shadow:0 3px 12px rgba(0, 0, 0, 0.22);}.cv-color-dot.svelte-1r78n4c {width:10px;height:10px;border-radius:50%;display:block;border:1px solid rgba(0, 0, 0, 0.12);}.cv-color-swatches.svelte-1r78n4c {display:flex;flex-direction:row;gap:4px;background:white;border-radius:100px;padding:4px 6px;box-shadow:0 4px 16px rgba(0, 0, 0, 0.18);border:1px solid rgba(0, 0, 0, 0.1);}.cv-color-swatch.svelte-1r78n4c {width:16px;height:16px;border-radius:50%;border:2px solid transparent;cursor:pointer;padding:0;transition:transform 0.1s, border-color 0.1s;}.cv-color-swatch.svelte-1r78n4c:hover {transform:scale(1.2);border-color:rgba(0, 0, 0, 0.3);}.cv-color-swatch.active.svelte-1r78n4c {border-color:rgba(0, 0, 0, 0.5);transform:scale(1.15);}'
13075
+ };
13076
+
13077
+ function HighlightColorPicker($$anchor, $$props) {
13078
+ push($$props, true);
13079
+ append_styles$1($$anchor, $$css$d);
13080
+
13081
+ let isExpanded = state(false);
13082
+ let rect = state(proxy({ top: 0, left: 0, width: 0 }));
13083
+
13084
+ user_effect(() => {
13085
+ set(rect, $$props.element.getBoundingClientRect(), true);
13086
+
13087
+ const update = () => {
13088
+ set(rect, $$props.element.getBoundingClientRect(), true);
13089
+ };
13090
+
13091
+ window.addEventListener('scroll', update, { capture: true, passive: true });
13092
+ window.addEventListener('resize', update, { passive: true });
13093
+
13094
+ return () => {
13095
+ window.removeEventListener('scroll', update, { capture: true });
13096
+ window.removeEventListener('resize', update);
13097
+
13098
+ if (clickTimer) clearTimeout(clickTimer);
13099
+ };
13100
+ });
13101
+
13102
+ let currentColorKey = user_derived(() => shareStore.highlightColors.get($$props.element) ?? DEFAULT_COLOR_KEY);
13103
+ let currentHex = user_derived(() => HIGHLIGHT_COLORS.find((c) => c.key === get(currentColorKey))?.hex ?? HIGHLIGHT_COLORS[0].hex);
13104
+ let clickTimer = null;
13105
+
13106
+ function handleTriggerClick(e) {
13107
+ e.stopPropagation();
13108
+ set(isExpanded, !get(isExpanded));
13109
+ }
13110
+
13111
+ function handleSwatchClick(e, key) {
13112
+ e.stopPropagation();
13113
+
13114
+ if (clickTimer) return; // defer to potential dblclick
13115
+
13116
+ clickTimer = setTimeout(
13117
+ () => {
13118
+ clickTimer = null;
13119
+ shareStore.setHighlightColor($$props.element, key);
13120
+ set(isExpanded, false);
13121
+ },
13122
+ 220
13123
+ );
13124
+ }
13125
+
13126
+ function handleSwatchDblClick(e, key) {
13127
+ e.stopPropagation();
13128
+
13129
+ if (clickTimer) {
13130
+ clearTimeout(clickTimer);
13131
+ clickTimer = null;
13132
+ }
13133
+
13134
+ shareStore.setAllHighlightColors(key);
13135
+ set(isExpanded, false);
13136
+ }
13137
+
13138
+ let centerX = user_derived(() => get(rect).left + get(rect).width / 2);
13139
+ let topY = user_derived(() => get(rect).top);
13140
+ var div = root$d();
13141
+ var button = child(div);
13142
+
13143
+ button.__click = handleTriggerClick;
13144
+
13145
+ var span = child(button);
13146
+
13147
+ reset(button);
13148
+
13149
+ var node = sibling(button, 2);
13150
+
13151
+ {
13152
+ var consequent = ($$anchor) => {
13153
+ var div_1 = root_1$6();
13154
+
13155
+ each(div_1, 21, () => HIGHLIGHT_COLORS, index, ($$anchor, color) => {
13156
+ var button_1 = root_2$5();
13157
+ let classes;
13158
+
13159
+ button_1.__click = (e) => handleSwatchClick(e, get(color).key);
13160
+ button_1.__dblclick = (e) => handleSwatchDblClick(e, get(color).key);
13161
+
13162
+ template_effect(() => {
13163
+ classes = set_class(button_1, 1, 'cv-color-swatch svelte-1r78n4c', null, classes, { active: get(currentColorKey) === get(color).key });
13164
+ set_style(button_1, `background: ${get(color).hex ?? ''};`);
13165
+ set_attribute(button_1, 'title', `${get(color).label ?? ''} · dbl-click to apply to all`);
13166
+ set_attribute(button_1, 'aria-label', get(color).label);
13167
+ set_attribute(button_1, 'aria-pressed', get(currentColorKey) === get(color).key);
13168
+ });
13169
+
13170
+ append($$anchor, button_1);
13171
+ });
13172
+
13173
+ reset(div_1);
13174
+ append($$anchor, div_1);
13175
+ };
13176
+
13177
+ if_block(node, ($$render) => {
13178
+ if (get(isExpanded)) $$render(consequent);
13179
+ });
13180
+ }
13181
+
13182
+ reset(div);
13183
+
13184
+ template_effect(() => {
13185
+ set_style(div, `left: ${get(centerX) ?? ''}px; top: ${get(topY) ?? ''}px;`);
13186
+ set_attribute(button, 'aria-expanded', get(isExpanded));
13187
+ set_style(span, `background: ${get(currentHex) ?? ''};`);
13188
+ });
13189
+
13190
+ append($$anchor, div);
13191
+ pop();
13192
+ }
13193
+
13194
+ delegate(['click', 'dblclick']);
13195
+
13196
+ var root_1$5 = from_html(`<span class="cv-annotation-tab-preview svelte-1r1spmr"> </span>`);
13197
+ var root_2$4 = from_html(`<span class="cv-annotation-tab-icon svelte-1r1spmr">+ note</span>`);
13198
+ var root_4$1 = from_html(`<button> </button>`);
13199
+ var root_3$3 = from_html(`<div class="cv-annotation-panel svelte-1r1spmr" role="none"><textarea class="cv-annotation-textarea svelte-1r1spmr" placeholder="Add a note…" rows="3"></textarea> <div class="cv-annotation-footer svelte-1r1spmr"><div class="cv-corner-selector svelte-1r1spmr" role="group" aria-label="Anchor corner"></div> <span class="cv-char-counter svelte-1r1spmr"> </span></div></div>`);
13200
+ var root$c = from_html(`<div class="cv-annotation-editor svelte-1r1spmr" role="none"><button aria-label="Annotation"><!></button> <!></div>`);
13201
+
13202
+ const $$css$c = {
13203
+ hash: 'svelte-1r1spmr',
13204
+ code: '.cv-annotation-editor.svelte-1r1spmr {position:fixed;z-index:9400;pointer-events:auto;display:flex;flex-direction:column;align-items:flex-start;gap:2px;}.cv-annotation-tab.svelte-1r1spmr {height:20px;padding:0 8px;border-radius:100px;border:1.5px solid rgba(0, 0, 0, 0.18);background:white;cursor:pointer;display:flex;align-items:center;gap:4px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.15);transition:box-shadow 0.15s;max-width:160px;overflow:hidden;}.cv-annotation-tab.svelte-1r1spmr:hover {box-shadow:0 3px 12px rgba(0, 0, 0, 0.22);}.cv-annotation-tab--has-text.svelte-1r1spmr {background:#fffbe6;border-color:rgba(180, 83, 9, 0.4);}.cv-annotation-tab-icon.svelte-1r1spmr {font-size:10px;line-height:1;color:#6b7280;}.cv-annotation-tab-preview.svelte-1r1spmr {font-size:9px;font-weight:600;color:#1a1a1a;font-family:ui-sans-serif, system-ui, sans-serif;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:130px;}.cv-annotation-panel.svelte-1r1spmr {background:white;border-radius:8px;border:1px solid rgba(0, 0, 0, 0.12);box-shadow:0 4px 16px rgba(0, 0, 0, 0.18);padding:8px;width:220px;display:flex;flex-direction:column;gap:6px;}.cv-annotation-textarea.svelte-1r1spmr {width:100%;box-sizing:border-box;resize:vertical;border:1px solid rgba(0, 0, 0, 0.15);border-radius:4px;padding:5px 7px;font-size:11px;font-family:ui-sans-serif, system-ui, sans-serif;color:#1a1a1a;line-height:1.5;outline:none;min-height:56px;}.cv-annotation-textarea.svelte-1r1spmr:focus {border-color:#b45309;box-shadow:0 0 0 2px rgba(180, 83, 9, 0.15);}.cv-annotation-footer.svelte-1r1spmr {display:flex;align-items:center;justify-content:space-between;}.cv-corner-selector.svelte-1r1spmr {display:flex;gap:2px;}.cv-corner-btn.svelte-1r1spmr {width:20px;height:20px;border-radius:4px;border:1px solid rgba(0, 0, 0, 0.12);background:white;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:10px;color:#6b7280;padding:0;transition:background 0.1s, border-color 0.1s;}.cv-corner-btn.svelte-1r1spmr:hover {background:#fef3c7;border-color:#b45309;color:#1a1a1a;}.cv-corner-btn.active.svelte-1r1spmr {background:#fef3c7;border-color:#b45309;color:#92400e;font-weight:700;}.cv-char-counter.svelte-1r1spmr {font-size:9px;color:#9ca3af;font-family:ui-sans-serif, system-ui, sans-serif;font-variant-numeric:tabular-nums;}'
13205
+ };
13206
+
13207
+ function HighlightAnnotationEditor($$anchor, $$props) {
13208
+ push($$props, true);
13209
+ append_styles$1($$anchor, $$css$c);
13210
+
13211
+ let isExpanded = state(false);
13212
+ let rect = state(proxy({ top: 0, left: 0, width: 0, height: 0, bottom: 0, right: 0 }));
13213
+
13214
+ user_effect(() => {
13215
+ set(rect, $$props.element.getBoundingClientRect(), true);
13216
+
13217
+ const update = () => {
13218
+ set(rect, $$props.element.getBoundingClientRect(), true);
13219
+ };
13220
+
13221
+ window.addEventListener('scroll', update, { capture: true, passive: true });
13222
+ window.addEventListener('resize', update, { passive: true });
13223
+
13224
+ return () => {
13225
+ window.removeEventListener('scroll', update, { capture: true });
13226
+ window.removeEventListener('resize', update);
13227
+ };
13228
+ });
13229
+
13230
+ let localText = state('');
13231
+ let localCorner = state(proxy(DEFAULT_ANNOTATION_CORNER));
13232
+
13233
+ // Initialize from store when element changes (or annotation changes externally)
13234
+ user_pre_effect(() => {
13235
+ const ann = shareStore.highlightAnnotations.get($$props.element);
13236
+ const newText = ann?.text ?? '';
13237
+
13238
+ if (get(localText) !== newText) {
13239
+ set(localText, newText, true);
13240
+ }
13241
+
13242
+ // Only sync corner from store when there is a stored annotation;
13243
+ // otherwise leave the locally-chosen corner intact.
13244
+ if (ann && get(localCorner) !== ann.corner) {
13245
+ set(localCorner, ann.corner, true);
13246
+ }
13247
+ });
13248
+
13249
+ function handleInput(e) {
13250
+ const value = e.target.value;
13251
+
13252
+ set(localText, value, true);
13253
+ shareStore.setAnnotation($$props.element, value, get(localCorner));
13254
+ }
13255
+
13256
+ function setCorner(c) {
13257
+ set(localCorner, c, true);
13258
+
13259
+ if (get(localText).trim().length > 0) {
13260
+ shareStore.setAnnotation($$props.element, get(localText), c);
13261
+ }
13262
+ }
13263
+
13264
+ function handleTabClick(e) {
13265
+ e.stopPropagation();
13266
+ set(isExpanded, !get(isExpanded));
13267
+ }
13268
+
13269
+ let tabStyle = user_derived(() => {
13270
+ switch (get(localCorner)) {
13271
+ case 'tl':
13272
+ return `top: ${get(rect).top}px; left: ${get(rect).left}px; transform: translateY(-100%);`;
13273
+
13274
+ case 'tr':
13275
+ return `top: ${get(rect).top}px; left: ${get(rect).right}px; transform: translate(-100%, -100%);`;
13276
+
13277
+ case 'bl':
13278
+ return `top: ${get(rect).bottom}px; left: ${get(rect).left}px;`;
13279
+
13280
+ case 'br':
13281
+ return `top: ${get(rect).bottom}px; left: ${get(rect).right}px; transform: translateX(-100%);`;
13282
+ }
13283
+ });
13284
+
13285
+ let preview = user_derived(() => get(localText).length > 0
13286
+ ? get(localText).slice(0, ANNOTATION_PREVIEW_LENGTH) + (get(localText).length > ANNOTATION_PREVIEW_LENGTH ? '…' : '')
13287
+ : null);
13288
+
13289
+ var div = root$c();
13290
+ var button = child(div);
13291
+ let classes;
13292
+
13293
+ button.__click = handleTabClick;
13294
+
13295
+ var node = child(button);
13296
+
13297
+ {
13298
+ var consequent = ($$anchor) => {
13299
+ var span = root_1$5();
13300
+ var text = child(span, true);
13301
+
13302
+ reset(span);
13303
+ template_effect(() => set_text(text, get(preview)));
13304
+ append($$anchor, span);
13305
+ };
13306
+
13307
+ var alternate = ($$anchor) => {
13308
+ var span_1 = root_2$4();
13309
+
13310
+ append($$anchor, span_1);
13311
+ };
13312
+
13313
+ if_block(node, ($$render) => {
13314
+ if (get(preview)) $$render(consequent); else $$render(alternate, false);
13315
+ });
13316
+ }
13317
+
13318
+ reset(button);
13319
+
13320
+ var node_1 = sibling(button, 2);
13321
+
13322
+ {
13323
+ var consequent_1 = ($$anchor) => {
13324
+ var div_1 = root_3$3();
13325
+ var textarea = child(div_1);
13326
+
13327
+ remove_textarea_child(textarea);
13328
+ textarea.__input = handleInput;
13329
+
13330
+ var div_2 = sibling(textarea, 2);
13331
+ var div_3 = child(div_2);
13332
+
13333
+ each(div_3, 21, () => CORNER_ICONS, ({ key, icon }) => key, ($$anchor, $$item) => {
13334
+ let key = () => get($$item).key;
13335
+ let icon = () => get($$item).icon;
13336
+ var button_1 = root_4$1();
13337
+ let classes_1;
13338
+
13339
+ button_1.__click = (e) => {
13340
+ e.stopPropagation();
13341
+ setCorner(key());
13342
+ };
13343
+
13344
+ var text_1 = child(button_1, true);
13345
+
13346
+ reset(button_1);
13347
+
13348
+ template_effect(() => {
13349
+ classes_1 = set_class(button_1, 1, 'cv-corner-btn svelte-1r1spmr', null, classes_1, { active: get(localCorner) === key() });
13350
+ set_attribute(button_1, 'title', `Anchor to ${key() ?? ''}`);
13351
+ set_attribute(button_1, 'aria-label', `Anchor to ${key() ?? ''}`);
13352
+ set_attribute(button_1, 'aria-pressed', get(localCorner) === key());
13353
+ set_text(text_1, icon());
13354
+ });
13355
+
13356
+ append($$anchor, button_1);
13357
+ });
13358
+
13359
+ reset(div_3);
13360
+
13361
+ var span_2 = sibling(div_3, 2);
13362
+ var text_2 = child(span_2, true);
13363
+
13364
+ reset(span_2);
13365
+ reset(div_2);
13366
+ reset(div_1);
13367
+
13368
+ template_effect(() => {
13369
+ set_attribute(textarea, 'maxlength', MAX_ANNOTATION_LENGTH);
13370
+ set_value(textarea, get(localText));
13371
+ set_text(text_2, MAX_ANNOTATION_LENGTH - get(localText).length);
13372
+ });
13373
+
13374
+ append($$anchor, div_1);
13375
+ };
13376
+
13377
+ if_block(node_1, ($$render) => {
13378
+ if (get(isExpanded)) $$render(consequent_1);
13379
+ });
13380
+ }
13381
+
13382
+ reset(div);
13383
+
13384
+ template_effect(() => {
13385
+ set_style(div, get(tabStyle));
13386
+ classes = set_class(button, 1, 'cv-annotation-tab svelte-1r1spmr', null, classes, { 'cv-annotation-tab--has-text': get(localText).length > 0 });
13387
+ set_attribute(button, 'title', get(localText).length > 0 ? get(localText) : 'Add annotation');
13388
+ set_attribute(button, 'aria-expanded', get(isExpanded));
13389
+ });
13390
+
13391
+ append($$anchor, div);
13392
+ pop();
13393
+ }
13394
+
13395
+ delegate(['click', 'input']);
13396
+
13397
+ var root_2$3 = from_html(`<!> <!>`, 1);
13398
+ var root_3$2 = from_html(`<div><span class="selection-label svelte-1dbf58w"> </span></div>`);
13399
+ var root$b = from_html(`<div class="share-overlay-ui"><!> <!> <!> <!></div>`);
12827
13400
 
12828
13401
  const $$css$b = {
12829
13402
  hash: 'svelte-1dbf58w',
@@ -12875,8 +13448,8 @@
12875
13448
 
12876
13449
  const target = e.target;
12877
13450
 
12878
- // 1. If we are on the helper or toolbar, do nothing (keep current selection)
12879
- if (target.closest('.hover-helper') || target.closest('.floating-bar')) {
13451
+ // 1. If we are on the helper, toolbar, color picker, or annotation editor, do nothing
13452
+ if (target.closest('.hover-helper') || target.closest('.floating-bar') || target.closest('.cv-color-picker') || target.closest('.cv-annotation-editor')) {
12880
13453
  return;
12881
13454
  }
12882
13455
 
@@ -12942,7 +13515,7 @@
12942
13515
  // Ignore clicks on UI
12943
13516
  const target = e.target;
12944
13517
 
12945
- if (target.closest('.floating-bar') || target.closest('.hover-helper')) return;
13518
+ if (target.closest('.floating-bar') || target.closest('.hover-helper') || target.closest('.cv-color-picker') || target.closest('.cv-annotation-editor')) return;
12946
13519
 
12947
13520
  // Disable drag on touch devices
12948
13521
  if (window.matchMedia('(pointer: coarse)').matches) return;
@@ -13035,7 +13608,7 @@
13035
13608
 
13036
13609
  const target = e.target;
13037
13610
 
13038
- if (target.closest('.hover-helper') || target.closest('.floating-bar')) return;
13611
+ if (target.closest('.hover-helper') || target.closest('.floating-bar') || target.closest('.cv-color-picker') || target.closest('.cv-annotation-editor')) return;
13039
13612
 
13040
13613
  // Intercept click on document
13041
13614
  e.preventDefault();
@@ -13082,7 +13655,43 @@
13082
13655
 
13083
13656
  {
13084
13657
  var consequent = ($$anchor) => {
13085
- var div_1 = root_1$5();
13658
+ var fragment = comment();
13659
+ var node_4 = first_child(fragment);
13660
+
13661
+ each(node_4, 16, () => [...shareStore.selectedElements], (el) => el, ($$anchor, el) => {
13662
+ var fragment_1 = root_2$3();
13663
+ var node_5 = first_child(fragment_1);
13664
+
13665
+ HighlightColorPicker(node_5, {
13666
+ get element() {
13667
+ return el;
13668
+ }
13669
+ });
13670
+
13671
+ var node_6 = sibling(node_5, 2);
13672
+
13673
+ HighlightAnnotationEditor(node_6, {
13674
+ get element() {
13675
+ return el;
13676
+ }
13677
+ });
13678
+
13679
+ append($$anchor, fragment_1);
13680
+ });
13681
+
13682
+ append($$anchor, fragment);
13683
+ };
13684
+
13685
+ if_block(node_3, ($$render) => {
13686
+ if (shareStore.selectionMode === 'highlight') $$render(consequent);
13687
+ });
13688
+ }
13689
+
13690
+ var node_7 = sibling(node_3, 2);
13691
+
13692
+ {
13693
+ var consequent_1 = ($$anchor) => {
13694
+ var div_1 = root_3$2();
13086
13695
  var span = child(div_1);
13087
13696
  var text = child(span, true);
13088
13697
 
@@ -13099,59 +13708,31 @@
13099
13708
  'svelte-1dbf58w'
13100
13709
  );
13101
13710
 
13102
- set_style(div_1, `left: ${get(selectionBox).left ?? ''}px; top: ${get(selectionBox).top ?? ''}px; width: ${get(selectionBox).width ?? ''}px; height: ${get(selectionBox).height ?? ''}px;`);
13103
-
13104
- set_text(text, shareStore.selectionMode === 'hide'
13105
- ? 'Select to hide'
13106
- : shareStore.selectionMode === 'highlight' ? 'Select to highlight' : 'Select to show');
13107
- });
13108
-
13109
- append($$anchor, div_1);
13110
- };
13111
-
13112
- if_block(node_3, ($$render) => {
13113
- if (get(selectionBox)) $$render(consequent);
13114
- });
13115
- }
13116
-
13117
- reset(div);
13118
- append($$anchor, div);
13119
- pop();
13120
- }
13121
-
13122
- /* focus-store.svelte.ts generated by Svelte v5.46.1 */
13123
-
13124
- class FocusStore {
13125
- #isActive = state(false);
13126
-
13127
- get isActive() {
13128
- return get(this.#isActive);
13129
- }
13711
+ set_style(div_1, `left: ${get(selectionBox).left ?? ''}px; top: ${get(selectionBox).top ?? ''}px; width: ${get(selectionBox).width ?? ''}px; height: ${get(selectionBox).height ?? ''}px;`);
13130
13712
 
13131
- set isActive(value) {
13132
- set(this.#isActive, value, true);
13133
- }
13713
+ set_text(text, shareStore.selectionMode === 'hide'
13714
+ ? 'Select to hide'
13715
+ : shareStore.selectionMode === 'highlight' ? 'Select to highlight' : 'Select to show');
13716
+ });
13134
13717
 
13135
- setIsActive(isActive) {
13136
- this.isActive = isActive;
13137
- }
13718
+ append($$anchor, div_1);
13719
+ };
13138
13720
 
13139
- /**
13140
- * Signals intent to exit focus mode.
13141
- * Logic is handled by the service observing this state.
13142
- */
13143
- exit() {
13144
- this.isActive = false;
13721
+ if_block(node_7, ($$render) => {
13722
+ if (get(selectionBox)) $$render(consequent_1);
13723
+ });
13145
13724
  }
13146
- }
13147
13725
 
13148
- const focusStore = new FocusStore();
13726
+ reset(div);
13727
+ append($$anchor, div);
13728
+ pop();
13729
+ }
13149
13730
 
13150
13731
  var root_1$4 = from_html(`<div class="cv-focus-banner-wrapper svelte-1yqpn7e"><div id="cv-exit-focus-banner" data-cv-scroll-offset="" class="svelte-1yqpn7e"><span>You are viewing a focused selection.</span> <button class="svelte-1yqpn7e">Show Full Page</button></div></div>`);
13151
13732
 
13152
13733
  const $$css$a = {
13153
13734
  hash: 'svelte-1yqpn7e',
13154
- code: '.cv-focus-banner-wrapper.svelte-1yqpn7e {position:fixed;top:0;left:0;right:0;z-index:9000;background-color:#0078d4;box-shadow:0 2px 8px rgba(0, 0, 0, 0.2);font-family:system-ui, sans-serif;}#cv-exit-focus-banner.svelte-1yqpn7e {color:white;padding:10px 20px;display:flex;align-items:center;justify-content:center;gap:16px;}button.svelte-1yqpn7e {background:white;color:#0078d4;border:none;padding:4px 12px;border-radius:4px;cursor:pointer;font-weight:600;}button.svelte-1yqpn7e:hover {background:#f0f0f0;}'
13735
+ code: '.cv-focus-banner-wrapper.svelte-1yqpn7e {position:fixed;top:0;left:0;right:0;z-index:9000;background-color:#f3cb52;box-shadow:0 2px 8px rgba(44, 26, 14, 0.15);font-family:system-ui, sans-serif;}#cv-exit-focus-banner.svelte-1yqpn7e {color:#2c1a0e;padding:10px 20px;display:flex;align-items:center;justify-content:center;gap:16px;}button.svelte-1yqpn7e {background:#804b18;color:#fdf6e3;border:none;padding:4px 12px;border-radius:4px;cursor:pointer;font-weight:600;}button.svelte-1yqpn7e:hover {background:#c4853a;}'
13155
13736
  };
13156
13737
 
13157
13738
  function FocusBanner($$anchor, $$props) {
@@ -13395,7 +13976,7 @@
13395
13976
  }
13396
13977
 
13397
13978
  checkAndShow() {
13398
- if (!this.persistence.isIntroSeen()) {
13979
+ if (!this.persistence.getItem('cv-intro-shown')) {
13399
13980
  setTimeout(
13400
13981
  () => {
13401
13982
  this.showCallout = true;
@@ -13409,7 +13990,7 @@
13409
13990
  dismiss() {
13410
13991
  this.showCallout = false;
13411
13992
  this.showPulse = false;
13412
- this.persistence.markIntroSeen();
13993
+ this.persistence.setItem('cv-intro-shown', 'true');
13413
13994
  }
13414
13995
  }
13415
13996
 
@@ -13417,26 +13998,23 @@
13417
13998
 
13418
13999
  const $$css$9 = {
13419
14000
  hash: 'svelte-1vlfixd',
13420
- code: '\n /* Root should allow clicks to pass through to the page unless hitting checking/interactive element */.cv-widget-root {position:fixed;top:0;left:0;width:0;height:0;z-index:9999;pointer-events:none; /* Crucial: Allow clicks to pass through */\n\n /* Light Theme Defaults */--cv-bg: white;--cv-text: rgba(0, 0, 0, 0.9);--cv-text-secondary: rgba(0, 0, 0, 0.6);--cv-border: rgba(0, 0, 0, 0.1);--cv-bg-hover: rgba(0, 0, 0, 0.05);--cv-primary: #3e84f4;--cv-primary-hover: #2563eb;--cv-danger: #dc2626;--cv-danger-bg: rgba(220, 38, 38, 0.1);--cv-shadow: rgba(0, 0, 0, 0.25);--cv-input-bg: white;--cv-input-border: rgba(0, 0, 0, 0.15);--cv-switch-bg: rgba(0, 0, 0, 0.1);--cv-switch-knob: white;--cv-modal-icon-bg: rgba(0, 0, 0, 0.08);--cv-icon-bg: rgba(255, 255, 255, 0.92);--cv-icon-color: rgba(0, 0, 0, 0.9);--cv-focus-ring: rgba(62, 132, 244, 0.2);--cv-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.1);font-family:inherit; /* Inherit font from host */}\n\n /* But interactive children need pointer-events back */.cv-widget-root > * {pointer-events:auto;}\n\n /* Exception: ShareOverlay manages its own pointer events */.cv-widget-root .cv-share-overlay {pointer-events:none; /* Overlay often passes clicks until specialized handles active */}.cv-widget-root[data-theme=\'dark\'] {\n /* Dark Theme Overrides */--cv-bg: #101722;--cv-text: #e2e8f0;--cv-text-secondary: rgba(255, 255, 255, 0.6);--cv-border: rgba(255, 255, 255, 0.1);--cv-bg-hover: rgba(255, 255, 255, 0.05);--cv-primary: #3e84f4;--cv-primary-hover: #60a5fa;--cv-danger: #f87171;--cv-danger-bg: rgba(248, 113, 113, 0.1);--cv-shadow: rgba(0, 0, 0, 0.5);--cv-input-bg: #1e293b;--cv-input-border: rgba(255, 255, 255, 0.1);--cv-switch-bg: rgba(255, 255, 255, 0.1);--cv-switch-knob: #e2e8f0;--cv-modal-icon-bg: rgba(255, 255, 255, 0.08);--cv-icon-bg: #1e293b;--cv-icon-color: #e2e8f0;--cv-focus-ring: rgba(62, 132, 244, 0.5);--cv-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.5);}.cv-hidden {display:none !important;}'
14001
+ code: '\n /* Root should allow clicks to pass through to the page unless hitting checking/interactive element */.cv-widget-root {position:fixed;top:0;left:0;width:0;height:0;z-index:9999;pointer-events:none; /* Crucial: Allow clicks to pass through */\n\n /* Light Theme Defaults */--cv-bg: white;--cv-text: rgba(0, 0, 0, 0.9);--cv-text-secondary: rgba(0, 0, 0, 0.6);--cv-border: rgba(0, 0, 0, 0.1);--cv-bg-hover: rgba(0, 0, 0, 0.05);--cv-primary: #3e84f4;--cv-primary-hover: #2563eb;--cv-danger: #dc2626;--cv-danger-bg: rgba(220, 38, 38, 0.1);--cv-shadow: rgba(0, 0, 0, 0.25);--cv-input-bg: white;--cv-input-border: rgba(0, 0, 0, 0.15);--cv-switch-bg: rgba(0, 0, 0, 0.1);--cv-switch-knob: white;--cv-modal-icon-bg: rgba(0, 0, 0, 0.08);--cv-icon-bg: rgba(255, 255, 255, 0.92);--cv-icon-color: rgba(0, 0, 0, 0.9);--cv-focus-ring: rgba(62, 132, 244, 0.2);--cv-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.1);--cv-modal-radius: 0.75rem;--cv-card-radius: 0.5rem;--cv-section-label-transform: uppercase;font-family:inherit; /* Inherit font from host */}\n\n /* But interactive children need pointer-events back */.cv-widget-root > * {pointer-events:auto;}\n\n /* Exception: ShareOverlay manages its own pointer events */.cv-widget-root .cv-share-overlay {pointer-events:none; /* Overlay often passes clicks until specialized handles active */}.cv-widget-root[data-theme=\'dark\'] {\n /* Dark Theme Overrides */--cv-bg: #101722;--cv-text: #e2e8f0;--cv-text-secondary: rgba(255, 255, 255, 0.6);--cv-border: rgba(255, 255, 255, 0.1);--cv-bg-hover: rgba(255, 255, 255, 0.05);--cv-primary: #3e84f4;--cv-primary-hover: #60a5fa;--cv-danger: #f87171;--cv-danger-bg: rgba(248, 113, 113, 0.1);--cv-shadow: rgba(0, 0, 0, 0.5);--cv-input-bg: #1e293b;--cv-input-border: rgba(255, 255, 255, 0.1);--cv-switch-bg: rgba(255, 255, 255, 0.1);--cv-switch-knob: #e2e8f0;--cv-modal-icon-bg: rgba(255, 255, 255, 0.08);--cv-icon-bg: #1e293b;--cv-icon-color: #e2e8f0;--cv-focus-ring: rgba(62, 132, 244, 0.5);--cv-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.5);--cv-modal-radius: 0.75rem;--cv-card-radius: 0.5rem;--cv-section-label-transform: uppercase;}.cv-hidden {display:none !important;}'
13421
14002
  };
13422
14003
 
13423
14004
  function UIRoot($$anchor, $$props) {
13424
14005
  push($$props, true);
13425
14006
  append_styles$1($$anchor, $$css$9);
13426
14007
 
14008
+ const { persistenceManager, resetToDefault } = getContext(RUNTIME_CALLBACKS_CTX);
14009
+ const iconSettingsStore = getContext(ICON_SETTINGS_CTX);
14010
+
13427
14011
  // --- Derived State ---
13428
14012
  const storeConfig = user_derived(() => activeStateStore.config);
13429
14013
 
13430
- const settingsEnabled = user_derived(() => $$props.options.settingsEnabled ?? true);
14014
+ const settingsEnabled = user_derived(() => $$props.options.settingsEnabled);
13431
14015
 
13432
14016
  // --- Services ---
13433
- const introManager = new IntroManager(
13434
- {
13435
- isIntroSeen: () => $$props.callbacks.isIntroSeen(),
13436
- markIntroSeen: () => $$props.callbacks.markIntroSeen()
13437
- },
13438
- () => $$props.options.callout
13439
- );
14017
+ const introManager = new IntroManager(persistenceManager, () => $$props.options.callout);
13440
14018
 
13441
14019
  const router = new UrlActionRouter({
13442
14020
  onOpenModal: openModal,
@@ -13447,9 +14025,6 @@
13447
14025
  // --- UI State ---
13448
14026
  let isModalOpen = state(false);
13449
14027
 
13450
- let isResetting = state(false);
13451
- let settingsIcon = state(void 0);
13452
-
13453
14028
  // --- Computed Props ---
13454
14029
  // Share Configuration
13455
14030
  const shareExclusions = user_derived(() => get(storeConfig).shareExclusions || {});
@@ -13486,28 +14061,20 @@
13486
14061
  }
13487
14062
 
13488
14063
  function handleReset() {
13489
- set(isResetting, true);
13490
- $$props.callbacks.resetToDefault();
13491
- get(settingsIcon)?.resetPosition();
14064
+ resetToDefault();
14065
+ iconSettingsStore.resetPositionAndCollapseState();
13492
14066
  showToast('Settings reset to default');
13493
-
13494
- setTimeout(
13495
- () => {
13496
- set(isResetting, false);
13497
- get(settingsIcon)?.resetPosition();
13498
- },
13499
- 600
13500
- );
13501
14067
  }
13502
14068
 
13503
14069
  function handleStartShare(mode = 'show') {
13504
14070
  closeModal();
14071
+ focusStore.exit();
13505
14072
  shareStore.setSelectionMode(mode);
13506
14073
  shareStore.toggleActive(true);
13507
14074
  }
13508
14075
 
13509
- // --- Settings Visibility ---
13510
- const shouldRenderSettings = user_derived(() => get(settingsEnabled) && (derivedStore.hasMenuOptions || uiStore.uiOptions.showTabGroups || get(isModalOpen)));
14076
+ // --- Icon Visibility ---
14077
+ const shouldShowIcon = user_derived(() => get(settingsEnabled) && !iconSettingsStore.isDismissed && (derivedStore.hasMenuOptions || uiStore.uiOptions.showTabGroups || get(isModalOpen)));
13511
14078
 
13512
14079
  var div = root$a();
13513
14080
  var node = child(div);
@@ -13589,58 +14156,42 @@
13589
14156
  let $2 = user_derived(() => $$props.options.icon?.opacity);
13590
14157
  let $3 = user_derived(() => $$props.options.icon?.scale);
13591
14158
 
13592
- bind_this(
13593
- SettingsIcon($$anchor, {
13594
- get position() {
13595
- return $$props.options.icon.position;
13596
- },
13597
-
13598
- get title() {
13599
- return uiStore.uiOptions.title;
13600
- },
13601
-
13602
- get pulse() {
13603
- return introManager.showPulse;
13604
- },
13605
-
13606
- onclick: openModal,
14159
+ SettingsIcon($$anchor, {
14160
+ get position() {
14161
+ return $$props.options.icon.position;
14162
+ },
13607
14163
 
13608
- get iconColor() {
13609
- return get($0);
13610
- },
14164
+ get title() {
14165
+ return uiStore.uiOptions.title;
14166
+ },
13611
14167
 
13612
- get backgroundColor() {
13613
- return get($1);
13614
- },
14168
+ get pulse() {
14169
+ return introManager.showPulse;
14170
+ },
13615
14171
 
13616
- get opacity() {
13617
- return get($2);
13618
- },
14172
+ onclick: openModal,
13619
14173
 
13620
- get scale() {
13621
- return get($3);
13622
- },
14174
+ get iconColor() {
14175
+ return get($0);
14176
+ },
13623
14177
 
13624
- get getIconPosition() {
13625
- return $$props.callbacks.getIconPosition;
13626
- },
14178
+ get backgroundColor() {
14179
+ return get($1);
14180
+ },
13627
14181
 
13628
- get saveIconPosition() {
13629
- return $$props.callbacks.saveIconPosition;
13630
- },
14182
+ get opacity() {
14183
+ return get($2);
14184
+ },
13631
14185
 
13632
- get clearIconPosition() {
13633
- return $$props.callbacks.clearIconPosition;
13634
- }
13635
- }),
13636
- ($$value) => set(settingsIcon, $$value, true),
13637
- () => get(settingsIcon)
13638
- );
14186
+ get scale() {
14187
+ return get($3);
14188
+ }
14189
+ });
13639
14190
  }
13640
14191
  };
13641
14192
 
13642
14193
  if_block(node_4, ($$render) => {
13643
- if (get(shouldRenderSettings) && $$props.options.icon.show) $$render(consequent_2);
14194
+ if (get(shouldShowIcon) && $$props.options.icon.show) $$render(consequent_2);
13644
14195
  });
13645
14196
  }
13646
14197
 
@@ -13649,10 +14200,6 @@
13649
14200
  {
13650
14201
  var consequent_3 = ($$anchor) => {
13651
14202
  Modal($$anchor, {
13652
- get isResetting() {
13653
- return get(isResetting);
13654
- },
13655
-
13656
14203
  onclose: closeModal,
13657
14204
  onreset: handleReset,
13658
14205
  onstartShare: handleStartShare
@@ -13678,7 +14225,7 @@
13678
14225
  this.options = {
13679
14226
  callbacks: options.callbacks,
13680
14227
  container: options.container || document.body,
13681
- settingsEnabled: options.settingsEnabled ?? true,
14228
+ settingsEnabled: options.settingsEnabled ?? false,
13682
14229
  theme: options.panel?.theme || 'light',
13683
14230
  callout: {
13684
14231
  show: options.callout?.show ?? false,
@@ -13704,13 +14251,17 @@
13704
14251
  if (this.app) {
13705
14252
  return;
13706
14253
  }
14254
+ // Map context dependency injection directly into app root
14255
+ const rootContext = new Map();
14256
+ rootContext.set(ICON_SETTINGS_CTX, this.options.callbacks.iconSettings);
14257
+ rootContext.set(RUNTIME_CALLBACKS_CTX, this.options.callbacks);
13707
14258
  // Mount Svelte App using Svelte 5 API
13708
14259
  this.app = mount(UIRoot, {
13709
14260
  target: this.options.container,
13710
14261
  props: {
13711
- callbacks: this.options.callbacks,
13712
14262
  options: this.options,
13713
14263
  },
14264
+ context: rootContext,
13714
14265
  });
13715
14266
  }
13716
14267
  /**
@@ -13727,14 +14278,11 @@
13727
14278
  * Initializes the UI manager (settings and share UI) using the provided config.
13728
14279
  */
13729
14280
  function initUIManager(runtime, config) {
13730
- const settingsEnabled = config.settings?.enabled !== false;
14281
+ const settingsEnabled = config.settings?.enabled === true;
13731
14282
  const callbacks = {
13732
14283
  resetToDefault: () => runtime.resetToDefault(),
13733
- getIconPosition: () => runtime.getIconPosition(),
13734
- saveIconPosition: (offset) => runtime.saveIconPosition(offset),
13735
- clearIconPosition: () => runtime.clearIconPosition(),
13736
- isIntroSeen: () => runtime.isIntroSeen(),
13737
- markIntroSeen: () => runtime.markIntroSeen(),
14284
+ iconSettings: runtime.iconSettingsStore,
14285
+ persistenceManager: runtime.persistenceManager,
13738
14286
  };
13739
14287
  const uiManager = new CustardUIManager({
13740
14288
  callbacks,
@@ -14179,12 +14727,16 @@
14179
14727
  return groups;
14180
14728
  }
14181
14729
 
14182
- var root_1$3 = from_html(`<div class="cv-highlight-box svelte-1mz0neo"></div> <div> </div>`, 1);
14730
+ var root_2$2 = from_html(`<button aria-label="Previous highlight">↑</button> <button aria-label="Next highlight">↓</button>`, 1);
14731
+ var root_4 = from_html(`<span class="cv-annotation-text svelte-1mz0neo"> </span>`);
14732
+ var root_5 = from_html(`<span class="cv-annotation-text svelte-1mz0neo"> </span>`);
14733
+ var root_3$1 = from_html(`<button><!></button>`);
14734
+ var root_1$3 = from_html(`<div class="cv-highlight-group svelte-1mz0neo"><div class="cv-highlight-marker svelte-1mz0neo"></div> <!> <div class="cv-highlight-pill svelte-1mz0neo"><a href="https://custardui.js.org" target="_blank" rel="noopener noreferrer" class="svelte-1mz0neo">CustardUI highlight↗</a></div> <!></div>`);
14183
14735
  var root$8 = from_html(`<div class="cv-highlight-overlay svelte-1mz0neo"></div>`);
14184
14736
 
14185
14737
  const $$css$7 = {
14186
14738
  hash: 'svelte-1mz0neo',
14187
- code: '.cv-highlight-overlay.svelte-1mz0neo {position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:8000;overflow:visible;}.cv-highlight-box.svelte-1mz0neo {position:absolute;\n /* Slightly thicker border looks more like a marker stroke */border:5px solid #d13438;box-sizing:border-box;\n\n /* Top-Left: 5px (Sharp)\n Top-Right: 80px\n Bottom-Right: 40px\n Bottom-Left: 100px\n (Separated by the slash for organic asymmetry)\n Horizontal Radius / Vertical Radius\n */border-radius:200px 15px 225px 15px / 15px 225px 15px 255px;\n\n /* A subtle transform to make it look slightly tilted/imperfect */transform:rotate(-0.5deg);\n\n /* Balanced shadows from before, but adjusted for the wobble */box-shadow:0 6px 15px rgba(0, 0, 0, 0.13),\n /* The inset shadow now follows the wobbly border-radius */ inset 0 0 8px 1px\n rgba(0, 0, 0, 0.12);pointer-events:none;\n /* Smoother rendering for the wobbled edges */backface-visibility:hidden;opacity:0.92;}.cv-highlight-arrow.svelte-1mz0neo {position:absolute;font-size:35px; /* Slightly larger for the "marker" feel */color:#d13438;font-weight:bold;width:40px;height:40px;line-height:40px;text-align:center;\n\n /* Hand-drawn style for the arrow: \n 1. Slight tilt to match the box\n 2. Multi-layer drop shadow to match the box\'s elevation \n */transform:rotate(3deg);filter:drop-shadow(2px 4px 6px rgba(0, 0, 0, 0.15)) drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));}\n\n /* Animations set to run 4 times and stop (forwards) */.cv-highlight-arrow.left.svelte-1mz0neo {\n animation: svelte-1mz0neo-floatArrowLeft 1.5s 4 forwards;}.cv-highlight-arrow.right.svelte-1mz0neo {\n animation: svelte-1mz0neo-floatArrowRight 1.5s 4 forwards;}.cv-highlight-arrow.top.svelte-1mz0neo {\n animation: svelte-1mz0neo-floatArrowTop 1.5s 4 forwards;}.cv-highlight-arrow.bottom.svelte-1mz0neo {\n animation: svelte-1mz0neo-floatArrowBottom 1.5s 4 forwards;}\n\n @keyframes svelte-1mz0neo-floatArrowLeft {\n 0%,\n 100% {\n transform: translateX(0);\n }\n 50% {\n transform: translateX(-10px);\n }\n }\n @keyframes svelte-1mz0neo-floatArrowRight {\n 0%,\n 100% {\n transform: translateX(0);\n }\n 50% {\n transform: translateX(10px);\n }\n }\n @keyframes svelte-1mz0neo-floatArrowTop {\n 0%,\n 100% {\n transform: translate(-50%, 0);\n }\n 50% {\n transform: translate(-50%, -10px);\n }\n }\n @keyframes svelte-1mz0neo-floatArrowBottom {\n 0%,\n 100% {\n transform: translate(-50%, 0);\n }\n 50% {\n transform: translate(-50%, 10px);\n }\n }'
14739
+ code: '.cv-highlight-overlay.svelte-1mz0neo {position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:8000;}.cv-highlight-group.svelte-1mz0neo {position:absolute;pointer-events:none;}.cv-highlight-marker.svelte-1mz0neo {position:absolute;inset:0;pointer-events:none;\n \n /* Marker Style */border:3.5px solid var(--cv-highlight-color);border-radius:200px 15px 225px 15px / 15px 225px 15px 255px;transform:rotate(-0.5deg);\n \n /* 3D INTERNAL VOLUME:\n Adds depth to the yellow border itself so it looks rounded.\n */box-shadow:inset 0 1px 2px rgba(129, 73, 25, 0.2),\n inset 0 -1px 1px rgba(255, 255, 255, 0.7);\n\n /* 2A-3 DOUBLE LIGHT PROJECTION:\n Stacks multiple drop-shadows to cast into the box interior.\n */filter:/* Sharp contact shadow for grounding */\n drop-shadow(0 2px 2px rgba(44, 26, 14, 0.15)) \n /* Light Source A: Casts shadow from top-left to bottom-right */\n drop-shadow(-8px 12px 10px rgba(44, 26, 14, 0.12))\n /* Light Source B: Casts shadow from top-right to bottom-left */\n drop-shadow(8px 12px 10px rgba(44, 26, 14, 0.12));\n \n animation: svelte-1mz0neo-highlightFadeIn 0.3s ease-out forwards;}.cv-nav-arrow.svelte-1mz0neo {position:absolute;z-index:10;right:-5px;pointer-events:auto;width:14px;height:14px;border-radius:100px;border:1px solid var(--cv-highlight-color);background:white;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:7px;color:#814919;font-weight:700;font-family:ui-sans-serif, system-ui, sans-serif;line-height:1;padding:0;box-shadow:0 4px 12px rgba(44, 26, 14, 0.15);opacity:0.7;}.cv-nav-arrow.svelte-1mz0neo:hover {opacity:1;}.cv-nav-arrow--up.svelte-1mz0neo {top:0px;}.cv-nav-arrow--down.svelte-1mz0neo {bottom:0px;}.cv-nav-arrow--hidden.svelte-1mz0neo {visibility:hidden;pointer-events:none;}.cv-highlight-pill.svelte-1mz0neo {position:absolute;z-index:10;bottom:-7px;right:14px;background:white;height:14px;padding:0 8px;display:flex;align-items:center;border-radius:100px;border:1px solid var(--cv-highlight-color);pointer-events:auto;white-space:nowrap;\n \n /* Stronger shadow to match the frame\'s new altitude */box-shadow:0 4px 12px rgba(44, 26, 14, 0.15);}.cv-highlight-pill.svelte-1mz0neo a:where(.svelte-1mz0neo) {font-size:8px;font-weight:700;color:#814919;text-decoration:none;font-family:ui-sans-serif, system-ui, sans-serif;text-transform:uppercase;letter-spacing:0.04em;line-height:1;}.cv-highlight-pill.svelte-1mz0neo:hover a:where(.svelte-1mz0neo) {opacity:0.8;}.cv-annotation-badge.svelte-1mz0neo {position:absolute;z-index:10;pointer-events:auto;max-width:180px;background:white;border:1.5px solid var(--cv-highlight-color);border-radius:6px;padding:4px 7px;cursor:pointer;box-shadow:0 2px 8px rgba(44, 26, 14, 0.15);font-family:ui-sans-serif, system-ui, sans-serif;text-align:left;}.cv-annotation-badge--expanded.svelte-1mz0neo {max-width:260px;z-index:20;}.cv-annotation-text.svelte-1mz0neo {display:block;font-size:9px;font-weight:600;color:#1a1a1a;line-height:1.4;word-break:break-word;white-space:pre-wrap;}\n\n @keyframes svelte-1mz0neo-highlightFadeIn {\n from { \n opacity: 0; \n transform: scale(0.98) rotate(-1deg); \n }\n to { \n opacity: 1; \n transform: scale(1) rotate(-0.5deg); \n }\n }'
14188
14740
  };
14189
14741
 
14190
14742
  function HighlightOverlay($$anchor, $$props) {
@@ -14192,77 +14744,147 @@
14192
14744
  append_styles$1($$anchor, $$css$7);
14193
14745
 
14194
14746
  let rects = user_derived(() => $$props.box.rects);
14747
+ let expandedSet = new SvelteSet();
14195
14748
 
14196
- function getArrowClass(rect) {
14197
- const viewportWidth = window.innerWidth;
14198
- const rectLeftViewport = rect.left - (window.pageXOffset || document.documentElement.scrollLeft);
14749
+ function getColorHex(rect) {
14750
+ const key = rect.color ?? DEFAULT_COLOR_KEY;
14199
14751
 
14200
- if (rectLeftViewport >= 50) return 'left';
14201
- if (viewportWidth - (rectLeftViewport + rect.width) >= 50) return 'right';
14202
- if (rect.top - (window.pageYOffset || document.documentElement.scrollTop) >= 50) return 'top';
14203
-
14204
- return 'bottom';
14752
+ return HIGHLIGHT_COLORS.find((c) => c.key === key)?.hex ?? HIGHLIGHT_COLORS[0].hex;
14205
14753
  }
14206
14754
 
14207
- function getArrowStyle(rect, direction) {
14208
- let style = '';
14755
+ function scrollToRect(rect) {
14756
+ rect.element.scrollIntoView({ behavior: 'smooth', block: 'center' });
14757
+ }
14209
14758
 
14210
- if (direction === 'left') {
14211
- style = `top: ${rect.top}px; left: ${rect.left - 40}px;`;
14212
- } else if (direction === 'right') {
14213
- style = `top: ${rect.top}px; left: ${rect.left + rect.width + 10}px;`;
14214
- } else if (direction === 'top') {
14215
- style = `top: ${rect.top - 40}px; left: ${rect.left + rect.width / 2 - 15}px;`;
14759
+ function toggleBadge(el) {
14760
+ if (expandedSet.has(el)) {
14761
+ expandedSet.delete(el);
14216
14762
  } else {
14217
- style = `top: ${rect.top + rect.height + 10}px; left: ${rect.left + rect.width / 2 - 15}px;`;
14763
+ expandedSet.add(el);
14218
14764
  }
14219
-
14220
- return style;
14221
14765
  }
14222
14766
 
14223
- function getArrowSymbol(direction) {
14224
- switch (direction) {
14225
- case 'left':
14226
- return '';
14767
+ function getBadgeStyle(corner) {
14768
+ switch (corner) {
14769
+ case 'tl':
14770
+ return 'top: 6px; left: 6px;';
14227
14771
 
14228
- case 'right':
14229
- return '';
14772
+ case 'tr':
14773
+ return 'top: 6px; right: 6px;';
14230
14774
 
14231
- case 'top':
14232
- return '';
14775
+ case 'bl':
14776
+ return 'bottom: 6px; left: 6px;';
14233
14777
 
14234
- case 'bottom':
14235
- return '';
14778
+ case 'br':
14779
+ return 'bottom: 6px; right: 6px;';
14236
14780
  }
14237
-
14238
- return '';
14239
14781
  }
14240
14782
 
14241
14783
  var div = root$8();
14242
14784
 
14243
- each(div, 21, () => get(rects), (rect) => `${rect.top}-${rect.left}-${rect.width}-${rect.height}`, ($$anchor, rect) => {
14244
- const dir = user_derived(() => getArrowClass(get(rect)));
14245
- var fragment = root_1$3();
14246
- var div_1 = first_child(fragment);
14247
- var div_2 = sibling(div_1, 2);
14248
- var text = child(div_2, true);
14785
+ each(div, 23, () => get(rects), (rect) => rect.element, ($$anchor, rect, i) => {
14786
+ var div_1 = root_1$3();
14787
+ var node = sibling(child(div_1), 2);
14788
+
14789
+ {
14790
+ var consequent = ($$anchor) => {
14791
+ var fragment = root_2$2();
14792
+ var button = first_child(fragment);
14793
+ let classes;
14794
+
14795
+ button.__click = () => scrollToRect(get(rects)[get(i) - 1]);
14249
14796
 
14250
- reset(div_2);
14797
+ var button_1 = sibling(button, 2);
14798
+ let classes_1;
14251
14799
 
14252
- template_effect(
14253
- ($0, $1) => {
14254
- set_style(div_1, `top: ${get(rect).top ?? ''}px; left: ${get(rect).left ?? ''}px; width: ${get(rect).width ?? ''}px; height: ${get(rect).height ?? ''}px;`);
14255
- set_class(div_2, 1, `cv-highlight-arrow ${get(dir) ?? ''}`, 'svelte-1mz0neo');
14256
- set_style(div_2, $0);
14257
- set_text(text, $1);
14258
- },
14259
- [
14260
- () => getArrowStyle(get(rect), get(dir)),
14261
- () => getArrowSymbol(get(dir))
14262
- ]
14263
- );
14800
+ button_1.__click = () => scrollToRect(get(rects)[get(i) + 1]);
14801
+
14802
+ template_effect(() => {
14803
+ classes = set_class(button, 1, 'cv-nav-arrow cv-nav-arrow--up svelte-1mz0neo', null, classes, { 'cv-nav-arrow--hidden': get(i) === 0 });
14804
+ classes_1 = set_class(button_1, 1, 'cv-nav-arrow cv-nav-arrow--down svelte-1mz0neo', null, classes_1, { 'cv-nav-arrow--hidden': get(i) === get(rects).length - 1 });
14805
+ });
14806
+
14807
+ append($$anchor, fragment);
14808
+ };
14809
+
14810
+ if_block(node, ($$render) => {
14811
+ if (get(rects).length > 1) $$render(consequent);
14812
+ });
14813
+ }
14814
+
14815
+ var node_1 = sibling(node, 4);
14816
+
14817
+ {
14818
+ var consequent_2 = ($$anchor) => {
14819
+ var button_2 = root_3$1();
14820
+ let classes_2;
14821
+
14822
+ button_2.__click = (e) => {
14823
+ e.stopPropagation();
14824
+ toggleBadge(get(rect).element);
14825
+ };
14826
+
14827
+ var node_2 = child(button_2);
14828
+
14829
+ {
14830
+ var consequent_1 = ($$anchor) => {
14831
+ var span = root_4();
14832
+ var text = child(span, true);
14833
+
14834
+ reset(span);
14835
+ template_effect(() => set_text(text, get(rect).annotation));
14836
+ append($$anchor, span);
14837
+ };
14838
+
14839
+ var alternate = ($$anchor) => {
14840
+ var span_1 = root_5();
14841
+ var text_1 = child(span_1, true);
14842
+
14843
+ reset(span_1);
14844
+
14845
+ template_effect(($0) => set_text(text_1, $0), [
14846
+ () => get(rect).annotation.length > ANNOTATION_PREVIEW_LENGTH
14847
+ ? get(rect).annotation.slice(0, ANNOTATION_PREVIEW_LENGTH) + '…'
14848
+ : get(rect).annotation
14849
+ ]);
14850
+
14851
+ append($$anchor, span_1);
14852
+ };
14853
+
14854
+ if_block(node_2, ($$render) => {
14855
+ if (expandedSet.has(get(rect).element)) $$render(consequent_1); else $$render(alternate, false);
14856
+ });
14857
+ }
14264
14858
 
14265
- append($$anchor, fragment);
14859
+ reset(button_2);
14860
+
14861
+ template_effect(
14862
+ ($0, $1, $2) => {
14863
+ classes_2 = set_class(button_2, 1, 'cv-annotation-badge svelte-1mz0neo', null, classes_2, $0);
14864
+ set_style(button_2, $1);
14865
+ set_attribute(button_2, 'aria-label', $2);
14866
+ },
14867
+ [
14868
+ () => ({
14869
+ 'cv-annotation-badge--expanded': expandedSet.has(get(rect).element)
14870
+ }),
14871
+
14872
+ () => getBadgeStyle(get(rect).annotationCorner ?? DEFAULT_ANNOTATION_CORNER),
14873
+ () => expandedSet.has(get(rect).element) ? 'Collapse annotation' : 'Expand annotation'
14874
+ ]
14875
+ );
14876
+
14877
+ append($$anchor, button_2);
14878
+ };
14879
+
14880
+ if_block(node_1, ($$render) => {
14881
+ if (get(rect).annotation) $$render(consequent_2);
14882
+ });
14883
+ }
14884
+
14885
+ reset(div_1);
14886
+ template_effect(($0) => set_style(div_1, `top: ${get(rect).top ?? ''}px; left: ${get(rect).left ?? ''}px; width: ${get(rect).width ?? ''}px; height: ${get(rect).height ?? ''}px; --cv-highlight-color: ${$0 ?? ''};`), [() => getColorHex(get(rect))]);
14887
+ append($$anchor, div_1);
14266
14888
  });
14267
14889
 
14268
14890
  reset(div);
@@ -14270,6 +14892,8 @@
14270
14892
  pop();
14271
14893
  }
14272
14894
 
14895
+ delegate(['click']);
14896
+
14273
14897
  /**
14274
14898
  * Groups elements by their parent.
14275
14899
  * Returns a Map where keys are parent elements and values are lists of child elements.
@@ -14293,7 +14917,7 @@
14293
14917
  * Abstracted to allow testing without real DOM/layout.
14294
14918
  * @param getScroll Callback to retrieve current scroll position {scrollTop, scrollLeft}.
14295
14919
  */
14296
- function calculateMergedRects(groups, getRect, getScroll) {
14920
+ function calculateMergedRects(groups, getRect, getScroll, colorMap, annotationMap) {
14297
14921
  const mergedRects = [];
14298
14922
  const { scrollTop, scrollLeft } = getScroll();
14299
14923
  // Iterate groups to ensure strict adjacency
@@ -14302,7 +14926,7 @@
14302
14926
  continue;
14303
14927
  // Optimization if only 1 child, no need to scan parent
14304
14928
  if (siblingsInGroup.length === 1) {
14305
- addMergedRect(mergedRects, siblingsInGroup, getRect, scrollTop, scrollLeft);
14929
+ addMergedRect(mergedRects, siblingsInGroup, getRect, scrollTop, scrollLeft, colorMap, annotationMap);
14306
14930
  continue;
14307
14931
  }
14308
14932
  // O(1) lookup
@@ -14313,24 +14937,41 @@
14313
14937
  const children = Array.from(parent.children);
14314
14938
  for (const child of children) {
14315
14939
  if (child instanceof HTMLElement && siblingsSet.has(child)) {
14940
+ // Flush if metadata differs from batch anchor
14941
+ if (currentBatch.length > 0 &&
14942
+ !sameMetadata(currentBatch[0], child, colorMap, annotationMap)) {
14943
+ addMergedRect(mergedRects, currentBatch, getRect, scrollTop, scrollLeft, colorMap, annotationMap);
14944
+ currentBatch = [];
14945
+ }
14316
14946
  currentBatch.push(child);
14317
14947
  }
14318
14948
  else {
14319
14949
  // Break in continuity
14320
14950
  if (currentBatch.length > 0) {
14321
- addMergedRect(mergedRects, currentBatch, getRect, scrollTop, scrollLeft);
14951
+ addMergedRect(mergedRects, currentBatch, getRect, scrollTop, scrollLeft, colorMap, annotationMap);
14322
14952
  currentBatch = [];
14323
14953
  }
14324
14954
  }
14325
14955
  }
14326
14956
  // Finalize last batch
14327
14957
  if (currentBatch.length > 0) {
14328
- addMergedRect(mergedRects, currentBatch, getRect, scrollTop, scrollLeft);
14958
+ addMergedRect(mergedRects, currentBatch, getRect, scrollTop, scrollLeft, colorMap, annotationMap);
14329
14959
  }
14330
14960
  }
14331
14961
  return mergedRects;
14332
14962
  }
14333
- function addMergedRect(resultList, elements, getRect, scrollTop, scrollLeft) {
14963
+ function sameMetadata(a, b, colorMap, annotationMap) {
14964
+ if ((colorMap?.get(a) ?? undefined) !== (colorMap?.get(b) ?? undefined))
14965
+ return false;
14966
+ const annA = annotationMap?.get(a);
14967
+ const annB = annotationMap?.get(b);
14968
+ if (annA?.text !== annB?.text)
14969
+ return false;
14970
+ if (annA?.corner !== annB?.corner)
14971
+ return false;
14972
+ return true;
14973
+ }
14974
+ function addMergedRect(resultList, elements, getRect, scrollTop, scrollLeft, colorMap, annotationMap) {
14334
14975
  if (elements.length === 0)
14335
14976
  return;
14336
14977
  let minTop = Infinity;
@@ -14353,7 +14994,7 @@
14353
14994
  maxBottom = bottom;
14354
14995
  });
14355
14996
  const PADDING = 10;
14356
- resultList.push({
14997
+ const rect = {
14357
14998
  top: minTop - PADDING,
14358
14999
  left: minLeft - PADDING,
14359
15000
  width: maxRight - minLeft + PADDING * 2,
@@ -14361,7 +15002,16 @@
14361
15002
  right: maxRight + PADDING,
14362
15003
  bottom: maxBottom + PADDING,
14363
15004
  element: elements[0],
14364
- });
15005
+ };
15006
+ const color = colorMap?.get(elements[0]);
15007
+ if (color !== undefined)
15008
+ rect.color = color;
15009
+ const annotation = annotationMap?.get(elements[0]);
15010
+ if (annotation !== undefined) {
15011
+ rect.annotation = annotation.text;
15012
+ rect.annotationCorner = annotation.corner;
15013
+ }
15014
+ resultList.push(rect);
14365
15015
  }
14366
15016
 
14367
15017
  /* highlight-service.svelte.ts generated by Svelte v5.46.1 */
@@ -14389,6 +15039,8 @@
14389
15039
  state = new HighlightState();
14390
15040
  resizeObserver;
14391
15041
  activeTargets = [];
15042
+ activeColors = new Map();
15043
+ activeAnnotations = new Map();
14392
15044
  onWindowResize = () => this.updatePositions();
14393
15045
 
14394
15046
  constructor(rootEl) {
@@ -14399,18 +15051,44 @@
14399
15051
  });
14400
15052
  }
14401
15053
 
15054
+ resolveTargets(encodedDescriptors) {
15055
+ const descriptors = deserialize(encodedDescriptors);
15056
+
15057
+ if (!descriptors || descriptors.length === 0) return [];
15058
+
15059
+ const targets = [];
15060
+
15061
+ descriptors.forEach((desc) => {
15062
+ const matchingEls = resolve(this.rootEl, desc);
15063
+
15064
+ if (matchingEls && matchingEls.length > 0) targets.push(...matchingEls);
15065
+ });
15066
+
15067
+ return targets;
15068
+ }
15069
+
14402
15070
  apply(encodedDescriptors) {
14403
15071
  const descriptors = deserialize(encodedDescriptors);
14404
15072
 
14405
15073
  if (!descriptors || descriptors.length === 0) return;
14406
15074
 
14407
15075
  const targets = [];
15076
+ const colors = new Map();
15077
+ const annotations = new Map();
14408
15078
 
14409
15079
  descriptors.forEach((desc) => {
14410
15080
  const matchingEls = resolve(this.rootEl, desc);
14411
15081
 
14412
15082
  if (matchingEls && matchingEls.length > 0) {
14413
15083
  targets.push(...matchingEls);
15084
+
15085
+ if (desc.color) {
15086
+ matchingEls.forEach((el) => colors.set(el, desc.color));
15087
+ }
15088
+
15089
+ if (desc.annotation && desc.annotationCorner) {
15090
+ matchingEls.forEach((el) => annotations.set(el, { text: desc.annotation, corner: desc.annotationCorner }));
15091
+ }
14414
15092
  }
14415
15093
  });
14416
15094
 
@@ -14433,6 +15111,9 @@
14433
15111
  // Create Overlay across the entire page (App will be mounted into it)
14434
15112
  this.activeTargets = targets;
14435
15113
 
15114
+ this.activeColors = colors;
15115
+ this.activeAnnotations = annotations;
15116
+
14436
15117
  // Start observing
14437
15118
  this.activeTargets.forEach((t) => this.resizeObserver.observe(t));
14438
15119
 
@@ -14440,16 +15121,11 @@
14440
15121
  window.addEventListener('resize', this.onWindowResize);
14441
15122
  this.renderHighlightOverlay();
14442
15123
 
14443
- // Scroll first target into view with header offset awareness
14444
- const firstTarget = targets[0];
15124
+ // Scroll topmost highlighted box into view
15125
+ const firstRect = this.state.rects[0];
14445
15126
 
14446
- if (firstTarget) {
14447
- // Use double-RAF to ensure layout stability (e.g. Svelte updates, animations)
14448
- requestAnimationFrame(() => {
14449
- requestAnimationFrame(() => {
14450
- scrollToElement(firstTarget);
14451
- });
14452
- });
15127
+ if (firstRect) {
15128
+ this.scrollToTargetSafely(firstRect.element);
14453
15129
  }
14454
15130
  }
14455
15131
 
@@ -14458,6 +15134,8 @@
14458
15134
  this.resizeObserver.disconnect();
14459
15135
  window.removeEventListener('resize', this.onWindowResize);
14460
15136
  this.activeTargets = [];
15137
+ this.activeColors.clear();
15138
+ this.activeAnnotations.clear();
14461
15139
  this.state.rects = [];
14462
15140
 
14463
15141
  const overlay = document.getElementById(ARROW_OVERLAY_ID);
@@ -14492,6 +15170,45 @@
14492
15170
  this.overlayApp = mount(HighlightOverlay, { target: overlay, props: { box: this.state } });
14493
15171
  }
14494
15172
 
15173
+ async scrollToTarget(element) {
15174
+ const currentShown = activeStateStore.state.shownToggles ?? [];
15175
+ const currentPeek = activeStateStore.state.peekToggles ?? [];
15176
+
15177
+ // Walk ancestors, collecting toggle IDs that need expansion
15178
+ const needsExpansion = [];
15179
+
15180
+ let current = element.parentElement;
15181
+
15182
+ while (current) {
15183
+ if (current.tagName.toLowerCase() === 'cv-toggle') {
15184
+ (current.getAttribute('toggle-id') || '').split(/\s+/).filter(Boolean).forEach((id) => {
15185
+ if (!currentShown.includes(id)) needsExpansion.push(id);
15186
+ });
15187
+ }
15188
+
15189
+ current = current.parentElement;
15190
+ }
15191
+
15192
+ // Expand any collapsed/peek ancestor toggles
15193
+ if (needsExpansion.length > 0) {
15194
+ activeStateStore.setToggles([...new Set([...currentShown, ...needsExpansion])], currentPeek.filter((id) => !needsExpansion.includes(id)));
15195
+ }
15196
+
15197
+ // Wait for CSS transitions if any toggles are peek, were just expanded, or are hidden (collapsing)
15198
+ if (needsExpansion.length > 0 || currentPeek.length > 0 || derivedStore.hiddenToggleIds.length > 0) {
15199
+ // 350ms = 300ms CSS transition + 50ms buffer
15200
+ await new Promise((resolve) => setTimeout(resolve, 350));
15201
+ }
15202
+
15203
+ element.scrollIntoView({ behavior: 'smooth', block: 'center' });
15204
+ }
15205
+
15206
+ scrollToTargetSafely(target) {
15207
+ void this.scrollToTarget(target).catch((error) => {
15208
+ console.error('Failed to scroll to target', error);
15209
+ });
15210
+ }
15211
+
14495
15212
  updatePositions() {
14496
15213
  if (this.activeTargets.length === 0) {
14497
15214
  this.state.rects = [];
@@ -14502,11 +15219,17 @@
14502
15219
  // Group by Parent (Siblings)
14503
15220
  const groups = groupSiblings(this.activeTargets);
14504
15221
 
14505
- // Calculate Union Rect for each group
14506
- this.state.rects = calculateMergedRects(groups, (el) => el.getBoundingClientRect(), () => ({
14507
- scrollTop: window.pageYOffset || document.documentElement.scrollTop,
14508
- scrollLeft: window.pageXOffset || document.documentElement.scrollLeft
14509
- }));
15222
+ // Calculate Union Rect for each group, sorted top-to-bottom
15223
+ this.state.rects = calculateMergedRects(
15224
+ groups,
15225
+ (el) => el.getBoundingClientRect(),
15226
+ () => ({
15227
+ scrollTop: window.pageYOffset || document.documentElement.scrollTop,
15228
+ scrollLeft: window.pageXOffset || document.documentElement.scrollLeft
15229
+ }),
15230
+ this.activeColors,
15231
+ this.activeAnnotations
15232
+ ).sort((a, b) => a.top - b.top);
14510
15233
  }
14511
15234
  }
14512
15235
 
@@ -14545,7 +15268,7 @@
14545
15268
  // Store safety check (Store changes affect UI)
14546
15269
  user_effect(() => {
14547
15270
  if (!focusStore.isActive && (document.body.classList.contains(BODY_SHOW_CLASS) || document.body.classList.contains(BODY_HIGHLIGHT_CLASS))) {
14548
- this.exitShowMode(false);
15271
+ this.exitShowMode(true);
14549
15272
  }
14550
15273
  });
14551
15274
  });
@@ -14574,17 +15297,39 @@
14574
15297
  const showDescriptors = url.searchParams.get(SHOW_PARAM);
14575
15298
  const hideDescriptors = url.searchParams.get(HIDE_PARAM);
14576
15299
  const highlightDescriptors = url.searchParams.get(HIGHLIGHT_PARAM);
15300
+ const hasAnyMode = showDescriptors || hideDescriptors || highlightDescriptors;
14577
15301
 
14578
- if (showDescriptors) {
14579
- this.applyShowMode(showDescriptors);
14580
- } else if (hideDescriptors) {
14581
- this.applyHideMode(hideDescriptors);
14582
- } else if (highlightDescriptors) {
14583
- this.applyHighlightMode(highlightDescriptors);
14584
- } else {
15302
+ if (!hasAnyMode) {
14585
15303
  if (document.body.classList.contains(BODY_SHOW_CLASS) || document.body.classList.contains(BODY_HIGHLIGHT_CLASS)) {
14586
15304
  this.exitShowMode(false);
14587
15305
  }
15306
+
15307
+ return;
15308
+ }
15309
+
15310
+ // Clear any existing active state once before re-applying
15311
+ if (document.body.classList.contains(BODY_SHOW_CLASS) || document.body.classList.contains(BODY_HIGHLIGHT_CLASS)) {
15312
+ this.exitShowMode(false);
15313
+ }
15314
+
15315
+ // Pre-resolve highlight targets so show/hide modes can account for them
15316
+ const highlightTargets = highlightDescriptors
15317
+ ? this.highlightService.resolveTargets(highlightDescriptors)
15318
+ : [];
15319
+
15320
+ // Apply show or hide (mutually exclusive with each other).
15321
+ // Pass highlight targets so they are kept visible in show mode / not hidden in hide mode.
15322
+ if (showDescriptors) {
15323
+ this.applyShowMode(showDescriptors, highlightTargets);
15324
+ } else if (hideDescriptors) {
15325
+ this.applyHideMode(hideDescriptors, highlightTargets);
15326
+ }
15327
+
15328
+ // Apply highlight independently — can coexist with show/hide.
15329
+ // Call highlightService.apply() directly to skip applyHighlightMode()'s guard,
15330
+ // which would otherwise see BODY_SHOW_CLASS and clear the show mode above.
15331
+ if (highlightDescriptors) {
15332
+ this.highlightService.apply(highlightDescriptors);
14588
15333
  }
14589
15334
  }
14590
15335
 
@@ -14592,7 +15337,7 @@
14592
15337
  * Applies focus mode to the specified descriptors.
14593
15338
  * @param encodedDescriptors - The encoded descriptors to apply.
14594
15339
  */
14595
- applyShowMode(encodedDescriptors) {
15340
+ applyShowMode(encodedDescriptors, keepTargets = []) {
14596
15341
  // Check if we are already in the right state to avoid re-rendering loops if feasible
14597
15342
  if (document.body.classList.contains(BODY_SHOW_CLASS) || document.body.classList.contains(BODY_HIGHLIGHT_CLASS)) {
14598
15343
  // If we are already active, we might want to check if descriptors changed?
@@ -14630,10 +15375,12 @@
14630
15375
  focusStore.setIsActive(true);
14631
15376
 
14632
15377
  document.body.classList.add(BODY_SHOW_CLASS);
14633
- this.renderShowView(targets);
15378
+
15379
+ // Merge keepTargets (e.g. highlight targets) so they are not hidden by show mode
15380
+ this.renderShowView([...targets, ...keepTargets]);
14634
15381
  }
14635
15382
 
14636
- applyHideMode(encodedDescriptors) {
15383
+ applyHideMode(encodedDescriptors, excludeTargets = []) {
14637
15384
  if (document.body.classList.contains(BODY_SHOW_CLASS) || document.body.classList.contains(BODY_HIGHLIGHT_CLASS)) {
14638
15385
  this.exitShowMode(false);
14639
15386
  }
@@ -14667,7 +15414,13 @@
14667
15414
  focusStore.setIsActive(true);
14668
15415
 
14669
15416
  document.body.classList.add(BODY_SHOW_CLASS);
14670
- this.renderHiddenView(targets);
15417
+
15418
+ // Exclude highlight targets from being hidden so they stay visible
15419
+ const excludeSet = new Set(excludeTargets);
15420
+
15421
+ const filteredTargets = excludeTargets.length > 0 ? targets.filter((t) => !excludeSet.has(t)) : targets;
15422
+
15423
+ this.renderHiddenView(filteredTargets);
14671
15424
  }
14672
15425
 
14673
15426
  applyHighlightMode(encodedDescriptors) {
@@ -14863,13 +15616,16 @@
14863
15616
  * Scans the DOM for [[ variable_name ]] patterns and replaces them with
14864
15617
  * reactive spans that update when the variable store changes.
14865
15618
  */
14866
- // Regex to find [[ variable : fallback ]]
15619
+ // Regex to find [[ variable : fallback ]] or [[ variable ? truthy : falsy ]]
14867
15620
  // Group 1: escape character (backslashes)
14868
15621
  // Group 2: variable name (alphanumeric, underscores, hyphens)
14869
- // Group 3 (optional): fallback value
14870
- const VAR_REGEX = /(\\)?\[\[\s*([a-zA-Z0-9_-]+)(?:\s*:\s*(.*?))?\s*\]\]/g;
15622
+ // Group 3 (optional): * modifier — when present, conditional resolves any value (user OR registry default)
15623
+ // Group 4 (optional): conditional truthy template (only when ? is present)
15624
+ // Group 5 (optional): conditional falsy string (only when ? is present)
15625
+ // Group 6 (optional): fallback value (only when : without ?)
15626
+ const VAR_REGEX = /(\\)?\[\[\s*([a-zA-Z0-9_-]+)(\*)?\s*(?:\?\s*(.*?)\s*:\s*(.*?)|:\s*(.*?))?\s*\]\]/g;
14871
15627
  // Non-global version for stateless testing
14872
- const VAR_TESTER = /(\\)?\[\[\s*([a-zA-Z0-9_-]+)(?:\s*:\s*(.*?))?\s*\]\]/;
15628
+ const VAR_TESTER = /(\\)?\[\[\s*([a-zA-Z0-9_-]+)(\*)?\s*(?:\?\s*(.*?)\s*:\s*(.*?)|:\s*(.*?))?\s*\]\]/;
14873
15629
  class PlaceholderBinder {
14874
15630
  /**
14875
15631
  * Scans the root element for text nodes containing variable placeholders.
@@ -14934,7 +15690,7 @@
14934
15690
  VAR_REGEX.lastIndex = 0;
14935
15691
  while ((match = VAR_REGEX.exec(text)) !== null) {
14936
15692
  hasMatch = true;
14937
- const [fullMatch, escape, name, fallback] = match;
15693
+ const [fullMatch, escape, name, modifier, condTruthy, condFalsy, fallback] = match;
14938
15694
  const index = match.index;
14939
15695
  if (!name)
14940
15696
  continue;
@@ -14951,8 +15707,15 @@
14951
15707
  // Create Placeholder Custom Element
14952
15708
  const el = document.createElement('cv-placeholder');
14953
15709
  el.setAttribute('name', name);
14954
- if (fallback)
15710
+ if (condTruthy !== undefined) {
15711
+ el.setAttribute('truthy', condTruthy);
15712
+ el.setAttribute('falsy', condFalsy ?? '');
15713
+ if (modifier === '*')
15714
+ el.setAttribute('any-value', '');
15715
+ }
15716
+ else if (fallback !== undefined) {
14955
15717
  el.setAttribute('fallback', fallback);
15718
+ }
14956
15719
  fragment.appendChild(el);
14957
15720
  // Register detection
14958
15721
  elementStore.registerPlaceholder(name);
@@ -15034,6 +15797,21 @@
15034
15797
  static isRelativeUrl(value) {
15035
15798
  return value.startsWith('/') || value.startsWith('./') || value.startsWith('../');
15036
15799
  }
15800
+ /**
15801
+ * Resolves value for a placeholder using only user-set values (no registry defaults).
15802
+ * Returns undefined if the user has not explicitly set a non-empty value.
15803
+ *
15804
+ * Used by conditional syntax `[[name ? truthy : falsy]]` (without `*`) and
15805
+ * `<cv-toggle placeholder-id="name">` (without `*`).
15806
+ *
15807
+ * @param name - The placeholder name to resolve
15808
+ * @param values - Record of user-set placeholder values
15809
+ * @returns The user-set value, or undefined if not set
15810
+ */
15811
+ static resolveUserValue(name, values) {
15812
+ const userVal = values[name];
15813
+ return userVal !== undefined && userVal !== '' ? userVal : undefined;
15814
+ }
15037
15815
  /**
15038
15816
  * Resolves value for a placeholder by checking and using sources in order of:
15039
15817
  * 1. user-set value, 2. registry default value, 3. inline fallback value
@@ -15053,7 +15831,7 @@
15053
15831
  if (userVal !== undefined && userVal !== '') {
15054
15832
  return userVal;
15055
15833
  }
15056
- else if (fallback) {
15834
+ else if (fallback !== undefined) {
15057
15835
  return fallback;
15058
15836
  }
15059
15837
  else if (registryDefault !== undefined && registryDefault !== '') {
@@ -15078,9 +15856,23 @@
15078
15856
  * @returns The interpolated string with all placeholders replaced
15079
15857
  */
15080
15858
  static interpolateString(template, values, attrName) {
15081
- return template.replace(VAR_REGEX, (_full, escape, name, fallback) => {
15859
+ return template.replace(VAR_REGEX, (_full, escape, name, modifier, condTruthy, condFalsy, fallback) => {
15082
15860
  if (escape)
15083
15861
  return `[[${name}]]`;
15862
+ if (condTruthy !== undefined) {
15863
+ let val = modifier === '*'
15864
+ ? PlaceholderBinder.resolveValue(name, undefined, values)
15865
+ : PlaceholderBinder.resolveUserValue(name, values);
15866
+ if (val === undefined)
15867
+ return condFalsy ?? '';
15868
+ // URL-encode the value component (same as regular placeholders)
15869
+ if (attrName && (attrName === 'href' || attrName === 'src')) {
15870
+ if (!PlaceholderBinder.isFullUrl(val) && !PlaceholderBinder.isRelativeUrl(val)) {
15871
+ val = encodeURIComponent(val);
15872
+ }
15873
+ }
15874
+ return condTruthy.replace(/\$/g, () => val);
15875
+ }
15084
15876
  let val = PlaceholderBinder.resolveValue(name, fallback, values);
15085
15877
  if (val === undefined)
15086
15878
  return `[[${name}]]`;
@@ -15118,10 +15910,30 @@
15118
15910
 
15119
15911
  /* runtime.svelte.ts generated by Svelte v5.46.1 */
15120
15912
 
15913
+ function stripAdaptationPlaceholders(state) {
15914
+ if (!state.placeholders) return state;
15915
+
15916
+ const filtered = {};
15917
+
15918
+ for (const [key, value] of Object.entries(state.placeholders)) {
15919
+ const def = placeholderRegistryStore.get(key);
15920
+
15921
+ if (!def?.adaptationPlaceholder) filtered[key] = value;
15922
+ }
15923
+
15924
+ return { ...state, placeholders: filtered };
15925
+ }
15926
+
15927
+ /**
15928
+ * The reactive runtime for CustardUI. Manages the full lifecycle: initialization,
15929
+ * state resolution, reactive side-effects (URL sync, persistence), DOM observation, and teardown.
15930
+ * Components (Toggle, TabGroup) are self-contained and self-managing via the global store.
15931
+ */
15121
15932
  class AppRuntime {
15122
15933
  rootEl;
15123
15934
  persistenceManager;
15124
15935
  focusService;
15936
+ iconSettingsStore;
15125
15937
  observer;
15126
15938
  destroyEffectRoot;
15127
15939
  onHashChange;
@@ -15129,6 +15941,7 @@
15129
15941
  constructor(opt) {
15130
15942
  this.rootEl = opt.rootEl || document.body;
15131
15943
  this.persistenceManager = new PersistenceManager(opt.storageKey);
15944
+ this.iconSettingsStore = new IconSettingsStore(this.persistenceManager);
15132
15945
 
15133
15946
  // Initialize all store singletons with config
15134
15947
  this.initStores(opt.configFile);
@@ -15245,7 +16058,7 @@
15245
16058
  this.destroyEffectRoot = effect_root(() => {
15246
16059
  // Automatic Persistence
15247
16060
  user_effect(() => {
15248
- this.persistenceManager.persistState(activeStateStore.state);
16061
+ this.persistenceManager.persistState(stripAdaptationPlaceholders(activeStateStore.state));
15249
16062
  this.persistenceManager.persistTabNavVisibility(uiStore.isTabGroupNavHeadingVisible);
15250
16063
  });
15251
16064
 
@@ -15315,32 +16128,14 @@
15315
16128
  resetToDefault() {
15316
16129
  this.persistenceManager.clearAll();
15317
16130
  activeStateStore.reset();
15318
- uiStore.reset();
15319
- uiStore.isTabGroupNavHeadingVisible = true;
15320
- }
15321
-
15322
- // --- Icon Position Persistence ---
15323
- getIconPosition() {
15324
- const raw = this.persistenceManager.getItem('cv-settings-icon-offset');
15325
-
15326
- return raw ? parseFloat(raw) : null;
15327
- }
15328
-
15329
- saveIconPosition(offset) {
15330
- this.persistenceManager.setItem('cv-settings-icon-offset', offset.toString());
15331
- }
15332
-
15333
- clearIconPosition() {
15334
- this.persistenceManager.removeItem('cv-settings-icon-offset');
15335
- }
15336
16131
 
15337
- // --- Intro Callout Persistence ---
15338
- isIntroSeen() {
15339
- return !!this.persistenceManager.getItem('cv-intro-shown');
15340
- }
16132
+ // Re-apply adaptation defaults so adaptation-controlled placeholders are not wiped by reset.
16133
+ if (adaptationStore.activeConfig?.defaults) {
16134
+ activeStateStore.applyAdaptationDefaults(adaptationStore.activeConfig.defaults);
16135
+ }
15341
16136
 
15342
- markIntroSeen() {
15343
- this.persistenceManager.setItem('cv-intro-shown', 'true');
16137
+ uiStore.reset();
16138
+ uiStore.isTabGroupNavHeadingVisible = true;
15344
16139
  }
15345
16140
 
15346
16141
  destroy() {
@@ -15536,7 +16331,8 @@
15536
16331
  let toggleId = prop($$props, 'toggleId', 7, ''),
15537
16332
  assetId = prop($$props, 'assetId', 7, ''),
15538
16333
  showPeekBorder = prop($$props, 'showPeekBorder', 7, false),
15539
- showLabel = prop($$props, 'showLabel', 7, false);
16334
+ showLabel = prop($$props, 'showLabel', 7, false),
16335
+ placeholderId = prop($$props, 'placeholderId', 7, '');
15540
16336
 
15541
16337
  // Derive toggle IDs from toggle-id prop (can have multiple space-separated IDs)
15542
16338
  let toggleIds = user_derived(() => (toggleId() || '').split(/\s+/).filter(Boolean));
@@ -15547,6 +16343,32 @@
15547
16343
  get(toggleIds).forEach((id) => elementStore.registerToggle(id));
15548
16344
  });
15549
16345
 
16346
+ // Placeholder mode: show/hide based on whether a placeholder has a value
16347
+ let placeholderName = user_derived(() => placeholderId().replace(/\*$/, ''));
16348
+
16349
+ let placeholderAny = user_derived(() => placeholderId().endsWith('*'));
16350
+ let isPlaceholderMode = user_derived(() => !!placeholderId());
16351
+
16352
+ let placeholderVisible = user_derived(() => {
16353
+ if (!get(isPlaceholderMode)) return false;
16354
+
16355
+ const values = activeStateStore.state.placeholders ?? {};
16356
+
16357
+ if (get(placeholderAny)) {
16358
+ return PlaceholderBinder.resolveValue(get(placeholderName), undefined, values) !== undefined;
16359
+ }
16360
+
16361
+ return PlaceholderBinder.resolveUserValue(get(placeholderName), values) !== undefined;
16362
+ });
16363
+
16364
+ user_effect(() => {
16365
+ if (placeholderId() && toggleId()) {
16366
+ console.warn(`[cv-toggle] Both 'toggle-id' and 'placeholder-id' are set. ` + `'placeholder-id' will take precedence and 'toggle-id' will be ignored.`);
16367
+ }
16368
+
16369
+ if (placeholderId()) elementStore.registerPlaceholder(get(placeholderName));
16370
+ });
16371
+
15550
16372
  // Derive label text from config
15551
16373
  let labelText = user_derived(() => {
15552
16374
  if (!get(toggleConfig)) return '';
@@ -15614,7 +16436,9 @@
15614
16436
  };
15615
16437
  });
15616
16438
 
15617
- let showFullContent = user_derived(() => get(showState) || get(peekState) && get(localExpanded) || get(peekState) && get(isSmallContent));
16439
+ let showFullContent = user_derived(() => get(isPlaceholderMode)
16440
+ ? get(placeholderVisible)
16441
+ : get(showState) || get(peekState) && get(localExpanded) || get(peekState) && get(isSmallContent));
15618
16442
 
15619
16443
  // Reset unconstrained state when toggling
15620
16444
  user_effect(() => {
@@ -15630,7 +16454,9 @@
15630
16454
  // Only show peek styling (mask) if it's peeking, not expanded locally, AND content is actually taller than peek height
15631
16455
  let showPeekContent = user_derived(() => !get(showState) && get(peekState) && !get(localExpanded) && !get(isSmallContent));
15632
16456
 
15633
- let isHidden = user_derived(() => !get(showState) && !get(peekState));
16457
+ let isHidden = user_derived(() => get(isPlaceholderMode)
16458
+ ? !get(placeholderVisible)
16459
+ : !get(showState) && !get(peekState));
15634
16460
 
15635
16461
  // Calculate dynamic max-height for animation
15636
16462
  let currentMaxHeight = user_derived(() => {
@@ -15700,6 +16526,15 @@
15700
16526
  set showLabel($$value = false) {
15701
16527
  showLabel($$value);
15702
16528
  flushSync();
16529
+ },
16530
+
16531
+ get placeholderId() {
16532
+ return placeholderId();
16533
+ },
16534
+
16535
+ set placeholderId($$value = '') {
16536
+ placeholderId($$value);
16537
+ flushSync();
15703
16538
  }
15704
16539
  };
15705
16540
 
@@ -15801,7 +16636,8 @@
15801
16636
  type: 'Boolean'
15802
16637
  },
15803
16638
 
15804
- showLabel: { attribute: 'show-label', reflect: true, type: 'Boolean' }
16639
+ showLabel: { attribute: 'show-label', reflect: true, type: 'Boolean' },
16640
+ placeholderId: { attribute: 'placeholder-id', reflect: true, type: 'String' }
15805
16641
  },
15806
16642
  ['default'],
15807
16643
  [],
@@ -16219,18 +17055,31 @@
16219
17055
  append_styles$1($$anchor, $$css$1);
16220
17056
 
16221
17057
  let name = prop($$props, 'name', 7),
16222
- fallback = prop($$props, 'fallback', 7);
17058
+ fallback = prop($$props, 'fallback', 7),
17059
+ truthy = prop($$props, 'truthy', 7),
17060
+ falsy = prop($$props, 'falsy', 7),
17061
+ anyValue = prop($$props, 'anyValue', 7);
16223
17062
 
16224
17063
  let value = user_derived(() => {
16225
17064
  if (!name()) return '';
16226
17065
 
17066
+ if (truthy() !== undefined) {
17067
+ const placeholders = activeStateStore.state.placeholders ?? {};
17068
+
17069
+ const val = anyValue()
17070
+ ? PlaceholderBinder.resolveValue(name(), undefined, placeholders)
17071
+ : PlaceholderBinder.resolveUserValue(name(), placeholders);
17072
+
17073
+ return val !== undefined ? truthy().replace(/\$/g, () => val) : falsy() ?? '';
17074
+ }
17075
+
16227
17076
  // 1. User Value
16228
17077
  const userVal = activeStateStore.state.placeholders?.[name()];
16229
17078
 
16230
17079
  if (userVal !== undefined && userVal !== '') return userVal;
16231
17080
 
16232
- // 2. Fallback
16233
- if (fallback()) return fallback();
17081
+ // 2. Fallback (explicit empty string is valid — shows nothing)
17082
+ if (fallback() !== undefined) return fallback();
16234
17083
 
16235
17084
  // 3. Registry Default
16236
17085
  const def = placeholderRegistryStore.get(name());
@@ -16272,6 +17121,33 @@
16272
17121
  set fallback($$value) {
16273
17122
  fallback($$value);
16274
17123
  flushSync();
17124
+ },
17125
+
17126
+ get truthy() {
17127
+ return truthy();
17128
+ },
17129
+
17130
+ set truthy($$value) {
17131
+ truthy($$value);
17132
+ flushSync();
17133
+ },
17134
+
17135
+ get falsy() {
17136
+ return falsy();
17137
+ },
17138
+
17139
+ set falsy($$value) {
17140
+ falsy($$value);
17141
+ flushSync();
17142
+ },
17143
+
17144
+ get anyValue() {
17145
+ return anyValue();
17146
+ },
17147
+
17148
+ set anyValue($$value) {
17149
+ anyValue($$value);
17150
+ flushSync();
16275
17151
  }
16276
17152
  };
16277
17153
 
@@ -16286,7 +17162,19 @@
16286
17162
  return pop($$exports);
16287
17163
  }
16288
17164
 
16289
- customElements.define('cv-placeholder', create_custom_element(Placeholder, { name: {}, fallback: {} }, [], [], true));
17165
+ customElements.define('cv-placeholder', create_custom_element(
17166
+ Placeholder,
17167
+ {
17168
+ name: { attribute: 'name', type: 'String' },
17169
+ fallback: { attribute: 'fallback', type: 'String' },
17170
+ truthy: { attribute: 'truthy', type: 'String' },
17171
+ falsy: { attribute: 'falsy', type: 'String' },
17172
+ anyValue: { attribute: 'any-value', type: 'Boolean' }
17173
+ },
17174
+ [],
17175
+ [],
17176
+ true
17177
+ ));
16290
17178
 
16291
17179
  var root_1 = from_html(`<label class="placeholder-label svelte-dpk3ag"> </label>`);
16292
17180
  var root = from_html(`<div><!> <input type="text"/></div>`);