{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "launchpad",
  "title": "Launchpad",
  "description": "First-run setup steps for the app home; the active step opens with its actions.",
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "@ikas/animated-number",
    "@ikas/button",
    "@ikas/collapse",
    "@ikas/motion",
    "@ikas/progress-bar",
    "@ikas/step-kit",
    "@ikas/swap-text",
    "@ikas/utils"
  ],
  "files": [
    {
      "path": "registry/ikas/launchpad.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronUpIcon, XIcon } from \"lucide-react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\n\nimport { EASE_OUT, ICON_SPRING, INSTANT, SPRING, SPRING_EXIT, STAGGER } from \"@/lib/motion\"\nimport { cn } from \"@/lib/utils\"\nimport { Button } from \"@/components/ui/button\"\nimport { AnimatedNumber } from \"@/components/ikas/animated-number\"\nimport { Collapse } from \"@/components/ikas/collapse\"\nimport { SwapText } from \"@/components/ikas/swap-text\"\nimport { ProgressBar, type ProgressSegmentType } from \"@/components/ikas/progress-bar\"\nimport { CheckGlyph, StepMarker, markerWidth, useStepMarkerShape, type StepMarkerShape } from \"@/components/ikas/step-kit\"\n\ntype LaunchpadStepStatus = \"todo\" | \"done\" | \"blocked\"\n\ntype LaunchpadStep = {\n  id: string\n  label: React.ReactNode\n  /** Shown under the label while the step is active. One or two sentences. */\n  description?: React.ReactNode\n  /** Rough effort shown on the right, e.g. \"~2 dk\". */\n  eta?: React.ReactNode\n  /** Defaults to \"todo\", or \"blocked\" while a step in `requires` is not done. */\n  status?: LaunchpadStepStatus\n  /** Ids of steps that must be done first. */\n  requires?: string[]\n  /** Buttons shown while the step is active. Primary first, then one secondary. */\n  actions?: React.ReactNode\n}\n\ntype LaunchpadProps = Omit<React.ComponentProps<\"section\">, \"title\"> & {\n  steps: LaunchpadStep[]\n  /** The open step. Leave undefined to let the Launchpad manage it. */\n  activeId?: string | null\n  /** Called when a step is focused, and when the active step is completed and the next one opens. */\n  onActiveChange?: (id: string) => void\n  /** Called once when the last step is done. */\n  onComplete?: () => void\n  /** Shows only the header and the next step. Leave undefined to let the Launchpad manage it. */\n  collapsed?: boolean\n  onCollapsedChange?: (collapsed: boolean) => void\n  /** Small label at the top left. */\n  title?: React.ReactNode\n  /** Shows a close button once every step is done. */\n  onDismiss?: () => void\n  /**\n   * rail: numbered steps on a line. timeline: the rail with motion: markers draw a check, the line fills\n   * down to the next step, the title is struck through. checklist: tickable rows that fold into a\n   * \"Tamamlananlar\" group, with a ring in the header.\n   */\n  layout?: \"rail\" | \"timeline\" | \"checklist\"\n  /** Step marker shape: diamond (default), circle, square or pill. Defaults to the StepMarkerProvider's. */\n  marker?: StepMarkerShape\n  /** Checklist only: lets the user tick a step done (or undo it). Without it, steps finish through their actions. */\n  onStepToggle?: (id: string, done: boolean) => void\n  /** Checklist and timeline: header title once every step is done. */\n  doneTitle?: React.ReactNode\n  /** Progress shown in the header: a ring, a bar or one of ProgressBar's segment types. Defaults to \"ring\" in the checklist, \"skew\" in rail and timeline. */\n  progressType?: LaunchpadProgressType\n}\n\ntype LaunchpadProgressType = \"ring\" | \"bar\" | ProgressSegmentType\n\ntype ResolvedStep = LaunchpadStep & { status: LaunchpadStepStatus; index: string; waitingFor?: ResolvedStep }\n\nconst pad = (n: number) => String(n).padStart(2, \"0\")\n\nfunction resolveSteps(steps: LaunchpadStep[]): ResolvedStep[] {\n  const done = new Set(steps.filter((s) => s.status === \"done\").map((s) => s.id))\n  const indexOf = new Map(steps.map((s, i) => [s.id, i]))\n  const resolved: ResolvedStep[] = steps.map((step, i) => ({ ...step, status: step.status ?? \"todo\", index: pad(i + 1) }))\n  for (const step of resolved) {\n    if (step.status === \"done\") continue\n    const missing = step.requires?.find((id) => !done.has(id))\n    if (missing !== undefined) {\n      step.status = \"blocked\"\n      const at = indexOf.get(missing)\n      if (at !== undefined) step.waitingFor = resolved[at]\n    }\n  }\n  return resolved\n}\n\n/** The step to open after `fromId`: the first open, unblocked step after it, then before it. */\nfunction nextStep(steps: ResolvedStep[], fromId?: string | null): string | null {\n  const from = steps.findIndex((s) => s.id === fromId)\n  const ordered = [...steps.slice(from + 1), ...steps.slice(0, Math.max(from, 0))]\n  return (ordered.find((s) => s.status === \"todo\") ?? ordered.find((s) => s.status === \"blocked\"))?.id ?? null\n}\n\nconst doneIds = (key: string) => key.split(\"|\").filter(Boolean)\n\n/** Outline and core of each marker shape, drawn in a 16px box. */\nfunction NodeShape({ shape, part, ...props }: { shape: StepMarkerShape; part: \"outline\" | \"core\" } & React.SVGProps<SVGElement>) {\n  const outline = part === \"outline\"\n  if (shape === \"circle\") return <circle cx={8} cy={8} r={outline ? 6.4 : 2.6} {...(props as React.SVGProps<SVGCircleElement>)} />\n  if (shape === \"square\") return outline ? <rect x={1.8} y={1.8} width={12.4} height={12.4} rx={3} {...(props as React.SVGProps<SVGRectElement>)} /> : <rect x={5.4} y={5.4} width={5.2} height={5.2} rx={1.2} {...(props as React.SVGProps<SVGRectElement>)} />\n  if (shape === \"pill\") return outline ? <rect x={0.8} y={3.6} width={14.4} height={8.8} rx={4.4} {...(props as React.SVGProps<SVGRectElement>)} /> : <rect x={4.6} y={6.4} width={6.8} height={3.2} rx={1.6} {...(props as React.SVGProps<SVGRectElement>)} />\n  return <path d={outline ? \"M8 1.6 14.4 8 8 14.4 1.6 8Z\" : \"M8 5.4 10.6 8 8 10.6 5.4 8Z\"} {...(props as React.SVGProps<SVGPathElement>)} />\n}\n\n/** Step marker: a diamond by default. Filled when done, with a core while active, dashed while blocked. */\nfunction StepNode({ status, active, shape }: { status: LaunchpadStepStatus; active: boolean; shape: StepMarkerShape }) {\n  return (\n    <svg data-slot=\"launchpad-node\" viewBox=\"0 0 16 16\" className=\"size-4 shrink-0 overflow-visible\" aria-hidden>\n      {status === \"done\" ? (\n        <NodeShape shape={shape} part=\"outline\" className=\"fill-foreground stroke-foreground\" strokeWidth={1.5} strokeLinejoin=\"round\" />\n      ) : active ? (\n        <>\n          <NodeShape shape={shape} part=\"outline\" className=\"fill-card stroke-foreground\" strokeWidth={1.5} strokeLinejoin=\"round\" />\n          <NodeShape shape={shape} part=\"core\" className=\"fill-foreground\" />\n        </>\n      ) : (\n        <NodeShape\n          shape={shape}\n          part=\"outline\"\n          className={cn(\"fill-card\", status === \"blocked\" ? \"stroke-border-strong\" : \"stroke-icon\")}\n          strokeWidth={1.5}\n          strokeLinejoin=\"round\"\n          strokeDasharray={status === \"blocked\" ? \"2.4 2.1\" : undefined}\n        />\n      )}\n    </svg>\n  )\n}\n\n/** Header ring of the checklist layout: fills with the done steps; at the end it turns green and a check draws in. */\nfunction ProgressRing({ done, total, small = false }: { done: number; total: number; small?: boolean }) {\n  const reduce = useReducedMotion()\n  const ratio = total > 0 ? done / total : 0\n  const complete = total > 0 && done === total\n  return (\n    <span\n      role=\"progressbar\"\n      aria-label=\"Tamamlanan adımlar\"\n      aria-valuemin={0}\n      aria-valuemax={total}\n      aria-valuenow={done}\n      aria-valuetext={`${total} adımdan ${done} tanesi tamamlandı`}\n      className={cn(\"relative grid shrink-0 place-items-center\", small ? \"size-5\" : \"size-9\", complete ? \"text-success\" : \"text-chart-ink\")}\n    >\n      <svg viewBox=\"0 0 36 36\" className=\"absolute inset-0 -rotate-90\" aria-hidden>\n        <circle cx={18} cy={18} r={15} fill=\"none\" strokeWidth={3.5} className=\"stroke-[color-mix(in_oklab,var(--chart-ink)_9%,var(--card))]\" />\n        <motion.circle\n          cx={18}\n          cy={18}\n          r={15}\n          fill=\"none\"\n          strokeWidth={3.5}\n          strokeLinecap=\"round\"\n          pathLength={1}\n          strokeDasharray=\"1 1\"\n          className=\"stroke-current transition-colors duration-200 ease-out\"\n          initial={false}\n          // At 0 the round cap would show as a dot, so the arc fades out instead.\n          animate={{ strokeDashoffset: 1 - ratio, opacity: ratio > 0 ? 1 : 0 }}\n          transition={reduce ? INSTANT : { strokeDashoffset: SPRING, opacity: { duration: 0.12 } }}\n        />\n      </svg>\n      <AnimatePresence initial={false}>\n        {complete && (\n          <motion.span\n            key=\"check\"\n            className=\"relative flex\"\n            initial={reduce ? { opacity: 0 } : { opacity: 0, scale: 0.6 }}\n            animate={{ opacity: 1, scale: 1, transition: reduce ? { duration: 0.12 } : { scale: { ...ICON_SPRING, delay: 0.12 }, opacity: { duration: 0.16, delay: 0.12 } } }}\n            exit={{ opacity: 0, transition: { duration: 0.1 } }}\n          >\n            <CheckGlyph delay={0.12} className={small ? \"size-3\" : \"size-4\"} />\n          </motion.span>\n        )}\n      </AnimatePresence>\n    </span>\n  )\n}\n\n/**\n * Header progress in any type but the checklist's big ring. Bar segments stay 10px each, so the\n * strip grows with the step count; the bar gets a fixed width; points keep their own size.\n */\nfunction HeaderProgress({ type, done, total }: { type: LaunchpadProgressType; done: number; total: number }) {\n  if (type === \"ring\") return <ProgressRing done={done} total={total} small />\n  const points = type === \"dot\" || type === \"diamond\"\n  const gap = type === \"notch\" ? 2 : 3\n  return (\n    <ProgressBar\n      aria-label=\"Tamamlanan adımlar\"\n      hideValue\n      variant={type === \"bar\" ? \"bar\" : \"segments\"}\n      segmentType={type === \"bar\" ? undefined : type}\n      value={done}\n      max={total}\n      className={cn(\"shrink-0\", type === \"bar\" ? \"w-24\" : points && \"w-auto\")}\n      style={type === \"bar\" || points ? undefined : { width: total * 10 + (total - 1) * gap }}\n    />\n  )\n}\n\n/**\n * The line between two markers: a faint dashed track, and an ink line that runs down it once the step\n * above is done. In the checklist each row draws its own half, so the line also follows rows that fold.\n */\nfunction RailSegment({ filled, delay = 0, className }: { filled: boolean; delay?: number; className?: string }) {\n  const reduce = useReducedMotion()\n  return (\n    <span aria-hidden data-slot=\"launchpad-rail\" className={cn(\"pointer-events-none absolute w-[1.5px] overflow-hidden\", className)}>\n      <span className=\"absolute inset-0 border-l-[1.5px] border-dashed border-border-strong\" />\n      <motion.span\n        className=\"absolute inset-0 origin-top bg-chart-ink\"\n        initial={false}\n        animate={{ scaleY: filled ? 1 : 0 }}\n        transition={reduce ? INSTANT : { ...SPRING, delay }}\n      />\n    </span>\n  )\n}\n\n/** How long a ticked row stays in place before it folds into \"Tamamlananlar\". */\nconst SETTLE_MS = 100\n\n/**\n * First-run steps on the app's home screen. A vertical rail of numbered steps:\n * the active one opens in place with its actions, any step can be focused, and\n * finishing a step opens the next one. Collapses to its header and the next step.\n */\nfunction Launchpad({\n  steps,\n  activeId,\n  onActiveChange,\n  onComplete,\n  collapsed,\n  onCollapsedChange,\n  title = \"Başlangıç\",\n  onDismiss,\n  layout = \"rail\",\n  marker,\n  onStepToggle,\n  doneTitle = \"Her şey hazır\",\n  progressType,\n  className,\n  style,\n  ...props\n}: LaunchpadProps) {\n  const baseId = React.useId()\n  const reduce = useReducedMotion()\n  const shape = useStepMarkerShape(marker)\n  const listRef = React.useRef<HTMLOListElement>(null)\n  const resolved = React.useMemo(() => resolveSteps(steps), [steps])\n  const doneCount = resolved.filter((s) => s.status === \"done\").length\n  const allDone = resolved.length > 0 && doneCount === resolved.length\n  const doneKey = resolved.map((s) => (s.status === \"done\" ? s.id : \"\")).join(\"|\")\n  const progress = progressType ?? (layout === \"checklist\" ? \"ring\" : \"skew\")\n\n  // Active step: controlled through activeId, or kept here.\n  const [ownActive, setOwnActive] = React.useState<string | null>(() => nextStep(resolved))\n  const active = activeId === undefined ? ownActive : activeId\n\n  const [ownCollapsed, setOwnCollapsed] = React.useState(false)\n  const isCollapsed = collapsed ?? ownCollapsed\n  const setCollapsed = (value: boolean) => {\n    if (collapsed === undefined) setOwnCollapsed(value)\n    onCollapsedChange?.(value)\n  }\n\n  // When the active step is completed, open the next one. Uncontrolled: adjust during render.\n  const [seenDoneKey, setSeenDoneKey] = React.useState(doneKey)\n  if (seenDoneKey !== doneKey) {\n    setSeenDoneKey(doneKey)\n    const wasDone = new Set(doneIds(seenDoneKey))\n    const justDone = ownActive !== null && !wasDone.has(ownActive) && doneIds(doneKey).includes(ownActive)\n    if (activeId === undefined && (ownActive === null || justDone)) setOwnActive(nextStep(resolved, ownActive))\n  }\n\n  // Controlled activeId and onComplete are told from an effect, after the change has rendered.\n  const latest = React.useRef({ onActiveChange, onComplete, activeId, resolved })\n  React.useLayoutEffect(() => {\n    latest.current = { onActiveChange, onComplete, activeId, resolved }\n  })\n  const previousDoneKey = React.useRef(doneKey)\n  React.useEffect(() => {\n    const before = new Set(doneIds(previousDoneKey.current))\n    previousDoneKey.current = doneKey\n    const now = doneIds(doneKey)\n    const { onActiveChange: change, onComplete: complete, activeId: controlled, resolved: current } = latest.current\n    if (controlled && now.includes(controlled) && !before.has(controlled)) {\n      const next = nextStep(current, controlled)\n      if (next) change?.(next)\n    }\n    if (now.length === current.length && before.size < current.length) complete?.()\n  }, [doneKey])\n\n  const focusStep = (id: string) => {\n    if (activeId === undefined) setOwnActive(id)\n    onActiveChange?.(id)\n  }\n\n  function onListKeyDown(event: React.KeyboardEvent<HTMLOListElement>) {\n    const buttons = Array.from(listRef.current?.querySelectorAll<HTMLButtonElement>(\"[data-slot=launchpad-step-trigger]\") ?? [])\n    const at = buttons.indexOf(document.activeElement as HTMLButtonElement)\n    if (at === -1) return\n    const target =\n      event.key === \"ArrowDown\" ? buttons[Math.min(at + 1, buttons.length - 1)]\n      : event.key === \"ArrowUp\" ? buttons[Math.max(at - 1, 0)]\n      : event.key === \"Home\" ? buttons[0]\n      : event.key === \"End\" ? buttons[buttons.length - 1]\n      : undefined\n    if (!target) return\n    event.preventDefault()\n    target.focus()\n  }\n\n  const upNext = resolved.find((s) => s.id === active && s.status !== \"done\") ?? resolved.find((s) => s.id === nextStep(resolved))\n  const listId = `${baseId}-steps`\n\n  // Checklist: a ticked row stays a beat, then folds into \"Tamamlananlar\". Un-ticked rows come back at once.\n  const [settled, setSettled] = React.useState<string[]>(() => doneIds(doneKey))\n  const [groupOpen, setGroupOpen] = React.useState(false)\n  // The done list starts closed again whenever the card finishes or stops being finished.\n  const [groupFor, setGroupFor] = React.useState(allDone)\n  if (groupFor !== allDone) {\n    setGroupFor(allDone)\n    setGroupOpen(false)\n  }\n  const rowRefs = React.useRef(new Map<string, HTMLLIElement>())\n  const focusNext = React.useRef(false)\n  // An un-ticked step leaves \"Tamamlananlar\" at once: adjusted during render, not in an effect.\n  const [settledFor, setSettledFor] = React.useState(doneKey)\n  if (settledFor !== doneKey) {\n    setSettledFor(doneKey)\n    const nowDone = new Set(doneIds(doneKey))\n    if (settled.some((id) => !nowDone.has(id))) setSettled(settled.filter((id) => nowDone.has(id)))\n  }\n  React.useEffect(() => {\n    if (layout !== \"checklist\") return\n    const timers = doneIds(doneKey).map((id) =>\n      setTimeout(() => {\n        // If focus was in the folding row, hand it to the step that opens next.\n        if (rowRefs.current.get(id)?.contains(document.activeElement)) focusNext.current = true\n        setSettled((current) => (current.includes(id) ? current : [...current, id]))\n      }, SETTLE_MS)\n    )\n    return () => timers.forEach(clearTimeout)\n  }, [doneKey, layout])\n  React.useEffect(() => {\n    if (!focusNext.current) return\n    focusNext.current = false\n    const next = listRef.current?.querySelector<HTMLElement>(\"[data-slot=launchpad-panel] button:not(:disabled), [data-slot=launchpad-step-trigger]\")\n    next?.focus({ preventScroll: true })\n  })\n\n  if (layout === \"checklist\") {\n    const openSteps = resolved.filter((s) => !settled.includes(s.id))\n    const settledSteps = resolved.filter((s) => settled.includes(s.id))\n    // Every row has folded away: the card shrinks to its header and the done list opens from there.\n    const finished = allDone && openSteps.length === 0\n    const rowMotion = reduce\n      ? { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0, transition: { duration: 0.1 } } }\n      : {\n          initial: { height: 0, opacity: 0 },\n          animate: { height: \"auto\", opacity: 1, transition: { height: SPRING, opacity: { duration: 0.2, delay: 0.04, ease: EASE_OUT } } },\n          exit: { height: 0, opacity: 0, transition: { height: SPRING_EXIT, opacity: { duration: 0.12 } } },\n        }\n    return (\n      <section\n        data-slot=\"launchpad\"\n        data-layout=\"checklist\"\n        data-collapsed={isCollapsed || undefined}\n        aria-labelledby={`${baseId}-title`}\n        className={cn(\"flex flex-col rounded-xl bg-card shadow-card\", className)}\n        style={style}\n        {...props}\n      >\n        <header className=\"flex min-h-14 items-center gap-3 py-3 pr-2.5 pl-4\">\n          {progress === \"ring\" && <ProgressRing done={doneCount} total={resolved.length} />}\n          <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n            <h2 id={`${baseId}-title`} className=\"truncate text-sm font-semibold text-foreground\">\n              <SwapText swapKey={allDone ? \"done\" : \"todo\"}>{allDone ? doneTitle : title}</SwapText>\n            </h2>\n            <p className=\"text-[12.5px] text-muted-foreground tabular-nums\">\n              <AnimatedNumber value={doneCount} />/{resolved.length} tamamlandı\n              {finished && (\n                <>\n                  {\" · \"}\n                  <button\n                    type=\"button\"\n                    aria-expanded={groupOpen}\n                    aria-controls={listId}\n                    onClick={() => setGroupOpen(!groupOpen)}\n                    className=\"rounded-sm text-foreground underline-offset-2 outline-none hover:underline focus-visible:ring-3 focus-visible:ring-ring/30\"\n                  >\n                    {groupOpen ? \"Adımları gizle\" : \"Adımları gör\"}\n                  </button>\n                </>\n              )}\n            </p>\n          </div>\n          {progress !== \"ring\" && <HeaderProgress type={progress} done={doneCount} total={resolved.length} />}\n          {/* A finished card has nothing left to collapse; it keeps only the close button. */}\n          {!finished && (\n            <Button\n              variant=\"ghost\"\n              size=\"icon-sm\"\n              aria-label={isCollapsed ? \"Genişlet\" : \"Küçült\"}\n              aria-expanded={!isCollapsed}\n              aria-controls={listId}\n              onClick={() => setCollapsed(!isCollapsed)}\n              className=\"text-icon hover:text-foreground aria-expanded:bg-transparent aria-expanded:hover:bg-muted\"\n            >\n              <motion.span aria-hidden className=\"flex\" initial={false} animate={{ rotate: isCollapsed ? 180 : 0 }} transition={reduce ? INSTANT : ICON_SPRING}>\n                <ChevronUpIcon />\n              </motion.span>\n            </Button>\n          )}\n          {allDone && onDismiss && (\n            <Button variant=\"ghost\" size=\"icon-sm\" aria-label=\"Kapat\" onClick={onDismiss} className={cn(\"text-icon\", !finished && \"-ml-2\")}>\n              <XIcon />\n            </Button>\n          )}\n        </header>\n\n        <Collapse open={isCollapsed && !!upNext} className=\"px-2.5 pb-2.5\">\n          {upNext && (\n            <button\n              type=\"button\"\n              onClick={() => (focusStep(upNext.id), setCollapsed(false))}\n              className=\"flex w-full items-center gap-3 rounded-lg border border-dashed px-2.5 py-2 text-left text-sm transition-colors duration-150 outline-none hover:bg-muted/60 focus-visible:ring-3 focus-visible:ring-ring/30\"\n            >\n              <span className=\"text-[13px] text-muted-foreground\">Sıradaki</span>\n              <span className=\"min-w-0 flex-1 truncate font-medium\">{upNext.label}</span>\n              {upNext.eta && <span className=\"shrink-0 text-[12px] text-muted-foreground tabular-nums\">{upNext.eta}</span>}\n            </button>\n          )}\n        </Collapse>\n\n        <Collapse open={finished ? groupOpen : !isCollapsed} id={listId}>\n          <div className=\"border-t\">\n            {/* When every row has folded away the list closes too, so no empty band is left between the rules. */}\n            <Collapse open={openSteps.length > 0}>\n              <ol ref={listRef} onKeyDown={onListKeyDown} aria-label=\"Yapılacak adımlar\" className=\"flex flex-col p-1.5\">\n                <AnimatePresence initial={false}>\n                  {openSteps.map((step, i) => {\n                    const isActive = step.id === active && !allDone\n                    // The line into this row is filled when the row above is done; the line out of it when this one is.\n                    const hasPrev = i > 0\n                    const hasNext = i < openSteps.length - 1\n                    const isDone = step.status === \"done\"\n                    const panelId = `${baseId}-step-${step.id}`\n                    const titleId = `${baseId}-title-${step.id}`\n                    const markerStatus = isDone ? \"done\" : step.status === \"blocked\" ? \"blocked\" : isActive ? \"current\" : \"upcoming\"\n                    const meta = step.status === \"blocked\" && step.waitingFor ? `${step.waitingFor.index} bitince` : step.eta\n                    return (\n                      <motion.li\n                        key={step.id}\n                        ref={(el: HTMLLIElement | null) => void (el ? rowRefs.current.set(step.id, el) : rowRefs.current.delete(step.id))}\n                        data-status={step.status}\n                        className=\"relative overflow-hidden\"\n                        {...rowMotion}\n                      >\n                        {hasPrev && <RailSegment filled={openSteps[i - 1].status === \"done\"} delay={STAGGER} className=\"top-0 left-[22.25px] h-[9px]\" />}\n                        {hasNext && <RailSegment filled={isDone} className=\"top-[33px] bottom-0 left-[22.25px]\" />}\n                        <div className={cn(\"grid grid-cols-[34px_minmax(0,1fr)] items-start gap-x-1.5 rounded-lg px-1.5 py-1 transition-colors duration-200 ease-out\", isActive && \"bg-muted/70\")}>\n                          {onStepToggle ? (\n                            <button\n                              type=\"button\"\n                              role=\"checkbox\"\n                              aria-checked={isDone}\n                              aria-labelledby={titleId}\n                              disabled={step.status === \"blocked\"}\n                              onClick={() => onStepToggle(step.id, !isDone)}\n                              className=\"grid size-[34px] place-items-center rounded-md outline-none focus-visible:ring-3 focus-visible:ring-ring/30 disabled:opacity-60 [&:active>span]:scale-90 [&>span]:transition-[scale] [&>span]:duration-100\"\n                            >\n                              <StepMarker status={markerStatus} shape={shape} size=\"sm\" />\n                            </button>\n                          ) : (\n                            <span className=\"grid size-[34px] place-items-center\">\n                              <StepMarker status={markerStatus} shape={shape} size=\"sm\" />\n                            </span>\n                          )}\n                          <div className=\"flex min-w-0 flex-col\">\n                            <button\n                              type=\"button\"\n                              id={titleId}\n                              data-slot=\"launchpad-step-trigger\"\n                              aria-current={isActive ? \"step\" : undefined}\n                              aria-expanded={isActive}\n                              aria-controls={panelId}\n                              onClick={() => focusStep(step.id)}\n                              className=\"flex min-h-[34px] w-full items-center gap-2 rounded-md text-left text-sm outline-none focus-visible:ring-3 focus-visible:ring-ring/30\"\n                            >\n                              {/* The strike draws left to right across each line when the step is ticked. */}\n                              <span\n                                className={cn(\n                                  \"min-w-0 flex-1 bg-[linear-gradient(currentColor,currentColor)] bg-[position:0_58%] bg-no-repeat transition-[background-size,color] ease-out motion-reduce:transition-none\",\n                                  isDone ? \"bg-[length:100%_1px] text-muted-foreground delay-[60ms] duration-[360ms]\"\n                                  : step.status === \"blocked\" && !isActive ? \"bg-[length:0%_1px] text-muted-foreground duration-150\"\n                                  : \"bg-[length:0%_1px] font-medium text-foreground duration-150\"\n                                )}\n                              >\n                                {step.label}\n                              </span>\n                              {meta && !isDone && <span className=\"shrink-0 text-[12px] text-muted-foreground tabular-nums\">{meta}</span>}\n                            </button>\n                            <Collapse open={isActive && !isDone} id={panelId}>\n                              <div data-slot=\"launchpad-panel\" className=\"flex flex-col gap-3 pb-2.5\">\n                                {step.description && <p className=\"max-w-prose text-[13px] text-muted-foreground\">{step.description}</p>}\n                                {step.status === \"blocked\" && step.waitingFor && (\n                                  <p className=\"text-[13px] text-foreground\">\n                                    Önce <span className=\"font-medium tabular-nums\">{step.waitingFor.index}</span> · {step.waitingFor.label} adımını tamamlayın.\n                                  </p>\n                                )}\n                                {step.actions && (\n                                  <fieldset disabled={step.status === \"blocked\"} className=\"flex min-w-0 flex-wrap items-center gap-2 disabled:opacity-60\">\n                                    {step.actions}\n                                  </fieldset>\n                                )}\n                              </div>\n                            </Collapse>\n                          </div>\n                        </div>\n                      </motion.li>\n                    )\n                  })}\n                </AnimatePresence>\n              </ol>\n            </Collapse>\n\n            <Collapse open={settledSteps.length > 0}>\n              <div className={cn(openSteps.length > 0 && \"border-t\")}>\n                {/* Once finished, the header's \"Adımları gör\" opens the list instead of this row. */}\n                <Collapse open={!finished}>\n                  <button\n                    type=\"button\"\n                    aria-expanded={groupOpen}\n                    aria-controls={`${baseId}-done`}\n                    onClick={() => setGroupOpen(!groupOpen)}\n                    className=\"flex min-h-11 w-full items-center gap-2 pr-4 pl-[52px] text-left text-[13px] text-muted-foreground outline-none focus-visible:ring-3 focus-visible:ring-ring/30 focus-visible:ring-inset\"\n                  >\n                    <span className=\"tabular-nums\">\n                      <AnimatedNumber value={settledSteps.length} /> tamamlandı\n                    </span>\n                    <motion.span aria-hidden className=\"ml-auto flex text-icon\" initial={false} animate={{ rotate: groupOpen ? 0 : 180 }} transition={reduce ? INSTANT : ICON_SPRING}>\n                      <ChevronUpIcon className=\"size-4\" />\n                    </motion.span>\n                  </button>\n                </Collapse>\n                <Collapse open={groupOpen} id={`${baseId}-done`}>\n                  <ul aria-label=\"Tamamlanan adımlar\" className=\"flex flex-col px-1.5 pb-1.5\">\n                    <AnimatePresence initial={false}>\n                      {settledSteps.map((step) => (\n                        <motion.li key={step.id} className=\"overflow-hidden\" {...rowMotion}>\n                          <div className=\"grid grid-cols-[34px_minmax(0,1fr)_auto] items-center gap-x-1.5 px-1.5 py-1\">\n                            <span className=\"grid size-[34px] place-items-center\">\n                              <StepMarker status=\"done\" shape={shape} size=\"sm\" />\n                            </span>\n                            <span className=\"min-w-0 truncate text-sm text-muted-foreground line-through decoration-border-strong\">{step.label}</span>\n                            {onStepToggle && (\n                              <Button variant=\"ghost\" size=\"xs\" onClick={() => onStepToggle(step.id, false)}>\n                                Geri al\n                              </Button>\n                            )}\n                          </div>\n                        </motion.li>\n                      ))}\n                    </AnimatePresence>\n                  </ul>\n                </Collapse>\n              </div>\n            </Collapse>\n          </div>\n        </Collapse>\n        <span role=\"status\" className=\"sr-only\">\n          {allDone ? \"Tüm adımlar tamamlandı\" : `${resolved.length} adımdan ${doneCount} tanesi tamamlandı`}\n        </span>\n      </section>\n    )\n  }\n\n  // rail and timeline share the layout; timeline swaps in the animated parts.\n  const timeline = layout === \"timeline\"\n  const markerVar = timeline ? ({ \"--marker-w\": `${markerWidth(shape, \"sm\")}px`, ...style } as React.CSSProperties) : style\n  return (\n    <section\n      data-slot=\"launchpad\"\n      data-layout={timeline ? \"timeline\" : undefined}\n      data-collapsed={isCollapsed || undefined}\n      aria-labelledby={`${baseId}-title`}\n      className={cn(\"flex flex-col rounded-xl bg-card shadow-card\", className)}\n      style={markerVar}\n      {...props}\n    >\n      <header className=\"flex min-h-12 items-center gap-3 py-2.5 pr-2.5 pl-5\">\n        <h2 id={`${baseId}-title`} className=\"flex min-w-0 items-center gap-2 text-[13px] font-semibold text-foreground\">\n          <span aria-hidden className=\"size-1.5 shrink-0 rotate-45 rounded-[1px] bg-foreground\" />\n          {timeline ? (\n            <SwapText swapKey={allDone ? \"done\" : \"todo\"} className=\"truncate\">\n              {allDone ? doneTitle : title}\n            </SwapText>\n          ) : (\n            <span className=\"truncate\">{title}</span>\n          )}\n        </h2>\n        <div className=\"ml-auto flex shrink-0 items-center gap-3\">\n          <span className=\"text-[12px] tabular-nums\" aria-hidden>\n            <span className=\"font-medium text-foreground\">{timeline ? <AnimatedNumber value={doneCount} format={pad} /> : pad(doneCount)}</span>\n            <span className=\"text-muted-foreground\">/{pad(resolved.length)}</span>\n          </span>\n          <HeaderProgress type={progress} done={doneCount} total={resolved.length} />\n          <Button\n            variant=\"ghost\"\n            size=\"icon-sm\"\n            aria-label={isCollapsed ? \"Genişlet\" : \"Küçült\"}\n            aria-expanded={!isCollapsed}\n            aria-controls={listId}\n            onClick={() => setCollapsed(!isCollapsed)}\n            className=\"-ml-1 text-icon hover:text-foreground aria-expanded:bg-transparent aria-expanded:hover:bg-muted\"\n          >\n            {/* Points up while open (collapse), turns to point down once collapsed (expand). */}\n            <motion.span\n              aria-hidden\n              className=\"flex\"\n              initial={false}\n              animate={{ rotate: isCollapsed ? 180 : 0 }}\n              transition={reduce ? INSTANT : ICON_SPRING}\n            >\n              <ChevronUpIcon />\n            </motion.span>\n          </Button>\n          {allDone && onDismiss && (\n            <Button variant=\"ghost\" size=\"icon-sm\" aria-label=\"Kapat\" onClick={onDismiss} className=\"-ml-2 text-icon\">\n              <XIcon />\n            </Button>\n          )}\n        </div>\n      </header>\n\n      {/* The up-next row opens as the list closes: two height springs, one motion. */}\n      <Collapse open={isCollapsed && !!upNext} className=\"px-2.5 pb-2.5\">\n        {upNext && (\n          <button\n            type=\"button\"\n            onClick={() => (focusStep(upNext.id), setCollapsed(false))}\n            className=\"flex w-full items-center gap-3 rounded-lg border border-dashed px-2.5 py-2 text-left text-sm transition-colors duration-150 outline-none hover:bg-muted/60 focus-visible:ring-3 focus-visible:ring-ring/30\"\n          >\n            <span className=\"text-[13px] text-muted-foreground\">Sıradaki</span>\n            <span className=\"text-[12px] text-muted-foreground tabular-nums\">{upNext.index}</span>\n            <span className=\"min-w-0 flex-1 truncate font-medium\">{upNext.label}</span>\n            {upNext.eta && <span className=\"shrink-0 text-[12px] text-muted-foreground tabular-nums\">{upNext.eta}</span>}\n          </button>\n        )}\n      </Collapse>\n\n      <Collapse open={!isCollapsed} id={listId}>\n        <ol ref={listRef} onKeyDown={onListKeyDown} aria-label=\"Adımlar\" className=\"flex flex-col border-t px-2.5 pt-2 pb-3\">\n          {resolved.map((step, i) => {\n            const isActive = step.id === active && !allDone\n            const isLast = i === resolved.length - 1\n            const panelId = `${baseId}-step-${step.id}`\n            const meta =\n              step.status === \"done\" ? \"tamamlandı\"\n              : step.status === \"blocked\" && step.waitingFor ? `${step.waitingFor.index} bitince`\n              : step.eta\n            return (\n              <li key={step.id} data-status={step.status} className=\"relative\">\n                {!isLast && timeline && (\n                  <RailSegment\n                    filled={step.status === \"done\"}\n                    delay={step.status === \"done\" ? 0.1 : 0}\n                    className=\"top-[31px] -bottom-[5px] left-[calc(10px+var(--marker-w)/2-0.75px)]\"\n                  />\n                )}\n                {!isLast && !timeline && (\n                  <span\n                    aria-hidden\n                    data-slot=\"launchpad-rail\"\n                    className={cn(\n                      \"absolute top-[29px] -bottom-[7px] left-[17.25px] border-l-[1.5px]\",\n                      step.status === \"done\" ? \"border-solid border-foreground\" : \"border-dashed border-border-strong\"\n                    )}\n                  />\n                )}\n                <button\n                  type=\"button\"\n                  data-slot=\"launchpad-step-trigger\"\n                  aria-current={isActive ? \"step\" : undefined}\n                  aria-expanded={isActive}\n                  aria-controls={panelId}\n                  onClick={() => focusStep(step.id)}\n                  className=\"flex w-full items-center gap-3 rounded-lg px-2.5 py-2 text-left transition-colors duration-150 outline-none hover:bg-muted/60 focus-visible:ring-3 focus-visible:ring-ring/30\"\n                >\n                  {timeline ? (\n                    <StepMarker\n                      status={step.status === \"done\" ? \"done\" : step.status === \"blocked\" ? \"blocked\" : isActive ? \"current\" : \"upcoming\"}\n                      shape={shape}\n                      size=\"sm\"\n                    />\n                  ) : (\n                    <StepNode status={step.status} active={isActive} shape={shape} />\n                  )}\n                  <span className=\"w-5 shrink-0 text-[12px] font-medium text-muted-foreground tabular-nums\">{step.index}</span>\n                  {timeline ? (\n                    // The strike runs across the words only, not the empty space after them.\n                    <span className=\"min-w-0 flex-1 text-sm\">\n                      <span\n                        className={cn(\n                          \"bg-[linear-gradient(currentColor,currentColor)] bg-[position:0_58%] bg-no-repeat box-decoration-clone transition-[background-size,color] ease-out motion-reduce:transition-none\",\n                          step.status === \"done\" ? \"bg-[length:100%_1px] text-muted-foreground delay-[60ms] duration-[360ms]\"\n                          : step.status === \"blocked\" && !isActive ? \"bg-[length:0%_1px] text-muted-foreground duration-150\"\n                          : \"bg-[length:0%_1px] font-medium text-foreground duration-150\"\n                        )}\n                      >\n                        {step.label}\n                      </span>\n                    </span>\n                  ) : (\n                    <span\n                      className={cn(\n                        \"min-w-0 flex-1 text-sm\",\n                        step.status === \"done\" || (step.status === \"blocked\" && !isActive) ? \"text-muted-foreground\" : \"font-medium text-foreground\"\n                      )}\n                    >\n                      {step.label}\n                    </span>\n                  )}\n                  {meta &&\n                    (timeline ? (\n                      <SwapText className=\"shrink-0 text-[12px] text-muted-foreground tabular-nums\">{meta}</SwapText>\n                    ) : (\n                      <span className=\"shrink-0 text-[12px] text-muted-foreground tabular-nums\">{meta}</span>\n                    ))}\n                </button>\n                <Collapse open={isActive} id={panelId}>\n                  <div className={cn(\"flex flex-col gap-3 pr-2.5 pb-3\", timeline ? \"pl-[calc(var(--marker-w)+54px)]\" : \"pl-[70px]\")}>\n                    {step.description && <p className=\"max-w-prose text-[13px] text-muted-foreground\">{step.description}</p>}\n                    {step.status === \"blocked\" && step.waitingFor && (\n                      <p className=\"text-[13px] text-foreground\">\n                        Önce <span className=\"font-medium tabular-nums\">{step.waitingFor.index}</span> · {step.waitingFor.label} adımını tamamlayın.\n                      </p>\n                    )}\n                    {step.actions && (\n                      <fieldset disabled={step.status === \"blocked\"} className=\"flex min-w-0 flex-wrap items-center gap-2 disabled:opacity-60\">\n                        {step.actions}\n                      </fieldset>\n                    )}\n                  </div>\n                </Collapse>\n              </li>\n            )\n          })}\n        </ol>\n        {timeline ? (\n          <Collapse open={allDone}>\n            <p role=\"status\" className=\"-mt-1 px-5 pb-4 text-[13px] text-success-subtle-foreground\">\n              Tüm adımlar tamamlandı. Bu kartı kapatabilirsiniz.\n            </p>\n          </Collapse>\n        ) : (\n          allDone && (\n            <p role=\"status\" className=\"-mt-1 px-5 pb-4 text-[13px] text-success-subtle-foreground\">\n              Tüm adımlar tamamlandı. Bu kartı kapatabilirsiniz.\n            </p>\n          )\n        )}\n      </Collapse>\n    </section>\n  )\n}\n\nexport { Launchpad, type LaunchpadProgressType, type LaunchpadProps, type LaunchpadStep, type LaunchpadStepStatus }\n",
      "type": "registry:component",
      "target": "@components/ikas/launchpad.tsx"
    }
  ],
  "docs": "https://builders.ikas.com/tr/docs/app-development/ui-kit/components/launchpad",
  "categories": [
    "feedback",
    "layout"
  ],
  "type": "registry:component"
}