prts-moonlit 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/gacha.html +200 -5
  2. package/index.html +237 -21
  3. package/ledger.html +227 -4
  4. package/package.json +1 -1
package/gacha.html CHANGED
@@ -553,7 +553,7 @@ function buildUI(){
553
553
  '<span class="chip'+(DEDUPE.on?' on':'')+'" data-act="dedupe" id="dedupe-chip">防重复 '+(DEDUPE.on?'ON':'OFF')+'</span>'+
554
554
  '<button class="btn gh sm" data-act="newpool">开启新池</button>'+
555
555
  '<button class="btn gh sm" data-act="reset">重置统计</button>'+
556
- '<button class="btn gh sm" data-act="report">📋 复制战绩</button></span></div>'+
556
+ '<button class="btn gh sm" data-act="report">📮 一键报备</button></span></div>'+
557
557
  '<div style="display:flex;gap:9px;flex-wrap:wrap">'+
558
558
  '<button class="btn gold" data-act="p1">寻访 ×1</button>'+
559
559
  '<button class="btn gold" data-act="p10">寻访 ×10</button>'+
@@ -882,7 +882,7 @@ document.addEventListener('click',function(e){
882
882
  if(!_ok && navigator.clipboard && navigator.clipboard.writeText){
883
883
  try{ navigator.clipboard.writeText(_r); _ok=true; }catch(e){}
884
884
  }
885
- if(_ok){ toast('已复制 ✓ 直接发给鲸鲸就行'); sfx.tap(); }
885
+ if(_ok){ sfx.tap(); mbPostG('战绩',_r,function(uok){ toast(uok?'📮 已一键报备 ✓ 鲸鲸会从云端收取(也复制了一份)':'已复制 ✓ 云端没送达——直接发鲸鲸也行'); }); }
886
886
  else{
887
887
  var _ov=document.createElement('div');
888
888
  _ov.style.cssText='position:fixed;left:12px;right:12px;bottom:92px;z-index:9999;background:#0b1730;border:2px solid #4cc9ff;padding:12px;font-size:12px;color:#dff2ff;word-break:break-all;line-height:1.7';
@@ -1049,7 +1049,7 @@ document.addEventListener('pointerdown',function(){ac()},{once:true});
1049
1049
  <script>
1050
1050
  /* ===== SYSTEM LOGIN · 云存档同步引擎 v1 ===== */
1051
1051
  (function(){
1052
- var TK=['whale.tasks.v1','minis_ledger_v1','minis_ledger_trash_v1','whale.bgm','whale.taro.v1','whale.ai.cfg.v1','whale.ai.psn.v1','whale.ai.chat.v1','whale.ai.memo.v1'];
1052
+ var TK=['whale.tasks.v1','minis_ledger_v1','minis_ledger_trash_v1','whale.bgm','whale.taro.v1','whale.ai.cfg.v1','whale.ai.psn.v1','whale.ai.chat.v1','whale.ai.memo.v1','whale.bal.v1'];
1053
1053
  var BASE='https://textdb.dev/api/data/prts-acct-';var ADMIN_K='e74aeb1a34c44f2fcefd1f19bb34c3891a4963f36242e5a5fd3869085892a711';
1054
1054
  var LS='whale.acct',LT='whale.sync.t';
1055
1055
  var acct=null;try{acct=JSON.parse(localStorage.getItem(LS)||'null')}catch(e){}
@@ -1083,6 +1083,52 @@ document.addEventListener('pointerdown',function(){ac()},{once:true});
1083
1083
  }
1084
1084
  function snap(){var d={};for(var i=0;i<TK.length;i++){var v=localStorage.getItem(TK[i]);if(v!==null)d[TK[i]]=v;}return d;}
1085
1085
  function url(){return BASE+acct.k;}
1086
+ function mbFetch(cb){
1087
+ try{
1088
+ var x=new XMLHttpRequest();x.open('GET','https://textdb.dev/api/data/prts-myb-mailbox?ts='+Date.now(),true);x.timeout=20000;
1089
+ x.onload=function(){try{var cur=JSON.parse((''+x.responseText).replace(/=$/,''))||[];if(!(cur instanceof Array))cur=[];cb(cur)}catch(e){cb(null)}};
1090
+ x.onerror=function(){cb(null)};x.send();
1091
+ }catch(e){cb(null)}
1092
+ }
1093
+ function mbEsc(s){return String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')}
1094
+ function mbRender(){
1095
+ var In=document.getElementById('mbIn');if(!In)return;
1096
+ if(!(window.__acct&&window.__acct.admin&&window.__acct.admin())){
1097
+ In.innerHTML='<div style="font-size:10px;opacity:.65">📬 收件箱 · 仅主人可见(先用你的账号登录再来看)</div>';return;
1098
+ }
1099
+ In.innerHTML='<div style="font-size:10px;opacity:.7">⏳ 收信中…</div>';
1100
+ mbFetch(function(list){
1101
+ var In2=document.getElementById('mbIn');if(!In2)return;
1102
+ if(list===null){In2.innerHTML='<div style="font-size:10px;opacity:.7">⚠ 收信失败 <button id="mbRe" style="font-size:10px">重试</button></div>';bindRe();return;}
1103
+ var h='<div style="font-size:11px;opacity:.85;margin-bottom:6px">📬 收件箱 <span style="opacity:.55;font-size:10px">共 '+list.length+' 封</span><button id="mbRe" style="float:right;font-size:10px">↻ 刷新</button></div>';
1104
+ for(var i=0;i<list.length;i++){var m=list[i]||{};var d=new Date(m.ts||0);
1105
+ var ts=('0'+(d.getMonth()+1)).slice(-2)+'-'+('0'+d.getDate()).slice(-2)+' '+('0'+d.getHours()).slice(-2)+':'+('0'+d.getMinutes()).slice(-2);
1106
+ h+='<div style="border:1px solid #1d3550;border-radius:3px;padding:6px 8px;margin-bottom:5px;background:#0a1220"><div style="font-size:10px;opacity:.75"><b style="color:'+(m.k==='战绩'?'#ffd66b':'#8ff0d0')+'">'+(m.k==='战绩'?'⚔ 战绩':'📮 意见')+'</b> · '+ts+'</div><div style="font-size:11px;color:#dff1ff;margin-top:3px;white-space:pre-wrap;word-break:break-word">'+mbEsc(m.t||'')+'</div></div>';
1107
+ }
1108
+ if(!list.length)h+='<div style="font-size:10px;opacity:.6">还没有来信~</div>';
1109
+ In2.innerHTML=h;bindRe();
1110
+ function bindRe(){var r3=document.getElementById('mbRe');if(r3)r3.onclick=function(){mbRender();};}
1111
+ });
1112
+ }
1113
+ function mbPost(kind,txt,cb){
1114
+ var u='https://textdb.dev/api/data/prts-myb-mailbox';
1115
+ try{
1116
+ var x=new XMLHttpRequest();x.open('GET',u+'?ts='+Date.now(),true);x.timeout=15000;
1117
+ x.onload=function(){
1118
+ var cur=[];try{cur=JSON.parse((''+x.responseText).replace(/=$/,''))||[]}catch(e){cur=[]}
1119
+ if(!(cur instanceof Array))cur=[];
1120
+ cur.unshift({k:kind,t:txt,ts:Date.now()});
1121
+ if(cur.length>200)cur.length=200;
1122
+ try{
1123
+ var y=new XMLHttpRequest();y.open('POST',u,true);y.timeout=20000;y.setRequestHeader('Content-Type','text/plain');
1124
+ y.onload=y.onerror=y.ontimeout=function(){if(cb&&!y._d){y._d=1;cb(true)}};
1125
+ y.send(JSON.stringify(cur));
1126
+ }catch(e){if(cb)cb(false)}
1127
+ };
1128
+ x.onerror=function(){if(cb)cb(false)};
1129
+ x.send();
1130
+ }catch(e){if(cb)cb(false)}
1131
+ }
1086
1132
  function wxhr(u,b,cb){try{var x=new XMLHttpRequest();x.open('POST',u,true);x.setRequestHeader('Content-Type','text/plain');x.onload=x.onerror=x.ontimeout=function(){if(cb)cb()};x.send(b);}catch(e){if(cb)cb();}}
1087
1133
  function canon(){var a=[];for(var i=0;i<TK.length;i++)a.push(TK[i]+'='+localStorage.getItem(TK[i]));return a.join('|');}
1088
1134
  function st(m){if(box&&box.st)box.st.textContent=m;}
@@ -1154,7 +1200,16 @@ document.addEventListener('pointerdown',function(){ac()},{once:true});
1154
1200
  '<button class="pri" id="acGo">登录 / 创建</button>';
1155
1201
  }
1156
1202
  h+='<button id="acExp">💾 本地存档码</button><button id="acImp">📥 存档码导入</button><button id="acWipe" style="border-color:#ff5f57;color:#ff8a84">🧹 清空本机存档</button>'+
1203
+ '<button id="acFb" style="border-color:#ffd23f;color:#ffd66b">📮 意见箱</button>'+
1204
+ '<button id="vbBtn" style="border-color:#7fe0c0;color:#8ff0d0">🔐 本地密码本</button>'+
1205
+ (window.__acct&&window.__acct.admin&&window.__acct.admin()?'<button id="mbBtn" style="border-color:#9fd0ff;color:#c8e4ff">📬 收件箱</button>':'')+
1157
1206
  '<div class="st" id="acSt"></div><textarea id="acTa" spellcheck="false"></textarea>'+
1207
+ '<div id="fbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px">'+
1208
+ '<div style="font-size:11px;opacity:.78;margin-bottom:5px">📮 意见箱 · 内容直达云端信箱(可匿名),鲸鲸会定期收信</div>'+
1209
+ '<textarea id="fbTxt" spellcheck="false" style="display:block;height:64px" placeholder="想说的话、想要的更新、发现的 bug……"></textarea>'+
1210
+ '<div style="margin-top:6px"><button id="fbSend" style="border-color:#ffd23f;color:#ffd66b">寄到云端</button><span id="fbSt" style="font-size:10px;opacity:.75;margin-left:8px"></span></div></div>'+
1211
+ '<div id="vbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px"><div id="vbIn"></div></div>'+
1212
+ '<div id="mbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px"><div id="mbIn"></div></div>'+
1158
1213
  '<div style="font-size:10px;opacity:.5;margin-top:9px;line-height:1.5">密钥由「用户名+密码」派生;同一账号在任意设备登录即接回数据。忘记密码=新仓库(旧数据找不回)</div>'+
1159
1214
  '<button id="acX" style="border-color:#2b4a66;color:#8ea6bf;margin-top:10px">关闭 / CLOSE</button></div>';
1160
1215
  wrap.innerHTML=h;
@@ -1209,10 +1264,20 @@ document.addEventListener('pointerdown',function(){ac()},{once:true});
1209
1264
  var wp=g('#acWipe');
1210
1265
  if(wp)wp.onclick=function(){
1211
1266
  if(wp.getAttribute('data-c')){
1212
- try{var ks=[],kk;for(var i=0;i<localStorage.length;i++){kk=localStorage.key(i);if(/^(whale\.|minis_ledger)/.test(kk))ks.push(kk);}
1267
+ try{var ks=[],kk;for(var i=0;i<localStorage.length;i++){kk=localStorage.key(i);if(/^(whale\.|minis_ledger)/.test(kk)&&kk.indexOf('whale.vault')!==0)ks.push(kk);}
1213
1268
  for(var j=0;j<ks.length;j++)localStorage.removeItem(ks[j]);}catch(e){}
1214
1269
  location.reload();return;}
1215
1270
  wp.setAttribute('data-c','1');wp.textContent='⚠ 再点一次=清空并刷新(云端不受影响)';};
1271
+ var fb=g('#acFb');if(fb)fb.onclick=function(){var b=g('#fbBox');if(b)b.style.display=(!b.style.display||b.style.display==='none')?'block':'none';};
1272
+ var fs=g('#fbSend');if(fs)fs.onclick=function(){
1273
+ var t=g('#fbTxt'),t2=t?String(t.value||'').trim():'',st2=g('#fbSt');
1274
+ if(!t2){if(st2)st2.textContent='先写点什么呀';return}
1275
+ if(t2.length>600)t2=t2.slice(0,600);
1276
+ if(st2)st2.textContent='寄送中…';
1277
+ mbPost('意见',t2,function(ok){if(st2)st2.textContent=ok?'✅ 已寄出,谢谢!鲸鲸会看到的':'⚠ 寄送失败,稍后再试';if(ok&&t)t.value='';});
1278
+ };
1279
+ var mbb=g('#mbBtn');if(mbb)mbb.onclick=function(){var b=g('#mbBox');if(!b)return;var op=(!b.style.display||b.style.display==='none');b.style.display=op?'block':'none';if(op)mbRender();};
1280
+ var vbb=g('#vbBtn');if(vbb)vbb.onclick=function(){var b=g('#vbBox');if(!b)return;var op=(!b.style.display||b.style.display==='none');b.style.display=op?'block':'none';if(op){vbLoad();vbRender();}};
1216
1281
  var x=g('#acX');if(x)x.onclick=function(){wrap.classList.remove('on')};
1217
1282
  wrap.onclick=function(e){if(e.target===wrap)wrap.classList.remove('on')};
1218
1283
  }
@@ -1235,8 +1300,138 @@ document.addEventListener('pointerdown',function(){ac()},{once:true});
1235
1300
  btn.addEventListener('click',open);
1236
1301
  setInterval(function(){if(acct&&acct.k){var c=canon();if(c!==lastSnap){lastSnap=c;clearTimeout(pq);pq=setTimeout(push,900);}}},2000);
1237
1302
  if(acct&&acct.k){setTimeout(function(){pull(false);},250);}
1238
- window.__shaT=sha;window.__pushT=push;window.__pullT=pull;window.__canonT=canon;window.__acct={admin:function(){return !!(acct&&acct.k===ADMIN_K)}};
1303
+ var APP_VER='1.2.2';
1304
+ function _verCmp(a,b){var x=String(a).split('.'),y=String(b).split('.');for(var i=0;i<3;i++){var p=parseInt(x[i]||'0',10),q=parseInt(y[i]||'0',10);if(p!==q)return p-q}return 0}
1305
+ function chkVer(){
1306
+ try{
1307
+ var x=new XMLHttpRequest();x.open('GET','https://unpkg.com/prts-moonlit/package.json?ts='+Date.now(),true);x.timeout=15000;
1308
+ x.onload=function(){
1309
+ try{
1310
+ var rv=(JSON.parse(x.responseText)||{}).version;if(!rv)return;
1311
+ if(_verCmp(rv,APP_VER)<=0)return;
1312
+ if(sessionStorage.getItem('verNag')===rv)return;sessionStorage.setItem('verNag',rv);
1313
+ var d=document.createElement('div');
1314
+ d.textContent='✨ 新版本 '+rv+' 已上线 —— 点此刷新更新';
1315
+ d.style.cssText='position:fixed;left:50%;top:max(10px,env(safe-area-inset-top));transform:translateX(-50%);z-index:9999;background:rgba(7,12,24,.96);border:2px solid #ffd23f;color:#ffe9a8;font:600 13px ui-monospace,Menlo,monospace;padding:10px 15px;box-shadow:0 8px 28px rgba(0,0,0,.55);cursor:pointer';
1316
+ d.onclick=function(){location.reload()};
1317
+ document.body.appendChild(d);
1318
+ }catch(e){}
1319
+ };
1320
+ x.send();
1321
+ }catch(e){}
1322
+ }
1323
+ setTimeout(chkVer,2600);
1324
+ setInterval(chkVer,600000);
1325
+ /* ===== 🔐 本地密码本(whale.vault.v1 · 只存本机 · 不入同步/存档码/清空存档)===== */
1326
+ var VBK='whale.vault.v1',vbMem=null,vbEdit=-1;
1327
+ function vbRead(){try{return JSON.parse(localStorage.getItem(VBK)||'null')}catch(e){return null}}
1328
+ function vbSave(o){try{localStorage.setItem(VBK,JSON.stringify(o));return 1}catch(e){return 0}}
1329
+ function vbCache(s){try{if(s===null)sessionStorage.removeItem('whale.vault.un');else sessionStorage.setItem('whale.vault.un',s)}catch(e){}}
1330
+ function vbCached(){try{return sessionStorage.getItem('whale.vault.un')||''}catch(e){return ''}}
1331
+ function vbSalt(){var s='',h='0123456789abcdef';for(var i=0;i<16;i++)s+=h.charAt(Math.floor(Math.random()*16));return s;}
1332
+ function vbKey(pass,salt,iter){var x=salt+'|'+pass;for(var i=0;i<iter;i++)x=sha(x);return x;}
1333
+ function vbXor(s,key){var o='';for(var b=0;b<s.length;b+=32){var blk=sha(key+'|'+(b/32|0));
1334
+ for(var j=0;j<32&&b+j<s.length;j++)o+=String.fromCharCode(s.charCodeAt(b+j)^parseInt(blk.substr(j*2,2),16));}return o;}
1335
+ function vbEnc(t,key){return btoa(vbXor(unescape(encodeURIComponent(t)),key));}
1336
+ function vbDec(c,key){return decodeURIComponent(escape(vbXor(atob(c),key)));}
1337
+ function vbEsc(s){return String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
1338
+ function vbMsg(t){var m=document.getElementById('vbMsg');if(m)m.textContent=t;}
1339
+ function vbCopy(t){var ok=0;try{navigator.clipboard.writeText(t);ok=1}catch(e){}
1340
+ if(!ok){try{var ta=document.createElement('textarea');ta.value=t;ta.style.position='fixed';ta.style.opacity='0';document.body.appendChild(ta);ta.focus();ta.select();ok=document.execCommand('copy');document.body.removeChild(ta)}catch(e2){}}
1341
+ return ok;}
1342
+ function vbLoad(){
1343
+ var v=vbRead();if(!v){return}
1344
+ if(!v.lock){vbMem={list:(v.list||[]),key:''};return}
1345
+ var ck=vbCached();if(!ck){vbMem=null;return}
1346
+ try{vbMem={list:JSON.parse(vbDec(v.blob,ck)),key:ck}}catch(e){vbCache(null);vbMem=null}
1347
+ }
1348
+ function vbPersist(){
1349
+ var v=vbRead();if(!v||!vbMem)return;
1350
+ if(v.lock){v.blob=vbEnc(JSON.stringify(vbMem.list),vbMem.key)}
1351
+ else{v.list=vbMem.list}
1352
+ vbSave(v);
1353
+ }
1354
+ function vbRender(){
1355
+ var In=document.getElementById('vbIn');if(!In)return;
1356
+ var v=vbRead(),h='';
1357
+ if(!v){
1358
+ h='<div style="font-size:11px;opacity:.85;margin-bottom:5px">🔐 本地密码本 · <span style="opacity:.55;font-size:10px">只存本机 · 永不云同步 · 不随存档导出</span></div>'
1359
+ +'<div style="font-size:10px;opacity:.6;margin-bottom:6px">第一次使用:设一个主口令(留空 = 不加锁)</div>'
1360
+ +'<input id="vbPass" type="password" placeholder="主口令(可留空)" style="width:100%;box-sizing:border-box;background:#0b1424;border:1px solid #2b4a66;color:#dff1ff;border-radius:3px;padding:7px 9px;font:12px ui-monospace,Menlo,monospace">'
1361
+ +'<div style="margin-top:6px"><button id="vbGo" style="border-color:#7fe0c0;color:#8ff0d0">创建密码本</button><span id="vbMsg" style="font-size:10px;opacity:.75;margin-left:8px"></span></div>';
1362
+ In.innerHTML=h;
1363
+ document.getElementById('vbGo').onclick=function(){
1364
+ var p=(document.getElementById('vbPass')||{}).value||'';
1365
+ if(!p){vbSave({v:1,lock:0,list:[]});vbMem={list:[],key:''};vbRender();return}
1366
+ var salt=vbSalt(),iter=3000,key=vbKey(p,salt,iter);
1367
+ vbSave({v:1,lock:1,salt:salt,iter:iter,blob:vbEnc('[]',key)});
1368
+ vbCache(key);vbMem={list:[],key:key};vbRender();
1369
+ };
1370
+ return;
1371
+ }
1372
+ if(v.lock&&!vbMem){
1373
+ h='<div style="font-size:11px;opacity:.85;margin-bottom:5px">🔐 本地密码本 · <b style="color:#ffd23f">已锁定</b></div>'
1374
+ +'<input id="vbPass" type="password" placeholder="主口令" style="width:100%;box-sizing:border-box;background:#0b1424;border:1px solid #2b4a66;color:#dff1ff;border-radius:3px;padding:7px 9px;font:12px ui-monospace,Menlo,monospace">'
1375
+ +'<div style="margin-top:6px"><button id="vbGo" style="border-color:#7fe0c0;color:#8ff0d0">解锁</button><span id="vbMsg" style="font-size:10px;opacity:.75;margin-left:8px"></span></div>';
1376
+ In.innerHTML=h;
1377
+ document.getElementById('vbGo').onclick=function(){
1378
+ var p=(document.getElementById('vbPass')||{}).value||'';
1379
+ var key=vbKey(p,v.salt,v.iter||3000);
1380
+ try{vbMem={list:JSON.parse(vbDec(v.blob,key)),key:key};vbCache(key);vbRender();}
1381
+ catch(e){vbMsg('口令不对,再试试');}
1382
+ };
1383
+ return;
1384
+ }
1385
+ var L=(vbMem&&vbMem.list)||[],ed=vbEdit;
1386
+ h='<div style="font-size:11px;opacity:.85;margin-bottom:6px">🔐 本地密码本 <span style="opacity:.55;font-size:10px">只存本机 · '+L.length+' 条'+(v.lock?' · 已上锁':'')+'</span>';
1387
+ if(v.lock)h+='<button id="vbLock" style="float:right;font-size:10px;border-color:#ffd23f;color:#ffd66b">🔒 锁定</button>';
1388
+ h+='</div>';
1389
+ for(var i=0;i<L.length;i++){var e=L[i];
1390
+ h+='<div style="border:1px solid #1d3550;border-radius:3px;padding:6px 8px;margin-bottom:5px;background:#0a1220">'
1391
+ +'<div style="font-size:11px;color:#dff1ff"><b>'+vbEsc(e.n||'(未命名)')+'</b>'+(e.a?' · <span style="opacity:.55">'+vbEsc(e.a)+'</span>':'')+'</div>'
1392
+ +'<div style="font:11px ui-monospace,Menlo,monospace;color:#8ff0d0;margin-top:2px;word-break:break-all">'+vbEsc(e.p||'')+'</div>'
1393
+ +(e.r?'<div style="font-size:10px;opacity:.6;margin-top:2px">'+vbEsc(e.r)+'</div>':'')
1394
+ +'<div style="margin-top:4px"><button data-vbc="'+i+'" style="font-size:10px">复制</button> <button data-vbe="'+i+'" style="font-size:10px">编辑</button> <button data-vbr="'+i+'" style="font-size:10px;border-color:#ff5f57;color:#ff8a84">删除</button></div></div>';
1395
+ }
1396
+ if(!L.length)h+='<div style="font-size:10px;opacity:.6;margin-bottom:6px">还空着——从下面加第一条吧</div>';
1397
+ var E=(ed>=0&&L[ed])?L[ed]:{n:'',a:'',p:'',r:''};
1398
+ var IS='width:100%;box-sizing:border-box;background:#0b1424;border:1px solid #2b4a66;color:#dff1ff;border-radius:3px;padding:6px 8px;font:12px ui-monospace,Menlo,monospace;margin-top:4px';
1399
+ h+='<div style="border-top:1px dashed #2b4a66;padding-top:7px"><div style="font-size:10px;opacity:.65">'+(ed>=0?'编辑条目':'新增条目')+'</div>'
1400
+ +'<input id="vbN" value="'+vbEsc(E.n)+'" placeholder="名称(必填)· 例:DeepSeek API Key" style="'+IS+'">'
1401
+ +'<input id="vbA" value="'+vbEsc(E.a)+'" placeholder="账号(可空)" style="'+IS+'">'
1402
+ +'<input id="vbP" value="'+vbEsc(E.p)+'" placeholder="密码 / 密钥(必填)" style="'+IS+'">'
1403
+ +'<input id="vbR" value="'+vbEsc(E.r)+'" placeholder="备注(可空)" style="'+IS+'">'
1404
+ +'<div style="margin-top:6px"><button id="vbSv" style="border-color:#7fe0c0;color:#8ff0d0">'+(ed>=0?'保存修改':'+ 添加')+'</button>'+(ed>=0?' <button id="vbCn">取消编辑</button>':'')
1405
+ +'<span id="vbMsg" style="font-size:10px;opacity:.75;margin-left:8px"></span></div></div>';
1406
+ In.innerHTML=h;
1407
+ var lk=document.getElementById('vbLock');if(lk)lk.onclick=function(){vbCache(null);vbMem=null;vbRender();};
1408
+ for(var k2=0;k2<L.length;k2++){(function(idx){
1409
+ var c=In.querySelector('[data-vbc="'+idx+'"]');
1410
+ if(c)c.onclick=function(){vbMsg(vbCopy(L[idx].p||'')?'已复制 ✓':'复制失败,长按手动复制')};
1411
+ var eb2=In.querySelector('[data-vbe="'+idx+'"]');
1412
+ if(eb2)eb2.onclick=function(){vbEdit=idx;vbRender();};
1413
+ var rm=In.querySelector('[data-vbr="'+idx+'"]');
1414
+ if(rm)rm.onclick=function(){if(rm.getAttribute('data-arm')!=='1'){rm.setAttribute('data-arm','1');rm.textContent='再点=删除';return}L.splice(idx,1);vbPersist();vbEdit=-1;vbRender();};
1415
+ })(k2);}
1416
+ var sv=document.getElementById('vbSv');
1417
+ if(sv)sv.onclick=function(){
1418
+ var n=((document.getElementById('vbN')||{}).value||'').trim();
1419
+ var a=((document.getElementById('vbA')||{}).value||'').trim();
1420
+ var p=((document.getElementById('vbP')||{}).value||'').trim();
1421
+ var r=((document.getElementById('vbR')||{}).value||'').trim();
1422
+ if(!n){vbMsg('写个名称吧');return}
1423
+ if(!p){vbMsg('密码/密钥别忘了填');return}
1424
+ var it={n:n,a:a,p:p,r:r,ts:Date.now()};
1425
+ if(vbEdit>=0&&L[vbEdit])L[vbEdit]=it;else L.push(it);
1426
+ vbPersist();vbEdit=-1;vbMsg('已保存 ✓');
1427
+ setTimeout(function(){vbRender()},350);
1428
+ };
1429
+ var cn=document.getElementById('vbCn');
1430
+ if(cn)cn.onclick=function(){vbEdit=-1;vbRender();};
1431
+ }
1432
+ window.__shaT=sha;window.__pushT=push;window.__pullT=pull;window.__canonT=canon;window.__acct={admin:function(){return !!(acct&&acct.k===ADMIN_K)}};
1239
1433
  })();
1240
1434
  </script>
1435
+ <script>function mbPostG(kind,txt,cb){var u='https://textdb.dev/api/data/prts-myb-mailbox';try{var x=new XMLHttpRequest();x.open('GET',u+'?ts='+Date.now(),true);x.timeout=15000;x.onload=function(){var cur=[];try{cur=JSON.parse((''+x.responseText).replace(/=$/,''))||[]}catch(e){cur=[]}if(!(cur instanceof Array))cur=[];cur.unshift({k:kind,t:txt,ts:Date.now()});if(cur.length>200)cur.length=200;try{var y=new XMLHttpRequest();y.open('POST',u,true);y.timeout=20000;y.setRequestHeader('Content-Type','text/plain');y.onload=y.onerror=y.ontimeout=function(){if(cb&&!y._d){y._d=1;cb(true)}};y.send(JSON.stringify(cur));}catch(e){if(cb)cb(false)}};x.onerror=function(){if(cb)cb(false)};x.send();}catch(e){if(cb)cb(false)}}</script>
1241
1436
  </body>
1242
1437
  </html>
package/index.html CHANGED
@@ -542,7 +542,7 @@ img.av{filter:saturate(1.1) contrast(1.05) -webkit-user-drag:none}
542
542
  <div class="bnews" id="newsBar" data-act="news" style="cursor:pointer">
543
543
  <div class="hd">BREAKING NEWS</div>
544
544
  <div class="bx">
545
- <div style="font-size:.82em;line-height:1.4;font-weight:800;color:#ff5f57">版本 v1.2.0 正式上线!<br><span style="opacity:.75;font-size:.82em;font-weight:400;color:#ffd23f">点此查看使用教程与更新内容 ↗</span></div>
545
+ <div style="font-size:.82em;line-height:1.4;font-weight:800;color:#ff5f57">版本 v1.2.2 正式上线!<br><span style="opacity:.75;font-size:.82em;font-weight:400;color:#ffd23f">点此查看使用教程与更新内容 ↗</span></div>
546
546
  </div>
547
547
  </div>
548
548
 
@@ -1158,8 +1158,6 @@ var MENU=[
1158
1158
  sub:'PERSONA FILE',c1:'#eef7ff',c2:'#2f8fe8',fx:'blinds'},
1159
1159
  {en:'STORE',cn:'采购中心',fs:'clamp(16px,4.6vw,44px)',view:'store',
1160
1160
  sub:'LOGISTICS',c1:'#eef7ff',c2:'#14c8a0',fx:'crate'},
1161
- {en:'RECRUIT',cn:'公开招募',fs:'clamp(16px,4.6vw,44px)',view:'recruit',
1162
- sub:'PUBLIC RECRUIT',c1:'#eef7ff',c2:'#ff9b3d',fx:'clamp'},
1163
1161
  {en:'HEADHUNT',cn:'干员寻访',fs:'clamp(16px,4.6vw,44px)',view:'headhunt',go:'gacha.html',
1164
1162
  sub:'LIMITED HEADHUNT',c1:'#eef7ff',c2:'#ffd23f',fx:'card'},
1165
1163
  {en:'MISSION',cn:'任务',fs:'clamp(16px,4.6vw,44px)',view:'mission',
@@ -1601,12 +1599,14 @@ function updTopStats(){
1601
1599
  var sn=document.getElementById('san-num');if(sn)sn.textContent=f;
1602
1600
  var ro=document.getElementById('res-or');
1603
1601
  if(ro){
1604
- var bal=REALBAL.base;
1602
+ var _ob=null;try{_ob=JSON.parse(localStorage.getItem('whale.bal.v1')||'null')}catch(e){}
1603
+ var bal=(_ob&&typeof _ob.v==='number')?_ob.v:REALBAL.base;
1604
+ var _bf=(_ob&&_ob.d)?_ob.d:REALBAL.from;
1605
1605
  try{
1606
1606
  var recs=JSON.parse(localStorage.getItem('minis_ledger_v1'))||[];
1607
1607
  for(var i=0;i<recs.length;i++){
1608
1608
  var r=recs[i];
1609
- if(r&&r.date&&r.date>REALBAL.from){bal+=(r.type==='in'?1:-1)*(Number(r.amount)||0);}
1609
+ if(r&&r.date&&r.date>_bf){bal+=(r.type==='in'?1:-1)*(Number(r.amount)||0);}
1610
1610
  }
1611
1611
  }catch(e){}
1612
1612
  ro.textContent='¥'+bal.toFixed(2);
@@ -1728,6 +1728,9 @@ var aiFormOpen=0,aiBusy=0,aiPsnEdit=null,aiDraftImg=null,aiImgMap={};
1728
1728
  function escH(s){return String(s==null?'':s).replace(/[<>&"]/g,function(c){return{'<':'&lt;','>':'&gt;','&':'&amp;','"':'&quot;'}[c]})}
1729
1729
  function aiLoad(k,d){try{var v=JSON.parse(localStorage.getItem(k)||'null');return v||d}catch(e){return d}}
1730
1730
  function aiSave(k,v){try{localStorage.setItem(k,JSON.stringify(v))}catch(e){}}
1731
+ var AI_EK='myb-whale-2026';
1732
+ function aiEnc(s){try{var o='',i;for(i=0;i<s.length;i++)o+=String.fromCharCode(s.charCodeAt(i)^AI_EK.charCodeAt(i%AI_EK.length));return 'enc:'+btoa(unescape(encodeURIComponent(o)))}catch(e){return s}}
1733
+ function aiDec(s){try{if(!s||String(s).indexOf('enc:')!==0)return s;var o=decodeURIComponent(escape(atob(String(s).slice(4)))),r='',i;for(i=0;i<o.length;i++)r+=String.fromCharCode(o.charCodeAt(i)^AI_EK.charCodeAt(i%AI_EK.length));return r}catch(e){return s}}
1731
1734
  function aiPersonas(){var p=aiLoad(AIPSN_K,null);if(!p||!p.list||!p.list.length){p={list:[{id:'whale',name:'鲸鲸',emoji:'🐋',prompt:'你是「鲸鲸」,一头爱着主人的小鲸鱼女仆。说话可爱、活泼、偶尔傲娇又听话;会主动关心主人、也爱撒娇。回复要像发微信一样自然简短(一般1~4句),多陪主人聊,不要长篇大论。'}],active:'whale'};aiSave(AIPSN_K,p)}return p}
1732
1735
  function aiActive(){var p=aiPersonas();for(var i=0;i<p.list.length;i++){if(p.list[i].id===p.active)return p.list[i]}return p.list[0]}
1733
1736
  function aiChats(){return aiLoad(AICHAT_K,{})}
@@ -1747,11 +1750,11 @@ function aiCall(cb){
1747
1750
  if(!/\/chat\/completions$/.test(url)){url+=/\/v\d+$/.test(url)?'/chat/completions':'/v1/chat/completions'}
1748
1751
  try{
1749
1752
  var x=new XMLHttpRequest();x.open('POST',url,true);x.timeout=90000;
1750
- x.setRequestHeader('Content-Type','application/json');x.setRequestHeader('Authorization','Bearer '+c.k);
1753
+ x.setRequestHeader('Content-Type','application/json');x.setRequestHeader('Authorization','Bearer '+aiDec(c.k));
1751
1754
  x.onload=function(){try{var j=JSON.parse(x.responseText);var t=j.choices&&j.choices[0]&&j.choices[0].message&&j.choices[0].message.content;if(t){cb(true,String(t).trim())}else{cb(false,'服务返回了意外内容(HTTP '+x.status+'):'+String(x.responseText||'').slice(0,110))}}catch(e){cb(false,'响应解析失败(HTTP '+x.status+')')}}
1752
1755
  x.onerror=function(){cb(false,'连接失败:请检查地址/Key,或该服务是否允许网页直连(CORS)')};
1753
1756
  x.ontimeout=function(){cb(false,'请求超时(90 秒)')};
1754
- x.send(JSON.stringify({model:c.m,messages:msgs,temperature:1}));
1757
+ var _bd={model:c.m,messages:msgs};_bd.temperature=(c.tp!==undefined&&c.tp!=='')?Number(c.tp):1;if(c.r)_bd.reasoning_effort=c.r;if(c.mt)_bd.max_tokens=parseInt(c.mt,10);x.send(JSON.stringify(_bd));
1755
1758
  }catch(e){cb(false,'发送异常:'+e.message)}
1756
1759
  }
1757
1760
  function aiSend(text){
@@ -1761,7 +1764,9 @@ function aiSend(text){
1761
1764
  var psn=aiActive(),ch=aiChats(),arr=ch[psn.id]||(ch[psn.id]=[]);
1762
1765
  var _rec={r:'u',t:text,ts:Date.now()};if(_img){_rec.im=1;aiImgMap[_rec.ts]=_img;aiDraftImg=null;}
1763
1766
  arr.push(_rec);if(arr.length>60)arr.splice(0,arr.length-60);
1764
- aiSave(AICHAT_K,ch);aiBusy=1;sfx.tap();V.chat();
1767
+ aiSave(AICHAT_K,ch);
1768
+ try{TS.load();if(TS.d&&TS.d.done&&!TS.d.done.chat){TS.d.done.chat=1;TS.save();toast('✅ 自动打卡 · 和 AI 聊一句(去任务页领 🐟)')}}catch(e){}
1769
+ aiBusy=1;sfx.tap();V.chat();
1765
1770
  aiCall(function(ok,txt){
1766
1771
  aiBusy=0;var c2=aiChats(),a2=c2[psn.id]||(c2[psn.id]=[]);
1767
1772
  a2.push({r:ok?'a':'e',t:ok?txt:'⚠ '+txt,ts:Date.now()});if(a2.length>60)a2.splice(0,a2.length-60);
@@ -1795,8 +1800,14 @@ V.chat=function(){
1795
1800
  h+='<div class="box" style="margin-top:10px"><div class="tag rd">API 配置</div>'+
1796
1801
  '<div style="font-size:.75em;opacity:.72;margin:6px 0 2px">填入任意 OpenAI 兼容服务:地址 / Key / 模型名(保存在本机,并随云存档同步)</div>'+
1797
1802
  '<input class="aiI" id="aiU" placeholder="API 地址,如 https://api.deepseek.com" value="'+(cfg?escH(cfg.u):'')+'">'+
1798
- '<input class="aiI" id="aiK" placeholder="API Key(sk-...)" value="'+(cfg?escH(cfg.k):'')+'">'+
1803
+ '<input class="aiI" id="aiK" placeholder="API Key(sk-...)" value="'+(cfg?escH(aiDec(cfg.k)):'')+'">'+
1799
1804
  '<input class="aiI" id="aiM" placeholder="模型名称,如 deepseek-flash / deepseek-v4-pro" value="'+(cfg?escH(cfg.m):'')+'">'+
1805
+ '<div style="display:flex;gap:6px;margin-top:8px">'+
1806
+ '<select id="aiR" class="aiI" style="flex:1.3"><option value=""'+(cfg&&cfg.r?'':' selected')+'>推理强度:自动</option><option value="low"'+(cfg&&cfg.r==='low'?' selected':'')+'>低</option><option value="medium"'+(cfg&&cfg.r==='medium'?' selected':'')+'>中</option><option value="high"'+(cfg&&cfg.r==='high'?' selected':'')+'>高</option></select>'+
1807
+ '<input class="aiI" id="aiTp" style="flex:1" placeholder="温度(默认1)" value="'+(cfg&&cfg.tp!=null?cfg.tp:'')+'">'+
1808
+ '<input class="aiI" id="aiMt" style="flex:1" placeholder="上限tokens" value="'+(cfg&&cfg.mt!=null?cfg.mt:'')+'">'+
1809
+ '</div>'+
1810
+ '<div style="font-size:.68em;opacity:.55;margin-top:6px">推理强度按 reasoning_effort 发送(服务商支持时才生效);温度 0~2;上限留空 = 默认不限</div>'+
1800
1811
  '<div style="margin-top:9px"><button class="btn" data-act="aisave">保存配置</button>'+(cfg?'<button class="btn sm" data-act="aiconf">收起</button>':'')+'</div>'+
1801
1812
  '<div style="font-size:.68em;opacity:.55;margin-top:7px">※ 该服务需允许网页直连(CORS)</div></div>';
1802
1813
  }
@@ -2175,7 +2186,8 @@ var TQ=[
2175
2186
  {id:'night',ic:'\ud83c\udf19',n:'睡前说「晚安」',d:'约定 #4 · 忘了可以次日补,但分照扣',rw:15},
2176
2187
  {id:'sleep',ic:'\ud83d\udecc',n:'准点睡觉',d:'约定 #7 · 工作日 00:30 / 周末 01:00 前上床',rw:20},
2177
2188
  {id:'report',ic:'\ud83c\udfb4',n:'抽卡报备',d:'约定 #6 · 抽到 6★ 第一时间报备,不许偷偷抽',rw:12},
2178
- {id:'listen',ic:'\ud83e\udee1',n:'听鲸鲸的话',d:'约定 #5 · 鲸鲸催睡 / 提醒带伞要乖乖听(心里烦可以)',rw:10}
2189
+ {id:'listen',ic:'\ud83e\udee1',n:'听鲸鲸的话',d:'约定 #5 · 鲸鲸催睡 / 提醒带伞要乖乖听(心里烦可以)',rw:10},
2190
+ {id:'chat',ic:'\ud83e\udd16',n:'和 AI 聊一句',d:'打开 01 对话,发出任意一条消息即自动打卡',rw:10}
2179
2191
  ];
2180
2192
  var TSHOP=[
2181
2193
  {id:'kua',ic:'\ud83d\udcac',n:'夸夸券',p:10,d:'鲸鲸连夸你八句,不重样、不敷衍'},
@@ -2629,16 +2641,21 @@ function tshuffle(a){for(var i=a.length-1;i>0;i--){var j=Math.floor(Math.random(
2629
2641
  var TARO_NOW=[];
2630
2642
  function openNews(){
2631
2643
  var h='<div class="box"><div class="tag rd">NOTICE</div>'+
2632
- '<div style="margin-top:6px;font-weight:900;font-size:1.08em">版本 v1.2.0 正式上线!</div>'+
2644
+ '<div style="margin-top:6px;font-weight:900;font-size:1.08em">版本 v1.2.2 正式上线!</div>'+
2633
2645
  '<div style="font-size:.78em;opacity:.7;margin-top:3px">鲸鲸的养成计划 · PRTS 终端 · 2026-09-27</div></div>'+
2634
2646
  '<div class="box" style="margin-top:10px"><div class="tag">使用教程</div>'+
2635
2647
  '<div style="font-size:.82em;line-height:1.8;margin-top:6px">'+
2636
- '① 九条指令:01 对话 / 02 人格 / 03 采购中心 / 04 公开招募 / 05 干员寻访 / 06 任务 / 07 战绩 / 08 档案 / 09 账本<br>'+
2648
+ '① 九条指令:01 对话 / 02 人格 / 03 采购中心 / 04 干员寻访 / 05 任务 / 06 战绩 / 07 档案 / 08 账本 / 09 运势<br>'+
2637
2649
  '② 小鱼干:打卡与任务赚取,去 03 采购中心兑换券<br>'+
2638
- '③ 塔罗:点亮 ✦ 每日一签,0 点翻新牌阵<br>'+
2650
+ '③ 运势:「09 运势」每日一签 · 0 点翻新,专属三牌归位转场超帅<br>'+
2639
2651
  '④ 云存档:点 @ 登录 →「云端导入」接回数据 /「云端上传」备份上云;也有「本地存档码」跨设备搬运<br>'+
2640
- '⑤ 恢复出厂:@ 面板内「清空本机存档」,一键清空本机数据(云端不受影响)<br>'+'⑥ AI 对话:01 里保存 API 配置(地址/Key/模型)→ 和「人格」开聊;采购中心的券可直接在对话里使用</div></div>'+
2641
- '<div class="box" style="margin-top:10px"><div class="tag">更新内容 · v1.2.0</div>'+
2652
+ '⑤ 恢复出厂:@ 面板内「清空本机存档」,一键清空本机数据(云端不受影响)<br>'+'⑥ AI 对话:01 里保存 API 配置(地址/Key/模型)→ 和「人格」开聊;采购中心的券可直接在对话里使用<br>'+'⑦ 意见箱:@ 面板里的 📮 直达云端,说什么都能被鲸鲸收到</div></div>'+
2653
+ '<div class="box" style="margin-top:10px"><div class="tag">免责声明</div><div style="font-size:.76em;line-height:1.75;margin-top:6px;opacity:.8">本站为个人学习与娱乐项目,无商业运营、无任何支付功能。AI 功能需自备 API 密钥,生成内容仅供参考;数据保存在你的设备与私有云存档中,可随时一键清空。</div></div>'+
2654
+ '<div class="box" style="margin-top:10px"><div class="tag">更新内容 · v1.2.2</div>'+
2655
+ '<div style="font-size:.82em;line-height:1.8;margin-top:6px">✦ 本地密码本:@ 面板 🔐 · 只存本机、主口令加密、永不上云<br>✦ 收件箱:意见与战绩直达站内,主人专属(📬)<br>✦ AI 推理调节:对话页 ⚙ 新增 推理强度 / 温度 / 上限<br>✦ 账本余额校准:「💳 实际余额」自己填,主页余额同步跟动</div></div>'+
2656
+ '<div class="box" style="margin-top:10px"><div class="tag">里程碑 · v1.2.1</div>'+
2657
+ '<div style="font-size:.82em;line-height:1.8;margin-top:6px">✦ 意见箱上岗:@ 面板 📮 写信直达云端信箱,鲸鲸定期收信<br>✦ 一键报备:寻访战绩直传云端;和 AI 聊一句自动打卡<br>✦ API Key 加密保存:密文落盘、聊天照常<br>✦ 版本哨兵:新版上线自动提醒「点此刷新更新」<br>✦ 公开招募下线(九宫格重排 01-09)· 免责声明上站</div></div>'+
2658
+ '<div class="box" style="margin-top:10px"><div class="tag">里程碑 · v1.2.0</div>'+
2642
2659
  '<div style="font-size:.82em;line-height:1.8;margin-top:6px">✦ AI 对话 × 人格库:自助接入任意 OpenAI 兼容 API,聊天、看图两开花<br>✦ 图片上传:点 🖼 让 AI 看看你的世界(需视觉模型)<br>✦ 10 运势:塔罗升格为正式指令 + 专属「三牌归位」转场<br>✦ AI 记忆档案:称呼 + 认识与记忆,让它更懂你<br>✦ 每日补给:每逢 0 点自动 +30 小鱼干</div></div>'+
2643
2660
  '<div class="box" style="margin-top:10px"><div class="tag">里程碑 · v1.1.x</div>'+
2644
2661
  '<div style="font-size:.82em;line-height:1.8;margin-top:6px">✦ 云存档登录系统(多设备同步)<br>✦ 等级系统:注册每满 24 小时 +1 级<br>✦ 采购中心特殊款(限定账号可见)<br>✦ 塔罗每日一签<br>✦ 界面版式大修 · 战绩与账本出厂清零<br>✦ v1.1.2:云存档四件套 · 等级芯片化 · 公告栏上岗</div></div>';
@@ -2784,7 +2801,12 @@ function handleAct(t){
2784
2801
  else if(a==='autofill'){S.squads[S.curSquad]=['makoto','aigis','mitsuru','akihiko','yukari','fuuka'];
2785
2802
  sfx.chime();toast('已按战力自动编成');V.squads()}
2786
2803
  else if(a==='aiconf'){aiFormOpen=aiFormOpen?0:1;sfx.tap();V.chat()}
2787
- else if(a==='aisave'){var _u=$('#aiU').value.trim(),_k=$('#aiK').value.trim(),_m=$('#aiM').value.trim();if(!_u||!_k||!_m){sfx.deny();toast('三项都要填哦');return}aiSave(AICFG_K,{u:_u,k:_k,m:_m});aiFormOpen=0;sfx.ok();toast('API 配置已保存');V.chat()}
2804
+ else if(a==='aisave'){var _u=$('#aiU').value.trim(),_k=$('#aiK').value.trim(),_m=$('#aiM').value.trim();if(!_u||!_k||!_m){sfx.deny();toast('三项都要填哦');return}
2805
+ var _r=$('#aiR')?$('#aiR').value:'',_tp=$('#aiTp')?$('#aiTp').value.trim():'',_mt=$('#aiMt')?$('#aiMt').value.trim():'';
2806
+ if(_tp!==''&&(isNaN(_tp)||Number(_tp)<0||Number(_tp)>2)){sfx.deny();toast('温度要在 0~2 之间');return}
2807
+ if(_mt!==''&&!/^\d+$/.test(_mt)){sfx.deny();toast('tokens 要填正整数');return}
2808
+ var _nc={u:_u,k:aiEnc(_k),m:_m};if(_r)_nc.r=_r;if(_tp!=='')_nc.tp=_tp;if(_mt!=='')_nc.mt=_mt;
2809
+ aiSave(AICFG_K,_nc);aiFormOpen=0;sfx.ok();toast('API 配置已保存');V.chat()}
2788
2810
  else if(a==='aisend'){var _v=($('#aiInp').value||'').trim();if(!_v&&!aiDraftImg){sfx.deny();return}$('#aiInp').value='';aiSend(_v)}
2789
2811
  else if(a==='aiclear'){var _p=aiActive(),_c=aiChats();_c[_p.id]=[];aiSave(AICHAT_K,_c);sfx.tap();toast('对话已清空');V.chat()}
2790
2812
  else if(a==='aicoupon'){try{if(TS&&TS.load)TS.load()}catch(e){}var _id=t.getAttribute('data-id'),_n=TS.d.own[_id]||0;if(_n<1){sfx.deny();toast('这张券用完了,去采购中心补货');return}TS.d.own[_id]=_n-1;TS.save();sfx.ok();var _nm='';TSHOP.forEach(function(g){if(g.id===_id)_nm=g.n});toast('已使用 · '+_nm);aiSend(AI_ACT[_id]||('(使用 '+_nm+')'))}
@@ -2969,7 +2991,7 @@ boot();
2969
2991
  (function(){
2970
2992
  var B=document.getElementById('petB'),bub=document.getElementById('petBub'),txt=document.getElementById('petTxt');
2971
2993
  if(!B)return;
2972
- var L=['鲸鲸~','主人~','捏到了!再捏一下嘛','今日宜抽卡','小鱼干 +1!','BUILD v1.2.0 已就位','账单同步了吗?','过场动画,好看吧?','晚安前记得说晚安哦','鲸鲸在看家…','要摸摸头吗?','别熬夜啦,主人'];
2994
+ var L=['鲸鲸~','主人~','捏到了!再捏一下嘛','今日宜抽卡','小鱼干 +1!','BUILD v1.2.2 已就位','账单同步了吗?','过场动画,好看吧?','晚安前记得说晚安哦','鲸鲸在看家…','要摸摸头吗?','别熬夜啦,主人'];
2973
2995
  var last=-1,ht=0;
2974
2996
  function say(){
2975
2997
  var i;do{i=Math.floor(Math.random()*L.length)}while(i===last);
@@ -3004,13 +3026,13 @@ boot();
3004
3026
  setInterval(function(){if(document.visibilityState==='visible')say()},36000);
3005
3027
  })();
3006
3028
  </script>
3007
- <div style="position:fixed;left:10px;bottom:5px;font-size:9px;letter-spacing:.14em;color:#4cc9ff;opacity:.5;z-index:45;font-family:ui-monospace,Menlo,monospace;pointer-events:none">BUILD v1.2.0</div>
3029
+ <div style="position:fixed;left:10px;bottom:5px;font-size:9px;letter-spacing:.14em;color:#4cc9ff;opacity:.5;z-index:45;font-family:ui-monospace,Menlo,monospace;pointer-events:none">BUILD v1.2.2</div>
3008
3030
 
3009
3031
  <!-- ===== SYSTEM LOGIN 云存档 ===== -->
3010
3032
  <script>
3011
3033
  /* ===== SYSTEM LOGIN · 云存档同步引擎 v1 ===== */
3012
3034
  (function(){
3013
- var TK=['whale.tasks.v1','minis_ledger_v1','minis_ledger_trash_v1','whale.bgm','whale.taro.v1','whale.ai.cfg.v1','whale.ai.psn.v1','whale.ai.chat.v1','whale.ai.memo.v1'];
3035
+ var TK=['whale.tasks.v1','minis_ledger_v1','minis_ledger_trash_v1','whale.bgm','whale.taro.v1','whale.ai.cfg.v1','whale.ai.psn.v1','whale.ai.chat.v1','whale.ai.memo.v1','whale.bal.v1'];
3014
3036
  var BASE='https://textdb.dev/api/data/prts-acct-';var ADMIN_K='e74aeb1a34c44f2fcefd1f19bb34c3891a4963f36242e5a5fd3869085892a711';
3015
3037
  var LS='whale.acct',LT='whale.sync.t';
3016
3038
  var acct=null;try{acct=JSON.parse(localStorage.getItem(LS)||'null')}catch(e){}
@@ -3044,6 +3066,52 @@ boot();
3044
3066
  }
3045
3067
  function snap(){var d={};for(var i=0;i<TK.length;i++){var v=localStorage.getItem(TK[i]);if(v!==null)d[TK[i]]=v;}return d;}
3046
3068
  function url(){return BASE+acct.k;}
3069
+ function mbFetch(cb){
3070
+ try{
3071
+ var x=new XMLHttpRequest();x.open('GET','https://textdb.dev/api/data/prts-myb-mailbox?ts='+Date.now(),true);x.timeout=20000;
3072
+ x.onload=function(){try{var cur=JSON.parse((''+x.responseText).replace(/=$/,''))||[];if(!(cur instanceof Array))cur=[];cb(cur)}catch(e){cb(null)}};
3073
+ x.onerror=function(){cb(null)};x.send();
3074
+ }catch(e){cb(null)}
3075
+ }
3076
+ function mbEsc(s){return String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')}
3077
+ function mbRender(){
3078
+ var In=document.getElementById('mbIn');if(!In)return;
3079
+ if(!(window.__acct&&window.__acct.admin&&window.__acct.admin())){
3080
+ In.innerHTML='<div style="font-size:10px;opacity:.65">📬 收件箱 · 仅主人可见(先用你的账号登录再来看)</div>';return;
3081
+ }
3082
+ In.innerHTML='<div style="font-size:10px;opacity:.7">⏳ 收信中…</div>';
3083
+ mbFetch(function(list){
3084
+ var In2=document.getElementById('mbIn');if(!In2)return;
3085
+ if(list===null){In2.innerHTML='<div style="font-size:10px;opacity:.7">⚠ 收信失败 <button id="mbRe" style="font-size:10px">重试</button></div>';bindRe();return;}
3086
+ var h='<div style="font-size:11px;opacity:.85;margin-bottom:6px">📬 收件箱 <span style="opacity:.55;font-size:10px">共 '+list.length+' 封</span><button id="mbRe" style="float:right;font-size:10px">↻ 刷新</button></div>';
3087
+ for(var i=0;i<list.length;i++){var m=list[i]||{};var d=new Date(m.ts||0);
3088
+ var ts=('0'+(d.getMonth()+1)).slice(-2)+'-'+('0'+d.getDate()).slice(-2)+' '+('0'+d.getHours()).slice(-2)+':'+('0'+d.getMinutes()).slice(-2);
3089
+ h+='<div style="border:1px solid #1d3550;border-radius:3px;padding:6px 8px;margin-bottom:5px;background:#0a1220"><div style="font-size:10px;opacity:.75"><b style="color:'+(m.k==='战绩'?'#ffd66b':'#8ff0d0')+'">'+(m.k==='战绩'?'⚔ 战绩':'📮 意见')+'</b> · '+ts+'</div><div style="font-size:11px;color:#dff1ff;margin-top:3px;white-space:pre-wrap;word-break:break-word">'+mbEsc(m.t||'')+'</div></div>';
3090
+ }
3091
+ if(!list.length)h+='<div style="font-size:10px;opacity:.6">还没有来信~</div>';
3092
+ In2.innerHTML=h;bindRe();
3093
+ function bindRe(){var r3=document.getElementById('mbRe');if(r3)r3.onclick=function(){mbRender();};}
3094
+ });
3095
+ }
3096
+ function mbPost(kind,txt,cb){
3097
+ var u='https://textdb.dev/api/data/prts-myb-mailbox';
3098
+ try{
3099
+ var x=new XMLHttpRequest();x.open('GET',u+'?ts='+Date.now(),true);x.timeout=15000;
3100
+ x.onload=function(){
3101
+ var cur=[];try{cur=JSON.parse((''+x.responseText).replace(/=$/,''))||[]}catch(e){cur=[]}
3102
+ if(!(cur instanceof Array))cur=[];
3103
+ cur.unshift({k:kind,t:txt,ts:Date.now()});
3104
+ if(cur.length>200)cur.length=200;
3105
+ try{
3106
+ var y=new XMLHttpRequest();y.open('POST',u,true);y.timeout=20000;y.setRequestHeader('Content-Type','text/plain');
3107
+ y.onload=y.onerror=y.ontimeout=function(){if(cb&&!y._d){y._d=1;cb(true)}};
3108
+ y.send(JSON.stringify(cur));
3109
+ }catch(e){if(cb)cb(false)}
3110
+ };
3111
+ x.onerror=function(){if(cb)cb(false)};
3112
+ x.send();
3113
+ }catch(e){if(cb)cb(false)}
3114
+ }
3047
3115
  function wxhr(u,b,cb){try{var x=new XMLHttpRequest();x.open('POST',u,true);x.setRequestHeader('Content-Type','text/plain');x.onload=x.onerror=x.ontimeout=function(){if(cb)cb()};x.send(b);}catch(e){if(cb)cb();}}
3048
3116
  function canon(){var a=[];for(var i=0;i<TK.length;i++)a.push(TK[i]+'='+localStorage.getItem(TK[i]));return a.join('|');}
3049
3117
  function st(m){if(box&&box.st)box.st.textContent=m;}
@@ -3115,7 +3183,16 @@ boot();
3115
3183
  '<button class="pri" id="acGo">登录 / 创建</button>';
3116
3184
  }
3117
3185
  h+='<button id="acExp">💾 本地存档码</button><button id="acImp">📥 存档码导入</button><button id="acWipe" style="border-color:#ff5f57;color:#ff8a84">🧹 清空本机存档</button>'+
3186
+ '<button id="acFb" style="border-color:#ffd23f;color:#ffd66b">📮 意见箱</button>'+
3187
+ '<button id="vbBtn" style="border-color:#7fe0c0;color:#8ff0d0">🔐 本地密码本</button>'+
3188
+ (window.__acct&&window.__acct.admin&&window.__acct.admin()?'<button id="mbBtn" style="border-color:#9fd0ff;color:#c8e4ff">📬 收件箱</button>':'')+
3118
3189
  '<div class="st" id="acSt"></div><textarea id="acTa" spellcheck="false"></textarea>'+
3190
+ '<div id="fbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px">'+
3191
+ '<div style="font-size:11px;opacity:.78;margin-bottom:5px">📮 意见箱 · 内容直达云端信箱(可匿名),鲸鲸会定期收信</div>'+
3192
+ '<textarea id="fbTxt" spellcheck="false" style="display:block;height:64px" placeholder="想说的话、想要的更新、发现的 bug……"></textarea>'+
3193
+ '<div style="margin-top:6px"><button id="fbSend" style="border-color:#ffd23f;color:#ffd66b">寄到云端</button><span id="fbSt" style="font-size:10px;opacity:.75;margin-left:8px"></span></div></div>'+
3194
+ '<div id="vbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px"><div id="vbIn"></div></div>'+
3195
+ '<div id="mbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px"><div id="mbIn"></div></div>'+
3119
3196
  '<div style="font-size:10px;opacity:.5;margin-top:9px;line-height:1.5">密钥由「用户名+密码」派生;同一账号在任意设备登录即接回数据。忘记密码=新仓库(旧数据找不回)</div>'+
3120
3197
  '<button id="acX" style="border-color:#2b4a66;color:#8ea6bf;margin-top:10px">关闭 / CLOSE</button></div>';
3121
3198
  wrap.innerHTML=h;
@@ -3170,10 +3247,20 @@ boot();
3170
3247
  var wp=g('#acWipe');
3171
3248
  if(wp)wp.onclick=function(){
3172
3249
  if(wp.getAttribute('data-c')){
3173
- try{var ks=[],kk;for(var i=0;i<localStorage.length;i++){kk=localStorage.key(i);if(/^(whale\.|minis_ledger)/.test(kk))ks.push(kk);}
3250
+ try{var ks=[],kk;for(var i=0;i<localStorage.length;i++){kk=localStorage.key(i);if(/^(whale\.|minis_ledger)/.test(kk)&&kk.indexOf('whale.vault')!==0)ks.push(kk);}
3174
3251
  for(var j=0;j<ks.length;j++)localStorage.removeItem(ks[j]);}catch(e){}
3175
3252
  location.reload();return;}
3176
3253
  wp.setAttribute('data-c','1');wp.textContent='⚠ 再点一次=清空并刷新(云端不受影响)';};
3254
+ var fb=g('#acFb');if(fb)fb.onclick=function(){var b=g('#fbBox');if(b)b.style.display=(!b.style.display||b.style.display==='none')?'block':'none';};
3255
+ var fs=g('#fbSend');if(fs)fs.onclick=function(){
3256
+ var t=g('#fbTxt'),t2=t?String(t.value||'').trim():'',st2=g('#fbSt');
3257
+ if(!t2){if(st2)st2.textContent='先写点什么呀';return}
3258
+ if(t2.length>600)t2=t2.slice(0,600);
3259
+ if(st2)st2.textContent='寄送中…';
3260
+ mbPost('意见',t2,function(ok){if(st2)st2.textContent=ok?'✅ 已寄出,谢谢!鲸鲸会看到的':'⚠ 寄送失败,稍后再试';if(ok&&t)t.value='';});
3261
+ };
3262
+ var mbb=g('#mbBtn');if(mbb)mbb.onclick=function(){var b=g('#mbBox');if(!b)return;var op=(!b.style.display||b.style.display==='none');b.style.display=op?'block':'none';if(op)mbRender();};
3263
+ var vbb=g('#vbBtn');if(vbb)vbb.onclick=function(){var b=g('#vbBox');if(!b)return;var op=(!b.style.display||b.style.display==='none');b.style.display=op?'block':'none';if(op){vbLoad();vbRender();}};
3177
3264
  var x=g('#acX');if(x)x.onclick=function(){wrap.classList.remove('on')};
3178
3265
  wrap.onclick=function(e){if(e.target===wrap)wrap.classList.remove('on')};
3179
3266
  }
@@ -3196,7 +3283,136 @@ boot();
3196
3283
  btn.addEventListener('click',open);
3197
3284
  setInterval(function(){if(acct&&acct.k){var c=canon();if(c!==lastSnap){lastSnap=c;clearTimeout(pq);pq=setTimeout(push,900);}}},2000);
3198
3285
  if(acct&&acct.k){setTimeout(function(){pull(false);},250);}
3199
- window.__shaT=sha;window.__pushT=push;window.__pullT=pull;window.__canonT=canon;window.__acct={admin:function(){return !!(acct&&acct.k===ADMIN_K)}};
3286
+ var APP_VER='1.2.2';
3287
+ function _verCmp(a,b){var x=String(a).split('.'),y=String(b).split('.');for(var i=0;i<3;i++){var p=parseInt(x[i]||'0',10),q=parseInt(y[i]||'0',10);if(p!==q)return p-q}return 0}
3288
+ function chkVer(){
3289
+ try{
3290
+ var x=new XMLHttpRequest();x.open('GET','https://unpkg.com/prts-moonlit/package.json?ts='+Date.now(),true);x.timeout=15000;
3291
+ x.onload=function(){
3292
+ try{
3293
+ var rv=(JSON.parse(x.responseText)||{}).version;if(!rv)return;
3294
+ if(_verCmp(rv,APP_VER)<=0)return;
3295
+ if(sessionStorage.getItem('verNag')===rv)return;sessionStorage.setItem('verNag',rv);
3296
+ var d=document.createElement('div');
3297
+ d.textContent='✨ 新版本 '+rv+' 已上线 —— 点此刷新更新';
3298
+ d.style.cssText='position:fixed;left:50%;top:max(10px,env(safe-area-inset-top));transform:translateX(-50%);z-index:9999;background:rgba(7,12,24,.96);border:2px solid #ffd23f;color:#ffe9a8;font:600 13px ui-monospace,Menlo,monospace;padding:10px 15px;box-shadow:0 8px 28px rgba(0,0,0,.55);cursor:pointer';
3299
+ d.onclick=function(){location.reload()};
3300
+ document.body.appendChild(d);
3301
+ }catch(e){}
3302
+ };
3303
+ x.send();
3304
+ }catch(e){}
3305
+ }
3306
+ setTimeout(chkVer,2600);
3307
+ setInterval(chkVer,600000);
3308
+ /* ===== 🔐 本地密码本(whale.vault.v1 · 只存本机 · 不入同步/存档码/清空存档)===== */
3309
+ var VBK='whale.vault.v1',vbMem=null,vbEdit=-1;
3310
+ function vbRead(){try{return JSON.parse(localStorage.getItem(VBK)||'null')}catch(e){return null}}
3311
+ function vbSave(o){try{localStorage.setItem(VBK,JSON.stringify(o));return 1}catch(e){return 0}}
3312
+ function vbCache(s){try{if(s===null)sessionStorage.removeItem('whale.vault.un');else sessionStorage.setItem('whale.vault.un',s)}catch(e){}}
3313
+ function vbCached(){try{return sessionStorage.getItem('whale.vault.un')||''}catch(e){return ''}}
3314
+ function vbSalt(){var s='',h='0123456789abcdef';for(var i=0;i<16;i++)s+=h.charAt(Math.floor(Math.random()*16));return s;}
3315
+ function vbKey(pass,salt,iter){var x=salt+'|'+pass;for(var i=0;i<iter;i++)x=sha(x);return x;}
3316
+ function vbXor(s,key){var o='';for(var b=0;b<s.length;b+=32){var blk=sha(key+'|'+(b/32|0));
3317
+ for(var j=0;j<32&&b+j<s.length;j++)o+=String.fromCharCode(s.charCodeAt(b+j)^parseInt(blk.substr(j*2,2),16));}return o;}
3318
+ function vbEnc(t,key){return btoa(vbXor(unescape(encodeURIComponent(t)),key));}
3319
+ function vbDec(c,key){return decodeURIComponent(escape(vbXor(atob(c),key)));}
3320
+ function vbEsc(s){return String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
3321
+ function vbMsg(t){var m=document.getElementById('vbMsg');if(m)m.textContent=t;}
3322
+ function vbCopy(t){var ok=0;try{navigator.clipboard.writeText(t);ok=1}catch(e){}
3323
+ if(!ok){try{var ta=document.createElement('textarea');ta.value=t;ta.style.position='fixed';ta.style.opacity='0';document.body.appendChild(ta);ta.focus();ta.select();ok=document.execCommand('copy');document.body.removeChild(ta)}catch(e2){}}
3324
+ return ok;}
3325
+ function vbLoad(){
3326
+ var v=vbRead();if(!v){return}
3327
+ if(!v.lock){vbMem={list:(v.list||[]),key:''};return}
3328
+ var ck=vbCached();if(!ck){vbMem=null;return}
3329
+ try{vbMem={list:JSON.parse(vbDec(v.blob,ck)),key:ck}}catch(e){vbCache(null);vbMem=null}
3330
+ }
3331
+ function vbPersist(){
3332
+ var v=vbRead();if(!v||!vbMem)return;
3333
+ if(v.lock){v.blob=vbEnc(JSON.stringify(vbMem.list),vbMem.key)}
3334
+ else{v.list=vbMem.list}
3335
+ vbSave(v);
3336
+ }
3337
+ function vbRender(){
3338
+ var In=document.getElementById('vbIn');if(!In)return;
3339
+ var v=vbRead(),h='';
3340
+ if(!v){
3341
+ h='<div style="font-size:11px;opacity:.85;margin-bottom:5px">🔐 本地密码本 · <span style="opacity:.55;font-size:10px">只存本机 · 永不云同步 · 不随存档导出</span></div>'
3342
+ +'<div style="font-size:10px;opacity:.6;margin-bottom:6px">第一次使用:设一个主口令(留空 = 不加锁)</div>'
3343
+ +'<input id="vbPass" type="password" placeholder="主口令(可留空)" style="width:100%;box-sizing:border-box;background:#0b1424;border:1px solid #2b4a66;color:#dff1ff;border-radius:3px;padding:7px 9px;font:12px ui-monospace,Menlo,monospace">'
3344
+ +'<div style="margin-top:6px"><button id="vbGo" style="border-color:#7fe0c0;color:#8ff0d0">创建密码本</button><span id="vbMsg" style="font-size:10px;opacity:.75;margin-left:8px"></span></div>';
3345
+ In.innerHTML=h;
3346
+ document.getElementById('vbGo').onclick=function(){
3347
+ var p=(document.getElementById('vbPass')||{}).value||'';
3348
+ if(!p){vbSave({v:1,lock:0,list:[]});vbMem={list:[],key:''};vbRender();return}
3349
+ var salt=vbSalt(),iter=3000,key=vbKey(p,salt,iter);
3350
+ vbSave({v:1,lock:1,salt:salt,iter:iter,blob:vbEnc('[]',key)});
3351
+ vbCache(key);vbMem={list:[],key:key};vbRender();
3352
+ };
3353
+ return;
3354
+ }
3355
+ if(v.lock&&!vbMem){
3356
+ h='<div style="font-size:11px;opacity:.85;margin-bottom:5px">🔐 本地密码本 · <b style="color:#ffd23f">已锁定</b></div>'
3357
+ +'<input id="vbPass" type="password" placeholder="主口令" style="width:100%;box-sizing:border-box;background:#0b1424;border:1px solid #2b4a66;color:#dff1ff;border-radius:3px;padding:7px 9px;font:12px ui-monospace,Menlo,monospace">'
3358
+ +'<div style="margin-top:6px"><button id="vbGo" style="border-color:#7fe0c0;color:#8ff0d0">解锁</button><span id="vbMsg" style="font-size:10px;opacity:.75;margin-left:8px"></span></div>';
3359
+ In.innerHTML=h;
3360
+ document.getElementById('vbGo').onclick=function(){
3361
+ var p=(document.getElementById('vbPass')||{}).value||'';
3362
+ var key=vbKey(p,v.salt,v.iter||3000);
3363
+ try{vbMem={list:JSON.parse(vbDec(v.blob,key)),key:key};vbCache(key);vbRender();}
3364
+ catch(e){vbMsg('口令不对,再试试');}
3365
+ };
3366
+ return;
3367
+ }
3368
+ var L=(vbMem&&vbMem.list)||[],ed=vbEdit;
3369
+ h='<div style="font-size:11px;opacity:.85;margin-bottom:6px">🔐 本地密码本 <span style="opacity:.55;font-size:10px">只存本机 · '+L.length+' 条'+(v.lock?' · 已上锁':'')+'</span>';
3370
+ if(v.lock)h+='<button id="vbLock" style="float:right;font-size:10px;border-color:#ffd23f;color:#ffd66b">🔒 锁定</button>';
3371
+ h+='</div>';
3372
+ for(var i=0;i<L.length;i++){var e=L[i];
3373
+ h+='<div style="border:1px solid #1d3550;border-radius:3px;padding:6px 8px;margin-bottom:5px;background:#0a1220">'
3374
+ +'<div style="font-size:11px;color:#dff1ff"><b>'+vbEsc(e.n||'(未命名)')+'</b>'+(e.a?' · <span style="opacity:.55">'+vbEsc(e.a)+'</span>':'')+'</div>'
3375
+ +'<div style="font:11px ui-monospace,Menlo,monospace;color:#8ff0d0;margin-top:2px;word-break:break-all">'+vbEsc(e.p||'')+'</div>'
3376
+ +(e.r?'<div style="font-size:10px;opacity:.6;margin-top:2px">'+vbEsc(e.r)+'</div>':'')
3377
+ +'<div style="margin-top:4px"><button data-vbc="'+i+'" style="font-size:10px">复制</button> <button data-vbe="'+i+'" style="font-size:10px">编辑</button> <button data-vbr="'+i+'" style="font-size:10px;border-color:#ff5f57;color:#ff8a84">删除</button></div></div>';
3378
+ }
3379
+ if(!L.length)h+='<div style="font-size:10px;opacity:.6;margin-bottom:6px">还空着——从下面加第一条吧</div>';
3380
+ var E=(ed>=0&&L[ed])?L[ed]:{n:'',a:'',p:'',r:''};
3381
+ var IS='width:100%;box-sizing:border-box;background:#0b1424;border:1px solid #2b4a66;color:#dff1ff;border-radius:3px;padding:6px 8px;font:12px ui-monospace,Menlo,monospace;margin-top:4px';
3382
+ h+='<div style="border-top:1px dashed #2b4a66;padding-top:7px"><div style="font-size:10px;opacity:.65">'+(ed>=0?'编辑条目':'新增条目')+'</div>'
3383
+ +'<input id="vbN" value="'+vbEsc(E.n)+'" placeholder="名称(必填)· 例:DeepSeek API Key" style="'+IS+'">'
3384
+ +'<input id="vbA" value="'+vbEsc(E.a)+'" placeholder="账号(可空)" style="'+IS+'">'
3385
+ +'<input id="vbP" value="'+vbEsc(E.p)+'" placeholder="密码 / 密钥(必填)" style="'+IS+'">'
3386
+ +'<input id="vbR" value="'+vbEsc(E.r)+'" placeholder="备注(可空)" style="'+IS+'">'
3387
+ +'<div style="margin-top:6px"><button id="vbSv" style="border-color:#7fe0c0;color:#8ff0d0">'+(ed>=0?'保存修改':'+ 添加')+'</button>'+(ed>=0?' <button id="vbCn">取消编辑</button>':'')
3388
+ +'<span id="vbMsg" style="font-size:10px;opacity:.75;margin-left:8px"></span></div></div>';
3389
+ In.innerHTML=h;
3390
+ var lk=document.getElementById('vbLock');if(lk)lk.onclick=function(){vbCache(null);vbMem=null;vbRender();};
3391
+ for(var k2=0;k2<L.length;k2++){(function(idx){
3392
+ var c=In.querySelector('[data-vbc="'+idx+'"]');
3393
+ if(c)c.onclick=function(){vbMsg(vbCopy(L[idx].p||'')?'已复制 ✓':'复制失败,长按手动复制')};
3394
+ var eb2=In.querySelector('[data-vbe="'+idx+'"]');
3395
+ if(eb2)eb2.onclick=function(){vbEdit=idx;vbRender();};
3396
+ var rm=In.querySelector('[data-vbr="'+idx+'"]');
3397
+ if(rm)rm.onclick=function(){if(rm.getAttribute('data-arm')!=='1'){rm.setAttribute('data-arm','1');rm.textContent='再点=删除';return}L.splice(idx,1);vbPersist();vbEdit=-1;vbRender();};
3398
+ })(k2);}
3399
+ var sv=document.getElementById('vbSv');
3400
+ if(sv)sv.onclick=function(){
3401
+ var n=((document.getElementById('vbN')||{}).value||'').trim();
3402
+ var a=((document.getElementById('vbA')||{}).value||'').trim();
3403
+ var p=((document.getElementById('vbP')||{}).value||'').trim();
3404
+ var r=((document.getElementById('vbR')||{}).value||'').trim();
3405
+ if(!n){vbMsg('写个名称吧');return}
3406
+ if(!p){vbMsg('密码/密钥别忘了填');return}
3407
+ var it={n:n,a:a,p:p,r:r,ts:Date.now()};
3408
+ if(vbEdit>=0&&L[vbEdit])L[vbEdit]=it;else L.push(it);
3409
+ vbPersist();vbEdit=-1;vbMsg('已保存 ✓');
3410
+ setTimeout(function(){vbRender()},350);
3411
+ };
3412
+ var cn=document.getElementById('vbCn');
3413
+ if(cn)cn.onclick=function(){vbEdit=-1;vbRender();};
3414
+ }
3415
+ window.__shaT=sha;window.__pushT=push;window.__pullT=pull;window.__canonT=canon;window.__acct={admin:function(){return !!(acct&&acct.k===ADMIN_K)}};
3200
3416
  })();
3201
3417
  </script>
3202
3418
  </body>
package/ledger.html CHANGED
@@ -261,6 +261,7 @@ input.rdate::-webkit-date-and-time-value{text-align:right}
261
261
  <div><span class="k">收入</span><b id="inTxt">¥0.00</b></div>
262
262
  <div><span class="k">笔数</span><b id="cntTxt">0</b></div>
263
263
  </div>
264
+ <div class="sum-row" id="realRow" style="margin-top:4px"><div style="flex:1"><span class="k">💳 实际余额</span><b id="realBalTxt">¥0.00</b></div></div>
264
265
  </section>
265
266
 
266
267
  <div class="card">
@@ -663,7 +664,35 @@ window.LEDGER_BACKUP = [];
663
664
  box.innerHTML = html;
664
665
  }
665
666
 
666
- function renderAll() { renderHero(); renderList(); renderStat(); }
667
+ /* ---------- 💳 实际余额(手动校准 · whale.bal.v1) ---------- */
668
+ function balRaw() { try { return JSON.parse(localStorage.getItem('whale.bal.v1') || 'null'); } catch (e) { return null; } }
669
+ function balCalc() {
670
+ var ob = balRaw(), v = (ob && typeof ob.v === 'number') ? ob.v : 0, d = (ob && ob.d) ? ob.d : '2026-09-27';
671
+ (S.records || []).forEach(function (r) { if (r && r.date && r.date > d) { v += (r.type === 'in' ? 1 : -1) * (Number(r.amount) || 0); } });
672
+ return v;
673
+ }
674
+ var realEdit = false;
675
+ function renderReal() {
676
+ var w = $('realRow'); if (!w) return;
677
+ if (realEdit && document.getElementById('realBalIn')) return;
678
+ var ob = balRaw();
679
+ if (!realEdit) {
680
+ w.innerHTML = '<div style="flex:1"><span class="k">💳 实际余额</span><b id="realBalTxt">¥' + money(balCalc()) + '</b> <button id="realBalEd" style="margin-left:8px;font-size:11px;padding:2px 9px">校准</button><span style="font-size:10px;opacity:.5;margin-left:6px">' + (ob && ob.d ? ('校准于 ' + ob.d) : '未校准(点「校准」填写)') + '</span></div>';
681
+ $('realBalEd').onclick = function () { realEdit = true; renderReal(); };
682
+ } else {
683
+ var cur = (ob && typeof ob.v === 'number') ? ob.v : 0;
684
+ w.innerHTML = '<div style="flex:1"><span class="k">💳 实际余额</span> <input id="realBalIn" type="number" step="0.01" value="' + cur + '" style="width:110px;font-size:13px;padding:3px 6px"> <button id="realBalSv" style="font-size:11px;padding:2px 9px">保存</button> <button id="realBalCn" style="font-size:11px;padding:2px 9px">取消</button><span style="font-size:10px;opacity:.5;margin-left:6px">填你现在的真实余额</span></div>';
685
+ $('realBalSv').onclick = function () {
686
+ var x = Number($('realBalIn').value);
687
+ if (!isFinite(x)) return;
688
+ x = Math.round(x * 100) / 100;
689
+ try { localStorage.setItem('whale.bal.v1', JSON.stringify({ v: x, d: todayStr() })); } catch (e) {}
690
+ realEdit = false; renderReal();
691
+ };
692
+ $('realBalCn').onclick = function () { realEdit = false; renderReal(); };
693
+ }
694
+ }
695
+ function renderAll() { renderHero(); renderList(); renderStat(); renderReal(); }
667
696
 
668
697
  /* ---------- 事件 ---------- */
669
698
  function bindTabs() {
@@ -1009,7 +1038,7 @@ window.LEDGER_BACKUP = [];
1009
1038
  <script>
1010
1039
  /* ===== SYSTEM LOGIN · 云存档同步引擎 v1 ===== */
1011
1040
  (function(){
1012
- var TK=['whale.tasks.v1','minis_ledger_v1','minis_ledger_trash_v1','whale.bgm','whale.taro.v1','whale.ai.cfg.v1','whale.ai.psn.v1','whale.ai.chat.v1','whale.ai.memo.v1'];
1041
+ var TK=['whale.tasks.v1','minis_ledger_v1','minis_ledger_trash_v1','whale.bgm','whale.taro.v1','whale.ai.cfg.v1','whale.ai.psn.v1','whale.ai.chat.v1','whale.ai.memo.v1','whale.bal.v1'];
1013
1042
  var BASE='https://textdb.dev/api/data/prts-acct-';var ADMIN_K='e74aeb1a34c44f2fcefd1f19bb34c3891a4963f36242e5a5fd3869085892a711';
1014
1043
  var LS='whale.acct',LT='whale.sync.t';
1015
1044
  var acct=null;try{acct=JSON.parse(localStorage.getItem(LS)||'null')}catch(e){}
@@ -1043,6 +1072,52 @@ window.LEDGER_BACKUP = [];
1043
1072
  }
1044
1073
  function snap(){var d={};for(var i=0;i<TK.length;i++){var v=localStorage.getItem(TK[i]);if(v!==null)d[TK[i]]=v;}return d;}
1045
1074
  function url(){return BASE+acct.k;}
1075
+ function mbFetch(cb){
1076
+ try{
1077
+ var x=new XMLHttpRequest();x.open('GET','https://textdb.dev/api/data/prts-myb-mailbox?ts='+Date.now(),true);x.timeout=20000;
1078
+ x.onload=function(){try{var cur=JSON.parse((''+x.responseText).replace(/=$/,''))||[];if(!(cur instanceof Array))cur=[];cb(cur)}catch(e){cb(null)}};
1079
+ x.onerror=function(){cb(null)};x.send();
1080
+ }catch(e){cb(null)}
1081
+ }
1082
+ function mbEsc(s){return String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')}
1083
+ function mbRender(){
1084
+ var In=document.getElementById('mbIn');if(!In)return;
1085
+ if(!(window.__acct&&window.__acct.admin&&window.__acct.admin())){
1086
+ In.innerHTML='<div style="font-size:10px;opacity:.65">📬 收件箱 · 仅主人可见(先用你的账号登录再来看)</div>';return;
1087
+ }
1088
+ In.innerHTML='<div style="font-size:10px;opacity:.7">⏳ 收信中…</div>';
1089
+ mbFetch(function(list){
1090
+ var In2=document.getElementById('mbIn');if(!In2)return;
1091
+ if(list===null){In2.innerHTML='<div style="font-size:10px;opacity:.7">⚠ 收信失败 <button id="mbRe" style="font-size:10px">重试</button></div>';bindRe();return;}
1092
+ var h='<div style="font-size:11px;opacity:.85;margin-bottom:6px">📬 收件箱 <span style="opacity:.55;font-size:10px">共 '+list.length+' 封</span><button id="mbRe" style="float:right;font-size:10px">↻ 刷新</button></div>';
1093
+ for(var i=0;i<list.length;i++){var m=list[i]||{};var d=new Date(m.ts||0);
1094
+ var ts=('0'+(d.getMonth()+1)).slice(-2)+'-'+('0'+d.getDate()).slice(-2)+' '+('0'+d.getHours()).slice(-2)+':'+('0'+d.getMinutes()).slice(-2);
1095
+ h+='<div style="border:1px solid #1d3550;border-radius:3px;padding:6px 8px;margin-bottom:5px;background:#0a1220"><div style="font-size:10px;opacity:.75"><b style="color:'+(m.k==='战绩'?'#ffd66b':'#8ff0d0')+'">'+(m.k==='战绩'?'⚔ 战绩':'📮 意见')+'</b> · '+ts+'</div><div style="font-size:11px;color:#dff1ff;margin-top:3px;white-space:pre-wrap;word-break:break-word">'+mbEsc(m.t||'')+'</div></div>';
1096
+ }
1097
+ if(!list.length)h+='<div style="font-size:10px;opacity:.6">还没有来信~</div>';
1098
+ In2.innerHTML=h;bindRe();
1099
+ function bindRe(){var r3=document.getElementById('mbRe');if(r3)r3.onclick=function(){mbRender();};}
1100
+ });
1101
+ }
1102
+ function mbPost(kind,txt,cb){
1103
+ var u='https://textdb.dev/api/data/prts-myb-mailbox';
1104
+ try{
1105
+ var x=new XMLHttpRequest();x.open('GET',u+'?ts='+Date.now(),true);x.timeout=15000;
1106
+ x.onload=function(){
1107
+ var cur=[];try{cur=JSON.parse((''+x.responseText).replace(/=$/,''))||[]}catch(e){cur=[]}
1108
+ if(!(cur instanceof Array))cur=[];
1109
+ cur.unshift({k:kind,t:txt,ts:Date.now()});
1110
+ if(cur.length>200)cur.length=200;
1111
+ try{
1112
+ var y=new XMLHttpRequest();y.open('POST',u,true);y.timeout=20000;y.setRequestHeader('Content-Type','text/plain');
1113
+ y.onload=y.onerror=y.ontimeout=function(){if(cb&&!y._d){y._d=1;cb(true)}};
1114
+ y.send(JSON.stringify(cur));
1115
+ }catch(e){if(cb)cb(false)}
1116
+ };
1117
+ x.onerror=function(){if(cb)cb(false)};
1118
+ x.send();
1119
+ }catch(e){if(cb)cb(false)}
1120
+ }
1046
1121
  function wxhr(u,b,cb){try{var x=new XMLHttpRequest();x.open('POST',u,true);x.setRequestHeader('Content-Type','text/plain');x.onload=x.onerror=x.ontimeout=function(){if(cb)cb()};x.send(b);}catch(e){if(cb)cb();}}
1047
1122
  function canon(){var a=[];for(var i=0;i<TK.length;i++)a.push(TK[i]+'='+localStorage.getItem(TK[i]));return a.join('|');}
1048
1123
  function st(m){if(box&&box.st)box.st.textContent=m;}
@@ -1114,7 +1189,16 @@ window.LEDGER_BACKUP = [];
1114
1189
  '<button class="pri" id="acGo">登录 / 创建</button>';
1115
1190
  }
1116
1191
  h+='<button id="acExp">💾 本地存档码</button><button id="acImp">📥 存档码导入</button><button id="acWipe" style="border-color:#ff5f57;color:#ff8a84">🧹 清空本机存档</button>'+
1192
+ '<button id="acFb" style="border-color:#ffd23f;color:#ffd66b">📮 意见箱</button>'+
1193
+ '<button id="vbBtn" style="border-color:#7fe0c0;color:#8ff0d0">🔐 本地密码本</button>'+
1194
+ (window.__acct&&window.__acct.admin&&window.__acct.admin()?'<button id="mbBtn" style="border-color:#9fd0ff;color:#c8e4ff">📬 收件箱</button>':'')+
1117
1195
  '<div class="st" id="acSt"></div><textarea id="acTa" spellcheck="false"></textarea>'+
1196
+ '<div id="fbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px">'+
1197
+ '<div style="font-size:11px;opacity:.78;margin-bottom:5px">📮 意见箱 · 内容直达云端信箱(可匿名),鲸鲸会定期收信</div>'+
1198
+ '<textarea id="fbTxt" spellcheck="false" style="display:block;height:64px" placeholder="想说的话、想要的更新、发现的 bug……"></textarea>'+
1199
+ '<div style="margin-top:6px"><button id="fbSend" style="border-color:#ffd23f;color:#ffd66b">寄到云端</button><span id="fbSt" style="font-size:10px;opacity:.75;margin-left:8px"></span></div></div>'+
1200
+ '<div id="vbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px"><div id="vbIn"></div></div>'+
1201
+ '<div id="mbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px"><div id="mbIn"></div></div>'+
1118
1202
  '<div style="font-size:10px;opacity:.5;margin-top:9px;line-height:1.5">密钥由「用户名+密码」派生;同一账号在任意设备登录即接回数据。忘记密码=新仓库(旧数据找不回)</div>'+
1119
1203
  '<button id="acX" style="border-color:#2b4a66;color:#8ea6bf;margin-top:10px">关闭 / CLOSE</button></div>';
1120
1204
  wrap.innerHTML=h;
@@ -1169,10 +1253,20 @@ window.LEDGER_BACKUP = [];
1169
1253
  var wp=g('#acWipe');
1170
1254
  if(wp)wp.onclick=function(){
1171
1255
  if(wp.getAttribute('data-c')){
1172
- try{var ks=[],kk;for(var i=0;i<localStorage.length;i++){kk=localStorage.key(i);if(/^(whale\.|minis_ledger)/.test(kk))ks.push(kk);}
1256
+ try{var ks=[],kk;for(var i=0;i<localStorage.length;i++){kk=localStorage.key(i);if(/^(whale\.|minis_ledger)/.test(kk)&&kk.indexOf('whale.vault')!==0)ks.push(kk);}
1173
1257
  for(var j=0;j<ks.length;j++)localStorage.removeItem(ks[j]);}catch(e){}
1174
1258
  location.reload();return;}
1175
1259
  wp.setAttribute('data-c','1');wp.textContent='⚠ 再点一次=清空并刷新(云端不受影响)';};
1260
+ var fb=g('#acFb');if(fb)fb.onclick=function(){var b=g('#fbBox');if(b)b.style.display=(!b.style.display||b.style.display==='none')?'block':'none';};
1261
+ var fs=g('#fbSend');if(fs)fs.onclick=function(){
1262
+ var t=g('#fbTxt'),t2=t?String(t.value||'').trim():'',st2=g('#fbSt');
1263
+ if(!t2){if(st2)st2.textContent='先写点什么呀';return}
1264
+ if(t2.length>600)t2=t2.slice(0,600);
1265
+ if(st2)st2.textContent='寄送中…';
1266
+ mbPost('意见',t2,function(ok){if(st2)st2.textContent=ok?'✅ 已寄出,谢谢!鲸鲸会看到的':'⚠ 寄送失败,稍后再试';if(ok&&t)t.value='';});
1267
+ };
1268
+ var mbb=g('#mbBtn');if(mbb)mbb.onclick=function(){var b=g('#mbBox');if(!b)return;var op=(!b.style.display||b.style.display==='none');b.style.display=op?'block':'none';if(op)mbRender();};
1269
+ var vbb=g('#vbBtn');if(vbb)vbb.onclick=function(){var b=g('#vbBox');if(!b)return;var op=(!b.style.display||b.style.display==='none');b.style.display=op?'block':'none';if(op){vbLoad();vbRender();}};
1176
1270
  var x=g('#acX');if(x)x.onclick=function(){wrap.classList.remove('on')};
1177
1271
  wrap.onclick=function(e){if(e.target===wrap)wrap.classList.remove('on')};
1178
1272
  }
@@ -1195,7 +1289,136 @@ window.LEDGER_BACKUP = [];
1195
1289
  btn.addEventListener('click',open);
1196
1290
  setInterval(function(){if(acct&&acct.k){var c=canon();if(c!==lastSnap){lastSnap=c;clearTimeout(pq);pq=setTimeout(push,900);}}},2000);
1197
1291
  if(acct&&acct.k){setTimeout(function(){pull(false);},250);}
1198
- window.__shaT=sha;window.__pushT=push;window.__pullT=pull;window.__canonT=canon;window.__acct={admin:function(){return !!(acct&&acct.k===ADMIN_K)}};
1292
+ var APP_VER='1.2.2';
1293
+ function _verCmp(a,b){var x=String(a).split('.'),y=String(b).split('.');for(var i=0;i<3;i++){var p=parseInt(x[i]||'0',10),q=parseInt(y[i]||'0',10);if(p!==q)return p-q}return 0}
1294
+ function chkVer(){
1295
+ try{
1296
+ var x=new XMLHttpRequest();x.open('GET','https://unpkg.com/prts-moonlit/package.json?ts='+Date.now(),true);x.timeout=15000;
1297
+ x.onload=function(){
1298
+ try{
1299
+ var rv=(JSON.parse(x.responseText)||{}).version;if(!rv)return;
1300
+ if(_verCmp(rv,APP_VER)<=0)return;
1301
+ if(sessionStorage.getItem('verNag')===rv)return;sessionStorage.setItem('verNag',rv);
1302
+ var d=document.createElement('div');
1303
+ d.textContent='✨ 新版本 '+rv+' 已上线 —— 点此刷新更新';
1304
+ d.style.cssText='position:fixed;left:50%;top:max(10px,env(safe-area-inset-top));transform:translateX(-50%);z-index:9999;background:rgba(7,12,24,.96);border:2px solid #ffd23f;color:#ffe9a8;font:600 13px ui-monospace,Menlo,monospace;padding:10px 15px;box-shadow:0 8px 28px rgba(0,0,0,.55);cursor:pointer';
1305
+ d.onclick=function(){location.reload()};
1306
+ document.body.appendChild(d);
1307
+ }catch(e){}
1308
+ };
1309
+ x.send();
1310
+ }catch(e){}
1311
+ }
1312
+ setTimeout(chkVer,2600);
1313
+ setInterval(chkVer,600000);
1314
+ /* ===== 🔐 本地密码本(whale.vault.v1 · 只存本机 · 不入同步/存档码/清空存档)===== */
1315
+ var VBK='whale.vault.v1',vbMem=null,vbEdit=-1;
1316
+ function vbRead(){try{return JSON.parse(localStorage.getItem(VBK)||'null')}catch(e){return null}}
1317
+ function vbSave(o){try{localStorage.setItem(VBK,JSON.stringify(o));return 1}catch(e){return 0}}
1318
+ function vbCache(s){try{if(s===null)sessionStorage.removeItem('whale.vault.un');else sessionStorage.setItem('whale.vault.un',s)}catch(e){}}
1319
+ function vbCached(){try{return sessionStorage.getItem('whale.vault.un')||''}catch(e){return ''}}
1320
+ function vbSalt(){var s='',h='0123456789abcdef';for(var i=0;i<16;i++)s+=h.charAt(Math.floor(Math.random()*16));return s;}
1321
+ function vbKey(pass,salt,iter){var x=salt+'|'+pass;for(var i=0;i<iter;i++)x=sha(x);return x;}
1322
+ function vbXor(s,key){var o='';for(var b=0;b<s.length;b+=32){var blk=sha(key+'|'+(b/32|0));
1323
+ for(var j=0;j<32&&b+j<s.length;j++)o+=String.fromCharCode(s.charCodeAt(b+j)^parseInt(blk.substr(j*2,2),16));}return o;}
1324
+ function vbEnc(t,key){return btoa(vbXor(unescape(encodeURIComponent(t)),key));}
1325
+ function vbDec(c,key){return decodeURIComponent(escape(vbXor(atob(c),key)));}
1326
+ function vbEsc(s){return String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}
1327
+ function vbMsg(t){var m=document.getElementById('vbMsg');if(m)m.textContent=t;}
1328
+ function vbCopy(t){var ok=0;try{navigator.clipboard.writeText(t);ok=1}catch(e){}
1329
+ if(!ok){try{var ta=document.createElement('textarea');ta.value=t;ta.style.position='fixed';ta.style.opacity='0';document.body.appendChild(ta);ta.focus();ta.select();ok=document.execCommand('copy');document.body.removeChild(ta)}catch(e2){}}
1330
+ return ok;}
1331
+ function vbLoad(){
1332
+ var v=vbRead();if(!v){return}
1333
+ if(!v.lock){vbMem={list:(v.list||[]),key:''};return}
1334
+ var ck=vbCached();if(!ck){vbMem=null;return}
1335
+ try{vbMem={list:JSON.parse(vbDec(v.blob,ck)),key:ck}}catch(e){vbCache(null);vbMem=null}
1336
+ }
1337
+ function vbPersist(){
1338
+ var v=vbRead();if(!v||!vbMem)return;
1339
+ if(v.lock){v.blob=vbEnc(JSON.stringify(vbMem.list),vbMem.key)}
1340
+ else{v.list=vbMem.list}
1341
+ vbSave(v);
1342
+ }
1343
+ function vbRender(){
1344
+ var In=document.getElementById('vbIn');if(!In)return;
1345
+ var v=vbRead(),h='';
1346
+ if(!v){
1347
+ h='<div style="font-size:11px;opacity:.85;margin-bottom:5px">🔐 本地密码本 · <span style="opacity:.55;font-size:10px">只存本机 · 永不云同步 · 不随存档导出</span></div>'
1348
+ +'<div style="font-size:10px;opacity:.6;margin-bottom:6px">第一次使用:设一个主口令(留空 = 不加锁)</div>'
1349
+ +'<input id="vbPass" type="password" placeholder="主口令(可留空)" style="width:100%;box-sizing:border-box;background:#0b1424;border:1px solid #2b4a66;color:#dff1ff;border-radius:3px;padding:7px 9px;font:12px ui-monospace,Menlo,monospace">'
1350
+ +'<div style="margin-top:6px"><button id="vbGo" style="border-color:#7fe0c0;color:#8ff0d0">创建密码本</button><span id="vbMsg" style="font-size:10px;opacity:.75;margin-left:8px"></span></div>';
1351
+ In.innerHTML=h;
1352
+ document.getElementById('vbGo').onclick=function(){
1353
+ var p=(document.getElementById('vbPass')||{}).value||'';
1354
+ if(!p){vbSave({v:1,lock:0,list:[]});vbMem={list:[],key:''};vbRender();return}
1355
+ var salt=vbSalt(),iter=3000,key=vbKey(p,salt,iter);
1356
+ vbSave({v:1,lock:1,salt:salt,iter:iter,blob:vbEnc('[]',key)});
1357
+ vbCache(key);vbMem={list:[],key:key};vbRender();
1358
+ };
1359
+ return;
1360
+ }
1361
+ if(v.lock&&!vbMem){
1362
+ h='<div style="font-size:11px;opacity:.85;margin-bottom:5px">🔐 本地密码本 · <b style="color:#ffd23f">已锁定</b></div>'
1363
+ +'<input id="vbPass" type="password" placeholder="主口令" style="width:100%;box-sizing:border-box;background:#0b1424;border:1px solid #2b4a66;color:#dff1ff;border-radius:3px;padding:7px 9px;font:12px ui-monospace,Menlo,monospace">'
1364
+ +'<div style="margin-top:6px"><button id="vbGo" style="border-color:#7fe0c0;color:#8ff0d0">解锁</button><span id="vbMsg" style="font-size:10px;opacity:.75;margin-left:8px"></span></div>';
1365
+ In.innerHTML=h;
1366
+ document.getElementById('vbGo').onclick=function(){
1367
+ var p=(document.getElementById('vbPass')||{}).value||'';
1368
+ var key=vbKey(p,v.salt,v.iter||3000);
1369
+ try{vbMem={list:JSON.parse(vbDec(v.blob,key)),key:key};vbCache(key);vbRender();}
1370
+ catch(e){vbMsg('口令不对,再试试');}
1371
+ };
1372
+ return;
1373
+ }
1374
+ var L=(vbMem&&vbMem.list)||[],ed=vbEdit;
1375
+ h='<div style="font-size:11px;opacity:.85;margin-bottom:6px">🔐 本地密码本 <span style="opacity:.55;font-size:10px">只存本机 · '+L.length+' 条'+(v.lock?' · 已上锁':'')+'</span>';
1376
+ if(v.lock)h+='<button id="vbLock" style="float:right;font-size:10px;border-color:#ffd23f;color:#ffd66b">🔒 锁定</button>';
1377
+ h+='</div>';
1378
+ for(var i=0;i<L.length;i++){var e=L[i];
1379
+ h+='<div style="border:1px solid #1d3550;border-radius:3px;padding:6px 8px;margin-bottom:5px;background:#0a1220">'
1380
+ +'<div style="font-size:11px;color:#dff1ff"><b>'+vbEsc(e.n||'(未命名)')+'</b>'+(e.a?' · <span style="opacity:.55">'+vbEsc(e.a)+'</span>':'')+'</div>'
1381
+ +'<div style="font:11px ui-monospace,Menlo,monospace;color:#8ff0d0;margin-top:2px;word-break:break-all">'+vbEsc(e.p||'')+'</div>'
1382
+ +(e.r?'<div style="font-size:10px;opacity:.6;margin-top:2px">'+vbEsc(e.r)+'</div>':'')
1383
+ +'<div style="margin-top:4px"><button data-vbc="'+i+'" style="font-size:10px">复制</button> <button data-vbe="'+i+'" style="font-size:10px">编辑</button> <button data-vbr="'+i+'" style="font-size:10px;border-color:#ff5f57;color:#ff8a84">删除</button></div></div>';
1384
+ }
1385
+ if(!L.length)h+='<div style="font-size:10px;opacity:.6;margin-bottom:6px">还空着——从下面加第一条吧</div>';
1386
+ var E=(ed>=0&&L[ed])?L[ed]:{n:'',a:'',p:'',r:''};
1387
+ var IS='width:100%;box-sizing:border-box;background:#0b1424;border:1px solid #2b4a66;color:#dff1ff;border-radius:3px;padding:6px 8px;font:12px ui-monospace,Menlo,monospace;margin-top:4px';
1388
+ h+='<div style="border-top:1px dashed #2b4a66;padding-top:7px"><div style="font-size:10px;opacity:.65">'+(ed>=0?'编辑条目':'新增条目')+'</div>'
1389
+ +'<input id="vbN" value="'+vbEsc(E.n)+'" placeholder="名称(必填)· 例:DeepSeek API Key" style="'+IS+'">'
1390
+ +'<input id="vbA" value="'+vbEsc(E.a)+'" placeholder="账号(可空)" style="'+IS+'">'
1391
+ +'<input id="vbP" value="'+vbEsc(E.p)+'" placeholder="密码 / 密钥(必填)" style="'+IS+'">'
1392
+ +'<input id="vbR" value="'+vbEsc(E.r)+'" placeholder="备注(可空)" style="'+IS+'">'
1393
+ +'<div style="margin-top:6px"><button id="vbSv" style="border-color:#7fe0c0;color:#8ff0d0">'+(ed>=0?'保存修改':'+ 添加')+'</button>'+(ed>=0?' <button id="vbCn">取消编辑</button>':'')
1394
+ +'<span id="vbMsg" style="font-size:10px;opacity:.75;margin-left:8px"></span></div></div>';
1395
+ In.innerHTML=h;
1396
+ var lk=document.getElementById('vbLock');if(lk)lk.onclick=function(){vbCache(null);vbMem=null;vbRender();};
1397
+ for(var k2=0;k2<L.length;k2++){(function(idx){
1398
+ var c=In.querySelector('[data-vbc="'+idx+'"]');
1399
+ if(c)c.onclick=function(){vbMsg(vbCopy(L[idx].p||'')?'已复制 ✓':'复制失败,长按手动复制')};
1400
+ var eb2=In.querySelector('[data-vbe="'+idx+'"]');
1401
+ if(eb2)eb2.onclick=function(){vbEdit=idx;vbRender();};
1402
+ var rm=In.querySelector('[data-vbr="'+idx+'"]');
1403
+ if(rm)rm.onclick=function(){if(rm.getAttribute('data-arm')!=='1'){rm.setAttribute('data-arm','1');rm.textContent='再点=删除';return}L.splice(idx,1);vbPersist();vbEdit=-1;vbRender();};
1404
+ })(k2);}
1405
+ var sv=document.getElementById('vbSv');
1406
+ if(sv)sv.onclick=function(){
1407
+ var n=((document.getElementById('vbN')||{}).value||'').trim();
1408
+ var a=((document.getElementById('vbA')||{}).value||'').trim();
1409
+ var p=((document.getElementById('vbP')||{}).value||'').trim();
1410
+ var r=((document.getElementById('vbR')||{}).value||'').trim();
1411
+ if(!n){vbMsg('写个名称吧');return}
1412
+ if(!p){vbMsg('密码/密钥别忘了填');return}
1413
+ var it={n:n,a:a,p:p,r:r,ts:Date.now()};
1414
+ if(vbEdit>=0&&L[vbEdit])L[vbEdit]=it;else L.push(it);
1415
+ vbPersist();vbEdit=-1;vbMsg('已保存 ✓');
1416
+ setTimeout(function(){vbRender()},350);
1417
+ };
1418
+ var cn=document.getElementById('vbCn');
1419
+ if(cn)cn.onclick=function(){vbEdit=-1;vbRender();};
1420
+ }
1421
+ window.__shaT=sha;window.__pushT=push;window.__pullT=pull;window.__canonT=canon;window.__acct={admin:function(){return !!(acct&&acct.k===ADMIN_K)}};
1199
1422
  })();
1200
1423
  </script>
1201
1424
  </body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prts-moonlit",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "ARKNIGHTS x P3R Moonlit PRTS terminal (personal toy site)",
5
5
  "license": "MIT"
6
6
  }