391 lines
15 KiB
JavaScript
Executable File
391 lines
15 KiB
JavaScript
Executable File
"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);
|
|
});
|
|
}
|
|
});
|