mirror of
https://github.com/langgenius/dify.git
synced 2026-05-10 05:56:31 +08:00
feat: can show add files
This commit is contained in:
parent
c3163840ec
commit
c2fb3604de
@ -46,6 +46,7 @@ import { SegmentedControl } from '@/app/components/base/segmented-control'
|
||||
import AgentNodeList from '@/app/components/workflow/nodes/_base/components/agent-node-list'
|
||||
import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars'
|
||||
import { FilePickerPanel } from '@/app/components/workflow/skill/editor/skill-editor/plugins/file-picker-panel'
|
||||
import FilePickerUploadModal from '@/app/components/workflow/skill/editor/skill-editor/plugins/file-picker-upload-modal'
|
||||
import { $createFileReferenceNode } from '@/app/components/workflow/skill/editor/skill-editor/plugins/file-reference-block/node'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
@ -138,6 +139,8 @@ const ComponentPicker = ({
|
||||
|
||||
const [queryString, setQueryString] = useState<string | null>(null)
|
||||
const [activeTab, setActiveTab] = useState<'variables' | 'files'>('variables')
|
||||
const [isFileUploadModalOpen, setIsFileUploadModalOpen] = useState(false)
|
||||
const [fileUploadModalKey, setFileUploadModalKey] = useState(0)
|
||||
|
||||
eventEmitter?.useSubscription((v: any) => {
|
||||
if (v.type === INSERT_VARIABLE_VALUE_BLOCK_COMMAND)
|
||||
@ -298,6 +301,10 @@ const ComponentPicker = ({
|
||||
visibility: isPositioned ? 'visible' : 'hidden',
|
||||
}}
|
||||
ref={refs.setFloating}
|
||||
onMouseDown={(event) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="w-full"
|
||||
@ -358,6 +365,12 @@ const ComponentPicker = ({
|
||||
className="w-full border-0 bg-transparent p-0 shadow-none"
|
||||
contentClassName="px-0"
|
||||
showHeader={false}
|
||||
showAddFiles
|
||||
onAddFiles={() => {
|
||||
setFileUploadModalKey(key => key + 1)
|
||||
setIsFileUploadModalOpen(true)
|
||||
handleClose()
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -473,20 +486,27 @@ const ComponentPicker = ({
|
||||
}, [isAgentTrigger, isSupportSandbox, triggerString, allFlattenOptions.length, workflowVariableBlock?.show, workflowVariableBlock?.showManageInputField, workflowVariableBlock?.onManageInputField, floatingStyles, isPositioned, refs, agentNodes, handleSelectAgent, handleClose, useExternalSearch, queryString, workflowVariableOptions, isSupportFileVar, showAssembleVariables, handleSelectAssembleVariables, currentBlock?.generatorType, t, activeTab, handleSelectWorkflowVariable, handleSelectFileReference])
|
||||
|
||||
return (
|
||||
<LexicalTypeaheadMenuPlugin
|
||||
options={(isSupportSandbox && triggerString === '/') ? [] : allFlattenOptions}
|
||||
onQueryChange={setQueryString}
|
||||
onSelectOption={onSelectOption}
|
||||
onOpen={handleOpen}
|
||||
// The `translate` class is used to workaround the issue that the `typeahead-menu` menu is not positioned as expected.
|
||||
// See also https://github.com/facebook/lexical/blob/772520509308e8ba7e4a82b6cd1996a78b3298d0/packages/lexical-react/src/shared/LexicalMenu.ts#L498
|
||||
//
|
||||
// We no need the position function of the `LexicalTypeaheadMenuPlugin`,
|
||||
// so the reference anchor should be positioned based on the range of the trigger string, and the menu will be positioned by the floating ui.
|
||||
anchorClassName="z-[999999] translate-y-[calc(-100%-3px)]"
|
||||
menuRenderFn={renderMenu}
|
||||
triggerFn={checkForTriggerMatch}
|
||||
/>
|
||||
<>
|
||||
<LexicalTypeaheadMenuPlugin
|
||||
options={(isSupportSandbox && triggerString === '/') ? [] : allFlattenOptions}
|
||||
onQueryChange={setQueryString}
|
||||
onSelectOption={onSelectOption}
|
||||
onOpen={handleOpen}
|
||||
// The `translate` class is used to workaround the issue that the `typeahead-menu` menu is not positioned as expected.
|
||||
// See also https://github.com/facebook/lexical/blob/772520509308e8ba7e4a82b6cd1996a78b3298d0/packages/lexical-react/src/shared/LexicalMenu.ts#L498
|
||||
//
|
||||
// We no need the position function of the `LexicalTypeaheadMenuPlugin`,
|
||||
// so the reference anchor should be positioned based on the range of the trigger string, and the menu will be positioned by the floating ui.
|
||||
anchorClassName="z-[999999] translate-y-[calc(-100%-3px)]"
|
||||
menuRenderFn={renderMenu}
|
||||
triggerFn={checkForTriggerMatch}
|
||||
/>
|
||||
<FilePickerUploadModal
|
||||
key={fileUploadModalKey}
|
||||
isOpen={isFileUploadModalOpen}
|
||||
onClose={() => setIsFileUploadModalOpen(false)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -118,6 +118,8 @@ type FilePickerPanelProps = {
|
||||
className?: string
|
||||
contentClassName?: string
|
||||
showHeader?: boolean
|
||||
showAddFiles?: boolean
|
||||
onAddFiles?: () => void
|
||||
}
|
||||
|
||||
const FilePickerPanel = ({
|
||||
@ -126,6 +128,8 @@ const FilePickerPanel = ({
|
||||
className,
|
||||
contentClassName,
|
||||
showHeader = true,
|
||||
showAddFiles = false,
|
||||
onAddFiles,
|
||||
}: FilePickerPanelProps) => {
|
||||
const { t } = useTranslation('workflow')
|
||||
const { data: treeData, isLoading, error } = useSkillAssetTreeData()
|
||||
@ -133,6 +137,7 @@ const FilePickerPanel = ({
|
||||
const containerSize = useSize(containerRef)
|
||||
|
||||
const treeNodes = useMemo(() => treeData?.children || [], [treeData?.children])
|
||||
|
||||
const initialOpenState = useMemo(() => {
|
||||
const nextState: Record<string, boolean> = {}
|
||||
if (!focusNodeId || treeNodes.length === 0)
|
||||
@ -162,6 +167,7 @@ const FilePickerPanel = ({
|
||||
if (target.closest('input, textarea, select'))
|
||||
return
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}}
|
||||
>
|
||||
{showHeader && (
|
||||
@ -214,6 +220,23 @@ const FilePickerPanel = ({
|
||||
</Tree>
|
||||
)}
|
||||
</div>
|
||||
{showAddFiles && (
|
||||
<>
|
||||
<div className="h-px bg-divider-subtle" />
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'flex h-9 w-full items-center gap-2 px-3 text-left hover:bg-state-base-hover',
|
||||
!onAddFiles && 'cursor-not-allowed opacity-50',
|
||||
)}
|
||||
onClick={onAddFiles}
|
||||
disabled={!onAddFiles}
|
||||
>
|
||||
<span className="i-ri-file-add-line size-4 text-text-secondary" aria-hidden="true" />
|
||||
<span className="text-[13px] font-medium leading-4 text-text-secondary">{t('skillEditor.addFiles')}</span>
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -0,0 +1,313 @@
|
||||
import type { Item } from '@/app/components/base/select'
|
||||
import type { TreeNodeData } from '@/app/components/workflow/skill/type'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
|
||||
import { ROOT_ID } from '@/app/components/workflow/skill/constants'
|
||||
import { useSkillAssetTreeData } from '@/app/components/workflow/skill/hooks/file-tree/data/use-skill-asset-tree'
|
||||
import { useSkillTreeUpdateEmitter } from '@/app/components/workflow/skill/hooks/file-tree/data/use-skill-tree-collaboration'
|
||||
import { useCreateOperations } from '@/app/components/workflow/skill/hooks/file-tree/operations/use-create-operations'
|
||||
import { toApiParentId } from '@/app/components/workflow/skill/utils/tree-utils'
|
||||
import { useWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { useUploadFileWithPresignedUrl } from '@/service/use-app-asset'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type FilePickerUploadModalProps = {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
defaultFolderId?: string
|
||||
}
|
||||
|
||||
type AddFileMode = 'create' | 'upload'
|
||||
type FolderOption = Item & {
|
||||
pathLabel: string
|
||||
depth: number
|
||||
hasChildren: boolean
|
||||
}
|
||||
|
||||
const FilePickerUploadModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
defaultFolderId,
|
||||
}: FilePickerUploadModalProps) => {
|
||||
const { t } = useTranslation('workflow')
|
||||
const appDetail = useAppStore(s => s.appDetail)
|
||||
const appId = appDetail?.id || ''
|
||||
const storeApi = useWorkflowStore()
|
||||
const { data: treeData } = useSkillAssetTreeData()
|
||||
const emitTreeUpdate = useSkillTreeUpdateEmitter()
|
||||
const uploadFile = useUploadFileWithPresignedUrl()
|
||||
const [mode, setMode] = useState<AddFileMode>('create')
|
||||
const [uploadFolderId, setUploadFolderId] = useState(defaultFolderId || ROOT_ID)
|
||||
const [fileName, setFileName] = useState('')
|
||||
const [isDragOver, setIsDragOver] = useState(false)
|
||||
|
||||
const treeNodes = useMemo(() => treeData?.children || [], [treeData?.children])
|
||||
const folderOptions = useMemo<FolderOption[]>(() => {
|
||||
const options: FolderOption[] = [{
|
||||
value: ROOT_ID,
|
||||
name: t('skillSidebar.rootFolder'),
|
||||
pathLabel: t('skillSidebar.rootFolder'),
|
||||
depth: 0,
|
||||
hasChildren: true,
|
||||
}]
|
||||
|
||||
const travelFolders = (nodes: TreeNodeData[]) => {
|
||||
nodes.forEach((node) => {
|
||||
if (node.node_type !== 'folder')
|
||||
return
|
||||
|
||||
const folderPath = node.path.replace(/^\//, '') || node.name
|
||||
const depth = Math.max(0, folderPath.split('/').length - 1)
|
||||
options.push({
|
||||
value: node.id,
|
||||
name: node.name,
|
||||
pathLabel: folderPath,
|
||||
depth,
|
||||
hasChildren: node.children.some(child => child.node_type === 'folder'),
|
||||
})
|
||||
if (node.children.length > 0)
|
||||
travelFolders(node.children)
|
||||
})
|
||||
}
|
||||
travelFolders(treeNodes)
|
||||
return options
|
||||
}, [t, treeNodes])
|
||||
|
||||
const effectiveUploadFolderId = useMemo(() => {
|
||||
return folderOptions.some(item => item.value === uploadFolderId)
|
||||
? uploadFolderId
|
||||
: ROOT_ID
|
||||
}, [folderOptions, uploadFolderId])
|
||||
const selectedFolderOption = useMemo(() => {
|
||||
return folderOptions.find(item => item.value === effectiveUploadFolderId) || folderOptions[0]
|
||||
}, [effectiveUploadFolderId, folderOptions])
|
||||
|
||||
const {
|
||||
fileInputRef,
|
||||
isCreating,
|
||||
handleFileChange: handleRawFileChange,
|
||||
} = useCreateOperations({
|
||||
parentId: toApiParentId(effectiveUploadFolderId),
|
||||
appId,
|
||||
storeApi,
|
||||
onClose: noop,
|
||||
})
|
||||
const isCreatingFile = uploadFile.isPending
|
||||
const isBusy = isCreating || isCreatingFile
|
||||
const trimmedFileName = fileName.trim()
|
||||
const canCreate = !!appId && !!trimmedFileName && !isBusy
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
if (isBusy)
|
||||
return
|
||||
onClose()
|
||||
}, [isBusy, onClose])
|
||||
|
||||
const handleUploadFilesChange = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const hasSelectedFiles = (e.target.files?.length ?? 0) > 0
|
||||
await handleRawFileChange(e)
|
||||
if (hasSelectedFiles)
|
||||
onClose()
|
||||
}, [handleRawFileChange, onClose])
|
||||
const handleDrop = useCallback(async (e: React.DragEvent<HTMLDivElement>) => {
|
||||
e.preventDefault()
|
||||
if (isBusy || !appId)
|
||||
return
|
||||
setIsDragOver(false)
|
||||
const droppedFiles = Array.from(e.dataTransfer.files || [])
|
||||
if (!droppedFiles.length)
|
||||
return
|
||||
const transfer = new DataTransfer()
|
||||
droppedFiles.forEach(file => transfer.items.add(file))
|
||||
await handleUploadFilesChange({
|
||||
target: {
|
||||
files: transfer.files,
|
||||
value: '',
|
||||
},
|
||||
} as React.ChangeEvent<HTMLInputElement>)
|
||||
}, [appId, handleUploadFilesChange, isBusy])
|
||||
const handleCreateFile = useCallback(async () => {
|
||||
if (!canCreate)
|
||||
return
|
||||
|
||||
try {
|
||||
const emptyBlob = new Blob([''], { type: 'text/plain' })
|
||||
const file = new File([emptyBlob], trimmedFileName)
|
||||
await uploadFile.mutateAsync({
|
||||
appId,
|
||||
file,
|
||||
parentId: toApiParentId(effectiveUploadFolderId),
|
||||
})
|
||||
emitTreeUpdate()
|
||||
onClose()
|
||||
}
|
||||
catch {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: t('skillSidebar.menu.createError'),
|
||||
})
|
||||
}
|
||||
}, [appId, canCreate, effectiveUploadFolderId, emitTreeUpdate, onClose, t, trimmedFileName, uploadFile])
|
||||
const modeLabel = t('skillEditor.uploadIn')
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isShow={isOpen}
|
||||
onClose={handleClose}
|
||||
title={t('skillEditor.addFiles')}
|
||||
className="max-w-[360px]"
|
||||
closable={!isBusy}
|
||||
clickOutsideNotClose={isBusy}
|
||||
>
|
||||
<div className="-mx-6 mt-4 h-px bg-divider-subtle" />
|
||||
<div className="mt-4 space-y-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<OptionCard
|
||||
className="flex-1"
|
||||
title={`${t('operation.create', { ns: 'common' })} ${t('skillSidebar.menu.newFile')}`}
|
||||
onSelect={() => setMode('create')}
|
||||
selected={mode === 'create'}
|
||||
disabled={isBusy}
|
||||
/>
|
||||
<OptionCard
|
||||
className="flex-1"
|
||||
title={t('skillEditor.uploadFiles')}
|
||||
onSelect={() => setMode('upload')}
|
||||
selected={mode === 'upload'}
|
||||
disabled={isBusy}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div className="text-text-secondary system-sm-medium">{modeLabel}</div>
|
||||
<SimpleSelect
|
||||
items={folderOptions as Item[]}
|
||||
defaultValue={effectiveUploadFolderId}
|
||||
notClearable
|
||||
className="h-8 rounded-lg bg-components-input-bg-normal pl-3 pr-10 hover:bg-state-base-hover-alt"
|
||||
optionWrapClassName="max-h-[260px] rounded-xl bg-components-panel-bg-blur"
|
||||
optionClassName="pr-3"
|
||||
renderTrigger={(selectedItem, open) => {
|
||||
const currentOption = selectedItem as FolderOption | null
|
||||
const label = currentOption?.pathLabel || selectedFolderOption?.pathLabel || t('skillSidebar.rootFolder')
|
||||
return (
|
||||
<div className="relative flex h-8 items-center rounded-lg bg-components-input-bg-normal pl-3 pr-10 hover:bg-state-base-hover-alt">
|
||||
<span className="i-ri-folder-line mr-2 size-4 shrink-0 text-text-secondary" aria-hidden="true" />
|
||||
<span className="min-w-0 truncate text-left text-components-input-text-filled system-sm-regular">{label}</span>
|
||||
<span className={cn(
|
||||
'i-ri-arrow-down-s-line absolute right-3 top-1/2 size-4 -translate-y-1/2 text-text-quaternary transition-transform',
|
||||
open && 'rotate-180',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
renderOption={({ item }) => {
|
||||
const option = item as FolderOption
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span style={{ width: `${option.depth * 16}px` }} aria-hidden="true" />
|
||||
<span className="i-ri-folder-line size-4 shrink-0 text-text-secondary" aria-hidden="true" />
|
||||
<span className="min-w-0 flex-1 truncate">{option.name}</span>
|
||||
{option.hasChildren && <span className="i-ri-arrow-right-s-line size-4 shrink-0 text-text-tertiary" aria-hidden="true" />}
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
onSelect={item => setUploadFolderId(String(item.value))}
|
||||
/>
|
||||
</div>
|
||||
{mode === 'create' && (
|
||||
<div className="space-y-1">
|
||||
<div className="text-text-secondary system-sm-medium">{t('skillSidebar.fileNamePlaceholder')}</div>
|
||||
<Input
|
||||
value={fileName}
|
||||
onChange={e => setFileName(e.target.value)}
|
||||
placeholder={t('skillSidebar.fileNamePlaceholder') || ''}
|
||||
disabled={isBusy}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter')
|
||||
void handleCreateFile()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
multiple
|
||||
className="hidden"
|
||||
onChange={handleUploadFilesChange}
|
||||
/>
|
||||
{mode === 'upload' && (
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className={cn(
|
||||
'flex h-12 cursor-pointer items-center justify-center gap-1 rounded-xl border border-dashed text-text-secondary',
|
||||
isDragOver ? 'border-components-button-primary-border bg-state-accent-hover' : 'border-divider-subtle bg-components-panel-bg',
|
||||
isBusy && 'cursor-not-allowed opacity-60',
|
||||
)}
|
||||
onClick={() => {
|
||||
if (!isBusy && appId)
|
||||
fileInputRef.current?.click()
|
||||
}}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault()
|
||||
if (!isBusy)
|
||||
setIsDragOver(true)
|
||||
}}
|
||||
onDragLeave={(e) => {
|
||||
e.preventDefault()
|
||||
setIsDragOver(false)
|
||||
}}
|
||||
onDrop={handleDrop}
|
||||
onKeyDown={(e) => {
|
||||
if ((e.key === 'Enter' || e.key === ' ') && !isBusy && appId)
|
||||
fileInputRef.current?.click()
|
||||
}}
|
||||
>
|
||||
<span className="i-ri-upload-cloud-line size-4 text-text-tertiary" aria-hidden="true" />
|
||||
<span className="system-sm-regular">{t('skillSidebar.dropTip')}</span>
|
||||
<span className="text-text-accent system-sm-medium">{t('skill.startTab.importModal.browseFiles')}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button onClick={handleClose} disabled={isBusy}>
|
||||
{t('operation.cancel', { ns: 'common' })}
|
||||
</Button>
|
||||
{mode === 'create'
|
||||
? (
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={handleCreateFile}
|
||||
disabled={!canCreate}
|
||||
loading={isCreatingFile}
|
||||
>
|
||||
{t('operation.create', { ns: 'common' })}
|
||||
</Button>
|
||||
)
|
||||
: (
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
disabled={!appId || isBusy}
|
||||
loading={isCreating}
|
||||
>
|
||||
{t('skillEditor.uploadFiles')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(FilePickerUploadModal)
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "تشغيل اختياري",
|
||||
"singleRun.testRunIteration": "تكرار تشغيل الاختبار",
|
||||
"singleRun.testRunLoop": "حلقة اختبار التشغيل",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "افتراضي",
|
||||
"tabs.addAll": "إضافة الكل",
|
||||
"tabs.agent": "استراتيجية الوكيل",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "Testlauf ",
|
||||
"singleRun.testRunIteration": "Testlaufiteration",
|
||||
"singleRun.testRunLoop": "Testdurchlauf-Schleife",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "Standard",
|
||||
"tabs.addAll": "Alles hinzufügen",
|
||||
"tabs.agent": "Agenten-Strategie",
|
||||
|
||||
@ -1219,6 +1219,7 @@
|
||||
"skill.startTab.templatesDesc": "Choose a template to bootstrap your agent's capabilities",
|
||||
"skill.startTab.templatesTitle": "Skill Templates",
|
||||
"skill.startTab.useThisSkill": "Use this Skill",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.authorizationBadge": "Auth",
|
||||
"skillEditor.authorizationRequired": "Authorization required before use.",
|
||||
"skillEditor.fileNotFound": "The file does not exist",
|
||||
@ -1229,6 +1230,8 @@
|
||||
"skillEditor.toolMissing": "Tool missing",
|
||||
"skillEditor.toolMissingDesc": "Go to <Plugins>Plugins</Plugins> to install",
|
||||
"skillEditor.unsupportedPreview": "This file type is not supported for preview",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"skillSidebar.addFile": "Upload File",
|
||||
"skillSidebar.addFolder": "New Folder",
|
||||
"skillSidebar.artifacts.emptyState": "Files generated by the agent during test runs. They may be automatically cleared later.",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "Ejecución de prueba",
|
||||
"singleRun.testRunIteration": "Iteración de ejecución de prueba",
|
||||
"singleRun.testRunLoop": "Bucle de prueba",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "predeterminado",
|
||||
"tabs.addAll": "Agregar todo",
|
||||
"tabs.agent": "Estrategia del agente",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "اجرای آزمایشی",
|
||||
"singleRun.testRunIteration": "تکرار اجرای آزمایشی",
|
||||
"singleRun.testRunLoop": "اجرای آزمایشی حلقه",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "پیشفرض",
|
||||
"tabs.addAll": "همه را اضافه کنید",
|
||||
"tabs.agent": "استراتژی نمایندگی",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "Exécution de test",
|
||||
"singleRun.testRunIteration": "Itération de l'exécution de test",
|
||||
"singleRun.testRunLoop": "Boucle d'exécution de test",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "Par défaut",
|
||||
"tabs.addAll": "Ajouter tout",
|
||||
"tabs.agent": "Stratégie d’agent",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "परीक्षण रन",
|
||||
"singleRun.testRunIteration": "परीक्षण रन पुनरावृत्ति",
|
||||
"singleRun.testRunLoop": "परीक्षण रन लूप",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "डिफ़ॉल्ट",
|
||||
"tabs.addAll": "सभी जोड़ें",
|
||||
"tabs.agent": "एजेंट रणनीति",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "Uji Coba",
|
||||
"singleRun.testRunIteration": "Iterasi Uji Coba",
|
||||
"singleRun.testRunLoop": "Uji Jalankan Loop",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "Default",
|
||||
"tabs.addAll": "Tambahkan semua",
|
||||
"tabs.agent": "Strategi Agen",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "Esecuzione Test ",
|
||||
"singleRun.testRunIteration": "Iterazione Esecuzione Test",
|
||||
"singleRun.testRunLoop": "Esegui ciclo di prova",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "Predefinito",
|
||||
"tabs.addAll": "Aggiungi tutto",
|
||||
"tabs.agent": "Strategia dell'agente",
|
||||
|
||||
@ -1142,6 +1142,9 @@
|
||||
"singleRun.testRun": "テスト実行",
|
||||
"singleRun.testRunIteration": "テスト実行(イテレーション)",
|
||||
"singleRun.testRunLoop": "テスト実行ループ",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"subGraphModal.canvasPlaceholder": "クリックして内部構造を設定",
|
||||
"subGraphModal.defaultValueHint": "以下の値を返す",
|
||||
"subGraphModal.internalStructure": "内部構造",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "테스트 실행",
|
||||
"singleRun.testRunIteration": "테스트 실행 반복",
|
||||
"singleRun.testRunLoop": "테스트 실행 루프",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "기본",
|
||||
"tabs.addAll": "모두 추가",
|
||||
"tabs.agent": "에이전트 전략",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "Testowe uruchomienie ",
|
||||
"singleRun.testRunIteration": "Iteracja testowego uruchomienia",
|
||||
"singleRun.testRunLoop": "Pętla testowa",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "Domyślny",
|
||||
"tabs.addAll": "Dodaj wszystko",
|
||||
"tabs.agent": "Strategia agenta",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "Execução de teste ",
|
||||
"singleRun.testRunIteration": "Iteração de execução de teste",
|
||||
"singleRun.testRunLoop": "Loop de Teste",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "Padrão",
|
||||
"tabs.addAll": "Adicionar tudo",
|
||||
"tabs.agent": "Estratégia do agente",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "Rulare de test ",
|
||||
"singleRun.testRunIteration": "Iterație rulare de test",
|
||||
"singleRun.testRunLoop": "Bucle de testare",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "Implicit",
|
||||
"tabs.addAll": "Adaugă tot",
|
||||
"tabs.agent": "Strategia agentului",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "Тестовый запуск ",
|
||||
"singleRun.testRunIteration": "Итерация тестового запуска",
|
||||
"singleRun.testRunLoop": "Тестовый цикл запуска",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "По умолчанию",
|
||||
"tabs.addAll": "Добавить всё",
|
||||
"tabs.agent": "Агентская стратегия",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "Testna vožnja",
|
||||
"singleRun.testRunIteration": "Testiranje ponovitve",
|
||||
"singleRun.testRunLoop": "Testni zagon zanke",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "Privzeto",
|
||||
"tabs.addAll": "Dodaj vse",
|
||||
"tabs.agent": "Agentska strategija",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "ทดสอบการทํางาน",
|
||||
"singleRun.testRunIteration": "การทดสอบการทําซ้ํา",
|
||||
"singleRun.testRunLoop": "รันลูปทดสอบ",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "ค่าเริ่มต้น",
|
||||
"tabs.addAll": "เพิ่มทั้งหมด",
|
||||
"tabs.agent": "กลยุทธ์ตัวแทน",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "Test Çalıştırma",
|
||||
"singleRun.testRunIteration": "Test Çalıştırma Yineleme",
|
||||
"singleRun.testRunLoop": "Test Çalıştırma Döngüsü",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "Varsayılan",
|
||||
"tabs.addAll": "Hepsini ekle",
|
||||
"tabs.agent": "Temsilci Stratejisi",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "Тестовий запуск",
|
||||
"singleRun.testRunIteration": "Ітерація тестового запуску",
|
||||
"singleRun.testRunLoop": "Тестовий цикл виконання",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "За замовчуванням",
|
||||
"tabs.addAll": "Додати все",
|
||||
"tabs.agent": "Стратегія агента",
|
||||
|
||||
@ -1105,6 +1105,9 @@
|
||||
"singleRun.testRun": "Chạy thử nghiệm ",
|
||||
"singleRun.testRunIteration": "Lặp chạy thử nghiệm",
|
||||
"singleRun.testRunLoop": "Chạy thử vòng lặp",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"tabs.-": "Mặc định",
|
||||
"tabs.addAll": "Thêm tất cả",
|
||||
"tabs.agent": "Chiến lược đại lý",
|
||||
|
||||
@ -1205,6 +1205,7 @@
|
||||
"skill.startTab.templatesDesc": "选择模板来快速构建你的 Agent 能力",
|
||||
"skill.startTab.templatesTitle": "Skill 模板",
|
||||
"skill.startTab.useThisSkill": "使用此 Skill",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.authorizationBadge": "Auth",
|
||||
"skillEditor.authorizationRequired": "使用前需要授权。",
|
||||
"skillEditor.fileNotFound": "文件不存在",
|
||||
@ -1215,6 +1216,8 @@
|
||||
"skillEditor.toolMissing": "工具缺失",
|
||||
"skillEditor.toolMissingDesc": "前往 <Plugins>插件</Plugins> 安装",
|
||||
"skillEditor.unsupportedPreview": "该文件类型不支持预览",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"skillSidebar.addFile": "上传文件",
|
||||
"skillSidebar.addFolder": "新建文件夹",
|
||||
"skillSidebar.artifacts.emptyState": "代理在测试运行期间生成的文件。这些文件可能会在稍后自动清除。",
|
||||
|
||||
@ -1152,9 +1152,12 @@
|
||||
"singleRun.testRun": "測試運行",
|
||||
"singleRun.testRunIteration": "測試運行迭代",
|
||||
"singleRun.testRunLoop": "測試運行循環",
|
||||
"skillEditor.addFiles": "Add files",
|
||||
"skillEditor.referenceFiles": "參考檔案",
|
||||
"skillEditor.toolMissing": "工具遺失",
|
||||
"skillEditor.toolMissingDesc": "前往 <Plugins>外掛</Plugins> 安裝",
|
||||
"skillEditor.uploadFiles": "Upload files",
|
||||
"skillEditor.uploadIn": "Upload in",
|
||||
"subGraphModal.canvasPlaceholder": "點擊配置內部結構",
|
||||
"subGraphModal.defaultValueHint": "返回以下值",
|
||||
"subGraphModal.internalStructure": "內部結構",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user