@anker-in/campaign-ui 0.0.33-alpha2 → 0.0.33-alpha4
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/cjs/components/chat/Markdown.d.ts +5 -0
- package/dist/cjs/components/chat/Messages.d.ts +3 -0
- package/dist/cjs/components/chat/Response.d.ts +2 -0
- package/dist/cjs/components/chat/action.d.ts +8 -2
- package/dist/cjs/components/chat/action.js +1 -1
- package/dist/cjs/components/chat/action.js.map +3 -3
- package/dist/cjs/components/chat/button.d.ts +3 -0
- package/dist/cjs/components/chat/button.js +2 -0
- package/dist/cjs/components/chat/button.js.map +7 -0
- package/dist/cjs/components/chat/index.d.ts +28 -4
- package/dist/cjs/components/chat/index.js +1 -5
- package/dist/cjs/components/chat/index.js.map +3 -3
- package/dist/cjs/components/chat/markdown.js +2 -0
- package/dist/cjs/components/chat/markdown.js.map +7 -0
- package/dist/cjs/components/chat/marksdown.d.ts +5 -0
- package/dist/cjs/components/chat/message.d.ts +2 -0
- package/dist/cjs/components/chat/messages.js +2 -0
- package/dist/cjs/components/chat/messages.js.map +7 -0
- package/dist/cjs/components/chat/props.d.ts +43 -0
- package/dist/cjs/components/chat/props.js +2 -0
- package/dist/cjs/components/chat/props.js.map +7 -0
- package/dist/cjs/components/chat/response copy.d.ts +2 -0
- package/dist/cjs/components/chat/response.js +2 -0
- package/dist/cjs/components/chat/response.js.map +7 -0
- package/dist/cjs/components/chat/rresponse.d.ts +2 -0
- package/dist/cjs/components/chat/suggestions.d.ts +3 -0
- package/dist/cjs/components/chat/suggestions.js +2 -0
- package/dist/cjs/components/chat/suggestions.js.map +7 -0
- package/dist/cjs/stories/chat.stories.d.ts +1 -0
- package/dist/cjs/stories/chat.stories.js +1 -1
- package/dist/cjs/stories/chat.stories.js.map +3 -3
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/components/chat/Markdown.d.ts +5 -0
- package/dist/esm/components/chat/Messages.d.ts +3 -0
- package/dist/esm/components/chat/Response.d.ts +2 -0
- package/dist/esm/components/chat/action.d.ts +8 -2
- package/dist/esm/components/chat/action.js +1 -1
- package/dist/esm/components/chat/action.js.map +3 -3
- package/dist/esm/components/chat/button.d.ts +3 -0
- package/dist/esm/components/chat/button.js +2 -0
- package/dist/esm/components/chat/button.js.map +7 -0
- package/dist/esm/components/chat/index.d.ts +28 -4
- package/dist/esm/components/chat/index.js +1 -5
- package/dist/esm/components/chat/index.js.map +3 -3
- package/dist/esm/components/chat/markdown.js +2 -0
- package/dist/esm/components/chat/markdown.js.map +7 -0
- package/dist/esm/components/chat/marksdown.d.ts +5 -0
- package/dist/esm/components/chat/message.d.ts +2 -0
- package/dist/esm/components/chat/messages.js +2 -0
- package/dist/esm/components/chat/messages.js.map +7 -0
- package/dist/esm/components/chat/props.d.ts +43 -0
- package/dist/esm/components/chat/props.js +2 -0
- package/dist/esm/components/chat/props.js.map +7 -0
- package/dist/esm/components/chat/response copy.d.ts +2 -0
- package/dist/esm/components/chat/response.js +2 -0
- package/dist/esm/components/chat/response.js.map +7 -0
- package/dist/esm/components/chat/rresponse.d.ts +2 -0
- package/dist/esm/components/chat/suggestions.d.ts +3 -0
- package/dist/esm/components/chat/suggestions.js +2 -0
- package/dist/esm/components/chat/suggestions.js.map +7 -0
- package/dist/esm/stories/chat.stories.d.ts +1 -0
- package/dist/esm/stories/chat.stories.js +1 -1
- package/dist/esm/stories/chat.stories.js.map +2 -2
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/components/chat/action.tsx +58 -14
- package/src/components/chat/button.tsx +23 -0
- package/src/components/chat/index.tsx +93 -69
- package/src/components/chat/markdown.tsx +36 -0
- package/src/components/chat/messages.tsx +208 -0
- package/src/components/chat/props.ts +51 -0
- package/src/components/chat/response.tsx +19 -0
- package/src/components/chat/suggestions.tsx +34 -0
- package/src/stories/chat.stories.tsx +5 -6
- package/src/styles/css/messages.css +12 -0
- package/src/styles/css/response.css +0 -1
- package/src/styles/css/suggestions.css +1 -0
- package/dist/cjs/components/chat/chatContext.js +0 -2
- package/dist/cjs/components/chat/chatContext.js.map +0 -7
- package/dist/cjs/components/theme.js +0 -2
- package/dist/cjs/components/theme.js.map +0 -7
- package/dist/esm/components/chat/chatContext.js +0 -2
- package/dist/esm/components/chat/chatContext.js.map +0 -7
- package/dist/esm/components/theme.js +0 -2
- package/dist/esm/components/theme.js.map +0 -7
- package/src/components/chat/chatContext.tsx +0 -161
|
@@ -1,29 +1,50 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { useEffect } from 'react'
|
|
3
3
|
import { useCopilotChat, useCopilotAction, useCopilotReadable } from '@copilotkit/react-core'
|
|
4
|
-
import {
|
|
5
|
-
import { Role, TextMessage } from '@copilotkit/runtime-client-gql'
|
|
4
|
+
import { Role, ActionExecutionMessage, TextMessage } from '@copilotkit/runtime-client-gql'
|
|
6
5
|
|
|
7
6
|
interface Message {
|
|
8
7
|
content: string
|
|
9
8
|
role: string
|
|
9
|
+
name?: string
|
|
10
|
+
scope?: any
|
|
11
|
+
arguments?: any
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
export interface ActionProps {
|
|
13
15
|
start?: string
|
|
14
16
|
history: Message[]
|
|
15
|
-
|
|
17
|
+
gotocheckoutRender?: string | ((_props: any) => React.ReactElement)
|
|
18
|
+
gotocartRender?: string | ((_props: any) => React.ReactElement)
|
|
19
|
+
addtocartRender?: string | ((_props: any) => React.ReactElement)
|
|
20
|
+
productRender?: string | ((_props: any) => React.ReactElement)
|
|
16
21
|
signupRender?: string | ((_props: any) => React.ReactElement)
|
|
17
22
|
children?: React.ReactNode
|
|
18
23
|
}
|
|
19
24
|
|
|
20
|
-
export const CopilotAction = ({
|
|
25
|
+
export const CopilotAction = ({
|
|
26
|
+
start,
|
|
27
|
+
history,
|
|
28
|
+
gotocheckoutRender,
|
|
29
|
+
gotocartRender,
|
|
30
|
+
addtocartRender,
|
|
31
|
+
productRender,
|
|
32
|
+
signupRender,
|
|
33
|
+
children,
|
|
34
|
+
}: ActionProps) => {
|
|
21
35
|
const { setMessages } = useCopilotChat()
|
|
22
36
|
|
|
23
37
|
useEffect(() => {
|
|
24
|
-
const originhistory = history?.filter(value => value?.content)
|
|
38
|
+
const originhistory = history?.filter(value => value?.content || value?.arguments)
|
|
25
39
|
if (originhistory?.length > 0) {
|
|
26
40
|
const content = originhistory?.map(value => {
|
|
41
|
+
if (value?.name && value?.arguments) {
|
|
42
|
+
return new ActionExecutionMessage({
|
|
43
|
+
name: value?.name,
|
|
44
|
+
arguments: value?.arguments,
|
|
45
|
+
scope: value?.scope,
|
|
46
|
+
})
|
|
47
|
+
}
|
|
27
48
|
return new TextMessage({
|
|
28
49
|
...value,
|
|
29
50
|
role: value?.role === 'user' ? Role.User : Role.Assistant,
|
|
@@ -33,18 +54,41 @@ export const CopilotAction = ({ history, buynowRender, signupRender, children }:
|
|
|
33
54
|
}
|
|
34
55
|
}, [history])
|
|
35
56
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
57
|
+
useCopilotAction({
|
|
58
|
+
name: 'go_to_checkout',
|
|
59
|
+
description: 'go to cart',
|
|
60
|
+
parameters: [{ sku: '', handle: '' }] as any,
|
|
61
|
+
render: gotocheckoutRender || (props => <div className="hidden">{JSON.stringify(props)}</div>),
|
|
62
|
+
handler: function () {
|
|
63
|
+
// console.log('buynow-props', props)
|
|
39
64
|
},
|
|
40
|
-
|
|
41
|
-
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
useCopilotAction({
|
|
68
|
+
name: 'go_to_cart',
|
|
69
|
+
description: 'go to cart',
|
|
70
|
+
parameters: [{ sku: '', handle: '' }] as any,
|
|
71
|
+
render: gotocartRender || (props => <div className="hidden">{JSON.stringify(props)}</div>),
|
|
72
|
+
handler: function () {
|
|
73
|
+
// console.log('buynow-props', props)
|
|
74
|
+
},
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
useCopilotAction({
|
|
78
|
+
name: 'add_to_cart',
|
|
79
|
+
description: 'add to cart',
|
|
80
|
+
parameters: [{ sku: '', handle: '' }] as any,
|
|
81
|
+
render: addtocartRender || (props => <div className="hidden">{JSON.stringify(props)}</div>),
|
|
82
|
+
handler: function () {
|
|
83
|
+
// console.log('buynow-props', props)
|
|
84
|
+
},
|
|
85
|
+
})
|
|
42
86
|
|
|
43
87
|
useCopilotAction({
|
|
44
|
-
name: '
|
|
45
|
-
description: '
|
|
88
|
+
name: 'show_product_card',
|
|
89
|
+
description: 'product card',
|
|
46
90
|
parameters: [{ sku: '', handle: '' }] as any,
|
|
47
|
-
render:
|
|
91
|
+
render: productRender || (props => <div className="hidden">{JSON.stringify(props)}</div>),
|
|
48
92
|
handler: function () {
|
|
49
93
|
// console.log('buynow-props', props)
|
|
50
94
|
},
|
|
@@ -58,7 +102,7 @@ export const CopilotAction = ({ history, buynowRender, signupRender, children }:
|
|
|
58
102
|
name: 'signup',
|
|
59
103
|
},
|
|
60
104
|
] as any,
|
|
61
|
-
render: signupRender || (props => <div>{JSON.stringify(props)}</div>),
|
|
105
|
+
render: signupRender || (props => <div className="hidden">{JSON.stringify(props)}</div>),
|
|
62
106
|
handler: function () {
|
|
63
107
|
// console.log('signup-props', props)
|
|
64
108
|
},
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useChatContext } from '@copilotkit/react-ui'
|
|
2
|
+
|
|
3
|
+
import type { ButtonProps } from './props.js'
|
|
4
|
+
|
|
5
|
+
const DefaultButton = ({ setOpen, setOpenTip }: ButtonProps) => {
|
|
6
|
+
const context = useChatContext()
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<button
|
|
10
|
+
onClick={() => {
|
|
11
|
+
setOpen(true)
|
|
12
|
+
setOpenTip(false)
|
|
13
|
+
}}
|
|
14
|
+
className="copilotKitButton "
|
|
15
|
+
aria-label="Open Chat"
|
|
16
|
+
>
|
|
17
|
+
<div className="copilotKitButtonIcon copilotKitButtonIconOpen">{context.icons.openIcon}</div>
|
|
18
|
+
<div className="copilotKitButtonIcon copilotKitButtonIconClose">{context.icons.closeIcon}</div>
|
|
19
|
+
</button>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default DefaultButton
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/exhaustive-deps */
|
|
2
|
+
import React from 'react'
|
|
1
3
|
import { useCallback, useState, useEffect } from 'react'
|
|
2
4
|
import { CopilotKit } from '@copilotkit/react-core'
|
|
3
5
|
import { CopilotPopup } from '@copilotkit/react-ui'
|
|
4
|
-
|
|
6
|
+
import DefaultButton from './button.js'
|
|
7
|
+
import Messages from './messages.js'
|
|
8
|
+
import ResponseButton from './response.js'
|
|
9
|
+
import Suggestions from './suggestions.js'
|
|
5
10
|
import { CopilotAction } from './action.js'
|
|
6
11
|
import fetcher from '../../helpers/fetcher.js'
|
|
7
12
|
|
|
8
|
-
// import '../../styles/chat.css'
|
|
9
|
-
|
|
10
13
|
export interface ChatProps {
|
|
11
14
|
title: string
|
|
12
15
|
runtimeUrl: string
|
|
@@ -14,20 +17,43 @@ export interface ChatProps {
|
|
|
14
17
|
/** GA 的 client id
|
|
15
18
|
*/
|
|
16
19
|
user_id: string
|
|
20
|
+
/** 是否登陆用户 0 or 1
|
|
21
|
+
*/
|
|
22
|
+
account?: string
|
|
23
|
+
/** 用户浏览器语言
|
|
24
|
+
*/
|
|
25
|
+
locale?: string
|
|
17
26
|
/** ?a=1&b=2
|
|
18
27
|
*/
|
|
19
28
|
query?: string
|
|
20
|
-
/**
|
|
29
|
+
/** 'follow' or 'unfollow' | true or false
|
|
30
|
+
* follow: ResponseButton 会跟随在 messages 的后边
|
|
31
|
+
* unfollow: ResponseButton 在聊天框底部
|
|
32
|
+
*/
|
|
33
|
+
showResponseButton?: string | boolean
|
|
34
|
+
/** 跳转 checkout action 卡片,接受参数: {"status":"complete","args":[{"sku":["A3936031"],"handle":"space-a40-a3936031"}],"result":""}
|
|
35
|
+
* 后端接口文档:https://anker-in.feishu.cn/wiki/DOYJwE9oxipWmYk072ncnJNznBb
|
|
36
|
+
*/
|
|
37
|
+
gotocheckoutRender?: string | ((_props: any) => React.ReactElement)
|
|
38
|
+
/** 打开购物车 action 卡片,接受参数: {"status":"complete","args":[{"sku":["A3936031"],"handle":"space-a40-a3936031"}],"result":""}
|
|
39
|
+
* 后端接口文档:https://anker-in.feishu.cn/wiki/DOYJwE9oxipWmYk072ncnJNznBb
|
|
40
|
+
*/
|
|
41
|
+
gotocartRender?: string | ((_props: any) => React.ReactElement)
|
|
42
|
+
/** 加购卡片,接受参数: {"status":"complete","args":[{"sku":["A3936031"],"handle":"space-a40-a3936031"}],"result":""}
|
|
21
43
|
* 后端接口文档:https://anker-in.feishu.cn/wiki/DOYJwE9oxipWmYk072ncnJNznBb
|
|
22
44
|
*/
|
|
23
|
-
|
|
24
|
-
/**
|
|
45
|
+
addtocartRender?: string | ((_props: any) => React.ReactElement)
|
|
46
|
+
/** 产品卡片,接受参数: {"status":"complete","args":[{"sku":["A3936031"],"handle":"space-a40-a3936031"}],"result":""}
|
|
47
|
+
* 后端接口文档:https://anker-in.feishu.cn/wiki/DOYJwE9oxipWmYk072ncnJNznBb
|
|
48
|
+
*/
|
|
49
|
+
productRender?: string | ((_props: any) => React.ReactElement)
|
|
50
|
+
/** 订阅卡片,接受参数: {"status":"complete","args":[{"sku":["A3936031"],"handle":"space-a40-a3936031"}],"result":""}
|
|
25
51
|
* 后端接口文档:https://anker-in.feishu.cn/wiki/DOYJwE9oxipWmYk072ncnJNznBb
|
|
26
52
|
*/
|
|
27
53
|
signupRender?: string | ((_props: any) => React.ReactElement)
|
|
28
54
|
/** CopilotPopup 自定义参数,参考:https://docs.copilotkit.ai/reference/components/CopilotPopup */
|
|
29
55
|
popup?: any
|
|
30
|
-
/** 文案,{"popupTip": "Hi ! Welcome to soundcore Innovations live chat!"} */
|
|
56
|
+
/** 文案,{"popupTip": "Hi ! Welcome to soundcore Innovations live chat!", "popupTipTimeout": [3000, 6000]} */
|
|
31
57
|
lang?: any
|
|
32
58
|
/** 参考此文档:https://docs.copilotkit.ai/concepts/customize-look-and-feel */
|
|
33
59
|
style?: any
|
|
@@ -41,38 +67,62 @@ const Chat = (props: ChatProps) => {
|
|
|
41
67
|
popup,
|
|
42
68
|
shopify_domain,
|
|
43
69
|
user_id = '',
|
|
70
|
+
account = '',
|
|
71
|
+
locale = '',
|
|
44
72
|
query = '',
|
|
45
|
-
|
|
73
|
+
showResponseButton,
|
|
74
|
+
gotocheckoutRender,
|
|
75
|
+
gotocartRender,
|
|
76
|
+
addtocartRender,
|
|
77
|
+
productRender,
|
|
46
78
|
signupRender,
|
|
47
79
|
style,
|
|
48
80
|
className = '',
|
|
49
81
|
lang,
|
|
50
82
|
} = props
|
|
83
|
+
const [autoTipOpen, autoTipClose] = lang?.popupTipTimeout || [3000, 6000]
|
|
51
84
|
|
|
52
85
|
const [openTip, setOpenTip] = useState(false)
|
|
53
86
|
const [openPopup, setOpenPopup] = useState(false)
|
|
54
87
|
const [start, setstart] = useState('')
|
|
88
|
+
const [currentSuggestions, setCurrentSuggestions] = useState([])
|
|
55
89
|
const [history, sethistory] = useState<[{ content: string; role: string }] | []>([])
|
|
56
90
|
|
|
57
91
|
const getStart = useCallback(async () => {
|
|
58
92
|
const result = await fetcher({
|
|
59
|
-
url: `${runtimeUrl}/copilotkit/start`,
|
|
93
|
+
url: `${runtimeUrl}/copilotkit/start${query}`,
|
|
60
94
|
method: 'GET',
|
|
61
|
-
headers: { user_id: user_id || '', shopify_domain
|
|
95
|
+
headers: { user_id: user_id || '', shopify_domain, account, locale },
|
|
62
96
|
})
|
|
63
97
|
|
|
98
|
+
setCurrentSuggestions(result?.suggested_questions?.map((item: string) => ({ message: item, title: item })) || [])
|
|
64
99
|
setstart(result?.opening_statement || '')
|
|
65
100
|
sethistory(result?.history || [])
|
|
66
|
-
}, [user_id])
|
|
101
|
+
}, [user_id, shopify_domain, account, locale])
|
|
102
|
+
|
|
103
|
+
const getSuggestions = useCallback(async () => {
|
|
104
|
+
const result = await fetcher({
|
|
105
|
+
url: `${runtimeUrl}/copilotkit/suggested_questions${query}`,
|
|
106
|
+
method: 'GET',
|
|
107
|
+
headers: { user_id: user_id || '', shopify_domain, account, locale },
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
setCurrentSuggestions(
|
|
111
|
+
result?.suggested_questions?.data?.map((item: string) => ({ message: item, title: item })) || []
|
|
112
|
+
)
|
|
113
|
+
}, [user_id, shopify_domain, account, locale])
|
|
67
114
|
|
|
68
115
|
const setOpenTipFn = () => {
|
|
69
116
|
if (!openTip) setOpenTip(true)
|
|
117
|
+
setTimeout(() => {
|
|
118
|
+
setOpenTip(false)
|
|
119
|
+
}, autoTipClose)
|
|
70
120
|
}
|
|
71
121
|
|
|
72
122
|
useEffect(() => {
|
|
73
123
|
const timer = setTimeout(() => {
|
|
74
124
|
setOpenTipFn()
|
|
75
|
-
},
|
|
125
|
+
}, autoTipOpen)
|
|
76
126
|
|
|
77
127
|
return () => {
|
|
78
128
|
clearTimeout(timer)
|
|
@@ -87,32 +137,39 @@ const Chat = (props: ChatProps) => {
|
|
|
87
137
|
|
|
88
138
|
return (
|
|
89
139
|
<div className={className} style={style || {}}>
|
|
90
|
-
<style>
|
|
91
|
-
{`
|
|
92
|
-
.copilotKitMessage.copilotKitAssistantMessage:has(.copilotKitMarkdown:empty) {
|
|
93
|
-
display: none;
|
|
94
|
-
}
|
|
95
|
-
`}
|
|
96
|
-
</style>
|
|
97
140
|
{user_id && runtimeUrl && (
|
|
98
141
|
<CopilotKit
|
|
99
|
-
runtimeUrl={`${runtimeUrl}/copilotkit/chat`}
|
|
142
|
+
runtimeUrl={`${runtimeUrl}/copilotkit/chat${query}`}
|
|
100
143
|
showDevConsole={false}
|
|
101
|
-
headers={{ user_id: user_id || '', shopify_domain: shopify_domain }}
|
|
144
|
+
headers={{ user_id: user_id || '', shopify_domain: shopify_domain, account, locale }}
|
|
102
145
|
>
|
|
103
146
|
<CopilotPopup
|
|
104
147
|
{...popup}
|
|
148
|
+
showResponseButton={showResponseButton !== 'follow'}
|
|
105
149
|
labels={{
|
|
106
150
|
title: title || 'DTC Live Chat',
|
|
107
151
|
initial: start || '',
|
|
108
152
|
}}
|
|
109
|
-
|
|
110
|
-
|
|
153
|
+
instructions={start || ''}
|
|
154
|
+
onInProgress={load => {
|
|
155
|
+
if (openPopup && load) setCurrentSuggestions([])
|
|
156
|
+
if (openPopup && !load) getSuggestions()
|
|
111
157
|
}}
|
|
158
|
+
Messages={({ messages, inProgress }) => (
|
|
159
|
+
<Messages
|
|
160
|
+
messages={messages}
|
|
161
|
+
inProgress={inProgress}
|
|
162
|
+
ResponseButton={showResponseButton === 'follow' ? <ResponseButton /> : undefined}
|
|
163
|
+
>
|
|
164
|
+
<Suggestions currentSuggestions={currentSuggestions} />
|
|
165
|
+
</Messages>
|
|
166
|
+
)}
|
|
112
167
|
Button={({ open, setOpen }) => {
|
|
113
|
-
const Button = popup?.Button
|
|
114
|
-
|
|
115
|
-
|
|
168
|
+
const Button = popup?.Button || DefaultButton
|
|
169
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
setOpenPopup(open)
|
|
172
|
+
}, [open])
|
|
116
173
|
return (
|
|
117
174
|
<>
|
|
118
175
|
{lang?.popupTip && openTip && !openPopup && (
|
|
@@ -129,53 +186,20 @@ const Chat = (props: ChatProps) => {
|
|
|
129
186
|
</button>
|
|
130
187
|
</div>
|
|
131
188
|
)}
|
|
132
|
-
|
|
133
|
-
{!Button && (
|
|
134
|
-
<button
|
|
135
|
-
onClick={() => {
|
|
136
|
-
setOpen(true)
|
|
137
|
-
setOpenTip(false)
|
|
138
|
-
}}
|
|
139
|
-
className="copilotKitButton "
|
|
140
|
-
aria-label="Open Chat"
|
|
141
|
-
>
|
|
142
|
-
<div className="copilotKitButtonIcon copilotKitButtonIconOpen">
|
|
143
|
-
<svg
|
|
144
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
145
|
-
viewBox="0 0 24 24"
|
|
146
|
-
fill="currentColor"
|
|
147
|
-
width="24"
|
|
148
|
-
height="24"
|
|
149
|
-
>
|
|
150
|
-
<g transform="translate(24, 0) scale(-1, 1)">
|
|
151
|
-
<path
|
|
152
|
-
fillRule="evenodd"
|
|
153
|
-
d="M5.337 21.718a6.707 6.707 0 01-.533-.074.75.75 0 01-.44-1.223 3.73 3.73 0 00.814-1.686c.023-.115-.022-.317-.254-.543C3.274 16.587 2.25 14.41 2.25 12c0-5.03 4.428-9 9.75-9s9.75 3.97 9.75 9c0 5.03-4.428 9-9.75 9-.833 0-1.643-.097-2.417-.279a6.721 6.721 0 01-4.246.997z"
|
|
154
|
-
clipRule="evenodd"
|
|
155
|
-
></path>
|
|
156
|
-
</g>
|
|
157
|
-
</svg>
|
|
158
|
-
</div>
|
|
159
|
-
<div className="copilotKitButtonIcon copilotKitButtonIconClose">
|
|
160
|
-
<svg
|
|
161
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
162
|
-
fill="none"
|
|
163
|
-
viewBox="0 0 24 24"
|
|
164
|
-
strokeWidth="1.5"
|
|
165
|
-
stroke="currentColor"
|
|
166
|
-
width="24"
|
|
167
|
-
height="24"
|
|
168
|
-
>
|
|
169
|
-
<path strokeLinecap="round" strokeLinejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"></path>
|
|
170
|
-
</svg>
|
|
171
|
-
</div>
|
|
172
|
-
</button>
|
|
173
|
-
)}
|
|
189
|
+
<Button open={open} setOpen={setOpen} setOpenTip={setOpenTip} />
|
|
174
190
|
</>
|
|
175
191
|
)
|
|
176
192
|
}}
|
|
177
193
|
>
|
|
178
|
-
<CopilotAction
|
|
194
|
+
<CopilotAction
|
|
195
|
+
start={start}
|
|
196
|
+
history={history}
|
|
197
|
+
gotocartRender={gotocartRender}
|
|
198
|
+
gotocheckoutRender={gotocheckoutRender}
|
|
199
|
+
addtocartRender={addtocartRender}
|
|
200
|
+
productRender={productRender}
|
|
201
|
+
signupRender={signupRender}
|
|
202
|
+
/>
|
|
179
203
|
</CopilotPopup>
|
|
180
204
|
</CopilotKit>
|
|
181
205
|
)}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type FC, memo } from 'react'
|
|
2
|
+
import ReactMarkdown, { type Options, type Components } from 'react-markdown'
|
|
3
|
+
import remarkGfm from 'remark-gfm'
|
|
4
|
+
import remarkMath from 'remark-math'
|
|
5
|
+
|
|
6
|
+
const MemoizedReactMarkdown: FC<Options> = memo(
|
|
7
|
+
ReactMarkdown,
|
|
8
|
+
(prevProps, nextProps) => prevProps.children === nextProps.children && prevProps.className === nextProps.className
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
type MarkdownProps = {
|
|
12
|
+
content: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const Markdown = ({ content }: MarkdownProps) => {
|
|
16
|
+
return (
|
|
17
|
+
<div className="copilotKitMarkdown">
|
|
18
|
+
<MemoizedReactMarkdown components={components} remarkPlugins={[remarkGfm, remarkMath]}>
|
|
19
|
+
{content}
|
|
20
|
+
</MemoizedReactMarkdown>
|
|
21
|
+
</div>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const components: Components = {
|
|
26
|
+
p({ children }) {
|
|
27
|
+
return <p>{children}</p>
|
|
28
|
+
},
|
|
29
|
+
a({ children, ...props }) {
|
|
30
|
+
return (
|
|
31
|
+
<a style={{ color: 'blue', textDecoration: 'underline' }} {...props} target="_blank" rel="noopener noreferrer">
|
|
32
|
+
{children}
|
|
33
|
+
</a>
|
|
34
|
+
)
|
|
35
|
+
},
|
|
36
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react'
|
|
2
|
+
import type { MessagesProps } from './props.js'
|
|
3
|
+
import { useChatContext } from '@copilotkit/react-ui'
|
|
4
|
+
import { Markdown } from './markdown.js'
|
|
5
|
+
import { type RenderFunctionStatus, useCopilotContext } from '@copilotkit/react-core'
|
|
6
|
+
import {
|
|
7
|
+
MessageStatusCode,
|
|
8
|
+
ActionExecutionMessage,
|
|
9
|
+
Message,
|
|
10
|
+
ResultMessage,
|
|
11
|
+
TextMessage,
|
|
12
|
+
Role,
|
|
13
|
+
} from '@copilotkit/runtime-client-gql'
|
|
14
|
+
|
|
15
|
+
const Messages = ({ messages, inProgress, ResponseButton, children }: MessagesProps) => {
|
|
16
|
+
const [isExpanded, setIsExpanded] = useState(true)
|
|
17
|
+
|
|
18
|
+
const { chatComponentsCache } = useCopilotContext()
|
|
19
|
+
|
|
20
|
+
const context = useChatContext()
|
|
21
|
+
const initialMessages = useMemo(() => makeInitialMessages(context.labels.initial), [context.labels.initial])
|
|
22
|
+
messages = [...initialMessages, ...messages]
|
|
23
|
+
|
|
24
|
+
const functionResults: Record<string, string> = {}
|
|
25
|
+
|
|
26
|
+
for (let i = 0; i < messages.length; i++) {
|
|
27
|
+
if (messages[i] instanceof ActionExecutionMessage) {
|
|
28
|
+
const id = messages[i].id
|
|
29
|
+
const resultMessage: ResultMessage | undefined = messages.find(
|
|
30
|
+
message => message instanceof ResultMessage && message.actionExecutionId === id
|
|
31
|
+
) as ResultMessage | undefined
|
|
32
|
+
|
|
33
|
+
if (resultMessage) {
|
|
34
|
+
functionResults[id] = ResultMessage.decodeResult(resultMessage.result || '')
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const messagesEndRef = React.useRef<HTMLDivElement>(null)
|
|
40
|
+
const messagesEndChildRef = React.useRef<HTMLDivElement>(null)
|
|
41
|
+
|
|
42
|
+
const scrollToBottom = () => {
|
|
43
|
+
if (messagesEndRef.current) {
|
|
44
|
+
messagesEndRef.current.scrollIntoView({
|
|
45
|
+
behavior: 'auto',
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
scrollToBottom()
|
|
52
|
+
}, [messages])
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
const textarea = document.querySelector('.copilotKitInput textarea') as HTMLTextAreaElement
|
|
56
|
+
|
|
57
|
+
textarea &&
|
|
58
|
+
textarea.addEventListener('click', function () {
|
|
59
|
+
if (textarea.value && document.activeElement === textarea) {
|
|
60
|
+
setIsExpanded(false)
|
|
61
|
+
} else {
|
|
62
|
+
setIsExpanded(true)
|
|
63
|
+
scrollToBottom()
|
|
64
|
+
}
|
|
65
|
+
})
|
|
66
|
+
textarea &&
|
|
67
|
+
textarea.addEventListener('input', function () {
|
|
68
|
+
if (textarea.value && document.activeElement === textarea) {
|
|
69
|
+
setIsExpanded(false)
|
|
70
|
+
} else {
|
|
71
|
+
setIsExpanded(true)
|
|
72
|
+
scrollToBottom()
|
|
73
|
+
}
|
|
74
|
+
})
|
|
75
|
+
}, [])
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
const content = messagesEndChildRef.current
|
|
79
|
+
if (isExpanded && content) {
|
|
80
|
+
content.style.maxHeight = '500px'
|
|
81
|
+
content.style.overflow = 'visible'
|
|
82
|
+
} else if (!isExpanded && content) {
|
|
83
|
+
content.style.maxHeight = '0'
|
|
84
|
+
content.style.overflow = 'hidden'
|
|
85
|
+
}
|
|
86
|
+
}, [isExpanded])
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div className="copilotKitMessages">
|
|
90
|
+
{messages.map((message, index) => {
|
|
91
|
+
const isCurrentMessage = index === messages.length - 1
|
|
92
|
+
|
|
93
|
+
if (message instanceof TextMessage && message.role === 'user') {
|
|
94
|
+
return (
|
|
95
|
+
<div key={index} className="copilotKitMessage copilotKitUserMessage">
|
|
96
|
+
{message.content}
|
|
97
|
+
</div>
|
|
98
|
+
)
|
|
99
|
+
} else if (message instanceof TextMessage && message.role == 'assistant') {
|
|
100
|
+
return (
|
|
101
|
+
<div key={index} className={`copilotKitMessage copilotKitAssistantMessage`}>
|
|
102
|
+
{isCurrentMessage && inProgress && !message.content ? (
|
|
103
|
+
context.icons.spinnerIcon
|
|
104
|
+
) : (
|
|
105
|
+
<Markdown content={message.content} />
|
|
106
|
+
)}
|
|
107
|
+
</div>
|
|
108
|
+
)
|
|
109
|
+
} else if (message instanceof ActionExecutionMessage) {
|
|
110
|
+
if (chatComponentsCache.current !== null && chatComponentsCache.current[message.name]) {
|
|
111
|
+
const render = chatComponentsCache.current[message.name]
|
|
112
|
+
// render a static string
|
|
113
|
+
if (typeof render === 'string') {
|
|
114
|
+
// when render is static, we show it only when in progress
|
|
115
|
+
if (isCurrentMessage && inProgress) {
|
|
116
|
+
return (
|
|
117
|
+
<div key={index} className={`copilotKitMessage copilotKitAssistantMessage`}>
|
|
118
|
+
{context.icons.spinnerIcon} <span className="inProgressLabel">{render}</span>
|
|
119
|
+
</div>
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
// Done - silent by default to avoid a series of "done" messages
|
|
123
|
+
else {
|
|
124
|
+
return null
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// render is a function
|
|
128
|
+
else {
|
|
129
|
+
const args = message.arguments
|
|
130
|
+
|
|
131
|
+
let status: RenderFunctionStatus = 'inProgress'
|
|
132
|
+
|
|
133
|
+
if (functionResults[message.id] !== undefined) {
|
|
134
|
+
status = 'complete'
|
|
135
|
+
} else if (message.status.code !== MessageStatusCode.Pending) {
|
|
136
|
+
status = 'executing'
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const toRender = render({
|
|
140
|
+
status: status as any,
|
|
141
|
+
args,
|
|
142
|
+
result: functionResults[message.id],
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// No result and complete: stay silent
|
|
146
|
+
if (!toRender && status === 'complete') {
|
|
147
|
+
return null
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (typeof toRender === 'string') {
|
|
151
|
+
return (
|
|
152
|
+
<div key={index} className={`copilotKitMessage copilotKitAssistantMessage`}>
|
|
153
|
+
{isCurrentMessage && inProgress && context.icons.spinnerIcon} {toRender}
|
|
154
|
+
</div>
|
|
155
|
+
)
|
|
156
|
+
} else {
|
|
157
|
+
return (
|
|
158
|
+
<div key={index} className="copilotKitCustomAssistantMessage">
|
|
159
|
+
{toRender}
|
|
160
|
+
</div>
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// No render function found- show the default message
|
|
166
|
+
else if (!inProgress || !isCurrentMessage) {
|
|
167
|
+
// Done - silent by default to avoid a series of "done" messages
|
|
168
|
+
return null
|
|
169
|
+
} else {
|
|
170
|
+
// In progress
|
|
171
|
+
return (
|
|
172
|
+
<div key={index} className={`copilotKitMessage copilotKitAssistantMessage`}>
|
|
173
|
+
{context.icons.spinnerIcon}
|
|
174
|
+
</div>
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
})}
|
|
179
|
+
<div className="responseButtonBox">{ResponseButton}</div>
|
|
180
|
+
<footer ref={messagesEndRef}>
|
|
181
|
+
<div className="copilotKitMessagesFooter" ref={messagesEndChildRef}>
|
|
182
|
+
{children}
|
|
183
|
+
</div>
|
|
184
|
+
</footer>
|
|
185
|
+
</div>
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function makeInitialMessages(initial?: string | string[]): Message[] {
|
|
190
|
+
let initialArray: string[] = []
|
|
191
|
+
if (initial) {
|
|
192
|
+
if (Array.isArray(initial)) {
|
|
193
|
+
initialArray.push(...initial)
|
|
194
|
+
} else {
|
|
195
|
+
initialArray.push(initial)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return initialArray.map(
|
|
200
|
+
message =>
|
|
201
|
+
new TextMessage({
|
|
202
|
+
role: Role.Assistant,
|
|
203
|
+
content: message,
|
|
204
|
+
})
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export default Messages
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Message } from '@copilotkit/runtime-client-gql'
|
|
3
|
+
|
|
4
|
+
export interface ButtonProps {
|
|
5
|
+
open: boolean
|
|
6
|
+
setOpen: (open: boolean) => void
|
|
7
|
+
setOpenTip: (open: boolean) => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface WindowProps {
|
|
11
|
+
open: boolean
|
|
12
|
+
setOpen: (open: boolean) => void
|
|
13
|
+
clickOutsideToClose: boolean
|
|
14
|
+
hitEscapeToClose: boolean
|
|
15
|
+
shortcut: string
|
|
16
|
+
children?: React.ReactNode
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface HeaderProps {
|
|
20
|
+
open: boolean
|
|
21
|
+
setOpen: (open: boolean) => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ChatSuggestions {
|
|
25
|
+
currentSuggestions: SuggestionsProps[]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SuggestionsProps {
|
|
29
|
+
title: string
|
|
30
|
+
message: string
|
|
31
|
+
partial?: boolean
|
|
32
|
+
className?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface MessagesProps {
|
|
36
|
+
messages: Message[]
|
|
37
|
+
inProgress: boolean
|
|
38
|
+
ResponseButton?: React.ReactElement
|
|
39
|
+
children?: React.ReactNode
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface InputProps {
|
|
43
|
+
inProgress: boolean
|
|
44
|
+
onSend: (text: string) => Promise<Message>
|
|
45
|
+
isVisible?: boolean
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ResponseButtonProps {
|
|
49
|
+
onClick: () => void
|
|
50
|
+
inProgress: boolean
|
|
51
|
+
}
|