feat: add plugin readme

This commit is contained in:
yessenia
2025-08-29 20:30:20 +08:00
parent 9aa43c9165
commit 4b4ec3438f
42 changed files with 565 additions and 119 deletions

View File

@ -128,13 +128,13 @@ const DetailHeader = ({
return false
if (!autoUpgradeInfo || !isFromMarketplace)
return false
if(autoUpgradeInfo.strategy_setting === 'disabled')
if (autoUpgradeInfo.strategy_setting === 'disabled')
return false
if(autoUpgradeInfo.upgrade_mode === AUTO_UPDATE_MODE.update_all)
if (autoUpgradeInfo.upgrade_mode === AUTO_UPDATE_MODE.update_all)
return true
if(autoUpgradeInfo.upgrade_mode === AUTO_UPDATE_MODE.partial && autoUpgradeInfo.include_plugins.includes(plugin_id))
if (autoUpgradeInfo.upgrade_mode === AUTO_UPDATE_MODE.partial && autoUpgradeInfo.include_plugins.includes(plugin_id))
return true
if(autoUpgradeInfo.upgrade_mode === AUTO_UPDATE_MODE.exclude && !autoUpgradeInfo.exclude_plugins.includes(plugin_id))
if (autoUpgradeInfo.upgrade_mode === AUTO_UPDATE_MODE.exclude && !autoUpgradeInfo.exclude_plugins.includes(plugin_id))
return true
return false
}, [autoUpgradeInfo, plugin_id, isFromMarketplace])
@ -331,6 +331,7 @@ const DetailHeader = ({
pluginPayload={{
provider: provider?.name || '',
category: AuthCategory.tool,
detail,
}}
/>
)

View File

@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'
import { useBoolean } from 'ahooks'
import copy from 'copy-to-clipboard'
import { RiClipboardLine, RiDeleteBinLine, RiEditLine, RiLoginCircleLine } from '@remixicon/react'
import type { EndpointListItem } from '../types'
import type { EndpointListItem, PluginDetail } from '../types'
import EndpointModal from './endpoint-modal'
import { NAME_FIELD } from './utils'
import { addDefaultValue, toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
@ -22,11 +22,13 @@ import {
} from '@/service/use-endpoints'
type Props = {
pluginDetail: PluginDetail
data: EndpointListItem
handleChange: () => void
}
const EndpointCard = ({
pluginDetail,
data,
handleChange,
}: Props) => {
@ -206,10 +208,11 @@ const EndpointCard = ({
)}
{isShowEndpointModal && (
<EndpointModal
formSchemas={formSchemas}
formSchemas={formSchemas as any}
defaultValues={formValue}
onCancel={hideEndpointModalConfirm}
onSaved={handleUpdate}
pluginDetail={pluginDetail}
/>
)}
</div>

View File

@ -102,14 +102,16 @@ const EndpointList = ({ detail }: Props) => {
key={index}
data={item}
handleChange={() => invalidateEndpointList(detail.plugin_id)}
pluginDetail={detail}
/>
))}
</div>
{isShowEndpointModal && (
<EndpointModal
formSchemas={formSchemas}
formSchemas={formSchemas as any}
onCancel={hideEndpointModal}
onSaved={handleCreate}
pluginDetail={detail}
/>
)}
</div>

View File

@ -10,12 +10,16 @@ import Form from '@/app/components/header/account-setting/model-provider-page/mo
import Toast from '@/app/components/base/toast'
import { useRenderI18nObject } from '@/hooks/use-i18n'
import cn from '@/utils/classnames'
import { ReadmeEntrance } from '../readme-panel/entrance'
import type { PluginDetail } from '../types'
import type { FormSchema } from '../../base/form/types'
type Props = {
formSchemas: any
formSchemas: FormSchema[]
defaultValues?: any
onCancel: () => void
onSaved: (value: Record<string, any>) => void
pluginDetail: PluginDetail
}
const extractDefaultValues = (schemas: any[]) => {
@ -32,6 +36,7 @@ const EndpointModal: FC<Props> = ({
defaultValues = {},
onCancel,
onSaved,
pluginDetail,
}) => {
const getValueFromI18nObject = useRenderI18nObject()
const { t } = useTranslation()
@ -43,7 +48,7 @@ const EndpointModal: FC<Props> = ({
const handleSave = () => {
for (const field of formSchemas) {
if (field.required && !tempCredential[field.name]) {
Toast.notify({ type: 'error', message: t('common.errorMsg.fieldRequired', { field: getValueFromI18nObject(field.label) }) })
Toast.notify({ type: 'error', message: t('common.errorMsg.fieldRequired', { field: typeof field.label === 'string' ? field.label : getValueFromI18nObject(field.label as Record<string, string>) }) })
return
}
}
@ -84,6 +89,7 @@ const EndpointModal: FC<Props> = ({
</ActionButton>
</div>
<div className='system-xs-regular mt-0.5 text-text-tertiary'>{t('plugin.detailPanel.endpointModalDesc')}</div>
<ReadmeEntrance pluginDetail={pluginDetail} className='px-0 pt-3' />
</div>
<div className='grow overflow-y-auto'>
<div className='px-4 py-2'>
@ -92,7 +98,7 @@ const EndpointModal: FC<Props> = ({
onChange={(v) => {
setTempCredential(v)
}}
formSchemas={formSchemas}
formSchemas={formSchemas as any}
isEditMode={true}
showOnVariableMap={{}}
validating={false}

View File

@ -3,7 +3,7 @@ import Drawer from '@/app/components/base/drawer'
import { PluginCategoryEnum, type PluginDetail } from '@/app/components/plugins/types'
import cn from '@/utils/classnames'
import type { FC } from 'react'
import { useEffect } from 'react'
import { useCallback, useEffect } from 'react'
import ActionList from './action-list'
import AgentStrategyList from './agent-strategy-list'
import DatasourceActionList from './datasource-action-list'
@ -11,8 +11,9 @@ import DetailHeader from './detail-header'
import EndpointList from './endpoint-list'
import ModelList from './model-list'
import { SubscriptionList } from './subscription-list'
import { usePluginStore } from './subscription-list/store'
import { usePluginStore } from './store'
import { TriggerEventsList } from './trigger/event-list'
import { ReadmeEntrance } from '../readme-panel/entrance'
type Props = {
detail?: PluginDetail
@ -25,19 +26,22 @@ const PluginDetailPanel: FC<Props> = ({
onUpdate,
onHide,
}) => {
const handleUpdate = (isDelete = false) => {
const handleUpdate = useCallback((isDelete = false) => {
if (isDelete)
onHide()
onUpdate()
}
}, [onHide, onUpdate])
const { setDetail } = usePluginStore()
useEffect(() => {
setDetail(!detail ? undefined : {
plugin_id: detail.plugin_id,
provider: `${detail.plugin_id}/${detail.declaration.name}`,
plugin_unique_identifier: detail.plugin_unique_identifier || '',
declaration: detail.declaration,
name: detail.name,
id: detail.id,
})
}, [detail])
@ -56,23 +60,24 @@ const PluginDetailPanel: FC<Props> = ({
>
{detail && (
<>
<DetailHeader
detail={detail}
onHide={onHide}
onUpdate={handleUpdate}
/>
<DetailHeader detail={detail} onUpdate={handleUpdate} onHide={onHide} />
<div className='grow overflow-y-auto'>
{detail.declaration.category === PluginCategoryEnum.trigger && (
<>
<SubscriptionList />
<TriggerEventsList />
</>
)}
{!!detail.declaration.tool && <ActionList detail={detail} />}
{!!detail.declaration.agent_strategy && <AgentStrategyList detail={detail} />}
{!!detail.declaration.endpoint && <EndpointList detail={detail} />}
{!!detail.declaration.model && <ModelList detail={detail} />}
{!!detail.declaration.datasource && <DatasourceActionList detail={detail} />}
<div className='flex min-h-full flex-col'>
<div className='flex-1'>
{detail.declaration.category === PluginCategoryEnum.trigger && (
<>
<SubscriptionList />
<TriggerEventsList />
</>
)}
{!!detail.declaration.tool && <ActionList detail={detail} />}
{!!detail.declaration.agent_strategy && <AgentStrategyList detail={detail} />}
{!!detail.declaration.endpoint && <EndpointList detail={detail} />}
{!!detail.declaration.model && <ModelList detail={detail} />}
{!!detail.declaration.datasource && <DatasourceActionList detail={detail} />}
</div>
<ReadmeEntrance pluginDetail={detail} className='mt-auto' />
</div>
</div>
</>
)}

View File

@ -0,0 +1,14 @@
import { create } from 'zustand'
import type { PluginDetail } from '../types'
export type SimpleDetail = Pick<PluginDetail, 'plugin_id' | 'declaration' | 'name' | 'plugin_unique_identifier' | 'id'> & { provider: string }
type Shape = {
detail: SimpleDetail | undefined
setDetail: (detail?: SimpleDetail) => void
}
export const usePluginStore = create<Shape>(set => ({
detail: undefined,
setDetail: (detail?: SimpleDetail) => set({ detail }),
}))

View File

@ -23,7 +23,8 @@ import { debounce } from 'lodash-es'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import LogViewer from '../log-viewer'
import { usePluginStore, usePluginSubscriptionStore } from '../store'
import { usePluginSubscriptionStore } from '../store'
import { usePluginStore } from '../../store'
type Props = {
onClose: () => void

View File

@ -14,7 +14,7 @@ import { useBoolean } from 'ahooks'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { SupportedCreationMethods } from '../../../types'
import { usePluginStore } from '../store'
import { usePluginStore } from '../../store'
import { useSubscriptionList } from '../use-subscription-list'
import { CommonCreateModal } from './common-modal'
import { OAuthClientSettingsModal } from './oauth-client'

View File

@ -20,7 +20,7 @@ import {
} from '@remixicon/react'
import React, { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { usePluginStore } from '../store'
import { usePluginStore } from '../../store'
type Props = {
oauthConfig?: TriggerOAuthConfig

View File

@ -1,17 +1,4 @@
import { create } from 'zustand'
import type { PluginDetail } from '../../types'
export type SimpleDetail = Pick<PluginDetail, 'plugin_id' | 'declaration' | 'name'> & { provider: string }
type Shape = {
detail: SimpleDetail | undefined
setDetail: (detail?: SimpleDetail) => void
}
export const usePluginStore = create<Shape>(set => ({
detail: undefined,
setDetail: (detail?: SimpleDetail) => set({ detail }),
}))
type ShapeSubscription = {
refresh?: () => void

View File

@ -1,6 +1,7 @@
import { useEffect } from 'react'
import { useTriggerSubscriptions } from '@/service/use-triggers'
import { usePluginStore, usePluginSubscriptionStore } from './store'
import { usePluginStore } from '../store'
import { usePluginSubscriptionStore } from './store'
export const useSubscriptionList = () => {
const detail = usePluginStore(state => state.detail)

View File

@ -40,6 +40,7 @@ import {
AuthCategory,
PluginAuthInAgent,
} from '@/app/components/plugins/plugin-auth'
import { ReadmeEntrance } from '../../readme-panel/entrance'
type Props = {
disabled?: boolean
@ -272,7 +273,10 @@ const ToolSelector: FC<Props> = ({
{/* base form */}
<div className='flex flex-col gap-3 px-4 py-2'>
<div className='flex flex-col gap-1'>
<div className='system-sm-semibold flex h-6 items-center text-text-secondary'>{t('plugin.detailPanel.toolSelector.toolLabel')}</div>
<div className='system-sm-semibold flex h-6 items-center justify-between text-text-secondary'>
{t('plugin.detailPanel.toolSelector.toolLabel')}
<ReadmeEntrance pluginDetail={currentProvider as any} showShortTip className='pb-0' />
</div>
<ToolPicker
placement='bottom'
offset={offset}
@ -314,6 +318,7 @@ const ToolSelector: FC<Props> = ({
pluginPayload={{
provider: currentProvider.name,
category: AuthCategory.tool,
detail: currentProvider as any,
}}
credentialId={value?.credential_id}
onAuthorizationItemClick={handleAuthorizationItemClick}

View File

@ -5,7 +5,7 @@ import { useTriggerProviderInfo } from '@/service/use-triggers'
import cn from '@/utils/classnames'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { usePluginStore } from '../subscription-list/store'
import { usePluginStore } from '../store'
import { EventDetailDrawer } from './event-detail-drawer'
type TriggerEventCardProps = {