@langgraph-js/ui 1.2.0 → 1.2.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.
Files changed (41) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +6 -6
  3. package/cli.mjs +36 -36
  4. package/dist/assets/{index-Du4LMUX2.css → index-BlHtM5cu.css} +1 -1
  5. package/dist/assets/index-C7SfDwhG.js +192 -0
  6. package/dist/index.html +14 -14
  7. package/index.html +22 -22
  8. package/package.json +8 -9
  9. package/src/chat/Chat.tsx +151 -164
  10. package/src/chat/FileUpload/index.ts +105 -105
  11. package/src/chat/chat.css +403 -403
  12. package/src/chat/components/FileList.css +128 -128
  13. package/src/chat/components/FileList.tsx +73 -73
  14. package/src/chat/components/HistoryList.tsx +192 -192
  15. package/src/chat/components/JsonEditorPopup.css +80 -80
  16. package/src/chat/components/JsonEditorPopup.tsx +56 -56
  17. package/src/chat/components/JsonToMessage/JsonToMessage.css +104 -0
  18. package/src/chat/components/JsonToMessage/JsonToMessage.tsx +114 -0
  19. package/src/chat/components/JsonToMessage/JsonToMessageButton.tsx +27 -0
  20. package/src/chat/components/JsonToMessage/index.tsx +5 -0
  21. package/src/chat/components/MessageAI.tsx +24 -24
  22. package/src/chat/components/MessageBox.tsx +39 -0
  23. package/src/chat/components/MessageHuman.tsx +55 -55
  24. package/src/chat/components/MessageTool.tsx +46 -46
  25. package/src/chat/components/UsageMetadata.tsx +40 -40
  26. package/src/chat/context/ChatContext.tsx +31 -29
  27. package/src/chat/context/ExtraParamsContext.tsx +41 -41
  28. package/src/chat/store/index.ts +25 -24
  29. package/src/chat/tools.ts +33 -33
  30. package/src/chat/types.ts +16 -16
  31. package/src/hooks/useLocalStorage.ts +27 -27
  32. package/src/index.ts +1 -1
  33. package/src/login/Login.css +93 -93
  34. package/src/login/Login.tsx +92 -92
  35. package/test/App.tsx +9 -9
  36. package/test/main.tsx +5 -5
  37. package/test/vite-env.d.ts +1 -1
  38. package/tsconfig.json +21 -21
  39. package/tsconfig.node.json +9 -9
  40. package/vite.config.ts +17 -17
  41. package/dist/assets/index-BHPbGlnP.js +0 -192
@@ -1,129 +1,129 @@
1
- .file-list {
2
- display: flex;
3
- gap: 0.5rem;
4
- border-radius: 8px;
5
- flex:1;
6
- }
7
-
8
- .file-list-header {
9
- margin-bottom: 1rem;
10
- }
11
-
12
- .file-upload-button {
13
- display: inline-flex;
14
- align-items: center;
15
- justify-content: center;
16
- width: 80px;
17
- height: 80px;
18
- color: #929292;
19
- background-color: #ebebeb;
20
- border-radius: 6px;
21
- cursor: pointer;
22
- transition: all 0.2s;
23
- }
24
-
25
- .file-upload-button svg {
26
- width: 32px;
27
- height: 32px;
28
- }
29
-
30
- .file-upload-button.empty {
31
- width: 32px;
32
- height: 32px;
33
- }
34
-
35
- .file-upload-button.empty svg {
36
- width: 20px;
37
- height: 20px;
38
- }
39
-
40
- .file-list-content {
41
- display: flex;
42
- flex-wrap: wrap;
43
- gap: 0.5rem;
44
- }
45
-
46
- .file-item {
47
- position: relative;
48
- width: 80px;
49
- height: 80px;
50
- border-radius: 6px;
51
- overflow: hidden;
52
- }
53
- .file-item img{
54
- border: 1px solid #e5e7eb;
55
- overflow: hidden;
56
- }
57
-
58
- .file-preview {
59
- width: 100%;
60
- height: 100%;
61
- object-fit: cover;
62
- }
63
-
64
- .file-info {
65
- padding: 0.75rem;
66
- }
67
-
68
- .file-name {
69
- display: block;
70
- font-size: 0.875rem;
71
- color: #374151;
72
- margin-bottom: 0.5rem;
73
- white-space: nowrap;
74
- overflow: hidden;
75
- text-overflow: ellipsis;
76
- }
77
-
78
- .file-actions {
79
- display: flex;
80
- gap: 0.5rem;
81
- }
82
-
83
- .upload-button,
84
- .remove-button {
85
- flex: 1;
86
- padding: 0.375rem 0.75rem;
87
- border: none;
88
- border-radius: 4px;
89
- font-size: 0.75rem;
90
- cursor: pointer;
91
- transition: all 0.2s;
92
- }
93
-
94
- .upload-button {
95
- color: black;
96
- }
97
-
98
- .upload-button:hover {
99
- background-color: #2563eb;
100
- }
101
-
102
- .remove-button {
103
- position: absolute;
104
- top: 2px;
105
- right: 2px;
106
- width: 20px;
107
- height: 20px;
108
- background-color: rgba(0, 0, 0, 0.5);
109
- color: white;
110
- border: none;
111
- border-radius: 50%;
112
- cursor: pointer;
113
- font-size: 16px;
114
- line-height: 1;
115
- display: flex;
116
- align-items: center;
117
- justify-content: center;
118
- transition: background-color 0.2s;
119
- }
120
-
121
- .remove-button:hover {
122
- background-color: rgba(0, 0, 0, 0.7);
123
- }
124
-
125
- .upload-button:disabled,
126
- .remove-button:disabled {
127
- opacity: 0.5;
128
- cursor: not-allowed;
1
+ .file-list {
2
+ display: flex;
3
+ gap: 0.5rem;
4
+ border-radius: 8px;
5
+ flex:1;
6
+ }
7
+
8
+ .file-list-header {
9
+ margin-bottom: 1rem;
10
+ }
11
+
12
+ .file-upload-button {
13
+ display: inline-flex;
14
+ align-items: center;
15
+ justify-content: center;
16
+ width: 80px;
17
+ height: 80px;
18
+ color: #929292;
19
+ background-color: #ebebeb;
20
+ border-radius: 6px;
21
+ cursor: pointer;
22
+ transition: all 0.2s;
23
+ }
24
+
25
+ .file-upload-button svg {
26
+ width: 32px;
27
+ height: 32px;
28
+ }
29
+
30
+ .file-upload-button.empty {
31
+ width: 32px;
32
+ height: 32px;
33
+ }
34
+
35
+ .file-upload-button.empty svg {
36
+ width: 20px;
37
+ height: 20px;
38
+ }
39
+
40
+ .file-list-content {
41
+ display: flex;
42
+ flex-wrap: wrap;
43
+ gap: 0.5rem;
44
+ }
45
+
46
+ .file-item {
47
+ position: relative;
48
+ width: 80px;
49
+ height: 80px;
50
+ border-radius: 6px;
51
+ overflow: hidden;
52
+ }
53
+ .file-item img{
54
+ border: 1px solid #e5e7eb;
55
+ overflow: hidden;
56
+ }
57
+
58
+ .file-preview {
59
+ width: 100%;
60
+ height: 100%;
61
+ object-fit: cover;
62
+ }
63
+
64
+ .file-info {
65
+ padding: 0.75rem;
66
+ }
67
+
68
+ .file-name {
69
+ display: block;
70
+ font-size: 0.875rem;
71
+ color: #374151;
72
+ margin-bottom: 0.5rem;
73
+ white-space: nowrap;
74
+ overflow: hidden;
75
+ text-overflow: ellipsis;
76
+ }
77
+
78
+ .file-actions {
79
+ display: flex;
80
+ gap: 0.5rem;
81
+ }
82
+
83
+ .upload-button,
84
+ .remove-button {
85
+ flex: 1;
86
+ padding: 0.375rem 0.75rem;
87
+ border: none;
88
+ border-radius: 4px;
89
+ font-size: 0.75rem;
90
+ cursor: pointer;
91
+ transition: all 0.2s;
92
+ }
93
+
94
+ .upload-button {
95
+ color: black;
96
+ }
97
+
98
+ .upload-button:hover {
99
+ background-color: #2563eb;
100
+ }
101
+
102
+ .remove-button {
103
+ position: absolute;
104
+ top: 2px;
105
+ right: 2px;
106
+ width: 20px;
107
+ height: 20px;
108
+ background-color: rgba(0, 0, 0, 0.5);
109
+ color: white;
110
+ border: none;
111
+ border-radius: 50%;
112
+ cursor: pointer;
113
+ font-size: 16px;
114
+ line-height: 1;
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ transition: background-color 0.2s;
119
+ }
120
+
121
+ .remove-button:hover {
122
+ background-color: rgba(0, 0, 0, 0.7);
123
+ }
124
+
125
+ .upload-button:disabled,
126
+ .remove-button:disabled {
127
+ opacity: 0.5;
128
+ cursor: not-allowed;
129
129
  }
@@ -1,73 +1,73 @@
1
- import React, { useState, useCallback } from "react";
2
- import { TmpFilesClient } from "../FileUpload";
3
- import "./FileList.css";
4
-
5
- interface FileListProps {
6
- onFileUploaded: (url: string) => void;
7
- }
8
-
9
- const FileList: React.FC<FileListProps> = ({ onFileUploaded }) => {
10
- const [files, setFiles] = useState<File[]>([]);
11
- const client = new TmpFilesClient();
12
- const MAX_FILES = 3;
13
-
14
- const handleFileChange = useCallback(
15
- async (event: React.ChangeEvent<HTMLInputElement>) => {
16
- const selectedFiles = Array.from(event.target.files || []);
17
- const imageFiles = selectedFiles.filter((file) => file.type.startsWith("image/"));
18
-
19
- // 检查是否超过最大数量限制
20
- if (files.length + imageFiles.length > MAX_FILES) {
21
- alert(`最多只能上传${MAX_FILES}张图片`);
22
- event.target.value = "";
23
- return;
24
- }
25
-
26
- setFiles((prev) => [...prev, ...imageFiles]);
27
-
28
- for (const file of imageFiles) {
29
- try {
30
- const result = await client.upload(file);
31
- if (result.data?.url) {
32
- onFileUploaded(result.data.url);
33
- }
34
- } catch (error) {
35
- console.error("Upload failed:", error);
36
- }
37
- }
38
-
39
- event.target.value = "";
40
- },
41
- [onFileUploaded, files.length]
42
- );
43
-
44
- const removeFile = useCallback((index: number) => {
45
- setFiles((prev) => prev.filter((_, i) => i !== index));
46
- }, []);
47
-
48
- return (
49
- <div className="file-list">
50
- {files.length < MAX_FILES && (
51
- <label className={`file-upload-button ${files.length === 0 ? "empty" : ""}`}>
52
- <svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
53
- <path d="M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z"/>
54
- <path d="M20 4h-3.17l-1.24-1.35A1.99 1.99 0 0 0 14.12 2H9.88c-.56 0-1.1.24-1.48.65L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 13c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/>
55
- </svg>
56
- <input type="file" accept="image/*" multiple onChange={handleFileChange} style={{ display: "none" }} />
57
- </label>
58
- )}
59
- <div className="file-list-content">
60
- {files.map((file, index) => (
61
- <div key={index} className="file-item">
62
- <img src={URL.createObjectURL(file)} alt={file.name} className="file-preview" />
63
- <button className="remove-button" onClick={() => removeFile(index)}>
64
- ×
65
- </button>
66
- </div>
67
- ))}
68
- </div>
69
- </div>
70
- );
71
- };
72
-
73
- export default FileList;
1
+ import React, { useState, useCallback } from "react";
2
+ import { TmpFilesClient } from "../FileUpload";
3
+ import "./FileList.css";
4
+
5
+ interface FileListProps {
6
+ onFileUploaded: (url: string) => void;
7
+ }
8
+
9
+ const FileList: React.FC<FileListProps> = ({ onFileUploaded }) => {
10
+ const [files, setFiles] = useState<File[]>([]);
11
+ const client = new TmpFilesClient();
12
+ const MAX_FILES = 3;
13
+
14
+ const handleFileChange = useCallback(
15
+ async (event: React.ChangeEvent<HTMLInputElement>) => {
16
+ const selectedFiles = Array.from(event.target.files || []);
17
+ const imageFiles = selectedFiles.filter((file) => file.type.startsWith("image/"));
18
+
19
+ // 检查是否超过最大数量限制
20
+ if (files.length + imageFiles.length > MAX_FILES) {
21
+ alert(`最多只能上传${MAX_FILES}张图片`);
22
+ event.target.value = "";
23
+ return;
24
+ }
25
+
26
+ setFiles((prev) => [...prev, ...imageFiles]);
27
+
28
+ for (const file of imageFiles) {
29
+ try {
30
+ const result = await client.upload(file);
31
+ if (result.data?.url) {
32
+ onFileUploaded(result.data.url);
33
+ }
34
+ } catch (error) {
35
+ console.error("Upload failed:", error);
36
+ }
37
+ }
38
+
39
+ event.target.value = "";
40
+ },
41
+ [onFileUploaded, files.length]
42
+ );
43
+
44
+ const removeFile = useCallback((index: number) => {
45
+ setFiles((prev) => prev.filter((_, i) => i !== index));
46
+ }, []);
47
+
48
+ return (
49
+ <div className="file-list">
50
+ {files.length < MAX_FILES && (
51
+ <label className={`file-upload-button ${files.length === 0 ? "empty" : ""}`}>
52
+ <svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
53
+ <path d="M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7z"/>
54
+ <path d="M20 4h-3.17l-1.24-1.35A1.99 1.99 0 0 0 14.12 2H9.88c-.56 0-1.1.24-1.48.65L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 13c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5z"/>
55
+ </svg>
56
+ <input type="file" accept="image/*" multiple onChange={handleFileChange} style={{ display: "none" }} />
57
+ </label>
58
+ )}
59
+ <div className="file-list-content">
60
+ {files.map((file, index) => (
61
+ <div key={index} className="file-item">
62
+ <img src={URL.createObjectURL(file)} alt={file.name} className="file-preview" />
63
+ <button className="remove-button" onClick={() => removeFile(index)}>
64
+ ×
65
+ </button>
66
+ </div>
67
+ ))}
68
+ </div>
69
+ </div>
70
+ );
71
+ };
72
+
73
+ export default FileList;