Merge branch 'main' into feat/mcp

This commit is contained in:
Novice
2025-07-02 15:31:12 +08:00
85 changed files with 1354 additions and 527 deletions

View File

@ -36,6 +36,7 @@ import AccessControl from '@/app/components/app/app-access-control'
import { AccessMode } from '@/models/access-control'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { formatTime } from '@/utils/time'
import { useGetUserCanAccessApp } from '@/service/access-control'
export type AppCardProps = {
app: App
@ -190,6 +191,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
}, [onRefresh, mutateApps, setShowAccessControl])
const Operations = (props: HtmlContentProps) => {
const { data: userCanAccessApp, isLoading: isGettingUserCanAccessApp } = useGetUserCanAccessApp({ appId: app?.id, enabled: (!!props?.open && systemFeatures.webapp_auth.enabled) })
const onMouseLeave = async () => {
props.onClose?.()
}
@ -267,10 +269,14 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => {
</button>
</>
)}
<Divider className="my-1" />
<button className='mx-1 flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 hover:bg-state-base-hover' onClick={onClickInstalledApp}>
<span className='system-sm-regular text-text-secondary'>{t('app.openInExplore')}</span>
</button>
{
(isGettingUserCanAccessApp || !userCanAccessApp?.result) ? null : <>
<Divider className="my-1" />
<button className='mx-1 flex h-8 cursor-pointer items-center gap-2 rounded-lg px-3 hover:bg-state-base-hover' onClick={onClickInstalledApp}>
<span className='system-sm-regular text-text-secondary'>{t('app.openInExplore')}</span>
</button>
</>
}
<Divider className="my-1" />
{
systemFeatures.webapp_auth.enabled && isCurrentWorkspaceEditor && <>

View File

@ -1124,6 +1124,129 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
<hr className='ml-0 mr-0' />
<Heading
url='/datasets/{dataset_id}/documents/{document_id}'
method='GET'
title='Get Document Detail'
name='#get-document-detail'
/>
<Row>
<Col>
Get a document's detail.
### Path
- `dataset_id` (string) Dataset ID
- `document_id` (string) Document ID
### Query
- `metadata` (string) Metadata filter, can be `all`, `only`, or `without`. Default is `all`.
### Response
Returns the document's detail.
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="GET" label="/datasets/{dataset_id}/documents/{document_id}" targetCode={`curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### Response Example
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"id": "f46ae30c-5c11-471b-96d0-464f5f32a7b2",
"position": 1,
"data_source_type": "upload_file",
"data_source_info": {
"upload_file": {
...
}
},
"dataset_process_rule_id": "24b99906-845e-499f-9e3c-d5565dd6962c",
"dataset_process_rule": {
"mode": "hierarchical",
"rules": {
"pre_processing_rules": [
{
"id": "remove_extra_spaces",
"enabled": true
},
{
"id": "remove_urls_emails",
"enabled": false
}
],
"segmentation": {
"separator": "**********page_ending**********",
"max_tokens": 1024,
"chunk_overlap": 0
},
"parent_mode": "paragraph",
"subchunk_segmentation": {
"separator": "\n",
"max_tokens": 512,
"chunk_overlap": 0
}
}
},
"document_process_rule": {
"id": "24b99906-845e-499f-9e3c-d5565dd6962c",
"dataset_id": "48a0db76-d1a9-46c1-ae35-2baaa919a8a9",
"mode": "hierarchical",
"rules": {
"pre_processing_rules": [
{
"id": "remove_extra_spaces",
"enabled": true
},
{
"id": "remove_urls_emails",
"enabled": false
}
],
"segmentation": {
"separator": "**********page_ending**********",
"max_tokens": 1024,
"chunk_overlap": 0
},
"parent_mode": "paragraph",
"subchunk_segmentation": {
"separator": "\n",
"max_tokens": 512,
"chunk_overlap": 0
}
}
},
"name": "xxxx",
"created_from": "web",
"created_by": "17f71940-a7b5-4c77-b60f-2bd645c1ffa0",
"created_at": 1750464191,
"tokens": null,
"indexing_status": "waiting",
"completed_at": null,
"updated_at": 1750464191,
"indexing_latency": null,
"error": null,
"enabled": true,
"disabled_at": null,
"disabled_by": null,
"archived": false,
"segment_count": 0,
"average_segment_length": 0,
"hit_count": null,
"display_status": "queuing",
"doc_form": "hierarchical_model",
"doc_language": "Chinese Simplified"
}
```
</CodeGroup>
</Col>
</Row>
___
<hr className='ml-0 mr-0' />
<Heading
url='/datasets/{dataset_id}/documents/status/{action}'
method='PATCH'

View File

@ -881,6 +881,130 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
<hr className='ml-0 mr-0' />
<Heading
url='/datasets/{dataset_id}/documents/{document_id}'
method='GET'
title='ドキュメントの詳細を取得'
name='#get-document-detail'
/>
<Row>
<Col>
ドキュメントの詳細を取得.
### Path
- `dataset_id` (string) ナレッジベースID
- `document_id` (string) ドキュメントID
### Query
- `metadata` (string) metadataのフィルター条件 `all`、`only`、または`without`。デフォルトは `all`。
### Response
ナレッジベースドキュメントの詳細を返す.
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="GET" label="/datasets/{dataset_id}/documents/{document_id}" targetCode={`curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### Response Example
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"id": "f46ae30c-5c11-471b-96d0-464f5f32a7b2",
"position": 1,
"data_source_type": "upload_file",
"data_source_info": {
"upload_file": {
...
}
},
"dataset_process_rule_id": "24b99906-845e-499f-9e3c-d5565dd6962c",
"dataset_process_rule": {
"mode": "hierarchical",
"rules": {
"pre_processing_rules": [
{
"id": "remove_extra_spaces",
"enabled": true
},
{
"id": "remove_urls_emails",
"enabled": false
}
],
"segmentation": {
"separator": "**********page_ending**********",
"max_tokens": 1024,
"chunk_overlap": 0
},
"parent_mode": "paragraph",
"subchunk_segmentation": {
"separator": "\n",
"max_tokens": 512,
"chunk_overlap": 0
}
}
},
"document_process_rule": {
"id": "24b99906-845e-499f-9e3c-d5565dd6962c",
"dataset_id": "48a0db76-d1a9-46c1-ae35-2baaa919a8a9",
"mode": "hierarchical",
"rules": {
"pre_processing_rules": [
{
"id": "remove_extra_spaces",
"enabled": true
},
{
"id": "remove_urls_emails",
"enabled": false
}
],
"segmentation": {
"separator": "**********page_ending**********",
"max_tokens": 1024,
"chunk_overlap": 0
},
"parent_mode": "paragraph",
"subchunk_segmentation": {
"separator": "\n",
"max_tokens": 512,
"chunk_overlap": 0
}
}
},
"name": "xxxx",
"created_from": "web",
"created_by": "17f71940-a7b5-4c77-b60f-2bd645c1ffa0",
"created_at": 1750464191,
"tokens": null,
"indexing_status": "waiting",
"completed_at": null,
"updated_at": 1750464191,
"indexing_latency": null,
"error": null,
"enabled": true,
"disabled_at": null,
"disabled_by": null,
"archived": false,
"segment_count": 0,
"average_segment_length": 0,
"hit_count": null,
"display_status": "queuing",
"doc_form": "hierarchical_model",
"doc_language": "Chinese Simplified"
}
```
</CodeGroup>
</Col>
</Row>
___
<hr className='ml-0 mr-0' />
<Heading
url='/datasets/{dataset_id}/documents/status/{action}'
method='PATCH'

View File

@ -1131,6 +1131,130 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
<hr className='ml-0 mr-0' />
<Heading
url='/datasets/{dataset_id}/documents/{document_id}'
method='GET'
title='获取文档详情'
name='#get-document-detail'
/>
<Row>
<Col>
获取文档详情.
### Path
- `dataset_id` (string) 知识库 ID
- `document_id` (string) 文档 ID
### Query
- `metadata` (string) metadata 过滤条件 `all`, `only`, 或者 `without`. 默认是 `all`.
### Response
返回知识库文档的详情.
</Col>
<Col sticky>
### Request Example
<CodeGroup title="Request" tag="GET" label="/datasets/{dataset_id}/documents/{document_id}" targetCode={`curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.apiBaseUrl}/datasets/{dataset_id}/documents/{document_id}' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
### Response Example
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"id": "f46ae30c-5c11-471b-96d0-464f5f32a7b2",
"position": 1,
"data_source_type": "upload_file",
"data_source_info": {
"upload_file": {
...
}
},
"dataset_process_rule_id": "24b99906-845e-499f-9e3c-d5565dd6962c",
"dataset_process_rule": {
"mode": "hierarchical",
"rules": {
"pre_processing_rules": [
{
"id": "remove_extra_spaces",
"enabled": true
},
{
"id": "remove_urls_emails",
"enabled": false
}
],
"segmentation": {
"separator": "**********page_ending**********",
"max_tokens": 1024,
"chunk_overlap": 0
},
"parent_mode": "paragraph",
"subchunk_segmentation": {
"separator": "\n",
"max_tokens": 512,
"chunk_overlap": 0
}
}
},
"document_process_rule": {
"id": "24b99906-845e-499f-9e3c-d5565dd6962c",
"dataset_id": "48a0db76-d1a9-46c1-ae35-2baaa919a8a9",
"mode": "hierarchical",
"rules": {
"pre_processing_rules": [
{
"id": "remove_extra_spaces",
"enabled": true
},
{
"id": "remove_urls_emails",
"enabled": false
}
],
"segmentation": {
"separator": "**********page_ending**********",
"max_tokens": 1024,
"chunk_overlap": 0
},
"parent_mode": "paragraph",
"subchunk_segmentation": {
"separator": "\n",
"max_tokens": 512,
"chunk_overlap": 0
}
}
},
"name": "xxxx",
"created_from": "web",
"created_by": "17f71940-a7b5-4c77-b60f-2bd645c1ffa0",
"created_at": 1750464191,
"tokens": null,
"indexing_status": "waiting",
"completed_at": null,
"updated_at": 1750464191,
"indexing_latency": null,
"error": null,
"enabled": true,
"disabled_at": null,
"disabled_by": null,
"archived": false,
"segment_count": 0,
"average_segment_length": 0,
"hit_count": null,
"display_status": "queuing",
"doc_form": "hierarchical_model",
"doc_language": "Chinese Simplified"
}
```
</CodeGroup>
</Col>
</Row>
___
<hr className='ml-0 mr-0' />
<Heading
url='/datasets/{dataset_id}/documents/status/{action}'
method='PATCH'

View File

@ -25,10 +25,13 @@ const Layout: FC<{
}
let appCode: string | null = null
if (redirectUrl)
appCode = redirectUrl?.split('/').pop() || null
else
if (redirectUrl) {
const url = new URL(`${window.location.origin}${decodeURIComponent(redirectUrl)}`)
appCode = url.pathname.split('/').pop() || null
}
else {
appCode = pathname.split('/').pop() || null
}
if (!appCode)
return

View File

@ -25,7 +25,10 @@ export default function CheckCode() {
const redirectUrl = searchParams.get('redirect_url')
const getAppCodeFromRedirectUrl = useCallback(() => {
const appCode = redirectUrl?.split('/').pop()
if (!redirectUrl)
return null
const url = new URL(`${window.location.origin}${decodeURIComponent(redirectUrl)}`)
const appCode = url.pathname.split('/').pop()
if (!appCode)
return null
@ -62,7 +65,7 @@ export default function CheckCode() {
localStorage.setItem('webapp_access_token', ret.data.access_token)
const tokenResp = await fetchAccessToken({ appCode, webAppAccessToken: ret.data.access_token })
await setAccessToken(appCode, tokenResp.access_token)
router.replace(redirectUrl)
router.replace(decodeURIComponent(redirectUrl))
}
}
catch (error) { console.error(error) }

View File

@ -23,7 +23,10 @@ const ExternalMemberSSOAuth = () => {
}
const getAppCodeFromRedirectUrl = useCallback(() => {
const appCode = redirectUrl?.split('/').pop()
if (!redirectUrl)
return null
const url = new URL(`${window.location.origin}${decodeURIComponent(redirectUrl)}`)
const appCode = url.pathname.split('/').pop()
if (!appCode)
return null

View File

@ -1,3 +1,4 @@
'use client'
import Link from 'next/link'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -33,7 +34,10 @@ export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAut
const redirectUrl = searchParams.get('redirect_url')
const getAppCodeFromRedirectUrl = useCallback(() => {
const appCode = redirectUrl?.split('/').pop()
if (!redirectUrl)
return null
const url = new URL(`${window.location.origin}${decodeURIComponent(redirectUrl)}`)
const appCode = url.pathname.split('/').pop()
if (!appCode)
return null
@ -87,7 +91,7 @@ export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAut
localStorage.setItem('webapp_access_token', res.data.access_token)
const tokenResp = await fetchAccessToken({ appCode, webAppAccessToken: res.data.access_token })
await setAccessToken(appCode, tokenResp.access_token)
router.replace(redirectUrl)
router.replace(decodeURIComponent(redirectUrl))
}
else {
Toast.notify({

View File

@ -23,7 +23,10 @@ const SSOAuth: FC<SSOAuthProps> = ({
const redirectUrl = searchParams.get('redirect_url')
const getAppCodeFromRedirectUrl = useCallback(() => {
const appCode = redirectUrl?.split('/').pop()
if (!redirectUrl)
return null
const url = new URL(`${window.location.origin}${decodeURIComponent(redirectUrl)}`)
const appCode = url.pathname.split('/').pop()
if (!appCode)
return null

View File

@ -46,7 +46,10 @@ const WebSSOForm: FC = () => {
}
const getAppCodeFromRedirectUrl = useCallback(() => {
const appCode = redirectUrl?.split('/').pop()
if (!redirectUrl)
return null
const url = new URL(`${window.location.origin}${decodeURIComponent(redirectUrl)}`)
const appCode = url.pathname.split('/').pop()
if (!appCode)
return null
@ -63,20 +66,20 @@ const WebSSOForm: FC = () => {
localStorage.setItem('webapp_access_token', tokenFromUrl)
const tokenResp = await fetchAccessToken({ appCode, webAppAccessToken: tokenFromUrl })
await setAccessToken(appCode, tokenResp.access_token)
router.replace(redirectUrl)
router.replace(decodeURIComponent(redirectUrl))
return
}
if (appCode && redirectUrl && localStorage.getItem('webapp_access_token')) {
const tokenResp = await fetchAccessToken({ appCode, webAppAccessToken: localStorage.getItem('webapp_access_token') })
await setAccessToken(appCode, tokenResp.access_token)
router.replace(redirectUrl)
router.replace(decodeURIComponent(redirectUrl))
}
})()
}, [getAppCodeFromRedirectUrl, redirectUrl, router, tokenFromUrl, message])
useEffect(() => {
if (webAppAccessMode && webAppAccessMode === AccessMode.PUBLIC && redirectUrl)
router.replace(redirectUrl)
router.replace(decodeURIComponent(redirectUrl))
}, [webAppAccessMode, router, redirectUrl])
if (tokenFromUrl) {

View File

@ -80,6 +80,8 @@ import {
import PluginDependency from '@/app/components/workflow/plugin-dependency'
import { supportFunctionCall } from '@/utils/tool-call'
import { MittProvider } from '@/context/mitt-context'
import { fetchAndMergeValidCompletionParams } from '@/utils/completion-params'
import Toast from '@/app/components/base/toast'
type PublishConfig = {
modelConfig: ModelConfig
@ -453,7 +455,21 @@ const Configuration: FC = () => {
...visionConfig,
enabled: supportVision,
}, true)
setCompletionParams({})
try {
const { params: filtered, removedDetails } = await fetchAndMergeValidCompletionParams(
provider,
modelId,
completionParams,
)
if (Object.keys(removedDetails).length)
Toast.notify({ type: 'warning', message: `${t('common.modelProvider.parametersInvalidRemoved')}: ${Object.entries(removedDetails).map(([k, reason]) => `${k} (${reason})`).join(', ')}` })
setCompletionParams(filtered)
}
catch (e) {
Toast.notify({ type: 'error', message: t('common.error') })
setCompletionParams({})
}
}
const isShowVisionConfig = !!currModel?.features?.includes(ModelFeatureEnum.vision)

View File

@ -0,0 +1,27 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { RiRefreshLine } from '@remixicon/react'
import cn from '@/utils/classnames'
import TooltipPlus from '@/app/components/base/tooltip'
type Props = {
className?: string,
popupContent?: string,
onClick: () => void
}
const SyncButton: FC<Props> = ({
className,
popupContent = '',
onClick,
}) => {
return (
<TooltipPlus popupContent={popupContent}>
<div className={cn(className, 'cursor-pointer select-none rounded-md p-1 hover:bg-state-base-hover')} onClick={onClick}>
<RiRefreshLine className='h-4 w-4 text-text-tertiary' />
</div>
</TooltipPlus>
)
}
export default React.memo(SyncButton)

View File

@ -3,6 +3,7 @@ import { Fragment, cloneElement, useRef } from 'react'
import cn from '@/utils/classnames'
export type HtmlContentProps = {
open?: boolean
onClose?: () => void
onClick?: () => void
}
@ -100,7 +101,8 @@ export default function CustomPopover({
}
>
{cloneElement(htmlContent as React.ReactElement, {
onClose: () => onMouseLeave(open),
open,
onClose: close,
...(manualClose
? {
onClick: close,

View File

@ -30,6 +30,7 @@ import useEditDocumentMetadata from '../metadata/hooks/use-edit-dataset-metadata
import DatasetMetadataDrawer from '../metadata/metadata-dataset/dataset-metadata-drawer'
import StatusWithAction from '../common/document-status-with-action/status-with-action'
import { useDocLink } from '@/context/i18n'
import { useFetchDefaultProcessRule } from '@/service/knowledge/use-create-dataset'
const FolderPlusIcon = ({ className }: React.SVGProps<SVGElement>) => {
return <svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" className={className ?? ''}>
@ -178,6 +179,8 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
router.push(`/datasets/${datasetId}/documents/create`)
}
const fetchDefaultProcessRuleMutation = useFetchDefaultProcessRule()
const handleSaveNotionPageSelected = async (selectedPages: NotionPage[]) => {
const workspacesMap = groupBy(selectedPages, 'workspace_id')
const workspaces = Object.keys(workspacesMap).map((workspaceId) => {
@ -186,6 +189,7 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
pages: workspacesMap[workspaceId],
}
})
const { rules } = await fetchDefaultProcessRuleMutation.mutateAsync('/datasets/process-rule')
const params = {
data_source: {
type: dataset?.data_source_type,
@ -209,7 +213,7 @@ const Documents: FC<IDocumentsProps> = ({ datasetId }) => {
},
indexing_technique: dataset?.indexing_technique,
process_rule: {
rules: {},
rules,
mode: ProcessMode.general,
},
} as CreateDocumentReq

View File

@ -9,6 +9,8 @@ import { useAppContext } from '@/context/app-context'
import { fetchNotionConnection } from '@/service/common'
import NotionIcon from '@/app/components/base/notion-icon'
import { noop } from 'lodash-es'
import { useTranslation } from 'react-i18next'
import Toast from '@/app/components/base/toast'
const Icon: FC<{
src: string
@ -33,6 +35,7 @@ const DataSourceNotion: FC<Props> = ({
const { isCurrentWorkspaceManager } = useAppContext()
const [canConnectNotion, setCanConnectNotion] = useState(false)
const { data } = useSWR(canConnectNotion ? '/oauth/data-source/notion' : null, fetchNotionConnection)
const { t } = useTranslation()
const connected = !!workspaces.length
@ -51,9 +54,19 @@ const DataSourceNotion: FC<Props> = ({
}
useEffect(() => {
if (data?.data)
window.location.href = data.data
}, [data])
if (data && 'data' in data) {
if (data.data && typeof data.data === 'string' && data.data.startsWith('http')) {
window.location.href = data.data
}
else if (data.data === 'internal') {
Toast.notify({
type: 'info',
message: t('common.dataSource.notion.integratedAlert'),
})
}
}
}, [data, t])
return (
<Panel
type={DataSourceType.notion}

View File

@ -14,7 +14,7 @@ const HeaderWrapper = ({
}: HeaderWrapperProps) => {
const pathname = usePathname()
const isBordered = ['/apps', '/datasets', '/datasets/create', '/tools'].includes(pathname)
// // Check if the current path is a workflow canvas & fullscreen
// Check if the current path is a workflow canvas & fullscreen
const inWorkflowCanvas = pathname.endsWith('/workflow')
const workflowCanvasMaximize = localStorage.getItem('workflow-canvas-maximize') === 'true'
const [hideHeader, setHideHeader] = useState(workflowCanvasMaximize)
@ -25,14 +25,12 @@ const HeaderWrapper = ({
setHideHeader(v.payload)
})
if (hideHeader && inWorkflowCanvas)
return null
return (
<div className={classNames(
'sticky left-0 right-0 top-0 z-[15] flex min-h-[56px] shrink-0 grow-0 basis-auto flex-col',
s.header,
isBordered ? 'border-b border-divider-regular' : '',
hideHeader && inWorkflowCanvas && 'hidden',
)}
>
{children}

View File

@ -85,14 +85,6 @@ const TextGeneration: FC<IMainProps> = ({
const router = useRouter()
const pathname = usePathname()
useEffect(() => {
const params = new URLSearchParams(searchParams)
if (params.has('mode')) {
params.delete('mode')
router.replace(`${pathname}?${params.toString()}`)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
// Notice this situation isCallBatchAPI but not in batch tab
const [isCallBatchAPI, setIsCallBatchAPI] = useState(false)

View File

@ -6,9 +6,9 @@ import Item from './item'
import type { Plugin } from '@/app/components/plugins/types.ts'
import cn from '@/utils/classnames'
import Link from 'next/link'
import { MARKETPLACE_URL_PREFIX } from '@/config'
import { RiArrowRightUpLine, RiSearchLine } from '@remixicon/react'
import { noop } from 'lodash-es'
import { getMarketplaceUrl } from '@/utils/var'
export type ListProps = {
wrapElemRef: React.RefObject<HTMLElement>
@ -32,7 +32,7 @@ const List = forwardRef<ListRef, ListProps>(({
const { t } = useTranslation()
const hasFilter = !searchText
const hasRes = list.length > 0
const urlWithSearchText = `${MARKETPLACE_URL_PREFIX}/?q=${searchText}&tags=${tags.join(',')}`
const urlWithSearchText = getMarketplaceUrl('', { q: searchText, tags: tags.join(',') })
const nextToStickyELemRef = useRef<HTMLDivElement>(null)
const { handleScroll, scrollPosition } = useStickyScroll({
@ -71,7 +71,7 @@ const List = forwardRef<ListRef, ListProps>(({
return (
<Link
className='system-sm-medium sticky bottom-0 z-10 flex h-8 cursor-pointer items-center rounded-b-lg border-[0.5px] border-t border-components-panel-border bg-components-panel-bg-blur px-4 py-1 text-text-accent-light-mode-only shadow-lg'
href={`${MARKETPLACE_URL_PREFIX}/`}
href={getMarketplaceUrl('')}
target='_blank'
>
<span>{t('plugin.findMoreInMarketplace')}</span>

View File

@ -11,12 +11,11 @@ import {
useEditInspectorVar,
useInvalidateConversationVarValues,
useInvalidateSysVarValues,
useLastRun,
useResetConversationVar,
useResetToLastRunValue,
useSysVarValues,
} from '@/service/use-workflow'
import { useCallback, useEffect, useState } from 'react'
import { useCallback } from 'react'
import { isConversationVar, isENV, isSystemVar } from '../nodes/_base/components/variable/utils'
import produce from 'immer'
import type { Node } from '@/app/components/workflow/types'
@ -123,6 +122,7 @@ const useInspectVarsCrud = () => {
nodeType: nodeInfo.data.type,
title: nodeInfo.data.title,
vars: payload,
nodePayload: nodeInfo.data,
})
}
else {
@ -180,16 +180,6 @@ const useInspectVarsCrud = () => {
invalidateSysVarValues()
}, [doEditInspectorVar, invalidateConversationVarValues, invalidateSysVarValues, setInspectVarValue])
const [currNodeId, setCurrNodeId] = useState<string | null>(null)
const [currEditVarId, setCurrEditVarId] = useState<string | null>(null)
const { data } = useLastRun(appId, currNodeId || '', !!currNodeId)
useEffect(() => {
if (data && currNodeId && currEditVarId) {
const inspectVar = getNodeInspectVars(currNodeId)?.vars?.find(item => item.id === currEditVarId)
resetToLastRunVarInStore(currNodeId, currEditVarId, data.outputs?.[inspectVar?.selector?.[1] || ''])
}
}, [data, currNodeId, currEditVarId, getNodeInspectVars, editInspectVarValue, resetToLastRunVarInStore])
const renameInspectVarName = async (nodeId: string, oldName: string, newName: string) => {
const varId = getVarId(nodeId, oldName)
if (!varId)
@ -212,9 +202,13 @@ const useInspectVarsCrud = () => {
}, [getInspectVar])
const resetToLastRunVar = async (nodeId: string, varId: string) => {
await doResetToLastRunValue(varId)
setCurrNodeId(nodeId)
setCurrEditVarId(varId)
const isSysVar = nodeId === 'sys'
const data = await doResetToLastRunValue(varId)
if(isSysVar)
invalidateSysVarValues()
else
resetToLastRunVarInStore(nodeId, varId, data.value)
}
return {

View File

@ -15,7 +15,7 @@ import { pluginManifestToCardPluginProps } from '@/app/components/plugins/instal
import { Badge as Badge2, BadgeState } from '@/app/components/base/badge/index'
import Link from 'next/link'
import { useTranslation } from 'react-i18next'
import { MARKETPLACE_URL_PREFIX } from '@/config'
import { getMarketplaceUrl } from '@/utils/var'
export type SwitchPluginVersionProps = {
uniqueIdentifier: string
@ -82,7 +82,7 @@ export const SwitchPluginVersion: FC<SwitchPluginVersionProps> = (props) => {
modalBottomLeft={
<Link
className='flex items-center justify-center gap-1'
href={`${MARKETPLACE_URL_PREFIX}/plugins/${pluginDetail.declaration.author}/${pluginDetail.declaration.name}`}
href={getMarketplaceUrl(`/plugins/${pluginDetail.declaration.author}/${pluginDetail.declaration.name}`)}
target='_blank'
>
<span className='system-xs-regular text-xs text-text-accent'>

View File

@ -65,10 +65,11 @@ const VarList: FC<Props> = ({
}, [list, onVarNameChange, onChange])
const handleVarReferenceChange = useCallback((index: number) => {
return (value: ValueSelector | string, varKindType: VarKindType) => {
return (value: ValueSelector | string, varKindType: VarKindType, varInfo?: Var) => {
const newList = produce(list, (draft) => {
if (!isSupportConstantValue || varKindType === VarKindType.variable) {
draft[index].value_selector = value as ValueSelector
draft[index].value_type = varInfo?.type
if (isSupportConstantValue)
draft[index].variable_type = VarKindType.variable

View File

@ -184,9 +184,11 @@ const VarReferencePicker: FC<Props> = ({
return startNode?.data
const node = getNodeInfoById(availableNodes, outputVarNodeId)?.data
return {
...node,
id: outputVarNodeId,
if (node) {
return {
...node,
id: outputVarNodeId,
}
}
}, [value, hasValue, isConstant, isIterationVar, iterationNode, availableNodes, outputVarNodeId, startNode, isLoopVar, loopNode])

View File

@ -14,6 +14,7 @@ import Split from '@/app/components/workflow/nodes/_base/components/split'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
import TypeSelector from '@/app/components/workflow/nodes/_base/components/selector'
import type { NodePanelProps } from '@/app/components/workflow/types'
import SyncButton from '@/app/components/base/button/sync-button'
const i18nPrefix = 'workflow.nodes.code'
const codeLanguages = [
@ -40,6 +41,7 @@ const Panel: FC<NodePanelProps<CodeNodeType>> = ({
handleVarListChange,
handleAddVariable,
handleRemoveVariable,
handleSyncFunctionSignature,
handleCodeChange,
handleCodeLanguageChange,
handleVarsChange,
@ -68,7 +70,12 @@ const Panel: FC<NodePanelProps<CodeNodeType>> = ({
<Field
title={t(`${i18nPrefix}.inputVars`)}
operations={
!readOnly ? <AddButton onClick={handleAddVariable} /> : undefined
!readOnly ? (
<div className="flex gap-2">
<SyncButton popupContent={t(`${i18nPrefix}.syncFunctionSignature`)} onClick={handleSyncFunctionSignature} />
<AddButton onClick={handleAddVariable} />
</div>
) : undefined
}
>
<VarList

View File

@ -84,6 +84,65 @@ const useConfig = (id: string, payload: CodeNodeType) => {
setInputs(newInputs)
}, [allLanguageDefault, inputs, setInputs])
const handleSyncFunctionSignature = useCallback(() => {
const generateSyncSignatureCode = (code: string) => {
let mainDefRe
let newMainDef
if (inputs.code_language === CodeLanguage.javascript) {
mainDefRe = /function\s+main\b\s*\([\s\S]*?\)/g
newMainDef = 'function main({{var_list}})'
let param_list = inputs.variables?.map(item => item.variable).join(', ') || ''
param_list = param_list ? `{${param_list}}` : ''
newMainDef = newMainDef.replace('{{var_list}}', param_list)
}
else if (inputs.code_language === CodeLanguage.python3) {
mainDefRe = /def\s+main\b\s*\([\s\S]*?\)/g
const param_list = []
for (const item of inputs.variables) {
let param = item.variable
let param_type = ''
switch (item.value_type) {
case VarType.string:
param_type = ': str'
break
case VarType.number:
param_type = ': float'
break
case VarType.object:
param_type = ': dict'
break
case VarType.array:
param_type = ': list'
break
case VarType.arrayNumber:
param_type = ': list[float]'
break
case VarType.arrayString:
param_type = ': list[str]'
break
case VarType.arrayObject:
param_type = ': list[dict]'
break
}
param += param_type
param_list.push(`${param}`)
}
newMainDef = `def main(${param_list.join(', ')})`
}
else { return code }
const newCode = code.replace(mainDefRe, newMainDef)
return newCode
}
const newInputs = produce(inputs, (draft) => {
draft.code = generateSyncSignatureCode(draft.code)
})
setInputs(newInputs)
}, [inputs, setInputs])
const {
handleVarsChange,
handleAddVariable: handleAddOutputVariable,
@ -119,6 +178,7 @@ const useConfig = (id: string, payload: CodeNodeType) => {
handleVarListChange,
handleAddVariable,
handleRemoveVariable,
handleSyncFunctionSignature,
handleCodeChange,
handleCodeLanguageChange,
handleVarsChange,

View File

@ -19,6 +19,8 @@ import Editor from '@/app/components/workflow/nodes/_base/components/prompt/edit
import StructureOutput from './components/structure-output'
import Switch from '@/app/components/base/switch'
import { RiAlertFill, RiQuestionLine } from '@remixicon/react'
import { fetchAndMergeValidCompletionParams } from '@/utils/completion-params'
import Toast from '@/app/components/base/toast'
const i18nPrefix = 'workflow.nodes.llm'
@ -68,10 +70,27 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
modelId: string
mode?: string
}) => {
handleCompletionParamsChange({})
handleModelChanged(model)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
(async () => {
try {
const { params: filtered, removedDetails } = await fetchAndMergeValidCompletionParams(
model.provider,
model.modelId,
inputs.model.completion_params,
)
const keys = Object.keys(removedDetails)
if (keys.length)
Toast.notify({ type: 'warning', message: `${t('common.modelProvider.parametersInvalidRemoved')}: ${keys.map(k => `${k} (${removedDetails[k]})`).join(', ')}` })
handleCompletionParamsChange(filtered)
}
catch (e) {
Toast.notify({ type: 'error', message: t('common.error') })
handleCompletionParamsChange({})
}
finally {
handleModelChanged(model)
}
})()
}, [inputs.model.completion_params])
return (
<div className='mt-2'>

View File

@ -136,6 +136,7 @@ export type Variable = {
variable: string
}
value_selector: ValueSelector
value_type?: VarType
variable_type?: VarKindType
value?: string
options?: string[]

View File

@ -63,6 +63,17 @@ const Right = ({
resetConversationVar(currentNodeVar.var.id)
}
const getCopyContent = () => {
const value = currentNodeVar?.var.value
if (value === null || value === undefined)
return ''
if (typeof value === 'object')
return JSON.stringify(value)
return String(value)
}
return (
<div className={cn('flex h-full flex-col')}>
{/* header */}
@ -124,7 +135,7 @@ const Right = ({
</Tooltip>
)}
{currentNodeVar.var.value_type !== 'secret' && (
<CopyFeedback content={currentNodeVar.var.value ? JSON.stringify(currentNodeVar.var.value) : ''} />
<CopyFeedback content={getCopyContent()} />
)}
</>
)}

View File

@ -29,9 +29,10 @@ export const useTabSearchParams = ({
const router = useRouter()
const pathName = pathnameFromHook || window?.location?.pathname
const searchParams = useSearchParams()
const searchParamValue = searchParams.has(searchParamName) ? decodeURIComponent(searchParams.get(searchParamName)!) : defaultTab
const [activeTab, setTab] = useState<string>(
!disableSearchParams
? (searchParams.get(searchParamName) || defaultTab)
? searchParamValue
: defaultTab,
)
@ -39,7 +40,7 @@ export const useTabSearchParams = ({
setTab(newActiveTab)
if (disableSearchParams)
return
router[`${routingBehavior}`](`${pathName}?${searchParamName}=${newActiveTab}`)
router[`${routingBehavior}`](`${pathName}?${searchParamName}=${encodeURIComponent(newActiveTab)}`)
}
return [activeTab, setActiveTab] as const

View File

@ -390,6 +390,8 @@ const translation = {
addChildChunk: 'Untergeordneten Block hinzufügen',
regenerationConfirmTitle: 'Möchten Sie untergeordnete Chunks regenerieren?',
searchResults_one: 'ERGEBNIS',
keywordEmpty: 'Das Schlüsselwort darf nicht leer sein.',
keywordDuplicate: 'Das Schlüsselwort existiert bereits',
},
}

View File

@ -199,9 +199,9 @@ const translation = {
accessControl: 'Web App Access Control',
accessItemsDescription: {
anyone: 'Anyone can access the web app (no login required)',
specific: 'Only specific members within the platform can access the Web application',
organization: 'All members within the platform can access the Web application',
external: 'Only authenticated external users can access the Web application',
specific: 'Only specific members within the platform can access the web app',
organization: 'All members within the platform can access the web app',
external: 'Only authenticated external users can access the web app',
},
accessControlDialog: {
title: 'Web App Access Control',
@ -218,7 +218,7 @@ const translation = {
members_one: '{{count}} MEMBER',
members_other: '{{count}} MEMBERS',
noGroupsOrMembers: 'No groups or members selected',
webAppSSONotEnabledTip: 'Please contact your organization administrator to configure external authentication for the Web application.',
webAppSSONotEnabledTip: 'Please contact your organization administrator to configure external authentication for the web app.',
operateGroupAndMember: {
searchPlaceholder: 'Search groups and members',
allMembers: 'All members',

View File

@ -456,6 +456,7 @@ const translation = {
connected: 'Connected',
disconnected: 'Disconnected',
changeAuthorizedPages: 'Change authorized pages',
integratedAlert: 'Notion is integrated via internal credential, no need to re-authorize.',
pagesAuthorized: 'Pages authorized',
sync: 'Sync',
remove: 'Remove',

View File

@ -549,6 +549,7 @@ const translation = {
advancedDependencies: 'Advanced Dependencies',
advancedDependenciesTip: 'Add some preloaded dependencies that take more time to consume or are not default built-in here',
searchDependencies: 'Search Dependencies',
syncFunctionSignature: 'Sync function signature to code',
},
templateTransform: {
inputVars: 'Input Variables',

View File

@ -389,6 +389,8 @@ const translation = {
characters_one: 'carácter',
regenerationSuccessMessage: 'Puede cerrar esta ventana.',
regenerationConfirmTitle: '¿Desea regenerar fragmentos secundarios?',
keywordEmpty: 'La palabra clave no puede estar vacía',
keywordDuplicate: 'La palabra clave ya existe',
},
}

View File

@ -388,6 +388,8 @@ const translation = {
regeneratingMessage: 'این ممکن است یک لحظه طول بکشد، لطفا صبر کنید...',
regenerationConfirmTitle: 'آیا می خواهید تکه های کودک را بازسازی کنید؟',
regenerationSuccessMessage: 'می توانید این پنجره را ببندید.',
keywordEmpty: 'کلمه کلیدی نمی‌تواند خالی باشد',
keywordDuplicate: 'این کلیدواژه قبلاً وجود دارد',
},
}

View File

@ -389,6 +389,8 @@ const translation = {
searchResults_zero: 'RÉSULTAT',
empty: 'Aucun Chunk trouvé',
editChildChunk: 'Modifier le morceau enfant',
keywordDuplicate: 'Le mot-clé existe déjà',
keywordEmpty: 'Le mot-clé ne peut pas être vide.',
},
}

View File

@ -390,6 +390,8 @@ const translation = {
chunkAdded: '1 हिस्सा जोड़ा गया',
chunkDetail: 'चंक विवरण',
regenerationConfirmMessage: 'चाइल्ड चंक्स को रीजनरेट करने से वर्तमान चाइल्ड चंक्स ओवरराइट हो जाएंगे, जिसमें संपादित चंक्स और नए जोड़े गए चंक्स शामिल हैं। पुनरुत्थान को पूर्ववत नहीं किया जा सकता है।',
keywordDuplicate: 'कीवर्ड पहले से मौजूद है',
keywordEmpty: 'कीवर्ड ख़ाली नहीं हो सकता',
},
}

View File

@ -391,6 +391,8 @@ const translation = {
regenerationSuccessMessage: 'È possibile chiudere questa finestra.',
childChunkAdded: '1 blocco figlio aggiunto',
childChunks_other: 'BLOCCHI FIGLIO',
keywordEmpty: 'La parola chiave non può essere vuota',
keywordDuplicate: 'La parola chiave esiste già',
},
}

View File

@ -388,6 +388,8 @@ const translation = {
editedAt: '編集日時',
expandChunks: 'チャンクを展開',
collapseChunks: 'チャンクを折りたたむ',
keywordDuplicate: 'そのキーワードは既に存在しています',
keywordEmpty: 'キーワードは空であってはいけません',
},
}

View File

@ -550,6 +550,7 @@ const translation = {
advancedDependencies: '高度な依存関係',
advancedDependenciesTip: '消費に時間がかかる、またはデフォルトで組み込まれていない事前ロードされた依存関係を追加します',
searchDependencies: '依存関係を検索',
syncFunctionSignature: 'コードの関数署名を同期',
},
templateTransform: {
inputVars: '入力変数',

View File

@ -388,6 +388,8 @@ const translation = {
addChunk: '청크 추가 (Add Chunk)',
characters_other: '문자',
regeneratingMessage: '시간이 걸릴 수 있으니 잠시만 기다려 주십시오...',
keywordDuplicate: '키워드가 이미 존재합니다.',
keywordEmpty: '키워드는 비워둘 수 없습니다.',
},
}

View File

@ -390,6 +390,8 @@ const translation = {
newChildChunk: 'Nowy fragment podrzędny',
clearFilter: 'Wyczyść filtr',
childChunks_one: 'FRAGMENT POTOMNY',
keywordDuplicate: 'Słowo kluczowe już istnieje',
keywordEmpty: 'Słowo kluczowe nie może być puste',
},
}

View File

@ -389,6 +389,8 @@ const translation = {
newChildChunk: 'Novo pedaço filho',
characters_one: 'personagem',
parentChunk: 'Pedaço pai',
keywordEmpty: 'A palavra-chave não pode estar vazia',
keywordDuplicate: 'A palavra-chave já existe',
},
}

View File

@ -389,6 +389,8 @@ const translation = {
regeneratingTitle: 'Regenerarea bucăților secundare',
addChildChunk: 'Adăugați o bucată copil',
searchResults_other: 'REZULTATELE',
keywordDuplicate: 'Cuvântul cheie există deja',
keywordEmpty: 'Cuvântul cheie nu poate fi gol',
},
}

View File

@ -389,6 +389,8 @@ const translation = {
characters_one: 'характер',
addChildChunk: 'Добавить дочерний чанк',
newChildChunk: 'Новый дочерний чанк',
keywordEmpty: 'Ключевое слово не может быть пустым',
keywordDuplicate: 'Ключевое слово уже существует',
},
}

View File

@ -389,6 +389,8 @@ const translation = {
chunk: 'Kos',
addChunk: 'Dodajanje kosa',
childChunkAdded: 'Dodan je 1 kos otroka',
keywordDuplicate: 'Ključna beseda že obstaja',
keywordEmpty: 'Ključna beseda ne more biti prazna',
},
}

View File

@ -388,6 +388,8 @@ const translation = {
searchResults_other: 'ผลลัพธ์',
regenerationSuccessMessage: 'คุณสามารถปิดหน้าต่างนี้ได้',
childChunks_one: 'ก้อนเด็ก',
keywordDuplicate: 'คำสำคัญมีอยู่แล้ว',
keywordEmpty: 'คีย์เวิร์ดไม่สามารถว่างเปล่าได้',
},
}

View File

@ -388,6 +388,8 @@ const translation = {
chunks_other: 'Parçalar',
editedAt: 'Şurada düzenlendi:',
addChildChunk: 'Alt Parça Ekle',
keywordDuplicate: 'Anahtar kelime zaten var',
keywordEmpty: 'Anahtar kelime boş olamaz',
},
}

View File

@ -389,6 +389,8 @@ const translation = {
regenerationSuccessMessage: 'Ви можете закрити це вікно.',
expandChunks: 'Розгортання фрагментів',
regenerationConfirmTitle: 'Хочете регенерувати дитячі шматки?',
keywordEmpty: 'Ключове слово не може бути порожнім',
keywordDuplicate: 'Ключове слово вже існує',
},
}

View File

@ -388,6 +388,8 @@ const translation = {
clearFilter: 'Bộ lọc rõ ràng',
chunk: 'Khúc',
edited: 'EDITED',
keywordDuplicate: 'Từ khóa đã tồn tại',
keywordEmpty: 'Từ khóa không được để trống',
},
}

View File

@ -387,6 +387,8 @@ const translation = {
editedAt: '编辑于',
expandChunks: '展开分段',
collapseChunks: '折叠分段',
keywordEmpty: '关键词不能为空',
keywordDuplicate: '关键词已经存在',
},
}

View File

@ -550,6 +550,7 @@ const translation = {
advancedDependencies: '高级依赖',
advancedDependenciesTip: '在这里添加一些预加载需要消耗较多时间或非默认内置的依赖包',
searchDependencies: '搜索依赖',
syncFunctionSignature: '同步函数签名至代码',
},
templateTransform: {
inputVars: '输入变量',

View File

@ -388,6 +388,8 @@ const translation = {
searchResults_zero: '結果',
parentChunks_other: '父塊',
newChildChunk: '新兒童塊',
keywordEmpty: '關鍵字不能為空',
keywordDuplicate: '關鍵字已經存在',
},
}

View File

@ -544,6 +544,7 @@ const translation = {
advancedDependencies: '高級依賴',
advancedDependenciesTip: '在這裡添加一些預加載需要消耗較多時間或非默認內置的依賴包',
searchDependencies: '搜索依賴',
syncFunctionSignature: '同步函數簽名至代碼',
},
templateTransform: {
inputVars: '輸入變量',

View File

@ -103,7 +103,7 @@
"mime": "^4.0.4",
"mitt": "^3.0.1",
"negotiator": "^0.6.3",
"next": "15.2.3",
"next": "15.2.4",
"next-themes": "^0.4.3",
"pinyin-pro": "^3.25.0",
"qrcode.react": "^4.2.0",
@ -235,7 +235,11 @@
},
"pnpm": {
"overrides": {
"esbuild@<0.25.0": "0.25.0"
"esbuild@<0.25.0": "0.25.0",
"pbkdf2@<3.1.3": "3.1.3",
"vite@<6.2.7": "6.2.7",
"prismjs@<1.30.0": "1.30.0",
"brace-expansion@<2.0.2": "2.0.2"
}
}
}

193
web/pnpm-lock.yaml generated
View File

@ -9,6 +9,10 @@ overrides:
'@types/react-dom': ~18.2.0
string-width: 4.2.3
esbuild@<0.25.0: 0.25.0
pbkdf2@<3.1.3: 3.1.3
vite@<6.2.7: 6.2.7
prismjs@<1.30.0: 1.30.0
brace-expansion@<2.0.2: 2.0.2
importers:
@ -207,8 +211,8 @@ importers:
specifier: ^0.6.3
version: 0.6.4
next:
specifier: 15.2.3
version: 15.2.3(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3)
specifier: 15.2.4
version: 15.2.4(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3)
next-themes:
specifier: ^0.4.3
version: 0.4.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@ -392,7 +396,7 @@ importers:
version: 8.5.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.5.0)
'@storybook/nextjs':
specifier: 8.5.0
version: 8.5.0(esbuild@0.25.0)(next@15.2.3(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3)(storybook@8.5.0)(type-fest@4.39.1)(typescript@4.9.5)(uglify-js@3.19.3)(webpack-hot-middleware@2.26.1)(webpack@5.99.5(esbuild@0.25.0)(uglify-js@3.19.3))
version: 8.5.0(esbuild@0.25.0)(next@15.2.4(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3)(storybook@8.5.0)(type-fest@4.39.1)(typescript@4.9.5)(uglify-js@3.19.3)(webpack-hot-middleware@2.26.1)(webpack@5.99.5(esbuild@0.25.0)(uglify-js@3.19.3))
'@storybook/react':
specifier: 8.5.0
version: 8.5.0(@storybook/test@8.5.0(storybook@8.5.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(storybook@8.5.0)(typescript@4.9.5)
@ -2086,8 +2090,8 @@ packages:
'@napi-rs/wasm-runtime@0.2.8':
resolution: {integrity: sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg==}
'@next/env@15.2.3':
resolution: {integrity: sha512-a26KnbW9DFEUsSxAxKBORR/uD9THoYoKbkpFywMN/AFvboTt94b8+g/07T8J6ACsdLag8/PDU60ov4rPxRAixw==}
'@next/env@15.2.4':
resolution: {integrity: sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==}
'@next/eslint-plugin-next@15.3.0':
resolution: {integrity: sha512-511UUcpWw5GWTyKfzW58U2F/bYJyjLE9e3SlnGK/zSXq7RqLlqFO8B9bitJjumLpj317fycC96KZ2RZsjGNfBw==}
@ -2103,50 +2107,50 @@ packages:
'@mdx-js/react':
optional: true
'@next/swc-darwin-arm64@15.2.3':
resolution: {integrity: sha512-uaBhA8aLbXLqwjnsHSkxs353WrRgQgiFjduDpc7YXEU0B54IKx3vU+cxQlYwPCyC8uYEEX7THhtQQsfHnvv8dw==}
'@next/swc-darwin-arm64@15.2.4':
resolution: {integrity: sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@next/swc-darwin-x64@15.2.3':
resolution: {integrity: sha512-pVwKvJ4Zk7h+4hwhqOUuMx7Ib02u3gDX3HXPKIShBi9JlYllI0nU6TWLbPT94dt7FSi6mSBhfc2JrHViwqbOdw==}
'@next/swc-darwin-x64@15.2.4':
resolution: {integrity: sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@next/swc-linux-arm64-gnu@15.2.3':
resolution: {integrity: sha512-50ibWdn2RuFFkOEUmo9NCcQbbV9ViQOrUfG48zHBCONciHjaUKtHcYFiCwBVuzD08fzvzkWuuZkd4AqbvKO7UQ==}
'@next/swc-linux-arm64-gnu@15.2.4':
resolution: {integrity: sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-arm64-musl@15.2.3':
resolution: {integrity: sha512-2gAPA7P652D3HzR4cLyAuVYwYqjG0mt/3pHSWTCyKZq/N/dJcUAEoNQMyUmwTZWCJRKofB+JPuDVP2aD8w2J6Q==}
'@next/swc-linux-arm64-musl@15.2.4':
resolution: {integrity: sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
'@next/swc-linux-x64-gnu@15.2.3':
resolution: {integrity: sha512-ODSKvrdMgAJOVU4qElflYy1KSZRM3M45JVbeZu42TINCMG3anp7YCBn80RkISV6bhzKwcUqLBAmOiWkaGtBA9w==}
'@next/swc-linux-x64-gnu@15.2.4':
resolution: {integrity: sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-linux-x64-musl@15.2.3':
resolution: {integrity: sha512-ZR9kLwCWrlYxwEoytqPi1jhPd1TlsSJWAc+H/CJHmHkf2nD92MQpSRIURR1iNgA/kuFSdxB8xIPt4p/T78kwsg==}
'@next/swc-linux-x64-musl@15.2.4':
resolution: {integrity: sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
'@next/swc-win32-arm64-msvc@15.2.3':
resolution: {integrity: sha512-+G2FrDcfm2YDbhDiObDU/qPriWeiz/9cRR0yMWJeTLGGX6/x8oryO3tt7HhodA1vZ8r2ddJPCjtLcpaVl7TE2Q==}
'@next/swc-win32-arm64-msvc@15.2.4':
resolution: {integrity: sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@next/swc-win32-x64-msvc@15.2.3':
resolution: {integrity: sha512-gHYS9tc+G2W0ZC8rBL+H6RdtXIyk40uLiaos0yj5US85FNhbFEndMA2nW3z47nzOWiSvXTZ5kBClc3rD0zJg0w==}
'@next/swc-win32-x64-msvc@15.2.4':
resolution: {integrity: sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@ -3311,7 +3315,7 @@ packages:
resolution: {integrity: sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==}
peerDependencies:
msw: ^2.4.9
vite: ^5.0.0 || ^6.0.0
vite: 6.2.7
peerDependenciesMeta:
msw:
optional: true
@ -3726,11 +3730,8 @@ packages:
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
brace-expansion@2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
brace-expansion@2.0.2:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
@ -4076,9 +4077,6 @@ packages:
compare-versions@6.1.1:
resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==}
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
confbox@0.1.8:
resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}
@ -4134,6 +4132,9 @@ packages:
create-ecdh@4.0.4:
resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==}
create-hash@1.1.3:
resolution: {integrity: sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==}
create-hash@1.2.0:
resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==}
@ -5383,6 +5384,9 @@ packages:
has-unicode@2.0.1:
resolution: {integrity: sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==}
hash-base@2.0.2:
resolution: {integrity: sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==}
hash-base@3.0.5:
resolution: {integrity: sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==}
engines: {node: '>= 0.10'}
@ -6565,8 +6569,8 @@ packages:
react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
next@15.2.3:
resolution: {integrity: sha512-x6eDkZxk2rPpu46E1ZVUWIBhYCLszmUY6fvHBFcbzJ9dD+qRX6vcHusaqqDlnY+VngKzKbAiG2iRCkPbmi8f7w==}
next@15.2.4:
resolution: {integrity: sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
hasBin: true
peerDependencies:
@ -6859,8 +6863,8 @@ packages:
resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==}
engines: {node: '>= 14.16'}
pbkdf2@3.1.2:
resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==}
pbkdf2@3.1.3:
resolution: {integrity: sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==}
engines: {node: '>=0.12'}
pdfjs-dist@4.4.168:
@ -7042,10 +7046,6 @@ packages:
resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
prismjs@1.27.0:
resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==}
engines: {node: '>=6'}
prismjs@1.30.0:
resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==}
engines: {node: '>=6'}
@ -7527,6 +7527,9 @@ packages:
deprecated: Rimraf versions prior to v4 are no longer supported
hasBin: true
ripemd160@2.0.1:
resolution: {integrity: sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==}
ripemd160@2.0.2:
resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==}
@ -8042,6 +8045,10 @@ packages:
tmpl@1.0.5:
resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
to-buffer@1.2.1:
resolution: {integrity: sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==}
engines: {node: '>= 0.4'}
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
@ -8367,8 +8374,8 @@ packages:
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
vite@6.2.6:
resolution: {integrity: sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==}
vite@6.2.7:
resolution: {integrity: sha512-qg3LkeuinTrZoJHHF94coSaTfIPyBYoywp+ys4qu20oSJFbKMYoIJo0FWJT9q6Vp49l6z9IsJRbHdcGtiKbGoQ==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
hasBin: true
peerDependencies:
@ -10565,7 +10572,7 @@ snapshots:
'@tybys/wasm-util': 0.9.0
optional: true
'@next/env@15.2.3': {}
'@next/env@15.2.4': {}
'@next/eslint-plugin-next@15.3.0':
dependencies:
@ -10578,28 +10585,28 @@ snapshots:
'@mdx-js/loader': 3.1.0(acorn@8.14.1)(webpack@5.99.5(esbuild@0.25.0)(uglify-js@3.19.3))
'@mdx-js/react': 3.1.0(@types/react@18.2.79)(react@19.0.0)
'@next/swc-darwin-arm64@15.2.3':
'@next/swc-darwin-arm64@15.2.4':
optional: true
'@next/swc-darwin-x64@15.2.3':
'@next/swc-darwin-x64@15.2.4':
optional: true
'@next/swc-linux-arm64-gnu@15.2.3':
'@next/swc-linux-arm64-gnu@15.2.4':
optional: true
'@next/swc-linux-arm64-musl@15.2.3':
'@next/swc-linux-arm64-musl@15.2.4':
optional: true
'@next/swc-linux-x64-gnu@15.2.3':
'@next/swc-linux-x64-gnu@15.2.4':
optional: true
'@next/swc-linux-x64-musl@15.2.3':
'@next/swc-linux-x64-musl@15.2.4':
optional: true
'@next/swc-win32-arm64-msvc@15.2.3':
'@next/swc-win32-arm64-msvc@15.2.4':
optional: true
'@next/swc-win32-x64-msvc@15.2.3':
'@next/swc-win32-x64-msvc@15.2.4':
optional: true
'@nodelib/fs.scandir@2.1.5':
@ -11211,7 +11218,7 @@ snapshots:
dependencies:
storybook: 8.5.0
'@storybook/nextjs@8.5.0(esbuild@0.25.0)(next@15.2.3(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3)(storybook@8.5.0)(type-fest@4.39.1)(typescript@4.9.5)(uglify-js@3.19.3)(webpack-hot-middleware@2.26.1)(webpack@5.99.5(esbuild@0.25.0)(uglify-js@3.19.3))':
'@storybook/nextjs@8.5.0(esbuild@0.25.0)(next@15.2.4(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3))(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3)(storybook@8.5.0)(type-fest@4.39.1)(typescript@4.9.5)(uglify-js@3.19.3)(webpack-hot-middleware@2.26.1)(webpack@5.99.5(esbuild@0.25.0)(uglify-js@3.19.3))':
dependencies:
'@babel/core': 7.26.10
'@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.10)
@ -11237,7 +11244,7 @@ snapshots:
find-up: 5.0.0
image-size: 1.2.1
loader-utils: 3.3.1
next: 15.2.3(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3)
next: 15.2.4(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3)
node-polyfill-webpack-plugin: 2.0.1(webpack@5.99.5(esbuild@0.25.0)(uglify-js@3.19.3))
pnp-webpack-plugin: 1.7.0(typescript@4.9.5)
postcss: 8.5.3
@ -11956,13 +11963,13 @@ snapshots:
chai: 5.2.0
tinyrainbow: 2.0.0
'@vitest/mocker@3.1.1(vite@6.2.6(@types/node@18.15.0)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))':
'@vitest/mocker@3.1.1(vite@6.2.7(@types/node@18.15.0)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))':
dependencies:
'@vitest/spy': 3.1.1
estree-walker: 3.0.3
magic-string: 0.30.17
optionalDependencies:
vite: 6.2.6(@types/node@18.15.0)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)
vite: 6.2.7(@types/node@18.15.0)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)
'@vitest/pretty-format@2.0.5':
dependencies:
@ -12488,12 +12495,7 @@ snapshots:
boolbase@1.0.0: {}
brace-expansion@1.1.11:
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
brace-expansion@2.0.1:
brace-expansion@2.0.2:
dependencies:
balanced-match: 1.0.2
@ -12846,8 +12848,6 @@ snapshots:
compare-versions@6.1.1: {}
concat-map@0.0.1: {}
confbox@0.1.8: {}
confbox@0.2.2: {}
@ -12905,6 +12905,13 @@ snapshots:
bn.js: 4.12.1
elliptic: 6.6.1
create-hash@1.1.3:
dependencies:
cipher-base: 1.0.6
inherits: 2.0.4
ripemd160: 2.0.2
sha.js: 2.4.11
create-hash@1.2.0:
dependencies:
cipher-base: 1.0.6
@ -12959,7 +12966,7 @@ snapshots:
diffie-hellman: 5.0.3
hash-base: 3.0.5
inherits: 2.0.4
pbkdf2: 3.1.2
pbkdf2: 3.1.3
public-encrypt: 4.0.3
randombytes: 2.1.0
randomfill: 1.0.4
@ -14577,6 +14584,10 @@ snapshots:
has-unicode@2.0.1:
optional: true
hash-base@2.0.2:
dependencies:
inherits: 2.0.4
hash-base@3.0.5:
dependencies:
inherits: 2.0.4
@ -16239,15 +16250,15 @@ snapshots:
minimatch@10.0.1:
dependencies:
brace-expansion: 2.0.1
brace-expansion: 2.0.2
minimatch@3.1.2:
dependencies:
brace-expansion: 1.1.11
brace-expansion: 2.0.2
minimatch@9.0.5:
dependencies:
brace-expansion: 2.0.1
brace-expansion: 2.0.2
minimist@1.2.8: {}
@ -16307,9 +16318,9 @@ snapshots:
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
next@15.2.3(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3):
next@15.2.4(@babel/core@7.26.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3):
dependencies:
'@next/env': 15.2.3
'@next/env': 15.2.4
'@swc/counter': 0.1.3
'@swc/helpers': 0.5.15
busboy: 1.6.0
@ -16319,14 +16330,14 @@ snapshots:
react-dom: 19.0.0(react@19.0.0)
styled-jsx: 5.1.6(@babel/core@7.26.10)(react@19.0.0)
optionalDependencies:
'@next/swc-darwin-arm64': 15.2.3
'@next/swc-darwin-x64': 15.2.3
'@next/swc-linux-arm64-gnu': 15.2.3
'@next/swc-linux-arm64-musl': 15.2.3
'@next/swc-linux-x64-gnu': 15.2.3
'@next/swc-linux-x64-musl': 15.2.3
'@next/swc-win32-arm64-msvc': 15.2.3
'@next/swc-win32-x64-msvc': 15.2.3
'@next/swc-darwin-arm64': 15.2.4
'@next/swc-darwin-x64': 15.2.4
'@next/swc-linux-arm64-gnu': 15.2.4
'@next/swc-linux-arm64-musl': 15.2.4
'@next/swc-linux-x64-gnu': 15.2.4
'@next/swc-linux-x64-musl': 15.2.4
'@next/swc-win32-arm64-msvc': 15.2.4
'@next/swc-win32-x64-msvc': 15.2.4
sass: 1.86.3
sharp: 0.33.5
transitivePeerDependencies:
@ -16563,7 +16574,7 @@ snapshots:
browserify-aes: 1.2.0
evp_bytestokey: 1.0.3
hash-base: 3.0.5
pbkdf2: 3.1.2
pbkdf2: 3.1.3
safe-buffer: 5.2.1
parse-entities@2.0.0:
@ -16644,13 +16655,14 @@ snapshots:
pathval@2.0.0: {}
pbkdf2@3.1.2:
pbkdf2@3.1.3:
dependencies:
create-hash: 1.2.0
create-hash: 1.1.3
create-hmac: 1.1.7
ripemd160: 2.0.2
ripemd160: 2.0.1
safe-buffer: 5.2.1
sha.js: 2.4.11
to-buffer: 1.2.1
pdfjs-dist@4.4.168:
optionalDependencies:
@ -16831,8 +16843,6 @@ snapshots:
ansi-styles: 5.2.0
react-is: 18.3.1
prismjs@1.27.0: {}
prismjs@1.30.0: {}
process-nextick-args@2.0.1: {}
@ -17247,7 +17257,7 @@ snapshots:
dependencies:
hastscript: 6.0.0
parse-entities: 2.0.0
prismjs: 1.27.0
prismjs: 1.30.0
regenerate-unicode-properties@10.2.0:
dependencies:
@ -17441,6 +17451,11 @@ snapshots:
dependencies:
glob: 7.2.3
ripemd160@2.0.1:
dependencies:
hash-base: 2.0.2
inherits: 2.0.4
ripemd160@2.0.2:
dependencies:
hash-base: 3.0.5
@ -18041,6 +18056,12 @@ snapshots:
tmpl@1.0.5: {}
to-buffer@1.2.1:
dependencies:
isarray: 2.0.5
safe-buffer: 5.2.1
typed-array-buffer: 1.0.3
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
@ -18389,7 +18410,7 @@ snapshots:
debug: 4.4.0
es-module-lexer: 1.6.0
pathe: 2.0.3
vite: 6.2.6(@types/node@18.15.0)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)
vite: 6.2.7(@types/node@18.15.0)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)
transitivePeerDependencies:
- '@types/node'
- jiti
@ -18404,7 +18425,7 @@ snapshots:
- tsx
- yaml
vite@6.2.6(@types/node@18.15.0)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1):
vite@6.2.7(@types/node@18.15.0)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1):
dependencies:
esbuild: 0.25.2
postcss: 8.5.3
@ -18420,7 +18441,7 @@ snapshots:
vitest@3.1.1(@types/debug@4.1.12)(@types/node@18.15.0)(happy-dom@17.4.4)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1):
dependencies:
'@vitest/expect': 3.1.1
'@vitest/mocker': 3.1.1(vite@6.2.6(@types/node@18.15.0)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))
'@vitest/mocker': 3.1.1(vite@6.2.7(@types/node@18.15.0)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1))
'@vitest/pretty-format': 3.1.1
'@vitest/runner': 3.1.1
'@vitest/snapshot': 3.1.1
@ -18436,7 +18457,7 @@ snapshots:
tinyexec: 0.3.2
tinypool: 1.0.2
tinyrainbow: 2.0.0
vite: 6.2.6(@types/node@18.15.0)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)
vite: 6.2.7(@types/node@18.15.0)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)
vite-node: 3.1.1(@types/node@18.15.0)(jiti@1.21.7)(sass@1.86.3)(terser@5.39.0)(yaml@2.7.1)
why-is-node-running: 2.3.0
optionalDependencies:

View File

@ -86,5 +86,8 @@ export const useGetUserCanAccessApp = ({ appId, isInstalledApp = true, enabled }
enabled: !!appId && enabled,
staleTime: 0,
gcTime: 0,
initialData: {
result: !enabled,
},
})
}

View File

@ -110,7 +110,7 @@ function unicodeToChar(text: string) {
function requiredWebSSOLogin(message?: string, code?: number) {
const params = new URLSearchParams()
params.append('redirect_url', globalThis.location.pathname)
params.append('redirect_url', encodeURIComponent(`${globalThis.location.pathname}${globalThis.location.search}`))
if (message)
params.append('message', message)
if (code)

View File

@ -139,7 +139,7 @@ export const useResetConversationVar = (appId: string) => {
export const useResetToLastRunValue = (appId: string) => {
return useMutation({
mutationKey: [NAME_SPACE, 'reset to last run value', appId],
mutationFn: async (varId: string) => {
mutationFn: async (varId: string): Promise<{ value: any }> => {
return put(`apps/${appId}/workflows/draft/variables/${varId}/reset`)
},
})

View File

@ -0,0 +1,88 @@
import type { FormValue, ModelParameterRule } from '@/app/components/header/account-setting/model-provider-page/declarations'
export const mergeValidCompletionParams = (
oldParams: FormValue | undefined,
rules: ModelParameterRule[],
): { params: FormValue; removedDetails: Record<string, string> } => {
if (!oldParams || Object.keys(oldParams).length === 0)
return { params: {}, removedDetails: {} }
const acceptedKeys = new Set(rules.map(r => r.name))
const ruleMap: Record<string, ModelParameterRule> = {}
rules.forEach((r) => {
ruleMap[r.name] = r
})
const nextParams: FormValue = {}
const removedDetails: Record<string, string> = {}
Object.entries(oldParams).forEach(([key, value]) => {
if (!acceptedKeys.has(key)) {
removedDetails[key] = 'unsupported'
return
}
const rule = ruleMap[key]
if (!rule) {
removedDetails[key] = 'unsupported'
return
}
switch (rule.type) {
case 'int':
case 'float': {
if (typeof value !== 'number') {
removedDetails[key] = 'invalid type'
return
}
const min = rule.min ?? Number.NEGATIVE_INFINITY
const max = rule.max ?? Number.POSITIVE_INFINITY
if (value < min || value > max) {
removedDetails[key] = `out of range (${min}-${max})`
return
}
nextParams[key] = value
return
}
case 'boolean': {
if (typeof value !== 'boolean') {
removedDetails[key] = 'invalid type'
return
}
nextParams[key] = value
return
}
case 'string':
case 'text': {
if (typeof value !== 'string') {
removedDetails[key] = 'invalid type'
return
}
if (Array.isArray(rule.options) && rule.options.length) {
if (!(rule.options as string[]).includes(value)) {
removedDetails[key] = 'unsupported option'
return
}
}
nextParams[key] = value
return
}
default: {
removedDetails[key] = `unsupported rule type: ${(rule as any)?.type ?? 'unknown'}`
}
}
})
return { params: nextParams, removedDetails }
}
export const fetchAndMergeValidCompletionParams = async (
provider: string,
modelId: string,
oldParams: FormValue | undefined,
): Promise<{ params: FormValue; removedDetails: Record<string, string> }> => {
const { fetchModelParameterRules } = await import('@/service/common')
const url = `/workspaces/current/model-providers/${provider}/models/parameter-rules?model=${modelId}`
const { data: parameterRules } = await fetchModelParameterRules(url)
return mergeValidCompletionParams(oldParams, parameterRules ?? [])
}