🎮ArcadeLab

安全实验场 Pro V2

by EpicCoder88
455 lines23.3 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>安全实验场 Pro V2</title>
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  :root {
    --bg: #0d1117; --panel: #161b22; --border: #30363d;
    --text: #e6edf3; --text-dim: #8b949e;
    --accent: #58a6ff; --green: #3fb950; --red: #f85149; --purple: #bc8cff;
    --radius: 12px; --radius-sm: 8px;
    --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  }
  html, body {
    height: 100%; font-family: -apple-system, sans-serif;
    background: var(--bg); color: var(--text); overflow: hidden;
  }

  /* ================= 桌面端布局 ================= */
  .app {
    display: grid;
    grid-template-rows: auto 1fr auto;
    grid-template-columns: minmax(0, 1fr) 360px;
    grid-template-areas:
      "header header"
      "stage  logs"
      "editor editor";
    height: 100vh; gap: 8px; padding: 8px;
  }

  /* ================= 移动端布局 ================= */
  @media (max-width: 860px) {
    .app {
      display: flex; flex-direction: column;
      height: 100vh; gap: 6px; padding: 6px;
    }
    .header { flex-shrink: 0; }
    .stage { flex: 1; min-height: 0; }
    .logs { flex-shrink: 0; max-height: 22vh; }
    .editor { flex-shrink: 0; max-height: 24vh; }
    
    .panel-toggle { display: flex !important; }
    .hide-mobile { display: none !important; }
  }

  /* ================= 顶栏 ================= */
  .header {
    display: flex; align-items: center; gap: 8px;
    padding: 10px 12px; background: var(--panel);
    border: 1px solid var(--border); border-radius: var(--radius);
    flex-wrap: wrap;
  }
  .brand { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 15px; margin-right: 6px; }
  .dot { width: 10px; height: 10px; border-radius: 50%; background: var(--green); box-shadow: 0 0 10px var(--green); animation: pulse 2s infinite; }
  @keyframes pulse { 0%,100%{opacity:1;} 50%{opacity:.45;} }
  .btn {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 7px 12px; font-size: 13px; font-weight: 600;
    border: 1px solid var(--border); border-radius: var(--radius-sm);
    background: var(--panel-2); color: var(--text); cursor: pointer;
    transition: all .15s; white-space: nowrap;
  }
  .btn:hover { transform: translateY(-1px); background: #22272e; }
  .btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
  .btn.green { background: var(--green); border-color: var(--green); color: #fff; }
  .btn.red { background: var(--red); border-color: var(--red); color: #fff; }
  .btn.purple { background: var(--purple); border-color: var(--purple); color: #1a1a1a; }
  .btn.ghost { background: transparent; }
  .panel-toggle { display: none; }

  /* ================= 沙箱区 ================= */
  .stage {
    position: relative; border-radius: var(--radius);
    overflow: hidden; background: #f6f8fa;
    border: 1px solid var(--border);
    box-shadow: 0 8px 30px rgba(0,0,0,.35);
    min-height: 0; transition: all .2s;
  }
  .stage:fullscreen { border-radius: 0; border: none; }
  .stage::before {
    content: "沙箱运行区"; position: absolute; top: 8px; left: 12px;
    z-index: 2; font-size: 11px; font-weight: 600; color: #57606a;
    background: rgba(255,255,255,.75); padding: 3px 8px;
    border-radius: 999px; backdrop-filter: blur(4px); pointer-events: none;
  }
  .stage iframe { width: 100%; height: 100%; border: 0; display: block; background: #fff; }

  /* ================= 日志面板 ================= */
  .logs {
    display: flex; flex-direction: column; background: var(--panel);
    border: 1px solid var(--border); border-radius: var(--radius);
    overflow: hidden; min-height: 0;
  }
  .logs-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 10px 14px; border-bottom: 1px solid var(--border);
    font-size: 12px; font-weight: 600; color: var(--text-dim); letter-spacing: .5px;
  }
  .logs-body {
    flex: 1; overflow-y: auto; padding: 8px; font-family: var(--mono);
    font-size: 12px; line-height: 1.5;
  }
  .logs-body::-webkit-scrollbar { width: 6px; }
  .logs-body::-webkit-scrollbar-thumb { background: #30363d; border-radius: 3px; }
  .log-item { display: flex; gap: 6px; padding: 4px 6px; border-radius: 4px; margin-bottom: 2px; }
  .log-time { color: #484f58; flex-shrink: 0; font-size: 11px; }
  .log-tag { flex-shrink: 0; font-size: 10px; font-weight: 700; padding: 1px 5px; border-radius: 3px; }
  .log-tag.info { background: rgba(88,166,255,.15); color: var(--accent); }
  .log-tag.ok { background: rgba(63,185,80,.15); color: var(--green); }
  .log-tag.warn { background: rgba(210,153,34,.15); color: #d29922; }
  .log-tag.error { background: rgba(248,81,73,.15); color: var(--red); }

  /* ================= 编辑器 ================= */
  .editor {
    display: flex; flex-direction: column; background: var(--panel);
    border: 1px solid var(--border); border-radius: var(--radius);
    padding: 10px; gap: 6px; height: 220px; min-height: 0;
  }
  .editor-head { display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: var(--text-dim); font-weight: 600; }
  .editor textarea {
    flex: 1; width: 100%; resize: none; background: var(--bg); color: var(--text);
    border: 1px solid var(--border); border-radius: var(--radius-sm);
    padding: 10px; font-family: var(--mono); font-size: 12.5px; line-height: 1.5; outline: none;
  }
  .editor textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(88,166,255,.12); }
  .editor-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
</style>
</head>
<body>

<div class="app">
  <!-- 顶栏 -->
  <header class="header">
    <div class="brand"><span class="dot"></span>安全实验场 <span style="font-size:11px;color:var(--text-dim);font-weight:500;">V2</span></div>
    <button class="btn primary" id="btnReload">⟳ 重载</button>
    <button class="btn red" id="btnTest1">测试1 · 原始弹窗</button>
    <button class="btn red" id="btnTest2">测试2 · iframe 绕过</button>
    <button class="btn purple" id="btnTest3">测试3 · Shadow DOM</button>
    <button class="btn green" id="btnTest4">测试4 · 文本显示</button>
    <button class="btn ghost" id="btnClearLog">清空日志</button>
    
    <!-- 移动端专属按钮 -->
    <button class="btn ghost panel-toggle" id="btnToggleLogs">📋 日志</button>
    <button class="btn ghost panel-toggle" id="btnToggleEditor">💻 代码</button>
    <button class="btn primary panel-toggle" id="btnFullscreen">⛶ 全屏</button>
  </header>

  <!-- 沙箱 -->
  <main class="stage" id="stage">
    <iframe id="sandboxFrame" sandbox="allow-scripts allow-same-origin allow-downloads allow-forms"></iframe>
  </main>

  <!-- 日志(移动端可折叠) -->
  <aside class="logs" id="logsPanel">
    <div class="logs-head">
      <span>运行日志</span>
      <span id="logCount">0</span>
    </div>
    <div class="logs-body" id="console"></div>
  </aside>

  <!-- 编辑器(移动端可折叠) -->
  <section class="editor" id="editorPanel">
    <div class="editor-head">
      <span>代码注入</span>
      <span id="statusBadge">沙箱就绪</span>
    </div>
    <textarea id="codeInput" placeholder="在此粘贴 HTML / JS..."></textarea>
    <div class="editor-actions">
      <button class="btn green" id="btnInject">▶ 注入运行</button>
      <button class="btn ghost" id="btnWrapDoc">包裹为完整文档</button>
    </div>
  </section>
</div>

<script>
  // ================== 基础工具 ==================
  const $ = id => document.getElementById(id);
  const consoleEl = $('console');
  let logCount = 0;

  function log(msg, type = 'info') {
    const t = new Date().toTimeString().slice(0, 8) + '.' + String(new Date().getMilliseconds()).padStart(3, '0');
    const tagMap = { info: '信息', ok: '成功', warn: '警告', error: '错误' };
    const item = document.createElement('div');
    item.className = 'log-item';
    item.innerHTML = `<span class="log-time">${t}</span><span class="log-tag ${type}">${tagMap[type]}</span><span>${msg}</span>`;
    consoleEl.appendChild(item);
    consoleEl.scrollTop = consoleEl.scrollHeight;
    logCount++; $('logCount').textContent = logCount;
    if (consoleEl.children.length > 200) consoleEl.removeChild(consoleEl.firstChild);
  }

  // ================== 沙箱 ==================
  const frame = $('sandboxFrame');
  const statusBadge = $('statusBadge');

  function resetSandbox() {
    frame.srcdoc = `<!DOCTYPE html>
<html><head><meta charset="UTF-8"><style>
  html,body{margin:0;height:100%;font-family:-apple-system,sans-serif;background:#f6f8fa;color:#57606a;}
  .tip{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:8px;font-size:14px;}
  .tip .icon{font-size:38px;opacity:.35;}
</style></head><body>
  <div class="tip"><div class="icon">◻</div><div>沙箱待命中</div><div style="font-size:12px;color:#8b949e;">点击上方测试按钮,或在下方注入代码</div></div>
</body></html>`;
    statusBadge.textContent = '沙箱已重载';
    log('沙箱已重载', 'ok');
  }

  function injectToSandbox(code) {
    const doc = frame.contentDocument;
    if (!doc) { log('无法访问沙箱', 'error'); return; }
    doc.open(); doc.write(code); doc.close();
    statusBadge.textContent = '已注入 · ' + new Date().toTimeString().slice(0, 8);
    log('代码已注入沙箱', 'ok');
  }

  // ================== 测试用例 ==================

  // 测试1:原始弹窗
  $('btnTest1').onclick = () => {
    const code = `<!DOCTYPE html><html><head><meta charset="UTF-8"><style>
      body{margin:0;font-family:-apple-system,sans-serif;background:#f6f8fa;color:#333;min-height:100vh;display:flex;align-items:center;justify-content:center;}
      #block-notice{position:fixed;top:20px;left:50%;transform:translateX(-50%);background:#3fb950;color:#fff;padding:10px 22px;border-radius:999px;font-weight:600;opacity:0;transition:opacity .3s;z-index:2147483647;font-size:13px;}
      #block-notice.show{opacity:1;}
    </style></head><body><h1>测试1 · 原始弹窗</h1><div id="block-notice">已阻止</div>
    <script>
      (function(){
        var NOTICE='block-notice',t=null;
        function notice(m){var e=document.getElementById(NOTICE);if(!e)return;e.textContent=m;e.classList.add('show');clearTimeout(t);t=setTimeout(function(){e.classList.remove('show');},2000);}
        var KW=['fake-virus','无害弹窗','绕过演示','overlay'];
        function has(t){if(!t)return false;var s=String(t).toLowerCase();return KW.some(function(k){return s.indexOf(k.toLowerCase())>=0;});}
        function susp(n){
          if(!n||n.nodeType!==1)return false;
          if(n.id&&has(n.id))return true;
          if(n.className&&typeof n.className==='string'&&has(n.className))return true;
          try{if(n.textContent&&has(n.textContent))return true;}catch(e){}
          if(n.tagName==='IFRAME'){var sd=n.getAttribute&&n.getAttribute('srcdoc');if(sd&&has(sd))return true;}
          if(n.querySelector&&n.querySelector('#fake-virus'))return true;
          return false;
        }
        function scan(r,d){
          if(!r||d>8)return null;
          if(susp(r))return r;
          if(r.children){for(var i=0;i<r.children.length;i++){var h=scan(r.children[i],d+1);if(h)return h;}}
          if(r.shadowRoot){var h2=scan(r.shadowRoot,d+1);if(h2)return h2;}
          return null;
        }
        function kill(n,m){if(!n)return;try{if(n.parentNode)n.parentNode.removeChild(n);}catch(e){}notice(m||'已阻止');}
        var oa=Node.prototype.appendChild,oi=Node.prototype.insertBefore;
        Node.prototype.appendChild=function(n){if(susp(n)){notice('已阻止:检测到可疑弹窗');return n;}return oa.call(this,n);};
        Node.prototype.insertBefore=function(n,r){if(susp(n)){notice('已阻止:检测到可疑弹窗');return n;}return oi.call(this,n,r);};
        new MutationObserver(function(ms){ms.forEach(function(m){m.addedNodes.forEach(function(n){if(susp(n))kill(n,'已阻止:检测到可疑弹窗');});});}).observe(document.documentElement,{childList:true,subtree:true,attributes:true});
        setInterval(function(){var h=scan(document.documentElement,0);if(h&&h!==document.documentElement&&h.parentNode)kill(h,'已阻止:检测到可疑弹窗');},400);
      })();
    <\/script>
    <script>
      setTimeout(function(){
        var o=document.createElement('div');
        o.id='fake-virus';
        o.innerHTML='<div style="position:fixed;inset:0;background:rgba(0,0,0,.78);display:flex;align-items:center;justify-content:center;z-index:999999;"><div style="background:#101010;color:#fff;padding:26px;border-radius:18px;text-align:center;"><div style="color:#00ff88;font-weight:700;">⚠ 无害弹窗演示</div><div style="font-size:56px;font-weight:800;">3</div></div></div>';
        document.body.appendChild(o);
      },500);
    <\/script>
    </body></html>`;
    injectToSandbox(code); log('运行测试1', 'info');
  };

  // 测试2:iframe 绕过
  $('btnTest2').onclick = () => {
    const code = `<!DOCTYPE html><html><head><meta charset="UTF-8"><style>
      body{margin:0;font-family:-apple-system,sans-serif;background:#f6f8fa;color:#333;min-height:100vh;display:flex;align-items:center;justify-content:center;}
      #block-notice{position:fixed;top:20px;left:50%;transform:translateX(-50%);background:#3fb950;color:#fff;padding:10px 22px;border-radius:999px;font-weight:600;opacity:0;transition:opacity .3s;z-index:2147483647;font-size:13px;}
      #block-notice.show{opacity:1;}
    </style></head><body><h1>测试2 · iframe 绕过</h1><div id="block-notice">已阻止</div>
    <script>
      (function(){
        var NOTICE='block-notice',t=null;
        function notice(m){var e=document.getElementById(NOTICE);if(!e)return;e.textContent=m;e.classList.add('show');clearTimeout(t);t=setTimeout(function(){e.classList.remove('show');},2000);}
        var KW=['fake-virus','无害弹窗','绕过演示','overlay'];
        function has(t){if(!t)return false;var s=String(t).toLowerCase();return KW.some(function(k){return s.indexOf(k.toLowerCase())>=0;});}
        function susp(n){
          if(!n||n.nodeType!==1)return false;
          if(n.id&&has(n.id))return true;
          if(n.className&&typeof n.className==='string'&&has(n.className))return true;
          try{if(n.textContent&&has(n.textContent))return true;}catch(e){}
          if(n.tagName==='IFRAME'){var sd=n.getAttribute&&n.getAttribute('srcdoc');if(sd&&has(sd))return true;}
          if(n.querySelector&&n.querySelector('#fake-virus'))return true;
          return false;
        }
        function scan(r,d){
          if(!r||d>8)return null;
          if(susp(r))return r;
          if(r.children){for(var i=0;i<r.children.length;i++){var h=scan(r.children[i],d+1);if(h)return h;}}
          if(r.shadowRoot){var h2=scan(r.shadowRoot,d+1);if(h2)return h2;}
          return null;
        }
        function kill(n,m){if(!n)return;try{if(n.parentNode)n.parentNode.removeChild(n);}catch(e){}notice(m||'已阻止');}
        var oa=Node.prototype.appendChild,oi=Node.prototype.insertBefore;
        Node.prototype.appendChild=function(n){if(susp(n)){notice('已阻止:检测到可疑弹窗');return n;}return oa.call(this,n);};
        Node.prototype.insertBefore=function(n,r){if(susp(n)){notice('已阻止:检测到可疑弹窗');return n;}return oi.call(this,n,r);};
        new MutationObserver(function(ms){ms.forEach(function(m){m.addedNodes.forEach(function(n){if(susp(n))kill(n,'已阻止:检测到可疑弹窗');});});}).observe(document.documentElement,{childList:true,subtree:true,attributes:true});
        setInterval(function(){var h=scan(document.documentElement,0);if(h&&h!==document.documentElement&&h.parentNode)kill(h,'已阻止:检测到可疑弹窗');},400);
      })();
    <\/script>
    <script>
      setTimeout(function(){
        var f=document.createElement('iframe');
        f.style.cssText='position:fixed;inset:0;width:100%;height:100%;border:0;z-index:999999;';
        f.srcdoc='<!DOCTYPE html><html><head><style>html,body{margin:0;height:100%;}.overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.78);}.box{padding:26px;border-radius:18px;background:#101010;color:#fff;text-align:center;}.title{color:#00ff88;font-weight:700;font-size:20px;}.count{font-size:56px;font-weight:800;}<\/style><\/head><body><div class="overlay"><div class="box"><div class="title">⚠ 绕过演示</div><div class="count" id="c">3</div></div></div><script>(function(){var e=document.getElementById("c"),n=3,t=setInterval(function(){n--;if(n>0){e.textContent=n;}else{clearInterval(t);}},1000);})();<\/script></body></html>';
        document.body.appendChild(f);
      },500);
    <\/script>
    </body></html>`;
    injectToSandbox(code); log('运行测试2', 'info');
  };

  // 测试3:Shadow DOM 绕过
  $('btnTest3').onclick = () => {
    const code = `<!DOCTYPE html><html><head><meta charset="UTF-8"><style>
      body{margin:0;font-family:-apple-system,sans-serif;background:#f6f8fa;color:#333;min-height:100vh;display:flex;align-items:center;justify-content:center;}
      #block-notice{position:fixed;top:20px;left:50%;transform:translateX(-50%);background:#3fb950;color:#fff;padding:10px 22px;border-radius:999px;font-weight:600;opacity:0;transition:opacity .3s;z-index:2147483647;font-size:13px;}
      #block-notice.show{opacity:1;}
    </style></head><body><h1>测试3 · Shadow DOM 绕过</h1><div id="block-notice">已阻止</div>
    <script>
      (function(){
        var NOTICE='block-notice',t=null;
        function notice(m){var e=document.getElementById(NOTICE);if(!e)return;e.textContent=m;e.classList.add('show');clearTimeout(t);t=setTimeout(function(){e.classList.remove('show');},2000);}
        var KW=['fake-virus','无害弹窗','绕过演示','overlay'];
        function has(t){if(!t)return false;var s=String(t).toLowerCase();return KW.some(function(k){return s.indexOf(k.toLowerCase())>=0;});}
        function susp(n){
          if(!n||n.nodeType!==1)return false;
          if(n.id&&has(n.id))return true;
          if(n.className&&typeof n.className==='string'&&has(n.className))return true;
          try{if(n.textContent&&has(n.textContent))return true;}catch(e){}
          if(n.tagName==='IFRAME'){var sd=n.getAttribute&&n.getAttribute('srcdoc');if(sd&&has(sd))return true;}
          if(n.querySelector&&n.querySelector('#fake-virus'))return true;
          return false;
        }
        function scan(r,d){
          if(!r||d>8)return null;
          if(susp(r))return r;
          if(r.children){for(var i=0;i<r.children.length;i++){var h=scan(r.children[i],d+1);if(h)return h;}}
          if(r.shadowRoot){var h2=scan(r.shadowRoot,d+1);if(h2)return h2;}
          return null;
        }
        function kill(n,m){if(!n)return;try{if(n.parentNode)n.parentNode.removeChild(n);}catch(e){}notice(m||'已阻止');}
        var oa=Node.prototype.appendChild,oi=Node.prototype.insertBefore;
        Node.prototype.appendChild=function(n){if(susp(n)){notice('已阻止:检测到可疑弹窗');return n;}return oa.call(this,n);};
        Node.prototype.insertBefore=function(n,r){if(susp(n)){notice('已阻止:检测到可疑弹窗');return n;}return oi.call(this,n,r);};
        new MutationObserver(function(ms){ms.forEach(function(m){m.addedNodes.forEach(function(n){if(susp(n))kill(n,'已阻止:检测到可疑弹窗');});});}).observe(document.documentElement,{childList:true,subtree:true,attributes:true});
        setInterval(function(){var h=scan(document.documentElement,0);if(h&&h!==document.documentElement&&h.parentNode)kill(h,'已阻止:检测到可疑弹窗');},400);
      })();
    <\/script>
    <script>
      setTimeout(function(){
        var h=document.createElement('div');
        document.body.appendChild(h);
        var s=h.attachShadow({mode:'open'});
        s.innerHTML='<style>.overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.78);z-index:999999;}.box{padding:26px;border-radius:18px;background:#101010;color:#fff;text-align:center;}.title{color:#af52de;font-weight:700;font-size:20px;}.count{font-size:56px;font-weight:800;}<\/style><div class="overlay"><div class="box"><div class="title">⚠ Shadow DOM 绕过</div><div class="count">3</div></div></div>';
      },500);
    <\/script>
    </body></html>`;
    injectToSandbox(code); log('运行测试3', 'info');
  };

  // 测试4:文本显示(刚集成进来的)
  $('btnTest4').onclick = () => {
    const code = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文本显示</title>
<style>
  body {
    margin: 0; min-height: 100vh; display: flex;
    align-items: center; justify-content: center;
    background: #f5f5f5;
    font-family: system-ui, -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
    color: #333; padding: 24px;
  }
  .card {
    max-width: 600px; width: 100%; background: #fff;
    border-radius: 16px; padding: 28px;
    box-shadow: 0 8px 30px rgba(0,0,0,.08);
    line-height: 1.9; font-size: 16px;
  }
  h1 { font-size: 20px; margin: 0 0 16px; color: #111; }
  p { margin: 0 0 12px; }
  .tip {
    margin-top: 20px; font-size: 13px; color: #888;
    border-top: 1px solid #eee; padding-top: 14px;
  }
</style>
</head>
<body>
  <div class="card">
    <h1>文本显示测试</h1>
    <p>这是一段中文文本,用来测试网页是否能正确显示,不乱码。</p>
    <p>English text, numbers 1234567890, symbols !@#$%^&*().</p>
    <p>如果打开后看到的是方块、问号或者奇怪字符,说明文件保存时编码不是 UTF-8,需要重新另存为 UTF-8 编码。</p>
    <div class="tip">编码:UTF-8</div>
  </div>
</body>
</html>`;
    injectToSandbox(code); log('运行测试4:文本显示', 'info');
  };

  // ================== 界面控制 ==================
  $('btnReload').onclick = resetSandbox;
  $('btnClearLog').onclick = () => { consoleEl.innerHTML = ''; logCount = 0; $('logCount').textContent = '0'; };

  $('btnInject').onclick = () => {
    const code = $('codeInput').value;
    if (!code.trim()) { log('代码为空', 'warn'); return; }
    injectToSandbox(code);
  };

  $('btnWrapDoc').onclick = () => {
    const raw = $('codeInput').value;
    $('codeInput').value = `<!DOCTYPE html><html><head><meta charset="UTF-8"><style>body{margin:0;background:#f6f8fa;font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;}</style></head><body>${raw}</body></html>`;
    log('已包裹为完整文档', 'info');
  };

  // 移动端折叠
  $('btnToggleLogs').onclick = () => {
    const p = $('logsPanel');
    p.classList.toggle('hide-mobile');
    $('editorPanel').classList.remove('hide-mobile');
  };
  $('btnToggleEditor').onclick = () => {
    const p = $('editorPanel');
    p.classList.toggle('hide-mobile');
    $('logsPanel').classList.remove('hide-mobile');
  };

  // 全屏沙箱
  $('btnFullscreen').onclick = () => {
    const stage = $('stage');
    if (stage.requestFullscreen) stage.requestFullscreen();
    else if (stage.webkitRequestFullscreen) stage.webkitRequestFullscreen();
    log('沙箱已进入全屏模式', 'info');
  };

  // 初始化
  resetSandbox();
  log('实验场 V2 初始化完成', 'ok');
  log('已集成文本显示测试(按钮 4)', 'info');
</script>
</body>
</html>

Game Source: 安全实验场 Pro V2

Creator: EpicCoder88

Libraries: none

Complexity: complex (455 lines, 23.3 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: pro-v2-epiccoder88" to link back to the original. Then publish at arcadelab.ai/publish.