diff --git a/web-core/.gitignore b/web-core/.gitignore new file mode 100644 index 0000000..c1b65ba --- /dev/null +++ b/web-core/.gitignore @@ -0,0 +1,41 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +output/ +*.local +vite.config.js +vite.config.d.ts +src/assets/login/ + +/cypress/videos/ +/cypress/screenshots/ + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo + +src/types/auto-imports.d.ts +src/types/components.d.ts +src/views/scriptManage/components/ceshi.png +oss + +backup diff --git a/web-core/.prettierignore b/web-core/.prettierignore new file mode 100644 index 0000000..d1e52fa --- /dev/null +++ b/web-core/.prettierignore @@ -0,0 +1,11 @@ +node_modules +dist +build +.output +.nuxt +coverage +*.min.js +*.min.css +package-lock.json +pnpm-lock.yaml +yarn.lock diff --git a/web-core/.prettierrc.json b/web-core/.prettierrc.json new file mode 100644 index 0000000..e2759a2 --- /dev/null +++ b/web-core/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://json.schemastore.org/prettierrc", + "semi": true, + "singleQuote": false, + "printWidth": 150, + "bracketSameLine": true, + "htmlWhitespaceSensitivity": "ignore" +} diff --git a/web-core/LICENSE b/web-core/LICENSE new file mode 100644 index 0000000..5bdde5a --- /dev/null +++ b/web-core/LICENSE @@ -0,0 +1,259 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +--- + +## 补充协议 | Supplementary Agreement + +> **补充协议 | Supplementary Agreement** +> +> 若您将本软件或其衍生版本以产品形式分发、销售或提供给**两个及以上独立的第三方主体**使用(无论采用买断、订阅、授权或任何其他商业模式),均须事先取得 HBAI-Ltd 的**书面商业授权**。 +> +> If you distribute, sell, or provide this software (or any derivative) as a product to **two or more independent third parties** (regardless of business model: one-time purchase, subscription, licensing, etc.), you must obtain **written commercial authorization** from HBAI-Ltd prior to such use. +> +> **五个以内(含五个)**的法人主体作为联合运营方共同使用本软件,且不向联合体以外的第三方分发或提供服务的,视为**内部使用,无需商业授权**。 +> +> **Five (5) or fewer** legal entities jointly operating and using this software internally, without distributing or providing services to parties outside the joint operation, are considered **internal use** and do not require commercial authorization. +> +> 在使用 Toonflow 的过程中,您不得删除或修改 Toonflow 控制台或应用程序中的标识或版权信息。 +> +> You may not remove or modify any trademarks, logos, or copyright notices in the Toonflow console or application. + +--- + +## 永久免费场景 | Always Free — No Authorization Required + +以下使用场景**永久免费**,无需任何授权: + +The following uses are **always free** and require no authorization: + +- ✅ 用 Toonflow 制作内容,在平台发布并获得分账 / Using Toonflow to produce content and earn platform revenue shares +- ✅ 二次开发,供自己团队内部使用 / Secondary development and modification for internal team use +- ✅ ≤ 5 个法人联合运营内部使用,不对外提供服务 / ≤5 legal entities operating jointly, internally only +- ✅ 个人学习、研究、非商业用途 / Personal learning, research, and non-commercial purposes + +--- + +## 授权定价 | Licensing Pricing + +商业授权费用与被授权方业务规模挂钩: + +Commercial licensing fees scale with the licensee's business scale: + +| 阶段 | 年销售额 | 年费 | +|------|---------|------| +| 🌱 扶持期 / Nurture | < ¥10 万 / $10k | **免费 / Free** | +| 🚀 初创期 / Startup | ¥10–50 万 / $10k–$50k | ¥5,000 / 年 / year | +| 📈 成长期 / Growth | ¥50–150 万 / $50k–$150k | ¥20,000 / 年 / year | +| 🏢 规模期 / Scale | ¥150–500 万 / $150k–$500k | ¥80,000 / 年 / year | +| 🌐 企业级 / Enterprise | > ¥500 万 / $500k | 面议 / Negotiable | + +--- + +## AGPL 用户保护条款 | AGPL User Protection + +> **不追溯条款 | Non-Retroactivity Clause** +> +> 本协议变更生效日期(v1.0.8 正式发布日)前,基于 AGPL-3.0 条款使用 Toonflow 的用户,其现有使用行为不受本协议变更的约束,继续按 AGPL-3.0 条款执行。 +> +> Users who used Toonflow under AGPL-3.0 prior to the effective date of this change (v1.0.8 official release date) are not affected by this license change. Their existing usage continues under AGPL-3.0. diff --git a/web-core/NOTICES.txt b/web-core/NOTICES.txt new file mode 100644 index 0000000..d3c76f0 --- /dev/null +++ b/web-core/NOTICES.txt @@ -0,0 +1,243 @@ +Name: @eonova/v3-directives +License: MIT +Repository: N/A + +----------------------------- + +Name: @icon-park/vue-next +License: Apache-2.0 +Repository: https://github.com/bytedance/IconPark + +----------------------------- + +Name: @tsconfig/node22 +License: MIT +Repository: https://github.com/tsconfig/bases + +----------------------------- + +Name: @types/license-checker +License: MIT +Repository: https://github.com/DefinitelyTyped/DefinitelyTyped + +----------------------------- + +Name: @types/node +License: MIT +Repository: https://github.com/DefinitelyTyped/DefinitelyTyped + +----------------------------- + +Name: @types/uuid +License: MIT +Repository: N/A + +----------------------------- + +Name: @vitejs/plugin-vue +License: MIT +Repository: https://github.com/vitejs/vite-plugin-vue + +----------------------------- + +Name: @vue/eslint-config-prettier +License: MIT +Repository: https://github.com/vuejs/eslint-config-prettier + +----------------------------- + +Name: @vue/eslint-config-typescript +License: MIT +Repository: https://github.com/vuejs/eslint-config-typescript + +----------------------------- + +Name: @vue/tsconfig +License: MIT +Repository: https://github.com/vuejs/tsconfig + +----------------------------- + +Name: @vueuse/core +License: MIT +Repository: https://github.com/vueuse/vueuse + +----------------------------- + +Name: @vueuse/core +License: MIT +Repository: https://github.com/vueuse/vueuse + +----------------------------- + +Name: ant-design-vue +License: MIT +Repository: https://github.com/vueComponent/ant-design-vue + +----------------------------- + +Name: axios +License: MIT +Repository: https://github.com/axios/axios + +----------------------------- + +Name: class-variance-authority +License: Apache-2.0 +Repository: https://github.com/joe-bell/cva + +----------------------------- + +Name: clsx +License: MIT +Repository: https://github.com/lukeed/clsx + +----------------------------- + +Name: dayjs +License: MIT +Repository: https://github.com/iamkun/dayjs + +----------------------------- + +Name: element-plus +License: MIT +Repository: https://github.com/element-plus/element-plus + +----------------------------- + +Name: eslint-plugin-oxlint +License: MIT +Repository: https://github.com/oxc-project/eslint-plugin-oxlint + +----------------------------- + +Name: eslint-plugin-vue +License: MIT +Repository: https://github.com/vuejs/eslint-plugin-vue + +----------------------------- + +Name: eslint +License: MIT +Repository: https://github.com/eslint/eslint + +----------------------------- + +Name: license-checker +License: BSD-3-Clause +Repository: https://github.com/davglass/license-checker + +----------------------------- + +Name: mammoth +License: BSD-2-Clause +Repository: https://github.com/mwilliamson/mammoth.js + +----------------------------- + +Name: npm-run-all2 +License: MIT +Repository: https://github.com/bcomnes/npm-run-all2 + +----------------------------- + +Name: oxlint +License: MIT +Repository: https://github.com/oxc-project/oxc + +----------------------------- + +Name: pinia-plugin-persistedstate +License: MIT +Repository: https://codeberg.org/praz/pinia-plugin-persistedstate + +----------------------------- + +Name: pinia +License: MIT +Repository: https://github.com/vuejs/pinia + +----------------------------- + +Name: prettier +License: MIT +Repository: https://github.com/prettier/prettier + +----------------------------- + +Name: sass +License: MIT +Repository: https://github.com/sass/dart-sass + +----------------------------- + +Name: typescript +License: Apache-2.0 +Repository: https://github.com/microsoft/TypeScript + +----------------------------- + +Name: unplugin-auto-import +License: MIT +Repository: https://github.com/unplugin/unplugin-auto-import + +----------------------------- + +Name: unplugin-vue-components +License: MIT +Repository: https://github.com/unplugin/unplugin-vue-components + +----------------------------- + +Name: uuid +License: MIT +Repository: https://github.com/uuidjs/uuid + +----------------------------- + +Name: vite-plugin-lazy-import +License: MIT +Repository: https://github.com/x-extends/vite-plugin-lazy-import + +----------------------------- + +Name: vite-plugin-singlefile +License: MIT +Repository: https://github.com/richardtallent/vite-plugin-singlefile + +----------------------------- + +Name: vite +License: MIT +Repository: https://github.com/vitejs/vite + +----------------------------- + +Name: vue-router +License: MIT +Repository: https://github.com/vuejs/router + +----------------------------- + +Name: vue-tsc +License: MIT +Repository: https://github.com/vuejs/language-tools + +----------------------------- + +Name: vue +License: MIT +Repository: https://github.com/vuejs/core + +----------------------------- + +Name: vuedraggable +License: MIT +Repository: https://github.com/SortableJS/Vue.Draggable + +----------------------------- + +Name: vxe-table +License: MIT +Repository: https://github.com/x-extends/vxe-table \ No newline at end of file diff --git a/web-core/README.md b/web-core/README.md new file mode 100644 index 0000000..c99b7fd --- /dev/null +++ b/web-core/README.md @@ -0,0 +1,487 @@ +

+ 中文 | + English +

+ +
+ +Toonflow Logo + +# Toonflow Web + +

+ + Toonflow 前端应用 +
+ 基于 Vue 3 + TypeScript + Vite 构建的现代化 Web 界面 +
+ AI短剧工厂的用户操作端 🎨 +
+

+

+ + Stars Badge + + + AGPL License Badge + + + release + +

+ + > 🎯 **现代化前端架构**:采用 Vue 3 组合式 API、TypeScript 类型安全、Vite 极速构建,打造流畅的用户体验! +
+ +--- + +# ⚠️ 重要提示 + +> **本仓库仅包含前端源代码,适用于开发者进行二次开发或定制。** +> +> 🎉 **如果您是普通用户,想要直接使用 Toonflow,请前往主仓库下载完整客户端:** +> +> | 平台 | 链接 | +> | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | +> | GitHub | 👉 [github.com/HBAI-Ltd/Toonflow-app](https://github.com/HBAI-Ltd/Toonflow-app) | +> | Gitee | 👉 [gitee.com/HBAI-Ltd/Toonflow-app](https://gitee.com/HBAI-Ltd/Toonflow-app) | +> +> 主仓库包含: +> +> - ✅ 完整的桌面客户端 +> - ✅ 后端服务 +> - ✅ 开箱即用的安装包 +> - ✅ 详细的使用教程 + +--- + +# 🌟 技术栈 + +- **框架**:Vue 3.5+ (组合式 API) +- **构建工具**:Vite 5.4+ +- **语言**:TypeScript 5.6+ +- **状态管理**:Pinia 2.2+ (支持持久化) +- **路由**:Vue Router 4.4+ +- **UI 组件库**: + - Ant Design Vue 4.2+ + - Element Plus 2.13+ + - VXE Table 4.17+ +- **工具库**: + - Axios - HTTP 请求 + - VueUse - Vue 组合式工具集 + - Day.js - 日期处理 + - Mammoth - Word 文档解析 + +--- + +# 🎨 主要功能模块 + +Toonflow Web 提供了完整的短剧创作前端界面,包含以下核心模块: + +- ✅ **项目管理** + 创建、编辑和管理短剧项目,支持项目状态追踪和多项目并行开发。 + +- ✅ **原始文本编辑** + 导入和编辑小说原文,支持 Word 文档解析,智能文本清洗和章节分割。 + +- ✅ **角色素材库** + 管理角色设定、角色图片等素材,支持批量生成、手动上传和在线编辑。 + +- ✅ **大纲管理** + 可视化编辑故事大纲和事件线,支持拖拽排序和智能生成。 + +- ✅ **剧本编辑器** + 结构化剧本编辑界面,支持对话、场景、情绪等多维度标注。 + +- ✅ **分镜设计** + 可视化分镜画布,支持拖拽布局、图像检测和 AI 对话式分镜生成。 + +- ✅ **视频配置** + 配置视频生成参数,支持多家 AI 视频服务商切换和视频下载。 + +- ✅ **任务监控** + 实时查看 AI 生成任务进度,支持任务队列管理和历史记录查询。 + +- ✅ **系统设置** + 配置 AI 服务商、提示词模板、用户权限等系统级参数。 + +--- + +# 📦 应用场景 + +- 短剧内容创作的前端操作界面 +- AI 辅助编剧工具的可视化平台 +- 分镜设计与视频生成的工作台 +- 多人协作的剧本管理系统 + +--- + +# 🚀 快速开始 + +## 💡 您是哪类用户? + +| 用户类型 | 推荐方案 | GitHub | Gitee | +| ---------------------------------------------- | -------------- | -------------------------------------------------------- | ------------------------------------------------------- | +| 🎬 **普通用户** - 想直接使用 Toonflow 创作短剧 | 下载完整客户端 | [Toonflow-app](https://github.com/HBAI-Ltd/Toonflow-app) | [Toonflow-app](https://gitee.com/HBAI-Ltd/Toonflow-app) | +| 👨‍💻 **开发者** - 想修改前端代码或二次开发 | 继续阅读本文档 | 本仓库 | 本仓库 | + +--- + +## 前置条件 + +在开发和运行本项目之前,请确保已安装: + +- ✅ **Node.js**:23.11.1 或更高版本 +- ✅ **Yarn**:1.22.0 或更高版本(推荐包管理器) +- ✅ **后端服务**:确保 Toonflow 后端服务已启动并可访问(可从 [GitHub](https://github.com/HBAI-Ltd/Toonflow-app) 或 [Gitee](https://gitee.com/HBAI-Ltd/Toonflow-app) 获取) + +## 本地开发 + +### 1. 克隆项目 + +**GitHub:** + +```bash +git clone https://github.com/HBAI-Ltd/Toonflow-web.git +cd Toonflow-web +``` + +**Gitee(国内推荐):** + +```bash +git clone https://gitee.com/HBAI-Ltd/Toonflow-web.git +cd Toonflow-web +``` + +### 2. 安装依赖 + +```bash +yarn install +``` + +### 3. 启动开发服务器 + +```bash +yarn dev +``` + +开发服务器默认运行在 `http://localhost:5173`,支持热模块替换(HMR)。 + +### 4. 构建生产版本 + +```bash +# 开发环境构建 +yarn build:dev + +# 生产环境构建 +yarn build:prod +``` + +构建产物将输出到 `dist` 目录。 + +### 5. 预览生产构建 + +```bash +yarn preview +``` + +--- + +## 生产部署 + +### 方式一:静态文件部署 + +1. **构建项目** + +```bash +yarn build:prod +``` + +2. **部署到 Web 服务器** + +将 `dist` 目录下的所有文件上传到 Nginx、Apache 或其他 Web 服务器的根目录。 + +**Nginx 配置示例:** + +```nginx +server { + listen 80; + server_name your-domain.com; + root /var/www/toonflow-web/dist; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + # API 代理(可选) + location /api/ { + proxy_pass http://localhost:10588/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } +} +``` + +### 方式二:与后端集成部署 + +将构建后的 `dist` 目录内容复制到后端的静态资源目录 `scripts/web` 中。 + +> 💡 **提示**:后端服务可从 [GitHub](https://github.com/HBAI-Ltd/Toonflow-app) 或 [Gitee](https://gitee.com/HBAI-Ltd/Toonflow-app) 仓库获取。 + +--- + +# 🔧 开发指南 + +## 开发环境准备 + +- **Node.js**:版本要求 23.11.1 及以上 +- **Yarn**:推荐作为项目包管理器 + +## 常用命令 + +```bash +# 安装依赖 +yarn install + +# 启动开发服务器(支持热更新) +yarn dev + +# 类型检查 +yarn type-check + +# 代码检查和自动修复 +yarn lint + +# 代码格式化 +yarn format + +# 构建开发版本 +yarn build:dev + +# 构建生产版本 +yarn build:prod + +# 预览生产构建 +yarn preview + +# 生成第三方许可证清单 +yarn license +``` + +## 项目结构 + +``` +📂 Toonflow-web/ +├─ 📂 public/ # 静态资源 +├─ 📂 scripts/ # 构建脚本 +│ └─ 📄 license.ts # 许可证生成脚本 +├─ 📂 src/ +│ ├─ 📂 assets/ # 静态资源(样式、图片等) +│ │ └─ 📄 main.css # 全局样式 +│ ├─ 📂 components/ # 公共组件 +│ │ ├─ 📄 sider.vue # 侧边栏组件 +│ │ ├─ 📂 chat/ # 聊天组件 +│ │ ├─ 📂 storyboardEditor/ # 分镜编辑器 +│ │ └─ 📂 videoConfig/ # 视频配置组件 +│ ├─ 📂 config/ # 配置文件 +│ │ └─ 📄 manufacturerConfig.ts # 厂商配置 +│ ├─ 📂 pages/ # 页面组件 +│ │ ├─ 📂 error/ # 错误页面 +│ │ ├─ 📂 login/ # 登录页面 +│ │ └─ 📂 workbench/ # 工作台 +│ ├─ 📂 router/ # 路由配置 +│ │ └─ 📄 index.ts # 路由定义 +│ ├─ 📂 stores/ # Pinia 状态管理 +│ │ ├─ 📄 index.ts # Store 入口 +│ │ ├─ 📄 loadingStore.ts # 加载状态 +│ │ ├─ 📄 user.ts # 用户状态 +│ │ └─ 📄 video.ts # 视频状态 +│ ├─ 📂 types/ # TypeScript 类型定义 +│ │ ├─ 📄 auto-imports.d.ts # 自动导入类型 +│ │ ├─ 📄 components.d.ts # 组件类型 +│ │ ├─ 📄 global.d.ts # 全局类型 +│ │ ├─ 📄 manufacturer.ts # 厂商类型 +│ │ └─ 📄 shims-vue.d.ts # Vue 模块声明 +│ ├─ 📂 utils/ # 工具函数 +│ │ ├─ 📄 axios.ts # HTTP 请求封装 +│ │ ├─ 📄 combineImages.ts # 图片合成 +│ │ ├─ 📄 error.ts # 错误处理 +│ │ ├─ 📄 parseNovel.ts # 小说解析 +│ │ ├─ 📄 splitGraph.ts # 图像分割 +│ │ ├─ 📄 throttle.ts # 节流防抖 +│ │ └─ 📄 wsClient.ts # WebSocket 客户端 +│ ├─ 📂 views/ # 视图页面 +│ │ ├─ 📂 project/ # 项目管理 +│ │ ├─ 📂 projectDetail/ # 项目详情 +│ │ │ ├─ 📂 components/ +│ │ │ │ ├─ 📂 assetsManager/ # 素材管理 +│ │ │ │ ├─ 📂 originalText/ # 原始文本 +│ │ │ │ ├─ 📂 outlineManager/ # 大纲管理 +│ │ │ │ ├─ 📂 overview/ # 项目概览 +│ │ │ │ └─ 📂 scriptManager/ # 剧本管理 +│ │ ├─ 📂 setting/ # 系统设置 +│ │ └─ 📂 taskList/ # 任务列表 +│ ├─ 📄 App.vue # 根组件 +│ └─ 📄 main.ts # 应用入口 +├─ 📄 components.d.ts # 全局组件类型 +├─ 📄 eslint.config.js # ESLint 配置 +├─ 📄 index.html # HTML 模板 +├─ 📄 package.json # 项目配置 +├─ 📄 tsconfig.json # TypeScript 配置 +├─ 📄 tsconfig.app.json # 应用 TS 配置 +├─ 📄 tsconfig.node.json # Node TS 配置 +├─ 📄 vite.config.ts # Vite 配置 +├─ 📄 LICENSE # 许可证 +├─ 📄 NOTICES.txt # 第三方依赖声明 +└─ 📄 README.md # 项目说明 +``` + +--- + +# 📝 开发计划 + +我们正持续优化前端体验,以下为近期开发重点: + +1. **UI/UX 优化** + - `🎨 暗色主题支持` 提供优雅的暗色模式选项,保护用户视力 + - `📱 响应式布局` 优化移动端和平板设备的显示效果 + - `⌨️ 快捷键系统` 添加常用操作的快捷键,提升操作效率 + +2. **功能增强** + - `📊 数据可视化` 增强项目数据统计和可视化图表展示 + - `🔄 实时协作` 支持多人同时编辑,实时同步状态 + - `💾 自动保存` 智能自动保存功能,防止数据丢失 + +3. **性能优化** + - `⚡ 虚拟滚动` 大数据列表的性能优化 + - `🗜️ 资源压缩` 优化构建产物体积,提升加载速度 + - `🔌 懒加载优化` 进一步优化路由和组件的懒加载策略 + +--- + +# 🐛 常见问题 + +### Q: 启动开发服务器时端口被占用? + +**A:** 修改 `vite.config.ts` 中的端口配置: + +```typescript +export default defineConfig({ + server: { + port: 3000, // 修改为其他端口 + }, +}); +``` + +### Q: 如何配置后端 API 地址? + +**A:** 在 `.env.dev` 中配置后端地址: + +```bash +VITE_TYPE=dev +VITE_BASE_URL=http://127.0.0.1:10588 +VITE_WS_URL=ws://127.0.0.1:10588 +``` + +### Q: 我只想使用 Toonflow,不需要开发,怎么办? + +**A:** 请前往主仓库下载完整客户端: + +- **GitHub**:👉 [Toonflow-app](https://github.com/HBAI-Ltd/Toonflow-app) +- **Gitee**:👉 [Toonflow-app](https://gitee.com/HBAI-Ltd/Toonflow-app) + +--- + +# 🔗 相关仓库 + +| 仓库 | 说明 | GitHub | Gitee | +| ---------------- | -------------------------------- | -------------------------------------------------- | ------------------------------------------------ | +| **Toonflow-app** | 完整客户端(推荐普通用户) | [GitHub](https://github.com/HBAI-Ltd/Toonflow-app) | [Gitee](https://gitee.com/HBAI-Ltd/Toonflow-app) | +| **Toonflow-web** | 前端源代码(本仓库,适合开发者) | [GitHub](https://github.com/HBAI-Ltd/Toonflow-web) | [Gitee](https://gitee.com/HBAI-Ltd/Toonflow-web) | + +--- + +# 👨‍👩‍👧‍👦 微信交流群 + +请到主仓库中查看 + +- **GitHub**:👉 [Toonflow-app](https://github.com/HBAI-Ltd/Toonflow-app) +- **Gitee**:👉 [Toonflow-app](https://gitee.com/HBAI-Ltd/Toonflow-app) + +--- + +# 💌 联系我们 + +📧 邮箱:[ltlctools@outlook.com](mailto:ltlctools@outlook.com?subject=Toonflow前端咨询) + +--- + +# 📜 许可证 + +Toonflow 基于 Apache-2.0 协议开源发布,并附有补充商业协议。 + +许可证详情:https://www.apache.org/licenses/LICENSE-2.0 + +## 补充协议 + +- 若将本软件以产品形式分发给 **2 个及以上独立第三方**使用,须取得 HBAI-Ltd **书面商业授权**。 +- **≤ 5 个法人**联合运营内部使用,不对外提供服务的,视为内部使用,**无需授权**。 +- 不得删除或修改 Toonflow 中的标识或版权信息。 + +## 永久免费场景 + +- ✅ 用 Toonflow 制作内容并获得平台分账 +- ✅ 二次开发供自己团队内部使用 +- ✅ ≤ 5 个法人联合运营内部使用 +- ✅ 个人学习、研究、非商业用途 + +## 商业授权定价 + +| 阶段 | 年销售额 | 年费 | +|------|---------|------| +| 🌱 扶持期 | < ¥10 万 | **申请即可免费授权** | +| 🚀 初创期 | ¥10–50 万 | ¥5,000/年 | +| 📈 成长期 | ¥50–150 万 | ¥20,000/年 | +| 🏢 规模期 | ¥150–500 万 | ¥80,000/年 | +| 🌐 企业级 | > ¥500 万 | 面议 | + +> **不追溯条款**:v1.0.8 发布前基于 AGPL-3.0 使用的用户,继续按 AGPL-3.0 执行,不受本协议变更约束。 + +完整协议详见 [LICENSE](./LICENSE) 文件。 +--- + +# ⭐️ 星标历史 + +[![Star History Chart](https://api.star-history.com/svg?repos=HBAI-Ltd/Toonflow-web&type=date&legend=top-left)](https://www.star-history.com/#HBAI-Ltd/Toonflow-web&type=date&legend=top-left) + +--- + +# 🙏 致谢 + +感谢以下开源项目为 Toonflow Web 提供强大支持: + +- [Vue.js](https://vuejs.org/) - 渐进式 JavaScript 框架 +- [Vite](https://vitejs.dev/) - 下一代前端构建工具 +- [Ant Design Vue](https://antdv.com/) - 企业级 UI 组件库 +- [Element Plus](https://element-plus.org/) - 基于 Vue 3 的组件库 +- [TDesign](https://element-plus.org/) - 为设计师 & 开发者,打造工作美学 +- [Pinia](https://pinia.vuejs.org/) - Vue 的直观状态管理库 + +感谢以下组织/单位/个人为 Toonflow 提供支持: + + + + + + +
+ 算能云 Logo + + 算能云 提供算力赞助 + [官网] +
+ +完整的第三方依赖清单请查阅 `NOTICES.txt` + +##### copyright © 北京爱阿科技有限公司 diff --git a/web-core/components.d.ts b/web-core/components.d.ts new file mode 100644 index 0000000..3e64530 --- /dev/null +++ b/web-core/components.d.ts @@ -0,0 +1,54 @@ +/* eslint-disable */ +// @ts-nocheck +// biome-ignore lint: disable +// oxlint-disable +// ------ +// Generated by unplugin-vue-components +// Read more: https://github.com/vuejs/core/pull/3399 + +export {}; + +/* prettier-ignore */ +declare module 'vue' { + export interface GlobalComponents { + AButton: typeof import('ant-design-vue/es')['Button'] + ACheckableTag: typeof import('ant-design-vue/es')['CheckableTag'] + ACollapse: typeof import('ant-design-vue/es')['Collapse'] + ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel'] + ADivider: typeof import('ant-design-vue/es')['Divider'] + AImage: typeof import('ant-design-vue/es')['Image'] + AInput: typeof import('ant-design-vue/es')['Input'] + AInputNumber: typeof import('ant-design-vue/es')['InputNumber'] + AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] + ALayout: typeof import('ant-design-vue/es')['Layout'] + ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent'] + ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider'] + AModal: typeof import('ant-design-vue/es')['Modal'] + APopconfirm: typeof import('ant-design-vue/es')['Popconfirm'] + AProgress: typeof import('ant-design-vue/es')['Progress'] + ARadio: typeof import('ant-design-vue/es')['Radio'] + ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup'] + ASelect: typeof import('ant-design-vue/es')['Select'] + ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] + ASpin: typeof import('ant-design-vue/es')['Spin'] + ASteps: typeof import('ant-design-vue/es')['Steps'] + ATag: typeof import('ant-design-vue/es')['Tag'] + ATextarea: typeof import('ant-design-vue/es')['Textarea'] + AUpload: typeof import('ant-design-vue/es')['Upload'] + AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger'] + BlurReveal: typeof import('./src/components/BlurReveal.vue')['default'] + Book: typeof import('./src/components/book/Book.vue')['default'] + BookDescription: typeof import('./src/components/book/BookDescription.vue')['default'] + BookHeader: typeof import('./src/components/book/BookHeader.vue')['default'] + BookTitle: typeof import('./src/components/book/BookTitle.vue')['default'] + ColourfulText: typeof import('./src/components/ColourfulText.vue')['default'] + FileUpload: typeof import('./src/components/fileUpload/FileUpload.vue')['default'] + FileUploadGrid: typeof import('./src/components/fileUpload/FileUploadGrid.vue')['default'] + MultiStepLoader: typeof import('./src/components/MultiStepLoader.vue')['default'] + NewAndEdit: typeof import('./src/components/newAndEdit/index.vue')['default'] + RainbowButton: typeof import('./src/components/RainbowButton.vue')['default'] + RouterLink: typeof import('vue-router')['RouterLink'] + RouterView: typeof import('vue-router')['RouterView'] + Sider: typeof import('./src/components/sider.vue')['default'] + } +} diff --git a/web-core/docs/README.en.md b/web-core/docs/README.en.md new file mode 100644 index 0000000..f62dfc2 --- /dev/null +++ b/web-core/docs/README.en.md @@ -0,0 +1,470 @@ +

+ 中文 | + English +

+ +
+ +Toonflow Logo + +# Toonflow Web + +

+ + Toonflow Frontend Application +
+ Modern Web interface built with Vue 3 + TypeScript + Vite +
+ User frontend for AI Short Drama Factory 🎨 +
+

+

+ + Stars Badge + + + AGPL License Badge + + + release + +

+ + > 🎯 **Modern Frontend Architecture**: Built with Vue 3 Composition API, TypeScript type safety, and Vite for lightning-fast builds, delivering a smooth user experience! +
+ +--- + +# ⚠️ Important Notice + +> **This repository only contains the frontend source code, suitable for developers for secondary development or customization.** +> +> 🎉 **If you are a regular user wishing to use Toonflow directly, please go to the main repository to download the full client:** +> +> | Platform | Link | +> | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------- | +> | GitHub | 👉 [github.com/HBAI-Ltd/Toonflow-app](https://github.com/HBAI-Ltd/Toonflow-app) | +> | Gitee | 👉 [gitee.com/HBAI-Ltd/Toonflow-app](https://gitee.com/HBAI-Ltd/Toonflow-app) | +> +> The main repository includes: +> +> - ✅ Complete desktop client +> - ✅ Backend service +> - ✅ Ready-to-use installer +> - ✅ Detailed usage tutorial + +--- + +# 🌟 Tech Stack + +- **Framework**: Vue 3.5+ (Composition API) +- **Build Tool**: Vite 5.4+ +- **Language**: TypeScript 5.6+ +- **State Management**: Pinia 2.2+ (supports persistence) +- **Routing**: Vue Router 4.4+ +- **UI Component Libraries**: + - Ant Design Vue 4.2+ + - Element Plus 2.13+ + - VXE Table 4.17+ +- **Utility Libraries**: + - Axios - HTTP requests + - VueUse - Vue composition utilities + - Day.js - Date handling + - Mammoth - Word document parsing + +--- + +# 🎨 Main Feature Modules + +Toonflow Web provides a complete frontend for short drama creation, including these core modules: + +- ✅ **Project Management** + Create, edit, and manage drama projects with project status tracking and multi-project parallel development. + +- ✅ **Raw Text Editing** + Import and edit novel manuscripts, supports Word document parsing, intelligent text cleansing, and chapter splitting. + +- ✅ **Character Asset Library** + Manage character settings and images; supports batch generation, manual upload, and online editing. + +- ✅ **Outline Management** + Visual editing of story outlines and event lines, with drag-and-drop sorting and smart generation. + +- ✅ **Script Editor** + Structured script editing interface, supports labeling dialogues, scenes, emotions, and more dimensions. + +- ✅ **Storyboard Design** + Visual storyboard canvas with drag-and-drop layout, image detection, and AI conversational storyboard generation. + +- ✅ **Video Configuration** + Configure video generation parameters, support switching among multiple AI video providers, and video downloading. + +- ✅ **Task Monitoring** + Real-time monitoring of AI generation tasks, supports task queue management and history search. + +- ✅ **System Settings** + Configure AI providers, prompt templates, user permissions, and other system-level parameters. + +--- + +# 📦 Application Scenarios + +- Frontend operation interface for short drama content creation +- Visual platform for AI-assisted screenwriting tools +- Workbench for storyboard design and video generation +- Scenario management system for multi-user collaboration + +--- + +# 🚀 Getting Started Quickly + +## 💡 What kind of user are you? + +| User Type | Recommended Solution | GitHub | Gitee | +| ------------------------------------------- | -------------------- | -------------------------------------------------------- | ------------------------------------------------------- | +| 🎬 **Regular User** - Want to use Toonflow | Download full client | [Toonflow-app](https://github.com/HBAI-Ltd/Toonflow-app) | [Toonflow-app](https://gitee.com/HBAI-Ltd/Toonflow-app) | +| 👨‍💻 **Developer** - Want to modify or extend | Continue reading | This repository | This repository | + +--- + +## Prerequisites + +Before developing and running this project, please ensure you have installed: + +- ✅ **Node.js**: version 23.11.1 or above +- ✅ **Yarn**: version 1.22.0 or above (recommended package manager) +- ✅ **Backend service**: Make sure the Toonflow backend service is running and accessible (available from [GitHub](https://github.com/HBAI-Ltd/Toonflow-app) or [Gitee](https://gitee.com/HBAI-Ltd/Toonflow-app)) + +## Local Development + +### 1. Clone the Project + +**GitHub:** + +```bash +git clone https://github.com/HBAI-Ltd/Toonflow-web.git +cd Toonflow-web +``` + +**Gitee (recommended for China):** + +```bash +git clone https://gitee.com/HBAI-Ltd/Toonflow-web.git +cd Toonflow-web +``` + +### 2. Install Dependencies + +```bash +yarn install +``` + +### 3. Start the Development Server + +```bash +yarn dev +``` + +The dev server runs at `http://localhost:5173` by default, with hot module replacement (HMR) enabled. + +### 4. Build for Production + +```bash +# Development build +yarn build:dev + +# Production build +yarn build:prod +``` + +The build output will be in the `dist` directory. + +### 5. Preview Production Build + +```bash +yarn preview +``` + +--- + +## Production Deployment + +### Method 1: Static File Deployment + +1. **Build the Project** + +```bash +yarn build:prod +``` + +2. **Deploy to Web Server** + +Upload all files in the `dist` directory to the root directory of Nginx, Apache, or any other web server. + +**Sample Nginx configuration:** + +```nginx +server { + listen 80; + server_name your-domain.com; + root /var/www/toonflow-web/dist; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } + + # API Proxy (optional) + location /api/ { + proxy_pass http://localhost:10588/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } +} +``` + +### Method 2: Integrated deployment with backend + +Copy the contents of the built `dist` directory into the backend's static resources directory `scripts/web`. + +> 💡 **Note**: The backend service can be obtained from [GitHub](https://github.com/HBAI-Ltd/Toonflow-app) or [Gitee](https://gitee.com/HBAI-Ltd/Toonflow-app). + +--- + +# 🔧 Development Guide + +## Prepare Development Environment + +- **Node.js**: version 23.11.1 or above required +- **Yarn**: recommended package manager + +## Common Commands + +```bash +# Install dependencies +yarn install + +# Start development server (supports hot reload) +yarn dev + +# Type checking +yarn type-check + +# Code linting and auto-fix +yarn lint + +# Code formatting +yarn format + +# Development build +yarn build:dev + +# Production build +yarn build:prod + +# Preview production build +yarn preview + +# Generate third-party license list +yarn license +``` + +## Project Structure + +``` +📂 Toonflow-web/ +├─ 📂 public/ # Static assets +├─ 📂 scripts/ # Build scripts +│ └─ 📄 license.ts # License generator script +├─ 📂 src/ +│ ├─ 📂 assets/ # Static assets (styles, images, etc.) +│ │ └─ 📄 main.css # Global styles +│ ├─ 📂 components/ # Common components +│ │ ├─ 📄 sider.vue # Sidebar component +│ │ ├─ 📂 chat/ # Chat components +│ │ ├─ 📂 storyboardEditor/ # Storyboard editor +│ │ └─ 📂 videoConfig/ # Video config components +│ ├─ 📂 config/ # Configuration files +│ │ └─ 📄 manufacturerConfig.ts # Manufacturer configuration +│ ├─ 📂 pages/ # Page components +│ │ ├─ 📂 error/ # Error pages +│ │ ├─ 📂 login/ # Login page +│ │ └─ 📂 workbench/ # Workbench +│ ├─ 📂 router/ # Routing configuration +│ │ └─ 📄 index.ts # Route definitions +│ ├─ 📂 stores/ # Pinia state management +│ │ ├─ 📄 index.ts # Store entry +│ │ ├─ 📄 loadingStore.ts # Loading state +│ │ ├─ 📄 user.ts # User state +│ │ └─ 📄 video.ts # Video state +│ ├─ 📂 types/ # TypeScript type definitions +│ │ ├─ 📄 auto-imports.d.ts # Auto-imported types +│ │ ├─ 📄 components.d.ts # Component types +│ │ ├─ 📄 global.d.ts # Global types +│ │ ├─ 📄 manufacturer.ts # Manufacturer types +│ │ └─ 📄 shims-vue.d.ts # Vue module declarations +│ ├─ 📂 utils/ # Utility functions +│ │ ├─ 📄 axios.ts # HTTP request wrapper +│ │ ├─ 📄 combineImages.ts # Image composition +│ │ ├─ 📄 error.ts # Error handling +│ │ ├─ 📄 parseNovel.ts # Novel parsing +│ │ ├─ 📄 splitGraph.ts # Image segmentation +│ │ ├─ 📄 throttle.ts # Throttling/debouncing +│ │ └─ 📄 wsClient.ts # WebSocket client +│ ├─ 📂 views/ # View pages +│ │ ├─ 📂 project/ # Project management +│ │ ├─ 📂 projectDetail/ # Project details +│ │ │ ├─ 📂 components/ +│ │ │ │ ├─ 📂 assetsManager/ # Asset management +│ │ │ │ ├─ 📂 originalText/ # Raw text +│ │ │ │ ├─ 📂 outlineManager/ # Outline management +│ │ │ │ ├─ 📂 overview/ # Project overview +│ │ │ │ └─ 📂 scriptManager/ # Script management +│ │ ├─ 📂 setting/ # System settings +│ │ └─ 📂 taskList/ # Task list +│ ├─ 📄 App.vue # Root component +│ └─ 📄 main.ts # App entry point +├─ 📄 components.d.ts # Global component types +├─ 📄 eslint.config.js # ESLint config +├─ 📄 index.html # HTML template +├─ 📄 package.json # Project config +├─ 📄 tsconfig.json # TypeScript config +├─ 📄 tsconfig.app.json # App TS config +├─ 📄 tsconfig.node.json # Node TS config +├─ 📄 vite.config.ts # Vite config +├─ 📄 LICENSE # License +├─ 📄 NOTICES.txt # Third-party notices +└─ 📄 README.md # Project documentation +``` + +--- + +# 📝 Development Plan + +We are continuously optimizing the frontend experience. Key development focuses in the near future: + +1. **UI/UX Optimization** + - `🎨 Dark theme support` Elegant dark mode option to protect eyesight + - `📱 Responsive layout` Improved display for mobile and tablet devices + - `⌨️ Shortcut system` Adding shortcuts for common operations to improve efficiency + +2. **Feature Enhancements** + - `📊 Data visualization` Advanced project statistics and chart displays + - `🔄 Real-time collaboration` Multi-user simultaneous editing and real-time sync + - `💾 Auto-save` Smart auto-save to prevent data loss + +3. **Performance Optimization** + - `⚡ Virtual scrolling` High-performance optimization for large lists + - `🗜️ Asset compression` Optimizing build size for faster loading + - `🔌 Lazy loading optimization` Further optimize route and component lazy loading strategy + +--- + +# 🐛 FAQ + +### Q: Port is occupied when starting dev server? + +**A:** Modify the port configuration in `vite.config.ts`: + +```typescript +export default defineConfig({ + server: { + port: 3000, // change to another port + }, +}); +``` + +### Q: How to configure the backend API address? + +**A:** Set the backend address in `.env.dev`: + +```bash +VITE_TYPE=dev +VITE_BASE_URL=http://127.0.0.1:10588 +VITE_WS_URL=ws://127.0.0.1:10588 +``` + +### Q: I just want to use Toonflow, not develop, what should I do? + +**A:** Please go to the main repository to download the complete client: + +- **GitHub**: 👉 [Toonflow-app](https://github.com/HBAI-Ltd/Toonflow-app) +- **Gitee**: 👉 [Toonflow-app](https://gitee.com/HBAI-Ltd/Toonflow-app) + +--- + +# 🔗 Related Repositories + +| Repository | Description | GitHub | Gitee | +| ---------------- | ---------------------------------- | -------------------------------------------------- | ------------------------------------------------ | +| **Toonflow-app** | Complete client (recommended user) | [GitHub](https://github.com/HBAI-Ltd/Toonflow-app) | [Gitee](https://gitee.com/HBAI-Ltd/Toonflow-app) | +| **Toonflow-web** | Frontend source code (this repo) | [GitHub](https://github.com/HBAI-Ltd/Toonflow-web) | [Gitee](https://gitee.com/HBAI-Ltd/Toonflow-web) | + +--- + +# 👨‍👩‍👧‍👦 WeChat Community Group + +~~Group 1~~ + +~~Group 2~~ + +~~Group 3~~ + +~~Group 4~~ + +~~Group 5~~ + +Group 6: + +Toonflow Logo +

Scan with WeChat to add. If the QR code expires, submit an Issue for update.

+ +--- + +# 💌 Contact Us + +📧 Email: [ltlctools@outlook.com](mailto:ltlctools@outlook.com?subject=Toonflow%20Frontend%20Consultation) + +--- + +# 📜 License + +Toonflow Web is open-sourced under the AGPL-3.0 license. See details: https://www.gnu.org/licenses/agpl-3.0.html + +You may use Toonflow Web for any purposes, including commercial, as long as you comply with the AGPL-3.0 terms and conditions. + +If you wish to obtain a proprietary commercial license free from AGPL-3.0 restrictions, please contact us via email. + +--- + +# ⭐️ Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=HBAI-Ltd/Toonflow-web&type=date&legend=top-left)](https://www.star-history.com/#HBAI-Ltd/Toonflow-web&type=date&legend=top-left) + +--- + +# 🙏 Acknowledgements + +Thanks to the following open source projects for their strong support for Toonflow Web: + +- [Vue.js](https://vuejs.org/) - Progressive JavaScript framework +- [Vite](https://vitejs.dev/) - Next generation frontend build tool +- [Ant Design Vue](https://antdv.com/) - Enterprise-level UI component library +- [Element Plus](https://element-plus.org/) - Vue 3-based component library +- [Pinia](https://pinia.vuejs.org/) - Intuitive state management library for Vue + +Thanks to the following organizations/units/individuals for supporting Toonflow: + + + + + + +
+ Suan Neng Yun Logo + + Suan Neng Yun provides compute sponsorship + [Official Site] +
+ +For a complete list of third-party dependencies, please refer to `NOTICES.txt` diff --git a/web-core/docs/logo.png b/web-core/docs/logo.png new file mode 100644 index 0000000..f23c2ea Binary files /dev/null and b/web-core/docs/logo.png differ diff --git a/web-core/docs/sponsored/sophnet.png b/web-core/docs/sponsored/sophnet.png new file mode 100644 index 0000000..53db918 Binary files /dev/null and b/web-core/docs/sponsored/sophnet.png differ diff --git a/web-core/docs/videoCover.png b/web-core/docs/videoCover.png new file mode 100644 index 0000000..a6aa056 Binary files /dev/null and b/web-core/docs/videoCover.png differ diff --git a/web-core/docs/videoQR.png b/web-core/docs/videoQR.png new file mode 100644 index 0000000..83aa151 Binary files /dev/null and b/web-core/docs/videoQR.png differ diff --git a/web-core/index.html b/web-core/index.html new file mode 100644 index 0000000..00dd1ed --- /dev/null +++ b/web-core/index.html @@ -0,0 +1,13 @@ + + + + + + + Toonflow + + +
+ + + diff --git a/web-core/package.json b/web-core/package.json new file mode 100644 index 0000000..d716153 --- /dev/null +++ b/web-core/package.json @@ -0,0 +1,67 @@ +{ + "name": "toonflow_web", + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite --host", + "preview": "vite preview", + "build": "vue-tsc --build --force && vite build", + "build-only": "vite build", + "type-check": "vue-tsc --build --force", + "license": "node scripts/license.ts", + "i18n:check": "node scripts/findUnusedI18n.ts" + }, + "dependencies": { + "@dagrejs/dagre": "^3.0.0", + "@devui-design/icons": "^1.4.0", + "@icon-park/vue-next": "^1.4.2", + "@tdesign-vue-next/chat": "^0.5.1", + "@vue-flow/background": "^1.3.2", + "@vue-flow/controls": "^1.1.3", + "@vue-flow/core": "^1.48.2", + "@vueuse/core": "^14.1.0", + "@webav/av-canvas": "^1.2.8", + "@webav/av-cliper": "^1.2.8", + "axios": "^1.13.2", + "clsx": "^2.1.1", + "dayjs": "^1.11.19", + "js-confetti": "^0.13.1", + "lodash": "^4.17.23", + "mammoth": "^1.12.0", + "md-editor-v3": "^6.4.0", + "monaco-editor": "^0.55.1", + "monaco-editor-vue3": "^1.0.5", + "p-limit": "^7.3.0", + "pinia": "^2.2.6", + "pinia-plugin-persistedstate": "^4.7.1", + "sass": "^1.97.0", + "socket.io-client": "^4.8.3", + "splitpanes": "^4.0.4", + "tdesign-vue-next": "^1.18.5", + "uuid": "^13.0.0", + "vue": "^3.5.12", + "vue-clip-track": "^0.1.5", + "vue-draggable-plus": "^0.6.1", + "vue-i18n": "11", + "vue-router": "^4.4.5" + }, + "devDependencies": { + "@tdesign-vue-next/auto-import-resolver": "^0.1.6", + "@tsconfig/node22": "^22.0.0", + "@types/license-checker": "^25.0.6", + "@types/node": "^22.9.0", + "@types/splitpanes": "^2.2.6", + "@types/uuid": "^11.0.0", + "@vitejs/plugin-vue": "^6.0.5", + "@vue/tsconfig": "^0.5.1", + "license-checker": "^25.0.1", + "postcss-px-to-viewport": "^1.1.1", + "prettier": "^3.3.3", + "typescript": "~5.6.3", + "unplugin-auto-import": "^21.0.0", + "unplugin-vue-components": "^31.0.0", + "vite": "^5.4.10", + "vite-plugin-singlefile": "^2.3.0", + "vue-tsc": "^2.1.10" + } +} diff --git a/web-core/public/favicon.ico b/web-core/public/favicon.ico new file mode 100644 index 0000000..2402d02 Binary files /dev/null and b/web-core/public/favicon.ico differ diff --git a/web-core/public/favicon.svg b/web-core/public/favicon.svg new file mode 100644 index 0000000..96ae552 --- /dev/null +++ b/web-core/public/favicon.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + diff --git a/web-core/scripts/findUnusedI18n.ts b/web-core/scripts/findUnusedI18n.ts new file mode 100644 index 0000000..7311b20 --- /dev/null +++ b/web-core/scripts/findUnusedI18n.ts @@ -0,0 +1,311 @@ +import fs from 'fs'; +import path from 'path'; + +// 配置 +const localesDir = './src/locales/language'; +const srcDir = './src'; + +// 递归获取文件 +function getFiles(dir: string, extensions: string[]): string[] { + const files: string[] = []; + + function walk(currentDir: string): void { + const items = fs.readdirSync(currentDir, { withFileTypes: true }); + + for (const item of items) { + const fullPath = path.join(currentDir, item.name); + + if (item.isDirectory()) { + if (!['node_modules', 'dist', '.git', 'locales'].includes(item.name)) { + walk(fullPath); + } + } else if (item.isFile()) { + const ext = path.extname(item.name); + if (extensions.includes(ext)) { + files.push(fullPath); + } + } + } + } + + walk(dir); + return files; +} + +// 获取所有 i18n key +function getAllI18nKeys(): Set { + const keys = new Set(); + const files = getFiles(localesDir, ['.json']); + + files.forEach(file => { + const content = JSON.parse(fs.readFileSync(file, 'utf-8')); + extractKeys(content, '', keys); + }); + + return keys; +} + +function extractKeys(obj: Record, prefix: string, keys: Set): void { + for (const key in obj) { + const fullKey = prefix ? `${prefix}.${key}` : key; + if (typeof obj[key] === 'object' && obj[key] !== null) { + extractKeys(obj[key] as Record, fullKey, keys); + } else { + keys.add(fullKey); + } + } +} + +// 判断字符串是否像 i18n key +function looksLikeI18nKey(str: string): boolean { + if (!str.includes('.')) return false; + if (str.startsWith('http')) return false; + if (str.includes('/')) return false; + if (str.includes(' ')) return false; + if (/^\d/.test(str)) return false; + if (str.includes('@')) return false; + return /^[a-zA-Z][a-zA-Z0-9]*(\.[a-zA-Z][a-zA-Z0-9]*)+$/.test(str); +} + +// 扫描源码中使用的 key +function getUsedKeys(allKeys: Set): Set { + const used = new Set(); + const files = getFiles(srcDir, ['.vue', '.js', '.ts', '.jsx', '.tsx']); + + const patterns = [ + /\$t\s*\(\s*['"`]([^'"`]+)['"`]/g, + /\bt\s*\(\s*['"`]([^'"`]+)['"`]/g, + /i18n\.t\s*\(\s*['"`]([^'"`]+)['"`]/g, + /label:\s*['"`]([^'"`]+)['"`]/g, + /title:\s*['"`]([^'"`]+)['"`]/g, + /placeholder:\s*['"`]([^'"`]+)['"`]/g, + /message:\s*['"`]([^'"`]+)['"`]/g, + /text:\s*['"`]([^'"`]+)['"`]/g, + ]; + + files.forEach(file => { + const content = fs.readFileSync(file, 'utf-8'); + + patterns.forEach(pattern => { + let match; + pattern.lastIndex = 0; + while ((match = pattern.exec(content)) !== null) { + used.add(match[1]); + } + }); + + allKeys.forEach(key => { + if ( + content.includes(`'${key}'`) || + content.includes(`"${key}"`) || + content.includes(`\`${key}\``) + ) { + used.add(key); + } + }); + }); + + return used; +} + +// 检查 key 是否存在 +function keyExists(key: string, allKeys: Set): boolean { + if (allKeys.has(key)) return true; + for (const existKey of allKeys) { + if (existKey.startsWith(`${key}.`)) return true; + } + return false; +} + +// 硬编码文本信息 +interface HardcodedText { + file: string; + line: number; + text: string; + context: string; +} + +// 检测硬编码的中英文 +function findHardcodedTexts(): HardcodedText[] { + const results: HardcodedText[] = []; + const files = getFiles(srcDir, ['.vue', '.js', '.ts', '.jsx', '.tsx']); + + // 中文匹配 + const chinesePattern = /[\u4e00-\u9fa5]+[^'"`]*[\u4e00-\u9fa5]*/g; + + // 英文短语匹配(至少两个单词,排除常见代码模式) + const englishPattern = /['"`]([A-Z][a-z]+(?:\s+[a-zA-Z]+){1,10})['"` ]/g; + + // 需要忽略的模式 + const ignorePatterns = [ + /console\.(log|warn|error|info)/, + /\/\/.*/, + /\/\*[\s\S]*?\*\//, + /import\s+/, + /export\s+/, + /require\s*\(/, + /^\s*\*/, + /eslint-disable/, + /TODO:|FIXME:|NOTE:/, + /http[s]?:\/\//, + /\.(vue|js|ts|css|scss|json|png|jpg|svg)/, + ]; + + // 常见的可忽略英文 + const ignoreEnglishWords = new Set([ + 'New Tab', + 'Click Me', + 'Hello World', + 'Vue Router', + 'Event Bus', + 'Local Storage', + 'Session Storage', + 'Content Type', + 'User Agent', + 'Access Control', + ]); + + files.forEach(filePath => { + const content = fs.readFileSync(filePath, 'utf-8'); + const lines = content.split('\n'); + + lines.forEach((line, index) => { + // 跳过注释和导入 + const shouldIgnore = ignorePatterns.some(pattern => pattern.test(line)); + if (shouldIgnore) return; + + // 跳过已使用 $t 或 t() 的行 + if (/\$t\s*\(/.test(line) || /\bt\s*\(/.test(line)) return; + + // 检测中文 + const chineseMatches = line.match(chinesePattern); + if (chineseMatches) { + chineseMatches.forEach(text => { + // 过滤掉注释中的中文 + if (line.trimStart().startsWith('//')) return; + if (line.trimStart().startsWith('*')) return; + + // 检查是否在字符串中 + const inString = + line.includes(`'${text}`) || + line.includes(`"${text}`) || + line.includes(`\`${text}`); + + if (inString && text.length >= 2) { + results.push({ + file: filePath, + line: index + 1, + text: text.trim(), + context: line.trim().slice(0, 100), + }); + } + }); + } + + // 检测英文短语 + let match; + englishPattern.lastIndex = 0; + while ((match = englishPattern.exec(line)) !== null) { + const text = match[1]; + + // 过滤 + if (ignoreEnglishWords.has(text)) continue; + if (text.length < 5) continue; + if (/^[A-Z_]+$/.test(text)) continue; // 常量 + if (looksLikeI18nKey(text)) continue; + + // 看起来像用户可见文本 + if (/^[A-Z][a-z]+(\s+[a-zA-Z]+)+$/.test(text)) { + results.push({ + file: filePath, + line: index + 1, + text, + context: line.trim().slice(0, 100), + }); + } + } + }); + }); + + // 去重 + const unique = new Map(); + results.forEach(item => { + const key = `${item.file}:${item.line}:${item.text}`; + if (!unique.has(key)) { + unique.set(key, item); + } + }); + + return [...unique.values()]; +} + +// 主逻辑 +function main(): void { + const allKeys = getAllI18nKeys(); + const usedKeys = getUsedKeys(allKeys); + + console.log(`\n📊 总 key 数量: ${allKeys.size}`); + console.log(`📊 使用中的 key: ${usedKeys.size}`); + + // 未使用的 key + const unused = [...allKeys].filter(key => !usedKeys.has(key)); + console.log(`\n❌ 未使用的 key (${unused.length}):\n`); + unused.sort().forEach(key => console.log(` - ${key}`)); + + // 缺失的 key + const missing = [...usedKeys].filter(key => { + if (!looksLikeI18nKey(key)) return false; + return !keyExists(key, allKeys); + }); + + if (missing.length) { + console.log(`\n⚠️ 缺失的 key (${missing.length}):\n`); + missing.sort().forEach(key => console.log(` - ${key}`)); + } + + // 硬编码文本 + console.log('\n🔍 检测硬编码文本...'); + const hardcoded = findHardcodedTexts(); + + if (hardcoded.length) { + console.log(`\n📝 硬编码文本 (${hardcoded.length}):\n`); + + // 按文件分组 + const byFile = new Map(); + hardcoded.forEach(item => { + const list = byFile.get(item.file) || []; + list.push(item); + byFile.set(item.file, list); + }); + + byFile.forEach((items, file) => { + console.log(`\n 📄 ${file}`); + items.forEach(item => { + console.log(` L${item.line}: "${item.text}"`); + }); + }); + } + + // // 输出报告 + // const report = { + // summary: { + // totalKeys: allKeys.size, + // usedKeys: usedKeys.size, + // unusedCount: unused.length, + // missingCount: missing.length, + // hardcodedCount: hardcoded.length, + // }, + // unused, + // missing, + // hardcoded: hardcoded.map(h => ({ + // file: h.file, + // line: h.line, + // text: h.text, + // })), + // }; + + // fs.writeFileSync('./i18n-report.json', JSON.stringify(report, null, 2)); + // console.log('\n✅ 报告已保存到 i18n-report.json'); +} + +main(); \ No newline at end of file diff --git a/web-core/scripts/license.ts b/web-core/scripts/license.ts new file mode 100644 index 0000000..5d04fc9 --- /dev/null +++ b/web-core/scripts/license.ts @@ -0,0 +1,72 @@ +import * as fs from "fs"; +import * as path from "path"; +import checker from "license-checker"; + +const excludeNames = ["toonflow-serve"]; +// const strictWhiteList = ["MIT", "BSD-2-Clause", "BSD-3-Clause", "BSD", "0BSD"]; +const strictWhiteList: string[] = []; + +// 检查是否在白名单协议 +function isStrictWhiteLicense(license: string): boolean { + const normalized = license.replace(/[\(\)]/g, "").trim(); + const parts = normalized.split(/\s*(OR|AND|\/)\s*/i).map((part) => part.trim()); + return parts.every((part) => strictWhiteList.some((wl) => part === wl || part.replace(/ with .*/i, "") === wl)); +} + +// 读取 package.json 里的直接依赖 +function getDirectDependencyNames(): string[] { + const pkg = JSON.parse(fs.readFileSync(path.join(process.cwd(), "package.json"), "utf-8")); + const deps = Object.keys(pkg.dependencies ?? {}); + const devDeps = Object.keys(pkg.devDependencies ?? {}); + return [...deps, ...devDeps]; +} + +// 执行主逻辑 +checker.init({ start: process.cwd() }, (err: Error, packages: Record) => { + if (err) { + console.error("license-checker 出错: ", err); + process.exit(1); + } + const directNames = getDirectDependencyNames(); + + interface PackageInfo { + name: string; + version: string; + licenses: string | string[]; + repository: string | undefined; + } + + const needDeclare: PackageInfo[] = []; + for (const fullName in packages) { + // fullName 一般形如 [@scope/]pkg@version, 但 license-checker 会带路径,如 @scope/name@1.0.0@./node_modules/@scope/name + // 所以可以正则只保留 name@version 部分 + // nameMatch[1] 为包名,nameMatch[2] 为版本 + const nameMatch = fullName.match(/^((?:@[^\/]+\/)?[^@]+)@([^@]+)$/); + if (!nameMatch) continue; + const name = nameMatch[1]; + // 仅关注直接依赖 + if (!directNames.includes(name!)) continue; + + const info = packages[fullName]; + const licenseArr: string[] = Array.isArray(info.licenses) ? info.licenses : [info.licenses]; + if (!licenseArr.every(isStrictWhiteLicense)) { + needDeclare.push({ + name: name!, + version: info.version, + licenses: licenseArr, + repository: info.repository, + }); + } + } + + // 排除名单过滤 + const filteredDeclare = needDeclare.filter((pkg) => pkg.name && !excludeNames.some((exName) => pkg.name.startsWith(exName))); + const content = filteredDeclare + .map( + (pkg) => + `Name: ${pkg.name}\nLicense: ${Array.isArray(pkg.licenses) ? pkg.licenses.join(", ") : pkg.licenses}\nRepository: ${pkg.repository ?? "N/A"}`, + ) + .join("\n\n-----------------------------\n\n"); + fs.writeFileSync(path.resolve(process.cwd(), "NOTICES.txt"), content, "utf-8"); + console.log("已生成依赖声明 NOTICES.txt"); +}); diff --git a/web-core/src/App.vue b/web-core/src/App.vue new file mode 100644 index 0000000..8d2a17a --- /dev/null +++ b/web-core/src/App.vue @@ -0,0 +1,132 @@ + + + + + diff --git a/web-core/src/assets/atomgit.svg b/web-core/src/assets/atomgit.svg new file mode 100644 index 0000000..3b82cde --- /dev/null +++ b/web-core/src/assets/atomgit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/web-core/src/assets/bg.png b/web-core/src/assets/bg.png new file mode 100644 index 0000000..f3abd20 Binary files /dev/null and b/web-core/src/assets/bg.png differ diff --git a/web-core/src/assets/logo.png b/web-core/src/assets/logo.png new file mode 100644 index 0000000..8914434 Binary files /dev/null and b/web-core/src/assets/logo.png differ diff --git a/web-core/src/assets/logo.svg b/web-core/src/assets/logo.svg new file mode 100644 index 0000000..ffbe33f --- /dev/null +++ b/web-core/src/assets/logo.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + diff --git a/web-core/src/assets/main.scss b/web-core/src/assets/main.scss new file mode 100644 index 0000000..e6e2590 --- /dev/null +++ b/web-core/src/assets/main.scss @@ -0,0 +1,1361 @@ +:root { + --titlebar-height: 0px; +} + +html, +body, +#app { + margin: 0; + background-color: var(--bgc); + color: var(--td-text-color-primary); +} + +img { + max-width: 100%; +} + +// 防止弹窗出现时滚动条消失导致页面宽度变化引起抖动 +html { + scrollbar-gutter: stable; +} + +// TDesign Dialog 弹出时会给 body 加 overflow:hidden,用 padding-right 补偿滚动条宽度 +// 但若页面本身无滚动条此补偿无效,直接禁用 body 的 overflow 锁定 +body.t-overflow-hidden { + overflow: auto !important; + padding-right: 0 !important; +} + +// Electron titleBar 偏移:一劳永逸处理所有 TDesign 固定定位弹出层被 titleBar 遮挡的问题 +// 如果新增了 TDesign 弹出层组件,在此处补充对应 class 即可 +body.is-electron { + --titlebar-height: 32px; + // Drawer 抽屉 + .t-drawer__wrap { + top: var(--titlebar-height) !important; + height: calc(100vh - var(--titlebar-height)) !important; + } + .t-drawer__content-wrapper { + top: 30px; + bottom: 0px; + height: auto; + } + + // Dialog 对话框 + .t-dialog__ctx { + top: var(--titlebar-height) !important; + height: calc(100vh - var(--titlebar-height)) !important; + } + + // ImageViewer 图片预览 + .t-image-viewer-preview__header, + .t-image-viewer__modal { + top: var(--titlebar-height) !important; + } + .t-image-viewer__modal { + height: calc(100vh - var(--titlebar-height)) !important; + } + + // Loading 全屏加载 + .t-loading__fullscreen { + top: var(--titlebar-height) !important; + height: calc(100vh - var(--titlebar-height)) !important; + } + + // Guide 引导 + .t-guide__highlight--popup, + .t-guide__overlay { + top: var(--titlebar-height) !important; + } + + // Message 全局提示 + .t-message__list { + top: calc(var(--titlebar-height) + 16px) !important; + } + + // Notification 通知 + .t-notification__show--top-left, + .t-notification__show--top-right { + top: calc(var(--titlebar-height) + 16px) !important; + } + .t-notification-list__show { + top: calc(var(--titlebar-height) + 16px) !important; + } +} +* { + box-sizing: border-box; +} + +.pr { + position: relative; +} +.pa { + position: absolute; +} +.pc { + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.c { + display: flex; + justify-content: center; + align-items: center; +} +.f { + display: flex; +} +.jc { + display: flex; + justify-content: center; +} +.jb { + display: flex; + justify-content: space-between; +} +.ja { + display: flex; + justify-content: space-around; +} +.je { + display: flex; + justify-content: space-evenly; +} +.ac { + display: flex; + align-items: center; +} +.fc { + display: flex; + flex-direction: column; +} +.fcr { + display: flex; + flex-direction: column-reverse; +} +.frr { + display: flex; + flex-direction: row-reverse; +} +.nw { + display: flex; + flex-wrap: nowrap; +} +.w { + display: flex; + flex-wrap: wrap; +} + +.spin { + animation: spin 1s linear infinite; +} +@keyframes spin { + 100% { + transform: rotate(360deg); + } +} +*::-webkit-scrollbar { + width: 6px; +} +*::-webkit-scrollbar-thumb { + background-color: #d5d5d5; + border-radius: 4px; + &:hover { + background-color: #bbb; + } +} +*::-webkit-scrollbar-track { + background-color: transparent; +} + +.noFooter { + .t-dialog__body--fullscreen--without-footer { + height: 100% !important; + } +} +.fullscreenDialog { + .t-dialog__wrap { + overflow: hidden !important; + } +} +:root, +:root[theme-mode="light"] { + --bgc: #ebebeb; + --page: #fff; + --td-brand-color-1: #f3f3f3; + --td-brand-color-2: #e3e3e3; + --td-brand-color-3: #c6c6c6; + --td-brand-color-4: #a9a9a9; + --td-brand-color-5: #8c8c8c; + --td-brand-color-6: #717171; + --td-brand-color-7: #595959; + --td-brand-color-8: #434343; + --td-brand-color-9: #303030; + --td-brand-color-10: #000000; + --td-warning-color-1: #fff1e9; + --td-warning-color-2: #ffd9c2; + --td-warning-color-3: #ffb98c; + --td-warning-color-4: #fa9550; + --td-warning-color-5: #e37318; + --td-warning-color-6: #be5a00; + --td-warning-color-7: #954500; + --td-warning-color-8: #713300; + --td-warning-color-9: #532300; + --td-warning-color-10: #3b1700; + --td-error-color-1: #fff0ed; + --td-error-color-2: #ffd8d2; + --td-error-color-3: #ffb9b0; + --td-error-color-4: #ff9285; + --td-error-color-5: #f6685d; + --td-error-color-6: #d54941; + --td-error-color-7: #ad352f; + --td-error-color-8: #881f1c; + --td-error-color-9: #68070a; + --td-error-color-10: #490002; + --td-success-color-1: #e3f9e9; + --td-success-color-2: #c6f3d7; + --td-success-color-3: #92dab2; + --td-success-color-4: #56c08d; + --td-success-color-5: #2ba471; + --td-success-color-6: #008858; + --td-success-color-7: #006c45; + --td-success-color-8: #005334; + --td-success-color-9: #003b23; + --td-success-color-10: #002515; + --td-gray-color-1: #f6f6f6; + --td-gray-color-2: #eeeceb; + --td-gray-color-3: #dddbdb; + --td-gray-color-4: #cac9c9; + --td-gray-color-5: #b6b6b6; + --td-gray-color-6: #a2a2a2; + --td-gray-color-7: #8f8f8f; + --td-gray-color-8: #7b7c7c; + --td-gray-color-9: #6a6b6b; + --td-gray-color-10: #585a5a; + --td-gray-color-11: #494a4a; + --td-gray-color-12: #3a3b3b; + --td-gray-color-13: #2c2e2e; + --td-gray-color-14: #202222; + --td-font-white-1: #fff; + --td-font-white-2: rgba(255, 255, 255, 0.55); + --td-font-white-3: rgba(255, 255, 255, 0.35); + --td-font-white-4: rgba(255, 255, 255, 0.22); + --td-font-gray-1: rgba(0, 0, 0, 0.9); + --td-font-gray-2: rgba(0, 0, 0, 0.6); + --td-font-gray-3: rgba(0, 0, 0, 0.4); + --td-font-gray-4: rgba(0, 0, 0, 0.26); + --td-brand-color: var(--td-brand-color-10); + --td-warning-color: var(--td-warning-color-5); + --td-error-color: var(--td-error-color-6); + --td-success-color: var(--td-success-color-5); + --td-brand-color-hover: var(--td-brand-color-9); + --td-brand-color-focus: var(--td-brand-color-2); + --td-brand-color-active: var(--td-brand-color-10); + --td-brand-color-disabled: var(--td-brand-color-3); + --td-brand-color-light: var(--td-brand-color-1); + --td-brand-color-light-hover: var(--td-brand-color-2); + --td-warning-color-hover: var(--td-warning-color-4); + --td-warning-color-focus: var(--td-warning-color-2); + --td-warning-color-active: var(--td-warning-color-6); + --td-warning-color-disabled: var(--td-warning-color-3); + --td-warning-color-light: var(--td-warning-color-1); + --td-warning-color-light-hover: var(--td-warning-color-2); + --td-error-color-hover: var(--td-error-color-5); + --td-error-color-focus: var(--td-error-color-2); + --td-error-color-active: var(--td-error-color-7); + --td-error-color-disabled: var(--td-error-color-3); + --td-error-color-light: var(--td-error-color-1); + --td-error-color-light-hover: var(--td-error-color-2); + --td-success-color-hover: var(--td-success-color-4); + --td-success-color-focus: var(--td-success-color-2); + --td-success-color-active: var(--td-success-color-6); + --td-success-color-disabled: var(--td-success-color-3); + --td-success-color-light: var(--td-success-color-1); + --td-success-color-light-hover: var(--td-success-color-2); + --td-mask-active: rgba(0, 0, 0, 0.6); + --td-mask-disabled: rgba(255, 255, 255, 0.6); + --td-bg-color-page: var(--td-gray-color-2); + --td-bg-color-container: #fff; + --td-bg-color-container-hover: var(--td-gray-color-1); + --td-bg-color-container-active: var(--td-gray-color-3); + --td-bg-color-container-select: #fff; + --td-bg-color-secondarycontainer: var(--td-gray-color-1); + --td-bg-color-secondarycontainer-hover: var(--td-gray-color-2); + --td-bg-color-secondarycontainer-active: var(--td-gray-color-4); + --td-bg-color-component: var(--td-gray-color-3); + --td-bg-color-component-hover: var(--td-gray-color-4); + --td-bg-color-component-active: var(--td-gray-color-6); + --td-bg-color-secondarycomponent: var(--td-gray-color-4); + --td-bg-color-secondarycomponent-hover: var(--td-gray-color-5); + --td-bg-color-secondarycomponent-active: var(--td-gray-color-6); + --td-bg-color-component-disabled: var(--td-gray-color-2); + --td-bg-color-specialcomponent: #fff; + --td-text-color-primary: var(--td-font-gray-1); + --td-text-color-secondary: var(--td-font-gray-2); + --td-text-color-placeholder: var(--td-font-gray-3); + --td-text-color-disabled: var(--td-font-gray-4); + --td-text-color-anti: #fff; + --td-text-color-brand: var(--td-brand-color-7); + --td-text-color-link: var(--td-brand-color-8); + --td-border-level-1-color: var(--td-gray-color-3); + --td-component-stroke: var(--td-gray-color-3); + --td-border-level-2-color: var(--td-gray-color-4); + --td-component-border: var(--td-gray-color-4); + --td-shadow-1: 0 1px 10px rgba(0, 0, 0, 0.05), 0 4px 5px rgba(0, 0, 0, 0.08), 0 2px 4px -1px rgba(0, 0, 0, 0.12); + --td-shadow-2: 0 3px 14px 2px rgba(0, 0, 0, 0.05), 0 8px 10px 1px rgba(0, 0, 0, 0.06), 0 5px 5px -3px rgba(0, 0, 0, 0.1); + --td-shadow-3: 0 6px 30px 5px rgba(0, 0, 0, 0.05), 0 16px 24px 2px rgba(0, 0, 0, 0.04), 0 8px 10px -5px rgba(0, 0, 0, 0.08); + --td-shadow-inset-top: inset 0 0.5px 0 #e7eaef; + --td-shadow-inset-right: inset 0.5px 0 0 #e7eaef; + --td-shadow-inset-bottom: inset 0 -0.5px 0 #e7eaef; + --td-table-shadow-color: rgba(0, 0, 0, 0.08); + --td-scrollbar-color: rgba(0, 0, 0, 0.1); + --td-scrollbar-hover-color: rgba(0, 0, 0, 0.3); + --td-scroll-track-color: #fff; +} + +:root.dark, +:root[theme-mode="dark"] { + --bgc: #3a3b3b; + --page: #242424; + --td-brand-color-1: #000000; + --td-brand-color-2: #303030; + --td-brand-color-3: #434343; + --td-brand-color-4: #595959; + --td-brand-color-5: #717171; + --td-brand-color-6: #8c8c8c; + --td-brand-color-7: #a9a9a9; + --td-brand-color-8: #c6c6c6; + --td-brand-color-9: #e3e3e3; + --td-brand-color-10: #f3f3f3; + --td-warning-color-1: #3b1700; + --td-warning-color-2: #532300; + --td-warning-color-3: #713300; + --td-warning-color-4: #954500; + --td-warning-color-5: #be5a00; + --td-warning-color-6: #e37318; + --td-warning-color-7: #fa9550; + --td-warning-color-8: #ffb98c; + --td-warning-color-9: #ffd9c2; + --td-warning-color-10: #fff1e9; + --td-error-color-1: #490002; + --td-error-color-2: #68070a; + --td-error-color-3: #881f1c; + --td-error-color-4: #ad352f; + --td-error-color-5: #d54941; + --td-error-color-6: #f6685d; + --td-error-color-7: #ff9285; + --td-error-color-8: #ffb9b0; + --td-error-color-9: #ffd8d2; + --td-error-color-10: #fff0ed; + --td-success-color-1: #002515; + --td-success-color-2: #003b23; + --td-success-color-3: #005334; + --td-success-color-4: #006c45; + --td-success-color-5: #008858; + --td-success-color-6: #2ba471; + --td-success-color-7: #56c08d; + --td-success-color-8: #92dab2; + --td-success-color-9: #c6f3d7; + --td-success-color-10: #e3f9e9; + --td-gray-color-1: #f6f6f6; + --td-gray-color-2: #eeeceb; + --td-gray-color-3: #dddbdb; + --td-gray-color-4: #cac9c9; + --td-gray-color-5: #b6b6b6; + --td-gray-color-6: #a2a2a2; + --td-gray-color-7: #8f8f8f; + --td-gray-color-8: #7b7c7c; + --td-gray-color-9: #6a6b6b; + --td-gray-color-10: #585a5a; + --td-gray-color-11: #494a4a; + --td-gray-color-12: #3a3b3b; + --td-gray-color-13: #2c2e2e; + --td-gray-color-14: #202222; + --td-font-white-1: rgba(255, 255, 255, 0.9); + --td-font-white-2: rgba(255, 255, 255, 0.55); + --td-font-white-3: rgba(255, 255, 255, 0.35); + --td-font-white-4: rgba(255, 255, 255, 0.22); + --td-font-gray-1: rgba(0, 0, 0, 0.9); + --td-font-gray-2: rgba(0, 0, 0, 0.6); + --td-font-gray-3: rgba(0, 0, 0, 0.4); + --td-font-gray-4: rgba(0, 0, 0, 0.26); + --td-brand-color: var(--td-brand-color-6); + --td-warning-color: var(--td-warning-color-5); + --td-error-color: var(--td-error-color-6); + --td-success-color: var(--td-success-color-5); + --td-brand-color-hover: var(--td-brand-color-5); + --td-brand-color-focus: var(--td-brand-color-2); + --td-brand-color-active: var(--td-brand-color-7); + --td-brand-color-disabled: var(--td-brand-color-3); + --td-brand-color-light: var(--td-brand-color-1); + --td-brand-color-light-hover: var(--td-brand-color-2); + --td-warning-color-hover: var(--td-warning-color-4); + --td-warning-color-focus: var(--td-warning-color-2); + --td-warning-color-active: var(--td-warning-color-6); + --td-warning-color-disabled: var(--td-warning-color-3); + --td-warning-color-light: var(--td-warning-color-1); + --td-warning-color-light-hover: var(--td-warning-color-2); + --td-error-color-hover: var(--td-error-color-5); + --td-error-color-focus: var(--td-error-color-2); + --td-error-color-active: var(--td-error-color-7); + --td-error-color-disabled: var(--td-error-color-3); + --td-error-color-light: var(--td-error-color-1); + --td-error-color-light-hover: var(--td-error-color-2); + --td-success-color-hover: var(--td-success-color-4); + --td-success-color-focus: var(--td-success-color-2); + --td-success-color-active: var(--td-success-color-6); + --td-success-color-disabled: var(--td-success-color-3); + --td-success-color-light: var(--td-success-color-1); + --td-success-color-light-hover: var(--td-success-color-2); + --td-mask-active: rgba(0, 0, 0, 0.4); + --td-mask-disabled: rgba(0, 0, 0, 0.6); + --td-bg-color-page: var(--td-gray-color-14); + --td-bg-color-container: var(--td-gray-color-13); + --td-bg-color-container-hover: var(--td-gray-color-12); + --td-bg-color-container-active: var(--td-gray-color-10); + --td-bg-color-container-select: var(--td-gray-color-9); + --td-bg-color-secondarycontainer: var(--td-gray-color-12); + --td-bg-color-secondarycontainer-hover: var(--td-gray-color-11); + --td-bg-color-secondarycontainer-active: var(--td-gray-color-9); + --td-bg-color-component: var(--td-gray-color-11); + --td-bg-color-component-hover: var(--td-gray-color-10); + --td-bg-color-component-active: var(--td-gray-color-9); + --td-bg-color-secondarycomponent: var(--td-gray-color-10); + --td-bg-color-secondarycomponent-hover: var(--td-gray-color-9); + --td-bg-color-secondarycomponent-active: var(--td-gray-color-8); + --td-bg-color-component-disabled: var(--td-gray-color-12); + --td-bg-color-specialcomponent: transparent; + --td-text-color-primary: var(--td-font-white-1); + --td-text-color-secondary: var(--td-font-white-2); + --td-text-color-placeholder: var(--td-font-white-3); + --td-text-color-disabled: var(--td-font-white-4); + --td-text-color-anti: #fff; + --td-text-color-brand: var(--td-brand-color-8); + --td-text-color-link: var(--td-brand-color-8); + --td-border-level-1-color: var(--td-gray-color-11); + --td-component-stroke: var(--td-gray-color-11); + --td-border-level-2-color: var(--td-gray-color-9); + --td-component-border: var(--td-gray-color-9); + --td-shadow-1: 0 4px 6px rgba(0, 0, 0, 0.06), 0 1px 10px rgba(0, 0, 0, 0.08), 0 2px 4px rgba(0, 0, 0, 0.12); + --td-shadow-2: 0 8px 10px rgba(0, 0, 0, 0.12), 0 3px 14px rgba(0, 0, 0, 0.1), 0 5px 5px rgba(0, 0, 0, 0.16); + --td-shadow-3: 0 16px 24px rgba(0, 0, 0, 0.14), 0 6px 30px rgba(0, 0, 0, 0.12), 0 8px 10px rgba(0, 0, 0, 0.2); + --td-shadow-inset-top: inset 0 0.5px 0 #5e5e5e; + --td-shadow-inset-right: inset 0.5px 0 0 #5e5e5e; + --td-shadow-inset-bottom: inset 0 -0.5px 0 #5e5e5e; + --td-table-shadow-color: rgba(0, 0, 0, 0.55); + --td-scrollbar-color: rgba(255, 255, 255, 0.1); + --td-scrollbar-hover-color: rgba(255, 255, 255, 0.3); + --td-scroll-track-color: #333; +} + +:root { + --td-font-family: pingfang sc, microsoft yahei, arial regular; + --td-font-family-medium: pingfang sc, microsoft yahei, arial medium; + --td-font-size-link-small: 12px; + --td-font-size-link-medium: 14px; + --td-font-size-link-large: 16px; + --td-font-size-mark-small: 12px; + --td-font-size-mark-medium: 14px; + --td-font-size-body-small: 12px; + --td-font-size-body-medium: 14px; + --td-font-size-body-large: 16px; + --td-font-size-title-small: 14px; + --td-font-size-title-medium: 16px; + --td-font-size-title-large: 18px; + --td-font-size-title-extraLarge: 20px; + --td-font-size-headline-small: 24px; + --td-font-size-headline-medium: 28px; + --td-font-size-headline-large: 36px; + --td-font-size-display-medium: 48px; + --td-font-size-display-large: 64px; + --td-line-height-link-small: 20px; + --td-line-height-link-medium: 22px; + --td-line-height-link-large: 24px; + --td-line-height-mark-small: 20px; + --td-line-height-mark-medium: 22px; + --td-line-height-body-small: 20px; + --td-line-height-body-medium: 22px; + --td-line-height-body-large: 24px; + --td-line-height-title-small: 22px; + --td-line-height-title-medium: 24px; + --td-line-height-title-large: 26px; + --td-line-height-title-extraLarge: 28px; + --td-line-height-headline-small: 32px; + --td-line-height-headline-medium: 36px; + --td-line-height-headline-large: 44px; + --td-line-height-display-medium: 56px; + --td-line-height-display-large: 72px; + --td-font-link-small: var(--td-font-size-link-small) / var(--td-line-height-link-small) var(--td-font-family); + --td-font-link-medium: var(--td-font-size-link-medium) / var(--td-line-height-link-medium) var(--td-font-family); + --td-font-link-large: var(--td-font-size-link-large) / var(--td-line-height-link-large) var(--td-font-family); + --td-font-mark-small: 600 var(--td-font-size-mark-small) / var(--td-line-height-mark-small) var(--td-font-family); + --td-font-mark-medium: 600 var(--td-font-size-mark-medium) / var(--td-line-height-mark-medium) var(--td-font-family); + --td-font-body-small: var(--td-font-size-body-small) / var(--td-line-height-body-small) var(--td-font-family); + --td-font-body-medium: var(--td-font-size-body-medium) / var(--td-line-height-body-medium) var(--td-font-family); + --td-font-body-large: var(--td-font-size-body-large) / var(--td-line-height-body-large) var(--td-font-family); + --td-font-title-small: 600 var(--td-font-size-title-small) / var(--td-line-height-title-small) var(--td-font-family); + --td-font-title-medium: 600 var(--td-font-size-title-medium) / var(--td-line-height-title-medium) var(--td-font-family); + --td-font-title-large: 600 var(--td-font-size-title-large) / var(--td-line-height-title-large) var(--td-font-family); + --td-font-headline-small: 600 var(--td-font-size-headline-small) / var(--td-line-height-headline-small) var(--td-font-family); + --td-font-headline-medium: 600 var(--td-font-size-headline-medium) / var(--td-line-height-headline-medium) var(--td-font-family); + --td-font-headline-large: 600 var(--td-font-size-headline-large) / var(--td-line-height-headline-large) var(--td-font-family); + --td-font-display-medium: 600 var(--td-font-size-display-medium) / var(--td-line-height-display-medium) var(--td-font-family); + --td-font-display-large: 600 var(--td-font-size-display-large) / var(--td-line-height-display-large) var(--td-font-family); + --td-radius-small: 2px; + --td-radius-default: 3px; + --td-radius-medium: 6px; + --td-radius-large: 9px; + --td-radius-extraLarge: 12px; + --td-radius-round: 999px; + --td-radius-circle: 50%; + --td-size-1: 2px; + --td-size-2: 4px; + --td-size-3: 6px; + --td-size-4: 8px; + --td-size-5: 12px; + --td-size-6: 16px; + --td-size-7: 20px; + --td-size-8: 24px; + --td-size-9: 28px; + --td-size-10: 32px; + --td-size-11: 36px; + --td-size-12: 40px; + --td-size-13: 48px; + --td-size-14: 56px; + --td-size-15: 64px; + --td-size-16: 72px; + --td-comp-size-xxxs: var(--td-size-6); + --td-comp-size-xxs: var(--td-size-7); + --td-comp-size-xs: var(--td-size-8); + --td-comp-size-s: var(--td-size-9); + --td-comp-size-m: var(--td-size-10); + --td-comp-size-l: var(--td-size-11); + --td-comp-size-xl: var(--td-size-12); + --td-comp-size-xxl: var(--td-size-13); + --td-comp-size-xxxl: var(--td-size-14); + --td-comp-size-xxxxl: var(--td-size-15); + --td-comp-size-xxxxxl: var(--td-size-16); + --td-pop-padding-s: var(--td-size-2); + --td-pop-padding-m: var(--td-size-3); + --td-pop-padding-l: var(--td-size-4); + --td-pop-padding-xl: var(--td-size-5); + --td-pop-padding-xxl: var(--td-size-6); + --td-comp-paddingLR-xxs: var(--td-size-1); + --td-comp-paddingLR-xs: var(--td-size-2); + --td-comp-paddingLR-s: var(--td-size-4); + --td-comp-paddingLR-m: var(--td-size-5); + --td-comp-paddingLR-l: var(--td-size-6); + --td-comp-paddingLR-xl: var(--td-size-8); + --td-comp-paddingLR-xxl: var(--td-size-10); + --td-comp-paddingTB-xxs: var(--td-size-1); + --td-comp-paddingTB-xs: var(--td-size-2); + --td-comp-paddingTB-s: var(--td-size-4); + --td-comp-paddingTB-m: var(--td-size-5); + --td-comp-paddingTB-l: var(--td-size-6); + --td-comp-paddingTB-xl: var(--td-size-8); + --td-comp-paddingTB-xxl: var(--td-size-10); + --td-comp-margin-xxs: var(--td-size-1); + --td-comp-margin-xs: var(--td-size-2); + --td-comp-margin-s: var(--td-size-4); + --td-comp-margin-m: var(--td-size-5); + --td-comp-margin-l: var(--td-size-6); + --td-comp-margin-xl: var(--td-size-7); + --td-comp-margin-xxl: var(--td-size-8); + --td-comp-margin-xxxl: var(--td-size-10); + --td-comp-margin-xxxxl: var(--td-size-12); + --td-chat-item-content-max-width: 100% !important; +} + +.t-dialog__mask { + backdrop-filter: blur(4px); +} +.t-image-viewer__modal-image { + user-select: none; +} + +.md-editor.md-editor { + // 编辑器基础变量 + --md-color: var(--td-text-color-primary); + --md-hover-color: var(--td-text-color-primary); + --md-bk-color: var(--td-bg-color-container); + --md-bk-color-outstand: var(--td-bg-color-secondarycontainer); + --md-bk-hover-color: var(--td-bg-color-container-hover); + --md-border-color: var(--td-border-level-1-color); + --md-border-hover-color: var(--td-border-level-2-color); + --md-border-active-color: var(--td-brand-color); + --md-modal-mask: var(--td-mask-active); + --md-modal-shadow: var(--td-shadow-2); + --md-scrollbar-bg-color: var(--td-scrollbar-color); + --md-scrollbar-thumb-color: var(--td-scrollbar-color); + --md-scrollbar-thumb-hover-color: var(--td-scrollbar-hover-color); + --md-scrollbar-thumb-active-color: var(--td-scrollbar-hover-color); + + color: var(--md-color); + background-color: var(--md-bk-color); + border: none; + + // 预览区主题变量 + .md-editor-preview.md-editor-preview { + --md-theme-color: var(--td-text-color-primary); + --md-theme-color-reverse: var(--td-bg-color-container-hover); + --md-theme-color-hover: var(--td-bg-color-container-hover); + --md-theme-color-hover-inset: var(--td-bg-color-secondarycontainer-hover); + --md-theme-link-color: var(--td-brand-color-8); + --md-theme-link-hover-color: var(--td-success-color); + --md-theme-border-color: var(--td-border-level-1-color); + --md-theme-border-color-reverse: var(--td-border-level-2-color); + --md-theme-border-color-inset: var(--td-border-level-2-color); + --md-theme-bg-color: var(--td-bg-color-container); + --md-theme-bg-color-inset: var(--td-bg-color-secondarycontainer); + + // 代码块 + --md-theme-code-color: var(--td-text-color-primary); + --md-theme-code-bg-color: var(--td-bg-color-secondarycontainer); + --md-theme-code-block-bg-color: var(--td-bg-color-secondarycontainer); + --md-theme-code-block-color: var(--td-text-color-primary); + --md-theme-code-inline-color: var(--td-error-color); + --md-theme-code-inline-bg-color: var(--td-bg-color-secondarycontainer); + --md-theme-code-copy-tips-color: var(--td-text-color-primary); + --md-theme-code-copy-tips-bg-color: var(--td-bg-color-container); + --md-theme-code-active-color: var(--td-warning-color); + + // 引用块 + --md-theme-quote-color: var(--td-text-color-secondary); + --md-theme-quote-bg-color: var(--td-bg-color-secondarycontainer); + --md-theme-quote-border-color: var(--td-brand-color-5); + --md-theme-blockquote-color: var(--td-text-color-secondary); + --md-theme-blockquote-bg-color: var(--td-bg-color-secondarycontainer); + + // 标题 + --md-theme-heading-color: var(--td-text-color-primary); + --md-theme-heading-border: var(--td-border-level-1-color); + --md-theme-heading-bg-color: transparent; + + // 表格 + --md-theme-table-color: var(--td-text-color-primary); + --md-theme-table-border-color: var(--td-border-level-1-color); + --md-theme-table-td-border-color: var(--td-border-level-1-color); + --md-theme-table-th-color: var(--td-text-color-primary); + --md-theme-table-thead-bg-color: var(--td-bg-color-secondarycontainer); + --md-theme-table-tr-bg-color: var(--td-bg-color-container); + --md-theme-table-stripe-color: var(--td-bg-color-secondarycontainer); + + // 文本样式 + --md-theme-strong-color: var(--td-text-color-primary); + --md-theme-del-color: var(--td-text-color-disabled); + --md-theme-em-color: var(--td-text-color-secondary); + + // 选中态 + --md-theme-slct-text-color: var(--td-font-white-1); + --md-theme-slct-bg-color: var(--td-brand-color-5); + + // 圆角 + --md-theme-radius-s: var(--td-radius-small); + --md-theme-radius-m: var(--td-radius-medium); + } + + // 编辑区 CodeMirror 样式 + .cm-editor.cm-editor { + .cm-gutters.cm-gutters { + background-color: var(--td-bg-color-secondarycontainer); + color: var(--td-text-color-placeholder); + border-right: 1px solid var(--td-border-level-1-color); + } + .cm-activeLineGutter.cm-activeLineGutter { + background-color: var(--td-bg-color-secondarycontainer-hover); + } + .cm-activeLine.cm-activeLine { + background-color: var(--td-bg-color-container-hover); + } + .cm-cursor.cm-cursor { + border-left-color: var(--td-text-color-primary); + } + .cm-selectionBackground.cm-selectionBackground { + background-color: var(--td-brand-color-2) !important; + } + .cm-panels.cm-panels { + background-color: var(--td-bg-color-secondarycontainer); + color: var(--td-text-color-primary); + border-color: var(--td-border-level-1-color); + } + } +} + +.splitpanes__splitter { + background-color: transparent !important; +} +.md-editor-code-head { + z-index: 1 !important; +} + +/* AirFlow Studio visual system */ +:root, +:root[theme-mode="light"], +:root[theme-mode="dark"], +:root.dark { + color-scheme: dark; + --air-bg-app: #050813; + --air-bg-shell: #090b18; + --air-surface-1: rgba(17, 21, 37, 0.92); + --air-surface-2: rgba(26, 29, 49, 0.88); + --air-surface-3: rgba(36, 38, 58, 0.82); + --air-border-soft: rgba(185, 188, 210, 0.16); + --air-border-strong: rgba(101, 217, 203, 0.38); + --air-text-primary: #f1f2ff; + --air-text-secondary: #b9bcd2; + --air-text-muted: #7e849d; + --air-accent-teal: #65d9cb; + --air-accent-violet: #7b6cff; + --air-accent-lavender: #b9a7ff; + --air-accent-blue: #3888ff; + --air-warning: #ffb547; + --air-danger: #ff6363; + --air-success: #2bd671; + --air-shadow-lg: 0 28px 80px rgba(0, 0, 0, 0.42); + --air-shadow-glow: 0 0 36px rgba(101, 217, 203, 0.2); + --air-font-body: "Plus Jakarta Sans", "Avenir Next", "HarmonyOS Sans SC", "MiSans", "PingFang SC", "Microsoft YaHei", sans-serif; + --air-font-display: "Outfit", "Avenir Next", "HarmonyOS Sans SC", "MiSans", "PingFang SC", "Microsoft YaHei", sans-serif; + --air-font-number: "Outfit", "Avenir Next", "DIN Alternate", "PingFang SC", sans-serif; + --air-text-xs: 0.75rem; + --air-text-sm: 0.875rem; + --air-text-md: 1rem; + --air-text-lg: 1.125rem; + --air-text-xl: 1.25rem; + --air-text-2xl: 1.5rem; + --air-text-3xl: 2rem; + --air-text-4xl: 2.5rem; + --air-leading-tight: 1.12; + --air-leading-title: 1.22; + --air-leading-body: 1.62; + --air-space-1: 0.25rem; + --air-space-2: 0.5rem; + --air-space-3: 0.75rem; + --air-space-4: 1rem; + --air-space-5: 1.25rem; + --air-space-6: 1.5rem; + --air-space-8: 2rem; + --air-space-10: 2.5rem; + --air-space-12: 3rem; + --air-space-16: 4rem; + --air-radius-sm: 0.375rem; + --air-radius-md: 0.5rem; + --air-radius-lg: 0.75rem; + --air-radius-xl: 1rem; + --air-ease-out: cubic-bezier(0.25, 1, 0.5, 1); + --air-field-height: 40px; + --air-field-height-sm: 32px; + --air-field-radius: 8px; + --air-field-bg: rgba(255, 255, 255, 0.055); + --air-field-bg-hover: rgba(255, 255, 255, 0.078); + --air-field-bg-disabled: rgba(255, 255, 255, 0.028); + --air-field-border: rgba(185, 188, 210, 0.18); + --air-field-border-hover: rgba(101, 217, 203, 0.38); + --air-field-shadow-focus: 0 0 0 3px rgba(101, 217, 203, 0.14); + + --bgc: var(--air-bg-app); + --page: rgba(9, 11, 24, 0.88); + + --td-brand-color-1: #0c1a24; + --td-brand-color-2: #112d34; + --td-brand-color-3: #18474a; + --td-brand-color-4: #226760; + --td-brand-color-5: #38a99c; + --td-brand-color-6: #65d9cb; + --td-brand-color-7: #7ff2e2; + --td-brand-color-8: #9af8ec; + --td-brand-color-9: #c6fff7; + --td-brand-color-10: #e6fffb; + --td-brand-color: var(--air-accent-teal); + --td-brand-color-hover: #7ff2e2; + --td-brand-color-focus: rgba(101, 217, 203, 0.22); + --td-brand-color-active: #38a99c; + --td-brand-color-disabled: rgba(101, 217, 203, 0.36); + + --td-text-color-primary: var(--air-text-primary); + --td-text-color-secondary: var(--air-text-secondary); + --td-text-color-placeholder: var(--air-text-muted); + --td-text-color-disabled: rgba(185, 188, 210, 0.42); + --td-bg-color-page: var(--air-bg-app); + --td-bg-color-container: rgba(17, 21, 37, 0.92); + --td-bg-color-container-hover: rgba(36, 38, 58, 0.95); + --td-bg-color-container-active: rgba(45, 49, 74, 0.96); + --td-bg-color-secondarycontainer: rgba(26, 29, 49, 0.86); + --td-bg-color-secondarycontainer-hover: rgba(38, 42, 65, 0.92); + --td-bg-color-component: rgba(255, 255, 255, 0.05); + --td-bg-color-component-hover: rgba(101, 217, 203, 0.1); + --td-component-border: var(--air-border-soft); + --td-border-level-1-color: var(--air-border-soft); + --td-border-level-2-color: rgba(185, 188, 210, 0.26); + --td-mask-active: rgba(1, 3, 10, 0.72); + --td-shadow-1: 0 10px 30px rgba(0, 0, 0, 0.28); + --td-shadow-2: 0 18px 48px rgba(0, 0, 0, 0.35); + --td-shadow-3: var(--air-shadow-lg); + --td-scrollbar-color: rgba(185, 188, 210, 0.22); + --td-scrollbar-hover-color: rgba(101, 217, 203, 0.48); + --td-radius-default: 6px; + --td-radius-medium: 8px; + --td-radius-large: 10px; + --td-radius-extraLarge: 12px; + --td-font-family: var(--air-font-body); + --td-font-family-medium: var(--air-font-body); + --td-font-size-body-small: var(--air-text-xs); + --td-font-size-body-medium: var(--air-text-sm); + --td-font-size-body-large: var(--air-text-md); + --td-font-size-title-small: var(--air-text-sm); + --td-font-size-title-medium: var(--air-text-md); + --td-font-size-title-large: var(--air-text-lg); + --td-font-size-headline-small: var(--air-text-2xl); + --td-font-size-headline-medium: var(--air-text-3xl); + --td-font-body-small: 400 var(--air-text-xs) / 1.5 var(--air-font-body); + --td-font-body-medium: 400 var(--air-text-sm) / 1.58 var(--air-font-body); + --td-font-body-large: 400 var(--air-text-md) / var(--air-leading-body) var(--air-font-body); + --td-font-title-small: 600 var(--air-text-sm) / 1.35 var(--air-font-body); + --td-font-title-medium: 600 var(--air-text-md) / 1.35 var(--air-font-body); + --td-font-title-large: 650 var(--air-text-lg) / 1.3 var(--air-font-body); +} + +html, +body, +#app { + min-height: 100%; + background: + radial-gradient(circle at 16% 12%, rgba(101, 217, 203, 0.14), transparent 32%), + radial-gradient(circle at 72% 22%, rgba(56, 136, 255, 0.14), transparent 34%), + radial-gradient(circle at 50% 88%, rgba(123, 108, 255, 0.18), transparent 38%), var(--air-bg-app); +} + +body { + font-family: var(--air-font-body); + font-size: var(--air-text-md); + line-height: var(--air-leading-body); + color: var(--air-text-primary); + font-kerning: normal; + font-feature-settings: + "liga" 1, + "kern" 1; +} + +h1, +h2, +h3, +h4, +.t-dialog__header { + font-family: var(--air-font-display); +} + +*::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +*::-webkit-scrollbar-thumb { + background-color: rgba(185, 188, 210, 0.2); + border: 2px solid transparent; + border-radius: 999px; + background-clip: padding-box; + + &:hover { + background-color: rgba(101, 217, 203, 0.44); + } +} + +.t-form { + color: var(--air-text-secondary); +} + +.t-form__item { + margin-bottom: 18px !important; +} + +.t-form__item:last-child { + margin-bottom: 0 !important; +} + +.t-form__label { + color: var(--air-text-secondary) !important; + font-size: var(--air-text-xs) !important; + font-weight: 700 !important; + line-height: 1.35 !important; +} + +.t-form__label label, +.t-form__label span { + letter-spacing: 0; +} + +.t-form__controls-content { + width: 100%; +} + +.t-form__help, +.t-form__extra { + color: var(--air-text-muted) !important; + font-size: var(--air-text-xs) !important; + line-height: 1.45 !important; +} + +.t-form__status, +.t-form__status-icon { + color: var(--air-danger) !important; +} + +.t-input, +.t-input-number, +.t-tag-input .t-input, +.t-textarea, +.t-textarea__inner, +.t-select .t-input, +.t-select-input, +.t-select__wrap, +.t-date-picker, +.t-date-picker .t-input { + border-color: var(--air-field-border) !important; + border-radius: var(--air-field-radius) !important; + background: var(--air-field-bg) !important; + color: var(--air-text-primary) !important; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04); + transition: + border-color 0.18s ease, + background 0.18s ease, + box-shadow 0.18s ease; +} + +.t-input, +.t-input-number, +.t-select .t-input, +.t-date-picker .t-input { + min-height: var(--air-field-height); +} + +.t-input.t-size-s, +.t-input-number.t-size-s, +.t-select .t-input.t-size-s, +.t-date-picker .t-input.t-size-s { + min-height: var(--air-field-height-sm); +} + +.t-input:hover, +.t-input-number:hover, +.t-tag-input .t-input:hover, +.t-textarea:hover, +.t-textarea__inner:hover, +.t-select .t-input:hover, +.t-select__wrap:hover, +.t-date-picker:hover, +.t-date-picker .t-input:hover { + border-color: var(--air-field-border-hover) !important; + background: var(--air-field-bg-hover) !important; +} + +.t-input:focus-within, +.t-input.t-is-focused, +.t-input-number:focus-within, +.t-input-number.t-is-focused, +.t-textarea:focus-within, +.t-textarea.t-is-focused, +.t-textarea__inner:focus, +.t-select .t-input:focus-within, +.t-select .t-input.t-is-focused, +.t-select__wrap.t-is-focused, +.t-date-picker:focus-within, +.t-date-picker .t-input.t-is-focused { + border-color: var(--air-accent-teal) !important; + box-shadow: var(--air-field-shadow-focus) !important; +} + +.t-input.t-is-disabled, +.t-input-number.t-is-disabled, +.t-textarea.t-is-disabled, +.t-textarea__inner:disabled, +.t-select.t-is-disabled .t-input, +.t-date-picker.t-is-disabled .t-input { + border-color: rgba(185, 188, 210, 0.1) !important; + background: var(--air-field-bg-disabled) !important; + color: var(--td-text-color-disabled) !important; + box-shadow: none !important; +} + +.t-input__inner, +.t-input input, +.t-input-number input, +.t-select-input, +.t-textarea__inner, +.t-textarea textarea { + color: var(--air-text-primary) !important; + font-size: var(--air-text-sm) !important; +} + +.t-input__inner::placeholder, +.t-input input::placeholder, +.t-input-number input::placeholder, +.t-select-input::placeholder, +.t-textarea__inner::placeholder, +.t-textarea textarea::placeholder { + color: rgba(185, 188, 210, 0.5) !important; +} + +.t-textarea__inner { + min-height: 96px; + padding: 10px 12px !important; + line-height: 1.55 !important; +} + +.t-input__prefix, +.t-input__suffix, +.t-input__prefix-icon, +.t-input__suffix-icon, +.t-input-number__decrease, +.t-input-number__increase { + color: var(--air-text-muted) !important; +} + +.t-radio-group, +.t-checkbox-group { + color: var(--air-text-secondary); +} + +.t-radio__label, +.t-checkbox__label { + color: var(--air-text-secondary) !important; +} + +.t-radio-button { + border-color: var(--air-field-border) !important; + background: rgba(255, 255, 255, 0.04) !important; + color: var(--air-text-secondary) !important; +} + +.t-radio-button.t-is-checked { + border-color: rgba(101, 217, 203, 0.45) !important; + background: rgba(101, 217, 203, 0.16) !important; + color: var(--air-text-primary) !important; +} + +.t-upload__dragger { + border-color: var(--air-field-border) !important; + border-radius: var(--air-field-radius) !important; + background: rgba(255, 255, 255, 0.04) !important; + color: var(--air-text-secondary) !important; +} + +.t-upload__dragger:hover { + border-color: var(--air-field-border-hover) !important; + background: rgba(101, 217, 203, 0.08) !important; +} + +.t-button { + border-radius: 10px; + transition: + transform 0.18s ease, + border-color 0.18s ease, + background 0.18s ease, + box-shadow 0.18s ease; +} + +.t-button:not(.t-is-disabled):hover { + transform: translateY(-1px); +} + +.t-button--theme-primary { + color: #061014 !important; + border-color: transparent !important; + background: linear-gradient(135deg, var(--air-accent-teal), var(--air-accent-lavender)) !important; + box-shadow: 0 10px 24px rgba(101, 217, 203, 0.16); +} + +.t-button--theme-primary:not(.t-is-disabled):hover { + box-shadow: 0 14px 30px rgba(101, 217, 203, 0.22); +} + +.t-button--variant-base.t-button--theme-default, +.t-button--variant-outline.t-button--theme-default { + color: var(--air-text-secondary); + border-color: var(--air-border-soft); + background: rgba(255, 255, 255, 0.045); +} + +.t-button--variant-text { + color: var(--air-text-secondary); +} + +.t-button--variant-outline.t-button--theme-primary { + color: var(--air-accent-teal) !important; + border-color: rgba(101, 217, 203, 0.32) !important; + background: rgba(101, 217, 203, 0.075) !important; + box-shadow: none !important; +} + +.t-button--variant-text.t-button--theme-primary, +.t-button--variant-text.t-button--theme-default, +.t-button--variant-text.t-button--theme-danger { + border-color: transparent !important; + background: transparent !important; + box-shadow: none !important; +} + +.t-button--variant-text.t-button--theme-primary { + color: var(--air-accent-teal) !important; +} + +.t-button--variant-text.t-button--theme-danger { + color: var(--air-danger) !important; +} + +.t-button--variant-text:not(.t-is-disabled):hover { + background: rgba(255, 255, 255, 0.055) !important; + box-shadow: none !important; +} + +.t-card, +.t-dialog, +.t-popup, +.t-dropdown__menu, +.t-select__dropdown, +.t-drawer__content-wrapper { + color: var(--air-text-primary); + border-color: var(--air-border-soft) !important; + background: rgba(17, 21, 37, 0.96) !important; + box-shadow: var(--air-shadow-lg) !important; + backdrop-filter: blur(24px); +} + +.t-drawer { + background: transparent !important; + box-shadow: none !important; + backdrop-filter: none !important; +} + +.t-drawer__content-wrapper { + border-left: 1px solid var(--air-border-soft); +} + +.t-card { + border-radius: var(--air-radius-md) !important; +} + +.t-card__body { + color: var(--air-text-secondary); +} + +.t-dialog { + border-radius: 12px !important; +} + +.t-tag { + color: var(--air-text-secondary) !important; + border-color: rgba(185, 188, 210, 0.18) !important; + background: rgba(255, 255, 255, 0.055) !important; +} + +.t-dialog__header { + padding-bottom: 14px !important; + color: var(--air-text-primary) !important; + border-bottom: 1px solid rgba(185, 188, 210, 0.12); +} + +.t-dialog__body { + color: var(--air-text-secondary) !important; +} + +.t-dialog__footer { + border-top: 1px solid rgba(185, 188, 210, 0.12); +} + +.t-alert { + border-color: rgba(255, 181, 71, 0.24) !important; + border-radius: var(--air-field-radius) !important; + background: rgba(255, 181, 71, 0.1) !important; + color: var(--air-text-secondary) !important; +} + +.t-collapse { + border-color: var(--air-border-soft) !important; + border-radius: var(--air-field-radius); + overflow: hidden; +} + +.t-collapse-panel { + background: rgba(255, 255, 255, 0.035) !important; +} + +.t-collapse-panel__header, +.t-collapse-panel__body { + color: var(--air-text-secondary) !important; + border-color: var(--air-border-soft) !important; +} + +.t-menu, +.t-default-menu { + border-color: var(--air-border-soft) !important; + background: transparent !important; + color: var(--air-text-secondary) !important; +} + +.t-default-menu .t-menu__item { + margin-bottom: 4px; + border-radius: var(--air-field-radius); + color: var(--air-text-secondary) !important; +} + +.t-default-menu .t-menu__item:hover:not(.t-is-active):not(.t-is-disabled) { + background: rgba(101, 217, 203, 0.08) !important; + color: var(--air-text-primary) !important; +} + +.t-default-menu .t-menu__item.t-is-active:not(.t-is-opened) { + background: rgba(101, 217, 203, 0.14) !important; + color: var(--air-text-primary) !important; +} + +.t-select__dropdown .t-popup__content, +.t-dropdown__menu { + border: 1px solid var(--air-border-soft) !important; + border-radius: var(--air-field-radius) !important; + background: rgba(11, 14, 28, 0.98) !important; +} + +.t-select-option, +.t-select-option-group__header { + color: var(--air-text-secondary) !important; +} + +.t-select-option:not(.t-is-disabled):not(.t-is-selected):hover, +.t-select-option.t-select-option--hover:not(.t-is-disabled):not(.t-is-selected), +.t-select-option.t-select-option__hover:not(.t-is-disabled):not(.t-is-selected) { + background: rgba(101, 217, 203, 0.1) !important; + color: var(--air-text-primary) !important; +} + +.t-select-option.t-is-selected:not(.t-is-disabled) { + background: rgba(101, 217, 203, 0.16) !important; + color: var(--air-text-primary) !important; +} + +.t-tabs__nav { + color: var(--air-text-secondary); +} + +.t-tabs__nav-item { + color: var(--air-text-secondary) !important; +} + +.t-tabs__nav-item:hover, +.t-tabs__nav-item.t-is-active { + color: var(--air-text-primary) !important; +} + +.t-tabs__bar { + background: var(--air-accent-teal) !important; + box-shadow: 0 0 18px rgba(101, 217, 203, 0.34); +} + +.t-table { + color: var(--air-text-secondary); + border: 1px solid var(--air-border-soft) !important; + border-radius: var(--air-radius-md); + background: rgba(9, 11, 24, 0.58) !important; + overflow: hidden; +} + +.t-table__content, +.t-table table { + background: transparent !important; +} + +.t-table th, +.t-table td { + border-color: rgba(185, 188, 210, 0.1) !important; + color: var(--air-text-secondary) !important; + background: transparent !important; +} + +.t-table th, +.t-table thead th, +.t-table__header th { + background: rgba(255, 255, 255, 0.045) !important; + color: var(--air-text-muted) !important; + font-weight: 650 !important; +} + +.t-table tbody tr { + background: rgba(14, 17, 31, 0.58) !important; + transition: + background 0.16s ease, + color 0.16s ease; +} + +.t-table--striped tbody tr:nth-child(even) { + background: rgba(255, 255, 255, 0.035) !important; +} + +.t-table tbody tr:hover, +.t-table--hoverable tbody tr:hover { + background: rgba(101, 217, 203, 0.075) !important; +} + +.t-table tbody tr:hover td { + color: var(--air-text-primary) !important; +} + +.t-table .t-table__cell--fixed-left, +.t-table .t-table__cell--fixed-right { + background: inherit !important; +} + +.t-table .t-table__cell--fixed-right { + border-left: 1px solid rgba(185, 188, 210, 0.12) !important; +} + +.t-table .t-table__cell--fixed-left { + border-right: 1px solid rgba(185, 188, 210, 0.08) !important; +} + +.t-table__pagination { + border-top: 1px solid rgba(185, 188, 210, 0.12) !important; + background: rgba(9, 11, 24, 0.72) !important; + color: var(--air-text-secondary) !important; +} + +.t-pagination { + color: var(--air-text-secondary) !important; +} + +.t-pagination__btn, +.t-pagination__number, +.t-pagination__jump { + border-color: rgba(185, 188, 210, 0.16) !important; + background: rgba(255, 255, 255, 0.045) !important; + color: var(--air-text-secondary) !important; +} + +.t-pagination__number.t-is-current { + border-color: rgba(101, 217, 203, 0.5) !important; + background: rgba(101, 217, 203, 0.92) !important; + color: #061014 !important; +} + +.t-message, +.t-notification { + border-color: var(--air-border-soft) !important; + background: rgba(17, 21, 37, 0.96) !important; + color: var(--air-text-primary) !important; + backdrop-filter: blur(18px); +} + +.t-tooltip__content { + border: 1px solid var(--air-border-soft); + background: rgba(9, 11, 24, 0.96) !important; + color: var(--air-text-primary) !important; +} + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + scroll-behavior: auto !important; + transition-duration: 0.01ms !important; + } +} diff --git a/web-core/src/assets/providers/ace.webp b/web-core/src/assets/providers/ace.webp new file mode 100644 index 0000000..5bd98ca Binary files /dev/null and b/web-core/src/assets/providers/ace.webp differ diff --git a/web-core/src/assets/providers/adobe.webp b/web-core/src/assets/providers/adobe.webp new file mode 100644 index 0000000..3cdba0d Binary files /dev/null and b/web-core/src/assets/providers/adobe.webp differ diff --git a/web-core/src/assets/providers/adobefirefly.webp b/web-core/src/assets/providers/adobefirefly.webp new file mode 100644 index 0000000..f4fbe01 Binary files /dev/null and b/web-core/src/assets/providers/adobefirefly.webp differ diff --git a/web-core/src/assets/providers/agentvoice.webp b/web-core/src/assets/providers/agentvoice.webp new file mode 100644 index 0000000..d89e44a Binary files /dev/null and b/web-core/src/assets/providers/agentvoice.webp differ diff --git a/web-core/src/assets/providers/agui.webp b/web-core/src/assets/providers/agui.webp new file mode 100644 index 0000000..0e4a5ae Binary files /dev/null and b/web-core/src/assets/providers/agui.webp differ diff --git a/web-core/src/assets/providers/ai2.webp b/web-core/src/assets/providers/ai2.webp new file mode 100644 index 0000000..56d0195 Binary files /dev/null and b/web-core/src/assets/providers/ai2.webp differ diff --git a/web-core/src/assets/providers/ai21.webp b/web-core/src/assets/providers/ai21.webp new file mode 100644 index 0000000..27e673c Binary files /dev/null and b/web-core/src/assets/providers/ai21.webp differ diff --git a/web-core/src/assets/providers/ai302.webp b/web-core/src/assets/providers/ai302.webp new file mode 100644 index 0000000..d9d14b6 Binary files /dev/null and b/web-core/src/assets/providers/ai302.webp differ diff --git a/web-core/src/assets/providers/ai360.webp b/web-core/src/assets/providers/ai360.webp new file mode 100644 index 0000000..a7dc337 Binary files /dev/null and b/web-core/src/assets/providers/ai360.webp differ diff --git a/web-core/src/assets/providers/aihubmix.webp b/web-core/src/assets/providers/aihubmix.webp new file mode 100644 index 0000000..8ee64d0 Binary files /dev/null and b/web-core/src/assets/providers/aihubmix.webp differ diff --git a/web-core/src/assets/providers/aimass.webp b/web-core/src/assets/providers/aimass.webp new file mode 100644 index 0000000..41ee310 Binary files /dev/null and b/web-core/src/assets/providers/aimass.webp differ diff --git a/web-core/src/assets/providers/aionlabs.webp b/web-core/src/assets/providers/aionlabs.webp new file mode 100644 index 0000000..5f6a3b2 Binary files /dev/null and b/web-core/src/assets/providers/aionlabs.webp differ diff --git a/web-core/src/assets/providers/aistudio.webp b/web-core/src/assets/providers/aistudio.webp new file mode 100644 index 0000000..39c61a7 Binary files /dev/null and b/web-core/src/assets/providers/aistudio.webp differ diff --git a/web-core/src/assets/providers/akashchat.webp b/web-core/src/assets/providers/akashchat.webp new file mode 100644 index 0000000..5315c91 Binary files /dev/null and b/web-core/src/assets/providers/akashchat.webp differ diff --git a/web-core/src/assets/providers/alephalpha.webp b/web-core/src/assets/providers/alephalpha.webp new file mode 100644 index 0000000..8029cea Binary files /dev/null and b/web-core/src/assets/providers/alephalpha.webp differ diff --git a/web-core/src/assets/providers/alibaba.webp b/web-core/src/assets/providers/alibaba.webp new file mode 100644 index 0000000..b7a1b9d Binary files /dev/null and b/web-core/src/assets/providers/alibaba.webp differ diff --git a/web-core/src/assets/providers/alibabacloud.webp b/web-core/src/assets/providers/alibabacloud.webp new file mode 100644 index 0000000..ee31596 Binary files /dev/null and b/web-core/src/assets/providers/alibabacloud.webp differ diff --git a/web-core/src/assets/providers/amp.webp b/web-core/src/assets/providers/amp.webp new file mode 100644 index 0000000..8b6e8cd Binary files /dev/null and b/web-core/src/assets/providers/amp.webp differ diff --git a/web-core/src/assets/providers/antgroup.webp b/web-core/src/assets/providers/antgroup.webp new file mode 100644 index 0000000..b28892b Binary files /dev/null and b/web-core/src/assets/providers/antgroup.webp differ diff --git a/web-core/src/assets/providers/anthropic.webp b/web-core/src/assets/providers/anthropic.webp new file mode 100644 index 0000000..dc45efd Binary files /dev/null and b/web-core/src/assets/providers/anthropic.webp differ diff --git a/web-core/src/assets/providers/antigravity.webp b/web-core/src/assets/providers/antigravity.webp new file mode 100644 index 0000000..69ec95f Binary files /dev/null and b/web-core/src/assets/providers/antigravity.webp differ diff --git a/web-core/src/assets/providers/anyscale.webp b/web-core/src/assets/providers/anyscale.webp new file mode 100644 index 0000000..f3004b6 Binary files /dev/null and b/web-core/src/assets/providers/anyscale.webp differ diff --git a/web-core/src/assets/providers/apertis.webp b/web-core/src/assets/providers/apertis.webp new file mode 100644 index 0000000..888ff6e Binary files /dev/null and b/web-core/src/assets/providers/apertis.webp differ diff --git a/web-core/src/assets/providers/apple.webp b/web-core/src/assets/providers/apple.webp new file mode 100644 index 0000000..19d657a Binary files /dev/null and b/web-core/src/assets/providers/apple.webp differ diff --git a/web-core/src/assets/providers/arcee.webp b/web-core/src/assets/providers/arcee.webp new file mode 100644 index 0000000..53412b4 Binary files /dev/null and b/web-core/src/assets/providers/arcee.webp differ diff --git a/web-core/src/assets/providers/askverdict.webp b/web-core/src/assets/providers/askverdict.webp new file mode 100644 index 0000000..34109c5 Binary files /dev/null and b/web-core/src/assets/providers/askverdict.webp differ diff --git a/web-core/src/assets/providers/assemblyai.webp b/web-core/src/assets/providers/assemblyai.webp new file mode 100644 index 0000000..548af24 Binary files /dev/null and b/web-core/src/assets/providers/assemblyai.webp differ diff --git a/web-core/src/assets/providers/atlascloud.webp b/web-core/src/assets/providers/atlascloud.webp new file mode 100644 index 0000000..dcab2a7 Binary files /dev/null and b/web-core/src/assets/providers/atlascloud.webp differ diff --git a/web-core/src/assets/providers/automatic.webp b/web-core/src/assets/providers/automatic.webp new file mode 100644 index 0000000..c8a3570 Binary files /dev/null and b/web-core/src/assets/providers/automatic.webp differ diff --git a/web-core/src/assets/providers/aws.webp b/web-core/src/assets/providers/aws.webp new file mode 100644 index 0000000..9634600 Binary files /dev/null and b/web-core/src/assets/providers/aws.webp differ diff --git a/web-core/src/assets/providers/aya.webp b/web-core/src/assets/providers/aya.webp new file mode 100644 index 0000000..e8e8b04 Binary files /dev/null and b/web-core/src/assets/providers/aya.webp differ diff --git a/web-core/src/assets/providers/azure.webp b/web-core/src/assets/providers/azure.webp new file mode 100644 index 0000000..e33e67d Binary files /dev/null and b/web-core/src/assets/providers/azure.webp differ diff --git a/web-core/src/assets/providers/azureai.webp b/web-core/src/assets/providers/azureai.webp new file mode 100644 index 0000000..fc13f48 Binary files /dev/null and b/web-core/src/assets/providers/azureai.webp differ diff --git a/web-core/src/assets/providers/baai.webp b/web-core/src/assets/providers/baai.webp new file mode 100644 index 0000000..be4c61c Binary files /dev/null and b/web-core/src/assets/providers/baai.webp differ diff --git a/web-core/src/assets/providers/baichuan.webp b/web-core/src/assets/providers/baichuan.webp new file mode 100644 index 0000000..491518b Binary files /dev/null and b/web-core/src/assets/providers/baichuan.webp differ diff --git a/web-core/src/assets/providers/baidu.webp b/web-core/src/assets/providers/baidu.webp new file mode 100644 index 0000000..f1f16f2 Binary files /dev/null and b/web-core/src/assets/providers/baidu.webp differ diff --git a/web-core/src/assets/providers/baiducloud.webp b/web-core/src/assets/providers/baiducloud.webp new file mode 100644 index 0000000..e019800 Binary files /dev/null and b/web-core/src/assets/providers/baiducloud.webp differ diff --git a/web-core/src/assets/providers/bailian.webp b/web-core/src/assets/providers/bailian.webp new file mode 100644 index 0000000..e9d6f68 Binary files /dev/null and b/web-core/src/assets/providers/bailian.webp differ diff --git a/web-core/src/assets/providers/baseten.webp b/web-core/src/assets/providers/baseten.webp new file mode 100644 index 0000000..e702a74 Binary files /dev/null and b/web-core/src/assets/providers/baseten.webp differ diff --git a/web-core/src/assets/providers/bedrock.webp b/web-core/src/assets/providers/bedrock.webp new file mode 100644 index 0000000..3ec360e Binary files /dev/null and b/web-core/src/assets/providers/bedrock.webp differ diff --git a/web-core/src/assets/providers/bfl.webp b/web-core/src/assets/providers/bfl.webp new file mode 100644 index 0000000..11e5dcf Binary files /dev/null and b/web-core/src/assets/providers/bfl.webp differ diff --git a/web-core/src/assets/providers/bilibili.webp b/web-core/src/assets/providers/bilibili.webp new file mode 100644 index 0000000..582f092 Binary files /dev/null and b/web-core/src/assets/providers/bilibili.webp differ diff --git a/web-core/src/assets/providers/bilibiliindex.webp b/web-core/src/assets/providers/bilibiliindex.webp new file mode 100644 index 0000000..fc25912 Binary files /dev/null and b/web-core/src/assets/providers/bilibiliindex.webp differ diff --git a/web-core/src/assets/providers/bing.webp b/web-core/src/assets/providers/bing.webp new file mode 100644 index 0000000..9493dc1 Binary files /dev/null and b/web-core/src/assets/providers/bing.webp differ diff --git a/web-core/src/assets/providers/briaai.webp b/web-core/src/assets/providers/briaai.webp new file mode 100644 index 0000000..f7bb431 Binary files /dev/null and b/web-core/src/assets/providers/briaai.webp differ diff --git a/web-core/src/assets/providers/burncloud.webp b/web-core/src/assets/providers/burncloud.webp new file mode 100644 index 0000000..937fb5f Binary files /dev/null and b/web-core/src/assets/providers/burncloud.webp differ diff --git a/web-core/src/assets/providers/bytedance.webp b/web-core/src/assets/providers/bytedance.webp new file mode 100644 index 0000000..0fe4c08 Binary files /dev/null and b/web-core/src/assets/providers/bytedance.webp differ diff --git a/web-core/src/assets/providers/capcut.webp b/web-core/src/assets/providers/capcut.webp new file mode 100644 index 0000000..cbe7fca Binary files /dev/null and b/web-core/src/assets/providers/capcut.webp differ diff --git a/web-core/src/assets/providers/centml.webp b/web-core/src/assets/providers/centml.webp new file mode 100644 index 0000000..e5241d7 Binary files /dev/null and b/web-core/src/assets/providers/centml.webp differ diff --git a/web-core/src/assets/providers/cerebras.webp b/web-core/src/assets/providers/cerebras.webp new file mode 100644 index 0000000..27ce007 Binary files /dev/null and b/web-core/src/assets/providers/cerebras.webp differ diff --git a/web-core/src/assets/providers/chatglm.webp b/web-core/src/assets/providers/chatglm.webp new file mode 100644 index 0000000..a8e6e14 Binary files /dev/null and b/web-core/src/assets/providers/chatglm.webp differ diff --git a/web-core/src/assets/providers/cherrystudio.webp b/web-core/src/assets/providers/cherrystudio.webp new file mode 100644 index 0000000..8a5b905 Binary files /dev/null and b/web-core/src/assets/providers/cherrystudio.webp differ diff --git a/web-core/src/assets/providers/civitai.webp b/web-core/src/assets/providers/civitai.webp new file mode 100644 index 0000000..16806f9 Binary files /dev/null and b/web-core/src/assets/providers/civitai.webp differ diff --git a/web-core/src/assets/providers/claude.webp b/web-core/src/assets/providers/claude.webp new file mode 100644 index 0000000..7a39612 Binary files /dev/null and b/web-core/src/assets/providers/claude.webp differ diff --git a/web-core/src/assets/providers/claudecode.webp b/web-core/src/assets/providers/claudecode.webp new file mode 100644 index 0000000..71b239c Binary files /dev/null and b/web-core/src/assets/providers/claudecode.webp differ diff --git a/web-core/src/assets/providers/cline.webp b/web-core/src/assets/providers/cline.webp new file mode 100644 index 0000000..382a1ed Binary files /dev/null and b/web-core/src/assets/providers/cline.webp differ diff --git a/web-core/src/assets/providers/clipdrop.webp b/web-core/src/assets/providers/clipdrop.webp new file mode 100644 index 0000000..09fcb3d Binary files /dev/null and b/web-core/src/assets/providers/clipdrop.webp differ diff --git a/web-core/src/assets/providers/cloudflare.webp b/web-core/src/assets/providers/cloudflare.webp new file mode 100644 index 0000000..14121f7 Binary files /dev/null and b/web-core/src/assets/providers/cloudflare.webp differ diff --git a/web-core/src/assets/providers/codeflicker.webp b/web-core/src/assets/providers/codeflicker.webp new file mode 100644 index 0000000..9984050 Binary files /dev/null and b/web-core/src/assets/providers/codeflicker.webp differ diff --git a/web-core/src/assets/providers/codegeex.webp b/web-core/src/assets/providers/codegeex.webp new file mode 100644 index 0000000..3c8b38c Binary files /dev/null and b/web-core/src/assets/providers/codegeex.webp differ diff --git a/web-core/src/assets/providers/codex.webp b/web-core/src/assets/providers/codex.webp new file mode 100644 index 0000000..4b4f98d Binary files /dev/null and b/web-core/src/assets/providers/codex.webp differ diff --git a/web-core/src/assets/providers/cogvideo.webp b/web-core/src/assets/providers/cogvideo.webp new file mode 100644 index 0000000..6bdc708 Binary files /dev/null and b/web-core/src/assets/providers/cogvideo.webp differ diff --git a/web-core/src/assets/providers/cogview.webp b/web-core/src/assets/providers/cogview.webp new file mode 100644 index 0000000..5e708f2 Binary files /dev/null and b/web-core/src/assets/providers/cogview.webp differ diff --git a/web-core/src/assets/providers/cohere.webp b/web-core/src/assets/providers/cohere.webp new file mode 100644 index 0000000..ad3dd30 Binary files /dev/null and b/web-core/src/assets/providers/cohere.webp differ diff --git a/web-core/src/assets/providers/colab.webp b/web-core/src/assets/providers/colab.webp new file mode 100644 index 0000000..b8d5ff4 Binary files /dev/null and b/web-core/src/assets/providers/colab.webp differ diff --git a/web-core/src/assets/providers/cometapi.webp b/web-core/src/assets/providers/cometapi.webp new file mode 100644 index 0000000..923852e Binary files /dev/null and b/web-core/src/assets/providers/cometapi.webp differ diff --git a/web-core/src/assets/providers/comfyui.webp b/web-core/src/assets/providers/comfyui.webp new file mode 100644 index 0000000..2a2c92a Binary files /dev/null and b/web-core/src/assets/providers/comfyui.webp differ diff --git a/web-core/src/assets/providers/commanda.webp b/web-core/src/assets/providers/commanda.webp new file mode 100644 index 0000000..ad3dd30 Binary files /dev/null and b/web-core/src/assets/providers/commanda.webp differ diff --git a/web-core/src/assets/providers/copilot.webp b/web-core/src/assets/providers/copilot.webp new file mode 100644 index 0000000..4dac146 Binary files /dev/null and b/web-core/src/assets/providers/copilot.webp differ diff --git a/web-core/src/assets/providers/copilotkit.webp b/web-core/src/assets/providers/copilotkit.webp new file mode 100644 index 0000000..451ff5b Binary files /dev/null and b/web-core/src/assets/providers/copilotkit.webp differ diff --git a/web-core/src/assets/providers/coqui.webp b/web-core/src/assets/providers/coqui.webp new file mode 100644 index 0000000..3abd148 Binary files /dev/null and b/web-core/src/assets/providers/coqui.webp differ diff --git a/web-core/src/assets/providers/coze.webp b/web-core/src/assets/providers/coze.webp new file mode 100644 index 0000000..cf9595b Binary files /dev/null and b/web-core/src/assets/providers/coze.webp differ diff --git a/web-core/src/assets/providers/crewai.webp b/web-core/src/assets/providers/crewai.webp new file mode 100644 index 0000000..062934b Binary files /dev/null and b/web-core/src/assets/providers/crewai.webp differ diff --git a/web-core/src/assets/providers/crusoe.webp b/web-core/src/assets/providers/crusoe.webp new file mode 100644 index 0000000..1159b64 Binary files /dev/null and b/web-core/src/assets/providers/crusoe.webp differ diff --git a/web-core/src/assets/providers/cursor.webp b/web-core/src/assets/providers/cursor.webp new file mode 100644 index 0000000..6cba45c Binary files /dev/null and b/web-core/src/assets/providers/cursor.webp differ diff --git a/web-core/src/assets/providers/cybercut.webp b/web-core/src/assets/providers/cybercut.webp new file mode 100644 index 0000000..fcddef4 Binary files /dev/null and b/web-core/src/assets/providers/cybercut.webp differ diff --git a/web-core/src/assets/providers/dalle.webp b/web-core/src/assets/providers/dalle.webp new file mode 100644 index 0000000..0431ee9 Binary files /dev/null and b/web-core/src/assets/providers/dalle.webp differ diff --git a/web-core/src/assets/providers/dbrx.webp b/web-core/src/assets/providers/dbrx.webp new file mode 100644 index 0000000..a0fcea0 Binary files /dev/null and b/web-core/src/assets/providers/dbrx.webp differ diff --git a/web-core/src/assets/providers/deepai.webp b/web-core/src/assets/providers/deepai.webp new file mode 100644 index 0000000..fb181cb Binary files /dev/null and b/web-core/src/assets/providers/deepai.webp differ diff --git a/web-core/src/assets/providers/deepcogito.webp b/web-core/src/assets/providers/deepcogito.webp new file mode 100644 index 0000000..24c6337 Binary files /dev/null and b/web-core/src/assets/providers/deepcogito.webp differ diff --git a/web-core/src/assets/providers/deepinfra.webp b/web-core/src/assets/providers/deepinfra.webp new file mode 100644 index 0000000..5bd91f7 Binary files /dev/null and b/web-core/src/assets/providers/deepinfra.webp differ diff --git a/web-core/src/assets/providers/deepl.webp b/web-core/src/assets/providers/deepl.webp new file mode 100644 index 0000000..6c051d4 Binary files /dev/null and b/web-core/src/assets/providers/deepl.webp differ diff --git a/web-core/src/assets/providers/deepmind.webp b/web-core/src/assets/providers/deepmind.webp new file mode 100644 index 0000000..532bff5 Binary files /dev/null and b/web-core/src/assets/providers/deepmind.webp differ diff --git a/web-core/src/assets/providers/deepseek.webp b/web-core/src/assets/providers/deepseek.webp new file mode 100644 index 0000000..4994b65 Binary files /dev/null and b/web-core/src/assets/providers/deepseek.webp differ diff --git a/web-core/src/assets/providers/dify.webp b/web-core/src/assets/providers/dify.webp new file mode 100644 index 0000000..ddc814e Binary files /dev/null and b/web-core/src/assets/providers/dify.webp differ diff --git a/web-core/src/assets/providers/doc2x.webp b/web-core/src/assets/providers/doc2x.webp new file mode 100644 index 0000000..0d9b4cf Binary files /dev/null and b/web-core/src/assets/providers/doc2x.webp differ diff --git a/web-core/src/assets/providers/docsearch.webp b/web-core/src/assets/providers/docsearch.webp new file mode 100644 index 0000000..8d59683 Binary files /dev/null and b/web-core/src/assets/providers/docsearch.webp differ diff --git a/web-core/src/assets/providers/dolphin.webp b/web-core/src/assets/providers/dolphin.webp new file mode 100644 index 0000000..c47ea76 Binary files /dev/null and b/web-core/src/assets/providers/dolphin.webp differ diff --git a/web-core/src/assets/providers/doubao.webp b/web-core/src/assets/providers/doubao.webp new file mode 100644 index 0000000..d139ff1 Binary files /dev/null and b/web-core/src/assets/providers/doubao.webp differ diff --git a/web-core/src/assets/providers/dreammachine.webp b/web-core/src/assets/providers/dreammachine.webp new file mode 100644 index 0000000..51c482c Binary files /dev/null and b/web-core/src/assets/providers/dreammachine.webp differ diff --git a/web-core/src/assets/providers/elevenlabs.webp b/web-core/src/assets/providers/elevenlabs.webp new file mode 100644 index 0000000..dfcf47b Binary files /dev/null and b/web-core/src/assets/providers/elevenlabs.webp differ diff --git a/web-core/src/assets/providers/elevenx.webp b/web-core/src/assets/providers/elevenx.webp new file mode 100644 index 0000000..da062ee Binary files /dev/null and b/web-core/src/assets/providers/elevenx.webp differ diff --git a/web-core/src/assets/providers/essentialai.webp b/web-core/src/assets/providers/essentialai.webp new file mode 100644 index 0000000..d172a16 Binary files /dev/null and b/web-core/src/assets/providers/essentialai.webp differ diff --git a/web-core/src/assets/providers/exa.webp b/web-core/src/assets/providers/exa.webp new file mode 100644 index 0000000..d63ff96 Binary files /dev/null and b/web-core/src/assets/providers/exa.webp differ diff --git a/web-core/src/assets/providers/fal.webp b/web-core/src/assets/providers/fal.webp new file mode 100644 index 0000000..ab8e46f Binary files /dev/null and b/web-core/src/assets/providers/fal.webp differ diff --git a/web-core/src/assets/providers/fastgpt.webp b/web-core/src/assets/providers/fastgpt.webp new file mode 100644 index 0000000..54dee4f Binary files /dev/null and b/web-core/src/assets/providers/fastgpt.webp differ diff --git a/web-core/src/assets/providers/featherless.webp b/web-core/src/assets/providers/featherless.webp new file mode 100644 index 0000000..5c7ddc9 Binary files /dev/null and b/web-core/src/assets/providers/featherless.webp differ diff --git a/web-core/src/assets/providers/figma.webp b/web-core/src/assets/providers/figma.webp new file mode 100644 index 0000000..973c9ea Binary files /dev/null and b/web-core/src/assets/providers/figma.webp differ diff --git a/web-core/src/assets/providers/fireworks.webp b/web-core/src/assets/providers/fireworks.webp new file mode 100644 index 0000000..f2e3846 Binary files /dev/null and b/web-core/src/assets/providers/fireworks.webp differ diff --git a/web-core/src/assets/providers/fishaudio.webp b/web-core/src/assets/providers/fishaudio.webp new file mode 100644 index 0000000..f57d156 Binary files /dev/null and b/web-core/src/assets/providers/fishaudio.webp differ diff --git a/web-core/src/assets/providers/flora.webp b/web-core/src/assets/providers/flora.webp new file mode 100644 index 0000000..3cbdcbd Binary files /dev/null and b/web-core/src/assets/providers/flora.webp differ diff --git a/web-core/src/assets/providers/flowith.webp b/web-core/src/assets/providers/flowith.webp new file mode 100644 index 0000000..fedddf5 Binary files /dev/null and b/web-core/src/assets/providers/flowith.webp differ diff --git a/web-core/src/assets/providers/flux.webp b/web-core/src/assets/providers/flux.webp new file mode 100644 index 0000000..d122dde Binary files /dev/null and b/web-core/src/assets/providers/flux.webp differ diff --git a/web-core/src/assets/providers/friendli.webp b/web-core/src/assets/providers/friendli.webp new file mode 100644 index 0000000..c4b69dc Binary files /dev/null and b/web-core/src/assets/providers/friendli.webp differ diff --git a/web-core/src/assets/providers/gemini.webp b/web-core/src/assets/providers/gemini.webp new file mode 100644 index 0000000..bc7bd6e Binary files /dev/null and b/web-core/src/assets/providers/gemini.webp differ diff --git a/web-core/src/assets/providers/geminicli.webp b/web-core/src/assets/providers/geminicli.webp new file mode 100644 index 0000000..60a9fe0 Binary files /dev/null and b/web-core/src/assets/providers/geminicli.webp differ diff --git a/web-core/src/assets/providers/gemma.webp b/web-core/src/assets/providers/gemma.webp new file mode 100644 index 0000000..24d1992 Binary files /dev/null and b/web-core/src/assets/providers/gemma.webp differ diff --git a/web-core/src/assets/providers/giteeai.webp b/web-core/src/assets/providers/giteeai.webp new file mode 100644 index 0000000..b3b8dcc Binary files /dev/null and b/web-core/src/assets/providers/giteeai.webp differ diff --git a/web-core/src/assets/providers/github.webp b/web-core/src/assets/providers/github.webp new file mode 100644 index 0000000..a31cffc Binary files /dev/null and b/web-core/src/assets/providers/github.webp differ diff --git a/web-core/src/assets/providers/githubcopilot.webp b/web-core/src/assets/providers/githubcopilot.webp new file mode 100644 index 0000000..3142ad1 Binary files /dev/null and b/web-core/src/assets/providers/githubcopilot.webp differ diff --git a/web-core/src/assets/providers/glama.webp b/web-core/src/assets/providers/glama.webp new file mode 100644 index 0000000..407687a Binary files /dev/null and b/web-core/src/assets/providers/glama.webp differ diff --git a/web-core/src/assets/providers/glif.webp b/web-core/src/assets/providers/glif.webp new file mode 100644 index 0000000..a5ee523 Binary files /dev/null and b/web-core/src/assets/providers/glif.webp differ diff --git a/web-core/src/assets/providers/glmv.webp b/web-core/src/assets/providers/glmv.webp new file mode 100644 index 0000000..a2dc6d3 Binary files /dev/null and b/web-core/src/assets/providers/glmv.webp differ diff --git a/web-core/src/assets/providers/google.webp b/web-core/src/assets/providers/google.webp new file mode 100644 index 0000000..248075b Binary files /dev/null and b/web-core/src/assets/providers/google.webp differ diff --git a/web-core/src/assets/providers/googlecloud.webp b/web-core/src/assets/providers/googlecloud.webp new file mode 100644 index 0000000..6a1b134 Binary files /dev/null and b/web-core/src/assets/providers/googlecloud.webp differ diff --git a/web-core/src/assets/providers/goose.webp b/web-core/src/assets/providers/goose.webp new file mode 100644 index 0000000..b4638a3 Binary files /dev/null and b/web-core/src/assets/providers/goose.webp differ diff --git a/web-core/src/assets/providers/gradio.webp b/web-core/src/assets/providers/gradio.webp new file mode 100644 index 0000000..118c45c Binary files /dev/null and b/web-core/src/assets/providers/gradio.webp differ diff --git a/web-core/src/assets/providers/greptile.webp b/web-core/src/assets/providers/greptile.webp new file mode 100644 index 0000000..a3bd8da Binary files /dev/null and b/web-core/src/assets/providers/greptile.webp differ diff --git a/web-core/src/assets/providers/grok.webp b/web-core/src/assets/providers/grok.webp new file mode 100644 index 0000000..064f0c0 Binary files /dev/null and b/web-core/src/assets/providers/grok.webp differ diff --git a/web-core/src/assets/providers/groq.webp b/web-core/src/assets/providers/groq.webp new file mode 100644 index 0000000..fb0e339 Binary files /dev/null and b/web-core/src/assets/providers/groq.webp differ diff --git a/web-core/src/assets/providers/grsai.png b/web-core/src/assets/providers/grsai.png new file mode 100644 index 0000000..f698cec Binary files /dev/null and b/web-core/src/assets/providers/grsai.png differ diff --git a/web-core/src/assets/providers/hailuo.webp b/web-core/src/assets/providers/hailuo.webp new file mode 100644 index 0000000..d3f57de Binary files /dev/null and b/web-core/src/assets/providers/hailuo.webp differ diff --git a/web-core/src/assets/providers/haiper.webp b/web-core/src/assets/providers/haiper.webp new file mode 100644 index 0000000..3a8d8e4 Binary files /dev/null and b/web-core/src/assets/providers/haiper.webp differ diff --git a/web-core/src/assets/providers/hedra.webp b/web-core/src/assets/providers/hedra.webp new file mode 100644 index 0000000..51598e4 Binary files /dev/null and b/web-core/src/assets/providers/hedra.webp differ diff --git a/web-core/src/assets/providers/higress.webp b/web-core/src/assets/providers/higress.webp new file mode 100644 index 0000000..915d6e4 Binary files /dev/null and b/web-core/src/assets/providers/higress.webp differ diff --git a/web-core/src/assets/providers/huawei.webp b/web-core/src/assets/providers/huawei.webp new file mode 100644 index 0000000..4d18538 Binary files /dev/null and b/web-core/src/assets/providers/huawei.webp differ diff --git a/web-core/src/assets/providers/huaweicloud.webp b/web-core/src/assets/providers/huaweicloud.webp new file mode 100644 index 0000000..4d18538 Binary files /dev/null and b/web-core/src/assets/providers/huaweicloud.webp differ diff --git a/web-core/src/assets/providers/huggingface.webp b/web-core/src/assets/providers/huggingface.webp new file mode 100644 index 0000000..e779196 Binary files /dev/null and b/web-core/src/assets/providers/huggingface.webp differ diff --git a/web-core/src/assets/providers/hunyuan.webp b/web-core/src/assets/providers/hunyuan.webp new file mode 100644 index 0000000..42f56f3 Binary files /dev/null and b/web-core/src/assets/providers/hunyuan.webp differ diff --git a/web-core/src/assets/providers/hyperbolic.webp b/web-core/src/assets/providers/hyperbolic.webp new file mode 100644 index 0000000..4205a36 Binary files /dev/null and b/web-core/src/assets/providers/hyperbolic.webp differ diff --git a/web-core/src/assets/providers/ibm.webp b/web-core/src/assets/providers/ibm.webp new file mode 100644 index 0000000..8124d63 Binary files /dev/null and b/web-core/src/assets/providers/ibm.webp differ diff --git a/web-core/src/assets/providers/ideogram.webp b/web-core/src/assets/providers/ideogram.webp new file mode 100644 index 0000000..d34bb84 Binary files /dev/null and b/web-core/src/assets/providers/ideogram.webp differ diff --git a/web-core/src/assets/providers/iflytekcloud.webp b/web-core/src/assets/providers/iflytekcloud.webp new file mode 100644 index 0000000..59070fa Binary files /dev/null and b/web-core/src/assets/providers/iflytekcloud.webp differ diff --git a/web-core/src/assets/providers/inception.webp b/web-core/src/assets/providers/inception.webp new file mode 100644 index 0000000..18c31e3 Binary files /dev/null and b/web-core/src/assets/providers/inception.webp differ diff --git a/web-core/src/assets/providers/inference.webp b/web-core/src/assets/providers/inference.webp new file mode 100644 index 0000000..6182df0 Binary files /dev/null and b/web-core/src/assets/providers/inference.webp differ diff --git a/web-core/src/assets/providers/infermatic.webp b/web-core/src/assets/providers/infermatic.webp new file mode 100644 index 0000000..74a1a6a Binary files /dev/null and b/web-core/src/assets/providers/infermatic.webp differ diff --git a/web-core/src/assets/providers/infinigence.webp b/web-core/src/assets/providers/infinigence.webp new file mode 100644 index 0000000..7837072 Binary files /dev/null and b/web-core/src/assets/providers/infinigence.webp differ diff --git a/web-core/src/assets/providers/inflection.webp b/web-core/src/assets/providers/inflection.webp new file mode 100644 index 0000000..4d88269 Binary files /dev/null and b/web-core/src/assets/providers/inflection.webp differ diff --git a/web-core/src/assets/providers/internlm.webp b/web-core/src/assets/providers/internlm.webp new file mode 100644 index 0000000..798b441 Binary files /dev/null and b/web-core/src/assets/providers/internlm.webp differ diff --git a/web-core/src/assets/providers/jimeng.webp b/web-core/src/assets/providers/jimeng.webp new file mode 100644 index 0000000..8189bad Binary files /dev/null and b/web-core/src/assets/providers/jimeng.webp differ diff --git a/web-core/src/assets/providers/jina.webp b/web-core/src/assets/providers/jina.webp new file mode 100644 index 0000000..462dff0 Binary files /dev/null and b/web-core/src/assets/providers/jina.webp differ diff --git a/web-core/src/assets/providers/junie.webp b/web-core/src/assets/providers/junie.webp new file mode 100644 index 0000000..250d269 Binary files /dev/null and b/web-core/src/assets/providers/junie.webp differ diff --git a/web-core/src/assets/providers/kilocode.webp b/web-core/src/assets/providers/kilocode.webp new file mode 100644 index 0000000..d485a4c Binary files /dev/null and b/web-core/src/assets/providers/kilocode.webp differ diff --git a/web-core/src/assets/providers/kimi.webp b/web-core/src/assets/providers/kimi.webp new file mode 100644 index 0000000..1c7a32d Binary files /dev/null and b/web-core/src/assets/providers/kimi.webp differ diff --git a/web-core/src/assets/providers/kling.webp b/web-core/src/assets/providers/kling.webp new file mode 100644 index 0000000..60afcac Binary files /dev/null and b/web-core/src/assets/providers/kling.webp differ diff --git a/web-core/src/assets/providers/kluster.webp b/web-core/src/assets/providers/kluster.webp new file mode 100644 index 0000000..4178f94 Binary files /dev/null and b/web-core/src/assets/providers/kluster.webp differ diff --git a/web-core/src/assets/providers/kolors.webp b/web-core/src/assets/providers/kolors.webp new file mode 100644 index 0000000..1315659 Binary files /dev/null and b/web-core/src/assets/providers/kolors.webp differ diff --git a/web-core/src/assets/providers/krea.webp b/web-core/src/assets/providers/krea.webp new file mode 100644 index 0000000..1ad238c Binary files /dev/null and b/web-core/src/assets/providers/krea.webp differ diff --git a/web-core/src/assets/providers/kwaikat.webp b/web-core/src/assets/providers/kwaikat.webp new file mode 100644 index 0000000..694dc83 Binary files /dev/null and b/web-core/src/assets/providers/kwaikat.webp differ diff --git a/web-core/src/assets/providers/kwaipilot.webp b/web-core/src/assets/providers/kwaipilot.webp new file mode 100644 index 0000000..3dfbbde Binary files /dev/null and b/web-core/src/assets/providers/kwaipilot.webp differ diff --git a/web-core/src/assets/providers/lambda.webp b/web-core/src/assets/providers/lambda.webp new file mode 100644 index 0000000..b7c0d79 Binary files /dev/null and b/web-core/src/assets/providers/lambda.webp differ diff --git a/web-core/src/assets/providers/langchain.webp b/web-core/src/assets/providers/langchain.webp new file mode 100644 index 0000000..0d696e0 Binary files /dev/null and b/web-core/src/assets/providers/langchain.webp differ diff --git a/web-core/src/assets/providers/langfuse.webp b/web-core/src/assets/providers/langfuse.webp new file mode 100644 index 0000000..e316b6c Binary files /dev/null and b/web-core/src/assets/providers/langfuse.webp differ diff --git a/web-core/src/assets/providers/langgraph.webp b/web-core/src/assets/providers/langgraph.webp new file mode 100644 index 0000000..9fda83d Binary files /dev/null and b/web-core/src/assets/providers/langgraph.webp differ diff --git a/web-core/src/assets/providers/langsmith.webp b/web-core/src/assets/providers/langsmith.webp new file mode 100644 index 0000000..e3349cb Binary files /dev/null and b/web-core/src/assets/providers/langsmith.webp differ diff --git a/web-core/src/assets/providers/leptonai.webp b/web-core/src/assets/providers/leptonai.webp new file mode 100644 index 0000000..d363824 Binary files /dev/null and b/web-core/src/assets/providers/leptonai.webp differ diff --git a/web-core/src/assets/providers/lg.webp b/web-core/src/assets/providers/lg.webp new file mode 100644 index 0000000..ffe13a4 Binary files /dev/null and b/web-core/src/assets/providers/lg.webp differ diff --git a/web-core/src/assets/providers/lightricks.webp b/web-core/src/assets/providers/lightricks.webp new file mode 100644 index 0000000..08e9dd4 Binary files /dev/null and b/web-core/src/assets/providers/lightricks.webp differ diff --git a/web-core/src/assets/providers/liquid.webp b/web-core/src/assets/providers/liquid.webp new file mode 100644 index 0000000..ddc9210 Binary files /dev/null and b/web-core/src/assets/providers/liquid.webp differ diff --git a/web-core/src/assets/providers/livekit.webp b/web-core/src/assets/providers/livekit.webp new file mode 100644 index 0000000..57e96a9 Binary files /dev/null and b/web-core/src/assets/providers/livekit.webp differ diff --git a/web-core/src/assets/providers/llamaindex.webp b/web-core/src/assets/providers/llamaindex.webp new file mode 100644 index 0000000..f53d674 Binary files /dev/null and b/web-core/src/assets/providers/llamaindex.webp differ diff --git a/web-core/src/assets/providers/llava.webp b/web-core/src/assets/providers/llava.webp new file mode 100644 index 0000000..69b5e4a Binary files /dev/null and b/web-core/src/assets/providers/llava.webp differ diff --git a/web-core/src/assets/providers/llmapi.webp b/web-core/src/assets/providers/llmapi.webp new file mode 100644 index 0000000..7f70f58 Binary files /dev/null and b/web-core/src/assets/providers/llmapi.webp differ diff --git a/web-core/src/assets/providers/lmstudio.webp b/web-core/src/assets/providers/lmstudio.webp new file mode 100644 index 0000000..38e2777 Binary files /dev/null and b/web-core/src/assets/providers/lmstudio.webp differ diff --git a/web-core/src/assets/providers/lobehub.webp b/web-core/src/assets/providers/lobehub.webp new file mode 100644 index 0000000..59b70b2 Binary files /dev/null and b/web-core/src/assets/providers/lobehub.webp differ diff --git a/web-core/src/assets/providers/longcat.webp b/web-core/src/assets/providers/longcat.webp new file mode 100644 index 0000000..c038a54 Binary files /dev/null and b/web-core/src/assets/providers/longcat.webp differ diff --git a/web-core/src/assets/providers/lovable.webp b/web-core/src/assets/providers/lovable.webp new file mode 100644 index 0000000..3bcec25 Binary files /dev/null and b/web-core/src/assets/providers/lovable.webp differ diff --git a/web-core/src/assets/providers/lovart.webp b/web-core/src/assets/providers/lovart.webp new file mode 100644 index 0000000..4cfdff5 Binary files /dev/null and b/web-core/src/assets/providers/lovart.webp differ diff --git a/web-core/src/assets/providers/luma.webp b/web-core/src/assets/providers/luma.webp new file mode 100644 index 0000000..c27970d Binary files /dev/null and b/web-core/src/assets/providers/luma.webp differ diff --git a/web-core/src/assets/providers/magic.webp b/web-core/src/assets/providers/magic.webp new file mode 100644 index 0000000..efc4f37 Binary files /dev/null and b/web-core/src/assets/providers/magic.webp differ diff --git a/web-core/src/assets/providers/make.webp b/web-core/src/assets/providers/make.webp new file mode 100644 index 0000000..99cae0e Binary files /dev/null and b/web-core/src/assets/providers/make.webp differ diff --git a/web-core/src/assets/providers/manus.webp b/web-core/src/assets/providers/manus.webp new file mode 100644 index 0000000..202dce6 Binary files /dev/null and b/web-core/src/assets/providers/manus.webp differ diff --git a/web-core/src/assets/providers/mastra.webp b/web-core/src/assets/providers/mastra.webp new file mode 100644 index 0000000..cc5e2d7 Binary files /dev/null and b/web-core/src/assets/providers/mastra.webp differ diff --git a/web-core/src/assets/providers/mcp.webp b/web-core/src/assets/providers/mcp.webp new file mode 100644 index 0000000..fe44a97 Binary files /dev/null and b/web-core/src/assets/providers/mcp.webp differ diff --git a/web-core/src/assets/providers/mcpso.webp b/web-core/src/assets/providers/mcpso.webp new file mode 100644 index 0000000..eb38cd5 Binary files /dev/null and b/web-core/src/assets/providers/mcpso.webp differ diff --git a/web-core/src/assets/providers/menlo.webp b/web-core/src/assets/providers/menlo.webp new file mode 100644 index 0000000..0d41e82 Binary files /dev/null and b/web-core/src/assets/providers/menlo.webp differ diff --git a/web-core/src/assets/providers/meta.webp b/web-core/src/assets/providers/meta.webp new file mode 100644 index 0000000..7d97e9f Binary files /dev/null and b/web-core/src/assets/providers/meta.webp differ diff --git a/web-core/src/assets/providers/metaai.webp b/web-core/src/assets/providers/metaai.webp new file mode 100644 index 0000000..aee59b4 Binary files /dev/null and b/web-core/src/assets/providers/metaai.webp differ diff --git a/web-core/src/assets/providers/metagpt.webp b/web-core/src/assets/providers/metagpt.webp new file mode 100644 index 0000000..16595e2 Binary files /dev/null and b/web-core/src/assets/providers/metagpt.webp differ diff --git a/web-core/src/assets/providers/microsoft.webp b/web-core/src/assets/providers/microsoft.webp new file mode 100644 index 0000000..420294f Binary files /dev/null and b/web-core/src/assets/providers/microsoft.webp differ diff --git a/web-core/src/assets/providers/midjourney.webp b/web-core/src/assets/providers/midjourney.webp new file mode 100644 index 0000000..65e68b0 Binary files /dev/null and b/web-core/src/assets/providers/midjourney.webp differ diff --git a/web-core/src/assets/providers/minimax.webp b/web-core/src/assets/providers/minimax.webp new file mode 100644 index 0000000..f2a3d97 Binary files /dev/null and b/web-core/src/assets/providers/minimax.webp differ diff --git a/web-core/src/assets/providers/mistral.webp b/web-core/src/assets/providers/mistral.webp new file mode 100644 index 0000000..7b1d849 Binary files /dev/null and b/web-core/src/assets/providers/mistral.webp differ diff --git a/web-core/src/assets/providers/modelscope.webp b/web-core/src/assets/providers/modelscope.webp new file mode 100644 index 0000000..916c187 Binary files /dev/null and b/web-core/src/assets/providers/modelscope.webp differ diff --git a/web-core/src/assets/providers/monica.webp b/web-core/src/assets/providers/monica.webp new file mode 100644 index 0000000..1541f6f Binary files /dev/null and b/web-core/src/assets/providers/monica.webp differ diff --git a/web-core/src/assets/providers/moonshot.webp b/web-core/src/assets/providers/moonshot.webp new file mode 100644 index 0000000..6d18d4a Binary files /dev/null and b/web-core/src/assets/providers/moonshot.webp differ diff --git a/web-core/src/assets/providers/morph.webp b/web-core/src/assets/providers/morph.webp new file mode 100644 index 0000000..8529023 Binary files /dev/null and b/web-core/src/assets/providers/morph.webp differ diff --git a/web-core/src/assets/providers/myshell.webp b/web-core/src/assets/providers/myshell.webp new file mode 100644 index 0000000..d4754e5 Binary files /dev/null and b/web-core/src/assets/providers/myshell.webp differ diff --git a/web-core/src/assets/providers/n8n.webp b/web-core/src/assets/providers/n8n.webp new file mode 100644 index 0000000..eca00d9 Binary files /dev/null and b/web-core/src/assets/providers/n8n.webp differ diff --git a/web-core/src/assets/providers/nanobanana.webp b/web-core/src/assets/providers/nanobanana.webp new file mode 100644 index 0000000..59efa7f Binary files /dev/null and b/web-core/src/assets/providers/nanobanana.webp differ diff --git a/web-core/src/assets/providers/nebius.webp b/web-core/src/assets/providers/nebius.webp new file mode 100644 index 0000000..6da4499 Binary files /dev/null and b/web-core/src/assets/providers/nebius.webp differ diff --git a/web-core/src/assets/providers/newapi.webp b/web-core/src/assets/providers/newapi.webp new file mode 100644 index 0000000..7c2e796 Binary files /dev/null and b/web-core/src/assets/providers/newapi.webp differ diff --git a/web-core/src/assets/providers/notebooklm.webp b/web-core/src/assets/providers/notebooklm.webp new file mode 100644 index 0000000..6f55c87 Binary files /dev/null and b/web-core/src/assets/providers/notebooklm.webp differ diff --git a/web-core/src/assets/providers/notion.webp b/web-core/src/assets/providers/notion.webp new file mode 100644 index 0000000..2969333 Binary files /dev/null and b/web-core/src/assets/providers/notion.webp differ diff --git a/web-core/src/assets/providers/nousresearch.webp b/web-core/src/assets/providers/nousresearch.webp new file mode 100644 index 0000000..85271a8 Binary files /dev/null and b/web-core/src/assets/providers/nousresearch.webp differ diff --git a/web-core/src/assets/providers/nova.webp b/web-core/src/assets/providers/nova.webp new file mode 100644 index 0000000..44ce36c Binary files /dev/null and b/web-core/src/assets/providers/nova.webp differ diff --git a/web-core/src/assets/providers/novelai.webp b/web-core/src/assets/providers/novelai.webp new file mode 100644 index 0000000..f3feebb Binary files /dev/null and b/web-core/src/assets/providers/novelai.webp differ diff --git a/web-core/src/assets/providers/novita.webp b/web-core/src/assets/providers/novita.webp new file mode 100644 index 0000000..b5cc029 Binary files /dev/null and b/web-core/src/assets/providers/novita.webp differ diff --git a/web-core/src/assets/providers/nplcloud.webp b/web-core/src/assets/providers/nplcloud.webp new file mode 100644 index 0000000..e92af49 Binary files /dev/null and b/web-core/src/assets/providers/nplcloud.webp differ diff --git a/web-core/src/assets/providers/nvidia.webp b/web-core/src/assets/providers/nvidia.webp new file mode 100644 index 0000000..076e347 Binary files /dev/null and b/web-core/src/assets/providers/nvidia.webp differ diff --git a/web-core/src/assets/providers/obsidian.webp b/web-core/src/assets/providers/obsidian.webp new file mode 100644 index 0000000..8f5e404 Binary files /dev/null and b/web-core/src/assets/providers/obsidian.webp differ diff --git a/web-core/src/assets/providers/ollama.webp b/web-core/src/assets/providers/ollama.webp new file mode 100644 index 0000000..1099327 Binary files /dev/null and b/web-core/src/assets/providers/ollama.webp differ diff --git a/web-core/src/assets/providers/openai.webp b/web-core/src/assets/providers/openai.webp new file mode 100644 index 0000000..28d6c1b Binary files /dev/null and b/web-core/src/assets/providers/openai.webp differ diff --git a/web-core/src/assets/providers/openchat.webp b/web-core/src/assets/providers/openchat.webp new file mode 100644 index 0000000..7b0866d Binary files /dev/null and b/web-core/src/assets/providers/openchat.webp differ diff --git a/web-core/src/assets/providers/openclaw.webp b/web-core/src/assets/providers/openclaw.webp new file mode 100644 index 0000000..c2d9ae9 Binary files /dev/null and b/web-core/src/assets/providers/openclaw.webp differ diff --git a/web-core/src/assets/providers/opencode.webp b/web-core/src/assets/providers/opencode.webp new file mode 100644 index 0000000..b302236 Binary files /dev/null and b/web-core/src/assets/providers/opencode.webp differ diff --git a/web-core/src/assets/providers/openhands.webp b/web-core/src/assets/providers/openhands.webp new file mode 100644 index 0000000..5f690ab Binary files /dev/null and b/web-core/src/assets/providers/openhands.webp differ diff --git a/web-core/src/assets/providers/openrouter.webp b/web-core/src/assets/providers/openrouter.webp new file mode 100644 index 0000000..0298867 Binary files /dev/null and b/web-core/src/assets/providers/openrouter.webp differ diff --git a/web-core/src/assets/providers/openwebui.webp b/web-core/src/assets/providers/openwebui.webp new file mode 100644 index 0000000..48ba7f2 Binary files /dev/null and b/web-core/src/assets/providers/openwebui.webp differ diff --git a/web-core/src/assets/providers/palm.webp b/web-core/src/assets/providers/palm.webp new file mode 100644 index 0000000..4c09e09 Binary files /dev/null and b/web-core/src/assets/providers/palm.webp differ diff --git a/web-core/src/assets/providers/parasail.webp b/web-core/src/assets/providers/parasail.webp new file mode 100644 index 0000000..fa945d7 Binary files /dev/null and b/web-core/src/assets/providers/parasail.webp differ diff --git a/web-core/src/assets/providers/perplexity.webp b/web-core/src/assets/providers/perplexity.webp new file mode 100644 index 0000000..7f4f830 Binary files /dev/null and b/web-core/src/assets/providers/perplexity.webp differ diff --git a/web-core/src/assets/providers/phidata.webp b/web-core/src/assets/providers/phidata.webp new file mode 100644 index 0000000..cf87b16 Binary files /dev/null and b/web-core/src/assets/providers/phidata.webp differ diff --git a/web-core/src/assets/providers/phind.webp b/web-core/src/assets/providers/phind.webp new file mode 100644 index 0000000..b67ee1d Binary files /dev/null and b/web-core/src/assets/providers/phind.webp differ diff --git a/web-core/src/assets/providers/pika.webp b/web-core/src/assets/providers/pika.webp new file mode 100644 index 0000000..10efd15 Binary files /dev/null and b/web-core/src/assets/providers/pika.webp differ diff --git a/web-core/src/assets/providers/pixverse.webp b/web-core/src/assets/providers/pixverse.webp new file mode 100644 index 0000000..ff620f6 Binary files /dev/null and b/web-core/src/assets/providers/pixverse.webp differ diff --git a/web-core/src/assets/providers/player2.webp b/web-core/src/assets/providers/player2.webp new file mode 100644 index 0000000..ba9296a Binary files /dev/null and b/web-core/src/assets/providers/player2.webp differ diff --git a/web-core/src/assets/providers/poe.webp b/web-core/src/assets/providers/poe.webp new file mode 100644 index 0000000..f8b84c5 Binary files /dev/null and b/web-core/src/assets/providers/poe.webp differ diff --git a/web-core/src/assets/providers/pollinations.webp b/web-core/src/assets/providers/pollinations.webp new file mode 100644 index 0000000..1c4b764 Binary files /dev/null and b/web-core/src/assets/providers/pollinations.webp differ diff --git a/web-core/src/assets/providers/ppio.webp b/web-core/src/assets/providers/ppio.webp new file mode 100644 index 0000000..78a219e Binary files /dev/null and b/web-core/src/assets/providers/ppio.webp differ diff --git a/web-core/src/assets/providers/prunaai.webp b/web-core/src/assets/providers/prunaai.webp new file mode 100644 index 0000000..829467f Binary files /dev/null and b/web-core/src/assets/providers/prunaai.webp differ diff --git a/web-core/src/assets/providers/pydanticai.webp b/web-core/src/assets/providers/pydanticai.webp new file mode 100644 index 0000000..145e981 Binary files /dev/null and b/web-core/src/assets/providers/pydanticai.webp differ diff --git a/web-core/src/assets/providers/qingyan.webp b/web-core/src/assets/providers/qingyan.webp new file mode 100644 index 0000000..7d39030 Binary files /dev/null and b/web-core/src/assets/providers/qingyan.webp differ diff --git a/web-core/src/assets/providers/qiniu.webp b/web-core/src/assets/providers/qiniu.webp new file mode 100644 index 0000000..b9d357f Binary files /dev/null and b/web-core/src/assets/providers/qiniu.webp differ diff --git a/web-core/src/assets/providers/qoder.webp b/web-core/src/assets/providers/qoder.webp new file mode 100644 index 0000000..834236e Binary files /dev/null and b/web-core/src/assets/providers/qoder.webp differ diff --git a/web-core/src/assets/providers/qwen.webp b/web-core/src/assets/providers/qwen.webp new file mode 100644 index 0000000..66b9557 Binary files /dev/null and b/web-core/src/assets/providers/qwen.webp differ diff --git a/web-core/src/assets/providers/railway.webp b/web-core/src/assets/providers/railway.webp new file mode 100644 index 0000000..19e55f0 Binary files /dev/null and b/web-core/src/assets/providers/railway.webp differ diff --git a/web-core/src/assets/providers/recraft.webp b/web-core/src/assets/providers/recraft.webp new file mode 100644 index 0000000..e5d1ecb Binary files /dev/null and b/web-core/src/assets/providers/recraft.webp differ diff --git a/web-core/src/assets/providers/relace.webp b/web-core/src/assets/providers/relace.webp new file mode 100644 index 0000000..f388a7f Binary files /dev/null and b/web-core/src/assets/providers/relace.webp differ diff --git a/web-core/src/assets/providers/replicate.webp b/web-core/src/assets/providers/replicate.webp new file mode 100644 index 0000000..6d92480 Binary files /dev/null and b/web-core/src/assets/providers/replicate.webp differ diff --git a/web-core/src/assets/providers/replit.webp b/web-core/src/assets/providers/replit.webp new file mode 100644 index 0000000..6654c88 Binary files /dev/null and b/web-core/src/assets/providers/replit.webp differ diff --git a/web-core/src/assets/providers/reve.webp b/web-core/src/assets/providers/reve.webp new file mode 100644 index 0000000..7c51947 Binary files /dev/null and b/web-core/src/assets/providers/reve.webp differ diff --git a/web-core/src/assets/providers/roocode.webp b/web-core/src/assets/providers/roocode.webp new file mode 100644 index 0000000..ab50e61 Binary files /dev/null and b/web-core/src/assets/providers/roocode.webp differ diff --git a/web-core/src/assets/providers/rsshub.webp b/web-core/src/assets/providers/rsshub.webp new file mode 100644 index 0000000..d10a819 Binary files /dev/null and b/web-core/src/assets/providers/rsshub.webp differ diff --git a/web-core/src/assets/providers/runninghub.png b/web-core/src/assets/providers/runninghub.png new file mode 100644 index 0000000..8a16b84 Binary files /dev/null and b/web-core/src/assets/providers/runninghub.png differ diff --git a/web-core/src/assets/providers/runway.webp b/web-core/src/assets/providers/runway.webp new file mode 100644 index 0000000..a2e86bd Binary files /dev/null and b/web-core/src/assets/providers/runway.webp differ diff --git a/web-core/src/assets/providers/rwkv.webp b/web-core/src/assets/providers/rwkv.webp new file mode 100644 index 0000000..816b0c7 Binary files /dev/null and b/web-core/src/assets/providers/rwkv.webp differ diff --git a/web-core/src/assets/providers/sambanova.webp b/web-core/src/assets/providers/sambanova.webp new file mode 100644 index 0000000..1ee6de4 Binary files /dev/null and b/web-core/src/assets/providers/sambanova.webp differ diff --git a/web-core/src/assets/providers/search1api.webp b/web-core/src/assets/providers/search1api.webp new file mode 100644 index 0000000..58af6a1 Binary files /dev/null and b/web-core/src/assets/providers/search1api.webp differ diff --git a/web-core/src/assets/providers/searchapi.webp b/web-core/src/assets/providers/searchapi.webp new file mode 100644 index 0000000..a364639 Binary files /dev/null and b/web-core/src/assets/providers/searchapi.webp differ diff --git a/web-core/src/assets/providers/sensenova.webp b/web-core/src/assets/providers/sensenova.webp new file mode 100644 index 0000000..b9a955d Binary files /dev/null and b/web-core/src/assets/providers/sensenova.webp differ diff --git a/web-core/src/assets/providers/siliconcloud.webp b/web-core/src/assets/providers/siliconcloud.webp new file mode 100644 index 0000000..1e593cf Binary files /dev/null and b/web-core/src/assets/providers/siliconcloud.webp differ diff --git a/web-core/src/assets/providers/skywork.webp b/web-core/src/assets/providers/skywork.webp new file mode 100644 index 0000000..9632bec Binary files /dev/null and b/web-core/src/assets/providers/skywork.webp differ diff --git a/web-core/src/assets/providers/smithery.webp b/web-core/src/assets/providers/smithery.webp new file mode 100644 index 0000000..7ea3c6b Binary files /dev/null and b/web-core/src/assets/providers/smithery.webp differ diff --git a/web-core/src/assets/providers/snowflake.webp b/web-core/src/assets/providers/snowflake.webp new file mode 100644 index 0000000..805ef96 Binary files /dev/null and b/web-core/src/assets/providers/snowflake.webp differ diff --git a/web-core/src/assets/providers/sophnet.webp b/web-core/src/assets/providers/sophnet.webp new file mode 100644 index 0000000..84e7341 Binary files /dev/null and b/web-core/src/assets/providers/sophnet.webp differ diff --git a/web-core/src/assets/providers/sora.webp b/web-core/src/assets/providers/sora.webp new file mode 100644 index 0000000..9753de5 Binary files /dev/null and b/web-core/src/assets/providers/sora.webp differ diff --git a/web-core/src/assets/providers/spark.webp b/web-core/src/assets/providers/spark.webp new file mode 100644 index 0000000..8fc089d Binary files /dev/null and b/web-core/src/assets/providers/spark.webp differ diff --git a/web-core/src/assets/providers/stability.webp b/web-core/src/assets/providers/stability.webp new file mode 100644 index 0000000..f04efb9 Binary files /dev/null and b/web-core/src/assets/providers/stability.webp differ diff --git a/web-core/src/assets/providers/statecloud.webp b/web-core/src/assets/providers/statecloud.webp new file mode 100644 index 0000000..468cadb Binary files /dev/null and b/web-core/src/assets/providers/statecloud.webp differ diff --git a/web-core/src/assets/providers/stepfun.webp b/web-core/src/assets/providers/stepfun.webp new file mode 100644 index 0000000..9812793 Binary files /dev/null and b/web-core/src/assets/providers/stepfun.webp differ diff --git a/web-core/src/assets/providers/straico.webp b/web-core/src/assets/providers/straico.webp new file mode 100644 index 0000000..b494771 Binary files /dev/null and b/web-core/src/assets/providers/straico.webp differ diff --git a/web-core/src/assets/providers/streamlake.webp b/web-core/src/assets/providers/streamlake.webp new file mode 100644 index 0000000..35773c0 Binary files /dev/null and b/web-core/src/assets/providers/streamlake.webp differ diff --git a/web-core/src/assets/providers/submodel.webp b/web-core/src/assets/providers/submodel.webp new file mode 100644 index 0000000..3c65533 Binary files /dev/null and b/web-core/src/assets/providers/submodel.webp differ diff --git a/web-core/src/assets/providers/suno.webp b/web-core/src/assets/providers/suno.webp new file mode 100644 index 0000000..5d77e25 Binary files /dev/null and b/web-core/src/assets/providers/suno.webp differ diff --git a/web-core/src/assets/providers/sync.webp b/web-core/src/assets/providers/sync.webp new file mode 100644 index 0000000..690c393 Binary files /dev/null and b/web-core/src/assets/providers/sync.webp differ diff --git a/web-core/src/assets/providers/targon.webp b/web-core/src/assets/providers/targon.webp new file mode 100644 index 0000000..c190fbc Binary files /dev/null and b/web-core/src/assets/providers/targon.webp differ diff --git a/web-core/src/assets/providers/tavily.webp b/web-core/src/assets/providers/tavily.webp new file mode 100644 index 0000000..a23e575 Binary files /dev/null and b/web-core/src/assets/providers/tavily.webp differ diff --git a/web-core/src/assets/providers/tencent.webp b/web-core/src/assets/providers/tencent.webp new file mode 100644 index 0000000..c0fceb5 Binary files /dev/null and b/web-core/src/assets/providers/tencent.webp differ diff --git a/web-core/src/assets/providers/tencentcloud.webp b/web-core/src/assets/providers/tencentcloud.webp new file mode 100644 index 0000000..1abc27f Binary files /dev/null and b/web-core/src/assets/providers/tencentcloud.webp differ diff --git a/web-core/src/assets/providers/tiangong.webp b/web-core/src/assets/providers/tiangong.webp new file mode 100644 index 0000000..87fa4d2 Binary files /dev/null and b/web-core/src/assets/providers/tiangong.webp differ diff --git a/web-core/src/assets/providers/tii.webp b/web-core/src/assets/providers/tii.webp new file mode 100644 index 0000000..c490fee Binary files /dev/null and b/web-core/src/assets/providers/tii.webp differ diff --git a/web-core/src/assets/providers/together.webp b/web-core/src/assets/providers/together.webp new file mode 100644 index 0000000..41605b9 Binary files /dev/null and b/web-core/src/assets/providers/together.webp differ diff --git a/web-core/src/assets/providers/topazlabs.webp b/web-core/src/assets/providers/topazlabs.webp new file mode 100644 index 0000000..6d18443 Binary files /dev/null and b/web-core/src/assets/providers/topazlabs.webp differ diff --git a/web-core/src/assets/providers/trae.webp b/web-core/src/assets/providers/trae.webp new file mode 100644 index 0000000..90036e0 Binary files /dev/null and b/web-core/src/assets/providers/trae.webp differ diff --git a/web-core/src/assets/providers/tripo.webp b/web-core/src/assets/providers/tripo.webp new file mode 100644 index 0000000..3b4e5dc Binary files /dev/null and b/web-core/src/assets/providers/tripo.webp differ diff --git a/web-core/src/assets/providers/turix.webp b/web-core/src/assets/providers/turix.webp new file mode 100644 index 0000000..8207fd6 Binary files /dev/null and b/web-core/src/assets/providers/turix.webp differ diff --git a/web-core/src/assets/providers/udio.webp b/web-core/src/assets/providers/udio.webp new file mode 100644 index 0000000..6b7cb30 Binary files /dev/null and b/web-core/src/assets/providers/udio.webp differ diff --git a/web-core/src/assets/providers/unstructured.webp b/web-core/src/assets/providers/unstructured.webp new file mode 100644 index 0000000..07f24fe Binary files /dev/null and b/web-core/src/assets/providers/unstructured.webp differ diff --git a/web-core/src/assets/providers/upstage.webp b/web-core/src/assets/providers/upstage.webp new file mode 100644 index 0000000..aed8e98 Binary files /dev/null and b/web-core/src/assets/providers/upstage.webp differ diff --git a/web-core/src/assets/providers/v0.webp b/web-core/src/assets/providers/v0.webp new file mode 100644 index 0000000..306d433 Binary files /dev/null and b/web-core/src/assets/providers/v0.webp differ diff --git a/web-core/src/assets/providers/vectorizerai.webp b/web-core/src/assets/providers/vectorizerai.webp new file mode 100644 index 0000000..5654095 Binary files /dev/null and b/web-core/src/assets/providers/vectorizerai.webp differ diff --git a/web-core/src/assets/providers/vercel.webp b/web-core/src/assets/providers/vercel.webp new file mode 100644 index 0000000..47b6272 Binary files /dev/null and b/web-core/src/assets/providers/vercel.webp differ diff --git a/web-core/src/assets/providers/vertexai.webp b/web-core/src/assets/providers/vertexai.webp new file mode 100644 index 0000000..d70d1c8 Binary files /dev/null and b/web-core/src/assets/providers/vertexai.webp differ diff --git a/web-core/src/assets/providers/vidu.webp b/web-core/src/assets/providers/vidu.webp new file mode 100644 index 0000000..b7d2bab Binary files /dev/null and b/web-core/src/assets/providers/vidu.webp differ diff --git a/web-core/src/assets/providers/viggle.webp b/web-core/src/assets/providers/viggle.webp new file mode 100644 index 0000000..5b67ef6 Binary files /dev/null and b/web-core/src/assets/providers/viggle.webp differ diff --git a/web-core/src/assets/providers/vllm.webp b/web-core/src/assets/providers/vllm.webp new file mode 100644 index 0000000..41d0fae Binary files /dev/null and b/web-core/src/assets/providers/vllm.webp differ diff --git a/web-core/src/assets/providers/volcengine.webp b/web-core/src/assets/providers/volcengine.webp new file mode 100644 index 0000000..b1c81c8 Binary files /dev/null and b/web-core/src/assets/providers/volcengine.webp differ diff --git a/web-core/src/assets/providers/voyage.webp b/web-core/src/assets/providers/voyage.webp new file mode 100644 index 0000000..6ee3ef9 Binary files /dev/null and b/web-core/src/assets/providers/voyage.webp differ diff --git a/web-core/src/assets/providers/wenxin.webp b/web-core/src/assets/providers/wenxin.webp new file mode 100644 index 0000000..f548092 Binary files /dev/null and b/web-core/src/assets/providers/wenxin.webp differ diff --git a/web-core/src/assets/providers/windsurf.webp b/web-core/src/assets/providers/windsurf.webp new file mode 100644 index 0000000..f797253 Binary files /dev/null and b/web-core/src/assets/providers/windsurf.webp differ diff --git a/web-core/src/assets/providers/workersai.webp b/web-core/src/assets/providers/workersai.webp new file mode 100644 index 0000000..d127bd0 Binary files /dev/null and b/web-core/src/assets/providers/workersai.webp differ diff --git a/web-core/src/assets/providers/xai.webp b/web-core/src/assets/providers/xai.webp new file mode 100644 index 0000000..25b52c2 Binary files /dev/null and b/web-core/src/assets/providers/xai.webp differ diff --git a/web-core/src/assets/providers/xiaomimimo.webp b/web-core/src/assets/providers/xiaomimimo.webp new file mode 100644 index 0000000..c82ba1d Binary files /dev/null and b/web-core/src/assets/providers/xiaomimimo.webp differ diff --git a/web-core/src/assets/providers/xinference.webp b/web-core/src/assets/providers/xinference.webp new file mode 100644 index 0000000..8b30bc5 Binary files /dev/null and b/web-core/src/assets/providers/xinference.webp differ diff --git a/web-core/src/assets/providers/xpay.webp b/web-core/src/assets/providers/xpay.webp new file mode 100644 index 0000000..4bc88c9 Binary files /dev/null and b/web-core/src/assets/providers/xpay.webp differ diff --git a/web-core/src/assets/providers/xuanyuan.webp b/web-core/src/assets/providers/xuanyuan.webp new file mode 100644 index 0000000..1447a13 Binary files /dev/null and b/web-core/src/assets/providers/xuanyuan.webp differ diff --git a/web-core/src/assets/providers/yandex.webp b/web-core/src/assets/providers/yandex.webp new file mode 100644 index 0000000..06985eb Binary files /dev/null and b/web-core/src/assets/providers/yandex.webp differ diff --git a/web-core/src/assets/providers/yi.webp b/web-core/src/assets/providers/yi.webp new file mode 100644 index 0000000..4444556 Binary files /dev/null and b/web-core/src/assets/providers/yi.webp differ diff --git a/web-core/src/assets/providers/youmind.webp b/web-core/src/assets/providers/youmind.webp new file mode 100644 index 0000000..3d54d10 Binary files /dev/null and b/web-core/src/assets/providers/youmind.webp differ diff --git a/web-core/src/assets/providers/yuanbao.webp b/web-core/src/assets/providers/yuanbao.webp new file mode 100644 index 0000000..8af86cc Binary files /dev/null and b/web-core/src/assets/providers/yuanbao.webp differ diff --git a/web-core/src/assets/providers/zai.webp b/web-core/src/assets/providers/zai.webp new file mode 100644 index 0000000..3ef4070 Binary files /dev/null and b/web-core/src/assets/providers/zai.webp differ diff --git a/web-core/src/assets/providers/zapier.webp b/web-core/src/assets/providers/zapier.webp new file mode 100644 index 0000000..a1a8889 Binary files /dev/null and b/web-core/src/assets/providers/zapier.webp differ diff --git a/web-core/src/assets/providers/zeabur.webp b/web-core/src/assets/providers/zeabur.webp new file mode 100644 index 0000000..e00a50d Binary files /dev/null and b/web-core/src/assets/providers/zeabur.webp differ diff --git a/web-core/src/assets/providers/zencoder.webp b/web-core/src/assets/providers/zencoder.webp new file mode 100644 index 0000000..f5776d0 Binary files /dev/null and b/web-core/src/assets/providers/zencoder.webp differ diff --git a/web-core/src/assets/providers/zenmux.webp b/web-core/src/assets/providers/zenmux.webp new file mode 100644 index 0000000..4af6c6d Binary files /dev/null and b/web-core/src/assets/providers/zenmux.webp differ diff --git a/web-core/src/assets/providers/zeroone.webp b/web-core/src/assets/providers/zeroone.webp new file mode 100644 index 0000000..688b520 Binary files /dev/null and b/web-core/src/assets/providers/zeroone.webp differ diff --git a/web-core/src/assets/providers/zhipu.webp b/web-core/src/assets/providers/zhipu.webp new file mode 100644 index 0000000..6c8f059 Binary files /dev/null and b/web-core/src/assets/providers/zhipu.webp differ diff --git a/web-core/src/components/editMdPreivew.vue b/web-core/src/components/editMdPreivew.vue new file mode 100644 index 0000000..15f7d58 --- /dev/null +++ b/web-core/src/components/editMdPreivew.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/web-core/src/components/hello.vue b/web-core/src/components/hello.vue new file mode 100644 index 0000000..047fe9a --- /dev/null +++ b/web-core/src/components/hello.vue @@ -0,0 +1,262 @@ + + + + + diff --git a/web-core/src/components/imageTools.vue b/web-core/src/components/imageTools.vue new file mode 100644 index 0000000..9c6b5bb --- /dev/null +++ b/web-core/src/components/imageTools.vue @@ -0,0 +1,145 @@ + + + + + diff --git a/web-core/src/components/modelSelect.vue b/web-core/src/components/modelSelect.vue new file mode 100644 index 0000000..c4527e6 --- /dev/null +++ b/web-core/src/components/modelSelect.vue @@ -0,0 +1,207 @@ + + + + + diff --git a/web-core/src/components/promptEditor.vue b/web-core/src/components/promptEditor.vue new file mode 100644 index 0000000..798a25f --- /dev/null +++ b/web-core/src/components/promptEditor.vue @@ -0,0 +1,548 @@ + + + + + diff --git a/web-core/src/components/setting/components/about.vue b/web-core/src/components/setting/components/about.vue new file mode 100644 index 0000000..4ed3892 --- /dev/null +++ b/web-core/src/components/setting/components/about.vue @@ -0,0 +1,614 @@ + + + + + diff --git a/web-core/src/components/setting/components/agentConfog.vue b/web-core/src/components/setting/components/agentConfog.vue new file mode 100644 index 0000000..48b5fa7 --- /dev/null +++ b/web-core/src/components/setting/components/agentConfog.vue @@ -0,0 +1,437 @@ + + + + + + diff --git a/web-core/src/components/setting/components/dbConfig.vue b/web-core/src/components/setting/components/dbConfig.vue new file mode 100644 index 0000000..09f4643 --- /dev/null +++ b/web-core/src/components/setting/components/dbConfig.vue @@ -0,0 +1,408 @@ + + + + + diff --git a/web-core/src/components/setting/components/devConfig.vue b/web-core/src/components/setting/components/devConfig.vue new file mode 100644 index 0000000..ee344dd --- /dev/null +++ b/web-core/src/components/setting/components/devConfig.vue @@ -0,0 +1,375 @@ + + + + + diff --git a/web-core/src/components/setting/components/fileManagement.vue b/web-core/src/components/setting/components/fileManagement.vue new file mode 100644 index 0000000..2ac4cde --- /dev/null +++ b/web-core/src/components/setting/components/fileManagement.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/web-core/src/components/setting/components/languageConfig.vue b/web-core/src/components/setting/components/languageConfig.vue new file mode 100644 index 0000000..398014e --- /dev/null +++ b/web-core/src/components/setting/components/languageConfig.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/web-core/src/components/setting/components/loginConfig.vue b/web-core/src/components/setting/components/loginConfig.vue new file mode 100644 index 0000000..69b9a91 --- /dev/null +++ b/web-core/src/components/setting/components/loginConfig.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/web-core/src/components/setting/components/logoutConfig.vue b/web-core/src/components/setting/components/logoutConfig.vue new file mode 100644 index 0000000..c54345a --- /dev/null +++ b/web-core/src/components/setting/components/logoutConfig.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/web-core/src/components/setting/components/memoryConfig.vue b/web-core/src/components/setting/components/memoryConfig.vue new file mode 100644 index 0000000..9fd0c36 --- /dev/null +++ b/web-core/src/components/setting/components/memoryConfig.vue @@ -0,0 +1,187 @@ + + + + + diff --git a/web-core/src/components/setting/components/modelMap.vue b/web-core/src/components/setting/components/modelMap.vue new file mode 100644 index 0000000..0a21b33 --- /dev/null +++ b/web-core/src/components/setting/components/modelMap.vue @@ -0,0 +1,371 @@ + + + + + diff --git a/web-core/src/components/setting/components/otherConfig.vue b/web-core/src/components/setting/components/otherConfig.vue new file mode 100644 index 0000000..6ebd4f2 --- /dev/null +++ b/web-core/src/components/setting/components/otherConfig.vue @@ -0,0 +1,81 @@ + + + + + diff --git a/web-core/src/components/setting/components/promptManage.vue b/web-core/src/components/setting/components/promptManage.vue new file mode 100644 index 0000000..5bab2e1 --- /dev/null +++ b/web-core/src/components/setting/components/promptManage.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/web-core/src/components/setting/components/requestConfig.vue b/web-core/src/components/setting/components/requestConfig.vue new file mode 100644 index 0000000..fdd35e6 --- /dev/null +++ b/web-core/src/components/setting/components/requestConfig.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/web-core/src/components/setting/components/skillManagement.vue b/web-core/src/components/setting/components/skillManagement.vue new file mode 100644 index 0000000..c931dc2 --- /dev/null +++ b/web-core/src/components/setting/components/skillManagement.vue @@ -0,0 +1,248 @@ + + + + + diff --git a/web-core/src/components/setting/components/uiConfig.vue b/web-core/src/components/setting/components/uiConfig.vue new file mode 100644 index 0000000..0e152bd --- /dev/null +++ b/web-core/src/components/setting/components/uiConfig.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/web-core/src/components/setting/components/vendorConfig.vue b/web-core/src/components/setting/components/vendorConfig.vue new file mode 100644 index 0000000..8cb2734 --- /dev/null +++ b/web-core/src/components/setting/components/vendorConfig.vue @@ -0,0 +1,1560 @@ + + + + + diff --git a/web-core/src/components/setting/components/vendorTest/AudioUploadBox.vue b/web-core/src/components/setting/components/vendorTest/AudioUploadBox.vue new file mode 100644 index 0000000..9745c6a --- /dev/null +++ b/web-core/src/components/setting/components/vendorTest/AudioUploadBox.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/web-core/src/components/setting/components/vendorTest/ImageModelTest.vue b/web-core/src/components/setting/components/vendorTest/ImageModelTest.vue new file mode 100644 index 0000000..8b640e8 --- /dev/null +++ b/web-core/src/components/setting/components/vendorTest/ImageModelTest.vue @@ -0,0 +1,275 @@ + + + + + diff --git a/web-core/src/components/setting/components/vendorTest/ImageUploadBox.vue b/web-core/src/components/setting/components/vendorTest/ImageUploadBox.vue new file mode 100644 index 0000000..9170ad8 --- /dev/null +++ b/web-core/src/components/setting/components/vendorTest/ImageUploadBox.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/web-core/src/components/setting/components/vendorTest/TextModelTest.vue b/web-core/src/components/setting/components/vendorTest/TextModelTest.vue new file mode 100644 index 0000000..06067fb --- /dev/null +++ b/web-core/src/components/setting/components/vendorTest/TextModelTest.vue @@ -0,0 +1,245 @@ + + + + + diff --git a/web-core/src/components/setting/components/vendorTest/VideoModelTest.vue b/web-core/src/components/setting/components/vendorTest/VideoModelTest.vue new file mode 100644 index 0000000..2691d0e --- /dev/null +++ b/web-core/src/components/setting/components/vendorTest/VideoModelTest.vue @@ -0,0 +1,494 @@ + + + + + diff --git a/web-core/src/components/setting/components/vendorTest/VideoUploadBox.vue b/web-core/src/components/setting/components/vendorTest/VideoUploadBox.vue new file mode 100644 index 0000000..efacf70 --- /dev/null +++ b/web-core/src/components/setting/components/vendorTest/VideoUploadBox.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/web-core/src/components/setting/index.vue b/web-core/src/components/setting/index.vue new file mode 100644 index 0000000..cca6896 --- /dev/null +++ b/web-core/src/components/setting/index.vue @@ -0,0 +1,169 @@ + + + + + diff --git a/web-core/src/components/storyboardImageCheck.vue b/web-core/src/components/storyboardImageCheck.vue new file mode 100644 index 0000000..ea0f3f5 --- /dev/null +++ b/web-core/src/components/storyboardImageCheck.vue @@ -0,0 +1,464 @@ + + + + + + + diff --git a/web-core/src/components/titleBar.vue b/web-core/src/components/titleBar.vue new file mode 100644 index 0000000..63ffe88 --- /dev/null +++ b/web-core/src/components/titleBar.vue @@ -0,0 +1,128 @@ + + + + + diff --git a/web-core/src/lib/vendorTemplate.ts b/web-core/src/lib/vendorTemplate.ts new file mode 100644 index 0000000..2ec0ce7 --- /dev/null +++ b/web-core/src/lib/vendorTemplate.ts @@ -0,0 +1,334 @@ +/** + * Toonflow AI供应商模板 + * @version 2.0 + */ + +// ============================================================ +// 类型定义 +// ============================================================ + +type VideoMode = + | "singleImage" //单图参考 + | "startEndRequired" //首尾帧(两张都得有) + | "endFrameOptional" //首尾帧(尾帧可选) + | "startFrameOptional" //首尾帧(首帧可选) + | "text" //文本 + | (`videoReference:${number}` | `imageReference:${number}` | `audioReference:${number}`)[]; //多参考(数字代表限制数量) + +interface TextModel { + name: string; + modelName: string; + type: "text"; + think: boolean; +} + +interface ImageModel { + name: string; + modelName: string; + type: "image"; + mode: ("text" | "singleImage" | "multiReference")[]; + associationSkills?: string; +} + +interface VideoModel { + name: string; + modelName: string; + type: "video"; + mode: VideoMode[]; + associationSkills?: string; + audio: "optional" | false | true; + durationResolutionMap: { duration: number[]; resolution: string[] }[]; +} + +interface TTSModel { + name: string; + modelName: string; + type: "tts"; + voices: { title: string; voice: string }[]; +} + +interface VendorConfig { + id: string; //唯一ID,作为文件名存储用户磁盘上,禁止符号 + version: string; //版本号,格式为x.y,需遵守语义化版本控制 + name: string; //供应商名称 + author: string; //作者 + description?: string; //描述,支持Markdown格式 + icon?: string; //图标,仅支持Base64格式,建议尺寸为128x128像素 + inputs: { key: string; label: string; type: "text" | "password" | "url"; required: boolean; placeholder?: string }[]; + inputValues: Record; + models: (TextModel | ImageModel | VideoModel | TTSModel)[]; +} + +type ReferenceList = + | { type: "image"; sourceType: "base64"; base64: string } + | { type: "audio"; sourceType: "base64"; base64: string } + | { type: "video"; sourceType: "base64"; base64: string }; + +interface ImageConfig { + prompt: string; + referenceList?: Extract[]; + size: "1K" | "2K" | "4K"; + aspectRatio: `${number}:${number}`; +} + +interface VideoConfig { + duration: number; + resolution: string; + aspectRatio: "16:9" | "9:16"; + prompt: string; + referenceList?: ReferenceList[]; + audio?: boolean; + mode: VideoMode[]; +} + +interface TTSConfig { + text: string; + voice: string; + speechRate: number; + pitchRate: number; + volume: number; + referenceList?: Extract[]; +} + +interface PollResult { + completed: boolean; + data?: string; + error?: string; +} + +// ============================================================ +// 全局声明 +// ============================================================ + +declare const axios: any; // HTTP请求库 +declare const logger: (msg: string) => void; // 日志函数 +declare const jsonwebtoken: any; // JWT处理库 +declare const zipImage: (base64: string, size: number) => Promise; // 图片压缩函数,返回有头base64字符串 +declare const zipImageResolution: (base64: string, w: number, h: number) => Promise; // 图片分辨率调整函数,返回有头base64字符串 +declare const mergeImages: (base64Arr: string[], maxSize?: string) => Promise; // 图片合成函数,返回有头base64字符串 +declare const urlToBase64: (url: string) => Promise; // URL转Base64函数,返回有头base64字符串 +declare const pollTask: (fn: () => Promise, interval?: number, timeout?: number) => Promise; // 轮询函数,fn为异步函数,interval为轮询间隔,timeout为超时时间,返回fn的结果 +declare const createOpenAI: any; +declare const createDeepSeek: any; +declare const createZhipu: any; +declare const createQwen: any; +declare const createAnthropic: any; +declare const createOpenAICompatible: any; +declare const createXai: any; +declare const createMinimax: any; +declare const createGoogleGenerativeAI: any; +declare const exports: { + vendor: VendorConfig; + textRequest: (m: TextModel) => any; //文本模型 + imageRequest: (c: ImageConfig, m: ImageModel) => Promise; //图片模型,返回有头base64字符串 + videoRequest: (c: VideoConfig, m: VideoModel) => Promise; //视频模型,返回有头base64字符串 + ttsRequest: (c: TTSConfig, m: TTSModel) => Promise; //(暂未开放)语音模型,返回有头base64字符串 + checkForUpdates?: () => Promise<{ hasUpdate: boolean; latestVersion: string; notice: string }>; //检查更新函数,返回是否有更新和最新版本号和更公告(支持Markdown格式) + updateVendor?: () => Promise; //更新函数,返回最新的代码文本 +}; + +// ============================================================ +// 供应商配置 +// ============================================================ + +const vendor: VendorConfig = { + id: "bull", + version: "2.0", + author: "Toonflow", + name: "空模板", + description: "## OpenAI标准格式接口,可修改请求地址并手动添加模型。", + inputs: [ + { key: "apiKey", label: "API密钥", type: "password", required: true }, + { key: "baseUrl", label: "请求地址", type: "url", required: true, placeholder: "示例:https://api.openai.com/v1" }, + ], + inputValues: { apiKey: "", baseUrl: "https://api.openai.com/v1" }, + models: [{ name: "GPT-4o", modelName: "gpt-4o", type: "text", think: false }], +}; + +// ============================================================ +// 适配器函数 +// ============================================================ + +const textRequest = (model: TextModel) => { + if (!vendor.inputValues.apiKey) throw new Error("缺少API Key"); + const apiKey = vendor.inputValues.apiKey.replace(/^Bearer\s+/i, ""); + return createOpenAI({ baseURL: vendor.inputValues.baseUrl, apiKey }).chat(model.modelName); +}; + +const imageRequest = async (config: ImageConfig, model: ImageModel): Promise => { + return ""; +}; + +const videoRequest = async (config: VideoConfig, model: VideoModel): Promise => { + return ""; +}; + +const ttsRequest = async (config: TTSConfig, model: TTSModel): Promise => { + return ""; +}; + +const checkForUpdates = async (): Promise<{ hasUpdate: boolean; latestVersion: string; notice: string }> => { + return { hasUpdate: false, latestVersion: "2.0", notice: "## 新版本更新公告" }; +}; + +const updateVendor = async (): Promise => { + return ""; +}; + +// ============================================================ +// 导出 +// ============================================================ + +exports.vendor = vendor; +exports.textRequest = textRequest; +exports.imageRequest = imageRequest; +exports.videoRequest = videoRequest; +exports.ttsRequest = ttsRequest; +exports.checkForUpdates = checkForUpdates; +exports.updateVendor = updateVendor; + +// 这行代码用于确保当前文件被识别为模块,避免全局变量冲突 +export {}; + +/** + * ============================================================ + * AI 代码生成指南 + * ============================================================ + * + * 【概述】 + * 本文件是 Toonflow AI 供应商适配模板。AI 在生成新供应商适配代码时, + * 必须严格遵守以下规则,并要求用户提供目标平台的 curl 调用示例或 API 文档信息作为输入依据。 + * + * 【前置要求】 + * 在生成代码前,请向用户索要以下信息(至少其一): + * 1. 目标 API 的 curl 请求示例(包含请求地址、Headers、Body 结构、响应结构) + * 2. 目标 API 的官方文档链接或文档截图/文本内容 + * 3. 需要适配的模型类型(text / image / video / tts)及其能力说明 + * 没有足够信息时,应主动追问,不要凭空编造 API 结构。 + * + * 【代码规则】 + * + * 1. 禁止引入任何外部包 + * 不可使用 import / require,仅能使用本文件「全局声明」区域中已声明的方法和对象, + * 包括:axios、logger、jsonwebtoken、zipImage、zipImageResolution、mergeImages、 + * urlToBase64、pollTask,以及 createOpenAI、createDeepSeek、createZhipu、createQwen、 + * createAnthropic、createOpenAICompatible、createXai、createMinimax、 + * createGoogleGenerativeAI 等 AI SDK 工厂函数。 + * + * 2. 禁止在 exports.* 函数外部声明离散的全大写常量 + * 错误示例:const API_URL = "https://..."; const MAX_RETRY = 3; + * 如果确实需要可配置的常量值,必须将其声明在 vendor.inputValues 中, + * 通过 vendor.inputValues.xxx 访问,让用户可在界面上配置。 + * 如果是纯逻辑内部使用的临时变量,应内联在对应的 exports.* 函数体内部,使用小驼峰命名。 + * + * 3. 逻辑尽量聚合在 exports.* 对应的函数内部 + * 每个适配函数(textRequest / imageRequest / videoRequest / ttsRequest) + * 应自包含,将请求构造、发送、轮询、结果解析等逻辑写在函数体内,避免拆分出大量外部辅助函数。 + * 如果多个函数确实存在公共逻辑(如签名计算、Token 生成、请求头构造), + * 可提取为文件内的小驼峰命名函数,放在「适配器函数」区块之前的「辅助工具」区块中, + * 且不可使用全大写命名。 + * + * 4. 命名规范 + * 所有变量、函数一律使用小驼峰命名(camelCase),禁止使用 UPPER_SNAKE_CASE。 + * + * 5. 不需要重新声明类型 + * 本文件顶部已完整定义了所有接口和类型(VendorConfig、ImageConfig、VideoConfig、 + * TTSConfig、TextModel、ImageModel、VideoModel、TTSModel、ReferenceList、PollResult 等), + * AI 生成代码时直接使用即可,不要重复声明。 + * + * 6. 返回值规范 + * - textRequest(model):返回 AI SDK 的 chat model 实例(通过 createOpenAI 等工厂函数创建)。 + * - imageRequest(config, model):返回有头 base64 字符串(如 "data:image/png;base64,...")。 + * config.referenceList 为 Extract[] 类型, + * 每个引用条目均为 base64 形式(sourceType 固定为 "base64")。 + * - videoRequest(config, model):返回有头 base64 字符串(如 "data:video/mp4;base64,...")。 + * config.referenceList 为 ReferenceList[] 类型,可包含 image / video / audio 三种引用, + * 每个引用条目均为 base64 形式(sourceType 固定为 "base64")。 + * config.mode 为当前激活的视频模式数组,需根据 mode 决定如何使用 referenceList。 + * - ttsRequest(config, model):返回有头 base64 字符串(如 "data:audio/mp3;base64,...")。 + * config.referenceList 为 Extract[] 类型(音频参考)。 + * 当 API 返回的是 URL 而非二进制数据时,使用 urlToBase64(url) 转换。 + * + * 7. ReferenceList 与 VideoMode 说明 + * ReferenceList 是统一的多媒体引用类型,每个条目包含: + * - type: "image" | "audio" | "video"(媒体类型) + * - sourceType: "base64"(当前模板固定为 base64) + * - base64(对应的数据) + * + * VideoMode 定义了视频模型支持的输入模式: + * - "text":纯文本生成视频 + * - "singleImage":单张首帧图片 + * - "startEndRequired":首尾帧(两张都必须提供) + * - "endFrameOptional":首尾帧(尾帧可选) + * - "startFrameOptional":首尾帧(首帧可选) + * - 数组形式如 ["imageReference:9", "videoReference:3", "audioReference:3"]: + * 多模态参考模式,数字表示该类型的最大数量限制。 + * + * 在 videoRequest 中,config.mode 表示当前选择的模式,需根据其值决定: + * - 如何从 config.referenceList 中提取对应类型的引用 + * - 如何构造 API 请求体中的图片/视频/音频参数 + * + * 8. 异步任务处理 + * 对于视频生成等需要轮询的异步任务,使用全局的 pollTask 函数: + * const result = await pollTask(async () => { + * const resp = await axios.get(...); + * if (resp.data.status === "SUCCESS") return { completed: true, data: resp.data.url }; + * if (resp.data.status === "FAILED") return { completed: true, error: resp.data.message }; + * return { completed: false }; + * }, 5000, 600000); // 每5秒轮询,10分钟超时 + * if (result.error) throw new Error(result.error); + * return await urlToBase64(result.data!); + * + * 9. 错误处理 + * 在每个函数开头校验必需参数(如 API Key),缺失时使用 throw new Error("...") 抛出。 + * API 请求失败时,从响应中提取有意义的错误信息抛出,不要吞掉异常。 + * + * 10. 日志输出 + * 在关键步骤使用 logger("...") 输出日志(如"开始提交任务"、"任务ID: xxx"、"轮询中..."), + * 便于调试。 + * + * 11. vendor 配置填写 + * - id:纯英文小写,作为文件名使用,禁止特殊符号和空格。 + * - version:语义化版本格式 "x.y"。 + * - inputs:根据目标 API 所需的认证信息配置(API Key、Secret、请求地址等)。 + * - models:根据目标平台支持的模型列表填写,注意正确设置 type 和各模型特有字段。 + * - VideoModel 的 mode 对应 API 支持的输入模式(参见规则 7 的 VideoMode 说明)。 + * - VideoModel 的 audio 字段:true(始终生成音频)、false(不生成)、"optional"(用户可选)。 + * - VideoModel 的 durationResolutionMap 对应各时长下可选的分辨率。 + * - VideoModel 的 associationSkills 可选,用于描述模型的特殊能力。 + * - ImageModel 的 mode 对应 API 支持的生图模式("text" 纯文本、"singleImage" 单图参考、"multiReference" 多图参考)。 + * - TTSModel 的 voices 对应可选的音色列表。 + * + * 12. 图片处理 + * - 需要压缩图片体积时使用 zipImage(base64, maxSizeKB)。 + * - 需要调整图片分辨率时使用 zipImageResolution(base64, width, height)。 + * - 需要将多张图片拼合为一张时使用 mergeImages(base64Arr, maxSize)。 + * - 以上函数均接收和返回有头 base64 字符串。 + * + * 13. 文件结构 + * 生成的代码必须保持本模板的整体结构: + * 类型定义区 → 全局声明区 → 供应商配置区 → [辅助工具区(可选)] → 适配器函数区 → 导出区 + * 不要打乱顺序,不要删除已有的结构注释分隔线。 + * 辅助工具区用于放置多个适配器函数共享的小驼峰命名辅助函数(如 getHeaders、getBaseUrl)。 + * + * 14. 导出规范 + * 必须导出以下字段(通过 exports.xxx = xxx 赋值): + * - exports.vendor(必须) + * - exports.textRequest(必须) + * - exports.imageRequest(必须) + * - exports.videoRequest(必须) + * - exports.ttsRequest(必须) + * - exports.checkForUpdates(可选) + * - exports.updateVendor(可选) + * 未实现的适配器函数保留空实现(return ""),不可省略导出。 + * 文件末尾必须包含 export {}; 以确保文件被识别为模块。 + * + * 【生成流程】 + * 当用户请求生成新的供应商适配时: + * 1. 确认用户已提供 curl 示例或 API 文档。 + * 2. 分析 API 的认证方式、端点地址、请求/响应结构。 + * 3. 基于本模板结构,填充 vendor 配置和对应的适配器函数。 + * 4. 根据当前模板的 ReferenceList 定义,按 base64 形式构造和消费 referenceList。 + * 5. 仅实现用户需要的模型类型,未用到的函数保留空实现(return "")。 + * 6. 生成完整可用的代码,确保无语法错误、无遗漏导出。 + */ diff --git a/web-core/src/locales/index.ts b/web-core/src/locales/index.ts new file mode 100644 index 0000000..dd7bb41 --- /dev/null +++ b/web-core/src/locales/index.ts @@ -0,0 +1,39 @@ +import { createI18n } from "vue-i18n"; +import { useLocalStorage } from "@vueuse/core"; +import zhCN from "./language/zh-CN.json"; +import zhTW from "./language/zh-TW.json"; +import en from "./language/en.json"; +import thTH from "./language/th_TH.json"; +import viVN from "./language/vi-VN.json"; +import jpJP from "./language/ja_JP.json"; +import ruRU from "./language/ru_RU.json"; + +const languageList = [ + { label: "简体中文", tips: "Chinese (Simplified)", value: "zh-CN" }, + { label: "繁體中文", tips: "Chinese (Traditional)", value: "zh-TW" }, + { label: "English", tips: "English", value: "en" }, + { label: "ไทย", tips: "Thai", value: "th-TH" }, + { label: "Tiếng Việt", tips: "Vietnamese", value: "vi-VN" }, + { label: "日本語", tips: "Japanese", value: "ja-JP" }, + { label: "Русский", tips: "Russian", value: "ru-RU" }, +]; + +const cachedLocale = useLocalStorage("locale", "zh-CN"); + +const i18n = createI18n({ + legacy: false, + locale: cachedLocale.value, + fallbackLocale: "en", + messages: { + "zh-CN": zhCN, + "zh-TW": zhTW, + en, + "th-TH": thTH, + "vi-VN": viVN, + "ja-JP": jpJP, + "ru-RU": ruRU, + }, +}); + +export { languageList, cachedLocale }; +export default i18n; diff --git a/web-core/src/locales/language/en.json b/web-core/src/locales/language/en.json new file mode 100644 index 0000000..d363ae1 --- /dev/null +++ b/web-core/src/locales/language/en.json @@ -0,0 +1,1689 @@ +{ + "components": { + "editMdPreivew": { + "title": "Edit", + "confirm": "Save", + "cancel": "Cancel" + }, + "imageTools": { + "copy": "Copy Image", + "preview": "Preview", + "download": "Download", + "msg": { + "imageLoadFailed": "Failed to load image", + "convertFailed": "Conversion failed", + "copied": "Copied to clipboard", + "copyFailed": "Copy failed", + "downloadFailed": "Download failed", + "downloadStarted": "Download started", + "downloadBlockedOpenNewWindow": "The current image source may restrict downloads; tried opening in a new window" + } + }, + "migrateShow": { + "title": "Migrate Data", + "desc": "Data from an older version was detected. Do you want to migrate it?", + "hide": "Don't show again", + "confirm": "OK", + "msg": { + "migrateSuccess": "Data migration successful", + "migrateFailed": "Data migration failed" + } + }, + "modelSelect": { + "placeholder": "Please select a model", + "type": { + "image": "Image", + "text": "Text", + "video": "Video" + }, + "msg": { + "fetchModelFailed": "Failed to fetch model data:" + }, + "goSetting": "Go to settings and add a model" + } + }, + "settings": { + "title": "ToonFlow Settings", + "menu": { + "language": "Language", + "vendorConfig": "Model Providers", + "agentConfig": "Agent Config", + "promptManage": "Prompt Management", + "skillManagement": "skillsSkillsManagement", + "memoryConfig": "Agent Memory", + "loginConfig": "Login Config", + "dbConfig": "Database", + "fileManagement": "File Management", + "otherConfig": "Other Config", + "requestConfig": "Request URL", + "about": "Check for Updates", + "logoutConfig": "Logout", + "skillsSkillsManagement": "SkillsSkills Management" + }, + "language": { + "desc": "Select the interface display language", + "msg": { + "saved": "Language settings saved" + } + }, + "vendor": { + "addVendor": "Add Provider", + "noVendor": "No providers yet. Please add one.", + "required": "Required", + "optionalSection": "Optional", + "modelSettings": "Model Settings", + "addManually": "Add Manually", + "test": "Test", + "edit": "Edit", + "delete": "Delete", + "deleteVendor": "Delete Provider", + "editCode": "Edit Code", + "updateConfig": "Update Config", + "addModel": "Add Model", + "editModel": "Edit Model", + "displayName": "Display Name", + "displayNamePlaceholder": "e.g., GPT-4o", + "modelId": "Model ID", + "modelIdPlaceholder": "e.g., gpt-4o", + "modelType": "Model Type", + "multimodal": "Multimodal", + "supported": "Supported", + "notSupported": "Not Supported", + "toolCall": "Tool Call", + "imageMode": "Image Mode", + "videoMode": "Video Mode", + "audioOutput": "Audio Output", + "durationResolution": "Duration / Resolution Mapping", + "durationSec": "Duration (sec)", + "resolution": "Resolution", + "enterAndPress": "Press Enter to add", + "addDurationResolution": "Add a Duration/Resolution pair", + "testResult": "Test Results", + "generating": "Generating...", + "addVendorDialog": "Add Provider", + "codeEditorInfo": "Please write TypeScript code to configure provider information", + "reset": "Reset", + "importFile": "Import File", + "textModel": "Text Model", + "imageModel": "Image Model", + "videoModel": "Video Model", + "textToImage": "Text to Image", + "textToVideo": "Text to Video", + "singleImage": "Single Image", + "multiImage": "Multi-Image Mode", + "multiReference": "Multi-Image Reference", + "multiReferenceMode": "Multi-Reference Mode", + "gridImage": "Grid Single Image", + "startEndRequired": "Start & End Frames (Both Required)", + "endFrameOptional": "Start & End Frames (End Frame Optional)", + "startFrameOptional": "Start & End Frames (Start Frame Optional)", + "textRef": "Text", + "imageRef": "Image", + "videoRef": "Video", + "audioRef": "Audio", + "audioOptional": "Optional", + "audioOnly": "Audio Video Only", + "noAudio": "Silent Video Only", + "msg": { + "getVendorListFailed": "Failed to fetch provider list", + "vendorConfigUpdated": "Provider config updated", + "updateFailed": "Update failed", + "highRiskConfirm": "⚠️ High-Risk Operation Confirmation", + "addVendorRiskBody": "Adding a new AI provider grants it access to system APIs. Please ensure you trust the code source of this provider!", + "iKnowRisk": "I understand the risks", + "cancel": "Cancel", + "confirmAgain": "⚠️ Confirm Again", + "addVendorConfirmBody": "Are you sure you want to add this provider? It will be included in the system's model scheduling.", + "confirmAndAdd": "Confirm & Add", + "goBackCheck": "Go Back", + "vendorAdded": "Provider added successfully", + "addFailed": "Failed to add", + "updateVendorRiskBody": "Updating AI provider configurations modifies its API access and behaviors. Please ensure you trust the modified code source!", + "updateVendorConfirmBody": "Are you sure you want to update this provider config? This will affect the system's model scheduling.", + "confirmAndUpdate": "Confirm & Update", + "updateSuccess": "Provider config updated successfully", + "fillDisplayName": "Please enter a display name", + "fillModelId": "Please enter a model ID", + "selectImageMode": "Please select an image mode", + "selectVideoMode": "Please select a video mode", + "groupPrefix": "Group {n}: ", + "addDuration": "Please add a duration", + "addResolution": "Please add a resolution", + "selectVendorFirst": "Please select a provider first", + "modelIdExists": "Model ID already exists", + "modelAdded": "Model added successfully", + "modelUpdated": "Model updated successfully", + "enterApiKey": "Please enter API KEY", + "enterApiUrl": "Please enter API URL", + "testSuccess": "Test successful", + "imageGenSuccess": "Image generated successfully", + "videoGenSuccess": "Video generated successfully", + "requestFailed": "Request failed", + "deleteModelConfirm": "Confirm Model Deletion", + "deleteModelBody": "This action cannot be undone. Do you want to continue?", + "confirmDelete": "Delete", + "modelDeleted": "Model deleted", + "deleteVendorConfirm": "Confirm Provider Deletion", + "deleteVendorBody": "Deleting this provider will also delete all its associated models. Do you want to continue?", + "vendorDeleted": "Provider deleted", + "vendorNeedsUpdate": "This provider version is outdated or missing. Please update to version 2.0 or above for the best experience.", + "deleteFailed": "Deletion failed", + "enabled": "Enabled", + "disabled": "Disabled", + "linkAddVendorRiskBody": "Adding a new AI vendor will give it access to the system API, please make sure you trust the vendor's link source!", + "importAdd": "Adding a new AI vendor will give it access to the system API, please make sure you trust the vendor's documentation source!", + "linkAddFailed": "Failed to add link" + }, + "think": "deep thinking", + "code": "code", + "linkAddPlaceholder": "Enter link to add", + "noFileSelected": "File imported successfully", + "linkAdd": "confirm" + }, + "agent": { + "bannerDesc": "Use the official Toonflow relay site for one-click configuration. Ready out-of-the-box, no manual setup needed.", + "visitWebsite": "Visit Website", + "fillKey": "Enter KEY", + "oneClickFill": "One-Click Fill", + "notOpen": "Not Available", + "notConfigured": "Not Configured", + "modelConfig": "Model Config", + "confirm": "Confirm", + "cancel": "Cancel", + "selectModel": "Select Model", + "fillKeyHeader": "Enter official KEY from Toonflow platform", + "keyPlaceholder": "Please enter KEY", + "save": "Save", + "msg": { + "notAvailable": "This feature is not yet available. Stay tuned!", + "configSuccess": "Configured successfully", + "updateConfigFailed": "Failed to update config: ", + "keyValid": "KEY is valid. Successfully connected to the Toonflow platform", + "keyInvalid": "KEY is invalid. Please check and re-enter: ", + "enterKey": "Please enter KEY", + "saveFailed": "Save failed: ", + "getAgentListFailed": "Failed to fetch Agent config list: ", + "toonflowNotFound": "Toonflow official transfer station does not exist" + }, + "temperature": "temperature" + }, + "memory": { + "warning": "The following settings are preset to recommended values. Unless you fully understand their implications, we recommend keeping the current settings.", + "vectorModelConfig": "Vector Model Config", + "modelFilePath": "Model File Path", + "quantizationType": "Quantization Type", + "quantizationPlaceholder": "Please enter quantization type", + "memoryParams": "Memory Parameters", + "messagesPerSummary": "Messages per Summary Trigger", + "messagesPerSummaryHelp": "Retains the last N conversation contexts.", + "shortTermLimit": "Short-Term Memory Limit", + "shortTermLimitHelp": "Number of candidate memories returned during retrieval.", + "summaryMaxLength": "Summary Max Length", + "summaryMaxLengthHelp": "Maximum characters allowed during message compression.", + "summaryLimit": "Compressed Message Query Limit", + "summaryLimitHelp": "Allowed number of compressed messages to query.", + "ragLimit": "RAG Search Limit", + "ragLimitHelp": "Number of messages retrieved during a search.", + "deepRetrieveSummaryLimit": "Vector Recall Compressed Limit", + "deepRetrieveSummaryLimitHelp": "Number of messages fetched when retrieving compressed message content.", + "saveConfig": "Save Config", + "clearMemory": "Clear Memory", + "restoreDefault": "Restore Defaults", + "msg": { + "saved": "Memory config saved", + "clearConfirmTitle": "Confirm Clear Memory", + "clearConfirmBody": "This will erase the AI's global memory data and cannot be undone. Continue?", + "confirmClear": "Clear", + "cancel": "Cancel", + "cleared": "Memory cleared", + "clearFailed": "Failed to clear memory" + }, + "modelMap": { + "name": "Model name", + "model": "Model", + "type": "type", + "editWord": "Bind prompt word", + "operation": "operate", + "bindingSuccessful": "Binding successful", + "bindingFailed": "Binding failed", + "currentBinding": "current binding", + "noBinding": "Not Bound", + "bound": "Bound", + "unbind": "Unbind", + "filenName": "File name" + } + }, + "login": { + "username": "Username", + "usernamePlaceholder": "Please enter username", + "password": "Password", + "passwordPlaceholder": "Please enter password", + "modify": "Modify", + "msg": { + "enterUsername": "Please enter username", + "usernameLength": "Username must be 2-20 characters long", + "enterPassword": "Please enter password", + "passwordLength": "Password must be 6-20 characters long", + "fetchFailed": "Failed to fetch user information", + "saveSuccess": "Saved successfully", + "saveFailed": "Failed to save" + } + }, + "db": { + "clearDb": "Clear Database", + "clearDbDesc": "Clear data in all tables while keeping table structures", + "clearData": "Clear Data", + "confirmAction": "Confirm Action", + "dbInfo": "Database Overview", + "dbInfoDesc": "View all table names and row counts", + "viewInfo": "View Info", + "tableName": "Table Name", + "rowCount": "Row Count", + "totalTables": "{count} tables in total", + "exportDb": "Export Database", + "exportDbDesc": "Export all data tables as a JSON backup file", + "exportData": "Export Data", + "importDb": "Import Database", + "importDbDesc": "Restore data from a JSON backup file (will overwrite current data)", + "importData": "Import Data", + "clearTable": "Clear Specific Table", + "clearTableDesc": "Select a data table and clear its data", + "clearTableBtn": "Clear Table", + "selectTable": "Select a table", + "msg": { + "clearDbTitle": "Clear Database", + "firstConfirm": "Are you sure you want to clear all data tables? This cannot be recovered!", + "secondConfirm": "Final confirmation: all data will be permanently lost after clearing!", + "keyword": "Clear", + "confirm": "Confirm", + "pleaseInput": "Please type", + "cleared": "All data tables have been cleared", + "operationFailed": "Operation failed, please try again", + "cancelled": "Operation cancelled", + "exportSuccess": "Database exported successfully", + "exportFailed": "Export failed", + "importSuccess": "Database imported successfully, redirecting to login page", + "importFailed": "Import failed", + "invalidFile": "Invalid backup file", + "clearTableSuccess": "Table cleared", + "clearTableFailed": "Failed to clear table", + "clearTableConfirm": "Are you sure you want to clear table {name}? This cannot be undone!", + "importConfirm": "Import will overwrite all current data, are you sure?", + "importSecondConfirm": "Final confirmation: all current data will be replaced after import!", + "noTableSelected": "Please select a table first", + "loadingDbInfo": "Loading database info...", + "loadDbInfoFailed": "Failed to load database info" + } + }, + "skill": { + "scanSkills": "Scan Skills", + "addSkill": "Add Skill", + "importFromHub": "Import from Toonflow-Hub", + "filterType": "Type", + "filterAttribution": "Attribution", + "searchPlaceholder": "Search skill by name", + "search": "Search", + "totalCount": "{count} Skills in total", + "typeMain": "Core", + "typeReferences": "Technique", + "noAttribution": "No Attribution", + "noAttributionTip": "⚠️ No attribution, Skill cannot be enabled", + "noEmbeddingTip": "⚠️ Not embedded, Skill cannot be enabled", + "notEmbedded": "Not Embedded", + "stateNormal": "Normal", + "stateGenerating": "Generating description", + "stateEmptyDesc": "Empty description", + "stateAttrError": "Attribution error", + "stateMd5Changed": "MD5 changed, update description recommended", + "embedding": "Embed", + "edit": "Edit", + "delete": "Delete", + "importFromHubDialog": "Import from Toonflow-Hub", + "shareLink": "Share Link", + "editSkillTitle": "Edit Skill - ", + "addSkillTitle": "Add Skill", + "skillName": "Skill Name", + "skillNamePlaceholder": "e.g. writing-assistant", + "path": "Path", + "attributionAgent": "Attribution Agent", + "selectAttribution": "Select Attribution Agent", + "description": "Description", + "aiGenerate": "AI Generate", + "descriptionPlaceholder": "Describe the purpose of this skill", + "cancel": "Cancel", + "save": "Save", + "createSkill": "Create Skill", + "attr": { + "productionDecision": "Production - Director", + "productionExecution": "Production - Cinematographer", + "productionSupervision": "Production - Supervisor", + "scriptDecision": "Script Agent - Coordinator", + "scriptExecution": "Script Agent - Writer", + "scriptSupervision": "Script Agent - Editor", + "universalAgent": "Production Assistant" + }, + "msg": { + "scanSuccess": "Scan completed, found {count} Skill files", + "fetchListFailed": "Failed to fetch skill list", + "fillContentFirst": "Please fill in Markdown content first", + "descGenSuccess": "Description generated successfully", + "descGenFailed": "Failed to generate description", + "fillNameFirst": "Please fill in Skill name first", + "updateSuccess": "Skill updated successfully", + "createSuccess": "Skill created successfully", + "updateFailed": "Failed to update Skill", + "createFailed": "Failed to create Skill", + "deleteConfirmTitle": "Confirm Deletion", + "deleteConfirmBody": "Are you sure you want to delete Skill \"{name}\"? This cannot be undone.", + "deleteSuccess": "Deleted successfully", + "deleteFailed": "Delete failed", + "embeddingSuccess": "Embedding completed", + "embeddingFailed": "Embedding failed" + }, + "fileLost": "File missing" + }, + "other": { + "requestTimeout": "Request Timeout", + "seconds": "Seconds", + "inputSeconds": "Please enter seconds", + "assetConcurrency": "Asset Generation Concurrency", + "count": "Tasks", + "inputCount": "Please enter number of tasks", + "chapterRegex": "Chapter Split Regex", + "restoreDefault": "Restore Defaults", + "regexPlaceholder": "Please enter a regular expression", + "showTitleBar": "show title bar", + "isElectron": "Switch to desktop mode", + "canvasScroll": "Canvas scroll", + "canvasIsDisabled": "Canvas zoom", + "agentCanvasScalingMethod": "Unlimited canvas wheel operation on production page", + "zoom": "Zoom", + "scroll": "scroll", + "isInteracting": "Unlimited canvas drag performance optimization on production page", + "closeIsInteracting": "closure", + "scriptEpisodeLength": "Script Episode Length Limit", + "chars": "chars", + "inputChars": "Please enter character count" + }, + "request": { + "warning": "Do not modify unless absolutely necessary", + "apiAddress": "API URL", + "apiPlaceholder": "Please enter API request URL", + "save": "Save", + "reset": "Reset", + "msg": { + "enterApi": "Please enter API URL", + "validUrl": "Please enter a valid HTTP/HTTPS URL", + "saved": "Request URL saved successfully", + "reset": "Reset to default URL", + "refreshFailed": "Refresh failed", + "refreshSuccess": "Refresh successful" + } + }, + "about": { + "slogan": "Open-source AI-driven Comic / Storyboard creation tool", + "latestVersion": "You are on the latest version", + "checkUpdate": "Check for Updates", + "codeRepository": "Code Repository", + "githubRepo": "GitHub Repository", + "giteeRepo": "Gitee Repository", + "versionUpdate": "Version Update", + "checkUpdateGithub": "Check Update (GitHub)", + "getFromGithub": "Get the latest release from GitHub", + "checkUpdateGitee": "Check Update (Gitee)", + "getFromGitee": "Get the latest release from Gitee", + "license": "License", + "licenseDesc": "Open-source license agreement · Click for details", + "updateAvailable": "new version found", + "currentVersion": "Current Version", + "latestVersionLabel": "Latest Version", + "selectUpdateSource": "Select Update Source", + "github": "GitHub", + "gitee": "Gitee", + "confirmUpdate": "Confirm Update", + "cancel": "Cancel", + "updating": "Updating...", + "updateSuccess": "Update successful, please restart the app", + "updateFailed": "Update failed, please try again", + "noUpdate": "You are already on the latest version", + "upToDate": "New version detected", + "confirmReinstall": "Copy link", + "reinstallRequired": "The browser will automatically open and download. If it does not open, please open it manually." + }, + "logout": { + "warning": "After logging out, you will need to log in again to access the system.", + "confirmLogout": "Are you sure you want to log out?", + "logout": "Logout", + "msg": { + "logoutSuccess": "Logged out successfully", + "logoutFailed": "Logout failed, please try again" + } + }, + "file": { + "quickOpen": "Quick Open Directory", + "open": "Open", + "dockerDesc": "For Docker/Separated deployments, please go to the \"/data/*\" directory to manage files manually.", + "desktopOnly": "This feature is only available on the desktop client", + "folders": { + "data": "data", + "dataDesc": "Data directory.", + "logs": "data/logs", + "logsDesc": "Runtime and error logs.", + "oss": "data/oss", + "ossDesc": "File storage related resources.", + "skills": "data/skills", + "skillsDesc": "Skills and prompt configuration files.", + "models": "data/models", + "modelsDesc": "Model files and configurations.", + "web": "data/web", + "webDesc": "Web-related resources (e.g., frontend build artifacts).", + "serve": "data/serve", + "serveDesc": "Backend service related files.", + "vendor": "data/vendor", + "vendorDesc": "Vendor directory." + }, + "openFailed": "Failed to open folder" + }, + "dev": { + "aiDevtool": "Toggle Desktop Mode", + "devtool": "Open Console", + "switchAiDevTool": "Enable/Disable {'@'}ai-sdk/devtools", + "localStorage": "Browser localStorage Manager", + "localStorageSearchPlaceholder": "Search by key or value", + "localStorageKey": "Key", + "localStorageValue": "Value", + "localStorageKeyPlaceholder": "Enter localStorage key", + "localStorageValuePlaceholder": "Enter localStorage value", + "actions": "Actions", + "refresh": "Refresh", + "add": "Add", + "update": "Update", + "clearAll": "Clear All", + "copyKey": "Copy Key", + "copyValue": "Copy Value", + "format": "Format JSON", + "localStorageCount": "Total {total}, showing {filtered}", + "creating": "Creating new item", + "editing": "Editing: {key}", + "edit": "Edit", + "delete": "Delete", + "save": "Save", + "reset": "Reset", + "warning": "The following are developer tools, please operate with caution!", + "openDevtool": "Open", + "devtoolsDoc": "Document address", + "devtoolsDesc": "After being turned on, a .devtools folder will be created in the Toonflow installation directory. Please ensure that Toonflow has write permissions (run as administrator).", + "devtoolsDesc2": "Run npx {'@'}ai-sdk/devtools in this directory to enable telemetry debugging", + "openDevtoolFailed": "Failed to open developer tools, please make sure Toonflow desktop is installed", + "notInElectron": "For WEB environment, please open the browser console manually", + "msg": { + "localStorageKeyRequired": "Please enter a localStorage key", + "localStorageKeyExists": "This key already exists. Please use a different key", + "localStorageSaved": "localStorage item saved", + "localStorageDeleted": "localStorage item deleted", + "localStorageCleared": "localStorage cleared", + "localStorageKeyCopied": "Key copied", + "localStorageValueCopied": "Value copied", + "copyFailed": "Copy failed", + "localStorageValueEmpty": "Value is empty and cannot be formatted", + "localStorageFormatted": "JSON formatted", + "localStorageFormatFailed": "Format failed. Please check whether the value is valid JSON", + "deleteConfirmTitle": "Confirm deleting localStorage item", + "deleteConfirmBody": "Are you sure you want to delete key: {key}?", + "confirmDelete": "Delete", + "clearConfirmTitle": "Confirm clearing localStorage", + "clearConfirmBody": "This will remove all localStorage data for the current site and cannot be undone. Continue?", + "confirmClear": "Clear", + "cancel": "Cancel" + } + } + }, + "workbench": { + "selectProject": "Please select a project", + "menu": { + "myProject": "My Projects", + "taskCenter": "Task Center", + "novel": "Novel Text", + "scriptAgent": "Script Agent", + "scriptManage": "Script Management", + "cornerScape": "Characters & Scenes", + "production": "Video Production", + "assetCenter": "Asset Center", + "settings": "Settings", + "jumpGithub": "Jump to Github", + "feedbackQuestions": "Feedback question" + }, + "project": { + "title": "My Projects", + "subtitle": "Manage all your short drama projects", + "newProject": "New Project", + "dialog": { + "editTitle": "Edit Project", + "addTitle": "New Project", + "save": "Save", + "ok": "OK", + "cancel": "Cancel", + "projectType": "Project Type", + "selectType": "Select Project Type", + "basedOnNovel": "Based on Novel Text", + "projectName": "Project Name", + "projectNamePh": "Please enter project name", + "novelType": "Novel Genre", + "novelTypePh": "e.g., Fantasy, Sci-Fi, Romance", + "artStyle": "Art Style", + "selected": "Selected:", + "selectArtStyle": "Please select an art style", + "newArtStyle": "New art style", + "loading": "Loading...", + "videoRatio": "Video Ratio", + "novelIntro": "Novel Synopsis", + "novelIntroPh": "Please enter novel synopsis", + "editArtStyleTitle": "Edit art style", + "newArtStyleTitle": "New art style", + "artStyleName": "Art style name", + "artStyleNamePh": "Please enter art style name", + "artStyleImage": "Art style image", + "remove": "Remove", + "uploadCover": "Upload Cover", + "artStylePrompt": "Prompt", + "aiExtract": "AI Extract Prompt", + "promptPlaceholder": "Enter prompt", + "visualManual": "Visual Manual", + "newVisualManual": "New visual manual", + "editVisualManualTitle": "Edit visual manual", + "newVisualManualTitle": "New visual manual", + "visualManualName": "Visual manual name", + "visualManualNamePh": "Please enter visual manual name", + "visualManualCover": "Visual manual cover", + "visualManualPrompt": "Visual manual prompt", + "modelData": "Select image model", + "videoModelData": "Select video model", + "prompt": { + "placeholder": "Enter prompt word", + "saveSuccess": "Update successful", + "title": "prompt word" + }, + "basedOnScript": "based on script", + "mdFile": "visual manual file", + "directorManual": "Director's Handbook", + "addDirectorManual": "New director manual", + "editingDirectorManual": "Edit Director's Manual", + "newDirecorManualTitle": "New director manual", + "directorManualPrompt": "Director's Manual Prompt Words", + "directorManualName": "Director's Manual Name", + "directorManualNamePh": "Enter Director's Manual name", + "directorFile": "Director's Manual Document", + "directorManualCover": "Director's Manual Cover" + }, + "msg": { + "fetchFailed": "Failed to fetch project list", + "notFound": "Project not found!", + "editSuccess": "Project edited successfully", + "editFailed": "Failed to edit project", + "addSuccess": "Project created successfully", + "addFailed": "Failed to create project", + "deleteHeader": "Delete Project", + "deleteBody": "Are you sure you want to delete this project?", + "deleteConfirm": "Delete", + "deleteCancel": "Cancel", + "deleteSuccess": "Project deleted successfully", + "deleteFailed": "Failed to delete project", + "extractSuccess": "Prompt extracted successfully", + "extractFailed": "Extraction failed", + "enterArtStyleName": "Please enter art style name", + "artStyleUpdated": "Art style updated", + "artStyleAdded": "Art style added", + "operationFailed": "Operation failed", + "enterVisualManualName": "Please enter visual manual name", + "enterVisualManualImage": "Please upload a cover image for the visual manual", + "enterVisualManualTabData": "prompt cannot be empty", + "visualManualUpdated": "Visual manual updated", + "visualManualAdded": "Visual manual added", + "deleteVisualManualHeader": "Delete Visual Manual", + "deleteVisualManualBody": "Are you sure you want to delete visual manual \"{name}\"?", + "deleteVisualManualConfirm": "Delete", + "deleteVisualManualCancel": "Cancel", + "enterProjectName": "Please enter project name", + "enterProjectIntro": "Please enter the novel introduction", + "enterProjectType": "Please enter project type", + "enterArtStyle": "Please select a project visual brochure", + "enterVideoRatio": "Please select video ratio", + "enterImageModel": "Please select a picture model", + "enterVideoModel": "Please select a video model", + "visualManualDeleted": "Delete successfully", + "selectMode": "Please select mode", + "deleteDirectorManualHeader": "Delete Director's Manual", + "deleteDirectorManualBody": "Are you sure you want to delete Director's Manual \"{name}\"?", + "directorManualUpdated": "Director's Manual updated", + "directorManualAdded": "Director's Manual added", + "directorManual": "Please select Project Director's Manual", + "modelProviderDisabled": "The video model or picture model supplier is not enabled or there is no model supplier, please configure it first" + }, + "type": { + "novel": "Based on the original novel", + "script": "Based on novel script" + } + }, + "novel": { + "importText": "Import Text", + "batchDelete": "Batch Delete", + "eventAnalysis": "Event Analysis", + "searchPlaceholder": "Search text names...", + "search": "Search", + "generating": "Generating...", + "genFailed": "Generation failed", + "viewDetail": "View Details", + "none": "None", + "edit": "Edit", + "delete": "Delete", + "col": { + "id": "No.", + "reel": "Volume", + "chapter": "Chapter Name", + "chapterData": "Chapter Content", + "event": "Event", + "operation": "Operation" + }, + "msg": { + "batchDeleteHeader": "Batch Delete", + "batchDeleteBody": "Are you sure you want to delete the selected {count} items?", + "batchDeleteSuccess": "Batch delete successful", + "deleteHeader": "Confirm Deletion", + "deleteBody": "Are you sure you want to delete the chapter named \"{name}\"?", + "deleteSuccess": "Deleted successfully", + "eventAnalysisHeader": "Event Analysis", + "eventAnalysisBody": "Are you sure you want to analyze events for the selected {count} items?" + }, + "import": { + "title": "Upload Novel Text", + "step1": "Step 1", + "step2": "Step 2", + "step3": "Step 3", + "dragUpload": "Drag and drop your novel file here or click to upload", + "uploadHint": "Supports .txt, .docx. Recommended file size under 10MB", + "or": "OR", + "pasteLabel": "Directly paste novel text", + "pastePlaceholder": "Please paste novel text here", + "chars": "chars", + "tooShort": "Content is too short, recommend at least 100 characters", + "parsedChapters": "{count} chapters parsed", + "nextStep": "Next", + "prevStep": "Previous", + "selectedInfo": "Selected: {count} chars (Must be < 200,000)", + "eventAnalysis": "Event Analysis", + "saveAndAnalyze": "Save Text and Analyze Events", + "col": { + "chapter": "Chapter", + "reel": "Volume", + "chapterName": "Chapter Name", + "chapterData": "Chapter Content" + }, + "msg": { + "parseFailed": "Failed to parse file. Please re-upload", + "selectFile": "Select file", + "docNotSupported": ".doc files do not support parsing, please convert to .ts files", + "unsupportedType": "Unsupported file type", + "fileTooLarge": "File exceeds 10MB. Please upload a smaller file", + "selectChapters": "Please select chapters first", + "saveSuccess": "Novel text saved successfully" + }, + "importAdd": "Drag and drop files here or click to upload", + "limit": "Support .ts format" + }, + "editDialog": { + "title": "Edit Novel Text", + "chapterName": "Chapter Name", + "chapterNamePh": "Please enter chapter name", + "eventContent": "Event Content", + "eventContentPh": "Enter event content", + "chapterContent": "Chapter Content", + "chapterContentPh": "Please enter chapter content", + "cancel": "Cancel", + "save": "Save", + "msg": { + "updateSuccess": "Novel text updated successfully" + } + }, + "event": { + "regenerate": "Regenerate Events", + "batchDelete": "Batch Delete", + "noData": "No event data. Click to start generation", + "generate": "Generate Events", + "generatingHint": "Generating events, please wait...", + "loading": "Loading...", + "delete": "Delete", + "col": { + "id": "Event ID", + "eventName": "Event Name", + "chapters": "Source Chapter", + "detail": "Event Details", + "createTime": "Created Time", + "operation": "Operation" + }, + "msg": { + "deleteHeader": "Delete Event", + "deleteBody": "Are you sure you want to delete this event?", + "deleteSuccess": "Deleted successfully", + "generateSuccess": "Events generated successfully", + "batchDeleteHeader": "Batch Delete", + "batchDeleteBody": "Are you sure you want to delete the selected {count} items?", + "batchDeleteSuccess": "Batch delete successful" + } + }, + "analysis": { + "analyzeFirst": "Please analyze events first", + "startAnalysis": "Start Analysis", + "chapterHeader": "Chapter {index} - {name}", + "analyzing": "Analyzing events" + } + }, + "scriptAgent": { + "inputPlaceholder": "Please enter content", + "chapterEvents": "Chapter Events", + "clearMessageMemory": "Clear Message Memory", + "clearSummaryMemory": "Clear Summary Memory", + "clearAllMemory": "Clear All Memory", + "edit": "Edit", + "storySkeleton": "Story Skeleton", + "adaptationStrategy": "Adaptation Strategy", + "script": "Script", + "noContent": "No content", + "relatedAssets": "Related Assets", + "editScript": "Edit Script", + "save": "Save", + "scriptTitle": "Title", + "titlePlaceholder": "Please enter title", + "content": "Content", + "contentPlaceholder": "Please enter script content", + "selectAssets": "Select Assets", + "noAssets": "No related assets", + "selectAssetsTitle": "Select Related Assets", + "welcomeMsg": "Hello! I am the Toonflow AI Assistant. Would you like me to start generating a script?", + "start": "Start", + "memoryType": { + "message": "Message Memory", + "summary": "Summary Memory", + "all": "All Memory" + }, + "forceGenerate": { + "title": "Force Generate?", + "desc": "Some chapters have not completed event analysis. Do you still want to force generate?", + "confirm": "Force Generate", + "cancel": "Cancel" + }, + "msg": { + "clearConfirm": "Confirm Clear", + "clearBody": "Are you sure you want to clear {type}? This action cannot be undone.", + "confirmClear": "Clear", + "cancel": "Cancel", + "memoryCleared": "{type} cleared", + "scriptUpdated": "Script updated successfully", + "scriptUpdateFailed": "Failed to update script, please try again later", + "searchScriptFailed": "Failed to search scripts", + "updated": "Saved successfully", + "error": "Save failed", + "reconnect": "Reconnect", + "notReconnect": "Confirm that the reconnection conversation will be cut off?", + "keepReconnect": "confirm", + "deleteConfirm": "Delete confirmation", + "deleteBody": "Delete text", + "confirmDelete": "Confirm deletion", + "scriptDeleted": "Script deleted" + }, + "reconnect": "Reconnect", + "deepThink": "Deep Think", + "thinkLevel": { + "off": "No Thinking", + "light": "Light Think", + "deep": "Deep Think", + "extreme": "Extreme Think" + } + }, + "cornerScape": { + "batchSettings": "Batch Gen Settings", + "quickActions": "Quick Actions", + "selectUngenerated": "Select All Ungenerated", + "selectGenerated": "Select All Generated", + "selectFailed": "Select Failed", + "invertSelection": "Invert Selection", + "clearSelection": "Clear Selection", + "batchPreview": "Batch Preview Images", + "assetTypeFilter": "Asset Type Filter", + "genModel": "Generation Model", + "resolution": "Resolution", + "resolutionPh": "Please select a resolution", + "concurrency": "Concurrency", + "concurrencyPh": "Enter concurrency level", + "startBatch": "Start generating images in batches", + "waitingGen": "Waiting...", + "generating": "Generating", + "genFailed": "Generation failed", + "imageError": "Image Error", + "typeRole": "Character", + "typeScene": "Scene", + "typeTool": "Prop", + "typeUnknown": "Unknown", + "descriptionSuffix": "Description: ", + "operateScriptFirst": "Please handle the script first", + "individualConfig": "Individual Config", + "noImage": "No Image", + "promptLabel": "Prompt", + "promptPh": "Please enter prompt", + "aiPolish": "AI Polish", + "regenerate": "Regenerate", + "filterRole": "Character", + "filterScene": "Scene", + "filterTool": "Prop", + "unnamed": "Unnamed", + "noDescription": "No description", + "msg": { + "selectModel": "Please select a generation model", + "selectResolution": "Please select a resolution", + "enterPrompt": "Please enter a prompt", + "enterPromptFirst": "Please enter a prompt first", + "genSuccess": "{name} generated successfully", + "genFailed": "{name} generation failed", + "promptGenSuccess": "Prompt generated successfully", + "polishFailed": "Failed to polish, please try again", + "selectAtLeastOne": "Select at least one asset for batch generation", + "batchStarted": "Batch generation started. Total: {count}, Concurrency: {concurrent}", + "batchItemFailed": "{name} generation failed: {error}", + "batchComplete": "Batch generation completed", + "batchFailed": "Batch generation failed", + "replaceFailed": "Replacement failed", + "replaceSuccess": "Replacement successful", + "promptGenFail": "Prompt word generation failed", + "saveSuccess": "Modification of prompt word successful", + "saveFailed": "Prompt word modification failed" + }, + "history": "historical pictures", + "confirmReplace": "Confirm replacement", + "batchGenerationPrompt": "Generate prompt words in batches", + "generatingPrompt": "Generating", + "selectAll": "Select all", + "selectPromptEmpty": "Select all prompt word is empty", + "noEmptyPrompt": "There are no assets with empty prompt word", + "selectedCount": "{count} assets selected", + "cancelGeneration": "Cancel generation", + "selectGenerating": "Select the item being generated", + "noGenerating": "No data being generated", + "checkNumber": "Select quantity" + }, + "script": { + "searchPlaceholder": "Search script names...", + "search": "Search", + "addScript": "New Script", + "cancelSelectAll": "Deselect All", + "selectAll": "Select All", + "exportScript": "Export Script", + "msg": { + "searchFailed": "Failed to search scripts", + "selectExport": "Please select a script to export", + "exportSuccess": "Export successful", + "exportFailed": "Failed to export script", + "deleteHeader": "Confirm Deletion", + "deleteBody": "Are you sure you want to delete this script? This cannot be undone.", + "deleteConfirm": "Delete", + "cancel": "Cancel", + "deleteSuccess": "Deleted successfully", + "deleteFailed": "Deletion failed", + "selectDelScript": "Please choose to delete the script", + "batchDeleteHeader": "Batch Delete", + "batchDeleteBody": "Are you sure you want to delete the selected {count} scripts? This cannot be undone.", + "batchDeleteSuccess": "Batch deletion successful", + "extractingInProgress": "Extracting", + "projectNotFound": "Item not found", + "selectsExport": "Please choose to export the script" + }, + "add": { + "title": "Add Script", + "scriptName": "Script Name", + "scriptNamePh": "Please enter script name", + "uploadFile": "Upload File", + "dragUpload": "Drag and drop your script file here or click to upload", + "uploadHint": "Supports .txt, .docx. Recommended file size under 10MB", + "scriptContent": "Script Content", + "scriptContentPh": "Please upload or enter script content...", + "relatedAssets": "Related Assets", + "selectAssets": "Select Assets", + "noAssets": "No related assets", + "cancel": "Cancel", + "confirm": "Confirm", + "msg": { + "fileReadFailed": "Failed to read file", + "docNotSupported": ".doc parsing is not supported. Please convert to .txt or .docx", + "unsupportedType": "Unsupported file type", + "fileTooLarge": "File exceeds 10MB. Please upload a smaller file", + "parsing": "Parsing file...", + "parseFailed": "Failed to parse file, please re-upload", + "selectAssetsTitle": "Select Related Assets", + "enterContent": "Please upload or enter script content", + "enterName": "Please enter script name", + "addSuccess": "Script added successfully", + "addFailed": "Failed to add script, please try again later" + } + }, + "edit": { + "title": "Script Details", + "scriptName": "Script Name", + "scriptNamePh": "Please enter script name", + "scriptContent": "Script Content", + "scriptContentPh": "Please enter script content...", + "relatedAssets": "Related Assets", + "selectAssets": "Select Assets", + "noAssets": "No related assets", + "msg": { + "selectAssetsTitle": "Select Related Assets", + "updateSuccess": "Script updated successfully", + "updateFailed": "Failed to update script, please try again later" + } + }, + "deleteScript": "Delete scripts in batches", + "extractAssets": "", + "import": { + "episodeRegexPh": "Customize the script splitting rule, leave it blank to use the default splitting rule (the default is to split according to the Episode X format)" + } + }, + "assets": { + "addPrefix": "Add", + "batchGenerate": "Batch Generate", + "generatePrompt": "Generate Prompt", + "generateImage": "Generate Image", + "batchDelete": "Batch Delete", + "searchPlaceholder": "Search asset names...", + "search": "Search", + "preview": "Preview", + "generate": "Generate", + "edit": "Edit", + "delete": "Delete", + "generating": "Generating", + "play": "Play", + "mediaPreview": "Media Preview", + "confirmBatch": "Confirm {type}!", + "model": "Model", + "resolution": "Resolution", + "resolutionPh": "Please select a resolution", + "batchGenPrompt": "Batch Generate Prompts", + "batchGenImage": "Batch Generate Images", + "role": "Character", + "prop": "Prop", + "scene": "Scene", + "clip": "Clip", + "uploadSuccess": "Upload successful", + "selectAtLeastOne": "Please select at least one asset", + "noDescription": "No description", + "promptGenSuccess": "Prompt for \"{name}\" generated successfully", + "promptGenFail": "Prompt generation for \"{name}\" failed: {error}", + "selectModel": "Please select a model", + "selectResolution": "Please select a resolution", + "noPromptForImage": "\"{name}\" lacks a prompt; cannot generate image", + "imageGenSuccess": "Image for \"{name}\" generated successfully", + "imageGenFail": "Image generation for \"{name}\" failed: {error}", + "confirmDeleteHeader": "Confirm Deletion", + "confirmBatchDeleteBody": "Are you sure you want to batch delete these assets? This cannot be undone.", + "confirmDeleteBody": "Are you sure you want to delete this asset? This cannot be undone.", + "deleteBtn": "Delete", + "cancelBtn": "Cancel", + "deleteSuccess": "Asset deleted successfully", + "deleteFail": "Failed to delete asset", + "colPreview": "Preview", + "colName": "Name", + "colPrompt": "Prompt", + "colDescribe": "Description", + "colRemark": "Remarks", + "colCreateTime": "Created Time", + "colOperation": "Operation", + "add": { + "name": "Name", + "namePh": "Please enter name", + "describe": "Description", + "describePh": "Please enter description", + "remark": "Remarks", + "remarkPh": "Please enter remarks", + "prompt": "Prompt", + "promptPh": "Please enter prompt", + "nameRequired": "Please enter a name", + "describeRequired": "Please enter details", + "remarkRequired": "Please enter remarks", + "updateSuccess": "Asset updated successfully", + "addSuccess": "Asset added successfully" + }, + "gen": { + "header": "Image Generation", + "uploadRef": "Upload Reference Image", + "optional": "Optional", + "promptLabel": "Generation Prompt", + "smartGenerate": "Smart Generation", + "generatingPrompt": "Generating smart prompt...", + "promptPlaceholder": "Describe the image you want to generate. e.g., A futuristic city full of tech, neon lights blinking, cyberpunk style...", + "selectModel": "Select Model", + "selectResolution": "Select Resolution", + "generateBtn": "Generate Image", + "resultTitle": "Results", + "generatedCount": "{count} generated. Please select one", + "generatingLabel": "Generating...", + "genFailed": "Generation failed", + "confirmSelect": "Confirm Selection", + "promptSuccess": "Prompt generated successfully", + "promptFail": "Failed to generate prompt", + "fillPrompt": "Please enter a prompt", + "pickResolution": "Please select a resolution", + "pickModel": "Please select a model", + "unnamed": "Unnamed", + "assetGenSuccess": "Asset generated successfully", + "assetGenFail": "Asset generation failed", + "uploadOk": "Uploaded successfully", + "imageSelected": "Image selected", + "imageDeleted": "Image deleted", + "imageSaved": "Image saved", + "completed": "Completed" + }, + "batch": { + "header": "Batch Generation", + "selected": "Selected {count} items", + "selectAll": "Select All", + "clearSelection": "Clear Selection", + "inputPh": "Please enter content", + "saveSelected": "Save Selected ({count})", + "colPreviewImg": "Preview", + "selectToSave": "Please select items to save", + "saveSuccess": "Saved successfully", + "saveFail": "Failed to save, please try again", + "promptDone": "Prompt generation completed", + "promptFail": "Prompt generation failed", + "missingPrompts": "{count} assets are missing prompts. Please generate prompts first", + "imageDone": "Image generation completed", + "imageGenFail": "Image generation failed", + "unknownError": "Unknown error", + "promptGenCancelled": "Generation canceled" + }, + "confirmCancellation": "Confirm cancellation", + "confirmAgain": "Confirm cancellation? \nAfter cancellation, the backend AI will continue to call for deductions.", + "sure": "Sure" + }, + "production": { + "selectPlaceholder": "Please select an episode", + "edit": "Edit", + "node": { + "script": { + "title": "Script", + "editDialog": "Edit Script" + }, + "scriptPlan": { + "title": "Shooting Plan", + "editDialog": "Edit Shooting Plan" + }, + "storyboard": { + "title": "Storyboard Panel", + "notGenerated": "Not Generated", + "scaleRatio": "Zoom Ratio", + "gridPreview": "Grid Preview", + "noPreviewImages": "No images available to preview", + "imageLoadFailed": "Failed to load image", + "promptPlaceholder": "Please enter the prompt word", + "prompt": "prompt word", + "editInfo": "Prompt word modification", + "selectedCount": "{count} selected", + "clearSelection": "Clear Selection", + "selectAll": "Select All" + }, + "storyboardTable": { + "title": "Storyboard Table", + "editDialog": "Edit Storyboard Table" + }, + "assets": { + "title": "Derived Assets", + "generateFailed": "Generation failed", + "notGenerated": "Not Generated", + "originalAsset": "Original Asset", + "derived": "Derived", + "noDerivedAssets": "No derived assets" + }, + "poster": { + "title": "Video Cover", + "coverCount": "{count} Items" + }, + "workbench": { + "title": "Video Workbench" + } + }, + "editImage": { + "upload": "Upload", + "generate": "Generate", + "saveFailed": "Save failed, please try again", + "fetchFailed": "Failed to fetch data", + "generating": "Generating...", + "deleteNode": "Delete Node", + "ratio": "Ratio", + "quality": "Quality", + "generateBtn": "Generate Image", + "selectImage": "Select Image", + "imageGeneration": "Image Generation", + "promptPlaceholder": "Describe the image you want to generate...", + "imageRef": "Image {index}", + "videoRef": "Video {index}", + "audioRef": "Audio {index}", + "reference": "Ref {index}", + "noReferences": "No reference images available", + "selectModel": "Please select a model first", + "selectQuality": "Please select a quality", + "selectRatio": "Please select a ratio", + "generateFailed": "Generation failed", + "generateFirst": "Please generate an image first", + "generatedResult": "Results", + "waitingGenerate": "Waiting...", + "layoutLR": "Auto Layout - Horizontal", + "layoutTB": "Auto Layout - Vertical", + "uploadAssetImage": "Upload Asset Image", + "uploadStoryboardImage": "Upload Storyboard Image", + "uploadImage": "Asset image upload", + "mode": "model", + "closeConfirmTitle": "Close the editing panel?", + "closeConfirmBody": "Unsaved data will be lost after closing" + }, + "save": "Select", + "cancel": "Cancel", + "chatBox": { + "inputPlaceholder": "Type a message...", + "generateDerivedAssets": "Generate Derived Assets", + "welcomeMessage": "Hello! I am your AI Assistant. How can I help you?", + "adjustModel": "Adjust Model", + "startMakingVideo": "Start Making Video", + "startMakingVideoPrompt": "Please help me start making a video", + "clearMessageMemory": "Clear Message Memory", + "clearSummaryMemory": "Clear Summary Memory", + "clearAllMemory": "Clear All Memory", + "messageMemory": "Message Memory", + "summaryMemory": "Summary Memory", + "allMemory": "All Memory", + "confirmClear": "Clear Memory", + "confirmClearBody": "Are you sure you want to clear {type}?", + "confirmClearBtn": "Confirm", + "memoryCleared": "{type} cleared" + }, + "wb": { + "quickPreview": "Quick Preview", + "videoGeneration": "Storyboard", + "videoEditing": "editing desk", + "hint": "Hint", + "extractLines": "Extract lines from video?", + "no": "No", + "confirm": "Yes", + "extractLinesQuestion": "Do you want to extract dialogue lines from the video as subtitles?", + "importingLoading": "Importing, please wait...", + "mainTrackVideo": "Main Track (Video)", + "subtitle1": "Subtitle 1", + "storyboardVideoName": "{storyboard}-{id}.mp4" + }, + "preview": { + "noImage": "No Image", + "storyboardDesc": "Storyboard Description", + "serialNumber": "No.", + "noDescription": "No Description", + "duration": "Duration", + "seconds": "s", + "relatedAssets": "Related Assets", + "role": "Character", + "prop": "Prop", + "scene": "Scene", + "noCharacters": "No characters appear", + "imagePrompt": "Image Prompt", + "selectAll": "Select All", + "exportImage": "Export Image", + "sceneDescription": "Scene Description", + "promptLabel": "Prompt", + "restoreSort": "Reset Sorting", + "restoreSortConfirm": "Are you sure you want to revert to the original sorting?", + "tip": "Tip", + "selectAtLeastOne": "Please select at least one storyboard to export", + "exportFilename": "Storyboard pictures" + }, + "generate": { + "noVideo": "No Video", + "videoPrompt": "Video Prompt", + "promptPlaceholder": "Enter prompt words to describe the video content you want to generate...", + "refImage": "Reference Image", + "image": "Image", + "refVideo": "Reference Video", + "refImageLabel": "Reference Image", + "refAudio": "Reference Audio", + "muteAudio": "Mute Audio", + "enableAudio": "Enable Audio", + "resolution": "Resolution", + "duration": "Duration", + "generate": "Generate", + "historyVersions": "History Versions", + "confirmSelection": "Confirm Selection", + "noHistory": "No history available", + "generating": "Generating", + "generatingPrompt": "Intelligent generation of prompt words", + "generateFailed": "Generation failed", + "selectAll": "Select All", + "videoTrack": "Video Track", + "batchGenerate": "Batch Generate", + "importToEditor": "Import to Editor", + "modeSingleImage": "Single Image", + "modeMultiImage": "Multi-Image", + "modeGridImage": "Grid Multi-Image", + "modeStartEnd": "Start & End Frames", + "modeText": "Text to Video", + "modeVideoRef": "Video Reference", + "modeImageRef": "Image Reference", + "modeAudioRef": "Audio Reference", + "modeTextRef": "Text Reference", + "startFrame": "Start Frame", + "startFrameOptional": "Start Frame (Optional)", + "endFrame": "End Frame", + "endFrameOptional": "End Frame (Optional)", + "selectRefImage": "Select Reference Image", + "selectRefImages": "Select Reference Images", + "selectEndFrame": "Select End Frame Image", + "selectRefVideoAsset": "Select Reference Video", + "selectRefAudioAsset": "Select Reference Audio", + "selectRefImageAsset": "Select Reference Image", + "selectImageSource": "Select Image Source", + "fromStoryboard": "Storyboard Image", + "fromStoryboardDesc": "Select image from storyboard list", + "fromAssets": "Asset Image", + "fromAssetsDesc": "Select image from asset library", + "confirmDelete": "Confirm Deletion", + "confirmDeleteBody": "Are you sure you want to delete this video? This cannot be undone.", + "delete": "Delete", + "cancel": "Cancel", + "deleteSuccess": "Video deleted successfully", + "deleteFailed": "Deletion failed", + "selectVideoFirst": "Please select a video first", + "confirmSuccess": "Selection confirmed", + "batchSubmitted": "Batch generation request submitted. Processing...", + "configNotFound": "Configuration not found", + "pollingFailed": "Video status query failed, please refresh manually", + "batchGeneratePrompt": "Generate prompt words in batches", + "promptEmpty": "Please enter a video prompt first", + "modelEmpty": "Please select a video generation model first", + "batchPromptEmpty": "Storyboard {name} is available for video prompts. \nPlease create or fill in the prompt first", + "batchModelEmpty": "Storyboard {names} has no model configured. Please select a model first" + }, + "editVideo": { + "reset": "Reset", + "undo": "Undo", + "redo": "Redo", + "split": "Split", + "delete": "Delete", + "rendering": "Rendering...", + "exportVideo": "Export Video", + "exportSuccess": "Video export completed", + "exportFailed": "Export failed", + "sampleSubtitle": "Sample Subtitle Text", + "customText": "Custom Text Content", + "transitionBetweenClips": "Transitions must be added between two adjacent clips", + "transitionExists": "A transition already exists at this location", + "videoPreviewArea": "Video Preview Area", + "clipMaterials": "Clip Materials", + "propertyPanel": "Properties Panel", + "selectClip": "Select a clip to view properties", + "basicInfo": "Basic Info", + "name": "Name", + "clipNamePlaceholder": "Clip Name", + "startTime": "Start", + "endTime": "End", + "totalDuration": "Total Duration", + "videoProperties": "Video Properties", + "opacity": "Opacity", + "volume": "Volume", + "playbackSpeed": "Playback Speed", + "audioProperties": "Audio Properties", + "fadeIn": "Fade In", + "fadeOut": "Fade Out", + "transitionProperties": "Transition Properties", + "transitionType": "Transition Type", + "transFade": "Fade", + "transSlide": "Slide", + "transWipe": "Wipe", + "transDissolve": "Dissolve", + "transZoom": "Zoom", + "transRotate": "Rotate", + "transitionDuration": "Transition Duration", + "subtitleProperties": "Subtitle Properties", + "textContent": "Text Content", + "fontSize": "Font Size", + "copy": "Copy", + "deleteConfirm": "Confirm Deletion", + "deleteClipConfirm": "Are you sure you want to delete this clip?", + "avCanvasNotInit": "AVCanvas not initialized", + "noExportContent": "No content to export", + "exportProject": "Export Project", + "transitionAdded": "Transition added: {name}", + "splitClip": "Split Clip", + "deleteClip": "Delete Clip", + "addClip": "Add {name}", + "duplicateClip": "Duplicate Clip", + "addTransition": "Add Transition", + "updateClip": "Update Clip {key}", + "updatePlaybackRate": "Update Playback Rate to {rate}x", + "updateTransitionDuration": "Update Transition Duration", + "playbackRateRange": "Playback rate must be between 0.1 and 10", + "updatePlaybackRateFailed": "Failed to update playback rate:", + "importProject": "Import Project", + "import": "Import" + }, + "clipType": { + "video": "Video", + "audio": "Audio", + "subtitle": "Subtitle", + "transition": "Transition", + "sticker": "Sticker", + "filter": "Filter", + "effect": "Effect" + }, + "track": { + "video": "Video", + "image": "Image", + "audio": "Audio", + "subtitle": "Subtitle", + "text": "Text", + "sticker": "Sticker", + "filter": "Filter", + "effect": "Effect" + }, + "transition": { + "fade": "Fade", + "slide": "Slide", + "slideLeft": "Slide Left", + "slideRight": "Slide Right", + "slideUp": "Slide Up", + "slideDown": "Slide Down", + "wipe": "Wipe", + "wipeLeft": "Wipe Left", + "wipeRight": "Wipe Right", + "wipeUp": "Wipe Up", + "wipeDown": "Wipe Down", + "dissolve": "Dissolve", + "zoom": "Zoom", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "rotate": "Rotate", + "circle": "Circle", + "diamond": "Diamond", + "clock": "Clock", + "blur": "Blur" + }, + "media": { + "titleText": "Title Text", + "subtitleText": "Subtitle Text", + "customText": "Custom Text", + "media": "Media", + "image": "Image", + "audio": "Audio", + "subtitle": "Subtitle", + "transition": "Transition", + "effect": "Effect", + "filter": "Filter", + "loading": "Loading...", + "subtitlePreview": "Text", + "video": "video" + }, + "effect": { + "fadeIn": "Fade In", + "fadeOut": "Fade Out", + "flash": "Flash", + "shake": "Shake", + "zoomIn": "Zoom In", + "zoomOut": "Zoom Out", + "pulse": "Pulse", + "rotateIn": "Rotate In", + "sticker1": "Sticker 1", + "sticker2": "Sticker 2" + }, + "filter": { + "grayscale": "Grayscale", + "sepia": "Sepia", + "warm": "Warm", + "cool": "Cool", + "vivid": "Vivid", + "bright": "Bright", + "highContrast": "High Contrast", + "blur": "Blur", + "invert": "Invert", + "semiTransparent": "Semi-Transparent" + }, + "guideSwitchEpisode": "Switch Episodes", + "guideSwitchEpisodeBody": "Switching episodes has been moved here!", + "guideRefresh": "Refresh Data", + "guideRefreshBody": "Click the refresh button to reload workspace data", + "guideCanvasNav": "Canvas Navigation", + "guideCanvasNavBody": "Scroll to pan vertically, Ctrl + Scroll to zoom, drag with left mouse button to pan freely", + "guideRightChat": "AI Chat", + "guideRightChatBody": "Chat with AI in the right panel to drive automated workflows", + "guideLayoutBtn": "Auto Layout", + "guideLayoutBtnBody": "Click this button to automatically arrange node layout", + "autoLayoutLR": "Automatic typesetting-left and right layout", + "autoLayoutTB": "Automatic typesetting-top and bottom layout", + "getFlowData": "Refresh workspace", + "confirm": "Switch episode confirmation", + "confirmEpisodesSwitch": "The current task is still in progress. Switching episodes will reconnect the session. Do you want to continue switching?" + }, + "task": { + "title": "Task List", + "subtitle": "Your latest task execution records", + "refresh": "Refresh", + "categoryLabel": "Task Category:", + "stateLabel": "Status:", + "noFailReason": "No failure reason", + "stateAll": "All", + "stateRunning": "Running", + "stateCompleted": "Completed", + "stateFailed": "Failed", + "fetchFailed": "Failed to fetch task list", + "col": { + "taskClass": "Task Category", + "relatedObjects": "Related Objects", + "model": "Model", + "describe": "Description", + "state": "Status", + "startTime": "Time", + "reason": "Reason for failure" + }, + "project": "Project name:" + }, + "noVideo": "No video yet", + "prompt": "Video prompt words", + "generateText": "AI generated prompt words", + "selectStoryboard": "Select storyboard", + "generate": { + "noVideo": "No data yet", + "generateText": "AI generated prompt words", + "selectStoryboard": "Select storyboard", + "generate": "Generate video", + "history": "Historical version", + "generating": "Generating", + "generateFailed": "Check the failure reason", + "selectAll": "Select all", + "selected": "Selected", + "batchGenerateText": "Generate prompt words in batches", + "batchGenerateVideo": "Generate videos in batches", + "importVideo": "Import to editing desk", + "emptyTrack": "Paragraph {index}", + "del": "delete", + "delConfirm": "Are you sure you want to delete this paragraph?", + "selectSource": "Select source", + "confirm": "Select from assets", + "cancel": "Select from storyboard", + "selectVideoFailed": "Video selection failed", + "selectVideoSuccess": "Video selection successful", + "previewVideo": "Video Preview", + "selectTrackFirst": "Please select the storyboard first", + "noSelectedVideo": "No videos selected", + "generateConfirm": "Confirm generation", + "generateConfirmBody": "Confirm to generate video", + "generateVideosInBatches": "Generate videos in batches", + "generateStarted": "Build starts", + "skipDataWithEmptyVideoPromptWords": "The video data that needs to be generated exists and the prompt word is empty.", + "duration": "duration", + "resolution": "resolution", + "delVideo": "Confirm to delete this video?", + "delSuccess": "Delete successfully", + "addReference": "add reference", + "promptPlaceholder": "Please enter the video prompt word", + "downloadVideo": "Download videos in batches", + "selectVideo": "Please check the video you want to download", + "batchDownloadVideo": "Download videos in batches", + "storyboard": "Storyboard", + "assets": "assets", + "promptText": "Generate video prompt word data", + "videoMenu": "Generate video", + "videoPreview": "Video preview", + "referenceImage": "Reference pictures", + "generatePrompt": "Generate prompt words", + "generateVideo": "Generate video" + } + }, + "login": { + "slogan": "Intelligent Short Drama Creation Platform", + "tips": "Default Account: admin / admin123", + "settings": "Server Settings", + "requestAddress": "Request URL", + "username": "Username", + "password": "Password", + "login": "Login", + "usernameRequired": "Please enter username", + "passwordRequired": "Please enter password", + "enterUsernameAndPassword": "Please enter username or password", + "loginSuccess": "Login successful", + "settingsSaved": "Settings saved" + }, + "components.storyboardImageCheck.camera": "Camera", + "components.storyboardImageCheck.dialogTitle": "Select Storyboard Image", + "components.storyboardImageCheck.preview": "Preview", + "components.storyboardImageCheck.src": "Preview Image", + "components.storyboardImageCheck.title": "Title", + "components.storyboardImageCheck.duration": "Duration", + "components.storyboardImageCheck.lines": "Lines", + "components.storyboardImageCheck.createTime": "Created Time", + "workbench.script.extractAssets": "Extract Assets", + "promptManage": { + "prompt": "prompt word" + }, + "hello": { + "welcomeTitle": "Welcome to ToonFlow", + "welcomeDesc": "AI-driven comic creation workflow platform, let us spend a minute to complete the initial configuration.", + "startConfig": "Start configuration", + "skip": "Skip boot", + "configModel": "Add model service", + "configData": "Configure Agent", + "startUse": "Get started", + "configModelTitle": "Add model service provider", + "configModelDesc": "First, you need to add at least one AI model service provider (such as OpenAI, Claude, etc.) in the settings and fill in the corresponding API Key.", + "configModelTip": "Clicking the button below will open the \"Model Service\" tab of the settings page. After adding the supplier, return here to continue.", + "configModelBtn": "Go to configure model service", + "configAgentTitle": "Assign Agent Model", + "configAgentDesc": "Next, assign models to each functional module in the Agent configuration so that the system knows which model to call to complete the task.", + "configAgentTip": "Clicking the button below will open the \"Agent Configuration\" tab of the settings page. Return here after assigning models to each function.", + "configAgentBtn": "Go to configure Agent", + "finishTitle": "🎉 Everything is ready!", + "finishDesc": "The configuration is complete and now you can start using all the features. \nIf you need to adjust it, you can modify it in the settings at any time.", + "qrcodeLabel": "Join the WeChat communication group to get more help:", + "githubLabel": "If you find it useful, please give us a ⭐ Star!", + "prevStep": "Previous step", + "nextStep": "Next step", + "finish": "Get started" + }, + "setting": { + "skillManagement": { + "search": "Search file name", + "empty": "No matching files", + "edit": "edit", + "selectOnTheLeft": "Please select a file from the left" + } + }, + "storyboard": { + "assets": { + "notExist": "Asset does not exist", + "notDerivativeExist": "Derivative assets do not exist", + "derivativeUpdateSuccess": "Update successful", + "derivativeState": "Not generated", + "derivativeAddSuccess": "Added successfully", + "derivativeDelSuccess": "Delete successfully", + "notGenerated": "Not generated" + }, + "addSuccess": "Update successful", + "state": { + "unused": "Not generated" + }, + "saveSuccess": "Added successfully" + }, + "productionAgent": { + "generating": "Generating" + }, + "skillScan": { + "scanning": "🔍 Parsing and loading Skill", + "scanComplete": "✨ Skill Scan Complete", + "inserted": "✅ Added {count} Skills", + "updated": "🔄 {count} Skill(s) updated", + "removed": "🗑️ {count} Skill(s) removed", + "scannedFiles": "📁 Scanned {count} files", + "noDescription": "📝 {count} Skill(s) missing description", + "noAttribution": "👤 {count} Skill(s) missing attribution", + "configWarning": "⚠️ Skill Config Warning", + "openSettings": "Open Settings", + "scanFailed": "❌ Scan Failed", + "checkNetwork": "🔌 Please check network connection or try again later", + "retryLater": "🔁 Please try again later" + }, + "generate": "Generate video", + "history": "Historical version", + "generating": "Generating", + "generateFailed": "fail", + "selectAll": "Select all", + "selected": "Selected", + "importVideo": "Import video", + "emptyTrack": "Paragraph {index}", + "del": "Confirm deletion", + "delConfirm": "Are you sure you want to delete this paragraph?", + "selectSource": "Select source", + "confirm": "Select from assets", + "cancel": "Select from storyboard", + "workbench.script.msg.selectExport": "", + "workbench.script.msg.exportFailed": "Export failed", + "workbench.production.node.assets.confirmDeleteBody": "Confirm whether to delete the asset", + "workbench.production.node.assets.removeFailed": "Failed to delete asset", + "version": { + "newVersion": "There is a new version, do you want to update it?" + }, + "workbench.production.generatedNode.localUpload": "Local upload", + "workbench.production.editImage.uploadFailed": "Image upload failed", + "workbench.production.editImage.noImage": "Please add pictures first", + "workbench.script.batchAddScript": "Upload scripts in batches", + "workbench.script.import.pasteLabel": "Paste the script content directly", + "workbench.script.import.col.scriptName": "Script name", + "workbench.script.import.col.scriptData": "Script content", + "workbench.script.import.episodeRegex": "Script splitting rules", + "workbench.script.import.episodeRegexPh": "Customize the script splitting rule, leave it blank to use the default splitting rule (the default is to split according to the Episode X format)", + "workbench.script.import.regexInvalid": "Invalid regular expression", + "workbench.script.import.parsedChapters": "{count} set parsed", + "workbench.script.import.msg.selectChapters": "Please check the script first", + "workbench.script.import.msg.saveSuccess": "Script saved successfully", + "workbench.script.import.batchTitle": "Upload scripts in batches", + "workbench.assets.sex": "gender", + "workbench.assets.audioText": "audio content", + "workbench.assets.audio": "Audio", + "workbench.assets.add.sex": "gender", + "workbench.assets.add.sexPh": "Please enter gender", + "settings.agent.advanced": "Advanced configuration", + "settings.agent.ordinary": "Easy configuration", + "settings.agent.temperature": "temperature", + "settings.agent.maxOutputTokens": "Maximum output token", + "settings.agent.auto": "Auto", + "settings.agent.manual": "Manual", + "settings.agent.autoHint": "Output length decided by the model", + "settings.agent.msg.notmodel": "No model selected", + "workbench.production.node.storyboard.generateImage": "Generate storyboards", + "workbench.generate.notSelectMode": "Please select a model first", + "workbench.production.node.storyboard.deleteSuccess": "Storyboard deleted successfully", + "workbench.production.node.storyboard.pleaseSelectImage": "Please select the storyboard first", + "workbench.cornerScape.audioState": "Binding", + "workbench.generate.generateError": "Failed to initiate build request", + "settings.vendor.videoGenerating": "Video generation is slow, please wait patiently", + "settings.memory.modelMap.editRefeshWord": "rebind", + "settings.memory.modelMap.delPrompt": "delete", + "settings.vendor.testModel": "test" +} diff --git a/web-core/src/locales/language/ja_JP.json b/web-core/src/locales/language/ja_JP.json new file mode 100644 index 0000000..94ee497 --- /dev/null +++ b/web-core/src/locales/language/ja_JP.json @@ -0,0 +1,1548 @@ +{ + "components": { + "editMdPreivew": { + "title": "編集", + "confirm": "保存", + "cancel": "キャンセル" + }, + "imageTools": { + "copy": "画像をコピー", + "preview": "プレビュー", + "download": "ダウンロード", + "msg": { + "imageLoadFailed": "画像の読み込みに失敗しました", + "convertFailed": "変換に失敗しました", + "copied": "クリップボードにコピーしました", + "copyFailed": "コピーに失敗しました", + "downloadFailed": "ダウンロードに失敗しました", + "downloadStarted": "ダウンロードを開始します", + "downloadBlockedOpenNewWindow": "現在の画像ソースでダウンロードが制限されている可能性があるため、新しいウィンドウで開きました" + } + }, + "migrateShow": { + "title": "データの移行", + "desc": "旧バージョンのデータが検出されました。データを移行しますか?", + "hide": "今後表示しない", + "confirm": "確定", + "msg": { + "migrateSuccess": "データの移行に成功しました", + "migrateFailed": "データの移行に失敗しました" + } + }, + "modelSelect": { + "placeholder": "モデルを選択してください", + "type": { + "image": "画像", + "text": "テキスト", + "video": "動画" + }, + "msg": { + "fetchModelFailed": "モデルデータの取得に失敗しました:" + }, + "goSetting": "設定に移動してモデルを追加します" + } + }, + "settings": { + "title": "ToonFlow 設定", + "menu": { + "language": "言語設定", + "vendorConfig": "モデルサービス", + "agentConfig": "Agent 設定", + "promptManage": "プロンプト管理", + "skillManagement": "スキル管理", + "memoryConfig": "Agent メモリ設定", + "loginConfig": "ログイン設定", + "dbConfig": "データベース操作", + "fileManagement": "ファイル管理", + "otherConfig": "その他の設定", + "requestConfig": "リクエストURL", + "about": "アップデートの確認", + "logoutConfig": "ログアウト", + "skillsSkillsManagement": "スキルスキル管理" + }, + "language": { + "desc": "インターフェースの表示言語を選択します", + "msg": { + "saved": "言語設定を保存しました" + } + }, + "vendor": { + "addVendor": "プロバイダーを追加", + "noVendor": "プロバイダーがありません。先に追加してください", + "required": "必須", + "optionalSection": "任意項目", + "modelSettings": "モデル設定", + "addManually": "手動追加", + "test": "テスト", + "edit": "編集", + "delete": "削除", + "deleteVendor": "プロバイダーを削除", + "editCode": "コードを編集", + "updateConfig": "設定を更新", + "addModel": "モデルを追加", + "editModel": "モデルを編集", + "displayName": "表示名", + "displayNamePlaceholder": "例:GPT-4o", + "modelId": "モデルID", + "modelIdPlaceholder": "例:gpt-4o", + "modelType": "モデルタイプ", + "multimodal": "マルチモーダル", + "supported": "対応", + "notSupported": "非対応", + "toolCall": "ツール呼び出し", + "imageMode": "画像モード", + "videoMode": "動画モード", + "audioOutput": "音声出力", + "durationResolution": "デュレーション/解像度マッピング", + "durationSec": "デュレーション(秒)", + "resolution": "解像度", + "enterAndPress": "入力してEnterを押す", + "addDurationResolution": "デュレーションと解像度を追加", + "testResult": "テスト結果", + "generating": "生成中...", + "addVendorDialog": "プロバイダーの追加", + "codeEditorInfo": "プロバイダー情報の設定にはTypeScriptコードを記述してください", + "reset": "リセット", + "importFile": "ファイルをインポート", + "textModel": "テキストモデル", + "imageModel": "画像モデル", + "videoModel": "動画モデル", + "textToImage": "テキストから画像生成", + "textToVideo": "テキストから動画生成", + "singleImage": "単一画像", + "multiImage": "複数画像モード", + "multiReference": "複数画像参照", + "multiReferenceMode": "マルチ参照モード", + "gridImage": "グリッド単一画像", + "startEndRequired": "開始・終了フレーム(両方必須)", + "endFrameOptional": "開始・終了フレーム(終了フレームは任意)", + "startFrameOptional": "開始・終了フレーム(開始フレームは任意)", + "textRef": "テキスト", + "imageRef": "画像", + "videoRef": "動画", + "audioRef": "音声", + "audioOptional": "任意", + "audioOnly": "音声付き動画のみ出力", + "noAudio": "無音動画のみ出力", + "msg": { + "getVendorListFailed": "プロバイダーリストの取得に失敗しました", + "vendorConfigUpdated": "プロバイダー設定を更新しました", + "updateFailed": "更新に失敗しました", + "highRiskConfirm": "⚠️ 高リスク操作の確認", + "addVendorRiskBody": "新しいAIプロバイダーを追加すると、システムAPIへのアクセス権が付与されます。プロバイダーのコードソースが信頼できるか確認してください!", + "iKnowRisk": "リスクを承知しました", + "cancel": "キャンセル", + "confirmAgain": "⚠️ 再確認", + "addVendorConfirmBody": "本当にこのプロバイダーを追加しますか?追加後、システムのモデルスケジューリングに組み込まれます。", + "confirmAndAdd": "確認して追加", + "goBackCheck": "戻って確認", + "vendorAdded": "プロバイダーが正常に追加されました", + "addFailed": "追加に失敗しました", + "updateVendorRiskBody": "AIプロバイダー設定を更新すると、システムAPIへのアクセス権と動作が変更されます。変更後のコードソースが信頼できるか確認してください!", + "updateVendorConfirmBody": "本当にこのプロバイダー設定を更新しますか?更新後、システムのモデルスケジューリングに影響します。", + "confirmAndUpdate": "確認して更新", + "updateSuccess": "プロバイダー設定の更新に成功しました", + "fillDisplayName": "表示名を入力してください", + "fillModelId": "モデルIDを入力してください", + "selectImageMode": "画像モードを選択してください", + "selectVideoMode": "動画モードを選択してください", + "groupPrefix": "第 {n} グループ:", + "addDuration": "デュレーションを追加してください", + "addResolution": "解像度を追加してください", + "selectVendorFirst": "先にプロバイダーを選択してください", + "modelIdExists": "モデルIDが既に存在します", + "modelAdded": "モデルが正常に追加されました", + "modelUpdated": "モデルが正常に更新されました", + "enterApiKey": "API KEY を入力してください", + "enterApiUrl": "API URL を入力してください", + "testSuccess": "テスト成功", + "imageGenSuccess": "画像の生成に成功しました", + "videoGenSuccess": "動画の生成に成功しました", + "requestFailed": "リクエストに失敗しました", + "deleteModelConfirm": "モデルの削除確認", + "deleteModelBody": "削除後は元に戻せません。続行しますか?", + "confirmDelete": "削除を確認", + "modelDeleted": "モデルを削除しました", + "deleteVendorConfirm": "プロバイダーの削除確認", + "deleteVendorBody": "削除すると、このプロバイダーのすべてのモデルも一緒に削除されます。続行しますか?", + "vendorDeleted": "プロバイダーを削除しました", + "deleteFailed": "削除に失敗しました", + "enabled": "有効", + "disabled": "無効", + "linkAddVendorRiskBody": "新しい AI ベンダーを追加すると、システム API にアクセスできるようになります。ベンダーのリンク ソースが信頼できることを確認してください。", + "importAdd": "新しい AI ベンダーを追加すると、システム API にアクセスできるようになります。ベンダーのドキュメント ソースが信頼できることを確認してください。", + "linkAddFailed": "リンクの追加に失敗しました" + }, + "think": "深い考え", + "code": "コード", + "linkAddPlaceholder": "追加するリンクを入力してください", + "noFileSelected": "ファイルは正常にインポートされました", + "linkAdd": "確認する" + }, + "agent": { + "bannerDesc": "Toonflow公式中継サイトを使用すると、設定をワンクリックで入力でき、手動設定なしですぐに使用できます。", + "visitWebsite": "サイトへ移動", + "fillKey": "KEYを入力", + "oneClickFill": "ワンクリック入力", + "notOpen": "未公開", + "notConfigured": "未設定", + "modelConfig": "モデル設定", + "confirm": "確定", + "cancel": "キャンセル", + "selectModel": "モデルを選択", + "fillKeyHeader": "Toonflowプラットフォームの公式KEYを入力", + "keyPlaceholder": "KEYを入力してください", + "save": "保存", + "msg": { + "notAvailable": "この機能は現在準備中です。ご期待ください", + "configSuccess": "設定に成功しました", + "updateConfigFailed": "設定の更新に失敗しました:", + "keyValid": "KEYが有効です。Toonflowプラットフォームに接続しました", + "keyInvalid": "KEYが無効です。確認して再入力してください:", + "enterKey": "KEYを入力してください", + "saveFailed": "保存に失敗しました:", + "getAgentListFailed": "Agent設定リストの取得に失敗しました:", + "toonflowNotFound": "トゥーンフロー公式乗換駅は存在しない" + }, + "temperature": "温度" + }, + "memory": { + "warning": "以下の設定項目には推奨値がプリセットされています。各設定の意味と影響を明確に理解していない限り、現在の設定を維持することをお勧めします。", + "vectorModelConfig": "ベクトルモデル設定", + "modelFilePath": "モデルファイルパス", + "quantizationType": "量子化タイプ", + "quantizationPlaceholder": "量子化タイプを入力してください", + "memoryParams": "メモリパラメータ", + "messagesPerSummary": "メッセージ圧縮トリガー件数", + "messagesPerSummaryHelp": "直近 N 件の会話コンテキストを保持します。", + "shortTermLimit": "単発取得の未圧縮メッセージ数", + "shortTermLimitHelp": "検索時に返される候補メモリの件数です。", + "summaryMaxLength": "要約最大文字数", + "summaryMaxLengthHelp": "メッセージ圧縮時に許容される最大文字数です。", + "summaryLimit": "検索可能な圧縮メッセージ数", + "summaryLimitHelp": "検索可能な圧縮メッセージの件数です。", + "ragLimit": "検索メモリ件数", + "ragLimitHelp": "検索時に取得するメッセージ数です。", + "deepRetrieveSummaryLimit": "ベクトル検索の圧縮メッセージ数", + "deepRetrieveSummaryLimitHelp": "圧縮メッセージ内容を検索する際に取得するメッセージ数です。", + "saveConfig": "設定を保存", + "clearMemory": "メモリをクリア", + "restoreDefault": "デフォルトに戻す", + "msg": { + "saved": "メモリ設定を保存しました", + "clearConfirmTitle": "メモリのクリア確認", + "clearConfirmBody": "AIのグローバルメモリデータがクリアされ、元に戻せなくなります。続行しますか?", + "confirmClear": "クリアを確認", + "cancel": "キャンセル", + "cleared": "メモリをクリアしました", + "clearFailed": "メモリのクリアに失敗しました" + }, + "modelMap": { + "name": "機種名", + "model": "モデル", + "type": "タイプ", + "editWord": "プロンプトワードをバインドする", + "operation": "操作する", + "bindingSuccessful": "バインド成功", + "bindingFailed": "バインドに失敗しました", + "currentBinding": "現在のバインディング", + "noBinding": "束縛されていない", + "bound": "バウンド", + "unbind": "バインドを解除する", + "filenName": "ファイル名" + } + }, + "login": { + "username": "ユーザー名", + "usernamePlaceholder": "ユーザー名を入力してください", + "password": "パスワード", + "passwordPlaceholder": "パスワードを入力してください", + "modify": "変更", + "msg": { + "enterUsername": "ユーザー名を入力してください", + "usernameLength": "ユーザー名は2〜20文字である必要があります", + "enterPassword": "パスワードを入力してください", + "passwordLength": "パスワードは6〜20文字である必要があります", + "fetchFailed": "ユーザー情報の取得に失敗しました", + "saveSuccess": "保存に成功しました", + "saveFailed": "保存に失敗しました" + } + }, + "db": { + "clearDb": "データベースをクリア", + "clearDbDesc": "データ構造を保持し、全テーブルのデータをクリアします", + "clearData": "データをクリア", + "confirmAction": "操作の確認", + "dbInfo": "データベース概要", + "dbInfoDesc": "全テーブル名と行数を表示します", + "viewInfo": "情報を見る", + "tableName": "テーブル名", + "rowCount": "行数", + "totalTables": "合計 {count} テーブル", + "exportDb": "データベースのエクスポート", + "exportDbDesc": "全テーブルデータをJSONバックアップファイルとしてエクスポートします", + "exportData": "データをエクスポート", + "importDb": "データベースのインポート", + "importDbDesc": "JSONバックアップファイルからデータを復元します(現在のデータは上書きされます)", + "importData": "データをインポート", + "clearTable": "指定テーブルをクリア", + "clearTableDesc": "テーブルを選択してデータをクリアします", + "clearTableBtn": "テーブルをクリア", + "selectTable": "テーブルを選択", + "msg": { + "clearDbTitle": "データベースのクリア", + "firstConfirm": "本当にすべてのテーブルをクリアしますか?クリアしたデータは復元できません!", + "secondConfirm": "これが最後の確認です。クリアするとすべてのデータが永久に失われます!", + "keyword": "クリア", + "confirm": "確定", + "pleaseInput": "入力してください", + "cleared": "すべてのテーブルをクリアしました", + "operationFailed": "操作に失敗しました。再試行してください", + "cancelled": "操作がキャンセルされました", + "exportSuccess": "データベースのエクスポートに成功しました", + "exportFailed": "エクスポートに失敗しました", + "importSuccess": "データベースのインポートに成功しました。ログインページに移動します", + "importFailed": "インポートに失敗しました", + "invalidFile": "無効なバックアップファイル", + "clearTableSuccess": "テーブルをクリアしました", + "clearTableFailed": "テーブルのクリアに失敗しました", + "clearTableConfirm": "テーブル {name} をクリアしますか?この操作は元に戻せません!", + "importConfirm": "インポートすると現在のデータがすべて上書きされます。続行しますか?", + "importSecondConfirm": "最終確認:インポート後、現在のデータはすべて置き換えられます!", + "noTableSelected": "テーブルを選択してください", + "loadingDbInfo": "データベース情報を読み込み中...", + "loadDbInfoFailed": "データベース情報の取得に失敗しました" + } + }, + "other": { + "requestTimeout": "リクエストタイムアウト", + "seconds": "秒", + "inputSeconds": "秒を入力してください", + "assetConcurrency": "アセット生成の同時実行数", + "count": "個", + "inputCount": "個数を入力してください", + "chapterRegex": "章分割の正規表現", + "restoreDefault": "デフォルトに戻す", + "regexPlaceholder": "正規表現を入力してください", + "showTitleBar": "タイトルバーを表示する", + "isElectron": "デスクトップモードに切り替える", + "canvasScroll": "キャンバススクロール", + "canvasIsDisabled": "キャンバスのズーム", + "agentCanvasScalingMethod": "制作ページでの無制限のキャンバス ホイール操作", + "zoom": "ズーム", + "scroll": "スクロール", + "isInteracting": "制作ページでの無制限のキャンバス ドラッグ パフォーマンスの最適化", + "closeIsInteracting": "閉鎖" + }, + "request": { + "warning": "特別な理由がない限り、変更や設定は不要です", + "apiAddress": "API アドレス", + "apiPlaceholder": "API リクエストアドレスを入力してください", + "save": "保存", + "reset": "リセット", + "msg": { + "enterApi": "API アドレスを入力してください", + "validUrl": "有効な HTTP/HTTPS アドレスを入力してください", + "saved": "リクエストアドレスを保存しました", + "reset": "デフォルトアドレスにリセットしました", + "refreshFailed": "更新に失敗しました" + }, + "refresh": "リフレッシュする" + }, + "about": { + "slogan": "オープンソースのAI主導コミック/絵コンテ制作ツール", + "latestVersion": "現在は最新バージョンです", + "checkUpdate": "アップデートの確認", + "codeRepository": "コードリポジトリ", + "githubRepo": "GitHub リポジトリ", + "giteeRepo": "Gitee リポジトリ", + "versionUpdate": "バージョンアップデート", + "checkUpdateGithub": "アップデート確認 (GitHub)", + "getFromGithub": "GitHub Releaseから最新版を取得", + "checkUpdateGitee": "アップデート確認 (Gitee)", + "getFromGitee": "Gitee Releaseから最新版を取得", + "license": "ライセンス", + "licenseDesc": "オープンソースライセンス・クリックして詳細を確認", + "updateAvailable": "新しいバージョンが見つかりました", + "upToDate": "新しいバージョンが検出されました", + "confirmReinstall": "リンクをコピー", + "reinstallRequired": "ブラウザが自動的に開いてダウンロードされます。開かない場合は手動で開いてください。" + }, + "logout": { + "warning": "ログアウト後、システムを継続して使用するには再ログインが必要です。", + "confirmLogout": "本当にログアウトしますか?", + "logout": "ログアウト", + "msg": { + "logoutSuccess": "ログアウトに成功しました", + "logoutFailed": "ログアウトに失敗しました。再試行してください" + } + }, + "file": { + "quickOpen": "ディレクトリをすばやく開く", + "open": "開く", + "dockerDesc": "Docker / フロント・バックエンド分離デプロイの場合は、「/data/*」ディレクトリに移動して手動でファイルを管理してください。", + "desktopOnly": "この機能はデスクトップ版のみ対応しています", + "folders": { + "data": "data", + "dataDesc": "データディレクトリ。", + "logs": "data/logs", + "logsDesc": "実行ログとエラーログ。", + "oss": "data/oss", + "ossDesc": "ファイルストレージ関連リソース。", + "skills": "data/skills", + "skillsDesc": "スキルとプロンプト設定ファイル。", + "models": "data/models", + "modelsDesc": "モデルファイルと設定。", + "web": "data/web", + "webDesc": "Web関連リソース(フロントエンドビルド成果物など)。", + "serve": "data/serve", + "serveDesc": "バックエンドサービス関連ファイル。" + }, + "openFailed": "フォルダを開けませんでした" + }, + "skill": { + "scanSkills": "スキャンスキル", + "fileLost": "ファイルがありません" + }, + "dev": { + "warning": "以下は開発者ツールです。注意して操作してください。", + "openDevtool": "開ける", + "devtoolsDoc": "書類のアドレス", + "devtoolsDesc": "オンにすると、Toonflow のインストール ディレクトリに .devtools フォルダーが作成されます。 Toonflow に書き込み権限があることを確認してください (管理者として実行)。", + "devtoolsDesc2": "このディレクトリで npx {'@'}ai-sdk/devtools を実行して、テレメトリのデバッグを有効にします", + "openDevtoolFailed": "開発者ツールを開けませんでした。Toonflow デスクトップがインストールされていることを確認してください", + "notInElectron": "WEB環境の場合はブラウザコンソールを手動で開いてください" + } + }, + "workbench": { + "selectProject": "プロジェクトを選択してください", + "menu": { + "myProject": "マイプロジェクト", + "taskCenter": "タスクセンター", + "novel": "小説の原文", + "scriptAgent": "シナリオ Agent", + "scriptManage": "シナリオ管理", + "cornerScape": "キャラ・背景制作", + "production": "動画制作", + "assetCenter": "アセットセンター", + "settings": "設定", + "jumpGithub": "Githubにジャンプ", + "feedbackQuestions": "フィードバックの質問" + }, + "project": { + "title": "マイプロジェクト", + "subtitle": "すべてのショートドラマプロジェクトを管理します", + "newProject": "新規プロジェクト", + "dialog": { + "editTitle": "プロジェクトの編集", + "addTitle": "新規プロジェクト", + "save": "保存", + "ok": "確定", + "cancel": "キャンセル", + "projectType": "プロジェクトタイプ", + "selectType": "プロジェクトタイプを選択", + "basedOnNovel": "小説の原文に基づく", + "projectName": "プロジェクト名", + "projectNamePh": "プロジェクト名を入力してください", + "novelType": "小説のジャンル", + "novelTypePh": "例:ファンタジー、SF、恋愛", + "artStyle": "ビジュアルマニュアル", + "selected": "選択済み:", + "selectArtStyle": "ビジュアルマニュアルを選択してください", + "newArtStyle": "新しいビジュアルマニュアル", + "loading": "読み込み中...", + "videoRatio": "画面アスペクト比", + "novelIntro": "小説のあらすじ", + "novelIntroPh": "あらすじを入力してください", + "editArtStyleTitle": "ビジュアルマニュアルの編集", + "newArtStyleTitle": "新しいビジュアルマニュアル", + "artStyleName": "ビジュアルマニュアル名", + "artStyleNamePh": "ビジュアルマニュアル名を入力してください", + "artStyleImage": "ビジュアルマニュアルカバー", + "remove": "削除", + "uploadCover": "カバーをアップロード", + "artStylePrompt": "ビジュアルマニュアルのプロンプトワード", + "aiExtract": "AI プロンプト抽出", + "promptPlaceholder": "画像生成時にビジュアルマニュアルを指定するために使用されるビジュアルマニュアルプロンプトワードについて説明します。", + "visualManual": "ビジュアルマニュアル", + "newVisualManual": "新しいビジュアルマニュアル", + "editVisualManualTitle": "ビジュアルマニュアルの編集", + "newVisualManualTitle": "新しいビジュアルマニュアル", + "visualManualName": "ビジュアルマニュアル名", + "visualManualNamePh": "ビジュアルマニュアル名を入力してください", + "visualManualCover": "ビジュアルマニュアルカバー", + "visualManualPrompt": "ビジュアルマニュアルのプロンプト", + "modelData": "画像モデルの選択", + "videoModelData": "ビデオモデルを選択してください", + "prompt": { + "placeholder": "プロンプトの単語を入力してください", + "saveSuccess": "更新に成功しました", + "title": "即効性のある言葉" + }, + "basedOnScript": "脚本に基づいて", + "mdFile": "ビジュアルマニュアルファイル", + "directorManual": "ディレクターズハンドブック", + "addDirectorManual": "新しいディレクターマニュアル", + "editingDirectorManual": "ディレクターズマニュアルを編集する", + "newDirecorManualTitle": "新しいディレクターマニュアル", + "directorManualPrompt": "ディレクターズマニュアル プロンプトワード", + "directorManualName": "ディレクターズマニュアル名", + "directorManualNamePh": "ディレクターズマニュアル名を入力してください", + "directorFile": "ディレクターズマニュアル文書", + "directorManualCover": "ディレクターズマニュアルの表紙" + }, + "msg": { + "fetchFailed": "プロジェクトリストの取得に失敗しました", + "notFound": "プロジェクトが見つかりません!", + "editSuccess": "プロジェクトを編集しました", + "editFailed": "プロジェクトの編集に失敗しました", + "addSuccess": "プロジェクトを新規作成しました", + "addFailed": "プロジェクトの作成に失敗しました", + "deleteHeader": "プロジェクトの削除", + "deleteBody": "本当にこのプロジェクトを削除しますか?", + "deleteConfirm": "削除", + "deleteCancel": "キャンセル", + "deleteSuccess": "プロジェクトを削除しました", + "deleteFailed": "プロジェクトの削除に失敗しました", + "extractSuccess": "プロンプトの抽出に成功しました", + "extractFailed": "抽出に失敗しました", + "enterArtStyleName": "ビジュアルマニュアル名を入力してください", + "artStyleUpdated": "ビジュアルマニュアルを更新しました", + "artStyleAdded": "ビジュアルマニュアルを追加しました", + "operationFailed": "操作に失敗しました", + "enterVisualManualName": "ビジュアルマニュアル名を入力してください", + "enterVisualManualImage": "ビジュアルマニュアルのカバー画像をアップロードしてください", + "enterVisualManualTabData": "プロンプトは空にできません", + "visualManualUpdated": "ビジュアルマニュアルを更新しました", + "visualManualAdded": "ビジュアルマニュアルを追加しました", + "deleteVisualManualHeader": "ビジュアルマニュアルを削除", + "deleteVisualManualBody": "ビジュアルマニュアル「{name}」を削除してよろしいですか?", + "deleteVisualManualConfirm": "削除", + "deleteVisualManualCancel": "キャンセル", + "enterProjectName": "プロジェクト名を入力してください", + "enterProjectIntro": "小説の紹介文を入力してください", + "enterProjectType": "プロジェクトのタイプを入力してください", + "enterArtStyle": "プロジェクトのビジュアルパンフレットを選択してください", + "enterVideoRatio": "ビデオ比率を選択してください", + "enterImageModel": "画像モデルを選択してください", + "enterVideoModel": "ビデオモデルを選択してください", + "visualManualDeleted": "正常に削除されました", + "selectMode": "モードを選択してください", + "deleteDirectorManualHeader": "ディレクターズマニュアルの削除", + "deleteDirectorManualBody": "ディレクターズマニュアル「{名前}」を削除してもよろしいですか?", + "directorManualUpdated": "ディレクターズマニュアルを更新しました", + "directorManualAdded": "ディレクターズマニュアルを追加しました", + "directorManual": "プロジェクトディレクターズマニュアルを選択してください", + "modelProviderDisabled": "ビデオ モデルまたは画像モデルのサプライヤーが有効になっていない、またはモデル サプライヤーがありません。最初に設定してください。" + }, + "type": { + "novel": "原作小説に基づいて", + "script": "小説の脚本に基づく" + } + }, + "novel": { + "importText": "原文をインポート", + "batchDelete": "一括削除", + "eventAnalysis": "イベント分析", + "searchPlaceholder": "原文の名前を検索...", + "search": "検索", + "generating": "生成中...", + "genFailed": "生成失敗", + "none": "なし", + "edit": "編集", + "delete": "削除", + "col": { + "id": "No.", + "reel": "巻", + "chapter": "章名", + "chapterData": "章の内容", + "event": "イベント", + "operation": "操作" + }, + "msg": { + "batchDeleteHeader": "一括削除", + "batchDeleteBody": "選択した {count} 件のデータを削除してもよろしいですか?", + "batchDeleteSuccess": "一括削除に成功しました", + "deleteHeader": "削除の確認", + "deleteBody": "章名「{name}」のデータを削除してもよろしいですか?", + "deleteSuccess": "削除に成功しました", + "eventAnalysisHeader": "イベント分析", + "eventAnalysisBody": "選択した {count} 件のデータのイベント分析を実行してもよろしいですか?" + }, + "import": { + "title": "小説の原文をアップロード", + "step1": "ステップ 1", + "step2": "ステップ 2", + "step3": "ステップ 3", + "dragUpload": "ここに小説ファイルをドラッグ&ドロップするか、クリックしてアップロード", + "uploadHint": "対応形式: .txt, .docx。ファイルサイズは10MB以下を推奨します", + "or": "または", + "pasteLabel": "小説の原文を直接貼り付け", + "pastePlaceholder": "小説の原文を入力してください", + "chars": "文字", + "tooShort": "内容が短すぎます。100文字以上を推奨します", + "parsedChapters": "{count} 章を解析しました", + "nextStep": "次へ", + "prevStep": "戻る", + "selectedInfo": "選択済み:{count} 文字 (200,000文字以内)", + "eventAnalysis": "イベント分析", + "saveAndAnalyze": "原文を保存してイベントを分析", + "col": { + "chapter": "章", + "reel": "巻", + "chapterName": "章名", + "chapterData": "章の内容" + }, + "msg": { + "parseFailed": "ファイルの解析に失敗しました。再アップロードしてください", + "selectFile": "ファイルを選択", + "docNotSupported": ".doc ファイルは解析をサポートしていません。.ts ファイルに変換してください。", + "unsupportedType": "未対応のファイル形式です", + "fileTooLarge": "ファイルサイズが10MBを超えています。より小さなファイルをアップロードしてください", + "selectChapters": "先に章を選択してください", + "saveSuccess": "小説の原文を保存しました" + }, + "importAdd": "ここにファイルをドラッグ アンド ドロップするか、クリックしてアップロードします", + "limit": ".ts形式をサポート" + }, + "editDialog": { + "title": "小説の原文を編集", + "chapterName": "章名", + "chapterNamePh": "章名を入力してください", + "eventContent": "イベント内容", + "eventContentPh": "イベント内容を入力してください", + "chapterContent": "章の内容", + "chapterContentPh": "章の内容を入力してください", + "cancel": "キャンセル", + "save": "保存", + "msg": { + "updateSuccess": "小説の原文を更新しました" + } + }, + "event": { + "regenerate": "イベントを再生成", + "batchDelete": "一括削除", + "noData": "イベントデータがありません。生成を開始してください", + "generate": "イベントを生成", + "generatingHint": "イベント生成中。しばらくお待ちください...", + "loading": "読み込み中...", + "delete": "削除", + "col": { + "id": "イベントID", + "eventName": "イベント名", + "chapters": "元の章", + "detail": "イベントのプロセス", + "createTime": "作成時間", + "operation": "操作" + }, + "msg": { + "deleteHeader": "イベントの削除", + "deleteBody": "このイベントを削除してもよろしいですか?", + "deleteSuccess": "削除に成功しました", + "generateSuccess": "イベントの生成に成功しました", + "batchDeleteHeader": "一括削除", + "batchDeleteBody": "選択した {count} 件のデータを削除してもよろしいですか?", + "batchDeleteSuccess": "一括削除に成功しました" + } + }, + "analysis": { + "analyzeFirst": "先にイベントを分析してください", + "startAnalysis": "分析を開始", + "chapterHeader": "第{index}章 - {name}", + "analyzing": "イベント分析中" + } + }, + "scriptAgent": { + "inputPlaceholder": "内容を入力してください", + "chapterEvents": "章のイベント", + "clearMessageMemory": "メッセージメモリをクリア", + "clearSummaryMemory": "要約メモリをクリア", + "clearAllMemory": "すべてのメモリをクリア", + "edit": "編集", + "storySkeleton": "ストーリーの骨格", + "adaptationStrategy": "脚色戦略", + "script": "シナリオ", + "noContent": "コンテンツがありません", + "relatedAssets": "関連アセット", + "editScript": "シナリオを編集", + "save": "保存", + "scriptTitle": "タイトル", + "titlePlaceholder": "タイトルを入力してください", + "content": "内容", + "contentPlaceholder": "シナリオの内容を入力してください", + "selectAssets": "アセットを選択", + "noAssets": "関連アセットがありません", + "selectAssetsTitle": "関連アセットの選択", + "welcomeMsg": "こんにちは!Toonflow アシスタントです。シナリオの生成を開始しましょうか?", + "start": "開始", + "memoryType": { + "message": "メッセージメモリ", + "summary": "要約メモリ", + "all": "すべてのメモリ" + }, + "msg": { + "clearConfirm": "クリアを確認", + "clearBody": "{type}をクリアしてもよろしいですか?この操作は取り消せません。", + "confirmClear": "クリアを確認", + "cancel": "キャンセル", + "memoryCleared": "{type}をクリアしました", + "scriptUpdated": "シナリオの更新に成功しました", + "scriptUpdateFailed": "シナリオの更新に失敗しました。後で再試行してください", + "searchScriptFailed": "シナリオの検索に失敗しました", + "updated": "正常に保存されました", + "error": "保存に失敗しました", + "reconnect": "再接続", + "notReconnect": "再接続の会話が切断されることを確認しますか?", + "keepReconnect": "確認する", + "deleteConfirm": "削除確認", + "deleteBody": "テキストの削除", + "confirmDelete": "削除の確認", + "scriptDeleted": "スクリプトが削除されました" + }, + "reconnect": "再接続" + }, + "cornerScape": { + "batchSettings": "一括生成設定", + "quickActions": "クイックコマンド", + "selectUngenerated": "未生成をすべて選択", + "selectGenerated": "生成済みをすべて選択", + "selectFailed": "エラーをすべて選択", + "invertSelection": "選択を反転", + "clearSelection": "選択を解除", + "batchPreview": "画像の一括プレビュー", + "assetTypeFilter": "アセットタイプで絞り込み", + "genModel": "生成モデル", + "resolution": "解像度", + "resolutionPh": "解像度を選択してください", + "concurrency": "同時実行数", + "concurrencyPh": "同時実行数を入力してください", + "startBatch": "バッチでイメージの生成を開始する", + "waitingGen": "生成待ち", + "generating": "生成中", + "genFailed": "生成失敗", + "imageError": "画像エラー", + "typeRole": "キャラクター", + "typeScene": "シーン", + "typeTool": "小道具", + "typeUnknown": "不明", + "descriptionSuffix": "説明:", + "operateScriptFirst": "先にシナリオを操作してください", + "individualConfig": "個別設定", + "noImage": "画像がありません", + "promptLabel": "プロンプト", + "promptPh": "プロンプトを入力してください", + "aiPolish": "AI で推敲", + "regenerate": "再生成", + "filterRole": "人物", + "filterScene": "シーン", + "filterTool": "小道具", + "unnamed": "無名", + "noDescription": "説明なし", + "msg": { + "selectModel": "生成モデルを選択してください", + "selectResolution": "解像度を選択してください", + "enterPrompt": "プロンプトを入力してください", + "enterPromptFirst": "先にプロンプトを入力してください", + "genSuccess": "{name} の生成に成功しました", + "genFailed": "{name} の生成に失敗しました", + "promptGenSuccess": "プロンプトの生成に成功しました", + "polishFailed": "推敲に失敗しました。再試行してください", + "selectAtLeastOne": "一括生成するアセットを少なくとも1つ選択してください", + "batchStarted": "一括生成を開始しました。全 {count} 件、同時実行数 {concurrent}", + "batchItemFailed": "{name} の生成に失敗しました:{error}", + "batchComplete": "一括生成が完了しました", + "batchFailed": "バッチ生成に失敗しました", + "replaceFailed": "交換に失敗しました", + "replaceSuccess": "交換に成功しました", + "promptGenFail": "プロンプト単語の生成に失敗しました", + "saveSuccess": "プロンプトワードの変更が成功しました", + "saveFailed": "プロンプトワードの変更に失敗しました" + }, + "history": "歴史的な写真", + "confirmReplace": "交換の確認", + "batchGenerationPrompt": "プロンプト単語をバッチで生成する", + "generatingPrompt": "生成中", + "selectAll": "すべて選択", + "selectPromptEmpty": "プロンプトの単語が空であることをすべて選択してください", + "noEmptyPrompt": "空のプロンプトワードを含むアセットはありません", + "selectedCount": "{count} 個のアセットが選択されました", + "cancelGeneration": "生成をキャンセルする", + "selectGenerating": "生成されるアイテムを選択します", + "noGenerating": "データは生成されていません", + "checkNumber": "数量を選択してください" + }, + "script": { + "searchPlaceholder": "シナリオ名を検索...", + "search": "検索", + "addScript": "シナリオを新規作成", + "cancelSelectAll": "全選択を解除", + "selectAll": "すべて選択", + "exportScript": "シナリオをエクスポート", + "msg": { + "searchFailed": "シナリオの検索に失敗しました", + "selectExport": "先にエクスポートするシナリオを選択してください", + "exportSuccess": "エクスポートに成功しました", + "exportFailed": "シナリオのエクスポートに失敗しました", + "deleteHeader": "削除の確認", + "deleteBody": "このシナリオを削除してもよろしいですか?この操作は取り消せません。", + "deleteConfirm": "削除", + "cancel": "キャンセル", + "deleteSuccess": "削除に成功しました", + "deleteFailed": "削除に失敗しました", + "selectDelScript": "スクリプトを削除することを選択してください", + "batchDeleteHeader": "一括削除", + "batchDeleteBody": "選択した {count} 件のシナリオを削除してもよろしいですか?この操作は取り消せません。", + "batchDeleteSuccess": "一括削除に成功しました", + "extractingInProgress": "抽出中", + "projectNotFound": "アイテムが見つかりません", + "selectsExport": "スクリプトをエクスポートすることを選択してください" + }, + "add": { + "title": "シナリオの追加", + "scriptName": "シナリオ名", + "scriptNamePh": "シナリオ名を入力してください", + "uploadFile": "ファイルをアップロード", + "dragUpload": "ここにシナリオファイルをドラッグ&ドロップするか、クリックしてアップロード", + "uploadHint": "対応形式: .txt, .docx。ファイルサイズは10MB以下を推奨します", + "scriptContent": "シナリオ内容", + "scriptContentPh": "シナリオ内容をアップロードまたは入力してください...", + "relatedAssets": "関連アセット", + "selectAssets": "アセットを選択", + "noAssets": "関連アセットがありません", + "cancel": "キャンセル", + "confirm": "確定", + "msg": { + "fileReadFailed": "ファイルの読み取りに失敗しました", + "docNotSupported": ".docファイルの解析は未対応です。.txtまたは.docx形式に変換してください", + "unsupportedType": "未対応のファイル形式です", + "fileTooLarge": "ファイルサイズが10MBを超えています。より小さなファイルをアップロードしてください", + "parsing": "ファイルを解析中...", + "parseFailed": "ファイルの解析に失敗しました。再アップロードしてください", + "selectAssetsTitle": "関連アセットの選択", + "enterContent": "シナリオ内容をアップロードまたは入力してください", + "enterName": "シナリオ名を入力してください", + "addSuccess": "シナリオを追加しました", + "addFailed": "シナリオの追加に失敗しました。後で再試行してください" + } + }, + "edit": { + "title": "シナリオ詳細", + "scriptName": "シナリオ名", + "scriptNamePh": "シナリオ名を入力してください", + "scriptContent": "シナリオ内容", + "scriptContentPh": "シナリオ内容を入力してください...", + "relatedAssets": "関連アセット", + "selectAssets": "アセットを選択", + "noAssets": "関連アセットがありません", + "msg": { + "selectAssetsTitle": "関連アセットの選択", + "updateSuccess": "シナリオの更新に成功しました", + "updateFailed": "シナリオの更新に失敗しました。後で再試行してください" + } + }, + "deleteScript": "スクリプトを一括で削除する", + "extractAssets": "", + "import": { + "episodeRegexPh": "スクリプト分割ルールをカスタマイズします。デフォルトの分割ルールを使用するには空白のままにしてください (デフォルトはエピソード X 形式に従って分割されます)。" + } + }, + "assets": { + "addPrefix": "追加", + "batchGenerate": "一括生成", + "generatePrompt": "プロンプトを生成", + "generateImage": "画像を生成", + "batchDelete": "一括削除", + "searchPlaceholder": "アセット名を検索...", + "search": "検索", + "preview": "プレビュー", + "generate": "生成", + "edit": "編集", + "delete": "削除", + "generating": "生成中", + "play": "再生", + "mediaPreview": "メディアプレビュー", + "confirmBatch": "{type} を確認してください!", + "model": "モデル", + "resolution": "解像度", + "resolutionPh": "解像度を選択してください", + "batchGenPrompt": "プロンプトの一括生成", + "batchGenImage": "画像の一括生成", + "role": "キャラクター", + "prop": "小道具", + "scene": "シーン", + "clip": "素材", + "uploadSuccess": "アップロードに成功しました", + "selectAtLeastOne": "アセットを少なくとも1つ選択してください", + "noDescription": "説明なし", + "promptGenSuccess": "「{name}」のプロンプト生成に成功しました", + "promptGenFail": "「{name}」のプロンプト生成に失敗しました:{error}", + "selectModel": "モデルを選択してください", + "selectResolution": "解像度を選択してください", + "noPromptForImage": "「{name}」にはプロンプトがありません。画像を生成できません", + "imageGenSuccess": "「{name}」の画像生成に成功しました", + "imageGenFail": "「{name}」の画像生成に失敗しました:{error}", + "confirmDeleteHeader": "削除の確認", + "confirmBatchDeleteBody": "これらのアセットを一括削除してもよろしいですか?この操作は取り消せません。", + "confirmDeleteBody": "このアセットを削除してもよろしいですか?この操作は取り消せません。", + "deleteBtn": "削除", + "cancelBtn": "キャンセル", + "deleteSuccess": "アセットの削除に成功しました", + "deleteFail": "アセットの削除に失敗しました", + "colPreview": "プレビュー", + "colName": "名前", + "colPrompt": "プロンプト", + "colDescribe": "説明", + "colRemark": "備考", + "colCreateTime": "作成時間", + "colOperation": "操作", + "add": { + "name": "名前", + "namePh": "名前を入力してください", + "describe": "説明", + "describePh": "説明を入力してください", + "remark": "備考", + "remarkPh": "備考を入力してください", + "prompt": "プロンプト", + "promptPh": "プロンプトを入力してください", + "nameRequired": "名前を入力してください", + "describeRequired": "詳細を入力してください", + "remarkRequired": "備考を入力してください", + "updateSuccess": "アセットの更新に成功しました", + "addSuccess": "アセットの追加に成功しました" + }, + "gen": { + "header": "画像の生成", + "uploadRef": "参照画像をアップロード", + "optional": "任意", + "promptLabel": "画像生成プロンプト", + "smartGenerate": "スマート生成", + "generatingPrompt": "スマートプロンプトを生成中...", + "promptPlaceholder": "生成したい画像の内容を記述してください。例:テクノロジー感あふれる未来都市、ネオンの瞬き、サイバーパンクスタイル...", + "selectModel": "モデルを選択", + "selectResolution": "解像度を選択", + "generateBtn": "画像を生成", + "resultTitle": "生成結果", + "generatedCount": "{count} 枚生成されました。1枚選択してください", + "generatingLabel": "生成中...", + "genFailed": "生成に失敗しました", + "confirmSelect": "選択を確定", + "promptSuccess": "プロンプトの生成に成功しました", + "promptFail": "プロンプトの生成に失敗しました", + "fillPrompt": "プロンプトを入力してください", + "pickResolution": "解像度を選択してください", + "pickModel": "モデルを選択してください", + "unnamed": "無名", + "assetGenSuccess": "アセットの生成に成功しました", + "assetGenFail": "アセットの生成に失敗しました", + "uploadOk": "アップロードに成功しました", + "imageSelected": "この画像を選択しました", + "imageDeleted": "この画像を削除しました", + "imageSaved": "画像を保存しました", + "completed": "完了" + }, + "batch": { + "header": "一括生成", + "selected": "{count} 件選択中", + "selectAll": "すべて選択", + "clearSelection": "選択をクリア", + "inputPh": "内容を入力してください", + "saveSelected": "選択項目を保存 ({count})", + "colPreviewImg": "プレビュー画像", + "selectToSave": "保存する項目を選択してください", + "saveSuccess": "保存に成功しました", + "saveFail": "保存に失敗しました。再試行してください", + "promptDone": "プロンプトの生成が完了しました", + "promptFail": "プロンプトの生成に失敗しました", + "missingPrompts": "プロンプトがないアセットが {count} 件あります。先にプロンプトを生成してください", + "imageDone": "画像の生成が完了しました", + "imageGenFail": "画像の生成に失敗しました", + "unknownError": "不明なエラー", + "promptGenCancelled": "生成がキャンセルされました" + }, + "confirmCancellation": "キャンセルの確認", + "confirmAgain": "キャンセルを確認しますか?キャンセル後も、バックエンド AI は控除を要求し続けます。", + "sure": "もちろん" + }, + "production": { + "selectPlaceholder": "エピソードを選択してください", + "edit": "編集", + "node": { + "script": { + "title": "シナリオ", + "editDialog": "シナリオの編集" + }, + "scriptPlan": { + "title": "撮影プラン", + "editDialog": "撮影プランの編集" + }, + "storyboard": { + "title": "絵コンテボード", + "notGenerated": "未生成", + "scaleRatio": "ズーム倍率", + "gridPreview": "グリッドプレビュー", + "noPreviewImages": "プレビュー可能な画像がありません", + "imageLoadFailed": "画像の読み込みに失敗しました", + "promptPlaceholder": "プロンプトの単語を入力してください", + "prompt": "即効性のある言葉", + "editInfo": "即時の単語の修正" + }, + "storyboardTable": { + "title": "絵コンテ表", + "editDialog": "絵コンテ表の編集" + }, + "assets": { + "title": "派生アセット", + "generateFailed": "生成失敗", + "notGenerated": "未生成", + "originalAsset": "元のアセット", + "derived": "派生", + "noDerivedAssets": "派生アセットなし" + }, + "poster": { + "title": "動画カバー", + "coverCount": "{count} 枚" + }, + "workbench": { + "title": "動画ワークベンチ" + } + }, + "editImage": { + "upload": "アップロード", + "generate": "生成", + "saveFailed": "保存に失敗しました。再試行してください", + "fetchFailed": "データの取得に失敗しました", + "generating": "生成中...", + "deleteNode": "ノードを削除", + "ratio": "アスペクト比", + "quality": "画質", + "generateBtn": "画像を生成", + "selectImage": "画像を選択", + "imageGeneration": "画像生成", + "promptPlaceholder": "生成したい画像を記述してください...", + "imageRef": "画像{index}", + "noReferences": "引用可能な参照画像がありません", + "selectModel": "先にモデルを選択してください", + "selectQuality": "画質を選択してください", + "selectRatio": "アスペクト比を選択してください", + "generateFailed": "生成に失敗しました", + "generateFirst": "先に画像を生成してください", + "generatedResult": "生成結果", + "waitingGenerate": "生成待ち", + "layoutLR": "自動レイアウト-左右", + "layoutTB": "自動レイアウト-上下", + "uploadAssetImage": "アセット画像アップロード", + "uploadStoryboardImage": "絵コンテ画像アップロード", + "uploadImage": "アセットイメージのアップロード", + "mode": "モデル", + "closeConfirmTitle": "編集パネルを閉じますか?", + "closeConfirmBody": "保存していないデータは閉じると失われます" + }, + "save": "選択", + "cancel": "キャンセル", + "chatBox": { + "inputPlaceholder": "メッセージを入力...", + "generateDerivedAssets": "派生アセットを生成", + "welcomeMessage": "こんにちは!AI アシスタントです。何かお手伝いできることはありますか?", + "adjustModel": "モデルを調整", + "startMakingVideo": "動画制作を開始", + "startMakingVideoPrompt": "動画の制作を開始してください", + "clearMessageMemory": "メッセージメモリをクリア", + "clearSummaryMemory": "要約メモリをクリア", + "clearAllMemory": "すべてのメモリをクリア", + "messageMemory": "メッセージメモリ", + "summaryMemory": "要約メモリ", + "allMemory": "すべてのメモリ", + "confirmClear": "メモリのクリア", + "confirmClearBody": "{type}をクリアしてもよろしいですか?", + "confirmClearBtn": "クリアを確定", + "memoryCleared": "{type}をクリアしました" + }, + "wb": { + "quickPreview": "クイックプレビュー", + "videoGeneration": "絵コンテ", + "videoEditing": "編集デスク", + "hint": "ヒント", + "extractLines": "動画からセリフを抽出しますか?", + "no": "いいえ", + "confirm": "確定", + "extractLinesQuestion": "字幕用に動画からセリフを抽出しますか?", + "importingLoading": "インポート中です。しばらくお待ちください...", + "mainTrackVideo": "メイントラック(動画)", + "subtitle1": "字幕1", + "storyboardVideoName": "{ストーリーボード}-{id}.mp4" + }, + "preview": { + "noImage": "画像なし", + "storyboardDesc": "絵コンテの説明", + "serialNumber": "No.", + "noDescription": "説明なし", + "duration": "デュレーション", + "seconds": "秒", + "relatedAssets": "関連アセット", + "role": "キャラクター", + "prop": "小道具", + "scene": "シーン", + "noCharacters": "登場人物なし", + "imagePrompt": "画像のプロンプト", + "selectAll": "すべて選択", + "exportImage": "画像をエクスポート", + "sceneDescription": "画面の説明", + "promptLabel": "プロンプト", + "restoreSort": "並び順をリセット", + "restoreSortConfirm": "初期の並び順にリセットしてもよろしいですか?", + "tip": "ヒント", + "selectAtLeastOne": "エクスポートする絵コンテを少なくとも1つ選択してください", + "exportFilename": "絵コンテの絵" + }, + "generate": { + "noVideo": "動画なし", + "videoPrompt": "動画プロンプト", + "promptPlaceholder": "生成するビデオ コンテンツを説明するプロンプト ワードを入力してください...", + "refImage": "参照画像", + "image": "画像", + "refVideo": "参照動画", + "refImageLabel": "参照画像", + "refAudio": "参照音声", + "muteAudio": "音声をミュート", + "enableAudio": "音声をオン", + "resolution": "解像度", + "duration": "デュレーション", + "generate": "生成", + "historyVersions": "履歴バージョン", + "confirmSelection": "選択を確定", + "noHistory": "履歴がありません", + "generating": "生成中", + "generateFailed": "生成失敗", + "selectAll": "すべて選択", + "videoTrack": "ビデオトラック", + "batchGenerate": "一括生成", + "importToEditor": "編集エディタにインポート", + "modeSingleImage": "単一画像", + "modeMultiImage": "複数画像", + "modeGridImage": "グリッド複数画像", + "modeStartEnd": "開始・終了フレーム", + "modeText": "テキストから動画生成", + "modeVideoRef": "動画参照", + "modeImageRef": "画像参照", + "modeAudioRef": "音声参照", + "modeTextRef": "テキスト参照", + "startFrame": "開始フレーム", + "startFrameOptional": "開始フレーム (任意)", + "endFrame": "終了フレーム", + "endFrameOptional": "終了フレーム (任意)", + "selectRefImage": "参照画像を選択", + "selectRefImages": "参照画像を選択", + "selectEndFrame": "終了フレーム画像を選択", + "selectRefVideoAsset": "参照動画を選択", + "selectRefAudioAsset": "参照音声を選択", + "selectRefImageAsset": "参照画像を選択", + "selectImageSource": "画像ソースを選択", + "fromStoryboard": "絵コンテ画像", + "fromStoryboardDesc": "絵コンテリストから画像を選択", + "fromAssets": "アセット画像", + "fromAssetsDesc": "アセットライブラリから画像を選択", + "confirmDelete": "削除の確認", + "confirmDeleteBody": "この動画を削除してもよろしいですか?この操作は取り消せません。", + "delete": "削除", + "cancel": "キャンセル", + "deleteSuccess": "動画の削除に成功しました", + "deleteFailed": "削除に失敗しました", + "selectVideoFirst": "先に動画を1つ選択してください", + "confirmSuccess": "選択の確定に成功しました", + "batchSubmitted": "一括生成リクエストを送信しました。処理中です...", + "configNotFound": "設定が見つかりません", + "pollingFailed": "ビデオ ステータスのクエリに失敗しました。手動で更新してください", + "batchGeneratePrompt": "プロンプト単語をバッチで生成する", + "batchPromptEmpty": "ストーリーボード {name} はビデオ プロンプトで使用できます。最初にプロンプ​​トを作成または入力してください", + "modelEmpty": "最初にビデオ生成モデルを選択してください", + "generatingPrompt": "プロンプトワードのインテリジェントな生成" + }, + "editVideo": { + "reset": "リセット", + "undo": "取り消し", + "redo": "やり直し", + "split": "分割", + "delete": "削除", + "rendering": "レンダリング中...", + "exportVideo": "動画をエクスポート", + "exportSuccess": "動画のエクスポートが完了しました", + "exportFailed": "エクスポートに失敗しました", + "sampleSubtitle": "サンプルの字幕テキスト", + "customText": "カスタムテキスト内容", + "transitionBetweenClips": "トランジションは2つの隣接するクリップ間に追加する必要があります", + "transitionExists": "この位置にはすでにトランジションが存在します", + "videoPreviewArea": "動画プレビューエリア", + "clipMaterials": "編集素材", + "propertyPanel": "プロパティパネル", + "selectClip": "属性を表示するクリップを選択してください", + "basicInfo": "基本情報", + "name": "名前", + "clipNamePlaceholder": "クリップ名", + "startTime": "開始", + "endTime": "終了", + "totalDuration": "合計デュレーション", + "videoProperties": "動画プロパティ", + "opacity": "不透明度", + "volume": "音量", + "playbackSpeed": "再生速度", + "audioProperties": "音声プロパティ", + "fadeIn": "フェードイン", + "fadeOut": "フェードアウト", + "transitionProperties": "トランジションプロパティ", + "transitionType": "トランジションの種類", + "transFade": "フェード", + "transSlide": "スライド", + "transWipe": "ワイプ", + "transDissolve": "ディゾルブ", + "transZoom": "ズーム", + "transRotate": "回転", + "transitionDuration": "トランジションの長さ", + "subtitleProperties": "字幕プロパティ", + "textContent": "テキスト内容", + "fontSize": "フォントサイズ", + "copy": "コピー", + "deleteConfirm": "削除の確認", + "deleteClipConfirm": "このクリップを削除してもよろしいですか?", + "avCanvasNotInit": "AVCanvas が初期化されていません", + "noExportContent": "エクスポートするコンテンツがありません", + "exportProject": "プロジェクトをエクスポート", + "transitionAdded": "トランジションを追加しました: {name}", + "splitClip": "クリップを分割", + "deleteClip": "クリップを削除", + "addClip": "{name} を追加", + "duplicateClip": "クリップを複製", + "addTransition": "トランジションを追加", + "updateClip": "クリップ {key} を更新", + "updatePlaybackRate": "再生速度を {rate}x に更新", + "updateTransitionDuration": "トランジションの長さを更新", + "playbackRateRange": "再生速度は 0.1 から 10 の間である必要があります", + "updatePlaybackRateFailed": "再生速度の更新に失敗しました:", + "importProject": "プロジェクトをインポート", + "import": "インポート" + }, + "clipType": { + "video": "動画", + "audio": "音声", + "subtitle": "字幕", + "transition": "トランジション", + "sticker": "ステッカー", + "filter": "フィルター", + "effect": "エフェクト" + }, + "track": { + "video": "動画", + "image": "画像", + "audio": "音声", + "subtitle": "字幕", + "text": "テキスト", + "sticker": "ステッカー", + "filter": "フィルター", + "effect": "エフェクト" + }, + "transition": { + "fade": "フェード", + "slide": "スライド", + "slideLeft": "左へスライド", + "slideRight": "右へスライド", + "slideUp": "上へスライド", + "slideDown": "下へスライド", + "wipe": "ワイプ", + "wipeLeft": "左へワイプ", + "wipeRight": "右へワイプ", + "wipeUp": "上へワイプ", + "wipeDown": "下へワイプ", + "dissolve": "ディゾルブ", + "zoom": "ズーム", + "zoomIn": "ズームイン", + "zoomOut": "ズームアウト", + "rotate": "回転", + "circle": "サークル", + "diamond": "ひし形", + "clock": "時計", + "blur": "ブラー" + }, + "media": { + "titleText": "タイトルテキスト", + "subtitleText": "字幕テキスト", + "customText": "カスタムテキスト", + "media": "メディア", + "image": "画像", + "audio": "音声", + "subtitle": "字幕", + "transition": "トランジション", + "effect": "エフェクト", + "filter": "フィルター", + "loading": "読み込み中...", + "subtitlePreview": "字", + "video": "ビデオ" + }, + "effect": { + "fadeIn": "フェードイン", + "fadeOut": "フェードアウト", + "flash": "フラッシュ", + "shake": "シェイク", + "zoomIn": "ズームイン", + "zoomOut": "ズームアウト", + "pulse": "パルス", + "rotateIn": "回転しながらイン", + "sticker1": "ステッカー 1", + "sticker2": "ステッカー 2" + }, + "filter": { + "grayscale": "モノクロ", + "sepia": "セピア", + "warm": "ウォーム", + "cool": "クール", + "vivid": "ビビッド", + "bright": "ブライト", + "highContrast": "ハイコントラスト", + "blur": "ブラー", + "invert": "反転", + "semiTransparent": "半透明" + }, + "guideSwitchEpisode": "エピソードを切り替え", + "guideSwitchEpisodeBody": "エピソードの切り替えはこちらに移動しました", + "autoLayoutLR": "自動組版 - 左右レイアウト", + "autoLayoutTB": "自動写植上下レイアウト", + "getFlowData": "ワークスペースを更新する", + "confirm": "エピソード切り替え確認", + "confirmEpisodesSwitch": "現在のタスクはまだ進行中です。エピソードを切り替えるとセッションが再接続されます。切り替えを続けますか?" + }, + "task": { + "title": "タスクリスト", + "subtitle": "最新のタスク実行履歴", + "refresh": "更新", + "categoryLabel": "タスクカテゴリ:", + "stateLabel": "ステータス:", + "noFailReason": "失敗理由なし", + "stateAll": "すべて", + "stateRunning": "進行中", + "stateCompleted": "完了", + "stateFailed": "生成失敗", + "fetchFailed": "タスクリストの取得に失敗しました", + "col": { + "taskClass": "タスクカテゴリ", + "relatedObjects": "関連オブジェクト", + "model": "モデル", + "describe": "説明", + "state": "ステータス", + "startTime": "時間", + "reason": "失敗の理由" + }, + "project": "プロジェクト名:" + }, + "noVideo": "まだビデオはありません", + "prompt": "ビデオのプロンプトワード", + "generateText": "AIが生成したプロンプトワード", + "selectStoryboard": "ストーリーボードを選択", + "generate": { + "noVideo": "まだデータがありません", + "generateText": "AIが生成したプロンプトワード", + "selectStoryboard": "ストーリーボードを選択", + "generate": "ビデオの生成", + "history": "歴史的バージョン", + "generating": "生成中", + "generateFailed": "失敗の原因を確認する", + "selectAll": "すべて選択", + "selected": "選択済み", + "batchGenerateText": "プロンプト単語をバッチで生成する", + "batchGenerateVideo": "バッチでビデオを生成する", + "importVideo": "編集デスクにインポートする", + "emptyTrack": "段落 {index}", + "del": "消去", + "delConfirm": "この段落を削除してもよろしいですか?", + "selectSource": "ソースを選択", + "confirm": "アセットから選択", + "cancel": "ストーリーボードから選択", + "selectVideoFailed": "ビデオの選択に失敗しました", + "selectVideoSuccess": "ビデオの選択が成功しました", + "previewVideo": "動画プレビュー", + "selectTrackFirst": "最初にストーリーボードを選択してください", + "noSelectedVideo": "動画が選択されていません", + "generateConfirm": "世代の確認", + "generateConfirmBody": "ビデオの生成を確認します", + "generateVideosInBatches": "バッチでビデオを生成する", + "generateStarted": "ビルドの開始", + "promptEmpty": "ビデオを生成するために必要なデータを確認してください。プロンプトの単語が空です。生成を続けますか?", + "skipDataWithEmptyVideoPromptWords": "生成する必要があるビデオ データは存在しますが、プロンプト ワードは空です。", + "duration": "間隔", + "resolution": "解決", + "delVideo": "このビデオを削除しますか?", + "delSuccess": "正常に削除されました", + "addReference": "参照を追加", + "promptPlaceholder": "動画のプロンプトワードを入力してください", + "downloadVideo": "動画をバッチでダウンロードする", + "selectVideo": "ダウンロードしたい動画にチェックを入れてください", + "batchDownloadVideo": "動画をバッチでダウンロードする", + "storyboard": "絵コンテ", + "assets": "資産", + "promptText": "ビデオプロンプトワードデータの生成", + "videoMenu": "ビデオの生成", + "videoPreview": "ビデオプレビュー", + "referenceImage": "参考写真", + "generatePrompt": "プロンプト単語を生成する", + "generateVideo": "ビデオの生成" + } + }, + "login": { + "slogan": "ショードラ制作支援ツール", + "tips": "デフォルトアカウント:admin / admin123", + "settings": "サーバー設定", + "requestAddress": "リクエストアドレス", + "username": "ユーザー名", + "password": "パスワード", + "login": "ログイン", + "usernameRequired": "ユーザー名を入力してください", + "passwordRequired": "パスワードを入力してください", + "enterUsernameAndPassword": "ユーザー名またはパスワードを入力してください", + "loginSuccess": "ログインに成功しました", + "settingsSaved": "設定を保存しました" + }, + "common": { + "cancel": "キャンセル", + "confirm": "確認", + "selectAssets": "アセットを選択", + "sessionExpired": "セッションが期限切れです。再度ログインしてください", + "openSettings": "設定を開く", + "defaultReel": "本文巻", + "save": "保存", + "submitting": "提出する", + "editSuccess": "変更が成功しました", + "editFailed": "変更に失敗しました", + "submit": "提出する" + }, + "components.storyboardImageCheck.camera": "カメラ", + "components.storyboardImageCheck.dialogTitle": "絵コンテ画像を選択", + "components.storyboardImageCheck.preview": "プレビュー", + "components.storyboardImageCheck.src": "プレビュー画像", + "components.storyboardImageCheck.title": "タイトル", + "components.storyboardImageCheck.duration": "時間", + "components.storyboardImageCheck.lines": "セリフ", + "components.storyboardImageCheck.createTime": "作成日時", + "workbench.script.extractAssets": "アセットを抽出", + "promptManage": { + "prompt": "即効性のある言葉" + }, + "hello": { + "welcomeTitle": "トゥーンフローへようこそ", + "welcomeDesc": "AI 主導のコミック作成ワークフロー プラットフォーム。1 分ほどかけて初期構成を完了してみましょう。", + "startConfig": "設定を開始する", + "skip": "スキップブート", + "configModel": "モデルサービスの追加", + "configData": "エージェントの構成", + "startUse": "始めましょう", + "configModelTitle": "モデルサービスプロバイダーを追加", + "configModelDesc": "まず、設定に少なくとも 1 つの AI モデル サービス プロバイダー (OpenAI、Claude など) を追加し、対応する API キーを入力する必要があります。", + "configModelTip": "下のボタンをクリックすると、設定ページの「モデルサービス」タブが開きます。サプライヤーを追加したら、ここに戻って続行します。", + "configModelBtn": "モデルサービスの構成に移動します", + "configAgentTitle": "エージェントモデルの割り当て", + "configAgentDesc": "次に、エージェント構成内の各機能モジュールにモデルを割り当て、タスクを完了するためにどのモデルを呼び出すべきかをシステムが認識できるようにします。", + "configAgentTip": "下のボタンをクリックすると、設定ページの「エージェント構成」タブが開きます。各機能にモデルを割り当てた後、ここに戻ります。", + "configAgentBtn": "エージェントの構成に移動します", + "finishTitle": "🎉 準備は万端です!", + "finishDesc": "設定が完了したので、すべての機能を使用できるようになります。調整する必要がある場合は、いつでも設定で変更できます。", + "qrcodeLabel": "さらに詳しいサポートを得るには、WeChat コミュニケーション グループに参加してください。", + "githubLabel": "役に立ったと思ったら、⭐スターをお願いします!", + "prevStep": "前のステップ", + "nextStep": "次のステップ", + "finish": "始めましょう" + }, + "setting": { + "skillManagement": { + "search": "ファイル名を検索する", + "empty": "一致するファイルがありません", + "edit": "編集", + "selectOnTheLeft": "左からファイルを選択してください" + } + }, + "storyboard": { + "assets": { + "notExist": "アセットが存在しません", + "notDerivativeExist": "派生資産は存在しません", + "derivativeUpdateSuccess": "更新に成功しました", + "derivativeState": "生成されない", + "derivativeAddSuccess": "正常に追加されました", + "derivativeDelSuccess": "正常に削除されました", + "notGenerated": "生成されない" + }, + "addSuccess": "更新に成功しました", + "state": { + "unused": "生成されない" + }, + "saveSuccess": "正常に追加されました" + }, + "productionAgent": { + "generating": "生成中" + }, + "skillScan": { + "scanning": "🔍 解析と読み込みスキル", + "scanComplete": "✨ Skill スキャン完了", + "inserted": "✅ {count} 個のスキルを追加しました", + "updated": "🔄 {count}個のSkillを更新", + "removed": "🗑️ {count}個のSkillを削除", + "scannedFiles": "📁 {count}個のファイルをスキャン", + "noDescription": "📝 {count}個のSkillに説明がありません", + "noAttribution": "👤 {count}個のSkillに帰属がありません", + "configWarning": "⚠️ Skill 設定警告", + "openSettings": "設定を開く", + "scanFailed": "❌ スキャン失敗", + "checkNetwork": "🔌 ネットワーク接続を確認するか、後で再試行してください", + "retryLater": "🔁 後で再試行してください" + }, + "generate": "ビデオの生成", + "history": "歴史的バージョン", + "generating": "生成中", + "generateFailed": "失敗", + "selectAll": "すべて選択", + "selected": "選択済み", + "importVideo": "ビデオをインポートする", + "emptyTrack": "段落 {インデックス 1}", + "del": "削除の確認", + "delConfirm": "この段落を削除してもよろしいですか?", + "selectSource": "ソースを選択", + "confirm": "アセットから選択", + "cancel": "ストーリーボードから選択", + "workbench.script.msg.exportFailed": "エクスポートに失敗しました", + "workbench.production.node.assets.confirmDeleteBody": "アセットを削除するかどうかの確認", + "workbench.production.node.assets.removeFailed": "アセットの削除に失敗しました", + "version": { + "newVersion": "新しいバージョンがあります。更新しますか?" + }, + "workbench.production.generatedNode.localUpload": "ローカルアップロード", + "workbench.production.editImage.uploadFailed": "画像のアップロードに失敗しました", + "workbench.production.editImage.noImage": "まずは写真を追加してください", + "workbench.script.batchAddScript": "スクリプトをバッチでアップロードする", + "workbench.script.import.pasteLabel": "スクリプトの内容を直接貼り付けます", + "workbench.script.import.col.scriptName": "スクリプト名", + "workbench.script.import.col.scriptData": "スクリプトの内容", + "workbench.script.import.episodeRegex": "スクリプト分割ルール", + "workbench.script.import.episodeRegexPh": "スクリプト分割ルールをカスタマイズします。デフォルトの分割ルールを使用するには空白のままにしてください (デフォルトはエピソード X 形式に従って分割されます)。", + "workbench.script.import.regexInvalid": "正規表現の形式が正しくありません", + "workbench.script.import.parsedChapters": "{count} セットが解析されました", + "workbench.script.import.msg.selectChapters": "まずはスクリプトを確認してください", + "workbench.script.import.msg.saveSuccess": "スクリプトは正常に保存されました", + "workbench.script.import.batchTitle": "スクリプトをバッチでアップロードする", + "workbench.assets.sex": "性別", + "workbench.assets.audioText": "音声コンテンツ", + "workbench.assets.audio": "オーディオ", + "workbench.assets.add.sex": "性別", + "workbench.assets.add.sexPh": "性別を入力してください", + "settings.agent.advanced": "高度な構成", + "settings.agent.ordinary": "簡単な設定", + "settings.agent.temperature": "温度", + "settings.agent.maxOutputTokens": "最大出力トークン", + "settings.agent.auto": "自動", + "settings.agent.manual": "手動", + "settings.agent.autoHint": "モデルが出力長を自動決定", + "settings.agent.msg.notmodel": "モデルが選択されていません", + "workbench.production.node.storyboard.generateImage": "ストーリーボードを生成する", + "workbench.generate.notSelectMode": "最初にモデルを選択してください", + "workbench.production.node.storyboard.deleteSuccess": "ストーリーボードが正常に削除されました", + "workbench.production.node.storyboard.pleaseSelectImage": "最初にストーリーボードを選択してください", + "workbench.cornerScape.audioState": "バインディング", + "workbench.generate.generateError": "ビルドリクエストの開始に失敗しました", + "settings.vendor.videoGenerating": "ビデオの生成が遅いので、しばらくお待ちください", + "settings.memory.modelMap.editRefeshWord": "再バインド", + "settings.memory.modelMap.delPrompt": "消去", + "settings.vendor.testModel": "テスト" +} diff --git a/web-core/src/locales/language/ru_RU.json b/web-core/src/locales/language/ru_RU.json new file mode 100644 index 0000000..7e46a12 --- /dev/null +++ b/web-core/src/locales/language/ru_RU.json @@ -0,0 +1,1547 @@ +{ + "components": { + "editMdPreivew": { + "title": "Редактировать", + "confirm": "Сохранить", + "cancel": "Отмена" + }, + "imageTools": { + "copy": "Копировать изображение", + "preview": "Предпросмотр", + "download": "Скачать", + "msg": { + "imageLoadFailed": "Не удалось загрузить изображение", + "convertFailed": "Ошибка конвертации", + "copied": "Скопировано в буфер обмена", + "copyFailed": "Ошибка копирования", + "downloadFailed": "Ошибка скачивания", + "downloadStarted": "Скачивание началось", + "downloadBlockedOpenNewWindow": "Текущий источник изображения может ограничивать скачивание, предпринята попытка открыть в новом окне" + } + }, + "migrateShow": { + "title": "Перенос данных", + "desc": "Обнаружены данные старой версии. Выполнить перенос?", + "hide": "Больше не показывать", + "confirm": "ОК", + "msg": { + "migrateSuccess": "Данные успешно перенесены", + "migrateFailed": "Ошибка переноса данных" + } + }, + "modelSelect": { + "placeholder": "Пожалуйста, выберите модель", + "type": { + "image": "Изображение", + "text": "Текст", + "video": "Видео" + }, + "msg": { + "fetchModelFailed": "Не удалось получить данные модели:" + }, + "goSetting": "Зайди в настройки и добавь модель." + } + }, + "settings": { + "title": "Настройки ToonFlow", + "menu": { + "language": "Язык", + "vendorConfig": "Поставщики моделей", + "agentConfig": "Настройки Agent", + "promptManage": "Управление подсказками", + "skillManagement": "Управление навыками", + "memoryConfig": "Память Agent", + "loginConfig": "Настройки входа", + "dbConfig": "База данных", + "fileManagement": "Управление файлами", + "otherConfig": "Другие настройки", + "requestConfig": "URL запросов", + "about": "Проверить обновления", + "logoutConfig": "Выйти", + "skillsSkillsManagement": "НавыкиУправление навыками" + }, + "language": { + "desc": "Выберите язык интерфейса", + "msg": { + "saved": "Языковые настройки сохранены" + } + }, + "vendor": { + "addVendor": "Добавить поставщика", + "noVendor": "Нет поставщиков. Пожалуйста, добавьте.", + "required": "Обязательно", + "optionalSection": "Необязательно", + "modelSettings": "Настройки модели", + "addManually": "Добавить вручную", + "test": "Тест", + "edit": "Редактировать", + "delete": "Удалить", + "deleteVendor": "Удалить поставщика", + "editCode": "Редактировать код", + "updateConfig": "Обновить конфигурацию", + "addModel": "Добавить модель", + "editModel": "Редактировать модель", + "displayName": "Отображаемое имя", + "displayNamePlaceholder": "Например: GPT-4o", + "modelId": "ID модели", + "modelIdPlaceholder": "Например: gpt-4o", + "modelType": "Тип модели", + "multimodal": "Мультимодальная", + "supported": "Поддерживается", + "notSupported": "Не поддерживается", + "toolCall": "Вызов инструментов", + "imageMode": "Режим изображения", + "videoMode": "Режим видео", + "audioOutput": "Аудиовыход", + "durationResolution": "Длительность / Разрешение", + "durationSec": "Длительность (сек)", + "resolution": "Разрешение", + "enterAndPress": "Введите и нажмите Enter", + "addDurationResolution": "Добавить длительность / разрешение", + "testResult": "Результаты теста", + "generating": "Генерация...", + "addVendorDialog": "Добавление поставщика", + "codeEditorInfo": "Пожалуйста, напишите код на TypeScript для конфигурации поставщика", + "reset": "Сброс", + "importFile": "Импорт файла", + "textModel": "Текстовая модель", + "imageModel": "Модель изображений", + "videoModel": "Видеомодель", + "textToImage": "Текст в изображение", + "textToVideo": "Текст в видео", + "singleImage": "Одно изображение", + "multiImage": "Много изображений", + "multiReference": "Множественный референс", + "multiReferenceMode": "Мульти-референсный режим", + "gridImage": "Сетка изображений", + "startEndRequired": "Первый и последний кадры (Оба обязательны)", + "endFrameOptional": "Первый и последний кадры (Последний необязателен)", + "startFrameOptional": "Первый и последний кадры (Первый необязателен)", + "textRef": "Текст", + "imageRef": "Изображение", + "videoRef": "Видео", + "audioRef": "Аудио", + "audioOptional": "Необязательно", + "audioOnly": "Только видео со звуком", + "noAudio": "Только видео без звука", + "msg": { + "getVendorListFailed": "Не удалось получить список поставщиков", + "vendorConfigUpdated": "Конфигурация поставщика обновлена", + "updateFailed": "Ошибка обновления", + "highRiskConfirm": "⚠️ Подтверждение опасной операции", + "addVendorRiskBody": "Добавление нового поставщика ИИ даст ему доступ к API системы. Убедитесь, что вы доверяете исходному коду этого поставщика!", + "iKnowRisk": "Я осознаю риски", + "cancel": "Отмена", + "confirmAgain": "⚠️ Повторное подтверждение", + "addVendorConfirmBody": "Вы уверены, что хотите добавить этого поставщика? Он будет включен в системное планирование моделей.", + "confirmAndAdd": "Подтвердить и добавить", + "goBackCheck": "Вернуться и проверить", + "vendorAdded": "Поставщик успешно добавлен", + "addFailed": "Ошибка добавления", + "updateVendorRiskBody": "Обновление конфигурации поставщика ИИ изменит его доступ к API системы и поведение. Убедитесь, что вы доверяете измененному коду!", + "updateVendorConfirmBody": "Вы уверены, что хотите обновить конфигурацию этого поставщика? Это повлияет на системное планирование моделей.", + "confirmAndUpdate": "Подтвердить и обновить", + "updateSuccess": "Конфигурация поставщика успешно обновлена", + "fillDisplayName": "Пожалуйста, введите отображаемое имя", + "fillModelId": "Пожалуйста, введите ID модели", + "selectImageMode": "Пожалуйста, выберите режим изображения", + "selectVideoMode": "Пожалуйста, выберите режим видео", + "groupPrefix": "Группа {n}: ", + "addDuration": "Пожалуйста, добавьте длительность", + "addResolution": "Пожалуйста, добавьте разрешение", + "selectVendorFirst": "Пожалуйста, сначала выберите поставщика", + "modelIdExists": "ID модели уже существует", + "modelAdded": "Модель успешно добавлена", + "modelUpdated": "Модель успешно обновлена", + "enterApiKey": "Пожалуйста, введите API KEY", + "enterApiUrl": "Пожалуйста, введите API URL", + "testSuccess": "Тест пройден", + "imageGenSuccess": "Изображение успешно сгенерировано", + "videoGenSuccess": "Видео успешно сгенерировано", + "requestFailed": "Ошибка запроса", + "deleteModelConfirm": "Подтверждение удаления модели", + "deleteModelBody": "Это действие необратимо. Продолжить?", + "confirmDelete": "Удалить", + "modelDeleted": "Модель удалена", + "deleteVendorConfirm": "Подтверждение удаления поставщика", + "deleteVendorBody": "Удаление поставщика также удалит все связанные с ним модели. Продолжить?", + "vendorDeleted": "Поставщик удален", + "deleteFailed": "Ошибка удаления", + "enabled": "Включено", + "disabled": "Неполноценный", + "linkAddVendorRiskBody": "Добавление нового поставщика ИИ предоставит ему доступ к системному API. Убедитесь, что вы доверяете источнику ссылок поставщика!", + "importAdd": "Добавление нового поставщика ИИ предоставит ему доступ к системному API. Убедитесь, что вы доверяете источнику документации поставщика!", + "linkAddFailed": "Не удалось добавить ссылку" + }, + "think": "глубокое мышление", + "code": "код", + "linkAddPlaceholder": "Введите ссылку для добавления", + "noFileSelected": "Файл успешно импортирован", + "linkAdd": "подтверждать" + }, + "agent": { + "bannerDesc": "Используйте официальный прокси-сервер Toonflow для конфигурации в один клик. Готово к использованию без ручной настройки.", + "visitWebsite": "Перейти на сайт", + "fillKey": "Введите KEY", + "oneClickFill": "Вставить в 1 клик", + "notOpen": "Недоступно", + "notConfigured": "Не настроено", + "modelConfig": "Настройки модели", + "confirm": "ОК", + "cancel": "Отмена", + "selectModel": "Выберите модель", + "fillKeyHeader": "Введите официальный KEY платформы Toonflow", + "keyPlaceholder": "Пожалуйста, введите KEY", + "save": "Сохранить", + "msg": { + "notAvailable": "Эта функция пока недоступна. Следите за обновлениями!", + "configSuccess": "Успешно настроено", + "updateConfigFailed": "Ошибка обновления конфигурации: ", + "keyValid": "KEY действителен. Успешное подключение к платформе Toonflow", + "keyInvalid": "KEY недействителен. Пожалуйста, проверьте и введите снова: ", + "enterKey": "Пожалуйста, введите KEY", + "saveFailed": "Ошибка сохранения: ", + "getAgentListFailed": "Не удалось получить список конфигураций Agent: " + }, + "temperature": "температура" + }, + "memory": { + "warning": "Следующие параметры предварительно настроены на рекомендуемые значения. Не изменяйте их, если не понимаете их значения и влияния.", + "vectorModelConfig": "Настройки векторной модели", + "modelFilePath": "Путь к файлу модели", + "quantizationType": "Тип квантования", + "quantizationPlaceholder": "Введите тип квантования", + "memoryParams": "Параметры памяти", + "messagesPerSummary": "Сообщений для запуска сжатия", + "messagesPerSummaryHelp": "Сохраняет контекст последних N сообщений.", + "shortTermLimit": "Лимит кратковременной памяти", + "shortTermLimitHelp": "Количество кандидатов памяти, возвращаемых при поиске.", + "summaryMaxLength": "Макс. длина сжатия", + "summaryMaxLengthHelp": "Максимальное количество символов при сжатии сообщений.", + "summaryLimit": "Лимит запросов сжатых сообщений", + "summaryLimitHelp": "Разрешенное количество сжатых сообщений для запроса.", + "ragLimit": "Лимит поиска RAG", + "ragLimitHelp": "Количество сообщений, извлекаемых при поиске.", + "deepRetrieveSummaryLimit": "Лимит извлечения векторов сжатия", + "deepRetrieveSummaryLimitHelp": "Количество сообщений, получаемых при поиске содержимого сжатых сообщений.", + "saveConfig": "Сохранить", + "clearMemory": "Очистить память", + "restoreDefault": "По умолчанию", + "msg": { + "saved": "Настройки памяти сохранены", + "clearConfirmTitle": "Подтверждение очистки", + "clearConfirmBody": "Это удалит глобальные данные памяти ИИ без возможности восстановления. Продолжить?", + "confirmClear": "Очистить", + "cancel": "Отмена", + "cleared": "Память очищена", + "clearFailed": "Не удалось очистить память" + }, + "modelMap": { + "name": "Название модели", + "model": "Модель", + "type": "тип", + "editWord": "Связать подсказку", + "operation": "действовать", + "bindingSuccessful": "Привязка прошла успешно", + "bindingFailed": "Привязка не удалась", + "currentBinding": "текущая привязка", + "noBinding": "Не связан", + "bound": "Граница", + "unbind": "Отвязать", + "filenName": "Имя файла" + } + }, + "login": { + "username": "Имя пользователя", + "usernamePlaceholder": "Введите имя пользователя", + "password": "Пароль", + "passwordPlaceholder": "Введите пароль", + "modify": "Изменить", + "msg": { + "enterUsername": "Пожалуйста, введите имя пользователя", + "usernameLength": "Имя пользователя должно содержать 2-20 символов", + "enterPassword": "Пожалуйста, введите пароль", + "passwordLength": "Пароль должен содержать 6-20 символов", + "fetchFailed": "Не удалось получить информацию о пользователе", + "saveSuccess": "Успешно сохранено", + "saveFailed": "Ошибка сохранения" + } + }, + "db": { + "clearDb": "Очистить базу данных", + "clearDbDesc": "Очистить данные во всех таблицах, сохранив их структуру", + "clearData": "Очистить данные", + "confirmAction": "Подтверждение", + "dbInfo": "Обзор базы данных", + "dbInfoDesc": "Просмотр имён таблиц и количества записей", + "viewInfo": "Просмотр", + "tableName": "Имя таблицы", + "rowCount": "Кол-во записей", + "totalTables": "Всего таблиц: {count}", + "exportDb": "Экспорт базы данных", + "exportDbDesc": "Экспортировать все таблицы в JSON-файл резервной копии", + "exportData": "Экспорт", + "importDb": "Импорт базы данных", + "importDbDesc": "Восстановить данные из JSON-файла (текущие данные будут перезаписаны)", + "importData": "Импорт", + "clearTable": "Очистить таблицу", + "clearTableDesc": "Выберите таблицу и очистите её данные", + "clearTableBtn": "Очистить", + "selectTable": "Выберите таблицу", + "msg": { + "clearDbTitle": "Очистка базы данных", + "firstConfirm": "Вы уверены, что хотите очистить все таблицы? Данные нельзя будет восстановить!", + "secondConfirm": "Это последнее предупреждение. Все данные будут потеряны навсегда!", + "keyword": "Очистить", + "confirm": "Подтвердить", + "pleaseInput": "Пожалуйста, введите", + "cleared": "Все таблицы очищены", + "operationFailed": "Ошибка операции, попробуйте снова", + "cancelled": "Операция отменена", + "exportSuccess": "База данных успешно экспортирована", + "exportFailed": "Ошибка экспорта", + "importSuccess": "База данных успешно импортирована, перенаправление на страницу входа", + "importFailed": "Ошибка импорта", + "invalidFile": "Недопустимый файл резервной копии", + "clearTableSuccess": "Таблица очищена", + "clearTableFailed": "Не удалось очистить таблицу", + "clearTableConfirm": "Очистить таблицу {name}? Это действие необратимо!", + "importConfirm": "Импорт перезапишет все текущие данные. Продолжить?", + "importSecondConfirm": "Последнее подтверждение: все текущие данные будут заменены!", + "noTableSelected": "Сначала выберите таблицу", + "loadingDbInfo": "Загрузка информации о базе данных...", + "loadDbInfoFailed": "Не удалось загрузить информацию о базе данных" + } + }, + "other": { + "requestTimeout": "Тайм-аут запроса", + "seconds": "Сек", + "inputSeconds": "Введите секунды", + "assetConcurrency": "Параллельная генерация ассетов", + "count": "Шт", + "inputCount": "Введите количество", + "chapterRegex": "Регулярное выражение для разбивки на главы", + "restoreDefault": "По умолчанию", + "regexPlaceholder": "Введите регулярное выражение", + "showTitleBar": "показать строку заголовка", + "isElectron": "Переключиться в режим рабочего стола", + "canvasScroll": "Прокрутка холста", + "canvasIsDisabled": "Масштабирование холста", + "agentCanvasScalingMethod": "Неограниченное использование колеса холста на рабочей странице", + "zoom": "Увеличить", + "scroll": "прокрутка", + "isInteracting": "Неограниченная оптимизация производительности перетаскивания холста на рабочей странице", + "closeIsInteracting": "закрытие" + }, + "request": { + "warning": "Не изменяйте без крайней необходимости", + "apiAddress": "URL API", + "apiPlaceholder": "Введите URL-адрес запроса API", + "save": "Сохранить", + "reset": "Сброс", + "msg": { + "enterApi": "Пожалуйста, введите URL API", + "validUrl": "Пожалуйста, введите действительный HTTP/HTTPS адрес", + "saved": "URL запроса успешно сохранен", + "reset": "Сброшено на адрес по умолчанию", + "refreshFailed": "Обновить не удалось", + "refreshSuccess": "Обновить успешно" + }, + "refresh": "обновить" + }, + "about": { + "slogan": "Опенсорсный ИИ-инструмент для создания комиксов и раскадровок", + "latestVersion": "У вас установлена последняя версия", + "checkUpdate": "Проверить обновления", + "codeRepository": "Репозиторий кода", + "githubRepo": "Репозиторий GitHub", + "giteeRepo": "Репозиторий Gitee", + "versionUpdate": "Обновление версии", + "checkUpdateGithub": "Проверить (GitHub)", + "getFromGithub": "Скачать последний релиз с GitHub", + "checkUpdateGitee": "Проверить (Gitee)", + "getFromGitee": "Скачать последний релиз с Gitee", + "license": "Лицензия", + "licenseDesc": "Лицензионное соглашение · Нажмите для подробностей", + "updateAvailable": "найдена новая версия", + "upToDate": "Обнаружена новая версия", + "confirmReinstall": "Скопировать ссылку", + "reinstallRequired": "Браузер автоматически откроется и загрузится. Если он не открывается, пожалуйста, откройте его вручную." + }, + "logout": { + "warning": "После выхода вам нужно будет снова войти в систему.", + "confirmLogout": "Вы уверены, что хотите выйти?", + "logout": "Выйти", + "msg": { + "logoutSuccess": "Успешный выход", + "logoutFailed": "Ошибка выхода, попробуйте снова" + } + }, + "file": { + "quickOpen": "Быстро открыть каталог", + "open": "Открыть", + "dockerDesc": "Для Docker/раздельного развертывания перейдите в каталог \"/data/*\" для управления файлами.", + "desktopOnly": "Эта функция доступна только в десктопной версии", + "folders": { + "data": "data", + "dataDesc": "Каталог данных.", + "logs": "data/logs", + "logsDesc": "Журналы выполнения и ошибок.", + "oss": "data/oss", + "ossDesc": "Ресурсы файлового хранилища.", + "skills": "data/skills", + "skillsDesc": "Файлы конфигурации навыков и промптов.", + "models": "data/models", + "modelsDesc": "Файлы моделей и конфигурации.", + "web": "data/web", + "webDesc": "Веб-ресурсы, например, сборка фронтенда.", + "serve": "data/serve", + "serveDesc": "Файлы бэкенд-сервисов." + }, + "openFailed": "Не удалось открыть папку" + }, + "skill": { + "scanSkills": "ScanSkills", + "fileLost": "Файл отсутствует" + }, + "dev": { + "warning": "Ниже приведены инструменты разработчика, будьте осторожны!", + "openDevtool": "Открыть", + "devtoolsDoc": "Адрес документа", + "devtoolsDesc": "После включения в каталоге установки Toonflow будет создана папка .devtools. Убедитесь, что у Toonflow есть права на запись (запуск от имени администратора).", + "devtoolsDesc2": "Запустите npx {'@'}ai-sdk/devtools в этом каталоге, чтобы включить отладку телеметрии.", + "openDevtoolFailed": "Не удалось открыть инструменты разработчика. Убедитесь, что установлен рабочий стол Toonflow.", + "notInElectron": "Для веб-среды откройте консоль браузера вручную." + } + }, + "workbench": { + "selectProject": "Пожалуйста, выберите проект", + "menu": { + "myProject": "Мои проекты", + "taskCenter": "Центр задач", + "novel": "Текст романа", + "scriptAgent": "Сценарий Agent", + "scriptManage": "Управление сценариями", + "cornerScape": "Персонажи и сцены", + "production": "Создание видео", + "assetCenter": "Центр ассетов", + "settings": "Настройки", + "jumpGithub": "Перейти на Гитхаб", + "feedbackQuestions": "Вопрос обратной связи" + }, + "project": { + "title": "Мои проекты", + "subtitle": "Управление всеми проектами коротких драм", + "newProject": "Новый проект", + "dialog": { + "editTitle": "Редактировать проект", + "addTitle": "Новый проект", + "save": "Сохранить", + "ok": "ОК", + "cancel": "Отмена", + "projectType": "Тип проекта", + "selectType": "Выберите тип", + "basedOnNovel": "На основе текста романа", + "projectName": "Название проекта", + "projectNamePh": "Введите название проекта", + "novelType": "Жанр романа", + "novelTypePh": "Например: Фэнтези, Фантастика, Романтика", + "artStyle": "Визуальное руководство", + "selected": "Выбрано:", + "selectArtStyle": "Пожалуйста, выберите визуальное руководство", + "newArtStyle": "Новое визуальное руководство", + "loading": "Загрузка...", + "videoRatio": "Соотношение сторон", + "novelIntro": "Синопсис романа", + "novelIntroPh": "Введите синопсис романа", + "editArtStyleTitle": "Редактировать визуальное руководство", + "newArtStyleTitle": "Новое визуальное руководство", + "artStyleName": "Визуальное название руководства", + "artStyleNamePh": "Пожалуйста, введите название визуального руководства", + "artStyleImage": "Обложка визуального руководства", + "remove": "Удалить", + "uploadCover": "Загрузить обложку", + "artStylePrompt": "Слова подсказки визуального руководства", + "aiExtract": "Извлечь промпт (ИИ)", + "promptPlaceholder": "Описывает слово-подсказку визуального руководства, используемое для указания визуального руководства при создании изображений.", + "visualManual": "Визуальное руководство", + "newVisualManual": "Новое визуальное руководство", + "editVisualManualTitle": "Редактировать визуальное руководство", + "newVisualManualTitle": "Новое визуальное руководство", + "visualManualName": "Название визуального руководства", + "visualManualNamePh": "Пожалуйста, введите название визуального руководства", + "visualManualCover": "Обложка визуального руководства", + "visualManualPrompt": "Промпт визуального руководства", + "modelData": "Выберите модель изображения", + "videoModelData": "Выберите модель видео", + "prompt": { + "placeholder": "Введите слово-подсказку", + "saveSuccess": "Обновление успешно выполнено", + "title": "подсказать слово" + }, + "basedOnScript": "на основе сценария", + "mdFile": "визуальный файл руководства", + "directorManual": "Справочник директора", + "addDirectorManual": "Новое руководство директора", + "editingDirectorManual": "Редактировать Руководство режиссера", + "newDirecorManualTitle": "Новое руководство директора", + "directorManualPrompt": "Слова-подсказки в руководстве режиссера", + "directorManualName": "Название руководства режиссера", + "directorManualNamePh": "Введите название руководства режиссера", + "directorFile": "Документ «Руководство директора»", + "directorManualCover": "Обложка руководства режиссера" + }, + "msg": { + "fetchFailed": "Не удалось получить список проектов", + "notFound": "Проект не найден!", + "editSuccess": "Проект успешно отредактирован", + "editFailed": "Ошибка редактирования проекта", + "addSuccess": "Проект успешно создан", + "addFailed": "Ошибка создания проекта", + "deleteHeader": "Удалить проект", + "deleteBody": "Вы уверены, что хотите удалить этот проект?", + "deleteConfirm": "Удалить", + "deleteCancel": "Отмена", + "deleteSuccess": "Проект успешно удален", + "deleteFailed": "Ошибка удаления проекта", + "extractSuccess": "Промпт успешно извлечен", + "extractFailed": "Ошибка извлечения", + "enterArtStyleName": "Пожалуйста, введите название визуального руководства", + "artStyleUpdated": "Обновлено визуальное руководство", + "artStyleAdded": "Добавлен визуальный мануал", + "operationFailed": "Ошибка операции", + "enterVisualManualName": "Пожалуйста, введите название визуального руководства", + "enterVisualManualImage": "Пожалуйста, загрузите обложку визуального руководства", + "enterVisualManualTabData": "Промпт не может быть пустым", + "visualManualUpdated": "Визуальное руководство обновлено", + "visualManualAdded": "Визуальное руководство добавлено", + "deleteVisualManualHeader": "Удалить визуальное руководство", + "deleteVisualManualBody": "Вы уверены, что хотите удалить визуальное руководство \"{name}\"?", + "deleteVisualManualConfirm": "Удалить", + "deleteVisualManualCancel": "Отмена", + "enterProjectName": "Пожалуйста, введите название проекта", + "enterProjectIntro": "Пожалуйста, введите вступление к роману", + "enterProjectType": "Пожалуйста, введите тип проекта", + "enterArtStyle": "Пожалуйста, выберите визуальную брошюру проекта", + "enterVideoRatio": "Пожалуйста, выберите соотношение видео", + "enterImageModel": "Пожалуйста, выберите модель изображения", + "enterVideoModel": "Пожалуйста, выберите модель видео", + "visualManualDeleted": "Удалить успешно", + "selectMode": "Пожалуйста, выберите режим", + "deleteDirectorManualHeader": "Удалить руководство режиссера", + "deleteDirectorManualBody": "Вы уверены, что хотите удалить Руководство режиссёра «{name}»?", + "directorManualUpdated": "Обновлено Руководство директора", + "directorManualAdded": "Добавлено Руководство режиссера", + "directorManual": "Пожалуйста, выберите Руководство для директора проекта", + "modelProviderDisabled": "Поставщик видеомодели или модели изображения не включен или поставщик модели отсутствует, сначала настройте его." + }, + "type": { + "novel": "По мотивам оригинального романа", + "script": "По новому сценарию" + } + }, + "novel": { + "importText": "Импорт текста", + "batchDelete": "Пакетное удаление", + "eventAnalysis": "Анализ событий", + "searchPlaceholder": "Поиск по названию...", + "search": "Поиск", + "generating": "Генерация...", + "genFailed": "Ошибка генерации", + "none": "Нет", + "edit": "Редактировать", + "delete": "Удалить", + "col": { + "id": "№", + "reel": "Том", + "chapter": "Название главы", + "chapterData": "Содержимое главы", + "event": "Событие", + "operation": "Действие" + }, + "msg": { + "batchDeleteHeader": "Пакетное удаление", + "batchDeleteBody": "Вы уверены, что хотите удалить выбранные {count} элементов?", + "batchDeleteSuccess": "Пакетное удаление завершено", + "deleteHeader": "Подтверждение удаления", + "deleteBody": "Удалить главу под названием «{name}»?", + "deleteSuccess": "Успешно удалено", + "eventAnalysisHeader": "Анализ событий", + "eventAnalysisBody": "Выполнить анализ событий для выбранных {count} элементов?" + }, + "import": { + "title": "Загрузить текст романа", + "step1": "Шаг 1", + "step2": "Шаг 2", + "step3": "Шаг 3", + "dragUpload": "Перетащите файл с романом сюда или нажмите для загрузки", + "uploadHint": "Поддерживаются форматы .txt, .docx. Рекомендуемый размер файла до 10 МБ", + "or": "ИЛИ", + "pasteLabel": "Вставить текст напрямую", + "pastePlaceholder": "Вставьте текст романа сюда", + "chars": "симв.", + "tooShort": "Слишком короткий текст, рекомендуется не менее 100 символов", + "parsedChapters": "Распознано {count} глав", + "nextStep": "Далее", + "prevStep": "Назад", + "selectedInfo": "Выбрано: {count} симв. (Максимум 200 000)", + "eventAnalysis": "Анализ событий", + "saveAndAnalyze": "Сохранить текст и анализировать", + "col": { + "chapter": "Глава", + "reel": "Том", + "chapterName": "Название главы", + "chapterData": "Содержимое главы" + }, + "msg": { + "parseFailed": "Не удалось распознать файл. Загрузите заново", + "selectFile": "Выберите файл", + "docNotSupported": "Файлы .doc не поддерживают синтаксический анализ, конвертируйте их в файлы .ts.", + "unsupportedType": "Неподдерживаемый тип файла", + "fileTooLarge": "Файл больше 10 МБ. Загрузите файл меньшего размера", + "selectChapters": "Сначала выберите главы", + "saveSuccess": "Текст романа успешно сохранен" + }, + "importAdd": "Перетащите файлы сюда или нажмите, чтобы загрузить", + "limit": "Поддержка формата .ts" + }, + "editDialog": { + "title": "Редактировать текст романа", + "chapterName": "Название главы", + "chapterNamePh": "Введите название главы", + "eventContent": "Содержание события", + "eventContentPh": "Введите содержание события", + "chapterContent": "Содержимое главы", + "chapterContentPh": "Введите содержимое главы", + "cancel": "Отмена", + "save": "Сохранить", + "msg": { + "updateSuccess": "Текст романа успешно обновлен" + } + }, + "event": { + "regenerate": "Перегенерировать события", + "batchDelete": "Пакетное удаление", + "noData": "Нет данных о событиях. Нажмите, чтобы начать генерацию", + "generate": "Сгенерировать события", + "generatingHint": "Генерация событий, пожалуйста, подождите...", + "loading": "Загрузка...", + "delete": "Удалить", + "col": { + "id": "ID события", + "eventName": "Название события", + "chapters": "Исходная глава", + "detail": "Детали события", + "createTime": "Время создания", + "operation": "Действие" + }, + "msg": { + "deleteHeader": "Удалить событие", + "deleteBody": "Вы уверены, что хотите удалить это событие?", + "deleteSuccess": "Успешно удалено", + "generateSuccess": "События успешно сгенерированы", + "batchDeleteHeader": "Пакетное удаление", + "batchDeleteBody": "Вы уверены, что хотите удалить выбранные {count} элементов?", + "batchDeleteSuccess": "Пакетное удаление завершено" + } + }, + "analysis": { + "analyzeFirst": "Пожалуйста, сначала проанализируйте события", + "startAnalysis": "Начать анализ", + "chapterHeader": "Глава {index} - {name}", + "analyzing": "Анализ событий" + } + }, + "scriptAgent": { + "inputPlaceholder": "Введите текст", + "chapterEvents": "События главы", + "clearMessageMemory": "Очистить память сообщений", + "clearSummaryMemory": "Очистить память сжатий", + "clearAllMemory": "Очистить всю память", + "edit": "Редактировать", + "storySkeleton": "Скелет истории", + "adaptationStrategy": "Стратегия адаптации", + "script": "Сценарий", + "noContent": "Нет содержимого", + "relatedAssets": "Связанные ассеты", + "editScript": "Редактировать сценарий", + "save": "Сохранить", + "scriptTitle": "Заголовок", + "titlePlaceholder": "Введите заголовок", + "content": "Содержимое", + "contentPlaceholder": "Введите текст сценария", + "selectAssets": "Выбрать ассеты", + "noAssets": "Нет связанных ассетов", + "selectAssetsTitle": "Выбор связанных ассетов", + "welcomeMsg": "Привет! Я ИИ-ассистент Toonflow. Хотите, чтобы я начал генерировать сценарий?", + "start": "Начать", + "memoryType": { + "message": "Память сообщений", + "summary": "Память сжатий", + "all": "Вся память" + }, + "msg": { + "clearConfirm": "Подтверждение очистки", + "clearBody": "Вы уверены, что хотите очистить {type}? Это действие необратимо.", + "confirmClear": "Очистить", + "cancel": "Отмена", + "memoryCleared": "{type} очищена", + "scriptUpdated": "Сценарий успешно обновлен", + "scriptUpdateFailed": "Не удалось обновить сценарий, попробуйте позже", + "searchScriptFailed": "Не удалось найти сценарии", + "updated": "Сохранено успешно.", + "error": "Сохранить не удалось", + "reconnect": "Восстановить соединение", + "notReconnect": "Подтвердить, что разговор при повторном подключении будет прерван?", + "keepReconnect": "подтверждать", + "deleteConfirm": "Удалить подтверждение", + "deleteBody": "Удалить текст", + "confirmDelete": "Подтвердить удаление", + "scriptDeleted": "Скрипт удален." + }, + "reconnect": "Восстановить соединение" + }, + "cornerScape": { + "batchSettings": "Пакетные настройки", + "quickActions": "Быстрые команды", + "selectUngenerated": "Выбрать несгенерированные", + "selectGenerated": "Выбрать сгенерированные", + "selectFailed": "Выбрать с ошибками", + "invertSelection": "Инвертировать выбор", + "clearSelection": "Снять выделение", + "batchPreview": "Пакетный предпросмотр", + "assetTypeFilter": "Фильтр по типу", + "genModel": "Модель генерации", + "resolution": "Разрешение", + "resolutionPh": "Выберите разрешение", + "concurrency": "Потоки", + "concurrencyPh": "Введите количество потоков", + "startBatch": "Начните генерировать изображения в пакетном режиме", + "waitingGen": "В очереди", + "generating": "Генерация", + "genFailed": "Ошибка генерации", + "imageError": "Ошибка изображения", + "typeRole": "Персонаж", + "typeScene": "Сцена", + "typeTool": "Предмет", + "typeUnknown": "Неизвестно", + "descriptionSuffix": "Описание: ", + "operateScriptFirst": "Пожалуйста, сначала обработайте сценарий", + "individualConfig": "Индивидуальные настройки", + "noImage": "Нет изображения", + "promptLabel": "Промпт", + "promptPh": "Введите промпт", + "aiPolish": "Улучшить с ИИ", + "regenerate": "Перегенерировать", + "filterRole": "Персонаж", + "filterScene": "Сцена", + "filterTool": "Предмет", + "unnamed": "Без имени", + "noDescription": "Нет описания", + "msg": { + "selectModel": "Выберите модель для генерации", + "selectResolution": "Выберите разрешение", + "enterPrompt": "Введите промпт", + "enterPromptFirst": "Сначала введите промпт", + "genSuccess": "{name} успешно сгенерирован", + "genFailed": "Ошибка генерации {name}", + "promptGenSuccess": "Промпт успешно сгенерирован", + "polishFailed": "Не удалось улучшить, попробуйте снова", + "selectAtLeastOne": "Выберите хотя бы один ассет для пакетной генерации", + "batchStarted": "Начата пакетная генерация. Всего: {count}, Потоков: {concurrent}", + "batchItemFailed": "Ошибка генерации {name}: {error}", + "batchComplete": "Пакетная генерация завершена", + "batchFailed": "Генерация пакета не удалась", + "replaceFailed": "Замена не удалась", + "replaceSuccess": "Замена прошла успешно", + "promptGenFail": "Не удалось создать быстрое слово.", + "saveSuccess": "Изменение слова подсказки успешно выполнено", + "saveFailed": "Изменение слова подсказки не удалось" + }, + "history": "исторические фотографии", + "confirmReplace": "Подтвердить замену", + "batchGenerationPrompt": "Генерируйте подсказки в пакетном режиме", + "generatingPrompt": "Создание", + "selectAll": "Выбрать все", + "selectPromptEmpty": "Выбрать все подсказки. Слово пусто.", + "noEmptyPrompt": "Нет ресурсов с пустым словом-подсказкой.", + "selectedCount": "Выбрано {count} объектов", + "cancelGeneration": "Отменить генерацию", + "selectGenerating": "Выберите создаваемый элемент", + "noGenerating": "Данные не генерируются", + "checkNumber": "Выберите количество" + }, + "script": { + "searchPlaceholder": "Поиск по названию сценария...", + "search": "Поиск", + "addScript": "Новый сценарий", + "cancelSelectAll": "Снять выбор со всех", + "selectAll": "Выбрать все", + "exportScript": "Экспорт сценария", + "msg": { + "searchFailed": "Не удалось найти сценарии", + "selectExport": "Сначала выберите сценарий для экспорта", + "exportSuccess": "Экспорт завершен", + "exportFailed": "Ошибка экспорта сценария", + "deleteHeader": "Подтверждение удаления", + "deleteBody": "Вы уверены, что хотите удалить этот сценарий? Это действие необратимо.", + "deleteConfirm": "Удалить", + "cancel": "Отмена", + "deleteSuccess": "Успешно удалено", + "deleteFailed": "Ошибка удаления", + "selectDelScript": "Пожалуйста, выберите удаление сценария", + "batchDeleteHeader": "Массовое удаление", + "batchDeleteBody": "Вы уверены, что хотите удалить выбранные {count} сценариев? Это действие необратимо.", + "batchDeleteSuccess": "Массовое удаление выполнено успешно", + "extractingInProgress": "Извлечение", + "projectNotFound": "Товар не найден", + "selectsExport": "Пожалуйста, выберите экспорт скрипта" + }, + "add": { + "title": "Добавить сценарий", + "scriptName": "Название сценария", + "scriptNamePh": "Введите название сценария", + "uploadFile": "Загрузить файл", + "dragUpload": "Перетащите файл сценария сюда или нажмите для загрузки", + "uploadHint": "Поддерживаются форматы .txt, .docx. Рекомендуемый размер файла до 10 МБ", + "scriptContent": "Текст сценария", + "scriptContentPh": "Загрузите или введите текст сценария...", + "relatedAssets": "Связанные ассеты", + "selectAssets": "Выбрать ассеты", + "noAssets": "Нет связанных ассетов", + "cancel": "Отмена", + "confirm": "ОК", + "msg": { + "fileReadFailed": "Ошибка чтения файла", + "docNotSupported": "Формат .doc не поддерживается. Конвертируйте в .txt или .docx", + "unsupportedType": "Неподдерживаемый тип файла", + "fileTooLarge": "Файл больше 10 МБ. Загрузите файл меньшего размера", + "parsing": "Распознавание файла...", + "parseFailed": "Не удалось распознать файл. Загрузите заново", + "selectAssetsTitle": "Выбор связанных ассетов", + "enterContent": "Пожалуйста, загрузите или введите текст сценария", + "enterName": "Пожалуйста, введите название сценария", + "addSuccess": "Сценарий успешно добавлен", + "addFailed": "Не удалось добавить сценарий, попробуйте позже" + } + }, + "edit": { + "title": "Детали сценария", + "scriptName": "Название сценария", + "scriptNamePh": "Введите название сценария", + "scriptContent": "Текст сценария", + "scriptContentPh": "Введите текст сценария...", + "relatedAssets": "Связанные ассеты", + "selectAssets": "Выбрать ассеты", + "noAssets": "Нет связанных ассетов", + "msg": { + "selectAssetsTitle": "Выбор связанных ассетов", + "updateSuccess": "Сценарий успешно обновлен", + "updateFailed": "Не удалось обновить сценарий, попробуйте позже" + } + }, + "deleteScript": "Удаление скриптов в пакетном режиме", + "extractAssets": "", + "import": { + "episodeRegexPh": "Настройте правило разделения сценария. Оставьте это поле пустым, чтобы использовать правило разделения по умолчанию (по умолчанию используется разделение в соответствии с форматом Episode X)." + } + }, + "assets": { + "addPrefix": "Добавить", + "batchGenerate": "Пакетная генерация", + "generatePrompt": "Сгенерировать промпт", + "generateImage": "Сгенерировать изображение", + "batchDelete": "Пакетное удаление", + "searchPlaceholder": "Поиск по названию ассета...", + "search": "Поиск", + "preview": "Предпросмотр", + "generate": "Генерация", + "edit": "Редактировать", + "delete": "Удалить", + "generating": "Генерация", + "play": "Воспроизведение", + "mediaPreview": "Предпросмотр медиа", + "confirmBatch": "Подтвердите {type}!", + "model": "Модель", + "resolution": "Разрешение", + "resolutionPh": "Выберите разрешение", + "batchGenPrompt": "Пакетная генерация промптов", + "batchGenImage": "Пакетная генерация изображений", + "role": "Персонаж", + "prop": "Предмет", + "scene": "Сцена", + "clip": "Клип", + "uploadSuccess": "Успешно загружено", + "selectAtLeastOne": "Выберите хотя бы один ассет", + "noDescription": "Нет описания", + "promptGenSuccess": "Промпт для «{name}» успешно сгенерирован", + "promptGenFail": "Ошибка генерации промпта для «{name}»: {error}", + "selectModel": "Пожалуйста, выберите модель", + "selectResolution": "Пожалуйста, выберите разрешение", + "noPromptForImage": "У «{name}» нет промпта; невозможно сгенерировать изображение", + "imageGenSuccess": "Изображение для «{name}» успешно сгенерировано", + "imageGenFail": "Ошибка генерации изображения для «{name}»: {error}", + "confirmDeleteHeader": "Подтверждение удаления", + "confirmBatchDeleteBody": "Вы уверены, что хотите удалить эти ассеты? Это действие необратимо.", + "confirmDeleteBody": "Вы уверены, что хотите удалить этот ассет? Это действие необратимо.", + "deleteBtn": "Удалить", + "cancelBtn": "Отмена", + "deleteSuccess": "Ассет успешно удален", + "deleteFail": "Не удалось удалить ассет", + "colPreview": "Превью", + "colName": "Название", + "colPrompt": "Промпт", + "colDescribe": "Описание", + "colRemark": "Примечание", + "colCreateTime": "Время создания", + "colOperation": "Действие", + "add": { + "name": "Название", + "namePh": "Введите название", + "describe": "Описание", + "describePh": "Введите описание", + "remark": "Примечание", + "remarkPh": "Введите примечание", + "prompt": "Промпт", + "promptPh": "Введите промпт", + "nameRequired": "Пожалуйста, введите название", + "describeRequired": "Пожалуйста, введите детали", + "remarkRequired": "Пожалуйста, введите примечание", + "updateSuccess": "Ассет успешно обновлен", + "addSuccess": "Ассет успешно добавлен" + }, + "gen": { + "header": "Генерация изображения", + "uploadRef": "Загрузить референс", + "optional": "Необязательно", + "promptLabel": "Промпт", + "smartGenerate": "Смарт-генерация", + "generatingPrompt": "Умная генерация промпта...", + "promptPlaceholder": "Опишите изображение, которое хотите сгенерировать. Например: футуристичный город будущего, неоновые огни, киберпанк...", + "selectModel": "Выбрать модель", + "selectResolution": "Выбрать разрешение", + "generateBtn": "Сгенерировать", + "resultTitle": "Результаты", + "generatedCount": "Сгенерировано {count} шт. Пожалуйста, выберите одно", + "generatingLabel": "Генерация...", + "genFailed": "Ошибка генерации", + "confirmSelect": "Подтвердить выбор", + "promptSuccess": "Промпт успешно сгенерирован", + "promptFail": "Ошибка генерации промпта", + "fillPrompt": "Пожалуйста, введите промпт", + "pickResolution": "Пожалуйста, выберите разрешение", + "pickModel": "Пожалуйста, выберите модель", + "unnamed": "Без имени", + "assetGenSuccess": "Ассет успешно сгенерирован", + "assetGenFail": "Ошибка генерации ассета", + "uploadOk": "Успешно загружено", + "imageSelected": "Изображение выбрано", + "imageDeleted": "Изображение удалено", + "imageSaved": "Изображение сохранено", + "completed": "Завершенный" + }, + "batch": { + "header": "Пакетная генерация", + "selected": "Выбрано {count} шт.", + "selectAll": "Выбрать все", + "clearSelection": "Очистить выбор", + "inputPh": "Введите текст", + "saveSelected": "Сохранить выбранное ({count})", + "colPreviewImg": "Превью", + "selectToSave": "Выберите элементы для сохранения", + "saveSuccess": "Успешно сохранено", + "saveFail": "Ошибка сохранения, попробуйте снова", + "promptDone": "Генерация промптов завершена", + "promptFail": "Ошибка генерации промптов", + "missingPrompts": "{count} ассетов не имеют промптов. Сначала сгенерируйте промпты", + "imageDone": "Генерация изображений завершена", + "imageGenFail": "Ошибка генерации изображений", + "unknownError": "Неизвестная ошибка", + "promptGenCancelled": "Генерация отменена" + }, + "confirmCancellation": "Подтвердить отмену", + "confirmAgain": "Подтвердить отмену? После отмены серверный ИИ продолжит требовать вычетов.", + "sure": "Конечно" + }, + "production": { + "selectPlaceholder": "Выберите эпизод", + "edit": "Редактировать", + "node": { + "script": { + "title": "Сценарий", + "editDialog": "Редактировать сценарий" + }, + "scriptPlan": { + "title": "План съемок", + "editDialog": "Редактировать план съемок" + }, + "storyboard": { + "title": "Панель раскадровки", + "notGenerated": "Не сгенерировано", + "scaleRatio": "Масштаб", + "gridPreview": "Просмотр сеткой", + "noPreviewImages": "Нет изображений для предпросмотра", + "imageLoadFailed": "Не удалось загрузить изображение", + "promptPlaceholder": "Пожалуйста, введите слово-подсказку", + "prompt": "подсказать слово", + "editInfo": "Подскажите изменение слова" + }, + "storyboardTable": { + "title": "Таблица раскадровки", + "editDialog": "Редактировать таблицу раскадровки" + }, + "assets": { + "title": "Производные ассеты", + "generateFailed": "Ошибка генерации", + "notGenerated": "Не сгенерировано", + "originalAsset": "Оригинал", + "derived": "Производные", + "noDerivedAssets": "Нет производных ассетов" + }, + "poster": { + "title": "Обложка видео", + "coverCount": "{count} шт." + }, + "workbench": { + "title": "Рабочий стол видео" + } + }, + "editImage": { + "upload": "Загрузить", + "generate": "Сгенерировать", + "saveFailed": "Ошибка сохранения, попробуйте снова", + "fetchFailed": "Не удалось получить данные", + "generating": "Генерация...", + "deleteNode": "Удалить узел", + "ratio": "Соотношение", + "quality": "Качество", + "generateBtn": "Сгенерировать изображение", + "selectImage": "Выбрать изображение", + "imageGeneration": "Генерация изображения", + "promptPlaceholder": "Опишите изображение, которое хотите сгенерировать...", + "imageRef": "Изображение {index}", + "noReferences": "Нет доступных референсов", + "selectModel": "Сначала выберите модель", + "selectQuality": "Выберите качество", + "selectRatio": "Выберите соотношение", + "generateFailed": "Ошибка генерации", + "generateFirst": "Сначала сгенерируйте изображение", + "generatedResult": "Результаты", + "waitingGenerate": "В очереди", + "layoutLR": "Авторазметка - горизонтально", + "layoutTB": "Авторазметка - вертикально", + "uploadAssetImage": "Загрузить изображение ресурса", + "uploadStoryboardImage": "Загрузить изображение раскадровки", + "uploadImage": "Загрузка изображения объекта", + "mode": "модель", + "closeConfirmTitle": "Закрыть панель редактирования?", + "closeConfirmBody": "Несохраненные данные будут потеряны после закрытия." + }, + "save": "Выбирать", + "cancel": "Отмена", + "chatBox": { + "inputPlaceholder": "Введите сообщение...", + "generateDerivedAssets": "Сгенерировать производные ассеты", + "welcomeMessage": "Привет! Я ваш ИИ-ассистент. Чем могу помочь?", + "adjustModel": "Настроить модель", + "startMakingVideo": "Начать создание видео", + "startMakingVideoPrompt": "Пожалуйста, помоги мне начать создание видео", + "clearMessageMemory": "Очистить память сообщений", + "clearSummaryMemory": "Очистить память сжатий", + "clearAllMemory": "Очистить всю память", + "messageMemory": "Память сообщений", + "summaryMemory": "Память сжатий", + "allMemory": "Вся память", + "confirmClear": "Очистить память", + "confirmClearBody": "Вы уверены, что хотите очистить {type}?", + "confirmClearBtn": "Подтвердить очистку", + "memoryCleared": "{type} очищена" + }, + "wb": { + "quickPreview": "Быстрый предпросмотр", + "videoGeneration": "Раскадровка", + "videoEditing": "монтажный стол", + "hint": "Подсказка", + "extractLines": "Извлечь реплики из видео?", + "no": "Нет", + "confirm": "Да", + "extractLinesQuestion": "Хотите извлечь диалоги из видео в качестве субтитров?", + "importingLoading": "Импорт, пожалуйста, подождите...", + "mainTrackVideo": "Основная дорожка (Видео)", + "subtitle1": "Субтитры 1", + "storyboardVideoName": "{раскадровка}-{id}.mp4" + }, + "preview": { + "noImage": "Нет изображения", + "storyboardDesc": "Описание раскадровки", + "serialNumber": "№", + "noDescription": "Нет описания", + "duration": "Длит.", + "seconds": "с", + "relatedAssets": "Связанные ассеты", + "role": "Персонаж", + "prop": "Предмет", + "scene": "Сцена", + "noCharacters": "Без персонажей", + "imagePrompt": "Промпт изображения", + "selectAll": "Выбрать все", + "exportImage": "Экспорт изображений", + "sceneDescription": "Описание кадра", + "promptLabel": "Промпт", + "restoreSort": "Сбросить сортировку", + "restoreSortConfirm": "Вы уверены, что хотите вернуть исходную сортировку?", + "tip": "Подсказка", + "selectAtLeastOne": "Пожалуйста, выберите хотя бы один кадр для экспорта", + "exportFilename": "Раскадровки изображений" + }, + "generate": { + "noVideo": "Нет видео", + "videoPrompt": "Промпт видео", + "promptPlaceholder": "Введите слова-подсказки, описывающие видеоконтент, который вы хотите создать...", + "refImage": "Референс", + "image": "Изображение", + "refVideo": "Референс видео", + "refImageLabel": "Референс изображения", + "refAudio": "Референс аудио", + "muteAudio": "Выключить звук", + "enableAudio": "Включить звук", + "resolution": "Разрешение", + "duration": "Длит.", + "generate": "Сгенерировать", + "historyVersions": "История версий", + "confirmSelection": "Подтвердить выбор", + "noHistory": "Нет истории", + "generating": "Генерация", + "generateFailed": "Ошибка генерации", + "selectAll": "Выбрать все", + "videoTrack": "Видеодорожка", + "batchGenerate": "Пакетная генерация", + "importToEditor": "Импорт в редактор", + "modeSingleImage": "Одно изображение", + "modeMultiImage": "Много изображений", + "modeGridImage": "Сетка изображений", + "modeStartEnd": "Первый и последний кадры", + "modeText": "Текст в видео", + "modeVideoRef": "По видео-референсу", + "modeImageRef": "По изображению", + "modeAudioRef": "По аудио-референсу", + "modeTextRef": "По тексту", + "startFrame": "Первый кадр", + "startFrameOptional": "Первый кадр (Необязательно)", + "endFrame": "Последний кадр", + "endFrameOptional": "Последний кадр (Необязательно)", + "selectRefImage": "Выбрать референс", + "selectRefImages": "Выбрать референсы", + "selectEndFrame": "Выбрать последний кадр", + "selectRefVideoAsset": "Выбрать видео-референс", + "selectRefAudioAsset": "Выбрать аудио-референс", + "selectRefImageAsset": "Выбрать изображение-референс", + "selectImageSource": "Выбрать источник изображения", + "fromStoryboard": "Раскадровка", + "fromStoryboardDesc": "Выбрать изображение из раскадровки", + "fromAssets": "Ресурс", + "fromAssetsDesc": "Выбрать изображение из библиотеки ресурсов", + "confirmDelete": "Подтверждение удаления", + "confirmDeleteBody": "Вы уверены, что хотите удалить это видео? Это действие необратимо.", + "delete": "Удалить", + "cancel": "Отмена", + "deleteSuccess": "Видео успешно удалено", + "deleteFailed": "Ошибка удаления", + "selectVideoFirst": "Пожалуйста, сначала выберите видео", + "confirmSuccess": "Выбор подтвержден", + "batchSubmitted": "Запрос на пакетную генерацию отправлен. В обработке...", + "configNotFound": "Конфигурация не найдена", + "pollingFailed": "Не удалось выполнить запрос статуса видео. Обновите вручную.", + "batchGeneratePrompt": "Генерируйте подсказки в пакетном режиме", + "batchPromptEmpty": "Раскадровка {name} доступна для видеоподсказок. Пожалуйста, сначала создайте или заполните приглашение", + "modelEmpty": "Сначала выберите модель создания видео", + "generatingPrompt": "Интеллектуальное генерирование подсказок" + }, + "editVideo": { + "reset": "Сброс", + "undo": "Отменить", + "redo": "Повторить", + "split": "Разделить", + "delete": "Удалить", + "rendering": "Рендеринг...", + "exportVideo": "Экспорт видео", + "exportSuccess": "Экспорт видео завершен", + "exportFailed": "Ошибка экспорта", + "sampleSubtitle": "Пример текста субтитров", + "customText": "Пользовательский текст", + "transitionBetweenClips": "Переходы должны добавляться между двумя соседними клипами", + "transitionExists": "Переход уже существует в этом месте", + "videoPreviewArea": "Область предпросмотра", + "clipMaterials": "Материалы клипа", + "propertyPanel": "Панель свойств", + "selectClip": "Выберите клип для просмотра свойств", + "basicInfo": "Основная информация", + "name": "Название", + "clipNamePlaceholder": "Название клипа", + "startTime": "Начало", + "endTime": "Конец", + "totalDuration": "Общая длительность", + "videoProperties": "Свойства видео", + "opacity": "Непрозрачность", + "volume": "Громкость", + "playbackSpeed": "Скорость", + "audioProperties": "Свойства аудио", + "fadeIn": "Плавное появление", + "fadeOut": "Плавное затухание", + "transitionProperties": "Свойства перехода", + "transitionType": "Тип перехода", + "transFade": "Затухание", + "transSlide": "Сдвиг", + "transWipe": "Вытеснение", + "transDissolve": "Растворение", + "transZoom": "Масштаб", + "transRotate": "Вращение", + "transitionDuration": "Длительность перехода", + "subtitleProperties": "Свойства субтитров", + "textContent": "Текст", + "fontSize": "Размер шрифта", + "copy": "Копировать", + "deleteConfirm": "Подтверждение удаления", + "deleteClipConfirm": "Вы уверены, что хотите удалить этот клип?", + "avCanvasNotInit": "AVCanvas не инициализирован", + "noExportContent": "Нет содержимого для экспорта", + "exportProject": "Экспорт проекта", + "transitionAdded": "Переход добавлен: {name}", + "splitClip": "Разделить клип", + "deleteClip": "Удалить клип", + "addClip": "Добавить {name}", + "duplicateClip": "Дублировать клип", + "addTransition": "Добавить переход", + "updateClip": "Обновить клип {key}", + "updatePlaybackRate": "Изменить скорость на {rate}x", + "updateTransitionDuration": "Обновить длительность перехода", + "playbackRateRange": "Скорость воспроизведения должна быть от 0.1 до 10", + "updatePlaybackRateFailed": "Ошибка изменения скорости:", + "importProject": "Импорт проекта", + "import": "Импорт" + }, + "clipType": { + "video": "Видео", + "audio": "Аудио", + "subtitle": "Субтитры", + "transition": "Переход", + "sticker": "Стикер", + "filter": "Фильтр", + "effect": "Эффект" + }, + "track": { + "video": "Видео", + "image": "Изображение", + "audio": "Аудио", + "subtitle": "Субтитры", + "text": "Текст", + "sticker": "Стикер", + "filter": "Фильтр", + "effect": "Эффект" + }, + "transition": { + "fade": "Затухание", + "slide": "Сдвиг", + "slideLeft": "Сдвиг влево", + "slideRight": "Сдвиг вправо", + "slideUp": "Сдвиг вверх", + "slideDown": "Сдвиг вниз", + "wipe": "Вытеснение", + "wipeLeft": "Вытеснение влево", + "wipeRight": "Вытеснение вправо", + "wipeUp": "Вытеснение вверх", + "wipeDown": "Вытеснение вниз", + "dissolve": "Растворение", + "zoom": "Масштаб", + "zoomIn": "Приближение", + "zoomOut": "Отдаление", + "rotate": "Вращение", + "circle": "Круг", + "diamond": "Ромб", + "clock": "Часы", + "blur": "Размытие" + }, + "media": { + "titleText": "Текст заголовка", + "subtitleText": "Текст субтитров", + "customText": "Пользовательский текст", + "media": "Медиа", + "image": "Изображение", + "audio": "Аудио", + "subtitle": "Субтитры", + "transition": "Переход", + "effect": "Эффект", + "filter": "Фильтр", + "loading": "Загрузка...", + "subtitlePreview": "Т", + "video": "видео" + }, + "effect": { + "fadeIn": "Появление", + "fadeOut": "Затухание", + "flash": "Вспышка", + "shake": "Тряска", + "zoomIn": "Приближение (эффект)", + "zoomOut": "Отдаление (эффект)", + "pulse": "Пульсация", + "rotateIn": "Вращение (вход)", + "sticker1": "Стикер 1", + "sticker2": "Стикер 2" + }, + "filter": { + "grayscale": "Ч/Б", + "sepia": "Сепия", + "warm": "Теплый", + "cool": "Холодный", + "vivid": "Яркий", + "bright": "Светлый", + "highContrast": "Контрастный", + "blur": "Размытие", + "invert": "Инверсия", + "semiTransparent": "Полупрозрачный" + }, + "guideSwitchEpisode": "Переключение эпизодов", + "guideSwitchEpisodeBody": "Переключение эпизодов перенесено сюда!", + "autoLayoutLR": "Автоматический набор текста — левая и правая раскладка", + "autoLayoutTB": "Автоматический набор текста — верхняя и нижняя раскладка", + "getFlowData": "Обновить рабочую область", + "confirm": "Подтверждение переключения эпизода", + "confirmEpisodesSwitch": "Текущая задача все еще находится в стадии выполнения. Переключение эпизодов приведет к переподключению сеанса. Продолжить переключение?" + }, + "task": { + "title": "Список задач", + "subtitle": "История выполнения ваших задач", + "refresh": "Обновить", + "categoryLabel": "Категория:", + "stateLabel": "Статус:", + "noFailReason": "Нет причины ошибки", + "stateAll": "Все", + "stateRunning": "В процессе", + "stateCompleted": "Завершено", + "stateFailed": "Ошибка", + "fetchFailed": "Не удалось получить список задач", + "col": { + "taskClass": "Категория задачи", + "relatedObjects": "Связанные объекты", + "model": "Модель", + "describe": "Описание", + "state": "Статус", + "startTime": "Время", + "reason": "Причина неудачи" + }, + "project": "Название проекта:" + }, + "noVideo": "Видео пока нет", + "prompt": "Видео слова-подсказки", + "generateText": "ИИ генерировал слова-подсказки", + "selectStoryboard": "Выберите раскадровку", + "generate": { + "noVideo": "Данных пока нет", + "generateText": "ИИ генерировал слова-подсказки", + "selectStoryboard": "Выберите раскадровку", + "generate": "Создать видео", + "history": "Историческая версия", + "generating": "Создание", + "generateFailed": "Проверьте причину сбоя", + "selectAll": "Выбрать все", + "selected": "Выбрано", + "batchGenerateText": "Генерируйте подсказки в пакетном режиме", + "batchGenerateVideo": "Генерируйте видео в пакетном режиме", + "importVideo": "Импортировать в редакторский стол", + "emptyTrack": "Абзац {index}", + "del": "удалить", + "delConfirm": "Вы уверены, что хотите удалить этот абзац?", + "selectSource": "Выберите источник", + "confirm": "Выбрать из активов", + "cancel": "Выбрать из раскадровки", + "selectVideoFailed": "Не удалось выбрать видео.", + "selectVideoSuccess": "Видео выбрано успешно", + "previewVideo": "Превью видео", + "selectTrackFirst": "Пожалуйста, сначала выберите раскадровку", + "noSelectedVideo": "Видео не выбрано", + "generateConfirm": "Подтвердить генерацию", + "generateConfirmBody": "Подтвердите создание видео", + "generateVideosInBatches": "Генерируйте видео в пакетном режиме", + "generateStarted": "Начало сборки", + "promptEmpty": "Проверьте данные, необходимые для создания видео, и слово подсказки пусто. Хотите ли вы продолжить его создание?", + "skipDataWithEmptyVideoPromptWords": "Видеоданные, которые необходимо сгенерировать, существуют, а слово подсказки пусто.", + "duration": "продолжительность", + "resolution": "разрешение", + "delVideo": "Подтвердить удаление этого видео?", + "delSuccess": "Удалить успешно", + "addReference": "добавить ссылку", + "promptPlaceholder": "Пожалуйста, введите слово-подсказку к видео", + "downloadVideo": "Скачивайте видео партиями", + "selectVideo": "Пожалуйста, проверьте видео, которое вы хотите скачать", + "batchDownloadVideo": "Скачивайте видео партиями", + "storyboard": "Раскадровка", + "assets": "ресурсы", + "promptText": "Генерация данных слов видеоподсказки", + "videoMenu": "Создать видео", + "videoPreview": "Предварительный просмотр видео", + "referenceImage": "Справочные изображения", + "generatePrompt": "Генерируйте слова-подсказки", + "generateVideo": "Создать видео" + } + }, + "login": { + "slogan": "Умная платформа для создания коротких драм", + "tips": "Аккаунт по умолчанию: admin / admin123", + "settings": "Настройки сервера", + "requestAddress": "URL запроса", + "username": "Имя пользователя", + "password": "Пароль", + "login": "Войти", + "usernameRequired": "Пожалуйста, введите имя пользователя", + "passwordRequired": "Пожалуйста, введите пароль", + "enterUsernameAndPassword": "Пожалуйста, введите имя пользователя или пароль", + "loginSuccess": "Успешный вход", + "settingsSaved": "Настройки сохранены" + }, + "common": { + "cancel": "Отмена", + "confirm": "Подтвердить", + "selectAssets": "Выбрать ресурсы", + "sessionExpired": "Сессия истекла, пожалуйста, войдите снова", + "openSettings": "Открыть настройки", + "defaultReel": "Основной том", + "save": "сохранять", + "submitting": "Отправка", + "editSuccess": "Модификация прошла успешно", + "editFailed": "Модификация не удалась", + "submit": "представлять на рассмотрение" + }, + "components.storyboardImageCheck.camera": "Камера", + "components.storyboardImageCheck.dialogTitle": "Выбрать изображение раскадровки", + "components.storyboardImageCheck.preview": "Предпросмотр", + "components.storyboardImageCheck.src": "Предпросмотр изображения", + "components.storyboardImageCheck.title": "Заголовок", + "components.storyboardImageCheck.duration": "Длительность", + "components.storyboardImageCheck.lines": "Реплики", + "components.storyboardImageCheck.createTime": "Дата создания", + "workbench.script.extractAssets": "Извлечь ресурсы", + "promptManage": { + "prompt": "подсказать слово" + }, + "hello": { + "welcomeTitle": "Добро пожаловать в ToonFlow", + "welcomeDesc": "Платформа рабочего процесса создания комиксов, управляемая искусственным интеллектом, давайте потратим минуту, чтобы завершить первоначальную настройку.", + "startConfig": "Начать настройку", + "skip": "Пропустить загрузку", + "configModel": "Добавить модельный сервис", + "configData": "Настроить агент", + "startUse": "Начать", + "configModelTitle": "Добавить поставщика услуг модели", + "configModelDesc": "Сначала вам необходимо добавить в настройках хотя бы одного поставщика услуг модели ИИ (например, OpenAI, Claude и т. д.) и заполнить соответствующий ключ API.", + "configModelTip": "Нажатие кнопки ниже откроет вкладку «Сервис модели» на странице настроек. После добавления поставщика вернитесь сюда, чтобы продолжить.", + "configModelBtn": "Перейти к настройке сервиса модели", + "configAgentTitle": "Назначить модель агента", + "configAgentDesc": "Затем назначьте модели каждому функциональному модулю в конфигурации агента, чтобы система знала, какую модель вызывать для выполнения задачи.", + "configAgentTip": "Нажатие кнопки ниже откроет вкладку «Конфигурация агента» на странице настроек. Вернитесь сюда после назначения моделей каждой функции.", + "configAgentBtn": "Перейти к настройке агента", + "finishTitle": "🎉Все готово!", + "finishDesc": "Настройка завершена, и теперь вы можете начать использовать все функции. Если вам нужно его настроить, вы можете изменить его в настройках в любое время.", + "qrcodeLabel": "Присоединяйтесь к группе общения WeChat, чтобы получить дополнительную помощь:", + "githubLabel": "Если вы найдете это полезным, пожалуйста, поставьте нам ⭐ Звезду!", + "prevStep": "Предыдущий шаг", + "nextStep": "Следующий шаг", + "finish": "Начать" + }, + "setting": { + "skillManagement": { + "search": "Поиск по имени файла", + "empty": "Нет подходящих файлов", + "edit": "редактировать", + "selectOnTheLeft": "Пожалуйста, выберите файл слева" + } + }, + "storyboard": { + "assets": { + "notExist": "Актив не существует", + "notDerivativeExist": "Производные активы не существуют", + "derivativeUpdateSuccess": "Обновление успешно выполнено", + "derivativeState": "Не создано", + "derivativeAddSuccess": "Добавлено успешно", + "derivativeDelSuccess": "Удалить успешно", + "notGenerated": "Не создано" + }, + "addSuccess": "Обновление успешно выполнено", + "state": { + "unused": "Не создано" + }, + "saveSuccess": "Добавлено успешно" + }, + "productionAgent": { + "generating": "Создание" + }, + "skillScan": { + "scanning": "🔍 Парсинг и загрузка навыков", + "scanComplete": "✨ Сканирование Skill завершено", + "inserted": "✅ Добавлено {count} навыков", + "updated": "🔄 Обновлено {count} Skill", + "removed": "🗑️ Удалено {count} Skill", + "scannedFiles": "📁 Просканировано {count} файлов", + "noDescription": "📝 {count} Skill без описания", + "noAttribution": "👤 {count} Skill без атрибуции", + "configWarning": "⚠️ Предупреждение конфигурации Skill", + "openSettings": "Открыть настройки", + "scanFailed": "❌ Сканирование не удалось", + "checkNetwork": "🔌 Проверьте сетевое подключение или повторите попытку позже", + "retryLater": "🔁 Повторите попытку позже" + }, + "generate": "Создать видео", + "history": "Историческая версия", + "generating": "Создание", + "generateFailed": "неудача", + "selectAll": "Выбрать все", + "selected": "Выбрано", + "importVideo": "Импортировать видео", + "emptyTrack": "Абзац {индекс 1}", + "del": "Подтвердить удаление", + "delConfirm": "Вы уверены, что хотите удалить этот абзац?", + "selectSource": "Выберите источник", + "confirm": "Выбрать из активов", + "cancel": "Выбрать из раскадровки", + "workbench.script.msg.exportFailed": "Экспорт не удался", + "workbench.production.node.assets.confirmDeleteBody": "Подтвердите, следует ли удалить объект", + "workbench.production.node.assets.removeFailed": "Не удалось удалить объект.", + "version": { + "newVersion": "Вышла новая версия, хотите ее обновить?" + }, + "workbench.production.generatedNode.localUpload": "Локальная загрузка", + "workbench.production.editImage.uploadFailed": "Не удалось загрузить изображение.", + "workbench.production.editImage.noImage": "Пожалуйста, сначала добавьте фотографии", + "workbench.script.batchAddScript": "Загружайте скрипты партиями", + "workbench.script.import.pasteLabel": "Вставьте содержимое скрипта напрямую", + "workbench.script.import.col.scriptName": "Имя сценария", + "workbench.script.import.col.scriptData": "Содержимое сценария", + "workbench.script.import.episodeRegex": "Правила разделения скриптов", + "workbench.script.import.episodeRegexPh": "Настройте правило разделения сценария. Оставьте это поле пустым, чтобы использовать правило разделения по умолчанию (по умолчанию используется разделение в соответствии с форматом Episode X).", + "workbench.script.import.regexInvalid": "Неверный формат регулярного выражения", + "workbench.script.import.parsedChapters": "{count} набор проанализирован", + "workbench.script.import.msg.selectChapters": "Пожалуйста, сначала проверьте сценарий", + "workbench.script.import.msg.saveSuccess": "Скрипт успешно сохранен", + "workbench.script.import.batchTitle": "Загружайте скрипты партиями", + "workbench.assets.sex": "пол", + "workbench.assets.audioText": "аудиоконтент", + "workbench.assets.audio": "Аудио", + "workbench.assets.add.sex": "пол", + "workbench.assets.add.sexPh": "Пожалуйста, укажите пол", + "settings.agent.advanced": "Расширенная конфигурация", + "settings.agent.ordinary": "Простая настройка", + "settings.agent.temperature": "температура", + "settings.agent.maxOutputTokens": "Максимальный выходной токен", + "settings.agent.auto": "Авто", + "settings.agent.manual": "Вручную", + "settings.agent.autoHint": "Длина вывода определяется моделью", + "settings.agent.msg.notmodel": "Модель не выбрана", + "workbench.production.node.storyboard.generateImage": "Создание раскадровки", + "workbench.generate.notSelectMode": "Пожалуйста, сначала выберите модель", + "workbench.production.node.storyboard.pleaseSelectImage": "Пожалуйста, сначала выберите раскадровку", + "workbench.cornerScape.audioState": "Связывание", + "workbench.generate.generateError": "Не удалось инициировать запрос на сборку.", + "settings.vendor.videoGenerating": "Создание видео происходит медленно, пожалуйста, подождите терпеливо", + "settings.memory.modelMap.editRefeshWord": "перевязать", + "settings.memory.modelMap.delPrompt": "удалить", + "settings.vendor.testModel": "тест" +} diff --git a/web-core/src/locales/language/th_TH.json b/web-core/src/locales/language/th_TH.json new file mode 100644 index 0000000..c43085e --- /dev/null +++ b/web-core/src/locales/language/th_TH.json @@ -0,0 +1,1536 @@ +{ + "components": { + "editMdPreivew": { + "title": "แก้ไข", + "confirm": "บันทึก", + "cancel": "ยกเลิก" + }, + "imageTools": { + "copy": "คัดลอกรูปภาพ", + "preview": "ดูตัวอย่าง", + "download": "ดาวน์โหลด", + "msg": { + "imageLoadFailed": "โหลดรูปภาพล้มเหลว", + "convertFailed": "แปลงไฟล์ล้มเหลว", + "copied": "คัดลอกไปยังคลิปบอร์ดแล้ว", + "copyFailed": "คัดลอกล้มเหลว", + "downloadFailed": "ดาวน์โหลดล้มเหลว", + "downloadStarted": "เริ่มการดาวน์โหลด", + "downloadBlockedOpenNewWindow": "แหล่งที่มารูปภาพปัจจุบันอาจจำกัดการดาวน์โหลด ระบบได้ลองเปิดในหน้าต่างใหม่แล้ว" + } + }, + "migrateShow": { + "title": "ย้ายข้อมูล", + "desc": "ตรวจพบว่าคุณมีข้อมูลจากเวอร์ชันเก่า ต้องการย้ายข้อมูลหรือไม่?", + "hide": "ไม่ต้องแสดงอีก", + "confirm": "ตกลง", + "msg": { + "migrateSuccess": "ย้ายข้อมูลสำเร็จ", + "migrateFailed": "ย้ายข้อมูลล้มเหลว" + } + }, + "modelSelect": { + "placeholder": "โปรดเลือกโมเดล", + "type": { + "image": "รูปภาพ", + "text": "ข้อความ", + "video": "วิดีโอ" + }, + "msg": { + "fetchModelFailed": "ดึงข้อมูลโมเดลล้มเหลว:" + }, + "goSetting": "ไปที่การตั้งค่าและเพิ่มโมเดล" + } + }, + "settings": { + "title": "การตั้งค่า ToonFlow", + "menu": { + "language": "ตั้งค่าภาษา", + "vendorConfig": "บริการโมเดล", + "agentConfig": "การตั้งค่า Agent", + "promptManage": "การจัดการพรอมต์", + "skillManagement": "จัดการซิลเลคต", + "memoryConfig": "หน่วยความจำ Agent", + "loginConfig": "การตั้งค่าเข้าสู่ระบบ", + "dbConfig": "จัดการฐานข้อมูล", + "fileManagement": "จัดการไฟล์", + "otherConfig": "การตั้งค่าอื่นๆ", + "requestConfig": "ที่อยู่คำขอ (API URL)", + "about": "ตรวจสอบการอัปเดต", + "logoutConfig": "ออกจากระบบ", + "skillsSkillsManagement": "การจัดการทักษะทักษะ" + }, + "language": { + "desc": "เลือกภาษาที่แสดงบนอินเทอร์เฟซ", + "msg": { + "saved": "บันทึกการตั้งค่าภาษาแล้ว" + } + }, + "vendor": { + "addVendor": "เพิ่มผู้ให้บริการ", + "noVendor": "ยังไม่มีผู้ให้บริการ โปรดเพิ่มก่อน", + "required": "จำเป็น", + "optionalSection": "ตัวเลือกเสริม", + "modelSettings": "การตั้งค่าโมเดล", + "addManually": "เพิ่มด้วยตนเอง", + "test": "ทดสอบ", + "edit": "แก้ไข", + "delete": "ลบ", + "deleteVendor": "ลบผู้ให้บริการ", + "editCode": "แก้ไขโค้ด", + "updateConfig": "อัปเดตการตั้งค่า", + "addModel": "เพิ่มโมเดล", + "editModel": "แก้ไขโมเดล", + "displayName": "ชื่อที่แสดง", + "displayNamePlaceholder": "ตัวอย่างเช่น: GPT-4o", + "modelId": "รหัสโมเดล (Model ID)", + "modelIdPlaceholder": "ตัวอย่างเช่น: gpt-4o", + "modelType": "ประเภทโมเดล", + "multimodal": "มัลติโมดัล (Multimodal)", + "supported": "รองรับ", + "notSupported": "ไม่รองรับ", + "toolCall": "การเรียกใช้เครื่องมือ", + "imageMode": "โหมดรูปภาพ", + "videoMode": "โหมดวิดีโอ", + "audioOutput": "เอาต์พุตเสียง", + "durationResolution": "การจับคู่ความยาว / ความละเอียด", + "durationSec": "ความยาว (วินาที)", + "resolution": "ความละเอียด", + "enterAndPress": "พิมพ์แล้วกด Enter", + "addDurationResolution": "เพิ่มชุดความยาว / ความละเอียด", + "testResult": "ผลการทดสอบ", + "generating": "กำลังสร้าง...", + "addVendorDialog": "เพิ่มผู้ให้บริการ", + "codeEditorInfo": "โปรดเขียนโค้ด TypeScript เพื่อตั้งค่าข้อมูลผู้ให้บริการ", + "reset": "รีเซ็ต", + "importFile": "นำเข้าไฟล์", + "textModel": "โมเดลข้อความ", + "imageModel": "โมเดลรูปภาพ", + "videoModel": "โมเดลวิดีโอ", + "textToImage": "สร้างภาพจากข้อความ", + "textToVideo": "สร้างวิดีโอจากข้อความ", + "singleImage": "รูปภาพเดียว", + "multiImage": "โหมดหลายรูปภาพ", + "multiReference": "อ้างอิงหลายรูปภาพ", + "multiReferenceMode": "โหมดอ้างอิงหลายแหล่ง", + "gridImage": "รูปภาพกริดเดียว", + "startEndRequired": "เฟรมแรกและสุดท้าย (จำเป็นต้องระบุทั้งสอง)", + "endFrameOptional": "เฟรมแรกและสุดท้าย (เฟรมสุดท้ายระบุหรือไม่ก็ได้)", + "startFrameOptional": "เฟรมแรกและสุดท้าย (เฟรมแรกระบุหรือไม่ก็ได้)", + "textRef": "ข้อความ", + "imageRef": "รูปภาพ", + "videoRef": "วิดีโอ", + "audioRef": "เสียง", + "audioOptional": "ตัวเลือก", + "audioOnly": "ส่งออกเฉพาะวิดีโอที่มีเสียง", + "noAudio": "ส่งออกเฉพาะวิดีโอที่ไม่มีเสียง", + "msg": { + "getVendorListFailed": "ดึงรายการผู้ให้บริการล้มเหลว", + "vendorConfigUpdated": "อัปเดตการตั้งค่าผู้ให้บริการแล้ว", + "updateFailed": "อัปเดตล้มเหลว", + "highRiskConfirm": "⚠️ ยืนยันการดำเนินการที่มีความเสี่ยงสูง", + "addVendorRiskBody": "การเพิ่มผู้ให้บริการ AI รายใหม่จะให้สิทธิ์ในการเข้าถึง API ของระบบ โปรดยืนยันว่าคุณเชื่อถือแหล่งที่มาของโค้ดจากผู้ให้บริการรายนี้!", + "iKnowRisk": "ฉันรับทราบความเสี่ยง", + "cancel": "ยกเลิก", + "confirmAgain": "⚠️ ยืนยันอีกครั้ง", + "addVendorConfirmBody": "คุณแน่ใจหรือไม่ว่าต้องการเพิ่มผู้ให้บริการรายนี้? หลังจากเพิ่มแล้ว ระบบจะนำไปใช้ในการจัดสรรโมเดล", + "confirmAndAdd": "ยืนยันและเพิ่ม", + "goBackCheck": "กลับไปตรวจสอบ", + "vendorAdded": "เพิ่มผู้ให้บริการสำเร็จแล้ว", + "addFailed": "เพิ่มล้มเหลว", + "updateVendorRiskBody": "การอัปเดตการตั้งค่าผู้ให้บริการ AI จะปรับเปลี่ยนสิทธิ์และพฤติกรรมการเข้าถึง API ของระบบ โปรดยืนยันว่าคุณเชื่อถือแหล่งที่มาของโค้ดที่ถูกแก้ไขนี้!", + "updateVendorConfirmBody": "คุณแน่ใจหรือไม่ว่าต้องการอัปเดตการตั้งค่าผู้ให้บริการนี้? การอัปเดตจะมีผลต่อการจัดสรรโมเดลของระบบ", + "confirmAndUpdate": "ยืนยันและอัปเดต", + "updateSuccess": "อัปเดตการตั้งค่าผู้ให้บริการสำเร็จ", + "fillDisplayName": "โปรดกรอกชื่อที่แสดง", + "fillModelId": "โปรดกรอกรหัสโมเดล", + "selectImageMode": "โปรดเลือกโหมดรูปภาพ", + "selectVideoMode": "โปรดเลือกโหมดวิดีโอ", + "groupPrefix": "กลุ่มที่ {n}:", + "addDuration": "โปรดเพิ่มความยาว", + "addResolution": "โปรดเพิ่มความละเอียด", + "selectVendorFirst": "โปรดเลือกผู้ให้บริการก่อน", + "modelIdExists": "รหัสโมเดลนี้มีอยู่แล้ว", + "modelAdded": "เพิ่มโมเดลสำเร็จแล้ว", + "modelUpdated": "อัปเดตโมเดลสำเร็จแล้ว", + "enterApiKey": "โปรดกรอก API KEY", + "enterApiUrl": "โปรดกรอก API URL", + "testSuccess": "ทดสอบสำเร็จ", + "imageGenSuccess": "สร้างรูปภาพสำเร็จ", + "videoGenSuccess": "สร้างวิดีโอสำเร็จ", + "requestFailed": "ส่งคำขอล้มเหลว", + "deleteModelConfirm": "ยืนยันการลบโมเดล", + "deleteModelBody": "หลังจากลบแล้วจะไม่สามารถกู้คืนได้ ต้องการดำเนินการต่อหรือไม่?", + "confirmDelete": "ยืนยันการลบ", + "modelDeleted": "ลบโมเดลแล้ว", + "deleteVendorConfirm": "ยืนยันการลบผู้ให้บริการ", + "deleteVendorBody": "หลังจากลบแล้ว โมเดลทั้งหมดภายใต้ผู้ให้บริการรายนี้จะถูกลบไปด้วย ต้องการดำเนินการต่อหรือไม่?", + "vendorDeleted": "ลบผู้ให้บริการแล้ว", + "deleteFailed": "ลบล้มเหลว", + "enabled": "เปิดใช้งานแล้ว", + "disabled": "พิการ", + "linkAddVendorRiskBody": "การเพิ่มผู้จำหน่าย AI ใหม่จะทำให้สามารถเข้าถึง API ระบบได้ โปรดตรวจสอบให้แน่ใจว่าคุณเชื่อถือแหล่งลิงก์ของผู้ขาย!", + "importAdd": "การเพิ่มผู้จำหน่าย AI ใหม่จะทำให้สามารถเข้าถึง API ระบบได้ โปรดตรวจสอบให้แน่ใจว่าคุณเชื่อถือแหล่งเอกสารประกอบของผู้ขาย!", + "linkAddFailed": "ไม่สามารถเพิ่มลิงก์" + }, + "think": "คิดลึก", + "code": "รหัส", + "linkAddPlaceholder": "ใส่ลิงค์เพื่อเพิ่ม", + "noFileSelected": "นำเข้าไฟล์เรียบร้อยแล้ว", + "linkAdd": "ยืนยัน" + }, + "agent": { + "bannerDesc": "ใช้เซิร์ฟเวอร์ตัวกลางอย่างเป็นทางการของ Toonflow รองรับการกรอกการตั้งค่าในคลิกเดียว พร้อมใช้งานทันทีโดยไม่ต้องตั้งค่าด้วยตนเอง", + "visitWebsite": "เข้าสู่เว็บไซต์", + "fillKey": "กรอก KEY", + "oneClickFill": "กรอกในคลิกเดียว", + "notOpen": "ยังไม่เปิดให้บริการ", + "notConfigured": "ยังไม่ได้ตั้งค่า", + "modelConfig": "การตั้งค่าโมเดล", + "confirm": "ยืนยัน", + "cancel": "ยกเลิก", + "selectModel": "เลือกโมเดล", + "fillKeyHeader": "กรอก KEY อย่างเป็นทางการจากแพลตฟอร์ม Toonflow", + "keyPlaceholder": "โปรดกรอก KEY", + "save": "บันทึก", + "msg": { + "notAvailable": "ฟีเจอร์นี้ยังไม่เปิดให้บริการ โปรดติดตามเร็วๆ นี้", + "configSuccess": "ตั้งค่าสำเร็จ", + "updateConfigFailed": "อัปเดตการตั้งค่าล้มเหลว:", + "keyValid": "KEY ถูกต้อง เชื่อมต่อกับแพลตฟอร์ม Toonflow สำเร็จแล้ว", + "keyInvalid": "KEY ไม่ถูกต้อง โปรดตรวจสอบและกรอกใหม่อีกครั้ง:", + "enterKey": "โปรดกรอก KEY", + "saveFailed": "บันทึกล้มเหลว:", + "getAgentListFailed": "ดึงรายการการตั้งค่า Agent ล้มเหลว:", + "toonflowNotFound": "ไม่มีสถานีรับส่งอย่างเป็นทางการของ Toonflow" + }, + "temperature": "อุณหภูมิ" + }, + "memory": { + "warning": "รายการการตั้งค่าต่อไปนี้ถูกกำหนดล่วงหน้าเป็นค่าที่แนะนำ เว้นแต่คุณจะเข้าใจความหมายและผลกระทบของการตั้งค่าแต่ละรายการอย่างชัดเจน ขอแนะนำให้คงการตั้งค่าปัจจุบันไว้", + "vectorModelConfig": "การตั้งค่าโมเดลเวกเตอร์ (Vector Model)", + "modelFilePath": "เส้นทางไฟล์โมเดล", + "quantizationType": "ประเภท Quantization", + "quantizationPlaceholder": "โปรดกรอกประเภท Quantization", + "memoryParams": "พารามิเตอร์หน่วยความจำ", + "messagesPerSummary": "จำนวนข้อความที่จะทริกเกอร์การบีบอัด", + "messagesPerSummaryHelp": "เก็บบริบทของบทสนทนา N รายการล่าสุด", + "shortTermLimit": "จำนวนข้อความที่ยังไม่ถูกบีบอัดในการดึงข้อมูลครั้งเดียว", + "shortTermLimitHelp": "จำนวนหน่วยความจำที่เป็นไปได้ที่จะถูกส่งคืนเมื่อทำการค้นหา", + "summaryMaxLength": "จำนวนตัวอักษรสูงสุดในการบีบอัด", + "summaryMaxLengthHelp": "จำนวนตัวอักษรสูงสุดที่อนุญาตเมื่อทำการบีบอัดข้อความ", + "summaryLimit": "จำนวนข้อความที่ถูกบีบอัดที่อนุญาตให้สืบค้น", + "summaryLimitHelp": "จำนวนข้อความที่ถูกบีบอัดที่อนุญาตให้ทำการสืบค้นได้", + "ragLimit": "จำนวนหน่วยความจำในการค้นหา", + "ragLimitHelp": "จำนวนข้อความที่จะดึงมาเมื่อทำการค้นหา", + "deepRetrieveSummaryLimit": "จำนวนข้อความที่ถูกบีบอัดที่จะเรียกคืนด้วยเวกเตอร์", + "deepRetrieveSummaryLimitHelp": "จำนวนข้อความที่จะดึงมาเมื่อทำการค้นหาเนื้อหาของข้อความที่ถูกบีบอัด", + "saveConfig": "บันทึกการตั้งค่า", + "clearMemory": "ล้างหน่วยความจำ", + "restoreDefault": "กู้คืนการตั้งค่าเริ่มต้น", + "msg": { + "saved": "บันทึกการตั้งค่าหน่วยความจำแล้ว", + "clearConfirmTitle": "ยืนยันการล้างหน่วยความจำ", + "clearConfirmBody": "การดำเนินการนี้จะล้างข้อมูลหน่วยความจำทั้งหมดของ AI และไม่สามารถกู้คืนได้ ต้องการดำเนินการต่อหรือไม่?", + "confirmClear": "ยืนยันการล้าง", + "cancel": "ยกเลิก", + "cleared": "ล้างหน่วยความจำแล้ว", + "clearFailed": "ล้างหน่วยความจำล้มเหลว" + }, + "modelMap": { + "name": "ชื่อรุ่น", + "model": "แบบอย่าง", + "type": "พิมพ์", + "editWord": "ผูกคำพร้อมท์", + "operation": "ดำเนินงาน", + "bindingSuccessful": "การผูกสำเร็จ", + "bindingFailed": "การเชื่อมโยงล้มเหลว", + "currentBinding": "การเชื่อมโยงปัจจุบัน", + "noBinding": "ไม่ผูกมัด", + "bound": "ผูกพัน", + "unbind": "เลิกผูก", + "filenName": "ชื่อไฟล์" + } + }, + "login": { + "username": "ชื่อผู้ใช้", + "usernamePlaceholder": "โปรดกรอกชื่อผู้ใช้", + "password": "รหัสผ่าน", + "passwordPlaceholder": "โปรดกรอกรหัสผ่าน", + "modify": "แก้ไข", + "msg": { + "enterUsername": "โปรดกรอกชื่อผู้ใช้", + "usernameLength": "ความยาวชื่อผู้ใช้ต้องอยู่ระหว่าง 2-20 ตัวอักษร", + "enterPassword": "โปรดกรอกรหัสผ่าน", + "passwordLength": "ความยาวรหัสผ่านต้องอยู่ระหว่าง 6-20 ตัวอักษร", + "fetchFailed": "ดึงข้อมูลผู้ใช้ล้มเหลว", + "saveSuccess": "บันทึกสำเร็จ", + "saveFailed": "บันทึกล้มเหลว" + } + }, + "db": { + "clearDb": "ล้างฐานข้อมูล", + "clearDbDesc": "ล้างข้อมูลทั้งหมดในตารางข้อมูล โดยคงไว้เพียงโครงสร้างตาราง", + "clearData": "ล้างข้อมูล", + "confirmAction": "ยืนยันการดำเนินการ", + "dbInfo": "ภาพรวมฐานข้อมูล", + "dbInfoDesc": "ดูชื่อตารางทั้งหมดและจำนวนแถว", + "viewInfo": "ดูข้อมูล", + "tableName": "ชื่อตาราง", + "rowCount": "จำนวนแถว", + "totalTables": "ทั้งหมด {count} ตาราง", + "exportDb": "ส่งออกฐานข้อมูล", + "exportDbDesc": "ส่งออกข้อมูลทั้งหมดเป็นไฟล์สำรอง JSON", + "exportData": "ส่งออกข้อมูล", + "importDb": "นำเข้าฐานข้อมูล", + "importDbDesc": "กู้คืนข้อมูลจากไฟล์สำรอง JSON (จะเขียนทับข้อมูลปัจจุบัน)", + "importData": "นำเข้าข้อมูล", + "clearTable": "ล้างตารางที่ระบุ", + "clearTableDesc": "เลือกตารางข้อมูลและล้างข้อมูลในตาราง", + "clearTableBtn": "ล้างตาราง", + "selectTable": "เลือกตาราง", + "msg": { + "clearDbTitle": "ล้างฐานข้อมูล", + "firstConfirm": "แน่ใจหรือไม่ว่าต้องการล้างข้อมูลในตารางทั้งหมด? ข้อมูลที่ถูกล้างจะไม่สามารถกู้คืนได้!", + "secondConfirm": "นี่คือการยืนยันครั้งสุดท้าย หลังจากล้างแล้วข้อมูลทั้งหมดจะสูญหายถาวร!", + "keyword": "ล้างข้อมูล", + "confirm": "ยืนยัน", + "pleaseInput": "โปรดพิมพ์", + "cleared": "ตารางข้อมูลทั้งหมดถูกล้างเรียบร้อยแล้ว", + "operationFailed": "การดำเนินการล้มเหลว โปรดลองอีกครั้ง", + "cancelled": "ยกเลิกการดำเนินการแล้ว", + "exportSuccess": "ส่งออกฐานข้อมูลสำเร็จ", + "exportFailed": "ส่งออกล้มเหลว", + "importSuccess": "นำเข้าฐานข้อมูลสำเร็จ กำลังไปที่หน้าเข้าสู่ระบบ", + "importFailed": "นำเข้าล้มเหลว", + "invalidFile": "ไฟล์สำรองไม่ถูกต้อง", + "clearTableSuccess": "ล้างตารางเรียบร้อยแล้ว", + "clearTableFailed": "ล้างตารางล้มเหลว", + "clearTableConfirm": "แน่ใจหรือไม่ว่าต้องการล้างตาราง {name}? การดำเนินการนี้ไม่สามารถย้อนกลับได้!", + "importConfirm": "การนำเข้าจะเขียนทับข้อมูลปัจจุบันทั้งหมด แน่ใจหรือไม่?", + "importSecondConfirm": "ยืนยันครั้งสุดท้าย: ข้อมูลปัจจุบันทั้งหมดจะถูกแทนที่หลังการนำเข้า!", + "noTableSelected": "โปรดเลือกตารางก่อน", + "loadingDbInfo": "กำลังโหลดข้อมูลฐานข้อมูล...", + "loadDbInfoFailed": "ไม่สามารถโหลดข้อมูลฐานข้อมูล" + } + }, + "other": { + "requestTimeout": "หมดเวลาคำขอ (Timeout)", + "seconds": "วินาที", + "inputSeconds": "โปรดกรอกเวลา (วินาที)", + "assetConcurrency": "จำนวนการสร้างสินทรัพย์พร้อมกัน (Concurrency)", + "count": "รายการ", + "inputCount": "โปรดกรอกจำนวนรายการ", + "chapterRegex": "นิพจน์ปกติสำหรับแยกตอน (Regex)", + "restoreDefault": "กู้คืนค่าเริ่มต้น", + "regexPlaceholder": "โปรดกรอกนิพจน์ปกติ (Regex)", + "showTitleBar": "แสดงแถบชื่อเรื่อง", + "isElectron": "สลับไปที่โหมดเดสก์ท็อป", + "canvasScroll": "เลื่อนผ้าใบ", + "canvasIsDisabled": "แคนวาสซูม", + "agentCanvasScalingMethod": "งานล้อผ้าใบตัวแทน", + "zoom": "ซูม", + "scroll": "เลื่อน", + "isInteracting": "การเพิ่มประสิทธิภาพการลากผ้าใบไม่จำกัดบนหน้าการผลิต", + "closeIsInteracting": "ปิด" + }, + "request": { + "warning": "หากไม่มีกรณีพิเศษ ไม่จำเป็นต้องแก้ไขหรือตั้งค่าใดๆ", + "apiAddress": "ที่อยู่ API", + "apiPlaceholder": "โปรดกรอกที่อยู่คำขอ API", + "save": "บันทึก", + "reset": "รีเซ็ต", + "msg": { + "enterApi": "โปรดกรอกที่อยู่ API", + "validUrl": "โปรดกรอกที่อยู่ HTTP/HTTPS ที่ถูกต้อง", + "saved": "บันทึกที่อยู่คำขอสำเร็จ", + "reset": "รีเซ็ตเป็นที่อยู่เริ่มต้นแล้ว", + "refreshFailed": "รีเฟรชล้มเหลว", + "refreshSuccess": "รีเฟรชสำเร็จ" + }, + "refresh": "รีเฟรช" + }, + "about": { + "slogan": "เครื่องมือสร้างการ์ตูน / สตอรี่บอร์ดที่ขับเคลื่อนด้วย AI แบบโอเพนซอร์ส", + "latestVersion": "นี่คือเวอร์ชันล่าสุด", + "checkUpdate": "ตรวจสอบการอัปเดต", + "codeRepository": "คลังเก็บโค้ด", + "githubRepo": "คลัง GitHub", + "giteeRepo": "คลัง Gitee", + "versionUpdate": "อัปเดตเวอร์ชัน", + "checkUpdateGithub": "ตรวจสอบการอัปเดต (GitHub)", + "getFromGithub": "รับเวอร์ชันล่าสุดจาก GitHub Release", + "checkUpdateGitee": "ตรวจสอบการอัปเดต (Gitee)", + "getFromGitee": "รับเวอร์ชันล่าสุดจาก Gitee Release", + "license": "ใบอนุญาต (License)", + "licenseDesc": "ข้อตกลงใบอนุญาตโอเพนซอร์ส · คลิกเพื่อดูรายละเอียด", + "updateAvailable": "พบเวอร์ชันใหม่แล้ว", + "upToDate": "ตรวจพบเวอร์ชันใหม่", + "confirmReinstall": "คัดลอกลิงก์", + "reinstallRequired": "เบราว์เซอร์จะเปิดและดาวน์โหลดโดยอัตโนมัติ หากไม่เปิดขึ้น โปรดเปิดด้วยตนเอง" + }, + "logout": { + "warning": "หลังจากออกจากระบบ คุณจะต้องเข้าสู่ระบบใหม่เพื่อใช้งานระบบต่อไป", + "confirmLogout": "แน่ใจหรือไม่ว่าต้องการออกจากระบบ?", + "logout": "ออกจากระบบ", + "msg": { + "logoutSuccess": "ออกจากระบบสำเร็จ", + "logoutFailed": "ออกจากระบบล้มเหลว โปรดลองอีกครั้ง" + } + }, + "file": { + "quickOpen": "เปิดไดเรกทอรีอย่างรวดเร็ว", + "open": "เปิด", + "dockerDesc": "สำหรับการปรับใช้ Docker/แยกส่วนหน้าและส่วนหลัง โปรดไปที่ไดเรกทอรี \"/data/*\" เพื่อจัดการไฟล์ด้วยตนเอง", + "desktopOnly": "ฟีเจอร์นี้รองรับเฉพาะเวอร์ชันเดสก์ท็อปเท่านั้น", + "folders": { + "data": "data", + "dataDesc": "ไดเรกทอรีข้อมูล", + "logs": "data/logs", + "logsDesc": "บันทึกการทำงานและบันทึกข้อผิดพลาด", + "oss": "data/oss", + "ossDesc": "ทรัพยากรที่เกี่ยวข้องกับการจัดเก็บไฟล์", + "skills": "data/skills", + "skillsDesc": "ไฟล์การตั้งค่าทักษะ (Skills) และพรอมต์", + "models": "data/models", + "modelsDesc": "ไฟล์โมเดลและการตั้งค่า", + "web": "data/web", + "webDesc": "ทรัพยากรเว็บ เช่น ผลลัพธ์จากการบิลด์ส่วนหน้า เป็นต้น", + "serve": "data/serve", + "serveDesc": "ไฟล์ที่เกี่ยวข้องกับบริการส่วนหลัง" + }, + "openFailed": "เปิดโฟลเดอร์ล้มเหลว" + }, + "skill": { + "scanSkills": "สแกนทักษะ", + "fileLost": "ไฟล์หายไป" + }, + "dev": { + "warning": "ต่อไปนี้เป็นเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ โปรดดำเนินการด้วยความระมัดระวัง!", + "openDevtool": "เปิด", + "devtoolsDoc": "ที่อยู่เอกสาร", + "devtoolsDesc": "หลังจากเปิดใช้งานแล้ว โฟลเดอร์ .devtools จะถูกสร้างขึ้นในไดเร็กทอรีการติดตั้ง Toonflow โปรดตรวจสอบให้แน่ใจว่า Toonflow มีสิทธิ์ในการเขียน (ทำงานในฐานะผู้ดูแลระบบ)", + "devtoolsDesc2": "เรียกใช้ npx {'@'}ai-sdk/devtools ในไดเรกทอรีนี้เพื่อเปิดใช้งานการแก้ไขจุดบกพร่องการวัดและส่งข้อมูลทางไกล", + "openDevtoolFailed": "ไม่สามารถเปิดเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์ได้ โปรดตรวจสอบให้แน่ใจว่าได้ติดตั้ง Toonflow desktop แล้ว", + "notInElectron": "สำหรับสภาพแวดล้อมแบบเว็บ โปรดเปิดคอนโซลเบราว์เซอร์ด้วยตนเอง" + } + }, + "workbench": { + "selectProject": "โปรดเลือกโปรเจกต์", + "menu": { + "myProject": "โปรเจกต์ของฉัน", + "taskCenter": "ศูนย์งาน", + "novel": "ต้นฉบับนิยาย", + "scriptAgent": "Agent บทภาพยนตร์", + "scriptManage": "จัดการบทภาพยนตร์", + "cornerScape": "สร้างตัวละครและฉาก", + "production": "ผลิตวิดีโอ", + "assetCenter": "ศูนย์สินทรัพย์", + "settings": "การตั้งค่า", + "jumpGithub": "ข้ามไปที่ Github", + "feedbackQuestions": "คำถามคำติชม" + }, + "project": { + "title": "โปรเจกต์ของฉัน", + "subtitle": "จัดการโปรเจกต์ละครสั้นทั้งหมดของคุณ", + "newProject": "โปรเจกต์ใหม่", + "dialog": { + "editTitle": "แก้ไขโปรเจกต์", + "addTitle": "โปรเจกต์ใหม่", + "save": "บันทึก", + "ok": "ตกลง", + "cancel": "ยกเลิก", + "projectType": "ประเภทโปรเจกต์", + "selectType": "เลือกประเภทโปรเจกต์", + "basedOnNovel": "สร้างจากต้นฉบับนิยาย", + "projectName": "ชื่อโปรเจกต์", + "projectNamePh": "โปรดกรอกชื่อโปรเจกต์", + "novelType": "ประเภทนิยาย", + "novelTypePh": "ตัวอย่างเช่น: แฟนตาซี, ไซไฟ, โรแมนติก", + "artStyle": "คู่มือภาพ", + "selected": "เลือกแล้ว:", + "selectArtStyle": "โปรดเลือกคู่มือแบบภาพ", + "newArtStyle": "คู่มือภาพใหม่", + "loading": "กำลังโหลด...", + "videoRatio": "อัตราส่วนวิดีโอ", + "novelIntro": "เรื่องย่อนิยาย", + "novelIntroPh": "โปรดกรอกเรื่องย่อนิยาย", + "editArtStyleTitle": "แก้ไขคู่มือภาพ", + "newArtStyleTitle": "คู่มือภาพใหม่", + "artStyleName": "ชื่อคู่มือแบบภาพ", + "artStyleNamePh": "โปรดป้อนชื่อคู่มือแบบภาพ", + "artStyleImage": "ภาพปกคู่มือภาพ", + "remove": "ลบออก", + "uploadCover": "อัปโหลดภาพปก", + "artStylePrompt": "ภาพพร้อมคำคู่มือ", + "aiExtract": "AI สกัดพรอมต์", + "promptPlaceholder": "อธิบายคำพร้อมท์คู่มือแบบภาพ ซึ่งใช้เพื่อระบุคู่มือแบบภาพเมื่อสร้างภาพ", + "visualManual": "คู่มือภาพ", + "newVisualManual": "คู่มือภาพใหม่", + "editVisualManualTitle": "แก้ไขคู่มือภาพ", + "newVisualManualTitle": "คู่มือภาพใหม่", + "visualManualName": "ชื่อคู่มือภาพ", + "visualManualNamePh": "โปรดป้อนชื่อคู่มือภาพ", + "visualManualCover": "ภาพปกคู่มือภาพ", + "visualManualPrompt": "Prompt คู่มือภาพ", + "modelData": "เลือกรุ่นรูปภาพ", + "videoModelData": "เลือกรุ่นวิดีโอ", + "prompt": { + "placeholder": "ป้อนคำที่พร้อมท์", + "saveSuccess": "อัปเดตสำเร็จ", + "title": "คำพูดที่รวดเร็ว" + }, + "basedOnScript": "ขึ้นอยู่กับสคริปต์", + "mdFile": "ไฟล์คู่มือภาพ", + "directorManual": "คู่มือกรรมการ", + "addDirectorManual": "คู่มือกรรมการใหม่", + "editingDirectorManual": "แก้ไขคู่มือกรรมการ", + "newDirecorManualTitle": "คู่มือกรรมการใหม่", + "directorManualPrompt": "คำพร้อมท์คู่มือผู้อำนวยการ", + "directorManualName": "ชื่อคู่มือผู้อำนวยการ", + "directorManualNamePh": "กรอกชื่อคู่มือผู้อำนวยการ", + "directorFile": "เอกสารคู่มือผู้อำนวยการ", + "directorManualCover": "ปกคู่มือผู้อำนวยการ" + }, + "msg": { + "fetchFailed": "ดึงรายการโปรเจกต์ล้มเหลว", + "notFound": "ไม่พบโปรเจกต์นี้!", + "editSuccess": "แก้ไขโปรเจกต์สำเร็จ", + "editFailed": "แก้ไขโปรเจกต์ล้มเหลว", + "addSuccess": "สร้างโปรเจกต์สำเร็จ", + "addFailed": "สร้างโปรเจกต์ล้มเหลว", + "deleteHeader": "ลบโปรเจกต์", + "deleteBody": "แน่ใจหรือไม่ว่าต้องการลบโปรเจกต์นี้?", + "deleteConfirm": "ลบ", + "deleteCancel": "ยกเลิก", + "deleteSuccess": "ลบโปรเจกต์สำเร็จ", + "deleteFailed": "ลบโปรเจกต์ล้มเหลว", + "extractSuccess": "สกัดพรอมต์สำเร็จ", + "extractFailed": "สกัดพรอมต์ล้มเหลว", + "enterArtStyleName": "โปรดป้อนชื่อคู่มือภาพ", + "artStyleUpdated": "อัปเดตคู่มือภาพแล้ว", + "artStyleAdded": "เพิ่มคู่มือภาพแล้ว", + "operationFailed": "การดำเนินการล้มเหลว", + "enterVisualManualName": "โปรดป้อนชื่อคู่มือภาพ", + "enterVisualManualImage": "โปรดอัปโหลดภาพปกคู่มือภาพ", + "enterVisualManualTabData": "Prompt ไม่สามารถว่างเปล่า", + "visualManualUpdated": "อัปเดตคู่มือภาพแล้ว", + "visualManualAdded": "เพิ่มคู่มือภาพแล้ว", + "deleteVisualManualHeader": "ลบคู่มือภาพ", + "deleteVisualManualBody": "คุณแน่ใจหรือไม่ว่าต้องการลบคู่มือภาพ \"{name}\"?", + "deleteVisualManualConfirm": "ลบ", + "deleteVisualManualCancel": "ยกเลิก", + "enterProjectName": "กรุณากรอกชื่อโครงการ", + "enterProjectIntro": "กรุณากรอกคำนำนวนิยาย", + "enterProjectType": "กรุณากรอกประเภทโครงการ", + "enterArtStyle": "โปรดเลือกโบรชัวร์ภาพโครงการ", + "enterVideoRatio": "โปรดเลือกอัตราส่วนวิดีโอ", + "enterImageModel": "กรุณาเลือกรุ่นรูปภาพ", + "enterVideoModel": "โปรดเลือกรุ่นวิดีโอ", + "visualManualDeleted": "ลบสำเร็จ", + "selectMode": "กรุณาเลือกโหมด", + "deleteDirectorManualHeader": "ลบคู่มือผู้อำนวยการ", + "deleteDirectorManualBody": "คุณแน่ใจหรือไม่ว่าต้องการลบคู่มือผู้อำนวยการ \"{name}\"?", + "directorManualUpdated": "ปรับปรุงคู่มือผู้อำนวยการแล้ว", + "directorManualAdded": "เพิ่มคู่มือผู้อำนวยการ", + "directorManual": "กรุณาเลือกคู่มือผู้อำนวยการโครงการ", + "modelProviderDisabled": "ไม่ได้เปิดใช้งานผู้จำหน่ายโมเดลวิดีโอหรือโมเดลรูปภาพ หรือไม่มีซัพพลายเออร์โมเดล โปรดกำหนดค่าก่อน" + }, + "type": { + "novel": "อิงจากนวนิยายต้นฉบับ", + "script": "อิงจากบทนวนิยาย" + } + }, + "novel": { + "importText": "นำเข้าต้นฉบับ", + "batchDelete": "ลบเป็นชุด", + "eventAnalysis": "วิเคราะห์เหตุการณ์", + "searchPlaceholder": "ค้นหาชื่อต้นฉบับ...", + "search": "ค้นหา", + "generating": "กำลังสร้าง...", + "genFailed": "สร้างล้มเหลว", + "none": "ไม่มี", + "edit": "แก้ไข", + "delete": "ลบ", + "col": { + "id": "ลำดับ", + "reel": "เล่ม", + "chapter": "ชื่อบท", + "chapterData": "เนื้อหาในบท", + "event": "เหตุการณ์", + "operation": "การจัดการ" + }, + "msg": { + "batchDeleteHeader": "ลบเป็นชุด", + "batchDeleteBody": "แน่ใจหรือไม่ว่าต้องการลบข้อมูลที่เลือกจำนวน {count} รายการ?", + "batchDeleteSuccess": "ลบเป็นชุดสำเร็จ", + "deleteHeader": "ยืนยันการลบ", + "deleteBody": "แน่ใจหรือไม่ว่าต้องการลบข้อมูลบทที่ชื่อว่า「{name}」?", + "deleteSuccess": "ลบสำเร็จ", + "eventAnalysisHeader": "วิเคราะห์เหตุการณ์", + "eventAnalysisBody": "แน่ใจหรือไม่ว่าต้องการวิเคราะห์เหตุการณ์ข้อมูลที่เลือกจำนวน {count} รายการ?" + }, + "import": { + "title": "อัปโหลดต้นฉบับนิยาย", + "step1": "ขั้นตอนที่ 1", + "step2": "ขั้นตอนที่ 2", + "step3": "ขั้นตอนที่ 3", + "dragUpload": "ลากไฟล์ต้นฉบับนิยายมาที่นี่ หรือคลิกเพื่ออัปโหลด", + "uploadHint": "รองรับรูปแบบ .txt, .docx แนะนำให้มีขนาดไฟล์ไม่เกิน 10MB", + "or": "หรือ", + "pasteLabel": "วางเนื้อหาต้นฉบับนิยายโดยตรง", + "pastePlaceholder": "โปรดกรอกเนื้อหาต้นฉบับนิยาย", + "chars": "ตัวอักษร", + "tooShort": "เนื้อหาสั้นเกินไป แนะนำให้อย่างน้อย 100 ตัวอักษร", + "parsedChapters": "แยกวิเคราะห์แล้ว {count} บท", + "nextStep": "ขั้นตอนถัดไป", + "prevStep": "ขั้นตอนก่อนหน้า", + "selectedInfo": "เลือกแล้ว: {count} ตัวอักษร (ต้องน้อยกว่า 200,000 ตัวอักษร)", + "eventAnalysis": "วิเคราะห์เหตุการณ์", + "saveAndAnalyze": "บันทึกต้นฉบับและวิเคราะห์เหตุการณ์", + "col": { + "chapter": "บท", + "reel": "เล่ม", + "chapterName": "ชื่อบท", + "chapterData": "เนื้อหาในบท" + }, + "msg": { + "parseFailed": "แยกวิเคราะห์ไฟล์ล้มเหลว โปรดอัปโหลดใหม่อีกครั้ง", + "selectFile": "เลือกไฟล์", + "docNotSupported": "ไฟล์ .doc ไม่รองรับการแยกวิเคราะห์ โปรดแปลงเป็นไฟล์ .ts", + "unsupportedType": "ไม่รองรับประเภทไฟล์นี้", + "fileTooLarge": "ไฟล์มีขนาดเกิน 10MB โปรดอัปโหลดไฟล์ขนาดเล็กกว่านี้", + "selectChapters": "โปรดทำเครื่องหมายเลือกบทก่อน", + "saveSuccess": "บันทึกต้นฉบับนิยายสำเร็จ" + }, + "importAdd": "ลากและวางไฟล์ที่นี่หรือคลิกเพื่ออัปโหลด", + "limit": "รองรับรูปแบบ .ts" + }, + "editDialog": { + "title": "แก้ไขต้นฉบับนิยาย", + "chapterName": "ชื่อบท", + "chapterNamePh": "โปรดกรอกชื่อบท", + "eventContent": "เนื้อหาเหตุการณ์", + "eventContentPh": "กรอกเนื้อหาเหตุการณ์", + "chapterContent": "เนื้อหาในบท", + "chapterContentPh": "โปรดกรอกเนื้อหาในบท", + "cancel": "ยกเลิก", + "save": "บันทึก", + "msg": { + "updateSuccess": "อัปเดตต้นฉบับนิยายสำเร็จ" + } + }, + "event": { + "regenerate": "สร้างเหตุการณ์ใหม่", + "batchDelete": "ลบเป็นชุด", + "noData": "ยังไม่มีข้อมูลเหตุการณ์ คลิกเพื่อเริ่มสร้าง", + "generate": "สร้างเหตุการณ์", + "generatingHint": "กำลังสร้างเหตุการณ์ โปรดรอสักครู่...", + "loading": "กำลังโหลด...", + "delete": "ลบ", + "col": { + "id": "ID เหตุการณ์", + "eventName": "ชื่อเหตุการณ์", + "chapters": "บทที่มา", + "detail": "ขั้นตอนเหตุการณ์", + "createTime": "เวลาที่สร้าง", + "operation": "การจัดการ" + }, + "msg": { + "deleteHeader": "ลบเหตุการณ์", + "deleteBody": "แน่ใจหรือไม่ว่าต้องการลบเหตุการณ์นี้?", + "deleteSuccess": "ลบสำเร็จ", + "generateSuccess": "สร้างเหตุการณ์สำเร็จ", + "batchDeleteHeader": "ลบเป็นชุด", + "batchDeleteBody": "แน่ใจหรือไม่ว่าต้องการลบข้อมูลที่เลือกจำนวน {count} รายการ?", + "batchDeleteSuccess": "ลบเป็นชุดสำเร็จ" + } + }, + "analysis": { + "analyzeFirst": "โปรดวิเคราะห์เหตุการณ์ก่อน", + "startAnalysis": "เริ่มวิเคราะห์", + "chapterHeader": "บทที่ {index} - {name}", + "analyzing": "กำลังวิเคราะห์เหตุการณ์" + } + }, + "scriptAgent": { + "inputPlaceholder": "โปรดกรอกเนื้อหา", + "chapterEvents": "เหตุการณ์ในบท", + "clearMessageMemory": "ล้างหน่วยความจำข้อความ", + "clearSummaryMemory": "ล้างหน่วยความจำสรุป", + "clearAllMemory": "ล้างหน่วยความจำทั้งหมด", + "edit": "แก้ไข", + "storySkeleton": "โครงเรื่อง", + "adaptationStrategy": "กลยุทธ์การดัดแปลง", + "script": "บทภาพยนตร์", + "noContent": "ยังไม่มีเนื้อหา", + "relatedAssets": "สินทรัพย์ที่เกี่ยวข้อง", + "editScript": "แก้ไขบทภาพยนตร์", + "save": "บันทึก", + "scriptTitle": "ชื่อเรื่อง", + "titlePlaceholder": "โปรดกรอกชื่อเรื่อง", + "content": "เนื้อหา", + "contentPlaceholder": "โปรดกรอกเนื้อหาบทภาพยนตร์", + "selectAssets": "เลือกสินทรัพย์", + "noAssets": "ยังไม่ได้เชื่อมโยงสินทรัพย์", + "selectAssetsTitle": "เลือกสินทรัพย์ที่เกี่ยวข้อง", + "welcomeMsg": "สวัสดี! ฉันคือผู้ช่วยอัจฉริยะ Toonflow ต้องการให้ฉันเริ่มสร้างบทภาพยนตร์ให้คุณเลยไหม?", + "start": "เริ่มต้น", + "memoryType": { + "message": "หน่วยความจำข้อความ", + "summary": "หน่วยความจำสรุป", + "all": "หน่วยความจำทั้งหมด" + }, + "msg": { + "clearConfirm": "ยืนยันการล้าง", + "clearBody": "แน่ใจหรือไม่ว่าต้องการล้าง{type}? การดำเนินการนี้ไม่สามารถยกเลิกได้", + "confirmClear": "ยืนยันการล้าง", + "cancel": "ยกเลิก", + "memoryCleared": "ล้าง{type}แล้ว", + "scriptUpdated": "อัปเดตบทภาพยนตร์สำเร็จ", + "scriptUpdateFailed": "อัปเดตบทภาพยนตร์ล้มเหลว โปรดลองอีกครั้งในภายหลัง", + "searchScriptFailed": "ค้นหาบทภาพยนตร์ล้มเหลว", + "updated": "บันทึกเรียบร้อยแล้ว", + "error": "บันทึกล้มเหลว", + "reconnect": "เชื่อมต่อใหม่", + "notReconnect": "ยืนยันว่าการสนทนาการเชื่อมต่อใหม่จะถูกตัดออกหรือไม่", + "keepReconnect": "ยืนยัน", + "deleteConfirm": "ลบการยืนยัน", + "deleteBody": "ลบข้อความ", + "confirmDelete": "ยืนยันการลบ", + "scriptDeleted": "ลบสคริปต์แล้ว" + }, + "reconnect": "เชื่อมต่อใหม่" + }, + "cornerScape": { + "batchSettings": "การตั้งค่าการสร้างเป็นชุด", + "quickActions": "คำสั่งด่วน", + "selectUngenerated": "เลือกรายการที่ยังไม่สร้างทั้งหมด", + "selectGenerated": "เลือกรายการที่สร้างแล้วทั้งหมด", + "selectFailed": "เลือกรายการที่เกิดข้อผิดพลาดทั้งหมด", + "invertSelection": "สลับการเลือก", + "clearSelection": "ยกเลิกการเลือก", + "batchPreview": "ดูตัวอย่างรูปภาพเป็นชุด", + "assetTypeFilter": "กรองตามประเภทสื่อ", + "genModel": "โมเดลการสร้าง", + "resolution": "ความละเอียด", + "resolutionPh": "โปรดเลือกความละเอียด", + "concurrency": "จำนวนที่สร้างพร้อมกัน", + "concurrencyPh": "โปรดกรอกจำนวนที่สร้างพร้อมกัน", + "startBatch": "เริ่มสร้างภาพเป็นชุด", + "waitingGen": "รอการสร้าง", + "generating": "กำลังสร้าง", + "genFailed": "สร้างล้มเหลว", + "imageError": "รูปภาพมีข้อผิดพลาด", + "typeRole": "ตัวละคร", + "typeScene": "ฉาก", + "typeTool": "เครื่องมือ", + "typeUnknown": "ไม่ทราบ", + "descriptionSuffix": "คำอธิบาย:", + "operateScriptFirst": "โปรดจัดการบทภาพยนตร์ก่อน", + "individualConfig": "ตั้งค่าแบบแยกเดี่ยว", + "noImage": "ยังไม่มีรูปภาพ", + "promptLabel": "พรอมต์", + "promptPh": "โปรดกรอกพรอมต์", + "aiPolish": "ใช้ AI ขัดเกลาข้อความ", + "regenerate": "สร้างใหม่", + "filterRole": "ตัวละคร", + "filterScene": "ฉาก", + "filterTool": "อุปกรณ์ประกอบฉาก", + "unnamed": "ไม่ได้ตั้งชื่อ", + "noDescription": "ไม่มีคำอธิบาย", + "msg": { + "selectModel": "โปรดเลือกโมเดลการสร้าง", + "selectResolution": "โปรดเลือกความละเอียด", + "enterPrompt": "โปรดกรอกพรอมต์", + "enterPromptFirst": "โปรดกรอกพรอมต์ก่อน", + "genSuccess": "สร้าง {name} สำเร็จ", + "genFailed": "สร้าง {name} ล้มเหลว", + "promptGenSuccess": "สร้างพรอมต์สำเร็จ", + "polishFailed": "ขัดเกลาข้อความล้มเหลว โปรดลองอีกครั้ง", + "selectAtLeastOne": "โปรดเลือกสินทรัพย์อย่างน้อยหนึ่งรายการเพื่อสร้างเป็นชุด", + "batchStarted": "เริ่มการสร้างเป็นชุดแล้ว จำนวน {count} รายการ ทำพร้อมกัน {concurrent} รายการ", + "batchItemFailed": "สร้าง {name} ล้มเหลว: {error}", + "batchComplete": "การสร้างเป็นชุดเสร็จสมบูรณ์", + "batchFailed": "การสร้างแบทช์ล้มเหลว", + "replaceFailed": "การเปลี่ยนล้มเหลว", + "replaceSuccess": "การเปลี่ยนสำเร็จ", + "promptGenFail": "การสร้างคำพร้อมท์ล้มเหลว", + "saveSuccess": "การแก้ไขคำพร้อมท์สำเร็จ", + "saveFailed": "การแก้ไขคำพร้อมท์ล้มเหลว" + }, + "history": "ภาพประวัติศาสตร์", + "confirmReplace": "ยืนยันการเปลี่ยน", + "batchGenerationPrompt": "สร้างคำพร้อมท์เป็นชุด", + "generatingPrompt": "กำลังสร้าง", + "selectAll": "เลือกทั้งหมด", + "selectPromptEmpty": "เลือกทั้งหมด คำที่แจ้งว่างเปล่า", + "noEmptyPrompt": "ไม่มีเนื้อหาที่มีคำว่าง", + "selectedCount": "เลือกเนื้อหาแล้ว {count} รายการ", + "cancelGeneration": "ยกเลิกการสร้าง", + "selectGenerating": "เลือกรายการที่กำลังสร้าง", + "noGenerating": "ไม่มีการสร้างข้อมูล", + "checkNumber": "เลือกปริมาณ" + }, + "script": { + "searchPlaceholder": "ค้นหาชื่อบทภาพยนตร์...", + "search": "ค้นหา", + "addScript": "สร้างบทภาพยนตร์ใหม่", + "cancelSelectAll": "ยกเลิกการเลือกทั้งหมด", + "selectAll": "เลือกทั้งหมด", + "exportScript": "ส่งออกบทภาพยนตร์", + "msg": { + "searchFailed": "ค้นหาบทภาพยนตร์ล้มเหลว", + "selectExport": "โปรดเลือกบทภาพยนตร์ที่จะส่งออกก่อน", + "exportSuccess": "ส่งออกสำเร็จ", + "exportFailed": "ส่งออกบทภาพยนตร์ล้มเหลว", + "deleteHeader": "ยืนยันการลบ", + "deleteBody": "แน่ใจหรือไม่ว่าต้องการลบบทภาพยนตร์นี้? การดำเนินการนี้ไม่สามารถยกเลิกได้", + "deleteConfirm": "ลบ", + "cancel": "ยกเลิก", + "deleteSuccess": "ลบสำเร็จ", + "deleteFailed": "ลบล้มเหลว", + "selectDelScript": "โปรดเลือกที่จะลบสคริปต์", + "batchDeleteHeader": "ลบเป็นกลุ่ม", + "batchDeleteBody": "แน่ใจหรือไม่ว่าต้องการลบบทภาพยนตร์ที่เลือก {count} รายการ? การดำเนินการนี้ไม่สามารถยกเลิกได้", + "batchDeleteSuccess": "ลบเป็นกลุ่มสำเร็จ", + "extractingInProgress": "การสกัด", + "projectNotFound": "ไม่พบรายการ", + "selectsExport": "โปรดเลือกส่งออกสคริปต์" + }, + "add": { + "title": "เพิ่มบทภาพยนตร์", + "scriptName": "ชื่อบทภาพยนตร์", + "scriptNamePh": "โปรดกรอกชื่อบทภาพยนตร์", + "uploadFile": "อัปโหลดไฟล์", + "dragUpload": "ลากไฟล์บทภาพยนตร์มาที่นี่ หรือคลิกเพื่ออัปโหลด", + "uploadHint": "รองรับรูปแบบ .txt, .docx แนะนำให้มีขนาดไฟล์ไม่เกิน 10MB", + "scriptContent": "เนื้อหาบทภาพยนตร์", + "scriptContentPh": "โปรดอัปโหลดหรือกรอกเนื้อหาบทภาพยนตร์...", + "relatedAssets": "สินทรัพย์ที่เกี่ยวข้อง", + "selectAssets": "เลือกสินทรัพย์", + "noAssets": "ยังไม่ได้เชื่อมโยงสินทรัพย์", + "cancel": "ยกเลิก", + "confirm": "ยืนยัน", + "msg": { + "fileReadFailed": "อ่านไฟล์ล้มเหลว", + "docNotSupported": "ไม่รองรับการแยกวิเคราะห์ไฟล์ .doc โปรดแปลงเป็นไฟล์ .txt หรือ .docx", + "unsupportedType": "ไม่รองรับประเภทไฟล์นี้", + "fileTooLarge": "ไฟล์มีขนาดเกิน 10MB โปรดอัปโหลดไฟล์ขนาดเล็กกว่านี้", + "parsing": "กำลังแยกวิเคราะห์ไฟล์...", + "parseFailed": "แยกวิเคราะห์ไฟล์ล้มเหลว โปรดอัปโหลดใหม่อีกครั้ง", + "selectAssetsTitle": "เลือกสินทรัพย์ที่เกี่ยวข้อง", + "enterContent": "โปรดอัปโหลดหรือกรอกเนื้อหาบทภาพยนตร์", + "enterName": "โปรดกรอกชื่อบทภาพยนตร์", + "addSuccess": "เพิ่มบทภาพยนตร์สำเร็จ", + "addFailed": "เพิ่มบทภาพยนตร์ล้มเหลว โปรดลองอีกครั้งในภายหลัง" + } + }, + "edit": { + "title": "รายละเอียดบทภาพยนตร์", + "scriptName": "ชื่อบทภาพยนตร์", + "scriptNamePh": "โปรดกรอกชื่อบทภาพยนตร์", + "scriptContent": "เนื้อหาบทภาพยนตร์", + "scriptContentPh": "โปรดกรอกเนื้อหาบทภาพยนตร์...", + "relatedAssets": "สินทรัพย์ที่เกี่ยวข้อง", + "selectAssets": "เลือกสินทรัพย์", + "noAssets": "ยังไม่ได้เชื่อมโยงสินทรัพย์", + "msg": { + "selectAssetsTitle": "เลือกสินทรัพย์ที่เกี่ยวข้อง", + "updateSuccess": "อัปเดตบทภาพยนตร์สำเร็จ", + "updateFailed": "อัปเดตบทภาพยนตร์ล้มเหลว โปรดลองอีกครั้งในภายหลัง" + } + }, + "deleteScript": "ลบสคริปต์เป็นชุด", + "extractAssets": "", + "import": { + "episodeRegexPh": "ปรับแต่งกฎการแยกสคริปต์ ปล่อยว่างไว้เพื่อใช้กฎการแยกเริ่มต้น (ค่าเริ่มต้นคือการแบ่งตามรูปแบบ Episode X)" + } + }, + "assets": { + "addPrefix": "เพิ่ม", + "batchGenerate": "สร้างเป็นชุด", + "generatePrompt": "สร้างพรอมต์", + "generateImage": "สร้างรูปภาพ", + "batchDelete": "ลบเป็นชุด", + "searchPlaceholder": "ค้นหาชื่อสินทรัพย์...", + "search": "ค้นหา", + "preview": "ดูตัวอย่าง", + "generate": "สร้าง", + "edit": "แก้ไข", + "delete": "ลบ", + "generating": "กำลังสร้าง", + "play": "เล่น", + "mediaPreview": "ดูตัวอย่างสื่อ", + "confirmBatch": "ยืนยัน{type}หรือไม่!", + "model": "โมเดล", + "resolution": "ความละเอียด", + "resolutionPh": "โปรดเลือกความละเอียด", + "batchGenPrompt": "สร้างพรอมต์เป็นชุด", + "batchGenImage": "สร้างรูปภาพเป็นชุด", + "role": "ตัวละคร", + "prop": "อุปกรณ์", + "scene": "ฉาก", + "clip": "ฟุตเทจ", + "uploadSuccess": "อัปโหลดสำเร็จ", + "selectAtLeastOne": "โปรดเลือกสินทรัพย์อย่างน้อย 1 รายการ", + "noDescription": "ไม่มีคำอธิบาย", + "promptGenSuccess": "สร้างพรอมต์「{name}」สำเร็จ", + "promptGenFail": "สร้างพรอมต์「{name}」ล้มเหลว: {error}", + "selectModel": "โปรดเลือกโมเดล", + "selectResolution": "โปรดเลือกความละเอียด", + "noPromptForImage": "「{name}」ไม่มีพรอมต์ ไม่สามารถสร้างรูปภาพได้", + "imageGenSuccess": "สร้างรูปภาพ「{name}」สำเร็จ", + "imageGenFail": "สร้างรูปภาพ「{name}」ล้มเหลว: {error}", + "confirmDeleteHeader": "ยืนยันการลบ", + "confirmBatchDeleteBody": "แน่ใจหรือไม่ว่าต้องการลบสินทรัพย์เหล่านี้เป็นชุด? การดำเนินการนี้ไม่สามารถยกเลิกได้", + "confirmDeleteBody": "แน่ใจหรือไม่ว่าต้องการลบสินทรัพย์นี้? การดำเนินการนี้ไม่สามารถยกเลิกได้", + "deleteBtn": "ลบ", + "cancelBtn": "ยกเลิก", + "deleteSuccess": "ลบสินทรัพย์สำเร็จ", + "deleteFail": "ลบสินทรัพย์ล้มเหลว", + "colPreview": "ดูตัวอย่าง", + "colName": "ชื่อ", + "colPrompt": "พรอมต์", + "colDescribe": "คำอธิบาย", + "colRemark": "หมายเหตุ", + "colCreateTime": "เวลาที่สร้าง", + "colOperation": "การจัดการ", + "add": { + "name": "ชื่อ", + "namePh": "โปรดกรอกชื่อ", + "describe": "คำอธิบาย", + "describePh": "โปรดกรอกคำอธิบาย", + "remark": "หมายเหตุ", + "remarkPh": "โปรดกรอกหมายเหตุ", + "prompt": "พรอมต์", + "promptPh": "โปรดกรอกพรอมต์", + "nameRequired": "โปรดกรอกชื่อ", + "describeRequired": "โปรดกรอกรายละเอียด", + "remarkRequired": "โปรดกรอกหมายเหตุ", + "updateSuccess": "อัปเดตสินทรัพย์สำเร็จ", + "addSuccess": "เพิ่มสินทรัพย์สำเร็จ" + }, + "gen": { + "header": "สร้างรูปภาพ", + "uploadRef": "อัปโหลดรูปภาพอ้างอิง", + "optional": "ตัวเลือก", + "promptLabel": "พรอมต์สร้างรูปภาพ", + "smartGenerate": "สร้างอัตโนมัติ (Smart Generate)", + "generatingPrompt": "กำลังสร้างพรอมต์อัจฉริยะ...", + "promptPlaceholder": "อธิบายเนื้อหารูปภาพที่คุณต้องการสร้าง ตัวอย่างเช่น: เมืองแห่งอนาคตที่เต็มไปด้วยเทคโนโลยี, แสงไฟนีออนกะพริบ, สไตล์ไซเบอร์พังค์...", + "selectModel": "เลือกโมเดล", + "selectResolution": "เลือกความละเอียด", + "generateBtn": "สร้างรูปภาพ", + "resultTitle": "ผลลัพธ์การสร้าง", + "generatedCount": "สร้างสำเร็จ {count} รูป โปรดเลือกหนึ่งรูป", + "generatingLabel": "กำลังสร้าง...", + "genFailed": "สร้างล้มเหลว", + "confirmSelect": "ยืนยันการเลือก", + "promptSuccess": "สร้างพรอมต์สำเร็จ", + "promptFail": "สร้างพรอมต์ล้มเหลว", + "fillPrompt": "โปรดกรอกพรอมต์", + "pickResolution": "โปรดเลือกความละเอียด", + "pickModel": "โปรดเลือกโมเดล", + "unnamed": "ไม่ได้ตั้งชื่อ", + "assetGenSuccess": "สร้างสินทรัพย์สำเร็จ", + "assetGenFail": "สร้างสินทรัพย์ล้มเหลว", + "uploadOk": "อัปโหลดสำเร็จ", + "imageSelected": "เลือกรูปภาพนี้แล้ว", + "imageDeleted": "ลบรูปภาพนี้แล้ว", + "imageSaved": "บันทึกรูปภาพแล้ว", + "completed": "สมบูรณ์" + }, + "batch": { + "header": "สร้างเป็นชุด", + "selected": "เลือกแล้ว {count} รายการ", + "selectAll": "เลือกทั้งหมด", + "clearSelection": "ล้างการเลือก", + "inputPh": "โปรดกรอกเนื้อหา", + "saveSelected": "บันทึกที่เลือก ({count})", + "colPreviewImg": "รูปตัวอย่าง", + "selectToSave": "โปรดเลือกรายการที่ต้องการบันทึก", + "saveSuccess": "บันทึกสำเร็จ", + "saveFail": "บันทึกล้มเหลว โปรดลองอีกครั้ง", + "promptDone": "สร้างพรอมต์เสร็จสมบูรณ์", + "promptFail": "สร้างพรอมต์ล้มเหลว", + "missingPrompts": "มี {count} สินทรัพย์ที่ไม่มีพรอมต์ โปรดสร้างพรอมต์ก่อน", + "imageDone": "สร้างรูปภาพเสร็จสมบูรณ์", + "imageGenFail": "สร้างรูปภาพล้มเหลว", + "unknownError": "ข้อผิดพลาดที่ไม่ทราบสาเหตุ", + "promptGenCancelled": "รุ่นถูกยกเลิก" + }, + "confirmCancellation": "ยืนยันการยกเลิก", + "confirmAgain": "ยืนยันการยกเลิก? หลังจากการยกเลิก AI แบ็กเอนด์จะยังคงเรียกร้องให้มีการหักเงินต่อไป", + "sure": "แน่นอน" + }, + "production": { + "selectPlaceholder": "โปรดเลือกตอน", + "edit": "แก้ไข", + "node": { + "script": { + "title": "บทภาพยนตร์", + "editDialog": "แก้ไขบทภาพยนตร์" + }, + "scriptPlan": { + "title": "แผนการถ่ายทำ", + "editDialog": "แก้ไขแผนการถ่ายทำ" + }, + "storyboard": { + "title": "แผงสตอรี่บอร์ด", + "notGenerated": "ยังไม่สร้าง", + "scaleRatio": "อัตราส่วนการย่อ/ขยาย", + "gridPreview": "ดูตัวอย่างแบบกริด", + "noPreviewImages": "ไม่มีรูปภาพให้ดูตัวอย่าง", + "imageLoadFailed": "โหลดรูปภาพล้มเหลว", + "promptPlaceholder": "กรุณากรอกคำที่พร้อมท์", + "prompt": "คำพูดที่รวดเร็ว", + "editInfo": "การปรับเปลี่ยนคำทันที" + }, + "storyboardTable": { + "title": "ตารางสตอรี่บอร์ด", + "editDialog": "แก้ไขตารางสตอรี่บอร์ด" + }, + "assets": { + "title": "สินทรัพย์ต่อยอด", + "generateFailed": "สร้างล้มเหลว", + "notGenerated": "ยังไม่สร้าง", + "originalAsset": "สินทรัพย์ต้นฉบับ", + "derived": "ต่อยอด", + "noDerivedAssets": "ไม่มีสินทรัพย์ต่อยอด" + }, + "poster": { + "title": "ภาพปกวิดีโอ", + "coverCount": "{count} รูป" + }, + "workbench": { + "title": "พื้นที่ทำงานวิดีโอ" + } + }, + "editImage": { + "upload": "อัปโหลด", + "generate": "สร้าง", + "saveFailed": "บันทึกล้มเหลว โปรดลองอีกครั้ง", + "fetchFailed": "ดึงข้อมูลล้มเหลว", + "generating": "กำลังสร้าง...", + "deleteNode": "ลบโหนด (Node)", + "ratio": "สัดส่วน", + "quality": "คุณภาพ", + "generateBtn": "สร้างรูปภาพ", + "selectImage": "เลือกรูปภาพ", + "imageGeneration": "สร้างรูปภาพ", + "promptPlaceholder": "อธิบายรูปภาพที่คุณต้องการสร้าง...", + "imageRef": "รูปที่ {index}", + "noReferences": "ไม่มีรูปภาพอ้างอิงให้ใช้งาน", + "selectModel": "โปรดเลือกโมเดลก่อน", + "selectQuality": "โปรดเลือกคุณภาพภาพ", + "selectRatio": "โปรดเลือกสัดส่วน", + "generateFailed": "สร้างล้มเหลว", + "generateFirst": "โปรดสร้างรูปภาพก่อน", + "generatedResult": "ผลลัพธ์การสร้าง", + "waitingGenerate": "รอการสร้าง", + "layoutLR": "จัดวางอัตโนมัติ-ซ้ายขวา", + "layoutTB": "จัดวางอัตโนมัติ-บนล่าง", + "uploadAssetImage": "อัปโหลดรูปทรัพยากร", + "uploadStoryboardImage": "อัปโหลดรูปสตอรี่บอร์ด", + "uploadImage": "การอัพโหลดรูปภาพสินทรัพย์", + "mode": "แบบอย่าง", + "closeConfirmTitle": "ปิดแผงแก้ไขใช่ไหม", + "closeConfirmBody": "ข้อมูลที่ไม่ได้บันทึกจะสูญหายไปหลังจากการปิด" + }, + "save": "เลือก", + "cancel": "ยกเลิก", + "chatBox": { + "inputPlaceholder": "พิมพ์ข้อความ...", + "generateDerivedAssets": "สร้างสินทรัพย์ต่อยอด", + "welcomeMessage": "สวัสดี! ฉันคือผู้ช่วย AI ของคุณ มีอะไรให้ฉันช่วยไหม?", + "adjustModel": "ปรับแต่งโมเดล", + "startMakingVideo": "เริ่มสร้างวิดีโอ", + "startMakingVideoPrompt": "โปรดช่วยฉันเริ่มสร้างวิดีโอ", + "clearMessageMemory": "ล้างหน่วยความจำข้อความ", + "clearSummaryMemory": "ล้างหน่วยความจำสรุป", + "clearAllMemory": "ล้างหน่วยความจำทั้งหมด", + "messageMemory": "หน่วยความจำข้อความ", + "summaryMemory": "หน่วยความจำสรุป", + "allMemory": "หน่วยความจำทั้งหมด", + "confirmClear": "ล้างหน่วยความจำ", + "confirmClearBody": "แน่ใจหรือไม่ว่าต้องการล้าง{type}?", + "confirmClearBtn": "ยืนยันการล้าง", + "memoryCleared": "ล้าง{type}แล้ว" + }, + "wb": { + "quickPreview": "ดูตัวอย่างแบบเร็ว", + "videoGeneration": "สตอรี่บอร์ด", + "videoEditing": "โต๊ะบรรณาธิการ", + "hint": "ข้อแนะนำ", + "extractLines": "ต้องการแยกบทสนทนาจากวิดีโอหรือไม่?", + "no": "ไม่", + "confirm": "ตกลง", + "extractLinesQuestion": "ต้องการแยกบทสนทนาจากวิดีโอมาเป็นซับไตเติลหรือไม่?", + "importingLoading": "กำลังนำเข้า โปรดรอสักครู่...", + "mainTrackVideo": "แทร็กหลัก (วิดีโอ)", + "subtitle1": "ซับไตเติล 1", + "storyboardVideoName": "{สตอรี่บอร์ด}-{id}.mp4" + }, + "preview": { + "noImage": "ยังไม่มีรูปภาพ", + "storyboardDesc": "คำอธิบายสตอรี่บอร์ด", + "serialNumber": "ลำดับ", + "noDescription": "ยังไม่มีคำอธิบาย", + "duration": "ความยาว", + "seconds": "วินาที", + "relatedAssets": "สินทรัพย์ที่เกี่ยวข้อง", + "role": "ตัวละคร", + "prop": "อุปกรณ์", + "scene": "ฉาก", + "noCharacters": "ไม่มีตัวละครปรากฏ", + "imagePrompt": "พรอมต์รูปภาพ", + "selectAll": "เลือกทั้งหมด", + "exportImage": "ส่งออกรูปภาพ", + "sceneDescription": "คำอธิบายฉาก", + "promptLabel": "พรอมต์", + "restoreSort": "คืนค่าการจัดเรียง", + "restoreSortConfirm": "แน่ใจหรือไม่ว่าต้องการคืนค่าการจัดเรียงกลับเป็นค่าเริ่มต้น?", + "tip": "เคล็ดลับ", + "selectAtLeastOne": "โปรดเลือกสตอรี่บอร์ดอย่างน้อยหนึ่งรายการเพื่อส่งออก", + "exportFilename": "ภาพสตอรี่บอร์ด" + }, + "generate": { + "noVideo": "ยังไม่มีวิดีโอ", + "videoPrompt": "พรอมต์วิดีโอ", + "promptPlaceholder": "ป้อนคำพร้อมท์เพื่ออธิบายเนื้อหาวิดีโอที่คุณต้องการสร้าง...", + "refImage": "รูปภาพอ้างอิง", + "image": "รูปภาพ", + "refVideo": "วิดีโออ้างอิง", + "refImageLabel": "รูปภาพอ้างอิง", + "refAudio": "เสียงอ้างอิง", + "muteAudio": "ปิดเสียง", + "enableAudio": "เปิดเสียง", + "resolution": "ความละเอียด", + "duration": "ความยาว", + "generate": "สร้าง", + "historyVersions": "ประวัติเวอร์ชัน", + "confirmSelection": "ยืนยันการเลือก", + "noHistory": "ยังไม่มีประวัติ", + "generating": "กำลังสร้าง", + "generateFailed": "สร้างล้มเหลว", + "selectAll": "เลือกทั้งหมด", + "videoTrack": "แทร็กวิดีโอ", + "batchGenerate": "สร้างเป็นชุด", + "importToEditor": "นำเข้าสู่หน้าต่างตัดต่อ", + "modeSingleImage": "รูปภาพเดียว", + "modeMultiImage": "หลายรูปภาพ", + "modeGridImage": "รูปภาพกริดหลายรูป", + "modeStartEnd": "เฟรมแรกและสุดท้าย", + "modeText": "ข้อความเป็นวิดีโอ", + "modeVideoRef": "อ้างอิงจากวิดีโอ", + "modeImageRef": "อ้างอิงจากรูปภาพ", + "modeAudioRef": "อ้างอิงจากเสียง", + "modeTextRef": "อ้างอิงจากข้อความ", + "startFrame": "เฟรมแรก", + "startFrameOptional": "เฟรมแรก (ตัวเลือก)", + "endFrame": "เฟรมสุดท้าย", + "endFrameOptional": "เฟรมสุดท้าย (ตัวเลือก)", + "selectRefImage": "เลือกรูปภาพอ้างอิง", + "selectRefImages": "เลือกรูปภาพอ้างอิงหลายรูป", + "selectEndFrame": "เลือกรูปเฟรมสุดท้าย", + "selectRefVideoAsset": "เลือกวิดีโออ้างอิง", + "selectRefAudioAsset": "เลือกเสียงอ้างอิง", + "selectRefImageAsset": "เลือกรูปภาพอ้างอิง", + "selectImageSource": "เลือกแหล่งที่มาของภาพ", + "fromStoryboard": "ภาพสตอรี่บอร์ด", + "fromStoryboardDesc": "เลือกภาพจากรายการสตอรี่บอร์ด", + "fromAssets": "ภาพแอสเซท", + "fromAssetsDesc": "เลือกภาพจากคลังแอสเซท", + "confirmDelete": "ยืนยันการลบ", + "confirmDeleteBody": "แน่ใจหรือไม่ว่าต้องการลบวิดีโอนี้? การดำเนินการนี้ไม่สามารถยกเลิกได้", + "delete": "ลบ", + "cancel": "ยกเลิก", + "deleteSuccess": "ลบวิดีโอสำเร็จ", + "deleteFailed": "ลบล้มเหลว", + "selectVideoFirst": "โปรดเลือกวิดีโอก่อน", + "confirmSuccess": "ยืนยันการเลือกสำเร็จ", + "batchSubmitted": "ส่งคำขอสร้างเป็นชุดแล้ว กำลังดำเนินการ...", + "configNotFound": "ไม่พบการตั้งค่า", + "pollingFailed": "การค้นหาสถานะวิดีโอล้มเหลว โปรดรีเฟรชด้วยตนเอง", + "batchGeneratePrompt": "สร้างคำพร้อมท์เป็นชุด", + "batchPromptEmpty": "บอร์ดเรื่องราว {name} พร้อมใช้งานสำหรับวิดีโอแจ้ง กรุณาสร้างหรือกรอกข้อความแจ้งก่อน", + "modelEmpty": "โปรดเลือกรุ่นการสร้างวิดีโอก่อน", + "generatingPrompt": "การสร้างคำที่รวดเร็วอย่างชาญฉลาด" + }, + "editVideo": { + "reset": "รีเซ็ต", + "undo": "เลิกทำ", + "redo": "ทำซ้ำ", + "split": "แยกคลิป (Split)", + "delete": "ลบ", + "rendering": "กำลังเรนเดอร์...", + "exportVideo": "ส่งออกวิดีโอ", + "exportSuccess": "ส่งออกวิดีโอสำเร็จแล้ว", + "exportFailed": "ส่งออกล้มเหลว", + "sampleSubtitle": "ข้อความซับไตเติลตัวอย่าง", + "customText": "เนื้อหาข้อความกำหนดเอง", + "transitionBetweenClips": "จำเป็นต้องเพิ่มทรานซิชันไว้ระหว่าง Clip สองอันที่อยู่ติดกัน", + "transitionExists": "มีทรานซิชันอยู่ตำแหน่งนี้แล้ว", + "videoPreviewArea": "พื้นที่ดูตัวอย่างวิดีโอ", + "clipMaterials": "ฟุตเทจตัดต่อ", + "propertyPanel": "แผงคุณสมบัติ (Properties)", + "selectClip": "เลือก Clip เพื่อดูคุณสมบัติ", + "basicInfo": "ข้อมูลพื้นฐาน", + "name": "ชื่อ", + "clipNamePlaceholder": "ชื่อ Clip", + "startTime": "เริ่มต้น", + "endTime": "สิ้นสุด", + "totalDuration": "ความยาวทั้งหมด", + "videoProperties": "คุณสมบัติวิดีโอ", + "opacity": "ความทึบแสง", + "volume": "ระดับเสียง", + "playbackSpeed": "ความเร็วในการเล่น", + "audioProperties": "คุณสมบัติเสียง", + "fadeIn": "เฟดอิน (Fade In)", + "fadeOut": "เฟดเอาต์ (Fade Out)", + "transitionProperties": "คุณสมบัติทรานซิชัน", + "transitionType": "ประเภททรานซิชัน", + "transFade": "เลือนหาย (Fade)", + "transSlide": "เลื่อน (Slide)", + "transWipe": "ปาด (Wipe)", + "transDissolve": "ละลาย (Dissolve)", + "transZoom": "ซูม (Zoom)", + "transRotate": "หมุน (Rotate)", + "transitionDuration": "ความยาวทรานซิชัน", + "subtitleProperties": "คุณสมบัติซับไตเติล", + "textContent": "เนื้อหาข้อความ", + "fontSize": "ขนาดตัวอักษร", + "copy": "คัดลอก", + "deleteConfirm": "ยืนยันการลบ", + "deleteClipConfirm": "แน่ใจหรือไม่ว่าต้องการลบ Clip นี้?", + "avCanvasNotInit": "AVCanvas ยังไม่ได้ถูกเตรียมเริ่มต้น", + "noExportContent": "ไม่มีเนื้อหาให้ส่งออก", + "exportProject": "ส่งออกโปรเจกต์", + "transitionAdded": "เพิ่มทรานซิชันแล้ว: {name}", + "splitClip": "แยกคลิป", + "deleteClip": "ลบคลิป", + "addClip": "เพิ่ม {name}", + "duplicateClip": "ทำซ้ำคลิป (Duplicate)", + "addTransition": "เพิ่มทรานซิชัน", + "updateClip": "อัปเดตคลิป {key}", + "updatePlaybackRate": "อัปเดตความเร็วการเล่นเป็น {rate}x", + "updateTransitionDuration": "อัปเดตความยาวทรานซิชัน", + "playbackRateRange": "ความเร็วในการเล่นต้องอยู่ระหว่าง 0.1 ถึง 10", + "updatePlaybackRateFailed": "อัปเดตความเร็วการเล่นล้มเหลว:", + "importProject": "นำเข้าโปรเจกต์", + "import": "นำเข้า" + }, + "clipType": { + "video": "วิดีโอ", + "audio": "เสียง", + "subtitle": "ซับไตเติล", + "transition": "ทรานซิชัน", + "sticker": "สติกเกอร์", + "filter": "ฟิลเตอร์", + "effect": "เอฟเฟกต์" + }, + "track": { + "video": "วิดีโอ", + "image": "รูปภาพ", + "audio": "เสียง", + "subtitle": "ซับไตเติล", + "text": "ข้อความ", + "sticker": "สติกเกอร์", + "filter": "ฟิลเตอร์", + "effect": "เอฟเฟกต์" + }, + "transition": { + "fade": "เลือนหาย (Fade)", + "slide": "เลื่อน (Slide)", + "slideLeft": "เลื่อนไปทางซ้าย", + "slideRight": "เลื่อนไปทางขวา", + "slideUp": "เลื่อนขึ้น", + "slideDown": "เลื่อนลง", + "wipe": "ปาด (Wipe)", + "wipeLeft": "ปาดไปทางซ้าย", + "wipeRight": "ปาดไปทางขวา", + "wipeUp": "ปาดขึ้น", + "wipeDown": "ปาดลง", + "dissolve": "ละลาย (Dissolve)", + "zoom": "ซูม (Zoom)", + "zoomIn": "ซูมเข้า", + "zoomOut": "ซูมออก", + "rotate": "หมุน", + "circle": "วงกลม", + "diamond": "สี่เหลี่ยมข้าวหลามตัด", + "clock": "นาฬิกา", + "blur": "เบลอ" + }, + "media": { + "titleText": "ข้อความชื่อเรื่อง", + "subtitleText": "ข้อความซับไตเติล", + "customText": "ข้อความกำหนดเอง", + "media": "สื่อ", + "image": "รูปภาพ", + "audio": "เสียง", + "subtitle": "ซับไตเติล", + "transition": "ทรานซิชัน", + "effect": "เอฟเฟกต์", + "filter": "ฟิลเตอร์", + "loading": "กำลังโหลด...", + "subtitlePreview": "ข้อความ", + "video": "วิดีโอ" + }, + "effect": { + "fadeIn": "เฟดอิน", + "fadeOut": "เฟดเอาต์", + "flash": "กะพริบ (Flash)", + "shake": "สั่น (Shake)", + "zoomIn": "ซูมเข้ามา", + "zoomOut": "ซูมออกไป", + "pulse": "เต้นเป็นจังหวะ (Pulse)", + "rotateIn": "หมุนเข้ามา", + "sticker1": "สติกเกอร์ 1", + "sticker2": "สติกเกอร์ 2" + }, + "filter": { + "grayscale": "ขาวดำ", + "sepia": "เรโทร (Sepia)", + "warm": "โทนอุ่น", + "cool": "โทนเย็น", + "vivid": "สีสดใส", + "bright": "สว่าง", + "highContrast": "คอนทราสต์สูง", + "blur": "เบลอ", + "invert": "สลับสี (Invert)", + "semiTransparent": "กึ่งโปร่งใส" + }, + "guideSwitchEpisode": "สลับตอน", + "guideSwitchEpisodeBody": "ฟีเจอร์การสลับตอนถูกย้ายมาที่นี่แล้วนะ!", + "autoLayoutLR": "เรียงพิมพ์อัตโนมัติ - เค้าโครงซ้ายและขวา", + "autoLayoutTB": "เค้าโครงเรียงพิมพ์อัตโนมัติบนและล่าง", + "getFlowData": "รีเฟรชพื้นที่ทำงาน", + "confirm": "สลับการยืนยันตอน", + "confirmEpisodesSwitch": "งานปัจจุบันยังอยู่ในระหว่างดำเนินการ การเปลี่ยนตอนจะเชื่อมต่อเซสชันอีกครั้ง คุณต้องการเปลี่ยนต่อหรือไม่?" + }, + "task": { + "title": "รายการงาน", + "subtitle": "บันทึกการทำงานล่าสุดของคุณ", + "refresh": "รีเฟรช", + "categoryLabel": "หมวดหมู่งานหลัก:", + "stateLabel": "สถานะ:", + "noFailReason": "ไม่มีระบุสาเหตุข้อผิดพลาด", + "stateAll": "ทั้งหมด", + "stateRunning": "กำลังดำเนินการ", + "stateCompleted": "เสร็จสมบูรณ์", + "stateFailed": "สร้างล้มเหลว", + "fetchFailed": "ดึงรายการงานล้มเหลว", + "col": { + "taskClass": "หมวดหมู่งานหลัก", + "relatedObjects": "ออบเจกต์ที่เกี่ยวข้อง", + "model": "โมเดล", + "describe": "คำอธิบาย", + "state": "สถานะ", + "startTime": "เวลา", + "reason": "สาเหตุของความล้มเหลว" + }, + "project": "ชื่อโครงการ:" + }, + "noVideo": "ยังไม่มีวิดีโอ", + "prompt": "คำแจ้งวิดีโอ", + "generateText": "AI สร้างคำที่รวดเร็ว", + "selectStoryboard": "เลือกกระดานเรื่องราว", + "generate": { + "noVideo": "ยังไม่มีข้อมูล", + "generateText": "AI สร้างคำที่รวดเร็ว", + "selectStoryboard": "เลือกกระดานเรื่องราว", + "generate": "สร้างวิดีโอ", + "history": "เวอร์ชันประวัติศาสตร์", + "generating": "กำลังสร้าง", + "generateFailed": "ตรวจสอบสาเหตุความล้มเหลว", + "selectAll": "เลือกทั้งหมด", + "selected": "เลือกแล้ว", + "batchGenerateText": "สร้างคำพร้อมท์เป็นชุด", + "batchGenerateVideo": "สร้างวิดีโอเป็นชุด", + "importVideo": "นำเข้าไปยังโต๊ะแก้ไข", + "emptyTrack": "ย่อหน้า {index}", + "del": "ลบ", + "delConfirm": "คุณแน่ใจหรือไม่ว่าต้องการลบย่อหน้านี้", + "selectSource": "เลือกแหล่งที่มา", + "confirm": "เลือกจากสินทรัพย์", + "cancel": "เลือกจากกระดานเรื่องราว", + "selectVideoFailed": "การเลือกวิดีโอล้มเหลว", + "selectVideoSuccess": "การเลือกวิดีโอสำเร็จ", + "previewVideo": "ตัวอย่างวิดีโอ", + "selectTrackFirst": "โปรดเลือกกระดานเรื่องราวก่อน", + "noSelectedVideo": "ไม่ได้เลือกวิดีโอ", + "generateConfirm": "ยืนยันการสร้าง", + "generateConfirmBody": "ยืนยันการสร้างวิดีโอ", + "generateVideosInBatches": "สร้างวิดีโอเป็นชุด", + "generateStarted": "เริ่มสร้าง", + "promptEmpty": "ตรวจสอบข้อมูลที่จำเป็นในการสร้างวิดีโอและคำแจ้งว่างเปล่า คุณต้องการสร้างมันต่อไปหรือไม่?", + "skipDataWithEmptyVideoPromptWords": "มีข้อมูลวิดีโอที่ต้องสร้างและคำแจ้งว่างเปล่า", + "duration": "ระยะเวลา", + "resolution": "ปณิธาน", + "delVideo": "ยืนยันการลบวิดีโอนี้หรือไม่", + "delSuccess": "ลบสำเร็จ", + "addReference": "เพิ่มการอ้างอิง", + "promptPlaceholder": "โปรดป้อนคำแจ้งของวิดีโอ", + "downloadVideo": "ดาวน์โหลดวิดีโอเป็นชุด", + "selectVideo": "โปรดตรวจสอบวิดีโอที่คุณต้องการดาวน์โหลด", + "batchDownloadVideo": "ดาวน์โหลดวิดีโอเป็นชุด", + "storyboard": "สตอรี่บอร์ด", + "assets": "สินทรัพย์", + "promptText": "สร้างข้อมูลคำพร้อมท์วิดีโอ", + "videoMenu": "สร้างวิดีโอ", + "videoPreview": "ตัวอย่างวิดีโอ", + "referenceImage": "รูปภาพอ้างอิง", + "generatePrompt": "สร้างคำพร้อมท์", + "generateVideo": "สร้างวิดีโอ" + } + }, + "login": { + "slogan": "แพลตฟอร์มสร้างละครสั้นอัจฉริยะ", + "tips": "บัญชีผู้ใช้เริ่มต้น: admin / admin123", + "settings": "การตั้งค่าเซิร์ฟเวอร์", + "requestAddress": "ที่อยู่คำขอ (Request Address)", + "username": "ชื่อผู้ใช้", + "password": "รหัสผ่าน", + "login": "เข้าสู่ระบบ", + "usernameRequired": "โปรดกรอกชื่อผู้ใช้", + "passwordRequired": "โปรดกรอกรหัสผ่าน", + "enterUsernameAndPassword": "โปรดกรอกชื่อผู้ใช้หรือรหัสผ่าน", + "loginSuccess": "เข้าสู่ระบบสำเร็จ", + "settingsSaved": "บันทึกการตั้งค่าแล้ว" + }, + "components.storyboardImageCheck.camera": "กล้อง", + "components.storyboardImageCheck.dialogTitle": "เลือกรูปสตอรี่บอร์ด", + "components.storyboardImageCheck.preview": "ดูตัวอย่าง", + "components.storyboardImageCheck.src": "รูปตัวอย่าง", + "components.storyboardImageCheck.title": "หัวข้อ", + "components.storyboardImageCheck.duration": "ระยะเวลา", + "components.storyboardImageCheck.lines": "บทพูด", + "components.storyboardImageCheck.createTime": "เวลาสร้าง", + "workbench.script.extractAssets": "ดึงทรัพยากร", + "promptManage": { + "prompt": "คำพูดที่รวดเร็ว" + }, + "hello": { + "welcomeTitle": "ยินดีต้อนรับสู่ ToonFlow", + "welcomeDesc": "แพลตฟอร์มเวิร์กโฟลว์การสร้างการ์ตูนที่ขับเคลื่อนด้วย AI ให้เราใช้เวลาสักครู่เพื่อกำหนดค่าเริ่มต้นให้เสร็จสิ้น", + "startConfig": "เริ่มการกำหนดค่า", + "skip": "ข้ามการบูต", + "configModel": "เพิ่มบริการโมเดล", + "configData": "กำหนดค่าตัวแทน", + "startUse": "เริ่มต้นเลย", + "configModelTitle": "เพิ่มผู้ให้บริการโมเดล", + "configModelDesc": "ขั้นแรก คุณต้องเพิ่มผู้ให้บริการโมเดล AI อย่างน้อยหนึ่งราย (เช่น OpenAI, Claude ฯลฯ) ในการตั้งค่าและกรอกคีย์ API ที่เกี่ยวข้อง", + "configModelTip": "การคลิกปุ่มด้านล่างจะเป็นการเปิดแท็บ \"บริการโมเดล\" ของหน้าการตั้งค่า หลังจากเพิ่มซัพพลายเออร์แล้ว ให้กลับมาที่นี่เพื่อดำเนินการต่อ", + "configModelBtn": "ไปที่กำหนดค่าบริการโมเดล", + "configAgentTitle": "กำหนดโมเดลตัวแทน", + "configAgentDesc": "จากนั้น กำหนดโมเดลให้กับโมดูลการทำงานแต่ละโมดูลในการกำหนดค่าตัวแทน เพื่อให้ระบบทราบว่าควรเรียกโมเดลใดเพื่อให้งานเสร็จสมบูรณ์", + "configAgentTip": "การคลิกปุ่มด้านล่างจะเป็นการเปิดแท็บ \"การกำหนดค่าตัวแทน\" ของหน้าการตั้งค่า กลับมาที่นี่หลังจากกำหนดโมเดลให้กับแต่ละฟังก์ชันแล้ว", + "configAgentBtn": "ไปที่กำหนดค่าตัวแทน", + "finishTitle": "🎉 ทุกอย่างพร้อมแล้ว!", + "finishDesc": "การกำหนดค่าเสร็จสมบูรณ์ และตอนนี้คุณสามารถเริ่มใช้คุณสมบัติทั้งหมดได้แล้ว หากคุณต้องการปรับเปลี่ยน คุณสามารถแก้ไขได้ในการตั้งค่าได้ตลอดเวลา", + "qrcodeLabel": "เข้าร่วมกลุ่มการสื่อสาร WeChat เพื่อรับความช่วยเหลือเพิ่มเติม:", + "githubLabel": "หากคุณพบว่ามีประโยชน์ โปรดให้ ⭐ ดาวแก่เรา!", + "prevStep": "ขั้นตอนก่อนหน้า", + "nextStep": "ขั้นตอนต่อไป", + "finish": "เริ่มต้นเลย" + }, + "setting": { + "skillManagement": { + "search": "ค้นหาชื่อไฟล์", + "empty": "ไม่มีไฟล์ที่ตรงกัน", + "edit": "แก้ไข", + "selectOnTheLeft": "โปรดเลือกไฟล์จากด้านซ้าย" + } + }, + "storyboard": { + "assets": { + "notExist": "ไม่มีเนื้อหาอยู่", + "notDerivativeExist": "ไม่มีสินทรัพย์อนุพันธ์", + "derivativeUpdateSuccess": "อัปเดตสำเร็จ", + "derivativeState": "ไม่ได้สร้างขึ้น", + "derivativeAddSuccess": "เพิ่มเรียบร้อยแล้ว", + "derivativeDelSuccess": "ลบสำเร็จ", + "notGenerated": "ไม่ได้สร้างขึ้น" + }, + "addSuccess": "อัปเดตสำเร็จ", + "state": { + "unused": "ไม่ได้สร้างขึ้น" + }, + "saveSuccess": "เพิ่มเรียบร้อยแล้ว" + }, + "productionAgent": { + "generating": "กำลังสร้าง" + }, + "skillScan": { + "scanning": "🔍 การแยกวิเคราะห์และการโหลดทักษะ", + "scanComplete": "✨ สแกน Skill เสร็จสมบูรณ์", + "inserted": "✅ เพิ่มทักษะ {count}", + "updated": "🔄 อัปเดตทักษะ {count}", + "removed": "🗑️ ลบ {count} Skill", + "scannedFiles": "📁 สแกน {count} ไฟล์", + "noDescription": "📝 {count} Skill ไม่มีคำอธิบาย", + "noAttribution": "👤 {count} Skill ไม่มีการระบุแหล่งที่มา", + "configWarning": "⚠️ คำเตือนการตั้งค่า Skill", + "openSettings": "เปิดการตั้งค่า", + "scanFailed": "❌ สแกนล้มเหลว", + "checkNetwork": "🔌 กรุณาตรวจสอบการเชื่อมต่อเครือข่ายหรือลองใหม่ภายหลัง", + "retryLater": "🔁 กรุณาลองใหม่ภายหลัง" + }, + "generate": "สร้างวิดีโอ", + "history": "เวอร์ชันประวัติศาสตร์", + "generating": "กำลังสร้าง", + "generateFailed": "ล้มเหลว", + "selectAll": "เลือกทั้งหมด", + "selected": "เลือกแล้ว", + "importVideo": "นำเข้าวิดีโอ", + "emptyTrack": "ย่อหน้า {ดัชนี 1}", + "del": "ยืนยันการลบ", + "delConfirm": "คุณแน่ใจหรือไม่ว่าต้องการลบย่อหน้านี้", + "selectSource": "เลือกแหล่งที่มา", + "confirm": "เลือกจากสินทรัพย์", + "cancel": "เลือกจากกระดานเรื่องราว", + "workbench.script.msg.exportFailed": "การส่งออกล้มเหลว", + "workbench.production.node.assets.confirmDeleteBody": "ยืนยันว่าจะลบเนื้อหาหรือไม่", + "workbench.production.node.assets.removeFailed": "ลบเนื้อหาไม่สำเร็จ", + "version": { + "newVersion": "มีเวอร์ชั่นใหม่แล้ว คุณต้องการอัปเดตหรือไม่?" + }, + "workbench.production.generatedNode.localUpload": "การอัปโหลดในเครื่อง", + "workbench.production.editImage.uploadFailed": "การอัปโหลดรูปภาพล้มเหลว", + "workbench.production.editImage.noImage": "กรุณาเพิ่มรูปภาพก่อน", + "workbench.script.batchAddScript": "อัปโหลดสคริปต์เป็นชุด", + "workbench.script.import.pasteLabel": "วางเนื้อหาสคริปต์โดยตรง", + "workbench.script.import.col.scriptName": "ชื่อสคริปต์", + "workbench.script.import.col.scriptData": "เนื้อหาสคริปต์", + "workbench.script.import.episodeRegex": "กฎการแยกสคริปต์", + "workbench.script.import.episodeRegexPh": "ปรับแต่งกฎการแยกสคริปต์ ปล่อยว่างไว้เพื่อใช้กฎการแยกเริ่มต้น (ค่าเริ่มต้นคือการแบ่งตามรูปแบบ Episode X)", + "workbench.script.import.regexInvalid": "รูปแบบนิพจน์ปกติไม่ถูกต้อง", + "workbench.script.import.parsedChapters": "แยกวิเคราะห์แล้ว {count} ชุด", + "workbench.script.import.msg.selectChapters": "โปรดตรวจสอบสคริปต์ก่อน", + "workbench.script.import.msg.saveSuccess": "บันทึกสคริปต์เรียบร้อยแล้ว", + "workbench.script.import.batchTitle": "อัปโหลดสคริปต์เป็นชุด", + "workbench.assets.sex": "เพศ", + "workbench.assets.audioText": "เนื้อหาเสียง", + "workbench.assets.audio": "เสียง", + "workbench.assets.add.sex": "เพศ", + "workbench.assets.add.sexPh": "กรุณากรอกเพศ", + "settings.agent.advanced": "การกำหนดค่าขั้นสูง", + "settings.agent.ordinary": "การกำหนดค่าง่าย", + "settings.agent.temperature": "อุณหภูมิ", + "settings.agent.maxOutputTokens": "โทเค็นเอาต์พุตสูงสุด", + "settings.agent.auto": "อัตโนมัติ", + "settings.agent.manual": "กำหนดเอง", + "settings.agent.autoHint": "ความยาวเอาต์พุตกำหนดโดยโมเดล", + "settings.agent.msg.notmodel": "ไม่ได้เลือกรุ่น", + "workbench.production.node.storyboard.generateImage": "สร้างสตอรี่บอร์ด", + "workbench.generate.notSelectMode": "กรุณาเลือกรุ่นก่อน", + "workbench.production.node.storyboard.deleteSuccess": "ลบสตอรี่บอร์ดเรียบร้อยแล้ว", + "workbench.production.node.storyboard.pleaseSelectImage": "โปรดเลือกกระดานเรื่องราวก่อน", + "workbench.cornerScape.audioState": "ผูกพัน", + "workbench.generate.generateError": "ไม่สามารถเริ่มต้นคำขอสร้างได้", + "settings.vendor.videoGenerating": "การสร้างวิดีโอช้า โปรดอดทนรอ", + "settings.memory.modelMap.editRefeshWord": "ย้อนกลับ", + "settings.memory.modelMap.delPrompt": "ลบ", + "settings.vendor.testModel": "ทดสอบ" +} diff --git a/web-core/src/locales/language/vi-VN.json b/web-core/src/locales/language/vi-VN.json new file mode 100644 index 0000000..c1567a4 --- /dev/null +++ b/web-core/src/locales/language/vi-VN.json @@ -0,0 +1,1540 @@ +{ + "components": { + "editMdPreivew": { + "title": "Chỉnh sửa", + "confirm": "Lưu", + "cancel": "Hủy" + }, + "imageTools": { + "copy": "Sao chép hình ảnh", + "preview": "Xem trước", + "download": "Tải xuống", + "msg": { + "imageLoadFailed": "Tải hình ảnh thất bại", + "convertFailed": "Chuyển đổi thất bại", + "copied": "Đã sao chép vào clipboard", + "copyFailed": "Sao chép thất bại", + "downloadFailed": "Tải xuống thất bại", + "downloadStarted": "Bắt đầu tải xuống", + "downloadBlockedOpenNewWindow": "Nguồn hình ảnh hiện tại có thể hạn chế tải xuống, đã thử mở trong cửa sổ mới" + } + }, + "migrateShow": { + "title": "Di chuyển dữ liệu", + "desc": "Phát hiện bạn có dữ liệu từ phiên bản cũ, bạn có muốn di chuyển không?", + "hide": "Không hiển thị lại", + "confirm": "Xác nhận", + "msg": { + "migrateSuccess": "Di chuyển dữ liệu thành công", + "migrateFailed": "Di chuyển dữ liệu thất bại" + } + }, + "modelSelect": { + "placeholder": "Vui lòng chọn mô hình", + "type": { + "image": "Hình ảnh", + "text": "Văn bản", + "video": "Video" + }, + "msg": { + "fetchModelFailed": "Lấy dữ liệu mô hình thất bại:" + } + } + }, + "settings": { + "title": "Cài đặt ToonFlow", + "menu": { + "language": "Cài đặt ngôn ngữ", + "vendorConfig": "Dịch vụ mô hình", + "agentConfig": "Cấu hình Agent", + "promptManage": "Quản lý Prompt", + "skillManagement": "Quản lý kĩ năng", + "memoryConfig": "Cấu hình bộ nhớ Agent", + "loginConfig": "Cấu hình đăng nhập", + "dbConfig": "Thao tác cơ sở dữ liệu", + "fileManagement": "Quản lý tệp", + "otherConfig": "Cấu hình khác", + "requestConfig": "Địa chỉ yêu cầu (API)", + "about": "Kiểm tra cập nhật", + "logoutConfig": "Đăng xuất", + "skillsSkillsManagement": "Kỹ năngQuản lý kỹ năng" + }, + "language": { + "desc": "Chọn ngôn ngữ hiển thị giao diện", + "msg": { + "saved": "Cài đặt ngôn ngữ đã được lưu" + } + }, + "vendor": { + "addVendor": "Thêm nhà cung cấp", + "noVendor": "Chưa có nhà cung cấp, vui lòng thêm trước", + "required": "Bắt buộc", + "optionalSection": "Tùy chọn", + "modelSettings": "Cài đặt mô hình", + "addManually": "Thêm thủ công", + "test": "Kiểm tra", + "edit": "Chỉnh sửa", + "delete": "Xóa", + "deleteVendor": "Xóa nhà cung cấp", + "editCode": "Chỉnh sửa mã", + "updateConfig": "Cập nhật cấu hình", + "addModel": "Thêm mô hình", + "editModel": "Chỉnh sửa mô hình", + "displayName": "Tên hiển thị", + "displayNamePlaceholder": "Ví dụ: GPT-4o", + "modelId": "Mã mô hình (Model ID)", + "modelIdPlaceholder": "Ví dụ: gpt-4o", + "modelType": "Loại mô hình", + "multimodal": "Đa phương thức (Multimodal)", + "supported": "Hỗ trợ", + "notSupported": "Không hỗ trợ", + "toolCall": "Gọi công cụ (Tool Call)", + "imageMode": "Chế độ hình ảnh", + "videoMode": "Chế độ video", + "audioOutput": "Đầu ra âm thanh", + "durationResolution": "Ánh xạ Thời lượng / Độ phân giải", + "durationSec": "Thời lượng (giây)", + "resolution": "Độ phân giải", + "enterAndPress": "Nhập và nhấn Enter", + "addDurationResolution": "Thêm một bộ Thời lượng / Độ phân giải", + "testResult": "Kết quả kiểm tra", + "generating": "Đang tạo...", + "addVendorDialog": "Thêm nhà cung cấp", + "codeEditorInfo": "Vui lòng viết mã TypeScript để cấu hình thông tin nhà cung cấp", + "reset": "Đặt lại", + "importFile": "Nhập tệp", + "textModel": "Mô hình văn bản", + "imageModel": "Mô hình hình ảnh", + "videoModel": "Mô hình video", + "textToImage": "Văn bản -> Hình ảnh", + "textToVideo": "Văn bản -> Video", + "singleImage": "Ảnh đơn", + "multiImage": "Chế độ nhiều ảnh", + "multiReference": "Tham chiếu nhiều ảnh", + "multiReferenceMode": "Chế độ đa tham chiếu", + "gridImage": "Lưới ảnh đơn", + "startEndRequired": "Khung hình đầu và cuối (Bắt buộc cả hai)", + "endFrameOptional": "Khung hình đầu và cuối (Khung hình cuối tùy chọn)", + "startFrameOptional": "Khung hình đầu và cuối (Khung hình đầu tùy chọn)", + "textRef": "Văn bản", + "imageRef": "Hình ảnh", + "videoRef": "Video", + "audioRef": "Âm thanh", + "audioOptional": "Tùy chọn", + "audioOnly": "Chỉ xuất video có âm thanh", + "noAudio": "Chỉ xuất video không âm thanh", + "msg": { + "getVendorListFailed": "Lấy danh sách nhà cung cấp thất bại", + "vendorConfigUpdated": "Cấu hình nhà cung cấp đã được cập nhật", + "updateFailed": "Cập nhật thất bại", + "highRiskConfirm": "⚠️ Xác nhận thao tác rủi ro cao", + "addVendorRiskBody": "Việc thêm nhà cung cấp AI mới sẽ cấp cho họ quyền truy cập vào API hệ thống, vui lòng đảm bảo bạn tin tưởng nguồn mã của nhà cung cấp này!", + "iKnowRisk": "Tôi hiểu rủi ro", + "cancel": "Hủy", + "confirmAgain": "⚠️ Xác nhận lại", + "addVendorConfirmBody": "Bạn có chắc chắn muốn thêm nhà cung cấp này không? Sau khi thêm, nó sẽ tham gia vào việc điều phối mô hình của hệ thống.", + "confirmAndAdd": "Xác nhận và thêm", + "goBackCheck": "Quay lại kiểm tra", + "vendorAdded": "Đã thêm nhà cung cấp thành công", + "addFailed": "Thêm thất bại", + "updateVendorRiskBody": "Cập nhật cấu hình nhà cung cấp AI sẽ thay đổi quyền truy cập API hệ thống và hành vi của họ, vui lòng đảm bảo bạn tin tưởng nguồn mã sau khi sửa đổi!", + "updateVendorConfirmBody": "Bạn có chắc chắn muốn cập nhật cấu hình nhà cung cấp này không? Việc cập nhật sẽ ảnh hưởng đến việc điều phối mô hình của hệ thống.", + "confirmAndUpdate": "Xác nhận và cập nhật", + "updateSuccess": "Cập nhật cấu hình nhà cung cấp thành công", + "fillDisplayName": "Vui lòng điền tên hiển thị", + "fillModelId": "Vui lòng điền mã mô hình", + "selectImageMode": "Vui lòng chọn chế độ hình ảnh", + "selectVideoMode": "Vui lòng chọn chế độ video", + "groupPrefix": "Nhóm thứ {n}:", + "addDuration": "Vui lòng thêm thời lượng", + "addResolution": "Vui lòng thêm độ phân giải", + "selectVendorFirst": "Vui lòng chọn nhà cung cấp trước", + "modelIdExists": "Mã mô hình đã tồn tại", + "modelAdded": "Đã thêm mô hình thành công", + "modelUpdated": "Đã cập nhật mô hình thành công", + "enterApiKey": "Vui lòng điền API KEY", + "enterApiUrl": "Vui lòng điền API URL", + "testSuccess": "Kiểm tra thành công", + "imageGenSuccess": "Tạo hình ảnh thành công", + "videoGenSuccess": "Tạo video thành công", + "requestFailed": "Yêu cầu thất bại", + "deleteModelConfirm": "Xác nhận xóa mô hình", + "deleteModelBody": "Không thể khôi phục sau khi xóa, bạn có muốn tiếp tục?", + "confirmDelete": "Xác nhận xóa", + "modelDeleted": "Mô hình đã bị xóa", + "deleteVendorConfirm": "Xác nhận xóa nhà cung cấp", + "deleteVendorBody": "Sau khi xóa, tất cả các mô hình thuộc nhà cung cấp này cũng sẽ bị xóa, bạn có muốn tiếp tục?", + "vendorDeleted": "Nhà cung cấp đã bị xóa", + "deleteFailed": "Xóa thất bại", + "enabled": "Đã bật", + "disabled": "Tàn tật", + "linkAddVendorRiskBody": "Việc thêm nhà cung cấp AI mới sẽ cấp cho nhà cung cấp đó quyền truy cập vào API hệ thống, vui lòng đảm bảo rằng bạn tin cậy nguồn liên kết của nhà cung cấp đó!", + "importAdd": "Việc thêm nhà cung cấp AI mới sẽ cấp cho nhà cung cấp đó quyền truy cập vào API hệ thống, vui lòng đảm bảo rằng bạn tin cậy vào nguồn tài liệu của nhà cung cấp đó!", + "linkAddFailed": "Không thể thêm liên kết" + }, + "think": "suy nghĩ sâu sắc", + "code": "mã số", + "linkAddPlaceholder": "Nhập liên kết để thêm", + "noFileSelected": "Đã nhập tệp thành công", + "linkAdd": "xác nhận" + }, + "agent": { + "bannerDesc": "Sử dụng máy chủ trung gian chính thức của Toonflow, hỗ trợ điền cấu hình bằng 1 cú nhấp chuột, sẵn sàng sử dụng ngay mà không cần thiết lập thủ công.", + "visitWebsite": "Truy cập trang web", + "fillKey": "Điền KEY", + "oneClickFill": "Điền bằng 1 cú nhấp chuột", + "notOpen": "Chưa mở", + "notConfigured": "Chưa cấu hình", + "modelConfig": "Cấu hình mô hình", + "confirm": "Xác nhận", + "cancel": "Hủy", + "selectModel": "Chọn mô hình", + "fillKeyHeader": "Điền KEY chính thức của nền tảng Toonflow", + "keyPlaceholder": "Vui lòng nhập KEY", + "save": "Lưu", + "msg": { + "notAvailable": "Tính năng này hiện chưa khả dụng, vui lòng chờ", + "configSuccess": "Cấu hình thành công", + "updateConfigFailed": "Cập nhật cấu hình thất bại:", + "keyValid": "KEY hợp lệ, kết nối với nền tảng Toonflow thành công", + "keyInvalid": "KEY không hợp lệ, vui lòng kiểm tra và nhập lại:", + "enterKey": "Vui lòng nhập KEY", + "saveFailed": "Lưu thất bại:", + "getAgentListFailed": "Lấy danh sách cấu hình Agent thất bại:", + "toonflowNotFound": "Trạm trung chuyển chính thức của Toonflow không tồn tại" + }, + "temperature": "nhiệt độ" + }, + "memory": { + "warning": "Các mục cấu hình dưới đây đã được đặt sẵn ở mức khuyến nghị. Trừ khi bạn hiểu rõ ý nghĩa và tác động của từng cấu hình, nếu không khuyên bạn nên giữ nguyên thiết lập hiện tại", + "vectorModelConfig": "Cấu hình mô hình vector", + "modelFilePath": "Đường dẫn tệp mô hình", + "quantizationType": "Loại lượng tử hóa", + "quantizationPlaceholder": "Vui lòng nhập loại lượng tử hóa", + "memoryParams": "Thông số bộ nhớ", + "messagesPerSummary": "Số tin nhắn kích hoạt nén", + "messagesPerSummaryHelp": "Giữ lại ngữ cảnh của N cuộc hội thoại gần nhất.", + "shortTermLimit": "Số tin nhắn chưa nén lấy trong 1 lần", + "shortTermLimitHelp": "Số lượng bộ nhớ đề xuất được trả về khi truy xuất.", + "summaryMaxLength": "Ký tự nén tối đa", + "summaryMaxLengthHelp": "Ký tự tối đa cho phép khi nén tin nhắn", + "summaryLimit": "Số tin nhắn đã nén cho phép truy vấn", + "summaryLimitHelp": "Số tin nhắn đã nén cho phép truy vấn", + "ragLimit": "Số lượng bộ nhớ tìm kiếm", + "ragLimitHelp": "Số lượng tin nhắn lấy được khi truy xuất.", + "deepRetrieveSummaryLimit": "Số tin nhắn nén thu hồi bằng vector", + "deepRetrieveSummaryLimitHelp": "Số lượng tin nhắn lấy được khi truy xuất nội dung tin nhắn đã nén.", + "saveConfig": "Lưu cấu hình", + "clearMemory": "Xóa bộ nhớ", + "restoreDefault": "Khôi phục cấu hình mặc định", + "msg": { + "saved": "Cấu hình bộ nhớ đã được lưu", + "clearConfirmTitle": "Xác nhận xóa bộ nhớ", + "clearConfirmBody": "Thao tác này sẽ xóa toàn bộ dữ liệu bộ nhớ toàn cục của AI và không thể khôi phục, bạn có muốn tiếp tục?", + "confirmClear": "Xác nhận xóa", + "cancel": "Hủy", + "cleared": "Bộ nhớ đã được xóa", + "clearFailed": "Xóa bộ nhớ thất bại" + }, + "modelMap": { + "name": "Tên mẫu", + "model": "Người mẫu", + "type": "kiểu", + "editWord": "Ràng buộc từ nhắc nhở", + "operation": "vận hành", + "bindingSuccessful": "Ràng buộc thành công", + "bindingFailed": "Ràng buộc không thành công", + "currentBinding": "ràng buộc hiện tại", + "noBinding": "Không bị ràng buộc", + "bound": "ràng buộc", + "unbind": "Hủy liên kết", + "filenName": "Tên tập tin" + } + }, + "login": { + "username": "Tên người dùng", + "usernamePlaceholder": "Vui lòng nhập tên người dùng", + "password": "Mật khẩu", + "passwordPlaceholder": "Vui lòng nhập mật khẩu", + "modify": "Sửa đổi", + "msg": { + "enterUsername": "Vui lòng nhập tên người dùng", + "usernameLength": "Độ dài tên người dùng từ 2-20 ký tự", + "enterPassword": "Vui lòng nhập mật khẩu", + "passwordLength": "Độ dài mật khẩu từ 6-20 ký tự", + "fetchFailed": "Lấy thông tin người dùng thất bại", + "saveSuccess": "Lưu thành công", + "saveFailed": "Lưu thất bại" + } + }, + "db": { + "clearDb": "Xóa sạch cơ sở dữ liệu", + "clearDbDesc": "Xóa toàn bộ dữ liệu trong tất cả các bảng, giữ lại cấu trúc bảng", + "clearData": "Xóa dữ liệu", + "confirmAction": "Xác nhận thao tác", + "dbInfo": "Tổng quan cơ sở dữ liệu", + "dbInfoDesc": "Xem tên bảng và số lượng bản ghi", + "viewInfo": "Xem thông tin", + "tableName": "Tên bảng", + "rowCount": "Số bản ghi", + "totalTables": "Tổng cộng {count} bảng", + "exportDb": "Xuất cơ sở dữ liệu", + "exportDbDesc": "Xuất tất cả dữ liệu thành tệp sao lưu JSON", + "exportData": "Xuất dữ liệu", + "importDb": "Nhập cơ sở dữ liệu", + "importDbDesc": "Khôi phục dữ liệu từ tệp sao lưu JSON (sẽ ghi đè dữ liệu hiện tại)", + "importData": "Nhập dữ liệu", + "clearTable": "Xóa bảng chỉ định", + "clearTableDesc": "Chọn một bảng dữ liệu và xóa dữ liệu trong bảng", + "clearTableBtn": "Xóa bảng", + "selectTable": "Chọn bảng", + "msg": { + "clearDbTitle": "Xóa sạch cơ sở dữ liệu", + "firstConfirm": "Bạn có chắc chắn muốn xóa toàn bộ bảng dữ liệu không? Dữ liệu sau khi xóa sẽ không thể khôi phục!", + "secondConfirm": "Đây là lần xác nhận cuối cùng, sau khi xóa toàn bộ dữ liệu sẽ bị mất vĩnh viễn!", + "keyword": "Xóa sạch", + "confirm": "Xác nhận", + "pleaseInput": "Vui lòng nhập", + "cleared": "Toàn bộ bảng dữ liệu đã được xóa", + "operationFailed": "Thao tác thất bại, vui lòng thử lại", + "cancelled": "Thao tác đã bị hủy", + "exportSuccess": "Xuất cơ sở dữ liệu thành công", + "exportFailed": "Xuất thất bại", + "importSuccess": "Nhập cơ sở dữ liệu thành công, đang chuyển đến trang đăng nhập", + "importFailed": "Nhập thất bại", + "invalidFile": "Tệp sao lưu không hợp lệ", + "clearTableSuccess": "Bảng đã được xóa", + "clearTableFailed": "Xóa bảng thất bại", + "clearTableConfirm": "Bạn có chắc chắn muốn xóa bảng {name} không? Thao tác này không thể hoàn tác!", + "importConfirm": "Nhập sẽ ghi đè toàn bộ dữ liệu hiện tại, bạn có chắc chắn muốn tiếp tục?", + "importSecondConfirm": "Xác nhận lần cuối: toàn bộ dữ liệu hiện tại sẽ bị thay thế sau khi nhập!", + "noTableSelected": "Vui lòng chọn một bảng trước", + "loadingDbInfo": "Đang tải thông tin cơ sở dữ liệu...", + "loadDbInfoFailed": "Không thể tải thông tin cơ sở dữ liệu" + } + }, + "other": { + "requestTimeout": "Thời gian chờ yêu cầu (Timeout)", + "seconds": "giây", + "inputSeconds": "Vui lòng nhập số giây", + "assetConcurrency": "Số luồng tạo tài nguyên đồng thời", + "count": "cái", + "inputCount": "Vui lòng nhập số lượng", + "chapterRegex": "Regex tách chương", + "restoreDefault": "Khôi phục mặc định", + "regexPlaceholder": "Vui lòng nhập biểu thức chính quy (Regex)", + "canvasScroll": "Cuộn vải", + "canvasIsDisabled": "Thu phóng canvas", + "agentCanvasScalingMethod": "Hoạt động bánh xe vải không giới hạn trên trang sản xuất", + "zoom": "Phóng", + "scroll": "cuộn", + "isInteracting": "Tối ưu hóa hiệu suất kéo canvas không giới hạn trên trang sản xuất", + "closeIsInteracting": "đóng cửa" + }, + "request": { + "warning": "Nếu không có trường hợp đặc biệt, không cần sửa đổi hoặc cấu hình", + "apiAddress": "Địa chỉ API", + "apiPlaceholder": "Vui lòng nhập địa chỉ yêu cầu API", + "save": "Lưu", + "reset": "Đặt lại", + "msg": { + "enterApi": "Vui lòng nhập địa chỉ API", + "validUrl": "Vui lòng nhập địa chỉ HTTP/HTTPS hợp lệ", + "saved": "Địa chỉ yêu cầu đã lưu thành công", + "reset": "Đã đặt lại về địa chỉ mặc định" + } + }, + "about": { + "slogan": "Công cụ sáng tạo truyện tranh / phân cảnh mã nguồn mở do AI điều khiển", + "latestVersion": "Đây là phiên bản mới nhất", + "checkUpdate": "Kiểm tra cập nhật", + "codeRepository": "Kho lưu trữ mã nguồn", + "githubRepo": "Kho GitHub", + "giteeRepo": "Kho Gitee", + "versionUpdate": "Cập nhật phiên bản", + "checkUpdateGithub": "Kiểm tra cập nhật (GitHub)", + "getFromGithub": "Lấy phiên bản mới nhất từ GitHub Release", + "checkUpdateGitee": "Kiểm tra cập nhật (Gitee)", + "getFromGitee": "Lấy phiên bản mới nhất từ Gitee Release", + "license": "Giấy phép", + "licenseDesc": "Thỏa thuận giấy phép mã nguồn mở · Nhấn để xem chi tiết", + "updateAvailable": "phiên bản mới được tìm thấy", + "upToDate": "Đã phát hiện phiên bản mới", + "confirmReinstall": "Sao chép liên kết", + "reinstallRequired": "Trình duyệt sẽ tự động mở và tải xuống. Nếu nó không mở, hãy mở nó bằng tay." + }, + "logout": { + "warning": "Sau khi đăng xuất, bạn cần đăng nhập lại để tiếp tục sử dụng hệ thống.", + "confirmLogout": "Bạn có chắc chắn muốn đăng xuất không?", + "logout": "Đăng xuất", + "msg": { + "logoutSuccess": "Đăng xuất thành công", + "logoutFailed": "Đăng xuất thất bại, vui lòng thử lại" + } + }, + "file": { + "quickOpen": "Mở thư mục nhanh", + "open": "Mở", + "dockerDesc": "Đối với Docker/Triển khai tách biệt Frontend-Backend, vui lòng đi tới thư mục \"/data/*\" để quản lý tệp thủ công.", + "desktopOnly": "Tính năng này chỉ hỗ trợ trên ứng dụng Desktop", + "folders": { + "data": "data", + "dataDesc": "Thư mục dữ liệu.", + "logs": "data/logs", + "logsDesc": "Nhật ký hệ thống và nhật ký lỗi.", + "oss": "data/oss", + "ossDesc": "Tài nguyên liên quan đến lưu trữ tệp.", + "skills": "data/skills", + "skillsDesc": "Tệp cấu hình kỹ năng và lời nhắc (Prompt).", + "models": "data/models", + "modelsDesc": "Tệp mô hình và cấu hình.", + "web": "data/web", + "webDesc": "Tài nguyên liên quan đến Web, như các sản phẩm build Frontend.", + "serve": "data/serve", + "serveDesc": "Tệp liên quan đến dịch vụ Backend." + }, + "openFailed": "Mở thư mục thất bại" + }, + "dev": { + "warning": "Sau đây là các công cụ dành cho nhà phát triển, vui lòng sử dụng thận trọng!", + "openDevtool": "Mở", + "devtoolsDoc": "Địa chỉ tài liệu", + "devtoolsDesc": "Sau khi bật lên sẽ tạo một thư mục .devtools trong thư mục cài đặt Toonflow. Hãy đảm bảo rằng Toonflow có quyền ghi (chạy với tư cách quản trị viên).", + "devtoolsDesc2": "Chạy npx {'@'}ai-sdk/devtools trong thư mục này để bật gỡ lỗi đo từ xa", + "openDevtoolFailed": "Không mở được công cụ dành cho nhà phát triển, vui lòng đảm bảo đã cài đặt Toonflow desktop", + "notInElectron": "Đối với môi trường WEB, vui lòng mở bảng điều khiển trình duyệt theo cách thủ công" + } + }, + "workbench": { + "selectProject": "Vui lòng chọn dự án", + "menu": { + "myProject": "Dự án của tôi", + "taskCenter": "Trung tâm tác vụ", + "novel": "Bản gốc tiểu thuyết", + "scriptAgent": "Agent Kịch bản", + "scriptManage": "Quản lý kịch bản", + "cornerScape": "Nhân vật & Bối cảnh", + "production": "Sản xuất video", + "assetCenter": "Trung tâm tài nguyên", + "settings": "Cài đặt", + "jumpGithub": "Chuyển tới Github", + "feedbackQuestions": "Câu hỏi phản hồi" + }, + "project": { + "title": "Dự án của tôi", + "subtitle": "Quản lý tất cả các dự án phim ngắn của bạn", + "newProject": "Dự án mới", + "dialog": { + "editTitle": "Chỉnh sửa dự án", + "addTitle": "Dự án mới", + "save": "Lưu", + "ok": "Xác nhận", + "cancel": "Hủy", + "projectType": "Loại dự án", + "selectType": "Chọn loại dự án", + "basedOnNovel": "Dựa trên bản gốc tiểu thuyết", + "projectName": "Tên dự án", + "projectNamePh": "Vui lòng nhập tên dự án", + "novelType": "Thể loại tiểu thuyết", + "novelTypePh": "Ví dụ: Huyền huyễn, Khoa học viễn tưởng, Ngôn tình", + "artStyle": "Hướng dẫn trực quan", + "selected": "Đã chọn:", + "selectArtStyle": "Vui lòng chọn hướng dẫn trực quan", + "newArtStyle": "Hướng dẫn trực quan mới", + "loading": "Đang tải...", + "videoRatio": "Tỷ lệ video", + "novelIntro": "Tóm tắt tiểu thuyết", + "novelIntroPh": "Vui lòng nhập tóm tắt tiểu thuyết", + "editArtStyleTitle": "Chỉnh sửa hướng dẫn trực quan", + "newArtStyleTitle": "Hướng dẫn trực quan mới", + "artStyleName": "Tên hướng dẫn trực quan", + "artStyleNamePh": "Vui lòng nhập tên hướng dẫn trực quan", + "artStyleImage": "Ảnh bìa hướng dẫn trực quan", + "remove": "Xóa bỏ", + "uploadCover": "Tải lên ảnh bìa", + "artStylePrompt": "Lời nhắc hướng dẫn sử dụng trực quan", + "aiExtract": "AI trích xuất Prompt", + "promptPlaceholder": "Mô tả từ nhắc hướng dẫn sử dụng trực quan, được sử dụng để chỉ định hướng dẫn trực quan khi tạo hình ảnh.", + "visualManual": "Hướng dẫn trực quan", + "newVisualManual": "Hướng dẫn trực quan mới", + "editVisualManualTitle": "Chỉnh sỮa hướng dẫn trực quan", + "newVisualManualTitle": "Hướng dẫn trực quan mới", + "visualManualName": "Tên hướng dẫn trực quan", + "visualManualNamePh": "Vui lòng nhập tên hướng dẫn trực quan", + "visualManualCover": "Ảnh bìa hướng dẫn trực quan", + "visualManualPrompt": "Prompt hướng dẫn trực quan", + "modelData": "Chọn mô hình hình ảnh", + "videoModelData": "Chọn mô hình video", + "prompt": { + "placeholder": "Nhập từ gợi ý", + "saveSuccess": "Cập nhật thành công", + "title": "lời nhắc" + }, + "basedOnScript": "dựa trên kịch bản", + "mdFile": "tập tin hướng dẫn trực quan", + "directorManual": "Sổ tay Giám đốc", + "addDirectorManual": "Hướng dẫn đạo diễn mới", + "editingDirectorManual": "Chỉnh sửa Sổ tay Giám đốc", + "newDirecorManualTitle": "Hướng dẫn đạo diễn mới", + "directorManualPrompt": "Hướng dẫn sử dụng lời nhắc của đạo diễn", + "directorManualName": "Tên sổ tay giám đốc", + "directorManualNamePh": "Nhập tên Sổ tay Giám đốc", + "directorFile": "Tài liệu hướng dẫn của Giám đốc", + "directorManualCover": "Bìa sách hướng dẫn của giám đốc" + }, + "msg": { + "fetchFailed": "Lấy danh sách dự án thất bại", + "notFound": "Không tìm thấy dự án này!", + "editSuccess": "Chỉnh sửa dự án thành công", + "editFailed": "Chỉnh sửa dự án thất bại", + "addSuccess": "Thêm dự án thành công", + "addFailed": "Thêm dự án thất bại", + "deleteHeader": "Xóa dự án", + "deleteBody": "Bạn có chắc chắn muốn xóa dự án này không?", + "deleteConfirm": "Xóa", + "deleteCancel": "Hủy", + "deleteSuccess": "Xóa dự án thành công", + "deleteFailed": "Xóa dự án thất bại", + "extractSuccess": "Trích xuất lời nhắc thành công", + "extractFailed": "Trích xuất thất bại", + "enterArtStyleName": "Vui lòng nhập tên hướng dẫn trực quan", + "artStyleUpdated": "Hướng dẫn trực quan được cập nhật", + "artStyleAdded": "Đã thêm hướng dẫn trực quan", + "operationFailed": "Thao tác thất bại", + "enterVisualManualName": "Vui lòng nhập tên hướng dẫn trực quan", + "enterVisualManualImage": "Vui lòng tải lên ảnh bìa hướng dẫn trực quan", + "enterVisualManualTabData": "Prompt không được để trống", + "visualManualUpdated": "Hướng dẫn trực quan được cập nhật", + "visualManualAdded": "Đã thêm hướng dẫn trực quan", + "deleteVisualManualHeader": "Xóa hướng dẫn trực quan", + "deleteVisualManualBody": "Bạn có chắc chắn muốn xóa hướng dẫn trực quan \"{name}\" không?", + "deleteVisualManualConfirm": "Xóa", + "deleteVisualManualCancel": "Hủy", + "enterProjectName": "Vui lòng nhập tên dự án", + "enterProjectIntro": "Vui lòng nhập phần giới thiệu tiểu thuyết", + "enterProjectType": "Vui lòng nhập loại dự án", + "enterArtStyle": "Vui lòng chọn một tài liệu trực quan của dự án", + "enterVideoRatio": "Vui lòng chọn tỷ lệ video", + "enterImageModel": "Vui lòng chọn mẫu hình ảnh", + "enterVideoModel": "Vui lòng chọn một mẫu video", + "visualManualDeleted": "Xóa thành công", + "selectMode": "Vui lòng chọn chế độ", + "deleteDirectorManualHeader": "Xóa sổ tay giám đốc", + "deleteDirectorManualBody": "Bạn có chắc chắn muốn xóa Sổ tay Giám đốc \"{name}\" không?", + "directorManualUpdated": "Sổ tay Giám đốc được cập nhật", + "directorManualAdded": "Đã thêm hướng dẫn sử dụng của Giám đốc", + "directorManual": "Vui lòng chọn Sổ tay Giám đốc Dự án", + "modelProviderDisabled": "Nhà cung cấp mô hình video hoặc mô hình hình ảnh chưa được bật hoặc không có nhà cung cấp mô hình, vui lòng định cấu hình trước" + }, + "type": { + "novel": "Dựa trên tiểu thuyết gốc", + "script": "Dựa trên kịch bản tiểu thuyết" + } + }, + "novel": { + "importText": "Nhập bản gốc", + "batchDelete": "Xóa hàng loạt", + "eventAnalysis": "Phân tích sự kiện", + "searchPlaceholder": "Tìm kiếm tên bản gốc...", + "search": "Tìm kiếm", + "generating": "Đang tạo...", + "genFailed": "Tạo thất bại", + "none": "Không có", + "edit": "Chỉnh sửa", + "delete": "Xóa", + "col": { + "id": "STT", + "reel": "Tập", + "chapter": "Tên chương", + "chapterData": "Nội dung chương", + "event": "Sự kiện", + "operation": "Thao tác" + }, + "msg": { + "batchDeleteHeader": "Xóa hàng loạt", + "batchDeleteBody": "Bạn có chắc chắn muốn xóa {count} dữ liệu đã chọn không?", + "batchDeleteSuccess": "Xóa hàng loạt thành công", + "deleteHeader": "Xác nhận xóa", + "deleteBody": "Bạn có chắc chắn muốn xóa dữ liệu của chương có tên \"{name}\" không?", + "deleteSuccess": "Xóa thành công", + "eventAnalysisHeader": "Phân tích sự kiện", + "eventAnalysisBody": "Bạn có chắc chắn muốn phân tích sự kiện cho {count} dữ liệu đã chọn không?" + }, + "import": { + "title": "Tải lên bản gốc tiểu thuyết", + "step1": "Bước 1", + "step2": "Bước 2", + "step3": "Bước 3", + "dragUpload": "Kéo thả tệp văn bản tiểu thuyết vào đây hoặc nhấp để tải lên", + "uploadHint": "Hỗ trợ định dạng .txt, .docx, dung lượng tệp khuyên dùng dưới 10MB", + "or": "Hoặc", + "pasteLabel": "Dán trực tiếp nội dung tiểu thuyết", + "pastePlaceholder": "Vui lòng nhập nội dung tiểu thuyết", + "chars": "ký tự", + "tooShort": "Nội dung quá ngắn, khuyên dùng ít nhất 100 ký tự", + "parsedChapters": "Đã phân tích {count} chương", + "nextStep": "Tiếp theo", + "prevStep": "Quay lại", + "selectedInfo": "Đã chọn: {count} chữ (nhỏ hơn 200.000 chữ)", + "eventAnalysis": "Phân tích sự kiện", + "saveAndAnalyze": "Lưu bản gốc và phân tích sự kiện", + "col": { + "chapter": "Chương", + "reel": "Tập", + "chapterName": "Tên chương", + "chapterData": "Nội dung chương" + }, + "msg": { + "parseFailed": "Phân tích tệp thất bại, vui lòng tải lên lại", + "selectFile": "Chọn tập tin", + "docNotSupported": "File .doc không hỗ trợ phân tích cú pháp, vui lòng chuyển đổi sang file .ts", + "unsupportedType": "Loại tệp không được hỗ trợ", + "fileTooLarge": "Dung lượng tệp vượt quá 10MB, vui lòng tải lên tệp nhỏ hơn", + "selectChapters": "Vui lòng đánh dấu chọn chương trước", + "saveSuccess": "Lưu bản gốc tiểu thuyết thành công" + }, + "importAdd": "Kéo và thả file vào đây hoặc bấm vào để tải lên", + "limit": "Hỗ trợ định dạng .ts" + }, + "editDialog": { + "title": "Chỉnh sửa bản gốc tiểu thuyết", + "chapterName": "Tên chương", + "chapterNamePh": "Vui lòng nhập tên chương", + "eventContent": "Nội dung sự kiện", + "eventContentPh": "Nhập nội dung sự kiện", + "chapterContent": "Nội dung chương", + "chapterContentPh": "Vui lòng nhập nội dung chương", + "cancel": "Hủy", + "save": "Lưu", + "msg": { + "updateSuccess": "Cập nhật bản gốc tiểu thuyết thành công" + } + }, + "event": { + "regenerate": "Tạo lại sự kiện", + "batchDelete": "Xóa hàng loạt", + "noData": "Chưa có dữ liệu sự kiện, nhấp để bắt đầu tạo", + "generate": "Tạo sự kiện", + "generatingHint": "Đang tạo sự kiện, vui lòng chờ...", + "loading": "Đang tải...", + "delete": "Xóa", + "col": { + "id": "ID Sự kiện", + "eventName": "Tên sự kiện", + "chapters": "Chương nguồn", + "detail": "Quá trình sự kiện", + "createTime": "Thời gian tạo", + "operation": "Thao tác" + }, + "msg": { + "deleteHeader": "Xóa sự kiện", + "deleteBody": "Bạn có chắc chắn muốn xóa sự kiện này không?", + "deleteSuccess": "Xóa thành công", + "generateSuccess": "Tạo sự kiện thành công", + "batchDeleteHeader": "Xóa hàng loạt", + "batchDeleteBody": "Bạn có chắc chắn muốn xóa {count} dữ liệu đã chọn không?", + "batchDeleteSuccess": "Xóa hàng loạt thành công" + } + }, + "analysis": { + "analyzeFirst": "Vui lòng phân tích sự kiện trước", + "startAnalysis": "Bắt đầu phân tích", + "chapterHeader": "Chương {index} - {name}", + "analyzing": "Đang phân tích sự kiện" + } + }, + "scriptAgent": { + "inputPlaceholder": "Vui lòng nhập nội dung", + "chapterEvents": "Sự kiện chương", + "clearMessageMemory": "Xóa bộ nhớ tin nhắn", + "clearSummaryMemory": "Xóa bộ nhớ tóm tắt", + "clearAllMemory": "Xóa toàn bộ bộ nhớ", + "edit": "Chỉnh sửa", + "storySkeleton": "Khung xương cốt truyện", + "adaptationStrategy": "Chiến lược chuyển thể", + "script": "Kịch bản", + "noContent": "Chưa có nội dung", + "relatedAssets": "Tài nguyên liên kết", + "editScript": "Chỉnh sửa kịch bản", + "save": "Lưu", + "scriptTitle": "Tiêu đề", + "titlePlaceholder": "Vui lòng nhập tiêu đề", + "content": "Nội dung", + "contentPlaceholder": "Vui lòng nhập nội dung kịch bản", + "selectAssets": "Chọn tài nguyên", + "noAssets": "Chưa liên kết tài nguyên", + "selectAssetsTitle": "Chọn tài nguyên liên kết", + "welcomeMsg": "Xin chào! Tôi là trợ lý thông minh Toonflow, bạn có muốn tôi bắt đầu tạo kịch bản cho bạn không?", + "start": "Bắt đầu", + "memoryType": { + "message": "Bộ nhớ tin nhắn", + "summary": "Bộ nhớ tóm tắt", + "all": "Toàn bộ bộ nhớ" + }, + "msg": { + "clearConfirm": "Xác nhận xóa", + "clearBody": "Bạn có chắc chắn muốn xóa {type} không? Không thể hoàn tác thao tác này.", + "confirmClear": "Xác nhận xóa", + "cancel": "Hủy", + "memoryCleared": "Đã xóa {type}", + "scriptUpdated": "Cập nhật kịch bản thành công", + "scriptUpdateFailed": "Cập nhật kịch bản thất bại, vui lòng thử lại sau", + "searchScriptFailed": "Tìm kiếm kịch bản thất bại", + "updated": "Đã lưu thành công", + "error": "Lưu không thành công", + "reconnect": "Kết nối lại", + "notReconnect": "Xác nhận rằng cuộc trò chuyện kết nối lại sẽ bị cắt?", + "keepReconnect": "xác nhận", + "deleteConfirm": "Xóa xác nhận", + "deleteBody": "Xóa văn bản", + "confirmDelete": "Xác nhận xóa", + "scriptDeleted": "Đã xóa tập lệnh" + }, + "reconnect": "kết nối lại" + }, + "cornerScape": { + "batchSettings": "Cài đặt tạo hàng loạt", + "quickActions": "Lệnh lối tắt", + "selectUngenerated": "Chọn tất cả các mục chưa tạo", + "selectGenerated": "Chọn tất cả các mục đã tạo", + "selectFailed": "Chọn các mục lỗi", + "invertSelection": "Đảo ngược vùng chọn", + "clearSelection": "Bỏ chọn", + "batchPreview": "Xem trước ảnh hàng loạt", + "assetTypeFilter": "Lọc loại tài nguyên", + "genModel": "Mô hình tạo", + "resolution": "Độ phân giải", + "resolutionPh": "Vui lòng chọn độ phân giải", + "concurrency": "Số lượng đồng thời", + "concurrencyPh": "Vui lòng nhập số lượng đồng thời", + "startBatch": "Bắt đầu tạo hình ảnh theo đợt", + "waitingGen": "Đang chờ tạo", + "generating": "Đang tạo", + "genFailed": "Tạo thất bại", + "imageError": "Lỗi hình ảnh", + "typeRole": "Nhân vật", + "typeScene": "Cảnh", + "typeTool": "Công cụ", + "typeUnknown": "Không xác định", + "descriptionSuffix": "Mô tả:", + "operateScriptFirst": "Vui lòng thao tác với kịch bản trước", + "individualConfig": "Cấu hình riêng lẻ", + "noImage": "Chưa có hình ảnh", + "promptLabel": "Lời nhắc (Prompt)", + "promptPh": "Vui lòng nhập lời nhắc", + "aiPolish": "AI trau chuốt", + "regenerate": "Tạo lại", + "filterRole": "Nhân vật", + "filterScene": "Cảnh", + "filterTool": "Đạo cụ", + "unnamed": "Chưa đặt tên", + "noDescription": "Không có mô tả", + "msg": { + "selectModel": "Vui lòng chọn mô hình tạo", + "selectResolution": "Vui lòng chọn độ phân giải", + "enterPrompt": "Vui lòng nhập lời nhắc", + "enterPromptFirst": "Vui lòng nhập lời nhắc trước", + "genSuccess": "Tạo {name} thành công", + "genFailed": "Tạo {name} thất bại", + "promptGenSuccess": "Tạo lời nhắc thành công", + "polishFailed": "Trau chuốt thất bại, vui lòng thử lại", + "selectAtLeastOne": "Vui lòng chọn ít nhất một tài nguyên để tạo hàng loạt", + "batchStarted": "Bắt đầu tạo hàng loạt, tổng cộng {count} mục, số luồng đồng thời {concurrent}", + "batchItemFailed": "Tạo {name} thất bại: {error}", + "batchComplete": "Tạo hàng loạt hoàn tất", + "batchFailed": "Tạo hàng loạt không thành công", + "replaceFailed": "Thay thế không thành công", + "replaceSuccess": "Thay thế thành công", + "promptGenFail": "Tạo từ nhắc nhở không thành công", + "saveSuccess": "Sửa đổi lời nhắc thành công", + "saveFailed": "Sửa đổi từ nhắc nhở không thành công" + }, + "history": "hình ảnh lịch sử", + "confirmReplace": "Xác nhận thay thế", + "batchGenerationPrompt": "Tạo các từ nhắc nhở theo đợt", + "generatingPrompt": "Đang tạo", + "selectAll": "Chọn tất cả", + "selectPromptEmpty": "Chọn tất cả từ nhắc trống", + "noEmptyPrompt": "Không có nội dung nào có từ nhắc trống", + "selectedCount": "đã chọn {count} nội dung", + "cancelGeneration": "Hủy tạo", + "selectGenerating": "Chọn mục đang được tạo", + "noGenerating": "Không có dữ liệu nào được tạo", + "checkNumber": "Chọn số lượng" + }, + "script": { + "searchPlaceholder": "Tìm kiếm tên kịch bản...", + "search": "Tìm kiếm", + "addScript": "Kịch bản mới", + "cancelSelectAll": "Bỏ chọn tất cả", + "selectAll": "Chọn tất cả", + "exportScript": "Xuất kịch bản", + "msg": { + "searchFailed": "Tìm kiếm kịch bản thất bại", + "selectExport": "Vui lòng chọn kịch bản cần xuất trước", + "exportSuccess": "Xuất thành công", + "exportFailed": "Xuất kịch bản thất bại", + "deleteHeader": "Xác nhận xóa", + "deleteBody": "Bạn có chắc chắn muốn xóa kịch bản này không? Không thể hoàn tác thao tác này.", + "deleteConfirm": "Xóa", + "cancel": "Hủy", + "deleteSuccess": "Xóa thành công", + "deleteFailed": "Xóa thất bại", + "selectDelScript": "Vui lòng chọn xóa tập lệnh", + "batchDeleteHeader": "Xóa hàng loạt", + "batchDeleteBody": "Bạn có chắc chắn muốn xóa {count} kịch bản đã chọn không? Không thể hoàn tác thao tác này.", + "batchDeleteSuccess": "Xóa hàng loạt thành công", + "extractingInProgress": "Giải nén", + "projectNotFound": "Không tìm thấy mục", + "selectsExport": "Vui lòng chọn xuất tập lệnh" + }, + "add": { + "title": "Thêm kịch bản mới", + "scriptName": "Tên kịch bản", + "scriptNamePh": "Vui lòng nhập tên kịch bản", + "uploadFile": "Tải lên tệp", + "dragUpload": "Kéo thả tệp kịch bản vào đây hoặc nhấp để tải lên", + "uploadHint": "Hỗ trợ định dạng .txt, .docx, dung lượng tệp khuyên dùng dưới 10MB", + "scriptContent": "Nội dung kịch bản", + "scriptContentPh": "Vui lòng tải lên hoặc nhập nội dung kịch bản...", + "relatedAssets": "Tài nguyên liên kết", + "selectAssets": "Chọn tài nguyên", + "noAssets": "Chưa liên kết tài nguyên", + "cancel": "Hủy", + "confirm": "Xác nhận", + "msg": { + "fileReadFailed": "Đọc tệp thất bại", + "docNotSupported": "Không hỗ trợ phân tích tệp .doc, vui lòng chuyển đổi sang .txt hoặc .docx", + "unsupportedType": "Loại tệp không được hỗ trợ", + "fileTooLarge": "Dung lượng tệp vượt quá 10MB, vui lòng tải lên tệp nhỏ hơn", + "parsing": "Đang phân tích tệp...", + "parseFailed": "Phân tích tệp thất bại, vui lòng tải lên lại", + "selectAssetsTitle": "Chọn tài nguyên liên kết", + "enterContent": "Vui lòng tải lên hoặc nhập nội dung kịch bản", + "enterName": "Vui lòng nhập tên kịch bản", + "addSuccess": "Thêm kịch bản thành công", + "addFailed": "Thêm kịch bản thất bại, vui lòng thử lại sau" + } + }, + "edit": { + "title": "Chi tiết kịch bản", + "scriptName": "Tên kịch bản", + "scriptNamePh": "Vui lòng nhập tên kịch bản", + "scriptContent": "Nội dung kịch bản", + "scriptContentPh": "Vui lòng nhập nội dung kịch bản...", + "relatedAssets": "Tài nguyên liên kết", + "selectAssets": "Chọn tài nguyên", + "noAssets": "Chưa liên kết tài nguyên", + "msg": { + "selectAssetsTitle": "Chọn tài nguyên liên kết", + "updateSuccess": "Cập nhật kịch bản thành công", + "updateFailed": "Cập nhật kịch bản thất bại, vui lòng thử lại sau" + } + }, + "deleteScript": "Xóa tập lệnh theo đợt", + "extractAssets": "", + "import": { + "episodeRegexPh": "Tùy chỉnh quy tắc chia tập lệnh, để trống để sử dụng quy tắc chia tập lệnh mặc định (mặc định là chia theo định dạng Tập X)" + } + }, + "assets": { + "addPrefix": "Thêm mới", + "batchGenerate": "Tạo hàng loạt", + "generatePrompt": "Tạo lời nhắc", + "generateImage": "Tạo hình ảnh", + "batchDelete": "Xóa hàng loạt", + "searchPlaceholder": "Tìm kiếm tên tài nguyên...", + "search": "Tìm kiếm", + "preview": "Xem trước", + "generate": "Tạo", + "edit": "Chỉnh sửa", + "delete": "Xóa", + "generating": "Đang tạo", + "play": "Phát", + "mediaPreview": "Xem trước Media", + "confirmBatch": "Xác nhận {type}!", + "model": "Mô hình", + "resolution": "Độ phân giải", + "resolutionPh": "Vui lòng chọn độ phân giải", + "batchGenPrompt": "Tạo lời nhắc hàng loạt", + "batchGenImage": "Tạo hình ảnh hàng loạt", + "role": "Nhân vật", + "prop": "Đạo cụ", + "scene": "Cảnh", + "clip": "Chất liệu (Clip)", + "uploadSuccess": "Tải lên thành công", + "selectAtLeastOne": "Vui lòng chọn ít nhất một tài nguyên", + "noDescription": "Không có mô tả", + "promptGenSuccess": "Tạo lời nhắc cho「{name}」thành công", + "promptGenFail": "Tạo lời nhắc cho「{name}」thất bại: {error}", + "selectModel": "Vui lòng chọn mô hình", + "selectResolution": "Vui lòng chọn độ phân giải", + "noPromptForImage": "「{name}」không có lời nhắc, không thể tạo hình ảnh", + "imageGenSuccess": "Tạo hình ảnh「{name}」thành công", + "imageGenFail": "Tạo hình ảnh「{name}」thất bại: {error}", + "confirmDeleteHeader": "Xác nhận xóa", + "confirmBatchDeleteBody": "Bạn có chắc chắn muốn xóa hàng loạt các tài nguyên này không? Không thể hoàn tác thao tác này.", + "confirmDeleteBody": "Bạn có chắc chắn muốn xóa tài nguyên này không? Không thể hoàn tác thao tác này.", + "deleteBtn": "Xóa", + "cancelBtn": "Hủy", + "deleteSuccess": "Xóa tài nguyên thành công", + "deleteFail": "Xóa tài nguyên thất bại", + "colPreview": "Xem trước", + "colName": "Tên", + "colPrompt": "Lời nhắc", + "colDescribe": "Mô tả", + "colRemark": "Ghi chú", + "colCreateTime": "Thời gian tạo", + "colOperation": "Thao tác", + "add": { + "name": "Tên", + "namePh": "Vui lòng nhập tên", + "describe": "Mô tả", + "describePh": "Vui lòng nhập mô tả", + "remark": "Ghi chú", + "remarkPh": "Vui lòng nhập ghi chú", + "prompt": "Lời nhắc", + "promptPh": "Vui lòng nhập lời nhắc", + "nameRequired": "Vui lòng nhập tên", + "describeRequired": "Vui lòng nhập chi tiết", + "remarkRequired": "Vui lòng nhập ghi chú", + "updateSuccess": "Cập nhật tài nguyên thành công", + "addSuccess": "Thêm tài nguyên thành công" + }, + "gen": { + "header": "Tạo hình ảnh", + "uploadRef": "Tải lên ảnh tham khảo", + "optional": "Tùy chọn", + "promptLabel": "Lời nhắc tạo ảnh", + "smartGenerate": "Tạo thông minh", + "generatingPrompt": "Đang tạo lời nhắc thông minh...", + "promptPlaceholder": "Mô tả nội dung hình ảnh bạn muốn tạo, ví dụ: Một thành phố tương lai đầy công nghệ, ánh đèn neon nhấp nháy, phong cách cyberpunk...", + "selectModel": "Chọn mô hình", + "selectResolution": "Chọn độ phân giải", + "generateBtn": "Tạo hình ảnh", + "resultTitle": "Kết quả tạo", + "generatedCount": "Đã tạo {count} ảnh, vui lòng chọn một", + "generatingLabel": "Đang tạo...", + "genFailed": "Tạo thất bại", + "confirmSelect": "Xác nhận lựa chọn", + "promptSuccess": "Tạo lời nhắc thành công", + "promptFail": "Tạo lời nhắc thất bại", + "fillPrompt": "Vui lòng điền lời nhắc", + "pickResolution": "Vui lòng chọn độ phân giải", + "pickModel": "Vui lòng chọn mô hình", + "unnamed": "Chưa đặt tên", + "assetGenSuccess": "Tạo tài nguyên thành công", + "assetGenFail": "Tạo tài nguyên thất bại", + "uploadOk": "Tải lên thành công", + "imageSelected": "Đã chọn hình ảnh này", + "imageDeleted": "Đã xóa hình ảnh này", + "imageSaved": "Hình ảnh đã được lưu", + "completed": "Hoàn thành" + }, + "batch": { + "header": "Tạo hàng loạt", + "selected": "Đã chọn {count} mục", + "selectAll": "Chọn tất cả", + "clearSelection": "Bỏ chọn tất cả", + "inputPh": "Vui lòng nhập nội dung", + "saveSelected": "Lưu các mục đã chọn ({count})", + "colPreviewImg": "Ảnh xem trước", + "selectToSave": "Vui lòng chọn mục để lưu", + "saveSuccess": "Lưu thành công", + "saveFail": "Lưu thất bại, vui lòng thử lại", + "promptDone": "Tạo lời nhắc hoàn tất", + "promptFail": "Tạo lời nhắc thất bại", + "missingPrompts": "Có {count} tài nguyên thiếu lời nhắc, vui lòng tạo lời nhắc trước", + "imageDone": "Tạo hình ảnh hoàn tất", + "imageGenFail": "Tạo hình ảnh thất bại", + "unknownError": "Lỗi không xác định", + "promptGenCancelled": "Thế hệ bị hủy" + }, + "confirmCancellation": "Xác nhận hủy", + "confirmAgain": "Xác nhận hủy? Sau khi hủy, AI phụ trợ sẽ tiếp tục yêu cầu khấu trừ.", + "sure": "Chắc chắn" + }, + "production": { + "selectPlaceholder": "Vui lòng chọn tập", + "edit": "Chỉnh sửa", + "node": { + "script": { + "title": "Kịch bản", + "editDialog": "Chỉnh sửa kịch bản" + }, + "scriptPlan": { + "title": "Kế hoạch quay", + "editDialog": "Chỉnh sửa kế hoạch quay" + }, + "storyboard": { + "title": "Bảng phân cảnh", + "notGenerated": "Chưa tạo", + "scaleRatio": "Tỷ lệ thu phóng", + "gridPreview": "Xem trước dạng lưới", + "noPreviewImages": "Không có ảnh để xem trước", + "imageLoadFailed": "Tải hình ảnh thất bại", + "promptPlaceholder": "Vui lòng nhập từ gợi ý", + "prompt": "lời nhắc", + "editInfo": "Sửa đổi từ nhanh chóng" + }, + "storyboardTable": { + "title": "Bảng danh sách phân cảnh", + "editDialog": "Chỉnh sửa bảng phân cảnh" + }, + "assets": { + "title": "Tài nguyên phái sinh", + "generateFailed": "Tạo thất bại", + "notGenerated": "Chưa tạo", + "originalAsset": "Tài nguyên gốc", + "derived": "Phái sinh", + "noDerivedAssets": "Không có tài nguyên phái sinh" + }, + "poster": { + "title": "Ảnh bìa video", + "coverCount": "{count} ảnh" + }, + "workbench": { + "title": "Bàn làm việc Video" + } + }, + "editImage": { + "upload": "Tải lên", + "generate": "Tạo", + "saveFailed": "Lưu thất bại, vui lòng thử lại", + "fetchFailed": "Lấy dữ liệu thất bại", + "generating": "Đang tạo...", + "deleteNode": "Xóa Node", + "ratio": "Tỷ lệ", + "quality": "Chất lượng", + "generateBtn": "Tạo hình ảnh", + "selectImage": "Chọn hình ảnh", + "imageGeneration": "Tạo hình ảnh", + "promptPlaceholder": "Mô tả hình ảnh bạn muốn tạo...", + "imageRef": "Ảnh {index}", + "noReferences": "Không có ảnh tham khảo nào có thể sử dụng", + "selectModel": "Vui lòng chọn mô hình trước", + "selectQuality": "Vui lòng chọn chất lượng", + "selectRatio": "Vui lòng chọn tỷ lệ", + "generateFailed": "Tạo thất bại", + "generateFirst": "Vui lòng tạo hình ảnh trước", + "generatedResult": "Kết quả tạo", + "waitingGenerate": "Đang chờ tạo", + "layoutLR": "Bố cục tự động - Ngang", + "layoutTB": "Bố cục tự động - Dọc", + "uploadAssetImage": "Tải lên hình ảnh tài sản", + "uploadStoryboardImage": "Tải lên hình ảnh storyboard", + "uploadImage": "Tải lên hình ảnh nội dung", + "mode": "người mẫu", + "closeConfirmTitle": "Đóng bảng chỉnh sửa?", + "closeConfirmBody": "Dữ liệu chưa lưu sẽ bị mất sau khi đóng" + }, + "save": "Lựa chọn", + "cancel": "Hủy", + "chatBox": { + "inputPlaceholder": "Nhập tin nhắn...", + "generateDerivedAssets": "Tạo tài nguyên phái sinh", + "welcomeMessage": "Xin chào! Tôi là trợ lý AI của bạn, tôi có thể giúp gì cho bạn?", + "adjustModel": "Điều chỉnh mô hình", + "startMakingVideo": "Bắt đầu làm video", + "startMakingVideoPrompt": "Hãy giúp tôi bắt đầu làm video", + "clearMessageMemory": "Xóa bộ nhớ tin nhắn", + "clearSummaryMemory": "Xóa bộ nhớ tóm tắt", + "clearAllMemory": "Xóa tất cả bộ nhớ", + "messageMemory": "Bộ nhớ tin nhắn", + "summaryMemory": "Bộ nhớ tóm tắt", + "allMemory": "Tất cả bộ nhớ", + "confirmClear": "Xóa bộ nhớ", + "confirmClearBody": "Bạn có chắc chắn muốn xóa {type} không?", + "confirmClearBtn": "Xác nhận xóa", + "memoryCleared": "Đã xóa {type}" + }, + "wb": { + "quickPreview": "Xem trước nhanh", + "videoGeneration": "Bảng phân cảnh", + "videoEditing": "bàn biên tập", + "hint": "Gợi ý", + "extractLines": "Bạn có muốn trích xuất lời thoại từ video không?", + "no": "Không", + "confirm": "Xác nhận", + "extractLinesQuestion": "Bạn có muốn trích xuất lời thoại từ video làm phụ đề không?", + "importingLoading": "Đang nhập, vui lòng chờ...", + "mainTrackVideo": "Track chính (Video)", + "subtitle1": "Phụ đề 1", + "storyboardVideoName": "{bảng phân cảnh}-{id}.mp4" + }, + "preview": { + "noImage": "Chưa có hình ảnh", + "storyboardDesc": "Mô tả phân cảnh", + "serialNumber": "STT", + "noDescription": "Chưa có mô tả", + "duration": "Thời lượng", + "seconds": "giây", + "relatedAssets": "Tài nguyên liên quan", + "role": "Nhân vật", + "prop": "Đạo cụ", + "scene": "Cảnh", + "noCharacters": "Không có nhân vật xuất hiện", + "imagePrompt": "Lời nhắc hình ảnh", + "selectAll": "Chọn tất cả", + "exportImage": "Xuất hình ảnh", + "sceneDescription": "Mô tả khung hình", + "promptLabel": "Lời nhắc", + "restoreSort": "Khôi phục sắp xếp", + "restoreSortConfirm": "Bạn có chắc chắn muốn khôi phục về thứ tự sắp xếp ban đầu không?", + "tip": "Mẹo", + "selectAtLeastOne": "Vui lòng chọn ít nhất một phân cảnh để xuất", + "exportFilename": "Hình ảnh kịch bản" + }, + "generate": { + "noVideo": "Chưa có video", + "videoPrompt": "Lời nhắc video", + "promptPlaceholder": "Nhập từ gợi ý để mô tả nội dung video bạn muốn tạo...", + "refImage": "Ảnh tham khảo", + "image": "Hình ảnh", + "refVideo": "Video tham khảo", + "refImageLabel": "Hình ảnh tham khảo", + "refAudio": "Âm thanh tham khảo", + "muteAudio": "Tắt âm thanh", + "enableAudio": "Bật âm thanh", + "resolution": "Độ phân giải", + "duration": "Thời lượng", + "generate": "Tạo", + "historyVersions": "Phiên bản lịch sử", + "confirmSelection": "Xác nhận đã chọn", + "noHistory": "Chưa có lịch sử", + "generating": "Đang tạo", + "generateFailed": "Tạo thất bại", + "selectAll": "Chọn tất cả", + "videoTrack": "Track video", + "batchGenerate": "Tạo hàng loạt", + "importToEditor": "Nhập vào bàn dựng", + "modeSingleImage": "Ảnh đơn", + "modeMultiImage": "Nhiều ảnh", + "modeGridImage": "Lưới nhiều ảnh", + "modeStartEnd": "Khung hình đầu/cuối", + "modeText": "Văn bản -> Video", + "modeVideoRef": "Video tham khảo", + "modeImageRef": "Ảnh tham khảo", + "modeAudioRef": "Âm thanh tham khảo", + "modeTextRef": "Văn bản tham khảo", + "startFrame": "Khung hình đầu", + "startFrameOptional": "Khung hình đầu (Tùy chọn)", + "endFrame": "Khung hình cuối", + "endFrameOptional": "Khung hình cuối (Tùy chọn)", + "selectRefImage": "Chọn ảnh tham khảo", + "selectRefImages": "Chọn các ảnh tham khảo", + "selectEndFrame": "Chọn ảnh khung hình cuối", + "selectRefVideoAsset": "Chọn video tham khảo", + "selectRefAudioAsset": "Chọn âm thanh tham khảo", + "selectRefImageAsset": "Chọn ảnh tham khảo", + "selectImageSource": "Chọn nguồn hình ảnh", + "fromStoryboard": "Hình storyboard", + "fromStoryboardDesc": "Chọn hình từ danh sách storyboard", + "fromAssets": "Hình tài nguyên", + "fromAssetsDesc": "Chọn hình từ thư viện tài nguyên", + "confirmDelete": "Xác nhận xóa", + "confirmDeleteBody": "Bạn có chắc chắn muốn xóa video này không? Không thể hoàn tác thao tác này.", + "delete": "Xóa", + "cancel": "Hủy", + "deleteSuccess": "Xóa video thành công", + "deleteFailed": "Xóa thất bại", + "selectVideoFirst": "Vui lòng chọn một video trước", + "confirmSuccess": "Xác nhận chọn thành công", + "batchSubmitted": "Đã gửi yêu cầu tạo hàng loạt, đang xử lý...", + "configNotFound": "Cấu hình không tồn tại", + "pollingFailed": "Truy vấn trạng thái video không thành công, vui lòng làm mới theo cách thủ công", + "batchGeneratePrompt": "Tạo các từ nhắc nhở theo đợt", + "batchPromptEmpty": "Có sẵn bảng phân cảnh {name} cho lời nhắc bằng video. Vui lòng tạo hoặc điền vào lời nhắc trước", + "modelEmpty": "Trước tiên hãy chọn mô hình tạo video", + "generatingPrompt": "Tạo lời nhắc thông minh" + }, + "editVideo": { + "reset": "Đặt lại", + "undo": "Hoàn tác (Undo)", + "redo": "Làm lại (Redo)", + "split": "Cắt / Chia nhỏ", + "delete": "Xóa", + "rendering": "Đang kết xuất (Render)...", + "exportVideo": "Xuất Video", + "exportSuccess": "Đã xuất video hoàn tất", + "exportFailed": "Xuất video thất bại", + "sampleSubtitle": "Văn bản phụ đề mẫu", + "customText": "Nội dung văn bản tùy chỉnh", + "transitionBetweenClips": "Chuyển cảnh cần được thêm vào giữa 2 Đoạn video (Clip) liền kề", + "transitionExists": "Vị trí này đã có chuyển cảnh", + "videoPreviewArea": "Khu vực xem trước video", + "clipMaterials": "Tài liệu dựng phim", + "propertyPanel": "Bảng thuộc tính", + "selectClip": "Chọn một Đoạn video (Clip) để xem thuộc tính", + "basicInfo": "Thông tin cơ bản", + "name": "Tên", + "clipNamePlaceholder": "Tên Đoạn video", + "startTime": "Bắt đầu", + "endTime": "Kết thúc", + "totalDuration": "Tổng thời lượng", + "videoProperties": "Thuộc tính video", + "opacity": "Độ mờ", + "volume": "Âm lượng", + "playbackSpeed": "Tốc độ phát", + "audioProperties": "Thuộc tính âm thanh", + "fadeIn": "Fade in (Rõ dần)", + "fadeOut": "Fade out (Mờ dần)", + "transitionProperties": "Thuộc tính chuyển cảnh", + "transitionType": "Loại chuyển cảnh", + "transFade": "Làm mờ (Fade)", + "transSlide": "Trượt (Slide)", + "transWipe": "Gạt (Wipe)", + "transDissolve": "Hòa tan (Dissolve)", + "transZoom": "Thu phóng (Zoom)", + "transRotate": "Xoay (Rotate)", + "transitionDuration": "Thời lượng chuyển cảnh", + "subtitleProperties": "Thuộc tính phụ đề", + "textContent": "Nội dung văn bản", + "fontSize": "Cỡ chữ", + "copy": "Sao chép", + "deleteConfirm": "Xác nhận xóa", + "deleteClipConfirm": "Bạn có chắc chắn muốn xóa Đoạn video này không?", + "avCanvasNotInit": "AVCanvas chưa được khởi tạo", + "noExportContent": "Không có nội dung nào để xuất", + "exportProject": "Xuất dự án", + "transitionAdded": "Đã thêm chuyển cảnh: {name}", + "splitClip": "Cắt Đoạn video", + "deleteClip": "Xóa Đoạn video", + "addClip": "Thêm {name}", + "duplicateClip": "Nhân bản Đoạn video", + "addTransition": "Thêm chuyển cảnh", + "updateClip": "Cập nhật Đoạn video {key}", + "updatePlaybackRate": "Cập nhật tốc độ phát thành {rate}x", + "updateTransitionDuration": "Cập nhật thời lượng chuyển cảnh", + "playbackRateRange": "Tốc độ phát phải nằm trong khoảng từ 0.1 đến 10", + "updatePlaybackRateFailed": "Cập nhật tốc độ phát thất bại:", + "importProject": "Nhập dự án", + "import": "Nhập" + }, + "clipType": { + "video": "Video", + "audio": "Âm thanh", + "subtitle": "Phụ đề", + "transition": "Chuyển cảnh", + "sticker": "Nhãn dán", + "filter": "Bộ lọc", + "effect": "Hiệu ứng" + }, + "track": { + "video": "Video", + "image": "Hình ảnh", + "audio": "Âm thanh", + "subtitle": "Phụ đề", + "text": "Văn bản", + "sticker": "Nhãn dán", + "filter": "Bộ lọc", + "effect": "Hiệu ứng" + }, + "transition": { + "fade": "Làm mờ (Fade)", + "slide": "Trượt (Slide)", + "slideLeft": "Trượt sang trái", + "slideRight": "Trượt sang phải", + "slideUp": "Trượt lên trên", + "slideDown": "Trượt xuống dưới", + "wipe": "Gạt (Wipe)", + "wipeLeft": "Gạt sang trái", + "wipeRight": "Gạt sang phải", + "wipeUp": "Gạt lên trên", + "wipeDown": "Gạt xuống dưới", + "dissolve": "Hòa tan", + "zoom": "Thu phóng", + "zoomIn": "Phóng to", + "zoomOut": "Thu nhỏ", + "rotate": "Xoay", + "circle": "Hình tròn", + "diamond": "Hình thoi", + "clock": "Đồng hồ", + "blur": "Làm mờ (Blur)" + }, + "media": { + "titleText": "Văn bản tiêu đề", + "subtitleText": "Văn bản phụ đề", + "customText": "Văn bản tùy chỉnh", + "media": "Media (Phương tiện)", + "image": "Hình ảnh", + "audio": "Âm thanh", + "subtitle": "Phụ đề", + "transition": "Chuyển cảnh", + "effect": "Hiệu ứng", + "filter": "Bộ lọc", + "loading": "Đang tải...", + "subtitlePreview": "Chữ", + "video": "băng hình" + }, + "effect": { + "fadeIn": "Rõ dần", + "fadeOut": "Mờ dần", + "flash": "Chớp nháy", + "shake": "Rung lắc", + "zoomIn": "Phóng to vào", + "zoomOut": "Thu nhỏ ra", + "pulse": "Nhịp đập", + "rotateIn": "Xoay vào", + "sticker1": "Nhãn dán 1", + "sticker2": "Nhãn dán 2" + }, + "filter": { + "grayscale": "Đen trắng", + "sepia": "Cổ điển (Sepia)", + "warm": "Tone ấm", + "cool": "Tone lạnh", + "vivid": "Rực rỡ", + "bright": "Sáng sủa", + "highContrast": "Độ tương phản cao", + "blur": "Làm mờ", + "invert": "Đảo màu", + "semiTransparent": "Bán trong suốt" + }, + "guideSwitchEpisode": "Chuyển đổi tập", + "guideSwitchEpisodeBody": "Tính năng chuyển đổi tập đã được chuyển đến đây nhé!", + "autoLayoutLR": "Tự động sắp chữ-bố trí bên trái và bên phải", + "autoLayoutTB": "Tự động sắp chữ bố cục trên và dưới", + "getFlowData": "Làm mới không gian làm việc", + "confirm": "Xác nhận chuyển tập", + "confirmEpisodesSwitch": "Nhiệm vụ hiện tại vẫn đang được tiến hành. Việc chuyển các tập sẽ kết nối lại phiên. Bạn có muốn tiếp tục chuyển đổi không?" + }, + "task": { + "title": "Danh sách tác vụ", + "subtitle": "Nhật ký thực thi tác vụ mới nhất của bạn", + "refresh": "Làm mới", + "categoryLabel": "Loại tác vụ:", + "stateLabel": "Trạng thái:", + "noFailReason": "Chưa có lý do thất bại", + "stateAll": "Tất cả", + "stateRunning": "Đang tiến hành", + "stateCompleted": "Đã hoàn thành", + "stateFailed": "Tạo thất bại", + "fetchFailed": "Lấy danh sách tác vụ thất bại", + "col": { + "taskClass": "Loại tác vụ", + "relatedObjects": "Đối tượng liên quan", + "model": "Mô hình", + "describe": "Mô tả", + "state": "Trạng thái", + "startTime": "Thời gian", + "reason": "Lý do thất bại" + }, + "project": "Tên dự án:" + }, + "noVideo": "Chưa có video", + "prompt": "Lời nhắc video", + "generateText": "Lời nhắc do AI tạo ra", + "selectStoryboard": "Chọn bảng phân cảnh", + "generate": { + "noVideo": "Chưa có dữ liệu", + "generateText": "Lời nhắc do AI tạo ra", + "selectStoryboard": "Chọn bảng phân cảnh", + "generate": "Tạo video", + "history": "Phiên bản lịch sử", + "generating": "Đang tạo", + "generateFailed": "Kiểm tra lý do thất bại", + "selectAll": "Chọn tất cả", + "selected": "Đã chọn", + "batchGenerateText": "Tạo các từ nhắc nhở theo đợt", + "batchGenerateVideo": "Tạo video hàng loạt", + "importVideo": "Nhập vào bàn chỉnh sửa", + "emptyTrack": "Đoạn {index}", + "del": "xóa bỏ", + "delConfirm": "Bạn có chắc chắn muốn xóa đoạn này?", + "selectSource": "Chọn nguồn", + "confirm": "Chọn từ nội dung", + "cancel": "Chọn từ bảng phân cảnh", + "selectVideoFailed": "Lựa chọn video không thành công", + "selectVideoSuccess": "Lựa chọn video thành công", + "previewVideo": "Xem trước video", + "selectTrackFirst": "Vui lòng chọn bảng phân cảnh trước", + "noSelectedVideo": "Không có video nào được chọn", + "generateConfirm": "Xác nhận thế hệ", + "generateConfirmBody": "Xác nhận để tạo video", + "generateVideosInBatches": "Tạo video hàng loạt", + "generateStarted": "Bắt đầu xây dựng", + "promptEmpty": "Kiểm tra dữ liệu cần tạo video và từ nhắc trống. Bạn có muốn tiếp tục tạo nó không?", + "skipDataWithEmptyVideoPromptWords": "Dữ liệu video cần được tạo đã tồn tại và từ nhắc trống.", + "duration": "khoảng thời gian", + "resolution": "nghị quyết", + "delVideo": "Xác nhận xóa video này?", + "delSuccess": "Xóa thành công", + "addReference": "thêm tài liệu tham khảo", + "promptPlaceholder": "Vui lòng nhập từ nhắc video", + "downloadVideo": "Tải xuống video hàng loạt", + "selectVideo": "Vui lòng kiểm tra video bạn muốn tải xuống", + "batchDownloadVideo": "Tải xuống video hàng loạt", + "storyboard": "Bảng phân cảnh", + "assets": "tài sản", + "promptText": "Tạo dữ liệu từ nhắc video", + "videoMenu": "Tạo video", + "videoPreview": "Xem trước video", + "referenceImage": "Hình ảnh tham khảo", + "generatePrompt": "Tạo các từ nhắc nhở", + "generateVideo": "Tạo video" + } + }, + "login": { + "slogan": "Nền tảng sáng tạo phim ngắn thông minh", + "tips": "Tài khoản mặc định: admin / admin123", + "settings": "Cài đặt máy chủ", + "requestAddress": "Địa chỉ yêu cầu", + "username": "Tên người dùng", + "password": "Mật khẩu", + "login": "Đăng nhập", + "usernameRequired": "Vui lòng nhập tên người dùng", + "passwordRequired": "Vui lòng nhập mật khẩu", + "enterUsernameAndPassword": "Vui lòng nhập tên người dùng hoặc mật khẩu", + "loginSuccess": "Đăng nhập thành công", + "settingsSaved": "Đã lưu cài đặt" + }, + "common": { + "cancel": "Hủy", + "confirm": "Xác nhận", + "selectAssets": "Chọn tài sản", + "sessionExpired": "Phiên đã hết hạn, vui lòng đăng nhập lại", + "openSettings": "Mở cài đặt", + "cancelled": "Đã hủy tạo", + "defaultReel": "Tập chính", + "save": "cứu", + "submitting": "Đang gửi", + "editSuccess": "Sửa đổi thành công", + "editFailed": "Sửa đổi không thành công", + "submit": "nộp" + }, + "components.storyboardImageCheck.camera": "Máy quay", + "components.storyboardImageCheck.dialogTitle": "Chọn hình storyboard", + "components.storyboardImageCheck.preview": "Xem trước", + "components.storyboardImageCheck.src": "Ảnh xem trước", + "components.storyboardImageCheck.title": "Tiêu đề", + "components.storyboardImageCheck.duration": "Thời lượng", + "components.storyboardImageCheck.lines": "Lời thoại", + "components.storyboardImageCheck.createTime": "Thời gian tạo", + "workbench.script.extractAssets": "Trích xuất tài sản", + "promptManage": { + "prompt": "lời nhắc" + }, + "hello": { + "welcomeTitle": "Chào mừng đến với ToonFlow", + "welcomeDesc": "Nền tảng quy trình tạo truyện tranh do AI điều khiển, chúng ta hãy dành một phút để hoàn thành cấu hình ban đầu.", + "startConfig": "Bắt đầu cấu hình", + "skip": "Bỏ qua khởi động", + "configModel": "Thêm dịch vụ mẫu", + "configData": "Cấu hình tác nhân", + "startUse": "Bắt đầu", + "configModelTitle": "Thêm nhà cung cấp dịch vụ mô hình", + "configModelDesc": "Trước tiên, bạn cần thêm ít nhất một nhà cung cấp dịch vụ mô hình AI (như OpenAI, Claude, v.v.) vào cài đặt và điền API Key tương ứng.", + "configModelTip": "Nhấp vào nút bên dưới sẽ mở tab \"Dịch vụ mẫu\" của trang cài đặt. Sau khi thêm nhà cung cấp, quay lại đây để tiếp tục.", + "configModelBtn": "Đi tới cấu hình dịch vụ mô hình", + "configAgentTitle": "Chỉ định mô hình đại lý", + "configAgentDesc": "Tiếp theo, gán mô hình cho từng mô-đun chức năng trong cấu hình Tác nhân để hệ thống biết cần gọi mô hình nào để hoàn thành nhiệm vụ.", + "configAgentTip": "Nhấp vào nút bên dưới sẽ mở tab \"Cấu hình tác nhân\" của trang cài đặt. Quay lại đây sau khi gán mô hình cho từng chức năng.", + "configAgentBtn": "Đi tới cấu hình Đại lý", + "finishTitle": "🎉 Mọi thứ đã sẵn sàng!", + "finishDesc": "Cấu hình đã hoàn tất và bây giờ bạn có thể bắt đầu sử dụng tất cả các tính năng. Nếu bạn cần điều chỉnh nó, bạn có thể sửa đổi nó trong cài đặt bất cứ lúc nào.", + "qrcodeLabel": "Tham gia nhóm liên lạc WeChat để nhận thêm trợ giúp:", + "githubLabel": "Nếu bạn thấy nó hữu ích, vui lòng cho chúng tôi ⭐ Sao!", + "prevStep": "Bước trước", + "nextStep": "Bước tiếp theo", + "finish": "Bắt đầu" + }, + "setting": { + "skillManagement": { + "search": "Tìm kiếm tên tập tin", + "empty": "Không có tập tin phù hợp", + "edit": "biên tập", + "selectOnTheLeft": "Vui lòng chọn một tập tin từ bên trái" + } + }, + "storyboard": { + "assets": { + "notExist": "Tài sản không tồn tại", + "notDerivativeExist": "Tài sản phái sinh không tồn tại", + "derivativeUpdateSuccess": "Cập nhật thành công", + "derivativeState": "Không được tạo", + "derivativeAddSuccess": "Đã thêm thành công", + "derivativeDelSuccess": "Xóa thành công", + "notGenerated": "Không được tạo" + }, + "addSuccess": "Cập nhật thành công", + "state": { + "unused": "Không được tạo" + }, + "saveSuccess": "Đã thêm thành công" + }, + "productionAgent": { + "generating": "Đang tạo" + }, + "skillScan": { + "scanning": "🔍 Phân tích và tải Skill", + "scanComplete": "✨ Quét Skill hoàn tất", + "inserted": "✅ Đã thêm kỹ năng {count}", + "updated": "🔄 Cập nhật {count} kỹ năng", + "removed": "🗑️ Xóa {count} Skill", + "scannedFiles": "📁 Đã quét {count} tệp", + "noDescription": "📝 {count} Skill thiếu mô tả", + "noAttribution": "👤 {count} Skill thiếu thông tin tác giả", + "configWarning": "⚠️ Cảnh báo cấu hình Skill", + "openSettings": "Mở cài đặt", + "scanFailed": "❌ Quét thất bại", + "checkNetwork": "🔌 Vui lòng kiểm tra kết nối mạng hoặc thử lại sau", + "retryLater": "🔁 Vui lòng thử lại sau" + }, + "generate": "Tạo video", + "history": "Phiên bản lịch sử", + "generating": "Đang tạo", + "generateFailed": "thất bại", + "selectAll": "Chọn tất cả", + "selected": "Đã chọn", + "importVideo": "Nhập video", + "emptyTrack": "Đoạn {chỉ mục 1}", + "del": "Xác nhận xóa", + "delConfirm": "Bạn có chắc chắn muốn xóa đoạn này?", + "selectSource": "Chọn nguồn", + "confirm": "Chọn từ nội dung", + "cancel": "Chọn từ bảng phân cảnh", + "workbench.script.msg.exportFailed": "Xuất không thành công", + "workbench.production.node.assets.confirmDeleteBody": "Xác nhận xem có xóa nội dung hay không", + "workbench.production.node.assets.removeFailed": "Không thể xóa nội dung", + "version": { + "newVersion": "Có phiên bản mới, bạn có muốn cập nhật không?" + }, + "workbench.production.generatedNode.localUpload": "Tải lên cục bộ", + "workbench.production.editImage.uploadFailed": "Tải hình ảnh lên không thành công", + "workbench.production.editImage.noImage": "Vui lòng thêm hình ảnh trước", + "workbench.script.batchAddScript": "Tải lên tập lệnh theo đợt", + "workbench.script.import.pasteLabel": "Dán nội dung tập lệnh trực tiếp", + "workbench.script.import.col.scriptName": "Tên tập lệnh", + "workbench.script.import.col.scriptData": "Nội dung kịch bản", + "workbench.script.import.episodeRegex": "Quy tắc chia tập lệnh", + "workbench.script.import.episodeRegexPh": "Tùy chỉnh quy tắc chia tập lệnh, để trống để sử dụng quy tắc chia tập lệnh mặc định (mặc định là chia theo định dạng Tập X)", + "workbench.script.import.regexInvalid": "Định dạng biểu thức chính quy không hợp lệ", + "workbench.script.import.parsedChapters": "bộ {count} được phân tích cú pháp", + "workbench.script.import.msg.selectChapters": "Vui lòng kiểm tra kịch bản trước", + "workbench.script.import.msg.saveSuccess": "Tập lệnh đã được lưu thành công", + "workbench.script.import.batchTitle": "Tải lên tập lệnh theo đợt", + "workbench.assets.sex": "giới tính", + "workbench.assets.audioText": "nội dung âm thanh", + "workbench.assets.audio": "Âm thanh", + "workbench.assets.add.sex": "giới tính", + "workbench.assets.add.sexPh": "Vui lòng nhập giới tính", + "settings.agent.advanced": "Cấu hình nâng cao", + "settings.agent.ordinary": "Cấu hình dễ dàng", + "settings.agent.temperature": "nhiệt độ", + "settings.agent.maxOutputTokens": "Mã thông báo đầu ra tối đa", + "settings.agent.auto": "Tự động", + "settings.agent.manual": "Thủ công", + "settings.agent.autoHint": "Độ dài đầu ra do mô hình quyết định", + "settings.agent.msg.notmodel": "Không có mô hình nào được chọn", + "workbench.production.node.storyboard.generateImage": "Tạo bảng phân cảnh", + "workbench.generate.notSelectMode": "Vui lòng chọn một mẫu trước", + "workbench.production.node.storyboard.deleteSuccess": "Đã xóa bảng phân cảnh thành công", + "workbench.production.node.storyboard.pleaseSelectImage": "Vui lòng chọn bảng phân cảnh trước", + "workbench.cornerScape.audioState": "Ràng buộc", + "workbench.generate.generateError": "Không thể bắt đầu yêu cầu xây dựng", + "settings.vendor.videoGenerating": "Quá trình tạo video chậm, vui lòng kiên nhẫn chờ đợi", + "settings.memory.modelMap.editRefeshWord": "buộc lại", + "settings.memory.modelMap.delPrompt": "xóa bỏ", + "settings.vendor.testModel": "Bài kiểm tra" +} diff --git a/web-core/src/locales/language/zh-CN.json b/web-core/src/locales/language/zh-CN.json new file mode 100644 index 0000000..a480579 --- /dev/null +++ b/web-core/src/locales/language/zh-CN.json @@ -0,0 +1,1827 @@ +{ + "components": { + "editMdPreivew": { + "title": "编辑", + "confirm": "保存", + "cancel": "取消" + }, + "imageTools": { + "copy": "复制图片", + "preview": "预览", + "download": "下载", + "msg": { + "imageLoadFailed": "图片加载失败", + "convertFailed": "转换失败", + "copied": "已复制到剪贴板", + "copyFailed": "复制失败", + "downloadFailed": "下载失败", + "downloadStarted": "开始下载", + "downloadBlockedOpenNewWindow": "当前图片源可能限制下载,已尝试在新窗口打开" + } + }, + "migrateShow": { + "title": "迁移数据", + "desc": "检测到您有旧版本的数据,是否需要迁移?", + "hide": "不再显示", + "confirm": "确定", + "msg": { + "migrateSuccess": "数据迁移成功", + "migrateFailed": "数据迁移失败" + } + }, + "modelSelect": { + "placeholder": "请选择模型", + "type": { + "image": "图像", + "text": "文本", + "video": "视频" + }, + "msg": { + "fetchModelFailed": "获取模型数据失败:" + }, + "goSetting": "去设置中添加模型" + }, + "storyboardImageCheck": { + "camera": "镜头", + "dialogTitle": "选择分镜图", + "preview": "预览", + "src": "预览图", + "title": "标题", + "duration": "时长", + "lines": "台词", + "createTime": "创建时间" + } + }, + "settings": { + "title": "ToonFlow设置", + "modelMap": { + "imageModel": "图像模型", + "videoModel": "视频模型", + "noModel": "暂无可用的图像或视频模型,请先在「模型服务」中添加", + "promptPlaceholder": "请输入该模式的提示词", + "save": "保存配置", + "col": { + "mode": "模式", + "prompt": "提示词" + }, + "mode": { + "text": "文生图/文生视频", + "singleImage": "单图", + "multiReference": "多参考图", + "startEndRequired": "首尾帧(两张必填)", + "endFrameOptional": "首尾帧(尾帧可选)", + "startFrameOptional": "首尾帧(首帧可选)", + "videoReference": "视频参考", + "imageReference": "图片参考", + "audioReference": "音频参考", + "referenceSuffix": "参考" + }, + "msg": { + "saveSuccess": "模型提示词映射已保存", + "saveFailed": "保存失败,请稍后重试" + } + }, + "menu": { + "ui": "界面设置", + "language": "语言设置", + "vendorConfig": "模型服务", + "modelMap": "模型映射", + "agentConfig": "Agent配置", + "promptManage": "提示词管理", + "skillManagement": "Skills技能管理", + "memoryConfig": "Agent记忆配置", + "loginConfig": "登录配置", + "dbConfig": "数据库操作", + "fileManagement": "文件管理", + "otherConfig": "其他配置", + "requestConfig": "请求地址", + "devConfig": "开发者选项", + "about": "检查更新", + "logoutConfig": "退出登录", + "skillsSkillsManagement": "Skills技能管理" + }, + "language": { + "desc": "选择界面显示语言", + "msg": { + "saved": "语言设置已保存" + } + }, + "vendor": { + "addVendor": "添加供应商", + "noVendor": "暂无供应商,请先添加", + "required": "必填", + "optionalSection": "选填项", + "modelSettings": "模型设置", + "addManually": "手动添加", + "test": { + "textTitle": "文本对话测试", + "imageTitle": "图像生成测试", + "videoTitle": "视频生成测试", + "textEmptyHint": "发送一条消息开始测试", + "you": "你", + "assistant": "助手", + "textInputPlaceholder": "输入消息,Ctrl+Enter 发送", + "send": "发送", + "clearHistory": "清空对话", + "prompt": "提示词", + "promptPlaceholder": "输入提示词", + "videoPromptPlaceholder": "输入视频描述(可选)", + "uploadImage": "点击或拖拽上传图片", + "uploadVideo": "点击或拖拽上传视频", + "uploadAudio": "点击或拖拽上传音频", + "supportFormat": "支持 JPG / PNG / WEBP", + "textToImage": "文生图", + "imageToImage": "图生图", + "multiRef": "多图参考", + "textToVideo": "文生视频", + "singleImageMode": "单图参考", + "selectMode": "选择测试模式", + "result": "生成结果", + "startTest": "开始测试", + "cancel": "取消", + "referenceImage": "参考图片", + "startFrame": "首帧(必填)", + "endFrame": "尾帧(必填)", + "startFrameOptional": "首帧(可选)", + "endFrameOptional": "尾帧(可选)", + "optional": "可选", + "image": "图片", + "video": "视频", + "audio": "音频", + "multiRefDesc": "多参考模式", + "textToVideoDesc": "仅通过文本描述生成视频", + "singleImageDesc": "以单张参考图片为基础生成视频", + "startEndRequiredDesc": "需同时提供首帧与尾帧图片", + "endFrameOptionalDesc": "首帧必填,尾帧可选", + "startFrameOptionalDesc": "尾帧必填,首帧可选" + }, + "edit": "编辑", + "delete": "删除", + "deleteVendor": "删除供应商", + "editCode": "编辑代码", + "updateConfig": "更新配置", + "addModel": "添加模型", + "editModel": "编辑模型", + "displayName": "显示名称", + "displayNamePlaceholder": "例如:GPT-4o", + "modelId": "模型标识", + "modelIdPlaceholder": "例如:gpt-4o", + "modelType": "模型类型", + "multimodal": "多模态", + "supported": "支持", + "notSupported": "不支持", + "toolCall": "工具调用", + "imageMode": "图像模式", + "videoMode": "视频模式", + "audioOutput": "音频输出", + "durationResolution": "时长 / 分辨率映射", + "durationSec": "时长(秒)", + "resolution": "分辨率", + "enterAndPress": "输入后回车", + "addDurationResolution": "添加一组时长 / 分辨率", + "testResult": "测试结果", + "generating": "正在生成中...", + "addVendorDialog": "添加供应商", + "codeEditorInfo": "请编写 TypeScript 代码配置供应商信息", + "reset": "重置", + "importFile": "导入文件", + "textModel": "文本模型", + "imageModel": "图像模型", + "videoModel": "视频模型", + "textToImage": "文生图", + "textToVideo": "文生视频", + "singleImage": "单图", + "multiImage": "多图模式", + "multiReference": "多图参考", + "multiReferenceMode": "多参模式", + "gridImage": "网格单图", + "startEndRequired": "首尾帧(两张必填)", + "endFrameOptional": "首尾帧(尾帧可选)", + "startFrameOptional": "首尾帧(首帧可选)", + "textRef": "文本", + "imageRef": "图片", + "videoRef": "视频", + "audioRef": "音频", + "audioOptional": "可选", + "audioOnly": "仅输出有声视频", + "noAudio": "仅输出无声视频", + "msg": { + "getVendorListFailed": "获取供应商列表失败", + "vendorConfigUpdated": "供应商配置已更新", + "updateFailed": "更新失败", + "highRiskConfirm": "⚠️ 高风险操作确认", + "addVendorRiskBody": "添加新的 AI 供应商将赋予其访问系统 API 的权限,请确认您信任该供应商的代码来源!", + "iKnowRisk": "我了解风险", + "cancel": "取消", + "confirmAgain": "⚠️ 再次确认", + "addVendorConfirmBody": "确定要添加该供应商吗?添加后它将参与系统的模型调度。", + "confirmAndAdd": "确认并添加", + "goBackCheck": "返回检查", + "vendorAdded": "供应商已成功添加", + "addFailed": "添加失败", + "updateVendorRiskBody": "更新 AI 供应商配置将修改其访问系统 API 的权限和行为,请确认您信任修改后的代码来源!", + "updateVendorConfirmBody": "确定要更新该供应商配置吗?更新后将影响系统的模型调度。", + "confirmAndUpdate": "确认并更新", + "updateSuccess": "供应商配置更新成功", + "fillDisplayName": "请填写显示名称", + "fillModelId": "请填写模型标识", + "selectImageMode": "请选择图像模式", + "selectVideoMode": "请选择视频模式", + "groupPrefix": "第 {n} 组:", + "addDuration": "请添加时长", + "addResolution": "请添加分辨率", + "selectVendorFirst": "请先选择供应商", + "modelIdExists": "模型标识已存在", + "modelAdded": "模型已成功添加", + "modelUpdated": "模型已成功更新", + "enterApiKey": "请填写 API KEY", + "enterApiUrl": "请填写 API URL", + "testSuccess": "测试成功", + "imageGenSuccess": "图像生成成功", + "videoGenSuccess": "视频生成成功", + "requestFailed": "请求失败:", + "deleteModelConfirm": "确认删除模型", + "deleteModelBody": "删除后不可恢复,是否继续?", + "confirmDelete": "确认删除", + "modelDeleted": "模型已删除", + "deleteVendorConfirm": "确认删除供应商", + "deleteVendorBody": "删除后该供应商下所有模型将一并删除,是否继续?", + "vendorDeleted": "供应商已删除", + "vendorNeedsUpdate": "该供应商版本过低或缺少版本信息,请更新至 2.0 及以上版本以获得最佳体验", + "deleteFailed": "删除失败", + "enabled": "已启用", + "disabled": "已禁用", + "linkAddVendorRiskBody": "添加新的 AI 供应商将赋予其访问系统 API 的权限,请确认您信任该供应商的链接来源!", + "importAdd": "添加新的 AI 供应商将赋予其访问系统 API 的权限,请确认您信任该供应商的文件来源!", + "linkAddFailed": "链接添加失败" + }, + "think": "深度思考", + "code": "代码", + "linkAddPlaceholder": "输入链接添加", + "noFileSelected": "成功导入文件", + "linkAdd": "确认" + }, + "agent": { + "bannerDesc": "使用 Toonflow 官方中转站点,支持一键填入配置,开箱即用,无需手动配置。", + "visitWebsite": "进入网站", + "fillKey": "填入KEY", + "oneClickFill": "一键填入", + "notOpen": "未开放", + "notConfigured": "未配置", + "modelConfig": "模型配置", + "confirm": "确认", + "cancel": "取消", + "selectModel": "选择模型", + "fillKeyHeader": "填入Toonflow平台的官方KEY", + "keyPlaceholder": "请输入 KEY", + "save": "保存", + "msg": { + "notAvailable": "该功能暂未开放,敬请期待", + "configSuccess": "配置成功", + "updateConfigFailed": "更新配置失败:", + "keyValid": "KEY有效,已成功连接Toonflow平台", + "keyInvalid": "KEY无效,请检查后重新输入:", + "enterKey": "请输入 KEY", + "saveFailed": "保存失败:", + "getAgentListFailed": "获取Agent配置列表失败:", + "toonflowNotFound": "Toonflow官方中转站不存在" + }, + "temperature": "温度" + }, + "memory": { + "warning": "以下配置项已预设为推荐值。除非您清楚了解各项配置的含义及影响,否则建议维持现有设置", + "vectorModelConfig": "向量模型配置", + "modelFilePath": "模型文件路径", + "quantizationType": "量化类型", + "quantizationPlaceholder": "请输入量化类型", + "memoryParams": "记忆参数", + "messagesPerSummary": "触发消息压缩条数", + "messagesPerSummaryHelp": "保留最近 N 条对话上下文。", + "shortTermLimit": "单次获取未压缩消息条数", + "shortTermLimitHelp": "检索时返回的候选记忆条数。", + "summaryMaxLength": "压缩最大字符", + "summaryMaxLengthHelp": "消息压缩时允许的最大字符", + "summaryLimit": "允许查询已压缩消息条数", + "summaryLimitHelp": "允许查询已压缩消息条数", + "ragLimit": "搜索记忆条数", + "ragLimitHelp": "检索时获取的消息数。", + "deepRetrieveSummaryLimit": "向量召回压缩消息数", + "deepRetrieveSummaryLimitHelp": "检索压缩消息内容时获取的消息数。", + "saveConfig": "保存配置", + "clearMemory": "清空记忆", + "restoreDefault": "恢复默认配置", + "msg": { + "saved": "记忆配置已保存", + "clearConfirmTitle": "确认清空记忆", + "clearConfirmBody": "该操作会清空 AI 全局记忆数据,且不可恢复,是否继续?", + "confirmClear": "确认清空", + "cancel": "取消", + "cleared": "记忆已清空", + "clearFailed": "清空记忆失败" + }, + "modelMap": { + "name": "模型名称", + "model": "模型", + "type": "类型", + "editWord": "绑定提示词", + "operation": "操作", + "bindingSuccessful": "绑定成功", + "bindingFailed": "绑定失败", + "currentBinding": "当前绑定", + "noBinding": "未绑定", + "bound": "已绑定", + "unbind": "取消绑定", + "filenName": "文件名称", + "addPrompt": "新增提示词", + "editPrompt": "编辑", + "addPromptTitle": "新增提示词", + "editPromptTitle": "编辑提示词", + "promptNamePlaceholder": "请输入提示词名称", + "promptTypePlaceholder": "请选择类型", + "typeText": "文本", + "typeImage": "图片", + "typeVideo": "视频", + "promptNameRequired": "请输入提示词名称", + "promptSaveSuccess": "保存成功" + } + }, + "login": { + "username": "用户名", + "usernamePlaceholder": "请输入用户名", + "password": "密码", + "passwordPlaceholder": "请输入密码", + "modify": "修改", + "msg": { + "enterUsername": "请输入用户名", + "usernameLength": "用户名长度为 2-20 个字符", + "enterPassword": "请输入密码", + "passwordLength": "密码长度为 6-20 个字符", + "fetchFailed": "获取用户信息失败", + "saveSuccess": "保存成功", + "saveFailed": "保存失败" + } + }, + "db": { + "clearDb": "清空数据库", + "clearDbDesc": "清空所有数据表中的数据,保留表结构", + "clearData": "清空数据", + "confirmAction": "确认操作", + "dbInfo": "数据库概览", + "dbInfoDesc": "查看所有数据表名称和记录数", + "viewInfo": "查看信息", + "tableName": "表名", + "rowCount": "记录数", + "totalTables": "共 {count} 个表", + "exportDb": "导出数据库", + "exportDbDesc": "将所有数据表导出为 JSON 备份文件", + "exportData": "导出数据", + "importDb": "导入数据库", + "importDbDesc": "从 JSON 备份文件恢复数据(将覆盖当前数据)", + "importData": "导入数据", + "clearTable": "清空指定表", + "clearTableDesc": "选择一个数据表并清空其中的数据", + "clearTableBtn": "清空表", + "selectTable": "请选择表", + "msg": { + "clearDbTitle": "清空数据库", + "firstConfirm": "确定要清空所有数据表吗?数据清空后无法恢复!", + "secondConfirm": "这是最后一次确认,清空后所有数据将永久丢失!", + "keyword": "清空", + "confirm": "确认", + "pleaseInput": "请输入", + "cleared": "所有数据表已清空", + "operationFailed": "操作失败,请重试", + "cancelled": "操作已取消", + "exportSuccess": "数据库导出成功", + "exportFailed": "导出失败", + "importSuccess": "数据库导入成功,即将跳转到登录页", + "importFailed": "导入失败", + "invalidFile": "无效的备份文件", + "clearTableSuccess": "表已清空", + "clearTableFailed": "清空表失败", + "clearTableConfirm": "确定要清空表 {name} 吗?此操作不可恢复!", + "importConfirm": "导入将覆盖当前所有数据,确定要继续吗?", + "importSecondConfirm": "最后确认:导入后当前数据将全部被替换!", + "noTableSelected": "请先选择一个表", + "loadingDbInfo": "加载数据库信息中...", + "loadDbInfoFailed": "获取数据库信息失败" + } + }, + "skill": { + "scanSkills": "扫描Skills", + "addSkill": "新增 Skill", + "importFromHub": "Toonflow-Hub 导入", + "filterType": "类型", + "filterAttribution": "归属", + "searchPlaceholder": "按名称搜索 skill", + "search": "查询", + "totalCount": "共 {count} 条 Skill", + "typeMain": "核心", + "typeReferences": "技法", + "noAttribution": "无归属", + "noAttributionTip": "⚠️无归属,无法启用Skill", + "noEmbeddingTip": "⚠️未向量化,无法启用Skill", + "notEmbedded": "未向量化", + "stateNormal": "正常", + "stateGenerating": "生成描述中", + "stateEmptyDesc": "描述为空", + "stateAttrError": "归属异常", + "stateMd5Changed": "MD5变动,建议更新描述", + "embedding": "向量化", + "edit": "编辑", + "delete": "删除", + "importFromHubDialog": "从 Toonflow-Hub 导入", + "shareLink": "分享链接", + "editSkillTitle": "编辑 Skill-", + "addSkillTitle": "新增 Skill", + "skillName": "Skill 名称", + "skillNamePlaceholder": "例如:writing-assistant", + "path": "路径", + "attributionAgent": "归属 Agent", + "selectAttribution": "选择归属 Agent", + "description": "描述", + "aiGenerate": "AI生成", + "descriptionPlaceholder": "描述这个 skill 的用途", + "cancel": "取消", + "save": "保存", + "createSkill": "创建 Skill", + "attr": { + "productionDecision": "视频生产-执行导演", + "productionExecution": "视频生产-摄影指导", + "productionSupervision": "视频生产-监制", + "scriptDecision": "剧本Agent-统筹", + "scriptExecution": "剧本Agent-编剧", + "scriptSupervision": "剧本Agent-编辑", + "universalAgent": "制片助理" + }, + "msg": { + "scanSuccess": "扫描成功,共扫描到 {count} 个 Skill 文件", + "fetchListFailed": "获取 skill 列表失败", + "fillContentFirst": "请先填写 Markdown 内容", + "descGenSuccess": "描述生成成功", + "descGenFailed": "描述生成失败", + "fillNameFirst": "请先填写 Skill 名称", + "updateSuccess": "Skill 更新成功", + "createSuccess": "Skill 创建成功", + "updateFailed": "Skill 更新失败", + "createFailed": "Skill 创建失败", + "deleteConfirmTitle": "确认删除", + "deleteConfirmBody": "确定要删除 Skill「{name}」吗?此操作不可恢复。", + "deleteSuccess": "删除成功", + "deleteFailed": "删除失败", + "embeddingSuccess": "向量化成功", + "embeddingFailed": "向量化失败" + }, + "fileLost": "文件丢失" + }, + "other": { + "requestTimeout": "请求超时时间", + "seconds": "秒", + "inputSeconds": "请输入秒", + "assetConcurrency": "资产生成并发数", + "count": "个", + "inputCount": "请输入个数", + "chapterRegex": "章节拆分正则", + "restoreDefault": "恢复默认", + "regexPlaceholder": "请输入正则表达式", + "canvasScroll": "画布滚动", + "canvasIsDisabled": "画布缩放", + "agentCanvasScalingMethod": "生产页无限画布滚轮操作", + "zoom": "缩放", + "scroll": "滚动", + "isInteracting": "生产页无限画布拖拽性能优化", + "closeIsInteracting": "关闭", + "openIsInteracting": "开启", + "scriptEpisodeLength": "剧本单集长度限制", + "chars": "字", + "inputChars": "请输入字数" + }, + "request": { + "warning": "如非特殊情况,不需要修改或者配置", + "apiAddress": "API 地址", + "apiPlaceholder": "请输入 API 请求地址", + "save": "保存", + "reset": "重置", + "msg": { + "enterApi": "请输入 API 地址", + "validUrl": "请输入有效的 HTTP/HTTPS 地址", + "saved": "请求地址保存成功", + "reset": "已重置为默认地址", + "refreshFailed": "刷新失败", + "refreshSuccess": "刷新成功" + }, + "refresh": "刷新" + }, + "about": { + "slogan": "开源的AI驱动漫画 / 分镜创作工具", + "latestVersion": "当前为最新版本", + "checkUpdate": "检查更新", + "codeRepository": "代码仓库", + "githubRepo": "GitHub 仓库", + "giteeRepo": "Gitee 仓库", + "versionUpdate": "版本更新", + "checkUpdateGithub": "检查更新(GitHub)", + "getFromGithub": "从GitHub Release 获取最新版本", + "checkUpdateGitee": "检查更新(Gitee)", + "getFromGitee": "从Gitee Release获取最新版本", + "license": "许可证", + "licenseDesc": "开源许可协议·点击查看详情", + "updateAvailable": "发现新版本", + "currentVersion": "当前版本", + "latestVersionLabel": "最新版本", + "selectUpdateSource": "选择更新源", + "github": "GitHub", + "gitee": "Gitee", + "confirmUpdate": "确认更新", + "cancel": "取消", + "updating": "正在更新...", + "updateSuccess": "更新成功,请重启应用", + "updateFailed": "更新失败,请重试", + "noUpdate": "当前已是最新版本", + "upToDate": "检测到新版本", + "reinstallRequired": "将自动打开浏览器并下载,如未打开请手动打开", + "confirmReinstall": "复制链接" + }, + "logout": { + "warning": "退出登录后,您需要重新登录才能继续使用系统。", + "confirmLogout": "确定要退出登录吗?", + "logout": "退出登录", + "msg": { + "logoutSuccess": "退出登录成功", + "logoutFailed": "退出登录失败,请重试" + } + }, + "file": { + "quickOpen": "快捷打开目录", + "open": "打开", + "dockerDesc": "Docker/前后端分离部署请前往\"/data/*\"目录手动管理文件。", + "desktopOnly": "该功能仅支持桌面端", + "folders": { + "data": "data", + "dataDesc": "数据目录。", + "logs": "data/logs", + "logsDesc": "运行日志与错误日志。", + "oss": "data/oss", + "ossDesc": "文件存储相关资源。", + "skills": "data/skills", + "skillsDesc": "技能与提示配置文件。", + "models": "data/models", + "modelsDesc": "模型文件与配置。", + "web": "data/web", + "webDesc": "Web 相关资源,如前端构建产物等。", + "serve": "data/serve", + "serveDesc": "后端服务相关文件。", + "vendor": "data/vendor", + "vendorDesc": "供应商目录。" + }, + "openFailed": "打开文件夹失败" + }, + "dev": { + "aiDevtool": "切换桌面端模式", + "devtool": "打开控制台", + "switchAiDevTool": "关闭/开启 {'@'}ai-sdk/devtools", + "localStorage": "浏览器 localStorage 管理", + "localStorageSearchPlaceholder": "按 key 或 value 搜索", + "localStorageKey": "Key", + "localStorageValue": "Value", + "localStorageKeyPlaceholder": "输入 localStorage 的 key", + "localStorageValuePlaceholder": "输入 localStorage 的 value", + "actions": "操作", + "refresh": "刷新", + "add": "新增", + "update": "更新", + "clearAll": "清空全部", + "copyKey": "复制 Key", + "copyValue": "复制 Value", + "format": "格式化 JSON", + "localStorageCount": "共 {total} 条,当前显示 {filtered} 条", + "creating": "正在新增", + "editing": "正在编辑:{key}", + "edit": "编辑", + "delete": "删除", + "save": "保存", + "reset": "重置", + "warning": "以下为开发者工具,谨慎操作!", + "openDevtool": "打开", + "devtoolsDoc": "文档地址", + "devtoolsDesc": "开启后会在Toonflow安装目录创建.devtools文件夹,请确保Toonflow有写入权限(管理员身份运行)。", + "devtoolsDesc2": "在该目录运行 npx {'@'}ai-sdk/devtools 开启遥测调试", + "openDevtoolFailed": "打开开发者工具失败,请确保已安装Toonflow桌面端", + "notInElectron": "WEB环境请手动打开浏览器控制台", + "msg": { + "localStorageKeyRequired": "请输入 localStorage 的 key", + "localStorageKeyExists": "该 key 已存在,请更换后再保存", + "localStorageSaved": "localStorage 已保存", + "localStorageDeleted": "localStorage 已删除", + "localStorageCleared": "localStorage 已清空", + "localStorageKeyCopied": "Key 已复制", + "localStorageValueCopied": "Value 已复制", + "copyFailed": "复制失败", + "localStorageValueEmpty": "当前 value 为空,无法格式化", + "localStorageFormatted": "JSON 格式化成功", + "localStorageFormatFailed": "格式化失败,请检查是否为合法 JSON", + "deleteConfirmTitle": "确认删除 localStorage", + "deleteConfirmBody": "确认删除 key:{key} 吗?", + "confirmDelete": "确认删除", + "clearConfirmTitle": "确认清空 localStorage", + "clearConfirmBody": "该操作会清空当前站点下所有 localStorage 数据,且不可恢复,是否继续?", + "confirmClear": "确认清空", + "cancel": "取消" + } + }, + "generate": { + "modelChnageSure": "确认清空" + } + }, + "workbench": { + "selectProject": "请选择项目", + "menu": { + "myProject": "我的项目", + "taskCenter": "任务中心", + "novel": "小说原文", + "scriptAgent": "剧本Agent", + "scriptManage": "剧本管理", + "cornerScape": "塑角造景", + "production": "视频生产", + "assetCenter": "资产中心", + "settings": "设置", + "jumpGithub": "跳转Github", + "feedbackQuestions": "反馈问题" + }, + "project": { + "title": "我的项目", + "subtitle": "管理您的所有短剧项目", + "newProject": "新建项目", + "dialog": { + "editTitle": "编辑项目", + "addTitle": "新建项目", + "save": "保存", + "ok": "确定", + "cancel": "取消", + "projectType": "项目类型", + "selectType": "选择项目类型", + "basedOnNovel": "基于小说原文", + "projectName": "项目名称", + "projectNamePh": "请输入项目名称", + "novelType": "小说类型", + "novelTypePh": "例如:玄幻、科幻、言情", + "artStyle": "画风", + "selected": "已选:", + "selectArtStyle": "请选择画风", + "newArtStyle": "新建画风", + "loading": "加载中...", + "videoRatio": "影片比例", + "novelIntro": "小说简介", + "novelIntroPh": "请输入小说简介", + "editArtStyleTitle": "编辑画风", + "newArtStyleTitle": "新建画风", + "artStyleName": "画风名称", + "artStyleNamePh": "请输入画风名称", + "artStyleImage": "画风图片", + "remove": "移除", + "uploadCover": "上传封面", + "artStylePrompt": "提示词", + "aiExtract": "AI提取提示词", + "promptPlaceholder": "描述提示词", + "visualManual": "视觉手册", + "newVisualManual": "新建视觉手册", + "editVisualManualTitle": "编辑视觉手册", + "newVisualManualTitle": "新建视觉手册", + "visualManualName": "视觉手册名称", + "visualManualNamePh": "请输入视觉手册名称", + "visualManualCover": "视觉手册封面", + "visualManualPrompt": "视觉手册提示词", + "modelData": "选择图片模型", + "videoModelData": "选择视频模型", + "prompt": { + "placeholder": "输入提示词", + "saveSuccess": "更新成功", + "title": "提示词" + }, + "basedOnScript": "基于剧本", + "mdFile": "视觉手册文件", + "directorManual": "导演手册", + "addDirectorManual": "新建导演手册", + "editingDirectorManual": "编辑导演手册", + "newDirecorManualTitle": "新建导演手册", + "directorManualPrompt": "导演手册提示词", + "directorManualName": "导演手册名称", + "directorManualNamePh": "输入导演手册名称", + "directorFile": "导演手册文件", + "directorManualCover": "导演手册封面" + }, + "msg": { + "fetchFailed": "获取项目列表失败", + "notFound": "未找到该项目!", + "editSuccess": "编辑项目成功", + "editFailed": "编辑项目失败", + "addSuccess": "新增项目成功", + "addFailed": "新增项目失败", + "deleteHeader": "删除项目", + "deleteBody": "确定要删除该项目吗?", + "deleteConfirm": "删除", + "deleteCancel": "取消", + "deleteSuccess": "删除项目成功", + "deleteFailed": "删除项目失败", + "extractSuccess": "提示词提取成功", + "extractFailed": "提取失败", + "enterArtStyleName": "请输入画风名称", + "artStyleUpdated": "画风已更新", + "artStyleAdded": "画风已添加", + "operationFailed": "操作失败", + "enterVisualManualName": "请输入视觉手册名称", + "enterVisualManualImage": "请上传视觉手册封面图片", + "enterVisualManualTabData": "提示词不能为空", + "visualManualUpdated": "视觉手册已更新", + "visualManualAdded": "视觉手册已添加", + "deleteVisualManualHeader": "删除视觉手册", + "deleteVisualManualBody": "确定要删除视觉手册「{name}」吗?", + "deleteVisualManualConfirm": "删除", + "deleteVisualManualCancel": "取消", + "enterProjectName": "请输入项目名称", + "enterProjectIntro": "请输入小说简介", + "enterProjectType": "请输入小说类型", + "enterArtStyle": "请选择项目视觉手册", + "enterVideoRatio": "请选择影片比例", + "enterImageModel": "请选择图片模型", + "enterVideoModel": "请选择视频模型", + "visualManualDeleted": "删除成功", + "selectMode": "请选择模式", + "deleteDirectorManualHeader": "删除导演手册", + "deleteDirectorManualBody": "确定要删除导演手册「{name}」吗?", + "directorManualUpdated": "导演手册已更新", + "directorManualAdded": "导演手册已添加", + "directorManual": "请选择项目导演手册", + "modelProviderDisabled": "视频模型或图片模型供应商未启用或无模型供应商,请先配置" + }, + "type": { + "novel": "基于小说原文", + "script": "基于小说剧本" + } + }, + "novel": { + "importText": "导入原文", + "batchDelete": "批量删除", + "eventAnalysis": "事件分析", + "searchPlaceholder": "搜索原文名称...", + "search": "搜索", + "generating": "生成中...", + "genFailed": "生成失败", + "viewDetail": "查看详情", + "none": "无", + "edit": "编辑", + "delete": "删除", + "col": { + "id": "序号", + "reel": "卷", + "chapter": "章节名称", + "chapterData": "章节内容", + "event": "事件", + "operation": "操作" + }, + "msg": { + "batchDeleteHeader": "批量删除", + "batchDeleteBody": "确定要删除选中的 {count} 条数据吗?", + "batchDeleteSuccess": "批量删除成功", + "deleteHeader": "删除确认", + "deleteBody": "确定要删除章节名称为「{name}」的数据吗?", + "deleteSuccess": "删除成功", + "eventAnalysisHeader": "事件分析", + "eventAnalysisBody": "确定要对选中的 {count} 条数据进行事件分析吗?" + }, + "import": { + "title": "上传小说原文", + "step1": "第一步", + "step2": "第二步", + "step3": "第三步", + "dragUpload": "拖拽小说原文文件到此处或点击上传", + "uploadHint": "支持 .txt, .docx 格式,建议文件大小不超过 10MB", + "or": "或", + "pasteLabel": "直接粘贴小说原文内容", + "pastePlaceholder": "请输入小说原文内容", + "chars": "字符", + "tooShort": "内容过短,建议至少100字符", + "parsedChapters": "已解析 {count} 章节", + "nextStep": "下一步", + "prevStep": "上一步", + "selectedInfo": "已勾选:{count}字", + "eventAnalysis": "事件分析", + "saveAndAnalyze": "保存原文并分析事件", + "col": { + "chapter": "章", + "reel": "卷", + "chapterName": "章节名称", + "chapterData": "章节内容" + }, + "msg": { + "parseFailed": "文件解析失败,请重新上传", + "selectFile": "选择文件", + "docNotSupported": ".doc文件不支持解析,请转换为.ts文件", + "unsupportedType": "不支持的文件类型", + "fileTooLarge": "文件大小超过10MB,请上传更小的文件", + "selectChapters": "请先勾选章节", + "saveSuccess": "小说原文保存成功" + }, + "importAdd": "拖拽文件到此处或点击上传", + "limit": "支持 .ts格式" + }, + "editDialog": { + "title": "编辑小说原文", + "chapterName": "章节名称", + "chapterNamePh": "请输入章节名称", + "eventContent": "事件内容", + "eventContentPh": "输入事件内容", + "chapterContent": "章节内容", + "chapterContentPh": "请输入章节内容", + "cancel": "取消", + "save": "保存", + "msg": { + "updateSuccess": "小说原文更新成功" + } + }, + "event": { + "regenerate": "重新生成事件", + "batchDelete": "批量删除", + "noData": "暂无事件数据,点击开始生成", + "generate": "生成事件", + "generatingHint": "事件生成中,请稍候...", + "loading": "加载中...", + "delete": "删除", + "col": { + "id": "事件ID", + "eventName": "事件名称", + "chapters": "来源章节", + "detail": "事件过程", + "createTime": "创建时间", + "operation": "操作" + }, + "msg": { + "deleteHeader": "删除事件", + "deleteBody": "确定要删除这个事件吗?", + "deleteSuccess": "删除成功", + "generateSuccess": "事件生成成功", + "batchDeleteHeader": "批量删除", + "batchDeleteBody": "确定要删除选中的 {count} 条数据吗?", + "batchDeleteSuccess": "批量删除成功" + } + }, + "analysis": { + "analyzeFirst": "请先分析事件", + "startAnalysis": "开始分析", + "chapterHeader": "第{index}章 - {name}", + "analyzing": "事件分析中" + } + }, + "scriptAgent": { + "inputPlaceholder": "请输入内容", + "chapterEvents": "章节事件", + "clearMessageMemory": "清空消息记忆", + "clearSummaryMemory": "清空摘要记忆", + "clearAllMemory": "清空全部记忆", + "edit": "编辑", + "storySkeleton": "故事骨架", + "adaptationStrategy": "改编策略", + "script": "剧本", + "noContent": "暂无内容", + "relatedAssets": "关联资产", + "editScript": "编辑剧本", + "save": "保存", + "scriptTitle": "标题", + "titlePlaceholder": "请输入标题", + "content": "内容", + "contentPlaceholder": "请输入剧本内容", + "selectAssets": "选择资产", + "noAssets": "暂未关联资产", + "selectAssetsTitle": "选择关联资产", + "welcomeMsg": "你好!我是 Toonflow 智能助手,需要我开始为您生成剧本吗?", + "start": "开始", + "memoryType": { + "message": "消息记忆", + "summary": "摘要记忆", + "all": "全部记忆" + }, + "forceGenerate": { + "title": "是否强制生成", + "desc": "当前存在尚未完成事件分析的章节,是否仍然强制继续生成?", + "confirm": "强制生成", + "cancel": "取消" + }, + "msg": { + "clearConfirm": "确认清空", + "clearBody": "确定要清空{type}吗?此操作无法撤销。", + "confirmClear": "确认清空", + "cancel": "取消", + "memoryCleared": "{type}已清空", + "scriptUpdated": "剧本更新成功", + "scriptUpdateFailed": "更新剧本失败,请稍后再试", + "searchScriptFailed": "搜索剧本失败", + "updated": "保存成功", + "error": "保存失败", + "reconnect": "重新连接", + "notReconnect": "重新连接对话会被切断是否确认", + "keepReconnect": "确认", + "deleteConfirm": "删除确认", + "deleteBody": "删除正文", + "confirmDelete": "确认删除", + "scriptDeleted": "脚本已删除" + }, + "reconnect": "重新连接", + "deepThink": "深度思考", + "thinkLevel": { + "off": "关闭思考", + "light": "轻度思考", + "deep": "深度思考", + "extreme": "极致思考" + } + }, + "cornerScape": { + "batchSettings": "批量生成设置", + "quickActions": "快捷指令", + "selectUngenerated": "全选未生成项", + "selectGenerated": "全选已生成项", + "selectFailed": "全选错误项", + "invertSelection": "反选", + "clearSelection": "取消选择", + "batchPreview": "批预览图片", + "assetTypeFilter": "素材类型筛选", + "genModel": "生成模型", + "resolution": "分辨率", + "resolutionPh": "请选择分辨率", + "concurrency": "并发数量", + "concurrencyPh": "请输入并发数", + "startBatch": "开始批量生成图片", + "waitingGen": "等待生成", + "generating": "生成中", + "genFailed": "生成失败", + "imageError": "图片错误", + "typeRole": "角色", + "typeScene": "场景", + "typeTool": "工具", + "typeUnknown": "未知", + "descriptionSuffix": "描述:", + "operateScriptFirst": "请先操作剧本", + "individualConfig": "单独配置", + "noImage": "暂无图片", + "promptLabel": "提示词", + "promptPh": "请输入提示词", + "aiPolish": "AI 润色", + "regenerate": "重新生成", + "filterRole": "人物", + "filterScene": "场景", + "filterTool": "道具", + "unnamed": "未命名", + "noDescription": "无描述", + "msg": { + "selectModel": "请选择生成模型", + "selectResolution": "请选择分辨率", + "enterPrompt": "请输入提示词", + "enterPromptFirst": "请先输入提示词", + "genSuccess": "{name} 生成成功", + "genFailed": "{name} 生成失败", + "promptGenSuccess": "提示词生成成功", + "polishFailed": "润色失败,请重试", + "selectAtLeastOne": "请至少选择一个资产进行批量生成", + "batchStarted": "开始批量生成,共 {count} 个,并发数 {concurrent}", + "batchItemFailed": "{name} 生成失败:{error}", + "batchComplete": "批量生成完成", + "batchFailed": "批量生成失败", + "replaceFailed": "替换失败", + "replaceSuccess": "替换成功", + "emptyPrompt": "勾选的数据{emptyPromptNames}提示词为空,请先生成提示词", + "promptGenFail": "提示词生成失败", + "saveSuccess": "修改提示词成功", + "saveFailed": "提示词修改失败" + }, + "history": "历史图片", + "confirmReplace": "确认替换", + "batchGenerationPrompt": "批量生成提示词", + "generatingPrompt": "生成中", + "selectAll": "全选", + "selectPromptEmpty": "全选提示词为空", + "noEmptyPrompt": "没有提示词为空的资产", + "selectedCount": "已选中{count}个资产", + "cancelGeneration": "取消生成", + "selectGenerating": "选择正在生成项", + "noGenerating": "没有正在生成的数据", + "checkNumber": "选择数量" + }, + "script": { + "searchPlaceholder": "搜索剧本名称...", + "search": "搜索", + "addScript": "新建剧本", + "cancelSelectAll": "取消全选", + "selectAll": "全选", + "exportScript": "导出剧本", + "msg": { + "extracting": "资产提取中", + "extractFailed": "资产提取失败", + "extractingInProgress": "正在提取中", + "projectNotFound": "项目未找到", + "selectExport": "请选择导出剧本", + "deleteHeader": "确认删除", + "deleteBody": "确认要删除这个剧本吗?次操作无法复原", + "deleteConfirm": "删除", + "cancel": "取消", + "deleteSuccess": "删除成功", + "deleteFailed": "删除失败", + "selectDelScript": "请选择删除剧本", + "batchDeleteHeader": "批量删除", + "batchDeleteBody": "确定要删除选中的{count}个剧本吗?此操作无法复原", + "batchDeleteSuccess": "批量删除成功", + "searchFailed": "搜索剧本失败", + "selectsExport": "请选择导出剧本" + }, + "add": { + "title": "新增剧本", + "scriptName": "剧本名称", + "scriptNamePh": "请输入剧本名称", + "uploadFile": "上传文件", + "dragUpload": "拖拽剧本文件到此处或点击上传", + "uploadHint": "支持 .txt, .docx 格式,建议文件大小不超过 10MB", + "scriptContent": "剧本内容", + "scriptContentPh": "请上传或输入剧本内容...", + "relatedAssets": "关联资产", + "selectAssets": "选择资产", + "noAssets": "暂未关联资产", + "cancel": "取消", + "confirm": "确认", + "msg": { + "fileReadFailed": "文件读取失败", + "docNotSupported": ".doc文件不支持解析,请转换为.txt或.docx文件", + "unsupportedType": "不支持的文件类型", + "fileTooLarge": "文件大小超过10MB,请上传更小的文件", + "parsing": "文件解析中...", + "parseFailed": "文件解析失败,请重新上传", + "selectAssetsTitle": "选择关联资产", + "enterContent": "请上传或输入剧本内容", + "enterName": "请输入剧本名称", + "addSuccess": "剧本添加成功", + "addFailed": "添加剧本失败,请稍后再试" + } + }, + "edit": { + "title": "剧本详情", + "scriptName": "剧本名称", + "scriptNamePh": "请输入剧本名称", + "scriptContent": "剧本内容", + "scriptContentPh": "请输入剧本内容...", + "relatedAssets": "关联资产", + "selectAssets": "选择资产", + "noAssets": "暂未关联资产", + "msg": { + "selectAssetsTitle": "选择关联资产", + "updateSuccess": "剧本更新成功", + "updateFailed": "更新剧本失败,请稍后再试" + } + }, + "deleteScript": "批量删除剧本", + "extractAssets": "提取资产", + "import": { + "getAiRegex": "AI解析正则", + "episodeRegexPh": "自定义剧本拆分正则" + } + }, + "assets": { + "addPrefix": "新增", + "batchGenerate": "批量生成", + "generatePrompt": "生成提示词", + "generateImage": "生成图片", + "batchDelete": "批量删除", + "searchPlaceholder": "搜索资产名称...", + "search": "搜索", + "preview": "预览", + "generate": "生成", + "edit": "编辑", + "delete": "删除", + "generating": "生成中", + "play": "播放", + "mediaPreview": "媒体预览", + "confirmBatch": "是否确认{type}!", + "model": "模型", + "resolution": "分辨率", + "resolutionPh": "请选择分辨率", + "batchGenPrompt": "批量生成提示词", + "batchGenImage": "批量生成图片", + "role": "角色", + "prop": "道具", + "scene": "场景", + "clip": "素材", + "uploadSuccess": "上传成功", + "selectAtLeastOne": "请至少选择一个资产", + "noDescription": "无描述", + "promptGenSuccess": "「{name}」提示词生成成功", + "promptGenFail": "「{name}」提示词生成失败:{error}", + "selectModel": "请选择模型", + "selectResolution": "请选择分辨率", + "noPromptForImage": "「{name}」没有提示词,无法生成图片", + "imageGenSuccess": "「{name}」图片生成成功", + "imageGenFail": "「{name}」图片生成失败:{error}", + "confirmDeleteHeader": "确认删除", + "confirmBatchDeleteBody": "确定要批量删除这些资产吗?此操作无法撤销。", + "confirmDeleteBody": "确定要删除这资产吗?此操作无法撤销。", + "deleteBtn": "删除", + "cancelBtn": "取消", + "deleteSuccess": "资产删除成功", + "deleteFail": "资产删除失败", + "colPreview": "预览", + "colName": "名称", + "colPrompt": "提示词", + "colDescribe": "描述", + "colRemark": "备注", + "colCreateTime": "创建时间", + "colOperation": "操作", + "add": { + "name": "名称", + "namePh": "请输入名称", + "describe": "描述", + "describePh": "请输入描述", + "remark": "备注", + "remarkPh": "请输入备注", + "prompt": "提示词", + "promptPh": "请输入提示词", + "nameRequired": "请输入名称", + "describeRequired": "请输入详情", + "remarkRequired": "请输入备注", + "updateSuccess": "资产更新成功", + "addSuccess": "资产添加成功" + }, + "gen": { + "header": "图片生成", + "uploadRef": "上传参考图片", + "optional": "可选", + "promptLabel": "生图提示词", + "smartGenerate": "智能生成", + "generatingPrompt": "智能生成提示词中...", + "promptPlaceholder": "描述您想要生成的图片内容,例如:一个充满科技感的未来城市,霓虹灯闪烁,赛博朋克风格...", + "selectModel": "选择模型", + "selectResolution": "选择分辨率", + "generateBtn": "生成图片", + "resultTitle": "生成结果", + "generatedCount": "已生成 {count} 张,请选择一张", + "generatingLabel": "生成中...", + "genFailed": "生成失败", + "confirmSelect": "确认选择", + "promptSuccess": "提示词生成成功", + "promptFail": "提示词生成失败", + "fillPrompt": "请填写提示词", + "pickResolution": "请选择分辨率", + "pickModel": "请选择模型", + "unnamed": "未命名", + "assetGenSuccess": "资产生成成功", + "assetGenFail": "资产生成失败", + "uploadOk": "上传成功", + "imageSelected": "已选择该图片", + "imageDeleted": "已删除该图片", + "imageSaved": "图片已保存", + "completed": "已完成" + }, + "batch": { + "header": "批量生成", + "selected": "已选择 {count} 项", + "selectAll": "全选", + "clearSelection": "清空选择", + "inputPh": "请输入内容", + "saveSelected": "保存选中 ({count})", + "colPreviewImg": "预览图", + "selectToSave": "请选择要保存的项目", + "saveSuccess": "保存成功", + "saveFail": "保存失败,请重试", + "promptDone": "提示词生成完成", + "promptFail": "提示词生成失败", + "missingPrompts": "有 {count} 个资产缺少提示词,请先生成提示词", + "imageDone": "图片生成完成", + "imageGenFail": "图片生成失败", + "unknownError": "未知错误", + "promptGenCancelled": "已取消生成" + }, + "confirmCancellation": "确定取消", + "confirmAgain": "确认取消吗?取消之后后台AI会继续调取扣费", + "sure": "确定" + }, + "production": { + "selectPlaceholder": "请选择剧集", + "edit": "编辑", + "node": { + "script": { + "title": "剧本", + "editDialog": "编辑剧本" + }, + "scriptPlan": { + "title": "导演计划", + "editDialog": "编辑导演计划" + }, + "storyboard": { + "title": "分镜面板", + "notGenerated": "未生成", + "scaleRatio": "缩放比例", + "gridPreview": "宫格预览", + "noPreviewImages": "暂无可预览的图片", + "imageLoadFailed": "图片加载失败", + "promptPlaceholder": "请输入提示词", + "prompt": "提示词", + "editInfo": "提示词修改", + "selectedCount": "已选 {count} 项", + "clearSelection": "取消选择", + "selectAll": "全选" + }, + "storyboardTable": { + "title": "分镜表", + "editDialog": "编辑分镜表" + }, + "assets": { + "title": "衍生资产", + "generateFailed": "生成失败", + "notGenerated": "未生成", + "originalAsset": "原资产", + "derived": "衍生", + "noDerivedAssets": "无衍生资产" + }, + "poster": { + "title": "视频封面", + "coverCount": "{count} 张" + }, + "workbench": { + "title": "视频工作台" + } + }, + "editImage": { + "upload": "上传", + "generate": "生成", + "saveFailed": "保存失败,请重试", + "fetchFailed": "获取数据失败", + "generating": "生成中...", + "deleteNode": "删除节点", + "ratio": "比例", + "quality": "质量", + "generateBtn": "生成图片", + "selectImage": "选择图片", + "imageGeneration": "图片生成", + "promptPlaceholder": "描述你想要生成的图片...", + "imageRef": "图{index}", + "videoRef": "视频{index}", + "audioRef": "音频{index}", + "reference": "参考{index}", + "noReferences": "暂无可引用的参考文件", + "selectModel": "请先选择模型", + "selectQuality": "请选择画质", + "selectRatio": "请选择比例", + "generateFailed": "生成失败", + "generateFirst": "请先生成图片", + "generatedResult": "生成结果", + "waitingGenerate": "等待生成", + "layoutLR": "自动排版-左右布局", + "layoutTB": "自动排版-上下布局", + "uploadAssetImage": "资产图片上传", + "uploadStoryboardImage": "分镜图片上传", + "uploadImage": "资产图片上传", + "mode": "模式", + "closeConfirmTitle": "是否关闭编辑面板?", + "closeConfirmBody": "关闭之后未保存的数据会丢失" + }, + "save": "选取", + "cancel": "取消", + "chatBox": { + "inputPlaceholder": "输入消息...", + "generateDerivedAssets": "生成衍生资产", + "welcomeMessage": "你好!我是你的 AI 助手,有什么可以帮你的吗?", + "adjustModel": "调整模型", + "startMakingVideo": "开始制作视频", + "startMakingVideoPrompt": "请帮我开始制作视频", + "clearMessageMemory": "清除消息记忆", + "clearSummaryMemory": "清除摘要记忆", + "clearAllMemory": "清除所有记忆", + "messageMemory": "消息记忆", + "summaryMemory": "摘要记忆", + "allMemory": "所有记忆", + "confirmClear": "清除记忆", + "confirmClearBody": "确定要清除{type}吗?", + "confirmClearBtn": "确定清除", + "memoryCleared": "{type}已清除" + }, + "wb": { + "quickPreview": "快速预览", + "videoGeneration": "分镜台", + "videoEditing": "剪辑台", + "hint": "提示", + "extractLines": "是否从视频中提取台词?", + "no": "否", + "confirm": "确定", + "extractLinesQuestion": "是否从视频中提取台词作为字幕?", + "importingLoading": "正在导入中,请稍候...", + "mainTrackVideo": "主轨道(视频)", + "subtitle1": "字幕1", + "storyboardVideoName": "{storyboard}-{id}.mp4" + }, + "preview": { + "noImage": "暂无图片", + "storyboardDesc": "分镜描述", + "serialNumber": "序号", + "noDescription": "暂无描述", + "duration": "时长", + "seconds": "秒", + "relatedAssets": "涉及资产", + "role": "角色", + "prop": "道具", + "scene": "场景", + "noCharacters": "暂无出场人物", + "imagePrompt": "图片提示词", + "selectAll": "全选", + "exportImage": "导出图片", + "sceneDescription": "画面描述", + "promptLabel": "提示词", + "restoreSort": "还原排序", + "restoreSortConfirm": "确定要还原为初始排序吗?", + "tip": "提示", + "selectAtLeastOne": "请至少选择一个分镜进行导出", + "exportFilename": "分镜图片" + }, + "generate": { + "noVideo": "暂无数据", + "videoPrompt": "视频提示词", + "promptPlaceholder": "输入提示词,描述你想要生成的视频内容...", + "refImage": "参考图", + "image": "图片", + "refVideo": "参考视频", + "refImageLabel": "参考图片", + "refAudio": "参考音频", + "muteAudio": "关闭音频", + "enableAudio": "开启音频", + "resolution": "分辨率", + "duration": "时长", + "generate": "生成", + "historyVersions": "历史版本", + "confirmSelection": "确认选中", + "noHistory": "暂无历史记录", + "generating": "生成中", + "generatingPrompt": "智能生成提示词中", + "generateFailed": "生成失败", + "selectAll": "全选", + "videoTrack": "视频轨道", + "batchGenerate": "批量生成", + "importToEditor": "导入剪辑台", + "modeSingleImage": "单图", + "modeMultiImage": "多图", + "modeGridImage": "网格多图", + "modeStartEnd": "首尾帧", + "modeText": "文生视频", + "modeVideoRef": "视频参考", + "modeImageRef": "图片参考", + "modeAudioRef": "音频参考", + "modeTextRef": "文本参考", + "startFrame": "首帧", + "startFrameOptional": "首帧(可选)", + "endFrame": "尾帧", + "endFrameOptional": "尾帧(可选)", + "selectRefImage": "选择参考图", + "selectRefImages": "选择参考图片", + "selectEndFrame": "选择尾帧图", + "selectRefVideoAsset": "选择参考视频", + "selectRefAudioAsset": "选择参考音频", + "selectRefImageAsset": "选择参考图片", + "selectImageSource": "选择图片来源", + "fromStoryboard": "分镜图", + "fromStoryboardDesc": "从分镜列表中选择图片", + "fromAssets": "资产图", + "fromAssetsDesc": "从资产库中选择图片", + "confirmDelete": "确认删除", + "confirmDeleteBody": "确定要删除这个视频吗?此操作无法撤销。", + "delete": "删除", + "cancel": "取消", + "deleteSuccess": "视频删除成功", + "deleteFailed": "删除失败", + "selectVideoFirst": "请先选择一个视频", + "confirmSuccess": "确认选中成功", + "batchSubmitted": "已提交批量生成请求,正在处理中...", + "configNotFound": "配置不存在", + "stateSuccess": "生成成功", + "statePending": "待生产", + "pollingFailed": "视频状态查询失败,请手动刷新", + "batchGeneratePrompt": "批量生成提示词", + "promptEmpty": "请先输入视频提示词", + "modelEmpty": "请先选择视频生成模型", + "batchPromptEmpty": "分镜 {names} 的视频提示词为空,请先生成或填写提示词", + "batchModelEmpty": "分镜 {names} 未配置视频生成模型,请先选择模型" + }, + "editVideo": { + "reset": "重置", + "undo": "撤销", + "redo": "重做", + "split": "分割", + "delete": "删除", + "rendering": "渲染中...", + "exportVideo": "导出视频", + "exportSuccess": "视频导出完成", + "exportFailed": "导出失败", + "sampleSubtitle": "示例字幕文本", + "customText": "自定义文本内容", + "transitionBetweenClips": "转场需要添加在两个相邻的 Clip 之间", + "transitionExists": "该位置已存在转场", + "videoPreviewArea": "视频预览区域", + "clipMaterials": "剪辑素材", + "propertyPanel": "属性面板", + "selectClip": "选择一个 Clip 查看属性", + "basicInfo": "基础信息", + "name": "名称", + "clipNamePlaceholder": "Clip 名称", + "startTime": "开始", + "endTime": "结束", + "totalDuration": "总时长", + "videoProperties": "视频属性", + "opacity": "不透明度", + "volume": "音量", + "playbackSpeed": "播放速度", + "audioProperties": "音频属性", + "fadeIn": "淡入", + "fadeOut": "淡出", + "transitionProperties": "转场属性", + "transitionType": "转场类型", + "transFade": "淡入淡出", + "transSlide": "滑动", + "transWipe": "擦除", + "transDissolve": "溶解", + "transZoom": "缩放", + "transRotate": "旋转", + "transitionDuration": "转场时长", + "subtitleProperties": "字幕属性", + "textContent": "文本内容", + "fontSize": "字体大小", + "copy": "复制", + "deleteConfirm": "删除确认", + "deleteClipConfirm": "确定要删除这个 Clip 吗?", + "avCanvasNotInit": "AVCanvas 尚未初始化", + "noExportContent": "没有可导出的内容", + "exportProject": "导出项目", + "transitionAdded": "已添加转场: {name}", + "splitClip": "分割片段", + "deleteClip": "删除片段", + "addClip": "添加 {name}", + "duplicateClip": "复制 Clip", + "addTransition": "添加转场", + "updateClip": "更新 Clip {key}", + "updatePlaybackRate": "更新播放倍速为 {rate}x", + "updateTransitionDuration": "更新转场时长", + "playbackRateRange": "播放倍速必须在 0.1 到 10 之间", + "updatePlaybackRateFailed": "更新播放倍速失败:", + "importProject": "导入项目", + "import": "导入" + }, + "clipType": { + "video": "视频", + "audio": "音频", + "subtitle": "字幕", + "transition": "转场", + "sticker": "贴纸", + "filter": "滤镜", + "effect": "特效" + }, + "track": { + "video": "视频", + "image": "图片", + "audio": "音频", + "subtitle": "字幕", + "text": "文本", + "sticker": "贴纸", + "filter": "滤镜", + "effect": "特效" + }, + "transition": { + "fade": "淡入淡出", + "slide": "滑动", + "slideLeft": "向左滑动", + "slideRight": "向右滑动", + "slideUp": "向上滑动", + "slideDown": "向下滑动", + "wipe": "擦除", + "wipeLeft": "向左擦除", + "wipeRight": "向右擦除", + "wipeUp": "向上擦除", + "wipeDown": "向下擦除", + "dissolve": "溶解", + "zoom": "缩放", + "zoomIn": "放大", + "zoomOut": "缩小", + "rotate": "旋转", + "circle": "圆形", + "diamond": "菱形", + "clock": "时钟", + "blur": "模糊" + }, + "media": { + "titleText": "标题文本", + "subtitleText": "字幕文本", + "customText": "自定义文本", + "media": "媒体", + "image": "图片", + "audio": "音频", + "subtitle": "字幕", + "transition": "转场", + "effect": "特效", + "filter": "滤镜", + "loading": "加载中...", + "subtitlePreview": "字", + "video": "视频" + }, + "effect": { + "fadeIn": "淡入", + "fadeOut": "淡出", + "flash": "闪烁", + "shake": "抖动", + "zoomIn": "放大进入", + "zoomOut": "缩小退出", + "pulse": "脉冲", + "rotateIn": "旋转进入", + "sticker1": "贴纸 1", + "sticker2": "贴纸 2" + }, + "filter": { + "grayscale": "黑白", + "sepia": "复古", + "warm": "暖色", + "cool": "冷色", + "vivid": "鲜艳", + "bright": "明亮", + "highContrast": "高对比", + "blur": "模糊", + "invert": "反色", + "semiTransparent": "半透明" + }, + "guideSwitchEpisode": "切换剧集", + "guideSwitchEpisodeBody": "切换剧集挪移到这里了哦", + "guideRefresh": "刷新数据", + "guideRefreshBody": "点击刷新按钮重新获取工作区数据", + "guideCanvasNav": "画布操作", + "guideCanvasNavBody": "滚轮上下平移画布,Ctrl + 滚轮缩放画布,按住鼠标左键拖拽自由平移", + "guideRightChat": "AI 对话", + "guideRightChatBody": "在右侧对话框中与 AI 交流,驱动自动化流程", + "guideLayoutBtn": "自动排版", + "guideLayoutBtnBody": "点击此按钮可自动整理节点布局", + "autoLayoutLR": "自动排版-左右布局", + "autoLayoutTB": "自动排版-上下布局", + "getFlowData": "刷新工作区", + "confirm": "切换剧集确认", + "confirmEpisodesSwitch": "当前任务仍在进行中,切换剧集会重连会话,是否继续切换?" + }, + "task": { + "title": "任务列表", + "subtitle": "您的最新任务执行记录", + "refresh": "刷新", + "categoryLabel": "任务大类:", + "stateLabel": "状态:", + "noFailReason": "暂无失败原因", + "stateAll": "全部", + "stateRunning": "进行中", + "stateCompleted": "已完成", + "stateFailed": "生成失败", + "fetchFailed": "获取任务列表失败", + "col": { + "taskClass": "任务大类", + "relatedObjects": "关联对象", + "model": "模型", + "describe": "描述", + "state": "状态", + "startTime": "时间", + "reason": "失败原因" + }, + "project": "项目名称:" + }, + "noVideo": "暂无视频", + "prompt": "视频提示词", + "generateText": "AI生成提示词", + "selectStoryboard": "选择分镜", + "generate": { + "noVideo": "暂无数据", + "prompt": "视频提示词", + "generateText": "生成提示词", + "selectStoryboard": "选择分镜", + "generate": "生成视频", + "history": "历史版本", + "generating": "生成中", + "generateFailed": "查看失败原因", + "selectAll": "全选", + "selected": "已选", + "batchGenerateText": "批量生成提示词", + "batchGenerateVideo": "批量生成视频", + "importVideo": "导入剪辑台", + "emptyTrack": "第{index}段", + "del": "删除", + "delConfirm": "确认删除该段?", + "selectSource": "选择来源", + "confirm": "从资产选择", + "cancel": "从分镜选择", + "generateSuccess": "视频生成成功", + "selectVideoFailed": "视频选择失败", + "selectVideoSuccess": "视频选择成功", + "previewVideo": "视频预览", + "selectTrackFirst": "请先选择分镜", + "noSelectedVideo": "没有选择的视频", + "generateConfirm": "确认生成", + "generateConfirmBody": "是否确认生成视频", + "generateVideosInBatches": "批量生成视频", + "generateStarted": "开始生成", + "promptEmpty": "勾选需要生成视频的数据有提示词为空,是否继续生成", + "skipDataWithEmptyVideoPromptWords": "需要生成的视频数据存在提示词为空", + "duration": "时长", + "resolution": "分辨率", + "delVideo": "确认删除此视频?", + "delSuccess": "删除成功", + "addReference": "添加参考", + "promptPlaceholder": "请输入视频提示词", + "downloadVideo": "批量下载视频", + "selectVideo": "请勾选需要下载的视频", + "batchDownloadVideo": "批量下载视频", + "storyboard": "分镜", + "assets": "资产", + "promptText": "生成视频提示词数据", + "videoMenu": "生成视频", + "videoPreview": "视频预览", + "referenceImage": "参考图片", + "generatePrompt": "生成提示词", + "generateVideo": "生成视频", + "modeChange": "清空内容", + "modeChangeConfirm": "切换模式将会清空当前选中的图片和提示词" + } + }, + "login": { + "slogan": "智能短剧创作平台", + "tips": "默认账号:admin / admin123", + "settings": "服务器设置", + "requestAddress": "请求地址", + "username": "用户名", + "password": "密码", + "login": "登录", + "usernameRequired": "请输入用户名", + "passwordRequired": "请输入密码", + "enterUsernameAndPassword": "请输入用户名或密码", + "loginSuccess": "登录成功", + "settingsSaved": "设置已保存" + }, + "common": { + "cancel": "取消", + "confirm": "确定", + "selectAssets": "选择资产", + "sessionExpired": "登录已过期,请重新登录", + "openSettings": "打开设置", + "cancelled": "已取消生成", + "defaultReel": "正文卷", + "save": "保存", + "submitting": "提交中", + "editSuccess": "修改成功", + "editFailed": "修改失败", + "submit": "提交" + }, + "workbench.project.msg.enterProjectQuality": "请选择图片质量", + "promptManage": { + "prompt": "提示词" + }, + "workbench.production.node.storyboard.batchGenerateImage": "重新生成", + "workbench.production.node.storyboard.batchGenerateFailed": "生成失败", + "workbench.production.node.storyboard.batchGenerateSuccess": "开始生成分镜", + "workbench.production.node.storyboard.removeFailed": "删除分镜失败", + "workbench.production.node.storyboard.editNode": "编辑", + "workbench.production.node.storyboard.deleteNode": "删除", + "workbench.production.node.storyboard.confirmDeleteBody": "确认是否删除该分镜", + "hello": { + "welcomeTitle": "欢迎使用 ToonFlow", + "welcomeDesc": "AI 驱动的漫画创作工作流平台,让我们花一分钟完成初始配置。", + "startConfig": "开始配置", + "skip": "跳过引导", + "configModel": "添加模型服务", + "configData": "配置 Agent", + "startUse": "开始使用", + "configModelTitle": "添加模型服务供应商", + "configModelDesc": "首先,你需要在设置中添加至少一个 AI 模型服务供应商(如 OpenAI、Claude 等),并填写对应的 API Key。", + "configModelTip": "点击下方按钮将打开设置页面的「模型服务」选项卡,添加供应商后回到此处继续。", + "configModelBtn": "前往配置模型服务", + "configAgentTitle": "分配 Agent 模型", + "configAgentDesc": "接下来,在 Agent 配置中为各个功能模块分配模型,这样系统才知道调用哪个模型来完成任务。", + "configAgentTip": "点击下方按钮将打开设置页面的「Agent 配置」选项卡,为各功能分配模型后回到此处。", + "configAgentBtn": "前往配置 Agent", + "finishTitle": "🎉 一切就绪!", + "finishDesc": "配置完成,现在你可以开始使用所有功能了。如需调整,随时可以在设置中修改。", + "qrcodeLabel": "加入微信交流群,获取更多帮助:", + "githubLabel": "如果觉得好用,请给我们一个 ⭐ Star 吧!", + "prevStep": "上一步", + "nextStep": "下一步", + "finish": "开始使用" + }, + "setting": { + "skillManagement": { + "search": "搜索文件名", + "empty": "没有匹配的文件", + "edit": "编辑", + "selectOnTheLeft": "请从左侧选择文件" + } + }, + "storyboard": { + "assetsNotExists": "资产不存在", + "assets": { + "notExist": "资产不存在", + "notDerivativeExist": "衍生资产不存在", + "derivativeUpdateSuccess": "更新成功", + "derivativeState": "未生成", + "derivativeAddSuccess": "添加成功", + "derivativeDelSuccess": "删除成功", + "notGenerated": "未生成" + }, + "addSuccess": "更新成功", + "state": { + "unused": "未生成" + }, + "saveSuccess": "添加成功" + }, + "productionAgent": { + "generating": "生成中" + }, + "skillScan": { + "scanning": "🔍 正在解析加载Skill", + "scanComplete": "✨ Skill 扫描完成", + "inserted": "✅ 新增{count}个Skill", + "updated": "🔄 更新{count}个Skill", + "removed": "🗑️ 移除{count}个Skill", + "scannedFiles": "📁 扫描 {count} 个文件", + "noDescription": "📝 {count}个Skill缺少描述", + "noAttribution": "👤 {count}个Skill缺少归属", + "configWarning": "⚠️ Skill 配置警告", + "openSettings": "打开设置", + "scanFailed": "❌ 扫描失败", + "checkNetwork": "🔌 请检查网络连接或稍后重试", + "retryLater": "🔁 请稍后重试" + }, + "generate": "生成视频", + "history": "历史版本", + "generating": "生成中", + "generateFailed": "失败", + "selectAll": "全选", + "selected": "已选 ", + "importVideo": "导入视频", + "emptyTrack": "123", + "del": "确认删除", + "delConfirm": "确认删除该段?", + "selectSource": "选择来源", + "confirm": "从资产选择", + "cancel": "从分镜选择", + "workbench.script.msg.waitExtract": "等待提取", + "workbench.script.msg.exportSuccess": "导出成功", + "workbench.script.msg.exportFailed": "导出失败", + "workbench.production.node.storyboard.videoDescPlaceholder": "请输入视频描述", + "workbench.production.node.storyboard.videoDesc": "视频描述", + "workbench.production.node.assets.confirmDeleteBody": "确认是否删除该资产", + "workbench.production.node.assets.removeFailed": "删除资产失败", + "version": { + "newVersion": "有新版本,是否更新?" + }, + "workbench.production.generatedNode.localUpload": "本地上传", + "workbench.production.editImage.uploadFailed": "图片上传失败", + "workbench.production.editImage.noImage": "请先添加图片", + "workbench.script.batchAddScript": "批量上传剧本", + "workbench.script.import.pasteLabel": "直接粘贴剧本内容", + "workbench.script.import.col.chapter": "集", + "workbench.script.import.col.scriptName": "剧本名称", + "workbench.script.import.col.scriptData": "剧本内容", + "workbench.script.import.episodeRegex": "剧本拆分正则", + "workbench.script.import.episodeRegexPh": "自定义剧本拆分正则,留空使用默认拆分正则(默认按 第X集 格式拆分)", + "workbench.script.import.regexInvalid": "正则表达式格式错误", + "workbench.script.import.parsedChapters": "已解析 {count} 集", + "workbench.script.import.msg.selectChapters": "请先勾选剧本", + "workbench.script.import.msg.saveSuccess": "剧本保存成功", + "workbench.script.import.batchTitle": "批量上传剧本", + "workbench.assets.sex": "性别", + "workbench.assets.audioText": "音频文本", + "workbench.assets.audio": "音频", + "workbench.assets.add.sex": "性别", + "workbench.assets.add.sexPh": "请输入性别", + "workbench.assets.add.pleaseUploadAudio": "请上传音频文件", + "workbench.cornerScape.assetsAudioLabel": "音频参考", + "workbench.cornerScape.selectAudio": "选择音频", + "workbench.cornerScape.noAudio": "暂无音频参考", + "workbench.assets.audioName": "音色", + "workbench.assets.add.audioName": "音色", + "workbench.assets.add.audioNamePh": "请输入音色", + "workbench.assets.add.audioFile": "音频文件", + "workbench.cornerScape.batchBingAudio": "一键绑定音频", + "workbench.cornerScape.msg.selectAtLeastBindOne": "请至少选择一个资产进行绑定", + "settings.generate.delConfirmBtn": "确认删除", + "settings.agent.advanced": "高级配置", + "settings.agent.ordinary": "简易配置", + "settings.agent.temperature": "温度", + "settings.agent.maxOutputTokens": "最大输出Token", + "settings.agent.auto": "自动", + "settings.agent.manual": "手动", + "settings.agent.autoHint": "由模型自动决定输出长度", + "settings.agent.msg.notmodel": "未选择模型", + "workbench.production.node.storyboard.generateImage": "生成分镜图", + "workbench.assets.preew": "1", + "workbench.generate.notSelectMode": "请先选择模型", + "workbench.production.node.storyboard.confirmBatchDeleteBody": "确认是否删除当前选中的{index}个分镜", + "workbench.production.node.storyboard.deleteSuccess": "分镜删除成功", + "workbench.production.node.storyboard.pleaseSelectImage": "请先选择分镜", + "workbench.production.editImage.reference": "图片{index}", + "workbench.cornerScape.audioState": "绑定中", + "workbench.generate.generateError": "发起生成请求失败", + "settings.vendor.videoGenerating": "视频生成较慢,请耐心等待", + "settings.memory.modelMap.editRefeshWord": "重新绑定", + "settings.memory.modelMap.delPrompt": "删除", + "workbench.cornerScape.textPromptInput": "提示词附加指令", + "workbench.cornerScape.textPromptPh": "请输入提示词附加指令(可选)", + "settings.vendor.testModel": "测试" +} diff --git a/web-core/src/locales/language/zh-TW.json b/web-core/src/locales/language/zh-TW.json new file mode 100644 index 0000000..8d8d161 --- /dev/null +++ b/web-core/src/locales/language/zh-TW.json @@ -0,0 +1,1537 @@ +{ + "components": { + "editMdPreivew": { + "title": "編輯", + "confirm": "儲存", + "cancel": "取消" + }, + "imageTools": { + "copy": "複製圖片", + "preview": "預覽", + "download": "下載", + "msg": { + "imageLoadFailed": "圖片載入失敗", + "convertFailed": "轉換失敗", + "copied": "已複製到剪貼簿", + "copyFailed": "複製失敗", + "downloadFailed": "下載失敗", + "downloadStarted": "開始下載", + "downloadBlockedOpenNewWindow": "目前的圖片來源可能限制下載,已嘗試在新視窗開啟" + } + }, + "migrateShow": { + "title": "轉移資料", + "desc": "偵測到您有舊版本的資料,是否需要轉移?", + "hide": "不再顯示", + "confirm": "確定", + "msg": { + "migrateSuccess": "資料轉移成功", + "migrateFailed": "資料轉移失敗" + } + }, + "modelSelect": { + "placeholder": "請選擇模型", + "type": { + "image": "圖片", + "text": "文字", + "video": "影片" + }, + "msg": { + "fetchModelFailed": "取得模型資料失敗:" + }, + "goSetting": "去設定中加入模型" + }, + "update": { + "title": "發現新版本", + "currentVersion": "目前版本:", + "latestVersion": "最新版本:", + "tip": "發現新版本,建議您更新以獲得更好的體驗", + "later": "稍後提醒", + "updateNow": "立即更新" + } + }, + "settings": { + "title": "ToonFlow 設定", + "menu": { + "language": "語言設定", + "vendorConfig": "模型服務", + "agentConfig": "Agent 設定", + "promptManage": "提示詞管理", + "skillManagement": "Skills技能管理", + "memoryConfig": "Agent 記憶設定", + "loginConfig": "登入設定", + "dbConfig": "資料庫操作", + "fileManagement": "檔案管理", + "otherConfig": "其他設定", + "requestConfig": "請求網址", + "about": "檢查更新", + "logoutConfig": "登出", + "skillsSkillsManagement": "Skills技能管理" + }, + "language": { + "desc": "選擇介面顯示語言", + "msg": { + "saved": "語言設定已儲存" + } + }, + "vendor": { + "addVendor": "新增供應商", + "noVendor": "暫無供應商,請先新增", + "required": "必填", + "optionalSection": "選填項目", + "modelSettings": "模型設定", + "addManually": "手動新增", + "test": "測試", + "edit": "編輯", + "delete": "刪除", + "deleteVendor": "刪除供應商", + "editCode": "編輯程式碼", + "updateConfig": "更新設定", + "addModel": "新增模型", + "editModel": "編輯模型", + "displayName": "顯示名稱", + "displayNamePlaceholder": "例如:GPT-4o", + "modelId": "模型 ID", + "modelIdPlaceholder": "例如:gpt-4o", + "modelType": "模型類型", + "multimodal": "多模態", + "supported": "支援", + "notSupported": "不支援", + "toolCall": "工具呼叫", + "imageMode": "圖片模式", + "videoMode": "影片模式", + "audioOutput": "音訊輸出", + "durationResolution": "時長 / 解析度對應", + "durationSec": "時長(秒)", + "resolution": "解析度", + "enterAndPress": "輸入後按下 Enter", + "addDurationResolution": "新增一組時長 / 解析度", + "testResult": "測試結果", + "generating": "正在生成中...", + "addVendorDialog": "新增供應商", + "codeEditorInfo": "請撰寫 TypeScript 程式碼設定供應商資訊", + "reset": "重設", + "importFile": "匯入檔案", + "textModel": "文字模型", + "imageModel": "圖片模型", + "videoModel": "影片模型", + "textToImage": "文字生成圖片", + "textToVideo": "文字生成影片", + "singleImage": "單圖", + "multiImage": "多圖模式", + "multiReference": "多圖參考", + "multiReferenceMode": "多參模式", + "gridImage": "網格單圖", + "startEndRequired": "首尾影格(兩張必填)", + "endFrameOptional": "首尾影格(尾影格選填)", + "startFrameOptional": "首尾影格(首影格選填)", + "textRef": "文字", + "imageRef": "圖片", + "videoRef": "影片", + "audioRef": "音訊", + "audioOptional": "選填", + "audioOnly": "僅輸出有聲影片", + "noAudio": "僅輸出無聲影片", + "msg": { + "getVendorListFailed": "取得供應商列表失敗", + "vendorConfigUpdated": "供應商設定已更新", + "updateFailed": "更新失敗", + "highRiskConfirm": "⚠️ 高風險操作確認", + "addVendorRiskBody": "新增的 AI 供應商將賦予其存取系統 API 的權限,請確認您信任該供應商的程式碼來源!", + "iKnowRisk": "我了解風險", + "cancel": "取消", + "confirmAgain": "⚠️ 再次確認", + "addVendorConfirmBody": "確定要新增該供應商嗎?新增後它將參與系統的模型排程。", + "confirmAndAdd": "確認並新增", + "goBackCheck": "返回檢查", + "vendorAdded": "供應商已成功新增", + "addFailed": "新增失敗", + "updateVendorRiskBody": "更新 AI 供應商設定將修改其存取系統 API 的權限和行為,請確認您信任修改後的程式碼來源!", + "updateVendorConfirmBody": "確定要更新該供應商設定嗎?更新後將影響系統的模型排程。", + "confirmAndUpdate": "確認並更新", + "updateSuccess": "供應商設定更新成功", + "fillDisplayName": "請填寫顯示名稱", + "fillModelId": "請填寫模型 ID", + "selectImageMode": "請選擇圖片模式", + "selectVideoMode": "請選擇影片模式", + "groupPrefix": "第 {n} 組:", + "addDuration": "請新增時長", + "addResolution": "請新增解析度", + "selectVendorFirst": "請先選擇供應商", + "modelIdExists": "模型 ID 已存在", + "modelAdded": "模型已成功新增", + "modelUpdated": "模型已成功更新", + "enterApiKey": "請填寫 API KEY", + "enterApiUrl": "請填寫 API URL", + "testSuccess": "測試成功", + "imageGenSuccess": "圖片生成成功", + "videoGenSuccess": "影片生成成功", + "requestFailed": "請求失敗", + "deleteModelConfirm": "確認刪除模型", + "deleteModelBody": "刪除後不可恢復,是否繼續?", + "confirmDelete": "確認刪除", + "modelDeleted": "模型已刪除", + "deleteVendorConfirm": "確認刪除供應商", + "deleteVendorBody": "刪除後該供應商下所有模型將一併刪除,是否繼續?", + "vendorDeleted": "供應商已刪除", + "deleteFailed": "刪除失敗", + "enabled": "已啟用", + "disabled": "已停用", + "linkAddVendorRiskBody": "新增新的 AI 供應商將賦予其存取系統 ​​API 的權限,請確認您信任該供應商的連結來源!", + "importAdd": "新增新的 AI 供應商將賦予其存取系統 ​​API 的權限,請確認您信任該供應商的文件來源!", + "linkAddFailed": "連結新增失敗" + }, + "think": "深度思考", + "code": "程式碼", + "linkAddPlaceholder": "輸入連結添加", + "noFileSelected": "成功導入文件", + "linkAdd": "確認" + }, + "agent": { + "bannerDesc": "使用 Toonflow 官方中繼網站,支援一鍵帶入設定,開箱即用,無需手動設定。", + "visitWebsite": "進入網站", + "fillKey": "填入 KEY", + "oneClickFill": "一鍵帶入", + "notOpen": "尚未開放", + "notConfigured": "尚未設定", + "modelConfig": "模型設定", + "confirm": "確認", + "cancel": "取消", + "selectModel": "選擇模型", + "fillKeyHeader": "填入 Toonflow 平台的官方 KEY", + "keyPlaceholder": "請輸入 KEY", + "save": "儲存", + "msg": { + "notAvailable": "該功能暫未開放,敬請期待", + "configSuccess": "設定成功", + "updateConfigFailed": "更新設定失敗:", + "keyValid": "KEY 有效,已成功連接 Toonflow 平台", + "keyInvalid": "KEY 無效,請檢查後重新輸入:", + "enterKey": "請輸入 KEY", + "saveFailed": "儲存失敗:", + "getAgentListFailed": "取得 Agent 設定列表失敗:" + }, + "temperature": "溫度" + }, + "memory": { + "warning": "以下設定項目已預設為推薦值。除非您清楚了解各項設定的含義及影響,否則建議維持現有設定", + "vectorModelConfig": "向量模型設定", + "modelFilePath": "模型檔案路徑", + "quantizationType": "量化類型", + "quantizationPlaceholder": "請輸入量化類型", + "memoryParams": "記憶參數", + "messagesPerSummary": "觸發訊息壓縮筆數", + "messagesPerSummaryHelp": "保留最近 N 筆對話上下文。", + "shortTermLimit": "單次取得未壓縮訊息筆數", + "shortTermLimitHelp": "檢索時回傳的候選記憶筆數。", + "summaryMaxLength": "壓縮最大字元數", + "summaryMaxLengthHelp": "訊息壓縮時允許的最大字元數", + "summaryLimit": "允許查詢已壓縮訊息筆數", + "summaryLimitHelp": "允許查詢已壓縮訊息筆數", + "ragLimit": "搜尋記憶筆數", + "ragLimitHelp": "檢索時取得的訊息數。", + "deepRetrieveSummaryLimit": "向量召回壓縮訊息數", + "deepRetrieveSummaryLimitHelp": "檢索壓縮訊息內容時取得的訊息數。", + "saveConfig": "儲存設定", + "clearMemory": "清除記憶", + "restoreDefault": "還原預設設定", + "msg": { + "saved": "記憶設定已儲存", + "clearConfirmTitle": "確認清除記憶", + "clearConfirmBody": "該操作會清除 AI 全域記憶資料,且不可復原,是否繼續?", + "confirmClear": "確認清除", + "cancel": "取消", + "cleared": "記憶已清除", + "clearFailed": "清除記憶失敗" + }, + "modelMap": { + "name": "模型名稱", + "model": "模型", + "type": "類型", + "editWord": "綁定提示詞", + "operation": "操作", + "bindingSuccessful": "綁定成功", + "bindingFailed": "綁定失敗", + "currentBinding": "目前綁定", + "noBinding": "未綁定", + "bound": "已綁定", + "unbind": "取消綁定", + "filenName": "文件名稱" + } + }, + "login": { + "username": "使用者名稱", + "usernamePlaceholder": "請輸入使用者名稱", + "password": "密碼", + "passwordPlaceholder": "請輸入密碼", + "modify": "修改", + "msg": { + "enterUsername": "請輸入使用者名稱", + "usernameLength": "使用者名稱長度為 2-20 個字元", + "enterPassword": "請輸入密碼", + "passwordLength": "密碼長度為 6-20 個字元", + "fetchFailed": "取得使用者資訊失敗", + "saveSuccess": "儲存成功", + "saveFailed": "儲存失敗" + } + }, + "db": { + "clearDb": "清除資料庫", + "clearDbDesc": "清除所有資料表中的資料,保留資料表結構", + "clearData": "清除資料", + "confirmAction": "確認操作", + "dbInfo": "資料庫概覽", + "dbInfoDesc": "查看所有資料表名稱和記錄數", + "viewInfo": "查看資訊", + "tableName": "表名", + "rowCount": "記錄數", + "totalTables": "共 {count} 個表", + "exportDb": "匯出資料庫", + "exportDbDesc": "將所有資料表匯出為 JSON 備份檔案", + "exportData": "匯出資料", + "importDb": "匯入資料庫", + "importDbDesc": "從 JSON 備份檔案恢復資料(將覆蓋當前資料)", + "importData": "匯入資料", + "clearTable": "清除指定資料表", + "clearTableDesc": "選擇一個資料表並清除其中的資料", + "clearTableBtn": "清除表", + "selectTable": "請選擇表", + "msg": { + "clearDbTitle": "清除資料庫", + "firstConfirm": "確定要清除所有資料表嗎?資料清除後無法復原!", + "secondConfirm": "這是最後一次確認,清除後所有資料將永久遺失!", + "keyword": "清除", + "confirm": "確認", + "pleaseInput": "請輸入", + "cleared": "所有資料表已清除", + "operationFailed": "操作失敗,請重試", + "cancelled": "操作已取消", + "exportSuccess": "資料庫匯出成功", + "exportFailed": "匯出失敗", + "importSuccess": "資料庫匯入成功,即將跳轉到登入頁", + "importFailed": "匯入失敗", + "invalidFile": "無效的備份檔案", + "clearTableSuccess": "資料表已清除", + "clearTableFailed": "清除資料表失敗", + "clearTableConfirm": "確定要清除表 {name} 嗎?此操作不可復原!", + "importConfirm": "匯入將覆蓋當前所有資料,確定要繼續嗎?", + "importSecondConfirm": "最後確認:匯入後當前資料將全部被替換!", + "noTableSelected": "請先選擇一個表", + "loadingDbInfo": "載入資料庫資訊中...", + "loadDbInfoFailed": "取得資料庫資訊失敗" + } + }, + "other": { + "requestTimeout": "請求逾時時間", + "seconds": "秒", + "inputSeconds": "請輸入秒", + "assetConcurrency": "資產生成並發數", + "count": "個", + "inputCount": "請輸入個數", + "chapterRegex": "章節拆分正規表示式", + "restoreDefault": "還原預設", + "regexPlaceholder": "請輸入正規表示式", + "canvasScroll": "畫布滾動", + "canvasIsDisabled": "畫布縮放", + "agentCanvasScalingMethod": "生產頁無限畫布滾輪操作", + "zoom": "縮放", + "scroll": "捲動", + "isInteracting": "生產頁無限畫布拖曳性能優化", + "closeIsInteracting": "關閉" + }, + "request": { + "warning": "若非特殊情況,不需要修改或者設定", + "apiAddress": "API 網址", + "apiPlaceholder": "請輸入 API 請求網址", + "save": "儲存", + "reset": "重設", + "msg": { + "enterApi": "請輸入 API 網址", + "validUrl": "請輸入有效的 HTTP/HTTPS 網址", + "saved": "請求網址儲存成功", + "reset": "已重設為預設網址", + "refreshFailed": "刷新失敗", + "refreshSuccess": "刷新成功" + }, + "refresh": "重新整理" + }, + "about": { + "slogan": "開源的 AI 驅動漫畫 / 分鏡創作工具", + "latestVersion": "目前為最新版本", + "checkUpdate": "檢查更新", + "codeRepository": "程式碼儲存庫", + "githubRepo": "GitHub 儲存庫", + "giteeRepo": "Gitee 儲存庫", + "versionUpdate": "版本更新", + "checkUpdateGithub": "檢查更新(GitHub)", + "getFromGithub": "從 GitHub Release 取得最新版本", + "checkUpdateGitee": "檢查更新(Gitee)", + "getFromGitee": "從 Gitee Release 取得最新版本", + "license": "授權條款", + "licenseDesc": "開源授權條款·點擊查看詳情", + "updateAvailable": "發現新版本", + "upToDate": "偵測到新版本", + "confirmReinstall": "複製連結", + "reinstallRequired": "將自動開啟瀏覽器並下載,如未開啟請手動開啟" + }, + "logout": { + "warning": "登出後,您需要重新登入才能繼續使用系統。", + "confirmLogout": "確定要登出嗎?", + "logout": "登出", + "msg": { + "logoutSuccess": "登出成功", + "logoutFailed": "登出失敗,請重試" + } + }, + "file": { + "quickOpen": "快速開啟目錄", + "open": "開啟", + "dockerDesc": "Docker/前後端分離部署請前往 \"/data/*\" 目錄手動管理檔案。", + "desktopOnly": "該功能僅支援桌面版", + "folders": { + "data": "data", + "dataDesc": "資料目錄。", + "logs": "data/logs", + "logsDesc": "執行記錄與錯誤記錄。", + "oss": "data/oss", + "ossDesc": "檔案儲存相關資源。", + "skills": "data/skills", + "skillsDesc": "技能與提示設定檔。", + "models": "data/models", + "modelsDesc": "模型檔案與設定。", + "web": "data/web", + "webDesc": "Web 相關資源,如前端建置產物等。", + "serve": "data/serve", + "serveDesc": "後端服務相關檔案。" + }, + "openFailed": "開啟資料夾失敗" + }, + "skill": { + "scanSkills": "掃描Skills" + }, + "dev": { + "warning": "以下為開發者工具,謹慎操作!", + "openDevtool": "打開", + "devtoolsDoc": "文件地址", + "devtoolsDesc": "開啟後會在Toonflow安裝目錄建立.devtools資料夾,請確保Toonflow有寫入權限(管理員身分執行)。", + "openDevtoolFailed": "開啟開發者工具失敗,請確保已安裝Toonflow桌面端", + "notInElectron": "WEB環境請手動開啟瀏覽器控制台" + } + }, + "workbench": { + "selectProject": "請選擇專案", + "menu": { + "myProject": "我的專案", + "taskCenter": "任務中心", + "novel": "小說原文", + "scriptAgent": "劇本 Agent", + "scriptManage": "劇本管理", + "cornerScape": "塑造角色與場景", + "production": "影片製作", + "assetCenter": "資產中心", + "settings": "設定", + "jumpGithub": "跳轉Github", + "feedbackQuestions": "回饋問題" + }, + "project": { + "title": "我的專案", + "subtitle": "管理您的所有短劇專案", + "newProject": "建立專案", + "dialog": { + "editTitle": "編輯專案", + "addTitle": "建立專案", + "save": "儲存", + "ok": "確定", + "cancel": "取消", + "projectType": "專案類型", + "selectType": "選擇專案類型", + "basedOnNovel": "基於小說原文", + "basedOnScript": "基於劇本", + "projectName": "專案名稱", + "projectNamePh": "請輸入專案名稱", + "novelType": "小說類型", + "novelTypePh": "例如:玄幻、科幻、言情", + "artStyle": "視覺手冊", + "selected": "已選:", + "selectArtStyle": "請選擇視覺手冊", + "newArtStyle": "新視覺手冊", + "loading": "載入中...", + "videoRatio": "影片比例", + "novelIntro": "小說簡介", + "novelIntroPh": "請輸入小說簡介", + "editArtStyleTitle": "編輯視覺手冊", + "newArtStyleTitle": "新視覺手冊", + "artStyleName": "視覺手冊名稱", + "artStyleNamePh": "請輸入視覺手冊名稱", + "artStyleImage": "視覺手冊封面", + "remove": "移除", + "uploadCover": "上傳封面", + "artStylePrompt": "視覺手冊提示詞", + "aiExtract": "AI 萃取提示詞", + "promptPlaceholder": "描述視覺手冊提示詞,用於產生圖片時指定視覺手冊", + "visualManual": "視覺手冊", + "newVisualManual": "新視覺手冊", + "editVisualManualTitle": "編輯視覺手冊", + "newVisualManualTitle": "新視覺手冊", + "visualManualName": "視覺手冊名稱", + "visualManualNamePh": "請輸入視覺手冊名稱", + "visualManualCover": "視覺手冊封面", + "visualManualPrompt": "視覺手冊提示詞", + "modelData": "選擇圖片模型", + "videoModelData": "選擇視訊模型", + "prompt": { + "placeholder": "輸入提示詞", + "saveSuccess": "更新成功", + "title": "提示詞" + }, + "mdFile": "視覺手冊文件", + "directorManual": "導演手冊", + "addDirectorManual": "新導演手冊", + "editingDirectorManual": "編輯導演手冊", + "newDirecorManualTitle": "新導演手冊", + "directorManualPrompt": "導演手冊提示詞", + "directorManualName": "導演手冊名稱", + "directorFile": "導演手冊文件", + "directorManualCover": "導演手冊封面" + }, + "msg": { + "fetchFailed": "取得專案列表失敗", + "notFound": "找不到該專案!", + "editSuccess": "編輯專案成功", + "editFailed": "編輯專案失敗", + "addSuccess": "新增專案成功", + "addFailed": "新增專案失敗", + "deleteHeader": "刪除專案", + "deleteBody": "確定要刪除該專案嗎?", + "deleteConfirm": "刪除", + "deleteCancel": "取消", + "deleteSuccess": "刪除專案成功", + "deleteFailed": "刪除專案失敗", + "extractSuccess": "提示詞萃取成功", + "extractFailed": "萃取失敗", + "enterArtStyleName": "請輸入視覺手冊名稱", + "artStyleUpdated": "視覺手冊已更新", + "artStyleAdded": "視覺手冊已添加", + "operationFailed": "操作失敗", + "enterVisualManualName": "請輸入視覺手冊名稱", + "enterVisualManualImage": "請上傳視覺手冊封面圖片", + "enterVisualManualTabData": "提示詞不能為空", + "visualManualUpdated": "視覺手冊已更新", + "visualManualAdded": "視覺手冊已添加", + "deleteVisualManualHeader": "刪除視覺手冊", + "deleteVisualManualBody": "確定要刪除視覺手冊「{name}」嗎?", + "deleteVisualManualConfirm": "刪除", + "deleteVisualManualCancel": "取消", + "emptyFields": "參數缺失", + "enterProjectName": "請輸入項目名稱", + "enterProjectIntro": "請輸入小說簡介", + "enterProjectType": "請輸入項目類型", + "enterArtStyle": "請選擇項目視覺手冊", + "enterVideoRatio": "請選擇影片比例", + "enterImageModel": "請選擇圖片模型", + "enterVideoModel": "請選擇視訊模型", + "visualManualDeleted": "刪除成功", + "selectMode": "請選擇模式", + "deleteDirectorManualHeader": "刪除導演手冊", + "deleteDirectorManualBody": "確定要刪除導演手冊「{name}」嗎?", + "directorManualUpdated": "導演手冊已更新", + "directorManualAdded": "導演手冊已添加", + "directorManual": "請選擇項目導演手冊", + "modelProviderDisabled": "視訊模型或圖片模型供應商未啟用或無模型供應商,請先配置" + }, + "type": { + "novel": "基於小說原文", + "script": "基於小說劇本" + } + }, + "novel": { + "importText": "匯入原文", + "batchDelete": "批次刪除", + "eventAnalysis": "事件分析", + "searchPlaceholder": "搜尋原文名稱...", + "search": "搜尋", + "generating": "生成中...", + "genFailed": "生成失敗", + "none": "無", + "edit": "編輯", + "delete": "刪除", + "col": { + "id": "序號", + "reel": "卷", + "chapter": "章節名稱", + "chapterData": "章節內容", + "event": "事件", + "operation": "操作" + }, + "msg": { + "batchDeleteHeader": "批次刪除", + "batchDeleteBody": "確定要刪除選定的 {count} 筆資料嗎?", + "batchDeleteSuccess": "批次刪除成功", + "deleteHeader": "刪除確認", + "deleteBody": "確定要刪除章節名稱為「{name}」的資料嗎?", + "deleteSuccess": "刪除成功", + "eventAnalysisHeader": "事件分析", + "eventAnalysisBody": "確定要對選定的 {count} 筆資料進行事件分析嗎?" + }, + "import": { + "title": "上傳小說原文", + "step1": "第一步", + "step2": "第二步", + "step3": "第三步", + "dragUpload": "拖曳小說原文檔案到此處或點擊上傳", + "uploadHint": "支援 .txt, .docx 格式,建議檔案大小不超過 10MB", + "or": "或", + "pasteLabel": "直接貼上小說原文內容", + "pastePlaceholder": "請輸入小說原文內容", + "chars": "字元", + "tooShort": "內容過短,建議至少 100 字元", + "parsedChapters": "已解析 {count} 章節", + "nextStep": "下一步", + "prevStep": "上一步", + "selectedInfo": "已勾選:{count} 字 (小於 200000 字)", + "eventAnalysis": "事件分析", + "saveAndAnalyze": "儲存原文並分析事件", + "col": { + "chapter": "章", + "reel": "卷", + "chapterName": "章節名稱", + "chapterData": "章節內容" + }, + "msg": { + "parseFailed": "檔案解析失敗,請重新上傳", + "selectFile": "選擇文件", + "docNotSupported": ".doc檔案不支援解析,請轉換為.ts文件", + "unsupportedType": "不支援的檔案類型", + "fileTooLarge": "檔案大小超過 10MB,請上傳更小的檔案", + "selectChapters": "請先勾選章節", + "saveSuccess": "小說原文儲存成功" + }, + "importAdd": "拖曳文件到此處或點擊上傳", + "limit": "支援 .ts格式" + }, + "editDialog": { + "title": "編輯小說原文", + "chapterName": "章節名稱", + "chapterNamePh": "請輸入章節名稱", + "eventContent": "事件內容", + "eventContentPh": "輸入事件內容", + "chapterContent": "章節內容", + "chapterContentPh": "請輸入章節內容", + "cancel": "取消", + "save": "儲存", + "msg": { + "updateSuccess": "小說原文更新成功" + } + }, + "event": { + "regenerate": "重新生成事件", + "batchDelete": "批次刪除", + "noData": "暫無事件資料,點擊開始生成", + "generate": "生成事件", + "generatingHint": "事件生成中,請稍候...", + "loading": "載入中...", + "delete": "刪除", + "col": { + "id": "事件 ID", + "eventName": "事件名稱", + "chapters": "來源章節", + "detail": "事件過程", + "createTime": "建立時間", + "operation": "操作" + }, + "msg": { + "deleteHeader": "刪除事件", + "deleteBody": "確定要刪除這個事件嗎?", + "deleteSuccess": "刪除成功", + "generateSuccess": "事件生成成功", + "batchDeleteHeader": "批次刪除", + "batchDeleteBody": "確定要刪除選定的 {count} 筆資料嗎?", + "batchDeleteSuccess": "批次刪除成功" + } + }, + "analysis": { + "analyzeFirst": "請先分析事件", + "startAnalysis": "開始分析", + "chapterHeader": "第{index}章 - {name}", + "analyzing": "事件分析中" + } + }, + "scriptAgent": { + "inputPlaceholder": "請輸入內容", + "chapterEvents": "章節事件", + "clearMessageMemory": "清除訊息記憶", + "clearSummaryMemory": "清除摘要記憶", + "clearAllMemory": "清除全部記憶", + "edit": "編輯", + "storySkeleton": "故事骨架", + "adaptationStrategy": "改編策略", + "script": "劇本", + "noContent": "暫無內容", + "relatedAssets": "關聯資產", + "editScript": "編輯劇本", + "save": "儲存", + "scriptTitle": "標題", + "titlePlaceholder": "請輸入標題", + "content": "內容", + "contentPlaceholder": "請輸入劇本內容", + "selectAssets": "選擇資產", + "noAssets": "暫未關聯資產", + "selectAssetsTitle": "選擇關聯資產", + "welcomeMsg": "你好!我是 Toonflow 智慧助手,需要我開始為您生成劇本嗎?", + "start": "開始", + "memoryType": { + "message": "訊息記憶", + "summary": "摘要記憶", + "all": "全部記憶" + }, + "msg": { + "clearConfirm": "確認清除", + "clearBody": "確定要清除{type}嗎?此操作無法復原。", + "confirmClear": "確認清除", + "cancel": "取消", + "memoryCleared": "{type}已清除", + "scriptUpdated": "劇本更新成功", + "scriptUpdateFailed": "更新劇本失敗,請稍後再試", + "searchScriptFailed": "搜尋劇本失敗", + "updated": "保存成功", + "error": "保存失敗", + "reconnect": "重新連接", + "notReconnect": "重新連結對話會被切斷是否確認", + "keepReconnect": "確認", + "deleteConfirm": "刪除確認", + "deleteBody": "刪除正文", + "confirmDelete": "確認刪除", + "scriptDeleted": "腳本已刪除" + }, + "reconnect": "重新連接" + }, + "cornerScape": { + "batchSettings": "批次生成設定", + "quickActions": "捷徑指令", + "selectUngenerated": "全選未生成項目", + "selectGenerated": "全選已生成項目", + "selectFailed": "全選錯誤項目", + "invertSelection": "反選", + "clearSelection": "取消選擇", + "batchPreview": "批次預覽圖片", + "assetTypeFilter": "素材類型篩選", + "genModel": "生成模型", + "resolution": "解析度", + "resolutionPh": "請選擇解析度", + "concurrency": "並發數量", + "concurrencyPh": "請輸入並發數", + "startBatch": "開始批量生成圖片", + "waitingGen": "等待生成", + "generating": "生成中", + "genFailed": "生成失敗", + "imageError": "圖片錯誤", + "typeRole": "角色", + "typeScene": "場景", + "typeTool": "工具", + "typeUnknown": "未知", + "descriptionSuffix": "描述:", + "operateScriptFirst": "請先操作劇本", + "individualConfig": "獨立設定", + "noImage": "暫無圖片", + "promptLabel": "提示詞", + "promptPh": "請輸入提示詞", + "aiPolish": "AI 潤飾", + "regenerate": "重新生成", + "filterRole": "人物", + "filterScene": "場景", + "filterTool": "道具", + "unnamed": "未命名", + "noDescription": "無描述", + "msg": { + "selectModel": "請選擇生成模型", + "selectResolution": "請選擇解析度", + "enterPrompt": "請輸入提示詞", + "enterPromptFirst": "請先輸入提示詞", + "genSuccess": "{name} 生成成功", + "genFailed": "{name} 生成失敗", + "promptGenSuccess": "提示詞生成成功", + "polishFailed": "潤飾失敗,請重試", + "selectAtLeastOne": "請至少選擇一個資產進行批次生成", + "batchStarted": "開始批次生成,共 {count} 個,並發數 {concurrent}", + "batchItemFailed": "{name} 生成失敗:{error}", + "batchComplete": "批次生成完成", + "batchFailed": "批量生成失敗", + "replaceFailed": "替換失敗", + "replaceSuccess": "替換成功", + "promptGenFail": "提示詞生成失敗", + "saveSuccess": "修改提示詞成功", + "saveFailed": "提示詞修改失敗" + }, + "history": "歷史圖片", + "confirmReplace": "確認替換", + "batchGenerationPrompt": "大量生成提示詞", + "generatingPrompt": "生成中", + "selectAll": "全選", + "selectPromptEmpty": "全選提示詞為空", + "noEmptyPrompt": "沒有提示詞為空的資產", + "selectedCount": "已選取{count}個資產", + "cancelGeneration": "取消生成", + "selectGenerating": "選擇正在產生項", + "noGenerating": "沒有正在產生的數據", + "checkNumber": "選擇數量" + }, + "script": { + "searchPlaceholder": "搜尋劇本名稱...", + "search": "搜尋", + "addScript": "建立劇本", + "cancelSelectAll": "取消全選", + "selectAll": "全選", + "exportScript": "匯出劇本", + "msg": { + "searchFailed": "搜尋劇本失敗", + "selectExport": "請先選擇要匯出的劇本", + "exportSuccess": "匯出成功", + "exportFailed": "匯出劇本失敗", + "deleteHeader": "確認刪除", + "deleteBody": "確定要刪除這個劇本嗎?此操作無法復原。", + "deleteConfirm": "刪除", + "cancel": "取消", + "deleteSuccess": "刪除成功", + "deleteFailed": "刪除失敗", + "selectDelScript": "請選擇刪除劇本", + "batchDeleteHeader": "批量刪除", + "batchDeleteBody": "確定要刪除選中的 {count} 個劇本嗎?此操作無法復原。", + "batchDeleteSuccess": "批量刪除成功", + "extractingInProgress": "正在提取中", + "projectNotFound": "項目未找到", + "selectsExport": "請選擇匯出劇本" + }, + "add": { + "title": "新增劇本", + "scriptName": "劇本名稱", + "scriptNamePh": "請輸入劇本名稱", + "uploadFile": "上傳檔案", + "dragUpload": "拖曳劇本檔案到此處或點擊上傳", + "uploadHint": "支援 .txt, .docx 格式,建議檔案大小不超過 10MB", + "scriptContent": "劇本內容", + "scriptContentPh": "請上傳或輸入劇本內容...", + "relatedAssets": "關聯資產", + "selectAssets": "選擇資產", + "noAssets": "暫未關聯資產", + "cancel": "取消", + "confirm": "確認", + "msg": { + "fileReadFailed": "檔案讀取失敗", + "docNotSupported": ".doc 檔案不支援解析,請轉換為 .txt 或 .docx 檔案", + "unsupportedType": "不支援的檔案類型", + "fileTooLarge": "檔案大小超過 10MB,請上傳更小的檔案", + "parsing": "檔案解析中...", + "parseFailed": "檔案解析失敗,請重新上傳", + "selectAssetsTitle": "選擇關聯資產", + "enterContent": "請上傳或輸入劇本內容", + "enterName": "請輸入劇本名稱", + "addSuccess": "劇本新增成功", + "addFailed": "新增劇本失敗,請稍後再試" + } + }, + "edit": { + "title": "劇本詳情", + "scriptName": "劇本名稱", + "scriptNamePh": "請輸入劇本名稱", + "scriptContent": "劇本內容", + "scriptContentPh": "請輸入劇本內容...", + "relatedAssets": "關聯資產", + "selectAssets": "選擇資產", + "noAssets": "暫未關聯資產", + "msg": { + "selectAssetsTitle": "選擇關聯資產", + "updateSuccess": "劇本更新成功", + "updateFailed": "更新劇本失敗,請稍後再試" + } + }, + "deleteScript": "大量刪除劇本", + "extractAssets": "", + "import": { + "episodeRegexPh": "自訂劇本拆分正規,留空使用預設拆分正規(預設按 第X集 格式拆分)" + } + }, + "assets": { + "addPrefix": "新增", + "batchGenerate": "批次生成", + "generatePrompt": "生成提示詞", + "generateImage": "生成圖片", + "batchDelete": "批次刪除", + "searchPlaceholder": "搜尋資產名稱...", + "search": "搜尋", + "preview": "預覽", + "generate": "生成", + "edit": "編輯", + "delete": "刪除", + "generating": "生成中", + "play": "播放", + "mediaPreview": "媒體預覽", + "confirmBatch": "是否確認{type}!", + "model": "模型", + "resolution": "解析度", + "resolutionPh": "請選擇解析度", + "batchGenPrompt": "批次生成提示詞", + "batchGenImage": "批次生成圖片", + "role": "角色", + "prop": "道具", + "scene": "場景", + "clip": "素材", + "uploadSuccess": "上傳成功", + "selectAtLeastOne": "請至少選擇一個資產", + "noDescription": "無描述", + "promptGenSuccess": "「{name}」提示詞生成成功", + "promptGenFail": "「{name}」提示詞生成失敗:{error}", + "selectModel": "請選擇模型", + "selectResolution": "請選擇解析度", + "noPromptForImage": "「{name}」沒有提示詞,無法生成圖片", + "imageGenSuccess": "「{name}」圖片生成成功", + "imageGenFail": "「{name}」圖片生成失敗:{error}", + "confirmDeleteHeader": "確認刪除", + "confirmBatchDeleteBody": "確定要批次刪除這些資產嗎?此操作無法復原。", + "confirmDeleteBody": "確定要刪除這個資產嗎?此操作無法復原。", + "deleteBtn": "刪除", + "cancelBtn": "取消", + "deleteSuccess": "資產刪除成功", + "deleteFail": "資產刪除失敗", + "colPreview": "預覽", + "colName": "名稱", + "colPrompt": "提示詞", + "colDescribe": "描述", + "colRemark": "備註", + "colCreateTime": "建立時間", + "colOperation": "操作", + "add": { + "name": "名稱", + "namePh": "請輸入名稱", + "describe": "描述", + "describePh": "請輸入描述", + "remark": "備註", + "remarkPh": "請輸入備註", + "prompt": "提示詞", + "promptPh": "請輸入提示詞", + "nameRequired": "請輸入名稱", + "describeRequired": "請輸入詳情", + "remarkRequired": "請輸入備註", + "updateSuccess": "資產更新成功", + "addSuccess": "資產新增成功" + }, + "gen": { + "header": "圖片生成", + "uploadRef": "上傳參考圖片", + "optional": "選填", + "promptLabel": "生圖提示詞", + "smartGenerate": "智能生成", + "generatingPrompt": "智能生成提示詞中...", + "promptPlaceholder": "描述您想要生成的圖片內容,例如:一個充滿科技感的未來城市,霓虹燈閃爍,賽博龐克風格...", + "selectModel": "選擇模型", + "selectResolution": "選擇解析度", + "generateBtn": "生成圖片", + "resultTitle": "生成結果", + "generatedCount": "已生成 {count} 張,請選擇一張", + "generatingLabel": "生成中...", + "genFailed": "生成失敗", + "confirmSelect": "確認選擇", + "promptSuccess": "提示詞生成成功", + "promptFail": "提示詞生成失敗", + "fillPrompt": "請填寫提示詞", + "pickResolution": "請選擇解析度", + "pickModel": "請選擇模型", + "unnamed": "未命名", + "assetGenSuccess": "資產生成成功", + "assetGenFail": "資產生成失敗", + "uploadOk": "上傳成功", + "imageSelected": "已選擇該圖片", + "imageDeleted": "已刪除該圖片", + "imageSaved": "圖片已儲存", + "completed": "已完成" + }, + "batch": { + "header": "批次生成", + "selected": "已選擇 {count} 項", + "selectAll": "全選", + "clearSelection": "清除選擇", + "inputPh": "請輸入內容", + "saveSelected": "儲存選取項目 ({count})", + "colPreviewImg": "預覽圖", + "selectToSave": "請選擇要儲存的項目", + "saveSuccess": "儲存成功", + "saveFail": "儲存失敗,請重試", + "promptDone": "提示詞生成完成", + "promptFail": "提示詞生成失敗", + "missingPrompts": "有 {count} 個資產缺少提示詞,請先生成提示詞", + "imageDone": "圖片生成完成", + "imageGenFail": "圖片生成失敗", + "unknownError": "未知錯誤", + "promptGenCancelled": "已取消生成" + }, + "confirmCancellation": "確定取消", + "confirmAgain": "確認取消嗎?\n取消之後後台AI會繼續調取扣費", + "sure": "確定" + }, + "production": { + "selectPlaceholder": "請選擇劇集", + "edit": "編輯", + "node": { + "script": { + "title": "劇本", + "editDialog": "編輯劇本" + }, + "scriptPlan": { + "title": "拍攝計畫", + "editDialog": "編輯拍攝計畫" + }, + "storyboard": { + "title": "分鏡面板", + "notGenerated": "未生成", + "scaleRatio": "縮放比例", + "gridPreview": "九宮格預覽", + "noPreviewImages": "暫無可預覽的圖片", + "loadFailed": "載入失敗: {src}", + "imageLoadFailed": "圖片載入失敗", + "promptPlaceholder": "請輸入提示詞", + "prompt": "提示詞", + "editInfo": "提示詞修改" + }, + "storyboardTable": { + "title": "分鏡表", + "editDialog": "編輯分鏡表" + }, + "assets": { + "title": "衍生資產", + "generateFailed": "生成失敗", + "notGenerated": "未生成", + "originalAsset": "原資產", + "derived": "衍生", + "noDerivedAssets": "無衍生資產" + }, + "poster": { + "title": "影片封面", + "coverCount": "{count} 張" + }, + "workbench": { + "title": "影片工作區" + } + }, + "editImage": { + "upload": "上傳", + "generate": "生成", + "saveFailed": "儲存失敗,請重試", + "fetchFailed": "取得資料失敗", + "generating": "生成中...", + "deleteNode": "刪除節點", + "ratio": "比例", + "quality": "畫質", + "generateBtn": "生成圖片", + "selectImage": "選擇圖片", + "imageGeneration": "圖片生成", + "promptPlaceholder": "描述你想要生成的圖片...", + "imageRef": "圖{index}", + "noReferences": "暫無可引用的參考圖", + "selectModel": "請先選擇模型", + "selectQuality": "請選擇畫質", + "selectRatio": "請選擇比例", + "generateFailed": "生成失敗", + "generateFirst": "請先生成圖片", + "generatedResult": "生成結果", + "waitingGenerate": "等待生成", + "uploadImage": "資產圖片上傳", + "mode": "模式", + "closeConfirmTitle": "關閉確認標題", + "closeConfirmBody": "關閉之後未儲存的資料會遺失" + }, + "save": "選取", + "cancel": "取消", + "chatBox": { + "inputPlaceholder": "輸入訊息...", + "generateDerivedAssets": "生成衍生資產", + "welcomeMessage": "你好!我是你的 AI 助手,有什麼可以幫你的嗎?", + "adjustModel": "調整模型", + "startMakingVideo": "開始製作影片", + "startMakingVideoPrompt": "請幫我開始製作影片", + "clearMessageMemory": "清除訊息記憶", + "clearSummaryMemory": "清除摘要記憶", + "clearAllMemory": "清除所有記憶", + "messageMemory": "訊息記憶", + "summaryMemory": "摘要記憶", + "allMemory": "所有記憶", + "confirmClear": "清除記憶", + "confirmClearBody": "確定要清除{type}嗎?", + "confirmClearBtn": "確定清除", + "memoryCleared": "{type}已清除" + }, + "wb": { + "quickPreview": "快速預覽", + "videoGeneration": "分鏡台", + "videoEditing": "剪輯台", + "hint": "提示", + "extractLines": "是否從影片中提取台詞?", + "no": "否", + "confirm": "確定", + "extractLinesQuestion": "是否從影片中提取台詞作為字幕?", + "importingLoading": "正在匯入中,請稍候...", + "mainTrackVideo": "主軌道(影片)", + "subtitle1": "字幕1", + "stereo441": "44.1kHz 立體聲", + "mono16": "16kHz 單聲道", + "sampleImage1": "範例圖片 1", + "storyboardVideoName": "{storyboard}-{id}.mp4" + }, + "preview": { + "noImage": "暫無圖片", + "storyboardDesc": "分鏡描述", + "serialNumber": "序號", + "noDescription": "暫無描述", + "duration": "時長", + "seconds": "秒", + "relatedAssets": "涉及資產", + "role": "角色", + "prop": "道具", + "scene": "場景", + "noCharacters": "暫無出場人物", + "imagePrompt": "圖片提示詞", + "selectAll": "全選", + "exportImage": "匯出圖片", + "sceneDescription": "畫面描述", + "promptLabel": "提示詞", + "restoreSort": "還原排序", + "restoreSortConfirm": "確定要還原為初始排序嗎?", + "tip": "提示", + "selectAtLeastOne": "請至少選擇一個分鏡進行匯出", + "exportFilename": "分鏡圖片" + }, + "generate": { + "noVideo": "暫無影片", + "videoPrompt": "影片提示詞", + "promptPlaceholder": "輸入提示詞,描述你想要生成的影片內容...", + "refImage": "參考圖", + "image": "圖片", + "refVideo": "參考影片", + "refImageLabel": "參考圖片", + "refAudio": "參考音訊", + "muteAudio": "關閉音訊", + "enableAudio": "開啟音訊", + "resolution": "解析度", + "duration": "時長", + "generate": "生成", + "historyVersions": "歷史版本", + "refresh": "重新整理", + "confirmSelection": "確認選取項目", + "noHistory": "暫無歷史記錄", + "generating": "生成中", + "generateFailed": "生成失敗", + "selectAll": "全選", + "videoTrack": "影片軌道", + "batchGenerate": "批次生成", + "importToEditor": "匯入剪輯台", + "modeSingleImage": "單圖", + "modeMultiImage": "多圖", + "modeGridImage": "網格多圖", + "modeStartEnd": "首尾影格", + "modeText": "文字生成影片", + "modeVideoRef": "影片參考", + "modeImageRef": "圖片參考", + "modeAudioRef": "音訊參考", + "modeTextRef": "文字參考", + "startFrame": "首影格", + "startFrameOptional": "首影格(選填)", + "endFrame": "尾影格", + "endFrameOptional": "尾影格(選填)", + "selectRefImage": "選擇參考圖", + "selectRefImages": "選擇參考圖片", + "selectEndFrame": "選擇尾影格圖", + "selectRefVideoAsset": "選擇參考影片", + "selectRefAudioAsset": "選擇參考音訊", + "selectRefImageAsset": "選擇參考圖片", + "selectImageSource": "選擇圖片來源", + "fromStoryboard": "分鏡圖", + "fromStoryboardDesc": "從分鏡列表中選擇圖片", + "fromAssets": "資產圖", + "fromAssetsDesc": "從資產庫中選擇圖片", + "confirmDelete": "確認刪除", + "confirmDeleteBody": "確定要刪除這個影片嗎?此操作無法復原。", + "delete": "刪除", + "cancel": "取消", + "deleteSuccess": "影片刪除成功", + "deleteFailed": "刪除失敗", + "selectVideoFirst": "請先選擇一個影片", + "confirmSuccess": "確認選取成功", + "batchSubmitted": "已送出批次生成請求,正在處理中...", + "configNotFound": "設定不存在", + "pollingFailed": "視訊狀態查詢失敗,請手動刷新", + "batchGeneratePrompt": "大量生成提示詞", + "batchPromptEmpty": "分鏡 {name} 有空視訊提示。 \n請先生成或填寫提示", + "modelEmpty": "請先選擇影片生成模型", + "generatingPrompt": "智慧生成提示詞中" + }, + "editVideo": { + "reset": "重設", + "undo": "復原", + "redo": "重做", + "split": "分割", + "delete": "刪除", + "rendering": "算圖渲染中...", + "exportVideo": "匯出影片", + "exportSuccess": "影片匯出完成", + "exportFailed": "匯出失敗", + "sampleSubtitle": "範例字幕文字", + "customText": "自訂文字內容", + "transitionBetweenClips": "轉場需要加入在兩個相鄰的片段之間", + "transitionExists": "該位置已存在轉場", + "videoPreviewArea": "影片預覽區域", + "clipMaterials": "剪輯素材", + "propertyPanel": "屬性面板", + "selectClip": "選擇一個片段查看屬性", + "basicInfo": "基本資訊", + "name": "名稱", + "clipNamePlaceholder": "片段名稱", + "startTime": "開始", + "endTime": "結束", + "totalDuration": "總時長", + "videoProperties": "影片屬性", + "opacity": "不透明度", + "volume": "音量", + "playbackSpeed": "播放速度", + "audioProperties": "音訊屬性", + "fadeIn": "淡入", + "fadeOut": "淡出", + "transitionProperties": "轉場屬性", + "transitionType": "轉場類型", + "transFade": "淡入淡出", + "transSlide": "滑動", + "transWipe": "擦除", + "transDissolve": "溶解", + "transZoom": "縮放", + "transRotate": "旋轉", + "transitionDuration": "轉場時長", + "subtitleProperties": "字幕屬性", + "textContent": "文字內容", + "fontSize": "字體大小", + "copy": "複製", + "deleteConfirm": "刪除確認", + "deleteClipConfirm": "確定要刪除這個片段嗎?", + "avCanvasNotInit": "AVCanvas 尚未初始化", + "noExportContent": "沒有可匯出的內容", + "exportProject": "匯出專案", + "transitionAdded": "已加入轉場: {name}", + "splitClip": "分割片段", + "deleteClip": "刪除片段", + "addClip": "新增 {name}", + "duplicateClip": "複製片段", + "addTransition": "加入轉場", + "updateClip": "更新片段 {key}", + "updatePlaybackRate": "更新播放速度為 {rate}x", + "updateTransitionDuration": "更新轉場時長", + "playbackRateRange": "播放速度必須在 0.1 到 10 之間", + "updatePlaybackRateFailed": "更新播放速度失敗:", + "importProject": "匯入專案", + "import": "匯入" + }, + "clipType": { + "video": "影片", + "audio": "音訊", + "subtitle": "字幕", + "transition": "轉場", + "sticker": "貼紙", + "filter": "濾鏡", + "effect": "特效" + }, + "track": { + "video": "影片", + "image": "圖片", + "audio": "音訊", + "subtitle": "字幕", + "text": "文字", + "sticker": "貼紙", + "filter": "濾鏡", + "effect": "特效" + }, + "transition": { + "fade": "淡入淡出", + "slide": "滑動", + "slideLeft": "向左滑動", + "slideRight": "向右滑動", + "slideUp": "向上滑動", + "slideDown": "向下滑動", + "wipe": "擦除", + "wipeLeft": "向左擦除", + "wipeRight": "向右擦除", + "wipeUp": "向上擦除", + "wipeDown": "向下擦除", + "dissolve": "溶解", + "zoom": "縮放", + "zoomIn": "放大", + "zoomOut": "縮小", + "rotate": "旋轉", + "circle": "圓形", + "diamond": "菱形", + "clock": "時鐘", + "blur": "模糊" + }, + "media": { + "titleText": "標題文字", + "subtitleText": "字幕文字", + "customText": "自訂文字", + "media": "媒體", + "image": "圖片", + "audio": "音訊", + "subtitle": "字幕", + "transition": "轉場", + "effect": "特效", + "filter": "濾鏡", + "loading": "載入中...", + "subtitlePreview": "字", + "video": "影片" + }, + "effect": { + "fadeIn": "淡入", + "fadeOut": "淡出", + "flash": "閃爍", + "shake": "抖動", + "zoomIn": "放大進入", + "zoomOut": "縮小退出", + "pulse": "脈衝", + "rotateIn": "旋轉進入", + "sticker1": "貼紙 1", + "sticker2": "貼紙 2" + }, + "filter": { + "grayscale": "黑白", + "sepia": "復古", + "warm": "暖色", + "cool": "冷色", + "vivid": "鮮豔", + "bright": "明亮", + "highContrast": "高對比", + "blur": "模糊", + "invert": "反色", + "semiTransparent": "半透明" + }, + "guideSwitchEpisode": "切換劇集", + "guideSwitchEpisodeBody": "切換劇集移到這裡了喔", + "autoLayoutLR": "自動排版-左右佈局", + "autoLayoutTB": "自動排版-上下佈局", + "getFlowData": "刷新工作區", + "confirm": "切換劇集確認", + "confirmEpisodesSwitch": "目前任務仍在進行中,切換劇集會重連會話,是否繼續切換?" + }, + "task": { + "title": "任務列表", + "subtitle": "您的最新任務執行紀錄", + "refresh": "重新整理", + "categoryLabel": "任務大類:", + "stateLabel": "狀態:", + "noFailReason": "暫無失敗原因", + "stateAll": "全部", + "stateRunning": "進行中", + "stateCompleted": "已完成", + "stateFailed": "生成失敗", + "fetchFailed": "取得任務列表失敗", + "col": { + "taskClass": "任務大類", + "relatedObjects": "關聯物件", + "model": "模型", + "describe": "描述", + "state": "狀態", + "startTime": "時間", + "reason": "失敗原因" + }, + "project": "項目名稱:" + }, + "noVideo": "暫無影片", + "prompt": "影片提示詞", + "generateText": "AI生成提示詞", + "selectStoryboard": "選擇分鏡", + "generate": { + "noVideo": "暫無數據", + "generateText": "AI生成提示詞", + "selectStoryboard": "選擇分鏡", + "generate": "產生影片", + "history": "歷史版本", + "generating": "生成中", + "generateFailed": "看失敗原因", + "selectAll": "全選", + "selected": "已選", + "batchGenerateText": "大量生成提示詞", + "batchGenerateVideo": "大量生成視頻", + "importVideo": "導入剪輯台", + "emptyTrack": "第{index}段", + "del": "刪除", + "delConfirm": "確認刪除該段?", + "selectSource": "選擇來源", + "confirm": "從資產選擇", + "cancel": "從分鏡選擇", + "selectVideoFailed": "視訊選擇失敗", + "selectVideoSuccess": "影片選擇成功", + "previewVideo": "影片預覽", + "selectTrackFirst": "請先選擇分鏡", + "noSelectedVideo": "沒有選擇的視頻", + "generateConfirm": "確認生成", + "generateConfirmBody": "是否確認生成視頻", + "generateVideosInBatches": "大量生成視頻", + "generateStarted": "生成開始", + "promptEmpty": "勾選需要產生影片的資料有提示詞為空,是否繼續生成", + "skipDataWithEmptyVideoPromptWords": "需要產生的影片資料存在提示詞為空", + "duration": "時長", + "resolution": "解析度", + "delVideo": "確認刪除此影片?", + "delSuccess": "刪除成功", + "addReference": "新增參考", + "promptPlaceholder": "請輸入影片提示詞", + "downloadVideo": "批量下載視頻", + "selectVideo": "請勾選需要下載的視頻", + "batchDownloadVideo": "批量下載視頻", + "storyboard": "分鏡", + "assets": "資產", + "promptText": "產生影片提示詞數據", + "videoMenu": "產生影片", + "videoPreview": "影片預覽", + "referenceImage": "參考圖片", + "generatePrompt": "產生提示詞", + "generateVideo": "產生影片" + } + }, + "login": { + "slogan": "智慧短劇創作平台", + "tips": "預設帳號:admin / admin123", + "settings": "伺服器設定", + "requestAddress": "請求網址", + "username": "使用者名稱", + "password": "密碼", + "login": "登入", + "usernameRequired": "請輸入使用者名稱", + "passwordRequired": "請輸入密碼", + "enterUsernameAndPassword": "請輸入使用者名稱或密碼", + "loginSuccess": "登入成功", + "settingsSaved": "設定已儲存" + }, + "promptManage": { + "prompt": "提示詞" + }, + "hello": { + "welcomeTitle": "歡迎使用 ToonFlow", + "welcomeDesc": "AI 驅動的漫畫創作工作流程平台,讓我們花一分鐘完成初始設定。", + "startConfig": "開始配置", + "skip": "跳過引導", + "configModel": "新增模型服務", + "configData": "配置 Agent", + "startUse": "開始使用", + "configModelTitle": "新增模型服務供應商", + "configModelDesc": "首先,你需要在設定中新增至少一個 AI 模型服務供應商(如 OpenAI、Claude 等),並填寫對應的 API Key。", + "configModelTip": "點擊下方按鈕將開啟設定頁面的「模型服務」選項卡,新增供應商後回到此處繼續。", + "configModelBtn": "前往配置模型服務", + "configAgentTitle": "分配 Agent 模型", + "configAgentDesc": "接下來,在 Agent 配置中為各個功能模組分配模型,這樣系統才知道要呼叫哪個模型來完成任務。", + "configAgentTip": "點擊下方按鈕將開啟設定頁面的「Agent 設定」標籤,為各功能指派模型後回到此處。", + "configAgentBtn": "前往配置 Agent", + "finishTitle": "🎉 一切就緒!", + "finishDesc": "配置完成,現在你可以開始使用所有功能了。\n如需調整,隨時可以在設定中修改。", + "qrcodeLabel": "加入微信交流群,獲得更多協助:", + "githubLabel": "如果覺得好用,請給我們一個 ⭐ Star 吧!", + "prevStep": "上一步", + "nextStep": "下一步", + "finish": "開始使用" + }, + "setting": { + "skillManagement": { + "search": "搜尋檔案名稱", + "empty": "沒有匹配的文件", + "edit": "編輯", + "selectOnTheLeft": "請從左側選擇文件" + } + }, + "common": { + "save": "儲存", + "submitting": "提交中", + "editSuccess": "修改成功", + "editFailed": "修改失敗", + "submit": "提交" + }, + "storyboard": { + "assets": { + "notExist": "資產不存在", + "notDerivativeExist": "衍生資產不存在", + "derivativeUpdateSuccess": "更新成功", + "derivativeState": "未生成", + "derivativeAddSuccess": "添加成功", + "derivativeDelSuccess": "刪除成功", + "notGenerated": "未生成" + }, + "addSuccess": "更新成功", + "state": { + "unused": "未生成" + }, + "saveSuccess": "添加成功" + }, + "productionAgent": { + "generating": "生成中" + }, + "skillScan": { + "scanning": "🔍 正在解析載入Skill", + "scanComplete": "✨ Skill 掃描完成", + "inserted": "✅ 新增{count}個Skill", + "updated": "🔄 更新{count}個Skill", + "removed": "🗑️ 移除{count}個Skill", + "scannedFiles": "📁 掃描 {count} 個檔案", + "noDescription": "📝 {count}個Skill缺少描述", + "noAttribution": "👤 {count}個Skill缺少歸屬", + "configWarning": "⚠️ Skill 配置警告", + "openSettings": "打開設定", + "scanFailed": "❌ 掃描失敗", + "checkNetwork": "🔌 請檢查網路連線或稍後重試", + "retryLater": "🔁 請稍後重試" + }, + "generate": "產生影片", + "history": "歷史版本", + "generating": "生成中", + "generateFailed": "失敗", + "selectAll": "全選", + "selected": "已選", + "importVideo": "導入影片", + "emptyTrack": "第{index 1}段", + "del": "確認刪除", + "delConfirm": "確認刪除該段?", + "selectSource": "選擇來源", + "confirm": "從資產選擇", + "cancel": "從分鏡選擇", + "workbench.script.msg.exportFailed": "匯出失敗", + "workbench.production.node.assets.confirmDeleteBody": "確認是否刪除該資產", + "workbench.production.node.assets.removeFailed": "刪除資產失敗", + "version": { + "newVersion": "有新版本,是否更新?" + }, + "workbench.production.generatedNode.localUpload": "本地上傳", + "workbench.production.editImage.uploadFailed": "圖片上傳失敗", + "workbench.production.editImage.noImage": "請先加入圖片", + "workbench.script.batchAddScript": "大量上傳劇本", + "workbench.script.import.pasteLabel": "直接貼上劇本內容", + "workbench.script.import.col.scriptName": "劇本名稱", + "workbench.script.import.col.scriptData": "劇本內容", + "workbench.script.import.episodeRegex": "劇本拆分正規", + "workbench.script.import.episodeRegexPh": "自訂劇本拆分正規,留空使用預設拆分正規(預設按 第X集 格式拆分)", + "workbench.script.import.regexInvalid": "正規表示式格式錯誤", + "workbench.script.import.parsedChapters": "已解析 {count} 集", + "workbench.script.import.msg.selectChapters": "請先勾選劇本", + "workbench.script.import.msg.saveSuccess": "劇本保存成功", + "workbench.script.import.batchTitle": "大量上傳劇本", + "workbench.assets.sex": "性別", + "workbench.assets.audioText": "音訊內容", + "workbench.assets.add.sex": "性別", + "workbench.assets.add.sexPh": "請輸入性別", + "settings.agent.advanced": "進階配置", + "settings.agent.ordinary": "簡易配置", + "settings.agent.temperature": "溫度", + "settings.agent.maxOutputTokens": "最大輸出Token", + "settings.agent.auto": "自動", + "settings.agent.manual": "手動", + "settings.agent.autoHint": "由模型自動決定輸出長度", + "settings.agent.msg.notmodel": "未選擇模型", + "workbench.production.node.storyboard.generateImage": "生成分鏡圖", + "workbench.generate.notSelectMode": "請先選擇模型", + "workbench.production.node.storyboard.deleteSuccess": "分鏡刪除成功", + "workbench.production.node.storyboard.pleaseSelectImage": "請先選擇分鏡", + "workbench.cornerScape.audioState": "綁定中", + "workbench.generate.generateError": "發起產生請求失敗", + "settings.vendor.videoGenerating": "影片產生較慢,請耐心等待", + "settings.memory.modelMap.editRefeshWord": "重新綁定", + "settings.memory.modelMap.delPrompt": "刪除", + "settings.vendor.testModel": "測試" +} diff --git a/web-core/src/main.ts b/web-core/src/main.ts new file mode 100644 index 0000000..65affb8 --- /dev/null +++ b/web-core/src/main.ts @@ -0,0 +1,33 @@ +import { createApp } from "vue"; +import { createPinia } from "pinia"; +import piniaPluginPersistedstate from "pinia-plugin-persistedstate"; +import App from "./App.vue"; +import router from "./router"; +import i18n from "./locales"; +import { install } from "@icon-park/vue-next/es/all"; +import "@icon-park/vue-next/styles/index.css"; + +import "tdesign-vue-next/es/style/index.css"; +import { LoadingDirective, LoadingPlugin } from "tdesign-vue-next"; + +import "@/utils/global"; + +import { Log } from "@webav/av-cliper"; +Log.setLogLevel(Log.warn); + +import "md-editor-v3/lib/style.css"; +import "splitpanes/dist/splitpanes.css"; + +import "./assets/main.scss"; + +import { imageOptimizer } from '@/utils/imageOptimizer' + +const app = createApp(App); +app.use(imageOptimizer) +install(app, "i"); +app.use(createPinia().use(piniaPluginPersistedstate)); +app.use(router); +app.use(i18n); +app.use(LoadingPlugin); +app.directive("loading", LoadingDirective); +app.mount("#app"); diff --git a/web-core/src/pages/error/404.vue b/web-core/src/pages/error/404.vue new file mode 100644 index 0000000..8eafd7f --- /dev/null +++ b/web-core/src/pages/error/404.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/web-core/src/pages/login/index.vue b/web-core/src/pages/login/index.vue new file mode 100644 index 0000000..a2bfa78 --- /dev/null +++ b/web-core/src/pages/login/index.vue @@ -0,0 +1,864 @@ + + + + + diff --git a/web-core/src/pages/workbench/index.vue b/web-core/src/pages/workbench/index.vue new file mode 100644 index 0000000..60d2622 --- /dev/null +++ b/web-core/src/pages/workbench/index.vue @@ -0,0 +1,377 @@ + + + + + diff --git a/web-core/src/router/index.ts b/web-core/src/router/index.ts new file mode 100644 index 0000000..61edc12 --- /dev/null +++ b/web-core/src/router/index.ts @@ -0,0 +1,81 @@ +import { createRouter, createWebHashHistory } from "vue-router"; +const router = createRouter({ + history: createWebHashHistory(), + routes: [ + { + path: "/:catchAll(.*)", + name: "404", + meta: { + title: "404", + }, + component: () => import("@/pages/error/404.vue"), + }, + { + path: "/", + redirect: "/workbench", + }, + { + path: "/workbench", + component: () => import("@/pages/workbench/index.vue"), + redirect: "/project", + children: [ + { + path: "/project", + component: () => import("@/views/project/index.vue"), + }, + { + path: "/task", + component: () => import("@/views/task/index.vue"), + }, + // { + // path: "/detail", + // component: () => import("@/views/detail/index.vue"), + // }, + { + path: "/novel", + component: () => import("@/views/novel/index.vue"), + }, + { + path: "/script", + component: () => import("@/views/script/index.vue"), + }, + { + path: "/scriptAgent", + component: () => import("@/views/scriptAgent/index.vue"), + }, + { + path: "/cornerScape", + component: () => import("@/views/cornerScape/index.vue"), + }, + { + path: "/production", + component: () => import("@/views/production/index.vue"), + }, + { + path: "/assets", + component: () => import("@/views/assets/index.vue"), + }, + { + path: "/test", + component: () => import("@/views/test/index.vue"), + }, + ], + }, + { + path: "/login", + component: () => import("@/pages/login/index.vue"), + }, + ], +}); +router.beforeEach((to, from, next) => { + if (to.path === "/login") { + next(); + } else { + if (localStorage.getItem("token")) { + next(); + } else { + next("/login"); + } + } +}); +export default router; diff --git a/web-core/src/stores/imageListCache.ts b/web-core/src/stores/imageListCache.ts new file mode 100644 index 0000000..99ba996 --- /dev/null +++ b/web-core/src/stores/imageListCache.ts @@ -0,0 +1,311 @@ +import "@/views/production/components/workbench/type/type"; +import axios from "@/utils/axios"; + +/** + * 图片列表缓存 Pinia Store + * 结构: { [projectId]: { [scriptId]: { [trackId]: CachedUploadItem[] } } } + * + * 缓存中的 src 只存储文件路径(不含 origin/host:port), + * 显示时通过后端接口批量获取当前可用的完整 URL, + * 解决后端端口变化导致缓存失效的问题。 + */ + +type CacheKey = string | number; + +/** 缓存中存储的项,src 为纯文件路径(不含 origin) */ +type CachedUploadItem = Omit & { src?: string }; + +type ImageListCacheData = Record>>; + +/** 用于向后端请求 URL 的标识信息 */ +interface ResolveUrlItem { + id: number | null | undefined; + sources: string | undefined; +} + +/** 生成 urlMap 的复合键: "id:sources" */ +function makeUrlKey(id: number | null | undefined, sources: string | undefined): string { + return `${id ?? ""}:${sources ?? ""}`; +} + +/** 从完整 URL 中提取路径部分(去掉 origin) */ +function extractPath(url: string | undefined): string { + if (!url) return ""; + // data: / blob: 等特殊协议直接保留 + if (url.startsWith("data:") || url.startsWith("blob:")) return url; + try { + const u = new URL(url); + return u.pathname + u.search + u.hash; + } catch { + // 如果已经是路径或者解析失败,直接返回 + return url; + } +} + +/** 将 UploadItem[] 转为缓存格式(src 只保留路径) */ +function toCachedItems(items: (UploadItem | TrackMedia)[]): CachedUploadItem[] { + return items.map((item) => ({ + ...JSON.parse(JSON.stringify(item)), + src: extractPath(item.src), + })); +} + +export default defineStore( + "imageListCache", + () => { + const cacheData = ref({}); + + /** URL 解析缓存: "id:sources" -> 后端返回的完整 URL,避免重复请求 */ + const urlMap = ref>({}); + + /** + * 批量通过 id + sources 获取当前可用的完整 URL + * + * 请求示例: + * POST /production/workbench/getFileUrl + * Body: { items: [{ id: 1, sources: "storyboard" }, { id: 2, sources: "assets" }] } + * + * 期望响应: + * { data: [{ id: 1, sources: "storyboard", url: "http://..." }, ...] } + * 或 { data: { "1:storyboard": "http://...", ... } } + */ + async function resolveUrls(items: ResolveUrlItem[]): Promise> { + if (!items.length) return {}; + // 过滤掉已经解析过的、id 无效的项 + const needResolve = items.filter((item) => { + if (item.id == null) return false; + const key = makeUrlKey(item.id, item.sources); + return true; + }); + if (needResolve.length) { + try { + const { data } = await axios.post("/production/workbench/getFileUrl", { + items: needResolve.map((item) => ({ id: item.id, sources: item.sources })), + }); + // axios 拦截器已返回 response.data,后端可能再包一层 { data: { ... } } + const rawData = data.data; + + // 兼容多种后端响应格式 + const resolved: Record = {}; + if (Array.isArray(rawData)) { + // 格式: [{ id: 1, sources: "storyboard", url: "http://..." }, ...] + rawData.forEach((item: any) => { + if (item.id != null && item.url) { + const key = makeUrlKey(item.id, item.sources); + resolved[key] = item.url; + } + }); + } else if (rawData && typeof rawData === "object" && !Array.isArray(rawData)) { + // 格式: { "id:sources": fullUrl } 或 { [compositeKey]: fullUrl } + Object.entries(rawData).forEach(([key, url]) => { + resolved[key] = url as string; + }); + } + + // 替换整个对象以触发 Vue 响应式更新 + urlMap.value = { ...urlMap.value, ...resolved }; + } catch (e) { + console.warn("[imageListCache] resolveUrls 请求失败,降级使用路径", e); + } + } + // 返回所有请求项的映射(含之前缓存的) + const result: Record = {}; + items.forEach((item) => { + const key = makeUrlKey(item.id, item.sources); + result[key] = urlMap.value[key] || item.id?.toString() || ""; + }); + return result; + } + + /** 通过 id + sources 同步解析为完整 URL(优先走内存缓存) */ + function resolveUrlSync(id: number | null | undefined, sources: string | undefined, fallbackPath?: string): string { + if (id != null) { + const key = makeUrlKey(id, sources); + if (urlMap.value[key]) return urlMap.value[key]; + } + // 降级返回原始路径 + return fallbackPath || ""; + } + + /** 将缓存项还原为带完整 URL 的 UploadItem[](同步版,需先调用 resolveUrls) */ + function toFullItems(items: CachedUploadItem[]): UploadItem[] { + return items.map((item) => ({ + ...item, + src: resolveUrlSync(item.id, (item as any).sources, item.src), + })) as UploadItem[]; + } + + /** + * 获取指定 project / script / track 的缓存图片列表 + * 返回的 src 为已解析的完整 URL(需先调用 resolveUrls 预热) + */ + function getCache(projectId: CacheKey, scriptId: CacheKey, trackId: CacheKey): UploadItem[] | undefined { + const cached = cacheData.value[projectId]?.[scriptId]?.[trackId]; + if (!cached) return undefined; + return toFullItems(cached); + } + + /** + * 获取缓存并异步解析 URL(推荐使用) + * 自动向后端请求路径对应的完整 URL,返回解析后的列表 + */ + async function getCacheWithResolve(projectId: CacheKey, scriptId: CacheKey, trackId: CacheKey): Promise { + const cached = cacheData.value[projectId]?.[scriptId]?.[trackId]; + if (!cached) return undefined; + // 收集所有需要解析的 id + sources + const resolveItems: ResolveUrlItem[] = cached + .filter((item) => item.id != null) + .map((item) => ({ id: item.id, sources: (item as any).sources })); + await resolveUrls(resolveItems); + return toFullItems(cached); + } + + /** + * 获取原始缓存数据(src 为路径,不解析 URL) + */ + function getRawCache(projectId: CacheKey, scriptId: CacheKey, trackId: CacheKey): CachedUploadItem[] | undefined { + return cacheData.value[projectId]?.[scriptId]?.[trackId]; + } + + /** + * 设置缓存(自动提取 src 路径部分) + * 同时将完整 URL 写入 urlMap,避免新增图片后裂图 + */ + function setCache(projectId: CacheKey, scriptId: CacheKey, trackId: CacheKey, imageList: (UploadItem | TrackMedia)[]): void { + if (!cacheData.value[projectId]) { + cacheData.value[projectId] = {}; + } + if (!cacheData.value[projectId][scriptId]) { + cacheData.value[projectId][scriptId] = {}; + } + // 将带完整 URL 的 src 通过 id:sources 写入 urlMap + let urlMapDirty = false; + imageList.forEach((item) => { + if (!item.src || item.id == null) return; + const key = makeUrlKey(item.id, (item as any).sources); + if (!urlMap.value[key]) { + urlMap.value[key] = item.src; + urlMapDirty = true; + } + }); + if (urlMapDirty) { + urlMap.value = { ...urlMap.value }; + } + cacheData.value[projectId][scriptId][trackId] = toCachedItems(imageList); + } + + /** + * 删除指定轨道的缓存 + */ + function removeCache(projectId: CacheKey, scriptId: CacheKey, trackId: CacheKey): void { + if (cacheData.value[projectId]?.[scriptId]) { + delete cacheData.value[projectId][scriptId][trackId]; + } + } + + /** + * 在指定 project / script 下所有轨道中,删除匹配指定图片 id 的图片项 + * @param projectId 项目 ID + * @param scriptId 脚本 ID + * @param imageId 要删除的图片 id + */ + function removeImageById(projectId: CacheKey, scriptId: CacheKey, imageId: number): void { + const scriptCache = cacheData.value[projectId]?.[scriptId]; + if (!scriptCache) return; + Object.keys(scriptCache).forEach((trackId) => { + scriptCache[trackId] = scriptCache[trackId].filter((item) => item.id !== imageId); + }); + } + + /** + * 清空指定 project / script 下的所有轨道缓存 + */ + function clearScriptCache(projectId: CacheKey, scriptId: CacheKey): void { + if (cacheData.value[projectId]) { + delete cacheData.value[projectId][scriptId]; + } + } + function clearProjectCache(projectId: CacheKey): void { + if (cacheData.value && cacheData.value?.[projectId]) { + delete cacheData.value[projectId]; + } + } + /** + * 从后端返回的 trackList 批量初始化缓存 + * 只有当对应轨道没有缓存时才写入(保留用户本地编辑) + */ + function initCacheFromTrackList(projectId: CacheKey, scriptId: CacheKey, trackList: TrackItem[]): void { + trackList.forEach((track) => { + if (track.id == null) return; + if (cacheData.value[projectId]?.[scriptId]?.[track.id]) return; + if (!cacheData.value[projectId]) cacheData.value[projectId] = {}; + if (!cacheData.value[projectId][scriptId]) cacheData.value[projectId][scriptId] = {}; + cacheData.value[projectId][scriptId][track.id] = toCachedItems(track.medias); + }); + } + + /** + * 从后端返回的 trackList 强制覆盖缓存 + */ + function forceInitCacheFromTrackList(projectId: CacheKey, scriptId: CacheKey, trackList: TrackItem[]): void { + if (cacheData.value[projectId]) { + delete cacheData.value[projectId][scriptId]; + } + if (!cacheData.value[projectId]) cacheData.value[projectId] = {}; + cacheData.value[projectId][scriptId] = {}; + trackList.forEach((track) => { + if (track.id == null) return; + cacheData.value[projectId][scriptId][track.id] = toCachedItems(track.medias); + }); + } + + /** + * 批量预热:收集指定 script 下所有轨道的文件路径,一次性向后端请求 URL + * 建议在 getGenerateData 拿到数据后调用一次 + */ + async function warmUpUrls(projectId: CacheKey, scriptId: CacheKey): Promise { + const scriptCache = cacheData.value[projectId]?.[scriptId]; + if (!scriptCache) return; + const allItems: ResolveUrlItem[] = []; + const seen = new Set(); + Object.values(scriptCache).forEach((items) => { + items.forEach((item) => { + if (item.id == null) return; + const key = makeUrlKey(item.id, (item as any).sources); + if (!seen.has(key)) { + seen.add(key); + allItems.push({ id: item.id, sources: (item as any).sources }); + } + }); + }); + await resolveUrls(allItems); + } + + /** + * 清除 URL 解析缓存(当后端地址/端口变更时调用) + */ + function clearUrlMap(): void { + urlMap.value = {}; + } + + return { + cacheData, + urlMap, + getCache, + getCacheWithResolve, + getRawCache, + setCache, + removeCache, + removeImageById, + clearScriptCache, + initCacheFromTrackList, + forceInitCacheFromTrackList, + resolveUrls, + resolveUrlSync, + warmUpUrls, + clearUrlMap, + clearProjectCache, + }; + }, + { persist: { pick: ["cacheData"] } }, +); diff --git a/web-core/src/stores/index.ts b/web-core/src/stores/index.ts new file mode 100644 index 0000000..370a000 --- /dev/null +++ b/web-core/src/stores/index.ts @@ -0,0 +1,31 @@ +import axios from "@/utils/axios"; + +export default defineStore( + "index", + () => { + const version = ref('v1.0.7') + + const activeMenu = ref(""); + + //当前项目 + const project = ref(null); + + //获取前项目ID + const projectId = computed(() => { + return project.value ? Number(project.value.id)! : -1; + }); + + const currentScriptId = ref(null); + + //设置当前项目 + async function setProjectById(id: number) { + const res = await axios.post("/project/getSingleProject", { id: id }); + project.value = res.data[0]; + const scriptData = await axios.post("/script/getScrptApi", { projectId: id }); + currentScriptId.value = scriptData.data?.id || null; + } + + return { version, activeMenu, project, projectId, currentScriptId, setProjectById }; + }, + { persist: false }, +); diff --git a/web-core/src/stores/loadingStore.ts b/web-core/src/stores/loadingStore.ts new file mode 100644 index 0000000..ecde8b9 --- /dev/null +++ b/web-core/src/stores/loadingStore.ts @@ -0,0 +1,47 @@ +import { ref } from "vue"; +import { defineStore } from "pinia"; + +function createDefaultMap(defaultValue: boolean): Record { + const inner: Record = {} as Record; + return new Proxy(inner, { + get(target, prop: string | number | symbol) { + if (prop in target) { + return target[prop as keyof typeof target]; + } + return defaultValue; + }, + set(target, prop: string | number | symbol, value: boolean) { + (target as any)[prop] = value; + return true; + }, + }) as Record; +} + +export default defineStore( + "loadingStore", + () => { + const videoGenerateloading = ref>(createDefaultMap(false)); + const assetGenerateloading = ref>(createDefaultMap(false)); + const assetGeneratePromptloading = ref>(createDefaultMap(false)); + const scriptGenerateLoading = ref>(createDefaultMap(false)); + const storeboardGenerateLoading = ref>(createDefaultMap(false)); + + const batchGenerateImageLoading = ref>(createDefaultMap(false)); + const batchGenerateStoryboardImageLoading = ref>(createDefaultMap(false)); + + const batchGeneratePromptLoading = ref>(createDefaultMap(false)); + const batchGenerateStoryboardPromptLoading = ref>(createDefaultMap(false)); + return { + videoGenerateloading, + assetGenerateloading, + assetGeneratePromptloading, + scriptGenerateLoading, + storeboardGenerateLoading, + batchGenerateImageLoading, + batchGenerateStoryboardImageLoading, + batchGeneratePromptLoading, + batchGenerateStoryboardPromptLoading, + }; + }, + { persist: false }, +); diff --git a/web-core/src/stores/productionAgent.ts b/web-core/src/stores/productionAgent.ts new file mode 100644 index 0000000..d20dd39 --- /dev/null +++ b/web-core/src/stores/productionAgent.ts @@ -0,0 +1,511 @@ +import axios from "@/utils/axios"; +import projectStore from "@/stores/project"; +import settingStore from "@/stores/setting"; +import { useChat } from "@/utils/useChat"; +import type { FlowData, Storyboard } from "@/views/production/utils/flowBuilder"; +import type { ChatMessagesData } from "@tdesign-vue-next/chat"; +import { useThrottleFn } from "@vueuse/core"; + +function makeProductionAgentStore(projectId: string) { + return defineStore(`productionAgent-${projectId}`, () => { + const defMsg: ChatMessagesData[] = [ + { + id: "welcome", + role: "assistant", + content: [ + { type: "text", status: "complete", data: $t("workbench.production.chatBox.welcomeMessage") }, + { + type: "suggestion", + status: "complete", + data: [{ title: $t("workbench.production.chatBox.startMakingVideo"), prompt: $t("workbench.production.chatBox.startMakingVideoPrompt") }], + }, + ], + }, + ]; + onMounted(() => { + if (messages.value.length <= 0) messages.value = [...defMsg, ...messages.value]; + }); + + const flowData = ref({ + script: "", // 剧本 + scriptPlan: "", //导演计划 + storyboardTable: "", //分镜表 + assets: [], // 衍生资产 + storyboard: [], //分镜面板 + workbench: { + videoList: [], + }, // 工作台数据 + }); + + const episodesId = ref(); + + const { connected, messages, chat, stopGenerate, socket, status, reconnect, connect, disconnect } = useChat({ + url: `${settingStore().baseUrl}/socket/productionAgent`, + auth: () => ({ + isolationKey: `${projectId}:productionAgent:${episodesId.value}`, + projectId: projectId, + scriptId: episodesId.value, + }), + manageLifecycle: false, + autoConnect: false, + xmlTags: [ + { tag: "script", keepInMessage: false }, + { tag: "scriptPlan", keepInMessage: false }, + { tag: "storyboardTable", keepInMessage: false }, + { tag: "storyboardItem", keepInMessage: false }, + ], + onXmlTag: async (data) => { + const { tag, value, children, attrs, status } = data; + if (tag === "script") { + flowData.value.script = value ?? ""; + } else if (tag === "scriptPlan") { + flowData.value.scriptPlan = value ?? ""; + } else if (tag === "storyboardTable") { + flowData.value.storyboardTable = value ?? ""; + } else if (tag === "storyboardItem") { + if (status === "complete") { + const prompt = attrs.prompt ?? ""; + const duration = Number(attrs.duration) || 0; + const track = attrs.track || ""; + const shouldGenerateImage = + (typeof attrs.shouldGenerateImage == "boolean" && attrs.shouldGenerateImage) || + String(attrs.shouldGenerateImage).toLowerCase() == "true" + ? 1 + : 0; + + const videoDesc = attrs?.videoDesc ?? ""; + const existingIndex = flowData.value.storyboard.findIndex( + (s) => s.prompt == prompt && s.duration == duration && videoDesc == s.videoDesc, + ); + if (existingIndex !== -1) { + // 已存在则更新 content,保留 id + flowData.value.storyboard[existingIndex].prompt = prompt; + } else { + // 不存在则追加新条目 + flowData.value.storyboard.push({ + prompt: prompt || "", + duration: Number(duration) || 0, + state: "未生成" as "未生成" | "生成中" | "已完成" | "生成失败", + src: null, + associateAssetsIds: JSON.parse(attrs.associateAssetsIds) || [], + videoDesc: videoDesc, + shouldGenerateImage: shouldGenerateImage, + }); + await addStoryboardInfo([ + { + prompt: prompt || "", + duration: Number(duration) || 0, + track: track || "", + state: "未生成" as "未生成" | "生成中" | "已完成" | "生成失败", + src: null, + videoDesc, + shouldGenerateImage, + associateAssetsIds: JSON.parse(attrs.associateAssetsIds) || [], + }, + ]); + } + } + } + if (status == "complete") { + throttledFn(); + } + }, + }); + + // 实际的节流方法 + const throttledFn = useThrottleFn( + () => { + setFlowData(episodesId.value); + }, + 500, + true, + true, + ); + // 注册 getPlanData 事件(无需依赖组件生命周期) + watch( + socket, + (s) => { + if (s) { + s.on("connect", () => { + getHistory(); + }); + s.on("getFlowData", (_, callback) => { + const returnData = JSON.parse(JSON.stringify(flowData.value)); + returnData.assets.forEach((item: any) => { + delete item.prompt; + delete item.flowId; + delete item.src; + if (item.derive && item.derive.length) { + item.derive.forEach((deriveItem: any) => { + delete deriveItem.prompt; + delete deriveItem.flowId; + delete deriveItem.src; + }); + } + }); + returnData.storyboard.forEach((item: any) => { + delete item.prompt; + delete item.src; + delete item.flowId; + }); + callback(returnData); + }); + s.on("addDeriveAsset", async (data, callback) => { + const assets = flowData.value.assets.find((a) => a.id === data.assetsId); + if (!assets) return callback({ success: false, message: $t("storyboard.assets.notExist") }); + const deriveAssetList = assets.derive || []; + const item = deriveAssetList.find((d) => d.id === data.id); + if (item) { + if (!item) return callback({ success: false, message: $t("storyboard.assets.notDerivativeExist") }); + item.name = data.name; + item.type = assets.type; + callback({ success: true, message: $t("storyboard.assets.derivativeUpdateSuccess") }); + } else { + deriveAssetList.push({ + assetsId: data.assetsId, + id: data.id, + name: data.name, + type: assets.type, + desc: data.describe, + prompt: "", + state: "未生成" as "未生成" | "生成中" | "已完成" | "生成失败", + src: "", + }); + callback({ success: true, message: $t("storyboard.assets.derivativeAddSuccess") }); + } + }); + s.on("delDeriveAsset", async (data, callback) => { + const assets = flowData.value.assets.find((a) => a.id === data.assetsId); + if (!assets) return callback({ success: false, message: $t("storyboard.assets.notExist") }); + const deriveAssetList = assets.derive || []; + const index = deriveAssetList.findIndex((d) => d.id === data.id); + if (index === -1) return callback({ success: false, message: $t("storyboard.assets.notDerivativeExist") }); + deriveAssetList.splice(index, 1); + callback({ success: true, message: $t("storyboard.assets.derivativeDelSuccess") }); + }); + s.on("generateDeriveAsset", async (data, callback) => { + const assetsData = await batchGenerateAssets(data.ids); + callback({ success: true, message: assetsData }); + }); + s.on("generateStoryboard", async (data, callback) => { + const storyData = await batchGenerateStoryboard(data.ids); + callback({ success: true, message: storyData }); + }); + } + }, + { immediate: true }, + ); + + async function setFlowData(scriptId?: number) { + await axios.post("/production/saveFlowData", { + projectId: projectId, + data: flowData.value, + episodesId: scriptId || episodesId.value, + }); + } + + async function getFlowData() { + const { data } = await axios.post("/production/getFlowData", { + projectId: projectId, + episodesId: episodesId.value, + }); + flowData.value = data; + } + async function batchGenerateStoryboard(allIds: number[], compulsory: boolean = false) { + try { + const { data } = await axios.post("/production/storyboard/batchGenerateImage", { + scriptId: episodesId.value, + projectId: projectId, + storyboardIds: allIds, + concurrentCount: settingStore().otherSetting.assetsBatchGenereateSize, + compulsory, + }); + if (data) { + if (flowData.value.storyboard.length === 0) { + flowData.value.storyboard = data; + return data; + } else { + flowData.value.storyboard.forEach((item) => { + const findData = data.find((i: any) => i.id == item.id); + if (findData) { + item.state = findData.state; + item.src = findData.src; + } + }); + } + } + return data; + } catch (e) { + window.$message.error((e as any)?.message); + } + } + async function batchGenerateAssets(allIds: number[]) { + flowData.value.assets.forEach((asset) => { + if (asset.derive) { + asset.derive.forEach((derive) => { + if (allIds.includes(derive.id)) { + derive.state = "生成中" as "未生成" | "生成中" | "已完成" | "生成失败"; + } + }); + } + }); + try { + const { data } = await axios.post("/production/assets/batchGenerateAssetsImage", { + assetIds: allIds, + projectId: projectId, + scriptId: episodesId.value, + concurrentCount: settingStore().otherSetting.assetsBatchGenereateSize, + }); + if (data) { + data.forEach((record: { id: number; state: "未生成" | "生成中" | "已完成" | "生成失败"; src: string }) => { + flowData.value.assets.forEach((asset) => { + if (asset.derive) { + asset.derive.forEach((derive) => { + if (derive.id === record.id) { + derive.state = record.state; + derive.src = record.src; + } + }); + } + }); + }); + } + return data; + } catch (e) {} + } + const assetsNotStateImageIds = computed(() => { + const ids: number[] = []; + flowData.value.assets.forEach((asset) => { + if (asset.derive) { + asset.derive.forEach((derive) => { + if (derive.state == ("生成中" as "未生成" | "生成中" | "已完成" | "生成失败")) { + ids.push(derive.id); + } + }); + } + }); + return ids; + }); + const storyboardNotStateImageIds = computed(() => { + const ids: number[] = []; + flowData.value.storyboard.forEach((asset) => { + if (asset.state == "生成中" && asset.id) { + ids.push(asset.id); + } + }); + return ids; + }); + // ---- 资产图片轮询 ---- + let assetsPollingTimer: number | null = null; + let assetsPollingInFlight = false; + + async function pollAssetsImages() { + const ids = assetsNotStateImageIds.value; + if (ids.length === 0 || assetsPollingInFlight) return; + assetsPollingInFlight = true; + try { + const { data } = await axios.post("/production/assets/pollingImage", { + ids: ids, + }); + if (!data || data.length === 0) return; + const records = data as Array<{ id: number; state: string; src?: string; errorReason?: string; prompt?: string }>; + records.forEach((record) => { + flowData.value.assets.forEach((asset) => { + if (!asset.derive) return; + asset.derive.forEach((derive) => { + if (derive.id === record.id) { + derive.state = record.state as "未生成" | "生成中" | "已完成" | "生成失败"; + if (record.src) derive.src = record.src; + derive.errorReason = record?.errorReason ?? ""; + derive.prompt = record?.prompt ?? ""; + } + }); + }); + }); + } catch (e) { + console.error("[assetsPolling] error", e); + } finally { + assetsPollingInFlight = false; + } + } + + function startAssetsPolling() { + if (assetsPollingTimer) return; + assetsPollingTimer = window.setInterval(async () => { + if (assetsNotStateImageIds.value.length === 0) { + stopAssetsPolling(); + return; + } + await pollAssetsImages(); + }, 5000); + // 立即执行一次 + pollAssetsImages(); + } + + function stopAssetsPolling() { + if (assetsPollingTimer) { + clearInterval(assetsPollingTimer); + assetsPollingTimer = null; + } + } + + watch( + () => assetsNotStateImageIds.value, + (ids) => { + if (ids.length > 0) { + startAssetsPolling(); + } else { + stopAssetsPolling(); + } + }, + ); + + // ---- 分镜图片轮询 ---- + let storyboardPollingTimer: number | null = null; + let storyboardPollingInFlight = false; + + async function pollStoryboardImages() { + const ids = storyboardNotStateImageIds.value; + if (ids.length === 0 || storyboardPollingInFlight) return; + storyboardPollingInFlight = true; + try { + const { data } = await axios.post("/production/storyboard/pollingImage", { + ids: ids, + }); + if (!data || data.length === 0) return; + const records = data as Array<{ id: number; state: string; src?: string; reason?: string }>; + records.forEach((record) => { + const item = flowData.value.storyboard.find((s) => s.id === record.id); + if (item) { + item.state = record.state as "未生成" | "生成中" | "已完成" | "生成失败"; + if (record.src) item.src = record.src; + item.reason = record?.reason ?? ""; + } + }); + } catch (e) { + console.error("[storyboardPolling] error", e); + } finally { + storyboardPollingInFlight = false; + } + } + + function startStoryboardPolling() { + if (storyboardPollingTimer) return; + storyboardPollingTimer = window.setInterval(async () => { + if (storyboardNotStateImageIds.value.length === 0) { + stopStoryboardPolling(); + return; + } + await pollStoryboardImages(); + }, 5000); + // 立即执行一次 + pollStoryboardImages(); + } + + function stopStoryboardPolling() { + if (storyboardPollingTimer) { + clearInterval(storyboardPollingTimer); + storyboardPollingTimer = null; + } + } + + watch( + () => storyboardNotStateImageIds.value, + (ids) => { + if (ids.length > 0) { + startStoryboardPolling(); + } else { + stopStoryboardPolling(); + } + }, + ); + + function updateContext() { + if (episodesId.value! < 0) return; + const ctx = { + isolationKey: `${projectId}:productionAgent:${episodesId.value}`, + projectId: projectId, + scriptId: episodesId.value, + }; + if (!connected.value) connect(); + socket.value!.emit("updateContext", ctx); + } + async function addStoryboardInfo(items: any[]) { + const { data } = await axios.post("/production/storyboard/batchAddStoryboardInfo", { + scriptId: episodesId.value, + data: items, + projectId: projectId, + }); + + flowData.value.storyboard.forEach((item) => { + const updated = data.find((d: Storyboard) => d.prompt == item.prompt && d.duration == item.duration && d.videoDesc == item.videoDesc); + if (updated) { + item.id = updated.id; + item.trackId = updated.trackId; + item.src = updated.src; + item.state = updated.state; + item.associateAssetsIds = updated.associateAssetsIds; + } + }); + } + + const loadingHistory = ref(false); + async function getHistory() { + loadingHistory.value = true; + const { data } = await axios.post(`/agents/getMemory`, { + projectId: projectId, + episodesId: episodesId.value, + agentType: "productionAgent", + }); + messages.value = []; + messages.value = [...defMsg, ...data]; + loadingHistory.value = false; + } + + const thinkLevel = ref(0); + + function updateThinkConfig(value: number) { + thinkLevel.value = value; + if (socket.value) { + socket.value.emit("updateThinkConfig", { think: value > 0, thinlLevel: value }); + } + } + + return { + connected, + messages, + chat, + stopGenerate, + socket, + status, + flowData, + setFlowData, + getFlowData, + episodesId, + stopAssetsPolling, + stopStoryboardPolling, + updateContext, + getHistory, + loadingHistory, + batchGenerateStoryboard, + reconnect, + thinkLevel, + updateThinkConfig, + }; + }); +} + +const storeMap = new Map>(); + +function createProductionAgentStore(projectId: string) { + if (!storeMap.has(projectId)) { + storeMap.set(projectId, makeProductionAgentStore(projectId)); + } + return storeMap.get(projectId)!; +} + +export default function useProductionAgentStore() { + const id = projectStore().project?.id; + if (!id) throw new Error("No project selected"); + return createProductionAgentStore(id)(); +} diff --git a/web-core/src/stores/project.ts b/web-core/src/stores/project.ts new file mode 100644 index 0000000..a964013 --- /dev/null +++ b/web-core/src/stores/project.ts @@ -0,0 +1,28 @@ +interface Project { + id: string; + name: string; + intro: string; + type: string; + artStyle: string | null; + videoRatio: string | null; + createTime: number; + updatedAt: number; + imageModel: string; + videoModel: string; + projectType: string; + imageQuality: "1K" | "2K" | "4K" | ""; + mode: string; + directorManual: string; +} + +export default defineStore( + "project", + () => { + const allProject = ref([]); + + const project = ref(null); + + return { allProject, project }; + }, + { persist: true }, +); diff --git a/web-core/src/stores/scriptAgent.ts b/web-core/src/stores/scriptAgent.ts new file mode 100644 index 0000000..0d7f8f0 --- /dev/null +++ b/web-core/src/stores/scriptAgent.ts @@ -0,0 +1,99 @@ +import axios from "@/utils/axios"; +import projectStore from "@/stores/project"; +import settingStore from "@/stores/setting"; +import { useChat } from "@/utils/useChat"; + +interface PlanData { + storySkeleton: string; + adaptationStrategy: string; + script: { id?: number; name: string; content: string }[]; +} + +function makeScriptAgentStore(projectId: string) { + return defineStore(`scriptAgent-${projectId}`, () => { + const planData = ref({ + storySkeleton: "", + adaptationStrategy: "", + script: [], + }); + + const { connected, messages, chat, stopGenerate, socket, status, disconnect, connect } = useChat({ + url: `${settingStore().baseUrl}/socket/scriptAgent`, + auth: () => ({ + isolationKey: `${projectId}:scriptAgent`, + projectId: projectId, + }), + manageLifecycle: false, + xmlTags: [ + { tag: "storySkeleton", keepInMessage: false }, + { tag: "adaptationStrategy", keepInMessage: false }, + { tag: "scriptItem", keepInMessage: false }, + ], + onXmlTag: (data) => { + const { tag, value, children, status, attrs } = data; + if (tag === "storySkeleton") { + planData.value.storySkeleton = value; + } else if (tag === "adaptationStrategy") { + planData.value.adaptationStrategy = value; + } else if (tag === "scriptItem") { + const name = attrs.name ?? ""; + const content = value; + if (name) { + const existingIndex = planData.value.script.findIndex((s) => s.name === name); + if (existingIndex !== -1) { + planData.value.script[existingIndex].content = content; + } else { + planData.value.script.push({ name, content }); + } + } + } + if (status === "complete") { + setPlanData(); + } + }, + autoConnect: false, + }); + + watch( + socket, + (s) => { + if (s) { + s.on("getPlanData", (_, callback) => { + callback(planData.value); + }); + } + }, + { immediate: true }, + ); + + async function setPlanData() { + await axios.post("/scriptAgent/setPlanData", { projectId: projectId, agentType: "scriptAgent", data: planData.value }); + } + + const thinkLevel = ref(0); + + function updateThinkConfig(value: number) { + thinkLevel.value = value; + if (socket.value) { + socket.value.emit("updateThinkConfig", { think: value > 0, thinlLevel: value }); + } + } + + return { connected, messages, chat, stopGenerate, socket, status, planData, setPlanData, connect, disconnect, thinkLevel, updateThinkConfig }; + }); +} + +const storeMap = new Map>(); + +function createScriptAgentStore(projectId: string) { + if (!storeMap.has(projectId)) { + storeMap.set(projectId, makeScriptAgentStore(projectId)); + } + return storeMap.get(projectId)!; +} + +export default function useScriptAgentStore() { + const id = projectStore().project?.id; + if (!id) throw new Error("No project selected"); + return createScriptAgentStore(id)(); +} diff --git a/web-core/src/stores/setting.ts b/web-core/src/stores/setting.ts new file mode 100644 index 0000000..66b81a3 --- /dev/null +++ b/web-core/src/stores/setting.ts @@ -0,0 +1,38 @@ +import { getDefaultApiBaseUrl } from "@/utils/apiBaseUrl"; + +export default defineStore( + "setting", + () => { + const showSetting = ref(false); + const isElectron = ref(false); + const canvasWheelEvent = ref("zoom"); + const activeMenu = ref("ui"); + + const baseUrl = ref(getDefaultApiBaseUrl()); + + const needUpdate = ref(false); + + const otherSetting = ref({ + axiosTimeOut: 60 * 10 * 1000, + assetsBatchGenereateSize: 5, + chapterReg: "/第\\s*([0-90-9零一二三四五六七八九十百千万]+)\\s*[章回节]\\s*([^\\n\\r]*)/g", + interacting: true, + scriptEpisodeLength: 5000, + }); + + const themeSetting = ref<{ + mode: "auto" | "light" | "dark"; + primaryColor: string; + fontSize: number; + }>({ + mode: "dark", + primaryColor: "#65D9CB", + fontSize: 16, + }); + + const language = ref("zh-CN"); + + return { showSetting, baseUrl, otherSetting, themeSetting, language, activeMenu, isElectron, canvasWheelEvent, needUpdate }; + }, + { persist: { pick: ["baseUrl", "otherSetting", "themeSetting", "language"] } }, +); diff --git a/web-core/src/stores/user.ts b/web-core/src/stores/user.ts new file mode 100644 index 0000000..bac8794 --- /dev/null +++ b/web-core/src/stores/user.ts @@ -0,0 +1,8 @@ +export default defineStore( + "user", + () => { + const token = localStorage.getItem("token"); + return { token }; + }, + { persist: true }, +); diff --git a/web-core/src/stores/video.ts b/web-core/src/stores/video.ts new file mode 100644 index 0000000..d27bf7d --- /dev/null +++ b/web-core/src/stores/video.ts @@ -0,0 +1,425 @@ +import axios from "@/utils/axios"; +import { ref, computed, watch, nextTick } from "vue"; + +// 图片项 +export interface ImageItem { + id: number; + filePath: string; + prompt: string; +} + +// 视频配置 - 用户创建的配置 +export interface VideoConfig { + id: number; + scriptId: number; + projectId: number; + model: string; + aiConfigId: number | undefined; + manufacturer: string; + mode: "startEnd" | "multi" | "single" | "text"; + startFrame: ImageItem | null; + endFrame: ImageItem | null; + images: ImageItem[]; + resolution: string; + duration: number; + prompt: string; + selectedResultId: number | null; // 选中的生成结果ID + createdAt: string; + audioEnabled: boolean; +} + +// 视频生成结果 +export interface VideoResult { + id: number; + configId: number; // 关联的配置ID + state: 0 | 1 | -1; // 生成中/成功/失败 + filePath: string; + firstFrame: string; + duration: number; + prompt: string; + createdAt: string; + errorReason?: string; +} + +export default defineStore( + "video", + () => { + // 配置列表 + const videoConfigs = ref([]); + // 生成结果列表 + const videoResults = ref([]); + // 当前脚本ID + const currentScriptId = ref(null); + // 当前项目ID + const currentProjectId = ref(null); + // 轮询定时器 + let pollingTimer: number | null = null; + // 配置ID计数器 + let configIdCounter = 0; + + // 获取当前脚本的配置列表 + const currentConfigs = computed(() => { + if (!currentScriptId.value) return []; + return videoConfigs.value.filter((c) => c.scriptId === currentScriptId.value); + }); + + // 获取待轮询的结果ID列表(state === 0) + const pendingResultIds = computed(() => { + return videoResults.value.filter((r) => r.state === 0).map((r) => r.id); + }); + + // 根据配置ID获取其所有生成结果 + function getResultsByConfigId(configId: number): VideoResult[] { + return videoResults.value.filter((r) => r.configId === configId); + } + + // 获取配置的选中结果 + function getSelectedResult(configId: number): VideoResult | null { + const config = videoConfigs.value.find((c) => c.id === configId); + if (!config || !config.selectedResultId) return null; + return videoResults.value.find((r) => r.id === config.selectedResultId) || null; + } + + // 设置当前脚本ID并加载数据 + async function setCurrentScript(scriptId: number, projectId: number) { + currentScriptId.value = scriptId; + currentProjectId.value = projectId; + // 同时获取视频配置和视频生成结果 + await Promise.all([fetchVideoConfigs(scriptId), fetchVideoData(scriptId)]); + } + + // 从后端获取视频数据(视频生成结果) + async function fetchVideoData(scriptId: number, specifyIds: number[] = []) { + try { + const reqBodyObj = { + scriptId: scriptId, + specifyIds: specifyIds, + }; + const { data } = await axios.post("/video/getVideo", reqBodyObj); + + if (specifyIds.length > 0) { + // 部分更新:只更新指定ID的结果状态 + if (data.length === 0) return; + // 创建新数组以触发响应式更新 + const updatedResults = videoResults.value.map((r) => { + const updated = data.find((item: any) => item.id === r.id); + if (updated) { + return { + ...r, + state: updated.state, + filePath: updated.filePath || r.filePath, + firstFrame: updated.firstFrame || r.firstFrame, + duration: updated.duration || r.duration, + errorReason: updated.errorReason || r.errorReason, + }; + } + return r; + }); + videoResults.value = updatedResults; + } else { + // 全量更新:解析后端数据,转换为结果列表 + // 只处理视频结果,不再重建配置(配置从 fetchVideoConfigs 获取) + parseVideoResults(data, scriptId); + } + } catch (error) { + console.error("获取视频数据失败:", error); + } + } + + // 解析后端视频数据为结果列表(不再重建配置) + function parseVideoResults(data: any[], scriptId: number) { + // 获取当前脚本的所有配置ID + const scriptConfigIds = videoConfigs.value.filter((c) => c.scriptId === scriptId).map((c) => c.id); + + // 清空当前脚本配置关联的旧结果 + videoResults.value = videoResults.value.filter((r) => !scriptConfigIds.includes(r.configId)); + + // 添加新的结果 + const newResults: VideoResult[] = data.map((item: any) => ({ + id: item.id, + configId: item.configId || 0, // 后端应该返回 configId + state: item.state, + filePath: item.filePath || "", + firstFrame: item.firstFrame || "", + duration: item.duration || item.time || 0, + prompt: item.prompt || "", + createdAt: new Date().toISOString(), + errorReason: item.errorReason || "", + })); + + // 按ID升序排序,新的在后面 + newResults.sort((a, b) => a.id - b.id); + + videoResults.value = [...videoResults.value, ...newResults]; + } + + // 从后端获取视频配置列表 + async function fetchVideoConfigs(scriptId: number) { + try { + const { data } = await axios.post("/video/getVideoConfigs", { scriptId }); + if (data && Array.isArray(data)) { + // 过滤掉当前脚本的旧配置 + videoConfigs.value = []; + // 添加从后端获取的配置 + data.forEach((item: any) => { + const config: VideoConfig = { + id: item.id, + scriptId: item.scriptId, + projectId: item.projectId, + model: item.model, + aiConfigId: item.aiConfigId, + manufacturer: item.manufacturer, + mode: item.mode, + startFrame: item.startFrame, + endFrame: item.endFrame, + images: item.images || [], + resolution: item.resolution, + duration: item.duration, + prompt: item.prompt || "", + selectedResultId: item.selectedResultId, + createdAt: item.createdAt || new Date().toISOString(), + audioEnabled: item.audioEnabled, + }; + videoConfigs.value.push(config); + + // 更新configIdCounter + if (config.id > configIdCounter) { + configIdCounter = config.id; + } + }); + } + } catch (error) { + console.error("获取视频配置失败:", error); + } + } + + // 从后端返回的数据添加配置(用于新增配置后) + function addConfigFromBackend(configData: any): VideoConfig { + const newConfig: VideoConfig = { + id: configData.id, + scriptId: configData.scriptId, + projectId: configData.projectId, + model: configData.model, + aiConfigId: configData.aiConfigId, + manufacturer: configData.manufacturer, + mode: configData.mode, + startFrame: configData.startFrame || null, + endFrame: configData.endFrame || null, + images: configData.images || [], + resolution: configData.resolution, + duration: configData.duration, + prompt: configData.prompt || "", + selectedResultId: configData.selectedResultId || null, + createdAt: configData.createdAt || new Date().toISOString(), + audioEnabled: configData.audioEnabled, + }; + videoConfigs.value.unshift(newConfig); + + // 更新configIdCounter + if (newConfig.id > configIdCounter) { + configIdCounter = newConfig.id; + } + + return newConfig; + } + + // 添加新配置(本地,仅用于临时操作) + function addConfig(configData: Omit): VideoConfig { + const newConfig: VideoConfig = { + ...configData, + // 确保图片字段有默认值 + startFrame: configData.startFrame || null, + endFrame: configData.endFrame || null, + images: configData.images || [], + id: ++configIdCounter, + selectedResultId: null, + createdAt: new Date().toISOString(), + }; + videoConfigs.value.push(newConfig); + return newConfig; + } + + // 删除配置 + async function removeConfig(configId: number) { + // 调用后端接口删除配置(包括文件和视频) + try { + await axios.post("/video/deleteVideoConfig", { id: configId }); + } catch (error) { + console.error("删除配置失败:", error); + throw error; + } + + // 删除本地 store 中的数据 + const index = videoConfigs.value.findIndex((c) => c.id === configId); + if (index !== -1) { + videoConfigs.value.splice(index, 1); + // 同时删除关联的结果 + videoResults.value = videoResults.value.filter((r) => r.configId !== configId); + } + } + + // 生成视频(单个配置) + async function generateVideo(configId: number): Promise { + const config = videoConfigs.value.find((c) => c.id === configId); + + if (!config) { + throw new Error($t("workbench.production.generate.configNotFound")); + } + + // 构建图片路径列表 + const videoImgs: string[] = []; + if (config.mode === "startEnd") { + if (config.startFrame) videoImgs.push(config.startFrame.filePath); + if (config.endFrame) videoImgs.push(config.endFrame.filePath); + } else if (config.mode === "single") { + if (config.startFrame) videoImgs.push(config.startFrame.filePath); + } else if (config.mode == "text") { + videoImgs.length = 0; + } else { + config.images.forEach((img) => videoImgs.push(img.filePath)); + } + // 调用后端接口 + const { data } = await axios.post("/video/generateVideo", { + projectId: config.projectId, + scriptId: config.scriptId, + mode: config.mode, + aiConfigId: config.aiConfigId, + configId: configId, // 关联配置ID + resolution: config.resolution, + filePath: videoImgs, + duration: config.duration, + prompt: config.prompt, + audioEnabled: config.audioEnabled, + }); + + // 添加新的结果到列表(使用后端返回的真实 ID) + if (data && data.id) { + const newResult: VideoResult = { + id: data.id, + configId: configId, + state: 0, // 生成中 + filePath: "", + firstFrame: "", + duration: config.duration, + prompt: config.prompt, + createdAt: new Date().toISOString(), + }; + // 使用展开运算符创建新数组,确保触发响应式更新 + videoResults.value = [...videoResults.value, newResult]; + + // 强制开始轮询,确保新添加的结果能被轮询到 + startPolling(true); + } + } + + // 选择一个结果作为最终选择 + function selectResult(configId: number, resultId: number) { + const config = videoConfigs.value.find((c) => c.id === configId); + if (config) { + config.selectedResultId = resultId; + } + } + + // 更新配置(仅基础字段) + function updateConfig(configId: number, updates: Partial>) { + const config = videoConfigs.value.find((c) => c.id === configId); + if (config) { + if (updates.prompt !== undefined) config.prompt = updates.prompt; + if (updates.resolution !== undefined) config.resolution = updates.resolution; + if (updates.duration !== undefined) config.duration = updates.duration; + } + } + + // 更新配置(包括图片字段) + function updateConfigFull( + configId: number, + updates: Partial>, + ) { + const config = videoConfigs.value.find((c) => c.id === configId); + if (config) { + if (updates.prompt !== undefined) config.prompt = updates.prompt; + if (updates.resolution !== undefined) config.resolution = updates.resolution; + if (updates.duration !== undefined) config.duration = updates.duration; + if (updates.startFrame !== undefined) config.startFrame = updates.startFrame; + if (updates.endFrame !== undefined) config.endFrame = updates.endFrame; + if (updates.images !== undefined) config.images = [...updates.images]; + if (updates.mode !== undefined) config.mode = updates.mode; + if (updates.audioEnabled !== undefined) config.audioEnabled = updates.audioEnabled; + } + } + + // 开始轮询 + function startPolling(force: boolean = false) { + if (pollingTimer) { + // 如果已有定时器且不是强制启动,直接返回 + if (!force) return; + // 强制启动时,先停止旧的定时器 + stopPolling(); + } + + // 使用 nextTick 确保 computed 属性已更新 + nextTick(() => { + if (pendingResultIds.value.length === 0) return; + + pollingTimer = window.setInterval(async () => { + if (pendingResultIds.value.length === 0) { + stopPolling(); + return; + } + if (currentScriptId.value) { + await fetchVideoData(currentScriptId.value, pendingResultIds.value); + } + }, 10000); + }); + } + + // 停止轮询 + function stopPolling() { + if (pollingTimer) { + clearInterval(pollingTimer); + pollingTimer = null; + } + } + + // 监听待处理结果变化,自动开始/停止轮询 + watch(pendingResultIds, (newVal) => { + if (newVal.length > 0) { + startPolling(); + } else { + stopPolling(); + } + }); + + // 清理 + function cleanup() { + stopPolling(); + } + + return { + // 状态 + videoConfigs, + videoResults, + currentScriptId, + currentProjectId, + currentConfigs, + pendingResultIds, + // 方法 + setCurrentScript, + fetchVideoData, + fetchVideoConfigs, + addConfig, + addConfigFromBackend, + removeConfig, + updateConfig, + updateConfigFull, + generateVideo, + selectResult, + getResultsByConfigId, + getSelectedResult, + startPolling, + stopPolling, + cleanup, + }; + }, + { persist: false }, +); diff --git a/web-core/src/types/global.d.ts b/web-core/src/types/global.d.ts new file mode 100644 index 0000000..6308038 --- /dev/null +++ b/web-core/src/types/global.d.ts @@ -0,0 +1,250 @@ +/** + * 全局变量类型声明 + */ +declare const $t: (key: string, ...args: any[]) => string; + +/** + * Vite 环境变量类型定义 + */ +interface ImportMetaEnv { + readonly BASE_URL: string; + readonly MODE: string; + readonly DEV: boolean; + readonly PROD: boolean; + readonly SSR: boolean; + // 可以根据需要添加自定义环境变量 + [key: string]: any; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} + +/** + * 项目实体 + */ +interface Project { + id?: string; + name?: string; + intro?: string | null; + type: string | null; + artStyle: string | null; + videoRatio: string | null; + userId?: number | null; + createTime?: number; + updatedAt?: number; + projectType: string | null; +} +interface GlobalSetting { + aspectRatio: string; // 影片比例,例如 "16:9" + style: string; // 画风,比如 "写实", "动漫", 未设置时可为 "" + colorScheme: string; // 色彩方案,如 "暖色", 未设置时可为 "" + musicStyle: string; // 音乐风格,如 "流行", 未设置时可为 "" + type: string; // 小说类型 +} +interface ModelDataType { + model: string; + apiKey: string; + baseURL: string; +} +interface ModalSetting { + languageModel: ModelDataType; // 语言模型名称 + imageModel: ModelDataType; // 图像模型名称 + videoModel: ModelDataType; // 视频模型名称 + audioModel: ModelDataType; // 音频模型名称 + plotAgentModel: ModelDataType; +} +/** + * 项目的详细包装数据 + */ +interface ProjectData { + /** 小说原文(可选) */ + novelText?: string; + /** 每集长度(可选) */ + episodeLength?: number; + /** 集数(可选) */ + episodeCount?: number; + /** 故事摘要(可选) */ + summary?: string; + /** 角色列表(可选) */ + characters?: Character[]; + /** 事件列表(可选) */ + events?: Event[]; + /** 剧本列表(可选) */ + scripts?: Script[]; + /** 分镜列表(可选) */ + storyboards?: Storyboard[]; + /** 视频列表(可选) */ + videos?: Video[]; +} + +/** + * 角色实体 + */ +interface Character { + id?: number; // 主键id + name: string; // 名称 + gender: string; // 性别 + appearance: string; // 外貌特征 + personality: string; // 角色性格 + intro: string; // 角色描述 + timbre?: string; // 音色描述 + age?: string; // 年龄段 + voicePath?: string; // 角色音频 + type: string; // 角色类型,如主角、配角 + relation: string; // 角色关系 + filePath?: string; // 人设图片 + projectId?: number; // 项目表id + eventId?: number; // 事件表id + generatedId?: string; // ai生成id +} +interface ProjectElement { + id: number; + type: string; + name: string; + description: string; + prompt: string; + remark: string; + imageUrl: string; +} +/** + * 事件实体 + */ +interface Event { + /** 事件唯一标识 */ + id: number; + /** 事件标题 */ + name: string; + /** 所属集数 */ + scope: number; + /** 事件描述 */ + description: string; + // 角色 + role: string; + /** 事件情感分数,用于刻画情感强度 */ + emotionalIndex: number; + //状态 + state: "已完成" | "进行中" | "未开始"; + //背景 + novelBack: string; + //归属项目id + projectId: number; + //ai生成的id + generatedId?: string; +} +interface UploadFile { + file?: File; + name: string; + url?: string; +} +/** + * 剧本实体 + */ +interface Script { + /** 剧本唯一标识 */ + id: string; + name?: string; + /** 所属集数 */ + episode: number; + /** 剧本标题 */ + title: string; + /** 剧本内容 */ + content: string; + /** 包含场景数 */ + scenes: number; + event?: string; +} + +/** + * 分镜实体 + */ +interface Storyboard { + id: number; + imageUrl: string; + intro: string; + name: string; + imgPrompt: string; + videoPrompt: string; +} +interface RoleData { + id: number; + name: string; + personality: string; + type: string; + relation: string; + generatedId: string; + deressIntro: DressIntro[]; +} +interface Props { + id: number; + name: string; + content: string; + type: string; + prompt: string; + remark: string; + scriptId: number; + projectId: number; + imageUrl: string; +} +interface Scenes { + id: number; + imageUrl: string; + intro: string; + name: string; + imgPrompt: string; + videoPrompt: string; +} +interface DressIntro { + intro: string; + filePath: string; +} +/** + * 视频实体 + */ +interface Video { + /** 视频唯一标识 */ + id: string; + /** 所属分镜ID */ + storyboardId: string; + /** 视频制作状态 */ + status: "pending" | "processing" | "completed" | "failed"; + /** 视频URL(可选,制作完成后有) */ + url?: string; + /** 制作进度(0~100) */ + progress: number; +} + +// interface Character { +// name: string; +// relationship: string; +// type: string; +// sex: "男" | "女" | "其他"; +// id: string; +// } +interface EventType { + chapter: string; + name: string; + detail: string; + state: "已完成" | "进行中" | "未开始"; + id?: string; + users: { + name: string; + id?: string; + feature: string; + }[]; + novelBack: string; + emotionalIndex?: number; +} + +// Vite 环境变量类型声明 +interface ImportMetaEnv { + BASE_URL: string | undefined; + readonly VITE_TYPE: string; + readonly VITE_BASE_URL: string; + readonly VITE_WS_URL: string; + // 在这里添加其他环境变量 +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} \ No newline at end of file diff --git a/web-core/src/types/shims-vue.d.ts b/web-core/src/types/shims-vue.d.ts new file mode 100644 index 0000000..aa8ffec --- /dev/null +++ b/web-core/src/types/shims-vue.d.ts @@ -0,0 +1,55 @@ +declare module "*.vue" { + import type { DefineComponent } from "vue"; + const component: DefineComponent<{}, {}, any>; + export default component; +} + +declare module "*.svg" { + const content: string; + export default content; +} + +declare module "*.png" { + const content: string; + export default content; +} + +declare module "*.webp" { + const content: string; + export default content; +} + +declare module "*.jpg" { + const content: string; + export default content; +} + +declare module "*.jpeg" { + const content: string; + export default content; +} + +declare module "*?raw" { + const content: string; + export default content; +} + +declare module "*.gif" { + const content: string; + export default content; +} + +declare module "*.ico" { + const content: string; + export default content; +} + +declare module "*.bmp" { + const content: string; + export default content; +} + +declare module "*.avif" { + const content: string; + export default content; +} \ No newline at end of file diff --git a/web-core/src/utils/apiBaseUrl.ts b/web-core/src/utils/apiBaseUrl.ts new file mode 100644 index 0000000..8f6d3b3 --- /dev/null +++ b/web-core/src/utils/apiBaseUrl.ts @@ -0,0 +1,71 @@ +const DEFAULT_API_PORT = "10588"; +const VITE_DEV_PORT = "50188"; +const LOCAL_HOSTS = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"]); + +function trimTrailingSlash(url: string) { + return url.replace(/\/+$/, ""); +} + +function ensureApiPath(url: string) { + try { + const parsedUrl = new URL(url); + if (parsedUrl.pathname === "/" || parsedUrl.pathname === "") { + parsedUrl.pathname = "/api"; + return trimTrailingSlash(parsedUrl.toString()); + } + } catch { + return url; + } + + return url; +} + +function isLocalHost(hostname: string) { + return LOCAL_HOSTS.has(hostname); +} + +function getBrowserOrigin() { + if (typeof window === "undefined") return ""; + return window.location.origin === "null" ? "" : window.location.origin; +} + +export function getDefaultApiBaseUrl() { + if (typeof window === "undefined") return `http://localhost:${DEFAULT_API_PORT}/api`; + + const { protocol, hostname, port } = window.location; + if (protocol === "file:") return `http://localhost:${DEFAULT_API_PORT}/api`; + + if (port === VITE_DEV_PORT) { + return `${protocol}//${hostname}:${DEFAULT_API_PORT}/api`; + } + + const origin = getBrowserOrigin(); + return origin ? `${origin}/api` : `http://localhost:${DEFAULT_API_PORT}/api`; +} + +export function normalizeApiBaseUrl(value?: string | null) { + const fallback = getDefaultApiBaseUrl(); + const nextValue = trimTrailingSlash((value || "").trim()); + + if (!nextValue) return fallback; + if (shouldReplaceLocalApiBaseUrl(nextValue)) return fallback; + + return ensureApiPath(nextValue); +} + +export function shouldReplaceLocalApiBaseUrl(value: string) { + if (typeof window === "undefined" || window.location.protocol === "file:") return false; + + const currentHostIsLocal = isLocalHost(window.location.hostname); + if (currentHostIsLocal && window.location.port === VITE_DEV_PORT) return false; + + try { + return isLocalHost(new URL(value).hostname); + } catch { + return false; + } +} + +export function getDefaultSocketBaseUrl() { + return getDefaultApiBaseUrl().replace(/\/api$/, ""); +} diff --git a/web-core/src/utils/assetsCheck.ts b/web-core/src/utils/assetsCheck.ts new file mode 100644 index 0000000..01c8ebe --- /dev/null +++ b/web-core/src/utils/assetsCheck.ts @@ -0,0 +1,114 @@ +import { h, ref, render, nextTick } from "vue"; +import { Dialog as TDialog } from "tdesign-vue-next"; +import AssetsView from "@/views/assets/index.vue"; + +interface Asset { + id: number; + assetsId: number | null; + name: string; + prompt: string; + describe: string; + remark: string; + src: string; + type: "role" | "tool" | "scene" | "clip" | "audio"; + imageId: number | null; + state: "未生成" | "生成中" | "已完成" | "生成失败"; + sonAssets?: Asset[]; +} + +export type AssetType = "role" | "tool" | "scene" | "clip" | "audio"; +export type ClipMediaType = "image" | "video" | "audio"; + +export interface AssetsSelectOptions { + /** 限制显示的资产类型,不传则显示全部 */ + types?: AssetType[]; + /** 当 types 包含 clip 时,限制 clip 的媒体子类型(图片/视频/音频),不传则显示全部 */ + clipMediaTypes?: ClipMediaType[]; + /** 是否多选,默认 true */ + multiple?: boolean; + /** 弹窗标题 */ + title?: string; + selectorMode?: boolean; +} + +export default function openAssetsSelector(options: AssetsSelectOptions = {}): Promise { + const { types, clipMediaTypes, multiple = true, title = window.$t("common.selectAssets"), selectorMode = false } = options; + return new Promise((resolve) => { + const container = document.createElement("div"); + document.body.appendChild(container); + + const visible = ref(false); + const assetsRef = ref>(); + let done = false; + + const cleanup = () => { + render(null, container); + container.remove(); + }; + + const finish = (assets: Asset[]) => { + if (done) return; + done = true; + visible.value = false; + renderDialog(); + resolve(assets); + }; + + const renderDialog = () => { + const vnode = h( + TDialog, + { + visible: visible.value, + header: title, + width: "80%", + top: "5vh", + destroyOnClose: true, + confirmBtn: window.$t("common.confirm"), + cancelBtn: window.$t("common.cancel"), + onConfirm: () => { + const selectedKeys = assetsRef.value?.selectedRowKeys || []; + const selectedSubKeys = assetsRef.value?.selectedSubRowKeys || []; + const data = assetsRef.value?.tableData || []; + // 选中的父资产 + const selectedParents = data.filter((item) => selectedKeys.includes(item.id)); + // 选中的子资产 + const selectedSubs: Asset[] = []; + data.forEach((item) => { + item.sonAssets?.forEach((sub: any) => { + if (selectedSubKeys.includes(sub.id)) selectedSubs.push(sub); + }); + }); + finish([...selectedParents, ...selectedSubs] as any[]); + }, + onClose: () => finish([]), + onCancel: () => finish([]), + onClosed: () => cleanup(), + }, + { + default: () => + h("div", { style: "height: 72vh; overflow: auto;" }, [ + h(AssetsView, { + ref: assetsRef, + selectorMode: selectorMode, + allowedTypes: types, + clipMediaTypes, + multiple, + }), + ]), + }, + ); + // 继承主应用上下文,使全局注册的组件可用 + const appContext = (document.querySelector("#app") as any)?.__vue_app__?._context; + if (appContext) { + vnode.appContext = appContext; + } + render(vnode, container); + }; + + renderDialog(); + nextTick(() => { + visible.value = true; + renderDialog(); + }); + }); +} diff --git a/web-core/src/utils/axios.ts b/web-core/src/utils/axios.ts new file mode 100644 index 0000000..dca157a --- /dev/null +++ b/web-core/src/utils/axios.ts @@ -0,0 +1,39 @@ +import axios from "axios"; +import router from "@/router/index"; +import { storeToRefs } from "pinia"; +import { MessagePlugin } from "tdesign-vue-next"; +import settingStore from "@/stores/setting"; +import { normalizeApiBaseUrl } from "@/utils/apiBaseUrl"; + +const instance = axios.create(); + +instance.interceptors.request.use(function (config) { + const { baseUrl, otherSetting } = storeToRefs(settingStore()); + const resolvedBaseUrl = normalizeApiBaseUrl(baseUrl.value); + if (baseUrl.value !== resolvedBaseUrl) { + baseUrl.value = resolvedBaseUrl; + } + config.baseURL = resolvedBaseUrl; + config.timeout = otherSetting.value.axiosTimeOut; + const token = localStorage.getItem("token"); + if (token) { + config.headers.Authorization = token; + } + return config; +}); + +instance.interceptors.response.use( + function (response) { + return response.data; + }, + function (error) { + if (error.status === 401) { + localStorage.removeItem("token"); + router.push("/login"); + MessagePlugin.error(window.$t("common.sessionExpired")); + } + return Promise.reject(error?.response?.data ?? error); + }, +); + +export default instance; diff --git a/web-core/src/utils/global.ts b/web-core/src/utils/global.ts new file mode 100644 index 0000000..8d826f9 --- /dev/null +++ b/web-core/src/utils/global.ts @@ -0,0 +1,17 @@ +import { MessagePlugin } from "tdesign-vue-next"; + +import i18n from "@/locales"; +const { t } = i18n.global; + +declare global { + interface Window { + $message: typeof MessagePlugin; + $port: string; + $t: typeof t; + } +} + +window.$message = MessagePlugin; + + +window.$t = t; diff --git a/web-core/src/utils/imageOptimizer.ts b/web-core/src/utils/imageOptimizer.ts new file mode 100644 index 0000000..1cc80d3 --- /dev/null +++ b/web-core/src/utils/imageOptimizer.ts @@ -0,0 +1,65 @@ +// plugins/imageOptimizer.ts +import type { App } from "vue"; + +interface ImageOptimizerOptions { + exclude?: string[]; +} + +const defaultOptions: ImageOptimizerOptions = { + exclude: [".t-image-viewer__modal-image"], +}; + +export const imageOptimizer = { + install(_app: App, userOptions: ImageOptimizerOptions = {}) { + const options = { ...defaultOptions, ...userOptions }; + const excludeSelector = options.exclude!.join(","); + + // CSS 处理样式,零 JS 开销 + const style = document.createElement("style"); + style.textContent = `img:not(${excludeSelector}) { content-visibility: auto; }`; + document.head.appendChild(style); + + const io = new IntersectionObserver((entries) => { + for (const entry of entries) { + if (entry.isIntersecting) { + (entry.target as HTMLImageElement).decoding = "async"; + io.unobserve(entry.target); + } + } + }); + + const processImage = (img: HTMLImageElement) => { + if (img.dataset.opt) return; + if (img.matches(excludeSelector)) return; + img.loading = "lazy"; + img.dataset.opt = "1"; + io.observe(img); + }; + + // MutationObserver 只处理新增节点,不全量扫描 + const mo = new MutationObserver((mutations) => { + // 收集本次批次所有新增 img,用 Set 去重 + const imgs = new Set(); + + for (const mutation of mutations) { + for (const node of mutation.addedNodes) { + if (node instanceof HTMLImageElement) { + imgs.add(node); + } else if (node instanceof HTMLElement) { + node.querySelectorAll("img").forEach((img) => imgs.add(img)); + } + } + } + + imgs.forEach(processImage); + }); + + // 初始化:仅首次全量扫描一次 + const init = () => { + document.querySelectorAll("img").forEach(processImage); + mo.observe(document.body, { childList: true, subtree: true }); + }; + + document.readyState === "loading" ? document.addEventListener("DOMContentLoaded", init, { once: true }) : init(); + }, +}; diff --git a/web-core/src/utils/parseNovel.ts b/web-core/src/utils/parseNovel.ts new file mode 100644 index 0000000..8fc30ad --- /dev/null +++ b/web-core/src/utils/parseNovel.ts @@ -0,0 +1,149 @@ +import settingStore from "@/stores/setting"; +const REEL_REGEX = /^(第[\d一二三四五六七八九十百千]+卷)\s*([^\n第]*)/gm; +const DEFAULT_CHAPTER_REGEX = /第\s*([0-90-9零一二三四五六七八九十百千万]+)\s*[章回节]\s*([^\n\r]*)/g; +const CHINESE_NUM_MAP: { [key: string]: number } = { + 零: 0, + 一: 1, + 二: 2, + 三: 3, + 四: 4, + 五: 5, + 六: 6, + 七: 7, + 八: 8, + 九: 9, +}; +const CHINESE_UNIT_MAP: { [key: string]: number } = { + 十: 10, + 百: 100, + 千: 1000, +}; +interface Chapter { + index: number; + chapter: string; + text: string; +} +interface Reel { + index: number; + reel: string; + chapters: Chapter[]; +} +function parseNumber(numStr: string): number { + if (/^\d+$/.test(numStr)) return parseInt(numStr, 10); + if (/^十[一二三四五六七八九]?$/.test(numStr)) { + if (numStr.length === 1) return 10; + return 10 + CHINESE_NUM_MAP[numStr[1]]; + } + let num = 0, + digit = 0; + for (const c of numStr) { + if (CHINESE_NUM_MAP[c] !== undefined) digit = CHINESE_NUM_MAP[c]; + else if (CHINESE_UNIT_MAP[c] !== undefined) { + if (digit === 0 && c === "十") digit = 1; + num += digit * CHINESE_UNIT_MAP[c]; + digit = 0; + } + } + num += digit; + return num; +} +export default function parseNovel(text: string): Reel[] { + REEL_REGEX.lastIndex = 0; + const reelMatches = Array.from(text.matchAll(REEL_REGEX)); + const reels: Reel[] = []; + let CHAPTER_REGEX; + + const regStr = settingStore().otherSetting.chapterReg; + if (regStr) { + const match = regStr.match(/^\/(.*)\/([igmuy]*)$/); + if (match) { + CHAPTER_REGEX = new RegExp(match[1], match[2]); + } else { + CHAPTER_REGEX = new RegExp(regStr); + } + } else { + CHAPTER_REGEX = DEFAULT_CHAPTER_REGEX; + } + + // 没有卷结构 + if (reelMatches.length === 0) { + const chapters: Chapter[] = []; + CHAPTER_REGEX.lastIndex = 0; + const matches = Array.from(text.matchAll(CHAPTER_REGEX)); + if (matches.length === 0 && text.trim() !== "") { + chapters.push({ index: 1, chapter: "", text: text.trim() }); + } else { + for (let i = 0; i < matches.length; i++) { + const start = matches[i].index! + matches[i][0].length; + const end = i + 1 < matches.length ? matches[i + 1].index! : text.length; + const content = text + .slice(start, end) + .replace(/^[\r\n]+/, "") + .trim(); + chapters.push({ + index: parseNumber(matches[i][1].replace(/第|章/g, "")), + chapter: matches[i][2].trim(), + text: content, + }); + } + } + // 对章节排序 + chapters.sort((a, b) => a.index - b.index); + reels.push({ + index: 1, + reel: "正文卷", + chapters, + }); + return reels; + } + + // 有卷结构 + const reelMap = new Map(); + for (let i = 0; i < reelMatches.length; i++) { + const match = reelMatches[i]; + const index = match.index!; + const reelRaw = match[1]; + const reelName = match[2]?.trim() || ""; + const end = i + 1 < reelMatches.length ? reelMatches[i + 1].index! : text.length; + const reelSection = text.slice(index, end); + + const chapterMatches = Array.from(reelSection.matchAll(CHAPTER_REGEX)); + const chapters: Chapter[] = []; + if (chapterMatches.length === 0 && reelSection.replace(REEL_REGEX, "").trim() !== "") { + chapters.push({ + index: 1, + chapter: "", + text: reelSection.replace(REEL_REGEX, "").trim(), + }); + } + for (let j = 0; j < chapterMatches.length; j++) { + const start = chapterMatches[j].index! + chapterMatches[j][0].length; + const end = j + 1 < chapterMatches.length ? chapterMatches[j + 1].index! : reelSection.length; + const content = reelSection + .slice(start, end) + .replace(/^[\r\n]+/, "") + .trim(); + chapters.push({ + index: parseNumber(chapterMatches[j][1].replace(/第|章/g, "")), + chapter: chapterMatches[j][2].trim(), + text: content, + }); + } + // 每卷内章节排序 + chapters.sort((a, b) => a.index - b.index); + + if (!reelMap.has(reelName)) { + reelMap.set(reelName, { + index: parseNumber(reelRaw.replace(/第|卷/g, "")), + reel: reelName, + chapters: [], + }); + } + reelMap.get(reelName)!.chapters.push(...chapters); + } + // 按卷序号排序输出 + const result = Array.from(reelMap.values()).sort((a, b) => a.index - b.index); + // 再次确保合并同名卷后,章节整体排序 + result.forEach((reel) => reel.chapters.sort((a, b) => a.index - b.index)); + return result; +} diff --git a/web-core/src/utils/parseScript.ts b/web-core/src/utils/parseScript.ts new file mode 100644 index 0000000..92534ad --- /dev/null +++ b/web-core/src/utils/parseScript.ts @@ -0,0 +1,112 @@ +// 默认剧本集正则:匹配"第X集 标题"格式,支持中文数字和阿拉伯数字 +const DEFAULT_EPISODE_REGEX = /第\s*([0-90-9零一二三四五六七八九十百千万]+)\s*集\s*([^\n\r]*)/g; + +const CHINESE_NUM_MAP: { [key: string]: number } = { + 零: 0, + 一: 1, + 二: 2, + 三: 3, + 四: 4, + 五: 5, + 六: 6, + 七: 7, + 八: 8, + 九: 9, +}; +const CHINESE_UNIT_MAP: { [key: string]: number } = { + 十: 10, + 百: 100, + 千: 1000, +}; + +/** + * 单集剧本 + */ +export interface Episode { + /** 集数序号 */ + index: number; + /** 剧本标题 */ + chapter: string; + /** 剧本正文内容 */ + text: string; +} + + +function parseNumber(numStr: string): number { + if (/^\d+$/.test(numStr)) return parseInt(numStr, 10); + if (/^十[一二三四五六七八九]?$/.test(numStr)) { + if (numStr.length === 1) return 10; + return 10 + CHINESE_NUM_MAP[numStr[1]]; + } + let num = 0, + digit = 0; + for (const c of numStr) { + if (CHINESE_NUM_MAP[c] !== undefined) digit = CHINESE_NUM_MAP[c]; + else if (CHINESE_UNIT_MAP[c] !== undefined) { + if (digit === 0 && c === "十") digit = 1; + num += digit * CHINESE_UNIT_MAP[c]; + digit = 0; + } + } + num += digit; + return num; +} + +/** + * 将正则字符串解析为 RegExp 对象 + */ +function parseRegStr(regStr: string): RegExp { + const match = regStr.match(/^\/(.*)\/([ igmuy]*)$/); + if (match) { + return new RegExp(match[1], match[2].includes("g") ? match[2] : match[2] + "g"); + } + return new RegExp(regStr, "g"); +} + +/** + * 解析剧本文本,提取各集的集数、标题和正文内容 + * @param text 剧本原始文本 + * @param customRegStr 自定义正则字符串(优先级最高),留空则依次回退到设置中的正则和默认正则 + * @returns 解析后的剧本分组列表(每组包含若干集) + */ +export default function parseScript(text: string, customRegStr?: string): Episode[] { + let EPISODE_REGEX: RegExp; + + // 优先级:调用方传入 > 默认正则 + const regStr = customRegStr?.trim(); + if (regStr) { + EPISODE_REGEX = parseRegStr(regStr); + } else { + EPISODE_REGEX = DEFAULT_EPISODE_REGEX; + } + + EPISODE_REGEX.lastIndex = 0; + const matches = Array.from(text.matchAll(EPISODE_REGEX)); + const episodes: Episode[] = []; + + if (matches.length === 0 && text.trim() !== "") { + // 没有找到集标记,将整段文本作为第 1 集 + episodes.push({ index: 1, chapter: "", text: text.trim() }); + } else { + for (let i = 0; i < matches.length; i++) { + const start = matches[i].index! + matches[i][0].length; + const end = i + 1 < matches.length ? matches[i + 1].index! : text.length; + const content = text + .slice(start, end) + .replace(/^[\r\n]+/, "") + .trim(); + episodes.push({ + index: parseNumber(matches[i][1]), + chapter: matches[i][2]?.trim() ?? "", + text: content, + }); + } + } + + // 按集数排序 + episodes.sort((a, b) => a.index - b.index); + + return episodes; +} + + diff --git a/web-core/src/utils/providersLogo.ts b/web-core/src/utils/providersLogo.ts new file mode 100644 index 0000000..5272334 --- /dev/null +++ b/web-core/src/utils/providersLogo.ts @@ -0,0 +1,349 @@ +// 动态加载 src/assets/providers 下所有图标 +const iconModules = import.meta.glob("@/assets/providers/*.{webp,png}", { + eager: true, + query: "?url", + import: "default", +}); + +/** 从本地 assets/providers 获取图标 URL */ +function icon(id: string): string { + for (const [key, url] of Object.entries(iconModules)) { + const filename = key.split("/").pop()?.split(".")[0]; + if (filename === id) return url; + } + return ""; +} + +export const providersLogo: Record = { + deepSeek: icon("deepseek"), + volcengine: icon("volcengine"), + kling: icon("kling"), + zhipu: icon("zhipu"), + qwen: icon("qwen"), + wan: icon("qwen"), + openai: icon("openai"), + vidu: icon("vidu"), + anthropic: icon("anthropic"), + claude: icon("claude"), + runninghub: icon("runninghub"), + gemini: icon("gemini"), + google: icon("google"), + xai: icon("xai"), + grok: icon("grok"), + grsai: icon("grsai"), + minimax: icon("minimax"), + baichuan: icon("baichuan"), + yi: icon("yi"), + moonshot: icon("moonshot"), + kimi: icon("kimi"), + stepfun: icon("stepfun"), + spark: icon("spark"), + hunyuan: icon("hunyuan"), + wenxin: icon("wenxin"), + doubao: icon("doubao"), + mistral: icon("mistral"), + meta: icon("meta"), + cohere: icon("cohere"), + perplexity: icon("perplexity"), + stability: icon("stability"), + luma: icon("luma"), + runway: icon("runway"), + pika: icon("pika"), + sora: icon("sora"), + flux: icon("flux"), + midjourney: icon("midjourney"), + suno: icon("suno"), + hailuo: icon("hailuo"), + pixverse: icon("pixverse"), + jimeng: icon("jimeng"), + nvidia: icon("nvidia"), + groq: icon("groq"), + recraft: icon("recraft"), + siliconcloud: icon("siliconcloud"), + ideogram: icon("ideogram"), + novita: icon("novita"), + // 新增 + together: icon("together"), + fireworks: icon("fireworks"), + replicate: icon("replicate"), + ollama: icon("ollama"), + openrouter: icon("openrouter"), + huggingface: icon("huggingface"), + aws: icon("aws"), + azure: icon("azure"), + lambda: icon("lambda"), + cerebras: icon("cerebras"), + sambanova: icon("sambanova"), + deepinfra: icon("deepinfra"), + fal: icon("fal"), + elevenlabs: icon("elevenlabs"), + haiper: icon("haiper"), + viggle: icon("viggle"), + tripo: icon("tripo"), + sensenova: icon("sensenova"), + tiangong: icon("tiangong"), + skywork: icon("skywork"), + internlm: icon("internlm"), + jina: icon("jina"), + voyage: icon("voyage"), + coze: icon("coze"), + dify: icon("dify"), + nanobanana: icon("nanobanana"), + ai21: icon("ai21"), + ai360: icon("ai360"), + bedrock: icon("bedrock"), + chatglm: icon("chatglm"), + dalle: icon("dalle"), + gemma: icon("gemma"), + rwkv: icon("rwkv"), + nova: icon("nova"), + vertexai: icon("vertexai"), + cloudflare: icon("cloudflare"), + nebius: icon("nebius"), + hyperbolic: icon("hyperbolic"), + inception: icon("inception"), + leptonai: icon("leptonai"), + friendli: icon("friendli"), + featherless: icon("featherless"), + centml: icon("centml"), + crusoe: icon("crusoe"), + parasail: icon("parasail"), + upstage: icon("upstage"), + tii: icon("tii"), + snowflake: icon("snowflake"), + inflection: icon("inflection"), + deepcogito: icon("deepcogito"), + alephalpha: icon("alephalpha"), + nousresearch: icon("nousresearch"), + dolphin: icon("dolphin"), + dbrx: icon("dbrx"), + kolors: icon("kolors"), + lightricks: icon("lightricks"), + novelai: icon("novelai"), + fishaudio: icon("fishaudio"), + coqui: icon("coqui"), + comfyui: icon("comfyui"), + automatic: icon("automatic"), + civitai: icon("civitai"), + modelscope: icon("modelscope"), + bailian: icon("bailian"), + xinference: icon("xinference"), + vllm: icon("vllm"), + lmstudio: icon("lmstudio"), + baseten: icon("baseten"), + // 补齐 + aya: icon("aya"), + azureai: icon("azureai"), + baidu: icon("baidu"), + bfl: icon("bfl"), + briaai: icon("briaai"), + burncloud: icon("burncloud"), + deepai: icon("deepai"), + deepmind: icon("deepmind"), + essentialai: icon("essentialai"), + glmv: icon("glmv"), + googlecloud: icon("googlecloud"), + hedra: icon("hedra"), + iflytekcloud: icon("iflytekcloud"), + infermatic: icon("infermatic"), + infinigence: icon("infinigence"), + kwaikat: icon("kwaikat"), + kwaipilot: icon("kwaipilot"), + llava: icon("llava"), + llmapi: icon("llmapi"), + longcat: icon("longcat"), + lovart: icon("lovart"), + monica: icon("monica"), + morph: icon("morph"), + myshell: icon("myshell"), + openchat: icon("openchat"), + openwebui: icon("openwebui"), + palm: icon("palm"), + phind: icon("phind"), + player2: icon("player2"), + poe: icon("poe"), + pollinations: icon("pollinations"), + ppio: icon("ppio"), + qingyan: icon("qingyan"), + sophnet: icon("sophnet"), + statecloud: icon("statecloud"), + straico: icon("straico"), + streamlake: icon("streamlake"), + submodel: icon("submodel"), + sync: icon("sync"), + targon: icon("targon"), + topazlabs: icon("topazlabs"), + turix: icon("turix"), + udio: icon("udio"), + workersai: icon("workersai"), + xiaomimimo: icon("xiaomimimo"), + xuanyuan: icon("xuanyuan"), + yandex: icon("yandex"), + yuanbao: icon("yuanbao"), + zeroone: icon("zeroone"), + newapi: icon("newapi"), + nplcloud: icon("nplcloud"), +}; + +export const modelProviderRules: Array<{ pattern: RegExp; provider: keyof typeof providersLogo }> = [ + { pattern: /gpt|o1|o3|o4|openai/i, provider: "openai" }, + { pattern: /claude|anthropic/i, provider: "anthropic" }, + { pattern: /deep[-_ ]?[-_ ]?seek/i, provider: "deepSeek" }, + { pattern: /gemini|veo/i, provider: "gemini" }, + { pattern: /qwen|qwq|tongyi|通义|wanx|万相|wan/i, provider: "qwen" }, + { pattern: /cog[-_ ]?view|cog[-_ ]?video|glm|zhipu|智谱/i, provider: "zhipu" }, + { pattern: /doubao|see[-_ ]?dream|see[-_ ]?dance|volc/i, provider: "volcengine" }, + { pattern: /kling|可灵/i, provider: "kling" }, + { pattern: /vidu/i, provider: "vidu" }, + { pattern: /running[-_ ]?hub/i, provider: "runninghub" }, + { pattern: /grok|xai|grs[-_ ]?ai/i, provider: "grsai" }, + { pattern: /mini[-_ ]?max|abab|hai[-_ ]?luo|海螺/i, provider: "minimax" }, + { pattern: /bai[-_ ]?chuan|百川/i, provider: "baichuan" }, + { pattern: /\byi\b|零一|yi-/i, provider: "yi" }, + { pattern: /moon[-_ ]?shot|kimi|月之暗面/i, provider: "moonshot" }, + { pattern: /step[-_ ]?fun|阶跃/i, provider: "stepfun" }, + { pattern: /spark|星火|讯飞/i, provider: "spark" }, + { pattern: /hun[-_ ]?yuan|混元|腾讯/i, provider: "hunyuan" }, + { pattern: /ernie|文心|wen[-_ ]?xin/i, provider: "wenxin" }, + { pattern: /mistral|mixtral|pixtral|codestral/i, provider: "mistral" }, + { pattern: /llama|meta/i, provider: "meta" }, + { pattern: /cohere|command[-_ ]?r/i, provider: "cohere" }, + { pattern: /perplexity|sonar/i, provider: "perplexity" }, + { pattern: /stable[-_ .]?diffusion|stability|sdxl|sd3/i, provider: "stability" }, + { pattern: /luma|dream[-_ .]?machine/i, provider: "luma" }, + { pattern: /runway|gen-[23]/i, provider: "runway" }, + { pattern: /pika/i, provider: "pika" }, + { pattern: /sora/i, provider: "sora" }, + { pattern: /flux/i, provider: "flux" }, + { pattern: /mid[-_ ]?journey|mj/i, provider: "midjourney" }, + { pattern: /suno/i, provider: "suno" }, + { pattern: /pix[-_ ]?verse/i, provider: "pixverse" }, + { pattern: /jimeng|即梦/i, provider: "jimeng" }, + { pattern: /nvidia|nemotron/i, provider: "nvidia" }, + { pattern: /groq/i, provider: "groq" }, + { pattern: /recraft/i, provider: "recraft" }, + { pattern: /silicon[-_ ]?cloud|silicon[-_ ]?flow|硅基/i, provider: "siliconcloud" }, + { pattern: /ideogram/i, provider: "ideogram" }, + { pattern: /novita/i, provider: "novita" }, + // 新增 + { pattern: /together/i, provider: "together" }, + { pattern: /fire[-_ ]?works/i, provider: "fireworks" }, + { pattern: /replicate/i, provider: "replicate" }, + { pattern: /ollama/i, provider: "ollama" }, + { pattern: /open[-_ ]?router/i, provider: "openrouter" }, + { pattern: /hugging[-_ ]?face|hf\b/i, provider: "huggingface" }, + { pattern: /bed[-_ ]?rock/i, provider: "bedrock" }, + { pattern: /\baws\b/i, provider: "aws" }, + { pattern: /azure/i, provider: "azure" }, + { pattern: /\blambda\b/i, provider: "lambda" }, + { pattern: /cerebras/i, provider: "cerebras" }, + { pattern: /samba[-_ ]?nova/i, provider: "sambanova" }, + { pattern: /deep[-_ ]?infra/i, provider: "deepinfra" }, + { pattern: /\bfal\b/i, provider: "fal" }, + { pattern: /eleven[-_ ]?labs/i, provider: "elevenlabs" }, + { pattern: /haiper/i, provider: "haiper" }, + { pattern: /viggle/i, provider: "viggle" }, + { pattern: /tripo/i, provider: "tripo" }, + { pattern: /sense[-_ ]?nova|商汤/i, provider: "sensenova" }, + { pattern: /tian[-_ ]?gong|天工/i, provider: "tiangong" }, + { pattern: /sky[-_ ]?work/i, provider: "skywork" }, + { pattern: /intern[-_ ]?lm|书生/i, provider: "internlm" }, + { pattern: /jina/i, provider: "jina" }, + { pattern: /voyage/i, provider: "voyage" }, + { pattern: /coze|扣子/i, provider: "coze" }, + { pattern: /dify/i, provider: "dify" }, + { pattern: /nano[-_ ]?banana/i, provider: "nanobanana" }, + { pattern: /ai[-_ ]?21|jamba|jurassic/i, provider: "ai21" }, + { pattern: /ai[-_ ]?360|360智脑|奇虎/i, provider: "ai360" }, + { pattern: /chat[-_ ]?glm/i, provider: "chatglm" }, + { pattern: /dall[\-·]?e/i, provider: "dalle" }, + { pattern: /gemma/i, provider: "gemma" }, + { pattern: /rwkv/i, provider: "rwkv" }, + { pattern: /\bnova\b/i, provider: "nova" }, + { pattern: /vertex[-_ ]?ai|vertex/i, provider: "vertexai" }, + { pattern: /cloud[-_ ]?flare|workers[-_ ]?ai/i, provider: "cloudflare" }, + { pattern: /nebius/i, provider: "nebius" }, + { pattern: /hyperbolic/i, provider: "hyperbolic" }, + { pattern: /inception/i, provider: "inception" }, + { pattern: /lepton[-_ ]?ai|lepton/i, provider: "leptonai" }, + { pattern: /friendli/i, provider: "friendli" }, + { pattern: /featherless/i, provider: "featherless" }, + { pattern: /centml/i, provider: "centml" }, + { pattern: /crusoe/i, provider: "crusoe" }, + { pattern: /para[-_ ]?sail/i, provider: "parasail" }, + { pattern: /up[-_ ]?stage|solar/i, provider: "upstage" }, + { pattern: /\btii\b|falcon/i, provider: "tii" }, + { pattern: /snow[-_ ]?flake|arctic/i, provider: "snowflake" }, + { pattern: /inflection/i, provider: "inflection" }, + { pattern: /deep[-_ ]?cogito/i, provider: "deepcogito" }, + { pattern: /aleph[-_ .]?alpha|luminous/i, provider: "alephalpha" }, + { pattern: /nous[-_ ]?research|nous|hermes/i, provider: "nousresearch" }, + { pattern: /dolphin/i, provider: "dolphin" }, + { pattern: /dbrx/i, provider: "dbrx" }, + { pattern: /kolors/i, provider: "kolors" }, + { pattern: /light[-_ ]?ricks|ltx/i, provider: "lightricks" }, + { pattern: /novel[-_ ]?ai|nai/i, provider: "novelai" }, + { pattern: /fish[-_ .]?audio|fish[-_ .]?speech/i, provider: "fishaudio" }, + { pattern: /coqui|xtts/i, provider: "coqui" }, + { pattern: /comfy[-_ ]?ui/i, provider: "comfyui" }, + { pattern: /automatic[-_ ]?1111|a1111|webui/i, provider: "automatic" }, + { pattern: /civit[-_ ]?ai/i, provider: "civitai" }, + { pattern: /model[-_ ]?scope|魔搭/i, provider: "modelscope" }, + { pattern: /bailian|百炼/i, provider: "bailian" }, + { pattern: /x[-_ ]?inference|xinference/i, provider: "xinference" }, + { pattern: /vllm/i, provider: "vllm" }, + { pattern: /lm[-_ .]?studio/i, provider: "lmstudio" }, + { pattern: /base[-_ ]?ten/i, provider: "baseten" }, + // 补齐 + { pattern: /\baya\b/i, provider: "aya" }, + { pattern: /azure[-_ ]?ai/i, provider: "azureai" }, + { pattern: /baidu|百度/i, provider: "baidu" }, + { pattern: /\bbfl\b|black[-_ .]?forest/i, provider: "bfl" }, + { pattern: /bria[-_ ]?ai|bria/i, provider: "briaai" }, + { pattern: /burn[-_ ]?cloud/i, provider: "burncloud" }, + { pattern: /deep[-_ ]?ai/i, provider: "deepai" }, + { pattern: /deep[-_ ]?mind/i, provider: "deepmind" }, + { pattern: /essential[-_ ]?ai/i, provider: "essentialai" }, + { pattern: /glmv/i, provider: "glmv" }, + { pattern: /google[-_ ]?cloud|gcp/i, provider: "googlecloud" }, + { pattern: /hedra/i, provider: "hedra" }, + { pattern: /iflytek[-_ ]?cloud|讯飞云/i, provider: "iflytekcloud" }, + { pattern: /infermatic/i, provider: "infermatic" }, + { pattern: /infini[-_ ]?gence|无问芯穹/i, provider: "infinigence" }, + { pattern: /kwai[-_ ]?kat|可图/i, provider: "kwaikat" }, + { pattern: /kwai[-_ ]?pilot/i, provider: "kwaipilot" }, + { pattern: /llava/i, provider: "llava" }, + { pattern: /llm[-_ ]?api/i, provider: "llmapi" }, + { pattern: /long[-_ ]?cat/i, provider: "longcat" }, + { pattern: /lovart/i, provider: "lovart" }, + { pattern: /monica/i, provider: "monica" }, + { pattern: /morph/i, provider: "morph" }, + { pattern: /my[-_ ]?shell/i, provider: "myshell" }, + { pattern: /open[-_ ]?chat/i, provider: "openchat" }, + { pattern: /open[-_ .]?web[-_ ]?ui/i, provider: "openwebui" }, + { pattern: /\bpalm\b/i, provider: "palm" }, + { pattern: /phind/i, provider: "phind" }, + { pattern: /player[-_ ]?2/i, provider: "player2" }, + { pattern: /\bpoe\b/i, provider: "poe" }, + { pattern: /pollinations/i, provider: "pollinations" }, + { pattern: /ppio|派欧/i, provider: "ppio" }, + { pattern: /qing[-_ ]?yan|清言/i, provider: "qingyan" }, + { pattern: /soph[-_ ]?net/i, provider: "sophnet" }, + { pattern: /state[-_ ]?cloud/i, provider: "statecloud" }, + { pattern: /straico/i, provider: "straico" }, + { pattern: /stream[-_ ]?lake/i, provider: "streamlake" }, + { pattern: /sub[-_ ]?model/i, provider: "submodel" }, + { pattern: /\bsync\b/i, provider: "sync" }, + { pattern: /targon/i, provider: "targon" }, + { pattern: /topaz[-_ ]?labs|topaz/i, provider: "topazlabs" }, + { pattern: /turix/i, provider: "turix" }, + { pattern: /udio/i, provider: "udio" }, + { pattern: /workers[-_ ]?ai/i, provider: "workersai" }, + { pattern: /xiaomi[-_ ]?mimo|小米mimo/i, provider: "xiaomimimo" }, + { pattern: /xuan[-_ ]?yuan|轩辕/i, provider: "xuanyuan" }, + { pattern: /yandex/i, provider: "yandex" }, + { pattern: /yuan[-_ ]?bao|元宝/i, provider: "yuanbao" }, + { pattern: /zero[-_ ]?one|零一/i, provider: "zeroone" }, + { pattern: /new[-_ ]?api/i, provider: "newapi" }, + { pattern: /npl[-_ ]?cloud/i, provider: "nplcloud" }, +]; diff --git a/web-core/src/utils/scanSkills.ts b/web-core/src/utils/scanSkills.ts new file mode 100644 index 0000000..1cb6a83 --- /dev/null +++ b/web-core/src/utils/scanSkills.ts @@ -0,0 +1,97 @@ +import axios from "@/utils/axios"; +import { NotifyPlugin, Progress } from "tdesign-vue-next"; +import { h, ref } from "vue"; +import { storeToRefs } from "pinia"; +import settingStore from "@/stores/setting"; + +// 延迟函数 +function delay(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +export default async () => { + const { showSetting, activeMenu } = storeToRefs(settingStore()); + + const progressValue = ref(0); + // 显示进度通知 + const notifyInstance = NotifyPlugin.info({ + title: $t("skillScan.scanning"), + content: () => + h("div", { style: "width: 100%; padding-top: 10px;" }, [ + h(Progress, { + percentage: progressValue.value, + status: progressValue.value >= 100 ? "success" : "active", + }), + ]), + duration: 0, + closeBtn: true, + }); + try { + const [{ data }] = await Promise.all([ + axios.post("/setting/skillManagement/scanSkills"), + (async () => { + for (let i = 1; i <= 10; i++) { + await delay(100); + progressValue.value = i * 9; + } + })(), + ]); + progressValue.value = 100; + await delay(300); + NotifyPlugin.close(notifyInstance); + // 成功通知 + const changes = [ + data.insertedCount && $t("skillScan.inserted", { count: data.insertedCount }), + data.updatedCount && $t("skillScan.updated", { count: data.updatedCount }), + data.removedCount && $t("skillScan.removed", { count: data.removedCount }), + ].filter(Boolean); + NotifyPlugin.success({ + title: $t("skillScan.scanComplete"), + content: `${$t("skillScan.scannedFiles", { count: data.totalFiles })} | ${changes.join(" | ")}`, + closeBtn: true, + }); + // 警告通知 + if (data.noDescriptionSkillCount > 0 || data.noAttributionSkillCount > 0) { + const warnings = []; + if (data.noDescriptionSkillCount > 0) { + warnings.push($t("skillScan.noDescription", { count: data.noDescriptionSkillCount })); + } + if (data.noAttributionSkillCount > 0) { + warnings.push($t("skillScan.noAttribution", { count: data.noAttributionSkillCount })); + } + const warningNotifyInstance = NotifyPlugin.warning({ + title: $t("skillScan.configWarning"), + content: warnings.join(" | "), + footer: () => + h( + "div", + { style: "text-align: right; padding-top: 4px;" }, + h( + "span", + { + style: "color: #ed7b2f; font-size: 12px; cursor: pointer;", + onClick: () => { + activeMenu.value = "skillManagement"; + showSetting.value = true; + NotifyPlugin.close(warningNotifyInstance); + }, + }, + $t("skillScan.openSettings"), + ), + ), + duration: 6000, + closeBtn: true, + }); + } + } catch (error) { + NotifyPlugin.close(notifyInstance); + NotifyPlugin.error({ + title: $t("skillScan.scanFailed"), + content: $t("skillScan.checkNetwork"), + footer: () => + h("div", { style: "text-align: right; padding-top: 4px;" }, h("span", { style: "color: #e34d59; font-size: 12px;" }, $t("skillScan.retryLater"))), + duration: 3000, + closeBtn: true, + }); + } +}; diff --git a/web-core/src/utils/theme.ts b/web-core/src/utils/theme.ts new file mode 100644 index 0000000..e92d929 --- /dev/null +++ b/web-core/src/utils/theme.ts @@ -0,0 +1,155 @@ +import settingStore from "@/stores/setting"; + +// HEX 转 HSL +const hexToHsl = (hex: string) => { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + if (!result) return { h: 0, s: 0, l: 0 }; + + const r = parseInt(result[1], 16) / 255; + const g = parseInt(result[2], 16) / 255; + const b = parseInt(result[3], 16) / 255; + const max = Math.max(r, g, b), + min = Math.min(r, g, b); + const l = (max + min) / 2; + let h = 0, + s = 0; + + if (max !== min) { + const d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6; + else if (max === g) h = ((b - r) / d + 2) / 6; + else h = ((r - g) / d + 4) / 6; + } + + return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100) }; +}; + +// HSL 转 HEX +const hslToHex = (h: number, s: number, l: number) => { + s /= 100; + l /= 100; + const c = (1 - Math.abs(2 * l - 1)) * s; + const x = c * (1 - Math.abs(((h / 60) % 2) - 1)); + const m = l - c / 2; + + let r = 0, + g = 0, + b = 0; + if (h < 60) [r, g, b] = [c, x, 0]; + else if (h < 120) [r, g, b] = [x, c, 0]; + else if (h < 180) [r, g, b] = [0, c, x]; + else if (h < 240) [r, g, b] = [0, x, c]; + else if (h < 300) [r, g, b] = [x, 0, c]; + else [r, g, b] = [c, 0, x]; + + const toHex = (n: number) => + Math.round((n + m) * 255) + .toString(16) + .padStart(2, "0"); + return `#${toHex(r)}${toHex(g)}${toHex(b)}`; +}; + +// 生成品牌色阶 +const generateColorPalette = (hex: string) => { + const { h, s, l } = hexToHsl(hex); + const lightLevels = [97, 92, 85, 75, 62, l, Math.max(l - 12, 20), Math.max(l - 24, 15), Math.max(l - 36, 10), Math.max(l - 48, 5)]; + return lightLevels.map((level) => hslToHex(h, s, level)); +}; + +// 应用主题模式 +export const applyThemeMode = (mode: string) => { + const targetMode = mode === "auto" ? (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light") : mode; + + // 方式 1:使用 theme-mode 属性 + if (targetMode === "dark") { + document.documentElement.setAttribute("theme-mode", "dark"); + } else { + document.documentElement.removeAttribute("theme-mode"); + } + + // 方式 2:使用 dark 类名 + if (targetMode === "dark") { + document.documentElement.classList.add("dark"); + } else { + document.documentElement.classList.remove("dark"); + } +}; + +// 应用主题色 +export const applyThemeColor = (color: string) => { + const root = document.documentElement; + const palette = generateColorPalette(color); + const isDark = root.getAttribute("theme-mode") === "dark"; + const colors = isDark ? [...palette].reverse() : palette; + + colors.forEach((c, i) => root.style.setProperty(`--td-brand-color-${i + 1}`, c)); + + ["", "-hover:5", "-focus:2", "-active:7", "-disabled:3", "-light:1", "-light-hover:2"].forEach((suffix) => { + const [name, level] = suffix.split(":"); + root.style.setProperty(`--td-brand-color${name}`, level ? `var(--td-brand-color-${level})` : "var(--td-brand-color-6)"); + }); + + root.style.setProperty("--td-text-color-brand", `var(--td-brand-color-${isDark ? 8 : 7})`); + root.style.setProperty("--td-text-color-link", "var(--td-brand-color-8)"); +}; + +// 使用 View Transition API 进行平滑过渡 +export const toggleThemeWithTransition = (event: MouseEvent | undefined, callback: () => void) => { + if (!document.startViewTransition) { + callback(); + return; + } + + const x = event?.clientX ?? window.innerWidth / 2; + const y = event?.clientY ?? window.innerHeight / 2; + const endRadius = Math.hypot(Math.max(x, window.innerWidth - x), Math.max(y, window.innerHeight - y)); + + const root = document.documentElement; + root.style.setProperty("--x", `${x}px`); + root.style.setProperty("--y", `${y}px`); + root.style.setProperty("--r", `${endRadius}px`); + + document.startViewTransition(callback); +}; + +// 初始化主题(在 App.vue 中调用) +export const initTheme = () => { + const { themeSetting } = storeToRefs(settingStore()); + // 应用缓存的主题设置 + applyThemeMode(themeSetting.value.mode); + applyThemeColor(themeSetting.value.primaryColor); + if (themeSetting.value.fontSize) { + document.documentElement.style.fontSize = `${themeSetting.value.fontSize}px`; + } + + // 监听系统主题变化 + window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => { + if (themeSetting.value.mode === "auto") { + toggleThemeWithTransition(undefined, () => { + const targetMode = e.matches ? "dark" : "light"; + + if (targetMode === "dark") { + document.documentElement.setAttribute("theme-mode", "dark"); + document.documentElement.classList.add("dark"); + } else { + document.documentElement.removeAttribute("theme-mode"); + document.documentElement.classList.remove("dark"); + } + + applyThemeColor(themeSetting.value.primaryColor); + }); + } + }); +}; + +// 导出 composable 供组件使用 +export const useTheme = () => { + const { themeSetting } = storeToRefs(settingStore()); + return { + themeSetting, + applyThemeMode, + applyThemeColor, + toggleThemeWithTransition, + }; +}; diff --git a/web-core/src/utils/useChat copy.ts b/web-core/src/utils/useChat copy.ts new file mode 100644 index 0000000..8d43e66 --- /dev/null +++ b/web-core/src/utils/useChat copy.ts @@ -0,0 +1,726 @@ +// useChat.ts +import { ref, shallowRef, onMounted, onUnmounted, computed } from "vue"; +import { io, Socket } from "socket.io-client"; +import type { ChatMessagesData, AIMessage, UserMessage, AIMessageContent, ChatMessageStatus } from "@tdesign-vue-next/chat"; + +// Socket 事件类型定义 +export interface MessageEvent { + id: string; + role: "assistant" | "user" | "system"; + name?: string; + status: ChatMessageStatus; + datetime: string; + content: any[]; + ext?: Record; +} + +export interface MessageUpdateEvent { + id: string; + status?: ChatMessageStatus; + ext?: Record; +} + +export interface ContentAddEvent { + messageId: string; + content: AIMessageContent; +} + +export interface ContentUpdateEvent { + messageId: string; + contentId: string; + type: string; + data?: any; + strategy?: "merge" | "append"; + status?: ChatMessageStatus; +} + +export interface XmlChildItem { + tag: string; + attrs: Record; + value: string; +} + +export interface XmlTagEvent { + messageId: string; + contentId?: string; + type: "text" | "markdown"; + tag: string; + value: string; + attrs: Record; + children: XmlChildItem[]; + status: ChatMessageStatus; +} + +export interface XmlTagOption { + tag: string; + keepInMessage?: boolean; +} + +export interface ChatSocketEvents { + // 发送事件 + chat: { content: string; attachments?: any[] }; + stop: { messageId: string }; + regenerate: { messageId: string }; + + // 接收事件 + message: MessageEvent; + "message:update": MessageUpdateEvent; + "content:add": ContentAddEvent; + "content:update": ContentUpdateEvent; + error: { code: string; message: string }; +} + +export interface UseChatOptions { + url: string; + auth?: Record | (() => Record); + autoConnect?: boolean; + xmlTags?: Array; + keepXmlInMessage?: boolean; + onXmlTag?: (event: XmlTagEvent) => void; + onError?: (error: { code: string; message: string }) => void; + onConnect?: () => void; + onDisconnect?: () => void; + manageLifecycle?: boolean; +} + +export function useChat(options: UseChatOptions) { + const { url, auth, autoConnect = true, xmlTags = [], keepXmlInMessage = true, onXmlTag, onError, onConnect, onDisconnect, manageLifecycle = true } = options; + + const socket = shallowRef(null); + const connected = ref(false); + const connecting = ref(false); + const messages = ref([]); + const currentMessageId = ref(null); + const status = ref<"idle" | "pending" | "streaming">("idle"); + const xmlData = ref>({}); + const xmlDataByMessage = ref>>({}); + const normalizedXmlTagOptions = Array.from( + new Map( + xmlTags + .map((item) => (typeof item === "string" ? { tag: item } : item)) + .filter((item): item is XmlTagOption => Boolean(item?.tag)) + .map((item) => [item.tag, item]), + ).values(), + ); + const normalizedXmlTags = normalizedXmlTagOptions.map((item) => item.tag); + const hiddenXmlTags = normalizedXmlTagOptions.filter((item) => item.keepInMessage ?? keepXmlInMessage ? false : true).map((item) => item.tag); + const emittedXmlState = new Map>(); + const rawContentState = new Map(); + + // 计算属性 - 修复:增加对内容流状态的判断 + const isGenerating = computed(() => { + const lastMsg = messages.value[messages.value.length - 1]; + if (!lastMsg || lastMsg.role !== "assistant") return false; + + const status = lastMsg.status; + // pending 或 streaming 状态都算生成中 + if (status === "pending" || status === "streaming") return true; + + // 额外检查:如果消息状态是其他,但有内容块还在流式中 + const aiMsg = lastMsg as AIMessage; + if (aiMsg.content?.some((c) => c.status === "pending" || c.status === "streaming")) { + return true; + } + + return false; + }); + + const lastMessage = computed(() => messages.value[messages.value.length - 1]); + + // 工具方法 + const findMessage = (id: string): ChatMessagesData | undefined => { + return messages.value.find((m) => m.id === id); + }; + + const findMessageIndex = (id: string): number => { + return messages.value.findIndex((m) => m.id === id); + }; + + const findContent = (msg: AIMessage, contentId: string): AIMessageContent | undefined => { + return msg.content?.find((c) => c.id === contentId); + }; + + const isXmlTextContent = (content: AIMessageContent): content is Extract => { + return (content.type === "text" || content.type === "markdown") && typeof content.data === "string"; + }; + + const getContentKey = (messageId: string, content: Pick) => `${messageId}:${content.id ?? content.type}`; + + const escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + + const parseXmlAttributes = (tagStr: string): Record => { + const attrs: Record = {}; + const attrRegex = /([\w-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g; + let match: RegExpExecArray | null; + while ((match = attrRegex.exec(tagStr)) !== null) { + attrs[match[1]] = match[2] ?? match[3]; + } + return attrs; + }; + + const parseXmlChildren = (content: string): XmlChildItem[] => { + const children: XmlChildItem[] = []; + // 匹配已闭合的子元素(含自闭合标签) + const childRegex = /<(\w+)((?:\s+[\w-]+\s*=\s*(?:"[^"]*"|'[^']*'))*)\s*(?:\/>|>([\s\S]*?)<\/\1>)/g; + let match: RegExpExecArray | null; + let lastIndex = 0; + while ((match = childRegex.exec(content)) !== null) { + children.push({ + tag: match[1], + attrs: parseXmlAttributes(match[2]), + value: match[3] ?? "", + }); + lastIndex = childRegex.lastIndex; + } + // 匹配尾部未闭合的子元素(流式场景) + const remaining = content.slice(lastIndex); + const unclosedMatch = remaining.match(/<(\w+)((?:\s+[\w-]+\s*=\s*(?:"[^"]*"|'[^']*'))*)\s*>([\s\S]*)$/); + if (unclosedMatch) { + children.push({ + tag: unclosedMatch[1], + attrs: parseXmlAttributes(unclosedMatch[2]), + value: unclosedMatch[3], + }); + } + return children; + }; + + const parseXmlTag = (text: string, tag: string) => { + const escapedTag = escapeRegExp(tag); + // Match opening tag with optional attributes: or + const openRegex = new RegExp(`<${escapedTag}(\\s[^>]*)?>`, "g"); + let lastMatch: RegExpExecArray | null = null; + let m: RegExpExecArray | null; + while ((m = openRegex.exec(text)) !== null) { + lastMatch = m; + } + if (!lastMatch) return null; + + const attrs = parseXmlAttributes(lastMatch[1] ?? ""); + const contentStart = lastMatch.index + lastMatch[0].length; + const closeTag = ``; + const closeIndex = text.indexOf(closeTag, contentStart); + const isComplete = closeIndex !== -1; + const value = text.slice(contentStart, isComplete ? closeIndex : text.length).trim(); + const children = parseXmlChildren(value); + + return { + value, + attrs, + children, + isComplete, + }; + }; + + const stripXmlFromMessage = (text: string) => { + let sanitized = text; + + for (const tag of hiddenXmlTags) { + const escapedTag = escapeRegExp(tag); + // Match tags with or without attributes + sanitized = sanitized.replace(new RegExp(`<${escapedTag}(?:\\s[^>]*)?>[\\s\\S]*?<\\/${escapedTag}>`, "g"), ""); + sanitized = sanitized.replace(new RegExp(`<${escapedTag}(?:\\s[^>]*)?>[\\s\\S]*$`, "g"), ""); + } + + return sanitized; + }; + + const getRawContentData = (messageId: string, content: AIMessageContent) => { + if (!isXmlTextContent(content)) return null; + return rawContentState.get(getContentKey(messageId, content)) ?? content.data; + }; + + const syncContentDisplay = (messageId: string, content: AIMessageContent) => { + if (!isXmlTextContent(content)) return; + const rawText = getRawContentData(messageId, content) ?? ""; + content.data = hiddenXmlTags.length ? stripXmlFromMessage(rawText) : rawText; + }; + + const syncXmlData = (messageId: string, content: AIMessageContent, messageStatus?: ChatMessageStatus) => { + if (!normalizedXmlTags.length) return; + if (!isXmlTextContent(content)) return; + + const contentKey = getContentKey(messageId, content); + const prevState = emittedXmlState.get(contentKey) ?? {}; + const nextState = { ...prevState }; + const nextMessageData = { ...(xmlDataByMessage.value[messageId] ?? {}) }; + const status = content.status ?? messageStatus ?? "pending"; + const rawText = getRawContentData(messageId, content); + + if (rawText === null) return; + + let changed = false; + + for (const tag of normalizedXmlTags) { + const parsed = parseXmlTag(rawText, tag); + if (parsed === null) continue; + + const { value, isComplete } = parsed; + const eventStatus = isComplete ? status === "error" || status === "stop" ? status : "complete" : status; + + const shouldEmit = prevState[tag] !== value || eventStatus === "complete"; + if (!shouldEmit) continue; + + nextState[tag] = value; + nextMessageData[tag] = value; + xmlData.value = { ...xmlData.value, [tag]: value }; + changed = true; + + onXmlTag?.({ + messageId, + contentId: content.id, + type: content.type, + tag, + value, + attrs: parsed.attrs, + children: parsed.children, + status: eventStatus, + }); + } + + if (!changed) return; + + emittedXmlState.set(contentKey, nextState); + xmlDataByMessage.value = { + ...xmlDataByMessage.value, + [messageId]: nextMessageData, + }; + }; + + const syncMessageXmlData = (messageId: string, message: ChatMessagesData | undefined, messageStatus?: ChatMessageStatus) => { + if (!message || message.role !== "assistant") return; + + const aiMessage = message as AIMessage; + aiMessage.content?.forEach((content) => syncXmlData(messageId, content, messageStatus ?? aiMessage.status)); + }; + + // 深度合并工具 + const deepMerge = >(target: T, source: Partial): T => { + if (typeof source !== "object" || source === null) return source as T; + + const result: Record = { ...target }; + + for (const key in source) { + const sourceVal = source[key]; + const targetVal = result[key]; + + if (Array.isArray(sourceVal)) { + result[key] = [...(Array.isArray(targetVal) ? targetVal : []), ...sourceVal]; + } else if (typeof sourceVal === "object" && sourceVal !== null) { + const base = typeof targetVal === "object" && targetVal !== null ? targetVal : {}; + result[key] = deepMerge(base as Record, sourceVal as Record); + } else if (sourceVal !== undefined) { + result[key] = sourceVal; + } + } + + return result as T; + }; + + // 字符串追加处理 + const appendStringData = (content: AIMessageContent, delta: string) => { + if (typeof content.data === "string") { + content.data += delta; + } else if (typeof content.data === "object" && content.data !== null) { + if ("text" in content.data && typeof delta === "string") { + (content.data as any).text = ((content.data as any).text || "") + delta; + } + } + }; + + // 处理内容更新的核心逻辑 + const handleContentUpdate = (event: ContentUpdateEvent) => { + const { messageId, contentId, type, data, strategy, status: eventStatus } = event; + + const msg = findMessage(messageId) as AIMessage; + if (!msg || msg.role !== "assistant") return; + + const content = findContent(msg, contentId); + if (!content) return; + + // 更新内容状态 + if (eventStatus) { + content.status = eventStatus; + } + + // 关键修复:当内容块开始流式输出时,同步更新消息状态 + if (eventStatus === "streaming" || (strategy === "append" && data)) { + if (msg.status === "pending") { + msg.status = "streaming"; + } + if (currentMessageId.value === messageId) { + status.value = "streaming"; + } + } + + // 无数据时仅更新状态 + if (data === undefined || data === null) { + syncXmlData(messageId, content, msg.status); + return; + } + + // 根据策略处理数据 + if (isXmlTextContent(content) && typeof data === "string") { + const contentKey = getContentKey(messageId, content); + const previousRaw = rawContentState.get(contentKey) ?? content.data; + const nextRaw = strategy === "append" ? previousRaw + data : data; + + rawContentState.set(contentKey, nextRaw); + syncContentDisplay(messageId, content); + } else if (strategy === "append") { + if (typeof data === "string") { + appendStringData(content, data); + } else if (typeof data === "object") { + content.data = deepMerge(content.data as any, data); + } + } else { + if (typeof content.data === "object" && typeof data === "object") { + content.data = { ...content.data, ...data }; + } else { + content.data = data; + } + } + + // 流式状态(如果没有显式指定状态且是追加模式) + if (!eventStatus && strategy === "append") { + content.status = "streaming"; + } + + syncXmlData(messageId, content, msg.status); + }; + + // 消息处理器 + const setupHandlers = () => { + if (!socket.value) return; + + // 新消息 + socket.value.on("message", (data: MessageEvent) => { + const newMessage: ChatMessagesData = { + id: data.id, + role: data.role, + name: data.name, + status: data.status || "pending", + datetime: data.datetime, + content: data.content || [], + ext: data.ext, + } as ChatMessagesData; + + if (data.role === "assistant") { + const aiMessage = newMessage as AIMessage; + aiMessage.content?.forEach((content) => { + if (!isXmlTextContent(content)) return; + rawContentState.set(getContentKey(data.id, content), content.data); + syncContentDisplay(data.id, content); + }); + } + + messages.value.push(newMessage); + + if (data.role === "assistant") { + const aiMessage = newMessage as AIMessage; + aiMessage.content?.forEach((content) => syncXmlData(data.id, content, aiMessage.status)); + } + + if (data.role === "assistant") { + currentMessageId.value = data.id; + status.value = data.status === "streaming" ? "streaming" : "pending"; + } + }); + + // 消息状态更新 + socket.value.on("message:update", (data: MessageUpdateEvent) => { + const msg = findMessage(data.id); + if (!msg) return; + + if (data.status) { + msg.status = data.status; + } + + if (data.ext) { + msg.ext = { ...msg.ext, ...data.ext }; + } + + if (data.status) { + syncMessageXmlData(data.id, msg, data.status); + } + + if (data.status === "streaming") { + status.value = "streaming"; + } + + if (data.status === "complete" || data.status === "error" || data.status === "stop") { + if (currentMessageId.value === data.id) { + currentMessageId.value = null; + status.value = "idle"; + } + } + }); + + // 添加内容块 - 修复:不要在这里改变消息状态 + socket.value.on("content:add", (data: ContentAddEvent) => { + const msg = findMessage(data.messageId) as AIMessage; + if (!msg || msg.role !== "assistant") return; + + if (!msg.content) { + msg.content = []; + } + + // 确保内容块有默认状态 + const content = { + ...data.content, + status: data.content.status || "pending", + // thinking 内容块默认折叠 + ...(data.content.type === "thinking" ? { ext: { collapsed: true, ...data.content.ext } } : {}), + }; + + if (isXmlTextContent(content)) { + rawContentState.set(getContentKey(data.messageId, content), content.data); + syncContentDisplay(data.messageId, content); + } + + // thinking 内容块需要放在 content 最前面,但如果最前面已经是 thinking 则放在其后 + if (content.type === "thinking") { + const firstNonThinkingIndex = msg.content.findIndex((c: any) => c.type !== "thinking"); + if (firstNonThinkingIndex === -1) { + msg.content.push(content); + } else { + msg.content.splice(firstNonThinkingIndex, 0, content); + } + } else { + msg.content.push(content); + } + syncXmlData(data.messageId, content, msg.status); + + // 关键修复:只有当内容块状态是 streaming 时才更新消息状态 + // pending 状态的内容块表示还没有真正开始输出 + if (content.status === "streaming") { + if (msg.status === "pending") { + msg.status = "streaming"; + } + } + }); + + // 内容更新(流式/完成) + socket.value.on("content:update", handleContentUpdate); + + // 错误处理 + socket.value.on("error", (error: { code: string; message: string }) => { + console.error("[Chat Error]", error); + onError?.(error); + }); + + // 连接事件 + socket.value.on("connect", () => { + connected.value = true; + connecting.value = false; + onConnect?.(); + }); + + socket.value.on("disconnect", (reason) => { + connected.value = false; + connecting.value = false; + onDisconnect?.(); + console.log("[Chat Disconnected]", reason); + }); + + socket.value.on("connect_error", (error) => { + connected.value = false; + connecting.value = false; + console.error("[Chat Connect Error]", error); + }); + }; + + // 连接管理 + const connect = () => { + if (socket.value?.connected || connecting.value) return; + + connecting.value = true; + + if (!socket.value) { + socket.value = io(url, { + transports: ["websocket", "polling"], + reconnection: true, + reconnectionAttempts: 10, + reconnectionDelay: 1000, + reconnectionDelayMax: 5000, + timeout: 10000, + auth: { token: localStorage.getItem("token"), ...(typeof auth === 'function' ? auth() : auth) }, + }); + + setupHandlers(); + } else { + socket.value.connect(); + } + }; + + const disconnect = () => { + socket.value?.disconnect(); + connected.value = false; + connecting.value = false; + }; + + const reconnect = () => { + disconnect(); + setTimeout(connect, 100); + }; + + // 发送方法 + const emit = (event: E, data?: ChatSocketEvents[E]) => { + if (!socket.value?.connected) { + console.warn("[Chat] Socket not connected"); + return false; + } + socket.value.emit(event, data); + return true; + }; + + // 监听方法 + const on = (event: E, callback: (data: ChatSocketEvents[E]) => void) => { + socket.value?.on(event, callback as any); + return () => socket.value?.off(event, callback as any); + }; + + const once = (event: E, callback: (data: ChatSocketEvents[E]) => void) => { + socket.value?.once(event, callback as any); + }; + + const off = (event: E, callback?: (data: ChatSocketEvents[E]) => void) => { + socket.value?.off(event, callback as any); + }; + + // 业务方法 + const chat = (content: string, attachments?: any[]) => { + if (!content.trim() && !attachments?.length) return false; + + const userMessage: UserMessage = { + id: `user_${Date.now()}`, + role: "user", + status: "complete", + datetime: new Date().toISOString(), + content: [{ type: "text", data: content, status: "complete" }], + }; + + if (attachments?.length) { + userMessage.content.push({ + type: "attachment", + data: attachments, + status: "complete", + }); + } + + messages.value.push(userMessage); + + return emit("chat", { content, attachments }); + }; + + const stopGenerate = (messageId?: string) => { + const id = messageId || currentMessageId.value; + if (!id) return false; + + // 立即更新本地状态,不等服务端响应 + const msg = findMessage(id); + if (msg) { + msg.status = "stop"; + } + currentMessageId.value = null; + status.value = "idle"; + + return emit("stop", { messageId: id }); + }; + + const regenerate = (messageId: string) => { + return emit("regenerate", { messageId }); + }; + + // 消息管理 + const clearMessages = () => { + messages.value = []; + currentMessageId.value = null; + status.value = "idle"; + xmlData.value = {}; + xmlDataByMessage.value = {}; + emittedXmlState.clear(); + rawContentState.clear(); + }; + + const removeMessage = (id: string) => { + const idx = findMessageIndex(id); + if (idx > -1) { + const msg = messages.value[idx] as AIMessage | undefined; + msg?.content?.forEach((content) => { + emittedXmlState.delete(getContentKey(id, content)); + rawContentState.delete(getContentKey(id, content)); + }); + const nextByMessage = { ...xmlDataByMessage.value }; + delete nextByMessage[id]; + xmlDataByMessage.value = nextByMessage; + messages.value.splice(idx, 1); + } + }; + + const removeMessagesAfter = (id: string) => { + const idx = findMessageIndex(id); + if (idx > -1) { + messages.value.splice(idx + 1); + } + }; + + const updateMessage = (id: string, updates: Partial) => { + const msg = findMessage(id); + if (msg) { + Object.assign(msg, updates); + } + }; + + const getContentByType = (messageId: string, type: T): Extract[] => { + const msg = findMessage(messageId) as AIMessage; + if (!msg || msg.role !== "assistant") return []; + return (msg.content?.filter((c) => c.type === type) || []) as Extract[]; + }; + + // 生命周期 + if (manageLifecycle) { + onMounted(() => { + if (autoConnect) connect(); + }); + + onUnmounted(() => { + disconnect(); + socket.value?.removeAllListeners(); + socket.value = null; + }); + } else if (autoConnect) { + connect(); + } + + return { + socket, + connected, + connecting, + status, + messages, + currentMessageId, + xmlData, + xmlDataByMessage, + isGenerating, + lastMessage, + connect, + disconnect, + reconnect, + emit, + on, + once, + off, + chat, + stopGenerate, + regenerate, + clearMessages, + removeMessage, + removeMessagesAfter, + updateMessage, + findMessage, + getContentByType, + }; +} + +export type UseChatReturn = ReturnType; diff --git a/web-core/src/utils/useChat.ts b/web-core/src/utils/useChat.ts new file mode 100644 index 0000000..9061165 --- /dev/null +++ b/web-core/src/utils/useChat.ts @@ -0,0 +1,763 @@ +// useChat.ts +import { ref, shallowRef, onMounted, onUnmounted, computed } from "vue"; +import { io, Socket } from "socket.io-client"; +import type { ChatMessagesData, AIMessage, UserMessage, AIMessageContent, ChatMessageStatus } from "@tdesign-vue-next/chat"; + +// Socket 事件类型定义 +export interface MessageEvent { + id: string; + role: "assistant" | "user" | "system"; + name?: string; + status: ChatMessageStatus; + datetime: string; + content: any[]; + ext?: Record; +} + +export interface MessageUpdateEvent { + id: string; + status?: ChatMessageStatus; + ext?: Record; +} + +export interface ContentAddEvent { + messageId: string; + content: AIMessageContent; +} + +export interface ContentUpdateEvent { + messageId: string; + contentId: string; + type: string; + data?: any; + strategy?: "merge" | "append"; + status?: ChatMessageStatus; +} + +export interface XmlChildItem { + tag: string; + attrs: Record; + value: string; +} + +export interface XmlTagEvent { + messageId: string; + contentId?: string; + type: "text" | "markdown"; + tag: string; + value: string; + attrs: Record; + children: XmlChildItem[]; + status: ChatMessageStatus; +} + +export interface XmlTagOption { + tag: string; + keepInMessage?: boolean; +} + +export interface ChatSocketEvents { + // 发送事件 + chat: { content: string; attachments?: any[] }; + stop: { messageId: string }; + regenerate: { messageId: string }; + + // 接收事件 + message: MessageEvent; + "message:update": MessageUpdateEvent; + "content:add": ContentAddEvent; + "content:update": ContentUpdateEvent; + error: { code: string; message: string }; +} + +export interface UseChatOptions { + url: string; + auth?: Record | (() => Record); + autoConnect?: boolean; + xmlTags?: Array; + keepXmlInMessage?: boolean; + onXmlTag?: (event: XmlTagEvent) => void; + onError?: (error: { code: string; message: string }) => void; + onConnect?: () => void; + onDisconnect?: () => void; + manageLifecycle?: boolean; +} + +export function useChat(options: UseChatOptions) { + const { + url, + auth, + autoConnect = true, + xmlTags = [], + keepXmlInMessage = true, + onXmlTag, + onError, + onConnect, + onDisconnect, + manageLifecycle = true, + } = options; + + const socket = shallowRef(null); + const connected = ref(false); + const connecting = ref(false); + const messages = ref([]); + const currentMessageId = ref(null); + const status = ref<"idle" | "pending" | "streaming">("idle"); + const xmlData = ref>({}); + const xmlDataByMessage = ref>>({}); + const normalizedXmlTagOptions = Array.from( + new Map( + xmlTags + .map((item) => (typeof item === "string" ? { tag: item } : item)) + .filter((item): item is XmlTagOption => Boolean(item?.tag)) + .map((item) => [item.tag, item]), + ).values(), + ); + const normalizedXmlTags = normalizedXmlTagOptions.map((item) => item.tag); + const hiddenXmlTags = normalizedXmlTagOptions.filter((item) => ((item.keepInMessage ?? keepXmlInMessage) ? false : true)).map((item) => item.tag); + const emittedXmlState = new Map>(); + const rawContentState = new Map(); + + // 计算属性 - 修复:增加对内容流状态的判断 + const isGenerating = computed(() => { + const lastMsg = messages.value[messages.value.length - 1]; + if (!lastMsg || lastMsg.role !== "assistant") return false; + + const status = lastMsg.status; + // pending 或 streaming 状态都算生成中 + if (status === "pending" || status === "streaming") return true; + + // 额外检查:如果消息状态是其他,但有内容块还在流式中 + const aiMsg = lastMsg as AIMessage; + if (aiMsg.content?.some((c) => c.status === "pending" || c.status === "streaming")) { + return true; + } + + return false; + }); + + const lastMessage = computed(() => messages.value[messages.value.length - 1]); + + // 工具方法 + const findMessage = (id: string): ChatMessagesData | undefined => { + return messages.value.find((m) => m.id === id); + }; + + const findMessageIndex = (id: string): number => { + return messages.value.findIndex((m) => m.id === id); + }; + + const findContent = (msg: AIMessage, contentId: string): AIMessageContent | undefined => { + return msg.content?.find((c) => c.id === contentId); + }; + + const isEmptyMessageContent = (msg: ChatMessagesData | undefined): boolean => { + if (!msg || msg.role !== "assistant") return false; + + const aiMsg = msg as AIMessage; + if (!aiMsg.content || aiMsg.content.length === 0) return true; + + return aiMsg.content.every((item) => { + if (item.data === null || item.data === undefined) return true; + if (typeof item.data === "string") return item.data.trim() === ""; + return false; + }); + }; + + const isXmlTextContent = (content: AIMessageContent): content is Extract => { + return (content.type === "text" || content.type === "markdown") && typeof content.data === "string"; + }; + + const getContentKey = (messageId: string, content: Pick) => `${messageId}:${content.id ?? content.type}`; + + const escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + + const parseXmlAttributes = (tagStr: string): Record => { + const attrs: Record = {}; + const attrRegex = /([\w-]+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g; + let match: RegExpExecArray | null; + while ((match = attrRegex.exec(tagStr)) !== null) { + attrs[match[1]] = match[2] ?? match[3]; + } + return attrs; + }; + + const parseXmlChildren = (content: string): XmlChildItem[] => { + const children: XmlChildItem[] = []; + // 匹配已闭合的子元素(含自闭合标签) + const childRegex = /<(\w+)((?:\s+[\w-]+\s*=\s*(?:"[^"]*"|'[^']*'))*)\s*(?:\/>|>([\s\S]*?)<\/\1>)/g; + let match: RegExpExecArray | null; + let lastIndex = 0; + while ((match = childRegex.exec(content)) !== null) { + children.push({ + tag: match[1], + attrs: parseXmlAttributes(match[2]), + value: match[3] ?? "", + }); + lastIndex = childRegex.lastIndex; + } + // 匹配尾部未闭合的子元素(流式场景) + const remaining = content.slice(lastIndex); + const unclosedMatch = remaining.match(/<(\w+)((?:\s+[\w-]+\s*=\s*(?:"[^"]*"|'[^']*'))*)\s*>([\s\S]*)$/); + if (unclosedMatch) { + children.push({ + tag: unclosedMatch[1], + attrs: parseXmlAttributes(unclosedMatch[2]), + value: unclosedMatch[3], + }); + } + return children; + }; + + const parseXmlTag = (text: string, tag: string) => { + const escapedTag = escapeRegExp(tag); + // Match opening tag with optional attributes: or + const openRegex = new RegExp(`<${escapedTag}(\\s[^>]*)?>`, "g"); + let lastMatch: RegExpExecArray | null = null; + let m: RegExpExecArray | null; + while ((m = openRegex.exec(text)) !== null) { + lastMatch = m; + } + if (!lastMatch) return null; + + const attrs = parseXmlAttributes(lastMatch[1] ?? ""); + const contentStart = lastMatch.index + lastMatch[0].length; + const closeTag = ``; + const closeIndex = text.indexOf(closeTag, contentStart); + const isComplete = closeIndex !== -1; + const value = text.slice(contentStart, isComplete ? closeIndex : text.length).trim(); + const children = parseXmlChildren(value); + + return { + value, + attrs, + children, + isComplete, + }; + }; + + const stripXmlFromMessage = (text: string) => { + let sanitized = text; + + for (const tag of hiddenXmlTags) { + const escapedTag = escapeRegExp(tag); + // Match tags with or without attributes + sanitized = sanitized.replace(new RegExp(`<${escapedTag}(?:\\s[^>]*)?>[\\s\\S]*?<\\/${escapedTag}>`, "g"), ""); + sanitized = sanitized.replace(new RegExp(`<${escapedTag}(?:\\s[^>]*)?>[\\s\\S]*$`, "g"), ""); + } + + return sanitized; + }; + + const getRawContentData = (messageId: string, content: AIMessageContent) => { + if (!isXmlTextContent(content)) return null; + return rawContentState.get(getContentKey(messageId, content)) ?? content.data; + }; + + const syncContentDisplay = (messageId: string, content: AIMessageContent) => { + if (!isXmlTextContent(content)) return; + const rawText = getRawContentData(messageId, content) ?? ""; + content.data = hiddenXmlTags.length ? stripXmlFromMessage(rawText) : rawText; + }; + + const syncXmlData = (messageId: string, content: AIMessageContent, messageStatus?: ChatMessageStatus) => { + if (!normalizedXmlTags.length) return; + if (!isXmlTextContent(content)) return; + + const contentKey = getContentKey(messageId, content); + const prevState = emittedXmlState.get(contentKey) ?? {}; + const nextState = { ...prevState }; + const nextMessageData = { ...(xmlDataByMessage.value[messageId] ?? {}) }; + const status = content.status ?? messageStatus ?? "pending"; + const rawText = getRawContentData(messageId, content); + + if (rawText === null) return; + + let changed = false; + + for (const tag of normalizedXmlTags) { + const parsed = parseXmlTag(rawText, tag); + if (parsed === null) continue; + + const { value, isComplete } = parsed; + const eventStatus = isComplete ? (status === "error" || status === "stop" ? status : "complete") : status; + + const shouldEmit = prevState[tag] !== value || eventStatus === "complete"; + if (!shouldEmit) continue; + + nextState[tag] = value; + nextMessageData[tag] = value; + xmlData.value = { ...xmlData.value, [tag]: value }; + changed = true; + + onXmlTag?.({ + messageId, + contentId: content.id, + type: content.type, + tag, + value, + attrs: parsed.attrs, + children: parsed.children, + status: eventStatus, + }); + } + + if (!changed) return; + + emittedXmlState.set(contentKey, nextState); + xmlDataByMessage.value = { + ...xmlDataByMessage.value, + [messageId]: nextMessageData, + }; + }; + + const syncMessageXmlData = (messageId: string, message: ChatMessagesData | undefined, messageStatus?: ChatMessageStatus) => { + if (!message || message.role !== "assistant") return; + + const aiMessage = message as AIMessage; + aiMessage.content?.forEach((content) => syncXmlData(messageId, content, messageStatus ?? aiMessage.status)); + }; + + // 深度合并工具 + const deepMerge = >(target: T, source: Partial): T => { + if (typeof source !== "object" || source === null) return source as T; + + const result: Record = { ...target }; + + for (const key in source) { + const sourceVal = source[key]; + const targetVal = result[key]; + + if (Array.isArray(sourceVal)) { + result[key] = [...(Array.isArray(targetVal) ? targetVal : []), ...sourceVal]; + } else if (typeof sourceVal === "object" && sourceVal !== null) { + const base = typeof targetVal === "object" && targetVal !== null ? targetVal : {}; + result[key] = deepMerge(base as Record, sourceVal as Record); + } else if (sourceVal !== undefined) { + result[key] = sourceVal; + } + } + + return result as T; + }; + + // 字符串追加处理 + const appendStringData = (content: AIMessageContent, delta: string) => { + if (typeof content.data === "string") { + content.data += delta; + } else if (typeof content.data === "object" && content.data !== null) { + if ("text" in content.data && typeof delta === "string") { + (content.data as any).text = ((content.data as any).text || "") + delta; + } + } + }; + + // 处理内容更新的核心逻辑 + const handleContentUpdate = (event: ContentUpdateEvent) => { + const { messageId, contentId, type, data, strategy, status: eventStatus } = event; + + const msg = findMessage(messageId) as AIMessage; + if (!msg || msg.role !== "assistant") return; + + const content = findContent(msg, contentId); + if (!content) return; + + // 更新内容状态 + if (eventStatus) { + content.status = eventStatus; + } + + // 关键修复:当内容块开始流式输出时,同步更新消息状态 + if (eventStatus === "streaming" || (strategy === "append" && data)) { + if (msg.status === "pending") { + msg.status = "streaming"; + } + if (currentMessageId.value === messageId) { + status.value = "streaming"; + } + } + + // 无数据时仅更新状态 + if (data === undefined || data === null) { + syncXmlData(messageId, content, msg.status); + return; + } + + // 根据策略处理数据 + if (isXmlTextContent(content) && typeof data === "string") { + const contentKey = getContentKey(messageId, content); + const previousRaw = rawContentState.get(contentKey) ?? content.data; + const nextRaw = strategy === "append" ? previousRaw + data : data; + + rawContentState.set(contentKey, nextRaw); + syncContentDisplay(messageId, content); + } else if (strategy === "append") { + if (typeof data === "string") { + appendStringData(content, data); + } else if (typeof data === "object") { + content.data = deepMerge(content.data as any, data); + } + } else { + if (typeof content.data === "object" && typeof data === "object") { + content.data = { ...content.data, ...data }; + } else { + content.data = data; + } + } + + // 流式状态(如果没有显式指定状态且是追加模式) + if (!eventStatus && strategy === "append") { + content.status = "streaming"; + } + + syncXmlData(messageId, content, msg.status); + }; + + // 消息处理器 + const setupHandlers = () => { + if (!socket.value) return; + + // 新消息 + socket.value.on("message", (data: MessageEvent) => { + const newMessage: ChatMessagesData = { + id: data.id, + role: data.role, + name: data.name, + status: data.status || "pending", + datetime: data.datetime, + content: data.content || [], + ext: data.ext, + } as ChatMessagesData; + + if (newMessage.status === "complete" && isEmptyMessageContent(newMessage)) { + return; + } + + if (data.role === "assistant") { + const aiMessage = newMessage as AIMessage; + aiMessage.content?.forEach((content) => { + if (!isXmlTextContent(content)) return; + rawContentState.set(getContentKey(data.id, content), content.data); + syncContentDisplay(data.id, content); + }); + } + + messages.value.push(newMessage); + + if (data.role === "assistant") { + const aiMessage = newMessage as AIMessage; + aiMessage.content?.forEach((content) => syncXmlData(data.id, content, aiMessage.status)); + } + + if (data.role === "assistant") { + currentMessageId.value = data.id; + status.value = data.status === "streaming" ? "streaming" : "pending"; + } + }); + + // 消息状态更新 + socket.value.on("message:update", (data: MessageUpdateEvent) => { + const msg = findMessage(data.id); + if (!msg) return; + + if (data.status) { + msg.status = data.status; + } + + if (data.ext) { + msg.ext = { ...msg.ext, ...data.ext }; + } + + if (data.status) { + syncMessageXmlData(data.id, msg, data.status); + } + + if (data.status === "complete" && isEmptyMessageContent(msg)) { + removeMessage(data.id); + if (currentMessageId.value === data.id) { + currentMessageId.value = null; + status.value = "idle"; + } + return; + } + + if (data.status === "streaming") { + status.value = "streaming"; + } + + if (data.status === "complete" || data.status === "error" || data.status === "stop") { + if (currentMessageId.value === data.id) { + currentMessageId.value = null; + status.value = "idle"; + } + } + }); + + // 添加内容块 - 修复:不要在这里改变消息状态 + socket.value.on("content:add", (data: ContentAddEvent) => { + const msg = findMessage(data.messageId) as AIMessage; + if (!msg || msg.role !== "assistant") return; + + if (!msg.content) { + msg.content = []; + } + + // 确保内容块有默认状态 + const content = { + ...data.content, + status: data.content.status || "pending", + // thinking 内容块默认折叠 + ...(data.content.type === "thinking" ? { ext: { collapsed: true, ...data.content.ext } } : {}), + }; + + if (isXmlTextContent(content)) { + rawContentState.set(getContentKey(data.messageId, content), content.data); + syncContentDisplay(data.messageId, content); + } + + // thinking 内容块需要放在 content 最前面,但如果最前面已经是 thinking 则放在其后 + if (content.type === "thinking") { + const firstNonThinkingIndex = msg.content.findIndex((c: any) => c.type !== "thinking"); + if (firstNonThinkingIndex === -1) { + msg.content.push(content); + } else { + msg.content.splice(firstNonThinkingIndex, 0, content); + } + } else { + msg.content.push(content); + } + syncXmlData(data.messageId, content, msg.status); + + // 关键修复:只有当内容块状态是 streaming 时才更新消息状态 + // pending 状态的内容块表示还没有真正开始输出 + if (content.status === "streaming") { + if (msg.status === "pending") { + msg.status = "streaming"; + } + } + }); + + // 内容更新(流式/完成) + socket.value.on("content:update", handleContentUpdate); + + // 错误处理 + socket.value.on("error", (error: { code: string; message: string }) => { + console.error("[Chat Error]", error); + onError?.(error); + }); + + // 连接事件 + socket.value.on("connect", () => { + connected.value = true; + connecting.value = false; + onConnect?.(); + }); + + socket.value.on("disconnect", (reason) => { + connected.value = false; + connecting.value = false; + onDisconnect?.(); + console.log("[Chat Disconnected]", reason); + }); + + socket.value.on("connect_error", (error) => { + connected.value = false; + connecting.value = false; + console.error("[Chat Connect Error]", error); + }); + }; + + // 连接管理 + const connect = () => { + if (socket.value?.connected || connecting.value) return; + + connecting.value = true; + + if (!socket.value) { + socket.value = io(url, { + transports: ["websocket", "polling"], + reconnection: true, + reconnectionAttempts: 10, + reconnectionDelay: 1000, + reconnectionDelayMax: 5000, + timeout: 10000, + auth: { token: localStorage.getItem("token"), ...(typeof auth === "function" ? auth() : auth) }, + }); + + setupHandlers(); + } else { + socket.value.connect(); + } + }; + + const disconnect = () => { + socket.value?.disconnect(); + connected.value = false; + connecting.value = false; + }; + + const reconnect = () => { + disconnect(); + setTimeout(connect, 100); + }; + + // 发送方法 + const emit = (event: E, data?: ChatSocketEvents[E]) => { + if (!socket.value?.connected) { + console.warn("[Chat] Socket not connected"); + return false; + } + socket.value.emit(event, data); + return true; + }; + + // 监听方法 + const on = (event: E, callback: (data: ChatSocketEvents[E]) => void) => { + socket.value?.on(event, callback as any); + return () => socket.value?.off(event, callback as any); + }; + + const once = (event: E, callback: (data: ChatSocketEvents[E]) => void) => { + socket.value?.once(event, callback as any); + }; + + const off = (event: E, callback?: (data: ChatSocketEvents[E]) => void) => { + socket.value?.off(event, callback as any); + }; + + // 业务方法 + const chat = (content: string, attachments?: any[]) => { + if (!content.trim() && !attachments?.length) return false; + + const userMessage: UserMessage = { + id: `user_${Date.now()}`, + role: "user", + status: "complete", + datetime: new Date().toISOString(), + content: [{ type: "text", data: content, status: "complete" }], + }; + + if (attachments?.length) { + userMessage.content.push({ + type: "attachment", + data: attachments, + status: "complete", + }); + } + + messages.value.push(userMessage); + + return emit("chat", { content, attachments }); + }; + + const stopGenerate = (messageId?: string) => { + const id = messageId || currentMessageId.value; + if (!id) return false; + + // 立即更新本地状态,不等服务端响应 + const msg = findMessage(id); + if (msg) { + msg.status = "stop"; + } + currentMessageId.value = null; + status.value = "idle"; + + return emit("stop", { messageId: id }); + }; + + const regenerate = (messageId: string) => { + return emit("regenerate", { messageId }); + }; + + // 消息管理 + const clearMessages = () => { + messages.value = []; + currentMessageId.value = null; + status.value = "idle"; + xmlData.value = {}; + xmlDataByMessage.value = {}; + emittedXmlState.clear(); + rawContentState.clear(); + }; + + const removeMessage = (id: string) => { + const idx = findMessageIndex(id); + if (idx > -1) { + const msg = messages.value[idx] as AIMessage | undefined; + msg?.content?.forEach((content) => { + emittedXmlState.delete(getContentKey(id, content)); + rawContentState.delete(getContentKey(id, content)); + }); + const nextByMessage = { ...xmlDataByMessage.value }; + delete nextByMessage[id]; + xmlDataByMessage.value = nextByMessage; + messages.value.splice(idx, 1); + } + }; + + const removeMessagesAfter = (id: string) => { + const idx = findMessageIndex(id); + if (idx > -1) { + messages.value.splice(idx + 1); + } + }; + + const updateMessage = (id: string, updates: Partial) => { + const msg = findMessage(id); + if (msg) { + Object.assign(msg, updates); + } + }; + + const getContentByType = (messageId: string, type: T): Extract[] => { + const msg = findMessage(messageId) as AIMessage; + if (!msg || msg.role !== "assistant") return []; + return (msg.content?.filter((c) => c.type === type) || []) as Extract[]; + }; + + // 生命周期 + if (manageLifecycle) { + onMounted(() => { + if (autoConnect) connect(); + }); + + onUnmounted(() => { + disconnect(); + socket.value?.removeAllListeners(); + socket.value = null; + }); + } else if (autoConnect) { + connect(); + } + + return { + socket, + connected, + connecting, + status, + messages, + currentMessageId, + xmlData, + xmlDataByMessage, + isGenerating, + lastMessage, + connect, + disconnect, + reconnect, + emit, + on, + once, + off, + chat, + stopGenerate, + regenerate, + clearMessages, + removeMessage, + removeMessagesAfter, + updateMessage, + findMessage, + getContentByType, + }; +} + +export type UseChatReturn = ReturnType; diff --git a/web-core/src/utils/useSocket.ts b/web-core/src/utils/useSocket.ts new file mode 100644 index 0000000..2d5ba98 --- /dev/null +++ b/web-core/src/utils/useSocket.ts @@ -0,0 +1,58 @@ +import { ref } from "vue"; +import { io, Socket } from "socket.io-client"; +import { getDefaultSocketBaseUrl } from "@/utils/apiBaseUrl"; + +export interface SocketEventMap { + //scriptAgent + getPlanData: (data: any, callback: (response: any) => void) => void; + setPlanData: (data: any) => void; + //productionAgent + getFlowData: (data: any, callback: (response: any) => void) => void; + setFlowData: (data: any) => void; + //公共 + message: string; + setKeyScript: { scriptId: number; key: string }; + stop: string; + textMessage: { type: "start" | "content" | "end"; messageId: string; delta: string | null; role: "assistant"; name: string }; + systemMessage: { messageId: string; content: string }; + thinkMessage: { type: "start" | "content" | "end"; messageId: string; delta: string | null; role: "assistant"; name: string }; +} + +export function useSocket(url = getDefaultSocketBaseUrl(), authOptions?: Record) { + let socket: Socket | null = null; + const connected = ref(false); + + const connect = () => { + if (socket) { + if (socket.disconnected) socket.connect(); + return; + } + + socket = io(url, { + transports: ["websocket", "polling"], + reconnection: true, + reconnectionAttempts: 10, + reconnectionDelay: 1000, + reconnectionDelayMax: 5000, + timeout: 10000, + auth: { token: localStorage.getItem("token"), ...authOptions }, + }); + + socket.on("connect", () => (connected.value = true)); + socket.on("disconnect", () => (connected.value = false)); + socket.on("connect_error", () => (connected.value = false)); + }; + + const disconnect = () => { + socket?.disconnect(); + connected.value = false; + }; + + const send = (event: E, ...args: T[E] extends void ? [] : [T[E]]) => socket?.emit(event, ...args); + const on = (event: E, callback: T[E] extends (...args: any[]) => any ? T[E] : (data: T[E]) => void) => + socket?.on(event, callback as any); + const off = (event: E, callback?: T[E] extends (...args: any[]) => any ? T[E] : (data: T[E]) => void) => + socket?.off(event, callback as any); + + return { connected, socket: { connect, disconnect, send, on, off } }; +} diff --git a/web-core/src/utils/videoPolling.ts b/web-core/src/utils/videoPolling.ts new file mode 100644 index 0000000..b0fd0b5 --- /dev/null +++ b/web-core/src/utils/videoPolling.ts @@ -0,0 +1,211 @@ +import axios from "@/utils/axios"; + +/** 轮询返回的单条视频记录 */ +export interface PollingVideoRecord { + id: number | string; + storyboardId: number | string; + filePath: string; + state: string; + errorReason?: string; +} + +/** 轮询配置选项 */ +export interface VideoPollingOptions { + /** 轮询间隔(毫秒),默认 5000 */ + interval?: number; + /** 最大连续失败次数,超过后自动停止,默认 10 */ + maxErrors?: number; + /** 错误退避最大延迟(毫秒),默认 30000 */ + maxBackoffDelay?: number; + /** 每次轮询结果回调 */ + onData?: (records: PollingVideoRecord[]) => void; + /** 全部完成回调(pendingIds 清空时触发) */ + onComplete?: () => void; + /** 轮询出错回调 */ + onError?: (error: any, errorCount: number) => void; + /** 连续失败次数过多,自动停止时的回调 */ + onMaxErrors?: () => void; +} + +/** + * 创建一个视频轮询实例,用于定期查询生成中的视频状态。 + * + * @param scriptId 脚本 ID + * @param options 轮询配置 + * + * @example + * ```ts + * const poller = createVideoPolling(scriptId, { + * onData(records) { + * // 处理轮询返回的视频记录,更新列表状态 + * }, + * onComplete() { + * // 所有视频已完成 + * }, + * }); + * + * // 添加待轮询的视频 ID + * poller.addIds([101, 102]); + * + * // 启动轮询 + * poller.start(); + * + * // 组件卸载时销毁 + * poller.destroy(); + * ``` + */ +export function createVideoPolling(id: number | string, options: VideoPollingOptions = {}) { + const { + interval = 5000, + maxErrors = 10, + maxBackoffDelay = 30000, + onData, + onComplete, + onError, + onMaxErrors, + } = options; + + /** 待轮询的视频 ID 列表 */ + let pendingIds: Array = []; + /** 定时器句柄 */ + let timer: number | null = null; + /** 是否正在执行轮询请求(防止并发重叠) */ + let inFlight = false; + /** 连续失败计数 */ + let errorCount = 0; + /** 用于取消进行中的请求 */ + let abortController: AbortController | null = null; + /** 是否已销毁 */ + let destroyed = false; + + /** 执行一次轮询请求 */ + async function poll() { + if (destroyed || pendingIds.length === 0 || inFlight) return; + + inFlight = true; + try { + abortController = new AbortController(); + const { data } = await axios.post( + "/production/workbench/videoPolling", + { id, specifyIds: [...pendingIds] }, + { signal: abortController.signal }, + ); + + if (destroyed) return; + errorCount = 0; + + if (!data || data.length === 0) { + // 后端无记录,清空待轮询列表 + pendingIds = []; + onComplete?.(); + stop(); + return; + } + + const records = data as PollingVideoRecord[]; + onData?.(records); + + // 移除已完成(成功或失败)的 ID + const doneIds = records + .filter((r) => r.state === "生成成功" || r.state === "生成失败") + .map((r) => r.id); + if (doneIds.length > 0) { + pendingIds = pendingIds.filter((id) => !doneIds.includes(id)); + } + // 后端未返回的 ID 视为已失效,一并移除 + const returnedIds = records.map((r) => r.id); + pendingIds = pendingIds.filter((id) => returnedIds.includes(id)); + + if (pendingIds.length === 0) { + onComplete?.(); + stop(); + } + } catch (error: any) { + if (error?.name === "CanceledError" || error?.code === "ERR_CANCELED") return; + errorCount++; + onError?.(error, errorCount); + if (errorCount >= maxErrors) { + onMaxErrors?.(); + stop(); + return; + } + } finally { + abortController = null; + inFlight = false; + } + } + + /** 安排下一次轮询 */ + function scheduleNext(delay: number) { + if (destroyed || pendingIds.length === 0) return; + timer = window.setTimeout(async () => { + timer = null; + await poll(); + // 轮询完成后若仍有待查询 ID,继续安排下一次 + if (pendingIds.length > 0 && !destroyed) { + const nextDelay = errorCount > 0 + ? Math.min(interval * Math.pow(2, errorCount - 1), maxBackoffDelay) + : interval; + scheduleNext(nextDelay); + } + }, delay); + } + + /** 启动轮询。force=true 时会重置错误计数并重新开始 */ + function start(force = false) { + if (destroyed) return; + if (timer) { + if (!force) return; + stop(); + } + if (pendingIds.length === 0) return; + errorCount = 0; + // 立即执行第一次 + scheduleNext(0); + } + + /** 停止轮询 */ + function stop() { + if (timer) { + clearTimeout(timer); + timer = null; + } + if (abortController) { + abortController.abort(); + abortController = null; + } + inFlight = false; + } + + /** 添加待轮询的视频 ID(自动去重) */ + function addIds(ids: Array) { + const set = new Set([...pendingIds, ...ids]); + pendingIds = [...set]; + } + + /** 获取当前待轮询的 ID 列表 */ + function getPendingIds(): Array { + return [...pendingIds]; + } + + /** 是否正在轮询中 */ + function isPolling(): boolean { + return timer !== null || inFlight; + } + + /** 销毁实例,停止轮询并清理所有状态 */ + function destroy() { + destroyed = true; + stop(); + pendingIds = []; + } + + return { + start, + stop, + addIds, + getPendingIds, + isPolling, + destroy, + }; +} diff --git a/web-core/src/views/assets/components/addAssets.vue b/web-core/src/views/assets/components/addAssets.vue new file mode 100644 index 0000000..305ce58 --- /dev/null +++ b/web-core/src/views/assets/components/addAssets.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/web-core/src/views/assets/components/addAudioAssets.vue b/web-core/src/views/assets/components/addAudioAssets.vue new file mode 100644 index 0000000..fedc319 --- /dev/null +++ b/web-core/src/views/assets/components/addAudioAssets.vue @@ -0,0 +1,336 @@ + + + + + diff --git a/web-core/src/views/assets/components/batchGeneration.vue b/web-core/src/views/assets/components/batchGeneration.vue new file mode 100644 index 0000000..08843d7 --- /dev/null +++ b/web-core/src/views/assets/components/batchGeneration.vue @@ -0,0 +1,554 @@ + + + + + diff --git a/web-core/src/views/assets/components/generateImage.vue b/web-core/src/views/assets/components/generateImage.vue new file mode 100644 index 0000000..0e9a2e5 --- /dev/null +++ b/web-core/src/views/assets/components/generateImage.vue @@ -0,0 +1,466 @@ + + + + + diff --git a/web-core/src/views/assets/index.vue b/web-core/src/views/assets/index.vue new file mode 100644 index 0000000..46b8a8f --- /dev/null +++ b/web-core/src/views/assets/index.vue @@ -0,0 +1,1697 @@ + + + + + + + diff --git a/web-core/src/views/cornerScape/index.vue b/web-core/src/views/cornerScape/index.vue new file mode 100644 index 0000000..44edb49 --- /dev/null +++ b/web-core/src/views/cornerScape/index.vue @@ -0,0 +1,1345 @@ + + + + + diff --git a/web-core/src/views/novel/components/editNodel.vue b/web-core/src/views/novel/components/editNodel.vue new file mode 100644 index 0000000..81ee1fe --- /dev/null +++ b/web-core/src/views/novel/components/editNodel.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/web-core/src/views/novel/components/event.vue b/web-core/src/views/novel/components/event.vue new file mode 100644 index 0000000..0359b92 --- /dev/null +++ b/web-core/src/views/novel/components/event.vue @@ -0,0 +1,206 @@ + + + + + diff --git a/web-core/src/views/novel/components/eventAnalysis.vue b/web-core/src/views/novel/components/eventAnalysis.vue new file mode 100644 index 0000000..e315796 --- /dev/null +++ b/web-core/src/views/novel/components/eventAnalysis.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/web-core/src/views/novel/components/importNovel.vue b/web-core/src/views/novel/components/importNovel.vue new file mode 100644 index 0000000..6cad8e5 --- /dev/null +++ b/web-core/src/views/novel/components/importNovel.vue @@ -0,0 +1,289 @@ + + + + + diff --git a/web-core/src/views/novel/index.vue b/web-core/src/views/novel/index.vue new file mode 100644 index 0000000..5d4793c --- /dev/null +++ b/web-core/src/views/novel/index.vue @@ -0,0 +1,395 @@ + + + + + diff --git a/web-core/src/views/production/components/editImage/generatedNode.vue b/web-core/src/views/production/components/editImage/generatedNode.vue new file mode 100644 index 0000000..bfd7597 --- /dev/null +++ b/web-core/src/views/production/components/editImage/generatedNode.vue @@ -0,0 +1,379 @@ + + + + + diff --git a/web-core/src/views/production/components/editImage/index.vue b/web-core/src/views/production/components/editImage/index.vue new file mode 100644 index 0000000..7cfb4ac --- /dev/null +++ b/web-core/src/views/production/components/editImage/index.vue @@ -0,0 +1,373 @@ + + + + + diff --git a/web-core/src/views/production/components/editImage/removeLine.vue b/web-core/src/views/production/components/editImage/removeLine.vue new file mode 100644 index 0000000..4c1b98c --- /dev/null +++ b/web-core/src/views/production/components/editImage/removeLine.vue @@ -0,0 +1,65 @@ + + + + diff --git a/web-core/src/views/production/components/editImage/results.vue b/web-core/src/views/production/components/editImage/results.vue new file mode 100644 index 0000000..f8122a1 --- /dev/null +++ b/web-core/src/views/production/components/editImage/results.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/web-core/src/views/production/components/editImage/uploadNode.vue b/web-core/src/views/production/components/editImage/uploadNode.vue new file mode 100644 index 0000000..8890d03 --- /dev/null +++ b/web-core/src/views/production/components/editImage/uploadNode.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/web-core/src/views/production/components/rightChatBox/index.vue b/web-core/src/views/production/components/rightChatBox/index.vue new file mode 100644 index 0000000..cdec4a2 --- /dev/null +++ b/web-core/src/views/production/components/rightChatBox/index.vue @@ -0,0 +1,312 @@ + + + + + diff --git a/web-core/src/views/production/components/workbench/editVideo/index.vue b/web-core/src/views/production/components/workbench/editVideo/index.vue new file mode 100644 index 0000000..88d00a9 --- /dev/null +++ b/web-core/src/views/production/components/workbench/editVideo/index.vue @@ -0,0 +1,534 @@ + + + + + diff --git a/web-core/src/views/production/components/workbench/editVideo/mediaLibrary.vue b/web-core/src/views/production/components/workbench/editVideo/mediaLibrary.vue new file mode 100644 index 0000000..d93e541 --- /dev/null +++ b/web-core/src/views/production/components/workbench/editVideo/mediaLibrary.vue @@ -0,0 +1,799 @@ + + + + + diff --git a/web-core/src/views/production/components/workbench/editVideo/propertyPanel.vue b/web-core/src/views/production/components/workbench/editVideo/propertyPanel.vue new file mode 100644 index 0000000..ba03a4e --- /dev/null +++ b/web-core/src/views/production/components/workbench/editVideo/propertyPanel.vue @@ -0,0 +1,557 @@ + + + + + diff --git a/web-core/src/views/production/components/workbench/editVideo/utils/clipMeta.ts b/web-core/src/views/production/components/workbench/editVideo/utils/clipMeta.ts new file mode 100644 index 0000000..4cd953b --- /dev/null +++ b/web-core/src/views/production/components/workbench/editVideo/utils/clipMeta.ts @@ -0,0 +1,40 @@ +import type { Clip } from "vue-clip-track"; + +const CLIP_ICONS: Record = { + video: "i-video", + audio: "i-music", + subtitle: "i-editor", + transition: "i-exchange", + sticker: "i-pic", + filter: "i-filter", + effect: "i-flash", +}; + +const CLIP_TYPE_KEYS: Record = { + video: 'workbench.production.clipType.video', + audio: 'workbench.production.clipType.audio', + subtitle: 'workbench.production.clipType.subtitle', + transition: 'workbench.production.clipType.transition', + sticker: 'workbench.production.clipType.sticker', + filter: 'workbench.production.clipType.filter', + effect: 'workbench.production.clipType.effect', +}; + +/** 获取 clip 类型对应的图标 */ +export function getClipIcon(clip: Clip): string { + return CLIP_ICONS[clip.type] || "i-file-text"; +} + +/** 获取 clip 类型的名称 */ +export function getClipTypeName(type: string): string { + const key = CLIP_TYPE_KEYS[type]; + return key ? $t(key) : type; +} + +/** 格式化时间为 MM:SS.ms 格式 */ +export function formatTime(seconds: number): string { + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + const ms = Math.floor((seconds % 1) * 100); + return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}.${ms.toString().padStart(2, "0")}`; +} diff --git a/web-core/src/views/production/components/workbench/editVideo/utils/filterEffect.ts b/web-core/src/views/production/components/workbench/editVideo/utils/filterEffect.ts new file mode 100644 index 0000000..cdc6448 --- /dev/null +++ b/web-core/src/views/production/components/workbench/editVideo/utils/filterEffect.ts @@ -0,0 +1,206 @@ +import type { FilterClip, EffectClip, Track } from "vue-clip-track"; + +/** 当前时间应用的滤镜 */ +export interface ActiveFilter { + clipId: string; + trackId: string; + filterType: string; + filterValue: number | Record; +} + +/** 当前时间应用的特效 */ +export interface ActiveEffect { + clipId: string; + trackId: string; + effectType: string; + progress: number; +} + +/** 获取当前时间点激活的滤镜 */ +export function getActiveFiltersAtTime(tracks: Track[], timeInSeconds: number): ActiveFilter[] { + const filters: ActiveFilter[] = []; + + for (const track of tracks) { + if (track.visible === false) continue; + if (track.type !== "filter") continue; + + for (const clip of track.clips) { + const filterClip = clip as FilterClip; + if (filterClip.type === "filter" && timeInSeconds >= filterClip.startTime && timeInSeconds <= filterClip.endTime) { + filters.push({ + clipId: filterClip.id, + trackId: filterClip.trackId, + filterType: filterClip.filterType, + filterValue: filterClip.filterValue, + }); + } + } + } + + return filters; +} + +/** 获取当前时间点激活的特效 */ +export function getActiveEffectsAtTime(tracks: Track[], timeInSeconds: number): ActiveEffect[] { + const effects: ActiveEffect[] = []; + + for (const track of tracks) { + if (track.visible === false) continue; + if (track.type !== "effect") continue; + + for (const clip of track.clips) { + const effectClip = clip as EffectClip; + if (effectClip.type === "effect" && timeInSeconds >= effectClip.startTime && timeInSeconds <= effectClip.endTime) { + const effectTotalDuration = effectClip.endTime - effectClip.startTime; + const elapsedTime = timeInSeconds - effectClip.startTime; + const progress = Math.min(elapsedTime / effectTotalDuration, 1); + + effects.push({ + clipId: effectClip.id, + trackId: effectClip.trackId, + effectType: effectClip.effectType, + progress, + }); + } + } + } + + return effects; +} + +/** 将滤镜列表转换为 CSS filter 字符串 */ +export function buildCSSFilter(filters: ActiveFilter[]): string { + const filterParts: string[] = []; + + for (const filter of filters) { + let value: number; + if (typeof filter.filterValue === "number") { + value = filter.filterValue; + } else if (typeof filter.filterValue === "object" && filter.filterValue !== null) { + value = (filter.filterValue as Record).value ?? Object.values(filter.filterValue).find((v) => typeof v === "number") ?? 0; + } else { + value = 0; + } + + switch (filter.filterType) { + case "blur": + filterParts.push(`blur(${value}px)`); + break; + case "brightness": + filterParts.push(`brightness(${Math.max(0, value)})`); + break; + case "contrast": + filterParts.push(`contrast(${Math.max(0, value)})`); + break; + case "saturate": + case "saturation": + filterParts.push(`saturate(${Math.max(0, value)})`); + break; + case "grayscale": + filterParts.push(`grayscale(${Math.min(Math.max(0, value), 1)})`); + break; + case "sepia": + filterParts.push(`sepia(${Math.min(Math.max(0, value), 1)})`); + break; + case "invert": + filterParts.push(`invert(${Math.min(Math.max(0, value), 1)})`); + break; + case "hue-rotate": + filterParts.push(`hue-rotate(${value}deg)`); + break; + case "opacity": + filterParts.push(`opacity(${Math.min(Math.max(0, value), 1)})`); + break; + case "drop-shadow": + if (typeof filter.filterValue === "object" && filter.filterValue !== null) { + const fv = filter.filterValue as Record; + const offsetX = fv.offsetX ?? 4; + const offsetY = fv.offsetY ?? 4; + const blurRadius = fv.blurRadius ?? 2; + const color = fv.color ?? "black"; + filterParts.push(`drop-shadow(${offsetX}px ${offsetY}px ${blurRadius}px ${color})`); + } + break; + default: + console.warn(`Unknown filter type: ${filter.filterType}`); + } + } + + return filterParts.join(" "); +} + +/** 应用特效到帧数据,返回透明度和变换 */ +export function applyEffectsToFrame( + effects: ActiveEffect[], + _frame: VideoFrame | ImageBitmap, + _time: number, +): { opacity: number; transform: string } { + let opacity = 1; + let transform = ""; + + for (const effect of effects) { + switch (effect.effectType) { + case "fadeIn": + opacity *= effect.progress; + break; + case "fadeOut": + opacity *= 1 - effect.progress; + break; + case "flash": { + const flashFrequency = 4; + opacity *= 0.5 + 0.5 * Math.sin(effect.progress * Math.PI * 2 * flashFrequency); + break; + } + case "pulse": { + const pulseScale = 1 + 0.1 * Math.sin(effect.progress * Math.PI * 4); + transform += ` scale(${pulseScale})`; + break; + } + case "shake": { + const shakeIntensity = 10; + const shakeX = Math.sin(effect.progress * Math.PI * 20) * shakeIntensity * (1 - effect.progress); + const shakeY = Math.cos(effect.progress * Math.PI * 20) * shakeIntensity * (1 - effect.progress); + transform += ` translate(${shakeX}px, ${shakeY}px)`; + break; + } + case "zoomIn": { + const zoomInScale = 0.5 + 0.5 * effect.progress; + opacity *= effect.progress; + transform += ` scale(${zoomInScale})`; + break; + } + case "zoomOut": { + const zoomOutScale = 1 + 0.5 * effect.progress; + opacity *= 1 - effect.progress; + transform += ` scale(${zoomOutScale})`; + break; + } + case "slideInLeft": { + const slideLeftX = -100 * (1 - effect.progress); + transform += ` translateX(${slideLeftX}%)`; + break; + } + case "slideInRight": { + const slideRightX = 100 * (1 - effect.progress); + transform += ` translateX(${slideRightX}%)`; + break; + } + case "rotateIn": { + const rotateAngle = 360 * (1 - effect.progress); + opacity *= effect.progress; + transform += ` rotate(${rotateAngle}deg)`; + break; + } + case "blur-in": + opacity *= effect.progress; + break; + case "blur-out": + opacity *= 1 - effect.progress; + break; + default: + console.warn(`Unknown effect type: ${effect.effectType}`); + } + } + + return { opacity: Math.max(0, Math.min(1, opacity)), transform }; +} diff --git a/web-core/src/views/production/components/workbench/editVideo/utils/mediaData.ts b/web-core/src/views/production/components/workbench/editVideo/utils/mediaData.ts new file mode 100644 index 0000000..b892e5a --- /dev/null +++ b/web-core/src/views/production/components/workbench/editVideo/utils/mediaData.ts @@ -0,0 +1,169 @@ +/** 媒体素材接口 */ +export interface MediaItem { + id: string; + type: "video" | "audio" | "image"; + name: string; + duration: number; + icon: string; + color: string; + url: string; + thumbnail?: string; + thumbnails?: string[]; + waveformData?: number[]; + loading?: boolean; + selected?: boolean; +} + +/** 音频素材接口 */ +export interface AudioItem { + id: string; + type: "audio"; + name: string; + duration: number; + url: string; + waveformData?: number[]; + loading?: boolean; +} + +/** 字幕/文本列表 */ +export function getTextItems() { + return [ + { id: "text-1", type: "subtitle", name: $t("workbench.production.media.titleText"), preview: "Aa", duration: 3 }, + { + id: "text-2", + type: "subtitle", + name: $t("workbench.production.media.subtitleText"), + preview: $t("workbench.production.media.subtitlePreview"), + duration: 3, + }, + { id: "text-3", type: "text", name: $t("workbench.production.media.customText"), preview: "Text", duration: 3 }, + ]; +} + +/** 转场效果列表 */ +export function getTransitionItems() { + return [ + { id: "trans-1", type: "transition", subType: "fade", name: $t("workbench.production.transition.fade"), icon: "i-round" }, + { id: "trans-2", type: "transition", subType: "slide", name: $t("workbench.production.transition.slide"), icon: "i-right" }, + { id: "trans-3", type: "transition", subType: "wipe", name: $t("workbench.production.transition.wipe"), icon: "i-erase" }, + { id: "trans-4", type: "transition", subType: "dissolve", name: $t("workbench.production.transition.dissolve"), icon: "i-platte" }, + { id: "trans-5", type: "transition", subType: "zoom", name: $t("workbench.production.transition.zoom"), icon: "i-zoom-in" }, + { id: "trans-6", type: "transition", subType: "rotate", name: $t("workbench.production.transition.rotate"), icon: "i-redo" }, + ]; +} + +/** 特效列表 */ +export function getEffectItems() { + return [ + { id: "fadeIn", type: "effect", effectType: "fadeIn", name: $t("workbench.production.effect.fadeIn"), icon: "i-sun-one" }, + { id: "fadeOut", type: "effect", effectType: "fadeOut", name: $t("workbench.production.effect.fadeOut"), icon: "i-moon" }, + { id: "flash", type: "effect", effectType: "flash", name: $t("workbench.production.effect.flash"), icon: "i-flashlamp" }, + { id: "shake", type: "effect", effectType: "shake", name: $t("workbench.production.effect.shake"), icon: "i-shake" }, + { id: "zoomIn", type: "effect", effectType: "zoomIn", name: $t("workbench.production.effect.zoomIn"), icon: "i-zoom-in" }, + { id: "zoomOut", type: "effect", effectType: "zoomOut", name: $t("workbench.production.effect.zoomOut"), icon: "i-zoom-out" }, + { id: "pulse", type: "effect", effectType: "pulse", name: $t("workbench.production.effect.pulse"), icon: "i-heartbeat" }, + { id: "rotateIn", type: "effect", effectType: "rotateIn", name: $t("workbench.production.effect.rotateIn"), icon: "i-redo" }, + { id: "sticker-1", type: "sticker", name: $t("workbench.production.effect.sticker1"), icon: "i-emotion-happy" }, + { id: "sticker-2", type: "sticker", name: $t("workbench.production.effect.sticker2"), icon: "i-star" }, + ]; +} + +/** 滤镜列表 */ +export function getFilterItems() { + return [ + { + id: "grayscale", + type: "filter", + filterType: "grayscale", + filterValue: 1, + name: $t("workbench.production.filter.grayscale"), + icon: "i-dark-mode", + }, + { id: "sepia", type: "filter", filterType: "sepia", filterValue: 1, name: $t("workbench.production.filter.sepia"), icon: "i-camera-one" }, + { id: "warm", type: "filter", filterType: "sepia", filterValue: 0.3, name: $t("workbench.production.filter.warm"), icon: "i-fire" }, + { id: "cool", type: "filter", filterType: "hue-rotate", filterValue: 180, name: $t("workbench.production.filter.cool"), icon: "i-snowflake" }, + { id: "saturate", type: "filter", filterType: "saturate", filterValue: 2, name: $t("workbench.production.filter.vivid"), icon: "i-brightness" }, + { + id: "brightness", + type: "filter", + filterType: "brightness", + filterValue: 1.3, + name: $t("workbench.production.filter.bright"), + icon: "i-sun-one", + }, + { + id: "contrast", + type: "filter", + filterType: "contrast", + filterValue: 1.5, + name: $t("workbench.production.filter.highContrast"), + icon: "i-contrast-view", + }, + { id: "blur", type: "filter", filterType: "blur", filterValue: 3, name: $t("workbench.production.filter.blur"), icon: "i-fog" }, + { + id: "invert", + type: "filter", + filterType: "invert", + filterValue: 1, + name: $t("workbench.production.filter.invert"), + icon: "i-reverse-rotation", + }, + { + id: "opacity", + type: "filter", + filterType: "opacity", + filterValue: 0.5, + name: $t("workbench.production.filter.semiTransparent"), + icon: "i-ghost", + }, + ]; +} + +/** 资源库 Tab 配置 */ +export function getLibraryTabs() { + return [ + { id: "video", label: $t("workbench.production.media.video"), icon: "i-video-file" }, + { id: "media", label: $t("workbench.production.media.media"), icon: "i-video" }, + { id: "image", label: $t("workbench.production.media.image"), icon: "i-pic" }, + { id: "audio", label: $t("workbench.production.media.audio"), icon: "i-music" }, + { id: "text", label: $t("workbench.production.media.subtitle"), icon: "i-text" }, + { id: "transition", label: $t("workbench.production.media.transition"), icon: "i-switch-themes" }, + { id: "effect", label: $t("workbench.production.media.effect"), icon: "i-magic" }, + { id: "filter", label: $t("workbench.production.media.filter"), icon: "i-color-filter" }, + ]; +} + +/** 格式化时长 */ +export function formatDuration(seconds: number): string { + if (seconds === 0) return $t("workbench.production.media.loading"); + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + if (mins > 0) { + return `${mins}:${secs.toString().padStart(2, "0")}`; + } + return `${secs.toFixed(1)}s`; +} + +/** 在 canvas 上绘制迷你波形图 */ +export function drawMiniWaveform(audioId: string, waveformData: number[]) { + const canvas = document.querySelector(`canvas[data-audio-id="${audioId}"]`) as HTMLCanvasElement; + if (!canvas) return; + + const ctx = canvas.getContext("2d"); + if (!ctx) return; + + const width = canvas.width; + const height = canvas.height; + + ctx.clearRect(0, 0, width, height); + + const barWidth = width / waveformData.length; + ctx.fillStyle = "rgba(16, 185, 129, 0.8)"; + + for (let i = 0; i < waveformData.length; i++) { + const barHeight = waveformData[i] * height * 0.9; + const x = i * barWidth; + const y = (height - barHeight) / 2; + ctx.fillRect(x, y, Math.max(1, barWidth - 1), barHeight); + } +} diff --git a/web-core/src/views/production/components/workbench/editVideo/utils/mediaLoader.ts b/web-core/src/views/production/components/workbench/editVideo/utils/mediaLoader.ts new file mode 100644 index 0000000..1defb1a --- /dev/null +++ b/web-core/src/views/production/components/workbench/editVideo/utils/mediaLoader.ts @@ -0,0 +1,67 @@ +import { + extractVideoThumbnails, + extractAudioWaveform, + extractVideoAudioWaveform, + type MediaClip, +} from "vue-clip-track"; + +/** 异步加载视频 clip 的缩略图 */ +export async function loadVideoClipThumbnails(tracksStore: any, clipId: string, sourceUrl: string) { + try { + const result = await extractVideoThumbnails(sourceUrl, { count: 20, width: 120 }); + const clip = tracksStore.getClip(clipId) as MediaClip; + if (clip && clip.type === "video") { + clip.thumbnails = result.thumbnails; + if (result.duration > 0 && clip.endTime - clip.startTime <= 0) { + clip.endTime = clip.startTime + result.duration; + clip.originalDuration = result.duration; + clip.trimEnd = result.duration; + } + } + } catch (error) { + console.error("Failed to load video thumbnails:", error); + } +} + +/** 异步加载音频 clip 的波形数据 */ +export async function loadAudioClipWaveform(tracksStore: any, clipId: string, sourceUrl: string) { + try { + const isVideo = sourceUrl.match(/\.(mp4|webm|mov|avi)$/i); + const result = isVideo + ? await extractVideoAudioWaveform(sourceUrl, { samples: 500 }) + : await extractAudioWaveform(sourceUrl, { samples: 500 }); + + const clip = tracksStore.getClip(clipId) as MediaClip; + if (clip && clip.type === "audio") { + clip.waveformData = result.waveformData; + + if (result.duration > 0) { + clip.originalDuration = result.duration; + const clipDuration = clip.endTime - clip.startTime; + if (clipDuration <= 0) { + clip.endTime = clip.startTime + result.duration; + clip.trimEnd = result.duration; + } + if (clip.trimEnd > result.duration) { + clip.trimEnd = result.duration; + } + } + } + } catch (error) { + console.error("Failed to load audio waveform:", error); + } +} + +/** 初始化时加载所有音频 clip 的波形数据 */ +export async function loadInitialAudioWaveforms(tracksStore: any) { + for (const track of tracksStore.tracks) { + for (const clip of track.clips) { + if (clip.type === "audio") { + const mediaClip = clip as MediaClip; + if (!mediaClip.waveformData || mediaClip.waveformData.length === 0) { + await loadAudioClipWaveform(tracksStore, mediaClip.id, mediaClip.sourceUrl); + } + } + } + } +} diff --git a/web-core/src/views/production/components/workbench/editVideo/utils/trackHelper.ts b/web-core/src/views/production/components/workbench/editVideo/utils/trackHelper.ts new file mode 100644 index 0000000..8b101c7 --- /dev/null +++ b/web-core/src/views/production/components/workbench/editVideo/utils/trackHelper.ts @@ -0,0 +1,83 @@ +import { generateId } from "vue-clip-track"; + +const TRACK_NAME_KEYS: Record = { + video: 'workbench.production.track.video', + image: 'workbench.production.track.image', + audio: 'workbench.production.track.audio', + subtitle: 'workbench.production.track.subtitle', + text: 'workbench.production.track.text', + sticker: 'workbench.production.track.sticker', + filter: 'workbench.production.track.filter', + effect: 'workbench.production.track.effect', +}; + +const DEFAULT_DURATIONS: Record = { + video: 5, + image: 5, + audio: 30, + subtitle: 3, + text: 3, + sticker: 3, + filter: 3, + effect: 3, + transition: 3, +}; + +/** 根据素材类型获取默认时长 */ +export function getDefaultDuration(mediaType: string, mediaData: any): number { + if (mediaData.duration && mediaData.duration > 0) { + return mediaData.duration; + } + return DEFAULT_DURATIONS[mediaType] || 3; +} + +/** 检查轨道在指定时间范围是否有足够空间(不重叠) */ +export function hasSpaceInTrack(track: any, startTime: number, duration: number): boolean { + const endTime = startTime + duration; + for (const clip of track.clips) { + if (clip.type === "transition") continue; + if (startTime < clip.endTime && endTime > clip.startTime) { + return false; + } + } + return true; +} + +/** 查找或创建匹配类型且有足够空间的轨道 */ +export function findOrCreateTrackWithSpace( + tracksStore: any, + mediaType: string, + startTime: number, + duration: number, + preferredTrackId?: string, +): { track: any; isNew: boolean } { + if (preferredTrackId) { + const preferredTrack = tracksStore.tracks.find((t: any) => t.id === preferredTrackId); + if (preferredTrack && preferredTrack.type === mediaType) { + if (hasSpaceInTrack(preferredTrack, startTime, duration)) { + return { track: preferredTrack, isNew: false }; + } + } + } + + const sameTypeTracks = tracksStore.sortedTracks.filter((t: any) => t.type === mediaType && !t.isMain); + for (const track of sameTypeTracks) { + if (hasSpaceInTrack(track, startTime, duration)) { + return { track, isNew: false }; + } + } + + const trackCount = tracksStore.getTrackCountByType(mediaType); + const newTrack = { + id: generateId("track-"), + type: mediaType, + name: `${TRACK_NAME_KEYS[mediaType] ? $t(TRACK_NAME_KEYS[mediaType]) : mediaType}${trackCount + 1}`, + visible: true, + locked: false, + clips: [], + order: tracksStore.tracks.length, + }; + + tracksStore.addTrack(newTrack); + return { track: newTrack, isNew: true }; +} diff --git a/web-core/src/views/production/components/workbench/editVideo/utils/transitionHelper.ts b/web-core/src/views/production/components/workbench/editVideo/utils/transitionHelper.ts new file mode 100644 index 0000000..6512e79 --- /dev/null +++ b/web-core/src/views/production/components/workbench/editVideo/utils/transitionHelper.ts @@ -0,0 +1,109 @@ +import { + generateId, + normalizeTime, + type TransitionClip, + type Clip, +} from "vue-clip-track"; + +const TRANSITION_NAME_KEYS: Record = { + fade: 'workbench.production.transition.fade', + slide: 'workbench.production.transition.slide', + wipe: 'workbench.production.transition.wipe', + dissolve: 'workbench.production.transition.dissolve', + zoom: 'workbench.production.transition.zoom', + rotate: 'workbench.production.transition.rotate', +}; + +/** 在已排序的非转场 clips 中,根据 dropTime 查找相邻的两个 clip */ +export function findAdjacentClipsAtTime( + clips: Clip[], + dropTime: number, +): { beforeClip: Clip; afterClip: Clip } | null { + const targetClip = clips.find((c) => dropTime >= c.startTime && dropTime <= c.endTime); + + if (targetClip) { + const clipMidPoint = (targetClip.startTime + targetClip.endTime) / 2; + const targetIndex = clips.indexOf(targetClip); + + if (dropTime < clipMidPoint) { + // 在前半部分,找前面相邻的 clip + if (targetIndex > 0) { + const prevClip = clips[targetIndex - 1]; + if (Math.abs(prevClip.endTime - targetClip.startTime) < 0.1) { + return { beforeClip: prevClip, afterClip: targetClip }; + } + } + } else { + // 在后半部分,找后面相邻的 clip + if (targetIndex < clips.length - 1) { + const nextClip = clips[targetIndex + 1]; + if (Math.abs(targetClip.endTime - nextClip.startTime) < 0.1) { + return { beforeClip: targetClip, afterClip: nextClip }; + } + } + } + } else { + // 鼠标不在任何 clip 上,尝试找 dropTime 附近的相邻 clips + for (let i = 0; i < clips.length - 1; i++) { + if (clips[i].endTime <= dropTime && clips[i + 1].startTime >= dropTime) { + if (Math.abs(clips[i].endTime - clips[i + 1].startTime) < 0.1) { + return { beforeClip: clips[i], afterClip: clips[i + 1] }; + } + } + } + } + + return null; +} + +/** + * 在两个 clip 之间添加转场 + * @returns 添加成功时返回转场信息,失败返回 null + */ +export function addTransitionBetweenClips( + tracksStore: any, + historyStore: any, + beforeClipId: string, + afterClipId: string, + transitionType: string = "fade", +): { transitionClip: TransitionClip; beforeClip: Clip; afterClip: Clip } | null { + const beforeClip = tracksStore.getClip(beforeClipId); + const afterClip = tracksStore.getClip(afterClipId); + + if (!beforeClip || !afterClip) { + console.error("未找到clip"); + return null; + } + + const track = tracksStore.tracks.find((t: any) => t.id === beforeClip.trackId); + if (!track) return null; + + // 检查是否已经有转场 + const hasExistingTransition = track.clips.some( + (c: any) => c.type === "transition" && c.startTime < beforeClip.endTime && c.endTime > beforeClip.endTime, + ); + + if (hasExistingTransition) { + window.$message.warning($t('workbench.production.editVideo.transitionExists')); + return null; + } + + const transitionDuration = 1; + const transitionClip: TransitionClip = { + id: generateId("clip-"), + trackId: beforeClip.trackId, + type: "transition", + startTime: normalizeTime(beforeClip.endTime - transitionDuration / 2), + endTime: normalizeTime(afterClip.startTime + transitionDuration / 2), + selected: false, + transitionType, + transitionDuration: normalizeTime(transitionDuration), + name: TRANSITION_NAME_KEYS[transitionType] ? $t(TRANSITION_NAME_KEYS[transitionType]) : transitionType, + }; + + tracksStore.addClip(beforeClip.trackId, transitionClip); + historyStore.pushSnapshot($t("workbench.production.editVideo.addTransition")); + tracksStore.clearSelection(); + + return { transitionClip, beforeClip, afterClip }; +} diff --git a/web-core/src/views/production/components/workbench/editVideo/utils/transitionRenderers.ts b/web-core/src/views/production/components/workbench/editVideo/utils/transitionRenderers.ts new file mode 100644 index 0000000..dd32906 --- /dev/null +++ b/web-core/src/views/production/components/workbench/editVideo/utils/transitionRenderers.ts @@ -0,0 +1,666 @@ +/** + * 转场效果渲染器模块 + * 提供各种转场效果的帧级渲染实现 + */ + +/** + * 转场渲染器接口 + */ +export interface TransitionRenderer { + /** + * 渲染转场效果 + * @param ctx - OffscreenCanvas 2D 上下文 + * @param frameA - 前一个视频帧(转场开始前的 clip) + * @param frameB - 后一个视频帧(转场结束后的 clip) + * @param progress - 转场进度 0-1 + * @param width - 画布宽度 + * @param height - 画布高度 + */ + render( + ctx: OffscreenCanvasRenderingContext2D, + frameA: VideoFrame | ImageBitmap | null, + frameB: VideoFrame | ImageBitmap | null, + progress: number, + width: number, + height: number, + ): void; +} + +/** + * 转场类型枚举 + */ +export type TransitionType = + | "fade" // 淡入淡出 + | "dissolve" // 溶解 + | "slide-left" // 向左滑动 + | "slide-right" // 向右滑动 + | "slide-up" // 向上滑动 + | "slide-down" // 向下滑动 + | "wipe-left" // 向左擦除 + | "wipe-right" // 向右擦除 + | "wipe-up" // 向上擦除 + | "wipe-down" // 向下擦除 + | "zoom-in" // 放大 + | "zoom-out" // 缩小 + | "rotate" // 旋转 + | "blur" // 模糊过渡 + | "pixelate" // 像素化过渡 + | "circle" // 圆形遮罩 + | "diamond" // 菱形遮罩 + | "clock"; // 时钟擦除 + +// ============ 转场渲染器实现 ============ + +/** + * 淡入淡出转场 + * 前一帧渐隐,后一帧渐显 + */ +export const fadeTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + // 绘制 frameA(渐隐) + if (frameA) { + ctx.globalAlpha = 1 - progress; + ctx.drawImage(frameA, 0, 0, width, height); + } + + // 绘制 frameB(渐显) + if (frameB) { + ctx.globalAlpha = progress; + ctx.drawImage(frameB, 0, 0, width, height); + } + + ctx.globalAlpha = 1; + }, +}; + +/** + * 溶解转场 + * 类似淡入淡出,但使用叠加混合模式 + */ +export const dissolveTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + // 绘制 frameA + if (frameA) { + ctx.globalAlpha = 1; + ctx.drawImage(frameA, 0, 0, width, height); + } + + // 使用 source-atop 混合模式绘制 frameB + if (frameB) { + ctx.globalCompositeOperation = "source-over"; + ctx.globalAlpha = progress; + ctx.drawImage(frameB, 0, 0, width, height); + } + + ctx.globalAlpha = 1; + ctx.globalCompositeOperation = "source-over"; + }, +}; + +/** + * 向左滑动转场 + * frameA 向左滑出,frameB 从右侧滑入 + */ +export const slideLeftTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + // 使用缓动函数让动画更自然 + const easedProgress = easeInOutCubic(progress); + const slideX = width * easedProgress; + + // frameA 向左滑出 + if (frameA) { + ctx.globalAlpha = 1; + ctx.drawImage(frameA, -slideX, 0, width, height); + } + + // frameB 从右侧滑入 + if (frameB) { + ctx.globalAlpha = 1; + ctx.drawImage(frameB, width - slideX, 0, width, height); + } + }, +}; + +/** + * 向右滑动转场 + * frameA 向右滑出,frameB 从左侧滑入 + */ +export const slideRightTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const easedProgress = easeInOutCubic(progress); + const slideX = width * easedProgress; + + // frameA 向右滑出 + if (frameA) { + ctx.globalAlpha = 1; + ctx.drawImage(frameA, slideX, 0, width, height); + } + + // frameB 从左侧滑入 + if (frameB) { + ctx.globalAlpha = 1; + ctx.drawImage(frameB, -width + slideX, 0, width, height); + } + }, +}; + +/** + * 向上滑动转场 + * frameA 向上滑出,frameB 从下方滑入 + */ +export const slideUpTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const easedProgress = easeInOutCubic(progress); + const slideY = height * easedProgress; + + // frameA 向上滑出 + if (frameA) { + ctx.globalAlpha = 1; + ctx.drawImage(frameA, 0, -slideY, width, height); + } + + // frameB 从下方滑入 + if (frameB) { + ctx.globalAlpha = 1; + ctx.drawImage(frameB, 0, height - slideY, width, height); + } + }, +}; + +/** + * 向下滑动转场 + * frameA 向下滑出,frameB 从上方滑入 + */ +export const slideDownTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const easedProgress = easeInOutCubic(progress); + const slideY = height * easedProgress; + + // frameA 向下滑出 + if (frameA) { + ctx.globalAlpha = 1; + ctx.drawImage(frameA, 0, slideY, width, height); + } + + // frameB 从上方滑入 + if (frameB) { + ctx.globalAlpha = 1; + ctx.drawImage(frameB, 0, -height + slideY, width, height); + } + }, +}; + +/** + * 向左擦除转场 + * 从右向左逐渐显示 frameB + */ +export const wipeLeftTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const easedProgress = easeInOutQuad(progress); + const wipeX = width * easedProgress; + + // 先绘制 frameA 作为底层 + if (frameA) { + ctx.globalAlpha = 1; + ctx.drawImage(frameA, 0, 0, width, height); + } + + // 使用裁剪区域绘制 frameB(从右向左) + if (frameB) { + ctx.save(); + ctx.beginPath(); + ctx.rect(width - wipeX, 0, wipeX, height); + ctx.clip(); + ctx.drawImage(frameB, 0, 0, width, height); + ctx.restore(); + } + }, +}; + +/** + * 向右擦除转场 + * 从左向右逐渐显示 frameB + */ +export const wipeRightTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const easedProgress = easeInOutQuad(progress); + const wipeX = width * easedProgress; + + // 先绘制 frameA 作为底层 + if (frameA) { + ctx.globalAlpha = 1; + ctx.drawImage(frameA, 0, 0, width, height); + } + + // 使用裁剪区域绘制 frameB(从左向右) + if (frameB) { + ctx.save(); + ctx.beginPath(); + ctx.rect(0, 0, wipeX, height); + ctx.clip(); + ctx.drawImage(frameB, 0, 0, width, height); + ctx.restore(); + } + }, +}; + +/** + * 向上擦除转场 + * 从下向上逐渐显示 frameB + */ +export const wipeUpTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const easedProgress = easeInOutQuad(progress); + const wipeY = height * easedProgress; + + // 先绘制 frameA 作为底层 + if (frameA) { + ctx.globalAlpha = 1; + ctx.drawImage(frameA, 0, 0, width, height); + } + + // 使用裁剪区域绘制 frameB(从下向上) + if (frameB) { + ctx.save(); + ctx.beginPath(); + ctx.rect(0, height - wipeY, width, wipeY); + ctx.clip(); + ctx.drawImage(frameB, 0, 0, width, height); + ctx.restore(); + } + }, +}; + +/** + * 向下擦除转场 + * 从上向下逐渐显示 frameB + */ +export const wipeDownTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const easedProgress = easeInOutQuad(progress); + const wipeY = height * easedProgress; + + // 先绘制 frameA 作为底层 + if (frameA) { + ctx.globalAlpha = 1; + ctx.drawImage(frameA, 0, 0, width, height); + } + + // 使用裁剪区域绘制 frameB(从上向下) + if (frameB) { + ctx.save(); + ctx.beginPath(); + ctx.rect(0, 0, width, wipeY); + ctx.clip(); + ctx.drawImage(frameB, 0, 0, width, height); + ctx.restore(); + } + }, +}; + +/** + * 放大转场 + * frameB 从中心放大覆盖 frameA + */ +export const zoomInTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const easedProgress = easeInOutCubic(progress); + + // 先绘制 frameA 作为底层 + if (frameA) { + ctx.globalAlpha = 1 - easedProgress * 0.5; + ctx.drawImage(frameA, 0, 0, width, height); + } + + // frameB 从中心放大 + if (frameB) { + const scale = easedProgress; + const scaledWidth = width * scale; + const scaledHeight = height * scale; + const offsetX = (width - scaledWidth) / 2; + const offsetY = (height - scaledHeight) / 2; + + ctx.globalAlpha = easedProgress; + ctx.drawImage(frameB, offsetX, offsetY, scaledWidth, scaledHeight); + } + + ctx.globalAlpha = 1; + }, +}; + +/** + * 缩小转场 + * frameA 缩小消失,显示 frameB + */ +export const zoomOutTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const easedProgress = easeInOutCubic(progress); + + // 先绘制 frameB 作为底层 + if (frameB) { + ctx.globalAlpha = 1; + ctx.drawImage(frameB, 0, 0, width, height); + } + + // frameA 缩小 + if (frameA) { + const scale = 1 - easedProgress; + const scaledWidth = width * scale; + const scaledHeight = height * scale; + const offsetX = (width - scaledWidth) / 2; + const offsetY = (height - scaledHeight) / 2; + + ctx.globalAlpha = 1 - easedProgress; + ctx.drawImage(frameA, offsetX, offsetY, scaledWidth, scaledHeight); + } + + ctx.globalAlpha = 1; + }, +}; + +/** + * 旋转转场 + * frameA 旋转消失,frameB 旋转出现 + */ +export const rotateTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const easedProgress = easeInOutCubic(progress); + const centerX = width / 2; + const centerY = height / 2; + + // frameA 旋转消失 + if (frameA && progress < 0.5) { + const angle = easedProgress * Math.PI; // 0 到 180度 + const scale = 1 - easedProgress * 2; + + ctx.save(); + ctx.translate(centerX, centerY); + ctx.rotate(angle); + ctx.scale(Math.max(0.01, scale), Math.max(0.01, scale)); + ctx.translate(-centerX, -centerY); + ctx.globalAlpha = 1 - easedProgress * 2; + ctx.drawImage(frameA, 0, 0, width, height); + ctx.restore(); + } + + // frameB 旋转出现 + if (frameB && progress >= 0.5) { + const angle = (easedProgress - 0.5) * Math.PI * 2 - Math.PI; // -180到0度 + const scale = (easedProgress - 0.5) * 2; + + ctx.save(); + ctx.translate(centerX, centerY); + ctx.rotate(angle); + ctx.scale(Math.max(0.01, scale), Math.max(0.01, scale)); + ctx.translate(-centerX, -centerY); + ctx.globalAlpha = (easedProgress - 0.5) * 2; + ctx.drawImage(frameB, 0, 0, width, height); + ctx.restore(); + } + + ctx.globalAlpha = 1; + }, +}; + +/** + * 圆形遮罩转场 + * 从中心向外扩展的圆形显示 frameB + */ +export const circleTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const easedProgress = easeInOutQuad(progress); + const centerX = width / 2; + const centerY = height / 2; + // 计算对角线长度作为最大半径 + const maxRadius = Math.sqrt(centerX * centerX + centerY * centerY); + const radius = maxRadius * easedProgress; + + // 先绘制 frameA 作为底层 + if (frameA) { + ctx.globalAlpha = 1; + ctx.drawImage(frameA, 0, 0, width, height); + } + + // 使用圆形裁剪绘制 frameB + if (frameB) { + ctx.save(); + ctx.beginPath(); + ctx.arc(centerX, centerY, radius, 0, Math.PI * 2); + ctx.clip(); + ctx.drawImage(frameB, 0, 0, width, height); + ctx.restore(); + } + }, +}; + +/** + * 菱形遮罩转场 + * 从中心向外扩展的菱形显示 frameB + */ +export const diamondTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const easedProgress = easeInOutQuad(progress); + const centerX = width / 2; + const centerY = height / 2; + const maxSize = Math.max(width, height); + const size = maxSize * easedProgress; + + // 先绘制 frameA 作为底层 + if (frameA) { + ctx.globalAlpha = 1; + ctx.drawImage(frameA, 0, 0, width, height); + } + + // 使用菱形裁剪绘制 frameB + if (frameB) { + ctx.save(); + ctx.beginPath(); + ctx.moveTo(centerX, centerY - size); // 上 + ctx.lineTo(centerX + size, centerY); // 右 + ctx.lineTo(centerX, centerY + size); // 下 + ctx.lineTo(centerX - size, centerY); // 左 + ctx.closePath(); + ctx.clip(); + ctx.drawImage(frameB, 0, 0, width, height); + ctx.restore(); + } + }, +}; + +/** + * 时钟擦除转场 + * 像时钟指针一样扫过显示 frameB + */ +export const clockTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const easedProgress = easeLinear(progress); + const centerX = width / 2; + const centerY = height / 2; + const maxRadius = Math.sqrt(centerX * centerX + centerY * centerY) * 1.5; + const angle = easedProgress * Math.PI * 2 - Math.PI / 2; // 从12点钟方向开始 + + // 先绘制 frameA 作为底层 + if (frameA) { + ctx.globalAlpha = 1; + ctx.drawImage(frameA, 0, 0, width, height); + } + + // 使用扇形裁剪绘制 frameB + if (frameB) { + ctx.save(); + ctx.beginPath(); + ctx.moveTo(centerX, centerY); + ctx.arc(centerX, centerY, maxRadius, -Math.PI / 2, angle, false); + ctx.lineTo(centerX, centerY); + ctx.closePath(); + ctx.clip(); + ctx.drawImage(frameB, 0, 0, width, height); + ctx.restore(); + } + }, +}; + +/** + * 模糊过渡转场 + * 通过模糊效果进行过渡(注意:Canvas 2D 的 filter 性能开销较大) + */ +export const blurTransition: TransitionRenderer = { + render(ctx, frameA, frameB, progress, width, height) { + ctx.clearRect(0, 0, width, height); + + const maxBlur = 20; + const blurA = progress * maxBlur; + const blurB = (1 - progress) * maxBlur; + + // 绘制 frameA(渐隐 + 模糊) + if (frameA) { + ctx.save(); + ctx.filter = `blur(${blurA}px)`; + ctx.globalAlpha = 1 - progress; + ctx.drawImage(frameA, 0, 0, width, height); + ctx.restore(); + } + + // 绘制 frameB(渐显 + 清晰) + if (frameB) { + ctx.save(); + ctx.filter = `blur(${blurB}px)`; + ctx.globalAlpha = progress; + ctx.drawImage(frameB, 0, 0, width, height); + ctx.restore(); + } + + ctx.globalAlpha = 1; + ctx.filter = "none"; + }, +}; + +// ============ 缓动函数 ============ + +/** + * 线性缓动 + */ +function easeLinear(t: number): number { + return t; +} + +/** + * 二次缓动 - 先慢后快再慢 + */ +function easeInOutQuad(t: number): number { + return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2; +} + +/** + * 三次缓动 - 更平滑的加减速 + */ +function easeInOutCubic(t: number): number { + return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2; +} + +// ============ 转场渲染器注册表 ============ + +/** + * 转场渲染器映射表 + */ +export const transitionRenderers: Record = { + fade: fadeTransition, + dissolve: dissolveTransition, + slide: slideLeftTransition, // 默认滑动方向 + "slide-left": slideLeftTransition, + "slide-right": slideRightTransition, + "slide-up": slideUpTransition, + "slide-down": slideDownTransition, + wipe: wipeRightTransition, // 默认擦除方向 + "wipe-left": wipeLeftTransition, + "wipe-right": wipeRightTransition, + "wipe-up": wipeUpTransition, + "wipe-down": wipeDownTransition, + zoom: zoomInTransition, // 默认放大 + "zoom-in": zoomInTransition, + "zoom-out": zoomOutTransition, + rotate: rotateTransition, + circle: circleTransition, + diamond: diamondTransition, + clock: clockTransition, + blur: blurTransition, +}; + +/** + * 获取转场渲染器 + * @param type 转场类型 + * @returns 对应的渲染器,如果类型未知则返回默认的淡入淡出 + */ +export function getTransitionRenderer(type: string): TransitionRenderer { + return transitionRenderers[type] || fadeTransition; +} + +/** + * 获取所有支持的转场类型 + */ +export function getSupportedTransitionTypes(): string[] { + return Object.keys(transitionRenderers); +} + +/** + * 转场类型的 i18n key 映射 + */ +const transitionTypeKeys: Record = { + fade: "workbench.production.transition.fade", + dissolve: "workbench.production.transition.dissolve", + slide: "workbench.production.transition.slide", + "slide-left": "workbench.production.transition.slideLeft", + "slide-right": "workbench.production.transition.slideRight", + "slide-up": "workbench.production.transition.slideUp", + "slide-down": "workbench.production.transition.slideDown", + wipe: "workbench.production.transition.wipe", + "wipe-left": "workbench.production.transition.wipeLeft", + "wipe-right": "workbench.production.transition.wipeRight", + "wipe-up": "workbench.production.transition.wipeUp", + "wipe-down": "workbench.production.transition.wipeDown", + zoom: "workbench.production.transition.zoom", + "zoom-in": "workbench.production.transition.zoomIn", + "zoom-out": "workbench.production.transition.zoomOut", + rotate: "workbench.production.transition.rotate", + circle: "workbench.production.transition.circle", + diamond: "workbench.production.transition.diamond", + clock: "workbench.production.transition.clock", + blur: "workbench.production.transition.blur", +}; + +export function getTransitionTypeName(type: string): string { + const key = transitionTypeKeys[type]; + return key ? $t(key) : type; +} diff --git a/web-core/src/views/production/components/workbench/editVideo/videoPreview.vue b/web-core/src/views/production/components/workbench/editVideo/videoPreview.vue new file mode 100644 index 0000000..0afec51 --- /dev/null +++ b/web-core/src/views/production/components/workbench/editVideo/videoPreview.vue @@ -0,0 +1,1655 @@ + + + + + diff --git a/web-core/src/views/production/components/workbench/generate copy.vue b/web-core/src/views/production/components/workbench/generate copy.vue new file mode 100644 index 0000000..88b4479 --- /dev/null +++ b/web-core/src/views/production/components/workbench/generate copy.vue @@ -0,0 +1,1960 @@ + + + + + + diff --git a/web-core/src/views/production/components/workbench/generate copy/components/trackList.vue b/web-core/src/views/production/components/workbench/generate copy/components/trackList.vue new file mode 100644 index 0000000..9c185b8 --- /dev/null +++ b/web-core/src/views/production/components/workbench/generate copy/components/trackList.vue @@ -0,0 +1,184 @@ + + + + + diff --git a/web-core/src/views/production/components/workbench/generate copy/components/videogenerate.vue b/web-core/src/views/production/components/workbench/generate copy/components/videogenerate.vue new file mode 100644 index 0000000..23e759d --- /dev/null +++ b/web-core/src/views/production/components/workbench/generate copy/components/videogenerate.vue @@ -0,0 +1,664 @@ + + + + + + diff --git a/web-core/src/views/production/components/workbench/generate copy/index.vue b/web-core/src/views/production/components/workbench/generate copy/index.vue new file mode 100644 index 0000000..73bf85e --- /dev/null +++ b/web-core/src/views/production/components/workbench/generate copy/index.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/web-core/src/views/production/components/workbench/generate/components/imageSelect.vue b/web-core/src/views/production/components/workbench/generate/components/imageSelect.vue new file mode 100644 index 0000000..fc162bb --- /dev/null +++ b/web-core/src/views/production/components/workbench/generate/components/imageSelect.vue @@ -0,0 +1,426 @@ + + + + + diff --git a/web-core/src/views/production/components/workbench/generate/components/modeMenu.vue b/web-core/src/views/production/components/workbench/generate/components/modeMenu.vue new file mode 100644 index 0000000..829208b --- /dev/null +++ b/web-core/src/views/production/components/workbench/generate/components/modeMenu.vue @@ -0,0 +1,173 @@ + + + + + + diff --git a/web-core/src/views/production/components/workbench/generate/components/track.vue b/web-core/src/views/production/components/workbench/generate/components/track.vue new file mode 100644 index 0000000..a4adb80 --- /dev/null +++ b/web-core/src/views/production/components/workbench/generate/components/track.vue @@ -0,0 +1,516 @@ + + + + + diff --git a/web-core/src/views/production/components/workbench/generate/components/video.vue b/web-core/src/views/production/components/workbench/generate/components/video.vue new file mode 100644 index 0000000..3382330 --- /dev/null +++ b/web-core/src/views/production/components/workbench/generate/components/video.vue @@ -0,0 +1,336 @@ + + + + + diff --git a/web-core/src/views/production/components/workbench/generate/index.vue b/web-core/src/views/production/components/workbench/generate/index.vue new file mode 100644 index 0000000..949408d --- /dev/null +++ b/web-core/src/views/production/components/workbench/generate/index.vue @@ -0,0 +1,555 @@ + + + + + diff --git a/web-core/src/views/production/components/workbench/index.vue b/web-core/src/views/production/components/workbench/index.vue new file mode 100644 index 0000000..de9dc16 --- /dev/null +++ b/web-core/src/views/production/components/workbench/index.vue @@ -0,0 +1,282 @@ + + + + + diff --git a/web-core/src/views/production/components/workbench/preview.vue b/web-core/src/views/production/components/workbench/preview.vue new file mode 100644 index 0000000..8e8e87a --- /dev/null +++ b/web-core/src/views/production/components/workbench/preview.vue @@ -0,0 +1,808 @@ + + + + + + + + diff --git a/web-core/src/views/production/components/workbench/type/type.ts b/web-core/src/views/production/components/workbench/type/type.ts new file mode 100644 index 0000000..29b2ae8 --- /dev/null +++ b/web-core/src/views/production/components/workbench/type/type.ts @@ -0,0 +1,105 @@ +type ReferenceType = "videoReference" | "imageReference" | "audioReference" | "textReference"; +type Type = "imageReference" | "startImage" | "endImage" | "videoReference" | "audioReference"; +type VideoMode = "singleImage" | "startEndRequired" | "endFrameOptional" | "startFrameOptional" | "text" | ReferenceType[]; + +interface UploadItemBase { + fileType: "image" | "video" | "audio"; + id: number | null; + src?: string; + prompt?: string; +} + +interface UploadItemStoryboard extends UploadItemBase { + sources: "storyboard"; + index: number; +} +interface VideoModel { + name: string; + modelName: string; + type: "video"; + mode: VideoMode[]; + associationSkills?: string; + audio: "optional" | false | true; + durationResolutionMap: { duration: number[]; resolution: string[] }[]; +} +interface UploadItemAssets extends UploadItemBase { + sources: "assets"; +} + +type UploadItem = UploadItemStoryboard | UploadItemAssets; + +interface StoryboardItem { + src: string; + createTime?: number | null; + duration?: string | null; + flowId?: number | null; + id: number; + index: number; + projectId?: number | null; + prompt?: string | null; + reason?: string | null; + scriptId?: number | null; + state?: string | null; + trackId?: number | null; + videoDesc?: string | null; +} + +interface TrackItem { + id: number; + prompt: string; + state: "未生成" | "生成中" | "已完成" | "生成失败"; + reason?: string; + selectVideoId?: number | null; + medias: TrackMedia[]; + videoList: VideoItem[]; + duration: number; +} + +interface VideoItem { + id: number; + src: string; + state: "未生成" | "生成中" | "已完成" | "生成失败"; + errorReason?: string | null; +} +interface TrackMediaBase { + src: string; + id?: number; + prompt?: string; + fileType: "image" | "video" | "audio"; + slotType?: Type; // 本地保存时记录的 slot 类型,用于切换轨道时精确还原位置 + index?: number; +} + +interface TrackMediaStoryboard extends TrackMediaBase { + sources: "storyboard"; + index?: number; +} + +interface TrackMediaAssets extends TrackMediaBase { + sources: "assets"; +} + +interface TrackMediaUnknown extends TrackMediaBase { + sources?: string; +} + +type TrackMedia = TrackMediaStoryboard | TrackMediaAssets | TrackMediaUnknown; + +interface HistoryVideoItem { + errorReason?: string | null; + src: string; + id: number; + duration?: number | string | null; + projectId?: number | null; + scriptId?: number | null; + state?: string | null; + time?: number | null; + videoTrackId?: number | null; +} +interface ModelSetting { + mode: string; + model: string; + resolution: string; + duration: number; + audio: boolean; +} diff --git a/web-core/src/views/production/index.vue b/web-core/src/views/production/index.vue new file mode 100644 index 0000000..f37269e --- /dev/null +++ b/web-core/src/views/production/index.vue @@ -0,0 +1,669 @@ + + + + diff --git a/web-core/src/views/production/node/assets.vue b/web-core/src/views/production/node/assets.vue new file mode 100644 index 0000000..ddcba04 --- /dev/null +++ b/web-core/src/views/production/node/assets.vue @@ -0,0 +1,315 @@ + + + + + diff --git a/web-core/src/views/production/node/poster.vue b/web-core/src/views/production/node/poster.vue new file mode 100644 index 0000000..df34b0b --- /dev/null +++ b/web-core/src/views/production/node/poster.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/web-core/src/views/production/node/script.vue b/web-core/src/views/production/node/script.vue new file mode 100644 index 0000000..850930c --- /dev/null +++ b/web-core/src/views/production/node/script.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/web-core/src/views/production/node/scriptPlan.vue b/web-core/src/views/production/node/scriptPlan.vue new file mode 100644 index 0000000..76ade3f --- /dev/null +++ b/web-core/src/views/production/node/scriptPlan.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/web-core/src/views/production/node/storyboard.vue b/web-core/src/views/production/node/storyboard.vue new file mode 100644 index 0000000..fee60ee --- /dev/null +++ b/web-core/src/views/production/node/storyboard.vue @@ -0,0 +1,687 @@ + + + + + diff --git a/web-core/src/views/production/node/storyboardTable.vue b/web-core/src/views/production/node/storyboardTable.vue new file mode 100644 index 0000000..b314dd2 --- /dev/null +++ b/web-core/src/views/production/node/storyboardTable.vue @@ -0,0 +1,211 @@ + + + + + diff --git a/web-core/src/views/production/node/workbench.vue b/web-core/src/views/production/node/workbench.vue new file mode 100644 index 0000000..5189175 --- /dev/null +++ b/web-core/src/views/production/node/workbench.vue @@ -0,0 +1,138 @@ + + + + + diff --git a/web-core/src/views/production/utils/dagre.ts b/web-core/src/views/production/utils/dagre.ts new file mode 100644 index 0000000..096578f --- /dev/null +++ b/web-core/src/views/production/utils/dagre.ts @@ -0,0 +1,58 @@ +import dagre from "@dagrejs/dagre"; +import { Position, useVueFlow, type Node, type Edge } from "@vue-flow/core"; + +/** + * 用于对流程图节点进行自动布局的组合式函数。 + * 通过 dagre 库计算节点和边的布局位置。 + */ +export function useLayout(flowId?: string) { + const { findNode, fitView } = useVueFlow(flowId ? { id: flowId } : undefined); + + const graph = ref(new dagre.graphlib.Graph()); + const previousDirection = ref("LR"); // 上一次布局方向 + + function layout(nodes: Node[], edges: Edge[], direction: "LR" | "TB", nodesep: number = 250, ranksep?: number) { + // 每次都新建 Graph,避免遗留节点和边影响布局 + const dagreGraph = new dagre.graphlib.Graph(); + graph.value = dagreGraph; + + dagreGraph.setDefaultEdgeLabel(() => ({})); + dagreGraph.setGraph({ + rankdir: direction, + nodesep, + ranksep: ranksep ?? nodesep, + }); + previousDirection.value = direction; + + const isHorizontal = direction === "LR"; + + // 设置节点尺寸 + for (const node of nodes) { + const graphNode = findNode(node.id); + dagreGraph.setNode(node.id, { + width: graphNode?.dimensions.width || 150, + height: graphNode?.dimensions.height || 50, + }); + } + + // 设置边 + for (const edge of edges) { + dagreGraph.setEdge(edge.source, edge.target); + } + + // 执行布局计算 + dagre.layout(dagreGraph); + // 返回带有新位置的节点 + return nodes.map((node) => { + const pos = dagreGraph.node(node.id); + return { + ...node, + targetPosition: isHorizontal ? Position.Left : Position.Top, + sourcePosition: isHorizontal ? Position.Right : Position.Bottom, + position: { x: pos.x, y: pos.y }, + }; + }); + } + + return { graph, layout, previousDirection }; +} diff --git a/web-core/src/views/production/utils/editImageType.ts b/web-core/src/views/production/utils/editImageType.ts new file mode 100644 index 0000000..d3533bc --- /dev/null +++ b/web-core/src/views/production/utils/editImageType.ts @@ -0,0 +1,92 @@ +// ===== 节点数据类型 ===== +export interface UploadNodeData { + image: string; +} + +export interface GeneratedNodeData { + generatedImage?: string; + references: { image: string }[]; + prompt: string; + model?: string; + ratio?: string; + quality?: string; + steps: number; +} + +export interface NodeUploadData { + type: "upload"; + id: string; + position: { x: number; y: number }; + data: UploadNodeData; +} + +export interface NodeGeneratedData { + type: "generated"; + id: string; + position: { x: number; y: number }; + data: GeneratedNodeData; +} + +export type NodeType = NodeUploadData | NodeGeneratedData; + +// ===== 精简后用于传输的类型 ===== +export interface CleanNode { + id: string; + type: string; + position: { x: number; y: number }; + data: UploadNodeData | Omit; +} + +export interface CleanEdge { + id: string; + source: string; + target: string; +} + +// ===== 默认边样式 ===== +export const DEFAULT_EDGE_OPTIONS = { + type: "removeLine" as const, + animated: true, + style: { stroke: "#00000" }, +}; + +// ===== 默认生成节点数据 ===== +export function createGeneratedData(image = "", prompt = ""): GeneratedNodeData { + return { + generatedImage: image, + references: [], + prompt, + model: "", + ratio: "", + quality: "", + steps: 49, + }; +} + +// ===== 数据精简工具函数 ===== +export function cleanNodes(nodes: NodeType[]): CleanNode[] { + return nodes.map((n) => ({ + id: n.id, + type: n.type, + position: n.position, + data: + n.type === "upload" + ? { image: n.data.image } + : { + generatedImage: n.data.generatedImage, + references: n.data.references?.map((r) => ({ image: r.image })) ?? [], + prompt: n.data.prompt, + model: n.data.model, + ratio: n.data.ratio, + quality: n.data.quality, + }, + })); +} + +export function cleanEdges(edges: { id: string; source: string; target: string }[]): CleanEdge[] { + return edges.map((e) => ({ + id: e.id, + source: e.source, + target: e.target, + })); +} diff --git a/web-core/src/views/production/utils/flowBuilder.ts b/web-core/src/views/production/utils/flowBuilder.ts new file mode 100644 index 0000000..27f94e6 --- /dev/null +++ b/web-core/src/views/production/utils/flowBuilder.ts @@ -0,0 +1,264 @@ +import type { Ref } from "vue"; +import { computed } from "vue"; + +// ==================== 固定节点 ID ==================== +const NODE_IDS = { + script: "script", + scriptPlan: "scriptPlan", + assets: "assets", + storyboardTable: "storyboardTable", + storyboard: "storyboard", + workbench: "workbench", + poster: "poster", +} as const; + +type NodeId = (typeof NODE_IDS)[keyof typeof NODE_IDS]; + +// ==================== 类型定义 ==================== +export interface DeriveAsset { + id: number; + assetsId: number | null; + name: string; + prompt: string; + desc: string; + src: string; + flowId?: number; + state: "未生成" | "生成中" | "已完成" | "生成失败"; + type: "role" | "tool" | "scene" | "clip"; + errorReason?: string; +} + +export interface AssetItem { + id: number; + name: string; + desc: string; + prompt: string; + src: string; + state: "未生成" | "生成中" | "已完成" | "生成失败"; + type: "role" | "tool" | "scene" | "clip"; + flowId?: number; + derive: DeriveAsset[]; + errorReason?: string; +} + +export interface Storyboard { + id?: number; + duration?: number; + prompt: string; + trackId?: number; + associateAssetsIds?: number[]; + src: string | null; + state: "未生成" | "生成中" | "已完成" | "生成失败"; + flowId?: number; + reason?: string; + videoDesc: string; + shouldGenerateImage: number; +} + +interface VideoList { + id: number; + prompt: string; + duration: number; + storyboardId: number; + trackId: number; +} + +export interface FlowData { + script: string; + scriptPlan: string; + assets: AssetItem[]; + storyboardTable: string; + storyboard: Storyboard[]; + workbench: { + videoList: VideoList[]; + }; +} + +export type NodePositions = Record; + +// 边样式 +const edgeStyle = { + stroke: "#00000", + strokeWidth: 4, +}; + +// ==================== 构建函数 ==================== +export function useFlowBuilder(flowData: Ref, nodePositions: Ref) { + const nodes = computed(() => { + const data = flowData.value; + const positions = nodePositions.value; + const ids = NODE_IDS; + + const allNodes = [ + // 1. Script 节点 + { + id: ids.script, + type: "script", + dragHandle: ".dragHandle", + position: positions[ids.script] || { x: 0, y: 0 }, + data: { + script: data.script, + handleIds: { + assets: `${ids.script}-assets`, + source: `${ids.script}-source`, + }, + }, + }, + // 1.5 ScriptPlan 节点 + { + id: ids.scriptPlan, + type: "scriptPlan", + dragHandle: ".dragHandle", + position: positions[ids.scriptPlan] || { x: 0, y: 0 }, + data: { + scriptPlan: data.scriptPlan, + handleIds: { + target: `${ids.scriptPlan}-target`, + source: `${ids.scriptPlan}-source`, + }, + }, + }, + // 2. Assets 节点 + { + id: ids.assets, + type: "assets", + dragHandle: ".dragHandle", + position: positions[ids.assets] || { x: 0, y: 0 }, + data: { + assets: data.assets, + handleIds: { + target: `${ids.assets}-target`, + }, + }, + }, + // 3. StoryboardTable 节点 + { + id: ids.storyboardTable, + type: "storyboardTable", + dragHandle: ".dragHandle", + position: positions[ids.storyboardTable] || { x: 0, y: 0 }, + data: { + storyboardTable: data.storyboardTable, + handleIds: { + target: `${ids.storyboardTable}-target`, + source: `${ids.storyboardTable}-source`, + }, + }, + }, + // 4. Storyboard 节点 + { + id: ids.storyboard, + type: "storyboard", + dragHandle: ".dragHandle", + position: positions[ids.storyboard] || { x: 0, y: 0 }, + data: { + storyboard: data.storyboard, + handleIds: { + target: `${ids.storyboard}-target`, + source: `${ids.storyboard}-source`, + }, + }, + }, + // 5. Workbench 节点 + { + id: ids.workbench, + type: "workbench", + dragHandle: ".dragHandle", + position: positions[ids.workbench] || { x: 0, y: 0 }, + data: { + ...data.workbench, + handleIds: { + target: `${ids.workbench}-target`, + source: `${ids.workbench}-source`, + }, + }, + }, + // 6. Poster 节点 + // { + // id: ids.poster, + // type: "poster", + // dragHandle: ".dragHandle", + // position: positions[ids.poster] || { x: 0, y: 0 }, + // data: { + // items: data.poster?.items ?? [], + // handleIds: { + // target: `${ids.poster}-target`, + // }, + // }, + // }, + ]; + + return allNodes; + }); + + const edges = computed(() => { + const ids = NODE_IDS; + + const allEdges = [ + // Script -> Assets + { + id: `${ids.script}-${ids.assets}`, + source: ids.script, + target: ids.assets, + sourceHandle: `${ids.script}-assets`, + targetHandle: `${ids.assets}-target`, + animated: false, + style: edgeStyle, + }, + // Script -> StoryboardTable + { + id: `${ids.script}-${ids.scriptPlan}`, + source: ids.script, + target: ids.scriptPlan, + sourceHandle: `${ids.script}-source`, + targetHandle: `${ids.scriptPlan}-target`, + animated: false, + style: edgeStyle, + }, + // ScriptPlan -> StoryboardTable + { + id: `${ids.scriptPlan}-${ids.storyboardTable}`, + source: ids.scriptPlan, + target: ids.storyboardTable, + sourceHandle: `${ids.scriptPlan}-source`, + targetHandle: `${ids.storyboardTable}-target`, + animated: false, + style: edgeStyle, + }, + // StoryboardTable -> Storyboard + { + id: `${ids.storyboardTable}-${ids.storyboard}`, + source: ids.storyboardTable, + target: ids.storyboard, + sourceHandle: `${ids.storyboardTable}-source`, + targetHandle: `${ids.storyboard}-target`, + animated: false, + style: edgeStyle, + }, + // Storyboard -> Workbench + { + id: `${ids.storyboard}-${ids.workbench}`, + source: ids.storyboard, + target: ids.workbench, + sourceHandle: `${ids.storyboard}-source`, + targetHandle: `${ids.workbench}-target`, + animated: false, + style: edgeStyle, + }, + // Workbench -> Poster + // { + // id: `${ids.workbench}-${ids.poster}`, + // source: ids.workbench, + // target: ids.poster, + // sourceHandle: `${ids.workbench}-source`, + // targetHandle: `${ids.poster}-target`, + // animated: false, + // style: edgeStyle, + // }, + ]; + + return allEdges; + }); + + return { nodes, edges }; +} diff --git a/web-core/src/views/project/components/addProject.vue b/web-core/src/views/project/components/addProject.vue new file mode 100644 index 0000000..b4cc63b --- /dev/null +++ b/web-core/src/views/project/components/addProject.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/web-core/src/views/project/components/projectDialog.vue b/web-core/src/views/project/components/projectDialog.vue new file mode 100644 index 0000000..f9d650d --- /dev/null +++ b/web-core/src/views/project/components/projectDialog.vue @@ -0,0 +1,1280 @@ + + + + + diff --git a/web-core/src/views/project/index.vue b/web-core/src/views/project/index.vue new file mode 100644 index 0000000..82169ef --- /dev/null +++ b/web-core/src/views/project/index.vue @@ -0,0 +1,831 @@ + + + + + diff --git a/web-core/src/views/script/components/addScript.vue b/web-core/src/views/script/components/addScript.vue new file mode 100644 index 0000000..99ac3d0 --- /dev/null +++ b/web-core/src/views/script/components/addScript.vue @@ -0,0 +1,338 @@ + + + + + diff --git a/web-core/src/views/script/components/batchAddScript.vue b/web-core/src/views/script/components/batchAddScript.vue new file mode 100644 index 0000000..6cd6f46 --- /dev/null +++ b/web-core/src/views/script/components/batchAddScript.vue @@ -0,0 +1,340 @@ + + + + + diff --git a/web-core/src/views/script/components/editScript.vue b/web-core/src/views/script/components/editScript.vue new file mode 100644 index 0000000..b089d07 --- /dev/null +++ b/web-core/src/views/script/components/editScript.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/web-core/src/views/script/index.vue b/web-core/src/views/script/index.vue new file mode 100644 index 0000000..e31901e --- /dev/null +++ b/web-core/src/views/script/index.vue @@ -0,0 +1,495 @@ + + + + + diff --git a/web-core/src/views/scriptAgent/index copy.vue b/web-core/src/views/scriptAgent/index copy.vue new file mode 100644 index 0000000..117ed78 --- /dev/null +++ b/web-core/src/views/scriptAgent/index copy.vue @@ -0,0 +1,729 @@ + + + + + diff --git a/web-core/src/views/scriptAgent/index.vue b/web-core/src/views/scriptAgent/index.vue new file mode 100644 index 0000000..9760865 --- /dev/null +++ b/web-core/src/views/scriptAgent/index.vue @@ -0,0 +1,910 @@ + + + + + diff --git a/web-core/src/views/task/index.vue b/web-core/src/views/task/index.vue new file mode 100644 index 0000000..897a700 --- /dev/null +++ b/web-core/src/views/task/index.vue @@ -0,0 +1,225 @@ + + + + + diff --git a/web-core/src/views/taskList/index.vue b/web-core/src/views/taskList/index.vue new file mode 100644 index 0000000..37b1451 --- /dev/null +++ b/web-core/src/views/taskList/index.vue @@ -0,0 +1,163 @@ + + + + + diff --git a/web-core/src/views/test/index.vue b/web-core/src/views/test/index.vue new file mode 100644 index 0000000..1d8b829 --- /dev/null +++ b/web-core/src/views/test/index.vue @@ -0,0 +1,20 @@ + + + + + diff --git a/web-core/tsconfig.app.json b/web-core/tsconfig.app.json new file mode 100644 index 0000000..4646487 --- /dev/null +++ b/web-core/tsconfig.app.json @@ -0,0 +1,16 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["src/**/*", "src/**/*.vue"], + "compilerOptions": { + "ignoreDeprecations": "5.0", + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "exclude": ["node_modules", "dist", "coverage", "**/*.test.ts", "src/**/__tests__/*"] +} diff --git a/web-core/tsconfig.json b/web-core/tsconfig.json new file mode 100644 index 0000000..007c469 --- /dev/null +++ b/web-core/tsconfig.json @@ -0,0 +1,16 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" + } + ], + "exclude": [ + "node_modules", + "**/node_modules/*", + "dist" + ] +} \ No newline at end of file diff --git a/web-core/tsconfig.node.json b/web-core/tsconfig.node.json new file mode 100644 index 0000000..a13e4f4 --- /dev/null +++ b/web-core/tsconfig.node.json @@ -0,0 +1,38 @@ +{ + "include": [ + "vite.config.*", + "vitest.config.*", + "cypress.config.*", + "nightwatch.conf.*", + "playwright.config.*" + ], + "exclude": [ + "node_modules", + "**/node_modules/*", + "dist", + "coverage", + "**/*.test.ts" + ], + "compilerOptions": { + "ignoreDeprecations": "5.0", + "composite": true, + "noEmit": false, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "noImplicitAny": false, + "target": "ES2022", + "lib": [ + "ES2023" + ], + "module": "ESNext", + "moduleResolution": "Bundler", + "types": [ + "node", + "vite/client" + ], + "verbatimModuleSyntax": false, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "resolveJsonModule": true, + "skipLibCheck": true + } +} \ No newline at end of file diff --git a/web-core/vite.config.ts b/web-core/vite.config.ts new file mode 100644 index 0000000..ad4c516 --- /dev/null +++ b/web-core/vite.config.ts @@ -0,0 +1,94 @@ +import { fileURLToPath, URL } from "node:url"; +import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; +import AutoImport from "unplugin-auto-import/vite"; +import Components from "unplugin-vue-components/vite"; +import { TDesignResolver } from "@tdesign-vue-next/auto-import-resolver"; +import { viteSingleFile } from "vite-plugin-singlefile"; +import postcsspxtoviewport from "postcss-px-to-viewport"; + +export default defineConfig({ + base: "./", + build: { + assetsInlineLimit: Infinity, + rollupOptions: { + output: { + inlineDynamicImports: true, + }, + }, + }, + plugins: [ + vue(), + AutoImport({ + dts: "src/types/auto-imports.d.ts", + imports: ["vue", "pinia", "vue-router"], + resolvers: [ + TDesignResolver({ + library: "vue-next", + }), + TDesignResolver({ + library: "chat", + }), + ], + }), + Components({ + dts: "src/types/components.d.ts", + resolvers: [ + TDesignResolver({ + library: "vue-next", + }), + TDesignResolver({ + library: "chat", + }), + ], + }), + viteSingleFile(), + ], + resolve: { + alias: { + "@": fileURLToPath(new URL("./src", import.meta.url)), + }, + }, + css: { + preprocessorOptions: { + scss: { + api: "modern-compiler", + }, + }, + postcss: { + plugins: [ + postcsspxtoviewport({ + // 要转化的单位 + unitToConvert: "px", + // UI设计稿的大小 + viewportWidth: 1600, + // 转换后的精度 + unitPrecision: 4, + // 转换后的单位 + viewportUnit: "rem", + // 字体转换后的单位 + fontViewportUnit: "rem", + // 能转换的属性,*表示所有属性,!border表示border不转 + propList: ["*"], + // 指定不转换为视窗单位的类名, + selectorBlackList: ["ignore"], + // 最小转换的值,小于等于1不转 + minPixelValue: 1, + // 是否在媒体查询的css代码中也进行转换,默认false + mediaQuery: true, + // 是否转换后直接更换属性值 + replace: true, + // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件 + exclude: [], + // 包含那些文件或者特定文件 + include: [], + // 是否处理横屏情况 + landscape: false, + }), + ], + }, + }, + server: { + port: 50188, + }, +}); diff --git a/web-core/yarn.lock b/web-core/yarn.lock new file mode 100644 index 0000000..7df46e2 --- /dev/null +++ b/web-core/yarn.lock @@ -0,0 +1,3956 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + +"@babel/helper-validator-identifier@^7.28.5": + version "7.28.5" + resolved "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" + integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== + +"@babel/parser@^7.23.5", "@babel/parser@^7.29.0": + version "7.29.2" + resolved "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.2.tgz#58bd50b9a7951d134988a1ae177a35ef9a703ba1" + integrity sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA== + dependencies: + "@babel/types" "^7.29.0" + +"@babel/runtime@^7.16.5", "@babel/runtime@^7.22.6", "@babel/runtime@^7.24.7": + version "7.29.2" + resolved "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.2.tgz#9a6e2d05f4b6692e1801cd4fb176ad823930ed5e" + integrity sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g== + +"@babel/types@^7.29.0": + version "7.29.0" + resolved "https://registry.npmmirror.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" + integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + +"@braintree/sanitize-url@^6.0.0": + version "6.0.4" + resolved "https://registry.npmmirror.com/@braintree/sanitize-url/-/sanitize-url-6.0.4.tgz#923ca57e173c6b232bbbb07347b1be982f03e783" + integrity sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A== + +"@codemirror/autocomplete@^6.0.0", "@codemirror/autocomplete@^6.18.7", "@codemirror/autocomplete@^6.3.2", "@codemirror/autocomplete@^6.7.1": + version "6.20.1" + resolved "https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.20.1.tgz#4cfbc8b2e1e25f890ec34a081037e58b4e44143e" + integrity sha512-1cvg3Vz1dSSToCNlJfRA2WSI4ht3K+WplO0UMOgmUYPivCyy2oueZY6Lx7M9wThm7SDUBViRmuT+OG/i8+ON9A== + dependencies: + "@codemirror/language" "^6.0.0" + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.17.0" + "@lezer/common" "^1.0.0" + +"@codemirror/commands@^6.0.0", "@codemirror/commands@^6.8.1": + version "6.10.3" + resolved "https://registry.npmmirror.com/@codemirror/commands/-/commands-6.10.3.tgz#01877060befdec352e8300dec1f185489c300635" + integrity sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q== + dependencies: + "@codemirror/language" "^6.0.0" + "@codemirror/state" "^6.6.0" + "@codemirror/view" "^6.27.0" + "@lezer/common" "^1.1.0" + +"@codemirror/lang-angular@^0.1.0": + version "0.1.4" + resolved "https://registry.npmmirror.com/@codemirror/lang-angular/-/lang-angular-0.1.4.tgz#5b9e940786ba201a9a42eab6db9501fa3fe2292a" + integrity sha512-oap+gsltb/fzdlTQWD6BFF4bSLKcDnlxDsLdePiJpCVNKWXSTAbiiQeYI3UmES+BLAdkmIC1WjyztC1pi/bX4g== + dependencies: + "@codemirror/lang-html" "^6.0.0" + "@codemirror/lang-javascript" "^6.1.2" + "@codemirror/language" "^6.0.0" + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.3.3" + +"@codemirror/lang-cpp@^6.0.0": + version "6.0.3" + resolved "https://registry.npmmirror.com/@codemirror/lang-cpp/-/lang-cpp-6.0.3.tgz#b175b59fcde8dd6e563b7feee8bbed81963a9491" + integrity sha512-URM26M3vunFFn9/sm6rzqrBzDgfWuDixp85uTY49wKudToc2jTHUrKIGGKs+QWND+YLofNNZpxcNGRynFJfvgA== + dependencies: + "@codemirror/language" "^6.0.0" + "@lezer/cpp" "^1.0.0" + +"@codemirror/lang-css@^6.0.0", "@codemirror/lang-css@^6.2.0": + version "6.3.1" + resolved "https://registry.npmmirror.com/@codemirror/lang-css/-/lang-css-6.3.1.tgz#763ca41aee81bb2431be55e3cfcc7cc8e91421a3" + integrity sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg== + dependencies: + "@codemirror/autocomplete" "^6.0.0" + "@codemirror/language" "^6.0.0" + "@codemirror/state" "^6.0.0" + "@lezer/common" "^1.0.2" + "@lezer/css" "^1.1.7" + +"@codemirror/lang-go@^6.0.0": + version "6.0.1" + resolved "https://registry.npmmirror.com/@codemirror/lang-go/-/lang-go-6.0.1.tgz#598222c90f56eae28d11069c612ca64d0306b057" + integrity sha512-7fNvbyNylvqCphW9HD6WFnRpcDjr+KXX/FgqXy5H5ZS0eC5edDljukm/yNgYkwTsgp2busdod50AOTIy6Jikfg== + dependencies: + "@codemirror/autocomplete" "^6.0.0" + "@codemirror/language" "^6.6.0" + "@codemirror/state" "^6.0.0" + "@lezer/common" "^1.0.0" + "@lezer/go" "^1.0.0" + +"@codemirror/lang-html@^6.0.0": + version "6.4.11" + resolved "https://registry.npmmirror.com/@codemirror/lang-html/-/lang-html-6.4.11.tgz#c46ba46ae642fd567cf05c4129005d2913ac248d" + integrity sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw== + dependencies: + "@codemirror/autocomplete" "^6.0.0" + "@codemirror/lang-css" "^6.0.0" + "@codemirror/lang-javascript" "^6.0.0" + "@codemirror/language" "^6.4.0" + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.17.0" + "@lezer/common" "^1.0.0" + "@lezer/css" "^1.1.0" + "@lezer/html" "^1.3.12" + +"@codemirror/lang-java@^6.0.0": + version "6.0.2" + resolved "https://registry.npmmirror.com/@codemirror/lang-java/-/lang-java-6.0.2.tgz#601d5b3d774a4a997d11647ccb6c05702c54bd5b" + integrity sha512-m5Nt1mQ/cznJY7tMfQTJchmrjdjQ71IDs+55d1GAa8DGaB8JXWsVCkVT284C3RTASaY43YknrK2X3hPO/J3MOQ== + dependencies: + "@codemirror/language" "^6.0.0" + "@lezer/java" "^1.0.0" + +"@codemirror/lang-javascript@^6.0.0", "@codemirror/lang-javascript@^6.1.2": + version "6.2.5" + resolved "https://registry.npmmirror.com/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz#b9ea6b2f0383ed6895fae7888c0322541538f10a" + integrity sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A== + dependencies: + "@codemirror/autocomplete" "^6.0.0" + "@codemirror/language" "^6.6.0" + "@codemirror/lint" "^6.0.0" + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.17.0" + "@lezer/common" "^1.0.0" + "@lezer/javascript" "^1.0.0" + +"@codemirror/lang-jinja@^6.0.0": + version "6.0.0" + resolved "https://registry.npmmirror.com/@codemirror/lang-jinja/-/lang-jinja-6.0.0.tgz#cc02cd1e45d1fed1226e3c3b44615503f794c904" + integrity sha512-47MFmRcR8UAxd8DReVgj7WJN1WSAMT7OJnewwugZM4XiHWkOjgJQqvEM1NpMj9ALMPyxmlziEI1opH9IaEvmaw== + dependencies: + "@codemirror/lang-html" "^6.0.0" + "@codemirror/language" "^6.0.0" + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.2.0" + "@lezer/lr" "^1.4.0" + +"@codemirror/lang-json@^6.0.0": + version "6.0.2" + resolved "https://registry.npmmirror.com/@codemirror/lang-json/-/lang-json-6.0.2.tgz#054b160671306667e25d80385286049841836179" + integrity sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ== + dependencies: + "@codemirror/language" "^6.0.0" + "@lezer/json" "^1.0.0" + +"@codemirror/lang-less@^6.0.0": + version "6.0.2" + resolved "https://registry.npmmirror.com/@codemirror/lang-less/-/lang-less-6.0.2.tgz#2e3d82a3ddb8710e6409689cd4a28c66558d0cb8" + integrity sha512-EYdQTG22V+KUUk8Qq582g7FMnCZeEHsyuOJisHRft/mQ+ZSZ2w51NupvDUHiqtsOy7It5cHLPGfHQLpMh9bqpQ== + dependencies: + "@codemirror/lang-css" "^6.2.0" + "@codemirror/language" "^6.0.0" + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + +"@codemirror/lang-liquid@^6.0.0": + version "6.3.2" + resolved "https://registry.npmmirror.com/@codemirror/lang-liquid/-/lang-liquid-6.3.2.tgz#623bf5776d9069ddae371ac9a1bd1914d70107b8" + integrity sha512-6PDVU3ZnfeYyz1at1E/ttorErZvZFXXt1OPhtfe1EZJ2V2iDFa0CwPqPgG5F7NXN0yONGoBogKmFAafKTqlwIw== + dependencies: + "@codemirror/autocomplete" "^6.0.0" + "@codemirror/lang-html" "^6.0.0" + "@codemirror/language" "^6.0.0" + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.0.0" + "@lezer/common" "^1.0.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.3.1" + +"@codemirror/lang-markdown@^6.0.0", "@codemirror/lang-markdown@^6.3.4": + version "6.5.0" + resolved "https://registry.npmmirror.com/@codemirror/lang-markdown/-/lang-markdown-6.5.0.tgz#29df87310a555b007beba8e12893363956a26e8e" + integrity sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw== + dependencies: + "@codemirror/autocomplete" "^6.7.1" + "@codemirror/lang-html" "^6.0.0" + "@codemirror/language" "^6.3.0" + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.0.0" + "@lezer/common" "^1.2.1" + "@lezer/markdown" "^1.0.0" + +"@codemirror/lang-php@^6.0.0": + version "6.0.2" + resolved "https://registry.npmmirror.com/@codemirror/lang-php/-/lang-php-6.0.2.tgz#bdc439d195c8e73513bc5b971a99a57b5c99ee55" + integrity sha512-ZKy2v1n8Fc8oEXj0Th0PUMXzQJ0AIR6TaZU+PbDHExFwdu+guzOA4jmCHS1Nz4vbFezwD7LyBdDnddSJeScMCA== + dependencies: + "@codemirror/lang-html" "^6.0.0" + "@codemirror/language" "^6.0.0" + "@codemirror/state" "^6.0.0" + "@lezer/common" "^1.0.0" + "@lezer/php" "^1.0.0" + +"@codemirror/lang-python@^6.0.0": + version "6.2.1" + resolved "https://registry.npmmirror.com/@codemirror/lang-python/-/lang-python-6.2.1.tgz#37c9930716110156865a95c548aa0eef5552863a" + integrity sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw== + dependencies: + "@codemirror/autocomplete" "^6.3.2" + "@codemirror/language" "^6.8.0" + "@codemirror/state" "^6.0.0" + "@lezer/common" "^1.2.1" + "@lezer/python" "^1.1.4" + +"@codemirror/lang-rust@^6.0.0": + version "6.0.2" + resolved "https://registry.npmmirror.com/@codemirror/lang-rust/-/lang-rust-6.0.2.tgz#69146e6b3e8f961ef149059aecb9e07bfd7bf3bd" + integrity sha512-EZaGjCUegtiU7kSMvOfEZpaCReowEf3yNidYu7+vfuGTm9ow4mthAparY5hisJqOHmJowVH3Upu+eJlUji6qqA== + dependencies: + "@codemirror/language" "^6.0.0" + "@lezer/rust" "^1.0.0" + +"@codemirror/lang-sass@^6.0.0": + version "6.0.2" + resolved "https://registry.npmmirror.com/@codemirror/lang-sass/-/lang-sass-6.0.2.tgz#38c1b0a1326cc9f5cb2741d2cd51cfbcd7abc0b2" + integrity sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q== + dependencies: + "@codemirror/lang-css" "^6.2.0" + "@codemirror/language" "^6.0.0" + "@codemirror/state" "^6.0.0" + "@lezer/common" "^1.0.2" + "@lezer/sass" "^1.0.0" + +"@codemirror/lang-sql@^6.0.0": + version "6.10.0" + resolved "https://registry.npmmirror.com/@codemirror/lang-sql/-/lang-sql-6.10.0.tgz#49bfbf6cf31516a99e674da9a399f4426101a95a" + integrity sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w== + dependencies: + "@codemirror/autocomplete" "^6.0.0" + "@codemirror/language" "^6.0.0" + "@codemirror/state" "^6.0.0" + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + +"@codemirror/lang-vue@^0.1.1": + version "0.1.3" + resolved "https://registry.npmmirror.com/@codemirror/lang-vue/-/lang-vue-0.1.3.tgz#bf79b9152cc18b4903d64c1f67e186ae045c8a97" + integrity sha512-QSKdtYTDRhEHCfo5zOShzxCmqKJvgGrZwDQSdbvCRJ5pRLWBS7pD/8e/tH44aVQT6FKm0t6RVNoSUWHOI5vNug== + dependencies: + "@codemirror/lang-html" "^6.0.0" + "@codemirror/lang-javascript" "^6.1.2" + "@codemirror/language" "^6.0.0" + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.3.1" + +"@codemirror/lang-wast@^6.0.0": + version "6.0.2" + resolved "https://registry.npmmirror.com/@codemirror/lang-wast/-/lang-wast-6.0.2.tgz#d2b14175e5e80d7878cbbb29e20ec90dc12d3a2b" + integrity sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q== + dependencies: + "@codemirror/language" "^6.0.0" + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + +"@codemirror/lang-xml@^6.0.0": + version "6.1.0" + resolved "https://registry.npmmirror.com/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz#e3e786e1a89fdc9520efe75c1d6d3de1c40eb91c" + integrity sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg== + dependencies: + "@codemirror/autocomplete" "^6.0.0" + "@codemirror/language" "^6.4.0" + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.0.0" + "@lezer/common" "^1.0.0" + "@lezer/xml" "^1.0.0" + +"@codemirror/lang-yaml@^6.0.0": + version "6.1.2" + resolved "https://registry.npmmirror.com/@codemirror/lang-yaml/-/lang-yaml-6.1.2.tgz#c84280c68fa7af456a355d91183b5e537e9b7038" + integrity sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw== + dependencies: + "@codemirror/autocomplete" "^6.0.0" + "@codemirror/language" "^6.0.0" + "@codemirror/state" "^6.0.0" + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.2.0" + "@lezer/lr" "^1.0.0" + "@lezer/yaml" "^1.0.0" + +"@codemirror/language-data@^6.5.1": + version "6.5.2" + resolved "https://registry.npmmirror.com/@codemirror/language-data/-/language-data-6.5.2.tgz#404dc3d50a80d0f76bc8f81f93cc1d82e322417c" + integrity sha512-CPkWBKrNS8stYbEU5kwBwTf3JB1kghlbh4FSAwzGW2TEscdeHHH4FGysREW86Mqnj3Qn09s0/6Ea/TutmoTobg== + dependencies: + "@codemirror/lang-angular" "^0.1.0" + "@codemirror/lang-cpp" "^6.0.0" + "@codemirror/lang-css" "^6.0.0" + "@codemirror/lang-go" "^6.0.0" + "@codemirror/lang-html" "^6.0.0" + "@codemirror/lang-java" "^6.0.0" + "@codemirror/lang-javascript" "^6.0.0" + "@codemirror/lang-jinja" "^6.0.0" + "@codemirror/lang-json" "^6.0.0" + "@codemirror/lang-less" "^6.0.0" + "@codemirror/lang-liquid" "^6.0.0" + "@codemirror/lang-markdown" "^6.0.0" + "@codemirror/lang-php" "^6.0.0" + "@codemirror/lang-python" "^6.0.0" + "@codemirror/lang-rust" "^6.0.0" + "@codemirror/lang-sass" "^6.0.0" + "@codemirror/lang-sql" "^6.0.0" + "@codemirror/lang-vue" "^0.1.1" + "@codemirror/lang-wast" "^6.0.0" + "@codemirror/lang-xml" "^6.0.0" + "@codemirror/lang-yaml" "^6.0.0" + "@codemirror/language" "^6.0.0" + "@codemirror/legacy-modes" "^6.4.0" + +"@codemirror/language@^6.0.0", "@codemirror/language@^6.11.3", "@codemirror/language@^6.3.0", "@codemirror/language@^6.4.0", "@codemirror/language@^6.6.0", "@codemirror/language@^6.8.0": + version "6.12.2" + resolved "https://registry.npmmirror.com/@codemirror/language/-/language-6.12.2.tgz#7db5a46757411cf251e8f450474c05710c27d42c" + integrity sha512-jEPmz2nGGDxhRTg3lTpzmIyGKxz3Gp3SJES4b0nAuE5SWQoKdT5GoQ69cwMmFd+wvFUhYirtDTr0/DRHpQAyWg== + dependencies: + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.23.0" + "@lezer/common" "^1.5.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + style-mod "^4.0.0" + +"@codemirror/legacy-modes@^6.4.0": + version "6.5.2" + resolved "https://registry.npmmirror.com/@codemirror/legacy-modes/-/legacy-modes-6.5.2.tgz#7e2976c79007cd3fa9ed8a1d690892184a7f5ecf" + integrity sha512-/jJbwSTazlQEDOQw2FJ8LEEKVS72pU0lx6oM54kGpL8t/NJ2Jda3CZ4pcltiKTdqYSRk3ug1B3pil1gsjA6+8Q== + dependencies: + "@codemirror/language" "^6.0.0" + +"@codemirror/lint@^6.0.0": + version "6.9.5" + resolved "https://registry.npmmirror.com/@codemirror/lint/-/lint-6.9.5.tgz#c7da006f3335a33014799a7375c82df558e89f90" + integrity sha512-GElsbU9G7QT9xXhpUg1zWGmftA/7jamh+7+ydKRuT0ORpWS3wOSP0yT1FOlIZa7mIJjpVPipErsyvVqB9cfTFA== + dependencies: + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.35.0" + crelt "^1.0.5" + +"@codemirror/search@^6.0.0", "@codemirror/search@^6.5.11": + version "6.6.0" + resolved "https://registry.npmmirror.com/@codemirror/search/-/search-6.6.0.tgz#3b83a1e35391e1575a83a3b485e3f95263ddaa0b" + integrity sha512-koFuNXcDvyyotWcgOnZGmY7LZqEOXZaaxD/j6n18TCLx2/9HieZJ5H6hs1g8FiRxBD0DNfs0nXn17g872RmYdw== + dependencies: + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.37.0" + crelt "^1.0.5" + +"@codemirror/state@^6.0.0", "@codemirror/state@^6.5.2", "@codemirror/state@^6.6.0": + version "6.6.0" + resolved "https://registry.npmmirror.com/@codemirror/state/-/state-6.6.0.tgz#b88dbdc14aea4ace3c6d67bb77fe28bb84e4394e" + integrity sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ== + dependencies: + "@marijn/find-cluster-break" "^1.0.0" + +"@codemirror/view@^6.0.0", "@codemirror/view@^6.17.0", "@codemirror/view@^6.23.0", "@codemirror/view@^6.27.0", "@codemirror/view@^6.35.0", "@codemirror/view@^6.37.0", "@codemirror/view@^6.38.2": + version "6.40.0" + resolved "https://registry.npmmirror.com/@codemirror/view/-/view-6.40.0.tgz#97198fd717ebf471ef594a5bd557a9f2d1d4d165" + integrity sha512-WA0zdU7xfF10+5I3HhUUq3kqOx3KjqmtQ9lqZjfK7jtYk4G72YW9rezcSywpaUMCWOMlq+6E0pO1IWg1TNIhtg== + dependencies: + "@codemirror/state" "^6.6.0" + crelt "^1.0.6" + style-mod "^4.1.0" + w3c-keyname "^2.2.4" + +"@dagrejs/dagre@^3.0.0": + version "3.0.0" + resolved "https://registry.npmmirror.com/@dagrejs/dagre/-/dagre-3.0.0.tgz#543f20188f7494db0f45d634f7b3760747f87f23" + integrity sha512-ZzhnTy1rfuoew9Ez3EIw4L2znPGnYYhfn8vc9c4oB8iw6QAsszbiU0vRhlxWPFnmmNSFAkrYeF1PhM5m4lAN0Q== + dependencies: + "@dagrejs/graphlib" "4.0.1" + +"@dagrejs/graphlib@4.0.1": + version "4.0.1" + resolved "https://registry.npmmirror.com/@dagrejs/graphlib/-/graphlib-4.0.1.tgz#a9cf907cc5ddf9140a64360ad487766f17d1ee36" + integrity sha512-IvcV6FduIIAmLwnH+yun+QtV36SC7mERqa86aClNqmMN09WhmPPYU8ckHrZBozErf+UvHPWOTJYaGYiIcs0DgA== + +"@devui-design/icons@^1.4.0": + version "1.4.0" + resolved "https://registry.npmmirror.com/@devui-design/icons/-/icons-1.4.0.tgz#41fe1ce82e1aa6b111ca30c6a553cfc4a073fd47" + integrity sha512-taAX1RNW0QHUqQTRPqLTYTB2PZIqUplhWeF4hcmWkSTjpWlDNI40DssG/WRb3sISkfBk/4BMUxxC5XeTL3jo7A== + +"@esbuild/aix-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" + integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + +"@esbuild/android-arm@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" + integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + +"@esbuild/android-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" + integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + +"@esbuild/darwin-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + +"@esbuild/darwin-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + +"@esbuild/freebsd-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" + integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + +"@esbuild/freebsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" + integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + +"@esbuild/linux-arm@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" + integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + +"@esbuild/linux-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" + integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + +"@esbuild/linux-loong64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" + integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + +"@esbuild/linux-mips64el@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" + integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + +"@esbuild/linux-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" + integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + +"@esbuild/linux-riscv64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" + integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + +"@esbuild/linux-s390x@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" + integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + +"@esbuild/netbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" + integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + +"@esbuild/openbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" + integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + +"@esbuild/sunos-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" + integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + +"@esbuild/win32-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" + integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + +"@esbuild/win32-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" + integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + +"@esbuild/win32-x64@0.21.5": + version "0.21.5" + resolved "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" + integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== + +"@icon-park/vue-next@^1.4.2": + version "1.4.2" + resolved "https://registry.npmmirror.com/@icon-park/vue-next/-/vue-next-1.4.2.tgz#818c048100401620e8112beb2636f10171b195cc" + integrity sha512-+QklF255wkfBOabY+xw6FAI0Bwln/RhdwCunNy/9sKdKuChtaU67QZqU67KGAvZUTeeBgsL+yaHHxqfQeGZXEQ== + +"@intlify/core-base@11.3.0": + version "11.3.0" + resolved "https://registry.npmmirror.com/@intlify/core-base/-/core-base-11.3.0.tgz#55e612501f08b564011cb16d9f4a08e9a05df3cb" + integrity sha512-NNX5jIwF4TJBe7RtSKDMOA6JD9mp2mRcBHAwt2X+Q8PvnZub0yj5YYXlFu2AcESdgQpEv/5Yx2uOCV/yh7YkZg== + dependencies: + "@intlify/devtools-types" "11.3.0" + "@intlify/message-compiler" "11.3.0" + "@intlify/shared" "11.3.0" + +"@intlify/devtools-types@11.3.0": + version "11.3.0" + resolved "https://registry.npmmirror.com/@intlify/devtools-types/-/devtools-types-11.3.0.tgz#2670ac6a786ff56ac7b84f9af472771c7bc954fa" + integrity sha512-G9CNL4WpANWVdUjubOIIS7/D2j/0j+1KJmhBJxHilWNKr9mmt3IjFV3Hq4JoBP23uOoC5ynxz/FHZ42M+YxfGw== + dependencies: + "@intlify/core-base" "11.3.0" + "@intlify/shared" "11.3.0" + +"@intlify/message-compiler@11.3.0": + version "11.3.0" + resolved "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-11.3.0.tgz#84c5d2af4a2119f836c5f6835b4b88e5d4076d4b" + integrity sha512-RAJp3TMsqohg/Wa7bVF3cChRhecSYBLrTCQSj7j0UtWVFLP+6iEJoE2zb7GU5fp+fmG5kCbUdzhmlAUCWXiUJw== + dependencies: + "@intlify/shared" "11.3.0" + source-map-js "^1.0.2" + +"@intlify/shared@11.3.0": + version "11.3.0" + resolved "https://registry.npmmirror.com/@intlify/shared/-/shared-11.3.0.tgz#7abb22f05e502f3c6bf64dc771c448cdaa81d864" + integrity sha512-LC6P/uay7rXL5zZ5+5iRJfLs/iUN8apu9tm8YqQVmW3Uq3X4A0dOFUIDuAmB7gAC29wTHOS3EiN/IosNSz0eNQ== + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.13" + resolved "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz#6342a19f44347518c93e43b1ac69deb3c4656a1f" + integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/remapping@^2.3.5": + version "2.3.5" + resolved "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz#375c476d1972947851ba1e15ae8f123047445aa1" + integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5": + version "1.5.5" + resolved "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz#6912b00d2c631c0d15ce1a7ab57cd657f2a8f8ba" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@^0.3.24": + version "0.3.31" + resolved "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz#db15d6781c931f3a251a3dac39501c98a6082fd0" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@lezer/common@^1.0.0", "@lezer/common@^1.0.2", "@lezer/common@^1.1.0", "@lezer/common@^1.2.0", "@lezer/common@^1.2.1", "@lezer/common@^1.3.0", "@lezer/common@^1.5.0": + version "1.5.1" + resolved "https://registry.npmmirror.com/@lezer/common/-/common-1.5.1.tgz#6e8c114ff5d36a41148e146a253734d3bb8807d3" + integrity sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw== + +"@lezer/cpp@^1.0.0": + version "1.1.5" + resolved "https://registry.npmmirror.com/@lezer/cpp/-/cpp-1.1.5.tgz#de5b0352b4e0825b5cb62334f6a69f8ddc6ec734" + integrity sha512-DIhSXmYtJKLehrjzDFN+2cPt547ySQ41nA8yqcDf/GxMc+YM736xqltFkvADL2M0VebU5I+3+4ks2Vv+Kyq3Aw== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + +"@lezer/css@^1.1.0", "@lezer/css@^1.1.7": + version "1.3.1" + resolved "https://registry.npmmirror.com/@lezer/css/-/css-1.3.1.tgz#583e0119768021c58a731d38e56a91c700b57e14" + integrity sha512-PYAKeUVBo3HFThruRyp/iK91SwiZJnzXh8QzkQlwijB5y+N5iB28+iLk78o2zmKqqV0uolNhCwFqB8LA7b0Svg== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.3.0" + +"@lezer/go@^1.0.0": + version "1.0.1" + resolved "https://registry.npmmirror.com/@lezer/go/-/go-1.0.1.tgz#3004b54f5e4c9719edcba98653f380baf8c0d1a2" + integrity sha512-xToRsYxwsgJNHTgNdStpcvmbVuKxTapV0dM0wey1geMMRc9aggoVyKgzYp41D2/vVOx+Ii4hmE206kvxIXBVXQ== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.3.0" + +"@lezer/highlight@^1.0.0", "@lezer/highlight@^1.1.3", "@lezer/highlight@^1.2.0", "@lezer/highlight@^1.2.1": + version "1.2.3" + resolved "https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.2.3.tgz#a20f324b71148a2ea9ba6ff42e58bbfaec702857" + integrity sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g== + dependencies: + "@lezer/common" "^1.3.0" + +"@lezer/html@^1.3.12": + version "1.3.13" + resolved "https://registry.npmmirror.com/@lezer/html/-/html-1.3.13.tgz#6a1305ae3bd2c9c01f877f8a8dc1e15ec652d01c" + integrity sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + +"@lezer/java@^1.0.0": + version "1.1.3" + resolved "https://registry.npmmirror.com/@lezer/java/-/java-1.1.3.tgz#9efd6a29b4142d07f211076a6fb5e8061c85e147" + integrity sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + +"@lezer/javascript@^1.0.0": + version "1.5.4" + resolved "https://registry.npmmirror.com/@lezer/javascript/-/javascript-1.5.4.tgz#11746955f957d33c0933f17d7594db54a8b4beea" + integrity sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.1.3" + "@lezer/lr" "^1.3.0" + +"@lezer/json@^1.0.0": + version "1.0.3" + resolved "https://registry.npmmirror.com/@lezer/json/-/json-1.0.3.tgz#e773a012ad0088fbf07ce49cfba875cc9e5bc05f" + integrity sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + +"@lezer/lr@^1.0.0", "@lezer/lr@^1.1.0", "@lezer/lr@^1.3.0", "@lezer/lr@^1.3.1", "@lezer/lr@^1.3.3", "@lezer/lr@^1.4.0": + version "1.4.8" + resolved "https://registry.npmmirror.com/@lezer/lr/-/lr-1.4.8.tgz#333de9bc9346057323ff09beb4cda47ccc38a498" + integrity sha512-bPWa0Pgx69ylNlMlPvBPryqeLYQjyJjqPx+Aupm5zydLIF3NE+6MMLT8Yi23Bd9cif9VS00aUebn+6fDIGBcDA== + dependencies: + "@lezer/common" "^1.0.0" + +"@lezer/markdown@^1.0.0": + version "1.6.3" + resolved "https://registry.npmmirror.com/@lezer/markdown/-/markdown-1.6.3.tgz#04beb444f656c2319ddf23554b1e4b0edf536071" + integrity sha512-jpGm5Ps+XErS+xA4urw7ogEGkeZOahVQF21Z6oECF0sj+2liwZopd2+I8uH5I/vZsRuuze3OxBREIANLf6KKUw== + dependencies: + "@lezer/common" "^1.5.0" + "@lezer/highlight" "^1.0.0" + +"@lezer/php@^1.0.0": + version "1.0.5" + resolved "https://registry.npmmirror.com/@lezer/php/-/php-1.0.5.tgz#4e6b79daa97b98f0ba300f592e6916661339e661" + integrity sha512-W7asp9DhM6q0W6DYNwIkLSKOvxlXRrif+UXBMxzsJUuqmhE7oVU+gS3THO4S/Puh7Xzgm858UNaFi6dxTP8dJA== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.1.0" + +"@lezer/python@^1.1.4": + version "1.1.18" + resolved "https://registry.npmmirror.com/@lezer/python/-/python-1.1.18.tgz#fa02fbf492741c82dc2dc98a0a042bd0d4d7f1d3" + integrity sha512-31FiUrU7z9+d/ElGQLJFXl+dKOdx0jALlP3KEOsGTex8mvj+SoE1FgItcHWK/axkxCHGUSpqIHt6JAWfWu9Rhg== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + +"@lezer/rust@^1.0.0": + version "1.0.2" + resolved "https://registry.npmmirror.com/@lezer/rust/-/rust-1.0.2.tgz#cc9a75605d67182a0e799ac40b1965a61dcc6ef0" + integrity sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + +"@lezer/sass@^1.0.0": + version "1.1.0" + resolved "https://registry.npmmirror.com/@lezer/sass/-/sass-1.1.0.tgz#c82e660aa5b39303d1de763923aef979fef1d3a4" + integrity sha512-3mMGdCTUZ/84ArHOuXWQr37pnf7f+Nw9ycPUeKX+wu19b7pSMcZGLbaXwvD2APMBDOGxPmpK/O6S1v1EvLoqgQ== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + +"@lezer/xml@^1.0.0": + version "1.0.6" + resolved "https://registry.npmmirror.com/@lezer/xml/-/xml-1.0.6.tgz#908c203923288f854eb8e2f4d9b06c437e8610b9" + integrity sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.0.0" + +"@lezer/yaml@^1.0.0": + version "1.0.4" + resolved "https://registry.npmmirror.com/@lezer/yaml/-/yaml-1.0.4.tgz#66a622188f1984a71d34506759b5807699043589" + integrity sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw== + dependencies: + "@lezer/common" "^1.2.0" + "@lezer/highlight" "^1.0.0" + "@lezer/lr" "^1.4.0" + +"@marijn/find-cluster-break@^1.0.0": + version "1.0.2" + resolved "https://registry.npmmirror.com/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz#775374306116d51c0c500b8c4face0f9a04752d8" + integrity sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g== + +"@parcel/watcher-android-arm64@2.5.6": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz#5f32e0dba356f4ac9a11068d2a5c134ca3ba6564" + integrity sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A== + +"@parcel/watcher-darwin-arm64@2.5.6": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz#88d3e720b59b1eceffce98dac46d7c40e8be5e8e" + integrity sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA== + +"@parcel/watcher-darwin-x64@2.5.6": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz#bf05d76a78bc15974f15ec3671848698b0838063" + integrity sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg== + +"@parcel/watcher-freebsd-x64@2.5.6": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz#8bc26e9848e7303ac82922a5ae1b1ef1bdb48a53" + integrity sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng== + +"@parcel/watcher-linux-arm-glibc@2.5.6": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz#1328fee1deb0c2d7865079ef53a2ba4cc2f8b40a" + integrity sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ== + +"@parcel/watcher-linux-arm-musl@2.5.6": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz#bad0f45cb3e2157746db8b9d22db6a125711f152" + integrity sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg== + +"@parcel/watcher-linux-arm64-glibc@2.5.6": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz#b75913fbd501d9523c5f35d420957bf7d0204809" + integrity sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA== + +"@parcel/watcher-linux-arm64-musl@2.5.6": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz#da5621a6a576070c8c0de60dea8b46dc9c3827d4" + integrity sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA== + +"@parcel/watcher-linux-x64-glibc@2.5.6": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz#ce437accdc4b30f93a090b4a221fd95cd9b89639" + integrity sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ== + +"@parcel/watcher-linux-x64-musl@2.5.6": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz#02400c54b4a67efcc7e2327b249711920ac969e2" + integrity sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg== + +"@parcel/watcher-win32-arm64@2.5.6": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz#caae3d3c7583ca0a7171e6bd142c34d20ea1691e" + integrity sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q== + +"@parcel/watcher-win32-ia32@2.5.6": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz#9ac922550896dfe47bfc5ae3be4f1bcaf8155d6d" + integrity sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g== + +"@parcel/watcher-win32-x64@2.5.6": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz#73fdafba2e21c448f0e456bbe13178d8fe11739d" + integrity sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw== + +"@parcel/watcher@^2.4.1": + version "2.5.6" + resolved "https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.6.tgz#3f932828c894f06d0ad9cfefade1756ecc6ef1f1" + integrity sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ== + dependencies: + detect-libc "^2.0.3" + is-glob "^4.0.3" + node-addon-api "^7.0.0" + picomatch "^4.0.3" + optionalDependencies: + "@parcel/watcher-android-arm64" "2.5.6" + "@parcel/watcher-darwin-arm64" "2.5.6" + "@parcel/watcher-darwin-x64" "2.5.6" + "@parcel/watcher-freebsd-x64" "2.5.6" + "@parcel/watcher-linux-arm-glibc" "2.5.6" + "@parcel/watcher-linux-arm-musl" "2.5.6" + "@parcel/watcher-linux-arm64-glibc" "2.5.6" + "@parcel/watcher-linux-arm64-musl" "2.5.6" + "@parcel/watcher-linux-x64-glibc" "2.5.6" + "@parcel/watcher-linux-x64-musl" "2.5.6" + "@parcel/watcher-win32-arm64" "2.5.6" + "@parcel/watcher-win32-ia32" "2.5.6" + "@parcel/watcher-win32-x64" "2.5.6" + +"@popperjs/core@^2.11.8": + version "2.11.8" + resolved "https://registry.npmmirror.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + +"@rolldown/pluginutils@1.0.0-rc.2": + version "1.0.0-rc.2" + resolved "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz#10324e74cb3396cb7b616042ea7e9e6aa7d8d458" + integrity sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw== + +"@rollup/rollup-android-arm-eabi@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz#a6742c74c7d9d6d604ef8a48f99326b4ecda3d82" + integrity sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg== + +"@rollup/rollup-android-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz#97247be098de4df0c11971089fd2edf80a5da8cf" + integrity sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q== + +"@rollup/rollup-darwin-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz#674852cf14cf11b8056e0b1a2f4e872b523576cf" + integrity sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg== + +"@rollup/rollup-darwin-x64@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz#36dfd7ed0aaf4d9d89d9ef983af72632455b0246" + integrity sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w== + +"@rollup/rollup-freebsd-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz#2f87c2074b4220260fdb52a9996246edfc633c22" + integrity sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA== + +"@rollup/rollup-freebsd-x64@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz#9b5a26522a38a95dc06616d1939d4d9a76937803" + integrity sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg== + +"@rollup/rollup-linux-arm-gnueabihf@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz#86aa4859385a8734235b5e40a48e52d770758c3a" + integrity sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw== + +"@rollup/rollup-linux-arm-musleabihf@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz#cbe70e56e6ece8dac83eb773b624fc9e5a460976" + integrity sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA== + +"@rollup/rollup-linux-arm64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz#d14992a2e653bc3263d284bc6579b7a2890e1c45" + integrity sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA== + +"@rollup/rollup-linux-arm64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz#2fdd1ddc434ea90aeaa0851d2044789b4d07f6da" + integrity sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA== + +"@rollup/rollup-linux-loong64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz#8a181e6f89f969f21666a743cd411416c80099e7" + integrity sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg== + +"@rollup/rollup-linux-loong64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz#904125af2babc395f8061daa27b5af1f4e3f2f78" + integrity sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q== + +"@rollup/rollup-linux-ppc64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz#a57970ac6864c9a3447411a658224bdcf948be22" + integrity sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA== + +"@rollup/rollup-linux-ppc64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz#bb84de5b26870567a4267666e08891e80bb56a63" + integrity sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA== + +"@rollup/rollup-linux-riscv64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz#72d00d2c7fb375ce3564e759db33f17a35bffab9" + integrity sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg== + +"@rollup/rollup-linux-riscv64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz#4c166ef58e718f9245bd31873384ba15a5c1a883" + integrity sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg== + +"@rollup/rollup-linux-s390x-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz#bb5025cde9a61db478c2ca7215808ad3bce73a09" + integrity sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w== + +"@rollup/rollup-linux-x64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz#9b66b1f9cd95c6624c788f021c756269ffed1552" + integrity sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg== + +"@rollup/rollup-linux-x64-musl@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz#b007ca255dc7166017d57d7d2451963f0bd23fd9" + integrity sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg== + +"@rollup/rollup-openbsd-x64@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz#e8b357b2d1aa2c8d76a98f5f0d889eabe93f4ef9" + integrity sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ== + +"@rollup/rollup-openharmony-arm64@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz#96c2e3f4aacd3d921981329831ff8dde492204dc" + integrity sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA== + +"@rollup/rollup-win32-arm64-msvc@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz#2d865149d706d938df8b4b8f117e69a77646d581" + integrity sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A== + +"@rollup/rollup-win32-ia32-msvc@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz#abe1593be0fa92325e9971c8da429c5e05b92c36" + integrity sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA== + +"@rollup/rollup-win32-x64-gnu@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz#c4af3e9518c9a5cd4b1c163dc81d0ad4d82e7eab" + integrity sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA== + +"@rollup/rollup-win32-x64-msvc@4.59.0": + version "4.59.0" + resolved "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz#4584a8a87b29188a4c1fe987a9fcf701e256d86c" + integrity sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA== + +"@socket.io/component-emitter@~3.1.0": + version "3.1.2" + resolved "https://registry.npmmirror.com/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz#821f8442f4175d8f0467b9daf26e3a18e2d02af2" + integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== + +"@tdesign-vue-next/auto-import-resolver@^0.1.6": + version "0.1.6" + resolved "https://registry.npmmirror.com/@tdesign-vue-next/auto-import-resolver/-/auto-import-resolver-0.1.6.tgz#e54e358b7e36055be279155722a1fbbf773a9591" + integrity sha512-VljSYxwP7x2S5d+OpA8AbQqB2UsJdL6iSDTTykON9Gwa2lF082Jtfq3GUigbhIoOUmk13U8ZKg2oS1XYbowLsw== + dependencies: + unplugin-utils "^0.3.0" + unplugin-vue-components "^29.0.0" + +"@tdesign-vue-next/chat@^0.5.1": + version "0.5.1" + resolved "https://registry.npmmirror.com/@tdesign-vue-next/chat/-/chat-0.5.1.tgz#3dbb965f666f4dc2833e5f3a8195710193e2de73" + integrity sha512-lfXZz/B0gHWQdy59/WWzDe10Tqu6MbuXMdcoGnuAiSp+aPreyEOq38tNJRmSVB/WV6ymtnW4PT916V5Dn7+B6A== + dependencies: + "@babel/runtime" "^7.22.6" + "@types/lodash-es" "^4.17.12" + clipboard "^2.0.11" + highlight.js "^11.9.0" + lodash-es "^4.17.21" + marked "^12.0.1" + marked-highlight "^2.1.1" + omi-vueify "^0.0.12" + tdesign-icons-vue-next "~0.4.2" + tdesign-vue-next "^1.18.3" + tdesign-web-components "1.3.1-alpha.10" + +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.npmmirror.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + +"@tsconfig/node22@^22.0.0": + version "22.0.5" + resolved "https://registry.npmmirror.com/@tsconfig/node22/-/node22-22.0.5.tgz#56be9f34ccffd3aa85f9cc7365ad9765d5829adb" + integrity sha512-hLf2ld+sYN/BtOJjHUWOk568dvjFQkHnLNa6zce25GIH+vxKfvTgm3qpaH6ToF5tu/NN0IH66s+Bb5wElHrLcw== + +"@types/codemirror@^0.0.108": + version "0.0.108" + resolved "https://registry.npmmirror.com/@types/codemirror/-/codemirror-0.0.108.tgz#e640422b666bf49251b384c390cdeb2362585bde" + integrity sha512-3FGFcus0P7C2UOGCNUVENqObEb4SFk+S8Dnxq7K6aIsLVs/vDtlangl3PEO0ykaKXyK56swVF6Nho7VsA44uhw== + dependencies: + "@types/tern" "*" + +"@types/estree@*", "@types/estree@1.0.8", "@types/estree@^1.0.0": + version "1.0.8" + resolved "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e" + integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w== + +"@types/license-checker@^25.0.6": + version "25.0.6" + resolved "https://registry.npmmirror.com/@types/license-checker/-/license-checker-25.0.6.tgz#c346285ee7e42bac58a4922059453f50a5d4175d" + integrity sha512-ju/75+YPkNE5vX1iPer+qtI1eI/LqJVYZgOsmSHI1iiEM1bQL5Gh1lEvyjR9T7ZXVE1FwJa2doWJEEmPNwbZkw== + +"@types/linkify-it@^5": + version "5.0.0" + resolved "https://registry.npmmirror.com/@types/linkify-it/-/linkify-it-5.0.0.tgz#21413001973106cda1c3a9b91eedd4ccd5469d76" + integrity sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q== + +"@types/lodash-es@^4.17.12": + version "4.17.12" + resolved "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b" + integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.17.24" + resolved "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.24.tgz#4ae334fc62c0e915ca8ed8e35dcc6d4eeb29215f" + integrity sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ== + +"@types/markdown-it@^14.0.1": + version "14.1.2" + resolved "https://registry.npmmirror.com/@types/markdown-it/-/markdown-it-14.1.2.tgz#57f2532a0800067d9b934f3521429a2e8bfb4c61" + integrity sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog== + dependencies: + "@types/linkify-it" "^5" + "@types/mdurl" "^2" + +"@types/mdurl@^2": + version "2.0.0" + resolved "https://registry.npmmirror.com/@types/mdurl/-/mdurl-2.0.0.tgz#d43878b5b20222682163ae6f897b20447233bdfd" + integrity sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg== + +"@types/node@^22.9.0": + version "22.19.15" + resolved "https://registry.npmmirror.com/@types/node/-/node-22.19.15.tgz#6091d99fdf7c08cb57dc8b1345d407ba9a1df576" + integrity sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg== + dependencies: + undici-types "~6.21.0" + +"@types/sortablejs@^1.15.1", "@types/sortablejs@^1.15.8": + version "1.15.9" + resolved "https://registry.npmmirror.com/@types/sortablejs/-/sortablejs-1.15.9.tgz#82d2337f54d4db827914d80dee5cf65539ae3c8b" + integrity sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ== + +"@types/splitpanes@^2.2.6": + version "2.2.6" + resolved "https://registry.npmmirror.com/@types/splitpanes/-/splitpanes-2.2.6.tgz#4f3509f12557549239131b053ca2ababeaa45668" + integrity sha512-3dV5sO1Ht74iER4jJU03mreL3f+Q2h47ZqXS6Sfbqc6hkCvsDrX1GA0NbYWRdNvZemPyTDzUoApWKeoGbALwkQ== + dependencies: + vue "^2.0.0" + +"@types/tern@*": + version "0.23.9" + resolved "https://registry.npmmirror.com/@types/tern/-/tern-0.23.9.tgz#6f6093a4a9af3e6bb8dde528e024924d196b367c" + integrity sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw== + dependencies: + "@types/estree" "*" + +"@types/tinycolor2@^1.4.3": + version "1.4.6" + resolved "https://registry.npmmirror.com/@types/tinycolor2/-/tinycolor2-1.4.6.tgz#670cbc0caf4e58dd61d1e3a6f26386e473087f06" + integrity sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw== + +"@types/trusted-types@^2.0.7": + version "2.0.7" + resolved "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== + +"@types/uuid@^11.0.0": + version "11.0.0" + resolved "https://registry.npmmirror.com/@types/uuid/-/uuid-11.0.0.tgz#f4fa34bbf2af941148ef1973c4361fc43617971c" + integrity sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA== + dependencies: + uuid "*" + +"@types/validator@^13.7.17": + version "13.15.10" + resolved "https://registry.npmmirror.com/@types/validator/-/validator-13.15.10.tgz#742b77ec34d58554b94a76a14cef30d59e3c16b9" + integrity sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA== + +"@types/web-bluetooth@^0.0.20": + version "0.0.20" + resolved "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz#f066abfcd1cbe66267cdbbf0de010d8a41b41597" + integrity sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow== + +"@types/web-bluetooth@^0.0.21": + version "0.0.21" + resolved "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.21.tgz#525433c784aed9b457aaa0ee3d92aeb71f346b63" + integrity sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA== + +"@vavt/copy2clipboard@^1.0.1": + version "1.0.3" + resolved "https://registry.npmmirror.com/@vavt/copy2clipboard/-/copy2clipboard-1.0.3.tgz#eae77af8e83835cae5d932c3dc025e8838bb4c12" + integrity sha512-HtG48r2FBYp9eRvGB3QGmtRBH1zzRRAVvFbGgFstOwz4/DDaNiX0uZc3YVKPydqgOav26pibr9MtoCaWxn7aeA== + +"@vavt/util@^2.1.0": + version "2.1.1" + resolved "https://registry.npmmirror.com/@vavt/util/-/util-2.1.1.tgz#5e7b04d57e44d679cbaa37502d9e996176bed5a0" + integrity sha512-QYn/B2qiQhAGZVBoe5/6pPcA3NiGcxIm9htpVBjcdIOZQaWe+8kE/akEgLPrHxv5dbQQawnyHBokadW1d1z1tw== + +"@vitejs/plugin-vue@^6.0.5": + version "6.0.5" + resolved "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-6.0.5.tgz#20ebb46c4da069753d9cfb1309c4334213cc3f7b" + integrity sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg== + dependencies: + "@rolldown/pluginutils" "1.0.0-rc.2" + +"@volar/language-core@2.4.15": + version "2.4.15" + resolved "https://registry.npmmirror.com/@volar/language-core/-/language-core-2.4.15.tgz#759d04cb4eab9920560b8bcfa4515d5b08a1b7ce" + integrity sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA== + dependencies: + "@volar/source-map" "2.4.15" + +"@volar/source-map@2.4.15": + version "2.4.15" + resolved "https://registry.npmmirror.com/@volar/source-map/-/source-map-2.4.15.tgz#18aba09994c0268e59a418f9d738e4a85302781d" + integrity sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg== + +"@volar/typescript@2.4.15": + version "2.4.15" + resolved "https://registry.npmmirror.com/@volar/typescript/-/typescript-2.4.15.tgz#1445d23f8e4f9ad821b6bfa58cf4a2b980dc5f97" + integrity sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg== + dependencies: + "@volar/language-core" "2.4.15" + path-browserify "^1.0.1" + vscode-uri "^3.0.8" + +"@vue-flow/background@^1.3.2": + version "1.3.2" + resolved "https://registry.npmmirror.com/@vue-flow/background/-/background-1.3.2.tgz#0c90cd05e5d60da017bbaf5a1c3eb6af7ed9b778" + integrity sha512-eJPhDcLj1wEo45bBoqTXw1uhl0yK2RaQGnEINqvvBsAFKh/camHJd5NPmOdS1w+M9lggc9igUewxaEd3iCQX2w== + +"@vue-flow/controls@^1.1.3": + version "1.1.3" + resolved "https://registry.npmmirror.com/@vue-flow/controls/-/controls-1.1.3.tgz#40866b553101fbef22d2b9a043965ed76fca4b2c" + integrity sha512-XCf+G+jCvaWURdFlZmOjifZGw3XMhN5hHlfMGkWh9xot+9nH9gdTZtn+ldIJKtarg3B21iyHU8JjKDhYcB6JMw== + +"@vue-flow/core@^1.48.2": + version "1.48.2" + resolved "https://registry.npmmirror.com/@vue-flow/core/-/core-1.48.2.tgz#cef8641b17f6220c257d4208bdb2082cee882225" + integrity sha512-raxhgKWE+G/mcEvXJjGFUDYW9rAI3GOtiHR3ZkNpwBWuIaCC1EYiBmKGwJOoNzVFgwO7COgErnK7i08i287AFA== + dependencies: + "@vueuse/core" "^10.5.0" + d3-drag "^3.0.0" + d3-interpolate "^3.0.1" + d3-selection "^3.0.0" + d3-zoom "^3.0.0" + +"@vue/compiler-core@3.5.30": + version "3.5.30" + resolved "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.30.tgz#0f984da9207f24f9ddfb700052a43247a953fd9a" + integrity sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw== + dependencies: + "@babel/parser" "^7.29.0" + "@vue/shared" "3.5.30" + entities "^7.0.1" + estree-walker "^2.0.2" + source-map-js "^1.2.1" + +"@vue/compiler-dom@3.5.30", "@vue/compiler-dom@^3.5.0": + version "3.5.30" + resolved "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.30.tgz#a38dbdd520479244c8b673123b4bd06a82e733ee" + integrity sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g== + dependencies: + "@vue/compiler-core" "3.5.30" + "@vue/shared" "3.5.30" + +"@vue/compiler-sfc@2.7.16": + version "2.7.16" + resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz#ff81711a0fac9c68683d8bb00b63f857de77dc83" + integrity sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg== + dependencies: + "@babel/parser" "^7.23.5" + postcss "^8.4.14" + source-map "^0.6.1" + optionalDependencies: + prettier "^1.18.2 || ^2.0.0" + +"@vue/compiler-sfc@3.5.30": + version "3.5.30" + resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz#5c716d844f240154263e99b25fba6e1802c0c8c6" + integrity sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A== + dependencies: + "@babel/parser" "^7.29.0" + "@vue/compiler-core" "3.5.30" + "@vue/compiler-dom" "3.5.30" + "@vue/compiler-ssr" "3.5.30" + "@vue/shared" "3.5.30" + estree-walker "^2.0.2" + magic-string "^0.30.21" + postcss "^8.5.8" + source-map-js "^1.2.1" + +"@vue/compiler-ssr@3.5.30": + version "3.5.30" + resolved "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.30.tgz#e9b407d7e56be1e307a7621f2e8d2501267ff1d0" + integrity sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA== + dependencies: + "@vue/compiler-dom" "3.5.30" + "@vue/shared" "3.5.30" + +"@vue/compiler-vue2@^2.7.16": + version "2.7.16" + resolved "https://registry.npmmirror.com/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz#2ba837cbd3f1b33c2bc865fbe1a3b53fb611e249" + integrity sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A== + dependencies: + de-indent "^1.0.2" + he "^1.2.0" + +"@vue/devtools-api@^6.5.0", "@vue/devtools-api@^6.6.3", "@vue/devtools-api@^6.6.4": + version "6.6.4" + resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz#cbe97fe0162b365edc1dba80e173f90492535343" + integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g== + +"@vue/language-core@2.2.12": + version "2.2.12" + resolved "https://registry.npmmirror.com/@vue/language-core/-/language-core-2.2.12.tgz#d01f7e865f593f968cb65c12a13d8337e65641f0" + integrity sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA== + dependencies: + "@volar/language-core" "2.4.15" + "@vue/compiler-dom" "^3.5.0" + "@vue/compiler-vue2" "^2.7.16" + "@vue/shared" "^3.5.0" + alien-signals "^1.0.3" + minimatch "^9.0.3" + muggle-string "^0.4.1" + path-browserify "^1.0.1" + +"@vue/reactivity@3.5.30": + version "3.5.30" + resolved "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.30.tgz#1ff13f7d570b16b4f009f007772c7b71be1dd09d" + integrity sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q== + dependencies: + "@vue/shared" "3.5.30" + +"@vue/runtime-core@3.5.30": + version "3.5.30" + resolved "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.30.tgz#abe448b25e88f583b1847323a2f19f5e4a21837d" + integrity sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg== + dependencies: + "@vue/reactivity" "3.5.30" + "@vue/shared" "3.5.30" + +"@vue/runtime-dom@3.5.30": + version "3.5.30" + resolved "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.30.tgz#41d1b6424b754300f735c2ecb1a7457b4125dab3" + integrity sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw== + dependencies: + "@vue/reactivity" "3.5.30" + "@vue/runtime-core" "3.5.30" + "@vue/shared" "3.5.30" + csstype "^3.2.3" + +"@vue/server-renderer@3.5.30": + version "3.5.30" + resolved "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.30.tgz#116515063d609d3ceca1170f3b09122f24f187b5" + integrity sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ== + dependencies: + "@vue/compiler-ssr" "3.5.30" + "@vue/shared" "3.5.30" + +"@vue/shared@3.5.30", "@vue/shared@^3.5.0": + version "3.5.30" + resolved "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.30.tgz#5d7a0d3ca151647484303fd9f057e2e13ecb80ef" + integrity sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ== + +"@vue/tsconfig@^0.5.1": + version "0.5.1" + resolved "https://registry.npmmirror.com/@vue/tsconfig/-/tsconfig-0.5.1.tgz#3124ec16cc0c7e04165b88dc091e6b97782fffa9" + integrity sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ== + +"@vueuse/core@^10.5.0": + version "10.11.1" + resolved "https://registry.npmmirror.com/@vueuse/core/-/core-10.11.1.tgz#15d2c0b6448d2212235b23a7ba29c27173e0c2c6" + integrity sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww== + dependencies: + "@types/web-bluetooth" "^0.0.20" + "@vueuse/metadata" "10.11.1" + "@vueuse/shared" "10.11.1" + vue-demi ">=0.14.8" + +"@vueuse/core@^14.1.0": + version "14.2.1" + resolved "https://registry.npmmirror.com/@vueuse/core/-/core-14.2.1.tgz#b5cf36a07b4ea973381e18523ad0ed6ddc98a5be" + integrity sha512-3vwDzV+GDUNpdegRY6kzpLm4Igptq+GA0QkJ3W61Iv27YWwW/ufSlOfgQIpN6FZRMG0mkaz4gglJRtq5SeJyIQ== + dependencies: + "@types/web-bluetooth" "^0.0.21" + "@vueuse/metadata" "14.2.1" + "@vueuse/shared" "14.2.1" + +"@vueuse/metadata@10.11.1": + version "10.11.1" + resolved "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-10.11.1.tgz#209db7bb5915aa172a87510b6de2ca01cadbd2a7" + integrity sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw== + +"@vueuse/metadata@14.2.1": + version "14.2.1" + resolved "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-14.2.1.tgz#bd3338a565c2f651b9d18ac0f8825aa6077ee461" + integrity sha512-1ButlVtj5Sb/HDtIy1HFr1VqCP4G6Ypqt5MAo0lCgjokrk2mvQKsK2uuy0vqu/Ks+sHfuHo0B9Y9jn9xKdjZsw== + +"@vueuse/shared@10.11.1": + version "10.11.1" + resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-10.11.1.tgz#62b84e3118ae6e1f3ff38f4fbe71b0c5d0f10938" + integrity sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA== + dependencies: + vue-demi ">=0.14.8" + +"@vueuse/shared@14.2.1": + version "14.2.1" + resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-14.2.1.tgz#829a271147937f6b105bb1422d3171e6142f47ba" + integrity sha512-shTJncjV9JTI4oVNyF1FQonetYAiTBd+Qj7cY89SWbXSkx7gyhrgtEdF2ZAVWS1S3SHlaROO6F2IesJxQEkZBw== + +"@webav/av-canvas@^1.2.8": + version "1.2.8" + resolved "https://registry.npmmirror.com/@webav/av-canvas/-/av-canvas-1.2.8.tgz#87a059ef51b6b61e331f06e183fdb1fccd0bf0a5" + integrity sha512-j7wvlmdQcwSeMDPiOjfnw2LR/+F/yqYScs9dzwh8SHQre6nnnxS/eOPBghhgVx/RlqFk6VR4UYtnXlhfgnhWtA== + dependencies: + "@webav/av-cliper" "1.2.8" + "@webav/internal-utils" "1.2.8" + +"@webav/av-cliper@1.2.8", "@webav/av-cliper@^1.2.8": + version "1.2.8" + resolved "https://registry.npmmirror.com/@webav/av-cliper/-/av-cliper-1.2.8.tgz#d1ad2e65f8baa0f37dca91c070d582ff4f5d5daa" + integrity sha512-lxCXgP5AJsl+6xRo1puU5lTv7rAsxX9StQVK8xYdbg02p9FswCt76iTPqNxmlJ0ZfV1g+lmyNufLXu/qBAnJnA== + dependencies: + "@webav/internal-utils" "1.2.8" + "@webav/mp4box.js" "^0.5.7" + opfs-tools "^0.7.2" + wave-resampler "^1.0.0" + +"@webav/internal-utils@1.2.8": + version "1.2.8" + resolved "https://registry.npmmirror.com/@webav/internal-utils/-/internal-utils-1.2.8.tgz#dd16608363eb1bedbbb6e592bfdd0bdcba26d353" + integrity sha512-ZiEXl4U6PRqotkux0eTUdVn5zxbzXYm4Xfjt+HvH5bowwGSJVho/mmbPrdPf3ZRtfIu0aWCWa04LUDBQuY5ltA== + dependencies: + "@webav/mp4box.js" "^0.5.7" + +"@webav/mp4box.js@^0.5.7": + version "0.5.7" + resolved "https://registry.npmmirror.com/@webav/mp4box.js/-/mp4box.js-0.5.7.tgz#fa100d724c3ddd4cd24fb1243f8a274823c49d38" + integrity sha512-KgScEtDpiLnWvj1Rj8Ri9fDWxTjfdNr7cmY/PT88EmttHh6cLxMAeyD/W8Vydes4z7qV/c5VDAaP4cTlaO0E9g== + +"@xmldom/xmldom@^0.8.6": + version "0.8.11" + resolved "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.11.tgz#b79de2d67389734c57c52595f7a7305e30c2d608" + integrity sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw== + +abab@^2.0.5, abab@^2.0.6: + version "2.0.6" + resolved "https://registry.npmmirror.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +abbrev@1: + version "1.1.1" + resolved "https://registry.npmmirror.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.npmmirror.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" + integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.15.0, acorn@^8.16.0, acorn@^8.5.0: + version "8.16.0" + resolved "https://registry.npmmirror.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + +agent-base@6: + version "6.0.2" + resolved "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +alien-signals@^1.0.3: + version "1.0.13" + resolved "https://registry.npmmirror.com/alien-signals/-/alien-signals-1.0.13.tgz#8d6db73462f742ee6b89671fbd8c37d0b1727a7e" + integrity sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +argparse@~1.0.3: + version "1.0.10" + resolved "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +array-find-index@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw== + +asap@^2.0.0: + version "2.0.6" + resolved "https://registry.npmmirror.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios@^1.13.2: + version "1.13.6" + resolved "https://registry.npmmirror.com/axios/-/axios-1.13.6.tgz#c3f92da917dc209a15dd29936d20d5089b6b6c98" + integrity sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ== + dependencies: + follow-redirects "^1.15.11" + form-data "^4.0.5" + proxy-from-env "^1.1.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +binary-extensions@^2.0.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== + +bluebird@~3.4.0: + version "3.4.7" + resolved "https://registry.npmmirror.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3" + integrity sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA== + +brace-expansion@^1.1.7: + version "1.1.12" + resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz#ab9b454466e5a8cc3a187beaad580412a9c5b843" + integrity sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.2: + version "2.0.2" + resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.3, braces@~3.0.2: + version "3.0.3" + resolved "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + +chalk@^2.4.1: + version "2.4.2" + resolved "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +cherry-markdown@^0.11.0-alpha-5: + version "0.11.0-alpha-5" + resolved "https://registry.npmmirror.com/cherry-markdown/-/cherry-markdown-0.11.0-alpha-5.tgz#aa2bd6c1fc4b2cf09f1b33178f00a1b845c41906" + integrity sha512-k+gFcg0MJUseONICrdtBR/pzAwSVjIbFvVriSDxoU2pXJxHF/kuw2s2MgBvzW8VYAajJVGVP/kam9nLuIwsMRQ== + dependencies: + "@types/codemirror" "^0.0.108" + crypto-js "^4.2.0" + dompurify "^3.2.6" + htmlparser2 "^10.0.0" + jsdom "~19.0.0" + ws "^8.18.0" + optionalDependencies: + mermaid "9.4.3" + +chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chokidar@^4.0.0: + version "4.0.3" + resolved "https://registry.npmmirror.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + +chokidar@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/chokidar/-/chokidar-5.0.0.tgz#949c126a9238a80792be9a0265934f098af369a5" + integrity sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw== + dependencies: + readdirp "^5.0.0" + +class-variance-authority@^0.7.0: + version "0.7.1" + resolved "https://registry.npmmirror.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz#4008a798a0e4553a781a57ac5177c9fb5d043787" + integrity sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg== + dependencies: + clsx "^2.1.1" + +clipboard@^2.0.11: + version "2.0.11" + resolved "https://registry.npmmirror.com/clipboard/-/clipboard-2.0.11.tgz#62180360b97dd668b6b3a84ec226975762a70be5" + integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw== + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + +clsx@^2.1.0, clsx@^2.1.1: + version "2.1.1" + resolved "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + +codemirror@^6.0.2: + version "6.0.2" + resolved "https://registry.npmmirror.com/codemirror/-/codemirror-6.0.2.tgz#4d3fea1ad60b6753f97ca835f2f48c6936a8946e" + integrity sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw== + dependencies: + "@codemirror/autocomplete" "^6.0.0" + "@codemirror/commands" "^6.0.0" + "@codemirror/language" "^6.0.0" + "@codemirror/lint" "^6.0.0" + "@codemirror/search" "^6.0.0" + "@codemirror/state" "^6.0.0" + "@codemirror/view" "^6.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@7: + version "7.2.0" + resolved "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" + integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== + +commander@^2.20.3: + version "2.20.3" + resolved "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +confbox@^0.1.8: + version "0.1.8" + resolved "https://registry.npmmirror.com/confbox/-/confbox-0.1.8.tgz#820d73d3b3c82d9bd910652c5d4d599ef8ff8b06" + integrity sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w== + +confbox@^0.2.2: + version "0.2.4" + resolved "https://registry.npmmirror.com/confbox/-/confbox-0.2.4.tgz#592e7be71f882a4a874e3c88f0ac1ef6f7da1ce5" + integrity sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ== + +construct-style-sheets-polyfill@3.0.1: + version "3.0.1" + resolved "https://registry.npmmirror.com/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-3.0.1.tgz#7f8669c819db070c913c44515606c9706c18beb6" + integrity sha512-vyAiTcS3i29IxjzJ12K2MGUUBYgrF69p6fKD+8ZPRLFhwTtuaxXLpazl4Yw4FITsQXwUMrh8juI1bOHWYjzkwA== + +copy-to-clipboard@^3.3.3: + version "3.3.3" + resolved "https://registry.npmmirror.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" + integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== + dependencies: + toggle-selection "^1.0.6" + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + +cose-base@^1.0.0: + version "1.0.3" + resolved "https://registry.npmmirror.com/cose-base/-/cose-base-1.0.3.tgz#650334b41b869578a543358b80cda7e0abe0a60a" + integrity sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg== + dependencies: + layout-base "^1.0.0" + +cose-base@^2.2.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/cose-base/-/cose-base-2.2.0.tgz#1c395c35b6e10bb83f9769ca8b817d614add5c01" + integrity sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g== + dependencies: + layout-base "^2.0.0" + +crelt@^1.0.5, crelt@^1.0.6: + version "1.0.6" + resolved "https://registry.npmmirror.com/crelt/-/crelt-1.0.6.tgz#7cc898ea74e190fb6ef9dae57f8f81cf7302df72" + integrity sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g== + +crypto-js@^4.2.0: + version "4.2.0" + resolved "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" + integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== + +cssfilter@0.0.10: + version "0.0.10" + resolved "https://registry.npmmirror.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae" + integrity sha512-FAaLDaplstoRsDR8XGYH51znUN0UY7nMc6Z9/fvE8EXGwvJE9hu7W2vHwx1+bd6gCYnln9nLbzxFTrcO9YQDZw== + +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.npmmirror.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.npmmirror.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + +csstype@^3.1.0, csstype@^3.2.3: + version "3.2.3" + resolved "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + +cytoscape-cose-bilkent@^4.1.0: + version "4.1.0" + resolved "https://registry.npmmirror.com/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz#762fa121df9930ffeb51a495d87917c570ac209b" + integrity sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ== + dependencies: + cose-base "^1.0.0" + +cytoscape-fcose@^2.1.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz#e4d6f6490df4fab58ae9cea9e5c3ab8d7472f471" + integrity sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ== + dependencies: + cose-base "^2.2.0" + +cytoscape@^3.23.0: + version "3.33.1" + resolved "https://registry.npmmirror.com/cytoscape/-/cytoscape-3.33.1.tgz#449e05d104b760af2912ab76482d24c01cdd4c97" + integrity sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ== + +"d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3, d3-array@^3.2.0: + version "3.2.4" + resolved "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" + integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== + dependencies: + internmap "1 - 2" + +d3-axis@3: + version "3.0.0" + resolved "https://registry.npmmirror.com/d3-axis/-/d3-axis-3.0.0.tgz#c42a4a13e8131d637b745fc2973824cfeaf93322" + integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw== + +d3-brush@3: + version "3.0.0" + resolved "https://registry.npmmirror.com/d3-brush/-/d3-brush-3.0.0.tgz#6f767c4ed8dcb79de7ede3e1c0f89e63ef64d31c" + integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ== + dependencies: + d3-dispatch "1 - 3" + d3-drag "2 - 3" + d3-interpolate "1 - 3" + d3-selection "3" + d3-transition "3" + +d3-chord@3: + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-chord/-/d3-chord-3.0.1.tgz#d156d61f485fce8327e6abf339cb41d8cbba6966" + integrity sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g== + dependencies: + d3-path "1 - 3" + +"d3-color@1 - 3", d3-color@3: + version "3.1.0" + resolved "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2" + integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA== + +d3-contour@4: + version "4.0.2" + resolved "https://registry.npmmirror.com/d3-contour/-/d3-contour-4.0.2.tgz#bb92063bc8c5663acb2422f99c73cbb6c6ae3bcc" + integrity sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA== + dependencies: + d3-array "^3.2.0" + +d3-delaunay@6: + version "6.0.4" + resolved "https://registry.npmmirror.com/d3-delaunay/-/d3-delaunay-6.0.4.tgz#98169038733a0a5babbeda55054f795bb9e4a58b" + integrity sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A== + dependencies: + delaunator "5" + +"d3-dispatch@1 - 3", d3-dispatch@3: + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e" + integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg== + +"d3-drag@2 - 3", d3-drag@3, d3-drag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba" + integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg== + dependencies: + d3-dispatch "1 - 3" + d3-selection "3" + +"d3-dsv@1 - 3", d3-dsv@3: + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73" + integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q== + dependencies: + commander "7" + iconv-lite "0.6" + rw "1" + +"d3-ease@1 - 3", d3-ease@3: + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4" + integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w== + +d3-fetch@3: + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-fetch/-/d3-fetch-3.0.1.tgz#83141bff9856a0edb5e38de89cdcfe63d0a60a22" + integrity sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw== + dependencies: + d3-dsv "1 - 3" + +d3-force@3: + version "3.0.0" + resolved "https://registry.npmmirror.com/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4" + integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg== + dependencies: + d3-dispatch "1 - 3" + d3-quadtree "1 - 3" + d3-timer "1 - 3" + +"d3-format@1 - 3", d3-format@3: + version "3.1.2" + resolved "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.2.tgz#01fdb46b58beb1f55b10b42ad70b6e344d5eb2ae" + integrity sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg== + +d3-geo@3: + version "3.1.1" + resolved "https://registry.npmmirror.com/d3-geo/-/d3-geo-3.1.1.tgz#6027cf51246f9b2ebd64f99e01dc7c3364033a4d" + integrity sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q== + dependencies: + d3-array "2.5.0 - 3" + +d3-hierarchy@3: + version "3.1.2" + resolved "https://registry.npmmirror.com/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz#b01cd42c1eed3d46db77a5966cf726f8c09160c6" + integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA== + +"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3, d3-interpolate@^3.0.1: + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d" + integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g== + dependencies: + d3-color "1 - 3" + +"d3-path@1 - 3", d3-path@3, d3-path@^3.1.0: + version "3.1.0" + resolved "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526" + integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ== + +d3-polygon@3: + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-polygon/-/d3-polygon-3.0.1.tgz#0b45d3dd1c48a29c8e057e6135693ec80bf16398" + integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg== + +"d3-quadtree@1 - 3", d3-quadtree@3: + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f" + integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw== + +d3-random@3: + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4" + integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ== + +d3-scale-chromatic@3: + version "3.1.0" + resolved "https://registry.npmmirror.com/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#34c39da298b23c20e02f1a4b239bd0f22e7f1314" + integrity sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ== + dependencies: + d3-color "1 - 3" + d3-interpolate "1 - 3" + +d3-scale@4: + version "4.0.2" + resolved "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396" + integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ== + dependencies: + d3-array "2.10.0 - 3" + d3-format "1 - 3" + d3-interpolate "1.2.0 - 3" + d3-time "2.1.1 - 3" + d3-time-format "2 - 4" + +"d3-selection@2 - 3", d3-selection@3, d3-selection@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31" + integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ== + +d3-shape@3: + version "3.2.0" + resolved "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5" + integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA== + dependencies: + d3-path "^3.1.0" + +"d3-time-format@2 - 4", d3-time-format@4: + version "4.1.0" + resolved "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3: + version "3.1.0" + resolved "https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" + integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + dependencies: + d3-array "2 - 3" + +"d3-timer@1 - 3", d3-timer@3: + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0" + integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA== + +"d3-transition@2 - 3", d3-transition@3: + version "3.0.1" + resolved "https://registry.npmmirror.com/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f" + integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w== + dependencies: + d3-color "1 - 3" + d3-dispatch "1 - 3" + d3-ease "1 - 3" + d3-interpolate "1 - 3" + d3-timer "1 - 3" + +d3-zoom@3, d3-zoom@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3" + integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw== + dependencies: + d3-dispatch "1 - 3" + d3-drag "2 - 3" + d3-interpolate "1 - 3" + d3-selection "2 - 3" + d3-transition "2 - 3" + +d3@^7.4.0, d3@^7.8.2: + version "7.9.0" + resolved "https://registry.npmmirror.com/d3/-/d3-7.9.0.tgz#579e7acb3d749caf8860bd1741ae8d371070cd5d" + integrity sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA== + dependencies: + d3-array "3" + d3-axis "3" + d3-brush "3" + d3-chord "3" + d3-color "3" + d3-contour "4" + d3-delaunay "6" + d3-dispatch "3" + d3-drag "3" + d3-dsv "3" + d3-ease "3" + d3-fetch "3" + d3-force "3" + d3-format "3" + d3-geo "3" + d3-hierarchy "3" + d3-interpolate "3" + d3-path "3" + d3-polygon "3" + d3-quadtree "3" + d3-random "3" + d3-scale "4" + d3-scale-chromatic "3" + d3-selection "3" + d3-shape "3" + d3-time "3" + d3-time-format "4" + d3-timer "3" + d3-transition "3" + d3-zoom "3" + +dagre-d3-es@7.0.9: + version "7.0.9" + resolved "https://registry.npmmirror.com/dagre-d3-es/-/dagre-d3-es-7.0.9.tgz#aca12fccd9d09955a4430029ba72ee6934542a8d" + integrity sha512-rYR4QfVmy+sR44IBDvVtcAmOReGBvRCWDpO2QjYwqgh9yijw6eSHBqaPG/LIOEy7aBsniLvtMW6pg19qJhq60w== + dependencies: + d3 "^7.8.2" + lodash-es "^4.17.21" + +data-urls@^3.0.1: + version "3.0.2" + resolved "https://registry.npmmirror.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" + integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + +dayjs@^1.11.10, dayjs@^1.11.19, dayjs@^1.11.7: + version "1.11.20" + resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.20.tgz#88d919fd639dc991415da5f4cb6f1b6650811938" + integrity sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ== + +de-indent@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" + integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg== + +debug@4, debug@^4.4.3, debug@~4.4.1: + version "4.4.3" + resolved "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" + integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== + dependencies: + ms "^2.1.3" + +debug@^3.1.0: + version "3.2.7" + resolved "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debuglog@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" + integrity sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw== + +decimal.js@^10.3.1: + version "10.6.0" + resolved "https://registry.npmmirror.com/decimal.js/-/decimal.js-10.6.0.tgz#e649a43e3ab953a72192ff5983865e509f37ed9a" + integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg== + +defu@^6.1.4: + version "6.1.4" + resolved "https://registry.npmmirror.com/defu/-/defu-6.1.4.tgz#4e0c9cf9ff68fe5f3d7f2765cc1a012dfdcb0479" + integrity sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg== + +delaunator@5: + version "5.0.1" + resolved "https://registry.npmmirror.com/delaunator/-/delaunator-5.0.1.tgz#39032b08053923e924d6094fe2cde1a99cc51278" + integrity sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw== + dependencies: + robust-predicates "^3.0.2" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +delegate@^3.1.2: + version "3.2.0" + resolved "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" + integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== + +detect-libc@^2.0.3: + version "2.1.2" + resolved "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== + +dezalgo@^1.0.0: + version "1.0.4" + resolved "https://registry.npmmirror.com/dezalgo/-/dezalgo-1.0.4.tgz#751235260469084c132157dfa857f386d4c33d81" + integrity sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig== + dependencies: + asap "^2.0.0" + wrappy "1" + +dingbat-to-unicode@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz#5091dd673241453e6b5865e26e5a4452cdef5c83" + integrity sha512-98l0sW87ZT58pU4i61wa2OHwxbiYSbuxsCBozaVnYX2iCnr3bLM3fIes1/ej7h1YdOKuKt/MLs706TVnALA65w== + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +dompurify@2.4.3: + version "2.4.3" + resolved "https://registry.npmmirror.com/dompurify/-/dompurify-2.4.3.tgz#f4133af0e6a50297fc8874e2eaedc13a3c308c03" + integrity sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ== + +dompurify@3.2.7: + version "3.2.7" + resolved "https://registry.npmmirror.com/dompurify/-/dompurify-3.2.7.tgz#721d63913db5111dd6dfda8d3a748cfd7982d44a" + integrity sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw== + optionalDependencies: + "@types/trusted-types" "^2.0.7" + +dompurify@^3.2.6: + version "3.3.3" + resolved "https://registry.npmmirror.com/dompurify/-/dompurify-3.3.3.tgz#680cae8af3e61320ddf3666a3bc843f7b291b2b6" + integrity sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA== + optionalDependencies: + "@types/trusted-types" "^2.0.7" + +domutils@^3.2.2: + version "3.2.2" + resolved "https://registry.npmmirror.com/domutils/-/domutils-3.2.2.tgz#edbfe2b668b0c1d97c24baf0f1062b132221bc78" + integrity sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + +duck@^0.1.12: + version "0.1.12" + resolved "https://registry.npmmirror.com/duck/-/duck-0.1.12.tgz#de7adf758421230b6d7aee799ce42670586b9efa" + integrity sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg== + dependencies: + underscore "^1.13.1" + +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + +elkjs@^0.8.2: + version "0.8.2" + resolved "https://registry.npmmirror.com/elkjs/-/elkjs-0.8.2.tgz#c37763c5a3e24e042e318455e0147c912a7c248e" + integrity sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ== + +engine.io-client@~6.6.1: + version "6.6.4" + resolved "https://registry.npmmirror.com/engine.io-client/-/engine.io-client-6.6.4.tgz#a04998787dd342b543eec5d4452da7bb540e7ff8" + integrity sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.4.1" + engine.io-parser "~5.2.1" + ws "~8.18.3" + xmlhttprequest-ssl "~2.1.1" + +engine.io-parser@~5.2.1: + version "5.2.3" + resolved "https://registry.npmmirror.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f" + integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q== + +entities@^4.2.0, entities@^4.4.0: + version "4.5.0" + resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +entities@^7.0.1: + version "7.0.1" + resolved "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz#26e8a88889db63417dcb9a1e79a3f1bc92b5976b" + integrity sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA== + +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +esbuild@^0.21.3: + version "0.21.5" + resolved "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" + integrity sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw== + optionalDependencies: + "@esbuild/aix-ppc64" "0.21.5" + "@esbuild/android-arm" "0.21.5" + "@esbuild/android-arm64" "0.21.5" + "@esbuild/android-x64" "0.21.5" + "@esbuild/darwin-arm64" "0.21.5" + "@esbuild/darwin-x64" "0.21.5" + "@esbuild/freebsd-arm64" "0.21.5" + "@esbuild/freebsd-x64" "0.21.5" + "@esbuild/linux-arm" "0.21.5" + "@esbuild/linux-arm64" "0.21.5" + "@esbuild/linux-ia32" "0.21.5" + "@esbuild/linux-loong64" "0.21.5" + "@esbuild/linux-mips64el" "0.21.5" + "@esbuild/linux-ppc64" "0.21.5" + "@esbuild/linux-riscv64" "0.21.5" + "@esbuild/linux-s390x" "0.21.5" + "@esbuild/linux-x64" "0.21.5" + "@esbuild/netbsd-x64" "0.21.5" + "@esbuild/openbsd-x64" "0.21.5" + "@esbuild/sunos-x64" "0.21.5" + "@esbuild/win32-arm64" "0.21.5" + "@esbuild/win32-ia32" "0.21.5" + "@esbuild/win32-x64" "0.21.5" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8" + integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw== + +escodegen@^2.0.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" + integrity sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionalDependencies: + source-map "~0.6.1" + +esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +exsolve@^1.0.7: + version "1.0.8" + resolved "https://registry.npmmirror.com/exsolve/-/exsolve-1.0.8.tgz#7f5e34da61cd1116deda5136e62292c096f50613" + integrity sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA== + +fast-json-patch@3.1.1: + version "3.1.1" + resolved "https://registry.npmmirror.com/fast-json-patch/-/fast-json-patch-3.1.1.tgz#85064ea1b1ebf97a3f7ad01e23f9337e72c66947" + integrity sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ== + +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +follow-redirects@^1.15.11: + version "1.15.11" + resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340" + integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ== + +form-data@^4.0.0, form-data@^4.0.5: + version "4.0.5" + resolved "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz#b49e48858045ff4cbf6b03e1805cebcad3679053" + integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.12" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.1: + version "7.2.3" + resolved "https://registry.npmmirror.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.npmmirror.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + integrity sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw== + dependencies: + delegate "^3.1.2" + +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + +graceful-fs@^4.1.2: + version "4.2.11" + resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.npmmirror.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +highlight.js@^11.9.0: + version "11.11.1" + resolved "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz#fca06fa0e5aeecf6c4d437239135fabc15213585" + integrity sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w== + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +htm@^3.1.1: + version "3.1.1" + resolved "https://registry.npmmirror.com/htm/-/htm-3.1.1.tgz#49266582be0dc66ed2235d5ea892307cc0c24b78" + integrity sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ== + +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== + dependencies: + whatwg-encoding "^2.0.0" + +htmlparser2@^10.0.0: + version "10.1.0" + resolved "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-10.1.0.tgz#fe3f2e12c73b6e462d4e10395db9c1119e4d6ae4" + integrity sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.2.2" + entities "^7.0.1" + +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + +iconv-lite@0.6, iconv-lite@0.6.3: + version "0.6.3" + resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + +immer@^10.1.1: + version "10.2.0" + resolved "https://registry.npmmirror.com/immer/-/immer-10.2.0.tgz#88a4ce06a1af64172d254b70f7cb04df51c871b1" + integrity sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw== + +immutable@^5.1.5: + version "5.1.5" + resolved "https://registry.npmmirror.com/immutable/-/immutable-5.1.5.tgz#93ee4db5c2a9ab42a4a783069f3c5d8847d40165" + integrity sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.16.1: + version "2.16.1" + resolved "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +js-confetti@^0.13.1: + version "0.13.1" + resolved "https://registry.npmmirror.com/js-confetti/-/js-confetti-0.13.1.tgz#0f171b8242dae9ce1a17fa4e09251f26a75800b1" + integrity sha512-Kau3apum0zYIvELfZv0lL5VY7dvzpbKi4Oq0XdfjDKdNZc/O+IeGRyLEej7GJ/1APbASvb+ZTWDxmTxxSFkTOg== + +js-tokens@^9.0.1: + version "9.0.1" + resolved "https://registry.npmmirror.com/js-tokens/-/js-tokens-9.0.1.tgz#2ec43964658435296f6761b34e10671c2d9527f4" + integrity sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ== + +jsdom@~19.0.0: + version "19.0.0" + resolved "https://registry.npmmirror.com/jsdom/-/jsdom-19.0.0.tgz#93e67c149fe26816d38a849ea30ac93677e16b6a" + integrity sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A== + dependencies: + abab "^2.0.5" + acorn "^8.5.0" + acorn-globals "^6.0.0" + cssom "^0.5.0" + cssstyle "^2.3.0" + data-urls "^3.0.1" + decimal.js "^10.3.1" + domexception "^4.0.0" + escodegen "^2.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^3.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^10.0.0" + ws "^8.2.3" + xml-name-validator "^4.0.0" + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +jszip@^3.7.1: + version "3.10.1" + resolved "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" + +khroma@^2.0.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/khroma/-/khroma-2.1.0.tgz#45f2ce94ce231a437cf5b63c2e886e6eb42bbbb1" + integrity sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw== + +layout-base@^1.0.0: + version "1.0.2" + resolved "https://registry.npmmirror.com/layout-base/-/layout-base-1.0.2.tgz#1291e296883c322a9dd4c5dd82063721b53e26e2" + integrity sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg== + +layout-base@^2.0.0: + version "2.0.1" + resolved "https://registry.npmmirror.com/layout-base/-/layout-base-2.0.1.tgz#d0337913586c90f9c2c075292069f5c2da5dd285" + integrity sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg== + +license-checker@^25.0.1: + version "25.0.1" + resolved "https://registry.npmmirror.com/license-checker/-/license-checker-25.0.1.tgz#4d14504478a5240a857bb3c21cd0491a00d761fa" + integrity sha512-mET5AIwl7MR2IAKYYoVBBpV0OnkKQ1xGj2IMMeEFIs42QAkEVjRtFZGWmQ28WeU7MP779iAgOaOy93Mn44mn6g== + dependencies: + chalk "^2.4.1" + debug "^3.1.0" + mkdirp "^0.5.1" + nopt "^4.0.1" + read-installed "~4.0.3" + semver "^5.5.0" + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + spdx-satisfies "^4.0.0" + treeify "^1.1.0" + +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + +linkify-it@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421" + integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ== + dependencies: + uc.micro "^2.0.0" + +local-pkg@^1.1.2: + version "1.1.2" + resolved "https://registry.npmmirror.com/local-pkg/-/local-pkg-1.1.2.tgz#c03d208787126445303f8161619dc701afa4abb5" + integrity sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A== + dependencies: + mlly "^1.7.4" + pkg-types "^2.3.0" + quansync "^0.2.11" + +lodash-es@^4.17.21: + version "4.17.23" + resolved "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.23.tgz#58c4360fd1b5d33afc6c0bbd3d1149349b1138e0" + integrity sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg== + +lodash@^4.17.23: + version "4.17.23" + resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" + integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== + +lop@^0.4.2: + version "0.4.2" + resolved "https://registry.npmmirror.com/lop/-/lop-0.4.2.tgz#c9c2f958a39b9da1c2f36ca9ad66891a9fe84640" + integrity sha512-RefILVDQ4DKoRZsJ4Pj22TxE3omDO47yFpkIBoDKzkqPRISs5U1cnAdg/5583YPkWPaLIYHOKRMQSvjFsO26cw== + dependencies: + duck "^0.1.12" + option "~0.2.1" + underscore "^1.13.1" + +lru-cache@^11.2.1: + version "11.2.7" + resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.2.7.tgz#9127402617f34cd6767b96daee98c28e74458d35" + integrity sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA== + +lucide-vue-next@^0.543.0: + version "0.543.0" + resolved "https://registry.npmmirror.com/lucide-vue-next/-/lucide-vue-next-0.543.0.tgz#3ac7f63ec60ed485730372513c7fe41e95250d4f" + integrity sha512-Az5kpNm/koKAwSNIKjsZ4uHV2tVfmlQlcHwFBygQ8gc5/jFg7An9OrxgDy/aE5m+HLx7VfLYqDxLr8gWecZbQA== + +magic-string@^0.30.19, magic-string@^0.30.21: + version "0.30.21" + resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz#56763ec09a0fa8091df27879fd94d19078c00d91" + integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.5" + +mammoth@^1.12.0: + version "1.12.0" + resolved "https://registry.npmmirror.com/mammoth/-/mammoth-1.12.0.tgz#102ed719a933ac08ed65b37e9856622f1051effb" + integrity sha512-cwnK1RIcRdDMi2HRx2EXGYlxqIEh0Oo3bLhorgnsVJi2UkbX1+jKxuBNR9PC5+JaX7EkmJxFPmo6mjLpqShI2w== + dependencies: + "@xmldom/xmldom" "^0.8.6" + argparse "~1.0.3" + base64-js "^1.5.1" + bluebird "~3.4.0" + dingbat-to-unicode "^1.0.1" + jszip "^3.7.1" + lop "^0.4.2" + path-is-absolute "^1.0.0" + underscore "^1.13.1" + xmlbuilder "^10.0.0" + +markdown-it-fence@^0.1.3: + version "0.1.3" + resolved "https://registry.npmmirror.com/markdown-it-fence/-/markdown-it-fence-0.1.3.tgz#26e90149b7de5658cdb27096b2e2b5ec4e72d6ce" + integrity sha512-mdZbkwJUyZXhyDX4QzD+WnIhxWBq9oIIJU22E6jCT7MHgIp79oEOJfwXIl/HqmfIxnXEdC47sBGn4h6chM4DKw== + +markdown-it-image-figures@^2.1.1: + version "2.1.1" + resolved "https://registry.npmmirror.com/markdown-it-image-figures/-/markdown-it-image-figures-2.1.1.tgz#fd32a2d0cec60ed8c3916d74fea70d5f9b56e4c7" + integrity sha512-mwXSQ2nPeVUzCMIE3HlLvjRioopiqyJLNph0pyx38yf9mpqFDhNGnMpAXF9/A2Xv0oiF2cVyg9xwfF0HNAz05g== + +markdown-it-sub@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/markdown-it-sub/-/markdown-it-sub-2.0.0.tgz#10f6c7bbf2faacf71ae1a64c75009c40ef9b2c94" + integrity sha512-iCBKgwCkfQBRg2vApy9vx1C1Tu6D8XYo8NvevI3OlwzBRmiMtsJ2sXupBgEA7PPxiDwNni3qIUkhZ6j5wofDUA== + +markdown-it-sup@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/markdown-it-sup/-/markdown-it-sup-2.0.0.tgz#683b9390929f3024fcd5291799c466ce3d367f44" + integrity sha512-5VgmdKlkBd8sgXuoDoxMpiU+BiEt3I49GItBzzw7Mxq9CxvnhE/k09HFli09zgfFDRixDQDfDxi0mgBCXtaTvA== + +markdown-it@^14.0.0: + version "14.1.1" + resolved "https://registry.npmmirror.com/markdown-it/-/markdown-it-14.1.1.tgz#856f90b66fc39ae70affd25c1b18b581d7deee1f" + integrity sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA== + dependencies: + argparse "^2.0.1" + entities "^4.4.0" + linkify-it "^5.0.0" + mdurl "^2.0.0" + punycode.js "^2.3.1" + uc.micro "^2.1.0" + +marked-highlight@^2.1.1: + version "2.2.3" + resolved "https://registry.npmmirror.com/marked-highlight/-/marked-highlight-2.2.3.tgz#e7e5e7ccfb1699356c3a45d851db8fe983689172" + integrity sha512-FCfZRxW/msZAiasCML4isYpxyQWKEEx44vOgdn5Kloae+Qc3q4XR7WjpKKf8oMLk7JP9ZCRd2vhtclJFdwxlWQ== + +marked@14.0.0: + version "14.0.0" + resolved "https://registry.npmmirror.com/marked/-/marked-14.0.0.tgz#79a1477358a59e0660276f8fec76de2c33f35d83" + integrity sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ== + +marked@^12.0.1: + version "12.0.2" + resolved "https://registry.npmmirror.com/marked/-/marked-12.0.2.tgz#b31578fe608b599944c69807b00f18edab84647e" + integrity sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q== + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + +md-editor-v3@^6.4.0: + version "6.4.0" + resolved "https://registry.npmmirror.com/md-editor-v3/-/md-editor-v3-6.4.0.tgz#c341abd69a06667a1c7d9e4f906dbeb8d99870e8" + integrity sha512-gd2COD1cYsvC9KyKODxqC2fmWWCM1uamvPWQLl9fdcUieUyvP8BY9ANOIrxkjdJ5W6f7JcAuAsgiUyJmlMOyzQ== + dependencies: + "@codemirror/autocomplete" "^6.18.7" + "@codemirror/commands" "^6.8.1" + "@codemirror/lang-markdown" "^6.3.4" + "@codemirror/language" "^6.11.3" + "@codemirror/language-data" "^6.5.1" + "@codemirror/search" "^6.5.11" + "@codemirror/state" "^6.5.2" + "@codemirror/view" "^6.38.2" + "@lezer/highlight" "^1.2.1" + "@types/markdown-it" "^14.0.1" + "@vavt/copy2clipboard" "^1.0.1" + "@vavt/util" "^2.1.0" + codemirror "^6.0.2" + lru-cache "^11.2.1" + lucide-vue-next "^0.543.0" + markdown-it "^14.0.0" + markdown-it-image-figures "^2.1.1" + markdown-it-sub "^2.0.0" + markdown-it-sup "^2.0.0" + medium-zoom "^1.1.0" + xss "^1.0.15" + +mdurl@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/mdurl/-/mdurl-2.0.0.tgz#80676ec0433025dd3e17ee983d0fe8de5a2237e0" + integrity sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w== + +medium-zoom@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/medium-zoom/-/medium-zoom-1.1.0.tgz#6efb6bbda861a02064ee71a2617a8dc4381ecc71" + integrity sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ== + +mermaid@9.4.3: + version "9.4.3" + resolved "https://registry.npmmirror.com/mermaid/-/mermaid-9.4.3.tgz#62cf210c246b74972ea98c19837519b6f03427f2" + integrity sha512-TLkQEtqhRSuEHSE34lh5bCa94KATCyluAXmFnNI2PRZwOpXFeqiJWwZl+d2CcemE1RS6QbbueSSq9QIg8Uxcyw== + dependencies: + "@braintree/sanitize-url" "^6.0.0" + cytoscape "^3.23.0" + cytoscape-cose-bilkent "^4.1.0" + cytoscape-fcose "^2.1.0" + d3 "^7.4.0" + dagre-d3-es "7.0.9" + dayjs "^1.11.7" + dompurify "2.4.3" + elkjs "^0.8.2" + khroma "^2.0.0" + lodash-es "^4.17.21" + non-layered-tidy-tree-layout "^2.0.2" + stylis "^4.1.2" + ts-dedent "^2.2.0" + uuid "^9.0.0" + web-worker "^1.2.0" + +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +minimatch@^3.1.1: + version "3.1.5" + resolved "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.5.tgz#580c88f8d5445f2bd6aa8f3cadefa0de79fbd69e" + integrity sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^9.0.3: + version "9.0.9" + resolved "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e" + integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg== + dependencies: + brace-expansion "^2.0.2" + +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mitt@^3.0.1: + version "3.0.1" + resolved "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz#ea36cf0cc30403601ae074c8f77b7092cdab36d1" + integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== + +mkdirp@^0.5.1: + version "0.5.6" + resolved "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mlly@^1.7.4, mlly@^1.8.0: + version "1.8.1" + resolved "https://registry.npmmirror.com/mlly/-/mlly-1.8.1.tgz#2ed89f4fbbbde26db9fcc86df5bfa3153cfacea1" + integrity sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ== + dependencies: + acorn "^8.16.0" + pathe "^2.0.3" + pkg-types "^1.3.1" + ufo "^1.6.3" + +monaco-editor-vue3@^1.0.5: + version "1.0.5" + resolved "https://registry.npmmirror.com/monaco-editor-vue3/-/monaco-editor-vue3-1.0.5.tgz#d19d07a8edae8eec1b07cd42010c4dc1094b18da" + integrity sha512-1n4ayGcuYpZcQ6A4PUYZYqXuf6n+A/2pUNdLnb8I39WZV0zkhfvSRNau3hB0qzq6vp6wSNZ7MgcjQRCJCiGHWQ== + +monaco-editor@^0.55.1: + version "0.55.1" + resolved "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.55.1.tgz#e74c6fe5a6bf985b817d2de3eb88d56afc494a1b" + integrity sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A== + dependencies: + dompurify "3.2.7" + marked "14.0.0" + +ms@^2.1.1, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +muggle-string@^0.4.1: + version "0.4.1" + resolved "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.4.1.tgz#3b366bd43b32f809dc20659534dd30e7c8a0d328" + integrity sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ== + +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + +nanostores@^0.11.4: + version "0.11.4" + resolved "https://registry.npmmirror.com/nanostores/-/nanostores-0.11.4.tgz#4ed323f7b2cbbf0342c2d6be3a0c833dab73af34" + integrity sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ== + +node-addon-api@^7.0.0: + version "7.1.1" + resolved "https://registry.npmmirror.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== + +non-layered-tidy-tree-layout@^2.0.2: + version "2.0.2" + resolved "https://registry.npmmirror.com/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz#57d35d13c356643fc296a55fb11ac15e74da7804" + integrity sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw== + +nopt@^4.0.1: + version "4.0.3" + resolved "https://registry.npmmirror.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48" + integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.0.0: + version "2.5.0" + resolved "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-normalize-package-bin@^1.0.0: + version "1.0.1" + resolved "https://registry.npmmirror.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" + integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== + +nwsapi@^2.2.0: + version "2.2.23" + resolved "https://registry.npmmirror.com/nwsapi/-/nwsapi-2.2.23.tgz#59712c3a88e6de2bb0b6ccc1070397267019cf6c" + integrity sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ== + +object-assign@>=4.0.1: + version "4.1.1" + resolved "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +obug@^2.1.1: + version "2.1.1" + resolved "https://registry.npmmirror.com/obug/-/obug-2.1.1.tgz#2cba74ff241beb77d63055ddf4cd1e9f90b538be" + integrity sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ== + +omi-transition@^0.1.11: + version "0.1.11" + resolved "https://registry.npmmirror.com/omi-transition/-/omi-transition-0.1.11.tgz#9f746879ed1f300fd675b196137848d96f4fddeb" + integrity sha512-AsiLCOzMUuFRQ2juGXnSVjcOA2eOb4WdZzbiALbeyXCUgxdRTlE0ekO08IgyvAe9Rdc9fhx0iEgLnwd7ufneCg== + dependencies: + omi latest + +omi-vueify@^0.0.12: + version "0.0.12" + resolved "https://registry.npmmirror.com/omi-vueify/-/omi-vueify-0.0.12.tgz#dee07a7ed1849c1f4a859608fdaabbad0f2a796e" + integrity sha512-Z7fircOM/vKmh4hKZss4V1h4u5FQAfvauzBwWTiTlHGJWsaXglGRiHatiSCWsKv8v9uNK/xYXz3p+2QbkevPZg== + +omi@^7.6.17, omi@^7.7.13, omi@latest: + version "7.7.13" + resolved "https://registry.npmmirror.com/omi/-/omi-7.7.13.tgz#7d86dfdee3676d999fc7f92f8229fda558e64279" + integrity sha512-LA/rLhGE7PO/OVN9Mp4TG4YL3Dt5dERM9fsVyiPmE75CRCuyW7vVfrYK8Ee7EPbQuTycYRsfbrD1YBQytOY/jw== + dependencies: + construct-style-sheets-polyfill "3.0.1" + reactive-signal "^2.0.1" + weakmap-polyfill "2.0.4" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.npmmirror.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +opfs-tools@^0.7.2: + version "0.7.4" + resolved "https://registry.npmmirror.com/opfs-tools/-/opfs-tools-0.7.4.tgz#999df0940c3bc0b79080a9977f44509b5637e41c" + integrity sha512-DJgQnXyPcCj3q8pKa8SjP4HfNBPffj8kO6W1nc2zQydTqu5G/AxOuIjmWnRxZ84nE1EOpzZzhj8MYCZP1jRR9A== + +option@~0.2.1: + version "0.2.4" + resolved "https://registry.npmmirror.com/option/-/option-0.2.4.tgz#fd475cdf98dcabb3cb397a3ba5284feb45edbfe4" + integrity sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A== + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.npmmirror.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.npmmirror.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.npmmirror.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-limit@^7.3.0: + version "7.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-7.3.0.tgz#821398d91491c6b6a1340ecd09cdc402a9c8d0ee" + integrity sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw== + dependencies: + yocto-queue "^1.2.1" + +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.npmmirror.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +path-browserify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +pathe@^2.0.1, pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.npmmirror.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +picomatch@^4.0.3: + version "4.0.3" + resolved "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz#796c76136d1eead715db1e7bad785dedd695a042" + integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q== + +pinia-plugin-persistedstate@^4.7.1: + version "4.7.1" + resolved "https://registry.npmmirror.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.7.1.tgz#d13880e0b7efdafd1b73fca3d73cd64ae34bde84" + integrity sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ== + dependencies: + defu "^6.1.4" + +pinia@^2.2.6: + version "2.3.1" + resolved "https://registry.npmmirror.com/pinia/-/pinia-2.3.1.tgz#54c476675b72f5abcfafa24a7582531ea8c23d94" + integrity sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug== + dependencies: + "@vue/devtools-api" "^6.6.3" + vue-demi "^0.14.10" + +pkg-types@^1.3.1: + version "1.3.1" + resolved "https://registry.npmmirror.com/pkg-types/-/pkg-types-1.3.1.tgz#bd7cc70881192777eef5326c19deb46e890917df" + integrity sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ== + dependencies: + confbox "^0.1.8" + mlly "^1.7.4" + pathe "^2.0.1" + +pkg-types@^2.3.0: + version "2.3.0" + resolved "https://registry.npmmirror.com/pkg-types/-/pkg-types-2.3.0.tgz#037f2c19bd5402966ff6810e32706558cb5b5726" + integrity sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig== + dependencies: + confbox "^0.2.2" + exsolve "^1.0.7" + pathe "^2.0.3" + +postcss-px-to-viewport@^1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/postcss-px-to-viewport/-/postcss-px-to-viewport-1.1.1.tgz#a25ca410b553c9892cc8b525cc710da47bf1aa55" + integrity sha512-2x9oGnBms+e0cYtBJOZdlwrFg/mLR4P1g2IFu7jYKvnqnH/HLhoKyareW2Q/x4sg0BgklHlP1qeWo2oCyPm8FQ== + dependencies: + object-assign ">=4.0.1" + postcss ">=5.0.2" + +postcss@>=5.0.2, postcss@^8.4.14, postcss@^8.4.43, postcss@^8.5.8: + version "8.5.8" + resolved "https://registry.npmmirror.com/postcss/-/postcss-8.5.8.tgz#6230ecc8fb02e7a0f6982e53990937857e13f399" + integrity sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +"prettier@^1.18.2 || ^2.0.0": + version "2.8.8" + resolved "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + +prettier@^3.3.3: + version "3.8.1" + resolved "https://registry.npmmirror.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" + integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +psl@^1.1.33: + version "1.15.0" + resolved "https://registry.npmmirror.com/psl/-/psl-1.15.0.tgz#bdace31896f1d97cec6a79e8224898ce93d974c6" + integrity sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w== + dependencies: + punycode "^2.3.1" + +punycode.js@^2.3.1: + version "2.3.1" + resolved "https://registry.npmmirror.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" + integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA== + +punycode@^2.1.1, punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +quansync@^0.2.11: + version "0.2.11" + resolved "https://registry.npmmirror.com/quansync/-/quansync-0.2.11.tgz#f9c3adda2e1272e4f8cf3f1457b04cbdb4ee692a" + integrity sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA== + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + +reactive-signal@^2.0.1: + version "2.0.1" + resolved "https://registry.npmmirror.com/reactive-signal/-/reactive-signal-2.0.1.tgz#54aa481b5811ea39156e246a82d9f1864783e08d" + integrity sha512-c+avHi2L+sHn9kkEsDZskRGtkfj6PtoTn1wGgutY2NZR9jCI0InXY7zqWbUXz6cGcuIXKMkQq/mtUiUXBFmKVA== + +read-installed@~4.0.3: + version "4.0.3" + resolved "https://registry.npmmirror.com/read-installed/-/read-installed-4.0.3.tgz#ff9b8b67f187d1e4c29b9feb31f6b223acd19067" + integrity sha512-O03wg/IYuV/VtnK2h/KXEt9VIbMUFbk3ERG0Iu4FhLZw0EP0T9znqrYDGn6ncbEsXUFaUjiVAWXHzxwt3lhRPQ== + dependencies: + debuglog "^1.0.1" + read-package-json "^2.0.0" + readdir-scoped-modules "^1.0.0" + semver "2 || 3 || 4 || 5" + slide "~1.1.3" + util-extend "^1.0.1" + optionalDependencies: + graceful-fs "^4.1.2" + +read-package-json@^2.0.0: + version "2.1.2" + resolved "https://registry.npmmirror.com/read-package-json/-/read-package-json-2.1.2.tgz#6992b2b66c7177259feb8eaac73c3acd28b9222a" + integrity sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA== + dependencies: + glob "^7.1.1" + json-parse-even-better-errors "^2.3.0" + normalize-package-data "^2.0.0" + npm-normalize-package-bin "^1.0.0" + +readable-stream@~2.3.6: + version "2.3.8" + resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readdir-scoped-modules@^1.0.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309" + integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw== + dependencies: + debuglog "^1.0.1" + dezalgo "^1.0.0" + graceful-fs "^4.1.2" + once "^1.3.0" + +readdirp@^4.0.1: + version "4.1.2" + resolved "https://registry.npmmirror.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== + +readdirp@^5.0.0: + version "5.0.0" + resolved "https://registry.npmmirror.com/readdirp/-/readdirp-5.0.0.tgz#fbf1f71a727891d685bb1786f9ba74084f6e2f91" + integrity sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ== + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve@^1.10.0: + version "1.22.11" + resolved "https://registry.npmmirror.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" + integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== + dependencies: + is-core-module "^2.16.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +robust-predicates@^3.0.2: + version "3.0.2" + resolved "https://registry.npmmirror.com/robust-predicates/-/robust-predicates-3.0.2.tgz#d5b28528c4824d20fc48df1928d41d9efa1ad771" + integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg== + +rollup@^4.20.0: + version "4.59.0" + resolved "https://registry.npmmirror.com/rollup/-/rollup-4.59.0.tgz#cf74edac17c1486f562d728a4d923a694abdf06f" + integrity sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg== + dependencies: + "@types/estree" "1.0.8" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.59.0" + "@rollup/rollup-android-arm64" "4.59.0" + "@rollup/rollup-darwin-arm64" "4.59.0" + "@rollup/rollup-darwin-x64" "4.59.0" + "@rollup/rollup-freebsd-arm64" "4.59.0" + "@rollup/rollup-freebsd-x64" "4.59.0" + "@rollup/rollup-linux-arm-gnueabihf" "4.59.0" + "@rollup/rollup-linux-arm-musleabihf" "4.59.0" + "@rollup/rollup-linux-arm64-gnu" "4.59.0" + "@rollup/rollup-linux-arm64-musl" "4.59.0" + "@rollup/rollup-linux-loong64-gnu" "4.59.0" + "@rollup/rollup-linux-loong64-musl" "4.59.0" + "@rollup/rollup-linux-ppc64-gnu" "4.59.0" + "@rollup/rollup-linux-ppc64-musl" "4.59.0" + "@rollup/rollup-linux-riscv64-gnu" "4.59.0" + "@rollup/rollup-linux-riscv64-musl" "4.59.0" + "@rollup/rollup-linux-s390x-gnu" "4.59.0" + "@rollup/rollup-linux-x64-gnu" "4.59.0" + "@rollup/rollup-linux-x64-musl" "4.59.0" + "@rollup/rollup-openbsd-x64" "4.59.0" + "@rollup/rollup-openharmony-arm64" "4.59.0" + "@rollup/rollup-win32-arm64-msvc" "4.59.0" + "@rollup/rollup-win32-ia32-msvc" "4.59.0" + "@rollup/rollup-win32-x64-gnu" "4.59.0" + "@rollup/rollup-win32-x64-msvc" "4.59.0" + fsevents "~2.3.2" + +rw@1: + version "1.3.3" + resolved "https://registry.npmmirror.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" + integrity sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sass@^1.97.0: + version "1.98.0" + resolved "https://registry.npmmirror.com/sass/-/sass-1.98.0.tgz#924ce85a3745ccaccd976262fdc1bc0c13aa8e57" + integrity sha512-+4N/u9dZ4PrgzGgPlKnaaRQx64RO0JBKs9sDhQ2pLgN6JQZ25uPQZKQYaBJU48Kd5BxgXoJ4e09Dq7nMcOUW3A== + dependencies: + chokidar "^4.0.0" + immutable "^5.1.5" + source-map-js ">=0.6.2 <2.0.0" + optionalDependencies: + "@parcel/watcher" "^2.4.1" + +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.npmmirror.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + +scule@^1.3.0: + version "1.3.0" + resolved "https://registry.npmmirror.com/scule/-/scule-1.3.0.tgz#6efbd22fd0bb801bdcc585c89266a7d2daa8fbd3" + integrity sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g== + +select@^1.1.2: + version "1.1.2" + resolved "https://registry.npmmirror.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + integrity sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA== + +"semver@2 || 3 || 4 || 5", semver@^5.5.0: + version "5.7.2" + resolved "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== + +slide@~1.1.3: + version "1.1.6" + resolved "https://registry.npmmirror.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + integrity sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw== + +socket.io-client@^4.8.3: + version "4.8.3" + resolved "https://registry.npmmirror.com/socket.io-client/-/socket.io-client-4.8.3.tgz#62717edd46a318c918125b57e92dc7f8bb71c34c" + integrity sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.4.1" + engine.io-client "~6.6.1" + socket.io-parser "~4.2.4" + +socket.io-parser@~4.2.4: + version "4.2.6" + resolved "https://registry.npmmirror.com/socket.io-parser/-/socket.io-parser-4.2.6.tgz#19156bf179af3931abd05260cfb1491822578a6f" + integrity sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.4.1" + +sortablejs@^1.15.0: + version "1.15.7" + resolved "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.15.7.tgz#83a0bddc472117ee328dea20b2e6f490fed20f86" + integrity sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A== + +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2, source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdx-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/spdx-compare/-/spdx-compare-1.0.0.tgz#2c55f117362078d7409e6d7b08ce70a857cd3ed7" + integrity sha512-C1mDZOX0hnu0ep9dfmuoi03+eOdDoz2yvK79RxbcrVEG1NO1Ph35yW102DHWKN4pk80nwCgeMmSY5L25VE4D9A== + dependencies: + array-find-index "^1.0.2" + spdx-expression-parse "^3.0.0" + spdx-ranges "^2.0.0" + +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.5.0" + resolved "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz#5d607d27fc806f66d7b64a766650fa890f04ed66" + integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.23" + resolved "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz#b069e687b1291a32f126893ed76a27a745ee2133" + integrity sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw== + +spdx-ranges@^2.0.0: + version "2.1.1" + resolved "https://registry.npmmirror.com/spdx-ranges/-/spdx-ranges-2.1.1.tgz#87573927ba51e92b3f4550ab60bfc83dd07bac20" + integrity sha512-mcdpQFV7UDAgLpXEE/jOMqvK4LBoO0uTQg0uvXUewmEFhpiZx5yJSZITHB8w1ZahKdhfZqP5GPEOKLyEq5p8XA== + +spdx-satisfies@^4.0.0: + version "4.0.1" + resolved "https://registry.npmmirror.com/spdx-satisfies/-/spdx-satisfies-4.0.1.tgz#9a09a68d80f5f1a31cfaebb384b0c6009e4969fe" + integrity sha512-WVzZ/cXAzoNmjCWiEluEA3BjHp5tiUmmhn9MK+X0tBbR9sOqtC6UQwmgCNrAIZvNlMuBUYAaHYfb2oqlF9SwKA== + dependencies: + spdx-compare "^1.0.0" + spdx-expression-parse "^3.0.0" + spdx-ranges "^2.0.0" + +splitpanes@^4.0.4: + version "4.0.4" + resolved "https://registry.npmmirror.com/splitpanes/-/splitpanes-4.0.4.tgz#e8dfbbef4e93853ea336dc43f903e20946b42c1d" + integrity sha512-RbysugZhjbCw5fgplvk3hOXr41stahQDtZhHVkhnnJI6H4wlGDhM2kIpbehy7v92duy9GnMa8zIhHigIV1TWtg== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +strip-literal@^3.1.0: + version "3.1.0" + resolved "https://registry.npmmirror.com/strip-literal/-/strip-literal-3.1.0.tgz#222b243dd2d49c0bcd0de8906adbd84177196032" + integrity sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg== + dependencies: + js-tokens "^9.0.1" + +style-mod@^4.0.0, style-mod@^4.1.0: + version "4.1.3" + resolved "https://registry.npmmirror.com/style-mod/-/style-mod-4.1.3.tgz#6e9012255bb799bdac37e288f7671b5d71bf9f73" + integrity sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ== + +stylis@^4.1.2: + version "4.3.6" + resolved "https://registry.npmmirror.com/stylis/-/stylis-4.3.6.tgz#7c7b97191cb4f195f03ecab7d52f7902ed378320" + integrity sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.npmmirror.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.npmmirror.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +tailwind-merge@^2.2.1, tailwind-merge@^2.3.0: + version "2.6.1" + resolved "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-2.6.1.tgz#a9d58240f664d21c33c379a092d9a273f833443b" + integrity sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ== + +tdesign-icons-vue-next@~0.4.2: + version "0.4.2" + resolved "https://registry.npmmirror.com/tdesign-icons-vue-next/-/tdesign-icons-vue-next-0.4.2.tgz#33c3394ed76ae85ed74cd6ab79f9554fa4b90c03" + integrity sha512-mTPk1ApcCA9oxDiSs9ttMdd09H8ICBooZIr2bwDEELnYr60sYSUbvWojQ2tp84MUAMuw21HgyVyGkT49db0GFg== + dependencies: + "@babel/runtime" "^7.16.5" + +tdesign-icons-web-components@^0.3.2: + version "0.3.3" + resolved "https://registry.npmmirror.com/tdesign-icons-web-components/-/tdesign-icons-web-components-0.3.3.tgz#072871fec8c0e478a1f3015e66640cc75db87f4c" + integrity sha512-qxdPnMJyCcjTy1GqpSpAp4fi+PIm+EkNGr6QQyftdxvSEBvRO2YxbfELXnu0nwIqy2RA5HUa6DSR4ftE31sH+w== + dependencies: + "@babel/runtime" "^7.16.5" + clsx "^2.1.1" + omi "^7.6.17" + tailwind-merge "^2.3.0" + +tdesign-vue-next@^1.18.3, tdesign-vue-next@^1.18.5: + version "1.18.5" + resolved "https://registry.npmmirror.com/tdesign-vue-next/-/tdesign-vue-next-1.18.5.tgz#54e581515416d10190e5773f6b013c8629e7723e" + integrity sha512-lc/zhb7sWH6wB4YNbLzNPhwhxYdWqdZpBBgjk9umAomJaPsKW1Y7eLLRFuBuvIopsjvWDE9gvFfI5K6vEZUFnw== + dependencies: + "@babel/runtime" "^7.22.6" + "@popperjs/core" "^2.11.8" + "@types/lodash-es" "^4.17.12" + "@types/sortablejs" "^1.15.1" + "@types/tinycolor2" "^1.4.3" + "@types/validator" "^13.7.17" + dayjs "^1.11.10" + lodash-es "^4.17.21" + mitt "^3.0.1" + sortablejs "^1.15.0" + tdesign-icons-vue-next "~0.4.2" + tinycolor2 "^1.6.0" + validator "^13.15.23" + +tdesign-web-components@1.3.1-alpha.10: + version "1.3.1-alpha.10" + resolved "https://registry.npmmirror.com/tdesign-web-components/-/tdesign-web-components-1.3.1-alpha.10.tgz#3fd77323484aee7a9da7f7c0ea9b1745286633ba" + integrity sha512-bfZYz/JIF0UvR0Tr9V6SUzVmxYaQCxtQG3w00u34QjICIbWExOniJfC9y13vXlp9+M85BHrswDUERUqw0ZW55g== + dependencies: + "@babel/runtime" "^7.24.7" + "@popperjs/core" "^2.11.8" + cherry-markdown "^0.11.0-alpha-5" + class-variance-authority "^0.7.0" + clsx "^2.1.0" + copy-to-clipboard "^3.3.3" + dayjs "^1.11.19" + fast-json-patch "3.1.1" + htm "^3.1.1" + immer "^10.1.1" + lodash-es "^4.17.21" + markdown-it-fence "^0.1.3" + nanostores "^0.11.4" + omi "^7.7.13" + omi-transition "^0.1.11" + tailwind-merge "^2.2.1" + tdesign-icons-web-components "^0.3.2" + zod "^3.23.8" + +tiny-emitter@^2.0.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== + +tinycolor2@^1.6.0: + version "1.6.0" + resolved "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e" + integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw== + +tinyglobby@^0.2.15: + version "0.2.15" + resolved "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz#e228dd1e638cea993d2fdb4fcd2d4602a79951c2" + integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.3" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.npmmirror.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== + +tough-cookie@^4.0.0: + version "4.1.4" + resolved "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-4.1.4.tgz#945f1461b45b5a8c76821c33ea49c3ac192c1b36" + integrity sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== + dependencies: + punycode "^2.1.1" + +treeify@^1.1.0: + version "1.1.0" + resolved "https://registry.npmmirror.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" + integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== + +ts-dedent@^2.2.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5" + integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ== + +typescript@~5.6.3: + version "5.6.3" + resolved "https://registry.npmmirror.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b" + integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw== + +uc.micro@^2.0.0, uc.micro@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" + integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== + +ufo@^1.6.3: + version "1.6.3" + resolved "https://registry.npmmirror.com/ufo/-/ufo-1.6.3.tgz#799666e4e88c122a9659805e30b9dc071c3aed4f" + integrity sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q== + +underscore@^1.13.1: + version "1.13.8" + resolved "https://registry.npmmirror.com/underscore/-/underscore-1.13.8.tgz#a93a21186c049dbf0e847496dba72b7bd8c1e92b" + integrity sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ== + +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + +unimport@^5.6.0: + version "5.7.0" + resolved "https://registry.npmmirror.com/unimport/-/unimport-5.7.0.tgz#d23db764c8ac6f116db410ec0638baa27859449c" + integrity sha512-njnL6sp8lEA8QQbZrt+52p/g4X0rw3bnGGmUcJnt1jeG8+iiqO779aGz0PirCtydAIVcuTBRlJ52F0u46z309Q== + dependencies: + acorn "^8.16.0" + escape-string-regexp "^5.0.0" + estree-walker "^3.0.3" + local-pkg "^1.1.2" + magic-string "^0.30.21" + mlly "^1.8.0" + pathe "^2.0.3" + picomatch "^4.0.3" + pkg-types "^2.3.0" + scule "^1.3.0" + strip-literal "^3.1.0" + tinyglobby "^0.2.15" + unplugin "^2.3.11" + unplugin-utils "^0.3.1" + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.npmmirror.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" + integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== + +unplugin-auto-import@^21.0.0: + version "21.0.0" + resolved "https://registry.npmmirror.com/unplugin-auto-import/-/unplugin-auto-import-21.0.0.tgz#d1ac3bf95f80fb4182ec8f3d65d3e3aad38a63ac" + integrity sha512-vWuC8SwqJmxZFYwPojhOhOXDb5xFhNNcEVb9K/RFkyk/3VnfaOjzitWN7v+8DEKpMjSsY2AEGXNgt6I0yQrhRQ== + dependencies: + local-pkg "^1.1.2" + magic-string "^0.30.21" + picomatch "^4.0.3" + unimport "^5.6.0" + unplugin "^2.3.11" + unplugin-utils "^0.3.1" + +unplugin-utils@^0.3.0, unplugin-utils@^0.3.1: + version "0.3.1" + resolved "https://registry.npmmirror.com/unplugin-utils/-/unplugin-utils-0.3.1.tgz#ef2873670a6a2a21bd2c9d31307257cc863a709c" + integrity sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog== + dependencies: + pathe "^2.0.3" + picomatch "^4.0.3" + +unplugin-vue-components@^29.0.0: + version "29.2.0" + resolved "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-29.2.0.tgz#5cf1aace82f6e7481fb068e6d23f33ef1ef849d6" + integrity sha512-QxBeBdmEflgtJRgMQMc/z/JVV5lcwXN5nOy5ehX6CKDGylIu6Qn4Goy8X95S0qOxF7EdI+uNhdBd4v5i0bvzCw== + dependencies: + chokidar "^3.6.0" + debug "^4.4.3" + local-pkg "^1.1.2" + magic-string "^0.30.19" + mlly "^1.8.0" + tinyglobby "^0.2.15" + unplugin "^2.3.10" + unplugin-utils "^0.3.1" + +unplugin-vue-components@^31.0.0: + version "31.0.0" + resolved "https://registry.npmmirror.com/unplugin-vue-components/-/unplugin-vue-components-31.0.0.tgz#fa31b8884ac76e78696e3a41f46e5b58a4539b60" + integrity sha512-4ULwfTZTLuWJ7+S9P7TrcStYLsSRkk6vy2jt/WTfgUEUb0nW9//xxmrfhyHUEVpZ2UKRRwfRb8Yy15PDbVZf+Q== + dependencies: + chokidar "^5.0.0" + local-pkg "^1.1.2" + magic-string "^0.30.21" + mlly "^1.8.0" + obug "^2.1.1" + picomatch "^4.0.3" + tinyglobby "^0.2.15" + unplugin "^2.3.11" + unplugin-utils "^0.3.1" + +unplugin@^2.3.10, unplugin@^2.3.11: + version "2.3.11" + resolved "https://registry.npmmirror.com/unplugin/-/unplugin-2.3.11.tgz#411e020dd2ba90e2fbe1e7bd63a5a399e6ee3b54" + integrity sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww== + dependencies: + "@jridgewell/remapping" "^2.3.5" + acorn "^8.15.0" + picomatch "^4.0.3" + webpack-virtual-modules "^0.6.2" + +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +util-extend@^1.0.1: + version "1.0.3" + resolved "https://registry.npmmirror.com/util-extend/-/util-extend-1.0.3.tgz#a7c216d267545169637b3b6edc6ca9119e2ff93f" + integrity sha512-mLs5zAK+ctllYBj+iAQvlDCwoxU/WDOUaJkcFudeiAX6OajC6BKXJUa9a+tbtkC11dz2Ufb7h0lyvIOVn4LADA== + +uuid@*, uuid@^13.0.0: + version "13.0.0" + resolved "https://registry.npmmirror.com/uuid/-/uuid-13.0.0.tgz#263dc341b19b4d755eb8fe36b78d95a6b65707e8" + integrity sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w== + +uuid@^9.0.0: + version "9.0.1" + resolved "https://registry.npmmirror.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +validator@^13.15.23: + version "13.15.26" + resolved "https://registry.npmmirror.com/validator/-/validator-13.15.26.tgz#36c3deeab30e97806a658728a155c66fcaa5b944" + integrity sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA== + +vite-plugin-singlefile@^2.3.0: + version "2.3.2" + resolved "https://registry.npmmirror.com/vite-plugin-singlefile/-/vite-plugin-singlefile-2.3.2.tgz#e0ff9e146fa3a63fecd53eade9ed33bd08d20da0" + integrity sha512-b8SxCi/gG7K298oJDcKOuZeU6gf6wIcCJAaEqUmmZXdjfuONlkyNyWZC3tEbN6QockRCNUd3it9eGTtpHGoYmg== + dependencies: + micromatch "^4.0.8" + +vite@^5.4.10: + version "5.4.21" + resolved "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz#84a4f7c5d860b071676d39ba513c0d598fdc7027" + integrity sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw== + dependencies: + esbuild "^0.21.3" + postcss "^8.4.43" + rollup "^4.20.0" + optionalDependencies: + fsevents "~2.3.3" + +vscode-uri@^3.0.8: + version "3.1.0" + resolved "https://registry.npmmirror.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c" + integrity sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ== + +vue-clip-track@^0.1.5: + version "0.1.5" + resolved "https://registry.npmmirror.com/vue-clip-track/-/vue-clip-track-0.1.5.tgz#bb320895adde66953ede4bfb3c3b44cd6f9a4783" + integrity sha512-fpMwqjuKcyGQBah8ggEK+G5sWF/syBpL/rVlgxawIuiTA8srmUu/Xj4eBFGFJO0AidDsHUa86efByB467HeDiA== + +vue-demi@>=0.14.8, vue-demi@^0.14.10: + version "0.14.10" + resolved "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz#afc78de3d6f9e11bf78c55e8510ee12814522f04" + integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg== + +vue-draggable-plus@^0.6.1: + version "0.6.1" + resolved "https://registry.npmmirror.com/vue-draggable-plus/-/vue-draggable-plus-0.6.1.tgz#d4ed54cbfd3e2a72c046074c39da1631e0c36a78" + integrity sha512-FbtQ/fuoixiOfTZzG3yoPl4JAo9HJXRHmBQZFB9x2NYCh6pq0TomHf7g5MUmpaDYv+LU2n6BPq2YN9sBO+FbIg== + dependencies: + "@types/sortablejs" "^1.15.8" + +vue-i18n@11: + version "11.3.0" + resolved "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-11.3.0.tgz#acb92847017f90b0fd96541cb3b08fdef50dc49c" + integrity sha512-1J+xDfDJTLhDxElkd3+XUhT7FYSZd2b8pa7IRKGxhWH/8yt6PTvi3xmWhGwhYT5EaXdatui11pF2R6tL73/zPA== + dependencies: + "@intlify/core-base" "11.3.0" + "@intlify/devtools-types" "11.3.0" + "@intlify/shared" "11.3.0" + "@vue/devtools-api" "^6.5.0" + +vue-router@^4.4.5: + version "4.6.4" + resolved "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz#a0a9cb9ef811a106d249e4bb9313d286718020d8" + integrity sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg== + dependencies: + "@vue/devtools-api" "^6.6.4" + +vue-tsc@^2.1.10: + version "2.2.12" + resolved "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-2.2.12.tgz#5f719b08ef7390a763c1a20169ca5c9d09d55688" + integrity sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw== + dependencies: + "@volar/typescript" "2.4.15" + "@vue/language-core" "2.2.12" + +vue@^2.0.0: + version "2.7.16" + resolved "https://registry.npmmirror.com/vue/-/vue-2.7.16.tgz#98c60de9def99c0e3da8dae59b304ead43b967c9" + integrity sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw== + dependencies: + "@vue/compiler-sfc" "2.7.16" + csstype "^3.1.0" + +vue@^3.5.12: + version "3.5.30" + resolved "https://registry.npmmirror.com/vue/-/vue-3.5.30.tgz#66df25e9795af3e5522b36f24f3d290fde83f8a0" + integrity sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg== + dependencies: + "@vue/compiler-dom" "3.5.30" + "@vue/compiler-sfc" "3.5.30" + "@vue/runtime-dom" "3.5.30" + "@vue/server-renderer" "3.5.30" + "@vue/shared" "3.5.30" + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.npmmirror.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-keyname@^2.2.4: + version "2.2.8" + resolved "https://registry.npmmirror.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz#7b17c8c6883d4e8b86ac8aba79d39e880f8869c5" + integrity sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ== + +w3c-xmlserializer@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923" + integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg== + dependencies: + xml-name-validator "^4.0.0" + +wave-resampler@^1.0.0: + version "1.0.0" + resolved "https://registry.npmmirror.com/wave-resampler/-/wave-resampler-1.0.0.tgz#127502338d28e6d5315be1abb06665c04bbb4fc6" + integrity sha512-bE3rbpZXuKAV52Cd8/BeJvy82ZqEHK8pPWHrZ9JioaVVTBlmWbDC+u4p9blhFcf0Skepb4hlOAHc25XfqLC48g== + +weakmap-polyfill@2.0.4: + version "2.0.4" + resolved "https://registry.npmmirror.com/weakmap-polyfill/-/weakmap-polyfill-2.0.4.tgz#bcc301e4c8eb4eda3e406f08f1a691093e407884" + integrity sha512-ZzxBf288iALJseijWelmECm/1x7ZwQn3sMYIkDr2VvZp7r6SEKuT8D0O9Wiq6L9Nl5mazrOMcmiZE/2NCenaxw== + +web-worker@^1.2.0: + version "1.5.0" + resolved "https://registry.npmmirror.com/web-worker/-/web-worker-1.5.0.tgz#71b2b0fbcc4293e8f0aa4f6b8a3ffebff733dcc5" + integrity sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw== + +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + +webpack-virtual-modules@^0.6.2: + version "0.6.2" + resolved "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz#057faa9065c8acf48f24cb57ac0e77739ab9a7e8" + integrity sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ== + +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.npmmirror.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.npmmirror.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^10.0.0: + version "10.0.0" + resolved "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da" + integrity sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + +whatwg-url@^11.0.0: + version "11.0.0" + resolved "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" + integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^8.18.0, ws@^8.2.3: + version "8.19.0" + resolved "https://registry.npmmirror.com/ws/-/ws-8.19.0.tgz#ddc2bdfa5b9ad860204f5a72a4863a8895fd8c8b" + integrity sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg== + +ws@~8.18.3: + version "8.18.3" + resolved "https://registry.npmmirror.com/ws/-/ws-8.18.3.tgz#b56b88abffde62791c639170400c93dcb0c95472" + integrity sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg== + +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + +xmlbuilder@^10.0.0: + version "10.1.1" + resolved "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-10.1.1.tgz#8cae6688cc9b38d850b7c8d3c0a4161dcaf475b0" + integrity sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +xmlhttprequest-ssl@~2.1.1: + version "2.1.2" + resolved "https://registry.npmmirror.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz#e9e8023b3f29ef34b97a859f584c5e6c61418e23" + integrity sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ== + +xss@^1.0.15: + version "1.0.15" + resolved "https://registry.npmmirror.com/xss/-/xss-1.0.15.tgz#96a0e13886f0661063028b410ed1b18670f4e59a" + integrity sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg== + dependencies: + commander "^2.20.3" + cssfilter "0.0.10" + +yocto-queue@^1.2.1: + version "1.2.2" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz#3e09c95d3f1aa89a58c114c99223edf639152c00" + integrity sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ== + +zod@^3.23.8: + version "3.25.76" + resolved "https://registry.npmmirror.com/zod/-/zod-3.25.76.tgz#26841c3f6fd22a6a2760e7ccb719179768471e34" + integrity sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==