{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "StatusMark-TS-CSS",
	"title": "StatusMark",
	"description": "A 20px status glyph for agent task lists that morphs in place from a dashed idle ring to a spinning or real-progress arc, then draws a check or a cross, with an optional label strike.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "StatusMark.css",
			"target": "@components/StatusMark.css",
			"content": ".status-mark {\n  --sm-size: 20px;\n  --sm-stroke: 2;\n  --sm-color: currentColor;\n  --sm-done: #22c55e;\n  --sm-error: #ef4444;\n  --sm-fill: 0.06;\n  --sm-font: 14px;\n  --sm-draw: 240ms;\n  --sm-strike-delay: 180ms;\n  --sm-muted: 0.55;\n  --sm-check-delay: 120ms;\n  --sm-label-o: 0.65;\n  --sm-ease-out: cubic-bezier(0.23, 1, 0.32, 1);\n  --sm-ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);\n\n  position: relative;\n  display: inline-flex;\n  align-items: center;\n  gap: calc(var(--sm-size) * 0.5);\n  vertical-align: middle;\n  line-height: 1;\n}\n\n.status-mark__glyph {\n  flex: none;\n  overflow: visible;\n  color: var(--sm-color);\n  transition: color 200ms ease;\n}\n\n.status-mark[data-status='done'] .status-mark__glyph {\n  color: var(--sm-done);\n}\n\n.status-mark[data-status='failed'] .status-mark__glyph {\n  color: var(--sm-error);\n}\n\n.status-mark__track {\n  fill: currentColor;\n  stroke: currentColor;\n  stroke-width: var(--sm-stroke);\n  fill-opacity: 0;\n  stroke-opacity: 0;\n  transition:\n    fill-opacity 180ms ease,\n    stroke-opacity 200ms ease;\n}\n\n.status-mark[data-status='running'] .status-mark__track {\n  stroke-opacity: 0.2;\n}\n\n.status-mark[data-status='done'] .status-mark__track,\n.status-mark[data-status='failed'] .status-mark__track {\n  fill-opacity: var(--sm-fill);\n}\n\n.status-mark__ring {\n  fill: none;\n  stroke: currentColor;\n  stroke-width: var(--sm-stroke);\n  stroke-linecap: round;\n  opacity: var(--sm-muted);\n  transition: opacity 200ms ease;\n}\n\n.status-mark[data-status='running'] .status-mark__ring,\n.status-mark[data-status='done'] .status-mark__ring,\n.status-mark[data-status='failed'] .status-mark__ring {\n  opacity: 1;\n}\n\n.status-mark__check,\n.status-mark__cross {\n  fill: none;\n  stroke: currentColor;\n  stroke-width: var(--sm-stroke);\n  stroke-linecap: round;\n  stroke-linejoin: round;\n  stroke-dasharray: 1 2;\n  stroke-dashoffset: 1.05;\n  opacity: 0;\n  transition:\n    stroke-dashoffset 160ms var(--sm-ease-out),\n    opacity 0ms linear 160ms;\n}\n\n.status-mark[data-status='done'] .status-mark__check,\n.status-mark[data-status='failed'] .status-mark__cross,\n.status-mark[data-status='cancelled'] .status-mark__cross {\n  stroke-dashoffset: 0;\n  opacity: 1;\n  transition:\n    stroke-dashoffset var(--sm-draw) var(--sm-ease-out) var(--sm-check-delay),\n    opacity 0ms linear var(--sm-check-delay);\n}\n\n.status-mark__label {\n  position: relative;\n  color: var(--sm-color);\n  font-size: var(--sm-font);\n  line-height: 1.25;\n  opacity: var(--sm-label-o);\n  transition: opacity 200ms ease;\n}\n\n.status-mark[data-status='running'] {\n  --sm-label-o: 1;\n}\n\n.status-mark[data-status='done'] {\n  --sm-label-o: 0.6;\n}\n\n.status-mark[data-status='failed'] {\n  --sm-label-o: 1;\n}\n\n.status-mark[data-status='cancelled'] {\n  --sm-label-o: 0.55;\n}\n\n.status-mark__strike {\n  position: absolute;\n  top: 50%;\n  right: 0;\n  left: 0;\n  height: max(1px, calc(var(--sm-font) / 14));\n  background: currentColor;\n  transform: scaleX(0);\n  transform-origin: left center;\n  translate: 0 -50%;\n  pointer-events: none;\n  transition: transform 160ms var(--sm-ease-out);\n}\n\n.status-mark[data-status='done'][data-strike] .status-mark__strike {\n  transform: scaleX(1);\n  transition: transform 280ms var(--sm-ease-out) var(--sm-strike-delay);\n}\n\n.status-mark__sr {\n  position: absolute;\n  width: 1px;\n  height: 1px;\n  overflow: hidden;\n  clip: rect(0 0 0 0);\n  white-space: nowrap;\n}\n\n@keyframes sm-breathe {\n  50% {\n    opacity: 0.45;\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .status-mark[data-indeterminate] .status-mark__ring {\n    animation: sm-breathe 1400ms var(--sm-ease-in-out) infinite;\n  }\n\n  .status-mark__check,\n  .status-mark__cross {\n    stroke-dashoffset: 0;\n    transition: opacity 200ms ease;\n  }\n\n  .status-mark__strike {\n    opacity: 0;\n    transform: scaleX(1) !important;\n    transition: opacity 200ms ease;\n  }\n\n  .status-mark[data-status='done'][data-strike] .status-mark__strike {\n    opacity: 1;\n  }\n}\n"
		},
		{
			"type": "registry:component",
			"path": "StatusMark.tsx",
			"content": "'use client';\n\nimport React, { useEffect, useLayoutEffect, useRef, type CSSProperties, type ReactNode } from 'react';\nimport { animate, useMotionValue, useReducedMotion } from 'motion/react';\n\nimport './StatusMark.css';\n\nexport type StatusMarkStatus = 'pending' | 'running' | 'done' | 'failed' | 'cancelled';\n\nexport interface StatusMarkProps {\n  status?: StatusMarkStatus;\n  progress?: number;\n  label?: ReactNode;\n  color?: string;\n  doneColor?: string;\n  errorColor?: string;\n  size?: number;\n  strokeWidth?: number;\n  dashes?: number;\n  fontSize?: number;\n  spinDuration?: number;\n  arcLength?: number;\n  drawDuration?: number;\n  fillOpacity?: number;\n  strike?: boolean;\n  strikeDelay?: number;\n  className?: string;\n  style?: CSSProperties;\n}\n\nconst UI = { type: 'spring' as const, duration: 0.3, bounce: 0 };\nconst MORPH = { duration: 0.3, ease: [0.77, 0, 0.175, 1] as [number, number, number, number] };\nconst CHECK = 'M7.5 12.25 10.5 15.25 16.75 8.75';\nconst CROSS = 'M8.5 8.5 15.5 15.5M15.5 8.5 8.5 15.5';\nconst TEXT: Record<StatusMarkStatus, string> = {\n  pending: 'Pending',\n  running: 'In progress',\n  done: 'Completed',\n  failed: 'Failed',\n  cancelled: 'Cancelled'\n};\nconst IDLE_DASH = 0.3;\n\nconst clamp01 = (v: number) => Math.min(1, Math.max(0, v));\n\nconst StatusMark: React.FC<StatusMarkProps> = ({\n  status = 'pending',\n  progress,\n  label,\n  color = 'currentColor',\n  doneColor = '#22c55e',\n  errorColor = '#ef4444',\n  size = 20,\n  strokeWidth = 2,\n  dashes = 8,\n  fontSize = 14,\n  spinDuration = 1100,\n  arcLength = 0.68,\n  drawDuration = 240,\n  fillOpacity = 0.06,\n  strike = true,\n  strikeDelay = 60,\n  className = '',\n  style\n}) => {\n  const reduce = useReducedMotion();\n  const r = 10 - strokeWidth / 2;\n  const C = 2 * Math.PI * r;\n  const P = C / Math.max(1, dashes);\n  const determinate = status === 'running' && typeof progress === 'number' && Number.isFinite(progress);\n  const indeterminate = status === 'running' && !determinate;\n  const solid = status === 'running' || status === 'done' || status === 'failed';\n  const targetArc = indeterminate ? arcLength : determinate ? clamp01(progress as number) : 1;\n\n  const mode = useMotionValue(solid ? 1 : 0);\n  const arc = useMotionValue(targetArc);\n  const travel = useMotionValue(0);\n  const ringRef = useRef<SVGCircleElement>(null);\n  const geo = useRef({ C, P });\n  geo.current = { C, P };\n  const gen = useRef(0);\n\n  const writeDash = () => {\n    const g = geo.current;\n    const m = mode.get();\n    const a = arc.get();\n    const dash = IDLE_DASH * g.P + (a * g.C - IDLE_DASH * g.P) * m;\n    const gap = (1 - IDLE_DASH) * g.P + ((1 - a) * g.C - (1 - IDLE_DASH) * g.P) * m;\n    ringRef.current?.setAttribute('stroke-dasharray', `${Math.max(0, dash)} ${Math.max(0, gap)}`);\n  };\n  useLayoutEffect(() => {\n    writeDash();\n    ringRef.current?.setAttribute('stroke-dashoffset', String(travel.get()));\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [C, P]);\n  useEffect(() => {\n    const offs = [\n      mode.on('change', writeDash),\n      arc.on('change', writeDash),\n      travel.on('change', (v: number) => ringRef.current?.setAttribute('stroke-dashoffset', String(v)))\n    ];\n    return () => {\n      offs.forEach(off => off());\n      mode.stop();\n      arc.stop();\n      travel.stop();\n    };\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, []);\n\n  useEffect(() => {\n    const g = ++gen.current;\n    if (reduce) {\n      mode.jump(solid ? 1 : 0);\n      arc.jump(targetArc);\n      travel.jump(0);\n      return;\n    }\n    if (mode.get() === 0) arc.jump(targetArc);\n    animate(mode, solid ? 1 : 0, MORPH);\n    animate(arc, targetArc, UI);\n    if (indeterminate) {\n      const t0 = travel.get();\n      animate(travel, [t0, t0 - C], { duration: spinDuration / 1000, ease: 'linear', repeat: Infinity });\n      return;\n    }\n    const unit = determinate ? C : P;\n    const to = Math.floor(travel.get() / unit) * unit;\n    animate(travel, to, UI).then(() => {\n      if (gen.current === g) travel.jump(0);\n    });\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [status, determinate, targetArc, reduce, C, P, spinDuration]);\n\n  const spoken = TEXT[status] + (determinate ? `, ${Math.round(clamp01(progress as number) * 100)}%` : '');\n  const hasLabel = label !== undefined && label !== null;\n\n  return (\n    <span\n      className={`status-mark${className ? ` ${className}` : ''}`}\n      data-status={status}\n      data-indeterminate={indeterminate ? '' : undefined}\n      data-strike={strike ? '' : undefined}\n      style={\n        {\n          '--sm-size': `${size}px`,\n          '--sm-stroke': strokeWidth,\n          '--sm-color': color,\n          '--sm-done': doneColor,\n          '--sm-error': errorColor,\n          '--sm-fill': fillOpacity,\n          '--sm-font': `${fontSize}px`,\n          '--sm-draw': `${drawDuration}ms`,\n          '--sm-strike-delay': `${120 + strikeDelay}ms`,\n          ...style\n        } as CSSProperties\n      }\n    >\n      <svg\n        className=\"status-mark__glyph\"\n        viewBox=\"0 0 24 24\"\n        width={size}\n        height={size}\n        role={hasLabel ? undefined : 'img'}\n        aria-label={hasLabel ? undefined : spoken}\n        aria-hidden={hasLabel || undefined}\n      >\n        <circle className=\"status-mark__track\" cx=\"12\" cy=\"12\" r={r} transform=\"rotate(-90 12 12)\" />\n        <circle ref={ringRef} className=\"status-mark__ring\" cx=\"12\" cy=\"12\" r={r} transform=\"rotate(-90 12 12)\" />\n        <path className=\"status-mark__check\" d={CHECK} pathLength=\"1\" />\n        <path className=\"status-mark__cross\" d={CROSS} pathLength=\"1\" />\n      </svg>\n      {hasLabel ? <span className=\"status-mark__sr\">{spoken}: </span> : null}\n      {hasLabel ? (\n        <span className=\"status-mark__label\">\n          {label}\n          <span className=\"status-mark__strike\" aria-hidden=\"true\" />\n        </span>\n      ) : null}\n    </span>\n  );\n};\n\nexport default StatusMark;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"motion@^12.23.12"
	]
}