mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 08:58:09 +08:00
merge main
This commit is contained in:
@ -47,7 +47,7 @@ const AccessControlDialog = ({
|
||||
>
|
||||
<Dialog.Panel className={cn('relative h-auto min-h-[323px] w-[600px] overflow-y-auto rounded-2xl bg-components-panel-bg p-0 shadow-xl transition-all', className)}>
|
||||
<div onClick={() => close()} className="absolute right-5 top-5 z-10 flex h-8 w-8 cursor-pointer items-center justify-center">
|
||||
<RiCloseLine className='h-5 w-5' />
|
||||
<RiCloseLine className='h-5 w-5 text-text-tertiary' />
|
||||
</div>
|
||||
{children}
|
||||
</Dialog.Panel>
|
||||
|
||||
@ -72,7 +72,7 @@ export default function AccessControl(props: AccessControlProps) {
|
||||
</div>
|
||||
<div className='flex flex-col gap-y-1 px-6 pb-3'>
|
||||
<div className='leading-6'>
|
||||
<p className='system-sm-medium'>{t('app.accessControlDialog.accessLabel')}</p>
|
||||
<p className='system-sm-medium text-text-tertiary'>{t('app.accessControlDialog.accessLabel')}</p>
|
||||
</div>
|
||||
<AccessControlItem type={AccessMode.ORGANIZATION}>
|
||||
<div className='flex items-center p-3'>
|
||||
|
||||
@ -35,6 +35,22 @@ export type FileUpload = {
|
||||
number_limits?: number
|
||||
transfer_methods?: TransferMethod[]
|
||||
}
|
||||
document?: EnabledOrDisabled & {
|
||||
number_limits?: number
|
||||
transfer_methods?: TransferMethod[]
|
||||
}
|
||||
audio?: EnabledOrDisabled & {
|
||||
number_limits?: number
|
||||
transfer_methods?: TransferMethod[]
|
||||
}
|
||||
video?: EnabledOrDisabled & {
|
||||
number_limits?: number
|
||||
transfer_methods?: TransferMethod[]
|
||||
}
|
||||
custom?: EnabledOrDisabled & {
|
||||
number_limits?: number
|
||||
transfer_methods?: TransferMethod[]
|
||||
}
|
||||
allowed_file_types?: string[]
|
||||
allowed_file_extensions?: string[]
|
||||
allowed_file_upload_methods?: TransferMethod[]
|
||||
|
||||
@ -580,11 +580,30 @@ The text generation application offers non-session support and is ideal for tran
|
||||
- `default` (string) Default value
|
||||
- `options` (array[string]) Option values
|
||||
- `file_upload` (object) File upload configuration
|
||||
- `image` (object) Image settings
|
||||
Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Image number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods, remote_url, local_file, must choose one
|
||||
- `document` (object) Document settings
|
||||
Currently only supports document types: `txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Document number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `image` (object) Image settings
|
||||
Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Image number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `audio` (object) Audio settings
|
||||
Currently only supports audio types: `mp3`, `m4a`, `wav`, `webm`, `amr`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Audio number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `video` (object) Video settings
|
||||
Currently only supports video types: `mp4`, `mov`, `mpeg`, `mpga`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Video number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `custom` (object) Custom settings
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Custom number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `system_parameters` (object) System parameters
|
||||
- `file_size_limit` (int) Document upload size limit (MB)
|
||||
- `image_file_size_limit` (int) Image file upload size limit (MB)
|
||||
|
||||
@ -578,11 +578,30 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `default` (string) デフォルト値
|
||||
- `options` (array[string]) オプション値
|
||||
- `file_upload` (object) ファイルアップロード設定
|
||||
- `image` (object) 画像設定
|
||||
現在は画像タイプのみ対応:`png`、`jpg`、`jpeg`、`webp`、`gif`
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) 画像数制限、デフォルトは3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト、remote_url、local_file、いずれかを選択
|
||||
- `document` (object) ドキュメント設定
|
||||
現在サポートされているドキュメントタイプ:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) ドキュメント数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `image` (object) 画像設定
|
||||
現在サポートされている画像タイプ:`png`, `jpg`, `jpeg`, `webp`, `gif`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) 画像数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `audio` (object) オーディオ設定
|
||||
現在サポートされているオーディオタイプ:`mp3`, `m4a`, `wav`, `webm`, `amr`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) オーディオ数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `video` (object) ビデオ設定
|
||||
現在サポートされているビデオタイプ:`mp4`, `mov`, `mpeg`, `mpga`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) ビデオ数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `custom` (object) カスタム設定
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) カスタム数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `system_parameters` (object) システムパラメータ
|
||||
- `file_size_limit` (int) ドキュメントアップロードサイズ制限(MB)
|
||||
- `image_file_size_limit` (int) 画像ファイルアップロードサイズ制限(MB)
|
||||
|
||||
@ -552,11 +552,30 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
- `default` (string) 默认值
|
||||
- `options` (array[string]) 选项值
|
||||
- `file_upload` (object) 文件上传配置
|
||||
- `image` (object) 图片设置
|
||||
当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`
|
||||
- `enabled` (bool) 是否开启
|
||||
- `number_limits` (int) 图片数量限制,默认 3
|
||||
- `transfer_methods` (array[string]) 传递方式列表,remote_url , local_file,必选一个
|
||||
- `document` (object) 文档设置
|
||||
当前仅支持文档类型:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 文档数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `image` (object) 图片设置
|
||||
当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 图片数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `audio` (object) 音频设置
|
||||
当前仅支持音频类型:`mp3`, `m4a`, `wav`, `webm`, `amr`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 音频数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `video` (object) 视频设置
|
||||
当前仅支持视频类型:`mp4`, `mov`, `mpeg`, `mpga`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 视频数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `custom` (object) 自定义设置
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 自定义数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `system_parameters` (object) 系统参数
|
||||
- `file_size_limit` (int) 文档上传大小限制 (MB)
|
||||
- `image_file_size_limit` (int) 图片文件上传大小限制(MB)
|
||||
|
||||
@ -1197,11 +1197,30 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
- `default` (string) Default value
|
||||
- `options` (array[string]) Option values
|
||||
- `file_upload` (object) File upload configuration
|
||||
- `image` (object) Image settings
|
||||
Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Image number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods, remote_url, local_file, must choose one
|
||||
- `document` (object) Document settings
|
||||
Currently only supports document types: `txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Document number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `image` (object) Image settings
|
||||
Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Image number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `audio` (object) Audio settings
|
||||
Currently only supports audio types: `mp3`, `m4a`, `wav`, `webm`, `amr`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Audio number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `video` (object) Video settings
|
||||
Currently only supports video types: `mp4`, `mov`, `mpeg`, `mpga`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Video number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `custom` (object) Custom settings
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Custom number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `system_parameters` (object) System parameters
|
||||
- `file_size_limit` (int) Document upload size limit (MB)
|
||||
- `image_file_size_limit` (int) Image file upload size limit (MB)
|
||||
|
||||
@ -1197,11 +1197,30 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `default` (string) デフォルト値
|
||||
- `options` (array[string]) オプション値
|
||||
- `file_upload` (object) ファイルアップロード設定
|
||||
- `image` (object) 画像設定
|
||||
現在サポートされている画像タイプ:`png`, `jpg`, `jpeg`, `webp`, `gif`
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) 画像数の制限、デフォルトは3
|
||||
- `transfer_methods` (array[string]) 転送方法のリスト、remote_url, local_file、いずれかを選択する必要があります
|
||||
- `document` (object) ドキュメント設定
|
||||
現在サポートされているドキュメントタイプ:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) ドキュメント数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `image` (object) 画像設定
|
||||
現在サポートされている画像タイプ:`png`, `jpg`, `jpeg`, `webp`, `gif`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) 画像数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `audio` (object) オーディオ設定
|
||||
現在サポートされているオーディオタイプ:`mp3`, `m4a`, `wav`, `webm`, `amr`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) オーディオ数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `video` (object) ビデオ設定
|
||||
現在サポートされているビデオタイプ:`mp4`, `mov`, `mpeg`, `mpga`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) ビデオ数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `custom` (object) カスタム設定
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) カスタム数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `system_parameters` (object) システムパラメータ
|
||||
- `file_size_limit` (int) ドキュメントアップロードサイズ制限(MB)
|
||||
- `image_file_size_limit` (int) 画像ファイルアップロードサイズ制限(MB)
|
||||
|
||||
@ -1229,11 +1229,30 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
- `default` (string) 默认值
|
||||
- `options` (array[string]) 选项值
|
||||
- `file_upload` (object) 文件上传配置
|
||||
- `image` (object) 图片设置
|
||||
当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`
|
||||
- `enabled` (bool) 是否开启
|
||||
- `number_limits` (int) 图片数量限制,默认 3
|
||||
- `transfer_methods` (array[string]) 传递方式列表,remote_url , local_file,必选一个
|
||||
- `document` (object) 文档设置
|
||||
当前仅支持文档类型:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 文档数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `image` (object) 图片设置
|
||||
当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 图片数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `audio` (object) 音频设置
|
||||
当前仅支持音频类型:`mp3`, `m4a`, `wav`, `webm`, `amr`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 音频数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `video` (object) 视频设置
|
||||
当前仅支持视频类型:`mp4`, `mov`, `mpeg`, `mpga`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 视频数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `custom` (object) 自定义设置
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 自定义数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `system_parameters` (object) 系统参数
|
||||
- `file_size_limit` (int) Document upload size limit (MB)
|
||||
- `image_file_size_limit` (int) Image file upload size limit (MB)
|
||||
|
||||
@ -1234,11 +1234,30 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
- `default` (string) Default value
|
||||
- `options` (array[string]) Option values
|
||||
- `file_upload` (object) File upload configuration
|
||||
- `image` (object) Image settings
|
||||
Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Image number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods, remote_url, local_file, must choose one
|
||||
- `document` (object) Document settings
|
||||
Currently only supports document types: `txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Document number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `image` (object) Image settings
|
||||
Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Image number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `audio` (object) Audio settings
|
||||
Currently only supports audio types: `mp3`, `m4a`, `wav`, `webm`, `amr`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Audio number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `video` (object) Video settings
|
||||
Currently only supports video types: `mp4`, `mov`, `mpeg`, `mpga`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Video number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `custom` (object) Custom settings
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Custom number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `system_parameters` (object) System parameters
|
||||
- `file_size_limit` (int) Document upload size limit (MB)
|
||||
- `image_file_size_limit` (int) Image file upload size limit (MB)
|
||||
|
||||
@ -1224,12 +1224,31 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `required` (bool) 必須かどうか
|
||||
- `default` (string) デフォルト値
|
||||
- `options` (array[string]) オプション値
|
||||
- `file_upload` (object) ファイルアップロード構成
|
||||
- `image` (object) 画像設定
|
||||
現在サポートされている画像タイプ:`png`, `jpg`, `jpeg`, `webp`, `gif`
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) 画像数の制限、デフォルトは3
|
||||
- `transfer_methods` (array[string]) 転送方法のリスト、remote_url, local_file、いずれかを選択する必要があります
|
||||
- `file_upload` (object) ファイルアップロード設定
|
||||
- `document` (object) ドキュメント設定
|
||||
現在サポートされているドキュメントタイプ:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) ドキュメント数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `image` (object) 画像設定
|
||||
現在サポートされている画像タイプ:`png`, `jpg`, `jpeg`, `webp`, `gif`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) 画像数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `audio` (object) オーディオ設定
|
||||
現在サポートされているオーディオタイプ:`mp3`, `m4a`, `wav`, `webm`, `amr`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) オーディオ数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `video` (object) ビデオ設定
|
||||
現在サポートされているビデオタイプ:`mp4`, `mov`, `mpeg`, `mpga`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) ビデオ数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `custom` (object) カスタム設定
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) カスタム数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `system_parameters` (object) システムパラメータ
|
||||
- `file_size_limit` (int) ドキュメントアップロードサイズ制限(MB)
|
||||
- `image_file_size_limit` (int) 画像ファイルアップロードサイズ制限(MB)
|
||||
|
||||
@ -1237,11 +1237,30 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
- `default` (string) 默认值
|
||||
- `options` (array[string]) 选项值
|
||||
- `file_upload` (object) 文件上传配置
|
||||
- `image` (object) 图片设置
|
||||
当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`
|
||||
- `enabled` (bool) 是否开启
|
||||
- `number_limits` (int) 图片数量限制,默认 3
|
||||
- `transfer_methods` (array[string]) 传递方式列表,remote_url , local_file,必选一个
|
||||
- `document` (object) 文档设置
|
||||
当前仅支持文档类型:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 文档数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `image` (object) 图片设置
|
||||
当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 图片数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `audio` (object) 音频设置
|
||||
当前仅支持音频类型:`mp3`, `m4a`, `wav`, `webm`, `amr`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 音频数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `video` (object) 视频设置
|
||||
当前仅支持视频类型:`mp4`, `mov`, `mpeg`, `mpga`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 视频数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `custom` (object) 自定义设置
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 自定义数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `system_parameters` (object) 系统参数
|
||||
- `file_size_limit` (int) 文档上传大小限制 (MB)
|
||||
- `image_file_size_limit` (int) 图片文件上传大小限制(MB)
|
||||
|
||||
@ -690,11 +690,30 @@ Workflow applications offers non-session support and is ideal for translation, a
|
||||
- `default` (string) Default value
|
||||
- `options` (array[string]) Option values
|
||||
- `file_upload` (object) File upload configuration
|
||||
- `image` (object) Image settings
|
||||
Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Image number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods, remote_url, local_file, must choose one
|
||||
- `document` (object) Document settings
|
||||
Currently only supports document types: `txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Document number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `image` (object) Image settings
|
||||
Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Image number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `audio` (object) Audio settings
|
||||
Currently only supports audio types: `mp3`, `m4a`, `wav`, `webm`, `amr`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Audio number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `video` (object) Video settings
|
||||
Currently only supports video types: `mp4`, `mov`, `mpeg`, `mpga`.
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Video number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `custom` (object) Custom settings
|
||||
- `enabled` (bool) Whether it is enabled
|
||||
- `number_limits` (int) Custom number limit, default is 3
|
||||
- `transfer_methods` (array[string]) List of transfer methods: `remote_url`, `local_file`. Must choose one.
|
||||
- `system_parameters` (object) System parameters
|
||||
- `file_size_limit` (int) Document upload size limit (MB)
|
||||
- `image_file_size_limit` (int) Image file upload size limit (MB)
|
||||
|
||||
@ -691,11 +691,30 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
|
||||
- `default` (string) デフォルト値
|
||||
- `options` (array[string]) オプション値
|
||||
- `file_upload` (object) ファイルアップロード設定
|
||||
- `image` (object) 画像設定
|
||||
現在サポートされている画像タイプのみ:`png`, `jpg`, `jpeg`, `webp`, `gif`
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) 画像数の制限、デフォルトは3
|
||||
- `transfer_methods` (array[string]) 転送方法のリスト、remote_url, local_file、いずれかを選択する必要があります
|
||||
- `document` (object) ドキュメント設定
|
||||
現在サポートされているドキュメントタイプ:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) ドキュメント数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `image` (object) 画像設定
|
||||
現在サポートされている画像タイプ:`png`, `jpg`, `jpeg`, `webp`, `gif`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) 画像数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `audio` (object) オーディオ設定
|
||||
現在サポートされているオーディオタイプ:`mp3`, `m4a`, `wav`, `webm`, `amr`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) オーディオ数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `video` (object) ビデオ設定
|
||||
現在サポートされているビデオタイプ:`mp4`, `mov`, `mpeg`, `mpga`。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) ビデオ数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `custom` (object) カスタム設定
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `number_limits` (int) カスタム数の上限。デフォルトは 3
|
||||
- `transfer_methods` (array[string]) 転送方法リスト:`remote_url`, `local_file`。いずれかを選択する必要があります。
|
||||
- `system_parameters` (object) システムパラメータ
|
||||
- `file_size_limit` (int) ドキュメントアップロードサイズ制限(MB)
|
||||
- `image_file_size_limit` (int) 画像ファイルアップロードサイズ制限(MB)
|
||||
|
||||
@ -678,11 +678,30 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
- `default` (string) 默认值
|
||||
- `options` (array[string]) 选项值
|
||||
- `file_upload` (object) 文件上传配置
|
||||
- `image` (object) 图片设置
|
||||
当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`
|
||||
- `enabled` (bool) 是否开启
|
||||
- `number_limits` (int) 图片数量限制,默认 3
|
||||
- `transfer_methods` (array[string]) 传递方式列表,remote_url , local_file,必选一个
|
||||
- `document` (object) 文档设置
|
||||
当前仅支持文档类型:`txt`, `md`, `markdown`, `pdf`, `html`, `xlsx`, `xls`, `docx`, `csv`, `eml`, `msg`, `pptx`, `ppt`, `xml`, `epub`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 文档数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `image` (object) 图片设置
|
||||
当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 图片数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `audio` (object) 音频设置
|
||||
当前仅支持音频类型:`mp3`, `m4a`, `wav`, `webm`, `amr`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 音频数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `video` (object) 视频设置
|
||||
当前仅支持视频类型:`mp4`, `mov`, `mpeg`, `mpga`。
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 视频数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `custom` (object) 自定义设置
|
||||
- `enabled` (bool) 是否启用
|
||||
- `number_limits` (int) 自定义数量限制,默认为 3
|
||||
- `transfer_methods` (array[string]) 传输方式列表:`remote_url`, `local_file`,必须选择一个。
|
||||
- `system_parameters` (object) 系统参数
|
||||
- `file_size_limit` (int) 文档上传大小限制 (MB)
|
||||
- `image_file_size_limit` (int) 图片文件上传大小限制(MB)
|
||||
|
||||
105
web/app/components/plugins/base/deprecation-notice.tsx
Normal file
105
web/app/components/plugins/base/deprecation-notice.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
import React, { useMemo } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import Link from 'next/link'
|
||||
import cn from '@/utils/classnames'
|
||||
import { RiAlertFill } from '@remixicon/react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { snakeCase2CamelCase } from '@/utils/format'
|
||||
import { useMixedTranslation } from '../marketplace/hooks'
|
||||
|
||||
type DeprecationNoticeProps = {
|
||||
status: 'deleted' | 'active'
|
||||
deprecatedReason: string
|
||||
alternativePluginId: string
|
||||
alternativePluginURL: string
|
||||
locale?: string
|
||||
className?: string
|
||||
innerWrapperClassName?: string
|
||||
iconWrapperClassName?: string
|
||||
textClassName?: string
|
||||
}
|
||||
|
||||
const i18nPrefix = 'plugin.detailPanel.deprecation'
|
||||
|
||||
const DeprecationNotice: FC<DeprecationNoticeProps> = ({
|
||||
status,
|
||||
deprecatedReason,
|
||||
alternativePluginId,
|
||||
alternativePluginURL,
|
||||
locale,
|
||||
className,
|
||||
innerWrapperClassName,
|
||||
iconWrapperClassName,
|
||||
textClassName,
|
||||
}) => {
|
||||
const { t } = useMixedTranslation(locale)
|
||||
|
||||
const deprecatedReasonKey = useMemo(() => {
|
||||
if (!deprecatedReason) return ''
|
||||
return snakeCase2CamelCase(deprecatedReason)
|
||||
}, [deprecatedReason])
|
||||
|
||||
// Check if the deprecatedReasonKey exists in i18n
|
||||
const hasValidDeprecatedReason = useMemo(() => {
|
||||
if (!deprecatedReason || !deprecatedReasonKey) return false
|
||||
|
||||
// Define valid reason keys that exist in i18n
|
||||
const validReasonKeys = ['businessAdjustments', 'ownershipTransferred', 'noMaintainer']
|
||||
return validReasonKeys.includes(deprecatedReasonKey)
|
||||
}, [deprecatedReason, deprecatedReasonKey])
|
||||
|
||||
if (status !== 'deleted')
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className={cn('w-full', className)}>
|
||||
<div className={cn(
|
||||
'relative flex items-start gap-x-0.5 overflow-hidden rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-2 shadow-xs shadow-shadow-shadow-3 backdrop-blur-[5px]',
|
||||
innerWrapperClassName,
|
||||
)}>
|
||||
<div className='absolute left-0 top-0 -z-10 h-full w-full bg-toast-warning-bg opacity-40' />
|
||||
<div className={cn('flex size-6 shrink-0 items-center justify-center', iconWrapperClassName)}>
|
||||
<RiAlertFill className='size-4 text-text-warning-secondary' />
|
||||
</div>
|
||||
<div className={cn('system-xs-regular grow py-1 text-text-primary', textClassName)}>
|
||||
{
|
||||
hasValidDeprecatedReason && alternativePluginId && (
|
||||
<Trans
|
||||
t={t}
|
||||
i18nKey={`${i18nPrefix}.fullMessage`}
|
||||
components={{
|
||||
CustomLink: (
|
||||
<Link
|
||||
href={alternativePluginURL}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
className='underline'
|
||||
/>
|
||||
),
|
||||
}}
|
||||
values={{
|
||||
deprecatedReason: t(`${i18nPrefix}.reason.${deprecatedReasonKey}`),
|
||||
alternativePluginId,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
hasValidDeprecatedReason && !alternativePluginId && (
|
||||
<span>
|
||||
{t(`${i18nPrefix}.onlyReason`, { deprecatedReason: t(`${i18nPrefix}.reason.${deprecatedReasonKey}`) })}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
{
|
||||
!hasValidDeprecatedReason && (
|
||||
<span>{t(`${i18nPrefix}.noReason`)}</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(DeprecationNotice)
|
||||
@ -6,7 +6,7 @@ import PluginTypeSwitch from './plugin-type-switch'
|
||||
import ListWrapper from './list/list-wrapper'
|
||||
import type { SearchParams } from './types'
|
||||
import { getMarketplaceCollectionsAndPlugins } from './utils'
|
||||
import { TanstackQueryIniter } from '@/context/query-client'
|
||||
import { TanstackQueryInitializer } from '@/context/query-client'
|
||||
|
||||
type MarketplaceProps = {
|
||||
locale: string
|
||||
@ -39,7 +39,7 @@ const Marketplace = async ({
|
||||
}
|
||||
|
||||
return (
|
||||
<TanstackQueryIniter>
|
||||
<TanstackQueryInitializer>
|
||||
<MarketplaceContextProvider
|
||||
searchParams={searchParams}
|
||||
shouldExclude={shouldExclude}
|
||||
@ -65,7 +65,7 @@ const Marketplace = async ({
|
||||
showInstallButton={showInstallButton}
|
||||
/>
|
||||
</MarketplaceContextProvider>
|
||||
</TanstackQueryIniter>
|
||||
</TanstackQueryInitializer>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ 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 { useGetLanguage, useI18N } from '@/context/i18n'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useInvalidateAllToolProviders } from '@/service/use-tools'
|
||||
@ -39,6 +39,7 @@ import { getMarketplaceUrl } from '@/utils/var'
|
||||
import { PluginAuth } from '@/app/components/plugins/plugin-auth'
|
||||
import { AuthCategory } from '@/app/components/plugins/plugin-auth'
|
||||
import { useAllToolProviders } from '@/service/use-tools'
|
||||
import DeprecationNotice from '../base/deprecation-notice'
|
||||
|
||||
const i18nPrefix = 'plugin.action'
|
||||
|
||||
@ -56,6 +57,7 @@ const DetailHeader = ({
|
||||
const { t } = useTranslation()
|
||||
const { theme } = useTheme()
|
||||
const locale = useGetLanguage()
|
||||
const { locale: currentLocale } = useI18N()
|
||||
const { checkForUpdates, fetchReleases } = useGitHubReleases()
|
||||
const { setShowUpdatePluginModal } = useModalContext()
|
||||
const { refreshModelProviders } = useProviderContext()
|
||||
@ -70,6 +72,9 @@ const DetailHeader = ({
|
||||
latest_version,
|
||||
meta,
|
||||
plugin_id,
|
||||
status,
|
||||
deprecated_reason,
|
||||
alternative_plugin_id,
|
||||
} = detail
|
||||
const { author, category, name, label, description, icon, verified, tool } = detail.declaration
|
||||
const isTool = category === PluginType.tool
|
||||
@ -98,7 +103,7 @@ const DetailHeader = ({
|
||||
if (isFromGitHub)
|
||||
return `https://github.com/${meta!.repo}`
|
||||
if (isFromMarketplace)
|
||||
return getMarketplaceUrl(`/plugins/${author}/${name}`, { theme })
|
||||
return getMarketplaceUrl(`/plugins/${author}/${name}`, { language: currentLocale, theme })
|
||||
return ''
|
||||
}, [author, isFromGitHub, isFromMarketplace, meta, name, theme])
|
||||
|
||||
@ -272,6 +277,15 @@ const DetailHeader = ({
|
||||
</ActionButton>
|
||||
</div>
|
||||
</div>
|
||||
{isFromMarketplace && (
|
||||
<DeprecationNotice
|
||||
status={status}
|
||||
deprecatedReason={deprecated_reason}
|
||||
alternativePluginId={alternative_plugin_id}
|
||||
alternativePluginURL={getMarketplaceUrl(`/plugins/${alternative_plugin_id}`, { language: currentLocale, theme })}
|
||||
className='mt-3'
|
||||
/>
|
||||
)}
|
||||
<Description className='mb-2 mt-3 h-auto' text={description[locale]} descriptionLineRows={2}></Description>
|
||||
{
|
||||
category === PluginType.tool && (
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import {
|
||||
RiArrowRightUpLine,
|
||||
@ -55,6 +55,8 @@ const PluginItem: FC<Props> = ({
|
||||
endpoints_active,
|
||||
meta,
|
||||
plugin_id,
|
||||
status,
|
||||
deprecated_reason,
|
||||
} = plugin
|
||||
const { category, author, name, label, description, icon, verified, meta: declarationMeta } = plugin.declaration
|
||||
|
||||
@ -70,9 +72,14 @@ const PluginItem: FC<Props> = ({
|
||||
return gte(langGeniusVersionInfo.current_version, declarationMeta.minimum_dify_version ?? '0.0.0')
|
||||
}, [declarationMeta.minimum_dify_version, langGeniusVersionInfo.current_version])
|
||||
|
||||
const handleDelete = () => {
|
||||
const isDeprecated = useMemo(() => {
|
||||
return status === 'deleted' && !!deprecated_reason
|
||||
}, [status, deprecated_reason])
|
||||
|
||||
const handleDelete = useCallback(() => {
|
||||
refreshPluginList({ category } as any)
|
||||
}
|
||||
}, [category, refreshPluginList])
|
||||
|
||||
const getValueFromI18nObject = useRenderI18nObject()
|
||||
const title = getValueFromI18nObject(label)
|
||||
const descriptionText = getValueFromI18nObject(description)
|
||||
@ -81,7 +88,7 @@ const PluginItem: FC<Props> = ({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'rounded-xl border-[1.5px] border-background-section-burn p-1',
|
||||
'relative overflow-hidden rounded-xl border-[1.5px] border-background-section-burn p-1',
|
||||
currentPluginID === plugin_id && 'border-components-option-card-option-selected-border',
|
||||
source === PluginSource.debugging
|
||||
? 'bg-[repeating-linear-gradient(-45deg,rgba(16,24,40,0.04),rgba(16,24,40,0.04)_5px,rgba(0,0,0,0.02)_5px,rgba(0,0,0,0.02)_10px)]'
|
||||
@ -91,10 +98,10 @@ const PluginItem: FC<Props> = ({
|
||||
setCurrentPluginID(plugin.plugin_id)
|
||||
}}
|
||||
>
|
||||
<div className={cn('hover-bg-components-panel-on-panel-item-bg relative rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg p-4 pb-3 shadow-xs', className)}>
|
||||
<div className={cn('hover-bg-components-panel-on-panel-item-bg relative z-10 rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-on-panel-item-bg p-4 pb-3 shadow-xs', className)}>
|
||||
<CornerMark text={categoriesMap[category].label} />
|
||||
{/* Header */}
|
||||
<div className="flex">
|
||||
<div className='flex'>
|
||||
<div className='flex h-10 w-10 items-center justify-center overflow-hidden rounded-xl border-[1px] border-components-panel-border-subtle'>
|
||||
<img
|
||||
className='h-full w-full'
|
||||
@ -102,13 +109,13 @@ const PluginItem: FC<Props> = ({
|
||||
alt={`plugin-${plugin_unique_identifier}-logo`}
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3 w-0 grow">
|
||||
<div className="flex h-5 items-center">
|
||||
<div className='ml-3 w-0 grow'>
|
||||
<div className='flex h-5 items-center'>
|
||||
<Title title={title} />
|
||||
{verified && <RiVerifiedBadgeLine className="ml-0.5 h-4 w-4 shrink-0 text-text-accent" />}
|
||||
{verified && <RiVerifiedBadgeLine className='ml-0.5 h-4 w-4 shrink-0 text-text-accent' />}
|
||||
{!isDifyVersionCompatible && <Tooltip popupContent={
|
||||
t('plugin.difyVersionNotCompatible', { minimalDifyVersion: declarationMeta.minimum_dify_version })
|
||||
}><RiErrorWarningLine color='red' className="ml-0.5 h-4 w-4 shrink-0 text-text-accent" /></Tooltip>}
|
||||
}><RiErrorWarningLine color='red' className='ml-0.5 h-4 w-4 shrink-0 text-text-accent' /></Tooltip>}
|
||||
<Badge className='ml-1 shrink-0'
|
||||
text={source === PluginSource.github ? plugin.meta!.version : plugin.version}
|
||||
hasRedCornerMark={(source === PluginSource.marketplace) && !!plugin.latest_version && plugin.latest_version !== plugin.version}
|
||||
@ -135,10 +142,11 @@ const PluginItem: FC<Props> = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mb-1 mt-1.5 flex h-4 items-center justify-between px-4'>
|
||||
<div className='flex items-center'>
|
||||
<div className='mb-1 mt-1.5 flex h-4 items-center gap-x-2 px-4'>
|
||||
{/* Organization & Name */}
|
||||
<div className='flex grow items-center overflow-hidden'>
|
||||
<OrgInfo
|
||||
className="mt-0.5"
|
||||
className='mt-0.5'
|
||||
orgName={orgName}
|
||||
packageName={name}
|
||||
packageNameClassName='w-auto max-w-[150px]'
|
||||
@ -146,15 +154,20 @@ const PluginItem: FC<Props> = ({
|
||||
{category === PluginType.extension && (
|
||||
<>
|
||||
<div className='system-xs-regular mx-2 text-text-quaternary'>·</div>
|
||||
<div className='system-xs-regular flex space-x-1 text-text-tertiary'>
|
||||
<RiLoginCircleLine className='h-4 w-4' />
|
||||
<span>{t('plugin.endpointsEnabled', { num: endpoints_active })}</span>
|
||||
<div className='system-xs-regular flex space-x-1 overflow-hidden text-text-tertiary'>
|
||||
<RiLoginCircleLine className='h-4 w-4 shrink-0' />
|
||||
<span
|
||||
className='truncate'
|
||||
title={t('plugin.endpointsEnabled', { num: endpoints_active })}
|
||||
>
|
||||
{t('plugin.endpointsEnabled', { num: endpoints_active })}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='flex items-center'>
|
||||
{/* Source */}
|
||||
<div className='flex shrink-0 items-center'>
|
||||
{source === PluginSource.github
|
||||
&& <>
|
||||
<a href={`https://github.com/${meta!.repo}`} target='_blank' className='flex items-center gap-1'>
|
||||
@ -192,7 +205,20 @@ const PluginItem: FC<Props> = ({
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
{/* Deprecated */}
|
||||
{source === PluginSource.marketplace && enable_marketplace && isDeprecated && (
|
||||
<div className='system-2xs-medium-uppercase flex shrink-0 items-center gap-x-2'>
|
||||
<span className='text-text-tertiary'>·</span>
|
||||
<span className='text-text-warning'>
|
||||
{t('plugin.deprecated')}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* BG Effect for Deprecated Plugin */}
|
||||
{source === PluginSource.marketplace && enable_marketplace && isDeprecated && (
|
||||
<div className='absolute bottom-[-71px] right-[-45px] z-0 size-40 bg-components-badge-status-light-warning-halo opacity-60 blur-[120px]' />
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -36,6 +36,9 @@ const PluginsPanel = () => {
|
||||
...plugin,
|
||||
latest_version: installedLatestVersion?.versions[plugin.plugin_id]?.version ?? '',
|
||||
latest_unique_identifier: installedLatestVersion?.versions[plugin.plugin_id]?.unique_identifier ?? '',
|
||||
status: installedLatestVersion?.versions[plugin.plugin_id]?.status ?? 'active',
|
||||
deprecated_reason: installedLatestVersion?.versions[plugin.plugin_id]?.deprecated_reason ?? '',
|
||||
alternative_plugin_id: installedLatestVersion?.versions[plugin.plugin_id]?.alternative_plugin_id ?? '',
|
||||
})) || []
|
||||
}, [pluginList, installedLatestVersion])
|
||||
|
||||
@ -66,20 +69,25 @@ const PluginsPanel = () => {
|
||||
onFilterChange={handleFilterChange}
|
||||
/>
|
||||
</div>
|
||||
{isPluginListLoading ? <Loading type='app' /> : (filteredList?.length ?? 0) > 0 ? (
|
||||
<div className='flex grow flex-wrap content-start items-start justify-center gap-2 self-stretch px-12'>
|
||||
<div className='w-full'>
|
||||
<List pluginList={filteredList || []} />
|
||||
</div>
|
||||
{!isLastPage && !isFetching && (
|
||||
<Button onClick={loadNextPage}>
|
||||
{t('workflow.common.loadMore')}
|
||||
</Button>
|
||||
{isPluginListLoading && <Loading type='app' />}
|
||||
{!isPluginListLoading && (
|
||||
<>
|
||||
{(filteredList?.length ?? 0) > 0 ? (
|
||||
<div className='flex grow flex-wrap content-start items-start justify-center gap-2 self-stretch px-12'>
|
||||
<div className='w-full'>
|
||||
<List pluginList={filteredList || []} />
|
||||
</div>
|
||||
{!isLastPage && !isFetching && (
|
||||
<Button onClick={loadNextPage}>
|
||||
{t('workflow.common.loadMore')}
|
||||
</Button>
|
||||
)}
|
||||
{isFetching && <div className='system-md-semibold text-text-secondary'>{t('appLog.detail.loading')}</div>}
|
||||
</div>
|
||||
) : (
|
||||
<Empty />
|
||||
)}
|
||||
{isFetching && <div className='system-md-semibold text-text-secondary'>{t('appLog.detail.loading')}</div>}
|
||||
</div>
|
||||
) : (
|
||||
<Empty />
|
||||
</>
|
||||
)}
|
||||
<PluginDetailPanel
|
||||
detail={currentPluginDetail}
|
||||
|
||||
@ -120,6 +120,9 @@ export type PluginDetail = {
|
||||
latest_unique_identifier: string
|
||||
source: PluginSource
|
||||
meta?: MetaData
|
||||
status: 'active' | 'deleted'
|
||||
deprecated_reason: string
|
||||
alternative_plugin_id: string
|
||||
}
|
||||
|
||||
export type PluginInfoFromMarketPlace = {
|
||||
@ -345,6 +348,9 @@ export type InstalledLatestVersionResponse = {
|
||||
[plugin_id: string]: {
|
||||
unique_identifier: string
|
||||
version: string
|
||||
status: 'active' | 'deleted'
|
||||
deprecated_reason: string
|
||||
alternative_plugin_id: string
|
||||
} | null
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ const Field: FC<Props> = ({
|
||||
<Tooltip popupContent={t('app.structOutput.moreFillTip')} disabled={depth !== MAX_DEPTH + 1}>
|
||||
<div
|
||||
className={cn('flex items-center justify-between rounded-md pr-2', !readonly && 'hover:bg-state-base-hover', depth !== MAX_DEPTH + 1 && 'cursor-pointer')}
|
||||
onClick={() => !readonly && onSelect?.([...valueSelector, name])}
|
||||
onMouseDown={() => !readonly && onSelect?.([...valueSelector, name])}
|
||||
>
|
||||
<div className='flex grow items-stretch'>
|
||||
<TreeIndentLine depth={depth} />
|
||||
|
||||
@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Workflow Panel Width Persistence Tests
|
||||
* Tests for GitHub issue #22745: Panel width persistence bug fix
|
||||
*/
|
||||
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
type PanelWidthSource = 'user' | 'system'
|
||||
|
||||
// Mock localStorage for testing
|
||||
const createMockLocalStorage = () => {
|
||||
const storage: Record<string, string> = {}
|
||||
return {
|
||||
getItem: jest.fn((key: string) => storage[key] || null),
|
||||
setItem: jest.fn((key: string, value: string) => {
|
||||
storage[key] = value
|
||||
}),
|
||||
removeItem: jest.fn((key: string) => {
|
||||
delete storage[key]
|
||||
}),
|
||||
clear: jest.fn(() => {
|
||||
Object.keys(storage).forEach(key => delete storage[key])
|
||||
}),
|
||||
get storage() { return { ...storage } },
|
||||
}
|
||||
}
|
||||
|
||||
// Core panel width logic extracted from the component
|
||||
const createPanelWidthManager = (storageKey: string) => {
|
||||
return {
|
||||
updateWidth: (width: number, source: PanelWidthSource = 'user') => {
|
||||
const newValue = Math.max(400, Math.min(width, 800))
|
||||
if (source === 'user')
|
||||
localStorage.setItem(storageKey, `${newValue}`)
|
||||
|
||||
return newValue
|
||||
},
|
||||
getStoredWidth: () => {
|
||||
const stored = localStorage.getItem(storageKey)
|
||||
return stored ? Number.parseFloat(stored) : 400
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
describe('Workflow Panel Width Persistence', () => {
|
||||
let mockLocalStorage: ReturnType<typeof createMockLocalStorage>
|
||||
|
||||
beforeEach(() => {
|
||||
mockLocalStorage = createMockLocalStorage()
|
||||
Object.defineProperty(globalThis, 'localStorage', {
|
||||
value: mockLocalStorage,
|
||||
writable: true,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('Node Panel Width Management', () => {
|
||||
const storageKey = 'workflow-node-panel-width'
|
||||
|
||||
it('should save user resize to localStorage', () => {
|
||||
const manager = createPanelWidthManager(storageKey)
|
||||
|
||||
const result = manager.updateWidth(500, 'user')
|
||||
|
||||
expect(result).toBe(500)
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith(storageKey, '500')
|
||||
})
|
||||
|
||||
it('should not save system compression to localStorage', () => {
|
||||
const manager = createPanelWidthManager(storageKey)
|
||||
|
||||
const result = manager.updateWidth(200, 'system')
|
||||
|
||||
expect(result).toBe(400) // Respects minimum width
|
||||
expect(localStorage.setItem).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should enforce minimum width of 400px', () => {
|
||||
const manager = createPanelWidthManager(storageKey)
|
||||
|
||||
// User tries to set below minimum
|
||||
const userResult = manager.updateWidth(300, 'user')
|
||||
expect(userResult).toBe(400)
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith(storageKey, '400')
|
||||
|
||||
// System compression below minimum
|
||||
const systemResult = manager.updateWidth(150, 'system')
|
||||
expect(systemResult).toBe(400)
|
||||
expect(localStorage.setItem).toHaveBeenCalledTimes(1) // Only user call
|
||||
})
|
||||
|
||||
it('should preserve user preferences during system compression', () => {
|
||||
localStorage.setItem(storageKey, '600')
|
||||
const manager = createPanelWidthManager(storageKey)
|
||||
|
||||
// System compresses panel
|
||||
manager.updateWidth(200, 'system')
|
||||
|
||||
// User preference should remain unchanged
|
||||
expect(localStorage.getItem(storageKey)).toBe('600')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Bug Scenario Reproduction', () => {
|
||||
it('should reproduce original bug behavior (for comparison)', () => {
|
||||
const storageKey = 'workflow-node-panel-width'
|
||||
|
||||
// Original buggy behavior - always saves regardless of source
|
||||
const buggyUpdate = (width: number) => {
|
||||
localStorage.setItem(storageKey, `${width}`)
|
||||
return Math.max(400, width)
|
||||
}
|
||||
|
||||
localStorage.setItem(storageKey, '500') // User preference
|
||||
buggyUpdate(200) // System compression pollutes localStorage
|
||||
|
||||
expect(localStorage.getItem(storageKey)).toBe('200') // Bug: corrupted state
|
||||
})
|
||||
|
||||
it('should verify fix prevents localStorage pollution', () => {
|
||||
const storageKey = 'workflow-node-panel-width'
|
||||
const manager = createPanelWidthManager(storageKey)
|
||||
|
||||
localStorage.setItem(storageKey, '500') // User preference
|
||||
manager.updateWidth(200, 'system') // System compression
|
||||
|
||||
expect(localStorage.getItem(storageKey)).toBe('500') // Fix: preserved state
|
||||
})
|
||||
})
|
||||
|
||||
describe('Edge Cases', () => {
|
||||
it('should handle multiple rapid operations correctly', () => {
|
||||
const manager = createPanelWidthManager('workflow-node-panel-width')
|
||||
|
||||
// Rapid system adjustments
|
||||
manager.updateWidth(300, 'system')
|
||||
manager.updateWidth(250, 'system')
|
||||
manager.updateWidth(180, 'system')
|
||||
|
||||
// Single user adjustment
|
||||
manager.updateWidth(550, 'user')
|
||||
|
||||
expect(localStorage.setItem).toHaveBeenCalledTimes(1)
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith('workflow-node-panel-width', '550')
|
||||
})
|
||||
|
||||
it('should handle corrupted localStorage gracefully', () => {
|
||||
localStorage.setItem('workflow-node-panel-width', '150') // Below minimum
|
||||
const manager = createPanelWidthManager('workflow-node-panel-width')
|
||||
|
||||
const storedWidth = manager.getStoredWidth()
|
||||
expect(storedWidth).toBe(150) // Returns raw value
|
||||
|
||||
// User can correct the preference
|
||||
const correctedWidth = manager.updateWidth(500, 'user')
|
||||
expect(correctedWidth).toBe(500)
|
||||
expect(localStorage.getItem('workflow-node-panel-width')).toBe('500')
|
||||
})
|
||||
})
|
||||
|
||||
describe('TypeScript Type Safety', () => {
|
||||
it('should enforce source parameter type', () => {
|
||||
const manager = createPanelWidthManager('workflow-node-panel-width')
|
||||
|
||||
// Valid source values
|
||||
manager.updateWidth(500, 'user')
|
||||
manager.updateWidth(500, 'system')
|
||||
|
||||
// Default to 'user'
|
||||
manager.updateWidth(500)
|
||||
|
||||
expect(localStorage.setItem).toHaveBeenCalledTimes(2) // user + default
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -105,15 +105,18 @@ const BasePanel: FC<BasePanelProps> = ({
|
||||
return Math.max(available, 400)
|
||||
}, [workflowCanvasWidth, otherPanelWidth])
|
||||
|
||||
const updateNodePanelWidth = useCallback((width: number) => {
|
||||
const updateNodePanelWidth = useCallback((width: number, source: 'user' | 'system' = 'user') => {
|
||||
// Ensure the width is within the min and max range
|
||||
const newValue = Math.max(400, Math.min(width, maxNodePanelWidth))
|
||||
localStorage.setItem('workflow-node-panel-width', `${newValue}`)
|
||||
|
||||
if (source === 'user')
|
||||
localStorage.setItem('workflow-node-panel-width', `${newValue}`)
|
||||
|
||||
setNodePanelWidth(newValue)
|
||||
}, [maxNodePanelWidth, setNodePanelWidth])
|
||||
|
||||
const handleResize = useCallback((width: number) => {
|
||||
updateNodePanelWidth(width)
|
||||
updateNodePanelWidth(width, 'user')
|
||||
}, [updateNodePanelWidth])
|
||||
|
||||
const {
|
||||
@ -127,7 +130,10 @@ const BasePanel: FC<BasePanelProps> = ({
|
||||
onResize: debounce(handleResize),
|
||||
})
|
||||
|
||||
const debounceUpdate = debounce(updateNodePanelWidth)
|
||||
const debounceUpdate = debounce((width: number) => {
|
||||
updateNodePanelWidth(width, 'system')
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (!workflowCanvasWidth)
|
||||
return
|
||||
@ -138,7 +144,7 @@ const BasePanel: FC<BasePanelProps> = ({
|
||||
const target = Math.max(workflowCanvasWidth - otherPanelWidth - reservedCanvasWidth, 400)
|
||||
debounceUpdate(target)
|
||||
}
|
||||
}, [nodePanelWidth, otherPanelWidth, workflowCanvasWidth, updateNodePanelWidth])
|
||||
}, [nodePanelWidth, otherPanelWidth, workflowCanvasWidth, debounceUpdate])
|
||||
|
||||
const { handleNodeSelect } = useNodesInteractions()
|
||||
const { nodesReadOnly } = useNodesReadOnly()
|
||||
|
||||
@ -0,0 +1,145 @@
|
||||
/**
|
||||
* Debug and Preview Panel Width Persistence Tests
|
||||
* Tests for GitHub issue #22745: Panel width persistence bug fix
|
||||
*/
|
||||
|
||||
import '@testing-library/jest-dom'
|
||||
|
||||
type PanelWidthSource = 'user' | 'system'
|
||||
|
||||
// Mock localStorage for testing
|
||||
const createMockLocalStorage = () => {
|
||||
const storage: Record<string, string> = {}
|
||||
return {
|
||||
getItem: jest.fn((key: string) => storage[key] || null),
|
||||
setItem: jest.fn((key: string, value: string) => {
|
||||
storage[key] = value
|
||||
}),
|
||||
removeItem: jest.fn((key: string) => {
|
||||
delete storage[key]
|
||||
}),
|
||||
clear: jest.fn(() => {
|
||||
Object.keys(storage).forEach(key => delete storage[key])
|
||||
}),
|
||||
get storage() { return { ...storage } },
|
||||
}
|
||||
}
|
||||
|
||||
// Preview panel width logic
|
||||
const createPreviewPanelManager = () => {
|
||||
const storageKey = 'debug-and-preview-panel-width'
|
||||
|
||||
return {
|
||||
updateWidth: (width: number, source: PanelWidthSource = 'user') => {
|
||||
const newValue = Math.max(400, Math.min(width, 800))
|
||||
if (source === 'user')
|
||||
localStorage.setItem(storageKey, `${newValue}`)
|
||||
|
||||
return newValue
|
||||
},
|
||||
getStoredWidth: () => {
|
||||
const stored = localStorage.getItem(storageKey)
|
||||
return stored ? Number.parseFloat(stored) : 400
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
describe('Debug and Preview Panel Width Persistence', () => {
|
||||
let mockLocalStorage: ReturnType<typeof createMockLocalStorage>
|
||||
|
||||
beforeEach(() => {
|
||||
mockLocalStorage = createMockLocalStorage()
|
||||
Object.defineProperty(globalThis, 'localStorage', {
|
||||
value: mockLocalStorage,
|
||||
writable: true,
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
describe('Preview Panel Width Management', () => {
|
||||
it('should save user resize to localStorage', () => {
|
||||
const manager = createPreviewPanelManager()
|
||||
|
||||
const result = manager.updateWidth(450, 'user')
|
||||
|
||||
expect(result).toBe(450)
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith('debug-and-preview-panel-width', '450')
|
||||
})
|
||||
|
||||
it('should not save system compression to localStorage', () => {
|
||||
const manager = createPreviewPanelManager()
|
||||
|
||||
const result = manager.updateWidth(300, 'system')
|
||||
|
||||
expect(result).toBe(400) // Respects minimum width
|
||||
expect(localStorage.setItem).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should behave identically to Node Panel', () => {
|
||||
const manager = createPreviewPanelManager()
|
||||
|
||||
// Both user and system operations should behave consistently
|
||||
manager.updateWidth(500, 'user')
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith('debug-and-preview-panel-width', '500')
|
||||
|
||||
manager.updateWidth(200, 'system')
|
||||
expect(localStorage.getItem('debug-and-preview-panel-width')).toBe('500')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Dual Panel Scenario', () => {
|
||||
it('should maintain independence from Node Panel', () => {
|
||||
localStorage.setItem('workflow-node-panel-width', '600')
|
||||
localStorage.setItem('debug-and-preview-panel-width', '450')
|
||||
|
||||
const manager = createPreviewPanelManager()
|
||||
|
||||
// System compresses preview panel
|
||||
manager.updateWidth(200, 'system')
|
||||
|
||||
// Only preview panel storage key should be unaffected
|
||||
expect(localStorage.getItem('debug-and-preview-panel-width')).toBe('450')
|
||||
expect(localStorage.getItem('workflow-node-panel-width')).toBe('600')
|
||||
})
|
||||
|
||||
it('should handle F12 scenario consistently', () => {
|
||||
const manager = createPreviewPanelManager()
|
||||
|
||||
// User sets preference
|
||||
manager.updateWidth(500, 'user')
|
||||
expect(localStorage.getItem('debug-and-preview-panel-width')).toBe('500')
|
||||
|
||||
// F12 opens causing viewport compression
|
||||
manager.updateWidth(180, 'system')
|
||||
|
||||
// User preference preserved
|
||||
expect(localStorage.getItem('debug-and-preview-panel-width')).toBe('500')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Consistency with Node Panel', () => {
|
||||
it('should enforce same minimum width rules', () => {
|
||||
const manager = createPreviewPanelManager()
|
||||
|
||||
// Same 400px minimum as Node Panel
|
||||
const result = manager.updateWidth(300, 'user')
|
||||
expect(result).toBe(400)
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith('debug-and-preview-panel-width', '400')
|
||||
})
|
||||
|
||||
it('should use same source parameter pattern', () => {
|
||||
const manager = createPreviewPanelManager()
|
||||
|
||||
// Default to 'user' when source not specified
|
||||
manager.updateWidth(500)
|
||||
expect(localStorage.setItem).toHaveBeenCalledWith('debug-and-preview-panel-width', '500')
|
||||
|
||||
// Explicit 'system' source
|
||||
manager.updateWidth(300, 'system')
|
||||
expect(localStorage.setItem).toHaveBeenCalledTimes(1) // Only user call
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -53,8 +53,9 @@ const DebugAndPreview = () => {
|
||||
const nodePanelWidth = useStore(s => s.nodePanelWidth)
|
||||
const panelWidth = useStore(s => s.previewPanelWidth)
|
||||
const setPanelWidth = useStore(s => s.setPreviewPanelWidth)
|
||||
const handleResize = useCallback((width: number) => {
|
||||
localStorage.setItem('debug-and-preview-panel-width', `${width}`)
|
||||
const handleResize = useCallback((width: number, source: 'user' | 'system' = 'user') => {
|
||||
if (source === 'user')
|
||||
localStorage.setItem('debug-and-preview-panel-width', `${width}`)
|
||||
setPanelWidth(width)
|
||||
}, [setPanelWidth])
|
||||
const maxPanelWidth = useMemo(() => {
|
||||
@ -74,7 +75,9 @@ const DebugAndPreview = () => {
|
||||
triggerDirection: 'left',
|
||||
minWidth: 400,
|
||||
maxWidth: maxPanelWidth,
|
||||
onResize: debounce(handleResize),
|
||||
onResize: debounce((width: number) => {
|
||||
handleResize(width, 'user')
|
||||
}),
|
||||
})
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user