{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "experiment-run",
  "type": "registry:component",
  "title": "Experiment run",
  "description": "Supplied experiment metadata, conditions and ordered protocol steps with explicit statuses and controlled recording actions.",
  "registryDependencies": [
    "https://vlak.dev/r/vlak-base.json",
    "https://vlak.dev/r/inter.json",
    "https://vlak.dev/r/button.json",
    "https://vlak.dev/r/vlak-lib.json"
  ],
  "dependencies": [
    "@stylexjs/stylex"
  ],
  "devDependencies": [
    "@stylexjs/babel-plugin"
  ],
  "docs": "Vlak leaves are StyleX. Compile them with @stylexjs/babel-plugin (Vite: @stylexjs/unplugin, Next: @stylexjs/nextjs-plugin). If you would rather not run a compiler, import @noorddev/vlak-react instead: it ships precompiled with one stylesheet.",
  "files": [
    {
      "path": "vlak/experiment-run.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as stylex from \"@stylexjs/stylex\";\nimport { vlak, mq } from \"./tokens.stylex\";\nimport { rs } from \"./rs\";\nimport { Button } from \"./button\";\n\n\nexport interface ExperimentCondition { id: string; label: React.ReactNode; value: React.ReactNode }\nexport interface ExperimentAction { id: string; label: string; disabled?: boolean }\nexport interface ExperimentStep { id: string; label: string; status: string; details?: React.ReactNode; actions?: readonly ExperimentAction[]; pending?: boolean }\nexport interface ExperimentRunProps extends React.HTMLAttributes<HTMLDivElement> {\n  label: string;\n  runId?: string;\n  protocol?: React.ReactNode;\n  status: string;\n  conditions?: readonly ExperimentCondition[];\n  steps: readonly ExperimentStep[];\n  /** Requests an explicit action. State remains supplied by the application. */\n  onAction?: (stepId: string, actionId: string) => void;\n}\n\nconst styles = stylex.create({\n  root: { display: \"grid\", gap: \"1rem\", minWidth: 0, color: vlak.ink, lineHeight: 1.45, overflowWrap: \"anywhere\" },\n  head: { display: \"flex\", flexWrap: \"wrap\", justifyContent: \"space-between\", alignItems: \"baseline\", gap: \"0.5rem 1rem\" },\n  title: { margin: 0, fontSize: \"1rem\", fontWeight: 600 },\n  context: { margin: 0, fontSize: \"0.75rem\", color: vlak.gray },\n  conditions: { display: \"grid\", gridTemplateColumns: { default: \"repeat(auto-fit, minmax(min(100%, 9rem), 1fr))\", [mq.phone]: \"minmax(0, 1fr)\" }, gap: \"1rem\", margin: 0, paddingBlock: \"1rem\", borderBlockWidth: vlak.hairline, borderBlockStyle: \"solid\", borderBlockColor: vlak.divider },\n  value: { margin: 0, marginTop: \"0.25rem\", fontSize: \"0.875rem\", fontVariantNumeric: \"tabular-nums\" },\n  list: { listStyleType: \"decimal\", paddingInlineStart: \"1.5rem\", margin: 0 },\n  step: { paddingBlock: \"0.75rem\", paddingInlineStart: \"0.25rem\", borderBottomWidth: vlak.hairline, borderBottomStyle: \"solid\", borderBottomColor: vlak.divider, fontSize: \"0.875rem\" },\n  details: { marginBlock: \"0.5rem\", fontSize: \"0.875rem\", maxWidth: \"66ch\" },\n  actions: { display: \"flex\", flexWrap: \"wrap\", gap: \"0.5rem\", marginTop: \"0.5rem\" },\n  action: { flexShrink: 0 },\n});\n\n/** Supplied run metadata and protocol steps, with application-owned transitions. */\nexport const ExperimentRun = React.forwardRef<HTMLDivElement, ExperimentRunProps>(function ExperimentRun({ label, runId, protocol, status, conditions = [], steps, onAction, className, style, children, ...props }, ref) {\n  const id = React.useId();\n  const root = rs([\"rs-experiment-run\", className], styles.root);\n  const head = rs([\"rs-experiment-run-head\"], styles.head);\n  const title = rs([\"rs-experiment-run-title\"], styles.title);\n  const context = rs([\"rs-experiment-run-context\"], styles.context);\n  const conditionsStyle = rs([\"rs-experiment-run-conditions\"], styles.conditions);\n  const valueStyle = rs([\"rs-experiment-run-value\"], styles.value);\n  const list = rs([\"rs-experiment-run-list\"], styles.list);\n  const stepStyle = rs([\"rs-experiment-run-step\"], styles.step);\n  const details = rs([\"rs-experiment-run-details\"], styles.details);\n  const actions = rs([\"rs-experiment-run-actions\"], styles.actions);\n  const actionStyle = rs([\"rs-experiment-run-action\"], styles.action);\n  return <div ref={ref} role=\"group\" aria-label={label} {...props} className={root.className} style={{ ...root.style, ...style }}>\n    <div {...head}><p {...title}>{label}</p><span {...context}>{status.trim() || \"Run status not supplied\"}</span></div>\n    {runId != null && <p {...context}>Run {runId}</p>}\n    {protocol != null && <div {...context}>{protocol}</div>}\n    {conditions.length > 0 && <dl {...conditionsStyle}>{conditions.map(condition => <div key={condition.id}><dt {...context}>{condition.label}</dt><dd {...valueStyle}>{condition.value == null || condition.value === \"\" ? \"Not supplied\" : condition.value}</dd></div>)}</dl>}\n    {steps.length === 0 ? <p {...context}>No steps supplied</p> : <ol {...list}>{steps.map((step, index) => <li key={step.id} {...stepStyle} aria-busy={step.pending || undefined}>\n      <div {...head}><span>{step.label}</span><span {...context} id={`${id}-${index}-status`}>{step.status.trim() || \"Step status not supplied\"}</span></div>\n      {step.details != null && <div {...details}>{step.details}</div>}\n      <span {...context} role=\"status\">{step.pending ? \"Update pending\" : null}</span>\n      {onAction && !!step.actions?.length && <div {...actions}>{step.actions.map(action => <Button variant=\"ghost\" size=\"sm\" key={action.id} {...actionStyle} type=\"button\" disabled={step.pending || action.disabled} aria-label={`${action.label}: step ${index + 1}, ${step.label}`} aria-describedby={`${id}-${index}-status`} onClick={() => onAction(step.id, action.id)}>{action.label}</Button>)}</div>}\n    </li>)}</ol>}\n    {children}\n  </div>;\n});\n",
      "type": "registry:component",
      "target": "components/vlak/experiment-run.tsx"
    },
    {
      "path": "vlak/styles/experiment-run.css",
      "content": "/* ── experiment-run: generated from packages/react/src/components/experiment-run.tsx ── */\n.rs-experiment-run{display:grid;gap:1rem;min-width:0;color:var(--text);line-height:1.45;overflow-wrap:anywhere}\n.rs-experiment-run-head{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:baseline;gap:0.5rem 1rem}\n.rs-experiment-run-title{margin:0;font-size:1rem;font-weight:600}\n.rs-experiment-run-context{margin:0;font-size:0.75rem;color:var(--text-secondary)}\n.rs-experiment-run-conditions{display:grid;grid-template-columns:repeat(auto-fit, minmax(min(100%, 9rem), 1fr));gap:1rem;margin:0;padding-block:1rem;border-block-width:1px;border-block-style:solid;border-block-color:var(--divider)}\n@media (max-width: 640px){.rs-experiment-run-conditions{grid-template-columns:minmax(0, 1fr)}}\n.rs-experiment-run-value{margin:0;margin-top:0.25rem;font-size:0.875rem;font-variant-numeric:tabular-nums}\n.rs-experiment-run-list{list-style-type:decimal;padding-inline-start:1.5rem;margin:0}\n.rs-experiment-run-step{padding-block:0.75rem;padding-inline-start:0.25rem;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:var(--divider);font-size:0.875rem}\n.rs-experiment-run-details{margin-block:0.5rem;font-size:0.875rem;max-width:66ch}\n.rs-experiment-run-actions{display:flex;flex-wrap:wrap;gap:0.5rem;margin-top:0.5rem}\n.rs-experiment-run-action{flex-shrink:0}\n",
      "type": "registry:file",
      "target": "styles/vlak/experiment-run.css"
    }
  ],
  "meta": {
    "vlak": {
      "category": "science",
      "classes": [
        "rs-experiment-run",
        "rs-experiment-run-head",
        "rs-experiment-run-title",
        "rs-experiment-run-context",
        "rs-experiment-run-conditions",
        "rs-experiment-run-value",
        "rs-experiment-run-list",
        "rs-experiment-run-step",
        "rs-experiment-run-details",
        "rs-experiment-run-actions",
        "rs-experiment-run-action"
      ],
      "snippet": "<div class=\"rs-experiment-run\" role=\"group\" aria-label=\"Optical measurement\"><div class=\"rs-experiment-run-head\"><p class=\"rs-experiment-run-title\">Optical measurement</p><span class=\"rs-experiment-run-context\">Awaiting review</span></div><p class=\"rs-experiment-run-context\">Run 042</p><ol class=\"rs-experiment-run-list\"><li class=\"rs-experiment-run-step\"><div class=\"rs-experiment-run-head\"><span>Review acquisition</span><span class=\"rs-experiment-run-context\">Not reviewed</span></div></li></ol></div>",
      "cssOnly": false,
      "registryDependencies": [
        "button",
        "vlak-lib"
      ],
      "aliases": [
        "Protocol run",
        "Lab notebook run",
        "Experiment record",
        "Acquisition workflow",
        "Scientific workflow"
      ],
      "example": "import { ExperimentRun } from \"@noorddev/vlak-react\";\n\n<ExperimentRun label=\"Optical measurement\" runId=\"042\" protocol=\"Protocol revision 3\" status=\"Awaiting review\" conditions={[{ id: \"temperature\", label: \"Recorded temperature\", value: \"21.4 °C\" }]} steps={[\n  { id: \"acquisition\", label: \"Acquire readings\", status: \"Recorded\", details: \"Data file supplied by the instrument\" },\n  { id: \"review\", label: \"Review acquisition\", status: \"Not reviewed\" },\n]} />",
      "usage": {
        "use": [
          "Lab notebooks, acquisition workspaces and protocol review with supplied run state.",
          "Pass explicit step actions and onAction to request changes; update supplied statuses after persistence.",
          "Keep protocol links, conditions and step details with the run they describe."
        ],
        "avoid": [
          "Inferring completion, experimental validity or the next scientific procedure.",
          "Treating an action request as proof that an instrument ran or that data was saved."
        ]
      },
      "keyboard": [
        {
          "keys": "Tab",
          "does": "Moves between supplied links and enabled step actions."
        },
        {
          "keys": "Enter, Space",
          "does": "Requests the focused step action through onAction."
        }
      ],
      "a11y": [
        "A named group contains conditions as a description list and steps as an ordered list.",
        "Action names include their step number and label; the current step status is their accessible description.",
        "Pending steps keep their recorded state, show pending text and disable actions. No internal success state is invented.",
        "Zero-valued conditions remain visible and missing conditions are explicitly labelled. Buttons are at least 44px with visible focus rings.",
        "The root div forwards native attributes and its ref."
      ]
    }
  }
}
