mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-02-19 00:54:46 +08:00
Merge remote-tracking branch 'origin/main' into feat/user-guide
This commit is contained in:
commit
77e50f889a
3
.github/workflows/pr-ci.yml
vendored
3
.github/workflows/pr-ci.yml
vendored
@ -59,5 +59,8 @@ jobs:
|
||||
- name: i18n Check
|
||||
run: pnpm i18n:check
|
||||
|
||||
- name: Hardcoded Strings Check
|
||||
run: pnpm i18n:hardcoded:strict
|
||||
|
||||
- name: Test
|
||||
run: pnpm test
|
||||
|
||||
@ -1 +0,0 @@
|
||||
pnpm lint-staged
|
||||
23
.pre-commit-config.yaml
Normal file
23
.pre-commit-config.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
repos:
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: biome-format-js
|
||||
name: Biome format (JS/TS)
|
||||
language: system
|
||||
entry: pnpm biome format --write --no-errors-on-unmatched
|
||||
files: '\.(js|jsx|ts|tsx|cjs|mjs|cts|mts)$'
|
||||
pass_filenames: true
|
||||
|
||||
- id: eslint-fix
|
||||
name: ESLint fix
|
||||
language: system
|
||||
entry: pnpm eslint --fix
|
||||
files: '\.(js|jsx|ts|tsx|cjs|mjs|cts|mts)$'
|
||||
pass_filenames: true
|
||||
|
||||
- id: biome-format-other
|
||||
name: Biome format (JSON/YAML/CSS/HTML)
|
||||
language: system
|
||||
entry: pnpm biome format --write --no-errors-on-unmatched
|
||||
files: '\.(json|yml|yaml|css|html)$'
|
||||
pass_filenames: true
|
||||
@ -116,14 +116,17 @@ dmg:
|
||||
writeUpdateInfo: false
|
||||
linux:
|
||||
artifactName: ${productName}-${version}-${arch}.${ext}
|
||||
executableName: CherryStudio
|
||||
target:
|
||||
- target: AppImage
|
||||
- target: deb
|
||||
- target: rpm
|
||||
- target: pacman
|
||||
maintainer: electronjs.org
|
||||
category: Utility
|
||||
desktop:
|
||||
entry:
|
||||
Name: Cherry Studio
|
||||
StartupWMClass: CherryStudio
|
||||
mimeTypes:
|
||||
- x-scheme-handler/cherrystudio
|
||||
|
||||
25
package.json
25
package.json
@ -3,6 +3,7 @@
|
||||
"version": "1.7.13",
|
||||
"private": true,
|
||||
"description": "A powerful AI assistant for producer.",
|
||||
"desktopName": "CherryStudio.desktop",
|
||||
"main": "./out/main/index.js",
|
||||
"author": "support@cherry-ai.com",
|
||||
"homepage": "https://github.com/CherryHQ/cherry-studio",
|
||||
@ -39,6 +40,8 @@
|
||||
"typecheck:node": "tsgo --noEmit -p tsconfig.node.json --composite false",
|
||||
"typecheck:web": "tsgo --noEmit -p tsconfig.web.json --composite false",
|
||||
"i18n:check": "dotenv -e .env -- tsx scripts/check-i18n.ts",
|
||||
"i18n:hardcoded": "tsx scripts/check-hardcoded-strings.ts",
|
||||
"i18n:hardcoded:strict": "I18N_STRICT=true tsx scripts/check-hardcoded-strings.ts",
|
||||
"i18n:sync": "dotenv -e .env -- tsx scripts/sync-i18n.ts",
|
||||
"i18n:translate": "dotenv -e .env -- tsx scripts/auto-translate-i18n.ts",
|
||||
"i18n:all": "pnpm i18n:sync && pnpm i18n:translate",
|
||||
@ -58,7 +61,7 @@
|
||||
"lint": "oxlint --fix && eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --cache && pnpm typecheck && pnpm i18n:check && pnpm format:check",
|
||||
"format": "biome format --write && biome lint --write",
|
||||
"format:check": "biome format && biome lint",
|
||||
"prepare": "git config blame.ignoreRevsFile .git-blame-ignore-revs && husky",
|
||||
"prepare": "git config blame.ignoreRevsFile .git-blame-ignore-revs && prek install",
|
||||
"claude": "dotenv -e .env -- claude",
|
||||
"release:aicore:alpha": "pnpm --filter @cherrystudio/ai-core version prerelease --preid alpha && pnpm --filter @cherrystudio/ai-core build && pnpm --filter @cherrystudio/ai-core publish --tag alpha --access public",
|
||||
"release:aicore:beta": "pnpm --filter @cherrystudio/ai-core version prerelease --preid beta && pnpm --filter @cherrystudio/ai-core build && pnpm --filter @cherrystudio/ai-core publish --tag beta --access public",
|
||||
@ -148,6 +151,7 @@
|
||||
"@floating-ui/dom": "1.7.3",
|
||||
"@google/genai": "1.0.1",
|
||||
"@hello-pangea/dnd": "^18.0.1",
|
||||
"@j178/prek": "^0.2.28",
|
||||
"@kangfenmao/keyv-storage": "^0.1.3",
|
||||
"@langchain/community": "^1.0.0",
|
||||
"@langchain/core": "1.0.2",
|
||||
@ -312,7 +316,6 @@
|
||||
"html-to-image": "^1.11.13",
|
||||
"html-to-text": "^9.0.5",
|
||||
"htmlparser2": "^10.0.0",
|
||||
"husky": "^9.1.7",
|
||||
"i18next": "^23.11.5",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"ipaddr.js": "^2.2.0",
|
||||
@ -325,7 +328,6 @@
|
||||
"katex": "0.16.22",
|
||||
"ky": "1.8.1",
|
||||
"linguist-languages": "^8.1.0",
|
||||
"lint-staged": "^15.5.0",
|
||||
"lodash": "^4.17.21",
|
||||
"lru-cache": "^11.1.0",
|
||||
"lucide-react": "^0.525.0",
|
||||
@ -394,6 +396,7 @@
|
||||
"tar": "^7.4.3",
|
||||
"tiny-pinyin": "^1.3.2",
|
||||
"tokenx": "^1.1.0",
|
||||
"ts-morph": "^27.0.2",
|
||||
"tsx": "^4.20.3",
|
||||
"turndown-plugin-gfm": "^1.0.2",
|
||||
"tw-animate-css": "^1.3.8",
|
||||
@ -460,6 +463,7 @@
|
||||
"@openrouter/ai-sdk-provider": "patches/@openrouter__ai-sdk-provider.patch"
|
||||
},
|
||||
"onlyBuiltDependencies": [
|
||||
"@j178/prek",
|
||||
"@kangfenmao/keyv-storage",
|
||||
"@paymoapp/electron-shutdown-handler",
|
||||
"@scarf/scarf",
|
||||
@ -477,15 +481,6 @@
|
||||
]
|
||||
},
|
||||
"packageManager": "pnpm@10.27.0",
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx,cjs,mjs,cts,mts}": [
|
||||
"biome format --write --no-errors-on-unmatched",
|
||||
"eslint --fix"
|
||||
],
|
||||
"*.{json,yml,yaml,css,html}": [
|
||||
"biome format --write --no-errors-on-unmatched"
|
||||
]
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.34.3",
|
||||
"@img/sharp-darwin-x64": "0.34.3",
|
||||
@ -493,14 +488,20 @@
|
||||
"@img/sharp-libvips-darwin-x64": "1.2.0",
|
||||
"@img/sharp-libvips-linux-arm64": "1.2.0",
|
||||
"@img/sharp-libvips-linux-x64": "1.2.0",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.2.0",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.2.0",
|
||||
"@img/sharp-linux-arm64": "0.34.3",
|
||||
"@img/sharp-linux-x64": "0.34.3",
|
||||
"@img/sharp-linuxmusl-arm64": "0.34.3",
|
||||
"@img/sharp-linuxmusl-x64": "0.34.3",
|
||||
"@img/sharp-win32-arm64": "0.34.3",
|
||||
"@img/sharp-win32-x64": "0.34.3",
|
||||
"@libsql/darwin-arm64": "0.4.7",
|
||||
"@libsql/darwin-x64": "0.4.7",
|
||||
"@libsql/linux-arm64-gnu": "0.4.7",
|
||||
"@libsql/linux-arm64-musl": "0.4.7",
|
||||
"@libsql/linux-x64-gnu": "0.4.7",
|
||||
"@libsql/linux-x64-musl": "0.4.7",
|
||||
"@libsql/win32-x64-msvc": "0.4.7",
|
||||
"@napi-rs/system-ocr-darwin-arm64": "1.0.2",
|
||||
"@napi-rs/system-ocr-darwin-x64": "1.0.2",
|
||||
|
||||
@ -3,6 +3,8 @@
|
||||
* 提供类型安全的 Provider 配置构建器
|
||||
*/
|
||||
|
||||
import { normalizeHeaders } from '@ai-sdk/provider-utils'
|
||||
|
||||
import type { ProviderId, ProviderSettingsMap } from './types'
|
||||
|
||||
/**
|
||||
@ -79,7 +81,7 @@ export class ProviderConfigBuilder<T extends ProviderId = ProviderId> {
|
||||
*/
|
||||
withRequestConfig(options: { headers?: Record<string, string>; fetch?: typeof fetch }): this {
|
||||
if (options.headers) {
|
||||
this.config.headers = { ...this.config.headers, ...options.headers }
|
||||
this.config.headers = normalizeHeaders({ ...this.config.headers, ...options.headers })
|
||||
}
|
||||
if (options.fetch) {
|
||||
this.config.fetch = options.fetch
|
||||
@ -140,6 +142,7 @@ export class ProviderConfigFactory {
|
||||
provider: CompleteProviderConfig<T>,
|
||||
options?: {
|
||||
headers?: Record<string, string>
|
||||
fetch?: typeof globalThis.fetch
|
||||
[key: string]: any
|
||||
}
|
||||
): ProviderSettingsMap[T] {
|
||||
@ -155,9 +158,10 @@ export class ProviderConfigFactory {
|
||||
}
|
||||
|
||||
// 设置请求配置
|
||||
if (options?.headers) {
|
||||
if (options?.headers || options?.fetch) {
|
||||
builder.withRequestConfig({
|
||||
headers: options.headers
|
||||
headers: options.headers,
|
||||
fetch: options.fetch
|
||||
})
|
||||
}
|
||||
|
||||
@ -171,6 +175,7 @@ export class ProviderConfigFactory {
|
||||
if (options) {
|
||||
const customOptions = { ...options }
|
||||
delete customOptions.headers // 已经处理过了
|
||||
delete customOptions.fetch
|
||||
if (Object.keys(customOptions).length > 0) {
|
||||
builder.withCustomParams(customOptions)
|
||||
}
|
||||
|
||||
@ -48,6 +48,7 @@ export enum IpcChannel {
|
||||
|
||||
App_QuoteToMain = 'app:quote-to-main',
|
||||
App_SetDisableHardwareAcceleration = 'app:set-disable-hardware-acceleration',
|
||||
App_SetUseSystemTitleBar = 'app:set-use-system-title-bar',
|
||||
|
||||
Notification_Send = 'notification:send',
|
||||
Notification_OnClick = 'notification:on-click',
|
||||
|
||||
@ -233,7 +233,8 @@ export enum codeTools {
|
||||
geminiCli = 'gemini-cli',
|
||||
openaiCodex = 'openai-codex',
|
||||
iFlowCli = 'iflow-cli',
|
||||
githubCopilotCli = 'github-copilot-cli'
|
||||
githubCopilotCli = 'github-copilot-cli',
|
||||
kimiCli = 'kimi-cli'
|
||||
}
|
||||
|
||||
export enum terminalApps {
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { normalizeHeaders, withUserAgentSuffix } from '@ai-sdk/provider-utils'
|
||||
|
||||
export const defaultAppHeaders = () => {
|
||||
return {
|
||||
'HTTP-Referer': 'https://cherry-ai.com',
|
||||
@ -5,6 +7,43 @@ export const defaultAppHeaders = () => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并多个 headers 对象,自动标准化大小写
|
||||
*
|
||||
* @param headerSets - headers 集合(按顺序合并,后者覆盖前者,但 User-Agent 会追加)
|
||||
* @returns 合并后的 headers(键全部小写)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const result = mergeHeaders(
|
||||
* { 'User-Agent': 'DefaultAgent', 'X-Custom': 'value1' },
|
||||
* { 'user-agent': 'CustomSuffix', 'X-Custom': 'value2' }
|
||||
* )
|
||||
* // result: { 'user-agent': 'DefaultAgent CustomSuffix', 'x-custom': 'value2' }
|
||||
* ```
|
||||
*/
|
||||
export function mergeHeaders(
|
||||
...headerSets: Array<HeadersInit | Record<string, string | undefined> | undefined>
|
||||
): Record<string, string> {
|
||||
const { result, userAgents } = headerSets.reduce(
|
||||
(acc, headerSet) => {
|
||||
const headers = new Headers(normalizeHeaders(headerSet))
|
||||
|
||||
const ua = headers.get('user-agent')
|
||||
if (ua) {
|
||||
acc.userAgents.push(ua)
|
||||
headers.delete('user-agent')
|
||||
}
|
||||
|
||||
headers.forEach((value, key) => acc.result.set(key, value))
|
||||
return acc
|
||||
},
|
||||
{ result: new Headers(), userAgents: [] as string[] }
|
||||
)
|
||||
|
||||
return userAgents.length > 0 ? withUserAgentSuffix(result, ...userAgents) : Object.fromEntries(result.entries())
|
||||
}
|
||||
|
||||
// Following two function are not being used for now.
|
||||
// I may use them in the future, so just keep them commented. - by eurfelux
|
||||
|
||||
|
||||
534
pnpm-lock.yaml
generated
534
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
360
scripts/__tests__/check-hardcoded-strings.test.ts
Normal file
360
scripts/__tests__/check-hardcoded-strings.test.ts
Normal file
@ -0,0 +1,360 @@
|
||||
import * as path from 'path'
|
||||
import { Node, Project } from 'ts-morph'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import {
|
||||
HardcodedStringDetector,
|
||||
hasCJK,
|
||||
hasEnglishUIText,
|
||||
isInCodeContext,
|
||||
isNonUIString,
|
||||
shouldSkipNode
|
||||
} from '../check-hardcoded-strings'
|
||||
|
||||
function createTestProject() {
|
||||
return new Project({
|
||||
skipAddingFilesFromTsConfig: true,
|
||||
skipFileDependencyResolution: true,
|
||||
compilerOptions: { jsx: 2 } // React JSX
|
||||
})
|
||||
}
|
||||
|
||||
function findStringLiteral(project: Project, code: string, targetString: string): Node | undefined {
|
||||
const sourceFile = project.createSourceFile('test.tsx', code, { overwrite: true })
|
||||
let found: Node | undefined
|
||||
sourceFile.forEachDescendant((node) => {
|
||||
if (Node.isStringLiteral(node) && node.getLiteralValue() === targetString) {
|
||||
found = node
|
||||
}
|
||||
})
|
||||
return found
|
||||
}
|
||||
|
||||
function findTemplateLiteral(project: Project, code: string): Node | undefined {
|
||||
const sourceFile = project.createSourceFile('test.tsx', code, { overwrite: true })
|
||||
let found: Node | undefined
|
||||
sourceFile.forEachDescendant((node) => {
|
||||
if (Node.isNoSubstitutionTemplateLiteral(node) || Node.isTemplateExpression(node)) {
|
||||
found = node
|
||||
}
|
||||
})
|
||||
return found
|
||||
}
|
||||
|
||||
// Mock fs module
|
||||
vi.mock('fs')
|
||||
|
||||
describe('check-hardcoded-strings', () => {
|
||||
const mockSrcDir = '/mock/src/renderer/src'
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
describe('hasCJK', () => {
|
||||
it('should detect Chinese characters', () => {
|
||||
expect(hasCJK('测试文本')).toBe(true)
|
||||
expect(hasCJK('Hello 世界')).toBe(true)
|
||||
expect(hasCJK('中文')).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect Japanese characters', () => {
|
||||
expect(hasCJK('こんにちは')).toBe(true) // Hiragana
|
||||
expect(hasCJK('カタカナ')).toBe(true) // Katakana
|
||||
expect(hasCJK('日本語')).toBe(true) // Kanji
|
||||
})
|
||||
|
||||
it('should detect Korean characters', () => {
|
||||
expect(hasCJK('한국어')).toBe(true) // Hangul
|
||||
expect(hasCJK('안녕하세요')).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for non-CJK text', () => {
|
||||
expect(hasCJK('Hello World')).toBe(false)
|
||||
expect(hasCJK('12345')).toBe(false)
|
||||
expect(hasCJK('')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('hasEnglishUIText', () => {
|
||||
it('should detect English UI text patterns', () => {
|
||||
expect(hasEnglishUIText('Create New File')).toBe(true)
|
||||
expect(hasEnglishUIText('Save As')).toBe(true)
|
||||
expect(hasEnglishUIText('Open Project')).toBe(true)
|
||||
})
|
||||
|
||||
it('should reject single words', () => {
|
||||
expect(hasEnglishUIText('Save')).toBe(false)
|
||||
expect(hasEnglishUIText('Cancel')).toBe(false)
|
||||
})
|
||||
|
||||
it('should reject lowercase text', () => {
|
||||
expect(hasEnglishUIText('create new file')).toBe(false)
|
||||
expect(hasEnglishUIText('save as')).toBe(false)
|
||||
})
|
||||
|
||||
it('should reject too long phrases', () => {
|
||||
expect(hasEnglishUIText('This Is A Very Long Phrase With Many Words')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isNonUIString', () => {
|
||||
it('should identify empty strings', () => {
|
||||
expect(isNonUIString('')).toBe(true)
|
||||
})
|
||||
|
||||
it('should identify pure numbers', () => {
|
||||
expect(isNonUIString('123')).toBe(true)
|
||||
expect(isNonUIString('0')).toBe(true)
|
||||
expect(isNonUIString('999')).toBe(true)
|
||||
})
|
||||
|
||||
it('should not mark regular UI text as non-UI', () => {
|
||||
expect(isNonUIString('Hello World')).toBe(false)
|
||||
expect(isNonUIString('Save')).toBe(false)
|
||||
expect(isNonUIString('确认')).toBe(false)
|
||||
expect(isNonUIString('请输入内容')).toBe(false)
|
||||
expect(isNonUIString('-')).toBe(false) // Even short strings may be UI in specific contexts
|
||||
})
|
||||
|
||||
it('should not filter technical strings (now handled by AST context)', () => {
|
||||
// With AST-based detection, these are no longer filtered
|
||||
// because we only check specific UI contexts where they rarely appear
|
||||
expect(isNonUIString('./path/to/file')).toBe(false)
|
||||
expect(isNonUIString('https://example.com')).toBe(false)
|
||||
expect(isNonUIString('#fff')).toBe(false)
|
||||
expect(isNonUIString('snake_case_id')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('File filtering', () => {
|
||||
const IGNORED_DIRS = ['__tests__', 'node_modules', 'i18n', 'locales', 'types', 'assets']
|
||||
const IGNORED_FILES = ['*.test.ts', '*.test.tsx', '*.d.ts']
|
||||
|
||||
const mockShouldSkipFile = (filePath: string): boolean => {
|
||||
const relativePath = filePath.replace(mockSrcDir + '/', '')
|
||||
|
||||
if (IGNORED_DIRS.some((dir) => relativePath.includes(dir))) {
|
||||
return true
|
||||
}
|
||||
|
||||
const fileName = path.basename(filePath)
|
||||
if (
|
||||
IGNORED_FILES.some((pattern) => {
|
||||
const regex = new RegExp(pattern.replace('*', '.*'))
|
||||
return regex.test(fileName)
|
||||
})
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
it('should skip test files', () => {
|
||||
expect(mockShouldSkipFile(`${mockSrcDir}/components/Button.test.tsx`)).toBe(true)
|
||||
expect(mockShouldSkipFile(`${mockSrcDir}/utils/helper.test.ts`)).toBe(true)
|
||||
})
|
||||
|
||||
it('should skip type definition files', () => {
|
||||
expect(mockShouldSkipFile(`${mockSrcDir}/types/index.d.ts`)).toBe(true)
|
||||
})
|
||||
|
||||
it('should skip i18n/locales directories', () => {
|
||||
expect(mockShouldSkipFile(`${mockSrcDir}/i18n/locales/en-us.json`)).toBe(true)
|
||||
expect(mockShouldSkipFile(`${mockSrcDir}/locales/zh-cn.json`)).toBe(true)
|
||||
})
|
||||
|
||||
it('should skip __tests__ directories', () => {
|
||||
expect(mockShouldSkipFile(`${mockSrcDir}/components/__tests__/Button.test.tsx`)).toBe(true)
|
||||
})
|
||||
|
||||
it('should NOT skip regular component files', () => {
|
||||
expect(mockShouldSkipFile(`${mockSrcDir}/components/Button.tsx`)).toBe(false)
|
||||
expect(mockShouldSkipFile(`${mockSrcDir}/pages/Home.tsx`)).toBe(false)
|
||||
})
|
||||
|
||||
it('should NOT skip regular TypeScript files', () => {
|
||||
expect(mockShouldSkipFile(`${mockSrcDir}/utils/helper.ts`)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('HardcodedStringDetector', () => {
|
||||
// These are integration tests that would require actual files
|
||||
// For unit testing, we test the exported utility functions instead
|
||||
|
||||
it('should be instantiable', () => {
|
||||
const detector = new HardcodedStringDetector()
|
||||
expect(detector).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('Legacy pattern compatibility (regex patterns for reference)', () => {
|
||||
// Keep legacy pattern tests for backward compatibility reference
|
||||
const CHINESE_PATTERNS = [
|
||||
{ regex: />([^<]*[\u4e00-\u9fff][^<]*)</g, name: 'JSX text content' },
|
||||
{
|
||||
regex: /(?:placeholder|title|label|message|description|tooltip)=["']([^"']*[\u4e00-\u9fff][^"']*)["']/g,
|
||||
name: 'attribute'
|
||||
}
|
||||
]
|
||||
|
||||
it('should detect Chinese characters in JSX text content (regex)', () => {
|
||||
const testLine = '<span>测试文本</span>'
|
||||
const matches = testLine.match(CHINESE_PATTERNS[0].regex)
|
||||
expect(matches).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should detect Chinese characters in placeholder attribute (regex)', () => {
|
||||
const testLine = 'placeholder="请输入内容"'
|
||||
const matches = testLine.match(CHINESE_PATTERNS[1].regex)
|
||||
expect(matches).not.toBeNull()
|
||||
})
|
||||
|
||||
it('should detect Chinese characters in title attribute (regex)', () => {
|
||||
const testLine = 'title="提示信息"'
|
||||
const matches = testLine.match(CHINESE_PATTERNS[1].regex)
|
||||
expect(matches).not.toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('shouldSkipNode', () => {
|
||||
let project: Project
|
||||
|
||||
beforeEach(() => {
|
||||
project = createTestProject()
|
||||
})
|
||||
|
||||
it('should skip import declarations', () => {
|
||||
const node = findStringLiteral(project, `import { foo } from 'some-module'`, 'some-module')
|
||||
expect(node).toBeDefined()
|
||||
expect(shouldSkipNode(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should skip export declarations', () => {
|
||||
const node = findStringLiteral(project, `export { foo } from 'some-module'`, 'some-module')
|
||||
expect(node).toBeDefined()
|
||||
expect(shouldSkipNode(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should skip logger calls', () => {
|
||||
const node = findStringLiteral(project, `logger.info('测试日志')`, '测试日志')
|
||||
expect(node).toBeDefined()
|
||||
expect(shouldSkipNode(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should skip console calls', () => {
|
||||
const node = findStringLiteral(project, `console.log('测试日志')`, '测试日志')
|
||||
expect(node).toBeDefined()
|
||||
expect(shouldSkipNode(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should skip t() translation function calls', () => {
|
||||
const node = findStringLiteral(project, `t('common.save')`, 'common.save')
|
||||
expect(node).toBeDefined()
|
||||
expect(shouldSkipNode(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should skip type alias declarations', () => {
|
||||
const node = findStringLiteral(project, `type Status = '成功' | '失败'`, '成功')
|
||||
expect(node).toBeDefined()
|
||||
expect(shouldSkipNode(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should skip interface declarations', () => {
|
||||
const node = findStringLiteral(project, `interface Foo { status: '成功' }`, '成功')
|
||||
expect(node).toBeDefined()
|
||||
expect(shouldSkipNode(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should skip enum members', () => {
|
||||
const node = findStringLiteral(project, `enum Status { Success = '成功' }`, '成功')
|
||||
expect(node).toBeDefined()
|
||||
expect(shouldSkipNode(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should skip language/locale variable declarations', () => {
|
||||
const node = findStringLiteral(project, `const languageOptions = ['中文', 'English']`, '中文')
|
||||
expect(node).toBeDefined()
|
||||
expect(shouldSkipNode(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should NOT skip regular string literals', () => {
|
||||
const node = findStringLiteral(project, `const message = '测试消息'`, '测试消息')
|
||||
expect(node).toBeDefined()
|
||||
expect(shouldSkipNode(node!)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('isInCodeContext', () => {
|
||||
let project: Project
|
||||
|
||||
beforeEach(() => {
|
||||
project = createTestProject()
|
||||
})
|
||||
|
||||
it('should detect tagged template expressions with css tag', () => {
|
||||
const node = findTemplateLiteral(project, 'const style = css`color: red;`')
|
||||
expect(node).toBeDefined()
|
||||
expect(isInCodeContext(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect tagged template expressions with styled tag', () => {
|
||||
const node = findTemplateLiteral(project, 'const Button = styled.button`padding: 10px;`')
|
||||
expect(node).toBeDefined()
|
||||
expect(isInCodeContext(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect CSS variable names', () => {
|
||||
const node = findStringLiteral(project, `const customStyle = 'color: blue'`, 'color: blue')
|
||||
expect(node).toBeDefined()
|
||||
expect(isInCodeContext(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect code variable names', () => {
|
||||
const node = findStringLiteral(project, `const pythonCode = 'print("hello")'`, 'print("hello")')
|
||||
expect(node).toBeDefined()
|
||||
expect(isInCodeContext(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect CSS property assignments', () => {
|
||||
const node = findStringLiteral(project, `const obj = { style: 'color: red' }`, 'color: red')
|
||||
expect(node).toBeDefined()
|
||||
expect(isInCodeContext(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect code property assignments', () => {
|
||||
const node = findStringLiteral(project, `const obj = { script: 'console.log(1)' }`, 'console.log(1)')
|
||||
expect(node).toBeDefined()
|
||||
expect(isInCodeContext(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect JSX style attributes', () => {
|
||||
const node = findStringLiteral(project, `<div style={'color: red'} />`, 'color: red')
|
||||
expect(node).toBeDefined()
|
||||
expect(isInCodeContext(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect executeJavaScript calls', () => {
|
||||
const node = findStringLiteral(project, `webview.executeJavaScript('document.title')`, 'document.title')
|
||||
expect(node).toBeDefined()
|
||||
expect(isInCodeContext(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect executeJavaScript with string concatenation', () => {
|
||||
const node = findStringLiteral(project, `webview.executeJavaScript('var x = ' + value + ';')`, 'var x = ')
|
||||
expect(node).toBeDefined()
|
||||
expect(isInCodeContext(node!)).toBe(true)
|
||||
})
|
||||
|
||||
it('should NOT detect regular strings', () => {
|
||||
const node = findStringLiteral(project, `const message = '普通消息'`, '普通消息')
|
||||
expect(node).toBeDefined()
|
||||
expect(isInCodeContext(node!)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -11,18 +11,24 @@ const workspaceConfigPath = path.join(__dirname, '..', 'pnpm-workspace.yaml')
|
||||
const packages = [
|
||||
'@img/sharp-darwin-arm64',
|
||||
'@img/sharp-darwin-x64',
|
||||
'@img/sharp-linux-arm64',
|
||||
'@img/sharp-linux-x64',
|
||||
'@img/sharp-win32-arm64',
|
||||
'@img/sharp-win32-x64',
|
||||
'@img/sharp-libvips-darwin-arm64',
|
||||
'@img/sharp-libvips-darwin-x64',
|
||||
'@img/sharp-libvips-linux-arm64',
|
||||
'@img/sharp-libvips-linuxmusl-arm64',
|
||||
'@img/sharp-libvips-linux-x64',
|
||||
'@img/sharp-libvips-linuxmusl-x64',
|
||||
'@img/sharp-linux-arm64',
|
||||
'@img/sharp-linux-x64',
|
||||
'@img/sharp-linuxmusl-arm64',
|
||||
'@img/sharp-linuxmusl-x64',
|
||||
'@img/sharp-win32-arm64',
|
||||
'@img/sharp-win32-x64',
|
||||
'@libsql/darwin-arm64',
|
||||
'@libsql/darwin-x64',
|
||||
'@libsql/linux-arm64-gnu',
|
||||
'@libsql/linux-x64-gnu',
|
||||
'@libsql/linux-arm64-musl',
|
||||
'@libsql/linux-x64-musl',
|
||||
'@libsql/win32-x64-msvc',
|
||||
'@napi-rs/system-ocr-darwin-arm64',
|
||||
'@napi-rs/system-ocr-darwin-x64',
|
||||
@ -34,7 +40,8 @@ const packages = [
|
||||
const platformToArch = {
|
||||
mac: 'darwin',
|
||||
windows: 'win32',
|
||||
linux: 'linux'
|
||||
linux: 'linux',
|
||||
linuxmusl: 'linuxmusl'
|
||||
}
|
||||
|
||||
exports.default = async function (context) {
|
||||
|
||||
464
scripts/check-hardcoded-strings.ts
Normal file
464
scripts/check-hardcoded-strings.ts
Normal file
@ -0,0 +1,464 @@
|
||||
/**
|
||||
* AST-based hardcoded string detection for i18n
|
||||
*/
|
||||
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import type { SourceFile } from 'ts-morph'
|
||||
import { Node, Project } from 'ts-morph'
|
||||
|
||||
const RENDERER_DIR = path.join(__dirname, '../src/renderer/src')
|
||||
const MAIN_DIR = path.join(__dirname, '../src/main')
|
||||
const EXTENSIONS = ['.tsx', '.ts']
|
||||
const IGNORED_DIRS = ['__tests__', 'node_modules', 'i18n', 'locales', 'types', 'assets']
|
||||
const IGNORED_FILES = ['*.test.ts', '*.test.tsx', '*.d.ts', '*prompts*.ts']
|
||||
|
||||
// 'content' is handled specially - only checked for specific components
|
||||
const UI_ATTRIBUTES = [
|
||||
'placeholder',
|
||||
'title',
|
||||
'label',
|
||||
'message',
|
||||
'description',
|
||||
'tooltip',
|
||||
'buttonLabel',
|
||||
'name',
|
||||
'detail',
|
||||
'body'
|
||||
]
|
||||
|
||||
const CONTEXT_SENSITIVE_ATTRIBUTES: Record<string, string[]> = {
|
||||
content: ['Tooltip', 'Popover', 'Modal', 'Popconfirm', 'Alert', 'Notification', 'Message']
|
||||
}
|
||||
|
||||
const UI_PROPERTIES = ['message', 'text', 'title', 'label', 'placeholder', 'description', 'detail']
|
||||
|
||||
interface Finding {
|
||||
file: string
|
||||
line: number
|
||||
content: string
|
||||
type: 'chinese' | 'english'
|
||||
source: 'renderer' | 'main'
|
||||
nodeType: string
|
||||
}
|
||||
|
||||
const CJK_RANGES = [
|
||||
'\u3000-\u303f', // CJK Symbols and Punctuation
|
||||
'\u3040-\u309f', // Hiragana
|
||||
'\u30a0-\u30ff', // Katakana
|
||||
'\u3100-\u312f', // Bopomofo
|
||||
'\u3400-\u4dbf', // CJK Unified Ideographs Extension A
|
||||
'\u4e00-\u9fff', // CJK Unified Ideographs
|
||||
'\uac00-\ud7af', // Hangul Syllables
|
||||
'\uf900-\ufaff' // CJK Compatibility Ideographs
|
||||
].join('')
|
||||
|
||||
function hasCJK(text: string): boolean {
|
||||
return new RegExp(`[${CJK_RANGES}]`).test(text)
|
||||
}
|
||||
|
||||
function hasEnglishUIText(text: string): boolean {
|
||||
const words = text.trim().split(/\s+/)
|
||||
if (words.length < 2 || words.length > 6) return false
|
||||
return /^[A-Z][a-z]+(\s+[A-Za-z]+){1,5}$/.test(text.trim())
|
||||
}
|
||||
|
||||
function createFinding(
|
||||
node: Node,
|
||||
sourceFile: SourceFile,
|
||||
type: 'chinese' | 'english',
|
||||
source: 'renderer' | 'main',
|
||||
nodeType: string
|
||||
): Finding {
|
||||
return {
|
||||
file: sourceFile.getFilePath(),
|
||||
line: sourceFile.getLineAndColumnAtPos(node.getStart()).line,
|
||||
content: node.getText().slice(0, 100),
|
||||
type,
|
||||
source,
|
||||
nodeType
|
||||
}
|
||||
}
|
||||
|
||||
function shouldSkipNode(node: Node): boolean {
|
||||
let current: Node | undefined = node
|
||||
|
||||
while (current) {
|
||||
const parent = current.getParent()
|
||||
if (!parent) break
|
||||
|
||||
if (Node.isImportDeclaration(parent) || Node.isExportDeclaration(parent)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (Node.isCallExpression(parent)) {
|
||||
const callText = parent.getExpression().getText()
|
||||
if (/^(logger|console)\.(log|error|warn|info|debug|silly|trace|withContext)/.test(callText)) {
|
||||
return true
|
||||
}
|
||||
const callee = parent.getExpression()
|
||||
if (Node.isIdentifier(callee) && callee.getText() === 't') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if (Node.isTypeNode(parent) || Node.isTypeAliasDeclaration(parent) || Node.isInterfaceDeclaration(parent)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (Node.isPropertySignature(parent)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (Node.isEnumMember(parent)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Native language names should stay in native form
|
||||
if (Node.isVariableDeclaration(parent)) {
|
||||
const varName = parent.getName()
|
||||
if (/language|locale/i.test(varName)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
current = parent
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function isNonUIString(text: string): boolean {
|
||||
if (text.length === 0) return true
|
||||
if (/^\d+$/.test(text)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
const CODE_CONTEXT = {
|
||||
cssTags: /^(css|keyframes|injectGlobal|createGlobalStyle|styled\.\w+)$/,
|
||||
cssNames: /style|css|animation/i,
|
||||
codeNames: /code|script|python|sql|query|html|template|regex|pattern|shim/i,
|
||||
jsxAttrs: new Set(['style', 'css']),
|
||||
execCalls: /\.(executeJavaScript|eval|Function|runPython|runPythonAsync)$/
|
||||
}
|
||||
|
||||
function isInCodeContext(node: Node): boolean {
|
||||
const parent = node.getParent()
|
||||
if (!parent) return false
|
||||
|
||||
if (Node.isTaggedTemplateExpression(parent)) {
|
||||
return CODE_CONTEXT.cssTags.test(parent.getTag().getText())
|
||||
}
|
||||
|
||||
if (Node.isVariableDeclaration(parent)) {
|
||||
const name = parent.getName()
|
||||
return CODE_CONTEXT.cssNames.test(name) || CODE_CONTEXT.codeNames.test(name)
|
||||
}
|
||||
|
||||
if (Node.isPropertyAssignment(parent)) {
|
||||
const name = parent.getName()
|
||||
return CODE_CONTEXT.cssNames.test(name) || CODE_CONTEXT.codeNames.test(name)
|
||||
}
|
||||
|
||||
if (Node.isJsxExpression(parent)) {
|
||||
const attr = parent.getParent()
|
||||
if (attr && Node.isJsxAttribute(attr)) {
|
||||
return CODE_CONTEXT.jsxAttrs.has(attr.getNameNode().getText())
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse up for code execution calls (handles string concatenation)
|
||||
let current: Node | undefined = parent
|
||||
while (current) {
|
||||
if (Node.isCallExpression(current)) {
|
||||
if (CODE_CONTEXT.execCalls.test(current.getExpression().getText())) {
|
||||
return true
|
||||
}
|
||||
break
|
||||
}
|
||||
if (!Node.isBinaryExpression(current) && !Node.isParenthesizedExpression(current)) {
|
||||
break
|
||||
}
|
||||
current = current.getParent()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function getJsxElementName(attrNode: Node): string | null {
|
||||
const parent = attrNode.getParent()
|
||||
if (!parent) return null
|
||||
|
||||
if (Node.isJsxOpeningElement(parent) || Node.isJsxSelfClosingElement(parent)) {
|
||||
return parent.getTagNameNode().getText()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function shouldCheckAttribute(attrName: string, elementName: string | null): boolean {
|
||||
if (UI_ATTRIBUTES.includes(attrName)) {
|
||||
return true
|
||||
}
|
||||
const allowedComponents = CONTEXT_SENSITIVE_ATTRIBUTES[attrName]
|
||||
if (allowedComponents && elementName) {
|
||||
return allowedComponents.includes(elementName)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
class HardcodedStringDetector {
|
||||
private project: Project
|
||||
|
||||
constructor() {
|
||||
this.project = new Project({
|
||||
skipAddingFilesFromTsConfig: true,
|
||||
skipFileDependencyResolution: true
|
||||
})
|
||||
}
|
||||
|
||||
scanFile(filePath: string, source: 'renderer' | 'main'): Finding[] {
|
||||
const findings: Finding[] = []
|
||||
|
||||
try {
|
||||
const sourceFile = this.project.addSourceFileAtPath(filePath)
|
||||
sourceFile.forEachDescendant((node) => {
|
||||
this.checkNode(node, sourceFile, source, findings)
|
||||
})
|
||||
this.project.removeSourceFile(sourceFile)
|
||||
} catch (error) {
|
||||
console.error(`Error parsing ${filePath}:`, error)
|
||||
}
|
||||
|
||||
return findings
|
||||
}
|
||||
|
||||
private checkNode(node: Node, sourceFile: SourceFile, source: 'renderer' | 'main', findings: Finding[]): void {
|
||||
if (shouldSkipNode(node)) return
|
||||
|
||||
if (Node.isJsxText(node)) {
|
||||
const text = node.getText().trim()
|
||||
if (text && hasCJK(text)) {
|
||||
// Skip SVG internal elements
|
||||
const parent = node.getParent()
|
||||
if (parent && (Node.isJsxElement(parent) || Node.isJsxSelfClosingElement(parent))) {
|
||||
const tagName = Node.isJsxElement(parent)
|
||||
? parent.getOpeningElement().getTagNameNode().getText()
|
||||
: parent.getTagNameNode().getText()
|
||||
if (['title', 'desc', 'text', 'tspan'].includes(tagName)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
findings.push(createFinding(node, sourceFile, 'chinese', source, 'JsxText'))
|
||||
}
|
||||
}
|
||||
|
||||
if (Node.isJsxAttribute(node)) {
|
||||
const attrName = node.getNameNode().getText()
|
||||
const elementName = getJsxElementName(node)
|
||||
|
||||
if (shouldCheckAttribute(attrName, elementName)) {
|
||||
const initializer = node.getInitializer()
|
||||
if (initializer && Node.isStringLiteral(initializer)) {
|
||||
const value = initializer.getLiteralValue()
|
||||
if (!isNonUIString(value)) {
|
||||
if (hasCJK(value)) {
|
||||
findings.push(createFinding(node, sourceFile, 'chinese', source, 'JsxAttribute'))
|
||||
} else if (source === 'renderer' && hasEnglishUIText(value)) {
|
||||
findings.push(createFinding(node, sourceFile, 'english', source, 'JsxAttribute'))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Node.isStringLiteral(node)) {
|
||||
if (isInCodeContext(node)) return
|
||||
|
||||
const value = node.getLiteralValue()
|
||||
if (isNonUIString(value)) return
|
||||
|
||||
const parent = node.getParent()
|
||||
|
||||
if (parent && Node.isPropertyAssignment(parent)) {
|
||||
const propName = parent.getName()
|
||||
if (UI_PROPERTIES.includes(propName)) {
|
||||
if (hasCJK(value)) {
|
||||
findings.push(createFinding(node, sourceFile, 'chinese', source, 'PropertyAssignment'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parent && Node.isCallExpression(parent)) {
|
||||
const callText = parent.getExpression().getText()
|
||||
if (
|
||||
/^(window\.toast|message|antdMessage|Modal|notification)\.(success|error|warning|info|confirm)/.test(callText)
|
||||
) {
|
||||
if (hasCJK(value)) {
|
||||
findings.push(createFinding(node, sourceFile, 'chinese', source, 'CallExpression'))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Node.isTemplateExpression(node) || Node.isNoSubstitutionTemplateLiteral(node)) {
|
||||
if (isInCodeContext(node)) return
|
||||
|
||||
const text = node.getText()
|
||||
if (hasCJK(text)) {
|
||||
findings.push(createFinding(node, sourceFile, 'chinese', source, 'TemplateLiteral'))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function shouldSkipFile(filePath: string, baseDir: string): boolean {
|
||||
const relativePath = path.relative(baseDir, filePath)
|
||||
|
||||
if (IGNORED_DIRS.some((dir) => relativePath.includes(dir))) {
|
||||
return true
|
||||
}
|
||||
|
||||
const fileName = path.basename(filePath)
|
||||
if (
|
||||
IGNORED_FILES.some((pattern) => {
|
||||
const regex = new RegExp(pattern.replace('*', '.*'))
|
||||
return regex.test(fileName)
|
||||
})
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function scanDirectory(dir: string, source: 'renderer' | 'main', detector: HardcodedStringDetector): Finding[] {
|
||||
const findings: Finding[] = []
|
||||
|
||||
if (!fs.existsSync(dir)) {
|
||||
return findings
|
||||
}
|
||||
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name)
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
if (!IGNORED_DIRS.includes(entry.name)) {
|
||||
findings.push(...scanDirectory(fullPath, source, detector))
|
||||
}
|
||||
} else if (entry.isFile() && EXTENSIONS.some((ext) => entry.name.endsWith(ext))) {
|
||||
if (!shouldSkipFile(fullPath, source === 'renderer' ? RENDERER_DIR : MAIN_DIR)) {
|
||||
findings.push(...detector.scanFile(fullPath, source))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return findings
|
||||
}
|
||||
|
||||
function formatFindings(findings: Finding[]): string {
|
||||
if (findings.length === 0) {
|
||||
return '✅ No hardcoded strings found!'
|
||||
}
|
||||
|
||||
const rendererFindings = findings.filter((f) => f.source === 'renderer')
|
||||
const mainFindings = findings.filter((f) => f.source === 'main')
|
||||
const chineseFindings = findings.filter((f) => f.type === 'chinese')
|
||||
const englishFindings = findings.filter((f) => f.type === 'english')
|
||||
|
||||
let output = ''
|
||||
|
||||
if (rendererFindings.length > 0) {
|
||||
output += '\n📦 Renderer Process:\n'
|
||||
output += '-'.repeat(50) + '\n'
|
||||
|
||||
const rendererChinese = rendererFindings.filter((f) => f.type === 'chinese')
|
||||
const rendererEnglish = rendererFindings.filter((f) => f.type === 'english')
|
||||
|
||||
if (rendererChinese.length > 0) {
|
||||
output += '\n⚠️ Hardcoded Chinese strings:\n'
|
||||
rendererChinese.forEach((f) => {
|
||||
const relativePath = path.relative(RENDERER_DIR, f.file)
|
||||
output += `\n📍 ${relativePath}:${f.line} [${f.nodeType}]\n`
|
||||
output += ` ${f.content}\n`
|
||||
})
|
||||
}
|
||||
|
||||
if (rendererEnglish.length > 0) {
|
||||
output += '\n⚠️ Potential hardcoded English strings:\n'
|
||||
rendererEnglish.forEach((f) => {
|
||||
const relativePath = path.relative(RENDERER_DIR, f.file)
|
||||
output += `\n📍 ${relativePath}:${f.line} [${f.nodeType}]\n`
|
||||
output += ` ${f.content}\n`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (mainFindings.length > 0) {
|
||||
output += '\n📦 Main Process:\n'
|
||||
output += '-'.repeat(50) + '\n'
|
||||
|
||||
const mainChinese = mainFindings.filter((f) => f.type === 'chinese')
|
||||
|
||||
if (mainChinese.length > 0) {
|
||||
output += '\n⚠️ Hardcoded Chinese strings:\n'
|
||||
mainChinese.forEach((f) => {
|
||||
const relativePath = path.relative(MAIN_DIR, f.file)
|
||||
output += `\n📍 ${relativePath}:${f.line} [${f.nodeType}]\n`
|
||||
output += ` ${f.content}\n`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
output += '\n' + '='.repeat(50) + '\n'
|
||||
output += `Total: ${findings.length} potential issues found\n`
|
||||
output += ` - Renderer: ${rendererFindings.length} (Chinese: ${rendererFindings.filter((f) => f.type === 'chinese').length}, English: ${rendererFindings.filter((f) => f.type === 'english').length})\n`
|
||||
output += ` - Main: ${mainFindings.length} (Chinese: ${mainFindings.length})\n`
|
||||
output += ` - Total Chinese: ${chineseFindings.length}\n`
|
||||
output += ` - Total English: ${englishFindings.length}\n`
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
export function main(): void {
|
||||
console.log('🔍 Scanning for hardcoded strings using AST analysis...\n')
|
||||
|
||||
const detector = new HardcodedStringDetector()
|
||||
|
||||
const rendererFindings = scanDirectory(RENDERER_DIR, 'renderer', detector)
|
||||
const mainFindings = scanDirectory(MAIN_DIR, 'main', detector)
|
||||
const findings = [...rendererFindings, ...mainFindings]
|
||||
|
||||
const output = formatFindings(findings)
|
||||
console.log(output)
|
||||
|
||||
// Strict mode for CI
|
||||
const strictMode = process.env.I18N_STRICT === 'true' || process.argv.includes('--strict')
|
||||
const chineseCount = findings.filter((f) => f.type === 'chinese').length
|
||||
|
||||
if (strictMode && chineseCount > 0) {
|
||||
console.error('\n❌ Hardcoded Chinese strings detected in strict mode!')
|
||||
console.error('Please replace these with i18n keys using the t() function.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (findings.length > 0) {
|
||||
console.log('\n💡 Tip: Consider replacing these strings with i18n keys.')
|
||||
console.log(' Use the t() function from react-i18next for translations.')
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
HardcodedStringDetector,
|
||||
hasCJK,
|
||||
hasEnglishUIText,
|
||||
isInCodeContext,
|
||||
isNonUIString,
|
||||
shouldSkipFile,
|
||||
shouldSkipNode,
|
||||
UI_ATTRIBUTES,
|
||||
UI_PROPERTIES
|
||||
}
|
||||
|
||||
main()
|
||||
@ -76,12 +76,12 @@ if (isLinux && process.env.XDG_SESSION_TYPE === 'wayland') {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set window class and name for X11
|
||||
* This ensures the system tray and window manager identify the app correctly
|
||||
* Set window class and name for Linux
|
||||
* This ensures the window manager identifies the app correctly on both X11 and Wayland
|
||||
*/
|
||||
if (isLinux) {
|
||||
app.commandLine.appendSwitch('class', 'cherry-studio')
|
||||
app.commandLine.appendSwitch('name', 'cherry-studio')
|
||||
app.commandLine.appendSwitch('class', 'CherryStudio')
|
||||
app.commandLine.appendSwitch('name', 'CherryStudio')
|
||||
}
|
||||
|
||||
// DocumentPolicyIncludeJSCallStacksInCrashReports: Enable features for unresponsive renderer js call stacks
|
||||
|
||||
@ -900,6 +900,9 @@ export async function registerIpc(mainWindow: BrowserWindow, app: Electron.App)
|
||||
ipcMain.handle(IpcChannel.App_SetDisableHardwareAcceleration, (_, isDisable: boolean) => {
|
||||
configManager.setDisableHardwareAcceleration(isDisable)
|
||||
})
|
||||
ipcMain.handle(IpcChannel.App_SetUseSystemTitleBar, (_, isActive: boolean) => {
|
||||
configManager.setUseSystemTitleBar(isActive)
|
||||
})
|
||||
ipcMain.handle(IpcChannel.TRACE_SAVE_DATA, (_, topicId: string) => saveSpans(topicId))
|
||||
ipcMain.handle(IpcChannel.TRACE_GET_DATA, (_, topicId: string, traceId: string, modelName?: string) =>
|
||||
getSpans(topicId, traceId, modelName)
|
||||
|
||||
@ -58,7 +58,7 @@ export default class GeneralReranker extends BaseReranker {
|
||||
return this.getRerankResult(searchResults, rerankResults)
|
||||
} catch (error: any) {
|
||||
const errorDetails = this.formatErrorMessage(url, error, requestBody)
|
||||
throw new Error(`重排序请求失败: ${error.message}\n请求详情: ${errorDetails}`)
|
||||
throw new Error(`Rerank request failed: ${error.message}\nRequest details: ${errorDetails}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,14 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.mock('os', () => ({
|
||||
default: {
|
||||
release: vi.fn(() => '10.0.0'),
|
||||
homedir: vi.fn(() => '/home/test')
|
||||
},
|
||||
release: vi.fn(() => '10.0.0'),
|
||||
homedir: vi.fn(() => '/home/test')
|
||||
}))
|
||||
|
||||
vi.mock('node:fs', () => ({
|
||||
default: {
|
||||
existsSync: vi.fn(() => false),
|
||||
@ -95,7 +104,8 @@ vi.mock('electron', () => {
|
||||
return '/mock/unknown'
|
||||
}),
|
||||
getAppPath: vi.fn(() => '/mock/app'),
|
||||
setPath: vi.fn()
|
||||
setPath: vi.fn(),
|
||||
getVersion: vi.fn(() => '1.0.0')
|
||||
}
|
||||
|
||||
const nativeTheme = {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { loggerService } from '@logger'
|
||||
import { generateUserAgent } from '@main/utils/systemInfo'
|
||||
import type { BrowserView, BrowserWindow } from 'electron'
|
||||
|
||||
export const logger = loggerService.withContext('MCPBrowserCDP')
|
||||
export const userAgent =
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
|
||||
export const userAgent = generateUserAgent()
|
||||
|
||||
export interface TabInfo {
|
||||
id: string
|
||||
|
||||
@ -109,7 +109,7 @@ class DifyKnowledgeServer {
|
||||
const parsed = SearchKnowledgeArgsSchema.safeParse(args)
|
||||
if (!parsed.success) {
|
||||
const errorDetails = JSON.stringify(parsed.error.format(), null, 2)
|
||||
throw new Error(`无效的参数:\n${errorDetails}`)
|
||||
throw new Error(`Invalid arguments:\n${errorDetails}`)
|
||||
}
|
||||
return await this.performSearchKnowledge(
|
||||
parsed.data.id,
|
||||
@ -144,7 +144,7 @@ class DifyKnowledgeServer {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
throw new Error(`API 请求失败,状态码 ${response.status}: ${errorText}`)
|
||||
throw new Error(`API request failed, status code ${response.status}: ${errorText}`)
|
||||
}
|
||||
|
||||
const apiResponse = await response.json()
|
||||
@ -161,7 +161,7 @@ class DifyKnowledgeServer {
|
||||
? knowledges.map((k) => `- **${k.name}** (ID: ${k.id})\n ${k.description || 'No Description'}`).join('\n')
|
||||
: '- No knowledges found.'
|
||||
|
||||
const formattedText = `### 可用知识库:\n\n${listText}`
|
||||
const formattedText = `### Available Knowledge Bases:\n\n${listText}`
|
||||
|
||||
return {
|
||||
content: [{ type: 'text', text: formattedText }]
|
||||
@ -206,13 +206,13 @@ class DifyKnowledgeServer {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text()
|
||||
throw new Error(`API 请求失败,状态码 ${response.status}: ${errorText}`)
|
||||
throw new Error(`API request failed, status code ${response.status}: ${errorText}`)
|
||||
}
|
||||
|
||||
const searchResponse: DifySearchKnowledgeResponse = await response.json()
|
||||
|
||||
if (!searchResponse || !Array.isArray(searchResponse.records)) {
|
||||
throw new Error(`从 Dify API 收到的响应格式无效: ${JSON.stringify(searchResponse)}`)
|
||||
throw new Error(`Invalid response format from Dify API: ${JSON.stringify(searchResponse)}`)
|
||||
}
|
||||
|
||||
const header = `### Query: ${query}\n\n`
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
// port https://github.com/zcaceres/fetch-mcp/blob/main/src/index.ts
|
||||
|
||||
import { generateUserAgent } from '@main/utils/systemInfo'
|
||||
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
|
||||
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'
|
||||
import { mergeHeaders } from '@shared/utils'
|
||||
import { net } from 'electron'
|
||||
import { JSDOM } from 'jsdom'
|
||||
import TurndownService from 'turndown'
|
||||
@ -18,11 +20,12 @@ export class Fetcher {
|
||||
private static async _fetch({ url, headers }: RequestPayload): Promise<Response> {
|
||||
try {
|
||||
const response = await net.fetch(url, {
|
||||
headers: {
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
||||
...headers
|
||||
}
|
||||
headers: mergeHeaders(
|
||||
{
|
||||
'User-Agent': generateUserAgent()
|
||||
},
|
||||
headers
|
||||
)
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
@ -23,6 +23,22 @@ import { promisify } from 'util'
|
||||
const execAsync = promisify(require('child_process').exec)
|
||||
const logger = loggerService.withContext('CodeToolsService')
|
||||
|
||||
// Sensitive environment variable keys to redact in logs
|
||||
const SENSITIVE_ENV_KEYS = ['API_KEY', 'APIKEY', 'AUTHORIZATION', 'TOKEN', 'SECRET', 'PASSWORD']
|
||||
|
||||
/**
|
||||
* Sanitize environment variables for safe logging
|
||||
* Redacts values of sensitive keys to prevent credential leakage
|
||||
*/
|
||||
function sanitizeEnvForLogging(env: Record<string, string>): Record<string, string> {
|
||||
const sanitized: Record<string, string> = {}
|
||||
for (const [key, value] of Object.entries(env)) {
|
||||
const isSensitive = SENSITIVE_ENV_KEYS.some((k) => key.toUpperCase().includes(k))
|
||||
sanitized[key] = isSensitive ? '<redacted>' : value
|
||||
}
|
||||
return sanitized
|
||||
}
|
||||
|
||||
interface VersionInfo {
|
||||
installed: string | null
|
||||
latest: string | null
|
||||
@ -87,6 +103,8 @@ class CodeToolsService {
|
||||
return '@iflow-ai/iflow-cli'
|
||||
case codeTools.githubCopilotCli:
|
||||
return '@github/copilot'
|
||||
case codeTools.kimiCli:
|
||||
return 'kimi-cli' // Python package
|
||||
default:
|
||||
throw new Error(`Unsupported CLI tool: ${cliTool}`)
|
||||
}
|
||||
@ -106,6 +124,8 @@ class CodeToolsService {
|
||||
return 'iflow'
|
||||
case codeTools.githubCopilotCli:
|
||||
return 'copilot'
|
||||
case codeTools.kimiCli:
|
||||
return 'kimi'
|
||||
default:
|
||||
throw new Error(`Unsupported CLI tool: ${cliTool}`)
|
||||
}
|
||||
@ -451,7 +471,7 @@ class CodeToolsService {
|
||||
}
|
||||
}
|
||||
|
||||
const needsUpdate = !!(installedVersion && latestVersion && installedVersion !== latestVersion)
|
||||
const needsUpdate = !!(latestVersion && isInstalled && (!installedVersion || installedVersion !== latestVersion))
|
||||
logger.info(
|
||||
`Version check result for ${cliTool}: installed=${installedVersion}, latest=${latestVersion}, needsUpdate=${needsUpdate}`
|
||||
)
|
||||
@ -613,7 +633,7 @@ class CodeToolsService {
|
||||
}
|
||||
|
||||
logger.info('Setting environment variables:', Object.keys(env))
|
||||
logger.info('Environment variable values:', env)
|
||||
logger.debug('Environment variable values:', sanitizeEnvForLogging(env))
|
||||
|
||||
if (isWindows) {
|
||||
// Windows uses set command
|
||||
@ -636,8 +656,7 @@ class CodeToolsService {
|
||||
.map(([key, value]) => {
|
||||
const sanitizedValue = String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"')
|
||||
const exportCmd = `export ${key}="${sanitizedValue}"`
|
||||
logger.info(`Setting env var: ${key}="${sanitizedValue}"`)
|
||||
logger.info(`Export command: ${exportCmd}`)
|
||||
logger.debug(`Setting env var: ${key}=<redacted>`)
|
||||
return exportCmd
|
||||
})
|
||||
.join(' && ')
|
||||
@ -647,26 +666,37 @@ class CodeToolsService {
|
||||
|
||||
let baseCommand = isWin ? `"${executablePath}"` : `"${bunPath}" "${executablePath}"`
|
||||
|
||||
// Add configuration parameters for OpenAI Codex
|
||||
if (cliTool === codeTools.openaiCodex && env.OPENAI_MODEL_PROVIDER && env.OPENAI_MODEL_PROVIDER != 'openai') {
|
||||
const provider = env.OPENAI_MODEL_PROVIDER
|
||||
const model = env.OPENAI_MODEL
|
||||
// delete the latest /
|
||||
const baseUrl = env.OPENAI_BASE_URL.replace(/\/$/, '')
|
||||
// Special handling for kimi-cli: use uvx instead of bun
|
||||
if (cliTool === codeTools.kimiCli) {
|
||||
const uvPath = path.join(os.homedir(), HOME_CHERRY_DIR, 'bin', await getBinaryName('uv'))
|
||||
baseCommand = `${uvPath} tool run ${packageName}`
|
||||
}
|
||||
|
||||
// Add configuration parameters for OpenAI Codex using command line args
|
||||
if (cliTool === codeTools.openaiCodex && env.OPENAI_MODEL_PROVIDER) {
|
||||
const providerId = env.OPENAI_MODEL_PROVIDER
|
||||
const providerName = env.OPENAI_MODEL_PROVIDER_NAME || providerId
|
||||
const normalizedBaseUrl = env.OPENAI_BASE_URL.replace(/\/$/, '')
|
||||
const model = _model
|
||||
|
||||
const configParams = [
|
||||
`--config model_provider="${provider}"`,
|
||||
`--config model="${model}"`,
|
||||
`--config model_providers.${provider}.name="${provider}"`,
|
||||
`--config model_providers.${provider}.base_url="${baseUrl}"`,
|
||||
`--config model_providers.${provider}.env_key="OPENAI_API_KEY"`
|
||||
`--config model_provider="${providerId}"`,
|
||||
`--config model_providers.${providerId}.name="${providerName}"`,
|
||||
`--config model_providers.${providerId}.base_url="${normalizedBaseUrl}"`,
|
||||
`--config model_providers.${providerId}.env_key="OPENAI_API_KEY"`,
|
||||
`--config model_providers.${providerId}.wire_api="responses"`,
|
||||
`--config model="${model}"`
|
||||
].join(' ')
|
||||
baseCommand = `${baseCommand} ${configParams}`
|
||||
}
|
||||
|
||||
const bunInstallPath = path.join(os.homedir(), HOME_CHERRY_DIR)
|
||||
|
||||
if (isInstalled) {
|
||||
// Special handling for kimi-cli: uvx handles installation automatically
|
||||
if (cliTool === codeTools.kimiCli) {
|
||||
// uvx will automatically download and run kimi-cli, no need to install
|
||||
// Just use the base command directly
|
||||
} else if (isInstalled) {
|
||||
// If already installed, run executable directly (with optional update message)
|
||||
if (updateMessage) {
|
||||
baseCommand = `echo "Checking ${cliTool} version..."${updateMessage} && ${baseCommand}`
|
||||
@ -777,14 +807,15 @@ class CodeToolsService {
|
||||
terminalArgs = args
|
||||
}
|
||||
|
||||
// Set cleanup task (delete temp file after 5 minutes)
|
||||
// Set cleanup task (delete temp file after 60 seconds)
|
||||
// Windows Terminal (UWP app) may take longer to initialize and read the file
|
||||
setTimeout(() => {
|
||||
try {
|
||||
fs.existsSync(batFilePath) && fs.unlinkSync(batFilePath)
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to cleanup temp bat file: ${error}`)
|
||||
}
|
||||
}, 10 * 1000) // Delete temp file after 10 seconds
|
||||
}, 60 * 1000) // Delete temp file after 60 seconds
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@ export enum ConfigKeys {
|
||||
SelectionAssistantFilterMode = 'selectionAssistantFilterMode',
|
||||
SelectionAssistantFilterList = 'selectionAssistantFilterList',
|
||||
DisableHardwareAcceleration = 'disableHardwareAcceleration',
|
||||
UseSystemTitleBar = 'useSystemTitleBar',
|
||||
Proxy = 'proxy',
|
||||
EnableDeveloperMode = 'enableDeveloperMode',
|
||||
ClientId = 'clientId',
|
||||
@ -251,6 +252,14 @@ export class ConfigManager {
|
||||
this.set(ConfigKeys.DisableHardwareAcceleration, value)
|
||||
}
|
||||
|
||||
getUseSystemTitleBar(): boolean {
|
||||
return this.get<boolean>(ConfigKeys.UseSystemTitleBar, false)
|
||||
}
|
||||
|
||||
setUseSystemTitleBar(value: boolean) {
|
||||
this.set(ConfigKeys.UseSystemTitleBar, value)
|
||||
}
|
||||
|
||||
setAndNotify(key: string, value: unknown) {
|
||||
this.set(key, value, true)
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { loggerService } from '@logger'
|
||||
import { mergeHeaders } from '@shared/utils'
|
||||
import { app, net, safeStorage } from 'electron'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
@ -70,11 +71,10 @@ class CopilotService {
|
||||
|
||||
constructor() {
|
||||
this.tokenFilePath = this.getTokenFilePath()
|
||||
this.headers = {
|
||||
...CONFIG.DEFAULT_HEADERS,
|
||||
this.headers = mergeHeaders(CONFIG.DEFAULT_HEADERS, {
|
||||
accept: 'application/json',
|
||||
'user-agent': 'Visual Studio Code (desktop)'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private getTokenFilePath = (): string => {
|
||||
@ -90,7 +90,7 @@ class CopilotService {
|
||||
*/
|
||||
private updateHeaders = (headers?: Record<string, string>): void => {
|
||||
if (headers && Object.keys(headers).length > 0) {
|
||||
this.headers = { ...headers }
|
||||
this.headers = mergeHeaders(this.headers, headers)
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,10 +139,9 @@ class CopilotService {
|
||||
|
||||
const response = await net.fetch(CONFIG.API_URLS.GITHUB_DEVICE_CODE, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...this.headers,
|
||||
headers: mergeHeaders(this.headers, {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
}),
|
||||
body: JSON.stringify({
|
||||
client_id: CONFIG.GITHUB_CLIENT_ID,
|
||||
scope: 'read:user'
|
||||
@ -178,10 +177,9 @@ class CopilotService {
|
||||
try {
|
||||
const response = await net.fetch(CONFIG.API_URLS.GITHUB_ACCESS_TOKEN, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
...this.headers,
|
||||
headers: mergeHeaders(this.headers, {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
}),
|
||||
body: JSON.stringify({
|
||||
client_id: CONFIG.GITHUB_CLIENT_ID,
|
||||
device_code,
|
||||
@ -247,10 +245,9 @@ class CopilotService {
|
||||
|
||||
const response = await net.fetch(CONFIG.API_URLS.COPILOT_TOKEN, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
...this.headers,
|
||||
headers: mergeHeaders(this.headers, {
|
||||
authorization: `token ${access_token}`
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// ExportService
|
||||
|
||||
import { loggerService } from '@logger'
|
||||
import { t } from '@main/utils/locales'
|
||||
import {
|
||||
AlignmentType,
|
||||
BorderStyle,
|
||||
@ -391,8 +392,8 @@ export class ExportService {
|
||||
const buffer = await Packer.toBuffer(doc)
|
||||
|
||||
const filePath = dialog.showSaveDialogSync({
|
||||
title: '保存文件',
|
||||
filters: [{ name: 'Word Document', extensions: ['docx'] }],
|
||||
title: t('dialog.save_file'),
|
||||
filters: [{ name: t('dialog.word_document'), extensions: ['docx'] }],
|
||||
defaultPath: fileName
|
||||
})
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ import {
|
||||
readTextFileWithAutoEncoding,
|
||||
scanDir
|
||||
} from '@main/utils/file'
|
||||
import { t } from '@main/utils/locales'
|
||||
import { documentExts, imageExts, KB, MB } from '@shared/config/constant'
|
||||
import { parseDataUrl } from '@shared/utils'
|
||||
import type { FileMetadata, NotesTreeNode } from '@types'
|
||||
@ -821,9 +822,9 @@ class FileStorage {
|
||||
): Promise<{ fileName: string; filePath: string; content?: Buffer; size: number } | null> => {
|
||||
try {
|
||||
const result: OpenDialogReturnValue = await dialog.showOpenDialog({
|
||||
title: '打开文件',
|
||||
title: t('dialog.open_file'),
|
||||
properties: ['openFile'],
|
||||
filters: [{ name: '所有文件', extensions: ['*'] }],
|
||||
filters: [{ name: t('dialog.all_files'), extensions: ['*'] }],
|
||||
...options
|
||||
})
|
||||
|
||||
@ -1437,7 +1438,7 @@ class FileStorage {
|
||||
): Promise<string> => {
|
||||
try {
|
||||
const result: SaveDialogReturnValue = await dialog.showSaveDialog({
|
||||
title: '保存文件',
|
||||
title: t('dialog.save_file'),
|
||||
defaultPath: fileName,
|
||||
...options
|
||||
})
|
||||
@ -1461,7 +1462,7 @@ class FileStorage {
|
||||
try {
|
||||
const filePath = dialog.showSaveDialogSync({
|
||||
defaultPath: `${name}.png`,
|
||||
filters: [{ name: 'PNG Image', extensions: ['png'] }]
|
||||
filters: [{ name: t('dialog.png_image'), extensions: ['png'] }]
|
||||
})
|
||||
|
||||
if (filePath) {
|
||||
@ -1476,7 +1477,7 @@ class FileStorage {
|
||||
public selectFolder = async (_: Electron.IpcMainInvokeEvent, options: OpenDialogOptions): Promise<string | null> => {
|
||||
try {
|
||||
const result: OpenDialogReturnValue = await dialog.showOpenDialog({
|
||||
title: '选择文件夹',
|
||||
title: t('dialog.select_folder'),
|
||||
properties: ['openDirectory'],
|
||||
...options
|
||||
})
|
||||
|
||||
@ -36,7 +36,7 @@ import type { MCPProgressEvent } from '@shared/config/types'
|
||||
import type { MCPServerLogEntry } from '@shared/config/types'
|
||||
import { IpcChannel } from '@shared/IpcChannel'
|
||||
import { buildFunctionCallToolName } from '@shared/mcp'
|
||||
import { defaultAppHeaders } from '@shared/utils'
|
||||
import { defaultAppHeaders, mergeHeaders } from '@shared/utils'
|
||||
import {
|
||||
BuiltinMCPServerNames,
|
||||
type GetResourceResponse,
|
||||
@ -285,10 +285,7 @@ class McpService {
|
||||
}
|
||||
|
||||
const prepareHeaders = () => {
|
||||
return {
|
||||
...defaultAppHeaders(),
|
||||
...server.headers
|
||||
}
|
||||
return mergeHeaders(defaultAppHeaders(), server.headers)
|
||||
}
|
||||
|
||||
// Create a promise for the initialization process
|
||||
@ -320,10 +317,9 @@ class McpService {
|
||||
return net.fetch(typeof url === 'string' ? url : url.toString(), init)
|
||||
},
|
||||
requestInit: {
|
||||
headers: {
|
||||
...defaultAppHeaders(),
|
||||
headers: mergeHeaders(defaultAppHeaders(), {
|
||||
APP: 'Cherry Studio'
|
||||
}
|
||||
})
|
||||
},
|
||||
authProvider
|
||||
}
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
import { t } from '@main/utils/locales'
|
||||
import { IpcChannel } from '@shared/IpcChannel'
|
||||
import { app, dialog, session, shell, webContents } from 'electron'
|
||||
import { promises as fs } from 'fs'
|
||||
|
||||
import { configManager } from './ConfigManager'
|
||||
|
||||
/**
|
||||
* init the useragent of the webview session
|
||||
* remove the CherryStudio and Electron from the useragent
|
||||
@ -13,9 +16,11 @@ export function initSessionUserAgent() {
|
||||
|
||||
wvSession.setUserAgent(newUA)
|
||||
wvSession.webRequest.onBeforeSendHeaders((details, cb) => {
|
||||
const language = configManager.getLanguage()
|
||||
const headers = {
|
||||
...details.requestHeaders,
|
||||
'User-Agent': details.url.includes('google.com') ? originUA : newUA
|
||||
'User-Agent': details.url.includes('google.com') ? originUA : newUA,
|
||||
'Accept-Language': `${language}, en;q=0.9, *;q=0.5`
|
||||
}
|
||||
cb({ requestHeaders: headers })
|
||||
})
|
||||
@ -137,9 +142,9 @@ export async function printWebviewToPDF(webviewId: number): Promise<string | nul
|
||||
|
||||
// Show save dialog
|
||||
const { canceled, filePath } = await dialog.showSaveDialog({
|
||||
title: 'Save as PDF',
|
||||
title: t('dialog.save_as_pdf'),
|
||||
defaultPath: defaultFilename,
|
||||
filters: [{ name: 'PDF Files', extensions: ['pdf'] }]
|
||||
filters: [{ name: t('dialog.pdf_files'), extensions: ['pdf'] }]
|
||||
})
|
||||
|
||||
if (canceled || !filePath) {
|
||||
@ -186,11 +191,11 @@ export async function saveWebviewAsHTML(webviewId: number): Promise<string | nul
|
||||
|
||||
// Show save dialog
|
||||
const { canceled, filePath } = await dialog.showSaveDialog({
|
||||
title: 'Save as HTML',
|
||||
title: t('dialog.save_as_html'),
|
||||
defaultPath: defaultFilename,
|
||||
filters: [
|
||||
{ name: 'HTML Files', extensions: ['html', 'htm'] },
|
||||
{ name: 'All Files', extensions: ['*'] }
|
||||
{ name: t('dialog.html_files'), extensions: ['html', 'htm'] },
|
||||
{ name: t('dialog.all_files'), extensions: ['*'] }
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
@ -1,17 +1,19 @@
|
||||
// just import the themeService to ensure the theme is initialized
|
||||
import './ThemeService'
|
||||
|
||||
import { normalizeHeaders, withUserAgentSuffix } from '@ai-sdk/provider-utils'
|
||||
import { is } from '@electron-toolkit/utils'
|
||||
import { loggerService } from '@logger'
|
||||
import { isDev, isLinux, isMac, isWin } from '@main/constant'
|
||||
import { getFilesDir } from '@main/utils/file'
|
||||
import { generateUserAgent } from '@main/utils/systemInfo'
|
||||
import { MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH } from '@shared/config/constant'
|
||||
import { IpcChannel } from '@shared/IpcChannel'
|
||||
import { app, BrowserWindow, nativeTheme, screen, shell } from 'electron'
|
||||
import { app, BrowserWindow, nativeImage, nativeTheme, screen, shell } from 'electron'
|
||||
import windowStateKeeper from 'electron-window-state'
|
||||
import { join } from 'path'
|
||||
|
||||
import icon from '../../../build/icon.png?asset'
|
||||
import iconPath from '../../../build/icon.png?asset'
|
||||
import { titleBarOverlayDark, titleBarOverlayLight } from '../config'
|
||||
import { configManager } from './ConfigManager'
|
||||
import { contextMenu } from './ContextMenu'
|
||||
@ -23,6 +25,9 @@ const DEFAULT_MINIWINDOW_HEIGHT = 400
|
||||
// const logger = loggerService.withContext('WindowService')
|
||||
const logger = loggerService.withContext('WindowService')
|
||||
|
||||
// Create nativeImage for Linux window icon (required for Wayland)
|
||||
const linuxIcon = isLinux ? nativeImage.createFromPath(iconPath) : undefined
|
||||
|
||||
export class WindowService {
|
||||
private static instance: WindowService | null = null
|
||||
private mainWindow: BrowserWindow | null = null
|
||||
@ -75,11 +80,12 @@ export class WindowService {
|
||||
trafficLightPosition: { x: 8, y: 13 }
|
||||
}
|
||||
: {
|
||||
frame: false // Frameless window for Windows and Linux
|
||||
// On Linux, allow using system title bar if setting is enabled
|
||||
frame: isLinux && configManager.getUseSystemTitleBar() ? true : false
|
||||
}),
|
||||
backgroundColor: isMac ? undefined : nativeTheme.shouldUseDarkColors ? '#181818' : '#FFFFFF',
|
||||
darkTheme: nativeTheme.shouldUseDarkColors,
|
||||
...(isLinux ? { icon } : {}),
|
||||
...(isLinux ? { icon: linuxIcon } : {}),
|
||||
webPreferences: {
|
||||
preload: join(__dirname, '../preload/index.js'),
|
||||
sandbox: false,
|
||||
@ -312,7 +318,23 @@ export class WindowService {
|
||||
}
|
||||
|
||||
private setupWebRequestHeaders(mainWindow: BrowserWindow) {
|
||||
mainWindow.webContents.session.webRequest.onHeadersReceived({ urls: ['*://*/*'] }, (details, callback) => {
|
||||
const webSession = mainWindow.webContents.session
|
||||
|
||||
// 拦截请求头,将 x-custom-user-agent 转换为 User-Agent
|
||||
// 这是因为 User-Agent 在 renderer 进程的 Fetch API 中是 forbidden header,无法直接设置
|
||||
webSession.webRequest.onBeforeSendHeaders({ urls: ['*://*/*'] }, (details, callback) => {
|
||||
const requestHeaders = normalizeHeaders(details.requestHeaders)
|
||||
|
||||
const customUA = requestHeaders['x-custom-user-agent']
|
||||
if (customUA) {
|
||||
requestHeaders['User-Agent'] = customUA
|
||||
delete requestHeaders['x-custom-user-agent']
|
||||
}
|
||||
|
||||
callback({ requestHeaders: withUserAgentSuffix(requestHeaders, generateUserAgent()) })
|
||||
})
|
||||
|
||||
webSession.webRequest.onHeadersReceived({ urls: ['*://*/*'] }, (details, callback) => {
|
||||
if (details.responseHeaders?.['X-Frame-Options']) {
|
||||
delete details.responseHeaders['X-Frame-Options']
|
||||
}
|
||||
@ -415,6 +437,23 @@ export class WindowService {
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* [Linux] Special handling for window activation
|
||||
* When the window is visible but covered by other windows, simply calling show() and focus()
|
||||
* is not enough to bring it to the front. We need to hide it first, then show it again.
|
||||
* This mimics the "close to tray and reopen" behavior which works correctly.
|
||||
*/
|
||||
if (isLinux && this.mainWindow.isVisible() && !this.mainWindow.isFocused()) {
|
||||
this.mainWindow.hide()
|
||||
setImmediate(() => {
|
||||
if (this.mainWindow && !this.mainWindow.isDestroyed()) {
|
||||
this.mainWindow.show()
|
||||
this.mainWindow.focus()
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* About setVisibleOnAllWorkspaces
|
||||
*
|
||||
|
||||
@ -1,6 +1,15 @@
|
||||
import type { MCPServer, MCPTool } from '@types'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.mock('os', () => ({
|
||||
default: {
|
||||
release: vi.fn(() => '10.0.0'),
|
||||
homedir: vi.fn(() => '/home/test')
|
||||
},
|
||||
release: vi.fn(() => '10.0.0'),
|
||||
homedir: vi.fn(() => '/home/test')
|
||||
}))
|
||||
|
||||
vi.mock('@main/apiServer/utils/mcp', () => ({
|
||||
getMCPServersFromRedux: vi.fn()
|
||||
}))
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { configManager } from '@main/services/ConfigManager'
|
||||
|
||||
import EnUs from '../../renderer/src/i18n/locales/en-us.json'
|
||||
import ZhCn from '../../renderer/src/i18n/locales/zh-cn.json'
|
||||
import ZhTw from '../../renderer/src/i18n/locales/zh-tw.json'
|
||||
@ -27,4 +29,21 @@ const locales = Object.fromEntries(
|
||||
].map(([locale, translation]) => [locale, { translation }])
|
||||
)
|
||||
|
||||
export { locales }
|
||||
/**
|
||||
* Get translation by key path (e.g., 'dialog.save_file')
|
||||
* This is a simplified version for main process, similar to i18next's t() function
|
||||
*/
|
||||
const t = (key: string): string => {
|
||||
const locale = locales[configManager.getLanguage()]
|
||||
const keys = key.split('.')
|
||||
let result: any = locale.translation
|
||||
for (const k of keys) {
|
||||
result = result?.[k]
|
||||
if (result === undefined) {
|
||||
return key
|
||||
}
|
||||
}
|
||||
return typeof result === 'string' ? result : key
|
||||
}
|
||||
|
||||
export { locales, t }
|
||||
|
||||
@ -503,6 +503,7 @@ const api = {
|
||||
quoteToMainWindow: (text: string) => ipcRenderer.invoke(IpcChannel.App_QuoteToMain, text),
|
||||
setDisableHardwareAcceleration: (isDisable: boolean) =>
|
||||
ipcRenderer.invoke(IpcChannel.App_SetDisableHardwareAcceleration, isDisable),
|
||||
setUseSystemTitleBar: (isActive: boolean) => ipcRenderer.invoke(IpcChannel.App_SetUseSystemTitleBar, isActive),
|
||||
trace: {
|
||||
saveData: (topicId: string) => ipcRenderer.invoke(IpcChannel.TRACE_SAVE_DATA, topicId),
|
||||
getData: (topicId: string, traceId: string, modelName?: string) =>
|
||||
|
||||
@ -174,8 +174,9 @@ describe('Copilot responses routing', () => {
|
||||
const config = providerToAiSdkConfig(provider, createModel('gpt-5-codex', 'GPT-5-CODEX'))
|
||||
|
||||
expect(config.providerId).toBe('github-copilot-openai-compatible')
|
||||
expect(config.options.headers?.['Editor-Version']).toBe(COPILOT_EDITOR_VERSION)
|
||||
expect(config.options.headers?.['Copilot-Integration-Id']).toBe(COPILOT_DEFAULT_HEADERS['Copilot-Integration-Id'])
|
||||
// Headers are normalized to lowercase by mergeHeaders
|
||||
expect(config.options.headers?.['editor-version']).toBe(COPILOT_EDITOR_VERSION)
|
||||
expect(config.options.headers?.['copilot-integration-id']).toBe(COPILOT_DEFAULT_HEADERS['Copilot-Integration-Id'])
|
||||
expect(config.options.headers?.['copilot-vision-request']).toBe('true')
|
||||
})
|
||||
|
||||
@ -184,8 +185,9 @@ describe('Copilot responses routing', () => {
|
||||
const config = providerToAiSdkConfig(provider, createModel('gpt-4'))
|
||||
|
||||
expect(config.providerId).toBe('github-copilot-openai-compatible')
|
||||
expect(config.options.headers?.['Editor-Version']).toBe(COPILOT_DEFAULT_HEADERS['Editor-Version'])
|
||||
expect(config.options.headers?.['Copilot-Integration-Id']).toBe(COPILOT_DEFAULT_HEADERS['Copilot-Integration-Id'])
|
||||
// Headers are normalized to lowercase by mergeHeaders
|
||||
expect(config.options.headers?.['editor-version']).toBe(COPILOT_DEFAULT_HEADERS['Editor-Version'])
|
||||
expect(config.options.headers?.['copilot-integration-id']).toBe(COPILOT_DEFAULT_HEADERS['Copilot-Integration-Id'])
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -33,9 +33,10 @@ import {
|
||||
isSupportStreamOptionsProvider,
|
||||
isVertexProvider
|
||||
} from '@renderer/utils/provider'
|
||||
import { defaultAppHeaders } from '@shared/utils'
|
||||
import { cloneDeep, isEmpty } from 'lodash'
|
||||
|
||||
import { customFetch } from '../../utils/customFetch'
|
||||
import { mergeHeaders } from '../../utils/headers'
|
||||
import type { AiSdkConfig } from '../types'
|
||||
import { aihubmixProviderCreator, newApiResolverCreator, vertexAnthropicProviderCreator } from './config'
|
||||
import { azureAnthropicProviderCreator } from './config/azure-anthropic'
|
||||
@ -162,11 +163,8 @@ export function providerToAiSdkConfig(actualProvider: Provider, model: Model): A
|
||||
if (isCopilotProvider) {
|
||||
const storedHeaders = store.getState().copilot.defaultHeaders ?? {}
|
||||
const options = ProviderConfigFactory.fromProvider('github-copilot-openai-compatible', baseConfig, {
|
||||
headers: {
|
||||
...COPILOT_DEFAULT_HEADERS,
|
||||
...storedHeaders,
|
||||
...actualProvider.extra_headers
|
||||
},
|
||||
headers: mergeHeaders(COPILOT_DEFAULT_HEADERS, storedHeaders, actualProvider.extra_headers),
|
||||
fetch: customFetch,
|
||||
name: actualProvider.id,
|
||||
includeUsage
|
||||
})
|
||||
@ -182,16 +180,18 @@ export function providerToAiSdkConfig(actualProvider: Provider, model: Model): A
|
||||
providerId: 'ollama',
|
||||
options: {
|
||||
...baseConfig,
|
||||
headers: {
|
||||
...actualProvider.extra_headers,
|
||||
fetch: customFetch,
|
||||
headers: mergeHeaders(actualProvider.extra_headers ?? {}, {
|
||||
Authorization: !isEmpty(baseConfig.apiKey) ? `Bearer ${baseConfig.apiKey}` : undefined
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理OpenAI模式
|
||||
const extraOptions: any = {}
|
||||
const extraOptions: any = {
|
||||
fetch: customFetch // 使用自定义 fetch 以支持 User-Agent header
|
||||
}
|
||||
extraOptions.endpoint = endpoint
|
||||
if (actualProvider.type === 'openai-response' && !isOpenAIChatCompletionOnlyModel(model)) {
|
||||
extraOptions.mode = 'responses'
|
||||
@ -199,10 +199,8 @@ export function providerToAiSdkConfig(actualProvider: Provider, model: Model): A
|
||||
extraOptions.mode = 'chat'
|
||||
}
|
||||
|
||||
extraOptions.headers = {
|
||||
...defaultAppHeaders(),
|
||||
...actualProvider.extra_headers
|
||||
}
|
||||
// NOTE: default app header在streamText就添加好了
|
||||
extraOptions.headers = actualProvider.extra_headers
|
||||
|
||||
if (aiSdkProviderId === 'openai') {
|
||||
extraOptions.headers['X-Api-Key'] = baseConfig.apiKey
|
||||
@ -359,33 +357,24 @@ export async function prepareSpecialProviderConfig(
|
||||
switch (provider.id) {
|
||||
case 'copilot': {
|
||||
const defaultHeaders = store.getState().copilot.defaultHeaders ?? {}
|
||||
const headers = {
|
||||
...COPILOT_DEFAULT_HEADERS,
|
||||
...defaultHeaders
|
||||
}
|
||||
const headers = mergeHeaders(COPILOT_DEFAULT_HEADERS, defaultHeaders)
|
||||
const { token } = await window.api.copilot.getToken(headers)
|
||||
config.options.apiKey = token
|
||||
config.options.headers = {
|
||||
...headers,
|
||||
...config.options.headers
|
||||
}
|
||||
config.options.headers = mergeHeaders(headers, config.options.headers)
|
||||
break
|
||||
}
|
||||
case 'cherryai': {
|
||||
config.options.fetch = async (url, options) => {
|
||||
config.options.fetch = async (url: RequestInfo | URL, options: RequestInit) => {
|
||||
// 在这里对最终参数进行签名
|
||||
const signature = await window.api.cherryai.generateSignature({
|
||||
method: 'POST',
|
||||
path: '/chat/completions',
|
||||
query: '',
|
||||
body: JSON.parse(options.body)
|
||||
body: JSON.parse(options.body as string)
|
||||
})
|
||||
return fetch(url, {
|
||||
return customFetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
...options.headers,
|
||||
...signature
|
||||
}
|
||||
headers: mergeHeaders((options.headers as Record<string, string>) ?? {}, signature)
|
||||
})
|
||||
}
|
||||
break
|
||||
@ -395,12 +384,11 @@ export async function prepareSpecialProviderConfig(
|
||||
const oauthToken = await window.api.anthropic_oauth.getAccessToken()
|
||||
config.options = {
|
||||
...config.options,
|
||||
headers: {
|
||||
...(config.options.headers ? config.options.headers : {}),
|
||||
headers: mergeHeaders(config.options.headers ?? {}, {
|
||||
'Content-Type': 'application/json',
|
||||
'anthropic-version': '2023-06-01',
|
||||
Authorization: `Bearer ${oauthToken}`
|
||||
},
|
||||
}),
|
||||
baseURL: 'https://api.anthropic.com/v1',
|
||||
apiKey: ''
|
||||
}
|
||||
|
||||
@ -182,7 +182,7 @@ const HtmlArtifactsPopup: React.FC<HtmlArtifactsPopupProps> = ({ open, title, ht
|
||||
ref={previewFrameRef}
|
||||
key={html} // Force recreate iframe when preview content changes
|
||||
srcDoc={html}
|
||||
title="HTML Preview"
|
||||
title={t('common.html_preview')}
|
||||
sandbox="allow-scripts allow-same-origin allow-forms"
|
||||
/>
|
||||
) : (
|
||||
|
||||
555
src/renderer/src/components/ErrorDetailModal/index.tsx
Normal file
555
src/renderer/src/components/ErrorDetailModal/index.tsx
Normal file
@ -0,0 +1,555 @@
|
||||
import CodeViewer from '@renderer/components/CodeViewer'
|
||||
import { useCodeStyle } from '@renderer/context/CodeStyleProvider'
|
||||
import type { SerializedAiSdkError, SerializedAiSdkErrorUnion, SerializedError } from '@renderer/types/error'
|
||||
import {
|
||||
isSerializedAiSdkAPICallError,
|
||||
isSerializedAiSdkDownloadError,
|
||||
isSerializedAiSdkError,
|
||||
isSerializedAiSdkErrorUnion,
|
||||
isSerializedAiSdkInvalidArgumentError,
|
||||
isSerializedAiSdkInvalidDataContentError,
|
||||
isSerializedAiSdkInvalidMessageRoleError,
|
||||
isSerializedAiSdkInvalidPromptError,
|
||||
isSerializedAiSdkInvalidToolInputError,
|
||||
isSerializedAiSdkJSONParseError,
|
||||
isSerializedAiSdkMessageConversionError,
|
||||
isSerializedAiSdkNoObjectGeneratedError,
|
||||
isSerializedAiSdkNoSpeechGeneratedError,
|
||||
isSerializedAiSdkNoSuchModelError,
|
||||
isSerializedAiSdkNoSuchProviderError,
|
||||
isSerializedAiSdkNoSuchToolError,
|
||||
isSerializedAiSdkRetryError,
|
||||
isSerializedAiSdkToolCallRepairError,
|
||||
isSerializedAiSdkTooManyEmbeddingValuesForCallError,
|
||||
isSerializedAiSdkTypeValidationError,
|
||||
isSerializedAiSdkUnsupportedFunctionalityError,
|
||||
isSerializedError
|
||||
} from '@renderer/types/error'
|
||||
import { formatAiSdkError, formatError, safeToString } from '@renderer/utils/error'
|
||||
import { parseDataUrl } from '@shared/utils'
|
||||
import { Button } from 'antd'
|
||||
import { Modal } from 'antd'
|
||||
import React, { memo, useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
interface ErrorDetailModalProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
error?: SerializedError
|
||||
}
|
||||
|
||||
const truncateLargeData = (
|
||||
data: string,
|
||||
t: (key: string) => string
|
||||
): { content: string; truncated: boolean; isLikelyBase64: boolean } => {
|
||||
const parsed = parseDataUrl(data)
|
||||
const isLikelyBase64 = parsed?.isBase64 ?? false
|
||||
|
||||
if (!data || data.length <= 100_000) {
|
||||
return { content: data, truncated: false, isLikelyBase64 }
|
||||
}
|
||||
|
||||
if (isLikelyBase64) {
|
||||
return {
|
||||
content: `[${t('error.base64DataTruncated')}]`,
|
||||
truncated: true,
|
||||
isLikelyBase64: true
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
content: data.slice(0, 100_000) + `\n\n... [${t('error.truncated')}]`,
|
||||
truncated: true,
|
||||
isLikelyBase64: false
|
||||
}
|
||||
}
|
||||
|
||||
// --- Styled Components ---
|
||||
|
||||
const ErrorDetailContainer = styled.div`
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
`
|
||||
|
||||
const ErrorDetailList = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
`
|
||||
|
||||
const ErrorDetailItem = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
`
|
||||
|
||||
const ErrorDetailLabel = styled.div`
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
font-size: 14px;
|
||||
`
|
||||
|
||||
const ErrorDetailValue = styled.div`
|
||||
font-family: var(--code-font-family);
|
||||
font-size: 12px;
|
||||
padding: 8px;
|
||||
background: var(--color-code-background);
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--color-border);
|
||||
word-break: break-word;
|
||||
color: var(--color-text);
|
||||
`
|
||||
|
||||
const StackTrace = styled.div`
|
||||
background: var(--color-background-soft);
|
||||
border: 1px solid var(--color-error);
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
font-family: var(--code-font-family);
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: var(--color-error);
|
||||
}
|
||||
`
|
||||
|
||||
const TruncatedBadge = styled.span`
|
||||
margin-left: 8px;
|
||||
padding: 2px 6px;
|
||||
font-size: 10px;
|
||||
font-weight: normal;
|
||||
color: var(--color-warning);
|
||||
background: var(--color-warning-bg, rgba(250, 173, 20, 0.1));
|
||||
border-radius: 4px;
|
||||
`
|
||||
|
||||
// --- Sub-Components ---
|
||||
|
||||
const BuiltinError = memo(({ error }: { error: SerializedError }) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<>
|
||||
{error.name && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.name')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.name}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.message && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.message')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.message}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.stack && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.stack')}:</ErrorDetailLabel>
|
||||
<StackTrace>
|
||||
<pre>{error.stack}</pre>
|
||||
</StackTrace>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
const AiSdkErrorBase = memo(({ error }: { error: SerializedAiSdkError }) => {
|
||||
const { t } = useTranslation()
|
||||
const tRef = useRef(t)
|
||||
useEffect(() => {
|
||||
tRef.current = t
|
||||
}, [t])
|
||||
|
||||
const { highlightCode } = useCodeStyle()
|
||||
const [highlightedString, setHighlightedString] = useState('')
|
||||
const [isTruncated, setIsTruncated] = useState(false)
|
||||
const cause = error.cause
|
||||
|
||||
useEffect(() => {
|
||||
const highlight = async () => {
|
||||
try {
|
||||
const { content: truncatedCause, truncated, isLikelyBase64 } = truncateLargeData(cause || '', tRef.current)
|
||||
setIsTruncated(truncated)
|
||||
|
||||
if (isLikelyBase64) {
|
||||
setHighlightedString(truncatedCause)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(truncatedCause || '{}')
|
||||
const formatted = JSON.stringify(parsed, null, 2)
|
||||
const result = await highlightCode(formatted, 'json')
|
||||
setHighlightedString(result)
|
||||
} catch {
|
||||
setHighlightedString(truncatedCause || '')
|
||||
}
|
||||
} catch {
|
||||
setHighlightedString(cause || '')
|
||||
}
|
||||
}
|
||||
const timer = setTimeout(highlight, 0)
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [highlightCode, cause])
|
||||
|
||||
return (
|
||||
<>
|
||||
<BuiltinError error={error} />
|
||||
{cause && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>
|
||||
{t('error.cause')}:{isTruncated && <TruncatedBadge>{t('error.truncatedBadge')}</TruncatedBadge>}
|
||||
</ErrorDetailLabel>
|
||||
<ErrorDetailValue>
|
||||
<div
|
||||
className="markdown [&_pre]:!bg-transparent [&_pre_span]:whitespace-pre-wrap"
|
||||
dangerouslySetInnerHTML={{ __html: highlightedString }}
|
||||
/>
|
||||
</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
const TruncatedCodeViewer = memo(
|
||||
({ value, label, language = 'json' }: { value: string; label: string; language?: string }) => {
|
||||
const { t } = useTranslation()
|
||||
const { content, truncated, isLikelyBase64 } = truncateLargeData(value, t)
|
||||
|
||||
return (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>
|
||||
{label}:{truncated && <TruncatedBadge>{t('error.truncatedBadge')}</TruncatedBadge>}
|
||||
</ErrorDetailLabel>
|
||||
{isLikelyBase64 ? (
|
||||
<ErrorDetailValue>{content}</ErrorDetailValue>
|
||||
) : (
|
||||
<CodeViewer value={content} className="source-view" language={language} expanded />
|
||||
)}
|
||||
</ErrorDetailItem>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
const AiSdkError = memo(({ error }: { error: SerializedAiSdkErrorUnion }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<ErrorDetailList>
|
||||
{(isSerializedAiSdkAPICallError(error) || isSerializedAiSdkDownloadError(error)) && error.url && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.requestUrl')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.url}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkAPICallError(error) && error.responseBody && (
|
||||
<TruncatedCodeViewer value={error.responseBody} label={t('error.responseBody')} />
|
||||
)}
|
||||
|
||||
{(isSerializedAiSdkAPICallError(error) || isSerializedAiSdkDownloadError(error)) && error.statusCode && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.statusCode')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.statusCode}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkAPICallError(error) && (
|
||||
<>
|
||||
{error.responseHeaders && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.responseHeaders')}:</ErrorDetailLabel>
|
||||
<CodeViewer
|
||||
value={JSON.stringify(error.responseHeaders, null, 2)}
|
||||
className="source-view"
|
||||
language="json"
|
||||
expanded
|
||||
/>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{error.requestBodyValues && (
|
||||
<TruncatedCodeViewer value={safeToString(error.requestBodyValues)} label={t('error.requestBodyValues')} />
|
||||
)}
|
||||
|
||||
{error.data && <TruncatedCodeViewer value={safeToString(error.data)} label={t('error.data')} />}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkDownloadError(error) && error.statusText && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.statusText')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.statusText}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkInvalidArgumentError(error) && error.parameter && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.parameter')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.parameter}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{(isSerializedAiSdkInvalidArgumentError(error) || isSerializedAiSdkTypeValidationError(error)) && error.value && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.value')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.value)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkInvalidDataContentError(error) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.content')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.content)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkInvalidMessageRoleError(error) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.role')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.role}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkInvalidPromptError(error) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.prompt')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.prompt)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkInvalidToolInputError(error) && (
|
||||
<>
|
||||
{error.toolName && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.toolName')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.toolName}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.toolInput && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.toolInput')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.toolInput}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{(isSerializedAiSdkJSONParseError(error) || isSerializedAiSdkNoObjectGeneratedError(error)) && error.text && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.text')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.text}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkMessageConversionError(error) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.originalMessage')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.originalMessage)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkNoSpeechGeneratedError(error) && error.responses && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.responses')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.responses.join(', ')}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkNoObjectGeneratedError(error) && (
|
||||
<>
|
||||
{error.response && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.response')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.response)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.usage && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.usage')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.usage)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.finishReason && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.finishReason')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.finishReason}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{(isSerializedAiSdkNoSuchModelError(error) ||
|
||||
isSerializedAiSdkNoSuchProviderError(error) ||
|
||||
isSerializedAiSdkTooManyEmbeddingValuesForCallError(error)) &&
|
||||
error.modelId && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.modelId')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.modelId}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{(isSerializedAiSdkNoSuchModelError(error) || isSerializedAiSdkNoSuchProviderError(error)) && error.modelType && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.modelType')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.modelType}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkNoSuchProviderError(error) && (
|
||||
<>
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.providerId')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.providerId}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.availableProviders')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.availableProviders.join(', ')}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkNoSuchToolError(error) && (
|
||||
<>
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.toolName')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.toolName}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
{error.availableTools && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.availableTools')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.availableTools?.join(', ') || t('common.none')}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkRetryError(error) && (
|
||||
<>
|
||||
{error.reason && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.reason')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.reason}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.lastError && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.lastError')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.lastError)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.errors && error.errors.length > 0 && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.errors')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.errors.map((e) => safeToString(e)).join('\n\n')}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkTooManyEmbeddingValuesForCallError(error) && (
|
||||
<>
|
||||
{error.provider && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.provider')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.provider}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.maxEmbeddingsPerCall && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.maxEmbeddingsPerCall')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.maxEmbeddingsPerCall}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.values && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.values')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.values)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkToolCallRepairError(error) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.originalError')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.originalError)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkUnsupportedFunctionalityError(error) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.functionality')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.functionality}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
<AiSdkErrorBase error={error} />
|
||||
</ErrorDetailList>
|
||||
)
|
||||
})
|
||||
|
||||
// --- Main Component ---
|
||||
|
||||
const ErrorDetailModal: React.FC<ErrorDetailModalProps> = ({ open, onClose, error }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const copyErrorDetails = useCallback(() => {
|
||||
if (!error) return
|
||||
let errorText: string
|
||||
if (isSerializedAiSdkError(error)) {
|
||||
errorText = formatAiSdkError(error)
|
||||
} else if (isSerializedError(error)) {
|
||||
errorText = formatError(error)
|
||||
} else {
|
||||
errorText = safeToString(error)
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(errorText)
|
||||
window.toast.addToast({ title: t('message.copied') })
|
||||
}, [error, t])
|
||||
|
||||
const renderErrorDetails = (error?: SerializedError) => {
|
||||
if (!error) return <div>{t('error.unknown')}</div>
|
||||
if (isSerializedAiSdkErrorUnion(error)) {
|
||||
return <AiSdkError error={error} />
|
||||
}
|
||||
return (
|
||||
<ErrorDetailList>
|
||||
<BuiltinError error={error} />
|
||||
</ErrorDetailList>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
centered
|
||||
title={t('error.detail')}
|
||||
open={open}
|
||||
onCancel={onClose}
|
||||
footer={[
|
||||
<Button key="copy" variant="text" color="default" onClick={copyErrorDetails}>
|
||||
{t('common.copy')}
|
||||
</Button>,
|
||||
<Button key="close" variant="text" color="default" onClick={onClose}>
|
||||
{t('common.close')}
|
||||
</Button>
|
||||
]}
|
||||
width="80%"
|
||||
style={{ maxWidth: '1200px', minWidth: '600px' }}>
|
||||
<ErrorDetailContainer>{renderErrorDetails(error)}</ErrorDetailContainer>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export { ErrorDetailModal }
|
||||
export default ErrorDetailModal
|
||||
export type { ErrorDetailModalProps }
|
||||
@ -3,6 +3,7 @@ import NavigationService from '@renderer/services/NavigationService'
|
||||
import type { Model } from '@renderer/types'
|
||||
import { ArrowUpRight } from 'lucide-react'
|
||||
import type { FC, MouseEvent } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import IndicatorLight from './IndicatorLight'
|
||||
@ -15,6 +16,8 @@ interface Props {
|
||||
}
|
||||
|
||||
export const FreeTrialModelTag: FC<Props> = ({ model, showLabel = true }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
if (model.provider !== 'cherryai') {
|
||||
return null
|
||||
}
|
||||
@ -57,7 +60,7 @@ export const FreeTrialModelTag: FC<Props> = ({ model, showLabel = true }) => {
|
||||
return (
|
||||
<Container>
|
||||
<IndicatorLight size={6} color="var(--color-primary)" animation={false} shadow={false} />
|
||||
<PoweredBy>Powered by </PoweredBy>
|
||||
<PoweredBy>{t('common.powered_by')}</PoweredBy>
|
||||
<LinkText onClick={onSelectProvider}>{getProviderLabel(providerId)}</LinkText>
|
||||
</Container>
|
||||
)
|
||||
|
||||
@ -1,27 +1,38 @@
|
||||
import { CheckCircleFilled, CloseCircleFilled, ExclamationCircleFilled, LoadingOutlined } from '@ant-design/icons'
|
||||
import { HealthStatus } from '@renderer/types/healthCheck'
|
||||
import { Flex, Tooltip, Typography } from 'antd'
|
||||
import React, { memo } from 'react'
|
||||
import React, { memo, useCallback } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import type { HealthResult } from './types'
|
||||
import { useHealthStatus } from './useHealthStatus'
|
||||
|
||||
export interface HealthStatusIndicatorProps {
|
||||
interface HealthStatusIndicatorProps {
|
||||
results: HealthResult[]
|
||||
loading?: boolean
|
||||
showLatency?: boolean
|
||||
onErrorClick?: (result: HealthResult) => void
|
||||
}
|
||||
|
||||
const HealthStatusIndicator: React.FC<HealthStatusIndicatorProps> = ({
|
||||
results,
|
||||
loading = false,
|
||||
showLatency = false
|
||||
showLatency = false,
|
||||
onErrorClick
|
||||
}) => {
|
||||
const { overallStatus, tooltip, latencyText } = useHealthStatus({
|
||||
results,
|
||||
showLatency
|
||||
})
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
if (!onErrorClick) return
|
||||
const failedResult = results.find((r) => r.status === HealthStatus.FAILED)
|
||||
if (failedResult) {
|
||||
onErrorClick(failedResult)
|
||||
}
|
||||
}, [onErrorClick, results])
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<IndicatorWrapper $type="checking">
|
||||
@ -32,17 +43,19 @@ const HealthStatusIndicator: React.FC<HealthStatusIndicatorProps> = ({
|
||||
|
||||
if (overallStatus === 'not_checked') return null
|
||||
|
||||
const isClickable = onErrorClick && results.some((r) => r.status === HealthStatus.FAILED)
|
||||
|
||||
let icon: React.ReactNode = null
|
||||
switch (overallStatus) {
|
||||
case 'success':
|
||||
icon = <CheckCircleFilled />
|
||||
break
|
||||
case 'error':
|
||||
icon = <CloseCircleFilled />
|
||||
break
|
||||
case 'partial':
|
||||
icon = <ExclamationCircleFilled />
|
||||
case 'partial': {
|
||||
const IconComponent = overallStatus === 'error' ? CloseCircleFilled : ExclamationCircleFilled
|
||||
icon = <IconComponent />
|
||||
break
|
||||
}
|
||||
default:
|
||||
return null
|
||||
}
|
||||
@ -51,19 +64,25 @@ const HealthStatusIndicator: React.FC<HealthStatusIndicatorProps> = ({
|
||||
<Flex align="center" gap={6}>
|
||||
{latencyText && <LatencyText type="secondary">{latencyText}</LatencyText>}
|
||||
<Tooltip title={tooltip} styles={{ body: { userSelect: 'text' } }}>
|
||||
<IndicatorWrapper $type={overallStatus}>{icon}</IndicatorWrapper>
|
||||
<IndicatorWrapper
|
||||
$type={overallStatus}
|
||||
$clickable={isClickable}
|
||||
onClick={isClickable ? handleClick : undefined}>
|
||||
{icon}
|
||||
</IndicatorWrapper>
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
const IndicatorWrapper = styled.div<{ $type: string }>`
|
||||
const IndicatorWrapper = styled.div<{ $type: string; $clickable?: boolean }>`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
color: ${(props) => {
|
||||
switch (props.$type) {
|
||||
cursor: ${({ $clickable }) => ($clickable ? 'pointer' : 'auto')};
|
||||
color: ${({ $type }) => {
|
||||
switch ($type) {
|
||||
case 'success':
|
||||
return 'var(--color-status-success)'
|
||||
case 'error':
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import type { SerializedError } from '@renderer/types/error'
|
||||
import type { HealthStatus } from '@renderer/types/healthCheck'
|
||||
|
||||
/**
|
||||
@ -6,7 +7,7 @@ import type { HealthStatus } from '@renderer/types/healthCheck'
|
||||
export interface HealthResult {
|
||||
status: HealthStatus
|
||||
latency?: number
|
||||
error?: string
|
||||
error?: SerializedError
|
||||
// 用于在 Tooltip 中显示额外上下文信息,例如 API Key 或模型名称
|
||||
label?: string
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ export const useHealthStatus = ({ results, showLatency = false }: UseHealthStatu
|
||||
|
||||
return (
|
||||
<li key={idx} style={{ marginBottom: idx === results.length - 1 ? 0 : '10px' }}>
|
||||
<Flex align="center" justify="space-between">
|
||||
<Flex align="flex-start" gap={5}>
|
||||
<strong style={{ color: statusColor }}>{statusText}</strong>
|
||||
{result.label}
|
||||
</Flex>
|
||||
@ -86,8 +86,8 @@ export const useHealthStatus = ({ results, showLatency = false }: UseHealthStatu
|
||||
{t('settings.provider.api.key.check.latency')}: {formatLatency(result.latency)}
|
||||
</div>
|
||||
)}
|
||||
{result.error && result.status === HealthStatus.FAILED && (
|
||||
<div style={{ marginTop: 2 }}>{result.error}</div>
|
||||
{result.status === HealthStatus.FAILED && result.error?.message && (
|
||||
<div style={{ marginTop: 2 }}>{result.error.message}</div>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
|
||||
@ -77,7 +77,7 @@ const InputEmbeddingDimension = ({
|
||||
<Tooltip title={t('knowledge.dimensions_auto_set')}>
|
||||
<Button
|
||||
role="button"
|
||||
aria-label="Get embedding dimension"
|
||||
aria-label={t('common.get_embedding_dimension')}
|
||||
disabled={disabled || loading}
|
||||
onClick={handleFetchDimension}
|
||||
icon={<RefreshIcon size={16} className={loading ? 'animation-rotate' : ''} />}
|
||||
|
||||
@ -8,7 +8,7 @@ import { isPreprocessProviderId, isWebSearchProviderId } from '@renderer/types'
|
||||
import type { ApiKeyConnectivity, ApiKeyWithStatus } from '@renderer/types/healthCheck'
|
||||
import { HealthStatus } from '@renderer/types/healthCheck'
|
||||
import { formatApiKeys, splitApiKeyString } from '@renderer/utils/api'
|
||||
import { formatErrorMessage } from '@renderer/utils/error'
|
||||
import { serializeHealthCheckError } from '@renderer/utils/error'
|
||||
import type { TFunction } from 'i18next'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
@ -218,17 +218,19 @@ export function useApiKeys({ provider, updateProvider }: UseApiKeysProps) {
|
||||
latency,
|
||||
error: undefined
|
||||
})
|
||||
} catch (error: any) {
|
||||
} catch (error: unknown) {
|
||||
// 连通性检查失败
|
||||
const serializedError = serializeHealthCheckError(error)
|
||||
|
||||
updateConnectivityState(keyToCheck, {
|
||||
checking: false,
|
||||
status: HealthStatus.FAILED,
|
||||
error: formatErrorMessage(error),
|
||||
error: serializedError,
|
||||
model: undefined,
|
||||
latency: undefined
|
||||
})
|
||||
|
||||
logger.error('failed to validate the connectivity of the api key', error)
|
||||
logger.error('failed to validate the connectivity of the api key', error as Error)
|
||||
}
|
||||
},
|
||||
[keys, connectivityStates, updateConnectivityState, provider]
|
||||
|
||||
@ -151,7 +151,7 @@ const PopupContainer: React.FC<Props> = ({
|
||||
{showTranslate && (
|
||||
<TranslateButton
|
||||
onClick={handleTranslate}
|
||||
aria-label="Translate text"
|
||||
aria-label={t('common.translate_text')}
|
||||
disabled={isTranslating || !textValue.trim()}>
|
||||
{isTranslating ? <LoadingOutlined spin /> : <Languages size={16} />}
|
||||
</TranslateButton>
|
||||
|
||||
@ -4,9 +4,11 @@ import { NodeViewContent, NodeViewWrapper, type ReactNodeViewProps, ReactNodeVie
|
||||
import { Button, Select, Tooltip } from 'antd'
|
||||
import type { FC } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const CodeBlockNodeView: FC<ReactNodeViewProps> = (props) => {
|
||||
const { node, updateAttributes } = props
|
||||
const { t } = useTranslation()
|
||||
const [languageOptions, setLanguageOptions] = useState<string[]>(DEFAULT_LANGUAGES)
|
||||
|
||||
// Detect language from node attrs or fallback
|
||||
@ -65,7 +67,7 @@ const CodeBlockNodeView: FC<ReactNodeViewProps> = (props) => {
|
||||
options={languageOptions.map((lang) => ({ value: lang, label: lang }))}
|
||||
style={{ minWidth: 90 }}
|
||||
/>
|
||||
<Tooltip title="Copy">
|
||||
<Tooltip title={t('common.copy')}>
|
||||
<Button
|
||||
size="small"
|
||||
type="text"
|
||||
|
||||
@ -2,12 +2,13 @@ import { PlusOutlined } from '@ant-design/icons'
|
||||
import { loggerService } from '@logger'
|
||||
import { Sortable, useDndReorder } from '@renderer/components/dnd'
|
||||
import HorizontalScrollContainer from '@renderer/components/HorizontalScrollContainer'
|
||||
import { isMac } from '@renderer/config/constant'
|
||||
import { isLinux, isMac } from '@renderer/config/constant'
|
||||
import { DEFAULT_MIN_APPS } from '@renderer/config/minapps'
|
||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
import { useFullscreen } from '@renderer/hooks/useFullscreen'
|
||||
import { useMinappPopup } from '@renderer/hooks/useMinappPopup'
|
||||
import { useMinapps } from '@renderer/hooks/useMinapps'
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { getThemeModeLabel, getTitleLabel } from '@renderer/i18n/label'
|
||||
import tabsService from '@renderer/services/TabsService'
|
||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||
@ -122,6 +123,7 @@ const TabsContainer: React.FC<TabsContainerProps> = ({ children }) => {
|
||||
const { settedTheme, toggleTheme } = useTheme()
|
||||
const { hideMinappPopup, minAppsCache } = useMinappPopup()
|
||||
const { minapps } = useMinapps()
|
||||
const { useSystemTitleBar } = useSettings()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const getTabId = (path: string): string => {
|
||||
@ -268,7 +270,7 @@ const TabsContainer: React.FC<TabsContainerProps> = ({ children }) => {
|
||||
<PlusOutlined />
|
||||
</AddTabButton>
|
||||
</HorizontalScrollContainer>
|
||||
<RightButtonsContainer>
|
||||
<RightButtonsContainer style={{ paddingRight: isLinux && useSystemTitleBar ? '12px' : undefined }}>
|
||||
<Tooltip
|
||||
title={t('settings.theme.title') + ': ' + getThemeModeLabel(settedTheme)}
|
||||
mouseEnterDelay={0.8}
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { lightbulbVariants } from '@renderer/utils/motionVariants'
|
||||
import { isEqual } from 'lodash'
|
||||
import { ChevronRight, Lightbulb } from 'lucide-react'
|
||||
import { motion } from 'motion/react'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import React, { useMemo } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
interface Props {
|
||||
@ -13,17 +12,11 @@ interface Props {
|
||||
}
|
||||
|
||||
const ThinkingEffect: React.FC<Props> = ({ isThinking, thinkingTimeText, content, expanded }) => {
|
||||
const [messages, setMessages] = useState<string[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
const messages = useMemo(() => {
|
||||
const allLines = (content || '').split('\n')
|
||||
const newMessages = isThinking ? allLines.slice(0, -1) : allLines
|
||||
const validMessages = newMessages.filter((line) => line.trim() !== '')
|
||||
|
||||
if (!isEqual(messages, validMessages)) {
|
||||
setMessages(validMessages)
|
||||
}
|
||||
}, [content, isThinking, messages])
|
||||
return newMessages.filter((line) => line.trim() !== '')
|
||||
}, [content, isThinking])
|
||||
|
||||
const showThinking = useMemo(() => {
|
||||
return isThinking && !expanded
|
||||
|
||||
@ -14,7 +14,6 @@ exports[`DynamicVirtualList > basic rendering > snapshot test 1`] = `
|
||||
<div>
|
||||
<div
|
||||
aria-hidden="false"
|
||||
aria-label="Dynamic Virtual List"
|
||||
class="c0 dynamic-virtual-list"
|
||||
role="region"
|
||||
style="overflow: auto; height: 100%;"
|
||||
|
||||
@ -243,7 +243,6 @@ function DynamicVirtualList<T>(props: DynamicVirtualListProps<T>) {
|
||||
ref={scrollerRef}
|
||||
className={className ? `dynamic-virtual-list ${className}` : 'dynamic-virtual-list'}
|
||||
role="region"
|
||||
aria-label="Dynamic Virtual List"
|
||||
aria-hidden={!showScrollbar}
|
||||
$autoHide={autoHideScrollbar}
|
||||
$show={showScrollbar}
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const WindowControlsContainer = styled.div`
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: var(--navbar-height);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { isLinux, isWin } from '@renderer/config/constant'
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { Tooltip } from 'antd'
|
||||
import { Minus, Square, X } from 'lucide-react'
|
||||
import type { SVGProps } from 'react'
|
||||
@ -49,6 +50,7 @@ const DEFAULT_DELAY = 1
|
||||
const WindowControls: React.FC = () => {
|
||||
const [isMaximized, setIsMaximized] = useState(false)
|
||||
const { t } = useTranslation()
|
||||
const { useSystemTitleBar } = useSettings()
|
||||
|
||||
useEffect(() => {
|
||||
// Check initial maximized state
|
||||
@ -67,6 +69,11 @@ const WindowControls: React.FC = () => {
|
||||
return null
|
||||
}
|
||||
|
||||
// Hide on Linux if using system title bar
|
||||
if (isLinux && useSystemTitleBar) {
|
||||
return null
|
||||
}
|
||||
|
||||
const handleMinimize = () => {
|
||||
window.api.windowControls.minimize()
|
||||
}
|
||||
|
||||
@ -16,7 +16,8 @@ const mocks = vi.hoisted(() => ({
|
||||
'knowledge.provider_not_found': '找不到提供商',
|
||||
'message.error.get_embedding_dimensions': '获取嵌入维度失败',
|
||||
'knowledge.dimensions_size_placeholder': '请输入维度大小',
|
||||
'knowledge.dimensions_auto_set': '自动设置维度'
|
||||
'knowledge.dimensions_auto_set': '自动设置维度',
|
||||
'common.get_embedding_dimension': 'Get Embedding Dimension'
|
||||
}
|
||||
return translations[k] || k
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ exports[`InputEmbeddingDimension > basic rendering > should match snapshot with
|
||||
data-title="自动设置维度"
|
||||
>
|
||||
<button
|
||||
aria-label="Get embedding dimension"
|
||||
aria-label="Get Embedding Dimension"
|
||||
role="button"
|
||||
type="button"
|
||||
>
|
||||
@ -52,7 +52,7 @@ exports[`InputEmbeddingDimension > basic rendering > should match snapshot with
|
||||
data-title="自动设置维度"
|
||||
>
|
||||
<button
|
||||
aria-label="Get embedding dimension"
|
||||
aria-label="Get Embedding Dimension"
|
||||
disabled=""
|
||||
role="button"
|
||||
type="button"
|
||||
|
||||
@ -22,12 +22,10 @@ export const Navbar: FC<Props> = ({ children, ...props }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<NavbarContainer {...props} style={{ backgroundColor }} $isFullScreen={isFullscreen}>
|
||||
{children}
|
||||
</NavbarContainer>
|
||||
{!isTopNavbar && !minappShow && <WindowControls />}
|
||||
</>
|
||||
<NavbarContainer {...props} style={{ backgroundColor }} $isFullScreen={isFullscreen}>
|
||||
{children}
|
||||
{!minappShow && <WindowControls />}
|
||||
</NavbarContainer>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -743,6 +743,12 @@ export const SYSTEM_MODELS: Record<SystemProviderId | 'defaultModel', Model[]> =
|
||||
provider: 'baichuan',
|
||||
name: 'Baichuan M3',
|
||||
group: 'Baichuan-M3'
|
||||
},
|
||||
{
|
||||
id: 'Baichuan-M3-Plus',
|
||||
provider: 'baichuan',
|
||||
name: 'Baichuan M3 Plus',
|
||||
group: 'Baichuan-M3'
|
||||
}
|
||||
],
|
||||
modelscope: [
|
||||
|
||||
@ -646,8 +646,8 @@ export const isBaichuanReasoningModel = (model?: Model): boolean => {
|
||||
}
|
||||
const modelId = getLowerBaseModelName(model.id, '/')
|
||||
|
||||
// Baichuan-M2 和 Baichuan-M3 是推理模型(注意:M2-Plus 不是推理模型)
|
||||
return (modelId.includes('baichuan-m2') && !modelId.includes('plus')) || modelId.includes('baichuan-m3')
|
||||
// Baichuan-M2 和 Baichuan-M3 是推理模型
|
||||
return modelId === 'baichuan-m2' || modelId === 'baichuan-m3'
|
||||
}
|
||||
|
||||
export function isReasoningModel(model?: Model): boolean {
|
||||
|
||||
@ -3,7 +3,7 @@ import { isMac } from '@renderer/config/constant'
|
||||
import { isLocalAi } from '@renderer/config/env'
|
||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
import db from '@renderer/databases'
|
||||
import i18n from '@renderer/i18n'
|
||||
import i18n, { setDayjsLocale } from '@renderer/i18n'
|
||||
import KnowledgeQueue from '@renderer/queue/KnowledgeQueue'
|
||||
import MemoryService from '@renderer/services/MemoryService'
|
||||
import { useAppDispatch } from '@renderer/store'
|
||||
@ -122,7 +122,9 @@ export function useAppInit() {
|
||||
}, [proxyUrl, proxyMode, proxyBypassRules])
|
||||
|
||||
useEffect(() => {
|
||||
i18n.changeLanguage(language || navigator.language || defaultLanguage)
|
||||
const currentLanguage = language || navigator.language || defaultLanguage
|
||||
i18n.changeLanguage(currentLanguage)
|
||||
setDayjsLocale(currentLanguage)
|
||||
}, [language])
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { createSelector } from '@reduxjs/toolkit'
|
||||
import { CHERRYAI_PROVIDER } from '@renderer/config/providers'
|
||||
import { getDefaultProvider } from '@renderer/services/AssistantService'
|
||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||
import { type RootState, useAppDispatch, useAppSelector } from '@renderer/store'
|
||||
import {
|
||||
addModel,
|
||||
addProvider,
|
||||
@ -13,12 +13,43 @@ import {
|
||||
} from '@renderer/store/llm'
|
||||
import type { Assistant, Model, Provider } from '@renderer/types'
|
||||
import { isSystemProvider } from '@renderer/types'
|
||||
import { withoutTrailingSlash } from '@renderer/utils/api'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { useDefaultModel } from './useAssistant'
|
||||
|
||||
const selectEnabledProviders = createSelector(
|
||||
(state) => state.llm.providers,
|
||||
(providers) => providers.filter((p) => p.enabled).concat(CHERRYAI_PROVIDER)
|
||||
/**
|
||||
* Normalizes provider apiHost by removing trailing slashes.
|
||||
* This ensures consistent URL concatenation across the application.
|
||||
*/
|
||||
function normalizeProvider<T extends Provider>(provider: T): T {
|
||||
return {
|
||||
...provider,
|
||||
apiHost: withoutTrailingSlash(provider.apiHost)
|
||||
}
|
||||
}
|
||||
|
||||
const selectProviders = (state: RootState) => state.llm.providers
|
||||
|
||||
const selectEnabledProviders = createSelector(selectProviders, (providers) =>
|
||||
providers
|
||||
.map(normalizeProvider)
|
||||
.filter((p) => p.enabled)
|
||||
.concat(CHERRYAI_PROVIDER)
|
||||
)
|
||||
|
||||
const selectSystemProviders = createSelector(selectProviders, (providers) =>
|
||||
providers.filter((p) => isSystemProvider(p)).map(normalizeProvider)
|
||||
)
|
||||
|
||||
const selectUserProviders = createSelector(selectProviders, (providers) =>
|
||||
providers.filter((p) => !isSystemProvider(p)).map(normalizeProvider)
|
||||
)
|
||||
|
||||
const selectAllProviders = createSelector(selectProviders, (providers) => providers.map(normalizeProvider))
|
||||
|
||||
const selectAllProvidersWithCherryAI = createSelector(selectProviders, (providers) =>
|
||||
[...providers, CHERRYAI_PROVIDER].map(normalizeProvider)
|
||||
)
|
||||
|
||||
export function useProviders() {
|
||||
@ -35,21 +66,20 @@ export function useProviders() {
|
||||
}
|
||||
|
||||
export function useSystemProviders() {
|
||||
return useAppSelector((state) => state.llm.providers.filter((p) => isSystemProvider(p)))
|
||||
return useAppSelector(selectSystemProviders)
|
||||
}
|
||||
|
||||
export function useUserProviders() {
|
||||
return useAppSelector((state) => state.llm.providers.filter((p) => !isSystemProvider(p)))
|
||||
return useAppSelector(selectUserProviders)
|
||||
}
|
||||
|
||||
export function useAllProviders() {
|
||||
return useAppSelector((state) => state.llm.providers)
|
||||
return useAppSelector(selectAllProviders)
|
||||
}
|
||||
|
||||
export function useProvider(id: string) {
|
||||
const provider =
|
||||
useAppSelector((state) => state.llm.providers.concat([CHERRYAI_PROVIDER]).find((p) => p.id === id)) ||
|
||||
getDefaultProvider()
|
||||
const allProviders = useAppSelector(selectAllProvidersWithCherryAI)
|
||||
const provider = useMemo(() => allProviders.find((p) => p.id === id) || getDefaultProvider(), [allProviders, id])
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
return {
|
||||
|
||||
@ -34,6 +34,7 @@ import {
|
||||
setTopicPosition,
|
||||
setTray as _setTray,
|
||||
setTrayOnClose,
|
||||
setUseSystemTitleBar as _setUseSystemTitleBar,
|
||||
setWindowStyle
|
||||
} from '@renderer/store/settings'
|
||||
import type { SidebarIcon, ThemeMode, TranslateLanguageCode } from '@renderer/types'
|
||||
@ -117,6 +118,10 @@ export function useSettings() {
|
||||
setDisableHardwareAcceleration(disableHardwareAcceleration: boolean) {
|
||||
dispatch(setDisableHardwareAcceleration(disableHardwareAcceleration))
|
||||
window.api.setDisableHardwareAcceleration(disableHardwareAcceleration)
|
||||
},
|
||||
setUseSystemTitleBar(useSystemTitleBar: boolean) {
|
||||
dispatch(_setUseSystemTitleBar(useSystemTitleBar))
|
||||
window.api.setUseSystemTitleBar(useSystemTitleBar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,17 @@
|
||||
import 'dayjs/locale/de'
|
||||
import 'dayjs/locale/el'
|
||||
import 'dayjs/locale/es'
|
||||
import 'dayjs/locale/fr'
|
||||
import 'dayjs/locale/ja'
|
||||
import 'dayjs/locale/pt'
|
||||
import 'dayjs/locale/ro'
|
||||
import 'dayjs/locale/ru'
|
||||
import 'dayjs/locale/zh-cn'
|
||||
import 'dayjs/locale/zh-tw'
|
||||
|
||||
import { loggerService } from '@logger'
|
||||
import { defaultLanguage } from '@shared/config/constant'
|
||||
import dayjs from 'dayjs'
|
||||
import i18n from 'i18next'
|
||||
import { initReactI18next } from 'react-i18next'
|
||||
|
||||
@ -43,6 +55,26 @@ export const getLanguageCode = () => {
|
||||
return getLanguage().split('-')[0]
|
||||
}
|
||||
|
||||
// Map i18n language codes to dayjs locale codes
|
||||
const dayjsLocaleMap: Record<string, string> = {
|
||||
'en-US': 'en',
|
||||
'ja-JP': 'ja',
|
||||
'ru-RU': 'ru',
|
||||
'zh-CN': 'zh-cn',
|
||||
'zh-TW': 'zh-tw',
|
||||
'de-DE': 'de',
|
||||
'el-GR': 'el',
|
||||
'es-ES': 'es',
|
||||
'fr-FR': 'fr',
|
||||
'pt-PT': 'pt',
|
||||
'ro-RO': 'ro'
|
||||
}
|
||||
|
||||
export const setDayjsLocale = (language: string) => {
|
||||
const dayjsLocale = dayjsLocaleMap[language] || 'en'
|
||||
dayjs.locale(dayjsLocale)
|
||||
}
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
resources,
|
||||
lng: getLanguage(),
|
||||
|
||||
@ -286,6 +286,19 @@ export const getMcpTypeLabel = (key: string): string => {
|
||||
return getLabel(mcpTypeKeyMap, key)
|
||||
}
|
||||
|
||||
const mcpProviderDescriptionKeyMap = {
|
||||
'302ai': 'settings.mcp.sync.providerDescriptions.302ai',
|
||||
bailian: 'settings.mcp.sync.providerDescriptions.bailian',
|
||||
lanyun: 'settings.mcp.sync.providerDescriptions.lanyun',
|
||||
mcprouter: 'settings.mcp.sync.providerDescriptions.mcprouter',
|
||||
modelscope: 'settings.mcp.sync.providerDescriptions.modelscope',
|
||||
tokenflux: 'settings.mcp.sync.providerDescriptions.tokenflux'
|
||||
} as const
|
||||
|
||||
export const getMcpProviderDescriptionLabel = (key: string): string => {
|
||||
return getLabel(mcpProviderDescriptionKeyMap, key)
|
||||
}
|
||||
|
||||
const miniappsStatusKeyMap = {
|
||||
visible: 'settings.miniapps.visible',
|
||||
disabled: 'settings.miniapps.disabled'
|
||||
|
||||
@ -271,6 +271,7 @@
|
||||
},
|
||||
"toolPermission": {
|
||||
"aria": {
|
||||
"allowAllRequest": "Always allow this tool",
|
||||
"allowRequest": "Allow tool request",
|
||||
"denyRequest": "Deny tool request",
|
||||
"hideDetails": "Hide tool details",
|
||||
@ -278,6 +279,7 @@
|
||||
"showDetails": "Show tool details"
|
||||
},
|
||||
"button": {
|
||||
"allowAll": "Always Allow",
|
||||
"cancel": "Cancel",
|
||||
"run": "Run"
|
||||
},
|
||||
@ -667,6 +669,10 @@
|
||||
"title": "New Topic"
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"create_session": "Create a session",
|
||||
"select_agent": "Select an agent"
|
||||
},
|
||||
"artifacts": {
|
||||
"button": {
|
||||
"download": "Download",
|
||||
@ -1200,6 +1206,8 @@
|
||||
"cancel": "Cancel",
|
||||
"chat": "Chat",
|
||||
"clear": "Clear",
|
||||
"clear_all": "Clear All",
|
||||
"click_to_replace": "Click to replace",
|
||||
"close": "Close",
|
||||
"collapse": "Collapse",
|
||||
"completed": "Completed",
|
||||
@ -1234,9 +1242,15 @@
|
||||
"footnote": "Reference content",
|
||||
"footnotes": "References",
|
||||
"fullscreen": "Entered fullscreen mode. Press F11 to exit",
|
||||
"generate_random_seed": "Generate random seed",
|
||||
"get_embedding_dimension": "Get embedding dimension",
|
||||
"go_to_settings": "Go to settings",
|
||||
"html_preview": "HTML Preview",
|
||||
"i_know": "I know",
|
||||
"ignore": "Ignore",
|
||||
"image_preview": "Image preview",
|
||||
"image_url": "Image URL",
|
||||
"image_url_or_upload": "Enter image URL or upload file",
|
||||
"inspect": "Inspect",
|
||||
"invalid_value": "Invalid Value",
|
||||
"knowledge_base": "Knowledge Base",
|
||||
@ -1246,6 +1260,7 @@
|
||||
"models": "Models",
|
||||
"more": "More",
|
||||
"name": "Name",
|
||||
"next_match": "Next match",
|
||||
"no_results": "No results",
|
||||
"none": "None",
|
||||
"off": "Off",
|
||||
@ -1257,13 +1272,17 @@
|
||||
"model": "Select a model"
|
||||
}
|
||||
},
|
||||
"powered_by": "Powered by ",
|
||||
"preview": "Preview",
|
||||
"previous_match": "Previous match",
|
||||
"prompt": "Prompt",
|
||||
"provider": "Provider",
|
||||
"reasoning_content": "Deep reasoning",
|
||||
"refresh": "Refresh",
|
||||
"regenerate": "Regenerate",
|
||||
"remove_image": "Remove image",
|
||||
"rename": "Rename",
|
||||
"required_field": "Required field",
|
||||
"reset": "Reset",
|
||||
"save": "Save",
|
||||
"saved": "Saved",
|
||||
@ -1286,14 +1305,29 @@
|
||||
"success": "Success",
|
||||
"swap": "Swap",
|
||||
"topics": "Topics",
|
||||
"translate_text": "Translate text",
|
||||
"unknown": "Unknown",
|
||||
"unnamed": "Unnamed",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"update_success": "Update successfully",
|
||||
"upload_files": "Upload file",
|
||||
"upload_image": "Upload image file",
|
||||
"uploaded_image": "Uploaded image",
|
||||
"warning": "Warning",
|
||||
"you": "You"
|
||||
},
|
||||
"dialog": {
|
||||
"all_files": "All Files",
|
||||
"html_files": "HTML Files",
|
||||
"open_file": "Open File",
|
||||
"pdf_files": "PDF Files",
|
||||
"png_image": "PNG Image",
|
||||
"save_as_html": "Save as HTML",
|
||||
"save_as_pdf": "Save as PDF",
|
||||
"save_file": "Save File",
|
||||
"select_folder": "Select Folder",
|
||||
"word_document": "Word Document"
|
||||
},
|
||||
"docs": {
|
||||
"title": "Docs"
|
||||
},
|
||||
@ -2015,10 +2049,65 @@
|
||||
"cancelled": "Cancelled",
|
||||
"completed": "Completed",
|
||||
"error": "Error occurred",
|
||||
"groupHeader": "{{count}} tool calls",
|
||||
"invoking": "Invoking",
|
||||
"labels": {
|
||||
"bash": "Bash",
|
||||
"edit": "Edit",
|
||||
"exitPlanMode": "ExitPlanMode",
|
||||
"glob": "Glob",
|
||||
"grep": "Grep",
|
||||
"mcpServerTool": "MCP Server Tool",
|
||||
"multiEdit": "MultiEdit",
|
||||
"notebookEdit": "NotebookEdit",
|
||||
"readFile": "Read File",
|
||||
"search": "Search",
|
||||
"skill": "Skill",
|
||||
"task": "Task",
|
||||
"todoWrite": "Todo Write",
|
||||
"tool": "Tool",
|
||||
"webFetch": "Web Fetch",
|
||||
"webSearch": "Web Search",
|
||||
"write": "Write"
|
||||
},
|
||||
"noData": "No data available for this tool",
|
||||
"pending": "Pending",
|
||||
"preview": "Preview",
|
||||
"raw": "Raw"
|
||||
"raw": "Raw",
|
||||
"runningCount": "{{count}} tools running",
|
||||
"sections": {
|
||||
"command": "Command",
|
||||
"exitCode": "Exit Code",
|
||||
"input": "Input",
|
||||
"output": "Output",
|
||||
"prompt": "Prompt",
|
||||
"searchQuery": "Search Query",
|
||||
"searchResults": "Search Results",
|
||||
"stderr": "stderr",
|
||||
"stdout": "stdout"
|
||||
},
|
||||
"status": {
|
||||
"done": "Done",
|
||||
"error": "Error",
|
||||
"failed": "Failed",
|
||||
"running": "Running",
|
||||
"success": "Success"
|
||||
},
|
||||
"truncated": "Output truncated (original: {{size}})",
|
||||
"units": {
|
||||
"done_one": "{{count}} Done",
|
||||
"done_other": "{{count}} Done",
|
||||
"file_one": "{{count}} file",
|
||||
"file_other": "{{count}} files",
|
||||
"item_one": "{{count}} item",
|
||||
"item_other": "{{count}} items",
|
||||
"line_one": "{{count}} line",
|
||||
"line_other": "{{count}} lines",
|
||||
"plan_one": "{{count}} plan",
|
||||
"plan_other": "{{count}} plans",
|
||||
"result_one": "{{count}} result",
|
||||
"result_other": "{{count}} results"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"added": "New topic added"
|
||||
@ -2226,14 +2315,18 @@
|
||||
"drop_markdown_hint": "Drop .md files or folders here to import",
|
||||
"empty": "No notes available yet",
|
||||
"expand": "unfold",
|
||||
"exportToWord": "Export to Word",
|
||||
"export_failed": "Failed to export to knowledge base",
|
||||
"export_knowledge": "Export notes to knowledge base",
|
||||
"export_success": "Successfully exported to the knowledge base",
|
||||
"export_to_word_failed": "Failed to export to Word",
|
||||
"folder": "folder",
|
||||
"new_folder": "New Folder",
|
||||
"new_note": "Create a new note",
|
||||
"no_content_to_copy": "No content to copy",
|
||||
"no_content_to_export": "No content to export",
|
||||
"no_file_selected": "Please select the file to upload",
|
||||
"no_note_selected": "Please select a note first",
|
||||
"no_valid_files": "No valid file was uploaded",
|
||||
"open_folder": "Open an external folder",
|
||||
"open_outside": "Open from external",
|
||||
@ -2484,6 +2577,39 @@
|
||||
}
|
||||
},
|
||||
"custom_size": "Custom Size",
|
||||
"dmxapi": {
|
||||
"generating_tip": "Generating with the official model, estimated wait time is 2-5 minutes for best results. Please check DMXAPI backend logs for the cost of this operation.",
|
||||
"style": ", Style: ",
|
||||
"style_types": {
|
||||
"25d_animation": "2.5D Animation",
|
||||
"3d_cartoon": "3D Cartoon",
|
||||
"american_retro": "American Retro",
|
||||
"baroque": "Baroque",
|
||||
"cartoon_illustration": "Cartoon Illustration",
|
||||
"chinese_gongbi": "Chinese Gongbi",
|
||||
"clay": "Clay",
|
||||
"cyberpunk": "Cyberpunk",
|
||||
"felt": "Felt",
|
||||
"flat": "Flat",
|
||||
"fresh_anime": "Fresh Anime",
|
||||
"ghibli": "Ghibli",
|
||||
"japanese_anime": "Japanese Anime",
|
||||
"little_people_book": "Little People Book",
|
||||
"monet_garden": "Monet Garden",
|
||||
"oil_painting": "Oil Painting",
|
||||
"pixar": "Pixar",
|
||||
"pixel_art": "Pixel Art",
|
||||
"poetic_ancient": "Poetic Ancient",
|
||||
"psychedelic": "Psychedelic",
|
||||
"sketch": "Sketch",
|
||||
"street_art": "Street Art",
|
||||
"texture": "Texture",
|
||||
"ukiyo_e": "Ukiyo-e",
|
||||
"watercolor": "Watercolor",
|
||||
"wood_carving": "Wood Carving",
|
||||
"yarn_doll": "Yarn Doll"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"image_file": "Edited Image",
|
||||
"magic_prompt_option_tip": "Intelligently enhances editing prompts",
|
||||
@ -2508,6 +2634,7 @@
|
||||
"style_type_tip": "Image generation style for V_2 and above",
|
||||
"width": "Width"
|
||||
},
|
||||
"generate_failed": "Failed to generate image",
|
||||
"generated_image": "Generated Image",
|
||||
"go_to_settings": "Go to Settings",
|
||||
"guidance_scale": "Guidance Scale",
|
||||
@ -2518,6 +2645,7 @@
|
||||
"image_file_required": "Please upload an image first",
|
||||
"image_file_retry": "Please re-upload an image first",
|
||||
"image_handle_required": "Please upload an image first.",
|
||||
"image_mix_failed": "Failed to mix images",
|
||||
"image_placeholder": "No image available",
|
||||
"image_retry": "Retry",
|
||||
"image_size_options": {
|
||||
@ -2527,6 +2655,7 @@
|
||||
"inference_steps_tip": "The number of inference steps to perform. More steps produce higher quality but take longer",
|
||||
"input_image": "Input Image",
|
||||
"input_parameters": "Input Parameters",
|
||||
"invalid_image_url": "Invalid image URL format",
|
||||
"learn_more": "Learn More",
|
||||
"magic_prompt_option": "Magic Prompt",
|
||||
"mode": {
|
||||
@ -2548,6 +2677,7 @@
|
||||
"no_image_generation_model": "No available image generation model, please add a model and set the endpoint type to {{endpoint_type}}",
|
||||
"number_images": "Number Images",
|
||||
"number_images_tip": "Number of images to generate (1-4)",
|
||||
"operation_failed": "Operation failed, please try again later",
|
||||
"paint_course": "tutorial",
|
||||
"per_image": "per image",
|
||||
"per_images": "per images",
|
||||
@ -2623,6 +2753,26 @@
|
||||
"resemblance": "Similarity",
|
||||
"resemblance_tip": "Controls similarity to original image",
|
||||
"seed_tip": "Controls upscaling randomness"
|
||||
},
|
||||
"zhipu": {
|
||||
"custom_size_divisible": "Custom size must be divisible by 16",
|
||||
"custom_size_hint": "Width and height must be between 512px-2048px, divisible by 16, and total pixels cannot exceed 2^21px",
|
||||
"custom_size_pixels": "Total pixels of custom size cannot exceed 2,097,152",
|
||||
"custom_size_range": "Custom size must be between 512px-2048px",
|
||||
"custom_size_required": "Please set custom width and height",
|
||||
"image_sizes": {
|
||||
"1024x1024_default": "1024x1024 (Default)",
|
||||
"1152x864": "1152x864",
|
||||
"1344x768": "1344x768",
|
||||
"1440x720": "1440x720",
|
||||
"720x1440": "720x1440",
|
||||
"768x1344": "768x1344",
|
||||
"864x1152": "864x1152"
|
||||
},
|
||||
"quality_options": {
|
||||
"hd": "HD",
|
||||
"standard_default": "Standard (Default)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
@ -2633,6 +2783,20 @@
|
||||
"category": "Category",
|
||||
"commands": "Commands",
|
||||
"confirm_uninstall": "Are you sure you want to uninstall {{name}}?",
|
||||
"content_saved": "Plugin content saved successfully",
|
||||
"detail": {
|
||||
"allowed_tools": "Allowed Tools",
|
||||
"author": "Author",
|
||||
"content": "Content",
|
||||
"description": "Description",
|
||||
"file": "File",
|
||||
"installed": "Installed",
|
||||
"metadata": "Metadata",
|
||||
"size": "Size",
|
||||
"source": "Source",
|
||||
"tags": "Tags",
|
||||
"tools": "Tools"
|
||||
},
|
||||
"install": "Install",
|
||||
"install_plugins_from_browser": "Browse available plugins to get started",
|
||||
"installing": "Installing...",
|
||||
@ -2983,6 +3147,11 @@
|
||||
"summary": "Summarize",
|
||||
"translate": "Translate"
|
||||
},
|
||||
"prompt": {
|
||||
"explain": "Please explain the following content. Requirements: Reply in {{language}}; do not include any explanation of this prompt, just give the response directly: \n\n",
|
||||
"refine": "Please optimize or polish the user input content wrapped in the XML tag <INPUT>, while maintaining the meaning and integrity of the original content. Requirements: Your output should be in the same language as the user input; do not include any explanation of this prompt, just give the response directly; do not output XML tags, output the optimized content directly: \n\n<INPUT>{{text}}</INPUT>",
|
||||
"summary": "Please summarize the following content. Requirements: Reply in {{language}}; do not include any explanation of this prompt, just give the response directly: \n\n"
|
||||
},
|
||||
"translate": {
|
||||
"smart_translate_tips": "Smart Translation: Content will be translated to the target language first; content already in the target language will be translated to the alternative language"
|
||||
},
|
||||
@ -4076,6 +4245,10 @@
|
||||
"jsonModeHint": "Edit the JSON representation of the MCP server configuration. Please ensure the format is correct before saving.",
|
||||
"jsonSaveError": "Failed to save JSON configuration.",
|
||||
"jsonSaveSuccess": "JSON configuration has been saved.",
|
||||
"lanyun": {
|
||||
"description": "Lanyun Technology Cloud Platform MCP Service",
|
||||
"name": "Lanyun Technology"
|
||||
},
|
||||
"logoUrl": "Logo URL",
|
||||
"logs": "Logs",
|
||||
"longRunning": "Long Running Mode",
|
||||
@ -4172,6 +4345,14 @@
|
||||
"getToken": "Get API Token",
|
||||
"getTokenDescription": "Retrieve your personal API token from your account",
|
||||
"noServersAvailable": "No MCP servers available",
|
||||
"providerDescriptions": {
|
||||
"302ai": "302.AI Platform MCP Service",
|
||||
"bailian": "Alibaba Cloud Bailian Platform MCP Service",
|
||||
"lanyun": "Lanyun Technology Cloud Platform MCP Service",
|
||||
"mcprouter": "MCP Router Platform MCP Service",
|
||||
"modelscope": "ModelScope Platform MCP Service",
|
||||
"tokenflux": "TokenFlux Platform MCP Service"
|
||||
},
|
||||
"selectProvider": "Select Provider:",
|
||||
"setToken": "Enter Your Token",
|
||||
"success": "Sync MCP Servers successful",
|
||||
@ -4649,6 +4830,9 @@
|
||||
"title": "Delete Provider"
|
||||
},
|
||||
"dmxapi": {
|
||||
"platform_enterprise": "ssvip.DMXAPI.com (Enterprise)",
|
||||
"platform_international": "www.DMXAPI.com (International)",
|
||||
"platform_official": "www.DMXAPI.cn (CNY)",
|
||||
"select_platform": "Select the platform"
|
||||
},
|
||||
"docs_check": "Check",
|
||||
@ -4985,6 +5169,13 @@
|
||||
"show": "Show Tray Icon",
|
||||
"title": "Tray"
|
||||
},
|
||||
"use_system_title_bar": {
|
||||
"confirm": {
|
||||
"content": "Changing the title bar style requires restarting the app to take effect. Do you want to restart now?",
|
||||
"title": "Restart Required"
|
||||
},
|
||||
"title": "Use System Title Bar (Linux)"
|
||||
},
|
||||
"zoom": {
|
||||
"reset": "Reset",
|
||||
"title": "Page Zoom"
|
||||
|
||||
@ -271,6 +271,7 @@
|
||||
},
|
||||
"toolPermission": {
|
||||
"aria": {
|
||||
"allowAllRequest": "总是允许此工具",
|
||||
"allowRequest": "允许工具请求",
|
||||
"denyRequest": "拒绝工具请求",
|
||||
"hideDetails": "隐藏工具详情",
|
||||
@ -278,6 +279,7 @@
|
||||
"showDetails": "显示工具详情"
|
||||
},
|
||||
"button": {
|
||||
"allowAll": "总是允许",
|
||||
"cancel": "取消",
|
||||
"run": "运行"
|
||||
},
|
||||
@ -667,6 +669,10 @@
|
||||
"title": "新建话题"
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"create_session": "请创建会话",
|
||||
"select_agent": "请选择智能体"
|
||||
},
|
||||
"artifacts": {
|
||||
"button": {
|
||||
"download": "下载",
|
||||
@ -1200,6 +1206,8 @@
|
||||
"cancel": "取消",
|
||||
"chat": "聊天",
|
||||
"clear": "清除",
|
||||
"clear_all": "清除全部",
|
||||
"click_to_replace": "点击替换",
|
||||
"close": "关闭",
|
||||
"collapse": "折叠",
|
||||
"completed": "完成",
|
||||
@ -1234,9 +1242,15 @@
|
||||
"footnote": "引用内容",
|
||||
"footnotes": "引用内容",
|
||||
"fullscreen": "已进入全屏模式,按 F11 退出",
|
||||
"generate_random_seed": "生成随机种子",
|
||||
"get_embedding_dimension": "获取嵌入维度",
|
||||
"go_to_settings": "前往设置",
|
||||
"html_preview": "HTML 预览",
|
||||
"i_know": "我知道了",
|
||||
"ignore": "忽略",
|
||||
"image_preview": "图片预览",
|
||||
"image_url": "图片链接",
|
||||
"image_url_or_upload": "输入图片链接或上传文件",
|
||||
"inspect": "检查",
|
||||
"invalid_value": "无效值",
|
||||
"knowledge_base": "知识库",
|
||||
@ -1246,6 +1260,7 @@
|
||||
"models": "模型",
|
||||
"more": "更多",
|
||||
"name": "名称",
|
||||
"next_match": "下一个匹配",
|
||||
"no_results": "无结果",
|
||||
"none": "无",
|
||||
"off": "关闭",
|
||||
@ -1257,13 +1272,17 @@
|
||||
"model": "选择模型"
|
||||
}
|
||||
},
|
||||
"powered_by": "由 ",
|
||||
"preview": "预览",
|
||||
"previous_match": "上一个匹配",
|
||||
"prompt": "提示词",
|
||||
"provider": "提供商",
|
||||
"reasoning_content": "已深度思考",
|
||||
"refresh": "刷新",
|
||||
"regenerate": "重新生成",
|
||||
"remove_image": "移除图片",
|
||||
"rename": "重命名",
|
||||
"required_field": "必填字段",
|
||||
"reset": "重置",
|
||||
"save": "保存",
|
||||
"saved": "已保存",
|
||||
@ -1286,14 +1305,29 @@
|
||||
"success": "成功",
|
||||
"swap": "交换",
|
||||
"topics": "话题",
|
||||
"translate_text": "翻译文本",
|
||||
"unknown": "未知",
|
||||
"unnamed": "未命名",
|
||||
"unsubscribe": "退订",
|
||||
"update_success": "更新成功",
|
||||
"upload_files": "上传文件",
|
||||
"upload_image": "上传图片文件",
|
||||
"uploaded_image": "已上传图片",
|
||||
"warning": "警告",
|
||||
"you": "用户"
|
||||
},
|
||||
"dialog": {
|
||||
"all_files": "所有文件",
|
||||
"html_files": "HTML 文件",
|
||||
"open_file": "打开文件",
|
||||
"pdf_files": "PDF 文件",
|
||||
"png_image": "PNG 图片",
|
||||
"save_as_html": "保存为 HTML",
|
||||
"save_as_pdf": "保存为 PDF",
|
||||
"save_file": "保存文件",
|
||||
"select_folder": "选择文件夹",
|
||||
"word_document": "Word 文档"
|
||||
},
|
||||
"docs": {
|
||||
"title": "帮助文档"
|
||||
},
|
||||
@ -2015,10 +2049,65 @@
|
||||
"cancelled": "已取消",
|
||||
"completed": "已完成",
|
||||
"error": "发生错误",
|
||||
"groupHeader": "{{count}} 个工具调用",
|
||||
"invoking": "调用中",
|
||||
"labels": {
|
||||
"bash": "执行命令",
|
||||
"edit": "编辑",
|
||||
"exitPlanMode": "退出计划模式",
|
||||
"glob": "文件匹配",
|
||||
"grep": "搜索",
|
||||
"mcpServerTool": "MCP 服务器工具",
|
||||
"multiEdit": "批量编辑",
|
||||
"notebookEdit": "笔记本编辑",
|
||||
"readFile": "读取文件",
|
||||
"search": "搜索",
|
||||
"skill": "技能",
|
||||
"task": "任务",
|
||||
"todoWrite": "待办写入",
|
||||
"tool": "工具",
|
||||
"webFetch": "网页获取",
|
||||
"webSearch": "网页搜索",
|
||||
"write": "写入"
|
||||
},
|
||||
"noData": "此工具暂无可用数据",
|
||||
"pending": "等待中",
|
||||
"preview": "预览",
|
||||
"raw": "原始"
|
||||
"raw": "原始",
|
||||
"runningCount": "{{count}} 个工具运行中",
|
||||
"sections": {
|
||||
"command": "命令",
|
||||
"exitCode": "退出码",
|
||||
"input": "输入",
|
||||
"output": "输出",
|
||||
"prompt": "提示",
|
||||
"searchQuery": "搜索查询",
|
||||
"searchResults": "搜索结果",
|
||||
"stderr": "标准错误",
|
||||
"stdout": "标准输出"
|
||||
},
|
||||
"status": {
|
||||
"done": "完成",
|
||||
"error": "错误",
|
||||
"failed": "失败",
|
||||
"running": "运行中",
|
||||
"success": "成功"
|
||||
},
|
||||
"truncated": "输出已截断(原始大小:{{size}})",
|
||||
"units": {
|
||||
"done_one": "{{count}} 项完成",
|
||||
"done_other": "{{count}} 项完成",
|
||||
"file_one": "{{count}} 个文件",
|
||||
"file_other": "{{count}} 个文件",
|
||||
"item_one": "{{count}} 项",
|
||||
"item_other": "{{count}} 项",
|
||||
"line_one": "{{count}} 行",
|
||||
"line_other": "{{count}} 行",
|
||||
"plan_one": "{{count}} 个计划",
|
||||
"plan_other": "{{count}} 个计划",
|
||||
"result_one": "{{count}} 个结果",
|
||||
"result_other": "{{count}} 个结果"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"added": "话题添加成功"
|
||||
@ -2226,14 +2315,18 @@
|
||||
"drop_markdown_hint": "拖拽 .md 文件或目录到此处导入",
|
||||
"empty": "暂无笔记",
|
||||
"expand": "展开",
|
||||
"exportToWord": "导出为 Word",
|
||||
"export_failed": "导出到知识库失败",
|
||||
"export_knowledge": "导出笔记到知识库",
|
||||
"export_success": "成功导出到知识库",
|
||||
"export_to_word_failed": "导出为 Word 失败",
|
||||
"folder": "文件夹",
|
||||
"new_folder": "新建文件夹",
|
||||
"new_note": "新建笔记",
|
||||
"no_content_to_copy": "没有内容可复制",
|
||||
"no_content_to_export": "没有内容可导出",
|
||||
"no_file_selected": "请选择要上传的文件",
|
||||
"no_note_selected": "请先选择一个笔记",
|
||||
"no_valid_files": "没有上传有效的文件",
|
||||
"open_folder": "打开外部文件夹",
|
||||
"open_outside": "从外部打开",
|
||||
@ -2484,6 +2577,39 @@
|
||||
}
|
||||
},
|
||||
"custom_size": "自定义尺寸",
|
||||
"dmxapi": {
|
||||
"generating_tip": "正在使用官方的模型生成,预计等待2~5分钟效果最好,本次消耗金额请到DMXAPI后台日志查看",
|
||||
"style": ",风格:",
|
||||
"style_types": {
|
||||
"25d_animation": "2.5D动画",
|
||||
"3d_cartoon": "3D卡通",
|
||||
"american_retro": "美式复古",
|
||||
"baroque": "巴洛克",
|
||||
"cartoon_illustration": "卡通插画",
|
||||
"chinese_gongbi": "中国工笔",
|
||||
"clay": "黏土",
|
||||
"cyberpunk": "赛博朋克",
|
||||
"felt": "毛毡",
|
||||
"flat": "平坦",
|
||||
"fresh_anime": "新鲜动漫",
|
||||
"ghibli": "吉卜力",
|
||||
"japanese_anime": "日本动漫",
|
||||
"little_people_book": "小人书",
|
||||
"monet_garden": "莫奈花园",
|
||||
"oil_painting": "油画",
|
||||
"pixar": "皮克斯",
|
||||
"pixel_art": "像素艺术",
|
||||
"poetic_ancient": "诗意古风",
|
||||
"psychedelic": "迷幻",
|
||||
"sketch": "草图",
|
||||
"street_art": "街头艺术",
|
||||
"texture": "纹理",
|
||||
"ukiyo_e": "浮世绘",
|
||||
"watercolor": "水彩",
|
||||
"wood_carving": "木雕",
|
||||
"yarn_doll": "纱线娃娃"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"image_file": "编辑的图像",
|
||||
"magic_prompt_option_tip": "智能优化编辑提示词",
|
||||
@ -2508,6 +2634,7 @@
|
||||
"style_type_tip": "图像生成风格,仅适用于 V_2 及以上版本",
|
||||
"width": "宽度"
|
||||
},
|
||||
"generate_failed": "生成图像失败",
|
||||
"generated_image": "生成图片",
|
||||
"go_to_settings": "去设置",
|
||||
"guidance_scale": "引导比例",
|
||||
@ -2518,6 +2645,7 @@
|
||||
"image_file_required": "请先上传图片",
|
||||
"image_file_retry": "请重新上传图片",
|
||||
"image_handle_required": "请先上传图片",
|
||||
"image_mix_failed": "图像混合失败",
|
||||
"image_placeholder": "暂无图片",
|
||||
"image_retry": "重试",
|
||||
"image_size_options": {
|
||||
@ -2527,6 +2655,7 @@
|
||||
"inference_steps_tip": "要执行的推理步数。步数越多,质量越高但耗时越长",
|
||||
"input_image": "输入图片",
|
||||
"input_parameters": "输入参数",
|
||||
"invalid_image_url": "无效的图片URL格式",
|
||||
"learn_more": "了解更多",
|
||||
"magic_prompt_option": "提示词增强",
|
||||
"mode": {
|
||||
@ -2548,6 +2677,7 @@
|
||||
"no_image_generation_model": "暂无可用的图片生成模型,请先新增模型并设置端点类型为 {{endpoint_type}}",
|
||||
"number_images": "生成数量",
|
||||
"number_images_tip": "一次生成的图片数量 (1-4)",
|
||||
"operation_failed": "操作失败,请稍后重试",
|
||||
"paint_course": "教程",
|
||||
"per_image": "每张图片",
|
||||
"per_images": "每张图片",
|
||||
@ -2623,6 +2753,26 @@
|
||||
"resemblance": "相似度",
|
||||
"resemblance_tip": "控制放大结果与原图的相似程度",
|
||||
"seed_tip": "控制放大结果的随机性"
|
||||
},
|
||||
"zhipu": {
|
||||
"custom_size_divisible": "自定义尺寸必须能被16整除",
|
||||
"custom_size_hint": "长宽均需满足512px-2048px之间,需被16整除,并保证最大像素数不超过2^21px",
|
||||
"custom_size_pixels": "自定义尺寸的总像素数不能超过2,097,152",
|
||||
"custom_size_range": "自定义尺寸必须在512px-2048px之间",
|
||||
"custom_size_required": "请设置自定义尺寸的宽度和高度",
|
||||
"image_sizes": {
|
||||
"1024x1024_default": "1024x1024(默认)",
|
||||
"1152x864": "1152x864",
|
||||
"1344x768": "1344x768",
|
||||
"1440x720": "1440x720",
|
||||
"720x1440": "720x1440",
|
||||
"768x1344": "768×1344",
|
||||
"864x1152": "864x1152"
|
||||
},
|
||||
"quality_options": {
|
||||
"hd": "高清",
|
||||
"standard_default": "标准(默认)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
@ -2633,6 +2783,20 @@
|
||||
"category": "类别",
|
||||
"commands": "命令",
|
||||
"confirm_uninstall": "确定要卸载 {{name}} 吗?",
|
||||
"content_saved": "插件内容保存成功",
|
||||
"detail": {
|
||||
"allowed_tools": "允许的工具",
|
||||
"author": "作者",
|
||||
"content": "内容",
|
||||
"description": "描述",
|
||||
"file": "文件",
|
||||
"installed": "安装时间",
|
||||
"metadata": "元数据",
|
||||
"size": "大小",
|
||||
"source": "来源",
|
||||
"tags": "标签",
|
||||
"tools": "工具"
|
||||
},
|
||||
"install": "安装",
|
||||
"install_plugins_from_browser": "浏览可用插件以开始使用",
|
||||
"installing": "安装中...",
|
||||
@ -2983,6 +3147,11 @@
|
||||
"summary": "总结",
|
||||
"translate": "翻译"
|
||||
},
|
||||
"prompt": {
|
||||
"explain": "请解释下面的内容。要求:使用 {{language}} 语言进行回复;请不要包含对本提示词的任何解释,直接给出回复: \n\n",
|
||||
"refine": "请对用XML标签<INPUT>包裹的用户输入内容进行优化或润色,并保持原内容的含义和完整性。要求:你的输出应当与用户输入内容的语言相同。;请不要包含对本提示词的任何解释,直接给出回复;请不要输出XML标签,直接输出优化后的内容: \n\n<INPUT>{{text}}</INPUT>",
|
||||
"summary": "请总结下面的内容。要求:使用 {{language}} 语言进行回复;请不要包含对本提示词的任何解释,直接给出回复: \n\n"
|
||||
},
|
||||
"translate": {
|
||||
"smart_translate_tips": "智能翻译:内容将优先翻译为目标语言;内容已是目标语言的,将翻译为备选语言"
|
||||
},
|
||||
@ -4076,6 +4245,10 @@
|
||||
"jsonModeHint": "编辑 MCP 服务器配置的 JSON 表示。保存前请确保格式正确",
|
||||
"jsonSaveError": "保存 JSON 配置失败",
|
||||
"jsonSaveSuccess": "JSON 配置已保存",
|
||||
"lanyun": {
|
||||
"description": "蓝耘科技云平台 MCP 服务",
|
||||
"name": "蓝耘科技"
|
||||
},
|
||||
"logoUrl": "标志网址",
|
||||
"logs": "日志",
|
||||
"longRunning": "长时间运行模式",
|
||||
@ -4172,6 +4345,14 @@
|
||||
"getToken": "获取 API 令牌",
|
||||
"getTokenDescription": "从您的帐户中获取个人 API 令牌",
|
||||
"noServersAvailable": "无可用的 MCP 服务器",
|
||||
"providerDescriptions": {
|
||||
"302ai": "302.AI 平台 MCP 服务",
|
||||
"bailian": "百炼平台服务",
|
||||
"lanyun": "蓝耘科技云平台 MCP 服务",
|
||||
"mcprouter": "MCP Router 平台 MCP 服务",
|
||||
"modelscope": "ModelScope 平台 MCP 服务",
|
||||
"tokenflux": "TokenFlux 平台 MCP 服务"
|
||||
},
|
||||
"selectProvider": "选择提供商:",
|
||||
"setToken": "输入您的令牌",
|
||||
"success": "同步 MCP 服务器成功",
|
||||
@ -4649,6 +4830,9 @@
|
||||
"title": "删除提供商"
|
||||
},
|
||||
"dmxapi": {
|
||||
"platform_enterprise": "ssvip.DMXAPI.com 生产级商用站",
|
||||
"platform_international": "www.DMXAPI.com 国际站",
|
||||
"platform_official": "www.DMXAPI.cn 人民币站",
|
||||
"select_platform": "选择平台"
|
||||
},
|
||||
"docs_check": "查看",
|
||||
@ -4985,6 +5169,13 @@
|
||||
"show": "显示托盘图标",
|
||||
"title": "托盘"
|
||||
},
|
||||
"use_system_title_bar": {
|
||||
"confirm": {
|
||||
"content": "更改标题栏样式需要重启应用才能生效,是否现在重启?",
|
||||
"title": "需要重启应用"
|
||||
},
|
||||
"title": "使用系统标题栏 (Linux)"
|
||||
},
|
||||
"zoom": {
|
||||
"reset": "重置",
|
||||
"title": "缩放"
|
||||
|
||||
@ -271,6 +271,7 @@
|
||||
},
|
||||
"toolPermission": {
|
||||
"aria": {
|
||||
"allowAllRequest": "總是允許此工具",
|
||||
"allowRequest": "允許工具請求",
|
||||
"denyRequest": "拒絕工具請求",
|
||||
"hideDetails": "隱藏工具詳細資訊",
|
||||
@ -278,6 +279,7 @@
|
||||
"showDetails": "顯示工具詳細資訊"
|
||||
},
|
||||
"button": {
|
||||
"allowAll": "總是允許",
|
||||
"cancel": "取消",
|
||||
"run": "執行"
|
||||
},
|
||||
@ -667,6 +669,10 @@
|
||||
"title": "新增話題"
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"create_session": "建立工作階段",
|
||||
"select_agent": "選擇一個代理人"
|
||||
},
|
||||
"artifacts": {
|
||||
"button": {
|
||||
"download": "下載",
|
||||
@ -1200,6 +1206,8 @@
|
||||
"cancel": "取消",
|
||||
"chat": "聊天",
|
||||
"clear": "清除",
|
||||
"clear_all": "全部清除",
|
||||
"click_to_replace": "點擊以替換",
|
||||
"close": "關閉",
|
||||
"collapse": "收合",
|
||||
"completed": "已完成",
|
||||
@ -1234,9 +1242,15 @@
|
||||
"footnote": "引用內容",
|
||||
"footnotes": "引用",
|
||||
"fullscreen": "已進入全螢幕模式,按 F11 結束",
|
||||
"generate_random_seed": "產生隨機種子",
|
||||
"get_embedding_dimension": "取得嵌入維度",
|
||||
"go_to_settings": "前往設定",
|
||||
"html_preview": "HTML 預覽",
|
||||
"i_know": "我知道了",
|
||||
"ignore": "忽略",
|
||||
"image_preview": "圖片預覽",
|
||||
"image_url": "圖片網址",
|
||||
"image_url_or_upload": "輸入圖片網址或上傳檔案",
|
||||
"inspect": "檢查",
|
||||
"invalid_value": "無效值",
|
||||
"knowledge_base": "知識庫",
|
||||
@ -1246,6 +1260,7 @@
|
||||
"models": "模型",
|
||||
"more": "更多",
|
||||
"name": "名稱",
|
||||
"next_match": "下一場比賽",
|
||||
"no_results": "沒有結果",
|
||||
"none": "無",
|
||||
"off": "關閉",
|
||||
@ -1257,13 +1272,17 @@
|
||||
"model": "選擇模型"
|
||||
}
|
||||
},
|
||||
"powered_by": "技術提供",
|
||||
"preview": "預覽",
|
||||
"previous_match": "上一場比賽",
|
||||
"prompt": "提示詞",
|
||||
"provider": "供應商",
|
||||
"reasoning_content": "已深度思考",
|
||||
"refresh": "重新整理",
|
||||
"regenerate": "重新產生",
|
||||
"remove_image": "移除圖片",
|
||||
"rename": "重新命名",
|
||||
"required_field": "必填欄位",
|
||||
"reset": "重設",
|
||||
"save": "儲存",
|
||||
"saved": "已儲存",
|
||||
@ -1286,14 +1305,29 @@
|
||||
"success": "成功",
|
||||
"swap": "交換",
|
||||
"topics": "話題",
|
||||
"translate_text": "翻譯文字",
|
||||
"unknown": "未知",
|
||||
"unnamed": "未命名",
|
||||
"unsubscribe": "取消訂閱",
|
||||
"update_success": "更新成功",
|
||||
"upload_files": "上傳檔案",
|
||||
"upload_image": "上傳圖片檔案",
|
||||
"uploaded_image": "已上傳圖片",
|
||||
"warning": "警告",
|
||||
"you": "您"
|
||||
},
|
||||
"dialog": {
|
||||
"all_files": "所有檔案",
|
||||
"html_files": "HTML 檔案",
|
||||
"open_file": "開啟檔案",
|
||||
"pdf_files": "PDF 檔案",
|
||||
"png_image": "PNG 影像",
|
||||
"save_as_html": "儲存為 HTML",
|
||||
"save_as_pdf": "儲存為 PDF",
|
||||
"save_file": "儲存檔案",
|
||||
"select_folder": "選擇資料夾",
|
||||
"word_document": "Word 文件"
|
||||
},
|
||||
"docs": {
|
||||
"title": "說明文件"
|
||||
},
|
||||
@ -2015,10 +2049,65 @@
|
||||
"cancelled": "已取消",
|
||||
"completed": "已完成",
|
||||
"error": "發生錯誤",
|
||||
"groupHeader": "{{count}} 次工具呼叫",
|
||||
"invoking": "呼叫中",
|
||||
"labels": {
|
||||
"bash": "執行命令",
|
||||
"edit": "編輯",
|
||||
"exitPlanMode": "退出計畫模式",
|
||||
"glob": "檔案匹配",
|
||||
"grep": "搜尋",
|
||||
"mcpServerTool": "MCP 伺服器工具",
|
||||
"multiEdit": "批次編輯",
|
||||
"notebookEdit": "筆記本編輯",
|
||||
"readFile": "讀取檔案",
|
||||
"search": "搜尋",
|
||||
"skill": "技能",
|
||||
"task": "任務",
|
||||
"todoWrite": "待辦寫入",
|
||||
"tool": "工具",
|
||||
"webFetch": "網頁擷取",
|
||||
"webSearch": "網頁搜尋",
|
||||
"write": "寫入"
|
||||
},
|
||||
"noData": "此工具暫無可用資料",
|
||||
"pending": "等待中",
|
||||
"preview": "預覽",
|
||||
"raw": "原始碼"
|
||||
"raw": "原始碼",
|
||||
"runningCount": "{{count}} 個工具正在運行",
|
||||
"sections": {
|
||||
"command": "命令",
|
||||
"exitCode": "退出碼",
|
||||
"input": "輸入",
|
||||
"output": "輸出",
|
||||
"prompt": "提示",
|
||||
"searchQuery": "搜尋查詢",
|
||||
"searchResults": "搜尋結果",
|
||||
"stderr": "標準錯誤",
|
||||
"stdout": "標準輸出"
|
||||
},
|
||||
"status": {
|
||||
"done": "完成",
|
||||
"error": "錯誤",
|
||||
"failed": "失敗",
|
||||
"running": "執行中",
|
||||
"success": "成功"
|
||||
},
|
||||
"truncated": "輸出已截斷(原始大小:{{size}})",
|
||||
"units": {
|
||||
"done_one": "{{count}} 完成",
|
||||
"done_other": "{{count}} 完成",
|
||||
"file_one": "{{count}} 個檔案",
|
||||
"file_other": "{{count}} 個檔案",
|
||||
"item_one": "{{count}} 個項目",
|
||||
"item_other": "{{count}} 個項目",
|
||||
"line_one": "{{count}} 行",
|
||||
"line_other": "{{count}} 行",
|
||||
"plan_one": "{{count}} 方案",
|
||||
"plan_other": "{{count}} 個方案",
|
||||
"result_one": "{{count}} 個結果",
|
||||
"result_other": "{{count}} 個結果"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"added": "新話題已新增"
|
||||
@ -2226,14 +2315,18 @@
|
||||
"drop_markdown_hint": "拖曳 .md 檔案或資料夾到此處匯入",
|
||||
"empty": "暫無筆記",
|
||||
"expand": "展開",
|
||||
"exportToWord": "匯出為 Word",
|
||||
"export_failed": "匯出至知識庫失敗",
|
||||
"export_knowledge": "匯出筆記至知識庫",
|
||||
"export_success": "成功匯出至知識庫",
|
||||
"export_to_word_failed": "匯出為 Word 失敗",
|
||||
"folder": "資料夾",
|
||||
"new_folder": "新增資料夾",
|
||||
"new_note": "新增筆記",
|
||||
"no_content_to_copy": "沒有內容可複製",
|
||||
"no_content_to_export": "沒有內容可匯出",
|
||||
"no_file_selected": "請選擇要上傳的檔案",
|
||||
"no_note_selected": "請先選擇一個筆記",
|
||||
"no_valid_files": "沒有上傳有效的檔案",
|
||||
"open_folder": "開啟外部資料夾",
|
||||
"open_outside": "從外部開啟",
|
||||
@ -2484,6 +2577,39 @@
|
||||
}
|
||||
},
|
||||
"custom_size": "自訂尺寸",
|
||||
"dmxapi": {
|
||||
"generating_tip": "使用官方模型生成,預計等待時間為 2–5 分鐘以獲得最佳結果。請查看 DMXAPI 後端日誌以了解此操作的成本。",
|
||||
"style": ",風格:",
|
||||
"style_types": {
|
||||
"25d_animation": "2.5D動畫",
|
||||
"3d_cartoon": "3D卡通",
|
||||
"american_retro": "美式復古",
|
||||
"baroque": "巴洛克",
|
||||
"cartoon_illustration": "卡通插圖",
|
||||
"chinese_gongbi": "中國工筆",
|
||||
"clay": "黏土",
|
||||
"cyberpunk": "賽博龐克",
|
||||
"felt": "氈",
|
||||
"flat": "平的",
|
||||
"fresh_anime": "新鮮動漫",
|
||||
"ghibli": "吉卜力",
|
||||
"japanese_anime": "日本動畫",
|
||||
"little_people_book": "小小人書",
|
||||
"monet_garden": "莫內花園",
|
||||
"oil_painting": "油畫",
|
||||
"pixar": "皮克斯",
|
||||
"pixel_art": "像素藝術",
|
||||
"poetic_ancient": "詩意的古代",
|
||||
"psychedelic": "迷幻",
|
||||
"sketch": "草圖",
|
||||
"street_art": "街頭藝術",
|
||||
"texture": "質地",
|
||||
"ukiyo_e": "浮世繪",
|
||||
"watercolor": "水彩",
|
||||
"wood_carving": "木雕",
|
||||
"yarn_doll": "毛線娃娃"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"image_file": "編輯影像",
|
||||
"magic_prompt_option_tip": "開啟後會自動調整編輯提示詞,以提升效果",
|
||||
@ -2508,6 +2634,7 @@
|
||||
"style_type_tip": "產生圖片的風格,僅適用於 V_2 及以上版本",
|
||||
"width": "寬度"
|
||||
},
|
||||
"generate_failed": "無法生成圖片",
|
||||
"generated_image": "產生的圖片",
|
||||
"go_to_settings": "前往設定",
|
||||
"guidance_scale": "引導比例",
|
||||
@ -2518,6 +2645,7 @@
|
||||
"image_file_required": "請先上傳圖片",
|
||||
"image_file_retry": "請重新上傳圖片",
|
||||
"image_handle_required": "請先上傳圖片。",
|
||||
"image_mix_failed": "無法混合圖片",
|
||||
"image_placeholder": "無圖片",
|
||||
"image_retry": "重試",
|
||||
"image_size_options": {
|
||||
@ -2527,6 +2655,7 @@
|
||||
"inference_steps_tip": "要執行的推理步數。步數越多,品質越高但耗時越長",
|
||||
"input_image": "輸入圖片",
|
||||
"input_parameters": "輸入參數",
|
||||
"invalid_image_url": "圖片網址格式無效",
|
||||
"learn_more": "了解更多",
|
||||
"magic_prompt_option": "提示詞增強",
|
||||
"mode": {
|
||||
@ -2548,6 +2677,7 @@
|
||||
"no_image_generation_model": "暫無可用的圖片產生模型,請先新增模型並設定端點類型為 {{endpoint_type}}",
|
||||
"number_images": "張數",
|
||||
"number_images_tip": "一次產生的圖片數量 (1-4)",
|
||||
"operation_failed": "操作失敗,請稍後再試",
|
||||
"paint_course": "教學",
|
||||
"per_image": "每張圖片",
|
||||
"per_images": "每張圖片",
|
||||
@ -2623,6 +2753,26 @@
|
||||
"resemblance": "相似度",
|
||||
"resemblance_tip": "控制放大結果與原圖的相似程度",
|
||||
"seed_tip": "控制放大結果的隨機性"
|
||||
},
|
||||
"zhipu": {
|
||||
"custom_size_divisible": "自訂尺寸必須能被 16 整除",
|
||||
"custom_size_hint": "寬度和高度必須介於 512px 到 2048px 之間,可被 16 整除,且總像素數不得超過 2^21px。",
|
||||
"custom_size_pixels": "自訂尺寸的總像素數不可超過 2,097,152",
|
||||
"custom_size_range": "自訂尺寸必須介於 512 像素至 2048 像素之間",
|
||||
"custom_size_required": "請設定自訂寬度和高度",
|
||||
"image_sizes": {
|
||||
"1024x1024_default": "1024x1024(預設)",
|
||||
"1152x864": "1152x864",
|
||||
"1344x768": "1344x768",
|
||||
"1440x720": "1440x720",
|
||||
"720x1440": "720x1440",
|
||||
"768x1344": "768x1344",
|
||||
"864x1152": "864x1152"
|
||||
},
|
||||
"quality_options": {
|
||||
"hd": "HD",
|
||||
"standard_default": "標準(預設)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
@ -2633,6 +2783,20 @@
|
||||
"category": "類別",
|
||||
"commands": "指令",
|
||||
"confirm_uninstall": "確定要解除安裝 {{name}} 嗎?",
|
||||
"content_saved": "外掛內容已成功儲存",
|
||||
"detail": {
|
||||
"allowed_tools": "允許的工具",
|
||||
"author": "作者",
|
||||
"content": "內容",
|
||||
"description": "描述",
|
||||
"file": "檔案",
|
||||
"installed": "已安裝",
|
||||
"metadata": "詮釋資料",
|
||||
"size": "尺寸",
|
||||
"source": "來源",
|
||||
"tags": "標籤",
|
||||
"tools": "工具"
|
||||
},
|
||||
"install": "安裝",
|
||||
"install_plugins_from_browser": "瀏覽可用外掛以開始使用",
|
||||
"installing": "安裝中...",
|
||||
@ -2983,6 +3147,11 @@
|
||||
"summary": "總結",
|
||||
"translate": "翻譯"
|
||||
},
|
||||
"prompt": {
|
||||
"explain": "請說明以下內容。要求:以{{language}}回覆;不要包含任何對此提示的說明,直接給出回應:",
|
||||
"refine": "請優化或潤飾以 XML 標籤 <INPUT> 包覆的使用者輸入內容,同時保留原意與完整性。要求:輸出須與使用者輸入使用相同語言;不需包含本提示的任何說明,直接給出回應;勿輸出 XML 標籤,直接輸出優化後的內容:\n\n{{text}}",
|
||||
"summary": "請直接以{{language}}回覆,無須任何說明。"
|
||||
},
|
||||
"translate": {
|
||||
"smart_translate_tips": "智慧翻譯:內容將優先翻譯為目標語言;內容已是目標語言時,將翻譯為備用語言"
|
||||
},
|
||||
@ -4076,6 +4245,10 @@
|
||||
"jsonModeHint": "編輯 MCP 伺服器設定的 JSON 表示。儲存前請確認格式正確",
|
||||
"jsonSaveError": "儲存 JSON 設定失敗",
|
||||
"jsonSaveSuccess": "JSON 設定已儲存",
|
||||
"lanyun": {
|
||||
"description": "藍雲科技雲平台MCP服務",
|
||||
"name": "藍雲科技"
|
||||
},
|
||||
"logoUrl": "Logo URL",
|
||||
"logs": "日誌",
|
||||
"longRunning": "長時間運作模式",
|
||||
@ -4172,6 +4345,14 @@
|
||||
"getToken": "取得 API 權杖",
|
||||
"getTokenDescription": "從帳戶取得個人 API 權杖",
|
||||
"noServersAvailable": "無可用的 MCP 伺服器",
|
||||
"providerDescriptions": {
|
||||
"302ai": "302.AI 平台 MCP 服務",
|
||||
"bailian": "阿里雲百鍊平台 MCP 服務",
|
||||
"lanyun": "藍雲科技雲平台 MCP 服務",
|
||||
"mcprouter": "MCP 路由器平台 MCP 服務",
|
||||
"modelscope": "ModelScope平台MCP服務",
|
||||
"tokenflux": "TokenFlux 平台 MCP 服務"
|
||||
},
|
||||
"selectProvider": "選擇提供者:",
|
||||
"setToken": "輸入權杖",
|
||||
"success": "同步 MCP 伺服器成功",
|
||||
@ -4649,6 +4830,9 @@
|
||||
"title": "刪除提供者"
|
||||
},
|
||||
"dmxapi": {
|
||||
"platform_enterprise": "ssvip.DMXAPI.com(企業版)",
|
||||
"platform_international": "www.DMXAPI.com(國際)",
|
||||
"platform_official": "www.DMXAPI.cn(人民幣)",
|
||||
"select_platform": "選擇平台"
|
||||
},
|
||||
"docs_check": "檢查",
|
||||
@ -4985,6 +5169,13 @@
|
||||
"show": "顯示系統匣圖示",
|
||||
"title": "系統匣"
|
||||
},
|
||||
"use_system_title_bar": {
|
||||
"confirm": {
|
||||
"content": "變更標題列樣式需要重新啟動應用程式才能生效。您要立即重新啟動嗎?",
|
||||
"title": "需要重新啟動"
|
||||
},
|
||||
"title": "使用系統標題列(Linux)"
|
||||
},
|
||||
"zoom": {
|
||||
"reset": "重設",
|
||||
"title": "縮放"
|
||||
|
||||
@ -271,6 +271,7 @@
|
||||
},
|
||||
"toolPermission": {
|
||||
"aria": {
|
||||
"allowAllRequest": "Dieses Tool immer erlauben",
|
||||
"allowRequest": "Werkzeuganfrage zulassen",
|
||||
"denyRequest": "Werkzeuganfrage ablehnen",
|
||||
"hideDetails": "Werkzeugdetails ausblenden",
|
||||
@ -278,6 +279,7 @@
|
||||
"showDetails": "Zeige Werkzeugdetails"
|
||||
},
|
||||
"button": {
|
||||
"allowAll": "Immer erlauben",
|
||||
"cancel": "Abbrechen",
|
||||
"run": "Laufen"
|
||||
},
|
||||
@ -667,6 +669,10 @@
|
||||
"title": "Neues Thema erstellen"
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"create_session": "Erstelle eine Sitzung",
|
||||
"select_agent": "Wählen Sie einen Agenten"
|
||||
},
|
||||
"artifacts": {
|
||||
"button": {
|
||||
"download": "Herunterladen",
|
||||
@ -1200,6 +1206,8 @@
|
||||
"cancel": "Abbrechen",
|
||||
"chat": "Chat",
|
||||
"clear": "Löschen",
|
||||
"clear_all": "Alles löschen",
|
||||
"click_to_replace": "Klicken zum Ersetzen",
|
||||
"close": "Schließen",
|
||||
"collapse": "Einklappen",
|
||||
"completed": "Abgeschlossen",
|
||||
@ -1234,9 +1242,15 @@
|
||||
"footnote": "Zitierte Inhalte",
|
||||
"footnotes": "Zitierte Inhalte",
|
||||
"fullscreen": "Vollbildmodus aktiviert, F11 zum Beenden",
|
||||
"generate_random_seed": "Zufälligen Seed generieren",
|
||||
"get_embedding_dimension": "Abruf der Einbettungsdimension",
|
||||
"go_to_settings": "Zu Einstellungen",
|
||||
"html_preview": "HTML-Vorschau",
|
||||
"i_know": "Verstanden",
|
||||
"ignore": "Ignorieren",
|
||||
"image_preview": "Bildvorschau",
|
||||
"image_url": "Bild-URL",
|
||||
"image_url_or_upload": "Gib Bild-URL ein oder lade Datei hoch",
|
||||
"inspect": "Prüfen",
|
||||
"invalid_value": "Ungültiger Wert",
|
||||
"knowledge_base": "Wissensdatenbank",
|
||||
@ -1246,6 +1260,7 @@
|
||||
"models": "Modelle",
|
||||
"more": "Mehr",
|
||||
"name": "Name",
|
||||
"next_match": "Nächstes Spiel",
|
||||
"no_results": "Keine Ergebnisse",
|
||||
"none": "Keine",
|
||||
"off": "Aus",
|
||||
@ -1257,13 +1272,17 @@
|
||||
"model": "Modell auswählen"
|
||||
}
|
||||
},
|
||||
"powered_by": "Bereitgestellt von",
|
||||
"preview": "Vorschau",
|
||||
"previous_match": "Vorheriges Spiel",
|
||||
"prompt": "Prompt",
|
||||
"provider": "Anbieter",
|
||||
"reasoning_content": "Tiefgehend nachgedacht",
|
||||
"refresh": "Aktualisieren",
|
||||
"regenerate": "Neu generieren",
|
||||
"remove_image": "Bild entfernen",
|
||||
"rename": "Umbenennen",
|
||||
"required_field": "Pflichtfeld",
|
||||
"reset": "Zurücksetzen",
|
||||
"save": "Speichern",
|
||||
"saved": "Gespeichert",
|
||||
@ -1286,14 +1305,29 @@
|
||||
"success": "Erfolgreich",
|
||||
"swap": "Tauschen",
|
||||
"topics": "Themen",
|
||||
"translate_text": "Text übersetzen",
|
||||
"unknown": "Unbekannt",
|
||||
"unnamed": "Unbenannt",
|
||||
"unsubscribe": "Abmelden",
|
||||
"update_success": "Erfolgreich aktualisiert",
|
||||
"upload_files": "Dateien hochladen",
|
||||
"upload_image": "Bilddatei hochladen",
|
||||
"uploaded_image": "Hochgeladenes Bild",
|
||||
"warning": "Warnung",
|
||||
"you": "Sie"
|
||||
},
|
||||
"dialog": {
|
||||
"all_files": "Alle Dateien",
|
||||
"html_files": "HTML-Dateien",
|
||||
"open_file": "Datei öffnen",
|
||||
"pdf_files": "PDF-Dateien",
|
||||
"png_image": "PNG-Bild",
|
||||
"save_as_html": "Als HTML speichern",
|
||||
"save_as_pdf": "Als PDF speichern",
|
||||
"save_file": "Datei speichern",
|
||||
"select_folder": "Ordner auswählen",
|
||||
"word_document": "Word-Dokument"
|
||||
},
|
||||
"docs": {
|
||||
"title": "Hilfedokumentation"
|
||||
},
|
||||
@ -2015,10 +2049,65 @@
|
||||
"cancelled": "Abgebrochen",
|
||||
"completed": "Abgeschlossen",
|
||||
"error": "Fehler aufgetreten",
|
||||
"groupHeader": "{{count}} Toolaufrufe",
|
||||
"invoking": "Wird aufgerufen",
|
||||
"labels": {
|
||||
"bash": "Bash",
|
||||
"edit": "Bearbeiten",
|
||||
"exitPlanMode": "ExitPlanModus",
|
||||
"glob": "Globus",
|
||||
"grep": "Grep",
|
||||
"mcpServerTool": "MCP-Server-Tool",
|
||||
"multiEdit": "MultiEdit",
|
||||
"notebookEdit": "NotizbuchBearbeiten",
|
||||
"readFile": "Datei lesen",
|
||||
"search": "Suche",
|
||||
"skill": "Fähigkeit",
|
||||
"task": "Aufgabe",
|
||||
"todoWrite": "Alles schreiben",
|
||||
"tool": "Werkzeug",
|
||||
"webFetch": "Web abrufen",
|
||||
"webSearch": "Websuche",
|
||||
"write": "Schreiben"
|
||||
},
|
||||
"noData": "Keine Daten für dieses Tool verfügbar",
|
||||
"pending": "Wartend",
|
||||
"preview": "Vorschau",
|
||||
"raw": "Roh"
|
||||
"raw": "Roh",
|
||||
"runningCount": "{{count}} Werkzeuge laufen",
|
||||
"sections": {
|
||||
"command": "Befehl",
|
||||
"exitCode": "Exit-Code",
|
||||
"input": "Eingabe",
|
||||
"output": "Ausgabe",
|
||||
"prompt": "Aufforderung",
|
||||
"searchQuery": "Suchbegriff",
|
||||
"searchResults": "Suchergebnisse",
|
||||
"stderr": "stderr",
|
||||
"stdout": "stdout"
|
||||
},
|
||||
"status": {
|
||||
"done": "Erledigt",
|
||||
"error": "Fehler",
|
||||
"failed": "Fehlgeschlagen",
|
||||
"running": "Laufen",
|
||||
"success": "Erfolg"
|
||||
},
|
||||
"truncated": "Ausgabe gekürzt (Original: {{size}})",
|
||||
"units": {
|
||||
"done_one": "{{count}} Erledigt",
|
||||
"done_other": "{{count}} Erledigt",
|
||||
"file_one": "{{count}} Datei",
|
||||
"file_other": "{{count}} Dateien",
|
||||
"item_one": "{{count}} Artikel",
|
||||
"item_other": "{{count}} Artikel",
|
||||
"line_one": "{{count}} Zeile",
|
||||
"line_other": "{{count}} Zeilen",
|
||||
"plan_one": "{{count}} Plan",
|
||||
"plan_other": "{{count}} Pläne",
|
||||
"result_one": "{{count}} Ergebnis",
|
||||
"result_other": "{{count}} Ergebnisse"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"added": "Thema erfolgreich hinzugefügt"
|
||||
@ -2226,14 +2315,18 @@
|
||||
"drop_markdown_hint": ".md-Datei oder Verzeichnis hierher ziehen zum Importieren",
|
||||
"empty": "Keine Notizen vorhanden",
|
||||
"expand": "Ausklappen",
|
||||
"exportToWord": "In Word exportieren",
|
||||
"export_failed": "Export in Wissensdatenbank fehlgeschlagen",
|
||||
"export_knowledge": "Notiz in Wissensdatenbank exportieren",
|
||||
"export_success": "Erfolgreich in Wissensdatenbank exportiert",
|
||||
"export_to_word_failed": "Export nach Word fehlgeschlagen",
|
||||
"folder": "Ordner",
|
||||
"new_folder": "Neuer Ordner",
|
||||
"new_note": "Neue Notiz",
|
||||
"no_content_to_copy": "Kein Inhalt zum Kopieren",
|
||||
"no_content_to_export": "Kein Inhalt zum Exportieren",
|
||||
"no_file_selected": "Bitte Datei zum Hochladen auswählen",
|
||||
"no_note_selected": "Bitte wählen Sie zuerst eine Notiz aus",
|
||||
"no_valid_files": "Keine gültigen Dateien hochgeladen",
|
||||
"open_folder": "Externen Ordner öffnen",
|
||||
"open_outside": "Extern öffnen",
|
||||
@ -2484,6 +2577,39 @@
|
||||
}
|
||||
},
|
||||
"custom_size": "Benutzerdefinierte Größe",
|
||||
"dmxapi": {
|
||||
"generating_tip": "Generierung mit dem offiziellen Modell, geschätzte Wartezeit beträgt 2–5 Minuten für beste Ergebnisse. Bitte überprüfe die DMXAPI-Backend-Logs für die Kosten dieses Vorgangs.",
|
||||
"style": ", Stil:",
|
||||
"style_types": {
|
||||
"25d_animation": "2.5D-Animation",
|
||||
"3d_cartoon": "3D-Cartoon",
|
||||
"american_retro": "Amerikanisches Retro",
|
||||
"baroque": "Barock",
|
||||
"cartoon_illustration": "Cartoon-Illustration",
|
||||
"chinese_gongbi": "Chinesische Gongbi",
|
||||
"clay": "Ton",
|
||||
"cyberpunk": "Cyberpunk",
|
||||
"felt": "Filz",
|
||||
"flat": "Flach",
|
||||
"fresh_anime": "Frisches Anime",
|
||||
"ghibli": "Ghibli",
|
||||
"japanese_anime": "Japanischer Anime",
|
||||
"little_people_book": "Kleine-Leute-Buch",
|
||||
"monet_garden": "Monet-Garten",
|
||||
"oil_painting": "Ölgemälde",
|
||||
"pixar": "Pixar",
|
||||
"pixel_art": "Pixelkunst",
|
||||
"poetic_ancient": "Poetisch Antik",
|
||||
"psychedelic": "Psychedelisch",
|
||||
"sketch": "Skizze",
|
||||
"street_art": "Street Art",
|
||||
"texture": "Textur",
|
||||
"ukiyo_e": "Ukiyo-e",
|
||||
"watercolor": "Aquarell",
|
||||
"wood_carving": "Holzschnitzerei",
|
||||
"yarn_doll": "Garnpuppe"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"image_file": "Zu bearbeitendes Bild",
|
||||
"magic_prompt_option_tip": "Intelligente Optimierung des Bearbeitungs-Prompts",
|
||||
@ -2508,6 +2634,7 @@
|
||||
"style_type_tip": "Bildgenerierungsstil, nur für V_2 und höher",
|
||||
"width": "Breite"
|
||||
},
|
||||
"generate_failed": "Fehler beim Generieren des Bildes",
|
||||
"generated_image": "Generiertes Bild",
|
||||
"go_to_settings": "Zu Einstellungen",
|
||||
"guidance_scale": "Guidance-Skala",
|
||||
@ -2518,6 +2645,7 @@
|
||||
"image_file_required": "Bitte laden Sie zuerst ein Bild hoch",
|
||||
"image_file_retry": "Bitte laden Sie das Bild erneut hoch",
|
||||
"image_handle_required": "Bitte laden Sie zuerst ein Bild hoch",
|
||||
"image_mix_failed": "Fehler beim Mischen der Bilder",
|
||||
"image_placeholder": "Kein Bild vorhanden",
|
||||
"image_retry": "Wiederholen",
|
||||
"image_size_options": {
|
||||
@ -2527,6 +2655,7 @@
|
||||
"inference_steps_tip": "Anzahl der auszuführenden Inference-Schritte. Mehr Schritte bedeuten höhere Qualität, aber längere Dauer",
|
||||
"input_image": "Eingabebild",
|
||||
"input_parameters": "Eingabeparameter",
|
||||
"invalid_image_url": "Ungültiges Bild-URL-Format",
|
||||
"learn_more": "Mehr erfahren",
|
||||
"magic_prompt_option": "Prompt-Verbesserung",
|
||||
"mode": {
|
||||
@ -2548,6 +2677,7 @@
|
||||
"no_image_generation_model": "Kein Bildgenerierungsmodell verfügbar. Bitte fügen Sie ein Modell hinzu und setzen Sie den Endpunkttyp auf {{endpoint_type}}",
|
||||
"number_images": "Generierungsanzahl",
|
||||
"number_images_tip": "Anzahl der Bilder pro Generierung (1-4)",
|
||||
"operation_failed": "Vorgang fehlgeschlagen, bitte versuchen Sie es später erneut",
|
||||
"paint_course": "Tutorial",
|
||||
"per_image": "Pro Bild",
|
||||
"per_images": "Pro Bild",
|
||||
@ -2623,6 +2753,26 @@
|
||||
"resemblance": "Ähnlichkeit",
|
||||
"resemblance_tip": "Ähnlichkeitsgrad des Upscale-Ergebnisses zum Originalbild kontrollieren",
|
||||
"seed_tip": "Kontrolle der Zufälligkeit des Upscale-Ergebnisses"
|
||||
},
|
||||
"zhipu": {
|
||||
"custom_size_divisible": "Benutzerdefinierte Größe muss durch 16 teilbar sein",
|
||||
"custom_size_hint": "Breite und Höhe müssen zwischen 512 px und 2048 px liegen, durch 16 teilbar sein, und die Gesamtzahl der Pixel darf 2^21 px nicht überschreiten.",
|
||||
"custom_size_pixels": "Die Gesamtpixel der benutzerdefinierten Größe dürfen 2.097.152 nicht überschreiten",
|
||||
"custom_size_range": "Benutzerdefinierte Größe muss zwischen 512px und 2048px liegen",
|
||||
"custom_size_required": "Bitte benutzerdefinierte Breite und Höhe einstellen",
|
||||
"image_sizes": {
|
||||
"1024x1024_default": "1024x1024 (Standard)",
|
||||
"1152x864": "1152x864",
|
||||
"1344x768": "1344x768",
|
||||
"1440x720": "1440x720",
|
||||
"720x1440": "720x1440",
|
||||
"768x1344": "768x1344",
|
||||
"864x1152": "864x1152"
|
||||
},
|
||||
"quality_options": {
|
||||
"hd": "HD",
|
||||
"standard_default": "Standard (Standard)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
@ -2633,6 +2783,20 @@
|
||||
"category": "Kategorie",
|
||||
"commands": "Befehle",
|
||||
"confirm_uninstall": "Sind Sie sicher, dass Sie {{name}} deinstallieren möchten?",
|
||||
"content_saved": "Plugin-Inhalt erfolgreich gespeichert",
|
||||
"detail": {
|
||||
"allowed_tools": "Erlaubte Werkzeuge",
|
||||
"author": "Autor",
|
||||
"content": "Inhalt",
|
||||
"description": "Beschreibung",
|
||||
"file": "Datei",
|
||||
"installed": "Installiert",
|
||||
"metadata": "Metadaten",
|
||||
"size": "Größe",
|
||||
"source": "Quelle",
|
||||
"tags": "Tags",
|
||||
"tools": "Werkzeuge"
|
||||
},
|
||||
"install": "Installieren",
|
||||
"install_plugins_from_browser": "Durchsuche verfügbare Plugins, um loszulegen",
|
||||
"installing": "Installiere…",
|
||||
@ -2983,6 +3147,11 @@
|
||||
"summary": "Zusammenfassen",
|
||||
"translate": "Übersetzen"
|
||||
},
|
||||
"prompt": {
|
||||
"explain": "Bitte erklären Sie den folgenden Inhalt. Anforderungen: Antworten Sie auf Deutsch; geben Sie keine Erklärung zu dieser Aufforderung, sondern antworten Sie direkt.",
|
||||
"refine": "Bitte optimieren oder überarbeiten Sie die im XML-Tag <INPUT> eingeschlossene Nutzereingabe, wobei die Bedeutung und Integrität des ursprünglichen Inhalts erhalten bleiben sollen. Anforderungen: Ihre Ausgabe sollte in derselben Sprache wie die Nutzereingabe erfolgen; geben Sie keine Erklärung zu dieser Aufforderung an, sondern liefern Sie direkt die Antwort; geben Sie keine XML-Tags aus, sondern geben Sie den optimierten Inhalt direkt aus:\n\n<INPUT>{{text}}</INPUT>",
|
||||
"summary": "Bitte fasse den folgenden Inhalt zusammen. Anforderungen: Antworte auf {{language}}; gib keine Erklärung zu dieser Aufforderung, sondern antworte direkt."
|
||||
},
|
||||
"translate": {
|
||||
"smart_translate_tips": "Intelligente Übersetzung: Inhalt wird bevorzugt in Zielsprache übersetzt; wenn Inhalt bereits in Zielsprache, Übersetzung in Alternativsprache"
|
||||
},
|
||||
@ -4076,6 +4245,10 @@
|
||||
"jsonModeHint": "JSON-Darstellung der MCP-Server-Konfiguration. Bitte sicherstellen, dass das Format korrekt ist, bevor gespeichert wird",
|
||||
"jsonSaveError": "JSON-Konfiguration speichern fehlgeschlagen",
|
||||
"jsonSaveSuccess": "JSON-Konfiguration erfolgreich gespeichert",
|
||||
"lanyun": {
|
||||
"description": "Lanyun Technology Cloud Platform MCP Service",
|
||||
"name": "Lanyun Technology"
|
||||
},
|
||||
"logoUrl": "Logo-URL",
|
||||
"logs": "Protokolle",
|
||||
"longRunning": "Lang laufender Modus",
|
||||
@ -4172,6 +4345,14 @@
|
||||
"getToken": "API-Token abrufen",
|
||||
"getTokenDescription": "Persönlichen API-Token aus Ihrem Konto abrufen",
|
||||
"noServersAvailable": "Keine MCP-Server verfügbar",
|
||||
"providerDescriptions": {
|
||||
"302ai": "302.AI-Plattform MCP-Dienst",
|
||||
"bailian": "Alibaba Cloud Bailian Platform MCP-Dienst",
|
||||
"lanyun": "Lanyun Technology Cloud Platform MCP-Dienst",
|
||||
"mcprouter": "MCP-Router-Plattform MCP-Service",
|
||||
"modelscope": "ModelScope-Plattform MCP-Dienst",
|
||||
"tokenflux": "TokenFlux Platform MCP Service"
|
||||
},
|
||||
"selectProvider": "Anbieter auswählen:",
|
||||
"setToken": "Ihren Token eingeben",
|
||||
"success": "MCP-Server erfolgreich synchronisiert",
|
||||
@ -4649,6 +4830,9 @@
|
||||
"title": "Anbieter löschen"
|
||||
},
|
||||
"dmxapi": {
|
||||
"platform_enterprise": "ssvip.DMXAPI.com (Unternehmen)",
|
||||
"platform_international": "www.DMXAPI.com (International)",
|
||||
"platform_official": "www.DMXAPI.cn (CNY)",
|
||||
"select_platform": "Plattform auswählen"
|
||||
},
|
||||
"docs_check": "Anzeigen",
|
||||
@ -4985,6 +5169,13 @@
|
||||
"show": "Tray-Symbol anzeigen",
|
||||
"title": "Tray"
|
||||
},
|
||||
"use_system_title_bar": {
|
||||
"confirm": {
|
||||
"content": "Das Ändern des Titelleistenstils erfordert einen Neustart der App, damit die Änderung wirksam wird. Möchten Sie jetzt neu starten?",
|
||||
"title": "Neustart erforderlich"
|
||||
},
|
||||
"title": "System-Titelleiste verwenden (Linux)"
|
||||
},
|
||||
"zoom": {
|
||||
"reset": "Zurücksetzen",
|
||||
"title": "Zoom"
|
||||
|
||||
@ -271,6 +271,7 @@
|
||||
},
|
||||
"toolPermission": {
|
||||
"aria": {
|
||||
"allowAllRequest": "Να επιτρέπεται πάντα αυτό το εργαλείο",
|
||||
"allowRequest": "Επίτρεψη αίτησης εργαλείου",
|
||||
"denyRequest": "Απόρριψη αιτήματος εργαλείου",
|
||||
"hideDetails": "Απόκρυψη λεπτομερειών εργαλείου",
|
||||
@ -278,6 +279,7 @@
|
||||
"showDetails": "Εμφάνιση λεπτομερειών εργαλείου"
|
||||
},
|
||||
"button": {
|
||||
"allowAll": "Να Επιτρέπεται Πάντα",
|
||||
"cancel": "Ακύρωση",
|
||||
"run": "Τρέξε"
|
||||
},
|
||||
@ -667,6 +669,10 @@
|
||||
"title": "Δημιουργία νέου θέματος"
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"create_session": "Δημιουργία συνεδρίας",
|
||||
"select_agent": "Επιλέξτε έναν πράκτορα"
|
||||
},
|
||||
"artifacts": {
|
||||
"button": {
|
||||
"download": "Λήψη",
|
||||
@ -1200,6 +1206,8 @@
|
||||
"cancel": "Άκυρο",
|
||||
"chat": "Συζήτηση",
|
||||
"clear": "Καθαρισμός",
|
||||
"clear_all": "Εκκαθάριση Όλων",
|
||||
"click_to_replace": "Κάντε κλικ για αντικατάσταση",
|
||||
"close": "Κλείσιμο",
|
||||
"collapse": "Σύμπτυξη",
|
||||
"completed": "Ολοκληρώθηκε",
|
||||
@ -1234,9 +1242,15 @@
|
||||
"footnote": "Παραπομπή",
|
||||
"footnotes": "Παραπομπές",
|
||||
"fullscreen": "Εισήχθη σε πλήρη οθόνη, πατήστε F11 για να έξω",
|
||||
"generate_random_seed": "Δημιουργία τυχαίου σπόρου",
|
||||
"get_embedding_dimension": "Λήψη διάστασης ενσωμάτωσης",
|
||||
"go_to_settings": "Πηγαίνετε στις ρυθμίσεις",
|
||||
"html_preview": "Προεπισκόπηση HTML",
|
||||
"i_know": "Το έχω καταλάβει",
|
||||
"ignore": "Αγνόησε",
|
||||
"image_preview": "Προεπισκόπηση εικόνας",
|
||||
"image_url": "Διεύθυνση URL εικόνας",
|
||||
"image_url_or_upload": "Εισαγάγετε διεύθυνση URL εικόνας ή ανεβάστε αρχείο",
|
||||
"inspect": "Επιθεώρηση",
|
||||
"invalid_value": "Μη έγκυρη τιμή",
|
||||
"knowledge_base": "Βάση Γνώσεων",
|
||||
@ -1246,6 +1260,7 @@
|
||||
"models": "Μοντέλα",
|
||||
"more": "Περισσότερα",
|
||||
"name": "Όνομα",
|
||||
"next_match": "Επόμενος αγώνας",
|
||||
"no_results": "Δεν βρέθηκαν αποτελέσματα",
|
||||
"none": "Χωρίς",
|
||||
"off": "Κλειστό",
|
||||
@ -1257,13 +1272,17 @@
|
||||
"model": "Επιλέξτε μοντέλο"
|
||||
}
|
||||
},
|
||||
"powered_by": "Με την υποστήριξη της",
|
||||
"preview": "Προεπισκόπηση",
|
||||
"previous_match": "Προηγούμενος αγώνας",
|
||||
"prompt": "Ενδεικτικός ρήματος",
|
||||
"provider": "Παρέχων",
|
||||
"reasoning_content": "Έχει σκεφτεί πολύ καλά",
|
||||
"refresh": "Ανανέωση",
|
||||
"regenerate": "Ξαναπαραγωγή",
|
||||
"remove_image": "Αφαίρεση εικόνας",
|
||||
"rename": "Μετονομασία",
|
||||
"required_field": "Υποχρεωτικό πεδίο",
|
||||
"reset": "Επαναφορά",
|
||||
"save": "Αποθήκευση",
|
||||
"saved": "Αποθηκεύτηκε",
|
||||
@ -1286,14 +1305,29 @@
|
||||
"success": "Επιτυχία",
|
||||
"swap": "Εναλλαγή",
|
||||
"topics": "Θέματα",
|
||||
"translate_text": "Μετάφραση κειμένου",
|
||||
"unknown": "Άγνωστο",
|
||||
"unnamed": "Χωρίς όνομα",
|
||||
"unsubscribe": "Απεγγραφή",
|
||||
"update_success": "Επιτυχής ενημέρωση",
|
||||
"upload_files": "Ανέβασμα αρχείου",
|
||||
"upload_image": "Μεταφόρτωση αρχείου εικόνας",
|
||||
"uploaded_image": "Μεταφορτωμένη εικόνα",
|
||||
"warning": "Προσοχή",
|
||||
"you": "Εσείς"
|
||||
},
|
||||
"dialog": {
|
||||
"all_files": "Όλα τα Αρχεία",
|
||||
"html_files": "Αρχεία HTML",
|
||||
"open_file": "Άνοιγμα Αρχείου",
|
||||
"pdf_files": "Αρχεία PDF",
|
||||
"png_image": "Εικόνα PNG",
|
||||
"save_as_html": "Αποθήκευση ως HTML",
|
||||
"save_as_pdf": "Αποθήκευση ως PDF",
|
||||
"save_file": "Αποθήκευση Αρχείου",
|
||||
"select_folder": "Επιλέξτε φάκελο",
|
||||
"word_document": "Έγγραφο Word"
|
||||
},
|
||||
"docs": {
|
||||
"title": "Βοήθεια"
|
||||
},
|
||||
@ -2015,10 +2049,65 @@
|
||||
"cancelled": "Ακυρώθηκε",
|
||||
"completed": "Ολοκληρώθηκε",
|
||||
"error": "Προέκυψε σφάλμα",
|
||||
"groupHeader": "{{count}} κλήσεις εργαλείων",
|
||||
"invoking": "κλήση σε εξέλιξη",
|
||||
"labels": {
|
||||
"bash": "Μπας",
|
||||
"edit": "Επεξεργασία",
|
||||
"exitPlanMode": "ΛειτουργίαΈξοδουΣχεδίου",
|
||||
"glob": "Σφαίρα",
|
||||
"grep": "Grep",
|
||||
"mcpServerTool": "Εργαλείο Διακομιστή MCP",
|
||||
"multiEdit": "ΠολλαπλήΕπεξεργασία",
|
||||
"notebookEdit": "ΣημειωματάριοΕπεξεργασία",
|
||||
"readFile": "Διάβασε Αρχείο",
|
||||
"search": "Αναζήτηση",
|
||||
"skill": "Δεξιότητα",
|
||||
"task": "Εργασία",
|
||||
"todoWrite": "Να γράψεις",
|
||||
"tool": "Εργαλείο",
|
||||
"webFetch": "Ανάκτηση Ιστού",
|
||||
"webSearch": "Αναζήτηση στον Ιστό",
|
||||
"write": "Γράψε"
|
||||
},
|
||||
"noData": "Δεν υπάρχουν διαθέσιμα δεδομένα για αυτό το εργαλείο",
|
||||
"pending": "Εκκρεμεί",
|
||||
"preview": "Προεπισκόπηση",
|
||||
"raw": "Ακατέργαστο"
|
||||
"raw": "Ακατέργαστο",
|
||||
"runningCount": "{{count}} εργαλεία εκτελούνται",
|
||||
"sections": {
|
||||
"command": "Εντολή",
|
||||
"exitCode": "Κωδικός Εξόδου",
|
||||
"input": "Εισαγωγή",
|
||||
"output": "Έξοδος",
|
||||
"prompt": "Προτροπή",
|
||||
"searchQuery": "ερώτημα αναζήτησης",
|
||||
"searchResults": "Αποτελέσματα Αναζήτησης",
|
||||
"stderr": "stderr",
|
||||
"stdout": "stdout"
|
||||
},
|
||||
"status": {
|
||||
"done": "Έγινε",
|
||||
"error": "Σφάλμα",
|
||||
"failed": "Απέτυχε",
|
||||
"running": "Τρέξιμο",
|
||||
"success": "Επιτυχία"
|
||||
},
|
||||
"truncated": "Η έξοδος περικόπηκε (πρωτότυπο: {{size}})",
|
||||
"units": {
|
||||
"done_one": "{{count}} Ολοκληρώθηκε",
|
||||
"done_other": "{{count}} Ολοκληρώθηκαν",
|
||||
"file_one": "{{count}} αρχείο",
|
||||
"file_other": "{{count}} αρχεία",
|
||||
"item_one": "{{count}} αντικείμενο",
|
||||
"item_other": "{{count}} αντικείμενα",
|
||||
"line_one": "{{count}} γραμμή",
|
||||
"line_other": "{{count}} γραμμές",
|
||||
"plan_one": "{{count}} σχέδιο",
|
||||
"plan_other": "{{count}} σχέδια",
|
||||
"result_one": "{{count}} αποτέλεσμα",
|
||||
"result_other": "{{count}} αποτελέσματα"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"added": "Η θεματική προστέθηκε επιτυχώς"
|
||||
@ -2226,14 +2315,18 @@
|
||||
"drop_markdown_hint": "Σύρετε και αποθέστε αρχεία ή φακέλους .md εδώ για εισαγωγή",
|
||||
"empty": "δεν υπάρχει σημείωση για τώρα",
|
||||
"expand": "να ανοίξει",
|
||||
"exportToWord": "Εξαγωγή σε Word",
|
||||
"export_failed": "Εξαγωγή στη βάση γνώσης απέτυχε",
|
||||
"export_knowledge": "εξαγωγή σημειώσεων στη βάση γνώσης",
|
||||
"export_success": "Επιτυχής εξαγωγή στην βάση γνώσης",
|
||||
"export_to_word_failed": "Αποτυχία εξαγωγής στο Word",
|
||||
"folder": "φάκελος",
|
||||
"new_folder": "Νέος φάκελος",
|
||||
"new_note": "Δημιουργία νέας σημείωσης",
|
||||
"no_content_to_copy": "Δεν υπάρχει περιεχόμενο προς αντιγραφή",
|
||||
"no_content_to_export": "Κανένα περιεχόμενο προς εξαγωγή",
|
||||
"no_file_selected": "Επιλέξτε το αρχείο για μεταφόρτωση",
|
||||
"no_note_selected": "Παρακαλώ επιλέξτε πρώτα μια σημείωση",
|
||||
"no_valid_files": "Δεν ανέβηκε έγκυρο αρχείο",
|
||||
"open_folder": "Άνοιγμα εξωτερικού φακέλου",
|
||||
"open_outside": "Από το εξωτερικό",
|
||||
@ -2484,6 +2577,39 @@
|
||||
}
|
||||
},
|
||||
"custom_size": "Προσαρμοσμένο μέγεθος",
|
||||
"dmxapi": {
|
||||
"generating_tip": "Η δημιουργία γίνεται με το επίσημο μοντέλο, ο εκτιμώμενος χρόνος αναμονής είναι 2-5 λεπτά για τα καλύτερα αποτελέσματα. Ελέγξτε τα αρχεία καταγραφής του διακομιστή DMXAPI για το κόστος αυτής της λειτουργίας.",
|
||||
"style": ", Στυλ:",
|
||||
"style_types": {
|
||||
"25d_animation": "Κινούμενα Σχέδια 2.5D",
|
||||
"3d_cartoon": "3D Καρτούν",
|
||||
"american_retro": "Αμερικανικό Ρετρό",
|
||||
"baroque": "Μπαρόκ",
|
||||
"cartoon_illustration": "Κινούμενη εικονογράφηση",
|
||||
"chinese_gongbi": "Κινέζικο Γκόνγκμπι",
|
||||
"clay": "Πηλός",
|
||||
"cyberpunk": "Κυβερνοπάνκ",
|
||||
"felt": "Ύφασμα",
|
||||
"flat": "Flat",
|
||||
"fresh_anime": "Φρέσκο Anime",
|
||||
"ghibli": "Γκίμπλι",
|
||||
"japanese_anime": "Ιαπωνικό Άνιμε",
|
||||
"little_people_book": "Βιβλίο για τους Μικρούς Ανθρώπους",
|
||||
"monet_garden": "Κήπος Μονέ",
|
||||
"oil_painting": "Ζωγραφική με λάδι",
|
||||
"pixar": "Pixar",
|
||||
"pixel_art": "Τέχνη των Πίξελ",
|
||||
"poetic_ancient": "Ποιητικό Αρχαίο",
|
||||
"psychedelic": "Ψυχεδελικό",
|
||||
"sketch": "Σχέδιο",
|
||||
"street_art": "Τέχνη του Δρόμου",
|
||||
"texture": "Υφή",
|
||||
"ukiyo_e": "Ουκιγιό-ε",
|
||||
"watercolor": "Νερομπογιά",
|
||||
"wood_carving": "Ξυλογλυπτική",
|
||||
"yarn_doll": "Κούκλα από κλωστή"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"image_file": "Επεξεργασμένη εικόνα",
|
||||
"magic_prompt_option_tip": "Έξυπνη βελτιστοποίηση της πρότασης επεξεργασίας",
|
||||
@ -2508,6 +2634,7 @@
|
||||
"style_type_tip": "Στυλ δημιουργίας εικόνας, ισχύει μόνο για την έκδοση V_2 και μεταγενέστερες",
|
||||
"width": "Πλάτος"
|
||||
},
|
||||
"generate_failed": "Αποτυχία δημιουργίας εικόνας",
|
||||
"generated_image": "Δημιουργία εικόνας",
|
||||
"go_to_settings": "Πηγαίνετε στις ρυθμίσεις",
|
||||
"guidance_scale": "Κλίμακα προσαρμογής",
|
||||
@ -2518,6 +2645,7 @@
|
||||
"image_file_required": "Παρακαλώ ανεβάστε πρώτα μια εικόνα",
|
||||
"image_file_retry": "Παρακαλώ ανεβάστε ξανά την εικόνα",
|
||||
"image_handle_required": "Παρακαλώ ανεβάστε πρώτα μια εικόνα",
|
||||
"image_mix_failed": "Αποτυχία ανάμειξης εικόνων",
|
||||
"image_placeholder": "Δεν υπάρχει εικόνα για τη στιγμή",
|
||||
"image_retry": "Δοκιμάστε ξανά",
|
||||
"image_size_options": {
|
||||
@ -2527,6 +2655,7 @@
|
||||
"inference_steps_tip": "Το πλήθος των βημάτων επεξεργασίας που πρέπει να εκτελεστούν. Περισσότερα βήματα = χαμηλότερη ποιότητα και μεγαλύτερος χρόνος εκτέλεσης",
|
||||
"input_image": "Εικόνα εισόδου",
|
||||
"input_parameters": "Παράμετροι εισόδου",
|
||||
"invalid_image_url": "Μη έγκυρη μορφή διεύθυνσης URL εικόνας",
|
||||
"learn_more": "Μάθετε περισσότερα",
|
||||
"magic_prompt_option": "Ενίσχυση προτροπής",
|
||||
"mode": {
|
||||
@ -2548,6 +2677,7 @@
|
||||
"no_image_generation_model": "Δεν υπάρχει διαθέσιμο μοντέλο δημιουργίας εικόνας. Προσθέστε ένα μοντέλο και ορίστε τον τύπο τερματικού σημείου ως {{endpoint_type}}",
|
||||
"number_images": "Ποσότητα δημιουργιών",
|
||||
"number_images_tip": "Ποσότητα εικόνων που θα δημιουργηθούν μια φορά (1-4)",
|
||||
"operation_failed": "Η λειτουργία απέτυχε, παρακαλώ δοκιμάστε ξανά αργότερα",
|
||||
"paint_course": "Εκπαίδευση",
|
||||
"per_image": "Ανά εικόνα",
|
||||
"per_images": "Ανά εικόνα",
|
||||
@ -2623,6 +2753,26 @@
|
||||
"resemblance": "Ομοιότητα",
|
||||
"resemblance_tip": "Ρυθμίστε την ομοιότητα της μεγεθυσμένης εικόνας με την αρχική",
|
||||
"seed_tip": "Ελέγχει την τυχαιότητα του αποτελέσματος μεγέθυνσης"
|
||||
},
|
||||
"zhipu": {
|
||||
"custom_size_divisible": "Το προσαρμοσμένο μέγεθος πρέπει να διαιρείται με το 16",
|
||||
"custom_size_hint": "Το πλάτος και το ύψος πρέπει να είναι μεταξύ 512px-2048px, να διαιρούνται με το 16 και τα συνολικά pixel να μην υπερβαίνουν τα 2^21px.",
|
||||
"custom_size_pixels": "Το συνολικό πλήθος εικονοστοιχείων προσαρμοσμένου μεγέθους δεν μπορεί να υπερβαίνει τα 2.097.152",
|
||||
"custom_size_range": "Το προσαρμοσμένο μέγεθος πρέπει να είναι μεταξύ 512px-2048px",
|
||||
"custom_size_required": "Παρακαλώ ορίστε προσαρμοσμένο πλάτος και ύψος",
|
||||
"image_sizes": {
|
||||
"1024x1024_default": "1024x1024 (Προεπιλογή)",
|
||||
"1152x864": "1152x864",
|
||||
"1344x768": "1344x768",
|
||||
"1440x720": "1440x720",
|
||||
"720x1440": "720x1440",
|
||||
"768x1344": "768x1344",
|
||||
"864x1152": "864x1152"
|
||||
},
|
||||
"quality_options": {
|
||||
"hd": "HD",
|
||||
"standard_default": "Πρότυπο (Προεπιλογή)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
@ -2633,6 +2783,20 @@
|
||||
"category": "Κατηγορία",
|
||||
"commands": "εντολή",
|
||||
"confirm_uninstall": "Είστε σίγουροι ότι θέλετε να απεγκαταστήσετε το {{name}};",
|
||||
"content_saved": "Το περιεχόμενο του πρόσθετου αποθηκεύτηκε με επιτυχία",
|
||||
"detail": {
|
||||
"allowed_tools": "Επιτρεπόμενα Εργαλεία",
|
||||
"author": "Συγγραφέας",
|
||||
"content": "Περιεχόμενο",
|
||||
"description": "Περιγραφή",
|
||||
"file": "Αρχείο",
|
||||
"installed": "Εγκατεστημένο",
|
||||
"metadata": "Μεταδεδομένα",
|
||||
"size": "Μέγεθος",
|
||||
"source": "Πηγή",
|
||||
"tags": "Ετικέτες",
|
||||
"tools": "Εργαλεία"
|
||||
},
|
||||
"install": "εγκατάσταση",
|
||||
"install_plugins_from_browser": "Περιηγηθείτε στα διαθέσιμα πρόσθετα για να ξεκινήσετε",
|
||||
"installing": "Εγκατάσταση...",
|
||||
@ -2983,6 +3147,11 @@
|
||||
"summary": "Σύνοψη",
|
||||
"translate": "Μετάφραση"
|
||||
},
|
||||
"prompt": {
|
||||
"explain": "Παρακαλώ εξήγησε το ακόλουθο περιεχόμενο.",
|
||||
"refine": "Παρακαλώ βελτιστοποιήστε ή γυαλίστε το περιεχόμενο που δίνει ο χρήστης, περιτριγυρισμένο με την ετικέτα XML <INPUT>, διατηρώντας το νόημα και την ακεραιότητα του αρχικού περιεχομένου. Απαιτήσεις: Η έξοδός σας πρέπει να είναι στην ίδια γλώσσα με την είσοδο του χρήστη· μην συμπεριλάβετε καμία εξήγηση αυτής της οδηγίας, δώστε απλώς την απάντηση· μην εκτυπώνετε ετικέτες XML, εκτυπώστε απευθείας το βελτιστοποιημένο περιεχόμενο.",
|
||||
"summary": "Παρακαλώ συνοψίστε το ακόλουθο περιεχόμενο. Απαιτήσεις: Απαντήστε στα ελληνικά· μη συμπεριλάβετε καμία εξήγηση αυτού του προτρόπου, δώστε απευθείας την απάντηση:"
|
||||
},
|
||||
"translate": {
|
||||
"smart_translate_tips": "Έξυπνη μετάφραση: το περιεχόμενο θα μεταφραστεί προτεραιακά στη στόχος γλώσσα· αν το περιεχόμενο είναι ήδη στη στόχος γλώσσα, θα μεταφραστεί στην εναλλακτική γλώσσα"
|
||||
},
|
||||
@ -4076,6 +4245,10 @@
|
||||
"jsonModeHint": "Επεξεργασία της εκφώνησης JSON του διακομιστή MCP. Παρακαλώ εξασφαλίστε ότι το μορφοποίηση είναι σωστό πριν από την αποθήκευση.",
|
||||
"jsonSaveError": "Αποτυχία αποθήκευσης της διαμορφωτικής ρύθμισης JSON",
|
||||
"jsonSaveSuccess": "Η διαμορφωτική ρύθμιση JSON αποθηκεύτηκε επιτυχώς",
|
||||
"lanyun": {
|
||||
"description": "Υπηρεσία MCP Πλατφόρμας Cloud Lanyun Technology",
|
||||
"name": "Lanyun Technology"
|
||||
},
|
||||
"logoUrl": "URL Λογότυπου",
|
||||
"logs": "Αρχεία καταγραφής",
|
||||
"longRunning": "Μακροχρόνια λειτουργία",
|
||||
@ -4172,6 +4345,14 @@
|
||||
"getToken": "Λήψη API Τοκεν",
|
||||
"getTokenDescription": "Λήψη ενός προσωπικού API τοκεν από τον λογαριασμό σας",
|
||||
"noServersAvailable": "Δεν υπάρχουν διαθέσιμοι MCP διακομιστές",
|
||||
"providerDescriptions": {
|
||||
"302ai": "302.Υπηρεσία MCP Πλατφόρμας AI",
|
||||
"bailian": "Υπηρεσία MCP Πλατφόρμας Bailian της Alibaba Cloud",
|
||||
"lanyun": "Υπηρεσία MCP της Πλατφόρμας Cloud Τεχνολογίας Lanyun",
|
||||
"mcprouter": "Πλατφόρμα Δρομολογητή MCP Υπηρεσία MCP",
|
||||
"modelscope": "Υπηρεσία MCP Πλατφόρμας ModelScope",
|
||||
"tokenflux": "Υπηρεσία MCP της Πλατφόρμας TokenFlux"
|
||||
},
|
||||
"selectProvider": "Επιλέξτε Πάροχο:",
|
||||
"setToken": "Εισαγάγετε το τοκεν σας",
|
||||
"success": "Ο συγχρονισμός MCP διακομιστή ολοκληρώθηκε επιτυχώς",
|
||||
@ -4649,6 +4830,9 @@
|
||||
"title": "Διαγραφή παρόχου"
|
||||
},
|
||||
"dmxapi": {
|
||||
"platform_enterprise": "ssvip.DMXAPI.com (Enterprise)",
|
||||
"platform_international": "www.DMXAPI.com (Διεθνές)",
|
||||
"platform_official": "www.DMXAPI.cn (CNY)",
|
||||
"select_platform": "Επιλέξτε πλατφόρμα"
|
||||
},
|
||||
"docs_check": "Άνοιγμα",
|
||||
@ -4985,6 +5169,13 @@
|
||||
"show": "Εμφάνιση εικονιδίου συνδρομής",
|
||||
"title": "Συνδρομή"
|
||||
},
|
||||
"use_system_title_bar": {
|
||||
"confirm": {
|
||||
"content": "Η αλλαγή του στυλ της γραμμής τίτλου απαιτεί επανεκκίνηση της εφαρμογής για να τεθεί σε ισχύ. Θέλετε να γίνει επανεκκίνηση τώρα;",
|
||||
"title": "Απαιτείται επανεκκίνηση"
|
||||
},
|
||||
"title": "Χρήση γραμμής τίτλου συστήματος (Linux)"
|
||||
},
|
||||
"zoom": {
|
||||
"reset": "Επαναφορά",
|
||||
"title": "Κλίμακα"
|
||||
|
||||
@ -271,6 +271,7 @@
|
||||
},
|
||||
"toolPermission": {
|
||||
"aria": {
|
||||
"allowAllRequest": "Permitir siempre esta herramienta",
|
||||
"allowRequest": "Permitir solicitud de herramienta",
|
||||
"denyRequest": "Denegar solicitud de herramienta",
|
||||
"hideDetails": "Ocultar detalles de la herramienta",
|
||||
@ -278,6 +279,7 @@
|
||||
"showDetails": "Mostrar detalles de la herramienta"
|
||||
},
|
||||
"button": {
|
||||
"allowAll": "Permitir siempre",
|
||||
"cancel": "Cancelar",
|
||||
"run": "Correr"
|
||||
},
|
||||
@ -667,6 +669,10 @@
|
||||
"title": "Crear nuevo tema"
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"create_session": "Crear una sesión",
|
||||
"select_agent": "Selecciona un agente"
|
||||
},
|
||||
"artifacts": {
|
||||
"button": {
|
||||
"download": "Descargar",
|
||||
@ -1200,6 +1206,8 @@
|
||||
"cancel": "Cancelar",
|
||||
"chat": "Chat",
|
||||
"clear": "Limpiar",
|
||||
"clear_all": "Borrar todo",
|
||||
"click_to_replace": "Haz clic para reemplazar",
|
||||
"close": "Cerrar",
|
||||
"collapse": "Colapsar",
|
||||
"completed": "Completado",
|
||||
@ -1234,9 +1242,15 @@
|
||||
"footnote": "Nota al pie",
|
||||
"footnotes": "Notas al pie",
|
||||
"fullscreen": "En modo pantalla completa, presione F11 para salir",
|
||||
"generate_random_seed": "Generar semilla aleatoria",
|
||||
"get_embedding_dimension": "Obtener dimensión de incrustación",
|
||||
"go_to_settings": "Ir a la configuración",
|
||||
"html_preview": "Vista previa de HTML",
|
||||
"i_know": "Entendido",
|
||||
"ignore": "Ignorar",
|
||||
"image_preview": "Vista previa de imagen",
|
||||
"image_url": "URL de la imagen",
|
||||
"image_url_or_upload": "Introduce la URL de la imagen o sube un archivo",
|
||||
"inspect": "Inspeccionar",
|
||||
"invalid_value": "Valor inválido",
|
||||
"knowledge_base": "Base de conocimiento",
|
||||
@ -1246,6 +1260,7 @@
|
||||
"models": "Modelos",
|
||||
"more": "Más",
|
||||
"name": "Nombre",
|
||||
"next_match": "Próximo partido",
|
||||
"no_results": "Sin resultados",
|
||||
"none": "无",
|
||||
"off": "Apagado",
|
||||
@ -1257,13 +1272,17 @@
|
||||
"model": "Seleccionar modelo"
|
||||
}
|
||||
},
|
||||
"powered_by": "Impulsado por",
|
||||
"preview": "Vista previa",
|
||||
"previous_match": "Partido anterior",
|
||||
"prompt": "Prompt",
|
||||
"provider": "Proveedor",
|
||||
"reasoning_content": "Pensamiento profundo",
|
||||
"refresh": "Actualizar",
|
||||
"regenerate": "Regenerar",
|
||||
"remove_image": "Quitar imagen",
|
||||
"rename": "Renombrar",
|
||||
"required_field": "Campo obligatorio",
|
||||
"reset": "Restablecer",
|
||||
"save": "Guardar",
|
||||
"saved": "Guardado",
|
||||
@ -1286,14 +1305,29 @@
|
||||
"success": "Éxito",
|
||||
"swap": "Intercambiar",
|
||||
"topics": "Temas",
|
||||
"translate_text": "Traducir texto",
|
||||
"unknown": "Desconocido",
|
||||
"unnamed": "Sin nombre",
|
||||
"unsubscribe": "Cancelar suscripción",
|
||||
"update_success": "Actualización exitosa",
|
||||
"upload_files": "Subir archivo",
|
||||
"upload_image": "Subir archivo de imagen",
|
||||
"uploaded_image": "Imagen cargada",
|
||||
"warning": "Advertencia",
|
||||
"you": "Usuario"
|
||||
},
|
||||
"dialog": {
|
||||
"all_files": "Todos los archivos",
|
||||
"html_files": "Archivos HTML",
|
||||
"open_file": "Abrir archivo",
|
||||
"pdf_files": "Archivos PDF",
|
||||
"png_image": "Imagen PNG",
|
||||
"save_as_html": "Guardar como HTML",
|
||||
"save_as_pdf": "Guardar como PDF",
|
||||
"save_file": "Guardar archivo",
|
||||
"select_folder": "Seleccionar carpeta",
|
||||
"word_document": "Documento de Word"
|
||||
},
|
||||
"docs": {
|
||||
"title": "Documentación de Ayuda"
|
||||
},
|
||||
@ -2015,10 +2049,65 @@
|
||||
"cancelled": "Cancelado",
|
||||
"completed": "Completado",
|
||||
"error": "Se ha producido un error",
|
||||
"groupHeader": "{{count}} llamadas a herramientas",
|
||||
"invoking": "En llamada",
|
||||
"labels": {
|
||||
"bash": "Bash",
|
||||
"edit": "Editar",
|
||||
"exitPlanMode": "ModoPlanDeSalida",
|
||||
"glob": "Globo",
|
||||
"grep": "Grep",
|
||||
"mcpServerTool": "Herramienta del Servidor MCP",
|
||||
"multiEdit": "MultiEdit",
|
||||
"notebookEdit": "CuadernoEditar",
|
||||
"readFile": "Leer archivo",
|
||||
"search": "Buscar",
|
||||
"skill": "Habilidad",
|
||||
"task": "Tarea",
|
||||
"todoWrite": "Todo Escribir",
|
||||
"tool": "Herramienta",
|
||||
"webFetch": "Obtención Web",
|
||||
"webSearch": "Búsqueda en la web",
|
||||
"write": "Escribir"
|
||||
},
|
||||
"noData": "No hay datos disponibles para esta herramienta.",
|
||||
"pending": "Pendiente",
|
||||
"preview": "Vista previa",
|
||||
"raw": "Crudo"
|
||||
"raw": "Crudo",
|
||||
"runningCount": "{{count}} herramientas en ejecución",
|
||||
"sections": {
|
||||
"command": "Comando",
|
||||
"exitCode": "Código de Salida",
|
||||
"input": "Entrada",
|
||||
"output": "Salida",
|
||||
"prompt": "Indicación",
|
||||
"searchQuery": "Consulta de búsqueda",
|
||||
"searchResults": "Resultados de búsqueda",
|
||||
"stderr": "stderr",
|
||||
"stdout": "stdout"
|
||||
},
|
||||
"status": {
|
||||
"done": "Hecho",
|
||||
"error": "Error",
|
||||
"failed": "Fallido",
|
||||
"running": "Corriendo",
|
||||
"success": "Éxito"
|
||||
},
|
||||
"truncated": "Salida truncada (original: {{size}})",
|
||||
"units": {
|
||||
"done_one": "{{count}} Hecho",
|
||||
"done_other": "{{count}} Hecho",
|
||||
"file_one": "{{count}} archivo",
|
||||
"file_other": "{{count}} archivos",
|
||||
"item_one": "{{count}} artículo",
|
||||
"item_other": "{{count}} artículos",
|
||||
"line_one": "{{count}} línea",
|
||||
"line_other": "{{count}} líneas",
|
||||
"plan_one": "{{count}} plan",
|
||||
"plan_other": "{{count}} planes",
|
||||
"result_one": "{{count}} resultado",
|
||||
"result_other": "{{count}} resultados"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"added": "Tema agregado con éxito"
|
||||
@ -2226,14 +2315,18 @@
|
||||
"drop_markdown_hint": "Arrastre y suelte archivos o carpetas de .md aquí para importar",
|
||||
"empty": "Sin notas por el momento",
|
||||
"expand": "expandir",
|
||||
"exportToWord": "Exportar a Word",
|
||||
"export_failed": "Exportación a la base de conocimientos fallida",
|
||||
"export_knowledge": "exportar notas a la base de conocimientos",
|
||||
"export_success": "Exportado con éxito a la base de conocimientos",
|
||||
"export_to_word_failed": "Error al exportar a Word",
|
||||
"folder": "carpeta",
|
||||
"new_folder": "Nueva carpeta",
|
||||
"new_note": "Crear nota nueva",
|
||||
"no_content_to_copy": "No hay contenido para copiar",
|
||||
"no_content_to_export": "Sin contenido para exportar",
|
||||
"no_file_selected": "Por favor, seleccione el archivo a subir",
|
||||
"no_note_selected": "Por favor, selecciona primero una nota.",
|
||||
"no_valid_files": "No se ha cargado un archivo válido",
|
||||
"open_folder": "abrir carpeta externa",
|
||||
"open_outside": "Abrir desde el exterior",
|
||||
@ -2484,6 +2577,39 @@
|
||||
}
|
||||
},
|
||||
"custom_size": "Tamaño personalizado",
|
||||
"dmxapi": {
|
||||
"generating_tip": "Generando con el modelo oficial, el tiempo de espera estimado es de 2-5 minutos para obtener los mejores resultados. Por favor, verifica los registros del backend de DMXAPI para conocer el costo de esta operación.",
|
||||
"style": ", Estilo:",
|
||||
"style_types": {
|
||||
"25d_animation": "Animación 2.5D",
|
||||
"3d_cartoon": "Caricatura 3D",
|
||||
"american_retro": "Retro Americano",
|
||||
"baroque": "Barroco",
|
||||
"cartoon_illustration": "Ilustración de caricatura",
|
||||
"chinese_gongbi": "Gongbi chino",
|
||||
"clay": "Arcilla",
|
||||
"cyberpunk": "Ciberpunk",
|
||||
"felt": "Fieltro",
|
||||
"flat": "Plano",
|
||||
"fresh_anime": "Anime Fresco",
|
||||
"ghibli": "Ghibli",
|
||||
"japanese_anime": "Anime japonés",
|
||||
"little_people_book": "Libro de Little People",
|
||||
"monet_garden": "Jardín Monet",
|
||||
"oil_painting": "Pintura al óleo",
|
||||
"pixar": "Pixar",
|
||||
"pixel_art": "Arte de píxeles",
|
||||
"poetic_ancient": "Poético Antiguo",
|
||||
"psychedelic": "Psicodélico",
|
||||
"sketch": "Boceto",
|
||||
"street_art": "Arte urbano",
|
||||
"texture": "Textura",
|
||||
"ukiyo_e": "Ukiyo-e",
|
||||
"watercolor": "Acuarela",
|
||||
"wood_carving": "Talla en madera",
|
||||
"yarn_doll": "Muñeca de hilo"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"image_file": "Imagen editada",
|
||||
"magic_prompt_option_tip": "Optimización inteligente de las palabras clave de edición",
|
||||
@ -2508,6 +2634,7 @@
|
||||
"style_type_tip": "Estilo de generación de imágenes, solo aplicable para la versión V_2 y posteriores",
|
||||
"width": "Ancho"
|
||||
},
|
||||
"generate_failed": "Error al generar la imagen",
|
||||
"generated_image": "Generar imagen",
|
||||
"go_to_settings": "Ir a configuración",
|
||||
"guidance_scale": "Escala de guía",
|
||||
@ -2518,6 +2645,7 @@
|
||||
"image_file_required": "Por favor, carga una imagen primero",
|
||||
"image_file_retry": "Vuelve a cargar la imagen",
|
||||
"image_handle_required": "Por favor, suba primero una imagen",
|
||||
"image_mix_failed": "Error al mezclar imágenes",
|
||||
"image_placeholder": "No hay imágenes por ahora",
|
||||
"image_retry": "Reintentar",
|
||||
"image_size_options": {
|
||||
@ -2527,6 +2655,7 @@
|
||||
"inference_steps_tip": "Número de pasos de inferencia a realizar. Cuantos más pasos, mejor la calidad pero más tiempo tarda",
|
||||
"input_image": "Imagen de entrada",
|
||||
"input_parameters": "Parámetros de entrada",
|
||||
"invalid_image_url": "Formato de URL de imagen inválido",
|
||||
"learn_more": "Más información",
|
||||
"magic_prompt_option": "Mejora de indicación",
|
||||
"mode": {
|
||||
@ -2548,6 +2677,7 @@
|
||||
"no_image_generation_model": "No hay modelos disponibles para generación de imágenes. Por favor, agregue un modelo y configure el tipo de punto final como {{endpoint_type}}",
|
||||
"number_images": "Cantidad de imágenes generadas",
|
||||
"number_images_tip": "Número de imágenes generadas por vez (1-4)",
|
||||
"operation_failed": "Operación fallida, por favor inténtelo de nuevo más tarde",
|
||||
"paint_course": "Tutorial",
|
||||
"per_image": "Por imagen",
|
||||
"per_images": "Por imagen",
|
||||
@ -2623,6 +2753,26 @@
|
||||
"resemblance": "Similitud",
|
||||
"resemblance_tip": "Controla el nivel de similitud entre el resultado ampliado y la imagen original",
|
||||
"seed_tip": "Controla la aleatoriedad del resultado de la ampliación"
|
||||
},
|
||||
"zhipu": {
|
||||
"custom_size_divisible": "El tamaño personalizado debe ser divisible por 16",
|
||||
"custom_size_hint": "El ancho y la altura deben estar entre 512px y 2048px, ser divisibles por 16 y el total de píxeles no puede exceder 2^21px.",
|
||||
"custom_size_pixels": "El total de píxeles de tamaño personalizado no puede exceder 2,097,152",
|
||||
"custom_size_range": "El tamaño personalizado debe estar entre 512px y 2048px",
|
||||
"custom_size_required": "Por favor, establece el ancho y la altura personalizados.",
|
||||
"image_sizes": {
|
||||
"1024x1024_default": "1024x1024 (Predeterminado)",
|
||||
"1152x864": "1152x864",
|
||||
"1344x768": "1344x768",
|
||||
"1440x720": "1440x720",
|
||||
"720x1440": "720x1440",
|
||||
"768x1344": "768x1344",
|
||||
"864x1152": "864x1152"
|
||||
},
|
||||
"quality_options": {
|
||||
"hd": "HD",
|
||||
"standard_default": "Estándar (Predeterminado)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
@ -2633,6 +2783,20 @@
|
||||
"category": "Categoría",
|
||||
"commands": "comando",
|
||||
"confirm_uninstall": "¿Estás seguro de que quieres desinstalar {{name}}?",
|
||||
"content_saved": "Contenido del plugin guardado exitosamente",
|
||||
"detail": {
|
||||
"allowed_tools": "Herramientas Permitidas",
|
||||
"author": "Autor",
|
||||
"content": "Contenido",
|
||||
"description": "Descripción",
|
||||
"file": "Archivo",
|
||||
"installed": "Instalado",
|
||||
"metadata": "Metadatos",
|
||||
"size": "Tamaño",
|
||||
"source": "Fuente",
|
||||
"tags": "Etiquetas",
|
||||
"tools": "Herramientas"
|
||||
},
|
||||
"install": "instalación",
|
||||
"install_plugins_from_browser": "Explora los complementos disponibles para empezar a usar",
|
||||
"installing": "Instalando...",
|
||||
@ -2983,6 +3147,11 @@
|
||||
"summary": "Resumen",
|
||||
"translate": "Traducir"
|
||||
},
|
||||
"prompt": {
|
||||
"explain": "Por favor, explica el siguiente contenido. Requisitos: Responde en {{language}}; no incluyas ninguna explicación de esta instrucción, solo da la respuesta directamente:",
|
||||
"refine": "Por favor, optimiza o pulsa el contenido del usuario envuelto en la etiqueta XML <INPUT>, manteniendo el significado y la integridad del contenido original. Requisitos: tu salida debe estar en el mismo idioma que la entrada del usuario; no incluyas ninguna explicación de este mensaje, solo da la respuesta directamente; no imprimas etiquetas XML, imprime el contenido optimizado directamente:",
|
||||
"summary": "Por favor, resume el siguiente contenido. Requisitos: responder en {{language}}; no incluyas ninguna explicación de este mensaje, solo da la respuesta directamente:"
|
||||
},
|
||||
"translate": {
|
||||
"smart_translate_tips": "Traducción inteligente: el contenido se traducirá primero al idioma de destino; si el contenido ya está en el idioma de destino, se traducirá al idioma alternativo"
|
||||
},
|
||||
@ -4076,6 +4245,10 @@
|
||||
"jsonModeHint": "Edite la representación JSON de la configuración del servidor MCP. Asegúrese de que el formato sea correcto antes de guardar.",
|
||||
"jsonSaveError": "Fallo al guardar la configuración JSON",
|
||||
"jsonSaveSuccess": "Configuración JSON guardada exitosamente",
|
||||
"lanyun": {
|
||||
"description": "Plataforma Cloud de Tecnología Lanyun Servicio MCP",
|
||||
"name": "Lanyun Technology"
|
||||
},
|
||||
"logoUrl": "URL del logotipo",
|
||||
"logs": "Registros",
|
||||
"longRunning": "Modo de ejecución prolongada",
|
||||
@ -4172,6 +4345,14 @@
|
||||
"getToken": "Obtener token de API",
|
||||
"getTokenDescription": "Obtener un token de API personal desde su cuenta",
|
||||
"noServersAvailable": "No hay servidores MCP disponibles",
|
||||
"providerDescriptions": {
|
||||
"302ai": "302.Servicio MCP de la Plataforma AI",
|
||||
"bailian": "Servicio MCP de la Plataforma Bailian de Alibaba Cloud",
|
||||
"lanyun": "Plataforma en la nube de tecnología Lanyun Servicio MCP",
|
||||
"mcprouter": "Plataforma de Enrutamiento MCP Servicio MCP",
|
||||
"modelscope": "Servicio MCP de la Plataforma ModelScope",
|
||||
"tokenflux": "Servicio MCP de la Plataforma TokenFlux"
|
||||
},
|
||||
"selectProvider": "Seleccionar proveedor:",
|
||||
"setToken": "Ingrese su token",
|
||||
"success": "Servidor MCP sincronizado correctamente",
|
||||
@ -4649,6 +4830,9 @@
|
||||
"title": "Eliminar proveedor"
|
||||
},
|
||||
"dmxapi": {
|
||||
"platform_enterprise": "ssvip.DMXAPI.com (Empresa)",
|
||||
"platform_international": "www.DMXAPI.com (Internacional)",
|
||||
"platform_official": "www.DMXAPI.cn (CNY)",
|
||||
"select_platform": "Seleccionar Plataforma"
|
||||
},
|
||||
"docs_check": "Ver",
|
||||
@ -4985,6 +5169,13 @@
|
||||
"show": "Mostrar bandera del sistema",
|
||||
"title": "Bandera"
|
||||
},
|
||||
"use_system_title_bar": {
|
||||
"confirm": {
|
||||
"content": "Cambiar el estilo de la barra de título requiere reiniciar la aplicación para que surta efecto. ¿Desea reiniciar ahora?",
|
||||
"title": "Reinicio requerido"
|
||||
},
|
||||
"title": "Usar la barra de título del sistema (Linux)"
|
||||
},
|
||||
"zoom": {
|
||||
"reset": "Restablecer",
|
||||
"title": "Escala"
|
||||
|
||||
@ -271,6 +271,7 @@
|
||||
},
|
||||
"toolPermission": {
|
||||
"aria": {
|
||||
"allowAllRequest": "Toujours autoriser cet outil",
|
||||
"allowRequest": "Autoriser la demande d'outil",
|
||||
"denyRequest": "Refuser la demande d'outil",
|
||||
"hideDetails": "Masquer les détails de l'outil",
|
||||
@ -278,6 +279,7 @@
|
||||
"showDetails": "Afficher les détails de l'outil"
|
||||
},
|
||||
"button": {
|
||||
"allowAll": "Toujours autoriser",
|
||||
"cancel": "Annuler",
|
||||
"run": "Courir"
|
||||
},
|
||||
@ -667,6 +669,10 @@
|
||||
"title": "Nouveau sujet"
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"create_session": "Créer une session",
|
||||
"select_agent": "Sélectionnez un agent"
|
||||
},
|
||||
"artifacts": {
|
||||
"button": {
|
||||
"download": "Télécharger",
|
||||
@ -1200,6 +1206,8 @@
|
||||
"cancel": "Annuler",
|
||||
"chat": "Chat",
|
||||
"clear": "Effacer",
|
||||
"clear_all": "Tout effacer",
|
||||
"click_to_replace": "Cliquer pour remplacer",
|
||||
"close": "Fermer",
|
||||
"collapse": "Réduire",
|
||||
"completed": "Terminé",
|
||||
@ -1234,9 +1242,15 @@
|
||||
"footnote": "Note de bas de page",
|
||||
"footnotes": "Notes de bas de page",
|
||||
"fullscreen": "Mode plein écran, appuyez sur F11 pour quitter",
|
||||
"generate_random_seed": "Générer une graine aléatoire",
|
||||
"get_embedding_dimension": "Obtenir la dimension d'intégration",
|
||||
"go_to_settings": "Aller aux paramètres",
|
||||
"html_preview": "Aperçu HTML",
|
||||
"i_know": "J'ai compris",
|
||||
"ignore": "Ignorer",
|
||||
"image_preview": "Aperçu de l'image",
|
||||
"image_url": "URL de l'image",
|
||||
"image_url_or_upload": "Entrez l'URL de l'image ou téléchargez un fichier",
|
||||
"inspect": "Vérifier",
|
||||
"invalid_value": "valeur invalide",
|
||||
"knowledge_base": "Base de connaissances",
|
||||
@ -1246,6 +1260,7 @@
|
||||
"models": "Modèles",
|
||||
"more": "Plus",
|
||||
"name": "Nom",
|
||||
"next_match": "Prochain match",
|
||||
"no_results": "Aucun résultat",
|
||||
"none": "Aucun",
|
||||
"off": "Désactivé",
|
||||
@ -1257,13 +1272,17 @@
|
||||
"model": "Choisir le modèle"
|
||||
}
|
||||
},
|
||||
"powered_by": "Propulsé par",
|
||||
"preview": "Aperçu",
|
||||
"previous_match": "Match précédent",
|
||||
"prompt": "Prompt",
|
||||
"provider": "Fournisseur",
|
||||
"reasoning_content": "Réflexion approfondie",
|
||||
"refresh": "Actualiser",
|
||||
"regenerate": "Regénérer",
|
||||
"remove_image": "Supprimer l'image",
|
||||
"rename": "Renommer",
|
||||
"required_field": "Champ obligatoire",
|
||||
"reset": "Réinitialiser",
|
||||
"save": "Enregistrer",
|
||||
"saved": "enregistré",
|
||||
@ -1286,14 +1305,29 @@
|
||||
"success": "Succès",
|
||||
"swap": "Échanger",
|
||||
"topics": "Sujets",
|
||||
"translate_text": "Traduire le texte",
|
||||
"unknown": "Inconnu",
|
||||
"unnamed": "Sans nom",
|
||||
"unsubscribe": "Se désabonner",
|
||||
"update_success": "Mise à jour réussie",
|
||||
"upload_files": "Uploader des fichiers",
|
||||
"upload_image": "Télécharger le fichier image",
|
||||
"uploaded_image": "Image téléchargée",
|
||||
"warning": "Avertissement",
|
||||
"you": "Vous"
|
||||
},
|
||||
"dialog": {
|
||||
"all_files": "Tous les fichiers",
|
||||
"html_files": "Fichiers HTML",
|
||||
"open_file": "Ouvrir le fichier",
|
||||
"pdf_files": "Fichiers PDF",
|
||||
"png_image": "Image PNG",
|
||||
"save_as_html": "Enregistrer au format HTML",
|
||||
"save_as_pdf": "Enregistrer au format PDF",
|
||||
"save_file": "Enregistrer le fichier",
|
||||
"select_folder": "Sélectionner un dossier",
|
||||
"word_document": "Document Word"
|
||||
},
|
||||
"docs": {
|
||||
"title": "Documentation d'aide"
|
||||
},
|
||||
@ -2015,10 +2049,65 @@
|
||||
"cancelled": "Annulé",
|
||||
"completed": "Terminé",
|
||||
"error": "Une erreur s'est produite",
|
||||
"groupHeader": "{{count}} appels d'outil",
|
||||
"invoking": "En cours d'exécution",
|
||||
"labels": {
|
||||
"bash": "Bash",
|
||||
"edit": "Modifier",
|
||||
"exitPlanMode": "ModePlanSortie",
|
||||
"glob": "Globe",
|
||||
"grep": "Grep",
|
||||
"mcpServerTool": "Outil du serveur MCP",
|
||||
"multiEdit": "MultiEdit",
|
||||
"notebookEdit": "ÉditionCahier",
|
||||
"readFile": "Lire le fichier",
|
||||
"search": "Rechercher",
|
||||
"skill": "Compétence",
|
||||
"task": "Tâche",
|
||||
"todoWrite": "À faire Écrire",
|
||||
"tool": "Outil",
|
||||
"webFetch": "Récupération Web",
|
||||
"webSearch": "Recherche Web",
|
||||
"write": "Écrire"
|
||||
},
|
||||
"noData": "Aucune donnée disponible pour cet outil",
|
||||
"pending": "En attente",
|
||||
"preview": "Aperçu",
|
||||
"raw": "Brut"
|
||||
"raw": "Brut",
|
||||
"runningCount": "{{count}} outils en cours d'exécution",
|
||||
"sections": {
|
||||
"command": "Commande",
|
||||
"exitCode": "Code de sortie",
|
||||
"input": "Entrée",
|
||||
"output": "Sortie",
|
||||
"prompt": "Invite",
|
||||
"searchQuery": "Requête de recherche",
|
||||
"searchResults": "Résultats de recherche",
|
||||
"stderr": "stderr",
|
||||
"stdout": "sortie standard"
|
||||
},
|
||||
"status": {
|
||||
"done": "Fait",
|
||||
"error": "Erreur",
|
||||
"failed": "Échoué",
|
||||
"running": "Courir",
|
||||
"success": "Succès"
|
||||
},
|
||||
"truncated": "Sortie tronquée (original : {{size}})",
|
||||
"units": {
|
||||
"done_one": "{{count}} Terminé",
|
||||
"done_other": "{{count}} Terminé",
|
||||
"file_one": "{{count}} fichier",
|
||||
"file_other": "{{count}} fichiers",
|
||||
"item_one": "{{count}} article",
|
||||
"item_other": "{{count}} articles",
|
||||
"line_one": "{{count}} ligne",
|
||||
"line_other": "{{count}} lignes",
|
||||
"plan_one": "{{count}} plan",
|
||||
"plan_other": "{{count}} plans",
|
||||
"result_one": "{{count}} résultat",
|
||||
"result_other": "{{count}} résultats"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"added": "Thème ajouté avec succès"
|
||||
@ -2226,14 +2315,18 @@
|
||||
"drop_markdown_hint": "Déposez ici des fichiers ou dossiers .md pour les importer",
|
||||
"empty": "Aucune note pour le moment",
|
||||
"expand": "développer",
|
||||
"exportToWord": "Exporter vers Word",
|
||||
"export_failed": "Échec de l'exportation vers la base de connaissances",
|
||||
"export_knowledge": "exporter la note vers la base de connaissances",
|
||||
"export_success": "Exporté avec succès vers la base de connaissances",
|
||||
"export_to_word_failed": "Échec de l'exportation vers Word",
|
||||
"folder": "dossier",
|
||||
"new_folder": "Nouveau dossier",
|
||||
"new_note": "Nouvelle note",
|
||||
"no_content_to_copy": "Aucun contenu à copier",
|
||||
"no_content_to_export": "Aucun contenu à exporter",
|
||||
"no_file_selected": "Veuillez sélectionner le fichier à télécharger",
|
||||
"no_note_selected": "Veuillez d'abord sélectionner une note",
|
||||
"no_valid_files": "Aucun fichier valide n’a été téléversé",
|
||||
"open_folder": "ouvrir le dossier externe",
|
||||
"open_outside": "Ouvrir depuis l'extérieur",
|
||||
@ -2484,6 +2577,39 @@
|
||||
}
|
||||
},
|
||||
"custom_size": "Dimensions personnalisées",
|
||||
"dmxapi": {
|
||||
"generating_tip": "Génération avec le modèle officiel, le temps d'attente estimé est de 2 à 5 minutes pour les meilleurs résultats. Veuillez consulter les journaux du backend DMXAPI pour le coût de cette opération.",
|
||||
"style": ", Style :",
|
||||
"style_types": {
|
||||
"25d_animation": "Animation 2.5D",
|
||||
"3d_cartoon": "Cartoon 3D",
|
||||
"american_retro": "Rétro américain",
|
||||
"baroque": "Baroque",
|
||||
"cartoon_illustration": "Illustration de bande dessinée",
|
||||
"chinese_gongbi": "Gongbi chinois",
|
||||
"clay": "Argile",
|
||||
"cyberpunk": "Cyberpunk",
|
||||
"felt": "Feutre",
|
||||
"flat": "Plat",
|
||||
"fresh_anime": "Anime Frais",
|
||||
"ghibli": "Ghibli",
|
||||
"japanese_anime": "Animé japonais",
|
||||
"little_people_book": "Petits Livres pour Petites Gens",
|
||||
"monet_garden": "Jardin Monet",
|
||||
"oil_painting": "Peinture à l'huile",
|
||||
"pixar": "Pixar",
|
||||
"pixel_art": "Art pixel",
|
||||
"poetic_ancient": "Poétique antique",
|
||||
"psychedelic": "Psychédélique",
|
||||
"sketch": "Croquis",
|
||||
"street_art": "Art de rue",
|
||||
"texture": "Texture",
|
||||
"ukiyo_e": "Ukiyo-e",
|
||||
"watercolor": "Aquarelle",
|
||||
"wood_carving": "Sculpture sur bois",
|
||||
"yarn_doll": "Poupée en laine"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"image_file": "Image éditée",
|
||||
"magic_prompt_option_tip": "Optimisation intelligente du mot-clé d'édition",
|
||||
@ -2508,6 +2634,7 @@
|
||||
"style_type_tip": "Стиль генерации изображения, применим к версии V_2 и выше",
|
||||
"width": "Largeur"
|
||||
},
|
||||
"generate_failed": "Échec de la génération de l'image",
|
||||
"generated_image": "Image générée",
|
||||
"go_to_settings": "Aller aux paramètres",
|
||||
"guidance_scale": "Échelle de guidance",
|
||||
@ -2518,6 +2645,7 @@
|
||||
"image_file_required": "Veuillez d'abord télécharger une image",
|
||||
"image_file_retry": "Veuillez réuploader l'image",
|
||||
"image_handle_required": "Veuillez d'abord télécharger une image",
|
||||
"image_mix_failed": "Échec du mélange des images",
|
||||
"image_placeholder": "Aucune image pour le moment",
|
||||
"image_retry": "Réessayer",
|
||||
"image_size_options": {
|
||||
@ -2527,6 +2655,7 @@
|
||||
"inference_steps_tip": "Nombre d'étapes d'inférence à effectuer. Plus il y a d'étapes, meilleure est la qualité mais plus c'est long",
|
||||
"input_image": "Image d'entrée",
|
||||
"input_parameters": "Paramètres d'entrée",
|
||||
"invalid_image_url": "Format d'URL d'image invalide",
|
||||
"learn_more": "En savoir plus",
|
||||
"magic_prompt_option": "Amélioration du prompt",
|
||||
"mode": {
|
||||
@ -2548,6 +2677,7 @@
|
||||
"no_image_generation_model": "Aucun modèle de génération d'image disponible pour le moment. Veuillez ajouter un modèle et définir le type de point de terminaison sur {{endpoint_type}}",
|
||||
"number_images": "Nombre d'images générées",
|
||||
"number_images_tip": "Le nombre d'images générées en une seule fois (1-4)",
|
||||
"operation_failed": "L'opération a échoué, veuillez réessayer plus tard",
|
||||
"paint_course": "Tutoriel",
|
||||
"per_image": "Par image",
|
||||
"per_images": "Par image",
|
||||
@ -2623,6 +2753,26 @@
|
||||
"resemblance": "Similarité",
|
||||
"resemblance_tip": "Contrôle le niveau de similarité entre le résultat agrandi et l'image originale",
|
||||
"seed_tip": "Contrôle la randomisation du résultat d'agrandissement"
|
||||
},
|
||||
"zhipu": {
|
||||
"custom_size_divisible": "La taille personnalisée doit être divisible par 16",
|
||||
"custom_size_hint": "La largeur et la hauteur doivent être comprises entre 512 px et 2048 px, divisibles par 16, et le nombre total de pixels ne peut pas dépasser 2^21 px.",
|
||||
"custom_size_pixels": "Le nombre total de pixels d'une taille personnalisée ne peut pas dépasser 2 097 152",
|
||||
"custom_size_range": "La taille personnalisée doit être comprise entre 512 px et 2048 px",
|
||||
"custom_size_required": "Veuillez définir une largeur et une hauteur personnalisées",
|
||||
"image_sizes": {
|
||||
"1024x1024_default": "1024x1024 (Par défaut)",
|
||||
"1152x864": "1152x864",
|
||||
"1344x768": "1344x768",
|
||||
"1440x720": "1440x720",
|
||||
"720x1440": "720x1440",
|
||||
"768x1344": "768x1344",
|
||||
"864x1152": "864x1152"
|
||||
},
|
||||
"quality_options": {
|
||||
"hd": "HD",
|
||||
"standard_default": "Standard (Par défaut)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
@ -2633,6 +2783,20 @@
|
||||
"category": "Catégorie",
|
||||
"commands": "commande",
|
||||
"confirm_uninstall": "Êtes-vous sûr de vouloir désinstaller {{name}} ?",
|
||||
"content_saved": "Contenu du plugin enregistré avec succès",
|
||||
"detail": {
|
||||
"allowed_tools": "Outils autorisés",
|
||||
"author": "Auteur",
|
||||
"content": "Contenu",
|
||||
"description": "Description",
|
||||
"file": "Fichier",
|
||||
"installed": "Installé",
|
||||
"metadata": "Métadonnées",
|
||||
"size": "Taille",
|
||||
"source": "Source",
|
||||
"tags": "Étiquettes",
|
||||
"tools": "Outils"
|
||||
},
|
||||
"install": "Installation",
|
||||
"install_plugins_from_browser": "Parcourir les plugins disponibles pour commencer",
|
||||
"installing": "Installation en cours...",
|
||||
@ -2983,6 +3147,11 @@
|
||||
"summary": "Résumé",
|
||||
"translate": "Traduire"
|
||||
},
|
||||
"prompt": {
|
||||
"explain": "Veuillez expliquer le contenu suivant. Exigences : répondre en {{language}} ; n’incluez aucune explication de cette consigne, fournissez simplement la réponse directement :",
|
||||
"refine": "Veuillez optimiser ou peaufiner le contenu saisi par l’utilisateur inclus dans la balise XML <INPUT>, tout en conservant le sens et l’intégrité du contenu original. Exigences : votre réponse doit être dans la même langue que la saisie utilisateur ; n’incluez aucune explication de cette consigne, fournissez directement la réponse ; ne produisez pas de balises XML, affichez le contenu optimisé directement.",
|
||||
"summary": "Je ne peux pas résumer le contenu, car aucun texte à résumer n’a été fourni après « [to be translated] »."
|
||||
},
|
||||
"translate": {
|
||||
"smart_translate_tips": "Traduction intelligente : le contenu sera d'abord traduit dans la langue cible ; si le contenu est déjà dans la langue cible, il sera traduit dans la langue secondaire"
|
||||
},
|
||||
@ -4076,6 +4245,10 @@
|
||||
"jsonModeHint": "Modifier la représentation JSON de la configuration des serveurs MCP. Assurez-vous que le format est correct avant de sauvegarder.",
|
||||
"jsonSaveError": "Échec de la sauvegarde de la configuration JSON",
|
||||
"jsonSaveSuccess": "Configuration JSON sauvegardée",
|
||||
"lanyun": {
|
||||
"description": "Plateforme Cloud Lanyun Technology Service MCP",
|
||||
"name": "Lanyun Technology"
|
||||
},
|
||||
"logoUrl": "Адрес логотипа",
|
||||
"logs": "Journaux",
|
||||
"longRunning": "Mode d'exécution prolongée",
|
||||
@ -4172,6 +4345,14 @@
|
||||
"getToken": "Получить API-токен",
|
||||
"getTokenDescription": "Получите персональный API-токен из вашей учетной записи",
|
||||
"noServersAvailable": "Нет доступных MCP-серверов",
|
||||
"providerDescriptions": {
|
||||
"302ai": "302.Service MCP de la Plateforme AI",
|
||||
"bailian": "Service MCP de la plateforme Bailian d'Alibaba Cloud",
|
||||
"lanyun": "Plateforme Cloud Lanyun Technology Service MCP",
|
||||
"mcprouter": "Plateforme de routeur MCP Service MCP",
|
||||
"modelscope": "Service MCP de la plateforme ModelScope",
|
||||
"tokenflux": "Service MCP de la plateforme TokenFlux"
|
||||
},
|
||||
"selectProvider": "Выберите провайдера:",
|
||||
"setToken": "Введите ваш токен",
|
||||
"success": "MCP-сервер успешно синхронизирован",
|
||||
@ -4649,6 +4830,9 @@
|
||||
"title": "Supprimer le fournisseur"
|
||||
},
|
||||
"dmxapi": {
|
||||
"platform_enterprise": "ssvip.DMXAPI.com (Entreprise)",
|
||||
"platform_international": "www.DMXAPI.com (International)",
|
||||
"platform_official": "www.DMXAPI.cn (CNY)",
|
||||
"select_platform": "Sélectionner la plateforme"
|
||||
},
|
||||
"docs_check": "Voir",
|
||||
@ -4985,6 +5169,13 @@
|
||||
"show": "Afficher l'icône dans la barre d'état système",
|
||||
"title": "Barre d'état système"
|
||||
},
|
||||
"use_system_title_bar": {
|
||||
"confirm": {
|
||||
"content": "La modification du style de la barre de titre nécessite le redémarrage de l'application pour prendre effet. Voulez-vous redémarrer maintenant ?",
|
||||
"title": "Redémarrage requis"
|
||||
},
|
||||
"title": "Utiliser la barre de titre du système (Linux)"
|
||||
},
|
||||
"zoom": {
|
||||
"reset": "Réinitialiser",
|
||||
"title": "Zoom"
|
||||
|
||||
@ -271,6 +271,7 @@
|
||||
},
|
||||
"toolPermission": {
|
||||
"aria": {
|
||||
"allowAllRequest": "常にこのツールを許可する",
|
||||
"allowRequest": "ツールリクエストを許可",
|
||||
"denyRequest": "ツールリクエストを拒否",
|
||||
"hideDetails": "ツールの詳細を非表示",
|
||||
@ -278,6 +279,7 @@
|
||||
"showDetails": "ツールの詳細を表示"
|
||||
},
|
||||
"button": {
|
||||
"allowAll": "常に許可",
|
||||
"cancel": "キャンセル",
|
||||
"run": "走る"
|
||||
},
|
||||
@ -667,6 +669,10 @@
|
||||
"title": "新しいトピック"
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"create_session": "セッションを作成",
|
||||
"select_agent": "エージェントを選択してください"
|
||||
},
|
||||
"artifacts": {
|
||||
"button": {
|
||||
"download": "ダウンロード",
|
||||
@ -1200,6 +1206,8 @@
|
||||
"cancel": "キャンセル",
|
||||
"chat": "チャット",
|
||||
"clear": "クリア",
|
||||
"clear_all": "すべてクリア",
|
||||
"click_to_replace": "クリックして置換",
|
||||
"close": "閉じる",
|
||||
"collapse": "折りたたむ",
|
||||
"completed": "完了",
|
||||
@ -1234,9 +1242,15 @@
|
||||
"footnote": "引用内容",
|
||||
"footnotes": "脚注",
|
||||
"fullscreen": "全画面モードに入りました。F11キーで終了します",
|
||||
"generate_random_seed": "ランダムシードを生成",
|
||||
"get_embedding_dimension": "埋め込み次元を取得",
|
||||
"go_to_settings": "設定に移動",
|
||||
"html_preview": "HTMLプレビュー",
|
||||
"i_know": "わかりました",
|
||||
"ignore": "無視",
|
||||
"image_preview": "画像プレビュー",
|
||||
"image_url": "画像URL",
|
||||
"image_url_or_upload": "画像のURLを入力するか、ファイルをアップロードしてください",
|
||||
"inspect": "検査",
|
||||
"invalid_value": "無効な値",
|
||||
"knowledge_base": "ナレッジベース",
|
||||
@ -1246,6 +1260,7 @@
|
||||
"models": "モデル",
|
||||
"more": "もっと",
|
||||
"name": "名前",
|
||||
"next_match": "次の試合",
|
||||
"no_results": "検索結果なし",
|
||||
"none": "無",
|
||||
"off": "オフ",
|
||||
@ -1257,13 +1272,17 @@
|
||||
"model": "モデルを選択"
|
||||
}
|
||||
},
|
||||
"powered_by": "搭載",
|
||||
"preview": "プレビュー",
|
||||
"previous_match": "前回の試合",
|
||||
"prompt": "プロンプト",
|
||||
"provider": "プロバイダー",
|
||||
"reasoning_content": "深く考察済み",
|
||||
"refresh": "更新",
|
||||
"regenerate": "再生成",
|
||||
"remove_image": "画像を削除",
|
||||
"rename": "名前を変更",
|
||||
"required_field": "必須項目",
|
||||
"reset": "リセット",
|
||||
"save": "保存",
|
||||
"saved": "保存されました",
|
||||
@ -1286,14 +1305,29 @@
|
||||
"success": "成功",
|
||||
"swap": "交換",
|
||||
"topics": "トピック",
|
||||
"translate_text": "翻訳",
|
||||
"unknown": "Unknown",
|
||||
"unnamed": "無題",
|
||||
"unsubscribe": "配信停止",
|
||||
"update_success": "更新成功",
|
||||
"upload_files": "ファイルをアップロードする",
|
||||
"upload_image": "画像ファイルをアップロード",
|
||||
"uploaded_image": "アップロードされた画像",
|
||||
"warning": "警告",
|
||||
"you": "あなた"
|
||||
},
|
||||
"dialog": {
|
||||
"all_files": "すべてのファイル",
|
||||
"html_files": "HTMLファイル",
|
||||
"open_file": "ファイルを開く",
|
||||
"pdf_files": "PDFファイル",
|
||||
"png_image": "PNG画像",
|
||||
"save_as_html": "HTMLとして保存",
|
||||
"save_as_pdf": "PDFとして保存",
|
||||
"save_file": "ファイルを保存",
|
||||
"select_folder": "フォルダを選択",
|
||||
"word_document": "ワード文書"
|
||||
},
|
||||
"docs": {
|
||||
"title": "ドキュメント"
|
||||
},
|
||||
@ -2015,10 +2049,65 @@
|
||||
"cancelled": "キャンセル",
|
||||
"completed": "完了",
|
||||
"error": "エラーが発生しました",
|
||||
"groupHeader": "{{count}}ツール呼び出し",
|
||||
"invoking": "呼び出し中",
|
||||
"labels": {
|
||||
"bash": "バッシュ",
|
||||
"edit": "編集",
|
||||
"exitPlanMode": "ExitPlanMode",
|
||||
"glob": "グローブ",
|
||||
"grep": "グレップ",
|
||||
"mcpServerTool": "MCPサーバーツール",
|
||||
"multiEdit": "マルチエディット",
|
||||
"notebookEdit": "ノートブック編集",
|
||||
"readFile": "ファイルを読む",
|
||||
"search": "検索",
|
||||
"skill": "スキル",
|
||||
"task": "タスク",
|
||||
"todoWrite": "やること 書く",
|
||||
"tool": "ツール",
|
||||
"webFetch": "ウェブフェッチ",
|
||||
"webSearch": "ウェブ検索",
|
||||
"write": "書く"
|
||||
},
|
||||
"noData": "このツールに利用可能なデータはありません",
|
||||
"pending": "保留中",
|
||||
"preview": "プレビュー",
|
||||
"raw": "生データ"
|
||||
"raw": "生データ",
|
||||
"runningCount": "{{count}}個のツールが実行中",
|
||||
"sections": {
|
||||
"command": "コマンド",
|
||||
"exitCode": "終了コード",
|
||||
"input": "入力",
|
||||
"output": "出力",
|
||||
"prompt": "プロンプト",
|
||||
"searchQuery": "検索クエリ",
|
||||
"searchResults": "検索結果",
|
||||
"stderr": "標準エラー出力",
|
||||
"stdout": "標準出力"
|
||||
},
|
||||
"status": {
|
||||
"done": "完了",
|
||||
"error": "エラー",
|
||||
"failed": "失敗",
|
||||
"running": "走っている",
|
||||
"success": "成功"
|
||||
},
|
||||
"truncated": "出力が切り捨てられました(元のサイズ: {{size}})",
|
||||
"units": {
|
||||
"done_one": "{{count}} 完了",
|
||||
"done_other": "{{count}} 完了",
|
||||
"file_one": "{{count}} ファイル",
|
||||
"file_other": "{{count}}個のファイル",
|
||||
"item_one": "{{count}}個のアイテム",
|
||||
"item_other": "{{count}}件のアイテム",
|
||||
"line_one": "{{count}}行",
|
||||
"line_other": "{{count}}行",
|
||||
"plan_one": "{{count}} プラン",
|
||||
"plan_other": "{{count}}件のプラン",
|
||||
"result_one": "{{count}}件の結果",
|
||||
"result_other": "{{count}}件の結果"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"added": "新しいトピックが追加されました"
|
||||
@ -2226,14 +2315,18 @@
|
||||
"drop_markdown_hint": ".md ファイルまたはディレクトリをここにドラッグ&ドロップしてインポートしてください",
|
||||
"empty": "暫無ノート",
|
||||
"expand": "展開",
|
||||
"exportToWord": "Wordにエクスポート",
|
||||
"export_failed": "知識ベースへのエクスポートに失敗しました",
|
||||
"export_knowledge": "ノートをナレッジベースにエクスポートする",
|
||||
"export_success": "知識ベースへのエクスポートが成功しました",
|
||||
"export_to_word_failed": "Wordへのエクスポートに失敗しました",
|
||||
"folder": "フォルダー",
|
||||
"new_folder": "新しいフォルダーを作成する",
|
||||
"new_note": "新規ノート作成",
|
||||
"no_content_to_copy": "コピーするコンテンツはありません",
|
||||
"no_content_to_export": "エクスポートするコンテンツがありません",
|
||||
"no_file_selected": "アップロードするファイルを選択してください",
|
||||
"no_note_selected": "まずノートを選択してください",
|
||||
"no_valid_files": "有効なファイルがアップロードされていません",
|
||||
"open_folder": "外部フォルダーを開きます",
|
||||
"open_outside": "外部から開く",
|
||||
@ -2484,6 +2577,39 @@
|
||||
}
|
||||
},
|
||||
"custom_size": "カスタムサイズ",
|
||||
"dmxapi": {
|
||||
"generating_tip": "公式モデルで生成中です。最良の結果のため、推定待機時間は2〜5分です。この操作のコストについては、DMXAPIバックエンドログをご確認ください。",
|
||||
"style": "スタイル:",
|
||||
"style_types": {
|
||||
"25d_animation": "2.5Dアニメーション",
|
||||
"3d_cartoon": "3Dカートゥーン",
|
||||
"american_retro": "アメリカン・レトロ",
|
||||
"baroque": "バロック",
|
||||
"cartoon_illustration": "漫画風イラスト",
|
||||
"chinese_gongbi": "中国の工筆",
|
||||
"clay": "クレイ",
|
||||
"cyberpunk": "サイバーパンク",
|
||||
"felt": "フェルト",
|
||||
"flat": "平ら",
|
||||
"fresh_anime": "フレッシュアニメ",
|
||||
"ghibli": "ジブリ",
|
||||
"japanese_anime": "日本のアニメ",
|
||||
"little_people_book": "小人の本",
|
||||
"monet_garden": "モネ庭園",
|
||||
"oil_painting": "油絵",
|
||||
"pixar": "ピクサー",
|
||||
"pixel_art": "ピクセルアート",
|
||||
"poetic_ancient": "詩的な古代",
|
||||
"psychedelic": "サイケデリック",
|
||||
"sketch": "スケッチ",
|
||||
"street_art": "ストリートアート",
|
||||
"texture": "テクスチャ",
|
||||
"ukiyo_e": "浮世絵",
|
||||
"watercolor": "水彩画",
|
||||
"wood_carving": "木彫刻",
|
||||
"yarn_doll": "糸人形"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"image_file": "編集画像",
|
||||
"magic_prompt_option_tip": "編集効果を向上させるための提示詞を最適化します",
|
||||
@ -2508,6 +2634,7 @@
|
||||
"style_type_tip": "画像生成スタイル、V_2 以上のバージョンでのみ適用",
|
||||
"width": "幅"
|
||||
},
|
||||
"generate_failed": "画像の生成に失敗しました",
|
||||
"generated_image": "生成画像",
|
||||
"go_to_settings": "設定に移動",
|
||||
"guidance_scale": "ガイダンススケール",
|
||||
@ -2518,6 +2645,7 @@
|
||||
"image_file_required": "画像を先にアップロードしてください",
|
||||
"image_file_retry": "画像を先にアップロードしてください",
|
||||
"image_handle_required": "最初に画像をアップロードしてください。",
|
||||
"image_mix_failed": "画像の混合に失敗しました",
|
||||
"image_placeholder": "画像がありません",
|
||||
"image_retry": "再試行",
|
||||
"image_size_options": {
|
||||
@ -2527,6 +2655,7 @@
|
||||
"inference_steps_tip": "実行する推論ステップ数。ステップ数が多いほど品質が向上しますが、時間がかかります",
|
||||
"input_image": "入力画像",
|
||||
"input_parameters": "パラメータ入力",
|
||||
"invalid_image_url": "無効な画像URLの形式",
|
||||
"learn_more": "詳しくはこちら",
|
||||
"magic_prompt_option": "プロンプト強化",
|
||||
"mode": {
|
||||
@ -2548,6 +2677,7 @@
|
||||
"no_image_generation_model": "利用可能な画像生成モデルがありません。モデルを追加し、エンドポイントタイプを {{endpoint_type}} に設定してください",
|
||||
"number_images": "生成数",
|
||||
"number_images_tip": "生成する画像の数(1-4)",
|
||||
"operation_failed": "操作に失敗しました。後でもう一度お試しください。",
|
||||
"paint_course": "チュートリアル",
|
||||
"per_image": "1枚あたり",
|
||||
"per_images": "複数枚あたり",
|
||||
@ -2623,6 +2753,26 @@
|
||||
"resemblance": "類似度",
|
||||
"resemblance_tip": "拡大結果と原画像の類似度を制御します",
|
||||
"seed_tip": "拡大結果のランダム性を制御します"
|
||||
},
|
||||
"zhipu": {
|
||||
"custom_size_divisible": "カスタムサイズは16で割り切れる必要があります",
|
||||
"custom_size_hint": "幅と高さは512px~2048pxの間で16で割り切れ、総ピクセル数は2^21pxを超えてはなりません",
|
||||
"custom_size_pixels": "カスタムサイズの総ピクセル数は2,097,152を超えることはできません",
|
||||
"custom_size_range": "カスタムサイズは512px~2048pxの間でなければなりません",
|
||||
"custom_size_required": "カスタム幅と高さを設定してください",
|
||||
"image_sizes": {
|
||||
"1024x1024_default": "1024x1024(デフォルト)",
|
||||
"1152x864": "1152×864",
|
||||
"1344x768": "1344x768",
|
||||
"1440x720": "1440x720",
|
||||
"720x1440": "720x1440",
|
||||
"768x1344": "768x1344",
|
||||
"864x1152": "864×1152"
|
||||
},
|
||||
"quality_options": {
|
||||
"hd": "HD",
|
||||
"standard_default": "標準(デフォルト)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
@ -2633,6 +2783,20 @@
|
||||
"category": "カテゴリー",
|
||||
"commands": "命令",
|
||||
"confirm_uninstall": "{{name}}をアンインストールしてもよろしいですか?",
|
||||
"content_saved": "プラグインコンテンツが正常に保存されました",
|
||||
"detail": {
|
||||
"allowed_tools": "許可されたツール",
|
||||
"author": "著者",
|
||||
"content": "コンテンツ",
|
||||
"description": "説明",
|
||||
"file": "ファイル",
|
||||
"installed": "インストール済み",
|
||||
"metadata": "メタデータ",
|
||||
"size": "サイズ",
|
||||
"source": "ソース",
|
||||
"tags": "タグ",
|
||||
"tools": "ツール"
|
||||
},
|
||||
"install": "インストール",
|
||||
"install_plugins_from_browser": "利用可能なプラグインを閲覧して、使用を開始してください",
|
||||
"installing": "インストール中...",
|
||||
@ -2983,6 +3147,11 @@
|
||||
"summary": "要約",
|
||||
"translate": "翻訳"
|
||||
},
|
||||
"prompt": {
|
||||
"explain": "以下の内容を説明してください。要件:{{language}}で返答すること;このプロンプトの説明は一切含めず、直接回答してください。",
|
||||
"refine": "ユーザー入力内容をXMLタグ<INPUT>で囲み、元の意味と整合性を保ちながら最適化または磨きをかけてください。要件:出力はユーザー入力と同じ言語で行い、このプロンプトの説明は一切含めず、直接応答を提供してください;XMLタグは出力せず、最適化された内容を直接出力してください。",
|
||||
"summary": "以下の内容を要約してください。要件:{{language}}で返答;このプロンプトの説明は一切含めず、直接回答してください。"
|
||||
},
|
||||
"translate": {
|
||||
"smart_translate_tips": "スマート翻訳:内容は優先的に目標言語に翻訳されます。すでに目標言語の場合は、備用言語に翻訳されます。"
|
||||
},
|
||||
@ -4076,6 +4245,10 @@
|
||||
"jsonModeHint": "MCPサーバー設定のJSON表現を編集します。保存する前に、フォーマットが正しいことを確認してください。",
|
||||
"jsonSaveError": "JSON設定の保存に失敗しました",
|
||||
"jsonSaveSuccess": "JSON設定が保存されました。",
|
||||
"lanyun": {
|
||||
"description": "Lanyun Technology Cloud Platform MCP Service",
|
||||
"name": "ランユン・テクノロジー"
|
||||
},
|
||||
"logoUrl": "ロゴURL",
|
||||
"logs": "ログ",
|
||||
"longRunning": "長時間運行モード",
|
||||
@ -4172,6 +4345,14 @@
|
||||
"getToken": "API トークンを取得する",
|
||||
"getTokenDescription": "アカウントから個人用 API トークンを取得します",
|
||||
"noServersAvailable": "利用可能な MCP サーバーがありません",
|
||||
"providerDescriptions": {
|
||||
"302ai": "302.AIプラットフォームMCPサービス",
|
||||
"bailian": "Alibaba Cloud Bailian Platform MCP Service",
|
||||
"lanyun": "Lanyun Technology Cloud Platform MCP Service",
|
||||
"mcprouter": "MCPルータープラットフォーム MCPサービス",
|
||||
"modelscope": "ModelScopeプラットフォームMCPサービス",
|
||||
"tokenflux": "TokenFlux Platform MCP Service"
|
||||
},
|
||||
"selectProvider": "プロバイダーを選択:",
|
||||
"setToken": "トークンを入力してください",
|
||||
"success": "MCPサーバーの同期成功",
|
||||
@ -4649,6 +4830,9 @@
|
||||
"title": "プロバイダーを削除"
|
||||
},
|
||||
"dmxapi": {
|
||||
"platform_enterprise": "ssvip.DMXAPI.com(エンタープライズ)",
|
||||
"platform_international": "www.DMXAPI.com(インターナショナル)",
|
||||
"platform_official": "www.DMXAPI.cn(人民元)",
|
||||
"select_platform": "プラットフォームを選択"
|
||||
},
|
||||
"docs_check": "チェック",
|
||||
@ -4985,6 +5169,13 @@
|
||||
"show": "トレイアイコンを表示",
|
||||
"title": "トレイ"
|
||||
},
|
||||
"use_system_title_bar": {
|
||||
"confirm": {
|
||||
"content": "タイトルバーのスタイルを変更するには、アプリを再起動する必要があります。今すぐ再起動しますか?",
|
||||
"title": "再起動が必要です"
|
||||
},
|
||||
"title": "システムタイトルバーを使用(Linux)"
|
||||
},
|
||||
"zoom": {
|
||||
"reset": "リセット",
|
||||
"title": "ページズーム"
|
||||
|
||||
@ -271,6 +271,7 @@
|
||||
},
|
||||
"toolPermission": {
|
||||
"aria": {
|
||||
"allowAllRequest": "Sempre permitir esta ferramenta",
|
||||
"allowRequest": "Permitir solicitação de ferramenta",
|
||||
"denyRequest": "Negar solicitação de ferramenta",
|
||||
"hideDetails": "Ocultar detalhes da ferramenta",
|
||||
@ -278,6 +279,7 @@
|
||||
"showDetails": "Mostrar detalhes da ferramenta"
|
||||
},
|
||||
"button": {
|
||||
"allowAll": "Sempre Permitir",
|
||||
"cancel": "Cancelar",
|
||||
"run": "Correr"
|
||||
},
|
||||
@ -667,6 +669,10 @@
|
||||
"title": "Novo Tópico"
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"create_session": "Criar uma sessão",
|
||||
"select_agent": "Selecione um agente"
|
||||
},
|
||||
"artifacts": {
|
||||
"button": {
|
||||
"download": "Baixar",
|
||||
@ -1200,6 +1206,8 @@
|
||||
"cancel": "Cancelar",
|
||||
"chat": "Bate-papo",
|
||||
"clear": "Limpar",
|
||||
"clear_all": "Limpar Tudo",
|
||||
"click_to_replace": "Clique para substituir",
|
||||
"close": "Fechar",
|
||||
"collapse": "Recolher",
|
||||
"completed": "Concluído",
|
||||
@ -1234,9 +1242,15 @@
|
||||
"footnote": "Nota de rodapé",
|
||||
"footnotes": "Notas de rodapé",
|
||||
"fullscreen": "Entrou no modo de tela cheia, pressione F11 para sair",
|
||||
"generate_random_seed": "Gerar semente aleatória",
|
||||
"get_embedding_dimension": "Obter dimensão de incorporação",
|
||||
"go_to_settings": "Ir para configurações",
|
||||
"html_preview": "Pré-visualização em HTML",
|
||||
"i_know": "Entendi",
|
||||
"ignore": "Pular",
|
||||
"image_preview": "Pré-visualização da imagem",
|
||||
"image_url": "URL da imagem",
|
||||
"image_url_or_upload": "Insira o URL da imagem ou carregue o arquivo",
|
||||
"inspect": "Verificar",
|
||||
"invalid_value": "Valor inválido",
|
||||
"knowledge_base": "Base de Conhecimento",
|
||||
@ -1246,6 +1260,7 @@
|
||||
"models": "Modelos",
|
||||
"more": "Mais",
|
||||
"name": "Nome",
|
||||
"next_match": "Próxima partida",
|
||||
"no_results": "Nenhum resultado",
|
||||
"none": "Nenhum",
|
||||
"off": "Desligado",
|
||||
@ -1257,13 +1272,17 @@
|
||||
"model": "Selecionar modelo"
|
||||
}
|
||||
},
|
||||
"powered_by": "Desenvolvido por",
|
||||
"preview": "Pré-visualização",
|
||||
"previous_match": "Partida anterior",
|
||||
"prompt": "Prompt",
|
||||
"provider": "Fornecedor",
|
||||
"reasoning_content": "Pensamento profundo concluído",
|
||||
"refresh": "Atualizar",
|
||||
"regenerate": "Regenerar",
|
||||
"remove_image": "Remover imagem",
|
||||
"rename": "Renomear",
|
||||
"required_field": "Campo obrigatório",
|
||||
"reset": "Redefinir",
|
||||
"save": "Salvar",
|
||||
"saved": "Guardado",
|
||||
@ -1286,14 +1305,29 @@
|
||||
"success": "Sucesso",
|
||||
"swap": "Trocar",
|
||||
"topics": "Tópicos",
|
||||
"translate_text": "Traduzir texto",
|
||||
"unknown": "Desconhecido",
|
||||
"unnamed": "Sem nome",
|
||||
"unsubscribe": "Cancelar inscrição",
|
||||
"update_success": "Atualização bem-sucedida",
|
||||
"upload_files": "Carregar arquivo",
|
||||
"upload_image": "Carregar arquivo de imagem",
|
||||
"uploaded_image": "Imagem carregada",
|
||||
"warning": "Aviso",
|
||||
"you": "Você"
|
||||
},
|
||||
"dialog": {
|
||||
"all_files": "Todos os Arquivos",
|
||||
"html_files": "Arquivos HTML",
|
||||
"open_file": "Abrir Arquivo",
|
||||
"pdf_files": "Arquivos PDF",
|
||||
"png_image": "Imagem PNG",
|
||||
"save_as_html": "Salvar como HTML",
|
||||
"save_as_pdf": "Salvar como PDF",
|
||||
"save_file": "Salvar Arquivo",
|
||||
"select_folder": "Selecionar Pasta",
|
||||
"word_document": "Documento do Word"
|
||||
},
|
||||
"docs": {
|
||||
"title": "Documentação de Ajuda"
|
||||
},
|
||||
@ -2015,10 +2049,65 @@
|
||||
"cancelled": "Cancelado",
|
||||
"completed": "Completo",
|
||||
"error": "Ocorreu um erro",
|
||||
"groupHeader": "{{count}} chamadas de ferramenta",
|
||||
"invoking": "Em execução",
|
||||
"labels": {
|
||||
"bash": "Bash",
|
||||
"edit": "Editar",
|
||||
"exitPlanMode": "ModoPlanoDeSaída",
|
||||
"glob": "Globo",
|
||||
"grep": "Grep",
|
||||
"mcpServerTool": "Ferramenta do Servidor MCP",
|
||||
"multiEdit": "MultiEdit",
|
||||
"notebookEdit": "NotebookEdit",
|
||||
"readFile": "Ler Arquivo",
|
||||
"search": "Pesquisar",
|
||||
"skill": "Habilidade",
|
||||
"task": "Tarefa",
|
||||
"todoWrite": "Fazer Escrever",
|
||||
"tool": "Ferramenta",
|
||||
"webFetch": "Busca na Web",
|
||||
"webSearch": "Pesquisa na Web",
|
||||
"write": "Escreva"
|
||||
},
|
||||
"noData": "Nenhum dado disponível para esta ferramenta",
|
||||
"pending": "Pendente",
|
||||
"preview": "Pré-visualização",
|
||||
"raw": "Bruto"
|
||||
"raw": "Bruto",
|
||||
"runningCount": "{{count}} ferramentas em execução",
|
||||
"sections": {
|
||||
"command": "Comando",
|
||||
"exitCode": "Código de Saída",
|
||||
"input": "Entrada",
|
||||
"output": "Saída",
|
||||
"prompt": "Prompt",
|
||||
"searchQuery": "Consulta de Pesquisa",
|
||||
"searchResults": "Resultados da Pesquisa",
|
||||
"stderr": "stderr",
|
||||
"stdout": "stdout"
|
||||
},
|
||||
"status": {
|
||||
"done": "Feito",
|
||||
"error": "Erro",
|
||||
"failed": "Falhou",
|
||||
"running": "Correndo",
|
||||
"success": "Sucesso"
|
||||
},
|
||||
"truncated": "Saída truncada (original: {{size}})",
|
||||
"units": {
|
||||
"done_one": "{{count}} Concluído",
|
||||
"done_other": "{{count}} Concluído",
|
||||
"file_one": "{{count}} arquivo",
|
||||
"file_other": "{{count}} arquivos",
|
||||
"item_one": "{{count}} item",
|
||||
"item_other": "{{count}} itens",
|
||||
"line_one": "{{count}} linha",
|
||||
"line_other": "{{count}} linhas",
|
||||
"plan_one": "{{count}} plano",
|
||||
"plan_other": "{{count}} planos",
|
||||
"result_one": "{{count}} resultado",
|
||||
"result_other": "{{count}} resultados"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"added": "Tópico adicionado com sucesso"
|
||||
@ -2226,14 +2315,18 @@
|
||||
"drop_markdown_hint": "Arraste e solte arquivos ou pastas .md aqui para importar",
|
||||
"empty": "Ainda não existem notas",
|
||||
"expand": "expandir",
|
||||
"exportToWord": "Exportar para Word",
|
||||
"export_failed": "Falha ao exportar para a base de conhecimento",
|
||||
"export_knowledge": "exportar anotações para a base de conhecimento",
|
||||
"export_success": "exportado com sucesso para a base de conhecimento",
|
||||
"export_to_word_failed": "Falha ao exportar para Word",
|
||||
"folder": "pasta",
|
||||
"new_folder": "Nova pasta",
|
||||
"new_note": "Nova nota",
|
||||
"no_content_to_copy": "Não há conteúdo para copiar",
|
||||
"no_content_to_export": "Sem conteúdo para exportar",
|
||||
"no_file_selected": "Selecione o arquivo a ser enviado",
|
||||
"no_note_selected": "Por favor, selecione uma nota primeiro",
|
||||
"no_valid_files": "Nenhum arquivo válido foi carregado",
|
||||
"open_folder": "Abrir pasta externa",
|
||||
"open_outside": "Abrir externamente",
|
||||
@ -2484,6 +2577,39 @@
|
||||
}
|
||||
},
|
||||
"custom_size": "Dimensão personalizada",
|
||||
"dmxapi": {
|
||||
"generating_tip": "Gerando com o modelo oficial, o tempo estimado de espera é de 2 a 5 minutos para obter os melhores resultados. Verifique os logs do backend DMXAPI para saber o custo desta operação.",
|
||||
"style": "Estilo:",
|
||||
"style_types": {
|
||||
"25d_animation": "Animação 2.5D",
|
||||
"3d_cartoon": "Desenho animado 3D",
|
||||
"american_retro": "Retrô Americano",
|
||||
"baroque": "Barroco",
|
||||
"cartoon_illustration": "Ilustração de desenho animado",
|
||||
"chinese_gongbi": "Gongbi Chinês",
|
||||
"clay": "Argila",
|
||||
"cyberpunk": "Ciborgue",
|
||||
"felt": "Sentiu",
|
||||
"flat": "Plano",
|
||||
"fresh_anime": "Anime Fresco",
|
||||
"ghibli": "Ghibli",
|
||||
"japanese_anime": "Anime Japonês",
|
||||
"little_people_book": "Livro de Gente Pequena",
|
||||
"monet_garden": "Jardim Monet",
|
||||
"oil_painting": "Pintura a Óleo",
|
||||
"pixar": "Pixar",
|
||||
"pixel_art": "Arte em Pixel",
|
||||
"poetic_ancient": "Poético Antigo",
|
||||
"psychedelic": "Psicodélico",
|
||||
"sketch": "Esboço",
|
||||
"street_art": "Arte de Rua",
|
||||
"texture": "Textura",
|
||||
"ukiyo_e": "Ukiyo-e",
|
||||
"watercolor": "Aquarela",
|
||||
"wood_carving": "Escultura em Madeira",
|
||||
"yarn_doll": "Boneca de Fios"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"image_file": "Imagem editada",
|
||||
"magic_prompt_option_tip": "Otimização inteligente da palavra-chave de edição",
|
||||
@ -2508,6 +2634,7 @@
|
||||
"style_type_tip": "Estilo de geração da imagem, aplicável apenas às versões V_2 e superiores",
|
||||
"width": "Largura"
|
||||
},
|
||||
"generate_failed": "Falha ao gerar imagem",
|
||||
"generated_image": "Imagem gerada",
|
||||
"go_to_settings": "Ir para configurações",
|
||||
"guidance_scale": "Escala de Direção",
|
||||
@ -2518,6 +2645,7 @@
|
||||
"image_file_required": "Por favor, faça o upload da imagem primeiro",
|
||||
"image_file_retry": "Por favor, faça o upload novamente da imagem",
|
||||
"image_handle_required": "Por favor, faça o upload da imagem primeiro",
|
||||
"image_mix_failed": "Falha ao misturar imagens",
|
||||
"image_placeholder": "Nenhuma imagem disponível no momento",
|
||||
"image_retry": "Tentar novamente",
|
||||
"image_size_options": {
|
||||
@ -2527,6 +2655,7 @@
|
||||
"inference_steps_tip": "Número de passos de inferência a serem executados. Quanto mais passos, melhor a qualidade, mas mais demorado",
|
||||
"input_image": "Imagem de entrada",
|
||||
"input_parameters": "Parâmetros de entrada",
|
||||
"invalid_image_url": "Formato de URL de imagem inválido",
|
||||
"learn_more": "Saiba Mais",
|
||||
"magic_prompt_option": "Aprimoramento de Prompt",
|
||||
"mode": {
|
||||
@ -2548,6 +2677,7 @@
|
||||
"no_image_generation_model": "Nenhum modelo de geração de imagem disponível no momento. Por favor, adicione um modelo e defina o tipo de endpoint como {{endpoint_type}}",
|
||||
"number_images": "Quantidade de Imagens",
|
||||
"number_images_tip": "Quantidade de imagens a serem geradas por vez (1-4)",
|
||||
"operation_failed": "Operação falhou, por favor tente novamente mais tarde",
|
||||
"paint_course": "Tutorial",
|
||||
"per_image": "Por imagem",
|
||||
"per_images": "Por imagem",
|
||||
@ -2623,6 +2753,26 @@
|
||||
"resemblance": "Similaridade",
|
||||
"resemblance_tip": "Controla o nível de semelhança entre o resultado ampliado e a imagem original",
|
||||
"seed_tip": "Controla a aleatoriedade do resultado de ampliação"
|
||||
},
|
||||
"zhipu": {
|
||||
"custom_size_divisible": "Tamanho personalizado deve ser divisível por 16",
|
||||
"custom_size_hint": "Largura e altura devem estar entre 512px e 2048px, ser divisíveis por 16, e o total de pixels não pode exceder 2^21px.",
|
||||
"custom_size_pixels": "O total de pixels de tamanho personalizado não pode exceder 2.097.152",
|
||||
"custom_size_range": "Tamanho personalizado deve estar entre 512px-2048px",
|
||||
"custom_size_required": "Por favor, defina largura e altura personalizadas",
|
||||
"image_sizes": {
|
||||
"1024x1024_default": "1024x1024 (Padrão)",
|
||||
"1152x864": "1152x864",
|
||||
"1344x768": "1344x768",
|
||||
"1440x720": "1440x720",
|
||||
"720x1440": "720x1440",
|
||||
"768x1344": "768x1344",
|
||||
"864x1152": "864x1152"
|
||||
},
|
||||
"quality_options": {
|
||||
"hd": "HD",
|
||||
"standard_default": "Padrão (Padrão)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
@ -2633,6 +2783,20 @@
|
||||
"category": "categoria",
|
||||
"commands": "comando",
|
||||
"confirm_uninstall": "Tem certeza de que deseja desinstalar {{name}}?",
|
||||
"content_saved": "Conteúdo do plugin salvo com sucesso",
|
||||
"detail": {
|
||||
"allowed_tools": "Ferramentas Permitidas",
|
||||
"author": "Autor",
|
||||
"content": "Conteúdo",
|
||||
"description": "Descrição",
|
||||
"file": "Arquivo",
|
||||
"installed": "Instalado",
|
||||
"metadata": "Metadados",
|
||||
"size": "Tamanho",
|
||||
"source": "Fonte",
|
||||
"tags": "Etiquetas",
|
||||
"tools": "Ferramentas"
|
||||
},
|
||||
"install": "Instalação",
|
||||
"install_plugins_from_browser": "Navegue pelos plugins disponíveis para começar a usar",
|
||||
"installing": "Instalando...",
|
||||
@ -2983,6 +3147,11 @@
|
||||
"summary": "Resumir",
|
||||
"translate": "Traduzir"
|
||||
},
|
||||
"prompt": {
|
||||
"explain": "Por favor, explique o seguinte conteúdo.",
|
||||
"refine": "Por favor, otimize ou aprimore o conteúdo fornecido pelo usuário dentro da tag XML <INPUT>, mantendo o significado e a integridade do conteúdo original. Requisitos: sua saída deve estar no mesmo idioma que o entrada do usuário; não inclua nenhuma explicação deste prompt, apenas forneça a resposta diretamente; não inclua tags XML, forneça o conteúdo otimizado diretamente:",
|
||||
"summary": "Resuma o conteúdo a seguir. Requisitos: Responda em {{language}}; não inclua nenhuma explicação deste prompt, apenas forneça a resposta diretamente:"
|
||||
},
|
||||
"translate": {
|
||||
"smart_translate_tips": "Tradução inteligente: o conteúdo será priorizado para tradução no idioma de destino; se o conteúdo já estiver no idioma de destino, será traduzido para o idioma alternativo"
|
||||
},
|
||||
@ -4076,6 +4245,10 @@
|
||||
"jsonModeHint": "Edite a representação JSON da configuração do servidor MCP. Certifique-se de que o formato está correto antes de salvar.",
|
||||
"jsonSaveError": "Falha ao salvar configuração JSON",
|
||||
"jsonSaveSuccess": "Configuração JSON salva com sucesso",
|
||||
"lanyun": {
|
||||
"description": "Plataforma Cloud da Lanyun Technology – Serviço MCP",
|
||||
"name": "Lanyun Technology"
|
||||
},
|
||||
"logoUrl": "URL do Logotipo",
|
||||
"logs": "Registros",
|
||||
"longRunning": "Modo de execução prolongada",
|
||||
@ -4172,6 +4345,14 @@
|
||||
"getToken": "Obter token de API",
|
||||
"getTokenDescription": "Obtenha um token de API pessoal da sua conta",
|
||||
"noServersAvailable": "Nenhum servidor MCP disponível",
|
||||
"providerDescriptions": {
|
||||
"302ai": "302.Serviço MCP da Plataforma AI",
|
||||
"bailian": "Alibaba Cloud Bailian Plataforma Serviço MCP",
|
||||
"lanyun": "Plataforma Cloud da Lanyun Technology - Serviço MCP",
|
||||
"mcprouter": "Plataforma de Roteamento MCP Serviço MCP",
|
||||
"modelscope": "Serviço MCP da Plataforma ModelScope",
|
||||
"tokenflux": "Serviço MCP da Plataforma TokenFlux"
|
||||
},
|
||||
"selectProvider": "Selecione o provedor:",
|
||||
"setToken": "Digite seu token",
|
||||
"success": "Servidor MCP sincronizado com sucesso",
|
||||
@ -4649,6 +4830,9 @@
|
||||
"title": "Excluir Fornecedor"
|
||||
},
|
||||
"dmxapi": {
|
||||
"platform_enterprise": "ssvip.DMXAPI.com (Empresa)",
|
||||
"platform_international": "www.DMXAPI.com (Internacional)",
|
||||
"platform_official": "www.DMXAPI.cn (CNY)",
|
||||
"select_platform": "Selecionar Plataforma"
|
||||
},
|
||||
"docs_check": "Verificar",
|
||||
@ -4985,6 +5169,13 @@
|
||||
"show": "Mostrar ícone de bandeja",
|
||||
"title": "Tray"
|
||||
},
|
||||
"use_system_title_bar": {
|
||||
"confirm": {
|
||||
"content": "Alterar o estilo da barra de título requer reiniciar o aplicativo para ter efeito. Deseja reiniciar agora?",
|
||||
"title": "Reinicialização Necessária"
|
||||
},
|
||||
"title": "Usar Barra de Título do Sistema (Linux)"
|
||||
},
|
||||
"zoom": {
|
||||
"reset": "Redefinir",
|
||||
"title": "Escala"
|
||||
|
||||
@ -271,6 +271,7 @@
|
||||
},
|
||||
"toolPermission": {
|
||||
"aria": {
|
||||
"allowAllRequest": "Permiteți întotdeauna acest instrument",
|
||||
"allowRequest": "Permite cererea instrumentului",
|
||||
"denyRequest": "Refuză cererea instrumentului",
|
||||
"hideDetails": "Ascunde detaliile instrumentului",
|
||||
@ -278,6 +279,7 @@
|
||||
"showDetails": "Afișează detaliile instrumentului"
|
||||
},
|
||||
"button": {
|
||||
"allowAll": "Permiteți întotdeauna",
|
||||
"cancel": "Anulează",
|
||||
"run": "Rulează"
|
||||
},
|
||||
@ -667,6 +669,10 @@
|
||||
"title": "Subiect nou"
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"create_session": "Creează o sesiune",
|
||||
"select_agent": "Selectați un agent"
|
||||
},
|
||||
"artifacts": {
|
||||
"button": {
|
||||
"download": "Descarcă",
|
||||
@ -1134,7 +1140,7 @@
|
||||
"model": "Model",
|
||||
"model_placeholder": "Selectează modelul de utilizat",
|
||||
"model_required": "Te rugăm să selectezi un model",
|
||||
"select_folder": "Selectează folderul",
|
||||
"select_folder": "Selectează dosarul",
|
||||
"set_custom_path": "Setează calea personalizată a terminalului",
|
||||
"supported_providers": "Furnizori acceptați",
|
||||
"terminal": "Terminal",
|
||||
@ -1200,6 +1206,8 @@
|
||||
"cancel": "Anulează",
|
||||
"chat": "Chat",
|
||||
"clear": "Golește",
|
||||
"clear_all": "Șterge tot",
|
||||
"click_to_replace": "Faceți clic pentru a înlocui",
|
||||
"close": "Închide",
|
||||
"collapse": "Restrânge",
|
||||
"completed": "Finalizat",
|
||||
@ -1234,9 +1242,15 @@
|
||||
"footnote": "Conținut de referință",
|
||||
"footnotes": "Referințe",
|
||||
"fullscreen": "S-a intrat în modul ecran complet. Apasă F11 pentru a ieși",
|
||||
"generate_random_seed": "Generează sămânță aleatoare",
|
||||
"get_embedding_dimension": "Obține dimensiunea de înglobare",
|
||||
"go_to_settings": "Mergi la setări",
|
||||
"html_preview": "Previzualizare HTML",
|
||||
"i_know": "Am înțeles",
|
||||
"ignore": "Ignoră",
|
||||
"image_preview": "Previzualizare imagine",
|
||||
"image_url": "URL imagine",
|
||||
"image_url_or_upload": "Introdu URL-ul imaginii sau încarcă fișierul",
|
||||
"inspect": "Inspectează",
|
||||
"invalid_value": "Valoare invalidă",
|
||||
"knowledge_base": "Bază de cunoștințe",
|
||||
@ -1246,6 +1260,7 @@
|
||||
"models": "Modele",
|
||||
"more": "Mai mult",
|
||||
"name": "Nume",
|
||||
"next_match": "Următorul meci",
|
||||
"no_results": "Niciun rezultat",
|
||||
"none": "Nimic",
|
||||
"off": "Oprit",
|
||||
@ -1257,13 +1272,17 @@
|
||||
"model": "Selectează un model"
|
||||
}
|
||||
},
|
||||
"powered_by": "Cu tehnologia",
|
||||
"preview": "Previzualizare",
|
||||
"previous_match": "Meciul anterior",
|
||||
"prompt": "Prompt",
|
||||
"provider": "Furnizor",
|
||||
"reasoning_content": "Raționament profund",
|
||||
"refresh": "Reîmprospătează",
|
||||
"regenerate": "Regenerează",
|
||||
"remove_image": "Elimină imaginea",
|
||||
"rename": "Redenumește",
|
||||
"required_field": "Câmp obligatoriu",
|
||||
"reset": "Resetează",
|
||||
"save": "Salvează",
|
||||
"saved": "Salvat",
|
||||
@ -1286,14 +1305,29 @@
|
||||
"success": "Succes",
|
||||
"swap": "Schimbă",
|
||||
"topics": "Subiecte",
|
||||
"translate_text": "Traduceți textul",
|
||||
"unknown": "Necunoscut",
|
||||
"unnamed": "Fără nume",
|
||||
"unsubscribe": "Dezabonează-te",
|
||||
"update_success": "Actualizat cu succes",
|
||||
"upload_files": "Încarcă fișier",
|
||||
"upload_image": "Încarcă fișier imagine",
|
||||
"uploaded_image": "Imagine încărcată",
|
||||
"warning": "Avertisment",
|
||||
"you": "Tu"
|
||||
},
|
||||
"dialog": {
|
||||
"all_files": "Toate fișierele",
|
||||
"html_files": "Fișiere HTML",
|
||||
"open_file": "Deschide fișier",
|
||||
"pdf_files": "Fișiere PDF",
|
||||
"png_image": "Imagine PNG",
|
||||
"save_as_html": "Salvează ca HTML",
|
||||
"save_as_pdf": "Salvează ca PDF",
|
||||
"save_file": "Salvează fișier",
|
||||
"select_folder": "Selectează folderul",
|
||||
"word_document": "Document Word"
|
||||
},
|
||||
"docs": {
|
||||
"title": "Documentație"
|
||||
},
|
||||
@ -2015,10 +2049,65 @@
|
||||
"cancelled": "Anulat",
|
||||
"completed": "Finalizat",
|
||||
"error": "A apărut o eroare",
|
||||
"groupHeader": "{{count}} apeluri de instrumente",
|
||||
"invoking": "Se invocă",
|
||||
"labels": {
|
||||
"bash": "Bash",
|
||||
"edit": "Editare",
|
||||
"exitPlanMode": "ExitPlanMode",
|
||||
"glob": "Glob",
|
||||
"grep": "Grep",
|
||||
"mcpServerTool": "Instrument Server MCP",
|
||||
"multiEdit": "MultiEdit",
|
||||
"notebookEdit": "NotebookEdit",
|
||||
"readFile": "Citește fișier",
|
||||
"search": "Căutare",
|
||||
"skill": "Abilitate",
|
||||
"task": "Sarcină",
|
||||
"todoWrite": "Totul Scrie",
|
||||
"tool": "Unelte",
|
||||
"webFetch": "Preluare web",
|
||||
"webSearch": "Căutare pe web",
|
||||
"write": "Scrie"
|
||||
},
|
||||
"noData": "Nu sunt disponibile date pentru acest instrument",
|
||||
"pending": "În așteptare",
|
||||
"preview": "Previzualizare",
|
||||
"raw": "Brut"
|
||||
"raw": "Brut",
|
||||
"runningCount": "{{count}} instrumente în execuție",
|
||||
"sections": {
|
||||
"command": "Comandă",
|
||||
"exitCode": "Cod de ieșire",
|
||||
"input": "Intrare",
|
||||
"output": "Ieșire",
|
||||
"prompt": "Punct",
|
||||
"searchQuery": "Interogare de căutare",
|
||||
"searchResults": "Rezultatele căutării",
|
||||
"stderr": "stderr",
|
||||
"stdout": "stdout"
|
||||
},
|
||||
"status": {
|
||||
"done": "Gata",
|
||||
"error": "Eroare",
|
||||
"failed": "Eșuat",
|
||||
"running": "Alergare",
|
||||
"success": "Succes"
|
||||
},
|
||||
"truncated": "Ieșire trunchiată (original: {{size}})",
|
||||
"units": {
|
||||
"done_one": "{{count}} Finalizat",
|
||||
"done_other": "{{count}} Finalizat",
|
||||
"file_one": "{{count}} fișier",
|
||||
"file_other": "{{count}} fișiere",
|
||||
"item_one": "{{count}} articol",
|
||||
"item_other": "{{count}} articole",
|
||||
"line_one": "{{count}} linie",
|
||||
"line_other": "{{count}} linii",
|
||||
"plan_one": "{{count}} plan",
|
||||
"plan_other": "{{count}} planuri",
|
||||
"result_one": "{{count}} rezultat",
|
||||
"result_other": "{{count}} rezultate"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"added": "Subiect nou adăugat"
|
||||
@ -2226,14 +2315,18 @@
|
||||
"drop_markdown_hint": "Trage fișiere sau dosare .md aici pentru a importa",
|
||||
"empty": "Încă nu există notițe disponibile",
|
||||
"expand": "Extinde",
|
||||
"exportToWord": "Exportă în Word",
|
||||
"export_failed": "Exportul în baza de cunoștințe a eșuat",
|
||||
"export_knowledge": "Exportă notițele în baza de cunoștințe",
|
||||
"export_success": "Exportat cu succes în baza de cunoștințe",
|
||||
"export_to_word_failed": "Exportul în Word a eșuat",
|
||||
"folder": "Dosar",
|
||||
"new_folder": "Dosar nou",
|
||||
"new_note": "Creează o notiță nouă",
|
||||
"no_content_to_copy": "Niciun conținut de copiat",
|
||||
"no_content_to_export": "Niciun conținut de exportat",
|
||||
"no_file_selected": "Te rugăm să selectezi fișierul de încărcat",
|
||||
"no_note_selected": "Te rog selectează mai întâi o notă",
|
||||
"no_valid_files": "Nu a fost încărcat niciun fișier valid",
|
||||
"open_folder": "Deschide un dosar extern",
|
||||
"open_outside": "Deschide extern",
|
||||
@ -2484,6 +2577,39 @@
|
||||
}
|
||||
},
|
||||
"custom_size": "Dimensiune personalizată",
|
||||
"dmxapi": {
|
||||
"generating_tip": "Generarea cu modelul oficial, timpul estimat de așteptare este de 2-5 minute pentru cele mai bune rezultate. Te rugăm să verifici logurile backend DMXAPI pentru costul acestei operațiuni.",
|
||||
"style": ", Stil:",
|
||||
"style_types": {
|
||||
"25d_animation": "Animație 2.5D",
|
||||
"3d_cartoon": "Desen animat 3D",
|
||||
"american_retro": "Retro American",
|
||||
"baroque": "Baroc",
|
||||
"cartoon_illustration": "Ilustrație de desen animat",
|
||||
"chinese_gongbi": "Gongbi chinezesc",
|
||||
"clay": "Argilă",
|
||||
"cyberpunk": "Cyberpunk",
|
||||
"felt": "Filț",
|
||||
"flat": "Plat",
|
||||
"fresh_anime": "Anime Proaspăt",
|
||||
"ghibli": "Ghibli",
|
||||
"japanese_anime": "Anime japonez",
|
||||
"little_people_book": "Cartea Oamenilor Mici",
|
||||
"monet_garden": "Grădina Monet",
|
||||
"oil_painting": "Pictură în ulei",
|
||||
"pixar": "Pixar",
|
||||
"pixel_art": "Artă pixelată",
|
||||
"poetic_ancient": "Poetic Antic",
|
||||
"psychedelic": "Psichedelic",
|
||||
"sketch": "Schiță",
|
||||
"street_art": "Artă stradală",
|
||||
"texture": "Textură",
|
||||
"ukiyo_e": "Ukiyo-e",
|
||||
"watercolor": "Acuarelă",
|
||||
"wood_carving": "Sculptură în lemn",
|
||||
"yarn_doll": "Păpușă din fire de lână"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"image_file": "Imagine editată",
|
||||
"magic_prompt_option_tip": "Îmbunătățește inteligent prompturile de editare",
|
||||
@ -2508,6 +2634,7 @@
|
||||
"style_type_tip": "Stil generare imagine pentru V_2 și versiuni ulterioare",
|
||||
"width": "Lățime"
|
||||
},
|
||||
"generate_failed": "Nu s-a putut genera imaginea",
|
||||
"generated_image": "Imagine generată",
|
||||
"go_to_settings": "Mergi la Setări",
|
||||
"guidance_scale": "Scară de ghidare",
|
||||
@ -2518,6 +2645,7 @@
|
||||
"image_file_required": "Te rugăm să încarci mai întâi o imagine",
|
||||
"image_file_retry": "Te rugăm să reîncarci mai întâi o imagine",
|
||||
"image_handle_required": "Te rugăm să încarci mai întâi o imagine.",
|
||||
"image_mix_failed": "Nu s-au putut combina imaginile",
|
||||
"image_placeholder": "Nicio imagine disponibilă",
|
||||
"image_retry": "Reîncearcă",
|
||||
"image_size_options": {
|
||||
@ -2527,6 +2655,7 @@
|
||||
"inference_steps_tip": "Numărul de pași de inferență de efectuat. Mai mulți pași produc o calitate mai mare, dar durează mai mult",
|
||||
"input_image": "Imagine de intrare",
|
||||
"input_parameters": "Parametri de intrare",
|
||||
"invalid_image_url": "Formatul URL-ului imaginii este invalid",
|
||||
"learn_more": "Află mai multe",
|
||||
"magic_prompt_option": "Prompt magic",
|
||||
"mode": {
|
||||
@ -2548,6 +2677,7 @@
|
||||
"no_image_generation_model": "Niciun model de generare imagini disponibil, te rugăm să adaugi un model și să setezi tipul endpoint-ului la {{endpoint_type}}",
|
||||
"number_images": "Număr imagini",
|
||||
"number_images_tip": "Numărul de imagini de generat (1-4)",
|
||||
"operation_failed": "Operațiunea a eșuat, vă rugăm să încercați din nou mai târziu",
|
||||
"paint_course": "tutorial",
|
||||
"per_image": "pe imagine",
|
||||
"per_images": "pe imagini",
|
||||
@ -2623,6 +2753,26 @@
|
||||
"resemblance": "Similaritate",
|
||||
"resemblance_tip": "Controlează similaritatea cu imaginea originală",
|
||||
"seed_tip": "Controlează aleatoriul scalării"
|
||||
},
|
||||
"zhipu": {
|
||||
"custom_size_divisible": "Dimensiunea personalizată trebuie să fie divizibilă cu 16",
|
||||
"custom_size_hint": "Lățimea și înălțimea trebuie să fie între 512px-2048px, divizibile cu 16, iar numărul total de pixeli nu poate depăși 2^21px",
|
||||
"custom_size_pixels": "Numărul total de pixeli ai dimensiunii personalizate nu poate depăși 2.097.152",
|
||||
"custom_size_range": "Dimensiunea personalizată trebuie să fie între 512px și 2048px.",
|
||||
"custom_size_required": "Vă rugăm să setați lățimea și înălțimea personalizate",
|
||||
"image_sizes": {
|
||||
"1024x1024_default": "1024x1024 (Implicit)",
|
||||
"1152x864": "1152x864",
|
||||
"1344x768": "1344x768",
|
||||
"1440x720": "1440x720",
|
||||
"720x1440": "720x1440",
|
||||
"768x1344": "768x1344",
|
||||
"864x1152": "864x1152"
|
||||
},
|
||||
"quality_options": {
|
||||
"hd": "HD",
|
||||
"standard_default": "Standard (Implicit)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
@ -2633,6 +2783,20 @@
|
||||
"category": "Categorie",
|
||||
"commands": "Comenzi",
|
||||
"confirm_uninstall": "Ești sigur că vrei să dezinstalezi {{name}}?",
|
||||
"content_saved": "Conținutul plugin-ului a fost salvat cu succes",
|
||||
"detail": {
|
||||
"allowed_tools": "Unelte Permise",
|
||||
"author": "Autor",
|
||||
"content": "Conținut",
|
||||
"description": "Descriere",
|
||||
"file": "Fișier",
|
||||
"installed": "Instalat",
|
||||
"metadata": "Metadate",
|
||||
"size": "Dimensiune",
|
||||
"source": "Sursă",
|
||||
"tags": "Etichete",
|
||||
"tools": "Unelte"
|
||||
},
|
||||
"install": "Instalează",
|
||||
"install_plugins_from_browser": "Răsfoiește pluginurile disponibile pentru a începe",
|
||||
"installing": "Se instalează...",
|
||||
@ -2983,6 +3147,11 @@
|
||||
"summary": "Rezumat",
|
||||
"translate": "Tradu"
|
||||
},
|
||||
"prompt": {
|
||||
"explain": "Te rog să explici următorul conținut. Cerințe: Răspunde în {{language}}; nu include nicio explicație a acestui prompt, oferă direct răspunsul:",
|
||||
"refine": "Te rugăm să optimizezi sau să îmbunătățești conținutul introdus de utilizator, cuprins în eticheta XML <INPUT>, păstrând sensul și integritatea textului original. Cerințe: rezultatul tău trebuie să fie în aceeași limbă cu cel introdus de utilizator; nu include nicio explicație referitoare la acest prompt, oferă direct răspunsul; nu afișa etichetele XML, ci doar conținutul optimizat.",
|
||||
"summary": "Vă rog să rezumați următorul conținut. Cerințe: Răspundeți în {{language}}; nu includeți nicio explicație a acestui prompt, oferiți direct răspunsul:"
|
||||
},
|
||||
"translate": {
|
||||
"smart_translate_tips": "Traducere inteligentă: Conținutul va fi tradus mai întâi în limba țintă; conținutul aflat deja în limba țintă va fi tradus în limba alternativă"
|
||||
},
|
||||
@ -4076,6 +4245,10 @@
|
||||
"jsonModeHint": "Editează reprezentarea JSON a configurației serverului MCP. Te rugăm să te asiguri că formatul este corect înainte de salvare.",
|
||||
"jsonSaveError": "Nu s-a putut salva configurația JSON.",
|
||||
"jsonSaveSuccess": "Configurația JSON a fost salvată.",
|
||||
"lanyun": {
|
||||
"description": "Lanyun Technology Cloud Platform MCP Service",
|
||||
"name": "Lanyun Technology"
|
||||
},
|
||||
"logoUrl": "URL logo",
|
||||
"logs": "Jurnale",
|
||||
"longRunning": "Mod rulare lungă",
|
||||
@ -4172,6 +4345,14 @@
|
||||
"getToken": "Obține token API",
|
||||
"getTokenDescription": "Obține tokenul tău personal API din contul tău",
|
||||
"noServersAvailable": "Nu există servere MCP disponibile",
|
||||
"providerDescriptions": {
|
||||
"302ai": "Serviciul MCP al Platformei 302.AI",
|
||||
"bailian": "Serviciul MCP al Platformei Bailian Alibaba Cloud",
|
||||
"lanyun": "Platforma Cloud Lanyun Technology - Serviciul MCP",
|
||||
"mcprouter": "Platformă Router MCP Serviciu MCP",
|
||||
"modelscope": "Serviciul MCP al Platformei ModelScope",
|
||||
"tokenflux": "Serviciul MCP al Platformei TokenFlux"
|
||||
},
|
||||
"selectProvider": "Selectează furnizor:",
|
||||
"setToken": "Introdu tokenul tău",
|
||||
"success": "Sincronizare servere MCP reușită",
|
||||
@ -4649,6 +4830,9 @@
|
||||
"title": "Șterge furnizor"
|
||||
},
|
||||
"dmxapi": {
|
||||
"platform_enterprise": "ssvip.DMXAPI.com (Întreprindere)",
|
||||
"platform_international": "www.DMXAPI.com (Internațional)",
|
||||
"platform_official": "www.DMXAPI.cn (CNY)",
|
||||
"select_platform": "Selectează platforma"
|
||||
},
|
||||
"docs_check": "Verifică",
|
||||
@ -4985,6 +5169,13 @@
|
||||
"show": "Afișează pictograma în zona de notificare",
|
||||
"title": "Zonă de notificare"
|
||||
},
|
||||
"use_system_title_bar": {
|
||||
"confirm": {
|
||||
"content": "Schimbarea stilului barei de titlu necesită repornirea aplicației pentru a intra în vigoare. Doriți să reporniți acum?",
|
||||
"title": "Repornire necesară"
|
||||
},
|
||||
"title": "Folosește bara de titlu a sistemului (Linux)"
|
||||
},
|
||||
"zoom": {
|
||||
"reset": "Resetează",
|
||||
"title": "Zoom pagină"
|
||||
|
||||
@ -271,6 +271,7 @@
|
||||
},
|
||||
"toolPermission": {
|
||||
"aria": {
|
||||
"allowAllRequest": "Всегда разрешать этот инструмент",
|
||||
"allowRequest": "Разрешить запрос инструмента",
|
||||
"denyRequest": "Отклонить запрос на инструмент",
|
||||
"hideDetails": "Скрыть сведения об инструменте",
|
||||
@ -278,6 +279,7 @@
|
||||
"showDetails": "Показать сведения об инструменте"
|
||||
},
|
||||
"button": {
|
||||
"allowAll": "Всегда разрешать",
|
||||
"cancel": "Отмена",
|
||||
"run": "Беги"
|
||||
},
|
||||
@ -667,6 +669,10 @@
|
||||
"title": "Новый топик"
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"create_session": "Создать сессию",
|
||||
"select_agent": "Выберите агента"
|
||||
},
|
||||
"artifacts": {
|
||||
"button": {
|
||||
"download": "Скачать",
|
||||
@ -1200,6 +1206,8 @@
|
||||
"cancel": "Отмена",
|
||||
"chat": "Чат",
|
||||
"clear": "Очистить",
|
||||
"clear_all": "Очистить всё",
|
||||
"click_to_replace": "Щёлкните для замены",
|
||||
"close": "Закрыть",
|
||||
"collapse": "Свернуть",
|
||||
"completed": "Завершено",
|
||||
@ -1234,9 +1242,15 @@
|
||||
"footnote": "Цитируемый контент",
|
||||
"footnotes": "Сноски",
|
||||
"fullscreen": "Вы вошли в полноэкранный режим. Нажмите F11 для выхода",
|
||||
"generate_random_seed": "Сгенерировать случайное зерно",
|
||||
"get_embedding_dimension": "Получить размерность вложения",
|
||||
"go_to_settings": "Перейти в настройки",
|
||||
"html_preview": "Предпросмотр HTML",
|
||||
"i_know": "Я понял",
|
||||
"ignore": "Игнорировать",
|
||||
"image_preview": "Предпросмотр изображения",
|
||||
"image_url": "URL изображения",
|
||||
"image_url_or_upload": "Введите URL изображения или загрузите файл",
|
||||
"inspect": "Осмотреть",
|
||||
"invalid_value": "недопустимое значение",
|
||||
"knowledge_base": "База знаний",
|
||||
@ -1246,6 +1260,7 @@
|
||||
"models": "Модели",
|
||||
"more": "Ещё",
|
||||
"name": "Имя",
|
||||
"next_match": "Следующий матч",
|
||||
"no_results": "Результатов не найдено",
|
||||
"none": "без",
|
||||
"off": "Выкл",
|
||||
@ -1257,13 +1272,17 @@
|
||||
"model": "Выбор модели"
|
||||
}
|
||||
},
|
||||
"powered_by": "С поддержкой",
|
||||
"preview": "Предварительный просмотр",
|
||||
"previous_match": "Предыдущий матч",
|
||||
"prompt": "Промпт",
|
||||
"provider": "Провайдер",
|
||||
"reasoning_content": "Глубокий анализ",
|
||||
"refresh": "Обновить",
|
||||
"regenerate": "Пересоздать",
|
||||
"remove_image": "Удалить изображение",
|
||||
"rename": "Переименовать",
|
||||
"required_field": "Обязательное поле",
|
||||
"reset": "Сбросить",
|
||||
"save": "Сохранить",
|
||||
"saved": "Сохранено",
|
||||
@ -1286,14 +1305,29 @@
|
||||
"success": "Успешно",
|
||||
"swap": "Поменять местами",
|
||||
"topics": "Топики",
|
||||
"translate_text": "Перевести текст",
|
||||
"unknown": "Неизвестно",
|
||||
"unnamed": "Без имени",
|
||||
"unsubscribe": "Отписаться",
|
||||
"update_success": "Обновление выполнено успешно",
|
||||
"upload_files": "Загрузить файл",
|
||||
"upload_image": "Загрузить файл изображения",
|
||||
"uploaded_image": "Загруженное изображение",
|
||||
"warning": "Предупреждение",
|
||||
"you": "Вы"
|
||||
},
|
||||
"dialog": {
|
||||
"all_files": "Все файлы",
|
||||
"html_files": "Файлы HTML",
|
||||
"open_file": "Открыть файл",
|
||||
"pdf_files": "Файлы PDF",
|
||||
"png_image": "PNG-изображение",
|
||||
"save_as_html": "Сохранить как HTML",
|
||||
"save_as_pdf": "Сохранить как PDF",
|
||||
"save_file": "Сохранить файл",
|
||||
"select_folder": "Выбрать папку",
|
||||
"word_document": "Документ Word"
|
||||
},
|
||||
"docs": {
|
||||
"title": "Документация"
|
||||
},
|
||||
@ -2015,10 +2049,65 @@
|
||||
"cancelled": "Отменено",
|
||||
"completed": "Завершено",
|
||||
"error": "Произошла ошибка",
|
||||
"groupHeader": "{{count}} вызовов инструментов",
|
||||
"invoking": "Вызов",
|
||||
"labels": {
|
||||
"bash": "Баш",
|
||||
"edit": "Редактировать",
|
||||
"exitPlanMode": "РежимВыходаПлана",
|
||||
"glob": "Глоб",
|
||||
"grep": "Grep",
|
||||
"mcpServerTool": "Инструмент сервера MCP",
|
||||
"multiEdit": "МультиРедакт",
|
||||
"notebookEdit": "РедакторБлокнота",
|
||||
"readFile": "Прочитать файл",
|
||||
"search": "Поиск",
|
||||
"skill": "Навык",
|
||||
"task": "Задача",
|
||||
"todoWrite": "Написать",
|
||||
"tool": "Инструмент",
|
||||
"webFetch": "Веб-запрос",
|
||||
"webSearch": "Поиск в интернете",
|
||||
"write": "Написать"
|
||||
},
|
||||
"noData": "Нет доступных данных для этого инструмента",
|
||||
"pending": "Ожидание",
|
||||
"preview": "Предпросмотр",
|
||||
"raw": "Исходный"
|
||||
"raw": "Исходный",
|
||||
"runningCount": "{{count}} инструментов запущено",
|
||||
"sections": {
|
||||
"command": "Команда",
|
||||
"exitCode": "Код выхода",
|
||||
"input": "Ввод",
|
||||
"output": "Вывод",
|
||||
"prompt": "Запрос",
|
||||
"searchQuery": "поисковый запрос",
|
||||
"searchResults": "Результаты поиска",
|
||||
"stderr": "stderr",
|
||||
"stdout": "stdout"
|
||||
},
|
||||
"status": {
|
||||
"done": "Готово",
|
||||
"error": "Ошибка",
|
||||
"failed": "Не удалось",
|
||||
"running": "Бег",
|
||||
"success": "Успех"
|
||||
},
|
||||
"truncated": "Вывод усечён (оригинал: {{size}})",
|
||||
"units": {
|
||||
"done_one": "{{count}} Готово",
|
||||
"done_other": "{{count}} Готово",
|
||||
"file_one": "{{count}} файл",
|
||||
"file_other": "{{count}} файлов",
|
||||
"item_one": "{{count}} элемент",
|
||||
"item_other": "{{count}} предметов",
|
||||
"line_one": "{{count}} строка",
|
||||
"line_other": "{{count}} строк",
|
||||
"plan_one": "{{count}} план",
|
||||
"plan_other": "{{count}} планов",
|
||||
"result_one": "{{count}} результат",
|
||||
"result_other": "{{count}} результатов"
|
||||
}
|
||||
},
|
||||
"topic": {
|
||||
"added": "Новый топик добавлен"
|
||||
@ -2226,14 +2315,18 @@
|
||||
"drop_markdown_hint": "Перетащите сюда файлы или папки .md для импорта",
|
||||
"empty": "заметок пока нет",
|
||||
"expand": "развернуть",
|
||||
"exportToWord": "Экспорт в Word",
|
||||
"export_failed": "Экспорт в базу знаний не выполнен",
|
||||
"export_knowledge": "Экспортировать заметки в базу знаний",
|
||||
"export_success": "Успешно экспортировано в базу знаний",
|
||||
"export_to_word_failed": "Не удалось экспортировать в Word",
|
||||
"folder": "папка",
|
||||
"new_folder": "Новая папка",
|
||||
"new_note": "Создать заметку",
|
||||
"no_content_to_copy": "Нет контента для копирования",
|
||||
"no_content_to_export": "Нет содержимого для экспорта",
|
||||
"no_file_selected": "Пожалуйста, выберите файл для загрузки",
|
||||
"no_note_selected": "Пожалуйста, сначала выберите заметку",
|
||||
"no_valid_files": "Не загружен действительный файл",
|
||||
"open_folder": "Откройте внешнюю папку",
|
||||
"open_outside": "открыть снаружи",
|
||||
@ -2484,6 +2577,39 @@
|
||||
}
|
||||
},
|
||||
"custom_size": "Пользовательский размер",
|
||||
"dmxapi": {
|
||||
"generating_tip": "Генерация с использованием официальной модели, ожидаемое время ожидания — 2–5 минут для достижения наилучших результатов. Пожалуйста, проверьте логи бэкенда DMXAPI для получения информации о стоимости этой операции.",
|
||||
"style": ", Стиль:",
|
||||
"style_types": {
|
||||
"25d_animation": "2.5D анимация",
|
||||
"3d_cartoon": "3D мультфильм",
|
||||
"american_retro": "Американский ретро",
|
||||
"baroque": "Барокко",
|
||||
"cartoon_illustration": "Мультяшная иллюстрация",
|
||||
"chinese_gongbi": "Китайский гунби",
|
||||
"clay": "Глина",
|
||||
"cyberpunk": "Киберпанк",
|
||||
"felt": "Фетр",
|
||||
"flat": "Плоский",
|
||||
"fresh_anime": "Свежее аниме",
|
||||
"ghibli": "Гибли",
|
||||
"japanese_anime": "Японское аниме",
|
||||
"little_people_book": "Книжка о маленьких людях",
|
||||
"monet_garden": "Сад Моне",
|
||||
"oil_painting": "Живопись маслом",
|
||||
"pixar": "Пиксар",
|
||||
"pixel_art": "Пиксель-арт",
|
||||
"poetic_ancient": "Поэтическое древнее",
|
||||
"psychedelic": "Психоделический",
|
||||
"sketch": "Эскиз",
|
||||
"street_art": "Уличное искусство",
|
||||
"texture": "Текстура",
|
||||
"ukiyo_e": "Укиё-э",
|
||||
"watercolor": "Акварель",
|
||||
"wood_carving": "Резьба по дереву",
|
||||
"yarn_doll": "Кукла из пряжи"
|
||||
}
|
||||
},
|
||||
"edit": {
|
||||
"image_file": "Изображение для редактирования",
|
||||
"magic_prompt_option_tip": "Интеллектуально оптимизирует подсказки для улучшения эффекта редактирования",
|
||||
@ -2508,6 +2634,7 @@
|
||||
"style_type_tip": "Стиль генерации изображений, доступен только для версий V_2 и выше",
|
||||
"width": "Ширина"
|
||||
},
|
||||
"generate_failed": "Не удалось сгенерировать изображение",
|
||||
"generated_image": "Сгенерированное изображение",
|
||||
"go_to_settings": "Перейти в настройки",
|
||||
"guidance_scale": "Масштаб руководства",
|
||||
@ -2518,6 +2645,7 @@
|
||||
"image_file_required": "Пожалуйста, сначала загрузите изображение",
|
||||
"image_file_retry": "Пожалуйста, сначала загрузите изображение",
|
||||
"image_handle_required": "Пожалуйста, сначала загрузите изображение.",
|
||||
"image_mix_failed": "Не удалось объединить изображения",
|
||||
"image_placeholder": "Изображение недоступно",
|
||||
"image_retry": "Повторить",
|
||||
"image_size_options": {
|
||||
@ -2527,6 +2655,7 @@
|
||||
"inference_steps_tip": "Количество шагов вывода для выполнения. Больше шагов производят более высокое качество, но занимают больше времени",
|
||||
"input_image": "Входное изображение",
|
||||
"input_parameters": "Ввести параметры",
|
||||
"invalid_image_url": "Неверный формат URL изображения",
|
||||
"learn_more": "Узнать больше",
|
||||
"magic_prompt_option": "Улучшение промпта",
|
||||
"mode": {
|
||||
@ -2548,6 +2677,7 @@
|
||||
"no_image_generation_model": "Нет доступных моделей изображения, пожалуйста, добавьте модель и установите тип конечной точки на {{endpoint_type}}",
|
||||
"number_images": "Количество изображений",
|
||||
"number_images_tip": "Количество изображений для генерации (1-4)",
|
||||
"operation_failed": "Операция не удалась, повторите попытку позже",
|
||||
"paint_course": "Руководство / Учебник",
|
||||
"per_image": "за изображение",
|
||||
"per_images": "за изображения",
|
||||
@ -2623,6 +2753,26 @@
|
||||
"resemblance": "Сходство",
|
||||
"resemblance_tip": "Насколько близко результат увеличения к исходному изображению",
|
||||
"seed_tip": "Контролирует случайный характер увеличения изображений для воспроизводимых результатов"
|
||||
},
|
||||
"zhipu": {
|
||||
"custom_size_divisible": "Пользовательский размер должен быть кратен 16",
|
||||
"custom_size_hint": "Ширина и высота должны быть от 512 до 2048 пикселей, кратны 16, а общее число пикселей не должно превышать 2^21 пиксель.",
|
||||
"custom_size_pixels": "Общее количество пикселей пользовательского размера не может превышать 2 097 152",
|
||||
"custom_size_range": "Пользовательский размер должен быть от 512 до 2048 пикселей",
|
||||
"custom_size_required": "Пожалуйста, задайте пользовательскую ширину и высоту",
|
||||
"image_sizes": {
|
||||
"1024x1024_default": "1024x1024 (По умолчанию)",
|
||||
"1152x864": "1152x864",
|
||||
"1344x768": "1344x768",
|
||||
"1440x720": "1440x720",
|
||||
"720x1440": "720x1440",
|
||||
"768x1344": "768x1344",
|
||||
"864x1152": "864x1152"
|
||||
},
|
||||
"quality_options": {
|
||||
"hd": "HD",
|
||||
"standard_default": "Стандарт (по умолчанию)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
@ -2633,6 +2783,20 @@
|
||||
"category": "категория",
|
||||
"commands": "команда",
|
||||
"confirm_uninstall": "Вы уверены, что хотите удалить {{name}}?",
|
||||
"content_saved": "Содержимое плагина успешно сохранено",
|
||||
"detail": {
|
||||
"allowed_tools": "Разрешённые инструменты",
|
||||
"author": "Автор",
|
||||
"content": "Контент",
|
||||
"description": "Описание",
|
||||
"file": "Файл",
|
||||
"installed": "Установлено",
|
||||
"metadata": "Метаданные",
|
||||
"size": "Размер",
|
||||
"source": "Источник",
|
||||
"tags": "Теги",
|
||||
"tools": "Инструменты"
|
||||
},
|
||||
"install": "установка",
|
||||
"install_plugins_from_browser": "Просмотрите доступные плагины, чтобы начать работу",
|
||||
"installing": "Установка...",
|
||||
@ -2983,6 +3147,11 @@
|
||||
"summary": "Суммаризировать",
|
||||
"translate": "Перевести"
|
||||
},
|
||||
"prompt": {
|
||||
"explain": "Пожалуйста, объясните следующее содержание. Требования: ответ на {{language}}; не включайте никаких пояснений к этому запросу, просто дайте ответ напрямую:",
|
||||
"refine": "Пожалуйста, оптимизируйте или отполируйте содержимое пользовательского ввода, заключённое в XML-тег <INPUT>, сохраняя смысл и целостность исходного текста. Требования: ваш ответ должен быть на том же языке, что и пользовательский ввод; не включайте никаких объяснений этого запроса, просто выдайте результат напрямую; не выводите XML-теги, выводите оптимизированное содержимое напрямую:",
|
||||
"summary": "Пожалуйста, кратко изложите следующий контент. Требования: ответить на {{language}}; не включать никаких пояснений к этому запросу, просто дать ответ напрямую."
|
||||
},
|
||||
"translate": {
|
||||
"smart_translate_tips": "Смарт-перевод: содержимое будет переведено на целевой язык; содержимое уже на целевом языке будет переведено на альтернативный язык"
|
||||
},
|
||||
@ -4076,6 +4245,10 @@
|
||||
"jsonModeHint": "Редактируйте JSON-форматирование конфигурации сервера MCP. Перед сохранением убедитесь, что формат правильный.",
|
||||
"jsonSaveError": "Не удалось сохранить конфигурацию JSON",
|
||||
"jsonSaveSuccess": "JSON конфигурация сохранена",
|
||||
"lanyun": {
|
||||
"description": "Lanyun Technology Cloud Platform MCP Service",
|
||||
"name": "Ланьюнь Технологии"
|
||||
},
|
||||
"logoUrl": "URL логотипа",
|
||||
"logs": "Журналы",
|
||||
"longRunning": "Длительный режим работы",
|
||||
@ -4172,6 +4345,14 @@
|
||||
"getToken": "Получить API токен",
|
||||
"getTokenDescription": "Получите персональный API токен из вашей учетной записи",
|
||||
"noServersAvailable": "Нет доступных серверов MCP",
|
||||
"providerDescriptions": {
|
||||
"302ai": "302.Сервис MCP Платформы AI",
|
||||
"bailian": "Сервис MCP платформы Alibaba Cloud Bailian",
|
||||
"lanyun": "Lanyun Technology Cloud Platform MCP Service",
|
||||
"mcprouter": "Платформа маршрутизатора MCP Сервис MCP",
|
||||
"modelscope": "Служба MCP платформы ModelScope",
|
||||
"tokenflux": "Сервис MCP платформы TokenFlux"
|
||||
},
|
||||
"selectProvider": "Выберите провайдера:",
|
||||
"setToken": "Введите ваш токен",
|
||||
"success": "Синхронизация серверов MCP успешна",
|
||||
@ -4649,6 +4830,9 @@
|
||||
"title": "Удалить провайдер"
|
||||
},
|
||||
"dmxapi": {
|
||||
"platform_enterprise": "ssvip.DMXAPI.com (Корпоративный)",
|
||||
"platform_international": "www.DMXAPI.com (Международный)",
|
||||
"platform_official": "www.DMXAPI.cn (CNY)",
|
||||
"select_platform": "Выберите платформу"
|
||||
},
|
||||
"docs_check": "Проверить",
|
||||
@ -4985,6 +5169,13 @@
|
||||
"show": "Показать значок в трее",
|
||||
"title": "Трей"
|
||||
},
|
||||
"use_system_title_bar": {
|
||||
"confirm": {
|
||||
"content": "Изменение стиля заголовка требует перезапуска приложения. Перезапустить сейчас?",
|
||||
"title": "Требуется перезагрузка"
|
||||
},
|
||||
"title": "Использовать системную строку заголовка (Linux)"
|
||||
},
|
||||
"zoom": {
|
||||
"reset": "Сбросить",
|
||||
"title": "Масштаб страницы"
|
||||
|
||||
@ -86,6 +86,11 @@ const CodeToolsPage: FC = () => {
|
||||
if (m.provider === 'silicon') {
|
||||
return isSiliconAnthropicCompatibleModel(m.id)
|
||||
}
|
||||
// Check if model belongs to an anthropic type provider (including custom providers)
|
||||
const anthropicProvider = providers.find((p) => p.id === m.provider)
|
||||
if (anthropicProvider?.type === 'anthropic') {
|
||||
return true
|
||||
}
|
||||
return m.id.includes('claude') || CLAUDE_OFFICIAL_SUPPORTED_PROVIDERS.includes(m.provider)
|
||||
}
|
||||
|
||||
@ -102,6 +107,11 @@ const CodeToolsPage: FC = () => {
|
||||
m.supported_endpoint_types?.includes(type as EndpointType)
|
||||
)
|
||||
}
|
||||
// Check if model belongs to an openai-response type provider (including custom providers)
|
||||
const openaiProvider = providers.find((p) => p.id === m.provider)
|
||||
if (openaiProvider?.type === 'openai-response') {
|
||||
return true
|
||||
}
|
||||
return m.id.includes('openai') || OPENAI_CODEX_SUPPORTED_PROVIDERS.includes(m.provider)
|
||||
}
|
||||
|
||||
@ -120,7 +130,7 @@ const CodeToolsPage: FC = () => {
|
||||
|
||||
return true
|
||||
},
|
||||
[selectedCliTool]
|
||||
[selectedCliTool, providers]
|
||||
)
|
||||
|
||||
const availableProviders = useMemo(() => {
|
||||
@ -215,10 +225,12 @@ const CodeToolsPage: FC = () => {
|
||||
}
|
||||
|
||||
// 准备启动环境
|
||||
const prepareLaunchEnvironment = async (): Promise<Record<string, string> | null> => {
|
||||
const prepareLaunchEnvironment = async (): Promise<{
|
||||
env: Record<string, string>
|
||||
} | null> => {
|
||||
if (selectedCliTool === codeTools.githubCopilotCli) {
|
||||
const userEnv = parseEnvironmentVariables(environmentVariables)
|
||||
return userEnv
|
||||
return { env: userEnv }
|
||||
}
|
||||
|
||||
if (!selectedModel) return null
|
||||
@ -229,7 +241,7 @@ const CodeToolsPage: FC = () => {
|
||||
const apiKey = aiProvider.getApiKey()
|
||||
|
||||
// 生成工具特定的环境变量
|
||||
const toolEnv = generateToolEnvironment({
|
||||
const { env: toolEnv } = generateToolEnvironment({
|
||||
tool: selectedCliTool,
|
||||
model: selectedModel,
|
||||
modelProvider,
|
||||
@ -240,7 +252,7 @@ const CodeToolsPage: FC = () => {
|
||||
// 合并用户自定义的环境变量
|
||||
const userEnv = parseEnvironmentVariables(environmentVariables)
|
||||
|
||||
return { ...toolEnv, ...userEnv }
|
||||
return { env: { ...toolEnv, ...userEnv } }
|
||||
}
|
||||
|
||||
// 执行启动操作
|
||||
@ -291,13 +303,13 @@ const CodeToolsPage: FC = () => {
|
||||
setIsLaunching(true)
|
||||
|
||||
try {
|
||||
const env = await prepareLaunchEnvironment()
|
||||
if (!env) {
|
||||
const result = await prepareLaunchEnvironment()
|
||||
if (!result) {
|
||||
window.toast.error(t('code.model_required'))
|
||||
return
|
||||
}
|
||||
|
||||
await executeLaunch(env)
|
||||
await executeLaunch(result.env)
|
||||
} catch (error) {
|
||||
logger.error('start code tools failed:', error as Error)
|
||||
window.toast.error(t('code.launch.error'))
|
||||
|
||||
@ -106,7 +106,7 @@ describe('generateToolEnvironment', () => {
|
||||
const model = createMockModel('qwen-turbo', 'dashscope')
|
||||
const provider = createMockProvider('dashscope', 'https://dashscope.aliyuncs.com/compatible-mode')
|
||||
|
||||
const env = generateToolEnvironment({
|
||||
const { env } = generateToolEnvironment({
|
||||
tool: codeTools.qwenCode,
|
||||
model,
|
||||
modelProvider: provider,
|
||||
@ -122,7 +122,7 @@ describe('generateToolEnvironment', () => {
|
||||
const model = createMockModel('qwen-turbo', 'dashscope')
|
||||
const provider = createMockProvider('dashscope', 'https://dashscope.aliyuncs.com/compatible-mode/v1')
|
||||
|
||||
const env = generateToolEnvironment({
|
||||
const { env } = generateToolEnvironment({
|
||||
tool: codeTools.qwenCode,
|
||||
model,
|
||||
modelProvider: provider,
|
||||
@ -138,7 +138,7 @@ describe('generateToolEnvironment', () => {
|
||||
const model = createMockModel('qwen-turbo', 'dashscope')
|
||||
const provider = createMockProvider('dashscope', '')
|
||||
|
||||
const env = generateToolEnvironment({
|
||||
const { env } = generateToolEnvironment({
|
||||
tool: codeTools.qwenCode,
|
||||
model,
|
||||
modelProvider: provider,
|
||||
@ -154,7 +154,7 @@ describe('generateToolEnvironment', () => {
|
||||
const model = createMockModel('qwen-plus', 'dashscope')
|
||||
const provider = createMockProvider('dashscope', 'https://dashscope.aliyuncs.com/v2')
|
||||
|
||||
const env = generateToolEnvironment({
|
||||
const { env } = generateToolEnvironment({
|
||||
tool: codeTools.qwenCode,
|
||||
model,
|
||||
modelProvider: provider,
|
||||
@ -170,7 +170,7 @@ describe('generateToolEnvironment', () => {
|
||||
const model = createMockModel('gpt-4', 'openai')
|
||||
const provider = createMockProvider('openai', 'https://api.openai.com')
|
||||
|
||||
const env = generateToolEnvironment({
|
||||
const { env } = generateToolEnvironment({
|
||||
tool: codeTools.openaiCodex,
|
||||
model,
|
||||
modelProvider: provider,
|
||||
@ -186,7 +186,7 @@ describe('generateToolEnvironment', () => {
|
||||
const model = createMockModel('gpt-4', 'iflow')
|
||||
const provider = createMockProvider('iflow', 'https://api.iflow.cn')
|
||||
|
||||
const env = generateToolEnvironment({
|
||||
const { env } = generateToolEnvironment({
|
||||
tool: codeTools.iFlowCli,
|
||||
model,
|
||||
modelProvider: provider,
|
||||
@ -202,7 +202,7 @@ describe('generateToolEnvironment', () => {
|
||||
const model = createMockModel('qwen-turbo', 'dashscope')
|
||||
const provider = createMockProvider('dashscope', 'https://dashscope.aliyuncs.com/compatible-mode/')
|
||||
|
||||
const env = generateToolEnvironment({
|
||||
const { env } = generateToolEnvironment({
|
||||
tool: codeTools.qwenCode,
|
||||
model,
|
||||
modelProvider: provider,
|
||||
@ -218,7 +218,7 @@ describe('generateToolEnvironment', () => {
|
||||
const model = createMockModel('qwen-plus', 'dashscope')
|
||||
const provider = createMockProvider('dashscope', 'https://dashscope.aliyuncs.com/v2beta')
|
||||
|
||||
const env = generateToolEnvironment({
|
||||
const { env } = generateToolEnvironment({
|
||||
tool: codeTools.qwenCode,
|
||||
model,
|
||||
modelProvider: provider,
|
||||
|
||||
@ -22,7 +22,8 @@ export const CLI_TOOLS = [
|
||||
{ value: codeTools.geminiCli, label: 'Gemini CLI' },
|
||||
{ value: codeTools.openaiCodex, label: 'OpenAI Codex' },
|
||||
{ value: codeTools.iFlowCli, label: 'iFlow CLI' },
|
||||
{ value: codeTools.githubCopilotCli, label: 'GitHub Copilot CLI' }
|
||||
{ value: codeTools.githubCopilotCli, label: 'GitHub Copilot CLI' },
|
||||
{ value: codeTools.kimiCli, label: 'Kimi CLI' }
|
||||
]
|
||||
|
||||
export const GEMINI_SUPPORTED_PROVIDERS = ['aihubmix', 'dmxapi', 'new-api', 'cherryin']
|
||||
@ -57,9 +58,10 @@ export const CLI_TOOL_PROVIDER_MAP: Record<string, (providers: Provider[]) => Pr
|
||||
providers.filter((p) => p.type === 'gemini' || GEMINI_SUPPORTED_PROVIDERS.includes(p.id)),
|
||||
[codeTools.qwenCode]: (providers) => providers.filter((p) => p.type.includes('openai')),
|
||||
[codeTools.openaiCodex]: (providers) =>
|
||||
providers.filter((p) => p.id === 'openai' || OPENAI_CODEX_SUPPORTED_PROVIDERS.includes(p.id)),
|
||||
providers.filter((p) => p.type === 'openai-response' || OPENAI_CODEX_SUPPORTED_PROVIDERS.includes(p.id)),
|
||||
[codeTools.iFlowCli]: (providers) => providers.filter((p) => p.type.includes('openai')),
|
||||
[codeTools.githubCopilotCli]: () => []
|
||||
[codeTools.githubCopilotCli]: () => [],
|
||||
[codeTools.kimiCli]: (providers) => providers.filter((p) => p.type.includes('openai'))
|
||||
}
|
||||
|
||||
export const getCodeToolsApiBaseUrl = (model: Model, type: EndpointType) => {
|
||||
@ -144,7 +146,7 @@ export const generateToolEnvironment = ({
|
||||
modelProvider: Provider
|
||||
apiKey: string
|
||||
baseUrl: string
|
||||
}): Record<string, string> => {
|
||||
}): { env: Record<string, string> } => {
|
||||
const env: Record<string, string> = {}
|
||||
const formattedBaseUrl = formatApiHost(baseUrl)
|
||||
|
||||
@ -179,6 +181,7 @@ export const generateToolEnvironment = ({
|
||||
env.OPENAI_BASE_URL = formattedBaseUrl
|
||||
env.OPENAI_MODEL = model.id
|
||||
env.OPENAI_MODEL_PROVIDER = modelProvider.id
|
||||
env.OPENAI_MODEL_PROVIDER_NAME = modelProvider.name
|
||||
break
|
||||
|
||||
case codeTools.iFlowCli:
|
||||
@ -190,9 +193,15 @@ export const generateToolEnvironment = ({
|
||||
case codeTools.githubCopilotCli:
|
||||
env.GITHUB_TOKEN = apiKey || ''
|
||||
break
|
||||
|
||||
case codeTools.kimiCli:
|
||||
env.KIMI_API_KEY = apiKey
|
||||
env.KIMI_BASE_URL = formattedBaseUrl
|
||||
env.KIMI_MODEL_NAME = model.id
|
||||
break
|
||||
}
|
||||
|
||||
return env
|
||||
return { env }
|
||||
}
|
||||
|
||||
export { default } from './CodeToolsPage'
|
||||
|
||||
@ -163,17 +163,17 @@ const Chat: FC<Props> = (props) => {
|
||||
|
||||
// TODO: more info
|
||||
const AgentInvalid = useCallback(() => {
|
||||
return <Alert type="warning" message="Select an agent" style={{ margin: '5px 16px' }} />
|
||||
}, [])
|
||||
return <Alert type="warning" message={t('chat.alerts.select_agent')} style={{ margin: '5px 16px' }} />
|
||||
}, [t])
|
||||
|
||||
// TODO: more info
|
||||
const SessionInvalid = useCallback(() => {
|
||||
return (
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
<Alert type="warning" message="Create a session" style={{ margin: '5px 16px' }} />
|
||||
<Alert type="warning" message={t('chat.alerts.create_session')} style={{ margin: '5px 16px' }} />
|
||||
</div>
|
||||
)
|
||||
}, [])
|
||||
}, [t])
|
||||
|
||||
return (
|
||||
<Container id="chat" className={classNames([messageStyle, { 'multi-select-mode': isMultiSelectMode }])}>
|
||||
|
||||
@ -67,7 +67,7 @@ const HeaderNavbar: FC<Props> = ({ activeAssistant, setActiveAssistant, activeTo
|
||||
}
|
||||
|
||||
return (
|
||||
<NavbarHeader className="home-navbar" style={{ height: 'var(--navbar-height)' }}>
|
||||
<NavbarHeader className="home-navbar" style={{ height: 'var(--navbar-height)', paddingRight: 16 }}>
|
||||
<div className="flex h-full min-w-0 flex-1 shrink items-center overflow-auto">
|
||||
{isTopNavbar && showAssistants && (
|
||||
<Tooltip title={t('navbar.hide_sidebar')} mouseEnterDelay={0.8}>
|
||||
|
||||
@ -427,10 +427,24 @@ const AgentSessionInputbarInner: FC<InnerProps> = ({ assistant, agentId, session
|
||||
// Clear text after successful send (draft is cleared automatically via onChange)
|
||||
setText('')
|
||||
setTimeoutTimer('agentSession_sendMessage', () => setText(''), 500)
|
||||
// Restore focus to textarea after sending to maintain IME state (fcitx5 issue)
|
||||
focusTextarea()
|
||||
} catch (error) {
|
||||
logger.warn('Failed to send message:', error as Error)
|
||||
}
|
||||
}, [sendDisabled, agentId, dispatch, assistant, sessionId, sessionTopicId, setText, setTimeoutTimer, text, files])
|
||||
}, [
|
||||
sendDisabled,
|
||||
agentId,
|
||||
dispatch,
|
||||
assistant,
|
||||
sessionId,
|
||||
sessionTopicId,
|
||||
setText,
|
||||
setTimeoutTimer,
|
||||
text,
|
||||
files,
|
||||
focusTextarea
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
if (!document.querySelector('.topview-fullscreen-container')) {
|
||||
|
||||
@ -260,11 +260,25 @@ const InputbarInner: FC<InputbarInnerProps> = ({ assistant: initialAssistant, se
|
||||
setFiles([])
|
||||
setTimeoutTimer('sendMessage_1', () => setText(''), 500)
|
||||
setTimeoutTimer('sendMessage_2', () => resizeTextArea(), 0)
|
||||
// Restore focus to textarea after sending to maintain IME state (fcitx5 issue)
|
||||
focusTextarea()
|
||||
} catch (error) {
|
||||
logger.warn('Failed to send message:', error as Error)
|
||||
parent?.recordException(error as Error)
|
||||
}
|
||||
}, [assistant, topic, text, mentionedModels, files, dispatch, setText, setFiles, setTimeoutTimer, resizeTextArea])
|
||||
}, [
|
||||
assistant,
|
||||
topic,
|
||||
text,
|
||||
mentionedModels,
|
||||
files,
|
||||
dispatch,
|
||||
setText,
|
||||
setFiles,
|
||||
setTimeoutTimer,
|
||||
resizeTextArea,
|
||||
focusTextarea
|
||||
])
|
||||
|
||||
const tokenCountProps = useMemo(() => {
|
||||
if (!config.showTokenCount || estimateTokenCount === undefined || !showInputEstimatedTokens) {
|
||||
|
||||
@ -1,80 +1,19 @@
|
||||
import CodeViewer from '@renderer/components/CodeViewer'
|
||||
import { useCodeStyle } from '@renderer/context/CodeStyleProvider'
|
||||
import { ErrorDetailModal } from '@renderer/components/ErrorDetailModal'
|
||||
import { useTimer } from '@renderer/hooks/useTimer'
|
||||
import { getHttpMessageLabel, getProviderLabel } from '@renderer/i18n/label'
|
||||
import { getProviderById } from '@renderer/services/ProviderService'
|
||||
import { useAppDispatch } from '@renderer/store'
|
||||
import { removeBlocksThunk } from '@renderer/store/thunk/messageThunk'
|
||||
import type { SerializedAiSdkError, SerializedAiSdkErrorUnion, SerializedError } from '@renderer/types/error'
|
||||
import {
|
||||
isSerializedAiSdkAPICallError,
|
||||
isSerializedAiSdkDownloadError,
|
||||
isSerializedAiSdkError,
|
||||
isSerializedAiSdkErrorUnion,
|
||||
isSerializedAiSdkInvalidArgumentError,
|
||||
isSerializedAiSdkInvalidDataContentError,
|
||||
isSerializedAiSdkInvalidMessageRoleError,
|
||||
isSerializedAiSdkInvalidPromptError,
|
||||
isSerializedAiSdkInvalidToolInputError,
|
||||
isSerializedAiSdkJSONParseError,
|
||||
isSerializedAiSdkMessageConversionError,
|
||||
isSerializedAiSdkNoObjectGeneratedError,
|
||||
isSerializedAiSdkNoSpeechGeneratedError,
|
||||
isSerializedAiSdkNoSuchModelError,
|
||||
isSerializedAiSdkNoSuchProviderError,
|
||||
isSerializedAiSdkNoSuchToolError,
|
||||
isSerializedAiSdkRetryError,
|
||||
isSerializedAiSdkToolCallRepairError,
|
||||
isSerializedAiSdkTooManyEmbeddingValuesForCallError,
|
||||
isSerializedAiSdkTypeValidationError,
|
||||
isSerializedAiSdkUnsupportedFunctionalityError,
|
||||
isSerializedError
|
||||
} from '@renderer/types/error'
|
||||
import type { ErrorMessageBlock, Message } from '@renderer/types/newMessage'
|
||||
import { formatAiSdkError, formatError, safeToString } from '@renderer/utils/error'
|
||||
import { formatFileSize } from '@renderer/utils/file'
|
||||
import { KB } from '@shared/config/constant'
|
||||
import { Button } from 'antd'
|
||||
import { Alert as AntdAlert, Modal } from 'antd'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Alert as AntdAlert } from 'antd'
|
||||
import React, { useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
|
||||
const HTTP_ERROR_CODES = [400, 401, 403, 404, 429, 500, 502, 503, 504]
|
||||
|
||||
const MAX_DISPLAY_SIZE = 100 * KB
|
||||
|
||||
/**
|
||||
* Truncate large data to prevent OOM when displaying error details.
|
||||
* Uses simple string operations to avoid regex performance issues with large strings.
|
||||
*/
|
||||
const truncateLargeData = (
|
||||
data: string,
|
||||
t: (key: string) => string
|
||||
): { content: string; truncated: boolean; isLikelyBase64: boolean } => {
|
||||
if (!data || data.length <= MAX_DISPLAY_SIZE) {
|
||||
return { content: data, truncated: false, isLikelyBase64: false }
|
||||
}
|
||||
|
||||
const isLikelyBase64 = data.includes('data:image/') && data.includes(';base64,')
|
||||
const formattedSize = formatFileSize(data.length)
|
||||
|
||||
if (isLikelyBase64) {
|
||||
return {
|
||||
content: `[${t('error.base64DataTruncated')} ~${formattedSize}]`,
|
||||
truncated: true,
|
||||
isLikelyBase64: true
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
content: data.slice(0, MAX_DISPLAY_SIZE) + `\n\n... [${t('error.truncated')} ${formattedSize}]`,
|
||||
truncated: true,
|
||||
isLikelyBase64: false
|
||||
}
|
||||
}
|
||||
|
||||
interface Props {
|
||||
block: ErrorMessageBlock
|
||||
message: Message
|
||||
@ -190,115 +129,6 @@ const MessageErrorInfo: React.FC<{ block: ErrorMessageBlock; message: Message }>
|
||||
)
|
||||
}
|
||||
|
||||
interface ErrorDetailModalProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
error?: SerializedError
|
||||
}
|
||||
|
||||
const ErrorDetailModal: React.FC<ErrorDetailModalProps> = ({ open, onClose, error }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const copyErrorDetails = () => {
|
||||
if (!error) return
|
||||
let errorText: string
|
||||
if (isSerializedAiSdkError(error)) {
|
||||
errorText = formatAiSdkError(error)
|
||||
} else if (isSerializedError(error)) {
|
||||
errorText = formatError(error)
|
||||
} else {
|
||||
// fallback
|
||||
errorText = safeToString(error)
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(errorText)
|
||||
window.toast.addToast({ title: t('message.copied') })
|
||||
}
|
||||
|
||||
const renderErrorDetails = (error?: SerializedError) => {
|
||||
if (!error) return <div>{t('error.unknown')}</div>
|
||||
if (isSerializedAiSdkErrorUnion(error)) {
|
||||
return <AiSdkError error={error} />
|
||||
}
|
||||
return (
|
||||
<ErrorDetailList>
|
||||
<BuiltinError error={error} />
|
||||
</ErrorDetailList>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
centered
|
||||
title={t('error.detail')}
|
||||
open={open}
|
||||
onCancel={onClose}
|
||||
footer={[
|
||||
<Button key="copy" variant="text" color="default" onClick={copyErrorDetails}>
|
||||
{t('common.copy')}
|
||||
</Button>,
|
||||
<Button key="close" variant="text" color={'default'} onClick={onClose}>
|
||||
{t('common.close')}
|
||||
</Button>
|
||||
]}
|
||||
width="80%"
|
||||
style={{ maxWidth: '1200px', minWidth: '600px' }}>
|
||||
<ErrorDetailContainer>{renderErrorDetails(error)}</ErrorDetailContainer>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
const ErrorDetailContainer = styled.div`
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
`
|
||||
|
||||
const ErrorDetailList = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
`
|
||||
|
||||
const ErrorDetailItem = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
`
|
||||
|
||||
const ErrorDetailLabel = styled.div`
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
font-size: 14px;
|
||||
`
|
||||
|
||||
const ErrorDetailValue = styled.div`
|
||||
font-family: var(--code-font-family);
|
||||
font-size: 12px;
|
||||
padding: 8px;
|
||||
background: var(--color-code-background);
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--color-border);
|
||||
word-break: break-word;
|
||||
color: var(--color-text);
|
||||
`
|
||||
|
||||
const StackTrace = styled.div`
|
||||
background: var(--color-background-soft);
|
||||
border: 1px solid var(--color-error);
|
||||
border-radius: 6px;
|
||||
padding: 12px;
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
font-family: var(--code-font-family);
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
color: var(--color-error);
|
||||
}
|
||||
`
|
||||
|
||||
const Alert = styled(AntdAlert)`
|
||||
margin: 0.5rem 0 !important;
|
||||
padding: 10px;
|
||||
@ -309,404 +139,4 @@ const Alert = styled(AntdAlert)`
|
||||
}
|
||||
`
|
||||
|
||||
const TruncatedBadge = styled.span`
|
||||
margin-left: 8px;
|
||||
padding: 2px 6px;
|
||||
font-size: 10px;
|
||||
font-weight: normal;
|
||||
color: var(--color-warning);
|
||||
background: var(--color-warning-bg, rgba(250, 173, 20, 0.1));
|
||||
border-radius: 4px;
|
||||
`
|
||||
|
||||
// 作为 base,渲染公共字段,应当在 ErrorDetailList 中渲染
|
||||
const BuiltinError = ({ error }: { error: SerializedError }) => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<>
|
||||
{error.name && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.name')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.name}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.message && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.message')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.message}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.stack && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.stack')}:</ErrorDetailLabel>
|
||||
<StackTrace>
|
||||
<pre>{error.stack}</pre>
|
||||
</StackTrace>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// Base component to render common fields, should be rendered inside ErrorDetailList
|
||||
const AiSdkErrorBase = ({ error }: { error: SerializedAiSdkError }) => {
|
||||
const { t } = useTranslation()
|
||||
const { highlightCode } = useCodeStyle()
|
||||
const [highlightedString, setHighlightedString] = useState('')
|
||||
const [isTruncated, setIsTruncated] = useState(false)
|
||||
const cause = error.cause
|
||||
|
||||
useEffect(() => {
|
||||
const highlight = async () => {
|
||||
try {
|
||||
// Truncate large data before processing to prevent OOM
|
||||
const { content: truncatedCause, truncated, isLikelyBase64 } = truncateLargeData(cause || '', t)
|
||||
setIsTruncated(truncated)
|
||||
|
||||
// Skip JSON parsing and syntax highlighting for base64 data
|
||||
if (isLikelyBase64) {
|
||||
setHighlightedString(truncatedCause)
|
||||
return
|
||||
}
|
||||
|
||||
// Try to parse and format JSON
|
||||
try {
|
||||
const parsed = JSON.parse(truncatedCause || '{}')
|
||||
const formatted = JSON.stringify(parsed, null, 2)
|
||||
const result = await highlightCode(formatted, 'json')
|
||||
setHighlightedString(result)
|
||||
} catch {
|
||||
// If not valid JSON, use as-is
|
||||
setHighlightedString(truncatedCause || '')
|
||||
}
|
||||
} catch {
|
||||
setHighlightedString(cause || '')
|
||||
}
|
||||
}
|
||||
const timer = setTimeout(highlight, 0)
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [highlightCode, cause, t])
|
||||
|
||||
return (
|
||||
<>
|
||||
<BuiltinError error={error} />
|
||||
{cause && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>
|
||||
{t('error.cause')}:{isTruncated && <TruncatedBadge>{t('error.truncatedBadge')}</TruncatedBadge>}
|
||||
</ErrorDetailLabel>
|
||||
<ErrorDetailValue>
|
||||
<div
|
||||
className="markdown [&_pre]:!bg-transparent [&_pre_span]:whitespace-pre-wrap"
|
||||
dangerouslySetInnerHTML={{ __html: highlightedString }}
|
||||
/>
|
||||
</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
// Wrapper component to safely display potentially large data in CodeViewer
|
||||
const TruncatedCodeViewer: React.FC<{
|
||||
value: string
|
||||
label: string
|
||||
language?: string
|
||||
}> = ({ value, label, language = 'json' }) => {
|
||||
const { t } = useTranslation()
|
||||
const { content, truncated, isLikelyBase64 } = truncateLargeData(value, t)
|
||||
|
||||
return (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>
|
||||
{label}:{truncated && <TruncatedBadge>{t('error.truncatedBadge')}</TruncatedBadge>}
|
||||
</ErrorDetailLabel>
|
||||
{isLikelyBase64 ? (
|
||||
<ErrorDetailValue>{content}</ErrorDetailValue>
|
||||
) : (
|
||||
<CodeViewer value={content} className="source-view" language={language} expanded />
|
||||
)}
|
||||
</ErrorDetailItem>
|
||||
)
|
||||
}
|
||||
|
||||
const AiSdkError = ({ error }: { error: SerializedAiSdkErrorUnion }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<ErrorDetailList>
|
||||
{(isSerializedAiSdkAPICallError(error) || isSerializedAiSdkDownloadError(error)) && (
|
||||
<>
|
||||
{error.url && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.requestUrl')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.url}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkAPICallError(error) && (
|
||||
<>{error.responseBody && <TruncatedCodeViewer value={error.responseBody} label={t('error.responseBody')} />}</>
|
||||
)}
|
||||
|
||||
{(isSerializedAiSdkAPICallError(error) || isSerializedAiSdkDownloadError(error)) && (
|
||||
<>
|
||||
{error.statusCode && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.statusCode')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.statusCode}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkAPICallError(error) && (
|
||||
<>
|
||||
{error.responseHeaders && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.responseHeaders')}:</ErrorDetailLabel>
|
||||
<CodeViewer
|
||||
value={JSON.stringify(error.responseHeaders, null, 2)}
|
||||
className="source-view"
|
||||
language="json"
|
||||
expanded
|
||||
/>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{error.requestBodyValues && (
|
||||
<TruncatedCodeViewer value={safeToString(error.requestBodyValues)} label={t('error.requestBodyValues')} />
|
||||
)}
|
||||
|
||||
{error.data && <TruncatedCodeViewer value={safeToString(error.data)} label={t('error.data')} />}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkDownloadError(error) && (
|
||||
<>
|
||||
{error.statusText && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.statusText')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.statusText}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkInvalidArgumentError(error) && (
|
||||
<>
|
||||
{error.parameter && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.parameter')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.parameter}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{(isSerializedAiSdkInvalidArgumentError(error) || isSerializedAiSdkTypeValidationError(error)) && (
|
||||
<>
|
||||
{error.value && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.value')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.value)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkInvalidDataContentError(error) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.content')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.content)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkInvalidMessageRoleError(error) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.role')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.role}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkInvalidPromptError(error) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.prompt')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.prompt)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkInvalidToolInputError(error) && (
|
||||
<>
|
||||
{error.toolName && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.toolName')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.toolName}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.toolInput && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.toolInput')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.toolInput}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{(isSerializedAiSdkJSONParseError(error) || isSerializedAiSdkNoObjectGeneratedError(error)) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.text')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.text}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkMessageConversionError(error) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.originalMessage')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.originalMessage)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkNoSpeechGeneratedError(error) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.responses')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.responses.join(', ')}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkNoObjectGeneratedError(error) && (
|
||||
<>
|
||||
{error.response && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.response')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.response)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.usage && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.usage')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.usage)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.finishReason && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.finishReason')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.finishReason}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{(isSerializedAiSdkNoSuchModelError(error) ||
|
||||
isSerializedAiSdkNoSuchProviderError(error) ||
|
||||
isSerializedAiSdkTooManyEmbeddingValuesForCallError(error)) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.modelId')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.modelId}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{(isSerializedAiSdkNoSuchModelError(error) || isSerializedAiSdkNoSuchProviderError(error)) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.modelType')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.modelType}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkNoSuchProviderError(error) && (
|
||||
<>
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.providerId')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.providerId}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.availableProviders')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.availableProviders.join(', ')}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkNoSuchToolError(error) && (
|
||||
<>
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.toolName')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.toolName}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
{error.availableTools && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.availableTools')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.availableTools?.join(', ') || t('common.none')}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkRetryError(error) && (
|
||||
<>
|
||||
{error.reason && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.reason')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.reason}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.lastError && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.lastError')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.lastError)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.errors && error.errors.length > 0 && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.errors')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.errors.map((e) => safeToString(e)).join('\n\n')}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkTooManyEmbeddingValuesForCallError(error) && (
|
||||
<>
|
||||
{error.provider && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.provider')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.provider}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.maxEmbeddingsPerCall && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.maxEmbeddingsPerCall')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.maxEmbeddingsPerCall}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
{error.values && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.values')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.values)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkToolCallRepairError(error) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.originalError')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{safeToString(error.originalError)}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
{isSerializedAiSdkUnsupportedFunctionalityError(error) && (
|
||||
<ErrorDetailItem>
|
||||
<ErrorDetailLabel>{t('error.functionality')}:</ErrorDetailLabel>
|
||||
<ErrorDetailValue>{error.functionality}</ErrorDetailValue>
|
||||
</ErrorDetailItem>
|
||||
)}
|
||||
|
||||
<AiSdkErrorBase error={error} />
|
||||
</ErrorDetailList>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(ErrorBlock)
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { Navbar, NavbarCenter, NavbarLeft, NavbarRight } from '@renderer/components/app/Navbar'
|
||||
import { HStack } from '@renderer/components/Layout'
|
||||
import SearchPopup from '@renderer/components/Popups/SearchPopup'
|
||||
import { isLinux, isWin } from '@renderer/config/constant'
|
||||
import { modelGenerating } from '@renderer/hooks/useRuntime'
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { useShortcut } from '@renderer/hooks/useShortcuts'
|
||||
@ -123,7 +122,7 @@ const HeaderNavbar: FC<Props> = ({
|
||||
justifyContent: 'flex-end',
|
||||
flex: activeTopicOrSession === 'topic' ? 1 : 'none',
|
||||
position: 'relative',
|
||||
paddingRight: isWin || isLinux ? '144px' : '15px',
|
||||
paddingRight: '15px',
|
||||
minWidth: activeTopicOrSession === 'topic' ? '' : 'auto'
|
||||
}}
|
||||
className="home-navbar-right">
|
||||
|
||||
@ -911,7 +911,8 @@ const HeaderRow = styled.div`
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding-right: 10px;
|
||||
margin-bottom: 5px;
|
||||
margin-bottom: 6px;
|
||||
margin-top: 2px;
|
||||
`
|
||||
|
||||
const HeaderIconButton = styled.div`
|
||||
|
||||
@ -62,7 +62,7 @@ const UnifiedAddButton: FC<UnifiedAddButtonProps> = ({ onCreateAssistant, setAct
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="-mt-[4px] mb-[6px]">
|
||||
<div className="-mt-[2px] mb-[6px]">
|
||||
<AddButton onClick={handleAddButtonClick}>{t('chat.add.assistant.title')}</AddButton>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -338,7 +338,7 @@ const WebviewSearch: FC<WebviewSearchProps> = ({ webviewRef, isWebviewReady, app
|
||||
type="text"
|
||||
onClick={goToPrevious}
|
||||
disabled={disableNavigation}
|
||||
aria-label="Previous match"
|
||||
aria-label={t('common.previous_match')}
|
||||
icon={<ChevronUp size={16} className="w-6" />}
|
||||
className="text-default-500 hover:text-default-900"
|
||||
/>
|
||||
@ -347,7 +347,7 @@ const WebviewSearch: FC<WebviewSearchProps> = ({ webviewRef, isWebviewReady, app
|
||||
type="text"
|
||||
onClick={goToNext}
|
||||
disabled={disableNavigation}
|
||||
aria-label="Next match"
|
||||
aria-label={t('common.next_match')}
|
||||
icon={<ChevronDown size={16} className="w-6" />}
|
||||
className="text-default-500 hover:text-default-900"
|
||||
/>
|
||||
|
||||
@ -10,7 +10,9 @@ const translations: Record<string, string> = {
|
||||
'common.close': 'Close',
|
||||
'common.error': 'Error',
|
||||
'common.no_results': 'No results',
|
||||
'common.search': 'Search'
|
||||
'common.search': 'Search',
|
||||
'common.next_match': 'Next match',
|
||||
'common.previous_match': 'Previous match'
|
||||
}
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
|
||||
@ -53,6 +53,25 @@ const HeaderNavbar = ({ notesTree, getCurrentNoteContent, onToggleStar, onExpand
|
||||
}
|
||||
}, [getCurrentNoteContent])
|
||||
|
||||
const handleExportToWord = useCallback(async () => {
|
||||
try {
|
||||
const content = getCurrentNoteContent?.()
|
||||
if (!content) {
|
||||
window.toast.warning(t('notes.no_content_to_export'))
|
||||
return
|
||||
}
|
||||
if (!activeNode) {
|
||||
window.toast.warning(t('notes.no_note_selected'))
|
||||
return
|
||||
}
|
||||
const fileName = activeNode.name.replace('.md', '')
|
||||
await window.api.export.toWord(content, fileName)
|
||||
} catch (error) {
|
||||
logger.error('Failed to export to Word:', error as Error)
|
||||
window.toast.error(t('notes.export_to_word_failed'))
|
||||
}
|
||||
}, [getCurrentNoteContent, activeNode])
|
||||
|
||||
const handleShowSettings = useCallback(() => {
|
||||
GeneralPopup.show({
|
||||
title: t('notes.settings.title'),
|
||||
@ -142,6 +161,8 @@ const HeaderNavbar = ({ notesTree, getCurrentNoteContent, onToggleStar, onExpand
|
||||
onClick: () => {
|
||||
if (item.copyAction) {
|
||||
handleCopyContent()
|
||||
} else if (item.exportToWordAction) {
|
||||
handleExportToWord()
|
||||
} else if (item.showSettingsPopup) {
|
||||
handleShowSettings()
|
||||
} else if (item.action) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { NotesSettings } from '@renderer/store/note'
|
||||
import { Copy, MonitorSpeaker, Settings, Type } from 'lucide-react'
|
||||
import { Copy, FileText, MonitorSpeaker, Settings, Type } from 'lucide-react'
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
export interface MenuItem {
|
||||
@ -12,6 +12,7 @@ export interface MenuItem {
|
||||
isActive?: (settings: NotesSettings) => boolean
|
||||
component?: (settings: NotesSettings, updateSettings: (newSettings: Partial<NotesSettings>) => void) => ReactNode
|
||||
copyAction?: boolean
|
||||
exportToWordAction?: boolean
|
||||
showSettingsPopup?: boolean
|
||||
}
|
||||
|
||||
@ -22,6 +23,12 @@ export const menuItems: MenuItem[] = [
|
||||
icon: Copy,
|
||||
copyAction: true
|
||||
},
|
||||
{
|
||||
key: 'export-to-word',
|
||||
labelKey: 'notes.exportToWord',
|
||||
icon: FileText,
|
||||
exportToWordAction: true
|
||||
},
|
||||
{
|
||||
key: 'divider0',
|
||||
type: 'divider',
|
||||
|
||||
@ -239,7 +239,7 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json()
|
||||
logger.error('Gemini API Error:', errorData)
|
||||
throw new Error(errorData.error?.message || '生成图像失败')
|
||||
throw new Error(errorData.error?.message || t('paintings.generate_failed'))
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
@ -340,7 +340,7 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json()
|
||||
logger.error('V3 API错误:', errorData)
|
||||
throw new Error(errorData.error?.message || '生成图像失败')
|
||||
throw new Error(errorData.error?.message || t('paintings.generate_failed'))
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
@ -468,7 +468,7 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json()
|
||||
logger.error('V3 Remix API错误:', errorData)
|
||||
throw new Error(errorData.error?.message || '图像混合失败')
|
||||
throw new Error(errorData.error?.message || t('paintings.image_mix_failed'))
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
@ -538,7 +538,7 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json()
|
||||
logger.error('通用API错误:', errorData)
|
||||
throw new Error(errorData.error?.message || '生成图像失败')
|
||||
throw new Error(errorData.error?.message || t('paintings.generate_failed'))
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
@ -704,11 +704,11 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
typeof item.options === 'function'
|
||||
? item.options(item, painting).map((option) => ({
|
||||
...option,
|
||||
label: option.label.startsWith('paintings.') ? t(option.label) : option.label
|
||||
label: option.labelKey ? t(option.labelKey) : option.label
|
||||
}))
|
||||
: item.options?.map((option) => ({
|
||||
...option,
|
||||
label: option.label.startsWith('paintings.') ? t(option.label) : option.label
|
||||
label: option.labelKey ? t(option.labelKey) : option.label
|
||||
}))
|
||||
|
||||
return (
|
||||
@ -728,11 +728,11 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
typeof item.options === 'function'
|
||||
? item.options(item, painting).map((option) => ({
|
||||
...option,
|
||||
label: option.label.startsWith('paintings.') ? t(option.label) : option.label
|
||||
label: option.labelKey ? t(option.labelKey) : option.label
|
||||
}))
|
||||
: item.options?.map((option) => ({
|
||||
...option,
|
||||
label: option.label.startsWith('paintings.') ? t(option.label) : option.label
|
||||
label: option.labelKey ? t(option.labelKey) : option.label
|
||||
}))
|
||||
|
||||
return (
|
||||
|
||||
@ -80,7 +80,7 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
|
||||
const modeOptions = MODEOPTIONS.map((ele) => {
|
||||
return {
|
||||
label: t(ele.label),
|
||||
label: t(ele.labelKey),
|
||||
value: ele.value
|
||||
}
|
||||
})
|
||||
@ -387,7 +387,7 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
}
|
||||
|
||||
if (painting.style_type) {
|
||||
params.prompt = prompt + ',风格:' + painting.style_type
|
||||
params.prompt = prompt + t('paintings.dmxapi.style') + painting.style_type
|
||||
}
|
||||
|
||||
if (Array.isArray(fileMap.imageFiles) && fileMap.imageFiles.length > 0) {
|
||||
@ -418,7 +418,7 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
}
|
||||
|
||||
if (painting.style_type) {
|
||||
params.prompt = prompt + ',风格:' + painting.style_type
|
||||
params.prompt = prompt + t('paintings.dmxapi.style') + painting.style_type
|
||||
}
|
||||
|
||||
const formData = new FormData()
|
||||
@ -467,7 +467,7 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
throw new Error('paintings.req_error_no_balance')
|
||||
}
|
||||
|
||||
throw new Error('操作失败,请稍后重试')
|
||||
throw new Error('paintings.operation_failed')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
@ -712,13 +712,7 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
) {
|
||||
return (
|
||||
<LoadTextWrap>
|
||||
<div>
|
||||
正在用使用官方的模型生产,
|
||||
<br />
|
||||
预计等待2~5分钟效果最好,
|
||||
<br />
|
||||
本次消耗金额请到DMXAPI后台日志查看
|
||||
</div>
|
||||
<div>{t('paintings.dmxapi.generating_tip')}</div>
|
||||
</LoadTextWrap>
|
||||
)
|
||||
}
|
||||
@ -822,7 +816,7 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
{painting.generationMode &&
|
||||
[generationModeType.EDIT, generationModeType.MERGE].includes(painting.generationMode) && (
|
||||
<>
|
||||
<SettingTitle className="mt-4 mb-1">参考图</SettingTitle>
|
||||
<SettingTitle className="mt-4 mb-1">{t('paintings.remix.image_file')}</SettingTitle>
|
||||
<ImageUploader
|
||||
fileMap={fileMap}
|
||||
maxImages={painting.generationMode === generationModeType.EDIT ? 1 : 3}
|
||||
@ -942,10 +936,10 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
<RadioTextBox>
|
||||
{STYLE_TYPE_OPTIONS.map((ele) => (
|
||||
<RadioTextItem
|
||||
key={ele.label}
|
||||
className={painting.style_type === ele.label ? 'selected' : ''}
|
||||
onClick={() => onSelectStyleType(ele.label)}>
|
||||
{ele.label}
|
||||
key={ele.value}
|
||||
className={painting.style_type === ele.value ? 'selected' : ''}
|
||||
onClick={() => onSelectStyleType(ele.value)}>
|
||||
{t(ele.labelKey)}
|
||||
</RadioTextItem>
|
||||
))}
|
||||
</RadioTextBox>
|
||||
|
||||
@ -319,7 +319,7 @@ const NewApiPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json()
|
||||
throw new Error(errorData.error?.message || '生成图像失败')
|
||||
throw new Error(errorData.error?.message || t('paintings.generate_failed'))
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
@ -119,7 +119,7 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
if (painting.imageSize === 'custom') {
|
||||
if (!customWidth || !customHeight) {
|
||||
window.modal.error({
|
||||
content: '请设置自定义尺寸的宽度和高度',
|
||||
content: t('paintings.zhipu.custom_size_required'),
|
||||
centered: true
|
||||
})
|
||||
return
|
||||
@ -127,7 +127,7 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
// 验证自定义尺寸是否符合智谱AI的要求
|
||||
if (customWidth < 512 || customWidth > 2048 || customHeight < 512 || customHeight > 2048) {
|
||||
window.modal.error({
|
||||
content: '自定义尺寸必须在512px-2048px之间',
|
||||
content: t('paintings.zhipu.custom_size_range'),
|
||||
centered: true
|
||||
})
|
||||
return
|
||||
@ -135,7 +135,7 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
|
||||
if (customWidth % 16 !== 0 || customHeight % 16 !== 0) {
|
||||
window.modal.error({
|
||||
content: '自定义尺寸必须能被16整除',
|
||||
content: t('paintings.zhipu.custom_size_divisible'),
|
||||
centered: true
|
||||
})
|
||||
return
|
||||
@ -145,7 +145,7 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
if (totalPixels > 2097152) {
|
||||
// 2^21 = 2097152
|
||||
window.modal.error({
|
||||
content: '自定义尺寸的总像素数不能超过2,097,152',
|
||||
content: t('paintings.zhipu.custom_size_pixels'),
|
||||
centered: true
|
||||
})
|
||||
return
|
||||
@ -375,7 +375,7 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
<Radio.Group value={painting.quality} onChange={(e) => onSelectQuality(e.target.value)}>
|
||||
{QUALITY_OPTIONS.map((option) => (
|
||||
<Radio key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
{t(option.label)}
|
||||
</Radio>
|
||||
))}
|
||||
</Radio.Group>
|
||||
@ -389,7 +389,7 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
style={{ width: '100%' }}>
|
||||
{IMAGE_SIZES.map((size) => (
|
||||
<Select.Option key={size.value} value={size.value}>
|
||||
{size.label}
|
||||
{t(size.label)}
|
||||
</Select.Option>
|
||||
))}
|
||||
<Select.Option value="custom" key="custom">
|
||||
@ -423,7 +423,7 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => {
|
||||
<span style={{ color: 'var(--color-text-2)', fontSize: '12px' }}>px</span>
|
||||
</HStack>
|
||||
<div style={{ marginTop: 5, fontSize: '12px', color: 'var(--color-text-3)' }}>
|
||||
长宽均需满足512px-2048px之间, 需被16整除, 并保证最大像素数不超过2^21px
|
||||
{t('paintings.zhipu.custom_size_hint')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -4,6 +4,7 @@ import { convertToBase64 } from '@renderer/utils'
|
||||
import { Button, Input, InputNumber, Select, Switch, Upload } from 'antd'
|
||||
import TextArea from 'antd/es/input/TextArea'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
interface DynamicFormRenderProps {
|
||||
schemaProperty: any
|
||||
@ -20,6 +21,7 @@ export const DynamicFormRender: React.FC<DynamicFormRenderProps> = ({
|
||||
value,
|
||||
onChange
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { type, enum: enumValues, description, default: defaultValue, format } = schemaProperty
|
||||
|
||||
const handleImageUpload = useCallback(
|
||||
@ -64,7 +66,7 @@ export const DynamicFormRender: React.FC<DynamicFormRenderProps> = ({
|
||||
}}
|
||||
value={value || defaultValue || ''}
|
||||
onChange={(e) => onChange(propertyName, e.target.value)}
|
||||
placeholder="Enter image URL or upload file"
|
||||
placeholder={t('common.image_url_or_upload')}
|
||||
prefix={<LinkOutlined style={{ color: '#999' }} />}
|
||||
/>
|
||||
<Upload
|
||||
@ -76,7 +78,7 @@ export const DynamicFormRender: React.FC<DynamicFormRenderProps> = ({
|
||||
}}>
|
||||
<Button
|
||||
icon={<UploadOutlined />}
|
||||
title="Upload image file"
|
||||
title={t('common.upload_image')}
|
||||
style={{
|
||||
borderTopLeftRadius: 0,
|
||||
borderBottomLeftRadius: 0,
|
||||
@ -119,14 +121,14 @@ export const DynamicFormRender: React.FC<DynamicFormRenderProps> = ({
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis'
|
||||
}}>
|
||||
{value.startsWith('data:') ? 'Uploaded image' : 'Image URL'}
|
||||
{value.startsWith('data:') ? t('common.uploaded_image') : t('common.image_url')}
|
||||
</div>
|
||||
<Button
|
||||
size="small"
|
||||
danger
|
||||
icon={<CloseOutlined />}
|
||||
onClick={() => onChange(propertyName, '')}
|
||||
title="Remove image"
|
||||
title={t('common.remove_image')}
|
||||
style={{ flexShrink: 0, minWidth: 'auto', padding: '0 8px' }}
|
||||
/>
|
||||
</div>
|
||||
@ -182,7 +184,7 @@ export const DynamicFormRender: React.FC<DynamicFormRenderProps> = ({
|
||||
size="small"
|
||||
icon={<RedoOutlined />}
|
||||
onClick={() => onChange(propertyName, generateRandomSeed())}
|
||||
title="Generate random seed"
|
||||
title={t('common.generate_random_seed')}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -6,6 +6,7 @@ import { Popconfirm, Upload } from 'antd'
|
||||
import { Button } from 'antd'
|
||||
import type { RcFile, UploadProps } from 'antd/es/upload'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
interface ImageUploaderProps {
|
||||
@ -28,6 +29,7 @@ const ImageUploader: React.FC<ImageUploaderProps> = ({
|
||||
onAddImage
|
||||
}) => {
|
||||
const { theme } = useTheme()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const handleBeforeUpload = (file: RcFile, index?: number) => {
|
||||
onAddImage(file, index)
|
||||
@ -46,7 +48,7 @@ const ImageUploader: React.FC<ImageUploaderProps> = ({
|
||||
<HeaderContainer>
|
||||
{fileMap.imageFiles && fileMap.imageFiles.length > 0 && (
|
||||
<Button size="small" onClick={onClearImages}>
|
||||
清除全部
|
||||
{t('common.clear_all')}
|
||||
</Button>
|
||||
)}
|
||||
</HeaderContainer>
|
||||
@ -68,13 +70,13 @@ const ImageUploader: React.FC<ImageUploaderProps> = ({
|
||||
handleBeforeUpload(file, index)
|
||||
}}>
|
||||
<ImagePreview>
|
||||
<img src={src} alt={`预览图${index + 1}`} />
|
||||
<img src={src} alt={`${t('common.image_preview')} ${index + 1}`} />
|
||||
</ImagePreview>
|
||||
</ImageUploadButton>
|
||||
<Popconfirm
|
||||
title="确定要删除这张图片吗?"
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
title={t('paintings.button.delete.image.confirm')}
|
||||
okText={t('common.confirm')}
|
||||
cancelText={t('common.cancel')}
|
||||
onConfirm={() => onDeleteImage(index)}>
|
||||
<DeleteButton>
|
||||
<DeleteOutlined />
|
||||
|
||||
@ -27,33 +27,33 @@ export type DMXApiModelGroups = {
|
||||
}
|
||||
|
||||
export const STYLE_TYPE_OPTIONS = [
|
||||
{ label: '吉卜力', value: '吉卜力' },
|
||||
{ label: '皮克斯', value: '皮克斯' },
|
||||
{ label: '绒线玩偶', value: '绒线玩偶' },
|
||||
{ label: '水彩画', value: '水彩画' },
|
||||
{ label: '卡通插画', value: '卡通插画' },
|
||||
{ label: '3D卡通', value: '3D卡通' },
|
||||
{ label: '日系动漫', value: '日系动漫' },
|
||||
{ label: '木雕', value: '木雕' },
|
||||
{ label: '唯美古风', value: '唯美古风' },
|
||||
{ label: '2.5D动画', value: '2.5D动画' },
|
||||
{ label: '清新日漫', value: '清新日漫' },
|
||||
{ label: '黏土', value: '黏土' },
|
||||
{ label: '小人书插画', value: '小人书插画' },
|
||||
{ label: '浮世绘', value: '浮世绘' },
|
||||
{ label: '毛毡', value: '毛毡' },
|
||||
{ label: '美式复古', value: '美式复古' },
|
||||
{ label: '赛博朋克', value: '赛博朋克' },
|
||||
{ label: '素描', value: '素描' },
|
||||
{ label: '莫奈花园', value: '莫奈花园' },
|
||||
{ label: '厚涂手绘', value: '厚涂手绘' },
|
||||
{ label: '扁平', value: '扁平' },
|
||||
{ label: '肌理', value: '肌理' },
|
||||
{ label: '像素艺术', value: '像素艺术' },
|
||||
{ label: '街头艺术', value: '街头艺术' },
|
||||
{ label: '迷幻', value: '迷幻' },
|
||||
{ label: '国风工笔', value: '国风工笔' },
|
||||
{ label: '巴洛克', value: '巴洛克' }
|
||||
{ labelKey: 'paintings.dmxapi.style_types.ghibli', value: '吉卜力' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.pixar', value: '皮克斯' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.yarn_doll', value: '绒线玩偶' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.watercolor', value: '水彩画' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.cartoon_illustration', value: '卡通插画' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.3d_cartoon', value: '3D卡通' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.japanese_anime', value: '日系动漫' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.wood_carving', value: '木雕' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.poetic_ancient', value: '唯美古风' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.25d_animation', value: '2.5D动画' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.fresh_anime', value: '清新日漫' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.clay', value: '黏土' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.little_people_book', value: '小人书插画' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.ukiyo_e', value: '浮世绘' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.felt', value: '毛毡' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.american_retro', value: '美式复古' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.cyberpunk', value: '赛博朋克' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.sketch', value: '素描' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.monet_garden', value: '莫奈花园' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.oil_painting', value: '厚涂手绘' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.flat', value: '扁平' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.texture', value: '肌理' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.pixel_art', value: '像素艺术' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.street_art', value: '街头艺术' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.psychedelic', value: '迷幻' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.chinese_gongbi', value: '国风工笔' },
|
||||
{ labelKey: 'paintings.dmxapi.style_types.baroque', value: '巴洛克' }
|
||||
]
|
||||
|
||||
export const COURSE_URL = 'http://seedream.dmxapi.cn/'
|
||||
@ -76,9 +76,9 @@ export const DEFAULT_PAINTING: DmxapiPainting = {
|
||||
}
|
||||
|
||||
export const MODEOPTIONS = [
|
||||
{ label: 'paintings.mode.generate', value: generationModeType.GENERATION },
|
||||
{ label: 'paintings.mode.edit', value: generationModeType.EDIT },
|
||||
{ label: 'paintings.mode.merge', value: generationModeType.MERGE }
|
||||
{ labelKey: 'paintings.mode.generate', value: generationModeType.GENERATION },
|
||||
{ labelKey: 'paintings.mode.edit', value: generationModeType.EDIT },
|
||||
{ labelKey: 'paintings.mode.merge', value: generationModeType.MERGE }
|
||||
]
|
||||
|
||||
// 获取模型分组数据
|
||||
|
||||
@ -33,16 +33,16 @@ export const DEFAULT_PAINTING = {
|
||||
}
|
||||
|
||||
export const QUALITY_OPTIONS = [
|
||||
{ label: '标准(默认)', value: 'standard' },
|
||||
{ label: '高清', value: 'hd' }
|
||||
{ label: 'paintings.zhipu.quality_options.standard_default', value: 'standard' },
|
||||
{ label: 'paintings.zhipu.quality_options.hd', value: 'hd' }
|
||||
]
|
||||
|
||||
export const IMAGE_SIZES = [
|
||||
{ label: '1024x1024 (默认)', value: '1024x1024' },
|
||||
{ label: '768x1344', value: '768x1344' },
|
||||
{ label: '864x1152', value: '864x1152' },
|
||||
{ label: '1344x768', value: '1344x768' },
|
||||
{ label: '1152x864', value: '1152x864' },
|
||||
{ label: '1440x720', value: '1440x720' },
|
||||
{ label: '720x1440', value: '720x1440' }
|
||||
{ label: 'paintings.zhipu.image_sizes.1024x1024_default', value: '1024x1024' },
|
||||
{ label: 'paintings.zhipu.image_sizes.768x1344', value: '768x1344' },
|
||||
{ label: 'paintings.zhipu.image_sizes.864x1152', value: '864x1152' },
|
||||
{ label: 'paintings.zhipu.image_sizes.1344x768', value: '1344x768' },
|
||||
{ label: 'paintings.zhipu.image_sizes.1152x864', value: '1152x864' },
|
||||
{ label: 'paintings.zhipu.image_sizes.1440x720', value: '1440x720' },
|
||||
{ label: 'paintings.zhipu.image_sizes.720x1440', value: '720x1440' }
|
||||
]
|
||||
|
||||
@ -29,17 +29,20 @@ export type ConfigItem = {
|
||||
tooltip?: string
|
||||
options?:
|
||||
| Array<{
|
||||
label: string
|
||||
/** i18n key for label (use t() to translate), mutually exclusive with label */
|
||||
labelKey?: string
|
||||
/** Direct display label (no translation needed), mutually exclusive with labelKey */
|
||||
label?: string
|
||||
title?: string
|
||||
value?: string | number
|
||||
icon?: string
|
||||
onlyV2?: boolean
|
||||
options?: Array<{ label: string; value: string | number; icon?: string; onlyV2?: boolean }>
|
||||
options?: Array<{ labelKey?: string; label?: string; value: string | number; icon?: string; onlyV2?: boolean }>
|
||||
}>
|
||||
| ((
|
||||
config: ConfigItem,
|
||||
painting: Partial<PaintingAction>
|
||||
) => Array<{ label: string; value: string | number; icon?: string; onlyV2?: boolean }>)
|
||||
) => Array<{ labelKey?: string; label?: string; value: string | number; icon?: string; onlyV2?: boolean }>)
|
||||
min?: number
|
||||
max?: number
|
||||
step?: number
|
||||
@ -160,7 +163,7 @@ export const createModeConfigs = (): Record<AihubmixMode, ConfigItem[]> => {
|
||||
key: 'size',
|
||||
title: 'paintings.aspect_ratio',
|
||||
options: [
|
||||
{ label: '自动', value: 'auto' },
|
||||
{ labelKey: 'paintings.image_size_options.auto', value: 'auto' },
|
||||
{ label: '1:1', value: '1024x1024' },
|
||||
{ label: '3:2', value: '1536x1024' },
|
||||
{ label: '2:3', value: '1024x1536' }
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
export const ASPECT_RATIOS = [
|
||||
{
|
||||
label: 'paintings.aspect_ratios.square',
|
||||
labelKey: 'paintings.aspect_ratios.square',
|
||||
options: [
|
||||
{
|
||||
label: '1:1',
|
||||
@ -9,7 +9,7 @@ export const ASPECT_RATIOS = [
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'paintings.aspect_ratios.landscape',
|
||||
labelKey: 'paintings.aspect_ratios.landscape',
|
||||
options: [
|
||||
{
|
||||
label: '1:2',
|
||||
@ -42,7 +42,7 @@ export const ASPECT_RATIOS = [
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'paintings.aspect_ratios.landscape',
|
||||
labelKey: 'paintings.aspect_ratios.landscape',
|
||||
options: [
|
||||
{
|
||||
label: '2:1',
|
||||
@ -78,28 +78,28 @@ export const ASPECT_RATIOS = [
|
||||
|
||||
export const STYLE_TYPES = [
|
||||
{
|
||||
label: 'paintings.style_types.auto',
|
||||
labelKey: 'paintings.style_types.auto',
|
||||
value: 'AUTO'
|
||||
},
|
||||
{
|
||||
label: 'paintings.style_types.general',
|
||||
labelKey: 'paintings.style_types.general',
|
||||
value: 'GENERAL'
|
||||
},
|
||||
{
|
||||
label: 'paintings.style_types.realistic',
|
||||
labelKey: 'paintings.style_types.realistic',
|
||||
value: 'REALISTIC'
|
||||
},
|
||||
{
|
||||
label: 'paintings.style_types.design',
|
||||
labelKey: 'paintings.style_types.design',
|
||||
value: 'DESIGN'
|
||||
},
|
||||
{
|
||||
label: 'paintings.style_types.3d',
|
||||
labelKey: 'paintings.style_types.3d',
|
||||
value: 'RENDER_3D',
|
||||
onlyV2: true // 仅V2模型支持
|
||||
},
|
||||
{
|
||||
label: 'paintings.style_types.anime',
|
||||
labelKey: 'paintings.style_types.anime',
|
||||
value: 'ANIME',
|
||||
onlyV2: true // 仅V2模型支持
|
||||
}
|
||||
@ -111,39 +111,39 @@ export const V3_STYLE_TYPES = STYLE_TYPES.filter((style) => !style.onlyV2)
|
||||
// 新增V3渲染速度选项
|
||||
export const RENDERING_SPEED_OPTIONS = [
|
||||
{
|
||||
label: 'paintings.rendering_speeds.default',
|
||||
labelKey: 'paintings.rendering_speeds.default',
|
||||
value: 'DEFAULT'
|
||||
},
|
||||
{
|
||||
label: 'paintings.rendering_speeds.turbo',
|
||||
labelKey: 'paintings.rendering_speeds.turbo',
|
||||
value: 'TURBO'
|
||||
},
|
||||
{
|
||||
label: 'paintings.rendering_speeds.quality',
|
||||
labelKey: 'paintings.rendering_speeds.quality',
|
||||
value: 'QUALITY'
|
||||
}
|
||||
]
|
||||
|
||||
export const QUALITY_OPTIONS = [
|
||||
{ label: 'paintings.quality_options.auto', value: 'auto' },
|
||||
{ label: 'paintings.quality_options.low', value: 'low' },
|
||||
{ label: 'paintings.quality_options.medium', value: 'medium' },
|
||||
{ label: 'paintings.quality_options.high', value: 'high' }
|
||||
{ labelKey: 'paintings.quality_options.auto', value: 'auto' },
|
||||
{ labelKey: 'paintings.quality_options.low', value: 'low' },
|
||||
{ labelKey: 'paintings.quality_options.medium', value: 'medium' },
|
||||
{ labelKey: 'paintings.quality_options.high', value: 'high' }
|
||||
]
|
||||
|
||||
export const MODERATION_OPTIONS = [
|
||||
{ label: 'paintings.moderation_options.auto', value: 'auto' },
|
||||
{ label: 'paintings.moderation_options.low', value: 'low' }
|
||||
{ labelKey: 'paintings.moderation_options.auto', value: 'auto' },
|
||||
{ labelKey: 'paintings.moderation_options.low', value: 'low' }
|
||||
]
|
||||
|
||||
export const BACKGROUND_OPTIONS = [
|
||||
{ label: 'paintings.background_options.auto', value: 'auto' },
|
||||
{ label: 'paintings.background_options.transparent', value: 'transparent' },
|
||||
{ label: 'paintings.background_options.opaque', value: 'opaque' }
|
||||
{ labelKey: 'paintings.background_options.auto', value: 'auto' },
|
||||
{ labelKey: 'paintings.background_options.transparent', value: 'transparent' },
|
||||
{ labelKey: 'paintings.background_options.opaque', value: 'opaque' }
|
||||
]
|
||||
|
||||
export const PERSON_GENERATION_OPTIONS = [
|
||||
{ label: 'paintings.person_generation_options.allow_all', value: 'ALLOW_ALL' },
|
||||
{ label: 'paintings.person_generation_options.allow_adult', value: 'ALLOW_ADULT' },
|
||||
{ label: 'paintings.person_generation_options.allow_none', value: 'DONT_ALLOW' }
|
||||
{ labelKey: 'paintings.person_generation_options.allow_all', value: 'ALLOW_ALL' },
|
||||
{ labelKey: 'paintings.person_generation_options.allow_adult', value: 'ALLOW_ADULT' },
|
||||
{ labelKey: 'paintings.person_generation_options.allow_none', value: 'DONT_ALLOW' }
|
||||
]
|
||||
|
||||
@ -44,32 +44,30 @@ const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, ag
|
||||
resolve()
|
||||
}
|
||||
|
||||
const items = (
|
||||
[
|
||||
{
|
||||
key: 'essential',
|
||||
label: t('agent.settings.essential')
|
||||
},
|
||||
{
|
||||
key: 'prompt',
|
||||
label: t('agent.settings.prompt')
|
||||
},
|
||||
{
|
||||
key: 'tooling',
|
||||
label: t('agent.settings.tooling.tab', 'Tooling & permissions')
|
||||
},
|
||||
{
|
||||
key: 'plugins',
|
||||
label: t('agent.settings.plugins.tab', 'Plugins')
|
||||
},
|
||||
{
|
||||
key: 'advanced',
|
||||
label: t('agent.settings.advance.title', 'Advanced Settings')
|
||||
}
|
||||
] as const satisfies { key: AgentSettingPopupTab; label: string }[]
|
||||
).filter(Boolean)
|
||||
const items = [
|
||||
{
|
||||
key: 'essential',
|
||||
label: t('agent.settings.essential')
|
||||
},
|
||||
{
|
||||
key: 'prompt',
|
||||
label: t('agent.settings.prompt')
|
||||
},
|
||||
{
|
||||
key: 'tooling',
|
||||
label: t('agent.settings.tooling.tab', 'Tooling & permissions')
|
||||
},
|
||||
{
|
||||
key: 'plugins',
|
||||
label: t('agent.settings.plugins.tab', 'Plugins')
|
||||
},
|
||||
{
|
||||
key: 'advanced',
|
||||
label: t('agent.settings.advance.title', 'Advanced Settings')
|
||||
}
|
||||
] as const satisfies { key: AgentSettingPopupTab; label: string }[]
|
||||
|
||||
const ModalContent = () => {
|
||||
const renderModalContent = () => {
|
||||
if (isLoading) {
|
||||
// TODO: use skeleton for better ux
|
||||
return (
|
||||
@ -146,7 +144,7 @@ const AgentSettingPopupContainer: React.FC<AgentSettingPopupParams> = ({ tab, ag
|
||||
}}
|
||||
width="min(800px, 70vw)"
|
||||
centered>
|
||||
<ModalContent />
|
||||
{renderModalContent()}
|
||||
</StyledModal>
|
||||
)
|
||||
}
|
||||
|
||||
@ -45,28 +45,26 @@ const SessionSettingPopupContainer: React.FC<SessionSettingPopupParams> = ({ tab
|
||||
resolve()
|
||||
}
|
||||
|
||||
const items = (
|
||||
[
|
||||
{
|
||||
key: 'essential',
|
||||
label: t('agent.settings.essential')
|
||||
},
|
||||
{
|
||||
key: 'prompt',
|
||||
label: t('agent.settings.prompt')
|
||||
},
|
||||
{
|
||||
key: 'tooling',
|
||||
label: t('agent.settings.tooling.tab', 'Tooling & permissions')
|
||||
},
|
||||
{
|
||||
key: 'advanced',
|
||||
label: t('agent.settings.advance.title', 'Advanced Settings')
|
||||
}
|
||||
] as const satisfies { key: AgentSettingPopupTab; label: string }[]
|
||||
).filter(Boolean)
|
||||
const items = [
|
||||
{
|
||||
key: 'essential',
|
||||
label: t('agent.settings.essential')
|
||||
},
|
||||
{
|
||||
key: 'prompt',
|
||||
label: t('agent.settings.prompt')
|
||||
},
|
||||
{
|
||||
key: 'tooling',
|
||||
label: t('agent.settings.tooling.tab', 'Tooling & permissions')
|
||||
},
|
||||
{
|
||||
key: 'advanced',
|
||||
label: t('agent.settings.advance.title', 'Advanced Settings')
|
||||
}
|
||||
] as const satisfies { key: AgentSettingPopupTab; label: string }[]
|
||||
|
||||
const ModalContent = () => {
|
||||
const renderModalContent = () => {
|
||||
if (isLoading) {
|
||||
// TODO: use skeleton for better ux
|
||||
return (
|
||||
@ -132,7 +130,7 @@ const SessionSettingPopupContainer: React.FC<SessionSettingPopupParams> = ({ tab
|
||||
}}
|
||||
width="min(800px, 70vw)"
|
||||
centered>
|
||||
<ModalContent />
|
||||
{renderModalContent()}
|
||||
</StyledModal>
|
||||
)
|
||||
}
|
||||
|
||||
@ -97,7 +97,7 @@ export const PluginDetailModal: FC<PluginDetailModalProps> = ({
|
||||
if (result.success) {
|
||||
setContent(editedContent)
|
||||
setIsEditing(false)
|
||||
window.toast?.success('Plugin content saved successfully')
|
||||
window.toast?.success(t('plugins.content_saved'))
|
||||
} else {
|
||||
window.toast?.error(`Failed to save: ${result.error.type}`)
|
||||
}
|
||||
@ -177,7 +177,7 @@ export const PluginDetailModal: FC<PluginDetailModalProps> = ({
|
||||
{/* Description */}
|
||||
{plugin.description && (
|
||||
<div className="mb-4">
|
||||
<h3 className="mb-2 font-semibold text-small">Description</h3>
|
||||
<h3 className="mb-2 font-semibold text-small">{t('plugins.detail.description')}</h3>
|
||||
<p className="text-default-600 text-small">{plugin.description}</p>
|
||||
</div>
|
||||
)}
|
||||
@ -185,7 +185,7 @@ export const PluginDetailModal: FC<PluginDetailModalProps> = ({
|
||||
{/* Author */}
|
||||
{plugin.author && (
|
||||
<div className="mb-4">
|
||||
<h3 className="mb-2 font-semibold text-small">Author</h3>
|
||||
<h3 className="mb-2 font-semibold text-small">{t('plugins.detail.author')}</h3>
|
||||
<p className="text-default-600 text-small">{plugin.author}</p>
|
||||
</div>
|
||||
)}
|
||||
@ -193,7 +193,7 @@ export const PluginDetailModal: FC<PluginDetailModalProps> = ({
|
||||
{/* Tools (for agents) */}
|
||||
{plugin.tools && plugin.tools.length > 0 && (
|
||||
<div className="mb-4">
|
||||
<h3 className="mb-2 font-semibold text-small">Tools</h3>
|
||||
<h3 className="mb-2 font-semibold text-small">{t('plugins.detail.tools')}</h3>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{plugin.tools.map((tool) => (
|
||||
<Tag key={tool}>{tool}</Tag>
|
||||
@ -205,7 +205,7 @@ export const PluginDetailModal: FC<PluginDetailModalProps> = ({
|
||||
{/* Allowed Tools (for commands) */}
|
||||
{plugin.allowed_tools && plugin.allowed_tools.length > 0 && (
|
||||
<div className="mb-4">
|
||||
<h3 className="mb-2 font-semibold text-small">Allowed Tools</h3>
|
||||
<h3 className="mb-2 font-semibold text-small">{t('plugins.detail.allowed_tools')}</h3>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{plugin.allowed_tools.map((tool) => (
|
||||
<Tag key={tool}>{tool}</Tag>
|
||||
@ -217,7 +217,7 @@ export const PluginDetailModal: FC<PluginDetailModalProps> = ({
|
||||
{/* Tags */}
|
||||
{plugin.tags && plugin.tags.length > 0 && (
|
||||
<div className="mb-4">
|
||||
<h3 className="mb-2 font-semibold text-small">Tags</h3>
|
||||
<h3 className="mb-2 font-semibold text-small">{t('plugins.detail.tags')}</h3>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{plugin.tags.map((tag) => (
|
||||
<Tag key={tag}>{tag}</Tag>
|
||||
@ -228,23 +228,23 @@ export const PluginDetailModal: FC<PluginDetailModalProps> = ({
|
||||
|
||||
{/* Metadata */}
|
||||
<div className="mb-4">
|
||||
<h3 className="mb-2 font-semibold text-small">Metadata</h3>
|
||||
<h3 className="mb-2 font-semibold text-small">{t('plugins.detail.metadata')}</h3>
|
||||
<div className="space-y-1 text-small">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-default-500">File:</span>
|
||||
<span className="text-default-500">{t('plugins.detail.file')}:</span>
|
||||
<span className="font-mono text-default-600 text-tiny">{plugin.filename}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-default-500">Size:</span>
|
||||
<span className="text-default-500">{t('plugins.detail.size')}:</span>
|
||||
<span className="text-default-600">{(plugin.size / 1024).toFixed(2)} KB</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-default-500">Source:</span>
|
||||
<span className="text-default-500">{t('plugins.detail.source')}:</span>
|
||||
<span className="font-mono text-default-600 text-tiny">{plugin.sourcePath}</span>
|
||||
</div>
|
||||
{plugin.installedAt && (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-default-500">Installed:</span>
|
||||
<span className="text-default-500">{t('plugins.detail.installed')}:</span>
|
||||
<span className="text-default-600">{new Date(plugin.installedAt).toLocaleString()}</span>
|
||||
</div>
|
||||
)}
|
||||
@ -254,7 +254,7 @@ export const PluginDetailModal: FC<PluginDetailModalProps> = ({
|
||||
{/* Content */}
|
||||
<div className="mb-4">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<h3 className="font-semibold text-small">Content</h3>
|
||||
<h3 className="font-semibold text-small">{t('plugins.detail.content')}</h3>
|
||||
{installed && !contentLoading && !contentError && (
|
||||
<div className="flex gap-2">
|
||||
{isEditing ? (
|
||||
|
||||
@ -365,16 +365,24 @@ const AssistantModelSettings: FC<Props> = ({ assistant, updateAssistant, updateA
|
||||
</Row>
|
||||
<Row align="middle" gutter={24}>
|
||||
<Col span={24}>
|
||||
<Slider
|
||||
min={0}
|
||||
max={MAX_CONTEXT_COUNT}
|
||||
onChange={setContextCount}
|
||||
onChangeComplete={onContextCountChange}
|
||||
value={typeof contextCount === 'number' ? contextCount : 0}
|
||||
marks={{ 0: '0', 25: '25', 50: '50', 75: '75', 100: t('chat.settings.max') }}
|
||||
step={1}
|
||||
tooltip={{ formatter: formatSliderTooltip, open: false }}
|
||||
/>
|
||||
<ContextSliderWrapper>
|
||||
<Slider
|
||||
min={0}
|
||||
max={MAX_CONTEXT_COUNT}
|
||||
onChange={setContextCount}
|
||||
onChangeComplete={onContextCountChange}
|
||||
value={typeof contextCount === 'number' ? contextCount : 0}
|
||||
marks={{
|
||||
0: '0',
|
||||
25: '25',
|
||||
50: '50',
|
||||
75: '75',
|
||||
100: <span style={{ position: 'absolute', right: -2 }}>{t('chat.settings.max')}</span>
|
||||
}}
|
||||
step={1}
|
||||
tooltip={{ formatter: formatSliderTooltip, open: false }}
|
||||
/>
|
||||
</ContextSliderWrapper>
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider style={{ margin: '10px 0' }} />
|
||||
@ -539,4 +547,8 @@ const ModelName = styled.span`
|
||||
display: inline-block;
|
||||
`
|
||||
|
||||
const ContextSliderWrapper = styled.div`
|
||||
padding-bottom: 5px;
|
||||
`
|
||||
|
||||
export default AssistantModelSettings
|
||||
|
||||
@ -2,10 +2,11 @@ import CodeEditor from '@renderer/components/CodeEditor'
|
||||
import { ResetIcon } from '@renderer/components/Icons'
|
||||
import { HStack } from '@renderer/components/Layout'
|
||||
import TextBadge from '@renderer/components/TextBadge'
|
||||
import { isMac, THEME_COLOR_PRESETS } from '@renderer/config/constant'
|
||||
import { isLinux, isMac, THEME_COLOR_PRESETS } from '@renderer/config/constant'
|
||||
import { DEFAULT_SIDEBAR_ICONS } from '@renderer/config/sidebar'
|
||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
|
||||
import { useTimer } from '@renderer/hooks/useTimer'
|
||||
import useUserTheme from '@renderer/hooks/useUserTheme'
|
||||
import { useAppDispatch } from '@renderer/store'
|
||||
import type { AssistantIconType } from '@renderer/store/settings'
|
||||
@ -68,12 +69,15 @@ const DisplaySettings: FC = () => {
|
||||
sidebarIcons,
|
||||
setTheme,
|
||||
assistantIconType,
|
||||
userTheme
|
||||
userTheme,
|
||||
useSystemTitleBar,
|
||||
setUseSystemTitleBar
|
||||
} = useSettings()
|
||||
const { navbarPosition, setNavbarPosition } = useNavbarPosition()
|
||||
const { theme, settedTheme } = useTheme()
|
||||
const { t } = useTranslation()
|
||||
const dispatch = useAppDispatch()
|
||||
const { setTimeoutTimer } = useTimer()
|
||||
const [currentZoom, setCurrentZoom] = useState(1.0)
|
||||
const { setUserTheme } = useUserTheme()
|
||||
|
||||
@ -88,6 +92,26 @@ const DisplaySettings: FC = () => {
|
||||
[setWindowStyle]
|
||||
)
|
||||
|
||||
const handleUseSystemTitleBarChange = (checked: boolean) => {
|
||||
window.modal.confirm({
|
||||
title: t('settings.use_system_title_bar.confirm.title'),
|
||||
content: t('settings.use_system_title_bar.confirm.content'),
|
||||
okText: t('common.confirm'),
|
||||
cancelText: t('common.cancel'),
|
||||
centered: true,
|
||||
onOk() {
|
||||
setUseSystemTitleBar(checked)
|
||||
setTimeoutTimer(
|
||||
'handleUseSystemTitleBarChange',
|
||||
() => {
|
||||
window.api.relaunchApp()
|
||||
},
|
||||
500
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleColorPrimaryChange = useCallback(
|
||||
(colorHex: string) => {
|
||||
setUserTheme({
|
||||
@ -260,6 +284,15 @@ const DisplaySettings: FC = () => {
|
||||
</SettingRow>
|
||||
</>
|
||||
)}
|
||||
{isLinux && (
|
||||
<>
|
||||
<SettingDivider />
|
||||
<SettingRow>
|
||||
<SettingRowTitle>{t('settings.use_system_title_bar.title')}</SettingRowTitle>
|
||||
<Switch checked={useSystemTitleBar} onChange={handleUseSystemTitleBarChange} />
|
||||
</SettingRow>
|
||||
</>
|
||||
)}
|
||||
</SettingGroup>
|
||||
<SettingGroup theme={theme}>
|
||||
<SettingTitle style={{ justifyContent: 'flex-start', gap: 5 }}>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user