{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "kill-switch",
  "title": "KillSwitch",
  "description": "Hold for a second to stop the agent. Letting go early does nothing. Space or Enter held does the same.",
  "registryDependencies": [
    "https://creance.pages.dev/r/creance-base.json"
  ],
  "files": [
    {
      "path": "kill-switch/KillSwitch.css",
      "content": ".cr-kill {\n  display: inline-grid;\n  justify-items: start;\n  gap: var(--cr-space-2);\n  font-family: var(--cr-font-family-sans);\n}\n\n.cr-kill__button {\n  position: relative;\n  display: inline-flex;\n  align-items: center;\n  gap: 10px;\n  min-height: 48px;\n  padding: 0 22px 0 18px;\n  overflow: hidden;\n  border: 1px solid color-mix(in srgb, var(--cr-color-danger-text) 35%, transparent);\n  border-radius: var(--cr-radius-full);\n  background: var(--cr-color-glass-control);\n  box-shadow: var(--cr-shadow-sm);\n  color: var(--cr-color-danger-text);\n  font: inherit;\n  font-size: var(--cr-font-size-md);\n  font-weight: var(--cr-font-weight-bold);\n  cursor: pointer;\n  touch-action: none;\n  user-select: none;\n  -webkit-user-select: none;\n}\n\n/* The fill sweeps across while held; at full, the agent stops. */\n.cr-kill__fill {\n  position: absolute;\n  inset: 0;\n  display: inline-flex;\n  align-items: center;\n  gap: 10px;\n  padding: 0 22px 0 18px;\n  background: var(--cr-color-danger-bg);\n  color: var(--cr-color-danger-fg);\n  clip-path: inset(0 100% 0 0);\n}\n\n.cr-kill__button[data-holding] .cr-kill__fill {\n  clip-path: inset(0 0 0 0);\n  transition: clip-path var(--cr-kill-hold) linear;\n}\n\n.cr-kill__button:focus-visible {\n  outline: var(--cr-focus-width) solid var(--cr-color-focus);\n  outline-offset: var(--cr-focus-offset);\n}\n\n/* Reduced motion: no sweep, the button fills at once and the label says to keep holding. */\n@media (prefers-reduced-motion: reduce) {\n  .cr-kill__button[data-holding] .cr-kill__fill { transition: none; }\n}\n\n.cr-kill__description {\n  color: var(--cr-color-text-muted);\n  font-size: var(--cr-font-size-sm);\n}\n\n.cr-kill__stopped {\n  display: inline-flex;\n  align-items: center;\n  gap: 10px;\n  min-height: 48px;\n  padding: 0 20px;\n  box-sizing: border-box;\n  border-radius: var(--cr-radius-full);\n  background: var(--cr-color-ink-bg);\n  color: var(--cr-color-ink-fg);\n  font-size: var(--cr-font-size-md);\n  font-weight: var(--cr-font-weight-semibold);\n}\n\n.cr-kill__resume {\n  padding: var(--cr-space-1) 0;\n  border: 0;\n  background: none;\n  color: var(--cr-color-text-link);\n  font: inherit;\n  font-weight: var(--cr-font-weight-semibold);\n  cursor: pointer;\n}\n\n.cr-kill__resume:focus-visible {\n  outline: var(--cr-focus-width) solid var(--cr-color-focus);\n  outline-offset: var(--cr-focus-offset);\n}\n\n.cr-kill[data-compact] .cr-kill__button,\n.cr-kill[data-compact] .cr-kill__fill {\n  min-height: var(--cr-control-height-sm);\n  padding: 0 var(--cr-space-4) 0 14px;\n  font-size: var(--cr-font-size-sm);\n}\n",
      "type": "registry:file",
      "target": "@components/creance/kill-switch/KillSwitch.css"
    },
    {
      "path": "kill-switch/KillSwitch.tsx",
      "content": "'use client';\n\nimport { useEffect, useId, useRef, useState, type CSSProperties, type ReactNode } from 'react';\nimport { cx } from '@/lib/creance/cx';\nimport { StopIcon } from '@/lib/creance/icons';\nimport './KillSwitch.css';\n\nexport interface KillSwitchProps {\n  /** Called once the button has been held for `holdMs`. */\n  onStop: () => void;\n  /** Milliseconds to hold. Default 1000. */\n  holdMs?: number;\n  /** Shown after stopping. Leave out to keep the button. */\n  stopped?: boolean;\n  /** \"Deploy Fixer stopped\". */\n  stoppedLabel?: ReactNode;\n  onResume?: () => void;\n  /** What stopping means, shown under the button: \"Stops Deploy Fixer after its current call.\" */\n  description?: ReactNode;\n  label?: string;\n  /** Smaller, with the description kept for screen readers but hidden on screen. For headers and toolbars. */\n  compact?: boolean;\n  className?: string;\n}\n\n/**\n * Always within reach, never pressed by accident: hold for a second to stop the agent; let go early\n * and nothing happens. Holding Space or Enter does the same from the keyboard.\n */\nexport function KillSwitch({\n  onStop,\n  holdMs = 1000,\n  stopped,\n  stoppedLabel = 'Agent stopped',\n  onResume,\n  description,\n  label = 'Hold to stop',\n  compact,\n  className,\n}: KillSwitchProps) {\n  const descId = useId();\n  const [holding, setHolding] = useState(false);\n  const timer = useRef<ReturnType<typeof setTimeout>>(undefined);\n  useEffect(() => () => clearTimeout(timer.current), []);\n\n  const start = () => {\n    if (holding) return;\n    setHolding(true);\n    timer.current = setTimeout(() => {\n      setHolding(false);\n      onStop();\n    }, holdMs);\n  };\n  const cancel = () => {\n    clearTimeout(timer.current);\n    setHolding(false);\n  };\n\n  if (stopped) {\n    return (\n      <div className={cx('cr-kill', className)}>\n        <span role=\"status\" className=\"cr-kill__stopped\">\n          <StopIcon />\n          {stoppedLabel}\n        </span>\n        {onResume && (\n          <button type=\"button\" className=\"cr-kill__resume\" onClick={onResume}>\n            Resume run\n          </button>\n        )}\n      </div>\n    );\n  }\n\n  return (\n    <div className={cx('cr-kill', className)} data-compact={compact || undefined}>\n      <button\n        type=\"button\"\n        className=\"cr-kill__button\"\n        data-holding={holding || undefined}\n        aria-describedby={descId}\n        style={{ '--cr-kill-hold': `${holdMs}ms` } as CSSProperties}\n        onPointerDown={(e) => {\n          if (e.button === 0) start();\n        }}\n        onPointerUp={cancel}\n        onPointerLeave={cancel}\n        onPointerCancel={cancel}\n        onKeyDown={(e) => {\n          if (e.key === ' ' || e.key === 'Enter') {\n            e.preventDefault();\n            if (!e.repeat) start();\n          }\n        }}\n        onKeyUp={(e) => {\n          if (e.key === ' ' || e.key === 'Enter') cancel();\n        }}\n        onBlur={cancel}\n        onContextMenu={(e) => e.preventDefault()}\n      >\n        <StopIcon />\n        <span className=\"cr-kill__label\">{holding ? 'Keep holding' : label}</span>\n        {/* A white copy of the content inside the sweeping fill, so the label reads on both sides of its edge. */}\n        <span className=\"cr-kill__fill\" aria-hidden=\"true\">\n          <StopIcon />\n          <span className=\"cr-kill__label\">{holding ? 'Keep holding' : label}</span>\n        </span>\n      </button>\n      <span id={descId} className={compact ? 'cr-visually-hidden' : 'cr-kill__description'}>\n        {description ?? `Press and hold for ${holdMs / 1000} second${holdMs === 1000 ? '' : 's'}.`}\n      </span>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@components/creance/kill-switch/KillSwitch.tsx"
    }
  ],
  "categories": [
    "organism",
    "agent"
  ],
  "type": "registry:ui"
}