prts-moonlit 1.2.1 → 1.2.3
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.
- package/gacha.html +149 -9
- package/index.html +175 -18
- package/ledger.html +179 -10
- package/package.json +1 -1
package/gacha.html
CHANGED
|
@@ -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,33 @@ 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,'&').replace(/</g,'<').replace(/>/g,'>')}
|
|
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
|
+
}
|
|
1086
1113
|
function mbPost(kind,txt,cb){
|
|
1087
1114
|
var u='https://textdb.dev/api/data/prts-myb-mailbox';
|
|
1088
1115
|
try{
|
|
@@ -1121,17 +1148,17 @@ document.addEventListener('pointerdown',function(){ac()},{once:true});
|
|
|
1121
1148
|
wxhr(url(),JSON.stringify({v:1,u:acct.u,c:acct.c||0,t:t,data:snap()}),function(){st('✔ 已上传 · 后台校验…');setTimeout(function(){verify(t)},600);});
|
|
1122
1149
|
}
|
|
1123
1150
|
function verify(t){
|
|
1124
|
-
|
|
1151
|
+
var x2=new XMLHttpRequest();x2.open('GET',url()+'?ts='+Date.now(),true);x2.timeout=20000;x2.onload=function(){try{var x=x2.responseText;
|
|
1125
1152
|
x=(x||'').replace(/=$/,'');
|
|
1126
1153
|
try{var rb=JSON.parse(x);if(rb.t===t){st('✔ 已同步 '+clock());return;}}catch(e){}
|
|
1127
1154
|
if(!window.__pv){window.__pv=1;setTimeout(function(){push(1);},800);}
|
|
1128
1155
|
else{window.__pv=0;st('⚠ 上传好像没送达(此浏览器限制跨域)——数据还在本机,可换 Safari 或用存档码');}
|
|
1129
|
-
}).
|
|
1156
|
+
}catch(e){st('⚠ 校验失败(可能网络受限)');}};x2.onerror=x2.ontimeout=function(){st('⚠ 校验失败(可能网络受限)');};x2.send();
|
|
1130
1157
|
}
|
|
1131
1158
|
function pull(manual){
|
|
1132
1159
|
if(!acct||!acct.k)return;
|
|
1133
1160
|
if(manual)st('↻ 同步中…');
|
|
1134
|
-
|
|
1161
|
+
var x2=new XMLHttpRequest();x2.open('GET',url()+'?ts='+Date.now(),true);x2.timeout=20000;x2.onload=function(){try{var x=x2.responseText;
|
|
1135
1162
|
x=(x||'').replace(/=$/,'');
|
|
1136
1163
|
if(!x){if(manual)st('☁ 云端还没有存档——点「云端上传」把本机数据传上去');return;}
|
|
1137
1164
|
var rb;try{rb=JSON.parse(x)}catch(e){st('⚠ 云端数据格式异常');return;}
|
|
@@ -1146,7 +1173,7 @@ document.addEventListener('pointerdown',function(){ac()},{once:true});
|
|
|
1146
1173
|
st(same2?'✔ 已是最新 '+clock():'☁ 云端比本机旧——点「云端上传」可覆盖云端');
|
|
1147
1174
|
}
|
|
1148
1175
|
else{st('✔ 已是云端最新');}
|
|
1149
|
-
}).
|
|
1176
|
+
}catch(e){st('⚠ 云端数据格式异常');}};x2.onerror=x2.ontimeout=function(){st('⚠ 云端连接失败');};x2.send();
|
|
1150
1177
|
}
|
|
1151
1178
|
function logout(){localStorage.removeItem(LS);acct=null;}
|
|
1152
1179
|
var CSS='.acWrap{position:fixed;inset:0;z-index:9998;background:rgba(3,6,14,.66);display:none;align-items:center;justify-content:center}.acWrap.on{display:flex}'+
|
|
@@ -1174,11 +1201,15 @@ document.addEventListener('pointerdown',function(){ac()},{once:true});
|
|
|
1174
1201
|
}
|
|
1175
1202
|
h+='<button id="acExp">💾 本地存档码</button><button id="acImp">📥 存档码导入</button><button id="acWipe" style="border-color:#ff5f57;color:#ff8a84">🧹 清空本机存档</button>'+
|
|
1176
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>':'')+
|
|
1177
1206
|
'<div class="st" id="acSt"></div><textarea id="acTa" spellcheck="false"></textarea>'+
|
|
1178
1207
|
'<div id="fbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px">'+
|
|
1179
1208
|
'<div style="font-size:11px;opacity:.78;margin-bottom:5px">📮 意见箱 · 内容直达云端信箱(可匿名),鲸鲸会定期收信</div>'+
|
|
1180
1209
|
'<textarea id="fbTxt" spellcheck="false" style="display:block;height:64px" placeholder="想说的话、想要的更新、发现的 bug……"></textarea>'+
|
|
1181
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>'+
|
|
1182
1213
|
'<div style="font-size:10px;opacity:.5;margin-top:9px;line-height:1.5">密钥由「用户名+密码」派生;同一账号在任意设备登录即接回数据。忘记密码=新仓库(旧数据找不回)</div>'+
|
|
1183
1214
|
'<button id="acX" style="border-color:#2b4a66;color:#8ea6bf;margin-top:10px">关闭 / CLOSE</button></div>';
|
|
1184
1215
|
wrap.innerHTML=h;
|
|
@@ -1197,7 +1228,7 @@ document.addEventListener('pointerdown',function(){ac()},{once:true});
|
|
|
1197
1228
|
st('✦ 仓库已创建,正在上传本机数据…');render();push();return;
|
|
1198
1229
|
}
|
|
1199
1230
|
st('↻ 查找云端仓库…');
|
|
1200
|
-
|
|
1231
|
+
var x3=new XMLHttpRequest();x3.open('GET',BASE+k+'?ts='+Date.now(),true);x3.timeout=20000;x3.onload=function(){try{var x=x3.responseText;
|
|
1201
1232
|
x=(x||'').replace(/=$/,'');
|
|
1202
1233
|
if(x&&x.length>2){
|
|
1203
1234
|
var rb=null;try{rb=JSON.parse(x)}catch(e){}if(!rb){acct=null;localStorage.removeItem(LS);st('⚠ 云端数据异常——没敢覆盖');return;}
|
|
@@ -1208,7 +1239,7 @@ document.addEventListener('pointerdown',function(){ac()},{once:true});
|
|
|
1208
1239
|
b.setAttribute('data-mode','create');b.textContent='✦ 没找到仓库——点此以本机数据创建';
|
|
1209
1240
|
st('(如果是新账号,创建后本机数据会上传成初始存档)');
|
|
1210
1241
|
}
|
|
1211
|
-
}).
|
|
1242
|
+
}catch(e){st('⚠ 云端连接失败,稍后再试');}};x3.onerror=x3.ontimeout=function(){st('⚠ 云端连接失败,稍后再试');};x3.send();
|
|
1212
1243
|
};
|
|
1213
1244
|
var pl=g('#acPull');if(pl)pl.onclick=function(){pull(1)};
|
|
1214
1245
|
var pu=g('#acPush');if(pu)pu.onclick=function(){if(acct&&acct.k)push();};
|
|
@@ -1233,7 +1264,7 @@ document.addEventListener('pointerdown',function(){ac()},{once:true});
|
|
|
1233
1264
|
var wp=g('#acWipe');
|
|
1234
1265
|
if(wp)wp.onclick=function(){
|
|
1235
1266
|
if(wp.getAttribute('data-c')){
|
|
1236
|
-
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);}
|
|
1237
1268
|
for(var j=0;j<ks.length;j++)localStorage.removeItem(ks[j]);}catch(e){}
|
|
1238
1269
|
location.reload();return;}
|
|
1239
1270
|
wp.setAttribute('data-c','1');wp.textContent='⚠ 再点一次=清空并刷新(云端不受影响)';};
|
|
@@ -1245,6 +1276,8 @@ document.addEventListener('pointerdown',function(){ac()},{once:true});
|
|
|
1245
1276
|
if(st2)st2.textContent='寄送中…';
|
|
1246
1277
|
mbPost('意见',t2,function(ok){if(st2)st2.textContent=ok?'✅ 已寄出,谢谢!鲸鲸会看到的':'⚠ 寄送失败,稍后再试';if(ok&&t)t.value='';});
|
|
1247
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();}};
|
|
1248
1281
|
var x=g('#acX');if(x)x.onclick=function(){wrap.classList.remove('on')};
|
|
1249
1282
|
wrap.onclick=function(e){if(e.target===wrap)wrap.classList.remove('on')};
|
|
1250
1283
|
}
|
|
@@ -1267,7 +1300,7 @@ document.addEventListener('pointerdown',function(){ac()},{once:true});
|
|
|
1267
1300
|
btn.addEventListener('click',open);
|
|
1268
1301
|
setInterval(function(){if(acct&&acct.k){var c=canon();if(c!==lastSnap){lastSnap=c;clearTimeout(pq);pq=setTimeout(push,900);}}},2000);
|
|
1269
1302
|
if(acct&&acct.k){setTimeout(function(){pull(false);},250);}
|
|
1270
|
-
var APP_VER='1.2.
|
|
1303
|
+
var APP_VER='1.2.3';
|
|
1271
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}
|
|
1272
1305
|
function chkVer(){
|
|
1273
1306
|
try{
|
|
@@ -1289,6 +1322,113 @@ function chkVer(){
|
|
|
1289
1322
|
}
|
|
1290
1323
|
setTimeout(chkVer,2600);
|
|
1291
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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
|
|
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
|
+
}
|
|
1292
1432
|
window.__shaT=sha;window.__pushT=push;window.__pullT=pull;window.__canonT=canon;window.__acct={admin:function(){return !!(acct&&acct.k===ADMIN_K)}};
|
|
1293
1433
|
})();
|
|
1294
1434
|
</script>
|
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.
|
|
545
|
+
<div style="font-size:.82em;line-height:1.4;font-weight:800;color:#ff5f57">版本 v1.2.3 正式上线!<br><span style="opacity:.75;font-size:.82em;font-weight:400;color:#ffd23f">点此查看使用教程与更新内容 ↗</span></div>
|
|
546
546
|
</div>
|
|
547
547
|
</div>
|
|
548
548
|
|
|
@@ -1599,12 +1599,14 @@ function updTopStats(){
|
|
|
1599
1599
|
var sn=document.getElementById('san-num');if(sn)sn.textContent=f;
|
|
1600
1600
|
var ro=document.getElementById('res-or');
|
|
1601
1601
|
if(ro){
|
|
1602
|
-
var
|
|
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;
|
|
1603
1605
|
try{
|
|
1604
1606
|
var recs=JSON.parse(localStorage.getItem('minis_ledger_v1'))||[];
|
|
1605
1607
|
for(var i=0;i<recs.length;i++){
|
|
1606
1608
|
var r=recs[i];
|
|
1607
|
-
if(r&&r.date&&r.date>
|
|
1609
|
+
if(r&&r.date&&r.date>_bf){bal+=(r.type==='in'?1:-1)*(Number(r.amount)||0);}
|
|
1608
1610
|
}
|
|
1609
1611
|
}catch(e){}
|
|
1610
1612
|
ro.textContent='¥'+bal.toFixed(2);
|
|
@@ -1752,7 +1754,7 @@ function aiCall(cb){
|
|
|
1752
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+')')}}
|
|
1753
1755
|
x.onerror=function(){cb(false,'连接失败:请检查地址/Key,或该服务是否允许网页直连(CORS)')};
|
|
1754
1756
|
x.ontimeout=function(){cb(false,'请求超时(90 秒)')};
|
|
1755
|
-
|
|
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));
|
|
1756
1758
|
}catch(e){cb(false,'发送异常:'+e.message)}
|
|
1757
1759
|
}
|
|
1758
1760
|
function aiSend(text){
|
|
@@ -1800,6 +1802,12 @@ V.chat=function(){
|
|
|
1800
1802
|
'<input class="aiI" id="aiU" placeholder="API 地址,如 https://api.deepseek.com" value="'+(cfg?escH(cfg.u):'')+'">'+
|
|
1801
1803
|
'<input class="aiI" id="aiK" placeholder="API Key(sk-...)" value="'+(cfg?escH(aiDec(cfg.k)):'')+'">'+
|
|
1802
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>'+
|
|
1803
1811
|
'<div style="margin-top:9px"><button class="btn" data-act="aisave">保存配置</button>'+(cfg?'<button class="btn sm" data-act="aiconf">收起</button>':'')+'</div>'+
|
|
1804
1812
|
'<div style="font-size:.68em;opacity:.55;margin-top:7px">※ 该服务需允许网页直连(CORS)</div></div>';
|
|
1805
1813
|
}
|
|
@@ -2633,7 +2641,7 @@ function tshuffle(a){for(var i=a.length-1;i>0;i--){var j=Math.floor(Math.random(
|
|
|
2633
2641
|
var TARO_NOW=[];
|
|
2634
2642
|
function openNews(){
|
|
2635
2643
|
var h='<div class="box"><div class="tag rd">NOTICE</div>'+
|
|
2636
|
-
'<div style="margin-top:6px;font-weight:900;font-size:1.08em">版本 v1.2.
|
|
2644
|
+
'<div style="margin-top:6px;font-weight:900;font-size:1.08em">版本 v1.2.3 正式上线!</div>'+
|
|
2637
2645
|
'<div style="font-size:.78em;opacity:.7;margin-top:3px">鲸鲸的养成计划 · PRTS 终端 · 2026-09-27</div></div>'+
|
|
2638
2646
|
'<div class="box" style="margin-top:10px"><div class="tag">使用教程</div>'+
|
|
2639
2647
|
'<div style="font-size:.82em;line-height:1.8;margin-top:6px">'+
|
|
@@ -2643,7 +2651,11 @@ function openNews(){
|
|
|
2643
2651
|
'④ 云存档:点 @ 登录 →「云端导入」接回数据 /「云端上传」备份上云;也有「本地存档码」跨设备搬运<br>'+
|
|
2644
2652
|
'⑤ 恢复出厂:@ 面板内「清空本机存档」,一键清空本机数据(云端不受影响)<br>'+'⑥ AI 对话:01 里保存 API 配置(地址/Key/模型)→ 和「人格」开聊;采购中心的券可直接在对话里使用<br>'+'⑦ 意见箱:@ 面板里的 📮 直达云端,说什么都能被鲸鲸收到</div></div>'+
|
|
2645
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>'+
|
|
2646
|
-
'<div class="box" style="margin-top:10px"><div class="tag">更新内容 · v1.2.
|
|
2654
|
+
'<div class="box" style="margin-top:10px"><div class="tag">更新内容 · v1.2.3</div>'+
|
|
2655
|
+
'<div style="font-size:.82em;line-height:1.8;margin-top:6px">✦ 云端同步加固:开屏拉取 / 云端导入 / 登录校验 / 上传校验全部换用更稳的传输通道——弱网与受限环境也能把存档稳稳拉回来</div></div>'+
|
|
2656
|
+
'<div class="box" style="margin-top:10px"><div class="tag">里程碑 · v1.2.2</div>'+
|
|
2657
|
+
'<div style="font-size:.82em;line-height:1.8;margin-top:6px">✦ 本地密码本:@ 面板 🔐 · 只存本机、主口令加密、永不上云<br>✦ 收件箱:意见与战绩直达站内,主人专属(📬)<br>✦ AI 推理调节:对话页 ⚙ 新增 推理强度 / 温度 / 上限<br>✦ 账本余额校准:「💳 实际余额」自己填,主页余额同步跟动</div></div>'+
|
|
2658
|
+
'<div class="box" style="margin-top:10px"><div class="tag">里程碑 · v1.2.1</div>'+
|
|
2647
2659
|
'<div style="font-size:.82em;line-height:1.8;margin-top:6px">✦ 意见箱上岗:@ 面板 📮 写信直达云端信箱,鲸鲸定期收信<br>✦ 一键报备:寻访战绩直传云端;和 AI 聊一句自动打卡<br>✦ API Key 加密保存:密文落盘、聊天照常<br>✦ 版本哨兵:新版上线自动提醒「点此刷新更新」<br>✦ 公开招募下线(九宫格重排 01-09)· 免责声明上站</div></div>'+
|
|
2648
2660
|
'<div class="box" style="margin-top:10px"><div class="tag">里程碑 · v1.2.0</div>'+
|
|
2649
2661
|
'<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>'+
|
|
@@ -2791,7 +2803,12 @@ function handleAct(t){
|
|
|
2791
2803
|
else if(a==='autofill'){S.squads[S.curSquad]=['makoto','aigis','mitsuru','akihiko','yukari','fuuka'];
|
|
2792
2804
|
sfx.chime();toast('已按战力自动编成');V.squads()}
|
|
2793
2805
|
else if(a==='aiconf'){aiFormOpen=aiFormOpen?0:1;sfx.tap();V.chat()}
|
|
2794
|
-
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}
|
|
2806
|
+
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}
|
|
2807
|
+
var _r=$('#aiR')?$('#aiR').value:'',_tp=$('#aiTp')?$('#aiTp').value.trim():'',_mt=$('#aiMt')?$('#aiMt').value.trim():'';
|
|
2808
|
+
if(_tp!==''&&(isNaN(_tp)||Number(_tp)<0||Number(_tp)>2)){sfx.deny();toast('温度要在 0~2 之间');return}
|
|
2809
|
+
if(_mt!==''&&!/^\d+$/.test(_mt)){sfx.deny();toast('tokens 要填正整数');return}
|
|
2810
|
+
var _nc={u:_u,k:aiEnc(_k),m:_m};if(_r)_nc.r=_r;if(_tp!=='')_nc.tp=_tp;if(_mt!=='')_nc.mt=_mt;
|
|
2811
|
+
aiSave(AICFG_K,_nc);aiFormOpen=0;sfx.ok();toast('API 配置已保存');V.chat()}
|
|
2795
2812
|
else if(a==='aisend'){var _v=($('#aiInp').value||'').trim();if(!_v&&!aiDraftImg){sfx.deny();return}$('#aiInp').value='';aiSend(_v)}
|
|
2796
2813
|
else if(a==='aiclear'){var _p=aiActive(),_c=aiChats();_c[_p.id]=[];aiSave(AICHAT_K,_c);sfx.tap();toast('对话已清空');V.chat()}
|
|
2797
2814
|
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+')'))}
|
|
@@ -2976,7 +2993,7 @@ boot();
|
|
|
2976
2993
|
(function(){
|
|
2977
2994
|
var B=document.getElementById('petB'),bub=document.getElementById('petBub'),txt=document.getElementById('petTxt');
|
|
2978
2995
|
if(!B)return;
|
|
2979
|
-
var L=['鲸鲸~','主人~','捏到了!再捏一下嘛','今日宜抽卡','小鱼干 +1!','BUILD v1.2.
|
|
2996
|
+
var L=['鲸鲸~','主人~','捏到了!再捏一下嘛','今日宜抽卡','小鱼干 +1!','BUILD v1.2.3 已就位','账单同步了吗?','过场动画,好看吧?','晚安前记得说晚安哦','鲸鲸在看家…','要摸摸头吗?','别熬夜啦,主人'];
|
|
2980
2997
|
var last=-1,ht=0;
|
|
2981
2998
|
function say(){
|
|
2982
2999
|
var i;do{i=Math.floor(Math.random()*L.length)}while(i===last);
|
|
@@ -3011,13 +3028,13 @@ boot();
|
|
|
3011
3028
|
setInterval(function(){if(document.visibilityState==='visible')say()},36000);
|
|
3012
3029
|
})();
|
|
3013
3030
|
</script>
|
|
3014
|
-
<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.
|
|
3031
|
+
<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.3</div>
|
|
3015
3032
|
|
|
3016
3033
|
<!-- ===== SYSTEM LOGIN 云存档 ===== -->
|
|
3017
3034
|
<script>
|
|
3018
3035
|
/* ===== SYSTEM LOGIN · 云存档同步引擎 v1 ===== */
|
|
3019
3036
|
(function(){
|
|
3020
|
-
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'];
|
|
3037
|
+
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'];
|
|
3021
3038
|
var BASE='https://textdb.dev/api/data/prts-acct-';var ADMIN_K='e74aeb1a34c44f2fcefd1f19bb34c3891a4963f36242e5a5fd3869085892a711';
|
|
3022
3039
|
var LS='whale.acct',LT='whale.sync.t';
|
|
3023
3040
|
var acct=null;try{acct=JSON.parse(localStorage.getItem(LS)||'null')}catch(e){}
|
|
@@ -3051,6 +3068,33 @@ boot();
|
|
|
3051
3068
|
}
|
|
3052
3069
|
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;}
|
|
3053
3070
|
function url(){return BASE+acct.k;}
|
|
3071
|
+
function mbFetch(cb){
|
|
3072
|
+
try{
|
|
3073
|
+
var x=new XMLHttpRequest();x.open('GET','https://textdb.dev/api/data/prts-myb-mailbox?ts='+Date.now(),true);x.timeout=20000;
|
|
3074
|
+
x.onload=function(){try{var cur=JSON.parse((''+x.responseText).replace(/=$/,''))||[];if(!(cur instanceof Array))cur=[];cb(cur)}catch(e){cb(null)}};
|
|
3075
|
+
x.onerror=function(){cb(null)};x.send();
|
|
3076
|
+
}catch(e){cb(null)}
|
|
3077
|
+
}
|
|
3078
|
+
function mbEsc(s){return String(s==null?'':s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')}
|
|
3079
|
+
function mbRender(){
|
|
3080
|
+
var In=document.getElementById('mbIn');if(!In)return;
|
|
3081
|
+
if(!(window.__acct&&window.__acct.admin&&window.__acct.admin())){
|
|
3082
|
+
In.innerHTML='<div style="font-size:10px;opacity:.65">📬 收件箱 · 仅主人可见(先用你的账号登录再来看)</div>';return;
|
|
3083
|
+
}
|
|
3084
|
+
In.innerHTML='<div style="font-size:10px;opacity:.7">⏳ 收信中…</div>';
|
|
3085
|
+
mbFetch(function(list){
|
|
3086
|
+
var In2=document.getElementById('mbIn');if(!In2)return;
|
|
3087
|
+
if(list===null){In2.innerHTML='<div style="font-size:10px;opacity:.7">⚠ 收信失败 <button id="mbRe" style="font-size:10px">重试</button></div>';bindRe();return;}
|
|
3088
|
+
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>';
|
|
3089
|
+
for(var i=0;i<list.length;i++){var m=list[i]||{};var d=new Date(m.ts||0);
|
|
3090
|
+
var ts=('0'+(d.getMonth()+1)).slice(-2)+'-'+('0'+d.getDate()).slice(-2)+' '+('0'+d.getHours()).slice(-2)+':'+('0'+d.getMinutes()).slice(-2);
|
|
3091
|
+
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>';
|
|
3092
|
+
}
|
|
3093
|
+
if(!list.length)h+='<div style="font-size:10px;opacity:.6">还没有来信~</div>';
|
|
3094
|
+
In2.innerHTML=h;bindRe();
|
|
3095
|
+
function bindRe(){var r3=document.getElementById('mbRe');if(r3)r3.onclick=function(){mbRender();};}
|
|
3096
|
+
});
|
|
3097
|
+
}
|
|
3054
3098
|
function mbPost(kind,txt,cb){
|
|
3055
3099
|
var u='https://textdb.dev/api/data/prts-myb-mailbox';
|
|
3056
3100
|
try{
|
|
@@ -3089,17 +3133,17 @@ boot();
|
|
|
3089
3133
|
wxhr(url(),JSON.stringify({v:1,u:acct.u,c:acct.c||0,t:t,data:snap()}),function(){st('✔ 已上传 · 后台校验…');setTimeout(function(){verify(t)},600);});
|
|
3090
3134
|
}
|
|
3091
3135
|
function verify(t){
|
|
3092
|
-
|
|
3136
|
+
var x2=new XMLHttpRequest();x2.open('GET',url()+'?ts='+Date.now(),true);x2.timeout=20000;x2.onload=function(){try{var x=x2.responseText;
|
|
3093
3137
|
x=(x||'').replace(/=$/,'');
|
|
3094
3138
|
try{var rb=JSON.parse(x);if(rb.t===t){st('✔ 已同步 '+clock());return;}}catch(e){}
|
|
3095
3139
|
if(!window.__pv){window.__pv=1;setTimeout(function(){push(1);},800);}
|
|
3096
3140
|
else{window.__pv=0;st('⚠ 上传好像没送达(此浏览器限制跨域)——数据还在本机,可换 Safari 或用存档码');}
|
|
3097
|
-
}).
|
|
3141
|
+
}catch(e){st('⚠ 校验失败(可能网络受限)');}};x2.onerror=x2.ontimeout=function(){st('⚠ 校验失败(可能网络受限)');};x2.send();
|
|
3098
3142
|
}
|
|
3099
3143
|
function pull(manual){
|
|
3100
3144
|
if(!acct||!acct.k)return;
|
|
3101
3145
|
if(manual)st('↻ 同步中…');
|
|
3102
|
-
|
|
3146
|
+
var x2=new XMLHttpRequest();x2.open('GET',url()+'?ts='+Date.now(),true);x2.timeout=20000;x2.onload=function(){try{var x=x2.responseText;
|
|
3103
3147
|
x=(x||'').replace(/=$/,'');
|
|
3104
3148
|
if(!x){if(manual)st('☁ 云端还没有存档——点「云端上传」把本机数据传上去');return;}
|
|
3105
3149
|
var rb;try{rb=JSON.parse(x)}catch(e){st('⚠ 云端数据格式异常');return;}
|
|
@@ -3114,7 +3158,7 @@ boot();
|
|
|
3114
3158
|
st(same2?'✔ 已是最新 '+clock():'☁ 云端比本机旧——点「云端上传」可覆盖云端');
|
|
3115
3159
|
}
|
|
3116
3160
|
else{st('✔ 已是云端最新');}
|
|
3117
|
-
}).
|
|
3161
|
+
}catch(e){st('⚠ 云端数据格式异常');}};x2.onerror=x2.ontimeout=function(){st('⚠ 云端连接失败');};x2.send();
|
|
3118
3162
|
}
|
|
3119
3163
|
function logout(){localStorage.removeItem(LS);acct=null;}
|
|
3120
3164
|
var CSS='.acWrap{position:fixed;inset:0;z-index:9998;background:rgba(3,6,14,.66);display:none;align-items:center;justify-content:center}.acWrap.on{display:flex}'+
|
|
@@ -3142,11 +3186,15 @@ boot();
|
|
|
3142
3186
|
}
|
|
3143
3187
|
h+='<button id="acExp">💾 本地存档码</button><button id="acImp">📥 存档码导入</button><button id="acWipe" style="border-color:#ff5f57;color:#ff8a84">🧹 清空本机存档</button>'+
|
|
3144
3188
|
'<button id="acFb" style="border-color:#ffd23f;color:#ffd66b">📮 意见箱</button>'+
|
|
3189
|
+
'<button id="vbBtn" style="border-color:#7fe0c0;color:#8ff0d0">🔐 本地密码本</button>'+
|
|
3190
|
+
(window.__acct&&window.__acct.admin&&window.__acct.admin()?'<button id="mbBtn" style="border-color:#9fd0ff;color:#c8e4ff">📬 收件箱</button>':'')+
|
|
3145
3191
|
'<div class="st" id="acSt"></div><textarea id="acTa" spellcheck="false"></textarea>'+
|
|
3146
3192
|
'<div id="fbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px">'+
|
|
3147
3193
|
'<div style="font-size:11px;opacity:.78;margin-bottom:5px">📮 意见箱 · 内容直达云端信箱(可匿名),鲸鲸会定期收信</div>'+
|
|
3148
3194
|
'<textarea id="fbTxt" spellcheck="false" style="display:block;height:64px" placeholder="想说的话、想要的更新、发现的 bug……"></textarea>'+
|
|
3149
3195
|
'<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>'+
|
|
3196
|
+
'<div id="vbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px"><div id="vbIn"></div></div>'+
|
|
3197
|
+
'<div id="mbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px"><div id="mbIn"></div></div>'+
|
|
3150
3198
|
'<div style="font-size:10px;opacity:.5;margin-top:9px;line-height:1.5">密钥由「用户名+密码」派生;同一账号在任意设备登录即接回数据。忘记密码=新仓库(旧数据找不回)</div>'+
|
|
3151
3199
|
'<button id="acX" style="border-color:#2b4a66;color:#8ea6bf;margin-top:10px">关闭 / CLOSE</button></div>';
|
|
3152
3200
|
wrap.innerHTML=h;
|
|
@@ -3165,7 +3213,7 @@ boot();
|
|
|
3165
3213
|
st('✦ 仓库已创建,正在上传本机数据…');render();push();return;
|
|
3166
3214
|
}
|
|
3167
3215
|
st('↻ 查找云端仓库…');
|
|
3168
|
-
|
|
3216
|
+
var x3=new XMLHttpRequest();x3.open('GET',BASE+k+'?ts='+Date.now(),true);x3.timeout=20000;x3.onload=function(){try{var x=x3.responseText;
|
|
3169
3217
|
x=(x||'').replace(/=$/,'');
|
|
3170
3218
|
if(x&&x.length>2){
|
|
3171
3219
|
var rb=null;try{rb=JSON.parse(x)}catch(e){}if(!rb){acct=null;localStorage.removeItem(LS);st('⚠ 云端数据异常——没敢覆盖');return;}
|
|
@@ -3176,7 +3224,7 @@ boot();
|
|
|
3176
3224
|
b.setAttribute('data-mode','create');b.textContent='✦ 没找到仓库——点此以本机数据创建';
|
|
3177
3225
|
st('(如果是新账号,创建后本机数据会上传成初始存档)');
|
|
3178
3226
|
}
|
|
3179
|
-
}).
|
|
3227
|
+
}catch(e){st('⚠ 云端连接失败,稍后再试');}};x3.onerror=x3.ontimeout=function(){st('⚠ 云端连接失败,稍后再试');};x3.send();
|
|
3180
3228
|
};
|
|
3181
3229
|
var pl=g('#acPull');if(pl)pl.onclick=function(){pull(1)};
|
|
3182
3230
|
var pu=g('#acPush');if(pu)pu.onclick=function(){if(acct&&acct.k)push();};
|
|
@@ -3201,7 +3249,7 @@ boot();
|
|
|
3201
3249
|
var wp=g('#acWipe');
|
|
3202
3250
|
if(wp)wp.onclick=function(){
|
|
3203
3251
|
if(wp.getAttribute('data-c')){
|
|
3204
|
-
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);}
|
|
3252
|
+
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);}
|
|
3205
3253
|
for(var j=0;j<ks.length;j++)localStorage.removeItem(ks[j]);}catch(e){}
|
|
3206
3254
|
location.reload();return;}
|
|
3207
3255
|
wp.setAttribute('data-c','1');wp.textContent='⚠ 再点一次=清空并刷新(云端不受影响)';};
|
|
@@ -3213,6 +3261,8 @@ boot();
|
|
|
3213
3261
|
if(st2)st2.textContent='寄送中…';
|
|
3214
3262
|
mbPost('意见',t2,function(ok){if(st2)st2.textContent=ok?'✅ 已寄出,谢谢!鲸鲸会看到的':'⚠ 寄送失败,稍后再试';if(ok&&t)t.value='';});
|
|
3215
3263
|
};
|
|
3264
|
+
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();};
|
|
3265
|
+
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();}};
|
|
3216
3266
|
var x=g('#acX');if(x)x.onclick=function(){wrap.classList.remove('on')};
|
|
3217
3267
|
wrap.onclick=function(e){if(e.target===wrap)wrap.classList.remove('on')};
|
|
3218
3268
|
}
|
|
@@ -3235,7 +3285,7 @@ boot();
|
|
|
3235
3285
|
btn.addEventListener('click',open);
|
|
3236
3286
|
setInterval(function(){if(acct&&acct.k){var c=canon();if(c!==lastSnap){lastSnap=c;clearTimeout(pq);pq=setTimeout(push,900);}}},2000);
|
|
3237
3287
|
if(acct&&acct.k){setTimeout(function(){pull(false);},250);}
|
|
3238
|
-
var APP_VER='1.2.
|
|
3288
|
+
var APP_VER='1.2.3';
|
|
3239
3289
|
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}
|
|
3240
3290
|
function chkVer(){
|
|
3241
3291
|
try{
|
|
@@ -3257,6 +3307,113 @@ function chkVer(){
|
|
|
3257
3307
|
}
|
|
3258
3308
|
setTimeout(chkVer,2600);
|
|
3259
3309
|
setInterval(chkVer,600000);
|
|
3310
|
+
/* ===== 🔐 本地密码本(whale.vault.v1 · 只存本机 · 不入同步/存档码/清空存档)===== */
|
|
3311
|
+
var VBK='whale.vault.v1',vbMem=null,vbEdit=-1;
|
|
3312
|
+
function vbRead(){try{return JSON.parse(localStorage.getItem(VBK)||'null')}catch(e){return null}}
|
|
3313
|
+
function vbSave(o){try{localStorage.setItem(VBK,JSON.stringify(o));return 1}catch(e){return 0}}
|
|
3314
|
+
function vbCache(s){try{if(s===null)sessionStorage.removeItem('whale.vault.un');else sessionStorage.setItem('whale.vault.un',s)}catch(e){}}
|
|
3315
|
+
function vbCached(){try{return sessionStorage.getItem('whale.vault.un')||''}catch(e){return ''}}
|
|
3316
|
+
function vbSalt(){var s='',h='0123456789abcdef';for(var i=0;i<16;i++)s+=h.charAt(Math.floor(Math.random()*16));return s;}
|
|
3317
|
+
function vbKey(pass,salt,iter){var x=salt+'|'+pass;for(var i=0;i<iter;i++)x=sha(x);return x;}
|
|
3318
|
+
function vbXor(s,key){var o='';for(var b=0;b<s.length;b+=32){var blk=sha(key+'|'+(b/32|0));
|
|
3319
|
+
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;}
|
|
3320
|
+
function vbEnc(t,key){return btoa(vbXor(unescape(encodeURIComponent(t)),key));}
|
|
3321
|
+
function vbDec(c,key){return decodeURIComponent(escape(vbXor(atob(c),key)));}
|
|
3322
|
+
function vbEsc(s){return String(s==null?'':s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
|
|
3323
|
+
function vbMsg(t){var m=document.getElementById('vbMsg');if(m)m.textContent=t;}
|
|
3324
|
+
function vbCopy(t){var ok=0;try{navigator.clipboard.writeText(t);ok=1}catch(e){}
|
|
3325
|
+
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){}}
|
|
3326
|
+
return ok;}
|
|
3327
|
+
function vbLoad(){
|
|
3328
|
+
var v=vbRead();if(!v){return}
|
|
3329
|
+
if(!v.lock){vbMem={list:(v.list||[]),key:''};return}
|
|
3330
|
+
var ck=vbCached();if(!ck){vbMem=null;return}
|
|
3331
|
+
try{vbMem={list:JSON.parse(vbDec(v.blob,ck)),key:ck}}catch(e){vbCache(null);vbMem=null}
|
|
3332
|
+
}
|
|
3333
|
+
function vbPersist(){
|
|
3334
|
+
var v=vbRead();if(!v||!vbMem)return;
|
|
3335
|
+
if(v.lock){v.blob=vbEnc(JSON.stringify(vbMem.list),vbMem.key)}
|
|
3336
|
+
else{v.list=vbMem.list}
|
|
3337
|
+
vbSave(v);
|
|
3338
|
+
}
|
|
3339
|
+
function vbRender(){
|
|
3340
|
+
var In=document.getElementById('vbIn');if(!In)return;
|
|
3341
|
+
var v=vbRead(),h='';
|
|
3342
|
+
if(!v){
|
|
3343
|
+
h='<div style="font-size:11px;opacity:.85;margin-bottom:5px">🔐 本地密码本 · <span style="opacity:.55;font-size:10px">只存本机 · 永不云同步 · 不随存档导出</span></div>'
|
|
3344
|
+
+'<div style="font-size:10px;opacity:.6;margin-bottom:6px">第一次使用:设一个主口令(留空 = 不加锁)</div>'
|
|
3345
|
+
+'<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">'
|
|
3346
|
+
+'<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>';
|
|
3347
|
+
In.innerHTML=h;
|
|
3348
|
+
document.getElementById('vbGo').onclick=function(){
|
|
3349
|
+
var p=(document.getElementById('vbPass')||{}).value||'';
|
|
3350
|
+
if(!p){vbSave({v:1,lock:0,list:[]});vbMem={list:[],key:''};vbRender();return}
|
|
3351
|
+
var salt=vbSalt(),iter=3000,key=vbKey(p,salt,iter);
|
|
3352
|
+
vbSave({v:1,lock:1,salt:salt,iter:iter,blob:vbEnc('[]',key)});
|
|
3353
|
+
vbCache(key);vbMem={list:[],key:key};vbRender();
|
|
3354
|
+
};
|
|
3355
|
+
return;
|
|
3356
|
+
}
|
|
3357
|
+
if(v.lock&&!vbMem){
|
|
3358
|
+
h='<div style="font-size:11px;opacity:.85;margin-bottom:5px">🔐 本地密码本 · <b style="color:#ffd23f">已锁定</b></div>'
|
|
3359
|
+
+'<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">'
|
|
3360
|
+
+'<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>';
|
|
3361
|
+
In.innerHTML=h;
|
|
3362
|
+
document.getElementById('vbGo').onclick=function(){
|
|
3363
|
+
var p=(document.getElementById('vbPass')||{}).value||'';
|
|
3364
|
+
var key=vbKey(p,v.salt,v.iter||3000);
|
|
3365
|
+
try{vbMem={list:JSON.parse(vbDec(v.blob,key)),key:key};vbCache(key);vbRender();}
|
|
3366
|
+
catch(e){vbMsg('口令不对,再试试');}
|
|
3367
|
+
};
|
|
3368
|
+
return;
|
|
3369
|
+
}
|
|
3370
|
+
var L=(vbMem&&vbMem.list)||[],ed=vbEdit;
|
|
3371
|
+
h='<div style="font-size:11px;opacity:.85;margin-bottom:6px">🔐 本地密码本 <span style="opacity:.55;font-size:10px">只存本机 · '+L.length+' 条'+(v.lock?' · 已上锁':'')+'</span>';
|
|
3372
|
+
if(v.lock)h+='<button id="vbLock" style="float:right;font-size:10px;border-color:#ffd23f;color:#ffd66b">🔒 锁定</button>';
|
|
3373
|
+
h+='</div>';
|
|
3374
|
+
for(var i=0;i<L.length;i++){var e=L[i];
|
|
3375
|
+
h+='<div style="border:1px solid #1d3550;border-radius:3px;padding:6px 8px;margin-bottom:5px;background:#0a1220">'
|
|
3376
|
+
+'<div style="font-size:11px;color:#dff1ff"><b>'+vbEsc(e.n||'(未命名)')+'</b>'+(e.a?' · <span style="opacity:.55">'+vbEsc(e.a)+'</span>':'')+'</div>'
|
|
3377
|
+
+'<div style="font:11px ui-monospace,Menlo,monospace;color:#8ff0d0;margin-top:2px;word-break:break-all">'+vbEsc(e.p||'')+'</div>'
|
|
3378
|
+
+(e.r?'<div style="font-size:10px;opacity:.6;margin-top:2px">'+vbEsc(e.r)+'</div>':'')
|
|
3379
|
+
+'<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>';
|
|
3380
|
+
}
|
|
3381
|
+
if(!L.length)h+='<div style="font-size:10px;opacity:.6;margin-bottom:6px">还空着——从下面加第一条吧</div>';
|
|
3382
|
+
var E=(ed>=0&&L[ed])?L[ed]:{n:'',a:'',p:'',r:''};
|
|
3383
|
+
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';
|
|
3384
|
+
h+='<div style="border-top:1px dashed #2b4a66;padding-top:7px"><div style="font-size:10px;opacity:.65">'+(ed>=0?'编辑条目':'新增条目')+'</div>'
|
|
3385
|
+
+'<input id="vbN" value="'+vbEsc(E.n)+'" placeholder="名称(必填)· 例:DeepSeek API Key" style="'+IS+'">'
|
|
3386
|
+
+'<input id="vbA" value="'+vbEsc(E.a)+'" placeholder="账号(可空)" style="'+IS+'">'
|
|
3387
|
+
+'<input id="vbP" value="'+vbEsc(E.p)+'" placeholder="密码 / 密钥(必填)" style="'+IS+'">'
|
|
3388
|
+
+'<input id="vbR" value="'+vbEsc(E.r)+'" placeholder="备注(可空)" style="'+IS+'">'
|
|
3389
|
+
+'<div style="margin-top:6px"><button id="vbSv" style="border-color:#7fe0c0;color:#8ff0d0">'+(ed>=0?'保存修改':'+ 添加')+'</button>'+(ed>=0?' <button id="vbCn">取消编辑</button>':'')
|
|
3390
|
+
+'<span id="vbMsg" style="font-size:10px;opacity:.75;margin-left:8px"></span></div></div>';
|
|
3391
|
+
In.innerHTML=h;
|
|
3392
|
+
var lk=document.getElementById('vbLock');if(lk)lk.onclick=function(){vbCache(null);vbMem=null;vbRender();};
|
|
3393
|
+
for(var k2=0;k2<L.length;k2++){(function(idx){
|
|
3394
|
+
var c=In.querySelector('[data-vbc="'+idx+'"]');
|
|
3395
|
+
if(c)c.onclick=function(){vbMsg(vbCopy(L[idx].p||'')?'已复制 ✓':'复制失败,长按手动复制')};
|
|
3396
|
+
var eb2=In.querySelector('[data-vbe="'+idx+'"]');
|
|
3397
|
+
if(eb2)eb2.onclick=function(){vbEdit=idx;vbRender();};
|
|
3398
|
+
var rm=In.querySelector('[data-vbr="'+idx+'"]');
|
|
3399
|
+
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();};
|
|
3400
|
+
})(k2);}
|
|
3401
|
+
var sv=document.getElementById('vbSv');
|
|
3402
|
+
if(sv)sv.onclick=function(){
|
|
3403
|
+
var n=((document.getElementById('vbN')||{}).value||'').trim();
|
|
3404
|
+
var a=((document.getElementById('vbA')||{}).value||'').trim();
|
|
3405
|
+
var p=((document.getElementById('vbP')||{}).value||'').trim();
|
|
3406
|
+
var r=((document.getElementById('vbR')||{}).value||'').trim();
|
|
3407
|
+
if(!n){vbMsg('写个名称吧');return}
|
|
3408
|
+
if(!p){vbMsg('密码/密钥别忘了填');return}
|
|
3409
|
+
var it={n:n,a:a,p:p,r:r,ts:Date.now()};
|
|
3410
|
+
if(vbEdit>=0&&L[vbEdit])L[vbEdit]=it;else L.push(it);
|
|
3411
|
+
vbPersist();vbEdit=-1;vbMsg('已保存 ✓');
|
|
3412
|
+
setTimeout(function(){vbRender()},350);
|
|
3413
|
+
};
|
|
3414
|
+
var cn=document.getElementById('vbCn');
|
|
3415
|
+
if(cn)cn.onclick=function(){vbEdit=-1;vbRender();};
|
|
3416
|
+
}
|
|
3260
3417
|
window.__shaT=sha;window.__pushT=push;window.__pullT=pull;window.__canonT=canon;window.__acct={admin:function(){return !!(acct&&acct.k===ADMIN_K)}};
|
|
3261
3418
|
})();
|
|
3262
3419
|
</script>
|
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
|
-
|
|
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,33 @@ 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,'&').replace(/</g,'<').replace(/>/g,'>')}
|
|
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
|
+
}
|
|
1046
1102
|
function mbPost(kind,txt,cb){
|
|
1047
1103
|
var u='https://textdb.dev/api/data/prts-myb-mailbox';
|
|
1048
1104
|
try{
|
|
@@ -1081,17 +1137,17 @@ window.LEDGER_BACKUP = [];
|
|
|
1081
1137
|
wxhr(url(),JSON.stringify({v:1,u:acct.u,c:acct.c||0,t:t,data:snap()}),function(){st('✔ 已上传 · 后台校验…');setTimeout(function(){verify(t)},600);});
|
|
1082
1138
|
}
|
|
1083
1139
|
function verify(t){
|
|
1084
|
-
|
|
1140
|
+
var x2=new XMLHttpRequest();x2.open('GET',url()+'?ts='+Date.now(),true);x2.timeout=20000;x2.onload=function(){try{var x=x2.responseText;
|
|
1085
1141
|
x=(x||'').replace(/=$/,'');
|
|
1086
1142
|
try{var rb=JSON.parse(x);if(rb.t===t){st('✔ 已同步 '+clock());return;}}catch(e){}
|
|
1087
1143
|
if(!window.__pv){window.__pv=1;setTimeout(function(){push(1);},800);}
|
|
1088
1144
|
else{window.__pv=0;st('⚠ 上传好像没送达(此浏览器限制跨域)——数据还在本机,可换 Safari 或用存档码');}
|
|
1089
|
-
}).
|
|
1145
|
+
}catch(e){st('⚠ 校验失败(可能网络受限)');}};x2.onerror=x2.ontimeout=function(){st('⚠ 校验失败(可能网络受限)');};x2.send();
|
|
1090
1146
|
}
|
|
1091
1147
|
function pull(manual){
|
|
1092
1148
|
if(!acct||!acct.k)return;
|
|
1093
1149
|
if(manual)st('↻ 同步中…');
|
|
1094
|
-
|
|
1150
|
+
var x2=new XMLHttpRequest();x2.open('GET',url()+'?ts='+Date.now(),true);x2.timeout=20000;x2.onload=function(){try{var x=x2.responseText;
|
|
1095
1151
|
x=(x||'').replace(/=$/,'');
|
|
1096
1152
|
if(!x){if(manual)st('☁ 云端还没有存档——点「云端上传」把本机数据传上去');return;}
|
|
1097
1153
|
var rb;try{rb=JSON.parse(x)}catch(e){st('⚠ 云端数据格式异常');return;}
|
|
@@ -1106,7 +1162,7 @@ window.LEDGER_BACKUP = [];
|
|
|
1106
1162
|
st(same2?'✔ 已是最新 '+clock():'☁ 云端比本机旧——点「云端上传」可覆盖云端');
|
|
1107
1163
|
}
|
|
1108
1164
|
else{st('✔ 已是云端最新');}
|
|
1109
|
-
}).
|
|
1165
|
+
}catch(e){st('⚠ 云端数据格式异常');}};x2.onerror=x2.ontimeout=function(){st('⚠ 云端连接失败');};x2.send();
|
|
1110
1166
|
}
|
|
1111
1167
|
function logout(){localStorage.removeItem(LS);acct=null;}
|
|
1112
1168
|
var CSS='.acWrap{position:fixed;inset:0;z-index:9998;background:rgba(3,6,14,.66);display:none;align-items:center;justify-content:center}.acWrap.on{display:flex}'+
|
|
@@ -1134,11 +1190,15 @@ window.LEDGER_BACKUP = [];
|
|
|
1134
1190
|
}
|
|
1135
1191
|
h+='<button id="acExp">💾 本地存档码</button><button id="acImp">📥 存档码导入</button><button id="acWipe" style="border-color:#ff5f57;color:#ff8a84">🧹 清空本机存档</button>'+
|
|
1136
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>':'')+
|
|
1137
1195
|
'<div class="st" id="acSt"></div><textarea id="acTa" spellcheck="false"></textarea>'+
|
|
1138
1196
|
'<div id="fbBox" style="display:none;margin-top:10px;border-top:1px dashed #2b4a66;padding-top:9px">'+
|
|
1139
1197
|
'<div style="font-size:11px;opacity:.78;margin-bottom:5px">📮 意见箱 · 内容直达云端信箱(可匿名),鲸鲸会定期收信</div>'+
|
|
1140
1198
|
'<textarea id="fbTxt" spellcheck="false" style="display:block;height:64px" placeholder="想说的话、想要的更新、发现的 bug……"></textarea>'+
|
|
1141
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>'+
|
|
1142
1202
|
'<div style="font-size:10px;opacity:.5;margin-top:9px;line-height:1.5">密钥由「用户名+密码」派生;同一账号在任意设备登录即接回数据。忘记密码=新仓库(旧数据找不回)</div>'+
|
|
1143
1203
|
'<button id="acX" style="border-color:#2b4a66;color:#8ea6bf;margin-top:10px">关闭 / CLOSE</button></div>';
|
|
1144
1204
|
wrap.innerHTML=h;
|
|
@@ -1157,7 +1217,7 @@ window.LEDGER_BACKUP = [];
|
|
|
1157
1217
|
st('✦ 仓库已创建,正在上传本机数据…');render();push();return;
|
|
1158
1218
|
}
|
|
1159
1219
|
st('↻ 查找云端仓库…');
|
|
1160
|
-
|
|
1220
|
+
var x3=new XMLHttpRequest();x3.open('GET',BASE+k+'?ts='+Date.now(),true);x3.timeout=20000;x3.onload=function(){try{var x=x3.responseText;
|
|
1161
1221
|
x=(x||'').replace(/=$/,'');
|
|
1162
1222
|
if(x&&x.length>2){
|
|
1163
1223
|
var rb=null;try{rb=JSON.parse(x)}catch(e){}if(!rb){acct=null;localStorage.removeItem(LS);st('⚠ 云端数据异常——没敢覆盖');return;}
|
|
@@ -1168,7 +1228,7 @@ window.LEDGER_BACKUP = [];
|
|
|
1168
1228
|
b.setAttribute('data-mode','create');b.textContent='✦ 没找到仓库——点此以本机数据创建';
|
|
1169
1229
|
st('(如果是新账号,创建后本机数据会上传成初始存档)');
|
|
1170
1230
|
}
|
|
1171
|
-
}).
|
|
1231
|
+
}catch(e){st('⚠ 云端连接失败,稍后再试');}};x3.onerror=x3.ontimeout=function(){st('⚠ 云端连接失败,稍后再试');};x3.send();
|
|
1172
1232
|
};
|
|
1173
1233
|
var pl=g('#acPull');if(pl)pl.onclick=function(){pull(1)};
|
|
1174
1234
|
var pu=g('#acPush');if(pu)pu.onclick=function(){if(acct&&acct.k)push();};
|
|
@@ -1193,7 +1253,7 @@ window.LEDGER_BACKUP = [];
|
|
|
1193
1253
|
var wp=g('#acWipe');
|
|
1194
1254
|
if(wp)wp.onclick=function(){
|
|
1195
1255
|
if(wp.getAttribute('data-c')){
|
|
1196
|
-
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);}
|
|
1197
1257
|
for(var j=0;j<ks.length;j++)localStorage.removeItem(ks[j]);}catch(e){}
|
|
1198
1258
|
location.reload();return;}
|
|
1199
1259
|
wp.setAttribute('data-c','1');wp.textContent='⚠ 再点一次=清空并刷新(云端不受影响)';};
|
|
@@ -1205,6 +1265,8 @@ window.LEDGER_BACKUP = [];
|
|
|
1205
1265
|
if(st2)st2.textContent='寄送中…';
|
|
1206
1266
|
mbPost('意见',t2,function(ok){if(st2)st2.textContent=ok?'✅ 已寄出,谢谢!鲸鲸会看到的':'⚠ 寄送失败,稍后再试';if(ok&&t)t.value='';});
|
|
1207
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();}};
|
|
1208
1270
|
var x=g('#acX');if(x)x.onclick=function(){wrap.classList.remove('on')};
|
|
1209
1271
|
wrap.onclick=function(e){if(e.target===wrap)wrap.classList.remove('on')};
|
|
1210
1272
|
}
|
|
@@ -1227,7 +1289,7 @@ window.LEDGER_BACKUP = [];
|
|
|
1227
1289
|
btn.addEventListener('click',open);
|
|
1228
1290
|
setInterval(function(){if(acct&&acct.k){var c=canon();if(c!==lastSnap){lastSnap=c;clearTimeout(pq);pq=setTimeout(push,900);}}},2000);
|
|
1229
1291
|
if(acct&&acct.k){setTimeout(function(){pull(false);},250);}
|
|
1230
|
-
var APP_VER='1.2.
|
|
1292
|
+
var APP_VER='1.2.3';
|
|
1231
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}
|
|
1232
1294
|
function chkVer(){
|
|
1233
1295
|
try{
|
|
@@ -1249,6 +1311,113 @@ function chkVer(){
|
|
|
1249
1311
|
}
|
|
1250
1312
|
setTimeout(chkVer,2600);
|
|
1251
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,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
|
|
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
|
+
}
|
|
1252
1421
|
window.__shaT=sha;window.__pushT=push;window.__pullT=pull;window.__canonT=canon;window.__acct={admin:function(){return !!(acct&&acct.k===ADMIN_K)}};
|
|
1253
1422
|
})();
|
|
1254
1423
|
</script>
|