public/dev_tools.js

分割 10 / 10 · 元ファイル 1638〜1712行付近

原文の連続部分です。長い圧縮行は行の途中で分割する場合があります。全分割を順に連結するとファイル全文になります。

        listEl.appendChild(row);
      });
    });
  }
  function pick(){
    return bbAll().then(function(l){
      const any=Object.keys(sel).some(function(k){ return sel[k]; });
      return any ? l.filter(function(r){ return sel[r.id]; }) : l;
    });
  }
  function out(list, how){
    if(!list.length){ toast('選ばれていません'); return; }
    const txt=JSON.stringify({bundleVersion:1, at:new Date().toISOString(), reports:list});
    const kb=Math.round(txt.length/102.4)/10;
    if(how==='copy'){
      if(navigator.clipboard && navigator.clipboard.writeText)
        navigator.clipboard.writeText(txt).then(function(){ toast('コピーした('+kb+'KB)'); })
          .catch(function(){ manualCopy(txt); });
      else manualCopy(txt);
    } else if(how==='share' && navigator.share){
      const f=new File([txt], 'hzg_bug.json', {type:'application/json'});
      const p=(navigator.canShare && navigator.canShare({files:[f]}))
        ? navigator.share({files:[f], title:'平成ゾンビ学園 バグ報告'})
        : navigator.share({title:'平成ゾンビ学園 バグ報告', text:txt.slice(0,60000)});
      p.catch(function(){ manualCopy(txt); });
    } else {
      const a=document.createElement('a');
      a.href=URL.createObjectURL(new Blob([txt],{type:'application/json'}));
      a.download='hzg_bug_'+new Date().toISOString().replace(/[:.]/g,'-').slice(0,19)+'.json';
      document.body.appendChild(a); a.click();
      setTimeout(function(){ URL.revokeObjectURL(a.href); a.remove(); }, 1000);
      toast('保存した('+kb+'KB)');
    }
  }
  function mkBtn(label, fn, bg){
    const b=document.createElement('button'); b.textContent=label;
    b.style.cssText='flex:1; min-width:86px; padding:9px 6px; border:0; border-radius:9px; font-weight:bold;'
      +'background:'+(bg||'#28533f')+'; color:#f7f0dd;';
    b.onclick=fn; foot.appendChild(b); return b;
  }
  /* サーバーへ送る(2026-08-28 K1指示。テスターの報告が端末に溜まるだけだったのを、
     押した先でK1が読めるようにする)。宛先は同じCloudflareの /api/bug 。
     成功したら「送った」と出す。圏外などで失敗しても端末の箱には残るので、後でもう一度送れる */
  mkBtn('サーバーへ送る', function(){
    pick().then(function(l){
      if(!l.length){ toast('選ばれていません'); return; }
      var ok=0, ng=0, done=0;
      l.forEach(function(rep){
        var body=JSON.stringify(Object.assign({}, rep, {
          note:rep.memo||'', who:(function(){ try{ return localStorage.getItem('hzg_tester')||''; }catch(e){ return ''; } })(),
          ua:navigator.userAgent
        }));
        fetch('/api/bug', {method:'POST', headers:{'content-type':'application/json'}, body:body})
          .then(function(r){ if(r.ok) ok++; else ng++; })
          .catch(function(){ ng++; })
          .then(function(){ done++;
            if(done===l.length) toast(ok+'件 送った'+(ng?(' / '+ng+'件 失敗'):''));
          });
      });
    });
  }, '#3d6ab4');
  mkBtn('コピー', function(){ pick().then(function(l){ out(l,'copy'); }); });
  mkBtn('ファイル保存', function(){ pick().then(function(l){ out(l,'file'); }); }, '#8a6d1c');
  if(navigator.share) mkBtn('共有', function(){ pick().then(function(l){ out(l,'share'); }); }, '#3d6ab4');
  mkBtn('全部消す', function(){
    if(!confirm('全部消していい?')) return;
    bbAll().then(function(l){ return Promise.all(l.map(function(r){ return bbDel(r.id); })); })
      .then(function(){ bbRefresh(); draw(); });
  }, '#7a2f23');
  draw();
}
/* 起動時にボタンを出す(開発ツールが止まっていればここまで来ない) */
setTimeout(bbBuildBtn, 800);

})();