{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "support-inbox",
  "type": "registry:block",
  "title": "Support inbox",
  "description": "Maintains a clear handling path across queue, conversation, and customer context.",
  "dependencies": [
    "motion"
  ],
  "categories": [
    "page-block"
  ],
  "docs": "https://motion-lexicon.pages.dev/en/components/support-inbox/",
  "meta": {
    "engines": [
      "motion"
    ],
    "runtimeCost": "light",
    "signature": "Conversation selection, reply delivery, and queue state share one handling context."
  },
  "files": [
    {
      "path": "src/registry/blocks/support-inbox.tsx",
      "type": "registry:component",
      "target": "components/motion-lexicon/blocks/support-inbox.tsx",
      "content": "\"use client\";\n\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport { useEffect, useRef, useState } from \"react\";\n\ntype Locale = \"zh\" | \"en\";\ntype SendState = \"idle\" | \"sending\" | \"sent\";\n\nexport type SupportInboxBlockProps = {\n  locale?: Locale;\n  className?: string;\n};\n\nconst ease = [0.23, 1, 0.32, 1] as const;\n\nexport function SupportInboxBlock({ locale = \"en\", className = \"\" }: SupportInboxBlockProps) {\n  const reduced = useReducedMotion();\n  const copy = locale === \"zh\" ? zh : en;\n  const [activeId, setActiveId] = useState(copy.conversations[0].id);\n  const [reply, setReply] = useState(\"\");\n  const [sendState, setSendState] = useState<SendState>(\"idle\");\n  const timer = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const active = copy.conversations.find((conversation) => conversation.id === activeId) ?? copy.conversations[0];\n\n  useEffect(() => () => {\n    if (timer.current) clearTimeout(timer.current);\n  }, []);\n\n  function sendReply() {\n    if (!reply.trim() || sendState === \"sending\") return;\n    if (timer.current) clearTimeout(timer.current);\n    setSendState(\"sending\");\n    timer.current = setTimeout(() => {\n      setSendState(\"sent\");\n      setReply(\"\");\n      timer.current = setTimeout(() => setSendState(\"idle\"), 1200);\n    }, reduced ? 0 : 720);\n  }\n\n  return (\n    <section 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}`} data-page-block=\"support-inbox\">\n      <header className=\"flex min-h-14 items-center justify-between gap-4 border-b border-neutral-200 bg-white px-4 sm:px-6 dark:border-white/10 dark:bg-[#1b1b1b]\">\n        <a className=\"flex min-h-11 items-center gap-2.5 rounded-lg font-semibold tracking-[-.02em] focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500\" href=\"#relay-inbox\">\n          <span className=\"grid size-8 place-items-center rounded-[10px] bg-neutral-950 text-white dark:bg-neutral-50 dark:text-neutral-950\" aria-hidden=\"true\"><svg viewBox=\"0 0 24 24\" className=\"size-4 fill-none stroke-current\" strokeWidth=\"1.8\"><path d=\"M5 6h14v10H9l-4 3V6Z\" /><path d=\"M8 10h8M8 13h5\" /></svg></span>\n          Relay\n        </a>\n        <div className=\"hidden min-w-0 flex-1 justify-center md:flex\"><label className=\"flex min-h-11 w-full max-w-sm items-center gap-2 rounded-lg border border-neutral-900/10 bg-white px-3 text-[11px] text-neutral-400 shadow-sm focus-within:ring-2 focus-within:ring-blue-500 dark:border-white/10 dark:bg-white/5\"><span aria-hidden=\"true\">⌕</span><input className=\"min-w-0 flex-1 bg-transparent text-[16px] text-neutral-950 outline-none placeholder:text-neutral-400 md:text-[11px] dark:text-white\" placeholder={copy.search} /></label></div>\n        <button className=\"min-h-11 rounded-lg border border-neutral-300 bg-white px-4 text-[12px] font-medium focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:border-white/15 dark:bg-transparent\" type=\"button\">{copy.settings}</button>\n      </header>\n\n      <main className=\"grid min-h-[655px] lg:grid-cols-[260px_minmax(360px,1fr)_260px]\" id=\"relay-inbox\">\n        <aside className=\"border-b border-neutral-200 bg-[#fafafa] p-3 lg:border-b-0 lg:border-r dark:border-white/10 dark:bg-[#181818]\">\n          <div className=\"flex min-h-11 items-center justify-between gap-3 px-2\"><h1 className=\"text-[13px] font-semibold\">{copy.inbox}</h1><span className=\"font-mono text-[9px] text-neutral-500\">{copy.conversations.length}</span></div>\n          <div className=\"mt-2 grid max-h-[238px] gap-1 overflow-auto lg:max-h-none\">\n            {copy.conversations.map((conversation) => (\n              <button\n                aria-pressed={conversation.id === active.id}\n                className={`min-h-[84px] rounded-lg p-3 text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 ${conversation.id === active.id ? \"bg-neutral-950 text-white dark:bg-neutral-50 dark:text-neutral-950\" : \"hover:bg-neutral-100 dark:hover:bg-white/5\"}`}\n                key={conversation.id}\n                onClick={() => { setActiveId(conversation.id); setSendState(\"idle\"); }}\n                type=\"button\"\n              >\n                <span className=\"flex items-center justify-between gap-3\"><strong className=\"truncate text-[11px]\">{conversation.name}</strong><span className=\"shrink-0 font-mono text-[8px] text-neutral-400\">{conversation.time}</span></span>\n                <span className=\"mt-2 block truncate text-[10px] text-neutral-500 dark:text-neutral-400\">{conversation.subject}</span>\n                <span className=\"mt-2 flex items-center gap-2\"><i className={`size-1.5 rounded-full ${conversation.priority ? \"bg-amber-500\" : \"bg-emerald-500\"}`} /><small className=\"text-[9px] text-neutral-400\">{conversation.channel}</small></span>\n              </button>\n            ))}\n          </div>\n        </aside>\n\n        <section className=\"min-w-0 bg-white dark:bg-[#1d1d1b]\" aria-live=\"polite\">\n          <AnimatePresence mode=\"wait\" initial={false}>\n            <motion.div className=\"grid min-h-[655px] grid-rows-[auto_1fr_auto]\" key={active.id} initial={{ opacity: 0, x: reduced ? 0 : 12 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: reduced ? 0 : -10 }} transition={{ duration: reduced ? 0 : .22, ease }}>\n              <header className=\"flex min-h-[76px] items-center justify-between gap-4 border-b border-neutral-900/10 px-4 sm:px-5 dark:border-white/10\">\n                <div className=\"min-w-0\"><span className=\"font-mono text-[9px] text-neutral-500 dark:text-neutral-400\">{active.ticket}</span><h2 className=\"mt-1 truncate text-[15px] font-semibold tracking-[-.02em]\">{active.subject}</h2></div>\n                <button className=\"min-h-11 shrink-0 rounded-lg bg-neutral-950 px-4 text-[11px] font-semibold text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:bg-neutral-50 dark:text-neutral-950\" type=\"button\">{copy.resolve}</button>\n              </header>\n\n              <div className=\"min-h-0 overflow-auto p-4 sm:p-5\">\n                <div className=\"mx-auto grid max-w-xl gap-5\">\n                  {active.messages.map((message, index) => <motion.article className={`grid gap-2 ${message.agent ? \"justify-items-end\" : \"justify-items-start\"}`} initial={{ opacity: 0, y: reduced ? 0 : 7 }} animate={{ opacity: 1, y: 0 }} transition={{ delay: reduced ? 0 : index * .05 }} key={`${active.id}-${index}`}><span className=\"text-[9px] text-neutral-400\">{message.author} · {message.time}</span><p className={`m-0 max-w-[88%] rounded-[10px] px-4 py-3 text-[11px] leading-5 ${message.agent ? \"rounded-br-md bg-neutral-950 text-white dark:bg-neutral-50 dark:text-neutral-950\" : \"rounded-bl-md bg-neutral-100 text-neutral-700 dark:bg-white/7 dark:text-neutral-200\"}`}>{message.body}</p></motion.article>)}\n                  {sendState === \"sent\" ? <motion.article className=\"grid justify-items-end gap-2\" initial={{ opacity: 0, y: reduced ? 0 : 8 }} animate={{ opacity: 1, y: 0 }}><span className=\"text-[9px] text-neutral-400\">{copy.you} · {copy.now}</span><p className=\"m-0 max-w-[88%] rounded-[10px] rounded-br-md bg-neutral-950 px-4 py-3 text-[11px] leading-5 text-white dark:bg-neutral-50 dark:text-neutral-950\">{copy.sentMessage}</p></motion.article> : null}\n                </div>\n              </div>\n\n              <div className=\"border-t border-neutral-900/10 p-3 sm:p-4 dark:border-white/10\">\n                <div className=\"mx-auto max-w-xl rounded-[10px] border border-neutral-900/10 bg-neutral-50 p-2 shadow-[inset_0_1px_2px_rgba(28,25,23,.04)] focus-within:ring-2 focus-within:ring-blue-500 dark:border-white/10 dark:bg-black/10\">\n                  <label className=\"sr-only\" htmlFor={`relay-reply-${active.id}`}>{copy.reply}</label>\n                  <textarea className=\"min-h-20 w-full resize-none bg-transparent px-2 py-2 text-[16px] leading-5 outline-none placeholder:text-neutral-400 md:text-[11px]\" id={`relay-reply-${active.id}`} onChange={(event) => { setReply(event.target.value); setSendState(\"idle\"); }} onKeyDown={(event) => { if ((event.metaKey || event.ctrlKey) && event.key === \"Enter\") { event.preventDefault(); sendReply(); } }} placeholder={copy.placeholder} value={reply} />\n                  <div className=\"flex items-center justify-between gap-3 px-1 pb-1\"><span className=\"font-mono text-[8px] text-neutral-400\">{sendState === \"sending\" ? copy.sending : sendState === \"sent\" ? copy.sent : copy.shortcut}</span><button className=\"min-h-11 rounded-lg bg-neutral-950 px-4 text-[11px] font-semibold text-white disabled:cursor-not-allowed disabled:opacity-45 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-600 dark:bg-neutral-50 dark:text-neutral-950\" disabled={!reply.trim() || sendState === \"sending\"} onClick={sendReply} type=\"button\">{sendState === \"sending\" ? copy.sending : copy.send}</button></div>\n                </div>\n              </div>\n            </motion.div>\n          </AnimatePresence>\n        </section>\n\n        <aside className=\"hidden border-l border-neutral-200 bg-[#fafafa] p-4 lg:block dark:border-white/10 dark:bg-[#181818]\">\n          <div className=\"flex items-center gap-3\"><span className={`grid size-10 place-items-center rounded-full text-[12px] font-bold ${active.avatarTone}`}>{active.initials}</span><div className=\"min-w-0\"><strong className=\"block truncate text-[12px]\">{active.name}</strong><span className=\"text-[9px] text-neutral-400\">{active.company}</span></div></div>\n          <dl className=\"mt-6 grid gap-4\">\n            {active.details.map((detail) => <div className=\"grid gap-1 border-b border-neutral-900/[.07] pb-3 dark:border-white/[.07]\" key={detail.label}><dt className=\"text-[9px] text-neutral-400\">{detail.label}</dt><dd className=\"m-0 text-[11px] font-medium\">{detail.value}</dd></div>)}\n          </dl>\n          <section className=\"mt-6 border-t border-neutral-200 pt-4 dark:border-white/10\" aria-labelledby={`relay-context-${active.id}`}><h3 className=\"text-[10px] font-semibold\" id={`relay-context-${active.id}`}>{copy.context}</h3><p className=\"mb-0 mt-3 text-[10px] leading-5 text-neutral-500 dark:text-neutral-300\">{active.context}</p></section>\n        </aside>\n      </main>\n    </section>\n  );\n}\n\ntype Conversation = {\n  id: string; name: string; initials: string; avatarTone: string; company: string; time: string; subject: string; ticket: string; channel: string; priority: boolean; context: string;\n  messages: Array<{ author: string; time: string; body: string; agent?: boolean }>;\n  details: Array<{ label: string; value: string }>;\n};\n\nconst enConversations: Conversation[] = [\n  { id: \"conv-1\", name: \"Maya Chen\", initials: \"MC\", avatarTone: \"bg-[#d6b08e] text-[#5a321c]\", company: \"Morrow Labs\", time: \"4m\", subject: \"Workspace members cannot accept invites\", ticket: \"TICKET · 2841\", channel: \"Email\", priority: true, context: \"Morrow Labs is rolling out Atlas to 42 teammates this week. Their security review was approved yesterday.\", messages: [{ author: \"Maya\", time: \"09:14\", body: \"Three teammates see an expired-link message even though the invitations were sent this morning.\" }, { author: \"You\", time: \"09:18\", body: \"I found the affected invitation batch. I’m checking the workspace domain policy before I refresh those links.\", agent: true }, { author: \"Maya\", time: \"09:21\", body: \"Thanks. Everyone is using our @morrowlabs.com address.\" }], details: [{ label: \"Plan\", value: \"Scale\" }, { label: \"Workspace\", value: \"morrow-labs\" }, { label: \"Region\", value: \"Singapore\" }, { label: \"Owner\", value: \"You\" }] },\n  { id: \"conv-2\", name: \"Leo Martins\", initials: \"LM\", avatarTone: \"bg-[#a8bfd3] text-[#24405a]\", company: \"Fable Studio\", time: \"12m\", subject: \"Need a copy of the July invoice\", ticket: \"TICKET · 2837\", channel: \"Chat\", priority: false, context: \"Fable Studio has been active for 18 months and recently added two contractor seats.\", messages: [{ author: \"Leo\", time: \"09:02\", body: \"Could you send the July invoice with our updated legal address?\" }, { author: \"You\", time: \"09:07\", body: \"I can regenerate it after confirming the billing profile. The new address is already saved.\", agent: true }], details: [{ label: \"Plan\", value: \"Team\" }, { label: \"Workspace\", value: \"fable-studio\" }, { label: \"Region\", value: \"Portugal\" }, { label: \"Owner\", value: \"You\" }] },\n  { id: \"conv-3\", name: \"Nora Singh\", initials: \"NS\", avatarTone: \"bg-[#b6c9aa] text-[#304b27]\", company: \"Aster Works\", time: \"26m\", subject: \"Export stopped at 92 percent\", ticket: \"TICKET · 2832\", channel: \"Email\", priority: true, context: \"Aster Works exports weekly research archives. Their latest archive contains 1,840 records.\", messages: [{ author: \"Nora\", time: \"08:41\", body: \"Our weekly export has stayed at 92 percent for about fifteen minutes.\" }, { author: \"You\", time: \"08:49\", body: \"The archive is still processing attachments. I’m watching the job and can restart only that stage if it stalls.\", agent: true }], details: [{ label: \"Plan\", value: \"Scale\" }, { label: \"Workspace\", value: \"aster-works\" }, { label: \"Region\", value: \"London\" }, { label: \"Owner\", value: \"You\" }] },\n];\n\nfunction localizeConversations(): Conversation[] {\n  const labels = [\"方案\", \"工作区\", \"区域\", \"负责人\"];\n  return enConversations.map((conversation, index) => ({ ...conversation, time: [\"4 分钟\", \"12 分钟\", \"26 分钟\"][index], subject: [\"工作区成员无法接受邀请\", \"需要一份七月发票\", \"导出停在 92%\"][index], channel: conversation.channel === \"Email\" ? \"邮件\" : \"在线聊天\", context: [\"Morrow Labs 本周正在向 42 位团队成员推广 Atlas，安全审查已于昨天通过。\", \"Fable Studio 已使用产品 18 个月，最近增加了两个外部协作者席位。\", \"Aster Works 每周导出研究档案，最新档案包含 1,840 条记录。\"][index], messages: index === 0 ? [{ author: \"Maya\", time: \"09:14\", body: \"三位同事看到链接已过期，但这些邀请都是今天上午发送的。\" }, { author: \"我\", time: \"09:18\", body: \"我已经找到受影响的邀请批次，正在检查工作区域名策略，然后会刷新链接。\", agent: true }, { author: \"Maya\", time: \"09:21\", body: \"谢谢，大家都使用 @morrowlabs.com 邮箱。\" }] : index === 1 ? [{ author: \"Leo\", time: \"09:02\", body: \"可以提供更新公司地址后的七月发票吗？\" }, { author: \"我\", time: \"09:07\", body: \"新的账单地址已经保存，我确认资料后会重新生成发票。\", agent: true }] : [{ author: \"Nora\", time: \"08:41\", body: \"本周导出已经停在 92% 大约十五分钟。\" }, { author: \"我\", time: \"08:49\", body: \"系统仍在处理附件。我正在观察任务，如果停滞会只重启这个阶段。\", agent: true }], details: conversation.details.map((detail, detailIndex) => ({ label: labels[detailIndex], value: detail.value === \"You\" ? \"我\" : detail.value })) }));\n}\n\nconst en = { search: \"Search conversations\", settings: \"Inbox settings\", inbox: \"Priority inbox\", resolve: \"Resolve\", reply: \"Reply\", placeholder: \"Write a clear next step…\", shortcut: \"⌘ ENTER TO SEND\", send: \"Send reply\", sending: \"Sending…\", sent: \"Reply sent\", sentMessage: \"I refreshed the invitation batch and confirmed the new links are active.\", you: \"You\", now: \"Now\", context: \"Customer context\", conversations: enConversations };\nconst zh = { search: \"搜索会话\", settings: \"收件箱设置\", inbox: \"优先收件箱\", resolve: \"解决会话\", reply: \"回复\", placeholder: \"写下清楚的下一步…\", shortcut: \"⌘ ENTER 发送\", send: \"发送回复\", sending: \"发送中…\", sent: \"回复已发送\", sentMessage: \"我已刷新邀请批次，并确认新链接可以正常使用。\", you: \"我\", now: \"刚刚\", context: \"客户背景\", conversations: localizeConversations() };\n"
    }
  ]
}
