@panphora/clayjs 1.8.2 → 1.9.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.
@@ -1,4 +1,4 @@
1
- /* clayjs 1.8.2 standalone build: every clayjs module in one file. https://clayjs.com/offline
1
+ /* clayjs 1.9.1 standalone build: every clayjs module in one file. https://clayjs.com/offline
2
2
  Third-party code inside keeps its own license (Sortable MIT, Squire MIT, DOMPurify Apache-2.0 OR MPL-2.0, MicroModal MIT, parse5 MIT): https://clayjs.com/THIRD-PARTY-NOTICES.md */
3
3
  (() => {
4
4
  var __create = Object.create;
@@ -111,7 +111,7 @@
111
111
  SAVE_TOKEN_ATTRS = ["savetoken"];
112
112
  LEGACY_SAVE_TOKEN_ATTRS = ["htmlclaytoken"];
113
113
  HOST_IDENTITY_ATTRS = ["documentid", "htmlclayid"];
114
- HOST_RESPONSE_ATTRS = ["documentetag"];
114
+ HOST_RESPONSE_ATTRS = ["documentetag", "sitevisibility"];
115
115
  HOST_TOKEN_ATTRS = [
116
116
  ...SAVE_TOKEN_ATTRS,
117
117
  ...LEGACY_SAVE_TOKEN_ATTRS,
@@ -157,11 +157,17 @@
157
157
  (attr) => document.documentElement.getAttribute(attr)
158
158
  );
159
159
  }
160
- var servedDocumentEtag, warnedAboutLegacyToken;
160
+ var servedDocumentEtag, SITE_VISIBILITIES, servedSiteVisibility, warnedAboutLegacyToken;
161
161
  var init_host_attrs = __esm({
162
162
  "src/core/host-attrs.js"() {
163
163
  init_root_attrs();
164
164
  servedDocumentEtag = typeof document === "undefined" ? null : document.documentElement.getAttribute("documentetag") || null;
165
+ SITE_VISIBILITIES = /* @__PURE__ */ new Set(["private", "public"]);
166
+ servedSiteVisibility = (() => {
167
+ if (typeof document === "undefined") return null;
168
+ const value = document.documentElement.getAttribute("sitevisibility");
169
+ return SITE_VISIBILITIES.has(value) ? value : null;
170
+ })();
165
171
  warnedAboutLegacyToken = false;
166
172
  }
167
173
  });
@@ -170,9 +176,10 @@
170
176
  var is_edit_mode_exports = {};
171
177
  __export(is_edit_mode_exports, {
172
178
  isEditMode: () => isEditMode,
173
- isOwner: () => isOwner
179
+ isOwner: () => isOwner,
180
+ siteVisibility: () => siteVisibility
174
181
  });
175
- var forcedEditMode, isViewOnlyDocument, isEditMode, isOwner;
182
+ var forcedEditMode, isViewOnlyDocument, isEditMode, isOwner, siteVisibility;
176
183
  var init_is_edit_mode = __esm({
177
184
  "src/core/is-edit-mode.js"() {
178
185
  init_cookie();
@@ -189,6 +196,7 @@
189
196
  isViewOnlyDocument = typeof document !== "undefined" && document.documentElement.hasAttribute("viewonly");
190
197
  isEditMode = query_default.editmode ? query_default.editmode === "true" : isViewOnlyDocument || servedStaleToken() ? false : forcedEditMode != null ? forcedEditMode : hasSaveToken() || Boolean(cookie_default.get("isAdminOfCurrentResource"));
191
198
  isOwner = Boolean(cookie_default.get("isAdminOfCurrentResource"));
199
+ siteVisibility = servedSiteVisibility;
192
200
  }
193
201
  });
194
202
 
@@ -607,14 +615,14 @@
607
615
  __export(mutation_exports, {
608
616
  default: () => mutation_default
609
617
  });
610
- function isLibraryRootAttr(record) {
611
- return record.type === "attributes" && record.target === document.documentElement && ROOT_LIBRARY_ATTR_SET.has(record.attributeName);
618
+ function isLibraryRootAttr(record2) {
619
+ return record2.type === "attributes" && record2.target === document.documentElement && ROOT_LIBRARY_ATTR_SET.has(record2.attributeName);
612
620
  }
613
621
  function withoutLibraryNoise(records3) {
614
622
  return records3.some(isLibraryRootAttr) ? records3.filter((r) => !isLibraryRootAttr(r)) : records3;
615
623
  }
616
- function policyBeforeMutation(record) {
617
- let source2 = record.target;
624
+ function policyBeforeMutation(record2) {
625
+ let source2 = record2.target;
618
626
  let clone = source2.cloneNode(false);
619
627
  const leaf = clone;
620
628
  while (source2.parentElement) {
@@ -623,8 +631,8 @@
623
631
  parent.appendChild(clone);
624
632
  clone = parent;
625
633
  }
626
- if (record.oldValue == null) leaf.removeAttribute(record.attributeName);
627
- else leaf.setAttribute(record.attributeName, record.oldValue);
634
+ if (record2.oldValue == null) leaf.removeAttribute(record2.attributeName);
635
+ else leaf.setAttribute(record2.attributeName, record2.oldValue);
628
636
  return resolveRegionPolicy(leaf);
629
637
  }
630
638
  var dummyElem, ROOT_LIBRARY_ATTR_SET, localMutation, existingHub, Mutation, mutation_default;
@@ -1144,7 +1152,7 @@
1144
1152
  },
1145
1153
  _recomputeHasNonPausable() {
1146
1154
  this._hasNonPausable = Object.values(this._callbacks).some(
1147
- (list2) => list2.some((cb) => cb.pausable === false)
1155
+ (list3) => list3.some((cb) => cb.pausable === false)
1148
1156
  );
1149
1157
  },
1150
1158
  _startObserving() {
@@ -1265,16 +1273,16 @@
1265
1273
  });
1266
1274
 
1267
1275
  // src/lib/content-dom.js
1268
- function copyControlState(source2, copy) {
1276
+ function copyControlState(source2, copy2) {
1269
1277
  if (!/^(INPUT|TEXTAREA|SELECT|OPTION)$/.test(source2.tagName || "")) return;
1270
1278
  const type = (source2.getAttribute?.("type") || "").toLowerCase();
1271
- if ("value" in source2 && "value" in copy && source2.tagName !== "OPTION" && type !== "checkbox" && type !== "radio" && type !== "file") copy.value = source2.value;
1272
- if (source2.tagName === "INPUT" && (type === "checkbox" || type === "radio")) copy.checked = source2.checked;
1273
- if (source2.tagName === "OPTION") copy.selected = source2.selected;
1279
+ if ("value" in source2 && "value" in copy2 && source2.tagName !== "OPTION" && type !== "checkbox" && type !== "radio" && type !== "file") copy2.value = source2.value;
1280
+ if (source2.tagName === "INPUT" && (type === "checkbox" || type === "radio")) copy2.checked = source2.checked;
1281
+ if (source2.tagName === "OPTION") copy2.selected = source2.selected;
1274
1282
  if (source2.tagName === "SELECT") {
1275
- for (let i2 = 0; i2 < source2.options.length; i2++) copy.options[i2].selected = source2.options[i2].selected;
1283
+ for (let i2 = 0; i2 < source2.options.length; i2++) copy2.options[i2].selected = source2.options[i2].selected;
1276
1284
  }
1277
- if ("indeterminate" in source2 && "indeterminate" in copy) copy.indeterminate = source2.indeterminate;
1285
+ if ("indeterminate" in source2 && "indeterminate" in copy2) copy2.indeterminate = source2.indeterminate;
1278
1286
  }
1279
1287
  function matchesWithin(node, selector, boundary, inherit) {
1280
1288
  if (inherit) return !!node.closest?.(selector);
@@ -1288,11 +1296,11 @@
1288
1296
  }
1289
1297
  function importTree(source2, targetDocument, capability, capabilityMatch, exclude, boundary, inherit, maps) {
1290
1298
  if (source2.nodeType === 1 && ((inherit ? hasCapability(source2, capability) : matchesWithin(source2, capabilityMatch, boundary, false)) || exclude && (inherit && source2 !== boundary ? source2.matches(exclude) : matchesWithin(source2, exclude, boundary, inherit)))) return null;
1291
- const copy = targetDocument.importNode(source2, false);
1292
- maps.cloneToLive.set(copy, source2);
1293
- maps.liveToClone.set(source2, copy);
1299
+ const copy2 = targetDocument.importNode(source2, false);
1300
+ maps.cloneToLive.set(copy2, source2);
1301
+ maps.liveToClone.set(source2, copy2);
1294
1302
  const sourceChildren = source2.nodeType === 1 && source2.tagName === "TEMPLATE" ? source2.content : source2;
1295
- const copyChildren = copy.nodeType === 1 && copy.tagName === "TEMPLATE" ? copy.content : copy;
1303
+ const copyChildren = copy2.nodeType === 1 && copy2.tagName === "TEMPLATE" ? copy2.content : copy2;
1296
1304
  if (sourceChildren !== source2) {
1297
1305
  maps.cloneToLive.set(copyChildren, sourceChildren);
1298
1306
  maps.liveToClone.set(sourceChildren, copyChildren);
@@ -1304,8 +1312,8 @@
1304
1312
  if (child.nodeType === 1) copyControlState(child, childCopy);
1305
1313
  }
1306
1314
  }
1307
- if (source2.nodeType === 1) copyControlState(source2, copy);
1308
- return copy;
1315
+ if (source2.nodeType === 1) copyControlState(source2, copy2);
1316
+ return copy2;
1309
1317
  }
1310
1318
  function createContentView(context, { capability = "data", exclude = null, inherit = true } = {}) {
1311
1319
  if (!context) throw new TypeError("createContentView requires a DOM context");
@@ -1658,8 +1666,8 @@
1658
1666
  return captureForSave({ emitForSync: false });
1659
1667
  }
1660
1668
  function serializeRecoveryClone(clone, { prepared = false } = {}) {
1661
- const copy = clonePreventingOnclone(clone);
1662
- return serializeClone(prepared ? copy : prepareCloneForSave(copy, { authored: false }));
1669
+ const copy2 = clonePreventingOnclone(clone);
1670
+ return serializeClone(prepared ? copy2 : prepareCloneForSave(copy2, { authored: false }));
1663
1671
  }
1664
1672
  function captureRecoveryPage() {
1665
1673
  return serializeSaveClone(captureSaveClone({ flushUndo: false, authored: false }));
@@ -2029,6 +2037,96 @@
2029
2037
  }
2030
2038
  });
2031
2039
 
2040
+ // src/ui/theme-parts.js
2041
+ function stateList({ hover = false, active: active3 = false, focus = false, disabled = false } = {}) {
2042
+ const list3 = ["base"];
2043
+ if (!disabled && hover) list3.push("hover");
2044
+ if (!disabled && active3) list3.push("active");
2045
+ if (focus) list3.push("focus");
2046
+ if (disabled) list3.push("disabled");
2047
+ return list3;
2048
+ }
2049
+ function neutral(t, pct) {
2050
+ if (t.background && t.background !== t.surface) return pct > 8 ? mix(t.background, t.text || "currentColor", 6) : t.background;
2051
+ if (t.surface) return mix(t.surface, t.text || "currentColor", pct);
2052
+ return t.background;
2053
+ }
2054
+ function button(t, variant, state2) {
2055
+ const r = [];
2056
+ const fill = { primary: ["accent", "accentText"], danger: ["danger", "dangerText"] }[variant];
2057
+ if (state2 === "base") r.push(...decl("font-family", t.font), ...decl("border-radius", t.radius));
2058
+ if (state2 === "base" && t.buttonHeight) r.push("height:auto", "block-size:auto", `min-height:${t.buttonHeight}`);
2059
+ if (fill) {
2060
+ const bg = t[fill[0]];
2061
+ const fg = t[fill[1]];
2062
+ if (state2 === "base") r.push(...decl("background", bg), ...decl("border-color", bg), ...decl("color", fg));
2063
+ if (state2 === "hover" && bg && fg) r.push(`background:${mix(bg, fg, 12)}`);
2064
+ if (state2 === "active" && bg) r.push(`border-color:${bg}`, ...fg ? [`background:${mix(bg, fg, 20)}`] : []);
2065
+ if (state2 === "disabled" && bg) r.push("opacity:.55");
2066
+ } else if (variant === "quiet") {
2067
+ if (state2 === "base") r.push(...decl("color", t.text));
2068
+ if (state2 === "hover") r.push(...decl("background", neutral(t, 7)));
2069
+ if (state2 === "active") r.push(...decl("background", neutral(t, 12)));
2070
+ if (state2 === "disabled") r.push(...decl("color", t.mutedText));
2071
+ } else {
2072
+ if (state2 === "base") r.push(...decl("background", t.surface), ...decl("color", t.text), ...decl("border-color", t.border));
2073
+ if (state2 === "hover") r.push(...decl("background", neutral(t, 7)));
2074
+ if (state2 === "active") r.push(...decl("background", neutral(t, 12)), ...decl("border-color", t.border));
2075
+ if (state2 === "disabled") r.push(...decl("color", t.mutedText));
2076
+ }
2077
+ if (state2 === "focus" && t.accent) r.push(`outline:2px solid ${t.accent}`);
2078
+ return r;
2079
+ }
2080
+ function tokenRules(theme2, role, state2 = "base") {
2081
+ if (!themed(theme2)) return [];
2082
+ if (role.startsWith("button.")) return button(theme2.tokens, role.slice(7), state2);
2083
+ return ROLES[role] ? ROLES[role](theme2.tokens, state2) : [];
2084
+ }
2085
+ function tokenRulesFor(theme2, role, active3) {
2086
+ return stateList(active3).flatMap((s2) => tokenRules(theme2, role, s2));
2087
+ }
2088
+ function partRules(theme2, names, active3) {
2089
+ if (!themed(theme2) || !names?.length) return [];
2090
+ const states2 = stateList(active3);
2091
+ const r = [];
2092
+ for (const name of names) {
2093
+ const part = theme2.parts[name];
2094
+ if (!part) continue;
2095
+ for (const s2 of states2) for (const [prop, value] of Object.entries(part[s2] || {})) r.push(`${prop}:${value}`);
2096
+ }
2097
+ return r;
2098
+ }
2099
+ function markParts(el5, theme2, names) {
2100
+ if (themed(theme2) && names?.length) el5.setAttribute("data-clay-part", names.join(" "));
2101
+ }
2102
+ function floorRules(rules, prop, px) {
2103
+ if (!px) return [];
2104
+ const last = [...rules].reverse().find((r) => r.startsWith(`${prop}:`));
2105
+ const value = last ? last.slice(prop.length + 1).trim() : "";
2106
+ const out = [`${prop}:${px}px`];
2107
+ if (value && value !== `${px}px`) out.push(`${prop}:max(${px}px, ${value})`);
2108
+ return out;
2109
+ }
2110
+ var themed, mix, decl, ROLES;
2111
+ var init_theme_parts = __esm({
2112
+ "src/ui/theme-parts.js"() {
2113
+ themed = (theme2) => !!theme2 && !theme2.bevel;
2114
+ mix = (a, b, pct) => `color-mix(in srgb, ${a}, ${b} ${pct}%)`;
2115
+ decl = (prop, value) => value ? [`${prop}:${value}`] : [];
2116
+ ROLES = {
2117
+ input: (t, s2) => s2 === "base" ? [...decl("font-family", t.font), ...decl("color", t.text), ...decl("background", t.surface), ...decl("border-color", t.border), ...decl("border-radius", t.radius)] : s2 === "hover" ? decl("border-color", t.mutedText) : s2 === "focus" && t.accent ? [`outline:2px solid ${t.accent}`, `border-color:${t.accent}`] : [],
2118
+ surface: (t, s2) => s2 === "base" ? [...decl("background", t.surface), ...decl("color", t.text), ...decl("border-color", t.border), ...decl("box-shadow", t.shadow), ...decl("border-radius", t.radius), ...decl("font-family", t.font)] : [],
2119
+ text: (t, s2) => s2 === "base" ? [...decl("color", t.text), ...decl("font-family", t.font)] : [],
2120
+ mutedText: (t, s2) => s2 === "base" ? [...decl("color", t.mutedText), ...decl("font-family", t.font)] : [],
2121
+ heading: (t, s2) => s2 === "base" ? [...decl("font-family", t.headingFont || t.font), ...decl("color", t.text)] : [],
2122
+ code: (t, s2) => s2 === "base" ? decl("color", t.text) : [],
2123
+ well: (t, s2) => s2 === "base" ? [...decl("background", t.background), ...t.border ? [`border:1px solid ${t.border}`] : [], ...decl("border-radius", t.radius)] : [],
2124
+ overlay: (t, s2) => s2 === "base" ? decl("background", t.overlay) : [],
2125
+ close: (t, s2) => s2 === "base" ? [...decl("background", t.surface), ...decl("color", t.mutedText), ...decl("border-color", t.border)] : s2 === "hover" ? decl("background", neutral(t, 7)) : s2 === "focus" && t.accent ? [`outline:2px solid ${t.accent}`] : []
2126
+ };
2127
+ }
2128
+ });
2129
+
2032
2130
  // src/ui/bevel-controls.js
2033
2131
  var bevel_controls_exports = {};
2034
2132
  __export(bevel_controls_exports, {
@@ -2044,6 +2142,7 @@
2044
2142
  pageScheme: () => pageScheme,
2045
2143
  paintInput: () => paintInput,
2046
2144
  protectIcon: () => protectIcon,
2145
+ restyleBox: () => restyleBox,
2047
2146
  setShown: () => setShown
2048
2147
  });
2049
2148
  function runtime(el5) {
@@ -2088,12 +2187,13 @@
2088
2187
  style(el5, rules);
2089
2188
  }
2090
2189
  }
2091
- function bevelButton(label, { variant = "default", small = false, onClick = null, extra = [], labelExtra = [], onState = null } = {}) {
2190
+ function bevelButton(label, { variant = "default", small = false, onClick = null, extra = [], labelExtra = [], onState = null, theme: theme2 = null, parts = [], labelParts = [] } = {}) {
2092
2191
  const v2 = VARIANT[variant] || VARIANT.default;
2093
2192
  const size = small ? SIZE.small : SIZE.normal;
2094
2193
  const reducedMotion = media("(prefers-reduced-motion: reduce)");
2095
2194
  const forced = media("(forced-colors: active)");
2096
2195
  const coarse = media("(pointer: coarse)");
2196
+ const narrow = media("(max-width: 760px)");
2097
2197
  const flags = { hovered: false, pressed: false, focusVisible: false, disabled: false };
2098
2198
  const pinned = /* @__PURE__ */ new Map();
2099
2199
  const b = runtime(make("button", []));
@@ -2101,6 +2201,11 @@
2101
2201
  const span = runtime(make("span", []));
2102
2202
  span.textContent = label;
2103
2203
  b.append(span);
2204
+ const variantName = VARIANT[variant] ? variant : "default";
2205
+ const names = ["button", `button.${variantName}`, ...parts];
2206
+ const labelNames = ["buttonLabel", ...labelParts];
2207
+ markParts(b, theme2, names);
2208
+ markParts(span, theme2, labelNames);
2104
2209
  function applyState() {
2105
2210
  const rules = [...RESET, "box-sizing:border-box", ...v2.rest, ...size];
2106
2211
  if (flags.disabled) {
@@ -2112,13 +2217,25 @@
2112
2217
  if (flags.focusVisible) rules.push(...RULES.focus);
2113
2218
  if (forced) rules.push(...MEDIA.forcedColors);
2114
2219
  if (coarse) rules.push(...MEDIA.coarseButton);
2220
+ if (narrow) rules.push("min-height:44px");
2115
2221
  rules.push(...extra);
2222
+ if (themed(theme2)) {
2223
+ const act = { hover: flags.hovered, active: flags.pressed, focus: flags.focusVisible, disabled: flags.disabled };
2224
+ rules.push(...tokenRulesFor(theme2, `button.${variantName}`, act), ...partRules(theme2, names, act));
2225
+ if (forced) rules.push(...MEDIA.forcedColors);
2226
+ rules.push(...floorRules(rules, "min-height", narrow ? 44 : coarse ? 40 : 0));
2227
+ }
2116
2228
  for (const [prop, value] of pinned) rules.push(`${prop}:${value}`);
2117
2229
  restyle(b, rules);
2118
2230
  const labelRules = [...RESET, "font:inherit", "color:inherit", ...RULES.buttonLabel];
2119
2231
  if (flags.disabled) labelRules.push(...RULES.buttonDisabledLabel);
2120
2232
  else if (flags.pressed) labelRules.push(...reducedMotion ? MEDIA.reducedMotion : RULES.buttonActiveLabel);
2121
2233
  labelRules.push(...labelExtra);
2234
+ if (themed(theme2)) {
2235
+ const act = { hover: flags.hovered, active: flags.pressed, focus: flags.focusVisible, disabled: flags.disabled };
2236
+ labelRules.push(...partRules(theme2, labelNames, act));
2237
+ if (flags.pressed && !flags.disabled && reducedMotion) labelRules.push(...MEDIA.reducedMotion);
2238
+ }
2122
2239
  restyle(span, labelRules);
2123
2240
  onState?.({ ...flags });
2124
2241
  }
@@ -2251,35 +2368,72 @@
2251
2368
  paint({ hovered: false, focusVisible: false });
2252
2369
  return b;
2253
2370
  }
2254
- function bevelBox(tag, rules = []) {
2255
- return runtime(make(tag, [...RESET, "box-sizing:border-box", ...rules]));
2371
+ function bevelBox(tag, rules = [], { theme: theme2 = null, parts = [], role = null } = {}) {
2372
+ const el5 = runtime(make(tag, [...RESET, "box-sizing:border-box", ...rules]));
2373
+ if (themed(theme2)) {
2374
+ if (role) style(el5, tokenRulesFor(theme2, role, {}));
2375
+ const names = [...["text", "mutedText", "well"].includes(role) ? [role] : [], ...parts];
2376
+ style(el5, partRules(theme2, names, {}));
2377
+ markParts(el5, theme2, names);
2378
+ }
2379
+ return el5;
2380
+ }
2381
+ function restyleBox(el5, rules) {
2382
+ restyle(el5, [...RESET, "box-sizing:border-box", ...rules]);
2256
2383
  }
2257
- function bevelSurface(tag, rules = []) {
2258
- return runtime(make(tag, [...RESET, "box-sizing:border-box", ...RULES.surface, `font:14px/1.5 ${FONT_SANS}`, ...rules]));
2384
+ function bevelSurface(tag, rules = [], { theme: theme2 = null, parts = [], role = "surface" } = {}) {
2385
+ const el5 = runtime(make(tag, [...RESET, "box-sizing:border-box", ...RULES.surface, `font:14px/1.5 ${FONT_SANS}`, ...rules]));
2386
+ if (themed(theme2)) {
2387
+ if (role) style(el5, tokenRulesFor(theme2, role, {}));
2388
+ style(el5, partRules(theme2, parts, {}));
2389
+ markParts(el5, theme2, parts);
2390
+ }
2391
+ return el5;
2259
2392
  }
2260
- function bevelWell(rules = []) {
2261
- return runtime(make("div", [...RESET, "display:block", "box-sizing:border-box", `font:14px/1.5 ${FONT_SANS}`, `color:${TOKENS2.ink}`, ...RULES.recess, ...rules]));
2393
+ function bevelWell(rules = [], { theme: theme2 = null, parts = [], role = "well" } = {}) {
2394
+ const el5 = runtime(make("div", [...RESET, "display:block", "box-sizing:border-box", `font:14px/1.5 ${FONT_SANS}`, `color:${TOKENS2.ink}`, ...RULES.recess, ...rules]));
2395
+ if (themed(theme2)) {
2396
+ if (role) style(el5, tokenRulesFor(theme2, role, {}));
2397
+ const names = [...["text", "mutedText", "well"].includes(role) ? [role] : [], ...parts];
2398
+ style(el5, partRules(theme2, names, {}));
2399
+ markParts(el5, theme2, names);
2400
+ }
2401
+ return el5;
2262
2402
  }
2263
- function bevelText(tag, rules = [], text) {
2403
+ function bevelText(tag, rules = [], text, { theme: theme2 = null, parts = [], role = "text" } = {}) {
2264
2404
  const el5 = runtime(make(tag, [...RESET, "font:inherit", `color:${TOKENS2.ink}`, ...rules]));
2265
2405
  if (text != null) el5.textContent = text;
2406
+ if (themed(theme2)) {
2407
+ if (role) style(el5, tokenRulesFor(theme2, role, {}));
2408
+ const names = [...["text", "mutedText", "well"].includes(role) ? [role] : [], ...parts];
2409
+ style(el5, partRules(theme2, names, {}));
2410
+ markParts(el5, theme2, names);
2411
+ }
2266
2412
  return el5;
2267
2413
  }
2268
- function paintInput(el5, { rules = [] } = {}) {
2414
+ function paintInput(el5, { rules = [], theme: theme2 = null, parts = [] } = {}) {
2269
2415
  const flags = { hovered: false, focused: false };
2270
2416
  const forced = media("(forced-colors: active)");
2271
2417
  const coarse = media("(pointer: coarse)");
2272
2418
  runtime(el5);
2419
+ const names = ["input", ...parts];
2420
+ markParts(el5, theme2, names);
2273
2421
  function applyState() {
2274
2422
  const height = el5.localName === "textarea" ? el5.style.getPropertyValue("height") : "";
2275
- const list2 = [...RESET, "box-sizing:border-box", ...RULES.input];
2276
- if (flags.hovered) list2.push(...RULES.inputHover);
2277
- if (flags.focused) list2.push(...RULES.inputFocus);
2278
- if (forced) list2.push(...MEDIA.forcedColors);
2279
- list2.push(...rules);
2280
- if (coarse) list2.push(...MEDIA.coarseInput);
2281
- if (height) list2.push(`height:${height}`);
2282
- restyle(el5, list2);
2423
+ const list3 = [...RESET, "box-sizing:border-box", ...RULES.input];
2424
+ if (flags.hovered) list3.push(...RULES.inputHover);
2425
+ if (flags.focused) list3.push(...RULES.inputFocus);
2426
+ if (forced) list3.push(...MEDIA.forcedColors);
2427
+ list3.push(...rules);
2428
+ if (coarse) list3.push(...MEDIA.coarseInput);
2429
+ if (themed(theme2)) {
2430
+ const act = { hover: flags.hovered && !el5.disabled, focus: flags.focused, disabled: !!el5.disabled };
2431
+ list3.push(...tokenRulesFor(theme2, "input", act), ...partRules(theme2, names, act));
2432
+ if (forced) list3.push(...MEDIA.forcedColors);
2433
+ if (coarse) list3.push(...floorRules(list3, "font-size", 16));
2434
+ }
2435
+ if (height) list3.push(`height:${height}`);
2436
+ restyle(el5, list3);
2283
2437
  }
2284
2438
  el5.addEventListener("pointerenter", () => {
2285
2439
  flags.hovered = true;
@@ -2319,6 +2473,7 @@
2319
2473
  "src/ui/bevel-controls.js"() {
2320
2474
  init_hostile_css();
2321
2475
  init_bevel();
2476
+ init_theme_parts();
2322
2477
  RUNTIME_ONLY = "no-save no-watch no-snapshot";
2323
2478
  RESET = ["all:initial", "color-scheme:inherit", "direction:ltr", "unicode-bidi:isolate"];
2324
2479
  SIZE = {
@@ -2337,7 +2492,7 @@
2337
2492
  if (typeof document !== "undefined") {
2338
2493
  document.addEventListener("keydown", () => {
2339
2494
  keyboardModality = true;
2340
- for (const refresh of focusedButtons) refresh();
2495
+ for (const refresh2 of focusedButtons) refresh2();
2341
2496
  }, true);
2342
2497
  document.addEventListener("pointerdown", () => {
2343
2498
  keyboardModality = false;
@@ -2437,6 +2592,13 @@
2437
2592
  });
2438
2593
 
2439
2594
  // src/core/host-meta.js
2595
+ function settle(outcome2, meta = bareHost()) {
2596
+ outcomes.set(meta, outcome2);
2597
+ return meta;
2598
+ }
2599
+ function hostMetaOutcome(meta) {
2600
+ return outcomes.get(meta) ?? "none";
2601
+ }
2440
2602
  function bareHost() {
2441
2603
  return { spec: null, extensions: [], document: null };
2442
2604
  }
@@ -2446,10 +2608,15 @@
2446
2608
  return inFlight;
2447
2609
  }
2448
2610
  if (!refreshing) {
2611
+ const previous = inFlight;
2449
2612
  refreshing = fetchMeta().catch(bareHost).finally(() => {
2450
2613
  refreshing = null;
2451
2614
  });
2452
- inFlight = refreshing;
2615
+ inFlight = refreshing.then(async (meta) => {
2616
+ if (hostMetaOutcome(meta) === "ok" || !previous) return meta;
2617
+ const last = await previous;
2618
+ return hostMetaOutcome(last) === "ok" ? last : meta;
2619
+ });
2453
2620
  }
2454
2621
  return refreshing;
2455
2622
  }
@@ -2458,6 +2625,7 @@
2458
2625
  return meta.extensions.includes(name);
2459
2626
  }
2460
2627
  async function fetchMeta() {
2628
+ if (!/^https?:$/.test(window.location.protocol)) return settle("none");
2461
2629
  const token = saveToken();
2462
2630
  const path = token ? `${META_PATH}/${token}` : META_PATH;
2463
2631
  const controller = new AbortController();
@@ -2476,33 +2644,34 @@
2476
2644
  redirect: "manual",
2477
2645
  signal: controller.signal
2478
2646
  });
2479
- if (!res.ok) return bareHost();
2647
+ if (!res.ok) return settle(res.status >= 500 ? "failed" : "none");
2480
2648
  const text = await res.text();
2481
- if (!text) return bareHost();
2649
+ if (!text) return settle("none");
2482
2650
  let body;
2483
2651
  try {
2484
2652
  body = JSON.parse(text);
2485
2653
  } catch (_) {
2486
- return bareHost();
2654
+ return settle("none");
2487
2655
  }
2488
- if (!body || typeof body !== "object" || typeof body.spec !== "number") return bareHost();
2489
- return {
2656
+ if (!body || typeof body !== "object" || typeof body.spec !== "number") return settle("none");
2657
+ return settle("ok", {
2490
2658
  spec: body.spec,
2491
2659
  extensions: Array.isArray(body.extensions) ? body.extensions : [],
2492
2660
  document: body.document && typeof body.document === "object" ? body.document : null
2493
- };
2661
+ });
2494
2662
  } catch (_) {
2495
- return bareHost();
2663
+ return settle("failed");
2496
2664
  } finally {
2497
2665
  clearTimeout(timeoutId);
2498
2666
  }
2499
2667
  }
2500
- var META_PATH, META_TIMEOUT_MS, inFlight, refreshing;
2668
+ var META_PATH, META_TIMEOUT_MS, outcomes, inFlight, refreshing;
2501
2669
  var init_host_meta = __esm({
2502
2670
  "src/core/host-meta.js"() {
2503
2671
  init_host_attrs();
2504
2672
  META_PATH = "/_/meta";
2505
2673
  META_TIMEOUT_MS = 6e3;
2674
+ outcomes = /* @__PURE__ */ new WeakMap();
2506
2675
  inFlight = null;
2507
2676
  refreshing = null;
2508
2677
  }
@@ -2513,7 +2682,7 @@
2513
2682
  let lastCall = executeFirst ? 0 : Date.now();
2514
2683
  let timeoutId = null;
2515
2684
  let pending4 = [];
2516
- function settle3(ctx, args, waiting) {
2685
+ function settle4(ctx, args, waiting) {
2517
2686
  let result2;
2518
2687
  try {
2519
2688
  result2 = callback.apply(ctx, args);
@@ -2541,7 +2710,7 @@
2541
2710
  lastCall = now3;
2542
2711
  const waiting = pending4.concat({ resolve, reject });
2543
2712
  pending4 = [];
2544
- settle3(ctx, args, waiting);
2713
+ settle4(ctx, args, waiting);
2545
2714
  } else {
2546
2715
  pending4.push({ resolve, reject });
2547
2716
  if (!timeoutId) {
@@ -2550,7 +2719,7 @@
2550
2719
  timeoutId = null;
2551
2720
  const waiting = pending4;
2552
2721
  pending4 = [];
2553
- settle3(ctx, args, waiting);
2722
+ settle4(ctx, args, waiting);
2554
2723
  }, remaining);
2555
2724
  }
2556
2725
  }
@@ -3632,7 +3801,7 @@
3632
3801
  }
3633
3802
  async function flushBytes(keepalive, deadline) {
3634
3803
  if (!baselineSettled()) await nextEvent("clay:baseline-settled", deadline);
3635
- let saved = false;
3804
+ let saved2 = false;
3636
3805
  let etag = null;
3637
3806
  for (let attempt = 0; attempt < 4; attempt++) {
3638
3807
  if (isSaveInProgress()) await settled(deadline);
@@ -3641,12 +3810,12 @@
3641
3810
  if (!bytesUnsaved()) {
3642
3811
  gateClearIfUnchanged(token);
3643
3812
  if (hasUnsavedState()) throw flushError("This document has unsaved work a save cannot write.", "blocked");
3644
- return { state: saved ? "saved" : "clean", etag: etag ?? lastSeenEtag() };
3813
+ return { state: saved2 ? "saved" : "clean", etag: etag ?? lastSeenEtag() };
3645
3814
  }
3646
3815
  const result2 = await withKeepalive(keepalive, () => savePage());
3647
3816
  if (result2.msgType === "skipped") continue;
3648
3817
  if (!result2.ok) throw flushError(result2.msg || "Save failed", result2.msgType === "conflict" || isSaveConflicted() ? "conflict" : "failed");
3649
- saved = true;
3818
+ saved2 = true;
3650
3819
  etag = result2.etag ?? null;
3651
3820
  }
3652
3821
  throw flushError("The document kept changing while saving.", "failed");
@@ -4229,8 +4398,8 @@
4229
4398
  function side(hit, ctx) {
4230
4399
  return { before: ctx?.before || "", hit, after: ctx?.after || "", cutBefore: !!ctx?.cutBefore, cutAfter: !!ctx?.cutAfter };
4231
4400
  }
4232
- function structuralSentence(record, kind) {
4233
- const reason = record.detail || record.reason;
4401
+ function structuralSentence(record2, kind) {
4402
+ const reason = record2.detail || record2.reason;
4234
4403
  const make2 = SENTENCE[reason];
4235
4404
  return make2 ? make2(kind.toLowerCase()) : "Your change here was replaced by the other edit.";
4236
4405
  }
@@ -4265,8 +4434,8 @@
4265
4434
  }
4266
4435
  return n2 || null;
4267
4436
  }
4268
- function localNode(record, ref) {
4269
- const root3 = window.clay?.conflicts?.recoveryOf?.(record.id)?.root;
4437
+ function localNode(record2, ref) {
4438
+ const root3 = window.clay?.conflicts?.recoveryOf?.(record2.id)?.root;
4270
4439
  const path = ref?.local?.[0];
4271
4440
  return root3 && path ? nodeAtPath(root3, path) : null;
4272
4441
  }
@@ -4284,7 +4453,7 @@
4284
4453
  const under = el5.isConnected ? headingAbove(el5) : null;
4285
4454
  return under ? `under ${under}` : null;
4286
4455
  }
4287
- function recoverySentence(record, rv, kind) {
4456
+ function recoverySentence(record2, rv, kind) {
4288
4457
  const s2 = rv.structure;
4289
4458
  const k2 = kind.toLowerCase();
4290
4459
  switch (`${s2.localAction}/${s2.remoteAction}`) {
@@ -4297,7 +4466,7 @@
4297
4466
  case "inserted/inserted":
4298
4467
  return "You and the other edit added different content in the same place. Their content is showing.";
4299
4468
  case "moved/moved": {
4300
- const mine = placeName(liveOf(s2.localPlacement?.parent) || localNode(record, s2.localPlacement?.parent));
4469
+ const mine = placeName(liveOf(s2.localPlacement?.parent) || localNode(record2, s2.localPlacement?.parent));
4301
4470
  const theirs = placeName(liveOf(s2.mergedPlacement?.parent));
4302
4471
  if (mine && theirs && mine !== theirs) {
4303
4472
  return `You moved this ${k2} ${mine}. The other edit moved it ${theirs}. Their position is showing.`;
@@ -4308,12 +4477,12 @@
4308
4477
  return "Your change here was replaced by the other edit.";
4309
4478
  }
4310
4479
  }
4311
- function recoveryRow(record, rv) {
4312
- const id = record.id;
4480
+ function recoveryRow(record2, rv) {
4481
+ const id = record2.id;
4313
4482
  const shown3 = rv.applied !== false;
4314
4483
  const live = shown3 ? liveOf(rv.subject) : null;
4315
4484
  const target = live ? visibleHost(live) : null;
4316
- const shape = elementOf(live) || elementOf(localNode(record, rv.subject));
4485
+ const shape = elementOf(live) || elementOf(localNode(record2, rv.subject));
4317
4486
  const plainKind = NON_ELEMENT[rv.subject?.nodeType];
4318
4487
  if (rv.attribute) {
4319
4488
  return {
@@ -4321,8 +4490,8 @@
4321
4490
  kind: "attr",
4322
4491
  name: nameOfElement(shape, rv.attribute.qualifiedName),
4323
4492
  target,
4324
- yours: side(String(record.local ?? "")),
4325
- now: shown3 ? side(String(record.remote ?? "")) : null,
4493
+ yours: side(String(record2.local ?? "")),
4494
+ now: shown3 ? side(String(record2.remote ?? "")) : null,
4326
4495
  sentence: shown3 ? null : CHANGED_AGAIN
4327
4496
  };
4328
4497
  }
@@ -4336,7 +4505,7 @@
4336
4505
  target,
4337
4506
  yours: null,
4338
4507
  now: null,
4339
- sentence: shown3 ? recoverySentence(record, rv, kind) : CHANGED_AGAIN
4508
+ sentence: shown3 ? recoverySentence(record2, rv, kind) : CHANGED_AGAIN
4340
4509
  };
4341
4510
  }
4342
4511
  const name = plainKind === "Comment" ? "Comment" : shape && HIDDEN.has(shape.localName) ? kindName(shape) : nameOfElement(shape);
@@ -4350,49 +4519,49 @@
4350
4519
  sentence: shown3 ? null : CHANGED_AGAIN
4351
4520
  };
4352
4521
  }
4353
- function rowOf(record) {
4354
- const id = record.id;
4355
- if (record.recovery?.version === 1) return recoveryRow(record, record.recovery);
4356
- if (record.kind === "attr") {
4357
- const el6 = record.el || null;
4522
+ function rowOf(record2) {
4523
+ const id = record2.id;
4524
+ if (record2.recovery?.version === 1) return recoveryRow(record2, record2.recovery);
4525
+ if (record2.kind === "attr") {
4526
+ const el6 = record2.el || null;
4358
4527
  return {
4359
4528
  id,
4360
4529
  kind: "attr",
4361
- name: nameOfElement(el6, record.name),
4530
+ name: nameOfElement(el6, record2.name),
4362
4531
  target: hostOf(el6),
4363
- yours: side(String(record.local ?? "")),
4364
- now: side(String(record.remote ?? "")),
4532
+ yours: side(String(record2.local ?? "")),
4533
+ now: side(String(record2.remote ?? "")),
4365
4534
  sentence: null
4366
4535
  };
4367
4536
  }
4368
- if (record.kind === "structure") {
4369
- const live = record.el && record.el.isConnected ? record.el : null;
4370
- const shape = live || record.base || record.el || null;
4537
+ if (record2.kind === "structure") {
4538
+ const live = record2.el && record2.el.isConnected ? record2.el : null;
4539
+ const shape = live || record2.base || record2.el || null;
4371
4540
  const kind = kindName(elementOf(shape));
4372
- const sentence = structuralSentence(record, kind);
4373
- const both = typeof record.local === "string" && typeof record.remote === "string";
4541
+ const sentence = structuralSentence(record2, kind);
4542
+ const both = typeof record2.local === "string" && typeof record2.remote === "string";
4374
4543
  return {
4375
4544
  id,
4376
4545
  kind: "struct",
4377
4546
  name: live ? nameOfElement(live) : kind,
4378
4547
  target: live ? hostOf(live) : null,
4379
- yours: both ? side(plain(record.local)) : null,
4380
- now: both ? side(plain(record.remote)) : null,
4548
+ yours: both ? side(plain(record2.local)) : null,
4549
+ now: both ? side(plain(record2.remote)) : null,
4381
4550
  sentence
4382
4551
  };
4383
4552
  }
4384
- const host = hostOf(record.node);
4385
- const el5 = elementOf(record.node);
4386
- const name = el5 && HIDDEN.has(el5.localName) ? kindName(el5) : record.node?.nodeType === 8 ? "Comment" : nameOfElement(el5);
4387
- const html = typeof record.lss === "number";
4553
+ const host2 = hostOf(record2.node);
4554
+ const el5 = elementOf(record2.node);
4555
+ const name = el5 && HIDDEN.has(el5.localName) ? kindName(el5) : record2.node?.nodeType === 8 ? "Comment" : nameOfElement(el5);
4556
+ const html = typeof record2.lss === "number";
4388
4557
  const text = (v2) => v2 == null ? "" : Array.isArray(v2) ? v2.join("") : String(v2);
4389
- const yours = html ? plain(record.local) : text(record.local);
4390
- const now3 = html ? plain(record.remote) : text(record.remote);
4558
+ const yours = html ? plain(record2.local) : text(record2.local);
4559
+ const now3 = html ? plain(record2.remote) : text(record2.remote);
4391
4560
  return {
4392
4561
  id,
4393
4562
  kind: "text",
4394
4563
  name,
4395
- target: host && !HIDDEN.has(host.localName) ? host : host ? visibleContainer(host) : null,
4564
+ target: host2 && !HIDDEN.has(host2.localName) ? host2 : host2 ? visibleContainer(host2) : null,
4396
4565
  yours: side(yours),
4397
4566
  now: side(now3),
4398
4567
  sentence: null
@@ -11953,27 +12122,27 @@
11953
12122
  }
11954
12123
  return broad(raw);
11955
12124
  }
11956
- function protectionOf(record) {
11957
- if (released.has(record)) return null;
11958
- const raws = [...rawSet([record])];
12125
+ function protectionOf(record2) {
12126
+ if (released.has(record2)) return null;
12127
+ const raws = [...rawSet([record2])];
11959
12128
  const values = raws.map((raw) => current(raw));
11960
12129
  if (!values.some(Boolean)) return null;
11961
12130
  if (values.some((state2) => !state2)) {
11962
12131
  for (const raw of raws) if (!states.has(raw)) broad(raw);
11963
- return protectionOf(record);
12132
+ return protectionOf(record2);
11964
12133
  }
11965
12134
  const union = [...new Set(values.flatMap((state2) => state2.nodes))];
11966
12135
  if (values.every((state2) => state2.mode === "source") && union.length === 1) return null;
11967
12136
  const nodes = values.some((state2) => state2.mode === "document") ? [document.documentElement] : union;
11968
12137
  return { nodes, status: values.some((state2) => state2.status === "unknown") ? "unknown" : "proved", generation: generation2 };
11969
12138
  }
11970
- function releaseProtection(record) {
11971
- for (const raw of rawOf(record)) {
12139
+ function releaseProtection(record2) {
12140
+ for (const raw of rawOf(record2)) {
11972
12141
  released.add(raw);
11973
12142
  states.delete(raw);
11974
12143
  }
11975
- released.add(record);
11976
- states.delete(record);
12144
+ released.add(record2);
12145
+ states.delete(record2);
11977
12146
  generation2++;
11978
12147
  }
11979
12148
  function allRaw(records3) {
@@ -12039,11 +12208,11 @@
12039
12208
  invalidate();
12040
12209
  return;
12041
12210
  }
12042
- const entries = /* @__PURE__ */ new Map();
12211
+ const entries2 = /* @__PURE__ */ new Map();
12043
12212
  for (const entry of result2.entries) {
12044
12213
  if (!entry || typeof entry !== "object" || !isElement(entry.from)) continue;
12045
- if (entries.has(entry.from)) entries.set(entry.from, null);
12046
- else entries.set(entry.from, entry);
12214
+ if (entries2.has(entry.from)) entries2.set(entry.from, null);
12215
+ else entries2.set(entry.from, entry);
12047
12216
  }
12048
12217
  for (const [raw, { state: state2, chains }] of frame.before) {
12049
12218
  if (released.has(raw)) continue;
@@ -12056,7 +12225,7 @@
12056
12225
  for (const chain of chains) {
12057
12226
  let found = null;
12058
12227
  for (let i2 = 0; i2 < chain.length; i2++) {
12059
- found = positive(entries.get(chain[i2]));
12228
+ found = positive(entries2.get(chain[i2]));
12060
12229
  if (found) {
12061
12230
  fallback2 ||= i2 > 0;
12062
12231
  break;
@@ -12128,7 +12297,7 @@
12128
12297
  return false;
12129
12298
  }
12130
12299
  };
12131
- rawOf = (record) => record.rawReports?.length ? record.rawReports : [record];
12300
+ rawOf = (record2) => record2.rawReports?.length ? record2.rawReports : [record2];
12132
12301
  rawSet = (records3) => new Set(records3.flatMap(rawOf).filter((raw) => !released.has(raw)));
12133
12302
  captureFootprintWitnesses = (nodes) => [...new Set(nodes)].map(witness);
12134
12303
  footprintWitnessesValid = (witnesses) => witnesses.every((w2) => valid(w2));
@@ -12524,12 +12693,12 @@
12524
12693
  return words.length === 1 ? first(words[0]) : first(words[0]) + first(words[1]);
12525
12694
  }
12526
12695
  function normalize(data) {
12527
- const list2 = data && Array.isArray(data.people) ? data.people : [];
12528
- const people = [];
12529
- for (const person of list2) {
12696
+ const list3 = data && Array.isArray(data.people) ? data.people : [];
12697
+ const people2 = [];
12698
+ for (const person of list3) {
12530
12699
  if (!person || typeof person.id !== "string" || !person.id) continue;
12531
12700
  const name = typeof person.name === "string" && person.name.trim() ? person.name.trim() : "Someone";
12532
- people.push({
12701
+ people2.push({
12533
12702
  id: person.id,
12534
12703
  name,
12535
12704
  canEdit: person.canEdit === true,
@@ -12538,7 +12707,7 @@
12538
12707
  }
12539
12708
  const count = data && data.anonymous;
12540
12709
  const anonymous = typeof count === "number" && Number.isFinite(count) && count > 0 ? Math.floor(count) : 0;
12541
- return { people, anonymous };
12710
+ return { people: people2, anonymous };
12542
12711
  }
12543
12712
  var SIZE2, OVERLAP, FONT, LABEL_FONT, RESET2, FACE, COLORS, Presence, presence;
12544
12713
  var init_presence = __esm({
@@ -12607,8 +12776,8 @@
12607
12776
  this.tip = null;
12608
12777
  }
12609
12778
  render() {
12610
- const { people, anonymous } = this.roster;
12611
- if (people.length + anonymous <= 1) {
12779
+ const { people: people2, anonymous } = this.roster;
12780
+ if (people2.length + anonymous <= 1) {
12612
12781
  this.hide();
12613
12782
  return;
12614
12783
  }
@@ -12616,7 +12785,7 @@
12616
12785
  this.hideTip();
12617
12786
  set(this.root, "color-scheme", pageScheme());
12618
12787
  this.stack.textContent = "";
12619
- for (const person of people) {
12788
+ for (const person of people2) {
12620
12789
  this.stack.appendChild(this.avatar(person));
12621
12790
  }
12622
12791
  if (this.stack.firstElementChild) set(this.stack.firstElementChild, "margin-left", "0");
@@ -14616,8 +14785,8 @@
14616
14785
  function project(root3) {
14617
14786
  const container = kidsOf(root3);
14618
14787
  const f2 = { root: root3, container, text: "", nodes: [], atoms: [], blocks: [] };
14619
- const walk = (list2) => {
14620
- for (const node of list2) {
14788
+ const walk = (list3) => {
14789
+ for (const node of list3) {
14621
14790
  if (node.nodeType === 3) {
14622
14791
  if (!node.data) continue;
14623
14792
  f2.nodes.push({ node, s: f2.text.length, e: f2.text.length + node.data.length });
@@ -14783,9 +14952,9 @@
14783
14952
  const subject = rec.recovery.subject;
14784
14953
  const live = liveOf2(subject, apply, subject.nodeType);
14785
14954
  if (!live) return null;
14786
- const local = t.local.text.slice(t.local.start, t.local.end);
14787
- if (STRUCTURAL.test(t.merged.text.slice(t.merged.start, t.merged.end)) || local.includes(BREAK2)) return null;
14788
- const op = { kind: "text", id: rec.id, ticket: apply.ticket, key: subject.key, fragment: t.local.fragment, encoding: t.encoding, local, ids: /* @__PURE__ */ new Set(), range: document.createRange() };
14955
+ const local2 = t.local.text.slice(t.local.start, t.local.end);
14956
+ if (STRUCTURAL.test(t.merged.text.slice(t.merged.start, t.merged.end)) || local2.includes(BREAK2)) return null;
14957
+ const op = { kind: "text", id: rec.id, ticket: apply.ticket, key: subject.key, fragment: t.local.fragment, encoding: t.encoding, local: local2, ids: /* @__PURE__ */ new Set(), range: document.createRange() };
14789
14958
  if (live.nodeType === 8) {
14790
14959
  const span2 = rebase(t.merged.text, live.data, t.merged.start, t.merged.end);
14791
14960
  if (!span2) return null;
@@ -15205,9 +15374,9 @@
15205
15374
  const selection = sel && sel.rangeCount ? [sel.anchorNode, sel.anchorOffset, sel.focusNode, sel.focusOffset] : null;
15206
15375
  return { attrs, controls, active: active3, caret, selection };
15207
15376
  }
15208
- function sameAttrs(el5, list2) {
15377
+ function sameAttrs(el5, list3) {
15209
15378
  const now3 = el5.attributes;
15210
- return now3.length === list2.length && list2.every(([a, value], i2) => now3[i2] === a && a.value === value);
15379
+ return now3.length === list3.length && list3.every(([a, value], i2) => now3[i2] === a && a.value === value);
15211
15380
  }
15212
15381
  function rollback(records3, page) {
15213
15382
  const touched = /* @__PURE__ */ new Set();
@@ -15221,10 +15390,10 @@
15221
15390
  }
15222
15391
  }
15223
15392
  for (const el5 of touched) {
15224
- const list2 = page.attrs.get(el5);
15225
- if (!list2 || sameAttrs(el5, list2)) continue;
15393
+ const list3 = page.attrs.get(el5);
15394
+ if (!list3 || sameAttrs(el5, list3)) continue;
15226
15395
  for (const a of [...el5.attributes]) el5.removeAttributeNode(a);
15227
- for (const [a, value] of list2) {
15396
+ for (const [a, value] of list3) {
15228
15397
  a.value = value;
15229
15398
  el5.setAttributeNode(a);
15230
15399
  }
@@ -15723,15 +15892,15 @@
15723
15892
  cells.push(box);
15724
15893
  return cells;
15725
15894
  }
15726
- function buildRow(record, index) {
15727
- const row = rowOf(record);
15895
+ function buildRow(record2, index) {
15896
+ const row = rowOf(record2);
15728
15897
  const box = bevelText("div", ["display:block", "padding:8px 12px 10px", ...index ? [`border-top:1px solid ${TOKENS2.line}`] : []]);
15729
15898
  const where = bevelText("div", ["display:flex", "align-items:center", "gap:4px", "font-size:12.5px", "font-weight:600", `color:${TOKENS2["ink-2"]}`, "margin-bottom:4px"]);
15730
15899
  where.append(bevelText("span", [], row.name));
15731
- if (row.target && row.target.isConnected) where.append(key(eyeButton(row), `eye:${record.id}`));
15900
+ if (row.target && row.target.isConnected) where.append(key(eyeButton(row), `eye:${record2.id}`));
15732
15901
  box.append(where);
15733
15902
  if (row.sentence) box.append(bevelText("div", ["display:block", "font-size:13px", `color:${TOKENS2["ink-2"]}`], row.sentence));
15734
- if (blockedRows.has(record.id)) box.append(bevelText("div", ["display:block", "font-size:13px", "margin-top:4px", `color:${WARN}`], blockedRows.get(record.id)));
15903
+ if (blockedRows.has(record2.id)) box.append(bevelText("div", ["display:block", "font-size:13px", "margin-top:4px", `color:${WARN}`], blockedRows.get(record2.id)));
15735
15904
  if (row.yours) {
15736
15905
  const grid = bevelText("div", ["display:grid", "grid-template-columns:44px 1fr", "gap:3px 10px", "align-items:start", `font:12.5px/1.5 ${FONT_MONO}`, ...row.sentence ? ["margin-top:4px"] : []]);
15737
15906
  grid.append(...valueLine("Yours", row.yours, { mine: true }));
@@ -21586,6 +21755,24 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
21586
21755
  makeSortable: () => makeSortable,
21587
21756
  ready: () => ready
21588
21757
  });
21758
+ function recordRuntimeAttrs(item) {
21759
+ return [item, ...item.querySelectorAll("*")].map((el5) => [el5, RUNTIME_ATTRS.map((name) => el5.getAttribute(name))]);
21760
+ }
21761
+ function restoreRuntimeAttrs(recorded) {
21762
+ for (const [el5, values] of recorded) {
21763
+ RUNTIME_ATTRS.forEach((name, i2) => {
21764
+ const before = values[i2];
21765
+ const now3 = el5.getAttribute(name);
21766
+ if (now3 === before) return;
21767
+ if (before === null) {
21768
+ if (name === "style" && now3) return;
21769
+ el5.removeAttribute(name);
21770
+ } else {
21771
+ el5.setAttribute(name, before);
21772
+ }
21773
+ });
21774
+ }
21775
+ }
21589
21776
  function makeSortable(sortableElem, Sortable) {
21590
21777
  let options = {};
21591
21778
  const childSelector = /^(UL|OL)$/.test(sortableElem.tagName) ? "li" : "*";
@@ -21634,12 +21821,47 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
21634
21821
  }));
21635
21822
  sortableElem.dispatchEvent(new Event("input", { bubbles: true }));
21636
21823
  };
21824
+ const ENDS = ["pointerup", "pointercancel", "dragend", "drop"];
21825
+ let recorded = null;
21826
+ const restoreSoon = () => {
21827
+ const pending4 = recorded;
21828
+ recorded = null;
21829
+ pressed.delete(pending4);
21830
+ ENDS.forEach((type) => document.removeEventListener(type, restoreSoon, true));
21831
+ if (pending4) setTimeout(() => restoreRuntimeAttrs(pending4), 0);
21832
+ };
21833
+ sortableElem.addEventListener("pointerdown", (event) => {
21834
+ let item = event.target instanceof Element ? event.target : null;
21835
+ while (item && item.parentElement !== sortableElem) item = item.parentElement;
21836
+ if (!item || recorded) return;
21837
+ recorded = recordRuntimeAttrs(item);
21838
+ pressed.add(recorded);
21839
+ ENDS.forEach((type) => document.addEventListener(type, restoreSoon, true));
21840
+ }, true);
21841
+ options.onUnchoose = restoreSoon;
21637
21842
  Sortable.create(sortableElem, options);
21638
21843
  }
21639
21844
  async function init16() {
21640
21845
  if (!isEditMode) return;
21641
21846
  const mod = await Promise.resolve().then(() => (init_Sortable_vendor(), Sortable_vendor_exports));
21642
21847
  const Sortable = window.Sortable || mod.default;
21848
+ onSnapshot((root3, { original }) => {
21849
+ for (const el5 of root3.querySelectorAll("[sortable] .sortable-chosen, [sortable] .sortable-ghost, [sortable] .sortable-drag")) {
21850
+ el5.classList.remove("sortable-chosen", "sortable-ghost", "sortable-drag");
21851
+ if (!el5.classList.length) el5.removeAttribute("class");
21852
+ }
21853
+ if (!pressed.size) return;
21854
+ const before = /* @__PURE__ */ new Map();
21855
+ for (const recording of pressed) for (const [el5, values] of recording) before.set(el5, values);
21856
+ for (const el5 of root3.querySelectorAll("[sortable] *")) {
21857
+ const values = before.get(original(el5));
21858
+ if (!values) continue;
21859
+ RUNTIME_ATTRS.forEach((name, i2) => {
21860
+ if (values[i2] === null) el5.removeAttribute(name);
21861
+ else el5.setAttribute(name, values[i2]);
21862
+ });
21863
+ }
21864
+ });
21643
21865
  document.querySelectorAll("[sortable]").forEach((el5) => makeSortable(el5, Sortable));
21644
21866
  mutation_default.onAddElement({
21645
21867
  selectorFilter: "[sortable]",
@@ -21652,13 +21874,16 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
21652
21874
  });
21653
21875
  });
21654
21876
  }
21655
- var EDITOR_UI_SELECTOR, ready, sortable_default;
21877
+ var EDITOR_UI_SELECTOR, RUNTIME_ATTRS, pressed, ready, sortable_default;
21656
21878
  var init_sortable = __esm({
21657
21879
  "src/plugins/sortable.js"() {
21658
21880
  init_is_edit_mode();
21659
21881
  init_mutation();
21660
21882
  init_region_capabilities();
21883
+ init_snapshot();
21661
21884
  EDITOR_UI_SELECTOR = capabilitySelector("history");
21885
+ RUNTIME_ATTRS = ["draggable", "style"];
21886
+ pressed = /* @__PURE__ */ new Set();
21662
21887
  ready = init16();
21663
21888
  sortable_default = init16;
21664
21889
  }
@@ -28545,12 +28770,12 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
28545
28770
  const finishOnce = () => {
28546
28771
  if (settled2) return false;
28547
28772
  settled2 = true;
28548
- host.close();
28773
+ host2.close();
28549
28774
  URL.revokeObjectURL(url);
28550
28775
  active2 = false;
28551
28776
  return true;
28552
28777
  };
28553
- const host = adapter.open({
28778
+ const host2 = adapter.open({
28554
28779
  content: stage,
28555
28780
  confirmLabel,
28556
28781
  onConfirm() {
@@ -28655,9 +28880,9 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
28655
28880
  event.preventDefault();
28656
28881
  }
28657
28882
  }
28658
- function dialogClose(label, inHeader) {
28883
+ function dialogClose(label, inHeader, theme2 = null, names = [], iconNames = []) {
28659
28884
  const hover = RULES.dialogCloseHover[0].slice("background:".length);
28660
- const b = bevelBox("button", [
28885
+ const base = [
28661
28886
  "position:absolute",
28662
28887
  "top:-1px",
28663
28888
  "right:-1px",
@@ -28672,26 +28897,82 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
28672
28897
  ...inHeader ? [] : [`border-bottom:${LINE}`],
28673
28898
  "cursor:pointer",
28674
28899
  "outline:none"
28675
- ]);
28900
+ ];
28901
+ const b = bevelBox("button", base);
28676
28902
  b.type = "button";
28677
28903
  b.setAttribute("aria-label", label);
28678
28904
  b.title = label;
28679
28905
  b.innerHTML = GLYPHS.dialogClose;
28680
- protectIcon(b.firstElementChild, 16);
28681
- b.addEventListener("pointerenter", () => set(b, "background", hover));
28682
- b.addEventListener("pointerleave", () => set(b, "background", TOKENS2.face));
28906
+ if (!themed(theme2)) {
28907
+ protectIcon(b.firstElementChild, 16);
28908
+ b.addEventListener("pointerenter", () => set(b, "background", hover));
28909
+ b.addEventListener("pointerleave", () => set(b, "background", TOKENS2.face));
28910
+ b.addEventListener("focus", () => {
28911
+ set(b, "outline", `2px solid ${TOKENS2.brass}`);
28912
+ set(b, "outline-offset", "-4px");
28913
+ });
28914
+ b.addEventListener("blur", () => set(b, "outline", "none"));
28915
+ return b;
28916
+ }
28917
+ const allNames = ["close", ...names];
28918
+ const icon = ["icon", ...iconNames];
28919
+ markParts(b, theme2, allNames);
28920
+ markParts(b.firstElementChild, theme2, icon);
28921
+ const flags = { hover: false, focus: false, pressed: false };
28922
+ const paint = () => {
28923
+ const act = { hover: flags.hover, active: flags.pressed, focus: flags.focus };
28924
+ const svg = b.firstElementChild;
28925
+ svg.style.cssText = "";
28926
+ protectIcon(svg, 16);
28927
+ style(svg, partRules(theme2, icon, act));
28928
+ restyleBox(b, [
28929
+ ...base,
28930
+ ...flags.hover ? [`background:${hover}`] : [],
28931
+ ...flags.focus ? [`outline:2px solid ${TOKENS2.brass}`, "outline-offset:-4px"] : [],
28932
+ ...tokenRulesFor(theme2, "close", act),
28933
+ ...partRules(theme2, allNames, act)
28934
+ ]);
28935
+ };
28936
+ b.addEventListener("pointerenter", () => {
28937
+ flags.hover = true;
28938
+ paint();
28939
+ });
28940
+ b.addEventListener("pointerleave", () => {
28941
+ flags.hover = false;
28942
+ flags.pressed = false;
28943
+ paint();
28944
+ });
28945
+ b.addEventListener("pointerdown", (e) => {
28946
+ if (e.button !== void 0 && e.button !== 0) return;
28947
+ flags.pressed = true;
28948
+ paint();
28949
+ });
28950
+ b.addEventListener("pointerup", () => {
28951
+ flags.pressed = false;
28952
+ paint();
28953
+ });
28683
28954
  b.addEventListener("focus", () => {
28684
- set(b, "outline", `2px solid ${TOKENS2.brass}`);
28685
- set(b, "outline-offset", "-4px");
28955
+ flags.focus = true;
28956
+ paint();
28957
+ });
28958
+ b.addEventListener("blur", () => {
28959
+ flags.focus = false;
28960
+ flags.pressed = false;
28961
+ paint();
28686
28962
  });
28687
- b.addEventListener("blur", () => set(b, "outline", "none"));
28963
+ paint();
28688
28964
  return b;
28689
28965
  }
28690
- function bevelDialog({ zIndex = "100", width = "600px", closable = false, titled = false } = {}) {
28691
- const root3 = bevelBox("div", ["display:block"]);
28966
+ function bevelDialog({ zIndex = "100", width = "600px", closable = false, titled = false, theme: theme2 = null, surface = "modal" } = {}) {
28967
+ const on3 = themed(theme2);
28968
+ const t = on3 ? theme2.tokens : {};
28969
+ const S2 = t.spacing;
28970
+ const names = (part) => surface === "modal" ? [`dialog.${part}`] : [`dialog.${part}`, `${surface}.${part}`];
28971
+ const root3 = bevelBox("div", ["display:block"], { theme: theme2, parts: names("root") });
28692
28972
  root3.setAttribute("data-clay-modal", "");
28973
+ if (on3) root3.setAttribute("data-clay-ui", surface);
28693
28974
  root3.setAttribute("aria-hidden", "true");
28694
- set(root3, "color-scheme", pageScheme());
28975
+ set(root3, "color-scheme", on3 ? theme2.scheme : pageScheme());
28695
28976
  const overlay = bevelBox("div", [
28696
28977
  "position:fixed",
28697
28978
  "inset:0",
@@ -28702,7 +28983,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
28702
28983
  "justify-content:center",
28703
28984
  "padding:min(96px, 10vh) 16px 16px",
28704
28985
  `background:color-mix(in srgb, ${TOKENS2.ground} 72%, transparent)`
28705
- ]);
28986
+ ], { theme: theme2, parts: names("overlay"), role: "overlay" });
28706
28987
  overlay.tabIndex = -1;
28707
28988
  const panel2 = bevelSurface("form", [
28708
28989
  "position:relative",
@@ -28712,7 +28993,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
28712
28993
  `width:min(${width}, 100%)`,
28713
28994
  "max-height:calc(100dvh - min(96px, 10vh) - 16px)",
28714
28995
  ...RULES.dialogPanel
28715
- ]);
28996
+ ], { theme: theme2, parts: names("panel") });
28716
28997
  panel2.setAttribute("role", "dialog");
28717
28998
  panel2.setAttribute("aria-modal", "true");
28718
28999
  let header = null;
@@ -28723,9 +29004,11 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
28723
29004
  "display:block",
28724
29005
  "flex:none",
28725
29006
  `padding:20px ${closable ? "68px" : "22px"} 16px 22px`,
28726
- ...RULES.dialogHeader
28727
- ]);
28728
- heading = bevelText("div", ["display:block", ...RULES.dialogHeading, "overflow-wrap:anywhere"]);
29007
+ ...RULES.dialogHeader,
29008
+ ...t.border ? [`border-bottom-color:${t.border}`] : [],
29009
+ ...S2 ? [`padding:${S2} ${closable ? `calc(${S2} + 46px)` : S2} calc(${S2} * .7) ${S2}`] : []
29010
+ ], { theme: theme2, parts: names("header") });
29011
+ heading = bevelText("div", ["display:block", ...RULES.dialogHeading, "overflow-wrap:anywhere"], void 0, { theme: theme2, parts: names("title"), role: "heading" });
28729
29012
  heading.setAttribute("role", "heading");
28730
29013
  heading.setAttribute("aria-level", "2");
28731
29014
  header.append(heading);
@@ -28739,22 +29022,25 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
28739
29022
  "overflow-x:hidden",
28740
29023
  `padding:20px ${closable && !titled ? "84px" : "22px"} 20px 22px`,
28741
29024
  "overflow-wrap:anywhere",
28742
- `font:14.5px/1.55 ${FONT_SANS}`
28743
- ]);
29025
+ `font:14.5px/1.55 ${FONT_SANS}`,
29026
+ ...S2 ? [`padding:${S2} ${closable && !titled ? `calc(${S2} + 62px)` : S2} ${S2} ${S2}`] : []
29027
+ ], void 0, { theme: theme2, parts: names("body") });
28744
29028
  const footer = bevelBox("div", [
28745
29029
  "display:flex",
28746
29030
  "flex:none",
28747
29031
  "flex-wrap:wrap",
28748
29032
  "justify-content:flex-end",
28749
29033
  "align-items:center",
28750
- ...RULES.dialogFooter
28751
- ]);
29034
+ ...RULES.dialogFooter,
29035
+ ...t.border ? [`border-top-color:${t.border}`] : [],
29036
+ ...S2 ? [`padding:calc(${S2} * .65) ${S2}`, `gap:calc(${S2} / 2)`] : []
29037
+ ], { theme: theme2, parts: [...names("footer"), ...names("actions")] });
28752
29038
  panel2.append(body, footer);
28753
- const close = closable ? dialogClose("Close", titled) : null;
29039
+ const close = closable ? dialogClose("Close", titled, theme2, names("close"), names("closeIcon")) : null;
28754
29040
  if (close) (header || panel2).append(close);
28755
29041
  overlay.append(panel2);
28756
29042
  root3.append(overlay);
28757
- return { root: root3, overlay, panel: panel2, header, heading, body, footer, close };
29043
+ return { root: root3, overlay, panel: panel2, header, heading, body, footer, close, theme: theme2 };
28758
29044
  }
28759
29045
  var dismissOf, FOCUSABLE, LINE;
28760
29046
  var init_bevel_dialog = __esm({
@@ -28762,6 +29048,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
28762
29048
  init_hostile_css();
28763
29049
  init_bevel_controls();
28764
29050
  init_bevel();
29051
+ init_theme_parts();
28765
29052
  dismissOf = /* @__PURE__ */ new WeakMap();
28766
29053
  FOCUSABLE = 'a[href], input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [contenteditable], [tabindex]:not([tabindex^="-"])';
28767
29054
  LINE = `1px solid ${TOKENS2["line-2"]}`;
@@ -28890,6 +29177,9 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
28890
29177
  if (!meta.extensions.includes("upload")) {
28891
29178
  return result(false, "This host does not store uploaded files", "skipped", "unsupported");
28892
29179
  }
29180
+ if (!meta.document?.upload) {
29181
+ return result(false, "You can't add files to this document", "error", "forbidden");
29182
+ }
28893
29183
  const cap = meta.document?.upload?.maxBytes;
28894
29184
  if (typeof cap === "number" && cap > 0 && file.size > cap) {
28895
29185
  return result(false, `That file is larger than this host accepts (${cap} bytes)`, "error", "too-large");
@@ -28989,6 +29279,605 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
28989
29279
  }
28990
29280
  });
28991
29281
 
29282
+ // src/ui/theme.js
29283
+ function validValue(prop, value) {
29284
+ if (typeof value !== "string") return false;
29285
+ const v2 = value.trim();
29286
+ if (!v2 || v2.length > 500 || /[;{}]|!\s*important/i.test(v2)) return false;
29287
+ const supports = globalThis.CSS?.supports;
29288
+ return typeof supports === "function" ? supports.call(globalThis.CSS, prop, v2) : true;
29289
+ }
29290
+ function plainObject(x2) {
29291
+ return !!x2 && typeof x2 === "object" && Object.getPrototypeOf(x2) === Object.prototype;
29292
+ }
29293
+ function normalize2(options) {
29294
+ if (!plainObject(options)) throw new TypeError("clay.theme: pass an options object, false or null");
29295
+ const { auto = true, colorScheme = "page", tokens = {}, parts = {}, ...rest } = options;
29296
+ const unknown = Object.keys(rest);
29297
+ if (unknown.length) throw new TypeError(`clay.theme: unknown option "${unknown[0]}"`);
29298
+ if (typeof auto !== "boolean") throw new TypeError("clay.theme: auto must be true or false");
29299
+ if (!SCHEMES.includes(colorScheme)) throw new TypeError(`clay.theme: colorScheme must be one of ${SCHEMES.join(", ")}`);
29300
+ if (!plainObject(tokens)) throw new TypeError("clay.theme: tokens must be an object");
29301
+ if (!plainObject(parts)) throw new TypeError("clay.theme: parts must be an object");
29302
+ const outTokens = {};
29303
+ for (const [name, value] of Object.entries(tokens)) {
29304
+ if (!(name in TOKEN_PROPS)) throw new TypeError(`clay.theme: unknown token "${name}"`);
29305
+ if (!validValue(TOKEN_PROPS[name], value)) throw new TypeError(`clay.theme: token "${name}" is not a valid ${TOKEN_PROPS[name]} value`);
29306
+ outTokens[name] = name === "spacing" && value.trim() === "0" ? "0px" : value.trim();
29307
+ }
29308
+ const outParts = {};
29309
+ for (const [name, states2] of Object.entries(parts)) {
29310
+ if (!PART_NAMES.has(name)) throw new TypeError(`clay.theme: unknown part "${name}"`);
29311
+ if (!plainObject(states2)) throw new TypeError(`clay.theme: part "${name}" must map states to declarations`);
29312
+ outParts[name] = {};
29313
+ for (const [state2, decls] of Object.entries(states2)) {
29314
+ if (!STATES.includes(state2)) throw new TypeError(`clay.theme: part "${name}" has unknown state "${state2}"`);
29315
+ if (!plainObject(decls)) throw new TypeError(`clay.theme: "${name}.${state2}" must be an object of CSS declarations`);
29316
+ outParts[name][state2] = {};
29317
+ for (const [prop, value] of Object.entries(decls)) {
29318
+ if (!/^-?[a-z][a-z-]*$/.test(prop) || prop.startsWith("--")) throw new TypeError(`clay.theme: "${prop}" in ${name}.${state2} is not a CSS property name`);
29319
+ if (!validValue(prop, value)) throw new TypeError(`clay.theme: "${prop}: ${value}" in ${name}.${state2} is not valid`);
29320
+ outParts[name][state2][prop] = value.trim();
29321
+ }
29322
+ }
29323
+ }
29324
+ return Object.freeze({ auto, colorScheme, tokens: Object.freeze(outTokens), parts: Object.freeze(outParts) });
29325
+ }
29326
+ function copy(c2) {
29327
+ if (c2 === false) return false;
29328
+ return JSON.parse(JSON.stringify(c2));
29329
+ }
29330
+ function theme(options) {
29331
+ initTheme();
29332
+ if (arguments.length === 0) return copy(config);
29333
+ if (options === false) config = false;
29334
+ else if (options === null) config = DEFAULT;
29335
+ else config = normalize2(options);
29336
+ return copy(config);
29337
+ }
29338
+ function initTheme() {
29339
+ if (initialized2) return;
29340
+ initialized2 = true;
29341
+ const pre = typeof window !== "undefined" ? window.clayTheme : void 0;
29342
+ if (pre === false) config = false;
29343
+ else if (pre === void 0 || pre === null) config = DEFAULT;
29344
+ else {
29345
+ try {
29346
+ config = normalize2(pre);
29347
+ } catch (err) {
29348
+ console.warn(`clayjs: ignoring window.clayTheme: ${err.message}`);
29349
+ config = DEFAULT;
29350
+ }
29351
+ }
29352
+ if (typeof window !== "undefined") {
29353
+ const clay4 = window.clay = window.clay || {};
29354
+ clay4.theme = theme;
29355
+ }
29356
+ }
29357
+ function parseColor(value) {
29358
+ const v2 = String(value).trim().toLowerCase();
29359
+ let m2 = /^#([0-9a-f]{3}|[0-9a-f]{6})$/.exec(v2);
29360
+ if (m2) {
29361
+ const h = m2[1].length === 3 ? [...m2[1]].map((c2) => c2 + c2).join("") : m2[1];
29362
+ return [0, 2, 4].map((i2) => parseInt(h.slice(i2, i2 + 2), 16)).concat(1);
29363
+ }
29364
+ m2 = /^rgba?\(\s*([\d.]+)[\s,]+([\d.]+)[\s,]+([\d.]+)(?:\s*[,/]\s*([\d.]+)(%?))?\s*\)$/.exec(v2);
29365
+ if (!m2) return null;
29366
+ const a = m2[4] === void 0 ? 1 : Number(m2[4]) / (m2[5] ? 100 : 1);
29367
+ return [Number(m2[1]), Number(m2[2]), Number(m2[3]), a];
29368
+ }
29369
+ function luminance([r, g, b]) {
29370
+ const lin = (c2) => (c2 /= 255) <= 0.03928 ? c2 / 12.92 : ((c2 + 0.055) / 1.055) ** 2.4;
29371
+ return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
29372
+ }
29373
+ function contrast(a, b) {
29374
+ const [x2, y2] = [luminance(a), luminance(b)].sort((p2, q3) => q3 - p2);
29375
+ return (x2 + 0.05) / (y2 + 0.05);
29376
+ }
29377
+ function chosenFont(value) {
29378
+ const families = String(value || "").split(",").map((f2) => f2.trim().replace(/^["']|["']$/g, "").toLowerCase()).filter(Boolean);
29379
+ return families.length && !families.every((f2) => DEFAULT_FONTS.has(f2));
29380
+ }
29381
+ function opaqueBackground(el5) {
29382
+ if (!el5) return null;
29383
+ const cs2 = getComputedStyle(el5);
29384
+ if (cs2.backgroundImage && cs2.backgroundImage !== "none") return { blocked: true };
29385
+ const raw = (cs2.backgroundColor || "").trim();
29386
+ if (TRANSPARENT.has(raw)) return null;
29387
+ const c2 = parseColor(raw);
29388
+ if (!c2) return { blocked: true };
29389
+ if (c2[3] === 0) return null;
29390
+ if (c2[3] < 1) return { blocked: true };
29391
+ return { value: raw, rgb: c2 };
29392
+ }
29393
+ function samplePage() {
29394
+ const out = {};
29395
+ const body = document.body;
29396
+ if (!body) return out;
29397
+ const cs2 = getComputedStyle(body);
29398
+ if (chosenFont(cs2.fontFamily)) out.font = cs2.fontFamily.trim();
29399
+ let bg = opaqueBackground(body);
29400
+ if (!bg) bg = opaqueBackground(document.documentElement);
29401
+ const text = parseColor(cs2.color);
29402
+ if (bg && !bg.blocked && text && text[3] === 1 && contrast(bg.rgb, text) >= 3) {
29403
+ out.background = bg.value;
29404
+ out.surface = bg.value;
29405
+ out.text = cs2.color;
29406
+ }
29407
+ return out;
29408
+ }
29409
+ function cssTokens() {
29410
+ const out = {};
29411
+ if (!document.body) return out;
29412
+ const cs2 = getComputedStyle(document.body);
29413
+ for (const [name, prop] of Object.entries(TOKEN_PROPS)) {
29414
+ const v2 = cs2.getPropertyValue(`--clay-ui-${kebab(name)}`).trim();
29415
+ if (v2 && validValue(prop, v2)) out[name] = name === "spacing" && v2 === "0" ? "0px" : v2;
29416
+ }
29417
+ return out;
29418
+ }
29419
+ function scheme(choice, tokens) {
29420
+ if (choice !== "page") return choice;
29421
+ const s2 = getComputedStyle(document.documentElement).colorScheme;
29422
+ if (s2 && s2 !== "normal") return s2;
29423
+ const meta = document.querySelector('meta[name="color-scheme"]')?.getAttribute("content")?.trim();
29424
+ if (meta && /\b(light|dark)\b/.test(meta)) return meta;
29425
+ const surface = parseColor(tokens.surface || "");
29426
+ if (surface) return luminance(surface) < 0.2 ? "dark" : "light";
29427
+ return "light dark";
29428
+ }
29429
+ function onColor(fill, t) {
29430
+ const f2 = parseColor(fill);
29431
+ const a = parseColor(t.text);
29432
+ const b = parseColor(t.surface);
29433
+ if (f2 && a && b) return contrast(f2, a) >= contrast(f2, b) ? t.text : t.surface;
29434
+ return null;
29435
+ }
29436
+ function resolveTheme() {
29437
+ initTheme();
29438
+ if (config === false) return { bevel: true, scheme: pageScheme(), tokens: {}, parts: {} };
29439
+ const sampled = config.auto ? samplePage() : {};
29440
+ const tokens = { ...sampled, ...cssTokens(), ...config.tokens };
29441
+ if (tokens.text && tokens.surface) {
29442
+ tokens.mutedText ??= `color-mix(in srgb, ${tokens.text} 70%, ${tokens.surface})`;
29443
+ tokens.border ??= `color-mix(in srgb, ${tokens.text} 18%, ${tokens.surface})`;
29444
+ tokens.overlay ??= `color-mix(in srgb, ${tokens.text} 20%, transparent)`;
29445
+ tokens.accent ??= tokens.text;
29446
+ }
29447
+ for (const [fill, on3] of [["accent", "accentText"], ["danger", "dangerText"]]) {
29448
+ if (!tokens[fill] || tokens[on3]) continue;
29449
+ const pick = onColor(tokens[fill], tokens);
29450
+ if (pick) tokens[on3] = pick;
29451
+ else console.warn(`clayjs: theme sets ${fill} without ${on3}; pass ${on3} so text on it stays readable`);
29452
+ }
29453
+ return { bevel: false, scheme: scheme(config.colorScheme, tokens), tokens, parts: config.parts };
29454
+ }
29455
+ var TOKEN_PROPS, STATES, DIALOG, NOTICE, PART_NAMES, SCHEMES, DEFAULT, kebab, config, initialized2, TRANSPARENT, DEFAULT_FONTS;
29456
+ var init_theme = __esm({
29457
+ "src/ui/theme.js"() {
29458
+ init_bevel_controls();
29459
+ TOKEN_PROPS = {
29460
+ font: "font-family",
29461
+ headingFont: "font-family",
29462
+ text: "color",
29463
+ mutedText: "color",
29464
+ background: "background-color",
29465
+ surface: "background-color",
29466
+ border: "border-color",
29467
+ accent: "background-color",
29468
+ accentText: "color",
29469
+ danger: "background-color",
29470
+ dangerText: "color",
29471
+ success: "color",
29472
+ warning: "color",
29473
+ radius: "border-radius",
29474
+ shadow: "box-shadow",
29475
+ overlay: "background-color",
29476
+ spacing: "outline-offset",
29477
+ buttonHeight: "min-height"
29478
+ };
29479
+ STATES = ["base", "hover", "active", "focus", "disabled"];
29480
+ DIALOG = ["root", "overlay", "panel", "header", "title", "body", "footer", "close", "closeIcon", "content", "input", "hint", "actions", "button", "buttonLabel", "well", "code", "copyButton"];
29481
+ NOTICE = ["root", "panel", "header", "title", "text", "icon", "dot", "actions", "button", "buttonLabel", "close", "well", "list", "row", "location", "valueLabel", "value", "before", "after", "mine", "theirs", "warning", "previewRing"];
29482
+ PART_NAMES = /* @__PURE__ */ new Set([
29483
+ "button",
29484
+ "button.default",
29485
+ "button.primary",
29486
+ "button.quiet",
29487
+ "button.danger",
29488
+ "buttonLabel",
29489
+ "input",
29490
+ "text",
29491
+ "mutedText",
29492
+ "icon",
29493
+ "well",
29494
+ "close",
29495
+ ...["dialog", "people", "crop"].flatMap((s2) => DIALOG.map((p2) => `${s2}.${p2}`)),
29496
+ "people.choices",
29497
+ "people.choice",
29498
+ "people.choiceLabel",
29499
+ "crop.stage",
29500
+ "crop.image",
29501
+ "crop.dim",
29502
+ "crop.selection",
29503
+ "crop.handle",
29504
+ "crop.handle.nw",
29505
+ "crop.handle.ne",
29506
+ "crop.handle.sw",
29507
+ "crop.handle.se",
29508
+ "toast.stack",
29509
+ "toast.panel",
29510
+ "toast.icon",
29511
+ "toast.text",
29512
+ "toast.close",
29513
+ "toast.closeIcon",
29514
+ ...["notice", "conflict", "section", "staleHost"].flatMap((s2) => NOTICE.map((p2) => `${s2}.${p2}`))
29515
+ ]);
29516
+ SCHEMES = ["page", "light", "dark"];
29517
+ DEFAULT = Object.freeze({ auto: true, colorScheme: "page", tokens: {}, parts: {} });
29518
+ kebab = (name) => name.replace(/[A-Z]/g, (c2) => `-${c2.toLowerCase()}`);
29519
+ config = null;
29520
+ initialized2 = false;
29521
+ TRANSPARENT = /* @__PURE__ */ new Set(["", "transparent", "rgba(0, 0, 0, 0)"]);
29522
+ DEFAULT_FONTS = /* @__PURE__ */ new Set(["serif", "times", "times new roman", "-webkit-standard", "tinos", "liberation serif"]);
29523
+ }
29524
+ });
29525
+
29526
+ // src/plugins/people.js
29527
+ var people_exports = {};
29528
+ __export(people_exports, {
29529
+ author: () => author,
29530
+ currentMe: () => currentMe,
29531
+ default: () => people_default,
29532
+ people: () => people,
29533
+ ready: () => ready2
29534
+ });
29535
+ function clean(person) {
29536
+ if (!person || typeof person !== "object") return null;
29537
+ const id = typeof person.id === "string" ? person.id : "";
29538
+ const name = typeof person.name === "string" ? person.name.trim().replace(/\s+/g, " ") : "";
29539
+ if (!ID.test(id) || !name || name.length > MAX_NAME || EMAIL.test(name)) return null;
29540
+ return { id, name };
29541
+ }
29542
+ function initialsOf2(name) {
29543
+ const letters = name.split(" ").filter(Boolean).slice(0, 2).map((word) => Array.from(word)[0]);
29544
+ return letters.join("").toUpperCase() || "?";
29545
+ }
29546
+ function colorOf(id) {
29547
+ let hash = 0;
29548
+ for (const ch of String(id)) hash = hash * 31 + ch.codePointAt(0) >>> 0;
29549
+ return hash % COLORS2;
29550
+ }
29551
+ function record({ id, name }) {
29552
+ return Object.freeze({ id, name, initials: initialsOf2(name), color: colorOf(id) });
29553
+ }
29554
+ function emit2() {
29555
+ document.dispatchEvent(new CustomEvent("clay:people", { detail: { me: currentMe() } }));
29556
+ }
29557
+ function registry() {
29558
+ return document.querySelector("[clay-people]");
29559
+ }
29560
+ function entries() {
29561
+ return [...document.querySelectorAll("[clay-people] data[value]")];
29562
+ }
29563
+ function saved() {
29564
+ if (cache) return cache;
29565
+ cache = /* @__PURE__ */ new Map();
29566
+ for (const el5 of entries()) {
29567
+ const person = clean({ id: el5.getAttribute("value"), name: el5.textContent });
29568
+ if (person && !cache.has(person.id)) cache.set(person.id, record(person));
29569
+ }
29570
+ return cache;
29571
+ }
29572
+ function readLocal() {
29573
+ if (local === void 0) {
29574
+ local = null;
29575
+ try {
29576
+ local = clean(JSON.parse(window.localStorage.getItem(STORE) || "null"));
29577
+ } catch (_) {
29578
+ }
29579
+ }
29580
+ return local;
29581
+ }
29582
+ function writeLocal(person) {
29583
+ local = person;
29584
+ try {
29585
+ window.localStorage.setItem(STORE, JSON.stringify(person));
29586
+ } catch (_) {
29587
+ }
29588
+ }
29589
+ function mint() {
29590
+ const bytes = new Uint8Array(16);
29591
+ crypto.getRandomValues(bytes);
29592
+ return btoa(String.fromCharCode(...bytes)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
29593
+ }
29594
+ function currentMe() {
29595
+ if (!editing()) return null;
29596
+ if (host.me) return record(host.me);
29597
+ if (host.unavailable) return null;
29598
+ if (outcome !== "ok" && outcome !== "none") return null;
29599
+ const mine = readLocal();
29600
+ return mine ? record(mine) : null;
29601
+ }
29602
+ function applyMeta(meta) {
29603
+ const nextOutcome = hostMetaOutcome(meta);
29604
+ if (nextOutcome !== "ok" && host.supports) return;
29605
+ const people2 = meta.document?.people;
29606
+ const supports = meta.extensions.includes("people") && !!people2 && typeof people2 === "object";
29607
+ const next = {
29608
+ supports,
29609
+ me: supports ? clean(people2.me) : null,
29610
+ members: supports && Array.isArray(people2.members) ? people2.members.map(clean).filter(Boolean) : null,
29611
+ unavailable: supports && people2.unavailable === true
29612
+ };
29613
+ const changed2 = nextOutcome !== outcome || JSON.stringify(next) !== JSON.stringify(host);
29614
+ host = next;
29615
+ outcome = nextOutcome;
29616
+ if (changed2) emit2();
29617
+ }
29618
+ async function refresh(fresh) {
29619
+ applyMeta(await hostMeta({ fresh }));
29620
+ }
29621
+ function get2(id) {
29622
+ const key2 = String(id ?? "");
29623
+ const live = host.me?.id === key2 && host.me || host.members?.find((person) => person.id === key2);
29624
+ if (live) return record(live);
29625
+ const mine = readLocal();
29626
+ if (mine?.id === key2) return record(mine);
29627
+ return saved().get(key2) || Object.freeze({ id: key2, name: "Unknown person", initials: "?", color: colorOf(key2) });
29628
+ }
29629
+ function list() {
29630
+ return [...saved().keys()].map(get2);
29631
+ }
29632
+ async function available() {
29633
+ await refresh(outcome !== "none");
29634
+ return host.members ? host.members.map(record) : null;
29635
+ }
29636
+ function add(person) {
29637
+ const p2 = clean(person);
29638
+ if (!p2) throw new TypeError("clay.people.add: needs an id of 8 to 64 letters, digits, _ or -, and a name of 1 to 120 characters");
29639
+ let root3 = registry();
29640
+ if (!root3) {
29641
+ root3 = document.createElement("div");
29642
+ root3.setAttribute("clay-people", "");
29643
+ root3.hidden = true;
29644
+ document.body.append(root3);
29645
+ }
29646
+ const same = entries().filter((el5) => el5.getAttribute("value") === p2.id);
29647
+ if (same.length) {
29648
+ if (same[0].textContent !== p2.name) same[0].textContent = p2.name;
29649
+ for (const extra of same.slice(1)) extra.remove();
29650
+ } else {
29651
+ const el5 = document.createElement("data");
29652
+ el5.setAttribute("value", p2.id);
29653
+ el5.textContent = p2.name;
29654
+ root3.append(el5);
29655
+ }
29656
+ cache = null;
29657
+ return record(p2);
29658
+ }
29659
+ function remove2(id) {
29660
+ const key2 = String(id ?? "");
29661
+ for (const el5 of entries()) if (el5.getAttribute("value") === key2) el5.remove();
29662
+ cache = null;
29663
+ }
29664
+ function canRename() {
29665
+ return editing() && !host.me && (outcome === "ok" || outcome === "none") && !!readLocal() && !host.unavailable;
29666
+ }
29667
+ function rename(name) {
29668
+ if (!canRename()) throw Object.assign(new Error("clay.people.rename: only a name chosen in this browser can be changed here"), { code: "not-renamable" });
29669
+ const mine = readLocal();
29670
+ const next = clean({ id: mine.id, name });
29671
+ if (!next) throw new TypeError("clay.people.rename: needs a name of 1 to 120 characters that is not an email address");
29672
+ writeLocal(next);
29673
+ if (entries().some((el5) => el5.getAttribute("value") === next.id)) add(next);
29674
+ emit2();
29675
+ return record(next);
29676
+ }
29677
+ function unavailable() {
29678
+ return Object.assign(new Error("Can't reach the host to check who you are. Try again in a moment."), { code: "people-unavailable" });
29679
+ }
29680
+ async function author(el5, attr = "data-by") {
29681
+ if (!window.clay?.isEditMode) throw new Error("clay.author: this page is not in edit mode");
29682
+ if (!(el5 instanceof Element)) throw new TypeError("clay.author: needs an element");
29683
+ if (host.supports) {
29684
+ const meta = await hostMeta({ fresh: true });
29685
+ const fresh = hostMetaOutcome(meta);
29686
+ if (fresh === "none") throw Object.assign(new Error("This page lost its connection to the host. Reload it to keep editing."), { code: "people-unavailable" });
29687
+ if (fresh !== "ok") throw unavailable();
29688
+ applyMeta(meta);
29689
+ if (host.unavailable) throw Object.assign(new Error("The app hosting this file can't name you right now. Check its Profile settings, then try again."), { code: "people-unavailable" });
29690
+ } else if (outcome !== "ok" && outcome !== "none") {
29691
+ await refresh(outcome === "failed");
29692
+ }
29693
+ if (outcome === "failed") throw unavailable();
29694
+ let me3 = currentMe();
29695
+ if (!me3) {
29696
+ const chosen = await askName();
29697
+ if (!chosen) return null;
29698
+ writeLocal(chosen);
29699
+ me3 = record(chosen);
29700
+ emit2();
29701
+ }
29702
+ add(me3);
29703
+ el5.setAttribute(attr, me3.id);
29704
+ return me3;
29705
+ }
29706
+ function askName() {
29707
+ if (asking && askingRoot?.isConnected) return asking;
29708
+ if (asking) cancelAsking();
29709
+ asking = new Promise((resolve) => {
29710
+ const theme2 = resolveTheme();
29711
+ const { root: root3, overlay, panel: panel2, heading, body, footer, close } = bevelDialog({
29712
+ zIndex: "2147483001",
29713
+ width: "420px",
29714
+ closable: true,
29715
+ titled: true,
29716
+ theme: theme2,
29717
+ surface: "people"
29718
+ });
29719
+ askingRoot = root3;
29720
+ cancelAsking = () => finish2(null);
29721
+ heading.textContent = "What name should appear on your changes?";
29722
+ panel2.setAttribute("aria-label", "Your name");
29723
+ const input = bevelInput("input", { rules: ["display:block", "width:100%", "margin:0"], theme: theme2, parts: ["dialog.input", "people.input"] });
29724
+ input.setAttribute("aria-label", "Your name");
29725
+ input.maxLength = MAX_NAME;
29726
+ input.autocomplete = "name";
29727
+ const hint = bevelText(
29728
+ "p",
29729
+ ["display:block", "margin:10px 0 0", "font-size:13px", "opacity:.8"],
29730
+ "Saved in this browser. Everyone who can read this page will see it.",
29731
+ { theme: theme2, parts: ["dialog.hint", "people.hint"], role: "mutedText" }
29732
+ );
29733
+ const hintText = hint.textContent;
29734
+ body.append(input, hint);
29735
+ const known = list().slice(0, 8);
29736
+ if (known.length) {
29737
+ const label = bevelText("p", ["display:block", "margin:18px 0 8px", "font-size:13px"], "Or continue as someone already in this page:", { theme: theme2, parts: ["people.choiceLabel"], role: "mutedText" });
29738
+ const row = bevelBox("div", ["display:flex", "flex-wrap:wrap", "gap:8px"], { theme: theme2, parts: ["people.choices"] });
29739
+ for (const person of known) {
29740
+ row.append(bevelButton(person.name, { small: true, onClick: () => finish2({ id: person.id, name: person.name }), theme: theme2, parts: ["dialog.button", "people.button", "people.choice"], labelParts: ["dialog.buttonLabel", "people.buttonLabel"] }));
29741
+ }
29742
+ body.append(label, row);
29743
+ }
29744
+ const cancel2 = bevelButton("Cancel", { onClick: () => finish2(null), theme: theme2, parts: ["dialog.button", "people.button"], labelParts: ["dialog.buttonLabel", "people.buttonLabel"] });
29745
+ const ok = bevelButton("Continue", { variant: "primary", onClick: () => submit2(), theme: theme2, parts: ["dialog.button", "people.button"], labelParts: ["dialog.buttonLabel", "people.buttonLabel"] });
29746
+ footer.append(cancel2, ok);
29747
+ const submit2 = () => {
29748
+ const name = input.value.trim().replace(/\s+/g, " ");
29749
+ if (!name) {
29750
+ input.focus();
29751
+ return;
29752
+ }
29753
+ if (EMAIL.test(name)) {
29754
+ hint.textContent = "Use a name, not an email address. Everyone who can read this page will see it.";
29755
+ input.focus();
29756
+ return;
29757
+ }
29758
+ finish2({ id: mint(), name: name.slice(0, MAX_NAME) });
29759
+ };
29760
+ input.addEventListener("input", () => {
29761
+ if (hint.textContent !== hintText) hint.textContent = hintText;
29762
+ });
29763
+ panel2.addEventListener("submit", (event) => {
29764
+ event.preventDefault();
29765
+ submit2();
29766
+ });
29767
+ close?.addEventListener("click", () => finish2(null));
29768
+ let pressedOnBackdrop = false;
29769
+ overlay.addEventListener("mousedown", (event) => {
29770
+ pressedOnBackdrop = event.target === overlay;
29771
+ });
29772
+ overlay.addEventListener("click", (event) => {
29773
+ if (pressedOnBackdrop && event.target === overlay) finish2(null);
29774
+ pressedOnBackdrop = false;
29775
+ });
29776
+ const onKey = (event) => {
29777
+ if (event.key === "Tab") {
29778
+ event.stopPropagation();
29779
+ keepFocusIn(panel2, event);
29780
+ return;
29781
+ }
29782
+ if (event.key === "Escape") {
29783
+ event.preventDefault();
29784
+ event.stopPropagation();
29785
+ finish2(null);
29786
+ } else if (event.key === "Enter" && event.target === input) {
29787
+ event.preventDefault();
29788
+ event.stopPropagation();
29789
+ submit2();
29790
+ }
29791
+ };
29792
+ document.addEventListener("keydown", onKey, true);
29793
+ let done = false;
29794
+ function finish2(person) {
29795
+ if (done) return;
29796
+ done = true;
29797
+ document.removeEventListener("keydown", onKey, true);
29798
+ root3.remove();
29799
+ if (person) writeLocal(person);
29800
+ asking = null;
29801
+ askingRoot = null;
29802
+ cancelAsking = null;
29803
+ resolve(person);
29804
+ }
29805
+ dismissOf.set(root3, () => finish2(null));
29806
+ root3.setAttribute("aria-hidden", "false");
29807
+ modalHost().append(root3);
29808
+ input.focus();
29809
+ });
29810
+ return asking;
29811
+ }
29812
+ function modalHost() {
29813
+ const open2 = [...document.querySelectorAll("dialog[open]")].filter((d) => d.matches(":modal"));
29814
+ return open2[open2.length - 1] || document.body;
29815
+ }
29816
+ function touchesRegistry(node) {
29817
+ return node.nodeType === 1 && (node.matches("[clay-people]") || !!node.querySelector("[clay-people]"));
29818
+ }
29819
+ function registrySignature() {
29820
+ return JSON.stringify([...saved().values()].map(({ id, name }) => [id, name]));
29821
+ }
29822
+ function observe() {
29823
+ lastRegistry = registrySignature();
29824
+ new MutationObserver((records3) => {
29825
+ for (const rec of records3) {
29826
+ const target = rec.target.nodeType === 1 ? rec.target : rec.target.parentElement;
29827
+ if (rec.attributeName === "clay-people" || target?.closest("[clay-people]") || [...rec.addedNodes].some(touchesRegistry) || [...rec.removedNodes].some(touchesRegistry)) {
29828
+ cache = null;
29829
+ const next = registrySignature();
29830
+ if (next !== lastRegistry) {
29831
+ lastRegistry = next;
29832
+ emit2();
29833
+ }
29834
+ return;
29835
+ }
29836
+ }
29837
+ }).observe(document.body, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ["value", "clay-people"] });
29838
+ }
29839
+ async function init18() {
29840
+ observe();
29841
+ if (!editing()) return;
29842
+ window.addEventListener("storage", (event) => {
29843
+ if (event.key !== STORE) return;
29844
+ local = void 0;
29845
+ emit2();
29846
+ });
29847
+ document.addEventListener("visibilitychange", () => {
29848
+ if (document.visibilityState === "visible" && outcome !== "none") refresh(true);
29849
+ });
29850
+ const first = refresh(false);
29851
+ await Promise.race([first, new Promise((resolve) => setTimeout(resolve, BOOT_WAIT_MS))]);
29852
+ }
29853
+ var editing, ID, STORE, BOOT_WAIT_MS, COLORS2, MAX_NAME, EMAIL, host, outcome, local, cache, asking, askingRoot, cancelAsking, lastRegistry, people, ready2, people_default;
29854
+ var init_people = __esm({
29855
+ "src/plugins/people.js"() {
29856
+ init_host_meta();
29857
+ init_is_edit_mode();
29858
+ init_bevel_dialog();
29859
+ init_bevel_controls();
29860
+ init_theme();
29861
+ editing = () => window.clay && "isEditMode" in window.clay ? !!window.clay.isEditMode : isEditMode;
29862
+ ID = /^[A-Za-z0-9_-]{8,64}$/;
29863
+ STORE = "clay:people:me";
29864
+ BOOT_WAIT_MS = 2e3;
29865
+ COLORS2 = 8;
29866
+ MAX_NAME = 120;
29867
+ EMAIL = /\S+@\S+\.\S+/;
29868
+ host = { supports: false, me: null, members: null, unavailable: false };
29869
+ outcome = null;
29870
+ cache = null;
29871
+ asking = null;
29872
+ askingRoot = null;
29873
+ cancelAsking = null;
29874
+ lastRegistry = "";
29875
+ people = Object.freeze({ get: get2, list, available, add, remove: remove2, canRename, rename });
29876
+ ready2 = init18();
29877
+ people_default = people;
29878
+ }
29879
+ });
29880
+
28992
29881
  // src/plugins/wire.js
28993
29882
  var wire_exports = {};
28994
29883
  __export(wire_exports, {
@@ -29023,7 +29912,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29023
29912
  startedAt: rec.startedAt
29024
29913
  };
29025
29914
  }
29026
- function emit2(rec, frame) {
29915
+ function emit3(rec, frame) {
29027
29916
  const snapshot = view2(rec);
29028
29917
  for (const fn2 of listeners) {
29029
29918
  try {
@@ -29037,7 +29926,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29037
29926
  function setState(rec, state2) {
29038
29927
  if (rec.state === state2) return;
29039
29928
  rec.state = state2;
29040
- emit2(rec);
29929
+ emit3(rec);
29041
29930
  }
29042
29931
  function arm(rec, ms, message, { cancelFirst = false, errorCode = null } = {}) {
29043
29932
  clearTimeout(rec.timer);
@@ -29119,7 +30008,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29119
30008
  rec.text = typeof frame.text === "string" ? frame.text : "";
29120
30009
  rec.state = "acked";
29121
30010
  notifyStatus(rec, frame);
29122
- emit2(rec, frame);
30011
+ emit3(rec, frame);
29123
30012
  break;
29124
30013
  case "wire/done":
29125
30014
  rec.result = Object.prototype.hasOwnProperty.call(frame, "payload") ? frame.payload : null;
@@ -29187,7 +30076,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29187
30076
  rec.landingTimer = setTimeout(() => {
29188
30077
  finish(rec, "done", null);
29189
30078
  }, LANDING_TIMEOUT_MS);
29190
- emit2(rec);
30079
+ emit3(rec);
29191
30080
  closeStreamIfIdle();
29192
30081
  }
29193
30082
  function finish(rec, state2, error, fields = {}) {
@@ -29211,13 +30100,13 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29211
30100
  rec.signalHandler = null;
29212
30101
  }
29213
30102
  releaseSaving(rec);
29214
- emit2(rec);
30103
+ emit3(rec);
29215
30104
  closeStreamIfIdle();
29216
30105
  prune();
29217
30106
  if (rec.settle) {
29218
- const settle3 = rec.settle;
30107
+ const settle4 = rec.settle;
29219
30108
  rec.settle = null;
29220
- settle3(view2(rec));
30109
+ settle4(view2(rec));
29221
30110
  }
29222
30111
  }
29223
30112
  function loadSaveLane() {
@@ -29243,14 +30132,14 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29243
30132
  function once(eventName, timeout) {
29244
30133
  return new Promise((resolve) => {
29245
30134
  let done = false;
29246
- const settle3 = () => {
30135
+ const settle4 = () => {
29247
30136
  if (done) return;
29248
30137
  done = true;
29249
- document.removeEventListener(eventName, settle3);
30138
+ document.removeEventListener(eventName, settle4);
29250
30139
  resolve();
29251
30140
  };
29252
- document.addEventListener(eventName, settle3);
29253
- setTimeout(settle3, timeout);
30141
+ document.addEventListener(eventName, settle4);
30142
+ setTimeout(settle4, timeout);
29254
30143
  });
29255
30144
  }
29256
30145
  async function flushSave2() {
@@ -29350,7 +30239,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29350
30239
  rec.settle = resolve;
29351
30240
  });
29352
30241
  records2.set(id, rec);
29353
- emit2(rec);
30242
+ emit3(rec);
29354
30243
  if (rec.signal) {
29355
30244
  rec.signalHandler = () => cancel(id);
29356
30245
  rec.signal.addEventListener("abort", rec.signalHandler, { once: true });
@@ -29454,11 +30343,11 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29454
30343
  postFrame({ type: "wire/cancel", id: rec.id }).catch(() => {
29455
30344
  });
29456
30345
  }
29457
- function get2(id) {
30346
+ function get3(id) {
29458
30347
  const rec = records2.get(id);
29459
30348
  return rec ? view2(rec) : void 0;
29460
30349
  }
29461
- function list() {
30350
+ function list2() {
29462
30351
  return [...records2.values()].map(view2);
29463
30352
  }
29464
30353
  function isBusy() {
@@ -29506,7 +30395,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29506
30395
  saveDepth = 0;
29507
30396
  saveLane = null;
29508
30397
  saveLaneReady = null;
29509
- wire = { send: send2, cancel, get: get2, list, isBusy, on: on2, helpers };
30398
+ wire = { send: send2, cancel, get: get3, list: list2, isBusy, on: on2, helpers };
29510
30399
  wire_default = wire;
29511
30400
  }
29512
30401
  });
@@ -29517,8 +30406,8 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29517
30406
  aiEdit: () => aiEdit
29518
30407
  });
29519
30408
  async function helperStateNow() {
29520
- const list2 = await wire_default.helpers();
29521
- const helper = list2.find((h) => h.name === HELPER);
30409
+ const list3 = await wire_default.helpers();
30410
+ const helper = list3.find((h) => h.name === HELPER);
29522
30411
  return helper && (helper.state === "ready" || helper.state === "unavailable") ? helper.state : null;
29523
30412
  }
29524
30413
  function pauseObservers() {
@@ -29670,11 +30559,11 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29670
30559
  }
29671
30560
  });
29672
30561
  session.handle = handle;
29673
- const outcome = await handle.done;
30562
+ const outcome2 = await handle.done;
29674
30563
  if (!session || session.handle !== handle) return;
29675
- if (outcome.state === "done") onDone(outcome.result || {});
29676
- else if (outcome.state === "cancelled") onError("HTML Clay stopped this edit.");
29677
- else onError(outcome.error || "the helper reported an error");
30564
+ if (outcome2.state === "done") onDone(outcome2.result || {});
30565
+ else if (outcome2.state === "cancelled") onError("HTML Clay stopped this edit.");
30566
+ else onError(outcome2.error || "the helper reported an error");
29678
30567
  }
29679
30568
  function isScriptURL(value) {
29680
30569
  const url = value.replace(/[\t\n\r]/g, "").replace(/^[\u0000- ]+/, "").toLowerCase();
@@ -29689,21 +30578,21 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29689
30578
  }
29690
30579
  function activeParts(root3) {
29691
30580
  const parts = /* @__PURE__ */ new Map();
29692
- const add = (part) => parts.set(part, (parts.get(part) || 0) + 1);
30581
+ const add2 = (part) => parts.set(part, (parts.get(part) || 0) + 1);
29693
30582
  for (const el5 of [root3, ...root3.querySelectorAll("*")]) {
29694
30583
  const tag = el5.tagName.toLowerCase();
29695
30584
  if (tag === "script") {
29696
- if (runsAsCode(el5)) add("script " + attrsOf(el5) + "|" + el5.textContent.trim());
30585
+ if (runsAsCode(el5)) add2("script " + attrsOf(el5) + "|" + el5.textContent.trim());
29697
30586
  } else if (ACTIVE_TAGS.has(tag) || tag === "meta" && el5.hasAttribute("http-equiv")) {
29698
- add(tag + " " + attrsOf(el5));
30587
+ add2(tag + " " + attrsOf(el5));
29699
30588
  } else if ((tag === "animate" || tag === "set") && /href/i.test(el5.getAttribute("attributeName") || "")) {
29700
- add(tag + " " + attrsOf(el5));
30589
+ add2(tag + " " + attrsOf(el5));
29701
30590
  }
29702
30591
  for (const attr of el5.attributes) {
29703
30592
  const name = attr.name.toLowerCase();
29704
- if (ACTIVE_ATTR.test(name)) add(tag + " " + name + "=" + attr.value.trim());
29705
- else if (name === "srcdoc") add(tag + " srcdoc=" + attr.value);
29706
- else if (isScriptURL(attr.value)) add(tag + " " + name + "=" + attr.value);
30593
+ if (ACTIVE_ATTR.test(name)) add2(tag + " " + name + "=" + attr.value.trim());
30594
+ else if (name === "srcdoc") add2(tag + " srcdoc=" + attr.value);
30595
+ else if (isScriptURL(attr.value)) add2(tag + " " + name + "=" + attr.value);
29707
30596
  }
29708
30597
  }
29709
30598
  return parts;
@@ -29823,7 +30712,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29823
30712
  ::highlight(clay-ai-edit) { background-color: ${TOKENS2["brass-soft"]} !important; }
29824
30713
  `;
29825
30714
  document.head.appendChild(style2);
29826
- const scheme = `color-scheme:${pageScheme()}`;
30715
+ const scheme2 = `color-scheme:${pageScheme()}`;
29827
30716
  ring2 = marked(bevelBox("div", [
29828
30717
  "box-sizing:content-box",
29829
30718
  "position:fixed",
@@ -29831,7 +30720,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29831
30720
  "pointer-events:none",
29832
30721
  `border:2px solid ${TOKENS2.brass}`,
29833
30722
  "border-radius:0",
29834
- scheme
30723
+ scheme2
29835
30724
  ]), "data-clay-ai-edit", "ring");
29836
30725
  setShown(ring2, false);
29837
30726
  panel = marked(bevelSurface("div", [
@@ -29842,7 +30731,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29842
30731
  "border-radius:12px",
29843
30732
  `box-shadow:${SHADOW}`,
29844
30733
  `font:13px/1.5 ${FONT_SANS}`,
29845
- scheme
30734
+ scheme2
29846
30735
  ]), "data-clay-ai-edit", "panel");
29847
30736
  setShown(panel, false);
29848
30737
  panel.setAttribute("role", "dialog");
@@ -29900,7 +30789,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29900
30789
  "align-items:center",
29901
30790
  `box-shadow:${SHADOW}`,
29902
30791
  `font:13px/1.5 ${FONT_SANS}`,
29903
- scheme
30792
+ scheme2
29904
30793
  ]), "data-clay-ai-edit", "status-bar");
29905
30794
  setShown(bar, false, "flex");
29906
30795
  barStatus = part(bevelText("div", [
@@ -29924,12 +30813,12 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
29924
30813
  bar.append(barStatus, barStop, barKeep, barRevert, barClose);
29925
30814
  chip = marked(bevelButton("AI", {
29926
30815
  small: true,
29927
- extra: ["position:fixed", "width:26px", "height:26px", `font:600 11px/1 ${FONT_SANS}`, ...FLOATING, scheme]
30816
+ extra: ["position:fixed", "width:26px", "height:26px", `font:600 11px/1 ${FONT_SANS}`, ...FLOATING, scheme2]
29928
30817
  }), "data-clay-ai-edit", "chip");
29929
30818
  chip.title = "Comment on this (\u2318J)";
29930
30819
  setShown(chip, false, "inline-grid");
29931
30820
  docBubble = marked(bevelButton("AI", {
29932
- extra: ["position:fixed", "width:40px", "height:40px", `font:600 14px/1 ${FONT_SANS}`, ...FLOATING, scheme]
30821
+ extra: ["position:fixed", "width:40px", "height:40px", `font:600 14px/1 ${FONT_SANS}`, ...FLOATING, scheme2]
29933
30822
  }), "data-clay-ai-edit", "bubble");
29934
30823
  docBubble.title = "Comment on the whole page";
29935
30824
  placeBubble();
@@ -30040,8 +30929,8 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
30040
30929
  }
30041
30930
  function placeBubble() {
30042
30931
  if (!docBubble) return;
30043
- const host = document.querySelector("[data-hcms-toggle-host]");
30044
- const rect = host && host.getBoundingClientRect();
30932
+ const host2 = document.querySelector("[data-hcms-toggle-host]");
30933
+ const rect = host2 && host2.getBoundingClientRect();
30045
30934
  const beside = !!rect && rect.width > 0 && rect.height > 0;
30046
30935
  docBubble.pin({
30047
30936
  right: beside ? `${Math.round((document.documentElement.clientWidth || window.innerWidth) - rect.left + 8)}px` : "16px",
@@ -30370,7 +31259,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
30370
31259
  selectionTimer = setTimeout(showChipForSelection, 150);
30371
31260
  });
30372
31261
  }
30373
- async function init18() {
31262
+ async function init19() {
30374
31263
  if (!isEditMode || panel) return;
30375
31264
  helperState = await helperStateNow();
30376
31265
  if (!helperState || panel) return;
@@ -30382,7 +31271,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
30382
31271
  }
30383
31272
  async function open(el5, { prompt } = {}) {
30384
31273
  if (!canOpenTarget(el5)) return false;
30385
- await init18();
31274
+ await init19();
30386
31275
  await new Promise((resolve) => setTimeout(resolve, 0));
30387
31276
  if (!panel || session || barMode || !canOpenTarget(el5)) return false;
30388
31277
  if (!panel.hidden && anchorEl === el5) {
@@ -30441,10 +31330,10 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
30441
31330
  MODAL_SELECTOR = '[data-clay-modal], dialog[open], [aria-modal="true"]';
30442
31331
  PAGE_FIELD = 'input,textarea,select,[contenteditable=""],[contenteditable="true"]';
30443
31332
  dom_ready_default(() => {
30444
- init18().catch(() => {
31333
+ init19().catch(() => {
30445
31334
  });
30446
31335
  });
30447
- aiEdit = { init: init18, open };
31336
+ aiEdit = { init: init19, open };
30448
31337
  }
30449
31338
  });
30450
31339
 
@@ -30452,7 +31341,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
30452
31341
  var demo_exports = {};
30453
31342
  __export(demo_exports, {
30454
31343
  demo: () => demo,
30455
- ready: () => ready2
31344
+ ready: () => ready3
30456
31345
  });
30457
31346
  function postedHtml(raw) {
30458
31347
  return typeof raw === "string" ? raw : "";
@@ -30467,9 +31356,9 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
30467
31356
  }
30468
31357
  }
30469
31358
  async function restore() {
30470
- const saved = localStorage.getItem(KEY);
30471
- if (!saved) return;
30472
- const doc = new DOMParser().parseFromString(saved, "text/html");
31359
+ const saved2 = localStorage.getItem(KEY);
31360
+ if (!saved2) return;
31361
+ const doc = new DOMParser().parseFromString(saved2, "text/html");
30473
31362
  if (!isEditMode) {
30474
31363
  for (const el5 of doc.querySelectorAll(":is([editable], [clay-editable])[contenteditable]")) {
30475
31364
  el5.removeAttribute("contenteditable");
@@ -30481,7 +31370,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
30481
31370
  scripts: { handle: true, matchMode: "smart" }
30482
31371
  });
30483
31372
  }
30484
- var KEY, LATENCY_MS, CHROME_SELECTOR, failNextSave, snapshotBody, realFetch, ready2, demo;
31373
+ var KEY, LATENCY_MS, CHROME_SELECTOR, failNextSave, snapshotBody, realFetch, ready3, demo;
30485
31374
  var init_demo = __esm({
30486
31375
  "src/plugins/demo.js"() {
30487
31376
  init_hyper_morph_vendor();
@@ -30540,7 +31429,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
30540
31429
  }, LATENCY_MS);
30541
31430
  });
30542
31431
  };
30543
- ready2 = restore();
31432
+ ready3 = restore();
30544
31433
  demo = {
30545
31434
  key: KEY,
30546
31435
  failNext() {
@@ -35087,7 +35976,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35087
35976
  const html = doc.childNodes.find((n2) => n2.nodeName === "html");
35088
35977
  const outside = doc.childNodes.filter((n2) => n2 !== html).map((n2) => n2.nodeName === "#documentType" ? { kind: "doctype", name: n2.name || "", publicId: n2.publicId || "", systemId: n2.systemId || "" } : { kind: n2.nodeName === "#comment" ? "comment" : n2.nodeName, value: n2.data });
35089
35978
  const root3 = html ? locOf(html, null, src) : null;
35090
- if (root3) settle(root3, src);
35979
+ if (root3) settle2(root3, src);
35091
35980
  return {
35092
35981
  src,
35093
35982
  doc,
@@ -35145,7 +36034,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35145
36034
  }
35146
36035
  return loc;
35147
36036
  }
35148
- function settle(loc, src, limit = Infinity) {
36037
+ function settle2(loc, src, limit = Infinity) {
35149
36038
  if (loc.kind !== "element") return;
35150
36039
  for (const cl2 of loc.children) {
35151
36040
  if (cl2.from >= limit && !(cl2.kind === "text" && !/\S/.test(cl2.value))) loc.movedIn = true;
@@ -35176,7 +36065,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35176
36065
  floor = cl2.to;
35177
36066
  }
35178
36067
  const inner = loc.located && loc.endTagged ? Math.min(limit, loc.closeTo) : limit;
35179
- for (const cl2 of loc.children) settle(cl2, src, inner);
36068
+ for (const cl2 of loc.children) settle2(cl2, src, inner);
35180
36069
  }
35181
36070
  function firstOutOfOrder(loc, flag = "outOfOrder") {
35182
36071
  if (loc[flag]) return loc.tag;
@@ -35218,7 +36107,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35218
36107
  }
35219
36108
  return out;
35220
36109
  }
35221
- function mix(h, s2) {
36110
+ function mix2(h, s2) {
35222
36111
  for (let i2 = 0; i2 < s2.length; i2++) {
35223
36112
  h ^= s2.charCodeAt(i2);
35224
36113
  h = Math.imul(h, 16777619);
@@ -35228,15 +36117,15 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35228
36117
  function hashKey(parts) {
35229
36118
  let a = 2166136261, b = 5381;
35230
36119
  for (const p2 of parts) {
35231
- a = mix(a, p2);
35232
- b = (Math.imul(b, 33) ^ mix(a ^ 2654435769, p2)) >>> 0;
36120
+ a = mix2(a, p2);
36121
+ b = (Math.imul(b, 33) ^ mix2(a ^ 2654435769, p2)) >>> 0;
35233
36122
  }
35234
36123
  return a.toString(36) + "." + b.toString(36);
35235
36124
  }
35236
36125
  function liveKids(el5) {
35237
- const list2 = el5.nodeType === 1 && el5.tagName === "TEMPLATE" && el5.content ? el5.content.childNodes : el5.childNodes;
36126
+ const list3 = el5.nodeType === 1 && el5.tagName === "TEMPLATE" && el5.content ? el5.content.childNodes : el5.childNodes;
35238
36127
  const out = [];
35239
- for (const n2 of list2) if (n2.nodeType === 1 || n2.nodeType === 3 || n2.nodeType === 8) out.push(n2);
36128
+ for (const n2 of list3) if (n2.nodeType === 1 || n2.nodeType === 3 || n2.nodeType === 8) out.push(n2);
35240
36129
  return out;
35241
36130
  }
35242
36131
  function keysOfLive(n2, keyMap) {
@@ -35810,8 +36699,8 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35810
36699
  return vDiff(P2.parseFromString(a.innerHTML, "text/html").body, P2.parseFromString(raw(b), "text/html").body, path + ">#noscript");
35811
36700
  }
35812
36701
  function vKids(el5) {
35813
- const list2 = el5.localName === "template" && el5.content ? el5.content.childNodes : el5.childNodes;
35814
- return Array.from(list2).filter((n2) => n2.nodeType === 1 || n2.nodeType === 3 || n2.nodeType === 8);
36702
+ const list3 = el5.localName === "template" && el5.content ? el5.content.childNodes : el5.childNodes;
36703
+ return Array.from(list3).filter((n2) => n2.nodeType === 1 || n2.nodeType === 3 || n2.nodeType === 8);
35815
36704
  }
35816
36705
  function vDiff(a, b, path, at3 = []) {
35817
36706
  if (a.nodeType !== b.nodeType) return path + ": node type " + a.nodeType + " vs " + b.nodeType;
@@ -35952,12 +36841,12 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35952
36841
  scheduleRefresh();
35953
36842
  }
35954
36843
  function report(bytes) {
35955
- const outcome = state.lastOutcome;
35956
- if (!outcome || outcome.text !== bytes) return;
36844
+ const outcome2 = state.lastOutcome;
36845
+ if (!outcome2 || outcome2.text !== bytes) return;
35957
36846
  state.lastOutcome = null;
35958
36847
  state.saves++;
35959
- if (outcome.scope === "full") reprint(outcome.reason);
35960
- else if (outcome.scope === "partial") partialReprint(outcome.reason, outcome.printed);
36848
+ if (outcome2.scope === "full") reprint(outcome2.reason);
36849
+ else if (outcome2.scope === "partial") partialReprint(outcome2.reason, outcome2.printed);
35961
36850
  }
35962
36851
  function queueRepair(event) {
35963
36852
  if (!state.installed) return;
@@ -38744,9 +39633,9 @@ ${t}` : `"use strict"; return (${t});`;
38744
39633
  cleanContentClone: () => cleanContentClone,
38745
39634
  createContentView: () => createContentView,
38746
39635
  default: () => dom_helpers_default,
38747
- init: () => init19
39636
+ init: () => init20
38748
39637
  });
38749
- function init19() {
39638
+ function init20() {
38750
39639
  window.clay = window.clay || {};
38751
39640
  window.clay.dom = Object.assign(window.clay.dom || {}, { createContentView, cleanContentClone });
38752
39641
  if (HTMLElement.prototype.hasOwnProperty("nearest")) {
@@ -38897,8 +39786,8 @@ ${t}` : `"use strict"; return (${t});`;
38897
39786
  init_nearest();
38898
39787
  init_pipe();
38899
39788
  init_content_dom();
38900
- init19();
38901
- dom_helpers_default = init19;
39789
+ init20();
39790
+ dom_helpers_default = init20;
38902
39791
  }
38903
39792
  });
38904
39793
 
@@ -38914,12 +39803,12 @@ ${t}` : `"use strict"; return (${t});`;
38914
39803
  timeoutId = setTimeout(() => {
38915
39804
  const waiting = pending4;
38916
39805
  pending4 = [];
38917
- settle2(callback, ctx, args, waiting);
39806
+ settle3(callback, ctx, args, waiting);
38918
39807
  }, delay);
38919
39808
  });
38920
39809
  };
38921
39810
  }
38922
- function settle2(callback, ctx, args, waiting) {
39811
+ function settle3(callback, ctx, args, waiting) {
38923
39812
  let result2;
38924
39813
  try {
38925
39814
  result2 = callback.apply(ctx, args);
@@ -39500,12 +40389,14 @@ ${t}` : `"use strict"; return (${t});`;
39500
40389
  });
39501
40390
 
39502
40391
  // src/ui/modal.js
39503
- var MicroModal, themodal, modal_default;
40392
+ var SURFACE_THEME, MicroModal, themodal, modal_default;
39504
40393
  var init_modal = __esm({
39505
40394
  "src/ui/modal.js"() {
39506
40395
  init_bevel_controls();
39507
40396
  init_hostile_css();
39508
40397
  init_bevel_dialog();
40398
+ init_theme();
40399
+ SURFACE_THEME = /* @__PURE__ */ Symbol("clay.surfaceTheme");
39509
40400
  MicroModal = (() => {
39510
40401
  "use strict";
39511
40402
  const FOCUSABLE_ELEMENTS = [
@@ -39674,8 +40565,8 @@ ${t}` : `"use strict"; return (${t});`;
39674
40565
  for (const id in triggerMap) validateModalPresence(id);
39675
40566
  return true;
39676
40567
  };
39677
- const init29 = (config) => {
39678
- const options = Object.assign({}, { openTrigger: "data-micromodal-trigger" }, config);
40568
+ const init30 = (config2) => {
40569
+ const options = Object.assign({}, { openTrigger: "data-micromodal-trigger" }, config2);
39679
40570
  const triggers = [...document.querySelectorAll(`[${options.openTrigger}]`)];
39680
40571
  const triggerMap = generateTriggerMap(triggers, options.openTrigger);
39681
40572
  if (options.debugMode === true && validateArgs(triggers, triggerMap) === false) return;
@@ -39686,8 +40577,8 @@ ${t}` : `"use strict"; return (${t});`;
39686
40577
  activeModal = new Modal(options);
39687
40578
  }
39688
40579
  };
39689
- const show2 = (targetModal, config) => {
39690
- const options = config || {};
40580
+ const show2 = (targetModal, config2) => {
40581
+ const options = config2 || {};
39691
40582
  options.targetModal = targetModal;
39692
40583
  if (options.debugMode === true && validateModalPresence(targetModal) === false) return;
39693
40584
  if (activeModal) activeModal.removeEventListeners();
@@ -39699,9 +40590,9 @@ ${t}` : `"use strict"; return (${t});`;
39699
40590
  activeModal.closeModal();
39700
40591
  activeModal = null;
39701
40592
  };
39702
- return { init: init29, show: show2, close };
40593
+ return { init: init30, show: show2, close };
39703
40594
  })();
39704
- themodal = /* @__PURE__ */ (() => {
40595
+ themodal = (() => {
39705
40596
  let html = "";
39706
40597
  let yes = "";
39707
40598
  let no2 = "";
@@ -39709,6 +40600,7 @@ ${t}` : `"use strict"; return (${t});`;
39709
40600
  let closeHtml = "";
39710
40601
  let title = "";
39711
40602
  let width = "";
40603
+ let theme2;
39712
40604
  let enableClickOutsideCloses = true;
39713
40605
  let disableScroll = true;
39714
40606
  let disableFocus = false;
@@ -39741,15 +40633,17 @@ ${t}` : `"use strict"; return (${t});`;
39741
40633
  document.body.style.overflow = "";
39742
40634
  }
39743
40635
  this._dismiss = dismiss3;
39744
- const shell = bevelDialog({ zIndex, closable: !!closeHtml, titled: !!title, ...width ? { width } : {} });
40636
+ const surfaceTheme = theme2 === void 0 ? resolveTheme() : theme2;
40637
+ const shell = bevelDialog({ zIndex, closable: !!closeHtml, titled: !!title, ...width ? { width } : {}, theme: surfaceTheme });
39745
40638
  const modalRootElem = shell.root;
39746
40639
  const modalOverlayElem = shell.overlay;
39747
40640
  const modalContainerElem = shell.panel;
39748
40641
  const modalContentElem = shell.body;
39749
40642
  const modalButtonsElem = shell.footer;
39750
40643
  const modalCloseElem = shell.close;
39751
- const modalNoElem = bevelButton("");
39752
- const modalYesElem = bevelButton("", { variant: "primary" });
40644
+ const buttonParts = { theme: surfaceTheme, parts: ["dialog.button"], labelParts: ["dialog.buttonLabel"] };
40645
+ const modalNoElem = bevelButton("", buttonParts);
40646
+ const modalYesElem = bevelButton("", { variant: "primary", ...buttonParts });
39753
40647
  modalYesElem.type = "submit";
39754
40648
  modalButtonsElem.append(modalNoElem, modalYesElem);
39755
40649
  const place4 = (target, value) => {
@@ -39827,6 +40721,7 @@ ${t}` : `"use strict"; return (${t});`;
39827
40721
  closeHtml = "";
39828
40722
  title = "";
39829
40723
  width = "";
40724
+ theme2 = void 0;
39830
40725
  enableClickOutsideCloses = true;
39831
40726
  disableScroll = true;
39832
40727
  disableFocus = false;
@@ -39841,7 +40736,7 @@ ${t}` : `"use strict"; return (${t});`;
39841
40736
  this.isShowing = true;
39842
40737
  myOnOpen.forEach((cb) => cb());
39843
40738
  if (!disableFocus) {
39844
- let firstInput = modalContentElem.querySelector(":is(input,textarea,button):not([hidden])") || [modalYesElem, modalNoElem].find((button) => !button.hidden);
40739
+ let firstInput = modalContentElem.querySelector(":is(input,textarea,button):not([hidden])") || [modalYesElem, modalNoElem].find((button2) => !button2.hidden);
39845
40740
  firstInput?.focus();
39846
40741
  firstInput?.setSelectionRange?.(-1, -1);
39847
40742
  }
@@ -39873,6 +40768,12 @@ ${t}` : `"use strict"; return (${t});`;
39873
40768
  set width(newVal) {
39874
40769
  width = newVal;
39875
40770
  },
40771
+ get [SURFACE_THEME]() {
40772
+ return theme2;
40773
+ },
40774
+ set [SURFACE_THEME](newVal) {
40775
+ theme2 = newVal;
40776
+ },
39876
40777
  get yes() {
39877
40778
  return yes;
39878
40779
  },
@@ -39940,10 +40841,11 @@ ${t}` : `"use strict"; return (${t});`;
39940
40841
 
39941
40842
  // src/ui/dialogs.js
39942
40843
  function createModal(promptText, yesCallback, extraContent = "", includeInput = false, defaultValue = "", yesLabel = "OK") {
39943
- const content = bevelBox("div", ["display:flex", "flex-direction:column", "gap:14px", `font:14.5px/1.55 ${FONT_SANS}`, `color:${TOKENS2["ink-2"]}`]);
40844
+ const theme2 = resolveTheme();
40845
+ const content = bevelBox("div", ["display:flex", "flex-direction:column", "gap:14px", `font:14.5px/1.55 ${FONT_SANS}`, `color:${TOKENS2["ink-2"]}`], { theme: theme2, parts: ["dialog.content"], role: "text" });
39944
40846
  let input = null;
39945
40847
  if (includeInput) {
39946
- input = bevelInput("input", { rules: ["display:block", "width:100%", "margin:0"] });
40848
+ input = bevelInput("input", { rules: ["display:block", "width:100%", "margin:0"], theme: theme2, parts: ["dialog.input"] });
39947
40849
  input.setAttribute("value", String(defaultValue));
39948
40850
  input.required = true;
39949
40851
  content.append(input);
@@ -39952,6 +40854,7 @@ ${t}` : `"use strict"; return (${t});`;
39952
40854
  modal_default.title = promptText;
39953
40855
  modal_default.html = includeInput || extraContent ? content : "";
39954
40856
  modal_default.width = "440px";
40857
+ modal_default[SURFACE_THEME] = theme2;
39955
40858
  modal_default.closeHtml = "x";
39956
40859
  modal_default.no = "Cancel";
39957
40860
  modal_default.yes = yesLabel;
@@ -40001,15 +40904,17 @@ ${t}` : `"use strict"; return (${t});`;
40001
40904
  return createModal(promptText, yesCallback, extraContent, false, "", "Confirm");
40002
40905
  }
40003
40906
  function tell(promptText, ...content) {
40004
- const box = bevelBox("div", ["display:flex", "flex-direction:column", "gap:12px"]);
40907
+ const theme2 = resolveTheme();
40908
+ const box = bevelBox("div", ["display:flex", "flex-direction:column", "gap:12px"], { theme: theme2, parts: ["dialog.content"] });
40005
40909
  for (const c2 of content) {
40006
- const paragraph = bevelText("div", [`font:14.5px/1.55 ${FONT_SANS}`, `color:${TOKENS2["ink-2"]}`, "overflow-wrap:anywhere"]);
40910
+ const paragraph = bevelText("div", [`font:14.5px/1.55 ${FONT_SANS}`, `color:${TOKENS2["ink-2"]}`, "overflow-wrap:anywhere"], void 0, { theme: theme2 });
40007
40911
  paragraph.innerHTML = c2;
40008
40912
  box.append(paragraph);
40009
40913
  }
40010
40914
  modal_default.title = promptText;
40011
40915
  modal_default.html = content.length ? box : "";
40012
40916
  modal_default.width = "470px";
40917
+ modal_default[SURFACE_THEME] = theme2;
40013
40918
  modal_default.closeHtml = "x";
40014
40919
  modal_default.yes = "OK";
40015
40920
  const promise = new Promise((resolve, reject) => {
@@ -40027,7 +40932,8 @@ ${t}` : `"use strict"; return (${t});`;
40027
40932
  return promise;
40028
40933
  }
40029
40934
  function snippet(title, content, extraContent = "") {
40030
- const box = bevelBox("div", ["display:block"]);
40935
+ const theme2 = resolveTheme();
40936
+ const box = bevelBox("div", ["display:block"], { theme: theme2, parts: ["dialog.content"] });
40031
40937
  const well = bevelBox("div", [
40032
40938
  "display:block",
40033
40939
  "max-width:100%",
@@ -40036,23 +40942,27 @@ ${t}` : `"use strict"; return (${t});`;
40036
40942
  "padding:14px 16px",
40037
40943
  `background:${TOKENS2.sunk}`,
40038
40944
  `border:1px solid ${TOKENS2["line-2"]}`
40039
- ]);
40040
- const pre = bevelText("pre", ["display:block", "margin:0", "white-space:nowrap", `font:13px/1.6 ${FONT_MONO}`]);
40945
+ ], { theme: theme2, parts: ["dialog.well"], role: "well" });
40946
+ const pre = bevelText("pre", ["display:block", "margin:0", "white-space:nowrap", `font:13px/1.6 ${FONT_MONO}`], void 0, { theme: theme2, parts: ["dialog.code"], role: "code" });
40041
40947
  pre.innerHTML = content;
40042
40948
  well.append(pre);
40043
- const copy = bevelButton("Copy", {
40949
+ const copy2 = bevelButton("Copy", {
40044
40950
  small: true,
40045
40951
  extra: ["margin:0 0 14px"],
40952
+ theme: theme2,
40953
+ parts: ["dialog.button", "dialog.copyButton"],
40954
+ labelParts: ["dialog.buttonLabel"],
40046
40955
  onClick: () => {
40047
40956
  copy_to_clipboard_default(content);
40048
40957
  toast_default("Copied to clipboard!", "success");
40049
40958
  }
40050
40959
  });
40051
- box.append(well, copy);
40960
+ box.append(well, copy2);
40052
40961
  box.insertAdjacentHTML("beforeend", extraContent || "");
40053
40962
  modal_default.title = title;
40054
40963
  modal_default.html = box;
40055
40964
  modal_default.width = "540px";
40965
+ modal_default[SURFACE_THEME] = theme2;
40056
40966
  modal_default.closeHtml = "x";
40057
40967
  modal_default.yes = "";
40058
40968
  const promise = new Promise((resolve) => {
@@ -40067,7 +40977,7 @@ ${t}` : `"use strict"; return (${t});`;
40067
40977
  modal_default.open();
40068
40978
  return promise;
40069
40979
  }
40070
- function init20() {
40980
+ function init21() {
40071
40981
  dom_ready_default(() => {
40072
40982
  const micromodalParentElem = document.querySelector("[data-clay-modal]");
40073
40983
  if (micromodalParentElem) {
@@ -40084,7 +40994,8 @@ ${t}` : `"use strict"; return (${t});`;
40084
40994
  init_copy_to_clipboard();
40085
40995
  init_bevel_controls();
40086
40996
  init_bevel();
40087
- init20();
40997
+ init_theme();
40998
+ init21();
40088
40999
  }
40089
41000
  });
40090
41001
 
@@ -40106,6 +41017,8 @@ ${t}` : `"use strict"; return (${t});`;
40106
41017
  init_modal();
40107
41018
  init_dialogs();
40108
41019
  init_save_feedback();
41020
+ init_theme();
41021
+ initTheme();
40109
41022
  clay3 = window.clay = window.clay || {};
40110
41023
  clay3.toast = toast_default;
40111
41024
  clay3.toastPersistent = toastPersistent;
@@ -40206,7 +41119,7 @@ ${t}` : `"use strict"; return (${t});`;
40206
41119
  var options_exports = {};
40207
41120
  __export(options_exports, {
40208
41121
  default: () => options_default,
40209
- init: () => init21,
41122
+ init: () => init22,
40210
41123
  parseOptionAttribute: () => parseOptionAttribute
40211
41124
  });
40212
41125
  function parseOptionAttribute(attrName, attrValue) {
@@ -40223,7 +41136,7 @@ ${t}` : `"use strict"; return (${t});`;
40223
41136
  const values = rawValue.split("|");
40224
41137
  return { name, rawValue, values, kind: match.kind, prefix: match.prefix, negated: match.kind === "show-not" };
40225
41138
  }
40226
- function init21() {
41139
+ function init22() {
40227
41140
  optionVisibility.start();
40228
41141
  }
40229
41142
  var STYLE_NAME, OPTION_PREFIXES, optionVisibility, options_default;
@@ -40357,12 +41270,12 @@ ${t}` : `"use strict"; return (${t});`;
40357
41270
  };
40358
41271
  window.clay.optionVisibility = optionVisibility;
40359
41272
  options_default = optionVisibility;
40360
- init21();
41273
+ init22();
40361
41274
  }
40362
41275
  });
40363
41276
 
40364
41277
  // src/events/onclickaway.js
40365
- function init22() {
41278
+ function init23() {
40366
41279
  document.addEventListener("click", function(event) {
40367
41280
  const elementsWithOnClickAway = document.querySelectorAll("[onclickaway]");
40368
41281
  elementsWithOnClickAway.forEach((element) => {
@@ -40383,7 +41296,7 @@ ${t}` : `"use strict"; return (${t});`;
40383
41296
  });
40384
41297
 
40385
41298
  // src/events/onclickchildren.js
40386
- function init23() {
41299
+ function init24() {
40387
41300
  document.addEventListener("click", function(event) {
40388
41301
  const parent = event.target.closest("[onclickchildren]");
40389
41302
  if (parent && event.target !== parent) {
@@ -40398,7 +41311,7 @@ ${t}` : `"use strict"; return (${t});`;
40398
41311
  });
40399
41312
 
40400
41313
  // src/events/onclone.js
40401
- function init24() {
41314
+ function init25() {
40402
41315
  if (Node.prototype.__hyperclayOnclone) {
40403
41316
  return;
40404
41317
  }
@@ -40451,7 +41364,7 @@ ${t}` : `"use strict"; return (${t});`;
40451
41364
  console.error("Error in onmutation execution:", error);
40452
41365
  }
40453
41366
  }
40454
- function init25() {
41367
+ function init26() {
40455
41368
  mutation_default.onAnyChange({ require: "observed" }, (changes2) => {
40456
41369
  const toFire = /* @__PURE__ */ new Set();
40457
41370
  for (const change of changes2) {
@@ -40471,7 +41384,7 @@ ${t}` : `"use strict"; return (${t});`;
40471
41384
  });
40472
41385
 
40473
41386
  // src/events/onpagemutation.js
40474
- function init26() {
41387
+ function init27() {
40475
41388
  const executeGlobalMutation = async (element) => {
40476
41389
  try {
40477
41390
  const code = element.getAttribute("onglobalmutation") || element.getAttribute("onpagemutation");
@@ -40511,7 +41424,7 @@ ${t}` : `"use strict"; return (${t});`;
40511
41424
  });
40512
41425
 
40513
41426
  // src/events/onrender.js
40514
- function init27() {
41427
+ function init28() {
40515
41428
  const executeRender = async (element) => {
40516
41429
  try {
40517
41430
  const code = element.getAttribute("onrender");
@@ -40544,15 +41457,15 @@ ${t}` : `"use strict"; return (${t});`;
40544
41457
  var events_exports = {};
40545
41458
  __export(events_exports, {
40546
41459
  default: () => events_default,
40547
- init: () => init28
41460
+ init: () => init29
40548
41461
  });
40549
- function init28() {
40550
- init22();
41462
+ function init29() {
40551
41463
  init23();
40552
41464
  init24();
40553
41465
  init25();
40554
41466
  init26();
40555
41467
  init27();
41468
+ init28();
40556
41469
  }
40557
41470
  var events_default;
40558
41471
  var init_events = __esm({
@@ -40563,8 +41476,8 @@ ${t}` : `"use strict"; return (${t});`;
40563
41476
  init_onmutation();
40564
41477
  init_onpagemutation();
40565
41478
  init_onrender();
40566
- init28();
40567
- events_default = init28;
41479
+ init29();
41480
+ events_default = init29;
40568
41481
  }
40569
41482
  });
40570
41483
 
@@ -40613,6 +41526,8 @@ ${t}` : `"use strict"; return (${t});`;
40613
41526
  // injects its own editing toggle there and clayjs's edit-mode signal is a
40614
41527
  // superset of the cms's, so the plugin is present exactly when it can be used.
40615
41528
  upload: { path: "plugins/upload.js", editOnly: true, default: false },
41529
+ // Who is editing and who wrote what. Not editOnly: a reader still sees names.
41530
+ people: { path: "plugins/people.js", editOnly: false, default: false },
40616
41531
  wire: { path: "plugins/wire.js", editOnly: false, default: false },
40617
41532
  // Edit mode only: the AI comment box is an editing gesture. Opt in with
40618
41533
  // `plugins=ai-edit`; it stays dormant on any host that does not list a ready
@@ -40656,12 +41571,13 @@ ${t}` : `"use strict"; return (${t});`;
40656
41571
  "plugins/cms.js": () => Promise.resolve().then(() => (init_cms2(), cms_exports)),
40657
41572
  "plugins/quickcrop.js": () => Promise.resolve().then(() => (init_quickcrop2(), quickcrop_exports)),
40658
41573
  "plugins/upload.js": () => Promise.resolve().then(() => (init_upload(), upload_exports)),
41574
+ "plugins/people.js": () => Promise.resolve().then(() => (init_people(), people_exports)),
40659
41575
  "plugins/wire.js": () => Promise.resolve().then(() => (init_wire(), wire_exports)),
40660
41576
  "plugins/ai-edit.js": () => Promise.resolve().then(() => (init_ai_edit(), ai_edit_exports)),
40661
41577
  "plugins/demo.js": () => Promise.resolve().then(() => (init_demo(), demo_exports)),
40662
41578
  "plugins/source.js": () => Promise.resolve().then(() => (init_source(), source_exports))
40663
41579
  };
40664
- var PLUGIN_ORDER = ["richclay", "indicator", "sortable", "undo", "quickcrop", "upload", "cms", "sync", "wire", "ai-edit", "demo", "source"];
41580
+ var PLUGIN_ORDER = ["richclay", "indicator", "sortable", "undo", "quickcrop", "upload", "people", "cms", "sync", "wire", "ai-edit", "demo", "source"];
40665
41581
  var IMPLIES = { cms: ["quickcrop", "upload"], "ai-edit": ["wire"] };
40666
41582
  function parseCsv(params, key2, enabled, apply) {
40667
41583
  const raw = params.get(key2);
@@ -40706,13 +41622,15 @@ ${t}` : `"use strict"; return (${t});`;
40706
41622
 
40707
41623
  // src/loader.js
40708
41624
  init_dom_ready();
41625
+ init_theme();
40709
41626
  function domReady() {
40710
41627
  return new Promise((resolve) => dom_ready_default(resolve));
40711
41628
  }
40712
41629
  async function boot(base, params, readyResolve) {
41630
+ initTheme();
40713
41631
  await domReady();
40714
41632
  const editMode = await MODULES["core/is-edit-mode.js"]();
40715
- const { isEditMode: isEditMode2, isOwner: isOwner2 } = editMode;
41633
+ const { isEditMode: isEditMode2, isOwner: isOwner2, siteVisibility: siteVisibility2 } = editMode;
40716
41634
  window.__hyperclayEditMode = isEditMode2;
40717
41635
  const regionPolicy = await MODULES["lib/region-policy.js"]();
40718
41636
  const plan = resolveModules(params, isEditMode2);
@@ -40720,7 +41638,7 @@ ${t}` : `"use strict"; return (${t});`;
40720
41638
  for (const path of plan.core) {
40721
41639
  loaded[path] = await MODULES[path]();
40722
41640
  }
40723
- assembleCore(loaded, { isEditMode: isEditMode2, isOwner: isOwner2 }, regionPolicy);
41641
+ assembleCore(loaded, { isEditMode: isEditMode2, isOwner: isOwner2, siteVisibility: siteVisibility2 }, regionPolicy);
40724
41642
  for (const path of plan.plugins) {
40725
41643
  let mod;
40726
41644
  try {
@@ -40737,7 +41655,7 @@ ${t}` : `"use strict"; return (${t});`;
40737
41655
  readyResolve(window.clay);
40738
41656
  document.dispatchEvent(new CustomEvent("clay:ready", { detail: { clay: window.clay } }));
40739
41657
  }
40740
- function assembleCore(loaded, { isEditMode: isEditMode2, isOwner: isOwner2 }, regionPolicy) {
41658
+ function assembleCore(loaded, { isEditMode: isEditMode2, isOwner: isOwner2, siteVisibility: siteVisibility2 }, regionPolicy) {
40741
41659
  const clay4 = window.clay;
40742
41660
  const mutation = loaded["lib/mutation.js"];
40743
41661
  const editModeMod = loaded["core/edit-mode.js"];
@@ -40745,6 +41663,7 @@ ${t}` : `"use strict"; return (${t});`;
40745
41663
  toggleEditMode: editModeMod.toggleEditMode,
40746
41664
  isEditMode: isEditMode2,
40747
41665
  isOwner: isOwner2,
41666
+ siteVisibility: siteVisibility2,
40748
41667
  Mutation: mutation.default,
40749
41668
  region: regionPolicy.windowRegionShape
40750
41669
  });
@@ -40785,6 +41704,10 @@ ${t}` : `"use strict"; return (${t});`;
40785
41704
  clay4.cms = mod.cms || mod.default;
40786
41705
  } else if (path === "plugins/upload.js") {
40787
41706
  clay4.upload = mod.upload || mod.default;
41707
+ } else if (path === "plugins/people.js") {
41708
+ clay4.people = mod.people;
41709
+ clay4.author = mod.author;
41710
+ Object.defineProperty(clay4, "me", { get: mod.currentMe, configurable: true, enumerable: true });
40788
41711
  } else if (path === "plugins/wire.js") {
40789
41712
  clay4.wire = mod.wire || mod.default;
40790
41713
  } else if (path === "plugins/ai-edit.js") {