@langgraph-js/ui 1.1.0 → 1.2.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.
@@ -1,192 +1,192 @@
1
- import React from "react";
2
- import { useChat } from "../context/ChatContext";
3
- import { getHistoryContent } from "@langgraph-js/sdk";
4
-
5
- interface HistoryListProps {
6
- onClose: () => void;
7
- formatTime: (date: Date) => string;
8
- }
9
-
10
- const HistoryList: React.FC<HistoryListProps> = ({ onClose, formatTime }) => {
11
- const { historyList, currentChatId, refreshHistoryList, createNewChat, deleteHistoryChat, toHistoryChat } = useChat();
12
- return (
13
- <div className="history-list">
14
- <div className="history-header">
15
- <div className="header-left">
16
- <h3>历史记录</h3>
17
- <button className="refresh-button" onClick={refreshHistoryList} title="刷新列表">
18
- 🔁
19
- </button>
20
- </div>
21
- <button className="close-button" onClick={onClose} title="关闭">
22
-
23
- </button>
24
- </div>
25
- <div className="history-content">
26
- <div
27
- className="history-items"
28
- onClick={() => {
29
- createNewChat();
30
- }}
31
- >
32
- <div className="history-item">
33
- <div className="history-title"> New Chat</div>
34
- </div>
35
- </div>
36
- {historyList.length === 0 ? (
37
- <div className="empty-history">暂无历史记录</div>
38
- ) : (
39
- <div className="history-items">
40
- {historyList
41
- .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())
42
- .map((thread) => (
43
- <div className={`history-item ${thread.thread_id === currentChatId ? "active" : ""}`} key={thread.thread_id}>
44
- <div className="history-info">
45
- <div className="history-title">{getHistoryContent(thread)}</div>
46
- <div className="history-meta">
47
- <span className="history-time">{formatTime(new Date(thread.created_at))}</span>
48
- <span className="history-status">{thread.status}</span>
49
- </div>
50
- </div>
51
- <div className="history-actions">
52
- <button
53
- className="action-button"
54
- onClick={() => {
55
- toHistoryChat(thread);
56
- }}
57
- title="恢复对话"
58
- >
59
-
60
- </button>
61
- <button
62
- className="action-button"
63
- onClick={async () => {
64
- await deleteHistoryChat(thread);
65
- }}
66
- title="删除对话"
67
- >
68
-
69
- </button>
70
- </div>
71
- </div>
72
- ))}
73
- </div>
74
- )}
75
- </div>
76
- <style>{`
77
- .history-list {
78
- background: #fff;
79
- border-radius: 8px;
80
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
81
- height: 100%;
82
- display: flex;
83
- flex-direction: column;
84
- }
85
-
86
- .history-header {
87
- padding: 16px;
88
- border-bottom: 1px solid #eee;
89
- display: flex;
90
- justify-content: space-between;
91
- align-items: center;
92
- }
93
-
94
- .header-left {
95
- display: flex;
96
- align-items: center;
97
- gap: 12px;
98
- }
99
-
100
- .history-header h3 {
101
- margin: 0;
102
- font-size: 18px;
103
- color: #333;
104
- }
105
-
106
- .history-content {
107
- flex: 1;
108
- overflow-y: auto;
109
- padding: 16px;
110
- }
111
-
112
- .history-items {
113
- display: flex;
114
- flex-direction: column;
115
- gap: 12px;
116
- }
117
-
118
- .history-item {
119
- display: flex;
120
- justify-content: space-between;
121
- align-items: center;
122
- padding: 12px;
123
- border-radius: 6px;
124
- background: #f8f9fa;
125
- transition: all 0.2s ease;
126
- }
127
-
128
- .history-item:hover {
129
- background: #f0f2f5;
130
- }
131
-
132
- .history-item.active {
133
- background: #e6f7ff;
134
- border: 1px solid #91d5ff;
135
- }
136
-
137
- .history-info {
138
- flex: 1;
139
- min-width: 0;
140
- }
141
-
142
- .history-title {
143
- font-size: 14px;
144
- color: #333;
145
- margin-bottom: 4px;
146
- white-space: nowrap;
147
- overflow: hidden;
148
- text-overflow: ellipsis;
149
- }
150
-
151
- .history-meta {
152
- display: flex;
153
- gap: 12px;
154
- color: #666;
155
- font-size: 12px;
156
- }
157
-
158
- .history-actions {
159
- display: flex;
160
- gap: 8px;
161
- margin-left: 12px;
162
- }
163
-
164
- .action-button, .close-button, .refresh-button {
165
- background: none;
166
- border: none;
167
- cursor: pointer;
168
- padding: 6px;
169
- font-size: 16px;
170
- border-radius: 4px;
171
- transition: all 0.2s ease;
172
- display: flex;
173
- align-items: center;
174
- justify-content: center;
175
- }
176
-
177
- .action-button:hover, .close-button:hover, .refresh-button:hover {
178
- background: rgba(0, 0, 0, 0.05);
179
- transform: scale(1.1);
180
- }
181
-
182
- .empty-history {
183
- text-align: center;
184
- color: #999;
185
- padding: 32px 0;
186
- }
187
- `}</style>
188
- </div>
189
- );
190
- };
191
-
192
- export default HistoryList;
1
+ import React from "react";
2
+ import { useChat } from "../context/ChatContext";
3
+ import { getHistoryContent } from "@langgraph-js/sdk";
4
+
5
+ interface HistoryListProps {
6
+ onClose: () => void;
7
+ formatTime: (date: Date) => string;
8
+ }
9
+
10
+ const HistoryList: React.FC<HistoryListProps> = ({ onClose, formatTime }) => {
11
+ const { historyList, currentChatId, refreshHistoryList, createNewChat, deleteHistoryChat, toHistoryChat } = useChat();
12
+ return (
13
+ <div className="history-list">
14
+ <div className="history-header">
15
+ <div className="header-left">
16
+ <h3>历史记录</h3>
17
+ <button className="refresh-button" onClick={refreshHistoryList} title="刷新列表">
18
+ 🔁
19
+ </button>
20
+ </div>
21
+ <button className="close-button" onClick={onClose} title="关闭">
22
+
23
+ </button>
24
+ </div>
25
+ <div className="history-content">
26
+ <div
27
+ className="history-items"
28
+ onClick={() => {
29
+ createNewChat();
30
+ }}
31
+ >
32
+ <div className="history-item">
33
+ <div className="history-title"> New Chat</div>
34
+ </div>
35
+ </div>
36
+ {historyList.length === 0 ? (
37
+ <div className="empty-history">暂无历史记录</div>
38
+ ) : (
39
+ <div className="history-items">
40
+ {historyList
41
+ .sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime())
42
+ .map((thread) => (
43
+ <div className={`history-item ${thread.thread_id === currentChatId ? "active" : ""}`} key={thread.thread_id}>
44
+ <div className="history-info">
45
+ <div className="history-title">{getHistoryContent(thread)}</div>
46
+ <div className="history-meta">
47
+ <span className="history-time">{formatTime(new Date(thread.created_at))}</span>
48
+ <span className="history-status">{thread.status}</span>
49
+ </div>
50
+ </div>
51
+ <div className="history-actions">
52
+ <button
53
+ className="action-button"
54
+ onClick={() => {
55
+ toHistoryChat(thread);
56
+ }}
57
+ title="恢复对话"
58
+ >
59
+
60
+ </button>
61
+ <button
62
+ className="action-button"
63
+ onClick={async () => {
64
+ await deleteHistoryChat(thread);
65
+ }}
66
+ title="删除对话"
67
+ >
68
+
69
+ </button>
70
+ </div>
71
+ </div>
72
+ ))}
73
+ </div>
74
+ )}
75
+ </div>
76
+ <style>{`
77
+ .history-list {
78
+ background: #fff;
79
+ border-radius: 8px;
80
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
81
+ height: 100%;
82
+ display: flex;
83
+ flex-direction: column;
84
+ }
85
+
86
+ .history-header {
87
+ padding: 16px;
88
+ border-bottom: 1px solid #eee;
89
+ display: flex;
90
+ justify-content: space-between;
91
+ align-items: center;
92
+ }
93
+
94
+ .header-left {
95
+ display: flex;
96
+ align-items: center;
97
+ gap: 12px;
98
+ }
99
+
100
+ .history-header h3 {
101
+ margin: 0;
102
+ font-size: 18px;
103
+ color: #333;
104
+ }
105
+
106
+ .history-content {
107
+ flex: 1;
108
+ overflow-y: auto;
109
+ padding: 16px;
110
+ }
111
+
112
+ .history-items {
113
+ display: flex;
114
+ flex-direction: column;
115
+ gap: 12px;
116
+ }
117
+
118
+ .history-item {
119
+ display: flex;
120
+ justify-content: space-between;
121
+ align-items: center;
122
+ padding: 12px;
123
+ border-radius: 6px;
124
+ background: #f8f9fa;
125
+ transition: all 0.2s ease;
126
+ }
127
+
128
+ .history-item:hover {
129
+ background: #f0f2f5;
130
+ }
131
+
132
+ .history-item.active {
133
+ background: #e6f7ff;
134
+ border: 1px solid #91d5ff;
135
+ }
136
+
137
+ .history-info {
138
+ flex: 1;
139
+ min-width: 0;
140
+ }
141
+
142
+ .history-title {
143
+ font-size: 14px;
144
+ color: #333;
145
+ margin-bottom: 4px;
146
+ white-space: nowrap;
147
+ overflow: hidden;
148
+ text-overflow: ellipsis;
149
+ }
150
+
151
+ .history-meta {
152
+ display: flex;
153
+ gap: 12px;
154
+ color: #666;
155
+ font-size: 12px;
156
+ }
157
+
158
+ .history-actions {
159
+ display: flex;
160
+ gap: 8px;
161
+ margin-left: 12px;
162
+ }
163
+
164
+ .action-button, .close-button, .refresh-button {
165
+ background: none;
166
+ border: none;
167
+ cursor: pointer;
168
+ padding: 6px;
169
+ font-size: 16px;
170
+ border-radius: 4px;
171
+ transition: all 0.2s ease;
172
+ display: flex;
173
+ align-items: center;
174
+ justify-content: center;
175
+ }
176
+
177
+ .action-button:hover, .close-button:hover, .refresh-button:hover {
178
+ background: rgba(0, 0, 0, 0.05);
179
+ transform: scale(1.1);
180
+ }
181
+
182
+ .empty-history {
183
+ text-align: center;
184
+ color: #999;
185
+ padding: 32px 0;
186
+ }
187
+ `}</style>
188
+ </div>
189
+ );
190
+ };
191
+
192
+ export default HistoryList;
@@ -0,0 +1,81 @@
1
+ .json-editor-popup-overlay {
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ right: 0;
6
+ bottom: 0;
7
+ background-color: rgba(0, 0, 0, 0.5);
8
+ display: flex;
9
+ align-items: center;
10
+ justify-content: center;
11
+ z-index: 1000;
12
+ }
13
+
14
+ .json-editor-popup-content {
15
+ background-color: white;
16
+ padding: 20px;
17
+ border-radius: 8px;
18
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
19
+ width: 80%;
20
+ max-width: 600px;
21
+ display: flex;
22
+ flex-direction: column;
23
+ }
24
+
25
+ .json-editor-popup-content h2 {
26
+ margin-top: 0;
27
+ margin-bottom: 15px;
28
+ font-size: 1.5rem;
29
+ color: #333;
30
+ }
31
+
32
+ .json-editor-popup-content textarea {
33
+ width: 100%;
34
+ padding: 10px;
35
+ border: 1px solid #ccc;
36
+ border-radius: 4px;
37
+ font-family: monospace;
38
+ font-size: 0.9rem;
39
+ resize: vertical;
40
+ box-sizing: border-box;
41
+ }
42
+
43
+ .json-editor-popup-content .error-message {
44
+ color: red;
45
+ font-size: 0.8rem;
46
+ margin-top: 5px;
47
+ margin-bottom: 10px;
48
+ }
49
+
50
+ .popup-actions {
51
+ display: flex;
52
+ justify-content: flex-end;
53
+ margin-top: 15px;
54
+ gap: 10px;
55
+ }
56
+
57
+ .popup-actions button {
58
+ padding: 8px 16px;
59
+ border: none;
60
+ border-radius: 4px;
61
+ cursor: pointer;
62
+ font-size: 0.9rem;
63
+ }
64
+
65
+ .popup-actions .save-button {
66
+ background-color: #3b82f6;
67
+ color: white;
68
+ }
69
+
70
+ .popup-actions .save-button:hover {
71
+ background-color: #2563eb;
72
+ }
73
+
74
+ .popup-actions .cancel-button {
75
+ background-color: #e5e7eb;
76
+ color: #374151;
77
+ }
78
+
79
+ .popup-actions .cancel-button:hover {
80
+ background-color: #d1d5db;
81
+ }
@@ -0,0 +1,57 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import './JsonEditorPopup.css';
3
+
4
+ interface JsonEditorPopupProps {
5
+ isOpen: boolean;
6
+ initialJson: object;
7
+ onClose: () => void;
8
+ onSave: (jsonData: object) => void;
9
+ }
10
+
11
+ const JsonEditorPopup: React.FC<JsonEditorPopupProps> = ({ isOpen, initialJson, onClose, onSave }) => {
12
+ const [jsonString, setJsonString] = useState('');
13
+ const [error, setError] = useState<string | null>(null);
14
+
15
+ useEffect(() => {
16
+ setJsonString(JSON.stringify(initialJson, null, 2));
17
+ setError(null); // Reset error when initialJson changes or popup opens
18
+ }, [initialJson, isOpen]);
19
+
20
+ if (!isOpen) {
21
+ return null;
22
+ }
23
+
24
+ const handleSave = () => {
25
+ try {
26
+ const parsedJson = JSON.parse(jsonString);
27
+ onSave(parsedJson);
28
+ onClose();
29
+ } catch (e) {
30
+ setError('JSON 格式无效,请检查后重试。');
31
+ console.error("Invalid JSON format:", e);
32
+ }
33
+ };
34
+
35
+ return (
36
+ <div className="json-editor-popup-overlay">
37
+ <div className="json-editor-popup-content">
38
+ <h2>编辑 Extra Parameters</h2>
39
+ <textarea
40
+ value={jsonString}
41
+ onChange={(e) => {
42
+ setJsonString(e.target.value);
43
+ setError(null); // Clear error on edit
44
+ }}
45
+ rows={15}
46
+ />
47
+ {error && <p className="error-message">{error}</p>}
48
+ <div className="popup-actions">
49
+ <button onClick={onClose} className="cancel-button">取消</button>
50
+ <button onClick={handleSave} className="save-button">保存</button>
51
+ </div>
52
+ </div>
53
+ </div>
54
+ );
55
+ };
56
+
57
+ export default JsonEditorPopup;
@@ -1,24 +1,24 @@
1
- import React from "react";
2
- import { RenderMessage } from "@langgraph-js/sdk";
3
- import { UsageMetadata } from "./UsageMetadata";
4
- import { getMessageContent } from "@langgraph-js/sdk";
5
- import Markdown from 'react-markdown'
6
- import remarkGfm from 'remark-gfm'
7
- interface MessageAIProps {
8
- message: RenderMessage;
9
- }
10
-
11
- const MessageAI: React.FC<MessageAIProps> = ({ message }) => {
12
- return (
13
- <div className="message ai">
14
- <div className="message-content">
15
- <div className="message-text markdown-body">
16
- <Markdown remarkPlugins={[remarkGfm]}>{getMessageContent(message.content)}</Markdown>
17
- </div>
18
- <UsageMetadata response_metadata={message.response_metadata as any} usage_metadata={message.usage_metadata||{}} spend_time={message.spend_time} />
19
- </div>
20
- </div>
21
- );
22
- };
23
-
24
- export default MessageAI;
1
+ import React from "react";
2
+ import { RenderMessage } from "@langgraph-js/sdk";
3
+ import { UsageMetadata } from "./UsageMetadata";
4
+ import { getMessageContent } from "@langgraph-js/sdk";
5
+ import Markdown from 'react-markdown'
6
+ import remarkGfm from 'remark-gfm'
7
+ interface MessageAIProps {
8
+ message: RenderMessage;
9
+ }
10
+
11
+ const MessageAI: React.FC<MessageAIProps> = ({ message }) => {
12
+ return (
13
+ <div className="message ai">
14
+ <div className="message-content">
15
+ <div className="message-text markdown-body">
16
+ <Markdown remarkPlugins={[remarkGfm]}>{getMessageContent(message.content)}</Markdown>
17
+ </div>
18
+ <UsageMetadata response_metadata={message.response_metadata as any} usage_metadata={message.usage_metadata||{}} spend_time={message.spend_time} />
19
+ </div>
20
+ </div>
21
+ );
22
+ };
23
+
24
+ export default MessageAI;