Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
赵月辉
/
fastgpt-migrated
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
a510f96b
authored
Jul 18, 2023
by
archer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
markdown guide
parent
505aff3d
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
176 additions
and
78 deletions
+176
-78
client/src/api/fetch.ts
+0
-2
client/src/components/ChatBox/index.tsx
+77
-56
client/src/components/Markdown/chat/Guide.tsx
+49
-0
client/src/components/Markdown/constant.ts
+3
-0
client/src/components/Markdown/index.tsx
+32
-13
client/src/pages/app/detail/components/Settings.tsx
+1
-1
client/src/pages/app/detail/components/edit/components/Nodes/NodeUserGuide.tsx
+1
-1
client/src/pages/chat/components/ToolMenu.tsx
+13
-5
No files found.
client/src/api/fetch.ts
View file @
a510f96b
...
...
@@ -89,8 +89,6 @@ export const streamFetch = ({
});
read
();
}
catch
(
err
:
any
)
{
console
.
log
(
111111111111111111
);
if
(
err
?.
message
===
'The user aborted a request.'
)
{
return
resolve
({
responseText
,
...
...
client/src/components/ChatBox/index.tsx
View file @
a510f96b
...
...
@@ -17,6 +17,7 @@ import { useUserStore } from '@/store/user';
import
{
Types
}
from
'mongoose'
;
import
{
HUMAN_ICON
,
quoteLenKey
,
rawSearchKey
}
from
'@/constants/chat'
;
import
Markdown
from
'@/components/Markdown'
;
import
{
EventNameEnum
}
from
'../Markdown/constant'
;
import
MyIcon
from
'@/components/Icon'
;
import
Avatar
from
'@/components/Avatar'
;
...
...
@@ -236,7 +237,7 @@ const ChatBox = (
* user confirm send prompt
*/
const
sendPrompt
=
useCallback
(
async
(
data
:
Record
<
string
,
any
>
=
{})
=>
{
async
(
data
:
Record
<
string
,
any
>
=
{}
,
inputVal
=
''
)
=>
{
if
(
isChatting
)
{
toast
({
title
:
'正在聊天中...请等待结束'
,
...
...
@@ -245,8 +246,7 @@ const ChatBox = (
return
;
}
// get input value
const
value
=
TextareaDom
.
current
?.
value
||
''
;
const
val
=
value
.
trim
().
replace
(
/
\n\s
*/g
,
'\n'
);
const
val
=
inputVal
.
trim
().
replace
(
/
\n\s
*/g
,
'\n'
);
if
(
!
val
)
{
toast
({
...
...
@@ -320,7 +320,7 @@ const ChatBox = (
});
if
(
!
err
?.
responseText
)
{
resetInputVal
(
value
);
resetInputVal
(
inputVal
);
setChatHistory
(
newChatList
.
slice
(
0
,
newChatList
.
length
-
2
));
}
...
...
@@ -403,11 +403,25 @@ const ChatBox = (
{
/* avatar */
}
<
ChatAvatar
src=
{
appAvatar
}
order=
{
1
}
mr=
{
[
'6px'
,
2
]
}
/>
{
/* message */
}
<
Flex
order=
{
2
}
pt=
{
2
}
maxW=
{
`calc(100% - ${isLargeWidth ? '75px' : '58px'})`
}
>
<
Card
bg=
{
'white'
}
px=
{
4
}
py=
{
3
}
borderRadius=
{
'0 8px 8px 8px'
}
>
{
welcomeText
}
</
Card
>
</
Flex
>
<
Card
order=
{
2
}
mt=
{
2
}
px=
{
4
}
py=
{
3
}
bg=
{
'white'
}
maxW=
{
`calc(100% - ${isLargeWidth ? '75px' : '58px'})`
}
borderRadius=
{
'0 8px 8px 8px'
}
>
<
Markdown
source=
{
`~~~guide \n${welcomeText}`
}
isChatting=
{
false
}
onClick=
{
(
e
)
=>
{
const
val
=
e
?.
data
;
if
(
e
?.
event
!==
EventNameEnum
.
guideClick
||
!
val
)
return
;
handleSubmit
((
data
)
=>
sendPrompt
(
data
,
val
))();
}
}
/>
</
Card
>
</
Flex
>
)
}
{
/* variable input */
}
...
...
@@ -416,54 +430,59 @@ const ChatBox = (
{
/* avatar */
}
<
ChatAvatar
src=
{
appAvatar
}
order=
{
1
}
mr=
{
[
'6px'
,
2
]
}
/>
{
/* message */
}
<
Flex
order=
{
2
}
pt=
{
2
}
maxW=
{
`calc(100% - ${isLargeWidth ? '75px' : '58px'})`
}
>
<
Card
bg=
{
'white'
}
px=
{
4
}
py=
{
3
}
borderRadius=
{
'0 8px 8px 8px'
}
>
<
Box
>
{
variableModules
.
map
((
item
)
=>
(
<
Box
w=
{
'min(100%,300px)'
}
key=
{
item
.
id
}
mb=
{
4
}
>
<
VariableLabel
required=
{
item
.
required
}
>
{
item
.
label
}
</
VariableLabel
>
{
item
.
type
===
VariableInputEnum
.
input
&&
(
<
Input
isDisabled=
{
variableIsFinish
}
{
...
register
(
item
.
key
,
{
required
:
item
.
required
})}
/>
)
}
{
item
.
type
===
VariableInputEnum
.
select
&&
(
<
MySelect
width=
{
'100%'
}
isDisabled=
{
variableIsFinish
}
list=
{
(
item
.
enums
||
[]).
map
((
item
)
=>
({
label
:
item
.
value
,
value
:
item
.
value
}))
}
value=
{
getValues
(
item
.
key
)
}
onchange=
{
(
e
)
=>
{
setValue
(
item
.
key
,
e
);
setRefresh
(
!
refresh
);
}
}
/>
)
}
</
Box
>
))
}
{
!
variableIsFinish
&&
(
<
Button
leftIcon=
{
<
MyIcon
name=
{
'chatFill'
}
w=
{
'16px'
}
/>
}
size=
{
'sm'
}
maxW=
{
'100px'
}
borderRadius=
{
'lg'
}
onClick=
{
handleSubmit
((
data
)
=>
{
onUpdateVariable
?.(
data
);
setVariables
(
data
);
<
Card
order=
{
2
}
mt=
{
2
}
flex=
{
1
}
bg=
{
'white'
}
px=
{
4
}
py=
{
3
}
borderRadius=
{
'0 8px 8px 8px'
}
maxW=
{
'min(100%,400px)'
}
>
{
variableModules
.
map
((
item
)
=>
(
<
Box
key=
{
item
.
id
}
mb=
{
4
}
>
<
VariableLabel
required=
{
item
.
required
}
>
{
item
.
label
}
</
VariableLabel
>
{
item
.
type
===
VariableInputEnum
.
input
&&
(
<
Input
isDisabled=
{
variableIsFinish
}
{
...
register
(
item
.
key
,
{
required
:
item
.
required
})}
>
{
'开始对话'
}
</
Button
>
/>
)
}
{
item
.
type
===
VariableInputEnum
.
select
&&
(
<
MySelect
width=
{
'100%'
}
isDisabled=
{
variableIsFinish
}
list=
{
(
item
.
enums
||
[]).
map
((
item
)
=>
({
label
:
item
.
value
,
value
:
item
.
value
}))
}
value=
{
getValues
(
item
.
key
)
}
onchange=
{
(
e
)
=>
{
setValue
(
item
.
key
,
e
);
setRefresh
(
!
refresh
);
}
}
/>
)
}
</
Box
>
</
Card
>
</
Flex
>
))
}
{
!
variableIsFinish
&&
(
<
Button
leftIcon=
{
<
MyIcon
name=
{
'chatFill'
}
w=
{
'16px'
}
/>
}
size=
{
'sm'
}
maxW=
{
'100px'
}
borderRadius=
{
'lg'
}
onClick=
{
handleSubmit
((
data
)
=>
{
onUpdateVariable
?.(
data
);
setVariables
(
data
);
})
}
>
{
'开始对话'
}
</
Button
>
)
}
</
Card
>
</
Flex
>
)
}
...
...
@@ -650,7 +669,7 @@ const ChatBox = (
onKeyDown=
{
(
e
)
=>
{
// 触发快捷发送
if
(
e
.
keyCode
===
13
&&
!
e
.
shiftKey
)
{
handleSubmit
(
sendPrompt
)();
handleSubmit
(
(
data
)
=>
sendPrompt
(
data
,
TextareaDom
.
current
?.
value
)
)();
e
.
preventDefault
();
}
// 全选内容
...
...
@@ -685,7 +704,9 @@ const ChatBox = (
height=
{
[
'18px'
,
'20px'
]
}
cursor=
{
'pointer'
}
color=
{
'gray.500'
}
onClick=
{
handleSubmit
(
sendPrompt
)
}
onClick=
{
()
=>
{
handleSubmit
((
data
)
=>
sendPrompt
(
data
,
TextareaDom
.
current
?.
value
))();
}
}
/>
)
}
</
Flex
>
...
...
client/src/components/Markdown/chat/Guide.tsx
0 → 100644
View file @
a510f96b
import
React
,
{
useMemo
}
from
'react'
;
import
{
Box
}
from
'@chakra-ui/react'
;
import
ReactMarkdown
from
'react-markdown'
;
import
RemarkGfm
from
'remark-gfm'
;
import
RemarkMath
from
'remark-math'
;
import
RehypeKatex
from
'rehype-katex'
;
import
'katex/dist/katex.min.css'
;
import
styles
from
'../index.module.scss'
;
import
{
EventNameEnum
}
from
'../constant'
;
const
Guide
=
({
text
,
onClick
}:
{
text
:
string
;
onClick
?:
(
e
:
any
)
=>
void
})
=>
{
const
formatText
=
useMemo
(()
=>
text
.
replace
(
/
\[(
.*
?)\]
/g
,
'[$1]()'
),
[
text
]);
return
(
<
ReactMarkdown
className=
{
`markdown ${styles.markdown}`
}
remarkPlugins=
{
[
RemarkGfm
,
RemarkMath
]
}
rehypePlugins=
{
[
RehypeKatex
]
}
components=
{
{
a
({
children
}:
any
)
{
return
(
<
Box
as=
{
'li'
}
py=
{
1
}
m=
{
0
}
>
<
Box
as=
{
'span'
}
color=
{
'blue.600'
}
textDecoration=
{
'underline'
}
cursor=
{
'pointer'
}
onClick=
{
()
=>
{
if
(
!
onClick
)
return
;
onClick
({
event
:
EventNameEnum
.
guideClick
,
data
:
String
(
children
)
});
}
}
>
{
String
(
children
)
}
</
Box
>
</
Box
>
);
}
}
}
>
{
formatText
}
</
ReactMarkdown
>
);
};
export
default
React
.
memo
(
Guide
);
client/src/components/Markdown/constant.ts
0 → 100644
View file @
a510f96b
export
enum
EventNameEnum
{
guideClick
=
'guideClick'
}
client/src/components/Markdown/index.tsx
View file @
a510f96b
import
React
from
'react'
;
import
React
,
{
useRef
}
from
'react'
;
import
ReactMarkdown
from
'react-markdown'
;
import
RemarkGfm
from
'remark-gfm'
;
import
RemarkMath
from
'remark-math'
;
...
...
@@ -7,19 +7,27 @@ import RemarkBreaks from 'remark-breaks';
import
'katex/dist/katex.min.css'
;
import
styles
from
'./index.module.scss'
;
import
dynamic
from
'next/dynamic'
;
import
Link
from
'./Link'
;
import
CodeLight
from
'./CodeLight'
;
import
MermaidCodeBlock
from
'./img/MermaidCodeBlock'
;
import
MdImage
from
'./img/Image'
;
function
Code
({
inline
,
className
,
children
}:
any
)
{
const
MermaidCodeBlock
=
dynamic
(()
=>
import
(
'./img/MermaidCodeBlock'
));
const
MdImage
=
dynamic
(()
=>
import
(
'./img/Image'
));
const
ChatGuide
=
dynamic
(()
=>
import
(
'./chat/Guide'
));
function
Code
({
inline
,
className
,
children
,
onClick
}:
any
)
{
const
match
=
/language-
(\w
+
)
/
.
exec
(
className
||
''
);
const
codeType
=
match
?.[
1
];
if
(
match
?.[
1
]
===
'mermaid'
)
{
if
(
codeType
===
'mermaid'
)
{
return
<
MermaidCodeBlock
code=
{
String
(
children
)
}
/>;
}
if
(
codeType
===
'guide'
)
{
return
<
ChatGuide
text=
{
String
(
children
)
}
onClick=
{
onClick
}
/>;
}
return
(
<
CodeLight
className=
{
className
}
inline=
{
inline
}
match=
{
match
}
>
{
children
}
...
...
@@ -31,7 +39,23 @@ function Image({ src }: { src?: string }) {
return
<
MdImage
src=
{
src
}
/>;
}
const
Markdown
=
({
source
,
isChatting
=
false
}:
{
source
:
string
;
isChatting
?:
boolean
})
=>
{
const
Markdown
=
({
source
,
isChatting
=
false
,
onClick
}:
{
source
:
string
;
isChatting
?:
boolean
;
onClick
?:
(
e
:
any
)
=>
void
;
})
=>
{
const
components
=
useRef
({
a
:
Link
,
img
:
Image
,
pre
:
'div'
,
p
:
'div'
,
code
:
(
props
:
any
)
=>
<
Code
{
...
props
}
onClick=
{
onClick
}
/>
});
return
(
<
ReactMarkdown
className=
{
`markdown ${styles.markdown}
...
...
@@ -39,13 +63,8 @@ const Markdown = ({ source, isChatting = false }: { source: string; isChatting?:
`
}
remarkPlugins=
{
[
RemarkGfm
,
RemarkMath
,
RemarkBreaks
]
}
rehypePlugins=
{
[
RehypeKatex
]
}
components=
{
{
a
:
Link
,
img
:
Image
,
pre
:
'div'
,
p
:
'div'
,
code
:
Code
}
}
// @ts-ignore
components=
{
components
.
current
}
>
{
source
}
</
ReactMarkdown
>
...
...
client/src/pages/app/detail/components/Settings.tsx
View file @
a510f96b
...
...
@@ -28,7 +28,7 @@ const Settings = ({ appId }: { appId: string }) => {
content
:
'确认删除该应用?'
});
const
[
settingAppInfo
,
setSettingAppInfo
]
=
useState
<
AppSchema
>
();
const
[
fullScreen
,
setFullScreen
]
=
useState
(
tru
e
);
const
[
fullScreen
,
setFullScreen
]
=
useState
(
fals
e
);
/* 点击删除 */
const
handleDelModel
=
useCallback
(
async
()
=>
{
...
...
client/src/pages/app/detail/components/edit/components/Nodes/NodeUserGuide.tsx
View file @
a510f96b
...
...
@@ -12,7 +12,7 @@ const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6);
import
MyTooltip
from
'@/components/MyTooltip'
;
const
welcomePlaceholder
=
'每次对话开始前,发送一个初始内容。
可使用的特殊
标记:\n[快捷按键]: 用户点击后可以直接发送该问题'
;
'每次对话开始前,发送一个初始内容。
支持标准 Markdown 语法,可使用的额外
标记:\n[快捷按键]: 用户点击后可以直接发送该问题'
;
const
NodeUserGuide
=
({
data
:
{
inputs
,
outputs
,
onChangeNode
,
...
props
}
...
...
client/src/pages/chat/components/ToolMenu.tsx
View file @
a510f96b
...
...
@@ -3,15 +3,23 @@ import { useChatBox } from '@/components/ChatBox';
import
{
ChatItemType
}
from
'@/types/chat'
;
import
{
Menu
,
MenuButton
,
MenuList
,
MenuItem
,
Box
}
from
'@chakra-ui/react'
;
import
MyIcon
from
'@/components/Icon'
;
import
{
useRouter
}
from
'next/router'
;
const
ToolMenu
=
({
history
}:
{
history
:
ChatItemType
[]
})
=>
{
const
{
onExportChat
}
=
useChatBox
();
const
router
=
useRouter
();
const
menuList
=
useRef
([
// {
// icon: 'shareLight',
// label: '分享对话',
// onClick: () => {}
// },
{
icon
:
'chatLight'
,
label
:
'新对话'
,
onClick
:
()
=>
{
router
.
push
({
query
:
{
appId
:
router
.
query
?.
appId
}
});
}
},
{
icon
:
'apiLight'
,
label
:
'HTML导出'
,
...
...
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