@agentuity/workbench 0.0.105 → 0.0.107
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/components/App.d.ts.map +1 -1
- package/dist/components/App.js +15 -13
- package/dist/components/App.js.map +1 -1
- package/dist/components/ai-elements/actions.d.ts +1 -1
- package/dist/components/ai-elements/actions.d.ts.map +1 -1
- package/dist/components/ai-elements/actions.js +1 -1
- package/dist/components/ai-elements/actions.js.map +1 -1
- package/dist/components/ai-elements/code-block.d.ts +1 -1
- package/dist/components/ai-elements/code-block.d.ts.map +1 -1
- package/dist/components/ai-elements/code-block.js +22 -20
- package/dist/components/ai-elements/code-block.js.map +1 -1
- package/dist/components/ai-elements/conversation.d.ts +2 -2
- package/dist/components/ai-elements/conversation.d.ts.map +1 -1
- package/dist/components/ai-elements/conversation.js +5 -3
- package/dist/components/ai-elements/conversation.js.map +1 -1
- package/dist/components/ai-elements/message.d.ts +1 -1
- package/dist/components/ai-elements/message.d.ts.map +1 -1
- package/dist/components/ai-elements/message.js +4 -9
- package/dist/components/ai-elements/message.js.map +1 -1
- package/dist/components/ai-elements/prompt-input.d.ts.map +1 -1
- package/dist/components/ai-elements/prompt-input.js +1 -1
- package/dist/components/ai-elements/prompt-input.js.map +1 -1
- package/dist/components/ai-elements/shimmer.d.ts.map +1 -1
- package/dist/components/ai-elements/shimmer.js +1 -1
- package/dist/components/ai-elements/shimmer.js.map +1 -1
- package/dist/components/internal/chat.d.ts +10 -0
- package/dist/components/internal/chat.d.ts.map +1 -0
- package/dist/components/internal/chat.js +104 -0
- package/dist/components/internal/chat.js.map +1 -0
- package/dist/components/internal/{Header.d.ts → header.d.ts} +4 -6
- package/dist/components/internal/header.d.ts.map +1 -0
- package/dist/components/internal/header.js +25 -0
- package/dist/components/internal/header.js.map +1 -0
- package/dist/components/internal/{InputSection.d.ts → input-section.d.ts} +9 -9
- package/dist/components/internal/input-section.d.ts.map +1 -0
- package/dist/components/internal/input-section.js +162 -0
- package/dist/components/internal/input-section.js.map +1 -0
- package/dist/components/internal/json-editor.d.ts +14 -0
- package/dist/components/internal/json-editor.d.ts.map +1 -0
- package/dist/components/internal/{MonacoJsonEditor.js → json-editor.js} +40 -37
- package/dist/components/internal/json-editor.js.map +1 -0
- package/dist/components/internal/logo.d.ts +2 -3
- package/dist/components/internal/logo.d.ts.map +1 -1
- package/dist/components/internal/logo.js +2 -2
- package/dist/components/internal/logo.js.map +1 -1
- package/dist/components/internal/resizable-provider.d.ts.map +1 -0
- package/dist/components/internal/resizable-provider.js.map +1 -0
- package/dist/components/internal/{Schema.d.ts → schema.d.ts} +2 -2
- package/dist/components/internal/schema.d.ts.map +1 -0
- package/dist/components/internal/schema.js +13 -0
- package/dist/components/internal/schema.js.map +1 -0
- package/dist/components/internal/{WorkbenchProvider.d.ts → workbench-provider.d.ts} +8 -4
- package/dist/components/internal/workbench-provider.d.ts.map +1 -0
- package/dist/components/internal/{WorkbenchProvider.js → workbench-provider.js} +87 -60
- package/dist/components/internal/workbench-provider.js.map +1 -0
- package/dist/components/ui/avatar.d.ts +1 -1
- package/dist/components/ui/avatar.d.ts.map +1 -1
- package/dist/components/ui/avatar.js.map +1 -1
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/command.d.ts +1 -1
- package/dist/components/ui/command.d.ts.map +1 -1
- package/dist/components/ui/command.js.map +1 -1
- package/dist/components/ui/dialog.d.ts +1 -1
- package/dist/components/ui/dialog.d.ts.map +1 -1
- package/dist/components/ui/dialog.js.map +1 -1
- package/dist/components/ui/dropdown-menu.d.ts +1 -1
- package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
- package/dist/components/ui/dropdown-menu.js.map +1 -1
- package/dist/components/ui/hover-card.d.ts +1 -1
- package/dist/components/ui/hover-card.d.ts.map +1 -1
- package/dist/components/ui/hover-card.js.map +1 -1
- package/dist/components/ui/input-group.d.ts +2 -2
- package/dist/components/ui/input-group.d.ts.map +1 -1
- package/dist/components/ui/input-group.js.map +1 -1
- package/dist/components/ui/input.d.ts +1 -1
- package/dist/components/ui/input.d.ts.map +1 -1
- package/dist/components/ui/scroll-area.d.ts +1 -1
- package/dist/components/ui/scroll-area.d.ts.map +1 -1
- package/dist/components/ui/scroll-area.js.map +1 -1
- package/dist/components/ui/textarea.d.ts +1 -1
- package/dist/components/ui/textarea.d.ts.map +1 -1
- package/dist/components/ui/theme-provider.d.ts.map +1 -1
- package/dist/components/ui/theme-provider.js +1 -1
- package/dist/components/ui/theme-provider.js.map +1 -1
- package/dist/components/ui/tooltip.d.ts +1 -1
- package/dist/components/ui/tooltip.d.ts.map +1 -1
- package/dist/components/ui/tooltip.js.map +1 -1
- package/dist/hooks/useAgentSchemas.d.ts +10 -10
- package/dist/hooks/useAgentSchemas.d.ts.map +1 -1
- package/dist/hooks/useAgentSchemas.js +9 -7
- package/dist/hooks/useAgentSchemas.js.map +1 -1
- package/dist/hooks/useLogger.d.ts.map +1 -1
- package/dist/hooks/useLogger.js +2 -1
- package/dist/hooks/useLogger.js.map +1 -1
- package/dist/hooks/useWorkbenchWebsocket.d.ts +2 -2
- package/dist/hooks/useWorkbenchWebsocket.d.ts.map +1 -1
- package/dist/hooks/useWorkbenchWebsocket.js +24 -20
- package/dist/hooks/useWorkbenchWebsocket.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -6
- package/dist/index.js.map +1 -1
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -1
- package/dist/lib/utils.js +59 -0
- package/dist/lib/utils.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js.map +1 -1
- package/dist/standalone.css +360 -295
- package/dist/types/config.d.ts +27 -18
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/base.css +186 -158
- package/src/components/App.tsx +31 -16
- package/src/components/ai-elements/actions.tsx +2 -2
- package/src/components/ai-elements/code-block.tsx +46 -32
- package/src/components/ai-elements/conversation.tsx +18 -17
- package/src/components/ai-elements/message.tsx +4 -9
- package/src/components/ai-elements/prompt-input.tsx +1 -1
- package/src/components/ai-elements/shimmer.tsx +1 -1
- package/src/components/internal/chat.tsx +326 -0
- package/src/components/internal/{Header.tsx → header.tsx} +37 -40
- package/src/components/internal/{InputSection.tsx → input-section.tsx} +173 -119
- package/src/components/internal/{MonacoJsonEditor.tsx → json-editor.tsx} +77 -49
- package/src/components/internal/logo.tsx +3 -5
- package/src/components/internal/schema.tsx +96 -0
- package/src/components/internal/{WorkbenchProvider.tsx → workbench-provider.tsx} +194 -68
- package/src/components/ui/avatar.tsx +1 -1
- package/src/components/ui/command.tsx +1 -1
- package/src/components/ui/dialog.tsx +1 -1
- package/src/components/ui/dropdown-menu.tsx +1 -1
- package/src/components/ui/hover-card.tsx +1 -1
- package/src/components/ui/input-group.tsx +1 -1
- package/src/components/ui/input.tsx +1 -1
- package/src/components/ui/scroll-area.tsx +1 -1
- package/src/components/ui/textarea.tsx +1 -1
- package/src/components/ui/theme-provider.tsx +1 -1
- package/src/components/ui/tooltip.tsx +1 -1
- package/src/hooks/useAgentSchemas.ts +26 -15
- package/src/hooks/useLogger.ts +7 -1
- package/src/hooks/useWorkbenchWebsocket.ts +67 -32
- package/src/index.ts +5 -9
- package/src/lib/utils.ts +88 -0
- package/src/server.ts +1 -1
- package/src/types/config.ts +28 -21
- package/dist/components/internal/Chat.d.ts +0 -14
- package/dist/components/internal/Chat.d.ts.map +0 -1
- package/dist/components/internal/Chat.js +0 -61
- package/dist/components/internal/Chat.js.map +0 -1
- package/dist/components/internal/Header.d.ts.map +0 -1
- package/dist/components/internal/Header.js +0 -31
- package/dist/components/internal/Header.js.map +0 -1
- package/dist/components/internal/InputSection.d.ts.map +0 -1
- package/dist/components/internal/InputSection.js +0 -152
- package/dist/components/internal/InputSection.js.map +0 -1
- package/dist/components/internal/MonacoJsonEditor.d.ts +0 -13
- package/dist/components/internal/MonacoJsonEditor.d.ts.map +0 -1
- package/dist/components/internal/MonacoJsonEditor.js.map +0 -1
- package/dist/components/internal/Schema.d.ts.map +0 -1
- package/dist/components/internal/Schema.js +0 -13
- package/dist/components/internal/Schema.js.map +0 -1
- package/dist/components/internal/WorkbenchProvider.d.ts.map +0 -1
- package/dist/components/internal/WorkbenchProvider.js.map +0 -1
- package/dist/components/ui/resizable-provider.d.ts.map +0 -1
- package/dist/components/ui/resizable-provider.js.map +0 -1
- package/src/components/internal/Chat.tsx +0 -201
- package/src/components/internal/Schema.tsx +0 -100
- /package/dist/components/{ui → internal}/resizable-provider.d.ts +0 -0
- /package/dist/components/{ui → internal}/resizable-provider.js +0 -0
- /package/src/components/{ui → internal}/resizable-provider.tsx +0 -0
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
3
|
+
import javascriptLang from '@shikijs/langs/javascript';
|
|
4
|
+
import jsonLang from '@shikijs/langs/json';
|
|
5
|
+
import typescriptLang from '@shikijs/langs/typescript';
|
|
6
|
+
import themeDarkModule from '@shikijs/themes/dark-plus';
|
|
7
|
+
import themeLightModule from '@shikijs/themes/light-plus';
|
|
5
8
|
import type { Element } from 'hast';
|
|
6
9
|
import { CheckIcon, CopyIcon } from 'lucide-react';
|
|
7
10
|
import {
|
|
@@ -13,21 +16,19 @@ import {
|
|
|
13
16
|
useRef,
|
|
14
17
|
useState,
|
|
15
18
|
} from 'react';
|
|
19
|
+
import type { ShikiTransformer, ThemeRegistration } from 'shiki';
|
|
16
20
|
import { createHighlighterCore } from 'shiki/core';
|
|
17
21
|
import { createOnigurumaEngine } from 'shiki/engine/oniguruma';
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import jsonLang from '@shikijs/langs/json';
|
|
22
|
-
import javascriptLang from '@shikijs/langs/javascript';
|
|
23
|
-
import typescriptLang from '@shikijs/langs/typescript';
|
|
22
|
+
import { cn } from '../../lib/utils';
|
|
23
|
+
import { Button } from '../ui/button';
|
|
24
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
|
|
24
25
|
|
|
25
26
|
// Extract theme objects from default exports
|
|
26
|
-
const
|
|
27
|
-
'default' in
|
|
27
|
+
const themeLight = (
|
|
28
|
+
'default' in themeLightModule ? themeLightModule.default : themeLightModule
|
|
28
29
|
) as ThemeRegistration;
|
|
29
|
-
const
|
|
30
|
-
'default' in
|
|
30
|
+
const themeDark = (
|
|
31
|
+
'default' in themeDarkModule ? themeDarkModule.default : themeDarkModule
|
|
31
32
|
) as ThemeRegistration;
|
|
32
33
|
|
|
33
34
|
type SupportedLanguage = 'json' | 'javascript' | 'typescript';
|
|
@@ -52,7 +53,7 @@ let highlighterPromise: ReturnType<typeof createHighlighterCore> | null = null;
|
|
|
52
53
|
function getHighlighter() {
|
|
53
54
|
if (!highlighterPromise) {
|
|
54
55
|
highlighterPromise = createHighlighterCore({
|
|
55
|
-
themes: [
|
|
56
|
+
themes: [themeLight, themeDark],
|
|
56
57
|
langs: [jsonLang, javascriptLang, typescriptLang],
|
|
57
58
|
engine: createOnigurumaEngine(import('shiki/wasm')),
|
|
58
59
|
});
|
|
@@ -87,17 +88,18 @@ export async function highlightCode(
|
|
|
87
88
|
showLineNumbers = false
|
|
88
89
|
): Promise<readonly [string, string]> {
|
|
89
90
|
const highlighter = await getHighlighter();
|
|
91
|
+
|
|
90
92
|
const transformers: ShikiTransformer[] = showLineNumbers ? [lineNumberTransformer] : [];
|
|
91
93
|
|
|
92
94
|
return [
|
|
93
95
|
highlighter.codeToHtml(code, {
|
|
94
96
|
lang: language,
|
|
95
|
-
theme:
|
|
97
|
+
theme: themeLight.name ?? 'github-light',
|
|
96
98
|
transformers,
|
|
97
99
|
}),
|
|
98
100
|
highlighter.codeToHtml(code, {
|
|
99
101
|
lang: language,
|
|
100
|
-
theme:
|
|
102
|
+
theme: themeDark.name ?? 'github-dark',
|
|
101
103
|
transformers,
|
|
102
104
|
}),
|
|
103
105
|
] as const;
|
|
@@ -111,14 +113,14 @@ export const CodeBlock = ({
|
|
|
111
113
|
children,
|
|
112
114
|
...props
|
|
113
115
|
}: CodeBlockProps) => {
|
|
114
|
-
const [
|
|
116
|
+
const [lightHtml, setLightHtml] = useState<string>('');
|
|
115
117
|
const [darkHtml, setDarkHtml] = useState<string>('');
|
|
116
118
|
const mounted = useRef(false);
|
|
117
119
|
|
|
118
120
|
useEffect(() => {
|
|
119
121
|
highlightCode(code, language, showLineNumbers).then(([light, dark]) => {
|
|
120
122
|
if (!mounted.current) {
|
|
121
|
-
|
|
123
|
+
setLightHtml(light);
|
|
122
124
|
setDarkHtml(dark);
|
|
123
125
|
mounted.current = true;
|
|
124
126
|
}
|
|
@@ -129,6 +131,9 @@ export const CodeBlock = ({
|
|
|
129
131
|
};
|
|
130
132
|
}, [code, language, showLineNumbers]);
|
|
131
133
|
|
|
134
|
+
const baseClass =
|
|
135
|
+
'overflow-hidden [&>pre]:m-0 [&>pre]:bg-background! [&>pre]:p-4 [&>pre]:text-foreground! [&>pre]:text-sm [&>pre]:whitespace-pre-wrap [&_code]:font-mono [&_code]:text-sm';
|
|
136
|
+
|
|
132
137
|
return (
|
|
133
138
|
<CodeBlockContext.Provider value={{ code }}>
|
|
134
139
|
<div
|
|
@@ -139,18 +144,22 @@ export const CodeBlock = ({
|
|
|
139
144
|
{...props}
|
|
140
145
|
>
|
|
141
146
|
<div className="relative">
|
|
147
|
+
{/* Light Mode */}
|
|
142
148
|
<div
|
|
143
|
-
className=
|
|
144
|
-
// biome-ignore lint/security/noDangerouslySetInnerHtml:
|
|
145
|
-
dangerouslySetInnerHTML={{ __html:
|
|
149
|
+
className={cn(baseClass, 'dark:hidden')}
|
|
150
|
+
// biome-ignore lint/security/noDangerouslySetInnerHtml: must be added via this method as per the library
|
|
151
|
+
dangerouslySetInnerHTML={{ __html: lightHtml }}
|
|
146
152
|
/>
|
|
153
|
+
|
|
154
|
+
{/* Dark Mode */}
|
|
147
155
|
<div
|
|
148
|
-
className=
|
|
149
|
-
// biome-ignore lint/security/noDangerouslySetInnerHtml:
|
|
156
|
+
className={cn(baseClass, 'hidden dark:block')}
|
|
157
|
+
// biome-ignore lint/security/noDangerouslySetInnerHtml: must be added via this method as per the library
|
|
150
158
|
dangerouslySetInnerHTML={{ __html: darkHtml }}
|
|
151
159
|
/>
|
|
160
|
+
|
|
152
161
|
{children && (
|
|
153
|
-
<div className="absolute top-
|
|
162
|
+
<div className="absolute top-1 right-1 flex items-center gap-2">{children}</div>
|
|
154
163
|
)}
|
|
155
164
|
</div>
|
|
156
165
|
</div>
|
|
@@ -194,14 +203,19 @@ export const CodeBlockCopyButton = ({
|
|
|
194
203
|
const Icon = isCopied ? CheckIcon : CopyIcon;
|
|
195
204
|
|
|
196
205
|
return (
|
|
197
|
-
<
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
+
<Tooltip>
|
|
207
|
+
<TooltipTrigger asChild>
|
|
208
|
+
<Button
|
|
209
|
+
className={cn('size-7 shrink-0 rounded-sm', className)}
|
|
210
|
+
onClick={copyToClipboard}
|
|
211
|
+
size="icon"
|
|
212
|
+
variant="ghost"
|
|
213
|
+
{...props}
|
|
214
|
+
>
|
|
215
|
+
{children ?? <Icon size={14} />}
|
|
216
|
+
</Button>
|
|
217
|
+
</TooltipTrigger>
|
|
218
|
+
<TooltipContent>Copy to clipboard</TooltipContent>
|
|
219
|
+
</Tooltip>
|
|
206
220
|
);
|
|
207
221
|
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { Button } from '../ui/button';
|
|
4
|
-
import { cn } from '../../lib/utils';
|
|
5
3
|
import { ArrowDownIcon } from 'lucide-react';
|
|
6
4
|
import type { ComponentProps } from 'react';
|
|
7
5
|
import { useCallback } from 'react';
|
|
8
6
|
import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom';
|
|
7
|
+
import { cn } from '../../lib/utils';
|
|
8
|
+
import { Button } from '../ui/button';
|
|
9
9
|
|
|
10
10
|
export type ConversationProps = ComponentProps<typeof StickToBottom>;
|
|
11
11
|
|
|
@@ -71,20 +71,21 @@ export const ConversationScrollButton = ({
|
|
|
71
71
|
}, [scrollToBottom]);
|
|
72
72
|
|
|
73
73
|
return (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
74
|
+
<Button
|
|
75
|
+
className={cn(
|
|
76
|
+
'absolute bottom-0 left-[50%] translate-x-[-50%] rounded-full transition-all duration-300',
|
|
77
|
+
isAtBottom
|
|
78
|
+
? 'opacity-0 pointer-events-none -translate-y-1/2'
|
|
79
|
+
: 'opacity-100 translate-y-0',
|
|
80
|
+
className
|
|
81
|
+
)}
|
|
82
|
+
onClick={handleScrollToBottom}
|
|
83
|
+
size="icon"
|
|
84
|
+
type="button"
|
|
85
|
+
variant="outline"
|
|
86
|
+
{...props}
|
|
87
|
+
>
|
|
88
|
+
<ArrowDownIcon className="size-4" />
|
|
89
|
+
</Button>
|
|
89
90
|
);
|
|
90
91
|
};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar';
|
|
2
|
-
import { cn } from '../../lib/utils';
|
|
3
1
|
import type { UIMessage } from 'ai';
|
|
4
2
|
import { cva, type VariantProps } from 'class-variance-authority';
|
|
5
3
|
import type { ComponentProps, HTMLAttributes } from 'react';
|
|
4
|
+
import { cn } from '../../lib/utils';
|
|
5
|
+
import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar';
|
|
6
6
|
|
|
7
7
|
export type MessageProps = HTMLAttributes<HTMLDivElement> & {
|
|
8
8
|
from: UIMessage['role'];
|
|
@@ -20,18 +20,13 @@ export const Message = ({ className, from, ...props }: MessageProps) => (
|
|
|
20
20
|
);
|
|
21
21
|
|
|
22
22
|
const messageContentVariants = cva(
|
|
23
|
-
'is-user:dark flex flex-col gap-2 overflow-hidden rounded-
|
|
23
|
+
'is-user:dark flex flex-col gap-2 overflow-hidden rounded-md text-sm',
|
|
24
24
|
{
|
|
25
25
|
variants: {
|
|
26
26
|
variant: {
|
|
27
|
-
contained: [
|
|
28
|
-
'max-w-[80%] px-4 py-3',
|
|
29
|
-
'group-[.is-user]:bg-primary group-[.is-user]:text-primary-foreground',
|
|
30
|
-
'group-[.is-assistant]:bg-secondary group-[.is-assistant]:text-foreground',
|
|
31
|
-
],
|
|
27
|
+
contained: ['max-w-[80%] px-4 py-3 bg-input/30 text-foreground'],
|
|
32
28
|
flat: [
|
|
33
29
|
'group-[.is-user]:max-w-[80%] group-[.is-user]:bg-secondary group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-foreground',
|
|
34
|
-
'group-[.is-assistant]:text-foreground',
|
|
35
30
|
],
|
|
36
31
|
},
|
|
37
32
|
},
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
useRef,
|
|
35
35
|
useState,
|
|
36
36
|
} from 'react';
|
|
37
|
+
import { cn } from '../../lib/utils';
|
|
37
38
|
import { Button } from '../ui/button';
|
|
38
39
|
import {
|
|
39
40
|
Command,
|
|
@@ -58,7 +59,6 @@ import {
|
|
|
58
59
|
InputGroupTextarea,
|
|
59
60
|
} from '../ui/input-group';
|
|
60
61
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select';
|
|
61
|
-
import { cn } from '../../lib/utils';
|
|
62
62
|
// ============================================================================
|
|
63
63
|
// Provider Context & Types
|
|
64
64
|
// ============================================================================
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { cn } from '../../lib/utils';
|
|
4
3
|
import { motion } from 'motion/react';
|
|
5
4
|
import { type CSSProperties, type ElementType, type JSX, memo, useMemo } from 'react';
|
|
5
|
+
import { cn } from '../../lib/utils';
|
|
6
6
|
|
|
7
7
|
export type TextShimmerProps = {
|
|
8
8
|
children: string;
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import { ChevronRight, Copy, Loader, RefreshCcw } from 'lucide-react';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { useLogger } from '../../hooks/useLogger';
|
|
4
|
+
import { cn, formatErrorForCopy } from '../../lib/utils';
|
|
5
|
+
import { Action, Actions } from '../ai-elements/actions';
|
|
6
|
+
import { CodeBlock } from '../ai-elements/code-block';
|
|
7
|
+
import {
|
|
8
|
+
Conversation,
|
|
9
|
+
ConversationContent,
|
|
10
|
+
ConversationScrollButton,
|
|
11
|
+
} from '../ai-elements/conversation';
|
|
12
|
+
import { Message, MessageContent } from '../ai-elements/message';
|
|
13
|
+
import { Shimmer } from '../ai-elements/shimmer';
|
|
14
|
+
import { InputSection } from './input-section';
|
|
15
|
+
import { useWorkbench } from './workbench-provider';
|
|
16
|
+
|
|
17
|
+
export interface ChatProps {
|
|
18
|
+
className?: string;
|
|
19
|
+
emptyState?: React.ReactNode;
|
|
20
|
+
onSchemaToggle?: () => void;
|
|
21
|
+
onSessionOpen?: (sessionId: string) => void;
|
|
22
|
+
schemaOpen?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function Chat({
|
|
26
|
+
className: _className,
|
|
27
|
+
emptyState,
|
|
28
|
+
onSchemaToggle,
|
|
29
|
+
onSessionOpen,
|
|
30
|
+
schemaOpen,
|
|
31
|
+
}: ChatProps) {
|
|
32
|
+
const {
|
|
33
|
+
agents,
|
|
34
|
+
clearAgentState,
|
|
35
|
+
connectionStatus,
|
|
36
|
+
isLoading,
|
|
37
|
+
messages,
|
|
38
|
+
selectedAgent,
|
|
39
|
+
setSelectedAgent,
|
|
40
|
+
submitMessage,
|
|
41
|
+
} = useWorkbench();
|
|
42
|
+
const logger = useLogger('Chat');
|
|
43
|
+
const [value, setValue] = useState('');
|
|
44
|
+
|
|
45
|
+
const handleSubmit = () => {
|
|
46
|
+
logger.debug('🎯 Chat handleSubmit - selectedAgent:', selectedAgent, 'value:', value);
|
|
47
|
+
|
|
48
|
+
const selectedAgentData = Object.values(agents).find(
|
|
49
|
+
(agent) => agent.metadata.agentId === selectedAgent
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
logger.debug('📊 Chat handleSubmit - selectedAgentData:', selectedAgentData);
|
|
53
|
+
|
|
54
|
+
const hasInputSchema = selectedAgentData?.schema?.input?.json;
|
|
55
|
+
|
|
56
|
+
logger.debug('📝 Chat handleSubmit - hasInputSchema:', hasInputSchema);
|
|
57
|
+
|
|
58
|
+
// If agent has no input schema, submit without requiring input
|
|
59
|
+
if (!hasInputSchema) {
|
|
60
|
+
submitMessage('');
|
|
61
|
+
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// For agents with input schema, require input
|
|
66
|
+
if (!value.trim()) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
submitMessage(value);
|
|
71
|
+
setValue('');
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<div className="relative flex flex-col h-full w-full overflow-hidden">
|
|
76
|
+
<Conversation
|
|
77
|
+
className="flex-1 overflow-y-auto [&>div]:overflow-y-auto"
|
|
78
|
+
id="chat-conversation"
|
|
79
|
+
>
|
|
80
|
+
{connectionStatus === 'disconnected' && emptyState ? (
|
|
81
|
+
<div className="flex flex-col h-full">{emptyState}</div>
|
|
82
|
+
) : (
|
|
83
|
+
<ConversationContent>
|
|
84
|
+
{messages.map((message) => {
|
|
85
|
+
const { role, parts, id } = message;
|
|
86
|
+
|
|
87
|
+
const isStreaming = parts.some(
|
|
88
|
+
(part) => part.type === 'text' && part.state === 'streaming'
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const tokens =
|
|
92
|
+
'tokens' in message ? (message as { tokens?: string }).tokens : undefined;
|
|
93
|
+
|
|
94
|
+
const duration =
|
|
95
|
+
'duration' in message
|
|
96
|
+
? (message as { duration?: string }).duration
|
|
97
|
+
: undefined;
|
|
98
|
+
|
|
99
|
+
const sessionId =
|
|
100
|
+
'sessionId' in message
|
|
101
|
+
? (message as { sessionId?: string }).sessionId
|
|
102
|
+
: undefined;
|
|
103
|
+
|
|
104
|
+
// Check for agent error in text content
|
|
105
|
+
let errorInfo:
|
|
106
|
+
| {
|
|
107
|
+
message: string;
|
|
108
|
+
stack?: string;
|
|
109
|
+
code?: string;
|
|
110
|
+
cause?: unknown;
|
|
111
|
+
}
|
|
112
|
+
| undefined;
|
|
113
|
+
|
|
114
|
+
if (parts.length === 1 && parts[0].type === 'text') {
|
|
115
|
+
const text = parts[0].text;
|
|
116
|
+
|
|
117
|
+
if (text.startsWith('{') && text.includes('"__agentError"')) {
|
|
118
|
+
try {
|
|
119
|
+
const parsed = JSON.parse(text) as {
|
|
120
|
+
__agentError?: boolean;
|
|
121
|
+
message?: string;
|
|
122
|
+
stack?: string;
|
|
123
|
+
code?: string;
|
|
124
|
+
cause?: unknown;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
if (parsed.__agentError) {
|
|
128
|
+
errorInfo = {
|
|
129
|
+
message: parsed.message || 'Unknown error',
|
|
130
|
+
stack: parsed.stack,
|
|
131
|
+
code: parsed.code,
|
|
132
|
+
cause: parsed.cause,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
// Not valid JSON, ignore
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<div key={id} className="mb-2">
|
|
143
|
+
{role === 'assistant' && (
|
|
144
|
+
<div className="w-fit flex items-center mb-2 text-muted-foreground text-sm transition-colors">
|
|
145
|
+
<Loader
|
|
146
|
+
className={cn(
|
|
147
|
+
'size-4 transition-all',
|
|
148
|
+
isStreaming ? 'animate-spin mr-2' : 'w-0 mr-2.5'
|
|
149
|
+
)}
|
|
150
|
+
/>
|
|
151
|
+
|
|
152
|
+
{isStreaming ? (
|
|
153
|
+
<Shimmer duration={1}>Running...</Shimmer>
|
|
154
|
+
) : (
|
|
155
|
+
<button
|
|
156
|
+
type="button"
|
|
157
|
+
className={cn(
|
|
158
|
+
'flex items-center bg-transparent border-none p-0 text-inherit',
|
|
159
|
+
sessionId && onSessionOpen
|
|
160
|
+
? 'hover:text-foreground transition-colors cursor-pointer'
|
|
161
|
+
: 'cursor-default'
|
|
162
|
+
)}
|
|
163
|
+
onClick={() => sessionId && onSessionOpen?.(sessionId)}
|
|
164
|
+
tabIndex={0}
|
|
165
|
+
aria-label="Open session"
|
|
166
|
+
disabled={!sessionId}
|
|
167
|
+
style={{ background: 'none' }}
|
|
168
|
+
>
|
|
169
|
+
{duration ? (
|
|
170
|
+
<>
|
|
171
|
+
Ran for
|
|
172
|
+
<span className="mx-1">{duration}</span>
|
|
173
|
+
</>
|
|
174
|
+
) : (
|
|
175
|
+
<>Finished</>
|
|
176
|
+
)}
|
|
177
|
+
|
|
178
|
+
{duration && tokens && (
|
|
179
|
+
<>
|
|
180
|
+
and consumed
|
|
181
|
+
<span className="mx-1">{tokens}</span> tokens
|
|
182
|
+
</>
|
|
183
|
+
)}
|
|
184
|
+
|
|
185
|
+
{sessionId && onSessionOpen && (
|
|
186
|
+
<ChevronRight className="size-4" />
|
|
187
|
+
)}
|
|
188
|
+
</button>
|
|
189
|
+
)}
|
|
190
|
+
</div>
|
|
191
|
+
)}
|
|
192
|
+
|
|
193
|
+
{(role === 'user' || !isStreaming) && (
|
|
194
|
+
<>
|
|
195
|
+
<Message
|
|
196
|
+
key={id}
|
|
197
|
+
from={role as 'user' | 'system' | 'assistant'}
|
|
198
|
+
className="p-0"
|
|
199
|
+
>
|
|
200
|
+
<MessageContent
|
|
201
|
+
className={cn(errorInfo && 'bg-destructive/10')}
|
|
202
|
+
>
|
|
203
|
+
{errorInfo ? (
|
|
204
|
+
errorInfo.stack ? (
|
|
205
|
+
<pre className="font-mono whitespace-pre-wrap overflow-x-auto text-destructive">
|
|
206
|
+
{errorInfo.stack}
|
|
207
|
+
</pre>
|
|
208
|
+
) : (
|
|
209
|
+
<p className="font-mono whitespace-pre-wrap overflow-x-auto text-destructive">
|
|
210
|
+
{errorInfo.message || 'Unknown error'}
|
|
211
|
+
</p>
|
|
212
|
+
)
|
|
213
|
+
) : (
|
|
214
|
+
parts.map((part, index) => {
|
|
215
|
+
switch (part.type) {
|
|
216
|
+
case 'text':
|
|
217
|
+
// json?
|
|
218
|
+
if (
|
|
219
|
+
part.text.startsWith('{') &&
|
|
220
|
+
part.text.endsWith('}')
|
|
221
|
+
) {
|
|
222
|
+
try {
|
|
223
|
+
const json = JSON.parse(part.text);
|
|
224
|
+
|
|
225
|
+
// json!
|
|
226
|
+
return (
|
|
227
|
+
<CodeBlock
|
|
228
|
+
key={`${id}-${part.text}-${index}`}
|
|
229
|
+
code={JSON.stringify(json, null, 2)}
|
|
230
|
+
language="json"
|
|
231
|
+
className="bg-transparent border-0 [&>div>div>pre]:bg-transparent! [&_pre]:p-0!"
|
|
232
|
+
/>
|
|
233
|
+
);
|
|
234
|
+
} catch (_error) {
|
|
235
|
+
// not json :(
|
|
236
|
+
return (
|
|
237
|
+
<div
|
|
238
|
+
key={`${id}-${part.text}-${index}`}
|
|
239
|
+
>
|
|
240
|
+
{part.text || ''}
|
|
241
|
+
</div>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// text/markdown
|
|
247
|
+
return (
|
|
248
|
+
<div key={`${id}-${part.text}-${index}`}>
|
|
249
|
+
{part.text || ''}
|
|
250
|
+
</div>
|
|
251
|
+
);
|
|
252
|
+
default:
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
)}
|
|
257
|
+
</MessageContent>
|
|
258
|
+
</Message>
|
|
259
|
+
|
|
260
|
+
<Actions
|
|
261
|
+
className={cn('mt-1 gap-0', role === 'user' && 'justify-end')}
|
|
262
|
+
>
|
|
263
|
+
{role === 'user' && (
|
|
264
|
+
<Action
|
|
265
|
+
tooltip="Re-run"
|
|
266
|
+
label="Re-run"
|
|
267
|
+
className="size-8 hover:bg-transparent!"
|
|
268
|
+
onClick={() =>
|
|
269
|
+
setValue(
|
|
270
|
+
parts
|
|
271
|
+
.filter((part) => part.type === 'text')
|
|
272
|
+
.map((part) => part.text)
|
|
273
|
+
.join('')
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
>
|
|
277
|
+
<RefreshCcw className="size-4" />
|
|
278
|
+
</Action>
|
|
279
|
+
)}
|
|
280
|
+
|
|
281
|
+
<Action
|
|
282
|
+
tooltip="Copy to clipboard"
|
|
283
|
+
label="Copy to clipboard"
|
|
284
|
+
className="size-8 hover:bg-transparent!"
|
|
285
|
+
onClick={() => {
|
|
286
|
+
const text = errorInfo
|
|
287
|
+
? formatErrorForCopy(errorInfo)
|
|
288
|
+
: parts
|
|
289
|
+
.filter((part) => part.type === 'text')
|
|
290
|
+
.map((part) => part.text)
|
|
291
|
+
.join('');
|
|
292
|
+
|
|
293
|
+
navigator.clipboard.writeText(text);
|
|
294
|
+
}}
|
|
295
|
+
>
|
|
296
|
+
<Copy className="size-4" />
|
|
297
|
+
</Action>
|
|
298
|
+
</Actions>
|
|
299
|
+
</>
|
|
300
|
+
)}
|
|
301
|
+
</div>
|
|
302
|
+
);
|
|
303
|
+
})}
|
|
304
|
+
</ConversationContent>
|
|
305
|
+
)}
|
|
306
|
+
|
|
307
|
+
<ConversationScrollButton />
|
|
308
|
+
</Conversation>
|
|
309
|
+
|
|
310
|
+
<InputSection
|
|
311
|
+
agents={agents}
|
|
312
|
+
clearAgentState={clearAgentState}
|
|
313
|
+
isLoading={isLoading}
|
|
314
|
+
isSchemaOpen={schemaOpen}
|
|
315
|
+
onChange={setValue}
|
|
316
|
+
onSchemaToggle={onSchemaToggle}
|
|
317
|
+
onSubmit={handleSubmit}
|
|
318
|
+
selectedAgent={selectedAgent}
|
|
319
|
+
setSelectedAgent={setSelectedAgent}
|
|
320
|
+
value={value}
|
|
321
|
+
/>
|
|
322
|
+
</div>
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export default Chat;
|