@panphora/clayjs 1.6.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/loader.js CHANGED
@@ -50,7 +50,7 @@ export async function boot(base, params, readyResolve) {
50
50
  continue;
51
51
  }
52
52
  loaded[path] = mod;
53
- attachPluginMember(path, mod); // immediately, not after the loop: hypercms's ?cms=true
53
+ attachPluginMember(path, mod, loaded); // immediately, not after the loop: hypercms's ?cms=true
54
54
  // auto-open runs as a microtask queued during ITS evaluation
55
55
  // (before boot resumes) and reads clay.RichClay and clay.undo,
56
56
  // which earlier plugins must have attached by then
@@ -83,6 +83,10 @@ function assembleCore(loaded, { isEditMode, isOwner }, regionPolicy) {
83
83
  const save = loaded["core/save.js"];
84
84
  const cacheBustMod = loaded["lib/cache-bust.js"];
85
85
  const hyperMorph = loaded["vendor/hyper-morph.vendor.js"];
86
+ const pageData = loaded["core/page-data.js"];
87
+
88
+ clay.readData = pageData.readData;
89
+ clay.writeData = pageData.writeData;
86
90
 
87
91
  if (save) {
88
92
  const saveFn = save.savePage || save.default;
@@ -107,7 +111,7 @@ function assembleCore(loaded, { isEditMode, isOwner }, regionPolicy) {
107
111
  if (hyperMorph) clay.morph = hyperMorph.morph;
108
112
  }
109
113
 
110
- function attachPluginMember(path, mod) {
114
+ function attachPluginMember(path, mod, loaded) {
111
115
  const clay = window.clay;
112
116
 
113
117
  if (path === "plugins/undo.js") {
@@ -115,6 +119,7 @@ function attachPluginMember(path, mod) {
115
119
  } else if (path === "sync/live-sync.js") {
116
120
  clay.morph = mod.morph;
117
121
  clay.conflicts = mod.conflicts;
122
+ loaded["core/page-data.js"].setPageDataLiveSync(mod.liveSync);
118
123
  } else if (path === "plugins/cms.js") {
119
124
  clay.cms = mod.cms || mod.default;
120
125
  } else if (path === "plugins/upload.js") {
@@ -506,10 +506,10 @@ async function flushSave() {
506
506
  // surface is an opt-in satellite script, absent on every page the loader
507
507
  // builds, so reading it here made "is a save on the wire" permanently false.
508
508
  const { isSaveInProgress } = await import("../core/save-core.js");
509
- // A held save is an AI edit preview waiting for Keep or Revert, not a page with
510
- // nothing to save: posting now would hand the agent a file that is about to change.
511
- const { savesHeld } = await import("../core/save.js");
512
- if (savesHeld()) throw new Error("keep or revert the AI edit first");
509
+ // A held save means another operation owns the page's persisted state, not a
510
+ // page with nothing to save: posting now could hand the agent stale bytes.
511
+ const { savesHeld, saveHoldReason } = await import("../core/save.js");
512
+ if (savesHeld()) throw new Error(saveHoldReason());
513
513
  const deadline = Date.now() + SAVE_FLUSH_TIMEOUT_MS;
514
514
 
515
515
  for (let attempt = 0; attempt < 3; attempt++) {
@@ -0,0 +1,41 @@
1
+ import { conflicts } from './conflicts.js';
2
+
3
+ const replacements = new WeakMap();
4
+
5
+ export const replacementFootprint = (rec) => replacements.get(rec) || null;
6
+
7
+ export function trackConflictFootprints(merge) {
8
+ const watched = new Map();
9
+ for (const rec of conflicts.list()) {
10
+ if (!rec.recovery?.text) continue;
11
+ const live = rec.recovery.subject.live;
12
+ const node = replacements.get(rec) || (live.length === 1 ? live[0] : null);
13
+ if (node?.nodeType === 1 && node.ownerDocument === document && node.isConnected) watched.set(rec, node);
14
+ }
15
+ if (!watched.size) return merge();
16
+ const observer = new MutationObserver(() => {});
17
+ observer.observe(document, { subtree: true, childList: true, attributes: true, characterData: true });
18
+ try {
19
+ const pending = merge();
20
+ const changes = observer.takeRecords();
21
+ if (changes.length !== 2) return pending;
22
+ const [insert, remove] = changes;
23
+ if (insert.type !== 'childList' || remove.type !== 'childList' || insert.target !== remove.target ||
24
+ insert.addedNodes.length !== 1 || insert.removedNodes.length ||
25
+ remove.removedNodes.length !== 1 || remove.addedNodes.length) return pending;
26
+ const old = remove.removedNodes[0];
27
+ const fresh = insert.addedNodes[0];
28
+ if (old.nodeType !== 1 || fresh.nodeType !== 1 || old.isConnected || !fresh.isConnected ||
29
+ old.namespaceURI !== fresh.namespaceURI || old.tagName === fresh.tagName ||
30
+ insert.nextSibling !== old || remove.previousSibling !== fresh ||
31
+ fresh.parentNode !== insert.target || fresh.previousSibling !== insert.previousSibling ||
32
+ fresh.nextSibling !== remove.nextSibling) return pending;
33
+ if (old.childNodes.length !== 1 || fresh.childNodes.length !== 1 || old.firstChild.nodeType !== 3 ||
34
+ !old.firstChild.isEqualNode(fresh.firstChild) || old.attributes.length !== fresh.attributes.length ||
35
+ [...old.attributes].some((a) => fresh.getAttributeNS(a.namespaceURI, a.localName) !== a.value)) return pending;
36
+ for (const [rec, node] of watched) if (node === old) replacements.set(rec, fresh);
37
+ return pending;
38
+ } finally {
39
+ observer.disconnect();
40
+ }
41
+ }
@@ -1,5 +1,6 @@
1
1
  import { HyperMorph } from "../vendor/hyper-morph.vendor.js";
2
2
  import { conflicts } from "./conflicts.js";
3
+ import { replacementFootprint } from "./conflict-footprints.js";
3
4
  import { SYNC_IGNORE_SELECTOR, REMOTE_WINS_SELECTOR } from "./live-sync.js";
4
5
  import { savePage } from "../core/save.js";
5
6
  import { gateMarkDirty } from "../lib/dirty-gate.js";
@@ -802,6 +803,8 @@ function footprintOf(rec, apply) {
802
803
  }
803
804
  const live = subject ? liveOf(subject, apply, subject.nodeType) : null;
804
805
  if (live) return live;
806
+ const replacement = replacementFootprint(rec);
807
+ if (replacement && onPage(replacement)) return replacement;
805
808
  const parent = r.structure && r.structure.localPlacement ? liveOf(r.structure.localPlacement.parent, apply, 1) : null;
806
809
  if (parent) return parent;
807
810
  const path = subject && subject.local.length ? subject.local[0] : [];
@@ -63,6 +63,33 @@ export function beginApply({ source, seq = null, etag = null, domain, root }) {
63
63
  return id;
64
64
  }
65
65
 
66
+ function insertionProof(raw) {
67
+ if (!raw.every((c) => c.kind === 'structure' && c.detail === 'insert-collision' && c.recovery?.applied === true && !c.recovery.unavailable && c.recovery.structure?.localAction === 'inserted' && c.recovery.structure.fragmentKind === 'element')) return null;
68
+ const subject = raw[0].recovery.subject;
69
+ if (subject?.local?.length !== 1 || subject.merged?.length !== 1 || subject.live?.length !== 1) return null;
70
+ return { local: subject.local[0], merged: subject.merged[0], live: subject.live[0] };
71
+ }
72
+
73
+ function foldInsertions(groups) {
74
+ const proofs = new Map(groups.map((raw) => [raw, insertionProof(raw)]));
75
+ const below = (parent, child) => parent.length < child.length && parent.every((step, i) => step === child[i]);
76
+ const at = (root, path) => path.reduce((node, step) => step === 'content' ? node?.content : node?.childNodes[step], root);
77
+ const folded = new Map();
78
+ for (const raw of groups) {
79
+ const child = proofs.get(raw);
80
+ let owner = raw;
81
+ if (child) {
82
+ for (const candidate of groups) {
83
+ const parent = proofs.get(candidate);
84
+ if (parent && parent.local.length < proofs.get(owner).local.length && below(parent.local, child.local) && below(parent.merged, child.merged) && (parent.live.contains(child.live) || at(parent.live, child.merged.slice(parent.merged.length)) === child.live)) owner = candidate;
85
+ }
86
+ }
87
+ if (!folded.has(owner)) folded.set(owner, [...owner]);
88
+ if (raw !== owner) folded.get(owner).push(...raw);
89
+ }
90
+ return [...folded.values()];
91
+ }
92
+
66
93
  export function completeApply(applyId, conflicts, { ticket }) {
67
94
  const apply = applies.get(applyId);
68
95
  if (!apply) return [];
@@ -74,7 +101,7 @@ export function completeApply(applyId, conflicts, { ticket }) {
74
101
  if (!groups.has(key)) groups.set(key, []);
75
102
  groups.get(key).push(c);
76
103
  }
77
- const ids = [...groups.values()].map((raw) => install(apply, raw));
104
+ const ids = foldInsertions([...groups.values()]).map((raw) => install(apply, raw));
78
105
  pending.delete(applyId);
79
106
  if (!ids.length) applies.delete(applyId);
80
107
  if (ids.length) changed({ added: ids.map((id) => records.get(id)) });
@@ -60,6 +60,7 @@ import { pageMaybeDirty, pauseGate, resumeGate, gateCaptureToken, gateClearIfUnc
60
60
  import { gestureSeen } from '../lib/user-gesture.js';
61
61
  import { SyncStream } from './stream.js';
62
62
  import { conflicts, beginApply, completeApply, failApply } from './conflicts.js';
63
+ import { trackConflictFootprints } from './conflict-footprints.js';
63
64
 
64
65
  // What a live-sync merge never reads or touches on any side: editor chrome,
65
66
  // content kept out of the save or the snapshot, frozen regions, and nodes
@@ -1339,6 +1340,7 @@ class LiveSync {
1339
1340
  * @param {object} [lane.extra] - extra mergeDocument options (the disk lane's beforeApply)
1340
1341
  */
1341
1342
  async _mergeIncoming(html, identityMap, { base, baseIdentityMap = null, captureLocal, synthetic, source = 'peer', seq = null, etag = null, extra = {} }) {
1343
+ const mergeDocument = (options) => trackConflictFootprints(() => HyperMorph.mergeDocument(options));
1342
1344
  const store = this.identity;
1343
1345
  // A frame may neither write these onto our root nor, by not carrying them,
1344
1346
  // take ours away. Returning false is hyper-morph's veto for both directions.
@@ -1396,7 +1398,7 @@ class LiveSync {
1396
1398
  let localRoot = null;
1397
1399
  let applyId = null;
1398
1400
  if (base == null) {
1399
- pending = HyperMorph.mergeDocument({
1401
+ pending = mergeDocument({
1400
1402
  ...common,
1401
1403
  base: null,
1402
1404
  local: { root: captureLocal(), toLive: originalSnapshotNode },
@@ -1414,7 +1416,7 @@ class LiveSync {
1414
1416
  const localRoot = captureLocal();
1415
1417
  const baseDoc = localRoot.ownerDocument;
1416
1418
  if (baseDoc.documentElement !== localRoot) baseDoc.replaceChild(localRoot, baseDoc.documentElement);
1417
- pending = HyperMorph.mergeDocument({
1419
+ pending = mergeDocument({
1418
1420
  ...common,
1419
1421
  base: baseDoc,
1420
1422
  local: { root: localRoot, toLive: originalSnapshotNode },
@@ -1432,7 +1434,7 @@ class LiveSync {
1432
1434
  domain: source === 'disk' ? 'save' : 'sync',
1433
1435
  });
1434
1436
  try {
1435
- pending = HyperMorph.mergeDocument({
1437
+ pending = mergeDocument({
1436
1438
  ...common,
1437
1439
  base,
1438
1440
  local: { root: localRoot, toLive: originalSnapshotNode },
@@ -72,8 +72,10 @@ function protectIcon(svg, size) {
72
72
  `fill:${attr('fill', svg.getAttribute('fill') ?? 'currentColor')}`,
73
73
  `stroke:${attr('stroke', svg.getAttribute('stroke') ?? 'none')}`,
74
74
  ];
75
- if (el.hasAttribute('stroke-width')) rules.push(`stroke-width:${el.getAttribute('stroke-width')}`);
76
- if (el.hasAttribute('stroke-linecap')) rules.push(`stroke-linecap:${el.getAttribute('stroke-linecap')}`);
75
+ for (const prop of ['stroke-width', 'stroke-linecap', 'stroke-linejoin']) {
76
+ const value = el.getAttribute(prop) ?? svg.getAttribute(prop);
77
+ if (value !== null) rules.push(`${prop}:${value}`);
78
+ }
77
79
  if (el.localName === 'path' && el.hasAttribute('d')) rules.push(`d:path("${el.getAttribute('d')}")`);
78
80
  style(el, rules);
79
81
  }
@@ -84,6 +86,7 @@ export function bevelButton(label, { variant = 'default', small = false, onClick
84
86
  const size = small ? SIZE.small : SIZE.normal;
85
87
  const reducedMotion = media('(prefers-reduced-motion: reduce)');
86
88
  const forced = media('(forced-colors: active)');
89
+ const coarse = media('(pointer: coarse)');
87
90
  const flags = { hovered: false, pressed: false, focusVisible: false, disabled: false };
88
91
  const pinned = new Map();
89
92
 
@@ -103,6 +106,7 @@ export function bevelButton(label, { variant = 'default', small = false, onClick
103
106
  }
104
107
  if (flags.focusVisible) rules.push(...RULES.focus);
105
108
  if (forced) rules.push(...MEDIA.forcedColors);
109
+ if (coarse) rules.push(...MEDIA.coarseButton);
106
110
  rules.push(...extra);
107
111
  for (const [prop, value] of pinned) rules.push(`${prop}:${value}`);
108
112
  restyle(b, rules);
@@ -234,6 +238,7 @@ export function bevelText(tag, rules = [], text) {
234
238
  export function paintInput(el, { rules = [] } = {}) {
235
239
  const flags = { hovered: false, focused: false };
236
240
  const forced = media('(forced-colors: active)');
241
+ const coarse = media('(pointer: coarse)');
237
242
  runtime(el);
238
243
  function applyState() {
239
244
  const height = el.localName === 'textarea' ? el.style.getPropertyValue('height') : '';
@@ -242,6 +247,7 @@ export function paintInput(el, { rules = [] } = {}) {
242
247
  if (flags.focused) list.push(...RULES.inputFocus);
243
248
  if (forced) list.push(...MEDIA.forcedColors);
244
249
  list.push(...rules);
250
+ if (coarse) list.push(...MEDIA.coarseInput);
245
251
  if (height) list.push(`height:${height}`);
246
252
  restyle(el, list);
247
253
  }
@@ -3,7 +3,7 @@
3
3
  // backdrop clicks and settling, so each keeps the rules it already had.
4
4
  import { set } from "../lib/hostile-css.js";
5
5
  import { bevelBox, bevelSurface, bevelText, pageScheme, protectIcon } from "./bevel-controls.js";
6
- import { TOKENS, FONT_SANS, RULES } from "./bevel.js";
6
+ import { TOKENS, FONT_SANS, RULES, GLYPHS } from "./bevel.js";
7
7
 
8
8
  // A frame's own way to settle itself. The modal is a singleton and clears every frame
9
9
  // when it opens; a frame it does not own (the crop dialog) registers here, so it is
@@ -32,8 +32,6 @@ export function keepFocusIn(container, event) {
32
32
  // The dashboard's dialog (Bevel's React dialog with the dashboard's overrides), drawn
33
33
  // inline. ClayJS loads no webfonts, so the title's Newsreader falls back to Georgia.
34
34
  const LINE = `1px solid ${TOKENS["line-2"]}`;
35
- const CLOSE_X =
36
- '<svg viewBox="0 0 256 256" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z"/></svg>';
37
35
 
38
36
  // The close: a column at the header's right edge in the button face, or a 62px square
39
37
  // at the panel's corner when there is no header.
@@ -48,7 +46,7 @@ function dialogClose(label, inHeader) {
48
46
  b.type = "button";
49
47
  b.setAttribute("aria-label", label);
50
48
  b.title = label;
51
- b.innerHTML = CLOSE_X;
49
+ b.innerHTML = GLYPHS.dialogClose;
52
50
  protectIcon(b.firstElementChild, 16);
53
51
  b.addEventListener("pointerenter", () => set(b, "background", hover));
54
52
  b.addEventListener("pointerleave", () => set(b, "background", TOKENS.face));
package/src/ui/bevel.js CHANGED
@@ -1,5 +1,5 @@
1
- // GENERATED from bevel/bevel.css by scripts/build-bevel-subset.mjs. Run `npm run build:bevel`.
2
- export const SOURCE_SHA256 = "4532d98386c76070626c0cccea91f512dafd0314c0c846dbf1eea9372ddde541";
1
+ // GENERATED from bevel/bevel.css and bevel/icons by scripts/build-bevel-subset.mjs. Run `npm run build:bevel`.
2
+ export const SOURCE_SHA256 = "775cf730e10d88d8b13440ba83e039a907ffb70bffe88d13c3bee8681af2e131";
3
3
  export const FONT_SANS = "system-ui,-apple-system,\"Segoe UI\",sans-serif";
4
4
  export const FONT_MONO = "ui-monospace,SFMono-Regular,Menlo,Consolas,monospace";
5
5
  export const SHADOW = "0 1px 2px rgba(0,0,0,.12),0 10px 28px -12px rgba(0,0,0,.35)";
@@ -33,6 +33,14 @@ export const TOKENS = {
33
33
  "ox-edge-lo": "light-dark(#451510, #3E1410)",
34
34
  "ox-ink": "light-dark(#FBEEE8, #FBEEE8)",
35
35
  };
36
+ export const GLYPHS = {
37
+ "toastSuccess": "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"square\" stroke-linejoin=\"miter\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m5 12 4 4L19 6\"/></svg>",
38
+ "toastWarning": "<svg width=\"16\" height=\"16\" viewBox=\"0 0 256 256\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z\"/></svg>",
39
+ "toastInfo": "<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.6\" stroke-linecap=\"square\" stroke-linejoin=\"miter\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0M12 7.75v.85M10.3 11.15H12v5.1M10.3 16.25h3.4\"/></svg>",
40
+ "toastClose": "<svg width=\"14\" height=\"14\" viewBox=\"0 0 256 256\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\"/></svg>",
41
+ "dialogClose": "<svg width=\"16\" height=\"16\" viewBox=\"0 0 256 256\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z\"/></svg>",
42
+ "conflictEye": "<svg width=\"16\" height=\"16\" viewBox=\"0 0 256 256\" fill=\"currentColor\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M247.31,124.76c-.35-.79-8.82-19.58-27.65-38.41C194.57,61.26,162.88,48,128,48S61.43,61.26,36.34,86.35C17.51,105.18,9,124,8.69,124.76a8,8,0,0,0,0,6.5c.35.79,8.82,19.57,27.65,38.4C61.43,194.74,93.12,208,128,208s66.57-13.26,91.66-38.34c18.83-18.83,27.3-37.61,27.65-38.4A8,8,0,0,0,247.31,124.76ZM128,192c-30.78,0-57.67-11.19-79.93-33.25A133.47,133.47,0,0,1,25,128,133.33,133.33,0,0,1,48.07,97.25C70.33,75.19,97.22,64,128,64s57.67,11.19,79.93,33.25A133.46,133.46,0,0,1,231.05,128C223.84,141.46,192.43,192,128,192Zm0-112a48,48,0,1,0,48,48A48.05,48.05,0,0,0,128,80Zm0,80a32,32,0,1,1,32-32A32,32,0,0,1,128,160Z\"/></svg>",
43
+ };
36
44
  export const RULES = {
37
45
  "button": [
38
46
  "display:inline-flex",
@@ -244,4 +252,16 @@ export const MEDIA = {
244
252
  "forcedColors": [
245
253
  "border:1px solid ButtonText",
246
254
  ],
255
+ "coarseInput": [
256
+ "font-size:16px",
257
+ "block-size:40px",
258
+ "min-block-size:40px",
259
+ "padding-inline:12px",
260
+ ],
261
+ "coarseButton": [
262
+ "block-size:40px",
263
+ "min-block-size:40px",
264
+ "padding-inline:14px",
265
+ "font-size:15px",
266
+ ],
247
267
  };
package/src/ui/dialogs.js CHANGED
@@ -9,7 +9,7 @@ import { TOKENS, FONT_SANS, FONT_MONO } from "./bevel.js";
9
9
  // are markup by design, and callers pass elements through them. Only the frame around
10
10
  // it is ClayJS's to style. The prompt is the dialog's title, as in the dashboard.
11
11
  function createModal(promptText, yesCallback, extraContent = "", includeInput = false, defaultValue = "", yesLabel = "OK") {
12
- const content = bevelBox("div", ["display:flex", "flex-direction:column", "gap:14px"]);
12
+ const content = bevelBox("div", ["display:flex", "flex-direction:column", "gap:14px", `font:14.5px/1.55 ${FONT_SANS}`, `color:${TOKENS["ink-2"]}`]);
13
13
  let input = null;
14
14
  if (includeInput) {
15
15
  input = bevelInput("input", { rules: ["display:block", "width:100%", "margin:0"] });
@@ -1,4 +1,4 @@
1
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";
2
+ export const SOURCE_SHA256 = "a1757cdc7333f40185cb497dd0a7db863289aac55e96c9d9f24239d56ceeacc2";
3
3
  export const ROOTS = [".hcms-shell"];
4
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}";
@@ -1,4 +1,4 @@
1
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";
2
+ export const SOURCE_SHA256 = "eadbbf799e0954f11ff553c5a37a759ceb42c6bb1698a0e46b1f273458acf8ed";
3
3
  export const ROOTS = [".qc-stage",".qc-backdrop"];
4
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}";
@@ -1,4 +1,4 @@
1
1
  // GENERATED from src/vendor/richclay.vendor.js and bevel/integrations/richclay.css by scripts/build-vendor-skins.mjs. Run `npm run build:skins`.
2
- export const SOURCE_SHA256 = "91b0339a9c534d4aa94d5b7179e5e9174fa61383f57fa2406cdac56b3719581c";
2
+ export const SOURCE_SHA256 = "69e3bc73ddf6654bb6245485dbad2a54956b8abd621889807b2de80875927982";
3
3
  export const ROOTS = [".richclay-toolbar",".richclay-float",".richclay-dialog"];
4
- export const CSS = "@layer clay-skin{\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog),:is(.richclay-toolbar, .richclay-float, .richclay-dialog) *{--richclay-surface:light-dark(#FFFCF6, #11131E) !important;--richclay-surface-alt:light-dark(#F4ECDF, #1A1D2C) !important;--richclay-text:light-dark(#2B241B, #ECEAF2) !important;--richclay-muted:light-dark(#6B5942, #8A90AB) !important;--richclay-border:light-dark(#D8C8AF, #353B52) !important;--richclay-accent:light-dark(#8F6310, #D9A445) !important;--richclay-accent-ink:light-dark(#FDF8F0, #0B0C13) !important;--richclay-focus:light-dark(#8F6310, #D9A445) !important;--richclay-danger:light-dark(#A83A2C, #FF5566) !important;--richclay-radius:0px !important;--richclay-control-size:32px !important;--richclay-shadow:0 1px 2px rgba(0,0,0,.12),0 10px 28px -12px rgba(0,0,0,.35) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog){font-family:system-ui,-apple-system,\"Segoe UI\",sans-serif !important}\n:where(.richclay-float, .richclay-dialog):is(.richclay-toolbar, .richclay-float, .richclay-dialog){color:light-dark(#2B241B, #ECEAF2) !important;background:light-dark(#FFFCF6, #11131E) !important;border:1px solid light-dark(#D8C8AF, #353B52) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-toolbar{gap:3px !important;margin:0 !important;padding:8px !important;border:1px solid light-dark(#D8C8AF, #353B52) !important;border-radius:0 !important;background:light-dark(#F4ECDF, #1A1D2C) !important;color:light-dark(#2B241B, #ECEAF2) !important}\n.richclay-toolbar:is(.richclay-toolbar, .richclay-float, .richclay-dialog){gap:3px !important;padding:8px !important;border:1px solid light-dark(#D8C8AF, #353B52) !important;border-radius:0 !important;background:light-dark(#F4ECDF, #1A1D2C) !important;color:light-dark(#2B241B, #ECEAF2) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-button{min-width:32px !important;height:32px !important;padding:0 7px !important;gap:6px !important;border:2px solid !important;border-color:light-dark(#FFF9ED, #3C4260) light-dark(#D6C3A5, #1B2033) light-dark(#D6C3A5, #1B2033) light-dark(#FFF9ED, #3C4260) !important;border-radius:0 !important;background:light-dark(#F1E7D4, #22273E) !important;font:14px system-ui,-apple-system,\"Segoe UI\",sans-serif !important;cursor:pointer !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-button:is([aria-pressed=\"true\"], [aria-expanded=\"true\"]){background:light-dark(#E3D0B3, #28304D) !important;border-color:light-dark(#B89F7B, #1F2339) light-dark(#F0E1C9, #4D587A) light-dark(#F0E1C9, #4D587A) light-dark(#B89F7B, #1F2339) !important;box-shadow:none !important;color:light-dark(#2B241B, #ECEAF2) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-button[aria-pressed=\"true\"] > svg{translate:1px 1px !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-button:disabled{opacity:.45 !important;cursor:not-allowed !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-button:hover:not([aria-pressed=\"true\"], [aria-expanded=\"true\"], :disabled){border-color:light-dark(#FFF9ED, #3C4260) light-dark(#D6C3A5, #1B2033) light-dark(#D6C3A5, #1B2033) light-dark(#FFF9ED, #3C4260) !important;background:color-mix(in srgb, light-dark(#F1E7D4, #22273E), light-dark(#FFF9ED, #3C4260) 45%) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-separator{align-self:center !important;height:22px !important;margin:0 5px !important;background:light-dark(#D8C8AF, #353B52) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-menu{inset-block-start:38px !important;min-width:210px !important;padding:5px !important;border-radius:0 !important;box-shadow:0 1px 2px rgba(0,0,0,.12),0 10px 28px -12px rgba(0,0,0,.35) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-menu-item{padding:8px 10px !important;border:0 !important;border-radius:0 !important;font:14px system-ui,-apple-system,\"Segoe UI\",sans-serif !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-menu-item[aria-checked=\"true\"]{background:light-dark(#F4ECDF, #1A1D2C) !important;box-shadow:inset 2px 0 light-dark(#8F6310, #D9A445) !important}\n.richclay-dialog:is(.richclay-toolbar, .richclay-float, .richclay-dialog){width:min(100%, 360px) !important;padding:14px 16px 12px !important;border:1px solid light-dark(#D8C8AF, #353B52) !important;border-radius:0 !important;background:light-dark(#FFFCF6, #11131E) !important;color:light-dark(#2B241B, #ECEAF2) !important;box-shadow:0 1px 2px rgba(0,0,0,.12),0 10px 28px -12px rgba(0,0,0,.35) !important}\n.richclay-dialog:is(.richclay-toolbar, .richclay-float, .richclay-dialog)::before{content:\"\" !important;position:absolute !important;left:calc(var(--richclay-dialog-arrow, 28px) - 6px) !important;top:-7px !important;width:12px !important;height:12px !important;background:light-dark(#FFFCF6, #11131E) !important;border-top:1px solid light-dark(#D8C8AF, #353B52) !important;border-left:1px solid light-dark(#D8C8AF, #353B52) !important;transform:rotate(45deg) !important}\n.richclay-dialog:is(.richclay-toolbar, .richclay-float, .richclay-dialog)[data-richclay-side=\"above\"]::before{top:auto !important;bottom:-7px !important;border:0 !important;border-right:1px solid light-dark(#D8C8AF, #353B52) !important;border-bottom:1px solid light-dark(#D8C8AF, #353B52) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-dialog-title{margin-bottom:2px !important;font:500 10px ui-monospace,SFMono-Regular,Menlo,Consolas,monospace !important;text-transform:uppercase !important;letter-spacing:.12em !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-field{gap:5px !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-field > span{color:light-dark(#4A4033, #C9CDE0) !important;font-size:12px !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-input{min-height:30px !important;padding:4px 8px !important;border:2px solid !important;border-color:light-dark(#B89F7B, #1F2339) light-dark(#F0E1C9, #4D587A) light-dark(#F0E1C9, #4D587A) light-dark(#B89F7B, #1F2339) !important;border-radius:0 !important;background:light-dark(#F4ECDF, #1A1D2C) !important;color:light-dark(#2B241B, #ECEAF2) !important;font:13px system-ui,-apple-system,\"Segoe UI\",sans-serif !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-dialog-actions{margin-top:4px !important;padding-top:12px !important;border-top:1px solid light-dark(#E8DCC6, #262B42) !important;flex-wrap:wrap !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) :is(.richclay-primary, .richclay-secondary){min-height:30px !important;padding:4px 10px !important;border:2px solid !important;border-color:light-dark(#FFF9ED, #3C4260) light-dark(#D6C3A5, #1B2033) light-dark(#D6C3A5, #1B2033) light-dark(#FFF9ED, #3C4260) !important;border-radius:0 !important;background:light-dark(#F1E7D4, #22273E) !important;color:light-dark(#2B241B, #ECEAF2) !important;font:13px system-ui,-apple-system,\"Segoe UI\",sans-serif !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-primary{background:light-dark(#2B241B, #ECEAF2) !important;color:light-dark(#FDF8F0, #0B0C13) !important;border-color:color-mix(in srgb, light-dark(#2B241B, #ECEAF2), white 24%) color-mix(in srgb, light-dark(#2B241B, #ECEAF2), black 32%) color-mix(in srgb, light-dark(#2B241B, #ECEAF2), black 32%) color-mix(in srgb, light-dark(#2B241B, #ECEAF2), white 24%) !important}\n.richclay-float:is(.richclay-toolbar, .richclay-float, .richclay-dialog){border-radius:0 !important;box-shadow:0 1px 2px rgba(0,0,0,.12),0 10px 28px -12px rgba(0,0,0,.35) !important}\n.richclay-float:is(.richclay-toolbar, .richclay-float, .richclay-dialog) > .richclay-toolbar{padding:5px !important}\n.richclay-toolbar{align-items:center !important;background:var(--richclay-surface-alt) !important;border:1px solid var(--richclay-border) !important;border-radius:var(--richclay-radius) !important;color:var(--richclay-text) !important;display:flex !important;flex-wrap:wrap !important;gap:2px !important;margin-block:0 6px !important;padding:4px !important}\n.richclay-toolbar:has(+ .richclay-editor){margin-block-end:0 !important;border-bottom-left-radius:0 !important;border-bottom-right-radius:0 !important}\n.richclay-button{align-items:center !important;appearance:none !important;background:transparent !important;border:1px solid transparent !important;border-radius:5px !important;color:currentColor !important;display:inline-flex !important;height:var(--richclay-control-size) !important;justify-content:center !important;margin:0 !important;min-width:var(--richclay-control-size) !important;padding:0 !important;position:relative !important}\n.richclay-button:hover,.richclay-button[aria-expanded=\"true\"],.richclay-menu-item:hover{background:var(--richclay-surface) !important;border-color:var(--richclay-border) !important}\n.richclay-button.is-active,.richclay-button[aria-pressed=\"true\"],.richclay-menu-item.is-active{background:rgb(0 0 0 / 0.13) !important;border-color:rgb(0 0 0 / 0.22) !important;color:var(--richclay-text) !important;box-shadow:inset 0 1px 3px rgb(0 0 0 / 0.26) !important}\n.richclay-button:focus-visible,.richclay-menu-item:focus-visible,.richclay-input:focus-visible,.richclay-primary:focus-visible,.richclay-secondary:focus-visible{outline:3px solid var(--richclay-focus) !important;outline-offset:2px !important}\n.richclay-button:disabled{color:var(--richclay-muted) !important;cursor:not-allowed !important;opacity:0.55 !important}\n.richclay-separator{align-self:stretch !important;background:var(--richclay-border) !important;display:inline-block !important;margin:5px 4px !important;width:1px !important}\n.richclay-menu-wrap{display:inline-flex !important;position:relative !important}\n.richclay-menu{background:var(--richclay-surface) !important;border:1px solid var(--richclay-border) !important;border-radius:var(--richclay-radius) !important;box-shadow:var(--richclay-shadow) !important;color:var(--richclay-text) !important;display:grid !important;gap:2px !important;inset-block-start:calc(100% + 4px) !important;inset-inline-start:0 !important;min-width:160px !important;padding:4px !important;position:absolute !important;z-index:1000 !important}\n.richclay-menu[hidden]{display:none !important}\n.richclay-menu-item{appearance:none !important;background:transparent !important;border:1px solid transparent !important;border-radius:4px !important;color:inherit !important;font:inherit !important;min-height:32px !important;padding:5px 9px !important;text-align:start !important}\n.richclay-float{left:0 !important;position:fixed !important;top:0 !important;will-change:transform !important;z-index:99999 !important}\n.richclay-float .richclay-toolbar{box-shadow:var(--richclay-shadow) !important;margin-block:0 !important}\n.richclay-float-rail .richclay-toolbar{--richclay-control-size:28px !important;flex-direction:column !important;flex-wrap:nowrap !important}\n.richclay-float-rail .richclay-toolbar svg{height:15px !important;width:15px !important}\n.richclay-float-rail .richclay-separator{align-self:stretch !important;height:1px !important;margin:4px 5px !important;width:auto !important}\n.richclay-float-rail .richclay-menu{inset-block-start:0 !important;inset-inline-start:calc(100% + 4px) !important}\n.richclay-dialog{background:var(--richclay-surface) !important;border:1px solid var(--richclay-border) !important;border-radius:var(--richclay-radius) !important;box-shadow:var(--richclay-shadow) !important;color:var(--richclay-text) !important;display:grid !important;gap:10px !important;box-sizing:border-box !important;width:min(420px, calc(100vw - 16px)) !important;z-index:99999 !important;padding:12px !important}\n.richclay-dialog-title{font-weight:650 !important}\n.richclay-field{display:grid !important;gap:4px !important}\n.richclay-field span{color:var(--richclay-muted) !important;font-size:0.875rem !important}\n.richclay-input{background:var(--richclay-surface) !important;border:1px solid var(--richclay-border) !important;border-radius:4px !important;color:var(--richclay-text) !important;font:inherit !important;min-height:36px !important;padding:6px 8px !important}\n.richclay-dialog-actions{display:flex !important;gap:8px !important;justify-content:flex-end !important}\n.richclay-primary,.richclay-secondary{appearance:none !important;border:1px solid var(--richclay-border) !important;border-radius:4px !important;font:inherit !important;min-height:34px !important;padding:5px 10px !important}\n.richclay-primary{background:var(--richclay-accent) !important;border-color:var(--richclay-accent) !important;color:var(--richclay-accent-ink) !important}\n.richclay-secondary{background:var(--richclay-surface-alt) !important;color:var(--richclay-text) !important}\n.richclay-sr-only{border:0 !important;clip:rect(0 0 0 0) !important;clip-path:inset(50%) !important;height:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;white-space:nowrap !important;width:1px !important}\n@media (forced-colors: active){\n.richclay-toolbar,.richclay-menu,.richclay-input{border-color:CanvasText !important}\n.richclay-dialog{border-color:CanvasText !important}\n.richclay-button.is-active,.richclay-button[aria-pressed=\"true\"],.richclay-menu-item.is-active,.richclay-primary{background:Highlight !important;color:HighlightText !important}\n.richclay-button:focus-visible,.richclay-menu-item:focus-visible,.richclay-input:focus-visible{outline-color:Highlight !important}\n}\n.richclay-toolbar[hidden]{display:none !important}\n.richclay-button[hidden]{display:none !important}\n.richclay-separator[hidden]{display:none !important}\n.richclay-menu-wrap[hidden]{display:none !important}\n.richclay-menu[hidden]{display:none !important}\n.richclay-dialog[hidden]{display:none !important}\n.richclay-field[hidden]{display:none !important}\n.richclay-dialog-actions[hidden]{display:none !important}\n}";
4
+ export const CSS = "@layer clay-skin{\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog),:is(.richclay-toolbar, .richclay-float, .richclay-dialog) *{--richclay-surface:light-dark(#FFFCF6, #11131E) !important;--richclay-surface-alt:light-dark(#F4ECDF, #1A1D2C) !important;--richclay-text:light-dark(#2B241B, #ECEAF2) !important;--richclay-muted:light-dark(#6B5942, #8A90AB) !important;--richclay-border:light-dark(#D8C8AF, #353B52) !important;--richclay-accent:light-dark(#8F6310, #D9A445) !important;--richclay-accent-ink:light-dark(#FDF8F0, #0B0C13) !important;--richclay-focus:light-dark(#8F6310, #D9A445) !important;--richclay-danger:light-dark(#A83A2C, #FF5566) !important;--richclay-radius:0px !important;--richclay-control-size:32px !important;--richclay-shadow:0 1px 2px rgba(0,0,0,.12),0 10px 28px -12px rgba(0,0,0,.35) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog){font-family:system-ui,-apple-system,\"Segoe UI\",sans-serif !important}\n:where(.richclay-float, .richclay-dialog):is(.richclay-toolbar, .richclay-float, .richclay-dialog){color:light-dark(#2B241B, #ECEAF2) !important;background:light-dark(#FFFCF6, #11131E) !important;border:1px solid light-dark(#D8C8AF, #353B52) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-toolbar{gap:3px !important;margin:0 !important;padding:8px !important;border:1px solid light-dark(#D8C8AF, #353B52) !important;border-radius:0 !important;background:light-dark(#F4ECDF, #1A1D2C) !important;color:light-dark(#2B241B, #ECEAF2) !important}\n.richclay-toolbar:is(.richclay-toolbar, .richclay-float, .richclay-dialog){gap:3px !important;padding:8px !important;border:1px solid light-dark(#D8C8AF, #353B52) !important;border-radius:0 !important;background:light-dark(#F4ECDF, #1A1D2C) !important;color:light-dark(#2B241B, #ECEAF2) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-button{min-width:32px !important;height:32px !important;padding:0 7px !important;gap:6px !important;border:2px solid !important;border-color:light-dark(#FFF9ED, #3C4260) light-dark(#D6C3A5, #1B2033) light-dark(#D6C3A5, #1B2033) light-dark(#FFF9ED, #3C4260) !important;border-radius:0 !important;background:light-dark(#F1E7D4, #22273E) !important;font:14px system-ui,-apple-system,\"Segoe UI\",sans-serif !important;cursor:pointer !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-button:is([aria-pressed=\"true\"], [aria-expanded=\"true\"]){background:light-dark(#E3D0B3, #28304D) !important;border-color:light-dark(#B89F7B, #1F2339) light-dark(#F0E1C9, #4D587A) light-dark(#F0E1C9, #4D587A) light-dark(#B89F7B, #1F2339) !important;box-shadow:none !important;color:light-dark(#2B241B, #ECEAF2) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-button[aria-pressed=\"true\"] > svg{translate:1px 1px !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-button:disabled{opacity:.45 !important;cursor:not-allowed !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-button:hover:not([aria-pressed=\"true\"], [aria-expanded=\"true\"], :disabled){border-color:light-dark(#FFF9ED, #3C4260) light-dark(#D6C3A5, #1B2033) light-dark(#D6C3A5, #1B2033) light-dark(#FFF9ED, #3C4260) !important;background:color-mix(in srgb, light-dark(#F1E7D4, #22273E), light-dark(#FFF9ED, #3C4260) 45%) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-separator{align-self:center !important;height:22px !important;margin:0 5px !important;background:light-dark(#D8C8AF, #353B52) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-menu{inset-block-start:38px !important;min-width:210px !important;padding:5px !important;border-radius:0 !important;box-shadow:0 1px 2px rgba(0,0,0,.12),0 10px 28px -12px rgba(0,0,0,.35) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-menu-item{padding:8px 10px !important;border:0 !important;border-radius:0 !important;font:14px system-ui,-apple-system,\"Segoe UI\",sans-serif !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-menu-item[aria-checked=\"true\"]{background:light-dark(#F4ECDF, #1A1D2C) !important;box-shadow:inset 2px 0 light-dark(#8F6310, #D9A445) !important}\n.richclay-dialog:is(.richclay-toolbar, .richclay-float, .richclay-dialog){width:min(100%, 360px) !important;padding:14px 16px 12px !important;border:1px solid light-dark(#D8C8AF, #353B52) !important;border-radius:0 !important;background:light-dark(#FFFCF6, #11131E) !important;color:light-dark(#2B241B, #ECEAF2) !important;box-shadow:0 1px 2px rgba(0,0,0,.12),0 10px 28px -12px rgba(0,0,0,.35) !important}\n.richclay-dialog:is(.richclay-toolbar, .richclay-float, .richclay-dialog)::before{content:\"\" !important;position:absolute !important;left:calc(var(--richclay-dialog-arrow, 28px) - 6px) !important;top:-7px !important;width:12px !important;height:12px !important;background:light-dark(#FFFCF6, #11131E) !important;border-top:1px solid light-dark(#D8C8AF, #353B52) !important;border-left:1px solid light-dark(#D8C8AF, #353B52) !important;transform:rotate(45deg) !important}\n.richclay-dialog:is(.richclay-toolbar, .richclay-float, .richclay-dialog)[data-richclay-side=\"above\"]::before{top:auto !important;bottom:-7px !important;border:0 !important;border-right:1px solid light-dark(#D8C8AF, #353B52) !important;border-bottom:1px solid light-dark(#D8C8AF, #353B52) !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-dialog-title{margin-bottom:2px !important;font:500 10px ui-monospace,SFMono-Regular,Menlo,Consolas,monospace !important;text-transform:uppercase !important;letter-spacing:.12em !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-field{gap:5px !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-field > span{color:light-dark(#4A4033, #C9CDE0) !important;font-size:12px !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-input{min-height:30px !important;padding:4px 8px !important;border:2px solid !important;border-color:light-dark(#B89F7B, #1F2339) light-dark(#F0E1C9, #4D587A) light-dark(#F0E1C9, #4D587A) light-dark(#B89F7B, #1F2339) !important;border-radius:0 !important;background:light-dark(#F4ECDF, #1A1D2C) !important;color:light-dark(#2B241B, #ECEAF2) !important;font:13px system-ui,-apple-system,\"Segoe UI\",sans-serif !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-dialog-actions{margin-top:4px !important;padding-top:12px !important;border-top:1px solid light-dark(#E8DCC6, #262B42) !important;flex-wrap:wrap !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) :is(.richclay-primary, .richclay-secondary){min-height:30px !important;padding:4px 10px !important;border:2px solid !important;border-color:light-dark(#FFF9ED, #3C4260) light-dark(#D6C3A5, #1B2033) light-dark(#D6C3A5, #1B2033) light-dark(#FFF9ED, #3C4260) !important;border-radius:0 !important;background:light-dark(#F1E7D4, #22273E) !important;color:light-dark(#2B241B, #ECEAF2) !important;font:13px system-ui,-apple-system,\"Segoe UI\",sans-serif !important}\n:is(.richclay-toolbar, .richclay-float, .richclay-dialog) .richclay-primary{background:light-dark(#2B241B, #ECEAF2) !important;color:light-dark(#FDF8F0, #0B0C13) !important;border-color:color-mix(in srgb, light-dark(#2B241B, #ECEAF2), white 24%) color-mix(in srgb, light-dark(#2B241B, #ECEAF2), black 32%) color-mix(in srgb, light-dark(#2B241B, #ECEAF2), black 32%) color-mix(in srgb, light-dark(#2B241B, #ECEAF2), white 24%) !important}\n.richclay-float:is(.richclay-toolbar, .richclay-float, .richclay-dialog){border-radius:0 !important;box-shadow:0 1px 2px rgba(0,0,0,.12),0 10px 28px -12px rgba(0,0,0,.35) !important}\n.richclay-float:is(.richclay-toolbar, .richclay-float, .richclay-dialog) > .richclay-toolbar{padding:5px !important}\n.richclay-toolbar{align-items:center !important;background:var(--richclay-surface-alt) !important;border:1px solid var(--richclay-border) !important;border-radius:var(--richclay-radius) !important;color:var(--richclay-text) !important;display:flex !important;flex-wrap:wrap !important;gap:2px !important;margin-block:0 6px !important;padding:4px !important}\n.richclay-toolbar:has(+ .richclay-editor){margin-block-end:0 !important;border-bottom-left-radius:0 !important;border-bottom-right-radius:0 !important}\n.richclay-button{align-items:center !important;appearance:none !important;background:transparent !important;border:1px solid transparent !important;border-radius:5px !important;color:currentColor !important;display:inline-flex !important;height:var(--richclay-control-size) !important;justify-content:center !important;margin:0 !important;min-width:var(--richclay-control-size) !important;padding:0 !important;position:relative !important}\n.richclay-button:hover,.richclay-button[aria-expanded=\"true\"],.richclay-menu-item:hover{background:var(--richclay-surface) !important;border-color:var(--richclay-border) !important}\n.richclay-button.is-active,.richclay-button[aria-pressed=\"true\"],.richclay-menu-item.is-active{background:rgb(0 0 0 / 0.13) !important;border-color:rgb(0 0 0 / 0.22) !important;color:var(--richclay-text) !important;box-shadow:inset 0 1px 3px rgb(0 0 0 / 0.26) !important}\n.richclay-button:focus-visible,.richclay-menu-item:focus-visible,.richclay-input:focus-visible,.richclay-primary:focus-visible,.richclay-secondary:focus-visible{outline:3px solid var(--richclay-focus) !important;outline-offset:2px !important}\n.richclay-button:disabled{color:var(--richclay-muted) !important;cursor:not-allowed !important;opacity:0.55 !important}\n.richclay-separator{align-self:stretch !important;background:var(--richclay-border) !important;display:inline-block !important;margin:5px 4px !important;width:1px !important}\n.richclay-menu-wrap{display:inline-flex !important;position:relative !important}\n.richclay-menu{background:var(--richclay-surface) !important;border:1px solid var(--richclay-border) !important;border-radius:var(--richclay-radius) !important;box-shadow:var(--richclay-shadow) !important;color:var(--richclay-text) !important;display:grid !important;gap:2px !important;inset-block-start:calc(100% + 4px) !important;inset-inline-start:0 !important;min-width:160px !important;padding:4px !important;position:absolute !important;z-index:1000 !important}\n.richclay-menu[hidden]{display:none !important}\n.richclay-menu-item{align-items:center !important;appearance:none !important;background:transparent !important;border:1px solid transparent !important;border-radius:4px !important;color:inherit !important;display:flex !important;font:inherit !important;gap:8px !important;min-height:32px !important;padding:5px 9px !important;text-align:start !important}\n.richclay-float{left:0 !important;position:fixed !important;top:0 !important;will-change:transform !important;z-index:99999 !important}\n.richclay-float .richclay-toolbar{box-shadow:var(--richclay-shadow) !important;margin-block:0 !important}\n.richclay-float-rail .richclay-toolbar{--richclay-control-size:28px !important;flex-direction:column !important;flex-wrap:nowrap !important}\n.richclay-float-rail .richclay-toolbar svg{height:15px !important;width:15px !important}\n.richclay-float-rail .richclay-separator{align-self:stretch !important;height:1px !important;margin:4px 5px !important;width:auto !important}\n.richclay-float-rail .richclay-menu{inset-block-start:0 !important;inset-inline-start:calc(100% + 4px) !important}\n.richclay-dialog{background:var(--richclay-surface) !important;border:1px solid var(--richclay-border) !important;border-radius:var(--richclay-radius) !important;box-shadow:var(--richclay-shadow) !important;color:var(--richclay-text) !important;display:grid !important;gap:10px !important;box-sizing:border-box !important;width:min(420px, calc(100vw - 16px)) !important;z-index:99999 !important;padding:12px !important}\n.richclay-dialog-title{font-weight:650 !important}\n.richclay-field{display:grid !important;gap:4px !important}\n.richclay-field span{color:var(--richclay-muted) !important;font-size:0.875rem !important}\n.richclay-input{background:var(--richclay-surface) !important;border:1px solid var(--richclay-border) !important;border-radius:4px !important;color:var(--richclay-text) !important;font:inherit !important;min-height:36px !important;padding:6px 8px !important}\n.richclay-dialog-actions{display:flex !important;gap:8px !important;justify-content:flex-end !important}\n.richclay-dialog-actions [data-richclay-remove-link]{margin-inline-end:auto !important}\n.richclay-primary,.richclay-secondary{appearance:none !important;border:1px solid var(--richclay-border) !important;border-radius:4px !important;font:inherit !important;min-height:34px !important;padding:5px 10px !important}\n.richclay-primary{background:var(--richclay-accent) !important;border-color:var(--richclay-accent) !important;color:var(--richclay-accent-ink) !important}\n.richclay-secondary{background:var(--richclay-surface-alt) !important;color:var(--richclay-text) !important}\n.richclay-sr-only{border:0 !important;clip:rect(0 0 0 0) !important;clip-path:inset(50%) !important;height:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;white-space:nowrap !important;width:1px !important}\n@media (forced-colors: active){\n.richclay-toolbar,.richclay-menu,.richclay-input{border-color:CanvasText !important}\n.richclay-dialog{border-color:CanvasText !important}\n.richclay-button.is-active,.richclay-button[aria-pressed=\"true\"],.richclay-menu-item.is-active,.richclay-primary{background:Highlight !important;color:HighlightText !important}\n.richclay-button:focus-visible,.richclay-menu-item:focus-visible,.richclay-input:focus-visible{outline-color:Highlight !important}\n}\n.richclay-toolbar[hidden]{display:none !important}\n.richclay-button[hidden]{display:none !important}\n.richclay-separator[hidden]{display:none !important}\n.richclay-menu-wrap[hidden]{display:none !important}\n.richclay-menu[hidden]{display:none !important}\n.richclay-menu-item[hidden]{display:none !important}\n.richclay-dialog[hidden]{display:none !important}\n.richclay-field[hidden]{display:none !important}\n.richclay-dialog-actions[hidden]{display:none !important}\n}";
package/src/ui/toast.js CHANGED
@@ -2,27 +2,19 @@
2
2
  // ❗️ don't use too much text!
3
3
  //
4
4
  // The Hyperclay dashboard's toast (Bevel's React toast with the dashboard's overrides),
5
- // drawn inline: a 3px tone edge, a 16px Phosphor glyph, 13.5px semibold text, a quiet close.
5
+ // drawn inline: a 3px tone edge, a 16px Bevel glyph, 13.5px semibold text, a quiet close.
6
6
  import { set } from "../lib/hostile-css.js";
7
7
  import { bevelBox, bevelSurface, bevelText, pageScheme, protectIcon } from "./bevel-controls.js";
8
- import { TOKENS, RULES } from "./bevel.js";
9
-
10
- const icon = (d) =>
11
- `<svg viewBox="0 0 256 256" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="${d}"/></svg>`;
12
-
13
- const CHECK = "M229.66,77.66l-128,128a8,8,0,0,1-11.32,0l-56-56a8,8,0,0,1,11.32-11.32L96,188.69,218.34,66.34a8,8,0,0,1,11.32,11.32Z";
14
- const WARNING = "M236.8,188.09,149.35,36.22h0a24.76,24.76,0,0,0-42.7,0L19.2,188.09a23.51,23.51,0,0,0,0,23.72A24.35,24.35,0,0,0,40.55,224h174.9a24.35,24.35,0,0,0,21.33-12.19A23.51,23.51,0,0,0,236.8,188.09ZM222.93,203.8a8.5,8.5,0,0,1-7.48,4.2H40.55a8.5,8.5,0,0,1-7.48-4.2,7.59,7.59,0,0,1,0-7.72L120.52,44.21a8.75,8.75,0,0,1,15,0l87.45,151.87A7.59,7.59,0,0,1,222.93,203.8ZM120,144V104a8,8,0,0,1,16,0v40a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,180Z";
15
- const INFO = "M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z";
16
- const X = "M205.66,194.34a8,8,0,0,1-11.32,11.32L128,139.31,61.66,205.66a8,8,0,0,1-11.32-11.32L116.69,128,50.34,61.66A8,8,0,0,1,61.66,50.34L128,116.69l66.34-66.35a8,8,0,0,1,11.32,11.32L139.31,128Z";
8
+ import { TOKENS, RULES, GLYPHS } from "./bevel.js";
17
9
 
18
10
  const TONES = {
19
- success: { edge: TOKENS.teal, icon: icon(CHECK) },
20
- error: { edge: TOKENS.ox, icon: icon(WARNING) },
21
- warning: { edge: TOKENS.brass, icon: icon(WARNING) },
22
- info: { edge: TOKENS.brass, icon: icon(INFO) },
11
+ success: { edge: TOKENS.teal, icon: GLYPHS.toastSuccess },
12
+ error: { edge: TOKENS.ox, icon: GLYPHS.toastWarning },
13
+ warning: { edge: TOKENS.brass, icon: GLYPHS.toastWarning },
14
+ info: { edge: TOKENS.brass, icon: GLYPHS.toastInfo },
23
15
  };
24
16
 
25
- const CLOSE_ICON = icon(X);
17
+ const CLOSE_ICON = GLYPHS.toastClose;
26
18
 
27
19
  const HIDDEN = ["opacity:0", "transform:translateX(24px)"];
28
20