feat: can show add files

This commit is contained in:
Joel 2026-02-11 11:44:44 +08:00
parent c3163840ec
commit c2fb3604de
25 changed files with 436 additions and 14 deletions

View File

@ -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)}
/>
</>
)
}

View File

@ -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>
)
}

View File

@ -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)

View File

@ -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": "استراتيجية الوكيل",

View File

@ -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",

View File

@ -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.",

View File

@ -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",

View File

@ -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": "استراتژی نمایندگی",

View File

@ -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 dagent",

View File

@ -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": "एजेंट रणनीति",

View File

@ -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",

View File

@ -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",

View File

@ -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": "内部構造",

View File

@ -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": "에이전트 전략",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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": "Агентская стратегия",

View File

@ -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",

View File

@ -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": "กลยุทธ์ตัวแทน",

View File

@ -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",

View File

@ -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": "Стратегія агента",

View File

@ -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ý",

View File

@ -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": "代理在测试运行期间生成的文件。这些文件可能会在稍后自动清除。",

View File

@ -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": "內部結構",