// ==UserScript== // @name MikuMiku-share - 链接转为二维码 // @namespace http://tampermonkey.net/ // @version 1.0 // @author MakotoArai // @description 把鼠标可以点击的链接转为二维码。 // @icon https://img.icons8.com/plasticine/100/qr-code.png // @match *://*/* // @connect * // @grant GM_addStyle // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_xmlhttpRequest // ==/UserScript== (function () { 'use strict'; var t=Object.defineProperty,s=(s,i,e)=>((s,i,e)=>i in s?t(s,i,{enumerable:true,configurable:true,writable:true,value:e}):s[i]=e)(s,"symbol"!=typeof i?i+"":i,e);class i{constructor(t){s(this,"shadowRoot"),this.shadowRoot=t;}injectStyles(){const t=document.createElement("style");t.textContent=this.getStyles(),this.shadowRoot.appendChild(t);}getStyles(){return "\n :host { all: initial; }\n \n /* --- 通用动画 --- */\n @keyframes sonar-wave {\n 0% { transform: scale(0.5); opacity: 0.5; }\n 100% { transform: scale(3); opacity: 0; }\n }\n \n /* --- QR码面板样式 --- */\n .mms-qrcode-pannel {position: fixed; z-index: 99999999; background-color: #ffffff; border-radius: 12px;box-shadow: 0 10px 30px rgba(0,0,0,0.15); padding: 16px; display: flex;flex-direction: column; align-items: center; gap: 12px; opacity: 0;transform: scale(0.95); transition: opacity 0.2s ease-out, transform 0.2s ease-out;pointer-events: none; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;user-select: none;\n }\n .mms-qrcode-pannel.visible { opacity: 1; transform: scale(1); pointer-events: all; }\n .mms-qrcode-pannel:active { cursor: grab; }\n .mms-qrcode-canvas {border-radius: 8px;overflow: hidden;cursor: pointer;transition: transform 0.2s ease;}\n .mms-qrcode-canvas:hover { transform: scale(1.02); }\n .mms-qrcode-pannel footer { display: flex; gap: 8px; width: 100%; }\n .mms-qrcode-pannel button {flex: 1; padding: 8px 12px; border: 1px solid #e0e0e0; background-color: #f5f5f5;color: #333; border-radius: 6px; font-size: 12px; cursor: pointer;transition: background-color 0.2s, border-color 0.2s; display: flex; align-items: center; justify-content: center; gap: 4px;}\n .mms-qrcode-pannel button:hover { background-color: #e9e9e9; border-color: #d0d0d0; }\n\n /* --- 设置面板样式 --- */\n .mms-settings-overlay {position: fixed; top: 0; left: 0; width: 100vw; height: 100vh;background-color: rgba(0,0,0,0.4); z-index: 99999998; opacity: 0;transition: opacity 0.3s ease; pointer-events: none; display: flex; align-items: center; justify-content: center;}\n .mms-settings-overlay.visible { opacity: 1; pointer-events: all; }\n .mms-settings-panel {\n width: 90%; max-width: 500px; background: #fff; border-radius: 16px;\n box-shadow: 0 15px 40px rgba(0,0,0,0.2); transform: scale(0.95);\n transition: transform 0.3s ease, opacity 0.3s ease; opacity: 0; display: flex;\n flex-direction: column; max-height: 80vh; overflow: hidden;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; color: #333;\n }\n .mms-settings-overlay.visible .mms-settings-panel { transform: scale(1); opacity: 1; }\n .mms-settings-panel h2 {\n margin: 0; padding: 20px; font-size: 18px; font-weight: 600; border-bottom: 1px solid #eee;\n }\n .mms-settings-panel .content { padding: 20px; overflow-y: auto; flex: 1; }\n .mms-settings-panel .form-group { margin-bottom: 20px; display: flex; flex-direction: column; }\n .mms-settings-panel label, .mms-settings-panel .switch-label {\n display: block; font-weight: 500; margin-bottom: 8px; font-size: 14px;\n }\n .mms-settings-panel input[type=\"text\"], \n .mms-settings-panel input[type=\"number\"], \n .mms-settings-panel textarea, \n .mms-settings-panel select {\n width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 6px;\n box-sizing: border-box; font-size: 14px; background-color: #fff;\n }\n .mms-settings-panel textarea { min-height: 80px; resize: vertical; }\n .mms-settings-panel .switch {\n display: flex; align-items: center; justify-content: space-between; margin-bottom: 0;\n }\n .mms-settings-panel .switch-label { margin-bottom: 0; }\n .mms-settings-panel .switch-control { cursor: pointer; }\n .mms-settings-panel .switch input { display: none; }\n .mms-settings-panel .switch .slider {\n display: inline-block; width: 40px; height: 22px; background: #ccc; border-radius: 11px;\n position: relative; transition: background 0.2s ease; vertical-align: middle;\n }\n .mms-settings-panel .switch .slider::before {\n content: ''; position: absolute; width: 18px; height: 18px; background: #fff;\n border-radius: 50%; top: 2px; left: 2px; transition: transform 0.2s ease;\n box-shadow: 0 1px 3px rgba(0,0,0,0.2);\n }\n .mms-settings-panel .switch input:checked + .slider { background: #4CAF50; }\n .mms-settings-panel .switch input:checked + .slider::before { transform: translateX(18px); }\n .mms-settings-panel .footer {\n padding: 15px 20px; border-top: 1px solid #eee; text-align: right; background-color: #f9f9f9;\n }\n .mms-settings-panel .action-button, .mms-settings-panel button.save {\n padding: 10px 20px; border: none; background-color: #007bff; color: white;\n border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500;\n transition: background-color 0.2s ease, transform 0.1s ease;\n }\n .mms-settings-panel .action-button:hover, .mms-settings-panel button.save:hover { background-color: #0056b3; }\n .mms-settings-panel .action-button:active, .mms-settings-panel button.save:active { transform: scale(0.98); }\n\n /* --- 局域网设备面板样式 --- */\n .mms-lan-panel .header {\n display: flex; justify-content: space-between; align-items: center; padding: 0 20px;\n height: 65px; border-bottom: 1px solid #eee; flex-shrink: 0;\n }\n .mms-lan-panel h2 { border-bottom: none; padding: 0; }\n .mms-lan-panel .status { display: flex; align-items: center; gap: 8px; font-size: 13px; color: #666; }\n .mms-lan-panel .status .dot {\n width: 10px; height: 10px; border-radius: 50%; background-color: #ccc;\n transition: background-color 0.3s ease;\n }\n .mms-lan-panel .status .dot.connecting { background-color: #f39c12; }\n .mms-lan-panel .status .dot.connected { background-color: #2ecc71; }\n .mms-lan-panel .status .dot.disconnected { background-color: #e74c3c; }\n .mms-lan-panel .content { padding: 0; }\n \n .mms-lan-scanner {\n position: relative; height: 200px; background: radial-gradient(circle, #f5faff 0%, #e6f2ff 100%);\n display: flex; align-items: center; justify-content: center; overflow: hidden; border-bottom: 1px solid #eee;\n }\n .mms-lan-scanner .mms-scan-wave {\n position: absolute; width: 100px; height: 100px; border-radius: 50%;\n border: 2px solid rgba(0, 123, 255, 0.5); background: transparent;\n opacity: 0; pointer-events: none;\n }\n .mms-lan-scanner.active .mms-scan-wave { animation: sonar-wave 2s infinite ease-out; }\n .mms-lan-scanner.active .mms-scan-wave:nth-child(2) { animation-delay: 0.5s; }\n .mms-lan-scanner.active .mms-scan-wave:nth-child(3) { animation-delay: 1s; }\n\n .mms-device-icon {\n z-index: 1; text-align: center; color: #007bff;\n }\n .mms-device-icon svg { width: 48px; height: 48px; fill: currentColor; }\n .mms-device-icon span { display: block; font-size: 12px; font-weight: 500; margin-top: 4px; }\n \n .mms-device-list-container { padding: 15px 20px; }\n .mms-device-list-container h3 { margin: 0 0 10px; font-size: 14px; color: #333; }\n .mms-device-list { list-style: none; padding: 0; margin: 0; }\n .mms-device-list .no-devices { color: #888; text-align: center; padding: 20px; font-size: 14px; }\n .mms-device-list .device-item {\n display: flex; align-items: center; padding: 12px 0; border-bottom: 1px solid #f0f0f0;\n gap: 12px; transition: background-color 0.2s;\n }\n .mms-device-list .device-item:last-child { border-bottom: none; }\n .mms-device-list .device-status-dot {\n width: 8px; height: 8px; border-radius: 50%; background-color: #ccc; flex-shrink: 0;\n }\n .device-item[data-state=\"connecting\"] .device-status-dot { background-color: #f39c12; }\n .device-item[data-state=\"connected\"] .device-status-dot { background-color: #2ecc71; }\n .device-item[data-state=\"failed\"] .device-status-dot { background-color: #e74c3c; }\n \n .mms-device-list .device-id { flex-grow: 1; font-size: 14px; font-family: monospace; }\n .mms-device-list .device-state { font-size: 12px; color: #666; }\n /* --- Toast 提示样式 --- */\n .mms-toast-container{position:fixed;top:20px;left:50%;transform:translateX(-50%);z-index:999999999;pointer-events:none;display:flex;flex-direction:column;gap:10px;align-items:center}\n .mms-toast{background:#fff;border-radius:8px;padding:12px 20px;box-shadow:0 4px 12px rgba(0,0,0,0.15);display:flex;align-items:center;gap:10px;min-width:200px;max-width:400px;opacity:0;transform:translateY(-20px);transition:all .3s ease;pointer-events:all;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;font-size:14px;line-height:1.5}\n .mms-toast.visible{opacity:1;transform:translateY(0)}\n .mms-toast-icon{display:flex;align-items:center;justify-content:center;width:20px;height:20px;flex-shrink:0}\n .mms-toast-icon svg{width:100%;height:100%;fill:currentColor}\n .mms-toast-success{background:#f0f9ff;border:1px solid #bae6fd;color:#0369a1}\n .mms-toast-success .mms-toast-icon{color:#10b981}\n .mms-toast-error{background:#fef2f2;border:1px solid #fecaca;color:#991b1b}\n .mms-toast-error .mms-toast-icon{color:#ef4444}\n .mms-toast-info{background:#f0f9ff;border:1px solid #bfdbfe;color:#1e40af}\n .mms-toast-info .mms-toast-icon{color:#3b82f6}\n .mms-toast-message{flex:1;word-break:break-word}\n "}}class e{static log(...t){}static error(...t){}static warn(...t){}}s(e,"prefix","[Miku Share]");const n={enabled:true,showOnHover:true,hoverDelay:300,qrCodePosition:"mouse",fixedPosition:"top-right",fixedOffset:{x:20,y:20},qrCodeSize:160,enableLAN:false,autoResolveRedirect:true,blacklist:["example.com"],whitelist:[],deviceId:"mms-"+Math.random().toString(36).substring(2,10)};class o{constructor(t){s(this,"config"),s(this,"listeners",[]),this.config={...t};}async init(){const t=await this.load();this.config={...this.config,...t};}get(){return this.config}async set(t){this.config={...this.config,...t},await this.save(),this.notify();}async reset(){this.config=n,await this.save(),this.notify();}async load(){try{const t=await GM_getValue("miku-share-config","{}");return JSON.parse(t)}catch(t){return e.error("Failed to load config",t),{}}}async save(){try{await GM_setValue("miku-share-config",JSON.stringify(this.config));}catch(t){e.error("Failed to save config",t);}}subscribe(t){this.listeners.push(t);}notify(){this.listeners.forEach(t=>t(this.config));}}function a(t){return t&&t.t&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}const r=a(function(){var t=function(){},s=Object.prototype.hasOwnProperty,i=Array.prototype.slice;function e(t,e,n){for(var o,a,r=0,h=(n=i.call(arguments,2)).length;r>1&1,e=0;e0;s--)e[s]=e[s]?e[s-1]^l.EXPONENT[m.X(l.LOG[e[s]]+t)]:e[s-1];e[0]=l.EXPONENT[m.X(l.LOG[e[0]]+t)];}for(t=0;t<=i;t++)e[t]=l.LOG[e[t]];},Z:function(){var t,s,i,e,n,o=0,a=this.i,r=this.buffer,h=this.width;for(n=0;nh*h;)d-=h*h,l++;for(o+=l*m.N4,e=0;e=a-2&&(t=a-2,n>9&&t--);var r=t;if(n>9){for(o[r+2]=0,o[r+3]=0;r--;)o[r+3]|=255&(s=o[r])<<4,o[r+2]=s>>4;o[2]|=255&t<<4,o[1]=t>>4,o[0]=64|t>>12;}else {for(o[r+1]=0,o[r+2]=0;r--;)o[r+2]|=255&(s=o[r])<<4,o[r+1]=s>>4;o[1]|=255&t<<4,o[0]=64|t>>4;}for(r=t+3-(n<10);r=5&&(i+=m.N1+e[s]-5);for(s=3;st||3*e[s-3]>=4*e[s]||3*e[s+3]>=4*e[s])&&(i+=m.N3);return i},F:function(){var t,s;this.u=this.buffer.slice();var i=0,e=3e4;for(s=0;s<8&&(this.W(s),(t=this.Z())>=1)1&e&&(n[o-1-s+8*o]=1,s<6?n[8+o*s]=1:n[8+o*(s+1)]=1);for(s=0;s<7;s++,e>>=1)1&e&&(n[8+o*(o-7+s)]=1,s?n[6-s+8*o]=1:n[7+8*o]=1);},P:function(){var t,s,i=this.p,e=this.C,n=this.v,o=0,a=this.Y(),r=this.k,h=this.S,c=this.u;for(t=0;t1)for(t=h.BLOCK[e],i=n-7;;){for(s=n-7;s>t-3&&(this.V(s,i),!(s6)for(t=d.BLOCK[o-7],s=17,i=0;i<6;i++)for(e=0;e<3;e++,s--)1&(s>11?o>>s-12:t>>s)?(n[5-i+a*(2-e+a-11)]=1,n[2-e+a-11+a*(5-i)]=1):(this.G(5-i,2-e+a-11),this.G(2-e+a-11,5-i));},K:function(t,s){var i=m.st(t,s);return 1===this.q[i]},D:function(){var t,s,i,e=1,n=1,o=this.width,a=o-1,r=o-1,h=(this.p+this.v)*(this.k+this.S)+this.S;for(s=0;ss&&(i=t,t=s,s=i),i=s,i+=s*s,(i>>=1)+t},X:function(t){for(;t>=255;)t=((t-=255)>>8)+(255&t);return t},N1:3,N2:3,N3:40,N4:10}),u=m,f=a.extend({draw:function(){this.element.src=this.qrious.toDataURL();},reset:function(){this.element.src="";},resize:function(){var t=this.element;t.width=t.height=this.qrious.size;}}),p=o.extend(function(t,s,i,e){this.name=t,this.modifiable=Boolean(s),this.defaultValue=i,this.it=e;},{transform:function(t){var s=this.it;return "function"==typeof s?s(t,this):t}}),v=o.extend(null,{abs:function(t){return null!=t?Math.abs(t):null},hasOwn:function(t,s){return Object.prototype.hasOwnProperty.call(t,s)},noop:function(){},toUpperCase:function(t){return null!=t?t.toUpperCase():null}}),g=o.extend(function(t){this.options={},t.forEach(function(t){this.options[t.name]=t;},this);},{exists:function(t){return null!=this.options[t]},get:function(t,s){return g.et(this.options[t],s)},getAll:function(t){var s,i=this.options,e={};for(s in i)v.hasOwn(i,s)&&(e[s]=g.et(i[s],t));return e},init:function(t,s,i){var e,n;for(e in "function"!=typeof i&&(i=v.noop),this.options)v.hasOwn(this.options,e)&&(g.nt(n=this.options[e],n.defaultValue,s),g.ot(n,s,i));this.rt(t,s,true);},set:function(t,s,i){return this.nt(t,s,i)},setAll:function(t,s){return this.rt(t,s)},nt:function(t,s,i,e){var n=this.options[t];if(!n)throw new Error("Invalid option: "+t);if(!n.modifiable&&!e)throw new Error("Option cannot be modified: "+t);return g.nt(n,s,i)},rt:function(t,s,i){if(!t)return false;var e,n=false;for(e in t)v.hasOwn(t,e)&&this.nt(e,t[e],s,i)&&(n=true);return n}},{ot:function(t,s,i){var e={get:function(){return g.et(t,s)}};t.modifiable&&(e.set=function(e){g.nt(t,e,s)&&i(e,t);}),Object.defineProperty(s,t.name,e);},et:function(t,s){return s["_"+t.name]},nt:function(t,s,i){var e="_"+t.name,n=i[e],o=t.transform(null!=s?s:t.defaultValue);return i[e]=o,o!==n}}),b=g,w=o.extend(function(){this.ht={};},{getService:function(t){var s=this.ht[t];if(!s)throw new Error("Service is not being managed with name: "+t);return s},setService:function(t,s){if(this.ht[t])throw new Error("Service is already managed with name: "+t);s&&(this.ht[t]=s);}}),x=new b([new p("background",true,"white"),new p("backgroundAlpha",true,1,v.abs),new p("element"),new p("foreground",true,"black"),new p("foregroundAlpha",true,1,v.abs),new p("level",true,"L",v.toUpperCase),new p("mime",true,"image/png"),new p("padding",true,null,v.abs),new p("size",true,100,v.abs),new p("value",true,"")]),y=new w,k=o.extend(function(t){x.init(t,this,this.update.bind(this));var s=x.get("element",this),i=y.getService("element"),e=s&&i.isCanvas(s)?s:i.createCanvas(),n=s&&i.isImage(s)?s:i.createImage();this.ct=new r(this,e,true),this.lt=new f(this,n,n===s),this.update();},{get:function(){return x.getAll(this)},set:function(t){x.setAll(t,this)&&this.update();},toDataURL:function(t){return this.canvas.toDataURL(t||this.mime)},update:function(){var t=new u({level:this.level,value:this.value});this.ct.render(t),this.lt.render(t);}},{use:function(t){y.setService(t.getName(),t);}});Object.defineProperties(k.prototype,{canvas:{get:function(){return this.ct.getElement()}},image:{get:function(){return this.lt.getElement()}}});var S=k,$=o.extend({getName:function(){}}).extend({createCanvas:function(){},createImage:function(){},getName:function(){return "element"},isCanvas:function(){},isImage:function(){}}).extend({createCanvas:function(){return document.createElement("canvas")},createImage:function(){return document.createElement("img")},isCanvas:function(t){return t instanceof HTMLCanvasElement},isImage:function(t){return t instanceof HTMLImageElement}});return S.use(new $),S}()),h=class t{constructor(t){s(this,"shadowRoot"),s(this,"container",null),s(this,"queue",[]),s(this,"isShowing",false),this.shadowRoot=t,this.createContainer();}createContainer(){this.container=document.createElement("div"),this.container.className="mms-toast-container",this.shadowRoot.appendChild(this.container);}show(t,s="info",i=3e3){this.queue.push({message:t,type:s}),this.isShowing||this.showNext();}showNext(){if(0===this.queue.length)return void(this.isShowing=false);this.isShowing=true;const{message:t,type:s}=this.queue.shift(),i=document.createElement("div");i.className=`mms-toast mms-toast-${s}`;const e=this.getIcon(s),n=document.createElement("span");n.className="mms-toast-icon",n.innerHTML=e;const o=document.createElement("span");o.className="mms-toast-message",o.textContent=t,i.appendChild(n),i.appendChild(o),this.container.appendChild(i),requestAnimationFrame(()=>{i.classList.add("visible");}),setTimeout(()=>{i.classList.remove("visible"),setTimeout(()=>{i.remove(),this.showNext();},300);},3e3);}getIcon(t){return {success:'',error:'',info:''}[t]}static getInstance(s){return t.instance||(t.instance=new t(s)),t.instance}};s(h,"instance",null);let c=h;function l(t,s,i="info"){c.getInstance(t).show(s,i);}class d{constructor(t,i,e){s(this,"element"),s(this,"canvas"),s(this,"currentConfig"),s(this,"currentUrl",null),s(this,"shadowRoot"),s(this,"lanSharer"),s(this,"isMouseOverPanel",false),s(this,"isDragging",false),s(this,"dragStartX",0),s(this,"dragStartY",0),s(this,"elementStartX",0),s(this,"elementStartY",0),s(this,"customPosition",null),s(this,"isEnlarged",false),s(this,"originalSize",160),s(this,"enlargedSize",320),s(this,"lastClickTime",0),this.shadowRoot=t,this.currentConfig=i.get(),this.lanSharer=e,this.originalSize=this.currentConfig.qrCodeSize,this.enlargedSize=2*this.originalSize,this.element=document.createElement("div"),this.element.className="mms-qrcode-pannel",t.appendChild(this.element),this.canvas=document.createElement("canvas"),this.canvas.className="mms-qrcode-canvas",this.element.addEventListener("mouseenter",()=>{this.isMouseOverPanel=true;}),this.element.addEventListener("mouseleave",()=>{this.isMouseOverPanel=false,this.isDragging||this.hide();}),this.element.addEventListener("mousedown",this.handleMouseDown.bind(this)),document.addEventListener("mousemove",this.handleMouseMove.bind(this)),document.addEventListener("mouseup",this.handleMouseUp.bind(this)),this.canvas.addEventListener("click",this.handleClick.bind(this));}updateConfig(t){this.currentConfig=t,this.originalSize=t.qrCodeSize,this.enlargedSize=2*this.originalSize,this.element.classList.contains("visible")&&this.currentUrl&&this.render();}show(t,s,i,e){this.currentUrl=t,this.isEnlarged=false,this.render(),this.updatePosition(s,i),this.element.classList.add("visible");}hide(){this.element.classList.remove("visible"),this.customPosition=null,this.isEnlarged=false;}isMouseOver(){return this.isMouseOverPanel}handleMouseDown(t){if("BUTTON"===t.target.tagName)return;this.isDragging=true,this.dragStartX=t.clientX,this.dragStartY=t.clientY;const s=this.element.getBoundingClientRect();this.elementStartX=s.left,this.elementStartY=s.top,this.element.style.cursor="grabbing",t.preventDefault();}handleMouseMove(t){if(!this.isDragging)return;const s=this.elementStartX+(t.clientX-this.dragStartX),i=this.elementStartY+(t.clientY-this.dragStartY);this.element.style.left=`${s}px`,this.element.style.top=`${i}px`,this.element.style.right="auto",this.element.style.bottom="auto",this.customPosition={x:s,y:i};}handleMouseUp(t){this.isDragging&&(this.isDragging=false,this.element.style.cursor="");}handleClick(t){const s=Date.now();s-this.lastClickTime<300&&(t.preventDefault(),this.toggleEnlarge()),this.lastClickTime=s;}toggleEnlarge(){this.isEnlarged=!this.isEnlarged,this.render();}render(){this.element.innerHTML="";const t=this.isEnlarged?this.enlargedSize:this.originalSize,s=Math.ceil(.05*t);new r({element:this.canvas,value:this.currentUrl||"",size:t,level:"H",padding:s,background:"#ffffff",foreground:"#000000"}),this.element.appendChild(this.canvas);const i=document.createElement("footer");if(this.currentConfig.enableLAN){const t=this.createButton("📡 局域网打开",this.handleLanShare.bind(this));i.appendChild(t);}i.hasChildNodes()&&this.element.appendChild(i);}createButton(t,s){const i=document.createElement("button");return i.textContent=t,i.addEventListener("click",s),i}handleLanShare(){if(this.currentUrl)try{const t=this.lanSharer.sendUrlToAll(this.currentUrl);0===t?l(this.shadowRoot,"没有已连接的设备","info"):l(this.shadowRoot,`已发送到 ${t} 个设备`,"success");}catch(t){l(this.shadowRoot,"发送失败","error");}}updatePosition(t,s){if(this.customPosition)return this.element.style.left=`${this.customPosition.x}px`,this.element.style.top=`${this.customPosition.y}px`,this.element.style.right="auto",void(this.element.style.bottom="auto");if("fixed"===this.currentConfig.qrCodePosition){const t=this.currentConfig.fixedOffset,s=this.currentConfig.fixedPosition;switch(this.element.style.left="auto",this.element.style.right="auto",this.element.style.top="auto",this.element.style.bottom="auto",s){case "top-left":this.element.style.top=`${t.y}px`,this.element.style.left=`${t.x}px`;break;case "top-right":this.element.style.top=`${t.y}px`,this.element.style.right=`${t.x}px`;break;case "bottom-left":this.element.style.bottom=`${t.y}px`,this.element.style.left=`${t.x}px`;break;case "bottom-right":this.element.style.bottom=`${t.y}px`,this.element.style.right=`${t.x}px`;}}else {const i=this.element.getBoundingClientRect(),e=15;let n=t+e,o=s+e;n+i.width>window.innerWidth&&(n=t-i.width-e),o+i.height>window.innerHeight&&(o=s-i.height-e),this.element.style.top=`${o}px`,this.element.style.left=`${n}px`,this.element.style.right="auto",this.element.style.bottom="auto";}}destroy(){document.removeEventListener("mousemove",this.handleMouseMove.bind(this)),document.removeEventListener("mouseup",this.handleMouseUp.bind(this));}}class m{static resolveUrl(t){try{const i=new URL(t),n=i.hostname;if("blog.51cto.com"===n&&i.pathname.startsWith("/transfer")){const s=decodeURIComponent(t.replace("https://blog.51cto.com/transfer?",""));if(s&&s.startsWith("http"))return e.log(`解析重定向链接: ${t} -> ${s}`),s}const o=this.redirectMap[n];if(o){if(o.path&&!i.pathname.startsWith(o.path))return t;const n=o.params||this.defaultParams;for(const o of n){const n=i.searchParams.get(o);if(n)try{const s=decodeURIComponent(n);if(s.startsWith("http://")||s.startsWith("https://"))return e.log(`解析重定向链接: ${t} -> ${s}`),s}catch(s){e.warn(`解码URL参数失败: ${o}=${n}`,s);}}}return t}catch(s){return e.warn("解析URL失败",s),t}}static isRedirectUrl(t){try{const s=new URL(t);return !!this.redirectMap[s.hostname]}catch(s){return false}}}s(m,"redirectMap",{"link.juejin.cn":{params:["target"]},"link.csdn.net":{params:["target"]},"link.zhihu.com":{params:["target"]},"c.pc.qq.com":{params:["url"]},"hd.nowcoder.com":{params:["target"]},"link.uisdc.com":{params:["redirect"]},"link.gitcode.com":{params:["target"]},"open.work.weixin.qq.com":{params:["uri"]},"www.jianshu.com":{path:"/go-wild",params:["url"]},"www.douban.com":{path:"/link2",params:["url"]},"steamcommunity.com":{path:"/linkfilter",params:["url"]},"www.tianyancha.com":{path:"/security",params:["target"]},"game.bilibili.com":{path:"/linkfilter",params:["url"]},"www.chinaz.com":{path:"/go.shtml",params:["url"]},"www.youtube.com":{path:"/redirect",params:["q"]},"mail.qq.com":{path:"/cgi-bin/readtemplate",params:["gourl"]},"weibo.cn":{path:"/sinaurl",params:["u"]},"afdian.com":{path:"/link",params:["target"]},"ask.latexstudio.net":{path:"/go/index",params:["url"]},"blzxteam.com":{path:"/gowild",params:["url"]},"cloud.tencent.com":{path:"/developer/tools/blog-entry",params:["target"]},"docs.qq.com":{path:"/scenario/link.html",params:["url"]},"gitee.com":{path:"/link",params:["target"]},"leetcode.cn":{path:"/link",params:["target"]},"sspai.com":{path:"/link",params:["target"]},"t.me":{path:"/iv",params:["url"]},"tieba.baidu.com":{path:"/mo/q/checkurl",params:["url"]},"www.gcores.com":{path:"/link",params:["target"]},"www.kookapp.cn":{path:"/go-wild",params:["url"]},"www.oschina.net":{path:"/action/GoToLink",params:["url"]},"www.qcc.com":{path:"/web/transfer-link",params:["link"]},"www.yuque.com":{path:"/r/goto",params:["url"]},"xie.infoq.cn":{path:"/link",params:["target"]},"www.infoq.cn":{path:"/link",params:["target"]},"www.baike.com":{path:"/redirect_link",params:["url"]},"developers.weixin.qq.com":{path:"/community/middlepage/href",params:["href"]},"developer.aliyun.com":{path:"/redirect",params:["target"]},"www.kdocs.cn":{path:"/office/link",params:["target"]},"dalao.ru":{path:"/link",params:["target"]},"wx.mail.qq.com":{path:"/xmspamcheck/xmsafejump",params:["url"]},"aiqicha.baidu.com":{path:"/safetip",params:["target"]},"forum.mczwlt.net":{path:"/outgoing",params:["url"]}}),s(m,"defaultParams",["target","url","q","gourl","u","redirect","toasturl","link","href","pfurl","uri"]);class u{constructor(t,i,e){s(this,"shadowRoot"),s(this,"configManager"),s(this,"qrCodePannel"),s(this,"currentConfig"),s(this,"hoverTimeout",null),s(this,"currentTarget",null),s(this,"processedLinks",new WeakSet),this.shadowRoot=t,this.configManager=i,this.currentConfig=this.configManager.get(),this.qrCodePannel=new d(this.shadowRoot,this.configManager,e),this.configManager.subscribe(t=>{this.currentConfig=t,this.qrCodePannel.updateConfig(t);});}observe(){document.addEventListener("mouseover",this.handleMouseOver.bind(this),true),document.addEventListener("mouseout",this.handleMouseOut.bind(this),true),this.replaceAllLinks(),new MutationObserver(()=>{this.replaceAllLinks();}).observe(document.body,{childList:true,subtree:true}),e.log("DOM 观察器已启动。");}replaceAllLinks(){this.currentConfig.autoResolveRedirect&&document.querySelectorAll("a[href]").forEach(t=>{const s=t;if(this.processedLinks.has(s))return;if(!this.isLinkValid(s.href))return;const i=m.resolveUrl(s.href);i!==s.href&&(s.setAttribute("data-original-href",s.href),s.href=i,this.processedLinks.add(s),e.log(`链接已替换: ${s.getAttribute("data-original-href")} -> ${i}`));});}handleMouseOver(t){if(!this.currentConfig.enabled||!this.currentConfig.showOnHover)return;const s=t.target.closest("a");s&&s.href&&this.isLinkValid(s.href)&&(this.hoverTimeout&&clearTimeout(this.hoverTimeout),this.currentTarget=s,this.hoverTimeout=window.setTimeout(()=>{if(this.currentTarget){const s=this.currentTarget.getAttribute("data-original-href")||this.currentTarget.href;this.qrCodePannel.show(this.currentTarget.href,t.clientX,t.clientY,s);}},this.currentConfig.hoverDelay));}handleMouseOut(t){this.hoverTimeout&&(clearTimeout(this.hoverTimeout),this.hoverTimeout=null),this.shadowRoot.contains(t.relatedTarget)||this.qrCodePannel.isMouseOver()||this.qrCodePannel.hide();}isLinkValid(t){if(!t.startsWith("http://")&&!t.startsWith("https://"))return false;const{blacklist:s,whitelist:i}=this.currentConfig,e=window.location.hostname;return i.length>0?i.some(t=>e.includes(t)):!(s.length>0&&s.some(t=>e.includes(t)))}}class f{constructor(t,i,e){s(this,"shadowRoot"),s(this,"lanSharer"),s(this,"config"),s(this,"overlayElement",null),s(this,"panelElement",null),this.shadowRoot=t,this.config=i,this.lanSharer=e;}show(){this.overlayElement||this.create(),this.overlayElement.classList.add("visible"),this.lanSharer.onSignalingStateChange=this.updateSignalingStatus.bind(this),this.lanSharer.onPeersUpdate=this.renderDeviceList.bind(this),this.lanSharer.connect("miku-share-default-network");}hide(){var t;null==(t=this.overlayElement)||t.classList.remove("visible"),this.lanSharer.disconnect();}create(){this.overlayElement=document.createElement("div"),this.overlayElement.className="mms-settings-overlay mms-lan-overlay",this.overlayElement.addEventListener("click",t=>{t.target===this.overlayElement&&this.hide();}),this.panelElement=document.createElement("div"),this.panelElement.className="mms-settings-panel mms-lan-panel",this.overlayElement.appendChild(this.panelElement),this.shadowRoot.appendChild(this.overlayElement),this.render();}render(){this.panelElement&&(this.panelElement.innerHTML=`\n
\n

局域网设备

\n
\n \n 未连接\n
\n
\n
\n
\n
\n
\n
\n
\n \n ${this.config.deviceId}\n
\n
\n
\n

已发现的设备:

\n
    \n
\n
\n
\n `);}updateSignalingStatus(t){const s=this.shadowRoot.querySelector("#mms-lan-status"),i=this.shadowRoot.querySelector(".mms-lan-scanner");if(!s||!i)return;const e=s.querySelector(".dot"),n=s.querySelector(".text");switch(e.className=`dot ${t}`,i.classList.toggle("active","connected"===t),t){case "connecting":n.textContent="连接中...";break;case "connected":n.textContent="已连接,正在扫描";break;case "disconnected":n.textContent="已断开";}}renderDeviceList(t){const s=this.shadowRoot.querySelector("#mms-device-list");s&&(s.innerHTML=0!==t.length?t.map(t=>`\n
  • \n \n ${t.id}\n ${this.translateState(t.state)}\n
  • \n `).join(""):'
  • 暂未发现其他设备...
  • ');}translateState(t){return {new:"初始",connecting:"连接中...",connected:"已连接",disconnected:"已断开",failed:"连接失败",closed:"已关闭"}[t]||"未知"}}class p{constructor(t){s(this,"ws",null),s(this,"peerConnections",new Map),s(this,"dataChannels",new Map),s(this,"localId"),s(this,"networkId","miku-share-default-network"),s(this,"onSignalingStateChange",null),s(this,"onPeersUpdate",null),this.localId=t.deviceId;}connect(t){var s;this.networkId=t||this.networkId,this.ws&&this.ws.readyState===WebSocket.OPEN?e.log("已连接到信令服务器。"):(null==(s=this.onSignalingStateChange)||s.call(this,"connecting"),this.ws=new WebSocket("wss://websockets.chilvers.io/"),this.ws.onopen=()=>{var t;e.log("成功连接到信令服务器。正在加入网络..."),null==(t=this.onSignalingStateChange)||t.call(this,"connected"),this.sendMessage({type:"user-joined",from:this.localId,payload:{networkId:this.networkId}});},this.ws.onmessage=t=>{const s=JSON.parse(t.data);s.from===this.localId||s.to&&s.to!==this.localId||this.handleSignalingMessage(s);},this.ws.onclose=()=>{var t;e.warn("与信令服务器的连接已断开。"),null==(t=this.onSignalingStateChange)||t.call(this,"disconnected"),this.disconnectAllPeers();},this.ws.onerror=t=>{var s;e.error("信令服务器连接错误:",t),null==(s=this.onSignalingStateChange)||s.call(this,"disconnected");});}disconnect(){var t;null==(t=this.ws)||t.close(),this.disconnectAllPeers();}sendUrlToAll(t){let s=0;return this.dataChannels.forEach((i,n)=>{"open"===i.readyState&&(i.send(JSON.stringify({type:"url",payload:t})),s++,e.log(`已将链接发送到设备 ${n}`));}),s}handleSignalingMessage(t){switch(e.log("收到信令:",t),t.type){case "user-joined":this.createOffer(t.from);break;case "offer":this.createAnswer(t.from,t.payload);break;case "answer":this.handleAnswer(t.from,t.payload);break;case "ice-candidate":this.handleIceCandidate(t.from,t.payload);break;case "user-left":this.disconnectPeer(t.from);}}createPeerConnection(t){if(this.peerConnections.has(t))return this.peerConnections.get(t);const s=new RTCPeerConnection({iceServers:[{urls:"stun:stun.l.google.com:19302"}]});return s.onicecandidate=s=>{s.candidate&&this.sendMessage({type:"ice-candidate",from:this.localId,to:t,payload:s.candidate});},s.onconnectionstatechange=()=>{this.updatePeersStatus();},s.ondatachannel=s=>{const i=s.channel;this.dataChannels.set(t,i),i.onopen=()=>e.log(`与 ${t} 的数据通道已打开。`),i.onmessage=t=>{const s=JSON.parse(t.data);"url"===s.type&&s.payload&&window.open(s.payload,"_blank");};},this.peerConnections.set(t,s),this.updatePeersStatus(),s}async createOffer(t){const s=this.createPeerConnection(t),i=s.createDataChannel("url-channel");this.dataChannels.set(t,i);const e=await s.createOffer();await s.setLocalDescription(e),this.sendMessage({type:"offer",from:this.localId,to:t,payload:s.localDescription});}async createAnswer(t,s){const i=this.createPeerConnection(t);await i.setRemoteDescription(new RTCSessionDescription(s));const e=await i.createAnswer();await i.setLocalDescription(e),this.sendMessage({type:"answer",from:this.localId,to:t,payload:i.localDescription});}async handleAnswer(t,s){const i=this.peerConnections.get(t);i&&await i.setRemoteDescription(new RTCSessionDescription(s));}async handleIceCandidate(t,s){const i=this.peerConnections.get(t);i&&await i.addIceCandidate(new RTCIceCandidate(s));}disconnectPeer(t){var s;null==(s=this.peerConnections.get(t))||s.close(),this.peerConnections.delete(t),this.dataChannels.delete(t),this.updatePeersStatus();}disconnectAllPeers(){this.peerConnections.forEach(t=>t.close()),this.peerConnections.clear(),this.dataChannels.clear(),this.updatePeersStatus();}sendMessage(t){if(this.ws&&this.ws.readyState===WebSocket.OPEN){const s={...t,payload:{...t.payload,networkId:this.networkId}};this.ws.send(JSON.stringify(s));}}updatePeersStatus(){var t;const s=Array.from(this.peerConnections.entries()).map(([t,s])=>({id:t,state:s.connectionState}));null==(t=this.onPeersUpdate)||t.call(this,s);}}class v{constructor(t,i){s(this,"shadowRoot"),s(this,"configManager"),s(this,"overlayElement",null),s(this,"panelElement",null),s(this,"lanPanel"),s(this,"lanSharer"),s(this,"Logger",e),this.shadowRoot=t,this.configManager=i;const n=this.configManager.get();this.lanSharer=new p(n),this.lanPanel=new f(this.shadowRoot,n,this.lanSharer);}getLanSharer(){return this.lanSharer}init(){this.overlayElement=document.createElement("div"),this.overlayElement.className="mms-settings-overlay",this.overlayElement.addEventListener("click",t=>{t.target===this.overlayElement&&this.hide();}),this.panelElement=document.createElement("div"),this.panelElement.className="mms-settings-panel",this.overlayElement.appendChild(this.panelElement),this.shadowRoot.appendChild(this.overlayElement);}toggleVisibility(){var t;(null==(t=this.overlayElement)?void 0:t.classList.contains("visible"))?this.hide():this.show();}show(){var t;this.render(),null==(t=this.overlayElement)||t.classList.add("visible");}hide(){var t;null==(t=this.overlayElement)||t.classList.remove("visible");}render(){if(!this.panelElement)return;const t=this.configManager.get();this.panelElement.innerHTML=`\n

    MikuShare设置

    \n
    \n
    \n \n
    \n
    \n ${this.createSwitch("master-switch","总开关",t.enabled)}\n
    \n
    \n ${this.createSwitch("hover-switch","鼠标悬浮显示二维码",t.showOnHover)}\n
    \n
    \n \n \n
    \n
    \n \n \n
    \n
    \n ${this.createSwitch("lan-switch","启用局域网分享",t.enableLAN)}\n
    \n
    \n ${this.createSwitch("auto-resolve-switch","自动解析重定向链接",t.autoResolveRedirect)}\n

    \n 自动识别并解析知乎、CSDN等网站的跳转链接,直接显示真实URL的二维码\n

    \n
    \n
    \n \n \n
    \n
    \n \n \n
    \n
    \n \n
    \n \n \n
    \n
    \n
    \n \n \n
    \n
    \n \n \n
    \n
    \n \n `,this.attachEventListeners();}createSwitch(t,s,i){return `\n
    \n ${s}\n \n
    \n `}attachEventListeners(){var t,s,i,e,n,o,a;null==(s=null==(t=this.panelElement)?void 0:t.querySelector("#mms-save-btn"))||s.addEventListener("click",this.saveConfig.bind(this)),null==(e=null==(i=this.panelElement)?void 0:i.querySelector("#mms-manage-lan-btn"))||e.addEventListener("click",()=>{this.lanPanel.show();});const r=null==(n=this.panelElement)?void 0:n.querySelector("#mms-position-select"),h=null==(o=this.panelElement)?void 0:o.querySelector("#fixed-position-group"),c=null==(a=this.panelElement)?void 0:a.querySelector("#fixed-offset-group");null==r||r.addEventListener("change",()=>{const t="fixed"===r.value;h&&(h.style.display=t?"":"none"),c&&(c.style.display=t?"":"none");});}async saveConfig(){var t,s,i;if(this.panelElement)try{const e={enabled:this.panelElement.querySelector("#master-switch").checked,showOnHover:this.panelElement.querySelector("#hover-switch").checked,hoverDelay:parseInt(this.panelElement.querySelector("#mms-hover-delay").value)||300,qrCodeSize:parseInt(this.panelElement.querySelector("#mms-qr-size").value)||160,enableLAN:this.panelElement.querySelector("#lan-switch").checked,autoResolveRedirect:this.panelElement.querySelector("#auto-resolve-switch").checked,qrCodePosition:this.panelElement.querySelector("#mms-position-select").value,fixedPosition:(null==(t=this.panelElement.querySelector("#mms-fixed-position"))?void 0:t.value)||"top-right",fixedOffset:{x:parseInt(null==(s=this.panelElement.querySelector("#mms-offset-x"))?void 0:s.value)||20,y:parseInt(null==(i=this.panelElement.querySelector("#mms-offset-y"))?void 0:i.value)||20},blacklist:this.panelElement.querySelector("#mms-blacklist").value.split("\n").map(t=>t.trim()).filter(Boolean),whitelist:this.panelElement.querySelector("#mms-whitelist").value.split("\n").map(t=>t.trim()).filter(Boolean)};await this.configManager.set(e),l(this.shadowRoot,"设置已保存","success"),setTimeout(()=>{this.hide();},500);}catch(e){l(this.shadowRoot,"保存失败,请重试","error"),this.Logger.error("保存配置失败:",e);}}}!async function(){if(window.self!==window.top)return void e.log("脚本在iframe中,已停止执行。");e.log("脚本开始初始化...");const t=new o(n);await t.init();const s=document.createElement("div");s.id="miku-share-host",document.body.appendChild(s);const a=s.attachShadow({mode:"open"});new i(a).injectStyles();const r=new v(a,t);r.init();const h=r.getLanSharer();GM_registerMenuCommand("⚙️ 打开MikuShare设置",()=>{r.toggleVisibility();}),new u(a,t,h).observe(),e.log("脚本初始化完成。");}(); })();