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
b332ed1b
Commit
b332ed1b
authored
Aug 03, 2023
by
Gillian97
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: app card view opt
parent
5d783a49
Changes
15
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
210 additions
and
160 deletions
+210
-160
basic.tsx
web/app/components/app-sidebar/basic.tsx
+1
-7
index.tsx
web/app/components/app/chat/answer/index.tsx
+1
-1
list.tsx
web/app/components/app/log/list.tsx
+1
-1
appCard.tsx
web/app/components/app/overview/appCard.tsx
+97
-65
refresh-hover.svg
web/app/components/app/overview/assets/refresh-hover.svg
+3
-0
refresh.svg
web/app/components/app/overview/assets/refresh.svg
+3
-0
share-link.tsx
web/app/components/app/overview/share-link.tsx
+0
-63
style.module.css
web/app/components/app/overview/style.module.css
+13
-0
index.tsx
web/app/components/base/copy-feedback/index.tsx
+57
-0
style.module.css
web/app/components/base/copy-feedback/style.module.css
+15
-0
input-copy.tsx
web/app/components/develop/secret-key/input-copy.tsx
+1
-1
invitation-link.tsx
...nt-setting/members-page/invited-modal/invitation-link.tsx
+1
-1
app-overview.en.ts
web/i18n/lang/app-overview.en.ts
+2
-8
app-overview.zh.ts
web/i18n/lang/app-overview.zh.ts
+2
-8
index.ts
web/utils/index.ts
+13
-5
No files found.
web/app/components/app-sidebar/basic.tsx
View file @
b332ed1b
...
...
@@ -4,13 +4,7 @@ import {
}
from
'@heroicons/react/24/outline'
import
Tooltip
from
'../base/tooltip'
import
AppIcon
from
'../base/app-icon'
const
chars
=
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'
export
function
randomString
(
length
:
number
)
{
let
result
=
''
for
(
let
i
=
length
;
i
>
0
;
--
i
)
result
+=
chars
[
Math
.
floor
(
Math
.
random
()
*
chars
.
length
)]
return
result
}
import
{
randomString
}
from
'@/utils'
export
type
IAppBasicProps
=
{
iconType
?:
'app'
|
'api'
|
'dataset'
|
'webapp'
|
'notion'
...
...
web/app/components/app/chat/answer/index.tsx
View file @
b332ed1b
...
...
@@ -6,7 +6,6 @@ import { useContext } from 'use-context-selector'
import
{
UserCircleIcon
}
from
'@heroicons/react/24/solid'
import
cn
from
'classnames'
import
type
{
DisplayScene
,
FeedbackFunc
,
Feedbacktype
,
IChatItem
,
SubmitAnnotationFunc
,
ThoughtItem
}
from
'../type'
import
{
randomString
}
from
'../../../app-sidebar/basic'
import
OperationBtn
from
'../operation'
import
LoadingAnim
from
'../loading-anim'
import
{
EditIcon
,
EditIconSolid
,
OpeningStatementIcon
,
RatingIcon
}
from
'../icon-component'
...
...
@@ -14,6 +13,7 @@ import s from '../style.module.css'
import
MoreInfo
from
'../more-info'
import
CopyBtn
from
'../copy-btn'
import
Thought
from
'../thought'
import
{
randomString
}
from
'@/utils'
import
type
{
Annotation
,
MessageRating
}
from
'@/models/log'
import
AppContext
from
'@/context/app-context'
import
Tooltip
from
'@/app/components/base/tooltip'
...
...
web/app/components/app/log/list.tsx
View file @
b332ed1b
...
...
@@ -16,8 +16,8 @@ import dayjs from 'dayjs'
import
{
createContext
,
useContext
}
from
'use-context-selector'
import
classNames
from
'classnames'
import
{
useTranslation
}
from
'react-i18next'
import
{
randomString
}
from
'../../app-sidebar/basic'
import
s
from
'./style.module.css'
import
{
randomString
}
from
'@/utils'
import
{
EditIconSolid
}
from
'@/app/components/app/chat/icon-component'
import
type
{
FeedbackFunc
,
Feedbacktype
,
IChatItem
,
SubmitAnnotationFunc
}
from
'@/app/components/app/chat/type'
import
type
{
Annotation
,
ChatConversationFullDetailResponse
,
ChatConversationGeneralDetail
,
ChatConversationsResponse
,
ChatMessage
,
ChatMessagesRequest
,
CompletionConversationFullDetailResponse
,
CompletionConversationGeneralDetail
,
CompletionConversationsResponse
}
from
'@/models/log'
...
...
web/app/components/app/overview/appCard.tsx
View file @
b332ed1b
...
...
@@ -4,29 +4,30 @@ import React, { useState } from 'react'
import
{
Cog8ToothIcon
,
DocumentTextIcon
,
PaintBrushIcon
,
RocketLaunchIcon
,
ShareIcon
,
}
from
'@heroicons/react/24/outline'
import
{
SparklesIcon
}
from
'@heroicons/react/24/solid'
import
{
usePathname
,
useRouter
}
from
'next/navigation'
import
{
useTranslation
}
from
'react-i18next'
import
SettingsModal
from
'./settings'
import
ShareLink
from
'./share-link'
import
EmbeddedModal
from
'./embedded'
import
CustomizeModal
from
'./customize'
import
style
from
'./style.module.css'
import
Tooltip
from
'@/app/components/base/tooltip'
import
AppBasic
,
{
randomString
}
from
'@/app/components/app-sidebar/basic'
import
AppBasic
from
'@/app/components/app-sidebar/basic'
import
{
asyncRunSafe
,
randomString
}
from
'@/utils'
import
Button
from
'@/app/components/base/button'
import
Tag
from
'@/app/components/base/tag'
import
Switch
from
'@/app/components/base/switch'
import
Divider
from
'@/app/components/base/divider'
import
CopyFeedback
from
'@/app/components/base/copy-feedback'
import
type
{
AppDetailResponse
}
from
'@/models/app'
import
'./style.css'
import
{
AppType
}
from
'@/types/app'
export
type
IAppCardProps
=
{
className
?:
string
appInfo
:
AppDetailResponse
cardType
?:
'ap
p'
|
'ap
i'
|
'webapp'
cardType
?:
'api'
|
'webapp'
customBgColor
?:
string
onChangeStatus
:
(
val
:
boolean
)
=>
Promise
<
any
>
onSaveSiteConfig
?:
(
params
:
any
)
=>
Promise
<
any
>
...
...
@@ -34,12 +35,12 @@ export type IAppCardProps = {
}
const
EmbedIcon
:
FC
<
{
className
?:
string
}
>
=
({
className
=
''
})
=>
{
return
<
div
className=
{
`
codeBrowserIcon
${className}`
}
></
div
>
return
<
div
className=
{
`
${style.codeBrowserIcon}
${className}`
}
></
div
>
}
function
AppCard
({
appInfo
,
cardType
=
'app'
,
cardType
=
'
web
app'
,
customBgColor
,
onChangeStatus
,
onSaveSiteConfig
,
...
...
@@ -49,24 +50,45 @@ function AppCard({
const
router
=
useRouter
()
const
pathname
=
usePathname
()
const
[
showSettingsModal
,
setShowSettingsModal
]
=
useState
(
false
)
const
[
showShareModal
,
setShowShareModal
]
=
useState
(
false
)
const
[
showEmbedded
,
setShowEmbedded
]
=
useState
(
false
)
const
[
showCustomizeModal
,
setShowCustomizeModal
]
=
useState
(
false
)
const
[
genLoading
,
setGenLoading
]
=
useState
(
false
)
const
{
t
}
=
useTranslation
()
const
OPERATIONS_MAP
=
{
webapp
:
[
{
opName
:
t
(
'appOverview.overview.appInfo.preview'
),
opIcon
:
RocketLaunchIcon
},
{
opName
:
t
(
'appOverview.overview.appInfo.share.entry'
),
opIcon
:
ShareIcon
},
appInfo
.
mode
===
AppType
.
chat
?
{
opName
:
t
(
'appOverview.overview.appInfo.embedded.entry'
),
opIcon
:
EmbedIcon
}
:
false
,
{
opName
:
t
(
'appOverview.overview.appInfo.settings.entry'
),
opIcon
:
Cog8ToothIcon
},
{
opName
:
t
(
'appOverview.overview.appInfo.preview'
),
opIcon
:
RocketLaunchIcon
,
},
appInfo
.
mode
===
AppType
.
chat
?
{
opName
:
t
(
'appOverview.overview.appInfo.embedded.entry'
),
opIcon
:
EmbedIcon
,
}
:
false
,
{
opName
:
t
(
'appOverview.overview.appInfo.customize.entry'
),
opIcon
:
PaintBrushIcon
,
},
{
opName
:
t
(
'appOverview.overview.appInfo.settings.entry'
),
opIcon
:
Cog8ToothIcon
,
},
].
filter
(
item
=>
!!
item
),
api
:
[{
opName
:
t
(
'appOverview.overview.apiInfo.doc'
),
opIcon
:
DocumentTextIcon
}],
api
:
[
{
opName
:
t
(
'appOverview.overview.apiInfo.doc'
),
opIcon
:
DocumentTextIcon
,
},
],
app
:
[],
}
const
isApp
=
cardType
===
'app'
||
cardType
===
'webapp'
const
basicName
=
isApp
?
appInfo
?.
site
?.
title
:
t
(
'appOverview.overview.apiInfo.title'
)
const
isApp
=
cardType
===
'webapp'
const
basicName
=
isApp
?
appInfo
?.
site
?.
title
:
t
(
'appOverview.overview.apiInfo.title'
)
const
runningStatus
=
isApp
?
appInfo
.
enable_site
:
appInfo
.
enable_api
const
{
app_base_url
,
access_token
}
=
appInfo
.
site
??
{}
const
appUrl
=
`
${
app_base_url
}
/
${
appInfo
.
mode
}
/
${
access_token
}
`
...
...
@@ -82,9 +104,9 @@ function AppCard({
return
()
=>
{
window
.
open
(
appUrl
,
'_blank'
)
}
case
t
(
'appOverview.overview.appInfo.
shar
e.entry'
):
case
t
(
'appOverview.overview.appInfo.
customiz
e.entry'
):
return
()
=>
{
setShow
Shar
eModal
(
true
)
setShow
Customiz
eModal
(
true
)
}
case
t
(
'appOverview.overview.appInfo.settings.entry'
):
return
()
=>
{
...
...
@@ -104,13 +126,17 @@ function AppCard({
}
}
const
onClickCustomize
=
()
=>
{
setShowCustomizeModal
(
true
)
const
onGenCode
=
async
()
=>
{
setGenLoading
(
true
)
await
asyncRunSafe
(
onGenerateCode
?.()
as
any
)
setGenLoading
(
false
)
}
return
(
<
div
className=
{
`flex flex-col w-full shadow-sm border-[0.5px] rounded-lg border-gray-200 ${className ?? ''}`
}
className=
{
`flex flex-col w-full shadow-sm border-[0.5px] rounded-lg border-gray-200 ${
className ?? ''
}`
}
>
<
div
className=
{
`px-6 py-4 ${customBgColor ?? bgColor} rounded-lg`
}
>
<
div
className=
"mb-2.5 flex flex-row items-start justify-between"
>
...
...
@@ -127,7 +153,9 @@ function AppCard({
/>
<
div
className=
"flex flex-row items-center h-9"
>
<
Tag
className=
"mr-2"
color=
{
runningStatus
?
'green'
:
'yellow'
}
>
{
runningStatus
?
t
(
'appOverview.overview.status.running'
)
:
t
(
'appOverview.overview.status.disable'
)
}
{
runningStatus
?
t
(
'appOverview.overview.status.running'
)
:
t
(
'appOverview.overview.status.disable'
)
}
</
Tag
>
<
Switch
defaultValue=
{
runningStatus
}
onChange=
{
onChangeStatus
}
/>
</
div
>
...
...
@@ -135,35 +163,62 @@ function AppCard({
<
div
className=
"flex flex-col justify-center py-2"
>
<
div
className=
"py-1"
>
<
div
className=
"pb-1 text-xs text-gray-500"
>
{
isApp
?
t
(
'appOverview.overview.appInfo.accessibleAddress'
)
:
t
(
'appOverview.overview.apiInfo.accessibleAddress'
)
}
{
isApp
?
t
(
'appOverview.overview.appInfo.accessibleAddress'
)
:
t
(
'appOverview.overview.apiInfo.accessibleAddress'
)
}
</
div
>
<
div
className=
"text-sm text-gray-800"
>
<
div
className=
"w-full h-9 pl-2 pr-0.5 py-0.5 bg-black bg-opacity-[0.02] rounded-lg border border-black border-opacity-5 justify-start items-center inline-flex"
>
<
div
className=
"h-4 px-2 justify-start items-start gap-2 flex flex-1"
>
<
div
className=
"text-gray-700 text-xs font-medium"
>
{
isApp
?
appUrl
:
apiUrl
}
</
div
>
</
div
>
</
div
>
<
Divider
type=
"vertical"
className=
"!h-3.5 shrink-0 !mx-0.5"
/>
<
CopyFeedback
content=
{
isApp
?
appUrl
:
apiUrl
}
selectorId=
{
randomString
(
8
)
}
className=
{
'hover:bg-gray-200'
}
/>
{
/* button copy link/ button regenerate */
}
{
isApp
&&
(
<
Tooltip
content=
{
t
(
'appOverview.overview.appInfo.regenerate'
)
||
''
}
selector=
{
`code-generate-${randomString(8)}`
}
>
<
div
className=
{
`pt-2 flex flex-row items-center ${!isApp ? 'mb-[calc(2rem_+_1px)]' : ''
}`
}
className=
"w-8 h-8 ml-0.5 cursor-pointer hover:bg-gray-200 rounded-lg"
onClick=
{
onGenCode
}
>
{
OPERATIONS_MAP
[
cardType
].
map
((
op
)
=>
{
<
div
className=
{
`w-full h-full ${style.refreshIcon} ${
genLoading ? style.generateLogo : ''
}`
}
></
div
>
</
div
>
</
Tooltip
>
)
}
</
div
>
</
div
>
</
div
>
<
div
className=
{
'pt-2 flex flex-row items-center'
}
>
{
OPERATIONS_MAP
[
cardType
].
map
((
op
:
any
)
=>
{
const
disabled
=
op
.
opName
===
t
(
'appOverview.overview.appInfo.settings.entry'
)
?
false
:
!
runningStatus
return
(
<
Button
className=
"mr-2
text-gray-800
"
className=
"mr-2
border-[0.5px] hover:outline hover:outline-[0.5px] hover:outline-gray-300 text-gray-700 font-medium bg-white shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)]
"
key=
{
op
.
opName
}
onClick=
{
genClickFuncByName
(
op
.
opName
)
}
disabled=
{
[
t
(
'appOverview.overview.appInfo.preview'
),
t
(
'appOverview.overview.appInfo.share.entry'
),
t
(
'appOverview.overview.appInfo.embedded.entry'
)].
includes
(
op
.
opName
)
&&
!
runningStatus
}
disabled=
{
disabled
}
>
<
Tooltip
content=
{
t
(
'appOverview.overview.appInfo.preUseReminder'
)
??
''
}
selector=
{
`op-btn-${randomString(16)}`
}
className=
{
([
t
(
'appOverview.overview.appInfo.preview'
),
t
(
'appOverview.overview.appInfo.share.entry'
),
t
(
'appOverview.overview.appInfo.embedded.entry'
)].
includes
(
op
.
opName
)
&&
!
runningStatus
)
?
'mt-[-8px]'
:
'!hidden'
content=
{
t
(
'appOverview.overview.appInfo.preUseReminder'
)
??
''
}
selector=
{
`op-btn-${randomString(16)}`
}
className=
{
disabled
?
'mt-[-8px]'
:
'!hidden'
}
>
<
div
className=
"flex flex-row items-center"
>
<
op
.
opIcon
className=
"h-4 w-4 mr-1.5 stroke-[1.8px]"
/>
...
...
@@ -177,30 +232,7 @@ function AppCard({
</
div
>
{
isApp
?
(
<
div
className=
{
'flex items-center px-6 py-2 box-border text-xs text-gray-500 bg-opacity-50 bg-white border-t-[0.5px] border-primary-50'
}
>
<
div
className=
"flex items-center hover:text-primary-600 hover:cursor-pointer"
onClick=
{
onClickCustomize
}
>
<
SparklesIcon
className=
"w-4 h-4 mr-1"
/>
{
t
(
'appOverview.overview.appInfo.customize.entry'
)
}
</
div
>
</
div
>
)
:
null
}
{
isApp
?
(
<
div
>
<
ShareLink
isShow=
{
showShareModal
}
onClose=
{
()
=>
setShowShareModal
(
false
)
}
linkUrl=
{
appUrl
}
onGenerateCode=
{
onGenerateCode
}
/>
<>
<
SettingsModal
appInfo=
{
appInfo
}
isShow=
{
showSettingsModal
}
...
...
@@ -220,7 +252,7 @@ function AppCard({
appId=
{
appInfo
.
id
}
mode=
{
appInfo
.
mode
}
/>
</
div
>
</>
)
:
null
}
</
div
>
...
...
web/app/components/app/overview/assets/refresh-hover.svg
0 → 100644
View file @
b332ed1b
<svg
width=
"32"
height=
"32"
viewBox=
"0 0 32 32"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
d=
"M21.6353 16.5954C21.4501 18.3353 20.4643 19.9658 18.833 20.9076C16.1226 22.4724 12.6569 21.5438 11.0921 18.8335L10.9255 18.5448M10.3641 15.4047C10.5493 13.6647 11.5352 12.0343 13.1665 11.0924C15.8768 9.5276 19.3425 10.4562 20.9073 13.1666L21.074 13.4552M10.3288 20.044L10.8168 18.2227L12.6382 18.7107M19.3616 13.2893L21.183 13.7774L21.671 11.956"
stroke=
"#1D2939"
stroke-width=
"1.5"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
</svg>
web/app/components/app/overview/assets/refresh.svg
0 → 100644
View file @
b332ed1b
<svg
width=
"32"
height=
"32"
viewBox=
"0 0 32 32"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
d=
"M21.6353 16.5954C21.4501 18.3353 20.4643 19.9658 18.833 20.9076C16.1226 22.4724 12.6569 21.5438 11.0921 18.8335L10.9255 18.5448M10.3641 15.4047C10.5493 13.6647 11.5352 12.0343 13.1665 11.0924C15.8768 9.5276 19.3425 10.4562 20.9073 13.1666L21.074 13.4552M10.3288 20.044L10.8168 18.2227L12.6382 18.7107M19.3616 13.2893L21.183 13.7774L21.671 11.956"
stroke=
"#667085"
stroke-width=
"1.5"
stroke-linecap=
"round"
stroke-linejoin=
"round"
/>
</svg>
web/app/components/app/overview/share-link.tsx
deleted
100644 → 0
View file @
5d783a49
'use client'
import
type
{
FC
}
from
'react'
import
React
,
{
useState
}
from
'react'
import
{
ArrowPathIcon
,
LinkIcon
,
}
from
'@heroicons/react/24/outline'
import
{
useTranslation
}
from
'react-i18next'
import
Modal
from
'@/app/components/base/modal'
import
Button
from
'@/app/components/base/button'
import
useCopyToClipboard
from
'@/hooks/use-copy-to-clipboard'
import
'./style.css'
type
IShareLinkProps
=
{
isShow
:
boolean
onClose
:
()
=>
void
onGenerateCode
:
()
=>
Promise
<
void
>
linkUrl
:
string
}
const
ShareLinkModal
:
FC
<
IShareLinkProps
>
=
({
linkUrl
,
isShow
,
onClose
,
onGenerateCode
,
})
=>
{
const
[
_
,
copy
]
=
useCopyToClipboard
()
const
[
genLoading
,
setGenLoading
]
=
useState
(
false
)
const
{
t
}
=
useTranslation
()
return
<
Modal
title=
{
t
(
'appOverview.overview.appInfo.share.explanation'
)
}
isShow=
{
isShow
}
onClose=
{
onClose
}
>
{
/* share url */
}
<
p
className=
'mt-5 text-xs font-medium text-gray-500'
>
{
t
(
'appOverview.overview.appInfo.share.shareUrl'
)
}
</
p
>
{
/* input share url */
}
<
input
disabled
type=
'text'
value=
{
linkUrl
}
className=
'mt-1 w-full bg-gray-50 p-2 text-primary-600 text-xs font-normal outline-gray-50 hover:outline-gray-50 cursor-pointer'
/>
{
/* button copy link/ button regenerate */
}
<
div
className=
'mt-4 flex gap-3'
>
<
Button
type=
"primary"
className=
'w-32'
onClick=
{
()
=>
{
copy
(
linkUrl
)
}
}
>
<
LinkIcon
className=
'w-4 h-4 mr-2'
/>
{
t
(
'appOverview.overview.appInfo.share.copyLink'
)
}
</
Button
>
<
Button
className=
'w-32'
onClick=
{
async
()
=>
{
setGenLoading
(
true
)
await
onGenerateCode
()
setGenLoading
(
false
)
}
}
>
<
ArrowPathIcon
className=
{
`w-4 h-4 mr-2 ${genLoading ? 'generateLogo' : ''}`
}
/>
{
t
(
'appOverview.overview.appInfo.share.regenerate'
)
}
</
Button
>
</
div
>
</
Modal
>
}
export
default
ShareLinkModal
web/app/components/app/overview/style.css
→
web/app/components/app/overview/style.
module.
css
View file @
b332ed1b
...
...
@@ -16,3 +16,16 @@
@apply
w-4
h-4
bg-center
bg-no-repeat;
background-image
:
url(./assets/code-browser.svg)
;
}
.refreshIcon
{
background-image
:
url(./assets/refresh.svg)
;
background-position
:
center
;
background-repeat
:
no-repeat
;
}
.refreshIcon
:hover
{
background-image
:
url(./assets/refresh-hover.svg)
;
background-position
:
center
;
background-repeat
:
no-repeat
;
}
web/app/components/base/copy-feedback/index.tsx
0 → 100644
View file @
b332ed1b
'use client'
import
React
,
{
useState
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
debounce
}
from
'lodash-es'
import
Tooltip
from
'../tooltip'
import
copyStyle
from
'./style.module.css'
import
useCopyToClipboard
from
'@/hooks/use-copy-to-clipboard'
type
Props
=
{
content
:
string
selectorId
:
string
className
?:
string
}
const
prefixEmbedded
=
'appOverview.overview.appInfo.embedded'
const
CopyFeedback
=
({
content
,
selectorId
,
className
}:
Props
)
=>
{
const
{
t
}
=
useTranslation
()
const
[
_
,
copy
]
=
useCopyToClipboard
()
const
[
isCopied
,
setIsCopied
]
=
useState
<
boolean
>
(
false
)
const
onClickCopy
=
debounce
(()
=>
{
copy
(
content
)
setIsCopied
(
true
)
},
100
)
const
onMouseLeave
=
debounce
(()
=>
{
setIsCopied
(
false
)
},
100
)
return
(
<
Tooltip
selector=
{
`common-copy-feedback-${selectorId}`
}
content=
{
(
isCopied
?
t
(
`${prefixEmbedded}.copied`
)
:
t
(
`${prefixEmbedded}.copy`
))
||
''
}
>
<
div
className=
{
`w-8 h-8 cursor-pointer hover:bg-gray-100 rounded-lg ${
className ?? ''
}`
}
onMouseLeave=
{
onMouseLeave
}
>
<
div
onClick=
{
onClickCopy
}
className=
{
`w-full h-full ${copyStyle.copyIcon} ${
isCopied ? copyStyle.copied : ''
}`
}
></
div
>
</
div
>
</
Tooltip
>
)
}
export
default
CopyFeedback
web/app/components/base/copy-feedback/style.module.css
0 → 100644
View file @
b332ed1b
.copyIcon
{
background-image
:
url(~@/app/components/develop/secret-key/assets/copy.svg)
;
background-position
:
center
;
background-repeat
:
no-repeat
;
}
.copyIcon
:hover
{
background-image
:
url(~@/app/components/develop/secret-key/assets/copy-hover.svg)
;
background-position
:
center
;
background-repeat
:
no-repeat
;
}
.copyIcon.copied
{
background-image
:
url(~@/app/components/develop/secret-key/assets/copied.svg)
;
}
web/app/components/develop/secret-key/input-copy.tsx
View file @
b332ed1b
...
...
@@ -3,7 +3,7 @@ import React, { useEffect, useRef, useState } from 'react'
import
copy
from
'copy-to-clipboard'
import
{
t
}
from
'i18next'
import
s
from
'./style.module.css'
import
{
randomString
}
from
'@/
app/components/app-sidebar/basic
'
import
{
randomString
}
from
'@/
utils
'
import
Tooltip
from
'@/app/components/base/tooltip'
type
IInputCopyProps
=
{
...
...
web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx
View file @
b332ed1b
...
...
@@ -4,7 +4,7 @@ import { t } from 'i18next'
import
s
from
'./index.module.css'
import
Tooltip
from
'@/app/components/base/tooltip'
import
useCopyToClipboard
from
'@/hooks/use-copy-to-clipboard'
import
{
randomString
}
from
'@/
app/components/app-sidebar/basic
'
import
{
randomString
}
from
'@/
utils
'
type
IInvitationLinkProps
=
{
value
?:
string
...
...
web/i18n/lang/app-overview.en.ts
View file @
b332ed1b
...
...
@@ -11,13 +11,7 @@ const translation = {
explanation
:
'Ready-to-use AI WebApp'
,
accessibleAddress
:
'Public URL'
,
preview
:
'Preview'
,
share
:
{
entry
:
'Share'
,
explanation
:
'Share the following URL to invite more people to access the application.'
,
shareUrl
:
'Share URL'
,
copyLink
:
'Copy Link'
,
regenerate
:
'Regenerate'
,
},
preUseReminder
:
'Please enable WebApp before continuing.'
,
settings
:
{
entry
:
'Settings'
,
...
...
@@ -47,7 +41,7 @@ const translation = {
},
customize
:
{
way
:
'way'
,
entry
:
'
Want to customize your WebApp?
'
,
entry
:
'
Customize
'
,
title
:
'Customize AI WebApp'
,
explanation
:
'You can customize the frontend of the Web App to fit your scenario and style needs.'
,
way1
:
{
...
...
web/i18n/lang/app-overview.zh.ts
View file @
b332ed1b
...
...
@@ -11,13 +11,7 @@ const translation = {
explanation
:
'开箱即用的 AI WebApp'
,
accessibleAddress
:
'公开访问 URL'
,
preview
:
'预览'
,
share
:
{
entry
:
'分享'
,
explanation
:
'将以下网址分享出去,让更多人访问该应用'
,
shareUrl
:
'分享 URL'
,
copyLink
:
'复制链接'
,
regenerate
:
'重新生成'
,
},
preUseReminder
:
'使用前请先打开开关'
,
settings
:
{
entry
:
'设置'
,
...
...
@@ -47,7 +41,7 @@ const translation = {
},
customize
:
{
way
:
'方法'
,
entry
:
'
想要进一步自定义 WebApp?
'
,
entry
:
'
定制化
'
,
title
:
'定制化 AI WebApp'
,
explanation
:
'你可以定制化 Web App 前端以符合你的情景与风格需求'
,
way1
:
{
...
...
web/utils/index.ts
View file @
b332ed1b
...
...
@@ -14,11 +14,19 @@ export async function asyncRunSafe<T = any>(fn: Promise<T>): Promise<[Error] | [
}
export
const
getTextWidthWithCanvas
=
(
text
:
string
,
font
?:
string
)
=>
{
const
canvas
=
document
.
createElement
(
'canvas'
)
;
const
ctx
=
canvas
.
getContext
(
'2d'
)
;
const
canvas
=
document
.
createElement
(
'canvas'
)
const
ctx
=
canvas
.
getContext
(
'2d'
)
if
(
ctx
)
{
ctx
.
font
=
font
??
'12px Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'
;
return
Number
(
ctx
.
measureText
(
text
).
width
.
toFixed
(
2
))
;
ctx
.
font
=
font
??
'12px Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'
return
Number
(
ctx
.
measureText
(
text
).
width
.
toFixed
(
2
))
}
return
0
;
return
0
}
const
chars
=
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'
export
function
randomString
(
length
:
number
)
{
let
result
=
''
for
(
let
i
=
length
;
i
>
0
;
--
i
)
result
+=
chars
[
Math
.
floor
(
Math
.
random
()
*
chars
.
length
)]
return
result
}
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