hyperclayjs 1.19.9 → 1.20.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
@@ -86,7 +86,7 @@ import 'hyperclayjs/presets/standard.js';
86
86
  |--------|------|-------------|
87
87
  | dialogs | 7.7KB | ask(), consent(), tell(), snippet() dialog functions |
88
88
  | the-modal | 21.5KB | Full modal window creation system - window.theModal |
89
- | toast | 10.7KB | Success/error message notifications, toast(msg, msgType) |
89
+ | toast | 15.9KB | Success/error message notifications, toast(msg, msgType) |
90
90
 
91
91
  ### Utilities (Core utilities (often auto-included))
92
92
 
@@ -121,7 +121,7 @@ import 'hyperclayjs/presets/standard.js';
121
121
  | Module | Size | Description |
122
122
  |--------|------|-------------|
123
123
  | file-upload | 10.7KB | File upload with progress |
124
- | live-sync | 11.4KB | Real-time DOM sync across browsers |
124
+ | live-sync | 11.5KB | Real-time DOM sync across browsers |
125
125
  | send-message | 1.3KB | Message sending utility |
126
126
 
127
127
  ### Vendor Libraries (Third-party libraries)
@@ -132,17 +132,17 @@ import 'hyperclayjs/presets/standard.js';
132
132
 
133
133
  ## Presets
134
134
 
135
- ### Minimal (~52.6KB)
135
+ ### Minimal (~57.8KB)
136
136
  Essential features for basic editing
137
137
 
138
138
  **Modules:** `save-core`, `snapshot`, `save-system`, `edit-mode-helpers`, `toast`, `save-toast`, `export-to-window`, `view-mode-excludes-edit-modules`
139
139
 
140
- ### Standard (~74.9KB)
140
+ ### Standard (~80.1KB)
141
141
  Standard feature set for most use cases
142
142
 
143
143
  **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`
144
144
 
145
- ### Everything (~209.1KB)
145
+ ### Everything (~214.4KB)
146
146
  All available features
147
147
 
148
148
  Includes all available modules across all categories.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperclayjs",
3
- "version": "1.19.9",
3
+ "version": "1.20.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",
@@ -327,13 +327,15 @@ class LiveSync {
327
327
  /**
328
328
  * Handle a notification message from the server
329
329
  * Shows a toast and emits an event for custom handling
330
- * @param {Object} data - { msgType, msg, action? }
330
+ * @param {Object} data - { msgType, msg, action?, persistent? }
331
331
  */
332
- handleNotification({ msgType, msg, action }) {
332
+ handleNotification({ msgType, msg, action, persistent }) {
333
333
  this._log(`Notification received: ${msgType} - ${msg}`);
334
334
 
335
335
  // Show toast if available
336
- if (window.toast) {
336
+ if (persistent && window.toastPersistent) {
337
+ window.toastPersistent(msg, msgType);
338
+ } else if (window.toast) {
337
339
  window.toast(msg, msgType);
338
340
  } else {
339
341
  console.log(`[LiveSync] Notification: ${msg}`);
@@ -341,12 +343,12 @@ class LiveSync {
341
343
 
342
344
  // Emit event for custom handling (e.g., reload button)
343
345
  document.dispatchEvent(new CustomEvent('hyperclay:notification', {
344
- detail: { msgType, msg, action }
346
+ detail: { msgType, msg, action, persistent }
345
347
  }));
346
348
 
347
349
  // Call notification callback if set
348
350
  if (this.onNotification) {
349
- this.onNotification({ msgType, msg, action });
351
+ this.onNotification({ msgType, msg, action, persistent });
350
352
  }
351
353
  }
352
354
 
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.19.9 - Minimal Browser-Native Loader
4
+ * HyperclayJS v1.20.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.
package/src/ui/toast.js CHANGED
@@ -6,7 +6,7 @@ const defaultIcons = {
6
6
  success: `<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.9404 23.9475L21.9099 31.224L35.1906 15.9045M3 4.5H44.9804V44.309H3V4.5Z" stroke="#33D131" stroke-width="4.3"/></svg>`,
7
7
  error: `<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M32.7383 14.4045L14 33.1429M32.7451 33.1429L14.0068 14.4046M3.01 4H44.99V43.809H3.01V4Z" stroke="#FF4450" stroke-width="4"/></svg>`,
8
8
  warning: `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke="#F5A623" stroke-width="2" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126ZM12 15.75h.007v.008H12v-.008Z"/></svg>`,
9
- info: `<svg viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="24" cy="24" r="20" stroke="#4A90D9" stroke-width="4"/><path d="M24 20V32M24 14V16" stroke="#4A90D9" stroke-width="4" stroke-linecap="round"/></svg>`
9
+ info: `<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke="#4A90D9" stroke-width="2" d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z"/></svg>`
10
10
  };
11
11
 
12
12
  // Hyperclay icons
@@ -143,6 +143,11 @@ const modernStyles = `
143
143
  height: 22px;
144
144
  }
145
145
 
146
+ [data-toast-theme="modern"] .toast.info .toast-icon svg {
147
+ width: 24px;
148
+ height: 24px;
149
+ }
150
+
146
151
  [data-toast-theme="modern"] .toast-message {
147
152
  position: relative;
148
153
  top: -1px;
@@ -315,12 +320,131 @@ function toast(message, messageType = "success") {
315
320
  });
316
321
  }
317
322
 
323
+ // Close button (button with pixel art X icon inside)
324
+ const CLOSE_BUTTON_HTML = `<button class="toast-close" type="button" aria-label="Close"><svg viewBox="0 0 558 558" xmlns="http://www.w3.org/2000/svg"><path d="M0,0h62.02v62.02h-62.02Z M62,0h62.02v62.02h-62.02Z M434,0h62.02v62.02h-62.02Z M496,0h62.02v62.02h-62.02Z M0,62h62.02v62.02h-62.02Z M62,62h62.02v62.02h-62.02Z M124,62h62.02v62.02h-62.02Z M372,62h62.02v62.02h-62.02Z M434,62h62.02v62.02h-62.02Z M496,62h62.02v62.02h-62.02Z M62,124h62.02v62.02h-62.02Z M124,124h62.02v62.02h-62.02Z M186,124h62.02v62.02h-62.02Z M310,124h62.02v62.02h-62.02Z M372,124h62.02v62.02h-62.02Z M434,124h62.02v62.02h-62.02Z M124,186h62.02v62.02h-62.02Z M186,186h62.02v62.02h-62.02Z M248,186h62.02v62.02h-62.02Z M310,186h62.02v62.02h-62.02Z M372,186h62.02v62.02h-62.02Z M186,248h62.02v62.02h-62.02Z M248,248h62.02v62.02h-62.02Z M310,248h62.02v62.02h-62.02Z M124,310h62.02v62.02h-62.02Z M186,310h62.02v62.02h-62.02Z M248,310h62.02v62.02h-62.02Z M310,310h62.02v62.02h-62.02Z M372,310h62.02v62.02h-62.02Z M62,372h62.02v62.02h-62.02Z M124,372h62.02v62.02h-62.02Z M186,372h62.02v62.02h-62.02Z M310,372h62.02v62.02h-62.02Z M372,372h62.02v62.02h-62.02Z M434,372h62.02v62.02h-62.02Z M0,434h62.02v62.02h-62.02Z M62,434h62.02v62.02h-62.02Z M124,434h62.02v62.02h-62.02Z M372,434h62.02v62.02h-62.02Z M434,434h62.02v62.02h-62.02Z M496,434h62.02v62.02h-62.02Z M0,496h62.02v62.02h-62.02Z M62,496h62.02v62.02h-62.02Z M434,496h62.02v62.02h-62.02Z M496,496h62.02v62.02h-62.02Z"/></svg></button>`;
325
+
326
+ // Persistent toast styles (added once)
327
+ const persistentToastStyles = `
328
+ .toast.toast-persistent {
329
+ position: relative;
330
+ align-items: center;
331
+ padding-right: 18px;
332
+ }
333
+ .toast-persistent .toast-close {
334
+ background: none;
335
+ border: none;
336
+ padding: 0;
337
+ margin-left: 3px;
338
+ position: relative;
339
+ top: -2px;
340
+ cursor: pointer;
341
+ opacity: 0.7;
342
+ transition: opacity 0.2s;
343
+ display: flex;
344
+ align-items: center;
345
+ justify-content: center;
346
+ color: white;
347
+ width: 22px;
348
+ height: 22px;
349
+ }
350
+ .toast-persistent .toast-close:hover {
351
+ opacity: 1;
352
+ }
353
+ .toast-persistent .toast-close svg {
354
+ width: 11px;
355
+ height: 11px;
356
+ fill: currentColor;
357
+ }
358
+ `;
359
+
360
+ let persistentStylesInjected = false;
361
+
362
+ // Track active persistent toasts by message
363
+ const activePersistentToasts = new Map();
364
+
365
+ // Persistent toast function - doesn't auto-dismiss, requires click to close
366
+ function toastPersistent(message, messageType = "warning") {
367
+ injectToastStyles(toastConfig.styles, toastConfig.theme);
368
+
369
+ // Inject persistent-specific styles once
370
+ if (!persistentStylesInjected) {
371
+ const styleSheet = document.createElement('style');
372
+ styleSheet.className = 'toast-styles-persistent';
373
+ styleSheet.setAttribute('save-remove', '');
374
+ styleSheet.setAttribute('snapshot-remove', '');
375
+ styleSheet.textContent = persistentToastStyles;
376
+ document.head.appendChild(styleSheet);
377
+ persistentStylesInjected = true;
378
+ }
379
+
380
+ const templates = toastConfig.templates;
381
+ const icons = toastConfig.icons;
382
+ const theme = toastConfig.theme;
383
+
384
+ // Get or create container for this theme
385
+ let toastContainer = document.querySelector(`.toast-container[data-toast-theme="${theme}"]`);
386
+ if (!toastContainer) {
387
+ toastContainer = document.createElement('div');
388
+ toastContainer.className = 'toast-container';
389
+ toastContainer.setAttribute('data-toast-theme', theme);
390
+ toastContainer.setAttribute('save-remove', '');
391
+ toastContainer.setAttribute('snapshot-remove', '');
392
+ document.body.append(toastContainer);
393
+ }
394
+
395
+ // If same message already showing, animate it out first
396
+ const existingToast = activePersistentToasts.get(message);
397
+ if (existingToast) {
398
+ existingToast.classList.add('hide');
399
+ setTimeout(() => existingToast.remove(), 500);
400
+ activePersistentToasts.delete(message);
401
+ }
402
+
403
+ // Create toast element
404
+ const icon = icons[messageType] || icons.warning;
405
+ const template = templates.toast[messageType] || templates.toast.warning;
406
+ const toastHtml = template
407
+ .replace('{icon}', icon)
408
+ .replace('{message}', message);
409
+
410
+ const tempDiv = document.createElement('div');
411
+ tempDiv.innerHTML = toastHtml.trim();
412
+ const toastElement = tempDiv.firstElementChild;
413
+ toastElement.classList.add('toast-persistent');
414
+
415
+ // Add close button as sibling (not inside message)
416
+ toastElement.insertAdjacentHTML('beforeend', CLOSE_BUTTON_HTML);
417
+
418
+ // Close handler - only on close button click
419
+ const closeBtn = toastElement.querySelector('.toast-close');
420
+ if (closeBtn) {
421
+ closeBtn.addEventListener('click', (e) => {
422
+ e.stopPropagation();
423
+ toastElement.classList.add('hide');
424
+ setTimeout(() => {
425
+ toastElement.remove();
426
+ activePersistentToasts.delete(message);
427
+ }, 500);
428
+ });
429
+ }
430
+
431
+ // Track this toast
432
+ activePersistentToasts.set(message, toastElement);
433
+
434
+ // Add to container and animate in
435
+ toastContainer.append(toastElement);
436
+ setTimeout(() => toastElement.classList.remove('hide'), 10);
437
+ }
438
+
318
439
  // Auto-export to window unless suppressed by loader
319
440
  if (!window.__hyperclayNoAutoExport) {
320
441
  window.toast = toast;
442
+ window.toastPersistent = toastPersistent;
321
443
  window.hyperclay = window.hyperclay || {};
322
444
  window.hyperclay.toast = toast;
445
+ window.hyperclay.toastPersistent = toastPersistent;
323
446
  window.h = window.hyperclay;
324
447
  }
325
448
 
449
+ export { toastPersistent };
326
450
  export default toast;