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
37c3b897
Unverified
Commit
37c3b897
authored
May 19, 2023
by
crazywoola
Committed by
GitHub
May 19, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Feature/add emoji (#103)
parent
f68b05d5
Changes
22
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
350 additions
and
74 deletions
+350
-74
layout.tsx
...p/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx
+1
-1
AppCard.tsx
web/app/(commonLayout)/apps/AppCard.tsx
+1
-1
Apps.tsx
web/app/(commonLayout)/apps/Apps.tsx
+1
-0
NewAppCard.tsx
web/app/(commonLayout)/apps/NewAppCard.tsx
+0
-1
NewAppDialog.tsx
web/app/(commonLayout)/apps/NewAppDialog.tsx
+24
-4
layout.tsx
...ut)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx
+2
-0
basic.tsx
web/app/components/app-sidebar/basic.tsx
+11
-5
index.tsx
web/app/components/app-sidebar/index.tsx
+4
-3
appCard.tsx
web/app/components/app/overview/appCard.tsx
+2
-4
index.tsx
web/app/components/base/app-icon/index.tsx
+10
-1
index.tsx
web/app/components/base/emoji-picker/index.tsx
+204
-0
style.module.css
web/app/components/base/emoji-picker/style.module.css
+12
-0
index.tsx
web/app/components/base/modal/index.tsx
+45
-45
index.tsx
web/app/components/header/index.tsx
+8
-4
index.tsx
web/app/components/header/nav/nav-selector/index.tsx
+4
-2
index.tsx
web/app/components/share/chat/index.tsx
+2
-0
header.tsx
web/app/components/share/header.tsx
+5
-1
datasets.ts
web/models/datasets.ts
+2
-0
share.ts
web/models/share.ts
+2
-0
package.json
web/package.json
+2
-0
apps.ts
web/service/apps.ts
+2
-2
app.ts
web/types/app.ts
+6
-0
No files found.
web/app/(commonLayout)/app/(appDetailLayout)/[appId]/layout.tsx
View file @
37c3b897
...
...
@@ -49,7 +49,7 @@ const AppDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
return
null
return
(
<
div
className=
{
cn
(
s
.
app
,
'flex'
,
'overflow-hidden'
)
}
>
<
AppSideBar
title=
{
response
.
name
}
desc=
{
appModeName
}
navigation=
{
navigation
}
/>
<
AppSideBar
title=
{
response
.
name
}
icon=
{
response
.
icon
}
icon_background=
{
response
.
icon_background
}
desc=
{
appModeName
}
navigation=
{
navigation
}
/>
<
div
className=
"bg-white grow"
>
{
children
}
</
div
>
</
div
>
)
...
...
web/app/(commonLayout)/apps/AppCard.tsx
View file @
37c3b897
...
...
@@ -47,7 +47,7 @@ const AppCard = ({
<>
<
Link
href=
{
`/app/${app.id}/overview`
}
className=
{
style
.
listItem
}
>
<
div
className=
{
style
.
listItemTitle
}
>
<
AppIcon
size=
'small'
/>
<
AppIcon
size=
'small'
icon=
{
app
.
icon
}
background=
{
app
.
icon_background
}
/>
<
div
className=
{
style
.
listItemHeading
}
>
<
div
className=
{
style
.
listItemHeadingContent
}
>
{
app
.
name
}
</
div
>
</
div
>
...
...
web/app/(commonLayout)/apps/Apps.tsx
View file @
37c3b897
...
...
@@ -17,6 +17,7 @@ const Apps = () => {
{
apps
.
map
(
app
=>
(<
AppCard
key=
{
app
.
id
}
app=
{
app
}
/>))
}
<
NewAppCard
/>
</
nav
>
)
}
...
...
web/app/(commonLayout)/apps/NewAppCard.tsx
View file @
37c3b897
...
...
@@ -9,7 +9,6 @@ import NewAppDialog from './NewAppDialog'
const
CreateAppCard
=
()
=>
{
const
{
t
}
=
useTranslation
()
const
[
showNewAppDialog
,
setShowNewAppDialog
]
=
useState
(
false
)
return
(
<
a
className=
{
classNames
(
style
.
listItem
,
style
.
newItemCard
)
}
onClick=
{
()
=>
setShowNewAppDialog
(
true
)
}
>
<
div
className=
{
style
.
listItemTitle
}
>
...
...
web/app/(commonLayout)/apps/NewAppDialog.tsx
View file @
37c3b897
...
...
@@ -17,6 +17,8 @@ import { createApp, fetchAppTemplates } from '@/service/apps'
import
AppIcon
from
'@/app/components/base/app-icon'
import
AppsContext
from
'@/context/app-context'
import
EmojiPicker
from
'@/app/components/base/emoji-picker'
type
NewAppDialogProps
=
{
show
:
boolean
onClose
?:
()
=>
void
...
...
@@ -31,6 +33,11 @@ const NewAppDialog = ({ show, onClose }: NewAppDialogProps) => {
const
[
newAppMode
,
setNewAppMode
]
=
useState
<
AppMode
>
()
const
[
isWithTemplate
,
setIsWithTemplate
]
=
useState
(
false
)
const
[
selectedTemplateIndex
,
setSelectedTemplateIndex
]
=
useState
<
number
>
(
-
1
)
// Emoji Picker
const
[
showEmojiPicker
,
setShowEmojiPicker
]
=
useState
(
false
)
const
[
emoji
,
setEmoji
]
=
useState
({
icon
:
'🍌'
,
icon_background
:
'#FFEAD5'
})
const
mutateApps
=
useContextSelector
(
AppsContext
,
state
=>
state
.
mutateApps
)
const
{
data
:
templates
,
mutate
}
=
useSWR
({
url
:
'/app-templates'
},
fetchAppTemplates
)
...
...
@@ -67,6 +74,8 @@ const NewAppDialog = ({ show, onClose }: NewAppDialogProps) => {
try
{
const
app
=
await
createApp
({
name
,
icon
:
emoji
.
icon
,
icon_background
:
emoji
.
icon_background
,
mode
:
isWithTemplate
?
templates
.
data
[
selectedTemplateIndex
].
mode
:
newAppMode
!
,
config
:
isWithTemplate
?
templates
.
data
[
selectedTemplateIndex
].
model_config
:
undefined
,
})
...
...
@@ -80,9 +89,20 @@ const NewAppDialog = ({ show, onClose }: NewAppDialogProps) => {
notify
({
type
:
'error'
,
message
:
t
(
'app.newApp.appCreateFailed'
)
})
}
isCreatingRef
.
current
=
false
},
[
isWithTemplate
,
newAppMode
,
notify
,
router
,
templates
,
selectedTemplateIndex
])
},
[
isWithTemplate
,
newAppMode
,
notify
,
router
,
templates
,
selectedTemplateIndex
,
emoji
])
return
(
return
<>
{
showEmojiPicker
&&
<
EmojiPicker
onSelect=
{
(
icon
,
icon_background
)
=>
{
console
.
log
(
icon
,
icon_background
)
setEmoji
({
icon
,
icon_background
})
setShowEmojiPicker
(
false
)
}
}
onClose=
{
()
=>
{
setEmoji
({
icon
:
'🍌'
,
icon_background
:
'#FFEAD5'
})
setShowEmojiPicker
(
false
)
}
}
/>
}
<
Dialog
show=
{
show
}
title=
{
t
(
'app.newApp.startToCreate'
)
}
...
...
@@ -96,7 +116,7 @@ const NewAppDialog = ({ show, onClose }: NewAppDialogProps) => {
<
h3
className=
{
style
.
newItemCaption
}
>
{
t
(
'app.newApp.captionName'
)
}
</
h3
>
<
div
className=
'flex items-center justify-between gap-3 mb-8'
>
<
AppIcon
size=
'large'
/>
<
AppIcon
size=
'large'
onClick=
{
()
=>
{
setShowEmojiPicker
(
true
)
}
}
className=
'cursor-pointer'
icon=
{
emoji
.
icon
}
background=
{
emoji
.
icon_background
}
/>
<
input
ref=
{
nameInputRef
}
className=
'h-10 px-3 text-sm font-normal bg-gray-100 rounded-lg grow'
/>
</
div
>
...
...
@@ -187,7 +207,7 @@ const NewAppDialog = ({ show, onClose }: NewAppDialogProps) => {
)
}
</
div
>
</
Dialog
>
)
</>
}
export
default
NewAppDialog
web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout.tsx
View file @
37c3b897
...
...
@@ -155,6 +155,8 @@ const DatasetDetailLayout: FC<IAppDetailLayoutProps> = (props) => {
<
div
className=
'flex'
style=
{
{
height
:
'calc(100vh - 56px)'
}
}
>
{
!
hideSideBar
&&
<
AppSideBar
title=
{
datasetRes
?.
name
||
'--'
}
icon=
{
datasetRes
?.
icon
||
'https://static.dify.ai/images/dataset-default-icon.png'
}
icon_background=
{
datasetRes
?.
icon_background
||
'#F5F5F5'
}
desc=
{
datasetRes
?.
description
||
'--'
}
navigation=
{
navigation
}
extraInfo=
{
<
ExtraInfo
/>
}
...
...
web/app/components/app-sidebar/basic.tsx
View file @
37c3b897
...
...
@@ -15,7 +15,8 @@ export function randomString(length: number) {
export
type
IAppBasicProps
=
{
iconType
?:
'app'
|
'api'
|
'dataset'
iconUrl
?:
string
icon
?:
string
,
icon_background
?:
string
,
name
:
string
type
:
string
|
React
.
ReactNode
hoverTip
?:
string
...
...
@@ -41,15 +42,20 @@ const ICON_MAP = {
'dataset'
:
<
AppIcon
innerIcon
=
{
DatasetSvg
}
className
=
'!border-[0.5px] !border-indigo-100 !bg-indigo-25'
/>
}
export
default
function
AppBasic
({
icon
Url
,
name
,
type
,
hoverTip
,
textStyle
,
iconType
=
'app'
}:
IAppBasicProps
)
{
export
default
function
AppBasic
({
icon
,
icon_background
,
name
,
type
,
hoverTip
,
textStyle
,
iconType
=
'app'
}:
IAppBasicProps
)
{
return
(
<
div
className=
"flex items-start"
>
{
icon
Url
&&
(
{
icon
&&
icon_background
&&
iconType
===
'app'
&&
(
<
div
className=
'flex-shrink-0 mr-3'
>
{
/* <img className="inline-block rounded-lg h-9 w-9" src={iconUrl} alt={name} /> */
}
{
ICON_MAP
[
iconType
]
}
<
AppIcon
icon=
{
icon
}
background=
{
icon_background
}
/>
</
div
>
)
}
{
iconType
!==
'app'
&&
<
div
className=
'flex-shrink-0 mr-3'
>
{
ICON_MAP
[
iconType
]
}
</
div
>
}
<
div
className=
"group"
>
<
div
className=
{
`flex flex-row items-center text-sm font-semibold text-gray-700 group-hover:text-gray-900 ${textStyle?.main}`
}
>
{
name
}
...
...
web/app/components/app-sidebar/index.tsx
View file @
37c3b897
...
...
@@ -7,6 +7,8 @@ export type IAppDetailNavProps = {
iconType
?:
'app'
|
'dataset'
title
:
string
desc
:
string
icon
:
string
icon_background
:
string
navigation
:
Array
<
{
name
:
string
href
:
string
...
...
@@ -16,13 +18,12 @@ export type IAppDetailNavProps = {
extraInfo
?:
React
.
ReactNode
}
const
sampleAppIconUrl
=
'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80'
const
AppDetailNav
:
FC
<
IAppDetailNavProps
>
=
({
title
,
desc
,
navigation
,
extraInfo
,
iconType
=
'app'
})
=>
{
const
AppDetailNav
:
FC
<
IAppDetailNavProps
>
=
({
title
,
desc
,
icon
,
icon_background
,
navigation
,
extraInfo
,
iconType
=
'app'
})
=>
{
return
(
<
div
className=
"flex flex-col w-56 overflow-y-auto bg-white border-r border-gray-200 shrink-0"
>
<
div
className=
"flex flex-shrink-0 p-4"
>
<
AppBasic
iconType=
{
iconType
}
icon
Url=
{
sampleAppIconUrl
}
name=
{
title
}
type=
{
desc
}
/>
<
AppBasic
iconType=
{
iconType
}
icon
=
{
icon
}
icon_background=
{
icon_background
}
name=
{
title
}
type=
{
desc
}
/>
</
div
>
<
nav
className=
"flex-1 p-4 space-y-1 bg-white"
>
{
navigation
.
map
((
item
,
index
)
=>
{
...
...
web/app/components/app/overview/appCard.tsx
View file @
37c3b897
...
...
@@ -29,9 +29,6 @@ export type IAppCardProps = {
onGenerateCode
?:
()
=>
Promise
<
any
>
}
// todo: get image url from appInfo
const
defaultUrl
=
'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80'
function
AppCard
({
appInfo
,
cardType
=
'app'
,
...
...
@@ -104,7 +101,8 @@ function AppCard({
<
div
className=
"mb-2.5 flex flex-row items-start justify-between"
>
<
AppBasic
iconType=
{
isApp
?
'app'
:
'api'
}
iconUrl=
{
defaultUrl
}
icon=
{
appInfo
.
icon
}
icon_background=
{
appInfo
.
icon_background
}
name=
{
basicName
}
type=
{
isApp
...
...
web/app/components/base/app-icon/index.tsx
View file @
37c3b897
...
...
@@ -2,6 +2,11 @@ import type { FC } from 'react'
import
classNames
from
'classnames'
import
style
from
'./style.module.css'
import
data
from
'@emoji-mart/data'
import
{
init
}
from
'emoji-mart'
init
({
data
})
export
type
AppIconProps
=
{
size
?:
'tiny'
|
'small'
|
'medium'
|
'large'
rounded
?:
boolean
...
...
@@ -9,14 +14,17 @@ export type AppIconProps = {
background
?:
string
className
?:
string
innerIcon
?:
React
.
ReactNode
onClick
?:
()
=>
void
}
const
AppIcon
:
FC
<
AppIconProps
>
=
({
size
=
'medium'
,
rounded
=
false
,
icon
,
background
,
className
,
innerIcon
,
onClick
,
})
=>
{
return
(
<
span
...
...
@@ -29,8 +37,9 @@ const AppIcon: FC<AppIconProps> = ({
style=
{
{
background
,
}
}
onClick=
{
onClick
}
>
{
innerIcon
?
innerIcon
:
<>
🤖
<
/>
}
{
innerIcon
?
innerIcon
:
icon
&&
icon
!==
''
?
<
em
-
emoji
id=
{
icon
}
/>
:
<
em
-
emoji
id=
{
'banana'
}
/>
}
</
span
>
)
}
...
...
web/app/components/base/emoji-picker/index.tsx
0 → 100644
View file @
37c3b897
'use client'
import
data
from
'@emoji-mart/data'
import
{
init
,
SearchIndex
}
from
'emoji-mart'
// import AppIcon from '@/app/components/base/app-icon'
import
cn
from
'classnames'
import
Divider
from
'@/app/components/base/divider'
import
Button
from
'@/app/components/base/button'
import
s
from
'./style.module.css'
import
{
useState
,
FC
,
ChangeEvent
}
from
'react'
import
{
MagnifyingGlassIcon
}
from
'@heroicons/react/24/outline'
import
React
from
'react'
import
Modal
from
'@/app/components/base/modal'
declare
global
{
namespace
JSX
{
interface
IntrinsicElements
{
'em-emoji'
:
React
.
DetailedHTMLProps
<
React
.
HTMLAttributes
<
HTMLElement
>
,
HTMLElement
>
;
}
}
}
init
({
data
})
async
function
search
(
value
:
string
)
{
const
emojis
=
await
SearchIndex
.
search
(
value
)
||
[]
const
results
=
emojis
.
map
((
emoji
:
any
)
=>
{
return
emoji
.
skins
[
0
].
native
})
return
results
}
const
backgroundColors
=
[
'#FFEAD5'
,
'#E4FBCC'
,
'#D3F8DF'
,
'#E0F2FE'
,
'#E0EAFF'
,
'#EFF1F5'
,
'#FBE8FF'
,
'#FCE7F6'
,
'#FEF7C3'
,
'#E6F4D7'
,
'#D5F5F6'
,
'#D1E9FF'
,
'#D1E0FF'
,
'#D5D9EB'
,
'#ECE9FE'
,
'#FFE4E8'
,
]
interface
IEmojiPickerProps
{
isModal
?:
boolean
onSelect
?:
(
emoji
:
string
,
background
:
string
)
=>
void
onClose
?:
()
=>
void
}
const
EmojiPicker
:
FC
<
IEmojiPickerProps
>
=
({
isModal
=
true
,
onSelect
,
onClose
})
=>
{
const
{
categories
}
=
data
as
any
const
[
selectedEmoji
,
setSelectedEmoji
]
=
useState
(
''
)
const
[
selectedBackground
,
setSelectedBackground
]
=
useState
(
backgroundColors
[
0
])
const
[
searchedEmojis
,
setSearchedEmojis
]
=
useState
([])
const
[
isSearching
,
setIsSearching
]
=
useState
(
false
)
return
isModal
?
<
Modal
onClose=
{
()
=>
{
}
}
isShow
closable=
{
false
}
className=
{
cn
(
s
.
container
,
'!w-[362px] !p-0'
)
}
>
<
div
className=
'flex flex-col items-center w-full p-3'
>
<
div
className=
"relative w-full"
>
<
div
className=
"absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none"
>
<
MagnifyingGlassIcon
className=
"w-5 h-5 text-gray-400"
aria
-
hidden=
"true"
/>
</
div
>
<
input
type=
"search"
id=
"search"
className=
'block w-full h-10 px-3 pl-10 text-sm font-normal bg-gray-100 rounded-lg'
placeholder=
"Search emojis..."
onChange=
{
async
(
e
:
ChangeEvent
<
HTMLInputElement
>
)
=>
{
if
(
e
.
target
.
value
===
''
)
{
setIsSearching
(
false
)
return
}
else
{
setIsSearching
(
true
)
const
emojis
=
await
search
(
e
.
target
.
value
)
setSearchedEmojis
(
emojis
)
}
}
}
/>
</
div
>
</
div
>
<
Divider
className=
'm-0 mb-3'
/>
<
div
className=
"w-full max-h-[200px] overflow-x-hidden overflow-y-auto px-3"
>
{
isSearching
&&
<>
<
div
key=
{
`category-search`
}
className=
'flex flex-col'
>
<
p
className=
'font-medium uppercase text-xs text-[#101828] mb-1'
>
Search
</
p
>
<
div
className=
'w-full h-full grid grid-cols-8 gap-1'
>
{
searchedEmojis
.
map
((
emoji
:
string
,
index
:
number
)
=>
{
return
<
div
key=
{
`emoji-search-${index}`
}
className=
'inline-flex w-10 h-10 rounded-lg items-center justify-center'
onClick=
{
()
=>
{
setSelectedEmoji
(
emoji
)
}
}
>
<
div
className=
'cursor-pointer w-8 h-8 p-1 flex items-center justify-center rounded-lg hover:ring-1 ring-offset-1 ring-gray-300'
>
<
em
-
emoji
id=
{
emoji
}
/>
</
div
>
</
div
>
})
}
</
div
>
</
div
>
</>
}
{
categories
.
map
((
category
:
any
,
index
:
number
)
=>
{
return
<
div
key=
{
`category-${index}`
}
className=
'flex flex-col'
>
<
p
className=
'font-medium uppercase text-xs text-[#101828] mb-1'
>
{
category
.
id
}
</
p
>
<
div
className=
'w-full h-full grid grid-cols-8 gap-1'
>
{
category
.
emojis
.
map
((
emoji
:
string
,
index
:
number
)
=>
{
return
<
div
key=
{
`emoji-${index}`
}
className=
'inline-flex w-10 h-10 rounded-lg items-center justify-center'
onClick=
{
()
=>
{
setSelectedEmoji
(
emoji
)
}
}
>
<
div
className=
'cursor-pointer w-8 h-8 p-1 flex items-center justify-center rounded-lg hover:ring-1 ring-offset-1 ring-gray-300'
>
<
em
-
emoji
id=
{
emoji
}
/>
</
div
>
</
div
>
})
}
</
div
>
</
div
>
})
}
</
div
>
{
/* Color Select */
}
<
div
className=
{
cn
(
'flex flex-col p-3 '
,
selectedEmoji
==
''
?
'opacity-25'
:
''
)
}
>
<
p
className=
'font-medium uppercase text-xs text-[#101828] mb-2'
>
Choose Style
</
p
>
<
div
className=
'w-full h-full grid grid-cols-8 gap-1'
>
{
backgroundColors
.
map
((
color
)
=>
{
return
<
div
key=
{
color
}
className=
{
cn
(
'cursor-pointer'
,
`hover:ring-1 ring-offset-1`
,
'inline-flex w-10 h-10 rounded-lg items-center justify-center'
,
color
===
selectedBackground
?
`ring-1 ring-gray-300`
:
''
,
)
}
onClick=
{
()
=>
{
setSelectedBackground
(
color
)
}
}
>
<
div
className=
{
cn
(
'w-8 h-8 p-1 flex items-center justify-center rounded-lg'
,
)
}
style=
{
{
background
:
color
}
}
>
{
selectedEmoji
!==
''
&&
<
em
-
emoji
id=
{
selectedEmoji
}
/>
}
</
div
>
</
div
>
})
}
</
div
>
</
div
>
<
Divider
className=
'm-0'
/>
<
div
className=
'w-full flex items-center justify-center p-3 gap-2'
>
<
Button
type=
"default"
className=
'w-full'
onClick=
{
()
=>
{
onClose
&&
onClose
()
}
}
>
Cancel
</
Button
>
<
Button
disabled=
{
selectedEmoji
==
''
}
type=
"primary"
className=
'w-full'
onClick=
{
()
=>
{
onSelect
&&
onSelect
(
selectedEmoji
,
selectedBackground
)
}
}
>
OK
</
Button
>
</
div
>
</
Modal
>
:
<>
</>
}
export
default
EmojiPicker
web/app/components/base/emoji-picker/style.module.css
0 → 100644
View file @
37c3b897
.container
{
display
:
flex
;
flex-direction
:
column
;
align-items
:
flex-start
;
width
:
362px
;
max-height
:
552px
;
border
:
0.5px
solid
#EAECF0
;
box-shadow
:
0px
12px
16px
-4px
rgba
(
16
,
24
,
40
,
0.08
),
0px
4px
6px
-2px
rgba
(
16
,
24
,
40
,
0.03
);
border-radius
:
12px
;
background
:
#fff
;
}
web/app/components/base/modal/index.tsx
View file @
37c3b897
...
...
@@ -25,51 +25,51 @@ export default function Modal({
closable
=
false
,
}:
IModal
)
{
return
(
<
Transition
appear
show=
{
isShow
}
as=
{
Fragment
}
>
<
Dialog
as=
"div"
className=
"relative z-10"
onClose=
{
onClose
}
>
<
Transition
.
Child
as=
{
Fragment
}
enter=
"ease-out duration-300"
enterFrom=
"opacity-0"
enterTo=
"opacity-100"
leave=
"ease-in duration-200"
leaveFrom=
"opacity-100"
leaveTo=
"opacity-0"
>
<
div
className=
"fixed inset-0 bg-black bg-opacity-25"
/>
</
Transition
.
Child
>
<
Transition
appear
show=
{
isShow
}
as=
{
Fragment
}
>
<
Dialog
as=
"div"
className=
"relative z-10"
onClose=
{
onClose
}
>
<
Transition
.
Child
as=
{
Fragment
}
enter=
"ease-out duration-300"
enterFrom=
"opacity-0"
enterTo=
"opacity-100"
leave=
"ease-in duration-200"
leaveFrom=
"opacity-100"
leaveTo=
"opacity-0"
>
<
div
className=
"fixed inset-0 bg-black bg-opacity-25"
/>
</
Transition
.
Child
>
<
div
className=
"fixed inset-0 overflow-y-auto"
>
<
div
className=
{
`flex min-h-full items-center justify-center p-4 text-center ${wrapperClassName}`
}
>
<
Transition
.
Child
as=
{
Fragment
}
enter=
"ease-out duration-300"
enterFrom=
"opacity-0 scale-95"
enterTo=
"opacity-100 scale-100"
leave=
"ease-in duration-200"
leaveFrom=
"opacity-100 scale-100"
leaveTo=
"opacity-0 scale-95"
>
<
Dialog
.
Panel
className=
{
`w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all ${className}`
}
>
{
title
&&
<
Dialog
.
Title
as=
"h3"
className=
"text-lg font-medium leading-6 text-gray-900"
>
{
title
}
</
Dialog
.
Title
>
}
{
description
&&
<
Dialog
.
Description
className=
'text-gray-500 text-xs font-normal mt-2'
>
{
description
}
</
Dialog
.
Description
>
}
{
closable
&&
<
div
className=
'absolute top-6 right-6 w-5 h-5 rounded-2xl flex items-center justify-center hover:cursor-pointer hover:bg-gray-100'
>
<
XMarkIcon
className=
'w-4 h-4 text-gray-500'
onClick=
{
onClose
}
/>
</
div
>
}
{
children
}
</
Dialog
.
Panel
>
</
Transition
.
Child
>
</
div
>
</
div
>
</
Dialog
>
</
Transition
>
<
div
className=
"fixed inset-0 overflow-y-auto"
>
<
div
className=
"flex min-h-full items-center justify-center p-4 text-center"
>
<
Transition
.
Child
as=
{
Fragment
}
enter=
"ease-out duration-300"
enterFrom=
"opacity-0 scale-95"
enterTo=
"opacity-100 scale-100"
leave=
"ease-in duration-200"
leaveFrom=
"opacity-100 scale-100"
leaveTo=
"opacity-0 scale-95"
>
<
Dialog
.
Panel
className=
{
`w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all ${className}`
}
>
{
title
&&
<
Dialog
.
Title
as=
"h3"
className=
"text-lg font-medium leading-6 text-gray-900"
>
{
title
}
</
Dialog
.
Title
>
}
{
description
&&
<
Dialog
.
Description
className=
'text-gray-500 text-xs font-normal mt-2'
>
{
description
}
</
Dialog
.
Description
>
}
{
closable
&&
<
div
className=
'absolute top-6 right-6 w-5 h-5 rounded-2xl flex items-center justify-center hover:cursor-pointer hover:bg-gray-100'
>
<
XMarkIcon
className=
'w-4 h-4 text-gray-500'
onClick=
{
onClose
}
/>
</
div
>
}
{
children
}
</
Dialog
.
Panel
>
</
Transition
.
Child
>
</
div
>
</
div
>
</
Dialog
>
</
Transition
>
)
}
web/app/components/header/index.tsx
View file @
37c3b897
...
...
@@ -69,11 +69,13 @@ const Header: FC<IHeaderProps> = ({ appItems, curApp, userProfile, onLogout, lan
text=
{
t
(
'common.menus.apps'
)
}
activeSegment=
{
[
'apps'
,
'app'
]
}
link=
'/apps'
curNav=
{
curApp
&&
{
id
:
curApp
.
id
,
name
:
curApp
.
name
}
}
curNav=
{
curApp
&&
{
id
:
curApp
.
id
,
name
:
curApp
.
name
,
icon
:
curApp
.
icon
,
icon_background
:
curApp
.
icon_background
}
}
navs=
{
appItems
.
map
(
item
=>
({
id
:
item
.
id
,
name
:
item
.
name
,
link
:
`/app/${item.id}/overview`
link
:
`/app/${item.id}/overview`
,
icon
:
item
.
icon
,
icon_background
:
item
.
icon_background
}))
}
createText=
{
t
(
'common.menus.newApp'
)
}
onCreate=
{
()
=>
setShowNewAppDialog
(
true
)
}
...
...
@@ -91,11 +93,13 @@ const Header: FC<IHeaderProps> = ({ appItems, curApp, userProfile, onLogout, lan
text=
{
t
(
'common.menus.datasets'
)
}
activeSegment=
'datasets'
link=
'/datasets'
curNav=
{
currentDataset
&&
{
id
:
currentDataset
.
id
,
name
:
currentDataset
.
name
}
}
curNav=
{
currentDataset
&&
{
id
:
currentDataset
.
id
,
name
:
currentDataset
.
name
,
icon
:
currentDataset
.
icon
,
icon_background
:
currentDataset
.
icon_background
}
}
navs=
{
datasets
.
map
(
dataset
=>
({
id
:
dataset
.
id
,
name
:
dataset
.
name
,
link
:
`/datasets/${dataset.id}/documents`
link
:
`/datasets/${dataset.id}/documents`
,
icon
:
dataset
.
icon
,
icon_background
:
dataset
.
icon_background
}))
}
createText=
{
t
(
'common.menus.newDataset'
)
}
onCreate=
{
()
=>
router
.
push
(
'/datasets/create'
)
}
...
...
web/app/components/header/nav/nav-selector/index.tsx
View file @
37c3b897
...
...
@@ -10,6 +10,8 @@ type NavItem = {
id
:
string
name
:
string
link
:
string
icon
:
string
icon_background
:
string
}
export
interface
INavSelectorProps
{
navs
:
NavItem
[]
...
...
@@ -66,7 +68,7 @@ const NavSelector = ({ curNav, navs, createText, onCreate }: INavSelectorProps)
<
Menu
.
Item
key=
{
nav
.
id
}
>
<
div
className=
{
itemClassName
}
onClick=
{
()
=>
router
.
push
(
nav
.
link
)
}
>
<
div
className=
'relative w-6 h-6 mr-2 bg-[#D5F5F6] rounded-[6px]'
>
<
AppIcon
size=
'tiny'
/>
<
AppIcon
size=
'tiny'
icon=
{
nav
.
icon
}
background=
{
nav
.
icon_background
}
/>
<
div
className=
'flex justify-center items-center absolute -right-0.5 -bottom-0.5 w-2.5 h-2.5 bg-white rounded'
>
<
Indicator
/>
</
div
>
...
...
@@ -102,4 +104,4 @@ const NavSelector = ({ curNav, navs, createText, onCreate }: INavSelectorProps)
)
}
export
default
NavSelector
\ No newline at end of file
export
default
NavSelector
web/app/components/share/chat/index.tsx
View file @
37c3b897
...
...
@@ -441,6 +441,8 @@ const Main: FC<IMainProps> = () => {
<
div
className=
'bg-gray-100'
>
<
Header
title=
{
siteInfo
.
title
}
icon=
{
siteInfo
.
icon
||
''
}
icon_background=
{
siteInfo
.
icon_background
||
'#FFEAD5'
}
isMobile=
{
isMobile
}
onShowSideBar=
{
showSidebar
}
onCreateNewChat=
{
()
=>
handleConversationIdChange
(
'-1'
)
}
...
...
web/app/components/share/header.tsx
View file @
37c3b897
...
...
@@ -7,6 +7,8 @@ import {
}
from
'@heroicons/react/24/solid'
export
type
IHeaderProps
=
{
title
:
string
icon
:
string
icon_background
:
string
isMobile
?:
boolean
onShowSideBar
?:
()
=>
void
onCreateNewChat
?:
()
=>
void
...
...
@@ -14,6 +16,8 @@ export type IHeaderProps = {
const
Header
:
FC
<
IHeaderProps
>
=
({
title
,
isMobile
,
icon
,
icon_background
,
onShowSideBar
,
onCreateNewChat
,
})
=>
{
...
...
@@ -28,7 +32,7 @@ const Header: FC<IHeaderProps> = ({
</
div
>
)
:
<
div
></
div
>
}
<
div
className=
'flex items-center space-x-2'
>
<
AppIcon
size=
"small"
/>
<
AppIcon
size=
"small"
icon=
{
icon
}
background=
{
icon_background
}
/>
<
div
className=
" text-sm text-gray-800 font-bold"
>
{
title
}
</
div
>
</
div
>
{
isMobile
?
(
...
...
web/models/datasets.ts
View file @
37c3b897
...
...
@@ -3,6 +3,8 @@ import { AppMode } from './app'
export
type
DataSet
=
{
id
:
string
name
:
string
icon
:
string
icon_background
:
string
description
:
string
permission
:
'only_me'
|
'all_team_members'
data_source_type
:
'upload_file'
...
...
web/models/share.ts
View file @
37c3b897
...
...
@@ -11,6 +11,8 @@ export type ConversationItem = {
export
type
SiteInfo
=
{
title
:
string
icon
:
string
icon_background
:
string
description
:
string
default_language
:
Locale
prompt_public
:
boolean
...
...
web/package.json
View file @
37c3b897
...
...
@@ -10,6 +10,7 @@
"fix"
:
"next lint --fix"
},
"dependencies"
:
{
"@emoji-mart/data"
:
"^1.1.2"
,
"@formatjs/intl-localematcher"
:
"^0.2.32"
,
"@headlessui/react"
:
"^1.7.13"
,
"@heroicons/react"
:
"^2.0.16"
,
...
...
@@ -33,6 +34,7 @@
"dayjs"
:
"^1.11.7"
,
"echarts"
:
"^5.4.1"
,
"echarts-for-react"
:
"^3.0.2"
,
"emoji-mart"
:
"^5.5.2"
,
"eslint"
:
"8.36.0"
,
"eslint-config-next"
:
"13.2.4"
,
"i18next"
:
"^22.4.13"
,
...
...
web/service/apps.ts
View file @
37c3b897
...
...
@@ -16,8 +16,8 @@ export const fetchAppTemplates: Fetcher<AppTemplatesResponse, { url: string }> =
return
get
(
url
)
as
Promise
<
AppTemplatesResponse
>
}
export
const
createApp
:
Fetcher
<
AppDetailResponse
,
{
name
:
string
;
mode
:
AppMode
;
config
?:
ModelConfig
}
>
=
({
name
,
mode
,
config
})
=>
{
return
post
(
'apps'
,
{
body
:
{
name
,
mode
,
model_config
:
config
}
})
as
Promise
<
AppDetailResponse
>
export
const
createApp
:
Fetcher
<
AppDetailResponse
,
{
name
:
string
;
icon
:
string
,
icon_background
:
string
,
mode
:
AppMode
;
config
?:
ModelConfig
}
>
=
({
name
,
icon
,
icon_background
,
mode
,
config
})
=>
{
return
post
(
'apps'
,
{
body
:
{
name
,
icon
,
icon_background
,
mode
,
model_config
:
config
}
})
as
Promise
<
AppDetailResponse
>
}
export
const
deleteApp
:
Fetcher
<
CommonResponse
,
string
>
=
(
appID
)
=>
{
...
...
web/types/app.ts
View file @
37c3b897
...
...
@@ -190,6 +190,12 @@ export type App = {
id
:
string
/** Name */
name
:
string
/** Icon */
icon
:
string
/** Icon Background */
icon_background
:
string
/** Mode */
mode
:
AppMode
/** Enable web app */
...
...
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