@panphora/clayjs 1.12.0 → 1.12.2

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.12.0 standalone build: every clayjs module in one file. https://clayjs.com/offline
1
+ /* clayjs 1.12.2 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;
@@ -102,29 +102,29 @@
102
102
  });
103
103
 
104
104
  // src/lib/region-capabilities.js
105
- function hasPolicyToken(element, token) {
105
+ function hasPolicyToken(element, token2) {
106
106
  if (!element || element.nodeType !== 1) return false;
107
107
  const clay5 = element.getAttribute?.("clay");
108
- return !!(clay5 && clay5.split(/\s+/).includes(token) || element.hasAttribute?.(token));
108
+ return !!(clay5 && clay5.split(/\s+/).includes(token2) || element.hasAttribute?.(token2));
109
109
  }
110
- function expandsTo(element, token) {
111
- if (hasPolicyToken(element, token)) return true;
110
+ function expandsTo(element, token2) {
111
+ if (hasPolicyToken(element, token2)) return true;
112
112
  for (const [bundle, members] of Object.entries(BUNDLES)) {
113
- if (members.includes(token) && hasPolicyToken(element, bundle)) return true;
113
+ if (members.includes(token2) && hasPolicyToken(element, bundle)) return true;
114
114
  }
115
115
  return false;
116
116
  }
117
117
  function capabilitySelector(capability) {
118
118
  const definition = CAPABILITIES[capability];
119
119
  if (!definition) throw new Error(`Unknown region capability: ${capability}`);
120
- return [...definition.tokens, ...definition.bundles].flatMap((token) => [`[clay~="${token}"]`, `[${token}]`]).join(", ");
120
+ return [...definition.tokens, ...definition.bundles].flatMap((token2) => [`[clay~="${token2}"]`, `[${token2}]`]).join(", ");
121
121
  }
122
122
  function closestWithCapability(node, capability) {
123
123
  let element = node && node.nodeType === 1 ? node : node?.parentElement;
124
124
  while (element && element.nodeType === 1) {
125
125
  const definition = CAPABILITIES[capability];
126
126
  if (!definition) throw new Error(`Unknown region capability: ${capability}`);
127
- if (definition.tokens.some((token) => expandsTo(element, token)) || definition.bundles.some((bundle) => hasPolicyToken(element, bundle))) return element;
127
+ if (definition.tokens.some((token2) => expandsTo(element, token2)) || definition.bundles.some((bundle) => hasPolicyToken(element, bundle))) return element;
128
128
  element = element.parentElement;
129
129
  }
130
130
  return null;
@@ -569,10 +569,10 @@
569
569
  strictestPolicy: () => strictestPolicy,
570
570
  windowRegionShape: () => windowRegionShape
571
571
  });
572
- function addRegionToken(el5, token) {
572
+ function addRegionToken(el5, token2) {
573
573
  const tokens = new Set((el5.getAttribute("clay") || "").trim().split(/\s+/).filter(Boolean));
574
- if (tokens.has(token)) return;
575
- tokens.add(token);
574
+ if (tokens.has(token2)) return;
575
+ tokens.add(token2);
576
576
  el5.setAttribute("clay", Array.from(tokens).join(" "));
577
577
  }
578
578
  function isSnapshotRemoved(node) {
@@ -922,14 +922,14 @@
922
922
  }
923
923
  this._log("Resumed", this._pauseDepth);
924
924
  },
925
- _log(message, data = null, type = "log") {
925
+ _log(message2, data = null, type = "log") {
926
926
  if (!this.debug) return;
927
927
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
928
928
  const prefix = `[Mutation ${timestamp}]`;
929
929
  if (data) {
930
- console[type](prefix, message, data);
930
+ console[type](prefix, message2, data);
931
931
  } else {
932
- console[type](prefix, message);
932
+ console[type](prefix, message2);
933
933
  }
934
934
  },
935
935
  _notify(type, changes2, onlyNonPausable = false) {
@@ -1526,7 +1526,7 @@
1526
1526
  }
1527
1527
  function hasOwnCapability(element, capability) {
1528
1528
  const definition = CAPABILITIES[capability];
1529
- return definition.tokens.some((token) => expandsTo(element, token)) || definition.bundles.some((bundle) => hasPolicyToken(element, bundle));
1529
+ return definition.tokens.some((token2) => expandsTo(element, token2)) || definition.bundles.some((bundle) => hasPolicyToken(element, bundle));
1530
1530
  }
1531
1531
  function pairTree(source3, copy2, maps, drop) {
1532
1532
  if (source3.nodeType === 1 && drop(source3)) {
@@ -2875,8 +2875,8 @@
2875
2875
  }
2876
2876
  async function fetchMeta() {
2877
2877
  if (!/^https?:$/.test(window.location.protocol)) return settle("none");
2878
- const token = saveToken();
2879
- const path = token ? `${META_PATH}/${token}` : META_PATH;
2878
+ const token2 = saveToken();
2879
+ const path = token2 ? `${META_PATH}/${token2}` : META_PATH;
2880
2880
  const controller = new AbortController();
2881
2881
  const timeoutId = setTimeout(() => controller.abort(), META_TIMEOUT_MS);
2882
2882
  try {
@@ -2885,7 +2885,7 @@
2885
2885
  // Same rule as the save lane. The token IS the credential, and a host that
2886
2886
  // mints per-document tokens must never send Access-Control-Allow-Credentials
2887
2887
  // back, so asking for cookies gets the answer blocked before it is read.
2888
- credentials: token ? "omit" : "same-origin",
2888
+ credentials: token2 ? "omit" : "same-origin",
2889
2889
  headers: { "Document-URL": window.location.href },
2890
2890
  cache: "no-store",
2891
2891
  // A redirect is not an answer (§5), and following one would send the
@@ -3162,11 +3162,11 @@
3162
3162
  return { ok: false, msg, msgType: "skipped", code: null, etag: null };
3163
3163
  }
3164
3164
  function buildSaveRequest(content, userDriven, signal, saveId, etag, keepalive = false) {
3165
- const token = saveToken();
3166
- const path = token ? `${SAVE_PATH}/${token}` : SAVE_PATH;
3165
+ const token2 = saveToken();
3166
+ const path = token2 ? `${SAVE_PATH}/${token2}` : SAVE_PATH;
3167
3167
  const options = {
3168
3168
  method: "POST",
3169
- credentials: token ? "omit" : "same-origin",
3169
+ credentials: token2 ? "omit" : "same-origin",
3170
3170
  signal,
3171
3171
  headers: {
3172
3172
  "Document-URL": window.location.href,
@@ -3204,11 +3204,11 @@
3204
3204
  return { ok: status >= 200 && status < 300, status, statusText: status === 200 ? "OK" : "Insufficient Storage", text: () => Promise.resolve(text) };
3205
3205
  }
3206
3206
  function sendToDraftStore(content) {
3207
- const send3 = draftTransport();
3208
- if (!send3) return Promise.resolve(draftResponse(503, { msg: "Saving in this browser is not ready yet", msgType: "error", code: "draft-not-ready" }));
3207
+ const send4 = draftTransport();
3208
+ if (!send4) return Promise.resolve(draftResponse(503, { msg: "Saving in this browser is not ready yet", msgType: "error", code: "draft-not-ready" }));
3209
3209
  return Promise.resolve().then(() => {
3210
3210
  try {
3211
- send3(content);
3211
+ send4(content);
3212
3212
  return draftResponse(200, { msg: "Saved in this browser", msgType: "success" });
3213
3213
  } catch (error) {
3214
3214
  return draftResponse(507, { msg: "Couldn't save in this browser. Storage is full or turned off.", msgType: "error", code: error.code || "draft-storage" });
@@ -3485,12 +3485,12 @@
3485
3485
  changes++;
3486
3486
  scheduleAnnounce();
3487
3487
  }
3488
- function gateClearIfUnchanged(token) {
3489
- if (!token) return;
3490
- if (changes === token.gen) {
3491
- clearedAt = token.gen;
3488
+ function gateClearIfUnchanged(token2) {
3489
+ if (!token2) return;
3490
+ if (changes === token2.gen) {
3491
+ clearedAt = token2.gen;
3492
3492
  }
3493
- for (const [el5, sig] of token.probe) {
3493
+ for (const [el5, sig] of token2.probe) {
3494
3494
  if (controlSignature(el5) === sig) probeCache.set(el5, sig);
3495
3495
  }
3496
3496
  scheduleAnnounce();
@@ -4069,8 +4069,8 @@
4069
4069
  for (const name of events) document.addEventListener(name, onSettle);
4070
4070
  });
4071
4071
  }
4072
- function flushError(message, state2) {
4073
- return Object.assign(new Error(message), { state: state2 });
4072
+ function flushError(message2, state2) {
4073
+ return Object.assign(new Error(message2), { state: state2 });
4074
4074
  }
4075
4075
  function nextEvent(name, deadline) {
4076
4076
  return new Promise((resolve, reject) => {
@@ -4110,9 +4110,9 @@
4110
4110
  for (let attempt = 0; attempt < 4; attempt++) {
4111
4111
  if (isSaveInProgress()) await settled(deadline);
4112
4112
  if (isSaveConflicted()) throw flushError("This document has a conflict to resolve.", "conflict");
4113
- const token = gateCaptureToken();
4113
+ const token2 = gateCaptureToken();
4114
4114
  if (!bytesUnsaved()) {
4115
- gateClearIfUnchanged(token);
4115
+ gateClearIfUnchanged(token2);
4116
4116
  if (hasUnsavedState()) throw flushError("This document has unsaved work a save cannot write.", "blocked");
4117
4117
  return { state: saved2 ? "saved" : "clean", etag: etag ?? lastSeenEtag() };
4118
4118
  }
@@ -4354,8 +4354,8 @@
4354
4354
  setPageDataLiveSync: () => setPageDataLiveSync,
4355
4355
  writeData: () => writeData
4356
4356
  });
4357
- function refusal(code, message, details) {
4358
- const error = new Error(message);
4357
+ function refusal(code, message2, details) {
4358
+ const error = new Error(message2);
4359
4359
  error.code = code;
4360
4360
  error.details = details;
4361
4361
  return error;
@@ -4371,8 +4371,8 @@
4371
4371
  body = await responseBody(response) || {};
4372
4372
  } catch {
4373
4373
  }
4374
- const message = body.message || body.error || response.statusText || `Request failed (${response.status})`;
4375
- const error = new Error(message);
4374
+ const message2 = body.message || body.error || response.statusText || `Request failed (${response.status})`;
4375
+ const error = new Error(message2);
4376
4376
  error.status = response.status;
4377
4377
  error.error = body.error;
4378
4378
  error.details = body.details;
@@ -4468,7 +4468,7 @@
4468
4468
  }
4469
4469
  function createPageDataApi({
4470
4470
  supports = hostSupports,
4471
- token = saveToken,
4471
+ token: token2 = saveToken,
4472
4472
  fetchImpl = (...args) => fetch(...args),
4473
4473
  getLocation = () => window.location,
4474
4474
  eventTarget = document,
@@ -4570,7 +4570,7 @@
4570
4570
  "Content-Type": "application/json",
4571
4571
  "If-Match": match
4572
4572
  };
4573
- const currentToken = token();
4573
+ const currentToken = token2();
4574
4574
  if (currentToken) headers["Save-Token"] = currentToken;
4575
4575
  const body = JSON.stringify(values);
4576
4576
  waiter = syncWaiter(eventTarget, syncTimeoutMs);
@@ -13610,8 +13610,8 @@
13610
13610
  RETRY_MS = 2e3;
13611
13611
  EVENTS = ["focusin", "focusout", "selectionchange", "clay:sync-applied", "clay:workspace-background", "clay:workspace-foreground"];
13612
13612
  EditingSender = class {
13613
- constructor({ send: send3, now: now3 = () => Date.now() }) {
13614
- this.send = send3;
13613
+ constructor({ send: send4, now: now3 = () => Date.now() }) {
13614
+ this.send = send4;
13615
13615
  this.now = now3;
13616
13616
  this.others = false;
13617
13617
  this.background = false;
@@ -14149,13 +14149,13 @@
14149
14149
  get unresolvedConflicts() {
14150
14150
  return conflicts.list().filter((c2) => c2.kind !== "apply-incomplete");
14151
14151
  }
14152
- _log(message, data = null) {
14152
+ _log(message2, data = null) {
14153
14153
  if (!this.debug) return;
14154
14154
  const prefix = `[LiveSync ${(/* @__PURE__ */ new Date()).toISOString()}]`;
14155
14155
  if (data !== null) {
14156
- console.log(prefix, message, data);
14156
+ console.log(prefix, message2, data);
14157
14157
  } else {
14158
- console.log(prefix, message);
14158
+ console.log(prefix, message2);
14159
14159
  }
14160
14160
  }
14161
14161
  /**
@@ -15085,7 +15085,7 @@
15085
15085
  ...extra
15086
15086
  };
15087
15087
  const dirty = this.lane === "live" && pageMaybeDirty();
15088
- const token = gateCaptureToken();
15088
+ const token2 = gateCaptureToken();
15089
15089
  let pending4;
15090
15090
  let localRoot = null;
15091
15091
  let applyId = null;
@@ -15150,7 +15150,7 @@
15150
15150
  }
15151
15151
  throw err;
15152
15152
  }
15153
- const typedDuringWait = gateCaptureToken().gen !== token.gen;
15153
+ const typedDuringWait = gateCaptureToken().gen !== token2.gen;
15154
15154
  let conflictIds;
15155
15155
  try {
15156
15156
  lineageCapture.prepareComplete(report2, conflicts.list());
@@ -15159,7 +15159,7 @@
15159
15159
  lineageCapture.finish(conflicts.list());
15160
15160
  }
15161
15161
  if (dirty && !report2.localDiverged) {
15162
- gateClearIfUnchanged(token);
15162
+ gateClearIfUnchanged(token2);
15163
15163
  }
15164
15164
  const mapIds = new Set(isIdMap(identityMap) ? Object.values(identityMap) : []);
15165
15165
  for (const [el5, id2] of report2.identities) {
@@ -15193,7 +15193,7 @@
15193
15193
  this._log("applyUpdate - pausing mutations and morphing");
15194
15194
  this.isPaused = true;
15195
15195
  const scrollX = window.scrollX;
15196
- const scrollY = window.scrollY;
15196
+ const scrollY2 = window.scrollY;
15197
15197
  mutation_default.pause();
15198
15198
  pauseGate();
15199
15199
  let diverged = false;
@@ -15231,7 +15231,7 @@
15231
15231
  });
15232
15232
  diverged = report2.localDiverged;
15233
15233
  typedDuringWait = typed;
15234
- window.scrollTo(scrollX, scrollY);
15234
+ window.scrollTo(scrollX, scrollY2);
15235
15235
  this._setPeerBase(html, identityMap, ticket);
15236
15236
  this._applyGen++;
15237
15237
  if (!(this._acceptedSaveTicket > ticket)) {
@@ -15282,7 +15282,7 @@
15282
15282
  this._log(`applyExternal - external disk change (seq=${seq})`);
15283
15283
  this.isPaused = true;
15284
15284
  const scrollX = window.scrollX;
15285
- const scrollY = window.scrollY;
15285
+ const scrollY2 = window.scrollY;
15286
15286
  mutation_default.pause();
15287
15287
  pauseGate();
15288
15288
  let diverged = false;
@@ -15325,7 +15325,7 @@
15325
15325
  diverged = report2.localDiverged;
15326
15326
  typedDuringWait = typed;
15327
15327
  this._setDiskBase(html, ticket);
15328
- window.scrollTo(scrollX, scrollY);
15328
+ window.scrollTo(scrollX, scrollY2);
15329
15329
  if (!(this._acceptedSaveTicket > ticket)) {
15330
15330
  if (typeof etag === "string" && etag) {
15331
15331
  recordEtag(etag);
@@ -16477,12 +16477,12 @@
16477
16477
  function drawBar(m2) {
16478
16478
  const n2 = m2 === "failed" ? failures().length : losses().length;
16479
16479
  const bar2 = bevelSurface("div", ["display:flex", "flex-wrap:wrap", "align-items:center", "gap:10px 12px", "padding:8px 8px 8px 14px", "width:max-content", "max-width:calc(100vw - 24px)"]);
16480
- const message = barMessage(m2, n2);
16481
- if (message !== lastAnnounced) {
16480
+ const message2 = barMessage(m2, n2);
16481
+ if (message2 !== lastAnnounced) {
16482
16482
  bar2.setAttribute("role", m2 === "refused" ? "alert" : "status");
16483
- lastAnnounced = message;
16483
+ lastAnnounced = message2;
16484
16484
  }
16485
- bar2.append(dot(), bevelText("span", [], message), key(bevelButton("Review", { onClick: openPanel }), "review"));
16485
+ bar2.append(dot(), bevelText("span", [], message2), key(bevelButton("Review", { onClick: openPanel }), "review"));
16486
16486
  return bar2;
16487
16487
  }
16488
16488
  function action(label, name, { variant = "default", onClick: onClick2, push = false } = {}) {
@@ -16806,8 +16806,8 @@
16806
16806
  return;
16807
16807
  }
16808
16808
  if (Date.now() - armedAt < DOUBLE_PRESS_MS) return;
16809
- const token = armed;
16810
- const current4 = () => armed === token && token.refusalGeneration === refusalGeneration && token.ledgerRevision === ledgerRevision && token.dirtyGeneration === gateCaptureToken().gen;
16809
+ const token2 = armed;
16810
+ const current4 = () => armed === token2 && token2.refusalGeneration === refusalGeneration && token2.ledgerRevision === ledgerRevision && token2.dirtyGeneration === gateCaptureToken().gen;
16811
16811
  if (!reloadAfterDiscard({ isCurrent: current4, reload: () => window.location.reload() })) {
16812
16812
  disarm();
16813
16813
  armLost = true;
@@ -22888,8 +22888,8 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
22888
22888
  const retired = /* @__PURE__ */ new Set();
22889
22889
  const kept = /* @__PURE__ */ new Map();
22890
22890
  let mirror = { epoch: null, seq: 0, sequencer: null, closedBy: null, stale: false, seen: /* @__PURE__ */ new Map(), stores: /* @__PURE__ */ new Map() };
22891
- function post(message) {
22892
- channel?.postMessage({ v: VERSION, ...message });
22891
+ function post2(message2) {
22892
+ channel?.postMessage({ v: VERSION, ...message2 });
22893
22893
  }
22894
22894
  function pushLog(m2, entry) {
22895
22895
  entry.bytes = encoder2.encode(JSON.stringify(entry.ops)).length;
@@ -22981,7 +22981,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
22981
22981
  }
22982
22982
  function announce() {
22983
22983
  const state2 = roomState();
22984
- post({ type: "data-state", state: state2 });
22984
+ post2({ type: "data-state", state: state2 });
22985
22985
  handlers2.state(state2);
22986
22986
  }
22987
22987
  function renewEpoch() {
@@ -23040,7 +23040,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
23040
23040
  }
23041
23041
  function closeRoom() {
23042
23042
  if (!room) return;
23043
- post({ type: "closed", from: clientId, state: roomState() });
23043
+ post2({ type: "closed", from: clientId, state: roomState() });
23044
23044
  room = null;
23045
23045
  releaseLock?.();
23046
23046
  releaseLock = null;
@@ -23100,7 +23100,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
23100
23100
  room.lastN.set(nKey, { n: frame.n, seq: room.seq });
23101
23101
  const data = { epoch: room.epoch, seq: room.seq, name: frame.name, ops, from, n: frame.n };
23102
23102
  advance(data);
23103
- post({ type: "data", data });
23103
+ post2({ type: "data", data });
23104
23104
  handlers2.frame(cloneJson(data));
23105
23105
  return { status: 200, body: { seq: room.seq, epoch: room.epoch }, retryAfter: 0 };
23106
23106
  }
@@ -23186,7 +23186,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
23186
23186
  if (changed2) announce();
23187
23187
  else tellLanded(moved);
23188
23188
  }
23189
- function request(message) {
23189
+ function request(message2) {
23190
23190
  return new Promise((resolve) => {
23191
23191
  const id2 = ++rid;
23192
23192
  const timer = setTimeout(() => {
@@ -23194,14 +23194,14 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
23194
23194
  ownPosts.delete(id2);
23195
23195
  resolve(null);
23196
23196
  }, REQUEST_MS);
23197
- if (message.type === "post") ownPosts.set(id2, mirror.sequencer);
23197
+ if (message2.type === "post") ownPosts.set(id2, mirror.sequencer);
23198
23198
  waiting.set(id2, (result2) => {
23199
23199
  clearTimeout(timer);
23200
23200
  waiting.delete(id2);
23201
23201
  ownPosts.delete(id2);
23202
23202
  resolve(result2);
23203
23203
  });
23204
- post({ ...message, from: clientId, rid: id2 });
23204
+ post2({ ...message2, from: clientId, rid: id2 });
23205
23205
  });
23206
23206
  }
23207
23207
  function onData(data) {
@@ -23236,7 +23236,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
23236
23236
  return;
23237
23237
  }
23238
23238
  if (!room && m2.type === "data-state" && m2.state?.epoch === mirror.epoch && m2.state.writer !== mirror.sequencer && Number.isInteger(m2.state.seq) && m2.state.seq < mirror.seq) {
23239
- post({ type: "forked", epoch: mirror.epoch, to: m2.state.writer });
23239
+ post2({ type: "forked", epoch: mirror.epoch, to: m2.state.writer });
23240
23240
  return;
23241
23241
  }
23242
23242
  if (m2.type === "forked") {
@@ -23254,9 +23254,9 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
23254
23254
  } else if (m2.type === "answer") {
23255
23255
  if (m2.to === clientId) waiting.get(m2.rid)?.(m2.result);
23256
23256
  } else if (room && m2.type === "post") {
23257
- if (m2.epoch === room.epoch) post({ type: "answer", to: m2.from, rid: m2.rid, result: roomApply(m2.from, m2.frame) });
23257
+ if (m2.epoch === room.epoch) post2({ type: "answer", to: m2.from, rid: m2.rid, result: roomApply(m2.from, m2.frame) });
23258
23258
  } else if (room && m2.type === "state-request") {
23259
- post({ type: "answer", to: m2.from, rid: m2.rid, result: roomState() });
23259
+ post2({ type: "answer", to: m2.from, rid: m2.rid, result: roomState() });
23260
23260
  }
23261
23261
  }
23262
23262
  function onPageHide() {
@@ -23317,7 +23317,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
23317
23317
  landed(name, at4) {
23318
23318
  if (stopped || !at4) return;
23319
23319
  if (applyLanded(name, at4)) tellLanded([name]);
23320
- post({ type: "landed", name, at: { epoch: at4.epoch, seq: at4.seq } });
23320
+ post2({ type: "landed", name, at: { epoch: at4.epoch, seq: at4.seq } });
23321
23321
  },
23322
23322
  get isSequencer() {
23323
23323
  return room !== null;
@@ -32069,8 +32069,8 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
32069
32069
  }
32070
32070
  function send(file, { onProgress, signal }) {
32071
32071
  return new Promise((resolve) => {
32072
- const token = saveToken();
32073
- const path = token ? `${UPLOAD_PATH}/${token}` : UPLOAD_PATH;
32072
+ const token2 = saveToken();
32073
+ const path = token2 ? `${UPLOAD_PATH}/${token2}` : UPLOAD_PATH;
32074
32074
  const url = new URL(path, window.location.origin).href;
32075
32075
  const xhr = new XMLHttpRequest();
32076
32076
  let settled2 = false;
@@ -32766,8 +32766,8 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
32766
32766
  return new URL(path, window.location.origin).href;
32767
32767
  }
32768
32768
  function wireToken() {
32769
- const token = saveToken();
32770
- return typeof token === "string" && token !== "" ? token : null;
32769
+ const token2 = saveToken();
32770
+ return typeof token2 === "string" && token2 !== "" ? token2 : null;
32771
32771
  }
32772
32772
  function view2(rec) {
32773
32773
  return {
@@ -32800,12 +32800,12 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
32800
32800
  rec.state = state2;
32801
32801
  emit3(rec);
32802
32802
  }
32803
- function arm(rec, ms2, message, { cancelFirst = false, errorCode = null } = {}) {
32803
+ function arm(rec, ms2, message2, { cancelFirst = false, errorCode = null } = {}) {
32804
32804
  clearTimeout(rec.timer);
32805
32805
  rec.timer = setTimeout(() => {
32806
32806
  rec.timer = null;
32807
32807
  if (cancelFirst) postCancel(rec);
32808
- finish(rec, "error", message, { errorCode, errorSource: "client" });
32808
+ finish(rec, "error", message2, { errorCode, errorSource: "client" });
32809
32809
  }, ms2);
32810
32810
  }
32811
32811
  function disarm2(rec) {
@@ -32821,8 +32821,8 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
32821
32821
  }
32822
32822
  function openStream() {
32823
32823
  if (streamReady) return streamReady;
32824
- const token = wireToken();
32825
- const path = `/_/wire/subscribe?page-url=${encodeURIComponent(window.location.href)}` + (token ? `&token=${encodeURIComponent(token)}` : "");
32824
+ const token2 = wireToken();
32825
+ const path = `/_/wire/subscribe?page-url=${encodeURIComponent(window.location.href)}` + (token2 ? `&token=${encodeURIComponent(token2)}` : "");
32826
32826
  stream = new EventSource(wireURL(path));
32827
32827
  stream.onmessage = (event) => {
32828
32828
  let frame;
@@ -33041,8 +33041,8 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
33041
33041
  "Content-Type": "application/json",
33042
33042
  "Page-URL": window.location.href
33043
33043
  };
33044
- const token = wireToken();
33045
- if (token) headers["Save-Token"] = token;
33044
+ const token2 = wireToken();
33045
+ if (token2) headers["Save-Token"] = token2;
33046
33046
  const response = await fetch(wireURL("/_/wire/send"), {
33047
33047
  method: "POST",
33048
33048
  headers,
@@ -33272,6 +33272,183 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
33272
33272
  }
33273
33273
  });
33274
33274
 
33275
+ // src/dom/form-data.js
33276
+ function getDataFromForm(container) {
33277
+ const formData = {};
33278
+ const processElement = (elem) => {
33279
+ const name = elem.getAttribute("name");
33280
+ const value = elem.value ?? elem.getAttribute("value");
33281
+ if (!name || elem.disabled) return;
33282
+ switch (elem.type) {
33283
+ case "checkbox":
33284
+ if (!formData[name]) {
33285
+ formData[name] = [];
33286
+ }
33287
+ if (elem.checked) {
33288
+ formData[name].push(value);
33289
+ }
33290
+ break;
33291
+ case "radio":
33292
+ if (elem.checked) {
33293
+ formData[name] = value;
33294
+ }
33295
+ break;
33296
+ case "select-multiple":
33297
+ formData[name] = Array.from(elem.selectedOptions, (option) => option.value);
33298
+ break;
33299
+ case "button":
33300
+ case "submit":
33301
+ case "reset":
33302
+ if (name && value) {
33303
+ formData[name] = value;
33304
+ }
33305
+ break;
33306
+ default:
33307
+ formData[name] = value;
33308
+ }
33309
+ };
33310
+ if (container instanceof HTMLFormElement) {
33311
+ Array.from(container.elements).forEach(processElement);
33312
+ } else {
33313
+ if (container.hasAttribute("name")) {
33314
+ processElement(container);
33315
+ }
33316
+ const elements = container.querySelectorAll("[name]");
33317
+ elements.forEach(processElement);
33318
+ }
33319
+ return formData;
33320
+ }
33321
+ var form_data_default;
33322
+ var init_form_data = __esm({
33323
+ "src/dom/form-data.js"() {
33324
+ form_data_default = getDataFromForm;
33325
+ }
33326
+ });
33327
+
33328
+ // src/plugins/message.js
33329
+ var message_exports = {};
33330
+ __export(message_exports, {
33331
+ default: () => message_default,
33332
+ message: () => message,
33333
+ send: () => send3
33334
+ });
33335
+ function fetchToken() {
33336
+ if (!tokenRequest) {
33337
+ tokenRequest = fetch(TOKEN_PATH, { credentials: "omit", cache: "no-store" }).then((res) => res.ok ? res.json() : null).then((body) => {
33338
+ if (body && typeof body.token === "string") {
33339
+ token = body.token;
33340
+ tokenAt = Date.now();
33341
+ }
33342
+ }).catch(() => {
33343
+ }).finally(() => {
33344
+ tokenRequest = null;
33345
+ });
33346
+ }
33347
+ return tokenRequest;
33348
+ }
33349
+ async function takeToken() {
33350
+ if (!token || Date.now() - tokenAt > TOKEN_MAX_AGE_MS) await fetchToken();
33351
+ const taken = token;
33352
+ token = null;
33353
+ return taken;
33354
+ }
33355
+ function signals() {
33356
+ return { webdriver: navigator.webdriver === true, trusted: { ...trusted } };
33357
+ }
33358
+ function isMessageForm(form) {
33359
+ if (!(form instanceof HTMLFormElement)) return false;
33360
+ if (form.hasAttribute("message-form")) return true;
33361
+ const action2 = form.getAttribute("action");
33362
+ if (!action2) return false;
33363
+ try {
33364
+ const url = new URL(action2, window.location.href);
33365
+ return url.origin === window.location.origin && url.pathname === MESSAGE_PATH;
33366
+ } catch {
33367
+ return false;
33368
+ }
33369
+ }
33370
+ async function send3(data = {}) {
33371
+ const result2 = await post(data);
33372
+ if (result2.code === "form-token") return post(data);
33373
+ return result2;
33374
+ }
33375
+ async function post(data) {
33376
+ const formToken = await takeToken();
33377
+ let res;
33378
+ try {
33379
+ res = await fetch(MESSAGE_PATH, {
33380
+ method: "POST",
33381
+ // A signed-in owner testing their own form must not trip the host's CSRF
33382
+ // guard, and the message never needs the visitor's cookies.
33383
+ credentials: "omit",
33384
+ headers: { "Content-Type": "application/json", "Document-URL": window.location.href },
33385
+ body: JSON.stringify({ ...data, formToken, formSignals: signals() })
33386
+ });
33387
+ } catch {
33388
+ return { ok: false, error: "The message couldn't be sent. Check your connection and try again." };
33389
+ }
33390
+ const body = await res.json().catch(() => null);
33391
+ if (res.ok) return body ? { ok: true, error: null } : { ok: false, error: "This page can't send messages." };
33392
+ if (!body) return { ok: false, error: `The message couldn't be sent (${res.status}).` };
33393
+ const error = body.error || body.msg || `The message couldn't be sent (${res.status}).`;
33394
+ return body.code === "form-token" ? { ok: false, error, code: "form-token" } : { ok: false, error };
33395
+ }
33396
+ async function onSubmit(event) {
33397
+ const form = event.target;
33398
+ if (event.defaultPrevented || !isMessageForm(form)) return;
33399
+ event.preventDefault();
33400
+ if (sending.has(form)) return;
33401
+ sending.add(form);
33402
+ try {
33403
+ const result2 = await send3(form_data_default(form));
33404
+ if (result2.ok) form.reset();
33405
+ const outcome2 = new CustomEvent(result2.ok ? "clay:message-sent" : "clay:message-failed", {
33406
+ bubbles: true,
33407
+ cancelable: true,
33408
+ detail: result2
33409
+ });
33410
+ if (!form.dispatchEvent(outcome2)) return;
33411
+ const toast3 = window.clay?.toast;
33412
+ if (!toast3) return;
33413
+ if (result2.ok) toast3(form.getAttribute("message-form") || "Sent", "success");
33414
+ else toast3(result2.error, "error");
33415
+ } finally {
33416
+ sending.delete(form);
33417
+ }
33418
+ }
33419
+ function warmToken(event) {
33420
+ if (token || tokenRequest) return;
33421
+ const form = event.target?.closest?.("form");
33422
+ if (form && isMessageForm(form)) fetchToken();
33423
+ }
33424
+ var MESSAGE_PATH, TOKEN_PATH, TOKEN_MAX_AGE_MS, TRUSTED_EVENTS, trusted, token, tokenAt, tokenRequest, sending, message, message_default;
33425
+ var init_message = __esm({
33426
+ "src/plugins/message.js"() {
33427
+ init_form_data();
33428
+ MESSAGE_PATH = "/_/message";
33429
+ TOKEN_PATH = "/_/form-token";
33430
+ TOKEN_MAX_AGE_MS = 90 * 60 * 1e3;
33431
+ TRUSTED_EVENTS = ["click", "keydown", "pointerdown", "touchstart", "input", "change"];
33432
+ trusted = Object.fromEntries(TRUSTED_EVENTS.map((type) => [type, 0]));
33433
+ token = null;
33434
+ tokenAt = 0;
33435
+ tokenRequest = null;
33436
+ sending = /* @__PURE__ */ new WeakSet();
33437
+ if (typeof document !== "undefined") {
33438
+ for (const type of TRUSTED_EVENTS) {
33439
+ document.addEventListener(type, (event) => {
33440
+ if (event.isTrusted) trusted[type]++;
33441
+ }, { capture: true, passive: true });
33442
+ }
33443
+ document.addEventListener("focusin", warmToken, true);
33444
+ document.addEventListener("input", warmToken, true);
33445
+ document.addEventListener("submit", onSubmit);
33446
+ }
33447
+ message = { send: send3 };
33448
+ message_default = message;
33449
+ }
33450
+ });
33451
+
33275
33452
  // src/plugins/ai-edit.js
33276
33453
  var ai_edit_exports = {};
33277
33454
  __export(ai_edit_exports, {
@@ -33519,11 +33696,11 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
33519
33696
  if (model2) parts.push(`(${model2})`);
33520
33697
  return parts.join(" ");
33521
33698
  }
33522
- function onError(message) {
33699
+ function onError(message2) {
33523
33700
  if (!session) return;
33524
33701
  revertSession();
33525
33702
  showBar("error");
33526
- setBarStatus(message, "warn");
33703
+ setBarStatus(message2, "warn");
33527
33704
  }
33528
33705
  function revertSession() {
33529
33706
  const s2 = session;
@@ -35264,8 +35441,8 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35264
35441
  } };
35265
35442
  return w2;
35266
35443
  }
35267
- function Ge2(e) {
35268
- let t = e && e.ownerDocument ? e.ownerDocument : e, n2 = j(G, t, "api");
35444
+ function Xe2(e) {
35445
+ let t = e && e.ownerDocument ? e.ownerDocument : e, n2 = j(X, t, "api");
35269
35446
  if (!n2) throw new R.NoRulesTag("api");
35270
35447
  return n2.rules;
35271
35448
  }
@@ -35347,29 +35524,29 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35347
35524
  }
35348
35525
  return e instanceof R.WriteRefused ? e.details = e.refusals : e instanceof R.WriteRejected ? e.details = { unknownKeys: e.unknownKeys, unmatched: e.unmatched } : e instanceof R.ShapeMismatch ? e.details = e.mismatches : Array.isArray(e.path) ? e.details = { path: e.path } : typeof e.target == "string" ? e.details = { target: e.target } : e.token !== void 0 ? e.details = { token: e.token } : e.version !== void 0 ? e.details = { version: e.version } : e.details = {}, e;
35349
35526
  }
35350
- function Xe2(e, t) {
35527
+ function Ge2(e, t) {
35351
35528
  let n2 = Te2(e) ? e : document, r = Te2(e) ? t : e;
35352
- return r === void 0 ? C(M(n2), n2, Ge2(n2)) : ne2.bind(n2, r).get();
35529
+ return r === void 0 ? C(M(n2), n2, Xe2(n2)) : ne2.bind(n2, r).get();
35353
35530
  }
35354
35531
  function Ye2(e, t, n2) {
35355
35532
  try {
35356
35533
  if (!Te2(e)) throw new Error("hyper-html-api: applyData(root, data, source?) needs a DOM root as the first argument.");
35357
- let r = n2 === void 0 ? Ge2(e) : ne2.bind(e, n2).rules;
35534
+ let r = n2 === void 0 ? Xe2(e) : ne2.bind(e, n2).rules;
35358
35535
  Wt2(e, r, t);
35359
35536
  } catch (r) {
35360
35537
  throw vt2(r);
35361
35538
  }
35362
35539
  return e;
35363
35540
  }
35364
- var Qe2, et2, X, Y, re2, $2, Ee2, R, k, W2, L2, P2, v, N, B, H, Z2, oe2, se2, Ne2, Pe2, Tt2, _e2, ee2, wt2, De2, me2, Ot2, xt2, Ct2, It2, x, G, ne2, Te2;
35541
+ var Qe2, et2, G, Y, re2, $2, Ee2, R, k, W2, L2, P2, v, N, B, H, Z2, oe2, se2, Ne2, Pe2, Tt2, _e2, ee2, wt2, De2, me2, Ot2, xt2, Ct2, It2, x, X, ne2, Te2;
35365
35542
  var init_hyper_html_api_data_vendor = __esm({
35366
35543
  "src/vendor/hyper-html-api-data.vendor.js"() {
35367
35544
  Qe2 = Object.defineProperty;
35368
35545
  et2 = (e, t) => {
35369
35546
  for (var n2 in t) Qe2(e, n2, { get: t[n2], enumerable: true });
35370
35547
  };
35371
- X = ["textContent", "innerText", "innerHTML", "outerHTML", "value", "checked", "selected", "disabled", "readOnly", "type", "tagName", "nodeName", "nodeType", "nodeValue", "childElementCount", "id", "className", "classList", "baseURI", "offsetWidth", "offsetHeight", "clientWidth", "clientHeight", "scrollWidth", "scrollHeight", "dataset", "currentSrc", "duration", "paused", "title", "documentURI", "contentType"];
35372
- Y = new Set(X);
35548
+ G = ["textContent", "innerText", "innerHTML", "outerHTML", "value", "checked", "selected", "disabled", "readOnly", "type", "tagName", "nodeName", "nodeType", "nodeValue", "childElementCount", "id", "className", "classList", "baseURI", "offsetWidth", "offsetHeight", "clientWidth", "clientHeight", "scrollWidth", "scrollHeight", "dataset", "currentSrc", "duration", "paused", "title", "documentURI", "contentType"];
35549
+ Y = new Set(G);
35373
35550
  re2 = /* @__PURE__ */ new Set(["textContent", "innerText", "innerHTML", "value", "checked", "selected", "disabled", "readOnly", "type", "id", "className", "title"]);
35374
35551
  $2 = /* @__PURE__ */ new Set(["tagName", "nodeName", "nodeType", "nodeValue", "childElementCount", "classList", "baseURI", "documentURI", "contentType", "offsetWidth", "offsetHeight", "clientWidth", "clientHeight", "scrollWidth", "scrollHeight", "currentSrc", "duration", "paused", "dataset"]);
35375
35552
  Ee2 = /* @__PURE__ */ new Set(["checked", "selected", "disabled", "readOnly", "paused"]);
@@ -35500,15 +35677,15 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35500
35677
  }, sameNode(e, t) {
35501
35678
  return e === t;
35502
35679
  } };
35503
- G = x;
35504
- ne2 = { extract: (e, t, n2 = {}) => C(M(e, n2), e, t, n2), apply: (e, t, n2, r = {}) => I2(M(e, { ...r, _write: true }), e, t, n2, r), findRulesIn: (e, t) => j(G, e, t), findRules: (e, t) => Ae2(G, e, t), bind: (e, t, n2 = {}) => {
35505
- let r = Ae2(G, e, t);
35680
+ X = x;
35681
+ ne2 = { extract: (e, t, n2 = {}) => C(M(e, n2), e, t, n2), apply: (e, t, n2, r = {}) => I2(M(e, { ...r, _write: true }), e, t, n2, r), findRulesIn: (e, t) => j(X, e, t), findRules: (e, t) => Ae2(X, e, t), bind: (e, t, n2 = {}) => {
35682
+ let r = Ae2(X, e, t);
35506
35683
  if (!r) {
35507
35684
  let i2 = typeof t == "string" ? `data-rules-name~="${t}"` : "the provided rules object";
35508
35685
  throw new Error(`hyper-html-api: could not resolve rules for ${i2}`);
35509
35686
  }
35510
35687
  return { ...r, get: () => C(M(e, n2), e, r.rules, n2), set: (i2) => I2(M(e, { ...n2, _write: true }), e, r.rules, i2, n2) };
35511
- }, parseStrict: de2, parseRelaxed: z2, ruleAttrIndex: b, splitRule: le2, errors: R, DOM_PROPERTIES: X };
35688
+ }, parseStrict: de2, parseRelaxed: z2, ruleAttrIndex: b, splitRule: le2, errors: R, DOM_PROPERTIES: G };
35512
35689
  Te2 = (e) => !!e && typeof e.nodeType == "number";
35513
35690
  }
35514
35691
  });
@@ -35615,12 +35792,12 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35615
35792
  return { raw, record: parse2(raw) };
35616
35793
  }
35617
35794
  function live2() {
35618
- return Xe2(doc, rules);
35795
+ return Ge2(doc, rules);
35619
35796
  }
35620
35797
  function captured() {
35621
35798
  const getHTML = window.clay?.getHTML;
35622
35799
  if (typeof getHTML !== "function") return live2();
35623
- return Xe2(new DOMParser().parseFromString(getHTML.call(window.clay), "text/html"), rules);
35800
+ return Ge2(new DOMParser().parseFromString(getHTML.call(window.clay), "text/html"), rules);
35624
35801
  }
35625
35802
  function storedKeys() {
35626
35803
  try {
@@ -35695,7 +35872,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35695
35872
  dropped.push(...failing);
35696
35873
  continue;
35697
35874
  }
35698
- const got = Xe2(trial, rules);
35875
+ const got = Ge2(trial, rules);
35699
35876
  const misfits = Object.keys(wanted).filter((name) => !fitsInto(wanted[name], got[name]));
35700
35877
  if (!misfits.length) return { data: wanted, dropped };
35701
35878
  for (const name of misfits) delete wanted[name];
@@ -35773,7 +35950,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35773
35950
  if (unparked != null && park(unparked, { keepSlot: true }) === null) {
35774
35951
  throw storageFailure(doc, new Error("an earlier saved copy could not be kept aside"));
35775
35952
  }
35776
- const mine = Xe2(new DOMParser().parseFromString(html, "text/html"), rules);
35953
+ const mine = Ge2(new DOMParser().parseFromString(html, "text/html"), rules);
35777
35954
  const current4 = getRaw();
35778
35955
  if (fitsInto(mine, base) && (current4 === lastRaw || current4 == null)) {
35779
35956
  setDraftStoreFailed(false);
@@ -35895,11 +36072,6 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35895
36072
  if (path && !/\.(html|htmlclay)$/.test(path)) path += ".html";
35896
36073
  return `/_/download${path}?as=htmlclay${src ? `&src=${encodeURIComponent(src)}` : ""}`;
35897
36074
  }
35898
- function signupTag(slot2, template) {
35899
- const explicit = template?.getAttribute("signup-tag");
35900
- const name = explicit || slot2.replace(/-v\d+$/, "");
35901
- return `app-${name}`.slice(0, 50);
35902
- }
35903
36075
  var SRC;
35904
36076
  var init_source = __esm({
35905
36077
  "src/plugins/visitor-drafts/source.js"() {
@@ -35967,14 +36139,22 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35967
36139
  transition: bottom .25s ease, opacity .2s ease;
35968
36140
  pointer-events: none;
35969
36141
  }
35970
- .hcp[hidden], .hcp[data-hcp-state="dismissed"], .hcp[data-hcp-state="off"], .hcp[data-hcp-intro] { display: none; }
36142
+ .hcp[hidden], .hcp[data-hcp-state="dismissed"], .hcp[data-hcp-state="off"] { display: none; }
35971
36143
  .hcp[data-hcp-typing] { opacity: 0; visibility: hidden; }
35972
36144
  .hcp > * { pointer-events: auto; }
35973
- .hcp svg { display: block; flex: none; }
36145
+ .hcp svg {
36146
+ display: block; flex: none; width: 16px; height: 16px; line-height: 1;
36147
+ fill: none; stroke: none; stroke-width: 0; stroke-linecap: butt; stroke-linejoin: miter;
36148
+ }
36149
+ .hcp .hcp-x svg, .hcp .hcp-actions .hcp-btn svg, .hcp-tab svg, .hcp-file svg { fill: currentColor; }
36150
+ .hcp .hcp-file svg { width: 20px; height: 20px; }
36151
+ .hcp .hcp-tab svg { width: 16px; height: 18px; }
36152
+ .hcp .hcp-dock .hcp-x svg { width: 12px; height: 12px; }
36153
+ .hcp button, .hcp a { border-radius: 0; line-height: 1.5; }
35974
36154
 
35975
36155
  .hcp-btn {
35976
36156
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
35977
- height: 34px; padding: 0 14px;
36157
+ height: 34px; min-height: 0; padding: 0 14px;
35978
36158
  font-family: var(--hcp-sans); font-size: 14px; font-weight: 500; line-height: 1.5;
35979
36159
  white-space: nowrap; text-decoration: none; cursor: pointer; user-select: none;
35980
36160
  color: var(--hcp-ink); background: var(--hcp-face);
@@ -35982,7 +36162,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35982
36162
  border-color: var(--hcp-edge-hi) var(--hcp-edge-lo) var(--hcp-edge-lo) var(--hcp-edge-hi);
35983
36163
  outline: none;
35984
36164
  }
35985
- .hcp-btn:hover { background: color-mix(in srgb, var(--hcp-face), var(--hcp-edge-hi) 45%); color: var(--hcp-ink); }
36165
+ .hcp .hcp-btn:hover { background: color-mix(in srgb, var(--hcp-face), var(--hcp-edge-hi) 45%); color: var(--hcp-ink); }
35986
36166
  .hcp-btn:active { border-color: var(--hcp-edge-lo) var(--hcp-edge-hi) var(--hcp-edge-hi) var(--hcp-edge-lo); }
35987
36167
  .hcp-btn:active > span { translate: 1px 1px; }
35988
36168
  .hcp-btn > span { display: inline-flex; align-items: center; gap: 7px; }
@@ -35993,18 +36173,20 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
35993
36173
  --hcp-edge-lo: color-mix(in srgb, var(--hcp-ink), black 32%);
35994
36174
  color: var(--hcp-ground);
35995
36175
  }
35996
- .hcp-btn--primary:hover { background: color-mix(in srgb, var(--hcp-ink), var(--hcp-ground) 12%); color: var(--hcp-ground); }
36176
+ .hcp .hcp-btn--primary:hover { background: color-mix(in srgb, var(--hcp-ink), var(--hcp-ground) 12%); color: var(--hcp-ground); }
35997
36177
 
35998
36178
  .hcp-dock { display: flex; align-items: stretch; box-shadow: 0 6px 16px -8px rgba(43, 36, 27, .45); }
36179
+ .hcp:not([data-hcp-arrived]) .hcp-dock { animation: hcp-arrive 2.4s cubic-bezier(.16, 1, .3, 1) 3s both; }
36180
+ @keyframes hcp-arrive { from { transform: translateX(calc(100% + 48px)); } to { transform: none; } }
35999
36181
  .hcp-tab { font-size: 13.5px; padding: 0 12px 0 10px; }
36000
36182
  .hcp-tab svg { color: var(--hcp-brass); }
36001
36183
  .hcp-dock .hcp-x {
36002
- width: 26px; height: 34px; display: inline-flex; align-items: center; justify-content: center;
36184
+ width: 26px; height: 34px; min-height: 0; border-radius: 0; display: inline-flex; align-items: center; justify-content: center;
36003
36185
  background: var(--hcp-face); color: var(--hcp-muted); cursor: pointer;
36004
36186
  border: 2px solid; border-left: 0;
36005
36187
  border-color: var(--hcp-edge-hi) var(--hcp-edge-lo) var(--hcp-edge-lo) var(--hcp-edge-hi);
36006
36188
  }
36007
- .hcp-dock .hcp-x:hover { color: var(--hcp-ink); }
36189
+ .hcp .hcp-dock .hcp-x:hover { color: var(--hcp-ink); background: var(--hcp-face); }
36008
36190
  .hcp-tab__label { display: inline; }
36009
36191
  .hcp[data-hcp-state="mini"] .hcp-tab__label,
36010
36192
  .hcp[data-hcp-state="mini"] .hcp-dock .hcp-x { display: none; }
@@ -36022,11 +36204,13 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
36022
36204
  @keyframes hcp-rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
36023
36205
 
36024
36206
  .hcp-card .hcp-x {
36025
- position: absolute; top: 10px; right: 10px; width: 28px; height: 28px;
36207
+ position: absolute; top: 12px; right: 12px; width: 24px; height: 24px; min-height: 0;
36026
36208
  display: inline-flex; align-items: center; justify-content: center;
36209
+ border: 1px solid var(--hcp-line-2); border-radius: 3px; background: var(--hcp-surface);
36027
36210
  color: var(--hcp-muted); cursor: pointer;
36028
36211
  }
36029
- .hcp-card .hcp-x:hover { color: var(--hcp-ink); background: var(--hcp-sunk); }
36212
+ .hcp .hcp-card .hcp-x:hover { color: var(--hcp-ink); border-color: var(--hcp-muted); background: var(--hcp-surface); }
36213
+ .hcp .hcp-card .hcp-x svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2; }
36030
36214
  .hcp-kicker {
36031
36215
  display: flex; align-items: center; gap: 8px;
36032
36216
  font-family: var(--hcp-mono); font-size: 11px; font-weight: 500; letter-spacing: .08em; text-transform: uppercase;
@@ -36054,14 +36238,14 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
36054
36238
  .hcp-file small { display: block; font-size: 12px; color: var(--hcp-muted); line-height: 1.4; }
36055
36239
  .hcp-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
36056
36240
  .hcp-actions .hcp-btn--primary { flex: 1 1 auto; }
36057
- .hcp-foot { margin-top: 12px; display: flex; justify-content: space-between; align-items: center; gap: 10px; }
36058
- .hcp-foot small { font-size: 12px; color: var(--hcp-muted); }
36241
+ .hcp-foot { margin-top: 12px; display: flex; justify-content: flex-end; }
36059
36242
  .hcp-quiet {
36243
+ min-height: 0; border-radius: 0;
36060
36244
  font-size: 12px; color: var(--hcp-muted); cursor: pointer;
36061
36245
  text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--hcp-line-2);
36062
36246
  white-space: nowrap;
36063
36247
  }
36064
- .hcp-quiet:hover { color: var(--hcp-ink); }
36248
+ .hcp .hcp-quiet:hover { color: var(--hcp-ink); background: none; }
36065
36249
 
36066
36250
  @media (max-width: 480px) {
36067
36251
  .hcp { right: 12px; bottom: calc(12px + var(--hcp-lift)); }
@@ -36070,12 +36254,14 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
36070
36254
  }
36071
36255
  @media (pointer: coarse) {
36072
36256
  .hcp-btn, .hcp-dock .hcp-x, .hcp-card .hcp-x { min-height: 44px; }
36073
- .hcp-card .hcp-x { width: 44px; }
36257
+ .hcp-card .hcp-x { width: 44px; height: 44px; top: 6px; right: 6px; }
36074
36258
  .hcp-quiet { display: inline-flex; align-items: center; min-height: 44px; }
36075
36259
  }
36076
36260
  @media (prefers-reduced-motion: reduce) {
36077
36261
  .hcp { transition: none; }
36078
36262
  .hcp[data-hcp-state="card"] .hcp-card { animation: none; }
36263
+ .hcp:not([data-hcp-arrived]) .hcp-dock { animation: hcp-arrive-fade .4s ease 3s both; }
36264
+ @keyframes hcp-arrive-fade { from { opacity: 0; } to { opacity: 1; } }
36079
36265
  .hcp-btn:active > span { translate: none; }
36080
36266
  }
36081
36267
  @media (forced-colors: active) {
@@ -36087,8 +36273,43 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
36087
36273
  }
36088
36274
  });
36089
36275
 
36276
+ // src/plugins/visitor-drafts/bottom-ui.js
36277
+ function announceBottomUi() {
36278
+ document.dispatchEvent(new CustomEvent(BOTTOM_UI_CHANGED));
36279
+ }
36280
+ function bottomBarHeight(x3, skip) {
36281
+ const h = window.innerHeight;
36282
+ let raise = 0;
36283
+ const hits = document.elementsFromPoint ? document.elementsFromPoint(x3, h - 3) : [];
36284
+ for (const hit of hits) {
36285
+ for (let el5 = hit; el5 && el5 !== document.body && el5 !== document.documentElement; el5 = el5.parentElement) {
36286
+ if (skip(el5)) break;
36287
+ const position = getComputedStyle(el5).position;
36288
+ if (position === "fixed" || position === "sticky") {
36289
+ const height = Math.ceil(h - el5.getBoundingClientRect().top);
36290
+ if (height < h / 3) raise = Math.max(raise, height);
36291
+ break;
36292
+ }
36293
+ }
36294
+ }
36295
+ return raise;
36296
+ }
36297
+ function promptPanel(box) {
36298
+ const panel2 = document.querySelector("#hcp:not([hidden])");
36299
+ if (!panel2) return 0;
36300
+ const r = panel2.getBoundingClientRect();
36301
+ if (!r.width || !r.height || r.right <= box.left || r.left >= box.right) return 0;
36302
+ return Math.ceil(window.innerHeight - r.top);
36303
+ }
36304
+ var BOTTOM_UI_CHANGED;
36305
+ var init_bottom_ui = __esm({
36306
+ "src/plugins/visitor-drafts/bottom-ui.js"() {
36307
+ BOTTOM_UI_CHANGED = "clay:bottom-ui-changed";
36308
+ }
36309
+ });
36310
+
36090
36311
  // src/plugins/visitor-drafts/prompt.js
36091
- function mountPrompt({ memory, appName, link: link2, onError: onError2 }) {
36312
+ function mountPrompt({ memory, appName, link: link2 }) {
36092
36313
  if (!document.getElementById("hcp-style")) {
36093
36314
  document.head.insertAdjacentHTML("beforeend", `<link rel="stylesheet" clay="editor-ui" href="${FONT_HREF}">
36094
36315
  <style clay="editor-ui" id="hcp-style">${PROMPT_CSS}</style>`);
@@ -36101,6 +36322,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
36101
36322
  const USES_TO_SHOW = 3;
36102
36323
  const IDLE_MS = 2500;
36103
36324
  const TEXTY = /^(INPUT|TEXTAREA|SELECT)$/;
36325
+ const OUR_UI = "#hcp, .clay-intro, .clay-owner";
36104
36326
  const linkEl = root3.querySelector("[data-hcp-link]");
36105
36327
  linkEl.href = link2;
36106
36328
  if (appName) root3.querySelector("[data-hcp-name]").textContent = appName;
@@ -36113,11 +36335,12 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
36113
36335
  if (state2 === "card") {
36114
36336
  mem.shown = true;
36115
36337
  memory.save();
36338
+ root3.setAttribute("data-hcp-arrived", "");
36116
36339
  }
36117
36340
  lift();
36118
36341
  }
36119
36342
  function isHostField(el5) {
36120
- return el5 && !root3.contains(el5) && (TEXTY.test(el5.tagName) || el5.isContentEditable);
36343
+ return el5 && !root3.contains(el5) && !el5.closest(OUR_UI) && (TEXTY.test(el5.tagName) || el5.isContentEditable);
36121
36344
  }
36122
36345
  let timer = null;
36123
36346
  function maybeShow(reason) {
@@ -36156,20 +36379,10 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
36156
36379
  if (liftFrame) return;
36157
36380
  liftFrame = requestAnimationFrame(() => {
36158
36381
  liftFrame = 0;
36159
- const y2 = innerHeight - 3, x3 = innerWidth - 24;
36160
- let raise = 0;
36161
- const hits = document.elementsFromPoint ? document.elementsFromPoint(x3, y2) : [];
36162
- for (let i2 = 0; i2 < hits.length; i2++) {
36163
- for (let el5 = hits[i2]; el5 && el5 !== document.body && el5 !== document.documentElement; el5 = el5.parentElement) {
36164
- if (root3.contains(el5) || el5.closest("[data-hcp-preview]")) break;
36165
- const pos = getComputedStyle(el5).position;
36166
- if (pos === "fixed" || pos === "sticky") {
36167
- raise = Math.max(raise, Math.ceil(innerHeight - el5.getBoundingClientRect().top));
36168
- break;
36169
- }
36170
- }
36171
- }
36172
- root3.style.setProperty("--hcp-lift", (raise > 0 && raise < innerHeight / 3 ? raise : 0) + "px");
36382
+ const raise = bottomBarHeight(innerWidth - 24, (el5) => root3.contains(el5) || el5.closest("[data-hcp-preview], .clay-owner"));
36383
+ const next = raise + "px";
36384
+ if (root3.style.getPropertyValue("--hcp-lift") !== next) root3.style.setProperty("--hcp-lift", next);
36385
+ announceBottomUi();
36173
36386
  });
36174
36387
  }
36175
36388
  function downloadCurrent() {
@@ -36218,6 +36431,9 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
36218
36431
  memory.save();
36219
36432
  }
36220
36433
  });
36434
+ root3.addEventListener("animationend", (e) => {
36435
+ if (e.animationName === "hcp-arrive" || e.animationName === "hcp-arrive-fade") root3.setAttribute("data-hcp-arrived", "");
36436
+ });
36221
36437
  root3.addEventListener("keydown", (e) => {
36222
36438
  if (e.key === "Escape" && root3.getAttribute("data-hcp-state") === "card") {
36223
36439
  set3("mini");
@@ -36230,11 +36446,7 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
36230
36446
  function clearError() {
36231
36447
  if (root3.getAttribute("data-hcp-reason") === "error") root3.setAttribute("data-hcp-reason", "use");
36232
36448
  }
36233
- function hideForIntro(on2) {
36234
- if (on2) root3.setAttribute("data-hcp-intro", "");
36235
- else root3.removeAttribute("data-hcp-intro");
36236
- }
36237
- const api = { set: set3, root: root3, showError, clearError, hideForIntro };
36449
+ const api = { set: set3, root: root3, showError, clearError };
36238
36450
  if (mem.dismissed) {
36239
36451
  root3.setAttribute("data-hcp-state", "dismissed");
36240
36452
  return api;
@@ -36254,26 +36466,25 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
36254
36466
  var init_prompt = __esm({
36255
36467
  "src/plugins/visitor-drafts/prompt.js"() {
36256
36468
  init_prompt_style();
36469
+ init_bottom_ui();
36257
36470
  FONT_HREF = "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@500&family=IBM+Plex+Sans:wght@400;500;600&family=Newsreader:opsz,wght@6..72,500&display=swap";
36258
36471
  ASIDE = `<aside class="hcp" id="hcp" hidden aria-label="Download this app" data-hcp-state="tab" data-hcp-reason="open" sap-ignore clay="editor-ui">
36259
36472
  <section class="hcp-card" id="hcp-card" role="dialog" aria-modal="false" aria-labelledby="hcp-title">
36260
- <button type="button" class="hcp-x" data-hcp="minimize" aria-label="Minimize"><svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M3 12h10v2H3z"/></svg></button>
36473
+ <button type="button" class="hcp-x" data-hcp="minimize" aria-label="Minimize"><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="square" aria-hidden="true"><path d="M5 12h14"/></svg></button>
36261
36474
  <div class="hcp-kicker">Free download</div>
36262
- <h2 class="hcp-title" id="hcp-title"><span data-hcp-for="use">Your numbers are saved in this browser only.</span><span data-hcp-for="open">This app is free to keep.</span><span data-hcp-for="return">Your numbers are saved in this browser only.</span><span data-hcp-for="error">Your work isn't being saved in this browser.</span></h2>
36263
- <p class="hcp-body"><span data-hcp-for="open">Download it as one file and use it on your computer, no account needed.</span><span data-hcp-for="use">Download the file to keep them on your computer for good.</span><span data-hcp-for="return">Download the file to keep them on your computer for good.</span><span data-hcp-for="error">Storage is full or turned off here. Download the file to keep what you've done.</span></p>
36475
+ <h2 class="hcp-title" id="hcp-title"><span data-hcp-for="use">Your work is saved in this browser only.</span><span data-hcp-for="open">This app is free to keep.</span><span data-hcp-for="return">Your work is saved in this browser only.</span><span data-hcp-for="error">Your work isn't being saved in this browser.</span></h2>
36476
+ <p class="hcp-body"><span data-hcp-for="open">Download it as one file and use it on your computer, no account needed.</span><span data-hcp-for="use">Download the file to keep it on your computer for good.</span><span data-hcp-for="return">Download the file to keep it on your computer for good.</span><span data-hcp-for="error">Storage is full or turned off here. Download the file to keep what you've done.</span></p>
36264
36477
  <div class="hcp-file">
36265
- <svg width="26" height="30" viewBox="0 0 26 30" fill="none" aria-hidden="true"><path d="M2 1h15l7 7v21H2z" fill="var(--hcp-surface)" stroke="currentColor" stroke-width="2"/><path d="M17 1v7h7" stroke="currentColor" stroke-width="2"/><path d="M7 15h12M7 19h12M7 23h8" stroke="var(--hcp-muted)" stroke-width="2"/></svg>
36266
- <div><b class="hcp-name" data-hcp-name>This app</b><small>One file with the whole app and your numbers in it</small></div>
36478
+ <svg width="20" height="20" viewBox="0 0 256 256" fill="currentColor" aria-hidden="true"><path d="M224,144v64a8,8,0,0,1-8,8H40a8,8,0,0,1-8-8V144a8,8,0,0,1,16,0v56H208V144a8,8,0,0,1,16,0Zm-101.66,5.66a8,8,0,0,0,11.32,0l40-40a8,8,0,0,0-11.32-11.32L136,124.69V32a8,8,0,0,0-16,0v92.69L93.66,98.34a8,8,0,0,0-11.32,11.32Z"/></svg>
36479
+ <div><b class="hcp-name" data-hcp-name>This app</b><small>One file with the whole app and your work in it</small></div>
36267
36480
  </div>
36268
- <p class="hcp-body">Open it with HTML Clay, a free app for Mac, Windows and Linux. It opens the file in a window and saves every change into it, so your numbers stay on your computer.</p>
36269
36481
  <div class="hcp-actions">
36270
- <a class="hcp-btn hcp-btn--primary" data-hcp-link href="/_/download?as=htmlclay"><span><svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true"><path d="M7 1h2v8.6l2.8-2.8 1.4 1.4L8 13.4 2.8 8.2l1.4-1.4L7 9.6zM2 13h12v2H2z"/></svg>Download free</span></a>
36271
- <button type="button" class="hcp-btn" data-hcp="minimize"><span>Not now</span></button>
36482
+ <a class="hcp-btn hcp-btn--primary" data-hcp-link href="/_/download?as=htmlclay"><span><svg width="16" height="16" viewBox="0 0 256 256" fill="currentColor" aria-hidden="true"><path d="M224,144v64a8,8,0,0,1-8,8H40a8,8,0,0,1-8-8V144a8,8,0,0,1,16,0v56H208V144a8,8,0,0,1,16,0Zm-101.66,5.66a8,8,0,0,0,11.32,0l40-40a8,8,0,0,0-11.32-11.32L136,124.69V32a8,8,0,0,0-16,0v92.69L93.66,98.34a8,8,0,0,0-11.32,11.32Z"/></svg>Download free</span></a>
36272
36483
  </div>
36273
- <div class="hcp-foot"><small>Free, and the file is yours to keep.</small><button type="button" class="hcp-quiet" data-hcp="dismiss">Don&rsquo;t show again</button></div>
36484
+ <div class="hcp-foot"><button type="button" class="hcp-quiet" data-hcp="dismiss">Don&rsquo;t show again</button></div>
36274
36485
  </section>
36275
36486
  <div class="hcp-dock">
36276
- <button type="button" class="hcp-btn hcp-tab" data-hcp="open" aria-controls="hcp-card" aria-expanded="false" aria-label="This app is free to keep"><span><svg width="16" height="18" viewBox="0 0 16 18" fill="none" aria-hidden="true"><path d="M1.5 1h8.5l4.5 4.5V17h-13z" stroke="currentColor" stroke-width="2"/><path d="M10 1v5h4.5" stroke="currentColor" stroke-width="2"/><path d="M8 8v6M5.5 11.5 8 14l2.5-2.5" stroke="currentColor" stroke-width="1.8"/></svg><span class="hcp-tab__label">This app is free to keep</span></span></button>
36487
+ <button type="button" class="hcp-btn hcp-tab" data-hcp="open" aria-controls="hcp-card" aria-expanded="false" aria-label="This app is free to keep"><span><svg width="16" height="16" viewBox="0 0 256 256" fill="currentColor" aria-hidden="true"><path d="M224,144v64a8,8,0,0,1-8,8H40a8,8,0,0,1-8-8V144a8,8,0,0,1,16,0v56H208V144a8,8,0,0,1,16,0Zm-101.66,5.66a8,8,0,0,0,11.32,0l40-40a8,8,0,0,0-11.32-11.32L136,124.69V32a8,8,0,0,0-16,0v92.69L93.66,98.34a8,8,0,0,0-11.32,11.32Z"/></svg><span class="hcp-tab__label">This app is free to keep</span></span></button>
36277
36488
  <button type="button" class="hcp-x" data-hcp="dismiss" aria-label="Hide this for good"><svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true"><path d="M1.4 0 6 4.6 10.6 0 12 1.4 7.4 6 12 10.6 10.6 12 6 7.4 1.4 12 0 10.6 4.6 6 0 1.4z"/></svg></button>
36278
36489
  </div>
36279
36490
  </aside>`;
@@ -36284,166 +36495,34 @@ tr.hcms-shell.hcms-inline-ghost { display: table-row; }
36284
36495
  var INTRO_CSS;
36285
36496
  var init_intro_style = __esm({
36286
36497
  "src/plugins/visitor-drafts/intro-style.js"() {
36287
- INTRO_CSS = `.clay-intro, .clay-about {
36288
- --hcp-ground: #FDF8F0;
36289
- --hcp-surface: #FFFCF6;
36290
- --hcp-sunk: #F4ECDF;
36291
- --hcp-ink: #2B241B;
36292
- --hcp-ink-2: #4A4033;
36293
- --hcp-muted: #6B5942;
36294
- --hcp-line: #E8DCC6;
36295
- --hcp-line-2: #D8C8AF;
36296
- --hcp-brass: #8F6310;
36297
- --hcp-brass-soft: #F0E4CC;
36298
- --hcp-face: #F1E7D4;
36299
- --hcp-edge-hi: #FFF9ED;
36300
- --hcp-edge-lo: #D6C3A5;
36301
- --hcp-shadow: 0 1px 2px rgba(23, 28, 36, .06), 0 14px 34px -14px rgba(43, 36, 27, .32);
36302
- --hcp-sans: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
36303
- --hcp-serif: Newsreader, Georgia, "Times New Roman", serif;
36304
- --hcp-mono: "IBM Plex Mono", ui-monospace, Menlo, monospace;
36305
- color-scheme: light;
36306
- }
36307
- @media (prefers-color-scheme: dark) {
36308
- .clay-intro, .clay-about {
36309
- --hcp-ground: #0B0C13; --hcp-surface: #11131E; --hcp-sunk: #1A1D2C;
36310
- --hcp-ink: #ECEAF2; --hcp-ink-2: #C9CDE0; --hcp-muted: #8A90AB;
36311
- --hcp-line: #262B42; --hcp-line-2: #353B52;
36312
- --hcp-brass: #D9A445; --hcp-brass-soft: #3A2F17;
36313
- --hcp-face: #22273E; --hcp-edge-hi: #3C4260; --hcp-edge-lo: #1B2033;
36314
- --hcp-shadow: 0 1px 2px rgba(0, 0, 0, .4), 0 16px 38px -14px rgba(0, 0, 0, .7);
36315
- color-scheme: dark;
36316
- }
36317
- }
36318
-
36319
- html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !important; min-height: 100%; overflow-y: auto !important; }
36320
-
36321
- .clay-intro, .clay-intro *, .clay-intro *::before, .clay-intro *::after,
36322
- .clay-about, .clay-about *::before, .clay-about *::after {
36323
- box-sizing: border-box; margin: 0; padding: 0; border: 0;
36324
- font: inherit; letter-spacing: normal; text-transform: none; text-shadow: none;
36325
- line-height: inherit; color: inherit; background: none; float: none; max-width: none;
36326
- }
36498
+ INTRO_CSS = `html:has(> body > .clay-intro) { height: auto !important; overflow-y: auto !important; }
36499
+ body:has(> .clay-intro) { height: auto !important; overflow: visible !important; }
36327
36500
  .clay-intro {
36328
- position: relative; z-index: 1; display: block;
36329
- min-height: 100svh;
36330
- background: var(--hcp-ground);
36331
- font-family: var(--hcp-sans); font-size: 16px; font-weight: 400; line-height: 1.5;
36332
- color: var(--hcp-ink); text-align: left;
36333
- -webkit-font-smoothing: antialiased;
36334
- }
36335
- .clay-intro__inner {
36336
- display: flex; flex-direction: column; gap: 18px;
36337
- width: 100%; max-width: 720px; min-height: 100svh; margin: 0 auto;
36338
- padding: max(24px, 6vh) 16px 32px;
36339
- }
36340
- .clay-intro .clay-intro__kicker {
36341
- font-family: var(--hcp-mono); font-size: 11px; font-weight: 500; letter-spacing: .08em; text-transform: uppercase;
36342
- color: var(--hcp-brass);
36343
- }
36344
- .clay-intro__name {
36345
- font-family: var(--hcp-serif); font-size: clamp(30px, 7vw, 44px); font-weight: 500; line-height: 1.12;
36346
- color: var(--hcp-ink); font-variation-settings: "opsz" 48;
36347
- }
36348
- .clay-intro__inner > p { font-size: 18px; line-height: 1.55; color: var(--hcp-ink-2); }
36349
- .clay-intro__inner > a { color: var(--hcp-brass); text-decoration: underline; text-underline-offset: 2px; }
36350
- .clay-intro__inner > video, .clay-intro__inner > img {
36351
- display: block; width: 100%; height: auto; aspect-ratio: 16 / 9;
36352
- background: var(--hcp-sunk); border: 1px solid var(--hcp-line);
36353
- }
36354
- .clay-intro__inner > ul, .clay-intro__inner > ol { padding-left: 22px; font-size: 18px; line-height: 1.55; color: var(--hcp-ink-2); }
36355
- .clay-intro__inner > ul { list-style: disc; }
36356
- .clay-intro__inner > ol { list-style: decimal; }
36357
-
36358
- .clay-intro__actions { display: flex; flex-direction: column; gap: 14px; align-items: stretch; }
36359
- .clay-intro__download {
36360
- align-self: flex-start;
36361
- display: inline-flex; align-items: center; justify-content: center; gap: 8px;
36362
- min-height: 44px; padding: 0 18px;
36363
- font-family: var(--hcp-sans); font-size: 15px; font-weight: 500; line-height: 1.5;
36364
- white-space: nowrap; text-decoration: none; cursor: pointer; user-select: none;
36365
- color: var(--hcp-ground); background: var(--hcp-ink);
36366
- border: 2px solid;
36367
- border-color: color-mix(in srgb, var(--hcp-ink), white 24%) color-mix(in srgb, var(--hcp-ink), black 32%) color-mix(in srgb, var(--hcp-ink), black 32%) color-mix(in srgb, var(--hcp-ink), white 24%);
36368
- transition: background .2s ease;
36369
- }
36370
- .clay-intro__download:hover { background: color-mix(in srgb, var(--hcp-ink), var(--hcp-ground) 12%); }
36371
-
36372
- .clay-intro__signup {
36373
- display: flex; flex-direction: column; gap: 9px;
36374
- padding: 14px 16px;
36375
- background: var(--hcp-surface); border: 1px solid var(--hcp-line); border-left: 3px solid var(--hcp-brass);
36376
- }
36377
- .clay-intro__signup label { font-size: 14px; line-height: 1.45; color: var(--hcp-ink-2); }
36378
- .clay-intro__row { display: flex; gap: 8px; }
36379
- .clay-intro__row input {
36380
- flex: 1 1 auto; min-width: 0; height: 40px; padding: 0 10px;
36381
- font-family: var(--hcp-sans); font-size: 15px; color: var(--hcp-ink);
36382
- background: var(--hcp-ground); border: 1px solid var(--hcp-line-2);
36383
- }
36384
- .clay-intro__row input::placeholder { color: var(--hcp-muted); }
36385
- .clay-intro__row button {
36386
- flex: none; height: 40px; padding: 0 14px;
36387
- font-family: var(--hcp-sans); font-size: 14px; font-weight: 500;
36388
- color: var(--hcp-ink); background: var(--hcp-face); cursor: pointer;
36389
- border: 2px solid; border-color: var(--hcp-edge-hi) var(--hcp-edge-lo) var(--hcp-edge-lo) var(--hcp-edge-hi);
36390
- }
36391
- .clay-intro__row button:hover { background: color-mix(in srgb, var(--hcp-face), var(--hcp-edge-hi) 45%); }
36392
- .clay-intro__hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; }
36393
- .clay-intro__msg { font-size: 14px; line-height: 1.5; color: var(--hcp-ink-2); }
36394
- .clay-intro__msg[hidden] { display: none; }
36395
- .clay-intro__row[hidden] { display: none; }
36396
-
36397
- .clay-intro__start {
36398
- margin-top: auto;
36399
- display: inline-flex; align-items: center; justify-content: center; gap: 10px;
36400
- width: 100%; min-height: 56px; padding: 0 20px;
36401
- font-family: var(--hcp-sans); font-size: 16px; font-weight: 500;
36402
- color: var(--hcp-ground); background: var(--hcp-ink); cursor: pointer;
36403
- border: 2px solid;
36404
- border-color: color-mix(in srgb, var(--hcp-ink), white 24%) color-mix(in srgb, var(--hcp-ink), black 32%) color-mix(in srgb, var(--hcp-ink), black 32%) color-mix(in srgb, var(--hcp-ink), white 24%);
36405
- transition: background .2s ease;
36406
- }
36407
- .clay-intro__start::after { content: "\\2193"; font-size: 15px; line-height: 1; }
36408
- .clay-intro__start:hover { background: color-mix(in srgb, var(--hcp-ink), var(--hcp-ground) 12%); }
36409
- .clay-intro__download:focus-visible, .clay-intro__start:focus-visible,
36410
- .clay-intro__row button:focus-visible, .clay-intro__row input:focus-visible,
36411
- .clay-about:focus-visible { outline: 2px solid var(--hcp-brass); outline-offset: 2px; }
36412
-
36413
- .clay-about {
36414
- position: fixed; top: 12px; right: 12px; z-index: 2147482000;
36415
- display: inline-flex; align-items: center; justify-content: center; gap: 7px;
36416
- height: 34px; padding: 0 12px;
36417
- font-family: var(--hcp-sans); font-size: 13px; font-weight: 500; line-height: 1.5;
36418
- white-space: nowrap; cursor: pointer; user-select: none;
36419
- color: var(--hcp-ink); background: var(--hcp-face);
36420
- border: 2px solid; border-color: var(--hcp-edge-hi) var(--hcp-edge-lo) var(--hcp-edge-lo) var(--hcp-edge-hi);
36421
- box-shadow: var(--hcp-shadow);
36422
- transition: background .2s ease;
36423
- }
36424
- .clay-about:hover { background: color-mix(in srgb, var(--hcp-face), var(--hcp-edge-hi) 45%); }
36425
-
36426
- @media (min-width: 560px) {
36427
- .clay-intro__actions { flex-direction: row; flex-wrap: wrap; align-items: center; }
36428
- .clay-intro__signup { flex: 1 1 320px; max-width: 520px; }
36429
- }
36430
- @media (pointer: coarse) {
36431
- .clay-intro__download, .clay-intro__start, .clay-intro__row input, .clay-intro__row button { min-height: 44px; }
36432
- .clay-about { min-height: 44px; }
36433
- }
36434
- @media (prefers-reduced-motion: reduce) {
36435
- .clay-intro, .clay-intro *, .clay-about { transition: none; animation: none; }
36501
+ position: relative;
36502
+ display: block;
36436
36503
  }
36437
- @media (forced-colors: active) {
36438
- .clay-intro__download, .clay-intro__start, .clay-intro__row button, .clay-about { border: 1px solid ButtonText; }
36439
- .clay-intro__signup { border: 1px solid CanvasText; }
36504
+ .clay-intro__close {
36505
+ position: absolute; top: 10px; right: 10px; z-index: 1;
36506
+ display: inline-flex; align-items: center; justify-content: center;
36507
+ width: 28px; height: 28px; min-height: 0; margin: 0; padding: 0;
36508
+ border: 0; border-radius: 4px; background: none; box-shadow: none;
36509
+ color: inherit; opacity: .55; cursor: pointer; line-height: 1;
36440
36510
  }
36441
- @media print { .clay-intro, .clay-about { display: none; } }
36511
+ .clay-intro .clay-intro__close:hover { opacity: 1; background: none; color: inherit; }
36512
+ .clay-intro__close:focus-visible { opacity: 1; outline: 2px solid currentColor; outline-offset: 2px; }
36513
+ .clay-intro__close svg { display: block; width: 12px; height: 12px; fill: currentColor; stroke: none; }
36514
+ @media (pointer: coarse) { .clay-intro__close { width: 44px; height: 44px; top: 4px; right: 4px; } }
36515
+ @media print { .clay-intro { display: none; } }
36442
36516
  `;
36443
36517
  }
36444
36518
  });
36445
36519
 
36446
36520
  // src/plugins/visitor-drafts/intro.js
36521
+ function isTextEntry(el5) {
36522
+ if (el5.isContentEditable || el5.closest(TEXT_ENTRY)) return true;
36523
+ if (el5.tagName !== "INPUT") return false;
36524
+ return NON_TEXT_INPUT.indexOf(el5.type) < 0;
36525
+ }
36447
36526
  function forced() {
36448
36527
  try {
36449
36528
  return new URLSearchParams(window.location.search).get("intro") === "1";
@@ -36461,191 +36540,89 @@ html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !importan
36461
36540
  }
36462
36541
  function focusApp() {
36463
36542
  const next = [...document.querySelectorAll(FOCUSABLE2)].find(
36464
- (el5) => !el5.disabled && !el5.closest("[clay~='editor-ui']") && (typeof el5.checkVisibility !== "function" || el5.checkVisibility())
36543
+ (el5) => !el5.disabled && !isTextEntry(el5) && !el5.closest("[clay~='editor-ui']") && (typeof el5.checkVisibility !== "function" || el5.checkVisibility())
36465
36544
  );
36466
- if (next) next.focus();
36467
- else document.body.focus();
36468
- }
36469
- function mountIntro({ memory, template, appName, link: link2, tag, source: source3, hadDraft, prompt, snapshot }) {
36470
- if (!template) return null;
36471
- const opts = { memory, template, appName, link: link2, tag, source: source3, hadDraft, prompt, snapshot };
36472
- if (forced()) return mount(opts);
36473
- if (memory.get().intro.done || hadDraft) return mountAbout(opts);
36474
- if (window.location.hash) return null;
36475
- if (window.matchMedia && window.matchMedia("print").matches) return null;
36476
- return mount(opts);
36477
- }
36478
- function mountAbout(opts) {
36479
- stylesOnce();
36480
- const button2 = document.createElement("button");
36481
- button2.type = "button";
36482
- button2.className = "clay-about";
36483
- button2.setAttribute("clay", "editor-ui");
36484
- button2.setAttribute("sap-ignore", "");
36485
- button2.textContent = COPY.about;
36486
- button2.addEventListener("click", () => {
36487
- button2.remove();
36488
- mount({ ...opts, fromAbout: true });
36489
- });
36490
- document.body.appendChild(button2);
36491
- return button2;
36545
+ if (next) next.focus({ preventScroll: true });
36546
+ else document.body.focus({ preventScroll: true });
36492
36547
  }
36493
- function mount(opts) {
36494
- const { memory, template, appName, link: link2, tag, source: source3, prompt, snapshot, fromAbout } = opts;
36548
+ function mountIntro({ memory, template, appName, hadDraft }) {
36549
+ if (!template || !template.content.textContent.trim() && !template.content.querySelector("img, svg, video, canvas")) return null;
36550
+ if (!forced()) {
36551
+ if (memory.get().intro.done || hadDraft) return null;
36552
+ if (window.location.hash) return null;
36553
+ if (window.matchMedia && window.matchMedia("print").matches) return null;
36554
+ }
36555
+ return mount({ memory, template, appName });
36556
+ }
36557
+ function mount({ memory, template, appName }) {
36495
36558
  stylesOnce();
36496
36559
  const section = document.createElement("section");
36497
36560
  section.className = "clay-intro";
36498
36561
  section.setAttribute("clay", "editor-ui");
36499
36562
  section.setAttribute("sap-ignore", "");
36500
36563
  section.setAttribute("aria-label", COPY.label(appName));
36501
- const inner = document.createElement("div");
36502
- inner.className = "clay-intro__inner";
36503
- const kicker = document.createElement("p");
36504
- kicker.className = "clay-intro__kicker";
36505
- kicker.textContent = COPY.kicker;
36506
- const name = document.createElement("h1");
36507
- name.className = "clay-intro__name";
36508
- name.textContent = appName;
36509
- const actions = document.createElement("div");
36510
- actions.className = "clay-intro__actions";
36511
- const download = document.createElement("a");
36512
- download.className = "clay-intro__download";
36513
- download.href = link2;
36514
- download.textContent = COPY.download;
36515
- const form = document.createElement("form");
36516
- form.className = "clay-intro__signup";
36517
- form.noValidate = true;
36518
- const label = document.createElement("label");
36519
- label.setAttribute("for", "clay-intro-email");
36520
- label.textContent = COPY.signupLabel;
36521
- const row = document.createElement("div");
36522
- row.className = "clay-intro__row";
36523
- const email = document.createElement("input");
36524
- email.id = "clay-intro-email";
36525
- email.type = "email";
36526
- email.autocomplete = "email";
36527
- email.required = true;
36528
- email.placeholder = COPY.emailPlaceholder;
36529
- const send3 = document.createElement("button");
36530
- send3.type = "submit";
36531
- send3.textContent = COPY.signup;
36532
- row.append(email, send3);
36533
- const honey = document.createElement("input");
36534
- honey.className = "clay-intro__hp";
36535
- honey.name = "website";
36536
- honey.tabIndex = -1;
36537
- honey.autocomplete = "off";
36538
- honey.setAttribute("aria-hidden", "true");
36539
- const message = document.createElement("p");
36540
- message.className = "clay-intro__msg";
36541
- message.setAttribute("role", "status");
36542
- message.hidden = true;
36543
- form.append(label, row, honey, message);
36544
- actions.append(download, form);
36545
- const start2 = document.createElement("button");
36546
- start2.type = "button";
36547
- start2.className = "clay-intro__start";
36548
- start2.textContent = COPY.start(appName);
36549
- inner.append(kicker, name, template.content.cloneNode(true), actions, start2);
36550
- section.appendChild(inner);
36551
- let observer2 = null;
36564
+ section.appendChild(template.content.cloneNode(true));
36565
+ const close = document.createElement("button");
36566
+ close.type = "button";
36567
+ close.className = "clay-intro__close";
36568
+ close.setAttribute("aria-label", COPY.close);
36569
+ close.innerHTML = '<svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true"><path d="M1.4 0 6 4.6 10.6 0 12 1.4 7.4 6 12 10.6 10.6 12 6 7.4 1.4 12 0 10.6 4.6 6 0 1.4z"/></svg>';
36570
+ section.appendChild(close);
36552
36571
  let gone = false;
36553
- function stop2() {
36554
- document.removeEventListener("input", onUse, true);
36555
- document.removeEventListener("change", onUse, true);
36556
- document.removeEventListener("clay:dirty", onUse, true);
36557
- if (observer2) {
36558
- observer2.disconnect();
36559
- observer2 = null;
36560
- }
36561
- if (prompt) prompt.hideForIntro(false);
36562
- }
36563
- function dismiss3(reason, target) {
36572
+ let frame = 0;
36573
+ function dismiss3(reason) {
36564
36574
  if (gone) return;
36565
36575
  gone = true;
36566
- const anchor = target && target.getBoundingClientRect ? target : document.activeElement;
36567
- const before = anchor && anchor.getBoundingClientRect ? anchor.getBoundingClientRect().top : 0;
36576
+ removeEventListener("scroll", onScroll);
36577
+ if (frame) cancelAnimationFrame(frame);
36578
+ const height = section.getBoundingClientRect().height;
36579
+ const before = window.scrollY;
36568
36580
  memory.get().intro.done = reason;
36569
36581
  memory.save();
36570
- stop2();
36571
36582
  section.remove();
36572
- if (reason === "button") {
36573
- window.scrollTo({ top: 0 });
36574
- focusApp();
36575
- } else if (anchor && anchor.getBoundingClientRect) {
36576
- window.scrollBy(0, anchor.getBoundingClientRect().top - before);
36583
+ if (reason === "scrolled") {
36584
+ const locked = /hidden|clip/.test(getComputedStyle(document.body).overflowY + getComputedStyle(document.documentElement).overflowY);
36585
+ window.scrollTo(0, locked ? 0 : Math.max(0, before - height));
36577
36586
  }
36578
- if (fromAbout) mountAbout(opts);
36587
+ announceBottomUi();
36588
+ if (reason !== "scrolled") focusApp();
36579
36589
  }
36580
- function unchanged() {
36581
- return !snapshot || snapshot() === baseline;
36590
+ function onScroll() {
36591
+ if (frame) return;
36592
+ frame = requestAnimationFrame(() => {
36593
+ frame = 0;
36594
+ const bottom = section.getBoundingClientRect().bottom;
36595
+ const atEnd = scrollY > 0 && scrollY + innerHeight >= document.documentElement.scrollHeight - 1;
36596
+ if (bottom < 1 || atEnd && bottom < innerHeight) dismiss3("scrolled");
36597
+ });
36582
36598
  }
36583
- function onUse(event) {
36584
- if (section.contains(event.target)) return;
36585
- if (event.type === "clay:dirty") {
36586
- if (!unchanged()) dismiss3("use", event.target);
36599
+ section.addEventListener("click", (event) => {
36600
+ if (event.target.closest(".clay-intro__close, [visitor-intro-dismiss]")) {
36601
+ dismiss3("closed");
36587
36602
  return;
36588
36603
  }
36589
- if (!unchanged() || event.target?.closest?.("[persist]")) {
36590
- dismiss3("use", event.target);
36591
- return;
36604
+ if (event.target.closest("[visitor-intro-start]")) {
36605
+ event.preventDefault();
36606
+ window.scrollTo({ top: window.scrollY + section.getBoundingClientRect().bottom, behavior: "smooth" });
36592
36607
  }
36593
- const target = event.target;
36594
- setTimeout(() => {
36595
- if (!gone && !unchanged()) dismiss3("use", target);
36596
- }, 0);
36597
- }
36598
- form.addEventListener("submit", (event) => {
36599
- event.preventDefault();
36600
- fetch("/_/newsletter/subscribe", {
36601
- method: "POST",
36602
- headers: { "Content-Type": "application/json" },
36603
- credentials: "same-origin",
36604
- body: JSON.stringify({ email: email.value.trim(), tags: [tag], source: source3, website: honey.value })
36605
- }).then((response) => {
36606
- if (!response.ok) throw new Error("signup failed");
36607
- row.hidden = true;
36608
- message.textContent = COPY.signupThanks;
36609
- message.hidden = false;
36610
- }).catch(() => {
36611
- message.textContent = COPY.signupFailed;
36612
- message.hidden = false;
36613
- });
36614
36608
  });
36615
- start2.addEventListener("click", () => dismiss3("button"));
36616
- const baseline = snapshot ? snapshot() : null;
36617
36609
  document.body.prepend(section);
36618
- document.addEventListener("input", onUse, true);
36619
- document.addEventListener("change", onUse, true);
36620
- document.addEventListener("clay:dirty", onUse, true);
36621
- if (prompt) {
36622
- prompt.hideForIntro(true);
36623
- if (typeof IntersectionObserver === "function") {
36624
- observer2 = new IntersectionObserver((entries3) => {
36625
- for (const entry of entries3) prompt.hideForIntro(entry.intersectionRatio > 0.3);
36626
- });
36627
- observer2.observe(section);
36628
- }
36629
- }
36610
+ announceBottomUi();
36611
+ addEventListener("scroll", onScroll, { passive: true });
36630
36612
  return section;
36631
36613
  }
36632
- var COPY, FOCUSABLE2;
36614
+ var COPY, FOCUSABLE2, TEXT_ENTRY, NON_TEXT_INPUT;
36633
36615
  var init_intro = __esm({
36634
36616
  "src/plugins/visitor-drafts/intro.js"() {
36635
36617
  init_intro_style();
36618
+ init_bottom_ui();
36636
36619
  COPY = {
36637
- kicker: "Free app",
36638
- download: "Download free",
36639
- signupLabel: "I make one of these about every two weeks. Get the next one by email.",
36640
- emailPlaceholder: "you@example.com",
36641
- signup: "Send me the next one",
36642
- signupThanks: "Thanks. Check your email for a link to confirm.",
36643
- signupFailed: "That didn't go through. Check the address and try again.",
36644
- start: (appName) => `Start using ${appName}`,
36645
- about: "About this app",
36620
+ close: "Hide this introduction",
36646
36621
  label: (appName) => `About ${appName}`
36647
36622
  };
36648
36623
  FOCUSABLE2 = "a[href], button, input, select, textarea, [tabindex]:not([tabindex='-1'])";
36624
+ TEXT_ENTRY = "textarea, select";
36625
+ NON_TEXT_INPUT = ["checkbox", "radio", "button", "submit", "reset", "file"];
36649
36626
  }
36650
36627
  });
36651
36628
 
@@ -36689,36 +36666,54 @@ html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !importan
36689
36666
  close.addEventListener("click", () => {
36690
36667
  chip2.remove();
36691
36668
  liftOff();
36669
+ release();
36692
36670
  });
36693
36671
  chip2.append(lead, action2, close);
36694
36672
  document.body.appendChild(chip2);
36673
+ const vars = document.createElement("style");
36674
+ vars.id = "clay-owner-vars";
36675
+ vars.setAttribute("clay", "editor-ui");
36676
+ document.head.appendChild(vars);
36677
+ let published = "";
36678
+ function publish(lift2) {
36679
+ const next = `:root { --clay-owner-room: ${Math.ceil(chip2.getBoundingClientRect().height) + 16}px; }`;
36680
+ if (vars.textContent !== next) vars.textContent = next;
36681
+ if (published === next + lift2) return;
36682
+ published = next + lift2;
36683
+ announceBottomUi();
36684
+ }
36685
+ function release() {
36686
+ vars.remove();
36687
+ announceBottomUi();
36688
+ }
36695
36689
  let frame = 0;
36696
36690
  function lift() {
36697
36691
  if (frame) return;
36698
36692
  frame = requestAnimationFrame(() => {
36699
36693
  frame = 0;
36700
- const y2 = window.innerHeight - 3;
36701
- let raise = 0;
36702
- const hits = document.elementsFromPoint ? document.elementsFromPoint(24, y2) : [];
36703
- for (const hit of hits) {
36704
- for (let el5 = hit; el5 && el5 !== document.body && el5 !== document.documentElement; el5 = el5.parentElement) {
36705
- if (chip2.contains(el5)) break;
36706
- const position = getComputedStyle(el5).position;
36707
- if (position === "fixed" || position === "sticky") {
36708
- raise = Math.max(raise, Math.ceil(window.innerHeight - el5.getBoundingClientRect().top));
36709
- break;
36710
- }
36711
- }
36712
- }
36713
- chip2.style.setProperty("--hcp-lift", (raise > 0 && raise < window.innerHeight / 3 ? raise : 0) + "px");
36694
+ let lift2 = bottomBarHeight(24, (el5) => chip2.contains(el5) || el5.closest("#hcp"));
36695
+ const panel2 = promptPanel(chip2.getBoundingClientRect());
36696
+ const covered = panel2 >= window.innerHeight / 3;
36697
+ if (panel2 && !covered) lift2 = Math.max(lift2, panel2 - 4);
36698
+ chip2.style.visibility = covered ? "hidden" : "";
36699
+ chip2.style.setProperty("--hcp-lift", lift2 + "px");
36700
+ publish(covered ? -1 : lift2);
36714
36701
  });
36715
36702
  }
36716
36703
  function liftOff() {
36717
36704
  removeEventListener("scroll", lift);
36718
36705
  removeEventListener("resize", lift);
36706
+ document.removeEventListener(BOTTOM_UI_CHANGED, lift);
36707
+ document.removeEventListener("transitionend", lift, true);
36708
+ document.removeEventListener("pointerup", lift, true);
36709
+ window.removeEventListener("hashchange", lift);
36719
36710
  }
36720
36711
  addEventListener("scroll", lift, { passive: true });
36721
36712
  addEventListener("resize", lift);
36713
+ document.addEventListener(BOTTOM_UI_CHANGED, lift);
36714
+ document.addEventListener("transitionend", lift, true);
36715
+ document.addEventListener("pointerup", lift, true);
36716
+ window.addEventListener("hashchange", lift);
36722
36717
  lift();
36723
36718
  return chip2;
36724
36719
  }
@@ -36726,6 +36721,7 @@ html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !importan
36726
36721
  var init_owner_chip = __esm({
36727
36722
  "src/plugins/visitor-drafts/owner-chip.js"() {
36728
36723
  init_is_edit_mode();
36724
+ init_bottom_ui();
36729
36725
  COPY2 = {
36730
36726
  draft: "You're seeing a private draft, like a visitor.",
36731
36727
  draftAction: "Edit the original",
@@ -36775,17 +36771,23 @@ html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !importan
36775
36771
  -webkit-font-smoothing: antialiased;
36776
36772
  }
36777
36773
  .clay-owner__action {
36778
- flex: none; font-weight: 600; color: var(--hcp-brass); cursor: pointer;
36774
+ flex: none; min-height: 0; font-weight: 600; color: var(--hcp-brass); cursor: pointer;
36779
36775
  text-decoration: underline; text-underline-offset: 2px; text-decoration-color: var(--hcp-line-2);
36780
36776
  white-space: nowrap;
36781
36777
  }
36782
- .clay-owner__action:hover { color: var(--hcp-ink); }
36778
+ .clay-owner .clay-owner__action:hover { color: var(--hcp-ink); background: none; }
36783
36779
  .clay-owner__x {
36784
36780
  flex: none; display: inline-flex; align-items: center; justify-content: center;
36785
- width: 24px; height: 24px; font-size: 15px; color: var(--hcp-muted); cursor: pointer;
36781
+ width: 24px; height: 24px; min-height: 0; font-size: 15px; color: var(--hcp-muted); cursor: pointer;
36786
36782
  }
36787
- .clay-owner__x:hover { color: var(--hcp-ink); }
36783
+ .clay-owner .clay-owner__x:hover { color: var(--hcp-ink); background: none; }
36788
36784
  .clay-owner__action:focus-visible, .clay-owner__x:focus-visible { outline: 2px solid var(--hcp-brass); outline-offset: 2px; }
36785
+
36786
+ .clay-owner svg {
36787
+ display: block; flex: none; width: 1em; height: 1em; line-height: 1;
36788
+ fill: currentColor; stroke: none; stroke-width: 0; stroke-linecap: butt; stroke-linejoin: miter;
36789
+ }
36790
+ .clay-owner button, .clay-owner a { border-radius: 0; line-height: 1.45; }
36789
36791
  @media (pointer: coarse) {
36790
36792
  .clay-owner__action, .clay-owner__x { min-height: 44px; }
36791
36793
  .clay-owner__x { width: 44px; }
@@ -36863,7 +36865,7 @@ html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !importan
36863
36865
  const template = document.querySelector("template[visitor-intro]");
36864
36866
  const start2 = () => {
36865
36867
  const prompt = mountPrompt({ memory, appName, link: downloadLink });
36866
- mountIntro({ memory, template, appName, link: downloadLink, tag: signupTag(slot, template), source, hadDraft: result2.hadDraft, prompt, snapshot: store.snapshot });
36868
+ mountIntro({ memory, template, appName, hadDraft: result2.hadDraft });
36867
36869
  if (isOwner) mountOwnerChip();
36868
36870
  document.addEventListener("clay:drafts-store-failed", () => prompt.showError());
36869
36871
  document.addEventListener("clay:drafts-saved", () => prompt.clearError());
@@ -42515,7 +42517,7 @@ html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !importan
42515
42517
  };
42516
42518
  var ot4 = (e) => it4(re4({}, "__esModule", { value: true }), e);
42517
42519
  var Vt3 = {};
42518
- Se4(Vt3, { applyData: () => xe4, default: () => Ft3, engine: () => X3, extractData: () => Oe4 });
42520
+ Se4(Vt3, { applyData: () => xe4, default: () => Ft3, engine: () => G3, extractData: () => Oe4 });
42519
42521
  var Y3 = ["textContent", "innerText", "innerHTML", "outerHTML", "value", "checked", "selected", "disabled", "readOnly", "type", "tagName", "nodeName", "nodeType", "nodeValue", "childElementCount", "id", "className", "classList", "baseURI", "offsetWidth", "offsetHeight", "clientWidth", "clientHeight", "scrollWidth", "scrollHeight", "dataset", "currentSrc", "duration", "paused", "title", "documentURI", "contentType"], Z4 = new Set(Y3), ie4 = /* @__PURE__ */ new Set(["textContent", "innerText", "innerHTML", "value", "checked", "selected", "disabled", "readOnly", "type", "id", "className", "title"]), $4 = /* @__PURE__ */ new Set(["tagName", "nodeName", "nodeType", "nodeValue", "childElementCount", "classList", "baseURI", "documentURI", "contentType", "offsetWidth", "offsetHeight", "clientWidth", "clientHeight", "scrollWidth", "scrollHeight", "currentSrc", "duration", "paused", "dataset"]), be4 = /* @__PURE__ */ new Set(["checked", "selected", "disabled", "readOnly", "paused"]);
42520
42522
  function oe4(e, t) {
42521
42523
  return t == null ? be4.has(e) ? false : "" : be4.has(e) ? t === "false" ? false : !!t : t;
@@ -43357,13 +43359,13 @@ html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !importan
43357
43359
  return p2 ? l.importNode(p2, true) : null;
43358
43360
  } };
43359
43361
  }
43360
- function Xe4(e) {
43362
+ function Ge4(e) {
43361
43363
  return e && e.nodeType === 1 && e.tagName === "SCRIPT" && e.hasAttribute && e.hasAttribute("data-rules-name");
43362
43364
  }
43363
43365
  function $t4(e) {
43364
43366
  return e ? (e.nodeType === 9 || e.nodeType === 11, e) : null;
43365
43367
  }
43366
- function Ge4(e, t, n2) {
43368
+ function Xe4(e, t, n2) {
43367
43369
  !e || e.nodeType !== 1 || (t === "value" && e.tagName === "INPUT" ? e.value = n2 === null ? "" : n2 : t === "checked" && e.tagName === "INPUT" ? e.checked = n2 !== null : t === "selected" && e.tagName === "OPTION" && (e.selected = n2 !== null));
43368
43370
  }
43369
43371
  function Ye4(e, t) {
@@ -43382,7 +43384,7 @@ html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !importan
43382
43384
  let i2 = $t4(e);
43383
43385
  if (!i2 || !i2.querySelectorAll) return [];
43384
43386
  let r = Array.from(i2.querySelectorAll(t));
43385
- n2.includeRulesTag || (r = r.filter((c2) => !Xe4(c2)));
43387
+ n2.includeRulesTag || (r = r.filter((c2) => !Ge4(c2)));
43386
43388
  let s2 = [];
43387
43389
  n2.skip && s2.push(n2.skip);
43388
43390
  let l = n2.templateAttr === null ? null : n2.templateAttr || "cms-template";
@@ -43402,9 +43404,9 @@ html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !importan
43402
43404
  e.textContent = t, Ye4(e, t);
43403
43405
  }, attr(e, t, n2) {
43404
43406
  if (n2 === void 0) return e.hasAttribute && e.hasAttribute(t) ? e.getAttribute(t) : null;
43405
- e.setAttribute(t, n2), Ge4(e, t, n2);
43407
+ e.setAttribute(t, n2), Xe4(e, t, n2);
43406
43408
  }, removeAttr(e, t) {
43407
- e && e.removeAttribute && e.removeAttribute(t), Ge4(e, t, null);
43409
+ e && e.removeAttribute && e.removeAttribute(t), Xe4(e, t, null);
43408
43410
  }, prop(e, t, n2) {
43409
43411
  if (n2 === void 0) {
43410
43412
  let i2 = e ? e[t] : void 0;
@@ -43472,7 +43474,7 @@ html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !importan
43472
43474
  let h = f2(a);
43473
43475
  if (!h?.querySelectorAll) return [];
43474
43476
  let y2 = o2().query(p2, h);
43475
- g.includeRulesTag || (y2 = y2.filter((S2) => !Xe4(S2)));
43477
+ g.includeRulesTag || (y2 = y2.filter((S2) => !Ge4(S2)));
43476
43478
  let A2 = [];
43477
43479
  g.skip && A2.push(g.skip);
43478
43480
  let T2 = g.templateAttr === null ? null : g.templateAttr || "cms-template";
@@ -43579,9 +43581,9 @@ html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !importan
43579
43581
  } };
43580
43582
  return w2;
43581
43583
  }
43582
- var G3 = x3;
43583
- var X3 = { extract: (e, t, n2 = {}) => C3(M3(e, n2), e, t, n2), apply: (e, t, n2, i2 = {}) => I4(M3(e, { ...i2, _write: true }), e, t, n2, i2), findRulesIn: (e, t) => j3(G3, e, t), findRules: (e, t) => Te4(G3, e, t), bind: (e, t, n2 = {}) => {
43584
- let i2 = Te4(G3, e, t);
43584
+ var X3 = x3;
43585
+ var G3 = { extract: (e, t, n2 = {}) => C3(M3(e, n2), e, t, n2), apply: (e, t, n2, i2 = {}) => I4(M3(e, { ...i2, _write: true }), e, t, n2, i2), findRulesIn: (e, t) => j3(X3, e, t), findRules: (e, t) => Te4(X3, e, t), bind: (e, t, n2 = {}) => {
43586
+ let i2 = Te4(X3, e, t);
43585
43587
  if (!i2) {
43586
43588
  let r = typeof t == "string" ? `data-rules-name~="${t}"` : "the provided rules object";
43587
43589
  throw new Error(`hyper-html-api: could not resolve rules for ${r}`);
@@ -43589,7 +43591,7 @@ html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !importan
43589
43591
  return { ...i2, get: () => C3(M3(e, n2), e, i2.rules, n2), set: (r) => I4(M3(e, { ...n2, _write: true }), e, i2.rules, r, n2) };
43590
43592
  }, parseStrict: pe4, parseRelaxed: z3, ruleAttrIndex: b2, splitRule: ce4, errors: R3, DOM_PROPERTIES: Y3 }, we4 = (e) => !!e && typeof e.nodeType == "number";
43591
43593
  function Qe4(e) {
43592
- let t = e && e.ownerDocument ? e.ownerDocument : e, n2 = j3(G3, t, "api");
43594
+ let t = e && e.ownerDocument ? e.ownerDocument : e, n2 = j3(X3, t, "api");
43593
43595
  if (!n2) throw new R3.NoRulesTag("api");
43594
43596
  return n2.rules;
43595
43597
  }
@@ -43673,19 +43675,19 @@ html:has(> body > .clay-intro), body:has(> .clay-intro) { height: auto !importan
43673
43675
  }
43674
43676
  function Oe4(e, t) {
43675
43677
  let n2 = we4(e) ? e : document, i2 = we4(e) ? t : e;
43676
- return i2 === void 0 ? C3(M3(n2), n2, Qe4(n2)) : X3.bind(n2, i2).get();
43678
+ return i2 === void 0 ? C3(M3(n2), n2, Qe4(n2)) : G3.bind(n2, i2).get();
43677
43679
  }
43678
43680
  function xe4(e, t, n2) {
43679
43681
  try {
43680
43682
  if (!we4(e)) throw new Error("hyper-html-api: applyData(root, data, source?) needs a DOM root as the first argument.");
43681
- let i2 = n2 === void 0 ? Qe4(e) : X3.bind(e, n2).rules;
43683
+ let i2 = n2 === void 0 ? Qe4(e) : G3.bind(e, n2).rules;
43682
43684
  Ut3(e, i2, t);
43683
43685
  } catch (i2) {
43684
43686
  throw zt3(i2);
43685
43687
  }
43686
43688
  return e;
43687
43689
  }
43688
- var Kt3 = { engine: X3, extractData: Oe4, applyData: xe4 }, Ft3 = Kt3;
43690
+ var Kt3 = { engine: G3, extractData: Oe4, applyData: xe4 }, Ft3 = Kt3;
43689
43691
  return ot4(Vt3);
43690
43692
  })();
43691
43693
  window.clay = window.clay || {};
@@ -46339,7 +46341,7 @@ ${t}` : `"use strict"; return (${t});`;
46339
46341
  document.body.append(box);
46340
46342
  return box;
46341
46343
  }
46342
- function build3(message, type) {
46344
+ function build3(message2, type) {
46343
46345
  const tone = TONES[type];
46344
46346
  const el5 = bevelSurface("div", [
46345
46347
  "display:flex",
@@ -46357,7 +46359,7 @@ ${t}` : `"use strict"; return (${t});`;
46357
46359
  const glyph = bevelText("span", ["display:inline-grid", "flex:none", "padding-top:1px", `color:${tone.edge}`]);
46358
46360
  glyph.innerHTML = tone.icon;
46359
46361
  protectIcon(glyph.firstElementChild, 16);
46360
- const text = bevelText("span", ["flex:1 1 auto", "min-width:120px", ...RULES.toastTitle, "overflow-wrap:anywhere"], message);
46362
+ const text = bevelText("span", ["flex:1 1 auto", "min-width:120px", ...RULES.toastTitle, "overflow-wrap:anywhere"], message2);
46361
46363
  el5.append(glyph, text);
46362
46364
  return el5;
46363
46365
  }
@@ -46407,34 +46409,34 @@ ${t}` : `"use strict"; return (${t});`;
46407
46409
  after?.();
46408
46410
  }, 500);
46409
46411
  }
46410
- function toast2(message, messageType = "success") {
46411
- const el5 = build3(message, TONES[messageType] ? messageType : "success");
46412
+ function toast2(message2, messageType = "success") {
46413
+ const el5 = build3(message2, TONES[messageType] ? messageType : "success");
46412
46414
  el5.addEventListener("click", () => dismiss2(el5));
46413
46415
  reveal(el5);
46414
46416
  setTimeout(() => dismiss2(el5), 6600);
46415
46417
  }
46416
- function toastPersistent(message, messageType = "warning") {
46417
- const existing = activePersistentToasts.get(message);
46418
+ function toastPersistent(message2, messageType = "warning") {
46419
+ const existing = activePersistentToasts.get(message2);
46418
46420
  if (existing) {
46419
46421
  dismiss2(existing);
46420
- activePersistentToasts.delete(message);
46422
+ activePersistentToasts.delete(message2);
46421
46423
  }
46422
- const el5 = build3(message, TONES[messageType] ? messageType : "warning");
46424
+ const el5 = build3(message2, TONES[messageType] ? messageType : "warning");
46423
46425
  set(el5, "cursor", "default");
46424
46426
  const close = closeButton((e) => {
46425
46427
  e.stopPropagation();
46426
46428
  dismiss2(el5, () => {
46427
- if (activePersistentToasts.get(message) === el5) activePersistentToasts.delete(message);
46429
+ if (activePersistentToasts.get(message2) === el5) activePersistentToasts.delete(message2);
46428
46430
  });
46429
46431
  });
46430
46432
  el5.append(close);
46431
- activePersistentToasts.set(message, el5);
46433
+ activePersistentToasts.set(message2, el5);
46432
46434
  reveal(el5);
46433
46435
  }
46434
- function dismissPersistent(message) {
46435
- const el5 = activePersistentToasts.get(message);
46436
+ function dismissPersistent(message2) {
46437
+ const el5 = activePersistentToasts.get(message2);
46436
46438
  if (!el5) return;
46437
- activePersistentToasts.delete(message);
46439
+ activePersistentToasts.delete(message2);
46438
46440
  dismiss2(el5);
46439
46441
  }
46440
46442
  var TONES, CLOSE_ICON, HIDDEN2, activePersistentToasts, toast_default;
@@ -47773,6 +47775,9 @@ ${t}` : `"use strict"; return (${t});`;
47773
47775
  // Who is editing and who wrote what. Not editOnly: a reader still sees names.
47774
47776
  people: { path: "plugins/people.js", editOnly: false, default: false },
47775
47777
  wire: { path: "plugins/wire.js", editOnly: false, default: false },
47778
+ // A visitor's contact form to the document's owner. Not editOnly: visitors are
47779
+ // who send messages.
47780
+ message: { path: "plugins/message.js", editOnly: false, default: false },
47776
47781
  // Edit mode only: the AI comment box is an editing gesture. Opt in with
47777
47782
  // `plugins=ai-edit`; it stays dormant on any host that does not list a ready
47778
47783
  // `ai-edit` helper.
@@ -47821,18 +47826,19 @@ ${t}` : `"use strict"; return (${t});`;
47821
47826
  "plugins/upload.js": () => Promise.resolve().then(() => (init_upload(), upload_exports)),
47822
47827
  "plugins/people.js": () => Promise.resolve().then(() => (init_people(), people_exports)),
47823
47828
  "plugins/wire.js": () => Promise.resolve().then(() => (init_wire(), wire_exports)),
47829
+ "plugins/message.js": () => Promise.resolve().then(() => (init_message(), message_exports)),
47824
47830
  "plugins/ai-edit.js": () => Promise.resolve().then(() => (init_ai_edit(), ai_edit_exports)),
47825
47831
  "plugins/demo.js": () => Promise.resolve().then(() => (init_demo(), demo_exports)),
47826
47832
  "plugins/visitor-drafts.js": () => Promise.resolve().then(() => (init_visitor_drafts(), visitor_drafts_exports)),
47827
47833
  "plugins/source.js": () => Promise.resolve().then(() => (init_source2(), source_exports))
47828
47834
  };
47829
- var PLUGIN_ORDER = ["visitor-drafts", "richclay", "indicator", "sortable", "undo", "quickcrop", "upload", "people", "cms", "sync", "live", "wire", "ai-edit", "demo", "source"];
47835
+ var PLUGIN_ORDER = ["visitor-drafts", "richclay", "indicator", "sortable", "undo", "quickcrop", "upload", "people", "cms", "sync", "live", "message", "wire", "ai-edit", "demo", "source"];
47830
47836
  var IMPLIES = { cms: ["quickcrop", "upload"], "ai-edit": ["wire"] };
47831
47837
  function parseCsv(params, key2, enabled, apply2) {
47832
47838
  const raw = params.get(key2);
47833
47839
  if (!raw) return;
47834
- for (const token of raw.split(",")) {
47835
- const name = token.trim();
47840
+ for (const token2 of raw.split(",")) {
47841
+ const name = token2.trim();
47836
47842
  if (!name) continue;
47837
47843
  if (!PLUGIN_PATHS[name]) {
47838
47844
  console.warn(`clayjs: unknown plugin "${name}"`);
@@ -47975,6 +47981,8 @@ ${t}` : `"use strict"; return (${t});`;
47975
47981
  clay5.people = mod.people;
47976
47982
  clay5.author = mod.author;
47977
47983
  Object.defineProperty(clay5, "me", { get: mod.currentMe, configurable: true, enumerable: true });
47984
+ } else if (path === "plugins/message.js") {
47985
+ clay5.message = mod.message;
47978
47986
  } else if (path === "plugins/wire.js") {
47979
47987
  clay5.wire = mod.wire || mod.default;
47980
47988
  } else if (path === "plugins/ai-edit.js") {