Feat/Native AI Agent (#640)

* refactor: only use message id

* feat: stream update, but react don't react on it dny

* feat: start stop

* feat: basic resoponse handlers

* feat: load handler config schema

* chore: upgrade to "moduleResolution": "bundler"

* fix: prompt concat

* feat: rjfs with mui

* fix: mui v7 upgrade

* fix: field editor tabs

* feat: Description field is i18n key, use i18nAlly extension to see it on VSCode. And use react-i18next to translate it on frontend.

* refactor: extract some shared components

* refactor: remove @mui/lab

* feat: faster editor

* feat: beautify editor

* refactor: better style

* fix: fullscreen style

* fix: array sort

* fix: editor not saved

* fix: broken array

* chore: upgrade mui deps

* Update type.d.ts

* feat: upgrade and install react dev tools

* refactor: simplify the code

* refactor: simplify the code

* refactor: simplify ai generated garbage code

* fix: translated label

* feat: translate enum and conditional show field based on enum

* feat: click to open source

* fix: ai generated garbage code to solve id and index problem

* refactor: simplify

* refactor: shorten

* test: add jest

* chore: node-linker=hoisted as electron forge suggested

* test: e2e

* fix: test failed on ci

* test: faster by disable some typecheck

* fix: ci

* fix: ci

* refactor: remove unused

* fix: use macos and take screenshot in ci

* docs:

(cherry picked from commit b1a9706264)

* fix: ci crash due to no dugite binary, due to NODE_ENV=test not set

* Update test.yml

* fix: ci

* Update release.yml

* docs: test

* refactor: move folders and lint

* refactor: organize pages to reduce level

* refactor: merge page service and workspace service to allow drag & drop of pages

* Update ipc-syncadaptor.ts

* Update browserViewMetaData.ts

* fix: view.webContents.loadURL won't reload on new electron version

* fix: try to disable annoying useless disabling pasting "feature"

https://github.com/electron/electron/issues/40995

* fix: initial page

* feat: allow sort add icon

* test: use vitest instead

* refactor: use emotion instead

* fix: all ts error after migrate to vitest and emotion

* fix: Component selectors can only be used in conjunction with @emotion/babel-plugin, the swc Emotion plugin, or another Emotion-aware compiler transform.

fix by not using this usage

* fix: too many open files

* test: add basic main page test

* refactor: split workspace type to make it more specific

* test: support mock react lazy import component by re-export them

* test: sidebar

* refactor: move mock to global

* test: testing library fix

* test: new wiki form

* docs: test with vitest

* lint: fix

* docs: pronounication & remove toc as gh build in

* feat: tools and rag

* feat: prompt for build-in tools

* fix: i18n

* fix: tool using

* test: fix

* refactor: Auto-create default wiki workspace if none exists (handled in backend)

* fix: create wiki workspace so it is on first one

* refactor: remove some useless feature

* refactor: use plugin instead of handler

* chore: make concatPrompt async iterator

* feat: show progress on frontend

* fix: errors

* fix: ConfigError: Config (unnamed): Key "overrides": This appears to be in eslintrc format rather than flat config format.

* Update package.json

* feat: allow windows to hide titlebar

* fix: logger error when ctrl+c on mac

* lint

* refactor: use plugin everywhere

* refactor: move more logic to plugin

* refactor: run tool in plugin, simplify abstraction

* refactor: remove ai generated duplicate

* refactor

* refactor: less plugins

* test: simplify wiki search test

* test: remove any

* fix: not streaming, tool order wrong

* test: plugin system and streaming

* refactor: remove useless auto reply plugin

* fix: hide duration expired tool calling message, so long tool result only show to ai once

* fix: $ props should lower cased

* test: support run as electron so can use sqlite3 compiled bin for electron

* docs: better electron as node run and doc

* test: restore to use threads instead of fock. Seems this also works for inmemory database

* test: fix frontend ui test

* lint: ai content

* test: fix coverage

* test: Refactor test mocks to dedicated __mocks__ directory

Moved common test mocks from setup-vitest.ts into separate files under src/__tests__/__mocks__ for better organization and maintainability. Updated documentation to reflect the new structure. Removed fileMock.js and updated setup-vitest.ts to import and use the new centralized mocks.

* Update ErrorDuringStart.md

* Update messageManagementPlugin.test.ts

* test: Fix Electron test process cleanup and update test config

Update test:unit script to use cross-env for ELECTRON_RUN_AS_NODE, ensuring child processes inherit the variable and preventing resource leaks. Remove manual process.env setting from vitest.config.ts and add documentation on handling related errors in Testing.md. Also, add 'hanging-process' reporter to Vitest config for improved diagnostics.

* fix: warning in test

* Create AgentInstanceWorkflow.md

* fix: duration bug

* fix: ai not response for tool result

* test: Add agent workflow feature and step definitions

Introduces a new Cucumber feature for agent workflow, including multi-round conversation and tool usage. Adds agent-specific step definitions for message input and chat history validation. Refactors application step definitions for improved selector handling and element interaction. Updates test scripts in package.json to include a preparation step for e2e tests.

* Update application.ts

* test: Add mock OpenAI server and tests

Introduces a MockOpenAIServer for simulating OpenAI chat completions, including tool call and tool result responses. Adds corresponding tests and updates vitest config to include tests in the features directory.

* test: simplify steps

* test: Add separate test userData and wiki folders

Introduces 'userData-test' and 'wiki-test' folders for test environments, updating appPaths, fileNames, and paths constants to distinguish between development and test modes. This helps prevent conflicts when running test and development instances simultaneously.

* Update eslint.config.mjs

@typescript-eslint/no-unnecessary-condition': 'off'

* test: Add data-testid attributes for test automation

Added data-testid attributes to form inputs and buttons in ExternalAPI components to improve test automation reliability. Updated feature files and step definitions to use these selectors, refined window switching logic, and improved timing for UI interactions. Also exposed isElectronDevelopment and added isMainWindowPage utility for window identification.

* test: fix wront page type by ai written garbage code

* Update application.ts

* Update Testing.md

* fix: no log during e2e test, and error creating wiki blocks other init steps

* test: click agent workspace button, and refactor mock server

* test: mock streamable openai api

* rename

* chore: try esbuild loader, renderer build too slow

* chore: organize webpack

* chore: ignore service code from hot reload

* Update Testing.md

* test: use full agentinstance in test

* chore: webpack error

* Update Testing.md

* chore: remove useless spectron

* test: EsbuildPlugin's `define` doesn't work, it won't set env properly.

* test: e2e mock openai

* lint: disable @typescript-eslint/require-await

* test: Add quick access to create default agent tab

Introduces a 'Create Default Agent' quick access button in the Agent New Tab page, with localization support. Adds utility to close all tabs and create a default agent tab for fallback scenarios, improves test selectors for tab and close actions, and refactors agent chat tab creation logic for consistency and testability.

* feat: remove unuse favorite

* feat: Add wiki operation and workspaces list plugins

Introduces wikiOperationPlugin and workspacesListPlugin for agent instance prompt and response handling. Updates plugin registry, test coverage, and default agent configuration to support wiki workspace listing and wiki note operations (create, update, delete) via tool calls. Refactors wikiSearchPlugin to delegate workspace list injection to workspacesListPlugin.

* Refactor plugin schema system for dynamic registration

Introduces a dynamic plugin schema registry, allowing plugins to register their parameter schemas and metadata at runtime. Refactors prompt concat schema generation to use dynamically registered plugin schemas, removes static plugin schema definitions, and updates all plugin files to export their parameter schemas. Adds new modelContextProtocolPlugin and schemaRegistry modules, and updates plugin initialization to register schemas and metadata. This enables extensibility and type safety for plugin configuration and validation.

* refactor: move PromptConfigForm to inside PromptPreviewDialog

* test: frontent render tool usage info

* test: split file

* test: fix error

* test: wiki operation

* test: remove log and clean up test deps

* Update i18next-electron-fs-backend.ts

* fix: wikiOperationInServer not called due to no message

* test: fix

* test: Refactor agent feature setup and improve mock server URL handling

Moved AI provider and model configuration to a shared setup scenario in agent.feature to avoid redundant steps in each test. Enhanced application.ts to use a fallback localhost URL for MOCK_SERVER_URL when the mock server is not available, improving test reliability.

* Remove retry logic from basicPromptConcatHandler

Eliminated retryCount and maxRetries from basicPromptConcatHandler, simplifying the control flow and removing the retry limit for LLM calls. Logging and yield logic were updated to reflect the removal of retries.

* Update agent.feature

* test: agent and default wiki

* test: refactor wiki cleanup

* Update agent.feature

* Refactor AI settings setup and add preference feature

Moved AI provider and model configuration steps from agent.feature to a new preference.feature file for better separation of concerns. Updated step definitions in agent.ts to improve robustness when reading and writing settings files, including type usage and directory checks.

* test: try debug can't stop bug

* Update Testing.md

* fix: cancel agent not update cancel button

* refactor: update Frontend use `void window.service.native.log` to log to file.

* test: log from renderer test

* feat: add default embedding model config and victor search service and search preference panel

* test: default embedding form and

* test: default agent

* refator: unused tool listing methods

* Refactor test database setup for integration tests

Centralizes in-memory SQLite test database initialization and cleanup in shared utilities for all integration tests. Updates agentDefinition and messageManagementPlugin tests to use the shared test database, improving reliability and reducing code duplication.

* fix: app path wrong in unit test

* feat: externalAPIDebug

* test: embedding service and let db use real in memory one

* test: provide at least one tab for close tab test

* fix: victor db not loaded in new connection

* test: disable hot reload

* Update DeveloperTools.tsx

* feat: tool message & wiki tool schema

* feat: pref to open db

* chore: skip ExternalsPlugin on dev

* fix: APIs doesn't accept 'tool' role, and it won't return anything when API calls

* fix: docs and remove ai fake fix

* refactor: get agent list don't need to have message

* Update basicPromptConcatHandler.failure.test.ts

* Update basicPromptConcatHandler.test.ts

* fix: role wrong cause e2e failed

* test: allow e2e create .db file to check

* feat: create new agent

* feat: getAgentDefinitionTemplatesFromWikis

* fix: Prevent update non-active (hiding) wiki workspace, so it won't pop up to cover other active agent workspace

* fix: Ensure the config change is fully persisted before proceeding

* test: Don't bring up window when running e2e test, otherwise it will annoy the developer who is doing other things.

* feat: Edit existing agent definition workflow

* Update StyledArrayContainer.tsx

* test: prevent mock server hang

* Refactor UI step definitions into separate file

Moved UI-related Cucumber step definitions from application.ts to a new ui.ts file for better separation of concerns and maintainability. application.ts now only contains application-specific logic.

* lint: ai auto fix

* Clean up feature files and improve test coverage

Removed unnecessary blank lines and improved formatting in feature files for better readability. Updated tsconfig to use 'bundler' module resolution. Enhanced EditAgentDefinitionContent test to mock and verify console.error output. Added type annotation for IAgentDefinitionService in basicPromptConcatHandler.failure.test.ts for improved type safety.

* test: simplify message text match

* feat: transcription and image generation model config

* fix: style props error

* chore: remove unused file

* chore: try to imporove dev start speed but no sig improvment

Replaces 'pnpm start' with 'pnpm start:init' for initial setup and documents slow startup in development. Adds a debug Webpack script, disables polling in renderer file watching, and only enables CircularDependencyPlugin in production/CI for faster dev builds. WebpackBar now only shows when DEBUG includes 'electron-forge:*'. ForkTsCheckerWebpackPlugin is now configured for async checks with memory limits and test file exclusion. Updates documentation to reflect these changes.

* docs: why not vite

* refactor: basic vite usage, but wiki worker is not running and view is not showing

* refactor: remove lazy inject so vite works and wiki worker works, wiki in browser view can loads

* Replace electron-squirrel-startup with inline implementation

Added a custom Squirrel event handler in src/helpers/squirrelStartup.ts to handle Windows install/update/uninstall events, replacing the electron-squirrel-startup package. This avoids ESM/CommonJS compatibility issues and simplifies event handling in src/main.ts.

* refactor: vite build and test

* fix: e2e still use dev wiki folder

* fix: provide env

* chore: "tiddlywiki": "5.3.7"

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Apply suggestion from @Copilot

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: missing i18n

* test:  Module did not self-register: '/home/runner/work/TidGi-Desktop/TidGi-Desktop/node_modules/better-sqlite3/build/Release/better_sqlite3.node'.

* feat: i18n

* feat: zh-Hant i18n

* Update test.yml

* feat: zh-Hans in test

* test: i18n

* refactor: ts forge config

* lint: fix

* chore: update wiki

* Update pnpm-lock.yaml

* chore: update github action versions

* Update wiki

* fix: pnpm then node

* fix: Multiple versions of pnpm specified

* Update test.yml

* fix: Failed to take screenshot: page.screenshot: Target page, context or browser has been closed

* chore: CodeQL Action major versions v1 and v2 have been deprecated.

* chore: parallel codeql

* Update test.yml

* fix: i18n test not passing

* test: step screenshot in each folder

* fix: can't unzip, may due to file path

* test: increase timeout in CI

* docs: more log about wiki creation, and add log to criticial path

* Refactor: logging for structured and consistent output

Replaces string-based logger messages with structured logging throughout the codebase, providing function names, error messages, and relevant context as objects. This improves log readability, enables better filtering and searching, and standardizes error and debug reporting across services.

* chore: debug wiki copy

* fix: wiki submodule not cloned

* Revert "test: increase timeout in CI"

This reverts commit eff8583a01.

* test: reduce wait time, because most check already will wait for a while

* test: batch some e2e steps to reduce screenshot count

* Update index.ts

* chore: remove webpack files, use vite plugins

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
lin onetwo 2025-10-10 17:16:56 +08:00 committed by GitHub
parent a39023627d
commit fa9751e5ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
533 changed files with 53202 additions and 11006 deletions

View file

@ -0,0 +1,5 @@
---
applyTo: '**/*.ts|tsx|js|jsx|tid'
---
用英文注释不要在注释里直接描述代码做了什么只记录未来你觉得自己可能不容易领会到的设计目的。暂时不用关心eslint的格式要求全部编辑完成后再用`pnpm exec eslint --fix`先不用处理格式以免影响你的主要工作。我使用powershell但尽量用无须审批的vscode内置功能工具少用需要人类审批的shell例如尽量不要通过创建新文件再用powershell覆盖原文件的方式来更新文件。没用的 props 不要保留,不要搞向前兼容,应用还未发布所以可以修改任何地方,修改时也要检查调用处。有话就直接在聊天中和我说就行,但必须做完所有可能的相关工作,不要频繁询问我方案来打扰我,你有自主权决定最好的方案并立即实施。

View file

@ -3,296 +3,141 @@ name: Release App
on:
push:
tags:
- 'v*.*.*'
- "v*.*.*"
paths-ignore:
- 'README.md'
- 'docs/**'
- '.vscode'
- "README.md"
- "docs/**"
- ".vscode"
pull_request:
branches:
- master
paths-ignore:
- 'docs/**'
- 'README.md'
- '.vscode'
- "docs/**"
- "README.md"
- ".vscode"
concurrency:
group: release-ci-group
cancel-in-progress: true
jobs:
Linux:
runs-on: ubuntu-latest
test:
uses: ./.github/workflows/test.yml
build:
needs: test
strategy:
matrix:
include:
- os: ubuntu-latest
platform: linux
arch: x64
- os: ubuntu-latest
platform: linux
arch: arm64
- os: macos-latest
platform: mac
arch: x64
- os: macos-latest
platform: mac
arch: arm64
- os: windows-latest
platform: win
arch: x64
- os: windows-latest
platform: win
arch: arm64
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
submodules: 'recursive'
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: lts/*
submodules: recursive
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 'latest'
run_install: false
- name: Cache dependencies
uses: actions/cache@v3
with:
path: |
**/node_modules
~/.pnpm-store
~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
${{ runner.os }}-node-
# only run codeql on Linux
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: javascript
- name: Install dependencies (x64)
run: pnpm install && pnpm remove registry-js
env:
# for dugute, see node_modules/.pnpm/dugite@2.7.1/node_modules/dugite/script/config.js
npm_config_arch: x64
- name: Make Linux (x64)
run: pnpm run make:linux-x64
env:
CI: true
CI_PULL_REQUEST: ${{ github.event_name == 'pull_request' }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies (arm64)
run: pnpm install dugite --force
env:
npm_config_arch: arm64
- name: Make Linux (arm64)
run: pnpm run make:linux-arm
env:
CI: true
CI_PULL_REQUEST: ${{ github.event_name == 'pull_request' }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
- name: Create Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
draft: true
generate_release_notes: true
files: out/make/**/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get Renderer Bundle Stats
uses: vio/bundle-stats-action@v1
with:
id: renderer
webpack-stats-path: 'out/webpack-stats-renderer.json'
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Get Main Bundle Stats
uses: vio/bundle-stats-action@v1
with:
id: main
webpack-stats-path: 'out/webpack-stats-main.json'
repo-token: ${{ secrets.GITHUB_TOKEN }}
MacOS:
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: 'recursive'
- name: Install Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v5
with:
node-version: lts/*
- name: Install dependencies
run: pnpm install
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 'latest'
run_install: false
- name: Cache dependencies
uses: actions/cache@v3
with:
path: |
**/node_modules
~/.pnpm-store
~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
${{ runner.os }}-node-
- name: Install dependencies (x64)
run: pnpm install && pnpm remove registry-js
env:
npm_config_arch: x64
- name: Make macOS (x64)
run: pnpm run make:mac-x64
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
CI: true
CI_PULL_REQUEST: ${{ github.event_name == 'pull_request' }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install dependencies (arm64)
run: pnpm install dugite --force
env:
npm_config_arch: arm64
- name: Make macOS (arm64)
run: pnpm run make:mac-arm
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
CI: true
CI_PULL_REQUEST: ${{ github.event_name == 'pull_request' }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
draft: true
generate_release_notes: true
files: out/make/**/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get Renderer Bundle Stats
uses: vio/bundle-stats-action@v1
with:
id: renderer
webpack-stats-path: 'out/webpack-stats-renderer.json'
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Get Main Bundle Stats
uses: vio/bundle-stats-action@v1
with:
id: main
webpack-stats-path: 'out/webpack-stats-main.json'
repo-token: ${{ secrets.GITHUB_TOKEN }}
Windows:
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: 'recursive'
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 'latest'
run_install: false
- name: Cache dependencies
uses: actions/cache@v3
with:
path: |
**/node_modules
~/.pnpm-store
~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
${{ runner.os }}-node-
# Windows specific: Set up CV dependency for pngquant-bin
- name: Set up CV dependency for pngquant-bin
if: matrix.platform == 'win'
uses: ilammy/msvc-dev-cmd@v1
# Install dependencies for x64 architectures
- name: Install dependencies (x64)
run: pnpm install
if: matrix.arch == 'x64'
run: |
${{ matrix.platform == 'linux' && 'pnpm install && pnpm remove registry-js' || 'pnpm install' }}
env:
npm_config_arch: x64
# - name: Add msi to path
# run: echo "${env:wix}bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
# Enable x32 if someone still need it
# - name: Make Windows (ia32)
# run: pnpm run make:win-ia32
# env:
# CSC_LINK: ${{ secrets.WIN_CERT }}
# CSC_KEY_PASSWORD: ${{ secrets.WIN_CERT_PASS }}
# CI: true
# CI_PULL_REQUEST: ${{ github.event_name == 'pull_request' }}
# GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# - name: Rename (ia32)
# run: |
# Get-ChildItem out/make/wix/ia32
# Rename-Item -Path "out/make/wix/ia32/TidGi.msi" -NewName "Install-TidGi-Windows-ia32.msi"
- name: Make Windows (x64)
run: pnpm run make:win-x64
env:
CSC_LINK: ${{ secrets.WIN_CERT }}
CSC_KEY_PASSWORD: ${{ secrets.WIN_CERT_PASS }}
CI: true
CI_PULL_REQUEST: ${{ github.event_name == 'pull_request' }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# - name: Rename (x64)
# run: |
# Get-ChildItem out/make/wix/x64
# Rename-Item -Path "out/make/wix/x64/TidGi.msi" -NewName "Install-TidGi-Windows-x64.msi"
# Install dependencies for arm64 architectures
- name: Install dependencies (arm64)
if: matrix.arch == 'arm64'
run: pnpm install dugite --force
env:
# based on TiddlyGit-Desktop/node_modules/.pnpm/dugite@2.7.1/node_modules/dugite/script/config.js
npm_config_arch: ia32
- name: Make Windows (arm64)
run: pnpm run make:win-arm
npm_config_arch: ${{ matrix.platform == 'win' && 'ia32' || 'arm64' }}
# Build step using direct electron-forge commands
- name: Build plugins
run: pnpm run build:plugin
- name: Make ${{ matrix.platform }} (${{ matrix.arch }})
run: |
pnpm exec electron-forge make --platform=${{ matrix.platform == 'mac' && 'darwin' || matrix.platform == 'win' && 'win32' || 'linux' }} --arch=${{ matrix.arch }}
env:
CSC_LINK: ${{ secrets.WIN_CERT }}
CSC_KEY_PASSWORD: ${{ secrets.WIN_CERT_PASS }}
NODE_ENV: production
# macOS specific environment variables
APPLE_ID: ${{ matrix.platform == 'mac' && secrets.APPLE_ID || '' }}
APPLE_ID_PASSWORD: ${{ matrix.platform == 'mac' && secrets.APPLE_ID_PASSWORD || '' }}
# Windows specific environment variables
CSC_LINK: ${{ matrix.platform == 'win' && secrets.WIN_CERT || '' }}
CSC_KEY_PASSWORD: ${{ matrix.platform == 'win' && secrets.WIN_CERT_PASS || '' }}
# Common environment variables
CI: true
CI_PULL_REQUEST: ${{ github.event_name == 'pull_request' }}
# Only need 1 set of analyzer reports (linux x64)
ANALYZE: ${{ matrix.platform == 'linux' && matrix.arch == 'x64' }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# - name: Rename (arm64)
# run: |
# Get-ChildItem out/make/wix/arm64
# Rename-Item -Path "out/make/wix/arm64/TidGi.msi" -NewName "Install-TidGi-Windows-arm64.msi"
# Upload analyzer reports and packaged apps as workflow artifacts (only linux x64)
- name: Upload analyzer reports
if: matrix.platform == 'linux' && matrix.arch == 'x64'
uses: actions/upload-artifact@v4
with:
name: analyzer-reports-${{ matrix.platform }}-${{ matrix.arch }}
path: |
.vite/renderer/bundle-analyzer-renderer.html
.vite/main/bundle-analyzer-main.html
if-no-files-found: ignore
- name: Upload packaged apps
if: (matrix.platform == 'mac' && matrix.arch == 'x64') || (matrix.platform == 'win' && matrix.arch == 'x64')
uses: actions/upload-artifact@v4
with:
name: packaged-apps-${{ matrix.platform }}-${{ matrix.arch }}
path: out/make/**
if-no-files-found: ignore
# Create Release (upload artifacts from all builds)
- name: Create Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
draft: true
generate_release_notes: true
# out/make/**/*.msi
files: |
out/make/**/*.exe
${{ matrix.platform == 'win' && 'out/make/**/*.exe' || 'out/make/**/*' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get Renderer Bundle Stats
uses: vio/bundle-stats-action@v1
with:
id: renderer
webpack-stats-path: 'out/webpack-stats-renderer.json'
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Get Main Bundle Stats
uses: vio/bundle-stats-action@v1
with:
id: main
webpack-stats-path: 'out/webpack-stats-main.json'
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Bundle analysis is handled by Vite configs when ANALYZE=true

75
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,75 @@
name: Test
on:
workflow_call:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
submodules: recursive
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
run_install: false
- name: Install Node.js
uses: actions/setup-node@v5
with:
node-version: lts/*
- name: Install dependencies
run: pnpm install
- name: Rebuild native modules for Electron
run: pnpm exec electron-rebuild -f -w better-sqlite3
- name: Run linting
run: pnpm run lint
# Install minimal Linux dependencies for Electron GUI testing https://www.electronjs.org/docs/latest/tutorial/testing-on-headless-ci
- name: Install Linux GUI dependencies
run: |
sudo apt-get update
sudo apt-get install -y xvfb
# Install Chinese fonts and locale support for i18n testing
sudo apt-get install -y fonts-noto-cjk fonts-wqy-zenhei language-pack-zh-hans
sudo locale-gen zh_CN.UTF-8
- name: Run tests
# E2E GUI tests with Electron on Linux require a virtual framebuffer, upgrade screen size from time to time.
run: xvfb-run --auto-servernum --server-args="-screen 0 2560x1440x24" pnpm run test
env:
CI: true
DISPLAY: :99
# Set Chinese locale for i18n testing
LANG: zh_CN.UTF-8
LC_ALL: zh_CN.UTF-8
timeout-minutes: 15
# Upload test artifacts (screenshots, logs)
- name: Upload test artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: test-artifacts
path: |
userData-test/logs/
userData-test/settings/
retention-days: 7
continue-on-error: true
codeql:
name: CodeQL Analysis
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: javascript-typescript
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3

7
.gitignore vendored
View file

@ -58,7 +58,8 @@ template/wiki/output/
template/wiki/settings/settings.json
# web build
.webpack
.webpack/
.vite/
out/
# testing and dev temp folders
@ -67,4 +68,8 @@ out/
deb2appimage_cache/
deb2appimage.json
userData-dev/
userData-test/
wiki-dev/
wiki-test/
*.tsbuildinfo
tsconfig.test.json.tsbuildinfo

1
.npmrc
View file

@ -2,3 +2,4 @@ strict-peer-dependencies=false
# This is a workaround for VSCode's Eslint extension not loading plugins correctly,
# see: https://github.com/pnpm/pnpm/issues/5447
public-hoist-pattern[]=*eslint*
node-linker=hoisted

View file

@ -3,11 +3,11 @@ function readPackage(pkg, context) {
delete pkg.optionalDependencies['registry-js'];
}
return pkg
return pkg;
}
module.exports = {
hooks: {
readPackage
}
}
readPackage,
},
};

19
.vscode/launch.json vendored
View file

@ -3,22 +3,6 @@
//
// 访: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"tasks": [
{
"type": "npm",
"script": "start:without-clean",
"group": "build",
"isBackground": true,
"problemMatcher": ["$ts-webpack-watch", "$ts-webpack-watch", "$ts-checker-eslint-webpack-watch"]
},
{
"type": "npm",
"script": "start",
"group": "build",
"isBackground": true,
"problemMatcher": ["$ts-webpack-watch", "$ts-webpack-watch", "$ts-checker-eslint-webpack-watch"]
}
],
"configurations": [
{
"name": "Debug Main Process",
@ -27,8 +11,7 @@
"args": ["start"],
"request": "launch",
"env": {
"NODE_ENV": "development",
"DEBUG_MAIN": "true"
"NODE_ENV": "development"
},
"type": "node",
"skipFiles": ["<node_internals>/**"]

11
.vscode/settings.json vendored
View file

@ -24,10 +24,19 @@
"tidgi",
"wouter"
],
"i18n-ally.sourceLanguage": "zh",
"i18n-ally.sourceLanguage": "zh-Hans",
"i18n-ally.localesPaths": "localization/locales",
"i18n-ally.namespace": false,
"i18n-ally.review.user.name": "{locale}/{namespaces}.json",
"i18n-ally.enabledFrameworks": ["i18next", "react"],
"i18n-ally.sortKeys": true,
"i18n-ally.keepFulfilled": true,
"i18n-ally.localeCountryMap": {
"en": "br", // show UK's flag instead of US's
"zh": "cn", // show Chinese flag for 'zh'
"ko": "ko" // show Korean flag for 'ko'
},
"i18n-ally.keySeparator": ".",
"prettier.printWidth": 160,
"i18n-ally.keystyle": "nested",
"editor.wordSeparators": "`~!@#%^&*()-=+[{]}\\|;:'\",.<>/?"

View file

@ -11,33 +11,17 @@
<hr>
# TOC
> Pronounce: Same as Tai Chi /ˌtaɪ ˈtʃiː/
🇬🇧 English | <a href="https://github.com/tiddly-gittly/TidGi-Desktop/blob/master/docs/readme/README.zh-CN.md">🇨🇳 简体中文</a>
<!-- toc -->
- [ShowCases And Demo](#showcases-and-demo)
- [Related Posts About TidGi](#related-posts-about-tidgi)
- [About TidGi-Desktop](#about-tidgi-desktop)
- [Download](#download)
- [Mobile App](#mobile-app)
- [Data Privacy Protection](#data-privacy-protection)
- [Differences](#differences)
- [Why Github?](#why-github)
- [Development](#development)
- [Credits](#credits)
- [Stargazers over time](#stargazers-over-time)
<!-- tocstop -->
---
🇬🇧 English | <a href="./docs/readme/README.zh-CN.md">🇨🇳 简体中文</a>
<div align="center">
[![GitHub Releases](https://img.shields.io/github/downloads/tiddly-gittly/TidGi-Desktop/latest/total?label=Download%20Latest%20Release&style=for-the-badge)](https://github.com/tiddly-gittly/TidGi-Desktop/releases/latest)
| [Download](https://github.com/tiddly-gittly/TidGi-Desktop/releases/latest) |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| [![GitHub Releases](https://img.shields.io/github/downloads/tiddly-gittly/TidGi-Desktop/latest/total?label=Download%20Latest%20Release&style=for-the-badge)](https://github.com/tiddly-gittly/TidGi-Desktop/releases/latest) |
| More: [Download](#download) |
More: [Download](#download)
</div>
## ShowCases And Demo
@ -53,20 +37,20 @@ More: [Download](#download)
<details>
| Load NodeJS Wiki |
| :-------------------------------------------------------: |
| Load NodeJS Wiki |
| :---------------------------------------------------------: |
| ![Screenshot of main-window](./docs/images/main-window.png) |
| Create Local Wiki | Clone Online Wiki |
| :-----------------------------------------------------------: | :---------------------------------------------------------------: |
| Create Local Wiki | Clone Online Wiki |
| :-------------------------------------------------------------: | :-----------------------------------------------------------------: |
| ![Screenshot of add-workspace](./docs/images/add-workspace.png) | ![Screenshot of clone-workspace](./docs/images/clone-workspace.png) |
| Translation, Preferences |
| :------------------------------------------------------------------------------------------------------------------------------------------------: |
| ![Screenshot of preference](./docs/images/preference.png) |
| Interactive code |
| Translation, Preferences |
| :--------------------------------------------------------------------------------------------------------------------------------------------------: |
| ![Screenshot of preference](./docs/images/preference.png) |
| Interactive code |
| ![Screen recording of zx-script in tiddlywiki](https://user-images.githubusercontent.com/3746270/133831500-ae91164c-7948-4de4-9a81-7017ed3b65c9.gif) |
| Community Plugin Library |
| Community Plugin Library |
| ![Screenshot of add-workspace](./docs/images/community-plugin-library.png) |
</details>
@ -114,8 +98,8 @@ It used to named TiddlyGit, means TiddlyWiki with easy Git backup, short for Tid
> For Windows MacOS normal user
| [Download](https://github.com/tiddly-gittly/TidGi-Desktop/releases/latest) |
| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| [Download](https://github.com/tiddly-gittly/TidGi-Desktop/releases/latest) |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| [![GitHub Releases](https://img.shields.io/github/downloads/tiddly-gittly/TidGi-Desktop/latest/total?label=Download%20Latest%20Release&style=for-the-badge)](https://github.com/tiddly-gittly/TidGi-Desktop/releases/latest) |
> For arch user

View file

@ -1,30 +0,0 @@
const feature = [
'--require-module ts-node/register',
'--require features/**/*.ts',
`--format progress-bar`,
'--format rerun:logs/@rerun.txt',
'--format usage:logs/usage.txt',
'--format message:logs/messages.ndjson',
'--publish-quiet',
].join(' ');
const cck = ['--require-module', 'ts-node/register', '--format', 'message'].join(' ');
const FORMATTERS_INCLUDE = ['attachments', 'data-tables', 'examples-tables', 'minimal', 'parameter-types', 'rules', 'stack-traces', '--publish-quiet'];
const htmlFormatter = [
`node_modules/@cucumber/compatibility-kit/features/{${FORMATTERS_INCLUDE.join(',')}}/*.feature`,
'--require-module',
'ts-node/register',
'--require',
`compatibility/features/{${FORMATTERS_INCLUDE.join(',')}}/*.ts`,
'--format',
'html:html-formatter.html',
'--publish-quiet',
].join(' ');
module.exports = {
default: feature,
// cck,
// htmlFormatter,
};

View file

@ -10,58 +10,126 @@ Explanation of our code can be found in the [Wiki](https://github.com/tiddly-git
<summary>To contribute, fork this repo, then clone it and setup development environment</summary>
First-Time Setup Commands
```shell
# First, clone the project:
# Clone the project that you forked
git clone https://github.com/YOUR_ACCOUNT/TidGi-Desktop.git
cd TidGi-Desktop
# Or maybe you are just using Github Desktop
# or GitKraken to clone this repo,
# and open it in your favorite code editor and terminal app
# switch to the nodejs version same as electron used version, other wise you may get
# Error: The module '/Users/linonetwo/Desktop/repo/TidGi-Desktop/node_modules/opencv4nodejs-prebuilt/build/Release/opencv4nodejs.node'
# was compiled against a different Node.js version using
# NODE_MODULE_VERSION 88. This version of Node.js requires
# NODE_MODULE_VERSION 93. Please try re-compiling or re-installing
# the module (for instance, using `npm rebuild` or `npm install`).
# See https://github.com/justadudewhohacks/opencv4nodejs/issues/401#issuecomment-463434713 if you still have problem rebuild opencv for @nut-tree/nut-js
# Switch to the correct Node.js version (recommended)
nvm use
# install the dependencies
# Install dependencies
pnpm install
npm i
# Run development mode
# You can see webpack error messages in http://localhost:9000/
npm start
# Build for production
npm run package
# Full setup with all checks
pnpm start:init
```
### Publish
Development Workflow
Add a tag like `vx.x.x` to a commit, and push it to the origin, Github will start building App for all three platforms.
1. First run: Use `pnpm start:init` to ensure everything is properly set up
2. Daily development: Use `pnpm run start:dev` for faster iteration
3. After pulling changes: Run `pnpm run build:plugin` if plugins were updated
4. Before committing: Run `pnpm run lint` and `pnpm run test`
After Github Action completed, you can open Releases to see the Draft release created by Github, add some comment and publish it.
Note: You can see webpack error messages at console during development.
</details>
## Package.json Scripts
### Development Scripts
#### Quick Development (Recommended for daily use)
```shell
pnpm run start:dev
```
This is the fastest way to start development. It directly launches the Electron app without running the full setup process, making it ideal for iterative development.
#### Full Development Setup
```shell
pnpm start
```
This runs the complete setup process including:
- `clean` - Clears build artifacts and development folders
- `init:git-submodule` - Updates git submodules
- `build:plugin` - Compiles TiddlyWiki plugins
- `start:dev` - Launches the Electron application
#### Debug Variants
```shell
pnpm run start:dev:debug-worker # Debug worker threads
pnpm run start:dev:debug-main # Debug main process
pnpm run start:dev:debug-react # Debug React renderer, react-devtool will be available in devtools
```
#### Show electron-packager debug logs
If you want to see detailed logs from electron-packager during packaging, set the environment variable `DEBUG=electron-packager`:
- Linux/macOS:
```shell
DEBUG=electron-packager pnpm run start:dev
```
- Windows PowerShell:
```shell
$env:DEBUG="electron-packager"; pnpm run start:dev
```
This will print verbose debug information from electron-packager to help diagnose packaging issues.
### Build & Package Scripts
```shell
pnpm run build:plugin # Compile TiddlyWiki plugins only
pnpm run package # Package for production
pnpm run package:dev # Package for testing (with NODE_ENV=test)
pnpm run make # Create distributable packages
```
### Testing Scripts
```shell
pnpm run test # Run all tests (unit + E2E)
pnpm run test:unit # Run Jest unit tests only
pnpm run test:e2e # Run Cucumber E2E tests only
```
### E2E Testing
E2E tests require the packaged application to run. Key points:
- Tests run against the packaged application to simulate real user scenarios
- Uses Playwright + Cucumber for browser automation
- Test reports are saved to `logs/` directory
### Utility Scripts
```shell
pnpm run clean # Clean build artifacts and temp folders
pnpm run clean:cache # Clear webpack and build caches, this can fix some error.
pnpm run lint # Run ESLint
pnpm run lint:fix # Run ESLint with auto-fix
```
### First-Time Setup Commands
## How to add dependency that used in a worker_thread
For example: `tiddlywiki`
1. `npm i tiddlywiki`
1. `pnpm i tiddlywiki`
1. Add `ExternalsPlugin` in webpack.plugins.js (maybe optional for some deps, tiddlywiki needs this because its custom `require` can't require things that is bundled by webpack. `dugite` don't need this step)
1. Add a `await fs.copy(path.join(projectRoot, 'node_modules/tiddlywiki')` in `scripts/afterPack.js` , to copy things to resource folder, that is outside of asar, so it can be used by the worker_thread in electron
@ -83,6 +151,9 @@ Some library doesn't fit electron usage, we move their code to this repo and mod
- When not installed in package.json, when make release, forge will throw error `An unhandled rejection has occurred inside Forge: Error: ENOENT: no such file or directory, stat '/Users/linonetwo/Desktop/repo/TiddlyGit-Desktop/node_modules/app-path/main'`
- [externalApp](https://github.com/desktop/desktop/blob/742b4c44c39d64d01048f1e85364d395432e3413/app/src/lib/editors/lookup.ts): This was used by [Github Desktop](https://github.com/desktop/desktop) to lookup the location of editors like VSCode, we use it in context menu to "open in default text editor"
- [sqlite-vec](https://github.com/asg017/sqlite-vec): The path from its method `getLoadablePath` maybe incorrect after electron app packaged. (It will be in `.webpack/main/index.js` in the dist folder instead of in `node_modules/sqlite-vec` folder.)
- Still need to install its `optionalDependencies` like `sqlite-vec-darwin-x64` in package.json
## Don't upgrade these dependency
### pure ESM
@ -103,25 +174,14 @@ Electron forge webpack don't support pure ESM yet
TBD
## Testing
[Testing Guide](./Testing.md)
## Logs
Are in `userData-dev/logs/TidGi-xxxx.log`, includes all `"level":"debug"` debug logs and `"level":"error"` errors.
## FAQ
### `Uncaught ReferenceError: require is not defined`
Or `Uncaught TypeError: Cannot read properties of undefined (reading 'call') at __webpack_require__ (index.js:4317:33)`
`pnpm run clean:cache` can fix this.
### Electron download slow
Add `.npmrc` on this project (sometimes the one at home folder is not working).
```npmrc
electron-mirror=https://registry.npmmirror.com/-/binary/electron/
electron_custom_dir={{ version }}
```
and run `node node_modules/electron/install.js` manually.
### Preparing native dependencies `Error: ENOENT: no such file or directory, stat 'xxx/node_modules/.pnpm/node_modules/@types/lodash-es'`
run `rm 'xxx/node_modules/.pnpm/node_modules/@types/lodash-es'` fixes it. Maybe pnpm install gets interrupted, and make a file-like symlink, get recognized as binary file. Remove it will work.
[ErrorDuringStart](./ErrorDuringStart.md)

View file

@ -1,5 +1,52 @@
# Deal with error when pnpm start
## `Uncaught ReferenceError: require is not defined`
Or `Uncaught TypeError: Cannot read properties of undefined (reading 'call') at __webpack_require__ (index.js:4317:33)`
`pnpm run clean:cache` can fix this.
## Electron download slow
Add `.npmrc` on this project (sometimes the one at home folder is not working).
```npmrc
electron-mirror=https://registry.npmmirror.com/-/binary/electron/
electron_custom_dir={{ version }}
```
and run `node node_modules/electron/install.js` manually.
## Preparing native dependencies `Error: ENOENT: no such file or directory, stat 'xxx/node_modules/.pnpm/node_modules/@types/lodash-es'`
Or `[FAILED: ENOENT: no such file or directory, stat 'C:\Users\linonetwo\Documents\repo-c\TidGi-Desktop\node_modules\.pnpm\node_modules\@radix-ui\react-compose-refs']`
Remove it by run `rm 'xxx/node_modules/.pnpm/node_modules/@types/lodash-es'` fixes it. Maybe pnpm install gets interrupted, and make a file-like symlink, get recognized as binary file. Remove it will work.
## An unhandled rejection has occurred inside Forge about node-abi
Solution: Update `@electron/rebuild` to latest version:
```shell
pnpm up @electron/rebuild@latest
```
## Fetch failed at fetchAvailableUpdates
We use [electron-chrome-web-store](https://github.com/samuelmaddock/electron-browser-shell/blob/master/packages/electron-chrome-web-store/README.md) to load react dev tools, so you need to add `https://clients2.google.com/service/update2/crx` to your Clash/Proxifier list. May need to enable system proxy and TUN mode or so.
## Finalizing package postPackage error
Add `DEBUG=electron-packager` to package, like:
`cross-env NODE_ENV=production DEBUG=electron-packager electron-forge make --platform=win32 --arch=x64`
<https://github.com/electron/forge/issues/3645>
Usually you need to fix [scripts\afterPack.js](../scripts/afterPack.js)
If use pnpm, need to copy dependency binary from `.pnpm` folder, but if add `node-linker=hoisted` to [.npmrc](../.npmrc) then we can simply copy from node_modules folder.
## no such file or directory dprint
> no such file or directory, stat 'TiddlyGit-Desktop/node_modules/.pnpm/node_modules/@dprint/darwin-arm64'
@ -45,3 +92,115 @@ Solution:
```sh
node_modules/.bin/electron-rebuild -f -w better-sqlite3
```
## During test, The module 'node_modules\better-sqlite3\build\Release\better_sqlite3.node' was compiled against a different Node.js version using
```log
NODE_MODULE_VERSION 135. This version of Node.js requires
NODE_MODULE_VERSION 127. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).
```
(The number above is larger)
Don't need to recompile, nodejs and electron have different NODE_MODULE_VERSION. You need to run test using electron as nodejs.
```sh
cross-env ELECTRON_RUN_AS_NODE=true ./node_modules/.bin/electron ./node_modules/vitest/vitest.mjs run
```
### 测试运行有中文乱码 `鈳幆鈳幆鈳幆鈳幆鈳幆鈳幆鈳幆鈳幆鈳幆鈳幆鈳幆鈳幆[4/4]鈳?`
救急可以用 `chcp 65001 && pnpm run test:unit`如果有空重启电脑则在时区设置里找到「系统区域设置」里勾选「Unicode Beta版」重启即可。
## Error: The module '/Users/linonetwo/Desktop/repo/TidGi-Desktop/node_modules/opencv4nodejs-prebuilt/build/Release/opencv4nodejs.node'
```log
was compiled against a different Node.js version using
NODE_MODULE_VERSION 127. This version of Node.js requires
NODE_MODULE_VERSION 135. Please try re-compiling or re-installing
the module (for instance, using `npm rebuild` or `npm install`).
```
(The number above is smaller)
Don't use `npm rebuild` or `npm install`, it doesn't works, it will still build for nodejs. We need to build with electron:
```sh
./node_modules/.bin/electron-rebuild
```
See <https://github.com/justadudewhohacks/opencv4nodejs/issues/401#issuecomment-463434713> if you still have problem rebuild opencv for @nut-tree/nut-js
## Command failed with exit code 1
When you see an error like:
```log
ELIFECYCLE Command failed with exit code 1.
```
This is a generic error and the real cause is usually shown earlier in the log. Sometimes, the actual error is hidden. You can set `DEBUG=electron-packager` to get more detailed logs (see [Show electron-packager debug logs](./Development.md#show-electron-packager-debug-logs)).
For example, after setting the debug variable, you may see:
```log
An unhandled exception has occurred inside Forge:
listen EACCES: permission denied 0.0.0.0:9000
Error: listen EACCES: permission denied 0.0.0.0:9000
```
This means the port 9000 is not accessible, possibly due to permission issues or the port already being in use. Try disable some startup service and restart computer. Some app may occupies port for its own use on startup.
## RangeError: Maximum call stack size exceeded at cloneObjectDeep
```js
const esbuild = require('esbuild');
//...
implementation: esbuild,
```
If tried to add this to `esbuildLoaderRule` will cause this error. The object contains an internal reference chain (`.default.default`) that triggers recursion when webpack-merge/clone-deep attempts to merge it.
## Error: Can't resolve 'os' in
```log
@ ./src/services/libs/i18n/i18next-electron-fs-backend.ts 3:0-44 147:10-22 172:10-22
@ ./src/services/libs/i18n/renderer.ts 4:0-77 8:20-37
@ ./src/renderer.tsx 20:0-65 36:5-21
ERROR in ./node_modules/winston/dist/winston/transports/stream.js 26:9-22
Module not found: Error: Can't resolve 'os' in 'C:\Users\linonetwo\Documents\repo-c\TidGi-Desktop\node_modules\winston\dist\winston\transports'
BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.
If you want to include a polyfill, you need to:
- add a fallback 'resolve.fallback: { "os": require.resolve("os-browserify/browser") }'
- install 'os-browserify'
If you don't want to include a polyfill, you can use an empty module like this:
resolve.fallback: { "os": false }
```
Usually because you import the server-side `logger` in renderer process code. You have to use `console` or add new transport in [rendererTransport.ts](src/services/libs/log/rendererTransport.ts).
## Startup stalled at `Launching dev servers for renderer process code`
Hangs here doesn't mean it stop working, just wait around 2 mins. Webpack dev server is quite slow, but will finally finished.
If you are not sure, try `pnpm run start:dev:debug-webpack`, which will also enables `WebpackBar` plugin.
### Why not using Vite?
1. Wait for <https://github.com/vitejs/vite/pull/3932> to replace `ThreadsPlugin`
2. Need to replace `inversify-inject-decorators` and `typeorm` that uses decorator first
## Error: ENOTDIR, not a directory at createError or supportedLanguages.json: ENOENT
May be `src/constants/paths.ts` have wrong value of `__dirname` or `process.resourcesPath` after package, like being `C:\Users\linonetwo\Documents\repo-c\TidGi-Desktop\out\TidGi-win32-x64\resources\app.asar\xxx`
Check `src/constants/appPaths.ts` and `src/constants/paths.ts`
## error: Your local changes to the following files would be overwritten by checkout
Clean up the local `template/wiki` folder. You can simply "Discard change" of that path, using github desktop.

5
docs/Publish.md Normal file
View file

@ -0,0 +1,5 @@
# Publish
Add a tag like `vx.x.x` to a commit, and push it to the origin, Github will start building App for all three platforms.
After Github Action completed, you can open Releases to see the Draft release created by Github, add some comment and publish it.

353
docs/Testing.md Normal file
View file

@ -0,0 +1,353 @@
# Testing Guide
Testing guide for TidGi-Desktop using Vitest + React Testing Library for unit tests and Playwright + Cucumber for E2E tests.
## Quick Start
```bash
# Run all tests
pnpm test
# Run unit tests only
pnpm test:unit
# Run E2E tests (requires prepare packaged app, but only when you modify code in ./src)
pnpm run test:prepare-e2e
# (When only modify tests in ./features folder, and you have packaged app before, only need to run this.)
pnpm test:e2e
# Or run a specific e2e test by using same `@xxx` as in the `.feature` file.
pnpm test:e2e --tags="@smoke"
# Or run a single e2e
pnpm test:e2e --name "Wiki-search tool usage"
# Run with coverage
pnpm test:unit -- --coverage
# Run a single test file to reduce execution time when fixing an issue.
pnpm test:unit src/services/agentDefinition/__tests__/responsePatternUtility.test.ts
# Start packed e2e electron app manually to see what's going on as a human (AI agent is not allowed to run this)
cross-env NODE_ENV=test pnpm dlx tsx ./scripts/start-e2e-app.ts
```
Except for above parameters, AI agent can't use other parameters, otherwise complex shell command usage or parameters will require human approval and may not passed.
## Project Setup
Test Configuration: TypeScript-first with `vitest.config.ts`
- Unit tests: Vitest + React Testing Library + jsdom
- E2E tests: Playwright + Cucumber
- Coverage: HTML reports in `coverage/`
Related file structure:
```tree
src/
├── __tests__/ # Global test setup & utilities
├── components/*/
│ └── __tests__/ # Component tests
└── services/*/
└── __tests__/ # Service tests
features/ # E2E tests
├── *.feature # Gherkin scenarios
├── stepDefinitions/ # Playwright implementations
└── supports/ # Test utilities
out/ # `test:prepare-e2e` Bundled production app to test
userData-test/ # User setting folder created during `test:e2e`
userData-dev/ # User setting folder created during `start:dev`
```
## Writing Unit Tests
Code here are truncated or shorten. You should always read actuarial test file to learn how to write.
### Component Testing Best Practices
```typescript
// Use semantic queries and user-event for realistic interactions
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
describe('WorkspaceSelector', () => {
it('should switch to Help page when clicking Help workspace', async () => {
const user = userEvent.setup();
render(<WorkspaceSelector />);
// Wait for async content to load
expect(await screen.findByText('Guide Page Content')).toBeInTheDocument();
// Use realistic user interactions
const helpText = await screen.findByText('Help');
await user.click(helpText);
// Assert on user-visible changes
expect(await screen.findByText('Help Page Content')).toBeInTheDocument();
});
});
```
### Effective Mocking
```typescript
// Mock complex components simply
vi.mock('../ComplexComponent', () => ({
default: () => <div data-testid='complex-component'>Mocked Component</div>,
}));
// Test-specific data for current test file
const workspacesSubject = new BehaviorSubject([
{ id: 'test-workspace', name: 'Test Wiki' },
]);
// Override global observables for this test
Object.defineProperty(window.observables.workspace, 'workspaces$', {
value: workspacesSubject.asObservable(),
writable: true,
});
```
### Global Mock Management
Centralize common mocks in `src/__tests__/__mocks__/` directory, and import them in `src/__tests__/setup-vitest.ts`:
- Services from window APIs (`window.service`, `window.remote`, `window.observables`) and container APIs (`@services/container`) are now mocked in `src/__tests__/__mocks__/window.ts``services-container.ts`
- Common libraries (`react-i18next` in `react-i18next.ts`, logger in `services-log.ts`)
Most of services should be in these mock files. Only mock specific small set of service API in new test files if needed.
Override in test files only when you need test-specific data:
```typescript
// Only override what's specific to this test
Object.defineProperty(window.observables.workspace, 'workspaces$', {
value: testSpecificWorkspaces$.asObservable(),
writable: true,
});
```
This keeps tests focused and reduces duplication across test files.
### Async Testing Patterns
```typescript
// Use findBy* for elements that appear asynchronously
expect(await screen.findByText('Loading complete')).toBeInTheDocument();
// Use waitForElementToBeRemoved for disappearing elements
await waitForElementToBeRemoved(() => screen.queryByText('Loading...'));
// Avoid unnecessary waitFor - prefer findBy*
// ❌ Don't do this
await waitFor(() => {
expect(screen.getByText('Content')).toBeInTheDocument();
});
// ✅ Do this instead
expect(await screen.findByText('Content')).toBeInTheDocument();
// Handle async component initialization to avoid act(...) warnings
// ✅ Create helper that waits for async loading
const renderComponent = async () => {
const result = render(<AsyncComponent />);
await waitFor(() => {
expect(screen.queryByText('Loading')).not.toBeInTheDocument();
});
return result;
};
// ✅ Use in tests
it('should test feature after loading', async () => {
await renderComponent();
// Now safe to test without act warnings
});
// ✅ For loading state tests, wait after assertion
it('should show loading initially', async () => {
render(<AsyncComponent />);
expect(screen.getByText('Loading')).toBeInTheDocument();
// Wait for completion to prevent warnings in subsequent async updates
await waitFor(() => {
expect(screen.queryByText('Loading')).not.toBeInTheDocument();
});
});
```
## Writing E2E Tests
### Feature File Example
```gherkin
# features/agent.feature
Feature: Agent Workflow
Background:
Given I launch the TidGi application
And I wait for the page to load completely
@agent
Scenario: Complete agent workflow
# Use generic steps for common UI interactions
When I click on a "settings button" element with selector "#open-preferences-button"
When I switch to "preferences" window
When I type "TestProvider" in "provider name input" element with selector "[data-testid='new-provider-name-input']"
# ... more generic steps
Then I should see 4 messages in chat history
```
### Step Definitions Architecture
The E2E testing framework uses a World-based architecture with Playwright + Cucumber:
```typescript
// features/stepDefinitions/application.ts - Generic application steps
export class ApplicationWorld {
app: ElectronApplication | undefined;
// ...
}
// Generic step definitions you usually must reuse.
When('I click on a(n) {string} element with selector {string}', async function(elementComment: string, selector: string) {
// ...
});
// Don't define specific step only for you own use, that would be selfish.
When('(Dont do this) I click on a specific button and wait for 2 seconds.', async function() {
// Strictly forbidden.
});
```
### Key E2E Testing Patterns
1. Window Management: Use `getWindow()` with retry logic for reliable window switching
2. Generic Steps: Reusable steps for common UI interactions with descriptive selectors
3. Domain Steps: Specific steps for complex workflows (like agent conversations)
4. Mock Services: Use tagged cleanup for feature-specific resources
5. Streaming Support: Special handling for real-time updates in chat interfaces
6. **Don't think about adding new step definitions** or **change timeout duration**, unless human ask you to do. You should always reuse existing steps, and debug the fundamental reason that causes timeout. Timeout usually because of expected element not percent.
7. If you forget to run `pnpm run test:prepare-e2e` after modify code in `./src` folder, you may find expected elements missing.
8. Usually don't need to add wait time, because most check already will wait for a while. Even add wait, can't be more than 0.2s.
## Testing Library Best Practices
### Query Priority (use in this order)
1. Accessible queries - `getByRole`, `getByLabelText`, `getByPlaceholderText`
2. Semantic queries - `getByAltText`, `getByTitle`
3. Test IDs - `getByTestId` (when accessibility queries aren't practical)
### Async Patterns
- Use `findBy*` instead of `getBy*` + `waitFor`
- Use `user-event` instead of `fireEvent` for realistic interactions
- Wait for initial async state in `beforeEach` to avoid act() warnings
### Common Antipatterns to Avoid
```typescript
// ❌ Testing implementation details
expect(component.state.isLoading).toBe(false);
// ✅ Testing user-visible behavior
expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
// ❌ Using act() wrapper unnecessarily
act(() => {
fireEvent.click(button);
});
// ✅ Using proper async testing
const user = userEvent.setup();
await user.click(button);
// ❌ Not handling async component initialization
render(<AsyncComponent />);
expect(screen.getByText('Content')).toBeInTheDocument(); // May cause act warnings
// ✅ Wait for async initialization to complete
const renderAsync = async () => {
const result = render(<AsyncComponent />);
await waitFor(() => expect(screen.queryByText('Loading')).not.toBeInTheDocument());
return result;
};
```
For complete Testing Library guidance, see [Testing Library docs](https://testing-library.com/docs/queries/about).
### Viewing e2e tests
We check `isTest` when `xxxWindow.show()`, so it won't popup while testing. You can clear the desktop windows so you can see it.
### Log
When AI is fixing issues, you can let it add more logs for troubleshooting, and then show the [latest test log files](../userData-test/logs) or [dev log files](../userData-dev/logs) to the AI. Of course, it's best to run tests using `pnpm test:unit`, as it's fast and can be automated by AI without manual intervention. The logs should also be visible in the test, just change the mock of [logger](../src/__tests__/__mocks__/services-log.ts) to use console log, and run a single test to get minimal logs.
If you want to send frontend log to the log file, you can't directly use `import { logger } from '@services/libs/log';` you need to use `void window.service.native.log('error', 'Renderer: xxx', { ...additionalMetadata });`.
Otherwise you will get [Can't resolve 'os' error](./ErrorDuringStart.md)
## User profile
When running tests — especially E2E or other tests that start an Electron instance — the test runner will set Electron's `userData` to `userData-test`. This ensures the test process uses a separate configuration and data directory from any development or production TidGi instance, and prevents accidental triggering of Electron's single-instance lock.
- `src/constants/appPaths.ts`: in test mode we call `app.setPath('userData', path.resolve(sourcePath, '..', 'userData-test'))` to redirect settings and cache.
- `src/helpers/singleInstance.ts`: the main process uses `app.requestSingleInstanceLock()` to enforce single-instance behavior; without a separate `userData` directory, a running local TidGi could conflict with test instances and cause one of them to exit.
For this reason, test workflows in this project (for example when running `pnpm test:e2e` or CI integration tests) need to do with `cross-env NODE_ENV=test` so it creates isolate state in `userData-test`.
## Errors
### close timed out after 10000ms / FILEHANDLE (unknown stack trace)
This happens because Electron/Vitest child processes do not inherit the ELECTRON_RUN_AS_NODE environment variable, so resources cannot be cleaned up and handles leak.
Do not set `ELECTRON_RUN_AS_NODE` in `vitest.config.ts` via `process.env.ELECTRON_RUN_AS_NODE = 'true'` — this only affects the main process, not child processes.
Always use cross-env in your test script. For example:
`cross-env ELECTRON_RUN_AS_NODE=1 pnpm exec ./node_modules/.bin/electron ./node_modules/vitest/vitest.mjs run`
Or run manually in shell: `$env:ELECTRON_RUN_AS_NODE=1; pnpm run test:unit`
We use `ELECTRON_RUN_AS_NODE` to solve native modules (like better-sqlite3) being compiled for the wrong Node.js version, see the section in [ErrorDuringStart.md](./ErrorDuringStart.md#during-test-the-module-node_modulesbetter-sqlite3buildreleasebetter_sqlite3node-was-compiled-against-a-different-nodejs-version-using).
#### Module did not self-register: '/home/runner/work/TidGi-Desktop/TidGi-Desktop/node_modules/better-sqlite3/build/Release/better_sqlite3.node'
May needs `pnpm exec electron-rebuild -f -w better-sqlite3`.
### An update to Component inside a test was not wrapped in act(...)
This warning occurs when React components perform asynchronous state updates during test execution. Common causes:
- Components with `useEffect` that fetch data on mount
- Async API calls that update component state
- Timers or intervals that trigger state changes
**Solution**: Wait for async operations to complete using helper functions:
```typescript
// Create async render helper
const renderAsyncComponent = async () => {
const result = render(<AsyncComponent />);
// Wait for loading to complete
await waitFor(() => {
expect(screen.queryByText('Loading')).not.toBeInTheDocument();
});
return result;
};
// Use in tests
it('should test feature', async () => {
await renderAsyncComponent();
// Now safe to interact without warnings
});
```
Avoid explicitly using `act()` - React Testing Library handles most cases automatically when using proper async patterns.
### E2E test open production app
See User profile section above, we need to set `NODE_ENV` as `test` to open with correct profile.
This is done by using `EnvironmentPlugin` in [webpack.plugins.js](../webpack.plugins.js). Note that EsbuildPlugin's `define` doesn't work, it won't set env properly.

View file

@ -13,7 +13,7 @@ Add your language, make it looks like:
"ja": "日本語",
"ru": "русский",
"vi": "Tiếng Việt",
"zh_CN": "简中"
"zh-Hans": "汉字"
}
```

View file

@ -1,4 +1,3 @@
<!-- Exported from TiddlyWiki at 00:22, 19th 七月 2023 -->
# TidGi development environment configuration (Linux)
@ -17,10 +16,9 @@ Download link: https://github.com/BeyondDimension/SteamTools
### Certificate verification
- firefox suggests that there is a potential security problem with the connection:
- Settings->Privacy and Security->Certificates->View Certificates->Certificate Authorities, import `/home/username/.local/share/Steam++/SteamTools.Certificate`, and check the box of "Trust this certificate authority to identify the website! Certificate`, check the box "Trust this certificate authority to identify the site".
- Settings->Privacy and Security->Certificates->View Certificates->Certificate Authorities, import `/home/username/.local/share/Steam++/SteamTools.Certificate`, and check the box of "Trust this certificate authority to identify the website! Certificate`, check the box "Trust this certificate authority to identify the site".
- SSL certificate problem with git:
- You need to turn off git's certificate verification: `git config --global http.sslverify false`.
- You need to turn off git's certificate verification: `git config --global http.sslverify false`.
### 2. nvm
@ -69,8 +67,8 @@ git config --global user.email "user@outlook.com"
Authentication for Git push
* Username: github username
* Password: github -> setting -> Developer settings -> Personal access tokens
- Username: github username
- Password: github -> setting -> Developer settings -> Personal access tokens
### 6. vscode

View file

@ -15,10 +15,10 @@
### 证书验证
- firefox提示连接有潜在的安全问题
- 设置->隐私与安全->证书->查看证书->证书颁发机构,导入`/home/username/.local/share/Steam++/SteamTools.Certificate`,勾选“信任由此证书颁发机构来标识网站”
- firefox提示连接有潜在的安全问题
- 设置->隐私与安全->证书->查看证书->证书颁发机构,导入`/home/username/.local/share/Steam++/SteamTools.Certificate`,勾选“信任由此证书颁发机构来标识网站”
- git操作提示SSL certificate problem
- 需要关闭git的证书验证`git config --global http.sslverify false`
- 需要关闭git的证书验证`git config --global http.sslverify false`
## 2. nvm
@ -67,8 +67,8 @@ git config --global user.email "user@outlook.com"
Git push时的鉴权
* Usernamegithub用户名
* Passwordgithub -> setting -> Developer settings -> Personal access tokens
- Usernamegithub用户名
- Passwordgithub -> setting -> Developer settings -> Personal access tokens
## 6. vscode

View file

@ -0,0 +1,171 @@
# AgentInstance and the plugin-based workflow
This document explains how an agentInstance invokes a handler and how logic is composed via plugins to enable strategy-like processing. It covers message persistence, streaming updates, tool calling, and second-round handoff.
## Overview
- Entry: `IAgentInstanceService.sendMsgToAgent` receives user input.
- Orchestrator: `basicPromptConcatHandler` drives prompt concatenation, AI calls, and plugin hooks.
- Plugins: `createHooksWithPlugins` attaches plugins to unified hooks with shared context, enabling decoupled, replaceable strategies.
- Data: message model `AgentInstanceMessage`, status model `AgentInstanceLatestStatus`.
### Handler selection and registration
- Source of handlerID: prefer the instances handlerID, fallback to the agent definitions handlerID (see `src/pages/Agent/store/agentChatStore/actions/agentActions.ts#getHandlerId` and the preferences hook `useHandlerConfigManagement.ts`).
- Backend registration: in `AgentInstanceService.initialize()`, `registerBuiltinHandlers()` registers `basicPromptConcatHandler` under the ID `basicPromptConcatHandler`; `initializePluginSystem()` registers built-in plugins.
- Runtime selection: inside `sendMsgToAgent()`, the handler is fetched from `this.agentHandlers` by agentDef.handlerID and started as an async generator `const generator = handler(handlerContext)`, then iterated with `for await (const result of generator)`.
Related code:
- [index.ts](../../src/services/agentInstance/index.ts): `initialize()`, `registerBuiltinHandlers()`, `sendMsgToAgent()`
- [basicPromptConcatHandler.ts](../../src/services/agentInstance/buildInAgentHandlers/basicPromptConcatHandler.ts)
## Sequence
```mermaid
sequenceDiagram
autonumber
participant User as User
participant AISvc as IAgentInstanceService
participant Handler as basicPromptConcatHandler
participant Hooks as Plugins(Hooks)
participant API as External API
User->>AISvc: sendMsgToAgent(text,file)
AISvc-->>Handler: append to agent.messages
Handler->>Hooks: userMessageReceived
Hooks-->>AISvc: saveUserMessage / debounceUpdateMessage
Handler->>Hooks: agentStatusChanged(working)
loop generation and streaming updates
Handler->>AISvc: concatPrompt(handlerConfig, messages)
AISvc-->>Handler: flatPrompts
Handler->>API: generateFromAI(flatPrompts)
API-->>Handler: update(content)
Handler->>Hooks: responseUpdate(update)
Hooks-->>AISvc: debounceUpdateMessage
end
API-->>Handler: done(final content)
Handler->>Hooks: responseComplete(done)
alt plugin requests next round
Hooks-->>Handler: actions.yieldNextRoundTo = self
Handler->>Handler: append messages and continue flow
else return to user
Handler-->>AISvc: completed(final)
end
```
## Key design points
### 1. Event-driven strategy composition
`createHooksWithPlugins` exposes unified hooks: `processPrompts`, `userMessageReceived`, `agentStatusChanged`, `responseUpdate`, `responseComplete`, `toolExecuted`.
Plugins subscribe as needed and compose different strategies without changing the main flow.
Plugin registration and wiring:
- At app init, `initializePluginSystem()` registers built-in plugins to a global registry.
- For each round, `createHooksWithPlugins(handlerConfig)` creates a fresh hooks instance and attaches plugins per config.
- `responseConcat()` and `promptConcat` also look up `builtInPlugins` and run plugin logic (e.g., `postProcess`) with a dedicated context.
Stateless plugins requirement:
- Plugins must be stateless. Do not persist cross-round or cross-session state inside closures.
- All state must travel through `context` (e.g., `handlerContext.agent.messages`, `metadata`).
- Plugins may be registered to multiple hooks across conversations and then discarded; internal mutable state risks races and contamination.
### 2. Messages as the source of truth
User, assistant, and tool result messages are all `AgentInstanceMessage`.
`duration` limits how many subsequent rounds include a message in context.
UI and persistence coordinate via `saveUserMessage` and `debounceUpdateMessage`.
Persistence and UI updates:
User messages: `messageManagementPlugin.userMessageReceived` persists via `IAgentInstanceService.saveUserMessage`, pushes into `handlerContext.agent.messages`, and calls `debounceUpdateMessage` to notify UI.
Streaming updates: `responseUpdate` maintains an in-progress assistant message (`metadata.isComplete=false`) with debounced UI updates.
Finalization: `responseComplete` persists the final assistant message and updates UI once more.
Tool results: `toolExecuted` persists messages with `metadata.isToolResult` and sets `metadata.isPersisted` to avoid duplicates.
### 3. Second-round handoff and control
Plugins may set `actions.yieldNextRoundTo = 'self'` in `responseComplete` to trigger another LLM round immediately.
The handler stops after reaching retry limits and returns the final result.
concatPrompt and prompt delivery:
`AgentInstanceService.concatPrompt` exposes an observable stream for prompt assembly. The handler uses `getFinalPromptResult` to obtain final prompts before calling the external API.
## Example plugins
### messageManagementPlugin
Responsibilities:
Persist user messages in `userMessageReceived` and sync UI.
Manage streaming assistant message in `responseUpdate`; persist final content in `responseComplete`.
Update status in `agentStatusChanged`.
Persist tool results in `toolExecuted` and mark as persisted.
Notes:
Update `handlerContext.agent.messages` in place for immediate UI rendering.
Use debounced updates to reduce re-renders.
Mark streaming messages with `metadata.isComplete`.
### wikiSearchPlugin
Responsibilities:
Inject available wiki workspaces and tool list in `processPrompts`.
On `responseComplete`, detect tool calls, execute, produce `isToolResult` message with `duration=1`.
Set `actions.yieldNextRoundTo = 'self'` to continue immediately with tool outputs.
Notes:
Validate parameters with zod.
Use messages as the carrier for tool I/O.
Set `duration=1` for tool-call assistant messages to economize context.
Tool calling details:
Parse: detect tool-call patterns via `matchToolCalling` in `responseComplete`.
Validate & execute: validate with zod, then `executeWikiSearchTool` uses workspace and wiki services to fetch results.
History: create an `isToolResult` message (`role: 'user'`, `duration=1`) for the next round; report via `hooks.toolExecuted.promise(...)` so messageManagementPlugin persists and notifies UI.
Loop: set `actions.yieldNextRoundTo='self'` to continue another round using tool outputs.
## Flow
```mermaid
flowchart TD
A[User input] --> B[sendMsgToAgent]
B --> C[Message enqueued to agent.messages]
C --> D[userMessageReceived persist + UI]
D --> E[agentStatusChanged = working]
E --> F[concatPrompt generate prompts]
F --> G[generateFromAI streaming]
G --> H[responseUpdate update UI]
H --> I{responseComplete}
I -->|tool call| J[Execute tool and write tool result message]
J --> K[actions.yieldNextRoundTo=self]
K --> F
I -->|plain reply| L[Complete and return to UI]
```
## Related code
- [basicPromptConcatHandler.ts](../../src/services/agentInstance/buildInAgentHandlers/basicPromptConcatHandler.ts)
- [messageManagementPlugin.ts](../../src/services/agentInstance/plugins/messageManagementPlugin.ts)
- [wikiSearchPlugin.ts](../../src/services/agentInstance/plugins/wikiSearchPlugin.ts)
- [interface.ts](../../src/services/agentInstance/interface.ts)
## Benefits
Loose coupling: the main flow stays unchanged while capabilities are pluggable.
Testability: plugins can be unit-tested and integration-tested with the handler.
Evolvability: new capabilities land as new plugins and hook subscriptions.
## Notes
Avoid double persistence; use `metadata` flags for dedup.
Ensure idempotency and robust error handling; prefer UI updates over persistence when degrading.
Control retry limits and exit conditions to avoid infinite loops.

View file

@ -59,25 +59,25 @@ async function loadFileContentHandler(request: Request) {
}
}
try {
try {
/**
* This function is called for every view, but seems register on two different view will throw error, so we check if it's already registered.
*/
if (!view.webContents.session.protocol.isProtocolHandled('filefix')) {
/**
* This function is called for every view, but seems register on two different view will throw error, so we check if it's already registered.
* Electron's bug, file protocol is not handle-able, won't get any callback. But things like `filea://` `filefix` works.
*/
if (!view.webContents.session.protocol.isProtocolHandled('filefix')) {
/**
* Electron's bug, file protocol is not handle-able, won't get any callback. But things like `filea://` `filefix` works.
*/
view.webContents.session.protocol.handle('filefix', loadFileContentHandler);
}
/**
* Alternative `open://` protocol for a backup if `file://` doesn't work for some reason.
*/
if (!view.webContents.session.protocol.isProtocolHandled('open')) {
view.webContents.session.protocol.handle('open', loadFileContentHandler);
}
} catch (error) {
logger.error(`Failed to register protocol: ${(error as Error).message}`, { function: 'handleViewFileContentLoading' });
view.webContents.session.protocol.handle('filefix', loadFileContentHandler);
}
/**
* Alternative `open://` protocol for a backup if `file://` doesn't work for some reason.
*/
if (!view.webContents.session.protocol.isProtocolHandled('open')) {
view.webContents.session.protocol.handle('open', loadFileContentHandler);
}
} catch (error) {
logger.error(`Failed to register protocol: ${(error as Error).message}`, { function: 'handleViewFileContentLoading' });
}
```
#### `protocol.handle('file')`
@ -85,37 +85,37 @@ async function loadFileContentHandler(request: Request) {
`protocol.handle('file'`'s handler won't receive anything.
```ts
public async handleFileProtocol(request: GlobalRequest): Promise<GlobalResponse> {
logger.info('handleFileProtocol() getting url', { url: request.url });
const { pathname } = new URL(request.url);
logger.info('handleFileProtocol() handle file:// or open:// This url will open file in-wiki', { pathname });
let fileExists = fs.existsSync(pathname);
logger.info(`This file (decodeURI) ${fileExists ? '' : 'not '}exists`, { pathname });
if (fileExists) {
return await net.fetch(pathname);
}
logger.info(`try find file relative to workspace folder`);
const workspace = await this.workspaceService.getActiveWorkspace();
if (workspace === undefined) {
logger.error(`No active workspace, abort. Try loading pathname as-is.`, { pathname });
return await net.fetch(pathname);
}
const filePathInWorkspaceFolder = path.resolve(workspace.wikiFolderLocation, pathname);
fileExists = fs.existsSync(filePathInWorkspaceFolder);
logger.info(`This file ${fileExists ? '' : 'not '}exists in workspace folder.`, { filePathInWorkspaceFolder });
if (fileExists) {
return await net.fetch(filePathInWorkspaceFolder);
}
logger.info(`try find file relative to TidGi App folder`);
// on production, __dirname will be in .webpack/main
const inTidGiAppAbsoluteFilePath = path.join(app.getAppPath(), '.webpack', 'renderer', pathname);
fileExists = fs.existsSync(inTidGiAppAbsoluteFilePath);
if (fileExists) {
return await net.fetch(inTidGiAppAbsoluteFilePath);
}
logger.warn(`This url can't be loaded in-wiki. Try loading url as-is.`, { url: request.url });
return await net.fetch(request.url);
public async handleFileProtocol(request: GlobalRequest): Promise<GlobalResponse> {
logger.info('handleFileProtocol() getting url', { url: request.url });
const { pathname } = new URL(request.url);
logger.info('handleFileProtocol() handle file:// or open:// This url will open file in-wiki', { pathname });
let fileExists = fs.existsSync(pathname);
logger.info(`This file (decodeURI) ${fileExists ? '' : 'not '}exists`, { pathname });
if (fileExists) {
return await net.fetch(pathname);
}
logger.info(`try find file relative to workspace folder`);
const workspace = await this.workspaceService.getActiveWorkspace();
if (workspace === undefined) {
logger.error(`No active workspace, abort. Try loading pathname as-is.`, { pathname });
return await net.fetch(pathname);
}
const filePathInWorkspaceFolder = path.resolve(workspace.wikiFolderLocation, pathname);
fileExists = fs.existsSync(filePathInWorkspaceFolder);
logger.info(`This file ${fileExists ? '' : 'not '}exists in workspace folder.`, { filePathInWorkspaceFolder });
if (fileExists) {
return await net.fetch(filePathInWorkspaceFolder);
}
logger.info(`try find file relative to TidGi App folder`);
// on production, __dirname will be in .webpack/main
const inTidGiAppAbsoluteFilePath = path.join(app.getAppPath(), '.webpack', 'renderer', pathname);
fileExists = fs.existsSync(inTidGiAppAbsoluteFilePath);
if (fileExists) {
return await net.fetch(inTidGiAppAbsoluteFilePath);
}
logger.warn(`This url can't be loaded in-wiki. Try loading url as-is.`, { url: request.url });
return await net.fetch(request.url);
}
```
if

View file

@ -0,0 +1,443 @@
# Wiki Workspace Creation
## Overview
Wiki workspaces are the core concept in TidGi, representing individual TiddlyWiki instances with associated configuration, Git repositories, and UI views. This document explains how wiki workspaces are created in two scenarios:
1. **Automatic creation** when the application starts with no existing workspaces
2. **Manual creation** through the frontend UI
## Automatic Workspace Creation
### Startup Flow
When TidGi launches without any existing workspaces, it automatically creates a default wiki workspace. This logic is implemented in the initialization chain:
```mermaid
sequenceDiagram
autonumber
participant Main as main.ts
participant Common as commonInit()
participant WikiGit as WikiGitWorkspaceService
participant Workspace as WorkspaceService
participant View as WorkspaceViewService
Main->>Common: app.on('ready')
Common->>Common: await app.whenReady()
Common->>Common: Initialize database & services
Common->>WikiGit: wikiGitWorkspaceService.initialize()
WikiGit->>Workspace: getWorkspacesAsList()
Workspace-->>WikiGit: workspaces[]
alt No wiki workspaces exist
WikiGit->>WikiGit: Create default config
WikiGit->>WikiGit: copyWikiTemplate()
WikiGit->>WikiGit: initWikiGitTransaction()
WikiGit->>Workspace: create(defaultConfig)
WikiGit->>WikiGit: initWikiGit()
end
Common->>Workspace: initializeDefaultPageWorkspaces()
Common->>View: initializeAllWorkspaceView()
```
### Implementation Details
#### 1. Entry Point (main.ts)
The initialization starts in `src/main.ts` in the `commonInit()` function:
```typescript
const commonInit = async (): Promise<void> => {
await app.whenReady();
await initDevelopmentExtension();
// Initialize database FIRST - all other services depend on it
await databaseService.initializeForApp();
// ... other initializations ...
// Auto-create default wiki workspace if none exists
await wikiGitWorkspaceService.initialize();
// Create default page workspaces before initializing all workspace views
await workspaceService.initializeDefaultPageWorkspaces();
// Perform wiki startup and git sync for each workspace
await workspaceViewService.initializeAllWorkspaceView();
};
```
#### 2. WikiGitWorkspaceService.initialize()
Located in `src/services/wikiGitWorkspace/index.ts`, this method checks if any wiki workspaces exist and creates a default one if needed:
```typescript
public async initialize(): Promise<void> {
const workspaceService = container.get<IWorkspaceService>(serviceIdentifier.Workspace);
const workspaces = await workspaceService.getWorkspacesAsList();
const wikiWorkspaces = workspaces.filter(w => isWikiWorkspace(w) && !w.isSubWiki);
// Exit if any wiki workspaces already exist
if (wikiWorkspaces.length > 0) return;
// Construct minimal default config with required fields
const defaultConfig: INewWikiWorkspaceConfig = {
order: 0,
wikiFolderLocation: DEFAULT_FIRST_WIKI_PATH,
storageService: SupportedStorageServices.local,
name: 'wiki',
port: 5212,
isSubWiki: false,
backupOnInterval: true,
readOnlyMode: false,
tokenAuth: false,
tagName: null,
mainWikiToLink: null,
mainWikiID: null,
excludedPlugins: [],
enableHTTPAPI: false,
lastNodeJSArgv: [],
homeUrl: '',
gitUrl: null,
};
try {
// Copy the wiki template first
const wikiService = container.get<IWikiService>(serviceIdentifier.Wiki);
await wikiService.copyWikiTemplate(DEFAULT_FIRST_WIKI_FOLDER_PATH, 'wiki');
// Create the workspace
await this.initWikiGitTransaction(defaultConfig);
} catch (error) {
logger.error(error.message, error);
}
}
```
#### 3. Wiki Template and Git Initialization
The `initWikiGitTransaction` method handles the complete workspace creation:
1. **Create workspace record**: Calls `workspaceService.create(newWorkspaceConfig)` to persist workspace configuration
2. **Copy wiki template**: Uses `wikiService.copyWikiTemplate()` to copy base TiddlyWiki files
3. **Initialize Git repository**: If not already initialized, calls `gitService.initWikiGit()`
4. **Rollback on failure**: If any step fails, removes the created workspace and wiki folder
```typescript
public initWikiGitTransaction = async (
newWorkspaceConfig: INewWikiWorkspaceConfig,
userInfo?: IGitUserInfos
): Promise<IWorkspace | undefined> => {
const workspaceService = container.get<IWorkspaceService>(serviceIdentifier.Workspace);
const newWorkspace = await workspaceService.create(newWorkspaceConfig);
try {
// ... Git initialization logic ...
if (await hasGit(wikiFolderLocation)) {
logger.warn('Skip git init because it already has a git setup.');
} else {
const gitService = container.get<IGitService>(serviceIdentifier.Git);
await gitService.initWikiGit(wikiFolderLocation, isSyncedWiki, !isSubWiki, gitUrl, userInfo);
}
return newWorkspace;
} catch (error) {
// Rollback: remove workspace and wiki folder
await workspaceService.remove(workspaceID);
await wikiService.removeWiki(wikiFolderLocation);
throw new InitWikiGitError(error.message);
}
};
```
#### 4. View Initialization
After workspace creation, `workspaceViewService.initializeAllWorkspaceView()` starts each wiki:
1. **Check wiki validity**: Verifies the wiki folder contains valid TiddlyWiki files
2. **Start wiki server**: Launches the TiddlyWiki Node.js server
3. **Create browser view**: Creates an Electron WebContentsView to display the wiki
4. **Load initial URL**: Navigates the view to the wiki's home URL
## Manual Workspace Creation
### User Interface Flow
Users can create new workspaces through the "Add Workspace" window:
```mermaid
flowchart TD
A[User clicks Add Workspace] --> B[Open AddWorkspace window]
B --> C{Creation Method}
C -->|Create New| D[useNewWiki hook]
C -->|Clone Existing| E[useCloneWiki hook]
C -->|Open Existing| F[useOpenWiki hook]
D --> G[Fill form: name, folder, port]
E --> H[Fill form: git URL, credentials]
F --> I[Select existing wiki folder]
G --> J[Submit form]
H --> J
I --> J
J --> K[callWikiInitialization]
K --> L[wikiGitWorkspace.initWikiGitTransaction]
L --> M[workspaceView.initializeWorkspaceView]
M --> N[workspaceView.setActiveWorkspaceView]
N --> O[Close AddWorkspace window]
```
### Frontend Components
#### 1. Form State Management (useForm.ts)
Located in `src/pages/AddWorkspace/useForm.ts`, manages workspace creation form state:
```typescript
export function useWikiWorkspaceForm(options?: { fromExisted: boolean }) {
const [wikiFolderName, wikiFolderNameSetter] = useState('tiddlywiki');
const [parentFolderLocation, parentFolderLocationSetter] = useState('');
const [wikiPort, wikiPortSetter] = useState(5212);
const [storageProvider, storageProviderSetter] = useState<SupportedStorageServices>(
SupportedStorageServices.local
);
// Initialize default folder path
useEffect(() => {
(async function getDefaultExistedWikiFolderPathEffect() {
const desktopPath = await window.service.context.get('DEFAULT_WIKI_FOLDER');
parentFolderLocationSetter(desktopPath);
})();
}, []);
// ... rest of form state ...
}
```
#### 2. Creation Hooks
Three main hooks handle different creation methods:
##### useNewWiki (useNewWiki.ts)
Creates a new wiki from template:
```typescript
export function useNewWiki(
isCreateMainWorkspace: boolean,
isCreateSub: boolean,
form: IWikiWorkspaceForm,
wikiCreationMessageSetter: (m: string) => void,
// ...
): () => Promise<void> {
const onSubmit = useCallback(async () => {
wikiCreationMessageSetter(t('AddWorkspace.Processing'));
try {
const newWorkspaceConfig = workspaceConfigFromForm(form, isCreateMainWorkspace, true);
// Create wiki folder and files
if (isCreateMainWorkspace) {
await window.service.wiki.copyWikiTemplate(
form.parentFolderLocation,
form.wikiFolderName
);
} else {
await window.service.wiki.copySubWikiTemplate(/* ... */);
}
// Initialize workspace and Git
await callWikiInitialization(newWorkspaceConfig, wikiCreationMessageSetter, t, gitUserInfo, {
from: WikiCreationMethod.Create
});
} catch (error) {
wikiCreationMessageSetter(error.message);
hasErrorSetter(true);
}
}, [/* dependencies */]);
return onSubmit;
}
```
##### useCloneWiki (useCloneWiki.ts)
Clones an existing wiki from a Git repository:
```typescript
export function useCloneWiki(/* ... */): () => Promise<void> {
const onSubmit = useCallback(async () => {
wikiCreationMessageSetter(t('AddWorkspace.Processing'));
try {
const newWorkspaceConfig = workspaceConfigFromForm(form, isCreateMainWorkspace, true);
// Clone from Git repository
if (isCreateMainWorkspace) {
await window.service.wiki.cloneWiki(
form.parentFolderLocation,
form.wikiFolderName,
form.gitRepoUrl,
form.gitUserInfo!
);
} else {
await window.service.wiki.cloneSubWiki(/* ... */);
}
// Initialize workspace
await callWikiInitialization(newWorkspaceConfig, wikiCreationMessageSetter, t, gitUserInfo, {
from: WikiCreationMethod.Clone
});
} catch (error) {
wikiCreationMessageSetter(error.message);
hasErrorSetter(true);
}
}, [/* dependencies */]);
return onSubmit;
}
```
##### useOpenWiki (useOpenWiki.ts)
Opens an existing wiki folder:
```typescript
export function useOpenWiki(/* ... */): () => Promise<void> {
const onSubmit = useCallback(async () => {
wikiCreationMessageSetter(t('AddWorkspace.Processing'));
try {
const newWorkspaceConfig = workspaceConfigFromForm(form, isCreateMainWorkspace, false);
// No need to copy template or clone, wiki folder already exists
// Just initialize workspace and start wiki
await callWikiInitialization(newWorkspaceConfig, wikiCreationMessageSetter, t, gitUserInfo, {
from: WikiCreationMethod.Open
});
} catch (error) {
wikiCreationMessageSetter(error.message);
hasErrorSetter(true);
}
}, [/* dependencies */]);
return onSubmit;
}
```
#### 3. Common Initialization (callWikiInitialization)
Located in `src/pages/AddWorkspace/useCallWikiInitialization.ts`, this function performs the final workspace initialization steps:
```typescript
export async function callWikiInitialization(
newWorkspaceConfig: INewWorkspaceConfig,
wikiCreationMessageSetter: (m: string) => void,
t: TFunction<'translation'>,
gitUserInfo: IGitUserInfos | undefined,
configs: ICallWikiInitConfig,
): Promise<void> {
// Step 1: Initialize workspace and Git
wikiCreationMessageSetter(t('Log.InitializeWikiGit'));
const newWorkspace = await window.service.wikiGitWorkspace.initWikiGitTransaction(
newWorkspaceConfig,
gitUserInfo
);
if (newWorkspace === undefined) {
throw new Error('newWorkspace is undefined');
}
// Step 2: Initialize workspace view (starts wiki server, creates browser view)
wikiCreationMessageSetter(t('Log.InitializeWorkspaceView'));
await window.service.workspaceView.initializeWorkspaceView(newWorkspace, {
isNew: true,
from: configs.from
});
// Step 3: Activate the new workspace
wikiCreationMessageSetter(t('Log.InitializeWorkspaceViewDone'));
await window.service.workspaceView.setActiveWorkspaceView(newWorkspace.id);
// Step 4: Close Add Workspace window (if not disabled)
if (!configs.notClose) {
await window.service.window.close(WindowNames.addWorkspace);
}
}
```
## Workspace Creation Validation
### Wiki Folder Validation
The `checkWikiExist` method in WikiService validates that a folder contains a valid TiddlyWiki:
1. **Check folder exists**: Verifies the wiki folder path exists
2. **Check tiddlywiki.info**: For main wikis, requires `tiddlywiki.info` file
3. **Check plugin files**: Verifies required TiddlyWiki core files exist
4. **Show error dialog**: If validation fails and `showDialog: true`, prompts user to remove invalid workspace
The error message in the CI logs shows this validation:
```log
无法找到之前还在该处的工作区知识库文件夹!该目录不是一个知识库文件夹
```
This occurs when `initWikiGit` completes but wiki template files are not yet created, causing `initializeAllWorkspaceView` to fail validation.
## Related Code
### Backend Services
- [main.ts](../../src/main.ts): Application initialization entry point
- [wikiGitWorkspace/index.ts](../../src/services/wikiGitWorkspace/index.ts): `initialize()`, `initWikiGitTransaction()`
- [workspacesView/index.ts](../../src/services/workspacesView/index.ts): `initializeAllWorkspaceView()`, `initializeWorkspaceView()`
- [wiki/index.ts](../../src/services/wiki/index.ts): `copyWikiTemplate()`, `checkWikiExist()`
- [git/index.ts](../../src/services/git/index.ts): `initWikiGit()`
### Frontend UI Components
- [AddWorkspace/useForm.ts](../../src/pages/AddWorkspace/useForm.ts): Form state management
- [AddWorkspace/useNewWiki.ts](../../src/pages/AddWorkspace/useNewWiki.ts): Create new wiki
- [AddWorkspace/useCloneWiki.ts](../../src/pages/AddWorkspace/useCloneWiki.ts): Clone from Git
- [AddWorkspace/useOpenWiki.ts](../../src/pages/AddWorkspace/useOpenWiki.ts): Open existing wiki
- [AddWorkspace/useCallWikiInitialization.ts](../../src/pages/AddWorkspace/useCallWikiInitialization.ts): Common initialization logic
## Common Issues
### 1. Wiki Validation Failure
**Symptom**: Error message "该目录不是一个知识库文件夹" during initialization
**Cause**: Wiki template files not fully created before validation runs
**Solution**: Ensure `copyWikiTemplate()` completes before calling `initWikiGitTransaction()`
### 2. Git Initialization Timeout
**Symptom**: Workspace creation hangs during Git initialization
**Cause**: Git operations taking too long in CI or slow network conditions
**Solution**: Implement timeout protection in `initWikiGit()` or skip Git init for local-only wikis
### 3. Worker Not Starting
**Symptom**: Wiki operations timeout after workspace creation
**Cause**: Worker initialization fails if wiki folder validation fails
**Solution**: Ensure wiki folder passes validation before starting worker
## Best Practices
1. **Atomic Operations**: Use transactions (`initWikiGitTransaction`) to rollback on failure
2. **Validation First**: Always validate wiki folders before starting services
3. **Progress Feedback**: Use `wikiCreationMessageSetter` to show user progress
4. **Error Handling**: Catch and display user-friendly error messages
5. **Default Values**: Provide sensible defaults for optional configuration
6. **Cleanup on Failure**: Always remove partially created workspaces on error

View file

@ -13,3 +13,30 @@ See [this 6aedff4b commit](https://github.com/tiddly-gittly/TidGi-Desktop/commit
Some services are sync, like `getSubWorkspacesAsListSync` `getActiveWorkspaceSync` from `src/services/workspaces/index.ts`, they can't be called from renderer, only can be used in the main process.
Because after pass through IPC, everything will be async, so its function typescript signature will be wrong.
## Use async service on frontend
Given
```ts
export const WorkspaceServiceIPCDescriptor = {
channel: WorkspaceChannel.name,
properties: {
workspaces$: ProxyPropertyType.Value$,
getWorkspacesAsList: ProxyPropertyType.Function,
get: ProxyPropertyType.Function,
get$: ProxyPropertyType.Function$,
},
};
```
Registered service's async method could be used like `await window.service.workspace.getWorkspacesAsList()`, and observable could be used as `window.observables.workspace.workspaces$.pipe()` (where pipe is a method on Observable on rxjs), and
```ts
import useObservable from 'beautiful-react-hooks/useObservable';
const workspace$ = useMemo(() => window.observables.workspace.get$(id), [id]);
useObservable(workspace$, workspaceSetter);
```
or in store use rxjs like `window.observables.workspace.get$(id).observe()`.

View file

@ -8,29 +8,9 @@
</div>
<hr>
# TOC
<a href="https://github.com/tiddly-gittly/TidGi-Desktop/">🇬🇧 English</a> | 🇨🇳 简体中文
<!-- toc -->
- [产品截图及试用](#产品截图及试用)
- [相关介绍](#相关介绍)
- [关于「太记-桌面版」](#%E5%85%B3%E4%BA%8E%E5%A4%AA%E8%AE%B0-%E6%A1%8C%E9%9D%A2%E7%89%88)
- [下载](#下载)
- [移动端应用](#移动端应用)
- [数据隐私保护](#数据隐私保护)
- [与竞品的差异](#与竞品的差异)
- [为什么使用 Github 存储?](#为什么使用Github存储)
- [参与开发](#参与开发)
- [鸣谢](#鸣谢)
- [点个星星 ⭐️ 吧](#点个星星%EF%B8%8F吧)
<!-- tocstop -->
---
| [最新版下载按钮](https://github.com/tiddly-gittly/TidGi-Desktop/releases/latest) |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| [![GitHub Releases](https://img.shields.io/github/downloads/tiddly-gittly/TidGi-Desktop/latest/total?label=Download%20Latest%20Release&style=for-the-badge)](https://github.com/tiddly-gittly/TidGi-Desktop/releases/latest) |
## 产品截图及试用
@ -45,21 +25,21 @@
<details>
| 加载 NodeJS 版维基 |
| :---------------------------------------------------------: |
| 加载 NodeJS 版维基 |
| :-----------------------------------------------------: |
| ![Screenshot of main-window](../images/main-window.png) |
| 新建本地维基 | 下载云端维基 |
| :-------------------------------------------------------------: | :-----------------------------------------------------------------: |
| 新建本地维基 | 下载云端维基 |
| :---------------------------------------------------------: | :-------------------------------------------------------------: |
| ![Screenshot of add-workspace](../images/add-workspace.png) | ![Screenshot of clone-workspace](../images/clone-workspace.png) |
| 多语言翻译和设置界面 |
| :--------------------------------------------------------------------------------------------------------------------------------------------------: |
| ![Screenshot of preference](../images/preference.png) |
| ![Screenshot of preference](../images/preference.png) |
| 交互式代码执行 |
| ![Screen recording of zx-script in tiddlywiki](https://user-images.githubusercontent.com/3746270/133831500-ae91164c-7948-4de4-9a81-7017ed3b65c9.gif) |
| 社区插件源 |
| ![Screenshot of add-workspace](../images/community-plugin-library.png) |
| ![Screenshot of add-workspace](../images/community-plugin-library.png) |
</details>

View file

@ -11,10 +11,26 @@ export default [
languageOptions: {
parserOptions: {
projectService: {
allowDefaultProject: ['./*.js', './*.mjs'],
allowDefaultProject: ['./*.js', './*.mjs', './*.*.js', './*.*.ts', './*.*.mjs'],
},
tsconfigRootDir: __dirname,
},
},
rules: {
'@typescript-eslint/no-unnecessary-condition': 'off',
'@typescript-eslint/require-await': 'off',
},
},
{
files: ['**/*.test.ts', '**/*.test.tsx', '**/*.spec.ts', '**/*.spec.tsx'],
rules: {
'@typescript-eslint/unbound-method': 'off',
'unicorn/prevent-abbreviations': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
},
},
];

114
features/agent.feature Normal file
View file

@ -0,0 +1,114 @@
Feature: Agent Workflow - Tool Usage and Multi-Round Conversation
As a user
I want to use an intelligent agent to search wiki content
So that I can get AI-powered explanations of wiki entries
Background:
Given I add test ai settings
Then I launch the TidGi application
And I wait for the page to load completely
And I should see a "page body" element with selector "body"
# Ensure we are in the correct workspace before each scenario to avoid wrong starting state
And I click on "agent workspace button and new tab button" elements with selectors:
| [data-testid='workspace-agent'] |
| [data-tab-id='new-tab-button'] |
@agent @mockOpenAI
Scenario: Wiki-search tool usage
Given I have started the mock OpenAI server
| response | stream |
| <tool_use name="wiki-search">{"workspaceName":"-VPTqPdNOEZHGO5vkwllY","filter":"[title[Index]]"}</tool_use> | false |
| TiddlyWiki Index 访 | false |
# Proceed with agent workflow in main window
# Step 1: Click new tab button
When I click on a "new tab button" element with selector "[data-tab-id='new-tab-button']"
And I should see a "search interface" element with selector ".aa-Autocomplete"
# Step 2: Click search box and wait for autocomplete
When I click on a "search input box" element with selector ".aa-Input"
And I should see an "autocomplete panel" element with selector ".aa-Panel"
# Step 3: Select agent from autocomplete (not new tab)
When I click on an "agent suggestion" element with selector '[data-autocomplete-source-id="agentsSource"] .aa-ItemWrapper'
And I should see a "message input box" element with selector "[data-testid='agent-message-input']"
# Step 4: Send message to agent - using generic steps combination
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type " wiki index " in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
Then I should see 4 messages in chat history
# Verify the last message contains the AI explanation about Index
And I should see "explanation in last message and explanation about edit" elements with selectors:
| [data-testid='message-bubble']:last-child:has-text('Index') |
| [data-testid='message-bubble']:last-child:has-text('') |
@agent @mockOpenAI
Scenario: Wiki operation
Given I have started the mock OpenAI server
| response | stream |
| <tool_use name="wiki-operation">{"workspaceName":"default","operation":"wiki-add-tiddler","title":"testNote","text":"test"}</tool_use> | false |
| <tool_use name="wiki-operation">{"workspaceName":"wiki","operation":"wiki-add-tiddler","title":"test","text":""}</tool_use>使 wiki | false |
| wiki "test" | false |
# Step 1: Start a fresh tab and run the two-round wiki operation flow
When I click on a "new tab button" element with selector "[data-tab-id='new-tab-button']"
And I should see a "search interface" element with selector ".aa-Autocomplete"
# Step 2: Click search box and wait for autocomplete
When I click on a "search input box" element with selector ".aa-Input"
And I should see an "autocomplete panel" element with selector ".aa-Panel"
# Step 3: Select agent from autocomplete (not new tab)
When I click on an "agent suggestion" element with selector '[data-autocomplete-source-id="agentsSource"] .aa-ItemWrapper'
And I should see a "message input box" element with selector "[data-testid='agent-message-input']"
# First round: try create note using default workspace (expected to fail)
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type " wiki test" in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
Then I should see 6 messages in chat history
# Verify there's an error message about workspace not found (in one of the middle messages)
And I should see a "workspace not exist error" element with selector "[data-testid='message-bubble']:has-text('default'):has-text('')"
# Verify the last message contains success confirmation
And I should see "success in last message and wiki workspace in last message" elements with selectors:
| [data-testid='message-bubble']:last-child:has-text('') |
| [data-testid='message-bubble']:last-child:has-text('wiki') |
@agent
Scenario: Create default agent from New Tab quick access
When I click on "new tab button and create default agent button" elements with selectors:
| [data-tab-id='new-tab-button'] |
| [data-testid='create-default-agent-button'] |
And I should see a "message input box" element with selector "[data-testid='agent-message-input']"
@agent
Scenario: Close all tabs then create default agent from fallback page
# Ensure starting from black/fallback page with no open tabs
Given I click on a "new tab button" element with selector "[data-tab-id='new-tab-button']"
When I click all "tab" elements matching selector "[data-testid='tab']"
When I click all "close tab button" elements matching selector "[data-testid='tab-close-button']"
And I should see a "new tab button" element with selector "[data-tab-id='new-tab-button']"
# When there is no active tab, this is "fallback new tab", it has same thing as new tab.
And I should see a "Create Default Agent" element with selector "[data-testid='create-default-agent-button']"
When I click on a "new tab button" element with selector "[data-tab-id='new-tab-button']"
And I should see a "Create Default Agent" element with selector "[data-testid='create-default-agent-button']"
When I click on a "create default agent button" element with selector "[data-testid='create-default-agent-button']"
And I should see a "message input box" element with selector "[data-testid='agent-message-input']"
Then I click all "close tab button" elements matching selector "[data-testid='tab-close-button']"
@agent @mockOpenAI
Scenario: Streamed assistant response can be cancelled mid-stream and send button returns
Given I have started the mock OpenAI server
| response | stream |
| partial_chunk_1<stream_split>partial_chunk_2<stream_split>partial_chunk_3<stream_split>partial_chunk_4 | true |
And I click on "new tab button and create default agent button" elements with selectors:
| [data-tab-id='new-tab-button'] |
| [data-testid='create-default-agent-button'] |
And I should see a "message input box" element with selector "[data-testid='agent-message-input']"
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type "Start long streaming" in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
# Wait for streaming container to appear and contain the first chunk
Then I should see "assistant streaming container and partial assistant text and cancel icon" elements with selectors:
| [data-testid='assistant-streaming-text'] |
| *:has-text('partial_chunk_1') |
| [data-testid='cancel-icon'] |
# Click cancel button mid-stream
When I click on a "cancel button" element with selector "[data-testid='agent-send-button']"
And I should see a "send icon" element with selector "[data-testid='send-icon']"
# Verify send button returned and stream stopped (no further chunks)
Then I should see a "send button" element with selector "[data-testid='agent-send-button']"
And I should not see a "partial chunk 4 text" element with selector "text='partial_chunk_4'"

View file

@ -0,0 +1,14 @@
module.exports = {
default: {
require: [
'ts-node/register',
'features/stepDefinitions/**/*.ts',
],
requireModule: ['ts-node/register'],
format: ['progress'],
formatOptions: {
snippetInterface: 'async-await',
},
paths: ['features/*.feature'],
},
};

View file

@ -0,0 +1,16 @@
Feature: TidGi Default Wiki
As a user
I want app auto create a default wiki workspace for me
So that I can start using wiki immediately
@wiki
Scenario: Application has default wiki workspace
# Note: tests expect the test wiki parent folder to exist. Run the preparation step before E2E:
# cross-env NODE_ENV=test pnpm dlx tsx scripts/developmentMkdir.ts
Given I cleanup test wiki
When I launch the TidGi application
And I wait for the page to load completely
Then I should see "page body and wiki workspace" elements with selectors:
| body |
| div[data-testid^='workspace-']:has-text('wiki') |
And the window title should contain ""

13
features/logging.feature Normal file
View file

@ -0,0 +1,13 @@
Feature: Renderer logging to backend (UI-driven)
Background:
Given I launch the TidGi application
And I wait for the page to load completely
@logging
Scenario: Renderer logs appear in backend log file
When I click on a "settings button" element with selector "#open-preferences-button"
When I switch to "preferences" window
When I click on a "sync section" element with selector "[data-testid='preference-section-sync']"
Then I should find log entries containing
| Preferences section clicked |

123
features/newAgent.feature Normal file
View file

@ -0,0 +1,123 @@
Feature: Create New Agent Workflow
As a user
I want to create a new agent definition using a multi-step wizard
So that I can customize agents for specific tasks and use them immediately
Background:
Given I add test ai settings
Then I launch the TidGi application
And I wait for the page to load completely
And I should see a "page body" element with selector "body"
# Ensure we are in the correct workspace before each scenario
When I click on an "agent workspace button" element with selector "[data-testid='workspace-agent']"
And I should see a "new tab button" element with selector "[data-tab-id='new-tab-button']"
@newAgent @mockOpenAI
Scenario: Create new agent definition and edit prompt and check server request
# Setup mock OpenAI server first
Given I have started the mock OpenAI server
| response | stream |
| | false |
# Step 1: Open new tab and navigate to CreateNewAgent
When I click on "new tab button and create new agent button" elements with selectors:
| [data-tab-id='new-tab-button'] |
| [data-testid='create-new-agent-button'] |
# Step 2: Verify first step content (Setup Agent: Name + Template)
Then I should see "step title and search input and agent name input field" elements with selectors:
| *:has-text('') |
| .aa-Input |
| [data-testid='agent-name-input-field'] |
# Step 3: Select template to advance to step 2
When I click on a "search input" element with selector ".aa-Input"
And I should see an "autocomplete panel" element with selector ".aa-Panel"
When I click on a "agent suggestion" element with selector '[data-autocomplete-source-id="templateAgentsSource"] .aa-ItemWrapper'
# Fill in agent name while still in step 1
When I clear text in "agent name input" element with selector "[data-testid='agent-name-input-field']"
When I type "" in "agent name input" element with selector "[data-testid='agent-name-input-field']"
# Advance to step 2 (Edit Prompt)
When I click on a "next button" element with selector "[data-testid='next-button']"
# Step 4: Verify second step content (Edit Prompt)
And I should see a "edit prompt title" element with selector "*:has-text('')"
# Step 4.1: Wait for PromptConfigForm to load
# Verify the PromptConfigForm is present with our new test id
And I should see a "prompt config form" element with selector "[data-testid='prompt-config-form']"
# Step 4.2: Navigate to the correct tab and expand array items to edit prompt
# Look for tabs in the PromptConfigForm
And I should see a "config tabs" element with selector "[data-testid='prompt-config-form'] .MuiTabs-root"
# Click on the first tab, expand array item, and click on the system prompt text field
When I click on "first config tab and expand array item button and system prompt text field" elements with selectors:
| [data-testid='prompt-config-form'] .MuiTab-root:first-of-type |
| [data-testid='prompt-config-form'] [role='tabpanel']:not([hidden]) button[title*=''], [data-testid='prompt-config-form'] [role='tabpanel']:not([hidden]) button svg[data-testid='ExpandMoreIcon'] |
| [data-testid='prompt-config-form'] [role='tabpanel']:not([hidden]) textarea[id*='_text']:not([readonly]) |
When I clear text in "system prompt text field" element with selector "[data-testid='prompt-config-form'] [role='tabpanel']:not([hidden]) textarea[id*='_text']:not([readonly])"
When I type "" in "system prompt text field" element with selector "[data-testid='prompt-config-form'] [role='tabpanel']:not([hidden]) textarea[id*='_text']:not([readonly])"
# Wait for form content save to backend
And I wait for 0.2 seconds
# Step 5: Advance to step 3 (Immediate Use)
When I click on a "next button" element with selector "[data-testid='next-button']"
# Step 6: Verify third step content (Immediate Use with chat interface)
And I should see a "immediate use title" element with selector "*:has-text('使')"
# Step 7: Test in the preview chat interface (part of step 3)
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type " Hello World" in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
# Verify the agent responds in the preview interface
Then I should see "user message and assistant message" elements with selectors:
| *:has-text(' Hello World') |
| *:has-text('') |
And the last AI request should contain system prompt ""
# Step 8: Save and start using (after testing in step 3)
When I click on a "save and use button" element with selector "button:has-text('使')"
# Verify agent was created and separate chat tab opened
Then I should see a "message input box" element with selector "[data-testid='agent-message-input']"
@editAgentDefinition @mockOpenAI
Scenario: Edit existing agent definition workflow
# Setup mock OpenAI server first
Given I have started the mock OpenAI server
| response | stream |
| | false |
# Step 1: Open new tab to access create default agent card
When I click on a "new tab button" element with selector "[data-tab-id='new-tab-button']"
# Step 2: Right-click on create default agent card to open context menu
When I right-click on a "create default agent card" element with selector "[data-testid='create-default-agent-button']"
# Step 3: Click on edit definition option in context menu
When I click on a "edit definition menu item" element with selector "[data-testid='edit-definition-menu-item']"
# Step 4: Verify direct edit interface (no steps - all content visible)
And I should see "edit agent title and basic info section and edit prompt section and immediate use section" elements with selectors:
| *:has-text('') |
| *:has-text('') |
| *:has-text('') |
| *:has-text('使') |
# Step 5: Edit agent name in the basic info section
And I should see a "agent name input" element with selector "[data-testid='edit-agent-name-input']"
When I clear text in "agent name input" element with selector "[data-testid='edit-agent-name-input-field']"
When I type "" in "agent name input" element with selector "[data-testid='edit-agent-name-input-field']"
# Step 6: Edit the prompt configuration - Wait for PromptConfigForm to load
# Verify the PromptConfigForm is present
And I should see a "prompt config form" element with selector "[data-testid='edit-agent-prompt-form']"
# Step 6.1: Navigate to the correct tab and expand array items to edit prompt
# Look for tabs in the PromptConfigForm
And I should see a "config tabs" element with selector "[data-testid='edit-agent-prompt-form'] .MuiTabs-root"
# Click on the first tab, expand array item, and click on the system prompt text field
When I click on "first config tab and expand array item button and system prompt text field" elements with selectors:
| [data-testid='edit-agent-prompt-form'] .MuiTab-root:first-of-type |
| [data-testid='edit-agent-prompt-form'] [role='tabpanel']:not([hidden]) button[title*=''], [data-testid='edit-agent-prompt-form'] [role='tabpanel']:not([hidden]) button svg[data-testid='ExpandMoreIcon'] |
| [data-testid='edit-agent-prompt-form'] [role='tabpanel']:not([hidden]) textarea[id*='_text']:not([readonly]) |
When I clear text in "system prompt text field" element with selector "[data-testid='edit-agent-prompt-form'] [role='tabpanel']:not([hidden]) textarea[id*='_text']:not([readonly])"
When I type "" in "system prompt text field" element with selector "[data-testid='edit-agent-prompt-form'] [role='tabpanel']:not([hidden]) textarea[id*='_text']:not([readonly])"
# Step 7: Test in the immediate use section (embedded chat)
# The immediate use section should show an embedded chat interface
# Find a message input in the immediate use section and test the agent
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type "" in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
# Verify the agent responds in the embedded chat
Then I should see "user message and assistant message" elements with selectors:
| *:has-text('') |
| *:has-text('') |
# Verify that the server received the request with the modified system prompt
And the last AI request should contain system prompt ""
# Step 8: Save the edited agent definition
And I should see a "save button" element with selector "[data-testid='edit-agent-save-button']"
When I click on a "save button" element with selector "[data-testid='edit-agent-save-button']"

View file

@ -1,27 +0,0 @@
Feature: Open
As a user of TidGi
I want to open the app
So I can be more productive
Scenario: Opening TidGi
Given the app is launched
Then the element "#new-user-tip" is on the page
Then the element "#add-workspace-button" is on the page
Scenario: Opening Add Workspace Page
Given the app is launched
Then the element "#add-workspace-button" is on the page
Then click on this element
Then " tidgi-dev" window show up
Scenario: Opening Preferences Page
Given the app is launched
Then the element "#open-preferences-button" is on the page
Then click on this element
Then "..." window show up
Scenario: Opening Notifications Page
Given the app is launched
Then the element "#open-notification-settings-button" is on the page
Then click on this element
Then "..." window show up

View file

@ -0,0 +1,82 @@
Feature: TidGi Preference
As a user
I want to configure my preferences for the intelligent agent and so on
So that I can customize its behavior and improve my experience
Background:
Given I clear test ai settings
Given I launch the TidGi application
And I wait for the page to load completely
And I should see a "page body" element with selector "body"
@setup
Scenario: Configure AI provider and default model
# Step 1: Configure AI settings first - Open preferences window, wait a second so its URL settle down.
When I click on a "settings button" element with selector "#open-preferences-button"
When I switch to "preferences" window
# Step 2: Navigate to External Services section (wait for sidebar animation)
When I click on an "external services section" element with selector "[data-testid='preference-section-externalAPI']"
# Step 3: Add new provider
When I click on an "add provider button" element with selector "[data-testid='add-new-provider-button']"
# Step 4: Fill provider form with mock server details (interface type already selected as openAICompatible)
When I type "TestProvider" in "provider name input" element with selector "[data-testid='new-provider-name-input']"
And I type "http://127.0.0.1:15121/v1" in "API endpoint input" element with selector "[data-testid='new-provider-base-url-input']"
When I click on an "add provider submit" element with selector "[data-testid='add-provider-submit-button']"
# Step 5: Select the new provider and add a model
When I click on "provider tab and add model button" elements with selectors:
| button[role='tab']:has-text('TestProvider') |
| [data-testid='add-new-model-button'] |
# Step 6: Add language model (will auto-fill as default language model)
When I type "test-model" in "model name input" element with selector "[data-testid='new-model-name-input']"
When I click on "save model button and add model button" elements with selectors:
| [data-testid='save-model-button'] |
| [data-testid='add-new-model-button'] |
# Step 7: Add embedding model (will auto-fill as default embedding model)
When I type "test-embedding-model" in "model name input" element with selector "[data-testid='new-model-name-input']"
When I click on "embedding feature checkbox and save model button and add model button" elements with selectors:
| [data-testid='feature-checkbox-embedding'] |
| [data-testid='save-model-button'] |
| [data-testid='add-new-model-button'] |
# Step 8: Add speech model (will auto-fill as default speech model)
When I type "test-speech-model" in "model name input" element with selector "[data-testid='new-model-name-input']"
# Uncheck language feature first (it's checked by default)
When I click on "language feature checkbox and speech feature checkbox and save model button" elements with selectors:
| [data-testid='feature-checkbox-language'] |
| [data-testid='feature-checkbox-speech'] |
| [data-testid='save-model-button'] |
# Step 9: Verify auto-fill worked by checking that autocomplete inputs have the correct selected values
# MUI Autocomplete shows selected value in the input, we check by looking for the model name in the visible text
Then I should see "default language model value test-model and default embedding model value test-embedding-model and default speech model value test-speech-model" elements with selectors:
| text='test-model' |
| text='test-embedding-model' |
| text='test-speech-model' |
# Verify the autocomplete is not empty and negative case remain explicit
Then I should not see a "empty first autocomplete placeholder" element with selector "xpath=(//label[contains(text(),'Preference.SelectModel')])[1]"
Then I should not see a "test-model after test-embedding-model (wrong order)" element with selector "xpath=//input[@value='test-embedding-model']/following::input[@value='test-model']"
# Verify there are exactly 3 filled model selectors
Then I should see "first autocomplete input with test-model and second autocomplete input with test-embedding-model and third autocomplete input with test-speech-model" elements with selectors:
| xpath=(//div[contains(@class,'MuiAutocomplete-root')]//input[@value='test-model'])[1] |
| xpath=(//div[contains(@class,'MuiAutocomplete-root')]//input[@value='test-embedding-model'])[1] |
| xpath=(//div[contains(@class,'MuiAutocomplete-root')]//input[@value='test-speech-model'])[1] |
# Step 10: Add ComfyUI provider with workflow path
When I click on "add provider button and select from preset dropdown and comfyui preset option and add provider submit and provider tab and add model button" elements with selectors:
| [data-testid='add-new-provider-button'] |
| div[role='combobox'] |
| li:has-text('comfyui') |
| [data-testid='add-provider-submit-button'] |
| button[role='tab']:has-text('comfyui') |
| [data-testid='add-new-model-button'] |
When I type "test-flux" in "model name input" element with selector "[data-testid='new-model-name-input']"
When I click on "language feature checkbox and imageGeneration feature checkbox" elements with selectors:
| [data-testid='feature-checkbox-language'] |
| [data-testid='feature-checkbox-imageGeneration'] |
When I type "C:/test/mock/workflow.json" in "workflow path input" element with selector "[data-testid='workflow-path-input']"
When I click on a "save model button" element with selector "[data-testid='save-model-button']"
Then I should see a "test-flux model chip" element with selector "[data-testid='model-chip-test-flux']"
# Verify workflow path was saved by clicking to edit
When I click on a "test-flux model chip" element with selector "[data-testid='model-chip-test-flux']"
Then I should see a "workflow path input with value" element with selector "[data-testid='workflow-path-input'][value='C:/test/mock/workflow.json']"
When I press "Escape" key
# Step 11: Close preferences window
When I close "preferences" window
And I ensure test ai settings exists

11
features/smoke.feature Normal file
View file

@ -0,0 +1,11 @@
Feature: TidGi Application Launch
As a user
I want to launch TidGi successfully
So that I can use the application
@smoke
Scenario: Application starts and shows interface
When I launch the TidGi application
And I wait for the page to load completely
And I should see a "page body" element with selector "body"
And the window title should contain ""

View file

@ -0,0 +1,299 @@
import { After, DataTable, Given, Then } from '@cucumber/cucumber';
import { AIGlobalSettings, AIProviderConfig } from '@services/externalAPI/interface';
import fs from 'fs-extra';
import { isEqual, omit } from 'lodash';
import path from 'path';
import type { ISettingFile } from '../../src/services/database/interface';
import { MockOpenAIServer } from '../supports/mockOpenAI';
import { settingsPath } from '../supports/paths';
import type { ApplicationWorld } from './application';
/**
* Generate deterministic embedding vector based on a semantic tag
* This allows us to control similarity in tests without writing full 384-dim vectors
*
* Strategy:
* - Similar tags (note1, note1-similar) -> similar vectors (high similarity)
* - Different tags (note1, note2) -> different vectors (medium similarity)
* - Unrelated tags (note1, unrelated) -> very different vectors (low similarity)
*/
function generateSemanticEmbedding(tag: string): number[] {
const vector: number[] = [];
// Parse tag to determine semantic relationship
// Format: "note1", "note2", "query-note1", "unrelated"
const baseTag = tag.replace(/-similar$/, '').replace(/^query-/, '');
const isSimilar = tag.includes('-similar');
const isQuery = tag.startsWith('query-');
const isUnrelated = tag === 'unrelated';
// Generate base vector from tag
const seed = Array.from(baseTag).reduce((hash, char) => {
return ((hash << 5) - hash) + char.charCodeAt(0);
}, 0);
for (let dimension = 0; dimension < 384; dimension++) {
const x = Math.sin((seed + dimension) * 0.1) * 10000;
let value = x - Math.floor(x);
// Adjust vector based on semantic relationship
if (isUnrelated) {
// Completely different direction
value = -value;
} else if (isSimilar || isQuery) {
// Very similar (>95% similarity) - add small noise
value = value + (Math.sin(dimension * 0.01) * 0.05);
}
// Normalize to [-1, 1]
vector.push(value * 2 - 1);
}
return vector;
}
// Agent-specific Given steps
Given('I have started the mock OpenAI server', function(this: ApplicationWorld, dataTable: DataTable | undefined, done: (error?: Error) => void) {
try {
const rules: Array<{ response: string; stream?: boolean; embedding?: number[] }> = [];
if (dataTable && typeof dataTable.raw === 'function') {
const rows = dataTable.raw();
// Skip header row
for (let index = 1; index < rows.length; index++) {
const row = rows[index];
const response = String(row[0] ?? '').trim();
const stream = String(row[1] ?? '').trim().toLowerCase() === 'true';
const embeddingTag = String(row[2] ?? '').trim();
// Generate embedding from semantic tag if provided
let embedding: number[] | undefined;
if (embeddingTag) {
embedding = generateSemanticEmbedding(embeddingTag);
}
if (response) rules.push({ response, stream, embedding });
}
}
this.mockOpenAIServer = new MockOpenAIServer(15121, rules);
this.mockOpenAIServer.start().then(() => {
done();
}).catch((error_: unknown) => {
done(error_ as Error);
});
} catch (error) {
done(error as Error);
}
});
// Mock OpenAI server cleanup - for scenarios using mock OpenAI
After({ tags: '@mockOpenAI' }, async function(this: ApplicationWorld) {
// Stop mock OpenAI server with timeout protection
if (this.mockOpenAIServer) {
try {
await Promise.race([
this.mockOpenAIServer.stop(),
new Promise<void>((resolve) => setTimeout(resolve, 2000)),
]);
} catch {
// Ignore errors during cleanup
} finally {
this.mockOpenAIServer = undefined;
}
}
});
// Only keep agent-specific steps that can't use generic ones
Then('I should see {int} messages in chat history', async function(this: ApplicationWorld, expectedCount: number) {
const currentWindow = this.currentWindow || this.mainWindow;
if (!currentWindow) {
throw new Error('No current window is available');
}
// Use precise selector based on the provided HTML structure
const messageSelector = '[data-testid="message-bubble"]';
try {
// Wait for messages to reach expected count, checking periodically for streaming
for (let attempt = 1; attempt <= expectedCount * 3; attempt++) {
try {
// Wait for at least one message to exist
await currentWindow.waitForSelector(messageSelector, { timeout: 5000 });
// Count current messages
const messages = currentWindow.locator(messageSelector);
const currentCount = await messages.count();
if (currentCount === expectedCount) {
return;
} else if (currentCount > expectedCount) {
throw new Error(`Expected ${expectedCount} messages but found ${currentCount} (too many)`);
}
// If not enough messages yet, wait a bit more for streaming
if (attempt < expectedCount * 3) {
await currentWindow.waitForTimeout(2000);
}
} catch (timeoutError) {
if (attempt === expectedCount * 3) {
throw timeoutError;
}
}
}
// Final attempt to get the count
const finalCount = await currentWindow.locator(messageSelector).count();
throw new Error(`Expected ${expectedCount} messages but found ${finalCount} after waiting for streaming to complete`);
} catch (error) {
throw new Error(`Could not find expected ${expectedCount} messages. Error: ${(error as Error).message}`);
}
});
Then('the last AI request should contain system prompt {string}', async function(this: ApplicationWorld, expectedPrompt: string) {
if (!this.mockOpenAIServer) {
throw new Error('Mock OpenAI server is not running');
}
const lastRequest = this.mockOpenAIServer.getLastRequest();
if (!lastRequest) {
throw new Error('No AI request has been made yet');
}
// Find system message in the request
const systemMessage = lastRequest.messages.find(message => message.role === 'system');
if (!systemMessage) {
throw new Error('No system message found in the AI request');
}
if (!systemMessage.content || !systemMessage.content.includes(expectedPrompt)) {
throw new Error(`Expected system prompt to contain "${expectedPrompt}", but got: "${systemMessage.content}"`);
}
});
Then('the last AI request should have {int} messages', async function(this: ApplicationWorld, expectedCount: number) {
if (!this.mockOpenAIServer) {
throw new Error('Mock OpenAI server is not running');
}
const lastRequest = this.mockOpenAIServer.getLastRequest();
if (!lastRequest) {
throw new Error('No AI request has been made yet');
}
const actualCount = lastRequest.messages.length;
if (actualCount !== expectedCount) {
throw new Error(`Expected ${expectedCount} messages in the AI request, but got ${actualCount}`);
}
});
// Shared provider config used across steps (kept at module scope for reuse)
const providerConfig: AIProviderConfig = {
provider: 'TestProvider',
baseURL: 'http://127.0.0.1:15121/v1',
models: [
{ name: 'test-model', features: ['language'] },
{ name: 'test-embedding-model', features: ['language', 'embedding'] },
{ name: 'test-speech-model', features: ['speech'] },
],
providerClass: 'openAICompatible',
isPreset: false,
enabled: true,
};
const desiredModelParameters = { temperature: 0.7, systemPrompt: 'You are a helpful assistant.', topP: 0.95 };
Given('I ensure test ai settings exists', function() {
// Build expected aiSettings from shared providerConfig and compare with actual
const modelsArray = providerConfig.models;
const modelName = modelsArray[0]?.name;
const providerName = providerConfig.provider;
const parsed = fs.readJsonSync(settingsPath) as Record<string, unknown>;
const actual = (parsed.aiSettings as Record<string, unknown> | undefined) || null;
if (!actual) {
throw new Error('aiSettings not found in settings file');
}
const actualProviders = (actual.providers as Array<Record<string, unknown>>) || [];
// Check TestProvider exists
const testProvider = actualProviders.find(p => p.provider === providerName);
if (!testProvider) {
console.error('TestProvider not found in actual providers:', JSON.stringify(actualProviders, null, 2));
throw new Error('TestProvider not found in aiSettings');
}
// Verify TestProvider configuration
if (!isEqual(testProvider, providerConfig)) {
console.error('TestProvider config mismatch. expected:', JSON.stringify(providerConfig, null, 2));
console.error('TestProvider config actual:', JSON.stringify(testProvider, null, 2));
throw new Error('TestProvider configuration does not match expected');
}
// Check ComfyUI provider exists
const comfyuiProvider = actualProviders.find(p => p.provider === 'comfyui');
if (!comfyuiProvider) {
console.error('ComfyUI provider not found in actual providers:', JSON.stringify(actualProviders, null, 2));
throw new Error('ComfyUI provider not found in aiSettings');
}
// Verify ComfyUI has test-flux model with workflow path
const comfyuiModels = (comfyuiProvider.models as Array<Record<string, unknown>>) || [];
const testFluxModel = comfyuiModels.find(m => m.name === 'test-flux');
if (!testFluxModel) {
console.error('test-flux model not found in ComfyUI models:', JSON.stringify(comfyuiModels, null, 2));
throw new Error('test-flux model not found in ComfyUI provider');
}
// Verify workflow path
const parameters = testFluxModel.parameters as Record<string, unknown> | undefined;
if (!parameters || parameters.workflowPath !== 'C:/test/mock/workflow.json') {
console.error('Workflow path mismatch. expected: C:/test/mock/workflow.json, actual:', parameters?.workflowPath);
throw new Error('Workflow path not correctly saved');
}
// Verify default config
const defaultConfig = actual.defaultConfig as Record<string, unknown>;
const api = defaultConfig.api as Record<string, unknown>;
if (api.provider !== providerName || api.model !== modelName) {
console.error('Default config mismatch. expected provider:', providerName, 'model:', modelName);
console.error('actual api:', JSON.stringify(api, null, 2));
throw new Error('Default configuration does not match expected');
}
});
Given('I add test ai settings', function() {
let existing = {} as ISettingFile;
if (fs.existsSync(settingsPath)) {
existing = fs.readJsonSync(settingsPath) as ISettingFile;
} else {
// ensure settings directory exists so writeJsonSync won't fail
fs.ensureDirSync(path.dirname(settingsPath));
}
const modelsArray = providerConfig.models;
const modelName = modelsArray[0]?.name;
const embeddingModelName = modelsArray[1]?.name;
const speechModelName = modelsArray[2]?.name;
const newAi: AIGlobalSettings = {
providers: [providerConfig],
defaultConfig: {
api: {
provider: providerConfig.provider,
model: modelName,
embeddingModel: embeddingModelName,
speechModel: speechModelName,
},
modelParameters: desiredModelParameters,
},
};
fs.writeJsonSync(settingsPath, { ...existing, aiSettings: newAi } as ISettingFile, { spaces: 2 });
});
Given('I clear test ai settings', function() {
if (!fs.existsSync(settingsPath)) return;
const parsed = fs.readJsonSync(settingsPath) as ISettingFile;
const cleaned = omit(parsed, ['aiSettings']);
fs.writeJsonSync(settingsPath, cleaned, { spaces: 2 });
});

View file

@ -0,0 +1,214 @@
import { After, AfterStep, Before, setWorldConstructor, When } from '@cucumber/cucumber';
import fs from 'fs-extra';
import path from 'path';
import { _electron as electron } from 'playwright';
import type { ElectronApplication, Page } from 'playwright';
import { isMainWindowPage, PageType } from '../../src/constants/pageTypes';
import { MockOpenAIServer } from '../supports/mockOpenAI';
import { logsDirectory, makeSlugPath, screenshotsDirectory } from '../supports/paths';
import { getPackedAppPath } from '../supports/paths';
export class ApplicationWorld {
app: ElectronApplication | undefined;
mainWindow: Page | undefined; // Keep for compatibility during transition
currentWindow: Page | undefined; // New state-managed current window
mockOpenAIServer: MockOpenAIServer | undefined;
async getWindow(windowType: string = 'main'): Promise<Page | undefined> {
if (!this.app) return undefined;
for (let attempt = 0; attempt < 3; attempt++) {
const pages = this.app.windows();
const extractFragment = (url: string) => {
if (!url) return '';
const afterHash = url.includes('#') ? url.split('#').slice(1).join('#') : '';
// remove leading slashes or colons like '/preferences' or ':Index'
return afterHash.replace(/^[:/]+/, '').split(/[/?#]/)[0] || '';
};
if (windowType === 'main') {
const mainWindow = pages.find(page => {
const pageType = extractFragment(page.url());
// file:///C:/Users/linonetwo/Documents/repo-c/TidGi-Desktop/out/TidGi-win32-x64/resources/app.asar/.webpack/renderer/main_window/index.html#/guide
// file:///...#/guide or tidgi://.../#:Index based on different workspace
return isMainWindowPage(pageType as PageType | undefined);
});
if (mainWindow) return mainWindow;
} else if (windowType === 'current') {
if (this.currentWindow) return this.currentWindow;
} else {
// match windows more flexibly by checking the full URL and fragment for the windowType
const specificWindow = pages.find(page => {
const rawUrl = page.url() || '';
const frag = extractFragment(rawUrl);
// Case-insensitive full-url match first (handles variants like '#:Index' or custom schemes)
if (rawUrl.toLowerCase().includes(windowType.toLowerCase())) return true;
// Fallback to fragment inclusion
return frag.toLowerCase().includes(windowType.toLowerCase());
});
if (specificWindow) return specificWindow;
}
// If window not found, wait 1 second and retry (except for the last attempt)
if (attempt < 2) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
return undefined;
}
}
setWorldConstructor(ApplicationWorld);
// setDefaultTimeout(50000);
Before(function(this: ApplicationWorld) {
// Create necessary directories under userData-test/logs to match appPaths in dev/test
if (!fs.existsSync(logsDirectory)) {
fs.mkdirSync(logsDirectory, { recursive: true });
}
// Create screenshots subdirectory in logs
if (!fs.existsSync(screenshotsDirectory)) {
fs.mkdirSync(screenshotsDirectory, { recursive: true });
}
});
After(async function(this: ApplicationWorld) {
if (this.app) {
try {
await this.app.close();
} catch (error) {
console.error('Error during cleanup:', error);
}
this.app = undefined;
this.mainWindow = undefined;
this.currentWindow = undefined;
}
});
AfterStep(async function(this: ApplicationWorld, { pickle, pickleStep, result }) {
// Only take screenshots in CI environment
// if (!process.env.CI) return;
try {
// Prefer an existing currentWindow if it's still open
let pageToUse: Page | undefined;
if (this.currentWindow && !this.currentWindow.isClosed()) {
pageToUse = this.currentWindow;
}
// If currentWindow is not available, try to re-acquire any open window from the app
if ((!pageToUse || pageToUse.isClosed()) && this.app) {
const openPages = this.app.windows().filter(p => !p.isClosed());
if (openPages.length > 0) {
pageToUse = openPages[0];
this.currentWindow = pageToUse;
}
}
const scenarioName = pickle.name;
const cleanScenarioName = makeSlugPath(scenarioName);
const stepText = pickleStep.text;
const cleanStepText = makeSlugPath(stepText, 120);
const stepStatus = result && typeof result.status === 'string' ? result.status : 'unknown-status';
const featureDirectory = path.resolve(screenshotsDirectory, cleanScenarioName);
// Create directory asynchronously to avoid blocking the event loop in CI
await fs.ensureDir(featureDirectory);
// Sometimes window close and don't wait for use to take picture, or window haven't open in this step, never mind, just skip.
/**
* Typical steps like:
* - I add test ai settings
* - I cleanup test wiki
* - I clear test ai settings
*/
if (!pageToUse || pageToUse.isClosed()) {
// console.warn(`Skipping screenshot: ${cleanStepText}`);
return;
}
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const screenshotPath = path.resolve(featureDirectory, `${timestamp}-${cleanStepText}-${stepStatus}.jpg`);
// Use conservative screenshot options for CI
await pageToUse.screenshot({ path: screenshotPath, fullPage: true, type: 'jpeg', quality: 10 });
} catch (screenshotError) {
console.warn('Failed to take screenshot:', screenshotError);
}
});
When('I launch the TidGi application', async function(this: ApplicationWorld) {
// For E2E tests on dev mode, use the packaged test version with NODE_ENV environment variable baked in
const packedAppPath = getPackedAppPath();
try {
this.app = await electron.launch({
executablePath: packedAppPath,
// Add debugging options to prevent app from closing and CI-specific args
args: [
'--no-sandbox',
'--disable-dev-shm-usage',
'--disable-gpu',
'--disable-software-rasterizer',
'--disable-background-timer-throttling',
'--disable-backgrounding-occluded-windows',
'--disable-renderer-backgrounding',
'--disable-features=TranslateUI',
'--disable-ipc-flooding-protection',
'--force-device-scale-factor=1',
'--high-dpi-support=1',
'--force-color-profile=srgb',
'--disable-extensions',
'--disable-plugins',
'--disable-default-apps',
'--virtual-time-budget=1000',
'--run-all-compositor-stages-before-draw',
'--disable-checker-imaging',
// Linux CI specific arguments
...(process.env.CI && process.platform === 'linux'
? [
'--disable-background-mode',
'--disable-features=VizDisplayCompositor',
'--use-gl=swiftshader',
'--disable-accelerated-2d-canvas',
'--disable-accelerated-jpeg-decoding',
'--disable-accelerated-mjpeg-decode',
'--disable-accelerated-video-decode',
]
: []),
],
env: {
...process.env,
NODE_ENV: 'test',
E2E_TEST: 'true',
// Ensure tests run in Chinese locale so i18n UI strings match expectations
// set multiple variables for cross-platform coverage
LANG: process.env.LANG || 'zh-Hans.UTF-8',
LANGUAGE: process.env.LANGUAGE || 'zh-Hans:zh',
LC_ALL: process.env.LC_ALL || 'zh-Hans.UTF-8',
// Force display settings for CI
ELECTRON_DISABLE_SECURITY_WARNINGS: 'true',
...(process.env.CI && {
ELECTRON_ENABLE_LOGGING: 'true',
ELECTRON_DISABLE_HARDWARE_ACCELERATION: 'true',
}),
},
timeout: 30000, // Increase timeout to 30 seconds for CI
});
// Wait longer for window in CI environment
const windowTimeout = process.env.CI ? 45000 : 10000;
this.mainWindow = await this.app.firstWindow({ timeout: windowTimeout });
this.currentWindow = this.mainWindow;
} catch (error) {
throw new Error(
`Failed to launch TidGi application: ${error as Error}. You should run \`pnpm run package\` before running the tests to ensure the app is built, and build with binaries like "dugite" and "tiddlywiki", see scripts/afterPack.js for more details.`,
);
}
});

View file

@ -1,48 +0,0 @@
/* eslint-disable @typescript-eslint/no-unused-expressions */
import { Given, setWorldConstructor, Then } from '@cucumber/cucumber';
import { delay } from 'bluebird';
import { expect } from 'chai';
import { TidGiWorld } from '../supports/world';
setWorldConstructor(TidGiWorld);
Given('the app is launched', async function(this: TidGiWorld) {
await delay(100);
await this.start();
const windowCount = await this.app?.client?.getWindowCount();
expect(windowCount).equal(1);
});
Then('the element {string} is on the page', async function(this: TidGiWorld, elementSelector: string) {
const result = await this.getElement(elementSelector);
expect(result).to.not.be.undefined;
this.updateContext({ previousElement: result });
});
Then('click on this element', async function(this: TidGiWorld) {
expect(this.context?.previousElement).to.not.be.undefined;
if (this.context?.previousElement !== undefined) {
await this.context.previousElement.click();
}
});
Then('click on {string} element', async function(this: TidGiWorld, elementSelector: string) {
const result = await this.getElement(elementSelector);
expect(result).to.not.be.undefined;
if (result !== undefined) {
this.updateContext({ previousElement: result });
await result.click();
}
});
Then('{string} window show up', async function(this: TidGiWorld, windowName: string) {
// await delay(1000);
const windowCount = await this.app?.client?.getWindowCount();
expect(windowCount).equal(2);
const handles = await this.app?.client?.getWindowHandles();
expect(handles).to.not.be.undefined;
if (handles !== undefined) {
await this.app?.client?.switchToWindow(handles[1]);
await this.waitReactReady();
const currentTitle = await this.app?.client?.getTitle();
expect(currentTitle).to.be.equal(windowName);
}
});

View file

@ -0,0 +1,19 @@
import { DataTable, Then } from '@cucumber/cucumber';
import fs from 'fs';
import path from 'path';
import { logsDirectory } from '../supports/paths';
import { ApplicationWorld } from './application';
Then('I should find log entries containing', async function(this: ApplicationWorld, dataTable: DataTable | undefined) {
const expectedRows = dataTable?.raw().map((r: string[]) => r[0]);
// Only consider normal daily log files like TidGi-2025-08-27.log and exclude exception logs
const files = fs.readdirSync(logsDirectory).filter((f) => /TidGi-\d{4}-\d{2}-\d{2}\.log$/.test(f));
const latestLogFilePath = files.length > 0 ? files.sort().reverse()[0] : null;
const content = latestLogFilePath ? fs.readFileSync(path.join(logsDirectory, latestLogFilePath), 'utf8') : '<no-log-found>';
const missing = expectedRows?.filter((r: string) => !content.includes(r));
if (missing?.length) {
throw new Error(`Missing expected log messages "${missing.map(item => item.slice(0, 10)).join('...", "')}..." on latest log file: ${latestLogFilePath}`);
}
});

View file

@ -0,0 +1,260 @@
import { DataTable, Then, When } from '@cucumber/cucumber';
import type { ApplicationWorld } from './application';
When('I wait for {float} seconds', async function(this: ApplicationWorld, seconds: number) {
await new Promise(resolve => setTimeout(resolve, seconds * 1000));
});
When('I wait for the page to load completely', async function(this: ApplicationWorld) {
const currentWindow = this.currentWindow || this.mainWindow;
await currentWindow?.waitForLoadState('networkidle', { timeout: 30000 });
});
Then('I should see a(n) {string} element with selector {string}', async function(this: ApplicationWorld, elementComment: string, selector: string) {
const currentWindow = this.currentWindow || this.mainWindow;
try {
await currentWindow?.waitForSelector(selector, { timeout: 10000 });
const isVisible = await currentWindow?.isVisible(selector);
if (!isVisible) {
throw new Error(`Element "${elementComment}" with selector "${selector}" is not visible`);
}
} catch (error) {
throw new Error(`Failed to find ${elementComment} with selector "${selector}": ${error as Error}`);
}
});
Then('I should see {string} elements with selectors:', async function(this: ApplicationWorld, elementDescriptions: string, dataTable: DataTable) {
const currentWindow = this.currentWindow || this.mainWindow;
if (!currentWindow) {
throw new Error('No current window is available');
}
const descriptions = elementDescriptions.split(' and ').map(d => d.trim());
const rows = dataTable.raw();
const errors: string[] = [];
if (descriptions.length !== rows.length) {
throw new Error(`Mismatch: ${descriptions.length} element descriptions but ${rows.length} selectors provided`);
}
// Check all elements in parallel for better performance
await Promise.all(rows.map(async ([selector], index) => {
const elementComment = descriptions[index];
try {
await currentWindow.waitForSelector(selector, { timeout: 10000 });
const isVisible = await currentWindow.isVisible(selector);
if (!isVisible) {
errors.push(`Element "${elementComment}" with selector "${selector}" is not visible`);
}
} catch (error) {
errors.push(`Failed to find "${elementComment}" with selector "${selector}": ${error as Error}`);
}
}));
if (errors.length > 0) {
throw new Error(`Failed to find elements:\n${errors.join('\n')}`);
}
});
Then('I should not see a(n) {string} element with selector {string}', async function(this: ApplicationWorld, elementComment: string, selector: string) {
const currentWindow = this.currentWindow || this.mainWindow;
if (!currentWindow) {
throw new Error('No current window is available');
}
try {
const element = currentWindow.locator(selector).first();
const count = await element.count();
if (count > 0) {
const isVisible = await element.isVisible();
if (isVisible) {
throw new Error(`Element "${elementComment}" with selector "${selector}" should not be visible but was found`);
}
}
// Element not found or not visible - this is expected
} catch (error) {
// If the error is our custom error, rethrow it
if (error instanceof Error && error.message.includes('should not be visible')) {
throw error;
}
// Otherwise, element not found is expected - pass the test
}
});
When('I click on a(n) {string} element with selector {string}', async function(this: ApplicationWorld, elementComment: string, selector: string) {
const targetWindow = await this.getWindow('current');
if (!targetWindow) {
throw new Error(`Window "current" is not available`);
}
try {
await targetWindow.waitForSelector(selector, { timeout: 10000 });
const isVisible = await targetWindow.isVisible(selector);
if (!isVisible) {
throw new Error(`Element "${elementComment}" with selector "${selector}" is not visible`);
}
await targetWindow.click(selector);
} catch (error) {
throw new Error(`Failed to find and click ${elementComment} with selector "${selector}" in current window: ${error as Error}`);
}
});
When('I click on {string} elements with selectors:', async function(this: ApplicationWorld, elementDescriptions: string, dataTable: DataTable) {
const targetWindow = await this.getWindow('current');
if (!targetWindow) {
throw new Error('Window "current" is not available');
}
const descriptions = elementDescriptions.split(' and ').map(d => d.trim());
const rows = dataTable.raw();
const errors: string[] = [];
if (descriptions.length !== rows.length) {
throw new Error(`Mismatch: ${descriptions.length} element descriptions but ${rows.length} selectors provided`);
}
// Click elements sequentially (not in parallel) to maintain order and avoid race conditions
for (let index = 0; index < rows.length; index++) {
const [selector] = rows[index];
const elementComment = descriptions[index];
try {
await targetWindow.waitForSelector(selector, { timeout: 10000 });
const isVisible = await targetWindow.isVisible(selector);
if (!isVisible) {
errors.push(`Element "${elementComment}" with selector "${selector}" is not visible`);
continue;
}
await targetWindow.click(selector);
} catch (error) {
errors.push(`Failed to find and click "${elementComment}" with selector "${selector}": ${error as Error}`);
}
}
if (errors.length > 0) {
throw new Error(`Failed to click elements:\n${errors.join('\n')}`);
}
});
When('I right-click on a(n) {string} element with selector {string}', async function(this: ApplicationWorld, elementComment: string, selector: string) {
const targetWindow = await this.getWindow('current');
if (!targetWindow) {
throw new Error(`Window "current" is not available`);
}
try {
await targetWindow.waitForSelector(selector, { timeout: 10000 });
const isVisible = await targetWindow.isVisible(selector);
if (!isVisible) {
throw new Error(`Element "${elementComment}" with selector "${selector}" is not visible`);
}
await targetWindow.click(selector, { button: 'right' });
} catch (error) {
throw new Error(`Failed to find and right-click ${elementComment} with selector "${selector}" in current window: ${error as Error}`);
}
});
When('I click all {string} elements matching selector {string}', async function(this: ApplicationWorld, elementComment: string, selector: string) {
const win = this.currentWindow || this.mainWindow;
if (!win) throw new Error('No active window available to click elements');
const locator = win.locator(selector);
const count = await locator.count();
if (count === 0) {
throw new Error(`No elements found for ${elementComment} with selector "${selector}"`);
}
// Single-pass reverse iteration to avoid index shift issues
for (let index = count - 1; index >= 0; index--) {
try {
await locator.nth(index).scrollIntoViewIfNeeded().catch(() => {});
await locator.nth(index).click({ force: true, timeout: 500 });
} catch (error) {
throw new Error(`Failed to click ${elementComment} at index ${index} with selector "${selector}": ${error as Error}`);
}
}
});
When('I type {string} in {string} element with selector {string}', async function(this: ApplicationWorld, text: string, elementComment: string, selector: string) {
const currentWindow = this.currentWindow || this.mainWindow;
if (!currentWindow) {
throw new Error('No current window is available');
}
try {
await currentWindow.waitForSelector(selector, { timeout: 10000 });
const element = currentWindow.locator(selector);
await element.fill(text);
} catch (error) {
throw new Error(`Failed to type in ${elementComment} element with selector "${selector}": ${error as Error}`);
}
});
When('I clear text in {string} element with selector {string}', async function(this: ApplicationWorld, elementComment: string, selector: string) {
const currentWindow = this.currentWindow || this.mainWindow;
if (!currentWindow) {
throw new Error('No current window is available');
}
try {
await currentWindow.waitForSelector(selector, { timeout: 10000 });
const element = currentWindow.locator(selector);
await element.clear();
} catch (error) {
throw new Error(`Failed to clear text in ${elementComment} element with selector "${selector}": ${error as Error}`);
}
});
When('the window title should contain {string}', async function(this: ApplicationWorld, expectedTitle: string) {
const currentWindow = this.currentWindow || this.mainWindow;
if (!currentWindow) {
throw new Error('No current window is available');
}
try {
const title = await currentWindow.title();
if (!title.includes(expectedTitle)) {
throw new Error(`Window title "${title}" does not contain "${expectedTitle}"`);
}
} catch (error) {
throw new Error(`Failed to check window title: ${error as Error}`);
}
});
// Generic keyboard action
When('I press {string} key', async function(this: ApplicationWorld, key: string) {
const currentWindow = this.currentWindow;
if (!currentWindow) {
throw new Error('No current window is available');
}
await currentWindow.keyboard.press(key);
});
// Generic window switching - sets currentWindow state for subsequent operations
// You may need to wait a second before switch, otherwise window's URL may not set yet.
When('I switch to {string} window', async function(this: ApplicationWorld, windowType: string) {
if (!this.app) {
throw new Error('Application is not available');
}
const targetWindow = await this.getWindow(windowType);
if (targetWindow) {
this.currentWindow = targetWindow; // Set currentWindow state
} else {
throw new Error(`Could not find ${windowType} window`);
}
});
// Generic window closing
When('I close {string} window', async function(this: ApplicationWorld, windowType: string) {
if (!this.app) {
throw new Error('Application is not available');
}
const targetWindow = await this.getWindow(windowType);
if (targetWindow) {
await targetWindow.close();
} else {
throw new Error(`Could not find ${windowType} window to close`);
}
});

View file

@ -0,0 +1,21 @@
import { When } from '@cucumber/cucumber';
import fs from 'fs-extra';
import type { IWorkspace } from '../../src/services/workspaces/interface';
import { settingsPath, wikiTestWikiPath } from '../supports/paths';
When('I cleanup test wiki', async function() {
if (fs.existsSync(wikiTestWikiPath)) fs.removeSync(wikiTestWikiPath);
type SettingsFile = { workspaces?: Record<string, IWorkspace> } & Record<string, unknown>;
if (!fs.existsSync(settingsPath)) return;
const settings = fs.readJsonSync(settingsPath) as SettingsFile;
const workspaces: Record<string, IWorkspace> = settings.workspaces ?? {};
const filtered: Record<string, IWorkspace> = {};
for (const id of Object.keys(workspaces)) {
const ws = workspaces[id];
const name = ws.name;
if (name === 'wiki' || id === 'wiki') continue;
filtered[id] = ws;
}
fs.writeJsonSync(settingsPath, { ...settings, workspaces: filtered }, { spaces: 2 });
});

View file

@ -1,33 +0,0 @@
import { After, Before } from '@cucumber/cucumber';
import fs from 'fs-extra';
import { SETTINGS_FOLDER } from '../../src/constants/appPaths';
import { DEFAULT_WIKI_FOLDER } from '../../src/constants/paths';
import { TidGiWorld } from './world';
Before(async function() {
// clear setting folder
await fs.remove(SETTINGS_FOLDER);
await fs.remove(DEFAULT_WIKI_FOLDER);
});
After(async function(this: TidGiWorld, testCase) {
// print logs if test failed
// if (this.app !== undefined && testCase.result?.status === Status.FAILED) {
// console.log('main:\n---\n');
// // FIXME: TypeError: this.app.client.getMainProcessLogs is not a function
// await this.app.client.getMainProcessLogs().then(function (logs) {
// logs.forEach(function (log) {
// console.log(log, '\n');
// });
// });
// console.log('renderer:\n---\n');
// await this.app.client.getRenderProcessLogs().then(function (logs) {
// logs.forEach(function (log) {
// console.log(JSON.stringify(log), '\n');
// });
// });
// console.log('\n');
// }
await this.close();
});

View file

@ -0,0 +1,315 @@
import type { CoreMessage } from 'ai';
import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
import type { AiAPIConfig } from '../../src/services/agentInstance/promptConcat/promptConcatSchema';
import { streamFromProvider } from '../../src/services/externalAPI/callProviderAPI';
import type { AIProviderConfig } from '../../src/services/externalAPI/interface';
import { MockOpenAIServer } from '../supports/mockOpenAI';
describe('Mock OpenAI Server', () => {
let server: MockOpenAIServer;
beforeAll(async () => {
const rules = [
// Call 1: Wiki search tool use
{
response: '<tool_use name="wiki-search">{"workspaceName":"-VPTqPdNOEZHGO5vkwllY","filter":"[title[Index]]"}</tool_use>',
stream: false,
},
// Call 2: Wiki search explanation
{
response: '在 TiddlyWiki 中Index 条目提供了编辑卡片的方法说明,点击右上角的编辑按钮可以开始对当前卡片进行编辑。',
stream: false,
},
// Call 3: Wiki operation with default workspace (will fail)
{
response: '<tool_use name="wiki-operation">{"workspaceName":"default","operation":"wiki-add-tiddler","title":"testNote","text":"test"}</tool_use>',
stream: false,
},
// Call 4: Wiki operation with wiki workspace (will succeed)
{
response: '<tool_use name="wiki-operation">{"workspaceName":"wiki","operation":"wiki-add-tiddler","title":"test","text":"这是测试内容"}</tool_use>',
stream: false,
},
// Call 5: Wiki operation confirmation
{
response: '已成功在工作区 wiki 中创建条目 "test"。',
stream: false,
},
// Call 6: General test response
{
response: '这是一个测试响应。',
stream: false,
},
];
server = new MockOpenAIServer(undefined, rules);
await server.start();
});
beforeEach(async () => {
// Reset call count before each test
await fetch(`${server.baseUrl}/reset`, { method: 'POST' });
});
afterAll(async () => {
await server.stop();
});
it('should return valid chat completion with tool call (first API call)', async () => {
const response = await fetch(`${server.baseUrl}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer test-key',
},
body: JSON.stringify({
model: 'test-model',
messages: [
{
role: 'user',
content: '搜索 wiki 中的 index 条目并解释',
},
],
}),
});
expect(response.status).toBe(200);
const data = await response.json();
expect(data).toHaveProperty('id');
expect(data).toHaveProperty('object', 'chat.completion');
expect(data).toHaveProperty('created');
expect(data).toHaveProperty('model', 'test-model'); // Verify it returns the requested model
expect(data).toHaveProperty('choices');
expect(data.choices).toHaveLength(1);
expect(data.choices[0]).toHaveProperty('message');
expect(data.choices[0].message).toHaveProperty('content');
expect(data.choices[0].message.content).toContain('<tool_use name="wiki-search">');
expect(data.choices[0].message.content).toContain('workspaceName');
expect(data.choices[0].message.content).toContain('-VPTqPdNOEZHGO5vkwllY');
expect(data.choices[0].finish_reason).toBe('stop');
});
it('should return valid chat completion with tool result response (second API call)', async () => {
// This simulates the second API call in a conversation
const response = await fetch(`${server.baseUrl}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer test-key',
},
body: JSON.stringify({
model: 'test-model', // Use the same model as in feature test
messages: [
{
role: 'user',
content: '搜索 wiki 中的 index 条目并解释',
},
{
role: 'assistant',
content: '<tool_use name="wiki-search">{"workspaceName":"-VPTqPdNOEZHGO5vkwllY","filter":"[title[Index]]"}</tool_use>',
},
{
role: 'tool',
content:
'Tool: wiki-search\nParameters: {"workspaceName":"-VPTqPdNOEZHGO5vkwllY","filter":"[title[Index]]"}\nError: Workspace with name or ID "-VPTqPdNOEZHGO5vkwllY" does not exist. Available workspaces: wiki (abc123), agent (agent), help (help), guide (guide), add (add)',
},
],
}),
});
expect(response.status).toBe(200);
const data = await response.json();
expect(data.choices[0].message.role).toBe('assistant');
// Each test is reset, so this is also the first call returning wiki-search tool use
expect(data.choices[0].message.content).toContain('<tool_use name="wiki-search">');
expect(data.choices[0].finish_reason).toBe('stop');
expect(data.model).toBe('test-model'); // Verify it returns the requested model
});
it('should work with different model names (first API call)', async () => {
const response = await fetch(`${server.baseUrl}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer test-key',
},
body: JSON.stringify({
model: 'custom-model-name',
messages: [
{
role: 'user',
content: 'Hello',
},
],
}),
});
expect(response.status).toBe(200);
const data = await response.json();
expect(data.model).toBe('custom-model-name');
expect(data.choices[0].message.role).toBe('assistant');
// First call returns wiki-search tool use, not the Hello response
expect(data.choices[0].message.content).toContain('<tool_use name="wiki-search">');
});
it('should support streaming response (first API call)', async () => {
const response = await fetch(`${server.baseUrl}/v1/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer test-key',
},
body: JSON.stringify({
model: 'test-model',
stream: true,
messages: [
{
role: 'user',
content: 'Hello',
},
],
}),
});
expect(response.status).toBe(200);
expect(response.headers.get('content-type')).toBe('text/plain; charset=utf-8');
const reader = response.body?.getReader();
const decoder = new TextDecoder();
let chunks = '';
if (reader) {
let done = false;
while (!done) {
const { value, done: readerDone } = await reader.read();
done = readerDone;
if (value) {
chunks += decoder.decode(value);
}
}
}
expect(chunks).toContain('data:');
expect(chunks).toContain('[DONE]');
expect(chunks).toContain('chat.completion.chunk');
// First call returns wiki-search tool use (check for JSON-escaped content)
expect(chunks).toContain('wiki-search');
});
it('should reproduce exact three-call conversation (wiki search + wiki operation)', async () => {
// Call 1: First API call returns wiki search tool use
let res = await fetch(`${server.baseUrl}/v1/chat/completions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer test-key' },
body: JSON.stringify({ model: 'test-model', messages: [{ role: 'user', content: '搜索 wiki 中的 index 条目并解释' }] }),
});
expect(res.status).toBe(200);
let data = await res.json();
expect(String(data.choices[0].message.content)).toBe(
'<tool_use name="wiki-search">{"workspaceName":"-VPTqPdNOEZHGO5vkwllY","filter":"[title[Index]]"}</tool_use>',
);
// Call 2: Second API call returns explanation
res = await fetch(`${server.baseUrl}/v1/chat/completions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer test-key' },
body: JSON.stringify({
model: 'test-model',
messages: [
{ role: 'user', content: '搜索 wiki 中的 index 条目并解释' },
{ role: 'assistant', content: '<tool_use name="wiki-search">{"workspaceName":"-VPTqPdNOEZHGO5vkwllY","filter":"[title[Index]]"}</tool_use>' },
{ role: 'tool', content: 'Tool: wiki-search\nParameters: ...\nError: Workspace not found' },
],
}),
});
expect(res.status).toBe(200);
data = await res.json();
expect(String(data.choices[0].message.content)).toContain('TiddlyWiki 中Index 条目提供了编辑卡片的方法说明');
// Call 3: Third API call (start wiki operation) returns default workspace tool use
res = await fetch(`${server.baseUrl}/v1/chat/completions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: 'Bearer test-key' },
body: JSON.stringify({
model: 'test-model',
messages: [
{ role: 'user', content: '在 wiki 里创建一个新笔记,内容为 test' },
],
}),
});
expect(res.status).toBe(200);
data = await res.json();
expect(String(data.choices[0].message.content)).toBe(
'<tool_use name="wiki-operation">{"workspaceName":"default","operation":"wiki-add-tiddler","title":"testNote","text":"test"}</tool_use>',
);
});
it('should integrate with streamFromProvider (SDK) for streaming responses', async () => {
// Reuse the existing server and update its rules to a single streaming rule
const streamingRule = [{ response: 'chunkA<stream_split>chunkB<stream_split>chunkC', stream: true }];
server.setRules(streamingRule);
// Build provider config that points to our mock server as openAICompatible
const providerConfig: AIProviderConfig = {
provider: 'TestProvider',
providerClass: 'openAICompatible',
baseURL: `${server.baseUrl}/v1`,
apiKey: 'test-key',
models: [{ name: 'test-model' }],
enabled: true,
};
const messages: CoreMessage[] = [
{ role: 'user', content: 'Start streaming' },
];
// streamFromProvider returns an object from streamText; call it and iterate
const aiConfig: AiAPIConfig = { api: { provider: 'TestProvider', model: 'test-model' }, modelParameters: {} } as AiAPIConfig;
const stream = streamFromProvider(aiConfig, messages, new AbortController().signal, providerConfig);
// The returned stream should expose `.textStream` as an AsyncIterable
// We'll collect chunks as they arrive and assert intermediate states are streaming
const receivedChunks: string[] = [];
if (!stream.textStream) throw new Error('Expected stream.textStream to be present');
for await (const chunk of stream.textStream) {
if (!chunk) continue;
let contentPiece: string | undefined;
if (typeof chunk === 'string') contentPiece = chunk;
else if (typeof chunk === 'object' && chunk !== null && 'content' in (chunk as Record<string, unknown>)) {
const c = (chunk as Record<string, unknown>).content;
if (typeof c === 'string') contentPiece = c;
}
if (contentPiece) {
// Append to receivedChunks and assert intermediate streaming behavior
receivedChunks.push(contentPiece);
// Intermediate assertions:
// - After first chunk: should contain only chunkA and not yet contain chunkC
if (receivedChunks.length === 1) {
expect(receivedChunks.join('')).toContain('chunkA');
expect(receivedChunks.join('')).not.toContain('chunkC');
}
// - After second chunk: should contain chunkA and chunkB, but not chunkC
if (receivedChunks.length === 2) {
expect(receivedChunks.join('')).toContain('chunkA');
expect(receivedChunks.join('')).toContain('chunkB');
expect(receivedChunks.join('')).not.toContain('chunkC');
}
}
}
// After stream completion, assemble chunks using the same '<stream_split>' separator used by rules
const assembled = receivedChunks.join('<stream_split>');
// streamingRule[0].response uses '<stream_split>' as separator, verify equality
expect(assembled).toBe(streamingRule[0].response);
});
});

View file

@ -0,0 +1,438 @@
import { createServer, IncomingMessage, Server, ServerResponse } from 'http';
import { AddressInfo } from 'net';
interface ChatMessage {
role: 'system' | 'user' | 'assistant' | 'tool';
content: string | null;
}
interface ChatRequest {
messages: ChatMessage[];
model?: string;
stream?: boolean;
}
interface Rule {
response: string;
stream?: boolean;
embedding?: number[]; // Optional: predefined embedding vector for this response
}
export class MockOpenAIServer {
private server: Server | null = null;
public port = 0;
public baseUrl = '';
private rules: Rule[] = [];
private callCount = 0; // Track total API calls (for chat completions)
private embeddingCallCount = 0; // Track embedding API calls separately
private lastRequest: ChatRequest | null = null; // Store the most recent request
private allRequests: ChatRequest[] = []; // Store all requests for debugging
constructor(private fixedPort?: number, rules?: Rule[]) {
if (rules && Array.isArray(rules)) this.rules = rules;
}
/**
* Update rules at runtime. This allows tests to reuse a running server
* and swap the response rules without creating a new server instance.
*/
public setRules(rules: Rule[]): void {
if (Array.isArray(rules)) {
this.rules = rules;
}
}
/**
* Get the most recent request received by the server
*/
public getLastRequest(): ChatRequest | null {
return this.lastRequest;
}
/**
* Get all requests received by the server (for debugging)
*/
public getAllRequests(): ChatRequest[] {
return this.allRequests;
}
/**
* Clear all stored requests
*/
public clearAllRequests(): void {
this.lastRequest = null;
this.allRequests = [];
this.callCount = 0;
this.embeddingCallCount = 0;
}
async start(): Promise<void> {
return new Promise((resolve, reject) => {
this.server = createServer((request: IncomingMessage, response: ServerResponse) => {
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
response.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (request.method === 'OPTIONS') {
if (!response.writableEnded && !response.headersSent) {
response.writeHead(200);
response.end();
}
return;
}
try {
const url = new URL(request.url || '', `http://127.0.0.1:${this.port}`);
if (request.method === 'GET' && url.pathname === '/health') {
if (!response.writableEnded && !response.headersSent) {
response.writeHead(200, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ status: 'ok' }));
}
return;
}
if (request.method === 'POST' && url.pathname === '/v1/chat/completions') {
void this.handleChatCompletions(request, response);
return;
}
if (request.method === 'POST' && url.pathname === '/v1/embeddings') {
void this.handleEmbeddings(request, response);
return;
}
if (request.method === 'POST' && url.pathname === '/reset') {
// Reset call count for testing
this.callCount = 0;
this.embeddingCallCount = 0;
this.lastRequest = null;
if (!response.writableEnded && !response.headersSent) {
response.writeHead(200, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ success: true, message: 'Call count reset' }));
}
return;
}
if (request.method === 'GET' && url.pathname === '/last-request') {
// Return the last received request for testing
if (!response.writableEnded && !response.headersSent) {
response.writeHead(200, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ lastRequest: this.lastRequest }));
}
return;
}
if (!response.writableEnded && !response.headersSent) {
response.writeHead(404, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ error: 'Not found' }));
}
} catch {
if (!response.writableEnded && !response.headersSent) {
response.writeHead(400, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ error: 'Bad request' }));
}
}
});
this.server.on('error', (error) => {
reject(new Error(String(error)));
});
this.server.on('listening', () => {
const addr = this.server!.address() as AddressInfo;
this.port = addr.port;
this.baseUrl = `http://127.0.0.1:${this.port}`;
resolve();
});
try {
this.server.listen(this.fixedPort || 0, '127.0.0.1');
} catch (error) {
reject(new Error(String(error)));
}
});
}
async stop(): Promise<void> {
if (!this.server) return;
return new Promise((resolve) => {
// Force close all connections before closing server
this.server!.closeAllConnections?.();
this.server!.close(() => {
this.server = null;
resolve();
});
// Fallback: force resolve after timeout to prevent hanging
setTimeout(() => {
if (this.server) {
this.server = null;
resolve();
}
}, 1000);
});
}
private async handleEmbeddings(request: IncomingMessage, response: ServerResponse) {
let body = '';
request.on('data', (chunk: Buffer) => {
body += chunk.toString();
});
request.on('end', () => {
try {
const embeddingRequest = JSON.parse(body) as { input: string | string[]; model?: string };
const inputs = Array.isArray(embeddingRequest.input) ? embeddingRequest.input : [embeddingRequest.input];
// Use embeddingCallCount to get predefined embeddings from rules
const embeddings = inputs.map((input) => {
this.embeddingCallCount++;
const ruleIndex = this.embeddingCallCount - 1;
const rule = this.rules[ruleIndex];
// Use predefined embedding from rule (generated by semantic tag in agent.ts)
if (rule?.embedding && Array.isArray(rule.embedding)) {
return rule.embedding;
}
// For UI-generated embeddings (not from agent chat), return a simple default vector
// This allows UI operations (like clicking "Generate" button in preferences) to work
const simpleVector: number[] = new Array<number>(384).fill(0);
// Add some variation based on input length to make it somewhat unique
simpleVector[0] = (input.length % 100) / 100;
return simpleVector;
});
const resp = {
object: 'list',
data: embeddings.map((embedding, index) => ({
object: 'embedding',
embedding,
index,
})),
model: embeddingRequest.model || 'text-embedding-ada-002',
usage: {
prompt_tokens: inputs.reduce((sum, input) => sum + input.length, 0),
total_tokens: inputs.reduce((sum, input) => sum + input.length, 0),
},
};
if (!response.writableEnded && !response.headersSent) {
response.setHeader('Content-Type', 'application/json');
response.writeHead(200);
response.end(JSON.stringify(resp));
}
} catch {
if (!response.writableEnded && !response.headersSent) {
response.writeHead(400, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ error: 'Invalid JSON' }));
}
}
});
}
private async handleChatCompletions(request: IncomingMessage, response: ServerResponse) {
let body = '';
request.on('data', (chunk: Buffer) => {
body += chunk.toString();
});
request.on('end', () => {
try {
// Parse request and handle each request based on provided rules
const chatRequest = JSON.parse(body) as ChatRequest;
// Store the request for testing validation
this.lastRequest = chatRequest;
this.allRequests.push(chatRequest);
if (chatRequest.stream) {
this.handleStreamingChatCompletions(chatRequest, response);
return;
}
const resp = this.generateChatCompletionResponse(chatRequest);
if (!response.writableEnded && !response.headersSent) {
response.setHeader('Content-Type', 'application/json');
response.writeHead(200);
response.end(JSON.stringify(resp));
}
} catch {
if (!response.writableEnded && !response.headersSent) {
response.writeHead(400, { 'Content-Type': 'application/json' });
response.end(JSON.stringify({ error: 'Invalid JSON' }));
}
}
});
}
private generateChatCompletionResponse(chatRequest: ChatRequest) {
const modelName = chatRequest.model || 'test-model';
// Increment call count for each API request
this.callCount++;
// Use call count to determine which response to return (1-indexed)
const ruleIndex = this.callCount - 1;
const responseRule = this.rules[ruleIndex];
if (!responseRule) {
return {
id: 'chatcmpl-test-' + Date.now().toString(),
object: 'chat.completion',
created: Math.floor(Date.now() / 1000),
model: modelName,
choices: [],
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
};
}
return {
id: 'chatcmpl-test-' + Date.now().toString(),
object: 'chat.completion',
created: Math.floor(Date.now() / 1000),
model: modelName,
choices: [
{
index: 0,
message: {
role: 'assistant',
content: responseRule.response,
},
finish_reason: 'stop',
},
],
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
};
}
private handleStreamingChatCompletions(chatRequest: ChatRequest, response: ServerResponse) {
if (response.writableEnded) return;
const modelName = chatRequest.model || 'test-model';
// Increment call count for streaming requests too
this.callCount++;
// Use call count to determine which response to return (1-indexed)
const ruleIndex = this.callCount - 1;
const responseRule = this.rules[ruleIndex];
// If matched: honor client's stream request. If client requests stream, always stream the matched.response.
if (responseRule && chatRequest.stream) {
response.setHeader('Content-Type', 'text/plain; charset=utf-8');
response.setHeader('Cache-Control', 'no-cache');
response.setHeader('Connection', 'keep-alive');
response.writeHead(200);
// Send first chunk with role
const roleChunk = {
id: 'chatcmpl-test-' + Date.now().toString(),
object: 'chat.completion.chunk',
created: Math.floor(Date.now() / 1000),
model: modelName,
choices: [
{
index: 0,
delta: { role: 'assistant' },
finish_reason: null,
},
],
};
// Send content chunks. Support multiple chunks separated by '<stream_split>'
const rawResponse = typeof responseRule.response === 'string'
? responseRule.response
: String(responseRule.response);
const chunks = rawResponse.split('<stream_split>');
const roleLine = `data: ${JSON.stringify(roleChunk)}\n\n`;
response.write(roleLine);
// Helper to write a chunk line
const writeChunkLine = (content: string) => {
const contentChunk = {
id: 'chatcmpl-test-' + Date.now().toString(),
object: 'chat.completion.chunk',
created: Math.floor(Date.now() / 1000),
model: modelName,
choices: [
{
index: 0,
delta: { content },
finish_reason: null,
},
],
};
const contentLine = `data: ${JSON.stringify(contentChunk)}\n\n`;
response.write(contentLine);
};
// Stream each chunk with a small delay to simulate streaming
// Chunks separator: '###' is used to denote chunk boundaries in the rule string
void (async () => {
for (let index = 0; index < chunks.length; index++) {
// If client closed connection, stop streaming
if (response.writableEnded) return;
writeChunkLine(chunks[index]);
// Short delay between chunks (simulate pacing).
await new Promise(resolve => setTimeout(resolve, 200));
}
// Send final empty chunk with finish_reason
if (!response.writableEnded) {
const finalChunk = {
id: 'chatcmpl-test-' + Date.now().toString(),
object: 'chat.completion.chunk',
created: Math.floor(Date.now() / 1000),
model: modelName,
choices: [
{
index: 0,
delta: {},
finish_reason: 'stop',
},
],
};
const finalLine = `data: ${JSON.stringify(finalChunk)}\n\n`;
response.write(finalLine);
response.write('data: [DONE]\n\n');
response.end();
}
})();
return;
}
// If matched but client did not request stream, return a regular JSON chat completion
if (responseRule && !chatRequest.stream) {
const resp = {
id: 'chatcmpl-test-' + Date.now().toString(),
object: 'chat.completion',
created: Math.floor(Date.now() / 1000),
model: modelName,
choices: [
{
index: 0,
message: {
role: 'assistant',
content: responseRule.response,
},
finish_reason: 'stop',
},
],
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
};
if (!response.writableEnded) {
response.setHeader('Content-Type', 'application/json');
response.writeHead(200);
response.end(JSON.stringify(resp));
}
return;
}
// Default for unmatched stream requests: send only DONE so client can close stream without producing assistant content
response.setHeader('Content-Type', 'text/plain; charset=utf-8');
response.setHeader('Cache-Control', 'no-cache');
response.setHeader('Connection', 'keep-alive');
response.writeHead(200);
response.write('data: [DONE]\n\n');
response.end();
}
}

View file

@ -0,0 +1,82 @@
import fs from 'fs';
import path from 'path';
export function getPackedAppPath(): string {
const platform = process.platform;
const outputDirectory = path.join(process.cwd(), 'out');
// Define possible app paths based on platform
const possiblePaths: string[] = [];
switch (platform) {
case 'win32':
possiblePaths.push(
path.join(outputDirectory, 'TidGi-win32-x64', 'tidgi.exe'),
path.join(outputDirectory, 'TidGi-win32-arm64', 'tidgi.exe'),
path.join(outputDirectory, 'TidGi-win32-ia32', 'tidgi.exe'),
);
break;
case 'darwin':
possiblePaths.push(
path.join(outputDirectory, 'TidGi-darwin-x64', 'TidGi.app', 'Contents', 'MacOS', 'TidGi'),
path.join(outputDirectory, 'TidGi-darwin-arm64', 'TidGi.app', 'Contents', 'MacOS', 'TidGi'),
);
break;
case 'linux':
possiblePaths.push(
path.join(outputDirectory, 'TidGi-linux-x64', 'tidgi'),
path.join(outputDirectory, 'TidGi-linux-arm64', 'tidgi'),
);
break;
default:
throw new Error(`Unsupported platform: ${platform}`);
}
// Find the first existing executable
for (const appPath of possiblePaths) {
if (fs.existsSync(appPath)) {
return appPath;
}
}
throw new Error(
`TidGi executable not found. Checked paths:\n${possiblePaths.join('\n')}\n\nYou should run \`pnpm run package:dev\` before running the tests to ensure the app is built.`,
);
}
// E2E logs paths used by tests
export const logsDirectory = path.resolve(process.cwd(), 'userData-test', 'logs');
export const screenshotsDirectory = path.resolve(logsDirectory, 'screenshots');
// Test settings paths used by E2E
export const settingsDirectory = path.resolve(process.cwd(), 'userData-test', 'settings');
export const settingsPath = path.resolve(settingsDirectory, 'settings.json');
// Repo root and test wiki paths
export const repoRoot = path.resolve(process.cwd());
export const wikiTestWikiPath = path.resolve(repoRoot, 'wiki-test', 'wiki');
// Archive-safe sanitization: generate a slug that is safe for zipping/unzipping across platforms.
// Rules:
// - allow Unicode letters/numbers (\p{L}\p{N}) and spaces, hyphen, underscore and parentheses
// - remove dots completely (to avoid trailing-dot issues on Windows)
// - replace any other char with '-' (this includes brackets, quotes, punctuation)
// - collapse multiple '-' into one, collapse multiple spaces into one, trim, and limit length
const unsafeChars = /[^\p{L}\p{N}\s\-_()]/gu;
const collapseDashes = /-+/g;
const collapseSpaces = /\s+/g;
export const makeSlugPath = (input: string | undefined, maxLength = 120) => {
let s = String(input || 'unknown').normalize('NFKC');
// remove dots explicitly
s = s.replace(/\./g, '');
// replace unsafe characters with dashes
let slug = s.replace(unsafeChars, '-');
// collapse consecutive dashes
slug = slug.replace(collapseDashes, '-');
// collapse spaces to single space, trim edges
slug = slug.replace(collapseSpaces, ' ').trim();
// trim leading/trailing dashes or spaces
slug = slug.replace(/^-+|-+$/g, '').replace(/^[\s]+|[\s]+$/g, '');
if (slug.length > maxLength) slug = slug.substring(0, maxLength);
if (!slug) slug = 'unknown';
return slug;
};

View file

@ -1,95 +0,0 @@
import { setDefaultTimeout, World } from '@cucumber/cucumber';
import path from 'path';
import { Application } from 'spectron';
// import { keyboard, Key } from '@nut-tree/nut-js';
setDefaultTimeout(30 * 1000);
const projectRoot = path.join(__dirname, '..', '..');
const packageName = process.env.npm_product_name ?? 'TidGi';
interface IContext {
previousElement?: WebdriverIO.Element;
}
/**
* Execution environment for TidGi in cucumber-js
*/
export class TidGiWorld extends World {
/** our electron app instance created by spectron */
public app?: Application;
/** store selected element and other things, so subsequent cucumber statement can get context */
public context?: IContext;
/** the compiled src/main.ts */
private readonly appPath = path.join(projectRoot, '.webpack', 'main', 'index.js');
/** cold start the electron app */
public async start(): Promise<void> {
this.app = new Application({
path: path.join(
projectRoot,
// The path to the binary depends on your platform and architecture
`out/${packageName}-darwin-x64/${packageName}.app/Contents/MacOS/${packageName}`,
),
args: [this.appPath],
chromeDriverArgs: ['--disable-extensions'],
cwd: projectRoot,
env: {
NODE_ENV: 'test',
},
port: 9156,
});
await this.app.start();
await this.waitReactReady();
}
public async getElement(selector: string): Promise<WebdriverIO.Element | undefined> {
const element = await this.app?.client?.$?.(selector);
// sometimes element exist, but has an error field
/* Element {
sessionId: 'ae55dccb0daecda748fa4239f89d03e5',
error: {
error: 'no such element',
message: 'no such element: Unable to locate element: {"method":"css selector","selector":"#test"}\n' +
' (Session info: chrome=89.0.4389.114)', */
if (element !== undefined && !('error' in element)) {
return element;
}
}
/**
* We add `<div id="test" />` to each page in react render, so we can wait until it exists
*/
public async waitReactReady(): Promise<void> {
await this?.app?.client?.waitUntil(async () => undefined !== (await this.getElement('#test')));
}
public updateContext(context: Partial<IContext>): void {
this.context = this.context === undefined ? context : { ...this.context, ...context };
}
// public async type(input: string): Promise<void> {
// await keyboard.type(input);
// }
// public async hitKey(key: Key, modifier?: Key): Promise<void> {
// if (modifier !== undefined) {
// await keyboard.pressKey(modifier);
// await keyboard.pressKey(key);
// await keyboard.releaseKey(key);
// await keyboard.releaseKey(modifier);
// } else {
// await keyboard.pressKey(key);
// await keyboard.releaseKey(key);
// }
// }
public async close(): Promise<void> {
await this.app?.stop();
}
public readClipboard(): string | undefined {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
return this.app?.electron?.clipboard?.readText?.();
}
}

22
features/tsconfig.json Normal file
View file

@ -0,0 +1,22 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"rootDir": "..",
"outDir": "../out/features",
"noEmit": true,
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"moduleResolution": "bundler",
"resolveJsonModule": true
},
"include": [
"**/*.ts",
"**/*.js",
"../src/constants/pageTypes.ts"
],
"exclude": [
"node_modules",
"**/*.d.ts"
]
}

View file

@ -0,0 +1,165 @@
Feature: Vector Search - Embedding Generation and Semantic Search
As a user
I want to use vector database to perform semantic search in my wiki
So that I can find relevant content based on meaning rather than exact keywords
Background:
Given I add test ai settings
Then I launch the TidGi application
And I wait for the page to load completely
And I should see a "page body" element with selector "body"
# Ensure we are in the agent workspace (not wiki workspace) for agent interaction
When I click on an "agent workspace button" element with selector "[data-testid='workspace-agent']"
And I should see a "new tab button" element with selector "[data-tab-id='new-tab-button']"
@vectorSearch @mockOpenAI
Scenario: Agent workflow - Create notes, update embeddings, then search
Given I have started the mock OpenAI server
| response | stream | embedding |
| <tool_use name="wiki-operation">{"workspaceName":"wiki","operation":"wiki-add-tiddler","title":"AI Agent Guide","text":"AI使"}</tool_use> | false | |
| wiki "AI Agent Guide" | false | |
| <tool_use name="wiki-operation">{"workspaceName":"wiki","operation":"wiki-add-tiddler","title":"Vector Database Tutorial","text":""}</tool_use> | false | |
| wiki "Vector Database Tutorial" | false | |
| <tool_use name="wiki-update-embeddings">{"workspaceName":"wiki","forceUpdate":false}</tool_use> | false | |
| | false | note1 |
| | false | note2 |
| wiki 22 | false | |
| <tool_use name="wiki-search">{"workspaceName":"wiki","searchType":"vector","query":"使AI","limit":5,"threshold":0.7}</tool_use> | false | |
| | false | query-note1 |
| wiki \n\n**Tiddler: AI Agent Guide** (Similarity: 95.0%)\nAI使 | false | |
# Step 1: Open agent chat interface
When I click on a "new tab button" element with selector "[data-tab-id='new-tab-button']"
And I should see a "search interface" element with selector ".aa-Autocomplete"
When I click on a "search input box" element with selector ".aa-Input"
And I should see an "autocomplete panel" element with selector ".aa-Panel"
When I click on an "agent suggestion" element with selector '[data-autocomplete-source-id="agentsSource"] .aa-ItemWrapper'
# Step 2: Create first note
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type " wiki AI Agent Guide AI使" in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
Then I should see 4 messages in chat history
# Step 3: Create second note
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type " Vector Database Tutorial " in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
Then I should see 8 messages in chat history
# Step 4: Update vector embeddings using agent tool
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type " wiki " in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
Then I should see 12 messages in chat history
# Step 5: Perform vector search
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type "使 wiki 使AI" in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
Then I should see 16 messages in chat history
# Verify the last message contains vector search results
And I should see "search result in last message" elements with selectors:
| [data-testid='message-bubble']:last-child:has-text('Tiddler: AI Agent Guide') |
@vectorSearch @mockOpenAI
Scenario: UI workflow - Generate embeddings via preferences, then search
Given I have started the mock OpenAI server
| response | stream | embedding |
| <tool_use name="wiki-operation">{"workspaceName":"wiki","operation":"wiki-add-tiddler","title":"Machine Learning Basics","text":""}</tool_use> | false | |
| wiki "Machine Learning Basics" | false | |
| | false | note3 |
| <tool_use name="wiki-search">{"workspaceName":"wiki","searchType":"vector","query":"","limit":5,"threshold":0.7}</tool_use> | false | |
| | false | query-note3 |
| wiki \n\n**Tiddler: Machine Learning Basics** (Similarity: 98.0%)\n | false | |
# Step 1: Create a test note via agent
When I click on "new tab button and create default agent button" elements with selectors:
| [data-tab-id='new-tab-button'] |
| [data-testid='create-default-agent-button'] |
And I should see a "message input box" element with selector "[data-testid='agent-message-input']"
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type " wiki Machine Learning Basics " in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
Then I should see 4 messages in chat history
# Step 2: Open preferences and manually generate embeddings via UI
When I click on a "settings button" element with selector "#open-preferences-button"
When I switch to "preferences" window
# Navigate to Search section (which contains vector database settings)
When I click on a "search section" element with selector "[data-testid='preference-section-search']"
# Wait for workspace list to load
# The Search.tsx renders workspace cards with name, status, and buttons
And I should see a "wiki workspace card" element with selector "*:has-text('wiki')"
# Click the generate button - use button text "生成" instead of data-testid
# The button shows "生成" for initial generation, "更新嵌入" after generation
When I click on a "generate button with text" element with selector "button:has-text('')"
# Verify generation completed with detailed status information
# Should show: workspace name, embedding count, note count, last updated time and action buttons
Then I should see "workspace name in status and embedding count status and embedding word and last updated label and update button after generation and delete button after generation" elements with selectors:
| *:has-text('wiki') |
| *:has-text('') |
| *:has-text('') |
| *:has-text('') |
| button:has-text('') |
| button:has-text('') |
# Close preferences
When I close "preferences" window
And I switch to "main" window
# Step 3: Perform vector search and verify results match agent workflow
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type "使 wiki " in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
Then I should see 8 messages in chat history
# Verify the last message contains vector search results
And I should see a "ML search result in last message" element with selector "[data-testid='message-bubble']:last-child:has-text('Tiddler: Machine Learning Basics')"
@vectorSearch @mockOpenAI
Scenario: Vector search with low similarity - No results below threshold, then lower threshold
Given I have started the mock OpenAI server
| response | stream | embedding |
| <tool_use name="wiki-operation">{"workspaceName":"wiki","operation":"wiki-add-tiddler","title":"AI Technology","text":""}</tool_use> | false | |
| wiki "AI Technology" | false | |
| <tool_use name="wiki-operation">{"workspaceName":"wiki","operation":"wiki-add-tiddler","title":"Machine Learning","text":""}</tool_use> | false | |
| wiki "Machine Learning" | false | |
| <tool_use name="wiki-update-embeddings">{"workspaceName":"wiki","forceUpdate":false}</tool_use> | false | |
| | false | note4 |
| | false | note5 |
| wiki 22 | false | |
| <tool_use name="wiki-search">{"workspaceName":"wiki","searchType":"vector","query":"","limit":5,"threshold":0.7}</tool_use> | false | |
| | false | unrelated |
| Wiki"wiki"0.7 | false | |
| <tool_use name="wiki-search">{"workspaceName":"wiki","searchType":"vector","query":"","limit":5,"threshold":0.1}</tool_use> | false | |
| | false | unrelated |
| wiki \n\n**Tiddler: AI Technology** (Similarity: 15.0%)\n | false | |
# Step 1: Open agent chat interface
When I click on "new tab button and create default agent button" elements with selectors:
| [data-tab-id='new-tab-button'] |
| [data-testid='create-default-agent-button'] |
And I should see a "message input box" element with selector "[data-testid='agent-message-input']"
# Step 2: Create first note about AI
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type " wiki AI Technology " in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
Then I should see 4 messages in chat history
# Step 3: Create second note about ML
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type " Machine Learning " in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
Then I should see 8 messages in chat history
# Step 4: Update vector embeddings
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type " wiki " in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
Then I should see 12 messages in chat history
# Step 5: Search for unrelated content with high threshold (should find nothing)
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type "使 wiki 0.7" in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
Then I should see 16 messages in chat history
# Verify the 16th message contains "no results found" with threshold info
Then I should see "no results in 16th message and threshold 0.7 in 16th message" elements with selectors:
| [data-testid='message-bubble']:nth-child(16):has-text('') |
| [data-testid='message-bubble']:nth-child(16):has-text('0.7') |
# Step 6: Lower threshold and search again (should find low-similarity results)
When I click on a "message input textarea" element with selector "[data-testid='agent-message-input']"
When I type "0.1" in "chat input" element with selector "[data-testid='agent-message-input']"
And I press "Enter" key
Then I should see 20 messages in chat history
# Verify the 20th message contains low-similarity result
Then I should see "AI Technology and low similarity in 20th message" elements with selectors:
| [data-testid='message-bubble']:nth-child(20):has-text('Tiddler: AI Technology') |
| [data-testid='message-bubble']:nth-child(20):has-text('15') |

View file

@ -1,190 +0,0 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
const packageJson = require('./package.json');
const beforeAsar = require('./scripts/beforeAsar').default;
const afterPack = require('./scripts/afterPack').default;
const { description } = packageJson;
const config = {
packagerConfig: {
name: 'TidGi',
executableName: 'tidgi',
win32metadata: {
CompanyName: 'TiddlyWiki Community',
OriginalFilename: 'TidGi Desktop',
},
protocols: [
{
name: 'TidGi Launch Protocol',
schemes: ['tidgi'],
},
],
icon: 'build-resources/icon.ico',
asar: {
unpack: '{**/.webpack/main/*.worker.*,**/.webpack/main/native_modules/path.txt}',
},
extraResource: ['localization', 'template/wiki', 'build-resources/menubar@2x.png', 'build-resources/menubarTemplate@2x.png'],
mac: {
category: 'productivity',
target: 'dmg',
icon: 'build-resources/icon.icns',
electronLanguages: ['zh_CN', 'en', 'ja'],
},
appBundleId: 'com.tidgi',
afterPrune: [afterPack],
beforeAsar: [beforeAsar],
},
makers: [
{
name: '@electron-forge/maker-squirrel',
platforms: ['win32'],
config: (arch) => {
return {
setupExe: `Install-TidGi-Windows-${arch}.exe`,
setupIcon: 'build-resources/icon-installer.ico',
description,
iconUrl: 'https://raw.githubusercontent.com/tiddly-gittly/TidGi-Desktop/master/build-resources/icon%405x.png',
};
},
},
// Env WIN_CSC_LINK is not correct https://github.com/rabbit-hole-syndrome/electron-forge-maker-portable/issues/7
// {
// name: '@rabbitholesyndrome/electron-forge-maker-portable',
// platforms: ['win32'],
// config: (arch) => {
// return {
// artifactName: `Portable-TidGi-Windows-${arch}.exe`,
// };
// },
// },
// ✖ Preparing native dependencies: 0 / 1 [FAILED: node-gyp failed to rebuild '/Users/runner/work/TidGi-Desktop/TidGi-Desktop/node_modules/.pnpm/@bitdisaster+exe-icon-extractor@1.0.10/node_modules/@bitdisaster/exe-icon-extractor']
// {
// name: '@electron-forge/maker-wix',
// config: (arch) => {
// return {
// language: 1033,
// manufacturer: 'tiddlywiki.org',
// programFilesFolderName: 'TiddlyWiki',
// shortcutFolderName: 'TiddlyWiki',
// description,
// exe: 'TidGi',
// name: 'TidGi',
// ui: {
// chooseDirectory: true,
// },
// appIconPath: 'build-resources/icon.ico',
// // WiX distributables do not handle prerelease information in the app version, removing it from the MSI (-prerelease3.4)
// // and https://github.com/felixrieseberg/electron-wix-msi/issues/110 ask use to use fixed number
// version: '1.0.0',
// };
// },
// },
{
name: '@electron-forge/maker-zip',
platforms: ['darwin'],
},
{
name: '@electron-forge/maker-deb',
platforms: ['linux'],
executableName: 'tidgi',
config: {
maintainer: 'Lin Onetwo <linonetwo012@gmail.com>',
mimeType: ['x-scheme-handler/tidgi'],
},
},
{
name: '@electron-forge/maker-rpm',
platforms: ['linux'],
executableName: 'tidgi',
config: {
maintainer: 'Lin Onetwo <linonetwo012@gmail.com>',
mimeType: ['x-scheme-handler/tidgi'],
},
},
/**
* [STARTED] Making a AppImage distributable for linux/x64
[FAILED] An error occured while making for target: AppImage
[FAILED] An error occured while making for target: AppImage
An unhandled rejection has occurred inside Forge:
[object Object]
*/
/**
* TypeError: maker.clone is not a function
at /home/runner/work/TidGi-Desktop/TidGi-Desktop/node_modules/.pnpm/@electron-forge+core@7.2.0/node_modules/@electron-forge/core/dist/api/make.js:120:45
*/
// {
// name: '@reforged/maker-appimage',
// platforms: ['linux'],
// config: {
// options: {
// maintainer: 'Lin Onetwo <linonetwo012@gmail.com>',
// homepage: 'https://github.com/tiddly-gittly/TidGi-Desktop',
// icon: 'build-resources/icon.png',
// },
// },
// },
/**
* Making for target: flatpak - On platform: linux - For arch: x64
An unhandled error has occurred inside Forge:
An error occured while making for target: flatpak
flatpak failed with status code 1
Error: flatpak failed with status code 1
at ChildProcess.<anonymous> (/home/runner/work/TidGi-Desktop/TidGi-Desktop/node_modules/@malept/flatpak-bundler/index.js:71:16)
at ChildProcess.emit (events.js:400:28)
at ChildProcess.emit (domain.js:475:12)
at maybeClose (internal/child_process.js:1058:16)
at Process.ChildProcess._handle.onexit (internal/child_process.js:293:5)
*/
// {
// name: '@electron-forge/maker-flatpak',
// },
/**
* Making for target: snap - On platform: linux - For arch: x64
An unhandled error has occurred inside Forge:
An error occured while making for target: snap
Command failed with a non-zero return code (2):
/snap/bin/snapcraft snap --target-arch=amd64 --output=/home/runner/work/TidGi-Desktop/TidGi-Desktop/out/make/snap/x64/tidgi_0.7.6-prerelease3.4_amd64.snap
Error: Command failed with a non-zero return code (2):
/snap/bin/snapcraft snap --target-arch=amd64 --output=/home/runner/work/TidGi-Desktop/TidGi-Desktop/out/make/snap/x64/tidgi_0.7.6-prerelease3.4_amd64.snap
*/
// {
// name: '@electron-forge/maker-snap',
// config: {
// features: {
// audio: true,
// mpris: 'org.tiddlywiki.tidgi',
// webgl: true,
// },
// summary: 'Personal knowledge-base note app with git and REST API.',
// },
// },
],
plugins: [
{ name: '@electron-forge/plugin-auto-unpack-natives', config: {} },
{
name: '@electron-forge/plugin-webpack',
config: {
port: 3012, // default is 3000, may collide with other
mainConfig: './webpack.main.config.js',
renderer: {
config: './webpack.renderer.config.js',
entryPoints: [
{
html: './src/renderer.html',
js: './src/renderer.tsx',
preload: {
js: './src/preload/index.ts',
},
name: 'main_window',
},
],
},
},
},
],
};
module.exports = config;

113
forge.config.ts Normal file
View file

@ -0,0 +1,113 @@
import { AutoUnpackNativesPlugin } from '@electron-forge/plugin-auto-unpack-natives';
import { VitePlugin } from '@electron-forge/plugin-vite';
import type { ForgeConfig } from '@electron-forge/shared-types';
import { readJsonSync } from 'fs-extra';
import path from 'path';
import afterPack from './scripts/afterPack';
import beforeAsar from './scripts/beforeAsar';
const packageJson = readJsonSync(path.join(__dirname, 'package.json')) as { description: string };
const supportedLanguages = readJsonSync(path.join(__dirname, 'localization', 'supportedLanguages.json')) as Record<string, string>;
const { description } = packageJson;
// Get list of supported language codes from centralized config
const supportedLanguageCodes = Object.keys(supportedLanguages);
const config: ForgeConfig = {
packagerConfig: {
name: 'TidGi',
executableName: 'tidgi',
win32metadata: {
CompanyName: 'TiddlyWiki Community',
OriginalFilename: 'TidGi Desktop',
},
protocols: [
{
name: 'TidGi Launch Protocol',
schemes: ['tidgi'],
},
],
icon: 'build-resources/icon.ico',
asar: {
// Unpack worker files, native modules path, and ALL .node binaries (including better-sqlite3)
unpack: '{**/.webpack/main/*.worker.*,**/.webpack/main/native_modules/path.txt,**/{.**,**}/**/*.node}',
},
extraResource: ['localization', 'template/wiki', 'build-resources/menubar@2x.png', 'build-resources/menubarTemplate@2x.png'],
// @ts-expect-error - mac config is valid
mac: {
category: 'productivity',
target: 'dmg',
icon: 'build-resources/icon.icns',
electronLanguages: supportedLanguageCodes,
},
appBundleId: 'com.tidgi',
afterPrune: [afterPack],
beforeAsar: [beforeAsar],
},
makers: [
{
name: '@electron-forge/maker-squirrel',
platforms: ['win32'],
config: (arch: string) => {
return {
setupExe: `Install-TidGi-Windows-${arch}.exe`,
setupIcon: 'build-resources/icon-installer.ico',
description,
iconUrl: 'https://raw.githubusercontent.com/tiddly-gittly/TidGi-Desktop/master/build-resources/icon%405x.png',
};
},
},
{
name: '@electron-forge/maker-zip',
platforms: ['darwin'],
config: {},
},
{
name: '@electron-forge/maker-deb',
platforms: ['linux'],
config: {
options: {
maintainer: 'Lin Onetwo <linonetwo012@gmail.com>',
mimeType: ['x-scheme-handler/tidgi'],
},
},
},
{
name: '@electron-forge/maker-rpm',
platforms: ['linux'],
config: {
options: {
maintainer: 'Lin Onetwo <linonetwo012@gmail.com>',
mimeType: ['x-scheme-handler/tidgi'],
},
},
},
],
plugins: [
new AutoUnpackNativesPlugin({}),
new VitePlugin({
// `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc.
build: [
{
// `entry` is an alias for `build.lib.entry` in the corresponding file of `config`.
entry: 'src/main.ts',
config: 'vite.main.config.ts',
target: 'main',
},
{
entry: 'src/preload/index.ts',
config: 'vite.preload.config.ts',
target: 'preload',
},
],
renderer: [
{
name: 'main_window',
config: 'vite.renderer.config.ts',
},
],
}),
],
};
export default config;

1
forge.env.d.ts vendored Normal file
View file

@ -0,0 +1 @@
/// <reference types="@electron-forge/plugin-vite/forge-vite-env" />

View file

@ -6,5 +6,6 @@
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/renderer.tsx"></script>
</body>
</html>

View file

@ -0,0 +1,549 @@
{
"APILogs": {
"CurrentAgent": "Showing logs for agent: {{agentId}}",
"Description": "Debug logs for external API calls made by this agent. Enable 'External API Debug' in preferences to start logging.",
"ErrorDetails": "Error Details",
"NoLogs": "No API logs found for this agent",
"NoResponse": "No response",
"RequestDetails": "Request Details",
"ResponseContent": "Response Content",
"ResponseMetadata": "Response Metadata",
"StatusCancel": "Cancelled",
"StatusDone": "Completed",
"StatusError": "Error",
"StatusStart": "Started",
"StatusUpdate": "Processing",
"Title": "API Debug Logs"
},
"Agent": {
"EditTitle": "Edit agent name",
"InvalidTabType": "Invalid tab type. A chat tab is required.",
"LoadingChat": "Loading conversation...",
"StartConversation": "Start a conversation",
"Untitled": "Untitled"
},
"Browser": {
"Back": "Step back",
"Bookmark": "Bookmark",
"CurrentUrl": "Current URL",
"EnterUrlPlaceholder": "Enter URL",
"Forward": "forward",
"Home": "Homepage",
"Refresh": "refresh",
"RenderPlaceholder": "This is the webpage rendering area."
},
"Chat": {
"Cancel": "Cancel",
"ConfigError": {
"GoToSettings": "Go to Settings",
"Title": "Configuration Issue"
},
"InputPlaceholder": "Type a message, Ctrl+Enter to send",
"Send": "Send",
"SessionGroup": {
}
},
"Common": {
},
"ContextMenu": {
"AddToCurrentSplitView": "Add to current split view",
"Close": "Close",
"CloseAbove": "Close the tab above",
"CloseBelow": "Close the tab below",
"CloseOther": "Close other tabs",
"CloseTabs": "Close multiple tabs",
"ConvertToSplitView": "Convert to split view",
"CreateSplitViewWithActive": "Create split view with active tab",
"Duplicate": "copy",
"NewTabBelow": "Open a new tab below",
"Pin": "Pinned Tab",
"Refresh": "refresh",
"RestoreClosed": "Restore closed tab",
"Unpin": "Unpin"
},
"CreateAgent": {
"AgentName": "Agent Name",
"AgentNameHelper": "Enter a descriptive name for your agent",
"AgentNamePlaceholder": "My Custom Agent",
"Back": "Back",
"CreatingPreview": "Creating preview agent...",
"EditPrompt": "Edit Prompt",
"EditPromptDescription": "Customize the system prompt and behavior of your agent",
"ImmediateUse": "Test & Use",
"ImmediateUseDescription": "Test your agent and start using it immediately",
"Next": "Next",
"NoTemplateSelected": "Please select a template first",
"Preview": "(Preview)",
"SaveAndUse": "Save & Use Agent",
"SearchTemplates": "Search for agent templates...",
"SelectTemplate": "Choose Template",
"SelectTemplateDescription": "Select an existing agent as a starting template",
"SelectedTemplate": "Selected Template",
"SetupAgent": "Setup Agent",
"SetupAgentDescription": "Configure your new agent by choosing a name and template",
"Steps": {
},
"Title": "Create New Agent"
},
"EditAgent": {
"AgentDescription": "Agent Description",
"AgentDescriptionHelper": "Describe the functionality and purpose of your intelligent agent.",
"AgentDescriptionPlaceholder": "Enter agent description...",
"AgentName": "Agent Name",
"AgentNameHelper": "Enter a descriptive name for your agent",
"AgentNamePlaceholder": "My Custom Agent",
"AgentNotFound": "Agent not found",
"EditBasic": "Edit Basic Info",
"EditBasicDescription": "Edit the basic information of your agent",
"EditPrompt": "Edit Prompt",
"EditPromptDescription": "Customize the system prompt and behavior of your agent",
"ImmediateUse": "Test & Use",
"ImmediateUseDescription": "Test your agent and start using it immediately",
"Loading": "Loading...",
"LoadingPromptConfig": "Loading prompt configuration...",
"PreviewChat": "Preview Chat",
"Save": "Save",
"Saving": "Saving...",
"Steps": {
},
"Title": "Edit Agent"
},
"ModelFeature": {
},
"ModelSelector": {
"Model": "model",
"NoModelSelected": "No model selected",
"SelectModel": "Select Model",
"Title": "model selection"
},
"NewTab": {
"CreateDefaultAgent": "Create Default Agent",
"CreateInstance": "Create Instance",
"CreateNewAgent": "Create New Agent",
"EditDefinition": "Edit Definition",
"NewTab": "New Tab",
"QuickAccess": "quick access",
"SearchPlaceholder": "Search for tabs or agents..."
},
"Preference": {
"AIAgent": "AI Agent",
"AIAgentDescription": "Manage AI Agent conversation records database",
"AIAgentDescriptionDetail": "Here you can view and delete the size and location information of the AI Agent conversation records database",
"APIKey": "API Key",
"AddNewModel": "Add New Model",
"AddNewProvider": "Add New Provider",
"AddProvider": "Add Provider",
"AgentDatabaseDescription": "All AI Agent conversation records are stored in this database, involving only conversations with AI, without affecting Wiki content, occupying {{size}}",
"BaseURL": "API Base URL",
"BaseURLRequired": "API Base URL is required",
"Browse": "Browse",
"CancelAddProvider": "Cancel Adding",
"ConfigureModelParameters": "configuration parameters",
"ConfigureProvider": "Configure {{provider}}",
"ConfirmDeleteAgentDatabase": "Are you sure you want to delete the database containing all AI conversation records? This action cannot be undone.",
"CustomProvider": "Custom Provider",
"DefaultAIModelSelection": "Default AI Model Selection",
"DefaultAIModelSelectionDescription": "Choose the default AI provider and model to use when not specifically set",
"DefaultEmbeddingModelSelection": "Default Embedding Model Selection",
"DefaultEmbeddingModelSelectionDescription": "Choose the default embedding model to use for semantic search and vector operations",
"DefaultImageGenerationModelSelection": "Default Image Generation Model Selection",
"DefaultImageGenerationModelSelectionDescription": "Choose the default image generation model to use for creating images from text",
"DefaultSpeechModelSelection": "Default Speech Generation Model Selection",
"DefaultSpeechModelSelectionDescription": "Choose the default speech generation model to use for text-to-speech operations",
"DefaultTranscriptionsModelSelection": "Default Transcriptions Model Selection",
"DefaultTranscriptionsModelSelectionDescription": "Choose the default transcriptions model to use for speech-to-text operations",
"DeleteAgentDatabase": "Delete AI Conversation Database",
"DeleteProvider": "Delete provider",
"DisabledProviderInfo": "This provider is disabled, and its models will not appear in the model selection list",
"EnableProvider": "Enable this provider",
"ExternalAPI": "External API",
"ExternalAPIDebug": "Enable API Debug Logging",
"ExternalAPIDebugDescription": "When enabled, all API requests and responses will be logged to the database for debugging purposes",
"ExternalApiDatabaseDescription": "Database containing external API debug information, occupying {{size}}",
"FailedToAddModel": "Failed to add model",
"FailedToAddProvider": "Failed to add provider",
"FailedToRemoveModel": "Failed to remove model",
"FailedToSaveSettings": "Failed to save settings",
"FailedToUpdateModel": "Failed to update model",
"FailedToUpdateProviderStatus": "Failed to update provider status",
"MaxTokens": "Maximum generation length",
"MaxTokensDescription": "The maximum number of characters (measured in tokens) that the model can generate in a single request.",
"ModelAddedSuccessfully": "Model added successfully",
"ModelAlreadyExists": "Model already exists",
"ModelCaption": "Model Display Name",
"ModelCaptionHelp": "A friendly name to display in the interface; if left blank, the model name will be used",
"ModelDetails": "Model Details",
"ModelFeatures": "Model Features",
"ModelName": "Model Name",
"ModelNameRequired": "Model name is required",
"ModelParameters": "model parameters",
"ModelParametersDescription": "Configure the behavior parameters of generative AI models, such as temperature and token limits.",
"ModelRemovedSuccessfully": "Model removed successfully",
"ModelUpdatedSuccessfully": "Model updated successfully",
"Models": "Available Models",
"NoPresetSelected": "No preset model selected",
"NoProvidersAvailable": "No providers available",
"OpenDatabaseFolder": "Open Database Folder",
"PresetModels": "Preset Models",
"PresetProvider": "Preset Provider",
"ProviderAddedSuccessfully": "Provider added successfully",
"ProviderAlreadyExists": "Provider name already exists",
"ProviderClass": "Provider Interface Type",
"ProviderConfiguration": "Provider Configuration",
"ProviderConfigurationDescription": "Configure the API key and other settings for AI providers",
"ProviderDisabled": "Provider disabled",
"ProviderEnabled": "Provider enabled",
"ProviderName": "Provider Name",
"ProviderNameRequired": "Provider name is required",
"SearchEmbeddingNoEmbeddingModelError": "Please configure the default embedding model settings in the external API section first.",
"SelectDefaultProvider": "Select default provider",
"SelectFromPresets": "Select from Preset Models",
"SelectModel": "Select Model",
"SettingsSaved": "Settings saved",
"SystemPrompt": "System Prompt",
"SystemPromptDescription": "Set the system instructions sent to the AI to define its behavior and capabilities",
"SystemPromptPlaceholder": "System prompt placeholder",
"Temperature": "Temperature",
"TemperatureDescription": "Lower values produce more deterministic and focused responses, while higher values yield more diverse and creative responses.",
"TopP": "Top P",
"TopPDescription": "Control the randomness of responses. Lower values make responses more deterministic, while higher values allow for greater variability.",
"WorkflowFile": "Workflow File",
"WorkflowFileHelp": "Path to the ComfyUI workflow JSON file",
"WorkflowFilePath": "Workflow File Path"
},
"Prompt": {
"AutoRefresh": "Preview auto-refreshes with input text changes",
"CodeEditor": "Code Editor",
"Flat": "Flat View",
"FormEditor": "Form Editor",
"LastUpdated": "Last updated",
"Loading": "Loading preview...",
"NoMessages": "No message available for preview",
"Preview": "Prompt Preview",
"SchemaNotProvided": "Schema Not Provided",
"SchemaNotProvidedDescription": "No JSON schema was provided or could be fetched. Form cannot be rendered.",
"Tree": "Tree View",
"ValidationErrors": "Validation Errors"
},
"PromptConfig": {
"AddItem": "Add project",
"EmptyArray": "No items have been added yet. Click the button below to add your first item.",
"ItemCount": "{{count}} items",
"RemoveItem": "Remove item",
"Tabs": {
"Prompts": "prompt",
"Response": "response"
},
"Tags": {
"HelperText": "Press Enter to add a tag after input, or select from predefined tags.",
"NoOptions": "No optional tags",
"Placeholder": "Input tags..."
}
},
"Schema": {
"AIConfig": {
"Description": "AI Conversation Settings Configuration",
"Title": "AI Configuration"
},
"AgentConfig": {
"Description": "Agent Configuration",
"Id": "Agent Unique Identifier",
"IdTitle": "Agent ID",
"PromptConfig": {
"Description": "Prompt configuration",
"Prompts": "Prompt Configuration List",
"Response": "Response Configuration List",
"Title": "Prompt Configuration"
},
"Title": "Agent Configuration"
},
"AutoReroll": {
},
"BaseAPIConfig": {
"API": "API providers and model configurations",
"APITitle": "API Configuration",
"Description": "Basic API Configuration",
"ModelParameters": "Model parameter configuration",
"ModelParametersTitle": "model parameters",
"Title": "Basic API Configuration"
},
"DefaultAgents": {
"Description": "Default Agent Configuration List",
"Title": "default intelligent agent"
},
"DynamicPosition": {
},
"FullReplacement": {
"Description": "Complete replacement of parameter configuration",
"SourceType": "source type",
"SourceTypeTitle": "source type",
"SourceTypes": {
},
"TargetId": "Target Element ID",
"TargetIdTitle": "Target ID",
"Title": "Fully replace parameters"
},
"Function": {
},
"HandlerConfig": {
},
"JavascriptTool": {
},
"MCP": {
"Description": "Model Context Protocol Parameter Configuration",
"Id": "MCP Server ID",
"IdTitle": "Server ID",
"ResponseProcessing": {
},
"TimeoutMessage": "timeout message",
"TimeoutMessageTitle": "timeout message",
"TimeoutSecond": "Timeout (seconds)",
"TimeoutSecondTitle": "timeout period",
"Title": "Model Context Protocol Parameters"
},
"ModelParameters": {
"Description": "Model parameter configuration",
"MaxTokens": "Maximum number of tokens generated",
"MaxTokensTitle": "Maximum token count",
"SystemPrompt": "Model system prompt words",
"SystemPromptTitle": "System prompt",
"Temperature": "Response generation temperature (higher = more creative)",
"TemperatureTitle": "temperature",
"Title": "model parameters",
"TopP": "Top P sampling parameter",
"TopPTitle": "Top P"
},
"Position": {
"Bottom": "Offset a few messages from the bottom",
"BottomTitle": "bottom offset",
"Description": "Position Parameter Configuration",
"TargetId": "target element ID",
"TargetIdTitle": "Target ID",
"Title": "positional arguments",
"Type": "Location Type",
"TypeTitle": "Location Type",
"Types": {
}
},
"Prompt": {
"Caption": "brief description",
"CaptionTitle": "description",
"Children": "The sub-prompt list will be concatenated from top to bottom, and from outer to inner, to form the final prompt text.",
"ChildrenTitle": "sub-prompt",
"Description": "Complete prompt configuration, including type and content",
"Enabled": "Whether to enable this prompt, only enabled ones will be incorporated into the final prompt.",
"EnabledTitle": "enable",
"Id": "The unique identifier for the prompt configuration, facilitating reference via targetId in PromptDynamicModification.",
"IdTitle": "ID",
"Role": "Prompt role for OpenAI-compatible interface",
"RoleTitle": "role",
"RoleType": {
"Assistant": "Assistant - AI's replies and responses",
"System": "System - Defines the behavioral rules and background settings for AI",
"User": "User - Simulate user input and requests"
},
"Tags": "Tag List",
"TagsTitle": "label",
"Text": "The prompt content can include syntax supported by wiki text, such as <<variable name>>.",
"TextTitle": "text",
"Title": "prompt"
},
"PromptDynamicModification": {
"DynamicModificationTypes": {
}
},
"PromptPart": {
},
"ProviderModel": {
"Description": "Provider and Model Configuration",
"EmbeddingModel": "Embedding model name for semantic search and vector operations",
"EmbeddingModelTitle": "Embedding Model",
"ImageGenerationModel": "Image generation model name for creating images from text",
"ImageGenerationModelTitle": "Image Generation Model",
"Model": "AI model name",
"ModelTitle": "Model",
"Provider": "AI provider name",
"ProviderTitle": "Provider",
"SpeechModel": "Speech generation model name for text-to-speech operations",
"SpeechModelTitle": "Speech Model",
"Title": "Provider and Model",
"TranscriptionsModel": "Transcriptions model name for speech-to-text operations",
"TranscriptionsModelTitle": "Transcriptions Model"
},
"RAG": {
"Removal": {
},
"SourceTypes": {
}
},
"Response": {
"Description": "The response from an external API, typically used as the target for dynamic modifications in responses, shares the same structure as the prompt. It can be filled with preset content or serve as a placeholder or container, where ResponseDynamicModification injects the specific content from the external API's response.",
"Title": "response"
},
"ResponseDynamicModification": {
"DynamicModificationTypes": {
},
"ResponseProcessingTypes": {
}
},
"ToolCalling": {
},
"Trigger": {
"Model": {
}
},
"Wiki": {
},
"WikiOperation": {
"Description": "Execute Tiddler operations (add, delete, or set text) in wiki workspaces",
"Title": "Wiki Operation",
"Tool": {
"Examples": {
},
"Parameters": {
"extraMeta": {
"Description": "JSON string of extra metadata such as tags and fields, defaults to \"{}\"",
"Title": "Extra Metadata"
},
"operation": {
"Description": "Type of operation to execute",
"Title": "Operation Type"
},
"options": {
"Description": "JSON string of operation options, defaults to \"{}\"",
"Title": "Operation Options"
},
"text": {
"Description": "Text content of the Tiddler",
"Title": "Tiddler Content"
},
"title": {
"Description": "Title of the Tiddler",
"Title": "Tiddler Title"
},
"workspaceName": {
"Description": "Name or ID of the workspace to operate on",
"Title": "Workspace Name"
}
}
},
"ToolListPosition": {
"Position": "Position relative to target element (before/after)",
"PositionTitle": "Insert Position",
"TargetId": "ID of the target element where the tool list will be inserted",
"TargetIdTitle": "Target ID"
},
"ToolResultDuration": "Number of rounds tool execution results remain visible in conversation, after which they become grayed out",
"ToolResultDurationTitle": "Tool Result Duration"
},
"WikiSearch": {
"Description": "Search for content in TiddlyWiki workspaces using filter expressions",
"SourceType": "Data source type",
"SourceTypeTitle": "source type",
"Title": "Wiki Search",
"Tool": {
"Parameters": {
"filter": {
"Description": "TiddlyWiki Filter Expressions",
"Title": "filter"
},
"limit": {
"Description": "Maximum number of results to return",
"Title": "limit"
},
"query": {
"Description": "Query text (natural language) used for vector search",
"Title": "query"
},
"searchType": {
"Description": "Choose a search mode based on rules or similarity.",
"Title": "search type"
},
"threshold": {
"Description": "Similarity threshold (0-1), vector results below this threshold will be filtered.",
"Title": "threshold"
},
"workspaceName": {
"Description": "Workspace name or ID to search for",
"Title": "Workspace Name"
}
}
},
"ToolListPosition": {
"Position": "Insertion position relative to the target position",
"PositionTitle": "insertion position",
"TargetId": "The ID of the target element, the tool list will be inserted relative to this element.",
"TargetIdTitle": "Target ID"
},
"ToolListPositionTitle": "Tool List Location",
"ToolResultDuration": "The number of turns during which the tool execution result remains visible in the conversation; after exceeding this number, the result will be displayed grayed out.",
"ToolResultDurationTitle": "Tool Result Duration Rounds"
}
},
"Search": {
"AvailableAgents": "Available Agents",
"FailedToCreateChatWithAgent": "Unable to create a conversation with the agent.",
"FailedToFetchAgents": "Failed to retrieve the agent list",
"NoAgentsFound": "Agent not found",
"NoClosedTabsFound": "No recently closed tabs",
"NoTabsFound": "No tabs found",
"OpenTabs": "Open tabs",
"RecentlyClosedTabs": "Recently closed tabs"
},
"SplitView": {
"NoTabs": "No tabs in split-screen view"
},
"Tab": {
"Title": {
"CreateNewAgent": "Create New Agent",
"EditAgentDefinition": "Edit Agent",
"NewTab": "New Tab",
"NewWeb": "Create a new webpage",
"SplitView": ""
}
},
"Tool": {
"Schema": {
"Description": "Description",
"Examples": "Usage Examples",
"Optional": "Optional",
"Parameters": "Parameters",
"Required": "Required"
},
"WikiOperation": {
"Error": {
"WorkspaceNotExist": "Workspace {{workspaceID}} does not exist",
"WorkspaceNotFound": "Workspace with name or ID \"{{workspaceName}}\" does not exist. Available workspaces: {{availableWorkspaces}}"
},
"Success": {
"Added": "Successfully added tiddler \"{{title}}\" in wiki workspace \"{{workspaceName}}\"",
"Deleted": "Successfully deleted tiddler \"{{title}}\" from wiki workspace \"{{workspaceName}}\"",
"Updated": "Successfully set text for tiddler \"{{title}}\" in wiki workspace \"{{workspaceName}}\""
}
},
"WikiSearch": {
"Error": {
"ExecutionFailed": "Tool execution failed: {{error}}",
"WorkspaceNotExist": "Workspace {{workspaceID}} does not exist",
"WorkspaceNotFound": "Workspace with name or ID \"{{workspaceName}}\" does not exist. Available workspaces: {{availableWorkspaces}}"
},
"Success": {
"Completed": "Wiki search completed successfully. Found {{totalResults}} total results, showing {{shownResults}}:\n\n",
"NoResults": "No results found for filter \"{{filter}}\" in wiki workspace \"{{workspaceName}}\"",
"NoVectorResults": "No eligible vector search results were found in the Wiki workspace \"{{workspaceName}}\" (similarity threshold: {{threshold}}).",
"VectorCompleted": "Based on vector search, the following related content was found in the workspace {{workspaceName}}:"
},
"UpdateEmbeddings": {
"Error": {
"ExecutionFailed": "Failed to generate embedding: {{error}}",
"NoAIConfig": "Please configure the AI provider and embedding model first (in Settings).",
"WorkspaceNotExist": "Workspace {{workspaceID}} does not exist.",
"WorkspaceNotFound": "The workspace name or ID \"{{workspaceName}}\" does not exist. Available workspaces: {{availableWorkspaces}}"
},
"Success": {
"Generated": "The vector embedding index has been successfully generated for the workspace {{workspaceName}}. Total of {{totalNotes}} notes and {{totalEmbeddings}} embeddings."
}
}
}
}
}

View file

@ -1,501 +1,489 @@
{
"Hello": "Hello",
"WorkspaceSelector": {
"Add": "Add",
"Guide": "Guide",
"Help": "Help",
"OpenWorkspaceTagTiddler": "Open {{tagName}}",
"DefaultTiddlers": "Default Tiddlers",
"OpenWorkspaceMenuName": "Open Workspace",
"EditWorkspace": "Config Workspace",
"RemoveWorkspace": "Remove Workspace",
"AreYouSure": "Are you sure you want to remove this workspace? \nRemoving the workspace will delete the workspace in this application, but will not delete the folders from the hard drive. \nBut, if you choose to delete the Wiki folder as well, all contents will be deleted.",
"RemoveWorkspaceAndDelete": "Remove workspace and delete Wiki folder from the disk",
"BadWorkspacePath": "There are some problem in your workspace setup",
"EditCurrentWorkspace": "Config Current Workspace",
"RemoveCurrentWorkspace": "Remove Current Workspace",
"HibernateWorkspace": "Hibernate Workspace",
"WakeUpWorkspace": "WakeUp Workspace",
"OpenWorkspaceFolder": "Open Folder",
"ReloadCurrentWorkspace": "Reload Current Workspace",
"OpenWorkspaceFolderInEditor": "Open Folder In External Editor",
"OpenWorkspaceFolderInGitGUI": "Open in Git GUI",
"OpenInBrowser": "Open in browser",
"OpenInBrowserDisabledHint": "(Config→EnableHTTPAPI to enable)"
},
"SideBar": {
"CommandPalette": "CmdPal",
"UpdateAvailable": "Update!",
"Preferences": "Pref..."
},
"ContextMenu": {
"OpenTidGi": "Open TidGi",
"OpenTidGiMenuBar": "Open TidGi MenuBar",
"OpenLinkInNewWindow": "Open Link in New Window",
"OpenWorkspaceInNewWindow": "Open Workspace in New Window",
"Preferences": "Preferences...",
"TidGiSupport": "TidGi Support",
"TidGiWebsite": "TidGi Website",
"Quit": "Quit",
"Notifications": "Notifications...",
"More": "More",
"About": "About",
"Reload": "Reload",
"Forward": "Forward→",
"Back": "Back←",
"DeveloperTools": "Developer Tools",
"InspectElement": "Inspect Element",
"LookUp": "Look Up \"{{word}}\"",
"CopyEmailAddress": "Copy Email Address",
"CopyLink": "Copy Link",
"OpenLinkInBrowser": "Open Link in Browser",
"CopyImageURL": "Copy Image URL",
"CopyImage": "Copy Image",
"AddToDictionary": "Add To Dictionary",
"SearchWithGoogle": "Search With Google",
"Cut": "Cut",
"Copy": "Copy",
"Paste": "Paste",
"RestartService": "Restart Service",
"RestartServiceComplete": "Restart Service Complete",
"SyncNow": "Sync To Cloud",
"NoNetworkConnection": "No Network Connection",
"OpenCommandPalette": "Open CommandPalette",
"BackupNow": "Git Backup Locally"
},
"Updater": {
"CheckingFailed": "Checking Failed (Network Error)",
"CheckUpdate": "Check Update",
"CheckingForUpdate": "Checking For Update...",
"DownloadProgress": "Download Progress",
"UpdateError": "Update Error",
"UpdateAvailable": "Update Available!",
"UpdateCancelled": "Update Cancelled",
"UpdateDownloaded": "Update Downloaded",
"UpdateNotAvailable": "You have latest version"
},
"AddWorkspace": {
"MainPageTipWithoutSidebar": "<0>Click </0><strong>Workspaces > Add Workspace</strong><0> on the menu, or </0><strong> Click Here</strong><2> to start using TiddlyWiki!</2>",
"MainPageTipWithSidebar": "<0>Click </0><1>+</1><2> button on the sidebar to start using TiddlyWiki!</2>",
"NotFilled": "Not Filled",
"GitRepoUrl": "Git repo online url",
"AddFileSystemPath": "Adding FileSystemPaths for sub-wiki",
"AddWorkspace": "Add Workspace",
"Advanced": "Advanced Settings",
"AndLinkToMainWorkspace": "and link to main Wiki",
"CreateWiki": "Create Wiki: ",
"CloneWiki": "Import Online Wiki: ",
"ImportWiki": "Import Wiki: ",
"LoginGithubAccount": "Login Github Account",
"LogoutGithubAccount": "Log out of Github account",
"MainWorkspaceDescription": "Contains TiddlyWiki's configuration files and public content when published as a blog.",
"NotLoggedIn": "Not logged in",
"SubWorkspaceDescription": "It must be attached to a main repository, which can be used to store private content, Note two points: the sub-knowledge base cannot be placed in the main knowledge base folder; the sub-knowledge base is generally used to synchronize data to a private Github repository, which can only be read and written by me, so the repository address cannot be the same as the main knowledge base.\nThe sub-knowledge base takes effect by creating a soft link (shortcut) to the main knowledge base. After the link is created, the content in the sub-knowledge base can be seen in the main knowledge base.",
"CloneOnlineWiki": "Import Online Wiki",
"CreateNewWiki": "Create New Wiki",
"ExistedWikiLocation": "Existed Wiki Location",
"OpenLocalWiki": "Open Local Wiki",
"SwitchCreateNewOrOpenExisted": "Switch to create a new or open an existing WIKI",
"MainWorkspace": "Main Workspace",
"SubWorkspace": "Sub Workspace",
"WorkspaceFolder": "Location of workspace's folder",
"WorkspaceParentFolder": "Parent Folder of workspace's folder",
"Choose": "Choose",
"MainWorkspaceLocation": "Path of main workspace",
"SubWorkspaceWillLinkTo": "Sub-Workspace will link to",
"BadWikiHtml": "Failed to create a wiki from this HTML file ",
"CanNotLoadList": "Can't load repository list, network connection is not good.",
"CreatePrivateRepository": "Create private repository",
"CreatePublicRepository": "Create a public repository",
"OmitMoreResult": "The list only shows the first {{loadCount}} results",
"Reload": "Reload",
"MainPageReloadTip": "<0><0>Try:<1><0>Click <2>Reload</2> button below or press <5>CMD_or_Ctrl + R</5> to reload the page.</0><1>Check the <2>Log Folder</2> to see what happened.</1><2>In the worst case you can still copy to backup the folder on your computer, right click on the workspace icon and select Delete Workspace, then re-import the folder on your computer (or import the previously backed up HTML version of the wiki by dragging the HTML in).</2></1></0></0>",
"Processing": "Processing...",
"SearchGithubRepoName": "Search Github repository name",
"WaitForLogin": "Wait for Login",
"WikiServerPort": "WIKI server port number (change if there is a conflict, generally the default is OK)",
"WorkspaceFolderNameToCreate": "The name of the new workspace folder",
"CantCreateFolderHere": "Cannot create folder \"{{newWikiPath}}\" there",
"Choose": "Choose",
"CloneOnlineWiki": "Import Online Wiki",
"CloneWiki": "Import Online Wiki: ",
"CreateLinkFromSubWikiToMainWikiFailed": "Cannot link folder \"{{subWikiPath}}\" to \"{{mainWikiTiddlersFolderPath}}\"",
"CreateLinkFromSubWikiToMainWikiSucceed": "The shortcut to the sub-wiki is successfully created in the main Wiki, and the shortcut that saves the file in the main Wiki will automatically save the file in the sub-Wiki.",
"CreateNewWiki": "Create New Wiki",
"CreatePrivateRepository": "Create private repository",
"CreatePublicRepository": "Create a public repository",
"CreateWiki": "Create Wiki: ",
"ExistedWikiLocation": "Existed Wiki Location",
"ExtractedWikiFolderName": "Converted WIKI folder name",
"GitDefaultBranchDescription": "The default branch of your Git, Github changed it from master to main after that event",
"GitEmailDescription": "Email used for Git commit, and is used to count daily activities on Github and other online git services",
"GitRepoUrl": "Git repo online url",
"GitTokenDescription": "The credentials used to log in to Git. Will expire after a certain period of time",
"GitUserNameDescription": "The account name used to log in to Git. Not the nickname",
"ImportWiki": "Import Wiki: ",
"LocalWikiHtml": "path to html file",
"LocalWorkspace": "Local Workspace",
"LocalWorkspaceDescription": "Only use locally, fully control your own data. TidGi will create a local git backup system for you, allowing you to go back to the previous versions of tiddlers, but all contents will be lost when the local folder is deleted.",
"LogoutToGetStorageServiceToken": "Log in to the online storage service to obtain latest credentials",
"MainPageReloadTip": "<0><0>Try:<1><0>Click <2>Reload</2> button below or press <5>CMD_or_Ctrl + R</5> to reload the page.</0><1>Check the <2>Log Folder</2> to see what happened.</1><2>In the worst case you can still copy to backup the folder on your computer, right click on the workspace icon and select Delete Workspace, then re-import the folder on your computer (or import the previously backed up HTML version of the wiki by dragging the HTML in).</2></1></0></0>",
"MainPageTipWithSidebar": "<0>Click </0><1>+</1><2> button on the sidebar to start using TiddlyWiki!</2>",
"MainPageTipWithoutSidebar": "<0>Click </0><strong>Workspaces > Add Workspace</strong><0> on the menu, or </0><strong> Click Here</strong><2> to start using TiddlyWiki!</2>",
"MainWorkspace": "Main Workspace",
"MainWorkspaceDescription": "Contains TiddlyWiki's configuration files and public content when published as a blog.",
"MainWorkspaceLocation": "Path of main workspace",
"NotFilled": "Not Filled",
"NotLoggedIn": "Not logged in",
"OmitMoreResult": "The list only shows the first {{loadCount}} results",
"OpenLocalWiki": "Open Local Wiki",
"OpenLocalWikiFromHTML": "import wiki.html",
"PathNotExist": "The path does not exist \"{{path}}\"",
"Processing": "Processing...",
"Reload": "Reload",
"SearchGithubRepoName": "Search Github repository name",
"StartCloningSubWiki": "Start cloning sub-Wiki",
"StartCloningWiki": "Start cloning Wiki",
"StartCreatingSubWiki": "Start creating sub-wiki",
"StartLinkingSubWikiToMainWiki": "Start linking sub-Wiki to main-Wiki",
"StartUsingTemplateToCreateWiki": "Start creating a wiki with templates",
"SubWikiCreationCompleted": "Sub Wiki is created",
"ThisPathIsNotAWikiFolder": "The directory is not a Wiki folder \"{{wikiPath}}\"",
"WikiExisted": "Wiki already exists at this location \"{{newWikiPath}}\"",
"WikiTemplateCopyCompleted": "Copied the template Wiki",
"WikiTemplateMissing": "Wiki template is missing \"{{TIDDLYWIKI_TEMPLATE_FOLDER_PATH}}\"",
"StartUpdatingWorkspace": "Updating workspace",
"WorkspaceUpdated": "The workspace is updated and the Wiki is being launching",
"StartLinkingSubWikiToMainWiki": "Start linking sub-Wiki to main-Wiki",
"AddFileSystemPath": "Adding FileSystemPaths for sub-wiki",
"TagName": "Tag Name",
"TagNameHelp": "Tiddlers with this Tag will be add to this sub-wiki (you can add or change this Tag later, by right-click workspace Icon and choose Edit Workspace)",
"GitToken": "Git Token",
"GitTokenDescription": "The credentials used to log in to Git. Will expire after a certain period of time",
"NoGitInfoAlert": "You haven't selected an online Git repo address, or you haven't successfully logged in to your Github account. Click the Create button will create a local wiki that will not be automatically sync to Github. Please be aware.",
"LocalWorkspace": "Local Workspace",
"LocalWorkspaceDescription": "Only use locally, fully control your own data. TidGi will create a local git backup system for you, allowing you to go back to the previous versions of tiddlers, but all contents will be lost when the local folder is deleted.",
"SubWorkspace": "Sub Workspace",
"SubWorkspaceDescription": "It must be attached to a main repository, which can be used to store private content, Note two points: the sub-knowledge base cannot be placed in the main knowledge base folder; the sub-knowledge base is generally used to synchronize data to a private Github repository, which can only be read and written by me, so the repository address cannot be the same as the main knowledge base.\nThe sub-knowledge base takes effect by creating a soft link (shortcut) to the main knowledge base. After the link is created, the content in the sub-knowledge base can be seen in the main knowledge base.",
"SubWorkspaceWillLinkTo": "Sub-Workspace will link to",
"SwitchCreateNewOrOpenExisted": "Switch to create a new or open an existing WIKI",
"SyncedWorkspace": "Synced Workspace",
"SyncedWorkspaceDescription": "To synchronize to an online storage service (such as Github), you need to login to a storage service or enter your login credentials, and have a good network connection. You can sync data across devices, and you still own the data when you use a trusted storage service. And even after the folder is accidentally deleted, you can still download the data from the online service to the local again.",
"GitEmailDescription": "Email used for Git commit, and is used to count daily activities on Github and other online git services",
"GitUserNameDescription": "The account name used to log in to Git. Not the nickname",
"LogoutToGetStorageServiceToken": "Log in to the online storage service to obtain latest credentials",
"AddWorkspace": "Add Workspace",
"WorkspaceUserName": "Workspace User Name",
"WorkspaceUserNameDetail": "The editor name used in the Wiki will be fill in the creator field when the Tiddler is created or edited. The editor name set in the workspace will override the global default editor name assigned in the preferences. This allows you to create Tiddlers with different identities in the same Wiki, with multiple workspace configed with different user name.",
"TagName": "Tag Name",
"TagNameHelp": "Tiddlers with this Tag will be add to this sub-wiki (you can add or change this Tag later, by right-click workspace Icon and choose Edit Workspace)",
"ThisPathIsNotAWikiFolder": "The directory is not a Wiki folder \"{{wikiPath}}\"",
"WaitForLogin": "Wait for Login",
"WikiExisted": "Wiki already exists at this location \"{{newWikiPath}}\"",
"WikiNotStarted": "Wiki is not started or not loaded",
"Advanced": "Advanced Settings",
"GitDefaultBranch": "Git Default Branch",
"GitDefaultBranchDescription": "The default branch of your Git, Github changed it from master to main after that event",
"LocalWikiHtml": "path to html file",
"OpenLocalWikiFromHTML": "import wiki.html",
"ExtractedWikiFolderName": "Converted WIKI folder name",
"BadWikiHtml": "Failed to create a wiki from this HTML file "
"WikiTemplateCopyCompleted": "Copied the template Wiki",
"WikiTemplateMissing": "Wiki template is missing \"{{TIDDLYWIKI_TEMPLATE_FOLDER_PATH}}\"",
"WorkspaceFolder": "Location of workspace's folder",
"WorkspaceFolderNameToCreate": "The name of the new workspace folder",
"WorkspaceParentFolder": "Parent Folder of workspace's folder",
"WorkspaceUserName": "Workspace User Name",
"WorkspaceUserNameDetail": "The editor name used in the Wiki will be fill in the creator field when the Tiddler is created or edited. The editor name set in the workspace will override the global default editor name assigned in the preferences. This allows you to create Tiddlers with different identities in the same Wiki, with multiple workspace configed with different user name."
},
"Cancel": "Cancel",
"ClickForDetails": "Click For Details",
"ContextMenu": {
"About": "About",
"AddToDictionary": "Add To Dictionary",
"Back": "Back←",
"BackupNow": "Git Backup Locally",
"Copy": "Copy",
"CopyEmailAddress": "Copy Email Address",
"CopyImage": "Copy Image",
"CopyImageURL": "Copy Image URL",
"CopyLink": "Copy Link",
"Cut": "Cut",
"DeveloperTools": "Developer Tools",
"Forward": "Forward→",
"InspectElement": "Inspect Element",
"LookUp": "Look Up \"{{word}}\"",
"More": "More",
"NoNetworkConnection": "No Network Connection",
"Notifications": "Notifications...",
"OpenCommandPalette": "Open CommandPalette",
"OpenLinkInBrowser": "Open Link in Browser",
"OpenTidGi": "Open TidGi",
"OpenTidGiMenuBar": "Open TidGi MenuBar",
"OpenWorkspaceInNewWindow": "Open Workspace in New Window",
"Paste": "Paste",
"Preferences": "Preferences...",
"Quit": "Quit",
"Reload": "Reload",
"RestartService": "Restart Service",
"RestartServiceComplete": "Restart Service Complete",
"SearchWithGoogle": "Search With Google",
"SyncNow": "Sync To Cloud",
"TidGiSupport": "TidGi Support",
"TidGiWebsite": "TidGi Website"
},
"Delete": "Delete",
"Dialog": {
"CantFindWorkspaceFolderRemoveWorkspace": "Cannot find the workspace folder that was still there before! \nThe folders that should have existed here may have been removed, or there is no wiki in this folder! \nDo you want to remove the workspace?",
"DoNotCare": "No, Never Mind",
"FocusedTiddlerNotFoundTitle": "Can't find focused tiddler",
"FocusedTiddlerNotFoundTitleDetail": "You can install the FocusedTiddler plugin in CPL.",
"Later": "Later",
"MadeWithLove": "<0>Made with </0><1>❤</1><2> by </2>",
"NeedCorrectTiddlywikiFolderPath": "The correct path needs to be passed in, and this path cannot be recognized by TiddlyWiki.",
"PathPassInCantUse": "The path passed in cannot be used",
"RemoveWorkspace": "Remove workspace",
"ReportBug": "Report Bug",
"ReportBugDetail": "If you have read the tutorial, and carefully read the error output text, and wisely check your input, you can click on the button.",
"RestartAppNow": "Restart App Now",
"RestartMessage": "You need to restart the app for this change to take affect.",
"RestartWikiNow": "Restart Wiki Now",
"Restarting": "Restarting",
"StorageServiceUserInfoNoFound": "Your storage service's UserInfo No Found",
"StorageServiceUserInfoNoFoundDetail": "Seems you haven't login to Your storage service, so we disable syncing for this wiki.",
"WorkspaceFolderRemoved": "Workspace folder is moved or is not a wiki folder"
},
"EditWorkspace": {
"Path": "Wiki Path",
"Save": "Save",
"AddExcludedPlugins": "Enter the name of the plugin you want to ignore",
"AddExcludedPluginsDescription": "You can search for installed plugins in the current wiki, or enter any plugin name.",
"AppearanceOptions": "Appearance Options",
"BackupOnInterval": "Backup On Interval",
"BackupOnIntervalDescription": "When enabled, data will be automatically backed up with local Git at regular intervals (interval in global settings), so that even if no cloud git synchronization address is configured, it will be automatically backed up locally.",
"Cancel": "Cancel",
"DisableAudioTitle": "Disable audio",
"DisableNotificationTitle": "Disable notifications",
"ClickToExpand": "Click To Expand",
"DisableAudio": "Prevent workspace from playing audio.",
"DisableAudioTitle": "Disable audio",
"DisableNotification": "Prevent workspace from sending notifications.",
"HibernateTitle": "Hibernate when not used",
"DisableNotificationTitle": "Disable notifications",
"EnableHTTPAPI": "Enable HTTP APIs",
"EnableHTTPAPIDescription": "Allow third-party programs such as TidGi-Mobile, Tiddlywiki-Collector webclipper, etc. to read and modify your notes through the HTTP network interface.",
"EnableHTTPS": "Enable HTTPS",
"EnableHTTPSDescription": "To provide secure TLS encrypted access, you need to have your own HTTPS certificate, which can be downloaded from the domain name provider, or you can search for free HTTPS certificate application methods.",
"ExcludedPlugins": "plugins to ignore",
"ExcludedPluginsDescription": "When starting the wiki as a blog in read-only mode, you may want to not load some editing-related plugins to reduce the size of the first-loaded web page, such as $:/plugins/tiddlywiki/codemirror, etc. After all, the loaded blog does not need these editing functions.",
"Generate": "Generate",
"HTTPSCertPath": "Cert file path",
"HTTPSCertPathDescription": "The location of the certificate file with the suffix .crt, generally ending with xxx_public.crt.",
"HTTPSKeyPath": "Key file path",
"HTTPSKeyPathDescription": "The location of the private key file with the suffix .key.",
"HTTPSPickCert": "Select the Cert file path",
"HTTPSPickKey": "Select the Key file path",
"HTTPSUploadCert": "Add Cert file",
"HTTPSUploadKey": "Add Key file",
"HibernateDescription": "Save CPU usage, memory and battery. This will disable auto sync, you need to manually commit and sync to backup data.",
"SelectLocal": "Select Local Image...",
"ResetDefaultIcon": "Reset Default Icon",
"NoRevert": "Caution! This operation can't be reverted.",
"HibernateTitle": "Hibernate when not used",
"IsSubWorkspace": "Is SubWorkspace",
"LastNodeJSArgv": "Command line arguments from the latest startup",
"LastVisitState": "Last page visited",
"URL": "Wiki URL",
"Port": "Local host server port",
"MainWorkspacePath": "Main Workspace Path",
"MiscOptions": "Misc",
"Name": "Workspace Name",
"NameDescription": "The name of the workspace, which will be displayed on the sidebar, can be different from the actual folder name of the Git repository in the workspace",
"NoRevert": "Caution! This operation can't be reverted.",
"Path": "Wiki Path",
"PathDescription": "Location of your local wiki folder.",
"Port": "Local host server port",
"ReadOnlyMode": "ReadOnly Mode",
"ReadOnlyModeDescription": "Can be used with intranet penetration, allowing TidGi work as a server program to deploy blogs. After opening, wiki can only be modified by directly modifying the file on the disk (including using git synchronization). The content cannot be modified on the web page, but anyone can access it.",
"ResetDefaultIcon": "Reset Default Icon",
"Save": "Save",
"SaveAndSyncOptions": "Save And Sync",
"SelectLocal": "Select Local Image...",
"ServerOptions": "Blog & Server Options",
"SyncOnInterval": "Sync On Interval",
"SyncOnIntervalDescription": "When on, it will automatically sync according to the time interval in the global settings, and will still automatically sync on startup, or manually by clicking the button. Will auto backup data to local git before sync. If turned off, there is only one automatic sync when the application is opened, and one manual sync when the user triggers it by clicking the sync button in the wiki. ",
"SyncOnStartup": "Sync On App Start",
"SyncOnStartupDescription": "Commit and Sync once the app cold start.",
"Name": "Workspace Name",
"NameDescription": "The name of the workspace, which will be displayed on the sidebar, can be different from the actual folder name of the Git repository in the workspace",
"BackupOnInterval": "Backup On Interval",
"BackupOnIntervalDescription": "When enabled, data will be automatically backed up with local Git at regular intervals (interval in global settings), so that even if no cloud git synchronization address is configured, it will be automatically backed up locally.",
"TiddlyWiki": "",
"TokenAuth": "Token Authenticate",
"TokenAuthAutoFillUserNameDescription": "This feature requires userName to be filled in global setting or workspace setting, if its empty, a default one will be auto filled into workspace setting, you can change it later.",
"TokenAuthCurrentHeader": "Credential authentication current request header",
"TokenAuthCurrentToken": "Current Token of Token Auth",
"TokenAuthCurrentTokenDescription": "This token is confidential, which needs to be regenerated after being leaked to a hostile entity, and credentials need to be updated for connected third-party applications after regeneration",
"TokenAuthCurrentTokenEmptyText": "Click the Generate button to generate a new credential",
"TokenAuthDescription": "When enabled, credentials need to be included in the HTTP request to read write to your knowledge base, which prevents other people in the same LAN from accessing notes, so improves server security. Cannot be turned on at the same time as read-only mode.",
"URL": "Wiki URL",
"UploadOrSelectPathDescription": "Click the upload button to submit the file to Taiji for storage, or click the select path button to select the file from your storage location.",
"WikiRootTiddler": "Wiki Root Tiddler",
"WikiRootTiddlerDescription": "Wiki's root tiddler determines the core behavior of the system, please read the official documentation to understand before modifying",
"WikiRootTiddlerItems": {
"all": "Load all at once",
"lazy-images": "Load images on demand",
"lazy-all": "Load images and text on demand"
},
"ReadOnlyModeDescription": "Can be used with intranet penetration, allowing TidGi work as a server program to deploy blogs. After opening, wiki can only be modified by directly modifying the file on the disk (including using git synchronization). The content cannot be modified on the web page, but anyone can access it.",
"ReadOnlyMode": "ReadOnly Mode",
"TokenAuth": "Token Authenticate",
"TokenAuthDescription": "When enabled, credentials need to be included in the HTTP request to read write to your knowledge base, which prevents other people in the same LAN from accessing notes, so improves server security. Cannot be turned on at the same time as read-only mode.",
"TokenAuthAutoFillUserNameDescription": "This feature requires userName to be filled in global setting or workspace setting, if its empty, a default one will be auto filled into workspace setting, you can change it later.",
"ServerOptions": "Blog & Server Options",
"EnableHTTPS": "Enable HTTPS",
"EnableHTTPSDescription": "To provide secure TLS encrypted access, you need to have your own HTTPS certificate, which can be downloaded from the domain name provider, or you can search for free HTTPS certificate application methods.",
"HTTPSUploadCert": "Add Cert file",
"HTTPSUploadKey": "Add Key file",
"TokenAuthCurrentHeader": "Credential authentication current request header",
"UploadOrSelectPathDescription": "Click the upload button to submit the file to Taiji for storage, or click the select path button to select the file from your storage location.",
"AddExcludedPlugins": "Enter the name of the plugin you want to ignore",
"HTTPSPickCert": "Select the Cert file path",
"HTTPSPickKey": "Select the Key file path",
"AddExcludedPluginsDescription": "You can search for installed plugins in the current wiki, or enter any plugin name.",
"ExcludedPlugins": "plugins to ignore",
"HTTPSCertPathDescription": "The location of the certificate file with the suffix .crt, generally ending with xxx_public.crt.",
"ExcludedPluginsDescription": "When starting the wiki as a blog in read-only mode, you may want to not load some editing-related plugins to reduce the size of the first-loaded web page, such as $:/plugins/tiddlywiki/codemirror, etc. After all, the loaded blog does not need these editing functions.",
"HTTPSCertPath": "Cert file path",
"HTTPSKeyPath": "Key file path",
"HTTPSKeyPathDescription": "The location of the private key file with the suffix .key.",
"LastNodeJSArgv": "Command line arguments from the latest startup",
"EnableHTTPAPI": "Enable HTTP APIs",
"EnableHTTPAPIDescription": "Allow third-party programs such as TidGi-Mobile, Tiddlywiki-Collector webclipper, etc. to read and modify your notes through the HTTP network interface.",
"TokenAuthCurrentToken": "Current Token of Token Auth",
"TokenAuthCurrentTokenDescription": "This token is confidential, which needs to be regenerated after being leaked to a hostile entity, and credentials need to be updated for connected third-party applications after regeneration",
"Generate": "Generate",
"TokenAuthCurrentTokenEmptyText": "Click the Generate button to generate a new credential",
"ClickToExpand": "Click To Expand",
"MainWorkspacePath": "Main Workspace Path",
"IsSubWorkspace": "Is SubWorkspace",
"AppearanceOptions": "Appearance Options",
"SaveAndSyncOptions": "Save And Sync",
"MiscOptions": "Misc"
}
},
"Dialog": {
"CantFindWorkspaceFolderRemoveWorkspace": "Cannot find the workspace folder that was still there before! \nThe folders that should have existed here may have been removed, or there is no wiki in this folder! \nDo you want to remove the workspace?",
"DoNotCare": "No, Never Mind",
"NeedCorrectTiddlywikiFolderPath": "The correct path needs to be passed in, and this path cannot be recognized by TiddlyWiki.",
"PathPassInCantUse": "The path passed in cannot be used",
"RemoveWorkspace": "Remove workspace",
"WorkspaceFolderRemoved": "Workspace folder is moved or is not a wiki folder",
"StorageServiceUserInfoNoFound": "Your storage service's UserInfo No Found",
"StorageServiceUserInfoNoFoundDetail": "Seems you haven't login to Your storage service, so we disable syncing for this wiki.",
"RestartMessage": "You need to restart the app for this change to take affect.",
"Later": "Later",
"RestartAppNow": "Restart App Now",
"RestartWikiNow": "Restart Wiki Now",
"Restarting": "Restarting",
"MadeWithLove": "<0>Made with </0><1>❤</1><2> by </2>",
"ReportBug": "Report Bug",
"ReportBugDetail": "If you have read the tutorial, and carefully read the error output text, and wisely check your input, you can click on the button.",
"FocusedTiddlerNotFoundTitle": "Can't find focused tiddler",
"FocusedTiddlerNotFoundTitleDetail": "You can install the FocusedTiddler plugin in CPL."
"Error": {
"ALreadyExistErrorDescription": "A folder already exists at this path, and a new knowledge base cannot be created here.",
"AlreadyExistError": "Folder already exist here.",
"CopyWikiTemplateError": "E-3 CopyWikiTemplateError",
"CopyWikiTemplateErrorDescription": "E-3 Attempt to copy or overwrite the latest wiki template to the corresponding location, but failed. This should be caused by your input.",
"DoubleWikiInstanceError": "E-4 DoubleWikiInstanceError",
"DoubleWikiInstanceErrorDescription": "E-4 You started the same Wiki twice. This may be caused by a bug in the program.",
"HTMLCanNotLoadError": "Current HTML file path can't be used.",
"HTMLCanNotLoadErrorDescription": "Please enter a path to a valid tiddlywiki.html file.",
"InitWikiGitError": "E-1 InitWikiGitError",
"InitWikiGitErrorDescription": "E-1 The template used by the new note repository failed to copy or the git initialization of the note repository failed. This should be a bug.",
"InitWikiGitRevertError": "E-2 InitWikiGitRevertError",
"InitWikiGitRevertErrorDescription": "E-2 Not only did the initialization of the note warehouse fail, but also the revocation failed. This is a serious problem, and you need to manually clean up the new folder generated in this location.",
"InitWikiGitSyncedWikiNoGitUserInfoError": "E-6 InitWikiGitSyncedWikiNoGitUserInfoErrorDescription",
"InitWikiGitSyncedWikiNoGitUserInfoErrorDescription": "E-6 Initializing the note repository synchronized to the cloud requires you to select a cloud git repository address and provide the certification credentials for the corresponding cloud service. However, this information is not currently available.",
"InsertMenuAfterSubMenuIndexError": "E-5 InsertMenuAfterSubMenuIndexError",
"InsertMenuAfterSubMenuIndexErrorDescription": "E-5 You try to insert menu with afterSubMenu \"{{afterSubMenu}}\" in menu \"{{menuID}}\", but we can not found it in menu \"{{menu}}\", please specific a menuitem with correct id attribute",
"MainWindowMissing": "E-7 This program can't access main window data, can't run normally.",
"SubWikiSMainWikiNotExistError": "The main wiki to which the child wiki is attached does not exist",
"SubWikiSMainWikiNotExistErrorDescription": "A sub-wiki must choose a main wiki to attach to when it is created, but now the main wiki that this sub-wiki should be attached to cannot be found and cannot be attached.",
"ViewLoadUrlError": "E-9 Failed to load the webpage error",
"ViewLoadUrlErrorDescription": "E-9 The Wiki page corresponding to the workspace failed to load, but we will try again soon",
"WikiRuntimeError": "E-13 Wiki Runtime Error",
"WikiRuntimeErrorDescription": "E-13 There is an error while running the wiki. Please check the log file for the reason, and upload and submit an issue for repair.",
"WorkspaceFailedToLoadError": "E-8 WorkspaceFailedToLoadError",
"WorkspaceFailedToLoadErrorDescription": "E-8 The Wiki page corresponding to the workspace failed to load. There are many reasons, but it is basically because of program bugs.",
"ZxInitializationError": "E-12 Zx code execution service initialization error",
"ZxInitializationErrorDescription": "E-12 Zx code execution service initialization error, please check the log file for the reason, and upload and submit the issue for repair.",
"ZxInitializationRetryFailedError": "E-10 Zx code execution service initialization retry error",
"ZxInitializationRetryFailedErrorDescription": "E-10 Zx code execution service initialization error, the error still fails after repeated retries, please upload the log file and submit an issue to report the error for repair.",
"ZxNotInitializedError": "E-11 Zx code execution service is not initialized error",
"ZxNotInitializedErrorDescription": "E-11 The Zx code execution service is not successfully initialized and will automatically try to initialize."
},
"ErrorMessage": "Error message",
"Help": {
"Alternatives": "Alternatives",
"Contribute": "Contribute to this site",
"Description": "Clicking the \"Open\" button will open the page in a new window. The page needs to be loaded from the Internet for the first time (5s - 1min), so it is not available when the network is disconnected. \nYou can modify the content of the opened page at will as a sandbox playground to try out the learned features. If you want to save the modified results, you can click Tiddlywiki's save button to save it as a single-page wiki in HTML format.",
"List": "Helps List",
"Tags": {
}
},
"LOG": {
"CommitBackupMessage": "Backup with TidGi-Desktop\t",
"CommitMessage": "Sync with TidGi-Desktop"
},
"LinOnetwo": "Lin Onetwo",
"Loading": "Loading",
"Log": {
"AddComplete": "Git Add successful",
"AddingFiles": "Start Git Add your files that needs backed up",
"CantForcePullError": "Failed to force pull, maybe repo is in special state",
"CantSyncGitNotInitialized": "Unable to sync, this folder is not initialized as a Git repository",
"CantSyncInSpecialGitStateAutoFixFailed": "Unable to Sync, this folder is in special condition, thus can't Sync directly. An auto-fix has been tried, but error still remains. Please resolve all the conflict manually (For example, use VSCode to open the wiki folder), if this still don't work out, please use professional Git tools (Source Tree, GitKraken) to solve this.",
"CantSyncInSpecialGitStateAutoFixSucceed": "This folder is in a special state, it could not be synchronized directly, but it has been automatically repaired",
"CantSynchronizeAndSyncScriptIsInDeadLoop": "Unable to sync, and Sync script is in a dead loop.",
"CheckingLocalGitRepoSanity": "Checking whether the local Git repository is properly initialized",
"CheckingLocalSyncState": "Detecting whether the local state needs to be synchronized to the cloud",
"CheckingRebaseStatus": "Analyzing the rebase processing plan",
"CommitComplete": "Local commit completed",
"FailedToOpenDirectory": "Failed To Open Directory {{path}} {{errorMessage}}",
"FailedToOpenFile": "Failed To Open File {{path}} {{errorMessage}}",
"FetchingData": "Pulling cloud data for comparison",
"FinishForcePull": "Finish force pull",
"GitMergeFailed": "Git merge results are not good, there may be loopholes in the merge strategy",
"GitPushFailed": "Git push result is bad, this usually means there is a network issue.",
"GitRepositoryConfigurateFailed": "Git repository configuration failed, see error log for details",
"GitRepositoryConfigurationFinished": "Git repository is configured",
"GitTokenExpireOrWrong": "The Git credential (Token) has expired and you need to log in again, or the credential does not correspond to the user name",
"GitTokenMissing": "Git token missing",
"HaveThingsToCommit": "There is content that needs to be submitted (commit), and it is being submitted automatically",
"StartGitInitialization": "Start initializing the local Git repository",
"InitializeWikiGit": "Initializing Wiki and Git",
"InitializeWorkspaceView": "Initializing workspace and browser view, and loading the web content, please wait",
"InitializeWorkspaceViewDone": "Created successfully, content will be loaded soon",
"LocalAheadStartUpload": "The local state is ahead of the cloud, and the upload starts",
"LocalStateBehindSync": "Local state is behind of the cloud, start merging data from the cloud.",
"LocalStateDivergeRebase": "The local state is divergent from the cloud and begins to rebase (Rebase)",
"NoNeedToSync": "No need to synchronize, the local state is consistent with the cloud",
"NotAGitRepository": "Not a git repository",
"PerformLastCheckBeforeSynchronizationFinish": "Perform the final check before synchronization ends",
"PrepareCloneOnlineWiki": "Preparing to import an online wiki.",
"PrepareSync": "Prepare to synchronize, use the logged-in author information",
"PreparingUserInfo": "Configuring identity information",
"RebaseConflictNeedsResolve": "Found conflict when performing git Rebase, need to resolve the conflict.",
"RebaseSucceed": "Rebase is successful, start uploading",
"SkipForcePull": "Skip force pull, no news from remote",
"StartBackupToGithubRemote": "The local Git where the Wiki is located is being backed up to the Github remote repository. The time required depends on the internet speed, please be patient",
"StartConfiguringGithubRemoteRepository": "After the repository is initialized, start to configure the Github remote repository",
"StartFetchingFromGithubRemote": "Fetching data from the remote Github repository, The time required depends on the internet speed, please be patient.",
"StartForcePull": "Start force pull remote, will completely overwrite local",
"StartGitInitialization": "Start initializing the local Git repository",
"StartResettingLocalToRemote": "Start clearing local and override with remote content",
"SyncFailedSystemError": "Synchronization failed, there may be a problem with the synchronization system",
"SynchronizationFailed": "Sync failed! \nYou need to use tools such as Github Desktop to check the status of the current Git repository. \nThe failure may be caused by the network. If this is the case, you can try again after adjusting the network.",
"SynchronizationFinish": "Synchronization complete",
"UsingUrlAndUsername": "Using Git Url {{githubRepoUrl}} with username {{username}} and accessToken {{accessToken}}",
"GitTokenMissing": "Git token missing",
"AddingFiles": "Start Git Add your files that needs backed up",
"AddComplete": "Git Add successful",
"CheckingLocalGitRepoSanity": "Checking whether the local Git repository is properly initialized",
"CheckingLocalSyncState": "Detecting whether the local state needs to be synchronized to the cloud",
"CheckingRebaseStatus": "Analyzing the rebase processing plan",
"InitializeWikiGit": "Initializing Wiki and Git",
"InitializeWorkspaceView": "Initializing workspace and browser view, and loading the web content, please wait",
"GitTokenExpireOrWrong": "The Git credential (Token) has expired and you need to log in again, or the credential does not correspond to the user name",
"InitializeWorkspaceViewDone": "Created successfully, content will be loaded soon",
"FailedToOpenFile": "Failed To Open File {{path}} {{errorMessage}}",
"FailedToOpenDirectory": "Failed To Open Directory {{path}} {{errorMessage}}",
"CantForcePullError": "Failed to force pull, maybe repo is in special state",
"StartForcePull": "Start force pull remote, will completely overwrite local",
"SkipForcePull": "Skip force pull, no news from remote",
"StartResettingLocalToRemote": "Start clearing local and override with remote content",
"FinishForcePull": "Finish force pull"
"SynchronizationFinish": "Synchronization complete"
},
"Cancel": "Cancel",
"Menu": {
"ActualSize": "Actual Size",
"Close": "Close",
"CurrentWorkspace": "Current Workspace",
"DeveloperToolsActiveWorkspace": "Open Developer Tools of Active Workspace",
"Edit": "Edit",
"ExportActiveTiddler": "Export Active Tiddler",
"ExportWholeWikiHTML": "Export Whole Wiki as HTML to folder",
"Find": "Find",
"FindMatches": "matches",
"FindNext": "Find Next",
"FindPrevious": "Find Previous",
"Help": "Help",
"History": "History",
"Home": "Home",
"Language": "Language",
"LearnMore": "Learn More...",
"PrintPage": "Print Page",
"ReportBugViaGithub": "Report a Bug via GitHub...",
"RequestFeatureViaGithub": "Request a New Feature via GitHub...",
"SelectNextWorkspace": "Select Next Workspace",
"SelectPreviousWorkspace": "Select Previous Workspace",
"TidGi": "TidGi",
"TidGiMenuBar": "TidGi MenuBar",
"View": "View",
"Wiki": "Wiki",
"Window": "Window",
"Workspaces": "Workspaces",
"ZoomIn": "Zoom In",
"ZoomOut": "Zoom Out"
},
"No": "No",
"Open": "Open",
"Preference": {
"AlwaysOnTop": "Always on top",
"AlwaysOnTopDetail": "Keep TidGis main window always on top of other windows, and will not be covered by other windows",
"AntiAntiLeech": "Some website has Anti-Leech, will prevent some images from being displayed on your wiki, we simulate a request header that looks like visiting that website to bypass this protection.",
"AskDownloadLocation": "Ask where to save each file before downloading",
"AttachToMenuBar": "Attach to menu bar",
"AttachToMenuBarShowSidebar": "Attach To Menu Bar Show Sidebar",
"AttachToMenuBarShowSidebarTip": "Generally, TidGi small window is only used to quickly view the current workspace, so the default synchronization with the main window workspace, do not need a sidebar, the default hidden sidebar.",
"AttachToMenuBarTip": "Make a small TidGi popup window that pop when you click appbar mini icon. Tip: Right-click on mini app icon to access context menu.",
"AttachToTaskbar": "Attach to taskbar",
"AttachToTaskbarShowSidebar": "Attach To Taskbar Show Sidebar",
"ChooseLanguage": "Choose Language 选择语言",
"ClearBrowsingData": "Clear Browsing Data (git isn't affected)",
"General": "UI & Interact",
"Sync": "Sync & Backup",
"SyncInterval": "Sync/Backup Interval",
"SyncIntervalDescription": "After this length of time, it will automatically start backing up to Github, if is a local workspace it will create a local git backup (take effect after restart app)",
"ClearBrowsingDataDescription": "Clear cookies, cache, and more",
"ClearBrowsingDataMessage": "Are you sure? All browsing data will be cleared. This action cannot be undone.",
"ConfirmDelete": "Confirm Delete",
"ConfirmDeleteExternalApiDatabase": "Are you sure you want to delete the database containing external API debug information? This action cannot be undone.",
"DarkTheme": "Dark Theme",
"DefaultUserName": "User Name",
"DefaultUserNameDetail": "The user name in the Wiki, this only take effect after restart, this will fill in the creator field of the newly created or edited tiddlers. Can be override by user name set in the workspace setting.",
"ShowSideBarDetail": "Sidebar lets you switch easily between workspaces.",
"ShowSideBarText": "Show button label on sidebar",
"ShowNavigationBar": "Show navigation bar",
"ShowNavigationBarDetail": "Navigation bar on the top lets you go back, forward, home, reload and see the URL.",
"ShowTitleBar": "Show title bar",
"ShowTitleBarDetail": "Title bar shows you the title of the current page.",
"HideMenuBar": "Hide menu bar",
"HideMenuBarDetail": "Hide the menu bar unless the Alt+M is pressed.",
"AttachToTaskbar": "Attach to taskbar",
"AttachToMenuBar": "Attach to menu bar",
"AttachToMenuBarTip": "Make a small TidGi popup window that pop when you click appbar mini icon. Tip: Right-click on mini app icon to access context menu.",
"OpenLogFolder": "Open the Log folder",
"OpenLogFolderDetail": "When reporting a problem, please open the latest .log file in the folder and send its content to the developer, or paste it to pastebin.com and then paste the URL into the Github Issue",
"SystemDefaultTheme": "System Defalut Theme",
"LightTheme": "Light Theme",
"DarkTheme": "Dark Theme",
"ShowSideBar": "Show SideBar",
"Theme": "Theme",
"Token": "Git credentials",
"TokenDescription": "The credentials used to authenticate to the Git server so you can securely synchronize content. Can be obtained by logging in to storage services (e.g., Github), or manually obtain \"personal access token\" and filled in here.",
"Reset": "Are you sure? All preferences will be restored to their original defaults. Browsing data won't be affected. This action cannot be undone.",
"ResetNow": "Reset Now",
"ClearBrowsingDataMessage": "Are you sure? All browsing data will be cleared. This action cannot be undone.",
"Notifications": "Notifications",
"NotificationsDetail": "Control notifications pause time",
"NotificationsDisableSchedule": "Automatically disable notifications by schedule:",
"NotificationsMuteAudio": "Mute audio when notifications are paused",
"TestNotification": "Test notifications",
"ItIsWorking": "It is working!",
"Languages": "Lang/语言",
"SpellCheck": "Spell check",
"SpellCheckLanguages": "Preferred spell checking languages",
"Downloads": "Downloads",
"DownloadLocation": "Download Location",
"AskDownloadLocation": "Ask where to save each file before downloading",
"RememberLastVisitState": "Remember last page visited, restore last visit state on open",
"Network": "Network",
"AntiAntiLeech": "Some website has Anti-Leech, will prevent some images from being displayed on your wiki, we simulate a request header that looks like visiting that website to bypass this protection.",
"DeleteExternalApiDatabase": "Delete External API Database",
"DeveloperTools": "Developer Tools",
"DisableAntiAntiLeech": "Disable Anti-Anti-Leech",
"DisableAntiAntiLeechDetail": "Enable this option to completely disable anti-anti-leech functionality.",
"DisableAntiAntiLeechForUrls": "Disable Anti-Anti-Leech for URLs",
"DisableAntiAntiLeechForUrlsDetail": "Enter one URL per line to only disable anti-anti-leech for these URLs. Because anti-anti-leech feature may cause some websites with anti-anti-anti-leech to be unable to load images.",
"PrivacyAndSecurity": "Privacy & Security",
"ShareBrowsingData": "Share browsing data (cookies, cache) between workspaces, if this is off, you can login into different 3rd party service in each workspace.",
"IgnoreCertificateErrors": "Ignore network certificate errors",
"ClearBrowsingDataDescription": "Clear cookies, cache, and more",
"System": "System",
"OpenAtLogin": "Open at login",
"OpenAtLoginMinimized": "Yes, but minimized (MacOS)",
"DeveloperTools": "Developer Tools",
"SwipeWithThreeFingersToNavigate": "Swipe with three fingers to navigate",
"Performance": "Performance",
"DownloadLocation": "Download Location",
"Downloads": "Downloads",
"FriendLinks": "FriendLinks",
"General": "UI & Interact",
"HibernateAllUnusedWorkspaces": "Hibernate unused workspaces at app launch",
"HibernateAllUnusedWorkspacesDescription": "Hibernate all workspaces at launch, except the last active workspace.",
"hardwareAcceleration": "Use hardware acceleration when available",
"Updates": "Updates",
"RestartToApplyUpdates": "Restart to Apply Updates",
"ReceivePreReleaseUpdates": "Receive pre-release updates",
"RestorePreferences": "Restore preferences to their original defaults",
"TiddlyWiki": "TiddlyWiki",
"FriendLinks": "FriendLinks",
"Miscellaneous": "Miscellaneous",
"TranslatiumIntro": "Translate Any Languages like a Pro",
"Translatium": "Translatium",
"WebCatalog": "WebCatalog",
"WebCatalogIntro": "Magically turn any websites into cross platform apps. Work more productively and forget about switching tabs.",
"WebCatalogEngineIntro": "WebCatalog is the initial code founder of TidGi, we reuse lots of important code from the open-source WebCatalog, many thanks to WebCatalog and its author Quang Lam",
"WebSite": "Website",
"Support": "Support",
"WikiMetaData": "Wiki Metadata",
"WikiMetaDataDescription": "Config Wiki metadata likes starting parameters",
"SwipeWithThreeFingersToNavigateDescription": "Navigate between pages with 3-finger gestures. Swipe left to go back or swipe right to go forward.<br/>To enable it, you also need to change<3>macOS Preferences → TrackPad → More Gestures → Swipe between page</3>to<5>Swipe with three fingers</5>or<7>Swipe with two or three fingers.</7>",
"TestNotificationDescription": "<0>If notifications dont show up, make sure you enable notifications in<1>macOS Preferences → Notifications → TidGi</1>.</0>",
"HowToEnableNotifications": "<0>TidGi supports notifications out of the box. But for some cases, to receive notifications, you will need to manually configure additional web app settings.</0><1>Learn more</1><2>.</2>",
"IgnoreCertificateErrorsDescription": "<0>Not recommended. </0><1>Learn more</1>.",
"OpenMetaDataFolder": "Open the metadata folder of TidGi workspace",
"OpenMetaDataFolderDetail": "TiddlyWiki's data and TidGi's workspace metadata are stored separately. TidGi's metadata includes workspace settings, etc., which are stored in this folder in JSON format.",
"HideMenuBar": "Hide menu bar",
"HideMenuBarDetail": "Hide the menu bar unless the Alt+M is pressed.",
"HideSideBar": "Hide SideBar",
"HideSideBarIconDetail": "Hide the icon and only display the name of the workspace to make the workspace list more compact",
"HideTitleBar": "Hide Title Bar",
"ToggleMenuBar": "Toggle Menu Bar",
"NoAttach": "Resume Window Mode",
"HowToEnableNotifications": "<0>TidGi supports notifications out of the box. But for some cases, to receive notifications, you will need to manually configure additional web app settings.</0><1>Learn more</1><2>.</2>",
"IgnoreCertificateErrors": "Ignore network certificate errors",
"IgnoreCertificateErrorsDescription": "<0>Not recommended. </0><1>Learn more</1>.",
"ItIsWorking": "It is working!",
"Languages": "Lang/语言",
"LightTheme": "Light Theme",
"MenubarAlwaysOnTop": "Menubar Always on top",
"MenubarAlwaysOnTopDetail": "Keep TidGis Menubar always on top of other windows, and will not be covered by other windows",
"AlwaysOnTop": "Always on top",
"AlwaysOnTopDetail": "Keep TidGis main window always on top of other windows, and will not be covered by other windows",
"RequireRestart": "Need to restart",
"ChooseLanguage": "Choose Language 选择语言",
"SyncBeforeShutdown": "Sync Before Shutdown",
"SyncBeforeShutdownDescription": "Automatically synchronize data before turning off the computer. Note that manually exiting the application will not trigger the synchronization, so as to prevent the wrong data from being synchronized when the application makes an error. \nWindows system does not support this function.",
"SyncOnlyWhenNoDraft": "Sync only when there are no drafts",
"SyncOnlyWhenNoDraftDescription": "Check if there are drafts or WYSIWYG editing before synchronizing, if so, it will not be synchronized this time, preventing the drafts from being synchronized to your blog. \n(Not working for sync-before-shutdown, for you may want to bring drafts from one computer to another to continue editing)",
"MoreWorkspaceSyncSettingsDescription": "Please right-click the workspace icon, open its workspace setting by click on \"Edit Workspace\" context menu item, and configure its independent synchronization settings in it.",
"Miscellaneous": "Miscellaneous",
"MoreWorkspaceSyncSettings": "More Workspace Sync Settings",
"ShowSideBarIcon": "Show sidebar workspace icons",
"HideSideBarIconDetail": "Hide the icon and only display the name of the workspace to make the workspace list more compact",
"MoreWorkspaceSyncSettingsDescription": "Please right-click the workspace icon, open its workspace setting by click on \"Edit Workspace\" context menu item, and configure its independent synchronization settings in it.",
"Network": "Network",
"Notifications": "Notifications",
"NotificationsDetail": "Control notifications pause time",
"NotificationsDisableSchedule": "Automatically disable notifications by schedule:",
"NotificationsMuteAudio": "Mute audio when notifications are paused",
"OpenAtLogin": "Open at login",
"OpenAtLoginMinimized": "Yes, but minimized (MacOS)",
"OpenLogFolder": "Open the Log folder",
"OpenLogFolderDetail": "When reporting a problem, please open the latest .log file in the folder and send its content to the developer, or paste it to pastebin.com and then paste the URL into the Github Issue",
"OpenMetaDataFolder": "Open the metadata folder of TidGi workspace",
"OpenMetaDataFolderDetail": "TiddlyWiki's data and TidGi's workspace metadata are stored separately. TidGi's metadata includes workspace settings, etc., which are stored in this folder in JSON format.",
"OpenV8CacheFolder": "Open the V8 cache folder",
"OpenV8CacheFolderDetail": "The V8 cache folder stores cached files that accelerate application startup",
"Performance": "Performance",
"PrivacyAndSecurity": "Privacy & Security",
"ReceivePreReleaseUpdates": "Receive pre-release updates",
"RememberLastVisitState": "Remember last page visited, restore last visit state on open",
"RequireRestart": "Need to restart",
"Reset": "Are you sure? All preferences will be restored to their original defaults. Browsing data won't be affected. This action cannot be undone.",
"ResetNow": "Reset Now",
"RestorePreferences": "Restore preferences to their original defaults",
"RunOnBackground": "Run On Background",
"RunOnBackgroundDetail": "When window is closed, Continue to run in the background without exiting. Quickly restore the window when opening the app again.",
"RunOnBackgroundDetailNotMac": "Recommend to enable Attach To Taskbar. So you can restore window use it.",
"AttachToTaskbarShowSidebar": "Attach To Taskbar Show Sidebar",
"AttachToMenuBarShowSidebar": "Attach To Menu Bar Show Sidebar",
"AttachToMenuBarShowSidebarTip": "Generally, TidGi small window is only used to quickly view the current workspace, so the default synchronization with the main window workspace, do not need a sidebar, the default hidden sidebar."
"Search": "Search & Embedding",
"SearchEmbeddingDelete": "Delete",
"SearchEmbeddingDeleteConfirm": "Are you sure you want to delete all embeddings for workspace \"{{workspaceName}}\"? This action cannot be undone.",
"SearchEmbeddingDeleteError": "Failed to delete embeddings: {{error}}",
"SearchEmbeddingGenerate": "Generate Embeddings",
"SearchEmbeddingGenerating": "Generating...",
"SearchEmbeddingLastUpdated": "Last updated: {{time}}",
"SearchEmbeddingNoAIConfigError": "Please configure AI API settings in the External API section first.",
"SearchEmbeddingStatusCompleted": "{{totalEmbeddings}} embeddings for {{totalNotes}} notes",
"SearchEmbeddingStatusError": "Error: {{error}}",
"SearchEmbeddingStatusGenerating": "Generating... ({{completed}}/{{total}})",
"SearchEmbeddingStatusIdle": "No embeddings generated",
"SearchEmbeddingUpdate": "Update Embeddings",
"SearchNoWorkspaces": "No workspaces found",
"ShareBrowsingData": "Share browsing data (cookies, cache) between workspaces, if this is off, you can login into different 3rd party service in each workspace.",
"ShowSideBar": "Show SideBar",
"ShowSideBarDetail": "Sidebar lets you switch easily between workspaces.",
"ShowSideBarIcon": "Show sidebar workspace icons",
"ShowSideBarText": "Show button label on sidebar",
"ShowTitleBar": "Show title bar",
"ShowTitleBarDetail": "Title bar shows you the title of the current page.",
"SpellCheck": "Spell check",
"SpellCheckLanguages": "Preferred spell checking languages",
"Support": "Support",
"SwipeWithThreeFingersToNavigate": "Swipe with three fingers to navigate",
"SwipeWithThreeFingersToNavigateDescription": "Navigate between pages with 3-finger gestures. Swipe left to go back or swipe right to go forward.<br/>To enable it, you also need to change<3>macOS Preferences → TrackPad → More Gestures → Swipe between page</3>to<5>Swipe with three fingers</5>or<7>Swipe with two or three fingers.</7>",
"Sync": "Sync & Backup",
"SyncBeforeShutdown": "Sync Before Shutdown",
"SyncBeforeShutdownDescription": "Automatically synchronize data before turning off the computer. Note that manually exiting the application will not trigger the synchronization, so as to prevent the wrong data from being synchronized when the application makes an error. \nWindows system does not support this function.",
"SyncInterval": "Sync/Backup Interval",
"SyncIntervalDescription": "After this length of time, it will automatically start backing up to Github, if is a local workspace it will create a local git backup (take effect after restart app)",
"SyncOnlyWhenNoDraft": "Sync only when there are no drafts",
"SyncOnlyWhenNoDraftDescription": "Check if there are drafts or WYSIWYG editing before synchronizing, if so, it will not be synchronized this time, preventing the drafts from being synchronized to your blog. \n(Not working for sync-before-shutdown, for you may want to bring drafts from one computer to another to continue editing)",
"System": "System",
"SystemDefaultTheme": "System Defalut Theme",
"TestNotification": "Test notifications",
"TestNotificationDescription": "<0>If notifications dont show up, make sure you enable notifications in<1>macOS Preferences → Notifications → TidGi</1>.</0>",
"Theme": "Theme",
"TiddlyWiki": "TiddlyWiki",
"ToggleMenuBar": "Toggle Menu Bar",
"Token": "Git credentials",
"TokenDescription": "The credentials used to authenticate to the Git server so you can securely synchronize content. Can be obtained by logging in to storage services (e.g., Github), or manually obtain \"personal access token\" and filled in here.",
"Translatium": "Translatium",
"TranslatiumIntro": "Translate Any Languages like a Pro",
"Updates": "Updates",
"WebCatalog": "WebCatalog",
"WebCatalogEngineIntro": "WebCatalog is the initial code founder of TidGi, we reuse lots of important code from the open-source WebCatalog, many thanks to WebCatalog and its author Quang Lam",
"WebCatalogIntro": "Magically turn any websites into cross platform apps. Work more productively and forget about switching tabs.",
"WebSite": "Website",
"WikiMetaData": "Wiki Metadata",
"WikiMetaDataDescription": "Config Wiki metadata likes starting parameters",
"hardwareAcceleration": "Use hardware acceleration when available"
},
"Error": {
"InitWikiGitError": "E-1 InitWikiGitError",
"InitWikiGitErrorDescription": "E-1 The template used by the new note repository failed to copy or the git initialization of the note repository failed. This should be a bug.",
"InitWikiGitRevertError": "E-2 InitWikiGitRevertError",
"InitWikiGitRevertErrorDescription": "E-2 Not only did the initialization of the note warehouse fail, but also the revocation failed. This is a serious problem, and you need to manually clean up the new folder generated in this location.",
"CopyWikiTemplateError": "E-3 CopyWikiTemplateError",
"CopyWikiTemplateErrorDescription": "E-3 Attempt to copy or overwrite the latest wiki template to the corresponding location, but failed. This should be caused by your input.",
"DoubleWikiInstanceError": "E-4 DoubleWikiInstanceError",
"DoubleWikiInstanceErrorDescription": "E-4 You started the same Wiki twice. This may be caused by a bug in the program.",
"InsertMenuAfterSubMenuIndexError": "E-5 InsertMenuAfterSubMenuIndexError",
"InsertMenuAfterSubMenuIndexErrorDescription": "E-5 You try to insert menu with afterSubMenu \"{{afterSubMenu}}\" in menu \"{{menuID}}\", but we can not found it in menu \"{{menu}}\", please specific a menuitem with correct id attribute",
"InitWikiGitSyncedWikiNoGitUserInfoError": "E-6 InitWikiGitSyncedWikiNoGitUserInfoErrorDescription",
"InitWikiGitSyncedWikiNoGitUserInfoErrorDescription": "E-6 Initializing the note repository synchronized to the cloud requires you to select a cloud git repository address and provide the certification credentials for the corresponding cloud service. However, this information is not currently available.",
"MainWindowMissing": "E-7 This program can't access main window data, can't run normally.",
"WorkspaceFailedToLoadError": "E-8 WorkspaceFailedToLoadError",
"WorkspaceFailedToLoadErrorDescription": "E-8 The Wiki page corresponding to the workspace failed to load. There are many reasons, but it is basically because of program bugs.",
"ViewLoadUrlError": "E-9 Failed to load the webpage error",
"ViewLoadUrlErrorDescription": "E-9 The Wiki page corresponding to the workspace failed to load, but we will try again soon",
"ZxInitializationRetryFailedError": "E-10 Zx code execution service initialization retry error",
"ZxInitializationRetryFailedErrorDescription": "E-10 Zx code execution service initialization error, the error still fails after repeated retries, please upload the log file and submit an issue to report the error for repair.",
"ZxNotInitializedError": "E-11 Zx code execution service is not initialized error",
"ZxNotInitializedErrorDescription": "E-11 The Zx code execution service is not successfully initialized and will automatically try to initialize.",
"ZxInitializationError": "E-12 Zx code execution service initialization error",
"ZxInitializationErrorDescription": "E-12 Zx code execution service initialization error, please check the log file for the reason, and upload and submit the issue for repair.",
"WikiRuntimeError": "E-13 Wiki Runtime Error",
"WikiRuntimeErrorDescription": "E-13 There is an error while running the wiki. Please check the log file for the reason, and upload and submit an issue for repair.",
"SubWikiSMainWikiNotExistError": "The main wiki to which the child wiki is attached does not exist",
"SubWikiSMainWikiNotExistErrorDescription": "A sub-wiki must choose a main wiki to attach to when it is created, but now the main wiki that this sub-wiki should be attached to cannot be found and cannot be attached.",
"HTMLCanNotLoadError": "Current HTML file path can't be used.",
"HTMLCanNotLoadErrorDescription": "Please enter a path to a valid tiddlywiki.html file.",
"ALreadyExistErrorDescription": "A folder already exists at this path, and a new knowledge base cannot be created here.",
"AlreadyExistError": "Folder already exist here."
},
"Loading": "Loading",
"Yes": "Yes",
"No": "No",
"LinOnetwo": "Lin Onetwo",
"Menu": {
"Help": "Help",
"ReportBugViaGithub": "Report a Bug via GitHub...",
"RequestFeatureViaGithub": "Request a New Feature via GitHub...",
"LearnMore": "Learn More...",
"Edit": "Edit",
"TidGi": "TidGi",
"TidGiMenuBar": "TidGi MenuBar",
"View": "View",
"SelectPreviousWorkspace": "Select Previous Workspace",
"SelectNextWorkspace": "Select Next Workspace",
"History": "History",
"Language": "Language",
"Window": "Window",
"Workspaces": "Workspaces",
"CurrentWorkspace": "Current Workspace",
"Back": "Back",
"Find": "Find",
"FindNext": "Find Next",
"FindPrevious": "Find Previous",
"Forward": "Forward",
"DeveloperToolsActiveWorkspace": "Open Developer Tools of Active Workspace",
"Home": "Home",
"ActualSize": "Actual Size",
"ZoomIn": "Zoom In",
"ZoomOut": "Zoom Out",
"Close": "Close",
"FindMatches": "matches",
"PrintPage": "Print Page",
"ExportActiveTiddler": "Export Active Tiddler",
"Wiki": "Wiki",
"ExportWholeWikiHTML": "Export Whole Wiki as HTML to folder"
},
"ErrorMessage": "Error message",
"ClickForDetails": "Click For Details",
"Save": "save",
"Scripting": {
"ExecutingScript": "Executing Script"
},
"Description": "Description",
"Tags": "Tags",
"Title": "Title",
"Delete": "Delete",
"Edit": "Edit",
"Open": "Open",
"Help": {
"Alternatives": "Alternatives",
"Description": "Clicking the \"Open\" button will open the page in a new window. The page needs to be loaded from the Internet for the first time (5s - 1min), so it is not available when the network is disconnected. \nYou can modify the content of the opened page at will as a sandbox playground to try out the learned features. If you want to save the modified results, you can click Tiddlywiki's save button to save it as a single-page wiki in HTML format.",
"List": "Helps List",
"Contribute": "Contribute to this site",
"Tags": {
"Docs": "Docs",
"FAQ": "FAQ",
"Intro": "Intro"
}
"SideBar": {
"Preferences": "Pref...",
"UpdateAvailable": "Update!"
},
"LOG": {
"CommitMessage": "Sync with TidGi-Desktop",
"CommitBackupMessage": "Backup with TidGi-Desktop\t"
}
"Unknown": "Unknown",
"Update": "update",
"Updater": {
"CheckUpdate": "Check Update",
"CheckingFailed": "Checking Failed (Network Error)",
"CheckingForUpdate": "Checking For Update...",
"UpdateAvailable": "Update Available!",
"UpdateNotAvailable": "You have latest version"
},
"WorkspaceSelector": {
"Add": "Add",
"Agent": "agent",
"AreYouSure": "Are you sure you want to remove this workspace? \nRemoving the workspace will delete the workspace in this application, but will not delete the folders from the hard drive. \nBut, if you choose to delete the Wiki folder as well, all contents will be deleted.",
"DedicatedWorkspace": "special workspace",
"DefaultTiddlers": "Default Tiddlers",
"EditCurrentWorkspace": "Config Current Workspace",
"EditWorkspace": "Config Workspace",
"Guide": "Guide",
"Help": "Help",
"HibernateWorkspace": "Hibernate Workspace",
"OpenInBrowser": "Open in browser",
"OpenInBrowserDisabledHint": "(Config→EnableHTTPAPI to enable)",
"OpenWorkspaceFolder": "Open Folder",
"OpenWorkspaceFolderInEditor": "Open Folder In External Editor",
"OpenWorkspaceFolderInGitGUI": "Open in Git GUI",
"OpenWorkspaceMenuName": "Open Workspace",
"OpenWorkspaceTagTiddler": "Open {{tagName}}",
"ReloadCurrentWorkspace": "Reload Current Workspace",
"RemoveCurrentWorkspace": "Remove Current Workspace",
"RemoveWorkspace": "Remove Workspace",
"RemoveWorkspaceAndDelete": "Remove workspace and delete Wiki folder from the disk",
"WakeUpWorkspace": "WakeUp Workspace"
},
"Yes": "Yes"
}

View file

@ -0,0 +1,562 @@
{
"APILogs": {
"CurrentAgent": "Afficher les journaux de l'agent intelligent : {{agentId}}",
"Description": "Journal de débogage des appels API externes pour cet agent. Activez le 'Débogage des API externes' dans les préférences pour commencer l'enregistrement.",
"ErrorDetails": "Détails de l'erreur",
"NoLogs": "Journal des API de cet agent intelligent introuvable",
"NoResponse": "pas de réponse",
"RequestDetails": "Détails de la demande",
"ResponseContent": "contenu de réponse",
"ResponseMetadata": "réponse aux métadonnées",
"StatusCancel": "annulé",
"StatusDone": "terminé",
"StatusError": "erreur",
"StatusStart": "commencé",
"StatusUpdate": "En cours de traitement",
"Title": "Journal de débogage API"
},
"Agent": {
"EditTitle": "Modifier le nom de l'agent intelligent",
"InvalidTabType": "Type d'onglet non valide. Un onglet de chat est requis.",
"LoadingChat": "Chargement de la conversation en cours...",
"StartConversation": "commencer la conversation",
"Untitled": "Sans titre"
},
"Browser": {
"Back": "reculer",
"Bookmark": "collection",
"CurrentUrl": "URL actuelle",
"EnterUrlPlaceholder": "entrer l'URL",
"Forward": "avancer",
"Home": "Page d'accueil",
"Refresh": "rafraîchir",
"RenderPlaceholder": "Voici la zone de rendu de la page web."
},
"Chat": {
"Cancel": "Annuler",
"ConfigError": {
"GoToSettings": "Aller aux paramètres",
"Title": "Problème de configuration"
},
"InputPlaceholder": "Tapez un message, Ctrl+Entrée pour envoyer",
"Send": "Envoyer",
"SessionGroup": {
}
},
"Common": {
},
"ContextMenu": {
"AddToCurrentSplitView": "ajouter à la division d'écran actuelle",
"Close": "fermer",
"CloseAbove": "Fermer l'onglet ci-dessus",
"CloseBelow": "Fermer l'onglet ci-dessous",
"CloseOther": "Fermer les autres onglets",
"CloseTabs": "Fermer plusieurs onglets",
"ConvertToSplitView": "passer en vue partagée",
"CreateSplitViewWithActive": "créer un écran partagé avec l'onglet actuel",
"Duplicate": "copier",
"NewTabBelow": "ouvrir un nouvel onglet ci-dessous",
"Pin": "Onglet fixe",
"Refresh": "rafraîchir",
"RestoreClosed": "récupérer l'onglet fermé",
"Unpin": "désépingler"
},
"CreateAgent": {
"AgentName": "Nom de l'agent intelligent",
"AgentNameHelper": "Donnez un nom descriptif à votre agent intelligent",
"AgentNamePlaceholder": "Entrez le nom de l'agent intelligent...",
"Back": "étape précédente",
"CreatingPreview": "Création de l'agent d'aperçu intelligent en cours...",
"EditPrompt": "Modifier l'invite",
"EditPromptDescription": "Personnalisez les invites système et le comportement de votre agent intelligent",
"ImmediateUse": "tester et utiliser",
"ImmediateUseDescription": "Testez votre agent et commencez à l'utiliser immédiatement",
"Next": "prochaine étape",
"NoTemplateSelected": "Veuillez d'abord sélectionner un modèle.",
"Preview": "(prévisualisation)",
"SaveAndUse": "Enregistrer et utiliser l'agent",
"SearchTemplates": "Modèle d'agent de recherche intelligent...",
"SelectTemplate": "choisir un modèle",
"SelectTemplateDescription": "Choisir un agent intelligent existant comme modèle de départ",
"SelectedTemplate": "Modèle sélectionné",
"SetupAgent": "configurer l'agent intelligent",
"SetupAgentDescription": "Nommez votre agent et choisissez un modèle comme point de départ",
"Title": "Créer un nouvel agent intelligent"
},
"EditAgent": {
"AgentDescription": "Description de l'agent intelligent",
"AgentDescriptionHelper": "Décrivez les fonctionnalités et l'utilité de votre agent intelligent",
"AgentDescriptionPlaceholder": "Entrez la description de l'agent intelligent...",
"AgentName": "nom de l'agent intelligent",
"AgentNameHelper": "Donnez un nom descriptif à votre agent intelligent",
"AgentNamePlaceholder": "Entrez le nom de l'agent intelligent...",
"AgentNotFound": "Agent non trouvé",
"EditBasic": "Modifier les informations de base",
"EditBasicDescription": "Modifier les informations de base de votre agent intelligent",
"EditPrompt": "Modifier les invites",
"EditPromptDescription": "Personnalisez les invites système et le comportement de votre agent intelligent",
"ImmediateUse": "tester et utiliser",
"ImmediateUseDescription": "Testez votre agent et commencez à l'utiliser immédiatement",
"Loading": "Chargement en cours...",
"LoadingPromptConfig": "Chargement de la configuration des invites en cours...",
"PreviewChat": "Aperçu du chat",
"Save": "sauvegarder",
"Saving": "Enregistrement en cours...",
"Title": "Définition de l'agent éditeur"
},
"ModelFeature": {
},
"ModelSelector": {
"Model": "modèle",
"NoModelSelected": "Aucun modèle sélectionné",
"SelectModel": "choisir un modèle",
"Title": "sélection de modèle"
},
"NewTab": {
"CreateDefaultAgent": "Créer un agent intelligent par défaut",
"CreateInstance": "créer une instance",
"CreateNewAgent": "Créer un nouvel agent intelligent",
"EditDefinition": "éditer la définition",
"NewTab": "Nouvel onglet",
"QuickAccess": "accès rapide",
"SearchPlaceholder": "Rechercher un onglet ou un agent intelligent..."
},
"Preference": {
"AIAgent": "agent intelligent",
"AIAgentDescription": "Gérer la base de données des enregistrements de conversation des agents IA",
"AIAgentDescriptionDetail": "Ici, vous pouvez consulter et supprimer les informations sur la taille et l'emplacement de la base de données des historiques de conversation avec l'Agent IA.",
"APIKey": "Clé API",
"AddNewModel": "Ajouter un nouveau modèle",
"AddNewProvider": "Ajouter un nouveau fournisseur",
"AddProvider": "Ajouter un fournisseur",
"AgentDatabaseDescription": "Tous les historiques de conversation des agents IA sont enregistrés dans cette base de données, concernant uniquement les échanges avec l'IA, sans affecter le contenu du Wiki, et occupant un espace de {{size}}.",
"BaseURL": "URL de base de l'API",
"BaseURLRequired": "L'URL de base de l'API est requise",
"Browse": "naviguer",
"CancelAddProvider": "Annuler l'ajout",
"ConfigureModelParameters": "paramètres de configuration",
"ConfigureProvider": "Configurer {{provider}}",
"ConfirmDelete": "Confirmer la suppression",
"ConfirmDeleteAgentDatabase": "Êtes-vous sûr de vouloir supprimer la base de données contenant tous les historiques de conversation avec l'IA ? Cette action est irréversible.",
"CustomProvider": "Fournisseur personnalisé",
"DefaultAIModelSelection": "Sélection par défaut du modèle AI",
"DefaultAIModelSelectionDescription": "Choisissez le fournisseur AI et le modèle par défaut à utiliser lorsqu'aucun n'est spécifiquement défini",
"DefaultEmbeddingModelSelection": "Choix du modèle d'intégration par défaut",
"DefaultEmbeddingModelSelectionDescription": "Sélectionnez le modèle d'incorporation par défaut pour la recherche sémantique et les opérations vectorielles",
"DefaultImageGenerationModelSelection": "Modèle de génération d'image par défaut sélectionné",
"DefaultImageGenerationModelSelectionDescription": "sélectionner le modèle de génération d'images par défaut pour les opérations de génération d'images à partir de texte",
"DefaultSpeechModelSelection": "Modèle de génération vocale par défaut sélectionné",
"DefaultSpeechModelSelectionDescription": "Sélectionnez le modèle de génération vocale par défaut pour les opérations de synthèse vocale",
"DefaultTranscriptionsModelSelection": "Sélection du modèle de reconnaissance vocale par défaut",
"DefaultTranscriptionsModelSelectionDescription": "Sélectionnez le modèle de reconnaissance vocale par défaut pour l'opération de conversion de la parole en texte",
"DeleteAgentDatabase": "Supprimer la base de données des conversations IA",
"DeleteExternalApiDatabase": "Supprimer la base de données API externe",
"DeleteProvider": "supprimer le fournisseur",
"DisabledProviderInfo": "Ce fournisseur est désactivé et ses modèles n'apparaîtront pas dans la liste de sélection des modèles",
"EnableProvider": "Activer ce fournisseur",
"ExternalAPI": "API externe",
"ExternalAPIDebug": "Activer le journal de débogage de l'API",
"ExternalAPIDebugDescription": "Une fois activé, toutes les requêtes et réponses API seront enregistrées dans la base de données à des fins de débogage.",
"ExternalApiDatabaseDescription": "Base de données contenant des informations de débogage d'API externes, occupant un espace de {{size}}",
"FailedToAddModel": "Échec de l'ajout du modèle",
"FailedToAddProvider": "Échec de l'ajout du fournisseur",
"FailedToRemoveModel": "Échec de la suppression du modèle",
"FailedToSaveSettings": "Échec de l'enregistrement des paramètres",
"FailedToUpdateModel": "Impossible de mettre à jour le modèle",
"FailedToUpdateProviderStatus": "Échec de la mise à jour du statut du fournisseur",
"MaxTokens": "longueur maximale de génération",
"MaxTokensDescription": "Le nombre maximum de caractères (calculé en tokens) qu'un modèle peut générer en une seule requête.",
"ModelAddedSuccessfully": "Modèle ajouté avec succès",
"ModelAlreadyExists": "Le modèle existe déjà",
"ModelCaption": "Nom d'affichage du modèle",
"ModelCaptionHelp": "Un nom convivial à afficher dans l'interface ; si laissé vide, le nom du modèle sera utilisé",
"ModelDetails": "Détails du modèle",
"ModelFeatures": "Fonctionnalités du modèle",
"ModelName": "Nom du modèle",
"ModelNameRequired": "Le nom du modèle est requis",
"ModelParameters": "paramètres du modèle",
"ModelParametersDescription": "Configurer les paramètres de comportement des modèles d'IA générative, tels que la température et les limites de tokens.",
"ModelRemovedSuccessfully": "Modèle supprimé avec succès",
"ModelUpdatedSuccessfully": "Modèle mis à jour avec succès",
"Models": "Modèles disponibles",
"NoPresetSelected": "Aucun modèle prédéfini sélectionné",
"NoProvidersAvailable": "Aucun fournisseur disponible",
"OpenDatabaseFolder": "Ouvrir le dossier de la base de données",
"PresetModels": "Modèles prédéfinis",
"PresetProvider": "Fournisseur prédéfini",
"ProviderAddedSuccessfully": "Fournisseur ajouté avec succès",
"ProviderAlreadyExists": "Le nom du fournisseur existe déjà",
"ProviderClass": "Type d'interface du fournisseur",
"ProviderConfiguration": "Configuration du fournisseur",
"ProviderConfigurationDescription": "Configurer la clé API et d'autres paramètres pour les fournisseurs AI",
"ProviderDisabled": "Fournisseur désactivé",
"ProviderEnabled": "Fournisseur activé",
"ProviderName": "Nom du fournisseur",
"ProviderNameRequired": "Le nom du fournisseur est requis",
"Search": "recherche et intégration",
"SearchEmbeddingDelete": "supprimer",
"SearchEmbeddingDeleteConfirm": "Êtes-vous sûr de vouloir supprimer tous les vecteurs d'incorporation de l'espace de travail \"{{workspaceName}}\" ? Cette action est irréversible.",
"SearchEmbeddingDeleteError": "Suppression intégrée échouée : {{error}}",
"SearchEmbeddingGenerate": "générer des embeddings",
"SearchEmbeddingGenerating": "Génération en cours...",
"SearchEmbeddingLastUpdated": "Dernière mise à jour : {{time}}",
"SearchEmbeddingNoAIConfigError": "Veuillez d'abord configurer les paramètres de l'API IA dans la section API externe.",
"SearchEmbeddingNoEmbeddingModelError": "Veuillez d'abord configurer les paramètres du modèle d'intégration par défaut dans la section API externe.",
"SearchEmbeddingStatusCompleted": "{{totalEmbeddings}} embeddings de {{totalNotes}} notes",
"SearchEmbeddingStatusError": "Erreur : {{error}}",
"SearchEmbeddingStatusGenerating": "Génération en cours... ({{completed}}/{{total}})",
"SearchEmbeddingStatusIdle": "embedding non généré",
"SearchEmbeddingUpdate": "mise à jour de l'intégration",
"SearchNoWorkspaces": "Espace de travail introuvable",
"SelectDefaultProvider": "choisir le fournisseur par défaut",
"SelectFromPresets": "Sélectionner parmi les modèles prédéfinis",
"SelectModel": "Sélectionner un modèle",
"SettingsSaved": "Paramètres enregistrés",
"SystemPrompt": "Invite système",
"SystemPromptDescription": "Définissez les instructions système envoyées à l'AI pour définir son comportement et ses capacités",
"SystemPromptPlaceholder": "placeholder d'invite système",
"Temperature": "Température",
"TemperatureDescription": "Une valeur plus faible produit une réponse plus déterministe et concentrée, tandis qu'une valeur plus élevée génère une réponse plus diversifiée et créative.",
"TopP": "Top P",
"TopPDescription": "Contrôler l'aléatoire des réponses. Une valeur plus faible rend la réponse plus déterminée, tandis qu'une valeur plus élevée permet davantage de possibilités.",
"WorkflowFile": "fichier de flux de travail",
"WorkflowFileHelp": "Chemin du fichier JSON du flux de travail ComfyUI",
"WorkflowFilePath": "chemin du fichier de flux de travail"
},
"Prompt": {
"AutoRefresh": "L'aperçu se rafraîchit automatiquement en fonction des modifications du texte saisi.",
"CodeEditor": "Éditeur de code",
"Flat": "vue en mosaïque",
"FormEditor": "Éditeur de formulaire",
"LastUpdated": "Dernière mise à jour",
"Loading": "Chargement de l'aperçu en cours...",
"NoMessages": "Aucun message à prévisualiser pour le moment.",
"Preview": "Aperçu des mots-clés",
"SchemaNotProvided": "Format non fourni",
"SchemaNotProvidedDescription": "Aucun schéma JSON n'a été fourni ou il n'a pas pu être obtenu correctement. Le formulaire d'édition ne peut pas être affiché.",
"Tree": "vue arborescente",
"ValidationErrors": "détecter une erreur"
},
"PromptConfig": {
"AddItem": "ajouter un projet",
"EmptyArray": "Aucun élément n'a encore été ajouté. Cliquez sur le bouton ci-dessous pour ajouter votre premier élément.",
"ItemCount": "{{count}} éléments",
"RemoveItem": "supprimer l'élément de la liste",
"Tabs": {
"Prompts": "mot-clé",
"Response": "réponse"
},
"Tags": {
"HelperText": "Appuyez sur Entrée après la saisie pour ajouter une étiquette, ou choisissez parmi les étiquettes prédéfinies.",
"NoOptions": "aucune étiquette disponible",
"Placeholder": "Entrer une étiquette..."
}
},
"Schema": {
"AIConfig": {
"Description": "Configuration des paramètres de conversation IA",
"Title": "Configuration de l'IA"
},
"AgentConfig": {
"Description": "Configuration de l'agent intelligent",
"Id": "Identifiant unique de l'agent intelligent",
"IdTitle": "ID de l'agent intelligent",
"PromptConfig": {
"Description": "configuration des mots-clés",
"Prompts": "Liste de configuration des mots-clés",
"Response": "Liste de configuration des réponses",
"Title": "configuration des mots-clés"
},
"Title": "Configuration de l'agent intelligent"
},
"AutoReroll": {
},
"BaseAPIConfig": {
"API": "Fournisseur d'API et configuration du modèle",
"APITitle": "Configuration de l'API",
"Description": "Configuration de l'API de base",
"ModelParameters": "Configuration des paramètres du modèle",
"ModelParametersTitle": "paramètres du modèle",
"Title": "Configuration de base des API"
},
"DefaultAgents": {
"Description": "Liste de configuration des agents intelligents par défaut",
"Title": "agent intelligent par défaut"
},
"DynamicPosition": {
},
"FullReplacement": {
"Description": "remplacement complet de la configuration des paramètres",
"SourceType": "type source",
"SourceTypeTitle": "type source",
"SourceTypes": {
},
"TargetId": "ID de l'élément cible",
"TargetIdTitle": "ID cible",
"Title": "paramètre de remplacement complet"
},
"Function": {
},
"HandlerConfig": {
},
"JavascriptTool": {
},
"MCP": {
"Description": "Configuration des paramètres du protocole de contexte du modèle",
"Id": "ID du serveur MCP",
"IdTitle": "ID du serveur",
"ResponseProcessing": {
},
"TimeoutMessage": "message en retard",
"TimeoutMessageTitle": "message en retard",
"TimeoutSecond": "Délai d'expiration (secondes)",
"TimeoutSecondTitle": "Délai d'expiration",
"Title": "Paramètres du protocole de contexte du modèle"
},
"ModelParameters": {
"Description": "Configuration des paramètres du modèle",
"MaxTokens": "nombre maximum de jetons générés",
"MaxTokensTitle": "nombre maximum de jetons",
"SystemPrompt": "Mot-clé d'invite du système de modèle",
"SystemPromptTitle": "Invite système",
"Temperature": "Température de génération des réponses (plus élevée = plus créatif)",
"TemperatureTitle": "température",
"Title": "paramètres du modèle",
"TopP": "Paramètre d'échantillonnage Top P",
"TopPTitle": "Top P"
},
"Position": {
"Bottom": "Décaler quelques messages depuis le bas",
"BottomTitle": "décalage du bas",
"Description": "configuration des paramètres de position",
"TargetId": "ID de l'élément cible",
"TargetIdTitle": "ID cible",
"Title": "paramètre positionnel",
"Type": "type d'emplacement",
"TypeTitle": "type d'emplacement",
"Types": {
}
},
"Prompt": {
"Caption": "brève description",
"CaptionTitle": "décrire",
"Children": "Liste des sous-indices, qui seront concaténés de haut en bas et de l'extérieur vers l'intérieur pour former le texte final d'indice.",
"ChildrenTitle": "sous-prompt",
"Description": "Configuration complète des invites, incluant le type et le contenu.",
"Enabled": "Activer cette invite, seules celles activées seront intégrées dans l'invite finale.",
"EnabledTitle": "activer",
"Id": "L'identifiant unique de configuration des mots d'invite, permettant de référencer par targetId dans PromptDynamicModification.",
"IdTitle": "ID",
"Role": "Rôle des invites d'interface compatible OpenAI",
"RoleTitle": "rôle",
"RoleType": {
"Assistant": "Assistant - Réponses et contenus de l'IA",
"System": "Système - Définir les règles de comportement et le contexte de l'IA",
"User": "Utilisateur - Simuler les entrées et requêtes de l'utilisateur"
},
"Tags": "Liste des étiquettes",
"TagsTitle": "étiquette",
"Text": "Le contenu des mots d'invite peut inclure la syntaxe prise en charge par le texte wiki, comme <<nom de variable>>.",
"TextTitle": "texte",
"Title": "mot-clé"
},
"PromptDynamicModification": {
"DynamicModificationTypes": {
}
},
"PromptPart": {
},
"ProviderModel": {
"Description": "Fournisseur et configuration du modèle",
"EmbeddingModel": "Nom du modèle d'incorporation pour la recherche sémantique et les opérations vectorielles",
"EmbeddingModelTitle": "modèle d'intégration",
"ImageGenerationModel": "Nom du modèle de génération d'images utilisé pour les opérations de génération d'images à partir de texte",
"ImageGenerationModelTitle": "modèle de génération d'images",
"Model": "Nom du modèle d'IA",
"ModelTitle": "modèle",
"Provider": "Nom du fournisseur d'IA",
"ProviderTitle": "fournisseur",
"SpeechModel": "Nom du modèle de génération vocale utilisé pour les opérations de synthèse vocale",
"SpeechModelTitle": "modèle vocal",
"Title": "modèle de fournisseur",
"TranscriptionsModel": "Nom du modèle de reconnaissance vocale utilisé pour la conversion de la parole en texte",
"TranscriptionsModelTitle": "modèle de reconnaissance vocale"
},
"RAG": {
"Removal": {
},
"SourceTypes": {
}
},
"Response": {
"Description": "La réponse de l'API externe, souvent utilisée comme cible pour des modifications dynamiques en réponse, a la même structure que les mots d'invite. Elle peut être préremplie avec du contenu prédéfini ou servir d'espace réservé (placeholder) ou de conteneur, où ResponseDynamicModification insère le contenu spécifique de la réponse de l'API externe.",
"Title": "réponse"
},
"ResponseDynamicModification": {
"DynamicModificationTypes": {
},
"ResponseProcessingTypes": {
}
},
"ToolCalling": {
},
"Trigger": {
"Model": {
}
},
"Wiki": {
},
"WikiOperation": {
"Description": "Effectuer des opérations sur les Tiddlers (ajout, suppression ou définition de texte) dans l'espace de travail Wiki",
"Title": "Opérations Wiki",
"Tool": {
"Examples": {
},
"Parameters": {
"extraMeta": {
"Description": "Chaîne JSON de métadonnées supplémentaires, telles que des étiquettes et des champs, par défaut \"{}\"",
"Title": "métadonnées supplémentaires"
},
"operation": {
"Description": "Type d'opération à effectuer",
"Title": "Type d'opération"
},
"options": {
"Description": "Chaîne JSON des options d'opération, par défaut \"{}\"",
"Title": "options d'opération"
},
"text": {
"Description": "Le contenu textuel de Tiddler",
"Title": "Contenu de Tiddler"
},
"title": {
"Description": "Le titre de Tiddler",
"Title": "Titre du Tiddler"
},
"workspaceName": {
"Description": "Nom ou ID de l'espace de travail à manipuler",
"Title": "Nom de l'espace de travail"
}
}
},
"ToolListPosition": {
"Position": "par rapport à la position d'insertion de l'élément cible (avant/après)",
"PositionTitle": "position d'insertion",
"TargetId": "ID de l'élément cible pour insérer la liste des outils",
"TargetIdTitle": "ID cible"
},
"ToolResultDuration": "Le nombre de tours pendant lesquels les résultats de l'exécution des outils restent visibles dans la conversation, après quoi ils seront affichés en gris.",
"ToolResultDurationTitle": "nombre de tours consécutifs des résultats de l'outil"
},
"WikiSearch": {
"Description": "Rechercher le contenu de l'espace de travail TiddlyWiki à l'aide d'expressions de filtre",
"SourceType": "type de source de données",
"SourceTypeTitle": "type source",
"Title": "Recherche Wiki",
"Tool": {
"Parameters": {
"filter": {
"Description": "Expression de filtre TiddlyWiki",
"Title": "filtre"
},
"limit": {
"Description": "nombre maximal de résultats retournés",
"Title": "limiter"
},
"query": {
"Description": "Texte de requête utilisé pour la recherche vectorielle (langage naturel)",
"Title": "requête"
},
"searchType": {
"Description": "Choisissez un mode de recherche basé sur des règles ou sur la similarité.",
"Title": "type de recherche"
},
"threshold": {
"Description": "Seuil de similarité (0-1), les résultats vectoriels en dessous de ce seuil seront filtrés.",
"Title": "seuil"
},
"workspaceName": {
"Description": "Nom ou ID de l'espace de travail à rechercher",
"Title": "Nom de l'espace de travail"
}
}
},
"ToolListPosition": {
"Position": "position d'insertion par rapport à la position cible",
"PositionTitle": "position d'insertion",
"TargetId": "ID de l'élément cible, la liste des outils sera insérée par rapport à cet élément",
"TargetIdTitle": "ID cible"
},
"ToolListPositionTitle": "Emplacement de la liste des outils",
"ToolResultDuration": "Le nombre de tours pendant lesquels les résultats de l'exécution des outils restent visibles dans la conversation, après quoi ils seront affichés en gris.",
"ToolResultDurationTitle": "nombre de tours consécutifs avec résultat d'outil"
}
},
"Search": {
"AvailableAgents": "agents intelligents disponibles",
"FailedToCreateChatWithAgent": "Impossible de créer une conversation avec l'agent intelligent.",
"FailedToFetchAgents": "Échec de l'obtention de la liste des agents intelligents",
"NoAgentsFound": "Agent non trouvé",
"NoClosedTabsFound": "Aucun onglet récemment fermé",
"NoTabsFound": "Aucun onglet trouvé",
"OpenTabs": "onglets ouverts",
"RecentlyClosedTabs": "Onglets récemment fermés"
},
"SplitView": {
"NoTabs": "Aucun onglet dans la vue fractionnée."
},
"Tab": {
"Title": {
"CreateNewAgent": "Créer un nouvel agent intelligent",
"EditAgentDefinition": "Agent éditorial intelligent",
"NewTab": "Nouvel onglet",
"NewWeb": "nouvelle page web",
"SplitView": ""
}
},
"Tool": {
"Schema": {
"Description": "décrire",
"Examples": "Exemple d'utilisation",
"Optional": "optionnel",
"Parameters": "paramètre",
"Required": "nécessaire"
},
"WikiOperation": {
"Error": {
"WorkspaceNotExist": "L'espace de travail {{workspaceID}} n'existe pas.",
"WorkspaceNotFound": "Le nom ou l'ID de l'espace de travail \"{{workspaceName}}\" n'existe pas. Espaces de travail disponibles : {{availableWorkspaces}}"
},
"Success": {
"Added": "Le Tiddler \"{{title}}\" a été ajouté avec succès à l'espace de travail Wiki \"{{workspaceName}}\".",
"Deleted": "Le Tiddler \"{{title}}\" a été supprimé avec succès de l'espace de travail Wiki \"{{workspaceName}}\".",
"Updated": "Le texte du Tiddler \"{{title}}\" a été défini avec succès dans l'espace de travail Wiki \"{{workspaceName}}\"."
}
},
"WikiSearch": {
"Error": {
"ExecutionFailed": "Exécution de l'outil échouée : {{error}}",
"WorkspaceNotExist": "L'espace de travail {{workspaceID}} n'existe pas",
"WorkspaceNotFound": "Le nom ou l'ID de l'espace de travail \"{{workspaceName}}\" n'existe pas. Espaces de travail disponibles : {{availableWorkspaces}}"
},
"Success": {
"Completed": "Recherche Wiki terminée. {{totalResults}} résultats trouvés au total, affichage de {{shownResults}} :",
"NoResults": "Aucun résultat trouvé pour le filtre \"{{filter}}\" dans l'espace de travail Wiki \"{{workspaceName}}\".",
"NoVectorResults": "Aucun résultat de recherche vectorielle conforme n'a été trouvé dans l'espace de travail Wiki \"{{workspaceName}}\" (seuil de similarité : {{threshold}}).",
"VectorCompleted": "Selon la recherche vectorielle, les contenus suivants ont été trouvés dans l'espace de travail {{workspaceName}} :"
},
"UpdateEmbeddings": {
"Error": {
"ExecutionFailed": "Génération d'embedding échouée : {{error}}",
"NoAIConfig": "Veuillez d'abord configurer le fournisseur d'IA et le modèle d'intégration (dans les paramètres).",
"WorkspaceNotExist": "L'espace de travail {{workspaceID}} n'existe pas",
"WorkspaceNotFound": "Le nom ou l'ID de l'espace de travail \"{{workspaceName}}\" n'existe pas. Espaces de travail disponibles : {{availableWorkspaces}}"
},
"Success": {
"Generated": "L'index d'embedding vectoriel pour l'espace de travail {{workspaceName}} a été généré avec succès. Total de {{totalNotes}} notes et {{totalEmbeddings}} embeddings."
}
}
}
},
"Unknown": "inconnu"
}

View file

@ -1,500 +1,472 @@
{
"Hello": "Bonjour",
"WorkspaceSelector": {
"Add": "Ajouter",
"Guide": "Guide",
"Help": "Aide",
"OpenWorkspaceTagTiddler": "Ouvrir {{tagName}}",
"DefaultTiddlers": "Tiddlers par défaut",
"OpenWorkspaceMenuName": "Ouvrir l'espace de travail",
"EditWorkspace": "Configurer l'espace de travail",
"RemoveWorkspace": "Supprimer l'espace de travail",
"AreYouSure": "Êtes-vous sûr de vouloir supprimer cet espace de travail ? \nLa suppression de l'espace de travail supprimera l'espace de travail dans cette application, mais ne supprimera pas les dossiers du disque dur. \nMais, si vous choisissez de supprimer également le dossier Wiki, tout le contenu sera supprimé.",
"RemoveWorkspaceAndDelete": "Supprimer l'espace de travail et supprimer le dossier Wiki du disque",
"BadWorkspacePath": "Il y a un problème dans la configuration de votre espace de travail",
"EditCurrentWorkspace": "Configurer l'espace de travail actuel",
"RemoveCurrentWorkspace": "Supprimer l'espace de travail actuel",
"HibernateWorkspace": "Mettre en veille l'espace de travail",
"WakeUpWorkspace": "Réveiller l'espace de travail",
"OpenWorkspaceFolder": "Ouvrir le dossier",
"ReloadCurrentWorkspace": "Recharger l'espace de travail actuel",
"OpenWorkspaceFolderInEditor": "Ouvrir le dossier dans un éditeur externe",
"OpenWorkspaceFolderInGitGUI": "Ouvrir dans Git GUI",
"OpenInBrowser": "Ouvrir dans le navigateur",
"OpenInBrowserDisabledHint": "(Configurer→ActiverHTTPAPI pour activer)"
},
"SideBar": {
"CommandPalette": "CmdPal",
"UpdateAvailable": "Mise à jour !",
"Preferences": "Préférences..."
},
"ContextMenu": {
"OpenTidGi": "Ouvrir TidGi",
"OpenTidGiMenuBar": "Ouvrir la barre de menu TidGi",
"OpenLinkInNewWindow": "Ouvrir le lien dans une nouvelle fenêtre",
"OpenWorkspaceInNewWindow": "Ouvrir l'espace de travail dans une nouvelle fenêtre",
"Preferences": "Préférences...",
"TidGiSupport": "Support TidGi",
"TidGiWebsite": "Site web TidGi",
"Quit": "Quitter",
"Notifications": "Notifications...",
"More": "Plus",
"About": "À propos",
"Reload": "Recharger",
"Forward": "Avancer→",
"Back": "Reculer←",
"DeveloperTools": "Outils de développement",
"InspectElement": "Inspecter l'élément",
"LookUp": "Rechercher \"{{word}}\"",
"CopyEmailAddress": "Copier l'adresse e-mail",
"CopyLink": "Copier le lien",
"OpenLinkInBrowser": "Ouvrir le lien dans le navigateur",
"CopyImageURL": "Copier l'URL de l'image",
"CopyImage": "Copier l'image",
"AddToDictionary": "Ajouter au dictionnaire",
"SearchWithGoogle": "Rechercher avec Google",
"Cut": "Couper",
"Copy": "Copier",
"Paste": "Coller",
"RestartService": "Redémarrer le service",
"RestartServiceComplete": "Redémarrage du service terminé",
"SyncNow": "Synchroniser avec le cloud",
"NoNetworkConnection": "Pas de connexion réseau",
"OpenCommandPalette": "Ouvrir la palette de commandes",
"BackupNow": "Sauvegarde Git locale"
},
"Updater": {
"CheckingFailed": "Échec de la vérification (erreur réseau)",
"CheckUpdate": "Vérifier les mises à jour",
"CheckingForUpdate": "Vérification des mises à jour...",
"DownloadProgress": "Progression du téléchargement",
"UpdateError": "Erreur de mise à jour",
"UpdateAvailable": "Mise à jour disponible !",
"UpdateCancelled": "Mise à jour annulée",
"UpdateDownloaded": "Mise à jour téléchargée",
"UpdateNotAvailable": "Vous avez la dernière version"
},
"AddWorkspace": {
"MainPageTipWithoutSidebar": "<0>Cliquez </0><strong>Espaces de travail > Ajouter un espace de travail</strong><0> dans le menu, ou </0><strong> Cliquez ici</strong><2> pour commencer à utiliser TiddlyWiki !</2>",
"MainPageTipWithSidebar": "<0>Cliquez </0><1>+</1><2> bouton sur la barre latérale pour commencer à utiliser TiddlyWiki !</2>",
"NotFilled": "Non rempli",
"GitRepoUrl": "URL du dépôt Git en ligne",
"AddFileSystemPath": "Ajout de chemins de système de fichiers pour le sous-wiki",
"AddWorkspace": "Ajouter un espace de travail",
"Advanced": "Paramètres avancés",
"AndLinkToMainWorkspace": "et lien vers le Wiki principal",
"CreateWiki": "Créer un Wiki : ",
"CloneWiki": "Importer un Wiki en ligne : ",
"ImportWiki": "Importer un Wiki : ",
"LoginGithubAccount": "Se connecter au compte Github",
"LogoutGithubAccount": "Se déconnecter du compte Github",
"MainWorkspaceDescription": "Contient les fichiers de configuration de TiddlyWiki et le contenu public lorsqu'il est publié en tant que blog.",
"NotLoggedIn": "Non connecté",
"SubWorkspaceDescription": "Il doit être attaché à un dépôt principal, qui peut être utilisé pour stocker du contenu privé. Notez deux points : la base de connaissances secondaire ne peut pas être placée dans le dossier de la base de connaissances principale ; la base de connaissances secondaire est généralement utilisée pour synchroniser les données avec un dépôt Github privé, qui ne peut être lu et écrit que par moi, donc l'adresse du dépôt ne peut pas être la même que celle de la base de connaissances principale.\nLa base de connaissances secondaire prend effet en créant un lien symbolique (raccourci) vers la base de connaissances principale. Après la création du lien, le contenu de la base de connaissances secondaire peut être vu dans la base de connaissances principale.",
"CloneOnlineWiki": "Importer un Wiki en ligne",
"CreateNewWiki": "Créer un nouveau Wiki",
"ExistedWikiLocation": "Emplacement du Wiki existant",
"OpenLocalWiki": "Ouvrir le Wiki local",
"SwitchCreateNewOrOpenExisted": "Passer à la création d'un nouveau Wiki ou à l'ouverture d'un Wiki existant",
"MainWorkspace": "Espace de travail principal",
"SubWorkspace": "Espace de travail secondaire",
"WorkspaceFolder": "Emplacement du dossier de l'espace de travail",
"WorkspaceParentFolder": "Dossier parent du dossier de l'espace de travail",
"Choose": "Choisir",
"MainWorkspaceLocation": "Chemin de l'espace de travail principal",
"SubWorkspaceWillLinkTo": "L'espace de travail secondaire sera lié à",
"BadWikiHtml": "Échec de la création d'un wiki à partir de ce fichier HTML",
"CanNotLoadList": "Impossible de charger la liste des dépôts, la connexion réseau n'est pas bonne.",
"CreatePrivateRepository": "Créer un dépôt privé",
"CreatePublicRepository": "Créer un dépôt public",
"OmitMoreResult": "La liste ne montre que les {{loadCount}} premiers résultats",
"Reload": "Recharger",
"MainPageReloadTip": "<0><0>Essayez :<1><0>Cliquez sur le bouton <2>Recharger</2> ci-dessous ou appuyez sur <5>CMD_or_Ctrl + R</5> pour recharger la page.</0><1>Vérifiez le <2>Dossier de journal</2> pour voir ce qui s'est passé.</1><2>Dans le pire des cas, vous pouvez toujours copier pour sauvegarder le dossier sur votre ordinateur, cliquez avec le bouton droit sur l'icône de l'espace de travail et sélectionnez Supprimer l'espace de travail, puis réimportez le dossier sur votre ordinateur (ou importez la version HTML précédemment sauvegardée du wiki en faisant glisser le HTML dedans).</2></1></0></0>",
"Processing": "Traitement...",
"SearchGithubRepoName": "Rechercher le nom du dépôt Github",
"WaitForLogin": "Attendre la connexion",
"WikiServerPort": "Numéro de port du serveur WIKI (changer en cas de conflit, généralement la valeur par défaut est correcte)",
"WorkspaceFolderNameToCreate": "Nom du nouveau dossier de l'espace de travail",
"CantCreateFolderHere": "Impossible de créer le dossier \"{{newWikiPath}}\" ici",
"Choose": "Choisir",
"CloneOnlineWiki": "Importer un Wiki en ligne",
"CloneWiki": "Importer un Wiki en ligne : ",
"CreateLinkFromSubWikiToMainWikiFailed": "Impossible de lier le dossier \"{{subWikiPath}}\" à \"{{mainWikiTiddlersFolderPath}}\"",
"CreateLinkFromSubWikiToMainWikiSucceed": "Le raccourci vers le sous-wiki est créé avec succès dans le Wiki principal, et le raccourci qui enregistre le fichier dans le Wiki principal enregistrera automatiquement le fichier dans le sous-wiki.",
"CreateNewWiki": "Créer un nouveau Wiki",
"CreatePrivateRepository": "Créer un dépôt privé",
"CreatePublicRepository": "Créer un dépôt public",
"CreateWiki": "Créer un Wiki : ",
"ExistedWikiLocation": "Emplacement du Wiki existant",
"ExtractedWikiFolderName": "Nom du dossier WIKI converti",
"GitDefaultBranchDescription": "La branche par défaut de votre Git, Github l'a changée de master à main après cet événement",
"GitEmailDescription": "E-mail utilisé pour les commits Git, et est utilisé pour compter les activités quotidiennes sur Github et d'autres services git en ligne",
"GitRepoUrl": "URL du dépôt Git en ligne",
"GitTokenDescription": "Les informations d'identification utilisées pour se connecter à Git. Expirera après une certaine période",
"GitUserNameDescription": "Le nom de compte utilisé pour se connecter à Git. Pas le surnom",
"ImportWiki": "Importer un Wiki : ",
"LocalWikiHtml": "chemin vers le fichier html",
"LocalWorkspace": "Espace de travail local",
"LocalWorkspaceDescription": "Utilisation uniquement locale, contrôle total de vos propres données. TidGi créera un système de sauvegarde git local pour vous, vous permettant de revenir aux versions précédentes des tiddlers, mais tout le contenu sera perdu lorsque le dossier local sera supprimé.",
"LogoutToGetStorageServiceToken": "Se connecter au service de stockage en ligne pour obtenir les dernières informations d'identification",
"MainPageReloadTip": "<0><0>Essayez :<1><0>Cliquez sur le bouton <2>Recharger</2> ci-dessous ou appuyez sur <5>CMD_or_Ctrl + R</5> pour recharger la page.</0><1>Vérifiez le <2>Dossier de journal</2> pour voir ce qui s'est passé.</1><2>Dans le pire des cas, vous pouvez toujours copier pour sauvegarder le dossier sur votre ordinateur, cliquez avec le bouton droit sur l'icône de l'espace de travail et sélectionnez Supprimer l'espace de travail, puis réimportez le dossier sur votre ordinateur (ou importez la version HTML précédemment sauvegardée du wiki en faisant glisser le HTML dedans).</2></1></0></0>",
"MainPageTipWithSidebar": "<0>Cliquez </0><1>+</1><2> bouton sur la barre latérale pour commencer à utiliser TiddlyWiki !</2>",
"MainPageTipWithoutSidebar": "<0>Cliquez </0><strong>Espaces de travail > Ajouter un espace de travail</strong><0> dans le menu, ou </0><strong> Cliquez ici</strong><2> pour commencer à utiliser TiddlyWiki !</2>",
"MainWorkspace": "Espace de travail principal",
"MainWorkspaceDescription": "Contient les fichiers de configuration de TiddlyWiki et le contenu public lorsqu'il est publié en tant que blog.",
"MainWorkspaceLocation": "Chemin de l'espace de travail principal",
"NotFilled": "Non rempli",
"NotLoggedIn": "Non connecté",
"OmitMoreResult": "La liste ne montre que les {{loadCount}} premiers résultats",
"OpenLocalWiki": "Ouvrir le Wiki local",
"OpenLocalWikiFromHTML": "importer wiki.html",
"PathNotExist": "Le chemin n'existe pas \"{{path}}\"",
"Processing": "Traitement...",
"Reload": "Recharger",
"SearchGithubRepoName": "Rechercher le nom du dépôt Github",
"StartCloningSubWiki": "Commencer à cloner le sous-wiki",
"StartCloningWiki": "Commencer à cloner le Wiki",
"StartCreatingSubWiki": "Commencer à créer le sous-wiki",
"StartLinkingSubWikiToMainWiki": "Commencer à lier le sous-wiki au Wiki principal",
"StartUsingTemplateToCreateWiki": "Commencer à créer un wiki avec des modèles",
"SubWikiCreationCompleted": "Le sous-wiki est créé",
"ThisPathIsNotAWikiFolder": "Le répertoire n'est pas un dossier Wiki \"{{wikiPath}}\"",
"WikiExisted": "Le Wiki existe déjà à cet emplacement \"{{newWikiPath}}\"",
"WikiTemplateCopyCompleted": "Le modèle Wiki a été copié",
"WikiTemplateMissing": "Le modèle Wiki est manquant \"{{TIDDLYWIKI_TEMPLATE_FOLDER_PATH}}\"",
"StartUpdatingWorkspace": "Mise à jour de l'espace de travail",
"WorkspaceUpdated": "L'espace de travail est mis à jour et le Wiki est en cours de lancement",
"StartLinkingSubWikiToMainWiki": "Commencer à lier le sous-wiki au Wiki principal",
"AddFileSystemPath": "Ajout de chemins de système de fichiers pour le sous-wiki",
"TagName": "Nom de l'étiquette",
"TagNameHelp": "Les tiddlers avec cette étiquette seront ajoutés à ce sous-wiki (vous pouvez ajouter ou modifier cette étiquette plus tard, en cliquant avec le bouton droit sur l'icône de l'espace de travail et en choisissant Modifier l'espace de travail)",
"GitToken": "Jeton Git",
"GitTokenDescription": "Les informations d'identification utilisées pour se connecter à Git. Expirera après une certaine période",
"NoGitInfoAlert": "Vous n'avez pas sélectionné d'adresse de dépôt Git en ligne, ou vous ne vous êtes pas connecté avec succès à votre compte Github. Cliquer sur le bouton Créer créera un wiki local qui ne sera pas automatiquement synchronisé avec Github. Veuillez en être conscient.",
"LocalWorkspace": "Espace de travail local",
"LocalWorkspaceDescription": "Utilisation uniquement locale, contrôle total de vos propres données. TidGi créera un système de sauvegarde git local pour vous, vous permettant de revenir aux versions précédentes des tiddlers, mais tout le contenu sera perdu lorsque le dossier local sera supprimé.",
"SubWorkspace": "Espace de travail secondaire",
"SubWorkspaceDescription": "Il doit être attaché à un dépôt principal, qui peut être utilisé pour stocker du contenu privé. Notez deux points : la base de connaissances secondaire ne peut pas être placée dans le dossier de la base de connaissances principale ; la base de connaissances secondaire est généralement utilisée pour synchroniser les données avec un dépôt Github privé, qui ne peut être lu et écrit que par moi, donc l'adresse du dépôt ne peut pas être la même que celle de la base de connaissances principale.\nLa base de connaissances secondaire prend effet en créant un lien symbolique (raccourci) vers la base de connaissances principale. Après la création du lien, le contenu de la base de connaissances secondaire peut être vu dans la base de connaissances principale.",
"SubWorkspaceWillLinkTo": "L'espace de travail secondaire sera lié à",
"SwitchCreateNewOrOpenExisted": "Passer à la création d'un nouveau Wiki ou à l'ouverture d'un Wiki existant",
"SyncedWorkspace": "Espace de travail synchronisé",
"SyncedWorkspaceDescription": "Pour synchroniser avec un service de stockage en ligne (comme Github), vous devez vous connecter à un service de stockage ou entrer vos informations d'identification, et avoir une bonne connexion réseau. Vous pouvez synchroniser les données entre les appareils, et vous possédez toujours les données lorsque vous utilisez un service de stockage de confiance. Et même après la suppression accidentelle du dossier, vous pouvez toujours télécharger les données du service en ligne vers le local à nouveau.",
"GitEmailDescription": "E-mail utilisé pour les commits Git, et est utilisé pour compter les activités quotidiennes sur Github et d'autres services git en ligne",
"GitUserNameDescription": "Le nom de compte utilisé pour se connecter à Git. Pas le surnom",
"LogoutToGetStorageServiceToken": "Se connecter au service de stockage en ligne pour obtenir les dernières informations d'identification",
"AddWorkspace": "Ajouter un espace de travail",
"WorkspaceUserName": "Nom d'utilisateur de l'espace de travail",
"WorkspaceUserNameDetail": "Le nom de l'éditeur utilisé dans le Wiki sera rempli dans le champ créateur lorsque le Tiddler est créé ou modifié. Le nom de l'éditeur défini dans l'espace de travail remplacera le nom de l'éditeur par défaut global attribué dans les préférences. Cela vous permet de créer des Tiddlers avec différentes identités dans le même Wiki, avec plusieurs espaces de travail configurés avec des noms d'utilisateur différents.",
"TagName": "Nom de l'étiquette",
"TagNameHelp": "Les tiddlers avec cette étiquette seront ajoutés à ce sous-wiki (vous pouvez ajouter ou modifier cette étiquette plus tard, en cliquant avec le bouton droit sur l'icône de l'espace de travail et en choisissant Modifier l'espace de travail)",
"ThisPathIsNotAWikiFolder": "Le répertoire n'est pas un dossier Wiki \"{{wikiPath}}\"",
"WaitForLogin": "Attendre la connexion",
"WikiExisted": "Le Wiki existe déjà à cet emplacement \"{{newWikiPath}}\"",
"WikiNotStarted": "Le Wiki n'est pas démarré ou n'est pas chargé",
"Advanced": "Paramètres avancés",
"GitDefaultBranch": "Branche par défaut de Git",
"GitDefaultBranchDescription": "La branche par défaut de votre Git, Github l'a changée de master à main après cet événement",
"LocalWikiHtml": "chemin vers le fichier html",
"OpenLocalWikiFromHTML": "importer wiki.html",
"ExtractedWikiFolderName": "Nom du dossier WIKI converti",
"BadWikiHtml": "Échec de la création d'un wiki à partir de ce fichier HTML"
"WikiTemplateCopyCompleted": "Le modèle Wiki a été copié",
"WikiTemplateMissing": "Le modèle Wiki est manquant \"{{TIDDLYWIKI_TEMPLATE_FOLDER_PATH}}\"",
"WorkspaceFolder": "Emplacement du dossier de l'espace de travail",
"WorkspaceFolderNameToCreate": "Nom du nouveau dossier de l'espace de travail",
"WorkspaceParentFolder": "Dossier parent du dossier de l'espace de travail",
"WorkspaceUserName": "Nom d'utilisateur de l'espace de travail",
"WorkspaceUserNameDetail": "Le nom de l'éditeur utilisé dans le Wiki sera rempli dans le champ créateur lorsque le Tiddler est créé ou modifié. Le nom de l'éditeur défini dans l'espace de travail remplacera le nom de l'éditeur par défaut global attribué dans les préférences. Cela vous permet de créer des Tiddlers avec différentes identités dans le même Wiki, avec plusieurs espaces de travail configurés avec des noms d'utilisateur différents."
},
"Cancel": "Annuler",
"ClickForDetails": "Cliquez pour plus de détails",
"ContextMenu": {
"About": "À propos",
"AddToDictionary": "Ajouter au dictionnaire",
"Back": "Reculer←",
"BackupNow": "Sauvegarde Git locale",
"Copy": "Copier",
"CopyEmailAddress": "Copier l'adresse e-mail",
"CopyImage": "Copier l'image",
"CopyImageURL": "Copier l'URL de l'image",
"CopyLink": "Copier le lien",
"Cut": "Couper",
"DeveloperTools": "Outils de développement",
"Forward": "Avancer→",
"InspectElement": "Inspecter l'élément",
"LookUp": "Rechercher \"{{word}}\"",
"More": "Plus",
"NoNetworkConnection": "Pas de connexion réseau",
"Notifications": "Notifications...",
"OpenCommandPalette": "Ouvrir la palette de commandes",
"OpenLinkInBrowser": "Ouvrir le lien dans le navigateur",
"OpenTidGi": "Ouvrir TidGi",
"OpenTidGiMenuBar": "Ouvrir la barre de menu TidGi",
"OpenWorkspaceInNewWindow": "Ouvrir l'espace de travail dans une nouvelle fenêtre",
"Paste": "Coller",
"Preferences": "Préférences...",
"Quit": "Quitter",
"Reload": "Recharger",
"RestartService": "Redémarrer le service",
"RestartServiceComplete": "Redémarrage du service terminé",
"SearchWithGoogle": "Rechercher avec Google",
"SyncNow": "Synchroniser avec le cloud",
"TidGiSupport": "Support TidGi",
"TidGiWebsite": "Site web TidGi"
},
"Delete": "Supprimer",
"Dialog": {
"CantFindWorkspaceFolderRemoveWorkspace": "Impossible de trouver le dossier de l'espace de travail qui était encore là avant ! \nLes dossiers qui devraient exister ici ont peut-être été supprimés, ou il n'y a pas de wiki dans ce dossier ! \nVoulez-vous supprimer l'espace de travail ?",
"DoNotCare": "Non, peu importe",
"FocusedTiddlerNotFoundTitle": "Impossible de trouver l'entrée actuellement ciblée",
"FocusedTiddlerNotFoundTitleDetail": "Vous pouvez installer le plugin FocusedTiddler sur CPL.",
"Later": "Plus tard",
"MadeWithLove": "<0>Fait avec </0><1>❤</1><2> par </2>",
"NeedCorrectTiddlywikiFolderPath": "Le chemin correct doit être passé, et ce chemin ne peut pas être reconnu par TiddlyWiki.",
"PathPassInCantUse": "Le chemin passé ne peut pas être utilisé",
"RemoveWorkspace": "Supprimer l'espace de travail",
"ReportBug": "Signaler un bug",
"ReportBugDetail": "Si vous avez lu le tutoriel, et lu attentivement le texte de sortie des erreurs, et vérifié judicieusement votre saisie, vous pouvez cliquer sur le bouton.",
"RestartAppNow": "Redémarrer l'application maintenant",
"RestartMessage": "Vous devez redémarrer l'application pour que ce changement prenne effet.",
"RestartWikiNow": "Redémarrer le Wiki maintenant",
"Restarting": "Redémarrage",
"StorageServiceUserInfoNoFound": "Les informations utilisateur de votre service de stockage sont introuvables",
"StorageServiceUserInfoNoFoundDetail": "Il semble que vous ne vous soyez pas connecté à votre service de stockage, nous désactivons donc la synchronisation pour ce wiki.",
"WorkspaceFolderRemoved": "Le dossier de l'espace de travail est déplacé ou n'est pas un dossier wiki"
},
"EditWorkspace": {
"Path": "Chemin du Wiki",
"Save": "Enregistrer",
"AddExcludedPlugins": "Entrez le nom du plugin que vous souhaitez ignorer",
"AddExcludedPluginsDescription": "Vous pouvez rechercher des plugins installés dans le wiki actuel, ou entrer n'importe quel nom de plugin.",
"AppearanceOptions": "Options d'apparence",
"BackupOnInterval": "Sauvegarde à intervalle",
"BackupOnIntervalDescription": "Lorsqu'il est activé, les données seront automatiquement sauvegardées avec le git local à intervalles réguliers (intervalle dans les paramètres globaux), de sorte que même si aucune adresse de synchronisation git cloud n'est configurée, elles seront automatiquement sauvegardées localement.",
"Cancel": "Annuler",
"DisableAudioTitle": "Désactiver l'audio",
"DisableNotificationTitle": "Désactiver les notifications",
"ClickToExpand": "Cliquez pour développer",
"DisableAudio": "Empêcher l'espace de travail de lire de l'audio.",
"DisableAudioTitle": "Désactiver l'audio",
"DisableNotification": "Empêcher l'espace de travail d'envoyer des notifications.",
"HibernateTitle": "Mettre en veille lorsqu'il n'est pas utilisé",
"DisableNotificationTitle": "Désactiver les notifications",
"EnableHTTPAPI": "Activer les API HTTP",
"EnableHTTPAPIDescription": "Permettre à des programmes tiers tels que TidGi-Mobile, Tiddlywiki-Collector webclipper, etc. de lire et de modifier vos notes via l'interface réseau HTTP.",
"EnableHTTPS": "Activer HTTPS",
"EnableHTTPSDescription": "Pour fournir un accès sécurisé chiffré TLS, vous devez avoir votre propre certificat HTTPS, qui peut être téléchargé auprès du fournisseur de nom de domaine, ou vous pouvez rechercher des méthodes d'application de certificat HTTPS gratuit.",
"ExcludedPlugins": "plugins à ignorer",
"ExcludedPluginsDescription": "Lors du démarrage du wiki en mode lecture seule en tant que blog, vous pouvez ne pas vouloir charger certains plugins liés à l'édition pour réduire la taille de la page web chargée en premier, tels que $:/plugins/tiddlywiki/codemirror, etc. Après tout, le blog chargé n'a pas besoin de ces fonctions d'édition.",
"Generate": "Générer",
"HTTPSCertPath": "Chemin du fichier de certificat",
"HTTPSCertPathDescription": "Emplacement du fichier de certificat avec le suffixe .crt, généralement se terminant par xxx_public.crt.",
"HTTPSKeyPath": "Chemin du fichier de clé",
"HTTPSKeyPathDescription": "Emplacement du fichier de clé privée avec le suffixe .key.",
"HTTPSPickCert": "Sélectionnez le chemin du fichier de certificat",
"HTTPSPickKey": "Sélectionnez le chemin du fichier de clé",
"HTTPSUploadCert": "Ajouter un fichier de certificat",
"HTTPSUploadKey": "Ajouter un fichier de clé",
"HibernateDescription": "Économiser l'utilisation du CPU, de la mémoire et de la batterie. Cela désactivera la synchronisation automatique, vous devrez effectuer manuellement les commits et la synchronisation pour sauvegarder les données.",
"SelectLocal": "Sélectionner une image locale...",
"ResetDefaultIcon": "Réinitialiser l'icône par défaut",
"NoRevert": "Attention ! Cette opération ne peut pas être annulée.",
"HibernateTitle": "Mettre en veille lorsqu'il n'est pas utilisé",
"IsSubWorkspace": "Est un sous-espace de travail",
"LastNodeJSArgv": "Arguments de ligne de commande du dernier démarrage",
"LastVisitState": "Dernière page visitée",
"URL": "URL du Wiki",
"Port": "Port du serveur local",
"MainWorkspacePath": "Chemin de l'espace de travail principal",
"MiscOptions": "Divers",
"Name": "Nom de l'espace de travail",
"NameDescription": "Le nom de l'espace de travail, qui sera affiché sur la barre latérale, peut être différent du nom réel du dossier du dépôt Git dans l'espace de travail",
"NoRevert": "Attention ! Cette opération ne peut pas être annulée.",
"Path": "Chemin du Wiki",
"PathDescription": "Emplacement de votre dossier wiki local.",
"Port": "Port du serveur local",
"ReadOnlyMode": "Mode lecture seule",
"ReadOnlyModeDescription": "Peut être utilisé avec la pénétration du réseau local, permettant à TidGi de fonctionner comme un programme serveur pour déployer des blogs. Après ouverture, le wiki ne peut être modifié qu'en modifiant directement le fichier sur le disque (y compris en utilisant la synchronisation git). Le contenu ne peut pas être modifié sur la page web, mais tout le monde peut y accéder.",
"ResetDefaultIcon": "Réinitialiser l'icône par défaut",
"Save": "Enregistrer",
"SaveAndSyncOptions": "Enregistrer et synchroniser",
"SelectLocal": "Sélectionner une image locale...",
"ServerOptions": "Options de blog et de serveur",
"SyncOnInterval": "Synchroniser à intervalle",
"SyncOnIntervalDescription": "Lorsqu'il est activé, il se synchronisera automatiquement selon l'intervalle de temps dans les paramètres globaux, et se synchronisera toujours automatiquement au démarrage, ou manuellement en cliquant sur le bouton. Sauvegardera automatiquement les données dans le git local avant la synchronisation. Si désactivé, il n'y a qu'une seule synchronisation automatique lors de l'ouverture de l'application, et une synchronisation manuelle lorsque l'utilisateur la déclenche en cliquant sur le bouton de synchronisation dans le wiki.",
"SyncOnStartup": "Synchroniser au démarrage de l'application",
"SyncOnStartupDescription": "Commit et synchronisation une fois l'application démarrée à froid.",
"Name": "Nom de l'espace de travail",
"NameDescription": "Le nom de l'espace de travail, qui sera affiché sur la barre latérale, peut être différent du nom réel du dossier du dépôt Git dans l'espace de travail",
"BackupOnInterval": "Sauvegarde à intervalle",
"BackupOnIntervalDescription": "Lorsqu'il est activé, les données seront automatiquement sauvegardées avec le git local à intervalles réguliers (intervalle dans les paramètres globaux), de sorte que même si aucune adresse de synchronisation git cloud n'est configurée, elles seront automatiquement sauvegardées localement.",
"TiddlyWiki": "",
"TokenAuth": "Authentification par jeton",
"TokenAuthAutoFillUserNameDescription": "Cette fonctionnalité nécessite que le nom d'utilisateur soit rempli dans les paramètres globaux ou les paramètres de l'espace de travail, s'il est vide, un nom par défaut sera automatiquement rempli dans les paramètres de l'espace de travail, vous pouvez le modifier plus tard.",
"TokenAuthCurrentHeader": "En-tête d'authentification actuelle",
"TokenAuthCurrentToken": "Jeton actuel d'authentification par jeton",
"TokenAuthCurrentTokenDescription": "Ce jeton est confidentiel, il doit être régénéré après avoir été divulgué à une entité hostile, et les informations d'identification doivent être mises à jour pour les applications tierces connectées après régénération",
"TokenAuthCurrentTokenEmptyText": "Cliquez sur le bouton Générer pour générer un nouveau jeton",
"TokenAuthDescription": "Lorsqu'il est activé, les informations d'identification doivent être incluses dans la requête HTTP pour lire et écrire dans votre base de connaissances, ce qui empêche d'autres personnes sur le même réseau local d'accéder aux notes, améliorant ainsi la sécurité du serveur. Ne peut pas être activé en même temps que le mode lecture seule.",
"URL": "URL du Wiki",
"UploadOrSelectPathDescription": "Cliquez sur le bouton de téléchargement pour soumettre le fichier à Taiji pour stockage, ou cliquez sur le bouton de sélection de chemin pour sélectionner le fichier à partir de votre emplacement de stockage.",
"WikiRootTiddler": "Tiddler racine du Wiki",
"WikiRootTiddlerDescription": "Le tiddler racine du Wiki détermine le comportement de base du système, veuillez lire la documentation officielle pour comprendre avant de modifier",
"WikiRootTiddlerItems": {
"all": "Tout charger en une fois",
"lazy-images": "Charger les images à la demande",
"lazy-all": "Charger les images et le texte à la demande"
},
"ReadOnlyModeDescription": "Peut être utilisé avec la pénétration du réseau local, permettant à TidGi de fonctionner comme un programme serveur pour déployer des blogs. Après ouverture, le wiki ne peut être modifié qu'en modifiant directement le fichier sur le disque (y compris en utilisant la synchronisation git). Le contenu ne peut pas être modifié sur la page web, mais tout le monde peut y accéder.",
"ReadOnlyMode": "Mode lecture seule",
"TokenAuth": "Authentification par jeton",
"TokenAuthDescription": "Lorsqu'il est activé, les informations d'identification doivent être incluses dans la requête HTTP pour lire et écrire dans votre base de connaissances, ce qui empêche d'autres personnes sur le même réseau local d'accéder aux notes, améliorant ainsi la sécurité du serveur. Ne peut pas être activé en même temps que le mode lecture seule.",
"TokenAuthAutoFillUserNameDescription": "Cette fonctionnalité nécessite que le nom d'utilisateur soit rempli dans les paramètres globaux ou les paramètres de l'espace de travail, s'il est vide, un nom par défaut sera automatiquement rempli dans les paramètres de l'espace de travail, vous pouvez le modifier plus tard.",
"ServerOptions": "Options de blog et de serveur",
"EnableHTTPS": "Activer HTTPS",
"EnableHTTPSDescription": "Pour fournir un accès sécurisé chiffré TLS, vous devez avoir votre propre certificat HTTPS, qui peut être téléchargé auprès du fournisseur de nom de domaine, ou vous pouvez rechercher des méthodes d'application de certificat HTTPS gratuit.",
"HTTPSUploadCert": "Ajouter un fichier de certificat",
"HTTPSUploadKey": "Ajouter un fichier de clé",
"TokenAuthCurrentHeader": "En-tête d'authentification actuelle",
"UploadOrSelectPathDescription": "Cliquez sur le bouton de téléchargement pour soumettre le fichier à Taiji pour stockage, ou cliquez sur le bouton de sélection de chemin pour sélectionner le fichier à partir de votre emplacement de stockage.",
"AddExcludedPlugins": "Entrez le nom du plugin que vous souhaitez ignorer",
"HTTPSPickCert": "Sélectionnez le chemin du fichier de certificat",
"HTTPSPickKey": "Sélectionnez le chemin du fichier de clé",
"AddExcludedPluginsDescription": "Vous pouvez rechercher des plugins installés dans le wiki actuel, ou entrer n'importe quel nom de plugin.",
"ExcludedPlugins": "plugins à ignorer",
"HTTPSCertPathDescription": "Emplacement du fichier de certificat avec le suffixe .crt, généralement se terminant par xxx_public.crt.",
"ExcludedPluginsDescription": "Lors du démarrage du wiki en mode lecture seule en tant que blog, vous pouvez ne pas vouloir charger certains plugins liés à l'édition pour réduire la taille de la page web chargée en premier, tels que $:/plugins/tiddlywiki/codemirror, etc. Après tout, le blog chargé n'a pas besoin de ces fonctions d'édition.",
"HTTPSCertPath": "Chemin du fichier de certificat",
"HTTPSKeyPath": "Chemin du fichier de clé",
"HTTPSKeyPathDescription": "Emplacement du fichier de clé privée avec le suffixe .key.",
"LastNodeJSArgv": "Arguments de ligne de commande du dernier démarrage",
"EnableHTTPAPI": "Activer les API HTTP",
"EnableHTTPAPIDescription": "Permettre à des programmes tiers tels que TidGi-Mobile, Tiddlywiki-Collector webclipper, etc. de lire et de modifier vos notes via l'interface réseau HTTP.",
"TokenAuthCurrentToken": "Jeton actuel d'authentification par jeton",
"TokenAuthCurrentTokenDescription": "Ce jeton est confidentiel, il doit être régénéré après avoir été divulgué à une entité hostile, et les informations d'identification doivent être mises à jour pour les applications tierces connectées après régénération",
"Generate": "Générer",
"TokenAuthCurrentTokenEmptyText": "Cliquez sur le bouton Générer pour générer un nouveau jeton",
"ClickToExpand": "Cliquez pour développer",
"MainWorkspacePath": "Chemin de l'espace de travail principal",
"IsSubWorkspace": "Est un sous-espace de travail",
"AppearanceOptions": "Options d'apparence",
"SaveAndSyncOptions": "Enregistrer et synchroniser",
"MiscOptions": "Divers"
}
},
"Dialog": {
"CantFindWorkspaceFolderRemoveWorkspace": "Impossible de trouver le dossier de l'espace de travail qui était encore là avant ! \nLes dossiers qui devraient exister ici ont peut-être été supprimés, ou il n'y a pas de wiki dans ce dossier ! \nVoulez-vous supprimer l'espace de travail ?",
"DoNotCare": "Non, peu importe",
"NeedCorrectTiddlywikiFolderPath": "Le chemin correct doit être passé, et ce chemin ne peut pas être reconnu par TiddlyWiki.",
"PathPassInCantUse": "Le chemin passé ne peut pas être utilisé",
"RemoveWorkspace": "Supprimer l'espace de travail",
"WorkspaceFolderRemoved": "Le dossier de l'espace de travail est déplacé ou n'est pas un dossier wiki",
"StorageServiceUserInfoNoFound": "Les informations utilisateur de votre service de stockage sont introuvables",
"StorageServiceUserInfoNoFoundDetail": "Il semble que vous ne vous soyez pas connecté à votre service de stockage, nous désactivons donc la synchronisation pour ce wiki.",
"RestartMessage": "Vous devez redémarrer l'application pour que ce changement prenne effet.",
"Later": "Plus tard",
"RestartAppNow": "Redémarrer l'application maintenant",
"RestartWikiNow": "Redémarrer le Wiki maintenant",
"Restarting": "Redémarrage",
"MadeWithLove": "<0>Fait avec </0><1>❤</1><2> par </2>",
"ReportBug": "Signaler un bug",
"ReportBugDetail": "Si vous avez lu le tutoriel, et lu attentivement le texte de sortie des erreurs, et vérifié judicieusement votre saisie, vous pouvez cliquer sur le bouton."
"Error": {
"ALreadyExistErrorDescription": "Un dossier existe déjà à ce chemin, et une nouvelle base de connaissances ne peut pas être créée ici.",
"AlreadyExistError": "Le dossier existe déjà ici.",
"CopyWikiTemplateError": "E-3 CopyWikiTemplateError",
"CopyWikiTemplateErrorDescription": "E-3 Tentative de copier ou de remplacer le dernier modèle wiki à l'emplacement correspondant, mais a échoué. Cela devrait être causé par votre saisie.",
"DoubleWikiInstanceError": "E-4 DoubleWikiInstanceError",
"DoubleWikiInstanceErrorDescription": "E-4 Vous avez démarré le même Wiki deux fois. Cela peut être causé par un bug dans le programme.",
"HTMLCanNotLoadError": "Le chemin du fichier HTML actuel ne peut pas être utilisé.",
"HTMLCanNotLoadErrorDescription": "Veuillez entrer un chemin vers un fichier tiddlywiki.html valide.",
"InitWikiGitError": "E-1 InitWikiGitError",
"InitWikiGitErrorDescription": "E-1 Le modèle utilisé par le nouveau dépôt de notes a échoué à copier ou l'initialisation git du dépôt de notes a échoué. Cela devrait être un bug.",
"InitWikiGitRevertError": "E-2 InitWikiGitRevertError",
"InitWikiGitRevertErrorDescription": "E-2 Non seulement l'initialisation du dépôt de notes a échoué, mais la révocation a également échoué. C'est un problème grave, et vous devez nettoyer manuellement le nouveau dossier généré à cet emplacement.",
"InitWikiGitSyncedWikiNoGitUserInfoError": "E-6 InitWikiGitSyncedWikiNoGitUserInfoErrorDescription",
"InitWikiGitSyncedWikiNoGitUserInfoErrorDescription": "E-6 L'initialisation du dépôt de notes synchronisé avec le cloud nécessite que vous sélectionniez une adresse de dépôt git cloud et fournissiez les informations d'identification pour le service cloud correspondant. Cependant, ces informations ne sont pas disponibles actuellement.",
"InsertMenuAfterSubMenuIndexError": "E-5 InsertMenuAfterSubMenuIndexError",
"InsertMenuAfterSubMenuIndexErrorDescription": "E-5 Vous essayez d'insérer un menu avec afterSubMenu \"{{afterSubMenu}}\" dans le menu \"{{menuID}}\", mais nous ne pouvons pas le trouver dans le menu \"{{menu}}\", veuillez spécifier un élément de menu avec l'attribut id correct",
"MainWindowMissing": "E-7 Ce programme ne peut pas accéder aux données de la fenêtre principale, ne peut pas fonctionner normalement.",
"SubWikiSMainWikiNotExistError": "Le wiki principal auquel le sous-wiki est attaché n'existe pas",
"SubWikiSMainWikiNotExistErrorDescription": "Un sous-wiki doit choisir un wiki principal auquel s'attacher lors de sa création, mais maintenant le wiki principal auquel ce sous-wiki devrait être attaché ne peut pas être trouvé et ne peut pas être attaché.",
"ViewLoadUrlError": "E-9 Erreur de chargement de la page web",
"ViewLoadUrlErrorDescription": "E-9 La page Wiki correspondant à l'espace de travail n'a pas pu être chargée, mais nous essaierons à nouveau bientôt",
"WikiRuntimeError": "E-13 Erreur d'exécution du Wiki",
"WikiRuntimeErrorDescription": "E-13 Il y a une erreur lors de l'exécution du wiki. Veuillez vérifier le fichier journal pour la raison, et télécharger et soumettre un problème pour réparation.",
"WorkspaceFailedToLoadError": "E-8 WorkspaceFailedToLoadError",
"WorkspaceFailedToLoadErrorDescription": "E-8 La page Wiki correspondant à l'espace de travail n'a pas pu être chargée. Il y a de nombreuses raisons, mais c'est essentiellement à cause de bugs du programme.",
"ZxInitializationError": "E-12 Erreur d'initialisation du service d'exécution de code Zx",
"ZxInitializationErrorDescription": "E-12 Erreur d'initialisation du service d'exécution de code Zx, veuillez vérifier le fichier journal pour la raison, et télécharger et soumettre le problème pour réparation.",
"ZxInitializationRetryFailedError": "E-10 Erreur de réessai d'initialisation du service d'exécution de code Zx",
"ZxInitializationRetryFailedErrorDescription": "E-10 Erreur d'initialisation du service d'exécution de code Zx, l'erreur persiste après plusieurs tentatives de réessai, veuillez télécharger le fichier journal et soumettre un problème pour signaler l'erreur pour réparation.",
"ZxNotInitializedError": "E-11 Erreur de non-initialisation du service d'exécution de code Zx",
"ZxNotInitializedErrorDescription": "E-11 Le service d'exécution de code Zx n'est pas initialisé avec succès et essaiera automatiquement de s'initialiser."
},
"ErrorMessage": "Message d'erreur",
"Help": {
"Alternatives": "Alternatives",
"Contribute": "Contribuer à ce site",
"Description": "Cliquer sur le bouton \"Ouvrir\" ouvrira la page dans une nouvelle fenêtre. La page doit être chargée depuis Internet pour la première fois (5s - 1min), elle n'est donc pas disponible lorsque le réseau est déconnecté. \nVous pouvez modifier le contenu de la page ouverte à volonté comme un terrain de jeu sandbox pour essayer les fonctionnalités apprises. Si vous souhaitez enregistrer les résultats modifiés, vous pouvez cliquer sur le bouton d'enregistrement de Tiddlywiki pour l'enregistrer en tant que wiki à page unique au format HTML.",
"List": "Liste des aides",
"Tags": {
}
},
"LOG": {
"CommitBackupMessage": "Sauvegarde avec TidGi-Desktop\t",
"CommitMessage": "Synchroniser avec TidGi-Desktop"
},
"LinOnetwo": "Lin Onetwo",
"Loading": "Chargement",
"Log": {
"AddComplete": "Ajout Git réussi",
"AddingFiles": "Commencer à ajouter vos fichiers à sauvegarder avec Git",
"CantForcePullError": "Échec du pull forcé, peut-être que le dépôt est dans un état spécial",
"CantSyncGitNotInitialized": "Impossible de synchroniser, ce dossier n'est pas initialisé en tant que dépôt Git",
"CantSyncInSpecialGitStateAutoFixFailed": "Impossible de synchroniser, ce dossier est dans un état spécial, il ne peut donc pas être synchronisé directement. Une tentative de correction automatique a été effectuée, mais l'erreur persiste. Veuillez résoudre tous les conflits manuellement (par exemple, utilisez VSCode pour ouvrir le dossier wiki), si cela ne fonctionne toujours pas, veuillez utiliser des outils Git professionnels (Source Tree, GitKraken) pour résoudre ce problème.",
"CantSyncInSpecialGitStateAutoFixSucceed": "Ce dossier est dans un état spécial, il ne pouvait pas être synchronisé directement, mais il a été réparé automatiquement",
"CantSynchronizeAndSyncScriptIsInDeadLoop": "Impossible de synchroniser, et le script de synchronisation est dans une boucle infinie.",
"CheckingLocalGitRepoSanity": "Vérification de l'initialisation correcte du dépôt Git local",
"CheckingLocalSyncState": "Détection de l'état local nécessitant une synchronisation avec le cloud",
"CheckingRebaseStatus": "Analyse du plan de traitement du rebase",
"CommitComplete": "Commit local terminé",
"FailedToOpenDirectory": "Échec de l'ouverture du répertoire {{path}} {{errorMessage}}",
"FailedToOpenFile": "Échec de l'ouverture du fichier {{path}} {{errorMessage}}",
"FetchingData": "Récupération des données du cloud pour comparaison",
"FinishForcePull": "Pull forcé terminé",
"GitMergeFailed": "Les résultats de la fusion Git ne sont pas bons, il peut y avoir des failles dans la stratégie de fusion",
"GitPushFailed": "Le résultat du push Git est mauvais, cela signifie généralement qu'il y a un problème de réseau.",
"GitRepositoryConfigurateFailed": "La configuration du dépôt Git a échoué, voir le journal des erreurs pour plus de détails",
"GitRepositoryConfigurationFinished": "Le dépôt Git est configuré",
"GitTokenExpireOrWrong": "Le jeton Git a expiré et vous devez vous reconnecter, ou le jeton ne correspond pas au nom d'utilisateur",
"GitTokenMissing": "Jeton Git manquant",
"HaveThingsToCommit": "Il y a du contenu qui doit être soumis (commit), et il est en cours de soumission automatique",
"StartGitInitialization": "Commencer à initialiser le dépôt Git local",
"InitializeWikiGit": "Initialisation du Wiki et de Git",
"InitializeWorkspaceView": "Initialisation de l'espace de travail et de la vue du navigateur, et chargement du contenu web, veuillez patienter",
"InitializeWorkspaceViewDone": "Créé avec succès, le contenu sera bientôt chargé",
"LocalAheadStartUpload": "L'état local est en avance sur le cloud, et le téléchargement commence",
"LocalStateBehindSync": "L'état local est en retard par rapport au cloud, commence à fusionner les données du cloud.",
"LocalStateDivergeRebase": "L'état local diverge du cloud et commence à rebaser (Rebase)",
"NoNeedToSync": "Pas besoin de synchroniser, l'état local est cohérent avec le cloud",
"NotAGitRepository": "Ce n'est pas un dépôt git",
"PerformLastCheckBeforeSynchronizationFinish": "Effectuer la dernière vérification avant la fin de la synchronisation",
"PrepareCloneOnlineWiki": "Préparation à l'importation d'un wiki en ligne.",
"PrepareSync": "Préparation à la synchronisation, utilisation des informations d'auteur connectées",
"PreparingUserInfo": "Configuration des informations d'identité",
"RebaseConflictNeedsResolve": "Conflit trouvé lors de l'exécution du rebase git, besoin de résoudre le conflit.",
"RebaseSucceed": "Rebase réussi, début du téléchargement",
"SkipForcePull": "Ignorer le pull forcé, pas de nouvelles du distant",
"StartBackupToGithubRemote": "Le Git local où se trouve le Wiki est en cours de sauvegarde vers le dépôt distant Github. Le temps nécessaire dépend de la vitesse d'Internet, veuillez être patient",
"StartConfiguringGithubRemoteRepository": "Après l'initialisation du dépôt, commencer à configurer le dépôt distant Github",
"StartFetchingFromGithubRemote": "Récupération des données du dépôt distant Github, le temps nécessaire dépend de la vitesse d'Internet, veuillez être patient.",
"StartForcePull": "Commencer le pull forcé distant, remplacera complètement le local",
"StartGitInitialization": "Commencer à initialiser le dépôt Git local",
"StartResettingLocalToRemote": "Commencer à effacer le local et remplacer par le contenu distant",
"SyncFailedSystemError": "La synchronisation a échoué, il peut y avoir un problème avec le système de synchronisation",
"SynchronizationFailed": "La synchronisation a échoué ! \nVous devez utiliser des outils tels que Github Desktop pour vérifier l'état du dépôt Git actuel. \nL'échec peut être causé par le réseau. Si c'est le cas, vous pouvez réessayer après avoir ajusté le réseau.",
"SynchronizationFinish": "Synchronisation terminée",
"UsingUrlAndUsername": "Utilisation de l'URL Git {{githubRepoUrl}} avec le nom d'utilisateur {{username}} et le jeton d'accès {{accessToken}}",
"GitTokenMissing": "Jeton Git manquant",
"AddingFiles": "Commencer à ajouter vos fichiers à sauvegarder avec Git",
"AddComplete": "Ajout Git réussi",
"CheckingLocalGitRepoSanity": "Vérification de l'initialisation correcte du dépôt Git local",
"CheckingLocalSyncState": "Détection de l'état local nécessitant une synchronisation avec le cloud",
"CheckingRebaseStatus": "Analyse du plan de traitement du rebase",
"InitializeWikiGit": "Initialisation du Wiki et de Git",
"InitializeWorkspaceView": "Initialisation de l'espace de travail et de la vue du navigateur, et chargement du contenu web, veuillez patienter",
"GitTokenExpireOrWrong": "Le jeton Git a expiré et vous devez vous reconnecter, ou le jeton ne correspond pas au nom d'utilisateur",
"InitializeWorkspaceViewDone": "Créé avec succès, le contenu sera bientôt chargé",
"FailedToOpenFile": "Échec de l'ouverture du fichier {{path}} {{errorMessage}}",
"FailedToOpenDirectory": "Échec de l'ouverture du répertoire {{path}} {{errorMessage}}",
"CantForcePullError": "Échec du pull forcé, peut-être que le dépôt est dans un état spécial",
"StartForcePull": "Commencer le pull forcé distant, remplacera complètement le local",
"SkipForcePull": "Ignorer le pull forcé, pas de nouvelles du distant",
"StartResettingLocalToRemote": "Commencer à effacer le local et remplacer par le contenu distant",
"FinishForcePull": "Pull forcé terminé"
"SynchronizationFinish": "Synchronisation terminée"
},
"Cancel": "Annuler",
"Menu": {
"ActualSize": "Taille réelle",
"Close": "Fermer",
"CurrentWorkspace": "Espace de travail actuel",
"DeveloperToolsActiveWorkspace": "Ouvrir les outils de développement de l'espace de travail actif",
"Edit": "Modifier",
"ExportActiveTiddler": "Exporter le Tiddler actif",
"ExportWholeWikiHTML": "Exporter tout le Wiki en HTML vers un dossier",
"Find": "Trouver",
"FindMatches": "correspondances",
"FindNext": "Trouver suivant",
"FindPrevious": "Trouver précédent",
"Help": "Aide",
"History": "Historique",
"Home": "Accueil",
"Language": "Langue",
"LearnMore": "En savoir plus...",
"PrintPage": "Imprimer la page",
"ReportBugViaGithub": "Signaler un bug via GitHub...",
"RequestFeatureViaGithub": "Demander une nouvelle fonctionnalité via GitHub...",
"SelectNextWorkspace": "Sélectionner l'espace de travail suivant",
"SelectPreviousWorkspace": "Sélectionner l'espace de travail précédent",
"TidGi": "TidGi",
"TidGiMenuBar": "Barre de menu TidGi",
"View": "Vue",
"Wiki": "Wiki",
"Window": "Fenêtre",
"Workspaces": "Espaces de travail",
"ZoomIn": "Agrandir",
"ZoomOut": "Rétrécir"
},
"No": "Non",
"Open": "Ouvrir",
"Preference": {
"AlwaysOnTop": "Toujours au-dessus",
"AlwaysOnTopDetail": "Garder la fenêtre principale de TidGi toujours au-dessus des autres fenêtres, et ne sera pas couverte par d'autres fenêtres",
"AntiAntiLeech": "Certains sites web ont une protection anti-leech, empêchant certaines images d'être affichées sur votre wiki, nous simulons un en-tête de requête qui ressemble à la visite de ce site web pour contourner cette protection.",
"AskDownloadLocation": "Demander où enregistrer chaque fichier avant de télécharger",
"AttachToMenuBar": "Attacher à la barre de menu",
"AttachToMenuBarShowSidebar": "Attacher à la barre de menu Afficher la barre latérale",
"AttachToMenuBarShowSidebarTip": "En général, la petite fenêtre TidGi est uniquement utilisée pour visualiser rapidement l'espace de travail actuel, donc la synchronisation avec l'espace de travail de la fenêtre principale n'est pas nécessaire, la barre latérale est masquée par défaut.",
"AttachToMenuBarTip": "Créer une petite fenêtre contextuelle TidGi qui apparaît lorsque vous cliquez sur l'icône mini de la barre d'application. Astuce : Cliquez avec le bouton droit sur l'icône mini de l'application pour accéder au menu contextuel.",
"AttachToTaskbar": "Attacher à la barre des tâches",
"AttachToTaskbarShowSidebar": "Attacher à la barre des tâches Afficher la barre latérale",
"ChooseLanguage": "Choisir la langue 选择语言",
"ClearBrowsingData": "Effacer les données de navigation (git n'est pas affecté)",
"General": "UI & Interact",
"Sync": "Synchronisation & Sauvegarde",
"SyncInterval": "Intervalle de synchronisation/sauvegarde",
"SyncIntervalDescription": "Après cette durée, il commencera automatiquement à sauvegarder sur Github, si c'est un espace de travail local, il créera une sauvegarde git locale (prend effet après le redémarrage de l'application)",
"ClearBrowsingDataDescription": "Effacer les cookies, le cache, et plus",
"ClearBrowsingDataMessage": "Êtes-vous sûr ? Toutes les données de navigation seront effacées. Cette action ne peut pas être annulée.",
"ConfirmDeleteExternalApiDatabase": "Êtes-vous sûr de vouloir supprimer la base de données contenant les informations de débogage d'API externes ? Cette action est irréversible.",
"DarkTheme": "Thème sombre",
"DefaultUserName": "Nom d'utilisateur",
"DefaultUserNameDetail": "Le nom d'utilisateur dans le Wiki, cela ne prend effet qu'après redémarrage, cela remplira le champ créateur des tiddlers nouvellement créés ou modifiés. Peut être remplacé par le nom d'utilisateur défini dans les paramètres de l'espace de travail.",
"ShowSideBarDetail": "La barre latérale vous permet de basculer facilement entre les espaces de travail.",
"ShowSideBarText": "Afficher le libellé du bouton sur la barre latérale",
"ShowNavigationBar": "Afficher la barre de navigation",
"ShowNavigationBarDetail": "La barre de navigation en haut vous permet de revenir en arrière, d'avancer, d'aller à la page d'accueil, de recharger et de voir l'URL.",
"ShowTitleBar": "Afficher la barre de titre",
"ShowTitleBarDetail": "La barre de titre vous montre le titre de la page actuelle.",
"HideMenuBar": "Masquer la barre de menu",
"HideMenuBarDetail": "Masquer la barre de menu sauf si Alt+M est pressé.",
"AttachToTaskbar": "Attacher à la barre des tâches",
"AttachToMenuBar": "Attacher à la barre de menu",
"AttachToMenuBarTip": "Créer une petite fenêtre contextuelle TidGi qui apparaît lorsque vous cliquez sur l'icône mini de la barre d'application. Astuce : Cliquez avec le bouton droit sur l'icône mini de l'application pour accéder au menu contextuel.",
"OpenLogFolder": "Ouvrir le dossier de journal",
"OpenLogFolderDetail": "Lors du signalement d'un problème, veuillez ouvrir le dernier fichier .log dans le dossier et envoyer son contenu au développeur, ou le coller sur pastebin.com puis coller l'URL dans le problème Github",
"SystemDefaultTheme": "Thème par défaut du système",
"LightTheme": "Thème clair",
"DarkTheme": "Thème sombre",
"ShowSideBar": "Afficher la barre latérale",
"Theme": "Thème",
"Token": "Informations d'identification Git",
"TokenDescription": "Les informations d'identification utilisées pour s'authentifier auprès du serveur Git afin de pouvoir synchroniser le contenu en toute sécurité. Peut être obtenu en se connectant à des services de stockage (par exemple, Github), ou en obtenant manuellement un \"jeton d'accès personnel\" et en le remplissant ici.",
"Reset": "Êtes-vous sûr ? Toutes les préférences seront réinitialisées à leurs valeurs par défaut d'origine. Les données de navigation ne seront pas affectées. Cette action ne peut pas être annulée.",
"ResetNow": "Réinitialiser maintenant",
"ClearBrowsingDataMessage": "Êtes-vous sûr ? Toutes les données de navigation seront effacées. Cette action ne peut pas être annulée.",
"Notifications": "Notifications",
"NotificationsDetail": "Contrôler le temps de pause des notifications",
"NotificationsDisableSchedule": "Désactiver automatiquement les notifications selon un horaire :",
"NotificationsMuteAudio": "Couper le son lorsque les notifications sont en pause",
"TestNotification": "Tester les notifications",
"ItIsWorking": "Ça fonctionne !",
"Languages": "Lang/语言",
"SpellCheck": "Vérification orthographique",
"SpellCheckLanguages": "Langues préférées pour la vérification orthographique",
"Downloads": "Téléchargements",
"DownloadLocation": "Emplacement de téléchargement",
"AskDownloadLocation": "Demander où enregistrer chaque fichier avant de télécharger",
"RememberLastVisitState": "Se souvenir de la dernière page visitée, restaurer l'état de la dernière visite à l'ouverture",
"Network": "Réseau",
"AntiAntiLeech": "Certains sites web ont une protection anti-leech, empêchant certaines images d'être affichées sur votre wiki, nous simulons un en-tête de requête qui ressemble à la visite de ce site web pour contourner cette protection.",
"DeveloperTools": "Outils de développement",
"DisableAntiAntiLeech": "Désactiver l'anti-anti-leech",
"DisableAntiAntiLeechDetail": "Activez cette option pour désactiver complètement la fonctionnalité anti-anti-leech.",
"DisableAntiAntiLeechForUrls": "Désactiver l'anti-anti-leech pour les URL",
"DisableAntiAntiLeechForUrlsDetail": "Entrez une URL par ligne pour désactiver uniquement l'anti-anti-leech pour ces URL. Parce que la fonctionnalité anti-anti-leech peut empêcher certains sites web avec anti-anti-anti-leech de charger des images.",
"PrivacyAndSecurity": "Confidentialité & Sécurité",
"ShareBrowsingData": "Partager les données de navigation (cookies, cache) entre les espaces de travail, si cette option est désactivée, vous pouvez vous connecter à différents services tiers dans chaque espace de travail.",
"IgnoreCertificateErrors": "Ignorer les erreurs de certificat réseau",
"ClearBrowsingDataDescription": "Effacer les cookies, le cache, et plus",
"System": "Système",
"OpenAtLogin": "Ouvrir à la connexion",
"OpenAtLoginMinimized": "Oui, mais minimisé (MacOS)",
"DeveloperTools": "Outils de développement",
"SwipeWithThreeFingersToNavigate": "Balayer avec trois doigts pour naviguer",
"Performance": "Performance",
"DownloadLocation": "Emplacement de téléchargement",
"Downloads": "Téléchargements",
"FriendLinks": "Liens amis",
"General": "UI & Interact",
"HibernateAllUnusedWorkspaces": "Mettre en veille les espaces de travail inutilisés au lancement de l'application",
"HibernateAllUnusedWorkspacesDescription": "Mettre en veille tous les espaces de travail au lancement, sauf le dernier espace de travail actif.",
"hardwareAcceleration": "Utiliser l'accélération matérielle lorsque disponible",
"Updates": "Mises à jour",
"RestartToApplyUpdates": "Redémarrer pour appliquer les mises à jour",
"ReceivePreReleaseUpdates": "Recevoir les mises à jour préliminaires",
"RestorePreferences": "Restaurer les préférences à leurs valeurs par défaut d'origine",
"TiddlyWiki": "TiddlyWiki",
"FriendLinks": "Liens amis",
"Miscellaneous": "Divers",
"TranslatiumIntro": "Traduire toutes les langues comme un pro",
"Translatium": "Translatium",
"WebCatalog": "WebCatalog",
"WebCatalogIntro": "Transformez magiquement n'importe quel site web en applications multiplateformes. Travaillez plus productivement et oubliez de changer d'onglet.",
"WebCatalogEngineIntro": "WebCatalog est le fondateur initial du code de TidGi, nous réutilisons beaucoup de code important de WebCatalog open-source, de nombreux remerciements à WebCatalog et à son auteur Quang Lam",
"WebSite": "Site web",
"Support": "Support",
"WikiMetaData": "Métadonnées du Wiki",
"WikiMetaDataDescription": "Configurer les métadonnées du Wiki comme les paramètres de démarrage",
"SwipeWithThreeFingersToNavigateDescription": "Naviguez entre les pages avec des gestes à trois doigts. Balayez vers la gauche pour revenir en arrière ou balayez vers la droite pour avancer.<br/>Pour l'activer, vous devez également changer<3>Préférences macOS → TrackPad → Plus de gestes → Balayer entre les pages</3>à<5>Balayer avec trois doigts</5>ou<7>Balayer avec deux ou trois doigts.</7>",
"TestNotificationDescription": "<0>Si les notifications ne s'affichent pas, assurez-vous d'activer les notifications dans<1>Préférences macOS → Notifications → TidGi</1>.</0>",
"HowToEnableNotifications": "<0>TidGi prend en charge les notifications dès la sortie de la boîte. Mais dans certains cas, pour recevoir des notifications, vous devrez configurer manuellement des paramètres supplémentaires de l'application web.</0><1>En savoir plus</1><2>.</2>",
"IgnoreCertificateErrorsDescription": "<0>Non recommandé. </0><1>En savoir plus</1>.",
"OpenMetaDataFolder": "Ouvrir le dossier des métadonnées de l'espace de travail TidGi",
"OpenMetaDataFolderDetail": "Les données de TiddlyWiki et les métadonnées de l'espace de travail de TidGi sont stockées séparément. Les métadonnées de TidGi incluent les paramètres de l'espace de travail, etc., qui sont stockées dans ce dossier au format JSON.",
"HideMenuBar": "Masquer la barre de menu",
"HideMenuBarDetail": "Masquer la barre de menu sauf si Alt+M est pressé.",
"HideSideBar": "Masquer la barre latérale",
"HideSideBarIconDetail": "Masquer l'icône et n'afficher que le nom de l'espace de travail pour rendre la liste des espaces de travail plus compacte",
"HideTitleBar": "Masquer la barre de titre",
"ToggleMenuBar": "Basculer la barre de menu",
"NoAttach": "Reprendre le mode fenêtre",
"HowToEnableNotifications": "<0>TidGi prend en charge les notifications dès la sortie de la boîte. Mais dans certains cas, pour recevoir des notifications, vous devrez configurer manuellement des paramètres supplémentaires de l'application web.</0><1>En savoir plus</1><2>.</2>",
"IgnoreCertificateErrors": "Ignorer les erreurs de certificat réseau",
"IgnoreCertificateErrorsDescription": "<0>Non recommandé. </0><1>En savoir plus</1>.",
"ItIsWorking": "Ça fonctionne !",
"Languages": "Lang/语言",
"LightTheme": "Thème clair",
"MenubarAlwaysOnTop": "Barre de menu toujours au-dessus",
"MenubarAlwaysOnTopDetail": "Garder la barre de menu de TidGi toujours au-dessus des autres fenêtres, et ne sera pas couverte par d'autres fenêtres",
"AlwaysOnTop": "Toujours au-dessus",
"AlwaysOnTopDetail": "Garder la fenêtre principale de TidGi toujours au-dessus des autres fenêtres, et ne sera pas couverte par d'autres fenêtres",
"RequireRestart": "Nécessite un redémarrage",
"ChooseLanguage": "Choisir la langue 选择语言",
"SyncBeforeShutdown": "Synchroniser avant l'arrêt",
"SyncBeforeShutdownDescription": "Synchroniser automatiquement les données avant d'éteindre l'ordinateur. Notez que la fermeture manuelle de l'application ne déclenchera pas la synchronisation, afin d'éviter que les mauvaises données ne soient synchronisées lorsque l'application rencontre une erreur. \nLe système Windows ne prend pas en charge cette fonction.",
"SyncOnlyWhenNoDraft": "Synchroniser uniquement lorsqu'il n'y a pas de brouillons",
"SyncOnlyWhenNoDraftDescription": "Vérifiez s'il y a des brouillons ou des éditions WYSIWYG avant de synchroniser, le cas échéant, il ne sera pas synchronisé cette fois-ci, empêchant les brouillons d'être synchronisés avec votre blog. \n(Ne fonctionne pas pour la synchronisation avant l'arrêt, car vous pouvez vouloir apporter des brouillons d'un ordinateur à un autre pour continuer l'édition)",
"MoreWorkspaceSyncSettingsDescription": "Veuillez cliquer avec le bouton droit sur l'icône de l'espace de travail, ouvrir ses paramètres d'espace de travail en cliquant sur l'élément de menu contextuel \"Modifier l'espace de travail\", et configurer ses paramètres de synchronisation indépendants.",
"Miscellaneous": "Divers",
"MoreWorkspaceSyncSettings": "Plus de paramètres de synchronisation de l'espace de travail",
"ShowSideBarIcon": "Afficher les icônes des espaces de travail dans la barre latérale",
"HideSideBarIconDetail": "Masquer l'icône et n'afficher que le nom de l'espace de travail pour rendre la liste des espaces de travail plus compacte",
"MoreWorkspaceSyncSettingsDescription": "Veuillez cliquer avec le bouton droit sur l'icône de l'espace de travail, ouvrir ses paramètres d'espace de travail en cliquant sur l'élément de menu contextuel \"Modifier l'espace de travail\", et configurer ses paramètres de synchronisation indépendants.",
"Network": "Réseau",
"Notifications": "Notifications",
"NotificationsDetail": "Contrôler le temps de pause des notifications",
"NotificationsDisableSchedule": "Désactiver automatiquement les notifications selon un horaire :",
"NotificationsMuteAudio": "Couper le son lorsque les notifications sont en pause",
"OpenAtLogin": "Ouvrir à la connexion",
"OpenAtLoginMinimized": "Oui, mais minimisé (MacOS)",
"OpenLogFolder": "Ouvrir le dossier de journal",
"OpenLogFolderDetail": "Lors du signalement d'un problème, veuillez ouvrir le dernier fichier .log dans le dossier et envoyer son contenu au développeur, ou le coller sur pastebin.com puis coller l'URL dans le problème Github",
"OpenMetaDataFolder": "Ouvrir le dossier des métadonnées de l'espace de travail TidGi",
"OpenMetaDataFolderDetail": "Les données de TiddlyWiki et les métadonnées de l'espace de travail de TidGi sont stockées séparément. Les métadonnées de TidGi incluent les paramètres de l'espace de travail, etc., qui sont stockées dans ce dossier au format JSON.",
"OpenV8CacheFolder": "Ouvrir le dossier de cache V8",
"OpenV8CacheFolderDetail": "Le dossier de cache V8 stocke les fichiers mis en cache qui accélèrent le démarrage de l'application",
"Performance": "Performance",
"PrivacyAndSecurity": "Confidentialité & Sécurité",
"ReceivePreReleaseUpdates": "Recevoir les mises à jour préliminaires",
"RememberLastVisitState": "Se souvenir de la dernière page visitée, restaurer l'état de la dernière visite à l'ouverture",
"RequireRestart": "Nécessite un redémarrage",
"Reset": "Êtes-vous sûr ? Toutes les préférences seront réinitialisées à leurs valeurs par défaut d'origine. Les données de navigation ne seront pas affectées. Cette action ne peut pas être annulée.",
"ResetNow": "Réinitialiser maintenant",
"RestorePreferences": "Restaurer les préférences à leurs valeurs par défaut d'origine",
"RunOnBackground": "Exécuter en arrière-plan",
"RunOnBackgroundDetail": "Lorsque la fenêtre est fermée, continuer à s'exécuter en arrière-plan sans quitter. Restaurer rapidement la fenêtre lors de la réouverture de l'application.",
"RunOnBackgroundDetailNotMac": "Recommandé d'activer Attacher à la barre des tâches. Vous pouvez ainsi restaurer la fenêtre en l'utilisant.",
"AttachToTaskbarShowSidebar": "Attacher à la barre des tâches Afficher la barre latérale",
"AttachToMenuBarShowSidebar": "Attacher à la barre de menu Afficher la barre latérale",
"AttachToMenuBarShowSidebarTip": "En général, la petite fenêtre TidGi est uniquement utilisée pour visualiser rapidement l'espace de travail actuel, donc la synchronisation avec l'espace de travail de la fenêtre principale n'est pas nécessaire, la barre latérale est masquée par défaut."
"ShareBrowsingData": "Partager les données de navigation (cookies, cache) entre les espaces de travail, si cette option est désactivée, vous pouvez vous connecter à différents services tiers dans chaque espace de travail.",
"ShowSideBar": "Afficher la barre latérale",
"ShowSideBarDetail": "La barre latérale vous permet de basculer facilement entre les espaces de travail.",
"ShowSideBarIcon": "Afficher les icônes des espaces de travail dans la barre latérale",
"ShowSideBarText": "Afficher le libellé du bouton sur la barre latérale",
"ShowTitleBar": "Afficher la barre de titre",
"ShowTitleBarDetail": "La barre de titre vous montre le titre de la page actuelle.",
"SpellCheck": "Vérification orthographique",
"SpellCheckLanguages": "Langues préférées pour la vérification orthographique",
"Support": "Support",
"SwipeWithThreeFingersToNavigate": "Balayer avec trois doigts pour naviguer",
"SwipeWithThreeFingersToNavigateDescription": "Naviguez entre les pages avec des gestes à trois doigts. Balayez vers la gauche pour revenir en arrière ou balayez vers la droite pour avancer.<br/>Pour l'activer, vous devez également changer<3>Préférences macOS → TrackPad → Plus de gestes → Balayer entre les pages</3>à<5>Balayer avec trois doigts</5>ou<7>Balayer avec deux ou trois doigts.</7>",
"Sync": "Synchronisation & Sauvegarde",
"SyncBeforeShutdown": "Synchroniser avant l'arrêt",
"SyncBeforeShutdownDescription": "Synchroniser automatiquement les données avant d'éteindre l'ordinateur. Notez que la fermeture manuelle de l'application ne déclenchera pas la synchronisation, afin d'éviter que les mauvaises données ne soient synchronisées lorsque l'application rencontre une erreur. \nLe système Windows ne prend pas en charge cette fonction.",
"SyncInterval": "Intervalle de synchronisation/sauvegarde",
"SyncIntervalDescription": "Après cette durée, il commencera automatiquement à sauvegarder sur Github, si c'est un espace de travail local, il créera une sauvegarde git locale (prend effet après le redémarrage de l'application)",
"SyncOnlyWhenNoDraft": "Synchroniser uniquement lorsqu'il n'y a pas de brouillons",
"SyncOnlyWhenNoDraftDescription": "Vérifiez s'il y a des brouillons ou des éditions WYSIWYG avant de synchroniser, le cas échéant, il ne sera pas synchronisé cette fois-ci, empêchant les brouillons d'être synchronisés avec votre blog. \n(Ne fonctionne pas pour la synchronisation avant l'arrêt, car vous pouvez vouloir apporter des brouillons d'un ordinateur à un autre pour continuer l'édition)",
"System": "Système",
"SystemDefaultTheme": "Thème par défaut du système",
"TestNotification": "Tester les notifications",
"TestNotificationDescription": "<0>Si les notifications ne s'affichent pas, assurez-vous d'activer les notifications dans<1>Préférences macOS → Notifications → TidGi</1>.</0>",
"Theme": "Thème",
"TiddlyWiki": "TiddlyWiki",
"ToggleMenuBar": "Basculer la barre de menu",
"Token": "Informations d'identification Git",
"TokenDescription": "Les informations d'identification utilisées pour s'authentifier auprès du serveur Git afin de pouvoir synchroniser le contenu en toute sécurité. Peut être obtenu en se connectant à des services de stockage (par exemple, Github), ou en obtenant manuellement un \"jeton d'accès personnel\" et en le remplissant ici.",
"Translatium": "Translatium",
"TranslatiumIntro": "Traduire toutes les langues comme un pro",
"Updates": "Mises à jour",
"WebCatalog": "WebCatalog",
"WebCatalogEngineIntro": "WebCatalog est le fondateur initial du code de TidGi, nous réutilisons beaucoup de code important de WebCatalog open-source, de nombreux remerciements à WebCatalog et à son auteur Quang Lam",
"WebCatalogIntro": "Transformez magiquement n'importe quel site web en applications multiplateformes. Travaillez plus productivement et oubliez de changer d'onglet.",
"WebSite": "Site web",
"WikiMetaData": "Métadonnées du Wiki",
"WikiMetaDataDescription": "Configurer les métadonnées du Wiki comme les paramètres de démarrage",
"hardwareAcceleration": "Utiliser l'accélération matérielle lorsque disponible"
},
"Error": {
"InitWikiGitError": "E-1 InitWikiGitError",
"InitWikiGitErrorDescription": "E-1 Le modèle utilisé par le nouveau dépôt de notes a échoué à copier ou l'initialisation git du dépôt de notes a échoué. Cela devrait être un bug.",
"InitWikiGitRevertError": "E-2 InitWikiGitRevertError",
"InitWikiGitRevertErrorDescription": "E-2 Non seulement l'initialisation du dépôt de notes a échoué, mais la révocation a également échoué. C'est un problème grave, et vous devez nettoyer manuellement le nouveau dossier généré à cet emplacement.",
"CopyWikiTemplateError": "E-3 CopyWikiTemplateError",
"CopyWikiTemplateErrorDescription": "E-3 Tentative de copier ou de remplacer le dernier modèle wiki à l'emplacement correspondant, mais a échoué. Cela devrait être causé par votre saisie.",
"DoubleWikiInstanceError": "E-4 DoubleWikiInstanceError",
"DoubleWikiInstanceErrorDescription": "E-4 Vous avez démarré le même Wiki deux fois. Cela peut être causé par un bug dans le programme.",
"InsertMenuAfterSubMenuIndexError": "E-5 InsertMenuAfterSubMenuIndexError",
"InsertMenuAfterSubMenuIndexErrorDescription": "E-5 Vous essayez d'insérer un menu avec afterSubMenu \"{{afterSubMenu}}\" dans le menu \"{{menuID}}\", mais nous ne pouvons pas le trouver dans le menu \"{{menu}}\", veuillez spécifier un élément de menu avec l'attribut id correct",
"InitWikiGitSyncedWikiNoGitUserInfoError": "E-6 InitWikiGitSyncedWikiNoGitUserInfoErrorDescription",
"InitWikiGitSyncedWikiNoGitUserInfoErrorDescription": "E-6 L'initialisation du dépôt de notes synchronisé avec le cloud nécessite que vous sélectionniez une adresse de dépôt git cloud et fournissiez les informations d'identification pour le service cloud correspondant. Cependant, ces informations ne sont pas disponibles actuellement.",
"MainWindowMissing": "E-7 Ce programme ne peut pas accéder aux données de la fenêtre principale, ne peut pas fonctionner normalement.",
"WorkspaceFailedToLoadError": "E-8 WorkspaceFailedToLoadError",
"WorkspaceFailedToLoadErrorDescription": "E-8 La page Wiki correspondant à l'espace de travail n'a pas pu être chargée. Il y a de nombreuses raisons, mais c'est essentiellement à cause de bugs du programme.",
"ViewLoadUrlError": "E-9 Erreur de chargement de la page web",
"ViewLoadUrlErrorDescription": "E-9 La page Wiki correspondant à l'espace de travail n'a pas pu être chargée, mais nous essaierons à nouveau bientôt",
"ZxInitializationRetryFailedError": "E-10 Erreur de réessai d'initialisation du service d'exécution de code Zx",
"ZxInitializationRetryFailedErrorDescription": "E-10 Erreur d'initialisation du service d'exécution de code Zx, l'erreur persiste après plusieurs tentatives de réessai, veuillez télécharger le fichier journal et soumettre un problème pour signaler l'erreur pour réparation.",
"ZxNotInitializedError": "E-11 Erreur de non-initialisation du service d'exécution de code Zx",
"ZxNotInitializedErrorDescription": "E-11 Le service d'exécution de code Zx n'est pas initialisé avec succès et essaiera automatiquement de s'initialiser.",
"ZxInitializationError": "E-12 Erreur d'initialisation du service d'exécution de code Zx",
"ZxInitializationErrorDescription": "E-12 Erreur d'initialisation du service d'exécution de code Zx, veuillez vérifier le fichier journal pour la raison, et télécharger et soumettre le problème pour réparation.",
"WikiRuntimeError": "E-13 Erreur d'exécution du Wiki",
"WikiRuntimeErrorDescription": "E-13 Il y a une erreur lors de l'exécution du wiki. Veuillez vérifier le fichier journal pour la raison, et télécharger et soumettre un problème pour réparation.",
"SubWikiSMainWikiNotExistError": "Le wiki principal auquel le sous-wiki est attaché n'existe pas",
"SubWikiSMainWikiNotExistErrorDescription": "Un sous-wiki doit choisir un wiki principal auquel s'attacher lors de sa création, mais maintenant le wiki principal auquel ce sous-wiki devrait être attaché ne peut pas être trouvé et ne peut pas être attaché.",
"HTMLCanNotLoadError": "Le chemin du fichier HTML actuel ne peut pas être utilisé.",
"HTMLCanNotLoadErrorDescription": "Veuillez entrer un chemin vers un fichier tiddlywiki.html valide.",
"ALreadyExistErrorDescription": "Un dossier existe déjà à ce chemin, et une nouvelle base de connaissances ne peut pas être créée ici.",
"AlreadyExistError": "Le dossier existe déjà ici."
},
"Loading": "Chargement",
"Yes": "Oui",
"No": "Non",
"LinOnetwo": "Lin Onetwo",
"Menu": {
"Help": "Aide",
"ReportBugViaGithub": "Signaler un bug via GitHub...",
"RequestFeatureViaGithub": "Demander une nouvelle fonctionnalité via GitHub...",
"LearnMore": "En savoir plus...",
"Edit": "Modifier",
"TidGi": "TidGi",
"TidGiMenuBar": "Barre de menu TidGi",
"View": "Vue",
"SelectPreviousWorkspace": "Sélectionner l'espace de travail précédent",
"SelectNextWorkspace": "Sélectionner l'espace de travail suivant",
"History": "Historique",
"Language": "Langue",
"Window": "Fenêtre",
"Workspaces": "Espaces de travail",
"CurrentWorkspace": "Espace de travail actuel",
"Back": "Retour",
"Find": "Trouver",
"FindNext": "Trouver suivant",
"FindPrevious": "Trouver précédent",
"Forward": "Avancer",
"DeveloperToolsActiveWorkspace": "Ouvrir les outils de développement de l'espace de travail actif",
"Home": "Accueil",
"ActualSize": "Taille réelle",
"ZoomIn": "Agrandir",
"ZoomOut": "Rétrécir",
"Close": "Fermer",
"FindMatches": "correspondances",
"PrintPage": "Imprimer la page",
"ExportActiveTiddler": "Exporter le Tiddler actif",
"Wiki": "Wiki",
"ExportWholeWikiHTML": "Exporter tout le Wiki en HTML vers un dossier"
},
"ErrorMessage": "Message d'erreur",
"ClickForDetails": "Cliquez pour plus de détails",
"Save": "sauvegarder",
"Scripting": {
"ExecutingScript": "Exécution du script"
},
"Description": "Description",
"Tags": "Étiquettes",
"Title": "Titre",
"Delete": "Supprimer",
"Edit": "Modifier",
"Open": "Ouvrir",
"Help": {
"Alternatives": "Alternatives",
"Description": "Cliquer sur le bouton \"Ouvrir\" ouvrira la page dans une nouvelle fenêtre. La page doit être chargée depuis Internet pour la première fois (5s - 1min), elle n'est donc pas disponible lorsque le réseau est déconnecté. \nVous pouvez modifier le contenu de la page ouverte à volonté comme un terrain de jeu sandbox pour essayer les fonctionnalités apprises. Si vous souhaitez enregistrer les résultats modifiés, vous pouvez cliquer sur le bouton d'enregistrement de Tiddlywiki pour l'enregistrer en tant que wiki à page unique au format HTML.",
"List": "Liste des aides",
"Contribute": "Contribuer à ce site",
"Tags": {
"Docs": "Docs",
"FAQ": "FAQ",
"Intro": "Intro"
}
"SideBar": {
"Preferences": "Préférences...",
"UpdateAvailable": "Mise à jour !"
},
"LOG": {
"CommitMessage": "Synchroniser avec TidGi-Desktop",
"CommitBackupMessage": "Sauvegarde avec TidGi-Desktop\t"
}
"Update": "mise à jour",
"Updater": {
"CheckUpdate": "Vérifier les mises à jour",
"CheckingFailed": "Échec de la vérification (erreur réseau)",
"CheckingForUpdate": "Vérification des mises à jour...",
"UpdateAvailable": "Mise à jour disponible !",
"UpdateNotAvailable": "Vous avez la dernière version"
},
"WorkspaceSelector": {
"Add": "Ajouter",
"Agent": "agent",
"AreYouSure": "Êtes-vous sûr de vouloir supprimer cet espace de travail ? \nLa suppression de l'espace de travail supprimera l'espace de travail dans cette application, mais ne supprimera pas les dossiers du disque dur. \nMais, si vous choisissez de supprimer également le dossier Wiki, tout le contenu sera supprimé.",
"DedicatedWorkspace": "Zone de travail spéciale",
"DefaultTiddlers": "Tiddlers par défaut",
"EditCurrentWorkspace": "Configurer l'espace de travail actuel",
"EditWorkspace": "Configurer l'espace de travail",
"Guide": "Guide",
"Help": "Aide",
"HibernateWorkspace": "Mettre en veille l'espace de travail",
"OpenInBrowser": "Ouvrir dans le navigateur",
"OpenInBrowserDisabledHint": "(Configurer→ActiverHTTPAPI pour activer)",
"OpenWorkspaceFolder": "Ouvrir le dossier",
"OpenWorkspaceFolderInEditor": "Ouvrir le dossier dans un éditeur externe",
"OpenWorkspaceFolderInGitGUI": "Ouvrir dans Git GUI",
"OpenWorkspaceMenuName": "Ouvrir l'espace de travail",
"OpenWorkspaceTagTiddler": "Ouvrir {{tagName}}",
"ReloadCurrentWorkspace": "Recharger l'espace de travail actuel",
"RemoveCurrentWorkspace": "Supprimer l'espace de travail actuel",
"RemoveWorkspace": "Supprimer l'espace de travail",
"RemoveWorkspaceAndDelete": "Supprimer l'espace de travail et supprimer le dossier Wiki du disque",
"WakeUpWorkspace": "Réveiller l'espace de travail"
},
"Yes": "Oui"
}

View file

@ -0,0 +1,563 @@
{
"APILogs": {
"CurrentAgent": "エージェントログを表示: {{agentId}}",
"Description": "このインテリジェントエージェントの外部API呼び出しデバッグログ。設定で「外部APIデバッグ」を有効にして記録を開始します。",
"ErrorDetails": "エラーの詳細",
"NoLogs": "このエージェントのAPIログが見つかりません",
"NoResponse": "応答なし",
"RequestDetails": "リクエストの詳細",
"ResponseContent": "レスポンス内容",
"ResponseMetadata": "応答メタデータ",
"StatusCancel": "キャンセル済み",
"StatusDone": "完了",
"StatusError": "エラー",
"StatusStart": "開始しました",
"StatusUpdate": "処理中",
"Title": "API デバッグログ"
},
"Agent": {
"EditTitle": "編集エージェント名",
"InvalidTabType": "無効なタブタイプです。チャットタブが必要です。",
"LoadingChat": "会話を読み込んでいます...",
"StartConversation": "会話を開始する",
"Untitled": "無題"
},
"Browser": {
"Back": "後退",
"Bookmark": "コレクション",
"CurrentUrl": "現在のURL",
"EnterUrlPlaceholder": "ウェブサイトを入力",
"Forward": "前進",
"Home": "ホームページ",
"Refresh": "リフレッシュ",
"RenderPlaceholder": "これはウェブページのレンダリング領域です"
},
"Chat": {
"Cancel": "キャンセル",
"ConfigError": {
"GoToSettings": "設定へ移動",
"Title": "設定の問題"
},
"InputPlaceholder": "メッセージを入力、Ctrl+Enterで送信",
"Send": "送信",
"SessionGroup": {
}
},
"Common": {
},
"ContextMenu": {
"AddToCurrentSplitView": "現在の分割画面に追加",
"Close": "閉じる",
"CloseAbove": "上のタブを閉じる",
"CloseBelow": "下のタブを閉じる",
"CloseOther": "他のタブを閉じる",
"CloseTabs": "複数のタブを閉じる",
"ConvertToSplitView": "分割ビューに切り替える",
"CreateSplitViewWithActive": "現在のタブで分割画面を作成",
"Duplicate": "コピー",
"NewTabBelow": "下に新しいタブを作成",
"Pin": "固定タブ",
"Refresh": "リフレッシュ",
"RestoreClosed": "閉じたタブを復元する",
"Unpin": "ピン留めを解除"
},
"CreateAgent": {
"AgentName": "エージェント名",
"AgentNameHelper": "あなたのエージェントに説明的な名前を付けます",
"AgentNamePlaceholder": "インテリジェントエージェント名を入力...",
"Back": "前のステップ",
"CreatingPreview": "プレビューエージェントを作成中...",
"EditPrompt": "編集プロンプト",
"EditPromptDescription": "カスタマイズ可能なインテリジェントエージェントのシステムプロンプトと動作",
"ImmediateUse": "テストして使用する",
"ImmediateUseDescription": "あなたのエージェントをテストしてすぐに使い始めましょう",
"Next": "次のステップ",
"NoTemplateSelected": "まずテンプレートを選択してください",
"Preview": "(プレビュー)",
"SaveAndUse": "保存してエージェントを使用する",
"SearchTemplates": "検索エージェントテンプレート...",
"SelectTemplate": "テンプレートを選択",
"SelectTemplateDescription": "既存のスマートエージェントを開始テンプレートとして選択する",
"SelectedTemplate": "選択されたテンプレート",
"SetupAgent": "エージェントを設定する",
"SetupAgentDescription": "あなたのエージェントに名前を付け、テンプレートを出発点として選択してください",
"Title": "新しいインテリジェントエージェントを作成する"
},
"EditAgent": {
"AgentDescription": "エージェントの説明",
"AgentDescriptionHelper": "あなたのエージェントの機能と用途を説明してください",
"AgentDescriptionPlaceholder": "入力エージェントの説明...",
"AgentName": "エージェント名",
"AgentNameHelper": "あなたのエージェントに説明的な名前を付けます",
"AgentNamePlaceholder": "インテリジェントエージェント名を入力...",
"AgentNotFound": "エージェントが見つかりません",
"EditBasic": "基本情報を編集",
"EditBasicDescription": "エージェントの基本情報を編集する",
"EditPrompt": "編集プロンプト",
"EditPromptDescription": "カスタマイズ可能なインテリジェントエージェントのシステムプロンプトと動作",
"ImmediateUse": "テストして使用する",
"ImmediateUseDescription": "あなたのエージェントをテストしてすぐに使い始めましょう",
"Loading": "読み込み中...",
"LoadingPromptConfig": "プロンプト設定を読み込み中...",
"PreviewChat": "チャットをプレビュー",
"Save": "保存",
"Saving": "保存中...",
"Title": "編集エージェントの定義"
},
"ModelFeature": {
},
"ModelSelector": {
"Model": "モデル",
"NoModelSelected": "モデルが選択されていません",
"SelectModel": "モデルを選択",
"Title": "モデル選択"
},
"NewTab": {
"CreateDefaultAgent": "デフォルトエージェントを作成する",
"CreateInstance": "インスタンスを作成する",
"CreateNewAgent": "新しいインテリジェントエージェントを作成",
"EditDefinition": "定義を編集する",
"NewTab": "新しいタブ",
"QuickAccess": "クイックアクセス",
"SearchPlaceholder": "タブまたはインテリジェントエージェントを検索..."
},
"Preference": {
"AIAgent": "エージェント",
"AIAgentDescription": "AI Agentの対話記録データベースを管理する",
"AIAgentDescriptionDetail": "ここではAI Agentの会話記録データベースのサイズと位置情報を確認および削除できます。",
"APIKey": "APIキー",
"AddNewModel": "新しいモデルを追加",
"AddNewProvider": "新しいプロバイダーを追加",
"AddProvider": "プロバイダーを追加",
"AgentDatabaseDescription": "すべてのAI Agentとの対話記録はこのデータベースに保存されており、AIとの会話のみが対象で、Wikiの内容には影響せず、使用容量は{{size}}です。",
"BaseURL": "APIベースURL",
"BaseURLRequired": "APIベースURLは必須です",
"Browse": "閲覧",
"CancelAddProvider": "追加をキャンセル",
"ConfigureModelParameters": "設定パラメータ",
"ConfigureProvider": "{{provider}}を設定",
"ConfirmDelete": "削除を確認",
"ConfirmDeleteAgentDatabase": "すべてのAI対話記録を含むデータベースを削除してもよろしいですかこの操作は取り消せません。",
"ConfirmDeleteExternalApiDatabase": "外部APIデバッグ情報を含むデータベースを削除してもよろしいですかこの操作は取り消せません。",
"CustomProvider": "カスタムプロバイダ",
"DefaultAIModelSelection": "デフォルトのAIモデル選択",
"DefaultAIModelSelectionDescription": "特に設定されていない場合に使用するデフォルトのAIプロバイダーとモデルを選択してください",
"DefaultEmbeddingModelSelection": "デフォルトの埋め込みモデル選択",
"DefaultEmbeddingModelSelectionDescription": "意味検索とベクトル操作に使用するデフォルトの埋め込みモデルの選択",
"DefaultImageGenerationModelSelection": "デフォルトの画像生成モデルの選択",
"DefaultImageGenerationModelSelectionDescription": "テキストから画像を生成する操作に使用するデフォルトの画像生成モデルを選択",
"DefaultSpeechModelSelection": "デフォルトの音声生成モデルの選択",
"DefaultSpeechModelSelectionDescription": "テキスト読み上げ操作に使用するデフォルトの音声生成モデルを選択",
"DefaultTranscriptionsModelSelection": "デフォルト音声認識モデルの選択",
"DefaultTranscriptionsModelSelectionDescription": "音声から文字への変換操作に使用するデフォルトの音声認識モデルを選択",
"DeleteAgentDatabase": "AI会話データベースを削除",
"DeleteExternalApiDatabase": "外部APIデータベースを削除する",
"DeleteProvider": "プロバイダーを削除する",
"DisabledProviderInfo": "このプロバイダーは無効になっており、そのモデルはモデル選択リストに表示されません",
"EnableProvider": "このプロバイダーを有効にする",
"ExternalAPI": "外部APIインターフェース",
"ExternalAPIDebug": "APIデバッグログを有効にする",
"ExternalAPIDebugDescription": "有効にすると、すべてのAPIリクエストとレスポンスがデバッグのためにデータベースに記録されます。",
"ExternalApiDatabaseDescription": "外部APIデバッグ情報を含むデータベースで、占有スペースは{{size}}です",
"FailedToAddModel": "モデルの追加に失敗しました",
"FailedToAddProvider": "プロバイダーの追加に失敗しました",
"FailedToRemoveModel": "モデルの削除に失敗しました",
"FailedToSaveSettings": "設定の保存に失敗しました",
"FailedToUpdateModel": "モデルを更新できません",
"FailedToUpdateProviderStatus": "プロバイダーのステータス更新に失敗しました",
"MaxTokens": "最大生成長さ",
"MaxTokensDescription": "モデルが1回のリクエストで生成できる最大文字数トークン単位",
"ModelAddedSuccessfully": "モデルが正常に追加されました",
"ModelAlreadyExists": "モデルは既に存在します",
"ModelCaption": "モデル表示名",
"ModelCaptionHelp": "インターフェースに表示するフレンドリーネーム。空白の場合はモデル名が使用されます",
"ModelDetails": "モデルの詳細",
"ModelFeatures": "モデル機能",
"ModelName": "モデル名",
"ModelNameRequired": "モデル名は必須です",
"ModelParameters": "モデルパラメータ",
"ModelParametersDescription": "生成AIモデルの動作パラメータ温度、トークン制限などを設定する",
"ModelRemovedSuccessfully": "モデルが正常に削除されました",
"ModelUpdatedSuccessfully": "モデル更新が成功しました",
"Models": "利用可能なモデル",
"NoPresetSelected": "プリセットモデルが選択されていません",
"NoProvidersAvailable": "利用可能なプロバイダーがありません",
"OpenDatabaseFolder": "データベースフォルダを開く",
"PresetModels": "プリセットモデル",
"PresetProvider": "プリセットプロバイダー",
"ProviderAddedSuccessfully": "プロバイダーが正常に追加されました",
"ProviderAlreadyExists": "プロバイダー名は既に存在します",
"ProviderClass": "プロバイダーインターフェースタイプ",
"ProviderConfiguration": "プロバイダー設定",
"ProviderConfigurationDescription": "AIプロバイダーのAPIキーやその他の設定を構成します",
"ProviderDisabled": "プロバイダーが無効になりました",
"ProviderEnabled": "プロバイダーが有効になりました",
"ProviderName": "プロバイダー名",
"ProviderNameRequired": "プロバイダー名は必須です",
"Search": "検索と埋め込み",
"SearchEmbeddingDelete": "削除",
"SearchEmbeddingDeleteConfirm": "ワークスペース「{{workspaceName}}」のすべてのベクトル埋め込みを削除してもよろしいですか?この操作は取り消せません。",
"SearchEmbeddingDeleteError": "埋め込みの削除に失敗しました:{{error}}",
"SearchEmbeddingGenerate": "埋め込みを生成",
"SearchEmbeddingGenerating": "生成中...",
"SearchEmbeddingLastUpdated": "最終更新:{{time}}",
"SearchEmbeddingNoAIConfigError": "外部APIセクションでAI APIの設定を先に構成してください。",
"SearchEmbeddingNoEmbeddingModelError": "外部APIセクションでデフォルトの埋め込みモデル設定を先に構成してください。",
"SearchEmbeddingStatusCompleted": "{{totalNotes}}個のノートの{{totalEmbeddings}}個の埋め込み",
"SearchEmbeddingStatusError": "エラー:{{error}}",
"SearchEmbeddingStatusGenerating": "生成中... ({{completed}}/{{total}})",
"SearchEmbeddingStatusIdle": "埋め込みが生成されていません",
"SearchEmbeddingUpdate": "埋め込みを更新",
"SearchNoWorkspaces": "ワークスペースが見つかりません",
"SelectDefaultProvider": "デフォルトプロバイダーを選択",
"SelectFromPresets": "プリセットモデルから選択",
"SelectModel": "モデルを選択",
"SettingsSaved": "設定が保存されました",
"SystemPrompt": "システムプロンプト",
"SystemPromptDescription": "AIに送信されるシステム命令を設定し、その動作と機能を定義します",
"SystemPromptPlaceholder": "システムプロンプトプレースホルダー",
"Temperature": "温度",
"TemperatureDescription": "低い値はより確定的で集中した応答を生成し、高い値はより多様で創造的な応答を生成します。",
"TopP": "トップP",
"TopPDescription": "応答のランダム性を制御します。低い値は応答をより確定的にし、高い値はより多くの可能性を許容します。",
"WorkflowFile": "ワークフローファイル",
"WorkflowFileHelp": "ComfyUI ワークフロー JSON ファイルのパス",
"WorkflowFilePath": "ワークフローファイルのパス"
},
"Prompt": {
"AutoRefresh": "プレビューは入力テキストの変更に応じて自動的に更新されます",
"CodeEditor": "コードエディタ",
"Flat": "グリッドビュー",
"FormEditor": "フォームエディター",
"LastUpdated": "前回の更新時間",
"Loading": "プレビューを読み込み中...",
"NoMessages": "まだプレビューできるメッセージはありません",
"Preview": "プロンプトプレビュー",
"SchemaNotProvided": "フォーマットが提供されていません",
"SchemaNotProvidedDescription": "JSONスキーマが提供されていないか、正しく取得できませんでした。編集フォームを表示できません。",
"Tree": "ツリービュー",
"ValidationErrors": "エラーを発見"
},
"PromptConfig": {
"AddItem": "プロジェクトを追加",
"EmptyArray": "まだアイテムが追加されていません。下のボタンをクリックして最初のアイテムを追加してください。",
"ItemCount": "{{count}} 件",
"RemoveItem": "リスト項目を削除",
"Tabs": {
"Prompts": "プロンプト",
"Response": "応答"
},
"Tags": {
"HelperText": "入力後、Enterキーを押してタグを追加するか、事前定義されたタグから選択してください",
"NoOptions": "選択可能なタグがありません",
"Placeholder": "入力タグ..."
}
},
"Schema": {
"AIConfig": {
"Description": "AI 会話設定の構成",
"Title": "AI設定"
},
"AgentConfig": {
"Description": "エージェント設定",
"Id": "エージェント一意識別子",
"IdTitle": "エージェントID",
"PromptConfig": {
"Description": "プロンプト設定",
"Prompts": "プロンプト設定リスト",
"Response": "応答設定リスト",
"Title": "プロンプト設定"
},
"Title": "エージェント設定"
},
"AutoReroll": {
},
"BaseAPIConfig": {
"API": "APIプロバイダーとモデル設定",
"APITitle": "API設定",
"Description": "基本API設定",
"ModelParameters": "モデルパラメータ設定",
"ModelParametersTitle": "モデルパラメータ",
"Title": "基本API設定"
},
"DefaultAgents": {
"Description": "デフォルトのインテリジェントエージェント設定リスト",
"Title": "デフォルトエージェント"
},
"DynamicPosition": {
},
"FullReplacement": {
"Description": "完全置換パラメータ設定",
"SourceType": "ソースタイプ",
"SourceTypeTitle": "ソースタイプ",
"SourceTypes": {
},
"TargetId": "ターゲット要素ID",
"TargetIdTitle": "ターゲットID",
"Title": "完全置換パラメータ"
},
"Function": {
},
"HandlerConfig": {
},
"JavascriptTool": {
},
"MCP": {
"Description": "モデルコンテキストプロトコルパラメータ設定",
"Id": "MCP サーバー ID",
"IdTitle": "サーバーID",
"ResponseProcessing": {
},
"TimeoutMessage": "タイムアウトメッセージ",
"TimeoutMessageTitle": "タイムアウトメッセージ",
"TimeoutSecond": "タイムアウト時間(秒)",
"TimeoutSecondTitle": "タイムアウト時間",
"Title": "モデルコンテキストプロトコルパラメータ"
},
"ModelParameters": {
"Description": "モデルパラメータ設定",
"MaxTokens": "生成される最大トークン数",
"MaxTokensTitle": "最大トークン数",
"SystemPrompt": "モデルシステムプロンプト",
"SystemPromptTitle": "システムプロンプト",
"Temperature": "応答生成温度(高いほど創造的)",
"TemperatureTitle": "温度",
"Title": "モデルパラメータ",
"TopP": "Top P サンプリングパラメータ",
"TopPTitle": "トップP"
},
"Position": {
"Bottom": "下部から数件のメッセージをオフセット",
"BottomTitle": "底部オフセット",
"Description": "位置パラメータ設定",
"TargetId": "ターゲット要素ID",
"TargetIdTitle": "ターゲットID",
"Title": "位置引数",
"Type": "位置タイプ",
"TypeTitle": "位置タイプ",
"Types": {
}
},
"Prompt": {
"Caption": "簡単な説明",
"CaptionTitle": "説明",
"Children": "サブプロンプトのリストは、上から下へ、外から内へと順に結合され、最終的なプロンプトテキストとなります。",
"ChildrenTitle": "サブプロンプト",
"Description": "完全なプロンプト設定、タイプと内容を含む",
"Enabled": "このプロンプトを有効にするかどうか、有効にしたものだけが最終的なプロンプトに組み込まれます。",
"EnabledTitle": "有効化",
"Id": "プロンプト設定の一意の識別子。PromptDynamicModificationでtargetIdを介して参照するのに便利です。",
"IdTitle": "ID",
"Role": "OpenAI互換インターフェースのプロンプトロール",
"RoleTitle": "キャラクター",
"RoleType": {
"Assistant": "アシスタント - AIの返信と応答内容",
"System": "システム - AIの行動ルールと背景設定を定義する",
"User": "ユーザー - ユーザーの入力とリクエストをシミュレートする"
},
"Tags": "タグリスト",
"TagsTitle": "ラベル",
"Text": "プロンプトの内容には、<<変数名>>などのウィキテキストでサポートされている構文を含めることができます。",
"TextTitle": "テキスト",
"Title": "プロンプト"
},
"PromptDynamicModification": {
"DynamicModificationTypes": {
}
},
"PromptPart": {
},
"ProviderModel": {
"Description": "プロバイダーとモデル設定",
"EmbeddingModel": "意味検索とベクトル操作のための埋め込みモデルの名称",
"EmbeddingModelTitle": "埋め込みモデル",
"ImageGenerationModel": "テキストから画像を生成する操作に使用される画像生成モデルの名称",
"ImageGenerationModelTitle": "画像生成モデル",
"Model": "AIモデル名",
"ModelTitle": "モデル",
"Provider": "AIプロバイダー名",
"ProviderTitle": "プロバイダー",
"SpeechModel": "音声生成モデルの名称(テキスト読み上げ操作用)",
"SpeechModelTitle": "音声モデル",
"Title": "プロバイダーモデル",
"TranscriptionsModel": "音声をテキストに変換する操作に使用される音声認識モデルの名称",
"TranscriptionsModelTitle": "音声認識モデル"
},
"RAG": {
"Removal": {
},
"SourceTypes": {
}
},
"Response": {
"Description": "外部APIのレスポンスは、通常、動的に変更される対象として応答され、その構造はプロンプトと同じです。プリセット内容を記入することもできますし、プレースホルダーやコンテナとして機能させ、ResponseDynamicModificationによって外部APIのレスポンスの具体的な内容が入力されることもあります。",
"Title": "応答"
},
"ResponseDynamicModification": {
"DynamicModificationTypes": {
},
"ResponseProcessingTypes": {
}
},
"ToolCalling": {
},
"Trigger": {
"Model": {
}
},
"Wiki": {
},
"WikiOperation": {
"Description": "Wiki ワークスペースで Tiddler 操作を実行する(追加、削除、またはテキストの設定)",
"Title": "Wiki 操作",
"Tool": {
"Examples": {
},
"Parameters": {
"extraMeta": {
"Description": "追加メタデータのJSON文字列タグやフィールド、デフォルトは「{}」",
"Title": "追加メタデータ"
},
"operation": {
"Description": "実行する操作のタイプ",
"Title": "操作タイプ"
},
"options": {
"Description": "操作オプションのJSON文字列、デフォルトは\"{}\"",
"Title": "操作オプション"
},
"text": {
"Description": "Tiddler のテキスト内容",
"Title": "Tiddler コンテンツ"
},
"title": {
"Description": "Tiddler のタイトル",
"Title": "Tiddler タイトル"
},
"workspaceName": {
"Description": "操作するワークスペースの名前またはID",
"Title": "ワークスペース名"
}
}
},
"ToolListPosition": {
"Position": "ターゲット要素に対する挿入位置before/after",
"PositionTitle": "挿入位置",
"TargetId": "ツールリストを挿入する対象要素のID",
"TargetIdTitle": "ターゲットID"
},
"ToolResultDuration": "ツールの実行結果が会話内で表示されるターン数。この数を超えると、結果はグレー表示になります。",
"ToolResultDurationTitle": "ツール結果の持続ターン数"
},
"WikiSearch": {
"Description": "フィルター式を使用してTiddlyWikiワークスペースの内容を検索する",
"SourceType": "データソースタイプ",
"SourceTypeTitle": "ソースタイプ",
"Title": "Wiki 検索",
"Tool": {
"Parameters": {
"filter": {
"Description": "TiddlyWiki フィルター式",
"Title": "フィルター"
},
"limit": {
"Description": "返される最大結果数",
"Title": "制限"
},
"query": {
"Description": "ベクトル検索時に使用するクエリテキスト(自然言語)",
"Title": "照会"
},
"searchType": {
"Description": "ルールベースまたは類似度ベースの検索モードを選択する",
"Title": "検索タイプ"
},
"threshold": {
"Description": "類似度閾値0-1、この閾値を下回るベクトル結果はフィルタリングされます",
"Title": "閾値"
},
"workspaceName": {
"Description": "検索するワークスペース名またはID",
"Title": "ワークスペース名"
}
}
},
"ToolListPosition": {
"Position": "目標位置に対する挿入位置",
"PositionTitle": "挿入位置",
"TargetId": "ターゲット要素のID、ツールリストはこの要素に対して挿入されます",
"TargetIdTitle": "ターゲットID"
},
"ToolListPositionTitle": "ツールリストの位置",
"ToolResultDuration": "ツールの実行結果が会話中に表示されるターン数。このターン数を超えると、結果はグレー表示になります。",
"ToolResultDurationTitle": "ツール結果の継続ラウンド数"
}
},
"Search": {
"AvailableAgents": "利用可能なインテリジェントエージェント",
"FailedToCreateChatWithAgent": "スマートエージェントとの対話を作成できません",
"FailedToFetchAgents": "インテリジェントエージェントリストの取得に失敗しました",
"NoAgentsFound": "エージェントが見つかりません",
"NoClosedTabsFound": "最近閉じたタブはありません",
"NoTabsFound": "タブが見つかりません",
"OpenTabs": "開いているタブ",
"RecentlyClosedTabs": "最近閉じたタブ"
},
"SplitView": {
"NoTabs": "分割ビューにタグがありません"
},
"Tab": {
"Title": {
"CreateNewAgent": "新しいインテリジェントエージェントを作成",
"EditAgentDefinition": "編集エージェント",
"NewTab": "新しいタブ",
"NewWeb": "新しいウェブページを作成",
"SplitView": ""
}
},
"Tool": {
"Schema": {
"Description": "説明",
"Examples": "使用例",
"Optional": "オプション",
"Parameters": "パラメータ",
"Required": "必須"
},
"WikiOperation": {
"Error": {
"WorkspaceNotExist": "ワークスペース{{workspaceID}}は存在しません",
"WorkspaceNotFound": "ワークスペース名またはID「{{workspaceName}}」は存在しません。利用可能なワークスペース:{{availableWorkspaces}}"
},
"Success": {
"Added": "Wikiワークスペース「{{workspaceName}}」にTiddler「{{title}}」を追加しました",
"Deleted": "Wikiワークスペース「{{workspaceName}}」からTiddler「{{title}}」の削除に成功しました",
"Updated": "Wikiワークスペース「{{workspaceName}}」でTiddler「{{title}}」のテキストを正常に設定しました"
}
},
"WikiSearch": {
"Error": {
"ExecutionFailed": "ツールの実行に失敗しました:{{error}}",
"WorkspaceNotExist": "ワークスペース{{workspaceID}}は存在しません",
"WorkspaceNotFound": "ワークスペース名またはID「{{workspaceName}}」は存在しません。利用可能なワークスペース:{{availableWorkspaces}}"
},
"Success": {
"Completed": "Wiki検索が完了しました。{{totalResults}}件の結果が見つかり、{{shownResults}}件を表示しています:",
"NoResults": "ワークスペース「{{workspaceName}}」でフィルター「{{filter}}」の結果が見つかりませんでした",
"NoVectorResults": "Wikiワークスペース「{{workspaceName}}」で条件に合致するベクトル検索結果が見つかりませんでした(類似度閾値:{{threshold}})。",
"VectorCompleted": "ベクトル検索に基づいて、ワークスペース「{{workspaceName}}」で以下の関連コンテンツが見つかりました:"
},
"UpdateEmbeddings": {
"Error": {
"ExecutionFailed": "埋め込みの生成に失敗しました:{{error}}",
"NoAIConfig": "まずAIプロバイダーと埋め込みモデルを設定してください設定内で。",
"WorkspaceNotExist": "ワークスペース{{workspaceID}}は存在しません",
"WorkspaceNotFound": "ワークスペース名またはID「{{workspaceName}}」は存在しません。利用可能なワークスペース:{{availableWorkspaces}}"
},
"Success": {
"Generated": "ワークスペース {{workspaceName}} のベクトル埋め込みインデックスの生成が成功しました。合計{{totalNotes}}個のノート、{{totalEmbeddings}}個の埋め込みです。"
}
}
}
},
"Unknown": "未知"
}

View file

@ -1,318 +1,471 @@
{
"Hello": "こんにちは",
"WorkspaceSelector": {
"Add": "追加",
"Guide": "ガイド",
"Help": "ヘルプ",
"OpenWorkspaceTagTiddler": "{{tagName}} を開く",
"DefaultTiddlers": "デフォルトのTiddlers",
"OpenWorkspaceMenuName": "ワークスペースを開く",
"EditWorkspace": "ワークスペースを編集",
"RemoveWorkspace": "ワークスペースを削除",
"AreYouSure": "このワークスペースを削除してもよろしいですか?\nワークスペースを削除すると、このアプリケーション内のワークスペースが削除されますが、ハードドライブからフォルダは削除されません。\nただし、Wikiフォルダも削除することを選択した場合、すべての内容が削除されます。",
"RemoveWorkspaceAndDelete": "ワークスペースを削除し、ディスクからWikiフォルダを削除する",
"BadWorkspacePath": "ワークスペースの設定に問題があります",
"EditCurrentWorkspace": "現在のワークスペースを編集",
"RemoveCurrentWorkspace": "現在のワークスペースを削除",
"HibernateWorkspace": "ワークスペースを休止状態にする",
"WakeUpWorkspace": "ワークスペースを再開する",
"OpenWorkspaceFolder": "フォルダを開く",
"ReloadCurrentWorkspace": "現在のワークスペースをリロード",
"OpenWorkspaceFolderInEditor": "外部エディタでフォルダを開く",
"OpenWorkspaceFolderInGitGUI": "Git GUIで開く",
"OpenInBrowser": "ブラウザで開く",
"OpenInBrowserDisabledHint": "HTTP APIを有効にする必要があります"
},
"Preference": {
"Reset": "本気ですか?すべての設定が元のデフォルトに復元されます。閲覧データは影響を受けません。このアクションは元に戻せません。",
"ResetNow": "今すぐリセット",
"ClearBrowsingDataMessage": "本気ですか?すべての閲覧データが消去されます。このアクションは元に戻せません。",
"Notifications": "通知",
"AlwaysOnTop": "常に最前面に表示",
"RequireRestart": "再起動が必要",
"HideSideBar": "サイドバーを隠す",
"HideTitleBar": "タイトルバーを隠す",
"ToggleMenuBar": "メニューバーを切り替える"
},
"AddWorkspace": {
"MainPageTipWithoutSidebar": "<0>メニューの</0><strong>ワークスペース > ワークスペースを追加</strong><0>をクリックするか、</0><strong>ここをクリック</strong><2>してTiddlyWikiを使い始めましょう</2>",
"MainPageTipWithSidebar": "<0>サイドバーの</0><1>+</1><2>ボタンをクリックしてTiddlyWikiを使い始めましょう</2>",
"NotFilled": "未入力",
"GitRepoUrl": "GitリポジトリのオンラインURL",
"AddFileSystemPath": "サブWikiのFileSystemPathsを追加する",
"AddWorkspace": "ワークスペースを追加",
"Advanced": "高度な設定",
"AndLinkToMainWorkspace": "メインWikiにリンク",
"CreateWiki": "Wikiを作成: ",
"CloneWiki": "オンラインWikiをインポート: ",
"ImportWiki": "Wikiをインポート: ",
"LoginGithubAccount": "Githubアカウントにログイン",
"LogoutGithubAccount": "Githubアカウントからログアウト",
"MainWorkspaceDescription": "TiddlyWikiの設定ファイルとブログとして公開されたときの公開コンテンツを含みます。",
"NotLoggedIn": "ログインしていません",
"SubWorkspaceDescription": "メインリポジトリに付随する必要があり、プライベートコンテンツを保存するために使用できます。注意点は2つありますサブナレッジベースはメインナレッジベースフォルダ内に配置できませんサブナレッジベースは一般的にプライベートGithubリポジトリにデータを同期するために使用され、私だけが読み書きできます。そのため、リポジトリアドレスはメインナレッジベースと同じにすることはできません。\nサブナレッジベースはメインナレッジベースへのソフトリンクショートカットを作成することで有効になります。リンクが作成されると、メインナレッジベース内でサブナレッジベースの内容を見ることができます。",
"CloneOnlineWiki": "オンラインWikiをインポート",
"CreateNewWiki": "新しいWikiを作成",
"ExistedWikiLocation": "既存のWikiの場所",
"OpenLocalWiki": "ローカルWikiを開く",
"SwitchCreateNewOrOpenExisted": "新しいWikiを作成するか、既存のWikiを開くかを切り替える",
"MainWorkspace": "メインワークスペース",
"SubWorkspace": "サブワークスペース",
"WorkspaceFolder": "ワークスペースフォルダの場所",
"WorkspaceParentFolder": "ワークスペースフォルダの親フォルダ",
"Choose": "選択",
"MainWorkspaceLocation": "メインワークスペースのパス",
"SubWorkspaceWillLinkTo": "サブワークスペースは次にリンクされます",
"BadWikiHtml": "このHTMLファイルからWikiを作成できませんでした",
"CanNotLoadList": "リポジトリリストを読み込めません。ネットワーク接続が良くありません。",
"CreatePrivateRepository": "プライベートリポジトリを作成",
"CreatePublicRepository": "パブリックリポジトリを作成",
"OmitMoreResult": "リストには最初の{{loadCount}}件のみが表示されます",
"Reload": "リロード",
"MainPageReloadTip": "<0><0>試してください:<1><0>下の<2>リロード</2>ボタンをクリックするか、<5>CMDまたはCtrl + R</5>を押してページをリロードします。</0><1><2>ログフォルダ</2>を開いて何が起こったかを確認します。</1><2>最悪の場合でも、コンピュータ上のフォルダをバックアップするためにコピーし、ワークスペースアイコンを右クリックしてワークスペースを削除し、コンピュータ上のフォルダを再インポートすることができますまたは、以前にバックアップしたHTMLバージョンのWikiをHTMLにドラッグしてインポートします。</2></1></0></0>",
"Processing": "処理中...",
"SearchGithubRepoName": "Githubリポジトリ名を検索",
"WaitForLogin": "ログインを待っています",
"WikiServerPort": "Wikiサーバーポート番号競合が発生した場合に変更、通常はデフォルトのままでOK",
"WorkspaceFolderNameToCreate": "新しいワークスペースフォルダの名前",
"CantCreateFolderHere": "ここにフォルダを作成できません \"{{newWikiPath}}\"",
"Choose": "選択",
"CloneOnlineWiki": "オンラインWikiをインポート",
"CloneWiki": "オンラインWikiをインポート: ",
"CreateLinkFromSubWikiToMainWikiFailed": "フォルダ \"{{subWikiPath}}\" を \"{{mainWikiTiddlersFolderPath}}\" にリンクできません",
"CreateLinkFromSubWikiToMainWikiSucceed": "メインWikiにサブWikiのショートカットが正常に作成されました。メインWikiにファイルを保存するショートカットは、自動的にサブWikiにファイルを保存します。",
"CreateNewWiki": "新しいWikiを作成",
"CreatePrivateRepository": "プライベートリポジトリを作成",
"CreatePublicRepository": "パブリックリポジトリを作成",
"CreateWiki": "Wikiを作成: ",
"ExistedWikiLocation": "既存のWikiの場所",
"ExtractedWikiFolderName": "変換されたWIKIフォルダ名",
"GitDefaultBranchDescription": "Gitのデフォルトブランチ。Githubはそのイベント後にmasterからmainに変更しました",
"GitEmailDescription": "Gitコミットに使用されるメールアドレスで、GithubなどのオンラインGitサービスでの毎日のアクティビティをカウントするために使用されます",
"GitRepoUrl": "GitリポジトリのオンラインURL",
"GitTokenDescription": "Gitにログインするために使用される資格情報。一定期間後に期限切れになります",
"GitUserNameDescription": "Gitにログインするために使用されるアカウント名。ニックネームではありません",
"ImportWiki": "Wikiをインポート: ",
"LocalWikiHtml": "htmlファイルへのパス",
"LocalWorkspace": "ローカルワークスペース",
"LocalWorkspaceDescription": "ローカルでのみ使用し、データを完全に管理します。TidGiはローカルGitバックアップシステムを作成し、以前のバージョンに戻ることができますが、ローカルフォルダが削除されるとすべての内容が失われます。",
"LogoutToGetStorageServiceToken": "最新の資格情報を取得するためにオンラインストレージサービスにログイン",
"MainPageReloadTip": "<0><0>試してください:<1><0>下の<2>リロード</2>ボタンをクリックするか、<5>CMDまたはCtrl + R</5>を押してページをリロードします。</0><1><2>ログフォルダ</2>を開いて何が起こったかを確認します。</1><2>最悪の場合でも、コンピュータ上のフォルダをバックアップするためにコピーし、ワークスペースアイコンを右クリックしてワークスペースを削除し、コンピュータ上のフォルダを再インポートすることができますまたは、以前にバックアップしたHTMLバージョンのWikiをHTMLにドラッグしてインポートします。</2></1></0></0>",
"MainPageTipWithSidebar": "<0>サイドバーの</0><1>+</1><2>ボタンをクリックしてTiddlyWikiを使い始めましょう</2>",
"MainPageTipWithoutSidebar": "<0>メニューの</0><strong>ワークスペース > ワークスペースを追加</strong><0>をクリックするか、</0><strong>ここをクリック</strong><2>してTiddlyWikiを使い始めましょう</2>",
"MainWorkspace": "メインワークスペース",
"MainWorkspaceDescription": "TiddlyWikiの設定ファイルとブログとして公開されたときの公開コンテンツを含みます。",
"MainWorkspaceLocation": "メインワークスペースのパス",
"NotFilled": "未入力",
"NotLoggedIn": "ログインしていません",
"OmitMoreResult": "リストには最初の{{loadCount}}件のみが表示されます",
"OpenLocalWiki": "ローカルWikiを開く",
"OpenLocalWikiFromHTML": "wiki.htmlをインポート",
"PathNotExist": "パスが存在しません \"{{path}}\"",
"Processing": "処理中...",
"Reload": "リロード",
"SearchGithubRepoName": "Githubリポジトリ名を検索",
"StartCloningSubWiki": "サブWikiのクローンを開始",
"StartCloningWiki": "Wikiのクローンを開始",
"StartCreatingSubWiki": "サブWikiの作成を開始",
"StartLinkingSubWikiToMainWiki": "サブWikiをメインWikiにリンクし始める",
"StartUsingTemplateToCreateWiki": "テンプレートを使用してWikiの作成を開始",
"SubWikiCreationCompleted": "サブWikiが作成されました",
"ThisPathIsNotAWikiFolder": "このディレクトリはWikiフォルダではありません \"{{wikiPath}}\"",
"WikiExisted": "この場所にWikiが既に存在します \"{{newWikiPath}}\"",
"WikiTemplateCopyCompleted": "テンプレートWikiをコピーしました",
"WikiTemplateMissing": "Wikiテンプレートがありません \"{{TIDDLYWIKI_TEMPLATE_FOLDER_PATH}}\"",
"StartUpdatingWorkspace": "ワークスペースを更新中",
"WorkspaceUpdated": "ワークスペースが更新され、Wikiが起動しています",
"StartLinkingSubWikiToMainWiki": "サブWikiをメインWikiにリンクし始める",
"AddFileSystemPath": "サブWikiのFileSystemPathsを追加する",
"TagName": "タグ名",
"TagNameHelp": "このタグを持つTiddlerはこのサブWikiに追加されます後で右クリックしてワークスペースアイコンを選択し、ワークスペースを編集することでこのタグを追加または変更できます",
"GitToken": "Gitトークン",
"GitTokenDescription": "Gitにログインするために使用される資格情報。一定期間後に期限切れになります",
"NoGitInfoAlert": "オンラインGitリポジトリアドレスを選択していないか、Githubアカウントに正常にログインしていません。作成ボタンをクリックすると、Githubに自動的に同期されないローカルWikiが作成されます。ご注意ください。",
"LocalWorkspace": "ローカルワークスペース",
"LocalWorkspaceDescription": "ローカルでのみ使用し、データを完全に管理します。TidGiはローカルGitバックアップシステムを作成し、以前のバージョンに戻ることができますが、ローカルフォルダが削除されるとすべての内容が失われます。",
"SubWorkspace": "サブワークスペース",
"SubWorkspaceDescription": "メインリポジトリに付随する必要があり、プライベートコンテンツを保存するために使用できます。注意点は2つありますサブナレッジベースはメインナレッジベースフォルダ内に配置できませんサブナレッジベースは一般的にプライベートGithubリポジトリにデータを同期するために使用され、私だけが読み書きできます。そのため、リポジトリアドレスはメインナレッジベースと同じにすることはできません。\nサブナレッジベースはメインナレッジベースへのソフトリンクショートカットを作成することで有効になります。リンクが作成されると、メインナレッジベース内でサブナレッジベースの内容を見ることができます。",
"SubWorkspaceWillLinkTo": "サブワークスペースは次にリンクされます",
"SwitchCreateNewOrOpenExisted": "新しいWikiを作成するか、既存のWikiを開くかを切り替える",
"SyncedWorkspace": "同期されたワークスペース",
"SyncedWorkspaceDescription": "オンラインストレージサービスGithubなどに同期するには、ストレージサービスにログインするか、ログイン資格情報を入力し、良好なネットワーク接続が必要です。デバイス間でデータを同期でき、信頼できるストレージサービスを使用している場合でもデータはあなたのものです。フォルダが誤って削除された場合でも、オンラインサービスからデータを再度ローカルにダウンロードできます。",
"GitEmailDescription": "Gitコミットに使用されるメールアドレスで、GithubなどのオンラインGitサービスでの毎日のアクティビティをカウントするために使用されます",
"GitUserNameDescription": "Gitにログインするために使用されるアカウント名。ニックネームではありません",
"LogoutToGetStorageServiceToken": "最新の資格情報を取得するためにオンラインストレージサービスにログイン",
"AddWorkspace": "ワークスペースを追加",
"WorkspaceUserName": "ワークスペースユーザー名",
"WorkspaceUserNameDetail": "Wikiで使用されるエディタ名は、Tiddlerが作成または編集されるときにcreatorフィールドに入力されます。ワークスペースで設定されたエディタ名は、設定で割り当てられたグローバルデフォルトのエディタ名を上書きします。これにより、異なるユーザー名で構成された複数のワークスペースを使用して、同じWikiで異なるアイデンティティでTiddlerを作成できます。",
"TagName": "タグ名",
"TagNameHelp": "このタグを持つTiddlerはこのサブWikiに追加されます後で右クリックしてワークスペースアイコンを選択し、ワークスペースを編集することでこのタグを追加または変更できます",
"ThisPathIsNotAWikiFolder": "このディレクトリはWikiフォルダではありません \"{{wikiPath}}\"",
"WaitForLogin": "ログインを待っています",
"WikiExisted": "この場所にWikiが既に存在します \"{{newWikiPath}}\"",
"WikiNotStarted": "Wikiが開始されていないか、読み込まれていません",
"Advanced": "高度な設定",
"GitDefaultBranch": "Gitデフォルトブランチ",
"GitDefaultBranchDescription": "Gitのデフォルトブランチ。Githubはそのイベント後にmasterからmainに変更しました",
"LocalWikiHtml": "htmlファイルへのパス",
"OpenLocalWikiFromHTML": "wiki.htmlをインポート",
"ExtractedWikiFolderName": "変換されたWIKIフォルダ名",
"BadWikiHtml": "このHTMLファイルからWikiを作成できませんでした"
"WikiTemplateCopyCompleted": "テンプレートWikiをコピーしました",
"WikiTemplateMissing": "Wikiテンプレートがありません \"{{TIDDLYWIKI_TEMPLATE_FOLDER_PATH}}\"",
"WorkspaceFolder": "ワークスペースフォルダの場所",
"WorkspaceFolderNameToCreate": "新しいワークスペースフォルダの名前",
"WorkspaceParentFolder": "ワークスペースフォルダの親フォルダ",
"WorkspaceUserName": "ワークスペースユーザー名",
"WorkspaceUserNameDetail": "Wikiで使用されるエディタ名は、Tiddlerが作成または編集されるときにcreatorフィールドに入力されます。ワークスペースで設定されたエディタ名は、設定で割り当てられたグローバルデフォルトのエディタ名を上書きします。これにより、異なるユーザー名で構成された複数のワークスペースを使用して、同じWikiで異なるアイデンティティでTiddlerを作成できます。"
},
"Cancel": "キャンセル",
"ClickForDetails": "詳細をクリック",
"ContextMenu": {
"About": "情報",
"AddToDictionary": "辞書に追加",
"Back": "戻る←",
"BackupNow": "ローカルGitバックアップ",
"Copy": "コピー",
"CopyEmailAddress": "メールアドレスをコピー",
"CopyImage": "画像をコピー",
"CopyImageURL": "画像URLをコピー",
"CopyLink": "リンクをコピー",
"Cut": "切り取り",
"DeveloperTools": "開発者ツール",
"Forward": "進む→",
"InspectElement": "要素を検査",
"LookUp": "\"{{word}}\" を調べる",
"More": "もっと見る",
"NoNetworkConnection": "ネットワーク接続がありません",
"Notifications": "通知...",
"OpenCommandPalette": "コマンドパレットを開く",
"OpenLinkInBrowser": "ブラウザでリンクを開く",
"OpenTidGi": "TidGiを開く",
"OpenTidGiMenuBar": "TidGiメニューバーを開く",
"OpenWorkspaceInNewWindow": "ワークスペースを新しいウィンドウで開く",
"Paste": "貼り付け",
"Preferences": "設定...",
"Quit": "終了",
"Reload": "リロード",
"RestartService": "サービスを再起動",
"RestartServiceComplete": "サービスの再起動が完了しました",
"SearchWithGoogle": "Googleで検索",
"SyncNow": "クラウドに同期",
"TidGiSupport": "TidGiサポート",
"TidGiWebsite": "TidGi公式サイト"
},
"Delete": "削除",
"Dialog": {
"CantFindWorkspaceFolderRemoveWorkspace": "以前そこにあったワークスペースフォルダが見つかりません!\nここに存在するはずのフォルダが移動されたか、このフォルダにWikiがありませんワークスペースを削除しますか",
"DoNotCare": "気にしない",
"FocusedTiddlerNotFoundTitle": "現在フォーカス中のチドラーを検索できません",
"FocusedTiddlerNotFoundTitleDetail": "CPL にて FocusedTiddler プラグインをインストールすることができます。",
"Later": "後で",
"MadeWithLove": "<0>❤</0><1>で作られました</1>",
"NeedCorrectTiddlywikiFolderPath": "正しいパスを入力する必要があります。このパスはTiddlyWikiで認識できません。",
"PathPassInCantUse": "入力されたパスは使用できません",
"RemoveWorkspace": "ワークスペースを削除",
"WorkspaceFolderRemoved": "ワークスペースフォルダが移動されたか、このフォルダはWikiフォルダではありません",
"StorageServiceUserInfoNoFound": "ストレージサービスのユーザー情報が見つかりません",
"StorageServiceUserInfoNoFoundDetail": "ストレージサービスにログインしていないようです。このWikiの同期は無効になります。",
"RestartMessage": "この変更を有効にするには、アプリを再起動する必要があります。",
"Later": "後で",
"RestartAppNow": "今すぐアプリを再起動",
"RestartWikiNow": "今すぐWikiを再起動",
"Restarting": "再起動中",
"MadeWithLove": "<0>❤</0><1>で作られました</1>",
"ReportBug": "バグを報告",
"ReportBugDetail": "チュートリアルを読み、エラーメッセージを注意深く読み、入力を確認した場合は、ボタンをクリックしてください。",
"FocusedTiddlerNotFoundTitle": "現在フォーカス中のチドラーを検索できません",
"FocusedTiddlerNotFoundTitleDetail": "CPL にて FocusedTiddler プラグインをインストールすることができます。"
},
"Loading": "読み込み中",
"SideBar": {
"CommandPalette": "コマンドパレット",
"UpdateAvailable": "更新があります!",
"Preferences": "設定..."
},
"ContextMenu": {
"OpenTidGi": "TidGiを開く",
"OpenTidGiMenuBar": "TidGiメニューバーを開く",
"OpenLinkInNewWindow": "リンクを新しいウィンドウで開く",
"OpenWorkspaceInNewWindow": "ワークスペースを新しいウィンドウで開く",
"Preferences": "設定...",
"TidGiSupport": "TidGiサポート",
"TidGiWebsite": "TidGi公式サイト",
"Quit": "終了",
"Notifications": "通知...",
"More": "もっと見る",
"About": "情報",
"Reload": "リロード",
"Forward": "進む→",
"Back": "戻る←",
"DeveloperTools": "開発者ツール",
"InspectElement": "要素を検査",
"LookUp": "\"{{word}}\" を調べる",
"CopyEmailAddress": "メールアドレスをコピー",
"CopyLink": "リンクをコピー",
"OpenLinkInBrowser": "ブラウザでリンクを開く",
"CopyImageURL": "画像URLをコピー",
"CopyImage": "画像をコピー",
"AddToDictionary": "辞書に追加",
"SearchWithGoogle": "Googleで検索",
"Cut": "切り取り",
"Copy": "コピー",
"Paste": "貼り付け",
"RestartService": "サービスを再起動",
"RestartServiceComplete": "サービスの再起動が完了しました",
"SyncNow": "クラウドに同期",
"NoNetworkConnection": "ネットワーク接続がありません",
"OpenCommandPalette": "コマンドパレットを開く",
"BackupNow": "ローカルGitバックアップ"
},
"Updater": {
"CheckingFailed": "更新の確認に失敗しました(ネットワークエラー)",
"CheckUpdate": "更新を確認",
"CheckingForUpdate": "更新を確認中...",
"DownloadProgress": "ダウンロード進行中",
"UpdateError": "更新エラー",
"UpdateAvailable": "新しいバージョンがあります!",
"UpdateCancelled": "更新がキャンセルされました",
"UpdateDownloaded": "更新がダウンロードされました",
"UpdateNotAvailable": "最新バージョンです"
},
"Menu": {
"TidGi": "TidGi",
"TidGiMenuBar": "TidGiメニューバー",
"Edit": "編集",
"View": "表示",
"Find": "検索",
"FindMatches": "一致",
"Close": "閉じる",
"FindNext": "次を検索",
"FindPrevious": "前を検索",
"Home": "ホーム",
"Back": "戻る",
"Forward": "進む",
"SelectPreviousWorkspace": "前のワークスペースを選択",
"SelectNextWorkspace": "次のワークスペースを選択",
"Language": "言語",
"History": "履歴",
"Workspaces": "ワークスペース",
"CurrentWorkspace": "現在のワークスペース",
"Window": "ウィンドウ",
"Help": "ヘルプ",
"ActualSize": "実際のサイズ",
"ZoomIn": "ズームイン",
"ZoomOut": "ズームアウト",
"ReportBugViaGithub": "GitHubでバグを報告...",
"RequestFeatureViaGithub": "GitHubで機能をリクエスト...",
"DeveloperToolsActiveWorkspace": "アクティブなワークスペースの開発者ツールを開く",
"LearnMore": "もっと詳しく...",
"PrintPage": "ページを印刷",
"ExportActiveTiddler": "アクティブなTiddlerをエクスポート",
"Wiki": "Wiki",
"ExportWholeWikiHTML": "Wiki全体をHTMLとしてエクスポート"
"RestartAppNow": "今すぐアプリを再起動",
"RestartMessage": "この変更を有効にするには、アプリを再起動する必要があります。",
"RestartWikiNow": "今すぐWikiを再起動",
"Restarting": "再起動中",
"StorageServiceUserInfoNoFound": "ストレージサービスのユーザー情報が見つかりません",
"StorageServiceUserInfoNoFoundDetail": "ストレージサービスにログインしていないようです。このWikiの同期は無効になります。",
"WorkspaceFolderRemoved": "ワークスペースフォルダが移動されたか、このフォルダはWikiフォルダではありません"
},
"EditWorkspace": {
"Path": "Wikiパス",
"Save": "保存",
"Cancel": "キャンセル",
"DisableAudioTitle": "オーディオを無効にする",
"DisableNotificationTitle": "通知を無効にする",
"DisableAudio": "ワークスペースがオーディオを再生するのを防ぎます。",
"DisableNotification": "ワークスペースが通知を送信するのを防ぎます。",
"HibernateTitle": "使用されていないときに休止状態にする",
"HibernateDescription": "CPU使用率、メモリ、バッテリーを節約します。これにより自動同期が無効になり、データをバックアップするために手動でコミットおよび同期する必要があります。",
"SelectLocal": "ローカル画像を選択...",
"ResetDefaultIcon": "デフォルトアイコンにリセット",
"NoRevert": "注意!この操作は元に戻せません。",
"LastVisitState": "最後に訪れたページ",
"URL": "Wiki URL",
"Port": "ローカルホストサーバーポート",
"PathDescription": "ローカルWikiフォルダの場所。",
"SyncOnInterval": "間隔で同期",
"SyncOnIntervalDescription": "オンにすると、グローバル設定の時間間隔に従って自動的に同期され、起動時にも自動的に同期され、ボタンをクリックして手動で同期することもできます。同期前にデータをローカルGitに自動バックアップします。オフにすると、アプリケーションが開かれたときに1回の自動同期があり、ユーザーがWiki内の同期ボタンをクリックして手動でトリガーしたときに1回の手動同期があります。",
"SyncOnStartup": "アプリ起動時に同期",
"SyncOnStartupDescription": "アプリがコールドスタートするときに1回コミットして同期します。",
"Name": "ワークスペース名",
"NameDescription": "ワークスペースの名前。サイドバーに表示され、ワークスペース内のGitリポジトリの実際のフォルダ名と異なる場合があります",
"AddExcludedPlugins": "無視するプラグイン名を入力",
"AddExcludedPluginsDescription": "現在のWikiにインストールされているプラグインを検索するか、任意のプラグイン名を入力できます。",
"AppearanceOptions": "外観オプション",
"BackupOnInterval": "間隔でバックアップ",
"BackupOnIntervalDescription": "有効にすると、定期的にローカルGitでデータが自動的にバックアップされますグローバル設定の間隔。これにより、クラウドGit同期アドレスが構成されていなくても、ローカルに自動的にバックアップされます。",
"WikiRootTiddler": "WikiルートTiddler",
"WikiRootTiddlerDescription": "WikiのルートTiddlerはシステムのコア動作を決定します。変更する前に公式ドキュメントを読んで理解してください",
"WikiRootTiddlerItems": {
"all": "一度にすべて読み込む",
"lazy-images": "必要に応じて画像を読み込む",
"lazy-all": "必要に応じて画像とテキストを読み込む"
},
"ReadOnlyMode": "読み取り専用モード",
"ReadOnlyModeDescription": "イントラネットペネトレーションと組み合わせて使用でき、TidGiをサーバープログラムとしてブログをデプロイできます。開くと、Wikiはディスク上のファイルを直接変更することでのみ変更できますgit同期を含む。Webページ上で内容を変更することはできませんが、誰でもアクセスできます。",
"TokenAuth": "トークン認証",
"TokenAuthDescription": "有効にすると、HTTPリクエストに資格情報を含める必要があり、同じLAN内の他の人がートにアクセスするのを防ぎ、サーバーのセキュリティを向上させます。読み取り専用モードと同時に有効にすることはできません。",
"TokenAuthAutoFillUserNameDescription": "この機能を有効にするには、グローバル設定またはワークスペース設定にユーザー名を入力する必要があります。空の場合、デフォルトのユーザー名がワークスペース設定に自動的に入力されます。後で変更できます。",
"TokenAuthCurrentHeader": "現在のリクエストヘッダーのトークン認証",
"ServerOptions": "ブログとサーバーのオプション",
"Cancel": "キャンセル",
"ClickToExpand": "クリックして展開",
"DisableAudio": "ワークスペースがオーディオを再生するのを防ぎます。",
"DisableAudioTitle": "オーディオを無効にする",
"DisableNotification": "ワークスペースが通知を送信するのを防ぎます。",
"DisableNotificationTitle": "通知を無効にする",
"EnableHTTPAPI": "HTTP APIを有効にする",
"EnableHTTPAPIDescription": "TidGi-Mobile、Tiddlywiki-Collector webclipperなどのサードパーティプログラムがHTTPネットワークインターフェースを介してートを読み書きできるようにします。",
"EnableHTTPS": "HTTPSを有効にする",
"EnableHTTPSDescription": "TLS暗号化アクセスを提供するには、独自のHTTPS証明書が必要です。ドメイン名プロバイダーからダウンロードするか、無料のHTTPS証明書の申請方法を検索できます。",
"HTTPSUploadCert": "Certファイルを追加",
"HTTPSPickCert": "Certファイルのパスを選択",
"ExcludedPlugins": "無視するプラグイン",
"ExcludedPluginsDescription": "読み取り専用モードでWikiをブログとして起動する場合、初回読み込みのWebページのサイズを小さくするために、$:/plugins/tiddlywiki/codemirrorなどの編集関連のプラグインを読み込まないようにすることができます。ブログにはこれらの編集機能は必要ありません。",
"Generate": "生成",
"HTTPSCertPath": "Certファイルのパス",
"HTTPSCertPathDescription": "拡張子が.crtの証明書ファイルの場所。通常、xxx_public.crtで終わります。",
"HTTPSKeyPathDescription": "拡張子が.keyの秘密鍵ファイルの場所。",
"HTTPSUploadKey": "Keyファイルを追加",
"HTTPSKeyPath": "Keyファイルのパス",
"HTTPSKeyPathDescription": "拡張子が.keyの秘密鍵ファイルの場所。",
"HTTPSPickCert": "Certファイルのパスを選択",
"HTTPSPickKey": "Keyファイルのパスを選択",
"UploadOrSelectPathDescription": "アップロードボタンをクリックしてファイルをTidGiに送信するか、パスを選択ボタンをクリックして保存場所からファイルを選択します。",
"ExcludedPlugins": "無視するプラグイン",
"AddExcludedPlugins": "無視するプラグイン名を入力",
"AddExcludedPluginsDescription": "現在のWikiにインストールされているプラグインを検索するか、任意のプラグイン名を入力できます。",
"ExcludedPluginsDescription": "読み取り専用モードでWikiをブログとして起動する場合、初回読み込みのWebページのサイズを小さくするために、$:/plugins/tiddlywiki/codemirrorなどの編集関連のプラグインを読み込まないようにすることができます。ブログにはこれらの編集機能は必要ありません。",
"LastNodeJSArgv": "最新の起動時のコマンドライン引数",
"TokenAuthCurrentToken": "現在のトークン認証トークン",
"TokenAuthCurrentTokenEmptyText": "新しい資格情報を生成するには、生成ボタンをクリックしてください",
"TokenAuthCurrentTokenDescription": "このトークンは機密情報であり、敵対的なエンティティに漏洩した場合は再生成する必要があり、再生成後に接続されたサードパーティアプリケーションの資格情報を更新する必要があります",
"Generate": "生成",
"ClickToExpand": "クリックして展開",
"MainWorkspacePath": "メインワークスペースのパス",
"HTTPSUploadCert": "Certファイルを追加",
"HTTPSUploadKey": "Keyファイルを追加",
"HibernateDescription": "CPU使用率、メモリ、バッテリーを節約します。これにより自動同期が無効になり、データをバックアップするために手動でコミットおよび同期する必要があります。",
"HibernateTitle": "使用されていないときに休止状態にする",
"IsSubWorkspace": "サブワークスペースです",
"AppearanceOptions": "外観オプション",
"LastNodeJSArgv": "最新の起動時のコマンドライン引数",
"LastVisitState": "最後に訪れたページ",
"MainWorkspacePath": "メインワークスペースのパス",
"MiscOptions": "その他のオプション",
"Name": "ワークスペース名",
"NameDescription": "ワークスペースの名前。サイドバーに表示され、ワークスペース内のGitリポジトリの実際のフォルダ名と異なる場合があります",
"NoRevert": "注意!この操作は元に戻せません。",
"Path": "Wikiパス",
"PathDescription": "ローカルWikiフォルダの場所。",
"Port": "ローカルホストサーバーポート",
"ReadOnlyMode": "読み取り専用モード",
"ReadOnlyModeDescription": "イントラネットペネトレーションと組み合わせて使用でき、TidGiをサーバープログラムとしてブログをデプロイできます。開くと、Wikiはディスク上のファイルを直接変更することでのみ変更できますgit同期を含む。Webページ上で内容を変更することはできませんが、誰でもアクセスできます。",
"ResetDefaultIcon": "デフォルトアイコンにリセット",
"Save": "保存",
"SaveAndSyncOptions": "保存と同期",
"MiscOptions": "その他のオプション"
"SelectLocal": "ローカル画像を選択...",
"ServerOptions": "ブログとサーバーのオプション",
"SyncOnInterval": "間隔で同期",
"SyncOnIntervalDescription": "オンにすると、グローバル設定の時間間隔に従って自動的に同期され、起動時にも自動的に同期され、ボタンをクリックして手動で同期することもできます。同期前にデータをローカルGitに自動バックアップします。オフにすると、アプリケーションが開かれたときに1回の自動同期があり、ユーザーがWiki内の同期ボタンをクリックして手動でトリガーしたときに1回の手動同期があります。",
"SyncOnStartup": "アプリ起動時に同期",
"SyncOnStartupDescription": "アプリがコールドスタートするときに1回コミットして同期します。",
"TiddlyWiki": "",
"TokenAuth": "トークン認証",
"TokenAuthAutoFillUserNameDescription": "この機能を有効にするには、グローバル設定またはワークスペース設定にユーザー名を入力する必要があります。空の場合、デフォルトのユーザー名がワークスペース設定に自動的に入力されます。後で変更できます。",
"TokenAuthCurrentHeader": "現在のリクエストヘッダーのトークン認証",
"TokenAuthCurrentToken": "現在のトークン認証トークン",
"TokenAuthCurrentTokenDescription": "このトークンは機密情報であり、敵対的なエンティティに漏洩した場合は再生成する必要があり、再生成後に接続されたサードパーティアプリケーションの資格情報を更新する必要があります",
"TokenAuthCurrentTokenEmptyText": "新しい資格情報を生成するには、生成ボタンをクリックしてください",
"TokenAuthDescription": "有効にすると、HTTPリクエストに資格情報を含める必要があり、同じLAN内の他の人がートにアクセスするのを防ぎ、サーバーのセキュリティを向上させます。読み取り専用モードと同時に有効にすることはできません。",
"URL": "Wiki URL",
"UploadOrSelectPathDescription": "アップロードボタンをクリックしてファイルをTidGiに送信するか、パスを選択ボタンをクリックして保存場所からファイルを選択します。",
"WikiRootTiddler": "WikiルートTiddler",
"WikiRootTiddlerDescription": "WikiのルートTiddlerはシステムのコア動作を決定します。変更する前に公式ドキュメントを読んで理解してください",
"WikiRootTiddlerItems": {
}
},
"Error": {
"ALreadyExistErrorDescription": "現在のパスには既にフォルダが存在するため、新しいナレッジベースをここに作成できません。",
"AlreadyExistError": "この場所はすでにフォルダーによって占有されています。",
"CopyWikiTemplateError": "E-3 ウィキテンプレートの複製エラー",
"CopyWikiTemplateErrorDescription": "E-3 最新のWikiテンプレートを対応する位置にコピーまたは上書きしようとしましたが、失敗しました。表示されたメッセージに従って入力を確認してください。",
"DoubleWikiInstanceError": "E-4 ウィキの重複起動エラー",
"DoubleWikiInstanceErrorDescription": "E-4 同じWikiを2回起動しました。これはプログラムのバグが原因である可能性があります。",
"HTMLCanNotLoadError": "提供されたHTMLファイルのパスは使用できません。",
"HTMLCanNotLoadErrorDescription": "利用可能なHTMLファイルへのパスを入力してください。",
"InitWikiGitError": "E-1 ノートリポジトリの初期化失敗エラー",
"InitWikiGitErrorDescription": "E-1 新しいートリポジトリに使用するテンプレートのコピーに失敗した、またはートリポジトリのgit初期化に失敗しました。これはバグである可能性があります。",
"InitWikiGitRevertError": "E-2 ノートリポジトリの初期化に失敗し、ロールバックも失敗したエラー",
"InitWikiGitRevertErrorDescription": "E-2 はノートの倉庫の初期化に失敗しただけでなく、取り消しも失敗しました。これは深刻な問題であり、この場所に生成された新しいフォルダを手動でクリーンアップする必要があります。",
"InitWikiGitSyncedWikiNoGitUserInfoError": "E-6 ートリポジトリの初期化に失敗しました。Git情報が提供されていないためです。エラー",
"InitWikiGitSyncedWikiNoGitUserInfoErrorDescription": "E-6 初期化同期のクラウドートリポジトリには、クラウド上のgitリポジトリアドレスの選択と、対応するクラウドサービスの認証情報の提供が必要ですが、現在これらの情報を取得できていません。",
"InsertMenuAfterSubMenuIndexError": "E-5 既存の目次にディレクトリテンプレートを挿入した後のエラー",
"InsertMenuAfterSubMenuIndexErrorDescription": "E-5 あなたはディレクトリを挿入しようとし、afterSubMenu \"{{afterSubMenu}}\" をディレクトリ menuID \"{{menuID}}\" 内で指定しましたが、ディレクトリ \"{{menu}}\" 内でそれを見つけることができませんでした。正しい menuID を使用してディレクトリを指定してください。",
"MainWindowMissing": "E-7 プログラムはメインウィンドウの情報を取得できず、正常に動作しません。",
"SubWikiSMainWikiNotExistError": "親Wikiが添付されているメインWikiは存在しません",
"SubWikiSMainWikiNotExistErrorDescription": "子Wikiを作成する際には、必ず親となるメインWikiを選択して紐付ける必要がありますが、現在この子Wikiが紐付くべきメインWikiが見つからず、アタッチできません。",
"ViewLoadUrlError": "E-9 ウェブページの読み込み失敗エラー",
"ViewLoadUrlErrorDescription": "E-9 ワークスペースに対応するWikiページの読み込みに失敗しましたが、再試行します",
"WikiRuntimeError": "E-13 ウィキの実行時にエラーが発生しました",
"WikiRuntimeErrorDescription": "E-13 ウィキの実行中にエラーが発生しました。原因についてはログファイルを確認し、issueをアップロードして提出してください。修正のために対応します。",
"WorkspaceFailedToLoadError": "E-8 ワークスペースの読み込み失敗エラー",
"WorkspaceFailedToLoadErrorDescription": "E-8 ワークスペースに対応するWikiページの読み込みに失敗しました。原因はさまざまですが、基本的にはプログラムのバグによるものです。",
"ZxInitializationError": "E-12 Zx コード実行サービスの初期化エラー",
"ZxInitializationErrorDescription": "E-12 Zx コード実行サービスの初期化エラーが発生しました。原因についてはログファイルを確認し、issueをアップロードして提出してください。修正のためにご協力お願いします。",
"ZxInitializationRetryFailedError": "E-11 Zx コード実行サービスの初期化再試行エラー",
"ZxInitializationRetryFailedErrorDescription": "E-11 Zx コード実行サービスの初期化エラーが発生しました。複数回再試行しても失敗しています。問題を修正するため、ログファイルをアップロードしてissueレポートでエラーを報告してください。",
"ZxNotInitializedError": "E-10 Zx コード実行サービスが初期化されていないエラー",
"ZxNotInitializedErrorDescription": "E-10 Zx コード実行サービスの初期化に失敗しました。自動的に初期化を試みます。"
},
"ErrorMessage": "エラーメッセージ",
"Help": {
"Alternatives": "その他のソース",
"Contribute": "このサイトにコンテンツを提供する",
"Description": "「開く」ボタンをクリックすると、新しいウィンドウでページが開きます。初回のページ読み込みには5秒から1分かかり、インターネットからのロードが必要ですオフラインでは利用できません。開いたページの内容は自由に編集可能で、学んだ機能をサンドボックスプレイグラウンドとして試すことができます。変更内容を保存したい場合は、TiddlyWikiの保存ボタンをクリックしてHTML形式の単一ページWikiとして保存してください。",
"List": "ヘルプリスト",
"Tags": {
}
},
"LOG": {
"CommitBackupMessage": "太記デスクトップ版を使用してバックアップ",
"CommitMessage": "太記デスクトップ版を使用して同期する"
},
"LinOnetwo": "林一二",
"Loading": "読み込み中",
"Log": {
"AddComplete": "追加(Git Add)成功",
"AddingFiles": "バックアップ対象のファイルを追加開始Git Add",
"CantForcePullError": "強制プルに失敗しました。リポジトリが特殊な状態にある可能性があります。",
"CantSyncGitNotInitialized": "同期できません。このフォルダはGitリポジトリとして初期化されていません",
"CantSyncInSpecialGitStateAutoFixFailed": "同期できません。このフォルダは特別な状態にあり、直接同期できません。自動修正が試みられましたが、エラーが残っています。すべての競合を手動で解決してくださいVSCodeを使用してWikiフォルダを開く。それでも解決しない場合は、プロのGitツールSource Tree、GitKrakenを使用して問題を解決してください。",
"CantSyncInSpecialGitStateAutoFixSucceed": "このフォルダは特別な状態にあり、直接同期できませんでしたが、自動的に修正されました",
"CantSynchronizeAndSyncScriptIsInDeadLoop": "同期できません。同期スクリプトがデッドループに陥っています。",
"CheckingLocalGitRepoSanity": "ローカルのGitリポジトリが正しく初期化されているかどうかを検出しています",
"CheckingLocalSyncState": "ローカルの状態をクラウドに同期する必要があるかどうかを検出中です",
"CheckingRebaseStatus": "リベース(Rebase)の処理方案を分析中です",
"CommitComplete": "ローカルコミットが完了しました",
"FailedToOpenDirectory": "フォルダを開けません {{path}} {{errorMessage}}",
"FailedToOpenFile": "ファイルを開けません {{path}} {{errorMessage}}",
"FetchingData": "クラウドデータを取得して比較しています",
"FinishForcePull": "強制プル完了",
"GitMergeFailed": "Gitマージの結果が良くありません。マージ戦略に欠陥がある可能性があります",
"GitPushFailed": "Gitプッシュの結果が良くありません。通常、ネットワークの問題を意味します。",
"GitRepositoryConfigurateFailed": "Gitリポジトリの設定に失敗しました。エラーログを参照してください",
"GitRepositoryConfigurationFinished": "Gitリポジトリの設定が完了しました",
"GitTokenExpireOrWrong": "Gitの認証情報Tokenが期限切れです。再度ログインするか、認証情報とユーザー名が一致していません。",
"GitTokenMissing": "Git 認証情報Tokenが不足しています",
"HaveThingsToCommit": "コミットする必要がある内容があり、自動的にコミットしています",
"StartGitInitialization": "ローカルGitリポジトリの初期化を開始します",
"InitializeWikiGit": "WikiとGitを初期化中です",
"InitializeWorkspaceView": "ワークスペースとブラウザウィンドウを初期化し、コンテンツを読み込んでいます。しばらくお待ちください。",
"InitializeWorkspaceViewDone": "作成に成功しました、コンテンツをロードします",
"LocalAheadStartUpload": "ローカルの状態がクラウドより進んでいるため、アップロードを開始します",
"LocalStateBehindSync": "ローカルの状態がクラウドより遅れているため、クラウドデータをマージします",
"LocalStateDivergeRebase": "ローカルの状態がクラウドと分岐しているため、リベースを開始します",
"NoNeedToSync": "同期の必要はありません。ローカルの状態はクラウドと一致しています",
"NotAGitRepository": "Gitリポジトリではありません",
"PerformLastCheckBeforeSynchronizationFinish": "同期終了前の最終チェックを実行します",
"PrepareCloneOnlineWiki": "オンラインWikiのインポートを準備しています",
"PrepareSync": "同期の準備をしています。ログインした著者情報を使用します",
"PreparingUserInfo": "アイデンティティ情報を設定しています",
"RebaseConflictNeedsResolve": "リベース中に競合が発生しました。競合を解決する必要があります",
"RebaseSucceed": "リベースが成功しました。アップロードを開始します",
"SkipForcePull": "強制プルをスキップ、リモートに更新なし",
"StartBackupToGithubRemote": "WikiがあるローカルGitをGithubリモートリポジトリにバックアップしています。所要時間はインターネット速度によりますので、しばらくお待ちください",
"StartConfiguringGithubRemoteRepository": "リポジトリの初期化後、Githubリモートリポジトリの設定を開始します",
"StartFetchingFromGithubRemote": "Githubリモートリポジトリからデータを取得しています。所要時間はインターネット速度によりますので、しばらくお待ちください",
"StartForcePull": "リモートコンテンツの強制フェッチを開始し、ローカルを完全に上書きします",
"StartGitInitialization": "ローカルGitリポジトリの初期化を開始します",
"StartResettingLocalToRemote": "ローカルをクリアし、リモートコンテンツで上書きを開始します",
"SyncFailedSystemError": "同期に失敗しました。同期システムに問題がある可能性があります",
"SynchronizationFailed": "同期に失敗しました!\nGithub Desktopなどのツールを使用して現在のGitリポジトリの状態を確認し、競合を手動で解決してください。"
}
"SynchronizationFailed": "同期に失敗しました!\nGithub Desktopなどのツールを使用して現在のGitリポジトリの状態を確認し、競合を手動で解決してください。",
"SynchronizationFinish": "同期完了"
},
"Menu": {
"ActualSize": "実際のサイズ",
"Close": "閉じる",
"CurrentWorkspace": "現在のワークスペース",
"DeveloperToolsActiveWorkspace": "アクティブなワークスペースの開発者ツールを開く",
"Edit": "編集",
"ExportActiveTiddler": "アクティブなTiddlerをエクスポート",
"ExportWholeWikiHTML": "Wiki全体をHTMLとしてエクスポート",
"Find": "検索",
"FindMatches": "一致",
"FindNext": "次を検索",
"FindPrevious": "前を検索",
"Help": "ヘルプ",
"History": "履歴",
"Home": "ホーム",
"Language": "言語",
"LearnMore": "もっと詳しく...",
"PrintPage": "ページを印刷",
"ReportBugViaGithub": "GitHubでバグを報告...",
"RequestFeatureViaGithub": "GitHubで機能をリクエスト...",
"SelectNextWorkspace": "次のワークスペースを選択",
"SelectPreviousWorkspace": "前のワークスペースを選択",
"TidGi": "TidGi",
"TidGiMenuBar": "TidGiメニューバー",
"View": "表示",
"Wiki": "Wiki",
"Window": "ウィンドウ",
"Workspaces": "ワークスペース",
"ZoomIn": "ズームイン",
"ZoomOut": "ズームアウト"
},
"No": "いいえ",
"Open": "開く",
"Preference": {
"AlwaysOnTop": "常に最前面に表示",
"AlwaysOnTopDetail": "太記のメインウィンドウを常に他のウィンドウの上に表示し、他のウィンドウで覆われないようにする",
"AntiAntiLeech": "あるウェブサイトはホットリンク防止対策を施しており、特定の画像があなたのWikiに表示されるのをブロックすることがあります。私たちはそのサイトへのリクエストヘッダーをシミュレートすることで、この制限を回避しています。",
"AskDownloadLocation": "ダウンロード前に各ファイルの保存場所を確認する",
"AttachToMenuBar": "メニューバーに追加",
"AttachToMenuBarShowSidebar": "メニューバーに追加されたウィンドウにはサイドバーが含まれています",
"AttachToMenuBarShowSidebarTip": "一般的に、太記小窓は現在のワークスペースを素早く確認するために使用されるため、デフォルトではメインウィンドウのワークスペースと同期しており、サイドバーは不要で、デフォルトで非表示になっています。",
"AttachToMenuBarTip": "タスクバー/メニューバーのアイコンをクリックするとポップアップする小太記ウィンドウを作成します。ヒント:右クリックでコンテキストメニューにアクセスできます。",
"AttachToTaskbar": "タスクバーにピン留めする",
"AttachToTaskbarShowSidebar": "タスクバーに追加されたウィンドウにはサイドバーが含まれています",
"ChooseLanguage": "言語を選択 Choose Language",
"ClearBrowsingData": "ブラウザのデータをクリアGitコンテンツには影響なし",
"ClearBrowsingDataDescription": "クッキー、キャッシュなどを消去する",
"ClearBrowsingDataMessage": "本気ですか?すべての閲覧データが消去されます。このアクションは元に戻せません。",
"DarkTheme": "ダークテーマ",
"DefaultUserName": "デフォルトの編集者名",
"DefaultUserNameDetail": "Wiki でデフォルトで使用される編集者名は、Tiddler の作成または編集時に creator フィールドに入力されます。ワークスペース内で設定された編集者名によって上書きされることがあります。",
"DeveloperTools": "開発者ツール",
"DisableAntiAntiLeech": "アンチホットリンクを無効にする",
"DisableAntiAntiLeechDetail": "チェックしてアンチホットリンク機能を完全に無効にする",
"DisableAntiAntiLeechForUrls": "以下のURLのアンチホットリンクを無効にする",
"DisableAntiAntiLeechForUrlsDetail": "入力された各行に1つのURLを記入し、これらのURLに対して個別に反盗用リンク防止機能を無効にします。この機能は、一部の反・反盗用リンク防止機能を持つウェブサイトで画像が読み込めなくなる可能性があるためです。",
"DownloadLocation": "ダウンロード場所",
"Downloads": "ダウンロード",
"FriendLinks": "友情リンク",
"General": "インターフェースとインタラクション",
"HibernateAllUnusedWorkspaces": "プログラム起動時に未使用のワークスペースをすべてスリープ状態にする",
"HibernateAllUnusedWorkspacesDescription": "起動時に、最後に使用していたアクティブなワークスペースを除いて、すべてのワークスペースを休止状態にします。",
"HideMenuBar": "隠しメニューバー",
"HideMenuBarDetail": "Alt + M を押すと、非表示になっているメニューバーが表示されます。",
"HideSideBar": "サイドバーを隠す",
"HideSideBarIconDetail": "アイコンを非表示にしてワークスペース名のみを表示し、ワークスペースリストをよりコンパクトにします",
"HideTitleBar": "タイトルバーを隠す",
"HowToEnableNotifications": "<0>TidGiはネイティブ通知機能をサポートしています。ただし、場合によっては通知を受け取るために、Webアプリの設定を手動で構成する必要があります。</0><1>詳細を見る</1><2>。</2>",
"IgnoreCertificateErrors": "ネットワーク証明書エラーを無視する",
"IgnoreCertificateErrorsDescription": "<0>お勧めしません。</0><1>詳細を確認</1>。",
"ItIsWorking": "使いやすい!",
"Languages": "言語/ランゲージ",
"LightTheme": "明るい色のテーマ",
"MenubarAlwaysOnTop": "メニューバーの小ウィンドウを他のウィンドウの上に保持する",
"MenubarAlwaysOnTopDetail": "太記のメニューバーウィンドウを常に他のウィンドウの上に表示させ、他のウィンドウで覆われないようにします。",
"Miscellaneous": "その他の設定",
"MoreWorkspaceSyncSettings": "さらに多くのワークスペース同期設定",
"MoreWorkspaceSyncSettingsDescription": "ワークスペースアイコンを右クリックし、右クリックメニューから「ワークスペースの編集」を選択して、ワークスペース設定を開いてください。そこで各ワークスペースの同期設定を行います。",
"Network": "ネットワーク",
"Notifications": "通知",
"NotificationsDetail": "通知一時停止時間を設定",
"NotificationsDisableSchedule": "時間による通知の自動無効化:",
"NotificationsMuteAudio": "一時停止通知時にワークスペースもミュートする",
"OpenAtLogin": "起動時に自動実行",
"OpenAtLoginMinimized": "起動時に自動で開き、最小化する (MacOS)",
"OpenLogFolder": "ログフォルダを開く",
"OpenLogFolderDetail": "問題を報告する際は、最新の日付の .log ファイルを開き、その内容を開発者に送信するか、pastebin.com に貼り付けてから URL を Github Issue に貼り付けてください。",
"OpenMetaDataFolder": "太記ワークスペースのメタ情報フォルダを開く",
"OpenMetaDataFolderDetail": "太微のデータと太記のワークスペースデータは別々に保存されています。太記のデータにはワークスペースの設定などが含まれており、それらはJSON形式でこのフォルダ内に保存されています。",
"OpenV8CacheFolder": "V8キャッシュフォルダを開く",
"OpenV8CacheFolderDetail": "V8キャッシュフォルダには、アプリケーションの起動を高速化するためのキャッシュファイルが保存されています。",
"Performance": "性能",
"PrivacyAndSecurity": "プライバシーとセキュリティ",
"ReceivePreReleaseUpdates": "プレリリース更新を受信する",
"RememberLastVisitState": "前回訪問したページを記憶し、次回開いた時に前回の状態を復元する",
"RequireRestart": "再起動が必要",
"Reset": "本気ですか?すべての設定が元のデフォルトに復元されます。閲覧データは影響を受けません。このアクションは元に戻せません。",
"ResetNow": "今すぐリセット",
"RestorePreferences": "すべての設定を元のデフォルト値にリセットする",
"RunOnBackground": "バックグラウンドで実行を維持する",
"RunOnBackgroundDetail": "ウィンドウを閉じても終了せず、バックグラウンドで動作を継続します。再度アプリを開くと、すばやくウィンドウが復元されます。",
"RunOnBackgroundDetailNotMac": "太記の小窓を開くことをお勧めします。これにより、メニューバー/タスクバーのアイコンからウィンドウを再び開くことができます。",
"ShareBrowsingData": "ワークスペース間でブラウザデータ(クッキー、キャッシュなど)を共有し、閉じた後は各ワークスペースで異なるサードパーティサービスのアカウントにログインできます。",
"ShowSideBar": "サイドバーを表示",
"ShowSideBarDetail": "サイドバーを使用すると、ワークスペース間を素早く切り替えることができます。",
"ShowSideBarIcon": "サイドバーワークスペースアイコンを表示する",
"ShowSideBarText": "サイドバー上のボタンのテキストを表示する",
"ShowTitleBar": "タイトルバーを表示する",
"ShowTitleBarDetail": "タイトルバーには現在のページのタイトルが表示されます。",
"SpellCheck": "スペルチェック",
"SpellCheckLanguages": "優先スペルチェック言語",
"Support": "サポート",
"SwipeWithThreeFingersToNavigate": "3本の指でスワイプして進む・戻る",
"SwipeWithThreeFingersToNavigateDescription": "3本の指を使ったジェスチャーでページ間をナビゲートします。左にスワイプすると戻り、右にスワイプすると進みます。<br/>これを有効にするには、<3>macOSの環境設定 → トラックパッド → その他のジェスチャー → ページ間をスワイプ</3>を<5>3本指でスワイプ</5>または<7>2本または3本指でスワイプ</7>に変更する必要があります。",
"Sync": "同期とバックアップ",
"SyncBeforeShutdown": "シャットダウン前に自動的に同期する",
"SyncBeforeShutdownDescription": "パソコンをシャットダウンする前にデータを自動的に同期します。手動でアプリケーションを終了しても同期はトリガーされないことに注意してください。これは、アプリケーションがエラーを起こした際に誤ったデータが同期されるのを防ぐためです。Windowsシステムではこの機能はサポートされていません。",
"SyncInterval": "同期/バックアップ間隔",
"SyncIntervalDescription": "この長さの時間が経過するごとに、自動的にGithubへのバックアップが開始されます。ワークスペースがローカルの場合は、ローカルバックアップが作成されます再起動後に有効になります。",
"SyncOnlyWhenNoDraft": "草稿がない場合にのみ同期する",
"SyncOnlyWhenNoDraftDescription": "同期前に下書きやWYSIWYG編集状態のエントリがあるか確認し、存在する場合は今回の同期を行いません。これにより、下書きがあなたのブログに同期されるのを防ぎます。シャットダウン前の自動同期には適用されません。おそらく、下書きを別のコンピュータに持ち運んで編集を続けたい場合があるためです。",
"System": "システム",
"SystemDefaultTheme": "システムデフォルトのテーマカラー",
"TestNotification": "テスト通知機能",
"TestNotificationDescription": "<0>通知が表示されない場合は、<1>macOSの環境設定 → 通知 → TidGi</1>で通知が有効になっていることを確認してください</0>",
"Theme": "テーマカラー",
"TiddlyWiki": "太微TiddlyWiki",
"ToggleMenuBar": "メニューバーの表示/非表示を切り替える",
"Token": "Git認証情報",
"TokenDescription": "Gitサーバーへの認証とコンテンツ同期に使用する認証情報は、Githubなどのオンラインストレージサービスにログインして取得するか、「Personal Access Token」を手動で取得し、ここに入力することができます。",
"Translatium": "翻訳素APP",
"TranslatiumIntro": "どんな言語でも外国語学部の達人のように翻訳する",
"Updates": "更新",
"WebCatalog": "ウェブサイトディレクトリApp",
"WebCatalogEngineIntro": "「ウェブディレクトリApp」はTidGiの初期コードの源であり、オープンソースの「ウェブディレクトリApp」から多くの重要なコードを再利用しました。これには「ウェブディレクトリApp」とその作者であるQuang Lamに感謝します。",
"WebCatalogIntro": "どんなウェブサイトも驚くほど簡単にクロスプラットフォームアプリに変身。 \nブラウザのタブを切り替える手間なく、仕事の効率を大幅アップ。",
"WebSite": "公式サイト",
"WikiMetaData": "Wikiメタ情報",
"WikiMetaDataDescription": "Wikiの起動パラメータを設定する",
"hardwareAcceleration": "ハードウェアアクセラレーションを使用する"
},
"Save": "保存",
"Scripting": {
"ExecutingScript": "スクリプトを実行中です"
},
"SideBar": {
"Preferences": "設定...",
"UpdateAvailable": "更新があります!"
},
"Update": "更新",
"Updater": {
"CheckUpdate": "更新を確認",
"CheckingFailed": "更新の確認に失敗しました(ネットワークエラー)",
"CheckingForUpdate": "更新を確認中...",
"UpdateAvailable": "新しいバージョンがあります!",
"UpdateNotAvailable": "最新バージョンです"
},
"WorkspaceSelector": {
"Add": "追加",
"Agent": "エージェント",
"AreYouSure": "このワークスペースを削除してもよろしいですか?\nワークスペースを削除すると、このアプリケーション内のワークスペースが削除されますが、ハードドライブからフォルダは削除されません。\nただし、Wikiフォルダも削除することを選択した場合、すべての内容が削除されます。",
"DedicatedWorkspace": "特別作業区域",
"DefaultTiddlers": "デフォルトのTiddlers",
"EditCurrentWorkspace": "現在のワークスペースを編集",
"EditWorkspace": "ワークスペースを編集",
"Guide": "ガイド",
"Help": "ヘルプ",
"HibernateWorkspace": "ワークスペースを休止状態にする",
"OpenInBrowser": "ブラウザで開く",
"OpenInBrowserDisabledHint": "HTTP APIを有効にする必要があります",
"OpenWorkspaceFolder": "フォルダを開く",
"OpenWorkspaceFolderInEditor": "外部エディタでフォルダを開く",
"OpenWorkspaceFolderInGitGUI": "Git GUIで開く",
"OpenWorkspaceMenuName": "ワークスペースを開く",
"OpenWorkspaceTagTiddler": "{{tagName}} を開く",
"ReloadCurrentWorkspace": "現在のワークスペースをリロード",
"RemoveCurrentWorkspace": "現在のワークスペースを削除",
"RemoveWorkspace": "ワークスペースを削除",
"RemoveWorkspaceAndDelete": "ワークスペースを削除し、ディスクからWikiフォルダを削除する",
"WakeUpWorkspace": "ワークスペースを再開する"
},
"Yes": "はい"
}

View file

@ -0,0 +1,563 @@
{
"APILogs": {
"CurrentAgent": "Показать журнал агента: {{agentId}}",
"Description": "Журнал отладки вызовов внешнего API этого агента. Включите параметр 'Отладка внешнего API' в настройках, чтобы начать запись.",
"ErrorDetails": "Подробности ошибки",
"NoLogs": "API-журнал этого агента не найден.",
"NoResponse": "не отвечает",
"RequestDetails": "детали запроса",
"ResponseContent": "ответное содержание",
"ResponseMetadata": "метаданные ответа",
"StatusCancel": "отменено",
"StatusDone": "завершено",
"StatusError": "ошибка",
"StatusStart": "начато",
"StatusUpdate": "в процессе обработки",
"Title": "Журнал отладки API"
},
"Agent": {
"EditTitle": "редактировать имя интеллектуального агента",
"InvalidTabType": "Неверный тип вкладки. Требуется вкладка чата.",
"LoadingChat": "Загрузка диалога...",
"StartConversation": "начать диалог",
"Untitled": "без названия"
},
"Browser": {
"Back": "назад",
"Bookmark": "коллекционировать",
"CurrentUrl": "текущий URL",
"EnterUrlPlaceholder": "введите URL",
"Forward": "вперёд",
"Home": "Главная страница",
"Refresh": "обновить",
"RenderPlaceholder": "Это область рендеринга веб-страницы."
},
"Chat": {
"Cancel": "Отмена",
"ConfigError": {
"GoToSettings": "Перейти к настройкам",
"Title": "Проблема с конфигурацией"
},
"InputPlaceholder": "Введите сообщение, Ctrl+Enter для отправки",
"Send": "Отправить",
"SessionGroup": {
}
},
"Common": {
},
"ContextMenu": {
"AddToCurrentSplitView": "добавить к текущему разделенному экрану",
"Close": "закрыть",
"CloseAbove": "Закрыть вкладку сверху",
"CloseBelow": "Закрыть вкладку ниже",
"CloseOther": "Закрыть другие вкладки",
"CloseTabs": "Закрыть несколько вкладок",
"ConvertToSplitView": "переключиться на разделенный экран",
"CreateSplitViewWithActive": "создать разделенный экран с текущей вкладкой",
"Duplicate": "копировать",
"NewTabBelow": "Открыть в новой вкладке",
"Pin": "Закрепленные вкладки",
"Refresh": "обновить",
"RestoreClosed": "восстановить закрытую вкладку",
"Unpin": "открепить"
},
"CreateAgent": {
"AgentName": "название агента",
"AgentNameHelper": "Дайте вашему агенту описательное имя",
"AgentNamePlaceholder": "Введите название агента...",
"Back": "предыдущий шаг",
"CreatingPreview": "Создание предварительного просмотра интеллектуального агента...",
"EditPrompt": "редактировать подсказку",
"EditPromptDescription": "Настройте системные подсказки и поведение вашего интеллектуального агента",
"ImmediateUse": "тестировать и использовать",
"ImmediateUseDescription": "Протестируйте своего агента и начните использовать немедленно",
"Next": "следующий шаг",
"NoTemplateSelected": "Пожалуйста, сначала выберите шаблон.",
"Preview": "(предварительный просмотр)",
"SaveAndUse": "Сохранить и использовать агента",
"SearchTemplates": "Шаблон интеллектуального агента поиска...",
"SelectTemplate": "Выбрать шаблон",
"SelectTemplateDescription": "Выберите существующего агента в качестве начального шаблона.",
"SelectedTemplate": "выбран шаблон",
"SetupAgent": "настроить интеллектуального агента",
"SetupAgentDescription": "Назовите своего агента и выберите шаблон в качестве отправной точки",
"Title": "Создать нового интеллектуального агента"
},
"EditAgent": {
"AgentDescription": "описание агента",
"AgentDescriptionHelper": "Опишите функции и назначение вашего агента.",
"AgentDescriptionPlaceholder": "Введите описание интеллектуального агента...",
"AgentName": "название агента",
"AgentNameHelper": "Дайте вашему агенту описательное имя",
"AgentNamePlaceholder": "Введите название агента...",
"AgentNotFound": "Агент не найден",
"EditBasic": "Редактировать основную информацию",
"EditBasicDescription": "Редактировать основную информацию вашего агента",
"EditPrompt": "редактировать подсказку",
"EditPromptDescription": "Настройте системные подсказки и поведение вашего интеллектуального агента",
"ImmediateUse": "тестировать и использовать",
"ImmediateUseDescription": "Протестируйте своего агента и начните использовать немедленно",
"Loading": "Загрузка...",
"LoadingPromptConfig": "Загружается конфигурация подсказок...",
"PreviewChat": "предварительный просмотр чата",
"Save": "сохранить",
"Saving": "Сохранение...",
"Title": "редактировать определение интеллектуального агента"
},
"ModelFeature": {
},
"ModelSelector": {
"Model": "модель",
"NoModelSelected": "модель не выбрана",
"SelectModel": "выбор модели",
"Title": "выбор модели"
},
"NewTab": {
"CreateDefaultAgent": "Создать агента по умолчанию",
"CreateInstance": "создать экземпляр",
"CreateNewAgent": "Создать нового интеллектуального агента",
"EditDefinition": "редактировать определение",
"NewTab": "Новая вкладка",
"QuickAccess": "Быстрый доступ",
"SearchPlaceholder": "Поиск вкладок или интеллектуальных агентов..."
},
"Preference": {
"AIAgent": "агент",
"AIAgentDescription": "Управление базой данных записей диалогов AI Agent",
"AIAgentDescriptionDetail": "Здесь можно просмотреть и удалить информацию о размере и местоположении базы данных записей диалогов AI Agent.",
"APIKey": "API ключ",
"AddNewModel": "Добавить новую модель",
"AddNewProvider": "Добавить нового поставщика",
"AddProvider": "Добавить поставщика",
"AgentDatabaseDescription": "Все записи диалогов с AI Agent хранятся в этой базе данных, касаются только общения с ИИ, не влияют на содержимое Wiki и занимают объем {{size}}.",
"BaseURL": "Базовый URL API",
"BaseURLRequired": "Базовый URL API обязателен",
"Browse": "просматривать",
"CancelAddProvider": "Отменить добавление",
"ConfigureModelParameters": "параметры конфигурации",
"ConfigureProvider": "Настроить {{provider}}",
"ConfirmDelete": "Подтвердить удаление",
"ConfirmDeleteAgentDatabase": "Вы уверены, что хотите удалить базу данных со всеми записями диалогов ИИ? Это действие нельзя отменить.",
"ConfirmDeleteExternalApiDatabase": "Вы уверены, что хотите удалить базу данных, содержащую отладочную информацию внешнего API? Это действие нельзя отменить.",
"CustomProvider": "Пользовательский поставщик",
"DefaultAIModelSelection": "Выбор AI модели по умолчанию",
"DefaultAIModelSelectionDescription": "Выберите поставщика AI и модель, которые будут использоваться по умолчанию",
"DefaultEmbeddingModelSelection": "выбор модели встраивания по умолчанию",
"DefaultEmbeddingModelSelectionDescription": "Выбор модели встраивания по умолчанию для семантического поиска и векторных операций",
"DefaultImageGenerationModelSelection": "выбор модели генерации изображений по умолчанию",
"DefaultImageGenerationModelSelectionDescription": "выбрать модель генерации изображений по умолчанию для операции создания изображения из текста",
"DefaultSpeechModelSelection": "выбор модели генерации голоса по умолчанию",
"DefaultSpeechModelSelectionDescription": "Выбрать модель генерации голоса по умолчанию для операции преобразования текста в речь",
"DefaultTranscriptionsModelSelection": "выбор модели распознавания речи по умолчанию",
"DefaultTranscriptionsModelSelectionDescription": "выбрать модель распознавания речи по умолчанию для операции преобразования голоса в текст",
"DeleteAgentDatabase": "Удалить базу данных диалогов ИИ",
"DeleteExternalApiDatabase": "удалить внешнюю базу данных API",
"DeleteProvider": "удалить поставщика",
"DisabledProviderInfo": "Этот поставщик отключен, его модели не будут отображаться в списке выбора моделей",
"EnableProvider": "Включить этого поставщика",
"ExternalAPI": "Внешний интерфейс",
"ExternalAPIDebug": "Включить журнал отладки API",
"ExternalAPIDebugDescription": "После включения все запросы и ответы API будут записываться в базу данных для отладки.",
"ExternalApiDatabaseDescription": "База данных, содержащая отладочную информацию внешнего API, занимает пространство размером {{size}}.",
"FailedToAddModel": "Не удалось добавить модель",
"FailedToAddProvider": "Не удалось добавить поставщика",
"FailedToRemoveModel": "Не удалось удалить модель",
"FailedToSaveSettings": "Не удалось сохранить настройки",
"FailedToUpdateModel": "Не удалось обновить модель",
"FailedToUpdateProviderStatus": "Не удалось обновить статус поставщика",
"MaxTokens": "максимальная длина генерации",
"MaxTokensDescription": "Максимальное количество символов (в токенах), которое модель может сгенерировать в одном запросе.",
"ModelAddedSuccessfully": "Модель успешно добавлена",
"ModelAlreadyExists": "Модель уже существует",
"ModelCaption": "Название модели для отображения",
"ModelCaptionHelp": "Дружественное имя для отображения в интерфейсе, если не заполнено, будет использовано имя модели",
"ModelDetails": "Детали модели",
"ModelFeatures": "Возможности модели",
"ModelName": "Имя модели",
"ModelNameRequired": "Имя модели обязательно",
"ModelParameters": "параметры модели",
"ModelParametersDescription": "Настройка параметров поведения генеративных моделей ИИ, таких как температура, ограничение токенов и другие.",
"ModelRemovedSuccessfully": "Модель успешно удалена",
"ModelUpdatedSuccessfully": "Модель успешно обновлена.",
"Models": "Доступные модели",
"NoPresetSelected": "Нет выбранной предустановленной модели",
"NoProvidersAvailable": "Нет доступных поставщиков",
"OpenDatabaseFolder": "Открыть папку базы данных",
"PresetModels": "Предустановленные модели",
"PresetProvider": "Предустановленный поставщик",
"ProviderAddedSuccessfully": "Поставщик успешно добавлен",
"ProviderAlreadyExists": "Имя поставщика уже существует",
"ProviderClass": "Тип интерфейса поставщика",
"ProviderConfiguration": "Конфигурация поставщика",
"ProviderConfigurationDescription": "Настройте API ключ поставщика AI и другие настройки",
"ProviderDisabled": "Поставщик отключен",
"ProviderEnabled": "Поставщик включен",
"ProviderName": "Имя поставщика",
"ProviderNameRequired": "Имя поставщика обязательно",
"Search": "поиск и внедрение",
"SearchEmbeddingDelete": "удалить",
"SearchEmbeddingDeleteConfirm": "Вы уверены, что хотите удалить все векторные вложения рабочей области \"{{workspaceName}}\"? Это действие нельзя отменить.",
"SearchEmbeddingDeleteError": "Удаление вложения не удалось: {{error}}",
"SearchEmbeddingGenerate": "генерировать вложения",
"SearchEmbeddingGenerating": "Генерация...",
"SearchEmbeddingLastUpdated": "Последнее обновление: {{time}}",
"SearchEmbeddingNoAIConfigError": "Пожалуйста, сначала настройте параметры AI API в разделе внешних API.",
"SearchEmbeddingNoEmbeddingModelError": "Пожалуйста, сначала настройте параметры модели встраивания по умолчанию в разделе внешних API.",
"SearchEmbeddingStatusCompleted": "{{totalEmbeddings}} вложений из {{totalNotes}} заметок",
"SearchEmbeddingStatusError": "Ошибка: {{error}}",
"SearchEmbeddingStatusGenerating": "Генерация... ({{completed}}/{{total}})",
"SearchEmbeddingStatusIdle": "встраивание не сгенерировано",
"SearchEmbeddingUpdate": "обновить встраивание",
"SearchNoWorkspaces": "Рабочая область не найдена",
"SelectDefaultProvider": "Выбрать поставщика по умолчанию",
"SelectFromPresets": "Выбрать из предустановленных моделей",
"SelectModel": "Выбрать модель",
"SettingsSaved": "Настройки сохранены",
"SystemPrompt": "Системная подсказка",
"SystemPromptDescription": "Установите системную инструкцию, отправляемую AI, определяющую его поведение и возможности",
"SystemPromptPlaceholder": "Заполнитель подсказки системы",
"Temperature": "Температура",
"TemperatureDescription": "Низкие значения приводят к более детерминированным и сфокусированным ответам, а высокие — к более разнообразным и творческим.",
"TopP": "Топ P",
"TopPDescription": "Управление случайностью ответов. Низкие значения делают ответы более предсказуемыми, а высокие — допускают больше вариантов.",
"WorkflowFile": "файл рабочего процесса",
"WorkflowFileHelp": "Путь к JSON-файлу рабочего процесса ComfyUI",
"WorkflowFilePath": "Путь к файлу рабочего процесса"
},
"Prompt": {
"AutoRefresh": "Предварительный просмотр автоматически обновляется при изменении введенного текста.",
"CodeEditor": "Редактор кода",
"Flat": "плиточное представление",
"FormEditor": "редактор форм",
"LastUpdated": "Дата последнего обновления",
"Loading": "Загрузка в предварительном просмотре...",
"NoMessages": "Нет сообщений для предварительного просмотра",
"Preview": "Предварительный просмотр подсказок",
"SchemaNotProvided": "Формат не предоставлен.",
"SchemaNotProvidedDescription": "Не предоставлена JSON Schema или не удалось её корректно получить. Форма редактирования не может быть отображена.",
"Tree": "Древовидное представление",
"ValidationErrors": "обнаружить ошибку"
},
"PromptConfig": {
"AddItem": "добавить проект",
"EmptyArray": "Еще не добавлено ни одного элемента. Нажмите на кнопку ниже, чтобы добавить первый элемент.",
"ItemCount": "{{count}} элементов",
"RemoveItem": "удалить элемент списка",
"Tabs": {
"Prompts": "подсказка",
"Response": "отклик"
},
"Tags": {
"HelperText": "После ввода нажмите Enter, чтобы добавить метку, или выберите из предопределенных меток.",
"NoOptions": "Нет доступных тегов",
"Placeholder": "Введите метку..."
}
},
"Schema": {
"AIConfig": {
"Description": "Настройка конфигурации AI-диалога",
"Title": "AI конфигурация"
},
"AgentConfig": {
"Description": "Конфигурация агента",
"Id": "уникальный идентификатор агента",
"IdTitle": "ID агента",
"PromptConfig": {
"Description": "настройка подсказок",
"Prompts": "Список конфигурации подсказок",
"Response": "список конфигураций ответа",
"Title": "настройка подсказок"
},
"Title": "Конфигурация агента"
},
"AutoReroll": {
},
"BaseAPIConfig": {
"API": "API-провайдеры и конфигурация моделей",
"APITitle": "Настройка API",
"Description": "Базовая настройка API",
"ModelParameters": "Настройка параметров модели",
"ModelParametersTitle": "параметры модели",
"Title": "Базовая настройка API"
},
"DefaultAgents": {
"Description": "список конфигураций агента по умолчанию",
"Title": "агент по умолчанию"
},
"DynamicPosition": {
},
"FullReplacement": {
"Description": "полная замена параметров конфигурации",
"SourceType": "тип источника",
"SourceTypeTitle": "тип источника",
"SourceTypes": {
},
"TargetId": "ID целевого элемента",
"TargetIdTitle": "ID цели",
"Title": "полная замена параметров"
},
"Function": {
},
"HandlerConfig": {
},
"JavascriptTool": {
},
"MCP": {
"Description": "Настройка параметров протокола контекста модели",
"Id": "Идентификатор сервера MCP",
"IdTitle": "ID сервера",
"ResponseProcessing": {
},
"TimeoutMessage": "сообщение с истекшим сроком",
"TimeoutMessageTitle": "сообщение о тайм-ауте",
"TimeoutSecond": "Таймаут (секунды)",
"TimeoutSecondTitle": "время ожидания",
"Title": "параметры протокола контекста модели"
},
"ModelParameters": {
"Description": "Настройка параметров модели",
"MaxTokens": "максимальное количество генерируемых токенов",
"MaxTokensTitle": "максимальное количество токенов",
"SystemPrompt": "системная подсказка модели",
"SystemPromptTitle": "системное приглашение",
"Temperature": "Температура генерации ответа (чем выше = тем креативнее)",
"TemperatureTitle": "температура",
"Title": "параметры модели",
"TopP": "Параметр выборки Top P",
"TopPTitle": "Топ P"
},
"Position": {
"Bottom": "смещение нескольких сообщений снизу",
"BottomTitle": "смещение дна",
"Description": "настройка позиционных параметров",
"TargetId": "ID целевого элемента",
"TargetIdTitle": "ID цели",
"Title": "позиционные аргументы",
"Type": "тип местоположения",
"TypeTitle": "тип местоположения",
"Types": {
}
},
"Prompt": {
"Caption": "краткое описание",
"CaptionTitle": "описание",
"Children": "Список подсказок будет объединен в итоговый текст подсказки сверху вниз, снаружи внутрь.",
"ChildrenTitle": "подсказка",
"Description": "Полная конфигурация подсказок, включая тип и содержание.",
"Enabled": "Включить эту подсказку? Только включенные подсказки будут добавлены в итоговый текст.",
"EnabledTitle": "включить",
"Id": "Уникальный идентификатор конфигурации подсказки, удобный для ссылки на targetId в PromptDynamicModification.",
"IdTitle": "ID",
"Role": "роль подсказки в интерфейсе, совместимом с OpenAI",
"RoleTitle": "персонаж",
"RoleType": {
"Assistant": "Помощник - ответы и реакции ИИ",
"System": "Система - определение правил поведения и фоновых установок ИИ",
"User": "Пользователь - имитация ввода и запросов пользователя"
},
"Tags": "список тегов",
"TagsTitle": "метка",
"Text": "Содержание подсказки может включать синтаксис, поддерживаемый вики-текстом, например <<имя_переменной>>.",
"TextTitle": "текст",
"Title": "подсказка"
},
"PromptDynamicModification": {
"DynamicModificationTypes": {
}
},
"PromptPart": {
},
"ProviderModel": {
"Description": "провайдер и конфигурация модели",
"EmbeddingModel": "Название модели встраивания для семантического поиска и векторных операций",
"EmbeddingModelTitle": "встраиваемая модель",
"ImageGenerationModel": "Название модели генерации изображений для операций создания изображений из текста",
"ImageGenerationModelTitle": "модель генерации изображений",
"Model": "Название модели ИИ",
"ModelTitle": "модель",
"Provider": "Название поставщика ИИ",
"ProviderTitle": "провайдер",
"SpeechModel": "Название модели генерации речи для операций преобразования текста в речь",
"SpeechModelTitle": "голосовая модель",
"Title": "модель поставщика",
"TranscriptionsModel": "Название модели распознавания речи для преобразования голоса в текст",
"TranscriptionsModelTitle": "модель распознавания речи"
},
"RAG": {
"Removal": {
},
"SourceTypes": {
}
},
"Response": {
"Description": "Ответ от внешнего API, который обычно служит целью для динамического изменения в ответе, имеет такую же структуру, как и подсказка. Можно заполнить его предустановленным содержимым или использовать в качестве заполнителя (контейнера), куда ResponseDynamicModification внесёт конкретное содержимое ответа от внешнего API.",
"Title": "отклик"
},
"ResponseDynamicModification": {
"DynamicModificationTypes": {
},
"ResponseProcessingTypes": {
}
},
"ToolCalling": {
},
"Trigger": {
"Model": {
}
},
"Wiki": {
},
"WikiOperation": {
"Description": "Выполнение операций с Tiddler (добавление, удаление или установка текста) в рабочей области Wiki",
"Title": "Wiki операции",
"Tool": {
"Examples": {
},
"Parameters": {
"extraMeta": {
"Description": "JSON-строка дополнительных метаданных, таких как теги и поля, по умолчанию \"{}\"",
"Title": "дополнительные метаданные"
},
"operation": {
"Description": "Тип выполняемой операции",
"Title": "тип операции"
},
"options": {
"Description": "JSON-строка параметров операции, по умолчанию \"{}\"",
"Title": "Опции действий"
},
"text": {
"Description": "Текстовое содержание Tiddler",
"Title": "Содержание Tiddler"
},
"title": {
"Description": "Заголовок Tiddler",
"Title": "Заголовок Tiddler"
},
"workspaceName": {
"Description": "Имя или идентификатор рабочей области для работы",
"Title": "название рабочего пространства"
}
}
},
"ToolListPosition": {
"Position": "относительно места вставки целевого элемента (before/after)",
"PositionTitle": "позиция вставки",
"TargetId": "ID целевого элемента для вставки списка инструментов",
"TargetIdTitle": "ID цели"
},
"ToolResultDuration": "Количество ходов в диалоге, в течение которых результаты выполнения инструмента остаются видимыми; после этого результаты будут отображаться серым цветом.",
"ToolResultDurationTitle": "количество раундов устойчивого результата инструмента"
},
"WikiSearch": {
"Description": "Поиск содержимого рабочей области TiddlyWiki с помощью фильтрующих выражений",
"SourceType": "тип источника данных",
"SourceTypeTitle": "тип источника",
"Title": "Поиск в Вики",
"Tool": {
"Parameters": {
"filter": {
"Description": "TiddlyWiki выражения фильтров",
"Title": "фильтр"
},
"limit": {
"Description": "максимальное количество возвращаемых результатов",
"Title": "ограничение"
},
"query": {
"Description": "текст запроса (естественный язык), используемый при векторном поиске",
"Title": "запрос"
},
"searchType": {
"Description": "Выберите один режим поиска на основе правил или сходства.",
"Title": "тип поиска"
},
"threshold": {
"Description": "Порог схожести (0-1), результаты векторов ниже этого порога будут отфильтрованы.",
"Title": "порог"
},
"workspaceName": {
"Description": "Имя или идентификатор рабочей области для поиска",
"Title": "название рабочего пространства"
}
}
},
"ToolListPosition": {
"Position": "Позиция вставки относительно целевой позиции",
"PositionTitle": "позиция вставки",
"TargetId": "ID целевого элемента, список инструментов будет вставлен относительно этого элемента.",
"TargetIdTitle": "ID цели"
},
"ToolListPositionTitle": "Расположение списка инструментов",
"ToolResultDuration": "Количество ходов, в течение которых результаты выполнения инструмента остаются видимыми в диалоге; после превышения этого количества результаты будут отображаться серым цветом.",
"ToolResultDurationTitle": "количество раундов устойчивого результата инструмента"
}
},
"Search": {
"AvailableAgents": "доступные агенты",
"FailedToCreateChatWithAgent": "Не удалось создать диалог с агентом.",
"FailedToFetchAgents": "Не удалось получить список агентов",
"NoAgentsFound": "Агент не найден",
"NoClosedTabsFound": "Нет недавно закрытых вкладок",
"NoTabsFound": "Вкладка не найдена.",
"OpenTabs": "открытые вкладки",
"RecentlyClosedTabs": "Недавно закрытые вкладки"
},
"SplitView": {
"NoTabs": "В разделенном экране нет вкладок."
},
"Tab": {
"Title": {
"CreateNewAgent": "Создать нового интеллектуального агента",
"EditAgentDefinition": "Редакторский интеллектуальный агент",
"NewTab": "Новая вкладка",
"NewWeb": "создать новую веб-страницу",
"SplitView": ""
}
},
"Tool": {
"Schema": {
"Description": "описание",
"Examples": "пример использования",
"Optional": "дополнительный",
"Parameters": "параметр",
"Required": "необходимый"
},
"WikiOperation": {
"Error": {
"WorkspaceNotExist": "Рабочее пространство {{workspaceID}} не существует",
"WorkspaceNotFound": "Название или идентификатор рабочего пространства \"{{workspaceName}}\" не существует. Доступные рабочие пространства: {{availableWorkspaces}}"
},
"Success": {
"Added": "Успешно добавлен Tiddler \"{{title}}\" в рабочее пространство Wiki \"{{workspaceName}}\".",
"Deleted": "Успешно удален Tiddler \"{{title}}\" из рабочего пространства Wiki \"{{workspaceName}}\".",
"Updated": "Текст Tiddler \"{{title}}\" успешно установлен в рабочем пространстве Wiki \"{{workspaceName}}\"."
}
},
"WikiSearch": {
"Error": {
"ExecutionFailed": "Выполнение инструмента завершилось неудачей: {{error}}",
"WorkspaceNotExist": "Рабочее пространство {{workspaceID}} не существует",
"WorkspaceNotFound": "Название или идентификатор рабочего пространства \"{{workspaceName}}\" не существует. Доступные рабочие пространства: {{availableWorkspaces}}"
},
"Success": {
"Completed": "Поиск в Wiki завершен. Найдено {{totalResults}} результатов, показано {{shownResults}}:",
"NoResults": "В рабочей области Wiki \"{{workspaceName}}\" не найдены результаты для фильтра \"{{filter}}\".",
"NoVectorResults": "В рабочей области Wiki \"{{workspaceName}}\" не найдено результатов векторного поиска, соответствующих условиям (порог сходства: {{threshold}}).",
"VectorCompleted": "По результатам векторного поиска в рабочей области {{workspaceName}} найдены следующие соответствующие материалы:"
},
"UpdateEmbeddings": {
"Error": {
"ExecutionFailed": "Не удалось создать вложение: {{error}}",
"NoAIConfig": "Пожалуйста, сначала настройте провайдера ИИ и модель внедрения (в настройках).",
"WorkspaceNotExist": "Рабочее пространство {{workspaceID}} не существует",
"WorkspaceNotFound": "Название или идентификатор рабочего пространства \"{{workspaceName}}\" не существует. Доступные рабочие пространства: {{availableWorkspaces}}"
},
"Success": {
"Generated": "Успешно создан индекс векторных вложений для рабочей области {{workspaceName}}. Всего заметок: {{totalNotes}}, вложений: {{totalEmbeddings}}."
}
}
}
},
"Unknown": "неизвестный"
}

View file

@ -1,331 +1,471 @@
{
"Hello": "Привет",
"WorkspaceSelector": {
"Add": "Добавить",
"Guide": "Руководство",
"Help": "Помощь",
"OpenWorkspaceTagTiddler": "Открыть {{tagName}}",
"DefaultTiddlers": "Тидлеры по умолчанию",
"OpenWorkspaceMenuName": "Открыть рабочее пространство",
"EditWorkspace": "Настроить рабочее пространство",
"RemoveWorkspace": "Удалить рабочее пространство",
"AreYouSure": "Вы уверены, что хотите удалить это рабочее пространство? Удаление рабочего пространства удалит его из приложения, но не удалит папки с жесткого диска. Однако, если вы выберете удаление папки Wiki, все содержимое будет удалено.",
"RemoveWorkspaceAndDelete": "Удалить рабочее пространство и удалить папку Wiki с диска",
"BadWorkspacePath": "В вашей настройке рабочего пространства есть проблемы",
"EditCurrentWorkspace": "Настроить текущее рабочее пространство",
"RemoveCurrentWorkspace": "Удалить текущее рабочее пространство",
"HibernateWorkspace": "Гибернация рабочего пространства",
"WakeUpWorkspace": "Пробудить рабочее пространство",
"OpenWorkspaceFolder": "Открыть папку",
"ReloadCurrentWorkspace": "Перезагрузить текущее рабочее пространство",
"OpenWorkspaceFolderInEditor": "Открыть папку во внешнем редакторе",
"OpenWorkspaceFolderInGitGUI": "Открыть в Git GUI",
"OpenInBrowser": "Открыть в браузере",
"OpenInBrowserDisabledHint": "(Настройки→Включить HTTP API)"
},
"SideBar": {
"CommandPalette": "Палитра команд",
"UpdateAvailable": "Доступно обновление!",
"Preferences": "Настройки..."
},
"ContextMenu": {
"OpenTidGi": "Открыть TidGi",
"OpenTidGiMenuBar": "Открыть меню TidGi",
"OpenLinkInNewWindow": "Открыть ссылку в новом окне",
"OpenWorkspaceInNewWindow": "Открыть рабочее пространство в новом окне",
"Preferences": "Настройки...",
"TidGiSupport": "Поддержка TidGi",
"TidGiWebsite": "Сайт TidGi",
"Quit": "Выйти",
"Notifications": "Уведомления...",
"More": "Еще",
"About": "О программе",
"Reload": "Перезагрузить",
"Forward": "Вперед→",
"Back": "Назад←",
"DeveloperTools": "Инструменты разработчика",
"InspectElement": "Инспектировать элемент",
"LookUp": "Искать \"{{word}}\"",
"CopyEmailAddress": "Копировать адрес электронной почты",
"CopyLink": "Копировать ссылку",
"OpenLinkInBrowser": "Открыть ссылку в браузере",
"CopyImageURL": "Копировать URL изображения",
"CopyImage": "Копировать изображение",
"AddToDictionary": "Добавить в словарь",
"SearchWithGoogle": "Искать в Google",
"Cut": "Вырезать",
"Copy": "Копировать",
"Paste": "Вставить",
"RestartService": "Перезапустить сервис",
"RestartServiceComplete": "Перезапуск сервиса завершен",
"SyncNow": "Синхронизировать с облаком",
"NoNetworkConnection": "Нет сетевого подключения",
"OpenCommandPalette": "Открыть палитру команд",
"BackupNow": "Резервное копирование на локальный Git"
},
"Updater": {
"CheckingFailed": "Проверка не удалась (ошибка сети)",
"CheckUpdate": "Проверить обновление",
"CheckingForUpdate": "Проверка обновления...",
"DownloadProgress": "Прогресс загрузки",
"UpdateError": "Ошибка обновления",
"UpdateAvailable": "Доступно обновление!",
"UpdateCancelled": "Обновление отменено",
"UpdateDownloaded": "Обновление загружено",
"UpdateNotAvailable": "У вас последняя версия"
},
"AddWorkspace": {
"MainPageTipWithoutSidebar": "<0>Нажмите </0><strong>Рабочие пространства > Добавить рабочее пространство</strong><0> в меню или </0><strong> Нажмите здесь</strong><2>, чтобы начать использовать TiddlyWiki!</2>",
"MainPageTipWithSidebar": "<0>Нажмите </0><1>+</1><2> кнопку на боковой панели, чтобы начать использовать TiddlyWiki!</2>",
"NotFilled": "Не заполнено",
"GitRepoUrl": "URL репозитория Git",
"AddFileSystemPath": "Добавление путей файловой системы для под-Wiki",
"AddWorkspace": "Добавить рабочее пространство",
"Advanced": "Расширенные настройки",
"AndLinkToMainWorkspace": "и привязать к основной Wiki",
"CreateWiki": "Создать Wiki: ",
"CloneWiki": "Импортировать онлайн Wiki: ",
"ImportWiki": "Импортировать Wiki: ",
"LoginGithubAccount": "Войти в аккаунт Github",
"LogoutGithubAccount": "Выйти из аккаунта Github",
"MainWorkspaceDescription": "Содержит файлы конфигурации TiddlyWiki и публичный контент при публикации в виде блога.",
"NotLoggedIn": "Не вошли в систему",
"SubWorkspaceDescription": "Должен быть привязан к основному репозиторию, который можно использовать для хранения личного контента. Обратите внимание на два момента: подбаза знаний не может быть размещена в папке основной базы знаний; подбаза знаний обычно используется для синхронизации данных с частным репозиторием Github, который может быть доступен только мне, поэтому адрес репозитория не может совпадать с адресом основной базы знаний.\nПодбаза знаний вступает в силу путем создания символической ссылки (ярлыка) на основную базу знаний. После создания ссылки содержимое подбазы знаний можно увидеть в основной базе знаний.",
"CloneOnlineWiki": "Импортировать онлайн Wiki",
"CreateNewWiki": "Создать новую Wiki",
"ExistedWikiLocation": "Местоположение существующей Wiki",
"OpenLocalWiki": "Открыть локальную Wiki",
"SwitchCreateNewOrOpenExisted": "Переключиться на создание новой или открытие существующей WIKI",
"MainWorkspace": "Основное рабочее пространство",
"SubWorkspace": "Подрабочее пространство",
"WorkspaceFolder": "Местоположение папки рабочего пространства",
"WorkspaceParentFolder": "Родительская папка рабочего пространства",
"Choose": "Выбрать",
"MainWorkspaceLocation": "Путь к основному рабочему пространству",
"SubWorkspaceWillLinkTo": "Подрабочее пространство будет привязано к",
"BadWikiHtml": "Не удалось создать wiki из этого HTML файла",
"CanNotLoadList": "Не удается загрузить список репозиториев, плохое сетевое соединение.",
"CreatePrivateRepository": "Создать частный репозиторий",
"CreatePublicRepository": "Создать публичный репозиторий",
"OmitMoreResult": "Список показывает только первые {{loadCount}} результатов",
"Reload": "Перезагрузить",
"MainPageReloadTip": "<0><0>Попробуйте:<1><0>Нажмите <2>Перезагрузить</2> кнопку ниже или нажмите <5>CMD_or_Ctrl + R</5>, чтобы перезагрузить страницу.</0><1>Проверьте <2>Папку логов</2>, чтобы узнать, что произошло.</1><2>В худшем случае вы все еще можете скопировать для резервного копирования папку на вашем компьютере, щелкните правой кнопкой мыши значок рабочего пространства и выберите Удалить рабочее пространство, затем повторно импортируйте папку на вашем компьютере (или импортируйте ранее сохраненную HTML-версию wiki, перетащив HTML в).</2></1></0></0>",
"Processing": "Обработка...",
"SearchGithubRepoName": "Поиск имени репозитория Github",
"WaitForLogin": "Ожидание входа",
"WikiServerPort": "Номер порта сервера WIKI (измените, если есть конфликт, обычно по умолчанию)",
"WorkspaceFolderNameToCreate": "Имя новой папки рабочего пространства",
"CantCreateFolderHere": "Невозможно создать папку \"{{newWikiPath}}\" здесь",
"Choose": "Выбрать",
"CloneOnlineWiki": "Импортировать онлайн Wiki",
"CloneWiki": "Импортировать онлайн Wiki: ",
"CreateLinkFromSubWikiToMainWikiFailed": "Невозможно создать ссылку из папки \"{{subWikiPath}}\" в \"{{mainWikiTiddlersFolderPath}}\"",
"CreateLinkFromSubWikiToMainWikiSucceed": "Ярлык на под-Wiki успешно создан в основной Wiki, и ярлык, сохраняющий файл в основной Wiki, автоматически сохранит файл в под-Wiki.",
"CreateNewWiki": "Создать новую Wiki",
"CreatePrivateRepository": "Создать частный репозиторий",
"CreatePublicRepository": "Создать публичный репозиторий",
"CreateWiki": "Создать Wiki: ",
"ExistedWikiLocation": "Местоположение существующей Wiki",
"ExtractedWikiFolderName": "Имя папки извлеченной WIKI",
"GitDefaultBranchDescription": "Основная ветка вашего Git, Github изменил ее с master на main после того событ<D18B><D182>я",
"GitEmailDescription": "Электронная почта, используемая для коммитов Git, и используется для учета ежедневной активности на Github и других онлайн-сервисах git",
"GitRepoUrl": "URL репозитория Git",
"GitTokenDescription": "Учетные данные, используемые для входа в Git. Истекает через определенный период времени",
"GitUserNameDescription": "Имя учетной записи, используемое для входа в Git. Не псевдоним",
"ImportWiki": "Импортировать Wiki: ",
"LocalWikiHtml": "путь к html файлу",
"LocalWorkspace": "Локальное рабочее пространство",
"LocalWorkspaceDescription": "Используется только локально, полностью контролируйте свои данные. TidGi создаст для вас локальную систему резервного копирования git, позволяющую вернуться к предыдущим версиям тидлеров, но все содержимое будет потеряно при удалении локальной папки.",
"LogoutToGetStorageServiceToken": "Войдите в онлайн-сервис хранения, чтобы получить последние учетные данные",
"MainPageReloadTip": "<0><0>Попробуйте:<1><0>Нажмите <2>Перезагрузить</2> кнопку ниже или нажмите <5>CMD_or_Ctrl + R</5>, чтобы перезагрузить страницу.</0><1>Проверьте <2>Папку логов</2>, чтобы узнать, что произошло.</1><2>В худшем случае вы все еще можете скопировать для резервного копирования папку на вашем компьютере, щелкните правой кнопкой мыши значок рабочего пространства и выберите Удалить рабочее пространство, затем повторно импортируйте папку на вашем компьютере (или импортируйте ранее сохраненную HTML-версию wiki, перетащив HTML в).</2></1></0></0>",
"MainPageTipWithSidebar": "<0>Нажмите </0><1>+</1><2> кнопку на боковой панели, чтобы начать использовать TiddlyWiki!</2>",
"MainPageTipWithoutSidebar": "<0>Нажмите </0><strong>Рабочие пространства > Добавить рабочее пространство</strong><0> в меню или </0><strong> Нажмите здесь</strong><2>, чтобы начать использовать TiddlyWiki!</2>",
"MainWorkspace": "Основное рабочее пространство",
"MainWorkspaceDescription": "Содержит файлы конфигурации TiddlyWiki и публичный контент при публикации в виде блога.",
"MainWorkspaceLocation": "Путь к основному рабочему пространству",
"NotFilled": "Не заполнено",
"NotLoggedIn": "Не вошли в систему",
"OmitMoreResult": "Список показывает только первые {{loadCount}} результатов",
"OpenLocalWiki": "Открыть локальную Wiki",
"OpenLocalWikiFromHTML": "импортировать wiki.html",
"PathNotExist": "Путь не существует \"{{path}}\"",
"Processing": "Обработка...",
"Reload": "Перезагрузить",
"SearchGithubRepoName": "Поиск имени репозитория Github",
"StartCloningSubWiki": "Начать клонирование под-Wiki",
"StartCloningWiki": "Начать клонирование Wiki",
"StartCreatingSubWiki": "Начать создание под-Wiki",
"StartLinkingSubWikiToMainWiki": "Начать привязку под-Wiki к основной Wiki",
"StartUsingTemplateToCreateWiki": "Начать создание Wiki с использованием шаблонов",
"SubWikiCreationCompleted": "Под-Wiki создана",
"ThisPathIsNotAWikiFolder": "Каталог не является папкой Wiki \"{{wikiPath}}\"",
"WikiExisted": "Wiki уже существует в этом месте \"{{newWikiPath}}\"",
"WikiTemplateCopyCompleted": "Шаблон Wiki скопирован",
"WikiTemplateMissing": "Шаблон Wiki отсутствует \"{{TIDDLYWIKI_TEMPLATE_FOLDER_PATH}}\"",
"StartUpdatingWorkspace": "Обновление рабочего пространства",
"WorkspaceUpdated": "Рабочее пространство обновлено, и Wiki запускается",
"StartLinkingSubWikiToMainWiki": "Начать привязку под-Wiki к основной Wiki",
"AddFileSystemPath": "Добавление путей файловой системы для под-Wiki",
"TagName": "Имя тега",
"TagNameHelp": "Тидлеры с этим тегом будут добавлены в эту под-Wiki (вы можете добавить или изменить этот тег позже, щелкнув правой кнопкой мыши значок рабочего пространства и выбрав Настроить рабочее пространство)",
"GitToken": "Git токен",
"GitTokenDescription": "Учетные данные, используемые для входа в Git. Истекает через определенный период времени",
"NoGitInfoAlert": "Вы не выбрали адрес онлайн-репозитория Git или не вошли в аккаунт Github. Нажатие кнопки Создать создаст локальную Wiki, которая не будет автоматически синхронизироваться с Github. Пожалуйста, будьте внимательны.",
"LocalWorkspace": "Локальное рабочее пространство",
"LocalWorkspaceDescription": "Используется только локально, полностью контролируйте свои данные. TidGi создаст для вас локальную систему резервного копирования git, позволяющую вернуться к предыдущим версиям тидлеров, но все содержимое будет потеряно при удалении локальной папки.",
"SubWorkspace": "Подрабочее пространство",
"SubWorkspaceDescription": "Должен быть привязан к основному репозиторию, который можно использовать для хранения личного контента. Обратите внимание на два момента: подбаза знаний не может быть размещена в папке основной базы знаний; подбаза знаний обычно используется для синхронизации данных с частным репозиторием Github, который может быть доступен только мне, поэтому адрес репозитория не может совпадать с адресом основной базы знаний.\nПодбаза знаний вступает в силу путем создания символической ссылки (ярлыка) на основную базу знаний. После создания ссылки содержимое подбазы знаний можно увидеть в основной базе знаний.",
"SubWorkspaceWillLinkTo": "Подрабочее пространство будет привязано к",
"SwitchCreateNewOrOpenExisted": "Переключиться на создание новой или открытие существующей WIKI",
"SyncedWorkspace": "Синхронизированное рабочее пространство",
"SyncedWorkspaceDescription": "Для синхронизации с онлайн-сервисом хранения (например, Github) необходимо войти в сервис хранения или ввести свои учетные данные и иметь хорошее сетевое соединение. Вы можете синхронизировать данные между устройствами, и вы все равно будете владеть данными, используя надежный сервис хранения. И даже после случайного удаления папки вы все равно сможете загрузить данные с онлайн-сервиса на локальный компьютер.",
"GitEmailDescription": "Электронная почта, используемая для коммитов Git, и используется для учета ежедневной активности на Github и других онлайн-сервисах git",
"GitUserNameDescription": "Имя учетной записи, используемое для входа в Git. Не псевдоним",
"LogoutToGetStorageServiceToken": "Войдите в онлайн-сервис хранения, чтобы получить последние учетные данные",
"AddWorkspace": "Добавить рабочее пространство",
"WorkspaceUserName": "Имя пользователя рабочего пространства",
"WorkspaceUserNameDetail": "Имя редактора, используемое в Wiki, будет заполнено в поле создателя при создании или редактировании тидлера. Имя редактора, установленное в рабочем пространстве, переопределит глобальное имя редактора, назначенное в настройках. Это позволяет создавать тидлеры с разными идентичностями в одной Wiki, с несколькими рабочими пространствами, настроенными с разными именами пользователей.",
"TagName": "Имя тега",
"TagNameHelp": "Тидлеры с этим тегом будут добавлены в эту под-Wiki (вы можете добавить или изменить этот тег позже, щелкнув правой кнопкой мыши значок рабочего пространства и выбрав Настроить рабочее пространство)",
"ThisPathIsNotAWikiFolder": "Каталог не является папкой Wiki \"{{wikiPath}}\"",
"WaitForLogin": "Ожидание входа",
"WikiExisted": "Wiki уже существует в этом месте \"{{newWikiPath}}\"",
"WikiNotStarted": "Wiki не запущена или не загружена",
"Advanced": "Расширенные настройки",
"GitDefaultBranch": "Основная ветка Git",
"GitDefaultBranchDescription": "Основная ветка вашего Git, Github изменил ее с master на main после того событ<D18B><D182>я",
"LocalWikiHtml": "путь к html файлу",
"OpenLocalWikiFromHTML": "импортировать wiki.html",
"ExtractedWikiFolderName": "Имя папки извлеченной WIKI",
"BadWikiHtml": "Не удалось создать wiki из этого HTML файла"
"WikiTemplateCopyCompleted": "Шаблон Wiki скопирован",
"WikiTemplateMissing": "Шаблон Wiki отсутствует \"{{TIDDLYWIKI_TEMPLATE_FOLDER_PATH}}\"",
"WorkspaceFolder": "Местоположение папки рабочего пространства",
"WorkspaceFolderNameToCreate": "Имя новой папки рабочего пространства",
"WorkspaceParentFolder": "Родительская папка рабочего пространства",
"WorkspaceUserName": "Имя пользователя рабочего пространства",
"WorkspaceUserNameDetail": "Имя редактора, используемое в Wiki, будет заполнено в поле создателя при создании или редактировании тидлера. Имя редактора, установленное в рабочем пространстве, переопределит глобальное имя редактора, назначенное в настройках. Это позволяет создавать тидлеры с разными идентичностями в одной Wiki, с несколькими рабочими пространствами, настроенными с разными именами пользователей."
},
"Cancel": "отменить",
"ClickForDetails": "Нажмите, чтобы узнать подробности",
"ContextMenu": {
"About": "О программе",
"AddToDictionary": "Добавить в словарь",
"Back": "Назад←",
"BackupNow": "Резервное копирование на локальный Git",
"Copy": "Копировать",
"CopyEmailAddress": "Копировать адрес электронной почты",
"CopyImage": "Копировать изображение",
"CopyImageURL": "Копировать URL изображения",
"CopyLink": "Копировать ссылку",
"Cut": "Вырезать",
"DeveloperTools": "Инструменты разработчика",
"Forward": "Вперед→",
"InspectElement": "Инспектировать элемент",
"LookUp": "Искать \"{{word}}\"",
"More": "Еще",
"NoNetworkConnection": "Нет сетевого подключения",
"Notifications": "Уведомления...",
"OpenCommandPalette": "Открыть палитру команд",
"OpenLinkInBrowser": "Открыть ссылку в браузере",
"OpenTidGi": "Открыть TidGi",
"OpenTidGiMenuBar": "Открыть меню TidGi",
"OpenWorkspaceInNewWindow": "Открыть рабочее пространство в новом окне",
"Paste": "Вставить",
"Preferences": "Настройки...",
"Quit": "Выйти",
"Reload": "Перезагрузить",
"RestartService": "Перезапустить сервис",
"RestartServiceComplete": "Перезапуск сервиса завершен",
"SearchWithGoogle": "Искать в Google",
"SyncNow": "Синхронизировать с облаком",
"TidGiSupport": "Поддержка TidGi",
"TidGiWebsite": "Сайт TidGi"
},
"Delete": "удалить",
"Dialog": {
"CantFindWorkspaceFolderRemoveWorkspace": "Не удалось найти папку Wiki рабочей области, которая ранее находилась здесь! Возможно, папка Wiki была перемещена или в ней нет содержимого Wiki. Удалить рабочую область?",
"DoNotCare": "В любом случае",
"FocusedTiddlerNotFoundTitle": "Не удалось найти текущий выделенный элемент.",
"FocusedTiddlerNotFoundTitleDetail": "Вы можете установить плагин FocusedTiddler в CPL.",
"Later": "Позже",
"MadeWithLove": "<0>Есть</0><1> ❤ </1><2>разработчики:</2>",
"NeedCorrectTiddlywikiFolderPath": "Необходимо передать правильный путь, но этот путь не распознается TiddlyWiki.",
"PathPassInCantUse": "Входящий путь недоступен.",
"RemoveWorkspace": "Удалить рабочую область",
"ReportBug": "Сообщить об ошибке",
"ReportBugDetail": "Если вы ознакомились с руководством, изучили процесс работы, внимательно прочитали сообщение об ошибке и подумали над ним, тщательно проверили свои данные и уверены в их правильности, то можете нажать кнопку.",
"RestartAppNow": "Перезапустить приложение сейчас",
"RestartMessage": "Вам нужно перезапустить приложение, чтобы изменения вступили в силу.",
"RestartWikiNow": "Сейчас перезапускается база знаний.",
"Restarting": "перезагрузка",
"StorageServiceUserInfoNoFound": "Не удалось найти информацию о пользователе службы резервного копирования хранилища.",
"StorageServiceUserInfoNoFoundDetail": "Похоже, вы еще не вошли в службу резервного копирования, поэтому синхронизация этой Wiki временно отключена. Войдите в систему, чтобы предоставить данные для синхронизации.",
"WorkspaceFolderRemoved": "Рабочая папка перемещена или эта папка не является Wiki"
},
"EditWorkspace": {
"Path": "Путь Wiki",
"Save": "Сохранить",
"AddExcludedPlugins": "Введите имя плагина, который нужно игнорировать",
"AddExcludedPluginsDescription": "Можно искать установленные плагины в текущей Wiki или ввести любое название плагина.",
"AppearanceOptions": "Настройка внешнего вида рабочей области",
"BackupOnInterval": "Резервное копирование по интервалу",
"BackupOnIntervalDescription": "При включении данные будут автоматически резервироваться с помощью локального Git через регулярные интервалы (интервал в глобальных настройках), так что даже если адрес облачной синхронизации git не настроен, они будут автоматически резервироваться локально.",
"Cancel": "Отмена",
"DisableAudioTitle": "Отключить аудио",
"DisableNotificationTitle": "Отключить уведомления",
"ClickToExpand": "нажмите, чтобы развернуть",
"DisableAudio": "Запретить рабочему пространству воспроизводить аудио.",
"DisableAudioTitle": "Отключить аудио",
"DisableNotification": "Запретить рабочему пространству отправлять уведомления.",
"HibernateTitle": "Гибернация при неиспользовании",
"DisableNotificationTitle": "Отключить уведомления",
"EnableHTTPAPI": "Включить HTTP API",
"EnableHTTPAPIDescription": "Разрешить сторонним программам, таким как мобильное приложение Taiji, плагин Taiji Collection - Clip и другим, читать и изменять ваши заметки через HTTP-сетевой интерфейс.",
"EnableHTTPS": "Включить HTTPS",
"EnableHTTPSDescription": "Для обеспечения безопасного TLS-зашифрованного доступа вам нужно иметь свой собственный HTTPS-сертификат, который можно скачать у поставщика доменных имен, или вы можете найти бесплатные методы получения HTTPS-сертификатов.",
"ExcludedPlugins": "Плагины, которые нужно игнорировать",
"ExcludedPluginsDescription": "При запуске Wiki в режиме только для чтения в качестве блога, вы можете отказаться от загрузки некоторых плагинов, связанных с редактированием, чтобы уменьшить размер первоначально загружаемой страницы. Например, можно исключить `$:/plugins/tiddlywiki/codemirror` и другие подобные плагины, так как функциональность редактирования не требуется для работы блога.",
"Generate": "генерировать",
"HTTPSCertPath": "Путь к файлу сертификата",
"HTTPSCertPathDescription": "Местоположение файла сертификата с расширением .crt, обычно заканчивается на xxx_public.crt.",
"HTTPSKeyPath": "Путь к файлу Key",
"HTTPSKeyPathDescription": "Расположение файла закрытого ключа с расширением .key.",
"HTTPSPickCert": "Выбрать путь к файлу сертификата",
"HTTPSPickKey": "Выбрать путь к файлу Key",
"HTTPSUploadCert": "Добавить файл сертификата",
"HTTPSUploadKey": "Добавить файл ключа",
"HibernateDescription": "Сохранение использования ЦП, памяти и батареи. Это отключит автоматическую синхронизацию, вам нужно будет вручную коммитить и синхронизировать для резервного копирования данных.",
"SelectLocal": "Выбрать локальное изображение...",
"ResetDefaultIcon": "Сбросить значок по умолчанию",
"NoRevert": "Внимание! Эта операция не может быть отменена.",
"HibernateTitle": "Гибернация при неиспользовании",
"IsSubWorkspace": "это дочерняя рабочая область",
"LastNodeJSArgv": "последние параметры командной строки при запуске",
"LastVisitState": "Последняя посещенная страница",
"URL": "URL Wiki",
"Port": "Порт локального сервера",
"MainWorkspacePath": "Основной путь рабочей области",
"MiscOptions": "Разные настройки",
"Name": "Имя рабочего пространства",
"NameDescription": "Имя рабочего пространства, которое будет отображаться на боковой панели, может отличаться от фактического имени папки репозитория Git в рабочем пространстве",
"NoRevert": "Внимание! Эта операция не может быть отменена.",
"Path": "Путь Wiki",
"PathDescription": "Местоположение вашей локальной папки wiki.",
"Port": "Порт локального сервера",
"ReadOnlyMode": "Режим только для чтения",
"ReadOnlyModeDescription": "Можно использовать с проникновением в локальную сеть, позволяя TidGi работать как серверная программа для развертывания блогов. После открытия wiki можно будет изменять только пу<D0BF><D183>ем прямого изменения файла на диске (включая использование синхронизации git). Содержимое нельзя изменить на веб-странице, но любой может получить к нему доступ.",
"ResetDefaultIcon": "Сбросить значок по умолчанию",
"Save": "Сохранить",
"SaveAndSyncOptions": "сохранение и синхронизация",
"SelectLocal": "Выбрать локальное изображение...",
"ServerOptions": "Настройки блога и сервера",
"SyncOnInterval": "Синхронизация по интервалу",
"SyncOnIntervalDescription": "При включении будет автоматически синхронизироваться в соответствии с временным интервалом в глобальных настройках, и все равно будет автоматически синхронизироваться при запуске или вручную при нажатии кнопки. Будет автоматически резервировать данные на локальный git перед синхронизацией. Если выключено, будет только одна автоматическая синхронизация при открытии приложения и одна ручная синхронизация при нажатии кнопки синхронизации в wiki.",
"SyncOnStartup": "Синхронизация при запуске приложения",
"SyncOnStartupDescription": "Коммит и синхронизация при холодном старте приложения.",
"Name": "Имя рабочего пространства",
"NameDescription": "Имя рабочего пространства, которое будет отображаться на боковой панели, может отличаться от фактического имени папки репозитория Git в рабочем пространстве",
"BackupOnInterval": "Резервное копирование по интервалу",
"BackupOnIntervalDescription": "При включении данные будут автоматически резервироваться с помощью локального Git через регулярные интервалы (интервал в глобальных настройках), так что даже если адрес облачной синхронизации git не настроен, они будут автоматически резервироваться локально.",
"TiddlyWiki": "",
"TokenAuth": "Аутентификация по токену",
"TokenAuthAutoFillUserNameDescription": "Эта функция требует заполнения userName в глобальных настройках или настройках рабочего пространства, если он пуст, по умолчанию будет автоматически заполнен в настройках рабочего пространства, вы можете изменить его позже.",
"TokenAuthCurrentHeader": "Текущий заголовок запроса аутентификации",
"TokenAuthCurrentToken": "Текущие доступные учетные данные для аутентификации",
"TokenAuthCurrentTokenDescription": "Конфиденциальная информация, которую необходимо перегенерировать после утечки к враждебным субъектам. После перегенерации требуется обновить учетные данные для подключенных сторонних приложений.",
"TokenAuthCurrentTokenEmptyText": "Нажмите кнопку \"Сгенерировать\", чтобы создать новый сертификат.",
"TokenAuthDescription": "При включении учетные данные должны быть включены в HTTP-запрос для чтения и записи в вашу базу знаний, что предотвращает доступ других людей в той же локальной сети к заметкам, улучшая безопасность сервера. Нельзя включить одновременно с режимом только для чтения.",
"URL": "URL Wiki",
"UploadOrSelectPathDescription": "Нажмите кнопку \"Загрузить\", чтобы отправить файл на хранение в Taiji, или нажмите кнопку \"Выбрать путь\", чтобы выбрать файл из вашего местоположения.",
"WikiRootTiddler": "Корневой тидлер Wiki",
"WikiRootTiddlerDescription": "Корневой тидлер Wiki определяет основное поведение системы, пожалуйста, прочитайте официальную документацию, чтобы понять перед изменением",
"WikiRootTiddlerItems": {
"all": "Загрузить все сразу",
"lazy-images": "Загружать изображения по требованию",
"lazy-all": "Загружать изображения и текст по требованию"
},
"ReadOnlyModeDescription": "Можно использовать с проникновением в локальную сеть, позволяя TidGi работать как серверная программа для развертывания блогов. После открытия wiki можно будет изменять только пу<D0BF><D183>ем прямого изменения файла на диске (включая использование синхронизации git). Содержимое нельзя изменить на веб-странице, но любой может получить к нему доступ.",
"ReadOnlyMode": "Режим только для чтения",
"TokenAuth": "Аутентификация по токену",
"TokenAuthDescription": "При включении учетные данные должны быть включены в HTTP-запрос для чтения и записи в вашу базу знаний, что предотвращает доступ других людей в той же локальной сети к заметкам, улучшая безопасность сервера. Нельзя включить одновременно с режимом только для чтения.",
"TokenAuthAutoFillUserNameDescription": "Эта функция требует заполнения userName в глобальных настройках или настройках рабочего пространства, если он пуст, по умолчанию будет автоматически заполнен в настройках рабочего пространства, вы можете изменить его позже.",
"ServerOptions": "Настройки блога и сервера",
"EnableHTTPS": "Включить HTTPS",
"EnableHTTPSDescription": "Для обеспечения безопасного TLS-зашифрованного доступа вам нужно иметь свой собственный HTTPS-сертификат, который можно скачать у поставщика доменных имен, или вы можете найти бесплатные методы получения HTTPS-сертификатов.",
"HTTPSUploadCert": "Добавить файл сертификата",
"HTTPSUploadKey": "Добавить файл ключа",
"TokenAuthCurrentHeader": "Текущий заголовок запроса аутентификации"
}
},
"Error": {
"ALreadyExistErrorDescription": "В текущем пути уже есть папка, новая база знаний не может быть создана здесь.",
"AlreadyExistError": "Это место уже занято папкой.",
"CopyWikiTemplateError": "E-3 Ошибка копирования шаблона вики",
"CopyWikiTemplateErrorDescription": "E-3 Попытка скопировать или перезаписать последний шаблон Вики в соответствующее место не удалась. Пожалуйста, проверьте ваш ввод согласно подсказке.",
"DoubleWikiInstanceError": "E-4 Ошибка повторного запуска вики",
"DoubleWikiInstanceErrorDescription": "E-4 Вы запустили одну и ту же Wiki дважды, возможно, это вызвано ошибкой в программе.",
"HTMLCanNotLoadError": "Указанный путь к HTML-файлу недоступен.",
"HTMLCanNotLoadErrorDescription": "Введите путь к доступному HTML-файлу.",
"InitWikiGitError": "E-1 Ошибка инициализации хранилища заметок",
"InitWikiGitErrorDescription": "E-1 Не удалось скопировать шаблон для нового хранилища заметок или произошла ошибка при инициализации git в хранилище заметок. Вероятно, это баг.",
"InitWikiGitRevertError": "E-2 Ошибка инициализации хранилища заметок и отмены изменений",
"InitWikiGitRevertErrorDescription": "E-2 не только не удалось инициализировать хранилище заметок, но и отмена также не удалась. Это серьезная проблема, требующая ручной очистки новой папки, созданной в этом месте.",
"InitWikiGitSyncedWikiNoGitUserInfoError": "E-6 Ошибка инициализации хранилища заметок: не предоставлена информация о Git",
"InitWikiGitSyncedWikiNoGitUserInfoErrorDescription": "E-6 Для инициализации синхронизации хранилища заметок с облаком вам необходимо выбрать адрес git-репозитория в облаке и предоставить учетные данные соответствующего облачного сервиса, однако в настоящее время эта информация не получена.",
"InsertMenuAfterSubMenuIndexError": "E-5 Ошибка после вставки шаблона содержания в существующее содержание",
"InsertMenuAfterSubMenuIndexErrorDescription": "E-5 Вы пытаетесь вставить каталог, и afterSubMenu \"{{afterSubMenu}}\" находится внутри каталога с menuID \"{{menuID}}\", но мы не смогли найти его в каталоге \"{{menu}}\". Пожалуйста, укажите правильный menuID для выбора каталога.",
"MainWindowMissing": "E-7 Программа не может получить информацию о главном окне и не может нормально работать.",
"SubWikiSMainWikiNotExistError": "Основной Wiki, к которому прикреплен подчиненный Wiki, не существует.",
"SubWikiSMainWikiNotExistErrorDescription": "При создании подчиненной Wiki необходимо выбрать основную Wiki, к которой она будет прикреплена. Однако сейчас невозможно найти основную Wiki, к которой должна быть прикреплена данная подчиненная Wiki, и поэтому прикрепление невозможно.",
"ViewLoadUrlError": "E-9 Ошибка загрузки веб-страницы",
"ViewLoadUrlErrorDescription": "Не удалось загрузить вики-страницу, соответствующую рабочей области E-9, но будет предпринята повторная попытка.",
"WikiRuntimeError": "E-13 Ошибка во время работы вики",
"WikiRuntimeErrorDescription": "E-13 Ошибка во время работы Wiki. Причину смотрите в файле журнала (log) и загрузите issue для исправления.",
"WorkspaceFailedToLoadError": "E-8 Ошибка загрузки рабочей области",
"WorkspaceFailedToLoadErrorDescription": "Не удалось загрузить Wiki-страницу, соответствующую рабочей области E-8. Причин может быть много, но в основном это связано с ошибкой в программе.",
"ZxInitializationError": "E-12 Zx Ошибка инициализации службы выполнения кода",
"ZxInitializationErrorDescription": "E-12 Zx Ошибка инициализации сервиса выполнения кода. Причину смотрите в файле log и загрузите issue для исправления.",
"ZxInitializationRetryFailedError": "E-11 Zx Ошибка повторной попытки инициализации службы выполнения кода",
"ZxInitializationRetryFailedErrorDescription": "E-11 Zx Ошибка инициализации сервиса выполнения кода. Повторные попытки не увенчались успехом. Пожалуйста, загрузите log-файл и отправьте issue для сообщения об ошибке с целью её исправления.",
"ZxNotInitializedError": "E-10 Zx Ошибка: служба выполнения кода не инициализирована",
"ZxNotInitializedErrorDescription": "E-10 Zx: Служба выполнения кода не была успешно инициализирована, будет предпринята автоматическая попытка инициализации."
},
"ErrorMessage": "сообщение об ошибке",
"Help": {
"Alternatives": "другие источники",
"Contribute": "Внести вклад в этот сайт",
"Description": "Нажатие кнопки «Открыть» откроет страницу в новом окне. При первом открытии страница загружается из интернета, что может занять от 5 секунд до 1 минуты; без подключения к сети функция недоступна. Вы можете свободно изменять содержимое открытой страницы, используя её как песочницу для экспериментов с изученными функциями. Если вы хотите сохранить результаты изменений, нажмите кнопку сохранения TiddlyWiki, чтобы сохранить их как одностраничную вики в формате HTML.",
"List": "список помощи",
"Tags": {
}
},
"LOG": {
"CommitBackupMessage": "Использование TaiJi Desktop для резервного копирования",
"CommitMessage": "Синхронизация с использованием TaiJi Desktop версии."
},
"LinOnetwo": "ЛинОнетво",
"Loading": "Загрузка",
"Log": {
"AddComplete": "Добавление (Git Add) успешно",
"AddingFiles": "Начать добавление (Git Add) файлов для резервного копирования",
"CantForcePullError": "Принудительное извлечение не удалось, возможно, репозиторий находится в особом состоянии.",
"CantSyncGitNotInitialized": "Не удается синхронизировать, эта папка не инициализирована как репозиторий Git.",
"CantSyncInSpecialGitStateAutoFixFailed": "Не удается синхронизировать. Эта папка находится в особом состоянии и не может быть синхронизирована напрямую. Была предпринята попытка автоматического исправления, но ошибка сохраняется. Пожалуйста, сначала устраните все конфликты (например, откройте с помощью VSCode). Если это не поможет, попробуйте решить проблему с помощью профессиональных инструментов Git (Source Tree, GitKraken).",
"CantSyncInSpecialGitStateAutoFixSucceed": "Эта папка находилась в особом состоянии и изначально не могла быть синхронизирована напрямую, но проблема была автоматически устранена.",
"CantSynchronizeAndSyncScriptIsInDeadLoop": "Не удается синхронизировать, и скрипт синхронизации зациклился.",
"CheckingLocalGitRepoSanity": "Проверяется, правильно ли инициализирован локальный репозиторий Git.",
"CheckingLocalSyncState": "Проверка необходимости синхронизации локального состояния с облаком",
"CheckingRebaseStatus": "Анализ вариантов обработки перебазирования (Rebase)",
"CommitComplete": "Локальная фиксация (Commit) завершена",
"FailedToOpenDirectory": "Не удается открыть папку {{path}} {{errorMessage}}",
"FailedToOpenFile": "Не удалось открыть файл {{path}} {{errorMessage}}",
"FetchingData": "Идет загрузка данных из облака для сравнения информации.",
"FinishForcePull": "Принудительное извлечение завершено",
"GitMergeFailed": "Плохой результат слияния в Git, возможно, есть уязвимость в стратегии слияния.",
"GitPushFailed": "Плохие результаты загрузки в Git обычно означают наличие проблем с сетью.",
"GitRepositoryConfigurationFinished": "Репозиторий Git настроен.",
"GitTokenExpireOrWrong": "Токен Git устарел, необходимо войти снова, или токен не соответствует имени пользователя.",
"GitTokenMissing": "Отсутствует учетные данные Git (Token)",
"HaveThingsToCommit": "Есть изменения, требующие отправки (Commit), выполняется автоматическая отправка.",
"InitializeWikiGit": "Инициализация Wiki и Git",
"InitializeWorkspaceView": "Идет инициализация рабочей области и окна браузера, загрузка содержимого. Пожалуйста, подождите.",
"InitializeWorkspaceViewDone": "Создание успешно, загрузка содержимого начнётся скоро.",
"LocalAheadStartUpload": "Локальное состояние опережает облачное, начинается загрузка.",
"LocalStateBehindSync": "Локальное состояние отстает от облачного, начинается слияние данных из облака.",
"LocalStateDivergeRebase": "Локальное состояние расходится с облачным, начинается перебазирование (Rebase).",
"NoNeedToSync": "Нет необходимости в синхронизации, локальное состояние соответствует облачному.",
"PerformLastCheckBeforeSynchronizationFinish": "провести окончательную проверку перед завершением синхронизации",
"PrepareCloneOnlineWiki": "Подготовка к импорту онлайн Wiki",
"PrepareSync": "Подготовка к синхронизации, использование информации авторизованного пользователя.",
"PreparingUserInfo": "Настройка идентификационных данных",
"RebaseConflictNeedsResolve": "При перебазировании (Rebase) обнаружен конфликт, необходимо разрешить конфликт.",
"RebaseSucceed": "Перебазирование (Rebase) успешно завершено, начинается загрузка.",
"SkipForcePull": "Пропустить принудительное извлечение, на удаленном сервере нет обновлений",
"StartBackupToGithubRemote": "Идет резервное копирование локального Git-репозитория Wiki в облачное хранилище Github. Время выполнения зависит от скорости интернета, пожалуйста, подождите терпеливо.",
"StartConfiguringGithubRemoteRepository": "Хранилище инициализировано, начинается настройка облачного репозитория Git.",
"StartFetchingFromGithubRemote": "Идет загрузка данных из удаленного репозитория Github. Время зависит от скорости интернета и размера репозитория, пожалуйста, подождите.",
"StartForcePull": "Начинается принудительное получение удаленного содержимого, что полностью перезапишет локальные данные.",
"StartGitInitialization": "Начало инициализации локального репозитория Git",
"StartResettingLocalToRemote": "Начать очистку локальных данных и заменить их содержимым с удаленного сервера.",
"SyncFailedSystemError": "Сбой синхронизации, возможно, проблема в системе синхронизации.",
"SynchronizationFailed": "Синхронизация не удалась! Вам необходимо проверить состояние текущего Git-репозитория с помощью таких инструментов, как Github Desktop. Сбой мог произойти из-за проблем с сетью; если это так, попробуйте повторить попытку после устранения неполадок с подключением.",
"SynchronizationFinish": "синхронное завершение"
},
"Menu": {
"TidGi": "TidGi",
"ActualSize": "Фактический размер",
"Close": "закрыть",
"CurrentWorkspace": "текущая рабочая область",
"DeveloperToolsActiveWorkspace": "Инструменты разработчика для активного рабочего пространства",
"Edit": "Редактировать",
"View": "Просмотр",
"ExportActiveTiddler": "экспортировать текущую заметку",
"ExportWholeWikiHTML": "Экспортировать всю Wiki в HTML",
"Find": "Найти",
"FindMatches": "один совпадение",
"FindNext": "Найти следующее",
"FindPrevious": "Найти предыдущее",
"ActualSize": "Фактический размер",
"ZoomIn": "Увеличить",
"ZoomOut": "Уменьшить",
"Language": "Язык",
"Help": "Помощь",
"History": "История",
"Home": "Главная",
"Workspaces": "Рабочие пространства",
"SelectNextWorkspace": "Выбрать следующее рабочее пространство",
"SelectPreviousWorkspace": "Выбрать предыдущее рабочее пространство",
"DeveloperToolsActiveWorkspace": "Инструменты разработчика для активного рабочего пространства",
"Wiki": "Wiki",
"Language": "Язык",
"LearnMore": "Узнать больше",
"PrintPage": "Печать страницы",
"ExportWholeWikiHTML": "Экспортировать всю Wiki в HTML",
"Window": "Окно",
"Help": "Помощь",
"ReportBugViaGithub": "Сообщить об ошибке через GitHub",
"RequestFeatureViaGithub": "Запросить функцию через GitHub",
"LearnMore": "Узнать больше"
"SelectNextWorkspace": "Выбрать следующее рабочее пространство",
"SelectPreviousWorkspace": "Выбрать предыдущее рабочее пространство",
"TidGi": "TidGi",
"TidGiMenuBar": "Слишком помню маленькое окно.",
"View": "Просмотр",
"Wiki": "Wiki",
"Window": "Окно",
"Workspaces": "Рабочие пространства",
"ZoomIn": "Увеличить",
"ZoomOut": "Уменьшить"
},
"No": "Нет",
"Open": "открыть",
"Preference": {
"Notifications": "Уведомления",
"AlwaysOnTop": "Всегда сверху",
"RequireRestart": "Требуется перезапуск",
"HideSideBar": "Скрыть боковую панель",
"HideTitleBar": "Скрыть заголовок",
"ToggleMenuBar": "Переключить меню",
"TiddlyWiki": "TiddlyWiki",
"General": "Общие",
"Sync": "Синхронизация",
"System": "Система",
"Network": "Сеть",
"Languages": "Языки",
"DeveloperTools": "Инструменты разработчика",
"Downloads": "Загрузки",
"PrivacyAndSecurity": "Конфиденциальность и безопасность",
"Performance": "Производительность",
"Updates": "Обновления",
"FriendLinks": "Дружественные ссылки",
"Miscellaneous": "Разное",
"WebCatalogEngineIntro": "Введение в WebCatalog Engine",
"WebCatalog": "WebCatalog",
"WebCatalogIntro": "Введение в WebCatalog",
"Translatium": "Translatium",
"TranslatiumIntro": "Введение в Translatium",
"WebSite": "Веб-сайт",
"Support": "Поддержка",
"WikiMetaData": "Метаданные Wiki",
"WikiMetaDataDescription": "Описание метаданных Wiki",
"DefaultUserNameDetail": "Имя пользователя по умолчанию",
"DefaultUserName": "Имя пользователя по умолчанию",
"RememberLastVisitState": "Запомнить последнее состояние посещения",
"Theme": "Тема",
"SystemDefaultTheme": "Тема по умолчанию",
"LightTheme": "Светлая тема",
"AlwaysOnTopDetail": "Детали всегда сверху",
"AntiAntiLeech": "Анти-анти-слив",
"AskDownloadLocation": "Спрашивать местоположение загрузки для каждого файла",
"AttachToMenuBar": "прикрепить к панели меню",
"AttachToMenuBarShowSidebar": "Окно, прикрепленное к строке меню, содержит боковую панель.",
"AttachToMenuBarShowSidebarTip": "Совет по показу боковой панели при прикреплении к меню",
"AttachToMenuBarTip": "Совет по прикреплению к меню",
"AttachToTaskbar": "Прикрепить к панели задач",
"AttachToTaskbarShowSidebar": "Показать боковую панель при прикреплении к панели задач",
"ChooseLanguage": "Выбрать язык",
"ClearBrowsingData": "Очистить данные браузера",
"ClearBrowsingDataDescription": "Описание очистки данных браузера",
"ClearBrowsingDataMessage": "Вы уверены? Все данные просмотров будут удалены. Это действие нельзя отменить.",
"DarkTheme": "Темная тема",
"ShowSideBar": "Показать боковую панель",
"ShowSideBarDetail": "Показать детали боковой панели",
"ShowSideBarIcon": "Показать иконку боковой панели",
"HideSideBarIconDetail": "Скрыть детали иконки боковой панели",
"ShowSideBarText": "Показать текст боковой панели",
"DefaultUserName": "Имя пользователя по умолчанию",
"DefaultUserNameDetail": "Имя пользователя по умолчанию",
"DeveloperTools": "Инструменты разработчика",
"DisableAntiAntiLeech": "Отключить анти-анти-слив",
"DisableAntiAntiLeechDetail": "Детали отключения анти-анти-слива",
"DisableAntiAntiLeechForUrls": "Отключить анти-анти-слив для URL",
"DisableAntiAntiLeechForUrlsDetail": "Детали отключения анти-анти-слива для URL",
"DownloadLocation": "Местоположение загрузок",
"Downloads": "Загрузки",
"FriendLinks": "Дружественные ссылки",
"General": "Общие",
"HibernateAllUnusedWorkspaces": "Гибернация всех неиспользуемых рабочих пространств",
"HibernateAllUnusedWorkspacesDescription": "Описание гибернации всех неиспользуемых рабочих пространств",
"HideMenuBar": "Скрыть меню",
"HideMenuBarDetail": "Скрыть детали меню",
"AlwaysOnTopDetail": "Детали всегда сверху",
"AttachToTaskbar": "Прикрепить к панели задач",
"AttachToMenuBarTip": "Совет по прикреплению к меню",
"AttachToTaskbarShowSidebar": "Показать боковую панель при прикреплении к панели задач",
"AttachToMenuBarShowSidebarTip": "Совет по показу боковой панели при прикреплении к меню",
"RunOnBackground": "Запуск в фоновом режиме",
"RunOnBackgroundDetail": "Детали запуска в фоновом режиме",
"RunOnBackgroundDetailNotMac": "Детали запуска в фоновом режиме (не для Mac)",
"HideSideBar": "Скрыть боковую панель",
"HideSideBarIconDetail": "Скрыть детали иконки боковой панели",
"HideTitleBar": "Скрыть заголовок",
"HowToEnableNotifications": "<0>TidGi поддерживает уведомления из коробки. Но в некоторых случаях, чтобы получать уведомления, вам нужно вручную настроить дополнительные параметры веб-приложения.</0><1>Узнать больше</1><2>.</2>",
"IgnoreCertificateErrors": "Игнорировать ошибки сертификатов",
"IgnoreCertificateErrorsDescription": "<0>Не рекомендуется.</0><1>Узнать больше</1>.",
"ItIsWorking": "Работает!",
"Languages": "Языки",
"LightTheme": "Светлая тема",
"MenubarAlwaysOnTop": "Меню всегда сверху",
"MenubarAlwaysOnTopDetail": "Детали меню всегда сверху",
"SyncBeforeShutdown": "Синхронизация перед выключением",
"SyncBeforeShutdownDescription": "Описание синхронизации перед выключением",
"SyncOnlyWhenNoDraft": "Синхронизация только при отсутствии черновиков",
"SyncOnlyWhenNoDraftDescription": "Описание синхронизации только при отсутствии черновиков",
"SyncInterval": "Интервал синхронизации",
"SyncIntervalDescription": "Описание интервала синхронизации",
"Miscellaneous": "Разное",
"MoreWorkspaceSyncSettings": "Дополнительные настройки синхронизации рабочего пространства",
"MoreWorkspaceSyncSettingsDescription": "Описание дополнительных настроек синхронизации рабочего пространства",
"Token": "Токен",
"TokenDescription": "Описание токена",
"Network": "Сеть",
"Notifications": "Уведомления",
"NotificationsDetail": "Детали уведомлений",
"NotificationsDisableSchedule": "Расписание отключения уведомлений",
"NotificationsMuteAudio": "Отключить звук уведомлений",
"TestNotification": "Тест уведомлений",
"HowToEnableNotifications": "<0>TidGi поддерживает уведомления из коробки. Но в некоторых случаях, чтобы получать уведомления, вам нужно вручную настроить дополнительные параметры веб-приложения.</0><1>Узнать больше</1><2>.</2>",
"OpenAtLogin": "Открывать при входе",
"OpenAtLoginMinimized": "Открывать при входе свернутым",
"ChooseLanguage": "Выбрать язык",
"SpellCheck": "Проверка орфографии",
"SpellCheckLanguages": "Языки проверки орфографии",
"OpenLogFolder": "Открыть папку с логами",
"OpenLogFolderDetail": "Детали открытия папки с логами",
"OpenMetaDataFolder": "Открыть папку с метаданными",
"OpenMetaDataFolderDetail": "Детали открытия папки с метаданными",
"OpenV8CacheFolder": "Открыть папку с кешем V8",
"OpenV8CacheFolderDetail": "Детали открытия папки с кешем V8",
"Performance": "Производительность",
"PrivacyAndSecurity": "Конфиденциальность и безопасность",
"ReceivePreReleaseUpdates": "Получать предварительные обновления",
"RememberLastVisitState": "Запомнить последнее состояние посещения",
"RequireRestart": "Требуется перезапуск",
"Reset": "Вы уверены? Все настройки будут восстановлены до исходных значений по умолчанию. Данные просмотров не затронуты. Это действие нельзя отменить.",
"ResetNow": "Сбросить немедленно",
"RestorePreferences": "Восстановить настройки",
"DownloadLocation": "Местоположение загрузок",
"AskDownloadLocation": "Спрашивать местоположение загрузки для каждого файла",
"DisableAntiAntiLeech": "Отключить анти-анти-слив",
"DisableAntiAntiLeechDetail": "Детали отключения анти-анти-слива",
"DisableAntiAntiLeechForUrls": "Отключить анти-анти-слив для URL",
"DisableAntiAntiLeechForUrlsDetail": "Детали отключения анти-анти-слива для URL",
"AntiAntiLeech": "Анти-анти-слив",
"RunOnBackground": "Запуск в фоновом режиме",
"RunOnBackgroundDetail": "Детали запуска в фоновом режиме",
"RunOnBackgroundDetailNotMac": "Детали запуска в фоновом режиме (не для Mac)",
"ShareBrowsingData": "Делиться данными браузера",
"IgnoreCertificateErrors": "Игнорировать ошибки сертификатов",
"ClearBrowsingData": "Очистить данные браузера",
"ClearBrowsingDataDescription": "Описание очистки данных браузера",
"IgnoreCertificateErrorsDescription": "<0>Не рекомендуется.</0><1>Узнать больше</1>.",
"HibernateAllUnusedWorkspaces": "Гибернация всех неиспользуемых рабочих пространств",
"HibernateAllUnusedWorkspacesDescription": "Описание гибернации всех неиспользуемых рабочих пространств",
"hardwareAcceleration": "Аппаратное ускорение",
"ReceivePreReleaseUpdates": "Получать предварительные обновления"
"ShowSideBar": "Показать боковую панель",
"ShowSideBarDetail": "Показать детали боковой панели",
"ShowSideBarIcon": "Показать иконку боковой панели",
"ShowSideBarText": "Показать текст боковой панели",
"ShowTitleBar": "показать заголовок",
"ShowTitleBarDetail": "В заголовке отображается название текущей страницы.",
"SpellCheck": "Проверка орфографии",
"SpellCheckLanguages": "Языки проверки орфографии",
"Support": "Поддержка",
"SwipeWithThreeFingersToNavigate": "Перемещайтесь вперед и назад, проводя тремя пальцами.",
"SwipeWithThreeFingersToNavigateDescription": "Навигация между страницами с помощью жеста тремя пальцами. Проведите влево, чтобы вернуться назад, и вправо — для перехода вперед.<br/>Чтобы включить эту функцию, также необходимо изменить настройки <3>в macOS: «Системные настройки» → «Трекпад» → «Жесты» → «Перемещение между страницами»</3> на <5>«Смахивание тремя пальцами»</5> или <7>«Смахивание двумя или тремя пальцами».</7>.",
"Sync": "Синхронизация",
"SyncBeforeShutdown": "Синхронизация перед выключением",
"SyncBeforeShutdownDescription": "Описание синхронизации перед выключением",
"SyncInterval": "Интервал синхронизации",
"SyncIntervalDescription": "Описание интервала синхронизации",
"SyncOnlyWhenNoDraft": "Синхронизация только при отсутствии черновиков",
"SyncOnlyWhenNoDraftDescription": "Описание синхронизации только при отсутствии черновиков",
"System": "Система",
"SystemDefaultTheme": "Тема по умолчанию",
"TestNotification": "Тест уведомлений",
"TestNotificationDescription": "<0>Если уведомление не отображается, убедитесь, что уведомления включены в <1>настройках macOS → Уведомления → TidGi</1></0>",
"Theme": "Тема",
"TiddlyWiki": "TiddlyWiki",
"ToggleMenuBar": "Переключить меню",
"Token": "Токен",
"TokenDescription": "Описание токена",
"Translatium": "Translatium",
"TranslatiumIntro": "Введение в Translatium",
"Updates": "Обновления",
"WebCatalog": "WebCatalog",
"WebCatalogEngineIntro": "Введение в WebCatalog Engine",
"WebCatalogIntro": "Введение в WebCatalog",
"WebSite": "Веб-сайт",
"WikiMetaData": "Метаданные Wiki",
"WikiMetaDataDescription": "Описание метаданных Wiki",
"hardwareAcceleration": "Аппаратное ускорение"
},
"Loading": "Загрузка",
"Dialog": {
"RestartMessage": "Вам нужно перезапустить приложение, чтобы изменения вступили в силу.",
"RestartAppNow": "Перезапустить приложение сейчас",
"Later": "Позже"
"Save": "сохранить",
"Scripting": {
"ExecutingScript": "Выполнение скрипта"
},
"No": "Нет",
"Yes": "Да",
"LinOnetwo": "ЛинОнетво"
"SideBar": {
"Preferences": "Настройки...",
"UpdateAvailable": "Доступно обновление!"
},
"Update": "обновить",
"Updater": {
"CheckUpdate": "Проверить обновление",
"CheckingFailed": "Проверка не удалась (ошибка сети)",
"CheckingForUpdate": "Проверка обновления...",
"UpdateAvailable": "Доступно обновление!",
"UpdateNotAvailable": "У вас последняя версия"
},
"WorkspaceSelector": {
"Add": "Добавить",
"Agent": "агент",
"AreYouSure": "Вы уверены, что хотите удалить это рабочее пространство? Удаление рабочего пространства удалит его из приложения, но не удалит папки с жесткого диска. Однако, если вы выберете удаление папки Wiki, все содержимое будет удалено.",
"DedicatedWorkspace": "специальная рабочая зона",
"DefaultTiddlers": "Тидлеры по умолчанию",
"EditCurrentWorkspace": "Настроить текущее рабочее пространство",
"EditWorkspace": "Настроить рабочее пространство",
"Guide": "Руководство",
"Help": "Помощь",
"HibernateWorkspace": "Гибернация рабочего пространства",
"OpenInBrowser": "Открыть в браузере",
"OpenInBrowserDisabledHint": "(Настройки→Включить HTTP API)",
"OpenWorkspaceFolder": "Открыть папку",
"OpenWorkspaceFolderInEditor": "Открыть папку во внешнем редакторе",
"OpenWorkspaceFolderInGitGUI": "Открыть в Git GUI",
"OpenWorkspaceMenuName": "Открыть рабочее пространство",
"OpenWorkspaceTagTiddler": "Открыть {{tagName}}",
"ReloadCurrentWorkspace": "Перезагрузить текущее рабочее пространство",
"RemoveCurrentWorkspace": "Удалить текущее рабочее пространство",
"RemoveWorkspace": "Удалить рабочее пространство",
"RemoveWorkspaceAndDelete": "Удалить рабочее пространство и удалить папку Wiki с диска",
"WakeUpWorkspace": "Пробудить рабочее пространство"
},
"Yes": "Да"
}

View file

@ -0,0 +1,599 @@
{
"APILogs": {
"CurrentAgent": "显示智能体日志: {{agentId}}",
"Description": "此智能体的外部接口调用调试日志。在偏好设置中启用「外部接口调试」以开始记录。",
"ErrorDetails": "错误详情",
"NoLogs": "未找到此智能体的接口调用日志",
"NoResponse": "无响应",
"RequestDetails": "请求详情",
"ResponseContent": "响应内容",
"ResponseMetadata": "响应元数据",
"StatusCancel": "已取消",
"StatusDone": "已完成",
"StatusError": "错误",
"StatusStart": "已开始",
"StatusUpdate": "处理中",
"Title": "外部接口调试日志"
},
"Agent": {
"EditTitle": "编辑智能体名字",
"InvalidTabType": "无效的标签页类型。需要聊天标签页。",
"LoadingChat": "正在加载对话...",
"StartConversation": "开始对话",
"Untitled": "未命名"
},
"Browser": {
"Back": "后退",
"Bookmark": "收藏",
"CurrentUrl": "当前 URL",
"EnterUrlPlaceholder": "输入网址",
"Forward": "前进",
"Home": "主页",
"Refresh": "刷新",
"RenderPlaceholder": "这是网页渲染区域"
},
"Chat": {
"Cancel": "取消",
"ConfigError": {
"GoToSettings": "前往设置",
"Title": "配置问题"
},
"InputPlaceholder": "输入消息Ctrl+Enter 发送",
"Send": "发送",
"SessionGroup": {
}
},
"Common": {
},
"ContextMenu": {
"AddToCurrentSplitView": "添加到当前分屏",
"Close": "关闭",
"CloseAbove": "关闭上方标签页",
"CloseBelow": "关闭下方标签页",
"CloseOther": "关闭其他标签页",
"CloseTabs": "关闭多个标签页",
"ConvertToSplitView": "转为分屏视图",
"CreateSplitViewWithActive": "和当前标签页创建分屏",
"Duplicate": "复制",
"NewTabBelow": "在下方新建标签页",
"Pin": "固定标签页",
"Refresh": "刷新",
"RestoreClosed": "恢复关闭的标签页",
"Unpin": "取消固定"
},
"CreateAgent": {
"AgentName": "智能体名称",
"AgentNameHelper": "为您的智能体起一个描述性的名字",
"AgentNamePlaceholder": "输入智能体名称...",
"Back": "上一步",
"CreatingPreview": "正在创建预览智能体...",
"EditPrompt": "编辑提示词",
"EditPromptDescription": "自定义您的智能体的系统提示词和行为",
"ImmediateUse": "测试并使用",
"ImmediateUseDescription": "测试您的智能体并立即开始使用",
"Next": "下一步",
"NoTemplateSelected": "请先选择一个模板",
"Preview": "(预览)",
"SaveAndUse": "保存并使用智能体",
"SearchTemplates": "搜索智能体模板...",
"SelectTemplate": "选择模板",
"SelectTemplateDescription": "选择一个现有的智能体作为起始模板",
"SelectedTemplate": "已选择模板",
"SetupAgent": "设置智能体",
"SetupAgentDescription": "为您的智能体命名并选择一个模板作为起点",
"Steps": {
},
"Title": "创建新智能体"
},
"EditAgent": {
"AgentDescription": "智能体描述",
"AgentDescriptionHelper": "描述您的智能体的功能和用途",
"AgentDescriptionPlaceholder": "输入智能体描述...",
"AgentName": "智能体名称",
"AgentNameHelper": "为您的智能体起一个描述性的名字",
"AgentNamePlaceholder": "输入智能体名称...",
"AgentNotFound": "智能体未找到",
"EditBasic": "编辑基本信息",
"EditBasicDescription": "编辑您的智能体的基本信息",
"EditPrompt": "编辑提示词",
"EditPromptDescription": "自定义您的智能体的系统提示词和行为",
"ImmediateUse": "测试并使用",
"ImmediateUseDescription": "测试您的智能体并立即开始使用",
"Loading": "加载中...",
"LoadingPromptConfig": "正在加载提示词配置...",
"PreviewChat": "预览聊天",
"Save": "保存",
"Saving": "保存中...",
"Steps": {
},
"Title": "编辑智能体定义"
},
"ModelFeature": {
},
"ModelSelector": {
"Model": "模型",
"NoModelSelected": "未选择模型",
"SelectModel": "选择模型",
"Title": "模型选择"
},
"NewTab": {
"CreateDefaultAgent": "创建默认智能体",
"CreateInstance": "创建实例",
"CreateNewAgent": "创建新智能体",
"EditDefinition": "编辑定义",
"NewTab": "新建标签页",
"QuickAccess": "快速访问",
"SearchPlaceholder": "搜索标签页或智能体..."
},
"Preference": {
"AIAgent": "智能体",
"AIAgentDescription": "管理智能体对话记录数据库",
"AIAgentDescriptionDetail": "这里可以查看和删除智能体对话记录数据库的大小和位置信息",
"APIKey": "API 密钥",
"AddNewModel": "添加新模型",
"AddNewProvider": "添加新提供商",
"AddProvider": "添加提供商",
"AgentDatabaseDescription": "所有智能体对话记录都保存在这个数据库里,仅涉及与人工智能的交谈,不影响维基内容,占用空间为 {{size}}",
"BaseURL": "API 地址",
"BaseURLRequired": "API 地址为必填项",
"Browse": "浏览",
"CancelAddProvider": "取消添加",
"ConfigureModelParameters": "配置参数",
"ConfigureProvider": "配置 {{provider}}",
"ConfirmDeleteAgentDatabase": "确定要删除包含所有智能体对话记录的数据库吗?此操作无法撤销。",
"CustomProvider": "自定义提供方",
"DefaultAIModelSelection": "默认人工智能模型选择",
"DefaultAIModelSelectionDescription": "选择在未具体设置时默认使用人工智能提供商和模型",
"DefaultEmbeddingModelSelection": "默认嵌入模型选择",
"DefaultEmbeddingModelSelectionDescription": "选择用于语义搜索和向量操作的默认嵌入模型",
"DefaultImageGenerationModelSelection": "默认图像生成模型选择",
"DefaultImageGenerationModelSelectionDescription": "选择用于文字生成图像操作的默认图像生成模型",
"DefaultSpeechModelSelection": "默认语音生成模型选择",
"DefaultSpeechModelSelectionDescription": "选择用于文字转语音操作的默认语音生成模型",
"DefaultTranscriptionsModelSelection": "默认语音识别模型选择",
"DefaultTranscriptionsModelSelectionDescription": "选择用于语音转文字操作的默认语音识别模型",
"DeleteAgentDatabase": "删除人工智能对话数据库",
"DeleteProvider": "删除提供商",
"DisabledProviderInfo": "此提供商已禁用,其模型不会在模型选择列表中显示",
"EnableProvider": "启用此提供商",
"ExternalAPI": "外部服务接口",
"ExternalAPIDebug": "启用接口调试日志",
"ExternalAPIDebugDescription": "开启后,所有接口请求和响应将被记录到数据库中以便调试",
"ExternalApiDatabaseDescription": "包含外部接口Debug 信息的数据库,占用空间为 {{size}}",
"FailedToAddModel": "无法添加模型",
"FailedToAddProvider": "添加提供商失败",
"FailedToRemoveModel": "无法删除模型",
"FailedToSaveSettings": "无法保存设置",
"FailedToUpdateModel": "无法更新模型",
"FailedToUpdateProviderStatus": "无法更新提供商状态",
"MaxTokens": "最大生成长度",
"MaxTokensDescription": "模型在一次请求中可以生成的最大字符数以token计算",
"ModelAddedSuccessfully": "模型添加成功",
"ModelAlreadyExists": "模型已存在",
"ModelCaption": "模型显示名称",
"ModelCaptionHelp": "在界面上显示的友好名称,如不填则使用模型名称",
"ModelDetails": "模型详细信息",
"ModelFeatures": "模型功能",
"ModelName": "模型名称",
"ModelNameRequired": "模型名称为必填项",
"ModelParameters": "模型参数",
"ModelParametersDescription": "配置生成式AI模型的行为参数如温度、token限制等",
"ModelRemovedSuccessfully": "模型删除成功",
"ModelUpdatedSuccessfully": "模型更新成功",
"Models": "可用模型",
"NoPresetSelected": "不使用预设模型",
"NoProvidersAvailable": "没有可用的提供商",
"OpenDatabaseFolder": "打开数据库文件夹",
"PresetModels": "预设模型",
"PresetProvider": "预置提供商",
"ProviderAddedSuccessfully": "提供商添加成功",
"ProviderAlreadyExists": "提供商名称已存在",
"ProviderClass": "提供商接口类型",
"ProviderConfiguration": "提供商配置",
"ProviderConfigurationDescription": "配置人工智能提供商的接口密钥和其他设置",
"ProviderDisabled": "提供方已禁用",
"ProviderEnabled": "提供方已启用",
"ProviderName": "提供商名称",
"ProviderNameRequired": "提供商名称为必填项",
"SearchEmbeddingNoEmbeddingModelError": "请先在外部API部分配置默认嵌入模型设置。",
"SelectDefaultProvider": "选择默认提供商",
"SelectFromPresets": "从预设模型中选择",
"SelectModel": "选择模型",
"SettingsSaved": "设置已保存",
"SystemPrompt": "系统提示词",
"SystemPromptDescription": "用于初始化AI行为的系统指令定义其行为和能力",
"SystemPromptPlaceholder": "系统提示词占位符",
"Temperature": "温度",
"TemperatureDescription": "较低的值会产生更确定性、更集中的响应,较高的值会产生更多样化、更创造性的响应",
"TopP": "Top P",
"TopPDescription": "控制响应的随机性。较低的值使响应更确定,较高的值允许更多的可能性",
"WorkflowFile": "工作流文件",
"WorkflowFileHelp": "ComfyUI 工作流 JSON 文件的路径",
"WorkflowFilePath": "工作流文件路径"
},
"Prompt": {
"AutoRefresh": "预览会随输入文本的变化自动刷新",
"CodeEditor": "代码编辑器",
"Flat": "平铺视图",
"FormEditor": "表单编辑器",
"LastUpdated": "上次更新时间",
"Loading": "加载预览中...",
"NoMessages": "还没有消息可以预览",
"Preview": "提示词预览",
"SchemaNotProvided": "格式未提供",
"SchemaNotProvidedDescription": "没有提供 JSON Schema 或无法正确获取到。编辑表单无法展示。",
"Tree": "树形视图",
"ValidationErrors": "发现错误"
},
"PromptConfig": {
"AddItem": "添加项目",
"EmptyArray": "还没有添加任何项目。点击下面的按钮添加第一个项目。",
"ItemCount": "{{count}} 项",
"RemoveItem": "删除列表项",
"Tabs": {
"Prompts": "提示词",
"Response": "响应"
},
"Tags": {
"HelperText": "输入后按 Enter 键添加标签,或从预定义标签中选择",
"NoOptions": "没有可选标签",
"Placeholder": "输入标签..."
}
},
"Schema": {
"AIConfig": {
"Description": "AI 会话设置配置",
"Title": "AI 配置"
},
"AgentConfig": {
"Description": "智能体配置",
"Id": "智能体唯一标识符",
"IdTitle": "智能体 ID",
"PromptConfig": {
"Description": "提示词配置",
"Prompts": "提示词配置列表",
"Response": "响应配置列表",
"Title": "提示词配置"
},
"Title": "智能体配置"
},
"AutoReroll": {
},
"BaseAPIConfig": {
"API": "API 提供商和模型配置",
"APITitle": "API 配置",
"Description": "基础接口配置",
"ModelParameters": "模型参数配置",
"ModelParametersTitle": "模型参数",
"Title": "基础接口配置"
},
"DefaultAgents": {
"Description": "默认智能体配置列表",
"Title": "默认智能体"
},
"DynamicPosition": {
},
"FullReplacement": {
"Description": "完全替换参数配置",
"SourceType": "数据来源类型,决定用什么内容来替换目标元素",
"SourceTypeTitle": "源类型",
"SourceTypes": {
},
"TargetId": "目标元素ID",
"TargetIdTitle": "目标ID",
"Title": "完全替换参数"
},
"Function": {
},
"HandlerConfig": {
},
"JavascriptTool": {
},
"MCP": {
"Description": "模型上下文协议参数配置",
"Id": "MCP 服务器 ID",
"IdTitle": "服务器 ID",
"ResponseProcessing": {
},
"TimeoutMessage": "超时消息",
"TimeoutMessageTitle": "超时消息",
"TimeoutSecond": "超时时间(秒)",
"TimeoutSecondTitle": "超时时间",
"Title": "模型上下文协议参数"
},
"ModelParameters": {
"Description": "模型参数配置",
"MaxTokens": "生成的最大令牌数量",
"MaxTokensTitle": "最大令牌数",
"SystemPrompt": "模型系统提示词",
"SystemPromptTitle": "系统提示词",
"Temperature": "响应生成温度(越高=越创造性)",
"TemperatureTitle": "温度",
"Title": "模型参数",
"TopP": "Top P 采样参数",
"TopPTitle": "Top P"
},
"Position": {
"Bottom": "自底部偏移几条消息",
"BottomTitle": "底部偏移",
"Description": "位置参数配置,用于确定内容插入的精确位置。支持相对位置、绝对位置、前置和后置四种定位方式",
"TargetId": "目标元素ID",
"TargetIdTitle": "目标ID",
"Title": "位置参数",
"Type": "位置类型,用于确定内容插入的精确位置。支持相对位置、绝对位置、前置和后置四种定位方式",
"TypeTitle": "位置类型",
"Types": {
}
},
"Prompt": {
"Caption": "简短描述",
"CaptionTitle": "描述",
"Children": "子提示词列表,将从上到下,从外到里地拼接为最终的提示词文本。",
"ChildrenTitle": "子提示词",
"Description": "完整的提示词配置,包含类型和内容",
"Enabled": "是否启用此提示词,启用的才会拼入到最终的提示词中",
"EnabledTitle": "启用",
"Id": "提示词配置的唯一标识符,方便在 PromptDynamicModification 里通过 targetId 引用。",
"IdTitle": "ID",
"Role": "OpenAI 兼容接口的提示词角色。system: 定义AI的行为规则和背景设定user: 模拟用户的输入和请求assistant: AI的回复和响应内容",
"RoleTitle": "角色",
"RoleType": {
"Assistant": "助手 - AI的回复和响应内容",
"System": "系统 - 定义AI的行为规则和背景设定",
"User": "用户 - 模拟用户的输入和请求"
},
"Tags": "标签列表",
"TagsTitle": "标签",
"Text": "提示词内容,可以包含维基文本支持的语法,例如<<变量名>>。",
"TextTitle": "文本",
"Title": "提示词"
},
"PromptDynamicModification": {
"DynamicModificationTypes": {
}
},
"PromptPart": {
},
"ProviderModel": {
"Description": "提供商和模型配置",
"EmbeddingModel": "用于语义搜索和向量操作的嵌入模型名称",
"EmbeddingModelTitle": "嵌入模型",
"ImageGenerationModel": "用于文字生成图像操作的图像生成模型名称",
"ImageGenerationModelTitle": "图像生成模型",
"Model": "AI 模型名称",
"ModelTitle": "模型",
"Provider": "AI 提供商名称",
"ProviderTitle": "提供商",
"SpeechModel": "用于文字转语音操作的语音生成模型名称",
"SpeechModelTitle": "语音模型",
"Title": "提供商模型",
"TranscriptionsModel": "用于语音转文字操作的语音识别模型名称",
"TranscriptionsModelTitle": "语音识别模型"
},
"RAG": {
"Removal": {
},
"SourceTypes": {
}
},
"Response": {
"Description": "外部API的响应通常作为响应动态修改的目标结构与提示词的一样可以填写预置内容也可以作为占位符或容器由 ResponseDynamicModification 填入外部API的响应的具体内容。",
"Title": "响应"
},
"ResponseDynamicModification": {
"DynamicModificationTypes": {
},
"ResponseProcessingTypes": {
}
},
"ToolCalling": {
},
"Trigger": {
"Model": {
}
},
"Wiki": {
},
"WikiOperation": {
"Description": "在维基工作区中执行 条目操作(添加、删除或设置文本)",
"Title": "Wiki 操作",
"Tool": {
"Examples": {
},
"Parameters": {
"extraMeta": {
"Description": "额外元数据的 JSON 字符串,如标签和字段,默认为 \"{}\"",
"Title": "额外元数据"
},
"operation": {
"Description": "要执行的操作类型",
"Title": "操作类型"
},
"options": {
"Description": "操作选项的 JSON 字符串,默认为 \"{}\"",
"Title": "操作选项"
},
"text": {
"Description": "条目的文本内容",
"Title": "条目内容"
},
"title": {
"Description": "条目的标题",
"Title": "条目标题"
},
"workspaceName": {
"Description": "要操作的工作区名称或ID",
"Title": "工作区名称"
}
}
},
"ToolListPosition": {
"Position": "相对于目标元素的插入位置before/after",
"PositionTitle": "插入位置",
"TargetId": "要插入工具列表的目标元素的ID",
"TargetIdTitle": "目标ID"
},
"ToolResultDuration": "工具执行结果在对话中保持可见的轮数,超过此轮数后结果将变灰显示",
"ToolResultDurationTitle": "工具结果持续轮数"
},
"WikiSearch": {
"Description": "使用筛选器表达式搜索 TiddlyWiki 工作区内容",
"SourceType": "数据源类型",
"SourceTypeTitle": "源类型",
"Title": "Wiki 搜索",
"Tool": {
"Parameters": {
"filter": {
"Description": "TiddlyWiki 筛选器表达式",
"Title": "过滤器"
},
"limit": {
"Description": "返回的最大结果数量",
"Title": "限制"
},
"query": {
"Description": "向量搜索时使用的查询文本(自然语言)",
"Title": "查询"
},
"searchType": {
"Description": "选择基于规则或基于相似度的一个搜索模式",
"Title": "搜索类型"
},
"threshold": {
"Description": "相似度阈值0-1低于此阈值的向量结果将被过滤",
"Title": "阈值"
},
"workspaceName": {
"Description": "要搜索的工作区名称或ID",
"Title": "工作区名称"
}
},
"Description": "在Wiki工作空间中搜索Tiddler内容支持传统filter搜索和向量语义搜索",
"UpdateEmbeddings": {
"Parameters": {
"forceUpdate": {
"Title": "强制更新",
"Description": "是否强制重新生成嵌入索引,覆盖已有的嵌入数据(如果为 true 则忽略增量更新)。"
},
"workspaceName": {
"Title": "工作区名称",
"Description": "要为其生成或更新向量嵌入索引的工作区名称或 ID。"
}
},
"Description": "为Wiki工作区生成或更新向量嵌入索引用于语义搜索",
"workspaceName": {
"Title": "工作区名称",
"Description": "要为其生成或更新向量嵌入索引的工作区名称或 ID。"
},
"forceUpdate": {
"Title": "强制更新",
"Description": "是否强制重新生成嵌入索引,覆盖已有的嵌入数据(如果设置为 true 则忽略增量更新)。"
}
}
},
"ToolListPosition": {
"Position": "相对于目标位置的插入位置",
"PositionTitle": "插入位置",
"TargetId": "目标元素的ID工具列表将相对于此元素插入",
"TargetIdTitle": "目标ID"
},
"ToolListPositionTitle": "工具列表位置",
"ToolResultDuration": "工具执行结果在对话中保持可见的轮数,超过此轮数后结果将变灰显示",
"ToolResultDurationTitle": "工具结果持续轮数"
},
"Plugin": {
"IdTitle": "ID",
"Id": "插件 ID",
"CaptionTitle": "标题",
"Caption": "简短描述",
"ContentTitle": "内容",
"Content": "插件内容或说明",
"ForbidOverridesTitle": "禁止覆盖",
"ForbidOverrides": "是否禁止在运行时覆盖此插件的参数",
"PluginIdTitle": "插件标识",
"PluginId": "用于选择具体插件的标识符"
}
},
"Search": {
"AvailableAgents": "可用的智能体",
"FailedToCreateChatWithAgent": "无法创建与智能体的对话",
"FailedToFetchAgents": "获取智能体列表失败",
"NoAgentsFound": "未找到智能体",
"NoClosedTabsFound": "没有最近关闭的标签页",
"NoTabsFound": "没有找到标签页",
"OpenTabs": "打开的标签页",
"RecentlyClosedTabs": "最近关闭的标签页"
},
"SplitView": {
"NoTabs": "分屏视图中没有标签"
},
"Tab": {
"Title": {
"CreateNewAgent": "创建新智能体",
"EditAgentDefinition": "编辑智能体",
"NewTab": "新建标签页",
"NewWeb": "新建网页",
"SplitView": ""
}
},
"Tool": {
"Schema": {
"Description": "描述",
"Examples": "使用示例",
"Optional": "可选",
"Parameters": "参数",
"Required": "必需"
},
"Plugin": {
"IdTitle": "ID",
"Id": "插件 ID",
"CaptionTitle": "标题",
"Caption": "简短描述",
"ContentTitle": "内容",
"Content": "插件内容或说明",
"ForbidOverridesTitle": "禁止覆盖",
"ForbidOverrides": "是否禁止在运行时覆盖此插件的参数",
"PluginIdTitle": "插件标识",
"PluginId": "用于选择具体插件的标识符"
},
"WikiOperation": {
"Error": {
"WorkspaceNotExist": "工作区{{workspaceID}}不存在",
"WorkspaceNotFound": "工作区名称或ID\"{{workspaceName}}\"不存在。可用工作区:{{availableWorkspaces}}"
},
"Success": {
"Added": "成功在维基工作区\"{{workspaceName}}\"中添加了条目\"{{title}}\"",
"Deleted": "成功从维基工作区\"{{workspaceName}}\"中删除了条目\"{{title}}\"",
"Updated": "成功在维基工作区\"{{workspaceName}}\"中设置了条目\"{{title}}\"的文本"
}
},
"WikiSearch": {
"Error": {
"ExecutionFailed": "工具执行失败:{{error}}",
"WorkspaceNotExist": "工作区{{workspaceID}}不存在",
"WorkspaceNotFound": "工作区名称或ID\"{{workspaceName}}\"不存在。可用工作区:{{availableWorkspaces}}",
"VectorSearchRequiresQuery": "向量搜索需要提供查询参数query",
"VectorSearchRequiresConfig": "向量搜索需要 AI 配置(请在设置中配置提供商和嵌入模型)",
"VectorSearchFailed": "向量搜索失败:{{error}}",
"FilterSearchRequiresFilter": "筛选搜索需要提供过滤器参数filter"
},
"Success": {
"Completed": "Wiki搜索完成。找到{{totalResults}}个总结果,显示{{shownResults}}个:\n\n",
"NoResults": "在维基工作区\"{{workspaceName}}\"中未找到过滤器\"{{filter}}\"的结果",
"NoVectorResults": "在维基工作区\"{{workspaceName}}\"中未找到符合条件的向量搜索结果(相似度阈值:{{threshold}})。",
"VectorCompleted": "根据向量搜索,在工作区 {{workspaceName}} 中找到以下相关内容:\n\n"
},
"UpdateEmbeddings": {
"Error": {
"ExecutionFailed": "生成嵌入失败:{{error}}",
"NoAIConfig": "请先配置人工智能提供商和嵌入模型(在设置中)。",
"WorkspaceNotExist": "工作区{{workspaceID}}不存在",
"WorkspaceNotFound": "工作区名称或ID\"{{workspaceName}}\"不存在。可用工作区:{{availableWorkspaces}}"
},
"Success": {
"Generated": "已成功为工作区 {{workspaceName}} 生成向量嵌入索引。总计{{totalNotes}}个笔记,{{totalEmbeddings}}个嵌入。"
}
}
}
}
}

View file

@ -1,501 +1,488 @@
{
"Hello": "你好",
"LinOnetwo": "林一二",
"WorkspaceSelector": {
"Add": "添加",
"Guide": "引导",
"Help": "帮助",
"OpenWorkspaceTagTiddler": "打开 {{tagName}}",
"DefaultTiddlers": "默认条目",
"OpenWorkspaceMenuName": "打开工作区",
"EditWorkspace": "配置工作区",
"EditCurrentWorkspace": "配置当前工作区",
"ReloadCurrentWorkspace": "刷新当前工作区",
"HibernateWorkspace": "休眠工作区",
"WakeUpWorkspace": "唤醒工作区",
"RemoveWorkspace": "移除工作区",
"RemoveCurrentWorkspace": "移除当前工作区",
"RemoveWorkspaceAndDelete": "移除工作区并删除Wiki文件夹",
"AreYouSure": "你确定要移除这个工作区吗移除工作区会删除本应用中的工作区但不会删除硬盘上的文件夹。如果你选择一并删除Wiki文件夹则所有内容都会被被删除。",
"BadWorkspacePath": "工作区路径有问题",
"OpenWorkspaceFolder": "打开文件夹",
"OpenWorkspaceFolderInEditor": "用外部编辑器打开文件夹",
"OpenWorkspaceFolderInGitGUI": "用可视化Git工具打开",
"OpenInBrowser": "用浏览器打开",
"OpenInBrowserDisabledHint": "(启用 HTTP API 才能使用)"
},
"SideBar": {
"CommandPalette": "搜索/命令",
"UpdateAvailable": "有新版本!",
"Preferences": "设置..."
"AddWorkspace": {
"AddFileSystemPath": "正在为子知识库添加FileSystemPaths",
"AddWorkspace": "添加工作区",
"Advanced": "高级设置",
"AndLinkToMainWorkspace": "并链接到主知识库",
"BadWikiHtml": "该HTML文件无法用于创建知识库",
"CanNotLoadList": "无法加载仓库列表,网络不佳",
"CantCreateFolderHere": "无法在该处创建文件夹 \"{{newWikiPath}}\"",
"Choose": "选择",
"CloneOnlineWiki": "导入线上知识库",
"CloneWiki": "导入线上知识库: ",
"CreateLinkFromSubWikiToMainWikiFailed": "无法链接文件夹 \"{{subWikiPath}}\" 到 \"{{mainWikiTiddlersFolderPath}}\"",
"CreateLinkFromSubWikiToMainWikiSucceed": "在主知识库内成功创建子知识库的快捷方式,快捷方式会自动将文件导入子知识库。",
"CreateNewWiki": "创建新知识库",
"CreatePrivateRepository": "创建私有仓库",
"CreatePublicRepository": "创建公开仓库",
"CreateWiki": "创建知识库: ",
"ExistedWikiLocation": "现有的知识库的位置",
"ExtractedWikiFolderName": "转换后的知识库文件夹名称",
"GitDefaultBranchDescription": "你的Git的默认分支Github在黑命贵事件后将其从master改为了main",
"GitEmailDescription": "用于Git提交记录的Email用于在Github等服务上统计每日提交量",
"GitRepoUrl": "Git仓库线上网址",
"GitTokenDescription": "用于登录Git的凭证一定时间后会过期",
"GitUserNameDescription": "用于登录Git的账户名注意是你的仓库网址中你的名字部分",
"ImportWiki": "导入知识库: ",
"LocalWikiHtml": "HTML文件的路径",
"LocalWorkspace": "本地知识库",
"LocalWorkspaceDescription": "仅在本地使用,完全掌控自己的数据。太记会为你创建一个本地的 git 备份系统,让你可以回退到之前的版本,但当文件夹被删除时所有内容还是会丢失。",
"LogoutToGetStorageServiceToken": "登录在线存储服务以获取最新凭证",
"MainPageReloadTip": "<0><0>请尝试:<1><0>点击下面的 <2>重新加载</2> 按钮,或用快捷键 <5>CMD/Ctrl + R</5> 来刷新页面。</0><1>或者打开 <2>Log文件夹</2> 来看看具体的错误原因。</1><2>最糟糕的情况下也可以复制备份你电脑上的文件夹,右键工作区图标选择删除工作区,然后重新导入电脑上的文件夹(或通过拖入 HTML 导入之前备份的 HTML 版 知识库。)</2></1></0></0>",
"MainPageTipWithSidebar": "<0>点击侧边栏上的这个 </0><1>+</1><2>(加号按钮)来开始使用太微!</2>",
"MainPageTipWithoutSidebar": "<0>使用菜单上的</0><strong> 工作区 → 添加工作区 </strong><0>或</0><strong> 点击此处 </strong><2>来开始使用太微!</2>",
"MainWorkspace": "主知识库",
"MainWorkspaceDescription": "包含了太微的配置文件,以及发布为博客时的公开内容。",
"MainWorkspaceLocation": "主知识库位置",
"NotFilled": "未填",
"NotLoggedIn": "未登录",
"OmitMoreResult": "列表仅展示前 {{loadCount}} 个结果",
"OpenLocalWiki": "导入本地知识库",
"OpenLocalWikiFromHTML": "导入HTML知识库",
"PathNotExist": "该路径不存在 \"{{path}}\"",
"Processing": "正在处理...",
"Reload": "重新加载",
"SearchGithubRepoName": "搜索Github仓库名",
"StartCloningSubWiki": "开始导入线上子知识库",
"StartCloningWiki": "开始导入线上知识库",
"StartCreatingSubWiki": "开始创建子知识库",
"StartLinkingSubWikiToMainWiki": "开始链接子知识库到父知识库",
"StartUsingTemplateToCreateWiki": "开始用模板创建知识库",
"SubWikiCreationCompleted": "子知识库创建完毕",
"SubWorkspace": "子知识库",
"SubWorkspaceDescription": "必须依附于一个主知识库可用于存放私有内容。注意两点子知识库不能放在主知识库文件夹内子知识库一般用于同步数据到一个私有的Github仓库内仅本人可读写故仓库地址不能与主知识库一样。\n子知识库通过创建一个到主知识库的软链接快捷方式来生效创建链接后主知识库内便可看到子知识库内的内容了。",
"SubWorkspaceWillLinkTo": "子知识库将链接到",
"SwitchCreateNewOrOpenExisted": "切换创建新的还是打开现有的知识库",
"SyncedWorkspace": "云端同步知识库",
"SyncedWorkspaceDescription": "同步到在线存储服务例如Github需要你登录存储服务或输入登录凭证并有良好的网络连接。可以跨设备同步数据在使用了值得信任的存储服务的情况下数据仍归你所有。而且文件夹被不慎删除后还可以从在线服务重新下载数据到本地。",
"TagName": "标签名",
"TagNameHelp": "加上此标签的笔记将会自动被放入这个子知识库内(可先不填,之后右键点击这个工作区的图标选择编辑工作区修改)",
"ThisPathIsNotAWikiFolder": "该目录不是一个知识库文件夹 \"{{wikiPath}}\"",
"WaitForLogin": "等待登录",
"WikiExisted": "知识库已经存在于该位置 \"{{newWikiPath}}\"",
"WikiNotStarted": "知识库 页面未成功启动或未成功加载",
"WikiTemplateCopyCompleted": "模板知识库复制完毕",
"WikiTemplateMissing": "知识库模板缺失 \"{{TIDDLYWIKI_TEMPLATE_FOLDER_PATH}}\"",
"WorkspaceFolder": "工作区文件夹的位置",
"WorkspaceFolderNameToCreate": "即将新建的知识库文件夹名",
"WorkspaceParentFolder": "文件夹所在的父文件夹",
"WorkspaceUserName": "工作区编辑者名",
"WorkspaceUserNameDetail": "在知识库中使用的编辑者名,将在创建或编辑条目时填入 creator 字段。工作区内设置的编辑者名,将覆盖设置里配的全局的默认编辑者名。这方便你通过创建多个配了不同编辑者名的工作区,在同一个知识库里用不同的身份创建条目。"
},
"Cancel": "取消",
"ClickForDetails": "点击了解详情",
"ContextMenu": {
"OpenTidGi": "打开太记",
"OpenTidGiMenuBar": "打开太记小窗口",
"OpenCommandPalette": "打开搜索与命令面板",
"OpenLinkInNewWindow": "在新窗口中打开链接",
"OpenWorkspaceInNewWindow": "在新窗口中打开工作区",
"Preferences": "设置...",
"TidGiSupport": "TidGi 用户支持",
"TidGiWebsite": "TidGi 官网",
"Quit": "退出",
"More": "更多",
"About": "关于",
"Notifications": "消息管理...",
"Reload": "刷新",
"Forward": "向前→",
"Back": "向后←",
"DeveloperTools": "Web 开发者工具",
"InspectElement": "检查 Web 元素",
"LookUp": "在字典中查看 \"{{word}}\"",
"AddToDictionary": "添加到字典",
"Back": "向后←",
"BackupNow": "立即本地Git备份",
"Copy": "复制",
"CopyEmailAddress": "复制电子邮件地址",
"CopyImage": "复制图片",
"CopyImageURL": "复制图片URL",
"Cut": "剪切",
"CopyLink": "复制链接",
"Cut": "剪切",
"DeveloperTools": "Web 开发者工具",
"Forward": "向前→",
"InspectElement": "检查 Web 元素",
"LookUp": "在字典中查看 \"{{word}}\"",
"More": "更多",
"NoNetworkConnection": "无网络连接",
"Notifications": "消息管理...",
"OpenCommandPalette": "打开搜索与命令面板",
"OpenLinkInBrowser": "在浏览器中打开链接",
"OpenTidGi": "打开太记",
"OpenTidGiMenuBar": "打开太记小窗口",
"OpenWorkspaceInNewWindow": "在新窗口中打开工作区",
"Paste": "粘贴",
"SearchWithGoogle": "用 Google 搜索",
"Preferences": "设置...",
"Quit": "退出",
"Reload": "刷新",
"RestartService": "重启服务",
"RestartServiceComplete": "重启服务成功",
"SearchWithGoogle": "用 Google 搜索",
"SyncNow": "立即同步云端",
"BackupNow": "立即本地Git备份",
"NoNetworkConnection": "无网络连接"
"TidGiSupport": "TidGi 用户支持",
"TidGiWebsite": "TidGi 官网"
},
"Updater": {
"CheckingFailed": "检查更新失败(网络错误)",
"CheckUpdate": "检查更新",
"CheckingForUpdate": "检查更新中…",
"DownloadProgress": "更新下载中",
"UpdateError": "更新出错",
"UpdateAvailable": "有新版本可用!",
"UpdateCancelled": "更新取消",
"UpdateDownloaded": "更新已下载",
"UpdateNotAvailable": "目前已是最新版"
},
"Menu": {
"TidGi": "太记",
"TidGiMenuBar": "太记小窗",
"Edit": "编辑",
"View": "查看",
"Find": "查找",
"FindMatches": "个匹配",
"Close": "关闭",
"FindNext": "查找下一个",
"FindPrevious": "查找上一个",
"Home": "首页",
"Back": "返回",
"Forward": "前进",
"SelectPreviousWorkspace": "选择前一个工作区",
"SelectNextWorkspace": "选择下一个工作区",
"Language": "语言/Lang",
"History": "历史",
"Workspaces": "工作区列表",
"CurrentWorkspace": "当前工作区",
"Window": "窗口",
"Help": "帮助",
"ActualSize": "正常大小",
"ZoomIn": "放大",
"ZoomOut": "缩小",
"ReportBugViaGithub": "通过 GitHub 反馈问题...",
"RequestFeatureViaGithub": "通过 GitHub 提新需求...",
"DeveloperToolsActiveWorkspace": "打开当前工作区的开发者工具",
"LearnMore": "了解更多...",
"PrintPage": "打印页面",
"ExportActiveTiddler": "导出当前笔记",
"Wiki": "Wiki",
"ExportWholeWikiHTML": "导出整个Wiki为HTML存入文件夹"
},
"AddWorkspace": {
"AddWorkspace": "添加工作区",
"MainPageTipWithoutSidebar": "<0>使用菜单上的</0><strong> 工作区 → 添加工作区 </strong><0>或</0><strong> 点击此处 </strong><2>来开始使用太微!</2>",
"MainPageTipWithSidebar": "<0>点击侧边栏上的这个 </0><1>+</1><2>(加号按钮)来开始使用太微!</2>",
"NotFilled": "未填",
"AndLinkToMainWorkspace": "并链接到主知识库",
"CreateWiki": "创建WIKI: ",
"ImportWiki": "导入WIKI: ",
"CloneWiki": "导入线上WIKI: ",
"OpenLocalWikiFromHTML": "导入WIKI.HTML",
"LocalWikiHtml": "HTML文件的路径",
"BadWikiHtml": "该HTML文件无法用于创建WIKI",
"ExtractedWikiFolderName": "转换后的WIKI文件夹名称",
"NotLoggedIn": "未登录",
"LogoutToGetStorageServiceToken": "登录在线存储服务以获取最新凭证",
"LogoutGithubAccount": "登出Github账号",
"LoginGithubAccount": "登录Github账号",
"GitToken": "Git Token",
"GitDefaultBranch": "Git默认分支",
"GitRepoUrl": "Git仓库线上网址",
"GitTokenDescription": "用于登录Git的凭证一定时间后会过期",
"GitDefaultBranchDescription": "你的Git的默认分支Github在黑命贵事件后将其从master改为了main",
"GitEmailDescription": "用于Git提交记录的Email用于在Github等服务上统计每日提交量",
"GitUserNameDescription": "用于登录Git的账户名注意是你的仓库网址中你的名字部分",
"SwitchCreateNewOrOpenExisted": "切换创建新的还是打开现有的WIKI",
"ExistedWikiLocation": "现有的WIKI的位置",
"CreateNewWiki": "创建新WIKI",
"OpenLocalWiki": "导入本地WIKI",
"CloneOnlineWiki": "导入线上WIKI",
"MainWorkspace": "主知识库",
"SubWorkspace": "子知识库",
"MainWorkspaceDescription": "包含了TiddlyWiki的配置文件以及发布为博客时的公开内容。",
"SubWorkspaceDescription": "必须依附于一个主知识库可用于存放私有内容。注意两点子知识库不能放在主知识库文件夹内子知识库一般用于同步数据到一个私有的Github仓库内仅本人可读写故仓库地址不能与主知识库一样。\n子知识库通过创建一个到主知识库的软链接快捷方式来生效创建链接后主知识库内便可看到子知识库内的内容了。",
"SyncedWorkspace": "云端同步知识库",
"LocalWorkspace": "本地知识库",
"SyncedWorkspaceDescription": "同步到在线存储服务例如Github需要你登录存储服务或输入登录凭证并有良好的网络连接。可以跨设备同步数据在使用了值得信任的存储服务的情况下数据仍归你所有。而且文件夹被不慎删除后还可以从在线服务重新下载数据到本地。",
"LocalWorkspaceDescription": "仅在本地使用,完全掌控自己的数据。太记会为你创建一个本地的 git 备份系统,让你可以回退到之前的版本,但当文件夹被删除时所有内容还是会丢失。",
"WorkspaceFolder": "工作区文件夹的位置",
"WorkspaceParentFolder": "文件夹所在的父文件夹",
"Choose": "选择",
"WikiServerPort": "WIKI服务器端口号出现冲突再改一般默认即可",
"MainWorkspaceLocation": "主知识库位置",
"SubWorkspaceWillLinkTo": "子知识库将链接到",
"WorkspaceFolderNameToCreate": "即将新建的知识库文件夹名",
"SearchGithubRepoName": "搜索Github仓库名",
"CreatePublicRepository": "创建公开仓库",
"CreatePrivateRepository": "创建私有仓库",
"Reload": "重新加载",
"MainPageReloadTip": "<0><0>请尝试:<1><0>点击下面的 <2>重新加载</2> 按钮,或用快捷键 <5>CMD/Ctrl + R</5> 来刷新页面。</0><1>或者打开 <2>Log文件夹</2> 来看看具体的错误原因。</1><2>最糟糕的情况下也可以复制备份你电脑上的文件夹,右键工作区图标选择删除工作区,然后重新导入电脑上的文件夹(或通过拖入 HTML 导入之前备份的 HTML 版 wiki。</2></1></0></0>",
"Processing": "正在处理...",
"WaitForLogin": "等待登录",
"CanNotLoadList": "无法加载仓库列表,网络不佳",
"OmitMoreResult": "列表仅展示前 {{loadCount}} 个结果",
"CreateLinkFromSubWikiToMainWikiSucceed": "在主Wiki内成功创建子Wiki的快捷方式将文件存入主Wiki内的快捷方式会自动将文件存入子Wiki。",
"CreateLinkFromSubWikiToMainWikiFailed": "无法链接文件夹 \"{{subWikiPath}}\" 到 \"{{mainWikiTiddlersFolderPath}}\"",
"StartUsingTemplateToCreateWiki": "开始用模板创建Wiki",
"PathNotExist": "该路径不存在 \"{{path}}\"",
"WikiTemplateMissing": "Wiki模板缺失 \"{{TIDDLYWIKI_TEMPLATE_FOLDER_PATH}}\"",
"WikiExisted": "Wiki已经存在于该位置 \"{{newWikiPath}}\"",
"CantCreateFolderHere": "无法在该处创建文件夹 \"{{newWikiPath}}\"",
"WikiTemplateCopyCompleted": "模板Wiki复制完毕",
"StartCreatingSubWiki": "开始创建子Wiki",
"SubWikiCreationCompleted": "子Wiki创建完毕",
"ThisPathIsNotAWikiFolder": "该目录不是一个Wiki文件夹 \"{{wikiPath}}\"",
"StartCloningWiki": "开始导入线上Wiki",
"StartCloningSubWiki": "开始导入线上子Wiki",
"StartUpdatingWorkspace": "正在更新工作区",
"WorkspaceUpdated": "工作区更新完毕正在启动Wiki",
"StartLinkingSubWikiToMainWiki": "开始链接子Wiki到父Wiki",
"AddFileSystemPath": "正在为子Wiki添加FileSystemPaths",
"WorkspaceUserName": "工作区编辑者名",
"WorkspaceUserNameDetail": "在 Wiki 中使用的编辑者名,将在创建或编辑 Tiddler 时填入 creator 字段。工作区内设置的编辑者名,将覆盖设置里配的全局的默认编辑者名。这方便你通过创建多个配了不同编辑者名的工作区,在同一个 Wiki 里用不同的身份创建 Tiddler。",
"TagName": "标签名",
"TagNameHelp": "加上此标签的笔记将会自动被放入这个子知识库内(可先不填,之后右键点击这个工作区的图标选择编辑工作区修改)",
"NoGitInfoAlert": "你未选择在线Git仓库地址或未成功登录在线Git存储服务账号可能被墙。点击创建按钮将创建一个不会自动同步到Github的本地仓库请注意。",
"WikiNotStarted": "Wiki 页面未成功启动或未成功加载",
"Advanced": "高级设置"
"Delete": "删除",
"Dialog": {
"CantFindWorkspaceFolderRemoveWorkspace": "无法找到之前还在该处的工作区知识库文件夹!本应存在于此处的知识库文件夹可能被移走了,或该文件夹内没有知识库!是否移除工作区?",
"DoNotCare": "不管",
"FocusedTiddlerNotFoundTitle": "无法查询到当前聚焦的条目",
"FocusedTiddlerNotFoundTitleDetail": "可以到 CPL 安装 FocusedTiddler 插件",
"Later": "稍后",
"MadeWithLove": "<0>有</0><1> ❤ </1><2>的开发者:</2>",
"NeedCorrectTiddlywikiFolderPath": "需要传入正确的路径,而此路径无法被太微识别。",
"PathPassInCantUse": "传入的路径无法使用",
"RemoveWorkspace": "移除工作区",
"ReportBug": "报告错误",
"ReportBugDetail": "如果你看过教程了解操作流程,并仔细读过报错内容并思考,仔细检查了自己的输入觉得没问题,可以点击按钮。",
"RestartAppNow": "现在重启应用",
"RestartMessage": "您需要重新启动本程序才能使此更改生效。",
"RestartWikiNow": "现在重启知识库",
"Restarting": "重启中",
"StorageServiceUserInfoNoFound": "找不到存储备份服务的用户信息",
"StorageServiceUserInfoNoFoundDetail": "似乎你尚未登录存储备份服务,因此此知识库的同步暂时禁用,直到你登录以提供有可用于同步的登录信息。",
"WorkspaceFolderRemoved": "工作区文件夹被移走或该文件夹不是知识库"
},
"EditWorkspace": {
"Path": "Wiki的位置",
"Save": "保存",
"Cancel": "取消",
"HibernateTitle": "开启休眠",
"DisableAudioTitle": "关闭声音",
"DisableNotificationTitle": "关闭提醒",
"HibernateDescription": "在工作区未使用时休眠以节省 CPU 和内存消耗并省电,这会关闭所有自动同步功能,需要手动同步备份数据。",
"DisableAudio": "阻止工作区中的声音播放",
"DisableNotification": "阻止工作区的消息提醒",
"SelectLocal": "选择本地图片...",
"ResetDefaultIcon": "还原默认图标",
"NoRevert": "注意!这个操作无法撤销!",
"URL": "本地服务器地址",
"LastVisitState": "上次访问的页面",
"Port": "本地服务器端口",
"PathDescription": "本地维基文件夹的地址",
"SyncOnInterval": "定时自动同步备份",
"SyncOnIntervalDescription": "开启后会根据全局设置里的时间间隔自动同步并且依然会在启动时自动同步点击按钮也可以手动同步。同步云端前会自动先把数据备份到本地Git。如果关闭则只有在应用程序打开时会有一次自动同步还有当用户通过点击维基中的同步按钮手动触发同步。",
"AddExcludedPlugins": "输入希望忽略的插件名",
"AddExcludedPluginsDescription": "可搜索当前知识库中已安装的插件,或输入任意插件名。",
"AppearanceOptions": "工作区外貌设置",
"BackupOnInterval": "定时自动备份",
"BackupOnIntervalDescription": "开启时每隔一段时间全局设置里的时间间隔会自动用本地Git备份数据一次这样即使没有配置云端同步地址也会自动备份到本地。",
"SyncOnStartup": "启动时自动同步",
"SyncOnStartupDescription": "在应用冷启动时自动同步一次。",
"Name": "工作区名",
"NameDescription": "工作区的名字将显示在侧边栏上可以与工作区Git仓库的实际文件夹名不同",
"WikiRootTiddler": "知识库根条目",
"WikiRootTiddlerDescription": "Wiki 的根条目root-tiddler决定了系统的核心行为修改前请阅读官方文档来了解",
"WikiRootTiddlerItems": {
"all": "全量加载",
"lazy-images": "按需加载图片",
"lazy-all": "按需加载图片和文本"
},
"ReadOnlyMode": "只读模式",
"ReadOnlyModeDescription": "可用于配合内网穿透让太记作为服务器程序部署博客。打开后将只能通过直接改文件的方式修改知识库内容包括git同步网页上将不能修改内容但任何人都可以访问。",
"TokenAuth": "凭证鉴权",
"TokenAuthDescription": "开启后HTTP请求中需要带上凭证才能读写知识库内容防止同一局域网下其他人访问笔记提高服务器的安全性。无法与只读模式同时开启。",
"TokenAuthAutoFillUserNameDescription": "此功能需要在全局设置或工作区设置里填写用户名,不然不会生效。若你未填,将自动在工作区设置里填一个默认值,你可自行修改。",
"TokenAuthCurrentHeader": "凭证鉴权当前请求头",
"ServerOptions": "博客和服务器设置",
"Cancel": "取消",
"ClickToExpand": "点击展开",
"DisableAudio": "阻止工作区中的声音播放",
"DisableAudioTitle": "关闭声音",
"DisableNotification": "阻止工作区的消息提醒",
"DisableNotificationTitle": "关闭提醒",
"EnableHTTPAPI": "启用 HTTP API",
"EnableHTTPAPIDescription": "允许第三方程序如太记移动端、太记搜藏-剪藏插件等等通过 HTTP 网络接口读取和修改你的笔记。",
"EnableHTTPS": "启用HTTPS",
"EnableHTTPSDescription": "提供安全的TLS加密访问需要你有自己的HTTPS证书可以从域名提供商那下载也可以搜索免费的HTTPS证书申请方式。",
"HTTPSUploadCert": "添加Cert文件",
"HTTPSPickCert": "选择Cert文件路径",
"ExcludedPlugins": "需忽略的插件",
"ExcludedPluginsDescription": "在只读模式启动知识库作为博客时,你可能希望不加载一些编辑相关的插件以减小初次加载的网页大小,例如 $:/plugins/tiddlywiki/codemirror 等,毕竟加载的博客不需要这些编辑功能。",
"Generate": "生成",
"HTTPSCertPath": "Cert文件路径",
"HTTPSCertPathDescription": "后缀为 .crt 的证书文件的所在位置,一般以 xxx_public.crt 结尾。",
"HTTPSKeyPathDescription": "后缀为 .key 的私钥文件的所在位置。",
"HTTPSUploadKey": "添加Key文件",
"HTTPSKeyPath": "Key文件路径",
"HTTPSKeyPathDescription": "后缀为 .key 的私钥文件的所在位置。",
"HTTPSPickCert": "选择Cert文件路径",
"HTTPSPickKey": "选择Key文件路径",
"UploadOrSelectPathDescription": "点击上传按钮将文件提交给太记保管,也可以点击选择路径按钮从你保管的位置选取文件。",
"ExcludedPlugins": "需忽略的插件",
"AddExcludedPlugins": "输入希望忽略的插件名",
"AddExcludedPluginsDescription": "可搜索当前Wiki中已安装的插件或输入任意插件名。",
"ExcludedPluginsDescription": "在只读模式启动Wiki作为博客时你可能希望不加载一些编辑相关的插件以减小初次加载的网页大小例如 $:/plugins/tiddlywiki/codemirror 等,毕竟加载的博客不需要这些编辑功能。",
"LastNodeJSArgv": "最近一次启动的命令行参数",
"TokenAuthCurrentToken": "当前可用的鉴权凭证",
"TokenAuthCurrentTokenEmptyText": "点击生成按钮来生成新的凭证",
"TokenAuthCurrentTokenDescription": "机密信息,泄露给敌意实体后需要重新生成,重新生成后需要为连接的第三方应用更新凭证",
"Generate": "生成",
"ClickToExpand": "点击展开",
"MainWorkspacePath": "主工作区路径",
"HTTPSUploadCert": "添加Cert文件",
"HTTPSUploadKey": "添加Key文件",
"HibernateDescription": "在工作区未使用时休眠以节省 CPU 和内存消耗并省电,这会关闭所有自动同步功能,需要手动同步备份数据。",
"HibernateTitle": "开启休眠",
"IsSubWorkspace": "是子工作区",
"AppearanceOptions": "工作区外貌设置",
"LastNodeJSArgv": "最近一次启动的命令行参数",
"LastVisitState": "上次访问的页面",
"MainWorkspacePath": "主工作区路径",
"MiscOptions": "杂项设置",
"Name": "工作区名",
"NameDescription": "工作区的名字将显示在侧边栏上可以与工作区Git仓库的实际文件夹名不同",
"NoRevert": "注意!这个操作无法撤销!",
"Path": "知识库的位置",
"PathDescription": "本地知识库文件夹的地址",
"Port": "本地服务器端口",
"ReadOnlyMode": "只读模式",
"ReadOnlyModeDescription": "可用于配合内网穿透让太记作为服务器程序部署博客。打开后将只能通过直接改文件的方式修改知识库内容包括git同步网页上将不能修改内容但任何人都可以访问。",
"ResetDefaultIcon": "还原默认图标",
"Save": "保存",
"SaveAndSyncOptions": "保存和同步",
"MiscOptions": "杂项设置"
"SelectLocal": "选择本地图片...",
"ServerOptions": "博客和服务器设置",
"SyncOnInterval": "定时自动同步备份",
"SyncOnIntervalDescription": "开启后会根据全局设置里的时间间隔自动同步并且依然会在启动时自动同步点击按钮也可以手动同步。同步云端前会自动先把数据备份到本地Git。如果关闭则只有在应用程序打开时会有一次自动同步还有当用户通过点击知识库中的同步按钮手动触发同步。",
"SyncOnStartup": "启动时自动同步",
"SyncOnStartupDescription": "在应用冷启动时自动同步一次。",
"TokenAuth": "凭证鉴权",
"TokenAuthAutoFillUserNameDescription": "此功能需要在全局设置或工作区设置里填写用户名,不然不会生效。若你未填,将自动在工作区设置里填一个默认值,你可自行修改。",
"TokenAuthCurrentHeader": "凭证鉴权当前请求头",
"TokenAuthCurrentToken": "当前可用的鉴权凭证",
"TokenAuthCurrentTokenDescription": "机密信息,泄露给敌意实体后需要重新生成,重新生成后需要为连接的第三方应用更新凭证",
"TokenAuthCurrentTokenEmptyText": "点击生成按钮来生成新的凭证",
"TokenAuthDescription": "开启后HTTP请求中需要带上凭证才能读写知识库内容防止同一局域网下其他人访问笔记提高服务器的安全性。无法与只读模式同时开启。",
"URL": "本地服务器地址",
"UploadOrSelectPathDescription": "点击上传按钮将文件提交给太记保管,也可以点击选择路径按钮从你保管的位置选取文件。",
"WikiRootTiddler": "知识库根条目",
"WikiRootTiddlerDescription": "知识库的根条目root-tiddler决定了系统的核心行为修改前请阅读官方文档来了解",
"TiddlyWiki": "太微",
"WikiRootTiddlerItems": {
}
},
"Scripting": {
"ExecutingScript": "正在执行脚本"
"Error": {
"ALreadyExistErrorDescription": "当前路径已有文件夹,新的知识库无法在此新建。",
"AlreadyExistError": "该处已被文件夹占用",
"CopyWikiTemplateError": "E-3 复制知识库模板错误",
"CopyWikiTemplateErrorDescription": "E-3 尝试把最新知识库模板复制或覆盖到对应位置,但是失败了,请根据提示检查你的输入。",
"DoubleWikiInstanceError": "E-4 重复启动知识库错误",
"DoubleWikiInstanceErrorDescription": "E-4 你启动了同一个知识库两次,这可能是程序 bug 导致的。",
"HTMLCanNotLoadError": "提供的 HTML 文件路径无法使用。",
"HTMLCanNotLoadErrorDescription": "请输入指向可用的 HTML 文件的路径。",
"InitWikiGitError": "E-1 笔记仓库初始化失败错误",
"InitWikiGitErrorDescription": "E-1 新笔记仓库所用的模板复制失败或者笔记仓库的git初始化失败这应该是个bug。",
"InitWikiGitRevertError": "E-2 笔记仓库初始化失败且撤销失败错误",
"InitWikiGitRevertErrorDescription": "E-2 不仅笔记仓库初始化失败了,而且撤销也失败了,这是个严重问题,需要你手动清理该位置生成的新文件夹。",
"InitWikiGitSyncedWikiNoGitUserInfoError": "E-6 笔记仓库初始化失败因为没有提供Git信息错误",
"InitWikiGitSyncedWikiNoGitUserInfoErrorDescription": "E-6 初始化同步到云端的笔记仓库需要你选择一个云端的 git 仓库地址,还有提供相应云服务的认证凭证,然而目前没有获得这些信息。",
"InsertMenuAfterSubMenuIndexError": "E-5 插入目录模板到现有目录后错误",
"InsertMenuAfterSubMenuIndexErrorDescription": "E-5 你尝试插入目录,且 afterSubMenu \"{{afterSubMenu}}\" 在目录 menuID \"{{menuID}}\" 内,但我们无法在目录 \"{{menu}}\" 里找到它,请用正确的 menuID 指定一个目录。",
"MainWindowMissing": "E-7 程序无法获取主窗口信息,无法正常运行。",
"SubWikiSMainWikiNotExistError": "子知识库所附着的主知识库不存在",
"SubWikiSMainWikiNotExistErrorDescription": "子知识库在创建时必须选择一个附着到的主知识库,但是现在这个子知识库所应该附着的主知识库找不到了,无法附着。",
"ViewLoadUrlError": "E-9 网页加载失败错误",
"ViewLoadUrlErrorDescription": "E-9 工作区对应的知识库網页加载失败了,但即将重试",
"WikiRuntimeError": "E-13 知识库运行时有错误",
"WikiRuntimeErrorDescription": "E-13 知识库运行时有错误,原因请查看 log 文件,并上传提交 issue 以便修复。",
"WorkspaceFailedToLoadError": "E-8 工作区加载失败错误",
"WorkspaceFailedToLoadErrorDescription": "E-8 工作区对应的知识库网页加载失败了,原因有很多,但基本是因为程序 Bug",
"ZxInitializationError": "E-12 Zx 代码执行服务初始化错误",
"ZxInitializationErrorDescription": "E-12 Zx 代码执行服务初始化错误,原因请查看 log 文件,并上传提交 issue 以便修复。",
"ZxInitializationRetryFailedError": "E-11 Zx 代码执行服务初始化重试错误",
"ZxInitializationRetryFailedErrorDescription": "E-11 Zx 代码执行服务初始化出错,重试多次依然失败错误,请上传 log 文件提交 issue 报告错误以便修复。",
"ZxNotInitializedError": "E-10 Zx 代码执行服务未初始化错误",
"ZxNotInitializedErrorDescription": "E-10 Zx 代码执行服务未成功初始化,将自动尝试初始化。"
},
"ErrorMessage": "报错信息",
"Help": {
"Alternatives": "其它源",
"Contribute": "向此网站贡献内容",
"Description": "点击「打开」按钮会在新窗口打开页面页面初次打开需要5秒到1分钟时间从互联网加载断网不可用。你可以随意修改打开的页面的内容作为沙箱游乐场随意尝试学到的功能如果想保存修改的结果可以点击太微的保存按钮保存为HTML格式的单页知识库。",
"List": "帮助列表",
"Tags": {
}
},
"LOG": {
"CommitBackupMessage": "使用太记桌面版备份",
"CommitMessage": "使用太记桌面版同步"
},
"LinOnetwo": "林一二",
"Loading": "加载中",
"Log": {
"StartGitInitialization": "开始初始化本地 Git 仓库",
"StartConfiguringGithubRemoteRepository": "仓库初始化完毕,开始配置 Git 云端仓库",
"StartBackupToGithubRemote": "正在将 Wiki 所在的本地 Git 备份到 Github 云端仓库,需要的时间取决于网速,请耐心等待",
"GitRepositoryConfigurateFailed": "Git 仓库配置失败,详见错误日志",
"GitRepositoryConfigurationFinished": "Git 仓库配置完毕",
"SynchronizationFailed": "同步失败!你需要用 Github Desktop 等工具检查当前 Git 仓库的状态。失败可能是网络原因导致的,如果的确如此,可在调整网络后重试。",
"NotAGitRepository": "不是一个 Git 仓库",
"CantSynchronizeAndSyncScriptIsInDeadLoop": "无法同步,而且同步脚本陷入死循环",
"AddComplete": "添加(Git Add)成功",
"AddingFiles": "开始添加(Git Add)待备份的文件",
"CantForcePullError": "强制拉取失败,可能仓库处在特殊状态",
"CantSyncGitNotInitialized": "无法同步,这个文件夹没有初始化为 Git 仓库",
"CantSyncInSpecialGitStateAutoFixFailed": "无法同步,这个文件夹处在特殊状态,不能直接进行同步,已尝试自动修复,但还是出现错误,请先解决所有冲突(例如使用 VSCode 打开),如果还不行,请尝试用专业 Git 工具Source Tree, GitKraken解决问题",
"CantSyncInSpecialGitStateAutoFixSucceed": "这个文件夹处在特殊状态,本来不能直接进行同步,但已自动修复",
"PrepareSync": "准备同步,使用登录的作者信息",
"CommitComplete": "本地提交(Commit)完成",
"CantSyncGitNotInitialized": "无法同步,这个文件夹没有初始化为 Git 仓库",
"HaveThingsToCommit": "有需要提交(Commit)的内容,正在自动提交",
"PreparingUserInfo": "正在配置身份信息",
"GitTokenMissing": "Git 凭证(Token)缺失",
"GitTokenExpireOrWrong": "Git 凭证(Token)已过期,需要重新登录一次,或凭证与用户名不对应",
"AddingFiles": "开始添加(Git Add)待备份的文件",
"AddComplete": "添加(Git Add)成功",
"FetchingData": "正在拉取云端数据,以便进行数据比对",
"NoNeedToSync": "无需同步,本地状态和云端一致",
"LocalAheadStartUpload": "本地状态超前于云端,开始上传",
"GitPushFailed": "Git上传的结果不佳这通常意味着有网络问题",
"LocalStateBehindSync": "本地状态落后于云端,开始合并云端数据",
"GitMergeFailed": "Git合并的结果不佳可能合并策略有漏洞",
"CantSynchronizeAndSyncScriptIsInDeadLoop": "无法同步,而且同步脚本陷入死循环",
"CheckingLocalGitRepoSanity": "正在检测本地 Git 仓库是否正确地初始化了",
"CheckingLocalSyncState": "正在检测本地状态是否需要同步到云端",
"LocalStateDivergeRebase": "本地状态与云端有分歧,开始变基(Rebase)",
"RebaseSucceed": "变基(Rebase)成功,开始上传",
"CheckingRebaseStatus": "正在分析变基(Rebase)的处理方案",
"RebaseConflictNeedsResolve": "变基(Rebase)时发现冲突,需要解决冲突",
"SyncFailedSystemError": "同步失败,同步系统可能出现问题",
"PerformLastCheckBeforeSynchronizationFinish": "进行同步结束前最后的检查",
"SynchronizationFinish": "同步完成",
"InitializeWikiGit": "正在初始化 Wiki 和 Git",
"InitializeWorkspaceView": "正在初始化工作区和浏览器窗口,并加载内容,请耐心等待",
"InitializeWorkspaceViewDone": "创建成功,即将加载内容",
"PrepareCloneOnlineWiki": "准备导入线上 Wiki",
"StartFetchingFromGithubRemote": "正在拉取Github远端仓库的数据需要的时间取决于网速和仓库大小请耐心等待",
"UsingUrlAndUsername": "使用 Git Url {{githubRepoUrl}} 和用户名 {{username}} 和 accessToken {{accessToken}}",
"CommitComplete": "本地提交(Commit)完成",
"FailedToOpenDirectory": "无法打开文件夹 {{path}} {{errorMessage}}",
"FailedToOpenFile": "无法打开文件 {{path}} {{errorMessage}}",
"CantForcePullError": "强制拉取失败,可能仓库处在特殊状态",
"StartForcePull": "开始强制拉取远端内容,将完全覆盖本地",
"FetchingData": "正在拉取云端数据,以便进行数据比对",
"FinishForcePull": "强制拉取完成",
"GitMergeFailed": "Git合并的结果不佳可能合并策略有漏洞",
"GitPushFailed": "Git上传的结果不佳这通常意味着有网络问题",
"GitRepositoryConfigurationFinished": "Git 仓库配置完毕",
"GitTokenExpireOrWrong": "Git 凭证(Token)已过期,需要重新登录一次,或凭证与用户名不对应",
"GitTokenMissing": "Git 凭证(Token)缺失",
"HaveThingsToCommit": "有需要提交(Commit)的内容,正在自动提交",
"InitializeWikiGit": "正在初始化知识库和 Git",
"InitializeWorkspaceView": "正在初始化工作区和浏览器窗口,并加载内容,请耐心等待",
"InitializeWorkspaceViewDone": "创建成功,即将加载内容",
"LocalAheadStartUpload": "本地状态超前于云端,开始上传",
"LocalStateBehindSync": "本地状态落后于云端,开始合并云端数据",
"LocalStateDivergeRebase": "本地状态与云端有分歧,开始变基(Rebase)",
"NoNeedToSync": "无需同步,本地状态和云端一致",
"PerformLastCheckBeforeSynchronizationFinish": "进行同步结束前最后的检查",
"PrepareCloneOnlineWiki": "准备导入线上知识库",
"PrepareSync": "准备同步,使用登录的作者信息",
"PreparingUserInfo": "正在配置身份信息",
"RebaseConflictNeedsResolve": "变基(Rebase)时发现冲突,需要解决冲突",
"RebaseSucceed": "变基(Rebase)成功,开始上传",
"SkipForcePull": "跳过强制拉取,远端没有更新",
"StartBackupToGithubRemote": "正在将知识库所在的本地 Git 备份到 Github 云端仓库,需要的时间取决于网速,请耐心等待",
"StartConfiguringGithubRemoteRepository": "仓库初始化完毕,开始配置 Git 云端仓库",
"StartFetchingFromGithubRemote": "正在拉取Github远端仓库的数据需要的时间取决于网速和仓库大小请耐心等待",
"StartForcePull": "开始强制拉取远端内容,将完全覆盖本地",
"StartGitInitialization": "开始初始化本地 Git 仓库",
"StartResettingLocalToRemote": "开始清空本地并用远端内容覆盖",
"FinishForcePull": "强制拉取完成"
"SyncFailedSystemError": "同步失败,同步系统可能出现问题",
"SynchronizationFailed": "同步失败!你需要用 Github Desktop 等工具检查当前 Git 仓库的状态。失败可能是网络原因导致的,如果的确如此,可在调整网络后重试。",
"SynchronizationFinish": "同步完成"
},
"Dialog": {
"NeedCorrectTiddlywikiFolderPath": "需要传入正确的路径,而此路径无法被 TiddlyWiki 识别。",
"CantFindWorkspaceFolderRemoveWorkspace": "无法找到之前还在该处的工作区Wiki文件夹本应存在于此处的WIki文件夹可能被移走了或该文件夹内没有Wiki是否移除工作区",
"WorkspaceFolderRemoved": "工作区文件夹被移走或该文件夹不是Wiki",
"RemoveWorkspace": "移除工作区",
"DoNotCare": "不管",
"PathPassInCantUse": "传入的路径无法使用",
"StorageServiceUserInfoNoFound": "找不到存储备份服务的用户信息",
"StorageServiceUserInfoNoFoundDetail": "似乎你尚未登录存储备份服务,因此此 Wiki 的同步暂时禁用,直到你登录以提供有可用于同步的登录信息。",
"Later": "稍后",
"RestartMessage": "您需要重新启动本程序才能使此更改生效。",
"RestartAppNow": "现在重启应用",
"RestartWikiNow": "现在重启知识库",
"Restarting": "重启中",
"ReportBug": "报告错误",
"ReportBugDetail": "如果你看过教程了解操作流程,并仔细读过报错内容并思考,仔细检查了自己的输入觉得没问题,可以点击按钮。",
"MadeWithLove": "<0>有</0><1> ❤ </1><2>的开发者:</2>",
"FocusedTiddlerNotFoundTitle": "无法查询到当前聚焦的条目",
"FocusedTiddlerNotFoundTitleDetail": "可以到 CPL 安装 FocusedTiddler 插件"
"Menu": {
"ActualSize": "正常大小",
"Close": "关闭",
"CurrentWorkspace": "当前工作区",
"DeveloperToolsActiveWorkspace": "打开当前工作区的开发者工具",
"Edit": "编辑",
"ExportActiveTiddler": "导出当前笔记",
"ExportWholeWikiHTML": "导出整个知识库为HTML存入文件夹",
"Find": "查找",
"FindMatches": "个匹配",
"FindNext": "查找下一个",
"FindPrevious": "查找上一个",
"Help": "帮助",
"History": "历史",
"Home": "首页",
"Language": "语言/Lang",
"LearnMore": "了解更多...",
"PrintPage": "打印页面",
"ReportBugViaGithub": "通过 GitHub 反馈问题...",
"RequestFeatureViaGithub": "通过 GitHub 提新需求...",
"SelectNextWorkspace": "选择下一个工作区",
"SelectPreviousWorkspace": "选择前一个工作区",
"TidGi": "太记",
"TidGiMenuBar": "太记小窗",
"View": "查看",
"Wiki": "知识库",
"Window": "窗口",
"Workspaces": "工作区列表",
"ZoomIn": "放大",
"ZoomOut": "缩小"
},
"Cancel": "取消",
"No": "不了",
"Open": "打开",
"Preference": {
"ClearBrowsingData": "清空浏览器数据不影响Git内容",
"DefaultUserNameDetail": "在 Wiki 中默认使用的编辑者名,将在创建或编辑 Tiddler 时填入 creator 字段。可以被工作区内设置的编辑者名覆盖。",
"DefaultUserName": "默认编辑者名",
"Token": "Git身份凭证",
"TokenDescription": "用于向Git服务器验证身份并同步内容的凭证可通过登录在线存储服务如Github来取得也可以手动获取「Personal Access Token」后填到这里。",
"Sync": "同步和备份",
"SyncInterval": "同步/备份间隔",
"SyncIntervalDescription": "每经过这段长度的时间后,就会自动开始备份到 Github如果工作区是本地工作区则会创建本地备份重启后生效",
"General": "界面和交互",
"ShowSideBarDetail": "侧边栏让你可以在工作区之间快速切换",
"ShowSideBarText": "展示侧边栏上按钮的文本",
"OpenLogFolder": "打开Log文件夹",
"OpenLogFolderDetail": "上报问题时,请打开日期最新的一个 .log 文件,将其内容发送给开发者,或黏贴到 pastebin.com 后将 URL 黏贴到 Github Issue 里",
"OpenMetaDataFolder": "打开太记工作区元信息文件夹",
"OpenMetaDataFolderDetail": "太微的数据和太记的工作区数据是分开存放的,太记的数据包含工作区的设置等,它们以 JSON 形式存放在这个文件夹里。",
"AlwaysOnTop": "保持窗口在其他窗口上方",
"AlwaysOnTopDetail": "让太记的主窗口永远保持在其它窗口上方,不会被其他窗口覆盖",
"AntiAntiLeech": "有的网站做了防盗链,会阻止某些图片在你的知识库上显示,我们通过模拟访问该网站的请求头来绕过这种限制。",
"AskDownloadLocation": "下载前询问每个文件的保存位置",
"AttachToMenuBar": "附加到菜单栏",
"AttachToMenuBarShowSidebar": "附加到菜单栏的窗口包含侧边栏",
"AttachToMenuBarShowSidebarTip": "一般太记小窗仅用于快速查看当前工作区,所以默认与主窗口工作区同步,不需要侧边栏,默认隐藏侧边栏。",
"AttachToMenuBarTip": "创建一个点击菜单栏/任务栏图标会弹出的小太记窗口。提示:右键单击小图标以访问上下文菜单。",
"AttachToTaskbar": "附加到任务栏",
"NoAttach": "恢复窗口模式",
"ToggleMenuBar": "切换显隐菜单栏",
"HideMenuBar": "隐藏菜单栏",
"HideMenuBarDetail": "按下 Alt + M 可以显示被隐藏的菜单栏",
"ShowNavigationBar": "显示导航栏",
"ShowNavigationBarDetail": "顶部的导航栏可以前进,后退,返回首页,重新加载,以及显示当前网页所在的 URL",
"ShowTitleBar": "显示标题栏",
"HideTitleBar": "隐藏标题栏",
"ShowTitleBarDetail": "在标题栏上会显示当前页面的标题",
"DarkTheme": "黑暗主题",
"LightTheme": "亮色主题",
"ShowSideBar": "显示侧边栏",
"HideSideBar": "隐藏侧边栏",
"ShowSideBarIcon": "展示侧边栏工作区图标",
"HideSideBarIconDetail": "隐藏图标只显示工作区的名字,让工作区列表更紧凑",
"SystemDefaultTheme": "系统默认主题色",
"Theme": "主题色",
"Reset": "你确定吗?所有首选项都将恢复为其原始默认值。浏览数据不会受到影响。此操作无法撤消。",
"ResetNow": "立即重置",
"ClearBrowsingDataMessage": "你确定吗?所有浏览数据将被清除。此操作无法撤消。",
"WikiMetaData": "Wiki元信息",
"WikiMetaDataDescription": "配置Wiki的启动参数",
"FriendLinks": "友链",
"Miscellaneous": "其他设置",
"Translatium": "翻译素APP",
"WebSite": "官网",
"ReceivePreReleaseUpdates": "接收预发布更新",
"SwipeWithThreeFingersToNavigate": "用三根手指轻扫来前进后退",
"TestNotification": "测试通知功能",
"Updates": "更新",
"WebCatalog": "网站目录App",
"WebCatalogEngineIntro": "「网站目录App」是TidGi的最初代码的来源我们重用了来自开源的「网站目录App」的许多重要代码这要感谢「网站目录App」及其作者 Quang Lam",
"WebCatalogIntro": "神奇地将任何网站变成跨平台的应用程序。\n让你更加高效地工作而无需在浏览器上来回切换浏览器Tab。",
"hardwareAcceleration": "使用硬件加速",
"Languages": "语言/Lang",
"Performance": "性能",
"PrivacyAndSecurity": "隐私和安全",
"AskDownloadLocation": "下载前询问每个文件的保存位置",
"AttachToTaskbarShowSidebar": "附加到任务栏的窗口包含侧边栏",
"ChooseLanguage": "选择语言 Choose Language",
"ClearBrowsingData": "清空浏览器数据不影响Git内容",
"ClearBrowsingDataDescription": "清除Cookie、缓存等",
"ClearBrowsingDataMessage": "你确定吗?所有浏览数据将被清除。此操作无法撤消。",
"ConfirmDelete": "确认删除",
"ConfirmDeleteExternalApiDatabase": "确定要删除包含外部 API Debug 信息的数据库吗?此操作无法撤销。",
"DarkTheme": "黑暗主题",
"DefaultUserName": "默认编辑者名",
"DefaultUserNameDetail": "在知识库中默认使用的编辑者名,将在创建或编辑条目时填入 creator 字段。可以被工作区内设置的编辑者名覆盖。",
"DeleteExternalApiDatabase": "删除外部 API 数据库",
"DeveloperTools": "开发者工具",
"DownloadLocation": "下载位置",
"Downloads": "下载",
"HibernateAllUnusedWorkspaces": "在程序启动时休眠所有未使用的工作区",
"HibernateAllUnusedWorkspacesDescription": "启动时休眠所有工作区,但上次关闭前最后使用的活动工作区除外。",
"IgnoreCertificateErrors": "忽略网络证书错误",
"ItIsWorking": "好使的!",
"Network": "网络",
"AntiAntiLeech": "有的网站做了防盗链会阻止某些图片在你的Wiki上显示我们通过模拟访问该网站的请求头来绕过这种限制。",
"DisableAntiAntiLeech": "禁用反防盗链",
"DisableAntiAntiLeechDetail": "勾选以完全禁用反防盗链功能",
"DisableAntiAntiLeechForUrls": "为以下网址禁用反防盗链",
"DisableAntiAntiLeechForUrlsDetail": "输入每行一个网址,单独为这些网址禁用反防盗链功能,因为该功能可能会导致一些带有反反防盗链功能的网站无法加载图片。",
"DownloadLocation": "下载位置",
"Downloads": "下载",
"ExternalApiDatabaseDescription": "包含外部 API Debug 信息的数据库,占用空间为 {{size}}",
"FriendLinks": "友链",
"General": "界面和交互",
"HibernateAllUnusedWorkspaces": "在程序启动时休眠所有未使用的工作区",
"HibernateAllUnusedWorkspacesDescription": "启动时休眠所有工作区,但上次关闭前最后使用的活动工作区除外。",
"HideMenuBar": "隐藏菜单栏",
"HideMenuBarDetail": "按下 Alt + M 可以显示被隐藏的菜单栏",
"HideSideBar": "隐藏侧边栏",
"HideSideBarIconDetail": "隐藏图标只显示工作区的名字,让工作区列表更紧凑",
"HideTitleBar": "隐藏标题栏",
"HowToEnableNotifications": "<0>TidGi支持原生通知功能。但在某些情况下要接收通知您需要手动配置一些Web应用设置。</0><1>了解详情</1><2>。</2>",
"IgnoreCertificateErrors": "忽略网络证书错误",
"IgnoreCertificateErrorsDescription": "<0>不建议。</0><1>了解详情</1>。",
"ItIsWorking": "好使的!",
"Languages": "语言/Lang",
"LightTheme": "亮色主题",
"MenubarAlwaysOnTop": "保持菜单栏小窗口在其他窗口上方",
"MenubarAlwaysOnTopDetail": "让太记的菜单栏小窗口永远保持在其它窗口上方,不会被其他窗口覆盖",
"Miscellaneous": "其他设置",
"MoreWorkspaceSyncSettings": "更多工作区同步设置",
"MoreWorkspaceSyncSettingsDescription": "请右键工作区图标,点右键菜单里的「编辑工作区」来打开工作区设置,在里面配各个工作区的同步设置。",
"Network": "网络",
"Notifications": "通知",
"NotificationsDetail": "设置通知暂停时间",
"NotificationsDisableSchedule": "按时间自动禁用通知:",
"NotificationsMuteAudio": "暂停通知时也同时静音工作区",
"OpenAtLogin": "开机自启",
"OpenAtLoginMinimized": "开机自启并最小化(MacOS)",
"RememberLastVisitState": "记住上次访问的页面,恢复打开时的上次访问状态",
"RestartToApplyUpdates": "重新启动以使改动生效",
"RestorePreferences": "将所有设置都恢复为其原始默认值",
"ShareBrowsingData": "在工作区之间共享浏览器数据cookies、缓存等关闭后可以每个工作区登不同的第三方服务账号。",
"SpellCheck": "拼写检查",
"SpellCheckLanguages": "首选拼写检查语言",
"Support": "支持",
"TiddlyWiki": "太微TiddlyWiki",
"System": "系统",
"TranslatiumIntro": "像外语系大佬一样翻译任何语言",
"HowToEnableNotifications": "<0>TidGi支持原生通知功能。但在某些情况下要接收通知您需要手动配置一些Web应用设置。</0><1>了解详情</1><2>。</2>",
"IgnoreCertificateErrorsDescription": "<0>不建议。</0><1>了解详情</1>。",
"SwipeWithThreeFingersToNavigateDescription": "使用3个指手势在页面之间导航。向左轻扫可返回向右轻扫可前进。<br/>要启用它,还需要更改<3>macOS首选项 → 触控板 → 更多手势 → 在页面间轻扫</3>到<5>用三个手指轻扫</5>或<7>用两个或三个手指轻扫。</7>",
"TestNotificationDescription": "<0>如果通知未显示,请确保在<1>macOS首选项 → 通知 → TidGi中启用通知</1></0>",
"RequireRestart": "需要重启",
"MenubarAlwaysOnTop": "保持菜单栏小窗口在其他窗口上方",
"MenubarAlwaysOnTopDetail": "让太记的菜单栏小窗口永远保持在其它窗口上方,不会被其他窗口覆盖",
"AlwaysOnTop": "保持窗口在其他窗口上方",
"AlwaysOnTopDetail": "让太记的主窗口永远保持在其它窗口上方,不会被其他窗口覆盖",
"ChooseLanguage": "选择语言 Choose Language",
"SyncBeforeShutdown": "在关机前自动同步",
"SyncBeforeShutdownDescription": "关电脑前自动同步数据注意手动退出应用不会触发同步以防应用出错时将错误数据同步上去。Windows 系统不支持此功能。",
"MoreWorkspaceSyncSettings": "更多工作区同步设置",
"MoreWorkspaceSyncSettingsDescription": "请右键工作区图标,点右键菜单里的「编辑工作区」来打开工作区设置,在里面配各个工作区的同步设置。",
"SyncOnlyWhenNoDraft": "在没有草稿时才同步",
"SyncOnlyWhenNoDraftDescription": "在同步前检查有没有草稿或处于所见即所得编辑状态的条目,如果有则本次不同步,防止将草稿同步到你的博客里。(对关机前自动同步无效,毕竟你很可能希望将草稿从一台电脑上带到另一台电脑上继续编辑)",
"OpenLogFolder": "打开Log文件夹",
"OpenLogFolderDetail": "上报问题时,请打开日期最新的一个 .log 文件,将其内容发送给开发者,或黏贴到 pastebin.com 后将 URL 黏贴到 Github Issue 里",
"OpenMetaDataFolder": "打开太记工作区元信息文件夹",
"OpenMetaDataFolderDetail": "太微的数据和太记的工作区数据是分开存放的,太记的数据包含工作区的设置等,它们以 JSON 形式存放在这个文件夹里。",
"OpenV8CacheFolder": "打开V8缓存文件夹",
"OpenV8CacheFolderDetail": "V8缓存文件夹存有加速应用启动的快取文件",
"Performance": "性能",
"PrivacyAndSecurity": "隐私和安全",
"ReceivePreReleaseUpdates": "接收预发布更新",
"RememberLastVisitState": "记住上次访问的页面,恢复打开时的上次访问状态",
"RequireRestart": "需要重启",
"Reset": "你确定吗?所有首选项都将恢复为其原始默认值。浏览数据不会受到影响。此操作无法撤消。",
"ResetNow": "立即重置",
"RestorePreferences": "将所有设置都恢复为其原始默认值",
"RunOnBackground": "保持后台运行",
"RunOnBackgroundDetail": "在窗口关闭时不退出,继续保持后台运行。再次打开应用时快速还原窗口。",
"RunOnBackgroundDetailNotMac": "建议开启太记小窗,以便通过菜单栏/任务栏图标重新打开窗口。",
"AttachToTaskbarShowSidebar": "附加到任务栏的窗口包含侧边栏",
"AttachToMenuBarShowSidebar": "附加到菜单栏的窗口包含侧边栏",
"AttachToMenuBarShowSidebarTip": "一般太记小窗仅用于快速查看当前工作区,所以默认与主窗口工作区同步,不需要侧边栏,默认隐藏侧边栏。"
"Search": "搜索和嵌入",
"SearchEmbeddingDelete": "删除",
"SearchEmbeddingDeleteConfirm": "确定要删除工作区\"{{workspaceName}}\"的所有向量嵌入吗?此操作无法撤销。",
"SearchEmbeddingDeleteError": "删除嵌入失败:{{error}}",
"SearchEmbeddingGenerate": "生成嵌入",
"SearchEmbeddingGenerating": "生成中...",
"SearchEmbeddingLastUpdated": "最后更新:{{time}}",
"SearchEmbeddingNoAIConfigError": "请先在外部API部分配置AI API设置。",
"SearchEmbeddingStatusCompleted": "{{totalNotes}}个笔记的{{totalEmbeddings}}个嵌入",
"SearchEmbeddingStatusError": "错误:{{error}}",
"SearchEmbeddingStatusGenerating": "生成中... ({{completed}}/{{total}})",
"SearchEmbeddingStatusIdle": "未生成嵌入",
"SearchEmbeddingUpdate": "更新嵌入",
"SearchNoWorkspaces": "未找到工作区",
"ShareBrowsingData": "在工作区之间共享浏览器数据cookies、缓存等关闭后可以每个工作区登不同的第三方服务账号。",
"ShowSideBar": "显示侧边栏",
"ShowSideBarDetail": "侧边栏让你可以在工作区之间快速切换",
"ShowSideBarIcon": "展示侧边栏工作区图标",
"ShowSideBarText": "展示侧边栏上按钮的文本",
"ShowTitleBar": "显示标题栏",
"ShowTitleBarDetail": "在标题栏上会显示当前页面的标题",
"SpellCheck": "拼写检查",
"SpellCheckLanguages": "首选拼写检查语言",
"Support": "支持",
"SwipeWithThreeFingersToNavigate": "用三根手指轻扫来前进后退",
"SwipeWithThreeFingersToNavigateDescription": "使用3个指手势在页面之间导航。向左轻扫可返回向右轻扫可前进。<br/>要启用它,还需要更改<3>macOS首选项 → 触控板 → 更多手势 → 在页面间轻扫</3>到<5>用三个手指轻扫</5>或<7>用两个或三个手指轻扫。</7>",
"Sync": "同步和备份",
"SyncBeforeShutdown": "在关机前自动同步",
"SyncBeforeShutdownDescription": "关电脑前自动同步数据注意手动退出应用不会触发同步以防应用出错时将错误数据同步上去。Windows 系统不支持此功能。",
"SyncInterval": "同步/备份间隔",
"SyncIntervalDescription": "每经过这段长度的时间后,就会自动开始备份到 Github如果工作区是本地工作区则会创建本地备份重启后生效",
"SyncOnlyWhenNoDraft": "在没有草稿时才同步",
"SyncOnlyWhenNoDraftDescription": "在同步前检查有没有草稿或处于所见即所得编辑状态的条目,如果有则本次不同步,防止将草稿同步到你的博客里。(对关机前自动同步无效,毕竟你很可能希望将草稿从一台电脑上带到另一台电脑上继续编辑)",
"System": "系统",
"SystemDefaultTheme": "系统默认主题色",
"TestNotification": "测试通知功能",
"TestNotificationDescription": "<0>如果通知未显示,请确保在<1>macOS首选项 → 通知 → TidGi中启用通知</1></0>",
"Theme": "主题色",
"TiddlyWiki": "太微",
"ToggleMenuBar": "切换显隐菜单栏",
"Token": "Git身份凭证",
"TokenDescription": "用于向Git服务器验证身份并同步内容的凭证可通过登录在线存储服务如Github来取得也可以手动获取「Personal Access Token」后填到这里。",
"Translatium": "翻译素APP",
"TranslatiumIntro": "像外语系大佬一样翻译任何语言",
"Updates": "更新",
"WebCatalog": "网站目录App",
"WebCatalogEngineIntro": "「网站目录App」是TidGi的最初代码的来源我们重用了来自开源的「网站目录App」的许多重要代码这要感谢「网站目录App」及其作者 Quang Lam",
"WebCatalogIntro": "神奇地将任何网站变成跨平台的应用程序。\n让你更加高效地工作而无需在浏览器上来回切换浏览器Tab。",
"WebSite": "官网",
"WikiMetaData": "Wiki元信息",
"WikiMetaDataDescription": "配置Wiki的启动参数",
"hardwareAcceleration": "使用硬件加速"
},
"Error": {
"InitWikiGitError": "E-1 笔记仓库初始化失败错误",
"InitWikiGitErrorDescription": "E-1 新笔记仓库所用的模板复制失败或者笔记仓库的git初始化失败这应该是个bug。",
"InitWikiGitRevertError": "E-2 笔记仓库初始化失败且撤销失败错误",
"InitWikiGitRevertErrorDescription": "E-2 不仅笔记仓库初始化失败了,而且撤销也失败了,这是个严重问题,需要你手动清理该位置生成的新文件夹。",
"CopyWikiTemplateError": "E-3 复制维基模板错误",
"CopyWikiTemplateErrorDescription": "E-3 尝试把最新维基模板复制或覆盖到对应位置,但是失败了,请根据提示检查你的输入。",
"DoubleWikiInstanceError": "E-4 重复启动维基错误",
"DoubleWikiInstanceErrorDescription": "E-4 你启动了同一个Wiki两次这可能是程序bug导致的。",
"InsertMenuAfterSubMenuIndexError": "E-5 插入目录模板到现有目录后错误",
"InsertMenuAfterSubMenuIndexErrorDescription": "E-5 你尝试插入目录,且 afterSubMenu \"{{afterSubMenu}}\" 在目录 menuID \"{{menuID}}\" 内,但我们无法在目录 \"{{menu}}\" 里找到它,请用正确的 menuID 指定一个目录。",
"InitWikiGitSyncedWikiNoGitUserInfoError": "E-6 笔记仓库初始化失败因为没有提供Git信息错误",
"InitWikiGitSyncedWikiNoGitUserInfoErrorDescription": "E-6 初始化同步到云端的笔记仓库需要你选择一个云端的 git 仓库地址,还有提供相应云服务的认证凭证,然而目前没有获得这些信息。",
"MainWindowMissing": "E-7 程序无法获取主窗口信息,无法正常运行。",
"WorkspaceFailedToLoadError": "E-8 工作区加载失败错误",
"WorkspaceFailedToLoadErrorDescription": "E-8 工作区对应的Wiki网页加载失败了原因有很多但基本是因为程序Bug",
"ViewLoadUrlError": "E-9 网页加载失败错误",
"ViewLoadUrlErrorDescription": "E-9 工作区对应的Wiki网页加载失败了但即将重试",
"ZxNotInitializedError": "E-10 Zx 代码执行服务未初始化错误",
"ZxNotInitializedErrorDescription": "E-10 Zx 代码执行服务未成功初始化,将自动尝试初始化。",
"ZxInitializationRetryFailedError": "E-11 Zx 代码执行服务初始化重试错误",
"ZxInitializationRetryFailedErrorDescription": "E-11 Zx 代码执行服务初始化出错,重试多次依然失败错误,请上传 log 文件提交 issue 报告错误以便修复。",
"ZxInitializationError": "E-12 Zx 代码执行服务初始化错误",
"ZxInitializationErrorDescription": "E-12 Zx 代码执行服务初始化错误,原因请查看 log 文件,并上传提交 issue 以便修复。",
"WikiRuntimeError": "E-13 维基运行时有错误",
"WikiRuntimeErrorDescription": "E-13 维基运行时有错误,原因请查看 log 文件,并上传提交 issue 以便修复。",
"SubWikiSMainWikiNotExistError": "子 Wiki 所附着的主 Wiki 不存在",
"SubWikiSMainWikiNotExistErrorDescription": "子 Wiki 在创建时必须选择一个附着到的主 Wiki但是现在这个子 Wiki 所应该附着的主 Wiki 找不到了,无法附着。",
"HTMLCanNotLoadError": "提供的 HTML 文件路径无法使用。",
"HTMLCanNotLoadErrorDescription": "请输入指向可用的 HTML 文件的路径。",
"ALreadyExistErrorDescription": "当前路径已有文件夹,新的知识库无法在此新建。",
"AlreadyExistError": "该处已被文件夹占用"
"Save": "保存",
"Scripting": {
"ExecutingScript": "正在执行脚本"
},
"Loading": "加载中",
"Delete": "删除",
"ErrorMessage": "报错信息",
"ClickForDetails": "点击了解详情",
"Yes": "是的",
"No": "不了",
"Title": "标题",
"Description": "描述",
"Tags": "标签",
"Open": "打开",
"Edit": "编辑",
"Help": {
"Alternatives": "其它源",
"Description": "点击「打开」按钮会在新窗口打开页面页面初次打开需要5秒到1分钟时间从互联网加载断网不可用。你可以随意修改打开的页面的内容作为沙箱游乐场随意尝试学到的功能如果想保存修改的结果可以点击太微的保存按钮保存为HTML格式的单页面维基。",
"List": "帮助列表",
"Contribute": "向此网站贡献内容",
"Tags": {
"Intro": "入门",
"FAQ": "答疑",
"Docs": "文档"
}
"SideBar": {
"Preferences": "设置...",
"UpdateAvailable": "有新版本!"
},
"LOG": {
"CommitMessage": "使用太记桌面版同步",
"CommitBackupMessage": "使用太记桌面版备份"
}
"Unknown": "未知",
"Updater": {
"CheckUpdate": "检查更新",
"CheckingFailed": "检查更新失败(网络错误)",
"CheckingForUpdate": "检查更新中…",
"UpdateAvailable": "有新版本可用!",
"UpdateNotAvailable": "目前已是最新版"
},
"WorkspaceSelector": {
"Add": "添加",
"Agent": "智能体",
"AreYouSure": "你确定要移除这个工作区吗?移除工作区会删除本应用中的工作区,但不会删除硬盘上的文件夹。如果你选择一并删除知识库文件夹,则所有内容都会被删除。",
"DefaultTiddlers": "默认条目",
"EditCurrentWorkspace": "配置当前工作区",
"EditWorkspace": "配置工作区",
"Guide": "引导",
"Help": "帮助",
"HibernateWorkspace": "休眠工作区",
"OpenInBrowser": "用浏览器打开",
"OpenInBrowserDisabledHint": "(启用 HTTP API 才能使用)",
"OpenWorkspaceFolder": "打开文件夹",
"OpenWorkspaceFolderInEditor": "用外部编辑器打开文件夹",
"OpenWorkspaceFolderInGitGUI": "用可视化Git工具打开",
"OpenWorkspaceMenuName": "打开工作区",
"OpenWorkspaceTagTiddler": "打开 {{tagName}}",
"ReloadCurrentWorkspace": "刷新当前工作区",
"RemoveCurrentWorkspace": "移除当前工作区",
"RemoveWorkspace": "移除工作区",
"RemoveWorkspaceAndDelete": "移除工作区并删除知识库文件夹",
"WakeUpWorkspace": "唤醒工作区"
},
"Yes": "是的"
}

View file

@ -0,0 +1,557 @@
{
"APILogs": {
"CurrentAgent": "顯示智慧體日誌: {{agentId}}",
"Description": "此智慧體的外部介面調用除錯日誌。在偏好設置中啟用「外部介面除錯」以開始記錄。",
"ErrorDetails": "錯誤詳情",
"NoLogs": "未找到此智慧體的介面調用日誌",
"NoResponse": "無響應",
"RequestDetails": "請求詳情",
"ResponseContent": "響應內容",
"ResponseMetadata": "響應元數據",
"StatusCancel": "已取消",
"StatusDone": "已完成",
"StatusError": "錯誤",
"StatusStart": "已開始",
"StatusUpdate": "處理中",
"Title": "外部介面除錯日誌"
},
"Agent": {
"EditTitle": "編輯智慧體名字",
"InvalidTabType": "無效的標籤頁類型。需要聊天標籤頁。",
"LoadingChat": "正在載入對話...",
"StartConversation": "開始對話",
"Untitled": "未命名"
},
"Browser": {
"Back": "後退",
"Bookmark": "收藏",
"CurrentUrl": "當前 URL",
"EnterUrlPlaceholder": "輸入網址",
"Forward": "前進",
"Home": "首頁",
"Refresh": "刷新",
"RenderPlaceholder": "這是網頁渲染區域"
},
"Chat": {
"Cancel": "取消",
"ConfigError": {
"GoToSettings": "前往設置",
"Title": "配置問題"
},
"InputPlaceholder": "輸入消息Ctrl+Enter 發送",
"Send": "發送",
"SessionGroup": {
}
},
"Common": {
},
"ContextMenu": {
"AddToCurrentSplitView": "添加到當前分屏",
"Close": "關閉",
"CloseAbove": "關閉上方標籤頁",
"CloseBelow": "關閉下方標籤頁",
"CloseOther": "關閉其他標籤頁",
"CloseTabs": "關閉多個標籤頁",
"ConvertToSplitView": "轉為分屏視圖",
"CreateSplitViewWithActive": "和當前標籤頁創建分屏",
"Duplicate": "複製",
"NewTabBelow": "在下方新建標籤頁",
"Pin": "固定標籤頁",
"Refresh": "刷新",
"RestoreClosed": "恢復關閉的標籤頁",
"Unpin": "取消固定"
},
"CreateAgent": {
"AgentName": "智慧體名稱",
"AgentNameHelper": "為您的智慧體取一個描述性的名字",
"AgentNamePlaceholder": "輸入智慧體名稱...",
"Back": "上一步",
"CreatingPreview": "正在創建預覽智慧體...",
"EditPrompt": "編輯提示詞",
"EditPromptDescription": "自訂您的智慧體的系統提示詞和行為",
"ImmediateUse": "測試並使用",
"ImmediateUseDescription": "測試您的智慧體並立即開始使用",
"Next": "下一步",
"NoTemplateSelected": "請先選擇一個模板",
"Preview": "(預覽)",
"SaveAndUse": "保存並使用智慧體",
"SearchTemplates": "搜索智慧體模板...",
"SelectTemplate": "選擇模板",
"SelectTemplateDescription": "選擇一個現有的智慧體作為起始模板",
"SelectedTemplate": "已選擇模板",
"SetupAgent": "設置智慧體",
"SetupAgentDescription": "為您的智慧體命名並選擇一個模板作為起點",
"Steps": {
},
"Title": "創建新智慧體"
},
"EditAgent": {
"AgentDescription": "智能體描述",
"AgentDescriptionHelper": "描述您的智慧體的功能和用途",
"AgentDescriptionPlaceholder": "輸入智慧體描述...",
"AgentName": "智慧體名稱",
"AgentNameHelper": "為您的智慧體取一個描述性的名字",
"AgentNamePlaceholder": "輸入智慧體名稱...",
"AgentNotFound": "智慧體未找到",
"EditBasic": "編輯基本資訊",
"EditBasicDescription": "編輯您的智慧體的基本資訊",
"EditPrompt": "編輯提示詞",
"EditPromptDescription": "自訂您的智慧體的系統提示詞和行為",
"ImmediateUse": "測試並使用",
"ImmediateUseDescription": "測試您的智慧體並立即開始使用",
"Loading": "載入中...",
"LoadingPromptConfig": "正在載入提示詞配置...",
"PreviewChat": "預覽聊天",
"Save": "保存",
"Saving": "保存中...",
"Steps": {
},
"Title": "編輯智慧體定義"
},
"ModelFeature": {
},
"ModelSelector": {
"Model": "模型",
"NoModelSelected": "未選擇模型",
"SelectModel": "選擇模型",
"Title": "模型選擇"
},
"NewTab": {
"CreateDefaultAgent": "創建默認智慧體",
"CreateInstance": "創建實例",
"CreateNewAgent": "創建新智慧體",
"EditDefinition": "編輯定義",
"NewTab": "新建標籤頁",
"QuickAccess": "快速訪問",
"SearchPlaceholder": "搜索標籤頁或智慧體..."
},
"Preference": {
"AIAgent": "智慧體",
"AIAgentDescription": "管理智慧體對話記錄資料庫",
"AIAgentDescriptionDetail": "這裡可以查看和刪除智慧體對話記錄資料庫的大小和位置資訊",
"APIKey": "API 金鑰",
"AddNewModel": "添加新模型",
"AddNewProvider": "添加新提供商",
"AddProvider": "添加提供商",
"AgentDatabaseDescription": "所有智慧體對話記錄都保存在這個資料庫裡,僅涉及與人工智慧的交談,不影響維基內容,占用空間為 {{size}}",
"BaseURL": "API 地址",
"BaseURLRequired": "API 地址為必填項",
"Browse": "瀏覽",
"CancelAddProvider": "取消添加",
"ConfigureModelParameters": "配置參數",
"ConfigureProvider": "配置 {{provider}}",
"ConfirmDeleteAgentDatabase": "確定要刪除包含所有智慧體對話記錄的資料庫嗎?此操作無法撤銷。",
"CustomProvider": "自訂提供方",
"DefaultAIModelSelection": "默認人工智慧模型選擇",
"DefaultAIModelSelectionDescription": "選擇在未具體設置時預設使用人工智慧提供商和模型",
"DefaultEmbeddingModelSelection": "默認嵌入模型選擇",
"DefaultEmbeddingModelSelectionDescription": "選擇用於語義搜索和向量操作的默認嵌入模型",
"DefaultImageGenerationModelSelection": "默認圖像生成模型選擇",
"DefaultImageGenerationModelSelectionDescription": "選擇用於文字生成圖像操作的默認圖像生成模型",
"DefaultSpeechModelSelection": "默認語音生成模型選擇",
"DefaultSpeechModelSelectionDescription": "選擇用於文字轉語音操作的默認語音生成模型",
"DefaultTranscriptionsModelSelection": "默認語音識別模型選擇",
"DefaultTranscriptionsModelSelectionDescription": "選擇用於語音轉文字操作的默認語音識別模型",
"DeleteAgentDatabase": "刪除人工智慧對話資料庫",
"DeleteProvider": "刪除提供商",
"DisabledProviderInfo": "此提供商已禁用,其模型不會在模型選擇列表中顯示",
"EnableProvider": "啟用此提供商",
"ExternalAPI": "外部服務介面",
"ExternalAPIDebug": "啟用介面除錯日誌",
"ExternalAPIDebugDescription": "開啟後,所有介面請求和響應將被記錄到資料庫中以便除錯",
"ExternalApiDatabaseDescription": "包含外部介面Debug 資訊的資料庫,占用空間為 {{size}}",
"FailedToAddModel": "無法添加模型",
"FailedToAddProvider": "添加提供商失敗",
"FailedToRemoveModel": "無法刪除模型",
"FailedToSaveSettings": "無法保存設置",
"FailedToUpdateModel": "無法更新模型",
"FailedToUpdateProviderStatus": "無法更新提供商狀態",
"MaxTokens": "最大生成長度",
"MaxTokensDescription": "模型在一次請求中可以生成的最大字元數以token計算",
"ModelAddedSuccessfully": "模型添加成功",
"ModelAlreadyExists": "模型已存在",
"ModelCaption": "模型顯示名稱",
"ModelCaptionHelp": "在界面上顯示的友好名稱,如不填則使用模型名稱",
"ModelDetails": "模型詳細資訊",
"ModelFeatures": "模型功能",
"ModelName": "模型名稱",
"ModelNameRequired": "模型名稱為必填項",
"ModelParameters": "模型參數",
"ModelParametersDescription": "配置生成式AI模型的行為參數如溫度、token限制等",
"ModelRemovedSuccessfully": "模型刪除成功",
"ModelUpdatedSuccessfully": "模型更新成功",
"Models": "可用模型",
"NoPresetSelected": "不使用預設模型",
"NoProvidersAvailable": "沒有可用的提供商",
"OpenDatabaseFolder": "打開資料庫文件夾",
"PresetModels": "預設模型",
"PresetProvider": "預置提供商",
"ProviderAddedSuccessfully": "提供商添加成功",
"ProviderAlreadyExists": "提供商名稱已存在",
"ProviderClass": "提供商介面類型",
"ProviderConfiguration": "提供商配置",
"ProviderConfigurationDescription": "配置人工智慧提供商的介面金鑰和其他設置",
"ProviderDisabled": "提供方已禁用",
"ProviderEnabled": "提供方已啟用",
"ProviderName": "提供商名稱",
"ProviderNameRequired": "提供商名稱為必填項",
"SearchEmbeddingNoEmbeddingModelError": "請先在外部API部分配置默認嵌入模型設置。",
"SelectDefaultProvider": "選擇默認提供商",
"SelectFromPresets": "從預設模型中選擇",
"SelectModel": "選擇模型",
"SettingsSaved": "設置已保存",
"SystemPrompt": "系統提示詞",
"SystemPromptDescription": "用於初始化AI行為的系統指令定義其行為和能力",
"SystemPromptPlaceholder": "系統提示詞佔位符",
"Temperature": "溫度",
"TemperatureDescription": "較低的值會產生更確定性、更集中的響應,較高的值會產生更多樣化、更創造性的響應",
"TopP": "Top P",
"TopPDescription": "控制響應的隨機性。較低的值使響應更確定,較高的值允許更多的可能性",
"WorkflowFile": "工作流檔案",
"WorkflowFileHelp": "ComfyUI 工作流 JSON 文件的路徑",
"WorkflowFilePath": "工作流檔案路徑"
},
"Prompt": {
"AutoRefresh": "預覽會隨輸入文本的變化自動刷新",
"CodeEditor": "代碼編輯器",
"Flat": "平鋪視圖",
"FormEditor": "表單編輯器",
"LastUpdated": "上次更新時間",
"Loading": "載入預覽中...",
"NoMessages": "還沒有消息可以預覽",
"Preview": "提示詞預覽",
"SchemaNotProvided": "格式未提供",
"SchemaNotProvidedDescription": "沒有提供 JSON Schema 或無法正確獲取到。編輯表單無法展示。",
"Tree": "樹形視圖",
"ValidationErrors": "發現錯誤"
},
"PromptConfig": {
"AddItem": "添加項目",
"EmptyArray": "還沒有添加任何項目。點擊下面的按鈕添加第一個項目。",
"ItemCount": "{{count}} 項",
"RemoveItem": "刪除列表項",
"Tabs": {
"Prompts": "提示詞",
"Response": "響應"
},
"Tags": {
"HelperText": "輸入後按 Enter 鍵添加標籤,或從預定義標籤中選擇",
"NoOptions": "沒有可選標籤",
"Placeholder": "輸入標籤..."
}
},
"Schema": {
"AIConfig": {
"Description": "AI 會話設置配置",
"Title": "AI 配置"
},
"AgentConfig": {
"Description": "智慧體配置",
"Id": "智慧體唯一標識符",
"IdTitle": "智慧體 ID",
"PromptConfig": {
"Description": "提示詞配置",
"Prompts": "提示詞配置列表",
"Response": "響應配置列表",
"Title": "提示詞配置"
},
"Title": "智慧體配置"
},
"AutoReroll": {
},
"BaseAPIConfig": {
"API": "API 提供商和模型配置",
"APITitle": "API 配置",
"Description": "基礎介面配置",
"ModelParameters": "模型參數配置",
"ModelParametersTitle": "模型參數",
"Title": "基礎介面配置"
},
"DefaultAgents": {
"Description": "默認智慧體配置列表",
"Title": "默認智慧體"
},
"DynamicPosition": {
},
"FullReplacement": {
"Description": "完全替換參數配置",
"SourceType": "數據來源類型,決定用什麼內容來替換目標元素",
"SourceTypeTitle": "源類型",
"SourceTypes": {
},
"TargetId": "目標元素ID",
"TargetIdTitle": "目標ID",
"Title": "完全替換參數"
},
"Function": {
},
"HandlerConfig": {
},
"JavascriptTool": {
},
"MCP": {
"Description": "模型上下文協議參數配置",
"Id": "MCP 伺服器 ID",
"IdTitle": "伺服器 ID",
"ResponseProcessing": {
},
"TimeoutMessage": "超時消息",
"TimeoutMessageTitle": "超時消息",
"TimeoutSecond": "超時時間(秒)",
"TimeoutSecondTitle": "超時時間",
"Title": "模型上下文協議參數"
},
"ModelParameters": {
"Description": "模型參數配置",
"MaxTokens": "生成的最大令牌數量",
"MaxTokensTitle": "最大令牌數",
"SystemPrompt": "模型系統提示詞",
"SystemPromptTitle": "系統提示詞",
"Temperature": "響應生成溫度(越高=越創造性)",
"TemperatureTitle": "溫度",
"Title": "模型參數",
"TopP": "Top P 採樣參數",
"TopPTitle": "Top P"
},
"Position": {
"Bottom": "自底部偏移幾條消息",
"BottomTitle": "底部偏移",
"Description": "位置參數配置,用於確定內容插入的精確位置。支持相對位置、絕對位置、前置和後置四種定位方式",
"TargetId": "目標元素ID",
"TargetIdTitle": "目標ID",
"Title": "位置參數",
"Type": "位置類型,用於確定內容插入的精確位置。支持相對位置、絕對位置、前置和後置四種定位方式",
"TypeTitle": "位置類型",
"Types": {
}
},
"Prompt": {
"Caption": "簡短描述",
"CaptionTitle": "描述",
"Children": "子提示詞列表,將從上到下,從外到裡地拼接為最終的提示詞文本。",
"ChildrenTitle": "子提示詞",
"Description": "完整的提示詞配置,包含類型和內容",
"Enabled": "是否啟用此提示詞,啟用的才會拼入到最終的提示詞中",
"EnabledTitle": "啟用",
"Id": "提示詞配置的唯一標識符,方便在 PromptDynamicModification 裡通過 targetId 引用。",
"IdTitle": "ID",
"Role": "OpenAI 相容介面的提示詞角色。system: 定義AI的行為規則和背景設定user: 模擬用戶的輸入和請求assistant: AI的回覆和響應內容",
"RoleTitle": "角色",
"RoleType": {
"Assistant": "助手 - AI的回覆和響應內容",
"System": "系統 - 定義AI的行為規則和背景設定",
"User": "用戶 - 模擬用戶的輸入和請求"
},
"Tags": "標籤列表",
"TagsTitle": "標籤",
"Text": "提示詞內容,可以包含維基文本支持的語法,例如<<變數名>>。",
"TextTitle": "文本",
"Title": "提示詞"
},
"PromptDynamicModification": {
"DynamicModificationTypes": {
}
},
"PromptPart": {
},
"ProviderModel": {
"Description": "提供商和模型配置",
"EmbeddingModel": "用於語義搜索和向量操作的嵌入模型名稱",
"EmbeddingModelTitle": "嵌入模型",
"ImageGenerationModel": "用於文字生成圖像操作的圖像生成模型名稱",
"ImageGenerationModelTitle": "圖像生成模型",
"Model": "AI 模型名稱",
"ModelTitle": "模型",
"Provider": "AI 提供商名稱",
"ProviderTitle": "提供商",
"SpeechModel": "用於文字轉語音操作的語音生成模型名稱",
"SpeechModelTitle": "語音模型",
"Title": "提供商模型",
"TranscriptionsModel": "用於語音轉文字操作的語音識別模型名稱",
"TranscriptionsModelTitle": "語音識別模型"
},
"RAG": {
"Removal": {
},
"SourceTypes": {
}
},
"Response": {
"Description": "外部API的響應通常作為響應動態修改的目標結構與提示詞的一樣可以填寫預置內容也可以作為占位符或容器由 ResponseDynamicModification 填入外部API的響應的具體內容。",
"Title": "響應"
},
"ResponseDynamicModification": {
"DynamicModificationTypes": {
},
"ResponseProcessingTypes": {
}
},
"ToolCalling": {
},
"Trigger": {
"Model": {
}
},
"Wiki": {
},
"WikiOperation": {
"Description": "在維基工作區中執行 條目操作(添加、刪除或設置文本)",
"Title": "Wiki 操作",
"Tool": {
"Examples": {
},
"Parameters": {
"extraMeta": {
"Description": "額外元數據的 JSON 字串,如標籤和欄位,預設為 \"{}\"",
"Title": "額外元數據"
},
"operation": {
"Description": "要執行的操作類型",
"Title": "操作類型"
},
"options": {
"Description": "操作選項的 JSON 字串,預設為 \"{}\"",
"Title": "操作選項"
},
"text": {
"Description": "條目的文本內容",
"Title": "條目內容"
},
"title": {
"Description": "條目的標題",
"Title": "條目標題"
},
"workspaceName": {
"Description": "要操作的工作區名稱或ID",
"Title": "工作區名稱"
}
}
},
"ToolListPosition": {
"Position": "相對於目標元素的插入位置before/after",
"PositionTitle": "插入位置",
"TargetId": "要插入工具列表的目標元素的ID",
"TargetIdTitle": "目標ID"
},
"ToolResultDuration": "工具執行結果在對話中保持可見的輪數,超過此輪數後結果將變灰顯示",
"ToolResultDurationTitle": "工具結果持續輪數"
},
"WikiSearch": {
"Description": "使用篩選器表達式搜索 TiddlyWiki 工作區內容",
"SourceType": "數據源類型",
"SourceTypeTitle": "源類型",
"Title": "Wiki 搜索",
"Tool": {
"Parameters": {
"filter": {
"Description": "TiddlyWiki 篩選器表達式",
"Title": "過濾器"
},
"limit": {
"Description": "返回的最大結果數量",
"Title": "限制"
},
"query": {
"Description": "向量搜尋時使用的查詢文本(自然語言)",
"Title": "查詢"
},
"searchType": {
"Description": "選擇基於規則或基於相似度的一個搜索模式",
"Title": "搜索類型"
},
"threshold": {
"Description": "相似度閾值0-1低於此閾值的向量結果將被過濾",
"Title": "閾值"
},
"workspaceName": {
"Description": "要搜索的工作區名稱或ID",
"Title": "工作區名稱"
}
},
"UpdateEmbeddings": {
"Parameters": {
"forceUpdate": {
},
"workspaceName": {
}
}
}
},
"ToolListPosition": {
"Position": "相對於目標位置的插入位置",
"PositionTitle": "插入位置",
"TargetId": "目標元素的ID工具列表將相對於此元素插入",
"TargetIdTitle": "目標ID"
},
"ToolListPositionTitle": "工具列表位置",
"ToolResultDuration": "工具執行結果在對話中保持可見的輪數,超過此輪數後結果將變灰顯示",
"ToolResultDurationTitle": "工具結果持續輪數"
}
},
"Search": {
"AvailableAgents": "可用的智慧體",
"FailedToCreateChatWithAgent": "無法創建與智慧體的對話",
"FailedToFetchAgents": "獲取智慧體列表失敗",
"NoAgentsFound": "未找到智慧體",
"NoClosedTabsFound": "沒有最近關閉的標籤頁",
"NoTabsFound": "沒有找到標籤頁",
"OpenTabs": "打開的標籤頁",
"RecentlyClosedTabs": "最近關閉的標籤頁"
},
"SplitView": {
"NoTabs": "分屏視圖中沒有標籤"
},
"Tab": {
"Title": {
"CreateNewAgent": "創建新智慧體",
"EditAgentDefinition": "編輯智慧體",
"NewTab": "新建標籤頁",
"NewWeb": "新建網頁",
"SplitView": ""
}
},
"Tool": {
"Schema": {
"Description": "描述",
"Examples": "使用範例",
"Optional": "可選",
"Parameters": "參數",
"Required": "必需"
},
"WikiOperation": {
"Error": {
"WorkspaceNotExist": "工作區{{workspaceID}}不存在",
"WorkspaceNotFound": "工作區名稱或ID\"{{workspaceName}}\"不存在。可用工作區:{{availableWorkspaces}}"
},
"Success": {
"Added": "成功在維基工作區\"{{workspaceName}}\"中添加了條目\"{{title}}\"",
"Deleted": "成功從維基工作區\"{{workspaceName}}\"中刪除了條目\"{{title}}\"",
"Updated": "成功在維基工作區\"{{workspaceName}}\"中設置了條目\"{{title}}\"的文本"
}
},
"WikiSearch": {
"Error": {
"ExecutionFailed": "工具執行失敗:{{error}}",
"WorkspaceNotExist": "工作區{{workspaceID}}不存在",
"WorkspaceNotFound": "工作區名稱或ID\"{{workspaceName}}\"不存在。可用工作區:{{availableWorkspaces}}"
},
"Success": {
"Completed": "Wiki搜索完成。找到{{totalResults}}個總結果,顯示{{shownResults}}個:\n\n",
"NoResults": "在維基工作區\"{{workspaceName}}\"中未找到過濾器\"{{filter}}\"的結果",
"NoVectorResults": "在維基工作區\"{{workspaceName}}\"中未找到符合條件的向量搜索結果(相似度閾值:{{threshold}})。",
"VectorCompleted": "根據向量搜索,在工作區 {{workspaceName}} 中找到以下相關內容:\n\n"
},
"UpdateEmbeddings": {
"Error": {
"ExecutionFailed": "生成嵌入失敗:{{error}}",
"NoAIConfig": "請先配置人工智慧提供商和嵌入模型(在設置中)。",
"WorkspaceNotExist": "工作區{{workspaceID}}不存在",
"WorkspaceNotFound": "工作區名稱或ID\"{{workspaceName}}\"不存在。可用工作區:{{availableWorkspaces}}"
},
"Success": {
"Generated": "已成功為工作區 {{workspaceName}} 生成向量嵌入索引。總計{{totalNotes}}個筆記,{{totalEmbeddings}}個嵌入。"
}
}
}
}
}

View file

@ -0,0 +1,490 @@
{
"AddWorkspace": {
"AddFileSystemPath": "正在為子知識庫添加FileSystemPaths",
"AddWorkspace": "添加工作區",
"Advanced": "進階設定",
"AndLinkToMainWorkspace": "並連結到主知識庫",
"BadWikiHtml": "該HTML文件無法用於創建知識庫",
"CanNotLoadList": "無法載入倉庫列表,網路不佳",
"CantCreateFolderHere": "無法在該處創建文件夾 \"{{newWikiPath}}\"",
"Choose": "選擇",
"CloneOnlineWiki": "導入線上知識庫",
"CloneWiki": "導入線上知識庫: ",
"CreateLinkFromSubWikiToMainWikiFailed": "無法連結文件夾 \"{{subWikiPath}}\" 到 \"{{mainWikiTiddlersFolderPath}}\"",
"CreateLinkFromSubWikiToMainWikiSucceed": "在主知識庫內成功創建子知識庫的捷徑,捷徑會自動將文件導入子知識庫。",
"CreateNewWiki": "創建新知識庫",
"CreatePrivateRepository": "創建私有倉庫",
"CreatePublicRepository": "創建公開倉庫",
"CreateWiki": "創建知識庫: ",
"ExistedWikiLocation": "現有的知識庫的位置",
"ExtractedWikiFolderName": "轉換後的知識庫文件夾名稱",
"GitDefaultBranchDescription": "你的Git的預設分支Github在黑命貴事件後將其從master改為了main",
"GitEmailDescription": "用於Git提交記錄的Email用於在Github等服務上統計每日提交量",
"GitRepoUrl": "Git倉庫線上網址",
"GitTokenDescription": "用於登錄Git的憑證一定時間後會過期",
"GitUserNameDescription": "用於登入Git的帳戶名注意是你的倉庫網址中你的名字部分",
"ImportWiki": "導入知識庫: ",
"LocalWikiHtml": "HTML文件的路徑",
"LocalWorkspace": "本地知識庫",
"LocalWorkspaceDescription": "僅在本地使用,完全掌控自己的數據。太記會為你創建一個本地的 git 備份系統,讓你可以回退到之前的版本,但當文件夾被刪除時所有內容還是會遺失。",
"LogoutToGetStorageServiceToken": "登錄在線儲存服務以獲取最新憑證",
"MainPageReloadTip": "<0><0>請嘗試:<1><0>點擊下面的 <2>重新載入</2> 按鈕,或用快捷鍵 <5>CMD/Ctrl + R</5> 來刷新頁面。</0><1>或者打開 <2>Log文件夾</2> 來看看具體的錯誤原因。</1><2>最糟糕的情況下也可以複製備份你電腦上的文件夾,右鍵工作區圖示選擇刪除工作區,然後重新導入電腦上的文件夾(或通過拖入 HTML 導入之前備份的 HTML 版 知識庫。)</2></1></0></0>",
"MainPageTipWithSidebar": "<0>點擊側邊欄上的這個 </0><1>+</1><2>(加號按鈕)來開始使用太微!</2>",
"MainPageTipWithoutSidebar": "<0>使用菜單上的</0><strong> 工作區 → 添加工作區 </strong><0>或</0><strong> 點擊此處 </strong><2>來開始使用太微!</2>",
"MainWorkspace": "主知識庫",
"MainWorkspaceDescription": "包含了太微的設定檔,以及發布為部落格時的公開內容。",
"MainWorkspaceLocation": "主知識庫位置",
"NotFilled": "未填",
"NotLoggedIn": "未登錄",
"OmitMoreResult": "列表僅展示前 {{loadCount}} 個結果",
"OpenLocalWiki": "導入本地知識庫",
"OpenLocalWikiFromHTML": "導入HTML知識庫",
"PathNotExist": "該路徑不存在 \"{{path}}\"",
"Processing": "正在處理...",
"Reload": "重新載入",
"SearchGithubRepoName": "搜索Github倉庫名",
"StartCloningSubWiki": "開始導入線上子知識庫",
"StartCloningWiki": "開始導入線上知識庫",
"StartCreatingSubWiki": "開始創建子知識庫",
"StartLinkingSubWikiToMainWiki": "開始連結子知識庫到父知識庫",
"StartUsingTemplateToCreateWiki": "開始用模板創建知識庫",
"SubWikiCreationCompleted": "子知識庫創建完畢",
"SubWorkspace": "子知識庫",
"SubWorkspaceDescription": "必須依附於一個主知識庫可用於存放私有內容。注意兩點子知識庫不能放在主知識庫文件夾內子知識庫一般用於同步數據到一個私有的Github倉庫內僅本人可讀寫故倉庫地址不能與主知識庫一樣。\n子知識庫透過創建一個到主知識庫的軟連結捷徑來生效創建連結後主知識庫內便可看到子知識庫內的內容了。",
"SubWorkspaceWillLinkTo": "子知識庫將連結到",
"SwitchCreateNewOrOpenExisted": "切換創建新的還是打開現有的知識庫",
"SyncedWorkspace": "雲端同步知識庫",
"SyncedWorkspaceDescription": "同步到在線儲存服務例如Github需要你登錄儲存服務或輸入登錄憑證並有良好的網路連接。可以跨設備同步數據在使用了值得信任的儲存服務的情況下數據仍歸你所有。而且文件夾被不慎刪除後還可以從在線服務重新下載數據到本地。",
"TagName": "標籤名",
"TagNameHelp": "加上此標籤的筆記將會自動被放入這個子知識庫內(可先不填,之後右鍵點擊這個工作區的圖示選擇編輯工作區修改)",
"ThisPathIsNotAWikiFolder": "該目錄不是一個知識庫文件夾 \"{{wikiPath}}\"",
"WaitForLogin": "等待登錄",
"WikiExisted": "知識庫已經存在於該位置 \"{{newWikiPath}}\"",
"WikiNotStarted": "知識庫 頁面未成功啟動或未成功載入",
"WikiTemplateCopyCompleted": "模板知識庫複製完畢",
"WikiTemplateMissing": "知識庫模板缺失 \"{{TIDDLYWIKI_TEMPLATE_FOLDER_PATH}}\"",
"WorkspaceFolder": "工作區文件夾的位置",
"WorkspaceFolderNameToCreate": "即將新建的知識庫文件夾名",
"WorkspaceParentFolder": "文件夾所在的父文件夾",
"WorkspaceUserName": "工作區編輯者名",
"WorkspaceUserNameDetail": "在知識庫中使用的編輯者名,將在創建或編輯條目時填入 creator 欄位。工作區內設置的編輯者名,將覆蓋設置裡配的全局的默認編輯者名。這方便你通過創建多個配了不同編輯者名的工作區,在同一個知識庫裡用不同的身份創建條目。"
},
"Cancel": "取消",
"ClickForDetails": "點擊了解詳情",
"ContextMenu": {
"About": "關於",
"AddToDictionary": "添加到字典",
"Back": "向後←",
"BackupNow": "立即本地Git備份",
"Copy": "複製",
"CopyEmailAddress": "複製電子郵件地址",
"CopyImage": "複製圖片",
"CopyImageURL": "複製圖片URL",
"CopyLink": "複製連結",
"Cut": "剪切",
"DeveloperTools": "Web 開發者工具",
"Forward": "向前→",
"InspectElement": "檢查 Web 元素",
"LookUp": "在字典中查看 \"{{word}}\"",
"More": "更多",
"NoNetworkConnection": "無網路連接",
"Notifications": "消息管理...",
"OpenCommandPalette": "打開搜索與命令面板",
"OpenLinkInBrowser": "在瀏覽器中打開連結",
"OpenTidGi": "打開太記",
"OpenTidGiMenuBar": "打開太記小窗口",
"OpenWorkspaceInNewWindow": "在新窗口中打開工作區",
"Paste": "黏貼",
"Preferences": "設置...",
"Quit": "退出",
"Reload": "刷新",
"RestartService": "重啟服務",
"RestartServiceComplete": "重啟服務成功",
"SearchWithGoogle": "用 Google 搜索",
"SyncNow": "立即同步雲端",
"TidGiSupport": "TidGi 用戶支持",
"TidGiWebsite": "TidGi 官網"
},
"Delete": "刪除",
"Dialog": {
"CantFindWorkspaceFolderRemoveWorkspace": "無法找到之前還在該處的工作區知識庫文件夾!本應存在於此處的知識庫文件夾可能被移走了,或該文件夾內沒有知識庫!是否移除工作區?",
"DoNotCare": "不管",
"FocusedTiddlerNotFoundTitle": "無法查詢到當前聚焦的條目",
"FocusedTiddlerNotFoundTitleDetail": "可以到 CPL 安裝 FocusedTiddler 插件",
"Later": "稍後",
"MadeWithLove": "<0>有</0><1> ❤ </1><2>的開發者:</2>",
"NeedCorrectTiddlywikiFolderPath": "需要傳入正確的路徑,而此路徑無法被太微識別。",
"PathPassInCantUse": "傳入的路徑無法使用",
"RemoveWorkspace": "移除工作區",
"ReportBug": "報告錯誤",
"ReportBugDetail": "如果你看過教學了解操作流程,並仔細讀過報錯內容並思考,仔細檢查了自己的輸入覺得沒問題,可以點擊按鈕。",
"RestartAppNow": "現在重啟應用",
"RestartMessage": "您需要重新啟動本程式才能使此更改生效。",
"RestartWikiNow": "現在重啟知識庫",
"Restarting": "重啟中",
"StorageServiceUserInfoNoFound": "找不到儲存備份服務的用戶資訊",
"StorageServiceUserInfoNoFoundDetail": "似乎你尚未登錄儲存備份服務,因此此知識庫的同步暫時禁用,直到你登錄以提供有可用於同步的登錄資訊。",
"WorkspaceFolderRemoved": "工作區文件夾被移走或該文件夾不是知識庫"
},
"EditWorkspace": {
"AddExcludedPlugins": "輸入希望忽略的插件名",
"AddExcludedPluginsDescription": "可搜索當前知識庫中已安裝的插件,或輸入任意插件名。",
"AppearanceOptions": "工作區外貌設置",
"BackupOnInterval": "定時自動備份",
"BackupOnIntervalDescription": "開啟時每隔一段時間全局設置裡的時間間隔會自動用本地Git備份數據一次這樣即使沒有配置雲端同步地址也會自動備份到本地。",
"Cancel": "取消",
"ClickToExpand": "點擊展開",
"DisableAudio": "阻止工作區中的聲音播放",
"DisableAudioTitle": "關閉聲音",
"DisableNotification": "阻止工作區的消息提醒",
"DisableNotificationTitle": "關閉提醒",
"EnableHTTPAPI": "啟用 HTTP API",
"EnableHTTPAPIDescription": "允許第三方程序如太記行動端、太記搜藏-剪藏插件等等通過 HTTP 網路介面讀取和修改你的筆記。",
"EnableHTTPS": "啟用HTTPS",
"EnableHTTPSDescription": "提供安全的TLS加密訪問需要你有自己的HTTPS證書可以從域名提供商那下載也可以搜索免費的HTTPS證書申請方式。",
"ExcludedPlugins": "需忽略的插件",
"ExcludedPluginsDescription": "在只讀模式啟動知識庫作為部落格時,你可能希望不載入一些編輯相關的插件以減小初次載入的網頁大小,例如 $:/plugins/tiddlywiki/codemirror 等,畢竟載入的部落格不需要這些編輯功能。",
"Generate": "生成",
"HTTPSCertPath": "Cert文件路徑",
"HTTPSCertPathDescription": "後綴為 .crt 的證書文件的所在位置,一般以 xxx_public.crt 結尾。",
"HTTPSKeyPath": "Key文件路徑",
"HTTPSKeyPathDescription": "後綴為 .key 的私鑰文件的所在位置。",
"HTTPSPickCert": "選擇Cert文件路徑",
"HTTPSPickKey": "選擇Key文件路徑",
"HTTPSUploadCert": "添加Cert文件",
"HTTPSUploadKey": "添加Key文件",
"HibernateDescription": "在工作區未使用時休眠以節省 CPU 和記憶體消耗並省電,這會關閉所有自動同步功能,需要手動同步備份數據。",
"HibernateTitle": "開啟休眠",
"IsSubWorkspace": "是子工作區",
"LastNodeJSArgv": "最近一次啟動的命令行參數",
"LastVisitState": "上次訪問的頁面",
"MainWorkspacePath": "主工作區路徑",
"MiscOptions": "雜項設置",
"Name": "工作區名",
"NameDescription": "工作區的名字將顯示在側邊欄上可以與工作區Git倉庫的實際文件夾名不同",
"NoRevert": "注意!這個操作無法撤銷!",
"Path": "知識庫的位置",
"PathDescription": "本地知識庫文件夾的地址",
"Port": "本地伺服器埠",
"ReadOnlyMode": "只讀模式",
"ReadOnlyModeDescription": "可用於配合內網穿透讓太記作為伺服器程序部署部落格。打開後將只能透過直接改文件的方式修改知識庫內容包括git同步網頁上將不能修改內容但任何人都可以訪問。",
"ResetDefaultIcon": "還原默認圖示",
"Save": "保存",
"SaveAndSyncOptions": "保存和同步",
"SelectLocal": "選擇本地圖片...",
"ServerOptions": "部落格和伺服器設置",
"SyncOnInterval": "定時自動同步備份",
"SyncOnIntervalDescription": "開啟後會根據全局設置裡的時間間隔自動同步並且依然會在啟動時自動同步點擊按鈕也可以手動同步。同步雲端前會自動先把數據備份到本地Git。如果關閉則只有在應用程式打開時會有一次自動同步還有當用戶通過點擊知識庫中的同步按鈕手動觸發同步。",
"SyncOnStartup": "啟動時自動同步",
"SyncOnStartupDescription": "在應用冷啟動時自動同步一次。",
"TiddlyWiki": "太微",
"TokenAuth": "憑證鑒權",
"TokenAuthAutoFillUserNameDescription": "此功能需要在全局設置或工作區設置裡填寫使用者名稱,不然不會生效。若你未填,將自動在工作區設置裡填一個預設值,你可自行修改。",
"TokenAuthCurrentHeader": "憑證鑒權當前請求頭",
"TokenAuthCurrentToken": "當前可用的鑒權憑證",
"TokenAuthCurrentTokenDescription": "機密資訊,洩露給敵意實體後需要重新生成,重新生成後需要為連接的第三方應用更新憑證",
"TokenAuthCurrentTokenEmptyText": "點擊生成按鈕來生成新的憑證",
"TokenAuthDescription": "開啟後HTTP請求中需要帶上憑證才能讀寫知識庫內容防止同一區域網路下其他人訪問筆記提高伺服器的安全性。無法與只讀模式同時開啟。",
"URL": "本地伺服器地址",
"UploadOrSelectPathDescription": "點擊上傳按鈕將文件提交給太記保管,也可以點擊選擇路徑按鈕從你保管的位置選取文件。",
"WikiRootTiddler": "知識庫根條目",
"WikiRootTiddlerDescription": "知識庫的根條目root-tiddler決定了系統的核心行為修改前請閱讀官方文件來了解",
"WikiRootTiddlerItems": {
}
},
"Error": {
"ALreadyExistErrorDescription": "當前路徑已有文件夾,新的知識庫無法在此新建。",
"AlreadyExistError": "該處已被文件夾占用",
"CopyWikiTemplateError": "E-3 複製知識庫模板錯誤",
"CopyWikiTemplateErrorDescription": "E-3 嘗試把最新知識庫模板複製或覆蓋到對應位置,但是失敗了,請根據提示檢查你的輸入。",
"DoubleWikiInstanceError": "E-4 重複啟動知識庫錯誤",
"DoubleWikiInstanceErrorDescription": "E-4 你啟動了同一個知識庫兩次,這可能是程序 bug 導致的。",
"HTMLCanNotLoadError": "提供的 HTML 文件路徑無法使用。",
"HTMLCanNotLoadErrorDescription": "請輸入指向可用的 HTML 文件的路徑。",
"InitWikiGitError": "E-1 筆記倉庫初始化失敗錯誤",
"InitWikiGitErrorDescription": "E-1 新筆記倉庫所用的模板複製失敗或者筆記倉庫的git初始化失敗這應該是個bug。",
"InitWikiGitRevertError": "E-2 筆記倉庫初始化失敗且撤銷失敗錯誤",
"InitWikiGitRevertErrorDescription": "E-2 不僅筆記倉庫初始化失敗了,而且撤銷也失敗了,這是個嚴重問題,需要你手動清理該位置生成的新文件夾。",
"InitWikiGitSyncedWikiNoGitUserInfoError": "E-6 筆記倉庫初始化失敗因為沒有提供Git資訊錯誤",
"InitWikiGitSyncedWikiNoGitUserInfoErrorDescription": "E-6 初始化同步到雲端的筆記倉庫需要你選擇一個雲端的 git 倉庫地址,還有提供相應雲服務的認證憑證,然而目前沒有獲得這些資訊。",
"InsertMenuAfterSubMenuIndexError": "E-5 插入目錄模板到現有目錄後錯誤",
"InsertMenuAfterSubMenuIndexErrorDescription": "E-5 你嘗試插入目錄,且 afterSubMenu \"{{afterSubMenu}}\" 在目錄 menuID \"{{menuID}}\" 內,但我們無法在目錄 \"{{menu}}\" 裡找到它,請用正確的 menuID 指定一個目錄。",
"MainWindowMissing": "E-7 程序無法獲取主窗口資訊,無法正常運行。",
"SubWikiSMainWikiNotExistError": "子知識庫所附著的主知識庫不存在",
"SubWikiSMainWikiNotExistErrorDescription": "子知識庫在創建時必須選擇一個附著到的主知識庫,但是現在這個子知識庫所應該附著的主知識庫找不到了,無法附著。",
"ViewLoadUrlError": "E-9 網頁載入失敗錯誤",
"ViewLoadUrlErrorDescription": "E-9 工作區對應的知識庫網頁載入失敗了,但即將重試",
"WikiRuntimeError": "E-13 知識庫運行時有錯誤",
"WikiRuntimeErrorDescription": "E-13 知識庫運行時有錯誤,原因請查看 log 文件,並上傳提交 issue 以便修復。",
"WorkspaceFailedToLoadError": "E-8 工作區載入失敗錯誤",
"WorkspaceFailedToLoadErrorDescription": "E-8 工作區對應的知識庫網頁載入失敗了,原因有很多,但基本是因為程序 Bug",
"ZxInitializationError": "E-12 Zx 代碼執行服務初始化錯誤",
"ZxInitializationErrorDescription": "E-12 Zx 代碼執行服務初始化錯誤,原因請查看 log 文件,並上傳提交 issue 以便修復。",
"ZxInitializationRetryFailedError": "E-11 Zx 代碼執行服務初始化重試錯誤",
"ZxInitializationRetryFailedErrorDescription": "E-11 Zx 代碼執行服務初始化出錯,重試多次依然失敗錯誤,請上傳 log 文件提交 issue 報告錯誤以便修復。",
"ZxNotInitializedError": "E-10 Zx 代碼執行服務未初始化錯誤",
"ZxNotInitializedErrorDescription": "E-10 Zx 代碼執行服務未成功初始化,將自動嘗試初始化。"
},
"ErrorMessage": "報錯資訊",
"Help": {
"Alternatives": "其它源",
"Contribute": "向此網站貢獻內容",
"Description": "點擊「打開」按鈕會在新窗口打開頁面頁面初次打開需要5秒到1分鐘時間從網路載入斷網不可用。你可以隨意修改打開的頁面的內容作為沙盒遊樂場隨意嘗試學到的功能如果想保存修改的結果可以點擊太微的保存按鈕保存為HTML格式的單頁知識庫。",
"List": "幫助列表",
"Tags": {
}
},
"LOG": {
"CommitBackupMessage": "使用太記桌面版備份",
"CommitMessage": "使用太記桌面版同步"
},
"LinOnetwo": "林一二",
"Loading": "載入中",
"Log": {
"AddComplete": "添加(Git Add)成功",
"AddingFiles": "開始添加(Git Add)待備份的文件",
"CantForcePullError": "強制拉取失敗,可能倉庫處在特殊狀態",
"CantSyncGitNotInitialized": "無法同步,這個文件夾沒有初始化為 Git 倉庫",
"CantSyncInSpecialGitStateAutoFixFailed": "無法同步,這個文件夾處在特殊狀態,不能直接進行同步,已嘗試自動修復,但還是出現錯誤,請先解決所有衝突(例如使用 VSCode 打開),如果還不行,請嘗試用專業 Git 工具Source Tree, GitKraken解決問題",
"CantSyncInSpecialGitStateAutoFixSucceed": "這個文件夾處在特殊狀態,本來不能直接進行同步,但已自動修復",
"CantSynchronizeAndSyncScriptIsInDeadLoop": "無法同步,而且同步腳本陷入死循環",
"CheckingLocalGitRepoSanity": "正在檢測本地 Git 倉庫是否正確地初始化了",
"CheckingLocalSyncState": "正在檢測本地狀態是否需要同步到雲端",
"CheckingRebaseStatus": "正在分析變基(Rebase)的處理方案",
"CommitComplete": "本地提交(Commit)完成",
"FailedToOpenDirectory": "無法打開文件夾 {{path}} {{errorMessage}}",
"FailedToOpenFile": "無法打開文件 {{path}} {{errorMessage}}",
"FetchingData": "正在拉取雲端數據,以便進行數據比對",
"FinishForcePull": "強制拉取完成",
"GitMergeFailed": "Git合併的結果不佳可能合併策略有漏洞",
"GitPushFailed": "Git上傳的結果不佳這通常意味著有網路問題",
"GitRepositoryConfigurationFinished": "Git 倉庫配置完畢",
"GitTokenExpireOrWrong": "Git 憑證(Token)已過期,需要重新登入一次,或憑證與使用者名稱不對應",
"GitTokenMissing": "Git 憑證(Token)缺失",
"HaveThingsToCommit": "有需要提交(Commit)的內容,正在自動提交",
"InitializeWikiGit": "正在初始化知識庫和 Git",
"InitializeWorkspaceView": "正在初始化工作區和瀏覽器窗口,並載入內容,請耐心等待",
"InitializeWorkspaceViewDone": "創建成功,即將載入內容",
"LocalAheadStartUpload": "本地狀態超前於雲端,開始上傳",
"LocalStateBehindSync": "本地狀態落後於雲端,開始合併雲端數據",
"LocalStateDivergeRebase": "本地狀態與雲端有分歧,開始變基(Rebase)",
"NoNeedToSync": "無需同步,本地狀態和雲端一致",
"PerformLastCheckBeforeSynchronizationFinish": "進行同步結束前最後的檢查",
"PrepareCloneOnlineWiki": "準備導入線上知識庫",
"PrepareSync": "準備同步,使用登錄的作者資訊",
"PreparingUserInfo": "正在配置身份資訊",
"RebaseConflictNeedsResolve": "變基(Rebase)時發現衝突,需要解決衝突",
"RebaseSucceed": "變基(Rebase)成功,開始上傳",
"SkipForcePull": "跳過強制拉取,遠端沒有更新",
"StartBackupToGithubRemote": "正在將知識庫所在的本地 Git 備份到 Github 雲端倉庫,需要的時間取決於網速,請耐心等待",
"StartConfiguringGithubRemoteRepository": "倉庫初始化完畢,開始配置 Git 雲端倉庫",
"StartFetchingFromGithubRemote": "正在拉取Github遠端倉庫的數據需要的時間取決於網速和倉庫大小請耐心等待",
"StartForcePull": "開始強制拉取遠端內容,將完全覆蓋本地",
"StartGitInitialization": "開始初始化本地 Git 倉庫",
"StartResettingLocalToRemote": "開始清空本地並用遠端內容覆蓋",
"SyncFailedSystemError": "同步失敗,同步系統可能出現問題",
"SynchronizationFailed": "同步失敗!你需要用 Github Desktop 等工具檢查當前 Git 倉庫的狀態。失敗可能是網路原因導致的,如果的確如此,可在調整網路後重試。",
"SynchronizationFinish": "同步完成"
},
"Menu": {
"ActualSize": "正常大小",
"Close": "關閉",
"CurrentWorkspace": "當前工作區",
"DeveloperToolsActiveWorkspace": "打開當前工作區的開發者工具",
"Edit": "編輯",
"ExportActiveTiddler": "導出當前筆記",
"ExportWholeWikiHTML": "導出整個知識庫為HTML存入文件夾",
"Find": "尋找",
"FindMatches": "個匹配",
"FindNext": "尋找下一個",
"FindPrevious": "尋找上一個",
"Help": "幫助",
"History": "歷史",
"Home": "首頁",
"Language": "語言/Lang",
"LearnMore": "了解更多...",
"PrintPage": "列印頁面",
"ReportBugViaGithub": "通過 GitHub 回饋問題...",
"RequestFeatureViaGithub": "通過 GitHub 提新需求...",
"SelectNextWorkspace": "選擇下一個工作區",
"SelectPreviousWorkspace": "選擇前一個工作區",
"TidGi": "太記",
"TidGiMenuBar": "太記小窗",
"View": "查看",
"Wiki": "知識庫",
"Window": "窗口",
"Workspaces": "工作區列表",
"ZoomIn": "放大",
"ZoomOut": "縮小"
},
"No": "不了",
"Open": "打開",
"Preference": {
"AlwaysOnTop": "保持窗口在其他窗口上方",
"AlwaysOnTopDetail": "讓太記的主窗口永遠保持在其它窗口上方,不會被其他窗口覆蓋",
"AntiAntiLeech": "有的網站做了防盜鏈,會阻止某些圖片在你的知識庫上顯示,我們透過模擬訪問該網站的請求頭來繞過這種限制。",
"AskDownloadLocation": "下載前詢問每個文件的保存位置",
"AttachToMenuBar": "附加到選單欄",
"AttachToMenuBarShowSidebar": "附加到選單欄的窗口包含側邊欄",
"AttachToMenuBarShowSidebarTip": "一般太記小窗僅用於快速查看當前工作區,所以默認與主窗口工作區同步,不需要側邊欄,默認隱藏側邊欄。",
"AttachToMenuBarTip": "創建一個點擊選單欄/任務欄圖示會彈出的小太記窗口。提示:右鍵單擊小圖示以訪問上下文菜單。",
"AttachToTaskbar": "附加到任務欄",
"AttachToTaskbarShowSidebar": "附加到任務欄的窗口包含側邊欄",
"ChooseLanguage": "選擇語言 Choose Language",
"ClearBrowsingData": "清空瀏覽器數據不影響Git內容",
"ClearBrowsingDataDescription": "清除Cookie、快取等",
"ClearBrowsingDataMessage": "你確定嗎?所有瀏覽數據將被清除。此操作無法撤消。",
"ConfirmDelete": "確認刪除",
"ConfirmDeleteExternalApiDatabase": "確定要刪除包含外部 API Debug 資訊的資料庫嗎?此操作無法撤銷。",
"DarkTheme": "黑暗主題",
"DefaultUserName": "默認編輯者名",
"DefaultUserNameDetail": "在知識庫中預設使用的編輯者名,將在創建或編輯條目時填入 creator 欄位。可以被工作區內設置的編輯者名覆蓋。",
"DeleteExternalApiDatabase": "刪除外部 API 資料庫",
"DeveloperTools": "開發者工具",
"DisableAntiAntiLeech": "禁用反防盜鏈",
"DisableAntiAntiLeechDetail": "勾選以完全禁用反防盜鏈功能",
"DisableAntiAntiLeechForUrls": "為以下網址禁用反防盜鏈",
"DisableAntiAntiLeechForUrlsDetail": "輸入每行一個網址,單獨為這些網址禁用反防盜鏈功能,因為該功能可能會導致一些帶有反反防盜鏈功能的網站無法載入圖片。",
"DownloadLocation": "下載位置",
"Downloads": "下載",
"ExternalApiDatabaseDescription": "包含外部 API Debug 資訊的資料庫,占用空間為 {{size}}",
"FriendLinks": "友鏈",
"General": "界面和交互",
"HibernateAllUnusedWorkspaces": "在程序啟動時休眠所有未使用的工作區",
"HibernateAllUnusedWorkspacesDescription": "啟動時休眠所有工作區,但上次關閉前最後使用的活動工作區除外。",
"HideMenuBar": "隱藏選單欄",
"HideMenuBarDetail": "按下 Alt + M 可以顯示被隱藏的選單欄",
"HideSideBar": "隱藏側邊欄",
"HideSideBarIconDetail": "隱藏圖示只顯示工作區的名字,讓工作區列表更緊湊",
"HideTitleBar": "隱藏標題欄",
"HowToEnableNotifications": "<0>TidGi支持原生通知功能。但在某些情況下要接收通知您需要手動配置一些Web應用設定。</0><1>了解詳情</1><2>。</2>",
"IgnoreCertificateErrors": "忽略網路證書錯誤",
"IgnoreCertificateErrorsDescription": "<0>不建議。</0><1>了解詳情</1>。",
"ItIsWorking": "好使的!",
"Languages": "語言/Lang",
"LightTheme": "亮色主題",
"MenubarAlwaysOnTop": "保持選單欄小窗口在其他窗口上方",
"MenubarAlwaysOnTopDetail": "讓太記的選單欄小窗口永遠保持在其它窗口上方,不會被其他窗口覆蓋",
"Miscellaneous": "其他設置",
"MoreWorkspaceSyncSettings": "更多工作區同步設定",
"MoreWorkspaceSyncSettingsDescription": "請右鍵工作區圖示,點右鍵菜單裡的「編輯工作區」來打開工作區設置,在裡面配各個工作區的同步設定。",
"Network": "網路",
"Notifications": "通知",
"NotificationsDetail": "設置通知暫停時間",
"NotificationsDisableSchedule": "按時間自動禁用通知:",
"NotificationsMuteAudio": "暫停通知時也同時靜音工作區",
"OpenAtLogin": "開機自啟",
"OpenAtLoginMinimized": "開機自啟並最小化(MacOS)",
"OpenLogFolder": "打開Log文件夾",
"OpenLogFolderDetail": "上報問題時,請打開日期最新的一個 .log 文件,將其內容發送給開發者,或黏貼到 pastebin.com 後將 URL 黏貼到 Github Issue 裡",
"OpenMetaDataFolder": "打開太記工作區元資訊文件夾",
"OpenMetaDataFolderDetail": "太微的數據和太記的工作區數據是分開存放的,太記的封包含工作區的設置等,它們以 JSON 形式存放在這個文件夾裡。",
"OpenV8CacheFolder": "打開V8快取文件夾",
"OpenV8CacheFolderDetail": "V8快取文件夾存有加速應用啟動的快取文件",
"Performance": "性能",
"PrivacyAndSecurity": "隱私和安全",
"ReceivePreReleaseUpdates": "接收預發布更新",
"RememberLastVisitState": "記住上次訪問的頁面,恢復打開時的上次訪問狀態",
"RequireRestart": "需要重啟",
"Reset": "你確定嗎?所有首選項都將恢復為其原始預設值。瀏覽數據不會受到影響。此操作無法撤消。",
"ResetNow": "立即重設",
"RestorePreferences": "將所有設置都恢復為其原始預設值",
"RunOnBackground": "保持後台運行",
"RunOnBackgroundDetail": "在窗口關閉時不退出,繼續保持後台運行。再次打開應用時快速還原窗口。",
"RunOnBackgroundDetailNotMac": "建議開啟太記小窗,以便透過選單欄/任務欄圖示重新打開窗口。",
"Search": "搜索和嵌入",
"SearchEmbeddingDelete": "刪除",
"SearchEmbeddingDeleteConfirm": "確定要刪除工作區\"{{workspaceName}}\"的所有向量嵌入嗎?此操作無法撤銷。",
"SearchEmbeddingDeleteError": "刪除嵌入失敗:{{error}}",
"SearchEmbeddingGenerate": "生成嵌入",
"SearchEmbeddingGenerating": "生成中...",
"SearchEmbeddingLastUpdated": "最後更新:{{time}}",
"SearchEmbeddingNoAIConfigError": "請先在外部API部分配置AI API設置。",
"SearchEmbeddingStatusCompleted": "{{totalNotes}}個筆記的{{totalEmbeddings}}個嵌入",
"SearchEmbeddingStatusError": "錯誤:{{error}}",
"SearchEmbeddingStatusGenerating": "生成中... ({{completed}}/{{total}})",
"SearchEmbeddingStatusIdle": "未生成嵌入",
"SearchEmbeddingUpdate": "更新嵌入",
"SearchNoWorkspaces": "未找到工作區",
"ShareBrowsingData": "在工作區之間共享瀏覽器數據cookies、快取等關閉後可以每個工作區登不同的第三方服務帳號。",
"ShowSideBar": "顯示側邊欄",
"ShowSideBarDetail": "側邊欄讓你可以在工作區之間快速切換",
"ShowSideBarIcon": "展示側邊欄工作區圖示",
"ShowSideBarText": "展示側邊欄上按鈕的文本",
"ShowTitleBar": "顯示標題欄",
"ShowTitleBarDetail": "在標題欄上會顯示當前頁面的標題",
"SpellCheck": "拼寫檢查",
"SpellCheckLanguages": "首選拼寫檢查語言",
"Support": "支持",
"SwipeWithThreeFingersToNavigate": "用三根手指輕掃來前進後退",
"SwipeWithThreeFingersToNavigateDescription": "使用3個指手勢在頁面之間導航。向左輕掃可返回向右輕掃可前進。<br/>要啟用它,還需要更改<3>macOS首選項 → 觸控板 → 更多手勢 → 在頁面間輕掃</3>到<5>用三個手指輕掃</5>或<7>用兩個或三個手指輕掃。</7>",
"Sync": "同步和備份",
"SyncBeforeShutdown": "在關機前自動同步",
"SyncBeforeShutdownDescription": "關電腦前自動同步數據注意手動退出應用不會觸發同步以防應用出錯時將錯誤數據同步上去。Windows 系統不支持此功能。",
"SyncInterval": "同步/備份間隔",
"SyncIntervalDescription": "每經過這段長度的時間後,就會自動開始備份到 Github如果工作區是本地工作區則會創建本地備份重啟後生效",
"SyncOnlyWhenNoDraft": "在沒有草稿時才同步",
"SyncOnlyWhenNoDraftDescription": "在同步前檢查有沒有草稿或處於所見即所得編輯狀態的條目,如果有則本次不同步,防止將草稿同步到你的部落格裡。(對關機前自動同步無效,畢竟你很可能希望將草稿從一台電腦上帶到另一台電腦上繼續編輯)",
"System": "系統",
"SystemDefaultTheme": "系統默認主題色",
"TestNotification": "測試通知功能",
"TestNotificationDescription": "<0>如果通知未顯示,請確保在<1>macOS首選項 → 通知 → TidGi中啟用通知</1></0>",
"Theme": "主題色",
"TiddlyWiki": "太微",
"ToggleMenuBar": "切換顯隱選單欄",
"Token": "Git身份憑證",
"TokenDescription": "用於向Git伺服器驗證身份並同步內容的憑證可透過登錄在線儲存服務如Github來取得也可以手動獲取「Personal Access Token」後填到這裡。",
"Translatium": "翻譯素APP",
"TranslatiumIntro": "像外語系大佬一樣翻譯任何語言",
"Updates": "更新",
"WebCatalog": "網站目錄App",
"WebCatalogEngineIntro": "「網站目錄App」是TidGi的最初代碼的來源我們重用了來自開源的「網站目錄App」的許多重要代碼這要感謝「網站目錄App」及其作者 Quang Lam",
"WebCatalogIntro": "神奇地將任何網站變成跨平台的應用程式。\n讓你更加高效地工作而無需在瀏覽器上來回切換瀏覽器Tab。",
"WebSite": "官網",
"WikiMetaData": "Wiki元資訊",
"WikiMetaDataDescription": "配置Wiki的啟動參數",
"hardwareAcceleration": "使用硬體加速"
},
"Save": "保存",
"Scripting": {
"ExecutingScript": "正在執行腳本"
},
"SideBar": {
"Preferences": "設置...",
"UpdateAvailable": "有新版本!"
},
"Unknown": "未知",
"Update": "更新",
"Updater": {
"CheckUpdate": "檢查更新",
"CheckingFailed": "檢查更新失敗(網路錯誤)",
"CheckingForUpdate": "檢查更新中…",
"UpdateAvailable": "有新版本可用!",
"UpdateNotAvailable": "目前已是最新版"
},
"WorkspaceSelector": {
"Add": "添加",
"Agent": "智慧體",
"AreYouSure": "你確定要移除這個工作區嗎?移除工作區會刪除本應用中的工作區,但不會刪除硬碟上的文件夾。如果你選擇一併刪除知識庫文件夾,則所有內容都會被刪除。",
"DedicatedWorkspace": "特殊工作區",
"DefaultTiddlers": "默認條目",
"EditCurrentWorkspace": "配置當前工作區",
"EditWorkspace": "配置工作區",
"Guide": "引導",
"Help": "幫助",
"HibernateWorkspace": "休眠工作區",
"OpenInBrowser": "用瀏覽器打開",
"OpenInBrowserDisabledHint": "(啟用 HTTP API 才能使用)",
"OpenWorkspaceFolder": "打開文件夾",
"OpenWorkspaceFolderInEditor": "用外部編輯器打開文件夾",
"OpenWorkspaceFolderInGitGUI": "用可視化Git工具打開",
"OpenWorkspaceMenuName": "打開工作區",
"OpenWorkspaceTagTiddler": "打開 {{tagName}}",
"ReloadCurrentWorkspace": "刷新當前工作區",
"RemoveCurrentWorkspace": "移除當前工作區",
"RemoveWorkspace": "移除工作區",
"RemoveWorkspaceAndDelete": "移除工作區並刪除知識庫文件夾",
"WakeUpWorkspace": "喚醒工作區"
},
"Yes": "是的"
}

View file

@ -1,6 +1,7 @@
{
"en": "English",
"zh_CN": "简中",
"zh-Hans": "汉字",
"zh-Hant": "漢字",
"ja": "日本語",
"fr": "Français",
"ru": "Русский"

View file

@ -3,5 +3,6 @@
"fr": "$:/languages/fr-FR",
"ja": "$:/languages/ja-JP",
"ru": "$:/languages/ru-RU",
"zh_CN": "$:/languages/zh-Hans"
"zh-Hans": "$:/languages/zh-Hans",
"zh-Hant": "$:/languages/zh-Hant"
}

View file

@ -4,37 +4,64 @@
"description": "Customizable personal knowledge-base with Github as unlimited storage and blogging platform.",
"version": "0.12.4",
"license": "MPL 2.0",
"packageManager": "pnpm@10.13.1",
"scripts": {
"start": "pnpm run clean && pnpm run init:git-submodule && pnpm run start:without-clean",
"start:without-clean": "pnpm run build:plugin && cross-env DEBUG=electron-packager NODE_ENV=development electron-forge start",
"clean": "rimraf -- ./out ./userData-dev ./node_modules/tiddlywiki/plugins/linonetwo && cross-env NODE_ENV=development npx ts-node scripts/developmentMkdir.ts",
"clean:cache": "rimraf -- ./.webpack ./node_modules/.cache",
"start:without-clean:debug-worker": "pnpm run build:plugin && cross-env NODE_ENV=development DEBUG_WORKER=true electron-forge start",
"start:without-clean:debug-main": "pnpm run build:plugin && cross-env NODE_ENV=development DEBUG_MAIN=true electron-forge start",
"start:init": "pnpm run clean && pnpm run init:git-submodule && pnpm run build:plugin && cross-env NODE_ENV=development pnpm dlx tsx scripts/developmentMkdir.ts && pnpm run start:dev",
"start:dev": "cross-env NODE_ENV=development electron-forge start",
"clean": "rimraf -- ./out ./logs ./userData-dev ./userData-test ./wiki-dev ./wiki-test ./node_modules/tiddlywiki/plugins/linonetwo && pnpm run clean:cache",
"clean:cache": "rimraf -- ./.vite ./node_modules/.cache",
"start:dev:debug-worker": "cross-env NODE_ENV=development DEBUG_WORKER=true electron-forge start",
"start:dev:debug-main": "cross-env NODE_ENV=development DEBUG_MAIN=true electron-forge start",
"start:dev:debug-vite": "cross-env NODE_ENV=development DEBUG=electron-forge:* electron-forge start",
"start:dev:debug-react": "cross-env NODE_ENV=development DEBUG_REACT=true electron-forge start",
"build:plugin": "zx scripts/compilePlugins.mjs",
"test": "pnpm run clean && cross-env NODE_ENV=test pnpm run package && pnpm run test:without-package",
"test:without-package": "mkdir -p logs && cross-env NODE_ENV=test cucumber-js",
"package": "pnpm run build:plugin && electron-forge package",
"make:mac-x64": "pnpm run build:plugin && cross-env NODE_ENV=production electron-forge make --platform=darwin --arch=x64",
"make:mac-arm": "pnpm run build:plugin && cross-env NODE_ENV=production electron-forge make --platform=darwin --arch=arm64",
"make:win-x64": "pnpm run build:plugin && cross-env NODE_ENV=production electron-forge make --platform=win32 --arch=x64",
"make:win-ia32": "pnpm run build:plugin && cross-env NODE_ENV=production electron-forge make --platform=win32 --arch=ia32",
"make:win-arm": "pnpm run build:plugin && cross-env NODE_ENV=production electron-forge make --platform=win32 --arch=arm64",
"make:linux-x64": "pnpm run build:plugin && cross-env NODE_ENV=production electron-forge make --platform=linux --arch=x64",
"make:linux-arm": "pnpm run build:plugin && cross-env NODE_ENV=production electron-forge make --platform=linux --arch=arm64",
"test": "pnpm run test:unit && pnpm run test:prepare-e2e && pnpm run test:e2e",
"test:unit": "cross-env ELECTRON_RUN_AS_NODE=1 ./node_modules/.bin/electron ./node_modules/vitest/vitest.mjs run",
"test:unit:coverage": "pnpm run test:unit --coverage",
"test:prepare-e2e": "pnpm run clean && pnpm run build:plugin && cross-env NODE_ENV=test DEBUG=electron-forge:* electron-forge package",
"test:e2e": "rimraf -- ./userData-test ./wiki-test && cross-env NODE_ENV=test pnpm dlx tsx scripts/developmentMkdir.ts && cross-env NODE_ENV=test cucumber-js --config features/cucumber.config.js",
"make": "pnpm run build:plugin && cross-env NODE_ENV=production electron-forge make",
"make:analyze": "cross-env ANALYZE=true pnpm run make",
"init:git-submodule": "git submodule update --init --recursive && git submodule update --remote",
"lint": "eslint ./src --ext js,ts,tsx,json",
"lint:fix": "eslint ./src --ext js,ts,tsx,json --fix",
"lint:fix": "eslint ./src --ext ts,tsx --fix",
"check": "tsc --noEmit --skipLibCheck",
"installType": "typesync"
},
"repository": "https://github.com/tiddly-gittly/TidGi-Desktop",
"author": "Lin Onetwo <linonetwo012@gmail.com>, Quang Lam <quang.lam2807@gmail.com>",
"main": ".webpack/main",
"main": ".vite/build/main.js",
"dependencies": {
"@ai-sdk/anthropic": "^1.2.11",
"@ai-sdk/deepseek": "^0.2.14",
"@ai-sdk/openai": "^1.3.22",
"@ai-sdk/openai-compatible": "^0.2.14",
"@algolia/autocomplete-js": "^1.19.1",
"@algolia/autocomplete-theme-classic": "^1.19.1",
"@dnd-kit/core": "6.3.1",
"@dnd-kit/modifiers": "9.0.0",
"@dnd-kit/sortable": "10.0.0",
"@dnd-kit/utilities": "3.2.2",
"@dr.pogodin/react-helmet": "^3.0.2",
"@fontsource/roboto": "^5.1.1",
"@monaco-editor/react": "^4.7.0",
"@mui/icons-material": "^7.1.1",
"@mui/material": "^7.1.1",
"@mui/system": "^7.1.1",
"@mui/types": "^7.4.3",
"@mui/x-date-pickers": "^8.4.0",
"@rjsf/core": "6.0.0-beta.8",
"@rjsf/mui": "6.0.0-beta.10",
"@rjsf/utils": "6.0.0-beta.10",
"@rjsf/validator-ajv8": "6.0.0-beta.8",
"ai": "^4.3.15",
"ansi-to-html": "^0.7.2",
"app-path": "^4.0.0",
"best-effort-json-parser": "1.1.2",
"beautiful-react-hooks": "5.0.3",
"best-effort-json-parser": "1.1.3",
"better-sqlite3": "^11.9.1",
"bluebird": "3.7.2",
"date-fns": "3.6.0",
"default-gateway": "6.0.3",
"dugite": "2.7.1",
"electron-dl": "^4.0.0",
@ -46,69 +73,76 @@
"exponential-backoff": "^3.1.1",
"fs-extra": "11.3.0",
"git-sync-js": "^2.0.5",
"graphql-hooks": "8.2.0",
"html-minifier-terser": "^7.2.0",
"i18next": "24.2.2",
"i18next": "25.2.1",
"i18next-electron-fs-backend": "3.0.3",
"i18next-fs-backend": "2.6.0",
"immer": "^10.1.1",
"intercept-stdout": "0.1.2",
"inversify": "6.2.1",
"inversify-inject-decorators": "3.1.0",
"ipaddr.js": "2.2.0",
"jimp": "1.6.0",
"json5": "^2.2.3",
"lodash": "4.17.21",
"material-ui-popup-state": "^5.3.3",
"material-ui-popup-state": "^5.3.6",
"menubar": "9.5.1",
"monaco-editor": "^0.52.2",
"nanoid": "^5.0.9",
"new-github-issue-url": "^1.0.0",
"node-fetch": "3.3.2",
"ollama-ai-provider": "^1.2.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-i18next": "15.5.2",
"react-masonry-css": "^1.0.16",
"react-window": "^1.8.11",
"reflect-metadata": "0.2.2",
"registry-js": "1.16.0",
"registry-js": "1.16.1",
"rotating-file-stream": "^3.2.5",
"rxjs": "7.8.1",
"semver": "7.7.0",
"rxjs": "7.8.2",
"semver": "7.7.2",
"simplebar": "6.3.1",
"simplebar-react": "3.3.0",
"source-map-support": "0.5.21",
"sqlite-vec": "0.1.7-alpha.2",
"strip-ansi": "^7.1.0",
"threads": "1.7.0",
"tapable": "^2.2.2",
"tiddlywiki": "5.3.7",
"type-fest": "4.33.0",
"type-fest": "4.41.0",
"typeorm": "^0.3.22",
"typescript-styled-is": "^2.1.0",
"v8-compile-cache-lib": "^3.0.1",
"winston": "3.17.0",
"winston-daily-rotate-file": "5.0.0",
"winston-transport": "4.9.0",
"wouter": "^3.5.1",
"zx": "8.3.1"
"wouter": "^3.7.1",
"zod": "^3.25.28",
"zustand": "^5.0.4",
"zx": "8.5.5"
},
"optionalDependencies": {
"@electron-forge/maker-deb": "7.6.1",
"@electron-forge/maker-flatpak": "7.6.1",
"@electron-forge/maker-rpm": "7.6.1",
"@electron-forge/maker-snap": "7.6.1",
"@electron-forge/maker-squirrel": "7.6.1",
"@electron-forge/maker-zip": "7.6.1",
"@reforged/maker-appimage": "^4.0.4",
"electron-squirrel-startup": "1.0.1"
"@electron-forge/maker-deb": "7.8.1",
"@electron-forge/maker-flatpak": "7.8.1",
"@electron-forge/maker-rpm": "7.8.1",
"@electron-forge/maker-snap": "7.8.1",
"@electron-forge/maker-squirrel": "7.8.1",
"@electron-forge/maker-zip": "7.8.1",
"@reforged/maker-appimage": "^5.0.0"
},
"devDependencies": {
"@cucumber/cucumber": "11.2.0",
"@dnd-kit/core": "6.3.1",
"@dnd-kit/modifiers": "9.0.0",
"@dnd-kit/sortable": "10.0.0",
"@dnd-kit/utilities": "3.2.2",
"@electron-forge/cli": "7.6.1",
"@electron-forge/plugin-auto-unpack-natives": "7.6.1",
"@electron-forge/plugin-webpack": "7.6.1",
"@electron/rebuild": "^3.7.1",
"@fontsource/roboto": "^5.1.1",
"@mui/icons-material": "^6.4.2",
"@mui/lab": "5.0.0-alpha.170",
"@mui/material": "^6.4.2",
"@mui/styled-engine-sc": "6.4.2",
"@mui/styles": "^6.4.2",
"@mui/x-date-pickers": "^7.25.0",
"@cucumber/cucumber": "^11.2.0",
"@electron-forge/cli": "7.8.1",
"@electron-forge/plugin-auto-unpack-natives": "7.8.1",
"@electron-forge/plugin-vite": "^7.9.0",
"@electron/rebuild": "^4.0.1",
"@fetsorn/vite-node-worker": "^1.0.1",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@types/better-sqlite3": "^7.6.13",
"@types/bluebird": "3.5.42",
"@types/chai": "5.0.1",
"@types/circular-dependency-plugin": "5.0.8",
"@types/fs-extra": "11.0.4",
"@types/html-minifier-terser": "^7.0.2",
"@types/i18next-fs-backend": "1.1.5",
@ -117,63 +151,51 @@
"@types/node": "22.13.0",
"@types/react": "19.0.8",
"@types/react-dom": "19.0.3",
"@types/react-helmet": "6.1.11",
"@types/react-jsonschema-form": "^1.7.13",
"@types/semver": "7.5.8",
"@types/source-map-support": "0.5.10",
"@types/styled-components": "5.1.34",
"@types/webpack-bundle-analyzer": "4.7.0",
"@types/webpack-node-externals": "3.0.4",
"@vercel/webpack-asset-relocator-loader": "1.7.3",
"beautiful-react-hooks": "5.0.2",
"@vitejs/plugin-react": "^5.0.4",
"@vitest/coverage-v8": "^3.2.3",
"@vitest/ui": "^3.2.3",
"chai": "5.1.2",
"circular-dependency-plugin": "5.2.2",
"copy-webpack-plugin": "12.0.2",
"cross-env": "7.0.3",
"css-loader": "6.11.0",
"date-fns": "3.6.0",
"dprint": "^0.48.0",
"electron": "34.0.2",
"esbuild": "^0.24.2",
"esbuild-loader": "^4.2.2",
"eslint-config-tidgi": "2.0.7",
"fork-ts-checker-webpack-plugin": "9.0.2",
"graphql-hooks": "8.2.0",
"json5": "^2.2.3",
"dprint": "^0.50.0",
"electron": "36.4.0",
"electron-chrome-web-store": "^0.12.0",
"esbuild": "^0.25.2",
"eslint-config-tidgi": "^2.2.0",
"identity-obj-proxy": "^3.0.0",
"jsdom": "^26.1.0",
"memory-fs": "^0.5.0",
"node-loader": "2.1.0",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-helmet": "6.1.0",
"react-i18next": "15.4.0",
"path-browserify": "^1.0.1",
"playwright": "^1.53.0",
"rimraf": "^6.0.1",
"simplebar": "6.3.0",
"simplebar-react": "3.3.0",
"style-loader": "4.0.0",
"styled-components": "6.1.14",
"threads-plugin": "1.4.0",
"ts-import-plugin": "3.0.0",
"ts-loader": "9.5.2",
"ts-node": "10.9.2",
"tw5-typed": "^0.5.14",
"typescript": "5.7.3",
"typescript-plugin-styled-components": "3.0.0",
"typesync": "0.14.0",
"webpack-bundle-analyzer": "4.10.2",
"webpack-node-externals": "3.0.0",
"webpack5-externals-plugin": "1.0.4",
"webpackbar": "7.0.0"
"tw5-typed": "^0.6.3",
"typescript": "5.8.3",
"typesync": "0.14.3",
"unplugin-swc": "^1.5.5",
"vite": "^7.1.9",
"vite-bundle-analyzer": "^1.2.3",
"vitest": "^3.2.3"
},
"pnpm": {
"overrides": {
"prebuild-install": "latest"
},
"onlyBuiltDependencies": [
"@swc/core",
"better-sqlite3",
"dprint",
"dugite",
"electron",
"electron-winstaller",
"esbuild",
"registry-js"
]
"registry-js",
"unrs-resolver"
],
"patchedDependencies": {}
},
"private": false
}

View file

@ -1,22 +0,0 @@
diff --git a/dist/config.js b/dist/config.js
index 4932902ac605eaa2c6d134fbcfdd7aae23d6accb..c3139159b4545fcc261b42afd84edd7a75a1368b 100644
--- a/dist/config.js
+++ b/dist/config.js
@@ -10,7 +10,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
const env = envVar.from(process.env);
export const llamaDirectory = path.join(__dirname, "..", "llama");
export const llamaToolchainsDirectory = path.join(llamaDirectory, "toolchains");
-export const llamaPrebuiltBinsDirectory = path.join(__dirname, "..", "llamaBins");
+export const llamaPrebuiltBinsDirectory = global.LLAMA_PREBUILT_BINS_DIRECTORY || path.join(__dirname, "..", "llamaBins");
export const llamaLocalBuildBinsDirectory = path.join(llamaDirectory, "localBuilds");
export const llamaBinsGrammarsDirectory = path.join(__dirname, "..", "llama", "grammars");
export const llamaCppDirectory = path.join(llamaDirectory, "llama.cpp");
@@ -27,7 +27,7 @@ export const localXpacksCacheDirectory = path.join(xpackDirectory, "cache");
export const buildMetadataFileName = "_nlcBuildMetadata.json";
export const xpmVersion = "^0.16.3";
export const builtinLlamaCppGitHubRepo = "ggerganov/llama.cpp";
-export const builtinLlamaCppRelease = await getBinariesGithubRelease();
+export const builtinLlamaCppRelease = "b2608";
export const isCI = env.get("CI")
.default("false")
.asBool();

30
patches/threads.patch Normal file
View file

@ -0,0 +1,30 @@
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index 05ed95f5686c5f7b5754ad02414fe90951036c2d..0000000000000000000000000000000000000000
diff --git a/package.json b/package.json
index c4ef12336424053c1d8f921d0366e36e7c12961a..4b29ad3673a5fcb59cf6e180880388fb7137f8e6 100644
--- a/package.json
+++ b/package.json
@@ -23,18 +23,22 @@
},
"exports": {
".": {
+ "types": "./dist/index.d.ts",
"require": "./dist/index.js",
"default": "./index.mjs"
},
"./observable": {
+ "types": "./observable.d.ts",
"require": "./observable.js",
"default": "./observable.mjs"
},
"./register": {
+ "types": "./register.d.ts",
"require": "./register.js",
"default": "./register.mjs"
},
"./worker": {
+ "types": "./worker.d.ts",
"require": "./worker.js",
"default": "./worker.mjs"
}

8985
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -1,148 +0,0 @@
/* eslint-disable security-node/detect-crlf */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable unicorn/import-style */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable unicorn/prevent-abbreviations */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/**
* Remove all .lproj files
* Based on https://ganeshrvel.medium.com/electron-builder-afterpack-configuration-5c2c986be665
* Adapted for electron forge https://github.com/electron-userland/electron-forge/issues/2248
*/
const path = require('path');
const fs = require('fs-extra');
const packageJSON = require('../package.json');
/**
* Specific which lproj you want to keep
*/
const keepingLprojRegEx = /(en|zh_CN)\.lproj/g;
/**
* Running postMake hook
* @param {*} buildPath /var/folders/qj/7j0zx32d0l75zmnrl1w3m3b80000gn/T/electron-packager/darwin-x64/TidGi-darwin-x64/Electron.app/Contents/Resources/app
* @param {*} electronVersion 12.0.6
* @param {*} platform darwin / win32 (even on win11 x64)
* @param {*} arch x64
* @returns
*/
exports.default = async (
buildPath,
electronVersion,
platform,
arch,
callback,
) => {
const cwd = path.resolve(buildPath, '..');
const projectRoot = path.resolve(__dirname, '..');
// const appParentPath = path.resolve(buildPath, '..', '..', '..', '..');
// const appPath = path.join(appParentPath, 'Electron.app');
// const shell = platform === 'darwin' ? '/bin/zsh' : undefined;
// const winMacLinuxPlatformName = platform === 'darwin' ? 'mac' : (platform === 'win32' ? 'win' : 'linux');
/** delete useless lproj files to make it clean */
// const lproj = glob.sync('*.lproj', { cwd });
const lproj = fs.readdirSync(cwd).filter((dir) => dir.endsWith('.lproj'));
const pathsToRemove = lproj
.filter((dir) => !keepingLprojRegEx.test(dir))
.map((dir) => path.join(cwd, dir));
if (platform === 'darwin') {
await Promise.all(pathsToRemove.map(async (dir) => {
await fs.remove(dir);
}));
}
console.log(`copy npm packages with node-worker dependencies with binary (dugite) or __filename usages (tiddlywiki), which can't be prepare properly by webpack`);
if (['production', 'test'].includes(process.env.NODE_ENV)) {
console.log('Copying tiddlywiki dependency to dist');
const sourceNodeModulesFolder = path.resolve(projectRoot, 'node_modules');
fs.cpSync(
path.join(sourceNodeModulesFolder, 'zx'),
path.join(cwd, 'node_modules', 'zx'),
{ dereference: true, recursive: true },
);
// not using pnpm, because after using it, it always causing problem here, causing `Error: spawn /bin/sh ENOENT` in github actions
// it can probably being "working directory didn't exist" in https://github.com/nodejs/node/issues/9644#issuecomment-282060923
// exec(`pnpm i --shamefully-hoist --prod --ignore-scripts`, { cwd: path.join(cwd, 'node_modules', 'zx'), shell });
// exec(`npm i --legacy-building --omit=dev`, {
// cwd: path.join(cwd, 'node_modules', 'zx'),
// shell,
// });
// exec(`npm i --legacy-building --omit=dev`, {
// cwd: path.join(cwd, 'node_modules', 'zx', 'node_modules', 'globby'),
// shell,
// });
// exec(`npm i --legacy-building --omit=dev --ignore-scripts`, {
// cwd: path.join(
// cwd,
// 'node_modules',
// 'zx',
// 'node_modules',
// 'node-fetch',
// ),
// shell,
// });
const packagePathsToCopyDereferenced = [
['tiddlywiki', 'package.json'],
['tiddlywiki', 'boot'],
['tiddlywiki', 'core'],
// only copy plugins that is used in src/services/wiki/wikiWorker/startNodeJSWiki.ts , other plugins can be installed via JSON from online plugin library
['tiddlywiki', 'plugins', 'linonetwo'],
['tiddlywiki', 'plugins', 'tiddlywiki', 'filesystem'],
['tiddlywiki', 'plugins', 'tiddlywiki', 'tiddlyweb'],
['tiddlywiki', 'tiddlywiki.js'],
// we only need its `main` binary, no need its dependency and code, because we already copy it to src/services/native/externalApp
['app-path', 'main'],
];
console.log(`Copying packagePathsToCopyDereferenced`);
for (const packagePathInNodeModules of packagePathsToCopyDereferenced) {
// some binary may not exist in other platforms, so allow failing here.
try {
fs.copySync(
path.resolve(sourceNodeModulesFolder, ...packagePathInNodeModules),
path.resolve(cwd, 'node_modules', ...packagePathInNodeModules),
{ dereference: true, recursive: true },
);
} catch (error) {
// some binary may not exist in other platforms, so allow failing here.
console.error(
`Error copying ${
packagePathInNodeModules.join(
'/',
)
} to dist, in afterPack.js, Error: ${error.message}`,
);
}
}
console.log('Copy dugite');
// it has things like `git/bin/libexec/git-core/git-add` link to `git/bin/libexec/git-core/git`, to reduce size, so can't use `dereference: true, recursive: true` here.
// And pnpm will have node_modules/dugite to be a shortcut, can't just copy it with `dereference: false`, have to copy from .pnpm folder
fs.copySync(
path.join(
sourceNodeModulesFolder,
'.pnpm',
`dugite@${packageJSON.dependencies.dugite}`,
'node_modules',
'dugite',
),
path.join(cwd, 'node_modules', 'dugite'),
{
dereference: false,
recursive: true,
},
);
}
/** sign it for mac m1 https://www.zhihu.com/question/431722091/answer/1592339574 (only work if user run this.)
* And have error
* ```
* An unhandled rejection has occurred inside Forge:
Error: Command failed: xattr -rd com.apple.quarantine /var/folders/t3/0jyr287x3rd2m0b6ml8w4f2c0000gn/T/electron-packager/darwin-x64/TidGi-darwin-x64-8UwtyU/Electron.app
xattr: No such file: /var/folders/t3/0jyr287x3rd2m0b6ml8w4f2c0000gn/T/electron-packager/darwin-x64/TidGi-darwin-x64-8UwtyU/Electron.app/Contents/Resources/node_modules/dugite
```
*/
// if (platform === 'darwin') {
// exec(`xattr -rd com.apple.quarantine ${appPath}`, { cwd: appParentPath, shell });
// }
/** complete this hook */
callback();
};

89
scripts/afterPack.ts Normal file
View file

@ -0,0 +1,89 @@
/**
* Copy necessary dependencies after packaging
* Based on https://ganeshrvel.medium.com/electron-builder-afterpack-configuration-5c2c986be665
* Adapted for electron forge https://github.com/electron-userland/electron-forge/issues/2248
*/
import fs from 'fs-extra';
import path from 'path';
/**
* Running afterPack hook
* Note: This must be a non-async function that accepts a callback for Electron Packager compatibility
* @param buildPath /var/folders/qj/7j0zx32d0l75zmnrl1w3m3b80000gn/T/electron-packager/darwin-x64/TidGi-darwin-x64/Electron.app/Contents/Resources/app
* @param electronVersion 12.0.6
* @param platform darwin / win32 (even on win11 x64)
* @param arch x64
* @param callback Callback to signal completion
*/
export default (
buildPath: string,
_electronVersion: string,
_platform: string,
_arch: string,
callback: () => void,
): void => {
const cwd = path.resolve(buildPath, '..');
const projectRoot = path.resolve(__dirname, '..');
console.log('Copy npm packages with node-worker dependencies with binary (dugite) or __filename usages (tiddlywiki), which cannot be prepared properly by webpack');
if (['production', 'test'].includes(process.env.NODE_ENV ?? '')) {
console.log('Copying tiddlywiki dependency to dist');
const sourceNodeModulesFolder = path.resolve(projectRoot, 'node_modules');
fs.cpSync(
path.join(sourceNodeModulesFolder, 'zx'),
path.join(cwd, 'node_modules', 'zx'),
{ dereference: true, recursive: true },
);
const packagePathsToCopyDereferenced: string[][] = [
['tiddlywiki', 'package.json'],
['tiddlywiki', 'boot'],
['tiddlywiki', 'core'],
// only copy plugins that is used in src/services/wiki/wikiWorker/startNodeJSWiki.ts, other plugins can be installed via JSON from online plugin library
['tiddlywiki', 'plugins', 'linonetwo'],
['tiddlywiki', 'plugins', 'tiddlywiki', 'filesystem'],
['tiddlywiki', 'plugins', 'tiddlywiki', 'tiddlyweb'],
['tiddlywiki', 'tiddlywiki.js'],
// we only need its `main` binary, no need its dependency and code, because we already copy it to src/services/native/externalApp
['app-path', 'main'],
// node binary
['better-sqlite3', 'build', 'Release', 'better_sqlite3.node'],
// Refer to `node_modules\sqlite-vec\index.cjs` for latest file names
// sqlite-vec: copy main entry files and platform-specific binary
['sqlite-vec', 'package.json'],
['sqlite-vec', 'index.cjs'],
[`sqlite-vec-${process.platform === 'win32' ? 'windows' : process.platform}-${process.arch}`],
];
console.log('Copying packagePathsToCopyDereferenced');
for (const packagePathInNodeModules of packagePathsToCopyDereferenced) {
// some binary may not exist in other platforms, so allow failing here.
try {
fs.copySync(
path.resolve(sourceNodeModulesFolder, ...packagePathInNodeModules),
path.resolve(cwd, 'node_modules', ...packagePathInNodeModules),
{ dereference: true },
);
} catch (error) {
// some binary may not exist in other platforms, so allow failing here.
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(
`Error copying ${packagePathInNodeModules.join('/')} to dist, in afterPack.ts: ${errorMessage}`,
);
}
}
console.log('Copy dugite');
// it has things like `git/bin/libexec/git-core/git-add` link to `git/bin/libexec/git-core/git`, to reduce size, so can't use `dereference: true, recursive: true` here.
fs.copySync(
path.join(sourceNodeModulesFolder, 'dugite'),
path.join(cwd, 'node_modules', 'dugite'),
{ dereference: false },
);
}
/** complete this hook */
callback();
};

View file

@ -1,31 +0,0 @@
/* eslint-disable @typescript-eslint/no-confusing-void-expression */
/* eslint-disable @typescript-eslint/promise-function-async */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable unicorn/import-style */
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable security/detect-child-process */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable unicorn/prevent-abbreviations */
/**
* Remove all .lproj files
* Based on https://ganeshrvel.medium.com/electron-builder-afterpack-configuration-5c2c986be665
* Adapted for electron forge https://github.com/electron-userland/electron-forge/issues/2248
*/
const path = require('path');
const fs = require('fs-extra');
/**
* @param {*} buildPath /var/folders/qj/7j0zx32d0l75zmnrl1w3m3b80000gn/T/electron-packager/darwin-x64/TidGi-darwin-x64/Electron.app/Contents/Resources/app
* @param {*} electronVersion 12.0.6
* @param {*} platform darwin
* @param {*} arch x64
* @returns
*/
exports.default = async (buildPath, electronVersion, platform, arch, callback) => {
const cwd = path.resolve(buildPath, '..');
const pathsToRemove = ['.webpack/main/localization/', '.webpack/main/native_modules/dist/', '.webpack/out/'].map((directory) => path.join(cwd, directory));
await Promise.all(pathsToRemove.map((directory) => fs.remove(directory).catch((error) => console.error(error))));
/** complete this hook */
callback();
};

46
scripts/beforeAsar.ts Normal file
View file

@ -0,0 +1,46 @@
/**
* Remove unnecessary directories before creating asar archive
* Based on https://ganeshrvel.medium.com/electron-builder-afterpack-configuration-5c2c986be665
* Adapted for electron forge https://github.com/electron-userland/electron-forge/issues/2248
*/
import fs from 'fs-extra';
import path from 'path';
/**
* Running beforeAsar hook
* Note: This must be a non-async function that accepts a callback for Electron Packager compatibility
* @param buildPath /var/folders/qj/7j0zx32d0l75zmnrl1w3m3b80000gn/T/electron-packager/darwin-x64/TidGi-darwin-x64/Electron.app/Contents/Resources/app
* @param electronVersion 12.0.6
* @param platform darwin
* @param arch x64
* @param callback Callback to signal completion
*/
export default (
buildPath: string,
_electronVersion: string,
_platform: string,
_arch: string,
callback: () => void,
): void => {
const cwd = path.resolve(buildPath, '..');
const pathsToRemove = ['.webpack/main/localization/', '.webpack/main/native_modules/dist/', '.webpack/out/'].map((directory) => path.join(cwd, directory));
// Execute async operations and call callback when done
Promise.all(
pathsToRemove.map(async (directory) => {
try {
await fs.remove(directory);
} catch (error: unknown) {
console.error(error);
}
}),
)
.then(() => {
/** complete this hook */
callback();
})
.catch((error: unknown) => {
console.error('Error in beforeAsar hook:', error);
callback();
});
};

View file

@ -1,7 +1,7 @@
import fs from 'fs-extra';
import { DEFAULT_WIKI_FOLDER } from '../src/constants/paths';
import { DEFAULT_FIRST_WIKI_FOLDER_PATH } from '../src/constants/paths';
try {
fs.removeSync(DEFAULT_WIKI_FOLDER);
fs.removeSync(DEFAULT_FIRST_WIKI_FOLDER_PATH);
} catch {}
fs.mkdirpSync(DEFAULT_WIKI_FOLDER);
fs.mkdirpSync(DEFAULT_FIRST_WIKI_FOLDER_PATH);

22
scripts/start-e2e-app.ts Normal file
View file

@ -0,0 +1,22 @@
// pnpm exec cross-env NODE_ENV=test pnpm dlx tsx ./scripts/start-e2e-app.ts
import { spawn } from 'child_process';
import { getPackedAppPath } from '../features/supports/paths';
// You can also use `pnpm dlx tsx scripts/startMockOpenAI.ts`
const appPath = getPackedAppPath();
console.log('Starting TidGi E2E app:', appPath);
const env = Object.assign({}, process.env, {
NODE_ENV: 'test',
LANG: process.env.LANG || 'zh-Hans.UTF-8',
LANGUAGE: process.env.LANGUAGE || 'zh-Hans:zh',
LC_ALL: process.env.LC_ALL || 'zh-Hans.UTF-8',
});
const child = spawn(appPath, [], { env, stdio: 'inherit' });
child.on('exit', code => process.exit(code ?? 0));
child.on('error', err => {
console.error('Failed to start TidGi app:', err);
process.exit(1);
});

View file

@ -0,0 +1,55 @@
// pnpm dlx tsx scripts/startMockOpenAI.ts
import { MockOpenAIServer } from '../features/supports/mockOpenAI';
async function main() {
// 使用固定端口 15121 用于手动测试和 E2E 测试
const server = new MockOpenAIServer(15121);
console.log('启动 Mock OpenAI 服务器...');
try {
await server.start();
console.log(`✅ Mock OpenAI 服务器已启动:`);
console.log(` URL: ${server.baseUrl}`);
console.log(` 端口: ${server.port}`);
console.log(` Health Check: ${server.baseUrl}/health`);
console.log('');
console.log('测试命令示例:');
console.log(`# Health Check:`);
console.log(`curl ${server.baseUrl}/health`);
console.log('');
console.log(`# Chat Completions:`);
console.log(`curl -X POST ${server.baseUrl}/v1/chat/completions \\`);
console.log(' -H "Content-Type: application/json" \\');
console.log(' -H "Authorization: Bearer test-key" \\');
console.log(' -d \'{"model": "test-model", "messages": [{"role": "user", "content": "搜索 wiki 中的 index 条目并解释"}]}\'');
console.log('');
console.log('PowerShell 测试命令:');
console.log(`Invoke-RestMethod -Uri "${server.baseUrl}/health"`);
console.log('');
console.log('按 Ctrl+C 停止服务器');
// 保持服务器运行
process.on('SIGINT', async () => {
console.log('\n正在停止服务器...');
await server.stop();
process.exit(0);
});
process.on('SIGTERM', async () => {
console.log('\n正在停止服务器...');
await server.stop();
process.exit(0);
});
// 防止进程退出 - 使用 setInterval 而不是空的 Promise
const keepAlive = setInterval(() => {
// 每10秒输出一次状态确认服务器还在运行
}, 10000);
} catch (error) {
console.error('❌ 启动服务器失败:', error);
process.exit(1);
}
}
main().catch(console.error);

View file

@ -0,0 +1,49 @@
import { vi } from 'vitest';
interface TranslationOptions {
ns?: string;
defaultValue?: string;
[key: string]: unknown;
}
/**
* Simple template string interpolation for testing
* Replaces {{key}} with values from options object
*/
function interpolateTemplate(template: string, options?: TranslationOptions): string {
if (!options) return template;
let result = template;
Object.entries(options).forEach(([key, value]) => {
if (key !== 'ns' && key !== 'defaultValue') {
const placeholder = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
result = result.replace(placeholder, String(value));
}
});
return result;
}
export const useTranslation = () => ({
t: (key: string, options?: string | TranslationOptions) => {
if (typeof options === 'string') return options;
if (typeof options === 'object' && options?.defaultValue) return options.defaultValue;
// Return the key with interpolated values for testing
return interpolateTemplate(key, options as TranslationOptions);
},
i18n: {
changeLanguage: vi.fn(),
},
});
export const getI18n = () => ({
t: (key: string, options?: string | TranslationOptions) => {
if (typeof options === 'string') return options;
if (typeof options === 'object' && options?.defaultValue) return options.defaultValue;
// Return the key with interpolated values for testing
return interpolateTemplate(key, options as TranslationOptions);
},
changeLanguage: vi.fn(),
});
export const Trans = ({ children }: { children: React.ReactNode }) => children;

View file

@ -0,0 +1,194 @@
import type { AIStreamResponse, IExternalAPIService } from '@/services/externalAPI/interface';
import { AgentBrowserService } from '@services/agentBrowser';
import { AgentDefinitionService } from '@services/agentDefinition';
import { AgentInstanceService } from '@services/agentInstance';
import { container } from '@services/container';
import type { IContextService } from '@services/context/interface';
import { DatabaseService } from '@services/database';
import { ExternalAPIService } from '@services/externalAPI';
import type { INativeService } from '@services/native/interface';
import type { IPreferenceService } from '@services/preferences/interface';
import serviceIdentifier from '@services/serviceIdentifier';
import { SupportedStorageServices } from '@services/types';
import type { IWikiService } from '@services/wiki/interface';
import { WikiEmbeddingService } from '@services/wikiEmbedding';
import type { IWindowService } from '@services/windows/interface';
import type { IWorkspace, IWorkspaceService } from '@services/workspaces/interface';
import type { IWorkspaceViewService } from '@services/workspacesView/interface';
import { Observable } from 'rxjs';
import { vi } from 'vitest';
// Mock bindServiceAndProxy to be an empty function
// This allows us to control service bindings in tests instead of using production bindings (while currently it is not called because it is called in `main.ts` and it is not executed during test.)
vi.mock('@services/libs/bindServiceAndProxy', () => ({
bindServiceAndProxy: vi.fn(),
}));
export const serviceInstances: {
workspace: Partial<IWorkspaceService>;
workspaceView: Partial<IWorkspaceViewService>;
window: Partial<IWindowService>;
native: Partial<INativeService>;
wiki: Partial<IWikiService>;
auth: Record<string, unknown>;
context: Partial<IContextService>;
preference: Partial<IPreferenceService>;
externalAPI: Partial<IExternalAPIService>;
} = {
workspace: {
countWorkspaces: vi.fn().mockResolvedValue(5),
openWorkspaceTiddler: vi.fn().mockResolvedValue(undefined),
// typed mocks for common methods tests will override; default returns shared fixtures
getWorkspacesAsList: vi.fn(async () => defaultWorkspaces),
exists: vi.fn(async (_id: string) => true),
get: vi.fn(async (id: string) => defaultWorkspaces.find(w => w.id === id) || defaultWorkspaces[0]),
// agent-instance functionality is provided under `agentInstance` key
},
workspaceView: {
// provide a properly-typed implementation wrapped by vi.fn
setActiveWorkspaceView: vi.fn().mockResolvedValue(undefined),
},
window: {
open: vi.fn().mockResolvedValue(undefined),
},
native: {
log: vi.fn().mockResolvedValue(undefined),
pickDirectory: vi.fn().mockResolvedValue(['/test/selected/path']),
},
wiki: {
getSubWikiPluginContent: vi.fn().mockResolvedValue([]),
// generic wikiOperationInServer mock: keep simple, allow test-specific overrides
wikiOperationInServer: vi.fn().mockResolvedValue([]) as IWikiService['wikiOperationInServer'],
},
auth: {
getStorageServiceUserInfo: vi.fn().mockResolvedValue(undefined),
},
context: {
get: vi.fn().mockResolvedValue(undefined),
},
preference: (() => {
const store: Record<string, unknown> = {};
return {
get: vi.fn(async (key: string) => store[key]),
set: vi.fn(async (key: string, value: unknown) => {
store[key] = value;
}),
resetWithConfirm: vi.fn(async () => undefined),
} as Partial<IPreferenceService>;
})(),
externalAPI: {
getAIConfig: vi.fn(async () => ({ api: { model: 'test-model', provider: 'test-provider' }, modelParameters: {} })),
getAIProviders: vi.fn(async () => []),
generateFromAI: vi.fn(async function*() {
// harmless await for linter
await Promise.resolve();
yield { requestId: 'r0', content: '', status: 'start' } as AIStreamResponse;
return;
}),
streamFromAI: vi.fn((_messages, _config) =>
new Observable<AIStreamResponse>((subscriber) => {
subscriber.next({ requestId: 'r1', content: 'ok', status: 'start' });
subscriber.next({ requestId: 'r1', content: 'ok', status: 'done' });
subscriber.complete();
})
),
generateEmbeddings: vi.fn(async () => ({
requestId: 'test-request',
embeddings: [[0.1, 0.2, 0.3, 0.4]], // Default 4D embedding
model: 'test-embedding-model',
object: 'embedding',
usage: {
prompt_tokens: 10,
total_tokens: 10,
},
status: 'done' as const,
})),
cancelAIRequest: vi.fn(async () => undefined),
updateProvider: vi.fn(async () => undefined),
deleteProvider: vi.fn(async () => undefined),
updateDefaultAIConfig: vi.fn(async () => undefined),
deleteFieldFromDefaultAIConfig: vi.fn(async () => undefined),
},
};
// Bind the shared mocks into container so real services resolved from container.get()
// will receive these mocks during tests.
container.bind(serviceIdentifier.Workspace).toConstantValue(serviceInstances.workspace);
container.bind(serviceIdentifier.WorkspaceView).toConstantValue(serviceInstances.workspaceView);
container.bind(serviceIdentifier.Window).toConstantValue(serviceInstances.window);
container.bind(serviceIdentifier.NativeService).toConstantValue(serviceInstances.native);
container.bind(serviceIdentifier.Wiki).toConstantValue(serviceInstances.wiki);
container.bind(serviceIdentifier.ExternalAPI).to(ExternalAPIService).inSingletonScope();
container.bind(serviceIdentifier.Preference).toConstantValue(serviceInstances.preference);
container.bind(serviceIdentifier.Context).toConstantValue(serviceInstances.context);
container.bind(serviceIdentifier.Authentication).toConstantValue(serviceInstances.auth);
container.bind(serviceIdentifier.AgentDefinition).to(AgentDefinitionService).inSingletonScope();
container.bind(serviceIdentifier.AgentBrowser).to(AgentBrowserService).inSingletonScope();
// Bind real DatabaseService instead of mock
container.bind(serviceIdentifier.Database).to(DatabaseService).inSingletonScope();
container.bind(serviceIdentifier.AgentInstance).to(AgentInstanceService).inSingletonScope();
container.bind(serviceIdentifier.WikiEmbedding).to(WikiEmbeddingService).inSingletonScope();
// Shared workspace fixtures used by many tests
const defaultWorkspaces: IWorkspace[] = [
{
id: 'test-wiki-1',
name: 'Test Wiki 1',
wikiFolderLocation: '/path/to/test-wiki-1',
homeUrl: 'http://localhost:5212/',
port: 5212,
isSubWiki: false,
mainWikiToLink: null,
tagName: null,
lastUrl: null,
active: true,
hibernated: false,
order: 0,
disableNotifications: false,
backupOnInterval: false,
disableAudio: false,
enableHTTPAPI: false,
excludedPlugins: [],
gitUrl: null,
hibernateWhenUnused: false,
readOnlyMode: false,
storageService: SupportedStorageServices.local,
subWikiFolderName: 'subwiki',
syncOnInterval: false,
syncOnStartup: false,
tokenAuth: false,
transparentBackground: false,
userName: '',
picturePath: null,
},
{
id: 'test-wiki-2',
name: 'Test Wiki 2',
wikiFolderLocation: '/path/to/test-wiki-2',
homeUrl: 'http://localhost:5213/',
port: 5213,
isSubWiki: false,
mainWikiToLink: null,
tagName: null,
lastUrl: null,
active: true,
hibernated: false,
order: 1,
disableNotifications: false,
backupOnInterval: false,
disableAudio: false,
enableHTTPAPI: false,
excludedPlugins: [],
gitUrl: null,
hibernateWhenUnused: false,
readOnlyMode: false,
storageService: SupportedStorageServices.local,
subWikiFolderName: 'subwiki',
syncOnInterval: false,
syncOnStartup: false,
tokenAuth: false,
transparentBackground: false,
userName: '',
picturePath: null,
},
];

View file

@ -0,0 +1,7 @@
import { vi } from 'vitest';
export const logger = {
error: vi.fn(),
info: vi.fn(),
debug: vi.fn(),
warn: vi.fn(),
};

View file

@ -0,0 +1,81 @@
import { AgentInstanceService } from '@services/agentInstance';
import { AgentInstanceMessage } from '@services/agentInstance/interface';
import { AgentPromptDescription } from '@services/agentInstance/promptConcat/promptConcatSchema';
import { container } from '@services/container';
import serviceIdentifier from '@services/serviceIdentifier';
import { BehaviorSubject, Observable } from 'rxjs';
import { vi } from 'vitest';
import { serviceInstances } from './services-container';
// Mock window.meta
globalThis.window = globalThis.window || {};
Object.defineProperty(window, 'meta', {
writable: true,
value: vi.fn(() => ({
windowName: 'main',
})),
});
// Mock window.remote
Object.defineProperty(window, 'remote', {
writable: true,
value: {
registerOpenFindInPage: vi.fn(),
registerCloseFindInPage: vi.fn(),
registerUpdateFindInPageMatches: vi.fn(),
unregisterOpenFindInPage: vi.fn(),
unregisterCloseFindInPage: vi.fn(),
unregisterUpdateFindInPageMatches: vi.fn(),
},
});
// Mock window.observables
Object.defineProperty(window, 'observables', {
writable: true,
value: {
preference: {
preference$: new BehaviorSubject({}).asObservable(),
},
workspace: {
workspaces$: new BehaviorSubject([]).asObservable(),
},
updater: {
updaterMetaData$: new BehaviorSubject(undefined).asObservable(),
},
auth: {
userInfo$: new BehaviorSubject(undefined).asObservable(),
},
agentInstance: {
concatPrompt: vi.fn((promptDescription: Pick<AgentPromptDescription, 'handlerConfig'>, messages: AgentInstanceMessage[]) => {
const agentInstanceService = container.get<AgentInstanceService>(serviceIdentifier.AgentInstance);
// Initialize handlers (plugins and built-in handlers) before calling concatPrompt
// We need to wrap this in an Observable since concatPrompt returns an Observable
return new Observable((observer) => {
const initAndCall = async () => {
try {
// Need to register plugins first. In test environment, this needs to be called manually. While in real
// environment, this is handled in `main.ts` when app start.
await agentInstanceService.initializeHandlers();
const resultObservable = agentInstanceService.concatPrompt(promptDescription, messages);
// Subscribe to the result and forward to our observer
resultObservable.subscribe(observer);
} catch (_error: unknown) {
// Log but keep test mocks resilient
console.warn(`Error while inserting dom node in react widget, this might be cause by use transclude widget for the wikitext contains widget.`, _error);
void _error;
observer.error(_error);
}
};
void initAndCall();
});
}),
},
},
});
// Mock window.service
Object.defineProperty(window, 'service', {
writable: true,
value: serviceInstances,
});

View file

@ -0,0 +1,30 @@
/**
* Simple example tests to verify that the test configuration is working correctly
*/
import { describe, expect, test, vi } from 'vitest';
describe('Environment Verification', () => {
test('Basic Vitest functionality works', () => {
expect(1 + 1).toBe(2);
});
test('TypeScript support works', () => {
const message: string = 'Hello, TidGi!';
expect(message).toBe('Hello, TidGi!');
});
test('Vitest mock functionality works', () => {
const mockFunction = vi.fn();
mockFunction('test');
expect(mockFunction).toHaveBeenCalledWith('test');
});
test('reflect-metadata decorator support', () => {
// Verify that reflect-metadata is loaded
expect(Reflect.getMetadata).toBeDefined();
});
test('Environment variables are set correctly', () => {
expect(process.env.NODE_ENV).toBe('test');
});
});

View file

@ -0,0 +1,8 @@
import 'reflect-metadata';
// Optimize Jest environment variables
process.env.NODE_OPTIONS = '--max-old-space-size=4096';
process.env.TS_NODE_COMPILER_OPTIONS = '{"skipLibCheck":true,"isolatedModules":true}';
// Disable some development mode warnings
process.env.SKIP_PREFLIGHT_CHECK = 'true';

View file

@ -0,0 +1,144 @@
import 'reflect-metadata';
import '@testing-library/jest-dom/vitest';
import { configure } from '@testing-library/dom';
configure({ computedStyleSupportsPseudoElements: false });
// Fix for JSDOM getComputedStyle issue - strip unsupported second parameter
const originalGetComputedStyle = window.getComputedStyle;
window.getComputedStyle = (elt) => originalGetComputedStyle.call(window, elt);
import './__mocks__/window';
import './__mocks__/services-container';
import { vi } from 'vitest';
vi.mock('react-i18next', () => import('./__mocks__/react-i18next'));
vi.mock('@services/libs/log', () => import('./__mocks__/services-log'));
/**
* Mock the `electron` module for testing
*
* CRITICAL: This mock is essential for proper test environment isolation.
*
* Why this mock is necessary:
* 1. In real Electron, app.setPath() and app.getPath() manage application directories
* 2. appPaths.ts calls app.setPath('userData', 'userData-test') in test environment
* 3. Without a proper mock, these calls would be no-ops and paths would be wrong
* 4. This leads to test databases/settings being created in wrong directories
*
* What this mock provides:
* - Functional app.setPath() that actually stores path values
* - Functional app.getPath() that retrieves stored paths
* - Proper test isolation by ensuring userData goes to 'userData-test/'
*
* This mock enables appPaths.ts to work correctly in tests, ensuring:
* - CACHE_DATABASE_FOLDER = 'userData-test/cache-database/'
* - SETTINGS_FOLDER = 'userData-test/settings/'
* - No pollution of project root directory during tests
*/
vi.mock('electron', () => {
// Create a mock that can store and retrieve userData path
let userDataPath = process.cwd(); // default
const mockApp = {
setPath: (key: string, value: string) => {
if (key === 'userData') {
userDataPath = value;
}
},
getPath: (key: string) => {
if (key === 'userData') return userDataPath;
if (key === 'home') return process.cwd();
return process.cwd();
},
// Provide version and name used by ContextService
getVersion: () => '0.0.0',
name: 'TidGi',
};
return {
default: {
app: mockApp,
},
// Also provide named export `app` to satisfy `import { app } from 'electron'`
app: mockApp,
};
});
// Import appPaths to ensure the path setup is executed during test initialization
// This is critical - without this import, appPaths.ts won't be evaluated and
// app.setPath('userData', 'userData-test') won't be called!
import '@/constants/appPaths';
// Some build-time globals (injected by bundlers) are not defined in test env.
// Provide them here to avoid ReferenceError when modules reference them.
(global as unknown as Record<string, unknown>).MAIN_WINDOW_VITE_DEV_SERVER_URL = undefined;
/**
* Mock matchMedia and other DOM APIs for components using autocomplete search functionality
*
* Why this mock is necessary:
* - @algolia/autocomplete-js uses matchMedia() to detect mobile devices for responsive behavior
* - @algolia/autocomplete-js also tries to access document/window event properties that don't exist in JSDOM
* - JSDOM test environment doesn't provide matchMedia() API by default
* - Without this mock, components using TemplateSearch or Search will throw errors
* - This enables CreateNewAgentContent and other search-related components to render in tests
*
* Components that need this:
* - CreateNewAgentContent (uses TemplateSearch)
* - NewTabContent (uses Search)
* - Any component using Search.tsx or autocomplete functionality
*/
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation((query: string) => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(), // deprecated
removeListener: vi.fn(), // deprecated
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
});
// Mock document and window with comprehensive event handling for autocomplete components
Object.defineProperty(document, 'documentElement', {
writable: true,
value: Object.assign(document.documentElement || document.createElement('html'), {
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
mousedown: vi.fn(),
ontouchstart: vi.fn(),
}),
});
Object.defineProperty(document, 'body', {
writable: true,
value: Object.assign(document.body || document.createElement('body'), {
mousedown: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
ontouchstart: vi.fn(),
}),
});
// Enhanced window mock with comprehensive event support
Object.defineProperty(window, 'addEventListener', {
writable: true,
value: vi.fn(),
});
Object.defineProperty(window, 'removeEventListener', {
writable: true,
value: vi.fn(),
});
// Mock touch events for autocomplete
Object.defineProperty(window, 'ontouchstart', {
writable: true,
value: vi.fn(),
});
// Prevent unhandled promise rejections from autocomplete
window.addEventListener = vi.fn();
window.removeEventListener = vi.fn();

View file

@ -0,0 +1,41 @@
import { Alert, Snackbar } from '@mui/material';
import React, { useCallback, useState } from 'react';
export interface ShowInfoSnackbarOptions {
message: string;
severity?: 'success' | 'info' | 'warning' | 'error';
}
export function useInfoSnackbar(): [
(options: ShowInfoSnackbarOptions) => void,
React.JSX.Element,
] {
const [open, setOpen] = useState(false);
const [message, setMessage] = useState('');
const [severity, setSeverity] = useState<'success' | 'info' | 'warning' | 'error'>('info');
const showInfoSnackbar = useCallback((options: ShowInfoSnackbarOptions) => {
setMessage(options.message);
setSeverity(options.severity || 'info');
setOpen(true);
}, []);
const handleClose = useCallback(() => {
setOpen(false);
}, []);
const InfoSnackbarComponent = (
<Snackbar
open={open}
autoHideDuration={6000}
onClose={handleClose}
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
>
<Alert onClose={handleClose} severity={severity} variant='filled'>
{message}
</Alert>
</Snackbar>
);
return [showInfoSnackbar, InfoSnackbarComponent];
}

View file

@ -1,5 +1,5 @@
import { List as ListRaw, ListItem as ListItemRaw, ListItemText as ListItemTextRaw } from '@mui/material';
import { styled } from 'styled-components';
import { styled } from '@mui/material/styles';
export const List = styled(ListRaw)`
& > li > div {
@ -7,7 +7,7 @@ export const List = styled(ListRaw)`
padding-bottom: 0;
}
`;
export const ListItem: typeof ListItemRaw = styled(ListItemRaw)`
export const ListItem = styled((props: React.ComponentProps<typeof ListItemRaw>) => <ListItemRaw {...props} />)`
svg {
color: ${({ theme }) => theme.palette.action.active};
}
@ -19,7 +19,7 @@ export const ListItem: typeof ListItemRaw = styled(ListItemRaw)`
color: ${({ theme }) => theme.palette.text.primary};
}
`;
export const ListItemText: typeof ListItemTextRaw = styled(ListItemTextRaw)`
export const ListItemText = styled((props: React.ComponentProps<typeof ListItemTextRaw>) => <ListItemTextRaw {...props} />)`
color: ${({ theme }) => theme.palette.text.primary};
input {
color: ${({ theme }) => theme.palette.text.primary};

View file

@ -1,10 +1,10 @@
import { Close as CloseIcon } from '@mui/icons-material';
import CloseIcon from '@mui/icons-material/Close';
import { Button, IconButton, Snackbar, Tooltip } from '@mui/material';
import { keyframes, styled } from '@mui/material/styles';
import { IWorkspace } from '@services/workspaces/interface';
import useDebouncedCallback from 'beautiful-react-hooks/useDebouncedCallback';
import React, { useCallback, useState } from 'react';
import { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import styled, { keyframes } from 'styled-components';
const progressAnimation = keyframes`
from {
@ -91,7 +91,7 @@ export function useRestartSnackbar(
<Snackbar
anchorOrigin={anchorOrigin}
open={opened}
onClose={(event, reason) => {
onClose={(_event, reason) => {
switch (reason) {
case 'timeout': {
if (inCountDown) {

View file

@ -1,6 +1,6 @@
import { styled, css } from 'styled-components';
import { css, styled } from '@mui/material/styles';
export const RootStyle = styled.div`
export const RootStyle = styled('div')`
.Mui-selected,
.Mui-checked {
${({ theme }) =>

View file

@ -1,20 +1,21 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
import { styled } from '@mui/material/styles';
import useDebouncedCallback from 'beautiful-react-hooks/useDebouncedCallback';
import Promise from 'bluebird';
import { ClientContext, GraphQLClient, useMutation, useQuery } from 'graphql-hooks';
import { trim } from 'lodash';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { styled } from 'styled-components';
import { Cached as CachedIcon, CreateNewFolder as CreateNewFolderIcon, Folder as FolderIcon } from '@mui/icons-material';
import CachedIcon from '@mui/icons-material/Cached';
import CreateNewFolderIcon from '@mui/icons-material/CreateNewFolder';
import FolderIcon from '@mui/icons-material/Folder';
import { Button, LinearProgress, List, ListItemButton, ListItemIcon, ListItemText, TextField } from '@mui/material';
import { GITHUB_GRAPHQL_API } from '@/constants/auth';
import { useUserInfoObservable } from '@services/auth/hooks';
import { IGithubSearchNode, IGithubSearchRepoQuery } from './interfaces';
const RepoSearchContainer = styled.div`
const RepoSearchContainer = styled('div')`
margin-top: 20px;
`;
const RepoSearchInput = styled(TextField)``;
@ -111,7 +112,9 @@ function SearchGithubRepoResultList({
const onSelectRepo = useCallback(
(url: string, name: string) => {
githubWikiUrlSetter(url);
typeof wikiFolderNameSetter === 'function' && wikiFolderNameSetter(name);
if (typeof wikiFolderNameSetter === 'function') {
wikiFolderNameSetter(name);
}
},
[githubWikiUrlSetter, wikiFolderNameSetter],
);
@ -135,7 +138,6 @@ function SearchGithubRepoResultList({
return () => {
clearTimeout(timeoutHandle);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [githubUsername, accessToken]);
// try refetch on error
const [retryInterval, retryIntervalSetter] = useState(100);
@ -149,13 +151,12 @@ function SearchGithubRepoResultList({
clearTimeout(timeoutHandle);
};
}
return () => {};
// eslint-disable-next-line react-hooks/exhaustive-deps
return undefined;
}, [error, githubUsername, accessToken, retryInterval]);
const [createRepository] = useMutation(CREATE_REPO_MUTATION);
const repositoryCount = data?.search?.repositoryCount;
const repositoryCount = data?.search.repositoryCount;
const repoList: IGithubSearchNode[] = useMemo(
() => (data !== undefined && (repositoryCount ?? 0) > 0 ? data.search.edges.map(({ node }) => node) : []),
[data, repositoryCount],
@ -163,13 +164,13 @@ function SearchGithubRepoResultList({
// auto select first one after first search
useEffect(() => {
if (githubWikiUrl?.length === 0 && repoList.length > 0) {
if (githubWikiUrl.length === 0 && repoList.length > 0) {
onSelectRepo(repoList[0].url, repoList[0].name);
}
}, [repoList, githubWikiUrl, onSelectRepo]);
const [isCreatingRepo, isCreatingRepoSetter] = useState(false);
const githubUserID = data?.repositoryOwner?.id;
const githubUserID = data?.repositoryOwner.id;
const wikiUrlToCreate = `https://github.com/${githubUsername ?? '???'}/${githubRepoSearchString}`;
const isCreateNewRepo = trim(githubWikiUrl) === wikiUrlToCreate;
const githubPagesUrl = `https://${githubUsername ?? '???'}.github.io/${githubRepoSearchString}`;
@ -241,7 +242,7 @@ function SearchGithubRepoResultList({
)}
</List>
{repoList.length === 0 && (
<ReloadButton color='secondary' endIcon={<CachedIcon />} onClick={async () => await refetchDebounced()}>
<ReloadButton color='secondary' endIcon={<CachedIcon />} onClick={() => void refetchDebounced()}>
{t('AddWorkspace.Reload')}
</ReloadButton>
)}

View file

@ -1,7 +1,7 @@
import { Button, TextField } from '@mui/material';
import { styled } from '@mui/material/styles';
import useDebouncedCallback from 'beautiful-react-hooks/useDebouncedCallback';
import { useTranslation } from 'react-i18next';
import { styled } from 'styled-components';
import { useUserInfoObservable } from '@services/auth/hooks';
import { IUserInfos } from '@services/auth/interface';
@ -12,7 +12,7 @@ import { useAuth, useGetGithubUserInfoOnLoad } from './gitTokenHooks';
const AuthingLoginButton = styled(Button)`
width: 100%;
`;
const GitTokenInput = styled(TextField)`
const GitTokenInput = styled((props: React.ComponentProps<typeof TextField> & { helperText?: string }) => <TextField fullWidth variant='standard' {...props} />)`
color: ${({ theme }) => theme.palette.text.primary};
input {
color: ${({ theme }) => theme.palette.text.primary};
@ -22,10 +22,6 @@ const GitTokenInput = styled(TextField)`
color: ${({ theme }) => theme.palette.text.secondary};
}
`;
GitTokenInput.defaultProps = {
fullWidth: true,
variant: 'standard',
};
export function GitTokenForm(props: {
children?: React.JSX.Element | Array<React.JSX.Element | undefined | string>;

View file

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-confusing-void-expression */
/* eslint-disable @typescript-eslint/await-thenable */
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
import { SupportedStorageServices } from '@services/types';
import { truncate } from 'lodash';
import { useCallback, useEffect } from 'react';
@ -11,7 +11,7 @@ export function useAuth(storageService: SupportedStorageServices): [() => Promis
await window.service.auth.set(`${storageService}-token`, '');
// await window.service.window.clearStorageData();
} catch (error) {
console.error(error);
void window.service.native.log('error', 'TokenForm: auth operation failed', { function: 'useAuth', error: String(error) });
}
}, [storageService]);
@ -66,7 +66,7 @@ export function useGetGithubUserInfoOnLoad(): void {
await window.service.auth.setUserInfos(userInfo);
}
} catch (error) {
console.error(error);
void window.service.native.log('error', 'TokenForm: get github user info failed', { function: 'useGetGithubUserInfoOnLoad', error: String(error) });
}
});
}, []);

View file

@ -1,23 +1,21 @@
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import { TabContext, TabPanel as TabPanelRaw } from '@mui/lab';
import { Tab as TabRaw, Tabs as TabsRaw } from '@mui/material';
import { Box, Tab as TabRaw, Tabs as TabsRaw } from '@mui/material';
import { keyframes, styled, Theme } from '@mui/material/styles';
import { SupportedStorageServices } from '@services/types';
import React, { useEffect, useState } from 'react';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import styled, { DefaultTheme, keyframes } from 'styled-components';
import { ListItemText } from '../ListItem';
import { GitTokenForm } from './GitTokenForm';
const Container = styled.div`
const Container = styled('div')`
width: 100%;
display: flex;
flex-direction: column;
background-color: ${({ theme }) => theme.palette.background.paper};
`;
const TabPanel = styled(TabPanelRaw)`
padding: 5px 0 !important;
padding-left: 16px !important;
const TabPanel = styled(Box)`
padding: 5px 0;
padding-left: 16px;
background-color: ${({ theme }) => theme.palette.background.paper};
`;
const Tabs = styled(TabsRaw)`
@ -26,17 +24,17 @@ const Tabs = styled(TabsRaw)`
background-color: ${({ theme }) => theme.palette.background.paper} !important;
}
`;
const TabsContainer = styled.div`
const TabsContainer = styled('div')`
background-color: ${({ theme }) => theme.palette.background.paper};
color: ${({ theme }) => theme.palette.text.primary};
display: flex;
padding: 15px 0;
flex-direction: row;
& ${Tabs} {
& .MuiTabs-root {
min-width: 160px;
}
`;
const backgroundColorShift = ({ theme }: { theme: DefaultTheme }) =>
const backgroundColorShift = ({ theme }: { theme: Theme }) =>
keyframes`
from {background-color: ${theme.palette.background.default};}
to {background-color: ${theme.palette.background.default};}
@ -74,10 +72,10 @@ export function TokenForm({ storageProvider, storageProviderSetter }: Props): Re
return (
<Container>
<ListItemText primary={t('Preference.Token')} secondary={t('Preference.TokenDescription')} />
<TabContext value={currentTab}>
<Box sx={{ display: 'flex', width: '100%' }}>
<TabsContainer>
<Tabs
onChange={(event: React.SyntheticEvent<Element, Event>, newValue: SupportedStorageServices) => {
onChange={(_event: React.SyntheticEvent, newValue: SupportedStorageServices) => {
currentTabSetter(newValue);
}}
orientation='vertical'
@ -89,17 +87,23 @@ export function TokenForm({ storageProvider, storageProviderSetter }: Props): Re
<Tab label='GitLab' value={SupportedStorageServices.gitlab} />
<Tab label='Gitee' value={SupportedStorageServices.gitee} />
</Tabs>
<TabPanel value={SupportedStorageServices.github}>
<GitTokenForm storageService={SupportedStorageServices.github} />
</TabPanel>
<TabPanel value={SupportedStorageServices.gitlab}>
<GitTokenForm storageService={SupportedStorageServices.gitlab} />
</TabPanel>
<TabPanel value={SupportedStorageServices.gitee}>
Gitee OAuth2
</TabPanel>
{currentTab === SupportedStorageServices.github && (
<TabPanel>
<GitTokenForm storageService={SupportedStorageServices.github} />
</TabPanel>
)}
{currentTab === SupportedStorageServices.gitlab && (
<TabPanel>
<GitTokenForm storageService={SupportedStorageServices.gitlab} />
</TabPanel>
)}
{currentTab === SupportedStorageServices.gitee && (
<TabPanel>
Gitee OAuth2
</TabPanel>
)}
</TabsContainer>
</TabContext>
</Box>
</Container>
);
}

View file

@ -1,4 +1,3 @@
/* eslint-disable react/display-name */
import { ForwardedRef, forwardRef } from 'react';
import { SVGContainer } from './SVGContainer';

View file

@ -1,6 +1,6 @@
import { styled } from 'styled-components';
import { styled } from '@mui/material/styles';
export const SVGContainer = styled.div`
export const SVGContainer = styled('div')`
& > svg {
width: 100%;
height: 100%;

View file

@ -0,0 +1,71 @@
import { app } from 'electron';
import path from 'path';
import { describe, expect, it } from 'vitest';
/**
* Tests for appPaths.ts
*
* NOTE: In test environment, the `electron` module is mocked in setup-vitest.ts
* The mock provides:
* - app.setPath(key, value): Stores path values in memory
* - app.getPath(key): Retrieves stored path values
*
* This allows appPaths.ts to correctly set userData path to 'userData-test'
* during test execution, ensuring test databases and settings are isolated
* from development/production data.
*/
describe('appPaths - Test Environment Path Configuration', () => {
it('should set userData path to userData-test directory in test environment', async () => {
// Import appPaths to trigger the path setup logic
await import('@/constants/appPaths');
const userDataPath = app.getPath('userData');
const expectedPath = path.resolve(process.cwd(), 'userData-test');
expect(userDataPath).toBe(expectedPath);
expect(userDataPath).toContain('userData-test');
});
it('should verify that CACHE_DATABASE_FOLDER uses the correct test path', async () => {
// Import the constants after appPaths has been loaded
const { CACHE_DATABASE_FOLDER } = await import('@/constants/appPaths');
const expectedCachePath = path.resolve(process.cwd(), 'userData-test', 'cache-database');
expect(CACHE_DATABASE_FOLDER).toBe(expectedCachePath);
expect(CACHE_DATABASE_FOLDER).toContain('userData-test');
expect(CACHE_DATABASE_FOLDER).toContain('cache-database');
});
it('should verify that SETTINGS_FOLDER uses the correct test path', async () => {
const { SETTINGS_FOLDER } = await import('@/constants/appPaths');
const expectedSettingsPath = path.resolve(process.cwd(), 'userData-test', 'settings');
expect(SETTINGS_FOLDER).toBe(expectedSettingsPath);
expect(SETTINGS_FOLDER).toContain('userData-test');
expect(SETTINGS_FOLDER).toContain('settings');
});
it('should verify environment detection is working correctly', async () => {
const { isTest } = await import('@/constants/environment');
// Verify we're in test environment
expect(process.env.NODE_ENV).toBe('test');
expect(process.env.VITEST).toBe('true');
expect(isTest).toBe(true);
});
it('should demonstrate that electron app mock is working', () => {
// This test documents how the electron mock works
const originalPath = app.getPath('userData');
// Test that setPath actually stores the value (unlike a no-op mock)
app.setPath('userData', '/some/test/path');
expect(app.getPath('userData')).toBe('/some/test/path');
// Restore original path
app.setPath('userData', originalPath);
expect(app.getPath('userData')).toBe(originalPath);
});
});

View file

@ -1,18 +1,43 @@
import { app } from 'electron';
import path from 'path';
import { __TEST__ as v8CompileCacheLibrary } from 'v8-compile-cache-lib';
import { isDevelopmentOrTest } from './environment';
import { httpsCertKeyFolderName, settingFolderName } from './fileNames';
import { isElectronDevelopment, isTest } from './environment';
import { cacheDatabaseFolderName, httpsCertKeyFolderName, settingFolderName } from './fileNames';
import { sourcePath } from './paths';
// in dev mode, set userData to a different folder, so gotTheLock will be true, we can run dev instance and normal instance.
if (isDevelopmentOrTest) {
app.setPath('userData', path.resolve(sourcePath, '..', 'userData-dev'));
/**
* Application Path Configuration
*
* Sets up isolated userData directories for different environments:
* - Test: userData-test/ (isolated from dev/prod)
* - Development: userData-dev/ (isolated from production)
* - Production: system default userData directory
*/
// Detect if we're in packaged app (E2E tests use packaged app with NODE_ENV=test)
const isPackaged = process.resourcesPath && !process.resourcesPath.includes('electron');
// Set isolated userData paths for dev/test
if (isTest) {
const userDataPath = isPackaged
? path.resolve(process.cwd(), 'userData-test') // E2E: packaged, use cwd (outside asar)
: path.resolve(sourcePath, 'userData-test'); // Unit tests: project/userData-test
app.setPath('userData', userDataPath);
} else if (isElectronDevelopment) {
app.setPath('userData', path.resolve(sourcePath, 'userData-dev'));
}
// Application directories
export const USER_DATA_FOLDER = app.getPath('userData');
export const SETTINGS_FOLDER = path.resolve(USER_DATA_FOLDER, settingFolderName);
export const HTTPS_CERT_KEY_FOLDER = path.resolve(USER_DATA_FOLDER, httpsCertKeyFolderName);
export const LOCAL_GIT_DIRECTORY = path.resolve(isDevelopmentOrTest ? path.join(sourcePath, '..') : process.resourcesPath, 'node_modules', 'dugite', 'git');
export const CACHE_DATABASE_FOLDER = path.resolve(USER_DATA_FOLDER, cacheDatabaseFolderName);
// Git directory (dugite package location)
export const LOCAL_GIT_DIRECTORY = isPackaged
? path.resolve(process.resourcesPath, 'node_modules', 'dugite', 'git')
: path.resolve(sourcePath, 'node_modules', 'dugite', 'git');
// Logging and cache directories
export const LOG_FOLDER = path.resolve(USER_DATA_FOLDER, 'logs');
export const V8_CACHE_FOLDER = v8CompileCacheLibrary.getCacheDir();
export const DEFAULT_DOWNLOADS_PATH = path.join(app.getPath('home'), 'Downloads');

View file

@ -68,15 +68,7 @@ export enum WikiChannel {
"tags": "Articles",
"type": "text/vnd.tiddlywiki",
"url": "http://www.networkworld.com/article/3028098/open-source-tools/tiddlywiki-a-free-open-source-wiki-revisited.html",
"text": "Interesting article giving the perspective of someone who has been away from TiddlyWiki for a few years:nn{{!!url}}nn<<<nWay back in the mists of time (actually, January 2009) I wrote about a really cool tool called TiddlyWiki, a “non-linear personal web notebook”. Fast forward to today and I just had an out of body experience: Completely by accident I found a TiddlyWiki that I started when I wrote that piece and it still works! nnFinding code that works flawlessly after just two or three years is magical enough but after seven years?! And given that TiddlyWiki is written as a single page Web application and considering how different browsers are now than they were in 2009, the fact that the old version of TiddlyWiki still works is not short of miraculous.n<<<n"
},
{
"title": ""A Thesis Notebook" by Alberto Molina",
"created": "20130302085406905",
"modified": "20130302084548184",
"tags": "Examples",
"url": "http://tesis.tiddlyspot.com/",
"text": "A thesis notebook based on TiddlyWiki.nn{{!!url}}nn<<<nThis is an example of a thesis notebook powered by TiddlyWiki 5.0.8-beta.nnTiddlyWiki is a great piece of software created by Jeremy Ruston. It allows you, among other things, to take notes, organise ideas, store information, and display all your stuff the way you want. It is an incredibly flexible tool you can adapt to fit almost all your needs.nnThis TiddlyWiki has been customized to serve as a philosophy notebook centered around authors, books and papers, concepts and theories, and personal notes. I use it along with Zotero, which is a dedicated bibliography software. Both are free, open source projects. TiddlyWiki can be downloaded at https://tiddlywiki.com.n<<<n"
"text": "Interesting article"
},
* ```
*/
@ -113,10 +105,6 @@ export enum PreferenceChannel {
update = 'update',
}
export enum PagesChannel {
name = 'PagesChannel',
}
export enum WindowChannel {
closeFindInPage = 'close-find-in-page',
name = 'WindowChannel',
@ -150,6 +138,20 @@ export enum SyncChannel {
name = 'SyncChannel',
}
export enum AgentChannel {
definition = 'AgentDefinitionChannel',
instance = 'AgentInstanceChannel',
browser = 'AgentBrowserChannel',
}
export enum ExternalAPIChannel {
name = 'ExternalAPIChannel',
}
export enum WikiEmbeddingChannel {
name = 'WikiEmbeddingChannel',
}
export type Channels =
| MainChannel
| AuthenticationChannel
@ -165,9 +167,12 @@ export type Channels =
| WikiGitWorkspaceChannel
| WorkspaceChannel
| WorkspaceViewChannel
| DatabaseChannel
| PreferenceChannel
| WindowChannel
| ThemeChannel
| I18NChannels
| MetaDataChannel
| SyncChannel;
| SyncChannel
| AgentChannel
| WikiEmbeddingChannel;

View file

@ -1,4 +1,5 @@
import { isElectronDevelopment } from './isElectronDevelopment';
export { isElectronDevelopment };
export const isTest = process.env.NODE_ENV === 'test';
export const isDevelopmentOrTest = isElectronDevelopment || isTest;

View file

@ -3,6 +3,7 @@ export const httpsCertKeyFolderName = 'https-keys';
export const cacheDatabaseFolderName = 'cache-database';
/** Used to place mock wiki during dev and testing */
export const developmentWikiFolderName = 'wiki-dev';
export const testWikiFolderName = 'wiki-test';
export const localizationFolderName = 'localization';
export const wikiPictureExtensions = ['jpg', 'jpeg', 'png', 'gif', 'tiff', 'tif', 'bmp', 'dib'];

View file

@ -1,8 +1,3 @@
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable unicorn/prefer-module */
// use `require` here, to check `typeof electron === 'string'`
import electron from 'electron';

View file

@ -0,0 +1,28 @@
export enum PageType {
/**
* Default empty page, have some user guide and new user settings.
*/
guide = 'guide',
/**
* Show list of available help resources to learn TiddlyWiki.
*/
help = 'help',
/**
* All "workspaces". It is hard to merge workspace concept with page concept, because will need to migrate all user data. So we leave them to be still workspace, but also call them wiki pages. And in event listeners about wiki page, we redirect them to call workspace methods.
*/
wiki = 'wiki',
/**
* Chat page for AI agents.
*/
agent = 'agent',
/**
* Special page type for the "add workspace" button.
*/
add = 'add',
}
export const defaultCreatedPageTypes: PageType[] = [PageType.agent, PageType.help, PageType.guide, PageType.add];
export function isMainWindowPage(pageType: PageType | undefined): boolean {
if (!pageType) return false;
return defaultCreatedPageTypes.includes(pageType) ||
pageType === PageType.wiki;
}

View file

@ -1,35 +1,71 @@
import os from 'os';
import path from 'path';
import { isMac } from '../helpers/system';
import { isDevelopmentOrTest } from './environment';
import { developmentWikiFolderName, localizationFolderName } from './fileNames';
import { isDevelopmentOrTest, isTest } from './environment';
import { developmentWikiFolderName, localizationFolderName, testWikiFolderName } from './fileNames';
/** src folder */
export const sourcePath = path.resolve(__dirname, '..');
/**
* Environment Detection & Path Resolution Strategy
*
* Three execution environments:
* 1. Development (pnpm start:dev) - Vite dev server
* 2. Unit Tests (ELECTRON_RUN_AS_NODE=1) - Vitest with Electron
* 3. Packaged (E2E/Production) - Built .asar file
*
* Key challenge: In unit tests, Electron sets process.resourcesPath to its internal directory,
* which is wrong. We detect this by checking if the path contains 'electron'.
*/
// Detect if we're in packaged app (not dev, not unit tests with electron's internal path)
const isPackaged = process.resourcesPath && !process.resourcesPath.includes('electron');
// Project root directory (outside asar in packaged apps)
export const sourcePath = isPackaged
? path.resolve(process.resourcesPath, '..') // Packaged: go up from resources/ to app root
: path.resolve(__dirname, '..', '..'); // Dev/Unit test: from src/constants to project root
// Build resources (only used in dev/test)
export const buildResourcePath = path.resolve(sourcePath, '..', 'build-resources');
export const developmentImageFolderPath = path.resolve(sourcePath, 'images');
// .app/Contents/Resources/wiki/
export const TIDDLYWIKI_TEMPLATE_FOLDER_PATH = isDevelopmentOrTest
? path.resolve(sourcePath, '..', 'template', 'wiki')
: path.resolve(process.resourcesPath, 'wiki');
export const TIDDLERS_PATH = 'tiddlers';
// Menubar icon
const menuBarIconFileName = isMac ? 'menubarTemplate@2x.png' : 'menubar@2x.png';
export const MENUBAR_ICON_PATH = path.resolve(isDevelopmentOrTest ? buildResourcePath : process.resourcesPath, menuBarIconFileName);
export const MENUBAR_ICON_PATH = isPackaged
? path.resolve(process.resourcesPath, menuBarIconFileName) // Packaged: resources/icon
: path.resolve(buildResourcePath, menuBarIconFileName); // Dev/Unit test: build-resources/icon
// System paths
export const CHROME_ERROR_PATH = 'chrome-error://chromewebdata/';
export const DESKTOP_PATH = path.join(os.homedir(), 'Desktop');
export const PACKAGE_PATH_BASE = isDevelopmentOrTest
? path.resolve(__dirname, '..', '..', 'node_modules')
: path.resolve(process.resourcesPath, 'node_modules');
// Node modules base (for native binaries and external packages)
export const PACKAGE_PATH_BASE = isPackaged
? path.resolve(process.resourcesPath, 'node_modules') // Packaged: resources/node_modules
: path.resolve(sourcePath, 'node_modules'); // Dev/Unit test: project/node_modules
// Package-specific paths
export const ZX_FOLDER = path.resolve(PACKAGE_PATH_BASE, 'zx', 'build', 'cli.js');
export const TIDDLYWIKI_PACKAGE_FOLDER = path.resolve(PACKAGE_PATH_BASE, 'tiddlywiki', 'boot');
export const SQLITE_BINARY_PATH = path.resolve(PACKAGE_PATH_BASE, 'better-sqlite3', 'build', 'Release', 'better_sqlite3.node');
export const LOCALIZATION_FOLDER = isDevelopmentOrTest
? path.resolve(sourcePath, '..', localizationFolderName)
: path.resolve(process.resourcesPath, localizationFolderName);
export const DEFAULT_WIKI_FOLDER = isDevelopmentOrTest ? path.resolve(sourcePath, '..', developmentWikiFolderName) : DESKTOP_PATH;
// Localization folder
export const LOCALIZATION_FOLDER = isPackaged
? path.resolve(process.resourcesPath, localizationFolderName) // Packaged: resources/localization
: path.resolve(sourcePath, localizationFolderName); // Dev/Unit test: project/localization
// Default wiki locations
// For E2E tests, always use project root's wiki-test (outside asar)
// process.resourcesPath: out/TidGi-.../resources -> need ../../.. to get to project root
export const DEFAULT_FIRST_WIKI_FOLDER_PATH = isTest && isPackaged
? path.resolve(process.resourcesPath, '..', '..', '..', testWikiFolderName) // E2E packaged: project root
: isTest
? path.resolve(__dirname, '..', '..', testWikiFolderName) // E2E dev: project root
: isDevelopmentOrTest
? path.resolve(sourcePath, developmentWikiFolderName) // Dev: use sourcePath
: DESKTOP_PATH; // Production: use desktop
export const DEFAULT_FIRST_WIKI_NAME = 'wiki';
export const DEFAULT_FIRST_WIKI_PATH = path.join(DEFAULT_WIKI_FOLDER, DEFAULT_FIRST_WIKI_NAME);
export const DEFAULT_FIRST_WIKI_PATH = path.join(DEFAULT_FIRST_WIKI_FOLDER_PATH, DEFAULT_FIRST_WIKI_NAME);
// TiddlyWiki template folder
export const TIDDLYWIKI_TEMPLATE_FOLDER_PATH = isPackaged
? path.resolve(process.resourcesPath, 'wiki') // Packaged: resources/wiki
: path.resolve(sourcePath, 'template', 'wiki'); // Dev/Unit test: project/template/wiki
export const TIDDLERS_PATH = 'tiddlers';

View file

@ -1,15 +0,0 @@
/**
* Paths that can be used in worker. Without electron.
*/
import path from 'path';
import { isWorkerDevelopmentOrTest } from './isWorkerDevelopment';
export const PACKAGE_PATH_BASE = isWorkerDevelopmentOrTest
? path.resolve(__dirname, '..', '..', 'node_modules')
: path.resolve(process.resourcesPath, 'node_modules');
// const sourcePath = path.resolve(__dirname, '..');
// const WEBPACK_MAIN_THREAD_DIST_PATH = isWorkerDevelopmentOrTest
// ? path.resolve(sourcePath, '..', '.webpack', 'main')
// : path.resolve(process.resourcesPath, '.webpack', 'main');
// // Path to native_modules
// export const NATIVE_MODULES_PATH = path.resolve(WEBPACK_MAIN_THREAD_DIST_PATH, 'native_modules');

26
src/debug.ts Normal file
View file

@ -0,0 +1,26 @@
import { session } from 'electron';
import { installExtension, updateExtensions } from 'electron-chrome-web-store';
export async function initDevelopmentExtension() {
if (process.env.NODE_ENV === 'development' && process.env.DEBUG_REACT === 'true') {
await installExtension('fmkadmapgofadopljbjfkapdkoienihi');
await updateExtensions();
await launchExtensionBackgroundWorkers();
}
}
function launchExtensionBackgroundWorkers() {
return Promise.all(
session.defaultSession.extensions.getAllExtensions().map(async (extension) => {
const manifest = extension.manifest as {
manifest_version: number;
background?: {
service_worker?: string;
};
};
if (manifest.manifest_version === 3 && manifest.background?.service_worker) {
await session.defaultSession.serviceWorkers.startWorkerForScope(extension.url);
}
}),
);
}

Some files were not shown because too many files have changed in this diff Show more