{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "agent-workspace",
  "type": "registry:block",
  "title": "Agent workspace",
  "description": "Organizes missions, thinking, tool execution, human approval, and evidence into one continuous agent product page.",
  "dependencies": [
    "motion"
  ],
  "categories": [
    "page-block"
  ],
  "docs": "https://motion-lexicon.pages.dev/en/components/agent-workspace/",
  "meta": {
    "engines": [
      "motion"
    ],
    "runtimeCost": "light",
    "signature": "A mission moves from request through visible execution and human approval into evidence-backed completion."
  },
  "files": [
    {
      "path": "src/registry/blocks/agent-workspace.tsx",
      "type": "registry:component",
      "target": "components/motion-lexicon/blocks/agent-workspace.tsx",
      "content": "\"use client\";\n\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { useEffect, useRef, useState } from \"react\";\n\ntype Locale = \"zh\" | \"en\";\ntype RunState = \"ready\" | \"thinking\" | \"working\" | \"approval\" | \"complete\";\nexport type AgentWorkspaceBlockProps = { locale?: Locale; className?: string };\n\nconst ease = [0.23, 1, 0.32, 1] as const;\n\nexport function AgentWorkspaceBlock({\n  locale = \"en\",\n  className = \"\",\n}: AgentWorkspaceBlockProps) {\n  const reduced = useReducedMotion();\n  const copy = locale === \"zh\" ? zh : en;\n  const [mission, setMission] = useState(0);\n  const [state, setState] = useState<RunState>(\"ready\");\n  const [prompt, setPrompt] = useState(\"\");\n  const timers = useRef<number[]>([]);\n  useEffect(() => () => timers.current.forEach(window.clearTimeout), []);\n  const run = () => {\n    timers.current.forEach(window.clearTimeout);\n    timers.current = [];\n    setPrompt(\"\");\n    setState(\"thinking\");\n    const stages: Array<[RunState, number]> = [\n      [\"working\", reduced ? 0 : 850],\n      [\"approval\", reduced ? 0 : 2100],\n    ];\n    let elapsed = 0;\n    stages.forEach(([next, wait]) => {\n      elapsed += wait;\n      timers.current.push(window.setTimeout(() => setState(next), elapsed));\n    });\n  };\n  const active = copy.missions[mission];\n\n  return (\n    <section\n      className={`min-h-[720px] w-full overflow-hidden rounded-[10px] border border-neutral-200 bg-[#f5f5f5] text-neutral-950 dark:border-white/10 dark:bg-[#151515] dark:text-neutral-50 ${className}`}\n      data-page-block=\"agent-workspace\"\n    >\n      <header className=\"flex min-h-14 items-center gap-3 border-b border-neutral-200 bg-white px-4 dark:border-white/10 dark:bg-[#1b1b1b]\">\n        <a\n          href=\"#relay-workspace\"\n          className=\"flex min-h-11 items-center gap-2.5 rounded-lg font-semibold tracking-[-.02em] outline-none focus-visible:ring-2 focus-visible:ring-blue-600\"\n        >\n          <span className=\"grid size-7 place-items-center rounded-lg bg-neutral-950 text-white dark:bg-neutral-50 dark:text-neutral-950\">\n            <svg\n              viewBox=\"0 0 24 24\"\n              className=\"size-3.5 fill-none stroke-current\"\n              strokeWidth=\"1.7\"\n            >\n              <path d=\"M5 17V7l7 4 7-4v10l-7 4-7-4Z\" />\n              <path d=\"m5 7 7-4 7 4M12 11v10\" />\n            </svg>\n          </span>\n          Relay\n        </a>\n        <span className=\"hidden h-4 w-px bg-neutral-200 sm:block dark:bg-white/10\" />\n        <span className=\"hidden text-[11px] text-neutral-500 sm:block dark:text-neutral-400\">\n          {copy.workspace}\n        </span>\n        <div className=\"ml-auto flex items-center gap-2\">\n          <span\n            className={`size-1.5 rounded-full ${state === \"complete\" ? \"bg-emerald-600\" : state === \"approval\" ? \"bg-amber-500\" : state === \"ready\" ? \"bg-neutral-300 dark:bg-neutral-600\" : \"bg-neutral-950 dark:bg-neutral-50\"}`}\n          />\n          <span className=\"text-[10px] text-neutral-500 dark:text-neutral-400\">\n            {copy.state[state]}\n          </span>\n        </div>\n      </header>\n      <div\n        className=\"grid min-h-[664px] lg:grid-cols-[188px_minmax(0,1fr)_220px]\"\n        id=\"relay-workspace\"\n      >\n        <aside className=\"hidden border-r border-neutral-200 bg-[#fafafa] p-3 lg:flex lg:flex-col dark:border-white/10 dark:bg-[#181818]\">\n          <span className=\"px-3 py-3 text-[10px] font-medium text-neutral-500 dark:text-neutral-400\">\n            {copy.missionsLabel}\n          </span>\n          <nav className=\"grid gap-1\">\n            {copy.missions.map((item, index) => (\n              <button\n                key={item.title}\n                type=\"button\"\n                onClick={() => {\n                  setMission(index);\n                  setState(\"ready\");\n                }}\n                className={`min-h-11 rounded-lg px-3 text-left text-[11px] outline-none focus-visible:ring-2 focus-visible:ring-blue-600 ${mission === index ? \"bg-neutral-950 font-medium text-white dark:bg-neutral-50 dark:text-neutral-950\" : \"text-neutral-500 hover:bg-neutral-100 dark:text-neutral-400 dark:hover:bg-white/5\"}`}\n              >\n                {item.short}\n              </button>\n            ))}\n          </nav>\n          <div className=\"mt-auto border-t border-neutral-200 px-3 pt-4 dark:border-white/10\">\n            <div className=\"flex -space-x-1\">\n              {[\"R\", \"D\", \"Q\"].map((agent, index) => (\n                <span\n                  key={agent}\n                  className={`grid size-7 place-items-center rounded-full border-2 border-[#fafafa] text-[8px] font-semibold text-white dark:border-[#181818] ${[\"bg-neutral-950\", \"bg-neutral-700\", \"bg-neutral-500\"][index]}`}\n                >\n                  {agent}\n                </span>\n              ))}\n            </div>\n            <strong className=\"mt-3 block text-[10px]\">{copy.team}</strong>\n            <span className=\"text-[9px] text-neutral-400\">{copy.teamMeta}</span>\n          </div>\n        </aside>\n        <main className=\"flex min-w-0 flex-col bg-white dark:bg-[#1b1b1b]\">\n          <div className=\"border-b border-neutral-200 px-5 py-6 sm:px-7 dark:border-white/10\">\n            <code className=\"text-[10px] text-neutral-500 dark:text-neutral-400\">\n              {active.kicker}\n            </code>\n            <h1 className=\"mt-2 max-w-[620px] text-[26px] font-semibold leading-[1.1] tracking-[-.03em]\">\n              {active.title}\n            </h1>\n            <p className=\"mt-3 max-w-[620px] text-[12px] leading-5 text-neutral-500 dark:text-neutral-400\">\n              {active.description}\n            </p>\n          </div>\n          <div className=\"grid min-h-14 grid-cols-[74px_1fr] items-center border-b border-neutral-200 px-5 text-[11px] sm:px-7 dark:border-white/10\">\n            <span className=\"text-neutral-400\">{copy.requestLabel}</span>\n            <p className=\"m-0 text-neutral-700 dark:text-neutral-200\">{active.request}</p>\n          </div>\n          <div className=\"flex flex-1 flex-col px-5 py-5 sm:px-7\">\n            <AnimatePresence mode=\"wait\">\n              <motion.div\n                key={`${mission}-${state}`}\n                initial={reduced ? false : { opacity: 0, y: 8 }}\n                animate={{ opacity: 1, y: 0 }}\n                exit={reduced ? undefined : { opacity: 0, y: -6 }}\n                transition={{ duration: reduced ? 0 : 0.3, ease }}\n                className=\"min-h-[286px]\"\n              >\n                {state === \"ready\" ? (\n                  <div>\n                    <div className=\"flex items-center justify-between gap-4 border-b border-neutral-200 pb-4 dark:border-white/10\">\n                      <div>\n                        <strong className=\"block text-[13px]\">{copy.readyTitle}</strong>\n                        <span className=\"mt-1 block text-[10px] text-neutral-500 dark:text-neutral-400\">{copy.readyBody}</span>\n                      </div>\n                      <button\n                        type=\"button\"\n                        onClick={run}\n                        className=\"min-h-11 shrink-0 rounded-lg bg-neutral-950 px-4 text-[10px] font-semibold text-white outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:bg-neutral-50 dark:text-neutral-950\"\n                      >\n                        {copy.start}\n                      </button>\n                    </div>\n                    <ol>\n                      {active.steps.map((step, index) => (\n                        <li key={step} className=\"grid min-h-12 grid-cols-[28px_1fr_auto] items-center gap-3 border-b border-neutral-100 text-[11px] last:border-b-0 dark:border-white/8\">\n                          <code className=\"text-[9px] text-neutral-400\">0{index + 1}</code>\n                          <span>{step}</span>\n                          <span className=\"text-[9px] text-neutral-400\">{copy.queued}</span>\n                        </li>\n                      ))}\n                    </ol>\n                  </div>\n                ) : (\n                  <div>\n                    <div className=\"flex items-center gap-3\">\n                      <span className=\"relative grid size-8 place-items-center rounded-lg border border-neutral-300 dark:border-white/20\">\n                        <span className=\"size-1.5 rounded-full bg-neutral-950 dark:bg-neutral-50\" />\n                        {state !== \"complete\" ? (\n                          <motion.i\n                            className=\"absolute inset-0 rounded-lg border border-neutral-500/50\"\n                            animate={\n                              reduced\n                                ? undefined\n                                : { scale: [1, 1.3], opacity: [0.5, 0] }\n                            }\n                            transition={{ duration: 1.4, repeat: Infinity }}\n                          />\n                        ) : null}\n                      </span>\n                      <div>\n                        <strong className=\"block text-[12px]\">\n                          {copy.runTitle[state]}\n                        </strong>\n                        <span className=\"text-[9px] text-neutral-400\">\n                          {copy.runMeta[state]}\n                        </span>\n                      </div>\n                    </div>\n                    <ol className=\"mt-4 border-t border-neutral-200 dark:border-white/10\">\n                      {active.steps.map((step, index) => {\n                        const visible =\n                          state === \"complete\" ||\n                          (state === \"approval\" && index < 3) ||\n                          (state === \"working\" && index < 2) ||\n                          (state === \"thinking\" && index < 1);\n                        const current =\n                          (state === \"thinking\" && index === 0) ||\n                          (state === \"working\" && index === 1) ||\n                          (state === \"approval\" && index === 2);\n                        return (\n                          <li\n                            key={step}\n                            className=\"grid min-h-12 grid-cols-[28px_1fr_auto] items-center gap-3 border-b border-neutral-100 dark:border-white/8\"\n                          >\n                            <span\n                              className={`grid size-5 place-items-center rounded-md border text-[8px] ${visible && !current ? \"border-emerald-600 bg-emerald-600 text-white\" : current ? \"border-neutral-950 text-neutral-950 dark:border-neutral-50 dark:text-neutral-50\" : \"border-neutral-200 text-neutral-300 dark:border-white/10\"}`}\n                            >\n                              {visible && !current ? \"✓\" : index + 1}\n                            </span>\n                            <span\n                              className={`text-[11px] ${visible ? \"text-neutral-700 dark:text-neutral-200\" : \"text-neutral-300 dark:text-neutral-600\"}`}\n                            >\n                              {step}\n                            </span>\n                            <span className=\"text-[9px] text-neutral-400\">{current ? copy.state[state] : visible ? copy.state.complete : copy.state.ready}</span>\n                          </li>\n                        );\n                      })}\n                    </ol>\n                    {state === \"approval\" ? (\n                      <div className=\"mt-4 flex items-center gap-3 border-t border-amber-300 pt-4 dark:border-amber-500/30\">\n                        <span className=\"text-amber-600\">!</span>\n                        <span className=\"text-[10px] text-amber-800 dark:text-amber-200\">\n                          {copy.approval}\n                        </span>\n                        <button\n                          type=\"button\"\n                          onClick={() => setState(\"complete\")}\n                          className=\"ml-auto min-h-11 rounded-lg bg-neutral-950 px-3 text-[9px] font-semibold text-white dark:bg-neutral-50 dark:text-neutral-950\"\n                        >\n                          {copy.approve}\n                        </button>\n                      </div>\n                    ) : null}\n                  </div>\n                )}\n              </motion.div>\n            </AnimatePresence>\n          </div>\n          <form\n              onSubmit={(event) => {\n                event.preventDefault();\n                if (prompt.trim()) run();\n              }}\n              className=\"m-4 mt-0 flex min-h-13 items-center gap-2 rounded-[10px] border border-neutral-200 bg-white p-1.5 focus-within:border-neutral-950 focus-within:ring-2 focus-within:ring-blue-600/20 sm:mx-7 dark:border-white/10 dark:bg-[#202020] dark:focus-within:border-neutral-50\"\n            >\n              <button\n                type=\"button\"\n                aria-label={copy.attach}\n                className=\"grid size-11 shrink-0 place-items-center rounded-lg text-neutral-400 hover:bg-neutral-100 dark:hover:bg-white/5\"\n              >\n                ＋\n              </button>\n              <input\n                value={prompt}\n                onChange={(event) => setPrompt(event.target.value)}\n                placeholder={copy.placeholder}\n                aria-label={copy.placeholder}\n                className=\"min-w-0 flex-1 bg-transparent text-[12px] outline-none placeholder:text-neutral-400\"\n              />\n              <button\n                type=\"submit\"\n                disabled={!prompt.trim()}\n                className=\"grid size-11 shrink-0 place-items-center rounded-lg bg-neutral-950 text-white disabled:opacity-30 dark:bg-neutral-50 dark:text-neutral-950\"\n              >\n                ↑\n              </button>\n          </form>\n        </main>\n        <aside className=\"hidden border-l border-neutral-200 bg-[#fafafa] p-4 lg:block dark:border-white/10 dark:bg-[#181818]\">\n          <span className=\"text-[10px] font-medium text-neutral-500 dark:text-neutral-400\">\n            {copy.evidence}\n          </span>\n          <div className=\"mt-4 border-t border-neutral-200 dark:border-white/10\">\n            {active.evidence.map((item, index) => (\n              <div\n                key={item}\n                className=\"grid min-h-[76px] grid-cols-[22px_1fr] content-center gap-2 border-b border-neutral-200 py-3 dark:border-white/10\"\n              >\n                <span className=\"grid size-5 place-items-center rounded-md border border-neutral-300 text-[8px] text-neutral-500 dark:border-white/20\">\n                  {state === \"complete\" ? \"✓\" : index + 1}\n                </span>\n                <div>\n                  <strong className=\"block text-[10px]\">{item}</strong>\n                  <span className=\"mt-1 block text-[8px] text-neutral-400\">\n                    {copy.evidenceState[index]}\n                  </span>\n                </div>\n              </div>\n            ))}\n          </div>\n        </aside>\n      </div>\n    </section>\n  );\n}\n\nconst en = {\n  workspace: \"Agent product workspace\",\n  requestLabel: \"Request\",\n  queued: \"Queued\",\n  evidenceState: [\"Verified\", \"Available\", \"Recorded\"],\n  missionsLabel: \"Missions\",\n  team: \"Interface team\",\n  teamMeta: \"3 agents · 1 active\",\n  readyTitle: \"Ready to run this mission\",\n  readyBody: \"The agent will show its evidence as it works.\",\n  start: \"Start mission\",\n  attach: \"Add context\",\n  placeholder: \"Ask Relay to continue…\",\n  evidence: \"Evidence\",\n  approval: \"The preview is ready. Publish it to the shared review URL?\",\n  approve: \"Approve\",\n  state: {\n    ready: \"Ready\",\n    thinking: \"Thinking\",\n    working: \"Working\",\n    approval: \"Needs approval\",\n    complete: \"Complete\",\n  },\n  runTitle: {\n    ready: \"Ready\",\n    thinking: \"Reading the request\",\n    working: \"Building the interface\",\n    approval: \"Waiting for approval\",\n    complete: \"Mission complete\",\n  },\n  runMeta: {\n    ready: \"\",\n    thinking: \"Comparing product context\",\n    working: \"Editing and checking\",\n    approval: \"One decision required\",\n    complete: \"All evidence recorded\",\n  },\n  missions: [\n    {\n      short: \"Agent launch\",\n      kicker: \"Interface mission\",\n      title: \"Ship the agent UI collection\",\n      description:\n        \"Turn visible agent work into a product surface people can understand, trust, and want to reuse.\",\n      request:\n        \"Build a cohesive set of beautiful components for thinking, tools, approvals, and handoffs.\",\n      steps: [\n        \"Map the agent interaction states\",\n        \"Build the component collection\",\n        \"Publish a browser preview\",\n        \"Record acceptance evidence\",\n      ],\n      evidence: [\"11 live components\", \"Reduced motion\", \"Registry source\"],\n    },\n    {\n      short: \"Research brief\",\n      kicker: \"Research mission\",\n      title: \"Turn sources into a decision\",\n      description:\n        \"Keep retrieval, synthesis, citations, and the next action connected in one calm workspace.\",\n      request:\n        \"Compare the strongest AI interface patterns and recommend a visual direction.\",\n      steps: [\n        \"Collect primary references\",\n        \"Group interaction patterns\",\n        \"Write the recommendation\",\n        \"Attach cited evidence\",\n      ],\n      evidence: [\"8 primary sources\", \"3 design paths\", \"Cited answer\"],\n    },\n    {\n      short: \"Release review\",\n      kicker: \"Review mission\",\n      title: \"Close the release with proof\",\n      description:\n        \"Move checks, screenshots, approvals, and deployment into one visible completion path.\",\n      request:\n        \"Review the release, fix actionable defects, and return one production address.\",\n      steps: [\n        \"Run quality gates\",\n        \"Inspect desktop and mobile\",\n        \"Approve production deploy\",\n        \"Verify the live artifact\",\n      ],\n      evidence: [\"197 unit tests\", \"34 browser checks\", \"Live artifact\"],\n    },\n  ],\n};\nconst zh = {\n  workspace: \"Agent 产品工作区\",\n  requestLabel: \"需求\",\n  queued: \"待执行\",\n  evidenceState: [\"已验证\", \"可使用\", \"已记录\"],\n  missionsLabel: \"任务\",\n  team: \"界面协作组\",\n  teamMeta: \"3 个 Agent · 1 个执行中\",\n  readyTitle: \"任务已经可以开始\",\n  readyBody: \"Agent 会在执行过程中持续展示证据。\",\n  start: \"开始任务\",\n  attach: \"添加上下文\",\n  placeholder: \"让 Relay 继续…\",\n  evidence: \"执行证据\",\n  approval: \"预览已经完成，是否发布到共享评审地址？\",\n  approve: \"允许发布\",\n  state: {\n    ready: \"待开始\",\n    thinking: \"思考中\",\n    working: \"执行中\",\n    approval: \"等待审批\",\n    complete: \"已完成\",\n  },\n  runTitle: {\n    ready: \"待开始\",\n    thinking: \"正在读取需求\",\n    working: \"正在构建界面\",\n    approval: \"正在等待审批\",\n    complete: \"任务完成\",\n  },\n  runMeta: {\n    ready: \"\",\n    thinking: \"比较产品上下文\",\n    working: \"编辑并运行检查\",\n    approval: \"需要一个明确决定\",\n    complete: \"所有证据已经记录\",\n  },\n  missions: [\n    {\n      short: \"Agent 组件发布\",\n      kicker: \"界面任务\",\n      title: \"发布 Agent 组件组\",\n      description:\n        \"把 Agent 的执行过程转化为人能理解、信任并愿意复用的产品界面。\",\n      request: \"为思考、工具、审批与交接设计一组统一且好看的组件。\",\n      steps: [\n        \"梳理 Agent 交互状态\",\n        \"构建组件集合\",\n        \"发布浏览器预览\",\n        \"记录验收证据\",\n      ],\n      evidence: [\"11 个实时组件\", \"支持减弱动效\", \"Registry 源码\"],\n    },\n    {\n      short: \"研究简报\",\n      kicker: \"研究任务\",\n      title: \"把来源转化为决策\",\n      description:\n        \"让检索、综合、引用与下一步行动在同一个安静工作区中连续发生。\",\n      request: \"比较优秀的 AI 界面模式，并给出视觉方向建议。\",\n      steps: [\"收集一手参考\", \"整理交互模式\", \"形成建议\", \"附上引用证据\"],\n      evidence: [\"8 个一手来源\", \"3 个设计方向\", \"带引用的回答\"],\n    },\n    {\n      short: \"发布验收\",\n      kicker: \"验收任务\",\n      title: \"用证据完成发布\",\n      description: \"把检查、截图、审批与部署组织成一条清楚的完成路径。\",\n      request: \"验收版本，处理可修复问题，最终只返回一个生产地址。\",\n      steps: [\"运行质量门禁\", \"检查桌面和手机\", \"批准生产部署\", \"验证线上产物\"],\n      evidence: [\"197 个单元测试\", \"34 个浏览器检查\", \"线上产物\"],\n    },\n  ],\n};\n"
    }
  ]
}
