This commit is contained in:
风之暇想
2025-08-15 19:25:35 +08:00
committed by GitHub
parent 06902725cd
commit 5e3de2f23c
10 changed files with 131 additions and 36 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
const CharSets1={"A":["甜","猛","浪","帮","拧","双","烟","高","立","百"],"B":["轻","真","看","按","夕","暑","涛","单","拍","助"],"C":["凸","引","号","咯","到","鸣","喊","是","疑","桂"],"D":["缓","页","离","灾","苦","疏","也","拒","触","灯"],"E":["泉","室","城","杏","尝","细","曲","靠","机","院"],"F":["峦","望","蹲","聚","旋","楼","应","晴","曾","爱"],"G":["茶","叹","梨","别","怯","于","圆","昼","缺","少"],"H":["梁","集","还","棋","熟","气","抓","摸","视","才"],"I":["棉","浓","着","湖","晃","但","团","甚","吓","涨"],"J":["猴","除","拉","意","成","抬","唱","春","宠","救"],"K":["为","卷","愁","功","晨","留","线","啦","忙","闪"],"L":["想","片","愿","准","兔","烦","勿","消","棕","吹"],"M":["棒","本","盯","却","喝","虹","桥","嘻","被","更"],"N":["拆","学","已","稻","使","摆","熊","秒","慢","越"],"O":["拿","拥","人","补","闹","了","没","续","火","停"],"P":["雀","羊","墙","并","冷","情","琴","散","江","小"],"Q":["响","太","虎","桌","压","我","今","影","拙","个"],"R":["跑","过","店","蜂","晓","变","条","雷","方","正"],"S":["凭","晚","得","耳","伴","星","贵","洁","蚊","叫"],"T":["定","多","堆","山","否","凉","柚","米","平","旗"],"U":["香","床","经","穿","迷","允","稍","老","观","瓣"],"V":["羞","搭","表","竖","陪","稠","柱","锅","窗","答"],"W":["分","送","包","重","只","滑","虚","柔","担","你"],"X":["聪","接","然","赶","兴","斜","医","敲","劝","虽"],"Y":["扳","难","非","温","增","顺","梅","水","躺","沉"],"Z":["生","块","地","易","直","黑","李","问","狼","往"],"a":["木","鱼","雨","慌","园","时","鼓","若","思","桃"],"b":["连","鹿","密","诉","厅","简","剩","年","错","现"],"c":["头","转","跟","用","扶","屋","满","墨","旧","虾"],"d":["薄","嗯","帅","知","志","捏","就","韧","柜","根"],"e":["起","岭","升","梦","钻","净","书","种","海","隐"],"f":["化","馆","啊","推","脆","灵","月","退","桑","吧"],"g":["败","爬","令","演","而","横","霞","光","张","疼"],"h":["心","急","夏","编","坐","扎","暖","加","挤","藤"],"i":["门","倦","盛","鲜","忧","矮","粗","感","假","听"],"j":["纸","必","鞋","蟹","厨","友","兆","电","勤","无"],"k":["因","凹","的","软","把","累","象","吉","池","带"],"l":["烫","眠","紫","降","扁","房","渐","呜","针","说"],"m":["读","静","白","枕","闭","窄","蛇","谈","红","休"],"n":["支","向","咕","握","冰","云","盆","寒","提","漂"],"o":["风","进","拖","闲","短","跳","托","夜","粒","显"],"p":["牛","金","很","呢","花","句","哪","以","清","洋"],"q":["嫩","减","酸","觉","嘘","弱","躲","秋","背","好"],"r":["谷","保","倚","遍","燕","咩","快","画","不","回"],"s":["盘","勺","亮","登","浮","皱","树","兰","列","节"],"t":["弟","行","扇","镜","阶","衣","篇","哦","富","奔"],"u":["叠","枝","挑","碗","零","大","品","冬","幸","呀"],"v":["惭","系","户","或","捧","钟","确","合","辣","兄"],"w":["袜","活","随","结","巧","乘","箱","次","眨","雪"],"x":["略","美","吵","落","厚","颗","料","一","呵","鹊"],"y":["繁","松","青","存","排","初","猜","纯","闻","千"],"z":["卧","堂","等","讲","困","热","灰","且","都","哎"],"0":["举","给","荔","忽","促","见","长","失","路","冻"],"1":["有","喵","撕","极","替","刚","首","睁","猫","从"],"2":["哇","趁","扛","峰","止","麦","叶","抖","柳","抱"],"3":["铺","剪","荷","新","拽","度","跨","晕","痛","段"],"4":["姐","哈","碰","飞","同","天","跃","倒","对","恋"],"5":["解","未","走","所","空","樱","音","断","蕊","醒"],"6":["哄","宽","蓝","游","勇","瀑","掐","惊","伞","蝶"],"7":["实","泊","狐","船","藏","淡","肯","又","妹","层"],"8":["朵","万","摇","豆","帽","呱","狮","粉","枣","叉"],"9":["斤","岂","豹","暗","梯","护","让","半","明","他"],"+":["绝","硬","莲","懂","枯","鸟","道","福","扭","乏"],"/":["扯","橙","尖","竟","古","稀","慕","街","站","全"],"=":["车","在","杯","念","竹","迎","亿","椅","河","优"]};const CN2B64=new Map();for(const[b64,arr]of Object.entries(CharSets1)){const code=b64.charCodeAt(0);for(const ch of arr){CN2B64.set(ch,code);}}
const mappingMode1={base64ToChinese(b64){const out=[];for(const ch of b64){const pool=CharSets1[ch];if(pool)out.push(pool[(Math.random()*10)|0]);}
return out.join('');},chineseToBase64(str){const out=[];for(const ch of str){const code=CN2B64.get(ch);if(code!==undefined)out.push(String.fromCharCode(code));}
const CharSets1={"A":["甜","猛","浪","帮","拧","双","烟","高","立","百"],"B":["轻","真","看","按","夕","暑","涛","单","拍","助"],"C":["凸","引","号","咯","到","鸣","喊","是","疑","桂"],"D":["缓","页","离","灾","苦","疏","也","拒","触","灯"],"E":["泉","室","城","杏","尝","细","曲","靠","机","院"],"F":["峦","望","蹲","聚","旋","楼","应","晴","曾","爱"],"G":["茶","叹","梨","别","怯","于","圆","昼","缺","少"],"H":["梁","集","还","棋","熟","气","抓","摸","视","才"],"I":["棉","浓","着","湖","晃","但","团","甚","吓","涨"],"J":["猴","除","拉","意","成","抬","唱","春","宠","救"],"K":["为","卷","愁","功","晨","留","线","啦","忙","闪"],"L":["想","片","愿","准","兔","烦","勿","消","棕","吹"],"M":["棒","本","盯","却","喝","虹","桥","嘻","被","更"],"N":["拆","学","已","稻","使","摆","熊","秒","慢","越"],"O":["拿","拥","人","补","闹","了","没","续","火","停"],"P":["雀","羊","墙","并","冷","情","琴","散","江","小"],"Q":["响","太","虎","桌","压","我","今","影","拙","个"],"R":["跑","过","店","蜂","晓","变","条","雷","方","正"],"S":["凭","晚","得","耳","伴","星","贵","洁","蚊","叫"],"T":["定","多","堆","山","否","凉","柚","米","平","旗"],"U":["香","床","经","穿","迷","允","稍","老","观","瓣"],"V":["羞","搭","表","竖","陪","稠","柱","锅","窗","答"],"W":["分","送","包","重","只","滑","虚","柔","担","你"],"X":["聪","接","然","赶","兴","斜","医","敲","劝","虽"],"Y":["扳","难","非","温","增","顺","梅","水","躺","沉"],"Z":["生","块","地","易","直","黑","李","问","狼","往"],"a":["木","鱼","雨","慌","园","时","鼓","若","思","桃"],"b":["连","鹿","密","诉","厅","简","剩","年","错","现"],"c":["头","转","跟","用","扶","屋","满","墨","旧","虾"],"d":["薄","嗯","帅","知","志","捏","就","韧","柜","根"],"e":["起","岭","升","梦","钻","净","书","种","海","隐"],"f":["化","馆","啊","推","脆","灵","月","退","桑","吧"],"g":["败","爬","令","演","而","横","霞","光","张","疼"],"h":["心","急","夏","编","坐","扎","暖","加","挤","藤"],"i":["门","倦","盛","鲜","忧","矮","粗","感","假","听"],"j":["纸","必","鞋","蟹","厨","友","兆","电","勤","无"],"k":["因","凹","的","软","把","累","象","吉","池","带"],"l":["烫","眠","紫","降","扁","房","渐","呜","针","说"],"m":["读","静","白","枕","闭","窄","蛇","谈","红","休"],"n":["支","向","咕","握","冰","云","盆","寒","提","漂"],"o":["风","进","拖","闲","短","跳","托","夜","粒","显"],"p":["牛","金","很","呢","花","句","哪","以","清","洋"],"q":["嫩","减","酸","觉","嘘","弱","躲","秋","背","好"],"r":["谷","保","倚","遍","燕","咩","快","画","不","回"],"s":["盘","勺","亮","登","浮","皱","树","兰","列","节"],"t":["弟","行","扇","镜","阶","衣","篇","哦","富","奔"],"u":["叠","枝","挑","碗","零","大","品","冬","幸","呀"],"v":["惭","系","户","或","捧","钟","确","合","辣","兄"],"w":["袜","活","随","结","巧","乘","箱","次","眨","雪"],"x":["略","美","吵","落","厚","颗","料","一","呵","鹊"],"y":["繁","松","青","存","排","初","猜","纯","闻","千"],"z":["卧","堂","等","讲","困","热","灰","且","都","哎"],"0":["举","给","荔","忽","促","见","长","失","路","冻"],"1":["有","喵","撕","极","替","刚","首","睁","猫","从"],"2":["哇","趁","扛","峰","止","麦","叶","抖","柳","抱"],"3":["铺","剪","荷","新","拽","度","跨","晕","痛","段"],"4":["姐","哈","碰","飞","同","天","跃","倒","对","恋"],"5":["解","未","走","所","空","樱","音","断","蕊","醒"],"6":["哄","宽","蓝","游","勇","瀑","掐","惊","伞","蝶"],"7":["实","泊","狐","船","藏","淡","肯","又","妹","层"],"8":["朵","万","摇","豆","帽","呱","狮","粉","枣","叉"],"9":["斤","岂","豹","暗","梯","护","让","半","明","他"],"+":["绝","硬","莲","懂","枯","鸟","道","福","扭","乏"],"/":["扯","橙","尖","竟","古","稀","慕","街","站","全"],"=":["车","在","杯","念","竹","迎","亿","椅","河","优"]};const CN2B64={};for(const[b64,arr]of Object.entries(CharSets1)){const code=b64.charCodeAt(0);for(const ch of arr){CN2B64[ch]=code;}}
const mappingMode1={Base64ToChinese(b64){const out=[];for(const ch of b64){const pool=CharSets1[ch];if(pool)out.push(pool[(Math.random()*10)|0]);}
return out.join('');},ChineseToBase64(str){const out=[];for(const ch of str){const code=CN2B64[ch];if(code!==undefined)out.push(String.fromCharCode(code));}
return out.join('');}};

File diff suppressed because one or more lines are too long

View File

@@ -1,43 +1,42 @@
const CharSets3={'0':'\u200C','1':'\u200D','2':'\u2060','3':'\u2061','4':'\u2062','5':'\u2063','6':'\u2064','7':'\u206A','8':'\u206B','9':'\u206C','a':'\u206D','b':'\u206E','c':'\u206F','d':'\u034F','e':'\uFEFF','f':'\u061C'};const ReverseCharSets3=Object.fromEntries(Object.entries(CharSets3).map(([k,v])=>[v,k]));const PAD_MARKER='\u200B';const BYTE2ZW=new Array(256);for(let i=0;i<256;i++){BYTE2ZW[i]=CharSets3[(i>>>4).toString(16)]+CharSets3[(i&0x0F).toString(16)];}
const C4=CharSets3;const BASE64_POOL='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';const randB64=n=>{if(n<=0)return'';const buf=new Uint32Array(n);crypto.getRandomValues(buf);let s='';for(let i=0;i<n;i++)s+=BASE64_POOL[buf[i]&63];return s;};const mappingMode3={base64ToZeroWidth(str,prefixLen=1,suffixLen=1){const bytes=new TextEncoder().encode(str);const zw=new Array(bytes.length);for(let i=0;i<bytes.length;i++)zw[i]=BYTE2ZW[bytes[i]];const padBits=(4-((bytes.length*8)&3))&3;return randB64(prefixLen)+zw.join('')+C4[padBits.toString(16)]+randB64(suffixLen);},zeroWidthToBase64(str){const nib=[];for(const ch of str){const n=parseInt(ReverseCharSets3[ch],16);if(!isNaN(n))nib.push(n);}
const C4=CharSets3;const BASE64_POOL='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';const randB64=n=>{if(n<=0)return'';const buf=new Uint32Array(n);crypto.getRandomValues(buf);let s='';for(let i=0;i<n;i++)s+=BASE64_POOL[buf[i]&63];return s;};const mappingMode3={Base64ToZeroWidth(str,prefixLen=1,suffixLen=1){const bytes=new TextEncoder().encode(str);const zw=new Array(bytes.length);for(let i=0;i<bytes.length;i++)zw[i]=BYTE2ZW[bytes[i]];const padBits=(4-((bytes.length*8)&3))&3;return randB64(prefixLen)+zw.join('')+C4[padBits.toString(16)]+randB64(suffixLen);},zeroWidthToBase64(str){const nib=[];for(const ch of str){const n=parseInt(ReverseCharSets3[ch],16);if(!isNaN(n))nib.push(n);}
if(nib.length<2)return'';const padBits=nib.pop()*4;const dataBits=nib.length*4-padBits;const byteLen=dataBits>>3;if(byteLen<=0)return'';const bytes=new Uint8Array(byteLen);let buf=0,bits=0,idx=0;for(let i=0;i<nib.length;i++){buf=(buf<<4)|nib[i];bits+=4;if(bits>=8){bytes[idx++]=buf>>>(bits-8);bits-=8;}}
return new TextDecoder().decode(bytes);},async encodeBlob(blob,prefixLen=4,suffixLen=4,chunkSize=512*1024){if(!(blob instanceof Blob))throw new TypeError('输入必须是Blob对象');const cores=navigator.hardwareConcurrency||4;const tasks=Math.ceil(blob.size/chunkSize);const results=new Array(tasks);let done=0;const workerSrc=`
const C4 = ${JSON.stringify(CharSets3)};
const C4 = ${JSON.stringify(CharSets3)};
const BYTE2ZW = ${JSON.stringify(BYTE2ZW)};
const BYTE2ZW = ${JSON.stringify(BYTE2ZW)};
const BASE64_POOL = '${BASE64_POOL}';
const BASE64_POOL = '${BASE64_POOL}';
const rand = n => {
const rand = n => {
const buf = new Uint32Array(n);
const buf = new Uint32Array(n);
crypto.getRandomValues(buf);
crypto.getRandomValues(buf);
let s = '';
let s = '';
for (let i = 0; i < n; ++i) s += BASE64_POOL[buf[i] & 63];
for (let i = 0; i < n; ++i) s += BASE64_POOL[buf[i] & 63];
return s;
return s;
};
};
onmessage = async ({data}) => {
onmessage = async ({data}) => {
const slice = data.blob.slice(data.offset, data.offset + data.chunkSize);
const slice = data.blob.slice(data.offset, data.offset + data.chunkSize);
const bytes = new Uint8Array(await slice.arrayBuffer());
const bytes = new Uint8Array(await slice.arrayBuffer());
let zw = '';
let zw = '';
for (let i = 0; i < bytes.length; i++) zw += BYTE2ZW[bytes[i]];
for (let i = 0; i < bytes.length; i++) zw += BYTE2ZW[bytes[i]];
const padBits = (4 - ((bytes.length * 8) & 3)) & 3;
const padBits = (4 - ((bytes.length * 8) & 3)) & 3;
postMessage({idx: data.idx, res: rand(data.prefixLen) + zw + C4[padBits.toString(16)] + rand(data.suffixLen)});
postMessage({idx: data.idx, res: rand(data.prefixLen) + zw + C4[padBits.toString(16)] + rand(data.suffixLen)});
};
};
`;const url=URL.createObjectURL(new Blob([workerSrc],{type:'text/javascript'}));return new Promise((resolve,reject)=>{const workers=Array.from({length:cores},()=>new Worker(url));workers.forEach(w=>{w.onmessage=({data})=>{results[data.idx]=data.res;if(++done===tasks){workers.forEach(x=>x.terminate());URL.revokeObjectURL(url);resolve(results.join(''));}};w.onerror=e=>{reject(e);workers.forEach(x=>x.terminate());URL.revokeObjectURL(url);};});for(let i=0;i<Math.min(cores,tasks);i++){workers[i].postMessage({blob,prefixLen,suffixLen,idx:i,offset:i*chunkSize,chunkSize});}});},decodeToBlob(str,mimeType='text/plain'){try{const b64=this.zeroWidthToBase64(str);const bin=atob(b64);const bytes=new Uint8Array(bin.length);for(let i=0;i<bin.length;i++)bytes[i]=bin.charCodeAt(i);return new Blob([bytes],{type:mimeType});}
catch{return new Blob(['decode error'],{type:'text/plain'});}},getEmojiLength:s=>[...s].length};
`;const url=URL.createObjectURL(new Blob([workerSrc],{type:'text/javascript'}));return new Promise((resolve,reject)=>{const workers=Array.from({length:cores},()=>new Worker(url));workers.forEach(w=>{w.onmessage=({data})=>{results[data.idx]=data.res;if(++done===tasks){workers.forEach(x=>x.terminate());URL.revokeObjectURL(url);resolve(results.join(''));}};w.onerror=e=>{reject(e);workers.forEach(x=>x.terminate());URL.revokeObjectURL(url);};});for(let i=0;i<Math.min(cores,tasks);i++){workers[i].postMessage({blob,prefixLen,suffixLen,idx:i,offset:i*chunkSize,chunkSize});}});},decodeToBlob(str,mimeType='text/plain'){try{const b64=this.zeroWidthToBase64(str);const bin=atob(b64);const bytes=new Uint8Array(bin.length);for(let i=0;i<bin.length;i++)bytes[i]=bin.charCodeAt(i);return new Blob([bytes],{type:mimeType});}catch{return new Blob(['decode error'],{type:'text/plain'});}},getEmojiLength:s=>[...s].length};

4
js/mapping-mode-4.js Normal file
View File

@@ -0,0 +1,4 @@
const CharSets4={"A":["ザ","ヒ","ケ"],"B":["ょ","ぴ","ド"],"C":["ヵ","ひ","ㇱ"],"D":["か","ウ","び"],"E":["ㇰ","ピ","ト"],"F":["モ","ン","レ"],"G":["マ","ぼ","ィ"],"H":["い","ゲ","ㇲ"],"I":["ゖ","ㇵ","る"],"J":["グ","ゟ","サ"],"K":["ォ","ュ","ㇿ"],"L":["ん","ゥ","ゔ"],"M":["セ","〲","゛"],"N":["れ","す","わ"],"O":["ダ","ゴ","そ"],"P":["ㇷ","ヴ","ぞ"],"Q":["ぽ","づ","じ"],"R":["つ","み","バ"],"S":["デ","ま","ㇼ"],"T":["な","ヹ","ぁ"],"U":["〻","お","ㇻ"],"V":["プ","ギ","ベ"],"W":["ゎ","ョ","に"],"X":["ヷ","こ","が"],"Y":["ざ","ヘ","ス"],"Z":["ゃ","ア","ミ"],"a":["ね","ふ","ゝ"],"b":["","ソ","ば"],"c":["ズ","ぺ","ペ"],"d":["ㇽ","ぐ","ゑ"],"e":["ゾ","ァ","ぅ"],"f":["ポ","も","ぷ"],"g":["ご","ㇸ","し"],"h":["ル","む","ら"],"i":["ヾ","ㇹ","ろ"],"j":["ジ","ャ","ボ"],"k":["ヲ","シ","ゼ"],"l":["ゐ","ッ","え"],"m":["ワ","テ","ヿ"],"n":["〴","う","き"],"o":["パ","ㇶ","ガ"],"p":["を","ラ","っ"],"q":["リ","ヂ","ゕ"],"r":["ぎ","メ","ヶ"],"s":["ク","〵","ビ"],"t":["ヺ","で","く"],"u":["よ","ゆ","ㇺ"],"v":["の","ぉ",""],"w":["フ","ム","せ"],"x":["ゞ","や","々"],"y":["め","タ","ぬ"],"z":["ㇳ","べ","チ"],"0":["ナ","だ","げ"],"1":["ぶ","け","ェ"],"2":["ヱ","ヨ","ぇ"],"3":["た","ゅ","ち"],"4":["ぃ","キ","ぢ"],"5":["ブ","ニ","ヸ"],"6":["ヮ","ほ","り"],"7":["ぱ","ぜ","ヌ"],"8":["ネ","ㇴ","ユ"],"9":["さ","ㇾ","ど"],"+":["て","イ","ヤ"],"/":["ツ","ず","ヅ"],"=":["は","と","あ"]};const JP2B64=new Map();for(const[b64,arr]of Object.entries(CharSets4)){const code=b64.charCodeAt(0);for(const ch of arr){JP2B64.set(ch,code);}}
const mappingMode4={Base64ToJapanese(b64){const out=[];for(const ch of b64){const pool=CharSets4[ch];if(pool)out.push(pool[Math.floor(Math.random()*pool.length)]);}
return out.join('');},JapaneseToBase64(str){const out=[];for(const ch of str){const code=JP2B64.get(ch);if(code!==undefined)out.push(String.fromCharCode(code));}
return out.join('');}};

View File

@@ -1,8 +1,7 @@
const DEFAULT_PASSWORD='a184f7b849ffffed24d266a30298c72ef2f5ad040db73bf37151fac767630728';const STORAGE_KEYS={THEME_MODE:'theme_mode',OUTPUT_MODE:'output_mode'};const ZERO_WIDTH_REGEX=/[\u200B-\u200F\uFEFF\u202A-\u202E\u2060-\u206F]/u;const BASE64_CHARS_REGEX=/[^A-Za-z0-9+/=]/g;const BASE64_FORMAT_REGEX=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u;const EMOJI_REGEX=/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u;const CHINESE_REGEX=/[\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF]/u;const PASSWORD_CHARSET="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+~`|}{[]:;?><,./-='\"\\";const textEncoder=new TextEncoder();(function applyThemeEarly(){const savedThemeMode=localStorage.getItem(STORAGE_KEYS.THEME_MODE);const isDarkMode=savedThemeMode?savedThemeMode==='dark':window.matchMedia('(prefers-color-scheme: dark)').matches;if(isDarkMode){document.documentElement.classList.add('dark');}})();async function handleStream(stream,writerData){const writer=stream.writable.getWriter();await writer.write(writerData);await writer.close();const reader=stream.readable.getReader();const chunks=[];while(true){const{done,value}=await reader.read();if(done)break;chunks.push(value);}
return new Blob(chunks).arrayBuffer();}
const toUint8Array=data=>data instanceof Uint8Array?data:new Uint8Array(typeof data==='string'?textEncoder.encode(data):data);const utils={arrayBufferToBase64:buffer=>sodium.to_base64(new Uint8Array(buffer),sodium.base64_variants.ORIGINAL),base64ToArrayBuffer:base64=>sodium.from_base64(base64,sodium.base64_variants.ORIGINAL).buffer,stringToArrayBuffer:str=>sodium.from_string(str).buffer,arrayBufferToString:buffer=>sodium.to_string(new Uint8Array(buffer)),generateRandomBytes:length=>sodium.randombytes_buf(length),compressData(data){if(typeof pako==='undefined'){throw new Error('压缩失败pako库未加载');}
const DEFAULT_PASSWORD='a184f7b849ffffed24d266a30298c72ef2f5ad040db73bf37151fac767630728';const STORAGE_KEYS={THEME_MODE:'theme_mode',OUTPUT_MODE:'output_mode',SYMMETRIC_PASSWORD:'symmetric_password'};const ZERO_WIDTH_REGEX=/[\u200B-\u200F\uFEFF\u202A-\u202E\u2060-\u206F]/u;const BASE64_CHARS_REGEX=/[^A-Za-z0-9+/=]/g;const BASE64_FORMAT_REGEX=/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/u;const EMOJI_REGEX=/[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u;const CHINESE_REGEX=/[\u4E00-\u9FFF\u3400-\u4DBF\uF900-\uFAFF]/u;const JAPANESE_REGEX=/[\u3040-\u309F\u30A0-\u30FF\uFF66-\uFF9F]/u;const PASSWORD_CHARSET="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+~`|}{[]:;?><,./-='\"\\";const textEncoder=new TextEncoder();(function applyThemeEarly(){const savedThemeMode=localStorage.getItem(STORAGE_KEYS.THEME_MODE);const isDarkMode=savedThemeMode?savedThemeMode==='dark':window.matchMedia('(prefers-color-scheme: dark)').matches;if(isDarkMode){document.documentElement.classList.add('dark');}})();const toUint8Array=data=>data instanceof Uint8Array?data:new Uint8Array(typeof data==='string'?textEncoder.encode(data):data);const utils={arrayBufferToBase64:buffer=>sodium.to_base64(new Uint8Array(buffer),sodium.base64_variants.ORIGINAL),Base64ToArrayBuffer:base64=>sodium.from_base64(base64,sodium.base64_variants.ORIGINAL).buffer,stringToArrayBuffer:str=>sodium.from_string(str).buffer,arrayBufferToString:buffer=>sodium.to_string(new Uint8Array(buffer)),generateRandomBytes:length=>sodium.randombytes_buf(length),compressData(data){if(typeof pako==='undefined'){throw new Error('压缩失败pako库未加载');}
return pako.deflateRaw(toUint8Array(data),{level:9});},decompressData(data){if(typeof pako==='undefined'){throw new Error('解压失败pako库未加载');}
const uint8Data=toUint8Array(data);try{return pako.inflateRaw(uint8Data);}catch{return pako.inflate(uint8Data);}},generatePassword(length){return Array.from({length},()=>PASSWORD_CHARSET[sodium.randombytes_uniform(PASSWORD_CHARSET.length)]).join('');},copyToClipboard(text){navigator.clipboard.writeText(text).catch(err=>{console.error('复制失败:',err);showNotification('复制到剪贴板失败,请手动复制。',false);});},detectCiphertextType(ciphertext){const limitedText=ciphertext.slice(0,1000);if(ZERO_WIDTH_REGEX.test(limitedText)){return'zero-width';}
const base64Chars=limitedText.replace(BASE64_CHARS_REGEX,'');const base64Ratio=base64Chars.length/limitedText.length;if(base64Ratio>0.8&&BASE64_FORMAT_REGEX.test(base64Chars)){return'base64';}
if(EMOJI_REGEX.test(limitedText)){return'emoji';}
if(CHINESE_REGEX.test(limitedText)){return'chinese';}},saveThemeMode(mode){if(mode==='light'||mode==='dark'){localStorage.setItem(STORAGE_KEYS.THEME_MODE,mode);}},getSavedThemeMode(){return localStorage.getItem(STORAGE_KEYS.THEME_MODE);},saveOutputMode(mode){const validModes=['chinese','base64','emoji','zero-width'];if(validModes.includes(mode)){localStorage.setItem(STORAGE_KEYS.OUTPUT_MODE,mode);}},getSavedOutputMode(){return localStorage.getItem(STORAGE_KEYS.OUTPUT_MODE)||'chinese';}};
if(CHINESE_REGEX.test(limitedText)){return'chinese';}
if(JAPANESE_REGEX.test(limitedText)){return'japanese';}},saveThemeMode(mode){if(mode==='light'||mode==='dark'){localStorage.setItem(STORAGE_KEYS.THEME_MODE,mode);}},getSavedThemeMode(){return localStorage.getItem(STORAGE_KEYS.THEME_MODE);},saveOutputMode(mode){const validModes=['chinese','base64','emoji','zero-width','japanese'];if(validModes.includes(mode)){localStorage.setItem(STORAGE_KEYS.OUTPUT_MODE,mode);}},getSavedOutputMode(){return localStorage.getItem(STORAGE_KEYS.OUTPUT_MODE)||'chinese';},savePassword(password){if(password){localStorage.setItem(STORAGE_KEYS.SYMMETRIC_PASSWORD,password);}else{localStorage.removeItem(STORAGE_KEYS.SYMMETRIC_PASSWORD);}},getSavedPassword(){return localStorage.getItem(STORAGE_KEYS.SYMMETRIC_PASSWORD);}};

View File

@@ -1,4 +1,4 @@
function showNotification(message,isSuccess=true){const container=document.createElement('div');container.className='fixed right-4 transition-all duration-300 ease-in-out translate-x-full z-50';container.style.top=`${(document.querySelector('nav')?.offsetHeight || 0) + 10}px`;const setWidth=()=>container.style.width=window.innerWidth<640?'90%':'280px';setWidth();window.addEventListener('resize',setWidth);const isDark=document.documentElement.classList.contains('dark');const[color,icon]=isSuccess?['green','check']:['red','exclamation'];container.innerHTML=`
function showNotification(message,isSuccess=true){const container=document.createElement('div');container.className='fixed right-4 transition-all duration-300 ease-in-out translate-x-full z-50';container.style.top=`${(document.querySelector('nav')?.offsetHeight || 0) + 10}px`;const setWidth=()=>container.style.width=window.innerWidth<640?'90%':'380px';setWidth();window.addEventListener('resize',setWidth);const isDark=document.documentElement.classList.contains('dark');const[color,icon]=isSuccess?['green','check']:['red','exclamation'];container.innerHTML=`
<div class="rounded-lg shadow-lg p-3 flex items-start border-l-4 border-${color}-500 ${isDark ? 'bg-gray-800 border-gray-700 text-white' : 'bg-white'}">
<div class="flex-shrink-0 mt-0.5 mr-2 text-${color}-500">
<i class="fa-solid fa-${icon}-circle"></i>
@@ -13,5 +13,5 @@ function showNotification(message,isSuccess=true){const container=document.creat
`;document.body.appendChild(container);const closeBtn=container.querySelector('button');setTimeout(()=>container.classList.remove('translate-x-full'),10);const remove=()=>{container.classList.add('translate-x-full');setTimeout(()=>container.remove(),300);};const timeoutId=setTimeout(remove,5000);closeBtn.addEventListener('click',()=>{clearTimeout(timeoutId);remove();});return container;}
function clearCryptoCache(){if(window.cryptoTempData){Object.values(window.cryptoTempData).forEach(data=>{if(data instanceof ArrayBuffer)new Uint8Array(data).fill(0);});window.cryptoTempData={};}
window.cryptoContext=null;typeof window.gc==='function'&&window.gc();}
document.addEventListener('DOMContentLoaded',()=>{const els=Object.fromEntries(['text-input','result-output','encryption-key','encrypt-btn','decrypt-btn','copy-btn','clear-btn','toggle-key-visibility','generate-password','result-status','theme-toggle','base64-toggle','paste-btn'].map(id=>[id.replace(/-./g,m=>m[1].toUpperCase()),document.getElementById(id)]));window.cryptoTempData={};window.cryptoContext=null;const savedTheme=utils.getSavedThemeMode();const isDark=(savedTheme||(window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'))==='dark';document.documentElement.classList.toggle('dark',isDark);els.themeToggle.innerHTML=`<i class="fa-solid fa-${isDark ? 'moon' : 'sun'} ${isDark ? 'text-white' : 'text-dark'}"></i>`;const modes=['chinese','base64','emoji','zero-width'];const modeNames={chinese:'中文',base64:'Base64',emoji:'Emoji','zero-width':'零宽'};const icons={chinese:'fa-solid fa-language',base64:'fa-solid fa-code',emoji:'fa-regular fa-face-smile','zero-width':'fa-brands fa-creative-commons-zero'};let outputMode=utils.getSavedOutputMode();els.base64Toggle.innerHTML=`<i class="${icons[outputMode]} theme-toggle-icon"></i>`;if(!window.crypto?.subtle){showNotification('不支持Web Crypto API无法使用加/解密功能',false);els.encryptBtn.disabled=els.decryptBtn.disabled=true;}
const baseStatusClass='absolute top-3 right-3 px-2 py-1 rounded-full text-xs font-medium';let isProcessing=false;const handleAction=async(action,inputGetter)=>{if(isProcessing)return showNotification('操作处理中,请稍候',false);const input=inputGetter();if(!input)return showNotification(`请输入要${action}的文本`,false);let password=els.encryptionKey.value.trim();const isDefault=!password;isDefault&&(password=DEFAULT_PASSWORD);try{isProcessing=true;els.encryptBtn.disabled=els.decryptBtn.disabled=true;els.resultStatus.textContent=`${action}中......`;els.resultStatus.className=`${baseStatusClass} bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200`;const result=await new Promise((resolve,reject)=>{setTimeout(async()=>{try{resolve(action==='加密'?await encryptionMethod1.encrypt(input,password,outputMode):await encryptionMethod1.decrypt(input,password));}catch(e){reject(e);}},0);});els.resultOutput.value=result;els.resultStatus.textContent=`${action}成功`;els.resultStatus.className=`${baseStatusClass} bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200`;showNotification(isDefault?`未检测到密码,现使用默认密码${action},建议更换为安全的密码。`:`文本已被${action}`,true);}catch(e){els.resultOutput.value='';els.resultStatus.textContent=`${action}失败`;els.resultStatus.className=`${baseStatusClass} bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200`;showNotification(e.message,false);}finally{clearCryptoCache();setTimeout(()=>els.resultStatus.classList.add('hidden'),1200);isProcessing=false;els.encryptBtn.disabled=els.decryptBtn.disabled=false;}};Object.entries({generatePassword:()=>{const pwd=utils.generatePassword(25);els.encryptionKey.value=pwd;utils.copyToClipboard(pwd);showNotification('已生成并复制25位随机密码');},toggleKeyVisibility:()=>{const isPassword=els.encryptionKey.type==='password';els.encryptionKey.type=isPassword?'text':'password';els.toggleKeyVisibility.innerHTML=`<i class="fa-regular fa-${isPassword ? 'eye' : 'eye-slash'}"></i>`;},base64Toggle:()=>{outputMode=modes[(modes.indexOf(outputMode)+1)%modes.length];utils.saveOutputMode(outputMode);els.base64Toggle.innerHTML=`<i class="${icons[outputMode]} theme-toggle-icon"></i>`;showNotification(`已切换到 ${modeNames[outputMode]} 密文`);},pasteBtn:()=>navigator.clipboard.readText().then(text=>{els.textInput.value=text;showNotification('已从剪贴板粘贴文本');}).catch(()=>els.textInput.select()),encryptBtn:()=>handleAction('加密',()=>els.textInput.value),decryptBtn:()=>handleAction('解密',()=>els.textInput.value.trim()),copyBtn:()=>{if(!els.resultOutput.value)return showNotification('没有可复制的结果',false);utils.copyToClipboard(els.resultOutput.value);showNotification('结果已复制到剪贴板');},clearBtn:()=>{els.textInput.value=els.resultOutput.value=els.encryptionKey.value='';els.resultStatus.classList.add('hidden');showNotification('已清空所有内容');},themeToggle:()=>{const isDark=document.documentElement.classList.toggle('dark');utils.saveThemeMode(isDark?'dark':'light');els.themeToggle.innerHTML=`<i class="fa-solid fa-${isDark ? 'moon' : 'sun'} ${isDark ? 'text-white' : 'text-dark'}"></i>`;document.querySelectorAll('.fixed.right-4.transition-all.duration-300.ease-in-out.z-50').forEach(el=>el.remove());}}).forEach(([key,handler])=>els[key]?.addEventListener('click',handler));});
document.addEventListener('DOMContentLoaded',()=>{const els=Object.fromEntries(['text-input','result-output','encryption-key','encrypt-btn','decrypt-btn','copy-btn','clear-btn','toggle-key-visibility','generate-password','save-password','result-status','theme-toggle','base64-toggle','paste-btn','encryption-mode-toggle','card-container'].map(id=>[id.replace(/-./g,m=>m[1].toUpperCase()),document.getElementById(id)]));window.cryptoTempData={};window.cryptoContext=null;const savedTheme=utils.getSavedThemeMode();const isDark=(savedTheme||(window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'))==='dark';document.documentElement.classList.toggle('dark',isDark);els.themeToggle.innerHTML=`<i class="fa-solid fa-${isDark ? 'moon' : 'sun'} ${isDark ? 'text-white' : 'text-dark'}"></i>`;els.encryptionKey.value=utils.getSavedPassword()||'';const modes=['chinese','base64','emoji','zero-width','japanese'];const modeNames={chinese:'中文',base64:'Base64',emoji:'Emoji','zero-width':'零宽',japanese:'日语'};const icons={chinese:'fa-solid fa-language',base64:'fa-solid fa-code',emoji:'fa-regular fa-face-smile','zero-width':'fa-brands fa-creative-commons-zero',japanese:'fa-regular fa-sun'};let outputMode=utils.getSavedOutputMode();els.base64Toggle.innerHTML=`<i class="${icons[outputMode]} theme-toggle-icon"></i>`;if(!window.crypto?.subtle){showNotification('不支持Web Crypto API无法使用加/解密功能',false);els.encryptBtn.disabled=els.decryptBtn.disabled=true;}
const baseStatusClass='absolute top-3 right-3 px-2 py-1 rounded-full text-xs font-medium';let isProcessing=false;const handleAction=async(action,inputGetter)=>{if(isProcessing)return showNotification('操作处理中,请稍候',false);const input=inputGetter();if(!input)return showNotification(`请输入要${action}的文本`,false);let password=els.encryptionKey.value.trim();const isDefault=!password;isDefault&&(password=DEFAULT_PASSWORD);try{isProcessing=true;els.encryptBtn.disabled=els.decryptBtn.disabled=true;els.resultStatus.textContent=`${action}中......`;els.resultStatus.className=`${baseStatusClass} bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200`;const result=await new Promise((resolve,reject)=>{setTimeout(async()=>{try{resolve(action==='加密'?await encryptionMethod1.encrypt(input,password,outputMode):await encryptionMethod1.decrypt(input,password));}catch(e){reject(e);}},0);});els.resultOutput.value=result;els.resultStatus.textContent=`${action}成功`;els.resultStatus.className=`${baseStatusClass} bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200`;showNotification(isDefault?`未检测到密码,现使用默认密码${action},建议更换为安全的密码。`:`文本已被${action}`,true);}catch(e){els.resultOutput.value='';els.resultStatus.textContent=`${action}失败`;els.resultStatus.className=`${baseStatusClass} bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200`;showNotification(e.message,false);}finally{clearCryptoCache();setTimeout(()=>els.resultStatus.classList.add('hidden'),1200);isProcessing=false;els.encryptBtn.disabled=els.decryptBtn.disabled=false;}};Object.entries({generatePassword:()=>{const pwd=utils.generatePassword(25);els.encryptionKey.value=pwd;utils.copyToClipboard(pwd);showNotification('已生成并复制25位随机密码');},toggleKeyVisibility:()=>{const isPassword=els.encryptionKey.type==='password';els.encryptionKey.type=isPassword?'text':'password';els.toggleKeyVisibility.innerHTML=`<i class="fa-regular fa-${isPassword ? 'eye' : 'eye-slash'}"></i>`;},savePassword:()=>{const password=els.encryptionKey.value.trim();utils.savePassword(password);showNotification(password?'密码已存本地(需要确保设备安全),清空密码再保存即删除。':'本地密码已删除');},base64Toggle:()=>{outputMode=modes[(modes.indexOf(outputMode)+1)%modes.length];utils.saveOutputMode(outputMode);els.base64Toggle.innerHTML=`<i class="${icons[outputMode]} theme-toggle-icon"></i>`;showNotification(`已切换到 ${modeNames[outputMode]} 密文`);},pasteBtn:()=>navigator.clipboard.readText().then(text=>{els.textInput.value=text;showNotification('已从剪贴板粘贴文本');}).catch(()=>els.textInput.select()),encryptBtn:()=>handleAction('加密',()=>els.textInput.value),decryptBtn:()=>handleAction('解密',()=>els.textInput.value.trim()),copyBtn:()=>{if(!els.resultOutput.value)return showNotification('没有可复制的结果',false);utils.copyToClipboard(els.resultOutput.value);showNotification('结果已复制到剪贴板');},clearBtn:()=>{els.textInput.value=els.resultOutput.value='';els.resultStatus.classList.add('hidden');showNotification('已清空内容');},themeToggle:()=>{const isDark=document.documentElement.classList.toggle('dark');utils.saveThemeMode(isDark?'dark':'light');els.themeToggle.innerHTML=`<i class="fa-solid fa-${isDark ? 'moon' : 'sun'} ${isDark ? 'text-white' : 'text-dark'}"></i>`;document.querySelectorAll('.fixed.right-4.transition-all.duration-300.ease-in-out.z-50').forEach(el=>el.remove());}}).forEach(([key,handler])=>els[key]?.addEventListener('click',handler));let isFirstClick=true;els.encryptionModeToggle?.addEventListener('click',function(){const cardContainer=els.cardContainer;if(!cardContainer)return;const icon=this.querySelector('i');if(isFirstClick){navigator.clipboard.writeText('https://github.com/fzxx/XiangYue').catch(()=>{});alert('非对称加密视 Github Stars关注量再考虑太少人用没有增加的必要\n\nGithub地址已复制 https://github.com/fzxx/XiangYue\n\n再次点按钮预可览非对称加密页面。');isFirstClick=false;}else{cardContainer.classList.toggle('flipped');if(cardContainer.classList.contains('flipped')){icon.classList.remove('fa-exchange');icon.classList.add('fa-sync-alt');}else{icon.classList.remove('fa-sync-alt');icon.classList.add('fa-exchange');}}});});