hyperclayjs 1.29.1 → 1.31.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/README.md CHANGED
@@ -44,11 +44,16 @@ npm install hyperclayjs
44
44
 
45
45
  ```javascript
46
46
  // Import specific modules
47
- import { savePage } from 'hyperclayjs/core/savePage.js';
48
- import toast from 'hyperclayjs/ui/toast.js';
47
+ import { savePage } from 'hyperclayjs/core/savePage';
48
+ import toast from 'hyperclayjs/ui/toast';
49
+ ```
50
+
51
+ To load a full preset, use the CDN loader with `?preset=standard`:
49
52
 
50
- // Or use presets
51
- import 'hyperclayjs/presets/standard.js';
53
+ ```html
54
+ <script type="module">
55
+ await import('https://cdn.jsdelivr.net/npm/hyperclayjs@1/src/hyperclay.js?preset=standard');
56
+ </script>
52
57
  ```
53
58
 
54
59
  ## Available Modules
@@ -65,7 +70,7 @@ import 'hyperclayjs/presets/standard.js';
65
70
  | save-core | 11.5KB | Basic save function only - hyperclay.savePage() |
66
71
  | save-system | 15.4KB | CMD+S, [trigger-save] button, savestatus attribute |
67
72
  | save-toast | 0.9KB | Toast notifications for save events |
68
- | snapshot | 11.5KB | Source of truth for page state - captures DOM snapshots for save and sync |
73
+ | snapshot | 11.9KB | Source of truth for page state - captures DOM snapshots for save and sync |
69
74
  | unsaved-warning | 1.3KB | Warn before leaving page with unsaved changes |
70
75
 
71
76
  ### Custom Attributes (HTML enhancements)
@@ -87,6 +92,7 @@ import 'hyperclayjs/presets/standard.js';
87
92
  | Module | Size | Description |
88
93
  |--------|------|-------------|
89
94
  | dialogs | 8.5KB | ask(), consent(), tell(), snippet() dialog functions |
95
+ | quickcrop | 16.8KB | Image-crop modal for upload flows - quickcrop(file) returns a cropped Blob; uses themodal when available |
90
96
  | the-modal | 23KB | Full modal window creation system - window.theModal |
91
97
  | toast | 15.8KB | Success/error message notifications, toast(msg, msgType) |
92
98
 
@@ -97,7 +103,7 @@ import 'hyperclayjs/presets/standard.js';
97
103
  | cache-bust | 0.6KB | Cache-bust href/src attributes |
98
104
  | cookie | 1.4KB | Cookie management (often auto-included) |
99
105
  | debounce | 0.7KB | Function debouncing |
100
- | mutation | 15.6KB | DOM mutation observation (often auto-included) |
106
+ | mutation | 16.4KB | DOM mutation observation (often auto-included) |
101
107
  | nearest | 3.4KB | Find nearest elements (often auto-included) |
102
108
  | throttle | 1.3KB | Function throttling |
103
109
 
@@ -123,29 +129,46 @@ import 'hyperclayjs/presets/standard.js';
123
129
  | Module | Size | Description |
124
130
  |--------|------|-------------|
125
131
  | file-upload | 11.3KB | File upload with progress |
126
- | live-sync | 25.3KB | Real-time DOM sync across browsers |
132
+ | live-sync | 25.9KB | Real-time DOM sync across browsers |
127
133
  | send-message | 1.3KB | Message sending utility |
128
134
 
135
+ ### Data & Undo (Page data and undo history)
136
+
137
+ | Module | Size | Description |
138
+ |--------|------|-------------|
139
+ | data | 0.5KB | Read/write structured data from the DOM via named rules tags — window.hyperclay.extractData() / applyData(). Backs the /_/api endpoint shape. |
140
+ | undo | 0.8KB | DOM-state undo/redo via MutationObserver inverse-op replay. Cmd+Z works out of the box; integrates with hypercms via window.hyperclay.undo. |
141
+
129
142
  ### Vendor Libraries (Third-party libraries)
130
143
 
131
144
  | Module | Size | Description |
132
145
  |--------|------|-------------|
133
146
  | hyper-morph | 18.8KB | DOM morphing with content-based element matching |
134
- | hypercms | 72.4KB | Live edit-in-place CMS sidebar driven by a hyper-html-api rules tag. Pairs with [sortable] and [hyper-morph]. |
147
+ | hypercms | 127KB | Live edit-in-place CMS sidebar driven by a hyper-html-api rules tag. Pairs with [sortable] and [hyper-morph]. |
135
148
 
136
149
  ## Presets
137
150
 
138
- ### Minimal (~62.3KB)
151
+ ### Minimal (~62.7KB)
139
152
  Essential features for basic editing
140
153
 
141
154
  **Modules:** `save-core`, `snapshot`, `save-system`, `edit-mode-helpers`, `toast`, `save-toast`, `export-to-window`, `view-mode-excludes-edit-modules`
142
155
 
143
- ### Standard (~89.2KB)
156
+ ### Standard (~90.1KB)
144
157
  Standard feature set for most use cases
145
158
 
146
- **Modules:** `save-core`, `snapshot`, `save-system`, `unsaved-warning`, `edit-mode-helpers`, `persist`, `option-visibility`, `event-attrs`, `dom-helpers`, `toast`, `save-toast`, `export-to-window`, `view-mode-excludes-edit-modules`
159
+ **Modules:** `save-core`, `snapshot`, `save-system`, `unsaved-warning`, `edit-mode-helpers`, `persist`, `option-visibility`, `event-attrs`, `dom-helpers`, `data`, `toast`, `save-toast`, `export-to-window`, `view-mode-excludes-edit-modules`
160
+
161
+ ### CMS (~204.7KB)
162
+ Visual CMS editing for rules-tag pages: hypercms sidebar, undo, drag-reorder, save
163
+
164
+ **Modules:** `save-core`, `snapshot`, `save-system`, `unsaved-warning`, `toast`, `save-toast`, `mutation`, `hypercms`, `sortable`, `undo`, `export-to-window`, `view-mode-excludes-edit-modules`
147
165
 
148
- ### Everything (~323.5KB)
166
+ ### Smooth Sailing (~350.5KB)
167
+ Everything, without gotchas
168
+
169
+ **Modules:** `save-core`, `save-system`, `unsaved-warning`, `save-toast`, `edit-mode-helpers`, `persist`, `snapshot`, `option-visibility`, `edit-mode`, `event-attrs`, `ajax-elements`, `sortable`, `movable`, `dom-helpers`, `input-helpers`, `onaftersave`, `save-freeze`, `dialogs`, `quickcrop`, `toast`, `the-modal`, `mutation`, `nearest`, `cookie`, `throttle`, `debounce`, `dom-ready`, `window-load`, `all-js`, `style-injection`, `form-data`, `hypercms`, `undo`, `data`, `slugify`, `copy-to-clipboard`, `query-params`, `behavior-collector`, `send-message`, `file-upload`, `refetch-on-save`, `export-to-window`, `view-mode-excludes-edit-modules`
170
+
171
+ ### Everything (~397.2KB)
149
172
  All available features
150
173
 
151
174
  Includes all available modules across all categories.
@@ -194,17 +217,17 @@ The configurator shows:
194
217
 
195
218
  ```
196
219
  hyperclayjs/
197
- ├── hyperclay.js # Self-detecting module loader
198
- ├── core/ # Core hyperclay features
199
- ├── custom-attributes/ # HTML attribute enhancements
200
- ├── ui/ # UI components (toast, modals, prompts)
201
- ├── utilities/ # General utilities (mutation, cookie, etc.)
202
- ├── dom-utilities/ # DOM manipulation helpers
203
- ├── string-utilities/ # String manipulation tools
204
- ├── communication/ # File upload and messaging
205
- ├── vendor/ # Third-party libraries (Sortable.js, etc.)
220
+ ├── src/hyperclay.js # Self-detecting module loader
221
+ ├── src/core/ # Core hyperclay features
222
+ ├── src/custom-attributes/ # HTML attribute enhancements
223
+ ├── src/ui/ # UI components (toast, modals, prompts)
224
+ ├── src/utilities/ # General utilities (mutation, cookie, etc.)
225
+ ├── src/dom-utilities/ # DOM manipulation helpers
226
+ ├── src/string-utilities/ # String manipulation tools
227
+ ├── src/communication/ # File upload and messaging
228
+ ├── src/vendor/ # Third-party libraries (Sortable.js, etc.)
206
229
  ├── scripts/ # Build and generation scripts
207
- └── starter-kit-configurator.html # Interactive configurator
230
+ └── website/config.html # Interactive configurator
208
231
  ```
209
232
 
210
233
  ### Setup
@@ -244,10 +267,10 @@ The configurator dynamically loads this file to always show accurate information
244
267
 
245
268
  ## Browser Support
246
269
 
247
- - Chrome 89+
248
- - Firefox 89+
249
- - Safari 15.4+
250
- - Edge 89+
270
+ - Chrome 90+
271
+ - Firefox 88+
272
+ - Safari 14+
273
+ - Edge 90+
251
274
 
252
275
  The loader uses ES modules with top-level await. Use `await import()` to ensure modules finish loading before your code runs.
253
276
 
@@ -266,6 +289,11 @@ hyperclay.initHyperclaySaveButton(); // Looks for [trigger-save]
266
289
  hyperclay.initSaveKeyboardShortcut(); // CMD/CTRL+S
267
290
  ```
268
291
 
292
+ Any element with the `trigger-save` attribute saves the page on click. For
293
+ viewers who can't save (not the owner / not in edit mode), the loader shows a
294
+ toast instead — "You're not the owner, changes are local only" — even on pages
295
+ that exclude the save modules via `view-mode-excludes-edit-modules`.
296
+
269
297
  ### Toast Notifications
270
298
 
271
299
  ```javascript
@@ -352,7 +380,7 @@ myFeature();
352
380
  <script src="/public/js/hyperclay.js?preset=standard" type="module"></script>
353
381
 
354
382
  <!-- Or specific features -->
355
- <script src="/public/js/hyperclay.js?features=save,edit-mode-helpers,toast" type="module"></script>
383
+ <script src="/public/js/hyperclay.js?features=save-core,edit-mode-helpers,toast" type="module"></script>
356
384
  ```
357
385
 
358
386
  ## Contributing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperclayjs",
3
- "version": "1.29.1",
3
+ "version": "1.31.0",
4
4
  "description": "Modular JavaScript library for building interactive malleable HTML files with Hyperclay",
5
5
  "type": "module",
6
6
  "main": "src/hyperclay.js",
@@ -10,7 +10,7 @@
10
10
  * │
11
11
  * ▼
12
12
  * ┌─────────────────────────────────────────────────────────┐
13
- * │ 2. SEND POST html to /live-sync/save
13
+ * │ 2. SEND POST html to /_/live-sync/save
14
14
  * │ (debounced, skip if unchanged) │
15
15
  * └─────────────────────────────────────────────────────────┘
16
16
  * │
@@ -343,7 +343,7 @@ class LiveSync {
343
343
  if (this.isDestroyed) return;
344
344
 
345
345
  const pageUrl = encodeURIComponent(window.location.href);
346
- const url = `/live-sync/stream?page-url=${pageUrl}`;
346
+ const url = `/_/live-sync/stream?page-url=${pageUrl}`;
347
347
  this.sse = new EventSource(url);
348
348
 
349
349
  this.sse.onopen = () => {
@@ -457,7 +457,7 @@ class LiveSync {
457
457
 
458
458
  this._log(`Sending update (HTML length: ${html.length}, lastHtml length: ${this.lastHtml?.length || 0})`);
459
459
 
460
- fetch('/live-sync/save', {
460
+ fetch('/_/live-sync/save', {
461
461
  method: 'POST',
462
462
  headers: { 'Content-Type': 'application/json', 'Page-URL': window.location.href },
463
463
  body: JSON.stringify({
@@ -646,6 +646,17 @@ class LiveSync {
646
646
  // receive time (in onmessage) so the staleness check covers own-save
647
647
  // echoes even when they don't reach this point.
648
648
  this.lastHtml = html;
649
+
650
+ // Announce that a remote morph just landed, so document-level listeners
651
+ // that are deaf to Mutation.pause (e.g. the hypercms form panel) can
652
+ // re-sync. Fires only on a successful apply, and only for genuine remote
653
+ // morphs — own-sender and stale-seq echoes are filtered upstream in
654
+ // onmessage before applyUpdate is ever called. Covers every SSE morph
655
+ // source (peer edit, version restore, body-swap) since they all funnel
656
+ // through this single choke point.
657
+ document.dispatchEvent(new CustomEvent('hyperclay:livesync-applied', {
658
+ detail: { seq }
659
+ }));
649
660
  } finally {
650
661
  this._log('applyUpdate - morph complete, resuming mutations');
651
662
  Mutation.resume();
@@ -19,7 +19,7 @@ function sendMessage(eventOrObj, successMessage = "Successfully sent", callback)
19
19
 
20
20
  data.behaviorData = behaviorCollector.getData();
21
21
 
22
- return fetch("/message", {
22
+ return fetch("/_/message", {
23
23
  method: 'POST',
24
24
  headers: { 'Content-Type': 'application/json' },
25
25
  body: JSON.stringify(data)
@@ -208,7 +208,7 @@ function createFileFromData(fileName, fileBody, onComplete = () => {}) {
208
208
 
209
209
  function uploadFileData(fileName, fileBody, progressCallback, extraData = {}) {
210
210
  const xhr = new XMLHttpRequest();
211
- xhr.open("POST", "/upload", true);
211
+ xhr.open("POST", "/_/upload", true);
212
212
  xhr.setRequestHeader("Content-Type", "application/json");
213
213
  xhr.setRequestHeader("Page-URL", window.location.href);
214
214
 
@@ -23,7 +23,7 @@ import {
23
23
  // =============================================================================
24
24
 
25
25
  let saveInProgress = false;
26
- const saveEndpoint = '/save';
26
+ const saveEndpoint = '/_/save';
27
27
 
28
28
  /**
29
29
  * Check if a save is currently in progress.
@@ -36,6 +36,8 @@
36
36
  * └─────────────────────────┘
37
37
  */
38
38
 
39
+ import { stripExtensionNoise } from '../utilities/extension-noise.js';
40
+
39
41
  // =============================================================================
40
42
  // HOOK REGISTRIES
41
43
  // =============================================================================
@@ -109,6 +111,11 @@ export function captureSnapshot() {
109
111
  el.remove();
110
112
  }
111
113
 
114
+ // Browser-extension noise (password-manager menus, Grammarly overlays, and
115
+ // marker attributes on real inputs) is not page content. Drop it from every
116
+ // snapshot so it never reaches a save, a comparison, or a live-sync broadcast.
117
+ stripExtensionNoise(clone);
118
+
112
119
  return clone;
113
120
  }
114
121
 
package/src/data.js ADDED
@@ -0,0 +1,10 @@
1
+ // Re-exports the vendored hyper-html-api data sugar as window.hyperclay
2
+ // .extractData / .applyData. The vendor file's wrapper attaches the public API
3
+ // to window.hyperclay during evaluation (see
4
+ // hyper-html-api/scripts/copy-to-hyperclayjs.js), so importing it is enough.
5
+ // No import-map entry needed — relative vendor import, same as undo.js. This is
6
+ // a core read/write primitive, so it is NOT edit-mode-only.
7
+
8
+ import { extractData, applyData, engine } from './vendor/hyper-html-api.vendor.js'
9
+
10
+ export { extractData, applyData, engine }
package/src/hyperclay.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * DO NOT EDIT THIS FILE DIRECTLY — it is generated from build/hyperclay.template.js
3
3
  *
4
- * HyperclayJS v1.29.1 - Minimal Browser-Native Loader
4
+ * HyperclayJS v1.31.0 - Minimal Browser-Native Loader
5
5
  *
6
6
  * Modules auto-init when imported (no separate init call needed).
7
7
  * Include `export-to-window` feature to export to window.hyperclay.
@@ -46,6 +46,7 @@ const MODULE_PATHS = {
46
46
  "onaftersave": "./custom-attributes/onaftersave.js",
47
47
  "save-freeze": "./custom-attributes/saveFreeze.js",
48
48
  "dialogs": "./ui/prompts.js",
49
+ "quickcrop": "./ui/quickcrop.js",
49
50
  "toast": "./ui/toast.js",
50
51
  "toast-hyperclay": "./ui/toast-hyperclay.js",
51
52
  "the-modal": "./ui/theModal.js",
@@ -63,6 +64,7 @@ const MODULE_PATHS = {
63
64
  "hyper-morph": "./vendor/hyper-morph.vendor.js",
64
65
  "hypercms": "./vendor/hypercms.vendor.js",
65
66
  "undo": "./undo.js",
67
+ "data": "./data.js",
66
68
  "slugify": "./string-utilities/slugify.js",
67
69
  "copy-to-clipboard": "./string-utilities/copy-to-clipboard.js",
68
70
  "query-params": "./string-utilities/query.js",
@@ -101,12 +103,31 @@ const PRESETS = {
101
103
  "option-visibility",
102
104
  "event-attrs",
103
105
  "dom-helpers",
106
+ "data",
104
107
  "toast",
105
108
  "save-toast",
106
109
  "export-to-window",
107
110
  "view-mode-excludes-edit-modules"
108
111
  ]
109
112
  },
113
+ "cms": {
114
+ "name": "CMS",
115
+ "description": "Visual CMS editing for rules-tag pages: hypercms sidebar, undo, drag-reorder, save",
116
+ "modules": [
117
+ "save-core",
118
+ "snapshot",
119
+ "save-system",
120
+ "unsaved-warning",
121
+ "toast",
122
+ "save-toast",
123
+ "mutation",
124
+ "hypercms",
125
+ "sortable",
126
+ "undo",
127
+ "export-to-window",
128
+ "view-mode-excludes-edit-modules"
129
+ ]
130
+ },
110
131
  "smooth-sailing": {
111
132
  "name": "Smooth Sailing",
112
133
  "description": "Everything, without gotchas",
@@ -129,6 +150,7 @@ const PRESETS = {
129
150
  "onaftersave",
130
151
  "save-freeze",
131
152
  "dialogs",
153
+ "quickcrop",
132
154
  "toast",
133
155
  "the-modal",
134
156
  "mutation",
@@ -143,6 +165,7 @@ const PRESETS = {
143
165
  "form-data",
144
166
  "hypercms",
145
167
  "undo",
168
+ "data",
146
169
  "slugify",
147
170
  "copy-to-clipboard",
148
171
  "query-params",
@@ -177,6 +200,7 @@ const PRESETS = {
177
200
  "onaftersave",
178
201
  "save-freeze",
179
202
  "dialogs",
203
+ "quickcrop",
180
204
  "toast",
181
205
  "the-modal",
182
206
  "mutation",
@@ -193,6 +217,7 @@ const PRESETS = {
193
217
  "hyper-morph",
194
218
  "hypercms",
195
219
  "undo",
220
+ "data",
196
221
  "slugify",
197
222
  "copy-to-clipboard",
198
223
  "query-params",
@@ -267,8 +292,14 @@ if (viewModeExcludesEdit) {
267
292
  }
268
293
  }
269
294
 
270
- // Modules that extend prototypes must load before modules that execute user code
271
- const LOAD_FIRST = new Set(['dom-helpers', 'all-js']);
295
+ // Modules that extend prototypes must load before modules that execute user code.
296
+ // 'mutation' is here so it evaluates (and installs window.hyperclay.Mutation) in
297
+ // the first wave, before hypercms in the rest wave: hypercms reads
298
+ // window.hyperclay.Mutation at evaluation/auto-open time, so loading mutation
299
+ // first makes ?cms=true auto-open deterministic instead of racing. Safe because
300
+ // export-to-window is awaited before the first wave, so mutation's window-install
301
+ // is not suppressed when it runs.
302
+ const LOAD_FIRST = new Set(['dom-helpers', 'all-js', 'mutation']);
272
303
 
273
304
  // export-to-window flips the flag, so it must load before other modules
274
305
  const LOAD_BEFORE_ALL = 'export-to-window';
@@ -308,6 +339,20 @@ try {
308
339
 
309
340
  if (debug) console.log('HyperclayJS: Ready');
310
341
 
342
+ // View-mode save notice: in edit mode, save-system's own [trigger-save]
343
+ // listener handles clicks. For everyone else that listener is either never
344
+ // installed (its init is edit-mode gated) or not loaded at all
345
+ // (view-mode-excludes-edit-modules), so a save click would silently do
346
+ // nothing. Catch it here and say why. Imports happen at click time, so this
347
+ // costs nothing on page load.
348
+ document.addEventListener('click', async (event) => {
349
+ if (!event.target.closest?.('[trigger-save]')) return;
350
+ const { isEditMode } = await import(`${baseUrl}/core/isAdminOfCurrentResource.js`);
351
+ if (isEditMode) return;
352
+ const { default: toast } = await import(`${baseUrl}/ui/toast.js`);
353
+ toast("You're not the owner, changes are local only", "warning");
354
+ });
355
+
311
356
  // ES module exports - allows destructuring from import()
312
357
  export const savePage = window.hyperclayModules['save-core']?.savePage ?? window.hyperclayModules['save-core']?.default;
313
358
  export const beforeSave = window.hyperclayModules['save-system']?.beforeSave ?? window.hyperclayModules['save-system']?.default;
@@ -327,6 +372,7 @@ export const ask = window.hyperclayModules['dialogs']?.ask ?? window.hyperclayMo
327
372
  export const consent = window.hyperclayModules['dialogs']?.consent ?? window.hyperclayModules['dialogs']?.default;
328
373
  export const tell = window.hyperclayModules['dialogs']?.tell ?? window.hyperclayModules['dialogs']?.default;
329
374
  export const snippet = window.hyperclayModules['dialogs']?.snippet ?? window.hyperclayModules['dialogs']?.default;
375
+ export const quickcrop = window.hyperclayModules['quickcrop']?.quickcrop ?? window.hyperclayModules['quickcrop']?.default;
330
376
  export const toast = window.hyperclayModules['toast']?.toast ?? window.hyperclayModules['toast']?.default;
331
377
  export const toastHyperclay = window.hyperclayModules['toast-hyperclay']?.toastHyperclay ?? window.hyperclayModules['toast-hyperclay']?.default;
332
378
  export const themodal = window.hyperclayModules['the-modal']?.themodal ?? window.hyperclayModules['the-modal']?.default;
@@ -346,6 +392,8 @@ export const HyperMorph = window.hyperclayModules['hyper-morph']?.HyperMorph ??
346
392
  export const morph = window.hyperclayModules['hyper-morph']?.morph ?? window.hyperclayModules['hyper-morph']?.default;
347
393
  export const cms = window.hyperclayModules['hypercms']?.cms ?? window.hyperclayModules['hypercms']?.default;
348
394
  export const undo = window.hyperclayModules['undo']?.undo ?? window.hyperclayModules['undo']?.default;
395
+ export const extractData = window.hyperclayModules['data']?.extractData ?? window.hyperclayModules['data']?.default;
396
+ export const applyData = window.hyperclayModules['data']?.applyData ?? window.hyperclayModules['data']?.default;
349
397
  export const slugify = window.hyperclayModules['slugify']?.slugify ?? window.hyperclayModules['slugify']?.default;
350
398
  export const copyToClipboard = window.hyperclayModules['copy-to-clipboard']?.copyToClipboard ?? window.hyperclayModules['copy-to-clipboard']?.default;
351
399
  export const query = window.hyperclayModules['query-params']?.query ?? window.hyperclayModules['query-params']?.default;