@townco/ui 0.1.6 → 0.1.8

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 (255) hide show
  1. package/README.md +11 -11
  2. package/dist/core/hooks/index.d.ts +1 -0
  3. package/dist/core/hooks/index.js +1 -0
  4. package/dist/core/hooks/use-chat-input.d.ts +17 -17
  5. package/dist/core/hooks/use-chat-input.js +55 -64
  6. package/dist/core/hooks/use-chat-messages.d.ts +11 -11
  7. package/dist/core/hooks/use-chat-messages.js +114 -121
  8. package/dist/core/hooks/use-chat-session.d.ts +5 -5
  9. package/dist/core/hooks/use-chat-session.js +80 -78
  10. package/dist/core/hooks/use-media-query.d.ts +5 -5
  11. package/dist/core/hooks/use-media-query.js +42 -38
  12. package/dist/core/index.d.ts +1 -1
  13. package/dist/core/index.js +1 -1
  14. package/dist/core/schemas/chat.d.ts +56 -83
  15. package/dist/core/schemas/chat.js +25 -27
  16. package/dist/core/store/chat-store.d.ts +22 -28
  17. package/dist/core/store/chat-store.js +50 -59
  18. package/dist/gui/components/Button.d.ts +7 -23
  19. package/dist/gui/components/Button.js +27 -40
  20. package/dist/gui/components/Card.d.ts +7 -26
  21. package/dist/gui/components/Card.js +8 -54
  22. package/dist/gui/components/ChatHeader.d.ts +31 -58
  23. package/dist/gui/components/ChatHeader.js +68 -171
  24. package/dist/gui/components/ChatInput.d.ts +54 -58
  25. package/dist/gui/components/ChatInput.js +207 -194
  26. package/dist/gui/components/ChatInputCommandMenu.d.ts +20 -0
  27. package/dist/gui/components/ChatInputCommandMenu.js +62 -0
  28. package/dist/gui/components/ChatLayout.d.ts +41 -71
  29. package/dist/gui/components/ChatLayout.js +87 -214
  30. package/dist/gui/components/ChatPanelTabContent.d.ts +9 -18
  31. package/dist/gui/components/ChatPanelTabContent.js +10 -88
  32. package/dist/gui/components/ChatSecondaryPanel.d.ts +11 -14
  33. package/dist/gui/components/ChatSecondaryPanel.js +38 -115
  34. package/dist/gui/components/ChatSidebar.d.ts +13 -26
  35. package/dist/gui/components/ChatSidebar.js +14 -48
  36. package/dist/gui/components/ChatStatus.d.ts +2 -4
  37. package/dist/gui/components/ChatStatus.js +34 -45
  38. package/dist/gui/components/Conversation.d.ts +14 -17
  39. package/dist/gui/components/Conversation.js +83 -143
  40. package/dist/gui/components/Dialog.d.ts +11 -57
  41. package/dist/gui/components/Dialog.js +8 -84
  42. package/dist/gui/components/DropdownMenu.d.ts +20 -101
  43. package/dist/gui/components/DropdownMenu.js +14 -161
  44. package/dist/gui/components/HeightTransition.d.ts +7 -12
  45. package/dist/gui/components/HeightTransition.js +77 -88
  46. package/dist/gui/components/Input.d.ts +6 -13
  47. package/dist/gui/components/Input.js +16 -27
  48. package/dist/gui/components/Label.d.ts +1 -7
  49. package/dist/gui/components/Label.js +2 -12
  50. package/dist/gui/components/MarkdownRenderer.d.ts +4 -6
  51. package/dist/gui/components/MarkdownRenderer.js +81 -178
  52. package/dist/gui/components/Message.d.ts +22 -25
  53. package/dist/gui/components/Message.js +97 -44
  54. package/dist/gui/components/MessageContent.d.ts +22 -29
  55. package/dist/gui/components/MessageContent.js +85 -157
  56. package/dist/gui/components/Reasoning.d.ts +24 -30
  57. package/dist/gui/components/Reasoning.js +60 -187
  58. package/dist/gui/components/Response.d.ts +9 -11
  59. package/dist/gui/components/Response.js +90 -229
  60. package/dist/gui/components/Select.d.ts +10 -69
  61. package/dist/gui/components/Select.js +12 -118
  62. package/dist/gui/components/Sonner.d.ts +1 -3
  63. package/dist/gui/components/Sonner.js +18 -29
  64. package/dist/gui/components/Tabs.d.ts +4 -24
  65. package/dist/gui/components/Tabs.js +4 -32
  66. package/dist/gui/components/Task.d.ts +24 -28
  67. package/dist/gui/components/Task.js +31 -164
  68. package/dist/gui/components/Textarea.d.ts +7 -15
  69. package/dist/gui/components/Textarea.js +46 -63
  70. package/dist/gui/components/ThinkingBlock.d.ts +10 -20
  71. package/dist/gui/components/ThinkingBlock.js +35 -134
  72. package/dist/gui/components/TodoList.d.ts +10 -12
  73. package/dist/gui/components/TodoList.js +7 -22
  74. package/dist/gui/components/TodoListItem.d.ts +6 -9
  75. package/dist/gui/components/TodoListItem.js +4 -18
  76. package/dist/gui/components/index.d.ts +16 -59
  77. package/dist/gui/components/index.js +18 -42
  78. package/dist/gui/lib/utils.js +1 -1
  79. package/dist/index.d.ts +1 -1
  80. package/dist/index.js +1 -1
  81. package/dist/index.test.js +1 -0
  82. package/dist/sdk/client/acp-client.d.ts +76 -88
  83. package/dist/sdk/client/acp-client.js +217 -215
  84. package/dist/sdk/index.d.ts +1 -1
  85. package/dist/sdk/index.js +1 -1
  86. package/dist/sdk/schemas/agent.d.ts +64 -111
  87. package/dist/sdk/schemas/agent.js +24 -24
  88. package/dist/sdk/schemas/message.d.ts +147 -245
  89. package/dist/sdk/schemas/message.js +40 -40
  90. package/dist/sdk/schemas/session.d.ts +135 -219
  91. package/dist/sdk/schemas/session.js +27 -27
  92. package/dist/sdk/transports/http.d.ts +55 -55
  93. package/dist/sdk/transports/http.js +469 -472
  94. package/dist/sdk/transports/stdio.d.ts +20 -20
  95. package/dist/sdk/transports/stdio.js +286 -289
  96. package/dist/sdk/transports/types.d.ts +42 -42
  97. package/dist/sdk/transports/websocket.d.ts +12 -12
  98. package/dist/sdk/transports/websocket.js +46 -52
  99. package/dist/tui/components/ChatView.d.ts +2 -4
  100. package/dist/tui/components/ChatView.js +18 -51
  101. package/dist/tui/components/GameOfLife.js +35 -64
  102. package/dist/tui/components/InputBox.d.ts +11 -18
  103. package/dist/tui/components/InputBox.js +10 -70
  104. package/dist/tui/components/MessageList.d.ts +2 -4
  105. package/dist/tui/components/MessageList.js +10 -37
  106. package/dist/tui/components/MultiSelect.d.ts +10 -15
  107. package/dist/tui/components/MultiSelect.js +73 -116
  108. package/dist/tui/components/ReadlineInput.d.ts +6 -12
  109. package/dist/tui/components/ReadlineInput.js +237 -252
  110. package/dist/tui/components/SingleSelect.d.ts +9 -15
  111. package/dist/tui/components/SingleSelect.js +43 -84
  112. package/dist/tui/components/StatusBar.d.ts +6 -11
  113. package/dist/tui/components/StatusBar.js +67 -102
  114. package/dist/tui/index.d.ts +1 -1
  115. package/dist/tui/index.js +1 -1
  116. package/package.json +6 -4
  117. package/src/styles/global.css +2 -0
  118. package/dist/core/hooks/index.d.ts.map +0 -1
  119. package/dist/core/hooks/index.js.map +0 -1
  120. package/dist/core/hooks/use-chat-input.d.ts.map +0 -1
  121. package/dist/core/hooks/use-chat-input.js.map +0 -1
  122. package/dist/core/hooks/use-chat-messages.d.ts.map +0 -1
  123. package/dist/core/hooks/use-chat-messages.js.map +0 -1
  124. package/dist/core/hooks/use-chat-session.d.ts.map +0 -1
  125. package/dist/core/hooks/use-chat-session.js.map +0 -1
  126. package/dist/core/index.d.ts.map +0 -1
  127. package/dist/core/index.js.map +0 -1
  128. package/dist/core/schemas/chat.d.ts.map +0 -1
  129. package/dist/core/schemas/chat.js.map +0 -1
  130. package/dist/core/schemas/index.d.ts.map +0 -1
  131. package/dist/core/schemas/index.js.map +0 -1
  132. package/dist/core/store/chat-store.d.ts.map +0 -1
  133. package/dist/core/store/chat-store.js.map +0 -1
  134. package/dist/gui/components/Button.d.ts.map +0 -1
  135. package/dist/gui/components/Button.js.map +0 -1
  136. package/dist/gui/components/Card.d.ts.map +0 -1
  137. package/dist/gui/components/Card.js.map +0 -1
  138. package/dist/gui/components/ChatInput.d.ts.map +0 -1
  139. package/dist/gui/components/ChatInput.js.map +0 -1
  140. package/dist/gui/components/ChatInterface.d.ts +0 -12
  141. package/dist/gui/components/ChatInterface.d.ts.map +0 -1
  142. package/dist/gui/components/ChatInterface.js +0 -204
  143. package/dist/gui/components/ChatInterface.js.map +0 -1
  144. package/dist/gui/components/ChatPreview.d.ts +0 -12
  145. package/dist/gui/components/ChatPreview.d.ts.map +0 -1
  146. package/dist/gui/components/ChatPreview.js +0 -214
  147. package/dist/gui/components/ChatPreview.js.map +0 -1
  148. package/dist/gui/components/ChatSecondaryPanel.d.ts.map +0 -1
  149. package/dist/gui/components/ChatSecondaryPanel.js.map +0 -1
  150. package/dist/gui/components/ChatStatus.d.ts.map +0 -1
  151. package/dist/gui/components/ChatStatus.js.map +0 -1
  152. package/dist/gui/components/ChatView.d.ts +0 -8
  153. package/dist/gui/components/ChatView.d.ts.map +0 -1
  154. package/dist/gui/components/ChatView.js +0 -42
  155. package/dist/gui/components/ChatView.js.map +0 -1
  156. package/dist/gui/components/ConfigPanel.d.ts +0 -20
  157. package/dist/gui/components/ConfigPanel.d.ts.map +0 -1
  158. package/dist/gui/components/ConfigPanel.js +0 -225
  159. package/dist/gui/components/ConfigPanel.js.map +0 -1
  160. package/dist/gui/components/Conversation.d.ts.map +0 -1
  161. package/dist/gui/components/Conversation.js.map +0 -1
  162. package/dist/gui/components/Dialog.d.ts.map +0 -1
  163. package/dist/gui/components/Dialog.js.map +0 -1
  164. package/dist/gui/components/HeightTransition.d.ts.map +0 -1
  165. package/dist/gui/components/HeightTransition.js.map +0 -1
  166. package/dist/gui/components/Input.d.ts.map +0 -1
  167. package/dist/gui/components/Input.js.map +0 -1
  168. package/dist/gui/components/InputBox.d.ts +0 -21
  169. package/dist/gui/components/InputBox.d.ts.map +0 -1
  170. package/dist/gui/components/InputBox.js +0 -90
  171. package/dist/gui/components/InputBox.js.map +0 -1
  172. package/dist/gui/components/Label.d.ts.map +0 -1
  173. package/dist/gui/components/Label.js.map +0 -1
  174. package/dist/gui/components/MarkdownRenderer.d.ts.map +0 -1
  175. package/dist/gui/components/MarkdownRenderer.js.map +0 -1
  176. package/dist/gui/components/Message.d.ts.map +0 -1
  177. package/dist/gui/components/Message.js.map +0 -1
  178. package/dist/gui/components/MessageContent.d.ts.map +0 -1
  179. package/dist/gui/components/MessageContent.js.map +0 -1
  180. package/dist/gui/components/MessageList.d.ts.map +0 -1
  181. package/dist/gui/components/MessageList.js.map +0 -1
  182. package/dist/gui/components/PlaygroundLayout.d.ts +0 -14
  183. package/dist/gui/components/PlaygroundLayout.d.ts.map +0 -1
  184. package/dist/gui/components/PlaygroundLayout.js +0 -49
  185. package/dist/gui/components/PlaygroundLayout.js.map +0 -1
  186. package/dist/gui/components/Reasoning.d.ts.map +0 -1
  187. package/dist/gui/components/Reasoning.js.map +0 -1
  188. package/dist/gui/components/Response.d.ts.map +0 -1
  189. package/dist/gui/components/Response.js.map +0 -1
  190. package/dist/gui/components/Select.d.ts.map +0 -1
  191. package/dist/gui/components/Select.js.map +0 -1
  192. package/dist/gui/components/StatusBar.d.ts +0 -12
  193. package/dist/gui/components/StatusBar.d.ts.map +0 -1
  194. package/dist/gui/components/StatusBar.js +0 -58
  195. package/dist/gui/components/StatusBar.js.map +0 -1
  196. package/dist/gui/components/Tabs.d.ts.map +0 -1
  197. package/dist/gui/components/Tabs.js.map +0 -1
  198. package/dist/gui/components/Task.d.ts.map +0 -1
  199. package/dist/gui/components/Task.js.map +0 -1
  200. package/dist/gui/components/Textarea.d.ts.map +0 -1
  201. package/dist/gui/components/Textarea.js.map +0 -1
  202. package/dist/gui/components/ThinkingBlock.d.ts.map +0 -1
  203. package/dist/gui/components/ThinkingBlock.js.map +0 -1
  204. package/dist/gui/components/TodoList.d.ts.map +0 -1
  205. package/dist/gui/components/TodoList.js.map +0 -1
  206. package/dist/gui/components/TodoListItem.d.ts.map +0 -1
  207. package/dist/gui/components/TodoListItem.js.map +0 -1
  208. package/dist/gui/components/index.d.ts.map +0 -1
  209. package/dist/gui/components/index.js.map +0 -1
  210. package/dist/gui/index.d.ts.map +0 -1
  211. package/dist/gui/index.js.map +0 -1
  212. package/dist/gui/lib/utils.d.ts.map +0 -1
  213. package/dist/gui/lib/utils.js.map +0 -1
  214. package/dist/index.d.ts.map +0 -1
  215. package/dist/index.js.map +0 -1
  216. package/dist/sdk/client/acp-client.d.ts.map +0 -1
  217. package/dist/sdk/client/acp-client.js.map +0 -1
  218. package/dist/sdk/client/index.d.ts.map +0 -1
  219. package/dist/sdk/client/index.js.map +0 -1
  220. package/dist/sdk/index.d.ts.map +0 -1
  221. package/dist/sdk/index.js.map +0 -1
  222. package/dist/sdk/schemas/agent.d.ts.map +0 -1
  223. package/dist/sdk/schemas/agent.js.map +0 -1
  224. package/dist/sdk/schemas/index.d.ts.map +0 -1
  225. package/dist/sdk/schemas/index.js.map +0 -1
  226. package/dist/sdk/schemas/message.d.ts.map +0 -1
  227. package/dist/sdk/schemas/message.js.map +0 -1
  228. package/dist/sdk/schemas/session.d.ts.map +0 -1
  229. package/dist/sdk/schemas/session.js.map +0 -1
  230. package/dist/sdk/transports/http.d.ts.map +0 -1
  231. package/dist/sdk/transports/http.js.map +0 -1
  232. package/dist/sdk/transports/index.d.ts.map +0 -1
  233. package/dist/sdk/transports/index.js.map +0 -1
  234. package/dist/sdk/transports/stdio.d.ts.map +0 -1
  235. package/dist/sdk/transports/stdio.js.map +0 -1
  236. package/dist/sdk/transports/types.d.ts.map +0 -1
  237. package/dist/sdk/transports/types.js.map +0 -1
  238. package/dist/sdk/transports/websocket.d.ts.map +0 -1
  239. package/dist/sdk/transports/websocket.js.map +0 -1
  240. package/dist/tui/components/ChatView.d.ts.map +0 -1
  241. package/dist/tui/components/ChatView.js.map +0 -1
  242. package/dist/tui/components/GameOfLife.d.ts.map +0 -1
  243. package/dist/tui/components/GameOfLife.js.map +0 -1
  244. package/dist/tui/components/InputBox.d.ts.map +0 -1
  245. package/dist/tui/components/InputBox.js.map +0 -1
  246. package/dist/tui/components/MessageList.d.ts.map +0 -1
  247. package/dist/tui/components/MessageList.js.map +0 -1
  248. package/dist/tui/components/ReadlineInput.d.ts.map +0 -1
  249. package/dist/tui/components/ReadlineInput.js.map +0 -1
  250. package/dist/tui/components/StatusBar.d.ts.map +0 -1
  251. package/dist/tui/components/StatusBar.js.map +0 -1
  252. package/dist/tui/components/index.d.ts.map +0 -1
  253. package/dist/tui/components/index.js.map +0 -1
  254. package/dist/tui/index.d.ts.map +0 -1
  255. package/dist/tui/index.js.map +0 -1
@@ -1,207 +1,220 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
1
2
  import { Slot } from "@radix-ui/react-slot";
3
+ import { Mic, Paperclip, SquareSlash } from "lucide-react";
2
4
  import * as React from "react";
3
- import { jsx as _jsx } from "react/jsx-runtime";
4
5
  import { useChatInput as useCoreChatInput } from "../../core/hooks/use-chat-input.js";
5
6
  import { useChatStore } from "../../core/store/chat-store.js";
6
7
  import { cn } from "../lib/utils.js";
7
8
  import { Button } from "./Button.js";
8
-
9
+ import { ChatInputCommandMenu as ChatInputCommandMenuComponent, } from "./ChatInputCommandMenu.js";
9
10
  const ChatInputContext = React.createContext(undefined);
10
11
  const useChatInputContext = () => {
11
- const context = React.useContext(ChatInputContext);
12
- if (!context) {
13
- throw new Error("ChatInput components must be used within ChatInput.Root");
14
- }
15
- return context;
12
+ const context = React.useContext(ChatInputContext);
13
+ if (!context) {
14
+ throw new Error("ChatInput components must be used within ChatInput.Root");
15
+ }
16
+ return context;
16
17
  };
17
- const ChatInputRoot = React.forwardRef(
18
- (
19
- {
20
- client,
21
- value: valueProp,
22
- onChange: onChangeProp,
23
- onSubmit: onSubmitProp,
24
- disabled = false,
25
- isSubmitting: isSubmittingProp,
26
- submitOnEnter = true,
27
- className,
28
- children,
29
- ...props
30
- },
31
- ref,
32
- ) => {
33
- const textareaRef = React.useRef(null);
34
- // Always call hooks unconditionally (React rules)
35
- const hookData = useCoreChatInput(client ?? null);
36
- const storeIsStreaming = useChatStore((state) => state.isStreaming);
37
- // Choose data source based on whether client is provided
38
- const value = hookData ? hookData.value : valueProp || "";
39
- const onChange = hookData ? hookData.onChange : onChangeProp || (() => {});
40
- const onSubmit = hookData
41
- ? hookData.onSubmit
42
- : onSubmitProp || (async () => {});
43
- const isSubmitting = hookData
44
- ? hookData.isSubmitting || storeIsStreaming
45
- : isSubmittingProp || false;
46
- const handleSubmit = async (e) => {
47
- e.preventDefault();
48
- if (value.trim() && !isSubmitting && !disabled) {
49
- await onSubmit();
50
- // Restore focus to textarea after submit
51
- setTimeout(() => {
52
- textareaRef.current?.focus();
53
- }, 0);
54
- }
55
- };
56
- // Expose textarea ref to children via context
57
- React.useEffect(() => {
58
- const textarea = document.querySelector('textarea[name="chat-input"]');
59
- if (textarea && textareaRef.current !== textarea) {
60
- textareaRef.current = textarea;
61
- }
62
- }, []);
63
- // Reset textarea height when value becomes empty (after submit)
64
- React.useEffect(() => {
65
- if (!value && textareaRef.current) {
66
- textareaRef.current.style.height = "auto";
67
- textareaRef.current.style.overflowY = "hidden";
68
- }
69
- }, [value]);
70
- return _jsx(ChatInputContext.Provider, {
71
- value: {
72
- value,
73
- onChange,
74
- onSubmit,
75
- disabled,
76
- isSubmitting,
77
- submitOnEnter,
78
- },
79
- children: _jsx("form", {
80
- ref: ref,
81
- onSubmit: handleSubmit,
82
- className: cn(
83
- "w-full divide-y overflow-hidden rounded-xl border bg-background shadow-sm",
84
- className,
85
- ),
86
- ...props,
87
- children: children,
88
- }),
89
- });
90
- },
91
- );
18
+ const ChatInputRoot = React.forwardRef(({ client, value: valueProp, onChange: onChangeProp, onSubmit: onSubmitProp, disabled = false, isSubmitting: isSubmittingProp, submitOnEnter = true, className, children, ...props }, ref) => {
19
+ const textareaRef = React.useRef(null);
20
+ // Always call hooks unconditionally (React rules)
21
+ const hookData = useCoreChatInput(client ?? null);
22
+ const storeIsStreaming = useChatStore((state) => state.isStreaming);
23
+ // Choose data source based on whether client is provided
24
+ const value = hookData ? hookData.value : valueProp || "";
25
+ const onChange = hookData ? hookData.onChange : onChangeProp || (() => { });
26
+ const onSubmit = hookData
27
+ ? hookData.onSubmit
28
+ : onSubmitProp || (async () => { });
29
+ const isSubmitting = hookData
30
+ ? hookData.isSubmitting || storeIsStreaming
31
+ : isSubmittingProp || false;
32
+ // Command menu state
33
+ const [showCommandMenu, setShowCommandMenu] = React.useState(false);
34
+ const [commandMenuQuery, setCommandMenuQuery] = React.useState("");
35
+ const [selectedMenuIndex, setSelectedMenuIndex] = React.useState(0);
36
+ const [menuItemCount, setMenuItemCount] = React.useState(0);
37
+ const [triggerCounter, setTriggerCounter] = React.useState(0);
38
+ const triggerMenuSelect = React.useCallback(() => {
39
+ setTriggerCounter((prev) => prev + 1);
40
+ }, []);
41
+ const handleSubmit = async (e) => {
42
+ e.preventDefault();
43
+ if (value.trim() && !isSubmitting && !disabled) {
44
+ await onSubmit();
45
+ // Restore focus to textarea after submit
46
+ setTimeout(() => {
47
+ textareaRef.current?.focus();
48
+ }, 0);
49
+ }
50
+ };
51
+ // Expose textarea ref to children via context
52
+ React.useEffect(() => {
53
+ const textarea = document.querySelector('textarea[name="chat-input"]');
54
+ if (textarea && textareaRef.current !== textarea) {
55
+ textareaRef.current = textarea;
56
+ }
57
+ }, []);
58
+ // Reset textarea height when value becomes empty (after submit)
59
+ React.useEffect(() => {
60
+ if (!value && textareaRef.current) {
61
+ textareaRef.current.style.height = "auto";
62
+ textareaRef.current.style.overflowY = "hidden";
63
+ }
64
+ }, [value]);
65
+ return (_jsx(ChatInputContext.Provider, { value: {
66
+ value,
67
+ onChange,
68
+ onSubmit,
69
+ disabled,
70
+ isSubmitting,
71
+ submitOnEnter,
72
+ showCommandMenu,
73
+ setShowCommandMenu,
74
+ commandMenuQuery,
75
+ setCommandMenuQuery,
76
+ selectedMenuIndex,
77
+ setSelectedMenuIndex,
78
+ menuItemCount,
79
+ setMenuItemCount,
80
+ triggerMenuSelect,
81
+ triggerCounter,
82
+ }, children: _jsx("form", { ref: ref, onSubmit: handleSubmit, className: cn("relative w-full divide-y rounded-xl border bg-background shadow-md", className), ...props, children: children }) }));
83
+ });
92
84
  ChatInputRoot.displayName = "ChatInput.Root";
93
- const ChatInputField = React.forwardRef(
94
- ({ asChild = false, className, onKeyDown, children, ...props }, ref) => {
95
- const { value, onChange, onSubmit, disabled, isSubmitting, submitOnEnter } =
96
- useChatInputContext();
97
- const textareaRef = React.useRef(null);
98
- const handleRef = React.useCallback(
99
- (node) => {
100
- textareaRef.current = node;
101
- if (typeof ref === "function") {
102
- ref(node);
103
- } else if (ref) {
104
- ref.current = node;
105
- }
106
- },
107
- [ref],
108
- );
109
- const handleKeyDown = (e) => {
110
- // Handle Enter without Shift - only submit if not already submitting
111
- if (submitOnEnter && e.key === "Enter" && !e.shiftKey) {
112
- // Only prevent default and submit if conditions are met
113
- if (value.trim() && !isSubmitting && !disabled) {
114
- e.preventDefault();
115
- onSubmit();
116
- } else if (isSubmitting || disabled) {
117
- // Block Enter when submitting or disabled - don't add newline
118
- e.preventDefault();
119
- }
120
- }
121
- onKeyDown?.(e);
122
- };
123
- const handleChange = (e) => {
124
- onChange(e.target.value);
125
- // Auto-resize
126
- const textarea = textareaRef.current;
127
- if (!textarea) return;
128
- textarea.style.height = "auto";
129
- const newHeight = Math.min(textarea.scrollHeight, 164);
130
- textarea.style.height = `${newHeight}px`;
131
- if (textarea.scrollHeight > 164) {
132
- textarea.style.overflowY = "auto";
133
- } else {
134
- textarea.style.overflowY = "hidden";
135
- }
136
- };
137
- const fieldProps = {
138
- ref: handleRef,
139
- name: "chat-input",
140
- value,
141
- onChange: handleChange,
142
- onKeyDown: handleKeyDown,
143
- disabled: disabled,
144
- ...props,
145
- };
146
- if (asChild && React.isValidElement(children)) {
147
- return React.cloneElement(children, fieldProps);
148
- }
149
- return _jsx("textarea", {
150
- ...fieldProps,
151
- className: cn(
152
- "w-full resize-none rounded-none border-none p-3 shadow-none",
153
- "outline-none ring-0 field-sizing-content max-h-[6lh]",
154
- "bg-transparent dark:bg-transparent focus-visible:ring-0",
155
- "text-sm placeholder:text-muted-foreground",
156
- "disabled:cursor-not-allowed disabled:opacity-50",
157
- className,
158
- ),
159
- });
160
- },
161
- );
85
+ const ChatInputField = React.forwardRef(({ asChild = false, className, onKeyDown, children, ...props }, ref) => {
86
+ const { value, onChange, onSubmit, disabled, isSubmitting, submitOnEnter, showCommandMenu, setShowCommandMenu, setCommandMenuQuery, setSelectedMenuIndex, menuItemCount, triggerMenuSelect, } = useChatInputContext();
87
+ const textareaRef = React.useRef(null);
88
+ const handleRef = React.useCallback((node) => {
89
+ textareaRef.current = node;
90
+ if (typeof ref === "function") {
91
+ ref(node);
92
+ }
93
+ else if (ref) {
94
+ ref.current = node;
95
+ }
96
+ }, [ref]);
97
+ const handleKeyDown = (e) => {
98
+ // Handle arrow keys and Enter when command menu is open
99
+ if (showCommandMenu && menuItemCount > 0) {
100
+ if (e.key === "ArrowDown") {
101
+ e.preventDefault();
102
+ setSelectedMenuIndex((prev) => (prev + 1) % menuItemCount);
103
+ return;
104
+ }
105
+ else if (e.key === "ArrowUp") {
106
+ e.preventDefault();
107
+ setSelectedMenuIndex((prev) => (prev - 1 + menuItemCount) % menuItemCount);
108
+ return;
109
+ }
110
+ else if (e.key === "Enter" && !e.shiftKey) {
111
+ e.preventDefault();
112
+ triggerMenuSelect();
113
+ return;
114
+ }
115
+ else if (e.key === "Escape") {
116
+ e.preventDefault();
117
+ setShowCommandMenu(false);
118
+ setCommandMenuQuery("");
119
+ return;
120
+ }
121
+ }
122
+ // Handle Enter without Shift - only submit if not already submitting
123
+ if (submitOnEnter && e.key === "Enter" && !e.shiftKey) {
124
+ // Only prevent default and submit if conditions are met
125
+ if (value.trim() && !isSubmitting && !disabled) {
126
+ e.preventDefault();
127
+ onSubmit();
128
+ }
129
+ else if (isSubmitting || disabled) {
130
+ // Block Enter when submitting or disabled - don't add newline
131
+ e.preventDefault();
132
+ }
133
+ }
134
+ onKeyDown?.(e);
135
+ };
136
+ const handleChange = (e) => {
137
+ const newValue = e.target.value;
138
+ onChange(newValue);
139
+ // Check if user typed "/" at the start to trigger command menu
140
+ if (newValue.startsWith("/") && !newValue.includes("\n")) {
141
+ setShowCommandMenu(true);
142
+ // Extract search query (everything after "/")
143
+ const query = newValue.slice(1);
144
+ setCommandMenuQuery(query);
145
+ }
146
+ else {
147
+ setShowCommandMenu(false);
148
+ setCommandMenuQuery("");
149
+ }
150
+ // Auto-resize
151
+ const textarea = textareaRef.current;
152
+ if (!textarea)
153
+ return;
154
+ textarea.style.height = "auto";
155
+ const newHeight = Math.min(textarea.scrollHeight, 164);
156
+ textarea.style.height = `${newHeight}px`;
157
+ if (textarea.scrollHeight > 164) {
158
+ textarea.style.overflowY = "auto";
159
+ }
160
+ else {
161
+ textarea.style.overflowY = "hidden";
162
+ }
163
+ };
164
+ const fieldProps = {
165
+ ref: handleRef,
166
+ name: "chat-input",
167
+ value,
168
+ onChange: handleChange,
169
+ onKeyDown: handleKeyDown,
170
+ disabled: disabled,
171
+ ...props,
172
+ };
173
+ if (asChild && React.isValidElement(children)) {
174
+ return React.cloneElement(children, fieldProps);
175
+ }
176
+ return (_jsx("textarea", { ...fieldProps, className: cn("w-full resize-none rounded-none border-none p-4 shadow-none", "outline-none ring-0 field-sizing-content max-h-[6lh]", "bg-transparent dark:bg-transparent focus-visible:ring-0", "text-sm placeholder:text-muted-foreground", "disabled:cursor-not-allowed disabled:opacity-50", className) }));
177
+ });
162
178
  ChatInputField.displayName = "ChatInput.Field";
163
- const ChatInputSubmit = React.forwardRef(
164
- (
165
- { asChild = false, className, disabled: disabledProp, children, ...props },
166
- ref,
167
- ) => {
168
- const { value, disabled, isSubmitting } = useChatInputContext();
169
- const isDisabled =
170
- disabledProp || disabled || isSubmitting || !value.trim();
171
- const Comp = asChild ? Slot : Button;
172
- return _jsx(Comp, {
173
- ref: ref,
174
- type: "submit",
175
- disabled: isDisabled,
176
- size: "icon",
177
- className: cn(
178
- !asChild &&
179
- "gap-1.5 rounded-lg bg-transparent text-foreground hover:bg-transparent",
180
- className,
181
- ),
182
- ...props,
183
- children: children,
184
- });
185
- },
186
- );
179
+ const ChatInputSubmit = React.forwardRef(({ asChild = false, className, disabled: disabledProp, children, ...props }, ref) => {
180
+ const { value, disabled, isSubmitting } = useChatInputContext();
181
+ const isDisabled = disabledProp || disabled || isSubmitting || !value.trim();
182
+ const Comp = asChild ? Slot : Button;
183
+ return (_jsx(Comp, { ref: ref, type: "submit", disabled: isDisabled, variant: !asChild ? "default" : undefined, size: "icon", className: cn(!asChild && "gap-1.5 rounded-full", className), ...props, children: children }));
184
+ });
187
185
  ChatInputSubmit.displayName = "ChatInput.Submit";
188
- const ChatInputToolbar = React.forwardRef(
189
- ({ className, children, ...props }, ref) => {
190
- return _jsx("div", {
191
- ref: ref,
192
- className: cn("flex items-center justify-between p-1", className),
193
- ...props,
194
- children: children,
195
- });
196
- },
197
- );
186
+ const ChatInputToolbar = React.forwardRef(({ className, children, ...props }, ref) => {
187
+ return (_jsx("div", { ref: ref, className: cn("flex items-center justify-between p-2", className), ...props, children: children }));
188
+ });
198
189
  ChatInputToolbar.displayName = "ChatInput.Toolbar";
199
- /* -------------------------------------------------------------------------------------------------
200
- * Exports
201
- * -----------------------------------------------------------------------------------------------*/
202
- export {
203
- ChatInputRoot as Root,
204
- ChatInputField as Field,
205
- ChatInputSubmit as Submit,
206
- ChatInputToolbar as Toolbar,
207
- };
190
+ const ChatInputActions = React.forwardRef(({ asChild = false, className, children, onClick, ...props }, ref) => {
191
+ const { value, onChange, setShowCommandMenu, setCommandMenuQuery } = useChatInputContext();
192
+ const handleClick = (e) => {
193
+ // Trigger command menu by setting "/" in the input
194
+ if (!value.startsWith("/")) {
195
+ onChange("/");
196
+ setShowCommandMenu(true);
197
+ setCommandMenuQuery("");
198
+ }
199
+ onClick?.(e);
200
+ };
201
+ const Comp = asChild ? Slot : Button;
202
+ return (_jsx(Comp, { ref: ref, type: "button", variant: "ghost", size: "icon", className: cn("rounded-full", className), onClick: handleClick, ...props, children: children || _jsx(SquareSlash, { className: "size-4" }) }));
203
+ });
204
+ ChatInputActions.displayName = "ChatInput.Actions";
205
+ const ChatInputAttachment = React.forwardRef(({ asChild = false, className, children, ...props }, ref) => {
206
+ const Comp = asChild ? Slot : Button;
207
+ return (_jsx(Comp, { ref: ref, type: "button", variant: "ghost", size: "icon", className: cn("rounded-full", className), ...props, children: children || _jsx(Paperclip, { className: "size-4" }) }));
208
+ });
209
+ ChatInputAttachment.displayName = "ChatInput.Attachment";
210
+ const ChatInputVoiceInput = React.forwardRef(({ asChild = false, className, children, ...props }, ref) => {
211
+ const Comp = asChild ? Slot : Button;
212
+ return (_jsx(Comp, { ref: ref, type: "button", variant: "ghost", size: "icon", className: cn("rounded-full", className), ...props, children: children || _jsx(Mic, { className: "size-4" }) }));
213
+ });
214
+ ChatInputVoiceInput.displayName = "ChatInput.VoiceInput";
215
+ const ChatInputCommandMenu = React.forwardRef(({ commands = [], className, onChange: _, ...props }, ref) => {
216
+ const { showCommandMenu, commandMenuQuery, selectedMenuIndex, setSelectedMenuIndex, setMenuItemCount, triggerCounter, onChange, } = useChatInputContext();
217
+ return (_jsx(ChatInputCommandMenuComponent, { ref: ref, commands: commands, showCommandMenu: showCommandMenu, commandMenuQuery: commandMenuQuery, selectedMenuIndex: selectedMenuIndex, setSelectedMenuIndex: setSelectedMenuIndex, setMenuItemCount: setMenuItemCount, triggerCounter: triggerCounter, onChange: onChange, className: className, ...props }));
218
+ });
219
+ ChatInputCommandMenu.displayName = "ChatInput.CommandMenu";
220
+ export { ChatInputRoot as Root, ChatInputField as Field, ChatInputSubmit as Submit, ChatInputToolbar as Toolbar, ChatInputActions as Actions, ChatInputAttachment as Attachment, ChatInputVoiceInput as VoiceInput, ChatInputCommandMenu as CommandMenu, };
@@ -0,0 +1,20 @@
1
+ import * as React from "react";
2
+ export interface CommandMenuItem {
3
+ id: string;
4
+ label: string;
5
+ description?: string;
6
+ icon?: React.ReactNode;
7
+ category?: string;
8
+ onSelect: () => void;
9
+ }
10
+ export interface ChatInputCommandMenuProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
11
+ commands?: CommandMenuItem[];
12
+ showCommandMenu: boolean;
13
+ commandMenuQuery: string;
14
+ selectedMenuIndex: number;
15
+ setSelectedMenuIndex: (index: number) => void;
16
+ setMenuItemCount: (count: number) => void;
17
+ triggerCounter: number;
18
+ onChange: (value: string) => void;
19
+ }
20
+ export declare const ChatInputCommandMenu: React.ForwardRefExoticComponent<ChatInputCommandMenuProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,62 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../lib/utils.js";
4
+ /* -------------------------------------------------------------------------------------------------
5
+ * ChatInputCommandMenu
6
+ * -----------------------------------------------------------------------------------------------*/
7
+ export const ChatInputCommandMenu = React.forwardRef(({ commands = [], showCommandMenu, commandMenuQuery, selectedMenuIndex, setSelectedMenuIndex, setMenuItemCount, triggerCounter, onChange, className, ...props }, ref) => {
8
+ // Fuzzy search implementation
9
+ const fuzzyMatch = React.useCallback((text, query) => {
10
+ const lowerText = text.toLowerCase();
11
+ const lowerQuery = query.toLowerCase();
12
+ if (!query)
13
+ return 1; // Show all when no query
14
+ if (lowerText.includes(lowerQuery)) {
15
+ // Exact substring match gets high score
16
+ return 1 - lowerQuery.length / lowerText.length;
17
+ }
18
+ // Fuzzy matching - check if all query chars appear in order
19
+ let queryIndex = 0;
20
+ for (let i = 0; i < lowerText.length && queryIndex < lowerQuery.length; i++) {
21
+ if (lowerText[i] === lowerQuery[queryIndex]) {
22
+ queryIndex++;
23
+ }
24
+ }
25
+ if (queryIndex === lowerQuery.length) {
26
+ // All chars found, score based on how spread out they are
27
+ return 0.5 - queryIndex / lowerText.length;
28
+ }
29
+ return 0; // No match
30
+ }, []);
31
+ // Filter and sort commands by relevance
32
+ const filteredCommands = React.useMemo(() => {
33
+ return commands
34
+ .map((cmd) => ({
35
+ ...cmd,
36
+ score: Math.max(fuzzyMatch(cmd.label, commandMenuQuery), cmd.description ? fuzzyMatch(cmd.description, commandMenuQuery) : 0),
37
+ }))
38
+ .filter((cmd) => cmd.score > 0)
39
+ .sort((a, b) => b.score - a.score);
40
+ }, [commands, commandMenuQuery, fuzzyMatch]);
41
+ // Update menu item count
42
+ React.useEffect(() => {
43
+ setMenuItemCount(filteredCommands.length);
44
+ }, [filteredCommands.length, setMenuItemCount]);
45
+ // Reset selected index when filtered results change
46
+ React.useEffect(() => {
47
+ setSelectedMenuIndex(0);
48
+ }, [setSelectedMenuIndex]);
49
+ // Handle selection when triggered
50
+ React.useEffect(() => {
51
+ if (triggerCounter > 0 && filteredCommands[selectedMenuIndex]) {
52
+ filteredCommands[selectedMenuIndex].onSelect();
53
+ // Clear the input after selection
54
+ onChange("");
55
+ }
56
+ }, [triggerCounter, filteredCommands, selectedMenuIndex, onChange]);
57
+ if (!showCommandMenu || filteredCommands.length === 0) {
58
+ return null;
59
+ }
60
+ return (_jsxs("div", { ref: ref, className: cn("absolute bottom-full left-0 z-50 mb-2 w-full max-w-md", "rounded-md border border-border bg-card p-2 shadow-lg", className), ...props, children: [_jsx("div", { className: "text-xs font-semibold text-muted-foreground px-2 py-1", children: "Commands" }), _jsx("div", { className: "max-h-64 overflow-y-auto", children: filteredCommands.map((command, index) => (_jsxs("button", { type: "button", onClick: () => command.onSelect(), className: cn("w-full rounded-sm px-2 py-2 text-left text-sm transition-colors", "flex items-start gap-2", "hover:bg-muted", index === selectedMenuIndex && "bg-muted"), children: [command.icon && (_jsx("span", { className: "shrink-0 mt-0.5", children: command.icon })), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("div", { className: "font-medium", children: command.label }), command.description && (_jsx("div", { className: "text-xs text-muted-foreground truncate", children: command.description }))] })] }, command.id))) })] }));
61
+ });
62
+ ChatInputCommandMenu.displayName = "ChatInputCommandMenu";
@@ -2,81 +2,51 @@ import * as React from "react";
2
2
  type PanelSize = "hidden" | "small" | "large";
3
3
  type PanelTabType = "todo" | "files" | "database";
4
4
  interface ChatLayoutContextValue {
5
- sidebarOpen: boolean;
6
- setSidebarOpen: (open: boolean) => void;
7
- panelSize: PanelSize;
8
- setPanelSize: (size: PanelSize) => void;
9
- activeTab: PanelTabType;
10
- setActiveTab: (tab: PanelTabType) => void;
5
+ sidebarOpen: boolean;
6
+ setSidebarOpen: (open: boolean) => void;
7
+ panelSize: PanelSize;
8
+ setPanelSize: (size: PanelSize) => void;
9
+ activeTab: PanelTabType;
10
+ setActiveTab: (tab: PanelTabType) => void;
11
11
  }
12
12
  declare const useChatLayoutContext: () => ChatLayoutContextValue;
13
- export interface ChatLayoutRootProps
14
- extends React.HTMLAttributes<HTMLDivElement> {
15
- /** Initial sidebar open state */
16
- defaultSidebarOpen?: boolean;
17
- /** Initial panel size state */
18
- defaultPanelSize?: PanelSize;
19
- /** Initial active tab */
20
- defaultActiveTab?: PanelTabType;
13
+ export interface ChatLayoutRootProps extends React.HTMLAttributes<HTMLDivElement> {
14
+ /** Initial sidebar open state */
15
+ defaultSidebarOpen?: boolean;
16
+ /** Initial panel size state */
17
+ defaultPanelSize?: PanelSize;
18
+ /** Initial active tab */
19
+ defaultActiveTab?: PanelTabType;
21
20
  }
22
- declare const ChatLayoutRoot: React.ForwardRefExoticComponent<
23
- ChatLayoutRootProps & React.RefAttributes<HTMLDivElement>
24
- >;
25
- export interface ChatLayoutHeaderProps
26
- extends React.HTMLAttributes<HTMLDivElement> {}
27
- declare const ChatLayoutHeader: React.ForwardRefExoticComponent<
28
- ChatLayoutHeaderProps & React.RefAttributes<HTMLDivElement>
29
- >;
30
- export interface ChatLayoutMainProps
31
- extends React.HTMLAttributes<HTMLDivElement> {}
32
- declare const ChatLayoutMain: React.ForwardRefExoticComponent<
33
- ChatLayoutMainProps & React.RefAttributes<HTMLDivElement>
34
- >;
35
- export interface ChatLayoutBodyProps
36
- extends React.HTMLAttributes<HTMLDivElement> {
37
- /** Whether to show toaster */
38
- showToaster?: boolean;
21
+ declare const ChatLayoutRoot: React.ForwardRefExoticComponent<ChatLayoutRootProps & React.RefAttributes<HTMLDivElement>>;
22
+ export interface ChatLayoutHeaderProps extends React.HTMLAttributes<HTMLDivElement> {
39
23
  }
40
- declare const ChatLayoutBody: React.ForwardRefExoticComponent<
41
- ChatLayoutBodyProps & React.RefAttributes<HTMLDivElement>
42
- >;
43
- export interface ChatLayoutMessagesProps
44
- extends React.HTMLAttributes<HTMLDivElement> {
45
- /** Callback when scroll position changes */
46
- onScrollChange?: (isAtBottom: boolean) => void;
47
- /** Whether to show scroll to bottom button */
48
- showScrollToBottom?: boolean;
24
+ declare const ChatLayoutHeader: React.ForwardRefExoticComponent<ChatLayoutHeaderProps & React.RefAttributes<HTMLDivElement>>;
25
+ export interface ChatLayoutMainProps extends React.HTMLAttributes<HTMLDivElement> {
49
26
  }
50
- declare const ChatLayoutMessages: React.ForwardRefExoticComponent<
51
- ChatLayoutMessagesProps & React.RefAttributes<HTMLDivElement>
52
- >;
53
- export interface ChatLayoutFooterProps
54
- extends React.HTMLAttributes<HTMLDivElement> {}
55
- declare const ChatLayoutFooter: React.ForwardRefExoticComponent<
56
- ChatLayoutFooterProps & React.RefAttributes<HTMLDivElement>
57
- >;
58
- export interface ChatLayoutSidebarProps
59
- extends React.HTMLAttributes<HTMLDivElement> {}
60
- declare const ChatLayoutSidebar: React.ForwardRefExoticComponent<
61
- ChatLayoutSidebarProps & React.RefAttributes<HTMLDivElement>
62
- >;
63
- export interface ChatLayoutAsideProps
64
- extends React.HTMLAttributes<HTMLDivElement> {
65
- /** Show panel on these breakpoints (default: lg and above) */
66
- breakpoint?: "md" | "lg" | "xl" | "2xl";
27
+ declare const ChatLayoutMain: React.ForwardRefExoticComponent<ChatLayoutMainProps & React.RefAttributes<HTMLDivElement>>;
28
+ export interface ChatLayoutBodyProps extends React.HTMLAttributes<HTMLDivElement> {
29
+ /** Whether to show toaster */
30
+ showToaster?: boolean;
67
31
  }
68
- declare const ChatLayoutAside: React.ForwardRefExoticComponent<
69
- ChatLayoutAsideProps & React.RefAttributes<HTMLDivElement>
70
- >;
71
- export {
72
- ChatLayoutRoot as Root,
73
- ChatLayoutHeader as Header,
74
- ChatLayoutMain as Main,
75
- ChatLayoutBody as Body,
76
- ChatLayoutMessages as Messages,
77
- ChatLayoutFooter as Footer,
78
- ChatLayoutSidebar as Sidebar,
79
- ChatLayoutAside as Aside,
80
- useChatLayoutContext,
81
- };
32
+ declare const ChatLayoutBody: React.ForwardRefExoticComponent<ChatLayoutBodyProps & React.RefAttributes<HTMLDivElement>>;
33
+ export interface ChatLayoutMessagesProps extends React.HTMLAttributes<HTMLDivElement> {
34
+ /** Callback when scroll position changes */
35
+ onScrollChange?: (isAtBottom: boolean) => void;
36
+ /** Whether to show scroll to bottom button */
37
+ showScrollToBottom?: boolean;
38
+ }
39
+ declare const ChatLayoutMessages: React.ForwardRefExoticComponent<ChatLayoutMessagesProps & React.RefAttributes<HTMLDivElement>>;
40
+ export interface ChatLayoutFooterProps extends React.HTMLAttributes<HTMLDivElement> {
41
+ }
42
+ declare const ChatLayoutFooter: React.ForwardRefExoticComponent<ChatLayoutFooterProps & React.RefAttributes<HTMLDivElement>>;
43
+ export interface ChatLayoutSidebarProps extends React.HTMLAttributes<HTMLDivElement> {
44
+ }
45
+ declare const ChatLayoutSidebar: React.ForwardRefExoticComponent<ChatLayoutSidebarProps & React.RefAttributes<HTMLDivElement>>;
46
+ export interface ChatLayoutAsideProps extends React.HTMLAttributes<HTMLDivElement> {
47
+ /** Show panel on these breakpoints (default: lg and above) */
48
+ breakpoint?: "md" | "lg" | "xl" | "2xl";
49
+ }
50
+ declare const ChatLayoutAside: React.ForwardRefExoticComponent<ChatLayoutAsideProps & React.RefAttributes<HTMLDivElement>>;
51
+ export { ChatLayoutRoot as Root, ChatLayoutHeader as Header, ChatLayoutMain as Main, ChatLayoutBody as Body, ChatLayoutMessages as Messages, ChatLayoutFooter as Footer, ChatLayoutSidebar as Sidebar, ChatLayoutAside as Aside, useChatLayoutContext, };
82
52
  export type { PanelSize, PanelTabType };