// ==UserScript== // @name 资源日记 // @namespace resource-diary-harmony // @version 9.0.2 // @description 资源捕获工具 // @author 一程 // @match *://*/* // @license MIT // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJnIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAlIiBzdG9wLWNvbG9yPSIjMEE4NEZGIi8+PHN0b3Agb2Zmc2V0PSIxMDAlIiBzdG9wLWNvbG9yPSIjNUJDMEJFIi8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3Qgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiByeD0iNiIgZmlsbD0idXJsKCNnKSIvPjxwYXRoIGQ9Ik0zLjUgNy41TDEyIDMuNUwyMC41IDcuNUwxMiAxMS41WiIgZmlsbD0id2hpdGUiIGZpbGwtb3BhY2l0eT0iMC45IiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjAuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0zLjUgNy41TDEyIDExLjVWMjAuNUwzLjUgMTYuNVoiIGZpbGw9IndoaXRlIiBmaWxsLW9wYWNpdHk9IjAuNiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNMjAuNSA3LjVMMTIgMTEuNVYyMC41TDIwLjUgMTYuNVoiIGZpbGw9IndoaXRlIiBmaWxsLW9wYWNpdHk9IjAuNiIgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIwLjYiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4= // @grant GM_download // @grant GM_addStyle // @grant unsafeWindow // ==/UserScript== (function(){ 'use strict'; if(!/^https?:$/.test(location.protocol))return; if(window.top!==window.self)return; if(window.ResourceDiary)return; const processedElements=new WeakSet(); const processedBg=new WeakSet(); const processedSheets=new WeakSet(); const processedUrls=new Set(); const CONFIG=Object.freeze({ MAX_RESOURCES:800, MAX_URL_CACHE:3000, UPDATE_DELAY:120, BATCH_SIZE:60, DEBOUNCE_TIME:100, LAZY_LOAD_OFFSET:'120px', SCAN_INTERVAL:4000, ITEM_HEIGHT:80, FETCH_TIMEOUT:30000, OBSERVER_DEBOUNCE:220, CSS_SCAN_INTERVAL:8000 }); const DANGEROUS_PROTOCOLS=new Set([ 'javascript:','data:text/html','data:text/javascript', 'vbscript:','mocha:','livescript:','about:','blob:javascript', 'file:','ftp:','telnet:','ssh:' ]); const ALLOWED_MIMES=new Set([ 'image/','audio/','video/','application/json', 'application/xml','text/plain','text/css','text/javascript', 'application/javascript','application/octet-stream', 'application/vnd.apple.mpegurl','application/x-mpegURL', 'application/dash+xml','video/mp2t','application/x-shockwave-flash' ]); const EXT_TO_TYPE=Object.freeze({ mp4:'mp4',webm:'webm',ogg:'ogg',ogv:'ogv',mov:'mov', avi:'avi',mkv:'mkv',flv:'flv',m3u8:'m3u8',mpd:'mpd', ts:'ts',m4v:'m4v','3gp':'3gp','3g2':'3gp',wmv:'wmv',m2ts:'ts', mp3:'mp3',wav:'wav',flac:'flac',aac:'aac',m4a:'m4a', wma:'wma',oga:'oga',weba:'weba',opus:'opus', aiff:'aiff',mid:'mid',midi:'mid',ac3:'ac3',amr:'amr', jpg:'jpg',jpeg:'jpg',png:'png',gif:'gif',webp:'webp', svg:'svg',ico:'ico',bmp:'bmp',avif:'avif',jxl:'jxl', cur:'ico',tif:'bmp',tiff:'bmp',heic:'avif',heif:'avif', css:'stylesheet',js:'script',mjs:'script',json:'json', woff:'font',woff2:'font',ttf:'font',otf:'font',eot:'font' }); const MIME_TO_TYPE=Object.freeze({ 'video/mp4':'mp4','video/webm':'webm','video/ogg':'ogg', 'video/quicktime':'mov','video/x-matroska':'mkv', 'video/x-flv':'flv','video/x-msvideo':'avi', 'video/3gpp':'3gp','video/x-m4v':'m4v', 'application/vnd.apple.mpegurl':'m3u8', 'application/x-mpegURL':'m3u8', 'application/dash+xml':'mpd', 'video/mp2t':'ts', 'audio/mpeg':'mp3','audio/wav':'wav','audio/flac':'flac', 'audio/aac':'aac','audio/ogg':'oga','audio/webm':'weba', 'audio/opus':'opus','audio/x-ms-wma':'wma', 'audio/aiff':'aiff','audio/midi':'mid','audio/x-m4a':'m4a', 'image/jpeg':'jpg','image/png':'png','image/gif':'gif', 'image/webp':'webp','image/svg+xml':'svg','image/avif':'avif', 'image/bmp':'bmp','image/x-icon':'ico','image/tiff':'bmp', 'image/heic':'avif','image/heif':'avif', 'text/css':'stylesheet', 'application/javascript':'script','text/javascript':'script', 'application/json':'json','application/xml':'xml' }); const TYPE_COLORS=Object.freeze({ jpg:'#FF6B9D',jpeg:'#FF6B9D',png:'#5BC0BE',gif:'#FFB74D', webp:'#4FC3F7',svg:'#FFA726',ico:'#A1887F',bmp:'#90A4AE', avif:'#81C784',jxl:'#7986CB',image:'#0A84FF', mp4:'#FF5252',webm:'#5C6BC0',ogg:'#FFB74D',ogv:'#FFB74D', mov:'#AB47BC',avi:'#8D6E63',mkv:'#78909C',flv:'#EC407A', m3u8:'#66BB6A',mpd:'#29B6F6',ts:'#FFA726',m4v:'#EF5350', '3gp':'#8D6E63',wmv:'#78909C', mp3:'#0A84FF',wav:'#26A69A',flac:'#FFA726',aac:'#EF5350', m4a:'#7E57C2',wma:'#78909C',oga:'#FF7043',weba:'#42A5F5', opus:'#AB47BC',audio:'#5BC0BE',aiff:'#26A69A',mid:'#7986CB', ac3:'#FF7043',amr:'#8D6E63', script:'#FFA726',stylesheet:'#26A69A',json:'#5C6BC0', xml:'#90A4AE',font:'#A1887F', XHR:'#EF5350',fetch:'#FF7043',datauri:'#78909C',other:'#0A84FF' }); const ICON_COLORS=Object.freeze({ box:['#0A84FF','#5BC0BE'], file:['#78909C','#90A4AE'], image:['#5BC0BE','#4FC3F7','#FFB74D'], video:['#FF5252','#AB47BC','#FFB74D'], audio:['#0A84FF','#26A69A','#7E57C2'], script:['#FFA726','#FF7043'], stylesheet:['#26A69A','#0A84FF','#FFB74D'], json:['#5C6BC0','#7986CB'], xml:['#90A4AE','#78909C'], font:['#A1887F','#8D6E63'], wifi:['#0A84FF','#5BC0BE'], download:['#FF7043','#26A69A'], link:['#42A5F5','#26A69A'], play:['#FF6B6B','#FFB74D'], pause:['#78909C','#90A4AE'], copy:['#5BC0BE','#0A84FF'], close:['#EF5350','#90A4AE'], folder:['#FFB74D','#FFA726'], folderOpen:['#FFB74D','#FFA726','#FF8A65'], music:['#7E57C2','#AB47BC'], film:['#5C6BC0','#FF5252','#FFB74D'], code:['#26A69A','#0A84FF','#FFA726'], waveform:['#0A84FF','#26A69A','#FF6B9D'], search:['#0A84FF','#5BC0BE'], settings:['#78909C','#90A4AE','#FFB74D'], refresh:['#26A69A','#0A84FF'], list:['#5BC0BE','#0A84FF'], grid:['#FFB74D','#5BC0BE'], heart:['#FF6B9D','#EF5350'], back:['#0A84FF'], more:['#90A4AE','#78909C','#5BC0BE'], harmony:['#0A84FF','#5BC0BE'] }); const Icons={ box:``, file:``, image:``, video:``, audio:``, script:``, stylesheet:``, json:``, xml:``, font:``, wifi:``, download:``, link:``, play:``, pause:``, copy:``, close:``, folder:``, folderOpen:``, music:``, film:``, code:``, waveform:``, search:``, settings:``, refresh:``, list:``, grid:``, sortDesc:``, sortAsc:``, heart:``, back:``, more:``, harmony:``, expand:``, compress:``, speed:``, loop:``, volume:``, volumeMute:``, pip:`` } const OriginalAPIs={ xhrOpen:XMLHttpRequest.prototype.open, xhrSend:XMLHttpRequest.prototype.send, fetch:window.fetch, pushState:history.pushState, replaceState:history.replaceState }; const SecurityUtils={ urlCache:new Map(), maxCacheSize:1000, isSafeUrl(url){ if(!url||typeof url!=='string')return false; const cached=this.urlCache.get(url); if(cached!==undefined)return cached; if(this.urlCache.size>this.maxCacheSize){ const entries=Array.from(this.urlCache.entries()).slice(-500); this.urlCache.clear(); entries.forEach(([k,v])=>this.urlCache.set(k,v)); } const lower=url.toLowerCase().trim(); for(const p of DANGEROUS_PROTOCOLS){ if(lower.startsWith(p)){this.urlCache.set(url,false);return false;} } if(lower.startsWith('data:')){ const mime=lower.split(',')[0].split(':')[1]?.split(';')[0]||''; const safe=Array.from(ALLOWED_MIMES).some(m=>mime.startsWith(m)); this.urlCache.set(url,safe);return safe; } if(lower.startsWith('blob:')){ try{const o=new URL(url);const safe=o.origin===location.origin;this.urlCache.set(url,safe);return safe;} catch{this.urlCache.set(url,false);return false;} } this.urlCache.set(url,true);return true; }, sanitizeAttr(v){return String(v).replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"').replace(/'/g,''').trim().slice(0,1000);}, sanitizeFilename(f){return f.replace(/[<>:"/\\|?*\x00-\x1f]/g,'_').slice(0,255)||'download';}, createElement(tag,attrs={},children=[]){ const el=document.createElement(tag); for(const [k,v] of Object.entries(attrs)){ if(v==null)continue; if(k.startsWith('on')||k==='innerHTML')continue; if((k==='href'||k==='src'||k==='action')&&!this.isSafeUrl(v))continue; if(k==='style'&&typeof v==='object')Object.assign(el.style,v); else if(k==='textContent')el.textContent=v; else if(k==='className')el.className=v; else if(k==='dataset')Object.assign(el.dataset,v); else el.setAttribute(k,this.sanitizeAttr(v)); } const frag=document.createDocumentFragment(); for(const c of children){ if(typeof c==='string')frag.appendChild(document.createTextNode(c)); else if(c instanceof Node)frag.appendChild(c); } if(frag.childNodes.length)el.appendChild(frag); return el; } }; const RD={ resources:[], filteredResources:[], currentFilter:'image', viewMode:'list', sortMode:'time-desc', isInitialized:false, isPanelOpen:false, isDestroyed:false, _xhrDataMap:new WeakMap(), _rafId:null, _lastUpdate:0, _refreshTimer:null, _statsDirty:true, _renderSignature:'', _lastRenderedFilter:'', _lastRenderedCount:0, _resourceObserver:null, _listeners:[], _bodyCheckInterval:null, _host:null, _shadowRoot:null, _elements:{}, _hljsLoaded:false, _hljsLoading:false, _hlsLoaded:false, _hlsLoading:false, _loadHlsJS(){ return new Promise((resolve,reject)=>{ if(window.Hls){ this._hlsLoaded=true; resolve(window.Hls); return; } if(this._hlsLoading){ const check=()=>{ if(window.Hls){ this._hlsLoaded=true; resolve(window.Hls); }else{ setTimeout(check,100); } }; check(); return; } this._hlsLoading=true; const script=document.createElement('script'); script.src='https://cdnjs.cloudflare.com/ajax/libs/hls.js/1.5.13/hls.min.js'; script.onload=()=>{ this._hlsLoaded=true; this._hlsLoading=false; resolve(window.Hls); }; script.onerror=()=>{ this._hlsLoading=false; reject(); }; document.head.appendChild(script); }); }, _loadHighlightJS(){ return new Promise((resolve,reject)=>{ if(window.hljs){ this._hljsLoaded=true; resolve(window.hljs); return; } if(this._hljsLoading){ const check=()=>{ if(window.hljs){ this._hljsLoaded=true; resolve(window.hljs); }else{ setTimeout(check,100); } }; check(); return; } this._hljsLoading=true; const script=document.createElement('script'); script.src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js'; script.onload=()=>{ this._hljsLoaded=true; this._hljsLoading=false; resolve(window.hljs); }; script.onerror=()=>{ this._hljsLoading=false; reject(); }; document.head.appendChild(script); }); }, _applyHighlight(element,language){ this._loadHighlightJS().then(hljs=>{ if(!this._shadowRoot.querySelector('#rd-hljs-style')){ const link=document.createElement('link'); link.id='rd-hljs-style'; link.rel='stylesheet'; link.href='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css'; this._shadowRoot.appendChild(link); } if(language){ hljs.highlightElement(element,{language}); }else{ hljs.highlightElement(element); } }).catch(()=>{}); }, _detectLanguageFromUrl(url){ if(!url)return null; const ext=url.split('.').pop()?.toLowerCase()||''; const map={ 'js':'javascript','mjs':'javascript','ts':'typescript', 'css':'css','scss':'scss','less':'less', 'html':'html','htm':'html','xml':'xml','svg':'xml', 'json':'json','jsonc':'json', 'py':'python','rb':'ruby','java':'java','c':'c','cpp':'cpp', 'go':'go','rs':'rust','php':'php','sh':'bash','bash':'bash', 'md':'markdown','sql':'sql','yaml':'yaml','yml':'yaml', 'toml':'toml','ini':'ini','conf':'ini', 'text':'plaintext','txt':'plaintext' }; return map[ext]||null; }, _detectLanguageFromContentType(ct){ if(!ct)return null; const lower=ct.toLowerCase(); if(lower.includes('javascript'))return 'javascript'; if(lower.includes('json'))return 'json'; if(lower.includes('css'))return 'css'; if(lower.includes('html'))return 'html'; if(lower.includes('xml'))return 'xml'; if(lower.includes('markdown'))return 'markdown'; if(lower.includes('yaml'))return 'yaml'; if(lower.includes('sql'))return 'sql'; if(lower.includes('python'))return 'python'; if(lower.includes('php'))return 'php'; return null; }, getDisplayType(url,contentType='',initiatorType=''){ if(!SecurityUtils.isSafeUrl(url))return 'other'; if(url.startsWith('blob:')){ const m=contentType||''; if(m.includes('video'))return m.includes('webm')?'webm':m.includes('ogg')?'ogg':'mp4'; if(m.includes('audio'))return m.includes('mpeg')?'mp3':m.includes('wav')?'wav':m.includes('flac')?'flac':m.includes('aac')?'aac':'mp3'; if(m.includes('image'))return m.includes('png')?'png':m.includes('gif')?'gif':m.includes('webp')?'webp':m.includes('svg')?'svg':'jpg'; return 'other'; } if(url.startsWith('data:')){ const m=url.split(',')[0].split(':')[1]?.split(';')[0]||''; if(m.includes('image')){ if(m.includes('svg'))return 'svg'; if(m.includes('gif'))return 'gif'; if(m.includes('png'))return 'png'; if(m.includes('webp'))return 'webp'; if(m.includes('avif'))return 'avif'; return m.includes('jpeg')||m.includes('jpg')?'jpg':'image'; } if(m.includes('video'))return 'mp4'; if(m.includes('audio'))return 'mp3'; return 'datauri'; } if(contentType){ const mt=contentType.split(';')[0].toLowerCase(); if(MIME_TO_TYPE[mt])return MIME_TO_TYPE[mt]; if(mt.includes('video'))return 'mp4'; if(mt.includes('audio'))return 'mp3'; if(mt.includes('image'))return 'image'; if(mt.includes('script'))return 'script'; if(mt.includes('css'))return 'stylesheet'; if(mt.includes('font'))return 'font'; } try{ const u=new URL(url);const ext=u.pathname.split('.').pop()?.toLowerCase()||''; if(EXT_TO_TYPE[ext])return EXT_TO_TYPE[ext]; }catch{} if(initiatorType==='xmlhttprequest')return 'XHR'; if(initiatorType==='fetch')return 'fetch'; return 'other'; }, getFilterType(displayType){ const t=displayType.toLowerCase(); if(['mp4','webm','ogg','ogv','mov','avi','mkv','flv','m3u8','mpd', 'ts','m4v','3gp','wmv','m2ts', 'mp3','wav','flac','aac','m4a','wma','oga','weba','opus','audio', 'aiff','mid','ac3','amr'].includes(t))return 'media'; if(['jpg','jpeg','png','gif','webp','svg','ico','bmp','avif','jxl','image', 'tif','tiff','heic','heif','cur'].includes(t))return 'image'; return 'other'; }, getPreviewUrl(r){ if(!SecurityUtils.isSafeUrl(r.url))return ''; if(r.filterType==='image')return r.url; return ''; }, formatUrl(url){ if(!SecurityUtils.isSafeUrl(url))return '[不安全URL已阻止]'; if(url.startsWith('data:')){const m=url.split(',')[0];return SecurityUtils.sanitizeAttr(m)+',...';} if(url.startsWith('blob:')){try{const u=new URL(url);return 'blob:...'+u.pathname.slice(-20);}catch{return 'blob:...';}} return SecurityUtils.sanitizeAttr(url); }, getTypeIcon(displayType){ const t=displayType.toLowerCase(); if(['mp4','webm','ogg','ogv','mov','avi','mkv','flv','m3u8','mpd','ts','m4v','3gp','wmv','m2ts'].includes(t))return Icons.video; if(['mp3','wav','flac','aac','m4a','wma','oga','weba','opus','audio','aiff','mid','ac3','amr'].includes(t))return Icons.audio; if(['jpg','jpeg','png','gif','webp','svg','ico','bmp','avif','jxl','image','tif','tiff','heic','heif','cur'].includes(t))return Icons.image; if(['script','js','mjs'].includes(t))return Icons.script; if(['stylesheet','css'].includes(t))return Icons.stylesheet; if(['json'].includes(t))return Icons.json; if(['xml'].includes(t))return Icons.xml; if(['font','woff','woff2','ttf','otf','eot'].includes(t))return Icons.font; if(['xhr'].includes(t))return Icons.wifi; if(['fetch'].includes(t))return Icons.download; if(['datauri'].includes(t))return Icons.link; return Icons.file; }, async copyToClipboard(text){ if(!text||typeof text!=='string'){this.showToast('无效的复制内容');return;} try{ if(navigator.clipboard?.writeText)await navigator.clipboard.writeText(text); else{ const ta=SecurityUtils.createElement('textarea',{value:text,style:{position:'fixed',left:'-9999px',opacity:'0'}}); document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta); } this.showToast('已复制'); }catch{this.showToast('复制失败,手动复制');prompt('手动复制',text);} }, async downloadResource(url,filename){ if(!SecurityUtils.isSafeUrl(url)){this.showToast('不安全的下载地址');return;} this.showToast('开始下载...'); try{ if(typeof GM_download==='function'&&GM_download.toString().includes('native code')){ const name=filename||this.extractFilename(url); GM_download({url,name,onload:()=>this.showToast('下载完成'),onerror:()=>{this.fallbackDownload(url,filename);}}); }else await this.fallbackDownload(url,filename); }catch{this.fallbackDownload(url,filename);} }, fallbackDownload(url,filename){ return new Promise((resolve,reject)=>{ try{ const a=document.createElement('a');a.href=url;a.download=filename||this.extractFilename(url);a.style.display='none';document.body.appendChild(a); let same=false;try{same=new URL(url,location.href).origin===location.origin;}catch{} if(url.startsWith('data:')||url.startsWith('blob:')||same){a.click();this.showToast('下载已开始');setTimeout(()=>{if(a.parentNode)document.body.removeChild(a);},100);resolve();} else{document.body.removeChild(a);this.fetchAndDownload(url,filename).then(resolve).catch(reject);} }catch(err){reject(err);} }); }, async fetchAndDownload(url,filename){ const ctrl=new AbortController();const tid=setTimeout(()=>ctrl.abort(),CONFIG.FETCH_TIMEOUT); try{ const resp=await fetch(url,{credentials:'omit',signal:ctrl.signal}); clearTimeout(tid); if(!resp.ok)throw new Error('Fetch failed'); const blob=await resp.blob();const blobUrl=URL.createObjectURL(blob); const a=document.createElement('a');a.href=blobUrl;a.download=filename||this.extractFilename(url);a.click(); setTimeout(()=>URL.revokeObjectURL(blobUrl),1000);this.showToast('下载完成'); }catch(err){ if(err.name==='AbortError')this.showToast('下载超时'); else{window.open(url,'_blank','noopener,noreferrer');this.showToast('已在新标签页打开');} } }, extractFilename(url){ try{ if(url.startsWith('data:')){const ext=url.match(/data:image\/(\w+)/)?.[1]||'bin';return `datauri_${Date.now()}.${ext}`;} if(url.startsWith('blob:'))return `blob_${Date.now()}.bin`; const u=new URL(url,location.href);const path=decodeURIComponent(u.pathname);const name=path.split('/').pop()||'download'; return SecurityUtils.sanitizeFilename(name); }catch{return `download_${Date.now()}`;} }, showToast(msg){ if(!this._shadowRoot)return; const old=this._shadowRoot.getElementById('rd-toast');if(old)old.remove(); const el=SecurityUtils.createElement('div',{id:'rd-toast',style:{}}); el.innerHTML=` ${msg} `; this._shadowRoot.appendChild(el); requestAnimationFrame(()=>{el.classList.add('show');}); setTimeout(()=>{ el.classList.remove('show'); setTimeout(()=>{if(el.parentNode)el.remove();},400); },2000); }, init(){if(this.isInitialized||this.isDestroyed)return;this._waitForBodyAndInit();}, _waitForBodyAndInit(){ if(document.body){this._delayedInit();return;} let attempts=0;const max=50;if(this._bodyCheckInterval)clearInterval(this._bodyCheckInterval); this._bodyCheckInterval=setInterval(()=>{ attempts++; if(document.body){clearInterval(this._bodyCheckInterval);this._bodyCheckInterval=null;this._delayedInit();} else if(attempts>=max){clearInterval(this._bodyCheckInterval);this._bodyCheckInterval=null;const obs=new MutationObserver((_,o)=>{if(document.body){o.disconnect();this._delayedInit();}});obs.observe(document.documentElement,{childList:true,subtree:true});} },100); }, _delayedInit(){ const fn=()=>{try{this._performInit();}catch(e){console.error('ResourceDiary init error:',e);this.isInitialized=false;setTimeout(()=>this._waitForBodyAndInit(),1000);}}; if(typeof requestIdleCallback!=='undefined')requestIdleCallback(fn,{timeout:2000});else setTimeout(fn,0); }, _performInit(){ if(this.isInitialized||this.isDestroyed)return; this.setupUI(); this.setupResourceCapture(); this.setupResourceObserver(); this.setupSPASupport(); this.setupKeyboardShortcuts(); this.setupVisibilityHandler(); setTimeout(()=>this.scanExistingResources(),500); setTimeout(()=>this.scanExistingResources(),1500); setTimeout(()=>this.scanExistingResources(),3000); this.setupPeriodicScan(); this.isInitialized=true; }, setupPeriodicScan(){ if(this._refreshTimer)clearInterval(this._refreshTimer); this._refreshTimer=setInterval(()=>{ if(document.hidden)return; this.scanExistingResources(); if(this.isPanelOpen)this.scheduleUpdate(); },CONFIG.SCAN_INTERVAL); }, rebuildUI(){ const wasOpen=this.isPanelOpen; const scrollTop=this._elements.list?.scrollTop||0; this.destroyUI(); this.setupUI(); this.updateFilterButtons(); this.scrollFilterToActive(); if(this._elements.list)this._elements.list.scrollTop=scrollTop; if(wasOpen){this._elements.panel?.classList.add('active');this.isPanelOpen=true;this.renderList();} }, destroy(){ this.isDestroyed=true; if(this._refreshTimer){clearInterval(this._refreshTimer);this._refreshTimer=null;} if(this._rafId){cancelAnimationFrame(this._rafId);this._rafId=null;} if(this._bodyCheckInterval){clearInterval(this._bodyCheckInterval);this._bodyCheckInterval=null;} if(this._resourceObserver){this._resourceObserver.disconnect();this._resourceObserver=null;} if(this._perfObserver){this._perfObserver.disconnect();this._perfObserver=null;} if(this._listeners){this._listeners.forEach(fn=>{try{fn();}catch{}});this._listeners=[];} XMLHttpRequest.prototype.open=OriginalAPIs.xhrOpen; XMLHttpRequest.prototype.send=OriginalAPIs.xhrSend; window.fetch=OriginalAPIs.fetch; history.pushState=OriginalAPIs.pushState; history.replaceState=OriginalAPIs.replaceState; this.destroyUI(); this.resources=[];this.filteredResources=[]; processedUrls.clear(); }, destroyUI(){ if(this._host&&this._host.parentNode)this._host.parentNode.removeChild(this._host); this._host=null;this._shadowRoot=null;this._elements={}; this._lockDepth=0; if(document.body){document.body.style.overflow='';document.documentElement.style.overflow='';document.body.style.paddingRight='';} }, updateFilterButtons(){ if(!this._shadowRoot)return; this._shadowRoot.querySelectorAll('.rd-filter').forEach(b=>b.classList.toggle('active',b.dataset.filter===this.currentFilter)); }, scrollFilterToActive(){ if(!this._shadowRoot)return; requestAnimationFrame(()=>{ const act=this._shadowRoot.querySelector('.rd-filter.active');const c=this._shadowRoot.querySelector('.rd-filters-scroll'); if(act&&c){const left=act.offsetLeft-(c.clientWidth/2)+(act.clientWidth/2);c.scrollTo({left:Math.max(0,left),behavior:'smooth'});} }); }, setupUI(){ if(this._host)this.destroyUI(); this._host=document.createElement('div'); this._host.style.cssText='position:fixed;top:0;left:0;width:0;height:0;overflow:visible;pointer-events:none;z-index:2147483647;'; this._shadowRoot=this._host.attachShadow({mode:'open'}); this.injectStyles(); this.createUI(); document.body.appendChild(this._host); this.bindEvents(); }, injectStyles(){ if(!this._shadowRoot)return; const style=document.createElement('style'); style.textContent=` :host{ all:initial; display:block; position:fixed;top:0;left:0;width:0;height:0;overflow:visible;pointer-events:none;z-index:2147483647; font-family:'HarmonyOS Sans SC','HarmonyOS Sans','PingFang SC','-apple-system',BlinkMacSystemFont,sans-serif; --rd-bg-primary:rgba(245,247,250,0.72); --rd-bg-secondary:rgba(255,255,255,0.65); --rd-bg-tertiary:rgba(255,255,255,0.45); --rd-bg-elevated:rgba(255,255,255,0.85); --rd-border:rgba(10,30,80,0.06); --rd-border-strong:rgba(10,30,80,0.1); --rd-text-primary:#1c1c1e; --rd-text-secondary:#5f6368; --rd-text-tertiary:#98989e; --rd-accent:#0A84FF; --rd-accent-light:#5BC0BE; --rd-accent-warm:#FFB74D; --rd-accent-glow:rgba(10,132,255,0.28); --rd-accent-glow-soft:rgba(10,132,255,0.12); --rd-shadow-card:0 2px 12px rgba(10,30,80,0.04),0 1px 2px rgba(10,30,80,0.02); --rd-shadow-elevated:0 8px 32px rgba(10,30,80,0.08),0 2px 8px rgba(10,30,80,0.04); --rd-shadow-fab:0 12px 40px rgba(10,132,255,0.18),inset 0 0.5px 0 rgba(255,255,255,0.6); --rd-radius-sm:12px; --rd-radius-md:18px; --rd-radius-lg:24px; --rd-radius-xl:32px; --rd-radius-pill:9999px; --rd-blur:blur(25px) saturate(180%); --rd-blur-light:blur(20px) saturate(160%); --rd-blur-mini:blur(15px) saturate(150%); } :host *{box-sizing:border-box;pointer-events:auto;} .rd-aura{ position:fixed; top:0;left:0;right:0;bottom:0; pointer-events:none; z-index:2147483646; opacity:0.5; background: radial-gradient(ellipse 800px 600px at 20% 0%, rgba(10,132,255,0.15), transparent 60%), radial-gradient(ellipse 600px 500px at 80% 30%, rgba(91,192,190,0.12), transparent 60%), radial-gradient(ellipse 500px 400px at 50% 80%, rgba(255,183,77,0.08), transparent 60%); animation:rdAuraFloat 18s ease-in-out infinite; } @keyframes rdAuraFloat{ 0%,100%{transform:translate3d(0,0,0) scale(1);} 33%{transform:translate3d(-2%,1%,0) scale(1.02);} 66%{transform:translate3d(2%,-1%,0) scale(0.98);} } #rd-fab{ position:fixed; bottom:calc(120px + env(safe-area-inset-bottom, 0px)); right:20px; width:60px;height:60px; border-radius:50%; background:linear-gradient(135deg,rgba(10,132,255,0.95),rgba(91,192,190,0.95)); backdrop-filter:var(--rd-blur-mini); -webkit-backdrop-filter:var(--rd-blur-mini); border:0.5px solid rgba(255,255,255,0.4); box-shadow:var(--rd-shadow-fab); display:flex;align-items:center;justify-content:center; cursor:pointer; transition:transform 0.45s cubic-bezier(0.34,1.56,0.64,1),box-shadow 0.45s ease,opacity 0.45s ease; user-select:none; -webkit-user-select:none; will-change:transform; transform:translateZ(0); color:#fff; pointer-events:auto; touch-action:manipulation; z-index:10; } #rd-fab::before{ content:''; position:absolute; inset:0; border-radius:50%; background:radial-gradient(circle at 30% 30%,rgba(255,255,255,0.5),transparent 60%); opacity:0.7; pointer-events:none; } #rd-fab:hover{ transform:scale(1.08) translateZ(0); box-shadow:0 16px 48px rgba(10,132,255,0.28),inset 0 0.5px 0 rgba(255,255,255,0.7); } #rd-fab:active{transform:scale(0.92) translateZ(0);} #rd-fab svg{width:32px;height:32px;stroke:currentColor;stroke-width:1.7;position:relative;z-index:1;} :host(.rd-open) #rd-fab{ opacity:0; pointer-events:none; transform:scale(0.6) translateZ(0); } #rd-backdrop{ position:fixed; inset:0; background:rgba(10,15,30,0.28); backdrop-filter:blur(6px) saturate(140%); -webkit-backdrop-filter:blur(6px) saturate(140%); opacity:0; pointer-events:none; transition:opacity 0.45s ease; z-index:9; } #rd-backdrop.active{ opacity:1; pointer-events:auto; } #rd-panel{ position:fixed; bottom:0;left:0;right:0; top:24px; transform:translateY(calc(100% + 24px)); transition:transform 0.6s cubic-bezier(0.32,0.94,0.6,1); width:100%; height:calc(100vh - 24px); background:var(--rd-bg-primary); backdrop-filter:var(--rd-blur); -webkit-backdrop-filter:var(--rd-blur); border-top:0.5px solid var(--rd-border-strong); border-radius:32px 32px 0 0; box-shadow:0 -16px 60px rgba(10,30,80,0.12),inset 0 0.5px 0 rgba(255,255,255,0.4); display:flex;flex-direction:column; overflow:hidden; contain:layout; z-index:11; } #rd-panel::before{ content:''; position:absolute;top:8px;left:50%; transform:translateX(-50%); width:40px;height:4px; background:rgba(10,30,80,0.18); border-radius:2px; } #rd-panel.active{transform:translateY(0);} .rd-header{ display:flex; align-items:center; padding:20px 20px 12px 20px; flex-shrink:0; gap:12px; flex-wrap:nowrap; overflow:hidden; } .rd-header .rd-title-area{ flex:1; display:flex; flex-direction:column; min-width:0; gap:2px; } .rd-header h3{ margin:0; font-size:20px; font-weight:600; color:var(--rd-text-primary); display:flex; align-items:center; gap:10px; white-space:nowrap; flex-shrink:0; letter-spacing:-0.02em; } .rd-header h3 .rd-header-icon{ width:32px;height:32px; border-radius:10px; background:linear-gradient(135deg,var(--rd-accent),var(--rd-accent-light)); display:inline-flex;align-items:center;justify-content:center; color:#fff; box-shadow:0 4px 16px var(--rd-accent-glow); } .rd-header h3 .rd-header-icon svg{width:20px;height:20px;stroke:currentColor;stroke-width:1.8;} .rd-header .rd-page-url{ font-size:11px; color:var(--rd-text-tertiary); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:100%; } .rd-controls{display:flex;gap:8px;flex-shrink:0;margin-left:auto;align-items:center;} .rd-icon-btn{ width:40px;height:40px; border:none; background:var(--rd-bg-tertiary); color:var(--rd-text-primary); border-radius:50%; cursor:pointer; display:flex;align-items:center;justify-content:center; padding:0; transition:transform 0.25s cubic-bezier(0.34,1.56,0.64,1),background 0.25s ease,box-shadow 0.25s ease; backdrop-filter:var(--rd-blur-mini); -webkit-backdrop-filter:var(--rd-blur-mini); box-shadow:var(--rd-shadow-card); will-change:transform; transform:translateZ(0); pointer-events:auto; touch-action:manipulation; border:0.5px solid var(--rd-border); } .rd-icon-btn:hover{ background:var(--rd-bg-elevated); transform:scale(1.06) translateZ(0); box-shadow:var(--rd-shadow-elevated); } .rd-icon-btn:active{transform:scale(0.9) translateZ(0);} .rd-icon-btn svg{width:20px;height:20px;stroke:currentColor;stroke-width:1.7;} .rd-toolbar{padding:8px 20px 12px 20px;flex-shrink:0;} .rd-filters-scroll{ overflow-x:auto; scrollbar-width:none;-ms-overflow-style:none; -webkit-overflow-scrolling:touch; overscroll-behavior-x:none; transform:translateZ(0); padding:4px 0; } .rd-filters-scroll::-webkit-scrollbar{display:none;} .rd-filters{display:flex;gap:10px;padding:2px 0;} .rd-filter{ padding:9px 16px 9px 9px; background:var(--rd-bg-tertiary); border-radius:16px; cursor:pointer; color:var(--rd-text-secondary); transition:transform 0.3s cubic-bezier(0.34,1.56,0.64,1),background 0.3s ease,color 0.3s ease,box-shadow 0.3s ease,border-color 0.3s ease; border:0.5px solid var(--rd-border); display:inline-flex;align-items:center; gap:10px; white-space:nowrap; will-change:transform,background,box-shadow,color; transform:translateZ(0); backdrop-filter:var(--rd-blur-mini); -webkit-backdrop-filter:var(--rd-blur-mini); box-shadow:var(--rd-shadow-card); pointer-events:auto; font-family:inherit; } .rd-filter:hover{ color:var(--rd-text-primary); background:var(--rd-bg-elevated); transform:translateY(-1px) translateZ(0); box-shadow:var(--rd-shadow-elevated); } .rd-filter.active{ background:linear-gradient(135deg,var(--rd-accent),#4FC3F7); color:#fff; box-shadow:0 8px 28px var(--rd-accent-glow),inset 0 0.5px 0 rgba(255,255,255,0.3); transform:translateY(0) translateZ(0); border-color:transparent; } .rd-filter-icon{ width:36px;height:36px;flex-shrink:0; display:flex;align-items:center;justify-content:center; border-radius:12px; background:linear-gradient(135deg,rgba(10,132,255,0.12),rgba(91,192,190,0.12)); transition:background 0.3s ease; } .rd-filter.active .rd-filter-icon{background:rgba(255,255,255,0.22);} .rd-filter svg{width:22px;height:22px;display:block;flex-shrink:0;stroke:currentColor;stroke-width:1.7;} .rd-filter-text{ display:flex;flex-direction:column;align-items:flex-start;justify-content:center; gap:1px;line-height:1.15; } .rd-filter-count{ font-size:16px; font-weight:700; color:var(--rd-text-primary); line-height:1.1; font-variant-numeric:tabular-nums; transition:color 0.3s ease; } .rd-filter-name{ font-size:11px; font-weight:500; color:var(--rd-text-tertiary); line-height:1.2; transition:color 0.3s ease; } .rd-filter.active .rd-filter-count{color:#fff;} .rd-filter.active .rd-filter-name{color:rgba(255,255,255,0.85);} .rd-list{ flex:1; overflow-y:auto; padding:6px 20px calc(20px + env(safe-area-inset-bottom, 0px)) 20px; -webkit-overflow-scrolling:touch; overscroll-behavior:none; will-change:scroll-position; scrollbar-width:thin;scrollbar-color:var(--rd-border-strong) transparent; } .rd-list::-webkit-scrollbar{width:4px;} .rd-list::-webkit-scrollbar-track{background:transparent;} .rd-list::-webkit-scrollbar-thumb{background:var(--rd-border-strong);border-radius:8px;} .rd-list-content{display:flex;flex-direction:column;gap:10px;padding-bottom:6px;} .rd-list-content.rd-grid-view{display:grid;grid-template-columns:repeat(auto-fill,minmax(148px,1fr));gap:12px;} .rd-list-content.rd-grid-view .rd-entry{flex-direction:column;align-items:stretch;min-height:auto;padding:10px;gap:8px;} .rd-list-content.rd-grid-view .rd-entry-thumb-wrapper{width:100%;height:92px;flex-shrink:0;border-radius:14px;align-self:stretch;} .rd-list-content.rd-grid-view .rd-entry-thumb{border-radius:14px;} .rd-list-content.rd-grid-view .rd-entry-fallback svg{width:34px;height:34px;} .rd-list-content.rd-grid-view .rd-entry-content{gap:6px;} .rd-list-content.rd-grid-view .rd-entry-header{flex-direction:column;align-items:flex-start;height:auto;gap:6px;} .rd-list-content.rd-grid-view .rd-entry-filename{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;width:100%;flex:none;font-size:12px;} .rd-list-content.rd-grid-view .rd-entry-actions{margin-left:0;width:100%;justify-content:flex-end;} .rd-list-content.rd-grid-view .rd-action-btn{width:30px;height:30px;} .rd-list-content.rd-grid-view .rd-entry-url{-webkit-line-clamp:1;max-height:1.5em;} .rd-entry{ display:flex;gap:14px; padding:14px 16px; background:var(--rd-bg-elevated); border-radius:var(--rd-radius-lg); transition:transform 0.25s ease,background 0.25s ease,border-color 0.25s ease,box-shadow 0.25s ease; cursor:pointer; align-items:center; min-height:72px; contain:layout style; border:0.5px solid var(--rd-border); box-shadow:var(--rd-shadow-card); pointer-events:auto; position:relative; overflow:hidden; touch-action:manipulation; } .rd-entry::before{ content:''; position:absolute; top:0;left:0;right:0;height:50%; background:linear-gradient(180deg,rgba(255,255,255,0.3),transparent); border-radius:var(--rd-radius-lg) var(--rd-radius-lg) 0 0; pointer-events:none; opacity:0.6; } .rd-entry:hover{ transform:translateY(-2px); box-shadow:0 12px 36px rgba(10,30,80,0.08),0 2px 8px rgba(10,30,80,0.04); border-color:var(--rd-accent-glow); background:var(--rd-bg-elevated); } .rd-entry:active{transform:scale(0.98);} .rd-entry-thumb-wrapper{ width:52px;height:52px; flex-shrink:0; display:flex;align-items:center;justify-content:center; background:linear-gradient(135deg,rgba(255,255,255,0.5),rgba(255,255,255,0.3)); border-radius:18px; overflow:hidden; align-self:center; contain:layout style; box-shadow:inset 0 1px 4px rgba(10,30,80,0.04),0 1px 2px rgba(10,30,80,0.02); border:0.5px solid var(--rd-border); position:relative; z-index:1; } .rd-entry-thumb{ width:100%;height:100%; object-fit:cover; border-radius:18px; } .rd-entry-fallback{ display:flex;align-items:center;justify-content:center; color:var(--rd-accent); } .rd-entry-fallback svg{width:28px;height:28px;stroke:currentColor;stroke-width:1.6;} .rd-entry-content{ flex:1; min-width:0; display:flex;flex-direction:column; justify-content:center; gap:4px; padding:0; contain:layout; position:relative; z-index:1; } .rd-entry-header{ display:flex;align-items:center; gap:10px; height:24px; min-width:0; overflow:hidden; } .rd-entry-type{ color:#fff; padding:0 10px; border-radius:8px; font-size:10px; font-weight:600; text-transform:uppercase; letter-spacing:0.5px; height:20px; line-height:20px; display:inline-flex;align-items:center;justify-content:center; flex-shrink:0; box-shadow:0 2px 8px rgba(10,30,80,0.08); } ${Object.entries(TYPE_COLORS).map(([t,c])=>`.rd-entry-type.${t}{background:linear-gradient(135deg,${c},${c}dd)!important;}`).join('')} .rd-entry-filename{ font-size:13px; font-weight:500; color:var(--rd-text-primary); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; flex:1; min-width:0; } .rd-entry-actions{ display:flex;gap:4px; margin-left:auto; align-items:center; flex-shrink:0; } .rd-action-btn{ cursor:pointer; width:34px;height:34px; border-radius:50%; background:rgba(255,255,255,0.25); transition:transform 0.2s cubic-bezier(0.34,1.56,0.64,1),background 0.2s ease,color 0.2s ease,box-shadow 0.2s ease; border:0.5px solid rgba(255,255,255,0.18); display:flex;align-items:center;justify-content:center; padding:0; color:var(--rd-text-secondary); pointer-events:auto; touch-action:manipulation; } .rd-action-btn:hover{ background:rgba(255,255,255,0.6); transform:scale(1.08); color:var(--rd-text-primary); box-shadow:0 4px 12px rgba(10,30,80,0.06); } .rd-action-btn:active{transform:scale(0.88);} .rd-action-btn svg{width:16px;height:16px;stroke:currentColor;stroke-width:1.7;pointer-events:none;} .rd-download-btn{color:var(--rd-accent);} .rd-download-btn:hover{color:#0070DD;background:rgba(10,132,255,0.1);} .rd-play-btn{color:#FF6B6B;} .rd-play-btn:hover{background:rgba(255,107,107,0.1);color:#FF5252;} .rd-copy-btn{color:var(--rd-text-secondary);} .rd-copy-btn:hover{background:rgba(0,0,0,0.04);color:var(--rd-text-primary);} .rd-entry-url{ word-break:break-all; color:var(--rd-text-tertiary); font-family:'HarmonyOS Sans Mono','SF Mono',Monaco,Consolas,monospace; font-size:10.5px; line-height:1.5; max-height:3em; overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; contain:layout; opacity:0.75; transition:opacity 0.3s; } .rd-entry:hover .rd-entry-url{opacity:1;} .rd-empty{ text-align:center; color:var(--rd-text-secondary); padding:80px 20px; font-size:14px; display:flex;flex-direction:column;align-items:center; gap:20px; contain:layout; } .rd-empty-icon{ width:80px;height:80px; border-radius:24px; background:linear-gradient(135deg,rgba(10,132,255,0.08),rgba(91,192,190,0.08)); display:flex;align-items:center;justify-content:center; color:var(--rd-accent); margin-bottom:8px; } .rd-empty-icon svg{width:44px;height:44px;stroke:currentColor;stroke-width:1.5;} #rd-toast{ position:fixed; top:50%;left:50%; transform:translate(-50%,-50%) scale(0.94); background:rgba(20,28,40,0.9); color:#f5f5f7; padding:14px 24px; border-radius:var(--rd-radius-pill); font-size:14px; font-weight:500; z-index:2147483647; pointer-events:none; opacity:0; transition:opacity 0.35s ease,transform 0.35s ease; box-shadow:0 16px 48px rgba(0,0,0,0.24),inset 0 0.5px 0 rgba(255,255,255,0.1); border:0.5px solid rgba(255,255,255,0.08); display:flex;align-items:center;gap:8px; } #rd-toast.show{ opacity:1; transform:translate(-50%,-50%) scale(1); } .rd-toast-icon{width:18px;height:18px;color:#5BC0BE;stroke-width:2.2;} #rd-inline-player,#rd-img-preview,#rd-text-preview,#rd-source-preview{ position:fixed; z-index:2147483647; inset:0;margin:auto; pointer-events:auto; animation:rdFadeIn 0.4s ease; border:0.5px solid var(--rd-border-strong); border-radius:var(--rd-radius-xl); box-shadow:0 40px 100px rgba(10,30,80,0.16),0 8px 32px rgba(10,30,80,0.06); } @keyframes rdFadeIn{ 0%{opacity:0;} 100%{opacity:1;} } #rd-inline-player{ width:92%;max-width:820px; height:84vh;max-height:84vh; background:var(--rd-bg-primary); overflow:hidden; display:flex;flex-direction:column; } #rd-player-header,#rd-img-preview-header,#rd-text-preview-header,#rd-source-preview-header{ display:flex;align-items:center;justify-content:space-between; padding:18px 20px; background:transparent; flex-shrink:0;height:auto; contain:layout; } #rd-player-title,#rd-img-preview-title,#rd-text-preview-title,#rd-source-preview-title{ color:var(--rd-text-primary); font-size:17px; font-weight:600; display:flex;align-items:center; gap:10px; letter-spacing:-0.01em; } #rd-player-title svg,#rd-img-preview-title svg,#rd-text-preview-title svg,#rd-source-preview-title svg{ width:22px;height:22px; stroke:currentColor;stroke-width:1.6; color:var(--rd-accent); } #rd-media-wrapper{ flex:1; display:flex;align-items:center;justify-content:center; background:linear-gradient(180deg,var(--rd-bg-secondary),var(--rd-bg-primary)); overflow:hidden; min-height:0; position:relative; width:100%; padding:4px 20px 20px 20px; } #rd-video{ width:100%;height:100%; max-width:100%;max-height:100%; object-fit:contain; display:block; background:#000; transform:translateZ(0); -webkit-transform:translateZ(0); will-change:transform; backface-visibility:hidden; -webkit-backface-visibility:hidden; } #rd-video-wrapper{ position:relative; width:100%;height:100%; display:flex;align-items:center;justify-content:center; border-radius:var(--rd-radius-md); overflow:hidden; contain:layout style paint; } .rd-video-loading{ position:absolute; top:50%;left:50%; transform:translate(-50%,-50%); width:48px;height:48px; border:3px solid rgba(255,255,255,0.15); border-top-color:#0A84FF; border-radius:50%; animation:rdSpin 0.8s linear infinite; pointer-events:none; z-index:2; } @keyframes rdSpin{to{transform:translate(-50%,-50%) rotate(360deg);}} .rd-video-controls{ position:absolute; bottom:0;left:0;right:0; display:flex;flex-direction:column; gap:6px; padding:10px 14px 24px; background:linear-gradient(0deg,rgba(0,0,0,0.7),transparent); opacity:0; transition:opacity 0.3s ease; z-index:3; } #rd-video-wrapper.rd-show-controls .rd-video-controls{opacity:1;} .rd-video-progress-bar{ width:100%; display:flex;align-items:center; gap:10px; } .rd-video-btn-row{ display:flex;align-items:center; gap:8px; } .rd-video-btn{ width:34px;height:34px; border:none; background:rgba(255,255,255,0.12); color:#fff; border-radius:8px; cursor:pointer; display:flex;align-items:center;justify-content:center; padding:0; flex-shrink:0; transition:background 0.2s ease,transform 0.15s ease; backdrop-filter:blur(4px); -webkit-backdrop-filter:blur(4px); } .rd-video-btn:hover{background:rgba(255,255,255,0.25);} .rd-video-btn:active{transform:scale(0.9);} .rd-video-btn.active{background:var(--rd-accent);} .rd-video-btn svg{width:18px;height:18px;stroke:currentColor;stroke-width:1.7;pointer-events:none;} .rd-video-progress{ flex:1;min-width:0; height:24px; display:flex;align-items:center; cursor:pointer; position:relative; } .rd-video-progress-track{ width:100%;height:4px; background:rgba(255,255,255,0.2); border-radius:4px; position:relative; overflow:hidden; transition:height 0.2s ease; } .rd-video-progress:hover .rd-video-progress-track{height:6px;} .rd-video-progress-filled{ position:absolute;left:0;top:0;height:100%; background:linear-gradient(90deg,#0A84FF,#5BC0BE); border-radius:4px; pointer-events:none; } .rd-video-progress-thumb{ position:absolute;top:50%; width:12px;height:12px; background:#fff; border-radius:50%; transform:translate(-50%,-50%); pointer-events:none; box-shadow:0 0 0 3px rgba(10,132,255,0.4); opacity:0; transition:opacity 0.2s ease; } .rd-video-progress:hover .rd-video-progress-thumb{opacity:1;} .rd-video-time{ font-size:12px; font-family:'HarmonyOS Sans Mono','SF Mono',Monaco,Consolas,monospace; color:rgba(255,255,255,0.9); flex-shrink:0; font-weight:500; user-select:none; -webkit-user-select:none; } .rd-video-speed-menu{ position:absolute; bottom:62px; right:14px; display:none; flex-direction:column; gap:2px; padding:6px; background:rgba(20,28,40,0.92); border-radius:12px; box-shadow:0 8px 32px rgba(0,0,0,0.3); backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px); border:0.5px solid rgba(255,255,255,0.08); z-index:4; } .rd-video-speed-menu.rd-open{display:flex;} .rd-video-speed-item{ padding:7px 16px; border-radius:8px; font-size:13px; font-weight:500; color:rgba(255,255,255,0.7); cursor:pointer; text-align:center; transition:background 0.15s ease,color 0.15s ease; user-select:none; -webkit-user-select:none; } .rd-video-speed-item:hover{background:rgba(255,255,255,0.08);color:#fff;} .rd-video-speed-item.active{background:var(--rd-accent);color:#fff;} .rd-video-center-btn{ position:absolute; top:50%;left:50%; transform:translate(-50%,-50%); width:64px;height:64px; border-radius:50%; background:rgba(0,0,0,0.5); border:none; color:#fff; display:flex;align-items:center;justify-content:center; cursor:pointer; z-index:2; opacity:0; transition:opacity 0.25s ease; backdrop-filter:blur(8px); -webkit-backdrop-filter:blur(8px); pointer-events:none; } #rd-video-wrapper.rd-show-center .rd-video-center-btn{opacity:1;pointer-events:auto;} .rd-video-center-btn svg{width:28px;height:28px;stroke:currentColor;stroke-width:1.8;fill:currentColor;} #rd-video-wrapper:fullscreen,#rd-video-wrapper:-webkit-full-screen{ border-radius:0; background:#000; } #rd-video-wrapper:fullscreen #rd-video,#rd-video-wrapper:-webkit-full-screen #rd-video{ border-radius:0; } #rd-inline-player:fullscreen,#rd-inline-player:-webkit-full-screen{ width:100%;max-width:100%; height:100%;max-height:100%; border-radius:0; } .rd-custom-audio{ width:100%; padding:28px 32px; display:flex;flex-direction:column; gap:20px; background:var(--rd-bg-secondary); border-radius:var(--rd-radius-lg); margin:8px 0; backdrop-filter:var(--rd-blur-mini); -webkit-backdrop-filter:var(--rd-blur-mini); border:0.5px solid var(--rd-border); box-shadow:var(--rd-shadow-card); position:relative; overflow:hidden; } .rd-custom-audio::before{ content:''; position:absolute; top:0;left:0;right:0;height:50%; background:linear-gradient(180deg,rgba(255,255,255,0.4),transparent); border-radius:var(--rd-radius-lg) var(--rd-radius-lg) 0 0; pointer-events:none; } .rd-audio-info{ display:flex;align-items:center; gap:14px; color:var(--rd-text-primary); font-size:14px; font-weight:500; word-break:break-all; position:relative; } .rd-audio-info-icon{ width:44px;height:44px; border-radius:14px; background:linear-gradient(135deg,var(--rd-accent),var(--rd-accent-light)); display:flex;align-items:center;justify-content:center; color:#fff; box-shadow:0 4px 16px var(--rd-accent-glow); } .rd-audio-info-icon svg{width:24px;height:24px;stroke:currentColor;stroke-width:1.6;} .rd-audio-filename{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;} .rd-audio-controls{display:flex;align-items:center;gap:14px;flex-wrap:wrap;position:relative;} .rd-play-pause-btn{ width:48px;height:48px; border-radius:50%; background:linear-gradient(135deg,var(--rd-accent),#4FC3F7); border:none; display:flex;align-items:center;justify-content:center; cursor:pointer; color:#fff; transition:all 0.3s cubic-bezier(0.34,1.56,0.64,1); flex-shrink:0; box-shadow:0 8px 24px var(--rd-accent-glow); pointer-events:auto; } .rd-play-pause-btn:hover{transform:scale(1.06);box-shadow:0 12px 36px var(--rd-accent-glow);} .rd-play-pause-btn:active{transform:scale(0.9);} .rd-play-pause-btn svg{width:22px;height:22px;stroke:currentColor;stroke-width:1.8;fill:currentColor;} .rd-progress-container{ flex:1; position:relative;height:32px; display:flex;align-items:center; cursor:pointer; pointer-events:auto; } .rd-progress-bg{ width:100%;height:4px; background:rgba(10,30,80,0.08); border-radius:4px; position:relative; overflow:hidden; } .rd-progress-filled{ position:absolute;left:0;top:0;height:100%;width:0; background:linear-gradient(90deg,var(--rd-accent),var(--rd-accent-light)); border-radius:4px; pointer-events:none; transition:width 0.08s; } .rd-progress-thumb{ position:absolute;top:50%; width:14px;height:14px; background:#fff; border-radius:50%; transform:translate(-50%,-50%); pointer-events:none; box-shadow:0 2px 8px var(--rd-accent-glow),0 0 0 2px var(--rd-accent); transition:box-shadow 0.2s,transform 0.2s; } .rd-progress-container:hover .rd-progress-thumb{ transform:translate(-50%,-50%) scale(1.15); box-shadow:0 4px 16px var(--rd-accent-glow),0 0 0 2px var(--rd-accent); } .rd-time{ font-size:12px; font-family:'HarmonyOS Sans Mono','SF Mono',Monaco,Consolas,monospace; color:var(--rd-text-secondary); display:flex;gap:4px; flex-shrink:0; font-weight:500; } #rd-img-preview{ width:92%;max-width:820px; height:84vh;max-height:84vh; background:var(--rd-bg-primary); display:flex;flex-direction:column; overflow:hidden; } #rd-img-preview-content{ flex:1; display:flex;align-items:center;justify-content:center; overflow:hidden; padding:20px; background:transparent; cursor:zoom-out; } #rd-preview-img{ max-width:100%;max-height:100%; width:auto;height:auto; object-fit:contain; border-radius:18px; cursor:default; box-shadow:0 8px 40px rgba(10,30,80,0.08); } #rd-text-preview,#rd-source-preview{ width:92%;max-width:820px; height:84vh; background:var(--rd-bg-primary); display:flex;flex-direction:column; overflow:hidden; } #rd-text-preview-scroll,#rd-source-preview-scroll{ flex:1; overflow:auto; padding:20px; background:var(--rd-bg-primary); -webkit-overflow-scrolling:touch; overscroll-behavior:none; will-change:scroll-position; } #rd-text-preview-scroll::-webkit-scrollbar,#rd-source-preview-scroll::-webkit-scrollbar{width:4px;} #rd-text-preview-scroll::-webkit-scrollbar-track,#rd-source-preview-scroll::-webkit-scrollbar-track{background:transparent;} #rd-text-preview-scroll::-webkit-scrollbar-thumb,#rd-source-preview-scroll::-webkit-scrollbar-thumb{background:var(--rd-border-strong);border-radius:8px;} #rd-text-preview-content,#rd-source-preview-content{ margin:0!important; padding:16px!important; border-radius:14px!important; min-height:calc(100% - 1px); box-sizing:border-box; display:block; } .rd-iframe-container{ flex:1; position:relative; background:transparent; overflow:hidden; border-radius:0 0 var(--rd-radius-xl) var(--rd-radius-xl); } .rd-iframe-container iframe{ width:100%;height:100%; border:none; background:transparent; } *{outline:none!important;-webkit-tap-highlight-color:transparent!important;} :host(.rd-scrolling) .rd-aura{animation-play-state:paused!important;} :host(.rd-scrolling) #rd-panel{backdrop-filter:none!important;-webkit-backdrop-filter:none!important;} :host(.rd-scrolling) .rd-list{background:var(--rd-bg-primary);} :host(.rd-scrolling) #rd-text-preview,:host(.rd-scrolling) #rd-source-preview{backdrop-filter:none!important;-webkit-backdrop-filter:none!important;} :host(.rd-scrolling) #rd-text-preview-scroll,:host(.rd-scrolling) #rd-source-preview-scroll{background:var(--rd-bg-primary);} @media (prefers-color-scheme: dark){ :host{ --rd-bg-primary:rgba(20,24,32,0.82); --rd-bg-secondary:rgba(36,40,50,0.65); --rd-bg-tertiary:rgba(255,255,255,0.04); --rd-bg-elevated:rgba(48,52,64,0.85); --rd-border:rgba(255,255,255,0.05); --rd-border-strong:rgba(255,255,255,0.08); --rd-text-primary:#f5f5f7; --rd-text-secondary:#a8acb3; --rd-text-tertiary:#6c6c70; --rd-accent:#0A84FF; --rd-accent-light:#5BC0BE; --rd-accent-glow:rgba(10,132,255,0.32); --rd-accent-glow-soft:rgba(10,132,255,0.16); --rd-shadow-card:0 2px 12px rgba(0,0,0,0.2),0 1px 2px rgba(0,0,0,0.1); --rd-shadow-elevated:0 8px 32px rgba(0,0,0,0.3),0 2px 8px rgba(0,0,0,0.15); --rd-shadow-fab:0 12px 40px rgba(10,132,255,0.32),inset 0 0.5px 0 rgba(255,255,255,0.1); } #rd-fab{ background:linear-gradient(135deg,rgba(10,132,255,0.92),rgba(91,192,190,0.92)); border-color:rgba(255,255,255,0.12); } #rd-panel{ background:var(--rd-bg-primary); border-top-color:rgba(255,255,255,0.06); box-shadow:0 -16px 60px rgba(0,0,0,0.4),inset 0 0.5px 0 rgba(255,255,255,0.06); } #rd-panel::before{background:rgba(255,255,255,0.18);} .rd-header h3{color:var(--rd-text-primary);} .rd-icon-btn{background:rgba(255,255,255,0.05);border-color:rgba(255,255,255,0.04);color:var(--rd-text-primary);} .rd-icon-btn:hover{background:rgba(255,255,255,0.1);} .rd-filter{background:rgba(255,255,255,0.04);border-color:rgba(255,255,255,0.04);color:var(--rd-text-secondary);} .rd-filter:hover{background:rgba(255,255,255,0.08);color:var(--rd-text-primary);} .rd-filter.active{background:linear-gradient(135deg,var(--rd-accent),#4FC3F7);color:#fff;} .rd-entry{background:var(--rd-bg-elevated);border-color:rgba(255,255,255,0.05);} .rd-entry:hover{background:rgba(255,255,255,0.1);border-color:rgba(10,132,255,0.2);} .rd-entry::before{background:linear-gradient(180deg,rgba(255,255,255,0.04),transparent);} .rd-entry-thumb-wrapper{background:linear-gradient(135deg,rgba(255,255,255,0.06),rgba(255,255,255,0.02));border-color:rgba(255,255,255,0.05);} .rd-action-btn{background:rgba(255,255,255,0.04);border-color:rgba(255,255,255,0.04);} .rd-action-btn:hover{background:rgba(255,255,255,0.08);} .rd-custom-audio{background:rgba(255,255,255,0.04);border-color:rgba(255,255,255,0.05);} .rd-custom-audio::before{background:linear-gradient(180deg,rgba(255,255,255,0.04),transparent);} .rd-progress-bg{background:rgba(255,255,255,0.08);} .rd-entry-url{color:var(--rd-text-tertiary);} .rd-empty{color:var(--rd-text-secondary);} .rd-empty-icon{background:linear-gradient(135deg,rgba(10,132,255,0.12),rgba(91,192,190,0.12));} } `; this._shadowRoot.appendChild(style); }, getFilterIcon(type){ const map={image:Icons.image, media:Icons.waveform, other:Icons.file}; return map[type]||Icons.file; }, getFilterName(type){ const names={image:'图片', media:'媒体', other:'其他'}; return names[type]||type; }, createUI(){ if(!this._shadowRoot)return; const c=document.createElement('div');c.className='rd-container'; c.innerHTML=`
${Icons.box}

${Icons.box} 资源日记

${SecurityUtils.sanitizeAttr(location.href)}
${['image','media','other'].map(f=>``).join('')}
`; this._shadowRoot.appendChild(c); this._elements={ floatingBtn:this._shadowRoot.getElementById('rd-fab'), backdrop:this._shadowRoot.getElementById('rd-backdrop'), panel:this._shadowRoot.getElementById('rd-panel'), list:this._shadowRoot.getElementById('rd-list'), listContent:this._shadowRoot.querySelector('.rd-list-content'), closeBtn:this._shadowRoot.getElementById('rd-close'), viewSourceBtn:this._shadowRoot.getElementById('rd-view-source'), viewModeBtn:this._shadowRoot.getElementById('rd-view-mode'), sortBtn:this._shadowRoot.getElementById('rd-sort') }; this.updateViewModeBtn(); this.updateSortBtn(); }, bindEvents(){ if(!this._shadowRoot)return; const root=this._shadowRoot; root.addEventListener('click',e=>{ const t=e.target; if(t.closest('#rd-close')){this.forceHidePanel();return;} if(t.closest('#rd-view-source')){this.viewSource();return;} if(t.closest('#rd-view-mode')){this.toggleViewMode();return;} if(t.closest('#rd-sort')){this.toggleSort();return;} const filter=t.closest('.rd-filter');if(filter){this.setFilter(filter.dataset.filter);return;} const action=t.closest('.rd-action-btn');if(action){e.stopPropagation();const url=action.dataset.url;if(!url||!SecurityUtils.isSafeUrl(url))return;if(action.classList.contains('rd-download-btn'))this.downloadResource(url);else if(action.classList.contains('rd-play-btn'))this.playMedia(url,action.dataset.displayType);else if(action.classList.contains('rd-copy-btn'))this.copyToClipboard(url);return;} const thumb=t.closest('.rd-entry-thumb-wrapper');if(thumb){e.stopPropagation();const url=thumb.dataset.url;if(url&&SecurityUtils.isSafeUrl(url))this.previewImage(url);return;} const entry=t.closest('.rd-entry');if(entry&&!t.closest('.rd-entry-actions')){const url=entry.dataset.url;const displayType=entry.dataset.displayType;const filterType=this.getFilterType(displayType);if(!url||!SecurityUtils.isSafeUrl(url))return;if(filterType==='media')this.playMedia(url,displayType);else if(filterType==='image')this.previewImage(url);else this.previewTextContent(url);} }); const fab=this._elements.floatingBtn; if(fab){ fab.addEventListener('click', e => { e.stopPropagation(); this.togglePanel(); }); } const bd=this._elements.backdrop;if(bd)bd.addEventListener('click',e=>{e.stopPropagation();this.hidePanel();}); const closeBtn=this._elements.closeBtn;if(closeBtn)closeBtn.addEventListener('click',e=>{e.stopPropagation();this.forceHidePanel();}); this.scheduleUpdate(); const list=this._elements.list;if(list){let sx=0,sy=0,sw=false,st=0;list.addEventListener('touchstart',e=>{sx=e.changedTouches[0].screenX;sy=e.changedTouches[0].screenY;st=Date.now();sw=false;},{passive:true});list.addEventListener('touchmove',e=>{if(!sx)return;const x=e.changedTouches[0].screenX,y=e.changedTouches[0].screenY;const dx=Math.abs(sx-x),dy=Math.abs(sy-y);if(dx>dy&&dx>10)sw=true;},{passive:true});list.addEventListener('touchend',e=>{if(!sw)return;const dx=sx-e.changedTouches[0].screenX;const dt=Date.now()-st;if(Math.abs(dx)>50&&dt<300){const filters=['image','media','other'];const ci=filters.indexOf(this.currentFilter);const ni=dx>0?Math.min(ci+1,filters.length-1):Math.max(ci-1,0);if(ni!==ci){this.setFilter(filters[ni]);setTimeout(()=>this.scrollFilterToActive(),50);}}sx=0;sw=false;},{passive:true});this._bindScrollOptimization(list);} }, _bindScrollOptimization(el){ if(!el||el._rdScrollBound)return;el._rdScrollBound=true; let t;el.addEventListener('scroll',()=>{this._host.classList.add('rd-scrolling');clearTimeout(t);t=setTimeout(()=>{this._host.classList.remove('rd-scrolling');},150);},{passive:true}); }, forceHidePanel(){ const p=this._elements.panel; if(p)p.classList.remove('active'); const bd=this._elements.backdrop; if(bd)bd.classList.remove('active'); if(this._host)this._host.classList.remove('rd-open'); this.isPanelOpen=false; this._unlockBodyScroll(); }, setupKeyboardShortcuts(){ const handler=e=>{ if(document.hidden)return; if(this._lastVisibleTime&&Date.now()-this._lastVisibleTime<800)return; if(e.key==='Escape'){ if(this._shadowRoot){ const hadOverlay=!!(this._shadowRoot.getElementById('rd-inline-player')||this._shadowRoot.getElementById('rd-img-preview')||this._shadowRoot.getElementById('rd-text-preview')||this._shadowRoot.getElementById('rd-source-preview')); if(hadOverlay){try{const v=this._shadowRoot.querySelector('#rd-inline-player video')||this._shadowRoot.querySelector('#rd-inline-player audio');if(v){if(v._hls){try{v._hls.destroy();}catch{}}v.pause();}}catch{}} this._shadowRoot.getElementById('rd-inline-player')?.remove(); this._shadowRoot.getElementById('rd-img-preview')?.remove(); this._shadowRoot.getElementById('rd-text-preview')?.remove(); this._shadowRoot.getElementById('rd-source-preview')?.remove(); if(hadOverlay){ this._unlockBodyScroll(); return; } } if(this.isPanelOpen)this.hidePanel(); } }; document.addEventListener('keydown',handler); this._listeners.push(()=>document.removeEventListener('keydown',handler)); }, setFilter(filter){ if(this.currentFilter===filter){ this.currentFilter=null; }else{ this.currentFilter=filter; } this.updateFilterButtons(); this.scrollFilterToActive(); if(this.currentFilter==='media'||this.currentFilter==='image')this.scanExistingResources(); this.scheduleUpdate(); }, toggleViewMode(){ this.viewMode=this.viewMode==='list'?'grid':'list'; this.updateViewModeBtn(); this.applyViewMode(); }, updateViewModeBtn(){ const btn=this._elements.viewModeBtn;if(!btn)return; btn.innerHTML=this.viewMode==='list'?Icons.list:Icons.grid; btn.title=this.viewMode==='list'?'列表视图(点击切换网格)':'网格视图(点击切换列表)'; }, applyViewMode(){ const c=this._elements.listContent;if(c)c.classList.toggle('rd-grid-view',this.viewMode==='grid'); }, toggleSort(){ const modes=['time-desc','name-asc','name-desc','time-asc']; const idx=modes.indexOf(this.sortMode); this.sortMode=modes[(idx+1)%modes.length]; this.updateSortBtn(); this._renderSignature=''; this.renderList(); this.showToast(this.getSortInfo(this.sortMode).title); }, updateSortBtn(){ const btn=this._elements.sortBtn;if(!btn)return; const info=this.getSortInfo(this.sortMode); btn.innerHTML=info.icon; btn.title=info.title; }, getSortInfo(mode){ const map={ 'time-desc':{title:'排序:最新优先',icon:Icons.sortDesc}, 'time-asc':{title:'排序:最旧优先',icon:Icons.sortAsc}, 'name-asc':{title:'排序:名称 A→Z',icon:Icons.sortAsc}, 'name-desc':{title:'排序:名称 Z→A',icon:Icons.sortDesc} }; return map[mode]||map['time-desc']; }, sortResources(arr){ const m=this.sortMode; arr.sort((a,b)=>{ switch(m){ case 'time-desc':return (b.timestamp||0)-(a.timestamp||0); case 'time-asc':return (a.timestamp||0)-(b.timestamp||0); case 'name-asc':{const x=(a.filename||'').toLowerCase(),y=(b.filename||'').toLowerCase();return xy?1:0;} case 'name-desc':{const x=(a.filename||'').toLowerCase(),y=(b.filename||'').toLowerCase();return xy?-1:0;} default:return 0; } }); return arr; }, _lockDepth:0, _savedBodyOverflow:'', _savedHtmlOverflow:'', _savedBodyPaddingRight:'', _lockBodyScroll(){ this._lockDepth++; if(this._lockDepth>1)return; const body=document.body; const html=document.documentElement; this._savedBodyOverflow=body.style.overflow; this._savedHtmlOverflow=html.style.overflow; this._savedBodyPaddingRight=body.style.paddingRight; const sw=window.innerWidth-document.documentElement.clientWidth; if(sw>0)body.style.paddingRight=sw+'px'; body.style.overflow='hidden'; html.style.overflow='hidden'; }, _unlockBodyScroll(){ if(this._lockDepth>0)this._lockDepth--; if(this._lockDepth>0)return; const body=document.body; const html=document.documentElement; body.style.overflow=this._savedBodyOverflow; html.style.overflow=this._savedHtmlOverflow; body.style.paddingRight=this._savedBodyPaddingRight; }, togglePanel(){ const p=this._elements.panel;if(!p)return; if(p.classList.contains('active')){this.hidePanel();}else{this._openPanel();} }, _openPanel(){ const p=this._elements.panel;if(!p)return; p.classList.add('active');this._elements.backdrop?.classList.add('active');this._host?.classList.add('rd-open');this.isPanelOpen=true;this._lockBodyScroll();this.manualRefresh();this.renderList();this.scrollFilterToActive(); }, hidePanel(){ const p=this._elements.panel;if(p)p.classList.remove('active');this._elements.backdrop?.classList.remove('active');this._host?.classList.remove('rd-open');this._host?.classList.remove('rd-scrolling');this.isPanelOpen=false; this._unlockBodyScroll(); if(this._shadowRoot){setTimeout(()=>{const ev=this._shadowRoot.querySelector('#rd-inline-player video');if(ev&&ev._hls){try{ev._hls.destroy();}catch{}}this._shadowRoot.getElementById('rd-inline-player')?.remove();this._shadowRoot.getElementById('rd-img-preview')?.remove();this._shadowRoot.getElementById('rd-text-preview')?.remove();this._shadowRoot.getElementById('rd-source-preview')?.remove();},300);} }, _ensureHostAttached(){ if(this.isDestroyed)return; if(this._host&&this._host.isConnected)return; const wasOpen=this.isPanelOpen; this.setupUI(); this.updateFilterButtons(); if(wasOpen)this._openPanel(); }, setupVisibilityHandler(){ const self=this; let wasOpen=false,wasHidden=false; const getVideo=()=>{try{return self._shadowRoot&&self._shadowRoot.querySelector('#rd-inline-player video');}catch{return null;}}; const resumeVideo=v=>{if(!v)return;if(v._hls){try{v._hls.startLoad();}catch{}}if(v._rdWasPlaying){v._rdWasPlaying=false;try{v.play().catch(()=>{});}catch{}}}; const onVis=()=>{ const v=getVideo(); if(document.hidden){ wasHidden=true;wasOpen=self.isPanelOpen; if(v&&!v.paused){v._rdWasPlaying=true;try{v.pause();}catch{}} }else{ self._lastVisibleTime=Date.now(); self._ensureHostAttached(); if(wasHidden){ wasHidden=false; if(wasOpen&&!self.isPanelOpen)self._openPanel(); wasOpen=false; resumeVideo(getVideo()); self.scanExistingResources(); } } }; const onPageShow=e=>{if(e&&e.persisted){self._lastVisibleTime=Date.now();self._ensureHostAttached();resumeVideo(getVideo());}}; document.addEventListener('visibilitychange',onVis); window.addEventListener('pageshow',onPageShow); this._listeners.push(()=>{document.removeEventListener('visibilitychange',onVis);window.removeEventListener('pageshow',onPageShow);}); }, previewImage(url){ if(!SecurityUtils.isSafeUrl(url)){this.showToast('不安全的图片地址');return;} if(!this._shadowRoot)return; this._shadowRoot.getElementById('rd-img-preview')?.remove(); const preview=SecurityUtils.createElement('div',{id:'rd-img-preview',style:{borderRadius:'32px'}}); const header=SecurityUtils.createElement('div',{id:'rd-img-preview-header'}); const title=SecurityUtils.createElement('span',{id:'rd-img-preview-title'});title.innerHTML=Icons.image+' 图片预览'; const close=SecurityUtils.createElement('button',{className:'rd-icon-btn',id:'rd-close-preview',title:'关闭'});close.innerHTML=Icons.close; header.appendChild(title);header.appendChild(close); const content=SecurityUtils.createElement('div',{id:'rd-img-preview-content'}); const img=document.createElement('img');img.id='rd-preview-img';img.src=url;img.alt='预览'; img.addEventListener('error',()=>{ content.innerHTML=`
${Icons.image}
图片加载失败
该图片可能已被删除或需要登录访问
`; }); content.appendChild(img); preview.appendChild(header);preview.appendChild(content); const closeHandler=()=>{preview.remove();this._unlockBodyScroll();}; close.addEventListener('click',closeHandler); preview.addEventListener('click',e=>{if(e.target===preview||e.target.id==='rd-img-preview-content'||e.target.id==='rd-img-preview-header')closeHandler();}); this._shadowRoot.appendChild(preview); this._lockBodyScroll(); }, async previewTextContent(url){ if(!SecurityUtils.isSafeUrl(url)){this.showToast('不安全的资源地址');return;} if(!this._shadowRoot)return; this.showToast('加载中...'); const ctrl=new AbortController();const tid=setTimeout(()=>ctrl.abort(),CONFIG.FETCH_TIMEOUT); try{ const resp=await fetch(url,{credentials:'omit',signal:ctrl.signal,headers:{'Accept':'text/plain,text/html,application/json'}}); clearTimeout(tid); const ct=resp.headers.get('content-type')||''; if(!/text\/|json|xml|javascript/.test(ct)){this.showIframePreview(url);return;} const text=await resp.text();const display=text; this._shadowRoot.getElementById('rd-text-preview')?.remove(); const preview=SecurityUtils.createElement('div',{id:'rd-text-preview',style:{borderRadius:'32px'}}); const header=SecurityUtils.createElement('div',{id:'rd-text-preview-header'}); const title=SecurityUtils.createElement('span',{id:'rd-text-preview-title'});title.innerHTML=Icons.file+' 内容预览'; const close=SecurityUtils.createElement('button',{className:'rd-icon-btn',id:'rd-text-preview-close',title:'关闭'});close.innerHTML=Icons.close; header.appendChild(title);header.appendChild(close); preview.appendChild(header); const scroll=SecurityUtils.createElement('div',{id:'rd-text-preview-scroll'}); const pre=document.createElement('pre');pre.id='rd-text-preview-content';pre.textContent=display; pre.style.whiteSpace='pre-wrap'; pre.style.wordBreak='break-word'; pre.style.background='#1e1e2e'; pre.style.color='#e0e0e0'; scroll.appendChild(pre);preview.appendChild(scroll); this._bindScrollOptimization(scroll); const handler=()=>{preview.remove();this._unlockBodyScroll();}; close.addEventListener('click',handler); preview.addEventListener('click',e=>{if(e.target===preview||e.target.id==='rd-text-preview-scroll')handler();}); this._shadowRoot.appendChild(preview); this._lockBodyScroll(); const lang=this._detectLanguageFromUrl(url)||this._detectLanguageFromContentType(ct); if(lang)this._applyHighlight(pre,lang); else if(ct.includes('html')||ct.includes('xml'))this._applyHighlight(pre,'html'); else this._applyHighlight(pre,'plaintext'); }catch(e){this.showIframePreview(url);} }, showIframePreview(url){ if(!this._shadowRoot)return; this._shadowRoot.getElementById('rd-text-preview')?.remove(); const preview=SecurityUtils.createElement('div',{id:'rd-text-preview',style:{display:'flex',flexDirection:'column',borderRadius:'32px'}}); const header=SecurityUtils.createElement('div',{id:'rd-text-preview-header'}); const title=SecurityUtils.createElement('span',{id:'rd-text-preview-title'});title.innerHTML=Icons.file+' 内容预览'; const controls=SecurityUtils.createElement('div',{style:{display:'flex',gap:'8px',alignItems:'center'}}); const open=SecurityUtils.createElement('button',{className:'rd-icon-btn',title:'新窗口打开'});open.innerHTML=Icons.link; const close=SecurityUtils.createElement('button',{className:'rd-icon-btn',id:'rd-text-preview-close',title:'关闭'});close.innerHTML=Icons.close; controls.appendChild(open);controls.appendChild(close); header.appendChild(title);header.appendChild(controls); const container=SecurityUtils.createElement('div',{className:'rd-iframe-container'}); const iframe=SecurityUtils.createElement('iframe',{src:url,sandbox:'allow-same-origin allow-scripts allow-popups allow-downloads'}); container.appendChild(iframe); preview.appendChild(header);preview.appendChild(container); const handler=()=>{preview.remove();this._unlockBodyScroll();}; close.addEventListener('click',handler); open.addEventListener('click',()=>{window.open(url,'_blank','noopener,noreferrer');}); preview.addEventListener('click',e=>{if(e.target===preview||e.target.className==='rd-iframe-container')handler();}); this._shadowRoot.appendChild(preview); this._lockBodyScroll(); }, viewSource(){ if(!this._shadowRoot)return; const src='\n'+document.documentElement.outerHTML; this._shadowRoot.getElementById('rd-source-preview')?.remove(); const preview=SecurityUtils.createElement('div',{id:'rd-source-preview',style:{borderRadius:'32px'}}); const header=SecurityUtils.createElement('div',{id:'rd-source-preview-header'}); const title=SecurityUtils.createElement('span',{id:'rd-source-preview-title'});title.innerHTML=Icons.code+' 页面源码'; const controls=SecurityUtils.createElement('div',{style:{display:'flex',gap:'8px',alignItems:'center'}}); const copyBtn=SecurityUtils.createElement('button',{className:'rd-icon-btn',id:'rd-source-preview-copy',title:'复制源码'});copyBtn.innerHTML=Icons.copy; const close=SecurityUtils.createElement('button',{className:'rd-icon-btn',id:'rd-source-preview-close',title:'关闭'});close.innerHTML=Icons.close; controls.appendChild(copyBtn);controls.appendChild(close); header.appendChild(title);header.appendChild(controls); const scroll=SecurityUtils.createElement('div',{id:'rd-source-preview-scroll'}); const pre=document.createElement('pre');pre.id='rd-source-preview-content';pre.textContent=src; pre.style.whiteSpace='pre-wrap'; pre.style.wordBreak='break-word'; pre.style.background='#1e1e2e'; pre.style.color='#e0e0e0'; scroll.appendChild(pre); preview.appendChild(header);preview.appendChild(scroll); this._bindScrollOptimization(scroll); const handler=()=>{preview.remove();this._unlockBodyScroll();}; copyBtn.addEventListener('click',()=>this.copyToClipboard(src)); close.addEventListener('click',handler); preview.addEventListener('click',e=>{if(e.target===preview||e.target.id==='rd-source-preview-scroll')handler();}); this._shadowRoot.appendChild(preview); this._lockBodyScroll(); this._applyHighlight(pre,'html'); }, VIDEO_TYPES:['mp4','webm','ogg','ogv','mov','avi','mkv','flv','m3u8','mpd','ts','m4v','3gp','wmv','m2ts'], AUDIO_TYPES:['mp3','wav','flac','aac','m4a','wma','oga','weba','opus','audio','aiff','mid','ac3','amr'], isVideoType(displayType){ const t=(displayType||'').toLowerCase(); return this.VIDEO_TYPES.includes(t); }, playMedia(url,displayType){ if(!SecurityUtils.isSafeUrl(url)){this.showToast('不安全的资源地址');return;} if(!this._shadowRoot)return; const ev=this._shadowRoot.querySelector('#rd-inline-player video');if(ev&&ev._hls){try{ev._hls.destroy();}catch{}} this._shadowRoot.getElementById('rd-inline-player')?.remove(); const player=SecurityUtils.createElement('div',{id:'rd-inline-player',style:{borderRadius:'32px'}}); const header=SecurityUtils.createElement('div',{id:'rd-player-header'}); const title=SecurityUtils.createElement('span',{id:'rd-player-title'}); const dt=(displayType||'').toLowerCase(); const isVideo=this.isVideoType(dt)||(!dt&&/\.(mp4|webm|ogg|ogv|mov|avi|mkv|flv|m3u8|mpd|ts|m4v|3gp|wmv)(\?|#|$)/i.test(url)); const isHLS=dt==='m3u8'||/\.m3u8(\?|#|$)/i.test(url); const isDash=dt==='mpd'||(!dt&&/\.mpd(\?|#|$)/i.test(url)); title.innerHTML=(isVideo?Icons.video:Icons.audio)+' '+(isVideo?'视频播放':'音频播放'); const close=SecurityUtils.createElement('button',{className:'rd-icon-btn',id:'rd-close-player',title:'关闭'});close.innerHTML=Icons.close; const headerDlBtn=SecurityUtils.createElement('button',{className:'rd-icon-btn',id:'rd-player-download',title:'下载'});headerDlBtn.innerHTML=Icons.download; const headerBtns=SecurityUtils.createElement('div',{style:{display:'flex',gap:'8px',alignItems:'center',flexShrink:'0'}}); headerBtns.appendChild(headerDlBtn);headerBtns.appendChild(close); header.appendChild(title);header.appendChild(headerBtns); headerDlBtn.addEventListener('click',e=>{e.stopPropagation();this.downloadResource(url);}); const wrapper=SecurityUtils.createElement('div',{id:'rd-media-wrapper'}); player.appendChild(header);player.appendChild(wrapper); let updateFullscreenUI=()=>{}; let toggleFullscreen=()=>{}; let hlsInstance=null; if(isVideo){ const NATIVE_VIDEO=['mp4','webm','ogg','ogv','mov','m4v','m3u8']; const showVideoError=()=>{ const errWrap=document.createElement('div'); errWrap.style.cssText='display:flex;flex-direction:column;align-items:center;gap:12px;padding:40px;color:var(--rd-text-secondary);text-align:center;'; errWrap.innerHTML=`
${Icons.video}
无法直接播放此视频格式
该视频格式(${dt||'未知'})可能需要专用播放器,请尝试下载后播放${isHLS?',HLS流媒体加载失败,可能是跨域限制或网络问题':isDash?',此为DASH流媒体格式浏览器可能不支持':''}
`; wrapper.innerHTML=''; wrapper.appendChild(errWrap); const dlBtn2=document.createElement('button'); dlBtn2.style.cssText='margin-top:6px;padding:10px 28px;border-radius:14px;background:var(--rd-accent);color:#fff;border:none;cursor:pointer;font-size:14px;font-weight:600;line-height:1;box-shadow:0 4px 14px rgba(10,132,255,0.25);transition:transform 0.2s ease,box-shadow 0.2s ease;'; dlBtn2.textContent='下载视频'; dlBtn2.addEventListener('mouseenter',()=>{dlBtn2.style.transform='scale(1.04)';dlBtn2.style.boxShadow='0 6px 20px rgba(10,132,255,0.35)';}); dlBtn2.addEventListener('mouseleave',()=>{dlBtn2.style.transform='scale(1)';dlBtn2.style.boxShadow='0 4px 14px rgba(10,132,255,0.25)';}); dlBtn2.addEventListener('click',()=>this.downloadResource(url)); errWrap.appendChild(dlBtn2); }; const videoWrap=document.createElement('div'); videoWrap.id='rd-video-wrapper'; const video=document.createElement('video'); video.id='rd-video'; video.width=1280; video.height=720; video.autoplay=true; video.playsInline=true; video.preload='auto'; video.setAttribute('playsinline',''); video.setAttribute('webkit-playsinline',''); video.setAttribute('x-webkit-airplay','allow'); if(isHLS){ if(video.canPlayType('application/vnd.apple.mpegurl')){ video.src=url; }else{ this._loadHlsJS().then(Hls=>{ if(Hls.isSupported()){ hlsInstance=new Hls({ enableWorker:true, lowLatencyMode:true, backBufferLength:90, maxBufferLength:30, maxMaxBufferLength:60, startLevel:-1, capLevelToPlayerSize:true }); video._hls=hlsInstance; hlsInstance.loadSource(url); hlsInstance.attachMedia(video); hlsInstance.on(Hls.Events.MANIFEST_PARSED,()=>{ video.play().catch(()=>{}); }); hlsInstance.on(Hls.Events.ERROR,(_,data)=>{ if(!data.fatal)return; if(data.type===Hls.ErrorTypes.NETWORK_ERROR){try{hlsInstance.startLoad();return;}catch{}} else if(data.type===Hls.ErrorTypes.MEDIA_ERROR){try{hlsInstance.recoverMediaError();return;}catch{}} clearTimeout(errTimer); loading.style.display='none'; showVideoError(); }); }else{ clearTimeout(errTimer); loading.style.display='none'; showVideoError(); } }).catch(()=>{ clearTimeout(errTimer); loading.style.display='none'; showVideoError(); }); } }else{ video.src=url; } const loading=document.createElement('div'); loading.className='rd-video-loading'; videoWrap.appendChild(video); videoWrap.appendChild(loading); const fmt=s=>{if(!isFinite(s)||s<0)return'0:00';const h=Math.floor(s/3600);const m=Math.floor(s%3600/60);const ss=Math.floor(s%60);if(h>0)return`${h}:${m.toString().padStart(2,'0')}:${ss.toString().padStart(2,'0')}`;return`${m}:${ss.toString().padStart(2,'0')}`;}; const centerBtn=document.createElement('button'); centerBtn.className='rd-video-center-btn'; centerBtn.innerHTML=Icons.play; videoWrap.appendChild(centerBtn); const controls=document.createElement('div'); controls.className='rd-video-controls'; const playPauseBtn=document.createElement('button'); playPauseBtn.className='rd-video-btn rd-video-play-btn'; playPauseBtn.innerHTML=Icons.play; const progressWrap=document.createElement('div'); progressWrap.className='rd-video-progress'; const progressTrack=document.createElement('div'); progressTrack.className='rd-video-progress-track'; const progressFilled=document.createElement('div'); progressFilled.className='rd-video-progress-filled'; const progressThumb=document.createElement('div'); progressThumb.className='rd-video-progress-thumb'; progressTrack.appendChild(progressFilled); progressTrack.appendChild(progressThumb); progressWrap.appendChild(progressTrack); const curTimeLabel=document.createElement('span'); curTimeLabel.className='rd-video-time'; curTimeLabel.textContent='0:00'; const durTimeLabel=document.createElement('span'); durTimeLabel.className='rd-video-time'; durTimeLabel.textContent='0:00'; const speedBtn=document.createElement('button'); speedBtn.className='rd-video-btn rd-video-speed-btn'; speedBtn.innerHTML=Icons.speed; speedBtn.title='播放速度'; const speedMenu=document.createElement('div'); speedMenu.className='rd-video-speed-menu'; const speeds=[0.5,0.75,1,1.25,1.5,2]; speeds.forEach(sp=>{ const item=document.createElement('div'); item.className='rd-video-speed-item'+(sp===1?' active':''); item.textContent=sp===1?'正常':sp+'x'; item.dataset.speed=sp; item.addEventListener('click',e=>{ e.stopPropagation(); video.playbackRate=sp; speedMenu.classList.remove('rd-open'); speedMenu.querySelectorAll('.rd-video-speed-item').forEach(i=>i.classList.toggle('active',i===item)); speedBtn.classList.toggle('active',sp!==1); }); speedMenu.appendChild(item); }); const volumeBtn=document.createElement('button'); volumeBtn.className='rd-video-btn rd-video-volume-btn'; volumeBtn.innerHTML=Icons.volume; const fsBtn=document.createElement('button'); fsBtn.className='rd-video-btn rd-video-fs-btn'; fsBtn.innerHTML=Icons.expand; fsBtn.title='全屏'; const progressBar=document.createElement('div'); progressBar.className='rd-video-progress-bar'; progressBar.appendChild(curTimeLabel); progressBar.appendChild(progressWrap); progressBar.appendChild(durTimeLabel); const btnRow=document.createElement('div'); btnRow.className='rd-video-btn-row'; btnRow.appendChild(playPauseBtn); const btnSpacer=document.createElement('div'); btnSpacer.style.cssText='flex:1'; btnRow.appendChild(btnSpacer); btnRow.appendChild(speedBtn); btnRow.appendChild(volumeBtn); btnRow.appendChild(fsBtn); controls.appendChild(progressBar); controls.appendChild(btnRow); videoWrap.appendChild(controls); videoWrap.appendChild(speedMenu); let errTimer=setTimeout(()=>{if(video.readyState===0)showVideoError();},8000); let controlsTimer=null; let centerTimer=null; const showControls=()=>{ videoWrap.classList.add('rd-show-controls'); if(controlsTimer)clearTimeout(controlsTimer); controlsTimer=setTimeout(()=>{videoWrap.classList.remove('rd-show-controls');},10000); if(video.paused)showCenter(); }; const hideControls=()=>{ if(controlsTimer)clearTimeout(controlsTimer); videoWrap.classList.remove('rd-show-controls'); }; const showCenter=()=>{ videoWrap.classList.add('rd-show-center'); if(centerTimer)clearTimeout(centerTimer); centerTimer=setTimeout(()=>{videoWrap.classList.remove('rd-show-center');},3000); }; const hideCenter=()=>{ if(centerTimer)clearTimeout(centerTimer); videoWrap.classList.remove('rd-show-center'); }; const togglePlay=()=>{ if(video.paused){video.play();}else{video.pause();} }; const updatePlayUI=()=>{ playPauseBtn.innerHTML=video.paused?Icons.play:Icons.pause; centerBtn.innerHTML=video.paused?Icons.play:Icons.pause; if(video.paused){showCenter();showControls();} else{hideCenter();} }; const updateProgress=()=>{ const p=video.currentTime/video.duration*100||0; progressFilled.style.width=p+'%'; progressThumb.style.left=p+'%'; curTimeLabel.textContent=fmt(video.currentTime); durTimeLabel.textContent=fmt(video.duration); }; const updateVolumeUI=()=>{ volumeBtn.innerHTML=video.muted||video.volume===0?Icons.volumeMute:Icons.volume; }; toggleFullscreen=()=>{ if(!document.fullscreenElement&&!document.webkitFullscreenElement){ const reqFs=videoWrap.requestFullscreen||videoWrap.webkitRequestFullscreen; if(reqFs){ const result=reqFs.call(videoWrap); if(result&&result.catch)result.catch(()=>{}); if(video.videoWidth>video.videoHeight&&screen.orientation&&screen.orientation.lock){ screen.orientation.lock('landscape').catch(()=>{}); }else if(video.videoHeight>video.videoWidth&&screen.orientation&&screen.orientation.lock){ screen.orientation.lock('portrait').catch(()=>{}); } } }else{ if(document.exitFullscreen)document.exitFullscreen(); else if(document.webkitExitFullscreen)document.webkitExitFullscreen(); if(screen.orientation&&screen.orientation.unlock){screen.orientation.unlock();} } }; updateFullscreenUI=()=>{ const isFs=!!(document.fullscreenElement||document.webkitFullscreenElement); fsBtn.innerHTML=isFs?Icons.compress:Icons.expand; fsBtn.title=isFs?'退出全屏':'全屏'; if(isFs&&video.videoWidth&&video.videoHeight){ if(screen.orientation&&screen.orientation.lock){ const target=video.videoWidth>video.videoHeight?'landscape':'portrait'; if(screen.orientation.type&&!screen.orientation.type.startsWith(target)){ screen.orientation.lock(target).catch(()=>{}); } } } }; video.addEventListener('loadstart',()=>{loading.style.display='block';}); video.addEventListener('waiting',()=>{loading.style.display='block';}); video.addEventListener('canplay',()=>{loading.style.display='none';}); video.addEventListener('playing',()=>{loading.style.display='none';updatePlayUI();}); video.addEventListener('loadeddata',()=>{clearTimeout(errTimer);loading.style.display='none';updateProgress();updatePlayUI();}); video.addEventListener('loadedmetadata',updateProgress); video.addEventListener('durationchange',updateProgress); video.addEventListener('timeupdate',updateProgress); video.addEventListener('play',updatePlayUI); video.addEventListener('pause',updatePlayUI); video.addEventListener('volumechange',updateVolumeUI); video.addEventListener('ended',()=>{updatePlayUI();}); video.addEventListener('error',()=>{clearTimeout(errTimer);loading.style.display='none';showVideoError();}); if(!NATIVE_VIDEO.includes(dt)&&dt){ video.addEventListener('canplay',()=>{ if(video.videoWidth===0){ const hint=document.createElement('div'); hint.style.cssText='position:absolute;bottom:60px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.7);color:#fff;padding:6px 14px;border-radius:10px;font-size:12px;pointer-events:none;white-space:nowrap;z-index:5;'; hint.textContent='当前格式仅支持音频播放,建议下载后查看完整视频'; videoWrap.appendChild(hint); } },{once:true}); } playPauseBtn.addEventListener('click',e=>{e.stopPropagation();togglePlay();}); centerBtn.addEventListener('click',e=>{e.stopPropagation();togglePlay();}); let clickTimer=null; video.addEventListener('click',()=>{ if(clickTimer){ clearTimeout(clickTimer); clickTimer=null; togglePlay(); }else{ clickTimer=setTimeout(()=>{ clickTimer=null; if(videoWrap.classList.contains('rd-show-controls')){ hideControls(); }else{ showControls(); } },250); } }); progressWrap.addEventListener('click',e=>{ e.stopPropagation(); const r=progressTrack.getBoundingClientRect(); const p=Math.max(0,Math.min(1,(e.clientX-r.left)/r.width)); video.currentTime=p*video.duration; }); let isDragging=false; progressWrap.addEventListener('touchstart',e=>{isDragging=true;video.pause();},{passive:true}); progressWrap.addEventListener('touchmove',e=>{ if(!isDragging)return; e.stopPropagation(); const r=progressTrack.getBoundingClientRect(); const p=Math.max(0,Math.min(1,(e.touches[0].clientX-r.left)/r.width)); video.currentTime=p*video.duration; },{passive:true}); progressWrap.addEventListener('touchend',()=>{isDragging=false;},{passive:true}); speedBtn.addEventListener('click',e=>{e.stopPropagation();speedMenu.classList.toggle('rd-open');}); volumeBtn.addEventListener('click',e=>{e.stopPropagation();video.muted=!video.muted;}); fsBtn.addEventListener('click',e=>{e.stopPropagation();toggleFullscreen();}); document.addEventListener('fullscreenchange',updateFullscreenUI); document.addEventListener('webkitfullscreenchange',updateFullscreenUI); player._videoKeyHandler=e=>{ if(!player.parentNode){document.removeEventListener('keydown',player._videoKeyHandler);return;} if(e.target.tagName==='INPUT'||e.target.tagName==='TEXTAREA')return; switch(e.key){ case' ':e.preventDefault();togglePlay();break; case'ArrowLeft':e.preventDefault();video.currentTime=Math.max(0,video.currentTime-5);break; case'ArrowRight':e.preventDefault();video.currentTime=Math.min(video.duration||0,video.currentTime+5);break; case'ArrowUp':e.preventDefault();video.volume=Math.min(1,video.volume+0.1);break; case'ArrowDown':e.preventDefault();video.volume=Math.max(0,video.volume-0.1);break; case'm':case'M':video.muted=!video.muted;break; case'f':case'F':toggleFullscreen();break; } }; document.addEventListener('keydown',player._videoKeyHandler); wrapper.appendChild(videoWrap); }else{ const audioUI=this.createCustomAudioPlayer(url); wrapper.appendChild(audioUI); } const handler=()=>{ try{if(hlsInstance){hlsInstance.destroy();hlsInstance=null;}if(player.querySelector('video'))player.querySelector('video').pause();if(player.querySelector('audio'))player.querySelector('audio').pause();}catch{} if(player._videoKeyHandler)document.removeEventListener('keydown',player._videoKeyHandler); document.removeEventListener('fullscreenchange',updateFullscreenUI); document.removeEventListener('webkitfullscreenchange',updateFullscreenUI); player.remove();this._unlockBodyScroll(); }; close.addEventListener('click',handler); player.addEventListener('click',e=>{if(e.target===player)handler();}); this._shadowRoot.appendChild(player); this._lockBodyScroll(); }, createCustomAudioPlayer(url){ const container=document.createElement('div'); container.className='rd-custom-audio'; const audio=document.createElement('audio'); audio.src=url;audio.preload='auto'; const info=document.createElement('div');info.className='rd-audio-info'; const infoIcon=document.createElement('div');infoIcon.className='rd-audio-info-icon'; infoIcon.innerHTML=Icons.music; const fname=document.createElement('div');fname.className='rd-audio-filename'; fname.textContent=this.extractFilename(url); info.appendChild(infoIcon);info.appendChild(fname); const controls=document.createElement('div');controls.className='rd-audio-controls'; const playBtn=document.createElement('button');playBtn.className='rd-play-pause-btn'; playBtn.innerHTML=Icons.play; const progWrap=document.createElement('div');progWrap.className='rd-progress-container'; const progBg=document.createElement('div');progBg.className='rd-progress-bg'; const progFilled=document.createElement('div');progFilled.className='rd-progress-filled'; const progThumb=document.createElement('div');progThumb.className='rd-progress-thumb'; progBg.appendChild(progFilled);progBg.appendChild(progThumb);progWrap.appendChild(progBg); const time=document.createElement('div');time.className='rd-time'; const cur=document.createElement('span');cur.textContent='0:00'; const sep=document.createElement('span');sep.textContent='/'; const dur=document.createElement('span');dur.textContent='0:00'; time.appendChild(cur);time.appendChild(sep);time.appendChild(dur); controls.appendChild(playBtn);controls.appendChild(progWrap);controls.appendChild(time); container.appendChild(info);container.appendChild(controls);container.appendChild(audio); const fmt=s=>{if(!isFinite(s))return'0:00';const m=Math.floor(s/60);const ss=Math.floor(s%60);return `${m}:${ss.toString().padStart(2,'0')}`;}; const updateDur=()=>{if(isFinite(audio.duration)&&audio.duration>0){dur.textContent=fmt(audio.duration);updateProg();}}; const updateProg=()=>{ const p=audio.currentTime/audio.duration*100||0; progFilled.style.width=p+'%'; progThumb.style.left=p+'%'; cur.textContent=fmt(audio.currentTime); }; audio.addEventListener('loadedmetadata',()=>{ if(audio.duration===Infinity||isNaN(audio.duration)){ audio.currentTime=1e101; audio.ontimeupdate=()=>{audio.ontimeupdate=null;audio.currentTime=0;updateDur();}; }else{updateDur();} }); audio.addEventListener('durationchange',updateDur); audio.addEventListener('timeupdate',updateProg); playBtn.addEventListener('click',()=>{if(audio.paused){audio.play();playBtn.innerHTML=Icons.pause;}else{audio.pause();playBtn.innerHTML=Icons.play;}}); audio.addEventListener('ended',()=>{playBtn.innerHTML=Icons.play;progFilled.style.width='0%';progThumb.style.left='0%';}); progWrap.addEventListener('click',e=>{ const r=progBg.getBoundingClientRect(); const p=Math.max(0,Math.min(1,(e.clientX-r.left)/r.width)); audio.currentTime=p*audio.duration; }); return container; }, addResource(r){ if(!r||!r.url)return false; const url=r.url; if(processedUrls.has(url))return false; processedUrls.add(url); if(processedUrls.size>CONFIG.MAX_URL_CACHE){ const it=processedUrls.values(); for(let i=0;i<500;i++)processedUrls.delete(it.next().value); } const displayType=this.getDisplayType(url,r.contentType||'',r.initiatorType||''); const filterType=this.getFilterType(displayType); r.displayType=displayType; r.filterType=filterType; r.icon=this.getTypeIcon(displayType); r.previewUrl=this.getPreviewUrl(r); r.filename=this.extractFilename(url); if(!r.timestamp)r.timestamp=Date.now(); if(this.resources.length>=CONFIG.MAX_RESOURCES){ this.resources.shift(); } this.resources.unshift(r); this._statsDirty=true; this.scheduleUpdate(); return true; }, scheduleUpdate(){ if(this._rafId)return; this._rafId=requestAnimationFrame(()=>{ this._rafId=null; const now=Date.now(); if(now-this._lastUpdate{ try{ const finalUrl=resp.url||url; const ct=resp.headers.get('content-type')||''; self.addResource({url:finalUrl,contentType:ct,initiatorType:'fetch',timestamp:Date.now()}); }catch{} }).catch(()=>{}); } return promise; }; if(typeof PerformanceObserver!=='undefined'){ try{ const obs=new PerformanceObserver(list=>{ for(const entry of list.getEntries()){ try{ const url=entry.name; if(!url||!SecurityUtils.isSafeUrl(url))continue; const it=entry.initiatorType||'resource'; let ct=''; if(entry.transferSize!==undefined&&entry.encodedBodySize>0){ const ext=url.split('?')[0].split('.').pop()?.toLowerCase()||''; if(['mp4','webm','ogg','ogv','mov','m4v','flv','mkv','avi','3gp','wmv','m2ts','ts'].includes(ext))ct='video'; else if(['mp3','wav','flac','aac','m4a','wma','oga','weba','opus','aiff','mid','ac3','amr'].includes(ext))ct='audio'; else if(['jpg','jpeg','png','gif','webp','svg','ico','bmp','avif','heic'].includes(ext))ct='image'; else if(ext==='css')ct='text/css'; else if(['js','mjs'].includes(ext))ct='application/javascript'; else if(['woff','woff2','ttf','otf','eot'].includes(ext))ct='font'; else if(['m3u8','mpd'].includes(ext))ct='application/vnd.apple.mpegurl'; } self.addResource({url,contentType:ct,initiatorType:it,timestamp:Date.now()}); }catch{} } }); obs.observe({entryTypes:['resource']}); this._perfObserver=obs; }catch{} try{ const entries=performance.getEntriesByType('resource'); for(const entry of entries){ try{ const url=entry.name; if(!url||!SecurityUtils.isSafeUrl(url))continue; const it=entry.initiatorType||'resource'; let ct=''; const ext=url.split('?')[0].split('.').pop()?.toLowerCase()||''; if(['mp4','webm','ogg','ogv','mov','m4v','flv','mkv','avi','3gp','wmv','m2ts','ts'].includes(ext))ct='video'; else if(['mp3','wav','flac','aac','m4a','wma','oga','weba','opus','aiff','mid','ac3','amr'].includes(ext))ct='audio'; else if(['jpg','jpeg','png','gif','webp','svg','ico','bmp','avif','heic'].includes(ext))ct='image'; else if(ext==='css')ct='text/css'; else if(['js','mjs'].includes(ext))ct='application/javascript'; else if(['woff','woff2','ttf','otf','eot'].includes(ext))ct='font'; else if(['m3u8','mpd'].includes(ext))ct='application/vnd.apple.mpegurl'; self.addResource({url,contentType:ct,initiatorType:it,timestamp:Date.now()}); }catch{} } }catch{} } }, setupResourceObserver(){ if(this._resourceObserver)return; const self=this; let pending=false; const flush=()=>{ pending=false; if(self.isDestroyed||!self._host)return; self._scanQueuedNodes(); if(self.isPanelOpen)self.scheduleUpdate(); }; const schedule=()=>{ if(pending)return; pending=true; requestAnimationFrame(flush); }; this._observerQueue=[]; this._resourceObserver=new MutationObserver(muts=>{ for(const m of muts){ if(m.type==='childList'){ for(const n of m.addedNodes){ if(n.nodeType===1){n._rdAttrChange=false;self._observerQueue.push(n);} } }else if(m.type==='attributes'){ m.target._rdAttrChange=true; self._observerQueue.push(m.target); } } if(self._observerQueue.length)schedule(); }); this._resourceObserver.observe(document.body,{childList:true,subtree:true,attributes:true,attributeFilter:['src','href','srcset','poster','data-src','data-original','data-bg','style','background','data-alt','data-video','data-video-src','data-media','data-mp4','data-hls','data-url','data-poster']}); }, _scanQueuedNodes(){ const queue=this._observerQueue; this._observerQueue=[]; const seen=new Set(); const attrChanged=new Set(); for(const node of queue){ if(node._rdAttrChange){ node._rdAttrChange=false; attrChanged.add(node); }else{ this._captureFromNode(node,seen); } } if(attrChanged.size){ for(const el of attrChanged){ this._captureElement(el); } } }, _captureFromNode(root,seen){ if(!root||root.nodeType!==1)return; if(!seen)seen=new Set(); const collect=(el)=>{ if(!el||el.nodeType!==1||seen.has(el))return; seen.add(el); this._captureElement(el); if(el.shadowRoot){ try{ const sEls=el.shadowRoot.querySelectorAll('img,video,audio,source,track,picture,object,embed,iframe,link,script,input[type=image],image,[data-src],[data-original],[data-lazy-src],[data-srcset],[data-bg],[data-poster]'); for(const s of sEls)collect(s); }catch{} } if(el.tagName==='IFRAME'){ this._scanIframeElement(el); } }; collect(root); if(root.querySelectorAll){ try{ const els=root.querySelectorAll('img,video,audio,source,track,picture,object,embed,iframe,link,script,input[type=image],image,[style],[background],[data-src],[data-original],[data-lazy-src],[data-lazy-srcset],[data-srcset],[data-bg],[data-bgset],[data-background],[data-poster],[data-alt],[data-video],[data-video-src],[data-media],[data-mp4],[data-hls],[data-url],[data-source],[data-file],[data-play]'); for(const el of els)collect(el); }catch{} } if(root.shadowRoot){ try{ const sEls=root.shadowRoot.querySelectorAll('img,video,audio,source,track,picture,object,embed,iframe,link,script,input[type=image],image,[data-src],[data-original],[data-lazy-src],[data-srcset],[data-bg],[data-poster]'); for(const s of sEls)collect(s); }catch{} } }, _scanIframeElement(iframe){ if(!iframe||iframe._rdScanned)return; iframe._rdScanned=true; const scanIframe=()=>{ try{ const doc=iframe.contentDocument; if(!doc)return; const elements=doc.querySelectorAll('img,video,audio,source,track,picture,object,embed,iframe,link,script,input[type=image],image'); for(const el of elements)this._captureElement(el,true); try{ const sel='[data-src],[data-original],[data-lazy-src],[data-lazy-srcset],[data-srcset],[data-bg],[data-bgset],[data-background],[data-image],[data-thumb],[data-poster],[data-alt],[data-video]'; const lazyEls=doc.querySelectorAll(sel); for(const el of lazyEls)this._captureElement(el,true); }catch{} }catch{} }; scanIframe(); iframe.addEventListener('load',scanIframe); }, _parseSrcset(srcset){ if(!srcset)return[]; const out=[]; for(const part of srcset.split(',')){ const u=part.trim().split(/\s+/)[0]; if(u)out.push(u); } return out; }, _captureElement(el,useCache){ if(!el||el.nodeType!==1)return; if(useCache&&processedElements.has(el))return; processedElements.add(el); const tag=(el.tagName||'').toLowerCase(); const push=(url,ct,it)=>{ if(url){const s=String(url).trim();if(s&&SecurityUtils.isSafeUrl(s))this.addResource({url:s,contentType:ct||'',initiatorType:it||'scan',timestamp:Date.now()});} }; const lazyAttrs=['data-src','data-original','data-lazy-src','data-lazy','data-srcset','data-lazy-srcset','data-bg','data-bgset','data-image','data-thumb','data-poster']; if(tag==='img'){ push(el.src,'image','scan'); const ss=el.getAttribute('srcset')||el.srcset;if(ss)this._parseSrcset(ss).forEach(u=>push(u,'image','srcset')); push(el.currentSrc,'image','current'); lazyAttrs.forEach(a=>{const v=el.getAttribute(a);if(v){if(a.includes('srcset')||a.includes('bgset'))this._parseSrcset(v).forEach(u=>push(u,'image','lazy'));else push(v,'image','lazy');}}); } else if(tag==='source'){ push(el.src,'','scan'); const ss=el.getAttribute('srcset')||el.srcset;if(ss)this._parseSrcset(ss).forEach(u=>push(u,'','srcset')); const vt=el.getAttribute('type')||''; if(vt)push(el.src,vt,'source-type'); } else if(tag==='video'){ push(el.src,el.type||'video','scan'); push(el.poster,'image','poster'); push(el.getAttribute('data-poster'),'image','lazy-poster'); if(el.src&&el.src.startsWith('blob:'))push(el.src,'video/blob','blob-media'); lazyAttrs.forEach(a=>{const v=el.getAttribute(a);if(v)push(v,'video','lazy');}); const videoLazyAttrs=['data-alt','data-video','data-video-src','data-media','data-mp4','data-hls','data-url','data-src-video','data-source','data-file','data-play']; videoLazyAttrs.forEach(a=>{const v=el.getAttribute(a);if(v&&SecurityUtils.isSafeUrl(v))push(v,'video','lazy-'+a);}); try{ const sources=el.querySelectorAll('source'); for(const s of sources){ push(s.src,s.getAttribute('type')||'video','video-source'); const ss=s.getAttribute('srcset')||s.srcset; if(ss)this._parseSrcset(ss).forEach(u=>push(u,'video','srcset')); } }catch{} if(!el.src&&!el.querySelector('source')){ const dataSrc=el.getAttribute('data-src')||el.getAttribute('data-video')||el.getAttribute('data-media')||el.getAttribute('data-alt'); if(dataSrc&&SecurityUtils.isSafeUrl(dataSrc))push(dataSrc,'video','fallback-src'); } } else if(tag==='audio'){ push(el.src,el.type||'audio','scan'); if(el.src&&el.src.startsWith('blob:'))push(el.src,'audio/blob','blob-media'); lazyAttrs.forEach(a=>{const v=el.getAttribute(a);if(v)push(v,'audio','lazy');}); } else if(tag==='track'){ push(el.src,'text/vtt','track'); } else if(tag==='iframe'){ push(el.src,'','iframe'); } else if(tag==='link'){ const rel=(el.rel||'').toLowerCase(); if(rel==='stylesheet'||rel==='preload'||rel==='prefetch'||rel==='icon'||rel==='shortcut icon'||rel==='apple-touch-icon'||rel==='manifest')push(el.href,'','link'); } else if(tag==='script'){ push(el.src,'','scan'); } else if(tag==='object'||tag==='embed'){ push(el.data,'','scan'); push(el.src,'','scan'); } else if(tag==='input'){ if((el.type||'').toLowerCase()==='image')push(el.src,'image','input-image'); } else if(tag==='image'){ push(el.getAttribute('href')||el.getAttribute('xlink:href'),'image','svg-image'); } else if(tag==='picture'){ const sources=el.querySelectorAll('source'); for(const s of sources){push(s.src,'','scan');const ss=s.getAttribute('srcset')||s.srcset;if(ss)this._parseSrcset(ss).forEach(u=>push(u,'image','srcset'));} } const inlineStyle=el.getAttribute('style'); if(inlineStyle){ const urls=inlineStyle.matchAll(/url\(\s*['"]?([^'")]+)['"]?\s*\)/g); for(const m of urls)push(m[1],'','bg'); } const bgAttr=el.getAttribute&&el.getAttribute('background'); if(bgAttr)push(bgAttr,'image','bg'); for(const a of['data-bg','data-background','data-bgset','data-bg-src']){ const v=el.getAttribute&&el.getAttribute(a); if(v){if(a.includes('bgset'))this._parseSrcset(v).forEach(u=>push(u,'image','lazy-bg'));else push(v,'image','lazy-bg');} } }, setupSPASupport(){ const self=this; const origPush=history.pushState; history.pushState=function(...args){ origPush.apply(this,args); setTimeout(()=>self.scanExistingResources(),500); }; const origReplace=history.replaceState; history.replaceState=function(...args){ origReplace.apply(this,args); setTimeout(()=>self.scanExistingResources(),500); }; window.addEventListener('popstate',()=>setTimeout(()=>self.scanExistingResources(),500)); }, scanExistingResources(){ const self=this; const scan=()=>{ const elements=document.querySelectorAll('img,video,audio,source,track,picture,object,embed,iframe,link,script,input[type=image],image'); const seen=new Set(); for(const el of elements){ if(seen.has(el))continue; seen.add(el); self._captureElement(el,true); } self._scanInlineAndBgImages(seen); self._scanLazyAttributes(seen); self._scanMetaMedia(); self._scanStyleSheets(); self._scanIframes(); self._scanShadowDOM(); }; scan(); }, _scanIframes(){ try{ const iframes=document.querySelectorAll('iframe'); for(const iframe of iframes){ try{ const doc=iframe.contentDocument; if(!doc)continue; const elements=doc.querySelectorAll('img,video,audio,source,track,picture,object,embed,iframe,link,script,input[type=image],image'); for(const el of elements){ this._captureElement(el,true); } try{ const sel='[data-src],[data-original],[data-lazy-src],[data-lazy-srcset],[data-srcset],[data-bg],[data-bgset],[data-background],[data-image],[data-thumb],[data-poster],[data-alt],[data-video],[data-video-src],[data-media],[data-mp4],[data-hls],[data-url],[data-source],[data-file],[data-play]'; const lazyEls=doc.querySelectorAll(sel); for(const el of lazyEls)this._captureElement(el,true); }catch{} const metas=doc.querySelectorAll('meta[property],meta[name]'); for(const m of metas){ const prop=(m.getAttribute('property')||m.getAttribute('name')||'').toLowerCase(); const content=m.getAttribute('content'); if(!content||!SecurityUtils.isSafeUrl(content))continue; if(prop.startsWith('og:image')||prop.startsWith('og:video')||prop.startsWith('og:audio')||prop==='twitter:image'||prop==='twitter:image:src'||prop==='twitter:player:stream'){ const ct=prop.includes('video')?'video':(prop.includes('audio')?'audio':'image'); this.addResource({url:content,contentType:ct,initiatorType:'iframe-meta',timestamp:Date.now()}); } } }catch{} } }catch{} }, _scanShadowDOM(){ try{ const walk=(root)=>{ if(!root||root.nodeType!==1)return; if(root.shadowRoot){ try{ const els=root.shadowRoot.querySelectorAll('img,video,audio,source,track,picture,object,embed,iframe,link,script,input[type=image],image,[data-src],[data-original],[data-lazy-src],[data-srcset],[data-bg],[data-poster]'); for(const el of els){ this._captureElement(el,true); if(el.shadowRoot)walk(el); } }catch{} } if(root.querySelectorAll){ const all=root.querySelectorAll('*'); for(const el of all){ if(el.shadowRoot)walk(el); } } }; walk(document.body); }catch{} }, _scanLazyAttributes(seen){ try{ const sel='[data-src],[data-original],[data-lazy-src],[data-lazy-srcset],[data-srcset],[data-bg],[data-bgset],[data-background],[data-image],[data-thumb],[data-poster],[data-alt],[data-video],[data-video-src],[data-media],[data-mp4],[data-hls],[data-url],[data-source],[data-file],[data-play]'; const all=document.querySelectorAll(sel); for(const el of all){ if(seen.has(el))continue; seen.add(el); this._captureElement(el,true); } }catch{} }, _scanMetaMedia(){ try{ const metas=document.querySelectorAll('meta[property],meta[name]'); for(const m of metas){ const prop=(m.getAttribute('property')||m.getAttribute('name')||'').toLowerCase(); const content=m.getAttribute('content'); if(!content||!SecurityUtils.isSafeUrl(content))continue; if(prop.startsWith('og:image')||prop.startsWith('og:video')||prop.startsWith('og:audio')||prop==='twitter:image'||prop==='twitter:image:src'||prop==='twitter:player:stream'){ const ct=prop.includes('video')?'video':(prop.includes('audio')?'audio':'image'); this.addResource({url:content,contentType:ct,initiatorType:'meta',timestamp:Date.now()}); } } }catch{} }, _scanInlineAndBgImages(seen){ try{ const all=document.querySelectorAll('[style],[background]'); for(const el of all){ if(seen.has(el))continue; seen.add(el); if(processedBg.has(el))continue; processedBg.add(el); const inlineStyle=el.getAttribute('style'); if(inlineStyle){ const urls=inlineStyle.matchAll(/url\(\s*['"]?([^'")]+)['"]?\s*\)/g); for(const m of urls){if(SecurityUtils.isSafeUrl(m[1]))this.addResource({url:m[1],contentType:'image',initiatorType:'bg',timestamp:Date.now()});} } const bgAttr=el.getAttribute('background'); if(bgAttr&&SecurityUtils.isSafeUrl(bgAttr))this.addResource({url:bgAttr,contentType:'image',initiatorType:'bg',timestamp:Date.now()}); const bg=getComputedStyle(el).backgroundImage; if(bg&&bg!=='none'){ const urls=bg.matchAll(/url\(\s*['"]?([^'")]+)['"]?\s*\)/g); for(const m of urls){if(m[1]&&SecurityUtils.isSafeUrl(m[1]))this.addResource({url:m[1],contentType:'image',initiatorType:'bg',timestamp:Date.now()});} } } }catch{} }, _scanStyleSheets(){ try{ for(const sheet of document.styleSheets){ if(processedSheets.has(sheet))continue; let rules; try{rules=sheet.cssRules||sheet.rules;}catch{processedSheets.add(sheet);continue;} if(!rules||rules.length===0)continue; processedSheets.add(sheet); for(const rule of rules){ if(rule.type===3||rule.type===CSSRule.IMPORT_RULE){ if(rule.href&&SecurityUtils.isSafeUrl(rule.href))this.addResource({url:rule.href,contentType:'text/css',initiatorType:'css-import',timestamp:Date.now()}); continue; } if(rule.type===5||rule.type===CSSRule.FONT_FACE_RULE){ const style=rule.style; if(style){ const src=style.getPropertyValue('src'); if(src){ const urls=src.matchAll(/url\(\s*['"]?([^'")]+)['"]?\s*\)/g); for(const m of urls){if(m[1]&&SecurityUtils.isSafeUrl(m[1]))this.addResource({url:m[1],contentType:'font',initiatorType:'css-font',timestamp:Date.now()});} } } continue; } const style=rule.style; if(!style)continue; for(let i=0;i{ if(this.currentFilter&&this.currentFilter!=='all'){ return this.getFilterType(r.displayType)===this.currentFilter; } return true; }); this.sortResources(filtered); this.filteredResources=filtered; this.updateCounts(); if(filtered.length===0){ if(this._lastRenderedFilter!=='__empty__'){ this._lastRenderedFilter='__empty__'; this._renderSignature=''; this._elements.listContent.innerHTML=`
${Icons.harmony}
暂无捕获的资源
浏览网页时将自动捕获图片、视频、音频等资源
`; } return; } const batch=filtered.slice(0,CONFIG.BATCH_SIZE); const sig=batch.map(r=>r.url).join('\x01')+this.currentFilter+batch.length+this.sortMode+this.viewMode; if(sig===this._renderSignature&&this.currentFilter===this._lastRenderedFilter){ return; } const filterChanged=this.currentFilter!==this._lastRenderedFilter; this._renderSignature=sig; this._lastRenderedFilter=this.currentFilter; const list=this._elements.list; const savedScroll=list?list.scrollTop:0; const frag=document.createDocumentFragment(); for(const r of batch){ frag.appendChild(this.createEntry(r)); } this._elements.listContent.innerHTML=''; this._elements.listContent.appendChild(frag); this.applyViewMode(); if(list&&!filterChanged)list.scrollTop=savedScroll; }, createEntry(r){ const entry=document.createElement('div'); entry.className='rd-entry'; entry.dataset.url=r.url; entry.dataset.displayType=r.displayType; const thumbWrap=document.createElement('div'); thumbWrap.className='rd-entry-thumb-wrapper'; const typeColor=TYPE_COLORS[r.displayType]||'#0A84FF'; thumbWrap.style.background=`linear-gradient(135deg,${typeColor}22,${typeColor}0d)`; if(r.previewUrl){ const img=document.createElement('img'); img.className='rd-entry-thumb'; img.src=r.previewUrl; img.loading='lazy'; img.onerror=()=>{ thumbWrap.innerHTML=`
${r.icon}
`; }; thumbWrap.appendChild(img); thumbWrap.dataset.url=r.url; }else{ thumbWrap.innerHTML=`
${r.icon}
`; } entry.appendChild(thumbWrap); const content=document.createElement('div'); content.className='rd-entry-content'; const header=document.createElement('div'); header.className='rd-entry-header'; const typeBadge=document.createElement('span'); typeBadge.className=`rd-entry-type ${r.displayType}`; typeBadge.textContent=r.displayType; header.appendChild(typeBadge); const filename=document.createElement('div'); filename.className='rd-entry-filename'; filename.textContent=r.filename; filename.title=r.filename; header.appendChild(filename); const actions=document.createElement('div'); actions.className='rd-entry-actions'; const copyBtn=document.createElement('button'); copyBtn.className='rd-action-btn rd-copy-btn'; copyBtn.title='复制链接'; copyBtn.innerHTML=Icons.copy; copyBtn.dataset.url=r.url; actions.appendChild(copyBtn); const isMedia=r.filterType==='media'; if(isMedia){ const playBtn=document.createElement('button'); playBtn.className='rd-action-btn rd-play-btn'; playBtn.title='播放'; playBtn.innerHTML=Icons.play; playBtn.dataset.url=r.url; playBtn.dataset.displayType=r.displayType; actions.appendChild(playBtn); } const dlBtn=document.createElement('button'); dlBtn.className='rd-action-btn rd-download-btn'; dlBtn.title='下载'; dlBtn.innerHTML=Icons.download; dlBtn.dataset.url=r.url; actions.appendChild(dlBtn); header.appendChild(actions); const urlEl=document.createElement('div'); urlEl.className='rd-entry-url'; urlEl.textContent=r.url; content.appendChild(header); content.appendChild(urlEl); entry.appendChild(content); return entry; }, updateCounts(){ if(!this._shadowRoot)return; const counts={all:0,image:0,media:0,other:0}; for(const r of this.resources){ const ft=this.getFilterType(r.displayType); counts.all++; counts[ft]=(counts[ft]||0)+1; } for(const f in counts){ const el=this._shadowRoot.querySelector(`[data-count="${f}"]`); if(el)el.textContent=counts[f]; } const urlEl=this._shadowRoot.getElementById('rd-page-url'); if(urlEl)urlEl.textContent=SecurityUtils.sanitizeAttr(location.href); }, }; RD.init(); window.ResourceDiary=RD; })();