hyperclayjs 1.5.0 → 1.7.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.
package/LICENSE CHANGED
@@ -31,11 +31,6 @@ This project includes the following third-party libraries:
31
31
  - License: 0BSD (Zero-Clause BSD)
32
32
  - https://github.com/bigskysoftware/idiomorph
33
33
 
34
- ### Tailwind CSS
35
- - Copyright (c) Tailwind Labs, Inc.
36
- - License: MIT
37
- - https://github.com/tailwindlabs/tailwindcss
38
-
39
34
  ### Sortable.js
40
35
  - Copyright (c) All contributors to Sortable
41
36
  - License: MIT
package/README.md CHANGED
@@ -60,11 +60,11 @@ import 'hyperclayjs/presets/standard.js';
60
60
  | autosave | 1.1KB | Auto-save on DOM changes, unsaved changes warning |
61
61
  | edit-mode | 1.8KB | Toggle edit mode on hyperclay on/off |
62
62
  | edit-mode-helpers | 5.4KB | Admin-only functionality: [edit-mode-input], [edit-mode-resource], [edit-mode-onclick] |
63
- | option-visibility | 4.7KB | Dynamic show/hide based on ancestor state with option:attribute="value" |
63
+ | option-visibility | 5.9KB | Dynamic show/hide based on ancestor state with option:attribute="value" |
64
64
  | persist | 2.5KB | Persist input/select/textarea values to the DOM with [persist] attribute |
65
65
  | save-core | 6.3KB | Basic save function only - hyperclay.savePage() |
66
- | save-system | 7KB | Manual save: keyboard shortcut (CMD+S), save button, change tracking |
67
- | save-toast | 0.9KB | Toast notifications for save events (opt-in) |
66
+ | save-system | 7.1KB | Manual save: keyboard shortcut (CMD+S), save button, change tracking |
67
+ | save-toast | 0.9KB | Toast notifications for save events |
68
68
 
69
69
  ### Custom Attributes (HTML enhancements)
70
70
 
@@ -82,7 +82,6 @@ import 'hyperclayjs/presets/standard.js';
82
82
  | Module | Size | Description |
83
83
  |--------|------|-------------|
84
84
  | dialogs | 8.4KB | ask(), consent(), tell(), snippet() dialog functions |
85
- | tailwind-play | 0.7KB | Live Tailwind CSS editing, lazy-loads ~370KB script in edit mode only |
86
85
  | the-modal | 19.8KB | Full modal window creation system - window.theModal |
87
86
  | toast | 7.7KB | Success/error message notifications, toast(msg, msgType) |
88
87
 
@@ -128,17 +127,17 @@ import 'hyperclayjs/presets/standard.js';
128
127
 
129
128
  ## Presets
130
129
 
131
- ### Minimal (~27.7KB)
130
+ ### Minimal (~27.8KB)
132
131
  Essential features for basic editing
133
132
 
134
133
  **Modules:** `save-core`, `save-system`, `edit-mode-helpers`, `toast`, `save-toast`, `export-to-window`
135
134
 
136
- ### Standard (~44.2KB)
135
+ ### Standard (~45.5KB)
137
136
  Standard feature set for most use cases
138
137
 
139
138
  **Modules:** `save-core`, `save-system`, `edit-mode-helpers`, `persist`, `option-visibility`, `event-attrs`, `dom-helpers`, `toast`, `save-toast`, `export-to-window`
140
139
 
141
- ### Everything (~149.6KB)
140
+ ### Everything (~150.2KB)
142
141
  All available features
143
142
 
144
143
  Includes all available modules across all categories.
@@ -150,7 +149,6 @@ Some modules with large vendor dependencies are **lazy-loaded** to optimize page
150
149
  | Module | Wrapper Size | Vendor Size | Loaded When |
151
150
  |--------|-------------|-------------|-------------|
152
151
  | `sortable` | ~3KB | ~118KB | Edit mode only |
153
- | `tailwind-play` | ~1KB | ~370KB | Edit mode only |
154
152
 
155
153
  **How it works:**
156
154
  - The wrapper module checks if the page is in edit mode (`isEditMode`)
@@ -160,7 +158,7 @@ Some modules with large vendor dependencies are **lazy-loaded** to optimize page
160
158
 
161
159
  This means:
162
160
  - **Editors** get full functionality when needed
163
- - **Viewers** never download ~500KB of vendor scripts
161
+ - **Viewers** never download the heavy vendor scripts
164
162
  - **Saved pages** stay clean with no leftover script tags
165
163
 
166
164
  ## Visual Configurator
@@ -369,7 +367,6 @@ MIT © Hyperclay
369
367
  This project includes the following open-source libraries:
370
368
 
371
369
  - **[Idiomorph](https://github.com/bigskysoftware/idiomorph)** - DOM morphing library by Big Sky Software (0BSD)
372
- - **[Tailwind CSS](https://github.com/tailwindlabs/tailwindcss)** - Utility-first CSS framework by Tailwind Labs (MIT)
373
370
  - **[Sortable.js](https://github.com/SortableJS/Sortable)** - Drag-and-drop library (MIT)
374
371
 
375
372
  ## Links
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Option Visibility (CSS Layers Implementation)
3
+ *
4
+ * Shows/hides elements based on `option:` attributes and ancestor matches.
5
+ *
6
+ * Usage:
7
+ * <div editmode="true">
8
+ * <button option:editmode="true">Visible</button>
9
+ * <button option:editmode="false">Hidden</button>
10
+ * </div>
11
+ *
12
+ * An element with `option:name="value"` is hidden by default.
13
+ * It becomes visible when ANY ancestor has `name="value"`.
14
+ *
15
+ * ---
16
+ *
17
+ * HOW IT WORKS:
18
+ * 1. Uses `display: none !important` to forcefully hide elements
19
+ * 2. Uses `display: revert-layer !important` to un-hide when ancestor matches
20
+ * `revert-layer` tells the browser: "Ignore rules in this layer, fall back to author styles"
21
+ * 3. This preserves the user's original `display` (flex, grid, block) without us knowing what it is
22
+ *
23
+ * BROWSER SUPPORT:
24
+ * Requires `@layer` and `revert-layer` support (~92% of browsers, 2022+).
25
+ * Falls back gracefully - elements remain visible if unsupported.
26
+ *
27
+ * TRADEOFFS:
28
+ * - Pro: Pure CSS after generation, zero JS overhead for toggling
29
+ * - Pro: Simple code, similar to original approach
30
+ * - Con: Loses to user `!important` rules (layered !important < unlayered !important)
31
+ */
32
+
33
+ import Mutation from "../utilities/mutation.js";
34
+
35
+ const optionVisibility = {
36
+ debug: false,
37
+ _started: false,
38
+ _styleElement: null,
39
+ _unsubscribe: null,
40
+
41
+ LAYER_NAME: 'option-visibility',
42
+ STYLE_CLASS: 'option-visibility-layer-styles',
43
+
44
+ log(...args) {
45
+ if (this.debug) console.log('[OptionVisibility:Layer]', ...args);
46
+ },
47
+
48
+ /**
49
+ * Check if browser supports the layer approach
50
+ */
51
+ isSupported() {
52
+ return typeof CSS !== 'undefined'
53
+ && typeof CSS.supports === 'function'
54
+ && CSS.supports('display', 'revert-layer');
55
+ },
56
+
57
+ /**
58
+ * Find all unique option:name="value" patterns using XPath (faster than regex on HTML)
59
+ */
60
+ findOptionAttributes() {
61
+ const patterns = new Map();
62
+
63
+ try {
64
+ const snapshot = document.evaluate(
65
+ '//*[@*[starts-with(name(), "option:")]]',
66
+ document.documentElement,
67
+ null,
68
+ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
69
+ null
70
+ );
71
+
72
+ for (let i = 0; i < snapshot.snapshotLength; i++) {
73
+ const el = snapshot.snapshotItem(i);
74
+ for (const attr of el.attributes) {
75
+ if (attr.name.startsWith('option:')) {
76
+ const name = attr.name.slice(7);
77
+ const value = attr.value;
78
+ const key = `${name}=${value}`;
79
+ if (!patterns.has(key)) {
80
+ patterns.set(key, { name, value });
81
+ }
82
+ }
83
+ }
84
+ }
85
+ } catch (error) {
86
+ this.log('XPath error, falling back to empty', error);
87
+ }
88
+
89
+ return [...patterns.values()];
90
+ },
91
+
92
+ /**
93
+ * Generate CSS rules wrapped in @layer
94
+ */
95
+ generateCSS(attributes) {
96
+ if (!attributes.length) return '';
97
+
98
+ const rules = attributes.map(({ name, value }) => {
99
+ const safeName = CSS.escape(name);
100
+ const safeValue = CSS.escape(value);
101
+
102
+ // Hidden by default, visible when ancestor matches
103
+ // Both rules need !important for consistency within the layer
104
+ return `[option\\:${safeName}="${safeValue}"]{display:none!important}[${safeName}="${safeValue}"] [option\\:${safeName}="${safeValue}"]{display:revert-layer!important}`;
105
+ }).join('');
106
+
107
+ return `@layer ${this.LAYER_NAME}{${rules}}`;
108
+ },
109
+
110
+ /**
111
+ * Update the style element with current rules
112
+ */
113
+ update() {
114
+ if (!this.isSupported()) {
115
+ this.log('Browser lacks revert-layer support, skipping');
116
+ return;
117
+ }
118
+
119
+ try {
120
+ const attributes = this.findOptionAttributes();
121
+ const css = this.generateCSS(attributes);
122
+
123
+ // Remove style element if no attributes
124
+ if (!css) {
125
+ if (this._styleElement) {
126
+ this._styleElement.remove();
127
+ this._styleElement = null;
128
+ this.log('Removed empty style element');
129
+ }
130
+ return;
131
+ }
132
+
133
+ // Skip if unchanged
134
+ if (this._styleElement?.textContent === css) {
135
+ this.log('Styles unchanged');
136
+ return;
137
+ }
138
+
139
+ // Create or update
140
+ if (!this._styleElement) {
141
+ this._styleElement = document.createElement('style');
142
+ this._styleElement.className = this.STYLE_CLASS;
143
+ document.head.appendChild(this._styleElement);
144
+ }
145
+
146
+ this._styleElement.textContent = css;
147
+ this.log(`Generated ${attributes.length} rules`);
148
+
149
+ } catch (error) {
150
+ console.error('[OptionVisibility:Layer] Error generating rules:', error);
151
+ }
152
+ },
153
+
154
+ start() {
155
+ if (this._started) return;
156
+
157
+ if (document.readyState === 'loading') {
158
+ document.addEventListener('DOMContentLoaded', () => this.start(), { once: true });
159
+ return;
160
+ }
161
+
162
+ this._started = true;
163
+
164
+ if (!this.isSupported()) {
165
+ console.warn('[OptionVisibility:Layer] Browser lacks revert-layer support. Elements will remain visible.');
166
+ return;
167
+ }
168
+
169
+ this.update();
170
+
171
+ // selectorFilter only triggers on option:* attribute changes (new patterns).
172
+ // Ancestor attribute changes (e.g., editmode="true" -> "false") are handled
173
+ // automatically by the browser - CSS rules re-evaluate when attributes change.
174
+ this._unsubscribe = Mutation.onAnyChange({
175
+ debounce: 200,
176
+ selectorFilter: el => [...el.attributes].some(attr => attr.name.startsWith('option:')),
177
+ omitChangeDetails: true
178
+ }, () => this.update());
179
+
180
+ this.log('Started');
181
+ },
182
+
183
+ stop() {
184
+ if (!this._started) return;
185
+
186
+ this._started = false;
187
+
188
+ if (this._unsubscribe) {
189
+ this._unsubscribe();
190
+ this._unsubscribe = null;
191
+ }
192
+
193
+ if (this._styleElement) {
194
+ this._styleElement.remove();
195
+ this._styleElement = null;
196
+ }
197
+
198
+ this.log('Stopped');
199
+ }
200
+ };
201
+
202
+ // Auto-export
203
+ if (!window.__hyperclayNoAutoExport) {
204
+ window.optionVisibility = optionVisibility;
205
+ window.hyperclay = window.hyperclay || {};
206
+ window.hyperclay.optionVisibility = optionVisibility;
207
+ window.h = window.hyperclay;
208
+ }
209
+
210
+ export default optionVisibility;
211
+
212
+ export function init() {
213
+ optionVisibility.start();
214
+ }
215
+
216
+ init();
package/core/savePage.js CHANGED
@@ -100,6 +100,8 @@ document.addEventListener('DOMContentLoaded', () => {
100
100
  if (isEditMode) {
101
101
  // Capture initial state immediately for comparison
102
102
  lastSavedContents = getPageContents();
103
+ // Set initial save status to 'saved'
104
+ document.documentElement.setAttribute('savestatus', 'saved');
103
105
  }
104
106
  });
105
107
 
@@ -121,7 +121,7 @@ export function savePage(callback = () => {}) {
121
121
 
122
122
  const msg = err.name === 'AbortError'
123
123
  ? 'Server not responding'
124
- : (err.message || 'Failed to save');
124
+ : 'Save failed';
125
125
 
126
126
  if (typeof callback === 'function') {
127
127
  callback({msg, msgType: "error"});
package/hyperclay.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * HyperclayJS v1.5.0 - Minimal Browser-Native Loader
2
+ * HyperclayJS v1.7.0 - Minimal Browser-Native Loader
3
3
  *
4
4
  * Modules auto-init when imported (no separate init call needed).
5
5
  * Include `export-to-window` feature to export to window.hyperclay.
@@ -31,7 +31,7 @@ const MODULE_PATHS = {
31
31
  "save-toast": "./core/saveToast.js",
32
32
  "edit-mode-helpers": "./core/adminSystem.js",
33
33
  "persist": "./core/enablePersistentFormInputValues.js",
34
- "option-visibility": "./core/optionVisibilityRuleGenerator.js",
34
+ "option-visibility": "./core/optionVisibility.js",
35
35
  "edit-mode": "./core/editmodeSystem.js",
36
36
  "event-attrs": "./custom-attributes/events.js",
37
37
  "ajax-elements": "./custom-attributes/ajaxElements.js",
@@ -43,7 +43,6 @@ const MODULE_PATHS = {
43
43
  "toast": "./ui/toast.js",
44
44
  "toast-hyperclay": "./ui/toast-hyperclay.js",
45
45
  "the-modal": "./ui/theModal.js",
46
- "tailwind-play": "./vendor/tailwind-play.js",
47
46
  "mutation": "./utilities/mutation.js",
48
47
  "nearest": "./utilities/nearest.js",
49
48
  "cookie": "./utilities/cookie.js",
@@ -114,7 +113,6 @@ const PRESETS = {
114
113
  "toast",
115
114
  "toast-hyperclay",
116
115
  "the-modal",
117
- "tailwind-play",
118
116
  "mutation",
119
117
  "nearest",
120
118
  "cookie",
@@ -68,7 +68,7 @@
68
68
  "string-utilities/query.js",
69
69
  "utilities/cookie.js"
70
70
  ],
71
- "core/optionVisibilityRuleGenerator.js": [
71
+ "core/optionVisibility.js": [
72
72
  "utilities/mutation.js"
73
73
  ],
74
74
  "core/savePage.js": [
@@ -155,14 +155,7 @@
155
155
  "utilities/pipe.js": [],
156
156
  "utilities/throttle.js": [],
157
157
  "vendor/Sortable.vendor.js": [],
158
- "vendor/idiomorph.min.js": [],
159
- "vendor/tailwind-play.js": [
160
- "core/isAdminOfCurrentResource.js",
161
- "utilities/loadVendorScript.js"
162
- ],
163
- "vendor/tailwind-play.vendor.js": [
164
- "dom-utilities/insertStyleTag.js"
165
- ]
158
+ "vendor/idiomorph.min.js": []
166
159
  },
167
160
  "modules": {
168
161
  "save-core": {
@@ -182,7 +175,7 @@
182
175
  "save-system": {
183
176
  "name": "save-system",
184
177
  "category": "core",
185
- "size": 7,
178
+ "size": 7.1,
186
179
  "files": [
187
180
  "core/savePage.js"
188
181
  ],
@@ -219,7 +212,7 @@
219
212
  "files": [
220
213
  "core/saveToast.js"
221
214
  ],
222
- "description": "Toast notifications for save events (opt-in)",
215
+ "description": "Toast notifications for save events",
223
216
  "exports": {}
224
217
  },
225
218
  "edit-mode-helpers": {
@@ -249,9 +242,9 @@
249
242
  "option-visibility": {
250
243
  "name": "option-visibility",
251
244
  "category": "core",
252
- "size": 4.7,
245
+ "size": 5.9,
253
246
  "files": [
254
- "core/optionVisibilityRuleGenerator.js"
247
+ "core/optionVisibility.js"
255
248
  ],
256
249
  "description": "Dynamic show/hide based on ancestor state with option:attribute=\"value\"",
257
250
  "exports": {}
@@ -415,16 +408,6 @@
415
408
  ]
416
409
  }
417
410
  },
418
- "tailwind-play": {
419
- "name": "tailwind-play",
420
- "category": "ui",
421
- "size": 0.7,
422
- "files": [
423
- "vendor/tailwind-play.js"
424
- ],
425
- "description": "Live Tailwind CSS editing, lazy-loads ~370KB script in edit mode only",
426
- "exports": {}
427
- },
428
411
  "mutation": {
429
412
  "name": "mutation",
430
413
  "category": "utilities",
@@ -695,7 +678,7 @@
695
678
  "save-toast": "./core/saveToast.js",
696
679
  "edit-mode-helpers": "./core/adminSystem.js",
697
680
  "persist": "./core/enablePersistentFormInputValues.js",
698
- "option-visibility": "./core/optionVisibilityRuleGenerator.js",
681
+ "option-visibility": "./core/optionVisibility.js",
699
682
  "edit-mode": "./core/editmodeSystem.js",
700
683
  "event-attrs": "./custom-attributes/events.js",
701
684
  "ajax-elements": "./custom-attributes/ajaxElements.js",
@@ -707,7 +690,6 @@
707
690
  "toast": "./ui/toast.js",
708
691
  "toast-hyperclay": "./ui/toast-hyperclay.js",
709
692
  "the-modal": "./ui/theModal.js",
710
- "tailwind-play": "./vendor/tailwind-play.js",
711
693
  "mutation": "./utilities/mutation.js",
712
694
  "nearest": "./utilities/nearest.js",
713
695
  "cookie": "./utilities/cookie.js",
@@ -760,8 +742,7 @@
760
742
  "modules": [
761
743
  "dialogs",
762
744
  "toast",
763
- "the-modal",
764
- "tailwind-play"
745
+ "the-modal"
765
746
  ]
766
747
  },
767
748
  "utilities": {
@@ -861,7 +842,6 @@
861
842
  "toast",
862
843
  "toast-hyperclay",
863
844
  "the-modal",
864
- "tailwind-play",
865
845
  "mutation",
866
846
  "nearest",
867
847
  "cookie",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperclayjs",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Modular JavaScript library for building interactive HTML applications with Hyperclay",
5
5
  "type": "module",
6
6
  "main": "hyperclay.js",
@@ -1,171 +0,0 @@
1
- /**
2
- *
3
- * Automatically show/hide elements with "option" attributes based on ancestors' attributes.
4
- *
5
- * # Usage:
6
- * optionVisibilityRuleGenerator.debug = true;
7
- * optionVisibilityRuleGenerator.start();
8
- *
9
- * # HTML Example:
10
- * <div editmode="true"> <!-- Parent element with matching attribute -->
11
- * <div option:editmode="true"></div> <!-- This will be visible -->
12
- * <div option:editmode="false"></div> <!-- This will be hidden -->
13
- * </div>
14
- *
15
- * Elements with `option:` attributes will be:
16
- * - Visible if any ancestor has matching attribute
17
- * - Hidden if no ancestor has matching attribute
18
- *
19
- */
20
- import Mutation from "../utilities/mutation.js";
21
-
22
- const optionVisibilityRuleGenerator = {
23
- debug: false,
24
- styleElement: null,
25
-
26
- HIDDEN_STYLES: `
27
- visibility: hidden;
28
- pointer-events: none;
29
- width: 0;
30
- height: 0;
31
- overflow: hidden;
32
- `,
33
- VISIBLE_STYLES: `
34
- visibility: visible;
35
- pointer-events: auto;
36
- width: auto;
37
- height: auto;
38
- overflow: visible;
39
- `,
40
-
41
- STYLE_CLASS: 'option-visibility-styles',
42
-
43
- log(message, ...args) {
44
- if (this.debug) {
45
- console.log(`[OptionVisibilityRuleGenerator] ${message}`, ...args);
46
- }
47
- },
48
-
49
- findOptionAttributes() {
50
- const html = document.documentElement.outerHTML;
51
- const optionAttributes = new Set(); // Using Set for unique combinations
52
- const optionRegex = /option:([^\s"']+)=["']([^"']+)["']/g; // regex: "option:" + (anything but space and quote) + equal + quote + (anything but quote) + quote
53
-
54
- let match;
55
- while ((match = optionRegex.exec(html)) !== null) {
56
- // Create a unique key for each name-value pair
57
- const key = JSON.stringify({name: match[1], value: match[2]});
58
- optionAttributes.add(key);
59
- }
60
-
61
- // Convert back to objects
62
- return Array.from(optionAttributes).map(key => JSON.parse(key));
63
- },
64
-
65
- minifyCSS(css) {
66
- return css
67
- .replace(/\s+/g, ' ')
68
- .replace(/{\s+/g, '{')
69
- .replace(/\s+}/g, '}')
70
- .replace(/;\s+/g, ';')
71
- .replace(/:\s+/g, ':')
72
- .trim();
73
- },
74
-
75
- generateCSSRules(optionAttributes) {
76
- const rules = [];
77
-
78
- optionAttributes.forEach(({name, value}) => {
79
- const escapedValue = value.replace(/["\\]/g, '\\$&');
80
-
81
- rules.push(`
82
- [option\\:${name}="${escapedValue}"] {
83
- ${this.HIDDEN_STYLES}
84
- }
85
- `);
86
-
87
- rules.push(`
88
- [${name}="${escapedValue}"] [option\\:${name}="${escapedValue}"] {
89
- ${this.VISIBLE_STYLES}
90
- }
91
- `);
92
- });
93
-
94
- return this.minifyCSS(rules.join('\n'));
95
- },
96
-
97
- generateRules() {
98
- try {
99
- this.log('Starting rule generation');
100
-
101
- const optionAttributes = this.findOptionAttributes();
102
- this.log('Found option attributes:', optionAttributes);
103
-
104
- // Early return if no option attributes found
105
- if (optionAttributes.length === 0) {
106
- this.log('No option attributes found, skipping style creation');
107
- return;
108
- }
109
-
110
- const cssRules = this.generateCSSRules(optionAttributes);
111
- this.log('Generated CSS rules:', cssRules);
112
-
113
- // Check if we already have these exact rules
114
- const existingStyleElement = document.head.querySelector(`.${this.STYLE_CLASS}`);
115
- if (existingStyleElement && existingStyleElement.textContent.trim() === cssRules) {
116
- this.log('Rules unchanged, skipping update');
117
- return;
118
- }
119
-
120
- // Create new style element
121
- const newStyleElement = document.createElement('style');
122
- newStyleElement.className = this.STYLE_CLASS;
123
- newStyleElement.textContent = cssRules;
124
- document.head.appendChild(newStyleElement);
125
-
126
- // Remove all previous style elements
127
- document.head
128
- .querySelectorAll(`.${this.STYLE_CLASS}`)
129
- .forEach(el => {
130
- if (el !== newStyleElement) {
131
- el.remove();
132
- }
133
- });
134
-
135
- this.styleElement = newStyleElement;
136
-
137
- this.log('Rule generation complete');
138
- } catch (error) {
139
- console.error('Error generating visibility rules:', error);
140
- }
141
- },
142
-
143
- start() {
144
- Mutation.onAnyChange({
145
- selectorFilter: el => [...el.attributes].some(attr => attr.name.startsWith('option:')),
146
- debounce: 200
147
- }, () => {
148
- this.generateRules();
149
- });
150
- this.generateRules();
151
- this.log('Started observing DOM mutations');
152
- },
153
- };
154
-
155
- // Auto-export to window unless suppressed by loader
156
- if (!window.__hyperclayNoAutoExport) {
157
- window.optionVisibilityRuleGenerator = optionVisibilityRuleGenerator;
158
- window.hyperclay = window.hyperclay || {};
159
- window.hyperclay.optionVisibilityRuleGenerator = optionVisibilityRuleGenerator;
160
- window.h = window.hyperclay;
161
- }
162
-
163
- export default optionVisibilityRuleGenerator;
164
-
165
- // Auto-initialize
166
- export function init() {
167
- optionVisibilityRuleGenerator.start();
168
- }
169
-
170
- // Auto-init when module is imported
171
- init();