// ==UserScript==
// @name 加载日记
// @namespace resource-diary
// @version 8.0.7
// @description 网页资源捕获工具、查看网页源代码
// @author DeepSeek
// @match *://*/*
// @license MIT
// @icon https://static.vecteezy.com/system/resources/thumbnails/010/796/909/small/multiple-files-icon-with-outline-style-vector.jpg
// @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 processedUrls=new Set();
const CONFIG=Object.freeze({
MAX_RESOURCES:600,
MAX_URL_CACHE:2500,
UPDATE_DELAY:120,
BATCH_SIZE:60,
DEBOUNCE_TIME:100,
LAZY_LOAD_OFFSET:'120px',
SCAN_INTERVAL:3000,
ITEM_HEIGHT:80,
FETCH_TIMEOUT:30000
});
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'
]);
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',
mp3:'mp3',wav:'wav',flac:'flac',aac:'aac',m4a:'m4a',
wma:'wma',oga:'oga',weba:'weba',opus:'opus',
jpg:'jpg',jpeg:'jpg',png:'png',gif:'gif',webp:'webp',
svg:'svg',ico:'ico',bmp:'bmp',avif:'avif',jxl:'jxl',
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',
'audio/mpeg':'mp3','audio/wav':'wav','audio/flac':'flac',
'audio/aac':'aac','audio/ogg':'oga','audio/webm':'weba',
'audio/opus':'opus',
'image/jpeg':'jpg','image/png':'png','image/gif':'gif',
'image/webp':'webp','image/svg+xml':'svg','image/avif':'avif',
'text/css':'stylesheet',
'application/javascript':'script','text/javascript':'script',
'application/json':'json','application/xml':'xml'
});
const TYPE_COLORS=Object.freeze({
jpg:'#f43f5e',jpeg:'#f43f5e',png:'#a855f7',gif:'#f97316',
webp:'#06b6d4',svg:'#f59e0b',ico:'#78716c',bmp:'#6b7280',
avif:'#22c55e',jxl:'#3b82f6',image:'#6366f1',
mp4:'#ef4444',webm:'#3b82f6',ogg:'#f59e0b',ogv:'#f59e0b',
mov:'#8b5cf6',avi:'#78716c',mkv:'#6b7280',flv:'#e84c6a',
m3u8:'#22c55e',mpd:'#06b6d4',
mp3:'#5b7fff',wav:'#34a853',flac:'#fbbc05',aac:'#ea4335',
m4a:'#9334e6',wma:'#5f6368',oga:'#ff6d01',weba:'#4285f4',
opus:'#8b5cf6',audio:'#5b7fff',
script:'#f59e0b',stylesheet:'#34a853',json:'#4285f4',
xml:'#9aa0a6',font:'#9aa0a6',
XHR:'#ea4335',fetch:'#ff6d01',datauri:'#6b7280',other:'#6366f1'
});
const Icons={
box:``,
file:``,
image:``,
video:``,
audio:``,
script:``,
stylesheet:``,
json:``,
xml:``,
font:``,
wifi:``,
download:``,
play:``,
pause:``,
copy:``,
close:``,
folder:``,
folderOpen:``,
music:``,
film:``,
link:``,
code:``,
waveform:``
};
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:'all',
isInitialized:false,
isPanelOpen:false,
isDestroyed:false,
_xhrDataMap:new WeakMap(),
_rafId:null,
_lastUpdate:0,
_refreshTimer:null,
_statsDirty:true,
_resourceObserver:null,
_listeners:[],
_bodyCheckInterval:null,
_host:null,
_shadowRoot:null,
_elements:{},
_hljsLoaded:false,
_hljsLoading:false,
_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/default.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 'mp4';
if(m.includes('audio'))return 'mp3';
if(m.includes('image'))return 'image';
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',
'mp3','wav','flac','aac','m4a','wma','oga','weba','opus','audio'].includes(t))return 'media';
if(['jpg','jpeg','png','gif','webp','svg','ico','bmp','avif','jxl','image'].includes(t))return 'image';
return 'other';
},
getPreviewUrl(r){
if(!SecurityUtils.isSafeUrl(r.url))return '';
if(r.filterType==='image')return r.url;
if(r.url.startsWith('data:')&&r.filterType==='image')return r.url;
if(r.url.startsWith('blob:')&&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'].includes(t))return Icons.video;
if(['mp3','wav','flac','aac','m4a','wma','oga','weba','opus','audio'].includes(t))return Icons.audio;
if(['jpg','jpeg','png','gif','webp','svg','ico','bmp','avif','jxl','image'].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:{position:'fixed',top:'50%',left:'50%',transform:'translate3d(-50%,-50%,0) scale(0.96)',background:'rgba(20,20,22,0.72)',color:'#f5f5f7',padding:'14px 32px',borderRadius:'24px',fontSize:'15px',fontWeight:'500',zIndex:'2147483647',pointerEvents:'none',opacity:'0',transition:'all 0.4s cubic-bezier(0.34,1.56,0.64,1)',backdropFilter:'blur(32px) saturate(220%)',webkitBackdropFilter:'blur(32px) saturate(220%)',willChange:'opacity,transform',boxShadow:'0 12px 48px rgba(0,0,0,0.16), inset 0 0.5px 0 rgba(255,255,255,0.08)',border:'0.5px solid rgba(255,255,255,0.06)'}});
el.textContent=msg;this._shadowRoot.appendChild(el);requestAnimationFrame(()=>{el.style.opacity='1';el.style.transform='translate3d(-50%,-50%,0) scale(1)';});
setTimeout(()=>{el.style.opacity='0';el.style.transform='translate3d(-50%,-50%,0) scale(0.96)';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();
setTimeout(()=>this.scanExistingResources(),500);
this.setupPeriodicScan();
this.isInitialized=true;
},
setupPeriodicScan(){
if(this._refreshTimer)clearInterval(this._refreshTimer);
this._refreshTimer=setInterval(()=>{if(!this.isPanelOpen)return;this.scanExistingResources();},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();}
},
destroyUI(){
if(this._host&&this._host.parentNode)this._host.parentNode.removeChild(this._host);
this._host=null;this._shadowRoot=null;this._elements={};
},
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:-apple-system,BlinkMacSystemFont,'HarmonyOS Sans','SF Pro Display','PingFang SC','Helvetica Neue',sans-serif;
--rd-bg:rgba(255,255,255,0.62);
--rd-bg2:rgba(245,245,247,0.72);
--rd-bg3:rgba(235,235,240,0.68);
--rd-border:rgba(0,0,0,0.04);
--rd-text:#1c1c1e;
--rd-text2:#6c6c70;
--rd-accent:#5b7fff;
--rd-accent-glow:rgba(91,127,255,0.25);
--rd-accent-hover:#4a6fe6;
--rd-shadow:0 12px 64px rgba(0,0,0,0.06),0 4px 16px rgba(0,0,0,0.02);
--rd-shadow-hover:0 20px 80px rgba(0,0,0,0.08),0 8px 24px rgba(0,0,0,0.03);
--rd-glow:0 0 60px rgba(91,127,255,0.08);
}
:host *{box-sizing:border-box;pointer-events:auto;}
.rd-container{pointer-events:auto;width:0;height:0;overflow:visible;position:relative;}
#rd-fab{position:fixed;bottom:120px;bottom:calc(120px + env(safe-area-inset-bottom, 0px));right:20px;width:54px;height:54px;border-radius:50%;background:var(--rd-bg);backdrop-filter:blur(40px) saturate(240%);-webkit-backdrop-filter:blur(40px) saturate(240%);border:0.5px solid rgba(255,255,255,0.3);box-shadow:0 8px 32px rgba(0,0,0,0.06),inset 0 0.5px 0 rgba(255,255,255,0.5);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all 0.4s cubic-bezier(0.34,1.56,0.64,1);user-select:none;will-change:transform,box-shadow;transform:translateZ(0);color:var(--rd-text);pointer-events:auto;}
#rd-fab:hover{transform:scale(1.06) translateZ(0);box-shadow:0 12px 40px rgba(0,0,0,0.08),inset 0 0.5px 0 rgba(255,255,255,0.6);}
#rd-fab:active{transform:scale(0.90) translateZ(0);}
#rd-fab svg{width:30px;height:30px;stroke:currentColor;stroke-width:1.6;}
#rd-panel{position:fixed;bottom:0;left:0;right:0;top:20px;transform:translateY(calc(100% + 20px));transition:transform 0.6s cubic-bezier(0.32,0.94,0.6,1);width:100%;height:calc(100vh - 20px);background:var(--rd-bg);backdrop-filter:blur(56px) saturate(240%);-webkit-backdrop-filter:blur(56px) saturate(240%);border-radius:32px 32px 0 0;box-shadow:var(--rd-shadow);display:flex;flex-direction:column;overflow:hidden;will-change:transform;contain:layout paint;pointer-events:auto;border:0.5px solid rgba(255,255,255,0.15);border-bottom:none;}
#rd-panel.active{transform:translateY(0);box-shadow:var(--rd-shadow-hover),var(--rd-glow);}
.rd-header{padding:18px;display:flex;align-items:center;flex-shrink:0;gap:16px;min-width:0;border-bottom:0.5px solid var(--rd-border);}
.rd-header h3{margin:0;font-size:22px;font-weight:600;color:var(--rd-text);display:flex;align-items:center;gap:10px;white-space:nowrap;flex-shrink:0;letter-spacing:-0.02em;}
.rd-header h3 svg{width:24px;height:24px;stroke:currentColor;stroke-width:1.6;}
.rd-controls{display:flex;gap:6px;flex-shrink:0;margin-left:auto;}
.rd-icon-btn{width:38px;height:38px;border:none;background:rgba(255,255,255,0.4);color:var(--rd-text);border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0;transition:all 0.3s cubic-bezier(0.34,1.56,0.64,1);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);box-shadow:0 1px 4px rgba(0,0,0,0.02);will-change:transform,background;transform:translateZ(0);pointer-events:auto;border:0.5px solid rgba(255,255,255,0.2);}
.rd-icon-btn:hover{background:rgba(255,255,255,0.6);transform:scale(1.04) translateZ(0);box-shadow:0 4px 16px rgba(0,0,0,0.04);}
.rd-icon-btn:active{transform:scale(0.90) translateZ(0);}
.rd-icon-btn svg{width:18px;height:18px;stroke:currentColor;stroke-width:1.8;}
.rd-toolbar{padding:10px 18px 12px 18px;flex-shrink:0;}
.rd-filters-scroll{overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none;-webkit-overflow-scrolling:touch;transform:translateZ(0);padding:2px 0;}
.rd-filters-scroll::-webkit-scrollbar{display:none;}
.rd-filters{display:flex;gap:10px;padding:2px 0;}
.rd-filter{padding:8px 18px;background:rgba(255,255,255,0.4);border-radius:9999px;cursor:pointer;font-size:13px;font-weight:500;color:var(--rd-text2);transition:all 0.3s cubic-bezier(0.34,1.56,0.64,1);border:0.5px solid rgba(255,255,255,0.2);display:inline-flex;align-items:center;gap:6px;white-space:nowrap;will-change:transform,background,box-shadow,color;transform:translateZ(0);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);box-shadow:0 1px 4px rgba(0,0,0,0.02);pointer-events:auto;}
.rd-filter:hover{color:var(--rd-text);background:rgba(255,255,255,0.6);transform:translateY(-1px) translateZ(0);box-shadow:0 4px 16px rgba(0,0,0,0.04);}
.rd-filter.active{background:var(--rd-accent);color:#fff;box-shadow:0 8px 32px var(--rd-accent-glow),inset 0 0.5px 0 rgba(255,255,255,0.2);transform:translateY(0) translateZ(0);}
.rd-filter svg{width:16px;height:16px;display:inline-block;flex-shrink:0;stroke:currentColor;stroke-width:1.8;}
.rd-filter-count{font-size:11px;font-weight:600;margin-left:1px;opacity:0.6;}
.rd-filter.active .rd-filter-count{opacity:1;}
.rd-list{flex:1;overflow-y:auto;padding:6px 18px 18px 18px;padding:6px 18px calc(18px + env(safe-area-inset-bottom, 0px)) 18px;scroll-behavior:smooth;-webkit-overflow-scrolling:touch;contain:layout;will-change:scroll-position;}
.rd-list::-webkit-scrollbar{width:4px;}
.rd-list::-webkit-scrollbar-track{background:transparent;}
.rd-list::-webkit-scrollbar-thumb{background:var(--rd-border);border-radius:8px;}
.rd-list{scrollbar-width:thin;scrollbar-color:var(--rd-border) transparent;}
.rd-list-content{display:flex;flex-direction:column;gap:12px;contain:layout;padding-bottom:6px;}
.rd-entry{display:flex;gap:14px;padding:12px 16px;background:rgba(255,255,255,0.45);border-radius:20px;transition:all 0.4s cubic-bezier(0.34,1.56,0.64,1);cursor:pointer;align-items:center;min-height:64px;will-change:transform,box-shadow,border-color;transform:translateZ(0);contain:layout;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:0.5px solid rgba(255,255,255,0.2);box-shadow:0 2px 8px rgba(0,0,0,0.02);pointer-events:auto;}
.rd-entry:hover{transform:translateX(4px) translateZ(0);box-shadow:0 8px 32px rgba(0,0,0,0.04),0 2px 8px rgba(0,0,0,0.02);border-color:rgba(91,127,255,0.2);background:rgba(255,255,255,0.55);}
.rd-entry:active{transform:scale(0.98) translateZ(0);}
.rd-entry-thumb-wrapper{width:48px;height:48px;flex-shrink:0;display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,0.5);border-radius:16px;overflow:hidden;align-self:center;contain:strict;box-shadow:inset 0 1px 4px rgba(0,0,0,0.03);border:0.5px solid rgba(255,255,255,0.2);}
.rd-entry-thumb{width:100%;height:100%;object-fit:cover;will-change:transform;border-radius:16px;}
.rd-entry-fallback{display:flex;align-items:center;justify-content:center;color:var(--rd-text2);}
.rd-entry-fallback svg{width:26px;height:26px;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;}
.rd-entry-header{display:flex;align-items:center;gap:10px;height:24px;}
.rd-entry-type{color:#fff;padding:0 12px;border-radius:32px;font-size:9px;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;will-change:transform;transform:translateZ(0);box-shadow:0 2px 12px rgba(0,0,0,0.06);}
${Object.entries(TYPE_COLORS).map(([t,c])=>`.rd-entry-type.${t}{background:${c}!important;}`).join('')}
.rd-entry-actions{display:flex;gap:2px;margin-left:auto;align-items:center;}
.rd-action-btn{cursor:pointer;width:32px;height:32px;border-radius:50%;background:rgba(255,255,255,0.2);transition:all 0.25s cubic-bezier(0.34,1.56,0.64,1);border:0.5px solid rgba(255,255,255,0.1);display:flex;align-items:center;justify-content:center;padding:0;color:var(--rd-text2);will-change:transform,background,color,box-shadow;transform:translateZ(0);pointer-events:auto;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);}
.rd-action-btn:hover{background:rgba(255,255,255,0.5);transform:scale(1.08) translateZ(0);color:var(--rd-text);box-shadow:0 4px 16px rgba(0,0,0,0.04);}
.rd-action-btn:active{transform:scale(0.88) translateZ(0);}
.rd-action-btn svg{width:16px;height:16px;stroke:currentColor;stroke-width:1.8;pointer-events:none;}
.rd-download-btn{color:var(--rd-accent);}
.rd-download-btn:hover{color:var(--rd-accent-hover);background:rgba(91,127,255,0.08);}
.rd-play-btn{color:#ff3b30;}
.rd-play-btn:hover{background:rgba(255,59,48,0.08);color:#e0352b;}
.rd-copy-btn{color:var(--rd-text2);}
.rd-copy-btn:hover{background:rgba(0,0,0,0.04);color:var(--rd-text);}
.rd-entry-url{word-break:break-all;color:var(--rd-text2);font-family:'SF Mono',Monaco,'HarmonyOS Sans Mono',Consolas,monospace;font-size:10px;line-height:1.5;max-height:3em;overflow:hidden;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;contain:layout;opacity:0.5;transition:opacity 0.3s;}
.rd-entry:hover .rd-entry-url{opacity:0.8;}
.rd-empty{text-align:center;color:var(--rd-text2);padding:60px 20px;font-size:15px;display:flex;flex-direction:column;align-items:center;gap:20px;contain:layout;opacity:0.5;}
.rd-empty svg{width:64px;height:64px;stroke:currentColor;stroke-width:1.2;opacity:0.3;}
#rd-inline-player,#rd-img-preview,#rd-text-preview,#rd-source-preview{position:fixed;z-index:2147483647;will-change:transform,opacity;transform:translate3d(-50%,-50%,0);backdrop-filter:blur(56px) saturate(240%);-webkit-backdrop-filter:blur(56px) saturate(240%);pointer-events:auto;animation:rdFadeIn 0.5s cubic-bezier(0.34,1.56,0.64,1);border:0.5px solid rgba(255,255,255,0.12);border-radius:32px;}
@keyframes rdFadeIn{0%{opacity:0;transform:translate3d(-50%,-50%,0) scale(0.94);}100%{opacity:1;transform:translate3d(-50%,-50%,0) scale(1);}}
#rd-inline-player{top:50%;left:50%;width:92%;max-width:780px;height:82vh;max-height:82vh;background:rgba(255,255,255,0.68);overflow:hidden;box-shadow:0 40px 100px rgba(0,0,0,0.12),0 8px 32px rgba(0,0,0,0.04);display:flex;flex-direction:column;contain:layout paint;}
#rd-player-header{display:flex;align-items:center;justify-content:space-between;padding:18px;background:transparent;flex-shrink:0;height:auto;contain:layout;}
#rd-player-title{color:var(--rd-text);font-size:17px;font-weight:600;display:flex;align-items:center;gap:10px;letter-spacing:-0.01em;}
#rd-player-title svg{width:22px;height:22px;stroke:currentColor;stroke-width:1.6;}
#rd-media-wrapper{flex:1;display:flex;align-items:center;justify-content:center;background:transparent;overflow:hidden;min-height:0;position:relative;width:100%;contain:layout paint;padding:4px 18px 18px 18px;}
#rd-video{width:100%;height:100%;max-width:100%;max-height:100%;object-fit:contain;display:block;will-change:transform;border-radius:16px;box-shadow:0 8px 32px rgba(0,0,0,0.04);}
.rd-custom-audio{width:100%;padding:24px 28px;display:flex;flex-direction:column;gap:16px;background:rgba(255,255,255,0.4);border-radius:20px;margin:8px 0;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);border:0.5px solid rgba(255,255,255,0.15);}
.rd-audio-info{display:flex;align-items:center;gap:12px;color:var(--rd-text);font-size:13px;font-weight:500;word-break:break-all;}
.rd-audio-info svg{width:28px;height:28px;stroke:currentColor;stroke-width:1.6;flex-shrink:0;opacity:0.7;}
.rd-audio-filename{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.rd-audio-controls{display:flex;align-items:center;gap:12px;flex-wrap:wrap;}
.rd-play-pause-btn{width:44px;height:44px;border-radius:50%;background:var(--rd-accent);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 32px var(--rd-accent-glow);pointer-events:auto;}
.rd-play-pause-btn:hover{transform:scale(1.05);box-shadow:0 12px 48px var(--rd-accent-glow);}
.rd-play-pause-btn:active{transform:scale(0.88);}
.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:3px;background:rgba(0,0,0,0.06);border-radius:6px;position:relative;overflow:hidden;}
.rd-progress-filled{position:absolute;left:0;top:0;height:100%;width:0;background:var(--rd-accent);border-radius:6px;pointer-events:none;transition:width 0.08s;}
.rd-progress-thumb{position:absolute;top:50%;width:14px;height:14px;background:var(--rd-accent);border-radius:50%;transform:translate(-50%,-50%);pointer-events:none;box-shadow:0 2px 16px var(--rd-accent-glow);transition:box-shadow 0.2s,transform 0.2s;border:2px solid rgba(255,255,255,0.3);}
.rd-progress-container:hover .rd-progress-thumb{transform:translate(-50%,-50%) scale(1.1);box-shadow:0 4px 24px var(--rd-accent-glow);}
.rd-time{font-size:12px;font-family:'SF Mono',Monaco,'HarmonyOS Sans Mono',Consolas,monospace;color:var(--rd-text2);display:flex;gap:4px;flex-shrink:0;font-weight:500;}
#rd-img-preview{top:50%;left:50%;width:92%;max-width:780px;height:82vh;max-height:82vh;background:rgba(255,255,255,0.68);display:flex;flex-direction:column;overflow:hidden;box-shadow:0 40px 100px rgba(0,0,0,0.12),0 8px 32px rgba(0,0,0,0.04);}
#rd-img-preview-header{display:flex;align-items:center;justify-content:space-between;padding:18px;background:transparent;height:auto;flex-shrink:0;contain:layout;}
#rd-img-preview-title{color:var(--rd-text);font-size:17px;font-weight:600;display:flex;align-items:center;gap:10px;}
#rd-img-preview-title svg{width:22px;height:22px;stroke:currentColor;stroke-width:1.6;}
#rd-img-preview-content{flex:1;display:flex;align-items:center;justify-content:center;overflow:hidden;padding:18px;background:transparent;cursor:zoom-out;contain:layout paint;}
#rd-preview-img{max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain;border-radius:16px;cursor:default;will-change:transform;box-shadow:0 8px 40px rgba(0,0,0,0.04);}
#rd-text-preview,#rd-source-preview{top:50%;left:50%;width:92%;max-width:780px;height:82vh;background:rgba(255,255,255,0.68);box-shadow:0 40px 100px rgba(0,0,0,0.12),0 8px 32px rgba(0,0,0,0.04);display:flex;flex-direction:column;overflow:hidden;}
#rd-text-preview-header,#rd-source-preview-header{display:flex;align-items:center;justify-content:space-between;padding:18px;background:transparent;height:auto;contain:layout;flex-shrink:0;}
#rd-text-preview-title,#rd-source-preview-title{font-weight:600;color:var(--rd-text);font-size:17px;display:flex;align-items:center;gap:10px;}
#rd-text-preview-title svg,#rd-source-preview-title svg{width:22px;height:22px;stroke:currentColor;stroke-width:1.6;}
#rd-text-preview-scroll,#rd-source-preview-scroll{flex:1;overflow:auto;padding:18px;background:transparent;-webkit-overflow-scrolling:touch;}
#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);border-radius:8px;}
#rd-text-preview-content,#rd-source-preview-content{margin:0;padding:0;font-family:'SF Mono',Monaco,'HarmonyOS Sans Mono',Consolas,monospace;font-size:13px;line-height:1.8;color:var(--rd-text);white-space:pre;word-break:normal;overflow-x:auto;background:transparent;border:none;outline:none;}
.rd-iframe-container{flex:1;position:relative;background:transparent;overflow:hidden;contain:layout paint;border-radius:0 0 32px 32px;}
.rd-iframe-container iframe{width:100%;height:100%;border:none;background:transparent;}
*{outline:none!important;-webkit-tap-highlight-color:transparent!important;}
@media (prefers-color-scheme: dark){
:host{
--rd-bg:rgba(28,28,30,0.78);
--rd-bg2:rgba(44,44,46,0.82);
--rd-bg3:rgba(58,58,60,0.78);
--rd-border:rgba(255,255,255,0.05);
--rd-text:#f5f5f7;
--rd-text2:#98989e;
--rd-accent:#5b7fff;
--rd-accent-glow:rgba(91,127,255,0.3);
--rd-accent-hover:#7a96ff;
--rd-shadow:0 12px 64px rgba(0,0,0,0.2),0 4px 16px rgba(0,0,0,0.08);
--rd-shadow-hover:0 20px 80px rgba(0,0,0,0.24),0 8px 24px rgba(0,0,0,0.1);
--rd-glow:0 0 60px rgba(91,127,255,0.06);
}
#rd-fab{background:rgba(28,28,30,0.7);border-color:rgba(255,255,255,0.06);}
#rd-panel{background:rgba(28,28,30,0.78);border-color:rgba(255,255,255,0.05);}
.rd-header h3{color:#f5f5f7;}
.rd-icon-btn{background:rgba(255,255,255,0.05);border-color:rgba(255,255,255,0.04);color:#f5f5f7;}
.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-text2);}
.rd-filter:hover{background:rgba(255,255,255,0.08);color:#f5f5f7;}
.rd-filter.active{background:var(--rd-accent);color:#fff;}
.rd-entry{background:rgba(255,255,255,0.04);border-color:rgba(255,255,255,0.04);}
.rd-entry:hover{background:rgba(255,255,255,0.08);border-color:rgba(91,127,255,0.15);}
.rd-entry-thumb-wrapper{background:rgba(255,255,255,0.04);border-color:rgba(255,255,255,0.04);}
.rd-action-btn{background:rgba(255,255,255,0.04);border-color:rgba(255,255,255,0.03);}
.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.04);}
.rd-progress-bg{background:rgba(255,255,255,0.06);}
#rd-inline-player,#rd-img-preview,#rd-text-preview,#rd-source-preview{background:rgba(28,28,30,0.82);border-color:rgba(255,255,255,0.04);}
.rd-entry-url{color:#98989e;}
.rd-entry-type{box-shadow:0 2px 12px rgba(0,0,0,0.15);}
}
`;
this._shadowRoot.appendChild(style);
},
getFilterIcon(type){const map={all:Icons.folder,media:Icons.waveform,image:Icons.image,other:Icons.file};return map[type]||Icons.file;},
getFilterName(type){const names={all:'全部',media:'媒体',image:'图片',other:'其他'};return names[type]||type;},
createUI(){
if(!this._shadowRoot)return;
const c=document.createElement('div');c.className='rd-container';
c.innerHTML=`
${Icons.box}
`;
this._shadowRoot.appendChild(c);
this._elements={floatingBtn:this._shadowRoot.getElementById('rd-fab'),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')};
},
bindEvents(){
if(!this._shadowRoot)return;
const root=this._shadowRoot;
root.addEventListener('click',e=>{
const t=e.target.closest?e.target.closest(e.target.tagName==='svg'?'svg':'*'):e.target;
if(t.closest('#rd-close')){this.hidePanel();return;}
if(t.closest('#rd-view-source')){this.viewSource();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);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);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 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=['all','media','image','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});}
},
setupKeyboardShortcuts(){
const handler=e=>{
if(e.key==='Escape'){
if(this._shadowRoot){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(this.isPanelOpen)this.hidePanel();
}
};
document.addEventListener('keydown',handler);
this._listeners.push(()=>document.removeEventListener('keydown',handler));
},
setFilter(filter){this.currentFilter=filter;this.updateFilterButtons();this.scrollFilterToActive();this.scheduleUpdate();},
togglePanel(){
const p=this._elements.panel;if(!p)return;
if(p.classList.contains('active')){this.hidePanel();}else{p.classList.add('active');this.isPanelOpen=true;this.manualRefresh();this.scheduleUpdate();this.scrollFilterToActive();}
},
hidePanel(){
const p=this._elements.panel;if(p)p.classList.remove('active');this.isPanelOpen=false;
if(this._shadowRoot){setTimeout(()=>{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);}
},
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='预览';
content.appendChild(img);
preview.appendChild(header);preview.appendChild(content);
const closeHandler=()=>preview.remove();
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);
},
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.length>50000?text.slice(0,50000)+'\n\n... (内容过长已截断)':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';
pre.style.wordBreak='normal';
pre.style.overflowX='auto';
scroll.appendChild(pre);preview.appendChild(scroll);
const handler=()=>preview.remove();
close.addEventListener('click',handler);
preview.addEventListener('click',e=>{if(e.target===preview||e.target.id==='rd-text-preview-scroll')handler();});
this._shadowRoot.appendChild(preview);
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();
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);
},
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 copy=SecurityUtils.createElement('button',{className:'rd-icon-btn',id:'rd-source-copy',title:'复制'});copy.innerHTML=Icons.copy;
const close=SecurityUtils.createElement('button',{className:'rd-icon-btn',id:'rd-source-close',title:'关闭'});close.innerHTML=Icons.close;
controls.appendChild(copy);controls.appendChild(close);
header.appendChild(title);header.appendChild(controls);
preview.appendChild(header);
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';
pre.style.wordBreak='normal';
pre.style.overflowX='auto';
scroll.appendChild(pre);preview.appendChild(scroll);
const handler=()=>preview.remove();
close.addEventListener('click',handler);
copy.addEventListener('click',()=>{this.copyToClipboard(src);});
preview.addEventListener('click',e=>{if(e.target===preview||e.target.id==='rd-source-preview-scroll')handler();});
this._shadowRoot.appendChild(preview);
this._applyHighlight(pre,'html');
},
setupSPASupport(){
let lastUrl=location.href;
const check=()=>{
if(location.href!==lastUrl){lastUrl=location.href;this.resources=[];this.filteredResources=[];processedUrls.clear();this._statsDirty=true;this.scheduleUpdate();setTimeout(()=>{this.scanExistingResources();if(!this._host||!this._host.parentNode)this.setupUI();},500);}
};
const origPush=OriginalAPIs.pushState;const origReplace=OriginalAPIs.replaceState;
history.pushState=function(...args){origPush.apply(this,args);setTimeout(check,50);};
history.replaceState=function(...args){origReplace.apply(this,args);setTimeout(check,50);};
const pop=()=>setTimeout(check,50);const hash=check;
window.addEventListener('popstate',pop);window.addEventListener('hashchange',hash);
this._listeners.push(()=>{window.removeEventListener('popstate',pop);window.removeEventListener('hashchange',hash);});
},
scanExistingResources(){
const processImages=()=>{const imgs=document.querySelectorAll('img[src], img[srcset]');for(let i=0;i{const videos=document.querySelectorAll('video[src], video source[src], video[data-src]');const audios=document.querySelectorAll('audio[src], audio source[src], audio[data-src]');[...videos,...audios].slice(0,CONFIG.BATCH_SIZE).forEach(el=>{if(el.src)this.tryAddMediaUrl(el.src,el.tagName.toLowerCase());if(el.dataset?.src)this.tryAddMediaUrl(el.dataset.src,el.tagName.toLowerCase());});};
const processShadowDOM=()=>{document.querySelectorAll('*').forEach(el=>{if(el.shadowRoot){el.shadowRoot.querySelectorAll('video[src], video source[src]').forEach(v=>{if(v.src)this.tryAddMediaUrl(v.src,'video');if(v.dataset?.src)this.tryAddMediaUrl(v.dataset.src,'video');});}});};
const processIframes=()=>{document.querySelectorAll('iframe').forEach(iframe=>{try{const doc=iframe.contentDocument||iframe.contentWindow?.document;if(doc){doc.querySelectorAll('video[src], video source[src]').forEach(v=>{if(v.src)this.tryAddMediaUrl(v.src,'video');});}}catch(e){}});};
const processBgImages=()=>{const els=document.querySelectorAll('body, body *');let idx=0;const batch=()=>{const end=Math.min(idx+CONFIG.BATCH_SIZE,els.length);for(;idx{const u=m.replace(/url\(["']?/,'').replace(/["']?\)$/,'');if(u.startsWith('data:'))this.tryAddDataUri(u,'css-bg');});}const blobMatches=bg.match(/url\(["']?(blob:[^"')]+)["']?\)/g);if(blobMatches){blobMatches.forEach(m=>{const u=m.replace(/url\(["']?/,'').replace(/["']?\)$/,'');if(u.startsWith('blob:'))this.tryAddBlobUrl(u,'css-bg','image');});}}}catch(e){}}if(idxrequestIdleCallback(processBgImages),1000);}else{setTimeout(processImages,1);setTimeout(processMedia,100);setTimeout(processShadowDOM,200);setTimeout(processIframes,300);setTimeout(processBgImages,1000);}
},
captureImageElement(img){
if(processedElements.has(img))return;processedElements.add(img);
const src=img.currentSrc||img.src;if(src){if(src.startsWith('data:'))this.tryAddDataUri(src,'img');else if(src.startsWith('blob:'))this.tryAddBlobUrl(src,'img','image');else this.tryAddImageUrl(src);}
if(img.srcset){img.srcset.split(',').forEach(s=>{const u=s.trim().split(' ')[0];if(u){if(u.startsWith('data:'))this.tryAddDataUri(u,'srcset');else if(u.startsWith('blob:'))this.tryAddBlobUrl(u,'srcset','image');else this.tryAddImageUrl(u);}});}
},
tryAddMediaUrl(url,tagName){
if(!url)return;if(processedUrls.has(url))return;
const isAudio=tagName==='audio';const ext=url.split('.').pop()?.split('?')[0].toLowerCase()||'';
const audioExts=new Set(['mp3','wav','flac','aac','m4a','wma','oga','weba','opus']);
const videoExts=new Set(['mp4','webm','ogg','ogv','mov','avi','mkv','flv','m3u8','mpd']);
let displayType;
if(isAudio||audioExts.has(ext))displayType=EXT_TO_TYPE[ext]||'mp3';
else if(videoExts.has(ext))displayType=EXT_TO_TYPE[ext]||'mp4';
else return;
processedUrls.add(url);this.addResource({url,displayType,filterType:'media',status:200});
},
tryAddBlobUrl(url,source,suggestedType){
if(!SecurityUtils.isSafeUrl(url))return;if(processedUrls.has(url))return;processedUrls.add(url);
let displayType=suggestedType||this.getDisplayType(url,'');
const filterType=this.getFilterType(displayType);
this.addResource({url,displayType,filterType,status:200,size:0,method:'BLOB',source});
},
tryAddImageUrl(url){
if(!url||processedUrls.has(url))return;const ext=url.split('.').pop()?.split('?')[0].toLowerCase()||'';
if(new Set(['jpg','jpeg','png','gif','webp','svg','ico','bmp','avif','jxl']).has(ext)){
processedUrls.add(url);this.addResource({url,displayType:EXT_TO_TYPE[ext]||'image',filterType:'image',status:200});
}
},
tryAddDataUri(url,source){
if(!SecurityUtils.isSafeUrl(url))return;if(processedUrls.has(url))return;processedUrls.add(url);
const displayType=this.getDisplayType(url,'');const filterType=this.getFilterType(displayType);
this.addResource({url,displayType,filterType,status:200,size:url.length,method:'DATA',source});
},
setupResourceObserver(){
if(this._resourceObserver)this._resourceObserver.disconnect();
this._resourceObserver=new MutationObserver(mutations=>{
for(const m of mutations){
for(const node of m.addedNodes){
if(node.nodeType!==1)continue;
if(node.tagName==='IMG')this.captureImageElement(node);
else if(node.tagName==='VIDEO'||node.tagName==='AUDIO')this.captureMediaElement(node);
else if(node.tagName==='IFRAME'){setTimeout(()=>{try{const doc=node.contentDocument||node.contentWindow?.document;if(doc){doc.querySelectorAll('video[src], video source[src]').forEach(v=>{if(v.src)this.tryAddMediaUrl(v.src,'video');});}}catch(e){}},1000);}
else if(node.querySelectorAll){
node.querySelectorAll('img[src], img[srcset]').forEach(img=>this.captureImageElement(img));
node.querySelectorAll('video, audio').forEach(el=>this.captureMediaElement(el));
node.querySelectorAll('*').forEach(el=>{if(el.shadowRoot){el.shadowRoot.querySelectorAll('video[src], video source[src]').forEach(v=>{if(v.src)this.tryAddMediaUrl(v.src,'video');});}});
}
}
if(m.type==='attributes'&&(m.target.tagName==='VIDEO'||m.target.tagName==='AUDIO')){
const ns=m.target.src||m.target.dataset?.src;if(ns)this.tryAddMediaUrl(ns,m.target.tagName.toLowerCase());
}
}
});
this._resourceObserver.observe(document.body,{childList:true,subtree:true,attributes:true,attributeFilter:['src','data-src']});
},
captureMediaElement(el){
if(processedElements.has(el))return;processedElements.add(el);
const observers=[];
const processSources=()=>{
const src=el.currentSrc||el.src||el.dataset?.src;if(src)this.tryAddMediaUrl(src,el.tagName.toLowerCase());
el.querySelectorAll('source').forEach(source=>{
const s=source.src||source.dataset?.src;if(!s||processedUrls.has(s))return;
const type=source.type||'';const ext=s.split('.').pop()?.split('?')[0].toLowerCase()||'';
const audioExts=new Set(['mp3','wav','flac','aac','m4a','wma','oga','weba','opus']);
const videoExts=new Set(['mp4','webm','ogg','ogv','mov','avi','mkv','flv','m3u8','mpd']);
let displayType;
if(type.includes('audio')||audioExts.has(ext))displayType=EXT_TO_TYPE[ext]||'mp3';
else if(type.includes('video')||videoExts.has(ext))displayType=EXT_TO_TYPE[ext]||'mp4';
else return;
processedUrls.add(s);this.addResource({url:s,displayType,filterType:'media',status:200});
});
};
processSources();
const loadHandler=()=>processSources();
el.addEventListener('loadstart',loadHandler,{once:true});
el.addEventListener('loadedmetadata',loadHandler,{once:true});
const attrObs=new MutationObserver(muts=>{muts.forEach(m=>{if(m.attributeName==='src'||m.attributeName==='data-src'){const ns=el.src||el.dataset?.src;if(ns)this.tryAddMediaUrl(ns,el.tagName.toLowerCase());}});});
attrObs.observe(el,{attributes:true,attributeFilter:['src','data-src']});
observers.push(attrObs);
if('IntersectionObserver' in window){const io=new IntersectionObserver(entries=>{entries.forEach(e=>{if(e.isIntersecting)processSources();});},{rootMargin:CONFIG.LAZY_LOAD_OFFSET});io.observe(el);observers.push(io);}
const cleanObs=new MutationObserver(muts=>{muts.forEach(m=>{m.removedNodes.forEach(node=>{if(node===el||(node.contains&&node.contains(el))){observers.forEach(o=>o.disconnect());cleanObs.disconnect();}});});});
if(document.body)cleanObs.observe(document.body,{childList:true,subtree:true});
},
setupResourceCapture(){
const processEntry=(entry)=>{
if(!entry.name)return;const url=entry.name;if(!SecurityUtils.isSafeUrl(url))return;if(processedUrls.has(url))return;
const displayType=this.getDisplayType(url,entry.responseContentType,entry.initiatorType);
const filterType=this.getFilterType(displayType);
if(filterType==='other'&&(url.includes('.m3u8')||url.includes('.mpd')||url.includes('manifest')||entry.responseContentType?.includes('mpegurl'))){
const ext=url.includes('.m3u8')?'m3u8':url.includes('.mpd')?'mpd':'mp4';
processedUrls.add(url);this.addResource({url,displayType:ext,filterType:'media',status:entry.responseStatus||200,size:entry.transferSize||entry.encodedBodySize||0,duration:entry.duration||0,cached:entry.transferSize===0&&entry.encodedBodySize>0});return;
}
if(filterType==='other'&&!entry.responseContentType?.includes('video')&&!entry.responseContentType?.includes('audio'))return;
processedUrls.add(url);let size=entry.transferSize||entry.encodedBodySize||0;if((url.startsWith('data:')||url.startsWith('blob:'))&&size===0)size=url.length;
this.addResource({url,displayType,filterType,status:entry.responseStatus||200,size,duration:entry.duration||0,cached:entry.transferSize===0&&entry.encodedBodySize>0});
};
performance.getEntriesByType('resource').forEach(processEntry);
try{const obs=new PerformanceObserver(list=>{list.getEntries().forEach(processEntry);});obs.observe({type:'resource',buffered:true});this._listeners.push(()=>obs.disconnect());}catch(e){}
const bufHandler=()=>{performance.clearResourceTimings();};performance.addEventListener('resourcetimingbufferfull',bufHandler);this._listeners.push(()=>performance.removeEventListener('resourcetimingbufferfull',bufHandler));
const self=this;
XMLHttpRequest.prototype.open=function(method,url){const data={method,url:String(url),startTime:performance.now()};self._xhrDataMap.set(this,data);return OriginalAPIs.xhrOpen.apply(this,arguments);};
XMLHttpRequest.prototype.send=function(){const xhr=this;const data=self._xhrDataMap.get(xhr);if(!data)return OriginalAPIs.xhrSend.apply(this,arguments);const loadHandler=()=>{try{if(self.isDestroyed)return;const rurl=xhr.responseURL||data.url;if(!SecurityUtils.isSafeUrl(rurl))return;if(processedUrls.has(rurl))return;const displayType=self.getDisplayType(rurl,xhr.getResponseHeader('content-type'),'xmlhttprequest');const filterType=self.getFilterType(displayType);processedUrls.add(rurl);self.addResource({url:rurl,displayType,filterType,status:xhr.status,size:xhr.responseText?.length||0,duration:performance.now()-data.startTime,method:data.method});}catch(e){}self._xhrDataMap.delete(xhr);};xhr.addEventListener('load',loadHandler,{once:true});return OriginalAPIs.xhrSend.apply(this,arguments);};
window.fetch=async (input,init)=>{if(self.isDestroyed)return OriginalAPIs.fetch(input,init);const url=typeof input==='string'?input:input?.url||input;if(!url||!SecurityUtils.isSafeUrl(url))return OriginalAPIs.fetch(input,init);const method=init?.method||'GET';const start=performance.now();const resp=await OriginalAPIs.fetch(input,init);Promise.resolve().then(async ()=>{try{if(!resp.ok||resp.type==='opaque')return;if(processedUrls.has(url))return;const clone=resp.clone();const displayType=self.getDisplayType(url,clone.headers.get('content-type'),'fetch');const filterType=self.getFilterType(displayType);processedUrls.add(url);let size=0;try{const blob=await clone.blob();size=blob.size;}catch(e){size=parseInt(clone.headers.get('content-length'))||0;}self.addResource({url,displayType,filterType,status:clone.status,size,duration:performance.now()-start,method});}catch(e){}});return resp;};
},
addResource(resource){
if(!SecurityUtils.isSafeUrl(resource.url))return;
const id=`${Date.now()}_${Math.random().toString(36).slice(2)}_${Math.random().toString(36).slice(2)}`;
this.resources.unshift({id,url:resource.url,displayType:resource.displayType||'other',filterType:resource.filterType||'other',status:resource.status||0,size:resource.size||0,duration:resource.duration||0,method:resource.method||'GET',timestamp:new Date().toLocaleTimeString('zh-CN'),cached:resource.cached||false});
if(this.resources.length>CONFIG.MAX_RESOURCES){const removed=this.resources.splice(CONFIG.MAX_RESOURCES);removed.forEach(r=>processedUrls.delete(r.url));}
if(processedUrls.size>CONFIG.MAX_URL_CACHE){const recent=this.resources.slice(0,Math.floor(CONFIG.MAX_URL_CACHE/2));processedUrls.clear();recent.forEach(r=>processedUrls.add(r.url));}
this._statsDirty=true;this.scheduleUpdate();
},
scheduleUpdate(){
if(this._rafId)return;
const now=performance.now();const elapsed=now-this._lastUpdate;
if(elapsed{this._rafId=null;if(performance.now()-this._lastUpdate>=CONFIG.UPDATE_DELAY)this._doUpdate();else this.scheduleUpdate();});}
else this._doUpdate();
},
_doUpdate(){this._lastUpdate=performance.now();this.filterResources();this.renderList();this.updateStats();},
filterResources(){this.filteredResources=this.resources.filter(r=>this.currentFilter==='all'||r.filterType===this.currentFilter);},
renderList(){
const list=this._elements.list;if(!list)return;const scrollTop=list.scrollTop;const content=this._elements.listContent;if(!content)return;
content.innerHTML='';
if(this.filteredResources.length===0){content.innerHTML=``;return;}
const frag=document.createDocumentFragment();this.filteredResources.forEach(r=>frag.appendChild(this.createEntryElement(r)));content.appendChild(frag);
if(scrollTop>0){requestAnimationFrame(()=>{list.scrollTop=scrollTop;});}
},
createEntryElement(r){
const preview=this.getPreviewUrl(r);const dispUrl=this.formatUrl(r.url);const icon=this.getTypeIcon(r.displayType);const isMedia=r.filterType==='media';const isImage=r.filterType==='image';const isDataUri=r.displayType==='datauri';
const entry=document.createElement('div');entry.className='rd-entry';entry.dataset.url=r.url;entry.dataset.displayType=r.displayType;entry.title=isMedia?'点击播放':(isImage||isDataUri)?'点击查看图片':'点击查看内容';
const thumb=document.createElement('div');thumb.className='rd-entry-thumb-wrapper';thumb.dataset.url=r.url;
if((isImage||isDataUri)&&preview){const img=document.createElement('img');img.className='rd-entry-thumb';img.src=preview;img.loading='lazy';img.decoding='async';img.onerror=function(){this.style.display='none';this.nextElementSibling.style.display='flex';};thumb.appendChild(img);}
const fallback=document.createElement('div');fallback.className='rd-entry-fallback';fallback.style.display=(isImage||isDataUri)&&preview?'none':'flex';fallback.innerHTML=icon;thumb.appendChild(fallback);
const content=document.createElement('div');content.className='rd-entry-content';
const header=document.createElement('div');header.className='rd-entry-header';
const typeSpan=document.createElement('span');typeSpan.className=`rd-entry-type ${r.displayType}`;typeSpan.textContent=r.displayType;
const actions=document.createElement('div');actions.className='rd-entry-actions';
if(isMedia){const play=document.createElement('button');play.className='rd-action-btn rd-play-btn';play.dataset.url=r.url;play.title='播放';play.innerHTML=Icons.play;actions.appendChild(play);}
const copy=document.createElement('button');copy.className='rd-action-btn rd-copy-btn';copy.dataset.url=r.url;copy.title='复制';copy.innerHTML=Icons.copy;
const download=document.createElement('button');download.className='rd-action-btn rd-download-btn';download.dataset.url=r.url;download.title='下载';download.innerHTML=Icons.download;
actions.appendChild(copy);actions.appendChild(download);
header.appendChild(typeSpan);header.appendChild(actions);
const urlDiv=document.createElement('div');urlDiv.className='rd-entry-url';urlDiv.textContent=dispUrl;
content.appendChild(header);content.appendChild(urlDiv);
entry.appendChild(thumb);entry.appendChild(content);
return entry;
},
updateStats(){
if(!this._statsDirty)return;this._statsDirty=false;const stats={all:0,media:0,image:0,other:0};
for(const r of this.resources){stats.all++;stats[r.filterType]++;}
if(this._shadowRoot){this._shadowRoot.querySelectorAll('.rd-filter-count').forEach(el=>{const t=el.dataset.count;if(stats.hasOwnProperty(t))el.textContent=stats[t];});}
},
extractRealMediaUrl(url){
try{if(url.match(/^https?:\/\/[^\s&]+$/))return url;const u=new URL(url,location.href);const params=u.searchParams;const candidates=['url','src','link','file','play','video','audio','stream','source','v','u'];for(const p of candidates){const val=params.get(p);if(val){const dec=decodeURIComponent(val);if(dec.startsWith('http'))return dec;}}if(url.includes('play')||url.includes('player')||url.includes('embed')){const m=url.match(/https?:\/\/[^\s&"']+\.(m3u8|mp4|webm|mkv|flv|mp3|aac)/i);if(m)return m[0];}return url;}catch(e){return url;}
},
_createAudioPlayerUI(url,mime,originalUrl){
const wrap=document.createElement('div');wrap.className='rd-custom-audio';
let fname='音频';try{const u=new URL(url,location.href);const parts=decodeURIComponent(u.pathname).split('/');const last=parts.pop();if(last&&last.includes('.'))fname=last;else fname='音频流';}catch(e){fname='音频';}
const info=document.createElement('div');info.className='rd-audio-info';info.innerHTML=`${Icons.music}${SecurityUtils.sanitizeAttr(fname)}`;
const ctrl=document.createElement('div');ctrl.className='rd-audio-controls';
const pp=document.createElement('button');pp.className='rd-play-pause-btn';pp.innerHTML=Icons.play;
const prog=document.createElement('div');prog.className='rd-progress-container';
const bg=document.createElement('div');bg.className='rd-progress-bg';
const fill=document.createElement('div');fill.className='rd-progress-filled';
const thumb=document.createElement('div');thumb.className='rd-progress-thumb';
prog.appendChild(bg);prog.appendChild(fill);prog.appendChild(thumb);
const time=document.createElement('div');time.className='rd-time';time.innerHTML='0:00/0:00';
ctrl.appendChild(pp);ctrl.appendChild(prog);ctrl.appendChild(time);
wrap.appendChild(info);wrap.appendChild(ctrl);
const audio=document.createElement('audio');audio.preload='auto';audio.style.position='absolute';audio.style.width='0';audio.style.height='0';audio.style.opacity='0';audio.style.pointerEvents='none';
const src=document.createElement('source');src.src=url;src.type=mime;audio.appendChild(src);audio.appendChild(document.createTextNode('不支持该格式'));
let dragging=false;let anim=null;
const fmt=s=>{if(isNaN(s))return '0:00';const m=Math.floor(s/60);const sec=Math.floor(s%60);return `${m}:${sec.toString().padStart(2,'0')}`;};
const update=()=>{if(!audio.duration||isNaN(audio.duration))return;const p=(audio.currentTime/audio.duration)*100;fill.style.width=`${p}%`;const rect=prog.getBoundingClientRect();thumb.style.left=`${(p/100)*rect.width}px`;const cur=time.querySelector('.rd-current-time');if(cur)cur.textContent=fmt(audio.currentTime);};
const setFromEvent=(cx)=>{const rect=prog.getBoundingClientRect();let x=cx-rect.left;x=Math.max(0,Math.min(x,rect.width));const p=(x/rect.width)*100;const nt=(p/100)*audio.duration;if(!isNaN(nt)){audio.currentTime=nt;update();}};
audio.addEventListener('timeupdate',()=>{if(!dragging)update();});
audio.addEventListener('loadedmetadata',()=>{const tot=time.querySelector('.rd-total-time');if(tot)tot.textContent=fmt(audio.duration);update();});
audio.addEventListener('ended',()=>{pp.innerHTML=Icons.play;});
pp.addEventListener('click',e=>{e.stopPropagation();if(audio.paused){audio.play().catch(()=>ResourceDiary.showToast('播放失败'));pp.innerHTML=Icons.pause;}else{audio.pause();pp.innerHTML=Icons.play;}});
prog.addEventListener('click',e=>{if(!audio.duration||isNaN(audio.duration))return;setFromEvent(e.clientX);});
const onMove=e=>{if(!dragging)return;setFromEvent(e.clientX);};const onUp=()=>{dragging=false;document.removeEventListener('mousemove',onMove);document.removeEventListener('mouseup',onUp);document.removeEventListener('touchmove',onTouchMove);document.removeEventListener('touchend',onTouchEnd);update();};
const onTouchMove=e=>{if(!dragging)return;e.preventDefault();setFromEvent(e.touches[0].clientX);};const onTouchEnd=e=>{onUp();};
prog.addEventListener('mousedown',e=>{e.preventDefault();if(!audio.duration||isNaN(audio.duration))return;dragging=true;setFromEvent(e.clientX);document.addEventListener('mousemove',onMove);document.addEventListener('mouseup',onUp);});
prog.addEventListener('touchstart',e=>{e.preventDefault();if(!audio.duration||isNaN(audio.duration))return;dragging=true;setFromEvent(e.touches[0].clientX);document.addEventListener('touchmove',onTouchMove,{passive:false});document.addEventListener('touchend',onTouchEnd);});
audio.play().then(()=>pp.innerHTML=Icons.pause).catch(()=>pp.innerHTML=Icons.play);
wrap.appendChild(audio);
const cleanup=()=>{audio.pause();audio.removeEventListener('timeupdate',update);audio.removeEventListener('loadedmetadata',update);audio.removeEventListener('ended',()=>{});if(anim)cancelAnimationFrame(anim);audio.src='';audio.load();};
return {element:wrap,cleanup};
},
playMedia(url){
if(!SecurityUtils.isSafeUrl(url)){this.showToast('不安全的媒体地址');return;}
if(!this._shadowRoot)return;
const realUrl=this.extractRealMediaUrl(url);
this._shadowRoot.getElementById('rd-inline-player')?.remove();
const isAudio=/\.(mp3|wav|flac|aac|ogg|m4a|weba|oga|opus)$/i.test(realUrl);
const mime=this.getMimeType(realUrl);
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'});title.innerHTML=(isAudio?Icons.music:Icons.film)+(isAudio?' 音频播放器':' 媒体播放器');
const close=SecurityUtils.createElement('button',{className:'rd-icon-btn',id:'rd-close-player',title:'关闭'});close.innerHTML=Icons.close;
header.appendChild(title);header.appendChild(close);player.appendChild(header);
const wrap=SecurityUtils.createElement('div',{id:'rd-media-wrapper'});
let customCleanup=null;
if(isAudio){
const {element,cleanup}=this._createAudioPlayerUI(realUrl,mime,url);wrap.appendChild(element);customCleanup=cleanup;
}else{
const video=document.createElement('video');video.id='rd-video';video.controls=true;video.autoplay=true;video.playsInline=true;
const src=document.createElement('source');src.src=realUrl;src.type=mime;video.appendChild(src);video.appendChild(document.createTextNode('不支持该格式'));
wrap.appendChild(video);
const adjust=()=>{const c=wrap;if(!c||!video)return;const cw=c.clientWidth,ch=c.clientHeight;const vr=video.videoWidth/video.videoHeight,cr=cw/ch;if(vr>cr){video.style.width='100%';video.style.height='auto';}else{video.style.width='auto';video.style.height='100%;';}};
video.addEventListener('loadedmetadata',adjust,{once:true});video.addEventListener('canplay',adjust,{once:true});
const resizeHandler=()=>{if(this._shadowRoot?.getElementById('rd-inline-player'))adjust();else window.removeEventListener('resize',resizeHandler);};
window.addEventListener('resize',resizeHandler,{passive:true});this._listeners.push(()=>window.removeEventListener('resize',resizeHandler));
}
player.appendChild(wrap);
const handler=()=>{if(customCleanup)customCleanup();player.remove();};
close.addEventListener('click',handler);
this._shadowRoot.appendChild(player);
},
getMimeType(url){try{const u=new URL(url,location.href);const ext=u.pathname.split('.').pop()?.toLowerCase()||'';const map={mp4:'video/mp4',webm:'video/webm',ogg:'video/ogg',ogv:'video/ogg',mov:'video/quicktime',avi:'video/x-msvideo',mkv:'video/x-matroska',flv:'video/x-flv',m3u8:'application/x-mpegURL',mpd:'application/dash+xml',mp3:'audio/mpeg',wav:'audio/wav',flac:'audio/flac',aac:'audio/aac',m4a:'audio/mp4',wma:'audio/x-ms-wma',oga:'audio/ogg',weba:'audio/webm',opus:'audio/opus'};return map[ext]||'video/mp4';}catch(e){return'video/mp4';}},
manualRefresh(){
const entries=performance.getEntriesByType('resource');const processed=new Set();
entries.forEach(entry=>{
if(!entry.name||processed.has(entry.name))return;processed.add(entry.name);
const url=entry.name;if(!SecurityUtils.isSafeUrl(url))return;if(processedUrls.has(url))return;
const status=entry.responseStatus||200;if(status>=400)return;
const displayType=this.getDisplayType(url,entry.responseContentType||'',entry.initiatorType);
const filterType=this.getFilterType(displayType);processedUrls.add(url);
let size=entry.transferSize||entry.encodedBodySize||0;if((url.startsWith('data:')||url.startsWith('blob:'))&&size===0)size=url.length;
this.addResource({url,displayType,filterType,status,size,duration:entry.duration||0,cached:entry.transferSize===0&&entry.encodedBodySize>0});
});
},
destroy(){
this.isDestroyed=true;
if(this._bodyCheckInterval){clearInterval(this._bodyCheckInterval);this._bodyCheckInterval=null;}
if(this._refreshTimer){clearInterval(this._refreshTimer);this._refreshTimer=null;}
if(this._rafId){cancelAnimationFrame(this._rafId);this._rafId=null;}
if(this._resourceObserver){this._resourceObserver.disconnect();this._resourceObserver=null;}
this._listeners.forEach(c=>c());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();delete window.ResourceDiary;this.isInitialized=false;
}
};
RD.init();
window.ResourceDiary=RD;
window.addEventListener('beforeunload',()=>{if(window.ResourceDiary)window.ResourceDiary.destroy();},{once:true});
})();