import { useState } from "react"; import { ChatBubbleList, type ChatMessageProp } from "@godxjp/ui/data-display"; import { ChatComposer } from "@godxjp/ui/data-entry"; import { Text } from "@godxjp/ui/general"; import { AppShell, Flex, MasterDetail, PageContainer, Sidebar, type SidebarSectionProp, Topbar, } from "@godxjp/ui/layout"; import { Conversations, type ConversationsEntryProp } from "@godxjp/ui/navigation"; import { Bot, MessageSquare, Pencil, Settings, Trash2, Users } from "lucide-react"; /** * Conversations — the session rail of a chat surface (Ant Design X `Conversations`). * * This page is the whole surface gh#559 is about: the rail on the left, the feed and the composer * on the right. They were shipped one half at a time, and the missing half is why every consumer * hand-rolled a stack of full-width Buttons — one tab stop per conversation, no per-row menu, no * recency buckets. * * The three cards below are one axis each, at rest, so the API is visible without clicking: * the live surface, the recency buckets (`groupable`), and the flat rail with a divider and a * disabled row. Composed only from real @godxjp/ui components. */ const sections: SidebarSectionProp[] = [ { label: "アシスタント", items: [ { id: "chat", label: "チャット", icon: MessageSquare }, { id: "agents", label: "エージェント", icon: Bot }, { id: "members", label: "メンバー", icon: Users }, ], }, { label: "管理", items: [{ id: "settings", label: "設定", icon: Settings }] }, ]; /** The rail's data, bucketed by recency — the `group` field is what `groupable` reads. */ const HISTORY: ConversationsEntryProp[] = [ { key: "invoice", label: "請求書の下書きを直す", group: "今日" }, { key: "expense", label: "経費精算の規則をまとめる", group: "今日" }, { key: "travel", label: "出張手当の確認", group: "過去7日間" }, { key: "onboarding", label: "入社手続きのチェックリスト", group: "過去7日間" }, { key: "payroll", label: "給与計算の締め日について", group: "それ以前" }, ]; /** A flat rail — no buckets, one rule, one archived row. */ const FLAT: ConversationsEntryProp[] = [ { key: "invoice", label: "請求書の下書きを直す", icon: }, { key: "expense", label: "経費精算の規則をまとめる", icon: }, { type: "divider", key: "rule" }, { key: "archived", label: "アーカイブ済みの相談", icon: , disabled: true }, ]; const TRANSCRIPT: Record = { invoice: [ { id: "m1", role: "user", content: "先月分の請求書の下書きを直したいです。" }, { id: "m2", role: "assistant", content: "差分は3件でした。金額、支払期日、宛名のうちどれから直しますか。", }, ], expense: [ { id: "m1", role: "user", content: "経費精算の規則を3行でまとめてください。" }, { id: "m2", role: "assistant", content: "領収書は原本、申請は当月末まで、3万円以上は事前承認が要ります。", }, ], }; export default function ConversationsDoc() { const [active, setActive] = useState("invoice"); const [draft, setDraft] = useState(""); const menu = { items: [ { key: "rename", label: "名前を変更", icon: }, { key: "delete", label: "削除", icon: , danger: true }, ], }; return ( {}} product={{ name: "CoreDesk", role: "アシスタント", color: "hsl(var(--primary))" }} /> } topbar={} > {/* THE SURFACE. The rail is the master; the feed and the composer are the detail. */} setActive("invoice") }} menu={menu} /> } > setDraft("")} placeholder="メッセージを入力" /> {/* BUCKETS, EXPANDED. `groupable` with no options groups but never collapses. */} recency buckets · groupable バケットの見出しは同じローヴィング順に入るので、畳む操作もレールを離れずに届く。 {/* FLAT, WITH A RULE AND A DISABLED ROW. */} flat rail · divider and a disabled row 無効な行は見えるが選べず、矢印キーの巡回からも外れる。無効なコントロールは フォーカスを受け取らないため。 ); }