dsh-mobile-web 0.1.2 → 0.1.4

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/client.js CHANGED
@@ -14,111 +14,268 @@ window.__ModuleLoader__.load({
14
14
  const h = React.createElement
15
15
  const { useState, useEffect, useCallback, useRef } = React
16
16
 
17
+
18
+ // ── QR 生成(qrcode-generator 2.0.4,MIT,Copyright (c) 2009 Kazuhiko Arase)──
19
+ // 无构建步骤的手写 client.js 不能 require npm 包;这里内联一个 MIT 的极简
20
+ // byte-mode 编码器(约 21KB,esbuild 压缩),在客户端直接算矩阵并绘制 SVG。
21
+ var __qrGen=(()=>{var or=(L,T)=>()=>{try{return T||L((T={exports:{}}).exports,T),T.exports}catch(N){throw T=0,N}};var vr=or((W,V)=>{var $=(function(){var L=function(x,w){var g=236,l=17,n=x,s=N[w],t=null,r=0,h=null,i=[],v={},_=function(a,f){r=n*4+17,t=(function(e){for(var u=new Array(e),o=0;o<e;o+=1){u[o]=new Array(e);for(var d=0;d<e;d+=1)u[o][d]=null}return u})(r),B(0,0),B(r-7,0),B(0,r-7),E(),C(),m(a,f),n>=7&&O(a),h==null&&(h=ir(n,s,i)),U(h,f)},B=function(a,f){for(var e=-1;e<=7;e+=1)if(!(a+e<=-1||r<=a+e))for(var u=-1;u<=7;u+=1)f+u<=-1||r<=f+u||(0<=e&&e<=6&&(u==0||u==6)||0<=u&&u<=6&&(e==0||e==6)||2<=e&&e<=4&&2<=u&&u<=4?t[a+e][f+u]=!0:t[a+e][f+u]=!1)},y=function(){for(var a=0,f=0,e=0;e<8;e+=1){_(!0,e);var u=P.getLostPoint(v);(e==0||a>u)&&(a=u,f=e)}return f},C=function(){for(var a=8;a<r-8;a+=1)t[a][6]==null&&(t[a][6]=a%2==0);for(var f=8;f<r-8;f+=1)t[6][f]==null&&(t[6][f]=f%2==0)},E=function(){for(var a=P.getPatternPosition(n),f=0;f<a.length;f+=1)for(var e=0;e<a.length;e+=1){var u=a[f],o=a[e];if(t[u][o]==null)for(var d=-2;d<=2;d+=1)for(var c=-2;c<=2;c+=1)d==-2||d==2||c==-2||c==2||d==0&&c==0?t[u+d][o+c]=!0:t[u+d][o+c]=!1}},O=function(a){for(var f=P.getBCHTypeNumber(n),e=0;e<18;e+=1){var u=!a&&(f>>e&1)==1;t[Math.floor(e/3)][e%3+r-8-3]=u}for(var e=0;e<18;e+=1){var u=!a&&(f>>e&1)==1;t[e%3+r-8-3][Math.floor(e/3)]=u}},m=function(a,f){for(var e=s<<3|f,u=P.getBCHTypeInfo(e),o=0;o<15;o+=1){var d=!a&&(u>>o&1)==1;o<6?t[o][8]=d:o<8?t[o+1][8]=d:t[r-15+o][8]=d}for(var o=0;o<15;o+=1){var d=!a&&(u>>o&1)==1;o<8?t[8][r-o-1]=d:o<9?t[8][15-o-1+1]=d:t[8][15-o-1]=d}t[r-8][8]=!a},U=function(a,f){for(var e=-1,u=r-1,o=7,d=0,c=P.getMaskFunction(f),p=r-1;p>0;p-=2)for(p==6&&(p-=1);;){for(var b=0;b<2;b+=1)if(t[u][p-b]==null){var D=!1;d<a.length&&(D=(a[d]>>>o&1)==1);var A=c(u,p-b);A&&(D=!D),t[u][p-b]=D,o-=1,o==-1&&(d+=1,o=7)}if(u+=e,u<0||r<=u){u-=e,e=-e;break}}},H=function(a,f){for(var e=0,u=0,o=0,d=new Array(f.length),c=new Array(f.length),p=0;p<f.length;p+=1){var b=f[p].dataCount,D=f[p].totalCount-b;u=Math.max(u,b),o=Math.max(o,D),d[p]=new Array(b);for(var A=0;A<d[p].length;A+=1)d[p][A]=255&a.getBuffer()[A+e];e+=b;var R=P.getErrorCorrectPolynomial(D),I=K(d[p],R.getLength()-1),S=I.mod(R);c[p]=new Array(R.getLength()-1);for(var A=0;A<c[p].length;A+=1){var X=A+S.getLength()-c[p].length;c[p][A]=X>=0?S.getAt(X):0}}for(var Z=0,A=0;A<f.length;A+=1)Z+=f[A].totalCount;for(var J=new Array(Z),Q=0,A=0;A<u;A+=1)for(var p=0;p<f.length;p+=1)A<d[p].length&&(J[Q]=d[p][A],Q+=1);for(var A=0;A<o;A+=1)for(var p=0;p<f.length;p+=1)A<c[p].length&&(J[Q]=c[p][A],Q+=1);return J},ir=function(a,f,e){for(var u=Y.getRSBlocks(a,f),o=G(),d=0;d<e.length;d+=1){var c=e[d];o.put(c.getMode(),4),o.put(c.getLength(),P.getLengthInBits(c.getMode(),a)),c.write(o)}for(var p=0,d=0;d<u.length;d+=1)p+=u[d].dataCount;if(o.getLengthInBits()>p*8)throw"code length overflow. ("+o.getLengthInBits()+">"+p*8+")";for(o.getLengthInBits()+4<=p*8&&o.put(0,4);o.getLengthInBits()%8!=0;)o.putBit(!1);for(;!(o.getLengthInBits()>=p*8||(o.put(g,8),o.getLengthInBits()>=p*8));)o.put(l,8);return H(o,u)};v.addData=function(a,f){f=f||"Byte";var e=null;switch(f){case"Numeric":e=q(a);break;case"Alphanumeric":e=z(a);break;case"Byte":e=rr(a);break;case"Kanji":e=tr(a);break;default:throw"mode:"+f}i.push(e),h=null},v.isDark=function(a,f){if(a<0||r<=a||f<0||r<=f)throw a+","+f;return t[a][f]},v.getModuleCount=function(){return r},v.make=function(){if(n<1){for(var a=1;a<40;a++){for(var f=Y.getRSBlocks(a,s),e=G(),u=0;u<i.length;u++){var o=i[u];e.put(o.getMode(),4),e.put(o.getLength(),P.getLengthInBits(o.getMode(),a)),o.write(e)}for(var d=0,u=0;u<f.length;u++)d+=f[u].dataCount;if(e.getLengthInBits()<=d*8)break}n=a}_(!1,y())},v.createTableTag=function(a,f){a=a||2,f=typeof f=="undefined"?a*4:f;var e="";e+='<table style="',e+=" border-width: 0px; border-style: none;",e+=" border-collapse: collapse;",e+=" padding: 0px; margin: "+f+"px;",e+='">',e+="<tbody>";for(var u=0;u<v.getModuleCount();u+=1){e+="<tr>";for(var o=0;o<v.getModuleCount();o+=1)e+='<td style="',e+=" border-width: 0px; border-style: none;",e+=" border-collapse: collapse;",e+=" padding: 0px; margin: 0px;",e+=" width: "+a+"px;",e+=" height: "+a+"px;",e+=" background-color: ",e+=v.isDark(u,o)?"#000000":"#ffffff",e+=";",e+='"/>';e+="</tr>"}return e+="</tbody>",e+="</table>",e},v.createSvgTag=function(a,f,e,u){var o={};typeof arguments[0]=="object"&&(o=arguments[0],a=o.cellSize,f=o.margin,e=o.alt,u=o.title),a=a||2,f=typeof f=="undefined"?a*4:f,e=typeof e=="string"?{text:e}:e||{},e.text=e.text||null,e.id=e.text?e.id||"qrcode-description":null,u=typeof u=="string"?{text:u}:u||{},u.text=u.text||null,u.id=u.text?u.id||"qrcode-title":null;var d=v.getModuleCount()*a+f*2,c,p,b,D,A="",R;for(R="l"+a+",0 0,"+a+" -"+a+",0 0,-"+a+"z ",A+='<svg version="1.1" xmlns="http://www.w3.org/2000/svg"',A+=o.scalable?"":' width="'+d+'px" height="'+d+'px"',A+=' viewBox="0 0 '+d+" "+d+'" ',A+=' preserveAspectRatio="xMinYMin meet"',A+=u.text||e.text?' role="img" aria-labelledby="'+F([u.id,e.id].join(" ").trim())+'"':"",A+=">",A+=u.text?'<title id="'+F(u.id)+'">'+F(u.text)+"</title>":"",A+=e.text?'<description id="'+F(e.id)+'">'+F(e.text)+"</description>":"",A+='<rect width="100%" height="100%" fill="white" cx="0" cy="0"/>',A+='<path d="',b=0;b<v.getModuleCount();b+=1)for(D=b*a+f,c=0;c<v.getModuleCount();c+=1)v.isDark(b,c)&&(p=c*a+f,A+="M"+p+","+D+R);return A+='" stroke="transparent" fill="black"/>',A+="</svg>",A},v.createDataURL=function(a,f){a=a||2,f=typeof f=="undefined"?a*4:f;var e=v.getModuleCount()*a+f*2,u=f,o=e-f;return fr(e,e,function(d,c){if(u<=d&&d<o&&u<=c&&c<o){var p=Math.floor((d-u)/a),b=Math.floor((c-u)/a);return v.isDark(b,p)?0:1}else return 1})},v.createImgTag=function(a,f,e){a=a||2,f=typeof f=="undefined"?a*4:f;var u=v.getModuleCount()*a+f*2,o="";return o+="<img",o+=' src="',o+=v.createDataURL(a,f),o+='"',o+=' width="',o+=u,o+='"',o+=' height="',o+=u,o+='"',e&&(o+=' alt="',o+=F(e),o+='"'),o+="/>",o};var F=function(a){for(var f="",e=0;e<a.length;e+=1){var u=a.charAt(e);switch(u){case"<":f+="&lt;";break;case">":f+="&gt;";break;case"&":f+="&amp;";break;case'"':f+="&quot;";break;default:f+=u;break}}return f},ur=function(a){var f=1;a=typeof a=="undefined"?f*2:a;var e=v.getModuleCount()*f+a*2,u=a,o=e-a,d,c,p,b,D,A={"\u2588\u2588":"\u2588","\u2588 ":"\u2580"," \u2588":"\u2584"," ":" "},R={"\u2588\u2588":"\u2580","\u2588 ":"\u2580"," \u2588":" "," ":" "},I="";for(d=0;d<e;d+=2){for(p=Math.floor((d-u)/f),b=Math.floor((d+1-u)/f),c=0;c<e;c+=1)D="\u2588",u<=c&&c<o&&u<=d&&d<o&&v.isDark(p,Math.floor((c-u)/f))&&(D=" "),u<=c&&c<o&&u<=d+1&&d+1<o&&v.isDark(b,Math.floor((c-u)/f))?D+=" ":D+="\u2588",I+=a<1&&d+1>=o?R[D]:A[D];I+=`
22
+ `}return e%2&&a>0?I.substring(0,I.length-e-1)+Array(e+1).join("\u2580"):I.substring(0,I.length-1)};return v.createASCII=function(a,f){if(a=a||1,a<2)return ur(f);a-=1,f=typeof f=="undefined"?a*2:f;var e=v.getModuleCount()*a+f*2,u=f,o=e-f,d,c,p,b,D=Array(a+1).join("\u2588\u2588"),A=Array(a+1).join(" "),R="",I="";for(d=0;d<e;d+=1){for(p=Math.floor((d-u)/a),I="",c=0;c<e;c+=1)b=1,u<=c&&c<o&&u<=d&&d<o&&v.isDark(p,Math.floor((c-u)/a))&&(b=0),I+=b?D:A;for(p=0;p<a;p+=1)R+=I+`
23
+ `}return R.substring(0,R.length-1)},v.renderTo2dContext=function(a,f){f=f||2;for(var e=v.getModuleCount(),u=0;u<e;u++)for(var o=0;o<e;o++)a.fillStyle=v.isDark(u,o)?"black":"white",a.fillRect(o*f,u*f,f,f)},v};L.stringToBytesFuncs={default:function(x){for(var w=[],g=0;g<x.length;g+=1){var l=x.charCodeAt(g);w.push(l&255)}return w}},L.stringToBytes=L.stringToBytesFuncs.default,L.createStringToBytes=function(x,w){var g=(function(){for(var n=nr(x),s=function(){var C=n.read();if(C==-1)throw"eof";return C},t=0,r={};;){var h=n.read();if(h==-1)break;var i=s(),v=s(),_=s(),B=String.fromCharCode(h<<8|i),y=v<<8|_;r[B]=y,t+=1}if(t!=w)throw t+" != "+w;return r})(),l=63;return function(n){for(var s=[],t=0;t<n.length;t+=1){var r=n.charCodeAt(t);if(r<128)s.push(r);else{var h=g[n.charAt(t)];typeof h=="number"?(h&255)==h?s.push(h):(s.push(h>>>8),s.push(h&255)):s.push(l)}}return s}};var T={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},N={L:1,M:0,Q:3,H:2},k={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},P=(function(){var x=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],w=1335,g=7973,l=21522,n={},s=function(t){for(var r=0;t!=0;)r+=1,t>>>=1;return r};return n.getBCHTypeInfo=function(t){for(var r=t<<10;s(r)-s(w)>=0;)r^=w<<s(r)-s(w);return(t<<10|r)^l},n.getBCHTypeNumber=function(t){for(var r=t<<12;s(r)-s(g)>=0;)r^=g<<s(r)-s(g);return t<<12|r},n.getPatternPosition=function(t){return x[t-1]},n.getMaskFunction=function(t){switch(t){case k.PATTERN000:return function(r,h){return(r+h)%2==0};case k.PATTERN001:return function(r,h){return r%2==0};case k.PATTERN010:return function(r,h){return h%3==0};case k.PATTERN011:return function(r,h){return(r+h)%3==0};case k.PATTERN100:return function(r,h){return(Math.floor(r/2)+Math.floor(h/3))%2==0};case k.PATTERN101:return function(r,h){return r*h%2+r*h%3==0};case k.PATTERN110:return function(r,h){return(r*h%2+r*h%3)%2==0};case k.PATTERN111:return function(r,h){return(r*h%3+(r+h)%2)%2==0};default:throw"bad maskPattern:"+t}},n.getErrorCorrectPolynomial=function(t){for(var r=K([1],0),h=0;h<t;h+=1)r=r.multiply(K([1,M.gexp(h)],0));return r},n.getLengthInBits=function(t,r){if(1<=r&&r<10)switch(t){case T.MODE_NUMBER:return 10;case T.MODE_ALPHA_NUM:return 9;case T.MODE_8BIT_BYTE:return 8;case T.MODE_KANJI:return 8;default:throw"mode:"+t}else if(r<27)switch(t){case T.MODE_NUMBER:return 12;case T.MODE_ALPHA_NUM:return 11;case T.MODE_8BIT_BYTE:return 16;case T.MODE_KANJI:return 10;default:throw"mode:"+t}else if(r<41)switch(t){case T.MODE_NUMBER:return 14;case T.MODE_ALPHA_NUM:return 13;case T.MODE_8BIT_BYTE:return 16;case T.MODE_KANJI:return 12;default:throw"mode:"+t}else throw"type:"+r},n.getLostPoint=function(t){for(var r=t.getModuleCount(),h=0,i=0;i<r;i+=1)for(var v=0;v<r;v+=1){for(var _=0,B=t.isDark(i,v),y=-1;y<=1;y+=1)if(!(i+y<0||r<=i+y))for(var C=-1;C<=1;C+=1)v+C<0||r<=v+C||y==0&&C==0||B==t.isDark(i+y,v+C)&&(_+=1);_>5&&(h+=3+_-5)}for(var i=0;i<r-1;i+=1)for(var v=0;v<r-1;v+=1){var E=0;t.isDark(i,v)&&(E+=1),t.isDark(i+1,v)&&(E+=1),t.isDark(i,v+1)&&(E+=1),t.isDark(i+1,v+1)&&(E+=1),(E==0||E==4)&&(h+=3)}for(var i=0;i<r;i+=1)for(var v=0;v<r-6;v+=1)t.isDark(i,v)&&!t.isDark(i,v+1)&&t.isDark(i,v+2)&&t.isDark(i,v+3)&&t.isDark(i,v+4)&&!t.isDark(i,v+5)&&t.isDark(i,v+6)&&(h+=40);for(var v=0;v<r;v+=1)for(var i=0;i<r-6;i+=1)t.isDark(i,v)&&!t.isDark(i+1,v)&&t.isDark(i+2,v)&&t.isDark(i+3,v)&&t.isDark(i+4,v)&&!t.isDark(i+5,v)&&t.isDark(i+6,v)&&(h+=40);for(var O=0,v=0;v<r;v+=1)for(var i=0;i<r;i+=1)t.isDark(i,v)&&(O+=1);var m=Math.abs(100*O/r/r-50)/5;return h+=m*10,h},n})(),M=(function(){for(var x=new Array(256),w=new Array(256),g=0;g<8;g+=1)x[g]=1<<g;for(var g=8;g<256;g+=1)x[g]=x[g-4]^x[g-5]^x[g-6]^x[g-8];for(var g=0;g<255;g+=1)w[x[g]]=g;var l={};return l.glog=function(n){if(n<1)throw"glog("+n+")";return w[n]},l.gexp=function(n){for(;n<0;)n+=255;for(;n>=256;)n-=255;return x[n]},l})();function K(x,w){if(typeof x.length=="undefined")throw x.length+"/"+w;var g=(function(){for(var n=0;n<x.length&&x[n]==0;)n+=1;for(var s=new Array(x.length-n+w),t=0;t<x.length-n;t+=1)s[t]=x[t+n];return s})(),l={};return l.getAt=function(n){return g[n]},l.getLength=function(){return g.length},l.multiply=function(n){for(var s=new Array(l.getLength()+n.getLength()-1),t=0;t<l.getLength();t+=1)for(var r=0;r<n.getLength();r+=1)s[t+r]^=M.gexp(M.glog(l.getAt(t))+M.glog(n.getAt(r)));return K(s,0)},l.mod=function(n){if(l.getLength()-n.getLength()<0)return l;for(var s=M.glog(l.getAt(0))-M.glog(n.getAt(0)),t=new Array(l.getLength()),r=0;r<l.getLength();r+=1)t[r]=l.getAt(r);for(var r=0;r<n.getLength();r+=1)t[r]^=M.gexp(M.glog(n.getAt(r))+s);return K(t,0).mod(n)},l}var Y=(function(){var x=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12,7,37,13],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],w=function(n,s){var t={};return t.totalCount=n,t.dataCount=s,t},g={},l=function(n,s){switch(s){case N.L:return x[(n-1)*4+0];case N.M:return x[(n-1)*4+1];case N.Q:return x[(n-1)*4+2];case N.H:return x[(n-1)*4+3];default:return}};return g.getRSBlocks=function(n,s){var t=l(n,s);if(typeof t=="undefined")throw"bad rs block @ typeNumber:"+n+"/errorCorrectionLevel:"+s;for(var r=t.length/3,h=[],i=0;i<r;i+=1)for(var v=t[i*3+0],_=t[i*3+1],B=t[i*3+2],y=0;y<v;y+=1)h.push(w(_,B));return h},g})(),G=function(){var x=[],w=0,g={};return g.getBuffer=function(){return x},g.getAt=function(l){var n=Math.floor(l/8);return(x[n]>>>7-l%8&1)==1},g.put=function(l,n){for(var s=0;s<n;s+=1)g.putBit((l>>>n-s-1&1)==1)},g.getLengthInBits=function(){return w},g.putBit=function(l){var n=Math.floor(w/8);x.length<=n&&x.push(0),l&&(x[n]|=128>>>w%8),w+=1},g},q=function(x){var w=T.MODE_NUMBER,g=x,l={};l.getMode=function(){return w},l.getLength=function(t){return g.length},l.write=function(t){for(var r=g,h=0;h+2<r.length;)t.put(n(r.substring(h,h+3)),10),h+=3;h<r.length&&(r.length-h==1?t.put(n(r.substring(h,h+1)),4):r.length-h==2&&t.put(n(r.substring(h,h+2)),7))};var n=function(t){for(var r=0,h=0;h<t.length;h+=1)r=r*10+s(t.charAt(h));return r},s=function(t){if("0"<=t&&t<="9")return t.charCodeAt(0)-48;throw"illegal char :"+t};return l},z=function(x){var w=T.MODE_ALPHA_NUM,g=x,l={};l.getMode=function(){return w},l.getLength=function(s){return g.length},l.write=function(s){for(var t=g,r=0;r+1<t.length;)s.put(n(t.charAt(r))*45+n(t.charAt(r+1)),11),r+=2;r<t.length&&s.put(n(t.charAt(r)),6)};var n=function(s){if("0"<=s&&s<="9")return s.charCodeAt(0)-48;if("A"<=s&&s<="Z")return s.charCodeAt(0)-65+10;switch(s){case" ":return 36;case"$":return 37;case"%":return 38;case"*":return 39;case"+":return 40;case"-":return 41;case".":return 42;case"/":return 43;case":":return 44;default:throw"illegal char :"+s}};return l},rr=function(x){var w=T.MODE_8BIT_BYTE,g=x,l=L.stringToBytes(x),n={};return n.getMode=function(){return w},n.getLength=function(s){return l.length},n.write=function(s){for(var t=0;t<l.length;t+=1)s.put(l[t],8)},n},tr=function(x){var w=T.MODE_KANJI,g=x,l=L.stringToBytesFuncs.SJIS;if(!l)throw"sjis not supported.";(function(t,r){var h=l(t);if(h.length!=2||(h[0]<<8|h[1])!=r)throw"sjis not supported."})("\u53CB",38726);var n=l(x),s={};return s.getMode=function(){return w},s.getLength=function(t){return~~(n.length/2)},s.write=function(t){for(var r=n,h=0;h+1<r.length;){var i=(255&r[h])<<8|255&r[h+1];if(33088<=i&&i<=40956)i-=33088;else if(57408<=i&&i<=60351)i-=49472;else throw"illegal char at "+(h+1)+"/"+i;i=(i>>>8&255)*192+(i&255),t.put(i,13),h+=2}if(h<r.length)throw"illegal char at "+(h+1)},s},j=function(){var x=[],w={};return w.writeByte=function(g){x.push(g&255)},w.writeShort=function(g){w.writeByte(g),w.writeByte(g>>>8)},w.writeBytes=function(g,l,n){l=l||0,n=n||g.length;for(var s=0;s<n;s+=1)w.writeByte(g[s+l])},w.writeString=function(g){for(var l=0;l<g.length;l+=1)w.writeByte(g.charCodeAt(l))},w.toByteArray=function(){return x},w.toString=function(){var g="";g+="[";for(var l=0;l<x.length;l+=1)l>0&&(g+=","),g+=x[l];return g+="]",g},w},er=function(){var x=0,w=0,g=0,l="",n={},s=function(r){l+=String.fromCharCode(t(r&63))},t=function(r){if(!(r<0)){if(r<26)return 65+r;if(r<52)return 97+(r-26);if(r<62)return 48+(r-52);if(r==62)return 43;if(r==63)return 47}throw"n:"+r};return n.writeByte=function(r){for(x=x<<8|r&255,w+=8,g+=1;w>=6;)s(x>>>w-6),w-=6},n.flush=function(){if(w>0&&(s(x<<6-w),x=0,w=0),g%3!=0)for(var r=3-g%3,h=0;h<r;h+=1)l+="="},n.toString=function(){return l},n},nr=function(x){var w=x,g=0,l=0,n=0,s={};s.read=function(){for(;n<8;){if(g>=w.length){if(n==0)return-1;throw"unexpected end of file./"+n}var r=w.charAt(g);if(g+=1,r=="=")return n=0,-1;if(r.match(/^\s$/))continue;l=l<<6|t(r.charCodeAt(0)),n+=6}var h=l>>>n-8&255;return n-=8,h};var t=function(r){if(65<=r&&r<=90)return r-65;if(97<=r&&r<=122)return r-97+26;if(48<=r&&r<=57)return r-48+52;if(r==43)return 62;if(r==47)return 63;throw"c:"+r};return s},ar=function(x,w){var g=x,l=w,n=new Array(x*w),s={};s.setPixel=function(i,v,_){n[v*g+i]=_},s.write=function(i){i.writeString("GIF87a"),i.writeShort(g),i.writeShort(l),i.writeByte(128),i.writeByte(0),i.writeByte(0),i.writeByte(0),i.writeByte(0),i.writeByte(0),i.writeByte(255),i.writeByte(255),i.writeByte(255),i.writeString(","),i.writeShort(0),i.writeShort(0),i.writeShort(g),i.writeShort(l),i.writeByte(0);var v=2,_=r(v);i.writeByte(v);for(var B=0;_.length-B>255;)i.writeByte(255),i.writeBytes(_,B,255),B+=255;i.writeByte(_.length-B),i.writeBytes(_,B,_.length-B),i.writeByte(0),i.writeString(";")};var t=function(i){var v=i,_=0,B=0,y={};return y.write=function(C,E){if(C>>>E)throw"length over";for(;_+E>=8;)v.writeByte(255&(C<<_|B)),E-=8-_,C>>>=8-_,B=0,_=0;B=C<<_|B,_=_+E},y.flush=function(){_>0&&v.writeByte(B)},y},r=function(i){for(var v=1<<i,_=(1<<i)+1,B=i+1,y=h(),C=0;C<v;C+=1)y.add(String.fromCharCode(C));y.add(String.fromCharCode(v)),y.add(String.fromCharCode(_));var E=j(),O=t(E);O.write(v,B);var m=0,U=String.fromCharCode(n[m]);for(m+=1;m<n.length;){var H=String.fromCharCode(n[m]);m+=1,y.contains(U+H)?U=U+H:(O.write(y.indexOf(U),B),y.size()<4095&&(y.size()==1<<B&&(B+=1),y.add(U+H)),U=H)}return O.write(y.indexOf(U),B),O.write(_,B),O.flush(),E.toByteArray()},h=function(){var i={},v=0,_={};return _.add=function(B){if(_.contains(B))throw"dup key:"+B;i[B]=v,v+=1},_.size=function(){return v},_.indexOf=function(B){return i[B]},_.contains=function(B){return typeof i[B]!="undefined"},_};return s},fr=function(x,w,g){for(var l=ar(x,w),n=0;n<w;n+=1)for(var s=0;s<x;s+=1)l.setPixel(s,n,g(s,n));var t=j();l.write(t);for(var r=er(),h=t.toByteArray(),i=0;i<h.length;i+=1)r.writeByte(h[i]);return r.flush(),"data:image/gif;base64,"+r};return L})();(function(){$.stringToBytesFuncs["UTF-8"]=function(L){function T(N){for(var k=[],P=0;P<N.length;P++){var M=N.charCodeAt(P);M<128?k.push(M):M<2048?k.push(192|M>>6,128|M&63):M<55296||M>=57344?k.push(224|M>>12,128|M>>6&63,128|M&63):(P++,M=65536+((M&1023)<<10|N.charCodeAt(P)&1023),k.push(240|M>>18,128|M>>12&63,128|M>>6&63,128|M&63))}return k}return T(L)}})();(function(L){typeof define=="function"&&define.amd?define([],L):typeof W=="object"&&(V.exports=L())})(function(){return $})});return vr();})();
24
+
25
+
17
26
  const CSS = `
18
27
  .dm-wrap { display: flex; flex-direction: column; gap: 16px; padding: 4px 2px; }
19
28
  .dm-card {
20
- border: 1px solid var(--dsw-alias-border-l1, #e6e7ec);
21
- background: var(--dsw-alias-bg-layer-1, #f5f6f8);
29
+ border: 1px solid var(--dsw-alias-border-l1);
30
+ background: var(--dsw-alias-bg-layer-1);
22
31
  border-radius: 16px; padding: 18px;
23
32
  animation: dm-in .34s cubic-bezier(.2,.75,.25,1) both;
24
33
  }
25
34
  .dm-card:nth-child(2) { animation-delay: .06s; }
26
35
  .dm-card:nth-child(3) { animation-delay: .12s; }
27
- .dm-title { margin: 0; font-size: 14px; font-weight: 600; color: var(--dsw-alias-label-primary, #1b1c20); }
28
- .dm-sub { margin: 6px 0 0; font-size: 12.5px; line-height: 18px; color: var(--dsw-alias-label-secondary, #6b6e78); }
36
+ .dm-title { margin: 0; font-size: 14px; font-weight: 600; color: var(--dsw-alias-label-primary); }
37
+ .dm-sub { margin: 6px 0 0; font-size: 12.5px; line-height: 18px; color: var(--dsw-alias-label-secondary); }
29
38
  .dm-urlRow { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
30
39
  .dm-url {
31
40
  flex: 1; min-width: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
32
- font-size: 12.5px; color: #4D6BFE; word-break: break-all;
41
+ font-size: 12.5px; color: var(--dsw-alias-label-primary); word-break: break-all;
33
42
  }
34
43
  .dm-btn {
35
44
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
36
- border: 1px solid #4D6BFE; border-radius: 10px; padding: 9px 16px;
45
+ border: 1px solid var(--dsw-alias-label-primary); border-radius: 10px; padding: 9px 16px;
37
46
  font-size: 13px; font-weight: 600; cursor: pointer;
38
- background: #4D6BFE; color: #fff;
47
+ background: var(--dsw-alias-label-primary); color: var(--dsw-alias-bg-layer-1);
39
48
  transition: background-color .16s ease, transform .09s ease, box-shadow .16s ease, opacity .16s ease;
40
49
  }
41
- .dm-btn:hover { background: #5f7bff; border-color: #5f7bff; box-shadow: 0 4px 14px rgba(77,107,254,.28); }
50
+ .dm-btn:hover { background: var(--dsw-alias-label-primary); border-color: var(--dsw-alias-label-primary); box-shadow: 0 4px 14px var(--dsw-alias-bg-layer-1); }
42
51
  .dm-btn:active { transform: scale(.97); }
43
- .dm-btn:focus-visible { outline: 2px solid #4D6BFE; outline-offset: 2px; }
52
+ .dm-btn:focus-visible { outline: 2px solid var(--dsw-alias-label-primary); outline-offset: 2px; }
44
53
  .dm-btn[disabled] { opacity: .6; cursor: default; transform: none; box-shadow: none; }
45
54
  .dm-ghost {
46
55
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
47
- border: 1px solid var(--dsw-alias-border-l2, #d0d2da); border-radius: 10px;
56
+ border: 1px solid var(--dsw-alias-border-l2); border-radius: 10px;
48
57
  padding: 9px 16px; font-size: 13px; font-weight: 500; cursor: pointer;
49
- background: transparent; color: var(--dsw-alias-label-primary, #1b1c20);
58
+ background: transparent; color: var(--dsw-alias-label-primary);
50
59
  transition: background-color .16s ease, border-color .16s ease, transform .09s ease;
51
60
  }
52
- .dm-ghost:hover { background: rgba(127,127,127,.08); }
61
+ .dm-ghost:hover { background: var(--dsw-alias-bg-layer-1); }
53
62
  .dm-ghost:active { transform: scale(.97); }
54
- .dm-ghost:focus-visible { outline: 2px solid #4D6BFE; outline-offset: 2px; }
63
+ .dm-ghost:focus-visible { outline: 2px solid var(--dsw-alias-label-primary); outline-offset: 2px; }
55
64
  .dm-danger {
56
65
  display: inline-flex; align-items: center; justify-content: center;
57
- border: 1px solid #e5484d; border-radius: 10px; padding: 7px 12px;
58
- font-size: 12.5px; font-weight: 500; cursor: pointer; background: transparent; color: #e5484d;
66
+ border: 1px solid var(--dsw-alias-state-error-primary); border-radius: 10px; padding: 7px 12px;
67
+ font-size: 12.5px; font-weight: 500; cursor: pointer; background: transparent; color: var(--dsw-alias-state-error-primary);
59
68
  transition: background-color .16s ease, color .16s ease, transform .09s ease;
60
69
  }
61
- .dm-danger:hover { background: rgba(229,72,77,.1); }
70
+ .dm-danger:hover { background: var(--dsw-alias-bg-layer-1); }
62
71
  .dm-danger:active { transform: scale(.96); }
63
- .dm-danger[data-confirm="true"] { background: #e5484d; color: #fff; }
72
+ .dm-danger[data-confirm="true"] { background: var(--dsw-alias-state-error-primary); color: var(--dsw-alias-bg-layer-1); }
64
73
  .dm-copy {
65
- flex: none; border: 1px solid var(--dsw-alias-border-l2, #d0d2da); background: transparent;
66
- color: var(--dsw-alias-label-secondary, #6b6e78); border-radius: 8px; padding: 5px 10px;
74
+ flex: none; border: 1px solid var(--dsw-alias-border-l2); background: transparent;
75
+ color: var(--dsw-alias-label-secondary); border-radius: 8px; padding: 5px 10px;
67
76
  font-size: 11.5px; cursor: pointer; transition: background-color .15s, color .15s, transform .09s;
68
77
  }
69
- .dm-copy:hover { background: rgba(127,127,127,.1); }
78
+ .dm-copy:hover { background: var(--dsw-alias-bg-layer-1); }
70
79
  .dm-copy:active { transform: scale(.95); }
71
- .dm-copy[data-copied="true"] { border-color: #18a058; color: #18a058; }
80
+ .dm-copy[data-copied="true"] { border-color: var(--dsw-alias-label-primary); color: var(--dsw-alias-label-primary); }
72
81
  .dm-code {
73
- margin: 14px 0 2px; text-align: center;
82
+ margin: 8px 0 2px; text-align: center;
74
83
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
75
- font-size: 40px; font-weight: 700; letter-spacing: 8px; color: #4D6BFE;
84
+ font-size: 30px; font-weight: 700; letter-spacing: 6px; color: var(--dsw-alias-label-primary);
76
85
  animation: dm-pop .4s cubic-bezier(.2,.8,.25,1.1) both;
77
86
  }
78
87
  .dm-codeExpired { opacity: .45; text-decoration: line-through; }
79
- .dm-count { text-align: center; font-size: 12px; color: var(--dsw-alias-label-secondary, #6b6e78); }
80
- .dm-count b { color: #4D6BFE; font-weight: 600; font-variant-numeric: tabular-nums; }
88
+ .dm-count { text-align: center; font-size: 12px; color: var(--dsw-alias-label-secondary); }
89
+ .dm-count b { color: var(--dsw-alias-label-primary); font-weight: 600; font-variant-numeric: tabular-nums; }
81
90
  .dm-row {
82
91
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
83
- padding: 11px 0; border-top: 1px solid var(--dsw-alias-border-l1, #e6e7ec);
92
+ padding: 11px 0; border-top: 1px solid var(--dsw-alias-border-l1);
84
93
  animation: dm-in .28s ease both;
85
94
  }
86
95
  .dm-row:first-of-type { border-top: 0; }
87
- .dm-name { font-size: 13.5px; font-weight: 500; color: var(--dsw-alias-label-primary, #1b1c20); }
88
- .dm-detail { font-size: 12px; color: var(--dsw-alias-label-secondary, #6b6e78); margin-top: 2px; }
89
- .dm-muted { color: var(--dsw-alias-label-secondary, #6b6e78); font-size: 13px; }
90
- .dm-err { color: var(--dsw-alias-state-error-primary, #e5484d); font-size: 12.5px; margin-top: 10px; }
91
- .dm-domain { margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--dsw-alias-border-l2, #d0d2da); }
96
+ .dm-name { font-size: 13.5px; font-weight: 500; color: var(--dsw-alias-label-primary); }
97
+ .dm-detail { font-size: 12px; color: var(--dsw-alias-label-secondary); margin-top: 2px; }
98
+ .dm-muted { color: var(--dsw-alias-label-secondary); font-size: 13px; }
99
+ .dm-err { color: var(--dsw-alias-state-error-primary); font-size: 12.5px; margin-top: 10px; }
100
+ .dm-domain { margin-top: 14px; padding-top: 12px; border-top: 1px dashed var(--dsw-alias-border-l2); }
92
101
  .dm-domainHead { display: flex; align-items: baseline; gap: 8px; margin-bottom: 8px; }
93
- .dm-domainLabel { font-size: 12.5px; font-weight: 600; color: var(--dsw-alias-label-primary, #1b1c20); }
94
- .dm-domainHint { font-size: 11.5px; color: var(--dsw-alias-label-secondary, #6b6e78); }
102
+ .dm-domainLabel { font-size: 12.5px; font-weight: 600; color: var(--dsw-alias-label-primary); }
103
+ .dm-domainHint { font-size: 11.5px; color: var(--dsw-alias-label-secondary); }
95
104
  .dm-input {
96
105
  flex: 1; min-width: 0; height: 34px; padding: 0 10px; border-radius: 8px;
97
- border: 1px solid var(--dsw-alias-border-l2, #d0d2da); background: transparent;
98
- color: var(--dsw-alias-label-primary, #1b1c20); font-size: 12.5px;
106
+ border: 1px solid var(--dsw-alias-border-l2); background: transparent;
107
+ color: var(--dsw-alias-label-primary); font-size: 12.5px;
99
108
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
100
109
  }
101
- .dm-input::placeholder { color: var(--dsw-alias-label-secondary, #9aa0ad); }
102
- .dm-input:focus { outline: none; border-color: #4D6BFE; box-shadow: 0 0 0 2px rgba(77,107,254,.16); }
110
+ .dm-input::placeholder { color: var(--dsw-alias-label-secondary); }
111
+ .dm-input:focus { outline: none; border-color: var(--dsw-alias-label-primary); box-shadow: 0 0 0 2px var(--dsw-alias-bg-layer-1); }
103
112
  .dm-btnSm { padding: 8px 14px; font-size: 12.5px; }
104
113
  .dm-actions { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 12px; }
105
114
  .dm-spin {
106
- width: 14px; height: 14px; border: 2px solid rgba(255,255,255,.4);
107
- border-top-color: #fff; border-radius: 50%;
115
+ width: 14px; height: 14px; border: 2px solid var(--dsw-alias-bg-layer-1);
116
+ border-top-color: var(--dsw-alias-bg-layer-1); border-radius: 50%;
108
117
  animation: dm-rot .8s linear infinite;
109
118
  }
110
119
  .dm-spinGhost {
111
- width: 14px; height: 14px; border: 2px solid rgba(77,107,254,.25);
112
- border-top-color: #4D6BFE; border-radius: 50%;
120
+ width: 14px; height: 14px; border: 2px solid var(--dsw-alias-bg-layer-1);
121
+ border-top-color: var(--dsw-alias-label-primary); border-radius: 50%;
113
122
  animation: dm-rot .8s linear infinite;
114
123
  }
115
- .dm-live { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--dsw-alias-label-secondary, #6b6e78); }
116
- .dm-liveDot { width: 7px; height: 7px; border-radius: 50%; background: #18a058; box-shadow: 0 0 0 0 rgba(24,160,88,.45); animation: dm-pulse 2s infinite; }
117
- .dm-liveDotOff { background: #b8bcc6; animation: none; }
124
+ .dm-live { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--dsw-alias-label-secondary); }
125
+ .dm-liveDot { width: 7px; height: 7px; border-radius: 50%; background: var(--dsw-alias-label-primary); box-shadow: 0 0 0 0 var(--dsw-alias-bg-layer-1); animation: dm-pulse 2s infinite; }
126
+ .dm-liveDotOff { background: var(--dsw-alias-label-secondary); animation: none; }
127
+ /* ── 连接二维码:单码 + 地址切换器(与设置卡片同一套 token/圆角/间距)── */
128
+ .dm-cardHead { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
129
+ /* 二维码卡:主视觉只有一张码、一行地址、一行提示、两条折叠备用路径 */
130
+ /* 引导式两步:① 选择网络(radio 列表) ② 扫码连接(140–150px 小码) */
131
+ /* 连接向导:只用 DSH 主题 token(无自定义配色 / 阴影 / 装饰) */
132
+ .dm-help {
133
+ border: 0; background: transparent; padding: 0;
134
+ color: var(--dsw-alias-label-secondary);
135
+ font-size: 12px; line-height: 1; cursor: help;
136
+ }
137
+ .dm-page { margin-top: 8px; }
138
+ .dm-stepText { margin: 0 0 6px; font-size: 11.5px; line-height: 16px; color: var(--dsw-alias-label-secondary); }
139
+ .dm-pageTitle { margin: 0 0 8px; font-size: 13px; font-weight: 600; line-height: 19px; color: var(--dsw-alias-label-primary); }
140
+ .dm-netList { list-style: none; margin: 0; padding: 0; }
141
+ .dm-netItem + .dm-netItem { border-top: 1px solid var(--dsw-alias-border-l1); }
142
+ .dm-netRow {
143
+ /* 网格列:radio | 固定宽标签列 | 地址(1fr) | 右侧动作,保证各行左右都对齐 */
144
+ display: grid; grid-template-columns: auto 4.6em minmax(0, 1fr) auto;
145
+ align-items: center; column-gap: 8px;
146
+ padding: 8px 2px; cursor: pointer;
147
+ font-size: 12.5px; line-height: 18px; color: var(--dsw-alias-label-primary);
148
+ }
149
+ .dm-netRowUnset { color: var(--dsw-alias-label-secondary); }
150
+ .dm-netRadio { flex: none; margin: 0; }
151
+ /* 标签列固定宽度:局域网(3 字)与公网(2 字)必须让地址列从同一 x 开始 */
152
+ .dm-netLabel { flex: none; width: 4.6em; white-space: nowrap; font-weight: 500; }
153
+ /* 地址用 DSH 默认字体(不设 font-family,继承面板),只降字号与颜色 */
154
+ .dm-netAddr {
155
+ flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
156
+ font-family: inherit; /* <code> 自带 monospace,必须显式 inherit 才与 DSH 字体一致 */
157
+ font-size: 11.5px; color: var(--dsw-alias-label-secondary);
158
+ }
159
+ /* 公网域名区:放在最上方(先设域名,再出码) */
160
+ .dm-domainBlock { margin: 0 0 4px; padding-bottom: 6px; border-bottom: 1px solid var(--dsw-alias-border-l1); }
161
+ .dm-domainHead {
162
+ display: grid; grid-template-columns: auto 4.6em minmax(0, 1fr) auto;
163
+ align-items: center; column-gap: 8px; min-height: 30px;
164
+ padding: 0 2px; /* 与地址行同内边距,动作列严格对齐 */
165
+ }
166
+ .dm-domainLabel { flex: none; width: 4.6em; white-space: nowrap; font-size: 12.5px; font-weight: 500; color: var(--dsw-alias-label-primary); }
167
+ .dm-domainValue {
168
+ flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
169
+ font-size: 11.5px; color: var(--dsw-alias-label-secondary);
170
+ }
171
+ .dm-domainValueUnset { color: var(--dsw-alias-label-tertiary); }
172
+ .dm-netUnsetText { flex: 1; min-width: 0; font-size: 11.5px; color: var(--dsw-alias-label-secondary); }
173
+ .dm-rowAction {
174
+ justify-self: end; flex: none; border: 0; background: transparent; padding: 0 2px;
175
+ color: var(--dsw-alias-label-secondary); font-size: 12px; cursor: pointer;
176
+ }
177
+ .dm-rowAction:hover { color: var(--dsw-alias-label-primary); }
178
+ .dm-domainPad { width: 13px; }
179
+ .dm-netEdit, .dm-netOpen {
180
+ flex: none; border: 0; background: transparent; padding: 0 2px;
181
+ color: var(--dsw-alias-label-secondary); font-size: 12px; cursor: pointer;
182
+ }
183
+ .dm-netEdit { text-decoration: underline; justify-self: end; }
184
+ .dm-netOpen { justify-self: end; }
185
+ .dm-remoteForm { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--dsw-alias-border-l1); }
186
+ .dm-remoteHint { margin: 0 0 6px; font-size: 11.5px; line-height: 16px; color: var(--dsw-alias-label-secondary); }
187
+ .dm-remoteRow { display: flex; align-items: center; gap: 8px; }
188
+ .dm-input {
189
+ flex: 1; min-width: 0; height: 32px; padding: 0 10px; border-radius: 8px;
190
+ border: 1px solid var(--dsw-alias-border-l2); background: transparent;
191
+ color: var(--dsw-alias-label-primary); font-size: 12.5px;
192
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
193
+ }
194
+ .dm-input:focus { outline: none; border-color: var(--dsw-alias-border-l2); }
195
+ .dm-stepBtn {
196
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px;
197
+ padding: 7px 14px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 8px;
198
+ background: transparent; color: var(--dsw-alias-label-primary);
199
+ font-size: 12.5px; font-weight: 500; line-height: 18px; cursor: pointer;
200
+ }
201
+ .dm-stepBtn:hover { background: var(--dsw-alias-bg-layer-1); }
202
+ .dm-stepBtn:disabled { opacity: 0.55; cursor: default; }
203
+ .dm-stepBtnPrimary { font-weight: 600; }
204
+ .dm-pageActions { display: flex; align-items: center; gap: 8px; margin-top: 12px; }
205
+ .dm-pageTools { display: flex; align-items: center; gap: 12px; margin-top: 6px; }
206
+ .dm-toolBtn {
207
+ display: inline-flex; align-items: center; gap: 4px;
208
+ border: 0; background: transparent; padding: 0;
209
+ color: var(--dsw-alias-label-secondary); font-size: 11.5px; line-height: 16px; cursor: pointer;
210
+ }
211
+ .dm-toolGlyph { display: inline-flex; align-items: center; justify-content: center; }
212
+ .dm-qrCard {
213
+ display: inline-grid; place-items: center; padding: 8px;
214
+ border-radius: 10px; border: 1px solid var(--dsw-alias-border-l1);
215
+ /* 二维码白底是扫码必需(黑白对比),不属于面板配色 */
216
+ background: #ffffff;
217
+ }
218
+ .dm-qrCard svg { display: block; }
219
+ .dm-scanHint { margin: 8px 0 0; font-size: 12px; line-height: 17px; color: var(--dsw-alias-label-secondary); }
220
+ .dm-summaryLine { margin: 0; font-size: 12.5px; line-height: 18px; color: var(--dsw-alias-label-secondary); }
221
+ .dm-devicesOneLine { margin: 10px 2px 0; font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-secondary); }
222
+ .dm-cardTight { padding: 12px 16px; }
223
+ .dm-collapse { margin-top: 14px; border-top: 1px dashed var(--dsw-alias-border-l2); padding-top: 10px; }
224
+ .dm-collapseHead { display: flex; align-items: center; justify-content: space-between; width: 100%; padding: 4px 0; border: 0; background: transparent; cursor: pointer; color: var(--dsw-alias-label-secondary); font-size: 12.5px; font-weight: 500; }
225
+ .dm-collapseCaret { font-size: 12px; }
226
+ .dm-collapseBody { padding-top: 10px; }
118
227
  @keyframes dm-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
119
228
  @keyframes dm-pop { 0% { opacity: 0; transform: scale(.86); } 62% { transform: scale(1.05); } 100% { opacity: 1; transform: scale(1); } }
120
229
  @keyframes dm-rot { to { transform: rotate(360deg); } }
121
- @keyframes dm-pulse { 0% { box-shadow: 0 0 0 0 rgba(24,160,88,.45); } 70% { box-shadow: 0 0 0 6px rgba(24,160,88,0); } 100% { box-shadow: 0 0 0 0 rgba(24,160,88,0); } }
230
+ /* ── 定稿(A · 极简留白):大字级对比 + 留白分组,全部 DSH token ── */
231
+ .dm-connectCard { position: relative; border: 0; background: transparent; padding: 24px 24px 16px; }
232
+ .dm-connectHead { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 20px; }
233
+ .dm-headRight { flex: none; display: inline-flex; align-items: center; gap: 8px; }
234
+ .dm-pageTitle { margin: 0; font-size: 16px; font-weight: 600; letter-spacing: -0.2px; line-height: 22px; color: var(--dsw-alias-label-primary); }
235
+ .dm-stepTag { flex: none; padding-top: 2px; font-size: 11px; line-height: 16px; color: var(--dsw-alias-label-tertiary); font-variant-numeric: tabular-nums; }
236
+ .dm-page.dm-inner { padding: 0; background: transparent; border: 0; animation: dm-page-in 160ms ease both; }
237
+ @keyframes dm-page-in {
238
+ from { opacity: 0; transform: translateY(6px); }
239
+ to { opacity: 1; transform: translateY(0); }
240
+ }
241
+ @media (prefers-reduced-motion: reduce) { .dm-page.dm-inner { animation: none; } }
242
+ .dm-netList { margin: 0; }
243
+ /* 不给行加左右 margin:否则第一行与后续行起点差 6px,标签/地址会错位;hairline 用内缩伪元素实现 */
244
+ .dm-netItem + .dm-netItem { border-top: 1px solid var(--dsw-alias-border-l1); margin: 0; }
245
+ .dm-netRow { min-height: 44px; padding: 0 2px; } /* 行内基础 padding 由上方 grid 规则统一 */
246
+ .dm-qrStage { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
247
+ .dm-qrWrap { flex: 1; display: flex; justify-content: center; padding: 4px 0; }
248
+ .dm-qrCard { padding: 8px; border-radius: 12px; border: 1px solid var(--dsw-alias-border-l1); background: #ffffff; }
249
+ .dm-scanHint { text-align: center; margin: 8px 0 0; }
250
+ .dm-pageTools { justify-content: center; margin-top: 10px; }
251
+ .dm-pageActions { margin-top: 20px; }
252
+ .dm-navRow { display: flex; justify-content: flex-end; margin-top: 16px; }
253
+ .dm-arrow {
254
+ flex: none; display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
255
+ gap: 2px; min-width: 48px; min-height: 56px; padding: 6px;
256
+ border: 0; background: transparent; cursor: pointer;
257
+ color: var(--dsw-alias-label-tertiary);
258
+ transition: color 120ms ease;
259
+ }
260
+ .dm-arrow:hover, .dm-arrow:active, .dm-arrow:focus-visible { color: var(--dsw-alias-label-primary); }
261
+ .dm-arrow:disabled { opacity: 0.4; cursor: default; }
262
+ .dm-arrowGlyph { font-size: 20px; line-height: 20px; }
263
+ .dm-arrowLabel { font-size: 12px; line-height: 16px; color: var(--dsw-alias-label-tertiary); }
264
+ .dm-arrow:hover .dm-arrowLabel, .dm-arrow:active .dm-arrowLabel { color: var(--dsw-alias-label-primary); }
265
+ @media (prefers-reduced-motion: reduce) { .dm-arrow { transition: none; } }
266
+ .dm-stepBtn { height: 36px; padding: 0 16px; border-radius: 8px; }
267
+ .dm-stepBtnPrimary {
268
+ border-color: transparent;
269
+ background: var(--dsw-alias-button-primary-fill);
270
+ color: var(--dsw-alias-label-primary-foreground);
271
+ font-weight: 600;
272
+ }
273
+ .dm-stepBtnPrimary:hover { background: var(--dsw-alias-button-primary-hover); }
274
+ .dm-stepBtnGhost { border-color: transparent; background: transparent; padding: 0 8px; font-weight: 400; }
275
+ .dm-stepBtnGhost:hover { background: var(--dsw-alias-interactive-bg-hover); }
276
+ .dm-summaryLine { margin-top: 2px; }
277
+ .dm-devicesOneLine { margin: 4px 2px 0; }
278
+ @keyframes dm-pulse { 0% { box-shadow: 0 0 0 0 var(--dsw-alias-bg-layer-1); } 70% { box-shadow: 0 0 0 6px var(--dsw-alias-bg-layer-1); } 100% { box-shadow: 0 0 0 0 var(--dsw-alias-bg-layer-1); } }
122
279
  @media (prefers-reduced-motion: reduce) {
123
280
  .dm-card, .dm-row, .dm-code { animation: none !important; }
124
281
  .dm-btn, .dm-ghost, .dm-danger, .dm-copy { transition: none !important; }
@@ -222,6 +379,52 @@ window.__ModuleLoader__.load({
222
379
  * 有些机器上会多出 VPN / 虚拟网卡的公网段 IPv4(例如 2.0.1.1/24),
223
380
  * 那不是手机能用来直连的入口,混进来就会显示成三条地址。
224
381
  */
382
+ /**
383
+ * 扫码即配对的链接:手机打开 /m/ 后自动用 ?code= 完成配对(与手输 6 位码
384
+ * 等价),不需要再手动确认。
385
+ * @param base - 地址(含协议/端口,不含尾斜杠)
386
+ * @param code - 6 位配对码
387
+ */
388
+ function qrUrl(base, code) {
389
+ const origin = String(base || '').replace(/\/+$/, '')
390
+ return origin + '/m/?code=' + encodeURIComponent(String(code))
391
+ }
392
+
393
+ /**
394
+ * 把文本生成二维码 SVG 字符串(quiet zone 4 个模块)。
395
+ * @param text - 要编码的 URL
396
+ * @param size - 渲染边长(px)
397
+ */
398
+ function qrSvg(text, size, quietZone) {
399
+ let qr = null
400
+ for (let v = 1; v <= 40; v += 1) {
401
+ try {
402
+ const candidate = __qrGen(v, 'M')
403
+ candidate.addData(text)
404
+ candidate.make()
405
+ qr = candidate
406
+ break
407
+ } catch (e) { /* 容量不够就换下一个版本 */ }
408
+ }
409
+ if (!qr) return ''
410
+ const n = qr.getModuleCount()
411
+ // 二维码右下角要叠两个小图标按钮:留 8 个模块的静区,图标只压在静区上,
412
+ // 不遮挡任何数据模块(不牺牲可扫性)。
413
+ const quiet = quietZone === undefined ? 4 : quietZone
414
+ const total = n + quiet * 2
415
+ let path = ''
416
+ for (let r = 0; r < n; r += 1) {
417
+ for (let c = 0; c < n; c += 1) {
418
+ if (!qr.isDark(r, c)) continue
419
+ path += 'M' + (c + quiet) + ' ' + (r + quiet) + 'h1v1h-1z'
420
+ }
421
+ }
422
+ return '<svg xmlns="http://www.w3.org/2000/svg" width="' + size + '" height="' + size +
423
+ '" viewBox="0 0 ' + total + ' ' + total + '" shape-rendering="crispEdges" role="img" aria-label="连接二维码">' +
424
+ '<rect width="' + total + '" height="' + total + '" fill="#fff"></rect>' +
425
+ '<path d="' + path + '" fill="#000"></path></svg>'
426
+ }
427
+
225
428
  function lanOnly(urls) {
226
429
  const isPrivate = (host) =>
227
430
  host.startsWith('10.') || host.startsWith('192.168.') || /^172\.(1[6-9]|2\d|3[01])\./.test(host)
@@ -263,10 +466,15 @@ window.__ModuleLoader__.load({
263
466
  const [err, setErr] = useState('')
264
467
  const [copied, setCopied] = useState('')
265
468
  const [confirmRevoke, setConfirmRevoke] = useState('')
266
- const [now, setNow] = useState(() => Date.now())
267
469
  const [domainDraft, setDomainDraft] = useState('')
268
470
  const [domainBusy, setDomainBusy] = useState(false)
269
471
  const [domainErr, setDomainErr] = useState('')
472
+ // 连接二维码:当前选中的地址 + 切换面板 + 「远程连接(可选)」折叠区
473
+ const [selectedBase, setSelectedBase] = useState('')
474
+ const [wizardStep, setWizardStep] = useState(1) // 1 / 2 / 0=完成后的摘要态
475
+ const [remoteOpen, setRemoteOpen] = useState(false)
476
+ // 手动重画二维码用:固定配对码下重画不改变内容,但保留入口以防宿主轮换码。
477
+ const [qrVersion, setQrVersion] = useState(0)
270
478
  const domainEdited = useRef(false)
271
479
  const confirmTimer = useRef()
272
480
  const copyTimer = useRef()
@@ -291,12 +499,6 @@ window.__ModuleLoader__.load({
291
499
  return () => clearInterval(t)
292
500
  }, [refresh])
293
501
 
294
- // 倒计时 tick
295
- useEffect(() => {
296
- const t = setInterval(() => setNow(Date.now()), 1000)
297
- return () => clearInterval(t)
298
- }, [])
299
-
300
502
  useEffect(() => () => {
301
503
  clearTimeout(confirmTimer.current)
302
504
  clearTimeout(copyTimer.current)
@@ -346,6 +548,7 @@ window.__ModuleLoader__.load({
346
548
  .then((j) => {
347
549
  if (j && j.ok) {
348
550
  domainEdited.current = false
551
+ setRemoteOpen(false)
349
552
  refresh()
350
553
  } else {
351
554
  setDomainErr((j && j.error) || '保存失败')
@@ -370,47 +573,116 @@ window.__ModuleLoader__.load({
370
573
 
371
574
  const addrs = (status && status.addresses) || { lan: [], publicBase: null }
372
575
  const devices = (status && status.devices) || []
373
- const urls = [...lanOnly(addrs.lan), ...(addrs.publicBase ? [addrs.publicBase] : [])]
576
+ const lanBases = lanOnly(addrs.lan)
577
+ const publicBase = addrs.publicBase || ''
578
+ const urls = [...lanBases, ...(publicBase ? [publicBase] : [])]
374
579
  // 「清除」只在用户自己填过域名时出现;没填时 publicBase 来自 profile 配置,
375
580
  // 清掉只会回到配置值,按钮会误导。
376
581
  const hasDomainOverride = addrs.publicBaseCustom === true
377
- const isFixed = !!(code && (code.fixed === true || code.expiresAt > now + 86400000))
378
- const expired = code && !isFixed ? now > code.expiresAt : false
582
+ // 二维码内部仍用 6 位配对码拼 ?code=,但它不再作为 UI 概念出现。
583
+ const pairingCode = code ? String(code.code || '') : ''
584
+ const candidates = [...lanBases, ...(publicBase ? [publicBase] : [])]
585
+ // 默认选中规则(用户口径):配了公网地址 → 默认公网(在家在外都能连);
586
+ // 没配 → 默认第一个局域网地址。用户手动切换后保持其选择。
587
+ const defaultBase = publicBase !== '' ? publicBase : (lanBases[0] || '')
588
+ const activeBase = candidates.indexOf(selectedBase) >= 0 ? selectedBase : defaultBase
589
+ const isPublicSelected = activeBase !== '' && activeBase === publicBase
590
+ const qrText = pairingCode && activeBase ? qrUrl(activeBase, pairingCode) : ''
591
+ const displayAddr = (base) => String(base || '').replace(/^https?:\/\//, '').replace(/\/+$/, '')
592
+ const selectedReason = publicBase !== '' ? '已配置公网地址,默认选公网' : '未配置公网地址,默认选第一个局域网地址'
379
593
 
380
- return h('div', { className: 'dm-wrap' },
381
- h('style', { dangerouslySetInnerHTML: { __html: CSS } }),
594
+ // 全部解释性文字都收进标题旁的 (?):正文只留步骤标题与最必要的一行。
595
+ // 所有解释都收在这里;正文只保留步骤必要的两行字。
596
+ const HELP_TEXT = [
597
+ '扫码即连:手机扫码后自动配对,无需输入配对码。',
598
+ '扫不开 / 没反应:多半是手机与电脑不在同一网络(例如电脑有线、手机用流量),把手机接到同一 Wi-Fi,或选择「公网」。',
599
+ '公网地址需要一条反向代理 / 隧道(frp、Cloudflare Tunnel、Tailscale 均可)。',
600
+ ].join(' ')
382
601
 
383
- // ── 访问地址 ──
384
- h('div', { className: 'dm-card' },
385
- h('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10 } },
386
- h('p', { className: 'dm-title' }, '访问地址'),
387
- h('span', { className: 'dm-live' },
388
- h('span', { className: 'dm-liveDot' + (urls.length ? '' : ' dm-liveDotOff') }),
389
- urls.length ? '就绪' : '未监听'),
390
- ),
391
- h('p', { className: 'dm-sub' }, '手机浏览器打开以下任意地址,输入配对码即可使用。'),
392
- urls.length
393
- ? urls.map((u, i) =>
394
- h('div', { key: u, className: 'dm-urlRow' },
395
- h('span', { className: 'dm-url' }, `${u}/m/`),
396
- h('button', {
602
+ // 分页导航箭头:左=上一步,右=下一步/完成;热区 ≥40×40,视觉 20px。
603
+ const navArrow = (direction, label, glyph, onClick, disabled) => h('button', {
604
+ type: 'button',
605
+ className: 'dm-arrow dm-arrow-' + direction,
606
+ 'aria-label': label,
607
+ title: label,
608
+ disabled: disabled === true,
609
+ onClick: onClick,
610
+ },
611
+ h('span', { className: 'dm-arrowGlyph' }, glyph),
612
+ h('span', { className: 'dm-arrowLabel' }, label),
613
+ )
614
+
615
+ const regenerate = () => {
616
+ setQrVersion((v) => v + 1)
617
+ issue()
618
+ }
619
+
620
+ const copyGlyph = copied === qrText
621
+ ? h('span', { className: 'dm-toolGlyph' }, '✓')
622
+ : h('span', {
623
+ className: 'dm-toolGlyph',
624
+ dangerouslySetInnerHTML: {
625
+ __html: '<svg width="12" height="12" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"><rect x="5.5" y="5.5" width="8" height="8" rx="1.6"></rect><path d="M10.5 3.5H3.9A1.4 1.4 0 0 0 2.5 4.9v6.6"></path></svg>',
626
+ },
627
+ })
628
+
629
+ // ── 第 1 页:选择网络(原生 radio,不用自绘圆圈/填色)──
630
+ const netOptions = lanBases.map((base) => ({ base: base, kind: 'lan', configured: true }))
631
+ netOptions.push({ base: publicBase, kind: 'public', configured: publicBase !== '' })
632
+
633
+ const netNodes = netOptions.map((option) => {
634
+ const active = option.configured && option.base === activeBase
635
+ const label = option.kind === 'public' ? '公网' : '局域网'
636
+ return h('li', { key: option.kind + ':' + option.base, className: 'dm-netItem' },
637
+ h('label', { className: 'dm-netRow' + (option.configured ? '' : ' dm-netRowUnset') },
638
+ h('input', {
639
+ type: 'radio',
640
+ name: 'dm-connect-net',
641
+ className: 'dm-netRadio',
642
+ checked: active,
643
+ disabled: !option.configured,
644
+ 'data-kind': option.kind,
645
+ 'data-base': option.base,
646
+ onChange: () => {
647
+ if (!option.configured) { setRemoteOpen(true); return }
648
+ setSelectedBase(option.base)
649
+ setRemoteOpen(false)
650
+ },
651
+ }),
652
+ h('span', { className: 'dm-netLabel' }, label),
653
+ option.configured
654
+ ? h('code', { className: 'dm-netAddr' }, displayAddr(option.base))
655
+ : h('span', { className: 'dm-netUnsetText' }, '未配置'),
656
+ option.configured && option.kind === 'lan'
657
+ ? h('button', {
658
+ type: 'button',
659
+ className: 'dm-rowAction',
660
+ 'aria-label': '复制局域网链接',
661
+ title: '复制局域网链接',
662
+ onClick: (event) => {
663
+ event.preventDefault()
664
+ onCopy(option.base + '/m/' + (pairingCode ? '?code=' + encodeURIComponent(pairingCode) : ''))
665
+ },
666
+ }, '复制')
667
+ : option.kind === 'public' && option.configured
668
+ ? h('button', {
397
669
  type: 'button',
398
- className: 'dm-copy',
399
- 'data-copied': copied === u ? 'true' : 'false',
400
- onClick: () => onCopy(`${u}/m/`),
401
- }, copied === u ? '已复制' : '复制'),
402
- ),
403
- )
404
- : h('p', { className: 'dm-muted', style: { marginTop: 10 } }, '未检测到地址:请确认插件已启用,或在下面填写公网域名。'),
405
-
406
- // 公网域名:反代 / 隧道的对外地址。填了之后会出现在上面的地址里,
407
- // 手机直接打开「域名/m/」,配对时只需要输入配对码。
408
- h('div', { className: 'dm-domain' },
409
- h('div', { className: 'dm-domainHead' },
410
- h('span', { className: 'dm-domainLabel' }, '公网域名'),
411
- h('span', { className: 'dm-domainHint' }, '可选 · 反向代理 / 隧道地址'),
412
- ),
413
- h('div', { className: 'dm-urlRow' },
670
+ className: 'dm-rowAction dm-netEdit',
671
+ onClick: (event) => { event.preventDefault(); setRemoteOpen(true) },
672
+ }, '修改')
673
+ : h('button', {
674
+ type: 'button',
675
+ className: 'dm-netOpen',
676
+ onClick: (event) => { event.preventDefault(); setRemoteOpen(true) },
677
+ }, '›'),
678
+ ),
679
+ )
680
+ })
681
+
682
+ const remoteFormNode = remoteOpen
683
+ ? h('div', { className: 'dm-remoteForm' },
684
+ h('p', { className: 'dm-remoteHint' }, '公网域名(可选,不在同一局域网时使用)'),
685
+ h('div', { className: 'dm-remoteRow' },
414
686
  h('input', {
415
687
  className: 'dm-input',
416
688
  value: domainDraft,
@@ -429,72 +701,127 @@ window.__ModuleLoader__.load({
429
701
  }),
430
702
  h('button', {
431
703
  type: 'button',
432
- className: 'dm-btn dm-btnSm',
704
+ className: 'dm-stepBtn',
433
705
  disabled: domainBusy,
434
706
  onClick: () => saveDomain(domainDraft.trim()),
435
707
  }, domainBusy ? '保存中…' : '保存'),
436
708
  addrs.publicBaseCustom
437
- ? h('button', {
438
- type: 'button',
439
- className: 'dm-ghost dm-btnSm',
440
- disabled: domainBusy,
441
- onClick: clearDomain,
442
- }, '清除')
709
+ ? h('button', { type: 'button', className: 'dm-stepBtn', disabled: domainBusy, onClick: clearDomain }, '清除')
443
710
  : null,
444
711
  ),
445
712
  domainErr ? h('p', { className: 'dm-err' }, domainErr) : null,
446
- ),
713
+ )
714
+ : null
715
+
716
+ // 单页布局(用户要求取消分页):先设公网域名 → 选连接地址 → 二维码
717
+ const domainBlockNode = h('div', { className: 'dm-domainBlock' },
718
+ h('div', { className: 'dm-domainHead' },
719
+ h('span', { className: 'dm-domainPad', 'aria-hidden': 'true' }),
720
+ h('span', { className: 'dm-domainLabel' }, '公网域名'),
721
+ publicBase
722
+ ? h('span', { className: 'dm-domainValue' }, displayAddr(publicBase))
723
+ : h('span', { className: 'dm-domainValue dm-domainValueUnset' }, '未配置'),
724
+ h('button', {
725
+ type: 'button',
726
+ className: 'dm-netEdit',
727
+ onClick: (event) => { event.preventDefault(); setRemoteOpen((open) => !open) },
728
+ }, remoteOpen || publicBase === '' ? '收起' : '修改'),
447
729
  ),
730
+ remoteOpen || publicBase === '' ? remoteFormNode : null,
731
+ )
732
+
733
+ const qrCardNode = h('div', { className: 'dm-qrCard', 'data-qr-url': qrText },
734
+ h('div', { dangerouslySetInnerHTML: { __html: qrSvg(qrText, 124) } }),
735
+ )
448
736
 
449
- // ── 手机配对 ──
450
- h('div', { className: 'dm-card' },
451
- h('p', { className: 'dm-title' }, '手机配对'),
452
- h('p', { className: 'dm-sub' }, '在手机 /m/ 页输入下方配对码即可;固定码长期有效,无需反复配对。'),
453
- code
454
- ? h('div', null,
455
- h('div', { className: 'dm-code' + (expired ? ' dm-codeExpired' : '') }, code.code),
456
- h('p', { className: 'dm-count' },
457
- isFixed
458
- ? '固定配对码 · 长期有效'
459
- : expired
460
- ? '配对码已过期,请重新生成'
461
- : h('span', null, '剩余 ', h('b', null, remaining(code.expiresAt, now))),
462
- ),
463
- h('div', { className: 'dm-actions' },
464
- h('button', { type: 'button', className: 'dm-ghost', onClick: issue, disabled: issuing },
465
- issuing ? h('span', { className: 'dm-spinGhost' }) : null,
466
- issuing ? '生成中…' : '重新生成'),
467
- ),
468
- )
469
- : h('div', { className: 'dm-actions' },
470
- h('button', { type: 'button', className: 'dm-btn', onClick: issue, disabled: issuing },
471
- issuing ? h('span', { className: 'dm-spin' }) : null,
472
- issuing ? '生成中…' : '生成配对码'),
473
- ),
474
- err ? h('p', { className: 'dm-err' }, err) : null,
737
+ const connectNode = h('div', { key: 'connect-page', className: 'dm-page dm-inner' },
738
+ h('div', { className: 'dm-connectHead' },
739
+ h('p', { className: 'dm-pageTitle' }, '连接手机'),
740
+ h('span', { className: 'dm-headRight' },
741
+ h('span', { className: 'dm-help', role: 'note', tabIndex: 0, title: HELP_TEXT, 'aria-label': HELP_TEXT }, '?'),
742
+ ),
475
743
  ),
744
+ domainBlockNode,
745
+ h('ul', { className: 'dm-netList' }, netNodes),
746
+ h('div', { className: 'dm-qrStage' },
747
+ h('div', { className: 'dm-qrWrap' },
748
+ pairingCode && activeBase
749
+ ? qrCardNode
750
+ : h('p', { className: 'dm-muted' }, activeBase ? '正在生成二维码…' : '先设置公网域名,或连接局域网后自动生成二维码'),
751
+ ),
752
+ ),
753
+ h('p', { className: 'dm-scanHint' }, '扫码后自动配对,无需输入配对码'),
754
+ h('div', { className: 'dm-pageTools' },
755
+ h('button', {
756
+ type: 'button',
757
+ className: 'dm-toolBtn',
758
+ 'aria-label': '复制链接',
759
+ title: '复制链接',
760
+ onClick: () => onCopy(qrText),
761
+ }, copyGlyph, h('span', null, copied === qrText ? '已复制' : '复制链接')),
762
+ h('button', {
763
+ type: 'button',
764
+ className: 'dm-toolBtn',
765
+ 'aria-label': '刷新二维码',
766
+ title: '刷新二维码',
767
+ onClick: regenerate,
768
+ }, '刷新'),
769
+ ),
770
+ )
476
771
 
477
- // ── 已配对设备 ──
478
- h('div', { className: 'dm-card' },
479
- h('p', { className: 'dm-title' }, `已配对设备(${devices.length})`),
480
- devices.length === 0
481
- ? h('p', { className: 'dm-muted', style: { marginTop: 8 } }, '还没有设备。用上面的配对码在手机上配对后,这里会显示。')
482
- : devices.map((d, i) => {
483
- const desc = describeDevice(d)
484
- return h('div', { key: d.deviceId, className: 'dm-row', style: { animationDelay: `${i * 40}ms` } },
485
- h('div', null,
486
- h('div', { className: 'dm-name', title: d.userAgent || '' }, desc.title),
487
- h('div', { className: 'dm-detail' }, desc.detail),
488
- ),
489
- h('button', {
490
- type: 'button',
491
- className: 'dm-danger',
492
- 'data-confirm': confirmRevoke === d.deviceId ? 'true' : 'false',
493
- onClick: () => revoke(d.deviceId),
494
- }, confirmRevoke === d.deviceId ? '确认撤销?' : '撤销'),
495
- )
496
- }),
772
+ const wizardNode = connectNode
773
+
774
+ const errNode = err ? h('p', { className: 'dm-err' }, err) : null
775
+
776
+ const deviceRowNodes = devices.map((d, i) => {
777
+ const desc = describeDevice(d)
778
+ return h('div', { key: d.deviceId, className: 'dm-row', style: { animationDelay: (i * 40) + 'ms' } },
779
+ h('div', null,
780
+ h('div', { className: 'dm-name', title: d.userAgent || '' }, desc.title),
781
+ h('div', { className: 'dm-detail' }, desc.detail),
782
+ ),
783
+ h('button', {
784
+ type: 'button',
785
+ className: 'dm-danger',
786
+ 'data-confirm': confirmRevoke === d.deviceId ? 'true' : 'false',
787
+ onClick: () => revoke(d.deviceId),
788
+ }, confirmRevoke === d.deviceId ? '确认撤销?' : '撤销'),
789
+ )
790
+ })
791
+
792
+ const devicesNode = devices.length === 0
793
+ ? h('p', { className: 'dm-devicesOneLine' }, '已配对设备 · 暂无')
794
+ : h('div', { className: 'dm-card dm-cardTight' },
795
+ h('p', { className: 'dm-title' }, '已配对设备(' + devices.length + ')'),
796
+ deviceRowNodes,
797
+ )
798
+
799
+ useEffect(() => {
800
+ // 配置变化(新增/清除公网域名、网卡增减)时回到默认选择;
801
+ // 同一批候选里用户切换过的选择保持不动。
802
+ setSelectedBase((current) => (candidates.indexOf(current) >= 0 ? current : defaultBase))
803
+ }, [publicBase, lanBases.join('|')])
804
+
805
+ useEffect(() => {
806
+ // 公网域名刚配置好('' → 非空):自动选中公网并出码,符合「保存后自动出码」。
807
+ if (publicBase !== '') setSelectedBase(publicBase)
808
+ }, [publicBase])
809
+
810
+ useEffect(() => {
811
+ // 二维码内部依赖 ?code=:状态里没有码时静默取一次;配对码不再作为
812
+ // UI 概念出现(宿主默认是固定码,取一次即可长期有效)。
813
+ if (!code && !issuing) issue()
814
+ }, [code, issuing])
815
+
816
+ return h('div', { className: 'dm-wrap' },
817
+ h('style', { dangerouslySetInnerHTML: { __html: CSS } }),
818
+
819
+ // ── 连接二维码(主路径:单码 + 地址切换;备用:配对码 / 远程连接 折叠)──
820
+ h('div', { className: 'dm-card dm-connectCard', 'data-qr-card': '1' },
821
+ wizardNode,
822
+ errNode,
497
823
  ),
824
+ devicesNode,
498
825
  )
499
826
  }
500
827