@neuronection/assistant-ui 0.51.0 → 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +4 -1
  3. package/dist/chat-branch-tree.d.ts +15 -2
  4. package/dist/chat-composer.d.ts +50 -2
  5. package/dist/chat-composer.js +1 -1
  6. package/dist/chat-core.d.ts +54 -4
  7. package/dist/chat-launcher.js +1 -1
  8. package/dist/chat-markdown.d.ts +8 -1
  9. package/dist/chat-markdown.js +1 -1
  10. package/dist/chat-message.d.ts +16 -1
  11. package/dist/chat-message.js +1 -1
  12. package/dist/chat-panel.js +1 -1
  13. package/dist/chat-pending.d.ts +25 -0
  14. package/dist/chat-pending.js +3 -0
  15. package/dist/chat-pending.js.map +1 -0
  16. package/dist/chat-reasoning.d.ts +10 -2
  17. package/dist/chat-reasoning.js +1 -1
  18. package/dist/chat-session-list.d.ts +7 -0
  19. package/dist/chat-session-list.js +1 -1
  20. package/dist/chat-suggestion-card.d.ts +19 -0
  21. package/dist/chat-suggestion-card.js +3 -0
  22. package/dist/chat-suggestion-card.js.map +1 -0
  23. package/dist/chat-tool-card.js +1 -1
  24. package/dist/chat-transcript.d.ts +81 -8
  25. package/dist/chat-transcript.js +1 -1
  26. package/dist/chat-turn-status.js +1 -1
  27. package/dist/chat-welcome.d.ts +23 -0
  28. package/dist/chat-welcome.js +3 -0
  29. package/dist/chat-welcome.js.map +1 -0
  30. package/dist/{chunk-PC3CE4W2.js → chunk-22MIIWC7.js} +45 -7
  31. package/dist/chunk-22MIIWC7.js.map +1 -0
  32. package/dist/{chunk-GDGKARRB.js → chunk-5C2OLUDR.js} +11 -2
  33. package/dist/chunk-5C2OLUDR.js.map +1 -0
  34. package/dist/{chunk-5BJ4VULP.js → chunk-EQPXM2R4.js} +23 -10
  35. package/dist/chunk-EQPXM2R4.js.map +1 -0
  36. package/dist/chunk-F4BTJQLK.js +43 -0
  37. package/dist/chunk-F4BTJQLK.js.map +1 -0
  38. package/dist/{chunk-TQDTFPM4.js → chunk-GIQZNEG5.js} +3 -3
  39. package/dist/chunk-GIQZNEG5.js.map +1 -0
  40. package/dist/{chunk-E3NZM4U7.js → chunk-GO4ILLS6.js} +17 -4
  41. package/dist/chunk-GO4ILLS6.js.map +1 -0
  42. package/dist/chunk-HXU6P5FD.js +479 -0
  43. package/dist/chunk-HXU6P5FD.js.map +1 -0
  44. package/dist/{chunk-MFM4W6ZO.js → chunk-I4R5BRC7.js} +3 -3
  45. package/dist/{chunk-MFM4W6ZO.js.map → chunk-I4R5BRC7.js.map} +1 -1
  46. package/dist/{chunk-5U45TQJE.js → chunk-I5H4KYEZ.js} +3 -3
  47. package/dist/{chunk-5U45TQJE.js.map → chunk-I5H4KYEZ.js.map} +1 -1
  48. package/dist/{chunk-S63SYV6O.js → chunk-INXQJM2K.js} +27 -6
  49. package/dist/chunk-INXQJM2K.js.map +1 -0
  50. package/dist/{chunk-HQ3X5SVR.js → chunk-KQLKITZL.js} +105 -16
  51. package/dist/chunk-KQLKITZL.js.map +1 -0
  52. package/dist/chunk-M4SYVPC4.js +3 -0
  53. package/dist/chunk-M4SYVPC4.js.map +1 -0
  54. package/dist/chunk-NOAKHVBJ.js +264 -0
  55. package/dist/chunk-NOAKHVBJ.js.map +1 -0
  56. package/dist/chunk-OOQO6VGO.js +33 -0
  57. package/dist/chunk-OOQO6VGO.js.map +1 -0
  58. package/dist/chunk-P3QUNCBR.js +366 -0
  59. package/dist/chunk-P3QUNCBR.js.map +1 -0
  60. package/dist/chunk-PABMR4RA.js +30 -0
  61. package/dist/chunk-PABMR4RA.js.map +1 -0
  62. package/dist/{chunk-SEZZVAGP.js → chunk-SR4IN564.js} +10 -3
  63. package/dist/chunk-SR4IN564.js.map +1 -0
  64. package/dist/index.d.ts +250 -35
  65. package/dist/index.js +40 -37
  66. package/dist/markdown-diff-view.js +1 -1
  67. package/dist/model-registry.js +1 -1
  68. package/dist/styles.css +1 -1
  69. package/dist/theme-scope.d.ts +1 -1
  70. package/dist/tokens.d.ts +1 -1
  71. package/dist/tokens.js +1 -1
  72. package/dist/{types-LhE1KFBf.d.ts → types-CxieoN2u.d.ts} +1 -1
  73. package/dist/types-pivJdPC8.d.ts +14 -0
  74. package/package.json +17 -5
  75. package/dist/chunk-5BJ4VULP.js.map +0 -1
  76. package/dist/chunk-C5JZCCU7.js +0 -220
  77. package/dist/chunk-C5JZCCU7.js.map +0 -1
  78. package/dist/chunk-E3NZM4U7.js.map +0 -1
  79. package/dist/chunk-GDGKARRB.js.map +0 -1
  80. package/dist/chunk-HQ3X5SVR.js.map +0 -1
  81. package/dist/chunk-NUD32RLL.js +0 -199
  82. package/dist/chunk-NUD32RLL.js.map +0 -1
  83. package/dist/chunk-PC3CE4W2.js.map +0 -1
  84. package/dist/chunk-S63SYV6O.js.map +0 -1
  85. package/dist/chunk-SEZZVAGP.js.map +0 -1
  86. package/dist/chunk-TQDTFPM4.js.map +0 -1
  87. package/dist/chunk-XOODPYJ3.js +0 -173
  88. package/dist/chunk-XOODPYJ3.js.map +0 -1
  89. package/dist/tree-4Hv9stjF.d.ts +0 -54
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/chat-session-list/ChatSessionList.tsx"],"names":["ChatSessionList"],"mappings":"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","file":"chunk-PC3CE4W2.js","sourcesContent":["import * as React from 'react'\nimport { Download, MessageSquarePlus, Pencil, Search, Trash2 } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\nimport { formatDistanceToNow, isToday, isYesterday, parseISO } from 'date-fns'\n\nimport { cn } from '../../lib/utils'\nimport { searchScore } from '../../lib/fuzzy'\n\nexport interface ChatSessionView {\n id: string\n title: string\n updatedAt?: string | number | Date\n meta?: Record<string, unknown>\n}\n\nexport interface ChatSessionListLabels {\n search: string\n searchPlaceholder: string\n newChat: string\n rename: string\n delete: string\n export: string\n today: string\n yesterday: string\n earlier: string\n empty: string\n noResults: string\n}\n\nexport interface ChatSessionListIcons {\n newChat: LucideIcon\n rename: LucideIcon\n delete: LucideIcon\n export: LucideIcon\n}\n\nexport interface ChatSessionListProps {\n sessions: ChatSessionView[]\n activeId?: string | null\n onSelect: (id: string) => void\n onNew?: () => void\n onRename?: (id: string) => void\n onDelete?: (id: string) => void\n onExport?: (id: string) => void\n searchable?: boolean\n groupByDate?: boolean\n labels?: Partial<ChatSessionListLabels>\n icons?: Partial<ChatSessionListIcons>\n className?: string\n}\n\nfunction toDate(value: string | number | Date | undefined): Date | undefined {\n if (value === undefined) {\n return undefined\n }\n if (value instanceof Date) {\n return value\n }\n if (typeof value === 'number') {\n return new Date(value)\n }\n return parseISO(value)\n}\n\nfunction bucketOf(date: Date | undefined): 'today' | 'yesterday' | 'earlier' | 'none' {\n if (date === undefined) {\n return 'none'\n }\n if (isToday(date)) {\n return 'today'\n }\n if (isYesterday(date)) {\n return 'yesterday'\n }\n return 'earlier'\n}\n\n/**\n * The session list beside a full-page chat (study's `ChatSessionList` +\n * health's history overlay, generalized): fuzzy search, date grouping,\n * active marking, per-session actions (rename/delete/export) via the\n * menu when handlers are provided. All actions stay app-side.\n */\nexport const ChatSessionList = React.forwardRef<HTMLDivElement, ChatSessionListProps>(\n function ChatSessionList(\n {\n sessions,\n activeId,\n onSelect,\n onNew,\n onRename,\n onDelete,\n onExport,\n searchable = true,\n groupByDate = true,\n labels,\n icons,\n className,\n },\n ref,\n ) {\n const [query, setQuery] = React.useState('')\n const NewIcon = icons?.newChat ?? MessageSquarePlus\n const RenameIcon = icons?.rename ?? Pencil\n const DeleteIcon = icons?.delete ?? Trash2\n const ExportIcon = icons?.export ?? Download\n\n const filtered = React.useMemo(() => {\n const trimmed = query.trim()\n if (trimmed === '') {\n return sessions\n }\n return sessions\n .map((session) => ({\n session,\n score: searchScore(trimmed, session.title),\n }))\n .filter((entry) => entry.score !== null)\n .sort((a, b) => (b.score ?? 0) - (a.score ?? 0))\n .map((entry) => entry.session)\n }, [sessions, query])\n\n const groups = React.useMemo(() => {\n if (!groupByDate) {\n return [{ key: 'all', label: null as string | null, sessions: filtered }]\n }\n const ordered = [...filtered].sort((a, b) => {\n const dateA = toDate(a.updatedAt)?.getTime() ?? 0\n const dateB = toDate(b.updatedAt)?.getTime() ?? 0\n return dateB - dateA\n })\n const byBucket = new Map<string, ChatSessionView[]>()\n for (const session of ordered) {\n const bucket = bucketOf(toDate(session.updatedAt))\n const list = byBucket.get(bucket) ?? []\n list.push(session)\n byBucket.set(bucket, list)\n }\n const order = ['today', 'yesterday', 'earlier', 'none']\n return order\n .filter((bucket) => byBucket.has(bucket))\n .map((bucket) => ({\n key: bucket,\n label:\n bucket === 'today'\n ? (labels?.today ?? 'Today')\n : bucket === 'yesterday'\n ? (labels?.yesterday ?? 'Yesterday')\n : bucket === 'earlier'\n ? (labels?.earlier ?? 'Earlier')\n : null,\n sessions: byBucket.get(bucket) ?? [],\n }))\n }, [filtered, groupByDate, labels])\n\n return (\n <div ref={ref} data-as=\"chat-session-list\" className={cn('flex h-full min-h-0 w-full flex-col gap-2', className)}>\n <div className=\"flex items-center gap-2\">\n {searchable ? (\n <div className=\"relative min-w-0 flex-1\">\n <Search className=\"pointer-events-none absolute start-2 top-1/2 size-3.5 -translate-y-1/2 text-[var(--as-muted-fg)]\" aria-hidden />\n <input\n type=\"search\"\n role=\"searchbox\"\n value={query}\n aria-label={labels?.search ?? 'Search conversations'}\n placeholder={labels?.searchPlaceholder ?? 'Search…'}\n onChange={(event) => setQuery(event.target.value)}\n className=\"w-full rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] py-1.5 pe-2 ps-8 text-xs text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n />\n </div>\n ) : null}\n {onNew ? (\n <button\n type=\"button\"\n onClick={onNew}\n aria-label={labels?.newChat ?? 'New conversation'}\n title={labels?.newChat ?? 'New conversation'}\n className=\"flex size-7 shrink-0 items-center justify-center rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-fg)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <NewIcon className=\"size-4\" aria-hidden />\n </button>\n ) : null}\n </div>\n {sessions.length === 0 ? (\n <p className=\"px-2 py-3 text-xs text-[var(--as-muted-fg)]\">{labels?.empty ?? 'No conversations yet'}</p>\n ) : filtered.length === 0 ? (\n <p className=\"px-2 py-3 text-xs text-[var(--as-muted-fg)]\">{labels?.noResults ?? 'No matches'}</p>\n ) : (\n <div role=\"list\" aria-label={labels?.search ?? 'Conversations'} className=\"min-h-0 flex-1 overflow-y-auto\">\n {groups.map((group) => (\n <div key={group.key} className=\"mb-2\">\n {group.label ? (\n <p className=\"px-2 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-[var(--as-muted-fg)]\">\n {group.label}\n </p>\n ) : null}\n {group.sessions.map((session) => {\n const isActive = session.id === activeId\n const date = toDate(session.updatedAt)\n const actions =\n onRename || onDelete || onExport\n ? [\n onRename\n ? {\n key: 'rename',\n label: labels?.rename ?? 'Rename',\n icon: <RenameIcon className=\"size-3.5\" aria-hidden />,\n onSelect: () => onRename(session.id),\n }\n : null,\n onExport\n ? {\n key: 'export',\n label: labels?.export ?? 'Export',\n icon: <ExportIcon className=\"size-3.5\" aria-hidden />,\n onSelect: () => onExport(session.id),\n }\n : null,\n onDelete\n ? {\n key: 'delete',\n label: labels?.delete ?? 'Delete',\n icon: <DeleteIcon className=\"size-3.5\" aria-hidden />,\n onSelect: () => onDelete(session.id),\n danger: true,\n }\n : null,\n ].filter((item): item is NonNullable<typeof item> => item !== null)\n : []\n return (\n <div\n key={session.id}\n role=\"listitem\"\n aria-current={isActive ? 'true' : undefined}\n data-active={isActive || undefined}\n className={cn(\n 'group/session flex cursor-pointer items-center gap-2 rounded-[var(--as-radius)] px-2 py-1.5 text-xs transition-colors hover:bg-[var(--as-surface-raised)]',\n isActive && 'bg-[var(--as-surface-raised)] font-medium text-[var(--as-fg)]',\n )}\n onClick={() => onSelect(session.id)}\n onKeyDown={(event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n onSelect(session.id)\n }\n }}\n tabIndex={0}\n >\n <span className=\"line-clamp-2 min-w-0 flex-1 text-[var(--as-fg)]\">{session.title}</span>\n {date ? (\n <span className=\"shrink-0 text-[10px] text-[var(--as-muted-fg)]\">\n {formatDistanceToNow(date, { addSuffix: true })}\n </span>\n ) : null}\n {actions.length > 0 ? (\n <span\n role=\"group\"\n aria-label={labels?.rename ?? 'Conversation actions'}\n className=\"flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity focus-within:opacity-100 group-hover/session:opacity-100\"\n onClick={(event) => event.stopPropagation()}\n onKeyDown={(event) => event.stopPropagation()}\n >\n {actions.map((action) => (\n <button\n key={action.key}\n type=\"button\"\n aria-label={action.label}\n title={action.label}\n onClick={action.onSelect}\n className={cn(\n 'rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-surface)] hover:text-[var(--as-fg)] focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]',\n action.danger && 'hover:text-[var(--as-danger)]',\n )}\n >\n {action.icon}\n </button>\n ))}\n </span>\n ) : null}\n </div>\n )\n })}\n </div>\n ))\n }\n </div>\n )}\n </div>\n )\n },\n)\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/chat-reasoning/ChatReasoning.tsx"],"names":["ChatReasoning"],"mappings":";;;;;;AA8BO,IAAM,aAAA,GAAsB,KAAA,CAAA,UAAA;AAAA,EACjC,SAASA,cAAAA,CACP,EAAE,IAAA,EAAM,SAAA,GAAY,OAAO,IAAA,EAAM,QAAA,EAAU,WAAA,GAAc,IAAA,EAAM,cAAc,MAAA,EAAQ,IAAA,EAAM,OAAO,KAAA,EAAO,SAAA,IACzG,GAAA,EACA;AACA,IAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAU,eAAS,WAAW,CAAA;AAC1E,IAAA,MAAM,eAAe,QAAA,KAAa,MAAA;AAClC,IAAA,MAAM,IAAA,GAAO,eAAe,QAAA,GAAW,gBAAA;AACvC,IAAA,MAAM,WAAiB,KAAA,CAAA,KAAA,EAAM;AAE7B,IAAA,MAAM,SAAS,MAAM;AACnB,MAAA,MAAM,OAAO,CAAC,IAAA;AACd,MAAA,IAAI,CAAC,YAAA,EAAc;AACjB,QAAA,mBAAA,CAAoB,IAAI,CAAA;AAAA,MAC1B;AACA,MAAA,YAAA,GAAe,IAAI,CAAA;AAAA,IACrB,CAAA;AAEA,IAAA,MAAM,SAAA,GAA2D,CAAC,KAAA,KAAU;AAC1E,MAAA,IAAI,KAAA,CAAM,GAAA,KAAQ,OAAA,IAAW,KAAA,CAAM,QAAQ,GAAA,EAAK;AAC9C,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,MAAA,EAAO;AAAA,MACT;AAAA,IACF,CAAA;AAEA,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAQ,gBAAA;AAAA,QACR,WAAA,EAAW,IAAA;AAAA,QACX,SAAA,EAAW,EAAA;AAAA,UACT,sHAAA;AAAA,UACA;AAAA,SACF;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAA,IAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,eAAA,EAAe,IAAA;AAAA,cACf,eAAA,EAAe,QAAA;AAAA,cACf,OAAA,EAAS,MAAA;AAAA,cACT,SAAA;AAAA,cACA,SAAA,EAAU,8OAAA;AAAA,cAEV,QAAA,EAAA;AAAA,gCAAA,GAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,mBAAA,EAAoB,aAAA,EAAW,IAAA,EAAC,CAAA;AAAA,oCAC/C,MAAA,EAAA,EAAK,SAAA,EAAU,QAAA,EAAU,QAAA,EAAA,MAAA,EAAQ,SAAS,UAAA,EAAW,CAAA;AAAA,gBACrD,4BACC,IAAA,CAAC,MAAA,EAAA,EAAK,IAAA,EAAK,QAAA,EAAS,WAAU,uCAAA,EAC5B,QAAA,EAAA;AAAA,kCAAA,GAAA,CAAC,OAAA,EAAA,EAAQ,IAAA,EAAK,IAAA,EAAK,SAAA,EAAU,2BAAA,EAA4B,CAAA;AAAA,sCACxD,MAAA,EAAA,EAAK,SAAA,EAAU,SAAA,EAAW,QAAA,EAAA,MAAA,EAAQ,aAAa,gBAAA,EAAY;AAAA,iBAAA,EAC9D,CAAA,GACE,IAAA;AAAA,gCACJ,GAAA;AAAA,kBAAC,WAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAW,EAAA,CAAG,wCAAA,EAA0C,IAAA,IAAQ,YAAY,CAAA;AAAA,oBAC5E,aAAA,EAAW;AAAA;AAAA;AACb;AAAA;AAAA,WACF;AAAA,UACC,IAAA,mBACC,GAAA,CAAC,KAAA,EAAA,EAAI,EAAA,EAAI,QAAA,EAAU,SAAA,EAAU,gDAAA,EAC3B,QAAA,kBAAA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,iDAAA,EAAmD,QAAA,EAAA,IAAA,EAAK,GACvE,CAAA,GACE;AAAA;AAAA;AAAA,KACN;AAAA,EAEJ;AACF","file":"chunk-S63SYV6O.js","sourcesContent":["import * as React from 'react'\nimport { Brain, ChevronDown } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\nimport { Spinner } from '../spinner/Spinner'\n\nexport interface ChatReasoningLabels {\n title: string\n streaming: string\n}\n\nexport interface ChatReasoningProps {\n /** Accumulated reasoning text (family `delta kind=\"reasoning\"`). */\n text: string\n /** Live reasoning stream — spinner + streaming label while on. */\n streaming?: boolean\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n labels?: Partial<ChatReasoningLabels>\n icon?: LucideIcon\n className?: string\n}\n\n/**\n * Collapsible thinking block for reasoning models (study's\n * `ReasoningBubble`, family-standard): streams live while the model\n * reasons, collapses to a summary row when done. Controlled-first.\n */\nexport const ChatReasoning = React.forwardRef<HTMLDivElement, ChatReasoningProps>(\n function ChatReasoning(\n { text, streaming = false, open: openProp, defaultOpen = true, onOpenChange, labels, icon: Icon = Brain, className },\n ref,\n ) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = openProp !== undefined\n const open = isControlled ? openProp : uncontrolledOpen\n const regionId = React.useId()\n\n const toggle = () => {\n const next = !open\n if (!isControlled) {\n setUncontrolledOpen(next)\n }\n onOpenChange?.(next)\n }\n\n const onKeyDown: React.KeyboardEventHandler<HTMLButtonElement> = (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n toggle()\n }\n }\n\n return (\n <div\n ref={ref}\n data-as=\"chat-reasoning\"\n data-open={open}\n className={cn(\n 'rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] text-xs text-[var(--as-muted-fg)]',\n className,\n )}\n >\n <button\n type=\"button\"\n aria-expanded={open}\n aria-controls={regionId}\n onClick={toggle}\n onKeyDown={onKeyDown}\n className=\"flex w-full items-center gap-2 px-2.5 py-1.5 text-left font-medium text-[var(--as-muted-fg)] transition-colors hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <Icon className=\"size-3.5 shrink-0\" aria-hidden />\n <span className=\"flex-1\">{labels?.title ?? 'Thinking'}</span>\n {streaming ? (\n <span role=\"status\" className=\"flex items-center gap-1.5 font-normal\">\n <Spinner size=\"sm\" className=\"text-[var(--as-muted-fg)]\" />\n <span className=\"sr-only\">{labels?.streaming ?? 'Thinking…'}</span>\n </span>\n ) : null}\n <ChevronDown\n className={cn('size-3.5 shrink-0 transition-transform', open && 'rotate-180')}\n aria-hidden\n />\n </button>\n {open ? (\n <div id={regionId} className=\"border-t border-[var(--as-border)] px-2.5 py-2\">\n <p className=\"whitespace-pre-wrap break-words leading-relaxed\">{text}</p>\n </div>\n ) : null}\n </div>\n )\n },\n)\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/chat-panel/ChatPanel.tsx"],"names":["ChatPanel"],"mappings":";;;;AA6BO,IAAM,SAAA,GAAkB,KAAA,CAAA,UAAA;AAAA,EAC7B,SAASA,UAAAA,CAAU,EAAE,OAAA,EAAS,KAAA,EAAO,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,EAAU,MAAA,EAAQ,SAAA,EAAU,EAAG,GAAA,EAAK;AACpG,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAQ,YAAA;AAAA,QACR,cAAA,EAAc,OAAA;AAAA,QACd,SAAA,EAAW,EAAA;AAAA,UACT,gFAAA;AAAA,UACA,YAAY,QAAA,IAAY,iBAAA;AAAA,UACxB;AAAA,SACF;AAAA,QAEC,QAAA,EAAA;AAAA,UAAA,KAAA,KAAU,MAAA,IAAa,YAAY,MAAA,mBAClC,IAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAQ,mBAAA;AAAA,cACR,SAAA,EAAU,+EAAA;AAAA,cAEV,QAAA,EAAA;AAAA,gCAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,+CAAA,EAAiD,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,gBACrE,0BAAU,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oCAAA,EAAsC,mBAAQ,CAAA,GAAS;AAAA;AAAA;AAAA,WACnF,GACE,IAAA;AAAA,UACH,MAAA,uBACE,KAAA,EAAA,EAAI,SAAA,EAAQ,qBAAoB,SAAA,EAAU,wDAAA,EACxC,kBACH,CAAA,GACE,IAAA;AAAA,0BACJ,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,8BAAA,EACb,QAAA,kBAAA,GAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAW,EAAA;AAAA,gBACT,qCAAA;AAAA,gBACA,YAAY,MAAA,IAAU;AAAA,eACxB;AAAA,cAEC,QAAA,EAAA;AAAA;AAAA,WACH,EACF,CAAA;AAAA,UACC,QAAA,mBACC,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mDACb,QAAA,kBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,EAAA,CAAG,YAAY,MAAA,IAAU,0BAA0B,CAAA,EAAI,QAAA,EAAA,QAAA,EAAS,GAClF,CAAA,GACE,IAAA;AAAA,UACH,yBACC,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2EAAA,EAA6E,kBAAO,CAAA,GACjG;AAAA;AAAA;AAAA,KACN;AAAA,EAEJ;AACF","file":"chunk-SEZZVAGP.js","sourcesContent":["import * as React from 'react'\n\nimport { cn } from '../../lib/utils'\n\nexport type ChatPanelVariant = 'page' | 'sidebar' | 'bubble'\n\nexport interface ChatPanelProps {\n /** Layout tier: full page (full-bleed shell, centered conversation column), sidebar (dense column), bubble (compact widget body). */\n variant: ChatPanelVariant\n /** Header row: title node + action nodes (session list, branch tree, expand, close). */\n title?: React.ReactNode\n actions?: React.ReactNode\n /** Status strip under the header (connection warnings, context summary). */\n banner?: React.ReactNode\n /** Usually `<ChatTranscript …/>`. */\n transcript: React.ReactNode\n /** Usually `<ChatComposer …/>`. */\n composer?: React.ReactNode\n /** Disclaimer row under the composer (health guidance, etc.). */\n footer?: React.ReactNode\n className?: string\n}\n\n/**\n * The composed chat host — the one uniform surface assembly behind the\n * family's three shapes (full page / sidepanel / bubble). Owns layout\n * only; transcript, composer and every feature slot stay app-composed\n * (ADR-006 tier 3, `RichTextEditor` precedent).\n */\nexport const ChatPanel = React.forwardRef<HTMLDivElement, ChatPanelProps>(\n function ChatPanel({ variant, title, actions, banner, transcript, composer, footer, className }, ref) {\n return (\n <div\n ref={ref}\n data-as=\"chat-panel\"\n data-variant={variant}\n className={cn(\n 'flex h-full min-h-0 w-full flex-col bg-[var(--as-surface)] text-[var(--as-fg)]',\n variant === 'bubble' && 'overflow-hidden',\n className,\n )}\n >\n {title !== undefined || actions !== undefined ? (\n <div\n data-as=\"chat-panel-header\"\n className=\"flex shrink-0 items-center gap-2 border-b border-[var(--as-border)] px-3 py-2\"\n >\n <div className=\"min-w-0 flex-1 truncate text-sm font-semibold\">{title}</div>\n {actions ? <div className=\"flex shrink-0 items-center gap-0.5\">{actions}</div> : null}\n </div>\n ) : null}\n {banner ? (\n <div data-as=\"chat-panel-banner\" className=\"shrink-0 px-3 py-1.5 text-xs text-[var(--as-muted-fg)]\">\n {banner}\n </div>\n ) : null}\n <div className=\"flex min-h-0 flex-1 flex-col\">\n <div\n className={cn(\n 'flex min-h-0 w-full flex-1 flex-col',\n variant === 'page' && 'mx-auto max-w-3xl',\n )}\n >\n {transcript}\n </div>\n </div>\n {composer ? (\n <div className=\"shrink-0 border-t border-[var(--as-border)] p-2\">\n <div className={cn(variant === 'page' && 'mx-auto w-full max-w-3xl')}>{composer}</div>\n </div>\n ) : null}\n {footer ? (\n <div className=\"shrink-0 px-3 pb-2 pt-1 text-center text-[10px] text-[var(--as-muted-fg)]\">{footer}</div>\n ) : null}\n </div>\n )\n },\n)\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/chat-turn-status/ChatTurnStatus.tsx"],"names":["ChatTurnStatus"],"mappings":";;;;AAqBA,SAAS,cAAc,EAAA,EAAoB;AACzC,EAAA,IAAI,KAAK,GAAA,EAAM;AACb,IAAA,OAAO,CAAA,EAAG,KAAK,GAAA,CAAI,CAAA,EAAG,KAAK,KAAA,CAAM,EAAE,CAAC,CAAC,CAAA,GAAA,CAAA;AAAA,EACvC;AACA,EAAA,OAAO,CAAA,EAAA,CAAI,KAAK,GAAA,EAAM,OAAA,CAAQ,KAAK,GAAA,GAAQ,CAAA,GAAI,CAAC,CAAC,CAAA,EAAA,CAAA;AACnD;AAQO,IAAM,cAAA,GAAuB,KAAA,CAAA,UAAA;AAAA,EAClC,SAASA,eAAAA,CACP,EAAE,KAAA,EAAO,SAAA,EAAW,OAAA,GAAU,KAAA,EAAO,MAAA,GAAS,GAAA,EAAK,MAAA,EAAQ,SAAA,EAAU,EACrE,GAAA,EACA;AACA,IAAA,MAAM,CAAC,KAAK,MAAM,CAAA,GAAU,eAAS,MAAM,IAAA,CAAK,KAAK,CAAA;AAErD,IAAM,gBAAU,MAAM;AACpB,MAAA,IAAI,cAAc,MAAA,EAAW;AAC3B,QAAA;AAAA,MACF;AACA,MAAA,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA;AACjB,MAAA,MAAM,EAAA,GAAK,OAAO,WAAA,CAAY,MAAM,OAAO,IAAA,CAAK,GAAA,EAAK,CAAA,EAAG,MAAM,CAAA;AAC9D,MAAA,OAAO,MAAM,MAAA,CAAO,aAAA,CAAc,EAAE,CAAA;AAAA,IACtC,CAAA,EAAG,CAAC,SAAA,EAAW,MAAM,CAAC,CAAA;AAEtB,IAAA,MAAM,OAAA,GAAU,cAAc,MAAA,GAAY,IAAA,GAAO,KAAK,GAAA,CAAI,CAAA,EAAG,MAAM,SAAS,CAAA;AAE5E,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,IAAA,EAAK,QAAA;AAAA,QACL,YAAA,EAAY,KAAA;AAAA,QACZ,SAAA,EAAQ,kBAAA;AAAA,QACR,cAAA,EAAc,OAAA;AAAA,QACd,SAAA,EAAW,EAAA;AAAA,UACT,mDAAA;AAAA,UACA,YAAY,MAAA,IACV,uGAAA;AAAA,UACF,YAAY,KAAA,IAAS,aAAA;AAAA,UACrB;AAAA,SACF;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,MAAA,EAAA,EAAK,aAAA,EAAW,IAAA,EAAC,SAAA,EAAU,yBAAA,EACzB,QAAA,EAAA,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,KAAA,qBACd,GAAA;AAAA,YAAC,MAAA;AAAA,YAAA;AAAA,cAEC,SAAA,EAAU,sDAAA;AAAA,cACV,OAAO,EAAE,cAAA,EAAgB,CAAA,EAAG,KAAA,GAAQ,IAAI,CAAA,CAAA,CAAA;AAAI,aAAA;AAAA,YAFvC;AAAA,WAIR,CAAA,EACH,CAAA;AAAA,0BACA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,SAAA,EAAW,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,UAChC,OAAA,KAAY,IAAA,mBACX,GAAA,CAAC,MAAA,EAAA,EAAK,aAAA,EAAW,IAAA,EAAC,SAAA,EAAU,oCAAA,EACzB,QAAA,EAAA,aAAA,CAAc,OAAO,CAAA,EACxB,CAAA,GACE,IAAA;AAAA,UACH,MAAA,EAAQ,wBAAQ,GAAA,CAAC,MAAA,EAAA,EAAK,WAAU,SAAA,EAAW,QAAA,EAAA,MAAA,CAAO,OAAM,CAAA,GAAU;AAAA;AAAA;AAAA,KACrE;AAAA,EAEJ;AACF","file":"chunk-TQDTFPM4.js","sourcesContent":["import * as React from 'react'\n\nimport { cn } from '../../lib/utils'\n\nexport interface ChatTurnStatusLabels {\n timer: string\n}\n\nexport interface ChatTurnStatusProps {\n /** Current turn phase, e.g. \"searching the catalog\". */\n label: string\n /** Turn start timestamp (ms). Presence enables the elapsed timer. */\n startedAt?: number\n /** `row` = inline pre-text line; `card` = soft surface block. */\n variant?: 'row' | 'card'\n /** Elapsed-timer refresh interval. Default 100 ms (study-proven). */\n tickMs?: number\n labels?: Partial<ChatTurnStatusLabels>\n className?: string\n}\n\nfunction formatElapsed(ms: number): string {\n if (ms < 1000) {\n return `${Math.max(0, Math.round(ms))} ms`\n }\n return `${(ms / 1000).toFixed(ms < 10000 ? 1 : 0)} s`\n}\n\n/**\n * Live pre-text turn status (study's `TurnTraceStatus` + `ThinkingDots`,\n * generalized for family plan 12 L1): animated dots + phase label, with\n * an optional elapsed timer. Presentational and controlled — the turn\n * state itself lives in `useChatStream`; this only narrates it.\n */\nexport const ChatTurnStatus = React.forwardRef<HTMLDivElement, ChatTurnStatusProps>(\n function ChatTurnStatus(\n { label, startedAt, variant = 'row', tickMs = 100, labels, className },\n ref,\n ) {\n const [now, setNow] = React.useState(() => Date.now())\n\n React.useEffect(() => {\n if (startedAt === undefined) {\n return\n }\n setNow(Date.now())\n const id = window.setInterval(() => setNow(Date.now()), tickMs)\n return () => window.clearInterval(id)\n }, [startedAt, tickMs])\n\n const elapsed = startedAt === undefined ? null : Math.max(0, now - startedAt)\n\n return (\n <div\n ref={ref}\n role=\"status\"\n aria-label={label}\n data-as=\"chat-turn-status\"\n data-variant={variant}\n className={cn(\n 'flex items-center gap-2 text-[var(--as-muted-fg)]',\n variant === 'card' &&\n 'rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] px-3 py-2.5',\n variant === 'row' && 'px-1 py-0.5',\n className,\n )}\n >\n <span aria-hidden className=\"flex items-center gap-1\">\n {[0, 1, 2].map((index) => (\n <span\n key={index}\n className=\"as-dot size-1.5 rounded-full bg-[var(--as-muted-fg)]\"\n style={{ animationDelay: `${index * 0.16}s` }}\n />\n ))}\n </span>\n <span className=\"text-xs\">{label}</span>\n {elapsed !== null ? (\n <span aria-hidden className=\"font-mono text-[10px] tabular-nums\">\n {formatElapsed(elapsed)}\n </span>\n ) : null}\n {labels?.timer ? <span className=\"sr-only\">{labels.timer}</span> : null}\n </div>\n )\n },\n)\n"]}
@@ -1,173 +0,0 @@
1
- import { MarkdownSurface } from './chunk-HQ3X5SVR.js';
2
- import { ChatMessage } from './chunk-C5JZCCU7.js';
3
- import { cn } from './chunk-RVVXVTH3.js';
4
- import * as React from 'react';
5
- import { ArrowDown } from 'lucide-react';
6
- import { useVirtualizer } from '@tanstack/react-virtual';
7
- import { jsxs, jsx } from 'react/jsx-runtime';
8
-
9
- function defaultRenderItem(message) {
10
- return /* @__PURE__ */ jsx(
11
- ChatMessage,
12
- {
13
- role: message.role,
14
- status: message.status,
15
- content: /* @__PURE__ */ jsx(MarkdownSurface, { value: message.content, streaming: message.status === "streaming" }),
16
- variants: message.variants
17
- }
18
- );
19
- }
20
- var ChatTranscript = React.forwardRef(
21
- function ChatTranscript2({
22
- items,
23
- renderItem = defaultRenderItem,
24
- live,
25
- emptyState,
26
- autoScroll = true,
27
- scrollThresholdPx = 120,
28
- virtualized = false,
29
- labels,
30
- className
31
- }, ref) {
32
- const scrollRef = React.useRef(null);
33
- const atBottomRef = React.useRef(true);
34
- const lastScrollTopRef = React.useRef(0);
35
- const lastTouchYRef = React.useRef(null);
36
- const [showJump, setShowJump] = React.useState(false);
37
- const [announcement, setAnnouncement] = React.useState("");
38
- const previousCountRef = React.useRef(-1);
39
- const onScroll = () => {
40
- const element = scrollRef.current;
41
- if (element === null) {
42
- return;
43
- }
44
- const distance = element.scrollHeight - element.scrollTop - element.clientHeight;
45
- const goingUp = element.scrollTop < lastScrollTopRef.current;
46
- lastScrollTopRef.current = element.scrollTop;
47
- const atBottom = distance <= scrollThresholdPx && !(goingUp && distance > 0);
48
- atBottomRef.current = atBottom;
49
- setShowJump(element.scrollHeight > element.clientHeight && !atBottom);
50
- };
51
- const cancelStick = () => {
52
- const element = scrollRef.current;
53
- if (element === null) {
54
- return;
55
- }
56
- atBottomRef.current = false;
57
- setShowJump(element.scrollHeight > element.clientHeight);
58
- };
59
- const onWheel = (event) => {
60
- if (event.deltaY < 0) {
61
- cancelStick();
62
- }
63
- };
64
- const onTouchStart = (event) => {
65
- lastTouchYRef.current = event.touches[0]?.clientY ?? null;
66
- };
67
- const onTouchMove = (event) => {
68
- const y = event.touches[0]?.clientY;
69
- if (y === void 0) {
70
- return;
71
- }
72
- if (lastTouchYRef.current !== null && y > lastTouchYRef.current) {
73
- cancelStick();
74
- }
75
- lastTouchYRef.current = y;
76
- };
77
- const onKeyDown = (event) => {
78
- if (event.key === "ArrowUp" || event.key === "PageUp" || event.key === "Home" || event.key === " " && event.shiftKey) {
79
- cancelStick();
80
- }
81
- };
82
- React.useEffect(() => {
83
- const element = scrollRef.current;
84
- if (element === null || !autoScroll || !atBottomRef.current) {
85
- return;
86
- }
87
- const frame = requestAnimationFrame(() => {
88
- element.scrollTop = element.scrollHeight;
89
- });
90
- return () => cancelAnimationFrame(frame);
91
- });
92
- React.useEffect(() => {
93
- if (previousCountRef.current === -1) {
94
- previousCountRef.current = items.length;
95
- return;
96
- }
97
- if (items.length > previousCountRef.current) {
98
- const added = items[items.length - 1];
99
- previousCountRef.current = items.length;
100
- if (added && added.role !== "user") {
101
- setAnnouncement(labels?.replied ?? "Assistant replied");
102
- }
103
- return;
104
- }
105
- previousCountRef.current = items.length;
106
- }, [items, labels]);
107
- const scrollToBottom = () => {
108
- const element = scrollRef.current;
109
- if (element !== null) {
110
- element.scrollTo({ top: element.scrollHeight, behavior: "smooth" });
111
- }
112
- };
113
- const rowCount = items.length + (live !== void 0 ? 1 : 0);
114
- const virtualizer = useVirtualizer({
115
- count: virtualized ? rowCount : 0,
116
- getScrollElement: () => scrollRef.current,
117
- estimateSize: () => 120,
118
- overscan: 8,
119
- initialRect: { width: 480, height: 600 }
120
- });
121
- return /* @__PURE__ */ jsxs("div", { ref, "data-as": "chat-transcript", className: cn("relative min-h-0 flex-1", className), children: [
122
- /* @__PURE__ */ jsx(
123
- "div",
124
- {
125
- ref: scrollRef,
126
- onScroll,
127
- onWheel,
128
- onTouchStart,
129
- onTouchMove,
130
- onKeyDown,
131
- role: "log",
132
- "aria-label": labels?.log ?? "Conversation",
133
- "aria-busy": live !== void 0,
134
- className: "h-full overflow-y-auto overscroll-contain px-1 py-2",
135
- children: items.length === 0 && live === void 0 ? /* @__PURE__ */ jsx("div", { className: "flex h-full min-h-40 items-center justify-center p-4 text-sm text-[var(--as-muted-fg)]", children: emptyState ?? (labels?.empty ?? "Start the conversation") }) : virtualized ? /* @__PURE__ */ jsx("div", { style: { height: virtualizer.getTotalSize(), position: "relative" }, children: virtualizer.getVirtualItems().map((row) => {
136
- const isLive = row.index >= items.length;
137
- const node = isLive ? live : renderItem(items[row.index], row.index);
138
- return /* @__PURE__ */ jsx(
139
- "div",
140
- {
141
- "data-index": row.index,
142
- ref: virtualizer.measureElement,
143
- style: { position: "absolute", top: 0, left: 0, width: "100%", transform: `translateY(${row.start}px)` },
144
- className: "px-2 py-1.5",
145
- children: node
146
- },
147
- row.key
148
- );
149
- }) }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 px-2", children: [
150
- items.map((message, index) => /* @__PURE__ */ jsx("div", { children: renderItem(message, index) }, message.id)),
151
- live !== void 0 ? /* @__PURE__ */ jsx("div", { "data-as": "chat-transcript-live", children: live }) : null
152
- ] })
153
- }
154
- ),
155
- /* @__PURE__ */ jsx("div", { "aria-live": "polite", className: "sr-only", children: announcement }),
156
- showJump ? /* @__PURE__ */ jsx(
157
- "button",
158
- {
159
- type: "button",
160
- onClick: scrollToBottom,
161
- "aria-label": labels?.scrollToBottom ?? "Scroll to latest",
162
- title: labels?.scrollToBottom ?? "Scroll to latest",
163
- className: "absolute bottom-3 left-1/2 flex size-8 -translate-x-1/2 items-center justify-center rounded-full border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)] shadow-[var(--as-shadow-2)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
164
- children: /* @__PURE__ */ jsx(ArrowDown, { className: "size-4", "aria-hidden": true })
165
- }
166
- ) : null
167
- ] });
168
- }
169
- );
170
-
171
- export { ChatTranscript };
172
- //# sourceMappingURL=chunk-XOODPYJ3.js.map
173
- //# sourceMappingURL=chunk-XOODPYJ3.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/chat-transcript/ChatTranscript.tsx"],"names":["ChatTranscript"],"mappings":"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","file":"chunk-XOODPYJ3.js","sourcesContent":["import * as React from 'react'\nimport { ArrowDown } from 'lucide-react'\nimport { useVirtualizer } from '@tanstack/react-virtual'\n\nimport { cn } from '../../lib/utils'\nimport { ChatMessage } from '../chat-message/ChatMessage'\nimport { MarkdownSurface } from '../chat-markdown/MarkdownSurface'\nimport type { ChatMessageView } from '../chat-core/types'\n\nexport interface ChatTranscriptLabels {\n log: string\n scrollToBottom: string\n empty: string\n replied: string\n}\n\nexport interface ChatTranscriptProps {\n items: ChatMessageView[]\n /** Override the default `ChatMessage` + `MarkdownSurface` rendering. */\n renderItem?: (message: ChatMessageView, index: number) => React.ReactNode\n /** Streaming tail — the live turn bubble (or any node). */\n live?: React.ReactNode\n emptyState?: React.ReactNode\n /** Stick to bottom while the user hasn't scrolled away. Default true. */\n autoScroll?: boolean\n /** Distance from the bottom that still counts as \"at the bottom\". Default 120 px. */\n scrollThresholdPx?: number\n /** Virtualize long transcripts (dynamic row measurement). Default false. */\n virtualized?: boolean\n labels?: Partial<ChatTranscriptLabels>\n className?: string\n}\n\nfunction defaultRenderItem(message: ChatMessageView) {\n return (\n <ChatMessage\n role={message.role}\n status={message.status}\n content={<MarkdownSurface value={message.content} streaming={message.status === 'streaming'} />}\n variants={message.variants}\n />\n )\n}\n\n/**\n * The conversation list: `role=\"log\"` with polite turn-completion\n * announcements, stick-to-bottom auto-scroll with a jump-to-latest pill,\n * optional virtualization for thousand-message threads.\n */\nexport const ChatTranscript = React.forwardRef<HTMLDivElement, ChatTranscriptProps>(\n function ChatTranscript(\n {\n items,\n renderItem = defaultRenderItem,\n live,\n emptyState,\n autoScroll = true,\n scrollThresholdPx = 120,\n virtualized = false,\n labels,\n className,\n },\n ref,\n ) {\n const scrollRef = React.useRef<HTMLDivElement | null>(null)\n const atBottomRef = React.useRef(true)\n const lastScrollTopRef = React.useRef(0)\n const lastTouchYRef = React.useRef<number | null>(null)\n const [showJump, setShowJump] = React.useState(false)\n const [announcement, setAnnouncement] = React.useState('')\n const previousCountRef = React.useRef(-1)\n\n const onScroll = () => {\n const element = scrollRef.current\n if (element === null) {\n return\n }\n const distance = element.scrollHeight - element.scrollTop - element.clientHeight\n const goingUp = element.scrollTop < lastScrollTopRef.current\n lastScrollTopRef.current = element.scrollTop\n const atBottom = distance <= scrollThresholdPx && !(goingUp && distance > 0)\n atBottomRef.current = atBottom\n setShowJump(element.scrollHeight > element.clientHeight && !atBottom)\n }\n\n /**\n * Fast streams re-pin the bottom on every render (rAF), which erases\n * a partial upward scroll before it can cross `scrollThresholdPx` —\n * so user scroll-up INTENT cancels stickiness synchronously here\n * (wheel up, touch drag up, scroll-up keys), and any upward scroll\n * event with distance left also breaks it. Scrolling back to the\n * bottom (or the jump pill) re-arms the follow.\n */\n const cancelStick = () => {\n const element = scrollRef.current\n if (element === null) {\n return\n }\n atBottomRef.current = false\n setShowJump(element.scrollHeight > element.clientHeight)\n }\n\n const onWheel = (event: React.WheelEvent<HTMLDivElement>) => {\n if (event.deltaY < 0) {\n cancelStick()\n }\n }\n\n const onTouchStart = (event: React.TouchEvent<HTMLDivElement>) => {\n lastTouchYRef.current = event.touches[0]?.clientY ?? null\n }\n\n const onTouchMove = (event: React.TouchEvent<HTMLDivElement>) => {\n const y = event.touches[0]?.clientY\n if (y === undefined) {\n return\n }\n if (lastTouchYRef.current !== null && y > lastTouchYRef.current) {\n cancelStick()\n }\n lastTouchYRef.current = y\n }\n\n const onKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (\n event.key === 'ArrowUp' ||\n event.key === 'PageUp' ||\n event.key === 'Home' ||\n (event.key === ' ' && event.shiftKey)\n ) {\n cancelStick()\n }\n }\n\n React.useEffect(() => {\n const element = scrollRef.current\n if (element === null || !autoScroll || !atBottomRef.current) {\n return\n }\n const frame = requestAnimationFrame(() => {\n element.scrollTop = element.scrollHeight\n })\n return () => cancelAnimationFrame(frame)\n })\n\n React.useEffect(() => {\n if (previousCountRef.current === -1) {\n previousCountRef.current = items.length\n return\n }\n if (items.length > previousCountRef.current) {\n const added = items[items.length - 1]\n previousCountRef.current = items.length\n if (added && added.role !== 'user') {\n setAnnouncement(labels?.replied ?? 'Assistant replied')\n }\n return\n }\n previousCountRef.current = items.length\n }, [items, labels])\n\n const scrollToBottom = () => {\n const element = scrollRef.current\n if (element !== null) {\n element.scrollTo({ top: element.scrollHeight, behavior: 'smooth' })\n }\n }\n\n const rowCount = items.length + (live !== undefined ? 1 : 0)\n const virtualizer = useVirtualizer({\n count: virtualized ? rowCount : 0,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => 120,\n overscan: 8,\n initialRect: { width: 480, height: 600 },\n })\n\n return (\n <div ref={ref} data-as=\"chat-transcript\" className={cn('relative min-h-0 flex-1', className)}>\n <div\n ref={scrollRef}\n onScroll={onScroll}\n onWheel={onWheel}\n onTouchStart={onTouchStart}\n onTouchMove={onTouchMove}\n onKeyDown={onKeyDown}\n role=\"log\"\n aria-label={labels?.log ?? 'Conversation'}\n aria-busy={live !== undefined}\n className=\"h-full overflow-y-auto overscroll-contain px-1 py-2\"\n >\n {items.length === 0 && live === undefined ? (\n <div className=\"flex h-full min-h-40 items-center justify-center p-4 text-sm text-[var(--as-muted-fg)]\">\n {emptyState ?? (labels?.empty ?? 'Start the conversation')}\n </div>\n ) : virtualized ? (\n <div style={{ height: virtualizer.getTotalSize(), position: 'relative' }}>\n {virtualizer.getVirtualItems().map((row) => {\n const isLive = row.index >= items.length\n const node = isLive ? live : renderItem(items[row.index]!, row.index)\n return (\n <div\n key={row.key}\n data-index={row.index}\n ref={virtualizer.measureElement}\n style={{ position: 'absolute', top: 0, left: 0, width: '100%', transform: `translateY(${row.start}px)` }}\n className=\"px-2 py-1.5\"\n >\n {node}\n </div>\n )\n })}\n </div>\n ) : (\n <div className=\"flex flex-col gap-3 px-2\">\n {items.map((message, index) => (\n <div key={message.id}>{renderItem(message, index)}</div>\n ))}\n {live !== undefined ? <div data-as=\"chat-transcript-live\">{live}</div> : null}\n </div>\n )}\n </div>\n <div aria-live=\"polite\" className=\"sr-only\">\n {announcement}\n </div>\n {showJump ? (\n <button\n type=\"button\"\n onClick={scrollToBottom}\n aria-label={labels?.scrollToBottom ?? 'Scroll to latest'}\n title={labels?.scrollToBottom ?? 'Scroll to latest'}\n className=\"absolute bottom-3 left-1/2 flex size-8 -translate-x-1/2 items-center justify-center rounded-full border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)] shadow-[var(--as-shadow-2)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <ArrowDown className=\"size-4\" aria-hidden />\n </button>\n ) : null}\n </div>\n )\n },\n)\n"]}
@@ -1,54 +0,0 @@
1
- import { e as ChatRole } from './types-LhE1KFBf.js';
2
-
3
- interface BranchNode {
4
- id: string;
5
- role: ChatRole;
6
- excerpt?: string;
7
- parentId: string | null;
8
- activeChildId: string | null;
9
- /** In insertion (sibling-order) order; the last entry is the newest sibling. */
10
- childIds: string[];
11
- }
12
- interface BranchTree {
13
- activeRootId: string | null;
14
- nodes: Record<string, BranchNode>;
15
- }
16
- type BranchNodeInput = Omit<BranchNode, 'childIds'>;
17
- /**
18
- * Index the server's branch-tree projection (family contract: the read-only
19
- * `GET …/tree` shape) and compute child lists. Insertion order defines
20
- * sibling order — the last child is the newest variant (fallback when a
21
- * parent has no `activeChildId`).
22
- */
23
- declare function buildBranchTree(input: {
24
- activeRootId?: string | null;
25
- nodes: BranchNodeInput[];
26
- }): BranchTree;
27
- /**
28
- * Degenerate single-path tree from a visible (already active-path) message
29
- * list — for apps/backends without branching yet. Every message parents to
30
- * the previous one; the tree renders identically to a branched one.
31
- */
32
- declare function linearTree(items: {
33
- id: string;
34
- role: ChatRole;
35
- excerpt?: string;
36
- }[]): BranchTree;
37
- /**
38
- * The visible conversation: pointer walk from the active root, following
39
- * each node's `activeChildId` (fallback: newest sibling). Cycle-safe.
40
- */
41
- declare function walkActivePath(tree: BranchTree): string[];
42
- declare function activePathSet(tree: BranchTree): Set<string>;
43
- /**
44
- * Sibling variant info for the inline `‹ n/N ›` switcher: the 1-based index
45
- * of `nodeId` among its siblings (roots are siblings of each other) and the
46
- * ordered sibling ids.
47
- */
48
- declare function variantInfo(tree: BranchTree, nodeId: string): {
49
- index: number;
50
- count: number;
51
- siblingIds: string[];
52
- };
53
-
54
- export { type BranchTree as B, type BranchNode as a, type BranchNodeInput as b, activePathSet as c, buildBranchTree as d, linearTree as l, variantInfo as v, walkActivePath as w };