@custardui/custardui 2.1.2-beta.0 → 2.1.2-beta.1

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 (73) hide show
  1. package/dist/custardui.js +82 -76
  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/ui-manager.d.ts.map +1 -1
  6. package/dist/types/src/lib/features/anchor/descriptor.d.ts.map +1 -1
  7. package/dist/types/src/lib/features/anchor/resolver.d.ts +1 -1
  8. package/dist/types/src/lib/features/anchor/resolver.d.ts.map +1 -1
  9. package/dist/types/src/lib/features/focus/services/focus-service.svelte.d.ts +1 -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/services/highlight-service.svelte.d.ts +1 -2
  12. package/dist/types/src/lib/features/highlight/services/highlight-service.svelte.d.ts.map +1 -1
  13. package/dist/types/src/lib/utils/init-utils.d.ts +2 -3
  14. package/dist/types/src/lib/utils/init-utils.d.ts.map +1 -1
  15. package/package.json +1 -1
  16. package/dist/types/tests/functional/fingerprint-compatibility.test.d.ts +0 -2
  17. package/dist/types/tests/functional/fingerprint-compatibility.test.d.ts.map +0 -1
  18. package/dist/types/tests/lib/features/adaptation/adaptation-manager.test.d.ts +0 -2
  19. package/dist/types/tests/lib/features/adaptation/adaptation-manager.test.d.ts.map +0 -1
  20. package/dist/types/tests/lib/features/adaptation/stores/adaptation-store.test.d.ts +0 -2
  21. package/dist/types/tests/lib/features/adaptation/stores/adaptation-store.test.d.ts.map +0 -1
  22. package/dist/types/tests/lib/features/anchor/anchor.test.d.ts +0 -2
  23. package/dist/types/tests/lib/features/anchor/anchor.test.d.ts.map +0 -1
  24. package/dist/types/tests/lib/features/focus/focus-logic.test.d.ts +0 -2
  25. package/dist/types/tests/lib/features/focus/focus-logic.test.d.ts.map +0 -1
  26. package/dist/types/tests/lib/features/focus/focus-store.test.d.ts +0 -2
  27. package/dist/types/tests/lib/features/focus/focus-store.test.d.ts.map +0 -1
  28. package/dist/types/tests/lib/features/highlight/highlight-logic.test.d.ts +0 -2
  29. package/dist/types/tests/lib/features/highlight/highlight-logic.test.d.ts.map +0 -1
  30. package/dist/types/tests/lib/features/labels/label-manager.test.d.ts +0 -2
  31. package/dist/types/tests/lib/features/labels/label-manager.test.d.ts.map +0 -1
  32. package/dist/types/tests/lib/features/labels/label-registry-store.test.d.ts +0 -2
  33. package/dist/types/tests/lib/features/labels/label-registry-store.test.d.ts.map +0 -1
  34. package/dist/types/tests/lib/features/labels/label-utils.test.d.ts +0 -2
  35. package/dist/types/tests/lib/features/labels/label-utils.test.d.ts.map +0 -1
  36. package/dist/types/tests/lib/features/notifications/stores/toast-store.test.d.ts +0 -2
  37. package/dist/types/tests/lib/features/notifications/stores/toast-store.test.d.ts.map +0 -1
  38. package/dist/types/tests/lib/features/placeholder/placeholder-binder.test.d.ts +0 -2
  39. package/dist/types/tests/lib/features/placeholder/placeholder-binder.test.d.ts.map +0 -1
  40. package/dist/types/tests/lib/features/placeholder/placeholder-manager.test.d.ts +0 -2
  41. package/dist/types/tests/lib/features/placeholder/placeholder-manager.test.d.ts.map +0 -1
  42. package/dist/types/tests/lib/features/settings/intro-manager.test.d.ts +0 -2
  43. package/dist/types/tests/lib/features/settings/intro-manager.test.d.ts.map +0 -1
  44. package/dist/types/tests/lib/features/settings/stores/icon-settings-store.test.d.ts +0 -2
  45. package/dist/types/tests/lib/features/settings/stores/icon-settings-store.test.d.ts.map +0 -1
  46. package/dist/types/tests/lib/features/share/share-logic.test.d.ts +0 -2
  47. package/dist/types/tests/lib/features/share/share-logic.test.d.ts.map +0 -1
  48. package/dist/types/tests/lib/features/share/share-store.test.d.ts +0 -2
  49. package/dist/types/tests/lib/features/share/share-store.test.d.ts.map +0 -1
  50. package/dist/types/tests/lib/features/url/url-action-handler.test.d.ts +0 -2
  51. package/dist/types/tests/lib/features/url/url-action-handler.test.d.ts.map +0 -1
  52. package/dist/types/tests/lib/features/url/url-state-manager.test.d.ts +0 -2
  53. package/dist/types/tests/lib/features/url/url-state-manager.test.d.ts.map +0 -1
  54. package/dist/types/tests/lib/services/url-action-router.test.d.ts +0 -2
  55. package/dist/types/tests/lib/services/url-action-router.test.d.ts.map +0 -1
  56. package/dist/types/tests/lib/stores/active-state-store.test.d.ts +0 -2
  57. package/dist/types/tests/lib/stores/active-state-store.test.d.ts.map +0 -1
  58. package/dist/types/tests/lib/stores/color-scheme-store.test.d.ts +0 -2
  59. package/dist/types/tests/lib/stores/color-scheme-store.test.d.ts.map +0 -1
  60. package/dist/types/tests/lib/stores/derived-store.test.d.ts +0 -2
  61. package/dist/types/tests/lib/stores/derived-store.test.d.ts.map +0 -1
  62. package/dist/types/tests/lib/stores/element-store.test.d.ts +0 -2
  63. package/dist/types/tests/lib/stores/element-store.test.d.ts.map +0 -1
  64. package/dist/types/tests/lib/stores/ui-store.test.d.ts +0 -2
  65. package/dist/types/tests/lib/stores/ui-store.test.d.ts.map +0 -1
  66. package/dist/types/tests/lib/utils/persistence.test.d.ts +0 -2
  67. package/dist/types/tests/lib/utils/persistence.test.d.ts.map +0 -1
  68. package/dist/types/tests/lib/utils/scroll-utils.test.d.ts +0 -2
  69. package/dist/types/tests/lib/utils/scroll-utils.test.d.ts.map +0 -1
  70. package/dist/types/tests/lib/utils/url-utils.test.d.ts +0 -2
  71. package/dist/types/tests/lib/utils/url-utils.test.d.ts.map +0 -1
  72. package/dist/types/tests/setup.d.ts +0 -2
  73. package/dist/types/tests/setup.d.ts.map +0 -1
package/dist/custardui.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @custardui/custardui v2.1.2-beta.0
2
+ * @custardui/custardui v2.1.2-beta.1
3
3
  * (c) 2026 Chan Ger Teck
4
4
  * Released under the MIT License.
5
5
  */
@@ -28,58 +28,61 @@
28
28
  return cleanbaseUrl + cleanPath;
29
29
  }
30
30
 
31
+ const SCRIPT_ATTRIBUTE_DEFAULTS = {
32
+ baseURL: '/',
33
+ configPath: '/custardui.config.json',
34
+ };
35
+ const FALLBACK_CONFIG = {
36
+ config: {},
37
+ settings: { enabled: false },
38
+ };
31
39
  /**
32
40
  * Finds the script tag that loaded the library and extracts configuration attributes.
33
41
  * Looks for `data-base-url` and `data-config-path`.
34
42
  */
35
43
  function getScriptAttributes() {
36
- let scriptTag = document.currentScript;
37
- const defaults = { baseURL: '/', configPath: '/custardui.config.json' };
38
- if (!scriptTag || !scriptTag.hasAttribute('data-base-url')) {
39
- const dataAttrMatch = document.querySelector('script[data-base-url]');
40
- if (dataAttrMatch) {
41
- scriptTag = dataAttrMatch;
42
- }
43
- else {
44
- // Fallback: try to find script by src pattern
45
- for (const script of document.scripts) {
46
- const src = script.src || '';
47
- if (/(?:custard(?:ui)?|@custardui\/custard(?:ui)?)(?:\.min)?\.(?:esm\.)?js($|\?)/i.test(src)) {
48
- scriptTag = script;
49
- break;
50
- }
51
- }
44
+ const scriptTag = findScriptTag();
45
+ if (!scriptTag)
46
+ return SCRIPT_ATTRIBUTE_DEFAULTS;
47
+ return {
48
+ baseURL: scriptTag.getAttribute('data-base-url') || SCRIPT_ATTRIBUTE_DEFAULTS.baseURL,
49
+ configPath: scriptTag.getAttribute('data-config-path') || SCRIPT_ATTRIBUTE_DEFAULTS.configPath,
50
+ };
51
+ }
52
+ function findScriptTag() {
53
+ const current = document.currentScript;
54
+ if (current?.hasAttribute('data-base-url'))
55
+ return current;
56
+ const byAttr = document.querySelector('script[data-base-url]');
57
+ if (byAttr)
58
+ return byAttr;
59
+ // Fallback: find script by src pattern
60
+ for (const script of document.scripts) {
61
+ if (/(?:custard(?:ui)?|@custardui\/custard(?:ui)?)(?:\.min)?\.(?:esm\.)?js($|\?)/i.test(script.src)) {
62
+ return script;
52
63
  }
53
64
  }
54
- if (scriptTag) {
55
- return {
56
- baseURL: scriptTag.getAttribute('data-base-url') || defaults.baseURL,
57
- configPath: scriptTag.getAttribute('data-config-path') || defaults.configPath,
58
- };
59
- }
60
- return defaults;
65
+ return null;
61
66
  }
62
67
  /**
63
68
  * Fetches and parses the configuration file.
69
+ * Returns the fallback config silently if the file is not found (404),
70
+ * since operating without a config file is a valid use case.
64
71
  */
65
72
  async function fetchConfig(configPath, baseURL) {
66
- const fallbackMinimalConfig = {
67
- config: {},
68
- settings: { enabled: true },
69
- };
70
73
  try {
71
74
  const fullConfigPath = prependBaseUrl(configPath, baseURL);
72
75
  const response = await fetch(fullConfigPath);
73
- if (!response.ok) {
74
- console.warn(`[CustardUI] Config file not found at ${fullConfigPath}. Using defaults.`);
75
- return fallbackMinimalConfig;
76
- }
77
- const config = await response.json();
78
- return config;
76
+ if (!response.ok)
77
+ return FALLBACK_CONFIG;
78
+ const text = await response.text();
79
+ if (!text.trim())
80
+ return FALLBACK_CONFIG;
81
+ return JSON.parse(text);
79
82
  }
80
83
  catch (error) {
81
84
  console.error('[CustardUI] Error loading config file:', error);
82
- return fallbackMinimalConfig;
85
+ return FALLBACK_CONFIG;
83
86
  }
84
87
  }
85
88
 
@@ -12210,33 +12213,45 @@
12210
12213
  return getStableTextContent(el).trim().replace(/\s+/g, ' ');
12211
12214
  }
12212
12215
 
12216
+ /**
12217
+ * Walks the ancestor chain of `el` to find the first element with a non-empty
12218
+ * `id` attribute. Returns both the id string and the ancestor element so the
12219
+ * caller can use the element itself as a query scope.
12220
+ * Returns `ancestorEl: null` when no id-bearing ancestor exists.
12221
+ */
12222
+ function findNearestIdAncestor(el) {
12223
+ let node = el.parentElement;
12224
+ while (node) {
12225
+ if (node.id)
12226
+ return { parentId: node.id, ancestorEl: node };
12227
+ node = node.parentElement;
12228
+ }
12229
+ return { parentId: undefined, ancestorEl: null };
12230
+ }
12213
12231
  /**
12214
12232
  * Creates an AnchorDescriptor for a given DOM element.
12215
12233
  */
12216
12234
  function createDescriptor(el) {
12217
12235
  const tag = el.tagName;
12218
12236
  const normalizedText = getStableNormalizedText(el);
12219
- // Find nearest parent with an ID
12220
- let parentId;
12221
- let parent = el.parentElement;
12222
- while (parent) {
12223
- if (parent.id) {
12224
- parentId = parent.id;
12225
- break;
12226
- }
12227
- parent = parent.parentElement;
12228
- }
12229
- // Calculate index relative to the container
12230
- const container = parent || document.body;
12237
+ const { parentId, ancestorEl: idAncestor } = findNearestIdAncestor(el);
12238
+ const container = idAncestor ?? document.body;
12231
12239
  const siblings = Array.from(container.querySelectorAll(tag));
12232
- const index = siblings.indexOf(el);
12240
+ const rawIndex = siblings.indexOf(el);
12241
+ if (rawIndex === -1) {
12242
+ console.error('[CustardUI] createDescriptor: element not found in container, ' +
12243
+ 'element may be detached from the DOM. Please open an issue.', el);
12244
+ }
12245
+ const index = rawIndex !== -1 ? rawIndex : 0;
12233
12246
  const descriptor = {
12234
12247
  tag,
12235
- index: index !== -1 ? index : 0,
12248
+ index,
12236
12249
  textSnippet: normalizedText.substring(0, 32),
12237
12250
  textHash: hashCode(normalizedText),
12238
- elementId: el.id,
12239
12251
  };
12252
+ if (el.id) {
12253
+ descriptor.elementId = el.id;
12254
+ }
12240
12255
  if (parentId) {
12241
12256
  descriptor.parentId = parentId;
12242
12257
  }
@@ -12438,7 +12453,7 @@
12438
12453
  * Returns an array of elements. For specific descriptors, usually contains 0 or 1 element.
12439
12454
  * For ID-only descriptors (tag='ANY'), may return multiple if duplicates exist.
12440
12455
  */
12441
- function resolve(root, descriptor) {
12456
+ function resolve(descriptor) {
12442
12457
  // 0. Direct ID Shortcut
12443
12458
  if (descriptor.elementId) {
12444
12459
  // Always support duplicate IDs for consistency, even if technically invalid HTML.
@@ -12450,19 +12465,15 @@
12450
12465
  return [];
12451
12466
  }
12452
12467
  // 1. Determine Scope
12453
- let scope = root;
12454
- // Optimization: If parentId exists, try to narrow scope immediately
12468
+ // Default to document.body so index pool matches descriptor.ts,
12469
+ // which also falls back to document.body when no id-bearing ancestor is found.
12470
+ let scope = document.body;
12471
+ // Optimization: If parentId exists, try to narrow scope immediately.
12455
12472
  if (descriptor.parentId) {
12456
- const foundParent = root.querySelector(`#${descriptor.parentId}`);
12473
+ const foundParent = document.getElementById(descriptor.parentId);
12457
12474
  if (foundParent instanceof HTMLElement) {
12458
12475
  scope = foundParent;
12459
12476
  }
12460
- else {
12461
- const globalParent = document.getElementById(descriptor.parentId);
12462
- if (globalParent) {
12463
- scope = globalParent;
12464
- }
12465
- }
12466
12477
  }
12467
12478
  // 2. Candidate Search & Scoring
12468
12479
  const candidates = scope.querySelectorAll(descriptor.tag);
@@ -14567,7 +14578,8 @@
14567
14578
  * Initializes the UI manager (settings and share UI) using the provided config.
14568
14579
  */
14569
14580
  function initUIManager(runtime, config) {
14570
- const settingsEnabled = config.settings?.enabled === true;
14581
+ const { enabled, ...widgetSettings } = config.settings ?? {};
14582
+ const settingsEnabled = enabled === true;
14571
14583
  const callbacks = {
14572
14584
  resetToDefault: () => runtime.resetToDefault(),
14573
14585
  iconSettings: runtime.iconSettingsStore,
@@ -14576,7 +14588,7 @@
14576
14588
  const uiManager = new CustardUIManager({
14577
14589
  callbacks,
14578
14590
  settingsEnabled,
14579
- ...config.settings,
14591
+ ...widgetSettings,
14580
14592
  });
14581
14593
  uiManager.render();
14582
14594
  return uiManager;
@@ -15728,7 +15740,6 @@
15728
15740
  }
15729
15741
 
15730
15742
  class HighlightService {
15731
- rootEl;
15732
15743
  overlayApp;
15733
15744
  state = new HighlightState();
15734
15745
  resizeObserver;
@@ -15737,9 +15748,7 @@
15737
15748
  activeAnnotations = new Map();
15738
15749
  onWindowResize = () => this.updatePositions();
15739
15750
 
15740
- constructor(rootEl) {
15741
- this.rootEl = rootEl;
15742
-
15751
+ constructor() {
15743
15752
  this.resizeObserver = new ResizeObserver(() => {
15744
15753
  this.updatePositions();
15745
15754
  });
@@ -15753,7 +15762,7 @@
15753
15762
  const targets = [];
15754
15763
 
15755
15764
  descriptors.forEach((desc) => {
15756
- const matchingEls = resolve(this.rootEl, desc);
15765
+ const matchingEls = resolve(desc);
15757
15766
 
15758
15767
  if (matchingEls && matchingEls.length > 0) targets.push(...matchingEls);
15759
15768
  });
@@ -15771,7 +15780,7 @@
15771
15780
  const annotations = new Map();
15772
15781
 
15773
15782
  descriptors.forEach((desc) => {
15774
- const matchingEls = resolve(this.rootEl, desc);
15783
+ const matchingEls = resolve(desc);
15775
15784
 
15776
15785
  if (matchingEls && matchingEls.length > 0) {
15777
15786
  targets.push(...matchingEls);
@@ -15937,7 +15946,6 @@
15937
15946
  const SHOW_ELEMENT_CLASS = 'cv-show-element';
15938
15947
 
15939
15948
  class FocusService {
15940
- rootEl;
15941
15949
  hiddenElements = new SvelteSet();
15942
15950
  dividers = new SvelteSet(); // Store Svelte App instances
15943
15951
  excludedTags;
@@ -15948,15 +15956,13 @@
15948
15956
 
15949
15957
  highlightService;
15950
15958
 
15951
- constructor(rootEl, options) {
15952
- this.rootEl = rootEl;
15953
-
15959
+ constructor(options) {
15954
15960
  const userTags = options.shareExclusions?.tags || [];
15955
15961
  const userIds = options.shareExclusions?.ids || [];
15956
15962
 
15957
15963
  this.excludedTags = new SvelteSet([...DEFAULT_EXCLUDED_TAGS, ...userTags].map((t) => t.toUpperCase()));
15958
15964
  this.excludedIds = new SvelteSet([...DEFAULT_EXCLUDED_IDS, ...userIds]);
15959
- this.highlightService = new HighlightService(this.rootEl);
15965
+ this.highlightService = new HighlightService();
15960
15966
 
15961
15967
  // Subscribe to store for exit signal
15962
15968
  this.unsubscribe = effect_root(() => {
@@ -16048,7 +16054,7 @@
16048
16054
  const targets = [];
16049
16055
 
16050
16056
  descriptors.forEach((desc) => {
16051
- const matchingEls = resolve(this.rootEl, desc);
16057
+ const matchingEls = resolve(desc);
16052
16058
 
16053
16059
  if (matchingEls && matchingEls.length > 0) {
16054
16060
  targets.push(...matchingEls);
@@ -16087,7 +16093,7 @@
16087
16093
  const targets = [];
16088
16094
 
16089
16095
  descriptors.forEach((desc) => {
16090
- const matchingEls = resolve(this.rootEl, desc);
16096
+ const matchingEls = resolve(desc);
16091
16097
 
16092
16098
  if (matchingEls && matchingEls.length > 0) {
16093
16099
  targets.push(...matchingEls);
@@ -16779,7 +16785,7 @@
16779
16785
  this.resolveInitialState(opt.adaptationConfig ?? null);
16780
16786
 
16781
16787
  // Resolve Exclusions
16782
- this.focusService = new FocusService(this.rootEl, {
16788
+ this.focusService = new FocusService({
16783
16789
  shareExclusions: opt.configFile.config?.shareExclusions || {}
16784
16790
  });
16785
16791
  }