@langgraph-js/ui 1.6.0 → 1.7.1
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/assets/index-BrQ4jQUO.js +248 -0
- package/dist/assets/{index-7vem5Peg.css → index-C0dczJ0v.css} +1 -1
- package/dist/index.html +2 -2
- package/package.json +3 -2
- package/src/chat/Chat.tsx +7 -6
- package/src/chat/components/JsonEditorPopup.tsx +158 -22
- package/src/chat/components/MessageBox.tsx +2 -3
- package/src/chat/components/MessageTool.tsx +37 -7
- package/src/chat/index.css +4 -0
- package/src/index.ts +1 -0
- package/dist/assets/index-CZ6k2dGe.js +0 -235
|
@@ -3,7 +3,6 @@ import MessageHuman from "./MessageHuman";
|
|
|
3
3
|
import MessageAI from "./MessageAI";
|
|
4
4
|
import MessageTool from "./MessageTool";
|
|
5
5
|
import { formatTokens, getMessageContent, LangGraphClient, RenderMessage } from "@langgraph-js/sdk";
|
|
6
|
-
import { motion } from "motion/react";
|
|
7
6
|
|
|
8
7
|
export const MessagesBox = ({
|
|
9
8
|
renderMessages,
|
|
@@ -19,7 +18,7 @@ export const MessagesBox = ({
|
|
|
19
18
|
return (
|
|
20
19
|
<div className="flex flex-col gap-4 w-full">
|
|
21
20
|
{renderMessages.map((message, index) => (
|
|
22
|
-
<
|
|
21
|
+
<div key={message.unique_id}>
|
|
23
22
|
{message.type === "human" ? (
|
|
24
23
|
<MessageHuman content={message.content} />
|
|
25
24
|
) : message.type === "tool" ? (
|
|
@@ -34,7 +33,7 @@ export const MessagesBox = ({
|
|
|
34
33
|
) : (
|
|
35
34
|
<MessageAI message={message} />
|
|
36
35
|
)}
|
|
37
|
-
</
|
|
36
|
+
</div>
|
|
38
37
|
))}
|
|
39
38
|
</div>
|
|
40
39
|
);
|
|
@@ -4,6 +4,9 @@ import { UsageMetadata } from "./UsageMetadata";
|
|
|
4
4
|
import { useChat } from "../context/ChatContext";
|
|
5
5
|
import Markdown from "react-markdown";
|
|
6
6
|
import remarkGfm from "remark-gfm";
|
|
7
|
+
import { Highlight, themes } from "prism-react-renderer";
|
|
8
|
+
|
|
9
|
+
const TOOL_COLORS = ["border-red-400", "border-blue-400", "border-green-500", "border-yellow-400", "border-purple-400", "border-pink-400", "border-indigo-400"];
|
|
7
10
|
|
|
8
11
|
interface MessageToolProps {
|
|
9
12
|
message: ToolMessage & RenderMessage;
|
|
@@ -14,16 +17,26 @@ interface MessageToolProps {
|
|
|
14
17
|
onToggleCollapse: () => void;
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
const getToolColorClass = (tool_name: string) => {
|
|
21
|
+
let hash = 0;
|
|
22
|
+
for (let i = 0; i < tool_name.length; i++) {
|
|
23
|
+
hash = tool_name.charCodeAt(i) + ((hash << 5) - hash);
|
|
24
|
+
}
|
|
25
|
+
const index = Math.abs(hash % TOOL_COLORS.length);
|
|
26
|
+
return TOOL_COLORS[index];
|
|
27
|
+
};
|
|
28
|
+
|
|
17
29
|
const MessageTool: React.FC<MessageToolProps> = ({ message, client, getMessageContent, formatTokens, isCollapsed, onToggleCollapse }) => {
|
|
18
30
|
const { getToolUIRender } = useChat();
|
|
19
31
|
const render = getToolUIRender(message.name!);
|
|
32
|
+
const borderColorClass = getToolColorClass(message.name!);
|
|
20
33
|
return (
|
|
21
34
|
<div className="flex flex-col w-full">
|
|
22
35
|
{render ? (
|
|
23
36
|
(render(message) as JSX.Element)
|
|
24
37
|
) : (
|
|
25
|
-
<div className=
|
|
26
|
-
<div className="flex items-center justify-between p-3 cursor-pointer hover:bg-gray-
|
|
38
|
+
<div className={`flex flex-col w-full bg-white rounded-lg shadow-sm border-2 ${borderColorClass} overflow-hidden`}>
|
|
39
|
+
<div className="flex items-center justify-between p-3 cursor-pointer hover:bg-gray-100 transition-colors" onClick={onToggleCollapse}>
|
|
27
40
|
<div className="text-sm font-medium text-gray-700" onClick={() => console.log(message)}>
|
|
28
41
|
{message.node_name} | {message.name}
|
|
29
42
|
</div>
|
|
@@ -59,19 +72,24 @@ const Previewer = ({ content }: { content: string }) => {
|
|
|
59
72
|
};
|
|
60
73
|
const isJSON = content.startsWith("{") && content.endsWith("}") && validJSON();
|
|
61
74
|
const isMarkdown = content.includes("#") || content.includes("```") || content.includes("*");
|
|
62
|
-
const [jsonMode, setJsonMode] = useState(
|
|
75
|
+
const [jsonMode, setJsonMode] = useState(isJSON);
|
|
63
76
|
const [markdownMode, setMarkdownMode] = useState(false);
|
|
64
|
-
|
|
77
|
+
const copyToClipboard = () => {
|
|
78
|
+
navigator.clipboard.writeText(content);
|
|
79
|
+
};
|
|
65
80
|
return (
|
|
66
81
|
<div className={`flex flex-col`}>
|
|
67
82
|
<div className="flex gap-2 mb-2">
|
|
83
|
+
<button onClick={copyToClipboard} className="px-2 py-1 text-xs font-medium text-gray-600 bg-green-100 rounded hover:bg-green-200 transition-colors">
|
|
84
|
+
copy
|
|
85
|
+
</button>
|
|
68
86
|
{isJSON && (
|
|
69
|
-
<button onClick={() => setJsonMode(!jsonMode)} className="px-2 py-1 text-xs font-medium text-gray-600 bg-
|
|
87
|
+
<button onClick={() => setJsonMode(!jsonMode)} className="px-2 py-1 text-xs font-medium text-gray-600 bg-orange-100 rounded hover:bg-orange-200 transition-colors">
|
|
70
88
|
json
|
|
71
89
|
</button>
|
|
72
90
|
)}
|
|
73
91
|
{isMarkdown && (
|
|
74
|
-
<button onClick={() => setMarkdownMode(!markdownMode)} className="px-2 py-1 text-xs font-medium text-gray-600 bg-
|
|
92
|
+
<button onClick={() => setMarkdownMode(!markdownMode)} className="px-2 py-1 text-xs font-medium text-gray-600 bg-blue-100 rounded hover:bg-blue-200 transition-colors">
|
|
75
93
|
markdown
|
|
76
94
|
</button>
|
|
77
95
|
)}
|
|
@@ -79,7 +97,19 @@ const Previewer = ({ content }: { content: string }) => {
|
|
|
79
97
|
|
|
80
98
|
<div className="flex flex-col max-h-[300px] overflow-auto border border-gray-200 rounded p-2 w-full text-xs font-mono whitespace-pre-wrap">
|
|
81
99
|
{jsonMode && isJSON ? (
|
|
82
|
-
<
|
|
100
|
+
<Highlight code={JSON.stringify(JSON.parse(content), null, 2)} language="json" theme={themes.oneLight}>
|
|
101
|
+
{({ className, style, tokens, getLineProps, getTokenProps }) => (
|
|
102
|
+
<pre style={style}>
|
|
103
|
+
{tokens.map((line, i) => (
|
|
104
|
+
<div key={i} {...getLineProps({ line })}>
|
|
105
|
+
{line.map((token, key) => (
|
|
106
|
+
<span key={key} {...getTokenProps({ token })} />
|
|
107
|
+
))}
|
|
108
|
+
</div>
|
|
109
|
+
))}
|
|
110
|
+
</pre>
|
|
111
|
+
)}
|
|
112
|
+
</Highlight>
|
|
83
113
|
) : markdownMode && isMarkdown ? (
|
|
84
114
|
<div className="markdown-body">
|
|
85
115
|
<Markdown remarkPlugins={[remarkGfm]}>{content}</Markdown>
|
package/src/index.ts
CHANGED