@townco/ui 0.1.47 → 0.1.49
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/dist/core/hooks/index.d.ts +1 -0
- package/dist/core/hooks/index.js +1 -0
- package/dist/core/hooks/use-chat-input.d.ts +2 -0
- package/dist/core/hooks/use-chat-input.js +11 -1
- package/dist/core/hooks/use-chat-messages.d.ts +22 -1
- package/dist/core/hooks/use-chat-messages.js +19 -4
- package/dist/core/hooks/use-chat-session.js +22 -0
- package/dist/core/hooks/use-message-history.d.ts +12 -0
- package/dist/core/hooks/use-message-history.js +113 -0
- package/dist/core/hooks/use-tool-calls.d.ts +11 -0
- package/dist/core/schemas/chat.d.ts +40 -0
- package/dist/core/schemas/chat.js +9 -0
- package/dist/core/schemas/tool-call.d.ts +34 -0
- package/dist/core/schemas/tool-call.js +27 -0
- package/dist/core/store/chat-store.d.ts +7 -0
- package/dist/core/store/chat-store.js +46 -0
- package/dist/gui/components/ChatEmptyState.d.ts +4 -0
- package/dist/gui/components/ChatEmptyState.js +2 -2
- package/dist/gui/components/ChatInput.d.ts +21 -1
- package/dist/gui/components/ChatInput.js +184 -7
- package/dist/gui/components/ChatLayout.d.ts +3 -2
- package/dist/gui/components/ChatLayout.js +7 -7
- package/dist/gui/components/ChatPanelTabContent.d.ts +17 -0
- package/dist/gui/components/ChatPanelTabContent.js +6 -5
- package/dist/gui/components/ChatView.d.ts +3 -1
- package/dist/gui/components/ChatView.js +81 -49
- package/dist/gui/components/ContextUsageButton.d.ts +2 -0
- package/dist/gui/components/ContextUsageButton.js +3 -1
- package/dist/gui/components/InvokingGroup.d.ts +9 -0
- package/dist/gui/components/InvokingGroup.js +16 -0
- package/dist/gui/components/MessageContent.js +122 -6
- package/dist/gui/components/PanelTabsHeader.d.ts +1 -1
- package/dist/gui/components/PanelTabsHeader.js +6 -1
- package/dist/gui/components/Response.js +2 -0
- package/dist/gui/components/Task.js +3 -3
- package/dist/gui/components/TodoListItem.js +1 -1
- package/dist/gui/components/ToolCall.js +57 -5
- package/dist/gui/components/ToolCallGroup.d.ts +8 -0
- package/dist/gui/components/ToolCallGroup.js +29 -0
- package/dist/gui/components/index.d.ts +1 -2
- package/dist/gui/components/index.js +1 -2
- package/dist/sdk/client/acp-client.d.ts +24 -1
- package/dist/sdk/client/acp-client.js +28 -7
- package/dist/sdk/schemas/message.d.ts +41 -9
- package/dist/sdk/schemas/message.js +15 -3
- package/dist/sdk/schemas/session.d.ts +75 -10
- package/dist/sdk/transports/http.d.ts +14 -0
- package/dist/sdk/transports/http.js +130 -36
- package/dist/sdk/transports/stdio.d.ts +14 -0
- package/dist/sdk/transports/stdio.js +27 -10
- package/dist/sdk/transports/types.d.ts +29 -0
- package/package.json +3 -3
- package/dist/core/hooks/index.d.ts.map +0 -1
- package/dist/core/hooks/index.js.map +0 -1
- package/dist/core/hooks/use-chat-input.d.ts.map +0 -1
- package/dist/core/hooks/use-chat-input.js.map +0 -1
- package/dist/core/hooks/use-chat-messages.d.ts.map +0 -1
- package/dist/core/hooks/use-chat-messages.js.map +0 -1
- package/dist/core/hooks/use-chat-session.d.ts.map +0 -1
- package/dist/core/hooks/use-chat-session.js.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js.map +0 -1
- package/dist/core/lib/logger.d.ts +0 -24
- package/dist/core/lib/logger.js +0 -108
- package/dist/core/schemas/chat.d.ts.map +0 -1
- package/dist/core/schemas/chat.js.map +0 -1
- package/dist/core/schemas/index.d.ts.map +0 -1
- package/dist/core/schemas/index.js.map +0 -1
- package/dist/core/store/chat-store.d.ts.map +0 -1
- package/dist/core/store/chat-store.js.map +0 -1
- package/dist/gui/components/Button.d.ts.map +0 -1
- package/dist/gui/components/Button.js.map +0 -1
- package/dist/gui/components/Card.d.ts.map +0 -1
- package/dist/gui/components/Card.js.map +0 -1
- package/dist/gui/components/ChatInput.d.ts.map +0 -1
- package/dist/gui/components/ChatInput.js.map +0 -1
- package/dist/gui/components/ChatSecondaryPanel.d.ts.map +0 -1
- package/dist/gui/components/ChatSecondaryPanel.js.map +0 -1
- package/dist/gui/components/ChatStatus.d.ts.map +0 -1
- package/dist/gui/components/ChatStatus.js.map +0 -1
- package/dist/gui/components/Conversation.d.ts.map +0 -1
- package/dist/gui/components/Conversation.js.map +0 -1
- package/dist/gui/components/Dialog.d.ts.map +0 -1
- package/dist/gui/components/Dialog.js.map +0 -1
- package/dist/gui/components/HeightTransition.d.ts.map +0 -1
- package/dist/gui/components/HeightTransition.js.map +0 -1
- package/dist/gui/components/Input.d.ts.map +0 -1
- package/dist/gui/components/Input.js.map +0 -1
- package/dist/gui/components/Label.d.ts.map +0 -1
- package/dist/gui/components/Label.js.map +0 -1
- package/dist/gui/components/MarkdownRenderer.d.ts.map +0 -1
- package/dist/gui/components/MarkdownRenderer.js.map +0 -1
- package/dist/gui/components/Message.d.ts.map +0 -1
- package/dist/gui/components/Message.js.map +0 -1
- package/dist/gui/components/MessageContent.d.ts.map +0 -1
- package/dist/gui/components/MessageContent.js.map +0 -1
- package/dist/gui/components/MessageList.d.ts.map +0 -1
- package/dist/gui/components/MessageList.js.map +0 -1
- package/dist/gui/components/Reasoning.d.ts.map +0 -1
- package/dist/gui/components/Reasoning.js.map +0 -1
- package/dist/gui/components/Response.d.ts.map +0 -1
- package/dist/gui/components/Response.js.map +0 -1
- package/dist/gui/components/Select.d.ts.map +0 -1
- package/dist/gui/components/Select.js.map +0 -1
- package/dist/gui/components/Tabs.d.ts.map +0 -1
- package/dist/gui/components/Tabs.js.map +0 -1
- package/dist/gui/components/Task.d.ts.map +0 -1
- package/dist/gui/components/Task.js.map +0 -1
- package/dist/gui/components/Textarea.d.ts.map +0 -1
- package/dist/gui/components/Textarea.js.map +0 -1
- package/dist/gui/components/ThinkingBlock.d.ts.map +0 -1
- package/dist/gui/components/ThinkingBlock.js.map +0 -1
- package/dist/gui/components/TodoList.d.ts.map +0 -1
- package/dist/gui/components/TodoList.js.map +0 -1
- package/dist/gui/components/TodoListItem.d.ts.map +0 -1
- package/dist/gui/components/TodoListItem.js.map +0 -1
- package/dist/gui/components/index.d.ts.map +0 -1
- package/dist/gui/components/index.js.map +0 -1
- package/dist/gui/index.d.ts.map +0 -1
- package/dist/gui/index.js.map +0 -1
- package/dist/gui/lib/utils.d.ts.map +0 -1
- package/dist/gui/lib/utils.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/sdk/client/acp-client.d.ts.map +0 -1
- package/dist/sdk/client/acp-client.js.map +0 -1
- package/dist/sdk/client/index.d.ts.map +0 -1
- package/dist/sdk/client/index.js.map +0 -1
- package/dist/sdk/index.d.ts.map +0 -1
- package/dist/sdk/index.js.map +0 -1
- package/dist/sdk/schemas/agent.d.ts.map +0 -1
- package/dist/sdk/schemas/agent.js.map +0 -1
- package/dist/sdk/schemas/index.d.ts.map +0 -1
- package/dist/sdk/schemas/index.js.map +0 -1
- package/dist/sdk/schemas/message.d.ts.map +0 -1
- package/dist/sdk/schemas/message.js.map +0 -1
- package/dist/sdk/schemas/session.d.ts.map +0 -1
- package/dist/sdk/schemas/session.js.map +0 -1
- package/dist/sdk/transports/http.d.ts.map +0 -1
- package/dist/sdk/transports/http.js.map +0 -1
- package/dist/sdk/transports/index.d.ts.map +0 -1
- package/dist/sdk/transports/index.js.map +0 -1
- package/dist/sdk/transports/stdio.d.ts.map +0 -1
- package/dist/sdk/transports/stdio.js.map +0 -1
- package/dist/sdk/transports/types.d.ts.map +0 -1
- package/dist/sdk/transports/types.js.map +0 -1
- package/dist/sdk/transports/websocket.d.ts.map +0 -1
- package/dist/sdk/transports/websocket.js.map +0 -1
- package/dist/test-http-client.d.ts +0 -11
- package/dist/test-http-client.d.ts.map +0 -1
- package/dist/test-http-client.js +0 -147
- package/dist/test-http-client.js.map +0 -1
- package/dist/test-http-transport.d.ts +0 -11
- package/dist/test-http-transport.d.ts.map +0 -1
- package/dist/test-http-transport.js +0 -127
- package/dist/test-http-transport.js.map +0 -1
- package/dist/tui/components/ChatView.d.ts.map +0 -1
- package/dist/tui/components/ChatView.js.map +0 -1
- package/dist/tui/components/GameOfLife.d.ts.map +0 -1
- package/dist/tui/components/GameOfLife.js.map +0 -1
- package/dist/tui/components/InputBox.d.ts.map +0 -1
- package/dist/tui/components/InputBox.js.map +0 -1
- package/dist/tui/components/MessageList.d.ts.map +0 -1
- package/dist/tui/components/MessageList.js.map +0 -1
- package/dist/tui/components/ReadlineInput.d.ts.map +0 -1
- package/dist/tui/components/ReadlineInput.js.map +0 -1
- package/dist/tui/components/StatusBar.d.ts.map +0 -1
- package/dist/tui/components/StatusBar.js.map +0 -1
- package/dist/tui/components/index.d.ts.map +0 -1
- package/dist/tui/components/index.js.map +0 -1
- package/dist/tui/index.d.ts.map +0 -1
- package/dist/tui/index.js.map +0 -1
|
@@ -2,6 +2,52 @@ import { createLogger } from "@townco/core";
|
|
|
2
2
|
import { create } from "zustand";
|
|
3
3
|
import { mergeToolCallUpdate } from "../schemas/tool-call.js";
|
|
4
4
|
const logger = createLogger("chat-store", "debug");
|
|
5
|
+
// Constants to avoid creating new empty arrays
|
|
6
|
+
const EMPTY_TODOS = [];
|
|
7
|
+
// Cache for memoized todos to prevent infinite re-render loops
|
|
8
|
+
let cachedTodos = {
|
|
9
|
+
sessionId: null,
|
|
10
|
+
toolCallId: null,
|
|
11
|
+
todos: EMPTY_TODOS,
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Selector to get todos for the current session (memoized to prevent infinite loops)
|
|
15
|
+
*/
|
|
16
|
+
export const selectTodosForCurrentSession = (state) => {
|
|
17
|
+
const sessionId = state.sessionId;
|
|
18
|
+
if (!sessionId)
|
|
19
|
+
return EMPTY_TODOS;
|
|
20
|
+
const sessionToolCalls = state.toolCalls[sessionId] || [];
|
|
21
|
+
// Find most recent TodoWrite tool call
|
|
22
|
+
const todoToolCalls = sessionToolCalls
|
|
23
|
+
.filter((tc) => tc.title === "todo_write")
|
|
24
|
+
.sort((a, b) => (b.startedAt || 0) - (a.startedAt || 0));
|
|
25
|
+
if (todoToolCalls.length === 0)
|
|
26
|
+
return EMPTY_TODOS;
|
|
27
|
+
const latestTodoCall = todoToolCalls[0];
|
|
28
|
+
if (!latestTodoCall?.rawInput?.todos ||
|
|
29
|
+
!Array.isArray(latestTodoCall.rawInput.todos)) {
|
|
30
|
+
return EMPTY_TODOS;
|
|
31
|
+
}
|
|
32
|
+
// Return cached result if the tool call hasn't changed
|
|
33
|
+
if (cachedTodos.sessionId === sessionId &&
|
|
34
|
+
cachedTodos.toolCallId === latestTodoCall.id) {
|
|
35
|
+
return cachedTodos.todos;
|
|
36
|
+
}
|
|
37
|
+
// Map tool schema to UI schema
|
|
38
|
+
const newTodos = latestTodoCall.rawInput.todos.map((todo, index) => ({
|
|
39
|
+
id: `${latestTodoCall.id}-${index}`,
|
|
40
|
+
text: todo.status === "in_progress" ? todo.activeForm : todo.content,
|
|
41
|
+
status: todo.status,
|
|
42
|
+
}));
|
|
43
|
+
// Update cache
|
|
44
|
+
cachedTodos = {
|
|
45
|
+
sessionId,
|
|
46
|
+
toolCallId: latestTodoCall.id,
|
|
47
|
+
todos: newTodos,
|
|
48
|
+
};
|
|
49
|
+
return newTodos;
|
|
50
|
+
};
|
|
5
51
|
/**
|
|
6
52
|
* Create chat store
|
|
7
53
|
*/
|
|
@@ -16,6 +16,10 @@ export interface ChatEmptyStateProps extends React.HTMLAttributes<HTMLDivElement
|
|
|
16
16
|
onGuideClick?: () => void;
|
|
17
17
|
/** Callback when "Open Files" is clicked */
|
|
18
18
|
onOpenFiles?: () => void;
|
|
19
|
+
/** Callback when "View Tools & MCPs" is clicked */
|
|
20
|
+
onOpenSettings?: () => void;
|
|
21
|
+
/** Count of tools and MCPs to display */
|
|
22
|
+
toolsAndMcpsCount?: number;
|
|
19
23
|
/** Callback when hovering over a prompt */
|
|
20
24
|
onPromptHover?: (prompt: string) => void;
|
|
21
25
|
/** Callback when mouse leaves a prompt */
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { ChevronRight } from "lucide-react";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { cn } from "../lib/utils.js";
|
|
5
|
-
export const ChatEmptyState = React.forwardRef(({ title, description, guideUrl, guideText = "Guide", suggestedPrompts = [], onPromptClick, onGuideClick, onOpenFiles, onPromptHover, onPromptLeave, className, ...props }, ref) => {
|
|
5
|
+
export const ChatEmptyState = React.forwardRef(({ title, description, guideUrl, guideText = "Guide", suggestedPrompts = [], onPromptClick, onGuideClick, onOpenFiles, onOpenSettings, toolsAndMcpsCount, onPromptHover, onPromptLeave, className, ...props }, ref) => {
|
|
6
6
|
const handlePromptClick = (prompt) => {
|
|
7
7
|
onPromptClick?.(prompt);
|
|
8
8
|
};
|
|
@@ -17,6 +17,6 @@ export const ChatEmptyState = React.forwardRef(({ title, description, guideUrl,
|
|
|
17
17
|
for (let i = 0; i < suggestedPrompts.length; i += 2) {
|
|
18
18
|
promptRows.push(suggestedPrompts.slice(i, i + 2));
|
|
19
19
|
}
|
|
20
|
-
return (_jsxs("div", { ref: ref, className: cn("flex flex-col items-start", className), ...props, children: [_jsx("h3", { className: "text-heading-4 text-text-primary mb-6", children: title }), _jsx("p", { className: "text-subheading text-text-secondary max-w-prose mb-6", children: description }), onOpenFiles && (_jsxs("button", { type: "button", onClick: onOpenFiles, className: "inline-flex items-center gap-1 py-1.5 pr-3 -
|
|
20
|
+
return (_jsxs("div", { ref: ref, className: cn("flex flex-col items-start", className), ...props, children: [_jsx("h3", { className: "text-heading-4 text-text-primary mb-6", children: title }), _jsx("p", { className: "text-subheading text-text-secondary max-w-prose mb-6", children: description }), (onOpenFiles || onOpenSettings) && (_jsxs("div", { className: "flex items-center gap-1 -ml-3 mb-6", children: [onOpenFiles && (_jsxs("button", { type: "button", onClick: onOpenFiles, className: "inline-flex items-center gap-1 py-1.5 pr-3 pl-3 rounded-lg hover:bg-accent transition-colors", children: [_jsx("span", { className: "text-paragraph-sm-medium text-foreground tracking-wide leading-none", children: "View Files" }), _jsx(ChevronRight, { className: "size-4 text-foreground shrink-0" })] })), onOpenSettings && (_jsxs("button", { type: "button", onClick: onOpenSettings, className: "inline-flex items-center gap-1 py-1.5 pr-3 pl-3 rounded-lg hover:bg-accent transition-colors", children: [_jsxs("span", { className: "text-paragraph-sm-medium text-foreground tracking-wide leading-none", children: ["View Tools & MCPs", toolsAndMcpsCount !== undefined && toolsAndMcpsCount > 0 && (_jsxs("span", { className: "ml-1", children: ["(", toolsAndMcpsCount, ")"] }))] }), _jsx(ChevronRight, { className: "size-4 text-foreground shrink-0" })] }))] })), (guideUrl || onGuideClick) && (_jsxs("button", { type: "button", onClick: handleGuideClick, className: "inline-flex items-center gap-1 py-1.5 pr-3 -ml-3 pl-3 rounded-lg hover:bg-accent transition-colors", children: [_jsx("span", { className: "text-paragraph-sm-medium text-foreground tracking-wide leading-none", children: guideText }), _jsx(ChevronRight, { className: "size-4 text-foreground shrink-0" })] })), suggestedPrompts.length > 0 && (_jsxs("div", { className: "flex flex-col gap-3 w-full max-w-prompt-container", children: [_jsx("p", { className: "text-label text-text-tertiary", children: "Suggested Prompts" }), _jsx("div", { className: "flex flex-col gap-2.5", children: promptRows.map((row) => (_jsx("div", { className: "flex gap-2.5 items-center", children: row.map((prompt) => (_jsx("button", { type: "button", onClick: () => handlePromptClick(prompt), onMouseEnter: () => onPromptHover?.(prompt), onMouseLeave: () => onPromptLeave?.(), className: "flex-1 flex items-start gap-2 p-3 bg-secondary hover:bg-secondary/80 rounded-2xl transition-colors min-w-0", children: _jsx("span", { className: "text-paragraph font-normal leading-normal text-text-tertiary truncate", children: prompt }) }, prompt))) }, row.join("-")))) })] }))] }));
|
|
21
21
|
});
|
|
22
22
|
ChatEmptyState.displayName = "ChatEmptyState";
|
|
@@ -35,6 +35,16 @@ export interface ChatInputRootProps extends Omit<React.FormHTMLAttributes<HTMLFo
|
|
|
35
35
|
declare const ChatInputRoot: React.ForwardRefExoticComponent<ChatInputRootProps & React.RefAttributes<HTMLFormElement>>;
|
|
36
36
|
export interface ChatInputFieldProps extends Omit<React.ComponentPropsWithoutRef<typeof Textarea>, "value" | "onChange"> {
|
|
37
37
|
asChild?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Callback when files are dropped
|
|
40
|
+
*/
|
|
41
|
+
onFilesDropped?: (files: Array<{
|
|
42
|
+
name: string;
|
|
43
|
+
path: string;
|
|
44
|
+
size: number;
|
|
45
|
+
mimeType: string;
|
|
46
|
+
data: string;
|
|
47
|
+
}>) => void;
|
|
38
48
|
}
|
|
39
49
|
declare const ChatInputField: React.ForwardRefExoticComponent<ChatInputFieldProps & React.RefAttributes<HTMLTextAreaElement>>;
|
|
40
50
|
export interface ChatInputSubmitProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
@@ -48,8 +58,18 @@ export interface ChatInputActionsProps extends React.ButtonHTMLAttributes<HTMLBu
|
|
|
48
58
|
asChild?: boolean;
|
|
49
59
|
}
|
|
50
60
|
declare const ChatInputActions: React.ForwardRefExoticComponent<ChatInputActionsProps & React.RefAttributes<HTMLButtonElement>>;
|
|
51
|
-
export interface ChatInputAttachmentProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
61
|
+
export interface ChatInputAttachmentProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "onChange"> {
|
|
52
62
|
asChild?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Callback when files are selected
|
|
65
|
+
*/
|
|
66
|
+
onFilesSelected?: (files: Array<{
|
|
67
|
+
name: string;
|
|
68
|
+
path: string;
|
|
69
|
+
size: number;
|
|
70
|
+
mimeType: string;
|
|
71
|
+
data: string;
|
|
72
|
+
}>) => void;
|
|
53
73
|
}
|
|
54
74
|
declare const ChatInputAttachment: React.ForwardRefExoticComponent<ChatInputAttachmentProps & React.RefAttributes<HTMLButtonElement>>;
|
|
55
75
|
export interface ChatInputVoiceInputProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Slot } from "@radix-ui/react-slot";
|
|
3
3
|
import { Mic, Paperclip, SquareSlash } from "lucide-react";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { useChatInput as useCoreChatInput } from "../../core/hooks/use-chat-input.js";
|
|
6
|
+
import { useMessageHistory } from "../../core/hooks/use-message-history.js";
|
|
6
7
|
import { useChatStore } from "../../core/store/chat-store.js";
|
|
7
8
|
import { cn } from "../lib/utils.js";
|
|
8
9
|
import { Button } from "./Button.js";
|
|
@@ -22,18 +23,28 @@ const ChatInputRoot = React.forwardRef(({ client, startSession, value: valueProp
|
|
|
22
23
|
// Always call hooks unconditionally (React rules)
|
|
23
24
|
const hookData = useCoreChatInput(client ?? null, startSession ?? dummyStartSession);
|
|
24
25
|
const storeIsStreaming = useChatStore((state) => state.isStreaming);
|
|
26
|
+
// Message history hook
|
|
27
|
+
const { addToHistory, navigatePrevious, navigateNext, resetNavigation } = useMessageHistory();
|
|
25
28
|
// Choose data source based on whether client is provided
|
|
26
29
|
const useHookData = client && startSession;
|
|
27
30
|
const value = useHookData ? hookData.value : valueProp || "";
|
|
28
31
|
const onChange = useHookData
|
|
29
32
|
? hookData.onChange
|
|
30
33
|
: onChangeProp || (() => { });
|
|
31
|
-
const
|
|
34
|
+
const baseOnSubmit = useHookData
|
|
32
35
|
? hookData.onSubmit
|
|
33
36
|
: onSubmitProp || (async () => { });
|
|
34
37
|
const isSubmitting = useHookData
|
|
35
38
|
? hookData.isSubmitting || storeIsStreaming
|
|
36
39
|
: isSubmittingProp || false;
|
|
40
|
+
// Wrap onSubmit to add messages to history
|
|
41
|
+
const onSubmit = React.useCallback(async () => {
|
|
42
|
+
const messageToSave = value.trim();
|
|
43
|
+
if (messageToSave) {
|
|
44
|
+
addToHistory(messageToSave);
|
|
45
|
+
}
|
|
46
|
+
await baseOnSubmit();
|
|
47
|
+
}, [value, addToHistory, baseOnSubmit]);
|
|
37
48
|
// Command menu state
|
|
38
49
|
const [showCommandMenu, setShowCommandMenu] = React.useState(false);
|
|
39
50
|
const [commandMenuQuery, setCommandMenuQuery] = React.useState("");
|
|
@@ -112,12 +123,16 @@ const ChatInputRoot = React.forwardRef(({ client, startSession, value: valueProp
|
|
|
112
123
|
setMenuItemCount,
|
|
113
124
|
triggerMenuSelect,
|
|
114
125
|
triggerCounter,
|
|
126
|
+
navigateHistoryPrevious: navigatePrevious,
|
|
127
|
+
navigateHistoryNext: navigateNext,
|
|
128
|
+
resetHistoryNavigation: resetNavigation,
|
|
115
129
|
}, children: _jsx("form", { ref: ref, onSubmit: handleSubmit, onClick: handleFormClick, onKeyDown: handleFormKeyDown, className: cn("relative w-full divide-y rounded-xl border bg-background shadow-md", className), ...props, children: children }) }));
|
|
116
130
|
});
|
|
117
131
|
ChatInputRoot.displayName = "ChatInput.Root";
|
|
118
|
-
const ChatInputField = React.forwardRef(({ asChild = false, className, onKeyDown, children, ...props }, ref) => {
|
|
119
|
-
const { value, onChange, onSubmit, disabled, isSubmitting, submitOnEnter, showCommandMenu, setShowCommandMenu, setCommandMenuQuery, setSelectedMenuIndex, menuItemCount, triggerMenuSelect, } = useChatInputContext();
|
|
132
|
+
const ChatInputField = React.forwardRef(({ asChild = false, className, onKeyDown, onFilesDropped, children, ...props }, ref) => {
|
|
133
|
+
const { value, onChange, onSubmit, disabled, isSubmitting, submitOnEnter, showCommandMenu, setShowCommandMenu, setCommandMenuQuery, setSelectedMenuIndex, menuItemCount, triggerMenuSelect, navigateHistoryPrevious, navigateHistoryNext, resetHistoryNavigation, } = useChatInputContext();
|
|
120
134
|
const textareaRef = React.useRef(null);
|
|
135
|
+
const [isDragging, setIsDragging] = React.useState(false);
|
|
121
136
|
const handleRef = React.useCallback((node) => {
|
|
122
137
|
textareaRef.current = node;
|
|
123
138
|
if (typeof ref === "function") {
|
|
@@ -127,6 +142,65 @@ const ChatInputField = React.forwardRef(({ asChild = false, className, onKeyDown
|
|
|
127
142
|
ref.current = node;
|
|
128
143
|
}
|
|
129
144
|
}, [ref]);
|
|
145
|
+
/**
|
|
146
|
+
* Convert file to base64
|
|
147
|
+
*/
|
|
148
|
+
const fileToBase64 = (file) => {
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
const reader = new FileReader();
|
|
151
|
+
reader.readAsDataURL(file);
|
|
152
|
+
reader.onload = () => {
|
|
153
|
+
const base64 = reader.result.split(",")[1];
|
|
154
|
+
resolve(base64 || "");
|
|
155
|
+
};
|
|
156
|
+
reader.onerror = (error) => reject(error);
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Handle file drop
|
|
161
|
+
*/
|
|
162
|
+
const handleDrop = async (e) => {
|
|
163
|
+
e.preventDefault();
|
|
164
|
+
setIsDragging(false);
|
|
165
|
+
const files = e.dataTransfer.files;
|
|
166
|
+
if (!files || files.length === 0 || !onFilesDropped)
|
|
167
|
+
return;
|
|
168
|
+
const processedFiles = [];
|
|
169
|
+
for (const file of Array.from(files)) {
|
|
170
|
+
if (!file.type.startsWith("image/"))
|
|
171
|
+
continue;
|
|
172
|
+
try {
|
|
173
|
+
const base64Data = await fileToBase64(file);
|
|
174
|
+
processedFiles.push({
|
|
175
|
+
name: file.name,
|
|
176
|
+
path: file.name,
|
|
177
|
+
size: file.size,
|
|
178
|
+
mimeType: file.type,
|
|
179
|
+
data: base64Data,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
console.error("Failed to process dropped file:", file.name, error);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (processedFiles.length > 0) {
|
|
187
|
+
onFilesDropped(processedFiles);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* Handle drag over (required to enable drop)
|
|
192
|
+
*/
|
|
193
|
+
const handleDragOver = (e) => {
|
|
194
|
+
e.preventDefault();
|
|
195
|
+
setIsDragging(true);
|
|
196
|
+
};
|
|
197
|
+
/**
|
|
198
|
+
* Handle drag leave
|
|
199
|
+
*/
|
|
200
|
+
const handleDragLeave = (e) => {
|
|
201
|
+
e.preventDefault();
|
|
202
|
+
setIsDragging(false);
|
|
203
|
+
};
|
|
130
204
|
const handleKeyDown = (e) => {
|
|
131
205
|
// Handle arrow keys and Enter when command menu is open
|
|
132
206
|
if (showCommandMenu && menuItemCount > 0) {
|
|
@@ -152,6 +226,46 @@ const ChatInputField = React.forwardRef(({ asChild = false, className, onKeyDown
|
|
|
152
226
|
return;
|
|
153
227
|
}
|
|
154
228
|
}
|
|
229
|
+
// Handle history navigation with up/down arrows when command menu is not open
|
|
230
|
+
// Only navigate history when cursor is at start/end of input or input is empty
|
|
231
|
+
if (!showCommandMenu) {
|
|
232
|
+
const textarea = textareaRef.current;
|
|
233
|
+
const cursorAtStart = textarea?.selectionStart === 0 && textarea?.selectionEnd === 0;
|
|
234
|
+
const cursorAtEnd = textarea?.selectionStart === value.length &&
|
|
235
|
+
textarea?.selectionEnd === value.length;
|
|
236
|
+
const isMultiline = value.includes("\n");
|
|
237
|
+
if (e.key === "ArrowUp" && (cursorAtStart || !value || !isMultiline)) {
|
|
238
|
+
const previousMessage = navigateHistoryPrevious(value);
|
|
239
|
+
if (previousMessage !== null) {
|
|
240
|
+
e.preventDefault();
|
|
241
|
+
onChange(previousMessage);
|
|
242
|
+
// Move cursor to end after a tick
|
|
243
|
+
setTimeout(() => {
|
|
244
|
+
if (textareaRef.current) {
|
|
245
|
+
textareaRef.current.selectionStart = previousMessage.length;
|
|
246
|
+
textareaRef.current.selectionEnd = previousMessage.length;
|
|
247
|
+
}
|
|
248
|
+
}, 0);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
else if (e.key === "ArrowDown" &&
|
|
253
|
+
(cursorAtEnd || !value || !isMultiline)) {
|
|
254
|
+
const nextMessage = navigateHistoryNext();
|
|
255
|
+
if (nextMessage !== null) {
|
|
256
|
+
e.preventDefault();
|
|
257
|
+
onChange(nextMessage);
|
|
258
|
+
// Move cursor to end after a tick
|
|
259
|
+
setTimeout(() => {
|
|
260
|
+
if (textareaRef.current) {
|
|
261
|
+
textareaRef.current.selectionStart = nextMessage.length;
|
|
262
|
+
textareaRef.current.selectionEnd = nextMessage.length;
|
|
263
|
+
}
|
|
264
|
+
}, 0);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
155
269
|
// Handle Enter without Shift - only submit if not already submitting
|
|
156
270
|
if (submitOnEnter && e.key === "Enter" && !e.shiftKey) {
|
|
157
271
|
// Only prevent default and submit if conditions are met
|
|
@@ -169,6 +283,8 @@ const ChatInputField = React.forwardRef(({ asChild = false, className, onKeyDown
|
|
|
169
283
|
const handleChange = (e) => {
|
|
170
284
|
const newValue = e.target.value;
|
|
171
285
|
onChange(newValue);
|
|
286
|
+
// Reset history navigation when user types
|
|
287
|
+
resetHistoryNavigation();
|
|
172
288
|
// Check if user typed "/" at the start to trigger command menu
|
|
173
289
|
if (newValue.startsWith("/") && !newValue.includes("\n")) {
|
|
174
290
|
setShowCommandMenu(true);
|
|
@@ -200,13 +316,16 @@ const ChatInputField = React.forwardRef(({ asChild = false, className, onKeyDown
|
|
|
200
316
|
value,
|
|
201
317
|
onChange: handleChange,
|
|
202
318
|
onKeyDown: handleKeyDown,
|
|
319
|
+
onDrop: handleDrop,
|
|
320
|
+
onDragOver: handleDragOver,
|
|
321
|
+
onDragLeave: handleDragLeave,
|
|
203
322
|
disabled: disabled,
|
|
204
323
|
...props,
|
|
205
324
|
};
|
|
206
325
|
if (asChild && React.isValidElement(children)) {
|
|
207
326
|
return React.cloneElement(children, fieldProps);
|
|
208
327
|
}
|
|
209
|
-
return (_jsx("textarea", { ...fieldProps, rows: 1, className: cn("w-full resize-none rounded-none border-none p-4 shadow-none", "outline-none ring-0 max-h-[6lh] min-h-[44px]", "bg-transparent dark:bg-transparent focus-visible:ring-0", "text-paragraph-sm placeholder:text-muted-foreground", "disabled:cursor-not-allowed disabled:opacity-50", className) }));
|
|
328
|
+
return (_jsx("textarea", { ...fieldProps, rows: 1, className: cn("w-full resize-none rounded-none border-none p-4 shadow-none", "outline-none ring-0 max-h-[6lh] min-h-[44px]", "bg-transparent dark:bg-transparent focus-visible:ring-0", "text-paragraph-sm placeholder:text-muted-foreground", "disabled:cursor-not-allowed disabled:opacity-50", isDragging && "ring-2 ring-primary ring-offset-2", className) }));
|
|
210
329
|
});
|
|
211
330
|
ChatInputField.displayName = "ChatInput.Field";
|
|
212
331
|
const ChatInputSubmit = React.forwardRef(({ asChild = false, className, disabled: disabledProp, children, ...props }, ref) => {
|
|
@@ -235,9 +354,67 @@ const ChatInputActions = React.forwardRef(({ asChild = false, className, childre
|
|
|
235
354
|
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" }) }));
|
|
236
355
|
});
|
|
237
356
|
ChatInputActions.displayName = "ChatInput.Actions";
|
|
238
|
-
const ChatInputAttachment = React.forwardRef(({ asChild = false, className, children, ...props }, ref) => {
|
|
357
|
+
const ChatInputAttachment = React.forwardRef(({ asChild = false, className, children, onFilesSelected, ...props }, ref) => {
|
|
358
|
+
const fileInputRef = React.useRef(null);
|
|
359
|
+
/**
|
|
360
|
+
* Convert file to base64
|
|
361
|
+
*/
|
|
362
|
+
const fileToBase64 = (file) => {
|
|
363
|
+
return new Promise((resolve, reject) => {
|
|
364
|
+
const reader = new FileReader();
|
|
365
|
+
reader.readAsDataURL(file);
|
|
366
|
+
reader.onload = () => {
|
|
367
|
+
// Remove the data URL prefix (e.g., "data:image/png;base64,")
|
|
368
|
+
const base64 = reader.result.split(",")[1];
|
|
369
|
+
resolve(base64 || "");
|
|
370
|
+
};
|
|
371
|
+
reader.onerror = (error) => reject(error);
|
|
372
|
+
});
|
|
373
|
+
};
|
|
374
|
+
/**
|
|
375
|
+
* Handle file selection
|
|
376
|
+
*/
|
|
377
|
+
const handleFileChange = async (e) => {
|
|
378
|
+
const files = e.target.files;
|
|
379
|
+
if (!files || files.length === 0)
|
|
380
|
+
return;
|
|
381
|
+
const processedFiles = [];
|
|
382
|
+
for (const file of Array.from(files)) {
|
|
383
|
+
// Only process image files
|
|
384
|
+
if (!file.type.startsWith("image/")) {
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
try {
|
|
388
|
+
const base64Data = await fileToBase64(file);
|
|
389
|
+
processedFiles.push({
|
|
390
|
+
name: file.name,
|
|
391
|
+
path: file.name, // For web, we don't have a real path
|
|
392
|
+
size: file.size,
|
|
393
|
+
mimeType: file.type,
|
|
394
|
+
data: base64Data,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
catch (error) {
|
|
398
|
+
console.error("Failed to process file:", file.name, error);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
if (processedFiles.length > 0 && onFilesSelected) {
|
|
402
|
+
onFilesSelected(processedFiles);
|
|
403
|
+
}
|
|
404
|
+
// Reset input so the same file can be selected again
|
|
405
|
+
if (fileInputRef.current) {
|
|
406
|
+
fileInputRef.current.value = "";
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
/**
|
|
410
|
+
* Handle button click to trigger file input
|
|
411
|
+
*/
|
|
412
|
+
const handleClick = (e) => {
|
|
413
|
+
e.preventDefault();
|
|
414
|
+
fileInputRef.current?.click();
|
|
415
|
+
};
|
|
239
416
|
const Comp = asChild ? Slot : Button;
|
|
240
|
-
return (_jsx(Comp, { ref: ref, type: "button", variant: "ghost", size: "icon", className: cn("rounded-full", className), ...props, children: children || _jsx(Paperclip, { className: "size-4" }) }));
|
|
417
|
+
return (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", multiple: true, style: { display: "none" }, onChange: handleFileChange }), _jsx(Comp, { ref: ref, type: "button", variant: "ghost", size: "icon", className: cn("rounded-full", className), onClick: handleClick, ...props, children: children || _jsx(Paperclip, { className: "size-4" }) })] }));
|
|
241
418
|
});
|
|
242
419
|
ChatInputAttachment.displayName = "ChatInput.Attachment";
|
|
243
420
|
const ChatInputVoiceInput = React.forwardRef(({ asChild = false, className, children, ...props }, ref) => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
type PanelSize = "hidden" | "small" | "large";
|
|
3
|
-
type PanelTabType = "todo" | "files" | "database";
|
|
3
|
+
type PanelTabType = "todo" | "files" | "database" | "sources" | "settings";
|
|
4
4
|
interface ChatLayoutContextValue {
|
|
5
5
|
sidebarOpen: boolean;
|
|
6
6
|
setSidebarOpen: (open: boolean) => void;
|
|
@@ -9,6 +9,7 @@ interface ChatLayoutContextValue {
|
|
|
9
9
|
activeTab: PanelTabType;
|
|
10
10
|
setActiveTab: (tab: PanelTabType) => void;
|
|
11
11
|
}
|
|
12
|
+
declare const ChatLayoutContext: React.Context<ChatLayoutContextValue | undefined>;
|
|
12
13
|
declare const useChatLayoutContext: () => ChatLayoutContextValue;
|
|
13
14
|
export interface ChatLayoutRootProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
14
15
|
/** Initial sidebar open state */
|
|
@@ -48,5 +49,5 @@ export interface ChatLayoutAsideProps extends React.HTMLAttributes<HTMLDivElemen
|
|
|
48
49
|
breakpoint?: "md" | "lg" | "xl" | "2xl";
|
|
49
50
|
}
|
|
50
51
|
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, };
|
|
52
|
+
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, ChatLayoutContext as Context, useChatLayoutContext, };
|
|
52
53
|
export type { PanelSize, PanelTabType };
|
|
@@ -31,7 +31,7 @@ const ChatLayoutHeader = React.forwardRef(({ className, children, ...props }, re
|
|
|
31
31
|
});
|
|
32
32
|
ChatLayoutHeader.displayName = "ChatLayout.Header";
|
|
33
33
|
const ChatLayoutMain = React.forwardRef(({ className, children, ...props }, ref) => {
|
|
34
|
-
return (_jsx(ResizablePanel, { defaultSize:
|
|
34
|
+
return (_jsx(ResizablePanel, { defaultSize: 70, minSize: 50, children: _jsx("div", { ref: ref, className: cn("flex flex-1 flex-col overflow-hidden h-full", className), ...props, children: children }) }));
|
|
35
35
|
});
|
|
36
36
|
ChatLayoutMain.displayName = "ChatLayout.Main";
|
|
37
37
|
const ChatLayoutBody = React.forwardRef(({ showToaster = true, className, children, ...props }, ref) => {
|
|
@@ -168,13 +168,13 @@ const ChatLayoutSidebar = React.forwardRef(({ className, children, ...props }, r
|
|
|
168
168
|
ChatLayoutSidebar.displayName = "ChatLayout.Sidebar";
|
|
169
169
|
const ChatLayoutAside = React.forwardRef(({ breakpoint = "lg", className, children, ...props }, ref) => {
|
|
170
170
|
const { panelSize } = useChatLayoutContext();
|
|
171
|
-
const [minSizePercent, setMinSizePercent] = React.useState(
|
|
172
|
-
// Convert
|
|
171
|
+
const [minSizePercent, setMinSizePercent] = React.useState(28);
|
|
172
|
+
// Convert 450px minimum to percentage based on window width
|
|
173
173
|
React.useEffect(() => {
|
|
174
174
|
const updateMinSize = () => {
|
|
175
|
-
const minPixels =
|
|
175
|
+
const minPixels = 450;
|
|
176
176
|
const minPercent = (minPixels / window.innerWidth) * 100;
|
|
177
|
-
setMinSizePercent(Math.max(minPercent,
|
|
177
|
+
setMinSizePercent(Math.max(minPercent, 28)); // Never less than 28% or 450px
|
|
178
178
|
};
|
|
179
179
|
updateMinSize();
|
|
180
180
|
window.addEventListener("resize", updateMinSize);
|
|
@@ -185,7 +185,7 @@ const ChatLayoutAside = React.forwardRef(({ breakpoint = "lg", className, childr
|
|
|
185
185
|
// Hidden state - don't render
|
|
186
186
|
if (panelSize === "hidden")
|
|
187
187
|
return null;
|
|
188
|
-
return (_jsxs(_Fragment, { children: [_jsx(ResizableHandle, { withHandle: true, className: "group-hover:opacity-100 opacity-0 transition-opacity" }), _jsx(ResizablePanel, { defaultSize:
|
|
188
|
+
return (_jsxs(_Fragment, { children: [_jsx(ResizableHandle, { withHandle: true, className: "group-hover:opacity-100 opacity-0 transition-opacity" }), _jsx(ResizablePanel, { defaultSize: 30, minSize: minSizePercent, maxSize: 40, className: "group", children: _jsx("div", { ref: ref, className: cn(
|
|
189
189
|
// Hidden by default, visible at breakpoint
|
|
190
190
|
"hidden h-full border-l border-border bg-card overflow-y-auto transition-all duration-300",
|
|
191
191
|
// Breakpoint visibility
|
|
@@ -197,4 +197,4 @@ ChatLayoutAside.displayName = "ChatLayout.Aside";
|
|
|
197
197
|
/* -------------------------------------------------------------------------------------------------
|
|
198
198
|
* Exports
|
|
199
199
|
* -----------------------------------------------------------------------------------------------*/
|
|
200
|
-
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, };
|
|
200
|
+
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, ChatLayoutContext as Context, useChatLayoutContext, };
|
|
@@ -24,3 +24,20 @@ export interface DatabaseTabContentProps extends React.HTMLAttributes<HTMLDivEle
|
|
|
24
24
|
data?: unknown;
|
|
25
25
|
}
|
|
26
26
|
export declare const DatabaseTabContent: React.ForwardRefExoticComponent<DatabaseTabContentProps & React.RefAttributes<HTMLDivElement>>;
|
|
27
|
+
export interface SettingsTabContentProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
28
|
+
tools?: Array<{
|
|
29
|
+
name: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
prettyName?: string;
|
|
32
|
+
icon?: string;
|
|
33
|
+
}>;
|
|
34
|
+
mcps?: Array<{
|
|
35
|
+
name: string;
|
|
36
|
+
transport: string;
|
|
37
|
+
}>;
|
|
38
|
+
subagents?: Array<{
|
|
39
|
+
name: string;
|
|
40
|
+
description: string;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
export declare const SettingsTabContent: React.ForwardRefExoticComponent<SettingsTabContentProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { mockSourceData } from "../data/mockSourceData.js";
|
|
4
|
-
import { mockTodoData } from "../data/mockTodoData.js";
|
|
5
4
|
import { cn } from "../lib/utils.js";
|
|
6
5
|
import { FileSystemView } from "./FileSystemView.js";
|
|
7
6
|
import { SourceListItem } from "./SourceListItem.js";
|
|
8
7
|
import { TodoList } from "./TodoList.js";
|
|
9
|
-
export const TodoTabContent = React.forwardRef(({ todos, className, ...props }, ref) => {
|
|
10
|
-
|
|
11
|
-
const displayTodos = todos && todos.length > 0 ? todos : mockTodoData;
|
|
12
|
-
return (_jsx("div", { ref: ref, className: cn("space-y-2", className), ...props, children: _jsx(TodoList, { todos: displayTodos }) }));
|
|
8
|
+
export const TodoTabContent = React.forwardRef(({ todos = [], className, ...props }, ref) => {
|
|
9
|
+
return (_jsx("div", { ref: ref, className: cn("space-y-2", className), ...props, children: _jsx(TodoList, { todos: todos }) }));
|
|
13
10
|
});
|
|
14
11
|
TodoTabContent.displayName = "TodoTabContent";
|
|
15
12
|
export const FilesTabContent = React.forwardRef(({ files = [], provider, onFileSelect, className, ...props }, ref) => {
|
|
@@ -44,3 +41,7 @@ export const DatabaseTabContent = React.forwardRef(({ data, className, ...props
|
|
|
44
41
|
return (_jsxs("div", { ref: ref, className: cn("space-y-4", className), ...props, children: [_jsx("h3", { className: "font-semibold text-subheading", children: "Database" }), _jsxs("div", { className: "text-paragraph-sm text-muted-foreground", children: [_jsx("p", { children: "Database viewer - panel automatically expanded to large size" }), _jsxs("div", { className: "mt-4 p-4 border border-border rounded", children: [_jsx("p", { children: "Your large data table would go here" }), data && typeof data === "object" ? (_jsx("pre", { className: "mt-2 text-caption overflow-auto", children: JSON.stringify(data, null, 2) })) : null] })] })] }));
|
|
45
42
|
});
|
|
46
43
|
DatabaseTabContent.displayName = "DatabaseTabContent";
|
|
44
|
+
export const SettingsTabContent = React.forwardRef(({ tools = [], mcps = [], subagents = [], className, ...props }, ref) => {
|
|
45
|
+
return (_jsxs("div", { ref: ref, className: cn("space-y-6", className), ...props, children: [_jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "font-semibold text-subheading", children: "Tools" }), tools.length > 0 ? (_jsx("div", { className: "space-y-2", children: tools.map((tool) => (_jsxs("div", { className: "p-3 border border-border rounded-lg bg-muted/30", children: [_jsx("div", { className: "font-medium text-paragraph-sm", children: tool.prettyName || tool.name }), tool.description && (_jsx("div", { className: "text-caption text-muted-foreground mt-1 line-clamp-1", children: tool.description }))] }, tool.name))) })) : (_jsx("p", { className: "text-paragraph-sm text-muted-foreground", children: "No tools available" }))] }), _jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "font-semibold text-subheading", children: "MCP Servers" }), mcps.length > 0 ? (_jsx("div", { className: "space-y-2", children: mcps.map((mcp) => (_jsxs("div", { className: "p-3 border border-border rounded-lg bg-muted/30", children: [_jsx("div", { className: "font-medium text-paragraph-sm", children: mcp.name }), _jsxs("div", { className: "text-caption text-muted-foreground mt-1", children: ["Transport: ", mcp.transport] })] }, mcp.name))) })) : (_jsx("p", { className: "text-paragraph-sm text-muted-foreground", children: "No MCP servers connected" }))] }), _jsxs("div", { className: "space-y-3", children: [_jsx("h3", { className: "font-semibold text-subheading", children: "Subagents" }), subagents.length > 0 ? (_jsx("div", { className: "space-y-2", children: subagents.map((subagent) => (_jsxs("div", { className: "p-3 border border-border rounded-lg bg-muted/30", children: [_jsx("div", { className: "font-medium text-paragraph-sm", children: subagent.name }), _jsx("div", { className: "text-caption text-muted-foreground mt-1 line-clamp-2", children: subagent.description })] }, subagent.name))) })) : (_jsx("p", { className: "text-paragraph-sm text-muted-foreground", children: "No subagents available" }))] })] }));
|
|
46
|
+
});
|
|
47
|
+
SettingsTabContent.displayName = "SettingsTabContent";
|
|
@@ -3,5 +3,7 @@ export interface ChatViewProps {
|
|
|
3
3
|
client: AcpClient | null;
|
|
4
4
|
initialSessionId?: string | null;
|
|
5
5
|
error?: string | null;
|
|
6
|
+
/** Optional debugger URL for viewing sessions */
|
|
7
|
+
debuggerUrl?: string;
|
|
6
8
|
}
|
|
7
|
-
export declare function ChatView({ client, initialSessionId, error: initError, }: ChatViewProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare function ChatView({ client, initialSessionId, error: initError, debuggerUrl, }: ChatViewProps): import("react/jsx-runtime").JSX.Element;
|