ai-schadcn-chat 0.1.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 (221) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +307 -0
  3. package/dist/ChatPanel-Cyq7boWQ.js +3468 -0
  4. package/dist/ChatPanel-Cyq7boWQ.js.map +1 -0
  5. package/dist/ChatPanel-D3VL5Vdq.cjs +10 -0
  6. package/dist/ChatPanel-D3VL5Vdq.cjs.map +1 -0
  7. package/dist/ChatProvider-DcrNbVMh.cjs +2 -0
  8. package/dist/ChatProvider-DcrNbVMh.cjs.map +1 -0
  9. package/dist/ChatProvider-DoD4jj4M.js +128 -0
  10. package/dist/ChatProvider-DoD4jj4M.js.map +1 -0
  11. package/dist/chat-engine-BaFdimfx.cjs +8 -0
  12. package/dist/chat-engine-BaFdimfx.cjs.map +1 -0
  13. package/dist/chat-engine-Cja0KtWE.js +520 -0
  14. package/dist/chat-engine-Cja0KtWE.js.map +1 -0
  15. package/dist/components/chat/AgentManager.d.ts +11 -0
  16. package/dist/components/chat/AgentManager.d.ts.map +1 -0
  17. package/dist/components/chat/AgentManager.js +110 -0
  18. package/dist/components/chat/Attachment.d.ts +34 -0
  19. package/dist/components/chat/Attachment.d.ts.map +1 -0
  20. package/dist/components/chat/Attachment.js +59 -0
  21. package/dist/components/chat/AttachmentPreview.d.ts +8 -0
  22. package/dist/components/chat/AttachmentPreview.d.ts.map +1 -0
  23. package/dist/components/chat/AttachmentPreview.js +43 -0
  24. package/dist/components/chat/ChatComposer.d.ts +11 -0
  25. package/dist/components/chat/ChatComposer.d.ts.map +1 -0
  26. package/dist/components/chat/ChatComposer.js +10 -0
  27. package/dist/components/chat/ChatHeader.d.ts +5 -0
  28. package/dist/components/chat/ChatHeader.d.ts.map +1 -0
  29. package/dist/components/chat/ChatHeader.js +132 -0
  30. package/dist/components/chat/ChatPanel.d.ts +36 -0
  31. package/dist/components/chat/ChatPanel.d.ts.map +1 -0
  32. package/dist/components/chat/ChatPanel.js +55 -0
  33. package/dist/components/chat/ChatProvider.d.ts +58 -0
  34. package/dist/components/chat/ChatProvider.d.ts.map +1 -0
  35. package/dist/components/chat/ChatProvider.js +145 -0
  36. package/dist/components/chat/Chatbubble.d.ts +30 -0
  37. package/dist/components/chat/Chatbubble.d.ts.map +1 -0
  38. package/dist/components/chat/Chatbubble.js +50 -0
  39. package/dist/components/chat/Markdown.d.ts +35 -0
  40. package/dist/components/chat/Markdown.d.ts.map +1 -0
  41. package/dist/components/chat/Markdown.js +130 -0
  42. package/dist/components/chat/Message.d.ts +22 -0
  43. package/dist/components/chat/Message.d.ts.map +1 -0
  44. package/dist/components/chat/Message.js +21 -0
  45. package/dist/components/chat/MessageInput.d.ts +24 -0
  46. package/dist/components/chat/MessageInput.d.ts.map +1 -0
  47. package/dist/components/chat/MessageInput.js +255 -0
  48. package/dist/components/chat/MessageList.d.ts +9 -0
  49. package/dist/components/chat/MessageList.d.ts.map +1 -0
  50. package/dist/components/chat/MessageList.js +249 -0
  51. package/dist/components/chat/MessageMarker.d.ts +25 -0
  52. package/dist/components/chat/MessageMarker.d.ts.map +1 -0
  53. package/dist/components/chat/MessageMarker.js +31 -0
  54. package/dist/components/chat/MessageScroller.d.ts +12 -0
  55. package/dist/components/chat/MessageScroller.d.ts.map +1 -0
  56. package/dist/components/chat/MessageScroller.js +48 -0
  57. package/dist/components/chat/MessageScrollerPrimitives.d.ts +76 -0
  58. package/dist/components/chat/MessageScrollerPrimitives.d.ts.map +1 -0
  59. package/dist/components/chat/MessageScrollerPrimitives.js +438 -0
  60. package/dist/components/chat/ProviderManager.d.ts +10 -0
  61. package/dist/components/chat/ProviderManager.d.ts.map +1 -0
  62. package/dist/components/chat/ProviderManager.js +110 -0
  63. package/dist/components/chat/ReasoningPanel.d.ts +15 -0
  64. package/dist/components/chat/ReasoningPanel.d.ts.map +1 -0
  65. package/dist/components/chat/ReasoningPanel.js +40 -0
  66. package/dist/components/chat/ThemeToggle.d.ts +4 -0
  67. package/dist/components/chat/ThemeToggle.d.ts.map +1 -0
  68. package/dist/components/chat/ThemeToggle.js +59 -0
  69. package/dist/components/index.d.ts +44 -0
  70. package/dist/components/index.d.ts.map +1 -0
  71. package/dist/components/index.js +49 -0
  72. package/dist/components/ui/avatar.d.ts +11 -0
  73. package/dist/components/ui/avatar.d.ts.map +1 -0
  74. package/dist/components/ui/avatar.js +33 -0
  75. package/dist/components/ui/badge.d.ts +10 -0
  76. package/dist/components/ui/badge.d.ts.map +1 -0
  77. package/dist/components/ui/badge.js +19 -0
  78. package/dist/components/ui/button.d.ts +12 -0
  79. package/dist/components/ui/button.d.ts.map +1 -0
  80. package/dist/components/ui/button.js +34 -0
  81. package/dist/components/ui/card.d.ts +8 -0
  82. package/dist/components/ui/card.d.ts.map +1 -0
  83. package/dist/components/ui/card.js +15 -0
  84. package/dist/components/ui/checkbox.d.ts +5 -0
  85. package/dist/components/ui/checkbox.d.ts.map +1 -0
  86. package/dist/components/ui/checkbox.js +8 -0
  87. package/dist/components/ui/collapsible.d.ts +6 -0
  88. package/dist/components/ui/collapsible.d.ts.map +1 -0
  89. package/dist/components/ui/collapsible.js +5 -0
  90. package/dist/components/ui/dialog.d.ts +24 -0
  91. package/dist/components/ui/dialog.d.ts.map +1 -0
  92. package/dist/components/ui/dialog.js +30 -0
  93. package/dist/components/ui/dropdown-menu.d.ts +17 -0
  94. package/dist/components/ui/dropdown-menu.d.ts.map +1 -0
  95. package/dist/components/ui/dropdown-menu.js +20 -0
  96. package/dist/components/ui/input.d.ts +4 -0
  97. package/dist/components/ui/input.d.ts.map +1 -0
  98. package/dist/components/ui/input.js +7 -0
  99. package/dist/components/ui/label.d.ts +6 -0
  100. package/dist/components/ui/label.d.ts.map +1 -0
  101. package/dist/components/ui/label.js +10 -0
  102. package/dist/components/ui/popover.d.ts +6 -0
  103. package/dist/components/ui/popover.d.ts.map +1 -0
  104. package/dist/components/ui/popover.js +9 -0
  105. package/dist/components/ui/scroll-area.d.ts +3 -0
  106. package/dist/components/ui/scroll-area.d.ts.map +1 -0
  107. package/dist/components/ui/scroll-area.js +8 -0
  108. package/dist/components/ui/select.d.ts +14 -0
  109. package/dist/components/ui/select.d.ts.map +1 -0
  110. package/dist/components/ui/select.js +26 -0
  111. package/dist/components/ui/separator.d.ts +3 -0
  112. package/dist/components/ui/separator.d.ts.map +1 -0
  113. package/dist/components/ui/separator.js +6 -0
  114. package/dist/components/ui/switch.d.ts +3 -0
  115. package/dist/components/ui/switch.d.ts.map +1 -0
  116. package/dist/components/ui/switch.js +6 -0
  117. package/dist/components/ui/tabs.d.ts +6 -0
  118. package/dist/components/ui/tabs.d.ts.map +1 -0
  119. package/dist/components/ui/tabs.js +11 -0
  120. package/dist/components/ui/tooltip.d.ts +6 -0
  121. package/dist/components/ui/tooltip.d.ts.map +1 -0
  122. package/dist/components/ui/tooltip.js +9 -0
  123. package/dist/components.cjs +2 -0
  124. package/dist/components.cjs.map +1 -0
  125. package/dist/components.js +129 -0
  126. package/dist/components.js.map +1 -0
  127. package/dist/hooks/index.d.ts +2 -0
  128. package/dist/hooks/index.d.ts.map +1 -0
  129. package/dist/hooks/index.js +3 -0
  130. package/dist/hooks/useChat.d.ts +6 -0
  131. package/dist/hooks/useChat.d.ts.map +1 -0
  132. package/dist/hooks/useChat.js +5 -0
  133. package/dist/hooks.cjs +2 -0
  134. package/dist/hooks.cjs.map +1 -0
  135. package/dist/hooks.js +8 -0
  136. package/dist/hooks.js.map +1 -0
  137. package/dist/index-Ci4qkGOp.cjs +20 -0
  138. package/dist/index-Ci4qkGOp.cjs.map +1 -0
  139. package/dist/index-NhYr-cEJ.js +567 -0
  140. package/dist/index-NhYr-cEJ.js.map +1 -0
  141. package/dist/index.cjs +2 -0
  142. package/dist/index.cjs.map +1 -0
  143. package/dist/index.d.ts +33 -0
  144. package/dist/index.d.ts.map +1 -0
  145. package/dist/index.js +32 -0
  146. package/dist/index.js.map +1 -0
  147. package/dist/lib/agents.d.ts +29 -0
  148. package/dist/lib/agents.d.ts.map +1 -0
  149. package/dist/lib/agents.js +145 -0
  150. package/dist/lib/attachments.d.ts +40 -0
  151. package/dist/lib/attachments.d.ts.map +1 -0
  152. package/dist/lib/attachments.js +152 -0
  153. package/dist/lib/chat-engine.d.ts +109 -0
  154. package/dist/lib/chat-engine.d.ts.map +1 -0
  155. package/dist/lib/chat-engine.js +505 -0
  156. package/dist/lib/index.d.ts +8 -0
  157. package/dist/lib/index.d.ts.map +1 -0
  158. package/dist/lib/index.js +7 -0
  159. package/dist/lib/persistence.d.ts +30 -0
  160. package/dist/lib/persistence.d.ts.map +1 -0
  161. package/dist/lib/persistence.js +104 -0
  162. package/dist/lib/providers.d.ts +40 -0
  163. package/dist/lib/providers.d.ts.map +1 -0
  164. package/dist/lib/providers.js +0 -0
  165. package/dist/lib/tw-merge.d.ts +9 -0
  166. package/dist/lib/tw-merge.d.ts.map +1 -0
  167. package/dist/lib/tw-merge.js +8 -0
  168. package/dist/lib/utils.d.ts +69 -0
  169. package/dist/lib/utils.d.ts.map +1 -0
  170. package/dist/lib/utils.js +166 -0
  171. package/dist/lib/voice.d.ts +57 -0
  172. package/dist/lib/voice.d.ts.map +1 -0
  173. package/dist/lib/voice.js +103 -0
  174. package/dist/lib.cjs +2 -0
  175. package/dist/lib.cjs.map +1 -0
  176. package/dist/lib.js +59 -0
  177. package/dist/lib.js.map +1 -0
  178. package/dist/marker.css +68 -0
  179. package/dist/package.json +6 -0
  180. package/dist/presets-BxnbImSM.cjs +2 -0
  181. package/dist/presets-BxnbImSM.cjs.map +1 -0
  182. package/dist/presets-D8VQTD8n.js +193 -0
  183. package/dist/presets-D8VQTD8n.js.map +1 -0
  184. package/dist/providers/anthropic.d.ts +30 -0
  185. package/dist/providers/anthropic.d.ts.map +1 -0
  186. package/dist/providers/anthropic.js +251 -0
  187. package/dist/providers/base.d.ts +55 -0
  188. package/dist/providers/base.d.ts.map +1 -0
  189. package/dist/providers/base.js +272 -0
  190. package/dist/providers/index.d.ts +15 -0
  191. package/dist/providers/index.d.ts.map +1 -0
  192. package/dist/providers/index.js +24 -0
  193. package/dist/providers/openai.d.ts +31 -0
  194. package/dist/providers/openai.d.ts.map +1 -0
  195. package/dist/providers/openai.js +192 -0
  196. package/dist/providers-BuaGy2tX.cjs +4 -0
  197. package/dist/providers-BuaGy2tX.cjs.map +1 -0
  198. package/dist/providers-kp-avTNl.js +324 -0
  199. package/dist/providers-kp-avTNl.js.map +1 -0
  200. package/dist/providers.cjs +2 -0
  201. package/dist/providers.cjs.map +1 -0
  202. package/dist/providers.js +14 -0
  203. package/dist/providers.js.map +1 -0
  204. package/dist/scroller.css +278 -0
  205. package/dist/style.css +1 -0
  206. package/dist/types/chat.d.ts +342 -0
  207. package/dist/types/chat.d.ts.map +1 -0
  208. package/dist/types/chat.js +4 -0
  209. package/dist/types/index.d.ts +9 -0
  210. package/dist/types/index.d.ts.map +1 -0
  211. package/dist/types/index.js +14 -0
  212. package/dist/types/presets.d.ts +25 -0
  213. package/dist/types/presets.d.ts.map +1 -0
  214. package/dist/types/presets.js +207 -0
  215. package/dist/types.cjs +2 -0
  216. package/dist/types.cjs.map +1 -0
  217. package/dist/types.js +16 -0
  218. package/dist/types.js.map +1 -0
  219. package/dist/typeset-presets.css +56 -0
  220. package/dist/typeset.css +505 -0
  221. package/package.json +158 -0
@@ -0,0 +1,110 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * AgentManager — a dialog to create, read, update, and delete switchable
4
+ * assistant "agents". Built-in agents are read-only (but can be duplicated);
5
+ * user agents persist to localStorage via `lib/agents`. Picking an agent
6
+ * calls `onApply`, which the header maps onto the live chat config.
7
+ */
8
+ import { ArrowLeft, Check, Copy, Pencil, Plus, Trash2, } from "lucide-react";
9
+ import { useEffect, useState } from "react";
10
+ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "../ui/dialog.js";
11
+ import { Button } from "../ui/button.js";
12
+ import { Input, Textarea } from "../ui/input.js";
13
+ import { Label } from "../ui/label.js";
14
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../ui/select.js";
15
+ import { Badge } from "../ui/badge.js";
16
+ import { deleteAgent as removeAgent, duplicateAgent, listAgents, saveAgent, } from "../../lib/agents.js";
17
+ import { cn } from "../../lib/utils.js";
18
+ const TONES = [
19
+ "friendly",
20
+ "professional",
21
+ "casual",
22
+ "concise",
23
+ "playful",
24
+ "academic",
25
+ ];
26
+ const EMPTY_DRAFT = {
27
+ name: "",
28
+ description: "",
29
+ icon: "🤖",
30
+ systemPrompt: "",
31
+ tone: "friendly",
32
+ suggestions: "",
33
+ };
34
+ function toDraft(a) {
35
+ return {
36
+ id: a.id,
37
+ name: a.name,
38
+ description: a.description ?? "",
39
+ icon: a.icon ?? "🤖",
40
+ systemPrompt: a.systemPrompt,
41
+ tone: a.tone ?? "friendly",
42
+ suggestions: (a.suggestions ?? []).join("\n"),
43
+ };
44
+ }
45
+ export function AgentManager({ open, onOpenChange, activeAgentId, onApply, startInCreate = false, }) {
46
+ const [agents, setAgents] = useState([]);
47
+ const [view, setView] = useState("list");
48
+ const [draft, setDraft] = useState(EMPTY_DRAFT);
49
+ const refresh = () => setAgents(listAgents());
50
+ useEffect(() => {
51
+ if (open) {
52
+ refresh();
53
+ setView(startInCreate ? "edit" : "list");
54
+ setDraft(EMPTY_DRAFT);
55
+ }
56
+ }, [open, startInCreate]);
57
+ const startCreate = () => {
58
+ setDraft(EMPTY_DRAFT);
59
+ setView("edit");
60
+ };
61
+ const startEdit = (a) => {
62
+ setDraft(toDraft(a));
63
+ setView("edit");
64
+ };
65
+ const canSave = draft.name.trim().length > 0 && draft.systemPrompt.trim().length > 0;
66
+ const onSave = () => {
67
+ if (!canSave)
68
+ return;
69
+ const saved = saveAgent({
70
+ id: draft.id,
71
+ name: draft.name,
72
+ description: draft.description,
73
+ icon: draft.icon,
74
+ systemPrompt: draft.systemPrompt,
75
+ tone: draft.tone,
76
+ suggestions: draft.suggestions.split("\n"),
77
+ });
78
+ refresh();
79
+ onApply(saved);
80
+ onOpenChange(false);
81
+ };
82
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsx(DialogContent, { className: "max-w-lg gap-0 overflow-hidden p-0", children: view === "list" ? (_jsx(ListView, { agents: agents, activeAgentId: activeAgentId, onCreate: startCreate, onEdit: startEdit, onApply: (a) => {
83
+ onApply(a);
84
+ onOpenChange(false);
85
+ }, onDuplicate: (id) => {
86
+ duplicateAgent(id);
87
+ refresh();
88
+ }, onDelete: (id) => {
89
+ removeAgent(id);
90
+ refresh();
91
+ } })) : (_jsx(EditView, { draft: draft, setDraft: setDraft, canSave: canSave, onBack: () => setView("list"), onSave: onSave })) }) }));
92
+ }
93
+ function ListView({ agents, activeAgentId, onCreate, onEdit, onApply, onDuplicate, onDelete, }) {
94
+ return (_jsxs(_Fragment, { children: [_jsxs(DialogHeader, { className: "border-b border-border/60 p-5 pb-4", children: [_jsx(DialogTitle, { children: "Agents" }), _jsx(DialogDescription, { children: "Switch, create, edit, or remove your assistant agents." })] }), _jsx("div", { className: "max-h-[55vh] space-y-1.5 overflow-y-auto p-3 scrollbar-thin", children: agents.map((a) => {
95
+ const active = a.id === activeAgentId;
96
+ return (_jsxs("div", { className: cn("group/agent flex items-center gap-3 rounded-xl border p-2.5 transition-colors", active
97
+ ? "border-primary/40 bg-primary/5"
98
+ : "border-border/60 hover:bg-accent/50"), children: [_jsx("span", { className: "grid size-9 shrink-0 place-items-center rounded-lg bg-muted text-lg", children: a.icon ?? "🤖" }), _jsxs(Button, { type: "button", variant: "ghost", onClick: () => onApply(a), className: "h-auto min-w-0 flex-1 flex-col items-stretch justify-start gap-0 p-0 text-left hover:bg-transparent", children: [_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "truncate text-sm font-medium", children: a.name }), active && (_jsx(Check, { className: "size-3.5 shrink-0 text-primary" })), a.builtIn && (_jsx(Badge, { variant: "secondary", className: "text-[9px]", children: "built-in" }))] }), a.description && (_jsx("span", { className: "block truncate text-xs font-normal text-muted-foreground", children: a.description }))] }), _jsxs("div", { className: "flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity group-hover/agent:opacity-100 focus-within:opacity-100", children: [_jsx(IconBtn, { label: "Duplicate", onClick: () => onDuplicate(a.id), children: _jsx(Copy, { className: "size-3.5" }) }), !a.builtIn && (_jsxs(_Fragment, { children: [_jsx(IconBtn, { label: "Edit", onClick: () => onEdit(a), children: _jsx(Pencil, { className: "size-3.5" }) }), _jsx(IconBtn, { label: "Delete", destructive: true, onClick: () => onDelete(a.id), children: _jsx(Trash2, { className: "size-3.5" }) })] }))] })] }, a.id));
99
+ }) }), _jsx(DialogFooter, { className: "border-t border-border/60 p-4", children: _jsxs(Button, { onClick: onCreate, className: "w-full gap-2 sm:w-auto", children: [_jsx(Plus, { className: "size-4" }), "New agent"] }) })] }));
100
+ }
101
+ function EditView({ draft, setDraft, canSave, onBack, onSave, }) {
102
+ const set = (k, v) => setDraft({ ...draft, [k]: v });
103
+ return (_jsxs(_Fragment, { children: [_jsx(DialogHeader, { className: "border-b border-border/60 p-5 pb-4", children: _jsxs(DialogTitle, { className: "flex items-center gap-2", children: [_jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", onClick: onBack, "aria-label": "Back to list", className: "text-muted-foreground hover:text-foreground", children: _jsx(ArrowLeft, { className: "size-4" }) }), draft.id ? "Edit agent" : "New agent"] }) }), _jsxs("div", { className: "max-h-[55vh] space-y-4 overflow-y-auto p-5 scrollbar-thin", children: [_jsxs("div", { className: "flex gap-3", children: [_jsx(Field, { label: "Icon", className: "w-20", children: _jsx(Input, { value: draft.icon, onChange: (e) => set("icon", e.target.value.slice(0, 2)), className: "text-center text-lg", maxLength: 2 }) }), _jsx(Field, { label: "Name", className: "flex-1", children: _jsx(Input, { value: draft.name, onChange: (e) => set("name", e.target.value), placeholder: "e.g. SQL expert" }) })] }), _jsx(Field, { label: "Description", children: _jsx(Input, { value: draft.description, onChange: (e) => set("description", e.target.value), placeholder: "One-line summary (optional)" }) }), _jsx(Field, { label: "System prompt", children: _jsx(Textarea, { value: draft.systemPrompt, onChange: (e) => set("systemPrompt", e.target.value), placeholder: "You are a helpful assistant that\u2026", className: "min-h-28 resize-y" }) }), _jsx(Field, { label: "Tone", children: _jsxs(Select, { value: draft.tone, onValueChange: (v) => set("tone", v), children: [_jsx(SelectTrigger, { className: "capitalize", children: _jsx(SelectValue, { placeholder: "Select a tone" }) }), _jsx(SelectContent, { children: TONES.map((t) => (_jsx(SelectItem, { value: t, className: "capitalize", children: t }, t))) })] }) }), _jsx(Field, { label: "Starter suggestions", hint: "one per line", children: _jsx(Textarea, { value: draft.suggestions, onChange: (e) => set("suggestions", e.target.value), placeholder: "Explain a concept\nReview my code", className: "min-h-20 resize-y" }) })] }), _jsxs(DialogFooter, { className: "gap-2 border-t border-border/60 p-4", children: [_jsx(Button, { variant: "ghost", onClick: onBack, children: "Cancel" }), _jsxs(Button, { onClick: onSave, disabled: !canSave, className: "gap-2", children: [_jsx(Check, { className: "size-4" }), "Save & use"] })] })] }));
104
+ }
105
+ function Field({ label, hint, className, children, }) {
106
+ return (_jsxs("div", { className: cn("space-y-1.5", className), children: [_jsxs(Label, { className: "flex items-center gap-1.5 text-xs font-medium text-muted-foreground", children: [label, hint && _jsxs("span", { className: "text-muted-foreground/60", children: ["\u00B7 ", hint] })] }), children] }));
107
+ }
108
+ function IconBtn({ label, destructive, onClick, children, }) {
109
+ return (_jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": label, onClick: onClick, className: cn("text-muted-foreground hover:bg-foreground/10", destructive ? "hover:text-destructive" : "hover:text-foreground"), children: children }));
110
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Attachment — sourced from `apps/v4/styles/radix-rhea/ui/attachment.tsx` in
3
+ * the official `shadcn-ui/ui` repo (https://github.com/shadcn-ui/ui).
4
+ *
5
+ * Source paths in the original module were remapped:
6
+ * - `@/lib/utils` → `../../lib/utils.js`
7
+ * - `radix-ui` (Slot.Root) → `@radix-ui/react-slot`
8
+ * - `@/styles/radix-rhea/ui/button` → `../ui/button.js`
9
+ */
10
+ import * as React from "react";
11
+ import { type VariantProps } from "class-variance-authority";
12
+ import { Button } from "../ui/button.js";
13
+ declare const attachmentVariants: (props?: ({
14
+ size?: "default" | "sm" | "xs" | null | undefined;
15
+ orientation?: "horizontal" | "vertical" | null | undefined;
16
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
17
+ declare function Attachment({ className, state, size, orientation, ...props }: React.ComponentProps<"div"> & VariantProps<typeof attachmentVariants> & {
18
+ state?: "idle" | "uploading" | "processing" | "error" | "done";
19
+ }): React.JSX.Element;
20
+ declare const attachmentMediaVariants: (props?: ({
21
+ variant?: "icon" | "image" | null | undefined;
22
+ } & import("class-variance-authority/types").ClassProp) | undefined) => string;
23
+ declare function AttachmentMedia({ className, variant, ...props }: React.ComponentProps<"div"> & VariantProps<typeof attachmentMediaVariants>): React.JSX.Element;
24
+ declare function AttachmentContent({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
25
+ declare function AttachmentTitle({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
26
+ declare function AttachmentDescription({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
27
+ declare function AttachmentActions({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
28
+ declare function AttachmentAction({ className, variant, size, ...props }: React.ComponentProps<typeof Button>): React.JSX.Element;
29
+ declare function AttachmentTrigger({ className, asChild, type, ...props }: React.ComponentProps<"button"> & {
30
+ asChild?: boolean;
31
+ }): React.JSX.Element;
32
+ declare function AttachmentGroup({ className, ...props }: React.ComponentProps<"div">): React.JSX.Element;
33
+ export { Attachment, AttachmentGroup, AttachmentMedia, AttachmentContent, AttachmentTitle, AttachmentDescription, AttachmentActions, AttachmentAction, AttachmentTrigger, };
34
+ //# sourceMappingURL=Attachment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Attachment.d.ts","sourceRoot":"","sources":["../../../src/components/chat/Attachment.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAIlE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAEzC,QAAA,MAAM,kBAAkB;;;8EAgBvB,CAAC;AAEF,iBAAS,UAAU,CAAC,EAClB,SAAS,EACT,KAAc,EACd,IAAgB,EAChB,WAA0B,EAC1B,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAC5B,YAAY,CAAC,OAAO,kBAAkB,CAAC,GAAG;IACxC,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,YAAY,GAAG,OAAO,GAAG,MAAM,CAAC;CAChE,qBAaF;AAED,QAAA,MAAM,uBAAuB;;8EAc5B,CAAC;AAEF,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,OAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,YAAY,CAAC,OAAO,uBAAuB,CAAC,qBAS5E;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAW7B;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,qBAW9B;AAED,iBAAS,qBAAqB,CAAC,EAC7B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,qBAY9B;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAW7B;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,OAAO,EACP,IAAgB,EAChB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,qBAUrC;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,OAAe,EACf,IAAI,EACJ,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,qBAWA;AAED,iBAAS,eAAe,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAW5E;AAED,OAAO,EACL,UAAU,EACV,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,CAAC"}
@@ -0,0 +1,59 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cva } from "class-variance-authority";
3
+ import { Slot } from "@radix-ui/react-slot";
4
+ import { cn } from "../../lib/utils.js";
5
+ import { Button } from "../ui/button.js";
6
+ const attachmentVariants = cva("group/attachment relative flex w-fit max-w-full min-w-0 shrink-0 flex-wrap rounded-2xl border bg-card text-card-foreground transition-colors focus-within:ring-1 focus-within:ring-ring/30 has-[>a,>button]:hover:bg-muted/50 data-[state=error]:border-destructive/30 data-[state=idle]:border-dashed", {
7
+ variants: {
8
+ size: {
9
+ default: "gap-2 text-sm has-data-[slot=attachment-content]:px-2.5 has-data-[slot=attachment-content]:py-2 has-data-[slot=attachment-media]:p-2",
10
+ sm: "gap-2.5 text-xs has-data-[slot=attachment-content]:px-2 has-data-[slot=attachment-content]:py-1.5 has-data-[slot=attachment-media]:p-1.5",
11
+ xs: "gap-1.5 rounded-xl text-xs has-data-[slot=attachment-content]:px-1.5 has-data-[slot=attachment-content]:py-1 has-data-[slot=attachment-media]:p-1",
12
+ },
13
+ orientation: {
14
+ horizontal: "min-w-40 items-center",
15
+ vertical: "w-24 flex-col has-data-[slot=attachment-content]:w-30",
16
+ },
17
+ },
18
+ });
19
+ function Attachment({ className, state = "done", size = "default", orientation = "horizontal", ...props }) {
20
+ const resolvedOrientation = orientation ?? "horizontal";
21
+ return (_jsx("div", { "data-slot": "attachment", "data-state": state, "data-size": size, "data-orientation": resolvedOrientation, className: cn(attachmentVariants({ size, orientation }), className), ...props }));
22
+ }
23
+ const attachmentMediaVariants = cva("relative flex aspect-square w-10 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted text-foreground group-data-[orientation=vertical]/attachment:w-full group-data-[size=sm]/attachment:w-8 group-data-[size=xs]/attachment:w-7 group-data-[size=xs]/attachment:rounded-md group-data-[state=error]/attachment:bg-destructive/10 group-data-[state=error]/attachment:text-destructive group-data-[orientation=vertical]/attachment:*:data-[slot=spinner]:size-6! [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 group-data-[orientation=vertical]/attachment:[&_svg:not([class*='size-'])]:size-6 group-data-[size=xs]/attachment:[&_svg:not([class*='size-'])]:size-3.5", {
24
+ variants: {
25
+ variant: {
26
+ icon: "",
27
+ image: "opacity-60 group-data-[state=done]/attachment:opacity-100 group-data-[state=idle]/attachment:opacity-100 *:[img]:aspect-square *:[img]:w-full *:[img]:object-cover",
28
+ },
29
+ },
30
+ defaultVariants: {
31
+ variant: "icon",
32
+ },
33
+ });
34
+ function AttachmentMedia({ className, variant = "icon", ...props }) {
35
+ return (_jsx("div", { "data-slot": "attachment-media", "data-variant": variant, className: cn(attachmentMediaVariants({ variant }), className), ...props }));
36
+ }
37
+ function AttachmentContent({ className, ...props }) {
38
+ return (_jsx("div", { "data-slot": "attachment-content", className: cn("max-w-full min-w-0 flex-1 leading-tight group-data-[orientation=vertical]/attachment:px-1", className), ...props }));
39
+ }
40
+ function AttachmentTitle({ className, ...props }) {
41
+ return (_jsx("span", { "data-slot": "attachment-title", className: cn("block max-w-full min-w-0 truncate font-medium group-data-[state=processing]/attachment:shimmer group-data-[state=uploading]/attachment:shimmer", className), ...props }));
42
+ }
43
+ function AttachmentDescription({ className, ...props }) {
44
+ return (_jsx("span", { "data-slot": "attachment-description", className: cn("mt-0.5 block min-w-0 truncate text-xs text-muted-foreground group-data-[state=error]/attachment:text-destructive/80", "max-w-full", className), ...props }));
45
+ }
46
+ function AttachmentActions({ className, ...props }) {
47
+ return (_jsx("div", { "data-slot": "attachment-actions", className: cn("relative z-20 flex shrink-0 items-center group-data-[orientation=vertical]/attachment:absolute group-data-[orientation=vertical]/attachment:top-3 group-data-[orientation=vertical]/attachment:right-3 group-data-[orientation=vertical]/attachment:gap-1", className), ...props }));
48
+ }
49
+ function AttachmentAction({ className, variant, size = "icon-sm", ...props }) {
50
+ return (_jsx(Button, { "data-slot": "attachment-action", variant: variant ?? "ghost", size: size, className: cn(className), ...props }));
51
+ }
52
+ function AttachmentTrigger({ className, asChild = false, type, ...props }) {
53
+ const Comp = asChild ? Slot : "button";
54
+ return (_jsx(Comp, { "data-slot": "attachment-trigger", type: asChild ? undefined : (type ?? "button"), className: cn("absolute inset-0 z-10 outline-none", className), ...props }));
55
+ }
56
+ function AttachmentGroup({ className, ...props }) {
57
+ return (_jsx("div", { "data-slot": "attachment-group", className: cn("flex min-w-0 scroll-fade-x snap-x snap-mandatory scroll-px-1 scrollbar-none gap-3 overflow-x-auto overscroll-x-contain py-1 *:data-[slot=attachment]:flex-none *:data-[slot=attachment]:snap-start", className), ...props }));
58
+ }
59
+ export { Attachment, AttachmentGroup, AttachmentMedia, AttachmentContent, AttachmentTitle, AttachmentDescription, AttachmentActions, AttachmentAction, AttachmentTrigger, };
@@ -0,0 +1,8 @@
1
+ import type { AttachmentMeta } from "../../types/chat.js";
2
+ export interface AttachmentPreviewProps {
3
+ att: AttachmentMeta;
4
+ state?: "idle" | "uploading" | "processing" | "error" | "done";
5
+ onRemove?: () => void;
6
+ }
7
+ export declare function AttachmentPreview({ att, state, onRemove, }: AttachmentPreviewProps): import("react").JSX.Element;
8
+ //# sourceMappingURL=AttachmentPreview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AttachmentPreview.d.ts","sourceRoot":"","sources":["../../../src/components/chat/AttachmentPreview.tsx"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAmB1D,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,cAAc,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,YAAY,GAAG,OAAO,GAAG,MAAM,CAAC;IAC/D,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,GAAG,EACH,KAAc,EACd,QAAQ,GACT,EAAE,sBAAsB,+BAuCxB"}
@@ -0,0 +1,43 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * AttachmentPreview — a single attachment chip built on the official shadcn
4
+ * `Attachment` block. Renders an image thumbnail (`AttachmentMedia
5
+ * variant="image"`) for image files and a type-specific icon otherwise.
6
+ *
7
+ * Used by both the composer (with a remove action) and the message list
8
+ * (read-only). The `state` prop drives the official component's visuals —
9
+ * `uploading`/`processing` trigger the shimmer on the title.
10
+ */
11
+ import { File as FileIcon, FileCode, FileJson, FileText, FileType, X, } from "lucide-react";
12
+ import { Attachment, AttachmentAction, AttachmentActions, AttachmentContent, AttachmentDescription, AttachmentMedia, AttachmentTitle, } from "./Attachment.js";
13
+ import { formatBytes } from "../../lib/utils.js";
14
+ function iconFor(mime) {
15
+ if (mime === "application/pdf")
16
+ return FileType;
17
+ if (mime === "application/json")
18
+ return FileJson;
19
+ if (mime.startsWith("text/csv") || mime === "text/csv")
20
+ return FileCode;
21
+ if (mime.startsWith("text/"))
22
+ return FileText;
23
+ return FileIcon;
24
+ }
25
+ function shortType(mime) {
26
+ if (!mime || mime === "application/octet-stream")
27
+ return "file";
28
+ if (mime.startsWith("image/"))
29
+ return mime.slice(6).toUpperCase();
30
+ if (mime === "application/pdf")
31
+ return "PDF";
32
+ if (mime === "application/json")
33
+ return "JSON";
34
+ if (mime.startsWith("text/"))
35
+ return mime.slice(5).toUpperCase();
36
+ return mime;
37
+ }
38
+ export function AttachmentPreview({ att, state = "done", onRemove, }) {
39
+ const mime = att.mimeType ?? att.mime ?? "";
40
+ const isImage = mime.startsWith("image/") && Boolean(att.dataUrl);
41
+ const Icon = iconFor(mime);
42
+ return (_jsxs(Attachment, { size: "default", state: state, className: "w-56 max-w-full bg-background/60", children: [isImage ? (_jsx(AttachmentMedia, { variant: "image", className: "size-11 rounded-lg ring-1 ring-border/60", children: _jsx("img", { src: att.dataUrl, alt: att.name, loading: "lazy" }) })) : (_jsx(AttachmentMedia, { className: "size-11 rounded-lg text-primary", children: _jsx(Icon, { className: "size-5" }) })), _jsxs(AttachmentContent, { children: [_jsx(AttachmentTitle, { children: att.name }), _jsxs(AttachmentDescription, { children: [formatBytes(att.size), " \u00B7 ", shortType(mime)] })] }), onRemove && (_jsx(AttachmentActions, { children: _jsx(AttachmentAction, { "aria-label": "Remove attachment", onClick: onRemove, children: _jsx(X, { className: "size-3.5" }) }) }))] }));
43
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Re-export of `MessageInput` to preserve the historical `ChatComposer`
3
+ * import path. The composer has been re-implemented on top of the
4
+ * official shadcn `Attachment` block (see `./MessageInput.tsx`).
5
+ *
6
+ * The `ChatComposer` interface intentionally matches what the original
7
+ * composer exposed: callers can keep using `<ChatComposer />` with no
8
+ * changes.
9
+ */
10
+ export { MessageInput as ChatComposer, type MessageInputProps as ChatComposerProps, } from "./MessageInput.js";
11
+ //# sourceMappingURL=ChatComposer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatComposer.d.ts","sourceRoot":"","sources":["../../../src/components/chat/ChatComposer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EACL,YAAY,IAAI,YAAY,EAC5B,KAAK,iBAAiB,IAAI,iBAAiB,GAC5C,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Re-export of `MessageInput` to preserve the historical `ChatComposer`
3
+ * import path. The composer has been re-implemented on top of the
4
+ * official shadcn `Attachment` block (see `./MessageInput.tsx`).
5
+ *
6
+ * The `ChatComposer` interface intentionally matches what the original
7
+ * composer exposed: callers can keep using `<ChatComposer />` with no
8
+ * changes.
9
+ */
10
+ export { MessageInput as ChatComposer, } from "./MessageInput.js";
@@ -0,0 +1,5 @@
1
+ export interface ChatHeaderProps {
2
+ className?: string;
3
+ }
4
+ export declare function ChatHeader({ className }: ChatHeaderProps): import("react").JSX.Element;
5
+ //# sourceMappingURL=ChatHeader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatHeader.d.ts","sourceRoot":"","sources":["../../../src/components/chat/ChatHeader.tsx"],"names":[],"mappings":"AA8CA,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,UAAU,CAAC,EAAE,SAAS,EAAE,EAAE,eAAe,+BAkZxD"}
@@ -0,0 +1,132 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Chat header — brand mark, title/subtitle, and the control cluster
4
+ * (personality, context documents, conversation history, theme toggle,
5
+ * overflow). Rendered on a translucent, blurred bar that floats above the
6
+ * message stream.
7
+ */
8
+ import { Check, ChevronDown, FileText, History, MessageSquarePlus, MoreHorizontal, Plus, Settings2, Sparkles, Trash2, Wrench, } from "lucide-react";
9
+ import { useState } from "react";
10
+ import { useChat } from "../../hooks/useChat.js";
11
+ import { getActiveAgentId, listAgents, setActiveAgentId, } from "../../lib/agents.js";
12
+ import { Badge } from "../ui/badge.js";
13
+ import { Button } from "../ui/button.js";
14
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "../ui/dropdown-menu.js";
15
+ import { Tooltip, TooltipContent, TooltipTrigger, } from "../ui/tooltip.js";
16
+ import { AgentManager } from "./AgentManager.js";
17
+ import { ThemeToggle } from "./ThemeToggle.js";
18
+ import { cn } from "../../lib/utils.js";
19
+ export function ChatHeader({ className }) {
20
+ const chat = useChat();
21
+ const cfg = chat.config;
22
+ const ui = cfg.ui ?? {};
23
+ const [agentsOpen, setAgentsOpen] = useState(false);
24
+ const [docsOpen, setDocsOpen] = useState(false);
25
+ const [managerOpen, setManagerOpen] = useState(false);
26
+ const [managerCreate, setManagerCreate] = useState(false);
27
+ const [activeAgentId, setActiveAgent] = useState(() => getActiveAgentId());
28
+ const agents = listAgents();
29
+ const activeAgent = agents.find((a) => a.id === activeAgentId) ??
30
+ agents.find((a) => a.name === cfg.personality?.name);
31
+ // ui.* visibility flags the user can toggle from the playground form.
32
+ // Default every chip to visible so out-of-the-box behaviour matches the
33
+ // previous always-render header. Setting any of these to false hides the
34
+ // corresponding control cluster; the brand mark, new-chat button, theme
35
+ // toggle, and overflow menu stay always-on.
36
+ const showModelSelector = ui.showModelSelector ?? true;
37
+ const showDocumentPicker = ui.showDocumentPicker ?? true;
38
+ const showToolCalls = ui.showToolCalls ?? true;
39
+ const enableConversationHistory = ui.enableConversationHistory ?? true;
40
+ const applyAgent = (agent) => {
41
+ chat.updateConfig({
42
+ systemPrompt: agent.systemPrompt,
43
+ personality: {
44
+ ...cfg.personality,
45
+ name: agent.name,
46
+ tone: agent.tone,
47
+ avatar: agent.icon,
48
+ ...(agent.locale ? { locale: agent.locale } : {}),
49
+ },
50
+ ui: {
51
+ ...ui,
52
+ title: agent.name,
53
+ ...(agent.suggestions && agent.suggestions.length > 0
54
+ ? { suggestions: agent.suggestions }
55
+ : {}),
56
+ },
57
+ });
58
+ setActiveAgentId(agent.id);
59
+ setActiveAgent(agent.id);
60
+ setAgentsOpen(false);
61
+ };
62
+ const openManager = (create) => {
63
+ setManagerCreate(create);
64
+ setManagerOpen(true);
65
+ setAgentsOpen(false);
66
+ };
67
+ const toggleDoc = (doc) => {
68
+ const current = cfg.documents ?? [];
69
+ const next = current.find((d) => d.id === doc.id)
70
+ ? current.filter((d) => d.id !== doc.id)
71
+ : [...current, doc];
72
+ chat.updateConfig({ documents: next });
73
+ };
74
+ const builtInDocs = [
75
+ {
76
+ id: "built-in:web",
77
+ name: "Web search guidance",
78
+ description: "Cites sources and flags primary vs. aggregated info.",
79
+ alwaysOn: false,
80
+ body: "When unsure, prefer citing authoritative web sources and link to them. Distinguish between primary sources and aggregators.",
81
+ },
82
+ {
83
+ id: "built-in:code",
84
+ name: "Code reviewer",
85
+ description: "Checks bugs, security, perf, readability & test coverage.",
86
+ alwaysOn: false,
87
+ body: "When reviewing code, point out: 1) correctness bugs, 2) security implications, 3) performance, 4) readability, 5) test coverage. Suggest minimal patches.",
88
+ },
89
+ {
90
+ id: "built-in:sql",
91
+ name: "SQL guardrails",
92
+ description: "Blocks unsafe SQL; requires WHERE clauses & parameters.",
93
+ alwaysOn: false,
94
+ body: "Never write destructive SQL without a WHERE clause. Prefer parameterized queries. Always state the table schema when joining.",
95
+ },
96
+ ];
97
+ const docCount = cfg.documents?.length ?? 0;
98
+ const conversations = chat.listConversationsMeta();
99
+ return (_jsxs("header", { className: cn("relative z-20 flex items-center justify-between gap-2 border-b border-border/60 bg-background/70 px-3 py-2.5 backdrop-blur-xl supports-[backdrop-filter]:bg-background/55 sm:px-4", className), children: [_jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [_jsx("span", { className: "relative grid size-9 shrink-0 place-items-center rounded-xl grad-primary text-primary-foreground shadow-sm glow-primary", children: activeAgent?.icon ? (_jsx("span", { className: "text-lg leading-none", children: activeAgent.icon })) : (_jsx(Sparkles, { className: "size-[18px]" })) }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "truncate text-sm font-semibold tracking-tight", children: ui.title ?? "Assistant" }), _jsxs("p", { className: "flex items-center gap-1.5 truncate text-xs text-muted-foreground", children: [_jsx("span", { className: "inline-block size-1.5 shrink-0 rounded-full bg-success shadow-[0_0_6px_currentColor]" }), _jsx("span", { className: "truncate", children: ui.subtitle ?? (cfg.model.label || cfg.model.id || "Ready") })] })] })] }), _jsxs("div", { className: "flex items-center gap-0.5", children: [showModelSelector && (_jsxs(DropdownMenu, { open: agentsOpen, onOpenChange: setAgentsOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", className: "hidden h-8 max-w-[180px] gap-1.5 rounded-lg px-2.5 text-xs font-medium text-muted-foreground hover:text-foreground sm:inline-flex", children: [_jsx("span", { className: "text-sm leading-none", children: activeAgent?.icon ?? "✨" }), _jsx("span", { className: "truncate", children: activeAgent?.name ?? cfg.personality?.name ?? "Agent" }), _jsx(ChevronDown, { className: "size-3 shrink-0 opacity-50" })] }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-64", children: [_jsx(DropdownMenuLabel, { children: "Agents" }), _jsx("div", { className: "max-h-64 overflow-y-auto", children: agents.map((a) => (_jsxs(DropdownMenuItem, { onClick: () => applyAgent(a), className: "gap-2", children: [_jsx("span", { className: "text-base leading-none", children: a.icon ?? "🤖" }), _jsx("span", { className: "min-w-0 flex-1 truncate", children: a.name }), a.id === activeAgent?.id && (_jsx(Check, { className: "size-3.5 shrink-0 text-primary" }))] }, a.id))) }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: () => openManager(true), children: [_jsx(Plus, { className: "size-3.5" }), "New agent"] }), _jsxs(DropdownMenuItem, { onClick: () => openManager(false), children: [_jsx(Settings2, { className: "size-3.5" }), "Manage agents\u2026"] })] })] })), showDocumentPicker && (_jsxs(DropdownMenu, { open: docsOpen, onOpenChange: setDocsOpen, children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "sm", className: "h-8 gap-1.5 rounded-lg px-2.5 text-xs font-medium text-muted-foreground hover:text-foreground", children: [_jsx(FileText, { className: "size-3.5" }), _jsx("span", { className: "hidden sm:inline", children: "Docs" }), docCount > 0 && (_jsx(Badge, { variant: "secondary", className: "ml-0.5 h-4 min-w-4 justify-center rounded-full px-1 text-[9px] tabular-nums", children: docCount }))] }) }) }), _jsx(TooltipContent, { children: "Context documents \u2014 extra instructions prepended to the model's system prompt" })] }), _jsxs(DropdownMenuContent, { align: "end", className: "w-72", children: [_jsx(DropdownMenuLabel, { children: "Context documents" }), builtInDocs.map((d) => {
100
+ const active = (cfg.documents ?? []).some((x) => x.id === d.id);
101
+ return (_jsx(DropdownMenuItem, { onClick: () => toggleDoc(d), className: "items-start gap-2", children: _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("span", { className: "flex items-center gap-2 truncate", children: [d.name, active && (_jsx(Badge, { variant: "secondary", className: "ml-auto text-[9px]", children: "on" }))] }), d.description && (_jsx("p", { className: "mt-0.5 truncate text-xs text-muted-foreground", children: d.description }))] }) }, d.id));
102
+ }), _jsx(DropdownMenuSeparator, {}), _jsx("p", { className: "px-2 py-1.5 text-xs text-muted-foreground", children: "Toggle a doc to prepend it to the model's context." })] })] })), _jsx("span", { className: "mx-1 hidden h-5 w-px bg-border/70 sm:block" }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", className: "rounded-lg text-muted-foreground hover:text-foreground", "aria-label": "New chat", onClick: () => chat.newConversation(), children: _jsx(MessageSquarePlus, { className: "size-4" }) }) }), _jsx(TooltipContent, { children: "New chat" })] }), enableConversationHistory && (_jsxs(DropdownMenu, { children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", className: "rounded-lg text-muted-foreground hover:text-foreground", "aria-label": "Conversation history", children: _jsx(History, { className: "size-4" }) }) }) }), _jsx(TooltipContent, { children: "History" })] }), _jsxs(DropdownMenuContent, { align: "end", className: "w-72", children: [_jsx(DropdownMenuLabel, { children: "Conversation history" }), _jsx(DropdownMenuSeparator, {}), conversations.length === 0 ? (_jsxs("p", { className: "px-2 py-6 text-center text-xs text-muted-foreground", children: ["No conversations yet.", _jsx("br", {}), "Send a message to start one."] })) : (_jsx("div", { className: "max-h-72 overflow-y-auto", children: conversations.map((c) => {
103
+ const active = c.id === chat.conversationId;
104
+ return (_jsxs("div", { className: cn("group/conv flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors", active ? "bg-accent" : "hover:bg-accent/60"), children: [_jsxs(Button, { type: "button", variant: "ghost", onClick: () => chat.setConversationId(c.id), className: "h-auto min-w-0 flex-1 justify-start gap-2 p-0 text-left font-normal hover:bg-transparent", children: [active ? (_jsx(Check, { className: "size-3.5 shrink-0 text-primary" })) : (_jsx("span", { className: "size-3.5 shrink-0" })), _jsxs("span", { className: "min-w-0 flex-1", children: [_jsx("span", { className: "block truncate font-medium", children: c.title }), _jsxs("span", { className: "block truncate text-[11px] text-muted-foreground", children: [c.messageCount, " msg", c.updatedAt ? ` · ${relativeTime(c.updatedAt)}` : ""] })] })] }), _jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": "Delete conversation", onClick: (e) => {
105
+ e.stopPropagation();
106
+ chat.deleteConversation(c.id);
107
+ }, className: "size-7 shrink-0 text-muted-foreground opacity-0 transition-opacity hover:bg-foreground/10 hover:text-destructive group-hover/conv:opacity-100", children: _jsx(Trash2, { className: "size-3.5" }) })] }, c.id));
108
+ }) }))] })] })), _jsx(ThemeToggle, { className: "rounded-lg text-muted-foreground hover:text-foreground" }), showToolCalls && chat.getActiveToolCallCount() > 0 && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "inline-flex h-7 items-center gap-1 rounded-lg border border-border/60 bg-card/60 px-2 text-xs font-medium text-muted-foreground", "aria-label": `${chat.getActiveToolCallCount()} tool calls in progress`, children: [_jsx(Wrench, { className: "size-3.5 animate-pulse" }), _jsx("span", { className: "font-mono", children: chat.getActiveToolCallCount() })] }) }), _jsxs(TooltipContent, { children: [chat.getActiveToolCallCount(), " tool call", chat.getActiveToolCallCount() === 1 ? "" : "s", " in progress"] })] })), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", className: "rounded-lg text-muted-foreground hover:text-foreground", "aria-label": "More", children: _jsx(MoreHorizontal, { className: "size-4" }) }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsxs(DropdownMenuItem, { onClick: () => openManager(false), children: [_jsx(Settings2, { className: "size-3.5" }), "Manage agents\u2026"] }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { onClick: () => chat.clear(), className: "text-destructive focus:text-destructive", children: [_jsx(Trash2, { className: "size-3.5" }), "Clear this conversation"] })] })] })] }), _jsx(AgentManager, { open: managerOpen, onOpenChange: setManagerOpen, activeAgentId: activeAgent?.id ?? null, onApply: applyAgent, startInCreate: managerCreate })] }));
109
+ }
110
+ function relativeTime(ts) {
111
+ const diff = Date.now() - ts;
112
+ const min = Math.round(diff / 60000);
113
+ if (min < 1)
114
+ return "just now";
115
+ if (min < 60)
116
+ return `${min}m ago`;
117
+ const hr = Math.round(min / 60);
118
+ if (hr < 24)
119
+ return `${hr}h ago`;
120
+ const day = Math.round(hr / 24);
121
+ if (day < 7)
122
+ return `${day}d ago`;
123
+ try {
124
+ return new Date(ts).toLocaleDateString(undefined, {
125
+ month: "short",
126
+ day: "numeric",
127
+ });
128
+ }
129
+ catch {
130
+ return "";
131
+ }
132
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The full chat panel — header + virtualized message list + composer.
3
+ * Drop this into any layout and you get a complete chat experience.
4
+ */
5
+ import type { CSSProperties, ReactNode } from "react";
6
+ import type { ChatConfig } from "../../types/chat.js";
7
+ export interface ChatPanelProps {
8
+ /** Inline config. Mutate at runtime via `useChat().updateConfig`. */
9
+ config: ChatConfig;
10
+ /** Layout. `panel` (default) is a card; `floating` is a fixed corner widget. */
11
+ layout?: "panel" | "floating" | "fullpage";
12
+ /** Override the default header render. */
13
+ renderHeader?: () => ReactNode;
14
+ /** Override the default footer render (above the composer). */
15
+ renderFooter?: () => ReactNode;
16
+ /** Extra className on the outer container. */
17
+ className?: string;
18
+ /** Extra className on the inner content wrapper of both the message list
19
+ * and composer (merged over the default `max-w-3xl` via tailwind-merge).
20
+ * Use this to let the conversation use more horizontal space in wide
21
+ * layouts, e.g. `contentClassName="max-w-5xl"`. */
22
+ contentClassName?: string;
23
+ /** Extra style. */
24
+ style?: CSSProperties;
25
+ /** Hide the header entirely. */
26
+ hideHeader?: boolean;
27
+ /** Hide the composer entirely (render a read-only viewer). */
28
+ hideComposer?: boolean;
29
+ }
30
+ /**
31
+ * One-shot: ChatProvider + TooltipProvider + layout shell + Header +
32
+ * MessageList + Composer. For full control, compose the primitives
33
+ * yourself and only wrap with <ChatProvider config={…} />.
34
+ */
35
+ export declare function ChatPanel(props: ChatPanelProps): ReactNode;
36
+ //# sourceMappingURL=ChatPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatPanel.d.ts","sourceRoot":"","sources":["../../../src/components/chat/ChatPanel.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAMtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGtD,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,MAAM,EAAE,UAAU,CAAC;IACnB,gFAAgF;IAChF,MAAM,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,UAAU,CAAC;IAC3C,0CAA0C;IAC1C,YAAY,CAAC,EAAE,MAAM,SAAS,CAAC;IAC/B,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,SAAS,CAAC;IAC/B,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;wDAGoD;IACpD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,gCAAgC;IAChC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,CA0C1D"}
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { TooltipProvider } from "../ui/tooltip.js";
3
+ import { ChatHeader } from "./ChatHeader.js";
4
+ import { ChatComposer } from "./ChatComposer.js";
5
+ import { MessageList } from "./MessageList.js";
6
+ import { cn } from "../../lib/utils.js";
7
+ import { ChatProvider, useOptionalChat } from "./ChatProvider.js";
8
+ /**
9
+ * One-shot: ChatProvider + TooltipProvider + layout shell + Header +
10
+ * MessageList + Composer. For full control, compose the primitives
11
+ * yourself and only wrap with <ChatProvider config={…} />.
12
+ */
13
+ export function ChatPanel(props) {
14
+ const { config, layout = "panel", renderHeader, renderFooter, className, contentClassName, style, hideHeader = false, hideComposer = false, } = props;
15
+ const containerClasses = layout === "floating"
16
+ ? "fixed bottom-5 right-5 z-40 flex w-[min(420px,calc(100vw-2.5rem))] h-[min(660px,calc(100vh-2.5rem))] flex-col overflow-hidden rounded-2xl surface-elevated app-mesh"
17
+ : layout === "fullpage"
18
+ ? "flex h-full w-full flex-col app-mesh"
19
+ : "flex h-full w-full flex-col overflow-hidden rounded-2xl surface-elevated app-mesh";
20
+ // If a parent already provided a ChatProvider (we are nested), use it
21
+ // and skip the internal one — otherwise the form mutates the outer
22
+ // engine but the header/list/composer inside this panel read from the
23
+ // inner engine, and the two would silently diverge. When there's no
24
+ // parent provider we create one so the panel works standalone too.
25
+ const outer = useOptionalChat();
26
+ const tree = (_jsx(TooltipProvider, { delayDuration: 200, children: _jsx(ChatSurface, { layout: layout, className: className, style: style, renderHeader: renderHeader, renderFooter: renderFooter, contentClassName: contentClassName, hideHeader: hideHeader, hideComposer: hideComposer }) }));
27
+ if (outer)
28
+ return tree;
29
+ return _jsx(ChatProvider, { config: config, children: tree });
30
+ }
31
+ /**
32
+ * ChatSurface — the inner panel tree. Reads `useOptionalChat` so it can
33
+ * apply density/theme/accent/font CSS hooks to its root div based on the
34
+ * active config. Lives in the same file as ChatPanel because it has no
35
+ * value as a standalone component.
36
+ */
37
+ function ChatSurface({ layout, className, style, renderHeader, renderFooter, contentClassName, hideHeader, hideComposer, }) {
38
+ const chat = useOptionalChat();
39
+ const ui = chat?.config.ui ?? {};
40
+ const density = ui.density ?? "comfortable";
41
+ const theme = ui.theme ?? "system";
42
+ const accentColor = ui.accentColor ?? "";
43
+ const fontFamily = ui.fontFamily ?? "";
44
+ const containerClasses = layout === "floating"
45
+ ? "fixed bottom-5 right-5 z-40 flex w-[min(420px,calc(100vw-2.5rem))] h-[min(660px,calc(100vh-2.5rem))] flex-col overflow-hidden rounded-2xl surface-elevated app-mesh"
46
+ : layout === "fullpage"
47
+ ? "flex h-full w-full flex-col app-mesh"
48
+ : "flex h-full w-full flex-col overflow-hidden rounded-2xl surface-elevated app-mesh";
49
+ const inlineVars = {};
50
+ if (accentColor)
51
+ inlineVars["--chat-accent"] = accentColor;
52
+ if (fontFamily)
53
+ inlineVars["--chat-font-family"] = fontFamily;
54
+ return (_jsxs("div", { className: cn(containerClasses, className), style: { ...style, ...inlineVars }, "data-density": density, "data-theme": theme, children: [renderHeader ? (renderHeader()) : hideHeader ? null : (_jsx(ChatHeader, {})), _jsx(MessageList, { contentClassName: contentClassName }), renderFooter ? renderFooter() : null, hideComposer ? null : _jsx(ChatComposer, { contentClassName: contentClassName })] }));
55
+ }
@@ -0,0 +1,58 @@
1
+ import { type ReactNode } from "react";
2
+ import type { AttachmentMeta, ChatConfig, ChatMessage, ConversationMeta } from "../../types/chat.js";
3
+ import { ChatEngine as ChatEngineImpl } from "../../lib/chat-engine.js";
4
+ export interface ChatContextValue {
5
+ engine: ChatEngineImpl;
6
+ config: ChatConfig;
7
+ /**
8
+ * Update the active config. Two overloads:
9
+ * - `updateConfig(partial)` — shallow-merge partial into the current config.
10
+ * - `updateConfig(updater)` — functional form, receives the current
11
+ * config and returns the partial to merge. Use this when the partial
12
+ * needs to read the current config (avoids stale-closure bugs in
13
+ * long-lived form components).
14
+ */
15
+ updateConfig(partial: Partial<ChatConfig>): void;
16
+ updateConfig(updater: (current: ChatConfig) => Partial<ChatConfig>): void;
17
+ messages: ChatMessage[];
18
+ isStreaming: boolean;
19
+ conversationId: string;
20
+ setConversationId: (id: string) => void;
21
+ send: (text: string, attachments?: AttachmentMeta[]) => Promise<void>;
22
+ sendFiles: (text: string, files: File[]) => Promise<void>;
23
+ abort: () => void;
24
+ regenerate: () => Promise<void>;
25
+ editAndResend: (messageId: string, newContent: string) => Promise<void>;
26
+ clear: () => void;
27
+ newConversation: () => string;
28
+ deleteMessage: (messageId: string) => void;
29
+ deleteConversation: (id: string) => void;
30
+ listConversations: () => string[];
31
+ listConversationsMeta: () => ConversationMeta[];
32
+ /** Count of in-flight tool calls for the active conversation. */
33
+ getActiveToolCallCount: () => number;
34
+ }
35
+ export interface ChatProviderProps {
36
+ config: ChatConfig;
37
+ children: ReactNode;
38
+ }
39
+ /**
40
+ * Mount a chat engine for the lifetime of the React subtree. Multiple
41
+ * ChatProviders can coexist; each gets its own engine. The provider
42
+ * uses useSyncExternalStore for concurrent-mode-safe subscriptions.
43
+ */
44
+ export declare function ChatProvider(props: ChatProviderProps): ReactNode;
45
+ /**
46
+ * Hook to access the chat engine. Throws when used outside a ChatProvider.
47
+ */
48
+ export declare function useChat(): ChatContextValue;
49
+ /**
50
+ * Variant of useChat that returns null instead of throwing. Use in
51
+ * components that may render both inside and outside a provider.
52
+ */
53
+ export declare function useOptionalChat(): ChatContextValue | null;
54
+ /**
55
+ * Default config helper for the MiniMax provider.
56
+ */
57
+ export declare function defaultConfig(overrides?: Partial<ChatConfig>): ChatConfig;
58
+ //# sourceMappingURL=ChatProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatProvider.d.ts","sourceRoot":"","sources":["../../../src/components/chat/ChatProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,WAAW,EACX,gBAAgB,EACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,UAAU,IAAI,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAGxE,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,UAAU,CAAC;IACnB;;;;;;;OAOG;IACH,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;IACjD,YAAY,CACV,OAAO,EAAE,CAAC,OAAO,EAAE,UAAU,KAAK,OAAO,CAAC,UAAU,CAAC,GACpD,IAAI,CAAC;IACR,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,cAAc,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,eAAe,EAAE,MAAM,MAAM,CAAC;IAC9B,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,kBAAkB,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,iBAAiB,EAAE,MAAM,MAAM,EAAE,CAAC;IAClC,qBAAqB,EAAE,MAAM,gBAAgB,EAAE,CAAC;IAChD,iEAAiE;IACjE,sBAAsB,EAAE,MAAM,MAAM,CAAC;CACtC;AAID,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,SAAS,CAyJhE;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,gBAAgB,CAM1C;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,gBAAgB,GAAG,IAAI,CAEzD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,SAAS,GAAE,OAAO,CAAC,UAAU,CAAM,GAAG,UAAU,CAE7E"}