You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
18 KiB
314 lines
18 KiB
<html>
|
|
<head>
|
|
<link rel="icon" href="./favicon.ico" />
|
|
<meta charset="utf-8" />
|
|
<title><%=title%></title>
|
|
<meta
|
|
name="viewport"
|
|
content="width=device-width,user-scalable=no,initial-scale=1,minimum-scale=1,maximum-scale=1,minimal-ui=true"
|
|
/>
|
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
<meta name="mobile-web-app-capable" content="yes">
|
|
<meta name="full-screen" content="yes" />
|
|
<meta name="screen-orientation" content="portrait" />
|
|
<meta name="x5-fullscreen" content="true" />
|
|
<meta name="360-fullscreen" content="true" />
|
|
<meta name="renderer" content="webkit" />
|
|
<meta name="force-rendering" content="webkit" />
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
|
|
|
<link rel="stylesheet" type="text/css" href="./index.css" />
|
|
<link rel="stylesheet" type="text/css" href="/workbench/css/workbench.css" />
|
|
<script>
|
|
(function () {
|
|
var m = new URLSearchParams(location.search).get('pw');
|
|
// iframe 内一律按游戏页处理,禁止再套壳(否则双引擎/无限嵌套会打崩 Electron)
|
|
if (window.parent !== window) {
|
|
m = 'game';
|
|
}
|
|
window.__PW_MODE = m === 'game' ? 'game' : 'shell';
|
|
document.documentElement.setAttribute('data-pw', window.__PW_MODE);
|
|
})();
|
|
</script>
|
|
</head>
|
|
<body style="overflow: hidden;">
|
|
<%- include(cocosToolBar, {config: config}) %>
|
|
<div id="pw-root" class="pw-root pw-mode-desktop">
|
|
<div id="pw-toolbar" class="pw-toolbar pw-shell-only">
|
|
<div class="pw-toolbar-left" title="菜单">
|
|
<span class="pw-brand">调试工作台</span>
|
|
<div class="pw-btn-group" role="group" aria-label="面板">
|
|
<button type="button" class="pw-btn pw-panel-btn" data-panel="hierarchy">层级</button>
|
|
<button type="button" class="pw-btn pw-panel-btn" data-panel="inspector">属性</button>
|
|
<button type="button" class="pw-btn pw-panel-btn" data-panel="assets">资源</button>
|
|
<button type="button" class="pw-btn pw-panel-btn" data-panel="perf">性能</button>
|
|
<button type="button" class="pw-btn pw-panel-btn" data-panel="console">控制台</button>
|
|
</div>
|
|
</div>
|
|
<div class="pw-toolbar-center" title="设备与快捷">
|
|
<select id="pw-device-select" class="pw-select" title="设备"></select>
|
|
<button type="button" id="pw-device-rotate" class="pw-btn pw-btn-icon" title="旋转" aria-label="旋转">
|
|
<svg class="pw-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 2a6 6 0 1 0 5.7 4.2" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M13 2v4h-4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
</button>
|
|
<button type="button" id="pw-game-refresh" class="pw-btn pw-btn-icon" title="刷新并重启游戏" aria-label="刷新">
|
|
<svg class="pw-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M13.5 8a5.5 5.5 0 1 1-1.6-3.9" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M13.5 2.5v3.2h-3.2" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
</button>
|
|
<button type="button" id="pw-show-fps" class="pw-btn" title="显示 FPS 浮窗" aria-pressed="false">FPS</button>
|
|
<button type="button" id="pw-game-pause" class="pw-btn">暂停</button>
|
|
<button type="button" id="pw-game-step" class="pw-btn pw-btn-icon" title="暂停时推进一帧" aria-label="按帧执行">
|
|
<svg class="pw-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 3.5 10 8l-7 4.5V3.5z" fill="currentColor"/><path d="M11.5 3.5h2v9h-2z" fill="currentColor"/></svg>
|
|
</button>
|
|
<button type="button" id="pw-pure-measure" class="pw-btn" title="关闭全部探针,测真实帧率" aria-pressed="false">纯净测帧</button>
|
|
<div class="pw-mode-toggle" role="group" aria-label="输入模式">
|
|
<button type="button" id="pw-mode-game" class="pw-btn active" data-mode="game">游戏</button>
|
|
<button type="button" id="pw-mode-inspect" class="pw-btn" data-mode="inspect">检视</button>
|
|
</div>
|
|
</div>
|
|
<div class="pw-toolbar-right" title="状态">
|
|
<div class="pw-live-stats" id="pw-live-stats" title="游戏实时帧率与占用内存">
|
|
<span id="pw-stat-fps" class="pw-stat">FPS --</span>
|
|
<span class="pw-stat-sep">·</span>
|
|
<span id="pw-stat-mem" class="pw-stat">内存 --</span>
|
|
</div>
|
|
<span id="pw-engine-status" class="pw-status">引擎等待中…</span>
|
|
<div class="pw-btn-group" role="group" aria-label="布局">
|
|
<select id="pw-layout-preset" class="pw-select" title="布局预设">
|
|
<option value="analyze">分析(按横竖屏)</option>
|
|
<option value="portrait">竖屏分析</option>
|
|
<option value="landscape">横屏分析</option>
|
|
<option value="scene">场景</option>
|
|
<option value="minimal">精简</option>
|
|
</select>
|
|
<button type="button" id="pw-layout-reset" class="pw-btn pw-btn-icon" title="重置布局" aria-label="重置布局">
|
|
<svg class="pw-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M3 4h10M3 8h10M3 12h6" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/><path d="M12.5 10.5 14 12l-1.5 1.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
</button>
|
|
</div>
|
|
<button type="button" id="pw-workbench-toggle" class="pw-btn active" title="开启/关闭调试工作台(关闭后仍保留设备选项)" aria-pressed="true">工作台</button>
|
|
</div>
|
|
</div>
|
|
<div id="pw-workspace" class="pw-workspace pw-shell-only"></div>
|
|
<script>
|
|
(function () {
|
|
if (window.__PW_MODE === 'game') {
|
|
document.write(
|
|
'<div id="pw-game-host" class="pw-game-host">' +
|
|
'<div id="pw-center" class="pw-center">' +
|
|
'<div id="content" class="content" style="overflow: hidden;">' +
|
|
'<div class="contentWrap">' +
|
|
'<div id="GameDiv" class="wrapper">' +
|
|
'<div id="Cocos3dGameContainer">' +
|
|
'<canvas id="GameCanvas" tabindex="-1" style="background-color: \'\';"></canvas>' +
|
|
'</div>' +
|
|
'<div id="splash"><div class="progress-bar stripes"><span></span></div></div>' +
|
|
'<div id="bulletin"><div id="sceneIsEmpty" class="inner"><%=tip_sceneIsEmpty%></div></div>' +
|
|
'<div class="error" id="error"><div class="title">Error <i>(Please open the console to see detailed errors)</i></div>' +
|
|
'<div class="error-main"></div><div class="error-stack"></div></div>' +
|
|
'</div></div></div>' +
|
|
'<div id="pw-pick-overlay" class="pw-pick-overlay" hidden></div>' +
|
|
'</div></div>'
|
|
);
|
|
} else {
|
|
// 壳页:不加载引擎,只挂 iframe 宿主 + 检视点选捕获层
|
|
document.write(
|
|
'<div id="pw-game-host" class="pw-game-host pw-game-iframe-host">' +
|
|
'<div id="pw-pick-capture" class="pw-pick-capture pw-shell-only" hidden title="检视:点击选取节点"></div>' +
|
|
'</div>'
|
|
);
|
|
}
|
|
})();
|
|
</script>
|
|
<div id="pw-drop-overlay" class="pw-drop-overlay pw-shell-only" hidden>
|
|
<div class="pw-drop-edge pw-drop-e-center" data-edge="center">合并到此区域</div>
|
|
<div class="pw-drop-edge pw-drop-e-left" data-edge="left">左侧新建</div>
|
|
<div class="pw-drop-edge pw-drop-e-right" data-edge="right">右侧新建</div>
|
|
<div class="pw-drop-edge pw-drop-e-top" data-edge="top">上方新建</div>
|
|
<div class="pw-drop-edge pw-drop-e-bottom" data-edge="bottom">下方新建</div>
|
|
</div>
|
|
<div id="pw-compact-bar" class="pw-compact-bar pw-shell-only">
|
|
<button type="button" class="pw-btn pw-panel-btn" data-panel="perf">性能</button>
|
|
<button type="button" class="pw-btn pw-panel-btn" data-panel="assets">资源</button>
|
|
<button type="button" class="pw-btn pw-panel-btn" data-panel="hierarchy">层级</button>
|
|
<button type="button" class="pw-btn pw-panel-btn" data-panel="inspector">属性</button>
|
|
<button type="button" class="pw-btn pw-panel-btn" data-panel="console">控制台</button>
|
|
<button type="button" id="pw-show-fps-compact" class="pw-btn" title="显示 FPS 浮窗" aria-pressed="false">FPS</button>
|
|
<button type="button" id="pw-game-step-compact" class="pw-btn" title="暂停时推进一帧">按帧执行</button>
|
|
</div>
|
|
<div id="pw-drawer" class="pw-drawer pw-shell-only" hidden>
|
|
<div class="pw-drawer-head">
|
|
<span id="pw-drawer-title">面板</span>
|
|
<button type="button" id="pw-drawer-close" class="pw-btn">关闭</button>
|
|
</div>
|
|
<div id="pw-drawer-body" class="pw-drawer-body"></div>
|
|
</div>
|
|
</div>
|
|
<!--
|
|
关键:壳页绝不能执行 cocosTemplate(双引擎会打崩 Electron/Cursor)。
|
|
放进 <template> 解析时不运行;仅 game 模式按序激活。
|
|
-->
|
|
<template id="pw-cocos-boot">
|
|
<%- include(cocosTemplate, {}) %>
|
|
</template>
|
|
<script>
|
|
(function () {
|
|
if (window.__PW_MODE !== 'game') return;
|
|
|
|
// cocosTemplate 用 window.onload 启动;动态激活时 load 常已错过,必须补跑且只跑一次
|
|
(function patchOnLoad() {
|
|
var ran = false;
|
|
function run(fn) {
|
|
if (ran || typeof fn !== 'function') return;
|
|
ran = true;
|
|
try { fn.call(window); } catch (e) { console.error('[调试工作台] onload 引导失败', e); }
|
|
}
|
|
try {
|
|
Object.defineProperty(window, 'onload', {
|
|
configurable: true,
|
|
enumerable: true,
|
|
get: function () { return null; },
|
|
set: function (fn) {
|
|
if (typeof fn !== 'function') return;
|
|
if (document.readyState === 'complete') {
|
|
setTimeout(function () { run(fn); }, 0);
|
|
} else {
|
|
window.addEventListener('load', function () { run(fn); });
|
|
}
|
|
},
|
|
});
|
|
} catch (e) {
|
|
console.warn('[调试工作台] 无法劫持 onload', e);
|
|
}
|
|
})();
|
|
|
|
var tpl = document.getElementById('pw-cocos-boot');
|
|
if (!tpl || !tpl.content) {
|
|
console.error('[调试工作台] 缺少 #pw-cocos-boot');
|
|
return;
|
|
}
|
|
var nodes = [];
|
|
var child = tpl.content.firstChild;
|
|
while (child) {
|
|
nodes.push(child);
|
|
child = child.nextSibling;
|
|
}
|
|
var i = 0;
|
|
var finished = false;
|
|
function bootWorkbench() {
|
|
if (finished) return;
|
|
finished = true;
|
|
window.__PW_ENGINE_BOOT_DONE = true;
|
|
var files = [
|
|
'/workbench/js/idle.js',
|
|
'/workbench/js/perf-sampler.js',
|
|
'/workbench/js/markers.js',
|
|
'/workbench/js/device.js',
|
|
'/workbench/js/agent-util.js',
|
|
'/workbench/js/agent-core.js',
|
|
'/workbench/js/agent-serialize.js',
|
|
'/workbench/js/agent-pick.js',
|
|
'/workbench/js/agent-console.js',
|
|
'/workbench/js/agent-assets.js',
|
|
'/workbench/js/agent.js',
|
|
];
|
|
var fi = 0;
|
|
function nextWb() {
|
|
if (fi >= files.length) return;
|
|
var el = document.createElement('script');
|
|
el.src = files[fi++];
|
|
el.onload = nextWb;
|
|
el.onerror = function () {
|
|
console.warn('[调试工作台] 探针脚本加载失败', el.src);
|
|
nextWb();
|
|
};
|
|
document.body.appendChild(el);
|
|
}
|
|
nextWb();
|
|
}
|
|
function next() {
|
|
if (i >= nodes.length) {
|
|
bootWorkbench();
|
|
return;
|
|
}
|
|
var node = nodes[i++];
|
|
if (!node) {
|
|
next();
|
|
return;
|
|
}
|
|
if (node.nodeName === 'SCRIPT') {
|
|
var s = document.createElement('script');
|
|
var src = node.getAttribute('src');
|
|
var typ = node.getAttribute('type') || '';
|
|
for (var a = 0; a < node.attributes.length; a++) {
|
|
var attr = node.attributes[a];
|
|
if (attr.name === 'src') continue;
|
|
s.setAttribute(attr.name, attr.value);
|
|
}
|
|
if (src) {
|
|
s.async = false;
|
|
var done = false;
|
|
var cont = function () {
|
|
if (done) return;
|
|
done = true;
|
|
next();
|
|
};
|
|
s.onload = cont;
|
|
s.onerror = function () {
|
|
console.warn('[调试工作台] 引擎脚本加载失败', src);
|
|
cont();
|
|
};
|
|
// importmap 常不触发 onload,必须推进,否则游戏永远黑屏
|
|
var isImportMap = typ.indexOf('importmap') >= 0;
|
|
s.src = src;
|
|
document.body.appendChild(s);
|
|
setTimeout(cont, isImportMap ? 0 : 8000);
|
|
return;
|
|
}
|
|
s.text = node.text || node.textContent || '';
|
|
document.body.appendChild(s);
|
|
next();
|
|
return;
|
|
}
|
|
try {
|
|
document.body.appendChild(node.cloneNode(true));
|
|
} catch (e) {
|
|
console.warn('[调试工作台] 挂载节点失败', e);
|
|
}
|
|
next();
|
|
}
|
|
console.info('[调试工作台] 游戏页开始激活引擎脚本', nodes.length);
|
|
next();
|
|
})();
|
|
</script>
|
|
<script>
|
|
(function () {
|
|
if (window.__PW_MODE === 'game') return;
|
|
var files = [
|
|
'/workbench/js/idle.js',
|
|
'/workbench/js/charts/ring-chart.js',
|
|
'/workbench/js/bridge.js',
|
|
'/workbench/js/panels/console.js',
|
|
'/workbench/js/panels/hierarchy.js',
|
|
'/workbench/js/panels/inspector.js',
|
|
'/workbench/js/panels/assets.js',
|
|
'/workbench/js/panels/perf.js',
|
|
'/workbench/js/layout-core.js',
|
|
'/workbench/js/layout-presets.js',
|
|
'/workbench/js/layout-render.js',
|
|
'/workbench/js/layout-dock.js',
|
|
'/workbench/js/layout-tree.js',
|
|
'/workbench/js/layout-float.js',
|
|
'/workbench/js/host.js',
|
|
];
|
|
var i = 0;
|
|
function next() {
|
|
if (i >= files.length) return;
|
|
var el = document.createElement('script');
|
|
el.src = files[i++];
|
|
el.onload = next;
|
|
el.onerror = next;
|
|
document.body.appendChild(el);
|
|
}
|
|
next();
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|