Merge branch 'feat/plugins' into dev/plugin-deploy

This commit is contained in:
JzoNg 2024-11-08 21:34:59 +08:00
commit f85ff9ef78
42 changed files with 897 additions and 528 deletions

View File

@ -15,20 +15,20 @@ const Layout = ({ children }: { children: ReactNode }) => {
<>
<GA gaType={GaType.admin} />
<SwrInitor>
<AppContextProvider>
<EventEmitterContextProvider>
<ProviderContextProvider>
<ModalContextProvider>
<HeaderWrapper>
<Header />
</HeaderWrapper>
<TanstackQueryIniter>
<TanstackQueryIniter>
<AppContextProvider>
<EventEmitterContextProvider>
<ProviderContextProvider>
<ModalContextProvider>
<HeaderWrapper>
<Header />
</HeaderWrapper>
{children}
</TanstackQueryIniter>
</ModalContextProvider>
</ProviderContextProvider>
</EventEmitterContextProvider>
</AppContextProvider>
</ModalContextProvider>
</ProviderContextProvider>
</EventEmitterContextProvider>
</AppContextProvider>
</TanstackQueryIniter>
</SwrInitor>
</>
)

View File

@ -222,7 +222,7 @@ const ModelProviderPage = ({ searchText }: Props) => {
{!collapse && !isPluginsLoading && (
<div className='grid grid-cols-2 gap-2'>
{plugins.map(plugin => (
<ProviderCard key={plugin.plugin_id} payload={plugin} />
<ProviderCard key={plugin.plugin_id} payload={plugin} onSuccess={updateModelProviders} />
))}
</div>
)}

View File

@ -50,15 +50,15 @@ const ModelList: FC<ModelListProps> = ({
return (
<div className='px-2 pb-2 rounded-b-xl'>
<div className='py-1 bg-white rounded-lg'>
<div className='py-1 bg-components-panel-bg rounded-lg'>
<div className='flex items-center pl-1 pr-[3px]'>
<span className='group shrink-0 flex items-center mr-2'>
<span className='group-hover:hidden inline-flex pl-1 pr-1.5 h-6 leading-6 text-xs font-medium text-gray-500'>
<span className='group-hover:hidden inline-flex items-center pl-1 pr-1.5 h-6 system-xs-medium text-text-tertiary'>
{t('common.modelProvider.modelsNum', { num: models.length })}
<RiArrowRightSLine className='mr-0.5 w-4 h-4 rotate-90' />
</span>
<span
className='hidden group-hover:inline-flex items-center pl-1 pr-1.5 h-6 text-xs font-medium text-gray-500 bg-gray-50 cursor-pointer rounded-lg'
className='hidden group-hover:inline-flex items-center pl-1 pr-1.5 h-6 system-xs-medium text-text-tertiary bg-state-base-hover cursor-pointer rounded-lg'
onClick={() => onCollapse()}
>
{t('common.modelProvider.modelsNum', { num: models.length })}

View File

@ -9,7 +9,7 @@ import Button from '@/app/components/base/button'
import { useTranslation } from 'react-i18next'
import { RiLoader2Line } from '@remixicon/react'
import Badge, { BadgeState } from '@/app/components/base/badge/index'
import { installPackageFromMarketPlace } from '@/service/plugins'
import { useInstallPackageFromMarketPlace } from '@/service/use-plugins'
import checkTaskStatus from '../../base/check-task-status'
const i18nPrefix = 'plugin.installModal'
@ -32,6 +32,7 @@ const Installed: FC<Props> = ({
onFailed,
}) => {
const { t } = useTranslation()
const { mutateAsync: installPackageFromMarketPlace } = useInstallPackageFromMarketPlace()
const [isInstalling, setIsInstalling] = React.useState(false)
const {
check,

View File

@ -34,7 +34,7 @@ export type MarketplaceContextValue = {
activePluginType: string
handleActivePluginTypeChange: (type: string) => void
plugins?: Plugin[]
setPlugins: (plugins: Plugin[]) => void
resetPlugins: () => void
sort: PluginsSort
handleSortChange: (sort: PluginsSort) => void
marketplaceCollectionsFromClient?: MarketplaceCollection[]
@ -53,7 +53,7 @@ export const MarketplaceContext = createContext<MarketplaceContextValue>({
activePluginType: PLUGIN_TYPE_SEARCH_MAP.all,
handleActivePluginTypeChange: () => {},
plugins: undefined,
setPlugins: () => {},
resetPlugins: () => {},
sort: DEFAULT_SORT,
handleSortChange: () => {},
marketplaceCollectionsFromClient: [],
@ -91,7 +91,7 @@ export const MarketplaceContextProvider = ({
} = useMarketplaceCollectionsAndPlugins()
const {
plugins,
setPlugins,
resetPlugins,
queryPlugins,
queryPluginsWithDebounced,
} = useMarketplacePlugins()
@ -104,7 +104,7 @@ export const MarketplaceContextProvider = ({
queryMarketplaceCollectionsAndPlugins({
category: activePluginTypeRef.current === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginTypeRef.current,
})
setPlugins(undefined)
resetPlugins()
return
}
@ -116,7 +116,7 @@ export const MarketplaceContextProvider = ({
sortBy: sortRef.current.sortBy,
sortOrder: sortRef.current.sortOrder,
})
}, [queryPluginsWithDebounced, queryMarketplaceCollectionsAndPlugins, setPlugins])
}, [queryPluginsWithDebounced, queryMarketplaceCollectionsAndPlugins, resetPlugins])
const handleFilterPluginTagsChange = useCallback((tags: string[]) => {
setFilterPluginTags(tags)
@ -126,7 +126,7 @@ export const MarketplaceContextProvider = ({
queryMarketplaceCollectionsAndPlugins({
category: activePluginTypeRef.current === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginTypeRef.current,
})
setPlugins(undefined)
resetPlugins()
return
}
@ -138,7 +138,7 @@ export const MarketplaceContextProvider = ({
sortBy: sortRef.current.sortBy,
sortOrder: sortRef.current.sortOrder,
})
}, [queryPlugins, setPlugins, queryMarketplaceCollectionsAndPlugins])
}, [queryPlugins, resetPlugins, queryMarketplaceCollectionsAndPlugins])
const handleActivePluginTypeChange = useCallback((type: string) => {
setActivePluginType(type)
@ -148,7 +148,7 @@ export const MarketplaceContextProvider = ({
queryMarketplaceCollectionsAndPlugins({
category: type === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : type,
})
setPlugins(undefined)
resetPlugins()
return
}
@ -160,7 +160,7 @@ export const MarketplaceContextProvider = ({
sortBy: sortRef.current.sortBy,
sortOrder: sortRef.current.sortOrder,
})
}, [queryPlugins, setPlugins, queryMarketplaceCollectionsAndPlugins])
}, [queryPlugins, resetPlugins, queryMarketplaceCollectionsAndPlugins])
const handleSortChange = useCallback((sort: PluginsSort) => {
setSort(sort)
@ -187,7 +187,7 @@ export const MarketplaceContextProvider = ({
activePluginType,
handleActivePluginTypeChange,
plugins,
setPlugins,
resetPlugins,
sort,
handleSortChange,
marketplaceCollectionsFromClient,

View File

@ -15,10 +15,10 @@ const Description = async ({
return (
<>
<h1 className='shrink-0 mb-2 text-center title-4xl-semi-bold text-text-primary'>
Empower your AI development
{t('marketplace.empower')}
</h1>
<h2 className='shrink-0 flex justify-center items-center text-center body-md-regular text-text-tertiary'>
Discover
{t('marketplace.discover')}
<span className="relative ml-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
{t('category.models')}
</span>
@ -30,11 +30,11 @@ const Description = async ({
<span className="relative ml-1 mr-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
{t('category.extensions')}
</span>
and
{t('marketplace.and')}
<span className="relative ml-1 mr-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
{t('category.bundles')}
</span>
in Dify Marketplace
{t('marketplace.inDifyMarketplace')}
</h2>
</>
)

View File

@ -4,7 +4,9 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
import { useDebounceFn } from 'ahooks'
import type { Plugin } from '../types'
import type {
Plugin,
} from '../types'
import type {
CollectionsAndPluginsSearchParams,
MarketplaceCollection,
@ -12,9 +14,9 @@ import type {
} from './types'
import {
getMarketplaceCollectionsAndPlugins,
getMarketplacePlugins,
} from './utils'
import i18n from '@/i18n/i18next-config'
import { useMutationPluginsFromMarketplace } from '@/service/use-plugins'
export const useMarketplaceCollectionsAndPlugins = () => {
const [isLoading, setIsLoading] = useState(false)
@ -41,28 +43,29 @@ export const useMarketplaceCollectionsAndPlugins = () => {
}
export const useMarketplacePlugins = () => {
const [isLoading, setIsLoading] = useState(false)
const [plugins, setPlugins] = useState<Plugin[]>()
const {
data,
mutate,
reset,
isPending,
} = useMutationPluginsFromMarketplace()
const queryPlugins = useCallback(async (query: PluginsSearchParams) => {
setIsLoading(true)
const { marketplacePlugins } = await getMarketplacePlugins(query)
setIsLoading(false)
const queryPlugins = useCallback((pluginsSearchParams: PluginsSearchParams) => {
mutate(pluginsSearchParams)
}, [mutate])
setPlugins(marketplacePlugins)
}, [])
const { run: queryPluginsWithDebounced } = useDebounceFn(queryPlugins, {
const { run: queryPluginsWithDebounced } = useDebounceFn((pluginsSearchParams) => {
mutate(pluginsSearchParams)
}, {
wait: 500,
})
return {
plugins,
setPlugins,
plugins: data?.data?.plugins,
resetPlugins: reset,
queryPlugins,
queryPluginsWithDebounced,
isLoading,
setIsLoading,
isLoading: isPending,
}
}

View File

@ -5,6 +5,7 @@ import SearchBoxWrapper from './search-box/search-box-wrapper'
import PluginTypeSwitch from './plugin-type-switch'
import ListWrapper from './list/list-wrapper'
import { getMarketplaceCollectionsAndPlugins } from './utils'
import { TanstackQueryIniter } from '@/context/query-client'
type MarketplaceProps = {
locale?: string
@ -17,18 +18,20 @@ const Marketplace = async ({
const { marketplaceCollections, marketplaceCollectionPluginsMap } = await getMarketplaceCollectionsAndPlugins()
return (
<MarketplaceContextProvider>
<Description locale={locale} />
<IntersectionLine />
<SearchBoxWrapper locale={locale} />
<PluginTypeSwitch locale={locale} />
<ListWrapper
locale={locale}
marketplaceCollections={marketplaceCollections}
marketplaceCollectionPluginsMap={marketplaceCollectionPluginsMap}
showInstallButton={showInstallButton}
/>
</MarketplaceContextProvider>
<TanstackQueryIniter>
<MarketplaceContextProvider>
<Description locale={locale} />
<IntersectionLine />
<SearchBoxWrapper locale={locale} />
<PluginTypeSwitch locale={locale} />
<ListWrapper
locale={locale}
marketplaceCollections={marketplaceCollections}
marketplaceCollectionPluginsMap={marketplaceCollectionPluginsMap}
showInstallButton={showInstallButton}
/>
</MarketplaceContextProvider>
</TanstackQueryIniter>
)
}

View File

@ -30,7 +30,7 @@ const PluginTypeSwitch = ({
const options = [
{
value: PLUGIN_TYPE_SEARCH_MAP.all,
text: 'All',
text: t('plugin.category.all'),
icon: null,
},
{

View File

@ -34,7 +34,7 @@ const PluginSettingModal: FC<Props> = ({
const handleSave = useCallback(async () => {
await onSave(tempPrivilege)
onHide()
}, [tempPrivilege])
}, [onHide, onSave, tempPrivilege])
return (
<Modal

View File

@ -13,6 +13,8 @@ import Description from '../card/base/description'
import Icon from '../card/base/card-icon'
import Title from '../card/base/title'
import OrgInfo from '../card/base/org-info'
import { useGitHubReleases } from '../install-plugin/hooks'
import { compareVersion, getLatestVersion } from '@/utils/semver'
import OperationDropdown from './operation-dropdown'
import PluginInfo from '@/app/components/plugins/plugin-page/plugin-info'
import ActionButton from '@/app/components/base/action-button'
@ -20,10 +22,13 @@ import Button from '@/app/components/base/button'
import Badge from '@/app/components/base/badge'
import Confirm from '@/app/components/base/confirm'
import Tooltip from '@/app/components/base/tooltip'
import Toast from '@/app/components/base/toast'
import { BoxSparkleFill } from '@/app/components/base/icons/src/vender/plugin'
import { Github } from '@/app/components/base/icons/src/public/common'
import { uninstallPlugin } from '@/service/plugins'
import { useGetLanguage } from '@/context/i18n'
import { useModalContext } from '@/context/modal-context'
import { API_PREFIX, MARKETPLACE_URL_PREFIX } from '@/config'
import cn from '@/utils/classnames'
@ -32,16 +37,18 @@ const i18nPrefix = 'plugin.action'
type Props = {
detail: PluginDetail
onHide: () => void
onDelete: () => void
onUpdate: () => void
}
const DetailHeader = ({
detail,
onHide,
onDelete,
onUpdate,
}: Props) => {
const { t } = useTranslation()
const locale = useGetLanguage()
const { fetchReleases } = useGitHubReleases()
const { setShowUpdatePluginModal } = useModalContext()
const {
installation_id,
@ -53,13 +60,51 @@ const DetailHeader = ({
} = detail
const { author, name, label, description, icon, verified } = detail.declaration
const isFromGitHub = source === PluginSource.github
// Only plugin installed from GitHub need to check if it's the new version
const hasNewVersion = useMemo(() => {
return source === PluginSource.github && latest_version !== version
}, [source, latest_version, version])
// #plugin TODO# update plugin
const handleUpdate = () => { }
const handleUpdate = async () => {
try {
const fetchedReleases = await fetchReleases(author, name)
if (fetchedReleases.length === 0)
return
const versions = fetchedReleases.map(release => release.tag_name)
const latestVersion = getLatestVersion(versions)
if (compareVersion(latestVersion, version) === 1) {
setShowUpdatePluginModal({
onSaveCallback: () => {
onUpdate()
},
payload: {
type: PluginSource.github,
github: {
originalPackageInfo: {
id: installation_id,
repo: meta!.repo,
version: meta!.version,
package: meta!.package,
releases: fetchedReleases,
},
},
},
})
}
else {
Toast.notify({
type: 'info',
message: 'No new version available',
})
}
}
catch {
Toast.notify({
type: 'error',
message: 'Failed to compare versions',
})
}
}
const [isShowPluginInfo, {
setTrue: showPluginInfo,
@ -82,9 +127,9 @@ const DetailHeader = ({
hideDeleting()
if (res.success) {
hideDeleteConfirm()
onDelete()
onUpdate()
}
}, [hideDeleteConfirm, hideDeleting, installation_id, showDeleting, onDelete])
}, [hideDeleteConfirm, hideDeleting, installation_id, showDeleting, onUpdate])
// #plugin TODO# used in apps
// const usedInApps = 3
@ -141,6 +186,7 @@ const DetailHeader = ({
</div>
<div className='flex gap-1'>
<OperationDropdown
source={detail.source}
onInfo={showPluginInfo}
onCheckVersion={handleUpdate}
onRemove={showDeleteConfirm}

View File

@ -4,12 +4,14 @@ import { useBoolean } from 'ahooks'
import { RiDeleteBinLine, RiEditLine, RiLoginCircleLine } from '@remixicon/react'
import type { EndpointListItem } from '../types'
import EndpointModal from './endpoint-modal'
import { NAME_FIELD } from './utils'
import { addDefaultValue, toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
import ActionButton from '@/app/components/base/action-button'
import CopyBtn from '@/app/components/base/copy-btn'
import Confirm from '@/app/components/base/confirm'
import Indicator from '@/app/components/header/indicator'
import Switch from '@/app/components/base/switch'
import Toast from '@/app/components/base/toast'
import {
deleteEndpoint,
disableEndpoint,
@ -19,10 +21,12 @@ import {
type Props = {
data: EndpointListItem
handleChange: () => void
}
const EndpointCard = ({
data,
handleChange,
}: Props) => {
const { t } = useTranslation()
const [active, setActive] = useState(data.enabled)
@ -38,9 +42,11 @@ const EndpointCard = ({
url: '/workspaces/current/endpoints/enable',
endpointID,
})
await handleChange()
}
catch (error) {
catch (error: any) {
console.error(error)
Toast.notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
setActive(false)
}
}
@ -50,9 +56,12 @@ const EndpointCard = ({
url: '/workspaces/current/endpoints/disable',
endpointID,
})
await handleChange()
hideDisableConfirm()
}
catch (error) {
console.error(error)
Toast.notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
setActive(true)
}
}
@ -77,9 +86,12 @@ const EndpointCard = ({
url: '/workspaces/current/endpoints/delete',
endpointID,
})
await handleChange()
hideDeleteConfirm()
}
catch (error) {
console.error(error)
Toast.notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
}
}
@ -89,25 +101,34 @@ const EndpointCard = ({
}] = useBoolean(false)
const formSchemas = useMemo(() => {
return toolCredentialToFormSchemas(data.declaration.settings)
return toolCredentialToFormSchemas([NAME_FIELD, ...data.declaration.settings])
}, [data.declaration.settings])
const formValue = useMemo(() => {
return addDefaultValue(data.settings, formSchemas)
}, [data.settings, formSchemas])
const formValue = {
name: data.name,
...data.settings,
}
return addDefaultValue(formValue, formSchemas)
}, [data.name, data.settings, formSchemas])
const handleUpdate = (state: any) => {
const handleUpdate = async (state: any) => {
const newName = state.name
delete state.name
try {
updateEndpoint({
url: '/workspaces/current/endpoints',
await updateEndpoint({
url: '/workspaces/current/endpoints/update',
body: {
endpoint_id: data.id,
settings: state,
name: state.name,
name: newName,
},
})
await handleChange()
hideEndpointModalConfirm()
}
catch (error) {
console.error(error)
Toast.notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
}
}

View File

@ -5,21 +5,27 @@ import { useBoolean } from 'ahooks'
import { RiAddLine } from '@remixicon/react'
import EndpointModal from './endpoint-modal'
import EndpointCard from './endpoint-card'
import { NAME_FIELD } from './utils'
import { toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
import ActionButton from '@/app/components/base/action-button'
import Tooltip from '@/app/components/base/tooltip'
import Toast from '@/app/components/base/toast'
import { usePluginPageContext } from '@/app/components/plugins/plugin-page/context'
import {
createEndpoint,
fetchEndpointList,
} from '@/service/plugins'
import cn from '@/utils/classnames'
const EndpointList = () => {
type Props = {
showTopBorder?: boolean
}
const EndpointList = ({ showTopBorder }: Props) => {
const { t } = useTranslation()
const pluginDetail = usePluginPageContext(v => v.currentPluginDetail)
const pluginUniqueID = pluginDetail.plugin_unique_identifier
const declaration = pluginDetail.declaration.endpoint
const { data } = useSWR(
const { data, mutate } = useSWR(
{
url: '/workspaces/current/endpoints/list/plugin',
params: {
@ -36,22 +42,27 @@ const EndpointList = () => {
}] = useBoolean(false)
const formSchemas = useMemo(() => {
return toolCredentialToFormSchemas(declaration.settings)
return toolCredentialToFormSchemas([NAME_FIELD, ...declaration.settings])
}, [declaration.settings])
const handleCreate = (state: any) => {
const handleCreate = async (state: any) => {
const newName = state.name
delete state.name
try {
createEndpoint({
url: '/workspaces/current/endpoints',
await createEndpoint({
url: '/workspaces/current/endpoints/create',
body: {
plugin_unique_identifier: pluginUniqueID,
settings: state,
name: state.name,
name: newName,
},
})
await mutate()
hideEndpointModal()
}
catch (error) {
console.error(error)
Toast.notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') })
}
}
@ -59,7 +70,7 @@ const EndpointList = () => {
return null
return (
<div className='px-4 py-2 border-t border-divider-subtle'>
<div className={cn('px-4 py-2 border-divider-subtle', showTopBorder && 'border-t')}>
<div className='mb-1 h-6 flex items-center justify-between text-text-secondary system-sm-semibold-uppercase'>
<div className='flex items-center gap-0.5'>
{t('plugin.detailPanel.endpoints')}
@ -81,6 +92,7 @@ const EndpointList = () => {
<EndpointCard
key={index}
data={item}
handleChange={mutate}
/>
))}
</div>

View File

@ -10,11 +10,11 @@ import { usePluginPageContext } from '@/app/components/plugins/plugin-page/conte
import cn from '@/utils/classnames'
type Props = {
onDelete: () => void
onUpdate: () => void
}
const PluginDetailPanel: FC<Props> = ({
onDelete,
onUpdate,
}) => {
const pluginDetail = usePluginPageContext(v => v.currentPluginDetail)
const setCurrentPluginDetail = usePluginPageContext(v => v.setCurrentPluginDetail)
@ -39,11 +39,11 @@ const PluginDetailPanel: FC<Props> = ({
<DetailHeader
detail={pluginDetail}
onHide={handleHide}
onDelete={onDelete}
onUpdate={onUpdate}
/>
<div className='grow overflow-y-auto'>
{!!pluginDetail.declaration.endpoint && <EndpointList />}
{!!pluginDetail.declaration.tool && <ActionList />}
{!!pluginDetail.declaration.endpoint && <EndpointList showTopBorder={!!pluginDetail.declaration.tool} />}
{!!pluginDetail.declaration.model && <ModelList />}
</div>
</>

View File

@ -1,105 +0,0 @@
import { PluginSource, PluginType } from '../types'
export const toolNotion = {
id: 'dlfajkgjdga-dfjalksjfglkds-dfjakld',
created_at: '2024-10-16 16:05:33',
updated_at: '2024-10-16 16:05:33',
name: 'notion page search',
plugin_id: 'Notion/notion-page-search',
plugin_unique_identifier: 'Notion/notion-page-search:1.2.0@fldsjflkdsajfldsakajfkls',
declaration: {
version: '1.2.0',
author: 'Notion',
name: 'notion page search',
category: PluginType.tool,
icon: 'https://via.placeholder.com/150',
label: {
'en-US': 'Notion Page Search',
'zh-Hans': 'Notion 页面搜索',
},
description: {
'en-US': 'Description: Search Notion pages and open visited ones faster. No admin access required.More and more info...More and more info...More and more info...',
'zh-Hans': '搜索 Notion 页面并更快地打开已访问的页面。无需管理员访问权限。More and more info...More and more info...More and more info...',
},
created_at: '2024-10-16 16:05:33',
resource: {},
plugins: {},
endpoint: {
settings: [
{
type: 'secret-input',
name: 'api-key',
required: true,
default: null,
options: null,
label: {
en_US: 'API-key',
zh_Hans: 'API-key',
},
help: null,
url: null,
placeholder: {
en_US: 'Please input your API key',
zh_Hans: '请输入你的 API key',
},
},
],
endpoints: [
{ path: '/duck/<app_id>', method: 'GET' },
{ path: '/neko', method: 'GET' },
],
},
tool: null, // TODO
verified: true,
},
installation_id: 'jflkdsjoewingljlsadjgoijg-dkfjldajglkajglask-dlfkajdg',
tenant_id: 'jflkdsjoewingljlsadjgoijg',
endpoints_setups: 2,
endpoints_active: 1,
version: '1.2.0',
source: PluginSource.marketplace,
meta: null,
}
export const toolNotionEndpoints = [
{
id: 'dlfajkgjdga-dfjalksjfglkds-dfjakld',
created_at: '2024-10-16 16:05:33',
updated_at: '2024-10-16 16:05:33',
settings: {
'api-key': '*******',
},
tenant_id: 'jflkdsjoewingljlsadjgoijg',
plugin_id: 'Notion/notion-page-search',
expired_at: '2024-10-16 16:05:33',
declaration: {
settings: [
{
type: 'secret-input',
name: 'api-key',
required: true,
default: null,
options: null,
label: {
en_US: 'API-key',
zh_Hans: 'API-key',
},
help: null,
url: null,
placeholder: {
en_US: 'Please input your API key',
zh_Hans: '请输入你的 API key',
},
},
],
endpoints: [
{ path: '/duck/<app_id>', method: 'GET' },
{ path: '/neko', method: 'GET' },
],
},
name: 'default',
enabled: true,
url: 'http://localhost:5002/e/45rj9V4TRxAjL0I2wXRZgZdXjdHEKBh8',
hook_id: '45rj9V4TRxAjL0I2wXRZgZdXjdHEKBh8',
},
]

View File

@ -21,7 +21,7 @@ const ModelList = () => {
return (
<div className='px-4 py-2'>
<div className='mb-1 h-6 flex items-center text-text-secondary system-sm-semibold-uppercase'>{t('plugin.detailPanel.modelNum', { num: res.data.length })}</div>
<div className='h-8 flex items-center'>
<div className='flex flex-col'>
{res.data.map(model => (
<div key={model.model} className='h-6 py-1 flex items-center'>
<ModelIcon

View File

@ -2,6 +2,7 @@
import type { FC } from 'react'
import React, { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { PluginSource } from '../types'
import { RiArrowRightUpLine, RiMoreFill } from '@remixicon/react'
import ActionButton from '@/app/components/base/action-button'
// import Button from '@/app/components/base/button'
@ -13,6 +14,7 @@ import {
import cn from '@/utils/classnames'
type Props = {
source: PluginSource
onInfo: () => void
onCheckVersion: () => void
onRemove: () => void
@ -20,10 +22,11 @@ type Props = {
}
const OperationDropdown: FC<Props> = ({
source,
detailUrl,
onInfo,
onCheckVersion,
onRemove,
detailUrl,
}) => {
const { t } = useTranslation()
const [open, doSetOpen] = useState(false)
@ -56,25 +59,33 @@ const OperationDropdown: FC<Props> = ({
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className='z-50'>
<div className='w-[160px] p-1 bg-components-panel-bg-blur rounded-xl border-[0.5px] border-components-panel-border shadow-lg'>
<div
onClick={() => {
onInfo()
handleTrigger()
}}
className='px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover'
>{t('plugin.detailPanel.operation.info')}</div>
<div
onClick={() => {
onCheckVersion()
handleTrigger()
}}
className='px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover'
>{t('plugin.detailPanel.operation.checkUpdate')}</div>
<a href={detailUrl} target='_blank' className='flex items-center px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover'>
<span className='grow'>{t('plugin.detailPanel.operation.viewDetail')}</span>
<RiArrowRightUpLine className='shrink-0 w-3.5 h-3.5 text-text-tertiary' />
</a>
<div className='my-1 h-px bg-divider-subtle'></div>
{source === PluginSource.github && (
<div
onClick={() => {
onInfo()
handleTrigger()
}}
className='px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover'
>{t('plugin.detailPanel.operation.info')}</div>
)}
{source === PluginSource.github && (
<div
onClick={() => {
onCheckVersion()
handleTrigger()
}}
className='px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover'
>{t('plugin.detailPanel.operation.checkUpdate')}</div>
)}
{source === PluginSource.marketplace && (
<a href={detailUrl} target='_blank' className='flex items-center px-3 py-1.5 rounded-lg text-text-secondary system-md-regular cursor-pointer hover:bg-state-base-hover'>
<span className='grow'>{t('plugin.detailPanel.operation.viewDetail')}</span>
<RiArrowRightUpLine className='shrink-0 w-3.5 h-3.5 text-text-tertiary' />
</a>
)}
{(source === PluginSource.marketplace || source === PluginSource.github) && (
<div className='my-1 h-px bg-divider-subtle'></div>
)}
<div
onClick={() => {
onRemove()

View File

@ -0,0 +1,21 @@
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
export const NAME_FIELD = {
type: FormTypeEnum.textInput,
name: 'name',
label: {
en_US: 'Endpoint Name',
zh_Hans: '端点名称',
ja_JP: 'エンドポイント名',
pt_BR: 'Nome do ponto final',
},
placeholder: {
en_US: 'Endpoint Name',
zh_Hans: '端点名称',
ja_JP: 'エンドポイント名',
pt_BR: 'Nome do ponto final',
},
required: true,
default: '',
help: null,
}

View File

@ -11,16 +11,13 @@ import {
useContextSelector,
} from 'use-context-selector'
import { useSelector as useAppContextSelector } from '@/context/app-context'
import type { Permissions, PluginDetail } from '../types'
import type { PluginDetail } from '../types'
import type { FilterState } from './filter-management'
import { PermissionType } from '../types'
import { useTranslation } from 'react-i18next'
import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
export type PluginPageContextValue = {
containerRef: React.RefObject<HTMLDivElement>
permissions: Permissions
setPermissions: (permissions: PluginPageContextValue['permissions']) => void
currentPluginDetail: PluginDetail | undefined
setCurrentPluginDetail: (plugin: PluginDetail) => void
filters: FilterState
@ -32,21 +29,16 @@ export type PluginPageContextValue = {
export const PluginPageContext = createContext<PluginPageContextValue>({
containerRef: { current: null },
permissions: {
install_permission: PermissionType.noOne,
debug_permission: PermissionType.noOne,
},
setPermissions: () => {},
currentPluginDetail: undefined,
setCurrentPluginDetail: () => {},
setCurrentPluginDetail: () => { },
filters: {
categories: [],
tags: [],
searchQuery: '',
},
setFilters: () => {},
setFilters: () => { },
activeTab: '',
setActiveTab: () => {},
setActiveTab: () => { },
options: [],
})
@ -63,10 +55,6 @@ export const PluginPageContextProvider = ({
}: PluginPageContextProviderProps) => {
const { t } = useTranslation()
const containerRef = useRef<HTMLDivElement>(null)
const [permissions, setPermissions] = useState<PluginPageContextValue['permissions']>({
install_permission: PermissionType.noOne,
debug_permission: PermissionType.noOne,
})
const [filters, setFilters] = useState<FilterState>({
categories: [],
tags: [],
@ -93,8 +81,6 @@ export const PluginPageContextProvider = ({
<PluginPageContext.Provider
value={{
containerRef,
permissions,
setPermissions,
currentPluginDetail,
setCurrentPluginDetail,
filters,

View File

@ -1,6 +1,6 @@
'use client'
import type { FC } from 'react'
import React, { useEffect } from 'react'
import React from 'react'
import {
RiArrowRightUpLine,
RiBugLine,
@ -9,19 +9,16 @@ import { useTranslation } from 'react-i18next'
import KeyValueItem from '../base/key-value-item'
import Tooltip from '@/app/components/base/tooltip'
import Button from '@/app/components/base/button'
import type { DebugInfo as DebugInfoTypes } from '../types'
import { fetchDebugKey } from '@/service/plugins'
import { useDebugKey } from '@/service/use-plugins'
const i18nPrefix = 'plugin.debugInfo'
const DebugInfo: FC = () => {
const { t } = useTranslation()
const [info, setInfo] = React.useState<DebugInfoTypes | null>(null)
useEffect(() => {
fetchDebugKey().then((res) => {
setInfo(res)
})
}, [])
const { data: info, isLoading } = useDebugKey()
if (isLoading) return null
return (
<Tooltip
triggerMethod='click'
@ -37,7 +34,7 @@ const DebugInfo: FC = () => {
</div>
<div className='space-y-0.5'>
<KeyValueItem
label={'Port'}
label={'URL'}
value={`${info?.host}:${info?.port}`}
/>
<KeyValueItem

View File

@ -206,7 +206,7 @@ const PluginPage = ({
{showPluginSettingModal && (
<PermissionSetModal
payload={permissions}
payload={permissions!}
onHide={setHidePluginSettingModal}
onSave={setPermissions}
/>

View File

@ -48,7 +48,7 @@ const PluginsPanel = () => {
) : (
<Empty />
)}
<PluginDetailPanel onDelete={() => invalidateInstalledPluginList()}/>
<PluginDetailPanel onUpdate={() => invalidateInstalledPluginList()}/>
</>
)
}

View File

@ -1,15 +1,12 @@
import { useEffect } from 'react'
import type { Permissions } from '../types'
import { PermissionType } from '../types'
import {
usePluginPageContext,
} from './context'
import { useAppContext } from '@/context/app-context'
import { updatePermission as doUpdatePermission, fetchPermission } from '@/service/plugins'
import Toast from '../../base/toast'
import { useTranslation } from 'react-i18next'
import { useInvalidatePermissions, useMutationPermissions, usePermissions } from '@/service/use-plugins'
const hasPermission = (permission: PermissionType, isAdmin: boolean) => {
const hasPermission = (permission: PermissionType | undefined, isAdmin: boolean) => {
if (!permission)
return false
if (permission === PermissionType.noOne)
return false
@ -22,29 +19,26 @@ const hasPermission = (permission: PermissionType, isAdmin: boolean) => {
const usePermission = () => {
const { t } = useTranslation()
const { isCurrentWorkspaceManager, isCurrentWorkspaceOwner } = useAppContext()
const [permissions, setPermissions] = usePluginPageContext(v => [v.permissions, v.setPermissions])
const { data: permissions } = usePermissions()
const invalidatePermissions = useInvalidatePermissions()
const { mutate: updatePermission, isPending: isUpdatePending } = useMutationPermissions({
onSuccess: () => {
invalidatePermissions()
Toast.notify({
type: 'success',
message: t('common.api.actionSuccess'),
})
},
})
const isAdmin = isCurrentWorkspaceManager || isCurrentWorkspaceOwner
const updatePermission = async (permission: Permissions) => {
await doUpdatePermission(permission)
setPermissions(permission)
Toast.notify({
type: 'success',
message: t('common.api.actionSuccess'),
})
}
useEffect(() => {
(async () => {
const permission = await fetchPermission()
setPermissions(permission)
})()
}, [])
return {
canManagement: hasPermission(permissions.install_permission, isAdmin),
canDebugger: hasPermission(permissions.debug_permission, isAdmin),
canManagement: hasPermission(permissions?.install_permission, isAdmin),
canDebugger: hasPermission(permissions?.debug_permission, isAdmin),
canSetPermissions: isAdmin,
permissions,
setPermissions: updatePermission,
isUpdatePending,
}
}

View File

@ -19,11 +19,13 @@ import { useBoolean } from 'ahooks'
type Props = {
className?: string
payload: Plugin
onSuccess: () => void
}
const ProviderCard: FC<Props> = ({
className,
payload,
onSuccess,
}) => {
const { t } = useTranslation()
const [isShowInstallFromMarketplace, {
@ -84,7 +86,10 @@ const ProviderCard: FC<Props> = ({
manifest={payload as any}
uniqueIdentifier={payload.latest_package_identifier}
onClose={hideInstallFromMarketplace}
onSuccess={hideInstallFromMarketplace}
onSuccess={() => {
onSuccess()
hideInstallFromMarketplace()
}}
/>
)
}

View File

@ -301,3 +301,7 @@ export type InstalledPluginListResponse = {
export type UninstallPluginResponse = {
success: boolean
}
export type PluginsFromMarketplaceResponse = {
plugins: Plugin[]
}

View File

@ -15,7 +15,7 @@ export const useMarketplace = (searchPluginText: string, filterPluginTags: strin
} = useMarketplaceCollectionsAndPlugins()
const {
plugins,
setPlugins,
resetPlugins,
queryPlugins,
queryPluginsWithDebounced,
isLoading: isPluginsLoading,
@ -37,9 +37,9 @@ export const useMarketplace = (searchPluginText: string, filterPluginTags: strin
}
else {
queryMarketplaceCollectionsAndPlugins()
setPlugins(undefined)
resetPlugins()
}
}, [searchPluginText, filterPluginTags, queryPlugins, queryMarketplaceCollectionsAndPlugins, queryPluginsWithDebounced, setPlugins])
}, [searchPluginText, filterPluginTags, queryPlugins, queryMarketplaceCollectionsAndPlugins, queryPluginsWithDebounced, resetPlugins])
return {
isLoading: isLoading || isPluginsLoading,

View File

@ -31,9 +31,11 @@ const Marketplace = ({
onClick={() => onMarketplaceScroll()}
/>
<div className='sticky top-0 pt-5 pb-3 bg-background-default-subtle z-10'>
<div className='title-2xl-semi-bold bg-gradient-to-r from-[rgba(11,165,236,0.95)] to-[rgba(21,90,239,0.95)] bg-clip-text text-transparent'>More from Marketplace</div>
<div className='title-2xl-semi-bold bg-gradient-to-r from-[rgba(11,165,236,0.95)] to-[rgba(21,90,239,0.95)] bg-clip-text text-transparent'>
{t('plugin.marketplace.moreFrom')}
</div>
<div className='flex items-center text-center body-md-regular text-text-tertiary'>
Discover
{t('plugin.marketplace.discover')}
<span className="relative ml-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
{t('plugin.category.models')}
</span>
@ -45,11 +47,11 @@ const Marketplace = ({
<span className="relative ml-1 mr-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
{t('plugin.category.extensions')}
</span>
and
{t('plugin.marketplace.and')}
<span className="relative ml-1 mr-1 body-md-medium text-text-secondary after:content-[''] after:absolute after:left-0 after:bottom-[1.5px] after:w-full after:h-2 after:bg-text-text-selected">
{t('plugin.category.bundles')}
</span>
in Dify Marketplace
{t('plugin.marketplace.inDifyMarketplace')}
</div>
</div>
{

View File

@ -80,7 +80,7 @@ export type Tool = {
export type ToolCredential = {
name: string
label: TypeWithI18N
help: TypeWithI18N
help: TypeWithI18N | null
placeholder: TypeWithI18N
type: string
required: boolean

View File

@ -57,6 +57,9 @@ const AllTools = ({
if (activeTab === ToolTypeEnum.Workflow)
mergedTools = workflowTools
if (!searchText)
return mergedTools.filter(toolWithProvider => toolWithProvider.tools.length > 0)
return mergedTools.filter((toolWithProvider) => {
return toolWithProvider.tools.some((tool) => {
return tool.label[language].toLowerCase().includes(searchText.toLowerCase())

View File

@ -1,10 +0,0 @@
const translation = {
plugins: {
title: 'Plugins',
},
discover: {
title: 'Explore Marketplace',
},
}
export default translation

View File

@ -1,5 +1,6 @@
const translation = {
category: {
all: 'All',
models: 'models',
tools: 'tools',
extensions: 'extensions',
@ -116,6 +117,13 @@ const translation = {
error: {
inValidGitHubUrl: 'Invalid GitHub URL. Please enter a valid URL in the format: https://github.com/owner/repo',
},
marketplace: {
empower: 'Empower your AI development',
discover: 'Discover',
and: 'and',
inDifyMarketplace: 'in Dify Marketplace',
moreFrom: 'More from Marketplace',
},
}
export default translation

View File

@ -1,10 +0,0 @@
const translation = {
plugins: {
title: '插件',
},
discover: {
title: '探索市场',
},
}
export default translation

View File

@ -1,5 +1,6 @@
const translation = {
category: {
all: '全部',
models: '模型',
tools: '工具',
extensions: '扩展',
@ -116,6 +117,13 @@ const translation = {
error: {
inValidGitHubUrl: '无效的 GitHub URL。请输入格式为 https://github.com/owner/repo 的有效 URL',
},
marketplace: {
empower: '助力您的 AI 开发',
discover: '探索',
and: '和',
inDifyMarketplace: '在 Dify 市场中',
moreFrom: '更多来自市场',
},
}
export default translation

View File

@ -64,6 +64,7 @@
"js-cookie": "^3.0.5",
"jwt-decode": "^4.0.0",
"katex": "^0.16.11",
"ky": "^1.7.2",
"lamejs": "^1.2.1",
"lexical": "^0.18.0",
"lodash-es": "^4.17.21",
@ -84,9 +85,9 @@
"react-hook-form": "^7.53.1",
"react-i18next": "^15.1.0",
"react-infinite-scroll-component": "^6.1.0",
"react-markdown": "^9.0.1",
"react-multi-email": "^1.0.25",
"react-papaparse": "^4.4.0",
"react-markdown": "^9.0.1",
"react-slider": "^2.0.6",
"react-sortablejs": "^6.1.4",
"react-syntax-highlighter": "^15.6.1",

View File

@ -133,6 +133,9 @@ importers:
katex:
specifier: ^0.16.11
version: 0.16.11
ky:
specifier: ^1.7.2
version: 1.7.2
lamejs:
specifier: ^1.2.1
version: 1.2.1
@ -5612,6 +5615,10 @@ packages:
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
engines: {node: '>=6'}
ky@1.7.2:
resolution: {integrity: sha512-OzIvbHKKDpi60TnF9t7UUVAF1B4mcqc02z5PIvrm08Wyb+yOcz63GRvEuVxNT18a9E1SrNouhB4W2NNLeD7Ykg==}
engines: {node: '>=18'}
lamejs@1.2.1:
resolution: {integrity: sha512-s7bxvjvYthw6oPLCm5pFxvA84wUROODB8jEO2+CE1adhKgrIvVOlmMgY8zyugxGrvRaDHNJanOiS21/emty6dQ==}
@ -14744,6 +14751,8 @@ snapshots:
kleur@4.1.5: {}
ky@1.7.2: {}
lamejs@1.2.1:
dependencies:
use-strict: 1.0.1

View File

@ -1,4 +1,4 @@
import { API_PREFIX, IS_CE_EDITION, MARKETPLACE_API_PREFIX, PUBLIC_API_PREFIX } from '@/config'
import { API_PREFIX, IS_CE_EDITION, PUBLIC_API_PREFIX } from '@/config'
import { refreshAccessTokenOrRelogin } from './refresh-token'
import Toast from '@/app/components/base/toast'
import type { AnnotationReply, MessageEnd, MessageReplace, ThoughtItem } from '@/app/components/base/chat/chat/type'
@ -17,27 +17,10 @@ import type {
WorkflowStartedResponse,
} from '@/types/workflow'
import { removeAccessToken } from '@/app/components/share/utils'
import type { FetchOptionType, ResponseError } from './fetch'
import { ContentType, base, baseOptions, getPublicToken } from './fetch'
const TIME_OUT = 100000
const ContentType = {
json: 'application/json',
stream: 'text/event-stream',
audio: 'audio/mpeg',
form: 'application/x-www-form-urlencoded; charset=UTF-8',
download: 'application/octet-stream', // for download
upload: 'multipart/form-data', // for upload
}
const baseOptions = {
method: 'GET',
mode: 'cors',
credentials: 'include', // always send cookies、HTTP Basic authentication.
headers: new Headers({
'Content-Type': ContentType.json,
}),
redirect: 'follow',
}
export type IOnDataMoreInfo = {
conversationId?: string
taskId?: string
@ -100,17 +83,6 @@ export type IOtherOptions = {
onTextReplace?: IOnTextReplace
}
type ResponseError = {
code: string
message: string
status: number
}
type FetchOptionType = Omit<RequestInit, 'body'> & {
params?: Record<string, any>
body?: BodyInit | Record<string, any> | null
}
function unicodeToChar(text: string) {
if (!text)
return ''
@ -277,153 +249,13 @@ const handleStream = (
read()
}
const baseFetch = <T>(
url: string,
fetchOptions: FetchOptionType,
{
isPublicAPI = false,
isMarketplaceAPI = false,
bodyStringify = true,
needAllResponseContent,
deleteContentType,
getAbortController,
silent,
}: IOtherOptions,
): Promise<T> => {
const options: typeof baseOptions & FetchOptionType = Object.assign({}, baseOptions, fetchOptions)
if (isMarketplaceAPI)
options.credentials = 'omit'
if (getAbortController) {
const abortController = new AbortController()
getAbortController(abortController)
options.signal = abortController.signal
}
if (isPublicAPI) {
const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0]
const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' })
let accessTokenJson = { [sharedToken]: '' }
try {
accessTokenJson = JSON.parse(accessToken)
}
catch (e) {
}
options.headers.set('Authorization', `Bearer ${accessTokenJson[sharedToken]}`)
}
else if (!isMarketplaceAPI) {
const accessToken = localStorage.getItem('console_token') || ''
options.headers.set('Authorization', `Bearer ${accessToken}`)
}
if (deleteContentType) {
options.headers.delete('Content-Type')
}
else {
const contentType = options.headers.get('Content-Type')
if (!contentType)
options.headers.set('Content-Type', ContentType.json)
}
const urlPrefix = (() => {
if (isMarketplaceAPI)
return MARKETPLACE_API_PREFIX
if (isPublicAPI)
return PUBLIC_API_PREFIX
return API_PREFIX
})()
let urlWithPrefix = `${urlPrefix}${url.startsWith('/') ? url : `/${url}`}`
const { method, params, body } = options
// handle query
if (method === 'GET' && params) {
const paramsArray: string[] = []
Object.keys(params).forEach(key =>
paramsArray.push(`${key}=${encodeURIComponent(params[key])}`),
)
if (urlWithPrefix.search(/\?/) === -1)
urlWithPrefix += `?${paramsArray.join('&')}`
else
urlWithPrefix += `&${paramsArray.join('&')}`
delete options.params
}
if (body && bodyStringify)
options.body = JSON.stringify(body)
// Handle timeout
return Promise.race([
new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('request timeout'))
}, TIME_OUT)
}),
new Promise((resolve, reject) => {
globalThis.fetch(urlWithPrefix, options as RequestInit)
.then((res) => {
const resClone = res.clone()
// Error handler
if (!/^(2|3)\d{2}$/.test(String(res.status))) {
const bodyJson = res.json()
switch (res.status) {
case 401:
return Promise.reject(resClone)
case 403:
bodyJson.then((data: ResponseError) => {
if (!silent)
Toast.notify({ type: 'error', message: data.message })
if (data.code === 'already_setup')
globalThis.location.href = `${globalThis.location.origin}/signin`
})
break
// fall through
default:
bodyJson.then((data: ResponseError) => {
if (!silent)
Toast.notify({ type: 'error', message: data.message })
})
}
return Promise.reject(resClone)
}
// handle delete api. Delete api not return content.
if (res.status === 204) {
resolve({ result: 'success' })
return
}
// return data
if (options.headers.get('Content-type') === ContentType.download || options.headers.get('Content-type') === ContentType.audio)
resolve(needAllResponseContent ? resClone : res.blob())
else resolve(needAllResponseContent ? resClone : res.json())
})
.catch((err) => {
if (!silent)
Toast.notify({ type: 'error', message: err })
reject(err)
})
}),
]) as Promise<T>
}
const baseFetch = base
export const upload = (options: any, isPublicAPI?: boolean, url?: string, searchParams?: string): Promise<any> => {
const urlPrefix = isPublicAPI ? PUBLIC_API_PREFIX : API_PREFIX
let token = ''
if (isPublicAPI) {
const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0]
const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' })
let accessTokenJson = { [sharedToken]: '' }
try {
accessTokenJson = JSON.parse(accessToken)
}
catch (e) {
}
token = accessTokenJson[sharedToken]
token = getPublicToken()
}
else {
const accessToken = localStorage.getItem('console_token') || ''
@ -499,9 +331,9 @@ export const ssePost = (
signal: abortController.signal,
}, fetchOptions)
const contentType = options.headers.get('Content-Type')
const contentType = (options.headers as Headers).get('Content-Type')
if (!contentType)
options.headers.set('Content-Type', ContentType.json)
(options.headers as Headers).set('Content-Type', ContentType.json)
getAbortController?.(abortController)
@ -559,18 +391,17 @@ export const ssePost = (
}
// base request
export const request = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
export const request = <T>(url: string, options = {}, otherOptions: IOtherOptions = {}) => {
return new Promise<T>((resolve, reject) => {
const otherOptionsForBaseFetch = otherOptions || {}
baseFetch<T>(url, options, otherOptionsForBaseFetch).then(resolve).catch((errResp) => {
baseFetch<T>(url, options, otherOptions).then(resolve).catch((errResp) => {
if (errResp?.status === 401) {
return refreshAccessTokenOrRelogin(TIME_OUT).then(() => {
baseFetch<T>(url, options, otherOptionsForBaseFetch).then(resolve).catch(reject)
baseFetch<T>(url, options, otherOptions).then(resolve).catch(reject)
}).catch(() => {
const {
isPublicAPI = false,
silent,
} = otherOptionsForBaseFetch
} = otherOptions
const bodyJson = errResp.json()
if (isPublicAPI) {
return bodyJson.then((data: ResponseError) => {
@ -629,6 +460,11 @@ export const post = <T>(url: string, options = {}, otherOptions?: IOtherOptions)
return request<T>(url, Object.assign({}, options, { method: 'POST' }), otherOptions)
}
// For Marketplace API
export const postMarketplace = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
return post<T>(url, options, { ...otherOptions, isMarketplaceAPI: true })
}
export const postPublic = <T>(url: string, options = {}, otherOptions?: IOtherOptions) => {
return post<T>(url, options, { ...otherOptions, isPublicAPI: true })
}

187
web/service/fetch.ts Normal file
View File

@ -0,0 +1,187 @@
import type { AfterResponseHook, BeforeErrorHook, BeforeRequestHook, Hooks } from 'ky'
import ky from 'ky'
import type { IOtherOptions } from './base'
import Toast from '@/app/components/base/toast'
import { API_PREFIX, MARKETPLACE_API_PREFIX, PUBLIC_API_PREFIX } from '@/config'
const TIME_OUT = 100000
export const ContentType = {
json: 'application/json',
stream: 'text/event-stream',
audio: 'audio/mpeg',
form: 'application/x-www-form-urlencoded; charset=UTF-8',
download: 'application/octet-stream', // for download
upload: 'multipart/form-data', // for upload
}
export type FetchOptionType = Omit<RequestInit, 'body'> & {
params?: Record<string, any>
body?: BodyInit | Record<string, any> | null
}
const afterResponse204: AfterResponseHook = async (_request, _options, response) => {
if (response.status === 204) return Response.json({ result: 'success' })
}
export type ResponseError = {
code: string
message: string
status: number
}
const afterResponseErrorCode = (otherOptions: IOtherOptions): AfterResponseHook => {
return async (_request, _options, response) => {
if (!/^(2|3)\d{2}$/.test(String(response.status))) {
const bodyJson = response.json() as Promise<ResponseError>
switch (response.status) {
case 401:
return Promise.reject(response)
case 403:
bodyJson.then((data: ResponseError) => {
if (!otherOptions.silent)
Toast.notify({ type: 'error', message: data.message })
if (data.code === 'already_setup')
globalThis.location.href = `${globalThis.location.origin}/signin`
})
break
// fall through
default:
bodyJson.then((data: ResponseError) => {
if (!otherOptions.silent)
Toast.notify({ type: 'error', message: data.message })
})
}
throw response
}
}
}
const beforeErrorToast = (otherOptions: IOtherOptions): BeforeErrorHook => {
return (error) => {
if (!otherOptions.silent)
Toast.notify({ type: 'error', message: error.message })
return error
}
}
export const getPublicToken = () => {
let token = ''
const sharedToken = globalThis.location.pathname.split('/').slice(-1)[0]
const accessToken = localStorage.getItem('token') || JSON.stringify({ [sharedToken]: '' })
let accessTokenJson = { [sharedToken]: '' }
try {
accessTokenJson = JSON.parse(accessToken)
}
catch {}
token = accessTokenJson[sharedToken]
return token || ''
}
const beforeRequestPublicAuthorization: BeforeRequestHook = (request) => {
const token = getPublicToken()
request.headers.set('Authorization', `Bearer ${token}`)
}
const beforeRequestAuthorization: BeforeRequestHook = (request) => {
const accessToken = localStorage.getItem('console_token') || ''
request.headers.set('Authorization', `Bearer ${accessToken}`)
}
const beforeRequestDeleteContentType: BeforeRequestHook = (request) => {
request.headers.delete('Content-Type')
}
const baseHooks: Hooks = {
afterResponse: [
afterResponse204,
],
}
const client = ky.create({
hooks: baseHooks,
timeout: TIME_OUT,
})
export const baseOptions: RequestInit = {
method: 'GET',
mode: 'cors',
credentials: 'include', // always send cookies、HTTP Basic authentication.
headers: new Headers({
'Content-Type': ContentType.json,
}),
redirect: 'follow',
}
async function base<T>(url: string, options: FetchOptionType = {}, otherOptions: IOtherOptions = {}): Promise<T> {
const { params, body, ...init } = Object.assign({}, baseOptions, options)
const {
isPublicAPI = false,
isMarketplaceAPI = false,
bodyStringify = true,
needAllResponseContent,
deleteContentType,
getAbortController,
} = otherOptions
const base
= isMarketplaceAPI
? MARKETPLACE_API_PREFIX
: isPublicAPI
? PUBLIC_API_PREFIX
: API_PREFIX
if (getAbortController) {
const abortController = new AbortController()
getAbortController(abortController)
options.signal = abortController.signal
}
const fetchPathname = `${base}${url.startsWith('/') ? url : `/${url}`}`
const res = await client.extend({
hooks: {
...baseHooks,
beforeError: [
...baseHooks.beforeError || [],
beforeErrorToast(otherOptions),
],
beforeRequest: [
...baseHooks.beforeRequest || [],
isPublicAPI && beforeRequestPublicAuthorization,
!isPublicAPI && !isMarketplaceAPI && beforeRequestAuthorization,
deleteContentType && beforeRequestDeleteContentType,
].filter(i => !!i),
afterResponse: [
...baseHooks.afterResponse || [],
afterResponseErrorCode(otherOptions),
],
},
})(fetchPathname, {
credentials: isMarketplaceAPI
? 'omit'
: (options.credentials || 'include'),
...init,
retry: {
methods: [],
},
...(bodyStringify ? { json: body } : { body: body as BodyInit }),
searchParams: params,
})
if (needAllResponseContent)
return res as T
const contentType = res.headers.get('content-type')
if (
contentType
&& [ContentType.download, ContentType.audio].includes(contentType)
)
return await res.blob() as T
return await res.json() as T
}
export {
client,
base,
}

View File

@ -1,5 +1,5 @@
import type { Fetcher } from 'swr'
import { del, get, getMarketplace, post, upload } from './base'
import { get, getMarketplace, post, upload } from './base'
import type {
CreateEndpointRequest,
EndpointOperationResponse,
@ -15,7 +15,6 @@ import type {
UpdateEndpointRequest,
uploadGitHubResponse,
} from '@/app/components/plugins/types'
import type { DebugInfo as DebugInfoTypes } from '@/app/components/plugins/types'
import type {
MarketplaceCollectionPluginsResponse,
MarketplaceCollectionsResponse,
@ -33,7 +32,7 @@ export const fetchEndpointList: Fetcher<EndpointsResponse, { url: string; params
export const deleteEndpoint: Fetcher<EndpointOperationResponse, { url: string; endpointID: string }> = ({ url, endpointID }) => {
// url = /workspaces/current/endpoints/delete
return del<EndpointOperationResponse>(url, { body: { endpoint_id: endpointID } })
return post<EndpointOperationResponse>(url, { body: { endpoint_id: endpointID } })
}
export const updateEndpoint: Fetcher<EndpointOperationResponse, { url: string; body: UpdateEndpointRequest }> = ({ url, body }) => {
@ -51,10 +50,6 @@ export const disableEndpoint: Fetcher<EndpointOperationResponse, { url: string;
return post<EndpointOperationResponse>(url, { body: { endpoint_id: endpointID } })
}
export const fetchDebugKey = async () => {
return get<DebugInfoTypes>('/workspaces/current/plugin/debugging-key')
}
export const uploadPackageFile = async (file: File) => {
const formData = new FormData()
formData.append('pkg', file)
@ -109,12 +104,6 @@ export const fetchManifestFromMarketPlace = async (uniqueIdentifier: string) =>
return getMarketplace<{ data: { plugin: PluginManifestInMarket } }>(`/plugins/identifier?unique_identifier=${uniqueIdentifier}`)
}
export const installPackageFromMarketPlace = async (uniqueIdentifier: string) => {
return post<InstallPackageResponse>('/workspaces/current/plugin/install/marketplace', {
body: { plugin_unique_identifiers: [uniqueIdentifier] },
})
}
export const fetchMarketplaceCollections: Fetcher<MarketplaceCollectionsResponse, { url: string; }> = ({ url }) => {
return get<MarketplaceCollectionsResponse>(url)
}
@ -131,10 +120,6 @@ export const checkTaskStatus = async (taskId: string) => {
return get<TaskStatusResponse>(`/workspaces/current/plugin/tasks/${taskId}`)
}
export const fetchPermission = async () => {
return get<Permissions>('/workspaces/current/plugin/permission/fetch')
}
export const updatePermission = async (permissions: Permissions) => {
return post('/workspaces/current/plugin/permission/change', { body: permissions })
}

View File

@ -1,11 +1,18 @@
import type { InstalledPluginListResponse } from '@/app/components/plugins/types'
import { get } from './base'
import {
useQueryClient,
} from '@tanstack/react-query'
import type {
DebugInfo as DebugInfoTypes,
InstallPackageResponse,
InstalledPluginListResponse,
Permissions,
PluginsFromMarketplaceResponse,
} from '@/app/components/plugins/types'
import type {
PluginsSearchParams,
} from '@/app/components/plugins/marketplace/types'
import { get, post, postMarketplace } from './base'
import {
useMutation,
useQuery,
useQueryClient,
} from '@tanstack/react-query'
const NAME_SPACE = 'plugins'
@ -27,3 +34,74 @@ export const useInvalidateInstalledPluginList = () => {
})
}
}
export const useInstallPackageFromMarketPlace = () => {
return useMutation({
mutationFn: (uniqueIdentifier: string) => {
return post<InstallPackageResponse>('/workspaces/current/plugin/install/marketplace', { body: { plugin_unique_identifiers: [uniqueIdentifier] } })
},
})
}
export const useDebugKey = () => {
return useQuery({
queryKey: [NAME_SPACE, 'debugKey'],
queryFn: () => get<DebugInfoTypes>('/workspaces/current/plugin/debugging-key'),
})
}
const usePermissionsKey = [NAME_SPACE, 'permissions']
export const usePermissions = () => {
return useQuery({
queryKey: usePermissionsKey,
queryFn: () => get<Permissions>('/workspaces/current/plugin/permission/fetch'),
})
}
export const useInvalidatePermissions = () => {
const queryClient = useQueryClient()
return () => {
queryClient.invalidateQueries(
{
queryKey: usePermissionsKey,
})
}
}
export const useMutationPermissions = ({
onSuccess,
}: {
onSuccess?: () => void
}) => {
return useMutation({
mutationFn: (payload: Permissions) => {
return post('/workspaces/current/plugin/permission/change', { body: payload })
},
onSuccess,
})
}
export const useMutationPluginsFromMarketplace = () => {
return useMutation({
mutationFn: (pluginsSearchParams: PluginsSearchParams) => {
const {
query,
sortBy,
sortOrder,
category,
tags,
} = pluginsSearchParams
return postMarketplace<{ data: PluginsFromMarketplaceResponse }>('/plugins/search/basic', {
body: {
page: 1,
page_size: 10,
query,
sort_by: sortBy,
sort_order: sortOrder,
category: category !== 'all' ? category : '',
tags,
},
})
},
})
}

View File

@ -95,10 +95,11 @@ html[data-theme="dark"] {
--color-components-checkbox-border-disabled: #FFFFFF03;
--color-components-checkbox-bg-unchecked: #FFFFFF08;
--color-components-checkbox-bg-unchecked-hover: #FFFFFF0D;
--color-components-checkbox-bg-disabled-checked: #155AEF33;
--color-components-radio-border-checked: #296DFF;
--color-components-radio-border-checked-hover: #5289FF;
--color-components-radio-border-checked-disabled: #FFFFFF14;
--color-components-radio-border-checked-disabled: #155AEF33;
--color-components-radio-bg-disabled: #FFFFFF08;
--color-components-radio-border: #FFFFFF66;
--color-components-radio-border-hover: #FFFFFF99;
@ -135,6 +136,9 @@ html[data-theme="dark"] {
--color-components-panel-on-panel-item-bg: #27272B;
--color-components-panel-on-panel-item-bg-hover: #3A3A40;
--color-components-panel-on-panel-item-bg-alt: #3A3A40;
--color-components-panel-on-panel-item-bg-transparent: #2C2C30F2;
--color-components-panel-on-panel-item-bg-hover-transparent: #3A3A4000;
--color-components-panel-on-panel-item-bg-destructive-hover-transparent: #FFFBFA00;
--color-components-panel-bg-transparent: #22222500;
@ -208,10 +212,12 @@ html[data-theme="dark"] {
--color-components-actionbar-bg: #222225;
--color-components-actionbar-border: #C8CEDA14;
--color-components-actionbar-bg-accent: #27272B;
--color-components-actionbar-border-accent: #5289FF;
--color-components-dropzone-bg-alt: #18181BCC;
--color-components-dropzone-bg: #18181B66;
--color-components-dropzone-bg-accent: #155AEF24;
--color-components-dropzone-bg-accent: #155AEF33;
--color-components-dropzone-border: #C8CEDA24;
--color-components-dropzone-border-alt: #C8CEDA33;
--color-components-dropzone-border-accent: #84ABFF;
@ -228,6 +234,14 @@ html[data-theme="dark"] {
--color-components-progress-gray-border: #98A2B2;
--color-components-progress-gray-bg: #C8CEDA05;
--color-components-progress-warning-progress: #FDB022;
--color-components-progress-warning-border: #FDB022;
--color-components-progress-warning-bg: #F790090A;
--color-components-progress-error-progress: #F97066;
--color-components-progress-error-border: #F97066;
--color-components-progress-error-bg: #F044380A;
--color-components-chat-input-audio-bg: #155AEF33;
--color-components-chat-input-audio-wave-default: #C8CEDA24;
--color-components-chat-input-bg-mask-1: #18181B0A;
@ -236,13 +250,70 @@ html[data-theme="dark"] {
--color-components-chat-input-audio-wave-active: #84ABFF;
--color-components-chat-input-audio-bg-alt: #18181BE5;
--color-components-Avatar-shape-fill-stop-0: #FFFFFFF2;
--color-components-Avatar-shape-fill-stop-100: #FFFFFFCC;
--color-components-avatar-shape-fill-stop-0: #FFFFFFF2;
--color-components-avatar-shape-fill-stop-100: #FFFFFFCC;
--color-components-Avatar-bg-mask-stop-0: #FFFFFF33;
--color-components-Avatar-bg-mask-stop-100: #FFFFFF08;
--color-components-avatar-bg-mask-stop-0: #FFFFFF33;
--color-components-avatar-bg-mask-stop-100: #FFFFFF08;
--color-components-Avatar-default-avatar-bg: #222225;
--color-components-avatar-default-avatar-bg: #222225;
--color-components-avatar-mask-darkmode-dimmed: #0000001F;
--color-components-label-gray: #C8CEDA24;
--color-components-premium-badge-blue-bg-stop-0: #5289FF;
--color-components-premium-badge-blue-bg-stop-100: #296DFF;
--color-components-premium-badge-blue-stroke-stop-0: #FFFFFF33;
--color-components-premium-badge-blue-stroke-stop-100: #296DFF;
--color-components-premium-badge-blue-text-stop-0: #EFF4FF;
--color-components-premium-badge-blue-text-stop-100: #B2CAFF;
--color-components-premium-badge-blue-glow: #004AEB;
--color-components-premium-badge-blue-bg-stop-0-hover: #84ABFF;
--color-components-premium-badge-blue-bg-stop-100-hover: #004AEB;
--color-components-premium-badge-blue-glow-hover: #D1E0FF;
--color-components-premium-badge-blue-stroke-stop-0-hover: #FFFFFF80;
--color-components-premium-badge-blue-stroke-stop-100-hover: #296DFF;
--color-components-premium-badge-highlight-stop-0: #FFFFFF1F;
--color-components-premium-badge-highlight-stop-100: #FFFFFF33;
--color-components-premium-badge-indigo-bg-stop-0: #6172F3;
--color-components-premium-badge-indigo-bg-stop-100: #3538CD;
--color-components-premium-badge-indigo-stroke-stop-0: #FFFFFF33;
--color-components-premium-badge-indigo-stroke-stop-100: #444CE7;
--color-components-premium-badge-indigo-text-stop-0: #EEF4FF;
--color-components-premium-badge-indigo-text-stop-100: #C7D7FE;
--color-components-premium-badge-indigo-glow: #3538CD;
--color-components-premium-badge-indigo-glow-hover: #E0EAFF;
--color-components-premium-badge-indigo-bg-stop-0-hover: #A4BCFD;
--color-components-premium-badge-indigo-bg-stop-100-hover: #3538CD;
--color-components-premium-badge-indigo-stroke-stop-0-hover: #FFFFFF80;
--color-components-premium-badge-indigo-stroke-stop-100-hover: #444CE7;
--color-components-premium-badge-grey-bg-stop-0: #676F83;
--color-components-premium-badge-grey-bg-stop-100: #495464;
--color-components-premium-badge-grey-stroke-stop-0: #FFFFFF1F;
--color-components-premium-badge-grey-stroke-stop-100: #495464;
--color-components-premium-badge-grey-text-stop-0: #F9FAFB;
--color-components-premium-badge-grey-text-stop-100: #E9EBF0;
--color-components-premium-badge-grey-glow: #354052;
--color-components-premium-badge-grey-glow-hover: #F2F4F7;
--color-components-premium-badge-grey-bg-stop-0-hover: #98A2B2;
--color-components-premium-badge-grey-bg-stop-100-hover: #354052;
--color-components-premium-badge-grey-stroke-stop-0-hover: #FFFFFF80;
--color-components-premium-badge-grey-stroke-stop-100-hover: #676F83;
--color-components-premium-badge-orange-bg-stop-0: #FF692E;
--color-components-premium-badge-orange-bg-stop-100: #E04F16;
--color-components-premium-badge-orange-stroke-stop-0: #FFFFFF33;
--color-components-premium-badge-orange-stroke-stop-100: #FF4405;
--color-components-premium-badge-orange-text-stop-0: #FEF6EE;
--color-components-premium-badge-orange-text-stop-100: #F9DBAF;
--color-components-premium-badge-orange-glow: #B93815;
--color-components-premium-badge-orange-glow-hover: #FDEAD7;
--color-components-premium-badge-orange-bg-stop-0-hover: #FF692E;
--color-components-premium-badge-orange-bg-stop-100-hover: #B93815;
--color-components-premium-badge-orange-stroke-stop-0-hover: #FFFFFF80;
--color-components-premium-badge-orange-stroke-stop-100-hover: #FF4405;
--color-text-primary: #FBFBFC;
--color-text-secondary: #D9D9DE;
@ -265,6 +336,7 @@ html[data-theme="dark"] {
--color-text-logo-text: #E9E9EC;
--color-text-empty-state-icon: #C8CEDA4D;
--color-text-inverted: #FFFFFF;
--color-text-inverted-dimm: #FFFFFFCC;
--color-background-body: #1D1D20;
--color-background-default-subtle: #222225;
@ -301,6 +373,7 @@ html[data-theme="dark"] {
--color-background-overlay-alt: #18181B66;
--color-background-surface-white: #FFFFFFE5;
--color-background-overlay-destructive: #F044384D;
--color-background-overlay-backdrop: #18181BF2;
--color-shadow-shadow-1: #0000000D;
--color-shadow-shadow-3: #0000001A;
@ -316,14 +389,26 @@ html[data-theme="dark"] {
--color-workflow-block-border: #FFFFFF14;
--color-workflow-block-parma-bg: #FFFFFF0D;
--color-workflow-block-bg: #27272B;
--color-workflow-block-bg-transparent: #27272BF5;
--color-workflow-block-border-highlight: #C8CEDA33;
--color-workflow-canvas-workflow-dot-color: #8585AD26;
--color-workflow-canvas-workflow-bg: #1D1D20;
--color-workflow-link-line-active: #296DFF;
--color-workflow-link-line-active: #5289FF;
--color-workflow-link-line-normal: #676F83;
--color-workflow-link-line-handle: #296DFF;
--color-workflow-link-line-handle: #5289FF;
--color-workflow-link-line-normal-transparent: #676F8333;
--color-workflow-link-line-failure-active: #FDB022;
--color-workflow-link-line-failure-handle: #FDB022;
--color-workflow-link-line-failure-button-bg: #F79009;
--color-workflow-link-line-failure-button-hover: #DC6803;
--color-workflow-link-line-success-active: #47CD89;
--color-workflow-link-line-success-handle: #47CD89;
--color-workflow-link-line-error-active: #F97066;
--color-workflow-link-line-error-handle: #F97066;
--color-workflow-minimap-bg: #27272B;
--color-workflow-minimap-block: #C8CEDA14;
@ -334,8 +419,8 @@ html[data-theme="dark"] {
--color-workflow-display-success-vignette-color: #17B26A40;
--color-workflow-display-success-bg-line-pattern: #18181BCC;
--color-workflow-display-glass-1: #FFFFFF03;
--color-workflow-display-glass-2: #FFFFFF08;
--color-workflow-display-glass-1: #FFFFFF08;
--color-workflow-display-glass-2: #FFFFFF0D;
--color-workflow-display-vignette-dark: #00000066;
--color-workflow-display-highlight: #FFFFFF1F;
--color-workflow-display-outline: #18181BF2;
@ -424,6 +509,7 @@ html[data-theme="dark"] {
--color-util-colors-orange-orange-500: #EF6820;
--color-util-colors-orange-orange-600: #F38744;
--color-util-colors-orange-orange-700: #F7B27A;
--color-util-colors-orange-orange-100-transparent: #77291700;
--color-util-colors-pink-pink-50: #4E0D30;
--color-util-colors-pink-pink-100: #851651;
@ -602,5 +688,10 @@ html[data-theme="dark"] {
--color-chatbot-bg: linear-gradient(180deg, rgba(34, 34, 37, 0.90) 0%, rgba(29, 29, 32, 0.90) 90.48%);
--color-chat-bubble-bg: linear-gradient(180deg, rgba(200, 206, 218, 0.08) 0%, rgba(200, 206, 218, 0.02) 100%);
--color-third-party-Github: #FFFFFF;
--color-third-party-Github-tertiary: #C8CEDA99;
--color-third-party-Github-secondary: #D9D9DE;
--color-third-party-model-bg-openai: #121212;
--color-third-party-model-bg-anthropic: #1D1917;
--color-third-party-model-bg-default: #0B0B0E;
--color-workflow-process-bg: linear-gradient(90deg, rgba(24, 24, 27, 0.25) 0%, rgba(24, 24, 27, 0.04) 100%);
}

View File

@ -86,7 +86,7 @@ html[data-theme="light"] {
--color-components-button-secondary-accent-border-disabled: #1018280A;
--color-components-checkbox-icon: #FFFFFF;
--color-components-checkbox-icon-disabled: #D0D5DC;
--color-components-checkbox-icon-disabled: #FFFFFF80;
--color-components-checkbox-bg: #155AEF;
--color-components-checkbox-bg-hover: #004AEB;
--color-components-checkbox-bg-disabled: #F2F4F7;
@ -95,10 +95,11 @@ html[data-theme="light"] {
--color-components-checkbox-border-disabled: #18181B0A;
--color-components-checkbox-bg-unchecked: #FFFFFF;
--color-components-checkbox-bg-unchecked-hover: #FFFFFF;
--color-components-checkbox-bg-disabled-checked: #B2CAFF;
--color-components-radio-border-checked: #155AEF;
--color-components-radio-border-checked-hover: #004AEB;
--color-components-radio-border-checked-disabled: #F2F4F7;
--color-components-radio-border-checked-disabled: #B2CAFF;
--color-components-radio-bg-disabled: #FFFFFF00;
--color-components-radio-border: #D0D5DC;
--color-components-radio-border-hover: #98A2B2;
@ -135,6 +136,9 @@ html[data-theme="light"] {
--color-components-panel-on-panel-item-bg: #FFFFFF;
--color-components-panel-on-panel-item-bg-hover: #F9FAFB;
--color-components-panel-on-panel-item-bg-alt: #F9FAFB;
--color-components-panel-on-panel-item-bg-transparent: #FFFFFFF2;
--color-components-panel-on-panel-item-bg-hover-transparent: #F9FAFB00;
--color-components-panel-on-panel-item-bg-destructive-hover-transparent: #FEF3F200;
--color-components-panel-bg-transparent: #FFFFFF00;
@ -161,10 +165,10 @@ html[data-theme="light"] {
--color-components-segmented-control-item-active-accent-bg: #FFFFFF;
--color-components-segmented-control-item-active-accent-border: #FFFFFF;
--color-components-option-card-option-bg: #F9FAFB;
--color-components-option-card-option-bg: #FCFCFD;
--color-components-option-card-option-selected-bg: #FFFFFF;
--color-components-option-card-option-selected-border: #296DFF;
--color-components-option-card-option-border: #F2F4F7;
--color-components-option-card-option-border: #E9EBF0;
--color-components-option-card-option-bg-hover: #FFFFFF;
--color-components-option-card-option-border-hover: #D0D5DC;
@ -208,10 +212,12 @@ html[data-theme="light"] {
--color-components-actionbar-bg: #FFFFFFF2;
--color-components-actionbar-border: #1018280A;
--color-components-actionbar-bg-accent: #F5F7FF;
--color-components-actionbar-border-accent: #B2CAFF;
--color-components-dropzone-bg-alt: #F2F4F7;
--color-components-dropzone-bg: #F9FAFB;
--color-components-dropzone-bg-accent: #EFF4FF;
--color-components-dropzone-bg-accent: #155AEF24;
--color-components-dropzone-border: #10182814;
--color-components-dropzone-border-alt: #10182833;
--color-components-dropzone-border-accent: #84ABFF;
@ -228,6 +234,14 @@ html[data-theme="light"] {
--color-components-progress-gray-border: #98A2B2;
--color-components-progress-gray-bg: #C8CEDA05;
--color-components-progress-warning-progress: #F79009;
--color-components-progress-warning-border: #F79009;
--color-components-progress-warning-bg: #F790090A;
--color-components-progress-error-progress: #F04438;
--color-components-progress-error-border: #F04438;
--color-components-progress-error-bg: #F044380A;
--color-components-chat-input-audio-bg: #EFF4FF;
--color-components-chat-input-audio-wave-default: #155AEF33;
--color-components-chat-input-bg-mask-1: #FFFFFF03;
@ -236,13 +250,70 @@ html[data-theme="light"] {
--color-components-chat-input-audio-wave-active: #296DFF;
--color-components-chat-input-audio-bg-alt: #FCFCFD;
--color-components-Avatar-shape-fill-stop-0: #FFFFFF;
--color-components-Avatar-shape-fill-stop-100: #FFFFFFE5;
--color-components-avatar-shape-fill-stop-0: #FFFFFF;
--color-components-avatar-shape-fill-stop-100: #FFFFFFE5;
--color-components-Avatar-bg-mask-stop-0: #FFFFFF1F;
--color-components-Avatar-bg-mask-stop-100: #FFFFFF14;
--color-components-avatar-bg-mask-stop-0: #FFFFFF1F;
--color-components-avatar-bg-mask-stop-100: #FFFFFF14;
--color-components-Avatar-default-avatar-bg: #D0D5DC;
--color-components-avatar-default-avatar-bg: #D0D5DC;
--color-components-avatar-mask-darkmode-dimmed: #FFFFFF00;
--color-components-label-gray: #F2F4F7;
--color-components-premium-badge-blue-bg-stop-0: #5289FF;
--color-components-premium-badge-blue-bg-stop-100: #155AEF;
--color-components-premium-badge-blue-stroke-stop-0: #FFFFFFF2;
--color-components-premium-badge-blue-stroke-stop-100: #155AEF;
--color-components-premium-badge-blue-text-stop-0: #F5F7FF;
--color-components-premium-badge-blue-text-stop-100: #D1E0FF;
--color-components-premium-badge-blue-glow: #00329E;
--color-components-premium-badge-blue-bg-stop-0-hover: #296DFF;
--color-components-premium-badge-blue-bg-stop-100-hover: #004AEB;
--color-components-premium-badge-blue-glow-hover: #84ABFF;
--color-components-premium-badge-blue-stroke-stop-0-hover: #FFFFFFF2;
--color-components-premium-badge-blue-stroke-stop-100-hover: #00329E;
--color-components-premium-badge-highlight-stop-0: #FFFFFF1F;
--color-components-premium-badge-highlight-stop-100: #FFFFFF4D;
--color-components-premium-badge-indigo-bg-stop-0: #8098F9;
--color-components-premium-badge-indigo-bg-stop-100: #444CE7;
--color-components-premium-badge-indigo-stroke-stop-0: #FFFFFFF2;
--color-components-premium-badge-indigo-stroke-stop-100: #6172F3;
--color-components-premium-badge-indigo-text-stop-0: #F5F8FF;
--color-components-premium-badge-indigo-text-stop-100: #E0EAFF;
--color-components-premium-badge-indigo-glow: #2D3282;
--color-components-premium-badge-indigo-glow-hover: #A4BCFD;
--color-components-premium-badge-indigo-bg-stop-0-hover: #6172F3;
--color-components-premium-badge-indigo-bg-stop-100-hover: #2D31A6;
--color-components-premium-badge-indigo-stroke-stop-0-hover: #FFFFFFF2;
--color-components-premium-badge-indigo-stroke-stop-100-hover: #2D31A6;
--color-components-premium-badge-grey-bg-stop-0: #98A2B2;
--color-components-premium-badge-grey-bg-stop-100: #676F83;
--color-components-premium-badge-grey-stroke-stop-0: #FFFFFFF2;
--color-components-premium-badge-grey-stroke-stop-100: #676F83;
--color-components-premium-badge-grey-text-stop-0: #FCFCFD;
--color-components-premium-badge-grey-text-stop-100: #F2F4F7;
--color-components-premium-badge-grey-glow: #101828;
--color-components-premium-badge-grey-glow-hover: #D0D5DC;
--color-components-premium-badge-grey-bg-stop-0-hover: #676F83;
--color-components-premium-badge-grey-bg-stop-100-hover: #354052;
--color-components-premium-badge-grey-stroke-stop-0-hover: #FFFFFFF2;
--color-components-premium-badge-grey-stroke-stop-100-hover: #354052;
--color-components-premium-badge-orange-bg-stop-0: #FF692E;
--color-components-premium-badge-orange-bg-stop-100: #E04F16;
--color-components-premium-badge-orange-stroke-stop-0: #FFFFFFF2;
--color-components-premium-badge-orange-stroke-stop-100: #E62E05;
--color-components-premium-badge-orange-text-stop-0: #FEFAF5;
--color-components-premium-badge-orange-text-stop-100: #FDEAD7;
--color-components-premium-badge-orange-glow: #772917;
--color-components-premium-badge-orange-glow-hover: #F7B27A;
--color-components-premium-badge-orange-bg-stop-0-hover: #FF4405;
--color-components-premium-badge-orange-bg-stop-100-hover: #B93815;
--color-components-premium-badge-orange-stroke-stop-0-hover: #FFFFFFF2;
--color-components-premium-badge-orange-stroke-stop-100-hover: #BC1B06;
--color-text-primary: #101828;
--color-text-secondary: #354052;
@ -265,6 +336,7 @@ html[data-theme="light"] {
--color-text-logo-text: #18222F;
--color-text-empty-state-icon: #D0D5DC;
--color-text-inverted: #000000;
--color-text-inverted-dimm: #000000F2;
--color-background-body: #F2F4F7;
--color-background-default-subtle: #FCFCFD;
@ -301,6 +373,7 @@ html[data-theme="light"] {
--color-background-overlay-alt: #10182866;
--color-background-surface-white: #FFFFFFF2;
--color-background-overlay-destructive: #F044384D;
--color-background-overlay-backdrop: #F2F4F7F2;
--color-shadow-shadow-1: #09090B08;
--color-shadow-shadow-3: #09090B0D;
@ -316,6 +389,7 @@ html[data-theme="light"] {
--color-workflow-block-border: #FFFFFF;
--color-workflow-block-parma-bg: #F2F4F7;
--color-workflow-block-bg: #FCFCFD;
--color-workflow-block-bg-transparent: #FCFCFDE5;
--color-workflow-block-border-highlight: #155AEF24;
--color-workflow-canvas-workflow-dot-color: #8585AD26;
@ -324,6 +398,17 @@ html[data-theme="light"] {
--color-workflow-link-line-active: #296DFF;
--color-workflow-link-line-normal: #D0D5DC;
--color-workflow-link-line-handle: #296DFF;
--color-workflow-link-line-normal-transparent: #D0D5DC33;
--color-workflow-link-line-failure-active: #F79009;
--color-workflow-link-line-failure-handle: #F79009;
--color-workflow-link-line-failure-button-bg: #DC6803;
--color-workflow-link-line-failure-button-hover: #B54708;
--color-workflow-link-line-success-active: #17B26A;
--color-workflow-link-line-success-handle: #17B26A;
--color-workflow-link-line-error-active: #F04438;
--color-workflow-link-line-error-handle: #F04438;
--color-workflow-minimap-bg: #E9EBF0;
--color-workflow-minimap-block: #C8CEDA4D;
@ -424,6 +509,7 @@ html[data-theme="light"] {
--color-util-colors-orange-orange-500: #EF6820;
--color-util-colors-orange-orange-600: #E04F16;
--color-util-colors-orange-orange-700: #B93815;
--color-util-colors-orange-orange-100-transparent: #FDEAD700;
--color-util-colors-pink-pink-50: #FDF2FA;
--color-util-colors-pink-pink-100: #FCE7F6;
@ -602,5 +688,10 @@ html[data-theme="light"] {
--color-chatbot-bg: linear-gradient(180deg, rgba(249, 250, 251, 0.90) 0%, rgba(242, 244, 247, 0.90) 90.48%);
--color-chat-bubble-bg: linear-gradient(180deg, #FFF 0%, rgba(255, 255, 255, 0.60) 100%);
--color-third-party-Github: #1B1F24;
--color-third-party-Github-tertiary: #1B1F24;
--color-third-party-Github-secondary: #1B1F24;
--color-third-party-model-bg-openai: #E3E5E8;
--color-third-party-model-bg-anthropic: #EEEDE7;
--color-third-party-model-bg-default: #F9FAFB;
--color-workflow-process-bg: linear-gradient(90deg, rgba(200, 206, 218, 0.20) 0%, rgba(200, 206, 218, 0.04) 100%);
}

View File

@ -95,6 +95,7 @@ const vars = {
'components-checkbox-border-disabled': 'var(--color-components-checkbox-border-disabled)',
'components-checkbox-bg-unchecked': 'var(--color-components-checkbox-bg-unchecked)',
'components-checkbox-bg-unchecked-hover': 'var(--color-components-checkbox-bg-unchecked-hover)',
'components-checkbox-bg-disabled-checked': 'var(--color-components-checkbox-bg-disabled-checked)',
'components-radio-border-checked': 'var(--color-components-radio-border-checked)',
'components-radio-border-checked-hover': 'var(--color-components-radio-border-checked-hover)',
@ -135,6 +136,9 @@ const vars = {
'components-panel-on-panel-item-bg': 'var(--color-components-panel-on-panel-item-bg)',
'components-panel-on-panel-item-bg-hover': 'var(--color-components-panel-on-panel-item-bg-hover)',
'components-panel-on-panel-item-bg-alt': 'var(--color-components-panel-on-panel-item-bg-alt)',
'components-panel-on-panel-item-bg-transparent': 'var(--color-components-panel-on-panel-item-bg-transparent)',
'components-panel-on-panel-item-bg-hover-transparent': 'var(--color-components-panel-on-panel-item-bg-hover-transparent)',
'components-panel-on-panel-item-bg-destructive-hover-transparent': 'var(--color-components-panel-on-panel-item-bg-destructive-hover-transparent)',
'components-panel-bg-transparent': 'var(--color-components-panel-bg-transparent)',
@ -208,6 +212,8 @@ const vars = {
'components-actionbar-bg': 'var(--color-components-actionbar-bg)',
'components-actionbar-border': 'var(--color-components-actionbar-border)',
'components-actionbar-bg-accent': 'var(--color-components-actionbar-bg-accent)',
'components-actionbar-border-accent': 'var(--color-components-actionbar-border-accent)',
'components-dropzone-bg-alt': 'var(--color-components-dropzone-bg-alt)',
'components-dropzone-bg': 'var(--color-components-dropzone-bg)',
@ -228,6 +234,14 @@ const vars = {
'components-progress-gray-border': 'var(--color-components-progress-gray-border)',
'components-progress-gray-bg': 'var(--color-components-progress-gray-bg)',
'components-progress-warning-progress': 'var(--color-components-progress-warning-progress)',
'components-progress-warning-border': 'var(--color-components-progress-warning-border)',
'components-progress-warning-bg': 'var(--color-components-progress-warning-bg)',
'components-progress-error-progress': 'var(--color-components-progress-error-progress)',
'components-progress-error-border': 'var(--color-components-progress-error-border)',
'components-progress-error-bg': 'var(--color-components-progress-error-bg)',
'components-chat-input-audio-bg': 'var(--color-components-chat-input-audio-bg)',
'components-chat-input-audio-wave-default': 'var(--color-components-chat-input-audio-wave-default)',
'components-chat-input-bg-mask-1': 'var(--color-components-chat-input-bg-mask-1)',
@ -236,13 +250,70 @@ const vars = {
'components-chat-input-audio-wave-active': 'var(--color-components-chat-input-audio-wave-active)',
'components-chat-input-audio-bg-alt': 'var(--color-components-chat-input-audio-bg-alt)',
'components-Avatar-shape-fill-stop-0': 'var(--color-components-Avatar-shape-fill-stop-0)',
'components-Avatar-shape-fill-stop-100': 'var(--color-components-Avatar-shape-fill-stop-100)',
'components-avatar-shape-fill-stop-0': 'var(--color-components-avatar-shape-fill-stop-0)',
'components-avatar-shape-fill-stop-100': 'var(--color-components-avatar-shape-fill-stop-100)',
'components-Avatar-bg-mask-stop-0': 'var(--color-components-Avatar-bg-mask-stop-0)',
'components-Avatar-bg-mask-stop-100': 'var(--color-components-Avatar-bg-mask-stop-100)',
'components-avatar-bg-mask-stop-0': 'var(--color-components-avatar-bg-mask-stop-0)',
'components-avatar-bg-mask-stop-100': 'var(--color-components-avatar-bg-mask-stop-100)',
'components-Avatar-default-avatar-bg': 'var(--color-components-Avatar-default-avatar-bg)',
'components-avatar-default-avatar-bg': 'var(--color-components-avatar-default-avatar-bg)',
'components-avatar-mask-darkmode-dimmed': 'var(--color-components-avatar-mask-darkmode-dimmed)',
'components-label-gray': 'var(--color-components-label-gray)',
'components-premium-badge-blue-bg-stop-0': 'var(--color-components-premium-badge-blue-bg-stop-0)',
'components-premium-badge-blue-bg-stop-100': 'var(--color-components-premium-badge-blue-bg-stop-100)',
'components-premium-badge-blue-stroke-stop-0': 'var(--color-components-premium-badge-blue-stroke-stop-0)',
'components-premium-badge-blue-stroke-stop-100': 'var(--color-components-premium-badge-blue-stroke-stop-100)',
'components-premium-badge-blue-text-stop-0': 'var(--color-components-premium-badge-blue-text-stop-0)',
'components-premium-badge-blue-text-stop-100': 'var(--color-components-premium-badge-blue-text-stop-100)',
'components-premium-badge-blue-glow': 'var(--color-components-premium-badge-blue-glow)',
'components-premium-badge-blue-bg-stop-0-hover': 'var(--color-components-premium-badge-blue-bg-stop-0-hover)',
'components-premium-badge-blue-bg-stop-100-hover': 'var(--color-components-premium-badge-blue-bg-stop-100-hover)',
'components-premium-badge-blue-glow-hover': 'var(--color-components-premium-badge-blue-glow-hover)',
'components-premium-badge-blue-stroke-stop-0-hover': 'var(--color-components-premium-badge-blue-stroke-stop-0-hover)',
'components-premium-badge-blue-stroke-stop-100-hover': 'var(--color-components-premium-badge-blue-stroke-stop-100-hover)',
'components-premium-badge-highlight-stop-0': 'var(--color-components-premium-badge-highlight-stop-0)',
'components-premium-badge-highlight-stop-100': 'var(--color-components-premium-badge-highlight-stop-100)',
'components-premium-badge-indigo-bg-stop-0': 'var(--color-components-premium-badge-indigo-bg-stop-0)',
'components-premium-badge-indigo-bg-stop-100': 'var(--color-components-premium-badge-indigo-bg-stop-100)',
'components-premium-badge-indigo-stroke-stop-0': 'var(--color-components-premium-badge-indigo-stroke-stop-0)',
'components-premium-badge-indigo-stroke-stop-100': 'var(--color-components-premium-badge-indigo-stroke-stop-100)',
'components-premium-badge-indigo-text-stop-0': 'var(--color-components-premium-badge-indigo-text-stop-0)',
'components-premium-badge-indigo-text-stop-100': 'var(--color-components-premium-badge-indigo-text-stop-100)',
'components-premium-badge-indigo-glow': 'var(--color-components-premium-badge-indigo-glow)',
'components-premium-badge-indigo-glow-hover': 'var(--color-components-premium-badge-indigo-glow-hover)',
'components-premium-badge-indigo-bg-stop-0-hover': 'var(--color-components-premium-badge-indigo-bg-stop-0-hover)',
'components-premium-badge-indigo-bg-stop-100-hover': 'var(--color-components-premium-badge-indigo-bg-stop-100-hover)',
'components-premium-badge-indigo-stroke-stop-0-hover': 'var(--color-components-premium-badge-indigo-stroke-stop-0-hover)',
'components-premium-badge-indigo-stroke-stop-100-hover': 'var(--color-components-premium-badge-indigo-stroke-stop-100-hover)',
'components-premium-badge-grey-bg-stop-0': 'var(--color-components-premium-badge-grey-bg-stop-0)',
'components-premium-badge-grey-bg-stop-100': 'var(--color-components-premium-badge-grey-bg-stop-100)',
'components-premium-badge-grey-stroke-stop-0': 'var(--color-components-premium-badge-grey-stroke-stop-0)',
'components-premium-badge-grey-stroke-stop-100': 'var(--color-components-premium-badge-grey-stroke-stop-100)',
'components-premium-badge-grey-text-stop-0': 'var(--color-components-premium-badge-grey-text-stop-0)',
'components-premium-badge-grey-text-stop-100': 'var(--color-components-premium-badge-grey-text-stop-100)',
'components-premium-badge-grey-glow': 'var(--color-components-premium-badge-grey-glow)',
'components-premium-badge-grey-glow-hover': 'var(--color-components-premium-badge-grey-glow-hover)',
'components-premium-badge-grey-bg-stop-0-hover': 'var(--color-components-premium-badge-grey-bg-stop-0-hover)',
'components-premium-badge-grey-bg-stop-100-hover': 'var(--color-components-premium-badge-grey-bg-stop-100-hover)',
'components-premium-badge-grey-stroke-stop-0-hover': 'var(--color-components-premium-badge-grey-stroke-stop-0-hover)',
'components-premium-badge-grey-stroke-stop-100-hover': 'var(--color-components-premium-badge-grey-stroke-stop-100-hover)',
'components-premium-badge-orange-bg-stop-0': 'var(--color-components-premium-badge-orange-bg-stop-0)',
'components-premium-badge-orange-bg-stop-100': 'var(--color-components-premium-badge-orange-bg-stop-100)',
'components-premium-badge-orange-stroke-stop-0': 'var(--color-components-premium-badge-orange-stroke-stop-0)',
'components-premium-badge-orange-stroke-stop-100': 'var(--color-components-premium-badge-orange-stroke-stop-100)',
'components-premium-badge-orange-text-stop-0': 'var(--color-components-premium-badge-orange-text-stop-0)',
'components-premium-badge-orange-text-stop-100': 'var(--color-components-premium-badge-orange-text-stop-100)',
'components-premium-badge-orange-glow': 'var(--color-components-premium-badge-orange-glow)',
'components-premium-badge-orange-glow-hover': 'var(--color-components-premium-badge-orange-glow-hover)',
'components-premium-badge-orange-bg-stop-0-hover': 'var(--color-components-premium-badge-orange-bg-stop-0-hover)',
'components-premium-badge-orange-bg-stop-100-hover': 'var(--color-components-premium-badge-orange-bg-stop-100-hover)',
'components-premium-badge-orange-stroke-stop-0-hover': 'var(--color-components-premium-badge-orange-stroke-stop-0-hover)',
'components-premium-badge-orange-stroke-stop-100-hover': 'var(--color-components-premium-badge-orange-stroke-stop-100-hover)',
'text-primary': 'var(--color-text-primary)',
'text-secondary': 'var(--color-text-secondary)',
@ -265,6 +336,7 @@ const vars = {
'text-logo-text': 'var(--color-text-logo-text)',
'text-empty-state-icon': 'var(--color-text-empty-state-icon)',
'text-inverted': 'var(--color-text-inverted)',
'text-inverted-dimm': 'var(--color-text-inverted-dimm)',
'background-body': 'var(--color-background-body)',
'background-default-subtle': 'var(--color-background-default-subtle)',
@ -301,6 +373,7 @@ const vars = {
'background-overlay-alt': 'var(--color-background-overlay-alt)',
'background-surface-white': 'var(--color-background-surface-white)',
'background-overlay-destructive': 'var(--color-background-overlay-destructive)',
'background-overlay-backdrop': 'var(--color-background-overlay-backdrop)',
'shadow-shadow-1': 'var(--color-shadow-shadow-1)',
'shadow-shadow-3': 'var(--color-shadow-shadow-3)',
@ -316,6 +389,7 @@ const vars = {
'workflow-block-border': 'var(--color-workflow-block-border)',
'workflow-block-parma-bg': 'var(--color-workflow-block-parma-bg)',
'workflow-block-bg': 'var(--color-workflow-block-bg)',
'workflow-block-bg-transparent': 'var(--color-workflow-block-bg-transparent)',
'workflow-block-border-highlight': 'var(--color-workflow-block-border-highlight)',
'workflow-canvas-workflow-dot-color': 'var(--color-workflow-canvas-workflow-dot-color)',
@ -324,6 +398,17 @@ const vars = {
'workflow-link-line-active': 'var(--color-workflow-link-line-active)',
'workflow-link-line-normal': 'var(--color-workflow-link-line-normal)',
'workflow-link-line-handle': 'var(--color-workflow-link-line-handle)',
'workflow-link-line-normal-transparent': 'var(--color-workflow-link-line-normal-transparent)',
'workflow-link-line-failure-active': 'var(--color-workflow-link-line-failure-active)',
'workflow-link-line-failure-handle': 'var(--color-workflow-link-line-failure-handle)',
'workflow-link-line-failure-button-bg': 'var(--color-workflow-link-line-failure-button-bg)',
'workflow-link-line-failure-button-hover': 'var(--color-workflow-link-line-failure-button-hover)',
'workflow-link-line-success-active': 'var(--color-workflow-link-line-success-active)',
'workflow-link-line-success-handle': 'var(--color-workflow-link-line-success-handle)',
'workflow-link-line-error-active': 'var(--color-workflow-link-line-error-active)',
'workflow-link-line-error-handle': 'var(--color-workflow-link-line-error-handle)',
'workflow-minimap-bg': 'var(--color-workflow-minimap-bg)',
'workflow-minimap-block': 'var(--color-workflow-minimap-block)',
@ -424,6 +509,7 @@ const vars = {
'util-colors-orange-orange-500': 'var(--color-util-colors-orange-orange-500)',
'util-colors-orange-orange-600': 'var(--color-util-colors-orange-orange-600)',
'util-colors-orange-orange-700': 'var(--color-util-colors-orange-orange-700)',
'util-colors-orange-orange-100-transparent': 'var(--color-util-colors-orange-orange-100-transparent)',
'util-colors-pink-pink-50': 'var(--color-util-colors-pink-pink-50)',
'util-colors-pink-pink-100': 'var(--color-util-colors-pink-pink-100)',
@ -599,6 +685,11 @@ const vars = {
'third-party-LangChain': 'var(--color-third-party-LangChain)',
'third-party-Langfuse': 'var(--color-third-party-Langfuse)',
'third-party-Github': 'var(--color-third-party-Github)',
'third-party-Github-tertiary': 'var(--color-third-party-Github-tertiary)',
'third-party-Github-secondary': 'var(--color-third-party-Github-secondary)',
'third-party-model-bg-openai': 'var(--color-third-party-model-bg-openai)',
'third-party-model-bg-anthropic': 'var(--color-third-party-model-bg-anthropic)',
'third-party-model-bg-default': 'var(--color-third-party-model-bg-default)',
}
export default vars