mcp-server-app.html 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. <div class="mcp-app">
  2. <!-- 过期警告弹窗 -->
  3. <div class="expiry-warning-modal" v-if="showExpiryWarning">
  4. <div class="expiry-warning-content">
  5. <div class="expiry-warning-icon">&#9888;</div>
  6. <h3 class="expiry-warning-title">{{ t('expiry_warning_title') }}</h3>
  7. <p class="expiry-warning-message">
  8. {{ t('expiry_warning_message', licenseInfo ? licenseInfo.daysRemaining : 0) }}
  9. </p>
  10. <div class="expiry-warning-actions">
  11. <a class="auth-link-button" :href="withUtm('https://www.vberai.com/game-engines/cocos')" target="_blank">
  12. {{ t('renew_now') }}
  13. </a>
  14. <button class="auth-dismiss-button" @click="dismissExpiryWarning">
  15. {{ t('i_understand') }}
  16. </button>
  17. </div>
  18. <div v-if="machineId" style="margin: 8px 0 0; padding: 6px 10px; background: rgba(255,255,255,0.05); border-radius: 4px; font-size: 11px; color: #888; text-align: center;">
  19. <span>{{ t('machine_id_label') || '机器码' }}: </span>
  20. <span style="font-family: monospace; user-select: all; color: #aaa;">{{ machineId.length > 20 ? machineId.substring(0, 10) + '***' + machineId.substring(machineId.length - 10) : machineId }}</span>
  21. </div>
  22. <div class="expiry-warning-contact">
  23. <span>{{ t('contact_support') }}: a549623165@gmail.com | {{ t('contact_wechat') }}: lidaxian-AI | 客服微信: vberai-support</span>
  24. </div>
  25. </div>
  26. </div>
  27. <!-- 变更激活码弹窗 -->
  28. <div class="change-code-modal" v-if="showChangeCodeModal">
  29. <div class="change-code-content">
  30. <h3>{{ t('change_code_title') }}</h3>
  31. <p class="change-code-desc">{{ t('change_code_desc') }}</p>
  32. <div class="auth-field">
  33. <label>{{ t('activation_code_label') }}</label>
  34. <input class="auth-input" type="text" v-model="newCode" :placeholder="t('code_placeholder')" />
  35. </div>
  36. <div class="auth-error" v-if="changeCodeError">{{ changeCodeError }}</div>
  37. <div v-if="machineId" style="margin: 8px 0; padding: 6px 10px; background: rgba(255,255,255,0.05); border-radius: 4px; font-size: 11px; color: #888; text-align: center;">
  38. <span>{{ t('machine_id_label') || '机器码' }}: </span>
  39. <span style="font-family: monospace; user-select: all; color: #aaa;">{{ machineId.length > 20 ? machineId.substring(0, 10) + '***' + machineId.substring(machineId.length - 10) : machineId }}</span>
  40. </div>
  41. <div class="change-code-actions">
  42. <button class="auth-activate-button" @click="changeLicenseCode" :disabled="changeCodeLoading">
  43. {{ changeCodeLoading ? t('activating') : t('confirm_change') }}
  44. </button>
  45. <button class="auth-dismiss-button" @click="showChangeCodeModal = false; newCode = ''; changeCodeError = ''">
  46. {{ t('cancel') }}
  47. </button>
  48. </div>
  49. </div>
  50. </div>
  51. <!-- 授权过期弹窗 -->
  52. <div class="expiry-warning-modal" v-if="showExpiredAlert">
  53. <div class="expiry-warning-content">
  54. <div class="expiry-warning-icon">&#128274;</div>
  55. <h3 class="expiry-warning-title">{{ t('license_expired_title') }}</h3>
  56. <p class="expiry-warning-message">{{ t('license_expired_message') }}</p>
  57. <div v-if="machineId" style="margin: 12px 0; padding: 8px 12px; background: rgba(255,255,255,0.05); border-radius: 4px; font-size: 11px; color: #888; text-align: center;">
  58. <span>{{ t('machine_id_label') || '机器码' }}: </span>
  59. <span style="font-family: monospace; user-select: all; color: #aaa;">{{ machineId.length > 20 ? machineId.substring(0, 10) + '***' + machineId.substring(machineId.length - 10) : machineId }}</span>
  60. </div>
  61. <div class="expiry-warning-actions">
  62. <a class="auth-link-button" :href="withUtm('https://www.vberai.com/game-engines/cocos')" target="_blank">
  63. {{ t('renew_now') }}
  64. </a>
  65. <button class="auth-dismiss-button" @click="confirmExpiredAlert">
  66. {{ t('go_to_login') }}
  67. </button>
  68. </div>
  69. </div>
  70. </div>
  71. <!-- 版本更新提醒弹窗 -->
  72. <div class="update-modal-overlay" v-if="showUpdateDialog">
  73. <div class="update-modal">
  74. <div class="update-modal-header">
  75. <div class="update-modal-badge">
  76. <template v-if="!updateInfo.hasUpdate">&#x2705; {{ t('update_no_update') }}</template>
  77. <span v-else-if="updateInfo.urgency === 'critical'" class="update-urgency-badge update-urgency-critical">{{ t('update_urgency_critical') }}</span>
  78. <span v-else-if="updateInfo.urgency === 'high'" class="update-urgency-badge update-urgency-high">{{ t('update_urgency_high') }}</span>
  79. <template v-else>&#x2728; {{ t('update_available_title') }}</template>
  80. </div>
  81. <h3 class="update-modal-title">{{ updateInfo.hasUpdate ? t('update_new_version', updateInfo.latestVersion) : t('update_no_update') }}</h3>
  82. <p class="update-modal-version">{{ t('update_current_version', currentPluginVersion) }}</p>
  83. <div class="update-modal-meta" v-if="updateInfo.hasUpdate">
  84. <span v-if="updateInfo.releaseDate">&#128197; {{ formatUpdateDate(updateInfo.releaseDate) }}</span>
  85. <span>&#128230; v{{ updateInfo.latestVersion }}</span>
  86. </div>
  87. </div>
  88. <div class="update-modal-body" v-if="updateInfo.hasUpdate">
  89. <div class="update-modal-changelog-label">{{ t('update_changelog') }}</div>
  90. <div class="update-modal-changelog">{{ updateInfo.changelog || t('update_changelog_empty') }}</div>
  91. </div>
  92. <div class="update-modal-footer" v-if="updateInfo.hasUpdate">
  93. <a class="update-btn-primary" :href="withUtm(updateInfo.downloadUrl || 'https://www.vberai.com/game-engines/cocos')" target="_blank" @click="dismissUpdate">
  94. &#x2B06;&#xFE0F; {{ t('update_now') }}
  95. </a>
  96. <div class="update-btn-row">
  97. <button class="update-btn-secondary" @click="dismissUpdate">
  98. {{ t('update_not_now') }}
  99. </button>
  100. <button class="update-btn-secondary update-btn-skip" @click="skipUpdateVersion">
  101. {{ t('update_skip_version', updateInfo.latestVersion) }}
  102. </button>
  103. </div>
  104. </div>
  105. <div class="update-modal-footer" v-else>
  106. <button class="update-btn-primary" @click="dismissUpdate">{{ t('update_not_now') }}</button>
  107. </div>
  108. </div>
  109. </div>
  110. <!-- 授权认证面板 -->
  111. <div class="auth-panel" v-if="showAuthPanel && !authDisabled">
  112. <div class="auth-brand">
  113. <img class="auth-logo" src="packages://cocos-mcp-server/static/icon.png" alt="Cocos MCP Server" />
  114. <div class="auth-brand-text">
  115. <div class="auth-brand-name">{{ t('brand_name') }}</div>
  116. <div class="auth-brand-subtitle">Cocos Creator MCP Server</div>
  117. </div>
  118. </div>
  119. <div class="auth-form">
  120. <!-- 登录方式已由下方 tab 指示,不再需要「选择登录方式」标题 -->
  121. <div class="auth-form-header">
  122. <select class="lang-select" :value="authLang" @change="onLangChange">
  123. <option v-for="loc in localeOptions" :key="loc.code" :value="loc.code">{{ loc.label }}</option>
  124. </select>
  125. </div>
  126. <!-- 订阅登录关闭时只剩激活码一种方式,不显示只有一个页签的 tab 栏 -->
  127. <div class="auth-tabs" v-if="subscriptionLoginEnabled">
  128. <button class="auth-tab" :class="{ 'auth-tab-active': authMode === 'subscription' }"
  129. @click="switchAuthMode('subscription')">{{ t('subscription_choose_button') }}</button>
  130. <button class="auth-tab" :class="{ 'auth-tab-active': authMode === 'license' }"
  131. @click="switchAuthMode('license')">{{ t('license_choose_button') }}</button>
  132. </div>
  133. <template v-if="authMode === 'license'">
  134. <p class="auth-subtitle">{{ t('auth_subtitle') }}</p>
  135. <p class="auth-store-hint">{{ t('store_purchase_hint') }}</p>
  136. <div class="auth-field">
  137. <label>{{ t('email_label') }}</label>
  138. <input class="auth-input" type="email" v-model="authEmail" :placeholder="t('email_placeholder')" />
  139. </div>
  140. <div class="auth-field">
  141. <label>{{ t('activation_code_label') }}</label>
  142. <input class="auth-input" type="text" v-model="authCode" :placeholder="t('code_placeholder')" />
  143. </div>
  144. <div class="auth-error" v-if="authError">{{ authError }}</div>
  145. <div class="auth-success" v-if="authSuccess">{{ authSuccess }}</div>
  146. <button class="auth-activate-button" @click="activateLicense" :disabled="authLoading">
  147. {{ authLoading ? t('activating') : t('activate_button') }}
  148. </button>
  149. <div class="auth-links">
  150. <a class="auth-link" :href="withUtm('https://www.vberai.com/game-engines/cocos')" target="_blank">
  151. {{ t('purchase_license') }}
  152. </a>
  153. </div>
  154. </template>
  155. <template v-else-if="authMode === 'subscription'">
  156. <div class="entitlement-state" v-if="subscriptionInfo.loggedIn && subscriptionInfo.denyCode">
  157. <div class="entitlement-account">
  158. <span class="entitlement-icon">⚠</span>
  159. <div><div class="entitlement-label">{{ t('subscription_logged_in') }}</div><div class="entitlement-email">{{ subscriptionInfo.email }}</div></div>
  160. </div>
  161. <div class="entitlement-message">{{ subscriptionError }}</div>
  162. <div class="entitlement-actions">
  163. <a class="entitlement-upgrade" :href="withUtm(subscriptionInfo.upgradeUrl || 'https://www.vberai.com/pricing')" target="_blank">{{ t('subscription_upgrade_action') }}</a>
  164. <button class="entitlement-logout" @click="logoutCurrentAuth">{{ t('logout_button') }}</button>
  165. </div>
  166. </div>
  167. <template v-else>
  168. <div class="auth-field">
  169. <label>{{ t('email_label') }}</label>
  170. <input class="auth-input" type="email" v-model="subscriptionEmail" :placeholder="t('email_placeholder')" />
  171. </div>
  172. <div class="auth-field">
  173. <label>{{ t('subscription_password_label') }}</label>
  174. <input class="auth-input" type="password" v-model="subscriptionPassword" :placeholder="t('subscription_password_placeholder')" @keyup.enter="loginSubscription" />
  175. </div>
  176. <div class="auth-error" v-if="subscriptionError">{{ subscriptionError }}</div>
  177. <div class="auth-success" v-if="subscriptionGooglePending">{{ t('subscription_google_pending') }}</div>
  178. <button class="auth-activate-button" @click="loginSubscription" :disabled="subscriptionLoading">
  179. {{ subscriptionLoading ? t('subscription_logging_in') : t('subscription_login_button') }}
  180. </button>
  181. <button class="auth-dismiss-button" style="width:100%; margin-top:8px" @click="loginSubscriptionGoogle" :disabled="subscriptionLoading">
  182. {{ subscriptionGooglePending ? t('subscription_google_pending_button') : t('subscription_google_button') }}
  183. </button>
  184. <div class="auth-links">
  185. <a class="auth-link" :href="withUtm((subscriptionInfo.webBase || 'https://test.vberai.com') + '/auth/register')" target="_blank">{{ t('subscription_register_button') }}</a>
  186. </div>
  187. </template>
  188. </template>
  189. <div class="auth-contact">
  190. <div class="auth-contact-item">📧 a549623165@gmail.com</div>
  191. <div class="auth-contact-item">💬 {{ t('contact_wechat') }}: lidaxian-AI</div>
  192. <div class="auth-contact-item">💬 客服微信: vberai-support</div>
  193. </div>
  194. <div class="auth-machine-id" v-if="machineId" style="margin-top: 10px; padding: 8px 12px; background: rgba(255,255,255,0.05); border-radius: 4px; font-size: 11px; color: #888; text-align: center;">
  195. <span>{{ t('machine_id_label') || '机器码' }}: </span>
  196. <span style="font-family: monospace; user-select: all; color: #aaa;">{{ machineId.length > 20 ? machineId.substring(0, 10) + '***' + machineId.substring(machineId.length - 10) : machineId }}</span>
  197. </div>
  198. </div>
  199. <div class="auth-footer">
  200. <span class="auth-version">v1.7.9</span>
  201. </div>
  202. </div>
  203. <!-- 主界面(授权通过后显示) -->
  204. <template v-if="!showAuthPanel || authDisabled">
  205. <!-- 顶部品牌区域 -->
  206. <div class="brand-top">
  207. <div class="brand-top-left">
  208. <img class="brand-top-logo" src="packages://cocos-mcp-server/static/icon.png" alt="Cocos MCP Server" />
  209. <div class="brand-top-text">
  210. <div class="brand-top-name">{{ t('brand_name') }}</div>
  211. <div class="brand-top-subtitle">Cocos Creator MCP Server v1.7.9</div>
  212. </div>
  213. </div>
  214. <div class="brand-top-right" v-if="isLicensed && (licenseInfo || subscriptionInfo.loggedIn) && !authDisabled">
  215. <div class="brand-right-row">
  216. <!-- 登录方式徽标:订阅优先(logoutCurrentAuth 也是先退订阅),据此决定顶栏显示哪套信息 -->
  217. <span class="brand-license-item brand-method-badge">{{ subscriptionInfo.loggedIn ? '👤 ' + t('subscription_method_label') : '🔑 ' + t('license_choose_button') }}</span>
  218. <span class="brand-license-divider">|</span>
  219. <span class="brand-license-item">📧 {{ getMaskedEmail }}</span>
  220. <!-- 剩余天数/许可证类型/变更激活码是激活码专属;订阅登录时不显示(订阅按用量计费,无固定到期天数) -->
  221. <template v-if="licenseInfo && !subscriptionInfo.loggedIn">
  222. <span class="brand-license-divider">|</span>
  223. <span class="brand-license-item">📋 {{ licenseInfo.licenseType }}</span>
  224. <span class="brand-license-divider">|</span>
  225. <span class="brand-license-item" :class="expiryColorClass">
  226. 📅 {{ licenseInfo.daysRemaining === 9999 ? t('permanent_label') : licenseInfo.daysRemaining + t('days_unit') }}
  227. </span>
  228. <a v-if="licenseInfo.daysRemaining <= 7 && licenseInfo.daysRemaining !== 9999"
  229. class="brand-renew-link" :href="withUtm('https://www.vberai.com/game-engines/cocos')" target="_blank">
  230. ⚠ {{ t('renew_now') }}
  231. </a>
  232. </template>
  233. </div>
  234. <!-- 邀请码功能暂时屏蔽
  235. <div class="brand-right-row" v-if="licenseInfo.inviteCode">
  236. <span class="brand-license-item" style="font-weight:bold; color:#5ad;">🎟️ {{ t('invite_code_label') }}: {{ licenseInfo.inviteCode }}</span>
  237. <button class="brand-action-btn" @click="copyInviteCode" style="margin-left:6px; font-size:11px;">{{ t('copy_button') }}</button>
  238. </div>
  239. -->
  240. <div class="brand-right-row">
  241. <button v-if="licenseInfo && !subscriptionInfo.loggedIn" class="brand-action-btn" @click="showChangeCodeModal = true">{{ t('change_code_button') }}</button>
  242. <!-- 订阅登录时常驻升级入口:随时可去官网升级订阅计划 -->
  243. <a v-if="subscriptionInfo.loggedIn" class="brand-action-btn brand-upgrade-btn" :href="withUtm(subscriptionInfo.upgradeUrl || 'https://www.vberai.com/pricing')" target="_blank">{{ t('subscription_upgrade_action') }}</a>
  244. <button class="brand-action-btn brand-logout-btn" @click="logoutCurrentAuth">{{ t('logout_button') }}</button>
  245. <select class="lang-select lang-select-sm" :value="authLang" @change="onLangChange">
  246. <option v-for="loc in localeOptions" :key="loc.code" :value="loc.code">{{ loc.label }}</option>
  247. </select>
  248. </div>
  249. </div>
  250. <div class="brand-top-right" v-else-if="!authDisabled">
  251. <select class="lang-select lang-select-sm" :value="authLang" @change="onLangChange">
  252. <option v-for="loc in localeOptions" :key="loc.code" :value="loc.code">{{ loc.label }}</option>
  253. </select>
  254. </div>
  255. </div>
  256. <!-- 选项卡导航 -->
  257. <div class="tab-navigation">
  258. <button class="tab-button" :class="{ active: activeTab === 'server' }" @click="switchTab('server')">
  259. <span>{{ t('server_tab') }}</span>
  260. </button>
  261. <button class="tab-button" :class="{ active: activeTab === 'tools' }" @click="switchTab('tools')">
  262. <span>{{ t('tools_tab') }}</span>
  263. </button>
  264. <button class="tab-button" :class="{ active: activeTab === 'config' }" @click="switchTab('config')">
  265. <span>{{ t('config_tab') }}</span>
  266. </button>
  267. </div>
  268. <!-- 服务器选项卡 -->
  269. <div class="tab-content" v-show="activeTab === 'server'">
  270. <section class="server-status">
  271. <h3>{{ t('server_status_title') }}</h3>
  272. <div class="status-info">
  273. <ui-prop>
  274. <ui-label slot="label">{{ t('status_label') }}</ui-label>
  275. <ui-label slot="content" class="status-value" :class="statusClass">{{ serverStatus }}</ui-label>
  276. </ui-prop>
  277. <ui-prop v-if="serverRunning">
  278. <ui-label slot="label">{{ t('connections_count') }}</ui-label>
  279. <ui-label slot="content">{{ connectedClients }}</ui-label>
  280. </ui-prop>
  281. </div>
  282. </section>
  283. <section class="server-controls">
  284. <ui-button @click="toggleServer" :disabled="isProcessing" class="primary">
  285. {{ serverRunning ? t('stop_server') : t('start_server') }}
  286. </ui-button>
  287. </section>
  288. <section class="server-settings">
  289. <h3>{{ t('server_settings_title') }}</h3>
  290. <div class="port-fallback-notice" v-if="portFallback">
  291. <div class="port-fallback-title">⚠ {{ t('port_fallback_title', portFallback.requestedPort, portFallback.actualPort) }}</div>
  292. <div class="port-fallback-body">{{ t('port_fallback_reason', portFallback.requestedPort) }}</div>
  293. <div class="port-fallback-body" v-if="portFallback.resyncedClients && portFallback.resyncedClients.length">
  294. {{ t('port_fallback_resynced', portFallback.resyncedClients.join('、')) }}
  295. </div>
  296. <div class="port-fallback-body" v-else>{{ t('port_fallback_no_clients') }}</div>
  297. <button class="auth-dismiss-button" @click="dismissPortFallback">{{ t('port_fallback_dismiss') }}</button>
  298. </div>
  299. <ui-prop>
  300. <ui-label slot="label">{{ t('transport_protocol') }}</ui-label>
  301. <ui-label slot="content" style="color: #007acc; font-weight: bold;">Streamable HTTP (MCP 2025-03-26)</ui-label>
  302. </ui-prop>
  303. <ui-prop>
  304. <ui-label slot="label">{{ t('port_label') }}</ui-label>
  305. <ui-num-input slot="content" v-model="settings.port" :min="1024" :max="65535" :step="1">
  306. </ui-num-input>
  307. </ui-prop>
  308. <ui-prop>
  309. <ui-label slot="label">{{ t('auto_start_label') }}</ui-label>
  310. <ui-checkbox slot="content" :value="settings.autoStart" @confirm="(e) => { settings.autoStart = e.target.value; }"></ui-checkbox>
  311. </ui-prop>
  312. <ui-prop>
  313. <ui-label slot="label">{{ t('debug_log_label') }}</ui-label>
  314. <ui-checkbox slot="content" :value="settings.debugLog" @confirm="(e) => { settings.debugLog = e.target.value; }"></ui-checkbox>
  315. </ui-prop>
  316. <ui-prop>
  317. <ui-label slot="label">{{ t('max_connections_label') }}</ui-label>
  318. <ui-num-input slot="content" v-model="settings.maxConnections" :min="1" :max="100" :step="1">
  319. </ui-num-input>
  320. </ui-prop>
  321. </section>
  322. <section class="server-info" v-if="serverRunning">
  323. <h3>{{ t('connection_info_title') }}</h3>
  324. <div class="connection-details">
  325. <ui-prop>
  326. <ui-label slot="label">{{ t('mcp_url') }}</ui-label>
  327. <ui-input slot="content" :value="httpUrl" readonly>
  328. <ui-button slot="suffix" @click="copyUrl">{{ t('copy') }}</ui-button>
  329. </ui-input>
  330. </ui-prop>
  331. <ui-prop>
  332. <ui-label slot="label">{{ t('transport_type') }}</ui-label>
  333. <ui-label slot="content">Streamable HTTP</ui-label>
  334. </ui-prop>
  335. </div>
  336. </section>
  337. <footer>
  338. <ui-button @click="saveSettings" :disabled="!settingsChanged">{{ t('save_settings') }}</ui-button>
  339. </footer>
  340. <!-- 品牌 & 版权信息区域 -->
  341. <section class="copyright-info">
  342. <div class="brand-header">
  343. <img class="brand-logo" src="packages://cocos-mcp-server/static/icon.png" alt="Cocos MCP Server" />
  344. <div class="brand-text">
  345. <div class="brand-name">{{ t('brand_name') }}</div>
  346. <div class="brand-subtitle">{{ t('brand_subtitle') }}</div>
  347. </div>
  348. </div>
  349. <div class="copyright-content">
  350. <div class="copyright-text">
  351. <div class="copyright-line">{{ t('copyright_line', '1.7.9') }}</div>
  352. <div class="copyright-line">{{ t('protocol_line') }}</div>
  353. </div>
  354. <div class="contact-info">
  355. <div class="contact-item">
  356. <span class="contact-label">📧 {{ t('contact_email') }}:</span>
  357. <span class="contact-value">a549623165@gmail.com</span>
  358. </div>
  359. <div class="contact-item">
  360. <span class="contact-label">💬 {{ t('contact_wechat') }}:</span>
  361. <span class="contact-value">lidaxian-AI</span>
  362. </div>
  363. <div class="contact-item">
  364. <span class="contact-label">💬 客服微信:</span>
  365. <span class="contact-value">vberai-support</span>
  366. </div>
  367. <div class="contact-item">
  368. <span class="contact-label">🐙 {{ t('contact_github') }}:</span>
  369. <span class="contact-value">https://github.com/DaxianLee</span>
  370. </div>
  371. </div>
  372. <div class="contact-info" style="margin-top: 6px;">
  373. <div class="contact-item">
  374. <span class="contact-label">🛒 {{ t('purchase_renew') }}:</span>
  375. <a class="contact-link" :href="withUtm('https://www.vberai.com/game-engines/cocos')" target="_blank">www.vberai.com/game-engines/cocos</a>
  376. </div>
  377. </div>
  378. <div style="margin-top: 8px; text-align: center;">
  379. <button class="brand-action-btn" @click="manualCheckUpdate" :disabled="updateChecking" style="font-size: 11px; padding: 3px 12px;">
  380. {{ updateChecking ? t('update_checking') : t('check_for_updates') }}
  381. </button>
  382. </div>
  383. <div v-if="updateCheckMessage" style="margin-top: 6px; text-align: center; font-size: 11px; color: #9ab;">{{ updateCheckMessage }}</div>
  384. <div v-if="machineId" style="margin-top: 8px; text-align: center; font-size: 11px; color: #888;">
  385. <span>{{ t('machine_id_label') || '机器码' }}: </span>
  386. <span style="font-family: monospace; user-select: all; color: #aaa;">{{ machineId.length > 20 ? machineId.substring(0, 10) + '***' + machineId.substring(machineId.length - 10) : machineId }}</span>
  387. </div>
  388. </div>
  389. </section>
  390. </div>
  391. <!-- 工具管理选项卡 -->
  392. <div class="tab-content" v-show="activeTab === 'tools'">
  393. <section class="tool-manager">
  394. <div class="tool-manager-header">
  395. <h3>{{ t('tool_management') }}</h3>
  396. </div>
  397. <div class="tools-section">
  398. <div class="tools-section-header">
  399. <div class="tools-section-title">
  400. <h4>{{ t('available_tools') }}</h4>
  401. <div class="tools-stats">
  402. {{ t('tools_stats', totalTools, enabledTools, disabledTools) }}
  403. </div>
  404. </div>
  405. <div class="tools-section-controls">
  406. <ui-button @click="selectAllTools" class="small">{{ t('select_all') }}</ui-button>
  407. <ui-button @click="deselectAllTools" class="small">{{ t('deselect_all') }}</ui-button>
  408. <ui-button @click="saveChanges" class="primary">{{ t('save_changes') }}</ui-button>
  409. </div>
  410. </div>
  411. <div class="tools-container">
  412. <div v-for="category in toolCategories" :key="category" class="tool-category">
  413. <div class="category-header">
  414. <h5>{{ getCategoryDisplayName(category) }}</h5>
  415. <div class="category-controls">
  416. <ui-button @click="toggleCategoryTools(category, true)" class="small">{{ t('select_all') }}</ui-button>
  417. <ui-button @click="toggleCategoryTools(category, false)" class="small">{{ t('deselect_all') }}</ui-button>
  418. </div>
  419. </div>
  420. <div class="tool-items">
  421. <div v-for="tool in getToolsByCategory(category)" :key="tool.name" class="tool-item">
  422. <ui-checkbox
  423. :value="tool.enabled"
  424. @change="(event) => updateToolStatus(category, tool.name, event.target.checked)"
  425. ></ui-checkbox>
  426. <div class="tool-info">
  427. <div class="tool-name">{{ tool.name }}</div>
  428. <div class="tool-description">{{ tool.description }}</div>
  429. </div>
  430. </div>
  431. </div>
  432. </div>
  433. </div>
  434. </div>
  435. </section>
  436. <!-- 工具管理选项卡版权信息 -->
  437. <section class="copyright-info">
  438. <div class="brand-header brand-compact">
  439. <img class="brand-logo" src="packages://cocos-mcp-server/static/icon.png" alt="Cocos MCP Server" />
  440. <div class="brand-name">{{ t('brand_name') }}</div>
  441. </div>
  442. <div class="copyright-content">
  443. <div class="copyright-text">
  444. <div class="copyright-line">🛠️ {{ t('tools_copyright_line1') }}</div>
  445. <div class="copyright-line">📧 a549623165@gmail.com | 💬 {{ t('contact_wechat') }}: lidaxian-AI | 💬 客服微信: vberai-support | 🛒 <a class="contact-link" :href="withUtm('https://www.vberai.com/game-engines/cocos')" target="_blank">{{ t('purchase_renew') }}</a></div>
  446. </div>
  447. </div>
  448. </section>
  449. </div>
  450. <!-- 一键配置选项卡 -->
  451. <div class="tab-content" v-show="activeTab === 'config'">
  452. <section class="quick-config">
  453. <div class="config-header">
  454. <h3>{{ t('quick_config_title') }}</h3>
  455. <p class="config-description">{{ t('quick_config_desc') }}</p>
  456. </div>
  457. <section class="server-info-section">
  458. <h4>{{ t('server_info') }}</h4>
  459. <ui-prop>
  460. <ui-label slot="label">{{ t('server_name_label') }}</ui-label>
  461. <ui-input slot="content" v-model="configServerName" placeholder="cocos-creator"></ui-input>
  462. </ui-prop>
  463. <ui-prop>
  464. <ui-label slot="label">{{ t('server_address') }}</ui-label>
  465. <ui-input slot="content" :value="configServerUrl" readonly></ui-input>
  466. </ui-prop>
  467. <ui-prop>
  468. <ui-label slot="label">{{ t('timeout_ms') }}</ui-label>
  469. <ui-num-input slot="content" v-model="configTimeout" :min="5000" :max="60000" :step="1000"></ui-num-input>
  470. </ui-prop>
  471. </section>
  472. <section class="ide-config-section">
  473. <div class="section-header">
  474. <h4>{{ t('auto_config_title') }}</h4>
  475. <div class="batch-actions">
  476. <ui-button @click="addToAllIDE" class="primary" :disabled="!serverRunning">{{ t('add_all') }}</ui-button>
  477. <ui-button @click="removeFromAllIDE" class="danger">{{ t('remove_all') }}</ui-button>
  478. </div>
  479. </div>
  480. <p class="section-note">{{ t('auto_config_note') }}</p>
  481. <div class="client-list">
  482. <div v-for="client in ideClients" :key="client.clientType" class="client-item">
  483. <div class="client-info">
  484. <div class="client-name">{{ client.clientName }}</div>
  485. <div class="client-path">{{ client.configPath }}</div>
  486. <div class="client-status" :class="client.exists ? 'exists' : 'not-exists'">
  487. {{ client.exists ? '✓ ' + t('configured') : '✗ ' + t('not_configured') }}
  488. </div>
  489. </div>
  490. <div class="client-actions">
  491. <ui-button @click="addToClient(client.clientType)" class="small" :disabled="client.exists">{{ t('auto_config_btn') }}</ui-button>
  492. <ui-button @click="removeFromClient(client.clientType)" class="small danger" :disabled="!client.exists">{{ t('delete_btn') }}</ui-button>
  493. <ui-button @click="copyClientConfig(client.clientType)" class="small">{{ t('copy_config') }}</ui-button>
  494. <ui-button @click="openConfigFile(client.configPath)" class="small">{{ t('open_config') }}</ui-button>
  495. </div>
  496. </div>
  497. </div>
  498. </section>
  499. <section class="cli-config-section">
  500. <div class="section-header">
  501. <h4>{{ t('manual_config_title') }}</h4>
  502. <ui-button @click="refreshCLICommands" class="small">{{ t('refresh_commands') }}</ui-button>
  503. </div>
  504. <p class="section-note">{{ t('manual_config_note') }}</p>
  505. <div class="scope-selector" style="margin-bottom: 15px; display: flex; gap: 10px; align-items: center;">
  506. <span style="color: #aaa;">{{ t('config_scope') }}</span>
  507. <ui-button
  508. @click="cliScope = 'user'"
  509. :class="cliScope === 'user' ? 'primary' : ''"
  510. class="small">
  511. {{ t('scope_user') }}
  512. </ui-button>
  513. <ui-button
  514. @click="cliScope = 'project'"
  515. :class="cliScope === 'project' ? 'primary' : ''"
  516. class="small">
  517. {{ t('scope_project') }}
  518. </ui-button>
  519. </div>
  520. <div class="cli-commands">
  521. <div class="cli-item">
  522. <div class="cli-name">Claude CLI</div>
  523. <div class="cli-command-box">
  524. <pre>{{ cliCommands.claude }}</pre>
  525. <ui-button @click="copyCLICommand('claude')" class="small">{{ t('copy') }}</ui-button>
  526. </div>
  527. </div>
  528. <div class="cli-item">
  529. <div class="cli-name">Gemini CLI</div>
  530. <div class="cli-command-box">
  531. <pre>{{ cliCommands.gemini }}</pre>
  532. <ui-button @click="copyCLICommand('gemini')" class="small">{{ t('copy') }}</ui-button>
  533. </div>
  534. </div>
  535. </div>
  536. </section>
  537. <section class="config-log">
  538. <h4>{{ t('operation_log') }}</h4>
  539. <div class="log-container">
  540. <div v-for="(log, index) in configLogs" :key="index" class="log-item" :class="log.type">
  541. <span class="log-time">{{ log.time }}</span>
  542. <span class="log-message">{{ log.message }}</span>
  543. </div>
  544. <div v-if="configLogs.length === 0" class="log-empty">{{ t('no_logs') }}</div>
  545. </div>
  546. </section>
  547. </section>
  548. <!-- 一键配置选项卡版权信息 -->
  549. <section class="copyright-info">
  550. <div class="brand-header brand-compact">
  551. <img class="brand-logo" src="packages://cocos-mcp-server/static/icon.png" alt="Cocos MCP Server" />
  552. <div class="brand-name">{{ t('brand_name') }}</div>
  553. </div>
  554. <div class="copyright-content">
  555. <div class="copyright-text">
  556. <div class="copyright-line">⚡ {{ t('config_copyright_line1') }}</div>
  557. <div class="copyright-line">📧 a549623165@gmail.com | 💬 {{ t('contact_wechat') }}: lidaxian-AI | 💬 客服微信: vberai-support | 🛒 <a class="contact-link" :href="withUtm('https://www.vberai.com/game-engines/cocos')" target="_blank">{{ t('purchase_renew') }}</a></div>
  558. </div>
  559. </div>
  560. </section>
  561. </div>
  562. </template><!-- /主界面 -->
  563. </div>