Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
D
dify
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ai-tech
dify
Commits
c33d7a14
Commit
c33d7a14
authored
Jul 24, 2023
by
金伟强
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add loading effect
parent
93d7adf2
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
109 additions
and
8 deletions
+109
-8
index.tsx
web/app/components/app/chat/answer/index.tsx
+3
-2
index.tsx
web/app/components/app/chat/index.tsx
+3
-0
index.tsx
web/app/components/app/chat/thought/index.tsx
+8
-3
type.ts
web/app/components/app/chat/type.ts
+1
-0
loading.svg
...p/components/base/icons/assets/public/thought/loading.svg
+10
-0
Loading.json
...app/components/base/icons/src/public/thought/Loading.json
+64
-0
Loading.tsx
web/app/components/base/icons/src/public/thought/Loading.tsx
+14
-0
index.ts
web/app/components/base/icons/src/public/thought/index.ts
+1
-0
index.tsx
web/app/components/explore/universal-chat/index.tsx
+3
-2
base.ts
web/service/base.ts
+2
-1
No files found.
web/app/components/app/chat/answer/index.tsx
View file @
c33d7a14
...
@@ -44,9 +44,10 @@ export type IAnswerProps = {
...
@@ -44,9 +44,10 @@ export type IAnswerProps = {
isResponsing
?:
boolean
isResponsing
?:
boolean
answerIconClassName
?:
string
answerIconClassName
?:
string
thoughts
?:
ThoughtItem
[]
thoughts
?:
ThoughtItem
[]
isThinking
?:
boolean
}
}
// The component needs to maintain its own state to control whether to display input component
// The component needs to maintain its own state to control whether to display input component
const
Answer
:
FC
<
IAnswerProps
>
=
({
item
,
feedbackDisabled
=
false
,
isHideFeedbackEdit
=
false
,
onFeedback
,
onSubmitAnnotation
,
displayScene
=
'web'
,
isResponsing
,
answerIconClassName
,
thoughts
})
=>
{
const
Answer
:
FC
<
IAnswerProps
>
=
({
item
,
feedbackDisabled
=
false
,
isHideFeedbackEdit
=
false
,
onFeedback
,
onSubmitAnnotation
,
displayScene
=
'web'
,
isResponsing
,
answerIconClassName
,
thoughts
,
isThinking
})
=>
{
const
{
id
,
content
,
more
,
feedback
,
adminFeedback
,
annotation
:
initAnnotation
}
=
item
const
{
id
,
content
,
more
,
feedback
,
adminFeedback
,
annotation
:
initAnnotation
}
=
item
const
[
showEdit
,
setShowEdit
]
=
useState
(
false
)
const
[
showEdit
,
setShowEdit
]
=
useState
(
false
)
const
[
loading
,
setLoading
]
=
useState
(
false
)
const
[
loading
,
setLoading
]
=
useState
(
false
)
...
@@ -204,7 +205,7 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedba
...
@@ -204,7 +205,7 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedba
:
(
:
(
<
div
>
<
div
>
{
(
thoughts
&&
thoughts
.
length
>
0
)
&&
(
{
(
thoughts
&&
thoughts
.
length
>
0
)
&&
(
<
Thought
list=
{
thoughts
||
[]
}
/>
<
Thought
list=
{
thoughts
||
[]
}
isThinking=
{
isThinking
}
/>
)
}
)
}
<
Markdown
content=
{
content
}
/>
<
Markdown
content=
{
content
}
/>
</
div
>
</
div
>
...
...
web/app/components/app/chat/index.tsx
View file @
c33d7a14
...
@@ -47,6 +47,7 @@ export type IChatProps = {
...
@@ -47,6 +47,7 @@ export type IChatProps = {
isShowSpeechToText
?:
boolean
isShowSpeechToText
?:
boolean
answerIconClassName
?:
string
answerIconClassName
?:
string
isShowConfigElem
?:
boolean
isShowConfigElem
?:
boolean
isThoughting
?:
boolean
}
}
const
Chat
:
FC
<
IChatProps
>
=
({
const
Chat
:
FC
<
IChatProps
>
=
({
...
@@ -156,6 +157,7 @@ const Chat: FC<IChatProps> = ({
...
@@ -156,6 +157,7 @@ const Chat: FC<IChatProps> = ({
if
(
item
.
isAnswer
)
{
if
(
item
.
isAnswer
)
{
const
isLast
=
item
.
id
===
chatList
[
chatList
.
length
-
1
].
id
const
isLast
=
item
.
id
===
chatList
[
chatList
.
length
-
1
].
id
const
thoughts
=
item
.
agent_thoughts
?.
filter
(
item
=>
item
.
thought
!==
'[DONE]'
)
const
thoughts
=
item
.
agent_thoughts
?.
filter
(
item
=>
item
.
thought
!==
'[DONE]'
)
const
isThinking
=
item
.
agent_thoughts
&&
item
.
agent_thoughts
?.
length
>
0
&&
!
item
.
agent_thoughts
.
find
(
item
=>
item
.
thought
!==
'[DONE]'
)
return
<
Answer
return
<
Answer
key=
{
item
.
id
}
key=
{
item
.
id
}
item=
{
item
}
item=
{
item
}
...
@@ -167,6 +169,7 @@ const Chat: FC<IChatProps> = ({
...
@@ -167,6 +169,7 @@ const Chat: FC<IChatProps> = ({
isResponsing=
{
isResponsing
&&
isLast
}
isResponsing=
{
isResponsing
&&
isLast
}
answerIconClassName=
{
answerIconClassName
}
answerIconClassName=
{
answerIconClassName
}
thoughts=
{
thoughts
}
thoughts=
{
thoughts
}
isThinking=
{
isThinking
}
/>
/>
}
}
return
<
Question
key=
{
item
.
id
}
id=
{
item
.
id
}
content=
{
item
.
content
}
more=
{
item
.
more
}
useCurrentUserAvatar=
{
useCurrentUserAvatar
}
/>
return
<
Question
key=
{
item
.
id
}
id=
{
item
.
id
}
content=
{
item
.
content
}
more=
{
item
.
more
}
useCurrentUserAvatar=
{
useCurrentUserAvatar
}
/>
...
...
web/app/components/app/chat/thought/index.tsx
View file @
c33d7a14
...
@@ -5,13 +5,14 @@ import cn from 'classnames'
...
@@ -5,13 +5,14 @@ import cn from 'classnames'
import
{
useTranslation
}
from
'react-i18next'
import
{
useTranslation
}
from
'react-i18next'
import
type
{
ThoughtItem
}
from
'../type'
import
type
{
ThoughtItem
}
from
'../type'
import
s
from
'./style.module.css'
import
s
from
'./style.module.css'
import
{
DataSet
,
Search
,
ThoughtList
,
WebReader
}
from
'@/app/components/base/icons/src/public/thought'
import
{
DataSet
,
Loading
as
LodingIcon
,
Search
,
ThoughtList
,
WebReader
}
from
'@/app/components/base/icons/src/public/thought'
import
{
ChevronDown
}
from
'@/app/components/base/icons/src/vender/line/arrows'
import
{
ChevronDown
}
from
'@/app/components/base/icons/src/vender/line/arrows'
// https://www.freecodecamp.org/news/how-to-write-a-regular-expression-for-a-url/
// https://www.freecodecamp.org/news/how-to-write-a-regular-expression-for-a-url/
const
urlRegex
=
/
(
https:
\/\/
www
\.
|http:
\/\/
www
\.
|https:
\/\/
|http:
\/\/)?[
a-zA-Z
]{2,}(\.[
a-zA-Z
]{2,})(\.[
a-zA-Z
]{2,})?\/[
a-zA-Z0-9
]{2,}
|
((
https:
\/\/
www
\.
|http:
\/\/
www
\.
|https:
\/\/
|http:
\/\/)?[
a-zA-Z
]{2,}(\.[
a-zA-Z
]{2,})(\.[
a-zA-Z
]{2,})?)
|
(
https:
\/\/
www
\.
|http:
\/\/
www
\.
|https:
\/\/
|http:
\/\/)?[
a-zA-Z0-9
]{2,}\.[
a-zA-Z0-9
]{2,}\.[
a-zA-Z0-9
]{2,}(\.[
a-zA-Z0-9
]{2,})?
/gi
const
urlRegex
=
/
(
https:
\/\/
www
\.
|http:
\/\/
www
\.
|https:
\/\/
|http:
\/\/)?[
a-zA-Z
]{2,}(\.[
a-zA-Z
]{2,})(\.[
a-zA-Z
]{2,})?\/[
a-zA-Z0-9
]{2,}
|
((
https:
\/\/
www
\.
|http:
\/\/
www
\.
|https:
\/\/
|http:
\/\/)?[
a-zA-Z
]{2,}(\.[
a-zA-Z
]{2,})(\.[
a-zA-Z
]{2,})?)
|
(
https:
\/\/
www
\.
|http:
\/\/
www
\.
|https:
\/\/
|http:
\/\/)?[
a-zA-Z0-9
]{2,}\.[
a-zA-Z0-9
]{2,}\.[
a-zA-Z0-9
]{2,}(\.[
a-zA-Z0-9
]{2,})?
/gi
export
type
IThoughtProps
=
{
export
type
IThoughtProps
=
{
list
:
ThoughtItem
[]
list
:
ThoughtItem
[]
isThinking
?:
boolean
}
}
const
getIcon
=
(
toolId
:
string
)
=>
{
const
getIcon
=
(
toolId
:
string
)
=>
{
...
@@ -27,6 +28,7 @@ const getIcon = (toolId: string) => {
...
@@ -27,6 +28,7 @@ const getIcon = (toolId: string) => {
const
Thought
:
FC
<
IThoughtProps
>
=
({
const
Thought
:
FC
<
IThoughtProps
>
=
({
list
,
list
,
isThinking
,
})
=>
{
})
=>
{
const
{
t
}
=
useTranslation
()
const
{
t
}
=
useTranslation
()
const
[
isShowDetail
,
setIsShowDetail
]
=
React
.
useState
(
false
)
const
[
isShowDetail
,
setIsShowDetail
]
=
React
.
useState
(
false
)
...
@@ -62,8 +64,11 @@ const Thought: FC<IThoughtProps> = ({
...
@@ -62,8 +64,11 @@ const Thought: FC<IThoughtProps> = ({
return
(
return
(
<
div
className=
{
cn
(
s
.
wrap
,
!
isShowDetail
&&
s
.
wrapHoverEffect
,
'inline-block mb-2 px-2 py-0.5 rounded-md text-xs text-gray-500 font-medium'
)
}
>
<
div
className=
{
cn
(
s
.
wrap
,
!
isShowDetail
&&
s
.
wrapHoverEffect
,
'inline-block mb-2 px-2 py-0.5 rounded-md text-xs text-gray-500 font-medium'
)
}
>
<
div
className=
'flex items-center h-6 space-x-1 cursor-pointer'
onClick=
{
()
=>
setIsShowDetail
(
!
isShowDetail
)
}
>
<
div
className=
'flex items-center h-6 space-x-1 cursor-pointer'
onClick=
{
()
=>
setIsShowDetail
(
!
isShowDetail
)
}
>
<
ThoughtList
/>
{
!
isThinking
?
<
ThoughtList
/>
:
<
div
className=
'animate-spin'
><
LodingIcon
/></
div
>
}
<
div
>
{
t
(
`explore.universalChat.thought.${isShowDetail ? 'hide' : 'show'}`
)
}{
t
(
'explore.universalChat.thought.processOfThought'
)
}
</
div
>
<
div
dangerouslySetInnerHTML=
{
{
__html
:
isThinking
?
getThoughtText
(
list
[
0
])
:
(
t
(
`explore.universalChat.thought.${isShowDetail ? 'hide' : 'show'}`
)
+
t
(
'explore.universalChat.thought.processOfThought'
)),
}
}
></
div
>
<
ChevronDown
className=
{
isShowDetail
?
'rotate-180'
:
''
}
/>
<
ChevronDown
className=
{
isShowDetail
?
'rotate-180'
:
''
}
/>
</
div
>
</
div
>
{
isShowDetail
&&
(
{
isShowDetail
&&
(
...
...
web/app/components/app/chat/type.ts
View file @
c33d7a14
...
@@ -21,6 +21,7 @@ export type ThoughtItem = {
...
@@ -21,6 +21,7 @@ export type ThoughtItem = {
tool
:
string
// plugin or dataset
tool
:
string
// plugin or dataset
thought
:
string
thought
:
string
tool_input
:
string
tool_input
:
string
message_id
:
string
}
}
export
type
IChatItem
=
{
export
type
IChatItem
=
{
id
:
string
id
:
string
...
...
web/app/components/base/icons/assets/public/thought/loading.svg
0 → 100644
View file @
c33d7a14
<svg
width=
"12"
height=
"12"
viewBox=
"0 0 12 12"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<g
clip-path=
"url(#clip0_7998_4025)"
>
<path
d=
"M6 1.125V2.375M6 9V11M2.875 6H1.125M10.625 6H9.875M9.22855 9.22855L8.875 8.875M9.33211 2.70789L8.625 3.415M2.46079 9.53921L3.875 8.125M2.56434 2.60434L3.625 3.665"
stroke=
"#667085"
stroke-width=
"1.25"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
</g>
<defs>
<clipPath
id=
"clip0_7998_4025"
>
<rect
width=
"12"
height=
"12"
fill=
"white"
/>
</clipPath>
</defs>
</svg>
web/app/components/base/icons/src/public/thought/Loading.json
0 → 100644
View file @
c33d7a14
{
"icon"
:
{
"type"
:
"element"
,
"isRootNode"
:
true
,
"name"
:
"svg"
,
"attributes"
:
{
"width"
:
"12"
,
"height"
:
"12"
,
"viewBox"
:
"0 0 12 12"
,
"fill"
:
"none"
,
"xmlns"
:
"http://www.w3.org/2000/svg"
},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"g"
,
"attributes"
:
{
"clip-path"
:
"url(#clip0_7998_4025)"
},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"path"
,
"attributes"
:
{
"d"
:
"M6 1.125V2.375M6 9V11M2.875 6H1.125M10.625 6H9.875M9.22855 9.22855L8.875 8.875M9.33211 2.70789L8.625 3.415M2.46079 9.53921L3.875 8.125M2.56434 2.60434L3.625 3.665"
,
"stroke"
:
"#667085"
,
"stroke-width"
:
"1.25"
,
"stroke-linecap"
:
"round"
,
"stroke-linejoin"
:
"round"
},
"children"
:
[]
}
]
},
{
"type"
:
"element"
,
"name"
:
"defs"
,
"attributes"
:
{},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"clipPath"
,
"attributes"
:
{
"id"
:
"clip0_7998_4025"
},
"children"
:
[
{
"type"
:
"element"
,
"name"
:
"rect"
,
"attributes"
:
{
"width"
:
"12"
,
"height"
:
"12"
,
"fill"
:
"white"
},
"children"
:
[]
}
]
}
]
}
]
},
"name"
:
"Loading"
}
\ No newline at end of file
web/app/components/base/icons/src/public/thought/Loading.tsx
0 → 100644
View file @
c33d7a14
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import
*
as
React
from
'react'
import
data
from
'./Loading.json'
import
IconBase
from
'@/app/components/base/icons/IconBase'
import
type
{
IconBaseProps
,
IconData
}
from
'@/app/components/base/icons/IconBase'
const
Icon
=
React
.
forwardRef
<
React
.
MutableRefObject
<
SVGElement
>
,
Omit
<
IconBaseProps
,
'data'
>>
((
props
,
ref
,
)
=>
<
IconBase
{
...
props
}
ref=
{
ref
}
data=
{
data
as
IconData
}
/>)
export
default
Icon
web/app/components/base/icons/src/public/thought/index.ts
View file @
c33d7a14
export
{
default
as
DataSet
}
from
'./DataSet'
export
{
default
as
DataSet
}
from
'./DataSet'
export
{
default
as
Loading
}
from
'./Loading'
export
{
default
as
Search
}
from
'./Search'
export
{
default
as
Search
}
from
'./Search'
export
{
default
as
ThoughtList
}
from
'./ThoughtList'
export
{
default
as
ThoughtList
}
from
'./ThoughtList'
export
{
default
as
WebReader
}
from
'./WebReader'
export
{
default
as
WebReader
}
from
'./WebReader'
web/app/components/explore/universal-chat/index.tsx
View file @
c33d7a14
...
@@ -480,7 +480,7 @@ const Main: FC<IMainProps> = () => {
...
@@ -480,7 +480,7 @@ const Main: FC<IMainProps> = () => {
getChatList
().
filter
(
item
=>
item
.
id
!==
responseItem
.
id
&&
item
.
id
!==
placeholderAnswerId
),
getChatList
().
filter
(
item
=>
item
.
id
!==
responseItem
.
id
&&
item
.
id
!==
placeholderAnswerId
),
(
draft
)
=>
{
(
draft
)
=>
{
if
(
!
draft
.
find
(
item
=>
item
.
id
===
questionId
))
if
(
!
draft
.
find
(
item
=>
item
.
id
===
questionId
))
draft
.
push
({
...
questionItem
})
draft
.
push
({
...
questionItem
}
as
any
)
draft
.
push
({
...
responseItem
})
draft
.
push
({
...
responseItem
})
})
})
...
@@ -508,6 +508,7 @@ const Main: FC<IMainProps> = () => {
...
@@ -508,6 +508,7 @@ const Main: FC<IMainProps> = () => {
onThought
(
thought
)
{
onThought
(
thought
)
{
if
(
thought
.
thought
===
'[DONE]'
)
if
(
thought
.
thought
===
'[DONE]'
)
return
return
responseItem
.
id
=
thought
.
message_id
;
// thought finished then start to return message
// thought finished then start to return message
(
responseItem
as
any
).
agent_thoughts
.
push
(
thought
)
(
responseItem
as
any
).
agent_thoughts
.
push
(
thought
)
const
newListWithAnswer
=
produce
(
const
newListWithAnswer
=
produce
(
...
@@ -515,9 +516,9 @@ const Main: FC<IMainProps> = () => {
...
@@ -515,9 +516,9 @@ const Main: FC<IMainProps> = () => {
(
draft
)
=>
{
(
draft
)
=>
{
if
(
!
draft
.
find
(
item
=>
item
.
id
===
questionId
))
if
(
!
draft
.
find
(
item
=>
item
.
id
===
questionId
))
draft
.
push
({
...
questionItem
})
draft
.
push
({
...
questionItem
})
draft
.
push
({
...
responseItem
})
draft
.
push
({
...
responseItem
})
})
})
// console.log('start render thought')
setChatList
(
newListWithAnswer
)
setChatList
(
newListWithAnswer
)
},
},
onError
()
{
onError
()
{
...
...
web/service/base.ts
View file @
c33d7a14
/* eslint-disable no-new, prefer-promise-reject-errors */
/* eslint-disable no-new, prefer-promise-reject-errors */
import
{
API_PREFIX
,
IS_CE_EDITION
,
PUBLIC_API_PREFIX
}
from
'@/config'
import
{
API_PREFIX
,
IS_CE_EDITION
,
PUBLIC_API_PREFIX
}
from
'@/config'
import
Toast
from
'@/app/components/base/toast'
import
Toast
from
'@/app/components/base/toast'
import
type
{
ThoughtItem
}
from
'@/app/components/app/chat/type'
const
TIME_OUT
=
100000
const
TIME_OUT
=
100000
...
@@ -30,7 +31,7 @@ export type IOnDataMoreInfo = {
...
@@ -30,7 +31,7 @@ export type IOnDataMoreInfo = {
}
}
export
type
IOnData
=
(
message
:
string
,
isFirstMessage
:
boolean
,
moreInfo
:
IOnDataMoreInfo
)
=>
void
export
type
IOnData
=
(
message
:
string
,
isFirstMessage
:
boolean
,
moreInfo
:
IOnDataMoreInfo
)
=>
void
export
type
IOnThought
=
(
though
:
{
id
:
string
;
tool
:
string
;
thought
:
string
}
)
=>
void
export
type
IOnThought
=
(
though
:
ThoughtItem
)
=>
void
export
type
IOnCompleted
=
(
hasError
?:
boolean
)
=>
void
export
type
IOnCompleted
=
(
hasError
?:
boolean
)
=>
void
export
type
IOnError
=
(
msg
:
string
)
=>
void
export
type
IOnError
=
(
msg
:
string
)
=>
void
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment