npcts 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 (285) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +139 -0
  3. package/dist/adapters/base.d.ts +13 -0
  4. package/dist/adapters/base.js +1 -0
  5. package/dist/adapters/electron/bridge.d.ts +4 -0
  6. package/dist/adapters/electron/bridge.js +88 -0
  7. package/dist/adapters/index.d.ts +2 -0
  8. package/dist/adapters/index.js +3 -0
  9. package/dist/core/browser.d.ts +25 -0
  10. package/dist/core/browser.js +1 -0
  11. package/dist/core/chat.d.ts +41 -0
  12. package/dist/core/chat.js +1 -0
  13. package/dist/core/database.d.ts +56 -0
  14. package/dist/core/database.js +50 -0
  15. package/dist/core/files.d.ts +24 -0
  16. package/dist/core/files.js +15 -0
  17. package/dist/core/index.d.ts +9 -0
  18. package/dist/core/index.js +10 -0
  19. package/dist/core/jobs.d.ts +20 -0
  20. package/dist/core/jobs.js +1 -0
  21. package/dist/core/layout.d.ts +30 -0
  22. package/dist/core/layout.js +41 -0
  23. package/dist/core/stream.d.ts +10 -0
  24. package/dist/core/stream.js +1 -0
  25. package/dist/core/types.d.ts +23 -0
  26. package/dist/core/types.js +1 -0
  27. package/dist/core/utils.d.ts +19 -0
  28. package/dist/core/utils.js +19 -0
  29. package/dist/index.d.ts +25 -0
  30. package/dist/index.js +29 -0
  31. package/dist/ui/chat/components/ChatHeaderBar.d.ts +13 -0
  32. package/dist/ui/chat/components/ChatHeaderBar.js +32 -0
  33. package/dist/ui/chat/components/ChatInterface.d.ts +10 -0
  34. package/dist/ui/chat/components/ChatInterface.js +19 -0
  35. package/dist/ui/chat/components/ChatMessage.d.ts +7 -0
  36. package/dist/ui/chat/components/ChatMessage.js +10 -0
  37. package/dist/ui/chat/components/ChatPane.d.ts +20 -0
  38. package/dist/ui/chat/components/ChatPane.js +20 -0
  39. package/dist/ui/chat/components/ChatView.d.ts +10 -0
  40. package/dist/ui/chat/components/ChatView.js +36 -0
  41. package/dist/ui/chat/components/ConversationList.d.ts +28 -0
  42. package/dist/ui/chat/components/ConversationList.js +113 -0
  43. package/dist/ui/chat/components/InPaneSearchBar.d.ts +12 -0
  44. package/dist/ui/chat/components/InPaneSearchBar.js +44 -0
  45. package/dist/ui/chat/components/InputArea.d.ts +7 -0
  46. package/dist/ui/chat/components/InputArea.js +46 -0
  47. package/dist/ui/chat/components/MessageAttachments.d.ts +5 -0
  48. package/dist/ui/chat/components/MessageAttachments.js +6 -0
  49. package/dist/ui/chat/components/MessageItem.d.ts +28 -0
  50. package/dist/ui/chat/components/MessageItem.js +43 -0
  51. package/dist/ui/chat/components/PredictiveTextOverlay.d.ts +10 -0
  52. package/dist/ui/chat/components/PredictiveTextOverlay.js +75 -0
  53. package/dist/ui/chat/context/ChatContext.d.ts +33 -0
  54. package/dist/ui/chat/context/ChatContext.js +157 -0
  55. package/dist/ui/chat/hooks/useAutoScroll.d.ts +1 -0
  56. package/dist/ui/chat/hooks/useAutoScroll.js +11 -0
  57. package/dist/ui/chat/index.d.ts +7 -0
  58. package/dist/ui/chat/index.js +7 -0
  59. package/dist/ui/dashboard/ChartWidget.d.ts +18 -0
  60. package/dist/ui/dashboard/ChartWidget.js +98 -0
  61. package/dist/ui/dashboard/QueryWidget.d.ts +29 -0
  62. package/dist/ui/dashboard/QueryWidget.js +117 -0
  63. package/dist/ui/dashboard/TableWidget.d.ts +14 -0
  64. package/dist/ui/dashboard/TableWidget.js +117 -0
  65. package/dist/ui/dashboard/Widget.d.ts +40 -0
  66. package/dist/ui/dashboard/Widget.js +26 -0
  67. package/dist/ui/dashboard/WidgetBuilder.d.ts +47 -0
  68. package/dist/ui/dashboard/WidgetBuilder.js +286 -0
  69. package/dist/ui/dashboard/WidgetGrid.d.ts +19 -0
  70. package/dist/ui/dashboard/WidgetGrid.js +11 -0
  71. package/dist/ui/dashboard/index.d.ts +8 -0
  72. package/dist/ui/dashboard/index.js +4 -0
  73. package/dist/ui/dialogs/BrowserUrlDialog.d.ts +8 -0
  74. package/dist/ui/dialogs/BrowserUrlDialog.js +203 -0
  75. package/dist/ui/dialogs/index.d.ts +1 -0
  76. package/dist/ui/dialogs/index.js +1 -0
  77. package/dist/ui/editors/ImageEditor.d.ts +49 -0
  78. package/dist/ui/editors/ImageEditor.js +264 -0
  79. package/dist/ui/editors/index.d.ts +2 -0
  80. package/dist/ui/editors/index.js +1 -0
  81. package/dist/ui/execution/ExecutionFilters.d.ts +11 -0
  82. package/dist/ui/execution/ExecutionFilters.js +27 -0
  83. package/dist/ui/execution/ExecutionHistoryList.d.ts +19 -0
  84. package/dist/ui/execution/ExecutionHistoryList.js +35 -0
  85. package/dist/ui/execution/index.d.ts +3 -0
  86. package/dist/ui/execution/index.js +2 -0
  87. package/dist/ui/files/components/FileTree.d.ts +18 -0
  88. package/dist/ui/files/components/FileTree.js +61 -0
  89. package/dist/ui/files/components/Sidebar.d.ts +18 -0
  90. package/dist/ui/files/components/Sidebar.js +67 -0
  91. package/dist/ui/files/components/index.d.ts +2 -0
  92. package/dist/ui/files/components/index.js +2 -0
  93. package/dist/ui/files/context/FileSystemContext.d.ts +23 -0
  94. package/dist/ui/files/context/FileSystemContext.js +65 -0
  95. package/dist/ui/files/context/index.d.ts +1 -0
  96. package/dist/ui/files/context/index.js +1 -0
  97. package/dist/ui/files/index.d.ts +2 -0
  98. package/dist/ui/files/index.js +2 -0
  99. package/dist/ui/hooks/index.d.ts +4 -0
  100. package/dist/ui/hooks/index.js +3 -0
  101. package/dist/ui/hooks/useDebounce.d.ts +8 -0
  102. package/dist/ui/hooks/useDebounce.js +18 -0
  103. package/dist/ui/hooks/usePaneTracking.d.ts +15 -0
  104. package/dist/ui/hooks/usePaneTracking.js +20 -0
  105. package/dist/ui/hooks/useQuery.d.ts +25 -0
  106. package/dist/ui/hooks/useQuery.js +71 -0
  107. package/dist/ui/index.d.ts +19 -0
  108. package/dist/ui/index.js +20 -0
  109. package/dist/ui/jinx/JinxEditor.d.ts +20 -0
  110. package/dist/ui/jinx/JinxEditor.js +34 -0
  111. package/dist/ui/jinx/JinxTree.d.ts +14 -0
  112. package/dist/ui/jinx/JinxTree.js +65 -0
  113. package/dist/ui/jinx/index.d.ts +3 -0
  114. package/dist/ui/jinx/index.js +2 -0
  115. package/dist/ui/knowledge-graph/KGControls.d.ts +12 -0
  116. package/dist/ui/knowledge-graph/KGControls.js +21 -0
  117. package/dist/ui/knowledge-graph/KGStats.d.ts +13 -0
  118. package/dist/ui/knowledge-graph/KGStats.js +18 -0
  119. package/dist/ui/knowledge-graph/KnowledgeGraphViewer.d.ts +22 -0
  120. package/dist/ui/knowledge-graph/KnowledgeGraphViewer.js +16 -0
  121. package/dist/ui/knowledge-graph/index.d.ts +4 -0
  122. package/dist/ui/knowledge-graph/index.js +3 -0
  123. package/dist/ui/layout/components/AppShell.d.ts +8 -0
  124. package/dist/ui/layout/components/AppShell.js +19 -0
  125. package/dist/ui/layout/components/ContentPaneContainer.d.ts +8 -0
  126. package/dist/ui/layout/components/ContentPaneContainer.js +95 -0
  127. package/dist/ui/layout/components/LayoutNode.d.ts +8 -0
  128. package/dist/ui/layout/components/LayoutNode.js +12 -0
  129. package/dist/ui/layout/components/PaneHeader.d.ts +17 -0
  130. package/dist/ui/layout/components/PaneHeader.js +40 -0
  131. package/dist/ui/layout/components/SplitView.d.ts +8 -0
  132. package/dist/ui/layout/components/SplitView.js +51 -0
  133. package/dist/ui/layout/components/Studio.d.ts +7 -0
  134. package/dist/ui/layout/components/Studio.js +12 -0
  135. package/dist/ui/layout/components/contextMenus/BrowserContextMenu.d.ts +13 -0
  136. package/dist/ui/layout/components/contextMenus/BrowserContextMenu.js +23 -0
  137. package/dist/ui/layout/components/contextMenus/EditorContextMenu.d.ts +15 -0
  138. package/dist/ui/layout/components/contextMenus/EditorContextMenu.js +32 -0
  139. package/dist/ui/layout/components/contextMenus/FileContextMenu.d.ts +15 -0
  140. package/dist/ui/layout/components/contextMenus/FileContextMenu.js +33 -0
  141. package/dist/ui/layout/components/contextMenus/PdfContextMenu.d.ts +13 -0
  142. package/dist/ui/layout/components/contextMenus/PdfContextMenu.js +23 -0
  143. package/dist/ui/layout/components/contextMenus/index.d.ts +4 -0
  144. package/dist/ui/layout/components/contextMenus/index.js +4 -0
  145. package/dist/ui/layout/components/index.d.ts +8 -0
  146. package/dist/ui/layout/components/index.js +8 -0
  147. package/dist/ui/layout/components/modals/AIEditModal.d.ts +9 -0
  148. package/dist/ui/layout/components/modals/AIEditModal.js +33 -0
  149. package/dist/ui/layout/components/modals/MemoryApprovalModal.d.ts +15 -0
  150. package/dist/ui/layout/components/modals/MemoryApprovalModal.js +23 -0
  151. package/dist/ui/layout/components/modals/PromptModal.d.ts +11 -0
  152. package/dist/ui/layout/components/modals/PromptModal.js +20 -0
  153. package/dist/ui/layout/components/modals/ResendModal.d.ts +16 -0
  154. package/dist/ui/layout/components/modals/ResendModal.js +22 -0
  155. package/dist/ui/layout/components/modals/index.d.ts +4 -0
  156. package/dist/ui/layout/components/modals/index.js +4 -0
  157. package/dist/ui/layout/context/LayoutContext.d.ts +32 -0
  158. package/dist/ui/layout/context/LayoutContext.js +144 -0
  159. package/dist/ui/layout/index.d.ts +2 -0
  160. package/dist/ui/layout/index.js +2 -0
  161. package/dist/ui/markdown/Markdown.d.ts +4 -0
  162. package/dist/ui/markdown/Markdown.js +4 -0
  163. package/dist/ui/memory/MemoryFilters.d.ts +12 -0
  164. package/dist/ui/memory/MemoryFilters.js +23 -0
  165. package/dist/ui/memory/MemoryList.d.ts +19 -0
  166. package/dist/ui/memory/MemoryList.js +36 -0
  167. package/dist/ui/memory/index.d.ts +3 -0
  168. package/dist/ui/memory/index.js +2 -0
  169. package/dist/ui/models/ModelCard.d.ts +16 -0
  170. package/dist/ui/models/ModelCard.js +30 -0
  171. package/dist/ui/models/ModelSelector.d.ts +13 -0
  172. package/dist/ui/models/ModelSelector.js +6 -0
  173. package/dist/ui/models/index.d.ts +3 -0
  174. package/dist/ui/models/index.js +2 -0
  175. package/dist/ui/npc/McpServerMenu.d.ts +15 -0
  176. package/dist/ui/npc/McpServerMenu.js +48 -0
  177. package/dist/ui/npc/NPCEditor.d.ts +21 -0
  178. package/dist/ui/npc/NPCEditor.js +17 -0
  179. package/dist/ui/npc/NPCList.d.ts +19 -0
  180. package/dist/ui/npc/NPCList.js +28 -0
  181. package/dist/ui/npc/index.d.ts +3 -0
  182. package/dist/ui/npc/index.js +2 -0
  183. package/dist/ui/photo/GalleryGrid.d.ts +25 -0
  184. package/dist/ui/photo/GalleryGrid.js +46 -0
  185. package/dist/ui/photo/ImageAdjustmentSliders.d.ts +37 -0
  186. package/dist/ui/photo/ImageAdjustmentSliders.js +56 -0
  187. package/dist/ui/photo/ImageLabelingCanvas.d.ts +47 -0
  188. package/dist/ui/photo/ImageLabelingCanvas.js +174 -0
  189. package/dist/ui/photo/ImageSourceTabs.d.ts +28 -0
  190. package/dist/ui/photo/ImageSourceTabs.js +46 -0
  191. package/dist/ui/photo/LayerPanel.d.ts +87 -0
  192. package/dist/ui/photo/LayerPanel.js +70 -0
  193. package/dist/ui/photo/Lightbox.d.ts +12 -0
  194. package/dist/ui/photo/Lightbox.js +90 -0
  195. package/dist/ui/photo/index.d.ts +6 -0
  196. package/dist/ui/photo/index.js +7 -0
  197. package/dist/ui/primitives/AutosizeTextarea.d.ts +8 -0
  198. package/dist/ui/primitives/AutosizeTextarea.js +17 -0
  199. package/dist/ui/primitives/Button.d.ts +7 -0
  200. package/dist/ui/primitives/Button.js +17 -0
  201. package/dist/ui/primitives/Card.d.ts +12 -0
  202. package/dist/ui/primitives/Card.js +8 -0
  203. package/dist/ui/primitives/Chart.d.ts +34 -0
  204. package/dist/ui/primitives/Chart.js +140 -0
  205. package/dist/ui/primitives/ContextMenu.d.ts +17 -0
  206. package/dist/ui/primitives/ContextMenu.js +33 -0
  207. package/dist/ui/primitives/DataTable.d.ts +13 -0
  208. package/dist/ui/primitives/DataTable.js +13 -0
  209. package/dist/ui/primitives/FileUpload.d.ts +10 -0
  210. package/dist/ui/primitives/FileUpload.js +24 -0
  211. package/dist/ui/primitives/ImageGrid.d.ts +21 -0
  212. package/dist/ui/primitives/ImageGrid.js +29 -0
  213. package/dist/ui/primitives/Input.d.ts +7 -0
  214. package/dist/ui/primitives/Input.js +10 -0
  215. package/dist/ui/primitives/Lightbox.d.ts +12 -0
  216. package/dist/ui/primitives/Lightbox.js +36 -0
  217. package/dist/ui/primitives/Modal.d.ts +10 -0
  218. package/dist/ui/primitives/Modal.js +33 -0
  219. package/dist/ui/primitives/RangeSlider.d.ts +13 -0
  220. package/dist/ui/primitives/RangeSlider.js +6 -0
  221. package/dist/ui/primitives/Select.d.ts +11 -0
  222. package/dist/ui/primitives/Select.js +10 -0
  223. package/dist/ui/primitives/Slider.d.ts +13 -0
  224. package/dist/ui/primitives/Slider.js +8 -0
  225. package/dist/ui/primitives/SortableList.d.ts +9 -0
  226. package/dist/ui/primitives/SortableList.js +19 -0
  227. package/dist/ui/primitives/Spinner.d.ts +2 -0
  228. package/dist/ui/primitives/Spinner.js +2 -0
  229. package/dist/ui/primitives/StarRating.d.ts +10 -0
  230. package/dist/ui/primitives/StarRating.js +10 -0
  231. package/dist/ui/primitives/Tabs.d.ts +15 -0
  232. package/dist/ui/primitives/Tabs.js +11 -0
  233. package/dist/ui/primitives/TagInput.d.ts +10 -0
  234. package/dist/ui/primitives/TagInput.js +25 -0
  235. package/dist/ui/primitives/index.d.ts +20 -0
  236. package/dist/ui/primitives/index.js +19 -0
  237. package/dist/ui/specialized/DiskUsageAnalyzer.d.ts +12 -0
  238. package/dist/ui/specialized/DiskUsageAnalyzer.js +38 -0
  239. package/dist/ui/specialized/OllamaModelManager.d.ts +2 -0
  240. package/dist/ui/specialized/OllamaModelManager.js +48 -0
  241. package/dist/ui/specialized/RichTextEditor.d.ts +8 -0
  242. package/dist/ui/specialized/RichTextEditor.js +64 -0
  243. package/dist/ui/specialized/SlideCanvas.d.ts +24 -0
  244. package/dist/ui/specialized/SlideCanvas.js +31 -0
  245. package/dist/ui/specialized/SpreadsheetGrid.d.ts +18 -0
  246. package/dist/ui/specialized/SpreadsheetGrid.js +28 -0
  247. package/dist/ui/specialized/StepEditor.d.ts +12 -0
  248. package/dist/ui/specialized/StepEditor.js +22 -0
  249. package/dist/ui/specialized/TerminalEmbed.d.ts +13 -0
  250. package/dist/ui/specialized/TerminalEmbed.js +50 -0
  251. package/dist/ui/specialized/index.d.ts +7 -0
  252. package/dist/ui/specialized/index.js +7 -0
  253. package/dist/ui/sql/SQLQueryEditor.d.ts +9 -0
  254. package/dist/ui/sql/SQLQueryEditor.js +10 -0
  255. package/dist/ui/sql/SQLResultsTable.d.ts +6 -0
  256. package/dist/ui/sql/SQLResultsTable.js +12 -0
  257. package/dist/ui/sql/SQLSchemaViewer.d.ts +14 -0
  258. package/dist/ui/sql/SQLSchemaViewer.js +18 -0
  259. package/dist/ui/sql/index.d.ts +3 -0
  260. package/dist/ui/sql/index.js +3 -0
  261. package/dist/ui/utils/cn.d.ts +1 -0
  262. package/dist/ui/utils/cn.js +1 -0
  263. package/dist/ui/utils/fileIcons.d.ts +2 -0
  264. package/dist/ui/utils/fileIcons.js +24 -0
  265. package/dist/ui/utils/fileUtils.d.ts +4 -0
  266. package/dist/ui/utils/fileUtils.js +11 -0
  267. package/dist/ui/utils/index.d.ts +3 -0
  268. package/dist/ui/utils/index.js +3 -0
  269. package/dist/ui/viewers/components/BrowserViewer.d.ts +9 -0
  270. package/dist/ui/viewers/components/BrowserViewer.js +44 -0
  271. package/dist/ui/viewers/components/CodeEditor.d.ts +11 -0
  272. package/dist/ui/viewers/components/CodeEditor.js +115 -0
  273. package/dist/ui/viewers/components/CsvViewer.d.ts +7 -0
  274. package/dist/ui/viewers/components/CsvViewer.js +18 -0
  275. package/dist/ui/viewers/components/ImageViewer.d.ts +7 -0
  276. package/dist/ui/viewers/components/ImageViewer.js +23 -0
  277. package/dist/ui/viewers/components/PdfViewer.d.ts +7 -0
  278. package/dist/ui/viewers/components/PdfViewer.js +29 -0
  279. package/dist/ui/viewers/components/Terminal.d.ts +7 -0
  280. package/dist/ui/viewers/components/Terminal.js +31 -0
  281. package/dist/ui/viewers/components/index.d.ts +6 -0
  282. package/dist/ui/viewers/components/index.js +6 -0
  283. package/dist/ui/viewers/index.d.ts +1 -0
  284. package/dist/ui/viewers/index.js +1 -0
  285. package/package.json +76 -0
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Core utility functions for npcts
3
+ */
4
+ /**
5
+ * Generate a unique ID string
6
+ * @returns A random 9-character alphanumeric string
7
+ */
8
+ export declare const generateId: () => string;
9
+ /**
10
+ * Hash an array of context objects for caching/comparison
11
+ * @param contexts - Array of context objects with type, path/url, and content
12
+ * @returns Base64 encoded hash string
13
+ */
14
+ export declare const hashContext: (contexts: Array<{
15
+ type: string;
16
+ path?: string;
17
+ url?: string;
18
+ content?: string;
19
+ }>) => string;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Core utility functions for npcts
3
+ */
4
+ /**
5
+ * Generate a unique ID string
6
+ * @returns A random 9-character alphanumeric string
7
+ */
8
+ export const generateId = () => Math.random().toString(36).substr(2, 9);
9
+ /**
10
+ * Hash an array of context objects for caching/comparison
11
+ * @param contexts - Array of context objects with type, path/url, and content
12
+ * @returns Base64 encoded hash string
13
+ */
14
+ export const hashContext = (contexts) => {
15
+ const contentString = contexts
16
+ .map(ctx => `${ctx.type}:${ctx.path || ctx.url}:${ctx.content?.substring(0, 100) || ''}`)
17
+ .join('|');
18
+ return btoa(contentString);
19
+ };
@@ -0,0 +1,25 @@
1
+ export * from "./core/chat";
2
+ export * from "./core/types";
3
+ export * from "./core/layout";
4
+ export * from "./core/files";
5
+ export * from "./core/utils";
6
+ export * from "./core/database";
7
+ export * from "./adapters/base";
8
+ export { createElectronAdapter } from "./adapters/electron/bridge";
9
+ export * from "./ui/hooks";
10
+ export * from "./ui/primitives";
11
+ export * from "./ui/chat";
12
+ export * from "./ui/files";
13
+ export { MessageItem } from "./ui/chat/components/MessageItem";
14
+ export * from "./ui/viewers";
15
+ export * from "./ui/dialogs";
16
+ export * from "./ui/specialized";
17
+ export { getFileIcon, convertFileToBase64 } from "./ui/utils";
18
+ export * from "./ui/layout/components/modals";
19
+ export * from "./ui/sql";
20
+ export * from "./ui/memory";
21
+ export * from "./ui/knowledge-graph";
22
+ export * from "./ui/execution";
23
+ export * from "./ui/models";
24
+ export * from "./ui/dashboard";
25
+ export * from "./ui/editors";
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ // Core modules
2
+ export * from "./core/chat";
3
+ export * from "./core/types";
4
+ export * from "./core/layout";
5
+ export * from "./core/files";
6
+ export * from "./core/utils";
7
+ export * from "./core/database";
8
+ // Adapters
9
+ export * from "./adapters/base";
10
+ export { createElectronAdapter } from "./adapters/electron/bridge";
11
+ // UI Hooks
12
+ export * from "./ui/hooks";
13
+ // UI Components
14
+ export * from "./ui/primitives";
15
+ export * from "./ui/chat";
16
+ export * from "./ui/files";
17
+ export { MessageItem } from "./ui/chat/components/MessageItem";
18
+ export * from "./ui/viewers";
19
+ export * from "./ui/dialogs";
20
+ export * from "./ui/specialized";
21
+ export { getFileIcon, convertFileToBase64 } from "./ui/utils";
22
+ export * from "./ui/layout/components/modals";
23
+ export * from "./ui/sql";
24
+ export * from "./ui/memory";
25
+ export * from "./ui/knowledge-graph";
26
+ export * from "./ui/execution";
27
+ export * from "./ui/models";
28
+ export * from "./ui/dashboard";
29
+ export * from "./ui/editors";
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ export interface ConversationStats {
3
+ messageCount?: number;
4
+ tokenCount?: number;
5
+ models?: Set<string>;
6
+ agents?: Set<string>;
7
+ totalAttachments?: number;
8
+ totalToolCalls?: number;
9
+ }
10
+ export declare const ChatHeaderBar: React.FC<{
11
+ stats: ConversationStats;
12
+ isEmpty: boolean;
13
+ }>;
@@ -0,0 +1,32 @@
1
+ import React from "react";
2
+ import { MessageSquare, Terminal, Code2, Users, Paperclip, Wrench } from "lucide-react";
3
+ export const ChatHeaderBar = ({ stats, isEmpty }) => {
4
+ if (isEmpty)
5
+ return null;
6
+ return (React.createElement("div", { className: "p-2 flex flex-wrap gap-x-4 gap-y-1 text-gray-400 min-h-[20px] theme-bg-secondary border-b theme-border" },
7
+ React.createElement("span", null,
8
+ React.createElement(MessageSquare, { size: 12, className: "inline mr-1" }),
9
+ stats.messageCount || 0,
10
+ " Msgs"),
11
+ React.createElement("span", null,
12
+ React.createElement(Terminal, { size: 12, className: "inline mr-1" }),
13
+ "~",
14
+ stats.tokenCount || 0,
15
+ " Tokens"),
16
+ React.createElement("span", null,
17
+ React.createElement(Code2, { size: 12, className: "inline mr-1" }),
18
+ stats.models?.size || 0,
19
+ " Models"),
20
+ React.createElement("span", null,
21
+ React.createElement(Users, { size: 12, className: "inline mr-1" }),
22
+ stats.agents?.size || 0,
23
+ " Agents"),
24
+ stats.totalAttachments ? (React.createElement("span", null,
25
+ React.createElement(Paperclip, { size: 12, className: "inline mr-1" }),
26
+ stats.totalAttachments,
27
+ " Attachments")) : null,
28
+ stats.totalToolCalls ? (React.createElement("span", null,
29
+ React.createElement(Wrench, { size: 12, className: "inline mr-1" }),
30
+ stats.totalToolCalls,
31
+ " Tool Calls")) : null));
32
+ };
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import type { AppServices } from "../../../adapters/base";
3
+ import type { ModelInfo } from "../../../core/types";
4
+ interface Props {
5
+ services: AppServices;
6
+ workspacePath?: string;
7
+ models?: ModelInfo[];
8
+ }
9
+ export declare const ChatInterface: React.FC<Props>;
10
+ export {};
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ import { ChatProvider } from "../context/ChatContext";
3
+ import { ConversationList } from "./ConversationList";
4
+ import { ChatView } from "./ChatView";
5
+ import { InputArea } from "./InputArea";
6
+ import { useChatContext } from "../context/ChatContext";
7
+ export const ChatInterface = ({ services, workspacePath, models }) => {
8
+ return (React.createElement(ChatProvider, { services: services, workspacePath: workspacePath, models: models },
9
+ React.createElement("div", { className: "flex h-full min-h-0 theme-bg-primary theme-text-primary border theme-border rounded-lg overflow-hidden" },
10
+ React.createElement("div", { className: "w-72 min-w-[16rem] theme-bg-secondary theme-border border-r" },
11
+ React.createElement(ConversationListWrapper, null)),
12
+ React.createElement("div", { className: "flex-1 flex flex-col min-w-0" },
13
+ React.createElement(ChatView, null),
14
+ React.createElement(InputArea, { models: models })))));
15
+ };
16
+ const ConversationListWrapper = () => {
17
+ const { conversations, activeConversationId, setActiveConversation, createConversation, deleteConversation, loading, } = useChatContext();
18
+ return (React.createElement(ConversationList, { conversations: conversations, activeConversationId: activeConversationId, onConversationSelect: setActiveConversation, onRefresh: () => createConversation(), loading: loading }));
19
+ };
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import type { ChatMessage as ChatMessageType } from "../../../core/chat";
3
+ interface Props {
4
+ message: ChatMessageType;
5
+ }
6
+ export declare const ChatMessageBubble: React.FC<Props>;
7
+ export {};
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import { Markdown } from "../../markdown/Markdown";
3
+ import { AttachmentList } from "./MessageAttachments";
4
+ export const ChatMessageBubble = ({ message }) => {
5
+ const isUser = message.role === "user";
6
+ return (React.createElement("div", { className: `flex flex-col gap-1 ${isUser ? "items-end" : "items-start"}` },
7
+ React.createElement("div", { className: `max-w-3xl rounded-lg px-3 py-2 text-sm border theme-border ${isUser ? "theme-message-user" : "theme-message-assistant"}` },
8
+ React.createElement(Markdown, { content: message.content || "" })),
9
+ message.attachments?.length ? React.createElement(AttachmentList, { attachments: message.attachments }) : null));
10
+ };
@@ -0,0 +1,20 @@
1
+ import React from "react";
2
+ import { ConversationStats } from "./ChatHeaderBar";
3
+ export interface ChatPaneProps {
4
+ isEmpty: boolean;
5
+ stats: ConversationStats;
6
+ scrollRef?: React.Ref<HTMLDivElement> | null;
7
+ onScroll?: React.UIEventHandler<HTMLDivElement>;
8
+ renderMessages: () => React.ReactNode;
9
+ renderSelectionToolbar?: () => React.ReactNode;
10
+ renderInputArea: () => React.ReactNode;
11
+ onClick?: React.MouseEventHandler<HTMLDivElement>;
12
+ onKeyDown?: React.KeyboardEventHandler<HTMLDivElement>;
13
+ paneRef?: React.Ref<HTMLDivElement> | null;
14
+ ariaLabel?: string;
15
+ isStreaming?: boolean;
16
+ isLoadingMessages?: boolean;
17
+ onDragOver?: React.DragEventHandler<HTMLDivElement>;
18
+ onDrop?: React.DragEventHandler<HTMLDivElement>;
19
+ }
20
+ export declare const ChatPane: React.FC<ChatPaneProps>;
@@ -0,0 +1,20 @@
1
+ import React from "react";
2
+ import { ChatHeaderBar } from "./ChatHeaderBar";
3
+ // Structural wrapper that mirrors the chat pane layout in ChatInterface.jsx
4
+ export const ChatPane = ({ isEmpty, stats, scrollRef, onScroll, renderMessages, renderSelectionToolbar, renderInputArea, onClick, onKeyDown, paneRef, ariaLabel, isStreaming, isLoadingMessages, onDragOver, onDrop, }) => {
5
+ return (React.createElement("div", { className: "flex-1 flex flex-col min-h-0 overflow-hidden relative focus:outline-none", tabIndex: -1, ref: paneRef, onClick: onClick, onKeyDown: onKeyDown, "aria-label": ariaLabel || "Chat pane", onDragOver: onDragOver, onDrop: onDrop },
6
+ React.createElement(ChatHeaderBar, { stats: stats, isEmpty: isEmpty }),
7
+ isEmpty ? (React.createElement("div", { className: "flex-1 flex items-center justify-center theme-text-muted" },
8
+ React.createElement("div", { className: "text-center" },
9
+ React.createElement("div", { className: "text-lg mb-2" }, "No messages yet"),
10
+ React.createElement("div", { className: "text-sm" }, "Start a conversation by typing below")))) : (React.createElement(React.Fragment, null,
11
+ React.createElement("div", { className: "flex-1 overflow-y-auto theme-bg-primary custom-scrollbar relative", ref: scrollRef, onScroll: onScroll },
12
+ React.createElement("div", { className: "flex flex-col gap-4 p-4 pb-24", style: { userSelect: "auto" } },
13
+ renderMessages(),
14
+ (isStreaming || isLoadingMessages) && (React.createElement("div", { className: "flex justify-center py-4" },
15
+ React.createElement("div", { className: "flex items-center gap-2 text-gray-400" },
16
+ React.createElement("div", { className: "w-4 h-4 border-2 border-gray-500 rounded-full border-t-transparent animate-spin" }),
17
+ React.createElement("span", null, "Loading..."))))),
18
+ renderSelectionToolbar ? renderSelectionToolbar() : null))),
19
+ renderInputArea()));
20
+ };
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import { Conversation } from "../../../core/chat";
3
+ export type MessageListProps = {
4
+ renderHeader?: (args: {
5
+ conversation?: Conversation | undefined;
6
+ messageCount: number;
7
+ }) => React.ReactNode;
8
+ renderMessage?: (message: any) => React.ReactNode;
9
+ };
10
+ export declare const ChatView: React.FC<MessageListProps>;
@@ -0,0 +1,36 @@
1
+ import React, { useEffect, useMemo, useRef } from "react";
2
+ import { useChatContext } from "../context/ChatContext";
3
+ import { ChatMessageBubble } from "./ChatMessage";
4
+ import { useAutoScroll } from "../hooks/useAutoScroll";
5
+ import { Spinner } from "../../primitives/Spinner";
6
+ // Message list pane with header; defaults mimic ChatInterface.jsx message pane styling
7
+ export const ChatView = ({ renderHeader, renderMessage, }) => {
8
+ const { messages, activeConversationId, streaming, loading, conversations } = useChatContext();
9
+ const currentMessages = useMemo(() => (activeConversationId ? messages[activeConversationId] || [] : []), [activeConversationId, messages]);
10
+ const listRef = useRef(null);
11
+ const scrollRef = useAutoScroll([currentMessages.length, streaming]);
12
+ useEffect(() => {
13
+ if (listRef.current) {
14
+ listRef.current.scrollTop = listRef.current.scrollHeight;
15
+ }
16
+ }, [currentMessages.length]);
17
+ const activeConversation = useMemo(() => conversations.find((c) => c.id === activeConversationId), [conversations, activeConversationId]);
18
+ const defaultHeader = (React.createElement("div", { className: "flex items-center justify-between px-4 py-2 border-b theme-border bg-gray-800/60 text-gray-300 text-xs" },
19
+ React.createElement("div", { className: "flex items-center gap-3" },
20
+ React.createElement("span", { className: "uppercase tracking-wide" },
21
+ "Conversation: ",
22
+ activeConversation?.id || "None"),
23
+ React.createElement("span", { className: "text-[11px] text-gray-400" },
24
+ "Msgs: ",
25
+ currentMessages.length)),
26
+ React.createElement("div", { className: "flex items-center gap-2" },
27
+ React.createElement("span", { className: "px-2 py-0.5 rounded bg-gray-700 text-gray-200 text-[11px]" }, "Auto"),
28
+ React.createElement("span", { className: "px-2 py-0.5 rounded bg-gray-700 text-gray-200 text-[11px]" }, "Select"))));
29
+ return (React.createElement("div", { className: "flex-1 flex flex-col theme-bg-primary" },
30
+ renderHeader
31
+ ? renderHeader({ conversation: activeConversation, messageCount: currentMessages.length })
32
+ : defaultHeader,
33
+ React.createElement("div", { ref: listRef, className: "flex-1 overflow-y-auto p-4 flex flex-col gap-3 custom-scrollbar" },
34
+ currentMessages.map((m) => renderMessage ? renderMessage(m) : React.createElement(ChatMessageBubble, { key: m.id, message: m })),
35
+ loading && React.createElement(Spinner, null))));
36
+ };
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+ import type { Conversation } from "../../../core/chat";
3
+ type ConversationWithTimestamps = Conversation & {
4
+ timestamp?: string;
5
+ last_message_timestamp?: string;
6
+ };
7
+ type Props = {
8
+ conversations?: ConversationWithTimestamps[] | null;
9
+ activeConversationId?: string | null;
10
+ currentFile?: string | null;
11
+ conversationsCollapsed?: boolean;
12
+ onToggleCollapse?: (collapsed: boolean) => void;
13
+ selectedConvos?: Set<string> | null;
14
+ onSelectedConvosChange?: (set: Set<string>) => void;
15
+ lastClickedIndex?: number | null;
16
+ onLastClickedIndexChange?: (index: number | null) => void;
17
+ onConversationSelect?: (id: string) => void;
18
+ onRefresh?: () => void;
19
+ onConversationContextMenu?: (pos: {
20
+ x: number;
21
+ y: number;
22
+ }) => void;
23
+ onConversationDragStart?: (e: React.DragEvent, conversation: ConversationWithTimestamps) => void;
24
+ onConversationDragEnd?: (e: React.DragEvent, conversation: ConversationWithTimestamps) => void;
25
+ loading?: boolean;
26
+ };
27
+ export declare const ConversationList: React.FC<Props>;
28
+ export {};
@@ -0,0 +1,113 @@
1
+ import React, { useMemo, useState } from "react";
2
+ import { File, ChevronRight } from "lucide-react";
3
+ import { cn } from "../../utils/cn";
4
+ export const ConversationList = ({ conversations, activeConversationId = null, currentFile = null, conversationsCollapsed, onToggleCollapse, selectedConvos, onSelectedConvosChange, lastClickedIndex, onLastClickedIndexChange, onConversationSelect, onRefresh, onConversationContextMenu, onConversationDragStart, onConversationDragEnd, loading, }) => {
5
+ const [internalCollapsed, setInternalCollapsed] = useState(false);
6
+ const collapsed = conversationsCollapsed ?? internalCollapsed;
7
+ const toggleCollapsed = () => {
8
+ const next = !collapsed;
9
+ onToggleCollapse ? onToggleCollapse(next) : setInternalCollapsed(next);
10
+ };
11
+ const [localSelected, setLocalSelected] = useState(new Set());
12
+ const selected = selectedConvos ?? localSelected;
13
+ const setSelected = (s) => {
14
+ onSelectedConvosChange ? onSelectedConvosChange(s) : setLocalSelected(s);
15
+ };
16
+ const [localLastIndex, setLocalLastIndex] = useState(null);
17
+ const lastIndex = lastClickedIndex ?? localLastIndex;
18
+ const setLastIndex = (i) => {
19
+ onLastClickedIndexChange ? onLastClickedIndexChange(i) : setLocalLastIndex(i);
20
+ };
21
+ const convArray = useMemo(() => {
22
+ if (!Array.isArray(conversations))
23
+ return [];
24
+ return [...conversations].sort((a, b) => {
25
+ const aTimestamp = new Date(a.last_message_timestamp || a.timestamp || 0).getTime();
26
+ const bTimestamp = new Date(b.last_message_timestamp || b.timestamp || 0).getTime();
27
+ return bTimestamp - aTimestamp;
28
+ });
29
+ }, [conversations]);
30
+ const header = (React.createElement("div", { className: "flex items-center justify-between px-4 py-2 mt-4" },
31
+ React.createElement("div", { className: "text-xs text-gray-500 font-medium" },
32
+ "Conversations (",
33
+ convArray.length,
34
+ ")"),
35
+ React.createElement("div", { className: "flex items-center gap-1" },
36
+ onRefresh && (React.createElement("button", { onClick: (e) => {
37
+ e.stopPropagation();
38
+ onRefresh();
39
+ }, className: "p-1 theme-hover rounded-full transition-all", title: "Refresh conversations" },
40
+ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" },
41
+ React.createElement("path", { d: "M21.5 2v6h-6M2.5 22v-6h6M2 11.5a10 10 0 0 1 18.44-4.5M22 12.5a10 10 0 0 1-18.44 4.5" })))),
42
+ React.createElement("button", { onClick: (e) => {
43
+ e.stopPropagation();
44
+ toggleCollapsed();
45
+ }, className: "p-1 theme-hover rounded-full transition-all", title: collapsed ? "Expand conversations" : "Collapse conversations" },
46
+ React.createElement(ChevronRight, { size: 16, className: `transform transition-transform ${collapsed ? "" : "rotate-90"}` })))));
47
+ if (!convArray.length) {
48
+ return (React.createElement("div", { className: "mt-4" },
49
+ header,
50
+ React.createElement("div", { className: "px-4 py-2 text-xs text-gray-500" }, "No conversations.")));
51
+ }
52
+ if (collapsed) {
53
+ const activeConversation = activeConversationId ? convArray.find((c) => c.id === activeConversationId) : null;
54
+ return (React.createElement("div", { className: "mt-4" },
55
+ header,
56
+ activeConversation && !currentFile && (React.createElement("div", { className: "px-1 mt-1" },
57
+ React.createElement("button", { key: activeConversation.id, onClick: () => onConversationSelect?.(activeConversation.id), className: "flex items-center gap-2 px-4 py-2 w-full theme-hover text-left rounded-lg transition-all duration-200 conversation-selected border-l-2 border-blue-500" },
58
+ React.createElement(File, { size: 16, className: "text-gray-400 flex-shrink-0" }),
59
+ React.createElement("div", { className: "flex flex-col overflow-hidden" },
60
+ React.createElement("span", { className: "text-sm truncate" }, activeConversation.title || activeConversation.id),
61
+ React.createElement("span", { className: "text-xs text-gray-500" }, activeConversation.timestamp
62
+ ? new Date(activeConversation.timestamp).toLocaleString()
63
+ : "")))))));
64
+ }
65
+ return (React.createElement("div", { className: "mt-4" },
66
+ header,
67
+ React.createElement("div", { className: "px-1" }, convArray.map((conv, index) => {
68
+ const isSelected = selected?.has(conv.id);
69
+ const isActive = conv.id === activeConversationId && !currentFile;
70
+ return (React.createElement("button", { key: conv.id, draggable: "true", onDragStart: (e) => {
71
+ e.dataTransfer.effectAllowed = "copyMove";
72
+ onConversationDragStart?.(e, conv);
73
+ }, onDragEnd: (e) => onConversationDragEnd?.(e, conv), onClick: (e) => {
74
+ if (e.ctrlKey || e.metaKey) {
75
+ const next = new Set(selected || new Set());
76
+ if (next.has(conv.id))
77
+ next.delete(conv.id);
78
+ else
79
+ next.add(conv.id);
80
+ setSelected(next);
81
+ setLastIndex(index);
82
+ }
83
+ else if (e.shiftKey && lastIndex !== null) {
84
+ const next = new Set();
85
+ const start = Math.min(lastIndex, index);
86
+ const end = Math.max(lastIndex, index);
87
+ for (let i = start; i <= end; i++) {
88
+ if (convArray[i])
89
+ next.add(convArray[i].id);
90
+ }
91
+ setSelected(next);
92
+ }
93
+ else {
94
+ setSelected(new Set([conv.id]));
95
+ onConversationSelect?.(conv.id);
96
+ setLastIndex(index);
97
+ }
98
+ }, onContextMenu: (e) => {
99
+ e.preventDefault();
100
+ if (!selected?.has(conv.id)) {
101
+ const next = new Set([conv.id]);
102
+ setSelected(next);
103
+ }
104
+ onConversationContextMenu?.({ x: e.clientX, y: e.clientY });
105
+ }, className: cn("flex items-center gap-2 px-4 py-2 w-full theme-hover text-left rounded-lg transition-all duration-200", isSelected || isActive ? "conversation-selected" : "theme-text-primary", isActive ? "border-l-2 border-blue-500" : "") },
106
+ React.createElement(File, { size: 16, className: "text-gray-400 flex-shrink-0" }),
107
+ React.createElement("div", { className: "flex flex-col overflow-hidden" },
108
+ React.createElement("span", { className: "text-sm truncate" }, conv.title || conv.id),
109
+ React.createElement("span", { className: "text-xs text-gray-500" }, conv.timestamp
110
+ ? new Date(conv.timestamp).toLocaleString()
111
+ : ""))));
112
+ }))));
113
+ };
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ interface Props {
3
+ searchTerm: string;
4
+ onSearchTermChange: (value: string) => void;
5
+ onNext: () => void;
6
+ onPrevious: () => void;
7
+ onClose: () => void;
8
+ resultCount: number;
9
+ currentIndex: number;
10
+ }
11
+ export declare const InPaneSearchBar: React.FC<Props>;
12
+ export {};
@@ -0,0 +1,44 @@
1
+ import React, { useEffect, useRef, useState } from "react";
2
+ import { ChevronLeft, ChevronRight, X } from "lucide-react";
3
+ export const InPaneSearchBar = ({ searchTerm, onSearchTermChange, onNext, onPrevious, onClose, resultCount, currentIndex, }) => {
4
+ const inputRef = useRef(null);
5
+ const [localInputTerm, setLocalInputTerm] = useState(searchTerm);
6
+ useEffect(() => {
7
+ if (inputRef.current) {
8
+ inputRef.current.focus();
9
+ inputRef.current.setSelectionRange(localInputTerm.length, localInputTerm.length);
10
+ }
11
+ }, [localInputTerm]);
12
+ useEffect(() => {
13
+ if (localInputTerm !== searchTerm) {
14
+ setLocalInputTerm(searchTerm);
15
+ }
16
+ }, [searchTerm, localInputTerm]);
17
+ const handleKeyDown = (e) => {
18
+ if (e.key === "Enter") {
19
+ e.preventDefault();
20
+ if (e.shiftKey) {
21
+ onPrevious();
22
+ }
23
+ else {
24
+ onNext();
25
+ }
26
+ }
27
+ if (e.key === "Escape") {
28
+ onClose();
29
+ }
30
+ };
31
+ return (React.createElement("div", { className: "flex items-center gap-2 w-full theme-bg-tertiary p-2 rounded-lg" },
32
+ React.createElement("input", { ref: inputRef, type: "text", value: localInputTerm, onChange: (e) => {
33
+ setLocalInputTerm(e.target.value);
34
+ onSearchTermChange(e.target.value);
35
+ }, className: "flex-1 theme-input text-xs rounded px-3 py-2 border-0 focus:ring-1 focus:ring-blue-500", placeholder: "Search messages...", onKeyDown: handleKeyDown }),
36
+ React.createElement("span", { className: "text-xs theme-text-muted min-w-[60px] text-center" }, resultCount > 0 ? `${currentIndex + 1} of ${resultCount}` : "No results"),
37
+ React.createElement("div", { className: "flex items-center gap-1" },
38
+ React.createElement("button", { onClick: onPrevious, disabled: resultCount === 0, className: "p-2 theme-hover rounded disabled:opacity-50", title: "Previous (Shift+Enter)" },
39
+ React.createElement(ChevronLeft, { size: 14 })),
40
+ React.createElement("button", { onClick: onNext, disabled: resultCount === 0, className: "p-2 theme-hover rounded disabled:opacity-50", title: "Next (Enter)" },
41
+ React.createElement(ChevronRight, { size: 14 })),
42
+ React.createElement("button", { onClick: onClose, className: "p-2 theme-hover rounded text-red-400", title: "Close search (Escape)" },
43
+ React.createElement(X, { size: 14 })))));
44
+ };
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import type { ModelInfo } from "../../../core/types";
3
+ interface Props {
4
+ models?: ModelInfo[];
5
+ }
6
+ export declare const InputArea: React.FC<Props>;
7
+ export {};
@@ -0,0 +1,46 @@
1
+ import React, { useMemo, useRef, useState } from "react";
2
+ import { useChatContext } from "../context/ChatContext";
3
+ export const InputArea = ({ models }) => {
4
+ const { send, streaming, attachments, setAttachments } = useChatContext();
5
+ const [value, setValue] = useState("");
6
+ const [selectedModelId, setSelectedModelId] = useState(models?.[0]?.id);
7
+ const fileInputRef = useRef(null);
8
+ const selectedModel = useMemo(() => models?.find((m) => m.id === selectedModelId) ?? selectedModelId ?? "", [models, selectedModelId]);
9
+ const onSend = async () => {
10
+ if (!value.trim())
11
+ return;
12
+ await send({
13
+ prompt: value,
14
+ model: selectedModel || "",
15
+ attachments,
16
+ stream: true,
17
+ });
18
+ setValue("");
19
+ setAttachments([]);
20
+ };
21
+ return (React.createElement("div", { className: "border-t theme-border p-3 space-y-2 theme-bg-tertiary" },
22
+ React.createElement("div", { className: "flex items-center gap-2" },
23
+ models?.length ? (React.createElement("select", { value: selectedModelId, onChange: (e) => setSelectedModelId(e.target.value), className: "text-xs px-2 py-1 rounded theme-input bg-transparent" }, models.map((m) => (React.createElement("option", { key: m.id, value: m.id }, m.displayName || m.id))))) : null,
24
+ React.createElement("button", { className: "px-2 py-1 text-xs theme-button theme-hover rounded", onClick: () => fileInputRef.current?.click() }, "Attach"),
25
+ React.createElement("input", { ref: fileInputRef, type: "file", multiple: true, onChange: (e) => {
26
+ const files = e.target.files;
27
+ if (!files)
28
+ return;
29
+ const next = Array.from(files).map((f) => ({
30
+ id: `${f.name}-${f.size}-${f.lastModified}`,
31
+ name: f.name,
32
+ path: f.path || f.name,
33
+ sizeBytes: f.size,
34
+ }));
35
+ setAttachments((prev) => [...prev, ...next]);
36
+ e.target.value = "";
37
+ }, className: "hidden" }),
38
+ attachments.length > 0 && (React.createElement("div", { className: "text-[11px] theme-text-muted" },
39
+ attachments.length,
40
+ " file",
41
+ attachments.length > 1 ? "s" : "",
42
+ " attached"))),
43
+ React.createElement("textarea", { value: value, onChange: (e) => setValue(e.target.value), placeholder: streaming ? "Streaming response..." : "Type a message or drop files...", rows: 3, className: `chat-input-textarea w-full theme-input text-sm rounded-lg pl-4 pr-20 py-3 focus:outline-none border-0 resize-none ${streaming ? "opacity-70 cursor-not-allowed" : ""}`, disabled: streaming }),
44
+ React.createElement("div", { className: "flex items-center gap-2 justify-end" },
45
+ React.createElement("button", { onClick: onSend, disabled: streaming || !value.trim(), className: "px-3 py-1.5 rounded theme-button-primary text-sm font-semibold disabled:opacity-50" }, streaming ? "Sending..." : "Send"))));
46
+ };
@@ -0,0 +1,5 @@
1
+ import React from "react";
2
+ import type { Attachment } from "../../../core/types";
3
+ export declare const AttachmentList: React.FC<{
4
+ attachments: Attachment[];
5
+ }>;
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ export const AttachmentList = ({ attachments }) => {
3
+ if (!attachments.length)
4
+ return null;
5
+ return (React.createElement("div", { className: "flex flex-wrap gap-2 text-xs text-gray-600 dark:text-gray-300" }, attachments.map((att) => (React.createElement("a", { key: att.id, href: att.path, className: "px-2 py-1 rounded bg-gray-200 dark:bg-gray-700 hover:underline", target: "_blank", rel: "noreferrer" }, att.name)))));
6
+ };
@@ -0,0 +1,28 @@
1
+ import React from "react";
2
+ type Message = {
3
+ id?: string;
4
+ timestamp?: string;
5
+ role: string;
6
+ content?: string;
7
+ npc?: string;
8
+ model?: string;
9
+ isStreaming?: boolean;
10
+ type?: string;
11
+ attachments?: any[];
12
+ tool_calls?: any[];
13
+ tool_use_history?: any[];
14
+ };
15
+ interface Props {
16
+ message: Message;
17
+ isSelected: boolean;
18
+ messageSelectionMode: boolean;
19
+ toggleMessageSelection: (id: string) => void;
20
+ handleMessageContextMenu: (e: React.MouseEvent, id: string) => void;
21
+ searchTerm: string;
22
+ isCurrentSearchResult: boolean;
23
+ onResendMessage?: (m: Message) => void;
24
+ onCreateBranch?: (idx: number) => void;
25
+ messageIndex: number;
26
+ }
27
+ export declare const MessageItem: React.FC<Props>;
28
+ export {};