@copilotkit/react-ui 1.8.6 → 1.8.7-next.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.
- package/CHANGELOG.md +10 -0
- package/dist/{chunk-Q2YY2NX3.mjs → chunk-24TDU7MY.mjs} +2 -2
- package/dist/{chunk-XNQO5AZZ.mjs → chunk-ABHUX6T6.mjs} +2 -2
- package/dist/{chunk-QJKMOGWN.mjs → chunk-BDNHZ3GW.mjs} +4 -3
- package/dist/chunk-BDNHZ3GW.mjs.map +1 -0
- package/dist/{chunk-NMNC4ROZ.mjs → chunk-DSQGQJI4.mjs} +2 -2
- package/dist/{chunk-HEIDCT7I.mjs → chunk-HWMFMBJC.mjs} +2 -2
- package/dist/chunk-HWMFMBJC.mjs.map +1 -0
- package/dist/{chunk-UN2E3HCK.mjs → chunk-IEMQ2SQW.mjs} +6 -4
- package/dist/chunk-IEMQ2SQW.mjs.map +1 -0
- package/dist/{chunk-ZY25LVYR.mjs → chunk-IJADIQAR.mjs} +20 -2
- package/dist/chunk-IJADIQAR.mjs.map +1 -0
- package/dist/{chunk-X6EFGEBJ.mjs → chunk-JOL7NS2W.mjs} +2 -2
- package/dist/{chunk-PCTCOQK2.mjs → chunk-KENCH7RN.mjs} +2 -2
- package/dist/{chunk-ZLRUNNS7.mjs → chunk-O34Z4XM2.mjs} +170 -30
- package/dist/chunk-O34Z4XM2.mjs.map +1 -0
- package/dist/{chunk-5M7ODWKH.mjs → chunk-OZXUB3V7.mjs} +3 -3
- package/dist/chunk-PLHTVHUW.mjs +82 -0
- package/dist/chunk-PLHTVHUW.mjs.map +1 -0
- package/dist/{chunk-62QMTKMJ.mjs → chunk-POWCBXRY.mjs} +3 -3
- package/dist/chunk-PXEVB7IK.mjs +1 -0
- package/dist/{chunk-HIORSNVD.mjs → chunk-Q2NFQTCQ.mjs} +2 -2
- package/dist/chunk-SLTG4L62.mjs +78 -0
- package/dist/chunk-SLTG4L62.mjs.map +1 -0
- package/dist/{chunk-SMJ3QQCE.mjs → chunk-T7N77F5Y.mjs} +2 -2
- package/dist/{chunk-YOEL33HG.mjs → chunk-UFN2VWSR.mjs} +2 -2
- package/dist/{chunk-2OTVZXDX.mjs → chunk-UH2UFL5W.mjs} +3 -3
- package/dist/{chunk-D5XIJNXQ.mjs → chunk-VGPQYMKJ.mjs} +8 -8
- package/dist/{chunk-WNC6OCIB.mjs → chunk-XFCMZH2H.mjs} +2 -2
- package/dist/{chunk-ORSMX3SE.mjs → chunk-XWG3L6QC.mjs} +15 -1
- package/dist/{chunk-ORSMX3SE.mjs.map → chunk-XWG3L6QC.mjs.map} +1 -1
- package/dist/{chunk-TOQ7P4DO.mjs → chunk-XZNY26GH.mjs} +2 -2
- package/dist/{chunk-GOAED4H6.mjs → chunk-Y7UO3RPW.mjs} +10 -10
- package/dist/components/chat/Button.js.map +1 -1
- package/dist/components/chat/Button.mjs +3 -3
- package/dist/components/chat/Chat.d.ts +23 -3
- package/dist/components/chat/Chat.js +341 -30
- package/dist/components/chat/Chat.js.map +1 -1
- package/dist/components/chat/Chat.mjs +16 -14
- package/dist/components/chat/ChatContext.d.ts +5 -0
- package/dist/components/chat/ChatContext.js +15 -1
- package/dist/components/chat/ChatContext.js.map +1 -1
- package/dist/components/chat/ChatContext.mjs +2 -2
- package/dist/components/chat/CodeBlock.js.map +1 -1
- package/dist/components/chat/CodeBlock.mjs +2 -2
- package/dist/components/chat/Header.js.map +1 -1
- package/dist/components/chat/Header.mjs +5 -5
- package/dist/components/chat/Icons.d.ts +2 -1
- package/dist/components/chat/Icons.js +17 -2
- package/dist/components/chat/Icons.js.map +1 -1
- package/dist/components/chat/Icons.mjs +5 -3
- package/dist/components/chat/ImageUploadQueue.d.ts +13 -0
- package/dist/components/chat/ImageUploadQueue.js +106 -0
- package/dist/components/chat/ImageUploadQueue.js.map +1 -0
- package/dist/components/chat/ImageUploadQueue.mjs +8 -0
- package/dist/components/chat/ImageUploadQueue.mjs.map +1 -0
- package/dist/components/chat/Input.d.ts +1 -1
- package/dist/components/chat/Input.js +2 -1
- package/dist/components/chat/Input.js.map +1 -1
- package/dist/components/chat/Input.mjs +3 -3
- package/dist/components/chat/Markdown.js.map +1 -1
- package/dist/components/chat/Markdown.mjs +3 -3
- package/dist/components/chat/Messages.d.ts +1 -1
- package/dist/components/chat/Messages.js +18 -0
- package/dist/components/chat/Messages.js.map +1 -1
- package/dist/components/chat/Messages.mjs +3 -3
- package/dist/components/chat/Modal.js +348 -37
- package/dist/components/chat/Modal.js.map +1 -1
- package/dist/components/chat/Modal.mjs +22 -20
- package/dist/components/chat/Popup.js +350 -39
- package/dist/components/chat/Popup.js.map +1 -1
- package/dist/components/chat/Popup.mjs +23 -21
- package/dist/components/chat/Sidebar.js +350 -39
- package/dist/components/chat/Sidebar.js.map +1 -1
- package/dist/components/chat/Sidebar.mjs +23 -21
- package/dist/components/chat/Suggestion.js.map +1 -1
- package/dist/components/chat/Suggestion.mjs +2 -2
- package/dist/components/chat/Window.js.map +1 -1
- package/dist/components/chat/Window.mjs +3 -3
- package/dist/components/chat/index.d.ts +1 -0
- package/dist/components/chat/index.js +354 -41
- package/dist/components/chat/index.js.map +1 -1
- package/dist/components/chat/index.mjs +30 -25
- package/dist/components/chat/messages/AssistantMessage.js.map +1 -1
- package/dist/components/chat/messages/AssistantMessage.mjs +5 -5
- package/dist/components/chat/messages/RenderActionExecutionMessage.js.map +1 -1
- package/dist/components/chat/messages/RenderActionExecutionMessage.mjs +6 -6
- package/dist/components/chat/messages/RenderAgentStateMessage.js.map +1 -1
- package/dist/components/chat/messages/RenderAgentStateMessage.mjs +6 -6
- package/dist/components/chat/messages/RenderImageMessage.d.ts +7 -0
- package/dist/components/chat/messages/RenderImageMessage.js +774 -0
- package/dist/components/chat/messages/RenderImageMessage.js.map +1 -0
- package/dist/components/chat/messages/RenderImageMessage.mjs +15 -0
- package/dist/components/chat/messages/RenderImageMessage.mjs.map +1 -0
- package/dist/components/chat/messages/RenderResultMessage.js.map +1 -1
- package/dist/components/chat/messages/RenderResultMessage.mjs +6 -6
- package/dist/components/chat/messages/RenderTextMessage.js +1 -1
- package/dist/components/chat/messages/RenderTextMessage.js.map +1 -1
- package/dist/components/chat/messages/RenderTextMessage.mjs +7 -7
- package/dist/components/chat/messages/UserMessage.js +1 -1
- package/dist/components/chat/messages/UserMessage.js.map +1 -1
- package/dist/components/chat/messages/UserMessage.mjs +1 -1
- package/dist/components/chat/props.d.ts +3 -0
- package/dist/components/chat/props.js.map +1 -1
- package/dist/components/dev-console/console.js.map +1 -1
- package/dist/components/dev-console/console.mjs +3 -3
- package/dist/components/dev-console/index.js.map +1 -1
- package/dist/components/dev-console/index.mjs +3 -3
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +354 -41
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +30 -25
- package/dist/index.d.ts +1 -0
- package/dist/index.js +354 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +31 -26
- package/package.json +6 -6
- package/src/components/chat/Chat.tsx +198 -18
- package/src/components/chat/ChatContext.tsx +7 -0
- package/src/components/chat/Icons.tsx +14 -0
- package/src/components/chat/ImageUploadQueue.tsx +77 -0
- package/src/components/chat/Input.tsx +8 -1
- package/src/components/chat/Messages.tsx +17 -0
- package/src/components/chat/index.tsx +1 -0
- package/src/components/chat/messages/RenderImageMessage.tsx +64 -0
- package/src/components/chat/messages/UserMessage.tsx +5 -1
- package/src/components/chat/props.ts +3 -0
- package/dist/chunk-HEIDCT7I.mjs.map +0 -1
- package/dist/chunk-QJKMOGWN.mjs.map +0 -1
- package/dist/chunk-SQMEPWVT.mjs +0 -1
- package/dist/chunk-UN2E3HCK.mjs.map +0 -1
- package/dist/chunk-ZLRUNNS7.mjs.map +0 -1
- package/dist/chunk-ZY25LVYR.mjs.map +0 -1
- /package/dist/{chunk-Q2YY2NX3.mjs.map → chunk-24TDU7MY.mjs.map} +0 -0
- /package/dist/{chunk-XNQO5AZZ.mjs.map → chunk-ABHUX6T6.mjs.map} +0 -0
- /package/dist/{chunk-NMNC4ROZ.mjs.map → chunk-DSQGQJI4.mjs.map} +0 -0
- /package/dist/{chunk-X6EFGEBJ.mjs.map → chunk-JOL7NS2W.mjs.map} +0 -0
- /package/dist/{chunk-PCTCOQK2.mjs.map → chunk-KENCH7RN.mjs.map} +0 -0
- /package/dist/{chunk-5M7ODWKH.mjs.map → chunk-OZXUB3V7.mjs.map} +0 -0
- /package/dist/{chunk-62QMTKMJ.mjs.map → chunk-POWCBXRY.mjs.map} +0 -0
- /package/dist/{chunk-SQMEPWVT.mjs.map → chunk-PXEVB7IK.mjs.map} +0 -0
- /package/dist/{chunk-HIORSNVD.mjs.map → chunk-Q2NFQTCQ.mjs.map} +0 -0
- /package/dist/{chunk-SMJ3QQCE.mjs.map → chunk-T7N77F5Y.mjs.map} +0 -0
- /package/dist/{chunk-YOEL33HG.mjs.map → chunk-UFN2VWSR.mjs.map} +0 -0
- /package/dist/{chunk-2OTVZXDX.mjs.map → chunk-UH2UFL5W.mjs.map} +0 -0
- /package/dist/{chunk-D5XIJNXQ.mjs.map → chunk-VGPQYMKJ.mjs.map} +0 -0
- /package/dist/{chunk-WNC6OCIB.mjs.map → chunk-XFCMZH2H.mjs.map} +0 -0
- /package/dist/{chunk-TOQ7P4DO.mjs.map → chunk-XZNY26GH.mjs.map} +0 -0
- /package/dist/{chunk-GOAED4H6.mjs.map → chunk-Y7UO3RPW.mjs.map} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -1,55 +1,59 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import "./chunk-EFZPSZWO.mjs";
|
|
3
|
+
import "./chunk-IU3WTXLQ.mjs";
|
|
3
4
|
import "./chunk-MMVDU6DF.mjs";
|
|
4
|
-
import "./chunk-
|
|
5
|
+
import "./chunk-PXEVB7IK.mjs";
|
|
6
|
+
import {
|
|
7
|
+
CopilotPopup
|
|
8
|
+
} from "./chunk-T7N77F5Y.mjs";
|
|
5
9
|
import {
|
|
6
10
|
CopilotSidebar
|
|
7
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-JOL7NS2W.mjs";
|
|
8
12
|
import "./chunk-WB3YULQ4.mjs";
|
|
9
|
-
import
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import "./chunk-GOAED4H6.mjs";
|
|
13
|
-
import "./chunk-HIORSNVD.mjs";
|
|
14
|
-
import "./chunk-2OTVZXDX.mjs";
|
|
13
|
+
import "./chunk-Y7UO3RPW.mjs";
|
|
14
|
+
import "./chunk-Q2NFQTCQ.mjs";
|
|
15
|
+
import "./chunk-UH2UFL5W.mjs";
|
|
15
16
|
import "./chunk-V7W6IM2V.mjs";
|
|
16
17
|
import {
|
|
17
18
|
CopilotDevConsole
|
|
18
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-VGPQYMKJ.mjs";
|
|
20
|
+
import "./chunk-Q5V6S67N.mjs";
|
|
19
21
|
import {
|
|
20
22
|
shouldShowDevConsole
|
|
21
23
|
} from "./chunk-6TCUJ3B7.mjs";
|
|
22
24
|
import "./chunk-KXE2JCUH.mjs";
|
|
23
25
|
import "./chunk-NRA3CFEE.mjs";
|
|
24
26
|
import "./chunk-BH6PCAAL.mjs";
|
|
25
|
-
import "./chunk-
|
|
26
|
-
import "./chunk-YOEL33HG.mjs";
|
|
27
|
+
import "./chunk-UFN2VWSR.mjs";
|
|
27
28
|
import {
|
|
28
29
|
CopilotChat
|
|
29
|
-
} from "./chunk-
|
|
30
|
-
import "./chunk-
|
|
31
|
-
import "./chunk-
|
|
32
|
-
import
|
|
30
|
+
} from "./chunk-O34Z4XM2.mjs";
|
|
31
|
+
import "./chunk-DSQGQJI4.mjs";
|
|
32
|
+
import "./chunk-24TDU7MY.mjs";
|
|
33
|
+
import {
|
|
34
|
+
RenderImageMessage
|
|
35
|
+
} from "./chunk-SLTG4L62.mjs";
|
|
36
|
+
import "./chunk-XFCMZH2H.mjs";
|
|
37
|
+
import "./chunk-POWCBXRY.mjs";
|
|
33
38
|
import {
|
|
34
39
|
UserMessage
|
|
35
|
-
} from "./chunk-
|
|
36
|
-
import "./chunk-NMNC4ROZ.mjs";
|
|
40
|
+
} from "./chunk-HWMFMBJC.mjs";
|
|
37
41
|
import {
|
|
38
42
|
AssistantMessage
|
|
39
|
-
} from "./chunk-
|
|
40
|
-
import "./chunk-
|
|
41
|
-
import "./chunk-
|
|
43
|
+
} from "./chunk-OZXUB3V7.mjs";
|
|
44
|
+
import "./chunk-XZNY26GH.mjs";
|
|
45
|
+
import "./chunk-PLHTVHUW.mjs";
|
|
46
|
+
import "./chunk-BDNHZ3GW.mjs";
|
|
42
47
|
import "./chunk-YQFVRDNC.mjs";
|
|
43
48
|
import {
|
|
44
49
|
Markdown
|
|
45
|
-
} from "./chunk-
|
|
46
|
-
import "./chunk-
|
|
47
|
-
import "./chunk-
|
|
50
|
+
} from "./chunk-ABHUX6T6.mjs";
|
|
51
|
+
import "./chunk-KENCH7RN.mjs";
|
|
52
|
+
import "./chunk-IJADIQAR.mjs";
|
|
48
53
|
import {
|
|
49
54
|
useChatContext
|
|
50
|
-
} from "./chunk-
|
|
51
|
-
import "./chunk-
|
|
52
|
-
import "./chunk-IU3WTXLQ.mjs";
|
|
55
|
+
} from "./chunk-IEMQ2SQW.mjs";
|
|
56
|
+
import "./chunk-XWG3L6QC.mjs";
|
|
53
57
|
import "./chunk-T26KLXLH.mjs";
|
|
54
58
|
import {
|
|
55
59
|
useCopilotChatSuggestions
|
|
@@ -65,6 +69,7 @@ export {
|
|
|
65
69
|
CopilotPopup,
|
|
66
70
|
CopilotSidebar,
|
|
67
71
|
Markdown,
|
|
72
|
+
RenderImageMessage,
|
|
68
73
|
UserMessage,
|
|
69
74
|
shouldShowDevConsole,
|
|
70
75
|
useChatContext,
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"publishConfig": {
|
|
10
10
|
"access": "public"
|
|
11
11
|
},
|
|
12
|
-
"version": "1.8.
|
|
12
|
+
"version": "1.8.7-next.0",
|
|
13
13
|
"sideEffects": [
|
|
14
14
|
"**/*.css"
|
|
15
15
|
],
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"ts-jest": "^29.1.1",
|
|
41
41
|
"tsup": "^6.7.0",
|
|
42
42
|
"typescript": "^5.2.3",
|
|
43
|
+
"eslint-config-custom": "1.4.6",
|
|
43
44
|
"tailwind-config": "1.4.6",
|
|
44
|
-
"tsconfig": "1.4.6"
|
|
45
|
-
"eslint-config-custom": "1.4.6"
|
|
45
|
+
"tsconfig": "1.4.6"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
48
|
"@headlessui/react": "^2.1.3",
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
"react-syntax-highlighter": "^15.5.0",
|
|
51
51
|
"remark-gfm": "^3.0.1",
|
|
52
52
|
"remark-math": "^5.1.1",
|
|
53
|
-
"@copilotkit/react-core": "1.8.
|
|
54
|
-
"@copilotkit/runtime-client-gql": "1.8.
|
|
55
|
-
"@copilotkit/shared": "1.8.
|
|
53
|
+
"@copilotkit/react-core": "1.8.7-next.0",
|
|
54
|
+
"@copilotkit/runtime-client-gql": "1.8.7-next.0",
|
|
55
|
+
"@copilotkit/shared": "1.8.7-next.0"
|
|
56
56
|
},
|
|
57
57
|
"keywords": [
|
|
58
58
|
"copilotkit",
|
|
@@ -57,6 +57,7 @@ import { RenderTextMessage as DefaultRenderTextMessage } from "./messages/Render
|
|
|
57
57
|
import { RenderActionExecutionMessage as DefaultRenderActionExecutionMessage } from "./messages/RenderActionExecutionMessage";
|
|
58
58
|
import { RenderResultMessage as DefaultRenderResultMessage } from "./messages/RenderResultMessage";
|
|
59
59
|
import { RenderAgentStateMessage as DefaultRenderAgentStateMessage } from "./messages/RenderAgentStateMessage";
|
|
60
|
+
import { RenderImageMessage as DefaultRenderImageMessage } from "./messages/RenderImageMessage";
|
|
60
61
|
import { AssistantMessage as DefaultAssistantMessage } from "./messages/AssistantMessage";
|
|
61
62
|
import { UserMessage as DefaultUserMessage } from "./messages/UserMessage";
|
|
62
63
|
import { Suggestion } from "./Suggestion";
|
|
@@ -69,7 +70,7 @@ import {
|
|
|
69
70
|
} from "@copilotkit/react-core";
|
|
70
71
|
import { reloadSuggestions } from "./Suggestion";
|
|
71
72
|
import { CopilotChatSuggestion } from "../../types/suggestions";
|
|
72
|
-
import { Message, Role, TextMessage } from "@copilotkit/runtime-client-gql";
|
|
73
|
+
import { Message, Role, TextMessage, ImageMessage } from "@copilotkit/runtime-client-gql";
|
|
73
74
|
import { randomId } from "@copilotkit/shared";
|
|
74
75
|
import {
|
|
75
76
|
AssistantMessageProps,
|
|
@@ -80,6 +81,7 @@ import {
|
|
|
80
81
|
} from "./props";
|
|
81
82
|
|
|
82
83
|
import { HintFunction, runAgent, stopAgent } from "@copilotkit/react-core";
|
|
84
|
+
import { ImageUploadQueue } from "./ImageUploadQueue";
|
|
83
85
|
|
|
84
86
|
/**
|
|
85
87
|
* Props for CopilotChat component.
|
|
@@ -145,6 +147,17 @@ export interface CopilotChatProps {
|
|
|
145
147
|
*/
|
|
146
148
|
labels?: CopilotChatLabels;
|
|
147
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Enable image upload button (image inputs only supported on some models)
|
|
152
|
+
*/
|
|
153
|
+
imageUploadsEnabled?: boolean;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The 'accept' attribute for the file input used for image uploads.
|
|
157
|
+
* Defaults to "image/*".
|
|
158
|
+
*/
|
|
159
|
+
inputFileAccept?: string;
|
|
160
|
+
|
|
148
161
|
/**
|
|
149
162
|
* A function that takes in context string and instructions and returns
|
|
150
163
|
* the system message to include in the chat request.
|
|
@@ -188,6 +201,11 @@ export interface CopilotChatProps {
|
|
|
188
201
|
*/
|
|
189
202
|
RenderResultMessage?: React.ComponentType<RenderMessageProps>;
|
|
190
203
|
|
|
204
|
+
/**
|
|
205
|
+
* A custom RenderImageMessage component to use instead of the default.
|
|
206
|
+
*/
|
|
207
|
+
RenderImageMessage?: React.ComponentType<RenderMessageProps>;
|
|
208
|
+
|
|
191
209
|
/**
|
|
192
210
|
* A custom Input component to use instead of the default.
|
|
193
211
|
*/
|
|
@@ -257,6 +275,11 @@ export type OnStopGeneration = (args: OnStopGenerationArguments) => void;
|
|
|
257
275
|
|
|
258
276
|
export type OnReloadMessages = (args: OnReloadMessagesArguments) => void;
|
|
259
277
|
|
|
278
|
+
export type ImageUpload = {
|
|
279
|
+
contentType: string;
|
|
280
|
+
bytes: string;
|
|
281
|
+
};
|
|
282
|
+
|
|
260
283
|
export function CopilotChat({
|
|
261
284
|
instructions,
|
|
262
285
|
onSubmitMessage,
|
|
@@ -273,14 +296,69 @@ export function CopilotChat({
|
|
|
273
296
|
RenderActionExecutionMessage = DefaultRenderActionExecutionMessage,
|
|
274
297
|
RenderAgentStateMessage = DefaultRenderAgentStateMessage,
|
|
275
298
|
RenderResultMessage = DefaultRenderResultMessage,
|
|
299
|
+
RenderImageMessage = DefaultRenderImageMessage,
|
|
276
300
|
Input = DefaultInput,
|
|
277
301
|
className,
|
|
278
302
|
icons,
|
|
279
303
|
labels,
|
|
280
304
|
AssistantMessage = DefaultAssistantMessage,
|
|
281
305
|
UserMessage = DefaultUserMessage,
|
|
306
|
+
imageUploadsEnabled,
|
|
307
|
+
inputFileAccept = "image/*",
|
|
282
308
|
}: CopilotChatProps) {
|
|
283
309
|
const { additionalInstructions, setChatInstructions } = useCopilotContext();
|
|
310
|
+
const [selectedImages, setSelectedImages] = useState<Array<ImageUpload>>([]);
|
|
311
|
+
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
312
|
+
|
|
313
|
+
// Clipboard paste handler
|
|
314
|
+
useEffect(() => {
|
|
315
|
+
if (!imageUploadsEnabled) return;
|
|
316
|
+
|
|
317
|
+
const handlePaste = async (e: ClipboardEvent) => {
|
|
318
|
+
const target = e.target as HTMLElement;
|
|
319
|
+
if (!target.parentElement?.classList.contains("copilotKitInput")) return;
|
|
320
|
+
|
|
321
|
+
const items = Array.from(e.clipboardData?.items || []);
|
|
322
|
+
const imageItems = items.filter((item) => item.type.startsWith("image/"));
|
|
323
|
+
|
|
324
|
+
if (imageItems.length === 0) return;
|
|
325
|
+
|
|
326
|
+
e.preventDefault(); // Prevent default paste behavior for images
|
|
327
|
+
|
|
328
|
+
const imagePromises: Promise<ImageUpload | null>[] = imageItems.map((item) => {
|
|
329
|
+
const file = item.getAsFile();
|
|
330
|
+
if (!file) return Promise.resolve(null);
|
|
331
|
+
|
|
332
|
+
return new Promise<ImageUpload | null>((resolve, reject) => {
|
|
333
|
+
const reader = new FileReader();
|
|
334
|
+
reader.onload = (e) => {
|
|
335
|
+
const base64String = (e.target?.result as string)?.split(",")[1];
|
|
336
|
+
if (base64String) {
|
|
337
|
+
resolve({
|
|
338
|
+
contentType: file.type,
|
|
339
|
+
bytes: base64String,
|
|
340
|
+
});
|
|
341
|
+
} else {
|
|
342
|
+
resolve(null);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
reader.onerror = reject;
|
|
346
|
+
reader.readAsDataURL(file);
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
try {
|
|
351
|
+
const loadedImages = (await Promise.all(imagePromises)).filter((img) => img !== null);
|
|
352
|
+
setSelectedImages((prev) => [...prev, ...loadedImages]);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
// TODO: Show an error message to the user
|
|
355
|
+
console.error("Error processing pasted images:", error);
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
document.addEventListener("paste", handlePaste);
|
|
360
|
+
return () => document.removeEventListener("paste", handlePaste);
|
|
361
|
+
}, [imageUploadsEnabled]);
|
|
284
362
|
|
|
285
363
|
useEffect(() => {
|
|
286
364
|
if (!additionalInstructions?.length) {
|
|
@@ -322,6 +400,17 @@ export function CopilotChat({
|
|
|
322
400
|
onReloadMessages,
|
|
323
401
|
);
|
|
324
402
|
|
|
403
|
+
// Wrapper for sendMessage to clear selected images
|
|
404
|
+
const handleSendMessage = (text: string) => {
|
|
405
|
+
const images = selectedImages;
|
|
406
|
+
setSelectedImages([]);
|
|
407
|
+
if (fileInputRef.current) {
|
|
408
|
+
fileInputRef.current.value = "";
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return sendMessage(text, images);
|
|
412
|
+
};
|
|
413
|
+
|
|
325
414
|
const chatContext = React.useContext(ChatContext);
|
|
326
415
|
const isVisible = chatContext ? chatContext.open : true;
|
|
327
416
|
|
|
@@ -339,6 +428,44 @@ export function CopilotChat({
|
|
|
339
428
|
}
|
|
340
429
|
};
|
|
341
430
|
|
|
431
|
+
const handleImageUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
432
|
+
if (!event.target.files || event.target.files.length === 0) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const files = Array.from(event.target.files).filter((file) => file.type.startsWith("image/"));
|
|
437
|
+
if (files.length === 0) return;
|
|
438
|
+
|
|
439
|
+
const fileReadPromises = files.map((file) => {
|
|
440
|
+
return new Promise<{ contentType: string; bytes: string }>((resolve, reject) => {
|
|
441
|
+
const reader = new FileReader();
|
|
442
|
+
reader.onload = (e) => {
|
|
443
|
+
const base64String = (e.target?.result as string)?.split(",")[1] || "";
|
|
444
|
+
if (base64String) {
|
|
445
|
+
resolve({
|
|
446
|
+
contentType: file.type,
|
|
447
|
+
bytes: base64String,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
reader.onerror = reject;
|
|
452
|
+
reader.readAsDataURL(file);
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
try {
|
|
457
|
+
const loadedImages = await Promise.all(fileReadPromises);
|
|
458
|
+
setSelectedImages((prev) => [...prev, ...loadedImages]);
|
|
459
|
+
} catch (error) {
|
|
460
|
+
// TODO: Show an error message to the user
|
|
461
|
+
console.error("Error reading files:", error);
|
|
462
|
+
}
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
const removeSelectedImage = (index: number) => {
|
|
466
|
+
setSelectedImages((prev) => prev.filter((_, i) => i !== index));
|
|
467
|
+
};
|
|
468
|
+
|
|
342
469
|
return (
|
|
343
470
|
<WrappedCopilotChat icons={icons} labels={labels} className={className}>
|
|
344
471
|
<Messages
|
|
@@ -348,6 +475,7 @@ export function CopilotChat({
|
|
|
348
475
|
RenderActionExecutionMessage={RenderActionExecutionMessage}
|
|
349
476
|
RenderAgentStateMessage={RenderAgentStateMessage}
|
|
350
477
|
RenderResultMessage={RenderResultMessage}
|
|
478
|
+
RenderImageMessage={RenderImageMessage}
|
|
351
479
|
messages={visibleMessages}
|
|
352
480
|
inProgress={isLoading}
|
|
353
481
|
onRegenerate={handleRegenerate}
|
|
@@ -364,17 +492,33 @@ export function CopilotChat({
|
|
|
364
492
|
message={suggestion.message}
|
|
365
493
|
partial={suggestion.partial}
|
|
366
494
|
className={suggestion.className}
|
|
367
|
-
onClick={(message) =>
|
|
495
|
+
onClick={(message) => handleSendMessage(message)}
|
|
368
496
|
/>
|
|
369
497
|
))}
|
|
370
498
|
</div>
|
|
371
499
|
)}
|
|
372
500
|
</Messages>
|
|
501
|
+
|
|
502
|
+
{imageUploadsEnabled && (
|
|
503
|
+
<>
|
|
504
|
+
<ImageUploadQueue images={selectedImages} onRemoveImage={removeSelectedImage} />
|
|
505
|
+
<input
|
|
506
|
+
type="file"
|
|
507
|
+
multiple
|
|
508
|
+
ref={fileInputRef}
|
|
509
|
+
onChange={handleImageUpload}
|
|
510
|
+
accept={inputFileAccept}
|
|
511
|
+
style={{ display: "none" }}
|
|
512
|
+
/>
|
|
513
|
+
</>
|
|
514
|
+
)}
|
|
515
|
+
|
|
373
516
|
<Input
|
|
374
517
|
inProgress={isLoading}
|
|
375
|
-
onSend={
|
|
518
|
+
onSend={handleSendMessage}
|
|
376
519
|
isVisible={isVisible}
|
|
377
520
|
onStop={stopGeneration}
|
|
521
|
+
onUpload={imageUploadsEnabled ? () => fileInputRef.current?.click() : undefined}
|
|
378
522
|
/>
|
|
379
523
|
</WrappedCopilotChat>
|
|
380
524
|
);
|
|
@@ -467,28 +611,64 @@ export const useCopilotChatLogic = (
|
|
|
467
611
|
visibleMessages.length == 0,
|
|
468
612
|
]);
|
|
469
613
|
|
|
470
|
-
const sendMessage = async (
|
|
614
|
+
const sendMessage = async (
|
|
615
|
+
messageContent: string,
|
|
616
|
+
imagesToUse?: Array<{ contentType: string; bytes: string }>,
|
|
617
|
+
) => {
|
|
618
|
+
// Use images passed in the call OR the ones from the state (passed via props)
|
|
619
|
+
const images = imagesToUse || [];
|
|
620
|
+
|
|
471
621
|
abortSuggestions();
|
|
472
622
|
setCurrentSuggestions([]);
|
|
473
623
|
|
|
474
|
-
|
|
475
|
-
content: messageContent,
|
|
476
|
-
role: Role.User,
|
|
477
|
-
});
|
|
624
|
+
let firstMessage: Message | null = null;
|
|
478
625
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
626
|
+
// If there's text content, send a text message first
|
|
627
|
+
if (messageContent.trim().length > 0) {
|
|
628
|
+
const textMessage = new TextMessage({
|
|
629
|
+
content: messageContent,
|
|
630
|
+
role: Role.User,
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
if (onSubmitMessage) {
|
|
634
|
+
try {
|
|
635
|
+
// Call onSubmitMessage only with text, as image handling is internal right now
|
|
636
|
+
await onSubmitMessage(messageContent);
|
|
637
|
+
} catch (error) {
|
|
638
|
+
console.error("Error in onSubmitMessage:", error);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
await appendMessage(textMessage, { followUp: images.length === 0 });
|
|
643
|
+
|
|
644
|
+
if (!firstMessage) {
|
|
645
|
+
firstMessage = textMessage;
|
|
484
646
|
}
|
|
485
647
|
}
|
|
486
|
-
// this needs to happen after onSubmitMessage, because it will trigger submission
|
|
487
|
-
// of the message to the endpoint. Some users depend on performing some actions
|
|
488
|
-
// before the message is submitted.
|
|
489
|
-
appendMessage(message);
|
|
490
648
|
|
|
491
|
-
|
|
649
|
+
// Send image messages
|
|
650
|
+
if (images.length > 0) {
|
|
651
|
+
for (let i = 0; i < images.length; i++) {
|
|
652
|
+
const imageMessage = new ImageMessage({
|
|
653
|
+
format: images[i].contentType.replace("image/", ""),
|
|
654
|
+
bytes: images[i].bytes,
|
|
655
|
+
role: Role.User,
|
|
656
|
+
});
|
|
657
|
+
await appendMessage(imageMessage, { followUp: i === images.length - 1 });
|
|
658
|
+
if (!firstMessage) {
|
|
659
|
+
firstMessage = imageMessage;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (!firstMessage) {
|
|
665
|
+
// Should not happen if send button is properly disabled, but handle just in case
|
|
666
|
+
return new TextMessage({ content: "", role: Role.User }); // Return a dummy message
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// The hook implicitly triggers API call on appendMessage.
|
|
670
|
+
// We return the first message sent (either text or first image)
|
|
671
|
+
return firstMessage;
|
|
492
672
|
};
|
|
493
673
|
|
|
494
674
|
const messages = visibleMessages;
|
|
@@ -81,6 +81,12 @@ export interface CopilotChatIcons {
|
|
|
81
81
|
*/
|
|
82
82
|
|
|
83
83
|
thumbsDownIcon?: React.ReactNode;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The icon to use for the upload button.
|
|
87
|
+
* @default <UploadIcon />
|
|
88
|
+
*/
|
|
89
|
+
uploadIcon?: React.ReactNode;
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
/**
|
|
@@ -221,6 +227,7 @@ export const ChatContextProvider = ({
|
|
|
221
227
|
copyIcon: DefaultIcons.CopyIcon,
|
|
222
228
|
thumbsUpIcon: DefaultIcons.ThumbsUpIcon,
|
|
223
229
|
thumbsDownIcon: DefaultIcons.ThumbsDownIcon,
|
|
230
|
+
uploadIcon: DefaultIcons.UploadIcon,
|
|
224
231
|
},
|
|
225
232
|
...icons,
|
|
226
233
|
}),
|
|
@@ -207,6 +207,20 @@ export const DownloadIcon = (
|
|
|
207
207
|
</svg>
|
|
208
208
|
);
|
|
209
209
|
|
|
210
|
+
export const UploadIcon = (
|
|
211
|
+
<svg
|
|
212
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
213
|
+
fill="none"
|
|
214
|
+
viewBox="0 0 24 24"
|
|
215
|
+
strokeWidth="1.5"
|
|
216
|
+
stroke="currentColor"
|
|
217
|
+
width="24"
|
|
218
|
+
height="24"
|
|
219
|
+
>
|
|
220
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
|
221
|
+
</svg>
|
|
222
|
+
);
|
|
223
|
+
|
|
210
224
|
export const CheckIcon = (
|
|
211
225
|
<svg
|
|
212
226
|
xmlns="http://www.w3.org/2000/svg"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
interface ImageUploadQueueProps {
|
|
4
|
+
images: Array<{ contentType: string; bytes: string }>;
|
|
5
|
+
onRemoveImage: (index: number) => void;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const ImageUploadQueue: React.FC<ImageUploadQueueProps> = ({
|
|
10
|
+
images,
|
|
11
|
+
onRemoveImage,
|
|
12
|
+
className = "",
|
|
13
|
+
}) => {
|
|
14
|
+
if (images.length === 0) return null;
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<div
|
|
18
|
+
className={`copilotKitImageUploadQueue ${className}`}
|
|
19
|
+
style={{
|
|
20
|
+
display: "flex",
|
|
21
|
+
flexWrap: "wrap",
|
|
22
|
+
gap: "8px",
|
|
23
|
+
margin: "8px",
|
|
24
|
+
padding: "8px",
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
{images.map((image, index) => (
|
|
28
|
+
<div
|
|
29
|
+
key={index}
|
|
30
|
+
className="copilotKitImageUploadQueueItem"
|
|
31
|
+
style={{
|
|
32
|
+
position: "relative",
|
|
33
|
+
display: "inline-block",
|
|
34
|
+
width: "60px",
|
|
35
|
+
height: "60px",
|
|
36
|
+
borderRadius: "4px",
|
|
37
|
+
overflow: "hidden",
|
|
38
|
+
}}
|
|
39
|
+
>
|
|
40
|
+
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
41
|
+
<img
|
|
42
|
+
src={`data:${image.contentType};base64,${image.bytes}`}
|
|
43
|
+
alt={`Selected image ${index + 1}`}
|
|
44
|
+
style={{
|
|
45
|
+
width: "100%",
|
|
46
|
+
height: "100%",
|
|
47
|
+
objectFit: "cover",
|
|
48
|
+
}}
|
|
49
|
+
/>
|
|
50
|
+
<button
|
|
51
|
+
onClick={() => onRemoveImage(index)}
|
|
52
|
+
className="copilotKitImageUploadQueueRemoveButton"
|
|
53
|
+
style={{
|
|
54
|
+
position: "absolute",
|
|
55
|
+
top: "2px",
|
|
56
|
+
right: "2px",
|
|
57
|
+
background: "rgba(0,0,0,0.6)",
|
|
58
|
+
color: "white",
|
|
59
|
+
border: "none",
|
|
60
|
+
borderRadius: "50%",
|
|
61
|
+
width: "18px",
|
|
62
|
+
height: "18px",
|
|
63
|
+
display: "flex",
|
|
64
|
+
alignItems: "center",
|
|
65
|
+
justifyContent: "center",
|
|
66
|
+
cursor: "pointer",
|
|
67
|
+
fontSize: "10px",
|
|
68
|
+
padding: 0,
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
✕
|
|
72
|
+
</button>
|
|
73
|
+
</div>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
@@ -5,7 +5,7 @@ import AutoResizingTextarea from "./Textarea";
|
|
|
5
5
|
import { usePushToTalk } from "../../hooks/use-push-to-talk";
|
|
6
6
|
import { useCopilotContext } from "@copilotkit/react-core";
|
|
7
7
|
|
|
8
|
-
export const Input = ({ inProgress, onSend, isVisible = false, onStop }: InputProps) => {
|
|
8
|
+
export const Input = ({ inProgress, onSend, isVisible = false, onStop, onUpload }: InputProps) => {
|
|
9
9
|
const context = useChatContext();
|
|
10
10
|
const copilotContext = useCopilotContext();
|
|
11
11
|
|
|
@@ -89,7 +89,14 @@ export const Input = ({ inProgress, onSend, isVisible = false, onStop }: InputPr
|
|
|
89
89
|
}}
|
|
90
90
|
/>
|
|
91
91
|
<div className="copilotKitInputControls">
|
|
92
|
+
{onUpload && (
|
|
93
|
+
<button onClick={onUpload} className="copilotKitInputControlButton">
|
|
94
|
+
{context.icons.uploadIcon}
|
|
95
|
+
</button>
|
|
96
|
+
)}
|
|
97
|
+
|
|
92
98
|
<div style={{ flexGrow: 1 }} />
|
|
99
|
+
|
|
93
100
|
{showPushToTalk && (
|
|
94
101
|
<button
|
|
95
102
|
onClick={() =>
|
|
@@ -12,6 +12,7 @@ export const Messages = ({
|
|
|
12
12
|
RenderActionExecutionMessage,
|
|
13
13
|
RenderAgentStateMessage,
|
|
14
14
|
RenderResultMessage,
|
|
15
|
+
RenderImageMessage,
|
|
15
16
|
AssistantMessage,
|
|
16
17
|
UserMessage,
|
|
17
18
|
onRegenerate,
|
|
@@ -105,6 +106,22 @@ export const Messages = ({
|
|
|
105
106
|
UserMessage={UserMessage}
|
|
106
107
|
/>
|
|
107
108
|
);
|
|
109
|
+
} else if (message.isImageMessage && message.isImageMessage()) {
|
|
110
|
+
return (
|
|
111
|
+
<RenderImageMessage
|
|
112
|
+
key={index}
|
|
113
|
+
message={message}
|
|
114
|
+
inProgress={inProgress}
|
|
115
|
+
index={index}
|
|
116
|
+
isCurrentMessage={isCurrentMessage}
|
|
117
|
+
AssistantMessage={AssistantMessage}
|
|
118
|
+
UserMessage={UserMessage}
|
|
119
|
+
onRegenerate={onRegenerate}
|
|
120
|
+
onCopy={onCopy}
|
|
121
|
+
onThumbsUp={onThumbsUp}
|
|
122
|
+
onThumbsDown={onThumbsDown}
|
|
123
|
+
/>
|
|
124
|
+
);
|
|
108
125
|
}
|
|
109
126
|
})}
|
|
110
127
|
{interrupt}
|
|
@@ -6,3 +6,4 @@ export { Markdown } from "./Markdown";
|
|
|
6
6
|
export { AssistantMessage } from "./messages/AssistantMessage";
|
|
7
7
|
export { UserMessage } from "./messages/UserMessage";
|
|
8
8
|
export { useChatContext } from "./ChatContext";
|
|
9
|
+
export { RenderImageMessage } from "./messages/RenderImageMessage";
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { RenderMessageProps } from "../props";
|
|
2
|
+
import { UserMessage as DefaultUserMessage } from "./UserMessage";
|
|
3
|
+
import { AssistantMessage as DefaultAssistantMessage } from "./AssistantMessage";
|
|
4
|
+
|
|
5
|
+
export function RenderImageMessage({
|
|
6
|
+
UserMessage = DefaultUserMessage,
|
|
7
|
+
AssistantMessage = DefaultAssistantMessage,
|
|
8
|
+
...props
|
|
9
|
+
}: RenderMessageProps) {
|
|
10
|
+
const {
|
|
11
|
+
message,
|
|
12
|
+
inProgress,
|
|
13
|
+
index,
|
|
14
|
+
isCurrentMessage,
|
|
15
|
+
onRegenerate,
|
|
16
|
+
onCopy,
|
|
17
|
+
onThumbsUp,
|
|
18
|
+
onThumbsDown,
|
|
19
|
+
} = props;
|
|
20
|
+
|
|
21
|
+
if (message.isImageMessage()) {
|
|
22
|
+
const imageData = `data:${message.format};base64,${message.bytes}`;
|
|
23
|
+
const imageComponent = (
|
|
24
|
+
<div className="copilotKitImage">
|
|
25
|
+
<img
|
|
26
|
+
src={imageData}
|
|
27
|
+
alt="User uploaded image"
|
|
28
|
+
style={{ maxWidth: "100%", maxHeight: "300px", borderRadius: "8px" }}
|
|
29
|
+
/>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
if (message.role === "user") {
|
|
34
|
+
return (
|
|
35
|
+
<UserMessage
|
|
36
|
+
key={index}
|
|
37
|
+
data-message-role="user"
|
|
38
|
+
message=""
|
|
39
|
+
rawData={message}
|
|
40
|
+
subComponent={imageComponent}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
} else if (message.role === "assistant") {
|
|
44
|
+
return (
|
|
45
|
+
<AssistantMessage
|
|
46
|
+
key={index}
|
|
47
|
+
data-message-role="assistant"
|
|
48
|
+
message=""
|
|
49
|
+
rawData={message}
|
|
50
|
+
subComponent={imageComponent}
|
|
51
|
+
isLoading={inProgress && isCurrentMessage && !message.bytes}
|
|
52
|
+
isGenerating={inProgress && isCurrentMessage && !!message.bytes}
|
|
53
|
+
isCurrentMessage={isCurrentMessage}
|
|
54
|
+
onRegenerate={() => onRegenerate?.(message.id)}
|
|
55
|
+
onCopy={onCopy}
|
|
56
|
+
onThumbsUp={onThumbsUp}
|
|
57
|
+
onThumbsDown={onThumbsDown}
|
|
58
|
+
/>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return null;
|
|
64
|
+
}
|