// ==UserScript== // @name B站一键备注 Rev // @namespace https://github.com/kaixinol/ // @version 2.0.0 // @author Kaesinol // @description B站UP主一键备注 | B站一键备注 Rev 的重写版本 // @license AGPL-3.0-or-later // @icon https://www.bilibili.com/favicon.ico // @website https://github.com/kaixinol/Bilibili-User-Memo // @supportURL https://github.com/kaixinol/Bilibili-User-Memo/issues // @match https://*.bilibili.com/* // @exclude https://*.hdslb.com/* // @require https://cdn.jsdelivr.net/npm/alpinejs@3.15.8/dist/cdn.min.js // @require https://cdn.jsdelivr.net/npm/query-selector-shadow-dom@1.0.1/dist/querySelectorShadowDom.js // @connect api.bilibili.com // @grant GM.xmlHttpRequest // @grant GM_addStyle // @grant GM_addValueChangeListener // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant unsafeWindow // @run-at document-body // @noframes // ==/UserScript== (function (Alpine, querySelectorShadowDom) { 'use strict'; const d=new Set;const importCSS = async e=>{d.has(e)||(d.add(e),(t=>{typeof GM_addStyle=="function"?GM_addStyle(t):(document.head||document.documentElement).appendChild(document.createElement("style")).append(t);})(e));}; function storageHas(key,storage){return null!==storage.getItem(key)}function storageGet(key,storage){let value=storage.getItem(key);if(void 0!==value)return JSON.parse(value)}function storageSet(key,value,storage){storage.setItem(key,JSON.stringify(value));} // @__NO_SIDE_EFFECTS__ function getGlobalConfig(config$1){return {lang:void 0,message:config$1?.message,abortEarly:void 0,abortPipeEarly:void 0}} // @__NO_SIDE_EFFECTS__ function _stringify(input){const type=typeof input;return "string"===type?`"${input}"`:"number"===type||"bigint"===type||"boolean"===type?`${input}`:"object"===type||"function"===type?(input&&Object.getPrototypeOf(input)?.constructor?.name)??"null":type}function _addIssue(context,label,dataset,config$1,other){const input=other&&"input"in other?other.input:dataset.value,expected=other?.expected??context.expects??null,received=other?.received??_stringify(input),issue={kind:context.kind,type:context.type,input:input,expected:expected,received:received,message:`Invalid ${label}: ${expected?`Expected ${expected} but r`:"R"}eceived ${received}`,requirement:context.requirement,path:other?.path,issues:other?.issues,lang:config$1.lang,abortEarly:config$1.abortEarly,abortPipeEarly:config$1.abortPipeEarly},isSchema="schema"===context.kind,message$1=other?.message??context.message??(context.reference,void issue.lang)??(isSchema?void issue.lang:null)??config$1.message??void issue.lang;void 0!==message$1&&(issue.message="function"==typeof message$1?message$1(issue):message$1);isSchema&&(dataset.typed=false);dataset.issues?dataset.issues.push(issue):dataset.issues=[issue];} // @__NO_SIDE_EFFECTS__ function _getStandardProps(context){return {version:1,vendor:"valibot",validate:value$1=>context["~run"]({value:value$1},getGlobalConfig())}} // @__NO_SIDE_EFFECTS__ function _isValidObjectKey(object$1,key){return Object.hasOwn(object$1,key)&&"__proto__"!==key&&"prototype"!==key&&"constructor"!==key} // @__NO_SIDE_EFFECTS__ function _joinExpects(values$1,separator){const list=[...new Set(values$1)];return list.length>1?`(${list.join(` ${separator} `)})`:list[0]??"never"} // @__NO_SIDE_EFFECTS__ function check(requirement,message$1){return {kind:"validation",type:"check",reference:check,async:false,expects:null,requirement:requirement,message:message$1,"~run"(dataset,config$1){dataset.typed&&!this.requirement(dataset.value)&&_addIssue(this,"input",dataset,config$1);return dataset}}} // @__NO_SIDE_EFFECTS__ function integer(message$1){return {kind:"validation",type:"integer",reference:integer,async:false,expects:null,requirement:Number.isInteger,message:message$1,"~run"(dataset,config$1){dataset.typed&&!this.requirement(dataset.value)&&_addIssue(this,"integer",dataset,config$1);return dataset}}} // @__NO_SIDE_EFFECTS__ function minValue(requirement,message$1){return {kind:"validation",type:"min_value",reference:minValue,async:false,expects:`>=${requirement instanceof Date?requirement.toJSON():_stringify(requirement)}`,requirement:requirement,message:message$1,"~run"(dataset,config$1){!dataset.typed||dataset.value>=this.requirement||_addIssue(this,"value",dataset,config$1,{received:dataset.value instanceof Date?dataset.value.toJSON():_stringify(dataset.value)});return dataset}}} // @__NO_SIDE_EFFECTS__ function getFallback(schema,dataset,config$1){return "function"==typeof schema.fallback?schema.fallback(dataset,config$1):schema.fallback} // @__NO_SIDE_EFFECTS__ function getDefault(schema,dataset,config$1){return "function"==typeof schema.default?schema.default(dataset,config$1):schema.default} // @__NO_SIDE_EFFECTS__ function array(item,message$1){return {kind:"schema",type:"array",reference:array,expects:"Array",async:false,item:item,message:message$1,get"~standard"(){ return _getStandardProps(this)},"~run"(dataset,config$1){const input=dataset.value;if(Array.isArray(input)){dataset.typed=true;dataset.value=[];for(let key=0;keyoption.expects),"|"),async:false,options:options,message:message$1,get"~standard"(){ return _getStandardProps(this)},"~run"(dataset,config$1){let validDataset,typedDatasets,untypedDatasets;for(const schema of this.options){const optionDataset=schema["~run"]({value:dataset.value},config$1);if(optionDataset.typed){if(!optionDataset.issues){validDataset=optionDataset;break}typedDatasets?typedDatasets.push(optionDataset):typedDatasets=[optionDataset];}else untypedDatasets?untypedDatasets.push(optionDataset):untypedDatasets=[optionDataset];}if(validDataset)return validDataset;if(typedDatasets){if(1===typedDatasets.length)return typedDatasets[0];_addIssue(this,"type",dataset,config$1,{issues:_subIssues(typedDatasets)});dataset.typed=true;}else {if(1===untypedDatasets?.length)return untypedDatasets[0];_addIssue(this,"type",dataset,config$1,{issues:_subIssues(untypedDatasets)});}return dataset}}} // @__NO_SIDE_EFFECTS__ function variant(key,options,message$1){return {kind:"schema",type:"variant",reference:variant,expects:"Object",async:false,key:key,options:options,message:message$1,get"~standard"(){ return _getStandardProps(this)},"~run"(dataset,config$1){const input=dataset.value;if(input&&"object"==typeof input){let outputDataset,maxDiscriminatorPriority=0,invalidDiscriminatorKey=this.key,expectedDiscriminators=[];const parseOptions=(variant$1,allKeys)=>{for(const schema of variant$1.options){if("variant"===schema.type)parseOptions(schema,new Set(allKeys).add(schema.key));else {let keysAreValid=true,currentPriority=0;for(const currentKey of allKeys){const discriminatorSchema=schema.entries[currentKey];if(currentKey in input?discriminatorSchema["~run"]({typed:false,value:input[currentKey]},{abortEarly:true}).issues:"exact_optional"!==discriminatorSchema.type&&"optional"!==discriminatorSchema.type&&"nullish"!==discriminatorSchema.type){keysAreValid=false;if(invalidDiscriminatorKey!==currentKey&&(maxDiscriminatorPriority0?uid:null}function getAttr(el,name){return normalizeUid(el.getAttribute(name))}function getAttrFromQuery(root,selector,attribute){const target=root.querySelector(selector);return target?normalizeUid(target.getAttribute(attribute)):null}function parseUidFromDataId(value){return value?normalizeUid(value.split("_")[1]):null}function readPreferredText(node){if(!node)return null;const original=node.dataset.biliOriginal?.trim();if(original)return original;const text=node.textContent?.trim();return text||null}function resolveRuleTextTarget(el,rule){return rule.textSelector?"watch"!==rule.textSource?function(el,textSelector){return el.querySelector(textSelector)||(el.matches(textSelector)?el:null)}(el,rule.textSelector):function(rule){return "trigger"in rule}(rule)?function(el,rule,textSelector){const directContainer=el.closest(rule.trigger.watch);if(directContainer)return directContainer.querySelector(textSelector);const watchTargets=querySelectorShadowDom.querySelectorAllDeep(rule.trigger.watch);for(const target of watchTargets){const scope=target.shadowRoot||target;if(scope.contains(el))return scope.querySelector(textSelector)}const first=watchTargets[0];return first?(first.shadowRoot||first).querySelector(textSelector):null}(el,rule,rule.textSelector):null:el}function syncRenderedNodeState(el,user,originalName,displayMode,options={}){const text=function(user,fallbackName,displayMode){const nickname=(user?.nickname||fallbackName||"").trim(),memo=(user?.memo||"").trim();if(!memo)return nickname;switch(displayMode){case 0:default:return nickname;case 1:return `${memo}(${nickname})`;case 2:return `${nickname}(${memo})`;case 3:return memo}}(user,originalName,displayMode);el.textContent!==text&&(el.textContent=text);const shouldHighlight=Boolean(!options.isEditableWrapper&&user?.memo&&user.memo!==originalName&&text!==originalName);el.classList.toggle("bili-memo-tag",shouldHighlight);}function syncElementMeta(el,meta){el.dataset.biliUid!==meta.uid&&(el.dataset.biliUid=meta.uid);el.dataset.biliOriginal!==meta.originalName&&(el.dataset.biliOriginal=meta.originalName);}function refreshTag(tag,user,displayMode){const originalName=tag.getAttribute("data-bili-original")||"";syncRenderedNodeState(tag,user,originalName,displayMode,{isEditableWrapper:tag.classList.contains("editable-textarea")});}function getUserAvatar(userID){const sourceSrc=querySelectorShadowDom.querySelectorDeep(`#user-avatar[data-user-profile-id="${userID}"] bili-avatar source , div.avatar source`)?.getAttribute("srcset");if(sourceSrc)return sourceSrc;const imgSrc=querySelectorShadowDom.querySelectorDeep(`up-avatar-wrap a[href*="${userID}"] img.bili-avatar-img`)?.getAttribute("data-src");return imgSrc||"https://i0.hdslb.com/bfs/face/member/noface.jpg@96w_96h_1c_1s.avif"}function normalizeDisplayMode(value){return "number"==typeof value&&value>=0&&value<=3?value:2}function normalizeUsers(raw){if(!Array.isArray(raw))return [];const cleaned=new Map;raw.forEach(entry=>{if(!entry||"object"!=typeof entry)return;const record2=entry,id=String(record2.id||"").trim();if(!id)return;const memo=String(record2.memo||"").trim();if(!memo)return;const nickname=String(record2.nickname||"").trim()||id,avatar=String(record2.avatar||"");cleaned.set(id,{id:id,nickname:nickname,avatar:avatar,memo:memo});});return Array.from(cleaned.values())}function saveUsersToStorage(users){_GM_setValue("biliUsers",users);}function ensureMemoStyleSheets(root){const sheets=root.adoptedStyleSheets,hasGlobal=sheets.includes(GLOBAL_STYLE_SHEET),hasCustom=sheets.includes(CUSTOM_MEMO_STYLE_SHEET);if(hasGlobal&&hasCustom)return;const next=sheets.slice();hasGlobal||next.push(GLOBAL_STYLE_SHEET);hasCustom||next.push(CUSTOM_MEMO_STYLE_SHEET);root.adoptedStyleSheets=next;}function ensureStylesForElement(target){const root=target.getRootNode();(root instanceof ShadowRoot||root instanceof Document)&&ensureMemoStyleSheets(root);}function resolveWatchScope(target){return target.shadowRoot||target}function isNodeInsideScope(node,scope){if(scope===document)return node.isConnected;let current=node;for(;current;){if(current===scope)return true;current=current instanceof ShadowRoot?current.host:current.parentNode;}return false}function persistWithGmStorage(key,initialValue){const persistFactory=Alpine.$persist;return persistFactory?persistFactory(initialValue).as(key).using(gmPersistStorage):initialValue}function applyCustomFontColor(color){color?document.documentElement.style.setProperty("--custom-font-color",color):document.documentElement.style.removeProperty("--custom-font-color");}function applyTheme(dark){document.documentElement.classList.toggle("memo-container-dark-theme",dark);}function formatIssues(issues){return issues.slice(0,2).map(({path:path,message:message})=>{const pathStr=path?.map(p=>p.key).filter(Boolean).join(".")||"";return pathStr?`[${pathStr}] ${message}`:message}).join("; ")}function decode(body){const form=new FormData;body.trim().split("&").forEach(function(bytes){if(bytes){const split=bytes.split("="),name=split.shift()?.replace(/\+/g," "),value=split.join("=").replace(/\+/g," ");form.append(decodeURIComponent(name),decodeURIComponent(value));}});return form}async function GM_fetch(input,init){const request=new Request(input,init);let data;init?.body&&(data=await request.text());return await function(request,init,data){return new Promise((resolve,reject)=>{if(request.signal&&request.signal.aborted)return reject(new DOMException("Aborted","AbortError"));GM.xmlHttpRequest({url:request.url,method:gmXHRMethod(request.method.toUpperCase()),headers:Object.fromEntries(new Headers(init?.headers).entries()),data:data,responseType:"blob",onload(res){try{resolve(function(req,res){const headers=function(h){const s=h.trim();if(!s)return new Headers;const array2=s.split("\r\n").map(value=>{let s2=value.split(":");return [s2[0].trim(),s2[1].trim()]});return new Headers(array2)}(res.responseHeaders),body="string"==typeof res.response?new Blob([res.response],{type:headers.get("Content-Type")||"text/plain"}):res.response;return new ResImpl(body,{statusCode:res.status,statusText:res.statusText,headers:headers,finalUrl:res.finalUrl,redirected:res.finalUrl===req.url})}(request,res));}catch(e){reject(e);}},onabort(){reject(new DOMException("Aborted","AbortError"));},ontimeout(){reject(new TypeError("Network request failed, timeout"));},onerror(err){reject(new TypeError("Failed to fetch: "+err.finalUrl));}});})}(request,init,data)}function gmXHRMethod(method){if(function(array2,element){return httpMethods.includes(element)}(0,method))return method;throw new Error(`unsupported http method ${method}`)}function validateConcurrency(concurrency){if(!Number.isInteger(concurrency)&&concurrency!==Number.POSITIVE_INFINITY||!(concurrency>0))throw new TypeError("Expected `concurrency` to be a number from 1 and up")}function registerUserStore(){if(Alpine.store("userList"))return;const store={isOpen:false,users:userStore.getUsers(),isDark:_GM_getValue("isDark",false),isRefreshing:false,refreshCurrent:0,refreshTotal:0,displayMode:userStore.displayMode,searchQuery:"",isMultiSelect:false,selectedIds:[],get filteredUsers(){const query=this.searchQuery.trim().toLowerCase();return query?this.users.filter(user=>{const id=String(user.id||""),nickname=(user.nickname||"").toLowerCase(),memo=(user.memo||"").toLowerCase();return id.includes(query)||nickname.includes(query)||memo.includes(query)}):this.users},updateUser(id,updates){const before=this.users.find(user=>user.id===id);before&&userStore.updateUser(id,updates,before.nickname||id);},removeUser(id){userStore.removeUser(id);},toggleMultiSelect(){this.isMultiSelect=!this.isMultiSelect;this.isMultiSelect||this.clearSelection();},clearSelection(){this.selectedIds=[];},invertSelection(ids){if(0===ids.length)return;const current=new Set(this.selectedIds),next=new Set(current);ids.forEach(id=>{next.has(id)?next.delete(id):next.add(id);});this.selectedIds=Array.from(next);},removeSelected(){if(0!==this.selectedIds.length){userStore.removeUsers(this.selectedIds);this.clearSelection();}},setDisplayMode(mode){userStore.setDisplayMode(mode);},async refreshData(){if(this.isRefreshing||0===this.users.length)return;this.isRefreshing=true;this.refreshCurrent=0;this.refreshTotal=this.users.length;const profiles=await async function(users,onProgress){const profiles=[],tasks=users.map(async user=>{try{const newData=await getUserInfo(String(user.id));if(!newData.nickname)return;profiles.push({id:user.id,nickname:newData.nickname,avatar:newData.avatar});}catch(error){logger_error(`刷新用户 [${user.id}] 失败:`,error);}finally{onProgress();}});await Promise.allSettled(tasks);return profiles}(this.users,()=>{this.refreshCurrent++;});userStore.updateUserProfiles(profiles);setTimeout(()=>{this.isRefreshing=false;},1e3);},exportData(){!function(users){const exportData=users.map(user=>({id:user.id,nickname:user.nickname,avatar:user.avatar||"",memo:user.memo||""})),jsonContent=JSON.stringify(exportData,null,2),blob=new Blob([jsonContent],{type:"application/json"}),url=URL.createObjectURL(blob),a=document.createElement("a");a.href=url;a.download=`bili-user-notes-${(new Date).toISOString().split("T")[0]}.json`;a.click();URL.revokeObjectURL(url);}(this.users);alert(`导出成功!\n已导出 ${this.users.length} 个用户的数据`);},async importData(){const readResult=await async function(){const file=await new Promise(resolve=>{const input=document.createElement("input");let settled=false;const finish=file=>{if(!settled){settled=true;resolve(file);}};input.type="file";input.accept="application/json";input.onchange=()=>{finish(input.files?.[0]||null);};input.oncancel=()=>finish(null);input.click();});if(!file)return {status:"cancelled"};try{const parsedData=JSON.parse(await file.text()),validation=function(data){const result=safeParse(CombinedSchema,data);return result.success?{ok:!0}:{ok:!1,error:`格式不匹配: ${formatIssues(result.issues)}`}}(parsedData);if(!validation.ok)return {status:"error",message:`导入失败:${validation.error}`};const importedUsers=function(parsedData){return Array.isArray(parsedData)?parsedData.map(user=>({id:user.id||user.bid,nickname:user.nickname||"",avatar:user.avatar||"",memo:user.memo||""})):parsedData&&"object"==typeof parsedData?Object.values(parsedData).map(user=>({id:user.id||user.bid,nickname:user.nickname||"",avatar:user.avatar||"",memo:user.memo||""})):[]}(parsedData).filter(user=>user.id&&user.nickname);return 0===importedUsers.length?{status:"error",message:"导入失败:没有有效的用户数据"}:{status:"ok",users:importedUsers}}catch{return {status:"error",message:"导入失败:JSON 格式错误或数据解析失败"}}}();if("cancelled"===readResult.status)return;if("error"===readResult.status){alert(readResult.message);return}const result=userStore.upsertImportedUsers(readResult.users);0!==result.added||0!==result.updated?alert(`导入成功!\n新增:${result.added} 个用户\n更新:${result.updated} 个用户`):alert("导入完成,但没有可应用的变更");}};Alpine.store("userList",store);const reactiveStore=Alpine.store("userList"),syncUsers=users=>{reactiveStore.users=users;if(0===reactiveStore.selectedIds.length)return;const userIds=new Set(users.map(u=>u.id));reactiveStore.selectedIds=reactiveStore.selectedIds.filter(id=>userIds.has(id));};userStore.subscribe(change=>{if("displayMode"!==change.type)if("users"!==change.type){syncUsers(change.users);reactiveStore.displayMode=change.displayMode;}else syncUsers(change.users);else reactiveStore.displayMode=change.displayMode;});}function useUserListStore(){return Alpine.store("userList")}function usePanelPrefsStore(){return Alpine.store("panelPrefs")}function initMainPanel(){if(document.getElementById("bili-memo-container"))return;registerUserStore();Alpine.store("panelPrefs")||Alpine.store("panelPrefs",function({getUserListStore:getUserListStore}){const initialOpenText=_GM_getValue("btn_open_text","UvU"),initialCloseText=_GM_getValue("btn_close_text","UwU"),initialDarkTheme=_GM_getValue("isDark",false),initialFontColor=_GM_getValue("customFontColor","").trim(),initialMemoCss=_GM_getValue("customMemoCss","");return {initialized:false,openText:persistWithGmStorage("toggle.openText",initialOpenText),closeText:persistWithGmStorage("toggle.closeText",initialCloseText),isDark:persistWithGmStorage("theme.isDark",initialDarkTheme),customFontColor:persistWithGmStorage("style.customFontColor",initialFontColor),customMemoCss:persistWithGmStorage("style.customMemoCss",initialMemoCss),cssStatus:"",showAdvancedCss:false,init(){if(this.initialized)return;this.initialized=true;applyTheme(this.isDark);getUserListStore().isDark=this.isDark;const cssVarColor=document.documentElement.style.getPropertyValue("--custom-font-color").trim();this.customFontColor=this.customFontColor||cssVarColor;applyCustomFontColor(this.customFontColor);this.applyMemoCss();},toggleTheme(){this.isDark=!this.isDark;getUserListStore().isDark=this.isDark;applyTheme(this.isDark);},editToggleText(isOpen){const currentText=isOpen?this.openText:this.closeText,nextText=prompt("修改文字:",currentText)?.trim();nextText&&(isOpen?this.openText=nextText:this.closeText=nextText);},onCustomColorInput(){applyCustomFontColor(this.customFontColor);},clearCustomColor(){this.customFontColor="";applyCustomFontColor("");alert("已取消自定义字体颜色");},closeAdvancedCss(){this.showAdvancedCss=false;},applyMemoCss(){const nextCss=this.customMemoCss||"",result=function(css){const nextCss=css??"";try{CUSTOM_MEMO_STYLE_SHEET.replaceSync(nextCss);}catch(error){const message=error instanceof Error?error.message:`未知错误: ${String(error)}`;logger_warn("⚠️ 自定义备注 CSS 解析失败:",error);return {ok:false,error:message,ruleCount:CUSTOM_MEMO_STYLE_SHEET.cssRules.length}}!function(){const targets=querySelectorShadowDom.querySelectorAllDeep(".bili-memo-tag, .editable-textarea, .bili-memo-input");if(!targets||0===targets.length)return;const roots=new Set;targets.forEach(el=>{const root=el.getRootNode();(root instanceof ShadowRoot||root instanceof Document)&&roots.add(root);});roots.forEach(root=>ensureMemoStyleSheets(root));}();return {ok:true,ruleCount:CUSTOM_MEMO_STYLE_SHEET.cssRules.length}}(nextCss);this.cssStatus=function(css,result){const lintResult=function(css){const s=css.trim();if(!s)return null;let q=null,esc=false,c=0,br=0,pr=0,bk=0;for(let i=0;i0){if("*"===ch&&"/"===nx){c--;i++;}}else if(q){if(esc){esc=false;continue}if("\\"===ch){esc=true;continue}ch===q&&(q=null);}else if("/"!==ch||"*"!==nx)if("'"!==ch&&'"'!==ch){"{"===ch?br++:"}"===ch?br--:"("===ch?pr++:")"===ch?pr--:"["===ch?bk++:"]"===ch&&bk--;if(br<0)return "多余的 '}'";if(pr<0)return "多余的 ')'";if(bk<0)return "多余的 ']'"}else q=ch;else {c++;i++;}}return c>0?"注释未闭合":q?`字符串未闭合:${q}`:br>0?"缺少 '}'":pr>0?"缺少 ')'":bk>0?"缺少 ']'":null}(css);if(lintResult)return `CSS 语法警告:${lintResult}`;if(!result.ok)return `CSS 语法错误:${result.error||"无法解析"}`;const parseWarn=function(css,ruleCount){if(!css.trim())return null;if(0!==(ruleCount||0))return null;const stripped=css.replace(/\/\*[\s\S]*?\*\//g,"").replace(/"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'/g,"");return /{/.test(stripped)?"浏览器未解析出任何规则,可能语法错误被忽略":null}(css,result.ruleCount);return parseWarn?`CSS 解析警告:${parseWarn}`:""}(nextCss,result);}}}({getUserListStore:useUserListStore}));!function(){if(!panelComponentsRegistered){panelComponentsRegistered=true;!function(){if(!panelBindingsRegistered){panelBindingsRegistered=true;Alpine.bind("panelImportBtn",()=>({type:"button",class:"panel-btn",title:"导入JSON文件,支持老格式","@click":"userList.importData()"}));Alpine.bind("panelMultiSelectBtn",()=>({type:"button",title:"按Ctrl + A 全选 / 反选",":class":"{ 'panel-btn': true, 'btn-active': userList.isMultiSelect }","@click":"userList.toggleMultiSelect()"}));Alpine.bind("panelRefreshBtn",()=>({type:"button",":disabled":"userList.isRefreshing",":class":"{ 'panel-btn': true, 'btn-disabled': userList.isRefreshing }",":title":"userList.isRefreshing ? '正在同步 Bilibili 最新数据...' : '刷新UP主名字和头像'","@click":"userList.refreshData()"}));Alpine.bind("panelSearchClearBtn",()=>({type:"button",class:"panel-search-clear","x-show":"userList.searchQuery","@click":"clearSearch()"}));Alpine.bind("panelExportBtn",()=>({type:"button",class:"panel-btn","@click":"userList.exportData()"}));}}();Alpine.data("panelShell",()=>({init(){usePanelPrefsStore().init();},get isOpen(){return useUserListStore().isOpen},set isOpen(next){useUserListStore().isOpen=Boolean(next);},handleSelectAll(event){const userList=useUserListStore();if(userList.isMultiSelect&&(event.ctrlKey||event.metaKey)&&"a"===event.key.toLowerCase()){event.preventDefault();userList.invertSelection(userList.filteredUsers.map(user=>user.id));}}}));Alpine.data("panelToggleBtn",()=>({get prefs(){return usePanelPrefsStore()},get isOpen(){return useUserListStore().isOpen},set isOpen(next){useUserListStore().isOpen=Boolean(next);},get openText(){return this.prefs.openText},get closeText(){return this.prefs.closeText},togglePanel(){this.isOpen=!this.isOpen;},editToggleText(){this.prefs.editToggleText(this.isOpen);}}));Alpine.data("panelSettings",()=>({displayModes:DISPLAY_MODE_OPTIONS,get userList(){return useUserListStore()},get prefs(){return usePanelPrefsStore()},get displayModeProxy(){return this.userList.displayMode},set displayModeProxy(mode){this.userList.setDisplayMode(Number(mode));},get isDark(){return this.prefs.isDark},get customFontColor(){return this.prefs.customFontColor},set customFontColor(next){this.prefs.customFontColor=next;},get customMemoCss(){return this.prefs.customMemoCss},set customMemoCss(next){this.prefs.customMemoCss=next;},get cssStatus(){return this.prefs.cssStatus},get showAdvancedCss(){return this.prefs.showAdvancedCss},toggleTheme(){this.prefs.toggleTheme();},onCustomColorInput(){this.prefs.onCustomColorInput();},closeAdvancedCss(){this.prefs.closeAdvancedCss();},handleColorSettingContextMenu(event){event.preventDefault();this.prefs.showAdvancedCss=!this.prefs.showAdvancedCss;this.prefs.showAdvancedCss&&this.$nextTick(()=>{const input=this.$refs.memoCssInput;input?.focus();});},handleColorSettingMouseDown(event){if(1===event.button){event.preventDefault();this.prefs.clearCustomColor();}},applyMemoCss(){this.prefs.applyMemoCss();}}));Alpine.data("panelActions",()=>({get userList(){return useUserListStore()},clearSearch(){this.userList.searchQuery="";},confirmRemoveSelected(){const count=this.userList.selectedIds.length;0!==count&&confirm(`确定要删除所选 ${count} 个用户吗?`)&&this.userList.removeSelected();}}));Alpine.data("userCard",userId=>({userId:userId,get userList(){return useUserListStore()},get currentUser(){return this.userList.users.find(item=>item.id===this.userId)},get isSelected(){return this.userList.selectedIds.includes(this.userId)},get isMultiSelect(){return this.userList.isMultiSelect},get selectedIds(){return this.userList.selectedIds},set selectedIds(next){this.userList.selectedIds=next;},toggleSelected(){const next=new Set(this.userList.selectedIds);next.has(this.userId)?next.delete(this.userId):next.add(this.userId);this.userList.selectedIds=Array.from(next);},handleCardClick(event){if(!this.isMultiSelect)return;const target=event.target;if(target&&!target.closest(".user-select")){event.preventDefault();this.toggleSelected();}},confirmRemove(){confirm("确定要删除吗?")&&this.userList.removeUser(this.userId);}}));Alpine.data("copyableUid",uid=>({uid:uid,copied:false,get isMultiSelect(){return useUserListStore().isMultiSelect},init(){this.refreshOverflow();},refreshOverflow(){this.$nextTick(()=>{const el=this.$el;el.classList.toggle("can-expand",el.scrollWidth>el.clientWidth);});},copy(){if(!this.isMultiSelect){navigator.clipboard.writeText(`UID:${this.uid}`);this.copied=true;window.setTimeout(()=>{this.copied=false;},500);}},get displayText(){return this.copied?"✅ 已复制":this.uid}}));Alpine.data("memoEditor",(userId,initialMemo="")=>({userId:userId,isEditing:false,memoDraft:String(initialMemo??""),get userList(){return useUserListStore()},get isMultiSelect(){return this.userList.isMultiSelect},get currentMemo(){return this.userList.users.find(item=>item.id===this.userId)?.memo||""},syncDraft(){this.isEditing||(this.memoDraft=this.currentMemo);},startEdit(){if(!this.isMultiSelect){this.isEditing=true;this.$nextTick(()=>{const input=this.$refs.memoInput;input?.focus();});}},commit(){this.isEditing=false;const nextMemo="string"==typeof this.memoDraft?this.memoDraft:String(this.memoDraft??"");this.userList.updateUser(this.userId,{memo:nextMemo});},cancel(){this.memoDraft=this.currentMemo;this.isEditing=false;},blurInput(){const input=this.$refs.memoInput;input?.blur();}}));}}();const finalHtml='\n
\n \n
\n
昵称显示模式:
\n \n\n
\n \n 自定义备注颜色\n
\n\n \n \n
\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n\n
\n \n \n \n \n \n \n \n \n \n \n 已选 \n \n
备注列表
\n \n \n \n
\n\n \n
自定义备注样式(CSS)
\n \n
\n
\n 这里支持完整 CSS 选择器。只影响备注相关元素时,建议用 .bili-memo-tag /\n .editable-textarea / .bili-memo-input\n
\n \n\n
\n
\n \n\n
\n 没有找到用户\n
\n
\n
\n \n\n'.replace("${appName}","备注管理").replace("${boxTemplate}",'\n \n
\n \n
\n \n \n
\n \n \n
\n
\n \n \n
\n \n \n \n \n \n \n \n\n'),container=document.createElement("div");container.id="bili-memo-container";container.innerHTML=finalHtml;document.body.appendChild(container);}function loadDisabledPageScopes(){const raw=_GM_getValue("disabledPageScopes",[]);if(!Array.isArray(raw))return [];const seen=new Set,patterns=[];raw.forEach(entry=>{if("string"!=typeof entry)return;const pattern=entry.trim();if(pattern&&!seen.has(pattern)){seen.add(pattern);patterns.push(pattern);}});return patterns}function saveDisabledPageScopes(patterns){_GM_setValue("disabledPageScopes",patterns);}var module_default=function(Alpine2){let persist=()=>{let alias,storage;try{storage=localStorage;}catch(e){console.error(e);console.warn("Alpine: $persist is using temporary storage since localStorage is unavailable.");let dummy=new Map;storage={getItem:dummy.get.bind(dummy),setItem:dummy.set.bind(dummy)};}return Alpine2.interceptor((initialValue,getter,setter,path,key)=>{let lookup=alias||`_x_${path}`,initial=storageHas(lookup,storage)?storageGet(lookup,storage):initialValue;setter(initial);Alpine2.effect(()=>{let value=getter();storageSet(lookup,value,storage);setter(value);});return initial},func=>{func.as=key=>{alias=key;return func},func.using=target=>{storage=target;return func};})};Object.defineProperty(Alpine2,"$persist",{get:()=>persist()});Alpine2.magic("persist",persist);Alpine2.persist=(key,{get:get,set:set},storage=localStorage)=>{let initial=storageHas(key,storage)?storageGet(key,storage):get();set(initial);Alpine2.effect(()=>{let value=get();storageSet(key,value,storage);set(value);});};};importCSS(".panel-container{color-scheme:light;position:fixed;height:46vh;left:0;right:0;bottom:0;z-index:9999;background:var(--bg-main);border-top:2px solid var(--primary-color);box-shadow:0 -4px 12px #00000014;transition:transform .3s cubic-bezier(.4,0,.2,1)}.panel-container.closed{transform:translateY(100%)}.panel-container.open{transform:translateY(0)}.panel-inner{padding:12px 20px;position:relative;height:100%;display:flex;flex-direction:column;box-sizing:border-box}.panel-toggle-btn{position:absolute;top:-34px;left:20px;width:70px;height:34px;border-radius:8px 8px 0 0;cursor:context-menu;font-size:18px;color:var(--text-inverse);background:var(--primary-color);border:none;display:flex;align-items:center;justify-content:center;transition:opacity .3s ease,background .2s ease,top .2s ease;-webkit-user-select:none;user-select:none}.panel-toggle-btn:hover{opacity:1!important;background:var(--primary-color)}.panel-mode-section{display:flex;align-items:center;gap:18px;padding:8px 12px;font-size:16px;position:relative}.panel-right-actions{margin-left:auto;display:flex;align-items:center;gap:10px}.panel-custom-css-section{margin-top:8px;padding:8px 12px;border-radius:10px;background:var(--bg-sub);border:1px solid var(--border-main);display:flex;flex-direction:column;gap:6px}.panel-custom-css-title{font-size:13px;font-weight:600;color:var(--text-main)}.panel-custom-css-input{width:100%;min-height:90px;resize:vertical;border-radius:8px;border:1px solid var(--border-main);background:var(--bg-main);color:var(--text-main);font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;padding:8px 10px;outline:none;transition:border-color .15s ease,box-shadow .15s ease}.panel-custom-css-input:focus{border-color:var(--primary-color);box-shadow:0 0 0 2px var(--primary-alpha)}.panel-custom-css-status{font-size:12px;color:var(--text-inverse);background:var(--danger-color);border-radius:6px;padding:4px 8px;line-height:1.4}.panel-custom-css-hint{font-size:12px;color:var(--text-sub);line-height:1.4}.panel-custom-color-setting{display:flex;align-items:center;gap:8px;padding:4px 8px;border-radius:10px;background:var(--bg-sub);border:1px solid var(--primary-color);color:var(--text-main);font-size:13px;cursor:pointer;position:relative}.color-preview{width:20px;height:20px;border-radius:4px;border:1px solid var(--border-main);pointer-events:none;background:var(--custom-font-color, var(--primary-color))}.ghost-color-picker{position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);opacity:0;width:0;height:0;overflow:hidden;z-index:-1;padding:0;margin:0;border:none}.panel-custom-color-setting label{cursor:pointer}.panel-custom-color-setting input[type=color]{width:24px;height:24px;border:1px solid var(--border-main);border-radius:6px;padding:0;background:var(--bg-main);cursor:pointer}.panel-custom-color-setting input[type=color]::-webkit-color-swatch-wrapper{padding:0}.panel-custom-color-setting input[type=color]::-webkit-color-swatch{border:none;border-radius:4px}.panel-theme-toggle{cursor:pointer;display:flex;align-items:center;justify-content:center;padding:6px;border-radius:8px;transition:all .2s ease;color:var(--text-sub)}.panel-theme-toggle:hover{color:var(--primary-color);transform:rotate(20deg);background:var(--overlay)}.memo-container-dark-theme .panel-theme-toggle:hover{background:var(--glass)}[x-cloak]{display:none!important}.panel-theme-toggle svg{width:20px;height:20px;stroke-width:2px}.panel-mode-label{font-size:15px;font-weight:600;color:var(--text-main)}.panel-mode-option{display:flex;align-items:center;gap:6px;font-size:14px;color:var(--text-sub);cursor:pointer}.panel-control-bar{display:flex;align-items:center;gap:12px;padding:12px 0}.panel-btn{padding:6px 16px;border:1px solid var(--primary-color);background:var(--bg-main);border-radius:20px;color:var(--primary-color);cursor:pointer;font-size:13px;font-weight:500;transition:all .2s}.panel-btn:hover,.panel-btn.btn-active{background:var(--primary-color);color:var(--text-inverse)}.panel-icon-btn{display:inline-flex;align-items:center;gap:6px;padding:6px 10px;border:1px solid var(--primary-color);background:var(--bg-main);border-radius:18px;color:var(--primary-color);cursor:pointer;font-size:12px;transition:all .2s}.panel-icon-btn svg{width:16px;height:16px}.panel-icon-btn:hover{background:var(--primary-color);color:var(--text-inverse)}.panel-icon-text{font-size:12px}.panel-title{font-size:16px;font-weight:700;color:var(--text-main);flex-grow:1}.panel-search-box{border:1px solid var(--primary-color);padding:6px 10px;border-radius:18px;width:180px;display:flex;align-items:center;background:var(--bg-sub);transition:all .2s}.panel-search-box:focus-within{border-color:var(--primary-color);background:var(--bg-main);box-shadow:0 0 0 2px var(--primary-alpha)}.panel-search-input{border:none;outline:none;width:100%;font-size:14px;background:transparent;color:var(--text-main)}.panel-search-clear{color:var(--border-main);font-size:14px;cursor:pointer;border:none;background:none;padding:0 4px}.panel-search-clear:hover{color:var(--primary-color)}.panel-users-container{flex:1;overflow-y:auto;margin-top:10px;padding-right:8px}.panel-users-container::-webkit-scrollbar{width:6px}.panel-users-container::-webkit-scrollbar-thumb{background:var(--border-main);border-radius:10px}.panel-users-container::-webkit-scrollbar-thumb:hover{background:var(--border-main)}.memo-container-dark-theme .panel-container{background:var(--bg-main);border-top-color:var(--primary-color);box-shadow:0 -6px 16px #00000080;color-scheme:dark}.memo-container-dark-theme .panel-toggle-btn{background:var(--primary-color);color:var(--text-inverse)}.memo-container-dark-theme .panel-mode-label{color:var(--text-main)}.memo-container-dark-theme .panel-mode-option{color:var(--text-sub)}.memo-container-dark-theme .panel-btn{background:transparent;color:var(--primary-color);border-color:var(--primary-color)}.memo-container-dark-theme .panel-btn:hover{background:var(--primary-color);color:var(--text-main)}.memo-container-dark-theme .panel-icon-btn{background:transparent;color:var(--primary-color);border-color:var(--primary-color)}.memo-container-dark-theme .panel-icon-btn:hover{background:var(--primary-color);color:var(--text-main)}.memo-container-dark-theme .panel-search-box{background:var(--bg-sub);border:2px solid var(--primary-color)}.memo-container-dark-theme .panel-search-input{color:var(--text-main)}.memo-container-dark-theme .panel-search-clear{color:var(--text-sub)}.memo-container-dark-theme .panel-users-container::-webkit-scrollbar-thumb{background:var(--border-main)}.memo-container-dark-theme .panel-title{color:var(--text-inverse)}.panel-btn:disabled,.panel-btn.btn-disabled{background-color:var(--border-main);color:var(--text-sub);cursor:not-allowed;opacity:.8;transform:none}.panel-icon-btn:disabled{background-color:var(--border-main);color:var(--text-sub);cursor:not-allowed;opacity:.8;transform:none}.loading-spinner{display:inline-block;animation:spin 2s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}");importCSS("*{box-sizing:border-box}button,input{font-family:inherit}:root{--custom-font-size: 14px;--primary-color: #fb7299;--primary-alpha: rgba(251, 114, 153, .15);--link-color: #00aeec;--bg-main: #ffffff;--bg-sub: #f6f8fa;--text-main: #333333;--text-sub: #666666;--text-inverse: #ffffff;--border-main: #d1d1d1;--shadow-elevated: 0 4px 12px rgba(0, 0, 0, .15);--shadow-dark: 0 8px 24px rgba(0, 0, 0, .4);--overlay: rgba(0, 0, 0, .15);--glass: rgba(255, 255, 255, .35);--focus-color: #0366d6;--focus-ring: rgba(3, 102, 214, .2);--danger-color: #d73a49;--danger-alpha: rgba(215, 58, 73, .1)}.memo-container-dark-theme{--bg-main: #18181b;--bg-sub: #202024;--border-main: #2f2f33;--text-main: #e5e7eb;--text-sub: #a1a1aa;--link-color: #7dd3fc}");importCSS('.panel-users-list{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;width:100%}.panel-empty-state{grid-column:1 / -1;display:flex;align-items:center;justify-content:center;padding:24px 12px;color:var(--text-sub);font-size:14px}.user-box{display:flex;align-items:center;flex-wrap:nowrap;position:relative;width:100%;height:72px;padding:0 12px;background:var(--bg-main);border:1px solid var(--border-main);border-radius:8px;box-shadow:var(--shadow-elevated);box-sizing:border-box;transition:all .2s ease}.user-box:hover{border-color:var(--primary-color);background:var(--primary-alpha)}.user-box.is-selected{border-color:var(--primary-color);box-shadow:0 0 0 2px var(--primary-alpha)}.user-box.multi-select{cursor:pointer}.user-select{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;margin-right:8px;flex-shrink:0;cursor:pointer}.user-select input{position:absolute;opacity:0;pointer-events:none}.user-select-mark{width:16px;height:16px;border-radius:4px;border:1px solid var(--border-main);background:var(--bg-main);box-shadow:var(--shadow-elevated)}.user-select input:checked+.user-select-mark{border-color:var(--primary-color);background:var(--primary-color);box-shadow:0 0 0 2px var(--primary-alpha)}.user-select input:checked+.user-select-mark:after{content:"";display:block;width:8px;height:4px;border-left:2px solid #fff;border-bottom:2px solid #fff;transform:translate(3px,4px) rotate(-45deg)}.user-avatar-wrapper{width:44px;height:44px;flex-shrink:0;border-radius:50%;overflow:hidden;margin-right:10px}.user-avatar{width:100%;height:100%;object-fit:cover}.user-info-section{width:85px;flex-shrink:0;display:flex;flex-direction:column;position:relative}.user-nickname-section{flex:1.5;min-width:0;padding:0 8px;display:flex;flex-direction:column}.user-nickname-link{color:var(--link-color);font-size:14px;font-weight:600;text-decoration:none;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.user-note-section{flex:1;min-width:0;display:flex;flex-direction:column;align-items:flex-start}.user-info-label{font-size:12px;color:var(--text-main);margin-bottom:2px}.user-id{font-family:monospace;font-size:12px;width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:block;transition:background .2s;cursor:pointer}.user-id.can-expand:hover{overflow:visible;position:absolute;width:fit-content;z-index:100;background-color:var(--bg-main);border:10px solid transparent;margin:-10px;padding:2px 8px;border-radius:4px;box-shadow:var(--shadow-elevated)}.user-note-input{border:none;outline:none;font-size:13px;width:100%;color:var(--custom-font-color, var(--primary-color));background:transparent;padding:0;cursor:pointer;border-bottom:2px dashed transparent;transition:all .2s}.user-note-input:hover{border-bottom-color:var(--primary-alpha)}.user-note-input.editing{cursor:text;background:var(--primary-alpha);border-bottom:2px solid var(--primary-color)}@media(max-width:1200px){.panel-users-list{grid-template-columns:repeat(2,1fr)}}@media(max-width:768px){.panel-users-list{grid-template-columns:1fr}.user-box{height:65px}}.memo-container-dark-theme .user-box{background:var(--bg-sub);border-color:var(--border-main);box-shadow:none}.memo-container-dark-theme .user-box:hover{background:var(--bg-sub);border-color:var(--primary-color)}.memo-container-dark-theme .user-nickname-link{color:var(--link-color)}.memo-container-dark-theme .user-info-label,.memo-container-dark-theme .user-id{color:var(--text-sub)}.memo-container-dark-theme .user-id.can-expand:hover{background-color:var(--bg-sub);color:var(--text-inverse);box-shadow:var(--shadow-dark)}.memo-container-dark-theme .user-note-input{color:var(--custom-font-color, var(--primary-color))}.memo-container-dark-theme .user-note-input.editing{background:var(--primary-alpha)}.memo-container-dark-theme .user-select-mark{background:var(--bg-sub);border-color:var(--text-sub);box-shadow:var(--shadow-dark)}.memo-container-dark-theme .user-select input:checked+.user-select-mark{border-color:var(--primary-color);background:var(--primary-color);box-shadow:0 0 0 2px var(--primary-alpha)}.delete-btn{height:16px;width:16px;position:absolute;top:5px;right:5px;color:var(--primary-color);cursor:pointer;z-index:10}.delete-btn:hover{stroke:var(--danger-color)}');var _GM_addValueChangeListener=(()=>"undefined"!=typeof GM_addValueChangeListener?GM_addValueChangeListener:void 0)(),_GM_getValue=(()=>"undefined"!=typeof GM_getValue?GM_getValue:void 0)(),_GM_registerMenuCommand=(()=>"undefined"!=typeof GM_registerMenuCommand?GM_registerMenuCommand:void 0)(),_GM_setValue=(()=>"undefined"!=typeof GM_setValue?GM_setValue:void 0)(),_GM_xmlhttpRequest=(()=>"undefined"!=typeof GM_xmlhttpRequest?GM_xmlhttpRequest:void 0)(),_unsafeWindow=(()=>"undefined"!=typeof unsafeWindow?unsafeWindow:void 0)(),ValiError=class extends Error{constructor(issues){super(issues[0].message);this.name="ValiError";this.issues=issues;}},InjectionMode=(InjectionMode2=>{InjectionMode2[InjectionMode2.Static=1]="Static";InjectionMode2[InjectionMode2.Dynamic=2]="Dynamic";InjectionMode2[InjectionMode2.Polling=3]="Polling";return InjectionMode2})(InjectionMode||{}),StyleScope=(StyleScope2=>{StyleScope2[StyleScope2.Minimal=1]="Minimal";StyleScope2[StyleScope2.Editable=2]="Editable";return StyleScope2})(StyleScope||{});const StyleScopeSchema=union([ literal(1), literal(2)]),selectorEntries={aSelector:optional(string()),textSelector:optional(string())},triggerSchema=strictObject({watch:string(),interval:pipe(number(),integer(),minValue(0))}),RawRuleSchema=variant("injectMode",[pipe( strictObject({name:string(),styleScope:StyleScopeSchema,injectMode:literal(1),...selectorEntries,textSource:optional(literal("self")),fontSize:optional(string())}), check(rule=>Boolean(rule.aSelector||rule.textSelector),"aSelector 和 textSelector 至少需要提供一个")),pipe( strictObject({name:string(),styleScope:StyleScopeSchema,injectMode:literal(2),...selectorEntries,textSource:optional(union([literal("self"),literal("watch")])),fontSize:optional(string()),trigger:triggerSchema,dynamicWatch:optional(boolean()),matchByName:optional(boolean())}), check(rule=>Boolean(rule.aSelector||rule.textSelector),"aSelector 和 textSelector 至少需要提供一个"), check(rule=>"watch"!==rule.textSource||Boolean(rule.textSelector),"textSource=watch 时必须提供 textSelector")),pipe( strictObject({name:string(),styleScope:StyleScopeSchema,injectMode:literal(3),...selectorEntries,textSource:optional(union([literal("self"),literal("watch")])),fontSize:optional(string()),trigger:triggerSchema,ignoreProcessed:optional(boolean()),matchByName:optional(boolean())}), check(rule=>Boolean(rule.aSelector||rule.textSelector),"aSelector 和 textSelector 至少需要提供一个"), check(rule=>"watch"!==rule.textSource||Boolean(rule.textSelector),"textSource=watch 时必须提供 textSelector"))]),config=function(){const dataset=array(strictObject({urlPattern:instance(RegExp),rule:RawRuleSchema}))["~run"]({value:[{urlPattern:/^https:\/\/www\.bilibili\.com\/(video|list)\/.*/,rule:{name:"视频页面",injectMode:1,styleScope:2,aSelector:".up-name"}},{urlPattern:/^https:\/\/www\.bilibili\.com\/(video|list)\/.*/,rule:{name:"视频页面",injectMode:1,styleScope:1,aSelector:"a.staff-name"}},{urlPattern:/^https:\/\/www\.bilibili\.com\/(video|list)\/.*/,rule:{name:"视频页面 - 推荐",injectMode:2,styleScope:1,aSelector:".upname a",textSelector:"span.name",trigger:{watch:".rcmd-tab",interval:1e3}}},{urlPattern:/^https:\/\/space\.bilibili\.com\/.*/,rule:{name:"空间",injectMode:1,styleScope:2,aSelector:".nickname",fontSize:"24px"}},{urlPattern:/^https:\/\/space\.bilibili\.com\/\d+\/favlist\?(?=[^#]*\bfid=\d+\b)(?=[^#]*\bftype=create\b)[^#]*(?:#.*)?$/,rule:{name:"空间收藏夹",injectMode:2,styleScope:1,aSelector:".bili-video-card__author",textSelector:".bili-video-card__text span[title]",trigger:{watch:".favlist-main",interval:1e3}}},{urlPattern:/^https:\/\/www\.bilibili\.com\/watchlater\/list(?:\?[^#]*)?(?:#.*)?$/,rule:{name:"稍后再看",injectMode:2,styleScope:1,aSelector:".bili-video-card__author",textSelector:".bili-video-card__text span[title]",trigger:{watch:"body",interval:1e3}}},{urlPattern:/^https:\/\/www\.bilibili\.com\/?(?:\?[^#]*)?(?:#.*)?$/,rule:{name:"首页推荐",injectMode:2,styleScope:1,aSelector:".bili-video-card__info--owner, .bili-video-card__author, a.up-name",textSelector:".bili-video-card__info--author, .bili-video-card__text span[title], .up-name__text",trigger:{watch:"#app",interval:1e3}}},{urlPattern:/^https:\/\/search\.bilibili\.com\/(?:all|video|bangumi|pgc|live|article|user)(?:\?[^#]*)?(?:#.*)?$/,rule:{name:"搜索结果",injectMode:2,styleScope:1,aSelector:".bili-video-card__info--owner, .bili-video-card__author, a.up-name",textSelector:".bili-video-card__info--author, .bili-video-card__text span[title], .up-name__text",trigger:{watch:"#app",interval:1e3}}},{urlPattern:/^https:\/\/www\.bilibili\.com\/v\/popular\/?(?:\?[^#]*)?(?:#.*)?$/,rule:{name:"热门页",injectMode:2,styleScope:1,aSelector:".bili-video-card__info--owner, .bili-video-card__author, a.up-name",textSelector:".bili-video-card__info--author, .bili-video-card__text span[title], .up-name__text",trigger:{watch:"#app",interval:1e3}}},{urlPattern:/^https:\/\/www\.bilibili\.com\/v\/[a-z]+\/?(?:\?[^#]*)?(?:#.*)?$/,rule:{name:"分区页",injectMode:2,styleScope:1,aSelector:".bili-video-card__info--owner, .bili-video-card__author, a.up-name",textSelector:".bili-video-card__info--author, .bili-video-card__text span[title], .up-name__text",trigger:{watch:"#app",interval:1e3}}},{urlPattern:/^https:\/\/www\.bilibili\.com\/c\/[a-z0-9_-]+\/?(?:\?[^#]*)?(?:#.*)?$/,rule:{name:"频道页",injectMode:2,styleScope:1,aSelector:".bili-video-card__info--owner, .bili-video-card__author, a.up-name",textSelector:".bili-video-card__info--author, .bili-video-card__text span[title], .up-name__text",trigger:{watch:"#app",interval:1e3}}},{urlPattern:/^https:\/\/[a-z0-9.]+\.bilibili\.com\/.*/,rule:{name:"评论区",injectMode:2,styleScope:2,aSelector:"#user-name a",trigger:{watch:"div#contents",interval:500},dynamicWatch:true}},{urlPattern:/^https:\/\/message\.bilibili\.com\/(?:[^#]*)?(?:#\/)?whisper(?:\/|$)/,rule:{name:"个人消息 - 私信",injectMode:3,styleScope:1,aSelector:'div[data-id^="contact"], div[class^="_ContactName_"]',textSelector:'div[class*="_SessionItem__Name"], div[class^="_ContactName_"]',trigger:{watch:'div[class^="_IM_"]',interval:2e3},ignoreProcessed:true}},{urlPattern:/^https:\/\/space\.bilibili\.com\/\d+\/dynamic\/*/,rule:{name:"个人空间动态",injectMode:2,styleScope:1,aSelector:"div.bili-dyn-title span.bili-dyn-title__text",trigger:{watch:".bili-dyn-list",interval:1e3}}},{urlPattern:/^https:\/\/message\.bilibili\.com\/(?:[^#]*)?(?:#\/)?(?:reply|love|at)(?:\/|$)/,rule:{name:"个人消息 - 回复/赞/AT",injectMode:2,styleScope:1,aSelector:"a.interaction-item__uname",trigger:{watch:"div.message-content",interval:1e3}}},{urlPattern:/^https:\/\/t\.bilibili\.com\/.*/,rule:{name:"动态页",injectMode:2,styleScope:2,aSelector:"div.bili-dyn-title span.bili-dyn-title__text",trigger:{watch:"div.bili-dyn-item__main",interval:1e3},dynamicWatch:true,matchByName:true}},{urlPattern:/^https:\/\/[a-z0-9.]+\.bilibili\.com\/.*/,rule:{name:"最近 - UP动态",injectMode:2,styleScope:2,aSelector:"div.user-name a",trigger:{watch:"div.header-content-panel",interval:1e3}}},{urlPattern:/^https:\/\/[a-z0-9.]+\.bilibili\.com\/.*/,rule:{name:"最近 - 收藏夹",injectMode:2,styleScope:1,aSelector:"span.header-fav-card__info--name",textSelector:"span.header-fav-card__info--name span",trigger:{watch:"div.favorite-panel-popover",interval:1e3}}},{urlPattern:/^https:\/\/[a-z0-9.]+\.bilibili\.com\/.*/,rule:{name:"最近 - 历史",injectMode:2,styleScope:2,textSelector:"div.header-history-card__info--name span",trigger:{watch:"div.history-panel-popover",interval:1e3},matchByName:true}},{urlPattern:/^https:\/\/[a-z0-9.]+\.bilibili\.com\/.*/,rule:{name:"最近 - 正在直播",injectMode:2,styleScope:1,aSelector:"a.up-item",textSelector:"div.up-name",trigger:{watch:"div.living-up-list",interval:1e3},matchByName:true,textSource:"watch"}},{urlPattern:/^https:\/\/www.bilibili\.com\/opus\/\d+/,rule:{name:"新版动态",injectMode:1,styleScope:2,aSelector:"div.opus-module-author__name"}}]},getGlobalConfig(void 0));if(dataset.issues)throw new ValiError(dataset.issues);return dataset.value}().map(function(entry){return {urlPattern:entry.urlPattern,rule:normalizeRule(entry.rule)}}),LOG_STYLE="color: white; background: #2196F3; padding: 2px 4px; border-radius: 3px; font-weight: bold;",PREFIX="[Bili-User-Memo]",logger_info=(msg,...args)=>{console.log(`%c${PREFIX}%c`,"background: #9e9e9e; "+LOG_STYLE,"",msg,...args);},logger_error=(msg,...args)=>{console.error(`%c${PREFIX}%c`,"background: #9e9e9e; "+LOG_STYLE,"",msg,...args);},logger_warn=(msg,...args)=>{console.warn(`%c${PREFIX}%c`,"background: #9e9e9e; "+LOG_STYLE,"",msg,...args);},logger_debug=(msg,...args)=>{},DIRECT_UID_ATTRS=["data-user-profile-id","bilisponsor-userid"],SPACE_UID_REGEX=/space\.bilibili\.com\/(\d+)/,UID_STRATEGIES=[function(el){return getAttr(el,"data-bili-uid")||parseUidFromDataId(getAttr(el,"data-id"))||function(el,names){for(const name of names){const value=getAttr(el,name);if(value)return value}return null}(el,DIRECT_UID_ATTRS)},function(el){const root=el.closest("div.bili-dyn-item__main");return root?getAttrFromQuery(root,"[bilisponsor-userid]","bilisponsor-userid")||getAttrFromQuery(root,"[data-user-profile-id]","data-user-profile-id")||parseUidFromDataId(getAttrFromQuery(root,"[data-id]","data-id")):null},function(el){const href=el.getAttribute("href")||location.href;if(!href)return null;const match=href.match(SPACE_UID_REGEX);return normalizeUid(match?.[1])},()=>function(){const initialState=_unsafeWindow.__INITIAL_STATE__;return normalizeUid(initialState?.detail?.basic?.uid)||normalizeUid(initialState?.detail?.modules?.find(module=>module.module_author)?.module_author?.mid)}()],userStore=new class{users=[];_displayMode=2;listeners=new Set;isSystemChanging=false;constructor(){this.refreshData();this.listenToRemoteChanges();}refreshData(){const{raw:rawUsers,users:nextUsers}=function(){const raw=_GM_getValue("biliUsers",[]);return {raw:raw,users:normalizeUsers(raw)}}(),nextDisplayMode=normalizeDisplayMode(_GM_getValue("displayMode",2)),usersChanged=!function(a,b){if(a.length!==b.length)return false;for(let i=0;i{remote&&!this.isSystemChanging&&this.applyRemoteUsers(newValue);});_GM_addValueChangeListener("displayMode",(_name,_oldValue,newValue,remote)=>{remote&&this.applyRemoteDisplayMode(newValue);});}applyRemoteUsers(rawUsers){try{this.withSystemLock(()=>{this.users=normalizeUsers(rawUsers);});this.emitUsers("remote");}catch(error){logger_error("同步外部数据失败",error);}}applyRemoteDisplayMode(rawMode){const nextMode=normalizeDisplayMode(rawMode);if(nextMode!==this._displayMode){this._displayMode=nextMode;this.emitDisplayMode("remote");}}get displayMode(){return this._displayMode}getUsers(){return function(users){return users.map(u=>({...u}))}(this.users)}subscribe(listener){this.listeners.add(listener);return ()=>this.listeners.delete(listener)}setDisplayMode(mode){const nextMode=normalizeDisplayMode(mode);if(nextMode!==this._displayMode){this._displayMode=nextMode;this.withSystemLock(()=>{!function(mode){_GM_setValue("displayMode",mode);}(nextMode);});this.emitDisplayMode("update");}}ensureUser(uid,originalName){const existing=this.users.find(u=>u.id===uid);if(existing){!originalName||existing.nickname&&existing.nickname!==uid||(existing.nickname=originalName);return existing}return {id:uid,nickname:originalName||uid,avatar:getUserAvatar(uid),memo:""}}findUserByName(name){if(name)return this.users.find(u=>u.nickname===name.trim())}updateUser(uid,updates,fallbackName=""){if(!uid)return false;const userIndex=this.findUserIndex(uid),existing=-1===userIndex?null:this.users[userIndex],nextMemo=this.resolveNextMemo(existing,updates);if(!existing)return this.createUserIfNeeded(uid,updates,fallbackName,nextMemo);if(!nextMemo)return this.removeExistingUser(uid,userIndex);const nextNickname=void 0!==updates.nickname?updates.nickname.trim():existing.nickname,nextAvatar=void 0!==updates.avatar?updates.avatar:existing.avatar;if(existing.memo===nextMemo&&existing.nickname===nextNickname&&existing.avatar===nextAvatar)return false;existing.memo=nextMemo;existing.nickname=nextNickname||uid;existing.avatar=nextAvatar;this.commitUsers("update",[uid]);logger_info(`📝 备注已更新 | UID:${uid} -> ${nextMemo}`);return true}updateUserMemo(uid,newMemo,fallbackName=""){return this.updateUser(uid,{memo:newMemo},fallbackName)}findUserIndex(uid){return this.users.findIndex(u=>u.id===uid)}resolveNextMemo(existing,updates){return void 0!==updates.memo?updates.memo.trim():(existing?.memo||"").trim()}createUserIfNeeded(uid,updates,fallbackName,nextMemo){if(!nextMemo)return false;this.users.push({id:uid,nickname:(updates.nickname||fallbackName||uid).trim(),avatar:updates.avatar??getUserAvatar(uid),memo:nextMemo});this.commitUsers("update",[uid]);logger_info(`📝 备注已更新 | UID:${uid} -> ${nextMemo}`);return true}removeExistingUser(uid,userIndex){this.users.splice(userIndex,1);this.commitUsers("remove",[uid]);logger_info(`🗑️ 备注清空,已删除用户记录 | UID:${uid}`);return true}removeUser(uid){if(!uid)return false;const index=this.users.findIndex(u=>u.id===uid);if(-1===index)return false;this.users.splice(index,1);this.commitUsers("remove",[uid]);return true}removeUsers(ids){const idSet=new Set(ids.filter(Boolean));if(0===idSet.size)return 0;const before=this.users.length;this.users=this.users.filter(u=>!idSet.has(u.id));const removed=before-this.users.length;removed>0&&this.commitUsers("remove",Array.from(idSet));return removed}upsertImportedUsers(importedUsers){const normalized=normalizeUsers(importedUsers);if(0===normalized.length)return {added:0,updated:0};let added=0,updated=0;const changedIds=[],userMap=new Map(this.users.map(u=>[u.id,u]));normalized.forEach(incoming=>{const existing=userMap.get(incoming.id);if(existing){if(existing.nickname!==incoming.nickname||existing.avatar!==incoming.avatar||existing.memo!==incoming.memo){existing.nickname=incoming.nickname;existing.avatar=incoming.avatar;existing.memo=incoming.memo;updated++;changedIds.push(incoming.id);}}else {this.users.push({...incoming});userMap.set(incoming.id,this.users[this.users.length-1]);added++;changedIds.push(incoming.id);}});(added>0||updated>0)&&this.commitUsers("import",changedIds);return {added:added,updated:updated}}updateUserProfiles(profiles){if(0===profiles.length)return 0;let updatedCount=0;const changedIds=[],userMap=new Map(this.users.map(u=>[u.id,u]));profiles.forEach(profile=>{const target=userMap.get(profile.id);if(target&&(target.nickname!==profile.nickname||target.avatar!==profile.avatar)){target.nickname=profile.nickname||target.nickname;target.avatar=profile.avatar||target.avatar;updatedCount++;changedIds.push(profile.id);}});updatedCount>0&&this.commitUsers("profile",changedIds);return updatedCount}withSystemLock(action){this.isSystemChanging=true;try{action();}finally{this.isSystemChanging=false;}}commitUsers(reason,changedIds=[]){this.withSystemLock(()=>{saveUsersToStorage(this.users);});this.emitUsers(reason,changedIds);}emitUsers(reason,changedIds=[]){this.emit({type:"users",users:this.getUsers(),reason:reason,changedIds:changedIds});}emitDisplayMode(reason){this.emit({type:"displayMode",displayMode:this._displayMode,reason:reason});}emit(change){this.listeners.forEach(listener=>{try{listener(change);}catch(error){logger_error("UserStore 监听器执行失败",error);}});}},GLOBAL_STYLE_SHEET=new CSSStyleSheet;GLOBAL_STYLE_SHEET.replaceSync(".editable-textarea{font-size:var(--custom-font-size, 13px);min-width:2ch;color:var(--custom-font-color, black);margin-right:8px;padding:4px 6px;display:inline-block;white-space:nowrap;background:var(--glass);border:none;border-bottom:2px solid var(--overlay);outline:none;border-radius:6px 6px 0 0;text-shadow:0 1px 2px var(--overlay);transition:border-color .2s ease,background .2s ease,opacity .2s ease;opacity:.85}.editable-textarea:hover{background:var(--glass)}.bili-memo-tag{cursor:pointer;display:inline;vertical-align:middle;color:var(--custom-font-color, black);font-size:var(--custom-font-size, 13px)}.bili-memo-input{background:var(--bg-main);border:1px solid var(--primary-color);font-size:var(--custom-font-size, 13px);padding:0 4px;margin-left:4px;border-radius:4px;outline:none;width:var(--memo-input-width, 60px);display:inline-block;height:calc(var(--custom-font-size, 14px) + 6px);line-height:calc(var(--custom-font-size, 14px) + 6px);vertical-align:middle}:host-context(.memo-container-dark-theme) .bili-memo-tag,.memo-container-dark-theme .bili-memo-tag{color:var(--custom-font-color, white)}:host-context(.memo-container-dark-theme) .editable-textarea,.memo-container-dark-theme .editable-textarea{background:var(--overlay);border-bottom-color:var(--glass);color:var(--custom-font-color, white)}:host-context(.memo-container-dark-theme) .bili-memo-input,.memo-container-dark-theme .bili-memo-input{background:var(--bg-main);border-color:var(--primary-color);color:var(--text-main)}");const CUSTOM_MEMO_STYLE_SHEET=new CSSStyleSheet;CUSTOM_MEMO_STYLE_SHEET.replaceSync("");const wrapperCache=new WeakMap;class DynamicRuleWatcher{constructor(rule,onTrigger){this.rule=rule;this.onTrigger=onTrigger;}static originalAttachShadow=Element.prototype.attachShadow;static attachShadowPatched=false;static attachShadowListeners=new Set;legacyObserver=null;legacyPollTimer=null;discoveryObservers=new Map;instanceObservers=new Map;handleShadowAttached=shadowRoot=>{this.observeDiscoveryScope(shadowRoot);this.scanAndAttachNewTargets();};start(){this.rule.dynamicWatch?this.startGlobalWatch():this.tryAttachOrPollLegacy();}stop(){if(this.legacyPollTimer){clearInterval(this.legacyPollTimer);this.legacyPollTimer=null;}if(this.legacyObserver){this.legacyObserver.disconnect();this.legacyObserver=null;}this.unregisterAttachShadowListener();this.discoveryObservers.forEach(observer=>observer.disconnect());this.discoveryObservers.clear();this.instanceObservers.forEach(({observer:observer})=>observer.disconnect());this.instanceObservers.clear();}startGlobalWatch(){this.rule.name,this.rule.trigger.watch;this.registerAttachShadowListener();this.observeDiscoveryScope(document);this.scanAndAttachNewTargets();}static ensureAttachShadowPatched(){if(DynamicRuleWatcher.attachShadowPatched)return;const originalAttachShadow=DynamicRuleWatcher.originalAttachShadow;Element.prototype.attachShadow=function(init){const shadowRoot=originalAttachShadow.call(this,init);for(const listener of DynamicRuleWatcher.attachShadowListeners)try{listener(shadowRoot);}catch(error){logger_debug();}return shadowRoot};DynamicRuleWatcher.attachShadowPatched=true;}registerAttachShadowListener(){DynamicRuleWatcher.ensureAttachShadowPatched();DynamicRuleWatcher.attachShadowListeners.add(this.handleShadowAttached);}unregisterAttachShadowListener(){DynamicRuleWatcher.attachShadowListeners.delete(this.handleShadowAttached);}observeDiscoveryScope(scope){if(this.discoveryObservers.has(scope))return;const observer=new MutationObserver(mutations=>{let needScan=false,nodesRemoved=false;for(const mutation of mutations){if(mutation.addedNodes.length>0){needScan=true;mutation.addedNodes.forEach(node=>this.discoverShadowScopesFromNode(node));}mutation.removedNodes.length>0&&(nodesRemoved=true);}if(needScan){this.scanAndAttachNewTargets();this.bridgeShadowMutationsToWatchScopes(scope);}if(nodesRemoved){this.cleanupDetachedTargets();this.cleanupDetachedDiscoveryScopes();}});observer.observe(scope,{childList:true,subtree:true});this.discoveryObservers.set(scope,observer);this.discoverShadowScopes(scope);}discoverShadowScopes(scope){scope.querySelectorAll("*").forEach(element=>this.observeHostShadowScope(element));}discoverShadowScopesFromNode(node){if(node instanceof Element){this.observeHostShadowScope(node);node.querySelectorAll("*").forEach(element=>this.observeHostShadowScope(element));}}observeHostShadowScope(element){const shadowRoot=element.shadowRoot;shadowRoot&&this.observeDiscoveryScope(shadowRoot);}scanAndAttachNewTargets(){querySelectorShadowDom.querySelectorAllDeep(this.rule.trigger.watch).forEach(target=>{const scope=resolveWatchScope(target),keyNode=target,current=this.instanceObservers.get(keyNode);if(current){if(current.scope!==scope){this.rule.name;current.observer.disconnect();this.attachInstanceWatcher(keyNode,scope);}}else {this.rule.name;this.attachInstanceWatcher(keyNode,scope);}});}createScopeObserver(scope){const observer=new MutationObserver(mutations=>{(function(mutations){return mutations.some(m=>m.addedNodes.length>0)})(mutations)&&this.onTrigger(this.rule,scope);});observer.observe(scope,{childList:true,subtree:true});return observer}bridgeShadowMutationsToWatchScopes(scope){if(!(scope instanceof ShadowRoot))return;if(0===this.instanceObservers.size)return;const touchedScopes=new Set;for(const{scope:watchScope}of this.instanceObservers.values())watchScope instanceof ShadowRoot&&watchScope===scope||isNodeInsideScope(scope,watchScope)&&touchedScopes.add(watchScope);0!==touchedScopes.size&&touchedScopes.forEach(watchScope=>this.onTrigger(this.rule,watchScope));}attachInstanceWatcher(keyNode,scope){const observer=this.createScopeObserver(scope);this.instanceObservers.set(keyNode,{observer:observer,scope:scope});this.onTrigger(this.rule,scope);}cleanupDetachedTargets(){for(const[node,{observer:observer}]of this.instanceObservers)if(!node.isConnected){logger_debug(this.rule.name);observer.disconnect();this.instanceObservers.delete(node);}}cleanupDetachedDiscoveryScopes(){for(const[scope,observer]of this.discoveryObservers)if(scope!==document&&!scope.isConnected){observer.disconnect();this.discoveryObservers.delete(scope);}}tryAttachOrPollLegacy(){this.attachLegacy()||this.legacyPollTimer||(this.legacyPollTimer=window.setInterval(()=>{if(this.attachLegacy()){this.legacyPollTimer&&clearInterval(this.legacyPollTimer);this.legacyPollTimer=null;this.rule.name;}},800));}attachLegacy(){const watchTarget=querySelectorShadowDom.querySelectorDeep(this.rule.trigger.watch);if(!watchTarget)return false;const scope=resolveWatchScope(watchTarget);this.legacyObserver=this.createScopeObserver(scope);this.onTrigger(this.rule,scope);return true}}class PollingRuleWatcher{constructor(rule,onTrigger){this.rule=rule;this.onTrigger=onTrigger;}pollTimer=null;start(){this.rule.name,this.rule.trigger.intervalMs,this.rule.trigger.watch;this.tick();this.pollTimer=window.setInterval(()=>this.tick(),this.rule.trigger.intervalMs);}stop(){if(this.pollTimer){clearInterval(this.pollTimer);this.pollTimer=null;}this.rule.name;}tick(){const watchTarget=querySelectorShadowDom.querySelectorDeep(this.rule.trigger.watch);if(!watchTarget)return;const scope=resolveWatchScope(watchTarget);this.onTrigger(this.rule,scope);}}class PageInjector{domReady=false;lastUrl="";staticRetryTimers=[];staticRetryToken=0;activeWatchers=new Map;activePollingWatchers=new Map;ruleDebounceTimers=new Map;constructor(){logger_info("🚀 PageInjector 正在启动...");userStore.refreshData();userStore.subscribe(change=>this.handleStoreChange(change));this.startUrlMonitor();this.onDomReady(async()=>{await this.waitForBiliEnvironment();await new Promise(resolve=>setTimeout(resolve,100));this.domReady=true;this.handleUrlChange();});}refreshData(){userStore.refreshData();this.domReady&&this.scanActiveRules(document);}handleStoreChange(change){if(this.domReady)if("displayMode"!==change.type)if("users"!==change.type)this.refreshRenderedNodes(change.users,change.displayMode);else {this.refreshRenderedNodes(change.users,userStore.displayMode,change.changedIds);"import"===change.reason&&this.scanMatchByNameRules(document);}else this.refreshRenderedNodes(userStore.getUsers(),change.displayMode);}refreshRenderedNodes(users,displayMode,changedIds){!function(users,displayMode,changedIds){const userMap=new Map(users.map(u=>[u.id,u]));changedIds&&changedIds.length>0?Array.from(new Set(changedIds.filter(Boolean))).forEach(uid=>{const selector=`[data-bili-uid="${function(value){return "undefined"!=typeof CSS&&"function"==typeof CSS.escape?CSS.escape(value):value.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}(uid)}"]`,tags=querySelectorShadowDom.querySelectorAllDeep(selector),user=userMap.get(uid);tags.forEach(tag=>refreshTag(tag,user,displayMode));}):querySelectorShadowDom.querySelectorAllDeep("[data-bili-uid]").forEach(tag=>{const uid=tag.getAttribute("data-bili-uid");uid&&refreshTag(tag,userMap.get(uid),displayMode);});}(users,displayMode,changedIds);}scanActiveRules(scope){const activeRules=[...this.activeWatchers.keys(),...this.activePollingWatchers.keys()];0!==activeRules.length&&this.scanSpecificRules(activeRules,scope);}startUrlMonitor(){this.lastUrl=unsafeWindow.location.href;window.setInterval(()=>{const currentUrl=unsafeWindow.location.href;if(currentUrl!==this.lastUrl){this.lastUrl=currentUrl;this.handleUrlChange();}},1e3);}handleUrlChange(){if(!this.domReady)return;const matchedRules=this.getMatchedRules(),groups=this.groupRulesByMode(matchedRules);this.applyStaticRules(groups.staticRules,document);this.reconcileWatchers(groups.dynamicRules);this.reconcilePollingWatchers(groups.pollingRules);}groupRulesByMode(rules){const groups={staticRules:[],dynamicRules:[],pollingRules:[]};rules.forEach(rule=>{rule.injectMode!==InjectionMode.Static?rule.injectMode!==InjectionMode.Dynamic?groups.pollingRules.push(rule):groups.dynamicRules.push(rule):groups.staticRules.push(rule);});return groups}applyStaticRules(staticRules,scope){if(0!==staticRules.length){this.scanSpecificRules(staticRules,scope);this.scheduleStaticRuleRetries(staticRules,scope);}else this.clearStaticRetryTimers();}reconcileWatchers(nextRules){for(const[rule,watcher]of this.activeWatchers)if(!nextRules.includes(rule)){watcher.stop();this.clearRuleDebounceTimers(rule);this.activeWatchers.delete(rule);}nextRules.forEach(rule=>{if(this.activeWatchers.has(rule))return;const watcher=new DynamicRuleWatcher(rule,(r,scope)=>{this.scheduleRuleScan(r,r.trigger.debounceMs,scope);});this.activeWatchers.set(rule,watcher);watcher.start();});}reconcilePollingWatchers(nextRules){for(const[rule,watcher]of this.activePollingWatchers)if(!nextRules.includes(rule)){watcher.stop();this.activePollingWatchers.delete(rule);}nextRules.forEach(rule=>{if(this.activePollingWatchers.has(rule))return;const watcher=new PollingRuleWatcher(rule,(r,scope)=>{this.scanSpecificRules([r],scope);});this.activePollingWatchers.set(rule,watcher);watcher.start();});}scanMatchByNameRules(scope){const rules=[...this.activeWatchers.keys(),...this.activePollingWatchers.keys()].filter(rule=>Boolean(rule.matchByName));0!==rules.length&&this.scanSpecificRules(rules,scope);}clearStaticRetryTimers(){this.staticRetryToken++;this.staticRetryTimers.forEach(timerId=>clearTimeout(timerId));this.staticRetryTimers=[];}scheduleStaticRuleRetries(staticRules,scope){this.clearStaticRetryTimers();const token=this.staticRetryToken;[350,900].forEach(delay=>{const timerId=window.setTimeout(()=>{this.domReady&&token===this.staticRetryToken&&this.scanSpecificRules(staticRules,scope);},delay);this.staticRetryTimers.push(timerId);});}scheduleRuleScan(rule,delay,scope){let scopeTimers=this.ruleDebounceTimers.get(rule);if(!scopeTimers){scopeTimers=new Map;this.ruleDebounceTimers.set(rule,scopeTimers);}const existingTimer=scopeTimers.get(scope);existingTimer&&clearTimeout(existingTimer);const timerId=window.setTimeout(()=>{const activeScopeTimers=this.ruleDebounceTimers.get(rule);activeScopeTimers?.delete(scope);activeScopeTimers&&0===activeScopeTimers.size&&this.ruleDebounceTimers.delete(rule);this.scanSpecificRules([rule],scope);},delay);scopeTimers.set(scope,timerId);}clearRuleDebounceTimers(rule){const scopeTimers=this.ruleDebounceTimers.get(rule);if(scopeTimers){scopeTimers.forEach(timerId=>clearTimeout(timerId));this.ruleDebounceTimers.delete(rule);}}scanSpecificRules(rules,scope){if(0===rules.length)return;const queue=[...rules],runChunk=deadline=>{(async()=>{for(;queue.length>0&&deadline.timeRemaining()>1;){const rule=queue.shift();await this.scanAndInjectRule(rule,scope);}queue.length>0&&this.requestIdle(runChunk);})();};this.requestIdle(runChunk);}requestIdle(cb){(window.requestIdleCallback||(fn=>setTimeout(()=>fn({timeRemaining:()=>16}),16)))(cb,{timeout:1e3});}async scanAndInjectRule(rule,scope){const selector=this.buildRuleSelector(rule);if(!selector)return;const elements=querySelectorShadowDom.querySelectorAllDeep(selector,scope);this.logRuleScanResult(rule,selector,elements.length);0!==elements.length&&elements.forEach(el=>{this.applyRuleToElement(el,rule);});}buildRuleSelector(rule){const baseSelector=rule.aSelector||rule.textSelector;return baseSelector?rule.ignoreProcessed?baseSelector:`${baseSelector}:not([data-bili-processed])`:null}logRuleScanResult(rule,selector,count){0!==count&&rule.injectMode!==InjectionMode.Static&&rule.injectMode===InjectionMode.Polling&&rule.name;}async applyRuleToElement(el,rule){if(el.classList.contains("editable-textarea")){el.setAttribute("data-bili-processed","true");return}const originalName=function(el,rule){return readPreferredText(resolveRuleTextTarget(el,rule))||readPreferredText(el)||""}(el,rule),uid=this.resolveElementUid(el,rule,originalName);if(!uid)return;const user=userStore.ensureUser(uid,originalName);await async function(el,user,rule,meta){const displayMode=userStore.displayMode;switch(rule.styleScope){case StyleScope.Minimal:return function(element,user,meta,displayMode){if(!element)return false;ensureStylesForElement(element);syncRenderedNodeState(element,user,meta.originalName,displayMode);syncElementMeta(element,meta);return true}(resolveRuleTextTarget(el,rule),user,meta,displayMode);case StyleScope.Editable:return function(el,user,rule,meta,displayMode){let wrapper=wrapperCache.get(el);if(!wrapper&&el.nextElementSibling?.classList.contains("editable-textarea")){wrapper=el.nextElementSibling;wrapperCache.set(el,wrapper);}if(!wrapper){wrapper=document.createElement("span");wrapper.classList.add("editable-textarea");wrapper.setAttribute("data-bili-processed","true");wrapper.addEventListener("click",e=>{e.stopPropagation();e.preventDefault();const uid=wrapper?.dataset.biliUid,originalName=wrapper?.dataset.biliOriginal||meta.originalName;if(!uid)return;const latestUser=userStore.ensureUser(uid,originalName);!function(targetElement,user){if(!user||targetElement.querySelector("input.bili-memo-input"))return;const originalName=targetElement.dataset.biliOriginal||targetElement.textContent||"",currentMemo=user.memo||originalName,input=document.createElement("input");input.type="text";input.value=currentMemo;input.className="bili-memo-input";input.placeholder="输入备注...";const updateWidth=()=>{const len=(input.value||"").replace(/[^\x00-\xff]/g,"xx").length;input.style.width=`${Math.max(8*len+20,80)}px`;};updateWidth();input.addEventListener("input",updateWidth);const parent=targetElement.parentElement;if(!parent)return;targetElement.style.display="none";parent.insertBefore(input,targetElement.nextSibling);input.focus();let exited=false;const saveAndExit=shouldSave=>{if(exited)return;exited=true;const newValue=input.value.trim();input.remove();targetElement.style.display="";if(shouldSave&&newValue!==currentMemo){userStore.updateUserMemo(user.id,newValue,originalName);syncRenderedNodeState(targetElement,{...user,memo:newValue},originalName,userStore.displayMode,{isEditableWrapper:targetElement.classList.contains("editable-textarea")});}};input.addEventListener("keydown",e=>{if(!e.isComposing)if("Enter"===e.key){e.preventDefault();saveAndExit(true);}else if("Escape"===e.key){e.preventDefault();saveAndExit(false);}});input.addEventListener("blur",()=>saveAndExit(true));input.addEventListener("click",e=>e.stopPropagation());}(wrapper,latestUser);});el.style.display="none";el.insertAdjacentElement("afterend",wrapper);wrapperCache.set(el,wrapper);}syncRenderedNodeState(wrapper,user,meta.originalName,displayMode,{isEditableWrapper:true});rule.fontSize&&wrapper.style.setProperty("--custom-font-size",rule.fontSize);syncElementMeta(wrapper,meta);ensureStylesForElement(wrapper);return true}(el,user,rule,meta,displayMode);default:logger_warn(`⚠️ 不支持的样式作用域: ${rule.styleScope}`);return false}}(el,user,rule,{uid:uid,originalName:originalName})&&el.setAttribute("data-bili-processed","true");}resolveElementUid(el,rule,originalName){const uid=function(el,silent=false){for(const strategy of UID_STRATEGIES){const uid=strategy(el);if(uid)return uid}silent||logger_warn("⚠️ 无法从元素中提取 UID:",el);return null}(el,Boolean(rule.matchByName));if(uid)return uid;if(el.matches('div[class^="_ContactName_"]')){const whisperUid=this.getActiveWhisperUid();if(whisperUid)return whisperUid}return rule.matchByName&&originalName&&userStore.findUserByName(originalName)?.id||null}getActiveWhisperUid(){return document.querySelector('div[class*="_SessionItemIsActive_"][data-id^="contact_"]')?.getAttribute("data-id")?.split("_")?.[1]||null}getMatchedRules(){const currentUrl=unsafeWindow.location.href;return config.filter(entry=>entry.urlPattern.test(currentUrl)).map(entry=>entry.rule)}onDomReady(callback){"complete"!==document.readyState&&"interactive"!==document.readyState?window.addEventListener("DOMContentLoaded",()=>callback(),{once:true}):callback();}async waitForBiliEnvironment(){return new Promise(resolve=>{const check2=()=>{unsafeWindow.__VUE__?resolve():setTimeout(check2,50);};check2();})}}let pageInjector=null;const gmPersistStorage={getItem:storageKey=>_GM_getValue(`panelPrefs:${storageKey}`,"")||null,setItem(storageKey,value){_GM_setValue(`panelPrefs:${storageKey}`,value);},removeItem(storageKey){_GM_setValue(`panelPrefs:${storageKey}`,"");}},UID=string(),UserSchema=object({id:UID,nickname:string(),avatar:optional(string()),memo:string()}),UserSchemaOld=object({bid:UID,nickname:string(),memo:string(),avatar:optional(string()),info:string()}),CombinedSchema=union([array(UserSchema),record(UID,UserSchemaOld)]);class ResImpl{constructor(body,init){this.rawBody=body;this.init=init;this.body=body.stream();const{headers:headers,statusCode:statusCode,statusText:statusText,finalUrl:finalUrl,redirected:redirected}=init;this.headers=headers;this.status=statusCode;this.statusText=statusText;this.url=finalUrl;this.type="basic";this.redirected=redirected;this._bodyUsed=false;}get bodyUsed(){return this._bodyUsed}get ok(){return this.status<300}arrayBuffer(){if(this.bodyUsed)throw new TypeError("Failed to execute 'arrayBuffer' on 'Response': body stream already read");this._bodyUsed=true;return this.rawBody.arrayBuffer()}blob(){if(this.bodyUsed)throw new TypeError("Failed to execute 'blob' on 'Response': body stream already read");this._bodyUsed=true;return Promise.resolve(this.rawBody.slice(0,this.rawBody.size,this.rawBody.type))}clone(){if(this.bodyUsed)throw new TypeError("Failed to execute 'clone' on 'Response': body stream already read");return new ResImpl(this.rawBody,this.init)}formData(){if(this.bodyUsed)throw new TypeError("Failed to execute 'formData' on 'Response': body stream already read");this._bodyUsed=true;return this.rawBody.text().then(decode)}async json(){if(this.bodyUsed)throw new TypeError("Failed to execute 'json' on 'Response': body stream already read");this._bodyUsed=true;return JSON.parse(await this.rawBody.text())}text(){if(this.bodyUsed)throw new TypeError("Failed to execute 'text' on 'Response': body stream already read");this._bodyUsed=true;return this.rawBody.text()}async bytes(){if(this.bodyUsed)throw new TypeError("Failed to execute 'bytes' on 'Response': body stream already read");this._bodyUsed=true;return new Uint8Array(await this.rawBody.arrayBuffer())}}const httpMethods=["GET","POST","PUT","DELETE","PATCH","HEAD","TRACE","OPTIONS","CONNECT"];class Node{value;next;constructor(value){this.value=value;}}class Queue{#head;#tail;#size;constructor(){this.clear();}enqueue(value){const node=new Node(value);if(this.#head){this.#tail.next=node;this.#tail=node;}else {this.#head=node;this.#tail=node;}this.#size++;}dequeue(){const current=this.#head;if(current){this.#head=this.#head.next;this.#size--;this.#head||(this.#tail=void 0);return current.value}}peek(){if(this.#head)return this.#head.value}clear(){this.#head=void 0;this.#tail=void 0;this.#size=0;}get size(){return this.#size}*[Symbol.iterator](){let current=this.#head;for(;current;){yield current.value;current=current.next;}}*drain(){for(;this.#head;)yield this.dequeue();}}const limit=function(concurrency){let rejectOnClear=false;"object"==typeof concurrency&&({concurrency:concurrency,rejectOnClear:rejectOnClear=false}=concurrency);validateConcurrency(concurrency);if("boolean"!=typeof rejectOnClear)throw new TypeError("Expected `rejectOnClear` to be a boolean");const queue=new Queue;let activeCount=0;const resumeNext=()=>{if(activeCount0){activeCount++;queue.dequeue().run();}},run=async(function_,resolve,arguments_)=>{const result=(async()=>function_(...arguments_))();resolve(result);try{await result;}catch{}(()=>{activeCount--;resumeNext();})();},generator=(function_,...arguments_)=>new Promise((resolve,reject)=>{((function_,resolve,reject,arguments_)=>{const queueItem={reject:reject};new Promise(internalResolve=>{queueItem.run=internalResolve;queue.enqueue(queueItem);}).then(run.bind(void 0,function_,resolve,arguments_));activeCountactiveCount},pendingCount:{get:()=>queue.size},clearQueue:{value(){if(!rejectOnClear){queue.clear();return}const abortError=AbortSignal.abort().reason;for(;queue.size>0;)queue.dequeue().reject(abortError);}},concurrency:{get:()=>concurrency,set(newConcurrency){validateConcurrency(newConcurrency);concurrency=newConcurrency;queueMicrotask(()=>{for(;activeCount0;)resumeNext();});}},map:{async value(iterable,function_){const promises=Array.from(iterable,(value,index)=>this(function_,value,index));return Promise.all(promises)}}});return generator}(2);"undefined"==typeof GM&&(window.GM={xmlHttpRequest:_GM_xmlhttpRequest});const MIXIN_KEY_ENC_TAB=[46,47,18,2,53,8,23,32,15,50,10,31,58,3,45,35,27,43,5,49,33,9,42,19,29,28,14,39,12,38,41,13,37,48,7,16,24,55,40,61,26,17,0,1,60,51,30,4,22,25,54,21,56,59,6,63,57,62,11,36,20,34,44,52],cache=new Map,getUserInfo=function(fn){return async(...args)=>{const key=JSON.stringify(args),now=Date.now();if(cache.has(key)&&now-cache.get(key).time<3e5)return cache.get(key).data;const result=await fn(...args);cache.set(key,{data:result,time:now});return result}}((...args)=>limit(async()=>{await new Promise(resolve=>setTimeout(resolve,300+500*Math.random()));return async function(mid){try{const{img_key:img_key,sub_key:sub_key}=await async function(){const now=Date.now(),cache2=_GM_getValue("bili_wbi_keys",null);if(cache2&&now-cache2.timestamp<36e5)return {img_key:cache2.img_key,sub_key:cache2.sub_key};try{const res=await GM_fetch("https://api.bilibili.com/x/web-interface/nav",{headers:{Referer:"https://www.bilibili.com/"}}),json=await res.json(),{img_url:img_url,sub_url:sub_url}=json.data.wbi_img,keys={img_key:img_url.slice(img_url.lastIndexOf("/")+1,img_url.lastIndexOf(".")),sub_key:sub_url.slice(sub_url.lastIndexOf("/")+1,sub_url.lastIndexOf("."))};_GM_setValue("bili_wbi_keys",{...keys,timestamp:now});return keys}catch(err){logger_error("Failed to fetch WBI keys",err);throw new Error("WBI key 初始化失败")}}(),url=`https://api.bilibili.com/x/space/wbi/acc/info?${function(params,img_key,sub_key){const mixin_key=(orig=>MIXIN_KEY_ENC_TAB.map(n=>orig[n]).join("").slice(0,32))(img_key+sub_key),curr_time=Math.round(Date.now()/1e3),chr_filter=/[!'()*]/g,signedParams={...params,wts:curr_time},query=Object.entries(signedParams).sort(([a],[b])=>a.localeCompare(b)).map(([key,value])=>{const filteredValue=String(value).replace(chr_filter,"");return `${encodeURIComponent(key)}=${encodeURIComponent(filteredValue)}`}).join("&");return `${query}&w_rid=${(message=>{const buffer=(new TextEncoder).encode(message),n=buffer.length,words=new Uint32Array(1+(n+8>>6)<<4);for(let i=0;i>2]|=buffer[i]<>2]|=128<4294967296*Math.abs(Math.sin(i+1))>>>0),S=[7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21],rotl=(x,n2)=>x<>>32-n2;for(let i=0;iview.setUint32(4*i,val,!0));return Array.from(new Uint8Array(outBuf)).map(b2=>b2.toString(16).padStart(2,"0")).join("")})(query+mixin_key)}`}({mid:mid,token:"",platform:"web",web_location:1550101},img_key,sub_key)}`,response=await GM_fetch(url,{headers:{Referer:"https://space.bilibili.com/"}});if(!response.ok)throw new Error(`HTTP error! status: ${response.status}`);const res=await response.json();if(0!==res.code)throw new Error(`Bilibili API error: ${res.message}`);return {nickname:res.data.name,avatar:res.data.face+"@96w_96h_1c_1s.avif"}}catch(error){logger_error("getUserInfo failed",error);throw error}}(...args)})),DISPLAY_MODE_OPTIONS=[{value:0,label:"昵称"},{value:1,label:"备注(昵称)"},{value:2,label:"昵称(备注)"},{value:3,label:"备注"}];let panelComponentsRegistered=false,panelBindingsRegistered=false;(async()=>{Alpine.plugin(module_default);const currentScopePattern=function(rawUrl=window.location.href){const url=new URL(rawUrl),firstPathSegment=url.pathname.split("/").filter(Boolean)[0];return firstPathSegment?`${url.origin}/${firstPathSegment}/*`:`${url.origin}/*`}(),pageDisabled=function(rawUrl=window.location.href){const target=function(rawUrl){const url=new URL(rawUrl);return `${url.origin}${url.pathname}`}(rawUrl);return loadDisabledPageScopes().some(pattern=>function(pattern){if(pattern.endsWith("/*")){const base=pattern.slice(0,-2).replace(/[.+?^${}()|[\]\\]/g,"\\$&");return new RegExp(`^${base}(?:/.*)?$`)}const escaped=pattern.split("*").map(chunk=>chunk.replace(/[.+?^${}()|[\]\\]/g,"\\$&")).join(".*");return new RegExp(`^${escaped}$`)}(pattern).test(target))}();pageDisabled?_GM_registerMenuCommand("✅在此页面启用",()=>{!function(scopePattern){const patterns=loadDisabledPageScopes(),next=patterns.filter(pattern=>pattern!==scopePattern);next.length!==patterns.length&&saveDisabledPageScopes(next);}(currentScopePattern);location.reload();}):_GM_registerMenuCommand("❌在此页面禁用",()=>{!function(scopePattern){const patterns=loadDisabledPageScopes();if(!patterns.includes(scopePattern)){patterns.push(scopePattern);saveDisabledPageScopes(patterns);}}(currentScopePattern);location.reload();});_GM_registerMenuCommand("❓帮助",()=>{window.open("https://github.com/kaixinol/Bilibili-User-Memo?tab=readme-ov-file#bilibili-%E7%94%A8%E6%88%B7%E5%A4%87%E6%B3%A8-ui-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E");});_GM_registerMenuCommand("❤️给作者一杯咖啡☕",()=>{window.open("https://s2.loli.net/2025/08/04/1hjKA5qwXHS8Glu.webp");});_GM_registerMenuCommand("🐛反馈",()=>{window.open("https://github.com/kaixinol/Bilibili-User-Memo/issues");});if(pageDisabled)console.info(`[Bilibili-User-Memo] 当前页面已禁用: ${currentScopePattern}`);else {_unsafeWindow.Alpine=Alpine;pageInjector||(pageInjector=new PageInjector);initMainPanel();}})(); })(Alpine, querySelectorShadowDom);