Merge branch 'feat/ui-asset-and-skin'
构建期皮肤合成工具链: 资源归属骨架 + override 校验(skin.mjs) + 独立自检 CLI(check-skin) + 实体化与合成(materialize) + 构建流水线(build-game) + 主题契约(framework/ui/theme)。 工具链 64/64、框架 90/90、typecheck 干净。最终整分支评审判定 merge-ready。 遗留(见 spec §6): Auto Atlas 构建期打包假设仍待人工建 .pac 后复测; Spine 整套替换假设因仓库无素材阻塞。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+7
-8
@@ -17,14 +17,13 @@ yarn-error.log*
|
||||
# ---------- Node 依赖 ----------
|
||||
**/node_modules/
|
||||
|
||||
# ---------- Cocos Creator 工程生成物(可由编辑器重建,勿入库) ----------
|
||||
# 适用于 cocoscreator_projects/ 下所有工程
|
||||
**/library/
|
||||
**/temp/
|
||||
**/local/
|
||||
**/build/
|
||||
**/native/
|
||||
**/profiles/
|
||||
# ---------- Cocos Creator 工程生成物 ----------
|
||||
# 由各工程自己的 .gitignore 用锚定路径负责(YouleNexus/.gitignore、templates/game-seed/.gitignore)。
|
||||
# 此处**不得**再写 **/library/ 这类无锚点模式——它会连 assets/framework 下的
|
||||
# native/ local/ profiles/ 等业务目录一起吞掉(已踩过,见 plan 2026-08-27 Task 1)。
|
||||
|
||||
# 构建流水线的临时工程(build-game 产生,可随时重建)
|
||||
cocoscreator_projects/build-workspace/
|
||||
|
||||
# ---------- 子游戏共享框架的 junction(指向 YouleNexus 真源,按机器重建) ----------
|
||||
# 由 scripts/setup-links 在每台机器上重建,不入库
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
#///////////////////////////
|
||||
# Cocos Creator 3D Project
|
||||
#///////////////////////////
|
||||
library/
|
||||
temp/
|
||||
local/
|
||||
build/
|
||||
profiles/
|
||||
native
|
||||
/library/
|
||||
/temp/
|
||||
/local/
|
||||
/build/
|
||||
/profiles/
|
||||
/native/
|
||||
#//////////////////////////
|
||||
# NPM
|
||||
#//////////////////////////
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# framework/ui — 平台界面资源(框架真源)
|
||||
|
||||
> 完整设计见 `docs/superpowers/specs/2026-08-27-ui-asset-and-skin-design.md`。
|
||||
> 本文件是该 spec §2「资源归属规则」的落地速查,改动前先读 spec。
|
||||
|
||||
## 唯一判定标准
|
||||
|
||||
**凡是放进 `framework/` 的,都会出现在每一个子游戏的包里。**
|
||||
|
||||
## 四条规则
|
||||
|
||||
1. **框架带完整一套默认皮肤**,不是占位图。依据:旧项目 444 个可比对平台资源中 73.9%
|
||||
在子游戏间完全相同;框架不带默认,这部分就要在每个子游戏各存一份且改了不会传播。
|
||||
2. **只放「所有子游戏都需要」的平台界面资源**。判定问句:*下一个子游戏还需要它吗?*
|
||||
「部分子游戏共用」**不构成**进框架的理由——放进去等于让其余子游戏白背包体。
|
||||
3. **目录划分 = 图集划分**。Cocos 的 Auto Atlas 只对同目录散图生效,故目录结构是性能设计。
|
||||
以下 **8 个目录**是可覆盖资源范围的全部(`scripts/lib/skin.mjs` 的 `COVERABLE_DIRS` 白名单,
|
||||
与此表逐字对应):
|
||||
| 目录 | 用途 |
|
||||
|---|---|
|
||||
| `atlas-common/` | 全程常驻:按钮、面板底、图标、通用弹窗 |
|
||||
| `atlas-login/` / `atlas-hall/` / `atlas-room/` | 各场景专用,按加载时机切分 |
|
||||
| `standalone/` | 超图集尺寸上限的大图、整屏背景(不进图集) |
|
||||
| `spine/` | Spine 骨骼动画,**每个资源一个子目录**,含骨骼数据/atlas/贴图 |
|
||||
| `audio/` / `font/` | 平台音效 / 字体 |
|
||||
|
||||
**`theme/` 不在此列**——那是 §4.2 规定的**框架 TypeScript 代码**位置(类型契约 + 默认值 +
|
||||
合并 + Provider),不是资源,**不可覆盖**、也不计入"框架可覆盖资源共 N 个"的统计。子游戏若在
|
||||
`override/theme/` 下放文件会被判为 `orphan`(框架里没有对应的可覆盖路径)。
|
||||
4. **图片资源统一用 PNG**。非 PNG 图片不做尺寸校验(`scripts/lib/skin.mjs`),会削弱保护。
|
||||
|
||||
## 子游戏怎么换肤
|
||||
|
||||
在 `games/<name>/assets/game/override/` 下放**同路径同名**文件即可,例如
|
||||
`override/atlas-hall/btn_start.png` 覆盖 `framework/ui/atlas-hall/btn_start.png`。
|
||||
|
||||
- 只换图片内容,**不要带 `.meta`**(UUID 在 meta 里,换了会断引用)
|
||||
- 因此**替换图必须与默认图同尺寸**;确需改尺寸时才连 `.meta` 一起提供(例外通道,合成脚本会覆写 uuid)
|
||||
- Spine 必须**整套**覆盖,缺件报错;Spine 贴图豁免同尺寸约束
|
||||
(⏸ 该行为尚未实测:仓库现无任何 Spine 素材,见 spec §6 待验证项 4,2026-08-27)
|
||||
- 自检:`npm run check-skin <name>`
|
||||
|
||||
**自定义 Prefab 不走 override**,放 `game/prefabs/`(组件替换通道,另见后续 spec)。
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"ver": "1.0.1",
|
||||
"importer": "text",
|
||||
"imported": true,
|
||||
"uuid": "8ef32ef9-9cdb-42aa-b6d1-b7ac9f9d05ba",
|
||||
"files": [
|
||||
".json"
|
||||
],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "997150b2-370b-4f83-857e-eff55be5e566",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "bedaf0af-31f4-4d84-877b-b81c7fa05d28",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "a76b91a3-80f2-4015-8d14-fcfa96a9dd26",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "6393a57b-7ba5-41d0-b510-411bc5f5092e",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "37ce3fec-20dc-4a99-a2e1-5ed951288291",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "ddd184a4-ac0b-4c67-9d17-8915235c3f97",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "d83eacdf-5929-43cd-87f7-25da1b145dae",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "bc260227-bf51-43a0-948b-e02b449ccc2b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "1.2.0",
|
||||
"importer": "directory",
|
||||
"imported": true,
|
||||
"uuid": "411f089b-cbbf-41cb-93dd-7c708d2262a5",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import type { ResolvedTheme } from './types.ts';
|
||||
|
||||
/**
|
||||
* 主题缺省值的**唯一来源**(spec §4.3)。
|
||||
* 这是 CLAUDE.md 第二准则明文列出的唯一例外——缺省集中在来源处,下游子游戏零兜底。
|
||||
* 不得以此为先例:协议数据缺失仍须显式暴露,不得 ?? 抹平。
|
||||
*/
|
||||
export const DEFAULT_THEME: ResolvedTheme = {
|
||||
colors: {
|
||||
primary: '#1E7A46',
|
||||
accent: '#F2B233',
|
||||
textPrimary: '#FFFFFF',
|
||||
textSecondary: '#C8D6CE',
|
||||
disabled: '#8A8A8A',
|
||||
warning: '#D94F3D',
|
||||
},
|
||||
fonts: {
|
||||
familyPath: null,
|
||||
sizeTitle: 32,
|
||||
sizeBody: 24,
|
||||
sizeCaption: 20,
|
||||
},
|
||||
layout: {
|
||||
myInfoAnchorX: 130,
|
||||
nicknameMaxChars: 8,
|
||||
infoNicknameMaxChars: 16,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "5f3f9c00-dd28-4c44-91c2-b747b751595c",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import type { ResolvedTheme, ThemeConfig } from './types.ts';
|
||||
import { DEFAULT_THEME } from './default-theme.ts';
|
||||
|
||||
/** 把子游戏的部分主题配置合并到默认主题上。分组独立合并,不改动 DEFAULT_THEME。 */
|
||||
export function resolveTheme(game: ThemeConfig = {}): ResolvedTheme {
|
||||
return {
|
||||
colors: { ...DEFAULT_THEME.colors, ...game.colors },
|
||||
fonts: { ...DEFAULT_THEME.fonts, ...game.fonts },
|
||||
layout: { ...DEFAULT_THEME.layout, ...game.layout },
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "86f0162e-5db7-47c9-9fe0-a31d1e9dd643",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { ResolvedTheme } from './types.ts';
|
||||
|
||||
let current: ResolvedTheme | null = null;
|
||||
|
||||
/**
|
||||
* 主题持有者。皮肤在构建期即定死,运行时不变,故为静态读取、**不做响应式**(spec §4.5)。
|
||||
* 子游戏在启动时调 init 注册自己的主题——框架不 import 子游戏(零耦合规则)。
|
||||
*/
|
||||
export const ThemeProvider = {
|
||||
init(theme: ResolvedTheme): void {
|
||||
current = theme;
|
||||
},
|
||||
get(): ResolvedTheme {
|
||||
if (!current) {
|
||||
throw new Error('ThemeProvider 未初始化:请在启动时调用 ThemeProvider.init(resolveTheme(theme))');
|
||||
}
|
||||
return current;
|
||||
},
|
||||
/** 仅供测试复位 */
|
||||
reset(): void {
|
||||
current = null;
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "105e374f-7644-4114-9ced-5082cc129939",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 主题契约。见 spec `2026-08-27-ui-asset-and-skin-design.md` §4。
|
||||
* 字段集不预先穷举——随框架 UI 抽象逐项浮现;加字段非破坏性,删字段/改语义为 breaking change。
|
||||
*/
|
||||
export interface ThemeColors {
|
||||
primary: string;
|
||||
accent: string;
|
||||
textPrimary: string;
|
||||
textSecondary: string;
|
||||
disabled: string;
|
||||
warning: string;
|
||||
}
|
||||
|
||||
export interface ThemeFonts {
|
||||
/** 字体资源逻辑路径;null = 用引擎默认字体 */
|
||||
familyPath: string | null;
|
||||
sizeTitle: number;
|
||||
sizeBody: number;
|
||||
sizeCaption: number;
|
||||
}
|
||||
|
||||
export interface ThemeLayout {
|
||||
/** 自己信息的对齐点 x(旧 Game_Config.Info.myPosition) */
|
||||
myInfoAnchorX: number;
|
||||
/** 主界面昵称最大字符数(旧 Game_Config.Info.Mainnickname) */
|
||||
nicknameMaxChars: number;
|
||||
/** 个人信息页昵称最大字符数(旧 Game_Config.Info.Infonickname) */
|
||||
infoNicknameMaxChars: number;
|
||||
}
|
||||
|
||||
/** 子游戏声明用:各组均可只写要改的字段 */
|
||||
export interface ThemeConfig {
|
||||
colors?: Partial<ThemeColors>;
|
||||
fonts?: Partial<ThemeFonts>;
|
||||
layout?: Partial<ThemeLayout>;
|
||||
}
|
||||
|
||||
/** 合并后的完整主题,框架内部消费 */
|
||||
export interface ResolvedTheme {
|
||||
colors: ThemeColors;
|
||||
fonts: ThemeFonts;
|
||||
layout: ThemeLayout;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.24",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "3974f701-a0a3-4590-ba45-db688cf5a672",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# Cocos Creator MCP Server v1.7.9
|
||||
# Cocos Creator MCP Server v1.8.0
|
||||
|
||||
## 安装说明
|
||||
|
||||
@@ -40,5 +40,5 @@
|
||||
- 🛒 购买/续费: https://www.vberai.com/game-engines/cocos
|
||||
|
||||
---
|
||||
构建信息: v1.7.9 (production) - 2026-07-18 16:06:14
|
||||
构建信息: v1.8.0 (production) - 2026-08-24 14:27:23
|
||||
作者: 李大仙 (LiDaxian)
|
||||
|
||||
@@ -123,11 +123,23 @@ module.exports = {
|
||||
"transport_protocol": "بروتوكول النقل",
|
||||
"transport_type": "نوع النقل",
|
||||
"mcp_url": "رابط MCP",
|
||||
"port_fallback_title": "المنفذ {0} غير متاح — تم التبديل إلى {1}",
|
||||
"port_fallback_reason": "المنفذ {0} محجوز من قبل النظام أو محظور بواسطة جدار الحماية/برنامج مكافحة الفيروسات (شائع في نظام Windows المثبت عليه Hyper-V أو WSL2 أو Docker). الخادم يعمل بشكل طبيعي على المنفذ الجديد.",
|
||||
"port_fallback_resynced": "تم تحديث إعدادات العملاء التالية: {0}. أعد تشغيلها ليصبح المنفذ الجديد ساري المفعول.",
|
||||
"port_fallback_no_clients": "إذا سبق أن أعددت عملاء الذكاء الاصطناعي، فأعد إعدادهم على المنفذ الجديد من خلال الإعداد بنقرة واحدة.",
|
||||
"port_fallback_dismiss": "حسنًا",
|
||||
"port_fallback_title": "المنفذ {0} مشغول — تم التبديل إلى {1}",
|
||||
"net_title": "الشبكة / الوكيل",
|
||||
"net_direct": "اتصال مباشر",
|
||||
"net_via": "عبر {0} ({1})",
|
||||
"net_source_manual": "يدوي",
|
||||
"net_source_env": "متغير بيئة",
|
||||
"net_source_system": "إعدادات النظام",
|
||||
"net_mode_auto": "كشف تلقائي",
|
||||
"net_mode_manual": "يدوي",
|
||||
"net_mode_off": "بدون وكيل",
|
||||
"net_save": "حفظ",
|
||||
"net_test": "اختبار الاتصال",
|
||||
"net_testing": "جارٍ الاختبار…",
|
||||
"net_test_ok": "تم الاتصال ({0} م.ث)",
|
||||
"net_test_fail": "فشل: {0}",
|
||||
"net_hint": "انتهاء مهلة تسجيل الدخول سببه الشبكة في الغالب. إن كنت تستخدم VPN/وكيلاً فأدخل عنوانه هنا (مثل http://127.0.0.1:7890) أو حوّل البرنامج إلى الوضع الشامل (TUN).",
|
||||
"net_pac_warning": "يستخدم النظام برنامج PAC للتهيئة التلقائية ولا يمكن للملحق تفسيره. اختر «يدوي» وأدخل عنوان الوكيل.",
|
||||
"port_label": "المنفذ",
|
||||
"auto_start_label": "بدء تلقائي",
|
||||
"debug_log_label": "سجل التصحيح",
|
||||
@@ -240,4 +252,7 @@ module.exports = {
|
||||
"subscription_network_error": "فشل الاتصال بالشبكة. تحقق من شبكتك وحاول مرة أخرى.",
|
||||
"subscription_server_error": "أرجع الخادم استجابة غير صالحة. حاول مرة أخرى لاحقًا.",
|
||||
"subscription_google_authorization_failed": "لم يكتمل تفويض Google. يرجى المحاولة مرة أخرى."
|
||||
,"lan_access_label": "السماح بالوصول عبر الشبكة المحلية"
|
||||
,"lan_access_warning": "مرتبط بالعنوان 0.0.0.0. يمكن لأي شخص على هذه الشبكة الاتصال والتحكم في المحرر — لا تتحقق نقطة النهاية هذه من أي بيانات اعتماد. فعّل هذا الخيار على الشبكات الموثوقة فقط."
|
||||
,"lan_address": "عنوان الشبكة المحلية"
|
||||
};
|
||||
|
||||
@@ -123,11 +123,23 @@ module.exports = {
|
||||
"transport_protocol": "Transportprotokoll",
|
||||
"transport_type": "Transporttyp",
|
||||
"mcp_url": "MCP-URL",
|
||||
"port_fallback_title": "Port {0} nicht verfügbar – zu {1} gewechselt",
|
||||
"port_fallback_reason": "Port {0} ist vom System reserviert oder wird von einer Firewall/Antivirensoftware blockiert (häufig unter Windows mit Hyper-V, WSL2 oder Docker). Der Server läuft auf dem neuen Port normal.",
|
||||
"port_fallback_resynced": "Konfiguration aktualisiert für: {0}. Starten Sie diese Clients neu, damit der neue Port wirksam wird.",
|
||||
"port_fallback_no_clients": "Falls Sie bereits AI-Clients eingerichtet haben, konfigurieren Sie sie über die Ein-Klick-Konfiguration erneut auf den neuen Port.",
|
||||
"port_fallback_dismiss": "Verstanden",
|
||||
"port_fallback_title": "Port {0} belegt – zu {1} gewechselt",
|
||||
"net_title": "Netzwerk / Proxy",
|
||||
"net_direct": "Direktverbindung",
|
||||
"net_via": "über {0} ({1})",
|
||||
"net_source_manual": "manuell",
|
||||
"net_source_env": "Umgebungsvariable",
|
||||
"net_source_system": "Systemeinstellungen",
|
||||
"net_mode_auto": "Automatisch",
|
||||
"net_mode_manual": "Manuell",
|
||||
"net_mode_off": "Kein Proxy",
|
||||
"net_save": "Speichern",
|
||||
"net_test": "Verbindung testen",
|
||||
"net_testing": "Wird getestet…",
|
||||
"net_test_ok": "Verbunden ({0} ms)",
|
||||
"net_test_fail": "Fehlgeschlagen: {0}",
|
||||
"net_hint": "Login-Timeouts sind fast immer ein Netzwerkproblem. Bei VPN/Proxy hier die Adresse eintragen (z. B. http://127.0.0.1:7890) oder den Client auf globalen (TUN) Modus umstellen.",
|
||||
"net_pac_warning": "Das System nutzt ein PAC-Skript, das dieses Plugin nicht auswerten kann. Auf Manuell umstellen und die Proxy-Adresse eintragen.",
|
||||
"port_label": "Port",
|
||||
"auto_start_label": "Autostart",
|
||||
"debug_log_label": "Debug-Protokoll",
|
||||
@@ -240,4 +252,7 @@ module.exports = {
|
||||
"subscription_network_error": "Netzwerkverbindung fehlgeschlagen. Überprüfen Sie Ihr Netzwerk und versuchen Sie es erneut.",
|
||||
"subscription_server_error": "Der Server hat eine ungültige Antwort zurückgegeben. Versuchen Sie es später erneut.",
|
||||
"subscription_google_authorization_failed": "Die Google-Autorisierung wurde nicht abgeschlossen. Bitte versuchen Sie es erneut."
|
||||
,"lan_access_label": "LAN-Zugriff erlauben"
|
||||
,"lan_access_warning": "An 0.0.0.0 gebunden. Jeder in diesem Netzwerk kann sich verbinden und den Editor steuern — dieser Endpunkt prüft keinerlei Anmeldedaten. Nur in einem vertrauenswürdigen Netzwerk aktivieren."
|
||||
,"lan_address": "LAN-Adresse"
|
||||
};
|
||||
|
||||
@@ -125,11 +125,23 @@ module.exports = {
|
||||
"transport_protocol": "Transport Protocol",
|
||||
"transport_type": "Transport Type",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "Port {0} unavailable — switched to {1}",
|
||||
"port_fallback_reason": "Port {0} is reserved by the system or blocked by a firewall/antivirus (common on Windows with Hyper-V, WSL2 or Docker installed). The server is running normally on the new port.",
|
||||
"port_fallback_resynced": "Updated the config for: {0}. Restart these clients for the new port to take effect.",
|
||||
"port_fallback_no_clients": "If you have already configured AI clients, re-run the one-click setup to point them at the new port.",
|
||||
"port_fallback_dismiss": "Got it",
|
||||
"port_fallback_title": "Port {0} is in use — switched to {1}",
|
||||
"net_title": "Network / Proxy",
|
||||
"net_direct": "Direct",
|
||||
"net_via": "via {0} ({1})",
|
||||
"net_source_manual": "manual",
|
||||
"net_source_env": "env var",
|
||||
"net_source_system": "system settings",
|
||||
"net_mode_auto": "Auto-detect",
|
||||
"net_mode_manual": "Manual",
|
||||
"net_mode_off": "No proxy",
|
||||
"net_save": "Save",
|
||||
"net_test": "Test connection",
|
||||
"net_testing": "Testing…",
|
||||
"net_test_ok": "Connected ({0} ms)",
|
||||
"net_test_fail": "Failed: {0}",
|
||||
"net_hint": "Login timeouts are usually a network issue. If you use a VPN/proxy, enter its address here (e.g. http://127.0.0.1:7890) or switch the client to global (TUN) mode.",
|
||||
"net_pac_warning": "The system uses a PAC auto-config script, which this plugin cannot evaluate. Switch to Manual and enter the proxy address.",
|
||||
"port_label": "Port",
|
||||
"auto_start_label": "Auto Start",
|
||||
"debug_log_label": "Debug Log",
|
||||
@@ -241,4 +253,7 @@ module.exports = {
|
||||
,"subscription_network_error": "Network connection failed. Check your network and try again."
|
||||
,"subscription_server_error": "The server returned an invalid response. Try again later."
|
||||
,"subscription_google_authorization_failed": "Google authorization was not completed. Please try again."
|
||||
,"lan_access_label": "Allow LAN access"
|
||||
,"lan_access_warning": "Bound to 0.0.0.0. Anyone on this network can connect and drive the editor — this endpoint verifies no credentials. Only enable on a trusted network."
|
||||
,"lan_address": "LAN address"
|
||||
};
|
||||
|
||||
@@ -123,11 +123,23 @@ module.exports = {
|
||||
"transport_protocol": "Protocolo de transporte",
|
||||
"transport_type": "Tipo de transporte",
|
||||
"mcp_url": "URL MCP",
|
||||
"port_fallback_title": "Puerto {0} no disponible: se cambió a {1}",
|
||||
"port_fallback_reason": "El puerto {0} está reservado por el sistema o bloqueado por un firewall/antivirus (habitual en Windows con Hyper-V, WSL2 o Docker). El servidor funciona con normalidad en el nuevo puerto.",
|
||||
"port_fallback_resynced": "Se actualizó la configuración de: {0}. Reinicie estos clientes para que el nuevo puerto surta efecto.",
|
||||
"port_fallback_no_clients": "Si ya configuró clientes de IA, vuelva a configurarlos en el nuevo puerto desde la configuración con un clic.",
|
||||
"port_fallback_dismiss": "Entendido",
|
||||
"port_fallback_title": "Puerto {0} ocupado: se cambió a {1}",
|
||||
"net_title": "Red / Proxy",
|
||||
"net_direct": "Conexión directa",
|
||||
"net_via": "vía {0} ({1})",
|
||||
"net_source_manual": "manual",
|
||||
"net_source_env": "variable de entorno",
|
||||
"net_source_system": "ajustes del sistema",
|
||||
"net_mode_auto": "Detección automática",
|
||||
"net_mode_manual": "Manual",
|
||||
"net_mode_off": "Sin proxy",
|
||||
"net_save": "Guardar",
|
||||
"net_test": "Probar conexión",
|
||||
"net_testing": "Probando…",
|
||||
"net_test_ok": "Conectado ({0} ms)",
|
||||
"net_test_fail": "Error: {0}",
|
||||
"net_hint": "Los tiempos de espera al iniciar sesión suelen ser de red. Si usas VPN/proxy, indica su dirección aquí (p. ej. http://127.0.0.1:7890) o cambia el cliente a modo global (TUN).",
|
||||
"net_pac_warning": "El sistema usa un script PAC que este plugin no puede evaluar. Cambia a Manual e introduce la dirección del proxy.",
|
||||
"port_label": "Puerto",
|
||||
"auto_start_label": "Inicio auto",
|
||||
"debug_log_label": "Registro de depuración",
|
||||
@@ -240,4 +252,7 @@ module.exports = {
|
||||
"subscription_network_error": "Falló la conexión de red. Comprueba tu red e inténtalo de nuevo.",
|
||||
"subscription_server_error": "El servidor devolvió una respuesta no válida. Inténtalo más tarde.",
|
||||
"subscription_google_authorization_failed": "No se completó la autorización de Google. Inténtalo de nuevo."
|
||||
,"lan_access_label": "Permitir acceso desde la red local"
|
||||
,"lan_access_warning": "Vinculado a 0.0.0.0. Cualquiera en esta red puede conectarse y manejar el editor: este punto de conexión no verifica ninguna credencial. Actívalo solo en una red de confianza."
|
||||
,"lan_address": "Dirección de red local"
|
||||
};
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extension_name": "Cocos MCP Server",
|
||||
"description": "AI MCP Server para sa Cocos Creator 3.8",
|
||||
"panel_title": "MCP Server",
|
||||
"open_panel": "Buksan ang MCP Panel",
|
||||
"start_server": "Simulan ang Server",
|
||||
"stop_server": "Ihinto ang Server",
|
||||
"server_status": "Katayuan ng Server",
|
||||
"port": "Port",
|
||||
"settings": "Mga Setting",
|
||||
"connected": "Nakakonekta",
|
||||
"disconnected": "Nadiskonekta",
|
||||
"server_running": "Tumatakbo ang server sa port {0}",
|
||||
"server_stopped": "Huminto na ang server",
|
||||
"auto_start": "Awtomatikong Simulan",
|
||||
"debug_log": "Debug Log",
|
||||
"max_connections": "Maximum na Koneksyon",
|
||||
"connection_info": "Impormasyon ng Koneksyon",
|
||||
"http_url": "HTTP URL",
|
||||
"copy": "Kopyahin",
|
||||
"save_settings": "I-save ang Mga Setting",
|
||||
"settings_saved": "Matagumpay na na-save ang mga setting",
|
||||
"server_started": "Nagsimula ang MCP Server",
|
||||
"server_stopped_msg": "Huminto ang MCP Server",
|
||||
"failed_to_start": "Nabigong simulan ang server",
|
||||
"failed_to_stop": "Nabigong ihinto ang server",
|
||||
"failed_to_save": "Nabigong i-save ang mga setting",
|
||||
"url_copied": "Nakopya ang HTTP URL sa clipboard",
|
||||
"tool_manager": "Tagapamahala ng Kasangkapan",
|
||||
"open_tool_manager": "Buksan ang Tagapamahala ng Kasangkapan",
|
||||
"create_config": "Gumawa ng Configuration",
|
||||
"edit_config": "I-edit ang Configuration",
|
||||
"delete_config": "Tanggalin ang Configuration",
|
||||
"import_config": "Mag-import ng Configuration",
|
||||
"export_config": "Mag-export ng Configuration",
|
||||
"apply_config": "Ilapat ang Configuration",
|
||||
"select_all": "Piliin Lahat",
|
||||
"deselect_all": "Alisin ang Lahat ng Pinili",
|
||||
"save_changes": "I-save ang Mga Pagbabago",
|
||||
"config_name": "Pangalan ng Configuration",
|
||||
"config_description": "Paglalarawan ng Configuration",
|
||||
"current_config": "Kasalukuyang Configuration",
|
||||
"tool_management": "Pamamahala ng Kasangkapan",
|
||||
"total_tools": "Kabuuang Kasangkapan",
|
||||
"enabled_tools": "Naka-enable",
|
||||
"disabled_tools": "Naka-disable",
|
||||
"no_config_selected": "Walang Napiling Configuration",
|
||||
"select_config_first": "Pumili muna ng configuration o gumawa ng bago",
|
||||
"config_created": "Matagumpay na nagawa ang configuration",
|
||||
"config_updated": "Matagumpay na na-update ang configuration",
|
||||
"config_deleted": "Matagumpay na natanggal ang configuration",
|
||||
"config_applied": "Matagumpay na nailapat ang configuration",
|
||||
"config_exported": "Matagumpay na na-export ang configuration",
|
||||
"config_imported": "Matagumpay na na-import ang configuration",
|
||||
"confirm_delete": "Kumpirmahin ang Pagtanggal",
|
||||
"delete_config_confirm": "Sigurado ka bang tatanggalin ang configuration na \"{0}\"? Hindi na ito maibabalik.",
|
||||
"max_config_slots_reached": "Naabot na ang maximum na slot ng configuration ({0})",
|
||||
"invalid_config_format": "Hindi wastong format ng configuration",
|
||||
"invalid_json_format": "Hindi wastong format ng JSON o istruktura ng configuration",
|
||||
"server_tab": "Server",
|
||||
"tools_tab": "Pamamahala ng Kasangkapan",
|
||||
"available_tools": "Mga Available na Kasangkapan",
|
||||
"scene_tools": "Mga Kasangkapan sa Scene",
|
||||
"node_tools": "Mga Kasangkapan sa Node",
|
||||
"component_tools": "Mga Kasangkapan sa Component",
|
||||
"prefab_tools": "Mga Kasangkapan sa Prefab",
|
||||
"project_tools": "Mga Kasangkapan sa Proyekto",
|
||||
"debug_tools": "Mga Kasangkapan sa Debug",
|
||||
"preferences_tools": "Mga Kagustuhan",
|
||||
"server_tools": "Mga Kasangkapan sa Server",
|
||||
"broadcast_tools": "Mga Kasangkapan sa Broadcast",
|
||||
"scene_advanced_tools": "Mga Advanced na Kasangkapan sa Scene",
|
||||
"scene_view_tools": "Mga Kasangkapan sa Scene View",
|
||||
"reference_image_tools": "Mga Kasangkapan sa Reference Image",
|
||||
"asset_advanced_tools": "Mga Advanced na Kasangkapan sa Asset",
|
||||
"validation_tools": "Mga Kasangkapan sa Validation",
|
||||
"brand_name": "LiDaxian MCP Series",
|
||||
"brand_subtitle": "LiDaxian MCP Series",
|
||||
"contact_email": "Email",
|
||||
"contact_wechat": "WeChat",
|
||||
"contact_github": "GitHub",
|
||||
"machine_id_label": "Machine ID",
|
||||
"purchase_renew": "Bumili / I-renew",
|
||||
"purchase_url": "https://www.vberai.com/game-engines/cocos",
|
||||
"auth_title": "Pumili ng paraan ng pag-sign in",
|
||||
"auth_subtitle": "Ilagay ang iyong email at activation code",
|
||||
"auth_choose_subtitle": "Pumili ng paraan ng pag-sign in upang magpatuloy",
|
||||
"email_label": "Email Address",
|
||||
"activation_code_label": "Activation Code",
|
||||
"email_placeholder": "Ilagay ang email",
|
||||
"code_placeholder": "Ilagay ang code (XXXXX-XXXXX-XXXXX-XXXXX-XXXXX)",
|
||||
"activate_button": "I-activate",
|
||||
"deactivate_button": "I-deactivate",
|
||||
"activating": "Ina-activate...",
|
||||
"deactivating": "Dina-deactivate...",
|
||||
"license_status": "Katayuan ng Lisensya",
|
||||
"license_type": "Uri ng Lisensya",
|
||||
"expiry_date": "Petsa ng Pag-expire",
|
||||
"days_remaining": "Natitirang Araw",
|
||||
"days_unit": "araw",
|
||||
"permanent": "Permanente",
|
||||
"expiry_warning_title": "Malapit Nang Mag-expire ang Lisensya",
|
||||
"expiry_warning_message": "Mag-e-expire ang iyong lisensya sa loob ng {0} araw. Mangyaring i-renew upang maiwasan ang pagkaantala ng serbisyo.",
|
||||
"renew_now": "I-renew Ngayon",
|
||||
"i_understand": "Naiintindihan Ko",
|
||||
"activation_success": "Matagumpay na na-activate ang lisensya!",
|
||||
"activation_failed": "Nabigo ang activation: {0}",
|
||||
"invalid_code": "Hindi wastong format ng activation code",
|
||||
"purchase_license": "Bumili ng Lisensya",
|
||||
"renew_license": "I-renew ang Lisensya",
|
||||
"contact_support": "Suporta",
|
||||
"language_switch": "Filipino",
|
||||
"version_label": "Bersyon",
|
||||
"license_required": "Kailangan ng lisensya upang simulan ang server",
|
||||
"license_check_failed": "Nabigo ang pagsusuri ng lisensya",
|
||||
|
||||
"status_stopped": "Huminto",
|
||||
"status_running": "Tumatakbo",
|
||||
"status_error": "Nabigo ang Koneksyon",
|
||||
"config_tab": "Mabilis na Config",
|
||||
"status_label": "Katayuan",
|
||||
"connections_count": "Mga Koneksyon",
|
||||
"transport_protocol": "Transport Protocol",
|
||||
"transport_type": "Uri ng Transport",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "Ginagamit ang port {0} — lumipat sa {1}",
|
||||
"net_title": "Network / Proxy",
|
||||
"net_direct": "Direktang koneksyon",
|
||||
"net_via": "sa pamamagitan ng {0} ({1})",
|
||||
"net_source_manual": "manual",
|
||||
"net_source_env": "environment variable",
|
||||
"net_source_system": "system settings",
|
||||
"net_mode_auto": "Auto-detect",
|
||||
"net_mode_manual": "Manual",
|
||||
"net_mode_off": "Walang proxy",
|
||||
"net_save": "I-save",
|
||||
"net_test": "I-test ang koneksyon",
|
||||
"net_testing": "Sinusubukan…",
|
||||
"net_test_ok": "Nakakonekta ({0} ms)",
|
||||
"net_test_fail": "Nabigo: {0}",
|
||||
"net_hint": "Ang login timeout ay karaniwang isyu sa network. Kung gumagamit ng VPN/proxy, ilagay ang address (hal. http://127.0.0.1:7890) o ilipat ang client sa global (TUN) mode.",
|
||||
"net_pac_warning": "Gumagamit ang system ng PAC script na hindi kayang basahin ng plugin. Pumili ng Manual at ilagay ang proxy address.",
|
||||
"port_label": "Port",
|
||||
"auto_start_label": "Awtomatikong Simulan",
|
||||
"debug_log_label": "Debug Log",
|
||||
"max_connections_label": "Maximum na Koneksyon",
|
||||
"server_settings_title": "Mga Setting ng Server",
|
||||
"server_status_title": "Katayuan ng Server",
|
||||
"connection_info_title": "Impormasyon ng Koneksyon",
|
||||
"tools_count": "{0} na kasangkapan",
|
||||
"tools_stats": "{0} na kasangkapan ({1} naka-enable / {2} naka-disable)",
|
||||
"quick_config_title": "Mabilis na Pag-configure ng MCP Clients",
|
||||
"quick_config_desc": "Awtomatikong i-configure ang kasalukuyang MCP server sa mga pangunahing AI editor at CLI tool",
|
||||
"server_info": "Impormasyon ng Server",
|
||||
"server_name_label": "Pangalan ng Server",
|
||||
"server_address": "Address ng Server",
|
||||
"timeout_ms": "Timeout (ms)",
|
||||
"auto_config_title": "Awtomatikong Config (IDE Editors + Codex CLI)",
|
||||
"add_all": "Idagdag Lahat",
|
||||
"remove_all": "Alisin Lahat",
|
||||
"auto_config_note": "I-click ang button upang awtomatikong baguhin ang mga config file (Cursor, Windsurf, Trae, Codex CLI)",
|
||||
"configured": "Naka-configure",
|
||||
"not_configured": "Hindi Naka-configure",
|
||||
"auto_config_btn": "Awtomatikong Config",
|
||||
"delete_btn": "Tanggalin",
|
||||
"copy_config": "Kopyahin ang Config",
|
||||
"open_config": "Buksan ang Config",
|
||||
"manual_config_title": "Manwal na Config (Claude CLI + Gemini CLI)",
|
||||
"refresh_commands": "I-refresh ang Mga Command",
|
||||
"manual_config_note": "Ang sumusunod na mga CLI tool ay nangangailangan ng manwal na pagpapatakbo ng terminal command",
|
||||
"config_scope": "Saklaw ng Config:",
|
||||
"scope_user": "Antas ng User",
|
||||
"scope_project": "Antas ng Proyekto",
|
||||
"operation_log": "Log ng Operasyon",
|
||||
"no_logs": "Walang record ng operasyon",
|
||||
"copyright_line": "© 2025 LiDaxian - Cocos Creator MCP Server v{0}",
|
||||
"protocol_line": "Batay sa Streamable HTTP Protocol (MCP 2025-03-26)",
|
||||
"tools_copyright_line1": "Sistema ng Kasangkapan - 13 intent-level na kasangkapan, 150+ operasyon sa editor, dispatch gamit ang action code",
|
||||
"config_copyright_line1": "Mabilis na Config - Sinusuportahan ang Cursor, Windsurf, Trae, Codex CLI, Claude CLI, Gemini CLI",
|
||||
"email_required": "Mangyaring ilagay ang email at activation code",
|
||||
"permanent_label": "Permanente",
|
||||
"logout_button": "Mag-log out",
|
||||
"invite_code_label": "Invite Code",
|
||||
"copy_button": "Kopyahin",
|
||||
"change_code_button": "Palitan ang Code",
|
||||
"change_code_title": "Palitan ang Activation Code",
|
||||
"change_code_desc": "Ilagay ang iyong bagong activation code. Aalisin muna ang kasalukuyang device binding.",
|
||||
"confirm_change": "Kumpirmahin",
|
||||
"cancel": "Kanselahin",
|
||||
"code_required": "Mangyaring ilagay ang activation code",
|
||||
"expiry_inline_warning": "Malapit nang mag-expire!",
|
||||
"store_purchase_hint": "Kung binili mo ang plugin na ito sa Cocos Store, mangyaring kontakin ako upang makuha ang iyong activation code.",
|
||||
"license_expired_title": "Nag-expire na ang Lisensya",
|
||||
"license_expired_message": "Nag-expire na ang iyong lisensya at hindi masisimulan ang server. Mangyaring i-renew o i-activate muli upang magpatuloy.",
|
||||
"go_to_login": "Pumunta sa Pag-login",
|
||||
|
||||
"log_load_config_failed": "Nabigong i-load ang katayuan ng config: {0}",
|
||||
"log_gen_cli_failed": "Nabigong bumuo ng mga CLI command: {0}",
|
||||
"log_add_client_failed": "Nabigong idagdag sa {0}: {1}",
|
||||
"log_remove_client_failed": "Nabigong alisin mula sa {0}: {1}",
|
||||
"quick_config_dialog_title": "One-click na Configuration",
|
||||
"config_operation_failed": "Nabigo ang operasyon ng configuration",
|
||||
"config_file_not_found_changed_path": "Nabigo ang configuration: hindi mahanap ang config file. Maaaring naka-install ang {0} sa custom na path.",
|
||||
"log_batch_add_failed": "Nabigo ang batch add: {0}",
|
||||
"log_batch_remove_failed": "Nabigo ang batch remove: {0}",
|
||||
"log_copied_config": "Nakopya ang config ng {0} sa clipboard",
|
||||
"log_gen_config_failed": "Nabigong bumuo ng config",
|
||||
"log_copy_config_failed": "Nabigong kopyahin ang config: {0}",
|
||||
"log_copied_command": "Nakopya ang command ng {0} sa clipboard",
|
||||
"log_copy_command_failed": "Nabigong kopyahin ang command: {0}",
|
||||
"log_opened_config_file": "Binuksan ang config file: {0}",
|
||||
"log_open_config_failed": "Nabigong buksan ang config file",
|
||||
"log_open_config_error": "Nabigong buksan ang config file: {0}",
|
||||
|
||||
"update_available_title": "May Available na Update",
|
||||
"update_new_version": "Available na ang bagong bersyon v{0}!",
|
||||
"update_now": "I-update Ngayon",
|
||||
"update_not_now": "Hindi Ngayon",
|
||||
"update_skip_version": "Laktawan ang v{0}",
|
||||
"check_for_updates": "Tingnan ang Mga Update",
|
||||
"update_checking": "Sinusuri...",
|
||||
"update_no_update": "Ginagamit mo na ang pinakabagong bersyon",
|
||||
"update_urgency_critical": "Kritikal na Update",
|
||||
"update_urgency_high": "Mahalagang Update",
|
||||
"update_current_version": "Kasalukuyang bersyon: v{0}",
|
||||
"update_changelog": "Changelog"
|
||||
,"update_changelog_empty": "Walang ibinigay na release notes para sa bersyong ito."
|
||||
,"subscription_login_title": "Account sa subscription ng VberAI"
|
||||
,"subscription_password_label": "Password"
|
||||
,"subscription_password_placeholder": "Ilagay ang password"
|
||||
,"subscription_login_button": "Mag-sign in gamit ang VberAI"
|
||||
,"subscription_google_button": "Mag-sign in gamit ang Google"
|
||||
,"subscription_google_pending": "Nabuksan ang browser. Kumpletuhin ang Google authorization, pagkatapos ay bumalik sa Cocos Creator."
|
||||
,"subscription_google_pending_button": "Naghihintay ng Google authorization..."
|
||||
,"subscription_register_button": "Gumawa ng VberAI account"
|
||||
,"subscription_logging_in": "Nagsa-sign in..."
|
||||
,"subscription_credentials_required": "Mangyaring ilagay ang email at password"
|
||||
,"subscription_login_success": "Naka-sign in ang subscription account"
|
||||
,"subscription_login_failed": "Nabigo ang pag-sign in ng subscription"
|
||||
,"subscription_not_entitled": "Hindi kasama ang Cocos 3.x MCP sa iyong kasalukuyang subscription. Mag-upgrade upang magpatuloy."
|
||||
,"subscription_upgrade_to": "Mag-upgrade sa {0} upang magamit ang produktong ito"
|
||||
,"subscription_upgrade_action": "I-upgrade ang subscription"
|
||||
,"subscription_logged_in": "Naka-sign in na account"
|
||||
,"subscription_choose_button": "Account sa subscription ng VberAI"
|
||||
,"license_choose_button": "Activation code"
|
||||
,"subscription_method_label": "Subscription"
|
||||
,"back_to_login_choices": "← Pumili ng ibang paraan ng pag-sign in"
|
||||
,"subscription_signed_out_other_device": "Naka-sign out: ginamit ang iyong account sa ibang device."
|
||||
,"subscription_google_cancelled": "Kinansela ang pag-sign in gamit ang Google."
|
||||
,"subscription_request_timeout": "Nag-timeout ang request. Suriin ang iyong network at subukan muli."
|
||||
,"subscription_network_error": "Nabigo ang koneksyon sa network. Suriin ang iyong network at subukan muli."
|
||||
,"subscription_server_error": "Nagbalik ang server ng hindi wastong tugon. Subukan muli mamaya."
|
||||
,"subscription_google_authorization_failed": "Hindi nakumpleto ang Google authorization. Mangyaring subukan muli."
|
||||
,"lan_access_label": "Payagan ang LAN access"
|
||||
,"lan_access_warning": "Naka-bind sa 0.0.0.0. Sinuman sa network na ito ay makakakonekta at makakakontrol sa editor — walang kredensyal na bine-verify ang endpoint na ito. I-enable lamang sa pinagkakatiwalaang network."
|
||||
,"lan_address": "LAN address"
|
||||
};
|
||||
@@ -123,11 +123,23 @@ module.exports = {
|
||||
"transport_protocol": "Protocole de transport",
|
||||
"transport_type": "Type de transport",
|
||||
"mcp_url": "URL MCP",
|
||||
"port_fallback_title": "Port {0} indisponible — basculé vers {1}",
|
||||
"port_fallback_reason": "Le port {0} est réservé par le système ou bloqué par un pare-feu/antivirus (fréquent sous Windows avec Hyper-V, WSL2 ou Docker). Le serveur fonctionne normalement sur le nouveau port.",
|
||||
"port_fallback_resynced": "Configuration mise à jour pour : {0}. Redémarrez ces clients pour que le nouveau port prenne effet.",
|
||||
"port_fallback_no_clients": "Si vous avez déjà configuré des clients IA, reconfigurez-les sur le nouveau port via la configuration en un clic.",
|
||||
"port_fallback_dismiss": "Compris",
|
||||
"port_fallback_title": "Port {0} occupé — basculé vers {1}",
|
||||
"net_title": "Réseau / Proxy",
|
||||
"net_direct": "Connexion directe",
|
||||
"net_via": "via {0} ({1})",
|
||||
"net_source_manual": "manuel",
|
||||
"net_source_env": "variable d’env.",
|
||||
"net_source_system": "réglages système",
|
||||
"net_mode_auto": "Détection auto",
|
||||
"net_mode_manual": "Manuel",
|
||||
"net_mode_off": "Sans proxy",
|
||||
"net_save": "Enregistrer",
|
||||
"net_test": "Tester la connexion",
|
||||
"net_testing": "Test en cours…",
|
||||
"net_test_ok": "Connecté ({0} ms)",
|
||||
"net_test_fail": "Échec : {0}",
|
||||
"net_hint": "Les délais de connexion viennent presque toujours du réseau. Si vous utilisez un VPN/proxy, saisissez son adresse ici (ex. http://127.0.0.1:7890) ou passez le client en mode global (TUN).",
|
||||
"net_pac_warning": "Le système utilise un script PAC que ce plugin ne peut pas évaluer. Passez en mode Manuel et saisissez l’adresse du proxy.",
|
||||
"port_label": "Port",
|
||||
"auto_start_label": "Démarrage auto",
|
||||
"debug_log_label": "Journal de débogage",
|
||||
@@ -240,4 +252,7 @@ module.exports = {
|
||||
"subscription_network_error": "Échec de la connexion réseau. Vérifiez votre réseau et réessayez.",
|
||||
"subscription_server_error": "Le serveur a renvoyé une réponse non valide. Réessayez plus tard.",
|
||||
"subscription_google_authorization_failed": "L'autorisation Google n'a pas été effectuée. Veuillez réessayer."
|
||||
,"lan_access_label": "Autoriser l'accès au réseau local"
|
||||
,"lan_access_warning": "Lié à 0.0.0.0. N'importe qui sur ce réseau peut se connecter et piloter l'éditeur — ce point de terminaison ne vérifie aucune information d'identification. À n'activer que sur un réseau de confiance."
|
||||
,"lan_address": "Adresse réseau local"
|
||||
};
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extension_name": "Cocos MCP सर्वर",
|
||||
"description": "Cocos Creator 3.8 के लिए AI MCP सर्वर",
|
||||
"panel_title": "MCP सर्वर",
|
||||
"open_panel": "MCP पैनल खोलें",
|
||||
"start_server": "सर्वर शुरू करें",
|
||||
"stop_server": "सर्वर रोकें",
|
||||
"server_status": "सर्वर स्थिति",
|
||||
"port": "पोर्ट",
|
||||
"settings": "सेटिंग्स",
|
||||
"connected": "कनेक्टेड",
|
||||
"disconnected": "डिस्कनेक्टेड",
|
||||
"server_running": "सर्वर पोर्ट {0} पर चल रहा है",
|
||||
"server_stopped": "सर्वर रुक गया है",
|
||||
"auto_start": "ऑटो स्टार्ट",
|
||||
"debug_log": "डीबग लॉगिंग",
|
||||
"max_connections": "अधिकतम कनेक्शन",
|
||||
"connection_info": "कनेक्शन जानकारी",
|
||||
"http_url": "HTTP URL",
|
||||
"copy": "कॉपी करें",
|
||||
"save_settings": "सेटिंग्स सहेजें",
|
||||
"settings_saved": "सेटिंग्स सफलतापूर्वक सहेजी गईं",
|
||||
"server_started": "MCP सर्वर शुरू हुआ",
|
||||
"server_stopped_msg": "MCP सर्वर रुक गया",
|
||||
"failed_to_start": "सर्वर शुरू करने में विफल",
|
||||
"failed_to_stop": "सर्वर रोकने में विफल",
|
||||
"failed_to_save": "सेटिंग्स सहेजने में विफल",
|
||||
"url_copied": "HTTP URL क्लिपबोर्ड पर कॉपी किया गया",
|
||||
"tool_manager": "टूल प्रबंधक",
|
||||
"open_tool_manager": "टूल प्रबंधक खोलें",
|
||||
"create_config": "कॉन्फ़िगरेशन बनाएँ",
|
||||
"edit_config": "कॉन्फ़िगरेशन संपादित करें",
|
||||
"delete_config": "कॉन्फ़िगरेशन हटाएँ",
|
||||
"import_config": "कॉन्फ़िगरेशन आयात करें",
|
||||
"export_config": "कॉन्फ़िगरेशन निर्यात करें",
|
||||
"apply_config": "कॉन्फ़िगरेशन लागू करें",
|
||||
"select_all": "सभी चुनें",
|
||||
"deselect_all": "चयन हटाएँ",
|
||||
"save_changes": "परिवर्तन सहेजें",
|
||||
"config_name": "कॉन्फ़िगरेशन नाम",
|
||||
"config_description": "कॉन्फ़िगरेशन विवरण",
|
||||
"current_config": "वर्तमान कॉन्फ़िगरेशन",
|
||||
"tool_management": "टूल प्रबंधन",
|
||||
"total_tools": "कुल टूल",
|
||||
"enabled_tools": "सक्षम",
|
||||
"disabled_tools": "अक्षम",
|
||||
"no_config_selected": "कोई कॉन्फ़िगरेशन चयनित नहीं",
|
||||
"select_config_first": "कृपया पहले कोई कॉन्फ़िगरेशन चुनें या नया बनाएँ",
|
||||
"config_created": "कॉन्फ़िगरेशन सफलतापूर्वक बनाया गया",
|
||||
"config_updated": "कॉन्फ़िगरेशन सफलतापूर्वक अपडेट किया गया",
|
||||
"config_deleted": "कॉन्फ़िगरेशन सफलतापूर्वक हटाया गया",
|
||||
"config_applied": "कॉन्फ़िगरेशन सफलतापूर्वक लागू किया गया",
|
||||
"config_exported": "कॉन्फ़िगरेशन सफलतापूर्वक निर्यात किया गया",
|
||||
"config_imported": "कॉन्फ़िगरेशन सफलतापूर्वक आयात किया गया",
|
||||
"confirm_delete": "हटाने की पुष्टि करें",
|
||||
"delete_config_confirm": "क्या आप वाकई कॉन्फ़िगरेशन \"{0}\" हटाना चाहते हैं? यह क्रिया पूर्ववत नहीं की जा सकती।",
|
||||
"max_config_slots_reached": "कॉन्फ़िगरेशन स्लॉट की अधिकतम सीमा पहुँच गई ({0})",
|
||||
"invalid_config_format": "अमान्य कॉन्फ़िगरेशन प्रारूप",
|
||||
"invalid_json_format": "अमान्य JSON प्रारूप या कॉन्फ़िगरेशन संरचना",
|
||||
"server_tab": "सर्वर",
|
||||
"tools_tab": "टूल प्रबंधन",
|
||||
"available_tools": "उपलब्ध टूल",
|
||||
"scene_tools": "सीन टूल",
|
||||
"node_tools": "नोड टूल",
|
||||
"component_tools": "कंपोनेंट टूल",
|
||||
"prefab_tools": "प्रीफ़ैब टूल",
|
||||
"project_tools": "प्रोजेक्ट टूल",
|
||||
"debug_tools": "डीबग टूल",
|
||||
"preferences_tools": "प्राथमिकताएँ",
|
||||
"server_tools": "सर्वर टूल",
|
||||
"broadcast_tools": "ब्रॉडकास्ट टूल",
|
||||
"scene_advanced_tools": "उन्नत सीन टूल",
|
||||
"scene_view_tools": "सीन व्यू टूल",
|
||||
"reference_image_tools": "संदर्भ छवि टूल",
|
||||
"asset_advanced_tools": "उन्नत एसेट टूल",
|
||||
"validation_tools": "सत्यापन टूल",
|
||||
"brand_name": "LiDaxian MCP Series",
|
||||
"brand_subtitle": "LiDaxian MCP Series",
|
||||
"contact_email": "ईमेल",
|
||||
"contact_wechat": "WeChat",
|
||||
"contact_github": "GitHub",
|
||||
"machine_id_label": "मशीन ID",
|
||||
"purchase_renew": "खरीदें / नवीनीकरण करें",
|
||||
"purchase_url": "https://www.vberai.com/game-engines/cocos",
|
||||
"auth_title": "साइन-इन विधि चुनें",
|
||||
"auth_subtitle": "अपना ईमेल और एक्टिवेशन कोड दर्ज करें",
|
||||
"auth_choose_subtitle": "जारी रखने के लिए साइन-इन विधि चुनें",
|
||||
"email_label": "ईमेल पता",
|
||||
"activation_code_label": "एक्टिवेशन कोड",
|
||||
"email_placeholder": "ईमेल दर्ज करें",
|
||||
"code_placeholder": "कोड दर्ज करें (XXXXX-XXXXX-XXXXX-XXXXX-XXXXX)",
|
||||
"activate_button": "सक्रिय करें",
|
||||
"deactivate_button": "निष्क्रिय करें",
|
||||
"activating": "सक्रिय किया जा रहा है...",
|
||||
"deactivating": "निष्क्रिय किया जा रहा है...",
|
||||
"license_status": "लाइसेंस स्थिति",
|
||||
"license_type": "लाइसेंस प्रकार",
|
||||
"expiry_date": "समाप्ति तिथि",
|
||||
"days_remaining": "शेष दिन",
|
||||
"days_unit": "दिन",
|
||||
"permanent": "स्थायी",
|
||||
"expiry_warning_title": "लाइसेंस जल्द समाप्त हो रहा है",
|
||||
"expiry_warning_message": "आपका लाइसेंस {0} दिनों में समाप्त हो जाएगा। सेवा में रुकावट से बचने के लिए कृपया नवीनीकरण करें।",
|
||||
"renew_now": "अभी नवीनीकरण करें",
|
||||
"i_understand": "समझ गया",
|
||||
"activation_success": "लाइसेंस सफलतापूर्वक सक्रिय हो गया!",
|
||||
"activation_failed": "सक्रियण विफल: {0}",
|
||||
"invalid_code": "एक्टिवेशन कोड का प्रारूप अमान्य है",
|
||||
"purchase_license": "लाइसेंस खरीदें",
|
||||
"renew_license": "लाइसेंस नवीनीकरण",
|
||||
"contact_support": "सहायता",
|
||||
"language_switch": "हिन्दी",
|
||||
"version_label": "संस्करण",
|
||||
"license_required": "सर्वर शुरू करने के लिए लाइसेंस आवश्यक है",
|
||||
"license_check_failed": "लाइसेंस जाँच विफल",
|
||||
|
||||
"status_stopped": "रुका हुआ",
|
||||
"status_running": "चल रहा है",
|
||||
"status_error": "कनेक्शन विफल",
|
||||
"config_tab": "त्वरित कॉन्फ़िग",
|
||||
"status_label": "स्थिति",
|
||||
"connections_count": "कनेक्शन",
|
||||
"transport_protocol": "ट्रांसपोर्ट प्रोटोकॉल",
|
||||
"transport_type": "ट्रांसपोर्ट प्रकार",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "पोर्ट {0} व्यस्त है — {1} पर बदल दिया गया",
|
||||
"net_title": "नेटवर्क / प्रॉक्सी",
|
||||
"net_direct": "सीधा कनेक्शन",
|
||||
"net_via": "{0} के माध्यम से ({1})",
|
||||
"net_source_manual": "मैनुअल",
|
||||
"net_source_env": "एनवायरनमेंट वेरिएबल",
|
||||
"net_source_system": "सिस्टम सेटिंग्स",
|
||||
"net_mode_auto": "स्वतः पहचान",
|
||||
"net_mode_manual": "मैनुअल",
|
||||
"net_mode_off": "प्रॉक्सी नहीं",
|
||||
"net_save": "सहेजें",
|
||||
"net_test": "कनेक्शन जाँचें",
|
||||
"net_testing": "जाँच हो रही है…",
|
||||
"net_test_ok": "कनेक्ट हो गया ({0} ms)",
|
||||
"net_test_fail": "विफल: {0}",
|
||||
"net_hint": "लॉगिन टाइमआउट आमतौर पर नेटवर्क की समस्या है। VPN/प्रॉक्सी उपयोग करते हों तो यहाँ पता डालें (जैसे http://127.0.0.1:7890) या क्लाइंट को ग्लोबल (TUN) मोड में बदलें।",
|
||||
"net_pac_warning": "सिस्टम PAC स्क्रिप्ट उपयोग कर रहा है, जिसे प्लगिन पढ़ नहीं सकता। «मैनुअल» चुनें और प्रॉक्सी पता डालें।",
|
||||
"port_label": "पोर्ट",
|
||||
"auto_start_label": "ऑटो स्टार्ट",
|
||||
"debug_log_label": "डीबग लॉग",
|
||||
"max_connections_label": "अधिकतम कनेक्शन",
|
||||
"server_settings_title": "सर्वर सेटिंग्स",
|
||||
"server_status_title": "सर्वर स्थिति",
|
||||
"connection_info_title": "कनेक्शन जानकारी",
|
||||
"tools_count": "{0} टूल",
|
||||
"tools_stats": "{0} टूल ({1} सक्षम / {2} अक्षम)",
|
||||
"quick_config_title": "MCP क्लाइंट त्वरित कॉन्फ़िगरेशन",
|
||||
"quick_config_desc": "वर्तमान MCP सर्वर को मुख्यधारा के AI एडिटर और CLI टूल में स्वतः कॉन्फ़िगर करें",
|
||||
"server_info": "सर्वर जानकारी",
|
||||
"server_name_label": "सर्वर नाम",
|
||||
"server_address": "सर्वर पता",
|
||||
"timeout_ms": "टाइमआउट (ms)",
|
||||
"auto_config_title": "स्वतः कॉन्फ़िग (IDE एडिटर + Codex CLI)",
|
||||
"add_all": "सभी जोड़ें",
|
||||
"remove_all": "सभी हटाएँ",
|
||||
"auto_config_note": "कॉन्फ़िग फ़ाइलें स्वतः संशोधित करने के लिए बटन दबाएँ (Cursor, Windsurf, Trae, Codex CLI)",
|
||||
"configured": "कॉन्फ़िगर किया गया",
|
||||
"not_configured": "कॉन्फ़िगर नहीं किया गया",
|
||||
"auto_config_btn": "स्वतः कॉन्फ़िग",
|
||||
"delete_btn": "हटाएँ",
|
||||
"copy_config": "कॉन्फ़िग कॉपी करें",
|
||||
"open_config": "कॉन्फ़िग खोलें",
|
||||
"manual_config_title": "मैनुअल कॉन्फ़िग (Claude CLI + Gemini CLI)",
|
||||
"refresh_commands": "कमांड रिफ़्रेश करें",
|
||||
"manual_config_note": "निम्नलिखित CLI टूल के लिए टर्मिनल में कमांड मैनुअल रूप से चलाना आवश्यक है",
|
||||
"config_scope": "कॉन्फ़िग स्कोप:",
|
||||
"scope_user": "यूज़र स्तर",
|
||||
"scope_project": "प्रोजेक्ट स्तर",
|
||||
"operation_log": "ऑपरेशन लॉग",
|
||||
"no_logs": "कोई ऑपरेशन रिकॉर्ड नहीं",
|
||||
"copyright_line": "© 2025 LiDaxian - Cocos Creator MCP Server v{0}",
|
||||
"protocol_line": "Streamable HTTP प्रोटोकॉल पर आधारित (MCP 2025-03-26)",
|
||||
"tools_copyright_line1": "टूल सिस्टम - 13 intent-level टूल, 150+ एडिटर ऑपरेशन, action कोड डिस्पैच",
|
||||
"config_copyright_line1": "त्वरित कॉन्फ़िग - Cursor, Windsurf, Trae, Codex CLI, Claude CLI, Gemini CLI समर्थित",
|
||||
"email_required": "कृपया ईमेल और एक्टिवेशन कोड दर्ज करें",
|
||||
"permanent_label": "स्थायी",
|
||||
"logout_button": "लॉग आउट",
|
||||
"invite_code_label": "आमंत्रण कोड",
|
||||
"copy_button": "कॉपी करें",
|
||||
"change_code_button": "कोड बदलें",
|
||||
"change_code_title": "एक्टिवेशन कोड बदलें",
|
||||
"change_code_desc": "अपना नया एक्टिवेशन कोड दर्ज करें। पहले वर्तमान डिवाइस बाइंडिंग हटाई जाएगी।",
|
||||
"confirm_change": "पुष्टि करें",
|
||||
"cancel": "रद्द करें",
|
||||
"code_required": "कृपया एक्टिवेशन कोड दर्ज करें",
|
||||
"expiry_inline_warning": "जल्द समाप्त हो रहा है!",
|
||||
"store_purchase_hint": "यदि आपने यह प्लगइन Cocos Store से खरीदा है, तो एक्टिवेशन कोड पाने के लिए कृपया मुझसे संपर्क करें।",
|
||||
"license_expired_title": "लाइसेंस समाप्त",
|
||||
"license_expired_message": "आपका लाइसेंस समाप्त हो चुका है और सर्वर शुरू नहीं किया जा सकता। जारी रखने के लिए कृपया नवीनीकरण करें या पुनः सक्रिय करें।",
|
||||
"go_to_login": "लॉगिन पर जाएँ",
|
||||
|
||||
"log_load_config_failed": "कॉन्फ़िग स्थिति लोड करने में विफल: {0}",
|
||||
"log_gen_cli_failed": "CLI कमांड बनाने में विफल: {0}",
|
||||
"log_add_client_failed": "{0} में जोड़ने में विफल: {1}",
|
||||
"log_remove_client_failed": "{0} से हटाने में विफल: {1}",
|
||||
"quick_config_dialog_title": "वन-क्लिक कॉन्फ़िगरेशन",
|
||||
"config_operation_failed": "कॉन्फ़िगरेशन ऑपरेशन विफल",
|
||||
"config_file_not_found_changed_path": "कॉन्फ़िगरेशन विफल: कॉन्फ़िग फ़ाइल नहीं मिली। संभव है कि {0} किसी कस्टम पथ पर इंस्टॉल हो।",
|
||||
"log_batch_add_failed": "बैच जोड़ना विफल: {0}",
|
||||
"log_batch_remove_failed": "बैच हटाना विफल: {0}",
|
||||
"log_copied_config": "{0} का कॉन्फ़िग क्लिपबोर्ड पर कॉपी किया गया",
|
||||
"log_gen_config_failed": "कॉन्फ़िग बनाने में विफल",
|
||||
"log_copy_config_failed": "कॉन्फ़िग कॉपी करने में विफल: {0}",
|
||||
"log_copied_command": "{0} की कमांड क्लिपबोर्ड पर कॉपी की गई",
|
||||
"log_copy_command_failed": "कमांड कॉपी करने में विफल: {0}",
|
||||
"log_opened_config_file": "कॉन्फ़िग फ़ाइल खोली गई: {0}",
|
||||
"log_open_config_failed": "कॉन्फ़िग फ़ाइल खोलने में विफल",
|
||||
"log_open_config_error": "कॉन्फ़िग फ़ाइल खोलने में विफल: {0}",
|
||||
|
||||
"update_available_title": "अपडेट उपलब्ध",
|
||||
"update_new_version": "नया संस्करण v{0} उपलब्ध है!",
|
||||
"update_now": "अभी अपडेट करें",
|
||||
"update_not_now": "अभी नहीं",
|
||||
"update_skip_version": "v{0} छोड़ें",
|
||||
"check_for_updates": "अपडेट जाँचें",
|
||||
"update_checking": "जाँच हो रही है...",
|
||||
"update_no_update": "आप नवीनतम संस्करण का उपयोग कर रहे हैं",
|
||||
"update_urgency_critical": "अत्यावश्यक अपडेट",
|
||||
"update_urgency_high": "महत्वपूर्ण अपडेट",
|
||||
"update_current_version": "वर्तमान संस्करण: v{0}",
|
||||
"update_changelog": "परिवर्तन सूची"
|
||||
,"update_changelog_empty": "इस संस्करण के लिए कोई रिलीज़ नोट नहीं दिया गया है।"
|
||||
,"subscription_login_title": "VberAI सब्सक्रिप्शन खाता"
|
||||
,"subscription_password_label": "पासवर्ड"
|
||||
,"subscription_password_placeholder": "पासवर्ड दर्ज करें"
|
||||
,"subscription_login_button": "VberAI से साइन इन करें"
|
||||
,"subscription_google_button": "Google से साइन इन करें"
|
||||
,"subscription_google_pending": "ब्राउज़र खुल गया है। Google प्राधिकरण पूरा करें, फिर Cocos Creator पर वापस आएँ।"
|
||||
,"subscription_google_pending_button": "Google प्राधिकरण की प्रतीक्षा है..."
|
||||
,"subscription_register_button": "VberAI खाता बनाएँ"
|
||||
,"subscription_logging_in": "साइन इन हो रहा है..."
|
||||
,"subscription_credentials_required": "कृपया ईमेल और पासवर्ड दर्ज करें"
|
||||
,"subscription_login_success": "सब्सक्रिप्शन खाते से साइन इन हो गया"
|
||||
,"subscription_login_failed": "सब्सक्रिप्शन साइन-इन विफल"
|
||||
,"subscription_not_entitled": "आपकी वर्तमान सब्सक्रिप्शन में Cocos 3.x MCP शामिल नहीं है। जारी रखने के लिए अपग्रेड करें।"
|
||||
,"subscription_upgrade_to": "इस उत्पाद का उपयोग करने के लिए {0} में अपग्रेड करें"
|
||||
,"subscription_upgrade_action": "सब्सक्रिप्शन अपग्रेड करें"
|
||||
,"subscription_logged_in": "साइन-इन खाता"
|
||||
,"subscription_choose_button": "VberAI सब्सक्रिप्शन खाता"
|
||||
,"license_choose_button": "एक्टिवेशन कोड"
|
||||
,"subscription_method_label": "सब्सक्रिप्शन"
|
||||
,"back_to_login_choices": "← दूसरी साइन-इन विधि चुनें"
|
||||
,"subscription_signed_out_other_device": "साइन आउट: आपका खाता किसी अन्य डिवाइस पर उपयोग किया गया।"
|
||||
,"subscription_google_cancelled": "Google साइन-इन रद्द कर दिया गया।"
|
||||
,"subscription_request_timeout": "अनुरोध का समय समाप्त हो गया। अपना नेटवर्क जाँचें और पुनः प्रयास करें।"
|
||||
,"subscription_network_error": "नेटवर्क कनेक्शन विफल। अपना नेटवर्क जाँचें और पुनः प्रयास करें।"
|
||||
,"subscription_server_error": "सर्वर ने अमान्य प्रतिक्रिया लौटाई। बाद में पुनः प्रयास करें।"
|
||||
,"subscription_google_authorization_failed": "Google प्राधिकरण पूरा नहीं हुआ। कृपया पुनः प्रयास करें।"
|
||||
,"lan_access_label": "LAN एक्सेस की अनुमति दें"
|
||||
,"lan_access_warning": "0.0.0.0 से बाइंड किया गया। इस नेटवर्क पर कोई भी व्यक्ति कनेक्ट करके एडिटर चला सकता है — यह एंडपॉइंट किसी भी क्रेडेंशियल की जाँच नहीं करता। केवल विश्वसनीय नेटवर्क पर ही सक्षम करें।"
|
||||
,"lan_address": "LAN पता"
|
||||
};
|
||||
@@ -0,0 +1,259 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extension_name": "Cocos MCP Server",
|
||||
"description": "Server AI MCP untuk Cocos Creator 3.8",
|
||||
"panel_title": "Server MCP",
|
||||
"open_panel": "Buka Panel MCP",
|
||||
"start_server": "Mulai Server",
|
||||
"stop_server": "Hentikan Server",
|
||||
"server_status": "Status Server",
|
||||
"port": "Port",
|
||||
"settings": "Pengaturan",
|
||||
"connected": "Terhubung",
|
||||
"disconnected": "Terputus",
|
||||
"server_running": "Server berjalan di port {0}",
|
||||
"server_stopped": "Server telah berhenti",
|
||||
"auto_start": "Mulai Otomatis",
|
||||
"debug_log": "Log Debug",
|
||||
"max_connections": "Koneksi Maksimum",
|
||||
"connection_info": "Info Koneksi",
|
||||
"http_url": "HTTP URL",
|
||||
"copy": "Salin",
|
||||
"save_settings": "Simpan Pengaturan",
|
||||
"settings_saved": "Pengaturan berhasil disimpan",
|
||||
"server_started": "Server MCP Dimulai",
|
||||
"server_stopped_msg": "Server MCP Dihentikan",
|
||||
"failed_to_start": "Gagal memulai server",
|
||||
"failed_to_stop": "Gagal menghentikan server",
|
||||
"failed_to_save": "Gagal menyimpan pengaturan",
|
||||
"url_copied": "HTTP URL disalin ke papan klip",
|
||||
"tool_manager": "Pengelola Alat",
|
||||
"open_tool_manager": "Buka Pengelola Alat",
|
||||
"create_config": "Buat Konfigurasi",
|
||||
"edit_config": "Edit Konfigurasi",
|
||||
"delete_config": "Hapus Konfigurasi",
|
||||
"import_config": "Impor Konfigurasi",
|
||||
"export_config": "Ekspor Konfigurasi",
|
||||
"apply_config": "Terapkan Konfigurasi",
|
||||
"select_all": "Pilih Semua",
|
||||
"deselect_all": "Batalkan Semua Pilihan",
|
||||
"save_changes": "Simpan Perubahan",
|
||||
"config_name": "Nama Konfigurasi",
|
||||
"config_description": "Deskripsi Konfigurasi",
|
||||
"current_config": "Konfigurasi Saat Ini",
|
||||
"tool_management": "Manajemen Alat",
|
||||
"total_tools": "Total Alat",
|
||||
"enabled_tools": "Aktif",
|
||||
"disabled_tools": "Nonaktif",
|
||||
"no_config_selected": "Tidak Ada Konfigurasi Dipilih",
|
||||
"select_config_first": "Silakan pilih konfigurasi atau buat yang baru terlebih dahulu",
|
||||
"config_created": "Konfigurasi berhasil dibuat",
|
||||
"config_updated": "Konfigurasi berhasil diperbarui",
|
||||
"config_deleted": "Konfigurasi berhasil dihapus",
|
||||
"config_applied": "Konfigurasi berhasil diterapkan",
|
||||
"config_exported": "Konfigurasi berhasil diekspor",
|
||||
"config_imported": "Konfigurasi berhasil diimpor",
|
||||
"confirm_delete": "Konfirmasi Penghapusan",
|
||||
"delete_config_confirm": "Anda yakin ingin menghapus konfigurasi \"{0}\"? Tindakan ini tidak dapat dibatalkan.",
|
||||
"max_config_slots_reached": "Slot konfigurasi maksimum tercapai ({0})",
|
||||
"invalid_config_format": "Format konfigurasi tidak valid",
|
||||
"invalid_json_format": "Format JSON atau struktur konfigurasi tidak valid",
|
||||
"server_tab": "Server",
|
||||
"tools_tab": "Manajemen Alat",
|
||||
"available_tools": "Alat Tersedia",
|
||||
"scene_tools": "Alat Scene",
|
||||
"node_tools": "Alat Node",
|
||||
"component_tools": "Alat Komponen",
|
||||
"prefab_tools": "Alat Prefab",
|
||||
"project_tools": "Alat Proyek",
|
||||
"debug_tools": "Alat Debug",
|
||||
"preferences_tools": "Preferensi",
|
||||
"server_tools": "Alat Server",
|
||||
"broadcast_tools": "Alat Siaran",
|
||||
"scene_advanced_tools": "Alat Scene Lanjutan",
|
||||
"scene_view_tools": "Alat Tampilan Scene",
|
||||
"reference_image_tools": "Alat Gambar Referensi",
|
||||
"asset_advanced_tools": "Alat Aset Lanjutan",
|
||||
"validation_tools": "Alat Validasi",
|
||||
"brand_name": "LiDaxian MCP Series",
|
||||
"brand_subtitle": "LiDaxian MCP Series",
|
||||
"contact_email": "Email",
|
||||
"contact_wechat": "WeChat",
|
||||
"contact_github": "GitHub",
|
||||
"machine_id_label": "ID Mesin",
|
||||
"purchase_renew": "Beli / Perpanjang",
|
||||
"purchase_url": "https://www.vberai.com/game-engines/cocos",
|
||||
"auth_title": "Pilih metode masuk",
|
||||
"auth_subtitle": "Masukkan email dan kode aktivasi Anda",
|
||||
"auth_choose_subtitle": "Pilih metode masuk untuk melanjutkan",
|
||||
"email_label": "Alamat Email",
|
||||
"activation_code_label": "Kode Aktivasi",
|
||||
"email_placeholder": "Masukkan email",
|
||||
"code_placeholder": "Masukkan kode (XXXXX-XXXXX-XXXXX-XXXXX-XXXXX)",
|
||||
"activate_button": "Aktifkan",
|
||||
"deactivate_button": "Nonaktifkan",
|
||||
"activating": "Mengaktifkan...",
|
||||
"deactivating": "Menonaktifkan...",
|
||||
"license_status": "Status Lisensi",
|
||||
"license_type": "Jenis Lisensi",
|
||||
"expiry_date": "Tanggal Kedaluwarsa",
|
||||
"days_remaining": "Sisa Hari",
|
||||
"days_unit": "hr",
|
||||
"permanent": "Permanen",
|
||||
"expiry_warning_title": "Lisensi Segera Kedaluwarsa",
|
||||
"expiry_warning_message": "Lisensi Anda akan kedaluwarsa dalam {0} hari. Silakan perpanjang agar layanan tidak terputus.",
|
||||
"renew_now": "Perpanjang Sekarang",
|
||||
"i_understand": "Saya Mengerti",
|
||||
"activation_success": "Lisensi berhasil diaktifkan!",
|
||||
"activation_failed": "Aktivasi gagal: {0}",
|
||||
"invalid_code": "Format kode aktivasi tidak valid",
|
||||
"purchase_license": "Beli Lisensi",
|
||||
"renew_license": "Perpanjang Lisensi",
|
||||
"contact_support": "Dukungan",
|
||||
"language_switch": "Bahasa Indonesia",
|
||||
"version_label": "Versi",
|
||||
"license_required": "Lisensi diperlukan untuk memulai server",
|
||||
"license_check_failed": "Pemeriksaan lisensi gagal",
|
||||
|
||||
"status_stopped": "Berhenti",
|
||||
"status_running": "Berjalan",
|
||||
"status_error": "Koneksi Gagal",
|
||||
"config_tab": "Konfigurasi Cepat",
|
||||
"status_label": "Status",
|
||||
"connections_count": "Koneksi",
|
||||
"transport_protocol": "Protokol Transport",
|
||||
"transport_type": "Jenis Transport",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "Port {0} sedang dipakai — beralih ke {1}",
|
||||
"net_title": "Jaringan / Proxy",
|
||||
"net_direct": "Koneksi langsung",
|
||||
"net_via": "via {0} ({1})",
|
||||
"net_source_manual": "manual",
|
||||
"net_source_env": "variabel lingkungan",
|
||||
"net_source_system": "pengaturan sistem",
|
||||
"net_mode_auto": "Deteksi otomatis",
|
||||
"net_mode_manual": "Manual",
|
||||
"net_mode_off": "Tanpa proxy",
|
||||
"net_save": "Simpan",
|
||||
"net_test": "Uji koneksi",
|
||||
"net_testing": "Menguji…",
|
||||
"net_test_ok": "Terhubung ({0} ms)",
|
||||
"net_test_fail": "Gagal: {0}",
|
||||
"net_hint": "Timeout login biasanya masalah jaringan. Jika memakai VPN/proxy, masukkan alamatnya (mis. http://127.0.0.1:7890) atau alihkan klien ke mode global (TUN).",
|
||||
"net_pac_warning": "Sistem memakai skrip PAC yang tidak bisa dibaca plugin. Pilih Manual dan masukkan alamat proxy.",
|
||||
"port_label": "Port",
|
||||
"auto_start_label": "Mulai Otomatis",
|
||||
"debug_log_label": "Log Debug",
|
||||
"max_connections_label": "Koneksi Maksimum",
|
||||
"server_settings_title": "Pengaturan Server",
|
||||
"server_status_title": "Status Server",
|
||||
"connection_info_title": "Info Koneksi",
|
||||
"tools_count": "{0} alat",
|
||||
"tools_stats": "{0} alat ({1} aktif / {2} nonaktif)",
|
||||
"quick_config_title": "Konfigurasi Cepat Klien MCP",
|
||||
"quick_config_desc": "Konfigurasikan server MCP saat ini secara otomatis ke editor AI dan alat CLI populer",
|
||||
"server_info": "Info Server",
|
||||
"server_name_label": "Nama Server",
|
||||
"server_address": "Alamat Server",
|
||||
"timeout_ms": "Batas Waktu (md)",
|
||||
"auto_config_title": "Konfigurasi Otomatis (Editor IDE + Codex CLI)",
|
||||
"add_all": "Tambah Semua",
|
||||
"remove_all": "Hapus Semua",
|
||||
"auto_config_note": "Klik tombol untuk mengubah file konfigurasi secara otomatis (Cursor, Windsurf, Trae, Codex CLI)",
|
||||
"configured": "Terkonfigurasi",
|
||||
"not_configured": "Belum Dikonfigurasi",
|
||||
"auto_config_btn": "Konfigurasi Otomatis",
|
||||
"delete_btn": "Hapus",
|
||||
"copy_config": "Salin Konfigurasi",
|
||||
"open_config": "Buka Konfigurasi",
|
||||
"manual_config_title": "Konfigurasi Manual (Claude CLI + Gemini CLI)",
|
||||
"refresh_commands": "Segarkan Perintah",
|
||||
"manual_config_note": "Alat CLI berikut perlu menjalankan perintah terminal secara manual",
|
||||
"config_scope": "Cakupan Konfigurasi:",
|
||||
"scope_user": "Tingkat Pengguna",
|
||||
"scope_project": "Tingkat Proyek",
|
||||
"operation_log": "Log Operasi",
|
||||
"no_logs": "Tidak ada catatan operasi",
|
||||
"copyright_line": "© 2025 LiDaxian - Cocos Creator MCP Server v{0}",
|
||||
"protocol_line": "Berbasis Protokol Streamable HTTP (MCP 2025-03-26)",
|
||||
"tools_copyright_line1": "Sistem Alat - 13 alat tingkat intent, 150+ operasi editor, dispatch berbasis kode action",
|
||||
"config_copyright_line1": "Konfigurasi Cepat - Mendukung Cursor, Windsurf, Trae, Codex CLI, Claude CLI, Gemini CLI",
|
||||
"email_required": "Silakan masukkan email dan kode aktivasi",
|
||||
"permanent_label": "Permanen",
|
||||
"logout_button": "Keluar",
|
||||
"invite_code_label": "Kode Undangan",
|
||||
"copy_button": "Salin",
|
||||
"change_code_button": "Ganti Kode",
|
||||
"change_code_title": "Ganti Kode Aktivasi",
|
||||
"change_code_desc": "Masukkan kode aktivasi baru Anda. Ikatan perangkat saat ini akan dilepas terlebih dahulu.",
|
||||
"confirm_change": "Konfirmasi",
|
||||
"cancel": "Batal",
|
||||
"code_required": "Silakan masukkan kode aktivasi",
|
||||
"expiry_inline_warning": "Segera kedaluwarsa!",
|
||||
"store_purchase_hint": "Jika Anda membeli plugin ini dari Cocos Store, silakan hubungi saya untuk mendapatkan kode aktivasi.",
|
||||
"license_expired_title": "Lisensi Kedaluwarsa",
|
||||
"license_expired_message": "Lisensi Anda telah kedaluwarsa dan server tidak dapat dimulai. Silakan perpanjang atau aktifkan ulang untuk melanjutkan.",
|
||||
"go_to_login": "Ke Halaman Masuk",
|
||||
|
||||
"log_load_config_failed": "Gagal memuat status konfigurasi: {0}",
|
||||
"log_gen_cli_failed": "Gagal membuat perintah CLI: {0}",
|
||||
"log_add_client_failed": "Gagal menambahkan ke {0}: {1}",
|
||||
"log_remove_client_failed": "Gagal menghapus dari {0}: {1}",
|
||||
"quick_config_dialog_title": "Konfigurasi Sekali Klik",
|
||||
"config_operation_failed": "Operasi konfigurasi gagal",
|
||||
"config_file_not_found_changed_path": "Konfigurasi gagal: file konfigurasi tidak ditemukan. {0} mungkin terpasang di path kustom.",
|
||||
"log_batch_add_failed": "Penambahan massal gagal: {0}",
|
||||
"log_batch_remove_failed": "Penghapusan massal gagal: {0}",
|
||||
"log_copied_config": "Konfigurasi {0} disalin ke papan klip",
|
||||
"log_gen_config_failed": "Gagal membuat konfigurasi",
|
||||
"log_copy_config_failed": "Gagal menyalin konfigurasi: {0}",
|
||||
"log_copied_command": "Perintah {0} disalin ke papan klip",
|
||||
"log_copy_command_failed": "Gagal menyalin perintah: {0}",
|
||||
"log_opened_config_file": "File konfigurasi dibuka: {0}",
|
||||
"log_open_config_failed": "Gagal membuka file konfigurasi",
|
||||
"log_open_config_error": "Gagal membuka file konfigurasi: {0}",
|
||||
|
||||
"update_available_title": "Pembaruan Tersedia",
|
||||
"update_new_version": "Versi baru v{0} sudah tersedia!",
|
||||
"update_now": "Perbarui Sekarang",
|
||||
"update_not_now": "Nanti Saja",
|
||||
"update_skip_version": "Lewati v{0}",
|
||||
"check_for_updates": "Periksa Pembaruan",
|
||||
"update_checking": "Memeriksa...",
|
||||
"update_no_update": "Anda sudah menggunakan versi terbaru",
|
||||
"update_urgency_critical": "Pembaruan Kritis",
|
||||
"update_urgency_high": "Pembaruan Penting",
|
||||
"update_current_version": "Versi saat ini: v{0}",
|
||||
"update_changelog": "Catatan Perubahan"
|
||||
,"update_changelog_empty": "Tidak ada catatan rilis untuk versi ini."
|
||||
,"subscription_login_title": "Akun langganan VberAI"
|
||||
,"subscription_password_label": "Kata Sandi"
|
||||
,"subscription_password_placeholder": "Masukkan kata sandi"
|
||||
,"subscription_login_button": "Masuk dengan VberAI"
|
||||
,"subscription_google_button": "Masuk dengan Google"
|
||||
,"subscription_google_pending": "Browser telah dibuka. Selesaikan otorisasi Google, lalu kembali ke Cocos Creator."
|
||||
,"subscription_google_pending_button": "Menunggu otorisasi Google..."
|
||||
,"subscription_register_button": "Buat akun VberAI"
|
||||
,"subscription_logging_in": "Sedang masuk..."
|
||||
,"subscription_credentials_required": "Silakan masukkan email dan kata sandi"
|
||||
,"subscription_login_success": "Akun langganan berhasil masuk"
|
||||
,"subscription_login_failed": "Gagal masuk dengan akun langganan"
|
||||
,"subscription_not_entitled": "Langganan Anda saat ini tidak mencakup Cocos 3.x MCP. Tingkatkan untuk melanjutkan."
|
||||
,"subscription_upgrade_to": "Tingkatkan ke {0} untuk menggunakan produk ini"
|
||||
,"subscription_upgrade_action": "Tingkatkan langganan"
|
||||
,"subscription_logged_in": "Akun yang masuk"
|
||||
,"subscription_choose_button": "Akun langganan VberAI"
|
||||
,"license_choose_button": "Kode aktivasi"
|
||||
,"subscription_method_label": "Langganan"
|
||||
,"back_to_login_choices": "← Pilih metode masuk lain"
|
||||
,"subscription_signed_out_other_device": "Keluar: akun Anda digunakan di perangkat lain."
|
||||
,"subscription_google_cancelled": "Masuk dengan Google dibatalkan."
|
||||
,"subscription_request_timeout": "Permintaan kehabisan waktu. Periksa jaringan Anda dan coba lagi."
|
||||
,"subscription_network_error": "Koneksi jaringan gagal. Periksa jaringan Anda dan coba lagi."
|
||||
,"subscription_server_error": "Server mengembalikan respons tidak valid. Coba lagi nanti."
|
||||
,"subscription_google_authorization_failed": "Otorisasi Google tidak selesai. Silakan coba lagi."
|
||||
,"lan_access_label": "Izinkan akses LAN"
|
||||
,"lan_access_warning": "Terikat ke 0.0.0.0. Siapa pun di jaringan ini dapat terhubung dan mengendalikan editor — endpoint ini tidak memverifikasi kredensial apa pun. Aktifkan hanya di jaringan tepercaya."
|
||||
,"lan_address": "Alamat LAN"
|
||||
};
|
||||
@@ -0,0 +1,259 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extension_name": "Cocos MCP Server",
|
||||
"description": "Server AI MCP per Cocos Creator 3.8",
|
||||
"panel_title": "Server MCP",
|
||||
"open_panel": "Apri pannello MCP",
|
||||
"start_server": "Avvia server",
|
||||
"stop_server": "Arresta server",
|
||||
"server_status": "Stato del server",
|
||||
"port": "Porta",
|
||||
"settings": "Impostazioni",
|
||||
"connected": "Connesso",
|
||||
"disconnected": "Disconnesso",
|
||||
"server_running": "Il server è in esecuzione sulla porta {0}",
|
||||
"server_stopped": "Il server è stato arrestato",
|
||||
"auto_start": "Avvio automatico",
|
||||
"debug_log": "Log di debug",
|
||||
"max_connections": "Connessioni massime",
|
||||
"connection_info": "Info connessione",
|
||||
"http_url": "URL HTTP",
|
||||
"copy": "Copia",
|
||||
"save_settings": "Salva impostazioni",
|
||||
"settings_saved": "Impostazioni salvate correttamente",
|
||||
"server_started": "Server MCP avviato",
|
||||
"server_stopped_msg": "Server MCP arrestato",
|
||||
"failed_to_start": "Impossibile avviare il server",
|
||||
"failed_to_stop": "Impossibile arrestare il server",
|
||||
"failed_to_save": "Impossibile salvare le impostazioni",
|
||||
"url_copied": "URL HTTP copiato negli appunti",
|
||||
"tool_manager": "Gestore strumenti",
|
||||
"open_tool_manager": "Apri gestore strumenti",
|
||||
"create_config": "Crea configurazione",
|
||||
"edit_config": "Modifica configurazione",
|
||||
"delete_config": "Elimina configurazione",
|
||||
"import_config": "Importa configurazione",
|
||||
"export_config": "Esporta configurazione",
|
||||
"apply_config": "Applica configurazione",
|
||||
"select_all": "Seleziona tutto",
|
||||
"deselect_all": "Deseleziona tutto",
|
||||
"save_changes": "Salva modifiche",
|
||||
"config_name": "Nome configurazione",
|
||||
"config_description": "Descrizione configurazione",
|
||||
"current_config": "Configurazione corrente",
|
||||
"tool_management": "Gestione strumenti",
|
||||
"total_tools": "Strumenti totali",
|
||||
"enabled_tools": "Abilitati",
|
||||
"disabled_tools": "Disabilitati",
|
||||
"no_config_selected": "Nessuna configurazione selezionata",
|
||||
"select_config_first": "Seleziona prima una configurazione oppure creane una nuova",
|
||||
"config_created": "Configurazione creata correttamente",
|
||||
"config_updated": "Configurazione aggiornata correttamente",
|
||||
"config_deleted": "Configurazione eliminata correttamente",
|
||||
"config_applied": "Configurazione applicata correttamente",
|
||||
"config_exported": "Configurazione esportata correttamente",
|
||||
"config_imported": "Configurazione importata correttamente",
|
||||
"confirm_delete": "Conferma eliminazione",
|
||||
"delete_config_confirm": "Vuoi davvero eliminare la configurazione \"{0}\"? L'operazione non può essere annullata.",
|
||||
"max_config_slots_reached": "Raggiunto il numero massimo di slot di configurazione ({0})",
|
||||
"invalid_config_format": "Formato di configurazione non valido",
|
||||
"invalid_json_format": "Formato JSON o struttura di configurazione non validi",
|
||||
"server_tab": "Server",
|
||||
"tools_tab": "Gestione strumenti",
|
||||
"available_tools": "Strumenti disponibili",
|
||||
"scene_tools": "Strumenti scena",
|
||||
"node_tools": "Strumenti nodo",
|
||||
"component_tools": "Strumenti componente",
|
||||
"prefab_tools": "Strumenti prefab",
|
||||
"project_tools": "Strumenti progetto",
|
||||
"debug_tools": "Strumenti di debug",
|
||||
"preferences_tools": "Preferenze",
|
||||
"server_tools": "Strumenti server",
|
||||
"broadcast_tools": "Strumenti broadcast",
|
||||
"scene_advanced_tools": "Strumenti scena avanzati",
|
||||
"scene_view_tools": "Strumenti vista scena",
|
||||
"reference_image_tools": "Strumenti immagine di riferimento",
|
||||
"asset_advanced_tools": "Strumenti asset avanzati",
|
||||
"validation_tools": "Strumenti di validazione",
|
||||
"brand_name": "LiDaxian MCP Series",
|
||||
"brand_subtitle": "LiDaxian MCP Series",
|
||||
"contact_email": "Email",
|
||||
"contact_wechat": "WeChat",
|
||||
"contact_github": "GitHub",
|
||||
"machine_id_label": "ID macchina",
|
||||
"purchase_renew": "Acquista / Rinnova",
|
||||
"purchase_url": "https://www.vberai.com/game-engines/cocos",
|
||||
"auth_title": "Scegli un metodo di accesso",
|
||||
"auth_subtitle": "Inserisci la tua email e il codice di attivazione",
|
||||
"auth_choose_subtitle": "Scegli un metodo di accesso per continuare",
|
||||
"email_label": "Indirizzo email",
|
||||
"activation_code_label": "Codice di attivazione",
|
||||
"email_placeholder": "Inserisci l'email",
|
||||
"code_placeholder": "Inserisci il codice (XXXXX-XXXXX-XXXXX-XXXXX-XXXXX)",
|
||||
"activate_button": "Attiva",
|
||||
"deactivate_button": "Disattiva",
|
||||
"activating": "Attivazione...",
|
||||
"deactivating": "Disattivazione...",
|
||||
"license_status": "Stato licenza",
|
||||
"license_type": "Tipo di licenza",
|
||||
"expiry_date": "Data di scadenza",
|
||||
"days_remaining": "Giorni rimanenti",
|
||||
"days_unit": "gg",
|
||||
"permanent": "Permanente",
|
||||
"expiry_warning_title": "Licenza in scadenza",
|
||||
"expiry_warning_message": "La tua licenza scadrà tra {0} giorni. Rinnovala per evitare interruzioni del servizio.",
|
||||
"renew_now": "Rinnova ora",
|
||||
"i_understand": "Ho capito",
|
||||
"activation_success": "Licenza attivata correttamente!",
|
||||
"activation_failed": "Attivazione non riuscita: {0}",
|
||||
"invalid_code": "Formato del codice di attivazione non valido",
|
||||
"purchase_license": "Acquista licenza",
|
||||
"renew_license": "Rinnova licenza",
|
||||
"contact_support": "Assistenza",
|
||||
"language_switch": "Italiano",
|
||||
"version_label": "Versione",
|
||||
"license_required": "È necessaria una licenza per avviare il server",
|
||||
"license_check_failed": "Controllo della licenza non riuscito",
|
||||
|
||||
"status_stopped": "Arrestato",
|
||||
"status_running": "In esecuzione",
|
||||
"status_error": "Connessione non riuscita",
|
||||
"config_tab": "Configurazione rapida",
|
||||
"status_label": "Stato",
|
||||
"connections_count": "Connessioni",
|
||||
"transport_protocol": "Protocollo di trasporto",
|
||||
"transport_type": "Tipo di trasporto",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "Porta {0} occupata — passata a {1}",
|
||||
"net_title": "Rete / Proxy",
|
||||
"net_direct": "Connessione diretta",
|
||||
"net_via": "tramite {0} ({1})",
|
||||
"net_source_manual": "manuale",
|
||||
"net_source_env": "variabile d’ambiente",
|
||||
"net_source_system": "impostazioni di sistema",
|
||||
"net_mode_auto": "Rilevamento automatico",
|
||||
"net_mode_manual": "Manuale",
|
||||
"net_mode_off": "Nessun proxy",
|
||||
"net_save": "Salva",
|
||||
"net_test": "Prova connessione",
|
||||
"net_testing": "Test in corso…",
|
||||
"net_test_ok": "Connesso ({0} ms)",
|
||||
"net_test_fail": "Non riuscito: {0}",
|
||||
"net_hint": "I timeout di accesso sono quasi sempre di rete. Se usi VPN/proxy, inserisci qui l’indirizzo (es. http://127.0.0.1:7890) o imposta il client in modalità globale (TUN).",
|
||||
"net_pac_warning": "Il sistema usa uno script PAC che questo plugin non può valutare. Passa a Manuale e inserisci l’indirizzo del proxy.",
|
||||
"port_label": "Porta",
|
||||
"auto_start_label": "Avvio automatico",
|
||||
"debug_log_label": "Log di debug",
|
||||
"max_connections_label": "Connessioni massime",
|
||||
"server_settings_title": "Impostazioni del server",
|
||||
"server_status_title": "Stato del server",
|
||||
"connection_info_title": "Info connessione",
|
||||
"tools_count": "{0} strumenti",
|
||||
"tools_stats": "{0} strumenti ({1} abilitati / {2} disabilitati)",
|
||||
"quick_config_title": "Configurazione rapida dei client MCP",
|
||||
"quick_config_desc": "Configura automaticamente il server MCP corrente nei principali editor AI e strumenti CLI",
|
||||
"server_info": "Info server",
|
||||
"server_name_label": "Nome del server",
|
||||
"server_address": "Indirizzo del server",
|
||||
"timeout_ms": "Timeout (ms)",
|
||||
"auto_config_title": "Configurazione automatica (editor IDE + Codex CLI)",
|
||||
"add_all": "Aggiungi tutti",
|
||||
"remove_all": "Rimuovi tutti",
|
||||
"auto_config_note": "Fai clic sul pulsante per modificare automaticamente i file di configurazione (Cursor, Windsurf, Trae, Codex CLI)",
|
||||
"configured": "Configurato",
|
||||
"not_configured": "Non configurato",
|
||||
"auto_config_btn": "Configurazione automatica",
|
||||
"delete_btn": "Elimina",
|
||||
"copy_config": "Copia configurazione",
|
||||
"open_config": "Apri configurazione",
|
||||
"manual_config_title": "Configurazione manuale (Claude CLI + Gemini CLI)",
|
||||
"refresh_commands": "Aggiorna comandi",
|
||||
"manual_config_note": "I seguenti strumenti CLI richiedono l'esecuzione manuale dei comandi da terminale",
|
||||
"config_scope": "Ambito configurazione:",
|
||||
"scope_user": "Livello utente",
|
||||
"scope_project": "Livello progetto",
|
||||
"operation_log": "Log operazioni",
|
||||
"no_logs": "Nessuna operazione registrata",
|
||||
"copyright_line": "© 2025 LiDaxian - Cocos Creator MCP Server v{0}",
|
||||
"protocol_line": "Basato sul protocollo Streamable HTTP (MCP 2025-03-26)",
|
||||
"tools_copyright_line1": "Sistema di strumenti - 13 strumenti a livello di intento, oltre 150 operazioni dell'editor, dispatch tramite codice action",
|
||||
"config_copyright_line1": "Configurazione rapida - Supporta Cursor, Windsurf, Trae, Codex CLI, Claude CLI, Gemini CLI",
|
||||
"email_required": "Inserisci email e codice di attivazione",
|
||||
"permanent_label": "Permanente",
|
||||
"logout_button": "Esci",
|
||||
"invite_code_label": "Codice invito",
|
||||
"copy_button": "Copia",
|
||||
"change_code_button": "Cambia codice",
|
||||
"change_code_title": "Cambia codice di attivazione",
|
||||
"change_code_desc": "Inserisci il nuovo codice di attivazione. L'associazione del dispositivo corrente verrà prima rilasciata.",
|
||||
"confirm_change": "Conferma",
|
||||
"cancel": "Annulla",
|
||||
"code_required": "Inserisci un codice di attivazione",
|
||||
"expiry_inline_warning": "In scadenza!",
|
||||
"store_purchase_hint": "Se hai acquistato questo plugin dal Cocos Store, contattami per ricevere il tuo codice di attivazione.",
|
||||
"license_expired_title": "Licenza scaduta",
|
||||
"license_expired_message": "La tua licenza è scaduta e il server non può essere avviato. Rinnova o riattiva per continuare.",
|
||||
"go_to_login": "Vai all'accesso",
|
||||
|
||||
"log_load_config_failed": "Impossibile caricare lo stato della configurazione: {0}",
|
||||
"log_gen_cli_failed": "Impossibile generare i comandi CLI: {0}",
|
||||
"log_add_client_failed": "Impossibile aggiungere a {0}: {1}",
|
||||
"log_remove_client_failed": "Impossibile rimuovere da {0}: {1}",
|
||||
"quick_config_dialog_title": "Configurazione con un clic",
|
||||
"config_operation_failed": "Operazione di configurazione non riuscita",
|
||||
"config_file_not_found_changed_path": "Configurazione non riuscita: file di configurazione non trovato. {0} potrebbe essere installato in un percorso personalizzato.",
|
||||
"log_batch_add_failed": "Aggiunta in blocco non riuscita: {0}",
|
||||
"log_batch_remove_failed": "Rimozione in blocco non riuscita: {0}",
|
||||
"log_copied_config": "Configurazione di {0} copiata negli appunti",
|
||||
"log_gen_config_failed": "Impossibile generare la configurazione",
|
||||
"log_copy_config_failed": "Impossibile copiare la configurazione: {0}",
|
||||
"log_copied_command": "Comando di {0} copiato negli appunti",
|
||||
"log_copy_command_failed": "Impossibile copiare il comando: {0}",
|
||||
"log_opened_config_file": "File di configurazione aperto: {0}",
|
||||
"log_open_config_failed": "Impossibile aprire il file di configurazione",
|
||||
"log_open_config_error": "Impossibile aprire il file di configurazione: {0}",
|
||||
|
||||
"update_available_title": "Aggiornamento disponibile",
|
||||
"update_new_version": "La nuova versione v{0} è disponibile!",
|
||||
"update_now": "Aggiorna ora",
|
||||
"update_not_now": "Non ora",
|
||||
"update_skip_version": "Salta v{0}",
|
||||
"check_for_updates": "Controlla aggiornamenti",
|
||||
"update_checking": "Controllo in corso...",
|
||||
"update_no_update": "Stai usando la versione più recente",
|
||||
"update_urgency_critical": "Aggiornamento critico",
|
||||
"update_urgency_high": "Aggiornamento importante",
|
||||
"update_current_version": "Versione corrente: v{0}",
|
||||
"update_changelog": "Novità"
|
||||
,"update_changelog_empty": "Per questa versione non sono state fornite note di rilascio."
|
||||
,"subscription_login_title": "Account in abbonamento VberAI"
|
||||
,"subscription_password_label": "Password"
|
||||
,"subscription_password_placeholder": "Inserisci la password"
|
||||
,"subscription_login_button": "Accedi con VberAI"
|
||||
,"subscription_google_button": "Accedi con Google"
|
||||
,"subscription_google_pending": "Il browser è stato aperto. Completa l'autorizzazione Google, poi torna a Cocos Creator."
|
||||
,"subscription_google_pending_button": "In attesa dell'autorizzazione Google..."
|
||||
,"subscription_register_button": "Crea un account VberAI"
|
||||
,"subscription_logging_in": "Accesso in corso..."
|
||||
,"subscription_credentials_required": "Inserisci email e password"
|
||||
,"subscription_login_success": "Accesso con account in abbonamento effettuato"
|
||||
,"subscription_login_failed": "Accesso con account in abbonamento non riuscito"
|
||||
,"subscription_not_entitled": "Il tuo abbonamento attuale non include Cocos 3.x MCP. Esegui l'upgrade per continuare."
|
||||
,"subscription_upgrade_to": "Passa a {0} per usare questo prodotto"
|
||||
,"subscription_upgrade_action": "Aggiorna abbonamento"
|
||||
,"subscription_logged_in": "Account connesso"
|
||||
,"subscription_choose_button": "Account in abbonamento VberAI"
|
||||
,"license_choose_button": "Codice di attivazione"
|
||||
,"subscription_method_label": "Abbonamento"
|
||||
,"back_to_login_choices": "← Scegli un altro metodo di accesso"
|
||||
,"subscription_signed_out_other_device": "Disconnesso: il tuo account è stato usato su un altro dispositivo."
|
||||
,"subscription_google_cancelled": "Accesso con Google annullato."
|
||||
,"subscription_request_timeout": "Richiesta scaduta. Controlla la connessione e riprova."
|
||||
,"subscription_network_error": "Connessione di rete non riuscita. Controlla la rete e riprova."
|
||||
,"subscription_server_error": "Il server ha restituito una risposta non valida. Riprova più tardi."
|
||||
,"subscription_google_authorization_failed": "L'autorizzazione Google non è stata completata. Riprova."
|
||||
,"lan_access_label": "Consenti accesso dalla rete locale"
|
||||
,"lan_access_warning": "Associato a 0.0.0.0. Chiunque su questa rete può connettersi e comandare l'editor: questo endpoint non verifica alcuna credenziale. Attivalo solo su una rete affidabile."
|
||||
,"lan_address": "Indirizzo rete locale"
|
||||
};
|
||||
@@ -123,11 +123,23 @@ module.exports = {
|
||||
"transport_protocol": "トランスポートプロトコル",
|
||||
"transport_type": "トランスポートタイプ",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "ポート {0} が使用できないため、{1} に切り替えました",
|
||||
"port_fallback_reason": "ポート {0} はシステムに予約されているか、ファイアウォール/ウイルス対策ソフトにブロックされています(Hyper-V、WSL2、Docker を導入した Windows でよく起こります)。サーバーは新しいポートで正常に動作しています。",
|
||||
"port_fallback_resynced": "次のクライアントの設定を更新しました:{0}。新しいポートを有効にするには再起動してください。",
|
||||
"port_fallback_no_clients": "AI クライアントを設定済みの場合は、「ワンクリック設定」で新しいポートに設定し直してください。",
|
||||
"port_fallback_dismiss": "了解",
|
||||
"port_fallback_title": "ポート {0} は使用中のため {1} に変更しました",
|
||||
"net_title": "ネットワーク / プロキシ",
|
||||
"net_direct": "直接接続",
|
||||
"net_via": "{0} 経由({1})",
|
||||
"net_source_manual": "手動設定",
|
||||
"net_source_env": "環境変数",
|
||||
"net_source_system": "システム設定",
|
||||
"net_mode_auto": "自動検出",
|
||||
"net_mode_manual": "手動指定",
|
||||
"net_mode_off": "プロキシを使わない",
|
||||
"net_save": "保存",
|
||||
"net_test": "接続テスト",
|
||||
"net_testing": "テスト中…",
|
||||
"net_test_ok": "接続できました({0} ms)",
|
||||
"net_test_fail": "接続失敗:{0}",
|
||||
"net_hint": "ログインのタイムアウトはほぼネットワーク起因です。VPN/プロキシ利用時はここにアドレス(例: http://127.0.0.1:7890)を入力するか、クライアントをグローバル(TUN)モードに切り替えてください。",
|
||||
"net_pac_warning": "システムが PAC 自動設定スクリプトを使用しており、本プラグインでは解釈できません。「手動指定」にしてアドレスを入力してください。",
|
||||
"port_label": "ポート",
|
||||
"auto_start_label": "自動起動",
|
||||
"debug_log_label": "デバッグログ",
|
||||
@@ -240,4 +252,7 @@ module.exports = {
|
||||
"subscription_network_error": "ネットワーク接続に失敗しました。ネットワークを確認して再試行してください。",
|
||||
"subscription_server_error": "サーバーから無効な応答が返されました。後でもう一度お試しください。",
|
||||
"subscription_google_authorization_failed": "Google 認証が完了しませんでした。もう一度お試しください。"
|
||||
,"lan_access_label": "LAN アクセスを許可"
|
||||
,"lan_access_warning": "0.0.0.0 にバインドしています。同じネットワーク上の誰でも接続してエディターを操作できます。このエンドポイントは資格情報を一切検証しません。信頼できるネットワークでのみ有効にしてください。"
|
||||
,"lan_address": "LAN アドレス"
|
||||
};
|
||||
|
||||
@@ -123,11 +123,23 @@ module.exports = {
|
||||
"transport_protocol": "전송 프로토콜",
|
||||
"transport_type": "전송 유형",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "포트 {0}을(를) 사용할 수 없어 {1}(으)로 전환했습니다",
|
||||
"port_fallback_reason": "포트 {0}이(가) 시스템에 예약되어 있거나 방화벽/백신에 의해 차단되었습니다(Hyper-V, WSL2 또는 Docker가 설치된 Windows에서 흔합니다). 서버는 새 포트에서 정상 실행 중입니다.",
|
||||
"port_fallback_resynced": "다음 클라이언트의 설정을 업데이트했습니다: {0}. 새 포트를 적용하려면 다시 시작하세요.",
|
||||
"port_fallback_no_clients": "AI 클라이언트를 설정한 적이 있다면 \"원클릭 설정\"에서 새 포트로 다시 설정하세요.",
|
||||
"port_fallback_dismiss": "확인",
|
||||
"port_fallback_title": "포트 {0}이(가) 사용 중이어서 {1}(으)로 변경했습니다",
|
||||
"net_title": "네트워크 / 프록시",
|
||||
"net_direct": "직접 연결",
|
||||
"net_via": "{0} 경유({1})",
|
||||
"net_source_manual": "직접 입력",
|
||||
"net_source_env": "환경 변수",
|
||||
"net_source_system": "시스템 설정",
|
||||
"net_mode_auto": "자동 감지",
|
||||
"net_mode_manual": "직접 지정",
|
||||
"net_mode_off": "프록시 사용 안 함",
|
||||
"net_save": "저장",
|
||||
"net_test": "연결 테스트",
|
||||
"net_testing": "테스트 중…",
|
||||
"net_test_ok": "연결 성공({0} ms)",
|
||||
"net_test_fail": "연결 실패: {0}",
|
||||
"net_hint": "로그인 시간 초과는 대개 네트워크 문제입니다. VPN/프록시를 쓰면 주소(예: http://127.0.0.1:7890)를 입력하거나 클라이언트를 전역(TUN) 모드로 바꾸세요.",
|
||||
"net_pac_warning": "시스템이 PAC 자동 구성 스크립트를 사용하며 플러그인이 해석할 수 없습니다. 「직접 지정」으로 바꿔 주소를 입력하세요.",
|
||||
"port_label": "포트",
|
||||
"auto_start_label": "자동 시작",
|
||||
"debug_log_label": "디버그 로그",
|
||||
@@ -240,4 +252,7 @@ module.exports = {
|
||||
"subscription_network_error": "네트워크 연결에 실패했습니다. 네트워크를 확인하고 다시 시도하세요.",
|
||||
"subscription_server_error": "서버가 잘못된 응답을 반환했습니다. 나중에 다시 시도하세요.",
|
||||
"subscription_google_authorization_failed": "Google 인증이 완료되지 않았습니다. 다시 시도하세요."
|
||||
,"lan_access_label": "LAN 접근 허용"
|
||||
,"lan_access_warning": "0.0.0.0에 바인딩되었습니다. 같은 네트워크의 누구나 연결해 에디터를 조작할 수 있으며, 이 엔드포인트는 자격 증명을 검증하지 않습니다. 신뢰할 수 있는 네트워크에서만 사용하세요."
|
||||
,"lan_address": "LAN 주소"
|
||||
};
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extension_name": "Cocos MCP Server",
|
||||
"description": "Pelayan AI MCP untuk Cocos Creator 3.8",
|
||||
"panel_title": "Pelayan MCP",
|
||||
"open_panel": "Buka Panel MCP",
|
||||
"start_server": "Mulakan Pelayan",
|
||||
"stop_server": "Hentikan Pelayan",
|
||||
"server_status": "Status Pelayan",
|
||||
"port": "Port",
|
||||
"settings": "Tetapan",
|
||||
"connected": "Disambungkan",
|
||||
"disconnected": "Terputus",
|
||||
"server_running": "Pelayan sedang berjalan pada port {0}",
|
||||
"server_stopped": "Pelayan telah berhenti",
|
||||
"auto_start": "Mula Automatik",
|
||||
"debug_log": "Log Nyahpepijat",
|
||||
"max_connections": "Sambungan Maksimum",
|
||||
"connection_info": "Maklumat Sambungan",
|
||||
"http_url": "HTTP URL",
|
||||
"copy": "Salin",
|
||||
"save_settings": "Simpan Tetapan",
|
||||
"settings_saved": "Tetapan berjaya disimpan",
|
||||
"server_started": "Pelayan MCP Dimulakan",
|
||||
"server_stopped_msg": "Pelayan MCP Dihentikan",
|
||||
"failed_to_start": "Gagal memulakan pelayan",
|
||||
"failed_to_stop": "Gagal menghentikan pelayan",
|
||||
"failed_to_save": "Gagal menyimpan tetapan",
|
||||
"url_copied": "HTTP URL disalin ke papan keratan",
|
||||
"tool_manager": "Pengurus Alat",
|
||||
"open_tool_manager": "Buka Pengurus Alat",
|
||||
"create_config": "Cipta Konfigurasi",
|
||||
"edit_config": "Edit Konfigurasi",
|
||||
"delete_config": "Padam Konfigurasi",
|
||||
"import_config": "Import Konfigurasi",
|
||||
"export_config": "Eksport Konfigurasi",
|
||||
"apply_config": "Guna Konfigurasi",
|
||||
"select_all": "Pilih Semua",
|
||||
"deselect_all": "Nyahpilih Semua",
|
||||
"save_changes": "Simpan Perubahan",
|
||||
"config_name": "Nama Konfigurasi",
|
||||
"config_description": "Penerangan Konfigurasi",
|
||||
"current_config": "Konfigurasi Semasa",
|
||||
"tool_management": "Pengurusan Alat",
|
||||
"total_tools": "Jumlah Alat",
|
||||
"enabled_tools": "Didayakan",
|
||||
"disabled_tools": "Dilumpuhkan",
|
||||
"no_config_selected": "Tiada Konfigurasi Dipilih",
|
||||
"select_config_first": "Sila pilih konfigurasi atau cipta yang baharu terlebih dahulu",
|
||||
"config_created": "Konfigurasi berjaya dicipta",
|
||||
"config_updated": "Konfigurasi berjaya dikemas kini",
|
||||
"config_deleted": "Konfigurasi berjaya dipadam",
|
||||
"config_applied": "Konfigurasi berjaya digunakan",
|
||||
"config_exported": "Konfigurasi berjaya dieksport",
|
||||
"config_imported": "Konfigurasi berjaya diimport",
|
||||
"confirm_delete": "Sahkan Pemadaman",
|
||||
"delete_config_confirm": "Adakah anda pasti mahu memadam konfigurasi \"{0}\"? Tindakan ini tidak boleh dibatalkan.",
|
||||
"max_config_slots_reached": "Slot konfigurasi maksimum telah dicapai ({0})",
|
||||
"invalid_config_format": "Format konfigurasi tidak sah",
|
||||
"invalid_json_format": "Format JSON atau struktur konfigurasi tidak sah",
|
||||
"server_tab": "Pelayan",
|
||||
"tools_tab": "Pengurusan Alat",
|
||||
"available_tools": "Alat Tersedia",
|
||||
"scene_tools": "Alat Scene",
|
||||
"node_tools": "Alat Node",
|
||||
"component_tools": "Alat Komponen",
|
||||
"prefab_tools": "Alat Prefab",
|
||||
"project_tools": "Alat Projek",
|
||||
"debug_tools": "Alat Nyahpepijat",
|
||||
"preferences_tools": "Keutamaan",
|
||||
"server_tools": "Alat Pelayan",
|
||||
"broadcast_tools": "Alat Siaran",
|
||||
"scene_advanced_tools": "Alat Scene Lanjutan",
|
||||
"scene_view_tools": "Alat Paparan Scene",
|
||||
"reference_image_tools": "Alat Imej Rujukan",
|
||||
"asset_advanced_tools": "Alat Aset Lanjutan",
|
||||
"validation_tools": "Alat Pengesahan",
|
||||
"brand_name": "LiDaxian MCP Series",
|
||||
"brand_subtitle": "LiDaxian MCP Series",
|
||||
"contact_email": "E-mel",
|
||||
"contact_wechat": "WeChat",
|
||||
"contact_github": "GitHub",
|
||||
"machine_id_label": "ID Mesin",
|
||||
"purchase_renew": "Beli / Perbaharui",
|
||||
"purchase_url": "https://www.vberai.com/game-engines/cocos",
|
||||
"auth_title": "Pilih kaedah log masuk",
|
||||
"auth_subtitle": "Masukkan e-mel dan kod pengaktifan anda",
|
||||
"auth_choose_subtitle": "Pilih kaedah log masuk untuk meneruskan",
|
||||
"email_label": "Alamat E-mel",
|
||||
"activation_code_label": "Kod Pengaktifan",
|
||||
"email_placeholder": "Masukkan e-mel",
|
||||
"code_placeholder": "Masukkan kod (XXXXX-XXXXX-XXXXX-XXXXX-XXXXX)",
|
||||
"activate_button": "Aktifkan",
|
||||
"deactivate_button": "Nyahaktifkan",
|
||||
"activating": "Mengaktifkan...",
|
||||
"deactivating": "Menyahaktifkan...",
|
||||
"license_status": "Status Lesen",
|
||||
"license_type": "Jenis Lesen",
|
||||
"expiry_date": "Tarikh Tamat Tempoh",
|
||||
"days_remaining": "Baki Hari",
|
||||
"days_unit": "hr",
|
||||
"permanent": "Kekal",
|
||||
"expiry_warning_title": "Lesen Akan Tamat Tempoh",
|
||||
"expiry_warning_message": "Lesen anda akan tamat tempoh dalam {0} hari. Sila perbaharui bagi mengelakkan gangguan perkhidmatan.",
|
||||
"renew_now": "Perbaharui Sekarang",
|
||||
"i_understand": "Saya Faham",
|
||||
"activation_success": "Lesen berjaya diaktifkan!",
|
||||
"activation_failed": "Pengaktifan gagal: {0}",
|
||||
"invalid_code": "Format kod pengaktifan tidak sah",
|
||||
"purchase_license": "Beli Lesen",
|
||||
"renew_license": "Perbaharui Lesen",
|
||||
"contact_support": "Sokongan",
|
||||
"language_switch": "Bahasa Melayu",
|
||||
"version_label": "Versi",
|
||||
"license_required": "Lesen diperlukan untuk memulakan pelayan",
|
||||
"license_check_failed": "Pemeriksaan lesen gagal",
|
||||
|
||||
"status_stopped": "Berhenti",
|
||||
"status_running": "Berjalan",
|
||||
"status_error": "Sambungan Gagal",
|
||||
"config_tab": "Konfigurasi Pantas",
|
||||
"status_label": "Status",
|
||||
"connections_count": "Sambungan",
|
||||
"transport_protocol": "Protokol Pengangkutan",
|
||||
"transport_type": "Jenis Pengangkutan",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "Port {0} sedang digunakan — bertukar ke {1}",
|
||||
"net_title": "Rangkaian / Proksi",
|
||||
"net_direct": "Sambungan terus",
|
||||
"net_via": "melalui {0} ({1})",
|
||||
"net_source_manual": "manual",
|
||||
"net_source_env": "pemboleh ubah persekitaran",
|
||||
"net_source_system": "tetapan sistem",
|
||||
"net_mode_auto": "Auto-kesan",
|
||||
"net_mode_manual": "Manual",
|
||||
"net_mode_off": "Tanpa proksi",
|
||||
"net_save": "Simpan",
|
||||
"net_test": "Uji sambungan",
|
||||
"net_testing": "Menguji…",
|
||||
"net_test_ok": "Berjaya disambung ({0} ms)",
|
||||
"net_test_fail": "Gagal: {0}",
|
||||
"net_hint": "Tamat masa log masuk biasanya masalah rangkaian. Jika guna VPN/proksi, masukkan alamatnya (cth. http://127.0.0.1:7890) atau tukar klien ke mod global (TUN).",
|
||||
"net_pac_warning": "Sistem menggunakan skrip PAC yang tidak dapat dibaca pemalam. Pilih Manual dan masukkan alamat proksi.",
|
||||
"port_label": "Port",
|
||||
"auto_start_label": "Mula Automatik",
|
||||
"debug_log_label": "Log Nyahpepijat",
|
||||
"max_connections_label": "Sambungan Maksimum",
|
||||
"server_settings_title": "Tetapan Pelayan",
|
||||
"server_status_title": "Status Pelayan",
|
||||
"connection_info_title": "Maklumat Sambungan",
|
||||
"tools_count": "{0} alat",
|
||||
"tools_stats": "{0} alat ({1} didayakan / {2} dilumpuhkan)",
|
||||
"quick_config_title": "Konfigurasi Pantas Klien MCP",
|
||||
"quick_config_desc": "Konfigurasikan pelayan MCP semasa secara automatik ke dalam editor AI dan alat CLI arus perdana",
|
||||
"server_info": "Maklumat Pelayan",
|
||||
"server_name_label": "Nama Pelayan",
|
||||
"server_address": "Alamat Pelayan",
|
||||
"timeout_ms": "Had Masa (ms)",
|
||||
"auto_config_title": "Konfigurasi Automatik (Editor IDE + Codex CLI)",
|
||||
"add_all": "Tambah Semua",
|
||||
"remove_all": "Buang Semua",
|
||||
"auto_config_note": "Klik butang untuk mengubah fail konfigurasi secara automatik (Cursor, Windsurf, Trae, Codex CLI)",
|
||||
"configured": "Dikonfigurasi",
|
||||
"not_configured": "Belum Dikonfigurasi",
|
||||
"auto_config_btn": "Konfigurasi Automatik",
|
||||
"delete_btn": "Padam",
|
||||
"copy_config": "Salin Konfigurasi",
|
||||
"open_config": "Buka Konfigurasi",
|
||||
"manual_config_title": "Konfigurasi Manual (Claude CLI + Gemini CLI)",
|
||||
"refresh_commands": "Muat Semula Arahan",
|
||||
"manual_config_note": "Alat CLI berikut memerlukan arahan terminal dijalankan secara manual",
|
||||
"config_scope": "Skop Konfigurasi:",
|
||||
"scope_user": "Tahap Pengguna",
|
||||
"scope_project": "Tahap Projek",
|
||||
"operation_log": "Log Operasi",
|
||||
"no_logs": "Tiada rekod operasi",
|
||||
"copyright_line": "© 2025 LiDaxian - Cocos Creator MCP Server v{0}",
|
||||
"protocol_line": "Berasaskan Protokol Streamable HTTP (MCP 2025-03-26)",
|
||||
"tools_copyright_line1": "Sistem Alat - 13 alat peringkat niat, 150+ operasi editor, penghantaran berasaskan kod action",
|
||||
"config_copyright_line1": "Konfigurasi Pantas - Menyokong Cursor, Windsurf, Trae, Codex CLI, Claude CLI, Gemini CLI",
|
||||
"email_required": "Sila masukkan e-mel dan kod pengaktifan",
|
||||
"permanent_label": "Kekal",
|
||||
"logout_button": "Log Keluar",
|
||||
"invite_code_label": "Kod Jemputan",
|
||||
"copy_button": "Salin",
|
||||
"change_code_button": "Tukar Kod",
|
||||
"change_code_title": "Tukar Kod Pengaktifan",
|
||||
"change_code_desc": "Masukkan kod pengaktifan baharu anda. Ikatan peranti semasa akan dilepaskan terlebih dahulu.",
|
||||
"confirm_change": "Sahkan",
|
||||
"cancel": "Batal",
|
||||
"code_required": "Sila masukkan kod pengaktifan",
|
||||
"expiry_inline_warning": "Hampir tamat tempoh!",
|
||||
"store_purchase_hint": "Jika anda membeli pemalam ini dari Cocos Store, sila hubungi saya untuk mendapatkan kod pengaktifan anda.",
|
||||
"license_expired_title": "Lesen Tamat Tempoh",
|
||||
"license_expired_message": "Lesen anda telah tamat tempoh dan pelayan tidak dapat dimulakan. Sila perbaharui atau aktifkan semula untuk meneruskan.",
|
||||
"go_to_login": "Pergi ke Log Masuk",
|
||||
|
||||
"log_load_config_failed": "Gagal memuatkan status konfigurasi: {0}",
|
||||
"log_gen_cli_failed": "Gagal menjana arahan CLI: {0}",
|
||||
"log_add_client_failed": "Gagal menambah ke {0}: {1}",
|
||||
"log_remove_client_failed": "Gagal membuang daripada {0}: {1}",
|
||||
"quick_config_dialog_title": "Konfigurasi Satu Klik",
|
||||
"config_operation_failed": "Operasi konfigurasi gagal",
|
||||
"config_file_not_found_changed_path": "Konfigurasi gagal: fail konfigurasi tidak ditemui. {0} mungkin dipasang dalam laluan tersuai.",
|
||||
"log_batch_add_failed": "Penambahan pukal gagal: {0}",
|
||||
"log_batch_remove_failed": "Pembuangan pukal gagal: {0}",
|
||||
"log_copied_config": "Konfigurasi {0} disalin ke papan keratan",
|
||||
"log_gen_config_failed": "Gagal menjana konfigurasi",
|
||||
"log_copy_config_failed": "Gagal menyalin konfigurasi: {0}",
|
||||
"log_copied_command": "Arahan {0} disalin ke papan keratan",
|
||||
"log_copy_command_failed": "Gagal menyalin arahan: {0}",
|
||||
"log_opened_config_file": "Fail konfigurasi dibuka: {0}",
|
||||
"log_open_config_failed": "Gagal membuka fail konfigurasi",
|
||||
"log_open_config_error": "Gagal membuka fail konfigurasi: {0}",
|
||||
|
||||
"update_available_title": "Kemas Kini Tersedia",
|
||||
"update_new_version": "Versi baharu v{0} telah tersedia!",
|
||||
"update_now": "Kemas Kini Sekarang",
|
||||
"update_not_now": "Bukan Sekarang",
|
||||
"update_skip_version": "Langkau v{0}",
|
||||
"check_for_updates": "Semak Kemas Kini",
|
||||
"update_checking": "Menyemak...",
|
||||
"update_no_update": "Anda sedang menggunakan versi terkini",
|
||||
"update_urgency_critical": "Kemas Kini Kritikal",
|
||||
"update_urgency_high": "Kemas Kini Penting",
|
||||
"update_current_version": "Versi semasa: v{0}",
|
||||
"update_changelog": "Log Perubahan"
|
||||
,"update_changelog_empty": "Tiada nota keluaran disediakan untuk versi ini."
|
||||
,"subscription_login_title": "Akaun langganan VberAI"
|
||||
,"subscription_password_label": "Kata Laluan"
|
||||
,"subscription_password_placeholder": "Masukkan kata laluan"
|
||||
,"subscription_login_button": "Log masuk dengan VberAI"
|
||||
,"subscription_google_button": "Log masuk dengan Google"
|
||||
,"subscription_google_pending": "Pelayar telah dibuka. Selesaikan kebenaran Google, kemudian kembali ke Cocos Creator."
|
||||
,"subscription_google_pending_button": "Menunggu kebenaran Google..."
|
||||
,"subscription_register_button": "Cipta akaun VberAI"
|
||||
,"subscription_logging_in": "Sedang log masuk..."
|
||||
,"subscription_credentials_required": "Sila masukkan e-mel dan kata laluan"
|
||||
,"subscription_login_success": "Akaun langganan telah log masuk"
|
||||
,"subscription_login_failed": "Log masuk akaun langganan gagal"
|
||||
,"subscription_not_entitled": "Langganan semasa anda tidak merangkumi Cocos 3.x MCP. Naik taraf untuk meneruskan."
|
||||
,"subscription_upgrade_to": "Naik taraf ke {0} untuk menggunakan produk ini"
|
||||
,"subscription_upgrade_action": "Naik taraf langganan"
|
||||
,"subscription_logged_in": "Akaun yang log masuk"
|
||||
,"subscription_choose_button": "Akaun langganan VberAI"
|
||||
,"license_choose_button": "Kod pengaktifan"
|
||||
,"subscription_method_label": "Langganan"
|
||||
,"back_to_login_choices": "← Pilih kaedah log masuk lain"
|
||||
,"subscription_signed_out_other_device": "Log keluar: akaun anda digunakan pada peranti lain."
|
||||
,"subscription_google_cancelled": "Log masuk Google telah dibatalkan."
|
||||
,"subscription_request_timeout": "Permintaan tamat masa. Semak rangkaian anda dan cuba lagi."
|
||||
,"subscription_network_error": "Sambungan rangkaian gagal. Semak rangkaian anda dan cuba lagi."
|
||||
,"subscription_server_error": "Pelayan mengembalikan respons tidak sah. Cuba lagi kemudian."
|
||||
,"subscription_google_authorization_failed": "Kebenaran Google tidak selesai. Sila cuba lagi."
|
||||
,"lan_access_label": "Benarkan akses LAN"
|
||||
,"lan_access_warning": "Terikat pada 0.0.0.0. Sesiapa sahaja dalam rangkaian ini boleh menyambung dan mengawal editor — endpoint ini tidak mengesahkan sebarang kelayakan. Dayakan hanya pada rangkaian yang dipercayai."
|
||||
,"lan_address": "Alamat LAN"
|
||||
};
|
||||
@@ -0,0 +1,259 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extension_name": "Cocos MCP Server",
|
||||
"description": "Serwer AI MCP dla Cocos Creator 3.8",
|
||||
"panel_title": "Serwer MCP",
|
||||
"open_panel": "Otwórz panel MCP",
|
||||
"start_server": "Uruchom serwer",
|
||||
"stop_server": "Zatrzymaj serwer",
|
||||
"server_status": "Stan serwera",
|
||||
"port": "Port",
|
||||
"settings": "Ustawienia",
|
||||
"connected": "Połączono",
|
||||
"disconnected": "Rozłączono",
|
||||
"server_running": "Serwer działa na porcie {0}",
|
||||
"server_stopped": "Serwer został zatrzymany",
|
||||
"auto_start": "Automatyczne uruchamianie",
|
||||
"debug_log": "Dziennik debugowania",
|
||||
"max_connections": "Maksymalna liczba połączeń",
|
||||
"connection_info": "Informacje o połączeniu",
|
||||
"http_url": "Adres HTTP",
|
||||
"copy": "Kopiuj",
|
||||
"save_settings": "Zapisz ustawienia",
|
||||
"settings_saved": "Ustawienia zapisane pomyślnie",
|
||||
"server_started": "Serwer MCP uruchomiony",
|
||||
"server_stopped_msg": "Serwer MCP zatrzymany",
|
||||
"failed_to_start": "Nie udało się uruchomić serwera",
|
||||
"failed_to_stop": "Nie udało się zatrzymać serwera",
|
||||
"failed_to_save": "Nie udało się zapisać ustawień",
|
||||
"url_copied": "Adres HTTP skopiowano do schowka",
|
||||
"tool_manager": "Menedżer narzędzi",
|
||||
"open_tool_manager": "Otwórz menedżera narzędzi",
|
||||
"create_config": "Utwórz konfigurację",
|
||||
"edit_config": "Edytuj konfigurację",
|
||||
"delete_config": "Usuń konfigurację",
|
||||
"import_config": "Importuj konfigurację",
|
||||
"export_config": "Eksportuj konfigurację",
|
||||
"apply_config": "Zastosuj konfigurację",
|
||||
"select_all": "Zaznacz wszystko",
|
||||
"deselect_all": "Odznacz wszystko",
|
||||
"save_changes": "Zapisz zmiany",
|
||||
"config_name": "Nazwa konfiguracji",
|
||||
"config_description": "Opis konfiguracji",
|
||||
"current_config": "Bieżąca konfiguracja",
|
||||
"tool_management": "Zarządzanie narzędziami",
|
||||
"total_tools": "Łączna liczba narzędzi",
|
||||
"enabled_tools": "Włączone",
|
||||
"disabled_tools": "Wyłączone",
|
||||
"no_config_selected": "Nie wybrano konfiguracji",
|
||||
"select_config_first": "Najpierw wybierz konfigurację lub utwórz nową",
|
||||
"config_created": "Konfiguracja utworzona pomyślnie",
|
||||
"config_updated": "Konfiguracja zaktualizowana pomyślnie",
|
||||
"config_deleted": "Konfiguracja usunięta pomyślnie",
|
||||
"config_applied": "Konfiguracja zastosowana pomyślnie",
|
||||
"config_exported": "Konfiguracja wyeksportowana pomyślnie",
|
||||
"config_imported": "Konfiguracja zaimportowana pomyślnie",
|
||||
"confirm_delete": "Potwierdź usunięcie",
|
||||
"delete_config_confirm": "Czy na pewno chcesz usunąć konfigurację \"{0}\"? Tej operacji nie można cofnąć.",
|
||||
"max_config_slots_reached": "Osiągnięto maksymalną liczbę slotów konfiguracji ({0})",
|
||||
"invalid_config_format": "Nieprawidłowy format konfiguracji",
|
||||
"invalid_json_format": "Nieprawidłowy format JSON lub struktura konfiguracji",
|
||||
"server_tab": "Serwer",
|
||||
"tools_tab": "Zarządzanie narzędziami",
|
||||
"available_tools": "Dostępne narzędzia",
|
||||
"scene_tools": "Narzędzia sceny",
|
||||
"node_tools": "Narzędzia węzłów",
|
||||
"component_tools": "Narzędzia komponentów",
|
||||
"prefab_tools": "Narzędzia prefabrykatów",
|
||||
"project_tools": "Narzędzia projektu",
|
||||
"debug_tools": "Narzędzia debugowania",
|
||||
"preferences_tools": "Preferencje",
|
||||
"server_tools": "Narzędzia serwera",
|
||||
"broadcast_tools": "Narzędzia rozgłaszania",
|
||||
"scene_advanced_tools": "Zaawansowane narzędzia sceny",
|
||||
"scene_view_tools": "Narzędzia widoku sceny",
|
||||
"reference_image_tools": "Narzędzia obrazu referencyjnego",
|
||||
"asset_advanced_tools": "Zaawansowane narzędzia zasobów",
|
||||
"validation_tools": "Narzędzia walidacji",
|
||||
"brand_name": "LiDaxian MCP Series",
|
||||
"brand_subtitle": "LiDaxian MCP Series",
|
||||
"contact_email": "E-mail",
|
||||
"contact_wechat": "WeChat",
|
||||
"contact_github": "GitHub",
|
||||
"machine_id_label": "Identyfikator maszyny",
|
||||
"purchase_renew": "Kup / Odnów",
|
||||
"purchase_url": "https://www.vberai.com/game-engines/cocos",
|
||||
"auth_title": "Wybierz metodę logowania",
|
||||
"auth_subtitle": "Wprowadź swój e-mail i kod aktywacyjny",
|
||||
"auth_choose_subtitle": "Wybierz metodę logowania, aby kontynuować",
|
||||
"email_label": "Adres e-mail",
|
||||
"activation_code_label": "Kod aktywacyjny",
|
||||
"email_placeholder": "Wprowadź e-mail",
|
||||
"code_placeholder": "Wprowadź kod (XXXXX-XXXXX-XXXXX-XXXXX-XXXXX)",
|
||||
"activate_button": "Aktywuj",
|
||||
"deactivate_button": "Dezaktywuj",
|
||||
"activating": "Aktywowanie...",
|
||||
"deactivating": "Dezaktywowanie...",
|
||||
"license_status": "Stan licencji",
|
||||
"license_type": "Typ licencji",
|
||||
"expiry_date": "Data wygaśnięcia",
|
||||
"days_remaining": "Pozostałe dni",
|
||||
"days_unit": "dni",
|
||||
"permanent": "Bezterminowa",
|
||||
"expiry_warning_title": "Licencja wkrótce wygaśnie",
|
||||
"expiry_warning_message": "Twoja licencja wygaśnie za {0} dni. Odnów ją, aby uniknąć przerwy w działaniu usługi.",
|
||||
"renew_now": "Odnów teraz",
|
||||
"i_understand": "Rozumiem",
|
||||
"activation_success": "Licencja aktywowana pomyślnie!",
|
||||
"activation_failed": "Aktywacja nie powiodła się: {0}",
|
||||
"invalid_code": "Nieprawidłowy format kodu aktywacyjnego",
|
||||
"purchase_license": "Kup licencję",
|
||||
"renew_license": "Odnów licencję",
|
||||
"contact_support": "Wsparcie",
|
||||
"language_switch": "Polski",
|
||||
"version_label": "Wersja",
|
||||
"license_required": "Do uruchomienia serwera wymagana jest licencja",
|
||||
"license_check_failed": "Sprawdzenie licencji nie powiodło się",
|
||||
|
||||
"status_stopped": "Zatrzymany",
|
||||
"status_running": "Działa",
|
||||
"status_error": "Połączenie nieudane",
|
||||
"config_tab": "Szybka konfiguracja",
|
||||
"status_label": "Stan",
|
||||
"connections_count": "Połączenia",
|
||||
"transport_protocol": "Protokół transportu",
|
||||
"transport_type": "Typ transportu",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "Port {0} zajęty — przełączono na {1}",
|
||||
"net_title": "Sieć / Proxy",
|
||||
"net_direct": "Połączenie bezpośrednie",
|
||||
"net_via": "przez {0} ({1})",
|
||||
"net_source_manual": "ręcznie",
|
||||
"net_source_env": "zmienna środowiskowa",
|
||||
"net_source_system": "ustawienia systemu",
|
||||
"net_mode_auto": "Wykryj automatycznie",
|
||||
"net_mode_manual": "Ręcznie",
|
||||
"net_mode_off": "Bez proxy",
|
||||
"net_save": "Zapisz",
|
||||
"net_test": "Testuj połączenie",
|
||||
"net_testing": "Testowanie…",
|
||||
"net_test_ok": "Połączono ({0} ms)",
|
||||
"net_test_fail": "Niepowodzenie: {0}",
|
||||
"net_hint": "Przekroczenia czasu logowania to zwykle problem sieci. Jeśli używasz VPN/proxy, podaj tu adres (np. http://127.0.0.1:7890) lub przełącz klienta w tryb globalny (TUN).",
|
||||
"net_pac_warning": "System używa skryptu PAC, którego wtyczka nie potrafi odczytać. Przełącz na Ręcznie i podaj adres proxy.",
|
||||
"port_label": "Port",
|
||||
"auto_start_label": "Automatyczne uruchamianie",
|
||||
"debug_log_label": "Dziennik debugowania",
|
||||
"max_connections_label": "Maksymalna liczba połączeń",
|
||||
"server_settings_title": "Ustawienia serwera",
|
||||
"server_status_title": "Stan serwera",
|
||||
"connection_info_title": "Informacje o połączeniu",
|
||||
"tools_count": "{0} narzędzi",
|
||||
"tools_stats": "{0} narzędzi ({1} włączonych / {2} wyłączonych)",
|
||||
"quick_config_title": "Szybka konfiguracja klientów MCP",
|
||||
"quick_config_desc": "Automatycznie skonfiguruj bieżący serwer MCP w popularnych edytorach AI i narzędziach CLI",
|
||||
"server_info": "Informacje o serwerze",
|
||||
"server_name_label": "Nazwa serwera",
|
||||
"server_address": "Adres serwera",
|
||||
"timeout_ms": "Limit czasu (ms)",
|
||||
"auto_config_title": "Konfiguracja automatyczna (edytory IDE + Codex CLI)",
|
||||
"add_all": "Dodaj wszystkie",
|
||||
"remove_all": "Usuń wszystkie",
|
||||
"auto_config_note": "Kliknij przycisk, aby automatycznie zmodyfikować pliki konfiguracyjne (Cursor, Windsurf, Trae, Codex CLI)",
|
||||
"configured": "Skonfigurowano",
|
||||
"not_configured": "Nie skonfigurowano",
|
||||
"auto_config_btn": "Konfiguruj automatycznie",
|
||||
"delete_btn": "Usuń",
|
||||
"copy_config": "Kopiuj konfigurację",
|
||||
"open_config": "Otwórz konfigurację",
|
||||
"manual_config_title": "Konfiguracja ręczna (Claude CLI + Gemini CLI)",
|
||||
"refresh_commands": "Odśwież polecenia",
|
||||
"manual_config_note": "Poniższe narzędzia CLI wymagają ręcznego wykonania poleceń w terminalu",
|
||||
"config_scope": "Zakres konfiguracji:",
|
||||
"scope_user": "Poziom użytkownika",
|
||||
"scope_project": "Poziom projektu",
|
||||
"operation_log": "Dziennik operacji",
|
||||
"no_logs": "Brak zapisanych operacji",
|
||||
"copyright_line": "© 2025 LiDaxian - Cocos Creator MCP Server v{0}",
|
||||
"protocol_line": "Oparte na protokole Streamable HTTP (MCP 2025-03-26)",
|
||||
"tools_copyright_line1": "System narzędzi - 13 narzędzi na poziomie intencji, ponad 150 operacji edytora, rozdzielanie według kodu action",
|
||||
"config_copyright_line1": "Szybka konfiguracja - Obsługa Cursor, Windsurf, Trae, Codex CLI, Claude CLI, Gemini CLI",
|
||||
"email_required": "Wprowadź e-mail i kod aktywacyjny",
|
||||
"permanent_label": "Bezterminowa",
|
||||
"logout_button": "Wyloguj",
|
||||
"invite_code_label": "Kod zaproszenia",
|
||||
"copy_button": "Kopiuj",
|
||||
"change_code_button": "Zmień kod",
|
||||
"change_code_title": "Zmień kod aktywacyjny",
|
||||
"change_code_desc": "Wprowadź nowy kod aktywacyjny. Bieżące powiązanie urządzenia zostanie najpierw zwolnione.",
|
||||
"confirm_change": "Potwierdź",
|
||||
"cancel": "Anuluj",
|
||||
"code_required": "Wprowadź kod aktywacyjny",
|
||||
"expiry_inline_warning": "Wkrótce wygasa!",
|
||||
"store_purchase_hint": "Jeśli kupiłeś tę wtyczkę w Cocos Store, skontaktuj się ze mną, aby otrzymać kod aktywacyjny.",
|
||||
"license_expired_title": "Licencja wygasła",
|
||||
"license_expired_message": "Twoja licencja wygasła i nie można uruchomić serwera. Odnów ją lub aktywuj ponownie, aby kontynuować.",
|
||||
"go_to_login": "Przejdź do logowania",
|
||||
|
||||
"log_load_config_failed": "Nie udało się wczytać stanu konfiguracji: {0}",
|
||||
"log_gen_cli_failed": "Nie udało się wygenerować poleceń CLI: {0}",
|
||||
"log_add_client_failed": "Nie udało się dodać do {0}: {1}",
|
||||
"log_remove_client_failed": "Nie udało się usunąć z {0}: {1}",
|
||||
"quick_config_dialog_title": "Konfiguracja jednym kliknięciem",
|
||||
"config_operation_failed": "Operacja konfiguracji nie powiodła się",
|
||||
"config_file_not_found_changed_path": "Konfiguracja nie powiodła się: nie znaleziono pliku konfiguracyjnego. {0} może być zainstalowany w niestandardowej ścieżce.",
|
||||
"log_batch_add_failed": "Zbiorcze dodawanie nie powiodło się: {0}",
|
||||
"log_batch_remove_failed": "Zbiorcze usuwanie nie powiodło się: {0}",
|
||||
"log_copied_config": "Skopiowano konfigurację {0} do schowka",
|
||||
"log_gen_config_failed": "Nie udało się wygenerować konfiguracji",
|
||||
"log_copy_config_failed": "Nie udało się skopiować konfiguracji: {0}",
|
||||
"log_copied_command": "Skopiowano polecenie {0} do schowka",
|
||||
"log_copy_command_failed": "Nie udało się skopiować polecenia: {0}",
|
||||
"log_opened_config_file": "Otwarto plik konfiguracyjny: {0}",
|
||||
"log_open_config_failed": "Nie udało się otworzyć pliku konfiguracyjnego",
|
||||
"log_open_config_error": "Nie udało się otworzyć pliku konfiguracyjnego: {0}",
|
||||
|
||||
"update_available_title": "Dostępna aktualizacja",
|
||||
"update_new_version": "Nowa wersja v{0} jest już dostępna!",
|
||||
"update_now": "Aktualizuj teraz",
|
||||
"update_not_now": "Nie teraz",
|
||||
"update_skip_version": "Pomiń v{0}",
|
||||
"check_for_updates": "Sprawdź aktualizacje",
|
||||
"update_checking": "Sprawdzanie...",
|
||||
"update_no_update": "Używasz najnowszej wersji",
|
||||
"update_urgency_critical": "Aktualizacja krytyczna",
|
||||
"update_urgency_high": "Ważna aktualizacja",
|
||||
"update_current_version": "Bieżąca wersja: v{0}",
|
||||
"update_changelog": "Lista zmian"
|
||||
,"update_changelog_empty": "Dla tej wersji nie podano informacji o zmianach."
|
||||
,"subscription_login_title": "Konto abonamentowe VberAI"
|
||||
,"subscription_password_label": "Hasło"
|
||||
,"subscription_password_placeholder": "Wprowadź hasło"
|
||||
,"subscription_login_button": "Zaloguj się przez VberAI"
|
||||
,"subscription_google_button": "Zaloguj się przez Google"
|
||||
,"subscription_google_pending": "Przeglądarka została otwarta. Dokończ autoryzację Google, a następnie wróć do Cocos Creator."
|
||||
,"subscription_google_pending_button": "Oczekiwanie na autoryzację Google..."
|
||||
,"subscription_register_button": "Załóż konto VberAI"
|
||||
,"subscription_logging_in": "Logowanie..."
|
||||
,"subscription_credentials_required": "Wprowadź e-mail i hasło"
|
||||
,"subscription_login_success": "Zalogowano na konto abonamentowe"
|
||||
,"subscription_login_failed": "Logowanie na konto abonamentowe nie powiodło się"
|
||||
,"subscription_not_entitled": "Twój obecny abonament nie obejmuje Cocos 3.x MCP. Podnieś plan, aby kontynuować."
|
||||
,"subscription_upgrade_to": "Przejdź na {0}, aby korzystać z tego produktu"
|
||||
,"subscription_upgrade_action": "Podnieś abonament"
|
||||
,"subscription_logged_in": "Zalogowane konto"
|
||||
,"subscription_choose_button": "Konto abonamentowe VberAI"
|
||||
,"license_choose_button": "Kod aktywacyjny"
|
||||
,"subscription_method_label": "Abonament"
|
||||
,"back_to_login_choices": "← Wybierz inną metodę logowania"
|
||||
,"subscription_signed_out_other_device": "Wylogowano: Twoje konto zostało użyte na innym urządzeniu."
|
||||
,"subscription_google_cancelled": "Logowanie przez Google zostało anulowane."
|
||||
,"subscription_request_timeout": "Przekroczono limit czasu żądania. Sprawdź sieć i spróbuj ponownie."
|
||||
,"subscription_network_error": "Połączenie sieciowe nie powiodło się. Sprawdź sieć i spróbuj ponownie."
|
||||
,"subscription_server_error": "Serwer zwrócił nieprawidłową odpowiedź. Spróbuj ponownie później."
|
||||
,"subscription_google_authorization_failed": "Autoryzacja Google nie została ukończona. Spróbuj ponownie."
|
||||
,"lan_access_label": "Zezwól na dostęp z sieci lokalnej"
|
||||
,"lan_access_warning": "Powiązano z 0.0.0.0. Każdy w tej sieci może się połączyć i sterować edytorem — ten punkt końcowy nie weryfikuje żadnych poświadczeń. Włączaj tylko w zaufanej sieci."
|
||||
,"lan_address": "Adres w sieci lokalnej"
|
||||
};
|
||||
@@ -123,11 +123,23 @@ module.exports = {
|
||||
"transport_protocol": "Protocolo de transporte",
|
||||
"transport_type": "Tipo de transporte",
|
||||
"mcp_url": "URL MCP",
|
||||
"port_fallback_title": "Porta {0} indisponível — alterada para {1}",
|
||||
"port_fallback_reason": "A porta {0} está reservada pelo sistema ou bloqueada por firewall/antivírus (comum no Windows com Hyper-V, WSL2 ou Docker). O servidor está funcionando normalmente na nova porta.",
|
||||
"port_fallback_resynced": "Configuração atualizada para: {0}. Reinicie esses clientes para que a nova porta tenha efeito.",
|
||||
"port_fallback_no_clients": "Se você já configurou clientes de IA, configure-os novamente para a nova porta pela configuração com um clique.",
|
||||
"port_fallback_dismiss": "Entendi",
|
||||
"port_fallback_title": "Porta {0} ocupada — alterada para {1}",
|
||||
"net_title": "Rede / Proxy",
|
||||
"net_direct": "Ligação direta",
|
||||
"net_via": "via {0} ({1})",
|
||||
"net_source_manual": "manual",
|
||||
"net_source_env": "variável de ambiente",
|
||||
"net_source_system": "definições do sistema",
|
||||
"net_mode_auto": "Deteção automática",
|
||||
"net_mode_manual": "Manual",
|
||||
"net_mode_off": "Sem proxy",
|
||||
"net_save": "Guardar",
|
||||
"net_test": "Testar ligação",
|
||||
"net_testing": "A testar…",
|
||||
"net_test_ok": "Ligado ({0} ms)",
|
||||
"net_test_fail": "Falhou: {0}",
|
||||
"net_hint": "Os tempos limite de início de sessão são quase sempre de rede. Se usar VPN/proxy, indique aqui o endereço (ex.: http://127.0.0.1:7890) ou mude o cliente para modo global (TUN).",
|
||||
"net_pac_warning": "O sistema usa um script PAC que este plugin não consegue avaliar. Mude para Manual e introduza o endereço do proxy.",
|
||||
"port_label": "Porta",
|
||||
"auto_start_label": "Início auto",
|
||||
"debug_log_label": "Log de depuração",
|
||||
@@ -240,4 +252,7 @@ module.exports = {
|
||||
"subscription_network_error": "Falha na conexão de rede. Verifique sua rede e tente novamente.",
|
||||
"subscription_server_error": "O servidor retornou uma resposta inválida. Tente novamente mais tarde.",
|
||||
"subscription_google_authorization_failed": "A autorização do Google não foi concluída. Tente novamente."
|
||||
,"lan_access_label": "Permitir acesso pela rede local"
|
||||
,"lan_access_warning": "Vinculado a 0.0.0.0. Qualquer pessoa nesta rede pode se conectar e operar o editor — este endpoint não verifica nenhuma credencial. Ative apenas em uma rede confiável."
|
||||
,"lan_address": "Endereço da rede local"
|
||||
};
|
||||
|
||||
@@ -123,11 +123,23 @@ module.exports = {
|
||||
"transport_protocol": "Транспортный протокол",
|
||||
"transport_type": "Тип транспорта",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "Порт {0} недоступен — выполнен переход на {1}",
|
||||
"port_fallback_reason": "Порт {0} зарезервирован системой или заблокирован брандмауэром/антивирусом (часто встречается в Windows с Hyper-V, WSL2 или Docker). Сервер нормально работает на новом порту.",
|
||||
"port_fallback_resynced": "Обновлена конфигурация для: {0}. Перезапустите эти клиенты, чтобы новый порт вступил в силу.",
|
||||
"port_fallback_no_clients": "Если вы уже настраивали ИИ-клиенты, перенастройте их на новый порт через конфигурацию в один клик.",
|
||||
"port_fallback_dismiss": "Понятно",
|
||||
"port_fallback_title": "Порт {0} занят — выполнен переход на {1}",
|
||||
"net_title": "Сеть / Прокси",
|
||||
"net_direct": "Прямое подключение",
|
||||
"net_via": "через {0} ({1})",
|
||||
"net_source_manual": "вручную",
|
||||
"net_source_env": "переменная окружения",
|
||||
"net_source_system": "системные настройки",
|
||||
"net_mode_auto": "Автоопределение",
|
||||
"net_mode_manual": "Вручную",
|
||||
"net_mode_off": "Без прокси",
|
||||
"net_save": "Сохранить",
|
||||
"net_test": "Проверить соединение",
|
||||
"net_testing": "Проверка…",
|
||||
"net_test_ok": "Соединение установлено ({0} мс)",
|
||||
"net_test_fail": "Ошибка: {0}",
|
||||
"net_hint": "Тайм-ауты входа почти всегда связаны с сетью. При использовании VPN/прокси укажите адрес здесь (например http://127.0.0.1:7890) или переключите клиент в глобальный режим (TUN).",
|
||||
"net_pac_warning": "Система использует PAC-скрипт, который плагин не может обработать. Переключитесь на «Вручную» и укажите адрес прокси.",
|
||||
"port_label": "Порт",
|
||||
"auto_start_label": "Автозапуск",
|
||||
"debug_log_label": "Журнал отладки",
|
||||
@@ -240,4 +252,7 @@ module.exports = {
|
||||
"subscription_network_error": "Сбой сетевого подключения. Проверьте сеть и повторите попытку.",
|
||||
"subscription_server_error": "Сервер вернул недопустимый ответ. Повторите попытку позже.",
|
||||
"subscription_google_authorization_failed": "Авторизация Google не завершена. Повторите попытку."
|
||||
,"lan_access_label": "Разрешить доступ по локальной сети"
|
||||
,"lan_access_warning": "Привязано к 0.0.0.0. Любой в этой сети может подключиться и управлять редактором — эта конечная точка не проверяет никаких учётных данных. Включайте только в доверенной сети."
|
||||
,"lan_address": "Адрес в локальной сети"
|
||||
};
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extension_name": "Cocos MCP Server",
|
||||
"description": "เซิร์ฟเวอร์ AI MCP สำหรับ Cocos Creator 3.8",
|
||||
"panel_title": "เซิร์ฟเวอร์ MCP",
|
||||
"open_panel": "เปิดแผง MCP",
|
||||
"start_server": "เริ่มเซิร์ฟเวอร์",
|
||||
"stop_server": "หยุดเซิร์ฟเวอร์",
|
||||
"server_status": "สถานะเซิร์ฟเวอร์",
|
||||
"port": "พอร์ต",
|
||||
"settings": "การตั้งค่า",
|
||||
"connected": "เชื่อมต่อแล้ว",
|
||||
"disconnected": "ตัดการเชื่อมต่อแล้ว",
|
||||
"server_running": "เซิร์ฟเวอร์กำลังทำงานบนพอร์ต {0}",
|
||||
"server_stopped": "เซิร์ฟเวอร์หยุดทำงานแล้ว",
|
||||
"auto_start": "เริ่มอัตโนมัติ",
|
||||
"debug_log": "บันทึกดีบัก",
|
||||
"max_connections": "จำนวนการเชื่อมต่อสูงสุด",
|
||||
"connection_info": "ข้อมูลการเชื่อมต่อ",
|
||||
"http_url": "HTTP URL",
|
||||
"copy": "คัดลอก",
|
||||
"save_settings": "บันทึกการตั้งค่า",
|
||||
"settings_saved": "บันทึกการตั้งค่าสำเร็จ",
|
||||
"server_started": "เซิร์ฟเวอร์ MCP เริ่มทำงานแล้ว",
|
||||
"server_stopped_msg": "เซิร์ฟเวอร์ MCP หยุดทำงานแล้ว",
|
||||
"failed_to_start": "เริ่มเซิร์ฟเวอร์ไม่สำเร็จ",
|
||||
"failed_to_stop": "หยุดเซิร์ฟเวอร์ไม่สำเร็จ",
|
||||
"failed_to_save": "บันทึกการตั้งค่าไม่สำเร็จ",
|
||||
"url_copied": "คัดลอก HTTP URL ไปยังคลิปบอร์ดแล้ว",
|
||||
"tool_manager": "ตัวจัดการเครื่องมือ",
|
||||
"open_tool_manager": "เปิดตัวจัดการเครื่องมือ",
|
||||
"create_config": "สร้างการกำหนดค่า",
|
||||
"edit_config": "แก้ไขการกำหนดค่า",
|
||||
"delete_config": "ลบการกำหนดค่า",
|
||||
"import_config": "นำเข้าการกำหนดค่า",
|
||||
"export_config": "ส่งออกการกำหนดค่า",
|
||||
"apply_config": "ใช้การกำหนดค่า",
|
||||
"select_all": "เลือกทั้งหมด",
|
||||
"deselect_all": "ยกเลิกเลือกทั้งหมด",
|
||||
"save_changes": "บันทึกการเปลี่ยนแปลง",
|
||||
"config_name": "ชื่อการกำหนดค่า",
|
||||
"config_description": "คำอธิบายการกำหนดค่า",
|
||||
"current_config": "การกำหนดค่าปัจจุบัน",
|
||||
"tool_management": "การจัดการเครื่องมือ",
|
||||
"total_tools": "เครื่องมือทั้งหมด",
|
||||
"enabled_tools": "เปิดใช้งาน",
|
||||
"disabled_tools": "ปิดใช้งาน",
|
||||
"no_config_selected": "ยังไม่ได้เลือกการกำหนดค่า",
|
||||
"select_config_first": "โปรดเลือกการกำหนดค่า หรือสร้างการกำหนดค่าใหม่ก่อน",
|
||||
"config_created": "สร้างการกำหนดค่าสำเร็จ",
|
||||
"config_updated": "อัปเดตการกำหนดค่าสำเร็จ",
|
||||
"config_deleted": "ลบการกำหนดค่าสำเร็จ",
|
||||
"config_applied": "ใช้การกำหนดค่าสำเร็จ",
|
||||
"config_exported": "ส่งออกการกำหนดค่าสำเร็จ",
|
||||
"config_imported": "นำเข้าการกำหนดค่าสำเร็จ",
|
||||
"confirm_delete": "ยืนยันการลบ",
|
||||
"delete_config_confirm": "คุณแน่ใจหรือไม่ว่าต้องการลบการกำหนดค่า \"{0}\" การดำเนินการนี้ไม่สามารถย้อนกลับได้",
|
||||
"max_config_slots_reached": "ถึงจำนวนช่องการกำหนดค่าสูงสุดแล้ว ({0})",
|
||||
"invalid_config_format": "รูปแบบการกำหนดค่าไม่ถูกต้อง",
|
||||
"invalid_json_format": "รูปแบบ JSON หรือโครงสร้างการกำหนดค่าไม่ถูกต้อง",
|
||||
"server_tab": "เซิร์ฟเวอร์",
|
||||
"tools_tab": "การจัดการเครื่องมือ",
|
||||
"available_tools": "เครื่องมือที่ใช้ได้",
|
||||
"scene_tools": "เครื่องมือฉาก",
|
||||
"node_tools": "เครื่องมือโหนด",
|
||||
"component_tools": "เครื่องมือคอมโพเนนต์",
|
||||
"prefab_tools": "เครื่องมือ Prefab",
|
||||
"project_tools": "เครื่องมือโปรเจกต์",
|
||||
"debug_tools": "เครื่องมือดีบัก",
|
||||
"preferences_tools": "การตั้งค่าส่วนตัว",
|
||||
"server_tools": "เครื่องมือเซิร์ฟเวอร์",
|
||||
"broadcast_tools": "เครื่องมือบรอดแคสต์",
|
||||
"scene_advanced_tools": "เครื่องมือฉากขั้นสูง",
|
||||
"scene_view_tools": "เครื่องมือมุมมองฉาก",
|
||||
"reference_image_tools": "เครื่องมือภาพอ้างอิง",
|
||||
"asset_advanced_tools": "เครื่องมือแอสเซทขั้นสูง",
|
||||
"validation_tools": "เครื่องมือตรวจสอบความถูกต้อง",
|
||||
"brand_name": "LiDaxian MCP Series",
|
||||
"brand_subtitle": "LiDaxian MCP Series",
|
||||
"contact_email": "อีเมล",
|
||||
"contact_wechat": "WeChat",
|
||||
"contact_github": "GitHub",
|
||||
"machine_id_label": "รหัสเครื่อง",
|
||||
"purchase_renew": "ซื้อ / ต่ออายุ",
|
||||
"purchase_url": "https://www.vberai.com/game-engines/cocos",
|
||||
"auth_title": "เลือกวิธีเข้าสู่ระบบ",
|
||||
"auth_subtitle": "กรอกอีเมลและรหัสเปิดใช้งานของคุณ",
|
||||
"auth_choose_subtitle": "เลือกวิธีเข้าสู่ระบบเพื่อดำเนินการต่อ",
|
||||
"email_label": "ที่อยู่อีเมล",
|
||||
"activation_code_label": "รหัสเปิดใช้งาน",
|
||||
"email_placeholder": "กรอกอีเมล",
|
||||
"code_placeholder": "กรอกรหัส (XXXXX-XXXXX-XXXXX-XXXXX-XXXXX)",
|
||||
"activate_button": "เปิดใช้งาน",
|
||||
"deactivate_button": "ยกเลิกการเปิดใช้งาน",
|
||||
"activating": "กำลังเปิดใช้งาน...",
|
||||
"deactivating": "กำลังยกเลิกการเปิดใช้งาน...",
|
||||
"license_status": "สถานะไลเซนส์",
|
||||
"license_type": "ประเภทไลเซนส์",
|
||||
"expiry_date": "วันหมดอายุ",
|
||||
"days_remaining": "จำนวนวันที่เหลือ",
|
||||
"days_unit": "วัน",
|
||||
"permanent": "ถาวร",
|
||||
"expiry_warning_title": "ไลเซนส์ใกล้หมดอายุ",
|
||||
"expiry_warning_message": "ไลเซนส์ของคุณจะหมดอายุในอีก {0} วัน โปรดต่ออายุเพื่อไม่ให้บริการหยุดชะงัก",
|
||||
"renew_now": "ต่ออายุตอนนี้",
|
||||
"i_understand": "รับทราบ",
|
||||
"activation_success": "เปิดใช้งานไลเซนส์สำเร็จ!",
|
||||
"activation_failed": "เปิดใช้งานไม่สำเร็จ: {0}",
|
||||
"invalid_code": "รูปแบบรหัสเปิดใช้งานไม่ถูกต้อง",
|
||||
"purchase_license": "ซื้อไลเซนส์",
|
||||
"renew_license": "ต่ออายุไลเซนส์",
|
||||
"contact_support": "ฝ่ายสนับสนุน",
|
||||
"language_switch": "ไทย",
|
||||
"version_label": "เวอร์ชัน",
|
||||
"license_required": "ต้องมีไลเซนส์จึงจะเริ่มเซิร์ฟเวอร์ได้",
|
||||
"license_check_failed": "ตรวจสอบไลเซนส์ไม่สำเร็จ",
|
||||
|
||||
"status_stopped": "หยุดแล้ว",
|
||||
"status_running": "กำลังทำงาน",
|
||||
"status_error": "เชื่อมต่อไม่สำเร็จ",
|
||||
"config_tab": "ตั้งค่าด่วน",
|
||||
"status_label": "สถานะ",
|
||||
"connections_count": "การเชื่อมต่อ",
|
||||
"transport_protocol": "โปรโตคอลการรับส่ง",
|
||||
"transport_type": "ประเภทการรับส่ง",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "พอร์ต {0} ถูกใช้งานอยู่ จึงเปลี่ยนเป็น {1}",
|
||||
"net_title": "เครือข่าย / พร็อกซี",
|
||||
"net_direct": "เชื่อมต่อโดยตรง",
|
||||
"net_via": "ผ่าน {0} ({1})",
|
||||
"net_source_manual": "กำหนดเอง",
|
||||
"net_source_env": "ตัวแปรสภาพแวดล้อม",
|
||||
"net_source_system": "การตั้งค่าระบบ",
|
||||
"net_mode_auto": "ตรวจหาอัตโนมัติ",
|
||||
"net_mode_manual": "กำหนดเอง",
|
||||
"net_mode_off": "ไม่ใช้พร็อกซี",
|
||||
"net_save": "บันทึก",
|
||||
"net_test": "ทดสอบการเชื่อมต่อ",
|
||||
"net_testing": "กำลังทดสอบ…",
|
||||
"net_test_ok": "เชื่อมต่อได้ ({0} ms)",
|
||||
"net_test_fail": "เชื่อมต่อไม่ได้: {0}",
|
||||
"net_hint": "การหมดเวลาเข้าสู่ระบบมักเกิดจากเครือข่าย หากใช้ VPN/พร็อกซี ให้กรอกที่อยู่ (เช่น http://127.0.0.1:7890) หรือสลับโปรแกรมพร็อกซีเป็นโหมดทั่วโลก (TUN)",
|
||||
"net_pac_warning": "ระบบใช้สคริปต์ PAC ซึ่งปลั๊กอินอ่านไม่ได้ กรุณาเลือก \"กำหนดเอง\" และกรอกที่อยู่พร็อกซี",
|
||||
"port_label": "พอร์ต",
|
||||
"auto_start_label": "เริ่มอัตโนมัติ",
|
||||
"debug_log_label": "บันทึกดีบัก",
|
||||
"max_connections_label": "จำนวนการเชื่อมต่อสูงสุด",
|
||||
"server_settings_title": "การตั้งค่าเซิร์ฟเวอร์",
|
||||
"server_status_title": "สถานะเซิร์ฟเวอร์",
|
||||
"connection_info_title": "ข้อมูลการเชื่อมต่อ",
|
||||
"tools_count": "{0} เครื่องมือ",
|
||||
"tools_stats": "{0} เครื่องมือ ({1} เปิดใช้งาน / {2} ปิดใช้งาน)",
|
||||
"quick_config_title": "ตั้งค่าไคลเอนต์ MCP อย่างรวดเร็ว",
|
||||
"quick_config_desc": "กำหนดค่าเซิร์ฟเวอร์ MCP ปัจจุบันลงในโปรแกรมแก้ไข AI และเครื่องมือ CLI ยอดนิยมโดยอัตโนมัติ",
|
||||
"server_info": "ข้อมูลเซิร์ฟเวอร์",
|
||||
"server_name_label": "ชื่อเซิร์ฟเวอร์",
|
||||
"server_address": "ที่อยู่เซิร์ฟเวอร์",
|
||||
"timeout_ms": "หมดเวลา (มิลลิวินาที)",
|
||||
"auto_config_title": "กำหนดค่าอัตโนมัติ (โปรแกรมแก้ไข IDE + Codex CLI)",
|
||||
"add_all": "เพิ่มทั้งหมด",
|
||||
"remove_all": "ลบทั้งหมด",
|
||||
"auto_config_note": "คลิกปุ่มเพื่อแก้ไขไฟล์กำหนดค่าโดยอัตโนมัติ (Cursor, Windsurf, Trae, Codex CLI)",
|
||||
"configured": "กำหนดค่าแล้ว",
|
||||
"not_configured": "ยังไม่ได้กำหนดค่า",
|
||||
"auto_config_btn": "กำหนดค่าอัตโนมัติ",
|
||||
"delete_btn": "ลบ",
|
||||
"copy_config": "คัดลอกการกำหนดค่า",
|
||||
"open_config": "เปิดการกำหนดค่า",
|
||||
"manual_config_title": "กำหนดค่าด้วยตนเอง (Claude CLI + Gemini CLI)",
|
||||
"refresh_commands": "รีเฟรชคำสั่ง",
|
||||
"manual_config_note": "เครื่องมือ CLI ต่อไปนี้ต้องรันคำสั่งในเทอร์มินัลด้วยตนเอง",
|
||||
"config_scope": "ขอบเขตการกำหนดค่า:",
|
||||
"scope_user": "ระดับผู้ใช้",
|
||||
"scope_project": "ระดับโปรเจกต์",
|
||||
"operation_log": "บันทึกการดำเนินการ",
|
||||
"no_logs": "ไม่มีบันทึกการดำเนินการ",
|
||||
"copyright_line": "© 2025 LiDaxian - Cocos Creator MCP Server v{0}",
|
||||
"protocol_line": "อิงตามโปรโตคอล Streamable HTTP (MCP 2025-03-26)",
|
||||
"tools_copyright_line1": "ระบบเครื่องมือ - เครื่องมือระดับเจตนา 13 รายการ, การดำเนินการในโปรแกรมแก้ไขกว่า 150 รายการ, ส่งต่อด้วยรหัส action",
|
||||
"config_copyright_line1": "ตั้งค่าด่วน - รองรับ Cursor, Windsurf, Trae, Codex CLI, Claude CLI, Gemini CLI",
|
||||
"email_required": "โปรดกรอกอีเมลและรหัสเปิดใช้งาน",
|
||||
"permanent_label": "ถาวร",
|
||||
"logout_button": "ออกจากระบบ",
|
||||
"invite_code_label": "รหัสเชิญ",
|
||||
"copy_button": "คัดลอก",
|
||||
"change_code_button": "เปลี่ยนรหัส",
|
||||
"change_code_title": "เปลี่ยนรหัสเปิดใช้งาน",
|
||||
"change_code_desc": "กรอกรหัสเปิดใช้งานใหม่ของคุณ การผูกอุปกรณ์ปัจจุบันจะถูกยกเลิกก่อน",
|
||||
"confirm_change": "ยืนยัน",
|
||||
"cancel": "ยกเลิก",
|
||||
"code_required": "โปรดกรอกรหัสเปิดใช้งาน",
|
||||
"expiry_inline_warning": "ใกล้หมดอายุ!",
|
||||
"store_purchase_hint": "หากคุณซื้อปลั๊กอินนี้จาก Cocos Store โปรดติดต่อผมเพื่อรับรหัสเปิดใช้งาน",
|
||||
"license_expired_title": "ไลเซนส์หมดอายุ",
|
||||
"license_expired_message": "ไลเซนส์ของคุณหมดอายุแล้ว จึงไม่สามารถเริ่มเซิร์ฟเวอร์ได้ โปรดต่ออายุหรือเปิดใช้งานใหม่เพื่อดำเนินการต่อ",
|
||||
"go_to_login": "ไปที่หน้าเข้าสู่ระบบ",
|
||||
|
||||
"log_load_config_failed": "โหลดสถานะการกำหนดค่าไม่สำเร็จ: {0}",
|
||||
"log_gen_cli_failed": "สร้างคำสั่ง CLI ไม่สำเร็จ: {0}",
|
||||
"log_add_client_failed": "เพิ่มลงใน {0} ไม่สำเร็จ: {1}",
|
||||
"log_remove_client_failed": "ลบออกจาก {0} ไม่สำเร็จ: {1}",
|
||||
"quick_config_dialog_title": "กำหนดค่าด้วยคลิกเดียว",
|
||||
"config_operation_failed": "การดำเนินการกำหนดค่าไม่สำเร็จ",
|
||||
"config_file_not_found_changed_path": "กำหนดค่าไม่สำเร็จ: ไม่พบไฟล์กำหนดค่า {0} อาจถูกติดตั้งไว้ในพาธที่กำหนดเอง",
|
||||
"log_batch_add_failed": "เพิ่มแบบกลุ่มไม่สำเร็จ: {0}",
|
||||
"log_batch_remove_failed": "ลบแบบกลุ่มไม่สำเร็จ: {0}",
|
||||
"log_copied_config": "คัดลอกการกำหนดค่า {0} ไปยังคลิปบอร์ดแล้ว",
|
||||
"log_gen_config_failed": "สร้างการกำหนดค่าไม่สำเร็จ",
|
||||
"log_copy_config_failed": "คัดลอกการกำหนดค่าไม่สำเร็จ: {0}",
|
||||
"log_copied_command": "คัดลอกคำสั่ง {0} ไปยังคลิปบอร์ดแล้ว",
|
||||
"log_copy_command_failed": "คัดลอกคำสั่งไม่สำเร็จ: {0}",
|
||||
"log_opened_config_file": "เปิดไฟล์กำหนดค่าแล้ว: {0}",
|
||||
"log_open_config_failed": "เปิดไฟล์กำหนดค่าไม่สำเร็จ",
|
||||
"log_open_config_error": "เปิดไฟล์กำหนดค่าไม่สำเร็จ: {0}",
|
||||
|
||||
"update_available_title": "มีอัปเดตใหม่",
|
||||
"update_new_version": "มีเวอร์ชันใหม่ v{0} แล้ว!",
|
||||
"update_now": "อัปเดตตอนนี้",
|
||||
"update_not_now": "ไว้ทีหลัง",
|
||||
"update_skip_version": "ข้าม v{0}",
|
||||
"check_for_updates": "ตรวจหาอัปเดต",
|
||||
"update_checking": "กำลังตรวจสอบ...",
|
||||
"update_no_update": "คุณกำลังใช้เวอร์ชันล่าสุดอยู่แล้ว",
|
||||
"update_urgency_critical": "อัปเดตเร่งด่วน",
|
||||
"update_urgency_high": "อัปเดตสำคัญ",
|
||||
"update_current_version": "เวอร์ชันปัจจุบัน: v{0}",
|
||||
"update_changelog": "บันทึกการเปลี่ยนแปลง"
|
||||
,"update_changelog_empty": "ไม่มีบันทึกการเปลี่ยนแปลงสำหรับเวอร์ชันนี้"
|
||||
,"subscription_login_title": "บัญชีสมาชิก VberAI"
|
||||
,"subscription_password_label": "รหัสผ่าน"
|
||||
,"subscription_password_placeholder": "กรอกรหัสผ่าน"
|
||||
,"subscription_login_button": "เข้าสู่ระบบด้วย VberAI"
|
||||
,"subscription_google_button": "เข้าสู่ระบบด้วย Google"
|
||||
,"subscription_google_pending": "เปิดเบราว์เซอร์แล้ว โปรดทำการอนุญาตผ่าน Google ให้เสร็จ แล้วกลับมาที่ Cocos Creator"
|
||||
,"subscription_google_pending_button": "กำลังรอการอนุญาตจาก Google..."
|
||||
,"subscription_register_button": "สร้างบัญชี VberAI"
|
||||
,"subscription_logging_in": "กำลังเข้าสู่ระบบ..."
|
||||
,"subscription_credentials_required": "โปรดกรอกอีเมลและรหัสผ่าน"
|
||||
,"subscription_login_success": "เข้าสู่ระบบบัญชีสมาชิกแล้ว"
|
||||
,"subscription_login_failed": "เข้าสู่ระบบบัญชีสมาชิกไม่สำเร็จ"
|
||||
,"subscription_not_entitled": "การสมัครสมาชิกปัจจุบันของคุณไม่รวม Cocos 3.x MCP โปรดอัปเกรดเพื่อดำเนินการต่อ"
|
||||
,"subscription_upgrade_to": "อัปเกรดเป็น {0} เพื่อใช้ผลิตภัณฑ์นี้"
|
||||
,"subscription_upgrade_action": "อัปเกรดการสมัครสมาชิก"
|
||||
,"subscription_logged_in": "บัญชีที่เข้าสู่ระบบ"
|
||||
,"subscription_choose_button": "บัญชีสมาชิก VberAI"
|
||||
,"license_choose_button": "รหัสเปิดใช้งาน"
|
||||
,"subscription_method_label": "สมาชิก"
|
||||
,"back_to_login_choices": "← เลือกวิธีเข้าสู่ระบบอื่น"
|
||||
,"subscription_signed_out_other_device": "ออกจากระบบแล้ว: บัญชีของคุณถูกใช้งานบนอุปกรณ์อื่น"
|
||||
,"subscription_google_cancelled": "การเข้าสู่ระบบด้วย Google ถูกยกเลิก"
|
||||
,"subscription_request_timeout": "คำขอหมดเวลา โปรดตรวจสอบเครือข่ายแล้วลองใหม่อีกครั้ง"
|
||||
,"subscription_network_error": "การเชื่อมต่อเครือข่ายล้มเหลว โปรดตรวจสอบเครือข่ายแล้วลองใหม่อีกครั้ง"
|
||||
,"subscription_server_error": "เซิร์ฟเวอร์ตอบกลับไม่ถูกต้อง โปรดลองใหม่ภายหลัง"
|
||||
,"subscription_google_authorization_failed": "การอนุญาตผ่าน Google ไม่เสร็จสมบูรณ์ โปรดลองใหม่อีกครั้ง"
|
||||
,"lan_access_label": "อนุญาตการเข้าถึงผ่าน LAN"
|
||||
,"lan_access_warning": "ผูกกับ 0.0.0.0 แล้ว ใครก็ตามในเครือข่ายนี้สามารถเชื่อมต่อและควบคุมโปรแกรมแก้ไขได้ ปลายทางนี้ไม่ตรวจสอบข้อมูลรับรองใด ๆ เปิดใช้เฉพาะในเครือข่ายที่เชื่อถือได้เท่านั้น"
|
||||
,"lan_address": "ที่อยู่ LAN"
|
||||
};
|
||||
@@ -0,0 +1,259 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extension_name": "Cocos MCP Server",
|
||||
"description": "Cocos Creator 3.8 için AI MCP Sunucusu",
|
||||
"panel_title": "MCP Sunucusu",
|
||||
"open_panel": "MCP Panelini Aç",
|
||||
"start_server": "Sunucuyu Başlat",
|
||||
"stop_server": "Sunucuyu Durdur",
|
||||
"server_status": "Sunucu Durumu",
|
||||
"port": "Bağlantı Noktası",
|
||||
"settings": "Ayarlar",
|
||||
"connected": "Bağlandı",
|
||||
"disconnected": "Bağlantı Kesildi",
|
||||
"server_running": "Sunucu {0} numaralı bağlantı noktasında çalışıyor",
|
||||
"server_stopped": "Sunucu durduruldu",
|
||||
"auto_start": "Otomatik Başlat",
|
||||
"debug_log": "Hata Ayıklama Günlüğü",
|
||||
"max_connections": "Maksimum Bağlantı",
|
||||
"connection_info": "Bağlantı Bilgisi",
|
||||
"http_url": "HTTP URL",
|
||||
"copy": "Kopyala",
|
||||
"save_settings": "Ayarları Kaydet",
|
||||
"settings_saved": "Ayarlar başarıyla kaydedildi",
|
||||
"server_started": "MCP Sunucusu Başlatıldı",
|
||||
"server_stopped_msg": "MCP Sunucusu Durduruldu",
|
||||
"failed_to_start": "Sunucu başlatılamadı",
|
||||
"failed_to_stop": "Sunucu durdurulamadı",
|
||||
"failed_to_save": "Ayarlar kaydedilemedi",
|
||||
"url_copied": "HTTP URL panoya kopyalandı",
|
||||
"tool_manager": "Araç Yöneticisi",
|
||||
"open_tool_manager": "Araç Yöneticisini Aç",
|
||||
"create_config": "Yapılandırma Oluştur",
|
||||
"edit_config": "Yapılandırmayı Düzenle",
|
||||
"delete_config": "Yapılandırmayı Sil",
|
||||
"import_config": "Yapılandırma İçe Aktar",
|
||||
"export_config": "Yapılandırma Dışa Aktar",
|
||||
"apply_config": "Yapılandırmayı Uygula",
|
||||
"select_all": "Tümünü Seç",
|
||||
"deselect_all": "Tüm Seçimi Kaldır",
|
||||
"save_changes": "Değişiklikleri Kaydet",
|
||||
"config_name": "Yapılandırma Adı",
|
||||
"config_description": "Yapılandırma Açıklaması",
|
||||
"current_config": "Geçerli Yapılandırma",
|
||||
"tool_management": "Araç Yönetimi",
|
||||
"total_tools": "Toplam Araç",
|
||||
"enabled_tools": "Etkin",
|
||||
"disabled_tools": "Devre Dışı",
|
||||
"no_config_selected": "Yapılandırma Seçilmedi",
|
||||
"select_config_first": "Lütfen önce bir yapılandırma seçin veya yeni bir tane oluşturun",
|
||||
"config_created": "Yapılandırma başarıyla oluşturuldu",
|
||||
"config_updated": "Yapılandırma başarıyla güncellendi",
|
||||
"config_deleted": "Yapılandırma başarıyla silindi",
|
||||
"config_applied": "Yapılandırma başarıyla uygulandı",
|
||||
"config_exported": "Yapılandırma başarıyla dışa aktarıldı",
|
||||
"config_imported": "Yapılandırma başarıyla içe aktarıldı",
|
||||
"confirm_delete": "Silmeyi Onayla",
|
||||
"delete_config_confirm": "\"{0}\" yapılandırmasını silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
|
||||
"max_config_slots_reached": "Maksimum yapılandırma yuvası sayısına ulaşıldı ({0})",
|
||||
"invalid_config_format": "Geçersiz yapılandırma biçimi",
|
||||
"invalid_json_format": "Geçersiz JSON biçimi veya yapılandırma yapısı",
|
||||
"server_tab": "Sunucu",
|
||||
"tools_tab": "Araç Yönetimi",
|
||||
"available_tools": "Kullanılabilir Araçlar",
|
||||
"scene_tools": "Sahne Araçları",
|
||||
"node_tools": "Düğüm Araçları",
|
||||
"component_tools": "Bileşen Araçları",
|
||||
"prefab_tools": "Prefab Araçları",
|
||||
"project_tools": "Proje Araçları",
|
||||
"debug_tools": "Hata Ayıklama Araçları",
|
||||
"preferences_tools": "Tercihler",
|
||||
"server_tools": "Sunucu Araçları",
|
||||
"broadcast_tools": "Yayın Araçları",
|
||||
"scene_advanced_tools": "Gelişmiş Sahne Araçları",
|
||||
"scene_view_tools": "Sahne Görünümü Araçları",
|
||||
"reference_image_tools": "Referans Görsel Araçları",
|
||||
"asset_advanced_tools": "Gelişmiş Varlık Araçları",
|
||||
"validation_tools": "Doğrulama Araçları",
|
||||
"brand_name": "LiDaxian MCP Series",
|
||||
"brand_subtitle": "LiDaxian MCP Series",
|
||||
"contact_email": "E-posta",
|
||||
"contact_wechat": "WeChat",
|
||||
"contact_github": "GitHub",
|
||||
"machine_id_label": "Makine Kimliği",
|
||||
"purchase_renew": "Satın Al / Yenile",
|
||||
"purchase_url": "https://www.vberai.com/game-engines/cocos",
|
||||
"auth_title": "Bir oturum açma yöntemi seçin",
|
||||
"auth_subtitle": "E-postanızı ve etkinleştirme kodunuzu girin",
|
||||
"auth_choose_subtitle": "Devam etmek için bir oturum açma yöntemi seçin",
|
||||
"email_label": "E-posta Adresi",
|
||||
"activation_code_label": "Etkinleştirme Kodu",
|
||||
"email_placeholder": "E-posta girin",
|
||||
"code_placeholder": "Kodu girin (XXXXX-XXXXX-XXXXX-XXXXX-XXXXX)",
|
||||
"activate_button": "Etkinleştir",
|
||||
"deactivate_button": "Devre Dışı Bırak",
|
||||
"activating": "Etkinleştiriliyor...",
|
||||
"deactivating": "Devre dışı bırakılıyor...",
|
||||
"license_status": "Lisans Durumu",
|
||||
"license_type": "Lisans Türü",
|
||||
"expiry_date": "Son Kullanma Tarihi",
|
||||
"days_remaining": "Kalan Gün",
|
||||
"days_unit": "gün",
|
||||
"permanent": "Kalıcı",
|
||||
"expiry_warning_title": "Lisansın Süresi Yakında Doluyor",
|
||||
"expiry_warning_message": "Lisansınızın süresi {0} gün içinde dolacak. Hizmet kesintisini önlemek için lütfen yenileyin.",
|
||||
"renew_now": "Şimdi Yenile",
|
||||
"i_understand": "Anladım",
|
||||
"activation_success": "Lisans başarıyla etkinleştirildi!",
|
||||
"activation_failed": "Etkinleştirme başarısız: {0}",
|
||||
"invalid_code": "Geçersiz etkinleştirme kodu biçimi",
|
||||
"purchase_license": "Lisans Satın Al",
|
||||
"renew_license": "Lisansı Yenile",
|
||||
"contact_support": "Destek",
|
||||
"language_switch": "Türkçe",
|
||||
"version_label": "Sürüm",
|
||||
"license_required": "Sunucuyu başlatmak için lisans gerekli",
|
||||
"license_check_failed": "Lisans denetimi başarısız",
|
||||
|
||||
"status_stopped": "Durduruldu",
|
||||
"status_running": "Çalışıyor",
|
||||
"status_error": "Bağlantı Başarısız",
|
||||
"config_tab": "Hızlı Yapılandırma",
|
||||
"status_label": "Durum",
|
||||
"connections_count": "Bağlantılar",
|
||||
"transport_protocol": "Aktarım Protokolü",
|
||||
"transport_type": "Aktarım Türü",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "Port {0} kullanımda — {1} portuna geçildi",
|
||||
"net_title": "Ağ / Proxy",
|
||||
"net_direct": "Doğrudan bağlantı",
|
||||
"net_via": "{0} üzerinden ({1})",
|
||||
"net_source_manual": "elle",
|
||||
"net_source_env": "ortam değişkeni",
|
||||
"net_source_system": "sistem ayarları",
|
||||
"net_mode_auto": "Otomatik algıla",
|
||||
"net_mode_manual": "Elle",
|
||||
"net_mode_off": "Proxy yok",
|
||||
"net_save": "Kaydet",
|
||||
"net_test": "Bağlantıyı test et",
|
||||
"net_testing": "Test ediliyor…",
|
||||
"net_test_ok": "Bağlandı ({0} ms)",
|
||||
"net_test_fail": "Başarısız: {0}",
|
||||
"net_hint": "Oturum açma zaman aşımları genellikle ağ kaynaklıdır. VPN/proxy kullanıyorsanız adresini buraya girin (örn. http://127.0.0.1:7890) ya da istemciyi global (TUN) moda alın.",
|
||||
"net_pac_warning": "Sistem, bu eklentinin çözemediği bir PAC betiği kullanıyor. Elle moduna geçip proxy adresini girin.",
|
||||
"port_label": "Bağlantı Noktası",
|
||||
"auto_start_label": "Otomatik Başlat",
|
||||
"debug_log_label": "Hata Ayıklama Günlüğü",
|
||||
"max_connections_label": "Maksimum Bağlantı",
|
||||
"server_settings_title": "Sunucu Ayarları",
|
||||
"server_status_title": "Sunucu Durumu",
|
||||
"connection_info_title": "Bağlantı Bilgisi",
|
||||
"tools_count": "{0} araç",
|
||||
"tools_stats": "{0} araç ({1} etkin / {2} devre dışı)",
|
||||
"quick_config_title": "MCP İstemcilerini Hızlı Yapılandır",
|
||||
"quick_config_desc": "Geçerli MCP sunucusunu yaygın AI editörlerine ve CLI araçlarına otomatik olarak yapılandırın",
|
||||
"server_info": "Sunucu Bilgisi",
|
||||
"server_name_label": "Sunucu Adı",
|
||||
"server_address": "Sunucu Adresi",
|
||||
"timeout_ms": "Zaman Aşımı (ms)",
|
||||
"auto_config_title": "Otomatik Yapılandırma (IDE Editörleri + Codex CLI)",
|
||||
"add_all": "Tümünü Ekle",
|
||||
"remove_all": "Tümünü Kaldır",
|
||||
"auto_config_note": "Yapılandırma dosyalarını otomatik değiştirmek için düğmeye tıklayın (Cursor, Windsurf, Trae, Codex CLI)",
|
||||
"configured": "Yapılandırıldı",
|
||||
"not_configured": "Yapılandırılmadı",
|
||||
"auto_config_btn": "Otomatik Yapılandır",
|
||||
"delete_btn": "Sil",
|
||||
"copy_config": "Yapılandırmayı Kopyala",
|
||||
"open_config": "Yapılandırmayı Aç",
|
||||
"manual_config_title": "Manuel Yapılandırma (Claude CLI + Gemini CLI)",
|
||||
"refresh_commands": "Komutları Yenile",
|
||||
"manual_config_note": "Aşağıdaki CLI araçları için terminal komutlarının elle çalıştırılması gerekir",
|
||||
"config_scope": "Yapılandırma Kapsamı:",
|
||||
"scope_user": "Kullanıcı Düzeyi",
|
||||
"scope_project": "Proje Düzeyi",
|
||||
"operation_log": "İşlem Günlüğü",
|
||||
"no_logs": "İşlem kaydı yok",
|
||||
"copyright_line": "© 2025 LiDaxian - Cocos Creator MCP Server v{0}",
|
||||
"protocol_line": "Streamable HTTP Protokolü temellidir (MCP 2025-03-26)",
|
||||
"tools_copyright_line1": "Araç Sistemi - 13 amaç düzeyinde araç, 150+ editör işlemi, action kodu ile dağıtım",
|
||||
"config_copyright_line1": "Hızlı Yapılandırma - Cursor, Windsurf, Trae, Codex CLI, Claude CLI, Gemini CLI desteklenir",
|
||||
"email_required": "Lütfen e-posta ve etkinleştirme kodunu girin",
|
||||
"permanent_label": "Kalıcı",
|
||||
"logout_button": "Çıkış Yap",
|
||||
"invite_code_label": "Davet Kodu",
|
||||
"copy_button": "Kopyala",
|
||||
"change_code_button": "Kodu Değiştir",
|
||||
"change_code_title": "Etkinleştirme Kodunu Değiştir",
|
||||
"change_code_desc": "Yeni etkinleştirme kodunuzu girin. Geçerli cihaz bağlaması önce serbest bırakılacaktır.",
|
||||
"confirm_change": "Onayla",
|
||||
"cancel": "İptal",
|
||||
"code_required": "Lütfen bir etkinleştirme kodu girin",
|
||||
"expiry_inline_warning": "Süresi yakında doluyor!",
|
||||
"store_purchase_hint": "Bu eklentiyi Cocos Store'dan satın aldıysanız, etkinleştirme kodunuzu almak için lütfen benimle iletişime geçin.",
|
||||
"license_expired_title": "Lisansın Süresi Doldu",
|
||||
"license_expired_message": "Lisansınızın süresi doldu ve sunucu başlatılamıyor. Devam etmek için lütfen yenileyin veya yeniden etkinleştirin.",
|
||||
"go_to_login": "Oturum Açmaya Git",
|
||||
|
||||
"log_load_config_failed": "Yapılandırma durumu yüklenemedi: {0}",
|
||||
"log_gen_cli_failed": "CLI komutları oluşturulamadı: {0}",
|
||||
"log_add_client_failed": "{0} öğesine eklenemedi: {1}",
|
||||
"log_remove_client_failed": "{0} öğesinden kaldırılamadı: {1}",
|
||||
"quick_config_dialog_title": "Tek Tıkla Yapılandırma",
|
||||
"config_operation_failed": "Yapılandırma işlemi başarısız",
|
||||
"config_file_not_found_changed_path": "Yapılandırma başarısız: yapılandırma dosyası bulunamadı. {0} özel bir yola kurulmuş olabilir.",
|
||||
"log_batch_add_failed": "Toplu ekleme başarısız: {0}",
|
||||
"log_batch_remove_failed": "Toplu kaldırma başarısız: {0}",
|
||||
"log_copied_config": "{0} yapılandırması panoya kopyalandı",
|
||||
"log_gen_config_failed": "Yapılandırma oluşturulamadı",
|
||||
"log_copy_config_failed": "Yapılandırma kopyalanamadı: {0}",
|
||||
"log_copied_command": "{0} komutu panoya kopyalandı",
|
||||
"log_copy_command_failed": "Komut kopyalanamadı: {0}",
|
||||
"log_opened_config_file": "Yapılandırma dosyası açıldı: {0}",
|
||||
"log_open_config_failed": "Yapılandırma dosyası açılamadı",
|
||||
"log_open_config_error": "Yapılandırma dosyası açılamadı: {0}",
|
||||
|
||||
"update_available_title": "Güncelleme Mevcut",
|
||||
"update_new_version": "Yeni sürüm v{0} yayınlandı!",
|
||||
"update_now": "Şimdi Güncelle",
|
||||
"update_not_now": "Şimdi Değil",
|
||||
"update_skip_version": "v{0} sürümünü atla",
|
||||
"check_for_updates": "Güncellemeleri Denetle",
|
||||
"update_checking": "Denetleniyor...",
|
||||
"update_no_update": "En son sürümü kullanıyorsunuz",
|
||||
"update_urgency_critical": "Kritik Güncelleme",
|
||||
"update_urgency_high": "Önemli Güncelleme",
|
||||
"update_current_version": "Geçerli sürüm: v{0}",
|
||||
"update_changelog": "Değişiklik Günlüğü"
|
||||
,"update_changelog_empty": "Bu sürüm için sürüm notu sağlanmadı."
|
||||
,"subscription_login_title": "VberAI abonelik hesabı"
|
||||
,"subscription_password_label": "Parola"
|
||||
,"subscription_password_placeholder": "Parolayı girin"
|
||||
,"subscription_login_button": "VberAI ile oturum aç"
|
||||
,"subscription_google_button": "Google ile oturum aç"
|
||||
,"subscription_google_pending": "Tarayıcı açıldı. Google yetkilendirmesini tamamlayın, ardından Cocos Creator'a dönün."
|
||||
,"subscription_google_pending_button": "Google yetkilendirmesi bekleniyor..."
|
||||
,"subscription_register_button": "VberAI hesabı oluştur"
|
||||
,"subscription_logging_in": "Oturum açılıyor..."
|
||||
,"subscription_credentials_required": "Lütfen e-posta ve parolayı girin"
|
||||
,"subscription_login_success": "Abonelik hesabıyla oturum açıldı"
|
||||
,"subscription_login_failed": "Abonelik oturum açma başarısız"
|
||||
,"subscription_not_entitled": "Mevcut aboneliğiniz Cocos 3.x MCP'yi içermiyor. Devam etmek için yükseltin."
|
||||
,"subscription_upgrade_to": "Bu ürünü kullanmak için {0} planına yükseltin"
|
||||
,"subscription_upgrade_action": "Aboneliği yükselt"
|
||||
,"subscription_logged_in": "Oturum açılan hesap"
|
||||
,"subscription_choose_button": "VberAI abonelik hesabı"
|
||||
,"license_choose_button": "Etkinleştirme kodu"
|
||||
,"subscription_method_label": "Abonelik"
|
||||
,"back_to_login_choices": "← Başka bir oturum açma yöntemi seç"
|
||||
,"subscription_signed_out_other_device": "Oturum kapatıldı: hesabınız başka bir cihazda kullanıldı."
|
||||
,"subscription_google_cancelled": "Google ile oturum açma iptal edildi."
|
||||
,"subscription_request_timeout": "İstek zaman aşımına uğradı. Ağınızı denetleyip yeniden deneyin."
|
||||
,"subscription_network_error": "Ağ bağlantısı başarısız. Ağınızı denetleyip yeniden deneyin."
|
||||
,"subscription_server_error": "Sunucu geçersiz bir yanıt döndürdü. Daha sonra yeniden deneyin."
|
||||
,"subscription_google_authorization_failed": "Google yetkilendirmesi tamamlanmadı. Lütfen yeniden deneyin."
|
||||
,"lan_access_label": "Yerel ağ erişimine izin ver"
|
||||
,"lan_access_warning": "0.0.0.0 adresine bağlandı. Bu ağdaki herkes bağlanıp editörü kullanabilir — bu uç nokta hiçbir kimlik bilgisini doğrulamaz. Yalnızca güvendiğiniz ağlarda etkinleştirin."
|
||||
,"lan_address": "Yerel ağ adresi"
|
||||
};
|
||||
@@ -124,11 +124,23 @@ module.exports = {
|
||||
"transport_protocol": "Giao thức truyền tải",
|
||||
"transport_type": "Loại truyền tải",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "Cổng {0} không khả dụng — đã chuyển sang {1}",
|
||||
"port_fallback_reason": "Cổng {0} bị hệ thống dành riêng hoặc bị tường lửa/phần mềm diệt virus chặn (thường gặp trên Windows có cài Hyper-V, WSL2 hoặc Docker). Máy chủ đang chạy bình thường trên cổng mới.",
|
||||
"port_fallback_resynced": "Đã cập nhật cấu hình cho: {0}. Hãy khởi động lại các client này để cổng mới có hiệu lực.",
|
||||
"port_fallback_no_clients": "Nếu bạn đã cấu hình client AI, hãy cấu hình lại sang cổng mới bằng thiết lập một chạm.",
|
||||
"port_fallback_dismiss": "Đã hiểu",
|
||||
"port_fallback_title": "Cổng {0} đang bận — đã chuyển sang {1}",
|
||||
"net_title": "Mạng / Proxy",
|
||||
"net_direct": "Kết nối trực tiếp",
|
||||
"net_via": "qua {0} ({1})",
|
||||
"net_source_manual": "thủ công",
|
||||
"net_source_env": "biến môi trường",
|
||||
"net_source_system": "cài đặt hệ thống",
|
||||
"net_mode_auto": "Tự động",
|
||||
"net_mode_manual": "Chỉ định thủ công",
|
||||
"net_mode_off": "Không dùng proxy",
|
||||
"net_save": "Lưu",
|
||||
"net_test": "Kiểm tra kết nối",
|
||||
"net_testing": "Đang kiểm tra…",
|
||||
"net_test_ok": "Kết nối thành công ({0} ms)",
|
||||
"net_test_fail": "Thất bại: {0}",
|
||||
"net_hint": "Lỗi hết thời gian đăng nhập thường do mạng. Nếu dùng VPN/proxy, hãy nhập địa chỉ (ví dụ http://127.0.0.1:7890) hoặc chuyển ứng dụng proxy sang chế độ toàn cục (TUN).",
|
||||
"net_pac_warning": "Hệ thống dùng script PAC tự động mà plugin không đọc được. Hãy chọn \"Chỉ định thủ công\" và nhập địa chỉ proxy.",
|
||||
"port_label": "Cổng",
|
||||
"auto_start_label": "Tự động khởi động",
|
||||
"debug_log_label": "Nhật ký gỡ lỗi",
|
||||
@@ -241,4 +253,7 @@ module.exports = {
|
||||
"subscription_network_error": "Kết nối mạng thất bại. Kiểm tra mạng và thử lại.",
|
||||
"subscription_server_error": "Máy chủ trả về phản hồi không hợp lệ. Thử lại sau.",
|
||||
"subscription_google_authorization_failed": "Ủy quyền Google chưa hoàn tất. Vui lòng thử lại."
|
||||
,"lan_access_label": "Cho phép truy cập LAN"
|
||||
,"lan_access_warning": "Đã gắn vào 0.0.0.0. Bất kỳ ai trong mạng này đều có thể kết nối và điều khiển trình biên tập — điểm cuối này không xác minh thông tin đăng nhập nào. Chỉ bật trong mạng đáng tin cậy."
|
||||
,"lan_address": "Địa chỉ LAN"
|
||||
};
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extension_name": "Cocos MCP 伺服器",
|
||||
"description": "適用於 Cocos Creator 3.8 的 AI MCP 伺服器",
|
||||
"panel_title": "MCP 伺服器",
|
||||
"open_panel": "開啟 MCP 面板",
|
||||
"start_server": "啟動伺服器",
|
||||
"stop_server": "停止伺服器",
|
||||
"server_status": "伺服器狀態",
|
||||
"port": "連接埠",
|
||||
"settings": "設定",
|
||||
"connected": "已連線",
|
||||
"disconnected": "未連線",
|
||||
"server_running": "伺服器正在連接埠 {0} 上執行",
|
||||
"server_stopped": "伺服器已停止",
|
||||
"auto_start": "自動啟動",
|
||||
"debug_log": "除錯日誌",
|
||||
"max_connections": "最大連線數",
|
||||
"connection_info": "連線資訊",
|
||||
"http_url": "HTTP 位址",
|
||||
"copy": "複製",
|
||||
"save_settings": "儲存設定",
|
||||
"settings_saved": "設定儲存成功",
|
||||
"server_started": "MCP 伺服器已啟動",
|
||||
"server_stopped_msg": "MCP 伺服器已停止",
|
||||
"failed_to_start": "啟動伺服器失敗",
|
||||
"failed_to_stop": "停止伺服器失敗",
|
||||
"failed_to_save": "儲存設定失敗",
|
||||
"url_copied": "HTTP 位址已複製到剪貼簿",
|
||||
"tool_manager": "工具管理員",
|
||||
"open_tool_manager": "開啟工具管理員",
|
||||
"create_config": "新增設定",
|
||||
"edit_config": "編輯設定",
|
||||
"delete_config": "刪除設定",
|
||||
"import_config": "匯入設定",
|
||||
"export_config": "匯出設定",
|
||||
"apply_config": "套用設定",
|
||||
"select_all": "全選",
|
||||
"deselect_all": "取消全選",
|
||||
"save_changes": "儲存變更",
|
||||
"config_name": "設定名稱",
|
||||
"config_description": "設定描述",
|
||||
"current_config": "目前設定",
|
||||
"tool_management": "工具管理",
|
||||
"total_tools": "工具總數",
|
||||
"enabled_tools": "已啟用",
|
||||
"disabled_tools": "已停用",
|
||||
"no_config_selected": "尚未選擇設定",
|
||||
"select_config_first": "請先選擇一個設定,或建立新的設定",
|
||||
"config_created": "設定建立成功",
|
||||
"config_updated": "設定更新成功",
|
||||
"config_deleted": "設定刪除成功",
|
||||
"config_applied": "設定套用成功",
|
||||
"config_exported": "設定匯出成功",
|
||||
"config_imported": "設定匯入成功",
|
||||
"confirm_delete": "確認刪除",
|
||||
"delete_config_confirm": "確定要刪除設定 \"{0}\" 嗎?此操作無法復原。",
|
||||
"max_config_slots_reached": "已達到最大設定插槽數量 ({0})",
|
||||
"invalid_config_format": "無效的設定格式",
|
||||
"invalid_json_format": "無效的 JSON 格式或設定結構",
|
||||
"server_tab": "伺服器",
|
||||
"tools_tab": "工具管理",
|
||||
"available_tools": "可用工具",
|
||||
"scene_tools": "場景工具",
|
||||
"node_tools": "節點工具",
|
||||
"component_tools": "元件工具",
|
||||
"prefab_tools": "預製件工具",
|
||||
"project_tools": "專案工具",
|
||||
"debug_tools": "除錯工具",
|
||||
"preferences_tools": "偏好設定",
|
||||
"server_tools": "伺服器工具",
|
||||
"broadcast_tools": "廣播工具",
|
||||
"scene_advanced_tools": "進階場景工具",
|
||||
"scene_view_tools": "場景檢視工具",
|
||||
"reference_image_tools": "參考圖片工具",
|
||||
"asset_advanced_tools": "進階資源工具",
|
||||
"validation_tools": "驗證工具",
|
||||
"brand_name": "李大仙 MCP 系列",
|
||||
"brand_subtitle": "LiDaxian MCP Series",
|
||||
"contact_email": "電子郵件",
|
||||
"contact_wechat": "微信",
|
||||
"contact_github": "GitHub",
|
||||
"machine_id_label": "機器碼",
|
||||
"purchase_renew": "購買/續約",
|
||||
"purchase_url": "https://www.vberai.com/game-engines/cocos",
|
||||
"auth_title": "選擇登入方式",
|
||||
"auth_subtitle": "請輸入您的電子郵件與啟用碼",
|
||||
"auth_choose_subtitle": "請選擇一種登入方式以繼續",
|
||||
"email_label": "電子郵件位址",
|
||||
"activation_code_label": "啟用碼",
|
||||
"email_placeholder": "請輸入電子郵件",
|
||||
"code_placeholder": "請輸入啟用碼 (XXXXX-XXXXX-XXXXX-XXXXX-XXXXX)",
|
||||
"activate_button": "啟用",
|
||||
"deactivate_button": "取消啟用",
|
||||
"activating": "啟用中...",
|
||||
"deactivating": "取消啟用中...",
|
||||
"license_status": "授權狀態",
|
||||
"license_type": "授權類型",
|
||||
"expiry_date": "到期日期",
|
||||
"days_remaining": "剩餘天數",
|
||||
"days_unit": "天",
|
||||
"permanent": "永久",
|
||||
"expiry_warning_title": "授權即將到期",
|
||||
"expiry_warning_message": "您的授權將在 {0} 天後到期,請及時續約以確保服務不中斷。",
|
||||
"renew_now": "立即續約",
|
||||
"i_understand": "我知道了",
|
||||
"activation_success": "授權啟用成功!",
|
||||
"activation_failed": "啟用失敗:{0}",
|
||||
"invalid_code": "無效的啟用碼格式",
|
||||
"purchase_license": "購買授權",
|
||||
"renew_license": "續約授權",
|
||||
"contact_support": "技術支援",
|
||||
"language_switch": "繁體中文",
|
||||
"version_label": "版本",
|
||||
"license_required": "需要授權才能啟動伺服器",
|
||||
"license_check_failed": "授權檢查失敗",
|
||||
|
||||
"status_stopped": "已停止",
|
||||
"status_running": "執行中",
|
||||
"status_error": "連線失敗",
|
||||
"config_tab": "一鍵設定",
|
||||
"status_label": "狀態",
|
||||
"connections_count": "連線數",
|
||||
"transport_protocol": "傳輸協定",
|
||||
"transport_type": "傳輸類型",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "連接埠 {0} 被佔用,已自動改用 {1}",
|
||||
"net_title": "網路 / 代理",
|
||||
"net_direct": "直連",
|
||||
"net_via": "經 {0}({1})",
|
||||
"net_source_manual": "手動填寫",
|
||||
"net_source_env": "環境變數",
|
||||
"net_source_system": "系統設定",
|
||||
"net_mode_auto": "自動偵測",
|
||||
"net_mode_manual": "手動指定",
|
||||
"net_mode_off": "不使用代理",
|
||||
"net_save": "儲存",
|
||||
"net_test": "測試連線",
|
||||
"net_testing": "測試中…",
|
||||
"net_test_ok": "連線正常({0} ms)",
|
||||
"net_test_fail": "連線失敗:{0}",
|
||||
"net_hint": "登入逾時多半是網路問題:伺服器在境外,若你在用 VPN/代理,請在此填寫代理位址(如 http://127.0.0.1:7890),或把代理軟體切到全域(TUN)模式。",
|
||||
"net_pac_warning": "系統使用了 PAC 自動設定指令碼,外掛無法解析。請改為「手動指定」並填寫代理位址。",
|
||||
"port_label": "連接埠",
|
||||
"auto_start_label": "自動啟動",
|
||||
"debug_log_label": "除錯日誌",
|
||||
"max_connections_label": "最大連線數",
|
||||
"server_settings_title": "伺服器設定",
|
||||
"server_status_title": "伺服器狀態",
|
||||
"connection_info_title": "連線資訊",
|
||||
"tools_count": "{0} 個工具",
|
||||
"tools_stats": "{0} 個工具 ({1} 啟用 / {2} 停用)",
|
||||
"quick_config_title": "一鍵設定 MCP 用戶端",
|
||||
"quick_config_desc": "自動將目前的 MCP 伺服器設定到主流 AI 編輯器與 CLI 工具中",
|
||||
"server_info": "伺服器資訊",
|
||||
"server_name_label": "伺服器名稱",
|
||||
"server_address": "伺服器位址",
|
||||
"timeout_ms": "逾時時間(ms)",
|
||||
"auto_config_title": "自動設定 (IDE 編輯器 + Codex CLI)",
|
||||
"add_all": "全部新增",
|
||||
"remove_all": "全部刪除",
|
||||
"auto_config_note": "點擊按鈕將自動修改設定檔(包括 Cursor、Windsurf、Trae、Codex CLI)",
|
||||
"configured": "已設定",
|
||||
"not_configured": "未設定",
|
||||
"auto_config_btn": "自動設定",
|
||||
"delete_btn": "刪除",
|
||||
"copy_config": "複製設定",
|
||||
"open_config": "開啟設定",
|
||||
"manual_config_title": "手動設定 (Claude CLI + Gemini CLI)",
|
||||
"refresh_commands": "重新整理指令",
|
||||
"manual_config_note": "以下 CLI 工具需要在終端機中手動執行指令進行設定",
|
||||
"config_scope": "設定範圍:",
|
||||
"scope_user": "使用者層級",
|
||||
"scope_project": "專案層級",
|
||||
"operation_log": "操作日誌",
|
||||
"no_logs": "目前沒有操作紀錄",
|
||||
"copyright_line": "© 2025 李大仙 (LiDaxian) - Cocos Creator MCP Server v{0}",
|
||||
"protocol_line": "基於 Streamable HTTP 協定 (MCP 2025-03-26)",
|
||||
"tools_copyright_line1": "工具系統 - 13 個意圖級工具,150+ 編輯器操作,action 操作碼分派",
|
||||
"config_copyright_line1": "一鍵設定 - 支援 Cursor、Windsurf、Trae、Codex CLI、Claude CLI、Gemini CLI",
|
||||
"email_required": "請輸入電子郵件與啟用碼",
|
||||
"permanent_label": "永久",
|
||||
"logout_button": "登出",
|
||||
"invite_code_label": "邀請碼",
|
||||
"copy_button": "複製",
|
||||
"change_code_button": "變更啟用碼",
|
||||
"change_code_title": "變更啟用碼",
|
||||
"change_code_desc": "輸入新的啟用碼,目前裝置的啟用佔位將被釋放後重新啟用。",
|
||||
"confirm_change": "確認變更",
|
||||
"cancel": "取消",
|
||||
"code_required": "請輸入啟用碼",
|
||||
"expiry_inline_warning": "即將到期!",
|
||||
"store_purchase_hint": "如果您已在 Cocos Store 購買了本外掛,請聯絡我取得啟用碼。",
|
||||
"license_expired_title": "授權已過期",
|
||||
"license_expired_message": "您的授權已過期,無法啟動服務。請續約或重新啟用以繼續使用。",
|
||||
"go_to_login": "重新登入",
|
||||
|
||||
"log_load_config_failed": "載入設定狀態失敗: {0}",
|
||||
"log_gen_cli_failed": "產生 CLI 指令失敗: {0}",
|
||||
"log_add_client_failed": "新增到 {0} 失敗: {1}",
|
||||
"log_remove_client_failed": "從 {0} 刪除失敗: {1}",
|
||||
"quick_config_dialog_title": "一鍵設定",
|
||||
"config_operation_failed": "設定操作失敗",
|
||||
"config_file_not_found_changed_path": "設定失敗:找不到設定檔,{0} 安裝時可能變更了路徑",
|
||||
"log_batch_add_failed": "批次新增失敗: {0}",
|
||||
"log_batch_remove_failed": "批次刪除失敗: {0}",
|
||||
"log_copied_config": "已複製 {0} 設定到剪貼簿",
|
||||
"log_gen_config_failed": "產生設定失敗",
|
||||
"log_copy_config_failed": "複製設定失敗: {0}",
|
||||
"log_copied_command": "已複製 {0} 指令到剪貼簿",
|
||||
"log_copy_command_failed": "複製指令失敗: {0}",
|
||||
"log_opened_config_file": "已開啟設定檔: {0}",
|
||||
"log_open_config_failed": "開啟設定檔失敗",
|
||||
"log_open_config_error": "開啟設定檔失敗: {0}",
|
||||
|
||||
"update_available_title": "發現新版本",
|
||||
"update_new_version": "新版本 v{0} 已發布!",
|
||||
"update_now": "立即更新",
|
||||
"update_not_now": "暫不更新",
|
||||
"update_skip_version": "略過 v{0}",
|
||||
"check_for_updates": "檢查更新",
|
||||
"update_checking": "檢查中...",
|
||||
"update_no_update": "目前已是最新版本",
|
||||
"update_urgency_critical": "緊急更新",
|
||||
"update_urgency_high": "重要更新",
|
||||
"update_current_version": "目前版本: v{0}",
|
||||
"update_changelog": "更新日誌"
|
||||
,"update_changelog_empty": "該版本尚未填寫更新內容。"
|
||||
,"subscription_login_title": "VberAI 官網訂閱帳號"
|
||||
,"subscription_password_label": "密碼"
|
||||
,"subscription_password_placeholder": "請輸入密碼"
|
||||
,"subscription_login_button": "使用 VberAI 帳號登入"
|
||||
,"subscription_google_button": "使用 Google 登入"
|
||||
,"subscription_google_pending": "已開啟瀏覽器,請完成 Google 授權後返回 Cocos Creator。"
|
||||
,"subscription_google_pending_button": "正在等待 Google 授權..."
|
||||
,"subscription_register_button": "註冊 VberAI 帳號"
|
||||
,"subscription_logging_in": "登入中..."
|
||||
,"subscription_credentials_required": "請輸入電子郵件與密碼"
|
||||
,"subscription_login_success": "訂閱帳號登入成功"
|
||||
,"subscription_login_failed": "訂閱帳號登入失敗"
|
||||
,"subscription_not_entitled": "目前的訂閱方案不包含 Cocos 3.x MCP,請升級後使用。"
|
||||
,"subscription_upgrade_to": "升級到「{0}」即可使用"
|
||||
,"subscription_upgrade_action": "前往升級訂閱"
|
||||
,"subscription_logged_in": "目前登入帳號"
|
||||
,"subscription_choose_button": "VberAI 官網訂閱帳號"
|
||||
,"license_choose_button": "啟用碼授權"
|
||||
,"subscription_method_label": "訂閱帳號"
|
||||
,"back_to_login_choices": "← 返回選擇登入方式"
|
||||
,"subscription_signed_out_other_device": "帳號已在另一台裝置登入,目前裝置已登出。"
|
||||
,"subscription_google_cancelled": "已取消 Google 登入"
|
||||
,"subscription_request_timeout": "請求逾時,請檢查網路後重試。"
|
||||
,"subscription_network_error": "網路連線失敗,請檢查網路後重試。"
|
||||
,"subscription_server_error": "伺服器回應異常,請稍後重試。"
|
||||
,"subscription_google_authorization_failed": "Google 授權未完成,請重試。"
|
||||
,"lan_access_label": "允許區域網路存取"
|
||||
,"lan_access_warning": "已繫結 0.0.0.0。同一網路內的任何人都能連上並操作編輯器,本介面不驗證任何憑證。請僅在可信任的網路下開啟。"
|
||||
,"lan_address": "區域網路位址"
|
||||
};
|
||||
@@ -124,11 +124,23 @@ module.exports = {
|
||||
"transport_protocol": "传输协议",
|
||||
"transport_type": "传输类型",
|
||||
"mcp_url": "MCP URL",
|
||||
"port_fallback_title": "端口 {0} 不可用,已自动切换到 {1}",
|
||||
"port_fallback_reason": "端口 {0} 被系统保留或被防火墙/杀毒软件拦截(安装了 Hyper-V、WSL2 或 Docker 的 Windows 上很常见)。服务已在新端口上正常运行。",
|
||||
"port_fallback_resynced": "已自动更新这些客户端的配置:{0}。请重启它们,新端口才会生效。",
|
||||
"port_fallback_no_clients": "如果已配置过 AI 客户端,请在\"一键配置\"里重新配置到新端口。",
|
||||
"port_fallback_dismiss": "知道了",
|
||||
"port_fallback_title": "端口 {0} 被占用,已自动改用 {1}",
|
||||
"net_title": "网络 / 代理",
|
||||
"net_direct": "直连",
|
||||
"net_via": "经 {0}({1})",
|
||||
"net_source_manual": "手动填写",
|
||||
"net_source_env": "环境变量",
|
||||
"net_source_system": "系统设置",
|
||||
"net_mode_auto": "自动检测",
|
||||
"net_mode_manual": "手动指定",
|
||||
"net_mode_off": "不使用代理",
|
||||
"net_save": "保存",
|
||||
"net_test": "测试连接",
|
||||
"net_testing": "测试中…",
|
||||
"net_test_ok": "连接正常({0} ms)",
|
||||
"net_test_fail": "连接失败:{0}",
|
||||
"net_hint": "登录超时多半是网络问题:服务器在境外,若你在用 VPN/代理,请在此填写代理地址(如 http://127.0.0.1:7890),或把代理软件切到全局(TUN)模式。",
|
||||
"net_pac_warning": "系统使用了 PAC 自动配置脚本,插件无法解析。请改为「手动指定」并填写代理地址。",
|
||||
"port_label": "端口",
|
||||
"auto_start_label": "自动启动",
|
||||
"debug_log_label": "调试日志",
|
||||
@@ -240,4 +252,7 @@ module.exports = {
|
||||
,"subscription_network_error": "网络连接失败,请检查网络后重试。"
|
||||
,"subscription_server_error": "服务器响应异常,请稍后重试。"
|
||||
,"subscription_google_authorization_failed": "Google 授权未完成,请重试。"
|
||||
,"lan_access_label": "允许局域网访问"
|
||||
,"lan_access_warning": "已绑定 0.0.0.0。同一网络内的任何人都能连上并操作编辑器,本接口不校验任何凭证。仅在可信网络下开启。"
|
||||
,"lan_address": "局域网地址"
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"$schema": "./@types/schema/package/index.json",
|
||||
"package_version": 2,
|
||||
"name": "cocos-mcp-server",
|
||||
"version": "1.7.9",
|
||||
"version": "1.8.0",
|
||||
"author": "LiDaxian",
|
||||
"editor": ">=3.7.0",
|
||||
"description": "i18n:cocos-mcp-server.description",
|
||||
@@ -236,6 +236,21 @@
|
||||
"methods": [
|
||||
"default.device-removed"
|
||||
]
|
||||
},
|
||||
"get-network-status": {
|
||||
"methods": [
|
||||
"getNetworkStatus"
|
||||
]
|
||||
},
|
||||
"set-proxy-config": {
|
||||
"methods": [
|
||||
"setProxyConfig"
|
||||
]
|
||||
},
|
||||
"test-connectivity": {
|
||||
"methods": [
|
||||
"testConnectivity"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scene": {
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.7.9
|
||||
1.8.0
|
||||
@@ -5,5 +5,7 @@
|
||||
"allowedOrigins": [
|
||||
"*"
|
||||
],
|
||||
"maxConnections": 10
|
||||
"maxConnections": 10,
|
||||
"allowLanAccess": false,
|
||||
"activePort": 3000
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"id": "1a189805-4f21-4147-91ab-461fe9ff5f28",
|
||||
"id": "e5e2ece0-4a82-44e1-8051-43ad5805be23",
|
||||
"name": "默认配置",
|
||||
"description": "自动创建的默认工具配置",
|
||||
"tools": [
|
||||
@@ -39,7 +39,7 @@
|
||||
"category": "cocos",
|
||||
"name": "editor",
|
||||
"enabled": true,
|
||||
"description": "Editor utilities — project info, console logs, preferences, build, preview, run/stop, reload.\nGroups: project_*, console_*, log_*, pref_*, build*, server_*, run, stop, reload.\n\"run\" launches preview (platform: browser/simulator/editor). \"stop\" stops editor preview. \"reload\" reloads editor window (Developer → 重新加载).\nExample: {action:\"run\", platform:\"editor\"} then {action:\"stop\"}"
|
||||
"description": "Editor utilities — project info, console logs, preferences, build, preview, run/stop/refresh, reload.\nGroups: project_*, console_*, log_*, pref_*, build*, server_*, run, stop, refresh_preview, reload.\n\"run\" launches preview (platform: browser/simulator/editor). \"stop\" stops editor preview. \"refresh_preview\" reloads the ALREADY RUNNING preview in place — use it after editing scenes or scripts instead of stop+run. \"reload\" reloads the editor window itself (Developer → 重新加载).\nExample: {action:\"run\", platform:\"editor\"} → edit something → {action:\"refresh_preview\"} → {action:\"stop\"}"
|
||||
},
|
||||
{
|
||||
"category": "cocos",
|
||||
@@ -102,10 +102,10 @@
|
||||
"description": "Label & Text — manage cc.Label, cc.RichText, cc.EditBox. Query/set text, font, style, outline, shadow. Batch operations.\nActions: info, list, set_text, set_font, set_style, set_outline, set_shadow, batch_set_font, batch_set_style.\nSupports: cc.Label (full), cc.RichText (text/font/fontSize/color), cc.EditBox (text/placeholder).\nExample: {action:\"set_text\", node:\"Title\", text:\"Hello\"}\n{action:\"set_style\", node:\"Title\", fontSize:32, isBold:true, color:{r:255,g:0,b:0,a:255}}\n{action:\"batch_set_font\", font:\"db://assets/fonts/myfont.ttf\"}"
|
||||
}
|
||||
],
|
||||
"createdAt": "2026-08-18T23:28:53.590Z",
|
||||
"updatedAt": "2026-08-18T23:28:53.590Z"
|
||||
"createdAt": "2026-08-27T13:18:56.268Z",
|
||||
"updatedAt": "2026-08-27T13:18:56.268Z"
|
||||
}
|
||||
],
|
||||
"currentConfigId": "1a189805-4f21-4147-91ab-461fe9ff5f28",
|
||||
"currentConfigId": "e5e2ece0-4a82-44e1-8051-43ad5805be23",
|
||||
"maxConfigSlots": 5
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { DEFAULT_THEME } from '../../YouleNexus/assets/framework/ui/theme/default-theme.ts';
|
||||
import { resolveTheme } from '../../YouleNexus/assets/framework/ui/theme/resolve-theme.ts';
|
||||
import { ThemeProvider } from '../../YouleNexus/assets/framework/ui/theme/theme-provider.ts';
|
||||
|
||||
test('resolveTheme 不传配置时返回默认主题', () => {
|
||||
const t = resolveTheme();
|
||||
assert.deepEqual(t, DEFAULT_THEME);
|
||||
});
|
||||
|
||||
test('resolveTheme 只覆盖给出的字段,其余取默认', () => {
|
||||
const t = resolveTheme({ colors: { primary: '#C8102E' } });
|
||||
assert.equal(t.colors.primary, '#C8102E');
|
||||
assert.equal(t.colors.accent, DEFAULT_THEME.colors.accent);
|
||||
assert.equal(t.fonts.sizeBody, DEFAULT_THEME.fonts.sizeBody);
|
||||
assert.equal(t.layout.myInfoAnchorX, DEFAULT_THEME.layout.myInfoAnchorX);
|
||||
});
|
||||
|
||||
test('resolveTheme 分组独立合并,互不影响', () => {
|
||||
const t = resolveTheme({ layout: { myInfoAnchorX: 130 } });
|
||||
assert.equal(t.layout.myInfoAnchorX, 130);
|
||||
assert.equal(t.layout.nicknameMaxChars, DEFAULT_THEME.layout.nicknameMaxChars);
|
||||
assert.deepEqual(t.colors, DEFAULT_THEME.colors);
|
||||
});
|
||||
|
||||
test('resolveTheme 不修改 DEFAULT_THEME 本身', () => {
|
||||
const before = DEFAULT_THEME.colors.primary;
|
||||
resolveTheme({ colors: { primary: '#000000' } });
|
||||
assert.equal(DEFAULT_THEME.colors.primary, before);
|
||||
});
|
||||
|
||||
test('ThemeProvider 未 init 就读取会抛错(第二准则:不兜底)', () => {
|
||||
ThemeProvider.reset();
|
||||
assert.throws(() => ThemeProvider.get(), /未初始化/);
|
||||
});
|
||||
|
||||
test('ThemeProvider init 后可读回同一份主题', () => {
|
||||
ThemeProvider.reset();
|
||||
const t = resolveTheme({ colors: { primary: '#123456' } });
|
||||
ThemeProvider.init(t);
|
||||
assert.equal(ThemeProvider.get().colors.primary, '#123456');
|
||||
});
|
||||
@@ -9,6 +9,8 @@
|
||||
"new-game": "node scripts/new-game.mjs",
|
||||
"check-cocos": "node scripts/check-cocos-version.mjs",
|
||||
"bump-cocos": "node scripts/bump-cocos.mjs",
|
||||
"check-skin": "node scripts/check-skin.mjs",
|
||||
"build-game": "node scripts/build-game.mjs",
|
||||
"test:framework": "node scripts/run-framework-tests.mjs",
|
||||
"typecheck:framework": "tsc -p tsconfig.framework.json --noEmit"
|
||||
},
|
||||
|
||||
@@ -9,11 +9,20 @@
|
||||
| `npm run check-cocos` | 校验所有子游戏工程的 `creator.version` 与宿主 `YouleNexus` 一致(不一致退出码 1) |
|
||||
| `npm run bump-cocos <version>` | 批量改宿主+所有子游戏的 `creator.version`(之后仍需逐个用编辑器打开迁移) |
|
||||
| `npm test` | 运行工具链全部单元测试 |
|
||||
| `npm run check-skin <name>` | 校验子游戏 `assets/game/override/` 是否合法(孤儿/尺寸不一致/Spine 缺件),不构建,秒级反馈 |
|
||||
| `npm run build-game <name> [-- --platform android]` | 完整构建流水线:版本校验 → 实体化到 `build-workspace/` → 皮肤合成 → Cocos CLI 构建 → 写 `build-info.json` |
|
||||
|
||||
## 关键约束
|
||||
- **framework 真源**唯一存放于 `YouleNexus/assets/framework`;各子游戏的 `assets/framework` 是指向它的 junction(不入库,见根 `.gitignore`)。clone 后跑 `setup-links` 重建。
|
||||
- **Cocos 版本必须全 monorepo 统一**(junction 共享同一份资源,跨版本不兼容)。升级走 `bump-cocos` + 逐工程编辑器迁移,详见 spec §8。
|
||||
- 子游戏私有内容只放 `games/<name>/assets/game/`;协议一律引用 `docs/protocol/`(spec §0.1)。
|
||||
- **皮肤在构建期合成**,不是运行时覆盖。子游戏把同名替换资源放 `assets/game/override/`,
|
||||
路径须与 `framework/ui/` 逐字对应;只换内容、不带 `.meta`(尺寸须一致),确需改尺寸才走
|
||||
带 meta 的例外通道。详见 spec `2026-08-27-ui-asset-and-skin-design.md`。
|
||||
- **框架真源全程只读**:合成只发生在 `build-workspace/` 的临时工程副本里。
|
||||
- **发布链路不依赖 junction**:`build-game` 直接从 `YouleNexus/assets/framework` 拷贝真源,
|
||||
CI clone 后无需先跑 `setup-links`。
|
||||
- 构建需环境变量 `COCOS_CREATOR` 指向 Cocos Creator 可执行文件(唯一来源,勿在脚本硬编码)。
|
||||
|
||||
## ⚠️ 删除子游戏工程的注意事项
|
||||
子游戏的 `assets/framework` 是指向框架真源的 junction。**不要用 `rm -rf` 删除整个子游戏工程**——git-bash 的 `rm -rf` 可能穿透 junction 误删框架真源。请用 Node 安全删除(对 junction 不穿透):
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env node
|
||||
import { join } from 'node:path';
|
||||
import { existsSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { HOST_PROJECT, GAMES_DIR, FRAMEWORK_SRC, BUILD_WORKSPACE, readCreatorVersion } from './lib/paths.mjs';
|
||||
import { materialize, composeSkin, SkinValidationError } from './lib/materialize.mjs';
|
||||
|
||||
/** 生产用构建实现:调 Cocos CLI。路径只从环境变量取,缺失即显式报错(第二准则)。 */
|
||||
function runCocosBuild(projectDir, platform) {
|
||||
const exe = process.env.COCOS_CREATOR;
|
||||
if (!exe) {
|
||||
throw new Error(
|
||||
'未设置环境变量 COCOS_CREATOR(Cocos Creator 可执行文件路径)。' +
|
||||
'这是构建的唯一来源,请在环境中配置,不要在脚本里硬编码。',
|
||||
);
|
||||
}
|
||||
execFileSync(exe, ['--project', projectDir, '--build', `platform=${platform}`], { stdio: 'inherit' });
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建一款子游戏:版本校验 → 实体化 → 皮肤合成 → 构建 → 写 build-info。
|
||||
* opts 可注入路径与 runBuild 用于测试。
|
||||
*/
|
||||
export function buildGame(name, opts = {}) {
|
||||
const gamesDir = opts.gamesDir ?? GAMES_DIR;
|
||||
const frameworkSrc = opts.frameworkSrc ?? FRAMEWORK_SRC;
|
||||
const hostProject = opts.hostProject ?? HOST_PROJECT;
|
||||
const workspace = opts.workspace ?? BUILD_WORKSPACE;
|
||||
const targetPlatform = opts.platform ?? 'android';
|
||||
const runBuild = opts.runBuild ?? runCocosBuild;
|
||||
|
||||
const gameDir = join(gamesDir, name);
|
||||
if (!existsSync(gameDir)) throw new Error(`找不到子游戏工程:${gameDir}`);
|
||||
|
||||
// 1) 引擎版本一致性(junction 共享资源,版本分叉会损坏真源)
|
||||
const expected = readCreatorVersion(hostProject);
|
||||
const actual = readCreatorVersion(gameDir);
|
||||
if (actual !== expected) {
|
||||
throw new Error(`引擎版本不一致:${name} 为 ${actual},宿主为 ${expected}`);
|
||||
}
|
||||
|
||||
// 2) 实体化到干净的工作区
|
||||
const workspaceDir = join(workspace, name);
|
||||
rmSync(workspaceDir, { recursive: true, force: true });
|
||||
materialize(gameDir, frameworkSrc, workspaceDir);
|
||||
|
||||
// 3) 皮肤合成(校验不过会抛 SkinValidationError,构建不会被调用)
|
||||
const skin = composeSkin(workspaceDir);
|
||||
|
||||
// 4) 构建
|
||||
runBuild(workspaceDir, targetPlatform);
|
||||
|
||||
// 5) 产物追溯
|
||||
const buildInfo = {
|
||||
game: name,
|
||||
frameworkCommit: opts.frameworkCommit ?? readFrameworkCommit(),
|
||||
builtAt: opts.now ?? new Date().toISOString(),
|
||||
platform: targetPlatform,
|
||||
skin: { matched: skin.matched.length, frameworkTotal: skin.frameworkTotal },
|
||||
};
|
||||
writeFileSync(join(workspaceDir, 'build-info.json'), `${JSON.stringify(buildInfo, null, 2)}\n`);
|
||||
|
||||
return { workspaceDir, skin: buildInfo.skin, buildInfo };
|
||||
}
|
||||
|
||||
function readFrameworkCommit() {
|
||||
try {
|
||||
return execFileSync('git', ['rev-parse', '--short', 'HEAD'], { encoding: 'utf8' }).trim();
|
||||
} catch {
|
||||
return null; // 非 git 环境:如实记 null,不编造
|
||||
}
|
||||
}
|
||||
|
||||
// CLI 入口(argv[1] 守卫与仓库其余脚本一致:无脚本名时不进 CLI 分支)
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
const name = process.argv[2];
|
||||
if (!name) {
|
||||
console.error('用法: npm run build-game <game> [-- --platform android]');
|
||||
process.exit(1);
|
||||
}
|
||||
const pIdx = process.argv.indexOf('--platform');
|
||||
let targetPlatform;
|
||||
if (pIdx > -1) {
|
||||
const val = process.argv[pIdx + 1];
|
||||
// 用户明确写了 --platform 却没给值(或紧跟下一个 flag):显式报错,
|
||||
// 不能落到 buildGame 的 `?? 'android'` 默认值——那会把"用户指定"悄悄变成"猜的"(第二准则)。
|
||||
if (!val || val.startsWith('--')) {
|
||||
console.error('用法: npm run build-game <game> [-- --platform <platform>](--platform 后必须跟平台名,如 android)');
|
||||
process.exit(1);
|
||||
}
|
||||
targetPlatform = val;
|
||||
}
|
||||
try {
|
||||
const r = buildGame(name, targetPlatform ? { platform: targetPlatform } : {});
|
||||
console.log(`皮肤命中 ${r.skin.matched} 处,框架可覆盖资源共 ${r.skin.frameworkTotal} 个`);
|
||||
console.log(`构建完成:${r.workspaceDir}`);
|
||||
} catch (e) {
|
||||
if (e instanceof SkinValidationError) {
|
||||
console.error(`\n${e.message}(子游戏 ${name}):`);
|
||||
for (const err of e.errors) console.error(` [${err.code}] ${err.path}\n ${err.message}`);
|
||||
} else {
|
||||
console.error(e.message);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env node
|
||||
import { join } from 'node:path';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { FRAMEWORK_UI_DIR, GAMES_DIR, gameOverrideDir } from './lib/paths.mjs';
|
||||
import { listCoverableAssets, validateOverrides } from './lib/skin.mjs';
|
||||
|
||||
/** 校验某子游戏的皮肤覆盖。opts 可注入路径用于测试。 */
|
||||
export function checkSkin(gameName, opts = {}) {
|
||||
const gamesDir = opts.gamesDir ?? GAMES_DIR;
|
||||
const frameworkUiDir = opts.frameworkUiDir ?? FRAMEWORK_UI_DIR;
|
||||
|
||||
const projectDir = join(gamesDir, gameName);
|
||||
if (!existsSync(projectDir)) {
|
||||
throw new Error(`找不到子游戏工程:${projectDir}`);
|
||||
}
|
||||
// override 目录不存在 = 该子游戏不换肤,合法
|
||||
const { matched, errors } = validateOverrides(frameworkUiDir, gameOverrideDir(projectDir));
|
||||
return { matched, errors, frameworkTotal: listCoverableAssets(frameworkUiDir).length };
|
||||
}
|
||||
|
||||
// CLI 入口:有错误则退出码 1
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
const name = process.argv[2];
|
||||
if (!name) {
|
||||
console.error('用法: npm run check-skin <game>');
|
||||
process.exit(1);
|
||||
}
|
||||
const { matched, errors, frameworkTotal } = checkSkin(name);
|
||||
console.log(`皮肤命中 ${matched.length} 处,框架可覆盖资源共 ${frameworkTotal} 个`);
|
||||
if (errors.length === 0) {
|
||||
console.log('OK: override 全部合法。');
|
||||
process.exit(0);
|
||||
}
|
||||
console.error(`\n发现 ${errors.length} 处问题(子游戏 ${name}):`);
|
||||
for (const e of errors) {
|
||||
console.error(` [${e.code}] ${e.path}\n ${e.message}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import { cpSync, mkdirSync, existsSync, readFileSync, writeFileSync, copyFileSync } from 'node:fs';
|
||||
import { join, posix, dirname, relative, sep, resolve, basename } from 'node:path';
|
||||
import { listCoverableAssets, validateOverrides } from './skin.mjs';
|
||||
import { gameOverrideDir, frameworkUiDirOf } from './paths.mjs';
|
||||
|
||||
/**
|
||||
* 工程缓存目录:实体化时跳过。
|
||||
* **只匹配工程根的直接子目录**——绝不可按目录名在任意层级匹配,否则会连
|
||||
* `assets/game/native/` 这类业务目录一起吞掉(与 .gitignore 无锚点模式同一类错误,见 Task 1)。
|
||||
*/
|
||||
const CACHE_DIRS = new Set(['library', 'temp', 'local', 'build', 'native', 'profiles', 'node_modules']);
|
||||
|
||||
export class SkinValidationError extends Error {
|
||||
constructor(errors) {
|
||||
super(`皮肤覆盖校验未通过(${errors.length} 处问题)`);
|
||||
this.name = 'SkinValidationError';
|
||||
this.errors = errors;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 把子游戏工程实体化到 destDir:
|
||||
* `assets/framework` 由 junction 变成对 frameworkSrc 的真实目录拷贝;跳过缓存目录。
|
||||
* **frameworkSrc 全程只读**(spec §5.1 ①)。
|
||||
*/
|
||||
export function materialize(gameDir, frameworkSrc, destDir) {
|
||||
if (existsSync(destDir)) throw new Error(`目标已存在:${destDir}`);
|
||||
mkdirSync(destDir, { recursive: true });
|
||||
|
||||
// 1) 拷工程本体,跳过缓存目录与 assets/framework(后者单独从真源拷)
|
||||
const frameworkRel = join('assets', 'framework');
|
||||
const gameDirNorm = resolve(gameDir);
|
||||
cpSync(gameDir, destDir, {
|
||||
recursive: true,
|
||||
dereference: true,
|
||||
filter: (src) => {
|
||||
// 处理 Windows UNC 长路径前缀 (\\?\):cpSync 传入的 src 在 Windows 上
|
||||
// 每一层都带该前缀,若不归一化,下面任何基于 rel 的比较都会恒假
|
||||
// (包括曾经直接 `src === frameworkLink` 的写法——那是 Critical 1 的成因)。
|
||||
const srcNorm = resolve(src.replace(/^\\\\\?\\/, ''));
|
||||
const rel = relative(gameDirNorm, srcNorm);
|
||||
if (!rel) return true; // gameDir 自身
|
||||
if (rel === frameworkRel) return false; // junction:单独从真源拷贝,不经此路径解引用
|
||||
return !CACHE_DIRS.has(rel.split(sep)[0]); // 只看第一段:锚定到工程根
|
||||
},
|
||||
});
|
||||
|
||||
// 2) 从真源实体化 framework(不依赖 junction 存在,spec §5.1 ②)
|
||||
cpSync(frameworkSrc, join(destDir, 'assets', 'framework'), { recursive: true, dereference: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* 把临时工程内的 `assets/game/override/**` 合成到 `assets/framework/ui/**`。
|
||||
* 先整体校验,不通过则抛错且**不写任何文件**(避免半成品)。
|
||||
*/
|
||||
export function composeSkin(destDir) {
|
||||
// 路径推导唯一权威在 paths.mjs(check-skin 用同一套),composeSkin 不再自己拼一份地图
|
||||
const frameworkUiDir = frameworkUiDirOf(destDir);
|
||||
const overrideDir = gameOverrideDir(destDir);
|
||||
const gameName = basename(destDir);
|
||||
|
||||
const { matched, errors } = validateOverrides(frameworkUiDir, overrideDir);
|
||||
if (errors.length) throw new SkinValidationError(errors);
|
||||
|
||||
for (const rel of matched) {
|
||||
const parts = rel.split(posix.sep);
|
||||
const from = join(overrideDir, ...parts);
|
||||
const to = join(frameworkUiDir, ...parts);
|
||||
mkdirSync(dirname(to), { recursive: true });
|
||||
copyFileSync(from, to);
|
||||
|
||||
// 例外通道:override 自带 meta 时,把 uuid 改回框架原值以保持引用
|
||||
const ovMeta = `${from}.meta`;
|
||||
const fwMeta = `${to}.meta`;
|
||||
if (existsSync(ovMeta) && existsSync(fwMeta)) {
|
||||
const fwUuid = JSON.parse(readFileSync(fwMeta, 'utf8')).uuid;
|
||||
if (!fwUuid) {
|
||||
// 静默通过会写出没有 uuid 的 .meta——Cocos 重新生成 UUID,框架 Prefab 对该
|
||||
// 资源的引用在发布包里断裂,这正是"保留 .meta"整个设计要防的唯一失效模式。
|
||||
throw new SkinValidationError([{
|
||||
path: rel,
|
||||
code: 'meta-missing-uuid',
|
||||
message: `${gameName}: 框架侧 .meta 没有 uuid(${fwMeta}),无法为例外通道保留引用,请先修复框架该文件的 .meta`,
|
||||
}]);
|
||||
}
|
||||
const merged = JSON.parse(readFileSync(ovMeta, 'utf8'));
|
||||
merged.uuid = fwUuid;
|
||||
writeFileSync(fwMeta, `${JSON.stringify(merged, null, 2)}\n`);
|
||||
} else if (existsSync(ovMeta) && !existsSync(fwMeta)) {
|
||||
// 框架侧没有 .meta 时整段合并被跳过,override 自带的 meta 会被无声丢弃——
|
||||
// 同样违反"下游不兜底":必须显式报错,而不是悄悄忽略 override 的意图。
|
||||
throw new SkinValidationError([{
|
||||
path: rel,
|
||||
code: 'meta-orphan',
|
||||
message: `${gameName}: override 提供了 ${rel}.meta,但框架侧没有对应的 .meta(${fwMeta}),无法合并 uuid(例外通道要求框架侧资源本身已有 .meta)`,
|
||||
}]);
|
||||
}
|
||||
}
|
||||
|
||||
return { matched, frameworkTotal: listCoverableAssets(frameworkUiDir).length };
|
||||
}
|
||||
@@ -8,6 +8,20 @@ export const HOST_PROJECT = join(ROOT, 'YouleNexus');
|
||||
export const FRAMEWORK_SRC = join(HOST_PROJECT, 'assets', 'framework');
|
||||
export const GAMES_DIR = join(ROOT, 'games');
|
||||
export const SEED_DIR = join(ROOT, 'templates', 'game-seed');
|
||||
export const BUILD_WORKSPACE = join(ROOT, 'build-workspace');
|
||||
|
||||
// 子游戏的皮肤覆盖目录
|
||||
export function gameOverrideDir(projectDir) {
|
||||
return join(projectDir, 'assets', 'game', 'override');
|
||||
}
|
||||
|
||||
// 某工程(宿主真源,或实体化后的临时工作区)的 framework/ui 目录。
|
||||
// check-skin 与 build-game(composeSkin) 共用同一份推导,不各自拼路径(唯一权威)。
|
||||
export function frameworkUiDirOf(projectDir) {
|
||||
return join(projectDir, 'assets', 'framework', 'ui');
|
||||
}
|
||||
|
||||
export const FRAMEWORK_UI_DIR = frameworkUiDirOf(HOST_PROJECT);
|
||||
|
||||
export function readCreatorVersion(projectDir) {
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import { readdirSync, statSync, readFileSync, existsSync } from 'node:fs';
|
||||
import { join, posix, sep, extname, relative } from 'node:path';
|
||||
|
||||
const IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.webp']);
|
||||
const SPINE_ROOT = 'spine'; // framework/ui/spine/<资源名>/ 为一套 Spine
|
||||
const SKIPPED = new Set(['.gitkeep']);
|
||||
|
||||
/**
|
||||
* 可被子游戏覆盖的框架资源目录(spec §2 规则 3 枚举的 8 个目录)。
|
||||
* `theme/` 不在其中——那是 §4.2 规定的框架 TS 代码位置,不是资源,不可覆盖。
|
||||
*/
|
||||
const COVERABLE_DIRS = new Set([
|
||||
'atlas-common', 'atlas-login', 'atlas-hall', 'atlas-room',
|
||||
'standalone', 'spine', 'audio', 'font',
|
||||
]);
|
||||
|
||||
/** 列出目录下所有资源的相对路径(正斜杠),排除 .meta 与 .gitkeep。 */
|
||||
export function listAssets(dir) {
|
||||
if (!existsSync(dir)) return [];
|
||||
const out = [];
|
||||
(function walk(cur) {
|
||||
for (const name of readdirSync(cur)) {
|
||||
const p = join(cur, name);
|
||||
if (statSync(p).isDirectory()) { walk(p); continue; }
|
||||
if (name.endsWith('.meta') || SKIPPED.has(name)) continue;
|
||||
out.push(relative(dir, p).split(sep).join(posix.sep));
|
||||
}
|
||||
})(dir);
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出框架 UI 目录下**可被覆盖**的资源(白名单目录,见 COVERABLE_DIRS)。
|
||||
* `frameworkUiDir` 下还有 `theme/`、`README.md` 等非资源内容,
|
||||
* 不属于可覆盖范围,也不计入"框架可覆盖资源共 N 个"的统计。
|
||||
*/
|
||||
export function listCoverableAssets(frameworkUiDir) {
|
||||
return listAssets(frameworkUiDir).filter((rel) => COVERABLE_DIRS.has(rel.split(posix.sep)[0]));
|
||||
}
|
||||
|
||||
/** 读 PNG 宽高;非 PNG 或读取失败返回 null。 */
|
||||
export function readPngSize(file) {
|
||||
try {
|
||||
const b = readFileSync(file);
|
||||
if (b.length < 24 || b.toString('ascii', 1, 4) !== 'PNG') return null;
|
||||
return { width: b.readUInt32BE(16), height: b.readUInt32BE(20) };
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
const isImage = (rel) => IMAGE_EXTS.has(extname(rel).toLowerCase());
|
||||
const isSpine = (rel) => rel.split(posix.sep)[0] === SPINE_ROOT;
|
||||
/** Spine 资源的分组键:spine/<资源名> */
|
||||
const spineGroup = (rel) => rel.split(posix.sep).slice(0, 2).join(posix.sep);
|
||||
|
||||
/**
|
||||
* 校验 override 是否合法。规则见 spec §3.2。
|
||||
* 返回 { matched: 相对路径[], errors: {path, code, message}[] }。
|
||||
*/
|
||||
export function validateOverrides(frameworkUiDir, overrideDir) {
|
||||
const fwList = listCoverableAssets(frameworkUiDir);
|
||||
const fwSet = new Set(fwList);
|
||||
const ovList = listAssets(overrideDir);
|
||||
const ovSet = new Set(ovList);
|
||||
|
||||
const matched = [];
|
||||
const errors = [];
|
||||
|
||||
// 1) 逐个 override 校验:孤儿 / 尺寸
|
||||
for (const rel of ovList) {
|
||||
if (!fwSet.has(rel)) {
|
||||
const base = rel.slice(0, rel.length - extname(rel).length);
|
||||
const alt = fwList.find((f) => f.slice(0, f.length - extname(f).length) === base);
|
||||
errors.push({
|
||||
path: rel,
|
||||
code: 'orphan',
|
||||
message: alt
|
||||
? `框架里没有 ${rel},但存在同名的 ${alt}——扩展名对不上`
|
||||
: `框架里没有对应资源:${rel}`,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Spine 贴图豁免尺寸校验(区域坐标在 .atlas 内,整套替换时自洽)
|
||||
const metaPath = join(overrideDir, ...`${rel}.meta`.split(posix.sep));
|
||||
const needSizeCheck =
|
||||
isImage(rel) && !isSpine(rel) && !existsSync(metaPath);
|
||||
if (needSizeCheck) {
|
||||
const a = readPngSize(join(frameworkUiDir, ...rel.split(posix.sep)));
|
||||
const b = readPngSize(join(overrideDir, ...rel.split(posix.sep)));
|
||||
if (!a || !b) {
|
||||
const side = !a ? '框架' : 'override';
|
||||
errors.push({
|
||||
path: rel,
|
||||
code: 'size-unreadable',
|
||||
message:
|
||||
`框架 UI 图片必须是 PNG(无法读取尺寸:${side})。` +
|
||||
`请改用 PNG,或连 .meta 一起提供(例外通道)`,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (a.width !== b.width || a.height !== b.height) {
|
||||
errors.push({
|
||||
path: rel,
|
||||
code: 'size-mismatch',
|
||||
message:
|
||||
`尺寸不一致:框架 ${a.width}x${a.height},override ${b.width}x${b.height}。` +
|
||||
`请改为同尺寸,或连 .meta 一起提供(例外通道)`,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
matched.push(rel);
|
||||
}
|
||||
|
||||
// 2) Spine 整套完整性:override 动了某套,就必须整套都在
|
||||
const touchedGroups = new Set(ovList.filter(isSpine).map(spineGroup));
|
||||
for (const g of touchedGroups) {
|
||||
const required = fwList.filter((f) => isSpine(f) && spineGroup(f) === g);
|
||||
const missing = required.filter((f) => !ovSet.has(f));
|
||||
if (missing.length) {
|
||||
errors.push({
|
||||
path: g,
|
||||
code: 'spine-incomplete',
|
||||
message: `Spine 必须整套覆盖,缺少:${missing.join('、')}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return { matched, errors };
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { join } from 'node:path';
|
||||
import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync, mkdtempSync, symlinkSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { platform } from 'node:process';
|
||||
import { deflateSync } from 'node:zlib';
|
||||
import { buildGame } from '../build-game.mjs';
|
||||
|
||||
const LINK_TYPE = platform === 'win32' ? 'junction' : 'dir';
|
||||
|
||||
function crc32(buf) {
|
||||
let c = ~0;
|
||||
for (const x of buf) { c ^= x; for (let i = 0; i < 8; i++) c = (c >>> 1) ^ (0xEDB88320 & -(c & 1)); }
|
||||
return ~c >>> 0;
|
||||
}
|
||||
function chunk(type, data) {
|
||||
const len = Buffer.alloc(4); len.writeUInt32BE(data.length);
|
||||
const td = Buffer.concat([Buffer.from(type, 'ascii'), data]);
|
||||
const c = Buffer.alloc(4); c.writeUInt32BE(crc32(td));
|
||||
return Buffer.concat([len, td, c]);
|
||||
}
|
||||
function makePng(w, h, rgb = [0, 0, 0]) {
|
||||
const ihdr = Buffer.alloc(13);
|
||||
ihdr.writeUInt32BE(w, 0); ihdr.writeUInt32BE(h, 4); ihdr[8] = 8; ihdr[9] = 2;
|
||||
const raw = Buffer.alloc(h * (1 + w * 3));
|
||||
for (let y = 0; y < h; y++) {
|
||||
const off = y * (1 + w * 3);
|
||||
raw[off] = 0;
|
||||
for (let x = 0; x < w; x++) {
|
||||
raw[off + 1 + x * 3] = rgb[0]; raw[off + 2 + x * 3] = rgb[1]; raw[off + 3 + x * 3] = rgb[2];
|
||||
}
|
||||
}
|
||||
return Buffer.concat([
|
||||
Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]),
|
||||
chunk('IHDR', ihdr), chunk('IDAT', deflateSync(raw)), chunk('IEND', Buffer.alloc(0)),
|
||||
]);
|
||||
}
|
||||
|
||||
const DEFAULT_BTN = makePng(64, 64, [1, 2, 3]);
|
||||
const SKINNED = makePng(64, 64, [9, 9, 9]);
|
||||
|
||||
function put(dir, rel, content) {
|
||||
const p = join(dir, ...rel.split('/'));
|
||||
mkdirSync(join(p, '..'), { recursive: true });
|
||||
writeFileSync(p, content);
|
||||
}
|
||||
|
||||
function scaffold(gameVersion = '3.8.8') {
|
||||
const root = mkdtempSync(join(tmpdir(), 'youle-build-'));
|
||||
const hostProject = join(root, 'nexus');
|
||||
const frameworkSrc = join(hostProject, 'assets', 'framework');
|
||||
mkdirSync(frameworkSrc, { recursive: true });
|
||||
writeFileSync(join(hostProject, 'package.json'), JSON.stringify({ name: 'YouleNexus', creator: { version: '3.8.8' } }));
|
||||
put(frameworkSrc, 'ui/atlas-hall/btn.png', DEFAULT_BTN);
|
||||
|
||||
// junction 故意指向一个诱饵目录而非 frameworkSrc 自身,与 materialize.test.mjs 同理:
|
||||
// 否则"经 junction 拷来的字节"与"从真源拷来的字节"完全相同,测不出 Critical 1 那类 bug。
|
||||
const frameworkDecoy = join(root, 'decoy', 'assets', 'framework');
|
||||
mkdirSync(frameworkDecoy, { recursive: true });
|
||||
put(frameworkDecoy, 'ui/atlas-hall/btn.png', DEFAULT_BTN);
|
||||
put(frameworkDecoy, 'ui/atlas-hall/__decoy_marker__.png', DEFAULT_BTN);
|
||||
|
||||
const gamesDir = join(root, 'games');
|
||||
const gameDir = join(gamesDir, 'demo');
|
||||
mkdirSync(join(gameDir, 'assets'), { recursive: true });
|
||||
writeFileSync(join(gameDir, 'package.json'), JSON.stringify({ name: 'demo', creator: { version: gameVersion } }));
|
||||
symlinkSync(frameworkDecoy, join(gameDir, 'assets', 'framework'), LINK_TYPE);
|
||||
|
||||
return { root, hostProject, frameworkSrc, frameworkDecoy, gamesDir, gameDir, workspace: join(root, 'build-workspace') };
|
||||
}
|
||||
function cleanup(root) { rmSync(root, { recursive: true, force: true }); }
|
||||
|
||||
function baseOpts(s, extra = {}) {
|
||||
return {
|
||||
gamesDir: s.gamesDir, frameworkSrc: s.frameworkSrc, hostProject: s.hostProject,
|
||||
workspace: s.workspace, platform: 'web-mobile',
|
||||
frameworkCommit: 'abc1234', now: '2026-08-27T00:00:00.000Z',
|
||||
runBuild: () => {},
|
||||
...extra,
|
||||
};
|
||||
}
|
||||
|
||||
test('buildGame 走通全流程:实体化 → 合成 → 调构建 → 写 build-info', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
put(s.gameDir, 'assets/game/override/atlas-hall/btn.png', SKINNED);
|
||||
const calls = [];
|
||||
const r = buildGame('demo', baseOpts(s, { runBuild: (dir, p) => calls.push([dir, p]) }));
|
||||
|
||||
assert.ok(readFileSync(join(r.workspaceDir, 'assets', 'framework', 'ui', 'atlas-hall', 'btn.png')).equals(SKINNED));
|
||||
assert.ok(readFileSync(join(s.frameworkSrc, 'ui', 'atlas-hall', 'btn.png')).equals(DEFAULT_BTN), '框架真源必须只读:合成只能发生在临时工作区');
|
||||
assert.ok(!existsSync(join(r.workspaceDir, 'assets', 'framework', 'ui', 'atlas-hall', '__decoy_marker__.png')),
|
||||
'framework 必须来自 frameworkSrc 真源,不能残留 junction 诱饵目标独有的文件(Critical 1)');
|
||||
assert.equal(calls.length, 1);
|
||||
assert.equal(calls[0][0], r.workspaceDir);
|
||||
assert.equal(calls[0][1], 'web-mobile');
|
||||
|
||||
const info = JSON.parse(readFileSync(join(r.workspaceDir, 'build-info.json'), 'utf8'));
|
||||
assert.equal(info.game, 'demo');
|
||||
assert.equal(info.frameworkCommit, 'abc1234');
|
||||
assert.equal(info.platform, 'web-mobile');
|
||||
assert.deepEqual(info.skin, { matched: 1, frameworkTotal: 1 });
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
|
||||
test('buildGame 在引擎版本不一致时中止,且不调用构建', () => {
|
||||
const s = scaffold('3.8.6');
|
||||
try {
|
||||
let called = false;
|
||||
assert.throws(
|
||||
() => buildGame('demo', baseOpts(s, { runBuild: () => { called = true; } })),
|
||||
/引擎版本不一致/,
|
||||
);
|
||||
assert.equal(called, false);
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
|
||||
test('buildGame 在皮肤校验失败时中止,且不调用构建', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
put(s.gameDir, 'assets/game/override/atlas-hall/typo.png', 'X');
|
||||
let called = false;
|
||||
assert.throws(
|
||||
() => buildGame('demo', baseOpts(s, { runBuild: () => { called = true; } })),
|
||||
/皮肤覆盖校验未通过/,
|
||||
);
|
||||
assert.equal(called, false);
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
|
||||
test('buildGame 重复构建:清掉旧工作区后重建(幂等)', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
const opts = baseOpts(s);
|
||||
buildGame('demo', opts);
|
||||
const r2 = buildGame('demo', opts); // 不应因目标已存在而抛错
|
||||
assert.ok(existsSync(join(r2.workspaceDir, 'build-info.json')));
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
|
||||
test('buildGame 对不存在的子游戏抛错', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
assert.throws(() => buildGame('nosuch', baseOpts(s)), /找不到子游戏工程/);
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
@@ -0,0 +1,85 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { join } from 'node:path';
|
||||
import { mkdirSync, writeFileSync, rmSync, mkdtempSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { deflateSync } from 'node:zlib';
|
||||
import { checkSkin } from '../check-skin.mjs';
|
||||
|
||||
function crc32(buf) {
|
||||
let c = ~0;
|
||||
for (const x of buf) { c ^= x; for (let i = 0; i < 8; i++) c = (c >>> 1) ^ (0xEDB88320 & -(c & 1)); }
|
||||
return ~c >>> 0;
|
||||
}
|
||||
function chunk(type, data) {
|
||||
const len = Buffer.alloc(4); len.writeUInt32BE(data.length);
|
||||
const td = Buffer.concat([Buffer.from(type, 'ascii'), data]);
|
||||
const c = Buffer.alloc(4); c.writeUInt32BE(crc32(td));
|
||||
return Buffer.concat([len, td, c]);
|
||||
}
|
||||
function makePng(w, h) {
|
||||
const ihdr = Buffer.alloc(13);
|
||||
ihdr.writeUInt32BE(w, 0); ihdr.writeUInt32BE(h, 4); ihdr[8] = 8; ihdr[9] = 2;
|
||||
const raw = Buffer.alloc(h * (1 + w * 3));
|
||||
return Buffer.concat([
|
||||
Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]),
|
||||
chunk('IHDR', ihdr), chunk('IDAT', deflateSync(raw)), chunk('IEND', Buffer.alloc(0)),
|
||||
]);
|
||||
}
|
||||
function put(dir, rel, content) {
|
||||
const p = join(dir, ...rel.split('/'));
|
||||
mkdirSync(join(p, '..'), { recursive: true });
|
||||
writeFileSync(p, content);
|
||||
}
|
||||
|
||||
function scaffold() {
|
||||
const root = mkdtempSync(join(tmpdir(), 'youle-checkskin-'));
|
||||
const frameworkUiDir = join(root, 'ui');
|
||||
const gamesDir = join(root, 'games');
|
||||
mkdirSync(frameworkUiDir, { recursive: true });
|
||||
mkdirSync(join(gamesDir, 'demo', 'assets', 'game', 'override'), { recursive: true });
|
||||
put(frameworkUiDir, 'atlas-hall/btn.png', makePng(64, 64));
|
||||
put(frameworkUiDir, 'atlas-hall/bg.png', makePng(64, 64));
|
||||
return { root, frameworkUiDir, gamesDir, overrideDir: join(gamesDir, 'demo', 'assets', 'game', 'override') };
|
||||
}
|
||||
|
||||
test('checkSkin 统计命中数与框架资源总数', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
put(s.overrideDir, 'atlas-hall/btn.png', makePng(64, 64));
|
||||
const r = checkSkin('demo', { gamesDir: s.gamesDir, frameworkUiDir: s.frameworkUiDir });
|
||||
assert.deepEqual(r.errors, []);
|
||||
assert.deepEqual(r.matched, ['atlas-hall/btn.png']);
|
||||
assert.equal(r.frameworkTotal, 2);
|
||||
} finally { rmSync(s.root, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
test('checkSkin 报出孤儿 override', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
put(s.overrideDir, 'atlas-hall/nope.png', makePng(64, 64));
|
||||
const r = checkSkin('demo', { gamesDir: s.gamesDir, frameworkUiDir: s.frameworkUiDir });
|
||||
assert.equal(r.errors.length, 1);
|
||||
assert.equal(r.errors[0].code, 'orphan');
|
||||
} finally { rmSync(s.root, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
test('checkSkin 对不存在的子游戏抛错', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
assert.throws(
|
||||
() => checkSkin('nosuch', { gamesDir: s.gamesDir, frameworkUiDir: s.frameworkUiDir }),
|
||||
/找不到子游戏工程/,
|
||||
);
|
||||
} finally { rmSync(s.root, { recursive: true, force: true }); }
|
||||
});
|
||||
|
||||
test('checkSkin 对没有 override 目录的子游戏返回零命中而非报错', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
rmSync(s.overrideDir, { recursive: true, force: true });
|
||||
const r = checkSkin('demo', { gamesDir: s.gamesDir, frameworkUiDir: s.frameworkUiDir });
|
||||
assert.deepEqual(r.matched, []);
|
||||
assert.deepEqual(r.errors, []);
|
||||
} finally { rmSync(s.root, { recursive: true, force: true }); }
|
||||
});
|
||||
@@ -5,7 +5,10 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
|
||||
const scriptsDir = join(dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const CLI = ['setup-links.mjs', 'new-game.mjs', 'check-cocos-version.mjs', 'bump-cocos.mjs'];
|
||||
const CLI = [
|
||||
'setup-links.mjs', 'new-game.mjs', 'check-cocos-version.mjs', 'bump-cocos.mjs',
|
||||
'check-skin.mjs', 'build-game.mjs',
|
||||
];
|
||||
|
||||
for (const name of CLI) {
|
||||
test(`${name} 被动态 import 时不崩溃(argv[1] undefined)`, () => {
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { join } from 'node:path';
|
||||
import { mkdirSync, writeFileSync, readFileSync, existsSync, rmSync, mkdtempSync, symlinkSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { platform } from 'node:process';
|
||||
import { deflateSync } from 'node:zlib';
|
||||
import { materialize, composeSkin, SkinValidationError } from '../lib/materialize.mjs';
|
||||
|
||||
const LINK_TYPE = platform === 'win32' ? 'junction' : 'dir';
|
||||
|
||||
function crc32(buf) {
|
||||
let c = ~0;
|
||||
for (const x of buf) { c ^= x; for (let i = 0; i < 8; i++) c = (c >>> 1) ^ (0xEDB88320 & -(c & 1)); }
|
||||
return ~c >>> 0;
|
||||
}
|
||||
function chunk(type, data) {
|
||||
const len = Buffer.alloc(4); len.writeUInt32BE(data.length);
|
||||
const td = Buffer.concat([Buffer.from(type, 'ascii'), data]);
|
||||
const c = Buffer.alloc(4); c.writeUInt32BE(crc32(td));
|
||||
return Buffer.concat([len, td, c]);
|
||||
}
|
||||
function makePng(w, h, rgb = [0, 0, 0]) {
|
||||
const ihdr = Buffer.alloc(13);
|
||||
ihdr.writeUInt32BE(w, 0); ihdr.writeUInt32BE(h, 4); ihdr[8] = 8; ihdr[9] = 2;
|
||||
const raw = Buffer.alloc(h * (1 + w * 3));
|
||||
for (let y = 0; y < h; y++) {
|
||||
const off = y * (1 + w * 3);
|
||||
raw[off] = 0;
|
||||
for (let x = 0; x < w; x++) {
|
||||
raw[off + 1 + x * 3] = rgb[0]; raw[off + 2 + x * 3] = rgb[1]; raw[off + 3 + x * 3] = rgb[2];
|
||||
}
|
||||
}
|
||||
return Buffer.concat([
|
||||
Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]),
|
||||
chunk('IHDR', ihdr), chunk('IDAT', deflateSync(raw)), chunk('IEND', Buffer.alloc(0)),
|
||||
]);
|
||||
}
|
||||
|
||||
const DEFAULT_BTN = makePng(64, 64, [1, 2, 3]);
|
||||
const DEFAULT_BG = makePng(64, 64, [4, 5, 6]);
|
||||
const SKINNED_BTN = makePng(64, 64, [9, 9, 9]);
|
||||
|
||||
function put(dir, rel, content) {
|
||||
const p = join(dir, ...rel.split('/'));
|
||||
mkdirSync(join(p, '..'), { recursive: true });
|
||||
writeFileSync(p, content);
|
||||
}
|
||||
function read(dir, rel) { return readFileSync(join(dir, ...rel.split('/')), 'utf8'); }
|
||||
function readBinary(dir, rel) { return readFileSync(join(dir, ...rel.split('/'))); }
|
||||
|
||||
/**
|
||||
* 造一个「框架真源 + 挂 junction 的子游戏工程」的临时环境。
|
||||
*
|
||||
* junction **故意不指向 frameworkSrc 本身**,而是指向一个内容相似、但多一个
|
||||
* 标记文件的「诱饵」目录(frameworkDecoy)。这样"实体化后的 framework 到底是
|
||||
* 从 junction 解引用来的,还是从真源 frameworkSrc 单独拷的"才是可判定的——
|
||||
* 若两者是同一目录,字节完全相同,Critical 1(junction 排除分支在 Windows 上
|
||||
* 从未生效)这个 bug 永远不会被测出来。
|
||||
*/
|
||||
function scaffold() {
|
||||
const root = mkdtempSync(join(tmpdir(), 'youle-mat-'));
|
||||
const frameworkSrc = join(root, 'nexus', 'assets', 'framework');
|
||||
mkdirSync(frameworkSrc, { recursive: true });
|
||||
put(frameworkSrc, 'ui/atlas-hall/btn.png', DEFAULT_BTN);
|
||||
put(frameworkSrc, 'ui/atlas-hall/btn.png.meta', '{"uuid":"fw-btn-uuid"}');
|
||||
put(frameworkSrc, 'ui/atlas-hall/bg.png', DEFAULT_BG);
|
||||
put(frameworkSrc, 'core/net.ts', 'export const x=1;');
|
||||
|
||||
// 诱饵:junction 实际指向这里。内容与 frameworkSrc 一致,但多一个标记文件。
|
||||
const frameworkDecoy = join(root, 'decoy', 'assets', 'framework');
|
||||
mkdirSync(frameworkDecoy, { recursive: true });
|
||||
put(frameworkDecoy, 'ui/atlas-hall/btn.png', DEFAULT_BTN);
|
||||
put(frameworkDecoy, 'ui/atlas-hall/btn.png.meta', '{"uuid":"fw-btn-uuid"}');
|
||||
put(frameworkDecoy, 'ui/atlas-hall/bg.png', DEFAULT_BG);
|
||||
put(frameworkDecoy, 'core/net.ts', 'export const x=1;');
|
||||
put(frameworkDecoy, 'ui/atlas-hall/__decoy_marker__.png', DEFAULT_BG); // 只存在于诱饵里
|
||||
|
||||
const gameDir = join(root, 'games', 'demo');
|
||||
mkdirSync(join(gameDir, 'assets'), { recursive: true });
|
||||
writeFileSync(join(gameDir, 'package.json'), JSON.stringify({ name: 'demo', creator: { version: '3.8.8' } }));
|
||||
symlinkSync(frameworkDecoy, join(gameDir, 'assets', 'framework'), LINK_TYPE);
|
||||
put(gameDir, 'assets/game/theme.ts', 'export const theme={};');
|
||||
mkdirSync(join(gameDir, 'library'), { recursive: true }); // 缓存目录,应被跳过
|
||||
writeFileSync(join(gameDir, 'library', 'junk'), 'x');
|
||||
|
||||
return { root, frameworkSrc, frameworkDecoy, gameDir, dest: join(root, 'workspace', 'demo') };
|
||||
}
|
||||
function cleanup(root) { rmSync(root, { recursive: true, force: true }); }
|
||||
|
||||
test('materialize 把 junction 变成真实目录拷贝,并跳过缓存目录', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
materialize(s.gameDir, s.frameworkSrc, s.dest);
|
||||
assert.ok(readBinary(s.dest, 'assets/framework/ui/atlas-hall/btn.png').equals(DEFAULT_BTN), '框架 btn.png 被正确拷贝');
|
||||
assert.equal(read(s.dest, 'assets/game/theme.ts'), 'export const theme={};', '游戏资源被拷贝');
|
||||
assert.ok(!existsSync(join(s.dest, 'library')), 'library 应被跳过');
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
|
||||
test('materialize 只跳过工程根的缓存目录,不吞 assets 下的同名业务目录', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
put(s.gameDir, 'assets/game/native/bridge.ts', 'BRIDGE');
|
||||
materialize(s.gameDir, s.frameworkSrc, s.dest);
|
||||
assert.equal(read(s.dest, 'assets/game/native/bridge.ts'), 'BRIDGE', 'assets 下的 native 是业务目录,必须保留');
|
||||
assert.ok(!existsSync(join(s.dest, 'library')), '工程根的 library 仍应被跳过');
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
|
||||
test('materialize 后写临时工程不影响框架真源', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
materialize(s.gameDir, s.frameworkSrc, s.dest);
|
||||
writeFileSync(join(s.dest, 'assets', 'framework', 'ui', 'atlas-hall', 'btn.png'), Buffer.from('MUTATED'));
|
||||
assert.ok(readBinary(s.frameworkSrc, 'ui/atlas-hall/btn.png').equals(DEFAULT_BTN), '真源必须只读');
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
|
||||
test('materialize 的 framework 来自真源 frameworkSrc,而非 junction 解引用(诱饵标记文件必须不存在)', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
materialize(s.gameDir, s.frameworkSrc, s.dest);
|
||||
assert.ok(
|
||||
!existsSync(join(s.dest, 'assets', 'framework', 'ui', 'atlas-hall', '__decoy_marker__.png')),
|
||||
'framework 必须单独从 frameworkSrc 真源拷贝;诱饵(junction 目标)独有的标记文件不应出现在工作区里',
|
||||
);
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
|
||||
test('materialize 拒绝覆盖已存在的目标目录', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
materialize(s.gameDir, s.frameworkSrc, s.dest);
|
||||
assert.throws(() => materialize(s.gameDir, s.frameworkSrc, s.dest), /已存在/);
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
|
||||
test('composeSkin 用 override 覆盖框架资源内容,保留框架 .meta', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
put(s.gameDir, 'assets/game/override/atlas-hall/btn.png', SKINNED_BTN);
|
||||
materialize(s.gameDir, s.frameworkSrc, s.dest);
|
||||
const r = composeSkin(s.dest);
|
||||
assert.deepEqual(r.matched, ['atlas-hall/btn.png'], 'matched 返回覆盖的文件');
|
||||
assert.equal(r.frameworkTotal, 2, '框架资源总数');
|
||||
assert.ok(readBinary(s.dest, 'assets/framework/ui/atlas-hall/btn.png').equals(SKINNED_BTN), '框架文件被 override 替换');
|
||||
assert.equal(read(s.dest, 'assets/framework/ui/atlas-hall/btn.png.meta'), '{"uuid":"fw-btn-uuid"}', 'meta 文件不变');
|
||||
assert.ok(readBinary(s.dest, 'assets/framework/ui/atlas-hall/bg.png').equals(DEFAULT_BG), '未覆盖的保持默认');
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
|
||||
test('composeSkin 例外通道:override 自带 meta 时,uuid 被改回框架的值', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
put(s.gameDir, 'assets/game/override/atlas-hall/btn.png', SKINNED_BTN);
|
||||
put(s.gameDir, 'assets/game/override/atlas-hall/btn.png.meta', '{"uuid":"game-own-uuid","ver":"1.0.0"}');
|
||||
materialize(s.gameDir, s.frameworkSrc, s.dest);
|
||||
composeSkin(s.dest);
|
||||
const meta = JSON.parse(read(s.dest, 'assets/framework/ui/atlas-hall/btn.png.meta'));
|
||||
assert.equal(meta.uuid, 'fw-btn-uuid', 'uuid 必须沿用框架的');
|
||||
assert.equal(meta.ver, '1.0.0', 'override meta 的其余字段保留');
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
|
||||
test('composeSkin 例外通道:框架侧 .meta 没有 uuid 时显式抛错(不得写出没有 uuid 的 .meta)', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
// 框架 bg.png 本身没有 .meta;给它补一份没有 uuid 字段的 .meta,
|
||||
// 模拟 fwUuid === undefined 的场景(JSON.stringify 会静默丢掉 undefined 的键)
|
||||
put(s.gameDir, 'assets/game/override/atlas-hall/bg.png', SKINNED_BTN);
|
||||
put(s.gameDir, 'assets/game/override/atlas-hall/bg.png.meta', '{"ver":"1.0.0"}');
|
||||
materialize(s.gameDir, s.frameworkSrc, s.dest);
|
||||
// 框架侧补一份没有 uuid 的 .meta(模拟框架 meta 本身缺 uuid 的坏数据)
|
||||
writeFileSync(join(s.dest, 'assets', 'framework', 'ui', 'atlas-hall', 'bg.png.meta'), '{"ver":"0.9.0"}');
|
||||
|
||||
let err;
|
||||
assert.throws(() => {
|
||||
try { composeSkin(s.dest); } catch (e) { err = e; throw e; }
|
||||
}, SkinValidationError);
|
||||
assert.equal(err.errors[0].code, 'meta-missing-uuid');
|
||||
assert.match(err.errors[0].path, /bg\.png/, '错误须指向具体文件相对路径');
|
||||
assert.match(err.errors[0].message, /demo/, '错误须指向子游戏名');
|
||||
assert.match(err.errors[0].message, /uuid/, '错误须说明具体原因');
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
|
||||
test('composeSkin 例外通道:框架侧没有 .meta 时显式抛错(不得静默丢弃 override 的 meta)', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
// bg.png 在框架侧没有 .meta,但 override 给它带了一份 —— 之前的实现会静默跳过整段合并
|
||||
put(s.gameDir, 'assets/game/override/atlas-hall/bg.png', SKINNED_BTN);
|
||||
put(s.gameDir, 'assets/game/override/atlas-hall/bg.png.meta', '{"uuid":"game-own-uuid"}');
|
||||
materialize(s.gameDir, s.frameworkSrc, s.dest);
|
||||
|
||||
let err;
|
||||
assert.throws(() => {
|
||||
try { composeSkin(s.dest); } catch (e) { err = e; throw e; }
|
||||
}, SkinValidationError);
|
||||
assert.equal(err.errors[0].code, 'meta-orphan');
|
||||
assert.match(err.errors[0].path, /bg\.png/, '错误须指向具体文件相对路径');
|
||||
assert.match(err.errors[0].message, /demo/, '错误须指向子游戏名');
|
||||
assert.match(err.errors[0].message, /\.meta/, '错误须说明具体原因');
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
|
||||
test('composeSkin 校验不过时抛 SkinValidationError 且不写任何文件', () => {
|
||||
const s = scaffold();
|
||||
try {
|
||||
// 同时放入一个合法的 override 和一个孤儿 override
|
||||
put(s.gameDir, 'assets/game/override/atlas-hall/btn.png', SKINNED_BTN); // 合法:框架里有此文件
|
||||
put(s.gameDir, 'assets/game/override/atlas-hall/typo.png', DEFAULT_BG); // 孤儿:框架里无此文件
|
||||
materialize(s.gameDir, s.frameworkSrc, s.dest);
|
||||
let validationErr;
|
||||
assert.throws(() => {
|
||||
try {
|
||||
composeSkin(s.dest);
|
||||
} catch (err) {
|
||||
validationErr = err;
|
||||
throw err;
|
||||
}
|
||||
}, SkinValidationError);
|
||||
|
||||
// 验证错误中确实有 orphan 错误指向孤儿文件
|
||||
assert.ok(validationErr.errors.some(e => e.code === 'orphan' && e.path === 'atlas-hall/typo.png'), 'orphan 错误必须被检测');
|
||||
|
||||
// 验证即使合法的 btn.png 也没有被写入(校验失败时不得部分写入)
|
||||
assert.ok(readBinary(s.dest, 'assets/framework/ui/atlas-hall/btn.png').equals(DEFAULT_BTN), '校验失败时不得部分写入:btn.png 应仍为框架默认值');
|
||||
} finally { cleanup(s.root); }
|
||||
});
|
||||
@@ -1,9 +1,9 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { join } from 'node:path';
|
||||
import { join, sep } from 'node:path';
|
||||
import { writeFileSync, mkdirSync } from 'node:fs';
|
||||
import { makeTempMonorepo, makeGameProject, cleanup } from './helpers.mjs';
|
||||
import { readCreatorVersion, listGameProjects } from '../lib/paths.mjs';
|
||||
import { readCreatorVersion, listGameProjects, BUILD_WORKSPACE, GAMES_DIR } from '../lib/paths.mjs';
|
||||
|
||||
test('readCreatorVersion 读取 package.json 的 creator.version', () => {
|
||||
const mono = makeTempMonorepo();
|
||||
@@ -42,3 +42,10 @@ test('listGameProjects 只列出含 package.json 的子目录', () => {
|
||||
test('listGameProjects 对不存在的目录返回空数组', () => {
|
||||
assert.deepEqual(listGameProjects('/no/such/dir'), []);
|
||||
});
|
||||
|
||||
test('BUILD_WORKSPACE 不在 GAMES_DIR 之下(红线③:临时工程不得置于 games/ 下)', () => {
|
||||
assert.ok(
|
||||
BUILD_WORKSPACE !== GAMES_DIR && !BUILD_WORKSPACE.startsWith(GAMES_DIR + sep),
|
||||
`BUILD_WORKSPACE(${BUILD_WORKSPACE}) 不应落在 GAMES_DIR(${GAMES_DIR}) 之下`,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { join } from 'node:path';
|
||||
import { mkdirSync, writeFileSync, rmSync, mkdtempSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { deflateSync } from 'node:zlib';
|
||||
import { listAssets, listCoverableAssets, readPngSize, validateOverrides } from '../lib/skin.mjs';
|
||||
|
||||
// ---- 测试辅助:零依赖生成指定尺寸的 PNG ----
|
||||
function crc32(buf) {
|
||||
let c = ~0;
|
||||
for (const x of buf) { c ^= x; for (let i = 0; i < 8; i++) c = (c >>> 1) ^ (0xEDB88320 & -(c & 1)); }
|
||||
return ~c >>> 0;
|
||||
}
|
||||
function chunk(type, data) {
|
||||
const len = Buffer.alloc(4); len.writeUInt32BE(data.length);
|
||||
const td = Buffer.concat([Buffer.from(type, 'ascii'), data]);
|
||||
const c = Buffer.alloc(4); c.writeUInt32BE(crc32(td));
|
||||
return Buffer.concat([len, td, c]);
|
||||
}
|
||||
function makePng(w, h) {
|
||||
const ihdr = Buffer.alloc(13);
|
||||
ihdr.writeUInt32BE(w, 0); ihdr.writeUInt32BE(h, 4); ihdr[8] = 8; ihdr[9] = 2;
|
||||
const raw = Buffer.alloc(h * (1 + w * 3));
|
||||
return Buffer.concat([
|
||||
Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]),
|
||||
chunk('IHDR', ihdr), chunk('IDAT', deflateSync(raw)), chunk('IEND', Buffer.alloc(0)),
|
||||
]);
|
||||
}
|
||||
function writeFile(dir, rel, content) {
|
||||
const p = join(dir, ...rel.split('/'));
|
||||
mkdirSync(join(p, '..'), { recursive: true });
|
||||
writeFileSync(p, content);
|
||||
return p;
|
||||
}
|
||||
function png(dir, rel, w = 64, h = 64) { return writeFile(dir, rel, makePng(w, h)); }
|
||||
|
||||
function makeDirs() {
|
||||
const root = mkdtempSync(join(tmpdir(), 'youle-skin-'));
|
||||
const fw = join(root, 'ui'); const ov = join(root, 'override');
|
||||
mkdirSync(fw, { recursive: true }); mkdirSync(ov, { recursive: true });
|
||||
return { root, fw, ov };
|
||||
}
|
||||
function cleanup(root) { rmSync(root, { recursive: true, force: true }); }
|
||||
|
||||
// ---- listAssets ----
|
||||
test('listAssets 返回正斜杠相对路径,排除 .meta 与 .gitkeep', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
png(d.fw, 'atlas-hall/btn.png');
|
||||
writeFile(d.fw, 'atlas-hall/btn.png.meta', '{}');
|
||||
writeFile(d.fw, 'audio/.gitkeep', '');
|
||||
writeFile(d.fw, 'audio/click.wav', 'x');
|
||||
assert.deepEqual(listAssets(d.fw).sort(), ['atlas-hall/btn.png', 'audio/click.wav']);
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
test('listAssets 对不存在的目录返回空数组', () => {
|
||||
assert.deepEqual(listAssets('/no/such/dir'), []);
|
||||
});
|
||||
|
||||
// ---- listCoverableAssets:Important 3——可覆盖范围须限定为 spec §2 规则 3 的 8 个目录 ----
|
||||
test('listCoverableAssets 排除 theme/ 与 README.md,只保留 8 个白名单目录下的资源', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
png(d.fw, 'atlas-hall/btn.png');
|
||||
writeFile(d.fw, 'README.md', '# framework ui');
|
||||
writeFile(d.fw, 'theme/default-theme.ts', 'export const DEFAULT_THEME = {};');
|
||||
writeFile(d.fw, 'theme/types.ts', 'export interface ThemeConfig {}');
|
||||
const covered = listCoverableAssets(d.fw).sort();
|
||||
assert.deepEqual(covered, ['atlas-hall/btn.png']);
|
||||
assert.ok(!covered.some((f) => f.startsWith('theme/')), 'theme/ 下的框架代码不属于可覆盖资源');
|
||||
assert.ok(!covered.includes('README.md'), 'README.md 不是可覆盖资源');
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
test('validateOverrides:override 放一个 .ts 到 theme/ 会被判为 orphan(不在可覆盖范围内)', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
png(d.fw, 'atlas-hall/btn.png');
|
||||
writeFile(d.fw, 'theme/resolve-theme.ts', 'export function resolveTheme() {}');
|
||||
// 子游戏企图覆盖框架代码
|
||||
writeFile(d.ov, 'theme/resolve-theme.ts', 'export function resolveTheme() { /* hijacked */ }');
|
||||
const r = validateOverrides(d.fw, d.ov);
|
||||
assert.equal(r.errors.length, 1);
|
||||
assert.equal(r.errors[0].code, 'orphan');
|
||||
assert.equal(r.errors[0].path, 'theme/resolve-theme.ts');
|
||||
assert.equal(r.matched.length, 0, 'theme 覆盖不应被当作合法命中');
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
// ---- readPngSize ----
|
||||
test('readPngSize 读出宽高;非 PNG 返回 null', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
const p = png(d.fw, 'a.png', 128, 64);
|
||||
assert.deepEqual(readPngSize(p), { width: 128, height: 64 });
|
||||
const w = writeFile(d.fw, 'b.wav', 'not a png');
|
||||
assert.equal(readPngSize(w), null);
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
// ---- validateOverrides:正常命中 ----
|
||||
test('同名同尺寸图片:命中,无错误', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
png(d.fw, 'atlas-hall/btn.png', 64, 64);
|
||||
png(d.ov, 'atlas-hall/btn.png', 64, 64);
|
||||
const r = validateOverrides(d.fw, d.ov);
|
||||
assert.deepEqual(r.errors, []);
|
||||
assert.deepEqual(r.matched, ['atlas-hall/btn.png']);
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
test('框架有、子游戏未覆盖:不是错误', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
png(d.fw, 'atlas-hall/a.png');
|
||||
png(d.fw, 'atlas-hall/b.png');
|
||||
png(d.ov, 'atlas-hall/a.png');
|
||||
const r = validateOverrides(d.fw, d.ov);
|
||||
assert.deepEqual(r.errors, []);
|
||||
assert.deepEqual(r.matched, ['atlas-hall/a.png']);
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
// ---- validateOverrides:孤儿 ----
|
||||
test('override 在框架里找不到对应路径 → orphan', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
png(d.fw, 'atlas-hall/btn.png');
|
||||
png(d.ov, 'atlas-hall/typo.png');
|
||||
const r = validateOverrides(d.fw, d.ov);
|
||||
assert.equal(r.errors.length, 1);
|
||||
assert.equal(r.errors[0].code, 'orphan');
|
||||
assert.equal(r.errors[0].path, 'atlas-hall/typo.png');
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
test('同名不同扩展名 → orphan,且提示框架里的实际扩展名', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
png(d.fw, 'atlas-hall/btn.png');
|
||||
writeFile(d.ov, 'atlas-hall/btn.jpg', 'x');
|
||||
const r = validateOverrides(d.fw, d.ov);
|
||||
assert.equal(r.errors[0].code, 'orphan');
|
||||
assert.match(r.errors[0].message, /btn\.png/);
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
// ---- validateOverrides:尺寸 ----
|
||||
test('图片尺寸不同且未带 meta → size-mismatch', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
png(d.fw, 'atlas-hall/btn.png', 64, 64);
|
||||
png(d.ov, 'atlas-hall/btn.png', 128, 128);
|
||||
const r = validateOverrides(d.fw, d.ov);
|
||||
assert.equal(r.errors.length, 1);
|
||||
assert.equal(r.errors[0].code, 'size-mismatch');
|
||||
assert.match(r.errors[0].message, /64x64/);
|
||||
assert.match(r.errors[0].message, /128x128/);
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
test('图片尺寸不同但自带 meta(例外通道)→ 通过', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
png(d.fw, 'atlas-hall/btn.png', 64, 64);
|
||||
png(d.ov, 'atlas-hall/btn.png', 128, 128);
|
||||
writeFile(d.ov, 'atlas-hall/btn.png.meta', '{"ver":"1.0.0"}');
|
||||
const r = validateOverrides(d.fw, d.ov);
|
||||
assert.deepEqual(r.errors, []);
|
||||
assert.deepEqual(r.matched, ['atlas-hall/btn.png']);
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
test('非图片资源(音效)不做尺寸校验', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
// 用真实的、有效的、尺寸不同的 PNG 字节,但扩展名是 .wav
|
||||
// 如果 isImage() 门控被删掉,因为尺寸不同会报 size-mismatch,用例失败
|
||||
writeFile(d.fw, 'audio/click.wav', makePng(64, 64));
|
||||
writeFile(d.ov, 'audio/click.wav', makePng(256, 256));
|
||||
const r = validateOverrides(d.fw, d.ov);
|
||||
assert.deepEqual(r.errors, []);
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
// ---- validateOverrides:尺寸不可读 ----
|
||||
test('图片尺寸无法读取(非 PNG)→ size-unreadable', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
png(d.fw, 'atlas-hall/btn.png', 64, 64);
|
||||
writeFile(d.ov, 'atlas-hall/btn.png', 'this is not a png but it is long enough to pass the 24-byte length check');
|
||||
const r = validateOverrides(d.fw, d.ov);
|
||||
assert.equal(r.errors.length, 1);
|
||||
assert.equal(r.errors[0].code, 'size-unreadable');
|
||||
assert.match(r.errors[0].message, /无法读取尺寸/);
|
||||
assert.match(r.errors[0].message, /PNG/);
|
||||
assert.equal(r.matched.length, 0);
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
test('readPngSize 长度足够但签名不对 → 返回 null', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
// 构造一个 24 字节以上、但 PNG 签名不对的数据
|
||||
const buf = Buffer.alloc(30, 0x00);
|
||||
buf.write('NOTPNG', 1, 'ascii');
|
||||
const p = writeFile(d.fw, 'test.png', buf);
|
||||
assert.equal(readPngSize(p), null);
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
// ---- validateOverrides:Spine ----
|
||||
test('Spine 整套覆盖 → 通过,且贴图豁免尺寸校验', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
writeFile(d.fw, 'spine/hero/hero.json', '{}');
|
||||
writeFile(d.fw, 'spine/hero/hero.atlas', 'atlas');
|
||||
png(d.fw, 'spine/hero/hero.png', 64, 64);
|
||||
writeFile(d.ov, 'spine/hero/hero.json', '{"v":2}');
|
||||
writeFile(d.ov, 'spine/hero/hero.atlas', 'atlas2');
|
||||
png(d.ov, 'spine/hero/hero.png', 256, 256); // 尺寸不同也应通过
|
||||
const r = validateOverrides(d.fw, d.ov);
|
||||
assert.deepEqual(r.errors, []);
|
||||
assert.equal(r.matched.length, 3);
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
|
||||
test('Spine 缺件 → spine-incomplete,并列出缺少的文件', () => {
|
||||
const d = makeDirs();
|
||||
try {
|
||||
writeFile(d.fw, 'spine/hero/hero.json', '{}');
|
||||
writeFile(d.fw, 'spine/hero/hero.atlas', 'atlas');
|
||||
png(d.fw, 'spine/hero/hero.png', 64, 64);
|
||||
png(d.ov, 'spine/hero/hero.png', 64, 64); // 只换贴图
|
||||
const r = validateOverrides(d.fw, d.ov);
|
||||
assert.equal(r.errors.length, 1);
|
||||
assert.equal(r.errors[0].code, 'spine-incomplete');
|
||||
assert.match(r.errors[0].message, /hero\.json/);
|
||||
assert.match(r.errors[0].message, /hero\.atlas/);
|
||||
} finally { cleanup(d.root); }
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
# Cocos Creator 工程生成物(锚定到本工程根,勿用无锚点模式——会吞掉 assets 下同名目录)
|
||||
/library/
|
||||
/temp/
|
||||
/local/
|
||||
/build/
|
||||
/profiles/
|
||||
/native/
|
||||
|
||||
node_modules/
|
||||
.vscode/
|
||||
.idea/
|
||||
@@ -223,7 +223,7 @@ node scripts/new-game.mjs gitignore-probe && \
|
||||
test -f games/gitignore-probe/.gitignore && echo "OK: 种子 .gitignore 已随克隆带入" && \
|
||||
cd G:/Works/YouleGamesCocosCreator && \
|
||||
git check-ignore cocoscreator_projects/games/gitignore-probe/library/x && echo "OK: 新工程 library 已忽略" ; \
|
||||
rm -rf cocoscreator_projects/games/gitignore-probe
|
||||
node -e "require('fs').rmSync('cocoscreator_projects/games/gitignore-probe', { recursive: true, force: true })"
|
||||
```
|
||||
|
||||
Expected: 打印 `OK: 种子 .gitignore 已随克隆带入` 与 `OK: 新工程 library 已忽略`;探针工程已删除。
|
||||
@@ -691,8 +691,11 @@ export function validateOverrides(frameworkUiDir, overrideDir) {
|
||||
}
|
||||
|
||||
// Spine 贴图豁免尺寸校验(区域坐标在 .atlas 内,整套替换时自洽)
|
||||
// 注意:override 是否自带 .meta 必须用 existsSync 查磁盘——listAssets 排除了 .meta,
|
||||
// 故 ovSet 里永远没有 .meta 项,用 ovSet.has 会让例外通道永不生效。
|
||||
const metaPath = join(overrideDir, ...`${rel}.meta`.split(posix.sep));
|
||||
const needSizeCheck =
|
||||
isImage(rel) && !isSpine(rel) && !ovSet.has(`${rel}.meta`);
|
||||
isImage(rel) && !isSpine(rel) && !existsSync(metaPath);
|
||||
if (needSizeCheck) {
|
||||
const a = readPngSize(join(frameworkUiDir, ...rel.split(posix.sep)));
|
||||
const b = readPngSize(join(overrideDir, ...rel.split(posix.sep)));
|
||||
@@ -1386,8 +1389,8 @@ function readFrameworkCommit() {
|
||||
}
|
||||
}
|
||||
|
||||
// CLI 入口
|
||||
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
// CLI 入口(argv[1] 守卫与仓库其余脚本一致:无脚本名时不进 CLI 分支)
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
const name = process.argv[2];
|
||||
if (!name) {
|
||||
console.error('用法: npm run build-game <game> [-- --platform android]');
|
||||
@@ -1769,7 +1772,7 @@ Run:
|
||||
|
||||
```bash
|
||||
cd G:/Works/YouleGamesCocosCreator/cocoscreator_projects
|
||||
rm -rf games/skin-e2e build-workspace/skin-e2e
|
||||
node -e "const {rmSync}=require('fs'); rmSync('games/skin-e2e',{recursive:true,force:true}); rmSync('build-workspace/skin-e2e',{recursive:true,force:true})"
|
||||
cd G:/Works/YouleGamesCocosCreator && git status --porcelain cocoscreator_projects/games
|
||||
```
|
||||
|
||||
|
||||
@@ -155,6 +155,7 @@ override 下每个文件的路径,必须能在 `framework/ui/` 下找到同路
|
||||
| override 文件在框架里**找不到对应路径** | ❌ **报错中止构建** |
|
||||
| **图片**:同名、同尺寸、仅图片文件 | ✅ 换内容,框架 `.meta` **原封不动** |
|
||||
| **图片**:同名、**尺寸不同**、仅图片文件 | ❌ 报错中止,提示改为同尺寸或走例外通道 |
|
||||
| **图片**:需校验尺寸却**读不出尺寸**(非 PNG,或文件损坏) | ❌ 报错中止(`size-unreadable`)——**不得**当作通过静默放行 |
|
||||
| 同名、override 自带 `.meta`(**例外通道**) | ✅ 拷贝两者,把框架默认 meta 的 `uuid` 覆写进去 |
|
||||
| 扩展名/格式不一致(png → jpg) | ❌ 报错中止 |
|
||||
| **非图片**(音效 / 字体):同名替换 | ✅ 换内容,`.meta` 原封不动,**不做尺寸校验** |
|
||||
@@ -268,9 +269,13 @@ npm run build-game <name> [--platform android]
|
||||
3. 皮肤合成 assets/game/override/** 覆盖到 assets/framework/ui/**
|
||||
按 §3.2 校验;任一不合法即中止;打印命中统计
|
||||
4. 构建 调 Cocos CLI 在临时工程上构建
|
||||
5. 产出 拷至 dist/<name>/,写 build-info.json
|
||||
5. 产出 写 build-info.json(拷至 dist/<name>/ 本期未实现,见下)
|
||||
```
|
||||
|
||||
> **`dist/<name>/` 拷贝:本期未实现。** `build-game.mjs` 目前止步于 `build-workspace/<name>/`
|
||||
> 内调用 Cocos CLI 构建 + 写 `build-info.json`;把构建产物拷到独立的 `dist/<name>/` 尚未落地
|
||||
> (构建产物的实际目录结构未经真实构建验证,贸然实现拷贝逻辑等于凭空猜测路径)。
|
||||
|
||||
### 5.1 四条硬规则
|
||||
|
||||
**① 绝不在框架真源上做合成。**
|
||||
@@ -314,12 +319,12 @@ npm run build-game <name> [--platform android]
|
||||
|
||||
以下三条是本方案的承重点,**须在第一个真实框架场景/资源出现后立即验证**,不得凭推断实现。
|
||||
|
||||
| # | 待验证 | 验证方法 | 失败后的备选 |
|
||||
|---|---|---|---|
|
||||
| 1 | Cocos 3.8.8 的 Auto Atlas 确在**构建期**打包,构建前替换散图能被正确纳入图集 | 造一个 `.pac` 目录,替换其中一张散图后构建,检查产物图集内容 | 改为"图集整体作为覆盖单位",由子游戏提供整套 plist+png |
|
||||
| 2 | 例外通道中,除顶层 `uuid` 外,图片 `.meta` 的 `subMetas`(spriteFrame 子 uuid)是否亦需同步覆写 | 用尺寸不同的图走例外通道,检查 Prefab 引用是否仍有效 | 合成脚本一并覆写 subMetas 的 uuid |
|
||||
| 3 | `build-workspace/<name>/library/` 跨次构建复用是否可靠,资源被替换后增量导入是否正确刷新 | 连续两次 build-game,第二次改动 override,比对产物 | 放弃缓存,每次全量导入(仅影响耗时) |
|
||||
| 4 | Cocos 的 `sp.SkeletonData` 在"整套替换内容、保留全部 `.meta`"后引用是否仍自洽(骨骼数据 ↔ atlas ↔ 贴图的 uuid 关联记在何处) | 用一套不同的 Spine 资源整套替换,检查场景内动画是否正常播放 | Spine 改走组件替换通道(子游戏注册自己的 `sp.Skeleton` 节点),不走资源覆盖 |
|
||||
| # | 待验证 | 验证方法 | 失败后的备选 | 实测结论(2026-08-27,Task 2) |
|
||||
|---|---|---|---|---|
|
||||
| 1 | Cocos 3.8.8 的 Auto Atlas 确在**构建期**打包,构建前替换散图能被正确纳入图集 | 造一个 `.pac` 目录,替换其中一张散图后构建,检查产物图集内容 | 改为"图集整体作为覆盖单位",由子游戏提供整套 plist+png | ⏸ **阻塞(非证伪)**。真正卡住的不是"构建能否跑完",而是**"造不出一个真实的 Auto Atlas 资产"**:MCP `asset.create` 只支持 `url`/`content`/`overwrite`,没有资产类型参数;对 `.pac` 命名文件夹做 `refresh`/`reimport` 也不会把 `importer` 从 `"directory"` 转成 `"auto-atlas"`,多次独立尝试一致。(`editor.build`/`editor.open_build_panel` 确实也只打开构建面板要求人工点击——但项目自己的 `build-game.mjs` 已实现走 `CocosCreator.exe --project <dir> --build "platform=..."` 的官方无头 CLI 构建,不经过 MCP;只是本机 `COCOS_CREATOR` 未配置、也未定位到编辑器可执行文件,这条路本身也未验证可用,且即便可用也不能替代"创建 Auto Atlas"这一步——那始终是编辑器 Assets 面板 `Create > Auto Atlas` 的菜单命令,CLI 构建帮不上。)需人工在编辑器 GUI 内手动执行该菜单命令后再复测。**在此之前不得默认本假设成立去推进 Task 3 的图集相关设计。** |
|
||||
| 2 | 例外通道中,除顶层 `uuid` 外,图片 `.meta` 的 `subMetas`(spriteFrame 子 uuid)是否亦需同步覆写 | 用尺寸不同的图走例外通道,检查 Prefab 引用是否仍有效 | 合成脚本一并覆写 subMetas 的 uuid | 🟡 **部分验证,范围窄于本行原意**。实测的是更基础的前提:保留原 `.meta`、只换图片内容(同尺寸)后刷新,顶层 `uuid` 与全部 `subMetas.uuid` 均未变化;换成 128×128(仍不提供新 `.meta`,未走例外通道)后刷新,顶层 `uuid` 依旧不变,且 sprite-frame 子 meta 的 `width`/`height`/`rawWidth`/`rawHeight`/`vertices`/`uv` 被 Cocos 自动重算为 128×128(前后 meta 全文见 task-2-report.md)。**未验证**的是本行字面所指的例外通道场景(override 自带一份独立生成的 `.meta`,合成脚本只覆写顶层 `uuid`,问 `subMetas` 里的子 uuid 要不要也同步覆写)——因待验证项 1 阻塞、无法在真实 `.pac` 图集环境下复测,这条更精确的问题仍待专项验证。spec §3.2 的例外通道设计**维持不变**,不降级、不简化。 |
|
||||
| 3 | `build-workspace/<name>/library/` 跨次构建复用是否可靠,资源被替换后增量导入是否正确刷新 | 连续两次 build-game,第二次改动 override,比对产物 | 放弃缓存,每次全量导入(仅影响耗时) | ⏸ **阻塞:缺构建环境**(2026-08-27,Task 8)。实测需要 `COCOS_CREATOR` 环境变量指向 Cocos Creator 3.8.8 可执行文件——本机未配置该变量,且 `where.exe CocosCreator.exe` 未能在 PATH 上定位到编辑器可执行文件,`build-game.mjs` 的实际构建步骤(内部调用 `CocosCreator.exe --project <dir> --build "platform=..."`)无法执行,因而无法运行"连续两次 build-game 比对耗时/产物"的实测动作。此外,`buildGame` 当前实现每次都会 `rmSync` 整个构建工作区(含 `library/`)后重新实体化,即便环境就绪,字面意义上的"library 跨次复用"在现有实现下也不成立——`library` 目录会随工作区一起被清空重建,第二次构建拿到的是全新的 `library`,不存在缓存复用可言。故本行结论有两层:(a) 环境阻塞,实测动作未能执行;(b) 静态阅读代码可确认现有实现未实现"保留 library 复用"的设计,若要验证复用效果需先改造 `buildGame` 为"不整体 rmSync 工作区、只刷新 assets",这已超出本任务范围,留作 spec 结尾「后续计划衔接」中已列出的独立任务。**在此之前不得默认 library 复用已生效或已失效去做性能相关决策。** |
|
||||
| 4 | Cocos 的 `sp.SkeletonData` 在"整套替换内容、保留全部 `.meta`"后引用是否仍自洽(骨骼数据 ↔ atlas ↔ 贴图的 uuid 关联记在何处) | 用一套不同的 Spine 资源整套替换,检查场景内动画是否正常播放 | Spine 改走组件替换通道(子游戏注册自己的 `sp.Skeleton` 节点),不走资源覆盖 | ⏸ **阻塞:仓库无 Spine 素材**。`projects/*/assets/spine/` 全部为空,无任何可用 Spine 导出资源。`cocos_spine` 工具的全部 action(`info`/`list_animations`/`list_skins`/`set_animation`/`set_skin`/`set_property`/`set_data`/`add_socket`/`remove_socket`)都要求场景内已存在带 `sp.Skeleton` 组件的节点,无法凭空验证。已在 `framework/ui/README.md` 加提示,待第一套真实 Spine 资源到位后专项验证,**不假定其可行**。 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ GameData.hallConfigName = ["enablePay",
|
||||
'wareHouse',//是否开启仓库功能0->不开启1->开启保险箱不开启赠送2->开启赠送不开启保险箱3->开启星星赠送(开启保险箱)4->开启房卡赠送(开启保险箱)5->开启星星房卡赠送(开启保险箱)
|
||||
'betaText',//版本号后的beta显示内容
|
||||
'launchImgUrl',//启动页图片地址
|
||||
'shareType:',//分享朋友圈类型0->链接分享 1->图片分享
|
||||
'shareType',//分享朋友圈类型0->链接分享 1->图片分享
|
||||
'shareImgUrl',//分享朋友圈图片链接
|
||||
'launchWaitTime',//启动等待时间
|
||||
'shareBgUrl',//分享背景图片
|
||||
@@ -151,7 +151,8 @@ GameData.hallConfigName = ["enablePay",
|
||||
'channelKey',//支付key
|
||||
'payCSS',//支付css
|
||||
'payJS',//支付js
|
||||
'openVideo'
|
||||
'openVideo',
|
||||
'showVisitor'//是否显示游客登陆
|
||||
];//是否开启视频];
|
||||
GameData.hallConfig = {
|
||||
enablePay:0,
|
||||
@@ -185,6 +186,7 @@ GameData.hallConfig = {
|
||||
payCSS:"",//支付css
|
||||
payJS:"",//支付js
|
||||
openVideo:0,//是否开启视频
|
||||
showVisitor:0//是否显示游客登陆
|
||||
};
|
||||
GameData.starName = "星星";
|
||||
GameData.roomCardName = "房卡";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,12 @@ function Player(seat){
|
||||
//this.offline = 0;//玩家是否离线0->否 1->离线
|
||||
this.canexit=1;//是否可以直接离开
|
||||
this.onstate=0;//0:在线1:离线2:通话中
|
||||
/*
|
||||
{"address":"江西省南昌市青云谱区施尧路靠近上海浦东发展银行(长天支行)","city":"南昌市",
|
||||
"cityCode":"0791","country":"中国","district":"青云谱区","latitude":28.623546,"longitude":115.900333,
|
||||
"province":"江西省","street":"施尧路"}
|
||||
*/
|
||||
//定位信息 牌桌外
|
||||
this.addr = null;
|
||||
this.invitecode = "";//邀请码
|
||||
this.isStart = false;//能否点击按钮开始游戏
|
||||
@@ -31,6 +37,14 @@ function Player(seat){
|
||||
this.charm = undefined;
|
||||
this.sign = "";//签名
|
||||
this.tel = "";//绑定手机号
|
||||
/*
|
||||
{"address":"江西省南昌市青云谱区施尧路靠近上海浦东发展银行(长天支行)","city":"南昌市",
|
||||
"cityCode":"0791","country":"中国","district":"青云谱区","latitude":28.623546,"longitude":115.900333,
|
||||
"province":"江西省","street":"施尧路"}
|
||||
*/
|
||||
//定位信息 牌桌内
|
||||
this.location = null;
|
||||
|
||||
}
|
||||
//玩家信息初始化
|
||||
if(typeof(Player.prototype.Init) == "undefined"){
|
||||
@@ -67,6 +81,7 @@ if(typeof(Player.prototype.Init) == "undefined"){
|
||||
this.charm = undefined;
|
||||
this.sign = "";//签名
|
||||
this.tel = "";//绑定手机号
|
||||
|
||||
};
|
||||
}
|
||||
//玩家微信信息初始化
|
||||
@@ -107,6 +122,7 @@ if(typeof(Player.prototype.SetMyInfo) == "undefined"){
|
||||
this.charm = object.charm;
|
||||
this.sign = object.sign;
|
||||
this.tel = object.tel;
|
||||
|
||||
//this.setTel();
|
||||
//this.bankpwd = object.bank;//是否设置仓库密码 this.bankpwd = 0;//是否设置仓库密码
|
||||
//this.paycode = _paycode;
|
||||
@@ -121,7 +137,7 @@ if(typeof(Player.prototype.SetLocationInfo) == "undefined"){
|
||||
}else{
|
||||
set_self(200, 7, "获取定位失败!", 0, 0);
|
||||
}
|
||||
|
||||
this.location = _locationinfo;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -279,6 +295,7 @@ if(typeof(Player.prototype.SetDeskInfo) == "undefined"){
|
||||
}else{
|
||||
this.paycode = "";
|
||||
}
|
||||
this.location = _data.location;
|
||||
this.charm = _data.charm;
|
||||
this.sign = _data.sign;
|
||||
};
|
||||
|
||||
@@ -421,6 +421,8 @@ Desk.login=function(_msg){//登录
|
||||
Func.createRoom();
|
||||
}
|
||||
Game_Modify.Reconnect(_msg.data.deskinfo);
|
||||
}else{
|
||||
Game_Modify.ReconnectNoMakewar();
|
||||
}
|
||||
//}
|
||||
}else{
|
||||
@@ -911,7 +913,7 @@ Desk.send_text=function(_msg){//发送消息
|
||||
if(!_msg.data.type){
|
||||
var pobj = Desk.GetPlayerBySeat(_msg.data.seat);
|
||||
GameUI.playTextVoice(_msg.data.text,pobj.sex);
|
||||
GameUI.ShowChat(Logic.ChangeToStatus(C_Player.seat,_msg.data.seat),_msg.data.text);
|
||||
GameUI.ShowChat(_msg.data.seat,_msg.data.text);
|
||||
GameUI.addCommunion(Logic.ChangeToStatus(C_Player.seat,_msg.data.seat),1,_msg.data.text,0,null,pobj.sex);
|
||||
}else if(_msg.data.type == 1){//全服公告
|
||||
GameUI.CloseChat();
|
||||
@@ -929,7 +931,7 @@ Desk.send_text=function(_msg){//发送消息
|
||||
var idx = Number(_msg.data.text);
|
||||
_msg.data.text = Game_Config.Info.TextContent[(idx - 1)%Game_Config.Info.TextContent.length];
|
||||
GameUI.playTextVoice(_msg.data.text);
|
||||
GameUI.ShowChat(Logic.ChangeToStatus(C_Player.seat,_msg.data.seat),_msg.data.text);
|
||||
GameUI.ShowChat(_msg.data.seat,_msg.data.text);
|
||||
GameUI.addCommunion(Logic.ChangeToStatus(C_Player.seat,_msg.data.seat),1,_msg.data.text,0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ Utl.Config.sound = "";
|
||||
|
||||
Utl.Config.pre_wxinfo = "tsgame_wxinfo_";
|
||||
Utl.Config.wxinfo = "tsgame_wxinfo";
|
||||
Utl.Config.visitorinfo = "tsgame_visitorinfo";
|
||||
Utl.Config.maxtime = 48;//小时
|
||||
Utl.Config.pre_validtime = "tsgame_validtime_";//有效时间(时间戳)
|
||||
Utl.Config.validtime = "tsgame_validtime";//有效时间(时间戳)
|
||||
@@ -545,43 +546,49 @@ Utl.openVideo = function (_playerList, _seat, _roomcode, roomtype) {
|
||||
set_self(152, 37, 0, 0, 0);
|
||||
set_self(153, 37, 0, 0, 0);
|
||||
set_self(107, 37, 0, 0, 0);
|
||||
for (var i = 0; i < Desk.PlayerList.length; i++) {
|
||||
if (Desk.PlayerList[i].nickname != "") {
|
||||
var ind = Logic.ChangeToStatus(C_Player.seat, i);
|
||||
var text = Func.subString(Desk.PlayerList[i].nickname, Game_Config.Info.Mainnickname, true);
|
||||
set_self(406 + ind, 7, text, 0, 0);
|
||||
if (ind == 0) {
|
||||
if (Game_Config.Info.myPositionDefault) {
|
||||
set_self(406 + ind, 18, get_self(376 + ind, 18, 0, 0, 0) + get_self(376 + ind, 20, 0, 0, 0) / 2 - text.gblen() * Game_Config.Info.textwidth_1 / 2, 0, 0);
|
||||
// if (Game_Modify.updatePlayerInfoUI) {
|
||||
// for (var i = 0; i < Desk.PlayerList.length; i++) {
|
||||
// Game_Modify.updatePlayerInfoUI(i);
|
||||
// }
|
||||
// } else {
|
||||
for (var i = 0; i < Desk.PlayerList.length; i++) {
|
||||
if (Desk.PlayerList[i].nickname != "") {
|
||||
var ind = Logic.ChangeToStatus(C_Player.seat, i);
|
||||
var text = Func.subString(Desk.PlayerList[i].nickname, Game_Config.Info.Mainnickname, true);
|
||||
set_self(406 + ind, 7, text, 0, 0);
|
||||
if (ind == 0) {
|
||||
if (Game_Config.Info.myPositionDefault) {
|
||||
set_self(406 + ind, 18, get_self(376 + ind, 18, 0, 0, 0) + get_self(376 + ind, 20, 0, 0, 0) / 2 - text.gblen() * Game_Config.Info.textwidth_1 / 2, 0, 0);
|
||||
} else {
|
||||
set_self(406 + ind, 18, Game_Config.Info.myPosition - text.gblen() * Game_Config.Info.textwidth_1 / 2, 0, 0);
|
||||
}
|
||||
} else {
|
||||
set_self(406 + ind, 18, Game_Config.Info.myPosition - text.gblen() * Game_Config.Info.textwidth_1 / 2, 0, 0);
|
||||
if (Game_Config.Info.otherPositionDefault) {
|
||||
set_self(406 + ind, 18, get_self(376 + ind, 18, 0, 0, 0) + get_self(376 + ind, 20, 0, 0, 0) / 2 - text.gblen() * Game_Config.Info.textwidth_1 / 2, 0, 0);
|
||||
} else {
|
||||
set_self(406 + ind, 18, Game_Config.Info.position[ind - 1] - text.gblen() * Game_Config.Info.textwidth_1 / 2, 0, 0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Game_Config.Info.otherPositionDefault) {
|
||||
set_self(406 + ind, 18, get_self(376 + ind, 18, 0, 0, 0) + get_self(376 + ind, 20, 0, 0, 0) / 2 - text.gblen() * Game_Config.Info.textwidth_1 / 2, 0, 0);
|
||||
} else {
|
||||
set_self(406 + ind, 18, Game_Config.Info.position[ind - 1] - text.gblen() * Game_Config.Info.textwidth_1 / 2, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
set_self(436 + ind, 7, 0, 0, 0);
|
||||
if (ind == 0) {
|
||||
if (Game_Config.Info.myPositionDefault) {
|
||||
set_self(436 + ind, 18, get_self(376 + ind, 18, 0, 0, 0) + get_self(376 + ind, 20, 0, 0, 0) / 2 - Game_Config.Info.textwidth_2 / 2, 0, 0);
|
||||
set_self(436 + ind, 7, 0, 0, 0);
|
||||
if (ind == 0) {
|
||||
if (Game_Config.Info.myPositionDefault) {
|
||||
set_self(436 + ind, 18, get_self(376 + ind, 18, 0, 0, 0) + get_self(376 + ind, 20, 0, 0, 0) / 2 - Game_Config.Info.textwidth_2 / 2, 0, 0);
|
||||
} else {
|
||||
set_self(436 + ind, 18, Game_Config.Info.myPosition - Game_Config.Info.textwidth_2 / 2, 0, 0);
|
||||
}
|
||||
} else {
|
||||
set_self(436 + ind, 18, Game_Config.Info.myPosition - Game_Config.Info.textwidth_2 / 2, 0, 0);
|
||||
}
|
||||
} else {
|
||||
if (Game_Config.Info.otherPositionDefault) {
|
||||
set_self(436 + ind, 18, get_self(376 + ind, 18, 0, 0, 0) + get_self(376 + ind, 20, 0, 0, 0) / 2 - Game_Config.Info.textwidth_2 / 2, 0, 0);
|
||||
} else {
|
||||
set_self(436 + ind, 18, Game_Config.Info.position[ind - 1] - Game_Config.Info.textwidth_2 / 2, 0, 0);
|
||||
if (Game_Config.Info.otherPositionDefault) {
|
||||
set_self(436 + ind, 18, get_self(376 + ind, 18, 0, 0, 0) + get_self(376 + ind, 20, 0, 0, 0) / 2 - Game_Config.Info.textwidth_2 / 2, 0, 0);
|
||||
} else {
|
||||
set_self(436 + ind, 18, Game_Config.Info.position[ind - 1] - Game_Config.Info.textwidth_2 / 2, 0, 0);
|
||||
}
|
||||
}
|
||||
Func.up_imgurl(116 + ind, Desk.PlayerList[i].avatar);
|
||||
set_group(43 + ind, 37, 1, 0, 0);
|
||||
}
|
||||
Func.up_imgurl(116 + ind, Desk.PlayerList[i].avatar);
|
||||
set_group(43 + ind, 37, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
// }
|
||||
play_ani123(1, 156, 35, 255, 100, 1000, 0, 0, 1);
|
||||
Utl.playMusic();
|
||||
set_self(3141, 37, 0, 0, 0);
|
||||
|
||||
@@ -364,8 +364,7 @@ Net.Send_get_player_grade1 = function(_data){
|
||||
Net.get_player_grade1 = function(_msg){
|
||||
gameCombat.get_player_grade1(_msg);
|
||||
GameUI.EndLoad();
|
||||
//Logic.SetCombatInfo(_msg.data);
|
||||
//GameUI.OpenCombat();
|
||||
|
||||
|
||||
}
|
||||
//发送获取战绩2
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -337,35 +337,35 @@ Logic.AppStart=function(){
|
||||
// 解决:覆盖全局字体渲染函数,统一使用更粗的中文友好字体
|
||||
|
||||
// 保存原始的 Canvas fillText 方法
|
||||
var originalFillText = CanvasRenderingContext2D.prototype.fillText;
|
||||
var originalStrokeText = CanvasRenderingContext2D.prototype.strokeText;
|
||||
// var originalFillText = CanvasRenderingContext2D.prototype.fillText;
|
||||
// var originalStrokeText = CanvasRenderingContext2D.prototype.strokeText;
|
||||
|
||||
// 覆盖 fillText 方法,在绘制前统一字体设置
|
||||
CanvasRenderingContext2D.prototype.fillText = function() {
|
||||
// 如果当前字体包含 'lighter' 或只有 'Arial',替换为更适合中文的字体
|
||||
if (this.font && (this.font.includes('lighter') || this.font.includes('Arial'))) {
|
||||
// 提取字体大小
|
||||
var sizeMatch = this.font.match(/(\d+)px/);
|
||||
if (sizeMatch) {
|
||||
var size = sizeMatch[1];
|
||||
// 使用 500 字重(中等粗细)+ 中文友好字体
|
||||
this.font = '500 ' + size + 'px "Microsoft YaHei", "PingFang SC", Arial, sans-serif';
|
||||
}
|
||||
}
|
||||
return originalFillText.apply(this, arguments);
|
||||
};
|
||||
// // 覆盖 fillText 方法,在绘制前统一字体设置
|
||||
// CanvasRenderingContext2D.prototype.fillText = function() {
|
||||
// // 如果当前字体包含 'lighter' 或只有 'Arial',替换为更适合中文的字体
|
||||
// if (this.font && (this.font.includes('lighter') || this.font.includes('Arial'))) {
|
||||
// // 提取字体大小
|
||||
// var sizeMatch = this.font.match(/(\d+)px/);
|
||||
// if (sizeMatch) {
|
||||
// var size = sizeMatch[1];
|
||||
// // 使用 500 字重(中等粗细)+ 中文友好字体
|
||||
// this.font = '500 ' + size + 'px "Microsoft YaHei", "PingFang SC", Arial, sans-serif';
|
||||
// }
|
||||
// }
|
||||
// return originalFillText.apply(this, arguments);
|
||||
// };
|
||||
|
||||
// 同样覆盖 strokeText 方法
|
||||
CanvasRenderingContext2D.prototype.strokeText = function() {
|
||||
if (this.font && (this.font.includes('lighter') || this.font.includes('Arial'))) {
|
||||
var sizeMatch = this.font.match(/(\d+)px/);
|
||||
if (sizeMatch) {
|
||||
var size = sizeMatch[1];
|
||||
this.font = '500 ' + size + 'px "Microsoft YaHei", "PingFang SC", Arial, sans-serif';
|
||||
}
|
||||
}
|
||||
return originalStrokeText.apply(this, arguments);
|
||||
};
|
||||
// // 同样覆盖 strokeText 方法
|
||||
// CanvasRenderingContext2D.prototype.strokeText = function() {
|
||||
// if (this.font && (this.font.includes('lighter') || this.font.includes('Arial'))) {
|
||||
// var sizeMatch = this.font.match(/(\d+)px/);
|
||||
// if (sizeMatch) {
|
||||
// var size = sizeMatch[1];
|
||||
// this.font = '500 ' + size + 'px "Microsoft YaHei", "PingFang SC", Arial, sans-serif';
|
||||
// }
|
||||
// }
|
||||
// return originalStrokeText.apply(this, arguments);
|
||||
// };
|
||||
|
||||
// console.log('[字体优化] Canvas 文字渲染已优化,中英文粗细统一');
|
||||
// ============================================================================
|
||||
@@ -1150,9 +1150,7 @@ Logic.TurnOffVoice = function(){
|
||||
|
||||
}
|
||||
|
||||
//Logic.SetCombatInfo=function(_data){
|
||||
//CombatInfo = _data.gradeinfo;
|
||||
//}
|
||||
|
||||
Logic.ChangeToStatus=function(myseat,seat){//将座位转化为对应位置
|
||||
return (seat-myseat+Game_Config.Max.PlayerCnt)%Game_Config.Max.PlayerCnt;
|
||||
}
|
||||
@@ -1798,6 +1796,7 @@ Logic.setConfigInfo = function(){
|
||||
}
|
||||
for(var i=0;i<GameData.hallConfigName.length;i++){
|
||||
var val = get_paravalue(GameData.serverConfig,GameData.hallConfigName[i]);
|
||||
// console.log(GameData.hallConfigName[i] + val);
|
||||
if(val != null){
|
||||
GameData.hallConfig[GameData.hallConfigName[i]] = val;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user