{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "elements-task",
  "type": "registry:component",
  "title": "Elements Task",
  "description": "Shared task state, labels, timing, and elapsed time for task elements.",
  "dependencies": [
    "@assistant-ui/react@^0.15.23"
  ],
  "files": [
    {
      "type": "registry:component",
      "path": "components/assistant-ui/utils/task.ts",
      "sourcePath": "packages/ui/src/components/react/assistant-ui/utils/task.ts",
      "content": "\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type {\n  ToolCallMessagePart,\n  ToolCallMessagePartStatus,\n} from \"@assistant-ui/react\";\n\nexport type TaskViewState =\n  | \"working\"\n  | \"waiting\"\n  | \"done\"\n  | \"failed\"\n  | \"cancelled\";\n\nexport type TaskTiming = NonNullable<ToolCallMessagePart[\"timing\"]>;\n\nexport const TASK_PAGE_SIZE = 4;\n\nconst LABEL_KEYS = [\n  \"description\",\n  \"task\",\n  \"title\",\n  \"name\",\n  \"prompt\",\n  \"query\",\n  \"instructions\",\n];\n\nconst META_KEYS = [\"subagent_type\", \"subagentType\", \"agent\", \"model\"];\n\nfunction firstString(args: unknown, keys: readonly string[]) {\n  if (typeof args !== \"object\" || args === null) return undefined;\n  const record = args as Record<string, unknown>;\n  for (const key of keys) {\n    const value = record[key];\n    if (typeof value === \"string\" && value.trim() !== \"\") return value.trim();\n  }\n  return undefined;\n}\n\nexport function taskStateOf(\n  status: ToolCallMessagePartStatus,\n  isError?: boolean,\n): TaskViewState {\n  if (status.type === \"running\") return \"working\";\n  if (status.type === \"requires-action\") return \"waiting\";\n  if (status.type === \"incomplete\") {\n    return status.reason === \"cancelled\" ? \"cancelled\" : \"failed\";\n  }\n  if (isError) return \"failed\";\n  return \"done\";\n}\n\nexport function taskLabel(toolName: string, args: unknown) {\n  return firstString(args, LABEL_KEYS) ?? toolName;\n}\n\nexport function taskMeta(args: unknown) {\n  return firstString(args, META_KEYS);\n}\n\nexport function formatElapsed(ms: number) {\n  if (ms < 1000) return \"<1s\";\n  const seconds = ms / 1000;\n  if (seconds < 10) return `${(Math.floor(seconds * 10) / 10).toFixed(1)}s`;\n  if (seconds < 60) return `${Math.floor(seconds)}s`;\n  return `${Math.floor(seconds / 60)}m ${Math.floor(seconds % 60)}s`;\n}\n\nexport function useTaskElapsed(\n  timing: TaskTiming | undefined,\n  running: boolean,\n) {\n  const ticking =\n    timing !== undefined && timing.completedAt === undefined && running;\n  const [now, setNow] = useState(() => Date.now());\n  const [wasTicking, setWasTicking] = useState(ticking);\n  if (wasTicking !== ticking) {\n    setWasTicking(ticking);\n    if (ticking) setNow(Date.now());\n  }\n\n  useEffect(() => {\n    if (!ticking) return undefined;\n    const id = setInterval(() => setNow(Date.now()), 1000);\n    return () => clearInterval(id);\n  }, [ticking]);\n\n  if (timing === undefined) return undefined;\n  if (timing.completedAt !== undefined) {\n    return Math.max(0, timing.completedAt - timing.startedAt);\n  }\n  if (!ticking) return undefined;\n  return Math.max(0, now - timing.startedAt);\n}\n"
    }
  ]
}