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:20:53Z
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:53Z
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:53Z
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:20:54Z
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 />);