hyperclayjs 1.15.0 → 1.17.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 +3 -3
- package/README.md +18 -18
- package/package.json +1 -1
- package/src/communication/live-sync.js +72 -139
- package/src/core/autosave.js +14 -0
- package/src/core/savePage.js +76 -19
- package/src/core/savePageCore.js +25 -3
- package/src/core/snapshot.js +91 -2
- package/src/core/tailwindInject.js +43 -2
- package/src/core/unsavedWarning.js +15 -3
- package/src/custom-attributes/autosize.esm.js +1 -0
- package/src/custom-attributes/autosize.js +15 -12
- package/src/custom-attributes/sortable.js +1 -1
- package/src/dom-utilities/insertStyleTag.js +14 -4
- package/src/hyperclay.js +40 -5
- package/src/ui/theModal.js +1 -1
- package/src/ui/toast-hyperclay.js +14 -6
- package/src/ui/toast.js +20 -7
- package/src/utilities/autosaveDebug.js +223 -0
- package/src/utilities/loadVendorScript.js +4 -4
- package/src/utilities/mutation.js +33 -8
- package/src/vendor/hyper-morph.vendor.js +22 -0
- package/src/vendor/idiomorph.min.js +0 -20
package/src/core/savePageCore.js
CHANGED
|
@@ -26,6 +26,14 @@ import {
|
|
|
26
26
|
let saveInProgress = false;
|
|
27
27
|
const saveEndpoint = `/save/${cookie.get("currentResource")}`;
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Check if a save is currently in progress.
|
|
31
|
+
* @returns {boolean}
|
|
32
|
+
*/
|
|
33
|
+
export function isSaveInProgress() {
|
|
34
|
+
return saveInProgress;
|
|
35
|
+
}
|
|
36
|
+
|
|
29
37
|
// =============================================================================
|
|
30
38
|
// RE-EXPORTS FROM SNAPSHOT (for backwards compat)
|
|
31
39
|
// =============================================================================
|
|
@@ -102,7 +110,7 @@ export function savePage(callback = () => {}) {
|
|
|
102
110
|
|
|
103
111
|
// Add timeout - abort if server doesn't respond within 12 seconds
|
|
104
112
|
const controller = new AbortController();
|
|
105
|
-
const timeoutId = setTimeout(() => controller.abort(
|
|
113
|
+
const timeoutId = setTimeout(() => controller.abort(), 12000);
|
|
106
114
|
|
|
107
115
|
fetch(saveEndpoint, {
|
|
108
116
|
method: 'POST',
|
|
@@ -175,12 +183,18 @@ export function saveHtml(html, callback = () => {}) {
|
|
|
175
183
|
return;
|
|
176
184
|
}
|
|
177
185
|
|
|
186
|
+
// Add timeout - abort if server doesn't respond within 12 seconds
|
|
187
|
+
const controller = new AbortController();
|
|
188
|
+
const timeoutId = setTimeout(() => controller.abort(), 12000);
|
|
189
|
+
|
|
178
190
|
fetch(saveEndpoint, {
|
|
179
191
|
method: 'POST',
|
|
180
192
|
credentials: 'include',
|
|
181
|
-
body: html
|
|
193
|
+
body: html,
|
|
194
|
+
signal: controller.signal
|
|
182
195
|
})
|
|
183
196
|
.then(res => {
|
|
197
|
+
clearTimeout(timeoutId);
|
|
184
198
|
return res.json().then(data => {
|
|
185
199
|
if (!res.ok) {
|
|
186
200
|
throw new Error(data.msg || data.error || `HTTP ${res.status}: ${res.statusText}`);
|
|
@@ -194,12 +208,20 @@ export function saveHtml(html, callback = () => {}) {
|
|
|
194
208
|
}
|
|
195
209
|
})
|
|
196
210
|
.catch(err => {
|
|
211
|
+
clearTimeout(timeoutId);
|
|
197
212
|
console.error('Failed to save page:', err);
|
|
213
|
+
|
|
214
|
+
// Normalize timeout errors
|
|
215
|
+
const error = err.name === 'AbortError'
|
|
216
|
+
? new Error('Server not responding')
|
|
217
|
+
: err;
|
|
218
|
+
|
|
198
219
|
if (typeof callback === 'function') {
|
|
199
|
-
callback(
|
|
220
|
+
callback(error);
|
|
200
221
|
}
|
|
201
222
|
})
|
|
202
223
|
.finally(() => {
|
|
224
|
+
clearTimeout(timeoutId);
|
|
203
225
|
saveInProgress = false;
|
|
204
226
|
});
|
|
205
227
|
}
|
package/src/core/snapshot.js
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* │ 3a. PREPARE HOOKS │ │ 3b. DONE │
|
|
22
22
|
* │ onPrepareForSave │ │ (live-sync stops here) │
|
|
23
23
|
* │ [onbeforesave] │ │ │
|
|
24
|
-
* │ [save-
|
|
24
|
+
* │ [save-remove] │ │ → emits snapshot-ready │
|
|
25
25
|
* │ │ └─────────────────────────┘
|
|
26
26
|
* │ ✓ Used by: SAVE only │
|
|
27
27
|
* └─────────────────────────┘
|
|
@@ -102,7 +102,7 @@ function prepareCloneForSave(clone) {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// Remove elements that shouldn't be saved
|
|
105
|
-
for (const el of clone.querySelectorAll('[save-
|
|
105
|
+
for (const el of clone.querySelectorAll('[save-remove]')) {
|
|
106
106
|
el.remove();
|
|
107
107
|
}
|
|
108
108
|
|
|
@@ -114,6 +114,95 @@ function prepareCloneForSave(clone) {
|
|
|
114
114
|
return "<!DOCTYPE html>" + clone.outerHTML;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
/**
|
|
118
|
+
* Capture snapshot prepared for dirty/change comparison.
|
|
119
|
+
*
|
|
120
|
+
* Like captureForSave but also strips [save-ignore] elements.
|
|
121
|
+
* Use this for comparing current state against baselines.
|
|
122
|
+
*
|
|
123
|
+
* @returns {string} HTML string with [save-remove] and [save-ignore] stripped
|
|
124
|
+
*/
|
|
125
|
+
export function captureForComparison() {
|
|
126
|
+
// CodeMirror pages: return editor content directly (same for save and compare)
|
|
127
|
+
if (isCodeMirrorPage()) {
|
|
128
|
+
return getCodeMirrorContents();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const clone = captureSnapshot();
|
|
132
|
+
|
|
133
|
+
// Run inline [onbeforesave] handlers
|
|
134
|
+
for (const el of clone.querySelectorAll('[onbeforesave]')) {
|
|
135
|
+
new Function(el.getAttribute('onbeforesave')).call(el);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Strip before hooks (hooks see the "final" state)
|
|
139
|
+
for (const el of clone.querySelectorAll('[save-remove], [save-ignore]')) {
|
|
140
|
+
el.remove();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Run registered prepare hooks
|
|
144
|
+
for (const hook of prepareForSaveHooks) {
|
|
145
|
+
hook(clone);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return "<!DOCTYPE html>" + clone.outerHTML;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Single-capture function for both saving and comparison.
|
|
153
|
+
*
|
|
154
|
+
* Clones the DOM once, then clones that clone for comparison.
|
|
155
|
+
* More efficient than calling captureForSave() and captureForComparison() separately.
|
|
156
|
+
*
|
|
157
|
+
* @param {Object} options
|
|
158
|
+
* @param {boolean} options.emitForSync - Whether to emit snapshot-ready event (default: true)
|
|
159
|
+
* @returns {{ forSave: string, forComparison: string }}
|
|
160
|
+
*/
|
|
161
|
+
export function captureForSaveAndComparison({ emitForSync = true } = {}) {
|
|
162
|
+
// CodeMirror pages: return editor content directly, skip snapshot-ready
|
|
163
|
+
if (isCodeMirrorPage()) {
|
|
164
|
+
const contents = getCodeMirrorContents();
|
|
165
|
+
return { forSave: contents, forComparison: contents };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const clone = captureSnapshot();
|
|
169
|
+
|
|
170
|
+
// Emit for live-sync before any stripping
|
|
171
|
+
if (emitForSync) {
|
|
172
|
+
document.dispatchEvent(new CustomEvent('hyperclay:snapshot-ready', {
|
|
173
|
+
detail: { documentElement: clone }
|
|
174
|
+
}));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Run inline [onbeforesave] handlers
|
|
178
|
+
for (const el of clone.querySelectorAll('[onbeforesave]')) {
|
|
179
|
+
new Function(el.getAttribute('onbeforesave')).call(el);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Clone for comparison before stripping (cheaper than cloning live DOM)
|
|
183
|
+
const compareClone = clone.cloneNode(true);
|
|
184
|
+
|
|
185
|
+
// Save clone: strip [save-remove], then run hooks
|
|
186
|
+
for (const el of clone.querySelectorAll('[save-remove]')) {
|
|
187
|
+
el.remove();
|
|
188
|
+
}
|
|
189
|
+
for (const hook of prepareForSaveHooks) {
|
|
190
|
+
hook(clone);
|
|
191
|
+
}
|
|
192
|
+
const forSave = "<!DOCTYPE html>" + clone.outerHTML;
|
|
193
|
+
|
|
194
|
+
// Compare clone: strip both, then run hooks
|
|
195
|
+
for (const el of compareClone.querySelectorAll('[save-remove], [save-ignore]')) {
|
|
196
|
+
el.remove();
|
|
197
|
+
}
|
|
198
|
+
for (const hook of prepareForSaveHooks) {
|
|
199
|
+
hook(compareClone);
|
|
200
|
+
}
|
|
201
|
+
const forComparison = "<!DOCTYPE html>" + compareClone.outerHTML;
|
|
202
|
+
|
|
203
|
+
return { forSave, forComparison };
|
|
204
|
+
}
|
|
205
|
+
|
|
117
206
|
/**
|
|
118
207
|
* PHASE 1-4: Full pipeline for saving to server.
|
|
119
208
|
*
|
|
@@ -1,15 +1,56 @@
|
|
|
1
1
|
import { insertStyles } from '../dom-utilities/insertStyleTag.js';
|
|
2
2
|
import cookie from '../utilities/cookie.js';
|
|
3
3
|
|
|
4
|
+
function findTailwindLink(resourceName) {
|
|
5
|
+
const targetPath = `/tailwindcss/${resourceName}.css`;
|
|
6
|
+
return [...document.querySelectorAll('link[rel="stylesheet"]')]
|
|
7
|
+
.find(el => {
|
|
8
|
+
try {
|
|
9
|
+
const url = new URL(el.getAttribute('href'), location.href);
|
|
10
|
+
return url.pathname === targetPath;
|
|
11
|
+
} catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function swapTailwindLink() {
|
|
18
|
+
const currentResource = cookie.get('currentResource');
|
|
19
|
+
if (!currentResource) return;
|
|
20
|
+
|
|
21
|
+
const oldLink = findTailwindLink(currentResource);
|
|
22
|
+
if (!oldLink) return;
|
|
23
|
+
|
|
24
|
+
const newLink = document.createElement('link');
|
|
25
|
+
newLink.rel = 'stylesheet';
|
|
26
|
+
const url = new URL(oldLink.getAttribute('href'), location.href);
|
|
27
|
+
url.searchParams.set('v', Date.now());
|
|
28
|
+
newLink.href = url.href;
|
|
29
|
+
newLink.setAttribute('save-ignore', '');
|
|
30
|
+
|
|
31
|
+
oldLink.insertAdjacentElement('afterend', newLink);
|
|
32
|
+
|
|
33
|
+
newLink.onload = () => {
|
|
34
|
+
oldLink.remove();
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
setTimeout(() => {
|
|
38
|
+
if (oldLink.parentNode) {
|
|
39
|
+
oldLink.remove();
|
|
40
|
+
}
|
|
41
|
+
}, 2000);
|
|
42
|
+
}
|
|
43
|
+
|
|
4
44
|
function init() {
|
|
5
45
|
const currentResource = cookie.get('currentResource');
|
|
6
46
|
if (!currentResource) return;
|
|
7
47
|
|
|
8
48
|
const href = `/tailwindcss/${currentResource}.css`;
|
|
9
49
|
insertStyles(href, (link) => {
|
|
10
|
-
link.setAttribute('
|
|
11
|
-
link.setAttribute('mutations-ignore', '');
|
|
50
|
+
link.setAttribute('save-ignore', '');
|
|
12
51
|
});
|
|
52
|
+
|
|
53
|
+
document.addEventListener('hyperclay:save-saved', swapTailwindLink);
|
|
13
54
|
}
|
|
14
55
|
|
|
15
56
|
init();
|
|
@@ -7,19 +7,31 @@
|
|
|
7
7
|
* Works independently of autosave - no mutation observer needed during editing,
|
|
8
8
|
* just a single comparison when the user tries to leave.
|
|
9
9
|
*
|
|
10
|
+
* Both current and stored content have [save-remove] and [save-ignore] stripped,
|
|
11
|
+
* so comparison is direct with no parsing needed.
|
|
12
|
+
*
|
|
10
13
|
* Requires the 'save-system' module (automatically included as dependency).
|
|
11
14
|
*/
|
|
12
15
|
|
|
13
16
|
import { isOwner, isEditMode } from "./isAdminOfCurrentResource.js";
|
|
14
|
-
import {
|
|
17
|
+
import { captureForComparison } from "./snapshot.js";
|
|
18
|
+
import { getLastSavedContents } from "./savePage.js";
|
|
19
|
+
import { logUnloadDiffSync, preloadIfEnabled } from "../utilities/autosaveDebug.js";
|
|
20
|
+
|
|
21
|
+
// Pre-load diff library if debug mode is on (so it's ready for unload)
|
|
22
|
+
preloadIfEnabled();
|
|
15
23
|
|
|
16
24
|
window.addEventListener('beforeunload', (event) => {
|
|
17
25
|
if (!isOwner || !isEditMode) return;
|
|
18
26
|
|
|
19
|
-
|
|
27
|
+
// Compare directly - both are already stripped
|
|
28
|
+
const currentForCompare = captureForComparison();
|
|
20
29
|
const lastSaved = getLastSavedContents();
|
|
21
30
|
|
|
22
|
-
if (
|
|
31
|
+
if (currentForCompare !== lastSaved) {
|
|
32
|
+
// Debug: log what's different before showing the warning
|
|
33
|
+
logUnloadDiffSync(currentForCompare, lastSaved);
|
|
34
|
+
|
|
23
35
|
event.preventDefault();
|
|
24
36
|
event.returnValue = '';
|
|
25
37
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=new Map;function t(t){var o=e.get(t);o&&o.destroy()}function o(t){var o=e.get(t);o&&o.update()}var r=null;"undefined"==typeof window?((r=function(e){return e}).destroy=function(e){return e},r.update=function(e){return e}):((r=function(t,o){return t&&Array.prototype.forEach.call(t.length?t:[t],function(t){return function(t){if(t&&t.nodeName&&"TEXTAREA"===t.nodeName&&!e.has(t)){var o,r=null,n=window.getComputedStyle(t),i=(o=t.value,function(){a({testForHeightReduction:""===o||!t.value.startsWith(o),restoreTextAlign:null}),o=t.value}),l=function(o){t.removeEventListener("autosize:destroy",l),t.removeEventListener("autosize:update",s),t.removeEventListener("input",i),window.removeEventListener("resize",s),Object.keys(o).forEach(function(e){return t.style[e]=o[e]}),e.delete(t)}.bind(t,{height:t.style.height,resize:t.style.resize,textAlign:t.style.textAlign,overflowY:t.style.overflowY,overflowX:t.style.overflowX,wordWrap:t.style.wordWrap});t.addEventListener("autosize:destroy",l),t.addEventListener("autosize:update",s),t.addEventListener("input",i),window.addEventListener("resize",s),t.style.overflowX="hidden",t.style.wordWrap="break-word",e.set(t,{destroy:l,update:s}),s()}function a(e){var o,i,l=e.restoreTextAlign,s=void 0===l?null:l,d=e.testForHeightReduction,u=void 0===d||d,c=n.overflowY;if(0!==t.scrollHeight&&("vertical"===n.resize?t.style.resize="none":"both"===n.resize&&(t.style.resize="horizontal"),u&&(o=function(e){for(var t=[];e&&e.parentNode&&e.parentNode instanceof Element;)e.parentNode.scrollTop&&t.push([e.parentNode,e.parentNode.scrollTop]),e=e.parentNode;return function(){return t.forEach(function(e){var t=e[0],o=e[1];t.style.scrollBehavior="auto",t.scrollTop=o,t.style.scrollBehavior=null})}}(t),t.style.height=""),i="content-box"===n.boxSizing?t.scrollHeight-(parseFloat(n.paddingTop)+parseFloat(n.paddingBottom)):t.scrollHeight+parseFloat(n.borderTopWidth)+parseFloat(n.borderBottomWidth),"none"!==n.maxHeight&&i>parseFloat(n.maxHeight)?("hidden"===n.overflowY&&(t.style.overflow="scroll"),i=parseFloat(n.maxHeight)):"hidden"!==n.overflowY&&(t.style.overflow="hidden"),t.style.height=i+"px",s&&(t.style.textAlign=s),o&&o(),r!==i&&(t.dispatchEvent(new Event("autosize:resized",{bubbles:!0})),r=i),c!==n.overflow&&!s)){var v=n.textAlign;"hidden"===n.overflow&&(t.style.textAlign="start"===v?"end":"start"),a({restoreTextAlign:v,testForHeightReduction:!0})}}function s(){a({testForHeightReduction:!0,restoreTextAlign:null})}}(t)}),t}).destroy=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],t),e},r.update=function(e){return e&&Array.prototype.forEach.call(e.length?e:[e],o),e});var n=r;export default n;
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
target.style.overflowY = 'hidden';
|
|
6
|
-
target.style.height = 'auto';
|
|
7
|
-
target.style.height = target.scrollHeight + 'px';
|
|
8
|
-
}
|
|
9
|
-
});
|
|
1
|
+
import autosize from './autosize.esm.js';
|
|
2
|
+
|
|
3
|
+
function init() {
|
|
4
|
+
document.querySelectorAll('textarea[autosize]').forEach(autosize);
|
|
10
5
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
const observer = new MutationObserver(mutations => {
|
|
7
|
+
mutations.forEach(mutation => {
|
|
8
|
+
mutation.addedNodes.forEach(node => {
|
|
9
|
+
if (node.nodeType === 1) {
|
|
10
|
+
if (node.matches?.('textarea[autosize]')) autosize(node);
|
|
11
|
+
node.querySelectorAll?.('textarea[autosize]').forEach(autosize);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
});
|
|
14
15
|
});
|
|
16
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
15
17
|
}
|
|
18
|
+
|
|
16
19
|
export { init };
|
|
17
20
|
export default init;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
- e.g. <ul sortable onsorted="console.log('Items reordered!')"></ul>
|
|
12
12
|
|
|
13
13
|
This wrapper conditionally loads the full Sortable.js vendor script (~118KB)
|
|
14
|
-
only when in edit mode. The script is injected with save-
|
|
14
|
+
only when in edit mode. The script is injected with save-remove so it's
|
|
15
15
|
stripped from the page before saving.
|
|
16
16
|
|
|
17
17
|
*/
|
|
@@ -62,14 +62,24 @@ function insertStyles(nameOrHref, cssOrCallback, callback) {
|
|
|
62
62
|
const href = nameOrHref;
|
|
63
63
|
const cb = typeof cssOrCallback === 'function' ? cssOrCallback : undefined;
|
|
64
64
|
|
|
65
|
-
//
|
|
66
|
-
const
|
|
65
|
+
// Helper to get base URL without query params (for comparison)
|
|
66
|
+
const getBaseUrl = (url) => {
|
|
67
|
+
try {
|
|
68
|
+
const parsed = new URL(url, window.location.href);
|
|
69
|
+
return parsed.origin + parsed.pathname;
|
|
70
|
+
} catch {
|
|
71
|
+
return url;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Normalize href to full URL path (without query params) for comparison
|
|
76
|
+
const normalizedHref = getBaseUrl(href);
|
|
67
77
|
|
|
68
|
-
// Find all links with matching normalized path
|
|
78
|
+
// Find all links with matching normalized path (ignoring query params like ?v=)
|
|
69
79
|
const existingLinks = [...document.querySelectorAll('link[rel="stylesheet"]')]
|
|
70
80
|
.filter(el => {
|
|
71
81
|
try {
|
|
72
|
-
return
|
|
82
|
+
return getBaseUrl(el.getAttribute('href')) === normalizedHref;
|
|
73
83
|
} catch {
|
|
74
84
|
return false;
|
|
75
85
|
}
|
package/src/hyperclay.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HyperclayJS v1.
|
|
2
|
+
* HyperclayJS v1.17.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.
|
|
@@ -56,7 +56,7 @@ const MODULE_PATHS = {
|
|
|
56
56
|
"all-js": "./dom-utilities/All.js",
|
|
57
57
|
"style-injection": "./dom-utilities/insertStyleTag.js",
|
|
58
58
|
"form-data": "./dom-utilities/getDataFromForm.js",
|
|
59
|
-
"
|
|
59
|
+
"hyper-morph": "./vendor/hyper-morph.vendor.js",
|
|
60
60
|
"slugify": "./string-utilities/slugify.js",
|
|
61
61
|
"copy-to-clipboard": "./string-utilities/copy-to-clipboard.js",
|
|
62
62
|
"query-params": "./string-utilities/query.js",
|
|
@@ -123,7 +123,6 @@ const PRESETS = {
|
|
|
123
123
|
"onaftersave",
|
|
124
124
|
"dialogs",
|
|
125
125
|
"toast",
|
|
126
|
-
"toast-hyperclay",
|
|
127
126
|
"the-modal",
|
|
128
127
|
"mutation",
|
|
129
128
|
"nearest",
|
|
@@ -136,7 +135,7 @@ const PRESETS = {
|
|
|
136
135
|
"all-js",
|
|
137
136
|
"style-injection",
|
|
138
137
|
"form-data",
|
|
139
|
-
"
|
|
138
|
+
"hyper-morph",
|
|
140
139
|
"slugify",
|
|
141
140
|
"copy-to-clipboard",
|
|
142
141
|
"query-params",
|
|
@@ -162,6 +161,7 @@ const EDIT_MODE_ONLY = new Set([
|
|
|
162
161
|
"sortable",
|
|
163
162
|
"onaftersave",
|
|
164
163
|
"cache-bust",
|
|
164
|
+
"hyper-morph",
|
|
165
165
|
"file-upload",
|
|
166
166
|
"live-sync",
|
|
167
167
|
"tailwind-inject"
|
|
@@ -276,7 +276,8 @@ export const All = window.hyperclayModules['all-js']?.All ?? window.hyperclayMod
|
|
|
276
276
|
export const insertStyles = window.hyperclayModules['style-injection']?.insertStyles ?? window.hyperclayModules['style-injection']?.default;
|
|
277
277
|
export const insertStyleTag = window.hyperclayModules['style-injection']?.insertStyleTag ?? window.hyperclayModules['style-injection']?.default;
|
|
278
278
|
export const getDataFromForm = window.hyperclayModules['form-data']?.getDataFromForm ?? window.hyperclayModules['form-data']?.default;
|
|
279
|
-
export const
|
|
279
|
+
export const HyperMorph = window.hyperclayModules['hyper-morph']?.HyperMorph ?? window.hyperclayModules['hyper-morph']?.default;
|
|
280
|
+
export const morph = window.hyperclayModules['hyper-morph']?.morph ?? window.hyperclayModules['hyper-morph']?.default;
|
|
280
281
|
export const slugify = window.hyperclayModules['slugify']?.slugify ?? window.hyperclayModules['slugify']?.default;
|
|
281
282
|
export const copyToClipboard = window.hyperclayModules['copy-to-clipboard']?.copyToClipboard ?? window.hyperclayModules['copy-to-clipboard']?.default;
|
|
282
283
|
export const query = window.hyperclayModules['query-params']?.query ?? window.hyperclayModules['query-params']?.default;
|
|
@@ -286,3 +287,37 @@ export const uploadFile = window.hyperclayModules['file-upload']?.uploadFile ??
|
|
|
286
287
|
export const createFile = window.hyperclayModules['file-upload']?.createFile ?? window.hyperclayModules['file-upload']?.default;
|
|
287
288
|
export const uploadFileBasic = window.hyperclayModules['file-upload']?.uploadFileBasic ?? window.hyperclayModules['file-upload']?.default;
|
|
288
289
|
export const liveSync = window.hyperclayModules['live-sync']?.liveSync ?? window.hyperclayModules['live-sync']?.default;
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Enable debug logging across all hyperclay modules that support it.
|
|
293
|
+
* @param {boolean} [enabled=true] - Whether to enable or disable debug logging
|
|
294
|
+
*/
|
|
295
|
+
export function setDebug(enabled = true) {
|
|
296
|
+
const modules = [
|
|
297
|
+
{ name: 'Mutation', obj: window.hyperclayModules['mutation']?.default },
|
|
298
|
+
{ name: 'LiveSync', obj: window.hyperclayModules['live-sync']?.liveSync },
|
|
299
|
+
{ name: 'OptionVisibility', obj: window.hyperclayModules['option-visibility']?.default },
|
|
300
|
+
];
|
|
301
|
+
|
|
302
|
+
const enabledModules = [];
|
|
303
|
+
for (const { name, obj } of modules) {
|
|
304
|
+
if (obj && 'debug' in obj) {
|
|
305
|
+
obj.debug = enabled;
|
|
306
|
+
enabledModules.push(name);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
console.log(`[hyperclay] Debug ${enabled ? 'enabled' : 'disabled'} for:`, enabledModules.join(', ') || 'no modules found');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Export debug to window.hyperclay
|
|
314
|
+
if (!window.__hyperclayNoAutoExport) {
|
|
315
|
+
window.hyperclay = window.hyperclay || {};
|
|
316
|
+
window.hyperclay.debug = setDebug;
|
|
317
|
+
window.h = window.hyperclay;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Auto-enable debug for all modules if ?debug=true was passed
|
|
321
|
+
if (debug) {
|
|
322
|
+
setDebug(true);
|
|
323
|
+
}
|
package/src/ui/theModal.js
CHANGED
|
@@ -595,7 +595,7 @@ const themodal = (() => {
|
|
|
595
595
|
const themodalMain = {
|
|
596
596
|
isShowing: false,
|
|
597
597
|
open() {
|
|
598
|
-
document.body.insertAdjacentHTML("afterbegin", "<div save-
|
|
598
|
+
document.body.insertAdjacentHTML("afterbegin", "<div save-remove class='micromodal-parent'>" + modalCss + modalHtml + "</div>");
|
|
599
599
|
|
|
600
600
|
const modalOverlayElem = document.querySelector(".micromodal__overlay");
|
|
601
601
|
const modalContentElem = document.querySelector(".micromodal__content");
|
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Toast Hyperclay -
|
|
2
|
+
* Toast Hyperclay - Configure toast() to use Hyperclay platform styling
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* When this module is loaded, it overrides the default toast styling so that
|
|
5
|
+
* all toast() calls (including from save-toast) use Hyperclay styling.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* the Hyperclay platform for backward compatibility.
|
|
7
|
+
* Also provides toastHyperclay() for backward compatibility.
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
10
|
import {
|
|
12
11
|
toastCore,
|
|
13
12
|
injectToastStyles,
|
|
13
|
+
setToastTheme,
|
|
14
14
|
hyperclayStyles,
|
|
15
15
|
hyperclayTemplates,
|
|
16
16
|
hyperclayIcons
|
|
17
17
|
} from './toast.js';
|
|
18
18
|
|
|
19
|
-
//
|
|
19
|
+
// Configure the base toast() to use hyperclay styling
|
|
20
|
+
setToastTheme({
|
|
21
|
+
styles: hyperclayStyles,
|
|
22
|
+
templates: hyperclayTemplates,
|
|
23
|
+
icons: hyperclayIcons,
|
|
24
|
+
theme: 'hyperclay'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Toast function with Hyperclay styling (kept for backward compatibility)
|
|
20
28
|
function toastHyperclay(message, messageType = "success") {
|
|
21
29
|
injectToastStyles(hyperclayStyles, 'hyperclay');
|
|
22
30
|
toastCore(message, messageType, {
|
package/src/ui/toast.js
CHANGED
|
@@ -185,13 +185,26 @@ export const hyperclayStyles = `
|
|
|
185
185
|
// Track which theme styles have been injected
|
|
186
186
|
const injectedThemes = new Set();
|
|
187
187
|
|
|
188
|
+
// Global toast configuration (can be overridden by toast-hyperclay module)
|
|
189
|
+
let toastConfig = {
|
|
190
|
+
styles: modernStyles,
|
|
191
|
+
templates: defaultTemplates,
|
|
192
|
+
icons: defaultIcons,
|
|
193
|
+
theme: 'modern'
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// Allow other modules (like toast-hyperclay) to override default toast styling
|
|
197
|
+
export function setToastTheme(config) {
|
|
198
|
+
Object.assign(toastConfig, config);
|
|
199
|
+
}
|
|
200
|
+
|
|
188
201
|
// Helper function to inject styles for a theme (additive, not replacing)
|
|
189
202
|
export function injectToastStyles(styles, theme) {
|
|
190
203
|
if (injectedThemes.has(theme)) return;
|
|
191
204
|
|
|
192
205
|
const styleSheet = document.createElement('style');
|
|
193
206
|
styleSheet.className = `toast-styles-${theme}`;
|
|
194
|
-
styleSheet.setAttribute('save-
|
|
207
|
+
styleSheet.setAttribute('save-remove', '');
|
|
195
208
|
styleSheet.textContent = styles;
|
|
196
209
|
document.head.appendChild(styleSheet);
|
|
197
210
|
|
|
@@ -210,7 +223,7 @@ export function toastCore(message, messageType = "success", config = {}) {
|
|
|
210
223
|
toastContainer = document.createElement('div');
|
|
211
224
|
toastContainer.className = 'toast-container';
|
|
212
225
|
toastContainer.setAttribute('data-toast-theme', theme);
|
|
213
|
-
toastContainer.setAttribute('save-
|
|
226
|
+
toastContainer.setAttribute('save-remove', '');
|
|
214
227
|
document.body.append(toastContainer);
|
|
215
228
|
}
|
|
216
229
|
|
|
@@ -242,13 +255,13 @@ export function toastCore(message, messageType = "success", config = {}) {
|
|
|
242
255
|
}, 6600);
|
|
243
256
|
}
|
|
244
257
|
|
|
245
|
-
// Main toast function - uses modern
|
|
258
|
+
// Main toast function - uses configured theme (default: modern, or hyperclay if toast-hyperclay loaded)
|
|
246
259
|
function toast(message, messageType = "success") {
|
|
247
|
-
injectToastStyles(
|
|
260
|
+
injectToastStyles(toastConfig.styles, toastConfig.theme);
|
|
248
261
|
toastCore(message, messageType, {
|
|
249
|
-
templates:
|
|
250
|
-
icons:
|
|
251
|
-
theme:
|
|
262
|
+
templates: toastConfig.templates,
|
|
263
|
+
icons: toastConfig.icons,
|
|
264
|
+
theme: toastConfig.theme
|
|
252
265
|
});
|
|
253
266
|
}
|
|
254
267
|
|