@intentius/behold 0.29.7 → 0.29.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/behold",
3
- "version": "0.29.7",
3
+ "version": "0.29.8",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "description": "behold — a live control plane on chant. See your whole estate (every substrate in one graph), coloured by drift; act through delegated, gated Ops.",
package/web/app.js CHANGED
@@ -8,11 +8,12 @@
8
8
  // floating control panel's chrome (panel.js — drag/snap/collapse/tabs, persisted
9
9
  // position), and the theme picker into the panel's View-tab slot (a stable element
10
10
  // renderPanelView never rewrites, so the select mounts once and survives re-renders).
11
+ import "./view-repo.js"; // behold#571: first, so every read carries the page's ?repo=
11
12
  import { createRefreshQueue } from "./refresh-queue.js";
12
13
  import { readViewQuery, writeViewQuery, settleView, settlePlace, refusalLine } from "./view-url.js";
13
14
  import { quietCardLines, readEmbed, selectMessage, viewFromMessage } from "./embed.js";
14
15
  import { WORDS as POP_WORDS, cardRoot as popCardRoot, placePopover, popReduce, popState, resourcePopover, rootPopover } from "./kiosk-popover.js";
15
- import { cssString, hoverCss, hoverFromMessage, hoverLinks, hoverMessage, isLightBg, rootColour, rootText } from "./kiosk-roots.js";
16
+ import { cssString, hoverCss, hoverFromMessage, hoverLinks, hoverMessage, isLightBg, rootPaint, rootText } from "./kiosk-roots.js";
16
17
  import { gateCards } from "./workspace-gates.js";
17
18
  import { cornerOf, estateLine, hrefOf as tgHref, legend as tgLegend, markLine, rootRows as tgRootRows, waveCards } from "./terragucci.js";
18
19
  import { laneElement as tgLane, rootOfCard as tgRootOfCard, historyElement as tgHistoryLane } from "./terragucci-timeline.js";
@@ -4179,7 +4180,7 @@ function kioskDots(svg) {
4179
4180
  if (!roots.length) return;
4180
4181
  const light = kioskLight();
4181
4182
  const dotBy = (text, root) => {
4182
- const colour = rootColour(root, roots, { light });
4183
+ const colour = rootPaint(root, roots, { light });
4183
4184
  if (!colour) return;
4184
4185
  let bb;
4185
4186
  try {
@@ -4194,7 +4195,8 @@ function kioskDots(svg) {
4194
4195
  c.setAttribute("cx", String(bb.x + bb.width + 9));
4195
4196
  c.setAttribute("cy", String(bb.y + bb.height / 2));
4196
4197
  c.setAttribute("r", "5.5");
4197
- c.setAttribute("fill", colour);
4198
+ // The theme's own root colour (--root-<i>): a style, since a fill attribute takes no var().
4199
+ c.style.fill = colour;
4198
4200
  c.setAttribute("pointer-events", "none");
4199
4201
  text.parentNode.insertBefore(c, text.nextSibling);
4200
4202
  };
@@ -4292,7 +4294,9 @@ function kioskPopRender() {
4292
4294
  }
4293
4295
  if (content.more > 0) ul.appendChild(Object.assign(document.createElement("li"), { className: "k-pop-more", textContent: `and ${content.more} more` }));
4294
4296
  }
4295
- add("p", "k-pop-hint", kioskPop.pinned ? POP_WORDS.pinnedHint : POP_WORDS.pinHint);
4297
+ // behold#571: a finger opens the popover with a tap, so a touch screen reads the tap's words.
4298
+ const touchOnly = typeof matchMedia === "function" && matchMedia("(hover: none)").matches;
4299
+ add("p", "k-pop-hint", kioskPop.pinned ? (touchOnly ? POP_WORDS.tapPinnedHint : POP_WORDS.pinnedHint) : POP_WORDS.pinHint);
4296
4300
  pop.hidden = false;
4297
4301
  const cb = card.getBoundingClientRect();
4298
4302
  const pb = pop.getBoundingClientRect();
@@ -4339,6 +4343,8 @@ function wireKioskFrame() {
4339
4343
  window.addEventListener("message", (e) => {
4340
4344
  if (!embedOpts.host || e.origin !== embedOpts.host) return;
4341
4345
  if (e.data && e.data.type === "behold:unpin") return kioskPopEvent({ type: "outside" });
4346
+ // behold#571: the kiosk's theme picker moved; follow it here without a reload.
4347
+ if (e.data && e.data.type === "behold:theme" && typeof e.data.name === "string") return void setTheme(e.data.name, { persist: false });
4342
4348
  const h = hoverFromMessage(e.data);
4343
4349
  if (h === undefined) return;
4344
4350
  kioskHostHover = h;
@@ -5982,6 +5988,54 @@ function ensureZoomControls(host) {
5982
5988
  },
5983
5989
  { passive: false },
5984
5990
  );
5991
+ // behold#571: on a touch screen (the kiosk on a phone), one finger pans and two pinch to zoom.
5992
+ let touch = null;
5993
+ const touchPoints = (e) => [...e.touches].map((t) => ({ x: t.clientX, y: t.clientY }));
5994
+ host.addEventListener(
5995
+ "touchstart",
5996
+ (e) => {
5997
+ if (!vb) return;
5998
+ touch = { pts: touchPoints(e), moved: false };
5999
+ },
6000
+ { passive: true },
6001
+ );
6002
+ host.addEventListener(
6003
+ "touchmove",
6004
+ (e) => {
6005
+ const s = currentSvg();
6006
+ if (!touch || !vb || !s) return;
6007
+ const pts = touchPoints(e);
6008
+ const r = s.getBoundingClientRect();
6009
+ e.preventDefault();
6010
+ if (pts.length >= 2 && touch.pts.length >= 2) {
6011
+ const dist = (p) => Math.hypot(p[0].x - p[1].x, p[0].y - p[1].y) || 1;
6012
+ const mid = (p) => ({ x: (p[0].x + p[1].x) / 2, y: (p[0].y + p[1].y) / 2 });
6013
+ const f = dist(touch.pts) / dist(pts);
6014
+ const m = mid(pts);
6015
+ const m0 = mid(touch.pts);
6016
+ const cx = vb[0] + ((m.x - r.left) / r.width) * vb[2];
6017
+ const cy = vb[1] + ((m.y - r.top) / r.height) * vb[3];
6018
+ const nw = Math.min(vbInit[2] * 3, Math.max(vbInit[2] / 60, vb[2] * f));
6019
+ const nh = nw * (vb[3] / vb[2]);
6020
+ vb[0] = cx - ((cx - vb[0]) * nw) / vb[2] - ((m.x - m0.x) / r.width) * nw;
6021
+ vb[1] = cy - ((cy - vb[1]) * nh) / vb[3] - ((m.y - m0.y) / r.height) * nh;
6022
+ vb[2] = nw;
6023
+ vb[3] = nh;
6024
+ } else if (pts.length === 1 && touch.pts.length === 1) {
6025
+ vb[0] -= ((pts[0].x - touch.pts[0].x) / r.width) * vb[2];
6026
+ vb[1] -= ((pts[0].y - touch.pts[0].y) / r.height) * vb[3];
6027
+ }
6028
+ if (!touch.moved && touch.pts.length && Math.abs(pts[0].x - touch.pts[0].x) + Math.abs(pts[0].y - touch.pts[0].y) > 3) touch.moved = true;
6029
+ if (touch.moved) panMoved = true;
6030
+ touch.pts = pts;
6031
+ vbAtFit = false;
6032
+ applyVB();
6033
+ },
6034
+ { passive: false },
6035
+ );
6036
+ host.addEventListener("touchend", (e) => {
6037
+ touch = e.touches.length ? { pts: touchPoints(e), moved: !!(touch && touch.moved) } : null;
6038
+ });
5985
6039
  let drag = false;
5986
6040
  let px = 0;
5987
6041
  let py = 0;
@@ -6478,6 +6532,8 @@ function ensureLayoutDrag(host) {
6478
6532
  layoutWired = true;
6479
6533
  host.addEventListener("pointerdown", (e) => {
6480
6534
  if (e.button !== 0 || !layoutIndex) return;
6535
+ // behold#571: in the kiosk's bare frame a finger pans the graph; it never moves a card.
6536
+ if (e.pointerType === "touch" && embedOpts.bare) return;
6481
6537
  const t = layoutTargetOf(e);
6482
6538
  if (!t || !currentSvg()) return;
6483
6539
  layoutDrag = { ...t, x0: e.clientX, y0: e.clientY, base: layoutDeltas[t.id] || {}, moved: false };
package/web/index.html CHANGED
@@ -426,6 +426,8 @@
426
426
  border: 1px solid var(--rule); border-radius: 8px; box-shadow: 0 6px 22px color-mix(in srgb, var(--shadow) 70%, transparent);
427
427
  font: 14px/1.4 var(--font-ui); pointer-events: auto; }
428
428
  #k-pop[hidden] { display: none; }
429
+ /* behold#571: on a phone the popover takes the frame's width, less a margin. */
430
+ @media (max-width: 600px) { #k-pop { max-width: calc(100vw - 16px); } }
429
431
  #k-pop.k-pinned { border-color: var(--focus); }
430
432
  #k-pop p { margin: 0.25em 0; }
431
433
  .k-pop-addr { font: 700 1.05em var(--font-mono); overflow-wrap: anywhere; }