Interaction And Draw Widgets

Handle

jsx
// generated by @readrun/widgets — edit .readrun/widgets/handle-demo.tsx, then re-run rr
// @readrun/widgets@10f3ae2 — generated 2026-08-01T09:21:00Z
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));

// globals:react
var require_react = __commonJS({
  "globals:react"(exports, module) {
    module.exports = globalThis.React;
  }
});

// globals:react/jsx-runtime
var require_jsx_runtime = __commonJS({
  "globals:react/jsx-runtime"(exports, module) {
    var React6 = globalThis.React;
    function jsx8(type, props, key) {
      const nextProps = key === void 0 ? props : Object.assign({}, props, { key });
      return React6.createElement(type, nextProps);
    }
    module.exports = { Fragment: React6.Fragment, jsx: jsx8, jsxs: jsx8 };
  }
});

// docs/.readrun/widgets/handle-demo.tsx
var import_react9 = __toESM(require_react(), 1);

// src/widgets/interaction/useDrag.ts
var import_react = __toESM(require_react(), 1);

// src/widgets/interaction/coords.ts
function applyMatrix(m, x, y) {
  return {
    x: m.a * x + m.c * y + m.e,
    y: m.b * x + m.d * y + m.f
  };
}
function screenToViewBox(target, clientX, clientY) {
  const ctm = target.getScreenCTM();
  if (!ctm) return null;
  const inv = ctm.inverse();
  return applyMatrix(inv, clientX, clientY);
}

// src/widgets/interaction/useDrag.ts
function useDrag(ref, onMove, opts) {
  const [isDragging, setIsDragging] = (0, import_react.useState)(false);
  const onMoveRef = (0, import_react.useRef)(onMove);
  const optsRef = (0, import_react.useRef)(opts);
  onMoveRef.current = onMove;
  optsRef.current = opts;
  (0, import_react.useEffect)(() => {
    const el = ref.current;
    if (!el) return;
    el.style.touchAction = "none";
    let dragging = false;
    function toViewBox(clientX, clientY) {
      return screenToViewBox(el, clientX, clientY);
    }
    function onPointerDown(e) {
      e.preventDefault();
      e.currentTarget.setPointerCapture(e.pointerId);
      dragging = true;
      setIsDragging(true);
      const pt = toViewBox(e.clientX, e.clientY);
      if (pt) optsRef.current?.onStart?.(pt.x, pt.y);
    }
    function onPointerMove(e) {
      if (!dragging) return;
      const pt = toViewBox(e.clientX, e.clientY);
      if (pt) onMoveRef.current(pt.x, pt.y);
    }
    function onPointerUp(e) {
      if (!dragging) return;
      dragging = false;
      setIsDragging(false);
      const pt = toViewBox(e.clientX, e.clientY);
      if (pt) optsRef.current?.onEnd?.(pt.x, pt.y);
    }
    function onPointerCancel() {
      dragging = false;
      setIsDragging(false);
    }
    el.addEventListener("pointerdown", onPointerDown);
    el.addEventListener("pointermove", onPointerMove);
    el.addEventListener("pointerup", onPointerUp);
    el.addEventListener("pointercancel", onPointerCancel);
    return () => {
      el.removeEventListener("pointerdown", onPointerDown);
      el.removeEventListener("pointermove", onPointerMove);
      el.removeEventListener("pointerup", onPointerUp);
      el.removeEventListener("pointercancel", onPointerCancel);
      el.style.touchAction = "";
    };
  }, [ref]);
  return { isDragging };
}

// src/widgets/interaction/useRaf.ts
var import_react2 = __toESM(require_react(), 1);

// src/widgets/interaction/useTrace.ts
var import_react3 = __toESM(require_react(), 1);

// src/widgets/interaction/usePointer.ts
var import_react4 = __toESM(require_react(), 1);

// src/widgets/interaction/Handle.tsx
var import_react5 = __toESM(require_react(), 1);
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
function Handle({
  x,
  y,
  onMove,
  r = 6,
  fill = "var(--text, #1f2328)",
  ariaLabel
}) {
  const ref = (0, import_react5.useRef)(null);
  useDrag(ref, onMove);
  const size = r * 2;
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
    "rect",
    {
      ref,
      x: x - r,
      y: y - r,
      width: size,
      height: size,
      fill,
      style: { cursor: "grab" },
      "aria-label": ariaLabel,
      role: ariaLabel ? "slider" : void 0
    }
  );
}

// src/widgets/primitives/index.tsx
var import_react8 = __toESM(require_react());

// src/presentation/components/ui/Label.tsx
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);

// src/presentation/components/ui/Slider.tsx
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);

// src/presentation/components/ui/Switch.tsx
var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);

// src/widgets/primitives/WidgetLayout.tsx
var import_react6 = __toESM(require_react(), 1);
var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
function VisualSlot({ children }) {
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
}
function ControlsSlot({ children }) {
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
}
function AsideSlot({ children }) {
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
}
VisualSlot.__widgetSlot = "visual";
ControlsSlot.__widgetSlot = "controls";
AsideSlot.__widgetSlot = "aside";
function extractSlots(children) {
  const slots = {};
  import_react6.default.Children.forEach(children, (child) => {
    if (import_react6.default.isValidElement(child)) {
      const t = child.type;
      if (t?.__widgetSlot) {
        slots[t.__widgetSlot] = child;
      }
    }
  });
  return slots;
}
function WidgetLayoutImpl(props) {
  const arrangement = props.arrangement ?? "visual-left";
  const slots = extractSlots(props.children);
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `readrun-widget readrun-widget--${arrangement}`, children: [
    (props.title || props.subtitle || props.headMeta) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
      "div",
      {
        className: "readrun-widget__head",
        style: { display: "flex", justifyContent: "space-between" },
        children: [
          /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
            props.title && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "readrun-widget__title", children: props.title }),
            props.subtitle && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "readrun-widget__subtitle", children: props.subtitle })
          ] }),
          props.headMeta && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children: props.headMeta })
        ]
      }
    ),
    /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "readrun-widget__body", children: [
      slots["visual"] && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "readrun-widget__visual", children: slots["visual"] }),
      (slots["controls"] || slots["aside"]) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "readrun-widget__sidebar", children: [
        slots["controls"] && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "readrun-widget__controls", children: slots["controls"] }),
        slots["aside"] && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "readrun-widget__aside", children: [
          /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "readrun-widget__aside-label", children: "What to notice" }),
          slots["aside"]
        ] })
      ] })
    ] })
  ] });
}
var WidgetLayout = Object.assign(WidgetLayoutImpl, {
  Visual: VisualSlot,
  Controls: ControlsSlot,
  Aside: AsideSlot
});

// src/widgets/primitives/FormulaSteps.tsx
var import_react7 = __toESM(require_react(), 1);
var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);

// src/widgets/primitives/index.tsx
function Shell({
  title,
  meta,
  children
}) {
  return /* @__PURE__ */ import_react8.default.createElement("div", { className: "viz-shell" }, (title || meta) && /* @__PURE__ */ import_react8.default.createElement("div", { className: "viz-shell-head" }, title && /* @__PURE__ */ import_react8.default.createElement("h2", null, title), meta && /* @__PURE__ */ import_react8.default.createElement("div", { className: "viz-shell-meta" }, meta)), children);
}
function Sub({ children }) {
  return /* @__PURE__ */ import_react8.default.createElement("p", { className: "viz-sub" }, children);
}
function Stage({
  children,
  style
}) {
  return /* @__PURE__ */ import_react8.default.createElement("div", { className: "viz-stage", style }, children);
}
function Stat({
  label,
  value,
  color
}) {
  return /* @__PURE__ */ import_react8.default.createElement("span", { className: "viz-stat" }, /* @__PURE__ */ import_react8.default.createElement("span", null, label), /* @__PURE__ */ import_react8.default.createElement("strong", { className: "viz-stat-value", style: color ? { color } : void 0 }, value));
}

// docs/.readrun/widgets/handle-demo.tsx
var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
function HandleDemo() {
  const [pos, setPos] = (0, import_react9.useState)({ x: 350, y: 190 });
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Shell, { title: "Handle drag", children: [
    /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sub, { children: "Drag the handle to move it. Handle uses useDrag internally and reports absolute SVG coords." }),
    /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", gap: 16, marginBottom: 12 }, children: [
      /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Stat, { label: "x", value: pos.x.toFixed(0) }),
      /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Stat, { label: "y", value: pos.y.toFixed(0) })
    ] }),
    /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Stage, { children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
      "svg",
      {
        viewBox: "0 0 700 380",
        width: 700,
        height: 380,
        style: { display: "block", maxWidth: "100%" },
        children: [
          Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
            "line",
            {
              x1: (i + 1) * 700 / 8,
              y1: 0,
              x2: (i + 1) * 700 / 8,
              y2: 380,
              stroke: "#e2e8f0",
              strokeWidth: 1
            },
            `vg${i}`
          )),
          Array.from({ length: 4 }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
            "line",
            {
              x1: 0,
              y1: (i + 1) * 380 / 4,
              x2: 700,
              y2: (i + 1) * 380 / 4,
              stroke: "#e2e8f0",
              strokeWidth: 1
            },
            `hg${i}`
          )),
          /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: pos.x, y1: 0, x2: pos.x, y2: 380, stroke: "#cbd5e1", strokeWidth: 1, strokeDasharray: "4 3" }),
          /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("line", { x1: 0, y1: pos.y, x2: 700, y2: pos.y, stroke: "#cbd5e1", strokeWidth: 1, strokeDasharray: "4 3" }),
          /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
            Handle,
            {
              x: pos.x,
              y: pos.y,
              onMove: (x, y) => setPos({ x, y }),
              r: 10
            }
          )
        ]
      }
    ) })
  ] });
}

// docs/.readrun/widgets/handle-demo.readrun-entry.ts
render(<HandleDemo />);

useRaf and easing

jsx
// generated by @readrun/widgets — edit .readrun/widgets/easing-anim-demo.tsx, then re-run rr
// @readrun/widgets@10f3ae2 — generated 2026-08-01T09:20:59Z
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));

// globals:react
var require_react = __commonJS({
  "globals:react"(exports, module) {
    module.exports = globalThis.React;
  }
});

// globals:react/jsx-runtime
var require_jsx_runtime = __commonJS({
  "globals:react/jsx-runtime"(exports, module) {
    var React6 = globalThis.React;
    function jsx8(type, props, key) {
      const nextProps = key === void 0 ? props : Object.assign({}, props, { key });
      return React6.createElement(type, nextProps);
    }
    module.exports = { Fragment: React6.Fragment, jsx: jsx8, jsxs: jsx8 };
  }
});

// docs/.readrun/widgets/easing-anim-demo.tsx
var import_react9 = __toESM(require_react(), 1);

// src/widgets/interaction/useDrag.ts
var import_react = __toESM(require_react(), 1);

// src/widgets/interaction/useRaf.ts
var import_react2 = __toESM(require_react(), 1);
var RAF_MAX_DT = 100;
function clampDt(dt) {
  return Math.min(Math.max(dt, 0), RAF_MAX_DT);
}
function useRaf(callback, opts) {
  const enabled = opts?.enabled ?? true;
  const callbackRef = (0, import_react2.useRef)(callback);
  callbackRef.current = callback;
  (0, import_react2.useEffect)(() => {
    if (!enabled) return;
    let rafId;
    let last = null;
    function frame(now) {
      const dt = last === null ? 0 : clampDt(now - last);
      last = now;
      callbackRef.current(dt);
      rafId = requestAnimationFrame(frame);
    }
    rafId = requestAnimationFrame(frame);
    return () => {
      cancelAnimationFrame(rafId);
    };
  }, [enabled]);
}

// src/widgets/interaction/useTrace.ts
var import_react3 = __toESM(require_react(), 1);

// src/widgets/interaction/usePointer.ts
var import_react4 = __toESM(require_react(), 1);

// src/widgets/interaction/Handle.tsx
var import_react5 = __toESM(require_react(), 1);
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);

// src/widgets/draw/easing.ts
function linear(t) {
  return t;
}
function cubicOut(t) {
  const u = 1 - t;
  return 1 - u * u * u;
}
function quadInOut(t) {
  if (t < 0.5) return 2 * t * t;
  const u = 1 - t;
  return 1 - 2 * u * u;
}
function lerp(a, b, t) {
  return a + (b - a) * t;
}
function tween(opts) {
  const { from, to, startedAt, durationMs, easing = cubicOut } = opts;
  if (durationMs <= 0) {
    return (_now) => ({ value: to, done: true });
  }
  return (now) => {
    const elapsed = Math.max(0, Math.min(durationMs, now - startedAt));
    const t = elapsed / durationMs;
    const easedT = easing(t);
    const value = lerp(from, to, easedT);
    return { value, done: elapsed >= durationMs };
  };
}

// src/widgets/primitives/index.tsx
var import_react8 = __toESM(require_react());

// src/presentation/components/ui/Label.tsx
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);

// src/presentation/components/ui/Slider.tsx
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);

// src/presentation/components/ui/Switch.tsx
var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);

// src/widgets/primitives/WidgetLayout.tsx
var import_react6 = __toESM(require_react(), 1);
var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
function VisualSlot({ children }) {
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
}
function ControlsSlot({ children }) {
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
}
function AsideSlot({ children }) {
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_jsx_runtime5.Fragment, { children });
}
VisualSlot.__widgetSlot = "visual";
ControlsSlot.__widgetSlot = "controls";
AsideSlot.__widgetSlot = "aside";
function extractSlots(children) {
  const slots = {};
  import_react6.default.Children.forEach(children, (child) => {
    if (import_react6.default.isValidElement(child)) {
      const t = child.type;
      if (t?.__widgetSlot) {
        slots[t.__widgetSlot] = child;
      }
    }
  });
  return slots;
}
function WidgetLayoutImpl(props) {
  const arrangement = props.arrangement ?? "visual-left";
  const slots = extractSlots(props.children);
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `readrun-widget readrun-widget--${arrangement}`, children: [
    (props.title || props.subtitle || props.headMeta) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
      "div",
      {
        className: "readrun-widget__head",
        style: { display: "flex", justifyContent: "space-between" },
        children: [
          /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { children: [
            props.title && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { className: "readrun-widget__title", children: props.title }),
            props.subtitle && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "readrun-widget__subtitle", children: props.subtitle })
          ] }),
          props.headMeta && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children: props.headMeta })
        ]
      }
    ),
    /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "readrun-widget__body", children: [
      slots["visual"] && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "readrun-widget__visual", children: slots["visual"] }),
      (slots["controls"] || slots["aside"]) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "readrun-widget__sidebar", children: [
        slots["controls"] && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "readrun-widget__controls", children: slots["controls"] }),
        slots["aside"] && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "readrun-widget__aside", children: [
          /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "readrun-widget__aside-label", children: "What to notice" }),
          slots["aside"]
        ] })
      ] })
    ] })
  ] });
}
var WidgetLayout = Object.assign(WidgetLayoutImpl, {
  Visual: VisualSlot,
  Controls: ControlsSlot,
  Aside: AsideSlot
});

// src/widgets/primitives/FormulaSteps.tsx
var import_react7 = __toESM(require_react(), 1);
var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);

// src/widgets/primitives/index.tsx
function Shell({
  title,
  meta,
  children
}) {
  return /* @__PURE__ */ import_react8.default.createElement("div", { className: "viz-shell" }, (title || meta) && /* @__PURE__ */ import_react8.default.createElement("div", { className: "viz-shell-head" }, title && /* @__PURE__ */ import_react8.default.createElement("h2", null, title), meta && /* @__PURE__ */ import_react8.default.createElement("div", { className: "viz-shell-meta" }, meta)), children);
}
function Sub({ children }) {
  return /* @__PURE__ */ import_react8.default.createElement("p", { className: "viz-sub" }, children);
}
function Btn({
  kind = "ghost",
  active,
  children,
  ...rest
}) {
  const cls = `viz-btn viz-btn-${kind}${active ? " viz-btn-active" : ""}`;
  return /* @__PURE__ */ import_react8.default.createElement("button", { className: cls, ...rest }, children);
}
function Stat({
  label,
  value,
  color
}) {
  return /* @__PURE__ */ import_react8.default.createElement("span", { className: "viz-stat" }, /* @__PURE__ */ import_react8.default.createElement("span", null, label), /* @__PURE__ */ import_react8.default.createElement("strong", { className: "viz-stat-value", style: color ? { color } : void 0 }, value));
}

// docs/.readrun/widgets/easing-anim-demo.tsx
var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
var EASINGS = { cubicOut, linear, quadInOut };
var COLORS = {
  cubicOut: "#4f46e5",
  linear: "#0ea5e9",
  quadInOut: "#10b981"
};
var DURATION = 1200;
var BAR_MAX = 540;
function EasingAnimDemo() {
  const [values, setValues] = (0, import_react9.useState)({
    cubicOut: 0,
    linear: 0,
    quadInOut: 0
  });
  const [running, setRunning] = (0, import_react9.useState)(false);
  const startRef = (0, import_react9.useRef)(null);
  const tweensRef = (0, import_react9.useRef)(null);
  function start() {
    const now = performance.now();
    startRef.current = now;
    tweensRef.current = {
      cubicOut: tween({ from: 0, to: BAR_MAX, startedAt: now, durationMs: DURATION, easing: cubicOut }),
      linear: tween({ from: 0, to: BAR_MAX, startedAt: now, durationMs: DURATION, easing: linear }),
      quadInOut: tween({ from: 0, to: BAR_MAX, startedAt: now, durationMs: DURATION, easing: quadInOut })
    };
    setRunning(true);
  }
  function reset() {
    setRunning(false);
    tweensRef.current = null;
    setValues({ cubicOut: 0, linear: 0, quadInOut: 0 });
  }
  useRaf(
    (_dt) => {
      if (!tweensRef.current) return;
      const now = performance.now();
      const next = { cubicOut: 0, linear: 0, quadInOut: 0 };
      let allDone = true;
      for (const key of Object.keys(tweensRef.current)) {
        const { value, done } = tweensRef.current[key](now);
        next[key] = value;
        if (!done) allDone = false;
      }
      setValues(next);
      if (allDone) setRunning(false);
    },
    { enabled: running }
  );
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Shell, { title: "useRaf + tween \u2014 easing animation", children: [
    /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Sub, { children: "Each bar animates from 0 to 100% using a different easing function driven by useRaf." }),
    /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", gap: 10, marginBottom: 20 }, children: [
      /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { kind: "primary", onClick: start, disabled: running, children: "Animate" }),
      /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Btn, { kind: "ghost", onClick: reset, children: "Reset" })
    ] }),
    /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: Object.keys(EASINGS).map((name) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { children: [
      /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", fontSize: 11, color: "#64748b", marginBottom: 4 }, children: [
        /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: { fontFamily: "ui-monospace, monospace", color: COLORS[name] }, children: name }),
        /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Stat, { label: "width", value: `${(values[name] / BAR_MAX * 100).toFixed(0)}%` })
      ] }),
      /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { style: { height: 28, background: "#f1f5f9", borderRadius: 4, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
        "div",
        {
          style: {
            height: "100%",
            width: values[name],
            background: COLORS[name],
            borderRadius: 4,
            transition: "none"
          }
        }
      ) })
    ] }, name)) })
  ] });
}

// docs/.readrun/widgets/easing-anim-demo.readrun-entry.ts
render(<EasingAnimDemo />);

Easing curves

jsx
// generated by @readrun/widgets — edit .readrun/widgets/easing-curves-demo.tsx, then re-run rr
// @readrun/widgets@10f3ae2 — generated 2026-08-01T09:20:59Z
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));

// globals:react
var require_react = __commonJS({
  "globals:react"(exports, module) {
    module.exports = globalThis.React;
  }
});

// globals:react/jsx-runtime
var require_jsx_runtime = __commonJS({
  "globals:react/jsx-runtime"(exports, module) {
    var React5 = globalThis.React;
    function jsx7(type, props, key) {
      const nextProps = key === void 0 ? props : Object.assign({}, props, { key });
      return React5.createElement(type, nextProps);
    }
    module.exports = { Fragment: React5.Fragment, jsx: jsx7, jsxs: jsx7 };
  }
});

// docs/.readrun/widgets/easing-curves-demo.tsx
var import_react4 = __toESM(require_react(), 1);

// src/widgets/draw/palette.ts
function parseHex(hex) {
  if (!/^#[0-9a-fA-F]{6}$/.test(hex)) {
    throw new Error(
      `palette.sequential: invalid hex colour "${hex}". Only 6-digit hex (#rrggbb) is accepted.`
    );
  }
  const v = parseInt(hex.slice(1), 16);
  return [v >> 16 & 255, v >> 8 & 255, v & 255];
}
function toHex(r, g, b) {
  const clamp = (n) => Math.max(0, Math.min(255, Math.round(n)));
  return "#" + [clamp(r), clamp(g), clamp(b)].map((c) => c.toString(16).padStart(2, "0")).join("");
}
var VIRIDIS_STOPS = [
  "#440154",
  "#482475",
  "#414487",
  "#355f8d",
  "#2a788e",
  "#21918c",
  "#22a884",
  "#44bf70",
  "#7ad151",
  "#bddf26",
  "#fde725"
];
var MAGMA_STOPS = [
  "#000004",
  "#180f3d",
  "#440f76",
  "#721f81",
  "#9e2f7f",
  "#cd4071",
  "#f1605d",
  "#fd9668",
  "#feca8d",
  "#fcfdbf"
];
var PLASMA_STOPS = [
  "#0d0887",
  "#46039f",
  "#7201a8",
  "#9c179e",
  "#bd3786",
  "#d8576b",
  "#ed7953",
  "#fb9f3a",
  "#fdc926",
  "#f0f921"
];
function sequential(stops, t) {
  const parsed = stops.map(parseHex);
  const clamped = Math.max(0, Math.min(1, t));
  const n = parsed.length;
  if (n === 1) return toHex(...parsed[0]);
  const scaled = clamped * (n - 1);
  const lo = Math.min(Math.floor(scaled), n - 2);
  const hi = lo + 1;
  const localT = scaled - lo;
  const [r0, g0, b0] = parsed[lo];
  const [r1, g1, b1] = parsed[hi];
  return toHex(r0 + (r1 - r0) * localT, g0 + (g1 - g0) * localT, b0 + (b1 - b0) * localT);
}
var palette = {
  math: {
    accent: "#4f46e5",
    // indigo-600
    accent2: "#0ea5e9",
    // sky-500
    dim: "#cbd5e1",
    // slate-300
    surface: "#ffffff",
    text: "#0f172a",
    // slate-900
    textDim: "#475569"
    // slate-600
  },
  neuro: {
    accent: "#f87171",
    // red-400 — spike colour
    accent2: "#fbbf24",
    // amber-400 — threshold marker
    dim: "#475569",
    // slate-600 — axis lines on dark bg
    surface: "#1e293b",
    // slate-800 — dark stage
    text: "#f1f5f9",
    // slate-100
    textDim: "#94a3b8"
    // slate-400
  },
  network: {
    // six-stop sequential for degree-coloured nodes lives in sequential();
    // these fields cover axes, labels, stage
    accent: "#6366f1",
    // indigo-500 — low-degree nodes
    accent2: "#ef4444",
    // red-500 — high-degree nodes
    dim: "#334155",
    // slate-700
    surface: "#0f172a",
    // slate-900
    text: "#f8fafc",
    // slate-50
    textDim: "#94a3b8"
    // slate-400
  },
  algo: {
    accent: "#0ea5e9",
    // sky-500 — compare highlight
    accent2: "#f43f5e",
    // rose-500 — swap highlight
    dim: "#334155",
    // slate-700
    surface: "#0f172a",
    // slate-900
    text: "#f8fafc",
    textDim: "#94a3b8"
  },
  sequential,
  viridis: (t) => sequential(VIRIDIS_STOPS, t),
  magma: (t) => sequential(MAGMA_STOPS, t),
  plasma: (t) => sequential(PLASMA_STOPS, t),
  viridisStops: VIRIDIS_STOPS,
  magmaStops: MAGMA_STOPS,
  plasmaStops: PLASMA_STOPS
};

// src/widgets/draw/easing.ts
function linear(t) {
  return t;
}
function cubicOut(t) {
  const u = 1 - t;
  return 1 - u * u * u;
}
function quadInOut(t) {
  if (t < 0.5) return 2 * t * t;
  const u = 1 - t;
  return 1 - 2 * u * u;
}

// src/widgets/primitives/index.tsx
var import_react3 = __toESM(require_react());

// src/presentation/components/ui/Label.tsx
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);

// src/presentation/components/ui/Slider.tsx
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);

// src/presentation/components/ui/Switch.tsx
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);

// src/widgets/primitives/WidgetLayout.tsx
var import_react = __toESM(require_react(), 1);
var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
function VisualSlot({ children }) {
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
}
function ControlsSlot({ children }) {
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
}
function AsideSlot({ children }) {
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
}
VisualSlot.__widgetSlot = "visual";
ControlsSlot.__widgetSlot = "controls";
AsideSlot.__widgetSlot = "aside";
function extractSlots(children) {
  const slots = {};
  import_react.default.Children.forEach(children, (child) => {
    if (import_react.default.isValidElement(child)) {
      const t = child.type;
      if (t?.__widgetSlot) {
        slots[t.__widgetSlot] = child;
      }
    }
  });
  return slots;
}
function WidgetLayoutImpl(props) {
  const arrangement = props.arrangement ?? "visual-left";
  const slots = extractSlots(props.children);
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `readrun-widget readrun-widget--${arrangement}`, children: [
    (props.title || props.subtitle || props.headMeta) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
      "div",
      {
        className: "readrun-widget__head",
        style: { display: "flex", justifyContent: "space-between" },
        children: [
          /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
            props.title && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "readrun-widget__title", children: props.title }),
            props.subtitle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "readrun-widget__subtitle", children: props.subtitle })
          ] }),
          props.headMeta && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { children: props.headMeta })
        ]
      }
    ),
    /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "readrun-widget__body", children: [
      slots["visual"] && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "readrun-widget__visual", children: slots["visual"] }),
      (slots["controls"] || slots["aside"]) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "readrun-widget__sidebar", children: [
        slots["controls"] && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "readrun-widget__controls", children: slots["controls"] }),
        slots["aside"] && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "readrun-widget__aside", children: [
          /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "readrun-widget__aside-label", children: "What to notice" }),
          slots["aside"]
        ] })
      ] })
    ] })
  ] });
}
var WidgetLayout = Object.assign(WidgetLayoutImpl, {
  Visual: VisualSlot,
  Controls: ControlsSlot,
  Aside: AsideSlot
});

// src/widgets/primitives/FormulaSteps.tsx
var import_react2 = __toESM(require_react(), 1);
var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);

// src/widgets/primitives/index.tsx
function Shell({
  title,
  meta,
  children
}) {
  return /* @__PURE__ */ import_react3.default.createElement("div", { className: "viz-shell" }, (title || meta) && /* @__PURE__ */ import_react3.default.createElement("div", { className: "viz-shell-head" }, title && /* @__PURE__ */ import_react3.default.createElement("h2", null, title), meta && /* @__PURE__ */ import_react3.default.createElement("div", { className: "viz-shell-meta" }, meta)), children);
}
function Sub({ children }) {
  return /* @__PURE__ */ import_react3.default.createElement("p", { className: "viz-sub" }, children);
}
function Stage({
  children,
  style
}) {
  return /* @__PURE__ */ import_react3.default.createElement("div", { className: "viz-stage", style }, children);
}

// docs/.readrun/widgets/easing-curves-demo.tsx
var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
var EASINGS = [
  ["linear", linear, palette.math.accent2],
  ["cubicOut", cubicOut, palette.math.accent],
  ["quadInOut", quadInOut, "#10b981"]
];
var W = 400;
var H = 300;
var PAD = 40;
var INNER_W = W - PAD * 2;
var INNER_H = H - PAD * 2;
function makePath(fn) {
  const pts = Array.from({ length: 101 }, (_, i) => {
    const t = i / 100;
    const v = fn(t);
    const x = PAD + t * INNER_W;
    const y = PAD + (1 - v) * INNER_H;
    return `${x},${y}`;
  });
  return "M " + pts.join(" L ");
}
function EasingCurvesDemo() {
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Shell, { title: "Easing curves", children: [
    /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Sub, { children: "All three easing functions plotted as y = f(t) over [0, 1]." }),
    /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Stage, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("svg", { viewBox: `0 0 ${W} ${H}`, width: W, height: H, style: { display: "block" }, children: [
      [0, 0.25, 0.5, 0.75, 1].map((v) => {
        const x = PAD + v * INNER_W;
        const y = PAD + (1 - v) * INNER_H;
        return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react4.default.Fragment, { children: [
          /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: x, y1: PAD, x2: x, y2: PAD + INNER_H, stroke: "#f1f5f9", strokeWidth: 1 }),
          /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: PAD, y1: y, x2: PAD + INNER_W, y2: y, stroke: "#f1f5f9", strokeWidth: 1 })
        ] }, v);
      }),
      /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: PAD, y1: PAD + INNER_H, x2: PAD + INNER_W, y2: PAD, stroke: "#e2e8f0", strokeWidth: 1, strokeDasharray: "4 3" }),
      /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: PAD, y1: PAD, x2: PAD, y2: PAD + INNER_H, stroke: "#94a3b8", strokeWidth: 1 }),
      /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("line", { x1: PAD, y1: PAD + INNER_H, x2: PAD + INNER_W, y2: PAD + INNER_H, stroke: "#94a3b8", strokeWidth: 1 }),
      /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: PAD + INNER_W / 2, y: H - 6, textAnchor: "middle", fontSize: 11, fill: "#94a3b8", fontFamily: "ui-monospace, monospace", children: "t" }),
      /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("text", { x: 10, y: PAD + INNER_H / 2, textAnchor: "middle", fontSize: 11, fill: "#94a3b8", fontFamily: "ui-monospace, monospace", transform: `rotate(-90, 10, ${PAD + INNER_H / 2})`, children: "output" }),
      EASINGS.map(([, fn, color]) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: makePath(fn), fill: "none", stroke: color, strokeWidth: 2.5 }, color))
    ] }) }),
    /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", gap: 20, marginTop: 12, flexWrap: "wrap" }, children: EASINGS.map(([name, , color]) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 6, fontSize: 11, color: "#475569" }, children: [
      /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { style: { display: "inline-block", width: 24, height: 3, background: color, borderRadius: 2 } }),
      /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("code", { style: { fontFamily: "ui-monospace, monospace" }, children: name })
    ] }, name)) })
  ] });
}

// docs/.readrun/widgets/easing-curves-demo.readrun-entry.ts
render(<EasingCurvesDemo />);

Palette

jsx
// generated by @readrun/widgets — edit .readrun/widgets/palette-demo.tsx, then re-run rr
// @readrun/widgets@10f3ae2 — generated 2026-08-01T09:21:00Z
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));

// globals:react
var require_react = __commonJS({
  "globals:react"(exports, module) {
    module.exports = globalThis.React;
  }
});

// globals:react/jsx-runtime
var require_jsx_runtime = __commonJS({
  "globals:react/jsx-runtime"(exports, module) {
    var React5 = globalThis.React;
    function jsx7(type, props, key) {
      const nextProps = key === void 0 ? props : Object.assign({}, props, { key });
      return React5.createElement(type, nextProps);
    }
    module.exports = { Fragment: React5.Fragment, jsx: jsx7, jsxs: jsx7 };
  }
});

// docs/.readrun/widgets/palette-demo.tsx
var import_react4 = __toESM(require_react(), 1);

// src/widgets/draw/palette.ts
function parseHex(hex) {
  if (!/^#[0-9a-fA-F]{6}$/.test(hex)) {
    throw new Error(
      `palette.sequential: invalid hex colour "${hex}". Only 6-digit hex (#rrggbb) is accepted.`
    );
  }
  const v = parseInt(hex.slice(1), 16);
  return [v >> 16 & 255, v >> 8 & 255, v & 255];
}
function toHex(r, g, b) {
  const clamp = (n) => Math.max(0, Math.min(255, Math.round(n)));
  return "#" + [clamp(r), clamp(g), clamp(b)].map((c) => c.toString(16).padStart(2, "0")).join("");
}
var VIRIDIS_STOPS = [
  "#440154",
  "#482475",
  "#414487",
  "#355f8d",
  "#2a788e",
  "#21918c",
  "#22a884",
  "#44bf70",
  "#7ad151",
  "#bddf26",
  "#fde725"
];
var MAGMA_STOPS = [
  "#000004",
  "#180f3d",
  "#440f76",
  "#721f81",
  "#9e2f7f",
  "#cd4071",
  "#f1605d",
  "#fd9668",
  "#feca8d",
  "#fcfdbf"
];
var PLASMA_STOPS = [
  "#0d0887",
  "#46039f",
  "#7201a8",
  "#9c179e",
  "#bd3786",
  "#d8576b",
  "#ed7953",
  "#fb9f3a",
  "#fdc926",
  "#f0f921"
];
function sequential(stops, t) {
  const parsed = stops.map(parseHex);
  const clamped = Math.max(0, Math.min(1, t));
  const n = parsed.length;
  if (n === 1) return toHex(...parsed[0]);
  const scaled = clamped * (n - 1);
  const lo = Math.min(Math.floor(scaled), n - 2);
  const hi = lo + 1;
  const localT = scaled - lo;
  const [r0, g0, b0] = parsed[lo];
  const [r1, g1, b1] = parsed[hi];
  return toHex(r0 + (r1 - r0) * localT, g0 + (g1 - g0) * localT, b0 + (b1 - b0) * localT);
}
var palette = {
  math: {
    accent: "#4f46e5",
    // indigo-600
    accent2: "#0ea5e9",
    // sky-500
    dim: "#cbd5e1",
    // slate-300
    surface: "#ffffff",
    text: "#0f172a",
    // slate-900
    textDim: "#475569"
    // slate-600
  },
  neuro: {
    accent: "#f87171",
    // red-400 — spike colour
    accent2: "#fbbf24",
    // amber-400 — threshold marker
    dim: "#475569",
    // slate-600 — axis lines on dark bg
    surface: "#1e293b",
    // slate-800 — dark stage
    text: "#f1f5f9",
    // slate-100
    textDim: "#94a3b8"
    // slate-400
  },
  network: {
    // six-stop sequential for degree-coloured nodes lives in sequential();
    // these fields cover axes, labels, stage
    accent: "#6366f1",
    // indigo-500 — low-degree nodes
    accent2: "#ef4444",
    // red-500 — high-degree nodes
    dim: "#334155",
    // slate-700
    surface: "#0f172a",
    // slate-900
    text: "#f8fafc",
    // slate-50
    textDim: "#94a3b8"
    // slate-400
  },
  algo: {
    accent: "#0ea5e9",
    // sky-500 — compare highlight
    accent2: "#f43f5e",
    // rose-500 — swap highlight
    dim: "#334155",
    // slate-700
    surface: "#0f172a",
    // slate-900
    text: "#f8fafc",
    textDim: "#94a3b8"
  },
  sequential,
  viridis: (t) => sequential(VIRIDIS_STOPS, t),
  magma: (t) => sequential(MAGMA_STOPS, t),
  plasma: (t) => sequential(PLASMA_STOPS, t),
  viridisStops: VIRIDIS_STOPS,
  magmaStops: MAGMA_STOPS,
  plasmaStops: PLASMA_STOPS
};

// src/widgets/primitives/index.tsx
var import_react3 = __toESM(require_react());

// src/presentation/components/ui/Label.tsx
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);

// src/presentation/components/ui/Slider.tsx
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);

// src/presentation/components/ui/Switch.tsx
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);

// src/widgets/primitives/WidgetLayout.tsx
var import_react = __toESM(require_react(), 1);
var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
function VisualSlot({ children }) {
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
}
function ControlsSlot({ children }) {
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
}
function AsideSlot({ children }) {
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children });
}
VisualSlot.__widgetSlot = "visual";
ControlsSlot.__widgetSlot = "controls";
AsideSlot.__widgetSlot = "aside";
function extractSlots(children) {
  const slots = {};
  import_react.default.Children.forEach(children, (child) => {
    if (import_react.default.isValidElement(child)) {
      const t = child.type;
      if (t?.__widgetSlot) {
        slots[t.__widgetSlot] = child;
      }
    }
  });
  return slots;
}
function WidgetLayoutImpl(props) {
  const arrangement = props.arrangement ?? "visual-left";
  const slots = extractSlots(props.children);
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `readrun-widget readrun-widget--${arrangement}`, children: [
    (props.title || props.subtitle || props.headMeta) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
      "div",
      {
        className: "readrun-widget__head",
        style: { display: "flex", justifyContent: "space-between" },
        children: [
          /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
            props.title && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { className: "readrun-widget__title", children: props.title }),
            props.subtitle && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "readrun-widget__subtitle", children: props.subtitle })
          ] }),
          props.headMeta && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { children: props.headMeta })
        ]
      }
    ),
    /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "readrun-widget__body", children: [
      slots["visual"] && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "readrun-widget__visual", children: slots["visual"] }),
      (slots["controls"] || slots["aside"]) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "readrun-widget__sidebar", children: [
        slots["controls"] && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "readrun-widget__controls", children: slots["controls"] }),
        slots["aside"] && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "readrun-widget__aside", children: [
          /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "readrun-widget__aside-label", children: "What to notice" }),
          slots["aside"]
        ] })
      ] })
    ] })
  ] });
}
var WidgetLayout = Object.assign(WidgetLayoutImpl, {
  Visual: VisualSlot,
  Controls: ControlsSlot,
  Aside: AsideSlot
});

// src/widgets/primitives/FormulaSteps.tsx
var import_react2 = __toESM(require_react(), 1);
var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);

// src/widgets/primitives/index.tsx
function Shell({
  title,
  meta,
  children
}) {
  return /* @__PURE__ */ import_react3.default.createElement("div", { className: "viz-shell" }, (title || meta) && /* @__PURE__ */ import_react3.default.createElement("div", { className: "viz-shell-head" }, title && /* @__PURE__ */ import_react3.default.createElement("h2", null, title), meta && /* @__PURE__ */ import_react3.default.createElement("div", { className: "viz-shell-meta" }, meta)), children);
}
function Sub({ children }) {
  return /* @__PURE__ */ import_react3.default.createElement("p", { className: "viz-sub" }, children);
}
function SectionLabel({
  children,
  style
}) {
  return /* @__PURE__ */ import_react3.default.createElement("div", { className: "viz-section-label", style }, children);
}

// docs/.readrun/widgets/palette-demo.tsx
var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
var PALETTES = [
  ["math", palette.math],
  ["neuro", palette.neuro],
  ["network", palette.network],
  ["algo", palette.algo]
];
var FIELDS = ["accent", "accent2", "dim", "surface", "text", "textDim"];
var SEQ_STOPS = ["#6366f1", "#818cf8", "#a5b4fc", "#c7d2fe", "#e0e7ff", "#f5f3ff"];
function PaletteDemo() {
  return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Shell, { title: "Palette swatches", children: [
    /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Sub, { children: "Named palettes and their six fields. Plus a sequential gradient built from custom stops." }),
    PALETTES.map(([name, pal]) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { marginBottom: 24 }, children: [
      /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionLabel, { children: name }),
      /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: FIELDS.map((field) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { textAlign: "center" }, children: [
        /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
          "div",
          {
            style: {
              width: 64,
              height: 40,
              background: pal[field],
              borderRadius: 6,
              border: "1px solid #e2e8f0",
              marginBottom: 4
            }
          }
        ),
        /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 11, color: "#64748b", fontFamily: "ui-monospace, monospace" }, children: field }),
        /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { fontSize: 11, color: "#94a3b8", fontFamily: "ui-monospace, monospace" }, children: pal[field] })
      ] }, field)) })
    ] }, name)),
    /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SectionLabel, { children: "sequential gradient (6 stops, t = 0..1)" }),
    /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", height: 40, borderRadius: 6, overflow: "hidden", border: "1px solid #e2e8f0" }, children: Array.from({ length: 60 }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
      "div",
      {
        style: {
          flex: 1,
          background: palette.sequential(SEQ_STOPS, i / 59)
        }
      },
      i
    )) }),
    /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { style: { display: "flex", gap: 8, marginTop: 8, flexWrap: "wrap" }, children: SEQ_STOPS.map((stop) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
      "div",
      {
        style: {
          width: 20,
          height: 20,
          background: stop,
          borderRadius: 4,
          border: "1px solid #e2e8f0"
        },
        title: stop
      },
      stop
    )) })
  ] });
}

// docs/.readrun/widgets/palette-demo.readrun-entry.ts
render(<PaletteDemo />);