| name | webui-tester |
| description | Test, debug, and modify Majsoul WebUI (Laya engine) within the Naki app. Use when the user asks to adjust "the screen" or "UI" - clarify whether they mean Naki's SwiftUI app or Majsoul's WebUI. This skill handles WebUI JavaScript execution, tile manipulation, and visual debugging. |
| allowed-tools | Read, Glob, Grep, Task |
**MCP 調用規則**: 所有 Naki MCP 工具必須透過 `/naki-mcp-proxy` skill 調用,禁止直接調用 `mcp__naki__*` 工具。
WebUI Tester Skill
This skill helps test and modify Majsoul's WebUI (game interface) running inside Naki's WKWebView.
🆕 NakiCoordinator - 統一協調器 (推薦)
Naki 提供了統一的 JavaScript 協調器,優先使用它而非直接訪問遊戲物件:
window.naki === window.NakiCoordinator
naki.state.isInGame()
naki.state.canExecuteAction()
naki.state.getFullState()
naki.state.getHandInfo()
naki.state.getAvailableOps()
naki.action.discard(tileIndex, {verify: true})
naki.action.pass({useBuiltin: true})
naki.action.chi(combIndex)
naki.action.pon()
naki.action.hora({useBuiltin: true})
naki.action.execute('pass', {})
naki.auto.setHule(true)
naki.auto.setNoFulu(true)
naki.auto.setMoqie(true)
naki.debug.getDiagnostics()
naki.debug.listMethods()
完整 API: 見 references/api-architecture.md
Critical Distinction: WebUI vs App UI
When the user says "adjust the screen" or "modify UI", ALWAYS clarify:
| Term | Meaning | Technology | How to Modify |
|---|
| WebUI | Majsoul game interface | Laya 3D Engine (JavaScript) | mcp__naki__execute_js |
| App UI | Naki application interface | SwiftUI | Edit Swift files |
Ask the user: "Do you mean the Majsoul game screen (WebUI) or the Naki app interface (App UI)?"
MCP Tool Pitfalls
1. execute_js MUST use return statement
CRITICAL: When using mcp__naki__execute_js, the JavaScript code MUST include a return statement to get results back.
mcp__naki__execute_js({ code: "document.title" })
mcp__naki__execute_js({ code: "return document.title" })
Why: The code is wrapped in a function, so without return, the result is lost.
2. Tile Index Mapping - Critical Pitfall
NEVER use Swift's tehai array index to find tiles in WebUI!
The Problem:
- Swift's
tehai array is sorted: tehai.sort { $0.index < $1.index }
- Majsoul's UI displays tiles in visual order (NOT sorted by index)
- Using
tehai[i] index will click the WRONG tile
Correct Approach (from WebViewModel.swift:226-266):
const typeMap = {'m': 1, 'p': 0, 's': 2};
const honorMap = {
'E': [3,1], 'S': [3,2], 'W': [3,3], 'N': [3,4],
'P': [3,5], 'F': [3,6], 'C': [3,7]
};
WebUI Object Reference
Key Majsoul/Laya objects accessible via JavaScript:
window.view.DesktopMgr.Inst
const mr = window.view.DesktopMgr.Inst.mainrole
mr.hand[]
tile.val.type
tile.val.index
tile.val.dora
tile._doraeffect
tile._recommendeffect
tile.effect_recommend
See @docs/majsoul-webui-objects-reference.md for complete reference.
Common WebUI Debugging Commands
Check Game State
return JSON.stringify({
bakaze: window.view.DesktopMgr.Inst.gameing_state?.bakaze,
kyoku: window.view.DesktopMgr.Inst.gameing_state?.kyoku
});
Get Hand Tiles
const mr = window.view.DesktopMgr.Inst.mainrole;
const tiles = mr.hand.map((t, i) => ({
index: i,
type: t.val?.type,
num: t.val?.index,
dora: t.val?.dora
}));
return JSON.stringify(tiles);
Find Specific Tile Position
const mr = window.view.DesktopMgr.Inst.mainrole;
for (let i = 0; i < mr.hand.length; i++) {
const t = mr.hand[i];
if (t.val?.type === 1 && t.val?.index === 5) {
return JSON.stringify({found: true, index: i, pos: t.transform?.position});
}
}
return JSON.stringify({found: false});
Check Player Names
const dm = window.view.DesktopMgr.Inst;
const names = dm.players?.map(p => p.character?.charid) || [];
return JSON.stringify(names);
Toggle Visual Effect
const mr = window.view.DesktopMgr.Inst.mainrole;
if (mr.hand[0]?.effect_recommend) {
mr.hand[0].effect_recommend.active = !mr.hand[0].effect_recommend.active;
}
return "toggled";
Workflow for WebUI Testing
-
Verify game is running:
mcp__naki__game_state
-
Get current hand info:
mcp__naki__game_hand
-
Execute test JavaScript (always use return!):
mcp__naki__execute_js({ code: "return ..." })
-
Check logs for errors:
mcp__naki__get_logs
Checklist Before WebUI Modification
Error Handling
If execute_js returns null or undefined:
- Check if game page is loaded
- Verify JavaScript has
return statement
- Check for JavaScript errors in logs
- Ensure object path exists (use optional chaining
?.)
Reference Documentation
For complete Majsoul WebUI object documentation, see:
NakiCoordinator 優先使用指南
| 任務 | 舊方法 | 新方法 (推薦) |
|---|
| 檢查遊戲狀態 | view.DesktopMgr.Inst.gameing | naki.state.isInGame() |
| 獲取手牌 | dm.mainrole.hand | naki.state.getHandInfo() |
| 執行打牌 | mr.setChoosePai(); mr.DoDiscardTile() | naki.action.discard(idx) |
| 執行 pass | 手動發送網路請求 | naki.action.pass() |
| 驗證動作 | 自己寫輪詢 | naki.action.execute('pass', {verify: true}) |