@panphora/clayjs 1.5.3 → 1.6.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 (46) hide show
  1. package/README.md +5 -3
  2. package/THIRD-PARTY-NOTICES.md +6 -0
  3. package/dist/clay.standalone.js +15041 -10657
  4. package/package.json +9 -4
  5. package/src/core/conflict-download.js +91 -0
  6. package/src/core/conflict-notice.js +720 -0
  7. package/src/core/conflict-presentation.js +301 -0
  8. package/src/core/save-conflict-notice.js +2 -192
  9. package/src/core/save-core.js +35 -2
  10. package/src/core/save-feedback.js +6 -0
  11. package/src/core/save.js +157 -3
  12. package/src/core/snapshot.js +21 -0
  13. package/src/core/stale-host-notice.js +21 -24
  14. package/src/core/unsaved-warning.js +64 -16
  15. package/src/lib/content-dom.js +4 -2
  16. package/src/lib/dirty-gate.js +37 -1
  17. package/src/lib/unsaved-state.js +55 -0
  18. package/src/loader-logic.js +26 -15
  19. package/src/loader.js +10 -3
  20. package/src/plugins/ai-edit.js +489 -179
  21. package/src/plugins/cms.js +12 -0
  22. package/src/plugins/indicator.js +35 -16
  23. package/src/plugins/quickcrop.js +103 -0
  24. package/src/plugins/richclay.js +21 -0
  25. package/src/plugins/source.js +13 -6
  26. package/src/plugins/wire.js +4 -0
  27. package/src/sync/conflict-revert.js +1138 -0
  28. package/src/sync/conflicts.js +159 -0
  29. package/src/sync/live-sync.js +130 -109
  30. package/src/sync/presence.js +42 -36
  31. package/src/sync/section-notice.js +53 -28
  32. package/src/ui/bevel-controls.js +277 -0
  33. package/src/ui/bevel-dialog.js +112 -0
  34. package/src/ui/bevel.js +247 -0
  35. package/src/ui/dialogs.js +63 -86
  36. package/src/ui/index.js +22 -11
  37. package/src/ui/modal.js +80 -387
  38. package/src/ui/skins/cms-toggle.js +15 -0
  39. package/src/ui/skins/cms.js +4 -0
  40. package/src/ui/skins/quickcrop.js +4 -0
  41. package/src/ui/skins/richclay.js +4 -0
  42. package/src/ui/toast.js +116 -401
  43. package/src/ui/vendor-skin.js +65 -0
  44. package/src/vendor/hyper-morph.vendor.js +4 -4
  45. package/src/vendor/quickcrop.vendor.js +14 -8
  46. package/src/vendor/richclay.vendor.js +22 -41
package/src/ui/modal.js CHANGED
@@ -1,3 +1,7 @@
1
+ import { bevelButton, setShown } from "./bevel-controls.js";
2
+ import { set } from "../lib/hostile-css.js";
3
+ import { bevelDialog, dismissOf } from "./bevel-dialog.js";
4
+
1
5
  /*
2
6
 
3
7
  theModal
@@ -9,10 +13,14 @@
9
13
  - user confirms
10
14
  - everything about the modal resets
11
15
 
12
- themodal.html = content;
16
+ themodal.html = content; // html, yes and no each take markup, or a node placed as it is
13
17
  themodal.yes = content;
14
18
  themodal.no = content;
15
19
 
20
+ themodal.closeHtml = "x"; // any non-empty value shows the corner close
21
+ themodal.title = content; // markup or a node: a header with the title (optional)
22
+ themodal.width = "440px"; // the panel's width (default 600px)
23
+
16
24
  themodal.disableFocus = true;
17
25
  themodal.disableScroll = true;
18
26
 
@@ -24,35 +32,11 @@
24
32
 
25
33
  */
26
34
 
27
- /*
28
-
29
- things you probably want to style
30
-
31
- .micromodal {}
32
- .micromodal .micromodal__container {}
33
- .micromodal .micromodal__content {}
34
- .micromodal .micromodal__heading {}
35
- .micromodal .micromodal__input {}
36
- .micromodal .micromodal__input:focus, .micromodal .micromodal__input:active {}
37
- .micromodal .micromodal__buttons {}
38
- .micromodal .micromodal__yes, .micromodal__no {}
39
- .micromodal .micromodal__yes {}
40
- .micromodal .micromodal__yes:focus, .micromodal__yes:hover {}
41
- .micromodal .micromodal__no {}
42
- .micromodal .micromodal__no:focus, .micromodal__no:hover {}
43
- .micromodal .micromodal__close {}
44
- .micromodal .micromodal__close:focus, .micromodal__close:hover {}
45
-
46
- */
47
-
48
-
49
-
50
-
51
-
52
-
53
35
  // MicroModal
54
36
  // MIT License (c) 2017 Indrashish Ghosh
55
37
  // MODIFIED: removed `this.activeElement.focus()` after modal is closed
38
+ // MODIFIED: takes the modal element itself, not an id, and adds no open class; the
39
+ // shell is created on open and removed on close, so nothing has to be looked up
56
40
 
57
41
  const MicroModal = (() => {
58
42
  'use strict'
@@ -86,7 +70,7 @@ const MicroModal = (() => {
86
70
  awaitOpenAnimation = false,
87
71
  debugMode = false
88
72
  }) {
89
- this.modal = document.getElementById(targetModal)
73
+ this.modal = typeof targetModal === 'string' ? document.getElementById(targetModal) : targetModal
90
74
 
91
75
  this.config = { debugMode, disableScroll, openTrigger, closeTrigger, openClass, onShow, onClose, awaitCloseAnimation, awaitOpenAnimation, disableFocus }
92
76
 
@@ -105,7 +89,6 @@ const MicroModal = (() => {
105
89
  showModal (event = null) {
106
90
  this.activeElement = document.activeElement
107
91
  this.modal.setAttribute('aria-hidden', 'false')
108
- this.modal.classList.add(this.config.openClass)
109
92
  this.scrollBehaviour('disable')
110
93
  this.addEventListeners()
111
94
 
@@ -122,33 +105,13 @@ const MicroModal = (() => {
122
105
  this.config.onShow(this.modal, this.activeElement, event)
123
106
  }
124
107
 
108
+ // Runs on the stored element even after a live-sync morph removed it from the
109
+ // DOM, so onClose, the keydown listener and the scroll restore are never stranded.
125
110
  closeModal (event = null) {
126
- const modal = this.modal
127
111
  this.modal.setAttribute('aria-hidden', 'true')
128
112
  this.removeEventListeners()
129
113
  this.scrollBehaviour('enable')
130
114
  this.config.onClose(this.modal, this.activeElement, event)
131
-
132
- if (this.config.awaitCloseAnimation) {
133
- const openClass = this.config.openClass // <- old school ftw
134
- this.modal.addEventListener('animationend', function handler () {
135
- modal.classList.remove(openClass)
136
- modal.removeEventListener('animationend', handler, false)
137
- }, false)
138
- } else {
139
- modal.classList.remove(this.config.openClass)
140
- }
141
- }
142
-
143
- closeModalById (targetModal) {
144
- // Fall back to the stored modal reference when the node was removed from
145
- // the DOM externally (e.g. a live-sync morph). Without this, getElementById
146
- // returns null, closeModal() is skipped, and its cleanup never runs — the
147
- // onClose callback, this.removeEventListeners() (which drops MicroModal's
148
- // document keydown listener), and scroll restore are all stranded.
149
- const modal = document.getElementById(targetModal)
150
- if (modal) this.modal = modal
151
- if (this.modal) this.closeModal()
152
115
  }
153
116
 
154
117
  scrollBehaviour (toggle) {
@@ -310,9 +273,9 @@ const MicroModal = (() => {
310
273
  activeModal.showModal()
311
274
  }
312
275
 
313
- const close = targetModal => {
276
+ const close = () => {
314
277
  if (!activeModal) return
315
- targetModal ? activeModal.closeModalById(targetModal) : activeModal.closeModal()
278
+ activeModal.closeModal()
316
279
  activeModal = null
317
280
  }
318
281
 
@@ -323,310 +286,10 @@ const MicroModal = (() => {
323
286
 
324
287
  // themodal.js
325
288
  // MIT License (c) 2023 David Miranda
326
-
327
- const modalCss = `<style class="micromodal-css">
328
- .micromodal {
329
- display: none;
330
- color: #fff;
331
- font-family: var(--hyperclay-modal-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace);
332
- font-size: var(--hyperclay-modal-font-size, 18px);
333
- }
334
-
335
- .micromodal button:not(.custom-button) {
336
- background: none;
337
- color: inherit;
338
- border: none;
339
- padding: 0;
340
- margin: 0;
341
- width: auto;
342
- overflow: visible;
343
- font: inherit;
344
- line-height: inherit;
345
- text-transform: none;
346
- text-align: center;
347
- text-decoration: none;
348
- cursor: pointer;
349
- -webkit-appearance: none;
350
- -moz-appearance: none;
351
- appearance: none;
352
- outline: 0;
353
- }
354
-
355
- .micromodal .micromodal__hide {
356
- display: none;
357
- }
358
-
359
- .micromodal.is-open {
360
- display: block;
361
- }
362
-
363
- .micromodal__overlay {
364
- position: fixed;
365
- top: 0;
366
- left: 0;
367
- right: 0;
368
- bottom: 0;
369
- display: flex;
370
- justify-content: center;
371
- align-items: center;
372
- background: rgba(0,0,0,.65);
373
- }
374
-
375
- .micromodal__container {
376
- position: relative;
377
- width: 100%;
378
- min-width: 0;
379
- max-width: min(550px, calc(100vw - 2rem));
380
- max-height: calc(100vh - 4rem);
381
- max-height: calc(100dvh - 2rem);
382
- box-sizing: border-box;
383
- overflow: hidden;
384
- border: 2px solid #FFFFFF;
385
- background-color: #11131E;
386
- }
387
-
388
- .micromodal__inner {
389
- overflow-x: hidden;
390
- overflow-y: auto;
391
- /* Must subtract container's 4px border (2px top + 2px bottom) from max-height,
392
- otherwise inner overflows past container since box-sizing: border-box
393
- makes the container's content area smaller than its max-height */
394
- max-height: calc(100dvh - 2rem - 4px);
395
- padding: 26px 40px 40px 40px;
396
- }
397
-
398
- @media (min-width: 640px) {
399
- .micromodal .micromodal__inner {
400
- padding: 52px 64px 60px 64px;
401
- }
402
- }
403
-
404
- .micromodal[aria-hidden="false"] .micromodal__overlay {
405
- animation: microModalFadeIn .2s cubic-bezier(0.0, 0.0, 0.2, 1);
406
- }
407
-
408
- .micromodal[aria-hidden="false"] .micromodal__container {
409
- animation: microModalSlideIn .2s cubic-bezier(0, 0, .2, 1);
410
- }
411
-
412
- .micromodal .micromodal__container,
413
- .micromodal .micromodal__overlay {
414
- will-change: transform;
415
- }
416
-
417
- @keyframes microModalFadeIn {
418
- from { opacity: 0; }
419
- to { opacity: 1; }
420
- }
421
-
422
- @keyframes microModalSlideIn {
423
- from { transform: translateY(15%); }
424
- to { transform: translateY(0); }
425
- }
426
-
427
- .micromodal .micromodal__content {
428
- margin-bottom: 14px;
429
- overflow-wrap: anywhere;
430
- }
431
-
432
- .micromodal .micromodal__heading {
433
- display: flex;
434
- flex-direction: column;
435
- gap: 2px;
436
- margin-bottom: 8px;
437
- overflow-wrap: anywhere;
438
- }
439
-
440
- .micromodal .micromodal__input {
441
- width: min(calc(100vw - 100px), 420px);
442
- padding: 6px 6px 7px;
443
- font-size: var(--hyperclay-modal-input-font-size, 16px);
444
- color: #000;
445
- background: #fff;
446
- }
447
-
448
- .micromodal .micromodal__input:focus, .micromodal .micromodal__input:active {
449
- outline: 3px solid #6A73B6;
450
- }
451
-
452
- .micromodal button.micromodal__yes {
453
- display: flex;
454
- justify-content: center;
455
- align-items: center;
456
- width: 100%;
457
- height: 39px;
458
- line-height: 0;
459
- border: 3px solid;
460
- border-top-color: #94BA6F;
461
- border-left-color: #94BA6F;
462
- border-bottom-color: #1A3004;
463
- border-right-color: #1A3004;
464
- background-color: #49870B;
465
- }
466
-
467
- .micromodal button.micromodal__yes:focus,
468
- .micromodal button.micromodal__yes:hover {
469
- background-color: #549B0D;
470
- }
471
-
472
- .micromodal button.micromodal__yes:active {
473
- border-top-color: #1A3004;
474
- border-left-color: #1A3004;
475
- border-bottom-color: #94BA6F;
476
- border-right-color: #94BA6F;
477
- }
478
-
479
- .micromodal button.micromodal__secondary-btn {
480
- display: flex;
481
- justify-content: center;
482
- align-items: center;
483
- width: 100%;
484
- height: 39px;
485
- line-height: 0;
486
- border: 3px solid;
487
- border-top-color: #474C65;
488
- border-left-color: #474C65;
489
- border-bottom-color: #131725;
490
- border-right-color: #131725;
491
- background-color: #1D1F2F;
492
- color: #E5E7EB;
493
- font-family: inherit;
494
- font-size: inherit;
495
- font-weight: bold;
496
- }
497
-
498
- .micromodal button.micromodal__secondary-btn:focus,
499
- .micromodal button.micromodal__secondary-btn:hover {
500
- background-color: #232639;
501
- }
502
-
503
- .micromodal button.micromodal__secondary-btn:active {
504
- border-top-color: #131725;
505
- border-left-color: #131725;
506
- border-bottom-color: #474C65;
507
- border-right-color: #474C65;
508
- }
509
-
510
- .micromodal:has(.snippet-code-block) .micromodal__content {
511
- margin-bottom: 0;
512
- }
513
-
514
- .micromodal .snippet-code-block {
515
- background-color: #292E54;
516
- padding: 1rem;
517
- margin-bottom: 14px;
518
- max-width: 420px;
519
- overflow-x: auto;
520
- }
521
-
522
- .micromodal .snippet-code-block pre {
523
- color: white;
524
- font-family: var(--hyperclay-modal-code-font-family, var(--hyperclay-modal-font-family, monospace));
525
- font-size: 0.875rem;
526
- white-space: nowrap;
527
- margin: 0;
528
- }
529
-
530
- .micromodal .snippet-warning {
531
- padding: 0.75rem;
532
- border: 2px solid #989742;
533
- background-color: #1E1E11;
534
- font-size: 0.875rem;
535
- color: #FBF7B7;
536
- max-width: 420px;
537
- }
538
-
539
- .micromodal .snippet-link-box {
540
- font-size: 16px;
541
- margin-top: 0.75rem;
542
- text-align: right;
543
- }
544
-
545
- .micromodal .snippet-link-box a {
546
- color: #F6F7F9;
547
- text-decoration: none;
548
- display: inline-flex;
549
- align-items: center;
550
- gap: 0.3rem;
551
- padding: 0.35rem 0.45rem;
552
- border: 1px solid #474C64;
553
- line-height: 1;
554
- }
555
-
556
- .micromodal .snippet-link-box a:hover {
557
- color: #E5E7EB;
558
- border-color: #75798B;
559
- }
560
-
561
- .micromodal .snippet-link-box a svg {
562
- width: 0.85rem;
563
- height: 0.85rem;
564
- flex-shrink: 0;
565
- }
566
-
567
- .micromodal button.micromodal__close {
568
- clip-path: polygon(0% 4%, 0% 0%, 100% 0%, 100% 100%, 94% 100%);
569
- position: absolute;
570
- top: -1px;
571
- right: -1px;
572
- width: 68px;
573
- }
574
-
575
- .micromodal .micromodal__close-bg {
576
- fill: #1D2032;
577
- }
578
-
579
- .micromodal .micromodal__close:hover .micromodal__close-bg {
580
- fill: #212543;
581
- }
582
-
583
- .micromodal .micromodal__close-x {
584
- fill: #fff;
585
- }
586
-
587
- .micromodal .micromodal__tell {
588
- max-width: 440px;
589
- margin-bottom: 28px;
590
- }
591
-
592
- .micromodal .micromodal__tell > * + * {
593
- margin-top: 20px;
594
- }
595
-
596
- .micromodal .micromodal__tell-title {
597
- font-size: var(--hyperclay-modal-title-font-size, 20px);
598
- font-weight: bold;
599
- }
600
-
601
- .micromodal .micromodal__tell-content {
602
- font-size: var(--hyperclay-modal-font-size, 16px);
603
- font-weight: normal;
604
- }
605
-
606
- @media (min-width: 640px) {
607
- .micromodal .micromodal__tell-title {
608
- font-size: var(--hyperclay-modal-title-font-size, 22px);
609
- }
610
- .micromodal .micromodal__tell-content {
611
- font-size: var(--hyperclay-modal-font-size, 18px);
612
- }
613
- }
614
- </style>`;
615
-
616
- const modalHtml = `<div class="micromodal" id="micromodal" aria-hidden="true">
617
- <div class="micromodal__overlay" tabindex="-1">
618
- <form class="micromodal__container" role="dialog" aria-modal="true">
619
- <div class="micromodal__inner">
620
- <div class="micromodal__content"></div>
621
- <div class="micromodal__buttons">
622
- <button class="micromodal__no" type="button"></button>
623
- <button class="micromodal__yes" type="submit"></button>
624
- </div>
625
- </div>
626
- <button class="micromodal__close" type="button" aria-label="Close modal"></button>
627
- </form>
628
- </div>
629
- </div>`;
289
+ //
290
+ // Drawn in Bevel (bevel-dialog.js): inline !important, no classes, no ids, so a
291
+ // page stylesheet cannot repaint it. What callers put in html, yes and no is theirs
292
+ // and goes in untouched.
630
293
 
631
294
  const themodal = (() => {
632
295
  let html = "";
@@ -634,6 +297,8 @@ const themodal = (() => {
634
297
  let no = "";
635
298
  let zIndex = "100";
636
299
  let closeHtml = "";
300
+ let title = "";
301
+ let width = "";
637
302
 
638
303
  let enableClickOutsideCloses = true;
639
304
  let disableScroll = true;
@@ -671,10 +336,10 @@ const themodal = (() => {
671
336
  // Singleton: only one modal on screen at a time. If one is still up (or
672
337
  // left stale state behind, e.g. a live-sync morph removed its DOM), reject
673
338
  // its promise and tear it down before opening the new one.
674
- if (this.isShowing || document.querySelector('.micromodal-parent')) {
339
+ if (this.isShowing || document.querySelector('[data-clay-modal]')) {
675
340
  this._dismiss?.();
676
341
  this._cleanupListeners?.();
677
- document.querySelectorAll('.micromodal-parent').forEach(n => n.remove());
342
+ document.querySelectorAll('[data-clay-modal]').forEach(n => { dismissOf.get(n)?.(); n.remove(); });
678
343
  this.isShowing = false;
679
344
  document.body.style.overflow = '';
680
345
  }
@@ -682,20 +347,33 @@ const themodal = (() => {
682
347
  // Expose this modal's dismiss so a later open()/close can settle it.
683
348
  this._dismiss = dismiss;
684
349
 
685
- document.body.insertAdjacentHTML("afterbegin", "<div clay='no-save no-snapshot' class='micromodal-parent'>" + modalCss + modalHtml + "</div>");
686
-
687
- const modalOverlayElem = document.querySelector(".micromodal__overlay");
688
- const modalContentElem = document.querySelector(".micromodal__content");
689
- const modalButtonsElem = document.querySelector(".micromodal__buttons");
690
- const modalYesElem = document.querySelector(".micromodal__yes");
691
- const modalNoElem = document.querySelector(".micromodal__no");
692
- const modalCloseElem = document.querySelector(".micromodal__close");
350
+ const shell = bevelDialog({ zIndex, closable: !!closeHtml, titled: !!title, ...(width ? { width } : {}) });
351
+ const modalRootElem = shell.root;
352
+ const modalOverlayElem = shell.overlay;
353
+ const modalContainerElem = shell.panel;
354
+ const modalContentElem = shell.body;
355
+ const modalButtonsElem = shell.footer;
356
+ const modalCloseElem = shell.close;
357
+ const modalNoElem = bevelButton('');
358
+ const modalYesElem = bevelButton('', { variant: 'primary' });
359
+ modalYesElem.type = 'submit';
360
+ modalButtonsElem.append(modalNoElem, modalYesElem);
361
+
362
+ // Markup is parsed in; a node is placed as it is, listeners and all.
363
+ const place = (target, value) => {
364
+ if (value instanceof Node) target.append(value);
365
+ else target.innerHTML = value;
366
+ };
367
+ place(modalContentElem, html);
368
+ place(modalYesElem.firstChild, yes);
369
+ place(modalNoElem.firstChild, no);
370
+ if (title) place(shell.heading, title);
371
+ if (title) modalContainerElem.setAttribute("aria-label", shell.heading.textContent.trim());
372
+ // No body content (a confirm that is all title) means no empty band between header and footer.
373
+ setShown(modalContentElem, !(html === "" || html == null), "block");
374
+ if ((html === "" || html == null) && title) set(modalButtonsElem, "border-top", "0");
693
375
 
694
- modalContentElem.innerHTML = html;
695
- modalYesElem.innerHTML = yes;
696
- modalNoElem.innerHTML = no;
697
- modalOverlayElem.style.zIndex = zIndex;
698
- modalCloseElem.innerHTML = closeHtml;
376
+ document.body.prepend(modalRootElem);
699
377
 
700
378
  // MODIFIED so modal doesn't close if mousedown happened inside the modal
701
379
  let mousedownOnBackdrop = false;
@@ -703,17 +381,17 @@ const themodal = (() => {
703
381
  // MODIFIED so modal doesn't close if mousedown happened inside the modal
704
382
  function handleMousedown(event) {
705
383
  // Check if mousedown started on backdrop (overlay but not container)
706
- mousedownOnBackdrop = event.target.closest(".micromodal__overlay") &&
707
- !event.target.closest(".micromodal__container");
384
+ mousedownOnBackdrop = modalOverlayElem.contains(event.target) &&
385
+ !modalContainerElem.contains(event.target);
708
386
  }
709
387
 
710
388
  function handleClick(event) {
711
389
  // Just close on no / close-button / backdrop; onClose runs dismiss().
712
- if (event.target.closest(".micromodal__no") || event.target.closest(".micromodal__close")) {
713
- MicroModal.close("micromodal");
390
+ if (modalNoElem.contains(event.target) || modalCloseElem?.contains(event.target)) {
391
+ MicroModal.close();
714
392
  // MODIFIED so modal doesn't close if mousedown happened inside the modal
715
- } else if (enableClickOutsideCloses && mousedownOnBackdrop && !event.target.closest(".micromodal__container") && event.target.closest(".micromodal__overlay")) {
716
- MicroModal.close("micromodal");
393
+ } else if (enableClickOutsideCloses && mousedownOnBackdrop && !modalContainerElem.contains(event.target) && modalOverlayElem.contains(event.target)) {
394
+ MicroModal.close();
717
395
  }
718
396
 
719
397
  // Reset after handling
@@ -721,7 +399,7 @@ const themodal = (() => {
721
399
  }
722
400
 
723
401
  function handleSubmit(event) {
724
- if (event.target.closest("#micromodal")) {
402
+ if (modalRootElem.contains(event.target)) {
725
403
  event.preventDefault();
726
404
 
727
405
  // Execute callbacks and check if any return false or throw errors
@@ -747,7 +425,7 @@ const themodal = (() => {
747
425
  // scheduled their resolve, so mark settled to stop onClose rejecting.
748
426
  if (shouldClose) {
749
427
  settled = true;
750
- MicroModal.close("micromodal");
428
+ MicroModal.close();
751
429
  }
752
430
  }
753
431
  }
@@ -765,14 +443,14 @@ const themodal = (() => {
765
443
  };
766
444
 
767
445
  function setButtonsVisibility () {
768
- modalButtonsElem.classList.toggle("micromodal__hide", !yes && !no);
769
- modalYesElem.classList.toggle("micromodal__hide", !yes);
770
- modalNoElem.classList.toggle("micromodal__hide", !no);
446
+ setShown(modalButtonsElem, !!(yes || no), "flex");
447
+ setShown(modalYesElem, !!yes, "inline-flex");
448
+ setShown(modalNoElem, !!no, "inline-flex");
771
449
  }
772
450
 
773
451
  setButtonsVisibility();
774
452
 
775
- MicroModal.show("micromodal", {
453
+ MicroModal.show(modalRootElem, {
776
454
  disableScroll,
777
455
  disableFocus: true, // we use our own
778
456
  // reset everything on close
@@ -781,13 +459,15 @@ const themodal = (() => {
781
459
  // (no / close / backdrop / Esc). No-op once already resolved.
782
460
  dismiss();
783
461
 
784
- document.querySelector(".micromodal-parent")?.remove();
462
+ modalRootElem.remove();
785
463
 
786
464
  html = "";
787
465
  yes = "";
788
466
  no = "";
789
467
  zIndex = "100";
790
468
  closeHtml = "";
469
+ title = "";
470
+ width = "";
791
471
 
792
472
  // reset to defaults
793
473
  enableClickOutsideCloses = true;
@@ -814,14 +494,15 @@ const themodal = (() => {
814
494
  myOnOpen.forEach(cb => cb());
815
495
 
816
496
  if (!disableFocus) {
817
- let firstInput = modalOverlayElem.querySelector(".micromodal__content :is(input,textarea,button):not(.micromodal__hide), .micromodal__buttons :is(input,textarea,button):not(.micromodal__hide)");
497
+ let firstInput = modalContentElem.querySelector(":is(input,textarea,button):not([hidden])") ||
498
+ [modalYesElem, modalNoElem].find(button => !button.hidden);
818
499
  firstInput?.focus();
819
500
  firstInput?.setSelectionRange?.(-1, -1);
820
501
  }
821
502
  },
822
503
  close() {
823
504
  // onClose runs the dismiss/reject path; just trigger the close.
824
- MicroModal.close("micromodal");
505
+ MicroModal.close();
825
506
  },
826
507
  get html() {
827
508
  return html;
@@ -835,6 +516,18 @@ const themodal = (() => {
835
516
  set closeHtml(newVal) {
836
517
  closeHtml = newVal;
837
518
  },
519
+ get title() {
520
+ return title;
521
+ },
522
+ set title(newVal) {
523
+ title = newVal;
524
+ },
525
+ get width() {
526
+ return width;
527
+ },
528
+ set width(newVal) {
529
+ width = newVal;
530
+ },
838
531
  get yes() {
839
532
  return yes;
840
533
  },
@@ -0,0 +1,15 @@
1
+ // The CMS's "Edit content" toggle in Bevel's button face. HyperCMS pins the toggle's fill
2
+ // to an opaque neutral (#0a0a0a or #fafafa) and exposes --hcms-toggle-bg and
3
+ // --hcms-toggle-color for authors; on a dark page Bevel's dark bottom-right edge vanished
4
+ // into the near-black fill and the button read as pressed in. Hand-written, not
5
+ // generated: the generated CMS skin may only pin the vendor's --mirk-* tokens.
6
+ import { TOKENS } from "../bevel.js";
7
+
8
+ export const ROOTS = ["[data-hcms-toggle-host]"];
9
+
10
+ export const CSS = `@layer clay-skin{
11
+ [data-hcms-toggle-host]{--hcms-toggle-bg:${TOKENS.face} !important;--hcms-toggle-color:${TOKENS.ink} !important}
12
+ [data-hcms-toggle-host] :is(.hcms-toggle__main, .hcms-toggle__arrow){border-color:${TOKENS["edge-hi"]} ${TOKENS["edge-lo"]} ${TOKENS["edge-lo"]} ${TOKENS["edge-hi"]} !important}
13
+ [data-hcms-toggle-host] :is(.hcms-toggle__main, .hcms-toggle__arrow):hover:not(:active){--hcms-toggle-bg:color-mix(in srgb, ${TOKENS.face}, ${TOKENS["edge-hi"]} 45%) !important}
14
+ [data-hcms-toggle-host] :is(.hcms-toggle__main, .hcms-toggle__arrow):active{border-color:${TOKENS["edge-lo"]} ${TOKENS["edge-hi"]} ${TOKENS["edge-hi"]} ${TOKENS["edge-lo"]} !important}
15
+ }`;
@@ -0,0 +1,4 @@
1
+ // GENERATED from src/vendor/hypercms.vendor.js by scripts/build-vendor-skins.mjs. Run `npm run build:skins`.
2
+ export const SOURCE_SHA256 = "c7a060a8458cc2ac182145f9415a623bf3fdfe5d5cd2b1724ad7d89959266862";
3
+ export const ROOTS = [".hcms-shell"];
4
+ export const CSS = "@layer clay-skin{\n:is(.hcms-shell):not([data-theme]),:is(.hcms-shell):not([data-theme]) *{--mirk-canvas:light-dark(#FDF8F0, #0B0C13) !important;--mirk-bg:light-dark(#FFFCF6, #11131E) !important;--mirk-fg:light-dark(#2B241B, #ECEAF2) !important;--mirk-accent:light-dark(#F4ECDF, #1A1D2C) !important;--mirk-destructive:light-dark(#A83A2C, #FF5566) !important;--mirk-focus-color:light-dark(#8F6310, #D9A445) !important;--mirk-bevel-bg:light-dark(#F1E7D4, #22273E) !important;--mirk-bevel-fg:light-dark(#2B241B, #ECEAF2) !important;--mirk-bevel-tl:light-dark(#FFF9ED, #3C4260) !important;--mirk-bevel-br:light-dark(#D6C3A5, #1B2033) !important;--mirk-bevel-hover-bg:color-mix(in srgb, light-dark(#F1E7D4, #22273E), light-dark(#FFF9ED, #3C4260) 45%) !important;--mirk-pill-inner-top:light-dark(#FFF9ED, #3C4260) !important;--mirk-input-border:light-dark(#D8C8AF, #353B52) !important;--mirk-placeholder-color:light-dark(#9A8A70, #6A7090) !important;--mirk-ctrl-bg:light-dark(#6B5942, #8A90AB) !important;--mirk-toggle-bg:light-dark(#FCF8F1, #4A5275) !important;--mirk-toggle-hi:light-dark(#FFF9ED, #6C759A) !important;--mirk-toggle-lo:light-dark(#D6C3A5, #1B2033) !important;--mirk-mark-fg:light-dark(#2B241B, #ECEAF2) !important;--mirk-sortable-dot:light-dark(#D8C8AF, #353B52) !important;--mirk-sortable-shadow:light-dark(#D6C3A5, #1B2033) !important;--mirk-sortable-label:light-dark(#6B5942, #8A90AB) !important;--mirk-sortable-placeholder:light-dark(#9A8A70, #6A7090) !important;--mirk-slider-fill:light-dark(#F0E4CC, #3A2F17) !important;--mirk-slider-nub-bg:light-dark(#FCF8F1, #4A5275) !important;--mirk-slider-nub-hi:light-dark(#FFF9ED, #6C759A) !important;--mirk-slider-nub-lo:light-dark(#D6C3A5, #1B2033) !important;--mirk-chip-surface:light-dark(#FFFCF6, #11131E) !important;--mirk-chip-edge:light-dark(#D8C8AF, #353B52) !important;--mirk-chip-primary-bg:light-dark(#2B241B, #ECEAF2) !important;--mirk-chip-primary-fg:light-dark(#FDF8F0, #0B0C13) !important;--mirk-chip-alert:light-dark(#A83A2C, #FF5566) !important;--mirk-radius:0px !important;--mirk-focus-offset:2px !important}\n:is(.hcms-shell){font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace !important}\n.hcms-shell .mirk-chip__actions .mirk-chip__action--primary{--mirk-bevel-bg:var(--mirk-chip-primary-bg) !important;--mirk-bevel-fg:var(--mirk-chip-primary-fg) !important;--mirk-bevel-tl:color-mix(in srgb, var(--mirk-chip-primary-bg), white 24%) !important;--mirk-bevel-br:color-mix(in srgb, var(--mirk-chip-primary-bg), black 32%) !important;--mirk-bevel-hover-bg:color-mix(in srgb, var(--mirk-chip-primary-bg), white 10%) !important}\n}";
@@ -0,0 +1,4 @@
1
+ // GENERATED from src/vendor/quickcrop.vendor.js and bevel/integrations/quickcrop.css by scripts/build-vendor-skins.mjs. Run `npm run build:skins`.
2
+ export const SOURCE_SHA256 = "49c5be87a001dd2e8fef962ac8c0c933e079fb4be98ecf2e4e1ec850ed922c5a";
3
+ export const ROOTS = [".qc-stage",".qc-backdrop"];
4
+ export const CSS = "@layer clay-skin{\n:is(.qc-stage, .qc-backdrop),:is(.qc-stage, .qc-backdrop) *{--qc-surface:light-dark(#FFFCF6, #11131E) !important;--qc-text:light-dark(#2B241B, #ECEAF2) !important;--qc-border:light-dark(#D8C8AF, #353B52) !important;--qc-radius:0px !important;--qc-font:system-ui,-apple-system,\"Segoe UI\",sans-serif !important;--qc-surface-hover:light-dark(#F4ECDF, #1A1D2C) !important;--qc-text-hover:color-mix(in srgb, light-dark(#2B241B, #ECEAF2), light-dark(#FDF8F0, #0B0C13) 12%) !important;--qc-on-dark:light-dark(#FDF8F0, #0B0C13) !important;--qc-overlay:color-mix(in srgb, light-dark(#FDF8F0, #0B0C13) 70%, transparent) !important;--qc-dim:rgba(0,0,0,.55) !important;--qc-crop-line:#fff !important}\n.qc-stage{margin-inline:auto !important;inline-size:fit-content !important;background:light-dark(#F4ECDF, #1A1D2C) !important}\n.qc-stage img{display:block !important;max-inline-size:100% !important}\n.qc-backdrop{position:fixed !important;inset:0 !important;background:var(--qc-overlay) !important;display:grid !important;place-items:center !important;z-index:1000 !important;animation:qc-fade .15s ease !important}\n.qc-modal{background:var(--qc-surface) !important;border:1px solid var(--qc-border) !important;border-radius:var(--qc-radius) !important;padding:16px !important;max-width:92vw !important;font-family:var(--qc-font) !important}\n.qc-bar{display:flex !important;justify-content:flex-end !important;gap:10px !important;margin-top:12px !important}\n.qc-btn{font:inherit !important;font-family:var(--qc-font) !important;font-size:.85rem !important;padding:.3rem .75rem !important;border:1px solid var(--qc-border) !important;border-radius:6px !important;background:var(--qc-surface) !important;color:var(--qc-text) !important;cursor:pointer !important}\n.qc-btn:hover{background:var(--qc-surface-hover) !important}\n.qc-btn-primary{background:var(--qc-text) !important;border-color:var(--qc-text) !important;color:var(--qc-on-dark) !important;font-weight:600 !important}\n.qc-btn-primary:hover{background:var(--qc-text-hover) !important;border-color:var(--qc-text-hover) !important}\n.qc-stage{position:relative !important;line-height:0 !important;user-select:none !important;-webkit-user-select:none !important;touch-action:none !important}\n.qc-stage img{display:block !important;max-width:100% !important;pointer-events:none !important}\n.qc-dim{position:absolute !important;inset:0 !important;background:var(--qc-dim) !important;pointer-events:none !important}\n.qc-box{position:absolute !important;cursor:move !important;outline:1px solid var(--qc-crop-line) !important;background:linear-gradient(var(--qc-crop-line),var(--qc-crop-line)) 0 33.33%/100% 1px no-repeat,linear-gradient(var(--qc-crop-line),var(--qc-crop-line)) 0 66.66%/100% 1px no-repeat,linear-gradient(var(--qc-crop-line),var(--qc-crop-line)) 33.33% 0/1px 100% no-repeat,linear-gradient(var(--qc-crop-line),var(--qc-crop-line)) 66.66% 0/1px 100% no-repeat !important;background-blend-mode:overlay !important}\n.qc-handle{position:absolute !important;box-sizing:border-box !important;width:28px !important;height:28px !important;border:7px solid transparent !important;background:var(--qc-crop-line) !important;background-clip:padding-box !important;border-radius:9px !important}\n.qc-nw{left:-14px !important;top:-14px !important;cursor:nwse-resize !important}\n.qc-ne{right:-14px !important;top:-14px !important;cursor:nesw-resize !important}\n.qc-sw{left:-14px !important;bottom:-14px !important;cursor:nesw-resize !important}\n.qc-se{right:-14px !important;bottom:-14px !important;cursor:nwse-resize !important}\n.qc-mount{line-height:0 !important}\n.qc-stage img[hidden]{display:none !important}\n.qc-backdrop[hidden]{display:none !important}\n.qc-bar[hidden]{display:none !important}\n}";