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.
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/dist/adapters/base.d.ts +13 -0
- package/dist/adapters/base.js +1 -0
- package/dist/adapters/electron/bridge.d.ts +4 -0
- package/dist/adapters/electron/bridge.js +88 -0
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.js +3 -0
- package/dist/core/browser.d.ts +25 -0
- package/dist/core/browser.js +1 -0
- package/dist/core/chat.d.ts +41 -0
- package/dist/core/chat.js +1 -0
- package/dist/core/database.d.ts +56 -0
- package/dist/core/database.js +50 -0
- package/dist/core/files.d.ts +24 -0
- package/dist/core/files.js +15 -0
- package/dist/core/index.d.ts +9 -0
- package/dist/core/index.js +10 -0
- package/dist/core/jobs.d.ts +20 -0
- package/dist/core/jobs.js +1 -0
- package/dist/core/layout.d.ts +30 -0
- package/dist/core/layout.js +41 -0
- package/dist/core/stream.d.ts +10 -0
- package/dist/core/stream.js +1 -0
- package/dist/core/types.d.ts +23 -0
- package/dist/core/types.js +1 -0
- package/dist/core/utils.d.ts +19 -0
- package/dist/core/utils.js +19 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +29 -0
- package/dist/ui/chat/components/ChatHeaderBar.d.ts +13 -0
- package/dist/ui/chat/components/ChatHeaderBar.js +32 -0
- package/dist/ui/chat/components/ChatInterface.d.ts +10 -0
- package/dist/ui/chat/components/ChatInterface.js +19 -0
- package/dist/ui/chat/components/ChatMessage.d.ts +7 -0
- package/dist/ui/chat/components/ChatMessage.js +10 -0
- package/dist/ui/chat/components/ChatPane.d.ts +20 -0
- package/dist/ui/chat/components/ChatPane.js +20 -0
- package/dist/ui/chat/components/ChatView.d.ts +10 -0
- package/dist/ui/chat/components/ChatView.js +36 -0
- package/dist/ui/chat/components/ConversationList.d.ts +28 -0
- package/dist/ui/chat/components/ConversationList.js +113 -0
- package/dist/ui/chat/components/InPaneSearchBar.d.ts +12 -0
- package/dist/ui/chat/components/InPaneSearchBar.js +44 -0
- package/dist/ui/chat/components/InputArea.d.ts +7 -0
- package/dist/ui/chat/components/InputArea.js +46 -0
- package/dist/ui/chat/components/MessageAttachments.d.ts +5 -0
- package/dist/ui/chat/components/MessageAttachments.js +6 -0
- package/dist/ui/chat/components/MessageItem.d.ts +28 -0
- package/dist/ui/chat/components/MessageItem.js +43 -0
- package/dist/ui/chat/components/PredictiveTextOverlay.d.ts +10 -0
- package/dist/ui/chat/components/PredictiveTextOverlay.js +75 -0
- package/dist/ui/chat/context/ChatContext.d.ts +33 -0
- package/dist/ui/chat/context/ChatContext.js +157 -0
- package/dist/ui/chat/hooks/useAutoScroll.d.ts +1 -0
- package/dist/ui/chat/hooks/useAutoScroll.js +11 -0
- package/dist/ui/chat/index.d.ts +7 -0
- package/dist/ui/chat/index.js +7 -0
- package/dist/ui/dashboard/ChartWidget.d.ts +18 -0
- package/dist/ui/dashboard/ChartWidget.js +98 -0
- package/dist/ui/dashboard/QueryWidget.d.ts +29 -0
- package/dist/ui/dashboard/QueryWidget.js +117 -0
- package/dist/ui/dashboard/TableWidget.d.ts +14 -0
- package/dist/ui/dashboard/TableWidget.js +117 -0
- package/dist/ui/dashboard/Widget.d.ts +40 -0
- package/dist/ui/dashboard/Widget.js +26 -0
- package/dist/ui/dashboard/WidgetBuilder.d.ts +47 -0
- package/dist/ui/dashboard/WidgetBuilder.js +286 -0
- package/dist/ui/dashboard/WidgetGrid.d.ts +19 -0
- package/dist/ui/dashboard/WidgetGrid.js +11 -0
- package/dist/ui/dashboard/index.d.ts +8 -0
- package/dist/ui/dashboard/index.js +4 -0
- package/dist/ui/dialogs/BrowserUrlDialog.d.ts +8 -0
- package/dist/ui/dialogs/BrowserUrlDialog.js +203 -0
- package/dist/ui/dialogs/index.d.ts +1 -0
- package/dist/ui/dialogs/index.js +1 -0
- package/dist/ui/editors/ImageEditor.d.ts +49 -0
- package/dist/ui/editors/ImageEditor.js +264 -0
- package/dist/ui/editors/index.d.ts +2 -0
- package/dist/ui/editors/index.js +1 -0
- package/dist/ui/execution/ExecutionFilters.d.ts +11 -0
- package/dist/ui/execution/ExecutionFilters.js +27 -0
- package/dist/ui/execution/ExecutionHistoryList.d.ts +19 -0
- package/dist/ui/execution/ExecutionHistoryList.js +35 -0
- package/dist/ui/execution/index.d.ts +3 -0
- package/dist/ui/execution/index.js +2 -0
- package/dist/ui/files/components/FileTree.d.ts +18 -0
- package/dist/ui/files/components/FileTree.js +61 -0
- package/dist/ui/files/components/Sidebar.d.ts +18 -0
- package/dist/ui/files/components/Sidebar.js +67 -0
- package/dist/ui/files/components/index.d.ts +2 -0
- package/dist/ui/files/components/index.js +2 -0
- package/dist/ui/files/context/FileSystemContext.d.ts +23 -0
- package/dist/ui/files/context/FileSystemContext.js +65 -0
- package/dist/ui/files/context/index.d.ts +1 -0
- package/dist/ui/files/context/index.js +1 -0
- package/dist/ui/files/index.d.ts +2 -0
- package/dist/ui/files/index.js +2 -0
- package/dist/ui/hooks/index.d.ts +4 -0
- package/dist/ui/hooks/index.js +3 -0
- package/dist/ui/hooks/useDebounce.d.ts +8 -0
- package/dist/ui/hooks/useDebounce.js +18 -0
- package/dist/ui/hooks/usePaneTracking.d.ts +15 -0
- package/dist/ui/hooks/usePaneTracking.js +20 -0
- package/dist/ui/hooks/useQuery.d.ts +25 -0
- package/dist/ui/hooks/useQuery.js +71 -0
- package/dist/ui/index.d.ts +19 -0
- package/dist/ui/index.js +20 -0
- package/dist/ui/jinx/JinxEditor.d.ts +20 -0
- package/dist/ui/jinx/JinxEditor.js +34 -0
- package/dist/ui/jinx/JinxTree.d.ts +14 -0
- package/dist/ui/jinx/JinxTree.js +65 -0
- package/dist/ui/jinx/index.d.ts +3 -0
- package/dist/ui/jinx/index.js +2 -0
- package/dist/ui/knowledge-graph/KGControls.d.ts +12 -0
- package/dist/ui/knowledge-graph/KGControls.js +21 -0
- package/dist/ui/knowledge-graph/KGStats.d.ts +13 -0
- package/dist/ui/knowledge-graph/KGStats.js +18 -0
- package/dist/ui/knowledge-graph/KnowledgeGraphViewer.d.ts +22 -0
- package/dist/ui/knowledge-graph/KnowledgeGraphViewer.js +16 -0
- package/dist/ui/knowledge-graph/index.d.ts +4 -0
- package/dist/ui/knowledge-graph/index.js +3 -0
- package/dist/ui/layout/components/AppShell.d.ts +8 -0
- package/dist/ui/layout/components/AppShell.js +19 -0
- package/dist/ui/layout/components/ContentPaneContainer.d.ts +8 -0
- package/dist/ui/layout/components/ContentPaneContainer.js +95 -0
- package/dist/ui/layout/components/LayoutNode.d.ts +8 -0
- package/dist/ui/layout/components/LayoutNode.js +12 -0
- package/dist/ui/layout/components/PaneHeader.d.ts +17 -0
- package/dist/ui/layout/components/PaneHeader.js +40 -0
- package/dist/ui/layout/components/SplitView.d.ts +8 -0
- package/dist/ui/layout/components/SplitView.js +51 -0
- package/dist/ui/layout/components/Studio.d.ts +7 -0
- package/dist/ui/layout/components/Studio.js +12 -0
- package/dist/ui/layout/components/contextMenus/BrowserContextMenu.d.ts +13 -0
- package/dist/ui/layout/components/contextMenus/BrowserContextMenu.js +23 -0
- package/dist/ui/layout/components/contextMenus/EditorContextMenu.d.ts +15 -0
- package/dist/ui/layout/components/contextMenus/EditorContextMenu.js +32 -0
- package/dist/ui/layout/components/contextMenus/FileContextMenu.d.ts +15 -0
- package/dist/ui/layout/components/contextMenus/FileContextMenu.js +33 -0
- package/dist/ui/layout/components/contextMenus/PdfContextMenu.d.ts +13 -0
- package/dist/ui/layout/components/contextMenus/PdfContextMenu.js +23 -0
- package/dist/ui/layout/components/contextMenus/index.d.ts +4 -0
- package/dist/ui/layout/components/contextMenus/index.js +4 -0
- package/dist/ui/layout/components/index.d.ts +8 -0
- package/dist/ui/layout/components/index.js +8 -0
- package/dist/ui/layout/components/modals/AIEditModal.d.ts +9 -0
- package/dist/ui/layout/components/modals/AIEditModal.js +33 -0
- package/dist/ui/layout/components/modals/MemoryApprovalModal.d.ts +15 -0
- package/dist/ui/layout/components/modals/MemoryApprovalModal.js +23 -0
- package/dist/ui/layout/components/modals/PromptModal.d.ts +11 -0
- package/dist/ui/layout/components/modals/PromptModal.js +20 -0
- package/dist/ui/layout/components/modals/ResendModal.d.ts +16 -0
- package/dist/ui/layout/components/modals/ResendModal.js +22 -0
- package/dist/ui/layout/components/modals/index.d.ts +4 -0
- package/dist/ui/layout/components/modals/index.js +4 -0
- package/dist/ui/layout/context/LayoutContext.d.ts +32 -0
- package/dist/ui/layout/context/LayoutContext.js +144 -0
- package/dist/ui/layout/index.d.ts +2 -0
- package/dist/ui/layout/index.js +2 -0
- package/dist/ui/markdown/Markdown.d.ts +4 -0
- package/dist/ui/markdown/Markdown.js +4 -0
- package/dist/ui/memory/MemoryFilters.d.ts +12 -0
- package/dist/ui/memory/MemoryFilters.js +23 -0
- package/dist/ui/memory/MemoryList.d.ts +19 -0
- package/dist/ui/memory/MemoryList.js +36 -0
- package/dist/ui/memory/index.d.ts +3 -0
- package/dist/ui/memory/index.js +2 -0
- package/dist/ui/models/ModelCard.d.ts +16 -0
- package/dist/ui/models/ModelCard.js +30 -0
- package/dist/ui/models/ModelSelector.d.ts +13 -0
- package/dist/ui/models/ModelSelector.js +6 -0
- package/dist/ui/models/index.d.ts +3 -0
- package/dist/ui/models/index.js +2 -0
- package/dist/ui/npc/McpServerMenu.d.ts +15 -0
- package/dist/ui/npc/McpServerMenu.js +48 -0
- package/dist/ui/npc/NPCEditor.d.ts +21 -0
- package/dist/ui/npc/NPCEditor.js +17 -0
- package/dist/ui/npc/NPCList.d.ts +19 -0
- package/dist/ui/npc/NPCList.js +28 -0
- package/dist/ui/npc/index.d.ts +3 -0
- package/dist/ui/npc/index.js +2 -0
- package/dist/ui/photo/GalleryGrid.d.ts +25 -0
- package/dist/ui/photo/GalleryGrid.js +46 -0
- package/dist/ui/photo/ImageAdjustmentSliders.d.ts +37 -0
- package/dist/ui/photo/ImageAdjustmentSliders.js +56 -0
- package/dist/ui/photo/ImageLabelingCanvas.d.ts +47 -0
- package/dist/ui/photo/ImageLabelingCanvas.js +174 -0
- package/dist/ui/photo/ImageSourceTabs.d.ts +28 -0
- package/dist/ui/photo/ImageSourceTabs.js +46 -0
- package/dist/ui/photo/LayerPanel.d.ts +87 -0
- package/dist/ui/photo/LayerPanel.js +70 -0
- package/dist/ui/photo/Lightbox.d.ts +12 -0
- package/dist/ui/photo/Lightbox.js +90 -0
- package/dist/ui/photo/index.d.ts +6 -0
- package/dist/ui/photo/index.js +7 -0
- package/dist/ui/primitives/AutosizeTextarea.d.ts +8 -0
- package/dist/ui/primitives/AutosizeTextarea.js +17 -0
- package/dist/ui/primitives/Button.d.ts +7 -0
- package/dist/ui/primitives/Button.js +17 -0
- package/dist/ui/primitives/Card.d.ts +12 -0
- package/dist/ui/primitives/Card.js +8 -0
- package/dist/ui/primitives/Chart.d.ts +34 -0
- package/dist/ui/primitives/Chart.js +140 -0
- package/dist/ui/primitives/ContextMenu.d.ts +17 -0
- package/dist/ui/primitives/ContextMenu.js +33 -0
- package/dist/ui/primitives/DataTable.d.ts +13 -0
- package/dist/ui/primitives/DataTable.js +13 -0
- package/dist/ui/primitives/FileUpload.d.ts +10 -0
- package/dist/ui/primitives/FileUpload.js +24 -0
- package/dist/ui/primitives/ImageGrid.d.ts +21 -0
- package/dist/ui/primitives/ImageGrid.js +29 -0
- package/dist/ui/primitives/Input.d.ts +7 -0
- package/dist/ui/primitives/Input.js +10 -0
- package/dist/ui/primitives/Lightbox.d.ts +12 -0
- package/dist/ui/primitives/Lightbox.js +36 -0
- package/dist/ui/primitives/Modal.d.ts +10 -0
- package/dist/ui/primitives/Modal.js +33 -0
- package/dist/ui/primitives/RangeSlider.d.ts +13 -0
- package/dist/ui/primitives/RangeSlider.js +6 -0
- package/dist/ui/primitives/Select.d.ts +11 -0
- package/dist/ui/primitives/Select.js +10 -0
- package/dist/ui/primitives/Slider.d.ts +13 -0
- package/dist/ui/primitives/Slider.js +8 -0
- package/dist/ui/primitives/SortableList.d.ts +9 -0
- package/dist/ui/primitives/SortableList.js +19 -0
- package/dist/ui/primitives/Spinner.d.ts +2 -0
- package/dist/ui/primitives/Spinner.js +2 -0
- package/dist/ui/primitives/StarRating.d.ts +10 -0
- package/dist/ui/primitives/StarRating.js +10 -0
- package/dist/ui/primitives/Tabs.d.ts +15 -0
- package/dist/ui/primitives/Tabs.js +11 -0
- package/dist/ui/primitives/TagInput.d.ts +10 -0
- package/dist/ui/primitives/TagInput.js +25 -0
- package/dist/ui/primitives/index.d.ts +20 -0
- package/dist/ui/primitives/index.js +19 -0
- package/dist/ui/specialized/DiskUsageAnalyzer.d.ts +12 -0
- package/dist/ui/specialized/DiskUsageAnalyzer.js +38 -0
- package/dist/ui/specialized/OllamaModelManager.d.ts +2 -0
- package/dist/ui/specialized/OllamaModelManager.js +48 -0
- package/dist/ui/specialized/RichTextEditor.d.ts +8 -0
- package/dist/ui/specialized/RichTextEditor.js +64 -0
- package/dist/ui/specialized/SlideCanvas.d.ts +24 -0
- package/dist/ui/specialized/SlideCanvas.js +31 -0
- package/dist/ui/specialized/SpreadsheetGrid.d.ts +18 -0
- package/dist/ui/specialized/SpreadsheetGrid.js +28 -0
- package/dist/ui/specialized/StepEditor.d.ts +12 -0
- package/dist/ui/specialized/StepEditor.js +22 -0
- package/dist/ui/specialized/TerminalEmbed.d.ts +13 -0
- package/dist/ui/specialized/TerminalEmbed.js +50 -0
- package/dist/ui/specialized/index.d.ts +7 -0
- package/dist/ui/specialized/index.js +7 -0
- package/dist/ui/sql/SQLQueryEditor.d.ts +9 -0
- package/dist/ui/sql/SQLQueryEditor.js +10 -0
- package/dist/ui/sql/SQLResultsTable.d.ts +6 -0
- package/dist/ui/sql/SQLResultsTable.js +12 -0
- package/dist/ui/sql/SQLSchemaViewer.d.ts +14 -0
- package/dist/ui/sql/SQLSchemaViewer.js +18 -0
- package/dist/ui/sql/index.d.ts +3 -0
- package/dist/ui/sql/index.js +3 -0
- package/dist/ui/utils/cn.d.ts +1 -0
- package/dist/ui/utils/cn.js +1 -0
- package/dist/ui/utils/fileIcons.d.ts +2 -0
- package/dist/ui/utils/fileIcons.js +24 -0
- package/dist/ui/utils/fileUtils.d.ts +4 -0
- package/dist/ui/utils/fileUtils.js +11 -0
- package/dist/ui/utils/index.d.ts +3 -0
- package/dist/ui/utils/index.js +3 -0
- package/dist/ui/viewers/components/BrowserViewer.d.ts +9 -0
- package/dist/ui/viewers/components/BrowserViewer.js +44 -0
- package/dist/ui/viewers/components/CodeEditor.d.ts +11 -0
- package/dist/ui/viewers/components/CodeEditor.js +115 -0
- package/dist/ui/viewers/components/CsvViewer.d.ts +7 -0
- package/dist/ui/viewers/components/CsvViewer.js +18 -0
- package/dist/ui/viewers/components/ImageViewer.d.ts +7 -0
- package/dist/ui/viewers/components/ImageViewer.js +23 -0
- package/dist/ui/viewers/components/PdfViewer.d.ts +7 -0
- package/dist/ui/viewers/components/PdfViewer.js +29 -0
- package/dist/ui/viewers/components/Terminal.d.ts +7 -0
- package/dist/ui/viewers/components/Terminal.js +31 -0
- package/dist/ui/viewers/components/index.d.ts +6 -0
- package/dist/ui/viewers/components/index.js +6 -0
- package/dist/ui/viewers/index.d.ts +1 -0
- package/dist/ui/viewers/index.js +1 -0
- 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
|
+
};
|
package/dist/index.d.ts
ADDED
|
@@ -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,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,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,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 {};
|