chore: track Cocos Creator project for future migration

This commit is contained in:
cabinw
2026-09-08 21:54:30 +08:00
parent feb2ec5de1
commit 9f8990b579
23 changed files with 37475 additions and 5 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,390 @@
"use strict";
// packages/cocos-ext/src/panels/default/index.ts
var LOCALE_STORAGE_KEY = "cocos-mcp-panel-locale";
var STR = {
zh: {
panelTitle: "Cocos MCP \u63A7\u5236\u53F0",
langLabel: "\u754C\u9762\u8BED\u8A00",
server: "\u670D\u52A1",
running: "\u8FD0\u884C\u4E2D",
stopped: "\u5DF2\u505C\u6B62",
port: "\u7AEF\u53E3",
connections: "\u8FDE\u63A5\u6570",
url: "\u9762\u677F URL\uFF08\u4EC5\u4F9B\u53C2\u8003\uFF09",
startServer: "\u542F\u52A8\u670D\u52A1",
stopServer: "\u505C\u6B62\u670D\u52A1",
refresh: "\u5237\u65B0",
runSelfTest: "\u4E00\u952E\u68C0\u6D4B",
selfTestTitle: "\u8FDE\u63A5\u8BCA\u65AD",
selfTestNever: "\u5C1A\u672A\u8FD0\u884C\u68C0\u6D4B",
quickStartTitle: "\u9996\u6B21\u4F7F\u7528",
quickStartStepServer: "\u542F\u52A8MCP\u670D\u52A1",
quickStartStepConfig: "\u914D\u7F6E\u81F3\u5C11\u4E00\u4E2AMCP\u5BA2\u6237\u7AEF",
quickStartStepConnect: "\u91CD\u542F\u5BA2\u6237\u7AEF\u5E76\u5EFA\u7ACB\u8FDE\u63A5",
quickStartDone: "\u5B8C\u6210",
quickStartPending: "\u5F85\u5B8C\u6210",
openGuide: "\u6253\u5F00Quick Start",
firstPromptLabel: "\u8FDE\u63A5\u6210\u529F\u540E\uFF0C\u5148\u8BA9AI\u8BFB\u53D6\u573A\u666F\uFF0C\u518D\u505A\u4E00\u4E2A\u5C0F\u6539\u52A8\u3002",
autoConfigTitle: "\u4E00\u952E\u914D\u7F6E MCP \u5BA2\u6237\u7AEF",
serverNamePh: "MCP \u670D\u52A1\u540D\uFF08\u5199\u5165\u5404\u5BA2\u6237\u7AEF\u914D\u7F6E\uFF09",
autoConfigAll: "\u4E00\u952E\u914D\u7F6E\u5168\u90E8\u5BA2\u6237\u7AEF",
configured: "\u5DF2\u914D\u7F6E",
notConfigured: "\u672A\u914D\u7F6E",
autoConfig: "\u4E00\u952E\u914D\u7F6E",
openConfig: "\u6253\u5F00\u914D\u7F6E",
cliTitle: "\u547D\u4EE4\u884C\u53C2\u8003\uFF08Codex / Gemini CLI\uFF09",
operationLogs: "\u64CD\u4F5C\u65E5\u5FD7",
clear: "\u6E05\u7A7A",
logLevelInfo: "\u4FE1\u606F",
logLevelWarn: "\u8B66\u544A",
logLevelError: "\u9519\u8BEF"
},
en: {
panelTitle: "Cocos MCP Console",
langLabel: "Language",
server: "Server",
running: "Running",
stopped: "Stopped",
port: "port",
connections: "Connections",
url: "Panel URL (reference)",
startServer: "Start Server",
stopServer: "Stop Server",
refresh: "Refresh",
runSelfTest: "Run Self-Test",
selfTestTitle: "Connection Diagnostics",
selfTestNever: "Self-test has not run yet",
quickStartTitle: "First Run",
quickStartStepServer: "Start the MCP service",
quickStartStepConfig: "Configure at least one MCP client",
quickStartStepConnect: "Restart the client and connect",
quickStartDone: "done",
quickStartPending: "pending",
openGuide: "Open Quick Start",
firstPromptLabel: "After connecting, inspect the scene before one small mutation.",
autoConfigTitle: "Auto Config MCP Clients",
serverNamePh: "server name (written to each client config)",
autoConfigAll: "Auto Config All",
configured: "configured",
notConfigured: "not configured",
autoConfig: "Auto Config",
openConfig: "Open Config",
cliTitle: "CLI reference (Codex / Gemini CLI)",
operationLogs: "Operation Logs",
clear: "Clear",
logLevelInfo: "info",
logLevelWarn: "warn",
logLevelError: "error"
}
};
function readLocale() {
try {
const v = localStorage.getItem(LOCALE_STORAGE_KEY);
if (v === "en" || v === "zh") {
return v;
}
} catch {
}
return "zh";
}
function writeLocale(l) {
try {
localStorage.setItem(LOCALE_STORAGE_KEY, l);
} catch {
}
}
var locale = readLocale();
function t(key) {
return STR[locale][key] ?? STR.en[key] ?? key;
}
var TEMPLATE = `
<div class="wrap">
<div class="row lang-bar">
<span id="langLabel" class="lang-label"></span>
<select id="langSelect" class="lang-select">
<option value="zh">\u4E2D\u6587</option>
<option value="en">English</option>
</select>
</div>
<h2 id="panelTitle"></h2>
<div class="card guide-card">
<div class="row guide-heading">
<h3 id="quickStartTitle"></h3>
<ui-button id="openGuideBtn"></ui-button>
</div>
<div id="quickStartSteps" class="guide-steps"></div>
<div id="firstPromptLabel" class="guide-note"></div>
</div>
<div class="card">
<div id="serverLine">-</div>
<div id="connLine">-</div>
<div id="urlLine">-</div>
<div class="row">
<ui-button id="startBtn"></ui-button>
<ui-button id="stopBtn"></ui-button>
<ui-button id="refreshBtn"></ui-button>
<ui-button id="selfTestBtn"></ui-button>
</div>
<h3 id="selfTestTitle" class="self-test-title"></h3>
<div id="selfTestResults" class="self-test-results"></div>
</div>
<div class="card">
<h3 id="autoConfigTitle"></h3>
<div class="row">
<ui-input id="serverNameInput"></ui-input>
<ui-button id="addAllBtn"></ui-button>
</div>
<div id="clientList"></div>
</div>
<div class="card">
<h3 id="cliTitle"></h3>
<div class="mono" id="claudeCmd"></div>
<div class="mono" id="geminiCmd"></div>
</div>
<div class="card">
<div class="row">
<h3 id="logsTitle"></h3>
<ui-button id="clearLogsBtn"></ui-button>
</div>
<div id="logs" class="logs"></div>
</div>
</div>
`;
var STYLE = `
.wrap { padding: 12px; color: #d4d4d4; font-size: 13px; }
.lang-bar { margin-bottom: 10px; }
.lang-label { margin-right: 8px; color: #aaa; }
.lang-select { background: #2d2d2d; color: #d4d4d4; border: 1px solid #444; border-radius: 4px; padding: 4px 8px; font-size: 13px; }
h2 { margin: 0 0 10px 0; font-size: 18px; }
h3 { margin: 0 0 8px 0; font-size: 14px; }
.card { border: 1px solid #333; border-radius: 6px; padding: 10px; margin-bottom: 10px; background: #1e1e1e; }
.row { display: flex; gap: 8px; align-items: center; margin-top: 8px; margin-bottom: 6px; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; background: #111; padding: 8px; border-radius: 4px; margin-bottom: 6px; word-break: break-all; }
.logs { max-height: 220px; overflow: auto; background: #111; border-radius: 4px; padding: 8px; }
.log { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; margin-bottom: 4px; white-space: pre-wrap; word-break: break-word; }
.log.error { color: #ff8a8a; }
.log.warn { color: #f7d774; }
.log.info { color: #9bd4ff; }
.client-row { display: grid; grid-template-columns: 110px 1fr auto auto; gap: 8px; align-items: center; margin: 6px 0; }
.tag { padding: 2px 6px; border-radius: 4px; font-size: 12px; background: #333; }
.ok { background: #1f5f2f; }
.bad { background: #6a2a2a; }
.guide-card { border-color: #315b4f; }
.guide-heading { justify-content: space-between; margin-top: 0; }
.guide-heading h3 { margin: 0; }
.guide-steps { display: grid; gap: 6px; }
.guide-step { display: grid; grid-template-columns: 68px 1fr; gap: 8px; align-items: center; }
.guide-status { padding: 2px 6px; border-radius: 4px; font-size: 12px; text-align: center; background: #5c4622; }
.guide-status.done { background: #1f5f2f; }
.guide-note { margin-top: 8px; color: #a8c9bd; }
.self-test-title { margin-top: 12px; }
.self-test-results { display: grid; gap: 5px; }
.self-test-row { display: grid; grid-template-columns: 22px 92px 1fr; gap: 6px; align-items: start; }
.self-test-icon.ok-text { color: #63d28b; }
.self-test-icon.bad-text { color: #ff8a8a; }
.self-test-message { color: #aaa; word-break: break-word; }
`;
module.exports = Editor.Panel.define({
template: TEMPLATE,
style: STYLE,
$: {
langLabel: "#langLabel",
langSelect: "#langSelect",
panelTitle: "#panelTitle",
quickStartTitle: "#quickStartTitle",
quickStartSteps: "#quickStartSteps",
openGuideBtn: "#openGuideBtn",
firstPromptLabel: "#firstPromptLabel",
serverLine: "#serverLine",
connLine: "#connLine",
urlLine: "#urlLine",
startBtn: "#startBtn",
stopBtn: "#stopBtn",
refreshBtn: "#refreshBtn",
selfTestBtn: "#selfTestBtn",
selfTestTitle: "#selfTestTitle",
selfTestResults: "#selfTestResults",
autoConfigTitle: "#autoConfigTitle",
serverNameInput: "#serverNameInput",
addAllBtn: "#addAllBtn",
clientList: "#clientList",
cliTitle: "#cliTitle",
claudeCmd: "#claudeCmd",
geminiCmd: "#geminiCmd",
logs: "#logs",
logsTitle: "#logsTitle",
clearLogsBtn: "#clearLogsBtn"
},
ready() {
const pluginName = "cocos-mcp";
locale = readLocale();
const readServerName = () => {
const input = this.$.serverNameInput;
const v = (input?.value ?? "").trim();
return v || "cocos-creator";
};
const applyStaticLabels = () => {
this.$.langLabel.textContent = t("langLabel");
this.$.panelTitle.textContent = t("panelTitle");
this.$.quickStartTitle.textContent = t("quickStartTitle");
this.$.openGuideBtn.textContent = t("openGuide");
this.$.firstPromptLabel.textContent = t("firstPromptLabel");
this.$.autoConfigTitle.textContent = t("autoConfigTitle");
this.$.cliTitle.textContent = t("cliTitle");
this.$.logsTitle.textContent = t("operationLogs");
this.$.startBtn.textContent = t("startServer");
this.$.stopBtn.textContent = t("stopServer");
this.$.refreshBtn.textContent = t("refresh");
this.$.selfTestBtn.textContent = t("runSelfTest");
this.$.selfTestTitle.textContent = t("selfTestTitle");
this.$.addAllBtn.textContent = t("autoConfigAll");
this.$.clearLogsBtn.textContent = t("clear");
const input = this.$.serverNameInput;
if (input) {
input.placeholder = t("serverNamePh");
}
const sel = this.$.langSelect;
if (sel) {
sel.value = locale;
}
};
const logLevelLabel = (level) => {
if (level === "error") {
return t("logLevelError");
}
if (level === "warn") {
return t("logLevelWarn");
}
return t("logLevelInfo");
};
const render = (state) => {
const run = state.server.running ? t("running") : t("stopped");
this.$.serverLine.textContent = `${t("server")}: ${run} (${t("port")}: ${state.server.port ?? "-"})`;
this.$.connLine.textContent = `${t("connections")}: ${state.server.connections}`;
this.$.urlLine.textContent = `${t("url")}: ${state.server.url ?? "-"}`;
this.$.claudeCmd.textContent = state.cliCommands.claude;
this.$.geminiCmd.textContent = state.cliCommands.gemini;
const selfTestResults = this.$.selfTestResults;
selfTestResults.innerHTML = "";
if (!state.selfTest) {
selfTestResults.textContent = t("selfTestNever");
} else {
for (const check of state.selfTest.checks) {
const row = document.createElement("div");
row.className = "self-test-row";
const icon = document.createElement("span");
icon.className = `self-test-icon ${check.ok ? "ok-text" : "bad-text"}`;
icon.textContent = check.ok ? "OK" : "X";
const id = document.createElement("strong");
id.textContent = check.id;
const message = document.createElement("span");
message.className = "self-test-message";
message.textContent = check.message;
row.append(icon, id, message);
selfTestResults.appendChild(row);
}
}
const guideSteps = this.$.quickStartSteps;
guideSteps.innerHTML = "";
const onboardingSteps = [
{ label: t("quickStartStepServer"), done: state.server.running },
{ label: t("quickStartStepConfig"), done: state.clients.some((client) => client.configured) },
{ label: t("quickStartStepConnect"), done: state.server.connections > 0 }
];
for (const step of onboardingSteps) {
const row = document.createElement("div");
row.className = "guide-step";
const status = document.createElement("span");
status.className = `guide-status${step.done ? " done" : ""}`;
status.textContent = step.done ? t("quickStartDone") : t("quickStartPending");
const label = document.createElement("span");
label.textContent = step.label;
row.append(status, label);
guideSteps.appendChild(row);
}
const list = this.$.clientList;
list.innerHTML = "";
for (const c of state.clients) {
const row = document.createElement("div");
row.className = "client-row";
const tagText = c.configured ? t("configured") : t("notConfigured");
const tagClass = c.configured ? "ok" : "bad";
row.innerHTML = `
<strong>${c.name}</strong>
<span title="${c.configPath}">${c.configPath}</span>
<span class="tag ${tagClass}">${tagText}</span>
<div>
<ui-button class="cfg-btn" data-client="${c.clientType}">${t("autoConfig")}</ui-button>
<ui-button class="open-btn" data-client="${c.clientType}">${t("openConfig")}</ui-button>
</div>
`;
list.appendChild(row);
}
list.querySelectorAll(".cfg-btn").forEach((el) => {
el.addEventListener("confirm", async () => {
const clientType = el.getAttribute("data-client");
await Editor.Message.request(pluginName, "add-mcp-to-client", clientType, readServerName());
await refresh();
});
});
list.querySelectorAll(".open-btn").forEach((el) => {
el.addEventListener("confirm", async () => {
const clientType = el.getAttribute("data-client");
await Editor.Message.request(pluginName, "open-client-config", clientType);
});
});
const logs = this.$.logs;
logs.innerHTML = "";
for (const l of state.logs.slice().reverse()) {
const line = document.createElement("div");
line.className = `log ${l.level}`;
line.textContent = `${l.ts} [${logLevelLabel(l.level)}] ${l.message}`;
logs.appendChild(line);
}
};
const refresh = async () => {
applyStaticLabels();
const state = await Editor.Message.request(pluginName, "get-mcp-panel-state", readServerName());
render(state);
};
this.$.langSelect?.addEventListener("change", () => {
const sel = this.$.langSelect;
const v = sel?.value === "en" ? "en" : "zh";
locale = v;
writeLocale(v);
refresh().catch((e) => console.error("[cocos-mcp panel] refresh after locale", e));
});
this.$.startBtn?.addEventListener("confirm", async () => {
await Editor.Message.request(pluginName, "start-mcp-server");
await refresh();
});
this.$.stopBtn?.addEventListener("confirm", async () => {
await Editor.Message.request(pluginName, "stop-mcp-server");
await refresh();
});
this.$.refreshBtn?.addEventListener("confirm", refresh);
this.$.selfTestBtn?.addEventListener("confirm", async () => {
await Editor.Message.request(pluginName, "run-mcp-self-test", readServerName());
await refresh();
});
this.$.openGuideBtn?.addEventListener("confirm", async () => {
await Editor.Message.request(pluginName, "open-user-guide");
});
this.$.addAllBtn?.addEventListener("confirm", async () => {
await Editor.Message.request(pluginName, "add-mcp-to-all-clients", readServerName());
await refresh();
});
this.$.clearLogsBtn?.addEventListener("confirm", async () => {
await Editor.Message.request(pluginName, "clear-panel-logs");
await refresh();
});
this.$.serverNameInput?.addEventListener("change", refresh);
refresh().catch((e) => {
console.error("[cocos-mcp panel] initial refresh failed", e);
});
}
});
File diff suppressed because it is too large Load Diff