diff --git a/docs/assets/ts/artplayer-plugin-danmuku.d.ts b/docs/assets/ts/artplayer-plugin-danmuku.d.ts index 554ce0fe2..f6d12b576 100644 --- a/docs/assets/ts/artplayer-plugin-danmuku.d.ts +++ b/docs/assets/ts/artplayer-plugin-danmuku.d.ts @@ -1,114 +1,96 @@ -export type Mode = 0 | 1 | 2 -export type Danmuku - = | Danmu[] - | string // URL - | (() => Promise) - | Promise - -export interface Slider { - min?: number - max?: number - steps?: { - name?: string - value?: number | string - show?: boolean - }[] -} - -export interface Danmu { - /** - * 弹幕文本 - */ - text: string - - /** - * 弹幕发送模式: 0: 滚动,1: 顶部,2: 底部 - */ - mode?: Mode - - /** - * 弹幕颜色 - */ - color?: string - - /** - * 弹幕出现的时间,单位为秒 - */ - time?: number - - /** - * 弹幕是否有描边, 默认为 false - */ - border?: boolean - - /** - * 弹幕自定义样式 - */ - style?: Partial -} - -export interface Option { - /** - * 弹幕数据: 函数,数组,Promise,URL - */ - danmuku: Danmuku - - /** - * 弹幕持续时间,范围在[1 ~ 10] - */ - speed?: number - - /** - * 弹幕上下边距,支持像素数字和百分比 - */ - margin?: [number | `${number}%`, number | `${number}%`] - - /** - * 弹幕透明度,范围在[0 ~ 1] - */ - opacity?: number - - /** - * 默认弹幕颜色,可以被单独弹幕项覆盖 - */ - color?: string - - /** - * 弹幕模式: 0: 滚动,1: 顶部,2: 底部 - */ - mode?: Mode - - /** - * 弹幕可见的模式 - */ - modes?: Mode[] - - /** - * 弹幕字体大小,支持像素数字和百分比 - */ - fontSize?: number | `${number}%` - - /** - * 弹幕是否防重叠 - */ - antiOverlap?: boolean - - /** - * 是否同步播放速度 - */ - synchronousPlayback?: boolean - - /** - * 弹幕发射器挂载点, 默认为播放器控制栏中部 - */ - mount?: HTMLDivElement | string - - /** - * 是否开启弹幕热度图 - */ - heatmap?: - | boolean - | { +// Generated from the package public declaration by yarn build:ts. Do not edit. +/* eslint-disable ts/no-redeclare -- Callable and public type namespace intentionally merge. */ +declare namespace artplayerPluginDanmukuDefinitions { + export type Mode = 0 | 1 | 2 + export type Danmuku = Danmu[] | string // URL + | (() => Promise) | Promise + export interface Slider { + min?: number + max?: number + steps?: { + name?: string + value?: number | string + show?: boolean + }[] + } + export interface Danmu { + /** + * 弹幕文本 + */ + text: string + /** + * 弹幕发送模式: 0: 滚动,1: 顶部,2: 底部 + */ + mode?: Mode + /** + * 弹幕颜色 + */ + color?: string + /** + * 弹幕出现的时间,单位为秒 + */ + time?: number + /** + * 弹幕是否有描边, 默认为 false + */ + border?: boolean + /** + * 弹幕自定义样式 + */ + style?: Partial + } + export interface Option { + /** + * 弹幕数据: 函数,数组,Promise,URL + */ + danmuku: Danmuku + /** + * 弹幕持续时间,范围在[1 ~ 10] + */ + speed?: number + /** + * 弹幕上下边距,支持像素数字和百分比 + */ + margin?: [ + number | `${number}%`, + number | `${number}%`, + ] + /** + * 弹幕透明度,范围在[0 ~ 1] + */ + opacity?: number + /** + * 默认弹幕颜色,可以被单独弹幕项覆盖 + */ + color?: string + /** + * 弹幕模式: 0: 滚动,1: 顶部,2: 底部 + */ + mode?: Mode + /** + * 弹幕可见的模式 + */ + modes?: Mode[] + /** + * 弹幕字体大小,支持像素数字和百分比 + */ + fontSize?: number | `${number}%` + /** + * 弹幕是否防重叠 + */ + antiOverlap?: boolean + /** + * 是否同步播放速度 + */ + synchronousPlayback?: boolean + /** + * 弹幕发射器挂载点, 默认为播放器控制栏中部 + */ + mount?: HTMLDivElement | string + /** + * 是否开启弹幕热度图 + */ + heatmap?: boolean | { xMin?: number xMax?: number yMin?: number @@ -120,140 +102,123 @@ export interface Option { smoothing?: number flattening?: number } - - /** - * 当播放器宽度小于此值时,弹幕发射器置于播放器底部 - */ - width?: number - - /** - * 热力图数据 - */ - points?: { time: number, value: number }[] - - /** - * 弹幕载入前的过滤器,只支持返回布尔值 - */ - filter?: (danmu: Danmu) => boolean - - /** - * 弹幕发送前的过滤器,支持返回 Promise - */ - beforeEmit?: (danmu: Danmu) => boolean | Promise - - /** - * 弹幕显示前的过滤器,支持返回 Promise - */ - beforeVisible?: (danmu: Danmu) => boolean | Promise - - /** - * 弹幕是否可见 - */ - visible?: boolean - - /** - * 是否开启弹幕发射器 - */ - emitter?: boolean - - /** - * 弹幕输入框最大长度, 范围在[1 ~ 1000] - */ - maxLength?: number - - /** - * 输入框锁定时间,范围在[1 ~ 60] - */ - lockTime?: number - - /** - * 弹幕主题,只在自定义挂载时生效 - */ - theme?: 'light' | 'dark' - - /** - * 不透明度配置项 - */ - OPACITY?: Slider - - /** - * 弹幕速度配置项 - */ - SPEED?: Slider - - /** - * 显示区域配置项 - */ - MARGIN?: Slider - - /** - * 弹幕字号配置项 - */ - FONT_SIZE?: Slider - - /** - * 颜色列表配置项 - */ - COLOR?: string[] + /** + * 当播放器宽度小于此值时,弹幕发射器置于播放器底部 + */ + width?: number + /** + * 热力图数据 + */ + points?: { + time: number + value: number + }[] + /** + * 弹幕载入前的过滤器,只支持返回布尔值 + */ + filter?: (danmu: Danmu) => boolean + /** + * 弹幕发送前的过滤器,支持返回 Promise + */ + beforeEmit?: (danmu: Danmu) => boolean | Promise + /** + * 弹幕显示前的过滤器,支持返回 Promise + */ + beforeVisible?: (danmu: Danmu) => boolean | Promise + /** + * 弹幕是否可见 + */ + visible?: boolean + /** + * 是否开启弹幕发射器 + */ + emitter?: boolean + /** + * 弹幕输入框最大长度, 范围在[1 ~ 1000] + */ + maxLength?: number + /** + * 输入框锁定时间,范围在[1 ~ 60] + */ + lockTime?: number + /** + * 弹幕主题,只在自定义挂载时生效 + */ + theme?: 'light' | 'dark' + /** + * 不透明度配置项 + */ + OPACITY?: Slider + /** + * 弹幕速度配置项 + */ + SPEED?: Slider + /** + * 显示区域配置项 + */ + MARGIN?: Slider + /** + * 弹幕字号配置项 + */ + FONT_SIZE?: Slider + /** + * 颜色列表配置项 + */ + COLOR?: string[] + } + export interface Result { + name: 'artplayerPluginDanmuku' + /** + * 发送一条实时弹幕 + */ + emit: (danmu: Danmu) => Result + /** + * 重载弹幕源,或者切换新弹幕 + */ + load: (danmuku?: Danmuku) => Promise + /** + * 实时改变弹幕配置 + */ + config: (option: Option) => Result + /** + * 隐藏弹幕层 + */ + hide: () => Result + /** + * 显示弹幕层 + */ + show: () => Result + /** + * 挂载弹幕输入框 + */ + mount: (el?: HTMLDivElement | string) => void + /** + * 重置弹幕 + */ + reset: () => Result + /** + * 弹幕配置 + */ + option: Option + /** + * 是否隐藏弹幕层 + */ + isHide: boolean + /** + * 是否弹幕层停止状态 + */ + isStop: boolean + } + export const artplayerPluginDanmuku: (option: Option) => (art: Artplayer) => Result } - -export interface Result { - name: 'artplayerPluginDanmuku' - - /** - * 发送一条实时弹幕 - */ - emit: (danmu: Danmu) => Result - - /** - * 重载弹幕源,或者切换新弹幕 - */ - load: (danmuku?: Danmuku) => Promise - - /** - * 实时改变弹幕配置 - */ - config: (option: Option) => Result - - /** - * 隐藏弹幕层 - */ - hide: () => Result - - /** - * 显示弹幕层 - */ - show: () => Result - - /** - * 挂载弹幕输入框 - */ - mount: (el?: HTMLDivElement | string) => void - - /** - * 重置弹幕 - */ - reset: () => Result - - /** - * 弹幕配置 - */ - option: Option - - /** - * 是否隐藏弹幕层 - */ - isHide: boolean - - /** - * 是否弹幕层停止状态 - */ - isStop: boolean +declare const artplayerPluginDanmuku: typeof artplayerPluginDanmukuDefinitions.artplayerPluginDanmuku +declare namespace artplayerPluginDanmuku { + export type Mode = artplayerPluginDanmukuDefinitions.Mode + export type Danmuku = artplayerPluginDanmukuDefinitions.Danmuku + export type Slider = artplayerPluginDanmukuDefinitions.Slider + export type Danmu = artplayerPluginDanmukuDefinitions.Danmu + export type Option = artplayerPluginDanmukuDefinitions.Option + export type Result = artplayerPluginDanmukuDefinitions.Result } - -declare const artplayerPluginDanmuku: (option: Option) => (art: Artplayer) => Result - -export default artplayerPluginDanmuku - export = artplayerPluginDanmuku export as namespace artplayerPluginDanmuku; diff --git a/docs/compiled/artplayer-plugin-danmuku.js b/docs/compiled/artplayer-plugin-danmuku.js index 57cc579f2..6ae4f81f1 100644 --- a/docs/compiled/artplayer-plugin-danmuku.js +++ b/docs/compiled/artplayer-plugin-danmuku.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDanmuku=e(),define(function(){return t.artplayerPluginDanmuku})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDanmuku=e()}(this,function(){"use strict";function t(t){return t instanceof Promise||"[object Promise]"===Object.prototype.toString.call(t)}function e(t){switch(t){case 1:case 2:case 3:default:return 0;case 4:return 2;case 5:return 1}}function i(t){if("string"!==typeof t)return[];const i=t.matchAll(/]*?p="(?

[^"]+)"[^>]*>(?.*?)<\/d>/gs);return Array.from(i).map(t=>{const i=t.groups.p.split(",");if(i.length>=8){return{text:t.groups.text.trim().replaceAll(""",'"').replaceAll("'","'").replaceAll("<","<").replaceAll(">",">").replaceAll("&","&"),time:Number(i[0]),mode:e(Number(i[1])),fontSize:Number(i[2]),color:`#${Number(i[3]).toString(16)}`,timestamp:Number(i[4]),pool:Number(i[5]),userID:i[6],rowID:Number(i[7])}}return null}).filter(Boolean)}function n({data:t}){const{xml:e,id:n}=t,a=i(e);globalThis.postMessage({danmus:a,id:n})}let a=0;function s(t,{signal:s,onCancel:r}={}){return new Promise((o,l)=>{let d,c,p,h=!1;const u=++a,m=()=>g(void 0,function(t){if(void 0!==t?.reason)return t.reason;const e=new Error("Bilibili Danmu loading cancelled");return e.name="AbortError",e}(s));function f(){let t,e=!1;try{if(d){const t=d;d=void 0,t.onmessage=null,t.onerror=null,t.onmessageerror=null,t.terminate()}}catch(i){t=i,e=!0}if(void 0!==c){const n=c;c=void 0;try{URL.revokeObjectURL(n)}catch(i){e||(t=i),e=!0}}if(e)throw t}function g(t,e){if(h)return;h=!0;let i=arguments.length>1;s?.removeEventListener("abort",m),p?.();try{f()}catch(n){i||(e=n),i=!0}i?l(e):o(t)}function y(t,e){if(!h){console.error("Error parsing Bilibili Danmu:",e);try{g(i(t))}catch(n){g(void 0,n)}}}s?.aborted?m():(s?.addEventListener("abort",m),p=r?.(m),h?p?.():async function(){const a=s?await fetch(t,{signal:s}):await fetch(t);if(h)return;const r=await a.text();if(!h)try{const t=`\n ${e.toString()}\n ${i.toString()}\n onmessage = ${n.toString()}\n `;if(c=URL.createObjectURL(new Blob([t],{type:"application/javascript"})),h)return void f();if(d=new Worker(c),h)return void f();d.onmessage=t=>{if(h||t.data?.id!==u)return;const{danmus:e}=t.data;Array.isArray(e)?g(e):g(void 0,new Error("Invalid Bilibili Danmu worker response"))},d.onerror=t=>{t.preventDefault?.(),y(r,t.error||t)},d.onmessageerror=t=>y(r,t.error||t),d.postMessage({xml:r,id:u})}catch(o){y(r,o)}}().catch(t=>g(void 0,t)))})}const r=new WeakMap,o=Symbol("cancelled danmuku input");function l(t){return r.has(t)||r.set(t,{closed:!1,tasks:new Set,emitting:[]}),r.get(t)}function d(t){const e=l(t),i=e.emitting[e.emitting.length-1];return!e.closed&&!t.art.isDestroy&&(!i||i.active())}function c(t){const e=l(t);e.closed=!0;for(const i of[...e.tasks])i.cancel()}function p(t,e,i){const n=t.states[e]||[];for(let a=0;a{const a=n.$ref.offsetTop,s=this.owner.getLeft(n.$ref)-i,r=n.$ref.clientHeight,o=n.$ref.clientWidth,l=s+o,d=e-l,c=l/n.$restTime,p={};p.top=a,p.left=s,p.height=r,p.width=o,p.right=d,p.speed=c,p.distance=l,p.time=n.$restTime,p.mode=n.mode,t.push(p)}),t}left(t){const e=t.getBoundingClientRect();return this.owner.isRotate?e.top:e.left}makeWait(t){this.owner.setState(t,"wait"),t.$ref&&(t.$ref.style.cssText=this.owner.constructor.cssText,t.$ref.style.visibility="hidden",t.$ref.style.marginLeft="0px",t.$ref.style.transform="translateX(0px)",t.$ref.style.transition="transform 0s linear 0s",this.owner.$refs.push(t.$ref),t.$ref=null)}resize(){const{clientWidth:t}=this.owner.$player;this.owner.filter("stop",e=>{if(0===e.mode)e.$ref.style.left=`${t}px`}),this.owner.filter("emit",e=>{switch(e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.left=`${t}px`,e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}})}continue(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("stop",e=>{switch(this.owner.setState(e,"emit"),e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}}),this}suspend(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("emit",e=>{switch(this.owner.setState(e,"stop"),e.mode){case 0:{const i=t-(this.owner.getLeft(e.$ref)-this.owner.getLeft(this.owner.$player));e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition="transform 0s linear 0s";break}}}),this}prepare(t,e){const i=this.owner,n=i.$ref;return e.danmu=t,e.ref=n,t.$ref=n,n.textContent=t.text,i.$danmuku.appendChild(n),n.style.opacity=i.option.opacity,n.style.fontSize=`${i.fontSize}px`,n.style.color=t.color,n.style.border=t.border?`1px solid ${t.color}`:null,n.style.backgroundColor=t.border?"rgb(0 0 0 / 50%)":null,i.utils.setStyles(n,t.style),n}place(t,e,i,n,a){switch(e.style.top=`${i}px`,e.style.visibility="visible",e.dataset.mode=t.mode,e.dataset.id=t.id||"",t.mode){case 0:e.style.left=`${a}px`,e.style.marginLeft="0px",e.style.transform=`translateX(${-n}px)`,e.style.transition=`transform ${t.$restTime}s linear 0s`;break;case 1:case 2:e.style.left="50%",e.style.marginLeft=`-${e.clientWidth/2}px`}}release(t){const{danmu:e,ref:i}=t;i&&e.$ref===i&&(i.style.cssText=this.owner.constructor.cssText,i.style.visibility="hidden",this.owner.$refs.push(i),e.$ref=null),t.ref=null}clear(){this.destroy(),this.owner.$refs=[],this.owner.$danmuku.textContent=""}destroy(){for(const t of this.nodes)t.parentElement&&t.parentElement.removeChild(t);for(const t of this.owner.queue)t.$ref=null;this.owner.$refs.length=0,this.nodes.clear()}}const u=Symbol("cancelled danmuku frame");class m{constructor(t){this.owner=t,this.generation=0,this.starts=0,this.frame=null,this.operation=null,this.closed=!1,this.running=!1,this.fault=!1,this.failedItems=new Set}active(t){const e=this.owner;return!this.closed&&!this.fault&&!e.art.isDestroy&&!e.isStop&&!e.isHide&&t.generation===this.generation}release(t){this.owner.renderer.release(t)}invalidate(){if(this.generation++,null!==this.frame&&window.cancelAnimationFrame(this.frame),this.frame=null,this.owner.timer=null,this.operation){const t=this.operation;this.operation=null,t.cancel(),this.release(t)}this.owner.workerClient?.cancel(),this.failedItems.clear()}report(t){try{this.owner.art.emit("artplayerPluginDanmuku:error",t)}catch(e){console.warn("Failed to report danmuku scheduling error:",e)}}fail(t){this.closed||this.fault||(this.fault=!0,this.invalidate(),this.report(t))}recover(){if(!this.closed&&!this.owner.art.isDestroy&&(this.failedItems.clear(),this.fault))try{this.owner.workerClient?.dispose(),this.owner.createWorker(),this.fault=!1}catch(t){this.report(t)}}schedule(){const t=this.owner;!this.running||this.closed||this.fault||t.art.isDestroy||t.isStop||null!==this.frame||this.operation||(this.frame=window.requestAnimationFrame(()=>{if(this.frame=null,t.timer=null,this.closed||this.fault||t.isStop||t.art.isDestroy)return;let e;const i=new Promise(t=>e=()=>t(u)),n={generation:this.generation,cancel:e,wait:t=>Promise.race([t,i]),ref:null};return this.operation=n,this.run(n).catch(t=>{this.active(n)&&this.fail(t)}).finally(()=>{this.release(n),this.operation===n&&(this.operation=null,this.schedule())})}),t.timer=this.frame)}async run(t){const e=this.owner;if(!e.art.playing||!this.active(t))return;e.filter("emit",t=>{const i=(Date.now()-t.$lastStartTime)/1e3;t.$restTime-=i,t.$lastStartTime=Date.now(),t.$restTime<=0&&e.makeWait(t)});const i=e.readys;for(const a of i){if(!this.active(t))return;if(this.failedItems.has(a))continue;let i;try{i=await t.wait(e.option.beforeVisible(a))}catch(n){if(!this.active(t))return;this.failedItems.add(a),this.report(n);continue}if(!this.active(t))return;if(!i)continue;const{clientWidth:s,clientHeight:r}=e.$player,o=e.renderer.prepare(a,t);if(!this.active(t))return;a.$lastStartTime=Date.now(),a.$restTime=e.speed;const l=s+o.clientWidth,{result:d}=await t.wait(e.postMessage({type:"getDanmuTop",target:{mode:a.mode,height:o.clientHeight,speed:l/a.$restTime},visibles:e.visibles,antiOverlap:e.option.antiOverlap,clientWidth:s,clientHeight:r,marginBottom:e.marginBottom,marginTop:e.marginTop}));if(!this.active(t)||a.$ref!==o)return;void 0!==d?(e.setState(a,"emit"),e.renderer.place(a,o,d,l,s),t.ref=null,e.art.emit("artplayerPluginDanmuku:visible",a)):(e.setState(a,"ready"),this.release(t))}}destroy(){this.closed=!0,this.invalidate()}}let f=0;class g{constructor(t,e){this.pending=new Map,this.closed=!1,this.failed=!1,this.onFailure=e,this.worker=t();try{this.worker.onmessage=t=>{const e=this.pending.get(t.data?.id);e&&(this.pending.delete(t.data.id),e.resolve(t.data))},this.worker.onerror=t=>{t.preventDefault?.(),this.fail(t.error||t)},this.worker.onmessageerror=t=>this.fail(t.error||t)}catch(i){try{this.dispose()}catch{}throw i}}request(t){t.id=++f;const{id:e}=t;return this.closed||this.failed?Promise.resolve({id:e,result:void 0}):new Promise((i,n)=>{this.pending.set(e,{resolve:i,reject:n});try{this.worker.postMessage(t)}catch(a){this.fail(a)}})}fail(t){if(!this.closed&&!this.failed){this.failed=!0;for(const e of this.pending.values())e.reject(t);this.pending.clear();try{this.dispose()}catch{}this.onFailure(t)}}cancel(){for(const[t,e]of this.pending)e.resolve({id:t,result:void 0});this.pending.clear()}dispose(){if(this.closed)return;this.closed=!0,this.cancel();let t,e=!1;const i=i=>{try{i()}catch(n){e||(t=n),e=!0}};for(const n of["onmessage","onerror","onmessageerror"])i(()=>this.worker[n]=null);if(i(()=>this.worker.terminate()),e)throw t}}const y='/*!\n * artplayer-plugin-danmuku.js v5.3.0\n * Github: https://github.com/zhw2590582/ArtPlayer\n * (c) 2017-2026 Harvey Zhao\n * Released under the MIT License.\n */\nfunction t({target:t,visibles:e,clientWidth:i,clientHeight:h,marginBottom:n,marginTop:o,antiOverlap:s}){const r=h-n,p=e.filter(e=>e.mode===t.mode&&e.top<=r).sort((t,e)=>t.top-e.top);if(0===p.length)return 2===t.mode?r-t.height:o;if(p.unshift({type:"top",top:0,left:0,right:0,height:o,width:i,speed:0,distance:i}),p.push({type:"bottom",top:r,left:0,right:0,height:n,width:i,speed:0,distance:i}),2===t.mode)for(let g=p.length-2;g>=0;g-=1){const e=p[g],i=p[g+1],h=e.top+e.height;if(i.top-h>=t.height)return i.top-t.height}else for(let g=1;g=t.height)return h}const a=[];for(let g=1;g{const i=Math.min(...e.map(t=>t.right)),h=Math.min(...t.map(t=>t.right));return i*e.length-h*t.length});break;case 1:case 2:a.sort((t,e)=>{const i=Math.max(...e.map(t=>t.width));return Math.max(...t.map(t=>t.width))*t.length-i*e.length})}return a[0][0].top}switch(t.mode){case 0:{const e=a.find(e=>e.every(e=>{if(ie.time}));return e&&e[0]?e[0].top:void 0}case 1:case 2:return}}onmessage=e=>{const{data:i}=e;if(!i.id||!i.type)return;const h=(0,{getDanmuTop:t}[i.type])(i);globalThis.postMessage({result:h,id:i.id})};\n',v="undefined"!==typeof self&&self.Blob&&new Blob(["URL.revokeObjectURL(import.meta.url);",y],{type:"text/javascript;charset=utf-8"});function w(t){let e;try{if(e=v&&(self.URL||self.webkitURL).createObjectURL(v),!e)throw"";const i=new Worker(e,{type:"module",name:t?.name});return i.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(e)}),i}catch(i){return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(y),{type:"module",name:t?.name})}}class k{constructor(t,e){const{constructor:i,template:n}=t;this.utils=i.utils,this.validator=i.validator,this.$danmuku=n.$danmuku,this.$player=n.$player,this.art=t,this.queue=[],this.$refs=[],this.isStop=!1,this.isHide=!1,this.timer=null,this.index=0,this.worker=null,this.workerClient=null,this.renderer=new h(this),this.scheduler=new m(this),this.option=k.option,this.states={wait:[],ready:[],emit:[],stop:[]},this.start=this.start.bind(this),this.stop=this.stop.bind(this),this.reset=this.reset.bind(this),this.resize=this.resize.bind(this),this.destroy=this.destroy.bind(this),this.seek=this.seek.bind(this),d(this),t.on("destroy",this.destroy);try{if(this.config(e,!0),!d(this))return;this.createWorker()}catch(a){throw t.off("destroy",this.destroy),c(this),this.scheduler.destroy(),a}t.on("video:play",this.start),t.on("video:playing",this.start),t.on("video:pause",this.stop),t.on("video:waiting",this.stop),t.on("resize",this.resize),t.on("video:seeking",this.seek),this.load().catch(t=>console.warn("Failed to load initial danmuku:",t))}static get option(){return{danmuku:[],speed:5,margin:[10,"25%"],opacity:1,color:"#FFFFFF",mode:0,modes:[0,1,2],fontSize:25,antiOverlap:!0,synchronousPlayback:!1,mount:void 0,heatmap:!1,width:512,points:[],filter:()=>!0,beforeEmit:()=>!0,beforeVisible:()=>!0,visible:!0,emitter:!0,maxLength:200,lockTime:5,theme:"dark",OPACITY:{},FONT_SIZE:{},MARGIN:{},SPEED:{},COLOR:[]}}static get scheme(){return{danmuku:"array|function|string|promise",speed:"number",margin:"array",opacity:"number",color:"string",mode:"number",modes:"array",fontSize:"number|string",antiOverlap:"boolean",synchronousPlayback:"boolean",mount:"?htmldivelement|string",heatmap:"object|boolean",width:"number",points:"array",filter:"function",beforeEmit:"function",beforeVisible:"function",visible:"boolean",emitter:"boolean",maxLength:"number",lockTime:"number",theme:"string",OPACITY:"object",FONT_SIZE:"object",MARGIN:"object",SPEED:"object",COLOR:"array"}}static get cssText(){return'\n user-select: none;\n position: absolute;\n white-space: pre;\n pointer-events: none;\n perspective: 500px;\n display: inline-block;\n will-change: transform;\n font-weight: normal;\n line-height: 1.125;\n visibility: hidden;\n font-family: SimHei, "Microsoft JhengHei", Arial, Helvetica, sans-serif;\n text-shadow: rgb(0, 0, 0) 1px 0px 1px, rgb(0, 0, 0) 0px 1px 1px, rgb(0, 0, 0) 0px -1px 1px, rgb(0, 0, 0) -1px 0px 1px;\n '}get isRotate(){return this.art.plugins?.autoOrientation?.state}get marginTop(){const{clamp:t}=this.utils,e=this.option.margin[0],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return k.option.margin[0]}get marginBottom(){const{clamp:t}=this.utils,e=this.option.margin[1],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return k.option.margin[1]}get fontSize(){const{clamp:t}=this.utils,{clientHeight:e}=this.$player,i=this.option.fontSize;if("number"===typeof i)return Math.round(t(i,12,e));if("string"===typeof i&&i.endsWith("%")){const n=Number.parseFloat(i)/100;return Math.round(t(e*n,12,e))}return k.option.fontSize}get $ref(){return this.renderer.acquire()}get readys(){return function(t){const{currentTime:e}=t.art,i=[];return p(t,"ready",t=>i.push(t)),p(t,"wait",t=>{e+.1>=t.time&&t.time>=e-.1&&i.push(t)}),i}(this)}get visibles(){return this.renderer.visibles}get speed(){return this.option.synchronousPlayback&&this.art.playbackRate?this.option.speed/Number(this.art.playbackRate):this.option.speed}async load(e){const{errorHandle:i}=this.utils,n=function(t,e){const i=l(t);if(e)for(const o of[...i.tasks])o.replace&&o.cancel();let n,a=i.closed||t.art.isDestroy;const s=new Promise(t=>n=t),r=new Set,d="function"===typeof AbortController?new AbortController:void 0,c={replace:e,signal:d?.signal,active:()=>!a&&!i.closed&&!t.art.isDestroy,onCancel:t=>c.active()?(r.add(t),()=>r.delete(t)):(t(),()=>{}),cancel(){if(!a){a=!0,n(o),d?.abort();for(const t of[...r])t();r.clear()}},wait:t=>Promise.race([t,s]),emit(e){i.emitting.push(c);try{return t.emit(e)}finally{i.emitting.pop()}},finish(){i.tasks.delete(c),r.clear()}};return a?n(o):i.tasks.add(c),c}(this,void 0===e);try{if(!n.active())return this;const a=function(e,i){return"function"===typeof e?{asynchronous:!0,value:e()}:t(e)?{asynchronous:!0,value:e}:"string"===typeof e?{asynchronous:!0,value:s(e,i)}:{asynchronous:!1,value:Array.isArray(e)?e:[]}}(e||this.option.danmuku,n),r=a.asynchronous?await n.wait(a.value):a.value;if(!n.active())return this;if(i(Array.isArray(r),"Danmuku need return an array as result"),void 0===e){if(this.reset(),!n.active())return this;this.queue=[],this.states={wait:[],ready:[],emit:[],stop:[]},this.renderer.clear()}for(let t=0;t{const i=s[e],n=r[e];return"function"===typeof i||"function"===typeof n||t(i)||t(n)?i!==n:JSON.stringify(i)!==JSON.stringify(n)}))&&!i)return this;const o=function(t,e,{defaults:i,validate:n,clamp:a,mount:s}){const r=Object.assign({},i,t,e);return n(r),r.mode=a(r.mode,0,2),r.speed=a(r.speed,1,10),r.opacity=a(r.opacity,0,1),r.lockTime=a(r.lockTime,1,60),r.maxLength=a(r.maxLength,1,1e3),r.mount=r.mount||s,r}(this.option,e,{defaults:k.option,validate:t=>this.validator(t,k.scheme),clamp:n,mount:a});if(!d(this))return this;const l=this.option.beforeVisible!==o.beforeVisible;return this.option=o,l&&(this.scheduler.invalidate(),this.scheduler.schedule()),e.fontSize&&(this.reset(),!d(this))||(this.option.visible?this.show():this.hide(),d(this)&&this.art.emit("artplayerPluginDanmuku:config",this.option)),this}getLeft(t){return this.renderer.left(t)}postMessage(t={}){return this.workerClient.request(t)}createWorker(){this.workerClient=new g(()=>new w,t=>this.scheduler.fail(t)),this.worker=this.workerClient.worker}filter(t,e){return p(this,t,e)}setState(t,e){!function(t,e,i){t.states[e.$state]=t.states[e.$state].filter(t=>t!==e),e.$state=i,e.$ref&&(e.$ref.dataset.state=i),t.states[i].push(e)}(this,t,e)}makeWait(t){this.renderer.makeWait(t)}update(){return this.scheduler.running=!0,this.scheduler.schedule(),this}resize(){this.renderer.resize()}continue(){return this.renderer.continue(),this}suspend(){return this.renderer.suspend(),this}stop(){return this.isStop=!0,this.scheduler.running=!1,this.scheduler.invalidate(),this.suspend(),this.art.emit("artplayerPluginDanmuku:stop"),this}start(){if(this.scheduler.closed||this.art.isDestroy)return this;const t=++this.scheduler.starts,e=this.scheduler.generation;this.isStop=!1,this.scheduler.recover();return this.scheduler.closed||this.art.isDestroy||this.isStop||this.scheduler.fault||e!==this.scheduler.generation||t!==this.scheduler.starts||(this.continue(),this.update(),this.art.emit("artplayerPluginDanmuku:start")),this}reset(){return this.scheduler.invalidate(),this.queue.forEach(t=>this.makeWait(t)),this.art.emit("artplayerPluginDanmuku:reset"),this.scheduler.recover(),this.scheduler.schedule(),this}seek(){this.scheduler.invalidate(),this.scheduler.schedule()}show(){return this.isHide=!1,this.$danmuku.style.opacity=1,this.option.visible=!0,this.art.emit("artplayerPluginDanmuku:show"),this}hide(){return this.isHide=!0,this.scheduler.invalidate(),this.$danmuku.style.opacity=0,this.option.visible=!1,this.art.emit("artplayerPluginDanmuku:hide"),this.scheduler.schedule(),this}destroy(){c(this),this.scheduler.destroy();try{try{this.stop()}finally{this.workerClient?.dispose()}}finally{this.renderer.destroy(),this.art.off("video:play",this.start),this.art.off("video:playing",this.start),this.art.off("video:pause",this.stop),this.art.off("video:waiting",this.stop),this.art.off("resize",this.resize),this.art.off("video:seeking",this.seek),this.art.off("destroy",this.destroy),this.art.emit("artplayerPluginDanmuku:destroy")}}}const x=4294967295;function $(t,e){let i=0,n=t.length;for(;iNumber.isFinite(t)&&t>0))return null;const r={xMin:0,xMax:t,yMin:0,yMax:128,scale:.25,opacity:.2,minHeight:Math.floor(.05*e),sampling:Math.max(1,Math.floor(t/100)),smoothing:.2,flattening:.2},o=Object.assign({},"object"===typeof a?a:{}),l=Object.assign({},r,o);for(const x of Object.keys(r))Number.isFinite(l[x])||(l[x]=r[x]);if(l.sampling<=0&&(l.sampling=r.sampling),l.xMin===l.xMax||l.yMin===l.yMax)return null;const d=Array.isArray(s)&&s.length>0,c=d?[...s]:function(t,e,i,n){if(!Number.isFinite(e)||e<=0||!Number.isFinite(i)||i<=0||!Number.isFinite(n)||n<=0||Math.floor(e/n)+1>x)return[];const a=t.map(t=>t.time).filter(t=>"number"===typeof t&&!Number.isNaN(t)).sort((t,e)=>t-e),s=[],r=i/e;if(!Number.isFinite(r))return[];for(let o=0;o<=e;o+=n){if(s.length>=x||o+n===o)return[];const t=o*r,e=(o+n)*r;s.push([o,$(a,e)-$(a,t)])}return s}(n,t,i,l.sampling);if(!c.length||!c.every(t=>Array.isArray(t)&&Number.isFinite(t[0])&&Number.isFinite(t[1])))return null;const p=c[c.length-1];if(p[0]!==t){if(c.length===x)return null;c.push([t,p[1]])}let h=1/0,u=-1/0;for(const x of c)h=Math.min(h,x[1]),u=Math.max(u,x[1]);const m=(h+u)/2;for(const x of c){const t=x[1];x[1]=t*(t>m?1+l.scale:1-l.scale)+l.minHeight}const f=!d&&!Number.isFinite(o.yMin)&&!Number.isFinite(o.yMax);let g=0;for(const x of c)g=Math.max(g,x[1]);const y=f&&g>r.yMax/4;y&&(l.yMax=4*g);const v=t=>y?Math.min(e,Math.max(.75*e,t)):t,w=(t,e,i,n)=>{const a=function(t,e){const i=e[0]-t[0],n=e[1]-t[1];return{length:Math.sqrt(i**2+n**2),angle:Math.atan2(n,i)}}(e||t,i||t),s=b(Math.cos(a.angle)*l.flattening,0,1,1,0),r=a.angle*s+(n?Math.PI:0),o=a.length*l.smoothing;return[t[0]+Math.cos(r)*o,v(t[1]+Math.sin(r)*o)]},k=c.map(i=>[b(i[0],l.xMin,l.xMax,0,t),v(b(i[1],l.yMin,l.yMax,e,0))]);if(!k.every(t=>t.every(Number.isFinite)))return null;const M=k.map((t,i,n)=>{if(0===i)return`M ${n[n.length-1][0]},${e} L ${t[0]},${e} L ${t[0]},${t[1]}`;const a=w(n[i-1],n[i-2],t),s=w(t,n[i-1],n[i+1],!0);return`C ${a[0]},${a[1]} ${s[0]},${s[1]} ${t[0]},${t[1]}${i===n.length-1?" z":""}`}).join(" ");return/NaN|Infinity/u.test(M)?null:{path:M,opacity:l.opacity,width:t,height:e}}let S=0;function z(t,e,i){if(t.isDestroy)return;const{query:n}=t.constructor.utils;let a;const s=[];let r,o,l,d=!1;const c=()=>!d&&!t.isDestroy;function p(e,i){s.push([e,i]),t.on(e,i)}function h(e){if(d)return;let i;d=!0,o=null,l=null;let n=!1;const a=t=>{try{t()}catch(e){n||(i=e),n=!0}};for(const[r,o]of s.splice(0))a(()=>t.off(r,o));if(e&&r&&t.controls.heatmap===r&&a(()=>t.controls.remove("heatmap")),r=void 0,n)throw i}function u(t){c()&&o&&l&&(o.setAttribute("offset",100*t+"%"),l.setAttribute("offset",100*t+"%"))}function m(s){try{!function(s=[]){if(!c())return;if(o=null,l=null,r.innerHTML="",t.option.isLive)return;const d=M({width:r.offsetWidth,height:r.offsetHeight,duration:t.duration,queue:e.queue,option:i,points:s});if(!d||!c())return;const p=r.ownerDocument;for(a||(a=p?"heatmap-solids":"heatmap-solids-"+ ++S);p?.getElementById(a);)a="heatmap-solids-"+ ++S;r.innerHTML=`\n \n \n \n \n \n \n \n \n \n \n \n `,o=n("#heatmap-start",r),l=n("#heatmap-stop",r),u(t.played)}(s)}catch(d){try{h(!0)}catch{}throw d}}p("destroy",()=>h(!0));try{t.controls.add({name:"heatmap",position:"top",html:"",style:{position:"absolute",top:"-100px",left:"0px",right:"0px",height:"100px",width:"100%",pointerEvents:"none"},beforeUnmount(){h(!1)},mounted(e){if(r=e,d)return t.controls.heatmap===e&&t.controls.remove("heatmap"),void(r=void 0);c()?(p("video:timeupdate",()=>u(t.played)),p("setBar",(t,e)=>{"played"===t&&u(e)}),p("ready",()=>m()),p("resize",()=>m()),p("artplayerPluginDanmuku:loaded",()=>m()),p("artplayerPluginDanmuku:points",t=>m(t))):h(!0)}})}catch(f){try{h(!0)}catch{}throw f}return()=>h(!0)}const E=new WeakMap;class F{constructor(t){this.art=t,this.closed=!1,this.disposers=new Set,this.cancelled=new Promise(t=>this.cancel=t)}get active(){return!this.closed&&!this.art.isDestroy}own(t){this.closed?t():this.disposers.add(t)}write(t,e,i,n=i=>t[e]=i){if(!this.active)return;const a=()=>"function"===typeof t.getPropertyPriority?t.getPropertyPriority(e):void 0;let s=E.get(t);s||(s=new Map,E.set(t,s));let r=s.get(e);r||(r={original:t[e],present:e in t||void 0!==t[e],priority:a(),owners:new Map,apply:n},s.set(e,r)),r.owners.has(this)||this.own(()=>{const i=[...r.owners.keys()],n=i[i.length-1]===this;if(r.owners.delete(this),n&&t[e]===r.last&&a()===r.lastPriority){const i=[...r.owners.values()];i.length?(r.apply(i[i.length-1]),r.last=t[e],r.lastPriority=a()):void 0!==r.priority?t.setProperty(e,r.original,r.priority):r.present?r.apply(r.original):delete t[e]}r.owners.size||(s.delete(e),s.size||E.delete(t))}),r.owners.delete(this),r.owners.set(this,i),r.apply(i),r.last=t[e],r.lastPriority=a()}on(t,e){if(!this.active)return;const i=this;function n(...t){if(i.active)return e.apply(this,t)}const a=()=>this.art.off(t,n);this.own(a);try{this.art.on(t,n),this.active||a()}catch(s){throw this.release(a),s}}proxy(t,e,i){if(!this.active)return;const n=this;function a(...t){if(n.active)return i.apply(this,t)}let s;const r=()=>{if("function"===typeof s){const t=this.art.events;"function"===typeof t?.remove&&t.destroyEvents?.has(s)?t.remove(s):s()}else t.removeEventListener?.(e,a)};this.own(r);try{s=this.art.proxy(t,e,a),this.active||r()}catch(o){throw this.release(r),o}}wait(t){return Promise.race([t,this.cancelled])}release(t){try{t()}catch(e){console.warn("Failed to dispose danmuku setting resource:",e)}}close(){if(!this.closed){this.closed=!0,this.cancel();for(const t of this.disposers)this.release(t);this.disposers.clear()}}}function C({min:t,max:e,container:i,findIndex:n,onChange:a,steps:s=[]}){const{lifecycle:r}=this;if(!r.active)return{reset(){}};const{query:o,clamp:l,setStyle:d}=this.utils;d(i,"touch-action","none"),i.innerHTML=`\n

\n
\n ${s.map(()=>'
').join("")}\n
\n
\n
\n
\n
\n ${s.map(t=>t.hide?"":`
${t.name}
`).join("")}\n
\n `;const c=o(".apd-slider-dot",i),p=o(".apd-slider-progress",i);let h=!1;function u(i){if(!r.active)return;if(void 0===i&&(i=n()),ie)return;const o=(i-t)/(e-t);c.style.left=100*o+"%",0===s.length&&(p.style.width=c.style.left),a(i)}function m(n){const{top:a,height:s,left:r,width:o}=i.getBoundingClientRect();if(this.art.isRotate){const i=l(n.clientY-a,0,s);u(Math.round(i/s*(e-t)+t))}else{const i=l(n.clientX-r,0,o);u(Math.round(i/o*(e-t)+t))}}return r.own(()=>h=!1),r.proxy(i,"click",t=>{m.call(this,t)}),r.proxy(i,"pointerdown",t=>{h=0===t.button}),r.on("document:pointermove",t=>{h&&m.call(this,t)}),r.on("document:pointerup",t=>{h&&(h=!1,m.call(this,t))}),{reset:u}}const D='',T='',P='',A='',L='',H='',O='',N='',V='',q='',I='',j='';function B(){const{option:t}=this;return`\n
\n ${I}${q}\n
\n
\n ${P}\n
\n
\n
\n \u6309\u7c7b\u578b\u5c4f\u853d\n
\n
\n ${A}${L}\n
\u6eda\u52a8
\n
\n
\n ${H}${O}\n
\u9876\u90e8
\n
\n
\n ${N}${V}\n
\u5e95\u90e8
\n
\n
\n
\n
\n
\n ${T}${D}\n \u9632\u6b62\u5f39\u5e55\u91cd\u53e0\n
\n
\n ${T}${D}\n \u540c\u6b65\u89c6\u9891\u901f\u5ea6\n
\n
\n
\n \u4e0d\u900f\u660e\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n \u663e\u793a\u533a\u57df\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u5b57\u53f7\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u901f\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n
\n
\n
\n
\n ${j}\n
\n
\n
\n \u6a21\u5f0f\n
\n
\n ${L}\n
\u6eda\u52a8
\n
\n
\n ${O}\n
\u9876\u90e8
\n
\n
\n ${V}\n
\u5e95\u90e8
\n
\n
\n
\n
\n \u989c\u8272\n
\n ${this.COLOR.map(t=>`
`).join("")}\n
\n
\n
\n
\n
\n \n
\u53d1\u9001
\n
\n `}if("undefined"!==typeof document){const t="artplayer-plugin-danmuku",e=Symbol.for("artplayer-plugin-danmuku.pending-style");let i=document.getElementById(t)||document[e];if(!i)if(i=document.createElement("style"),i.id=t,"loading"===document.readyState){document[e]=i;const n=()=>{document.removeEventListener("DOMContentLoaded",n),document[e]===i&&delete document[e];const a=document.getElementById(t);a?a.textContent=i.textContent:(document.head||document.documentElement).appendChild(i)};document.addEventListener("DOMContentLoaded",n,{once:!0})}else(document.head||document.documentElement).appendChild(i);i.textContent='.artplayer-plugin-danmuku{display:flex;position:relative;z-index:99;align-items:center;justify-content:center;font-size:12px;height:32px;width:100%;color:#fff;font-weight:300;flex-shrink:0;gap:10px}.artplayer-plugin-danmuku .apd-icon{cursor:pointer;opacity:.75;transition:all .2s ease;fill:#fff}.artplayer-plugin-danmuku .apd-icon:hover{opacity:1}.artplayer-plugin-danmuku .apd-config{display:flex;position:relative}.artplayer-plugin-danmuku .apd-config .apd-config-panel{position:absolute;bottom:24px;left:0;width:320px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-config .apd-config-panel .apd-config-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-config:hover .apd-config-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-config-mode,.artplayer-plugin-danmuku .apd-config-slider,.artplayer-plugin-danmuku .apd-config-other,.artplayer-plugin-danmuku .apd-style-mode{margin-bottom:15px}.artplayer-plugin-danmuku .apd-modes{display:flex;align-items:center;margin-top:5px;gap:20px}.artplayer-plugin-danmuku .apd-modes .apd-mode{cursor:pointer;text-align:center}.artplayer-plugin-danmuku .apd-modes .apd-mode:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-config-slider{display:flex;align-items:center;gap:12px}.artplayer-plugin-danmuku .apd-config-slider .apd-value{width:32px;text-align:right}.artplayer-plugin-danmuku .apd-slider{position:relative;flex:1;display:flex;height:20px;align-items:center;justify-content:center;cursor:pointer}.artplayer-plugin-danmuku .apd-slider .apd-slider-line{position:relative;height:2px;width:100%;overflow:hidden;border-radius:3px;background-color:#ffffff40}.artplayer-plugin-danmuku .apd-slider .apd-slider-points{position:absolute;inset:0;display:flex;align-items:center;justify-content:space-between}.artplayer-plugin-danmuku .apd-slider .apd-slider-points .apd-slider-point{width:2px;height:2px;border-radius:50%;background-color:#ffffff80}.artplayer-plugin-danmuku .apd-slider .apd-slider-progress{width:0%;height:100%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-dot{position:absolute;transform:translate(-6px);left:0%;width:12px;height:12px;border-radius:50%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps{display:flex;align-items:center;justify-content:space-between;position:absolute;bottom:-12px;width:calc(100% + 32px);color:#777}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps .apd-slider-step{flex-shrink:0;width:36px;text-align:center;scale:.95}.artplayer-plugin-danmuku .apd-config-other{display:flex;align-items:center;gap:20px}.artplayer-plugin-danmuku .apd-config-other .apd-check-off,.artplayer-plugin-danmuku .apd-config-other .apd-check-on{width:16px;height:16px}.artplayer-plugin-danmuku .apd-config-other .apd-other{display:flex;align-items:center;cursor:pointer;gap:2px}.artplayer-plugin-danmuku .apd-config-other .apd-other:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-emitter{display:flex;flex:1;align-items:center;height:100%;background-color:#ffffff40;border-radius:5px}.artplayer-plugin-danmuku .apd-style{position:relative;display:flex;align-items:center;justify-content:center}.artplayer-plugin-danmuku .apd-style .apd-style-panel{position:absolute;bottom:24px;left:0;width:200px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-style .apd-style-panel .apd-style-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-style:hover .apd-style-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-colors{display:flex;flex-wrap:wrap;margin-top:5px;gap:8px}.artplayer-plugin-danmuku .apd-colors .apd-color{width:16px;height:16px;border-radius:2px;cursor:pointer}.artplayer-plugin-danmuku .apd-colors .apd-color.apd-active{border:1px solid black;box-shadow:0 0 0 1px #fff}.artplayer-plugin-danmuku .apd-input{outline:none;height:100%;flex:1;min-width:0;width:auto;border:none;line-height:1;color:#fff;background-color:transparent}.artplayer-plugin-danmuku .apd-input::placeholder{color:#ffffff80}.artplayer-plugin-danmuku .apd-send{display:flex;align-items:center;justify-content:center;height:100%;width:60px;flex-shrink:0;cursor:pointer;text-shadow:none;border-top-right-radius:5px;border-bottom-right-radius:5px;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-send.apd-lock{cursor:not-allowed;color:#666;background-color:#e7e7e7}.art-controls-center .apd-emitter{width:260px;flex:none}.art-fullscreen .artplayer-plugin-danmuku,.art-fullscreen-web .artplayer-plugin-danmuku{height:38px;gap:16px}.art-fullscreen .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen-web .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-on,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-on{width:28px;height:28px}.art-fullscreen .artplayer-plugin-danmuku .apd-emitter,.art-fullscreen-web .artplayer-plugin-danmuku .apd-emitter{width:400px;flex:none}.art-video-player>.artplayer-plugin-danmuku{position:absolute;left:0;right:0;bottom:-40px;padding:0 10px}.art-video-player:has(>.artplayer-plugin-danmuku){margin-bottom:40px}[data-danmuku-emitter=false] .apd-emitter{display:none!important}[data-danmuku-emitter=false] .art-controls-center .artplayer-plugin-danmuku{justify-content:flex-end;gap:18px}[data-danmuku-emitter=false].art-fullscreen .art-controls-center .artplayer-plugin-danmuku,[data-danmuku-emitter=false].art-fullscreen-web .art-controls-center .artplayer-plugin-danmuku{gap:24px}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-icon{fill:#333}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-emitter{background-color:#f1f2f3}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input{color:#000}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input::placeholder{color:#0000004d}[data-danmuku-visible=false] .apd-toggle-off{display:block}[data-danmuku-visible=false] .apd-toggle-on,[data-danmuku-visible=true] .apd-toggle-off{display:none}[data-danmuku-visible=true] .apd-toggle-on{display:block}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-on{display:none}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-off,[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-on{display:block}[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-off,[data-danmuku-sync-video=false] .apd-sync-video .apd-check-on{display:none}[data-danmuku-sync-video=false] .apd-sync-video .apd-check-off,[data-danmuku-sync-video=true] .apd-sync-video .apd-check-on{display:block}[data-danmuku-sync-video=true] .apd-sync-video .apd-check-off{display:none}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-off{display:block}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-on{display:none}[data-danmuku-mode0=false] .art-danmuku [data-mode="0"]{opacity:0!important}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-off{display:none}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-on{display:block}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"]{color:#00a1d6}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"] path{fill:#00a1d6}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-off{display:block}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-on{display:none}[data-danmuku-mode1=false] .art-danmuku [data-mode="1"]{opacity:0!important}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-off{display:none}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-on{display:block}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"]{color:#00a1d6}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"] path{fill:#00a1d6}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-off{display:block}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-on{display:none}[data-danmuku-mode2=false] .art-danmuku [data-mode="2"]{opacity:0!important}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-off{display:none}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-on{display:block}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"]{color:#00a1d6}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"] path{fill:#00a1d6}'}class R{constructor(t,e){this.art=t,this.danmuku=e,this.utils=t.constructor.utils;const{setStyle:i}=this.utils,{$controlsCenter:n}=t.template;this.template={$controlsCenter:n,$mount:n,$danmuku:null,$toggle:null,$config:null,$configPanel:null,$configModes:null,$style:null,$stylePanel:null,$styleModes:null,$colors:null,$opacitySlider:null,$opacityValue:null,$marginSlider:null,$marginValue:null,$fontSizeSlider:null,$fontSizeValue:null,$speedSlider:null,$speedValue:null,$input:null,$send:null},this.slider={opacity:null,margin:null,fontSize:null,speed:null},this.emitting=!1,this.isLock=!1,this.timer=null,this.lifecycle=new F(t),this.destroy=this.destroy.bind(this),this.lifecycle.own(()=>t.off("destroy",this.destroy));try{if(t.on("destroy",this.destroy),!this.lifecycle.active)return;if(this.lifecycle.write(n.style,"display","flex",t=>i(n,"display",t)),this.createTemplate(),!this.lifecycle.active)return;if(this.createSliders(),!this.lifecycle.active)return;if(this.createEvents(),this.mount(this.option.mount),!this.lifecycle.active)return;this.lifecycle.on("resize",()=>this.resize()),this.lifecycle.on("fullscreen",t=>this.onFullscreen(t)),this.lifecycle.on("fullscreenWeb",t=>this.onFullscreen(t)),this.lifecycle.proxy(this.template.$config,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$config,$panel:this.template.$configPanel})}),this.lifecycle.proxy(this.template.$style,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$style,$panel:this.template.$stylePanel})})}catch(a){try{this.destroy()}catch(s){console.warn("Failed to roll back danmuku setting:",s)}throw a}}static get icons(){return{$on:I,$off:q,$config:P,$style:j,$mode_0_off:A,$mode_0_on:L,$mode_1_off:H,$mode_1_on:O,$mode_2_off:N,$mode_2_on:V,$check_on:T,$check_off:D}}get option(){return this.danmuku.option}get outside(){return this.template.$mount!==this.template.$controlsCenter}get TEMPLATE(){return B.call(this)}get OPACITY(){return{min:0,max:100,steps:[],...this.option.OPACITY}}get FONT_SIZE(){return{min:12,max:120,steps:[],...this.option.FONT_SIZE}}get MARGIN(){return{min:0,max:3,steps:[{name:"1/4",value:[10,"75%"]},{name:"\u534a\u5c4f",value:[10,"50%"]},{name:"3/4",value:[10,"25%"]},{name:"\u6ee1\u5c4f",value:[10,10]}],...this.option.MARGIN}}get SPEED(){return{min:0,max:4,steps:[{name:"\u6781\u6162",value:10},{name:"\u8f83\u6162",value:7.5,hide:!0},{name:"\u9002\u4e2d",value:5},{name:"\u8f83\u5feb",value:2.5,hide:!0},{name:"\u6781\u5feb",value:1}],...this.option.SPEED}}get COLOR(){return this.option.COLOR.length?this.option.COLOR:["#FE0302","#FF7204","#FFAA02","#FFD302","#FFFF00","#A0EE00","#00CD00","#019899","#4266BE","#89D5FF","#CC0273","#222222","#9B9B9B","#FFFFFF"]}query(t){const{query:e}=this.utils,{$danmuku:i}=this.template;return e(t,i)}append(t,e){if(!this.lifecycle.active)return;const{append:i}=this.utils;[...t.children].includes(e)||i(t,e)}setData(t,e){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,{$mount:n}=this.template;this.lifecycle.write(i.dataset,t,e),this.outside&&this.lifecycle.write(n.dataset,t,e)}createTemplate(){const{createElement:t,tooltip:e}=this.utils,i=t("div");this.template.$danmuku=i,i.className="artplayer-plugin-danmuku",i.innerHTML=this.TEMPLATE,this.template.$toggle=this.query(".apd-toggle"),this.template.$config=this.query(".apd-config"),this.template.$configPanel=this.query(".apd-config-panel"),this.template.$configModes=this.query(".apd-config-mode .apd-modes"),this.template.$style=this.query(".apd-style"),this.template.$stylePanel=this.query(".apd-style-panel"),this.template.$styleModes=this.query(".apd-style-mode .apd-modes"),this.template.$colors=this.query(".apd-colors"),this.template.$antiOverlap=this.query(".apd-anti-overlap"),this.template.$syncVideo=this.query(".apd-sync-video"),this.template.$opacitySlider=this.query(".apd-config-opacity .apd-slider"),this.template.$opacityValue=this.query(".apd-config-opacity .apd-value"),this.template.$marginSlider=this.query(".apd-config-margin .apd-slider"),this.template.$marginValue=this.query(".apd-config-margin .apd-value"),this.template.$fontSizeSlider=this.query(".apd-config-fontSize .apd-slider"),this.template.$fontSizeValue=this.query(".apd-config-fontSize .apd-value"),this.template.$speedSlider=this.query(".apd-config-speed .apd-slider"),this.template.$speedValue=this.query(".apd-config-speed .apd-value"),this.template.$input=this.query(".apd-input"),this.template.$send=this.query(".apd-send");const{$toggle:n}=this.template;this.lifecycle.on("artplayerPluginDanmuku:show",()=>{e(n,"\u5173\u95ed\u5f39\u5e55")}),this.lifecycle.on("artplayerPluginDanmuku:hide",()=>{e(n,"\u6253\u5f00\u5f39\u5e55")})}createEvents(){const{$toggle:t,$configModes:e,$styleModes:i,$colors:n,$antiOverlap:a,$syncVideo:s,$send:r,$input:o}=this.template;this.lifecycle.proxy(t,"click",()=>{this.danmuku.config({visible:!this.option.visible}),this.reset()}),this.lifecycle.proxy(e,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.option.modes.includes(i)?this.danmuku.config({modes:this.option.modes.filter(t=>t!==i)}):this.danmuku.config({modes:[...this.option.modes,i]}),this.reset()}),this.lifecycle.proxy(a,"click",()=>{this.danmuku.config({antiOverlap:!this.option.antiOverlap}),this.reset()}),this.lifecycle.proxy(s,"click",()=>{this.danmuku.config({synchronousPlayback:!this.option.synchronousPlayback}),this.reset()}),this.lifecycle.proxy(i,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.danmuku.config({mode:i}),this.reset()}),this.lifecycle.proxy(n,"click",t=>{const e=t.target.closest(".apd-color");e&&(this.danmuku.config({color:e.dataset.color}),this.reset())}),this.lifecycle.proxy(r,"click",()=>this.emit()),this.lifecycle.proxy(o,"keypress",t=>{"Enter"===t.key&&(t.preventDefault(),this.emit())})}createSliders(){this.slider.opacity=this.createSlider({...this.OPACITY,container:this.template.$opacitySlider,findIndex:()=>Math.round(100*this.option.opacity),onChange:t=>{const{$opacityValue:e}=this.template;e.textContent=`${t}%`,this.danmuku.config({opacity:t/100})}}),this.slider.margin=this.createSlider({...this.MARGIN,container:this.template.$marginSlider,findIndex:()=>this.MARGIN.steps.findIndex(t=>t.value[0]===this.option.margin[0]&&t.value[1]===this.option.margin[1]),onChange:t=>{const e=this.MARGIN.steps[t];if(!e)return;const{$marginValue:i}=this.template;i.textContent=e.name,this.danmuku.config({margin:e.value})}}),this.slider.fontSize=this.createSlider({...this.FONT_SIZE,container:this.template.$fontSizeSlider,findIndex:()=>this.danmuku.fontSize,onChange:t=>{const{$fontSizeValue:e}=this.template;e.textContent=`${t}px`,t!==this.danmuku.fontSize&&this.danmuku.config({fontSize:t})}}),this.slider.speed=this.createSlider({...this.SPEED,container:this.template.$speedSlider,findIndex:()=>this.SPEED.steps.findIndex(t=>t.value===this.option.speed),onChange:t=>{const e=this.SPEED.steps[t];if(!e)return;const{$speedValue:i}=this.template;i.textContent=e.name,this.danmuku.config({speed:e.value})}})}createSlider(t){return C.call(this,t)}onFullscreen(t){if(!this.lifecycle.active)return;const{$danmuku:e,$controlsCenter:i,$mount:n}=this.template;this.outside?t?this.append(i,e):this.append(n,e):this.append(i,e)}onMouseEnter({$control:t,$panel:e}){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,n=t.getBoundingClientRect(),a=e.getBoundingClientRect(),s=i.getBoundingClientRect(),r=a.width/2-n.width/2,o=s.left-(n.left-r),l=n.right+r-s.right;e.style.left=o>0?`${-r+o}px`:l>0?-r-l+"px":-r+"px"}emit(){return async function(t){const{lifecycle:e,template:{$input:i}}=t;if(!e.active)return;const n=i.value.trim();if(!n.length||t.isLock||t.emitting)return;const a={text:n,mode:t.option.mode,color:t.option.color,time:t.art.currentTime},s=t=>{e.active&&console.error("Error emitting danmuku:",t)};try{t.emitting=!0;const n=await e.wait(t.option.beforeEmit(a));if(!e.active)return;if(t.emitting=!1,!0!==n)return;if(a.border=!0,delete a.time,Promise.resolve(t.danmuku.emit(a)).catch(s),!e.active)return;i.value="",t.lock()}catch(r){s(r),e.active&&(t.emitting=!1)}}(this)}lock(){!function(t){if(!t.lifecycle.active)return;const{addClass:e}=t.utils,{$send:i}=t.template;clearTimeout(t.timer),t.isLock=!0;let n=t.option.lockTime;i.textContent=n,e(i,"apd-lock");const a=()=>{t.timer=setTimeout(()=>{t.lifecycle.active&&(0===n?t.unlock():(n-=1,i.textContent=n,a()))},1e3)};a()}(this)}unlock(){!function(t){if(clearTimeout(t.timer),t.timer=null,t.isLock=!1,!t.lifecycle.active)return;const{removeClass:e}=t.utils,{$send:i}=t.template;i.textContent="\u53d1\u9001",e(i,"apd-lock")}(this)}resize(){if(!this.lifecycle.active)return;if(this.outside)return;if(this.art.fullscreen)return;if(this.art.fullscreenWeb)return;const{$player:t,$controlsCenter:e}=this.art.template,{$danmuku:i}=this.template;this.art.widtht.dataset.color===this.option.color.toUpperCase());s&&t(s,"apd-active"),e(i,this.option.visible?"\u5173\u95ed\u5f39\u5e55":"\u6253\u5f00\u5f39\u5e55"),this.resize()}mount(t){if(!this.lifecycle.active)return;const{errorHandle:e}=this.utils,i="string"===typeof t?document.querySelector(t):t;e(i,`Can not find the mount point: ${t}`),this.append(i,this.template.$danmuku),this.template.$mount=i,this.reset()}destroy(){if(this.lifecycle.closed)return;this.lifecycle.close(),clearTimeout(this.timer),this.timer=null,this.emitting=!1,this.isLock=!1;const t=this.template.$danmuku;t?.parentElement&&t.parentElement.removeChild(t)}}function Z(t){return e=>{const i=new k(e,t);let n;try{n=e.isDestroy?void 0:new R(e,i),!e.isDestroy&&i.option.heatmap&&z(e,i,i.option.heatmap)}catch(a){try{n?.destroy()}catch{}try{i.destroy()}catch{}throw a}return{name:"artplayerPluginDanmuku",emit:i.emit.bind(i),load:i.load.bind(i),config:i.config.bind(i),hide:i.hide.bind(i),show:i.show.bind(i),reset:i.reset.bind(i),mount:n?n.mount.bind(n):()=>{},get option(){return i.option},get isHide(){return i.isHide},get isStop(){return i.isStop}}}}return Z.icons=R.icons,Z}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDanmuku=e(),define(function(){return t.artplayerPluginDanmuku})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDanmuku=e()}(this,function(){"use strict";function t(t){return t instanceof Promise||"[object Promise]"===Object.prototype.toString.call(t)}function e(t){switch(t){case 1:case 2:case 3:default:return 0;case 4:return 2;case 5:return 1}}function i(t){if("string"!==typeof t)return[];const i=t.matchAll(/]*?p="(?

[^"]+)"[^>]*>(?.*?)<\/d>/gs);return Array.from(i).map(t=>{const i=t.groups.p.split(",");if(i.length>=8){return{text:t.groups.text.trim().replaceAll(""",'"').replaceAll("'","'").replaceAll("<","<").replaceAll(">",">").replaceAll("&","&"),time:Number(i[0]),mode:e(Number(i[1])),fontSize:Number(i[2]),color:`#${Number(i[3]).toString(16)}`,timestamp:Number(i[4]),pool:Number(i[5]),userID:i[6],rowID:Number(i[7])}}return null}).filter(Boolean)}function n({data:t}){const{xml:e,id:n}=t,a=i(e);globalThis.postMessage({danmus:a,id:n})}let a=0;function s(t,{signal:s,onCancel:r}={}){return new Promise((o,l)=>{let d,c,p,h=!1;const u=++a,m=()=>g(void 0,function(t){if(void 0!==t?.reason)return t.reason;const e=new Error("Bilibili Danmu loading cancelled");return e.name="AbortError",e}(s));function f(){let t,e=!1;try{if(d){const t=d;d=void 0,t.onmessage=null,t.onerror=null,t.onmessageerror=null,t.terminate()}}catch(i){t=i,e=!0}if(void 0!==c){const n=c;c=void 0;try{URL.revokeObjectURL(n)}catch(i){e||(t=i),e=!0}}if(e)throw t}function g(t,e){if(h)return;h=!0;let i=arguments.length>1;s?.removeEventListener("abort",m),p?.();try{f()}catch(n){i||(e=n),i=!0}i?l(e):o(t)}function y(t,e){if(!h){console.error("Error parsing Bilibili Danmu:",e);try{g(i(t))}catch(n){g(void 0,n)}}}s?.aborted?m():(s?.addEventListener("abort",m),p=r?.(m),h?p?.():async function(){const a=s?await fetch(t,{signal:s}):await fetch(t);if(h)return;const r=await a.text();if(!h)try{const t=`\n ${e.toString()}\n ${i.toString()}\n onmessage = ${n.toString()}\n `;if(c=URL.createObjectURL(new Blob([t],{type:"application/javascript"})),h)return void f();if(d=new Worker(c),h)return void f();d.onmessage=t=>{if(h||t.data?.id!==u)return;const{danmus:e}=t.data;Array.isArray(e)?g(e):g(void 0,new Error("Invalid Bilibili Danmu worker response"))},d.onerror=t=>{t.preventDefault?.(),y(r,t.error||t)},d.onmessageerror=t=>y(r,t.error||t),d.postMessage({xml:r,id:u})}catch(o){y(r,o)}}().catch(t=>g(void 0,t)))})}const r=new WeakMap,o=Symbol("cancelled danmuku input");function l(t){return r.has(t)||r.set(t,{closed:!1,tasks:new Set,emitting:[]}),r.get(t)}function d(t){const e=l(t),i=e.emitting[e.emitting.length-1];return!e.closed&&!t.art.isDestroy&&(!i||i.active())}function c(t){const e=l(t);e.closed=!0;for(const i of[...e.tasks])i.cancel()}function p(t,e,i){const n=t.states[e]||[];for(let a=0;a{const a=n.$ref.offsetTop,s=this.owner.getLeft(n.$ref)-i,r=n.$ref.clientHeight,o=n.$ref.clientWidth,l=s+o,d=e-l,c=l/n.$restTime,p={};p.top=a,p.left=s,p.height=r,p.width=o,p.right=d,p.speed=c,p.distance=l,p.time=n.$restTime,p.mode=n.mode,t.push(p)}),t}left(t){const e=t.getBoundingClientRect();return this.owner.isRotate?e.top:e.left}makeWait(t){this.owner.setState(t,"wait"),t.$ref&&(t.$ref.style.cssText=this.owner.constructor.cssText,t.$ref.style.visibility="hidden",t.$ref.style.marginLeft="0px",t.$ref.style.transform="translateX(0px)",t.$ref.style.transition="transform 0s linear 0s",this.owner.$refs.push(t.$ref),t.$ref=null)}resize(){const{clientWidth:t}=this.owner.$player;this.owner.filter("stop",e=>{if(0===e.mode)e.$ref.style.left=`${t}px`}),this.owner.filter("emit",e=>{switch(e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.left=`${t}px`,e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}})}continue(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("stop",e=>{switch(this.owner.setState(e,"emit"),e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}}),this}suspend(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("emit",e=>{switch(this.owner.setState(e,"stop"),e.mode){case 0:{const i=t-(this.owner.getLeft(e.$ref)-this.owner.getLeft(this.owner.$player));e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition="transform 0s linear 0s";break}}}),this}prepare(t,e){const i=this.owner,n=i.$ref;return e.danmu=t,e.ref=n,t.$ref=n,n.textContent=t.text,i.$danmuku.appendChild(n),n.style.opacity=i.option.opacity,n.style.fontSize=`${i.fontSize}px`,n.style.color=t.color,n.style.border=t.border?`1px solid ${t.color}`:null,n.style.backgroundColor=t.border?"rgb(0 0 0 / 50%)":null,i.utils.setStyles(n,t.style),n}place(t,e,i,n,a){switch(e.style.top=`${i}px`,e.style.visibility="visible",e.dataset.mode=t.mode,e.dataset.id=t.id||"",t.mode){case 0:e.style.left=`${a}px`,e.style.marginLeft="0px",e.style.transform=`translateX(${-n}px)`,e.style.transition=`transform ${t.$restTime}s linear 0s`;break;case 1:case 2:e.style.left="50%",e.style.marginLeft=`-${e.clientWidth/2}px`}}release(t){const{danmu:e,ref:i}=t;i&&e.$ref===i&&(i.style.cssText=this.owner.constructor.cssText,i.style.visibility="hidden",this.owner.$refs.push(i),e.$ref=null),t.ref=null}clear(){this.destroy(),this.owner.$refs=[],this.owner.$danmuku.textContent=""}destroy(){for(const t of this.nodes)t.parentElement&&t.parentElement.removeChild(t);for(const t of this.owner.queue)t.$ref=null;this.owner.$refs.length=0,this.nodes.clear()}}const u=Symbol("cancelled danmuku frame");class m{constructor(t){this.owner=t,this.generation=0,this.starts=0,this.frame=null,this.operation=null,this.closed=!1,this.running=!1,this.fault=!1,this.failedItems=new Set}active(t){const e=this.owner;return!this.closed&&!this.fault&&!e.art.isDestroy&&!e.isStop&&!e.isHide&&t.generation===this.generation}release(t){this.owner.renderer.release(t)}invalidate(){if(this.generation++,null!==this.frame&&window.cancelAnimationFrame(this.frame),this.frame=null,this.owner.timer=null,this.operation){const t=this.operation;this.operation=null,t.cancel(),this.release(t)}this.owner.workerClient?.cancel(),this.failedItems.clear()}report(t){try{this.owner.art.emit("artplayerPluginDanmuku:error",t)}catch(e){console.warn("Failed to report danmuku scheduling error:",e)}}fail(t){this.closed||this.fault||(this.fault=!0,this.invalidate(),this.report(t))}recover(){if(!this.closed&&!this.owner.art.isDestroy&&(this.failedItems.clear(),this.fault))try{this.owner.workerClient?.dispose(),this.owner.createWorker(),this.fault=!1}catch(t){this.report(t)}}schedule(){const t=this.owner;!this.running||this.closed||this.fault||t.art.isDestroy||t.isStop||null!==this.frame||this.operation||(this.frame=window.requestAnimationFrame(()=>{if(this.frame=null,t.timer=null,this.closed||this.fault||t.isStop||t.art.isDestroy)return;let e;const i=new Promise(t=>e=()=>t(u)),n={generation:this.generation,cancel:e,wait:t=>Promise.race([t,i]),ref:null};return this.operation=n,this.run(n).catch(t=>{this.active(n)&&this.fail(t)}).finally(()=>{this.release(n),this.operation===n&&(this.operation=null,this.schedule())})}),t.timer=this.frame)}async run(t){const e=this.owner;if(!e.art.playing||!this.active(t))return;e.filter("emit",t=>{const i=(Date.now()-t.$lastStartTime)/1e3;t.$restTime-=i,t.$lastStartTime=Date.now(),t.$restTime<=0&&e.makeWait(t)});const i=e.readys;for(const a of i){if(!this.active(t))return;if(this.failedItems.has(a))continue;let i;try{i=await t.wait(e.option.beforeVisible(a))}catch(n){if(!this.active(t))return;this.failedItems.add(a),this.report(n);continue}if(!this.active(t))return;if(!i)continue;const{clientWidth:s,clientHeight:r}=e.$player,o=e.renderer.prepare(a,t);if(!this.active(t))return;a.$lastStartTime=Date.now(),a.$restTime=e.speed;const l=s+o.clientWidth,d=await t.wait(e.postMessage({type:"getDanmuTop",target:{mode:a.mode,height:o.clientHeight,speed:l/a.$restTime},visibles:e.visibles,antiOverlap:e.option.antiOverlap,clientWidth:s,clientHeight:r,marginBottom:e.marginBottom,marginTop:e.marginTop})),c="symbol"===typeof d?void 0:d.result;if(!this.active(t)||a.$ref!==o)return;void 0!==c?(e.setState(a,"emit"),e.renderer.place(a,o,c,l,s),t.ref=null,e.art.emit("artplayerPluginDanmuku:visible",a)):(e.setState(a,"ready"),this.release(t))}}destroy(){this.closed=!0,this.invalidate()}}let f=0;class g{constructor(t,e){this.pending=new Map,this.closed=!1,this.failed=!1,this.onFailure=e,this.worker=t();try{this.worker.onmessage=t=>{const e=this.pending.get(t.data?.id);e&&(this.pending.delete(t.data.id),e.resolve(t.data))},this.worker.onerror=t=>{t.preventDefault?.(),this.fail(t.error||t)},this.worker.onmessageerror=t=>this.fail(t.error||t)}catch(i){try{this.dispose()}catch{}throw i}}request(t){t.id=++f;const{id:e}=t;return this.closed||this.failed?Promise.resolve({id:e,result:void 0}):new Promise((i,n)=>{this.pending.set(e,{resolve:i,reject:n});try{this.worker.postMessage(t)}catch(a){this.fail(a)}})}fail(t){if(!this.closed&&!this.failed){this.failed=!0;for(const e of this.pending.values())e.reject(t);this.pending.clear();try{this.dispose()}catch{}this.onFailure(t)}}cancel(){for(const[t,e]of this.pending)e.resolve({id:t,result:void 0});this.pending.clear()}dispose(){if(this.closed)return;this.closed=!0,this.cancel();let t,e=!1;const i=i=>{try{i()}catch(n){e||(t=n),e=!0}};for(const n of["onmessage","onerror","onmessageerror"])i(()=>this.worker[n]=null);if(i(()=>this.worker.terminate()),e)throw t}}const y='/*!\n * artplayer-plugin-danmuku.js v5.3.0\n * Github: https://github.com/zhw2590582/ArtPlayer\n * (c) 2017-2026 Harvey Zhao\n * Released under the MIT License.\n */\nfunction t({target:t,visibles:e,clientWidth:i,clientHeight:h,marginBottom:n,marginTop:o,antiOverlap:s}){const r=h-n,p=e.filter(e=>e.mode===t.mode&&e.top<=r).sort((t,e)=>t.top-e.top);if(0===p.length)return 2===t.mode?r-t.height:o;if(p.unshift({type:"top",top:0,left:0,right:0,height:o,width:i,speed:0,distance:i}),p.push({type:"bottom",top:r,left:0,right:0,height:n,width:i,speed:0,distance:i}),2===t.mode)for(let g=p.length-2;g>=0;g-=1){const e=p[g],i=p[g+1],h=e.top+e.height;if(i.top-h>=t.height)return i.top-t.height}else for(let g=1;g=t.height)return h}const a=[];for(let g=1;g{const i=Math.min(...e.map(t=>t.right)),h=Math.min(...t.map(t=>t.right));return i*e.length-h*t.length});break;case 1:case 2:a.sort((t,e)=>{const i=Math.max(...e.map(t=>t.width));return Math.max(...t.map(t=>t.width))*t.length-i*e.length})}return a[0][0].top}switch(t.mode){case 0:{const e=a.find(e=>e.every(e=>{if(ie.time}));return e&&e[0]?e[0].top:void 0}case 1:case 2:return}}onmessage=e=>{const{data:i}=e;if(!i.id||!i.type)return;const h=(0,{getDanmuTop:t}[i.type])(i);globalThis.postMessage({result:h,id:i.id})};\n',v="undefined"!==typeof self&&self.Blob&&new Blob(["URL.revokeObjectURL(import.meta.url);",y],{type:"text/javascript;charset=utf-8"});function w(t){let e;try{if(e=v&&(self.URL||self.webkitURL).createObjectURL(v),!e)throw"";const i=new Worker(e,{type:"module",name:t?.name});return i.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(e)}),i}catch(i){return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(y),{type:"module",name:t?.name})}}class k{constructor(t,e){const{constructor:i,template:n}=t;this.utils=i.utils,this.validator=i.validator,this.$danmuku=n.$danmuku,this.$player=n.$player,this.art=t,this.queue=[],this.$refs=[],this.isStop=!1,this.isHide=!1,this.timer=null,this.index=0,this.worker=null,this.workerClient=null,this.renderer=new h(this),this.scheduler=new m(this),this.option=k.option,this.states={wait:[],ready:[],emit:[],stop:[]},this.start=this.start.bind(this),this.stop=this.stop.bind(this),this.reset=this.reset.bind(this),this.resize=this.resize.bind(this),this.destroy=this.destroy.bind(this),this.seek=this.seek.bind(this),d(this),t.on("destroy",this.destroy);try{if(this.config(e,!0),!d(this))return;this.createWorker()}catch(a){throw t.off("destroy",this.destroy),c(this),this.scheduler.destroy(),a}t.on("video:play",this.start),t.on("video:playing",this.start),t.on("video:pause",this.stop),t.on("video:waiting",this.stop),t.on("resize",this.resize),t.on("video:seeking",this.seek),this.load().catch(t=>console.warn("Failed to load initial danmuku:",t))}static get option(){return{danmuku:[],speed:5,margin:[10,"25%"],opacity:1,color:"#FFFFFF",mode:0,modes:[0,1,2],fontSize:25,antiOverlap:!0,synchronousPlayback:!1,mount:void 0,heatmap:!1,width:512,points:[],filter:()=>!0,beforeEmit:()=>!0,beforeVisible:()=>!0,visible:!0,emitter:!0,maxLength:200,lockTime:5,theme:"dark",OPACITY:{},FONT_SIZE:{},MARGIN:{},SPEED:{},COLOR:[]}}static get scheme(){return{danmuku:"array|function|string|promise",speed:"number",margin:"array",opacity:"number",color:"string",mode:"number",modes:"array",fontSize:"number|string",antiOverlap:"boolean",synchronousPlayback:"boolean",mount:"?htmldivelement|string",heatmap:"object|boolean",width:"number",points:"array",filter:"function",beforeEmit:"function",beforeVisible:"function",visible:"boolean",emitter:"boolean",maxLength:"number",lockTime:"number",theme:"string",OPACITY:"object",FONT_SIZE:"object",MARGIN:"object",SPEED:"object",COLOR:"array"}}static get cssText(){return'\n user-select: none;\n position: absolute;\n white-space: pre;\n pointer-events: none;\n perspective: 500px;\n display: inline-block;\n will-change: transform;\n font-weight: normal;\n line-height: 1.125;\n visibility: hidden;\n font-family: SimHei, "Microsoft JhengHei", Arial, Helvetica, sans-serif;\n text-shadow: rgb(0, 0, 0) 1px 0px 1px, rgb(0, 0, 0) 0px 1px 1px, rgb(0, 0, 0) 0px -1px 1px, rgb(0, 0, 0) -1px 0px 1px;\n '}get isRotate(){return this.art.plugins?.autoOrientation?.state}get marginTop(){const{clamp:t}=this.utils,e=this.option.margin[0],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return k.option.margin[0]}get marginBottom(){const{clamp:t}=this.utils,e=this.option.margin[1],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return k.option.margin[1]}get fontSize(){const{clamp:t}=this.utils,{clientHeight:e}=this.$player,i=this.option.fontSize;if("number"===typeof i)return Math.round(t(i,12,e));if("string"===typeof i&&i.endsWith("%")){const n=Number.parseFloat(i)/100;return Math.round(t(e*n,12,e))}return k.option.fontSize}get $ref(){return this.renderer.acquire()}get readys(){return function(t){const{currentTime:e}=t.art,i=[];return p(t,"ready",t=>i.push(t)),p(t,"wait",t=>{e+.1>=t.time&&t.time>=e-.1&&i.push(t)}),i}(this)}get visibles(){return this.renderer.visibles}get speed(){return this.option.synchronousPlayback&&this.art.playbackRate?this.option.speed/Number(this.art.playbackRate):this.option.speed}async load(e){const{errorHandle:i}=this.utils,n=function(t,e){const i=l(t);if(e)for(const o of[...i.tasks])o.replace&&o.cancel();let n,a=i.closed||t.art.isDestroy;const s=new Promise(t=>n=t),r=new Set,d="function"===typeof AbortController?new AbortController:void 0,c={replace:e,signal:d?.signal,active:()=>!a&&!i.closed&&!t.art.isDestroy,onCancel:t=>c.active()?(r.add(t),()=>r.delete(t)):(t(),()=>{}),cancel(){if(!a){a=!0,n(o),d?.abort();for(const t of[...r])t();r.clear()}},wait:t=>Promise.race([t,s]),emit(e){i.emitting.push(c);try{return t.emit(e)}finally{i.emitting.pop()}},finish(){i.tasks.delete(c),r.clear()}};return a?n(o):i.tasks.add(c),c}(this,void 0===e);try{if(!n.active())return this;const a=function(e,i){return"function"===typeof e?{asynchronous:!0,value:e()}:t(e)?{asynchronous:!0,value:e}:"string"===typeof e?{asynchronous:!0,value:s(e,i)}:{asynchronous:!1,value:Array.isArray(e)?e:[]}}(e||this.option.danmuku,n),r=a.asynchronous?await n.wait(a.value):a.value;if(!n.active())return this;i(Array.isArray(r),"Danmuku need return an array as result");const o=r;if(void 0===e){if(this.reset(),!n.active())return this;this.queue=[],this.states={wait:[],ready:[],emit:[],stop:[]},this.renderer.clear()}for(let t=0;t{const i=s[e],n=r[e];return"function"===typeof i||"function"===typeof n||t(i)||t(n)?i!==n:JSON.stringify(i)!==JSON.stringify(n)}))&&!i)return this;const o=function(t,e,{defaults:i,validate:n,clamp:a,mount:s}){const r=Object.assign({},i,t,e);return n(r),r.mode=a(r.mode,0,2),r.speed=a(r.speed,1,10),r.opacity=a(r.opacity,0,1),r.lockTime=a(r.lockTime,1,60),r.maxLength=a(r.maxLength,1,1e3),r.mount=r.mount||s,r}(this.option,e,{defaults:k.option,validate:t=>this.validator(t,k.scheme),clamp:n,mount:a});if(!d(this))return this;const l=this.option.beforeVisible!==o.beforeVisible;return this.option=o,l&&(this.scheduler.invalidate(),this.scheduler.schedule()),e.fontSize&&(this.reset(),!d(this))||(this.option.visible?this.show():this.hide(),d(this)&&this.art.emit("artplayerPluginDanmuku:config",this.option)),this}getLeft(t){return this.renderer.left(t)}postMessage(t={}){return this.workerClient.request(t)}createWorker(){this.workerClient=new g(()=>new w,t=>this.scheduler.fail(t)),this.worker=this.workerClient.worker}filter(t,e){return p(this,t,e)}setState(t,e){!function(t,e,i){t.states[e.$state]=t.states[e.$state].filter(t=>t!==e),e.$state=i,e.$ref&&(e.$ref.dataset.state=i),t.states[i].push(e)}(this,t,e)}makeWait(t){this.renderer.makeWait(t)}update(){return this.scheduler.running=!0,this.scheduler.schedule(),this}resize(){this.renderer.resize()}continue(){return this.renderer.continue(),this}suspend(){return this.renderer.suspend(),this}stop(){return this.isStop=!0,this.scheduler.running=!1,this.scheduler.invalidate(),this.suspend(),this.art.emit("artplayerPluginDanmuku:stop"),this}start(){if(this.scheduler.closed||this.art.isDestroy)return this;const t=++this.scheduler.starts,e=this.scheduler.generation;this.isStop=!1,this.scheduler.recover();return this.scheduler.closed||this.art.isDestroy||this.isStop||this.scheduler.fault||e!==this.scheduler.generation||t!==this.scheduler.starts||(this.continue(),this.update(),this.art.emit("artplayerPluginDanmuku:start")),this}reset(){return this.scheduler.invalidate(),this.queue.forEach(t=>this.makeWait(t)),this.art.emit("artplayerPluginDanmuku:reset"),this.scheduler.recover(),this.scheduler.schedule(),this}seek(){this.scheduler.invalidate(),this.scheduler.schedule()}show(){return this.isHide=!1,this.$danmuku.style.opacity=1,this.option.visible=!0,this.art.emit("artplayerPluginDanmuku:show"),this}hide(){return this.isHide=!0,this.scheduler.invalidate(),this.$danmuku.style.opacity=0,this.option.visible=!1,this.art.emit("artplayerPluginDanmuku:hide"),this.scheduler.schedule(),this}destroy(){c(this),this.scheduler.destroy();try{try{this.stop()}finally{this.workerClient?.dispose()}}finally{this.renderer.destroy(),this.art.off("video:play",this.start),this.art.off("video:playing",this.start),this.art.off("video:pause",this.stop),this.art.off("video:waiting",this.stop),this.art.off("resize",this.resize),this.art.off("video:seeking",this.seek),this.art.off("destroy",this.destroy),this.art.emit("artplayerPluginDanmuku:destroy")}}}const x=4294967295;function $(t,e){let i=0,n=t.length;for(;iNumber.isFinite(t)&&t>0))return null;const r={xMin:0,xMax:t,yMin:0,yMax:128,scale:.25,opacity:.2,minHeight:Math.floor(.05*e),sampling:Math.max(1,Math.floor(t/100)),smoothing:.2,flattening:.2},o=Object.assign({},"object"===typeof a?a:{}),l=Object.assign({},r,o);for(const x of Object.keys(r))Number.isFinite(l[x])||(l[x]=r[x]);if(l.sampling<=0&&(l.sampling=r.sampling),l.xMin===l.xMax||l.yMin===l.yMax)return null;const d=Array.isArray(s)&&s.length>0,c=d?[...s]:function(t,e,i,n){if(!Number.isFinite(e)||e<=0||!Number.isFinite(i)||i<=0||!Number.isFinite(n)||n<=0||Math.floor(e/n)+1>x)return[];const a=t.map(t=>t.time).filter(t=>"number"===typeof t&&!Number.isNaN(t)).sort((t,e)=>t-e),s=[],r=i/e;if(!Number.isFinite(r))return[];for(let o=0;o<=e;o+=n){if(s.length>=x||o+n===o)return[];const t=o*r,e=(o+n)*r;s.push([o,$(a,e)-$(a,t)])}return s}(n,t,i,l.sampling);if(!c.length||!c.every(t=>Array.isArray(t)&&Number.isFinite(t[0])&&Number.isFinite(t[1])))return null;const p=c[c.length-1];if(p[0]!==t){if(c.length===x)return null;c.push([t,p[1]])}let h=1/0,u=-1/0;for(const x of c)h=Math.min(h,x[1]),u=Math.max(u,x[1]);const m=(h+u)/2;for(const x of c){const t=x[1];x[1]=t*(t>m?1+l.scale:1-l.scale)+l.minHeight}const f=!d&&!Number.isFinite(o.yMin)&&!Number.isFinite(o.yMax);let g=0;for(const x of c)g=Math.max(g,x[1]);const y=f&&g>r.yMax/4;y&&(l.yMax=4*g);const v=t=>y?Math.min(e,Math.max(.75*e,t)):t,w=(t,e,i,n)=>{const a=function(t,e){const i=e[0]-t[0],n=e[1]-t[1];return{length:Math.sqrt(i**2+n**2),angle:Math.atan2(n,i)}}(e||t,i||t),s=b(Math.cos(a.angle)*l.flattening,0,1,1,0),r=a.angle*s+(n?Math.PI:0),o=a.length*l.smoothing;return[t[0]+Math.cos(r)*o,v(t[1]+Math.sin(r)*o)]},k=c.map(i=>[b(i[0],l.xMin,l.xMax,0,t),v(b(i[1],l.yMin,l.yMax,e,0))]);if(!k.every(t=>t.every(Number.isFinite)))return null;const M=k.map((t,i,n)=>{if(0===i)return`M ${n[n.length-1][0]},${e} L ${t[0]},${e} L ${t[0]},${t[1]}`;const a=w(n[i-1],n[i-2],t),s=w(t,n[i-1],n[i+1],!0);return`C ${a[0]},${a[1]} ${s[0]},${s[1]} ${t[0]},${t[1]}${i===n.length-1?" z":""}`}).join(" ");return/NaN|Infinity/u.test(M)?null:{path:M,opacity:l.opacity,width:t,height:e}}let S=0;function z(t,e,i){if(t.isDestroy)return;const{query:n}=t.constructor.utils;let a;const s=[];let r,o,l,d=!1;const c=()=>!d&&!t.isDestroy;function p(e,i){const n=i;s.push([e,n]),t.on(e,n)}function h(e){if(d)return;let i;d=!0,o=null,l=null;let n=!1;const a=t=>{try{t()}catch(e){n||(i=e),n=!0}};for(const[r,o]of s.splice(0))a(()=>t.off(r,o));if(e&&r&&t.controls.heatmap===r&&a(()=>t.controls.remove("heatmap")),r=void 0,n)throw i}function u(t){c()&&o&&l&&(o.setAttribute("offset",100*t+"%"),l.setAttribute("offset",100*t+"%"))}function m(s){try{!function(s=[]){if(!c())return;o=null,l=null;const d=r;if(d.innerHTML="",t.option.isLive)return;const p=M({width:d.offsetWidth,height:d.offsetHeight,duration:t.duration,queue:e.queue,option:i,points:s});if(!p||!c())return;const h=d.ownerDocument;for(a||(a=h?"heatmap-solids":"heatmap-solids-"+ ++S);h?.getElementById(a);)a="heatmap-solids-"+ ++S;d.innerHTML=`\n \n \n \n \n \n \n \n \n \n \n \n `,o=n("#heatmap-start",d),l=n("#heatmap-stop",d),u(t.played)}(s)}catch(d){try{h(!0)}catch{}throw d}}p("destroy",()=>h(!0));try{t.controls.add({name:"heatmap",position:"top",html:"",style:{position:"absolute",top:"-100px",left:"0px",right:"0px",height:"100px",width:"100%",pointerEvents:"none"},beforeUnmount(){h(!1)},mounted(e){if(r=e,d)return t.controls.heatmap===e&&t.controls.remove("heatmap"),void(r=void 0);c()?(p("video:timeupdate",()=>u(t.played)),p("setBar",(t,e)=>{"played"===t&&u(e)}),p("ready",()=>m()),p("resize",()=>m()),p("artplayerPluginDanmuku:loaded",()=>m()),p("artplayerPluginDanmuku:points",t=>m(t))):h(!0)}})}catch(f){try{h(!0)}catch{}throw f}return()=>h(!0)}const E=new WeakMap;class F{constructor(t){this.art=t,this.closed=!1,this.disposers=new Set,this.cancelled=new Promise(t=>this.cancel=t)}get active(){return!this.closed&&!this.art.isDestroy}own(t){this.closed?t():this.disposers.add(t)}write(t,e,i,n=i=>t[e]=i){const a=t;if(!this.active)return;const s=()=>"function"===typeof a.getPropertyPriority?a.getPropertyPriority(e):void 0;let r=E.get(a);r||(r=new Map,E.set(a,r));let o=r.get(e);o||(o={original:a[e],present:e in a||void 0!==a[e],priority:s(),owners:new Map,apply:n},r.set(e,o)),o.owners.has(this)||this.own(()=>{const t=[...o.owners.keys()],i=t[t.length-1]===this;if(o.owners.delete(this),i&&a[e]===o.last&&s()===o.lastPriority){const t=[...o.owners.values()];t.length?(o.apply(t[t.length-1]),o.last=a[e],o.lastPriority=s()):void 0!==o.priority?a.setProperty(e,o.original,o.priority):o.present?o.apply(o.original):delete a[e]}o.owners.size||(r.delete(e),r.size||E.delete(a))}),o.owners.delete(this),o.owners.set(this,i),o.apply(i),o.last=a[e],o.lastPriority=s()}on(t,e){if(!this.active)return;const i=this;function n(...t){if(i.active)return e.apply(this,t)}const a=()=>this.art.off(t,n);this.own(a);try{this.art.on(t,n),this.active||a()}catch(s){throw this.release(a),s}}proxy(t,e,i){if(!this.active)return;const n=this;function a(...t){if(n.active)return i.apply(this,t)}let s;const r=()=>{if("function"===typeof s){const t=this.art.events;"function"===typeof t?.remove&&t.destroyEvents?.has(s)?t.remove(s):s()}else t.removeEventListener?.(e,a)};this.own(r);try{s=this.art.proxy(t,e,a),this.active||r()}catch(o){throw this.release(r),o}}wait(t){return Promise.race([t,this.cancelled])}release(t){try{t()}catch(e){console.warn("Failed to dispose danmuku setting resource:",e)}}close(){if(!this.closed){this.closed=!0,this.cancel();for(const t of this.disposers)this.release(t);this.disposers.clear()}}}function C({min:t,max:e,container:i,findIndex:n,onChange:a,steps:s=[]}){const{lifecycle:r}=this;if(!r.active)return{reset(){}};const{query:o,clamp:l,setStyle:d}=this.utils;d(i,"touch-action","none"),i.innerHTML=`\n

\n
\n ${s.map(()=>'
').join("")}\n
\n
\n
\n
\n
\n ${s.map(t=>t.hide?"":`
${t.name}
`).join("")}\n
\n `;const c=o(".apd-slider-dot",i),p=o(".apd-slider-progress",i);let h=!1;function u(i){if(!r.active)return;if(void 0===i&&(i=n()),ie)return;const o=(i-t)/(e-t);c.style.left=100*o+"%",0===s.length&&(p.style.width=c.style.left),a(i)}function m(n){const{top:a,height:s,left:r,width:o}=i.getBoundingClientRect();if(this.art.isRotate){const i=l(n.clientY-a,0,s);u(Math.round(i/s*(e-t)+t))}else{const i=l(n.clientX-r,0,o);u(Math.round(i/o*(e-t)+t))}}return r.own(()=>h=!1),r.proxy(i,"click",t=>{m.call(this,t)}),r.proxy(i,"pointerdown",t=>{h=0===t.button}),r.on("document:pointermove",t=>{h&&m.call(this,t)}),r.on("document:pointerup",t=>{h&&(h=!1,m.call(this,t))}),{reset:u}}const D='',T='',P='',A='',L='',H='',O='',N='',V='',q='',I='',j='';function B(){const{option:t}=this;return`\n
\n ${I}${q}\n
\n
\n ${P}\n
\n
\n
\n \u6309\u7c7b\u578b\u5c4f\u853d\n
\n
\n ${A}${L}\n
\u6eda\u52a8
\n
\n
\n ${H}${O}\n
\u9876\u90e8
\n
\n
\n ${N}${V}\n
\u5e95\u90e8
\n
\n
\n
\n
\n
\n ${T}${D}\n \u9632\u6b62\u5f39\u5e55\u91cd\u53e0\n
\n
\n ${T}${D}\n \u540c\u6b65\u89c6\u9891\u901f\u5ea6\n
\n
\n
\n \u4e0d\u900f\u660e\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n \u663e\u793a\u533a\u57df\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u5b57\u53f7\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u901f\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n
\n
\n
\n
\n ${j}\n
\n
\n
\n \u6a21\u5f0f\n
\n
\n ${L}\n
\u6eda\u52a8
\n
\n
\n ${O}\n
\u9876\u90e8
\n
\n
\n ${V}\n
\u5e95\u90e8
\n
\n
\n
\n
\n \u989c\u8272\n
\n ${this.COLOR.map(t=>`
`).join("")}\n
\n
\n
\n
\n
\n \n
\u53d1\u9001
\n
\n `}if("undefined"!==typeof document){const t="artplayer-plugin-danmuku",e=Symbol.for("artplayer-plugin-danmuku.pending-style"),i=document;let n=document.getElementById(t)||i[e];if(!n)if(n=document.createElement("style"),n.id=t,"loading"===document.readyState){i[e]=n;const a=()=>{document.removeEventListener("DOMContentLoaded",a),i[e]===n&&delete i[e];const s=document.getElementById(t);s?s.textContent=n.textContent:(document.head||document.documentElement).appendChild(n)};document.addEventListener("DOMContentLoaded",a,{once:!0})}else(document.head||document.documentElement).appendChild(n);n.textContent='.artplayer-plugin-danmuku{display:flex;position:relative;z-index:99;align-items:center;justify-content:center;font-size:12px;height:32px;width:100%;color:#fff;font-weight:300;flex-shrink:0;gap:10px}.artplayer-plugin-danmuku .apd-icon{cursor:pointer;opacity:.75;transition:all .2s ease;fill:#fff}.artplayer-plugin-danmuku .apd-icon:hover{opacity:1}.artplayer-plugin-danmuku .apd-config{display:flex;position:relative}.artplayer-plugin-danmuku .apd-config .apd-config-panel{position:absolute;bottom:24px;left:0;width:320px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-config .apd-config-panel .apd-config-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-config:hover .apd-config-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-config-mode,.artplayer-plugin-danmuku .apd-config-slider,.artplayer-plugin-danmuku .apd-config-other,.artplayer-plugin-danmuku .apd-style-mode{margin-bottom:15px}.artplayer-plugin-danmuku .apd-modes{display:flex;align-items:center;margin-top:5px;gap:20px}.artplayer-plugin-danmuku .apd-modes .apd-mode{cursor:pointer;text-align:center}.artplayer-plugin-danmuku .apd-modes .apd-mode:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-config-slider{display:flex;align-items:center;gap:12px}.artplayer-plugin-danmuku .apd-config-slider .apd-value{width:32px;text-align:right}.artplayer-plugin-danmuku .apd-slider{position:relative;flex:1;display:flex;height:20px;align-items:center;justify-content:center;cursor:pointer}.artplayer-plugin-danmuku .apd-slider .apd-slider-line{position:relative;height:2px;width:100%;overflow:hidden;border-radius:3px;background-color:#ffffff40}.artplayer-plugin-danmuku .apd-slider .apd-slider-points{position:absolute;inset:0;display:flex;align-items:center;justify-content:space-between}.artplayer-plugin-danmuku .apd-slider .apd-slider-points .apd-slider-point{width:2px;height:2px;border-radius:50%;background-color:#ffffff80}.artplayer-plugin-danmuku .apd-slider .apd-slider-progress{width:0%;height:100%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-dot{position:absolute;transform:translate(-6px);left:0%;width:12px;height:12px;border-radius:50%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps{display:flex;align-items:center;justify-content:space-between;position:absolute;bottom:-12px;width:calc(100% + 32px);color:#777}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps .apd-slider-step{flex-shrink:0;width:36px;text-align:center;scale:.95}.artplayer-plugin-danmuku .apd-config-other{display:flex;align-items:center;gap:20px}.artplayer-plugin-danmuku .apd-config-other .apd-check-off,.artplayer-plugin-danmuku .apd-config-other .apd-check-on{width:16px;height:16px}.artplayer-plugin-danmuku .apd-config-other .apd-other{display:flex;align-items:center;cursor:pointer;gap:2px}.artplayer-plugin-danmuku .apd-config-other .apd-other:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-emitter{display:flex;flex:1;align-items:center;height:100%;background-color:#ffffff40;border-radius:5px}.artplayer-plugin-danmuku .apd-style{position:relative;display:flex;align-items:center;justify-content:center}.artplayer-plugin-danmuku .apd-style .apd-style-panel{position:absolute;bottom:24px;left:0;width:200px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-style .apd-style-panel .apd-style-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-style:hover .apd-style-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-colors{display:flex;flex-wrap:wrap;margin-top:5px;gap:8px}.artplayer-plugin-danmuku .apd-colors .apd-color{width:16px;height:16px;border-radius:2px;cursor:pointer}.artplayer-plugin-danmuku .apd-colors .apd-color.apd-active{border:1px solid black;box-shadow:0 0 0 1px #fff}.artplayer-plugin-danmuku .apd-input{outline:none;height:100%;flex:1;min-width:0;width:auto;border:none;line-height:1;color:#fff;background-color:transparent}.artplayer-plugin-danmuku .apd-input::placeholder{color:#ffffff80}.artplayer-plugin-danmuku .apd-send{display:flex;align-items:center;justify-content:center;height:100%;width:60px;flex-shrink:0;cursor:pointer;text-shadow:none;border-top-right-radius:5px;border-bottom-right-radius:5px;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-send.apd-lock{cursor:not-allowed;color:#666;background-color:#e7e7e7}.art-controls-center .apd-emitter{width:260px;flex:none}.art-fullscreen .artplayer-plugin-danmuku,.art-fullscreen-web .artplayer-plugin-danmuku{height:38px;gap:16px}.art-fullscreen .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen-web .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-on,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-on{width:28px;height:28px}.art-fullscreen .artplayer-plugin-danmuku .apd-emitter,.art-fullscreen-web .artplayer-plugin-danmuku .apd-emitter{width:400px;flex:none}.art-video-player>.artplayer-plugin-danmuku{position:absolute;left:0;right:0;bottom:-40px;padding:0 10px}.art-video-player:has(>.artplayer-plugin-danmuku){margin-bottom:40px}[data-danmuku-emitter=false] .apd-emitter{display:none!important}[data-danmuku-emitter=false] .art-controls-center .artplayer-plugin-danmuku{justify-content:flex-end;gap:18px}[data-danmuku-emitter=false].art-fullscreen .art-controls-center .artplayer-plugin-danmuku,[data-danmuku-emitter=false].art-fullscreen-web .art-controls-center .artplayer-plugin-danmuku{gap:24px}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-icon{fill:#333}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-emitter{background-color:#f1f2f3}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input{color:#000}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input::placeholder{color:#0000004d}[data-danmuku-visible=false] .apd-toggle-off{display:block}[data-danmuku-visible=false] .apd-toggle-on,[data-danmuku-visible=true] .apd-toggle-off{display:none}[data-danmuku-visible=true] .apd-toggle-on{display:block}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-on{display:none}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-off,[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-on{display:block}[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-off,[data-danmuku-sync-video=false] .apd-sync-video .apd-check-on{display:none}[data-danmuku-sync-video=false] .apd-sync-video .apd-check-off,[data-danmuku-sync-video=true] .apd-sync-video .apd-check-on{display:block}[data-danmuku-sync-video=true] .apd-sync-video .apd-check-off{display:none}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-off{display:block}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-on{display:none}[data-danmuku-mode0=false] .art-danmuku [data-mode="0"]{opacity:0!important}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-off{display:none}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-on{display:block}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"]{color:#00a1d6}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"] path{fill:#00a1d6}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-off{display:block}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-on{display:none}[data-danmuku-mode1=false] .art-danmuku [data-mode="1"]{opacity:0!important}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-off{display:none}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-on{display:block}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"]{color:#00a1d6}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"] path{fill:#00a1d6}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-off{display:block}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-on{display:none}[data-danmuku-mode2=false] .art-danmuku [data-mode="2"]{opacity:0!important}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-off{display:none}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-on{display:block}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"]{color:#00a1d6}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"] path{fill:#00a1d6}'}class R{constructor(t,e){this.art=t,this.danmuku=e,this.utils=t.constructor.utils;const{setStyle:i}=this.utils,{$controlsCenter:n}=t.template;this.template={$controlsCenter:n,$mount:n,$danmuku:null,$toggle:null,$config:null,$configPanel:null,$configModes:null,$style:null,$stylePanel:null,$styleModes:null,$colors:null,$opacitySlider:null,$opacityValue:null,$marginSlider:null,$marginValue:null,$fontSizeSlider:null,$fontSizeValue:null,$speedSlider:null,$speedValue:null,$input:null,$send:null},this.slider={opacity:null,margin:null,fontSize:null,speed:null},this.emitting=!1,this.isLock=!1,this.timer=null,this.lifecycle=new F(t),this.destroy=this.destroy.bind(this),this.lifecycle.own(()=>t.off("destroy",this.destroy));try{if(t.on("destroy",this.destroy),!this.lifecycle.active)return;if(this.lifecycle.write(n.style,"display","flex",t=>i(n,"display",t)),this.createTemplate(),!this.lifecycle.active)return;if(this.createSliders(),!this.lifecycle.active)return;if(this.createEvents(),this.mount(this.option.mount),!this.lifecycle.active)return;this.lifecycle.on("resize",()=>this.resize()),this.lifecycle.on("fullscreen",t=>this.onFullscreen(t)),this.lifecycle.on("fullscreenWeb",t=>this.onFullscreen(t)),this.lifecycle.proxy(this.template.$config,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$config,$panel:this.template.$configPanel})}),this.lifecycle.proxy(this.template.$style,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$style,$panel:this.template.$stylePanel})})}catch(a){try{this.destroy()}catch(s){console.warn("Failed to roll back danmuku setting:",s)}throw a}}static get icons(){return{$on:I,$off:q,$config:P,$style:j,$mode_0_off:A,$mode_0_on:L,$mode_1_off:H,$mode_1_on:O,$mode_2_off:N,$mode_2_on:V,$check_on:T,$check_off:D}}get option(){return this.danmuku.option}get outside(){return this.template.$mount!==this.template.$controlsCenter}get TEMPLATE(){return B.call(this)}get OPACITY(){return{min:0,max:100,steps:[],...this.option.OPACITY}}get FONT_SIZE(){return{min:12,max:120,steps:[],...this.option.FONT_SIZE}}get MARGIN(){return{min:0,max:3,steps:[{name:"1/4",value:[10,"75%"]},{name:"\u534a\u5c4f",value:[10,"50%"]},{name:"3/4",value:[10,"25%"]},{name:"\u6ee1\u5c4f",value:[10,10]}],...this.option.MARGIN}}get SPEED(){return{min:0,max:4,steps:[{name:"\u6781\u6162",value:10},{name:"\u8f83\u6162",value:7.5,hide:!0},{name:"\u9002\u4e2d",value:5},{name:"\u8f83\u5feb",value:2.5,hide:!0},{name:"\u6781\u5feb",value:1}],...this.option.SPEED}}get COLOR(){return this.option.COLOR.length?this.option.COLOR:["#FE0302","#FF7204","#FFAA02","#FFD302","#FFFF00","#A0EE00","#00CD00","#019899","#4266BE","#89D5FF","#CC0273","#222222","#9B9B9B","#FFFFFF"]}query(t){const{query:e}=this.utils,{$danmuku:i}=this.template;return e(t,i)}append(t,e){if(!this.lifecycle.active)return;const{append:i}=this.utils;[...t.children].includes(e)||i(t,e)}setData(t,e){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,{$mount:n}=this.template;this.lifecycle.write(i.dataset,t,e),this.outside&&this.lifecycle.write(n.dataset,t,e)}createTemplate(){const{createElement:t,tooltip:e}=this.utils,i=t("div");this.template.$danmuku=i,i.className="artplayer-plugin-danmuku",i.innerHTML=this.TEMPLATE,this.template.$toggle=this.query(".apd-toggle"),this.template.$config=this.query(".apd-config"),this.template.$configPanel=this.query(".apd-config-panel"),this.template.$configModes=this.query(".apd-config-mode .apd-modes"),this.template.$style=this.query(".apd-style"),this.template.$stylePanel=this.query(".apd-style-panel"),this.template.$styleModes=this.query(".apd-style-mode .apd-modes"),this.template.$colors=this.query(".apd-colors"),this.template.$antiOverlap=this.query(".apd-anti-overlap"),this.template.$syncVideo=this.query(".apd-sync-video"),this.template.$opacitySlider=this.query(".apd-config-opacity .apd-slider"),this.template.$opacityValue=this.query(".apd-config-opacity .apd-value"),this.template.$marginSlider=this.query(".apd-config-margin .apd-slider"),this.template.$marginValue=this.query(".apd-config-margin .apd-value"),this.template.$fontSizeSlider=this.query(".apd-config-fontSize .apd-slider"),this.template.$fontSizeValue=this.query(".apd-config-fontSize .apd-value"),this.template.$speedSlider=this.query(".apd-config-speed .apd-slider"),this.template.$speedValue=this.query(".apd-config-speed .apd-value"),this.template.$input=this.query(".apd-input"),this.template.$send=this.query(".apd-send");const{$toggle:n}=this.template;this.lifecycle.on("artplayerPluginDanmuku:show",()=>{e(n,"\u5173\u95ed\u5f39\u5e55")}),this.lifecycle.on("artplayerPluginDanmuku:hide",()=>{e(n,"\u6253\u5f00\u5f39\u5e55")})}createEvents(){const{$toggle:t,$configModes:e,$styleModes:i,$colors:n,$antiOverlap:a,$syncVideo:s,$send:r,$input:o}=this.template;this.lifecycle.proxy(t,"click",()=>{this.danmuku.config({visible:!this.option.visible}),this.reset()}),this.lifecycle.proxy(e,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.option.modes.includes(i)?this.danmuku.config({modes:this.option.modes.filter(t=>t!==i)}):this.danmuku.config({modes:[...this.option.modes,i]}),this.reset()}),this.lifecycle.proxy(a,"click",()=>{this.danmuku.config({antiOverlap:!this.option.antiOverlap}),this.reset()}),this.lifecycle.proxy(s,"click",()=>{this.danmuku.config({synchronousPlayback:!this.option.synchronousPlayback}),this.reset()}),this.lifecycle.proxy(i,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.danmuku.config({mode:i}),this.reset()}),this.lifecycle.proxy(n,"click",t=>{const e=t.target.closest(".apd-color");e&&(this.danmuku.config({color:e.dataset.color}),this.reset())}),this.lifecycle.proxy(r,"click",()=>this.emit()),this.lifecycle.proxy(o,"keypress",t=>{"Enter"===t.key&&(t.preventDefault(),this.emit())})}createSliders(){this.slider.opacity=this.createSlider({...this.OPACITY,container:this.template.$opacitySlider,findIndex:()=>Math.round(100*this.option.opacity),onChange:t=>{const{$opacityValue:e}=this.template;e.textContent=`${t}%`,this.danmuku.config({opacity:t/100})}}),this.slider.margin=this.createSlider({...this.MARGIN,container:this.template.$marginSlider,findIndex:()=>this.MARGIN.steps.findIndex(t=>t.value[0]===this.option.margin[0]&&t.value[1]===this.option.margin[1]),onChange:t=>{const e=this.MARGIN.steps[t];if(!e)return;const{$marginValue:i}=this.template;i.textContent=e.name,this.danmuku.config({margin:e.value})}}),this.slider.fontSize=this.createSlider({...this.FONT_SIZE,container:this.template.$fontSizeSlider,findIndex:()=>this.danmuku.fontSize,onChange:t=>{const{$fontSizeValue:e}=this.template;e.textContent=`${t}px`,t!==this.danmuku.fontSize&&this.danmuku.config({fontSize:t})}}),this.slider.speed=this.createSlider({...this.SPEED,container:this.template.$speedSlider,findIndex:()=>this.SPEED.steps.findIndex(t=>t.value===this.option.speed),onChange:t=>{const e=this.SPEED.steps[t];if(!e)return;const{$speedValue:i}=this.template;i.textContent=e.name,this.danmuku.config({speed:e.value})}})}createSlider(t){return C.call(this,t)}onFullscreen(t){if(!this.lifecycle.active)return;const{$danmuku:e,$controlsCenter:i,$mount:n}=this.template;this.outside?t?this.append(i,e):this.append(n,e):this.append(i,e)}onMouseEnter({$control:t,$panel:e}){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,n=t.getBoundingClientRect(),a=e.getBoundingClientRect(),s=i.getBoundingClientRect(),r=a.width/2-n.width/2,o=s.left-(n.left-r),l=n.right+r-s.right;e.style.left=o>0?`${-r+o}px`:l>0?-r-l+"px":-r+"px"}emit(){return async function(t){const{lifecycle:e,template:{$input:i}}=t;if(!e.active)return;const n=i.value.trim();if(!n.length||t.isLock||t.emitting)return;const a={text:n,mode:t.option.mode,color:t.option.color,time:t.art.currentTime},s=t=>{e.active&&console.error("Error emitting danmuku:",t)};try{t.emitting=!0;const n=await e.wait(t.option.beforeEmit(a));if(!e.active)return;if(t.emitting=!1,!0!==n)return;if(a.border=!0,delete a.time,Promise.resolve(t.danmuku.emit(a)).catch(s),!e.active)return;i.value="",t.lock()}catch(r){s(r),e.active&&(t.emitting=!1)}}(this)}lock(){!function(t){if(!t.lifecycle.active)return;const{addClass:e}=t.utils,{$send:i}=t.template;clearTimeout(t.timer),t.isLock=!0;let n=t.option.lockTime;i.textContent=n,e(i,"apd-lock");const a=()=>{t.timer=setTimeout(()=>{t.lifecycle.active&&(0===n?t.unlock():(n-=1,i.textContent=n,a()))},1e3)};a()}(this)}unlock(){!function(t){if(clearTimeout(t.timer),t.timer=null,t.isLock=!1,!t.lifecycle.active)return;const{removeClass:e}=t.utils,{$send:i}=t.template;i.textContent="\u53d1\u9001",e(i,"apd-lock")}(this)}resize(){if(!this.lifecycle.active)return;if(this.outside)return;if(this.art.fullscreen)return;if(this.art.fullscreenWeb)return;const{$player:t,$controlsCenter:e}=this.art.template,{$danmuku:i}=this.template;this.art.widtht.dataset.color===this.option.color.toUpperCase());s&&t(s,"apd-active"),e(i,this.option.visible?"\u5173\u95ed\u5f39\u5e55":"\u6253\u5f00\u5f39\u5e55"),this.resize()}mount(t){if(!this.lifecycle.active)return;const{errorHandle:e}=this.utils,i="string"===typeof t?document.querySelector(t):t;e(i,`Can not find the mount point: ${t}`),this.append(i,this.template.$danmuku),this.template.$mount=i,this.reset()}destroy(){if(this.lifecycle.closed)return;this.lifecycle.close(),clearTimeout(this.timer),this.timer=null,this.emitting=!1,this.isLock=!1;const t=this.template.$danmuku;t?.parentElement&&t.parentElement.removeChild(t)}}function Z(t){return e=>{const i=new k(e,t);let n;try{n=e.isDestroy?void 0:new R(e,i),!e.isDestroy&&i.option.heatmap&&z(e,i,i.option.heatmap)}catch(a){try{n?.destroy()}catch{}try{i.destroy()}catch{}throw a}return{name:"artplayerPluginDanmuku",emit:i.emit.bind(i),load:i.load.bind(i),config:i.config.bind(i),hide:i.hide.bind(i),show:i.show.bind(i),reset:i.reset.bind(i),mount:n?n.mount.bind(n):()=>{},get option(){return i.option},get isHide(){return i.isHide},get isStop(){return i.isStop}}}}return Z.icons=R.icons,Z}); diff --git a/docs/compiled/artplayer-plugin-danmuku.legacy.js b/docs/compiled/artplayer-plugin-danmuku.legacy.js index 8f98323ce..4811a82a9 100644 --- a/docs/compiled/artplayer-plugin-danmuku.legacy.js +++ b/docs/compiled/artplayer-plugin-danmuku.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDanmuku=e(),define(function(){return t.artplayerPluginDanmuku})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDanmuku=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.defineProperties,i=Object.getOwnPropertyDescriptors,n=Object.getOwnPropertySymbols,a=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,r=Math.pow,o=(e,i,n)=>i in e?t(e,i,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[i]=n,l=(t,e)=>{for(var i in e||(e={}))a.call(e,i)&&o(t,i,e[i]);if(n)for(var i of n(e))s.call(e,i)&&o(t,i,e[i]);return t},d=(t,n)=>e(t,i(n)),c=(t,e,i)=>new Promise((n,a)=>{var s=t=>{try{o(i.next(t))}catch(e){a(e)}},r=t=>{try{o(i.throw(t))}catch(e){a(e)}},o=t=>t.done?n(t.value):Promise.resolve(t.value).then(s,r);o((i=i.apply(t,e)).next())});function p(t){return t instanceof Promise||"[object Promise]"===Object.prototype.toString.call(t)}function h(t){switch(t){case 1:case 2:case 3:default:return 0;case 4:return 2;case 5:return 1}}function u(t){if("string"!==typeof t)return[];const e=new RegExp(']*?p="(?

[^"]+)"[^>]*>(?.*?)<\\/d>',"gs"),i=t.matchAll(e);return Array.from(i).map(t=>{const e=t.groups.p.split(",");if(e.length>=8){return{text:t.groups.text.trim().replaceAll(""",'"').replaceAll("'","'").replaceAll("<","<").replaceAll(">",">").replaceAll("&","&"),time:Number(e[0]),mode:h(Number(e[1])),fontSize:Number(e[2]),color:`#${Number(e[3]).toString(16)}`,timestamp:Number(e[4]),pool:Number(e[5]),userID:e[6],rowID:Number(e[7])}}return null}).filter(Boolean)}function m({data:t}){const{xml:e,id:i}=t,n=u(e);globalThis.postMessage({danmus:n,id:i})}let f=0;function g(t,{signal:e,onCancel:i}={}){return new Promise((n,a)=>{let s,r,o,l=!1;const d=++f,p=()=>y(void 0,function(t){if(void 0!==(null==t?void 0:t.reason))return t.reason;const e=new Error("Bilibili Danmu loading cancelled");return e.name="AbortError",e}(e));function g(){let t,e=!1;try{if(s){const t=s;s=void 0,t.onmessage=null,t.onerror=null,t.onmessageerror=null,t.terminate()}}catch(i){t=i,e=!0}if(void 0!==r){const n=r;r=void 0;try{URL.revokeObjectURL(n)}catch(i){e||(t=i),e=!0}}if(e)throw t}function y(t,i){if(l)return;l=!0;let s=arguments.length>1;null==e||e.removeEventListener("abort",p),null==o||o();try{g()}catch(r){s||(i=r),s=!0}s?a(i):n(t)}function v(t,e){if(!l){console.error("Error parsing Bilibili Danmu:",e);try{y(u(t))}catch(i){y(void 0,i)}}}(null==e?void 0:e.aborted)?p():(null==e||e.addEventListener("abort",p),o=null==i?void 0:i(p),l?null==o||o():function(){return c(this,null,function*(){const i=e?yield fetch(t,{signal:e}):yield fetch(t);if(l)return;const n=yield i.text();if(!l)try{const t=`\n ${h.toString()}\n ${u.toString()}\n onmessage = ${m.toString()}\n `;if(r=URL.createObjectURL(new Blob([t],{type:"application/javascript"})),l)return void g();if(s=new Worker(r),l)return void g();s.onmessage=t=>{var e;if(l||(null==(e=t.data)?void 0:e.id)!==d)return;const{danmus:i}=t.data;Array.isArray(i)?y(i):y(void 0,new Error("Invalid Bilibili Danmu worker response"))},s.onerror=t=>{var e;null==(e=t.preventDefault)||e.call(t),v(n,t.error||t)},s.onmessageerror=t=>v(n,t.error||t),s.postMessage({xml:n,id:d})}catch(a){v(n,a)}})}().catch(t=>y(void 0,t)))})}const y=new WeakMap,v=Symbol("cancelled danmuku input");function w(t){return y.has(t)||y.set(t,{closed:!1,tasks:new Set,emitting:[]}),y.get(t)}function k(t){const e=w(t),i=e.emitting[e.emitting.length-1];return!e.closed&&!t.art.isDestroy&&(!i||i.active())}function x(t){const e=w(t);e.closed=!0;for(const i of[...e.tasks])i.cancel()}function b(t,e,i){const n=t.states[e]||[];for(let a=0;a{const a=n.$ref.offsetTop,s=this.owner.getLeft(n.$ref)-i,r=n.$ref.clientHeight,o=n.$ref.clientWidth,l=s+o,d=e-l,c=l/n.$restTime,p={};p.top=a,p.left=s,p.height=r,p.width=o,p.right=d,p.speed=c,p.distance=l,p.time=n.$restTime,p.mode=n.mode,t.push(p)}),t}left(t){const e=t.getBoundingClientRect();return this.owner.isRotate?e.top:e.left}makeWait(t){this.owner.setState(t,"wait"),t.$ref&&(t.$ref.style.cssText=this.owner.constructor.cssText,t.$ref.style.visibility="hidden",t.$ref.style.marginLeft="0px",t.$ref.style.transform="translateX(0px)",t.$ref.style.transition="transform 0s linear 0s",this.owner.$refs.push(t.$ref),t.$ref=null)}resize(){const{clientWidth:t}=this.owner.$player;this.owner.filter("stop",e=>{if(0===e.mode)e.$ref.style.left=`${t}px`}),this.owner.filter("emit",e=>{switch(e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.left=`${t}px`,e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}})}continue(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("stop",e=>{switch(this.owner.setState(e,"emit"),e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}}),this}suspend(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("emit",e=>{switch(this.owner.setState(e,"stop"),e.mode){case 0:{const i=t-(this.owner.getLeft(e.$ref)-this.owner.getLeft(this.owner.$player));e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition="transform 0s linear 0s";break}}}),this}prepare(t,e){const i=this.owner,n=i.$ref;return e.danmu=t,e.ref=n,t.$ref=n,n.textContent=t.text,i.$danmuku.appendChild(n),n.style.opacity=i.option.opacity,n.style.fontSize=`${i.fontSize}px`,n.style.color=t.color,n.style.border=t.border?`1px solid ${t.color}`:null,n.style.backgroundColor=t.border?"rgb(0 0 0 / 50%)":null,i.utils.setStyles(n,t.style),n}place(t,e,i,n,a){switch(e.style.top=`${i}px`,e.style.visibility="visible",e.dataset.mode=t.mode,e.dataset.id=t.id||"",t.mode){case 0:e.style.left=`${a}px`,e.style.marginLeft="0px",e.style.transform=`translateX(${-n}px)`,e.style.transition=`transform ${t.$restTime}s linear 0s`;break;case 1:case 2:e.style.left="50%",e.style.marginLeft=`-${e.clientWidth/2}px`}}release(t){const{danmu:e,ref:i}=t;i&&e.$ref===i&&(i.style.cssText=this.owner.constructor.cssText,i.style.visibility="hidden",this.owner.$refs.push(i),e.$ref=null),t.ref=null}clear(){this.destroy(),this.owner.$refs=[],this.owner.$danmuku.textContent=""}destroy(){for(const t of this.nodes)t.parentElement&&t.parentElement.removeChild(t);for(const t of this.owner.queue)t.$ref=null;this.owner.$refs.length=0,this.nodes.clear()}}const M=Symbol("cancelled danmuku frame");class S{constructor(t){this.owner=t,this.generation=0,this.starts=0,this.frame=null,this.operation=null,this.closed=!1,this.running=!1,this.fault=!1,this.failedItems=new Set}active(t){const e=this.owner;return!this.closed&&!this.fault&&!e.art.isDestroy&&!e.isStop&&!e.isHide&&t.generation===this.generation}release(t){this.owner.renderer.release(t)}invalidate(){var t;if(this.generation++,null!==this.frame&&window.cancelAnimationFrame(this.frame),this.frame=null,this.owner.timer=null,this.operation){const t=this.operation;this.operation=null,t.cancel(),this.release(t)}null==(t=this.owner.workerClient)||t.cancel(),this.failedItems.clear()}report(t){try{this.owner.art.emit("artplayerPluginDanmuku:error",t)}catch(e){console.warn("Failed to report danmuku scheduling error:",e)}}fail(t){this.closed||this.fault||(this.fault=!0,this.invalidate(),this.report(t))}recover(){var t;if(!this.closed&&!this.owner.art.isDestroy&&(this.failedItems.clear(),this.fault))try{null==(t=this.owner.workerClient)||t.dispose(),this.owner.createWorker(),this.fault=!1}catch(e){this.report(e)}}schedule(){const t=this.owner;!this.running||this.closed||this.fault||t.art.isDestroy||t.isStop||null!==this.frame||this.operation||(this.frame=window.requestAnimationFrame(()=>{if(this.frame=null,t.timer=null,this.closed||this.fault||t.isStop||t.art.isDestroy)return;let e;const i=new Promise(t=>e=()=>t(M)),n={generation:this.generation,cancel:e,wait:t=>Promise.race([t,i]),ref:null};return this.operation=n,this.run(n).catch(t=>{this.active(n)&&this.fail(t)}).finally(()=>{this.release(n),this.operation===n&&(this.operation=null,this.schedule())})}),t.timer=this.frame)}run(t){return c(this,null,function*(){const e=this.owner;if(!e.art.playing||!this.active(t))return;e.filter("emit",t=>{const i=(Date.now()-t.$lastStartTime)/1e3;t.$restTime-=i,t.$lastStartTime=Date.now(),t.$restTime<=0&&e.makeWait(t)});const i=e.readys;for(const a of i){if(!this.active(t))return;if(this.failedItems.has(a))continue;let i;try{i=yield t.wait(e.option.beforeVisible(a))}catch(n){if(!this.active(t))return;this.failedItems.add(a),this.report(n);continue}if(!this.active(t))return;if(!i)continue;const{clientWidth:s,clientHeight:r}=e.$player,o=e.renderer.prepare(a,t);if(!this.active(t))return;a.$lastStartTime=Date.now(),a.$restTime=e.speed;const l=s+o.clientWidth,{result:d}=yield t.wait(e.postMessage({type:"getDanmuTop",target:{mode:a.mode,height:o.clientHeight,speed:l/a.$restTime},visibles:e.visibles,antiOverlap:e.option.antiOverlap,clientWidth:s,clientHeight:r,marginBottom:e.marginBottom,marginTop:e.marginTop}));if(!this.active(t)||a.$ref!==o)return;void 0!==d?(e.setState(a,"emit"),e.renderer.place(a,o,d,l,s),t.ref=null,e.art.emit("artplayerPluginDanmuku:visible",a)):(e.setState(a,"ready"),this.release(t))}})}destroy(){this.closed=!0,this.invalidate()}}let z=0;class E{constructor(t,e){this.pending=new Map,this.closed=!1,this.failed=!1,this.onFailure=e,this.worker=t();try{this.worker.onmessage=t=>{var e;const i=this.pending.get(null==(e=t.data)?void 0:e.id);i&&(this.pending.delete(t.data.id),i.resolve(t.data))},this.worker.onerror=t=>{var e;null==(e=t.preventDefault)||e.call(t),this.fail(t.error||t)},this.worker.onmessageerror=t=>this.fail(t.error||t)}catch(i){try{this.dispose()}catch(n){}throw i}}request(t){t.id=++z;const{id:e}=t;return this.closed||this.failed?Promise.resolve({id:e,result:void 0}):new Promise((i,n)=>{this.pending.set(e,{resolve:i,reject:n});try{this.worker.postMessage(t)}catch(a){this.fail(a)}})}fail(t){if(!this.closed&&!this.failed){this.failed=!0;for(const e of this.pending.values())e.reject(t);this.pending.clear();try{this.dispose()}catch(e){}this.onFailure(t)}}cancel(){for(const[t,e]of this.pending)e.resolve({id:t,result:void 0});this.pending.clear()}dispose(){if(this.closed)return;this.closed=!0,this.cancel();let t,e=!1;const i=i=>{try{i()}catch(n){e||(t=n),e=!0}};for(const n of["onmessage","onerror","onmessageerror"])i(()=>this.worker[n]=null);if(i(()=>this.worker.terminate()),e)throw t}}const F='/*!\n * artplayer-plugin-danmuku.js v5.3.0\n * Github: https://github.com/zhw2590582/ArtPlayer\n * (c) 2017-2026 Harvey Zhao\n * Released under the MIT License.\n */\nfunction t({target:t,visibles:e,clientWidth:i,clientHeight:h,marginBottom:n,marginTop:o,antiOverlap:s}){const r=h-n,p=e.filter(e=>e.mode===t.mode&&e.top<=r).sort((t,e)=>t.top-e.top);if(0===p.length)return 2===t.mode?r-t.height:o;if(p.unshift({type:"top",top:0,left:0,right:0,height:o,width:i,speed:0,distance:i}),p.push({type:"bottom",top:r,left:0,right:0,height:n,width:i,speed:0,distance:i}),2===t.mode)for(let g=p.length-2;g>=0;g-=1){const e=p[g],i=p[g+1],h=e.top+e.height;if(i.top-h>=t.height)return i.top-t.height}else for(let g=1;g=t.height)return h}const a=[];for(let g=1;g{const i=Math.min(...e.map(t=>t.right)),h=Math.min(...t.map(t=>t.right));return i*e.length-h*t.length});break;case 1:case 2:a.sort((t,e)=>{const i=Math.max(...e.map(t=>t.width));return Math.max(...t.map(t=>t.width))*t.length-i*e.length})}return a[0][0].top}switch(t.mode){case 0:{const e=a.find(e=>e.every(e=>{if(ie.time}));return e&&e[0]?e[0].top:void 0}case 1:case 2:return}}onmessage=e=>{const{data:i}=e;if(!i.id||!i.type)return;const h=(0,{getDanmuTop:t}[i.type])(i);globalThis.postMessage({result:h,id:i.id})};\n',C="undefined"!==typeof self&&self.Blob&&new Blob(["URL.revokeObjectURL(import.meta.url);",F],{type:"text/javascript;charset=utf-8"});function D(t){let e;try{if(e=C&&(self.URL||self.webkitURL).createObjectURL(C),!e)throw"";const i=new Worker(e,{type:"module",name:null==t?void 0:t.name});return i.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(e)}),i}catch(i){return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(F),{type:"module",name:null==t?void 0:t.name})}}class P{constructor(t,e){const{constructor:i,template:n}=t;this.utils=i.utils,this.validator=i.validator,this.$danmuku=n.$danmuku,this.$player=n.$player,this.art=t,this.queue=[],this.$refs=[],this.isStop=!1,this.isHide=!1,this.timer=null,this.index=0,this.worker=null,this.workerClient=null,this.renderer=new $(this),this.scheduler=new S(this),this.option=P.option,this.states={wait:[],ready:[],emit:[],stop:[]},this.start=this.start.bind(this),this.stop=this.stop.bind(this),this.reset=this.reset.bind(this),this.resize=this.resize.bind(this),this.destroy=this.destroy.bind(this),this.seek=this.seek.bind(this),k(this),t.on("destroy",this.destroy);try{if(this.config(e,!0),!k(this))return;this.createWorker()}catch(a){throw t.off("destroy",this.destroy),x(this),this.scheduler.destroy(),a}t.on("video:play",this.start),t.on("video:playing",this.start),t.on("video:pause",this.stop),t.on("video:waiting",this.stop),t.on("resize",this.resize),t.on("video:seeking",this.seek),this.load().catch(t=>console.warn("Failed to load initial danmuku:",t))}static get option(){return{danmuku:[],speed:5,margin:[10,"25%"],opacity:1,color:"#FFFFFF",mode:0,modes:[0,1,2],fontSize:25,antiOverlap:!0,synchronousPlayback:!1,mount:void 0,heatmap:!1,width:512,points:[],filter:()=>!0,beforeEmit:()=>!0,beforeVisible:()=>!0,visible:!0,emitter:!0,maxLength:200,lockTime:5,theme:"dark",OPACITY:{},FONT_SIZE:{},MARGIN:{},SPEED:{},COLOR:[]}}static get scheme(){return{danmuku:"array|function|string|promise",speed:"number",margin:"array",opacity:"number",color:"string",mode:"number",modes:"array",fontSize:"number|string",antiOverlap:"boolean",synchronousPlayback:"boolean",mount:"?htmldivelement|string",heatmap:"object|boolean",width:"number",points:"array",filter:"function",beforeEmit:"function",beforeVisible:"function",visible:"boolean",emitter:"boolean",maxLength:"number",lockTime:"number",theme:"string",OPACITY:"object",FONT_SIZE:"object",MARGIN:"object",SPEED:"object",COLOR:"array"}}static get cssText(){return'\n user-select: none;\n position: absolute;\n white-space: pre;\n pointer-events: none;\n perspective: 500px;\n display: inline-block;\n will-change: transform;\n font-weight: normal;\n line-height: 1.125;\n visibility: hidden;\n font-family: SimHei, "Microsoft JhengHei", Arial, Helvetica, sans-serif;\n text-shadow: rgb(0, 0, 0) 1px 0px 1px, rgb(0, 0, 0) 0px 1px 1px, rgb(0, 0, 0) 0px -1px 1px, rgb(0, 0, 0) -1px 0px 1px;\n '}get isRotate(){var t,e;return null==(e=null==(t=this.art.plugins)?void 0:t.autoOrientation)?void 0:e.state}get marginTop(){const{clamp:t}=this.utils,e=this.option.margin[0],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return P.option.margin[0]}get marginBottom(){const{clamp:t}=this.utils,e=this.option.margin[1],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return P.option.margin[1]}get fontSize(){const{clamp:t}=this.utils,{clientHeight:e}=this.$player,i=this.option.fontSize;if("number"===typeof i)return Math.round(t(i,12,e));if("string"===typeof i&&i.endsWith("%")){const n=Number.parseFloat(i)/100;return Math.round(t(e*n,12,e))}return P.option.fontSize}get $ref(){return this.renderer.acquire()}get readys(){return function(t){const{currentTime:e}=t.art,i=[];return b(t,"ready",t=>i.push(t)),b(t,"wait",t=>{e+.1>=t.time&&t.time>=e-.1&&i.push(t)}),i}(this)}get visibles(){return this.renderer.visibles}get speed(){return this.option.synchronousPlayback&&this.art.playbackRate?this.option.speed/Number(this.art.playbackRate):this.option.speed}load(t){return c(this,null,function*(){const{errorHandle:e}=this.utils,i=function(t,e){const i=w(t);if(e)for(const d of[...i.tasks])d.replace&&d.cancel();let n,a=i.closed||t.art.isDestroy;const s=new Promise(t=>n=t),r=new Set,o="function"===typeof AbortController?new AbortController:void 0,l={replace:e,signal:null==o?void 0:o.signal,active:()=>!a&&!i.closed&&!t.art.isDestroy,onCancel:t=>l.active()?(r.add(t),()=>r.delete(t)):(t(),()=>{}),cancel(){if(!a){a=!0,n(v),null==o||o.abort();for(const t of[...r])t();r.clear()}},wait:t=>Promise.race([t,s]),emit(e){i.emitting.push(l);try{return t.emit(e)}finally{i.emitting.pop()}},finish(){i.tasks.delete(l),r.clear()}};return a?n(v):i.tasks.add(l),l}(this,void 0===t);try{if(!i.active())return this;const n=function(t,e){return"function"===typeof t?{asynchronous:!0,value:t()}:p(t)?{asynchronous:!0,value:t}:"string"===typeof t?{asynchronous:!0,value:g(t,e)}:{asynchronous:!1,value:Array.isArray(t)?t:[]}}(t||this.option.danmuku,i),a=n.asynchronous?yield i.wait(n.value):n.value;if(!i.active())return this;if(e(Array.isArray(a),"Danmuku need return an array as result"),void 0===t){if(this.reset(),!i.active())return this;this.queue=[],this.states={wait:[],ready:[],emit:[],stop:[]},this.renderer.clear()}for(let t=0;t{const e=a[t],i=s[t];return"function"===typeof e||"function"===typeof i||p(e)||p(i)?e!==i:JSON.stringify(e)!==JSON.stringify(i)}))&&!e)return this;const r=function(t,e,{defaults:i,validate:n,clamp:a,mount:s}){const r=Object.assign({},i,t,e);return n(r),r.mode=a(r.mode,0,2),r.speed=a(r.speed,1,10),r.opacity=a(r.opacity,0,1),r.lockTime=a(r.lockTime,1,60),r.maxLength=a(r.maxLength,1,1e3),r.mount=r.mount||s,r}(this.option,t,{defaults:P.option,validate:t=>this.validator(t,P.scheme),clamp:i,mount:n});if(!k(this))return this;const o=this.option.beforeVisible!==r.beforeVisible;return this.option=r,o&&(this.scheduler.invalidate(),this.scheduler.schedule()),t.fontSize&&(this.reset(),!k(this))||(this.option.visible?this.show():this.hide(),k(this)&&this.art.emit("artplayerPluginDanmuku:config",this.option)),this}getLeft(t){return this.renderer.left(t)}postMessage(t={}){return this.workerClient.request(t)}createWorker(){this.workerClient=new E(()=>new D,t=>this.scheduler.fail(t)),this.worker=this.workerClient.worker}filter(t,e){return b(this,t,e)}setState(t,e){!function(t,e,i){t.states[e.$state]=t.states[e.$state].filter(t=>t!==e),e.$state=i,e.$ref&&(e.$ref.dataset.state=i),t.states[i].push(e)}(this,t,e)}makeWait(t){this.renderer.makeWait(t)}update(){return this.scheduler.running=!0,this.scheduler.schedule(),this}resize(){this.renderer.resize()}continue(){return this.renderer.continue(),this}suspend(){return this.renderer.suspend(),this}stop(){return this.isStop=!0,this.scheduler.running=!1,this.scheduler.invalidate(),this.suspend(),this.art.emit("artplayerPluginDanmuku:stop"),this}start(){if(this.scheduler.closed||this.art.isDestroy)return this;const t=++this.scheduler.starts,e=this.scheduler.generation;this.isStop=!1,this.scheduler.recover();return this.scheduler.closed||this.art.isDestroy||this.isStop||this.scheduler.fault||e!==this.scheduler.generation||t!==this.scheduler.starts||(this.continue(),this.update(),this.art.emit("artplayerPluginDanmuku:start")),this}reset(){return this.scheduler.invalidate(),this.queue.forEach(t=>this.makeWait(t)),this.art.emit("artplayerPluginDanmuku:reset"),this.scheduler.recover(),this.scheduler.schedule(),this}seek(){this.scheduler.invalidate(),this.scheduler.schedule()}show(){return this.isHide=!1,this.$danmuku.style.opacity=1,this.option.visible=!0,this.art.emit("artplayerPluginDanmuku:show"),this}hide(){return this.isHide=!0,this.scheduler.invalidate(),this.$danmuku.style.opacity=0,this.option.visible=!1,this.art.emit("artplayerPluginDanmuku:hide"),this.scheduler.schedule(),this}destroy(){var t;x(this),this.scheduler.destroy();try{try{this.stop()}finally{null==(t=this.workerClient)||t.dispose()}}finally{this.renderer.destroy(),this.art.off("video:play",this.start),this.art.off("video:playing",this.start),this.art.off("video:pause",this.stop),this.art.off("video:waiting",this.stop),this.art.off("resize",this.resize),this.art.off("video:seeking",this.seek),this.art.off("destroy",this.destroy),this.art.emit("artplayerPluginDanmuku:destroy")}}}const T=4294967295;function A(t,e){let i=0,n=t.length;for(;iNumber.isFinite(t)&&t>0))return null;const o={xMin:0,xMax:t,yMin:0,yMax:128,scale:.25,opacity:.2,minHeight:Math.floor(.05*e),sampling:Math.max(1,Math.floor(t/100)),smoothing:.2,flattening:.2},l=Object.assign({},"object"===typeof a?a:{}),d=Object.assign({},o,l);for(const r of Object.keys(o))Number.isFinite(d[r])||(d[r]=o[r]);if(d.sampling<=0&&(d.sampling=o.sampling),d.xMin===d.xMax||d.yMin===d.yMax)return null;const c=Array.isArray(s)&&s.length>0,p=c?[...s]:function(t,e,i,n){if(!Number.isFinite(e)||e<=0||!Number.isFinite(i)||i<=0||!Number.isFinite(n)||n<=0||Math.floor(e/n)+1>T)return[];const a=t.map(t=>t.time).filter(t=>"number"===typeof t&&!Number.isNaN(t)).sort((t,e)=>t-e),s=[],r=i/e;if(!Number.isFinite(r))return[];for(let o=0;o<=e;o+=n){if(s.length>=T||o+n===o)return[];const t=o*r,e=(o+n)*r;s.push([o,A(a,e)-A(a,t)])}return s}(n,t,i,d.sampling);if(!p.length||!p.every(t=>Array.isArray(t)&&Number.isFinite(t[0])&&Number.isFinite(t[1])))return null;const h=p[p.length-1];if(h[0]!==t){if(p.length===T)return null;p.push([t,h[1]])}let u=1/0,m=-1/0;for(const r of p)u=Math.min(u,r[1]),m=Math.max(m,r[1]);const f=(u+m)/2;for(const r of p){const t=r[1];r[1]=t*(t>f?1+d.scale:1-d.scale)+d.minHeight}const g=!c&&!Number.isFinite(l.yMin)&&!Number.isFinite(l.yMax);let y=0;for(const r of p)y=Math.max(y,r[1]);const v=g&&y>o.yMax/4;v&&(d.yMax=4*y);const w=t=>v?Math.min(e,Math.max(.75*e,t)):t,k=(t,e,i,n)=>{const a=function(t,e){const i=e[0]-t[0],n=e[1]-t[1];return{length:Math.sqrt(r(i,2)+r(n,2)),angle:Math.atan2(n,i)}}(e||t,i||t),s=L(Math.cos(a.angle)*d.flattening,0,1,1,0),o=a.angle*s+(n?Math.PI:0),l=a.length*d.smoothing;return[t[0]+Math.cos(o)*l,w(t[1]+Math.sin(o)*l)]},x=p.map(i=>[L(i[0],d.xMin,d.xMax,0,t),w(L(i[1],d.yMin,d.yMax,e,0))]);if(!x.every(t=>t.every(Number.isFinite)))return null;const b=x.map((t,i,n)=>{if(0===i)return`M ${n[n.length-1][0]},${e} L ${t[0]},${e} L ${t[0]},${t[1]}`;const a=k(n[i-1],n[i-2],t),s=k(t,n[i-1],n[i+1],!0);return`C ${a[0]},${a[1]} ${s[0]},${s[1]} ${t[0]},${t[1]}${i===n.length-1?" z":""}`}).join(" ");return/NaN|Infinity/u.test(b)?null:{path:b,opacity:d.opacity,width:t,height:e}}let H=0;function N(t,e,i){if(t.isDestroy)return;const{query:n}=t.constructor.utils;let a;const s=[];let r,o,l,d=!1;const c=()=>!d&&!t.isDestroy;function p(e,i){s.push([e,i]),t.on(e,i)}function h(e){if(d)return;let i;d=!0,o=null,l=null;let n=!1;const a=t=>{try{t()}catch(e){n||(i=e),n=!0}};for(const[r,o]of s.splice(0))a(()=>t.off(r,o));if(e&&r&&t.controls.heatmap===r&&a(()=>t.controls.remove("heatmap")),r=void 0,n)throw i}function u(t){c()&&o&&l&&(o.setAttribute("offset",100*t+"%"),l.setAttribute("offset",100*t+"%"))}function m(s){try{!function(s=[]){if(!c())return;if(o=null,l=null,r.innerHTML="",t.option.isLive)return;const d=O({width:r.offsetWidth,height:r.offsetHeight,duration:t.duration,queue:e.queue,option:i,points:s});if(!d||!c())return;const p=r.ownerDocument;for(a||(a=p?"heatmap-solids":"heatmap-solids-"+ ++H);null==p?void 0:p.getElementById(a);)a="heatmap-solids-"+ ++H;r.innerHTML=`\n \n \n \n \n \n \n \n \n \n \n \n `,o=n("#heatmap-start",r),l=n("#heatmap-stop",r),u(t.played)}(s)}catch(d){try{h(!0)}catch(p){}throw d}}p("destroy",()=>h(!0));try{t.controls.add({name:"heatmap",position:"top",html:"",style:{position:"absolute",top:"-100px",left:"0px",right:"0px",height:"100px",width:"100%",pointerEvents:"none"},beforeUnmount(){h(!1)},mounted(e){if(r=e,d)return t.controls.heatmap===e&&t.controls.remove("heatmap"),void(r=void 0);c()?(p("video:timeupdate",()=>u(t.played)),p("setBar",(t,e)=>{"played"===t&&u(e)}),p("ready",()=>m()),p("resize",()=>m()),p("artplayerPluginDanmuku:loaded",()=>m()),p("artplayerPluginDanmuku:points",t=>m(t))):h(!0)}})}catch(f){try{h(!0)}catch(g){}throw f}return()=>h(!0)}const V=new WeakMap;class j{constructor(t){this.art=t,this.closed=!1,this.disposers=new Set,this.cancelled=new Promise(t=>this.cancel=t)}get active(){return!this.closed&&!this.art.isDestroy}own(t){this.closed?t():this.disposers.add(t)}write(t,e,i,n=i=>t[e]=i){if(!this.active)return;const a=()=>"function"===typeof t.getPropertyPriority?t.getPropertyPriority(e):void 0;let s=V.get(t);s||(s=new Map,V.set(t,s));let r=s.get(e);r||(r={original:t[e],present:e in t||void 0!==t[e],priority:a(),owners:new Map,apply:n},s.set(e,r)),r.owners.has(this)||this.own(()=>{const i=[...r.owners.keys()],n=i[i.length-1]===this;if(r.owners.delete(this),n&&t[e]===r.last&&a()===r.lastPriority){const i=[...r.owners.values()];i.length?(r.apply(i[i.length-1]),r.last=t[e],r.lastPriority=a()):void 0!==r.priority?t.setProperty(e,r.original,r.priority):r.present?r.apply(r.original):delete t[e]}r.owners.size||(s.delete(e),s.size||V.delete(t))}),r.owners.delete(this),r.owners.set(this,i),r.apply(i),r.last=t[e],r.lastPriority=a()}on(t,e){if(!this.active)return;const i=this;function n(...t){if(i.active)return e.apply(this,t)}const a=()=>this.art.off(t,n);this.own(a);try{this.art.on(t,n),this.active||a()}catch(s){throw this.release(a),s}}proxy(t,e,i){if(!this.active)return;const n=this;function a(...t){if(n.active)return i.apply(this,t)}let s;const r=()=>{var i,n;if("function"===typeof s){const t=this.art.events;"function"===typeof(null==t?void 0:t.remove)&&(null==(i=t.destroyEvents)?void 0:i.has(s))?t.remove(s):s()}else null==(n=t.removeEventListener)||n.call(t,e,a)};this.own(r);try{s=this.art.proxy(t,e,a),this.active||r()}catch(o){throw this.release(r),o}}wait(t){return Promise.race([t,this.cancelled])}release(t){try{t()}catch(e){console.warn("Failed to dispose danmuku setting resource:",e)}}close(){if(!this.closed){this.closed=!0,this.cancel();for(const t of this.disposers)this.release(t);this.disposers.clear()}}}function q({min:t,max:e,container:i,findIndex:n,onChange:a,steps:s=[]}){const{lifecycle:r}=this;if(!r.active)return{reset(){}};const{query:o,clamp:l,setStyle:d}=this.utils;d(i,"touch-action","none"),i.innerHTML=`\n

\n
\n ${s.map(()=>'
').join("")}\n
\n
\n
\n
\n
\n ${s.map(t=>t.hide?"":`
${t.name}
`).join("")}\n
\n `;const c=o(".apd-slider-dot",i),p=o(".apd-slider-progress",i);let h=!1;function u(i){if(!r.active)return;if(void 0===i&&(i=n()),ie)return;const o=(i-t)/(e-t);c.style.left=100*o+"%",0===s.length&&(p.style.width=c.style.left),a(i)}function m(n){const{top:a,height:s,left:r,width:o}=i.getBoundingClientRect();if(this.art.isRotate){const i=l(n.clientY-a,0,s);u(Math.round(i/s*(e-t)+t))}else{const i=l(n.clientX-r,0,o);u(Math.round(i/o*(e-t)+t))}}return r.own(()=>h=!1),r.proxy(i,"click",t=>{m.call(this,t)}),r.proxy(i,"pointerdown",t=>{h=0===t.button}),r.on("document:pointermove",t=>{h&&m.call(this,t)}),r.on("document:pointerup",t=>{h&&(h=!1,m.call(this,t))}),{reset:u}}const I='',R='',B='',Z='',W='',_='',U='',G='',X='',Y='',J='',K='';function Q(){const{option:t}=this;return`\n
\n ${J}${Y}\n
\n
\n ${B}\n
\n
\n
\n \u6309\u7c7b\u578b\u5c4f\u853d\n
\n
\n ${Z}${W}\n
\u6eda\u52a8
\n
\n
\n ${_}${U}\n
\u9876\u90e8
\n
\n
\n ${G}${X}\n
\u5e95\u90e8
\n
\n
\n
\n
\n
\n ${R}${I}\n \u9632\u6b62\u5f39\u5e55\u91cd\u53e0\n
\n
\n ${R}${I}\n \u540c\u6b65\u89c6\u9891\u901f\u5ea6\n
\n
\n
\n \u4e0d\u900f\u660e\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n \u663e\u793a\u533a\u57df\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u5b57\u53f7\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u901f\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n
\n
\n
\n
\n ${K}\n
\n
\n
\n \u6a21\u5f0f\n
\n
\n ${W}\n
\u6eda\u52a8
\n
\n
\n ${U}\n
\u9876\u90e8
\n
\n
\n ${X}\n
\u5e95\u90e8
\n
\n
\n
\n
\n \u989c\u8272\n
\n ${this.COLOR.map(t=>`
`).join("")}\n
\n
\n
\n
\n
\n \n
\u53d1\u9001
\n
\n `}if("undefined"!==typeof document){const t="artplayer-plugin-danmuku",e=Symbol.for("artplayer-plugin-danmuku.pending-style");let i=document.getElementById(t)||document[e];if(!i)if(i=document.createElement("style"),i.id=t,"loading"===document.readyState){document[e]=i;const n=()=>{document.removeEventListener("DOMContentLoaded",n),document[e]===i&&delete document[e];const a=document.getElementById(t);a?a.textContent=i.textContent:(document.head||document.documentElement).appendChild(i)};document.addEventListener("DOMContentLoaded",n,{once:!0})}else(document.head||document.documentElement).appendChild(i);i.textContent='.artplayer-plugin-danmuku{display:flex;position:relative;z-index:99;align-items:center;justify-content:center;font-size:12px;height:32px;width:100%;color:#fff;font-weight:300;flex-shrink:0;gap:10px}.artplayer-plugin-danmuku .apd-icon{cursor:pointer;opacity:.75;transition:all .2s ease;fill:#fff}.artplayer-plugin-danmuku .apd-icon:hover{opacity:1}.artplayer-plugin-danmuku .apd-config{display:flex;position:relative}.artplayer-plugin-danmuku .apd-config .apd-config-panel{position:absolute;bottom:24px;left:0;width:320px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-config .apd-config-panel .apd-config-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-config:hover .apd-config-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-config-mode,.artplayer-plugin-danmuku .apd-config-slider,.artplayer-plugin-danmuku .apd-config-other,.artplayer-plugin-danmuku .apd-style-mode{margin-bottom:15px}.artplayer-plugin-danmuku .apd-modes{display:flex;align-items:center;margin-top:5px;gap:20px}.artplayer-plugin-danmuku .apd-modes .apd-mode{cursor:pointer;text-align:center}.artplayer-plugin-danmuku .apd-modes .apd-mode:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-config-slider{display:flex;align-items:center;gap:12px}.artplayer-plugin-danmuku .apd-config-slider .apd-value{width:32px;text-align:right}.artplayer-plugin-danmuku .apd-slider{position:relative;flex:1;display:flex;height:20px;align-items:center;justify-content:center;cursor:pointer}.artplayer-plugin-danmuku .apd-slider .apd-slider-line{position:relative;height:2px;width:100%;overflow:hidden;border-radius:3px;background-color:#ffffff40}.artplayer-plugin-danmuku .apd-slider .apd-slider-points{position:absolute;inset:0;display:flex;align-items:center;justify-content:space-between}.artplayer-plugin-danmuku .apd-slider .apd-slider-points .apd-slider-point{width:2px;height:2px;border-radius:50%;background-color:#ffffff80}.artplayer-plugin-danmuku .apd-slider .apd-slider-progress{width:0%;height:100%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-dot{position:absolute;transform:translate(-6px);left:0%;width:12px;height:12px;border-radius:50%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps{display:flex;align-items:center;justify-content:space-between;position:absolute;bottom:-12px;width:calc(100% + 32px);color:#777}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps .apd-slider-step{flex-shrink:0;width:36px;text-align:center;scale:.95}.artplayer-plugin-danmuku .apd-config-other{display:flex;align-items:center;gap:20px}.artplayer-plugin-danmuku .apd-config-other .apd-check-off,.artplayer-plugin-danmuku .apd-config-other .apd-check-on{width:16px;height:16px}.artplayer-plugin-danmuku .apd-config-other .apd-other{display:flex;align-items:center;cursor:pointer;gap:2px}.artplayer-plugin-danmuku .apd-config-other .apd-other:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-emitter{display:flex;flex:1;align-items:center;height:100%;background-color:#ffffff40;border-radius:5px}.artplayer-plugin-danmuku .apd-style{position:relative;display:flex;align-items:center;justify-content:center}.artplayer-plugin-danmuku .apd-style .apd-style-panel{position:absolute;bottom:24px;left:0;width:200px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-style .apd-style-panel .apd-style-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-style:hover .apd-style-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-colors{display:flex;flex-wrap:wrap;margin-top:5px;gap:8px}.artplayer-plugin-danmuku .apd-colors .apd-color{width:16px;height:16px;border-radius:2px;cursor:pointer}.artplayer-plugin-danmuku .apd-colors .apd-color.apd-active{border:1px solid black;box-shadow:0 0 0 1px #fff}.artplayer-plugin-danmuku .apd-input{outline:none;height:100%;flex:1;min-width:0;width:auto;border:none;line-height:1;color:#fff;background-color:transparent}.artplayer-plugin-danmuku .apd-input::placeholder{color:#ffffff80}.artplayer-plugin-danmuku .apd-send{display:flex;align-items:center;justify-content:center;height:100%;width:60px;flex-shrink:0;cursor:pointer;text-shadow:none;border-top-right-radius:5px;border-bottom-right-radius:5px;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-send.apd-lock{cursor:not-allowed;color:#666;background-color:#e7e7e7}.art-controls-center .apd-emitter{width:260px;flex:none}.art-fullscreen .artplayer-plugin-danmuku,.art-fullscreen-web .artplayer-plugin-danmuku{height:38px;gap:16px}.art-fullscreen .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen-web .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-on,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-on{width:28px;height:28px}.art-fullscreen .artplayer-plugin-danmuku .apd-emitter,.art-fullscreen-web .artplayer-plugin-danmuku .apd-emitter{width:400px;flex:none}.art-video-player>.artplayer-plugin-danmuku{position:absolute;left:0;right:0;bottom:-40px;padding:0 10px}.art-video-player:has(>.artplayer-plugin-danmuku){margin-bottom:40px}[data-danmuku-emitter=false] .apd-emitter{display:none!important}[data-danmuku-emitter=false] .art-controls-center .artplayer-plugin-danmuku{justify-content:flex-end;gap:18px}[data-danmuku-emitter=false].art-fullscreen .art-controls-center .artplayer-plugin-danmuku,[data-danmuku-emitter=false].art-fullscreen-web .art-controls-center .artplayer-plugin-danmuku{gap:24px}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-icon{fill:#333}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-emitter{background-color:#f1f2f3}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input{color:#000}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input::placeholder{color:#0000004d}[data-danmuku-visible=false] .apd-toggle-off{display:block}[data-danmuku-visible=false] .apd-toggle-on,[data-danmuku-visible=true] .apd-toggle-off{display:none}[data-danmuku-visible=true] .apd-toggle-on{display:block}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-on{display:none}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-off,[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-on{display:block}[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-off,[data-danmuku-sync-video=false] .apd-sync-video .apd-check-on{display:none}[data-danmuku-sync-video=false] .apd-sync-video .apd-check-off,[data-danmuku-sync-video=true] .apd-sync-video .apd-check-on{display:block}[data-danmuku-sync-video=true] .apd-sync-video .apd-check-off{display:none}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-off{display:block}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-on{display:none}[data-danmuku-mode0=false] .art-danmuku [data-mode="0"]{opacity:0!important}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-off{display:none}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-on{display:block}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"]{color:#00a1d6}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"] path{fill:#00a1d6}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-off{display:block}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-on{display:none}[data-danmuku-mode1=false] .art-danmuku [data-mode="1"]{opacity:0!important}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-off{display:none}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-on{display:block}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"]{color:#00a1d6}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"] path{fill:#00a1d6}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-off{display:block}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-on{display:none}[data-danmuku-mode2=false] .art-danmuku [data-mode="2"]{opacity:0!important}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-off{display:none}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-on{display:block}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"]{color:#00a1d6}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"] path{fill:#00a1d6}'}class tt{constructor(t,e){this.art=t,this.danmuku=e,this.utils=t.constructor.utils;const{setStyle:i}=this.utils,{$controlsCenter:n}=t.template;this.template={$controlsCenter:n,$mount:n,$danmuku:null,$toggle:null,$config:null,$configPanel:null,$configModes:null,$style:null,$stylePanel:null,$styleModes:null,$colors:null,$opacitySlider:null,$opacityValue:null,$marginSlider:null,$marginValue:null,$fontSizeSlider:null,$fontSizeValue:null,$speedSlider:null,$speedValue:null,$input:null,$send:null},this.slider={opacity:null,margin:null,fontSize:null,speed:null},this.emitting=!1,this.isLock=!1,this.timer=null,this.lifecycle=new j(t),this.destroy=this.destroy.bind(this),this.lifecycle.own(()=>t.off("destroy",this.destroy));try{if(t.on("destroy",this.destroy),!this.lifecycle.active)return;if(this.lifecycle.write(n.style,"display","flex",t=>i(n,"display",t)),this.createTemplate(),!this.lifecycle.active)return;if(this.createSliders(),!this.lifecycle.active)return;if(this.createEvents(),this.mount(this.option.mount),!this.lifecycle.active)return;this.lifecycle.on("resize",()=>this.resize()),this.lifecycle.on("fullscreen",t=>this.onFullscreen(t)),this.lifecycle.on("fullscreenWeb",t=>this.onFullscreen(t)),this.lifecycle.proxy(this.template.$config,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$config,$panel:this.template.$configPanel})}),this.lifecycle.proxy(this.template.$style,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$style,$panel:this.template.$stylePanel})})}catch(a){try{this.destroy()}catch(s){console.warn("Failed to roll back danmuku setting:",s)}throw a}}static get icons(){return{$on:J,$off:Y,$config:B,$style:K,$mode_0_off:Z,$mode_0_on:W,$mode_1_off:_,$mode_1_on:U,$mode_2_off:G,$mode_2_on:X,$check_on:R,$check_off:I}}get option(){return this.danmuku.option}get outside(){return this.template.$mount!==this.template.$controlsCenter}get TEMPLATE(){return Q.call(this)}get OPACITY(){return l({min:0,max:100,steps:[]},this.option.OPACITY)}get FONT_SIZE(){return l({min:12,max:120,steps:[]},this.option.FONT_SIZE)}get MARGIN(){return l({min:0,max:3,steps:[{name:"1/4",value:[10,"75%"]},{name:"\u534a\u5c4f",value:[10,"50%"]},{name:"3/4",value:[10,"25%"]},{name:"\u6ee1\u5c4f",value:[10,10]}]},this.option.MARGIN)}get SPEED(){return l({min:0,max:4,steps:[{name:"\u6781\u6162",value:10},{name:"\u8f83\u6162",value:7.5,hide:!0},{name:"\u9002\u4e2d",value:5},{name:"\u8f83\u5feb",value:2.5,hide:!0},{name:"\u6781\u5feb",value:1}]},this.option.SPEED)}get COLOR(){return this.option.COLOR.length?this.option.COLOR:["#FE0302","#FF7204","#FFAA02","#FFD302","#FFFF00","#A0EE00","#00CD00","#019899","#4266BE","#89D5FF","#CC0273","#222222","#9B9B9B","#FFFFFF"]}query(t){const{query:e}=this.utils,{$danmuku:i}=this.template;return e(t,i)}append(t,e){if(!this.lifecycle.active)return;const{append:i}=this.utils;[...t.children].includes(e)||i(t,e)}setData(t,e){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,{$mount:n}=this.template;this.lifecycle.write(i.dataset,t,e),this.outside&&this.lifecycle.write(n.dataset,t,e)}createTemplate(){const{createElement:t,tooltip:e}=this.utils,i=t("div");this.template.$danmuku=i,i.className="artplayer-plugin-danmuku",i.innerHTML=this.TEMPLATE,this.template.$toggle=this.query(".apd-toggle"),this.template.$config=this.query(".apd-config"),this.template.$configPanel=this.query(".apd-config-panel"),this.template.$configModes=this.query(".apd-config-mode .apd-modes"),this.template.$style=this.query(".apd-style"),this.template.$stylePanel=this.query(".apd-style-panel"),this.template.$styleModes=this.query(".apd-style-mode .apd-modes"),this.template.$colors=this.query(".apd-colors"),this.template.$antiOverlap=this.query(".apd-anti-overlap"),this.template.$syncVideo=this.query(".apd-sync-video"),this.template.$opacitySlider=this.query(".apd-config-opacity .apd-slider"),this.template.$opacityValue=this.query(".apd-config-opacity .apd-value"),this.template.$marginSlider=this.query(".apd-config-margin .apd-slider"),this.template.$marginValue=this.query(".apd-config-margin .apd-value"),this.template.$fontSizeSlider=this.query(".apd-config-fontSize .apd-slider"),this.template.$fontSizeValue=this.query(".apd-config-fontSize .apd-value"),this.template.$speedSlider=this.query(".apd-config-speed .apd-slider"),this.template.$speedValue=this.query(".apd-config-speed .apd-value"),this.template.$input=this.query(".apd-input"),this.template.$send=this.query(".apd-send");const{$toggle:n}=this.template;this.lifecycle.on("artplayerPluginDanmuku:show",()=>{e(n,"\u5173\u95ed\u5f39\u5e55")}),this.lifecycle.on("artplayerPluginDanmuku:hide",()=>{e(n,"\u6253\u5f00\u5f39\u5e55")})}createEvents(){const{$toggle:t,$configModes:e,$styleModes:i,$colors:n,$antiOverlap:a,$syncVideo:s,$send:r,$input:o}=this.template;this.lifecycle.proxy(t,"click",()=>{this.danmuku.config({visible:!this.option.visible}),this.reset()}),this.lifecycle.proxy(e,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.option.modes.includes(i)?this.danmuku.config({modes:this.option.modes.filter(t=>t!==i)}):this.danmuku.config({modes:[...this.option.modes,i]}),this.reset()}),this.lifecycle.proxy(a,"click",()=>{this.danmuku.config({antiOverlap:!this.option.antiOverlap}),this.reset()}),this.lifecycle.proxy(s,"click",()=>{this.danmuku.config({synchronousPlayback:!this.option.synchronousPlayback}),this.reset()}),this.lifecycle.proxy(i,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.danmuku.config({mode:i}),this.reset()}),this.lifecycle.proxy(n,"click",t=>{const e=t.target.closest(".apd-color");e&&(this.danmuku.config({color:e.dataset.color}),this.reset())}),this.lifecycle.proxy(r,"click",()=>this.emit()),this.lifecycle.proxy(o,"keypress",t=>{"Enter"===t.key&&(t.preventDefault(),this.emit())})}createSliders(){this.slider.opacity=this.createSlider(d(l({},this.OPACITY),{container:this.template.$opacitySlider,findIndex:()=>Math.round(100*this.option.opacity),onChange:t=>{const{$opacityValue:e}=this.template;e.textContent=`${t}%`,this.danmuku.config({opacity:t/100})}})),this.slider.margin=this.createSlider(d(l({},this.MARGIN),{container:this.template.$marginSlider,findIndex:()=>this.MARGIN.steps.findIndex(t=>t.value[0]===this.option.margin[0]&&t.value[1]===this.option.margin[1]),onChange:t=>{const e=this.MARGIN.steps[t];if(!e)return;const{$marginValue:i}=this.template;i.textContent=e.name,this.danmuku.config({margin:e.value})}})),this.slider.fontSize=this.createSlider(d(l({},this.FONT_SIZE),{container:this.template.$fontSizeSlider,findIndex:()=>this.danmuku.fontSize,onChange:t=>{const{$fontSizeValue:e}=this.template;e.textContent=`${t}px`,t!==this.danmuku.fontSize&&this.danmuku.config({fontSize:t})}})),this.slider.speed=this.createSlider(d(l({},this.SPEED),{container:this.template.$speedSlider,findIndex:()=>this.SPEED.steps.findIndex(t=>t.value===this.option.speed),onChange:t=>{const e=this.SPEED.steps[t];if(!e)return;const{$speedValue:i}=this.template;i.textContent=e.name,this.danmuku.config({speed:e.value})}}))}createSlider(t){return q.call(this,t)}onFullscreen(t){if(!this.lifecycle.active)return;const{$danmuku:e,$controlsCenter:i,$mount:n}=this.template;this.outside?t?this.append(i,e):this.append(n,e):this.append(i,e)}onMouseEnter({$control:t,$panel:e}){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,n=t.getBoundingClientRect(),a=e.getBoundingClientRect(),s=i.getBoundingClientRect(),r=a.width/2-n.width/2,o=s.left-(n.left-r),l=n.right+r-s.right;e.style.left=o>0?`${-r+o}px`:l>0?-r-l+"px":-r+"px"}emit(){return function(t){return c(this,null,function*(){const{lifecycle:e,template:{$input:i}}=t;if(!e.active)return;const n=i.value.trim();if(!n.length||t.isLock||t.emitting)return;const a={text:n,mode:t.option.mode,color:t.option.color,time:t.art.currentTime},s=t=>{e.active&&console.error("Error emitting danmuku:",t)};try{t.emitting=!0;const n=yield e.wait(t.option.beforeEmit(a));if(!e.active)return;if(t.emitting=!1,!0!==n)return;if(a.border=!0,delete a.time,Promise.resolve(t.danmuku.emit(a)).catch(s),!e.active)return;i.value="",t.lock()}catch(r){s(r),e.active&&(t.emitting=!1)}})}(this)}lock(){!function(t){if(!t.lifecycle.active)return;const{addClass:e}=t.utils,{$send:i}=t.template;clearTimeout(t.timer),t.isLock=!0;let n=t.option.lockTime;i.textContent=n,e(i,"apd-lock");const a=()=>{t.timer=setTimeout(()=>{t.lifecycle.active&&(0===n?t.unlock():(n-=1,i.textContent=n,a()))},1e3)};a()}(this)}unlock(){!function(t){if(clearTimeout(t.timer),t.timer=null,t.isLock=!1,!t.lifecycle.active)return;const{removeClass:e}=t.utils,{$send:i}=t.template;i.textContent="\u53d1\u9001",e(i,"apd-lock")}(this)}resize(){if(!this.lifecycle.active)return;if(this.outside)return;if(this.art.fullscreen)return;if(this.art.fullscreenWeb)return;const{$player:t,$controlsCenter:e}=this.art.template,{$danmuku:i}=this.template;this.art.widtht.dataset.color===this.option.color.toUpperCase());s&&t(s,"apd-active"),e(i,this.option.visible?"\u5173\u95ed\u5f39\u5e55":"\u6253\u5f00\u5f39\u5e55"),this.resize()}mount(t){if(!this.lifecycle.active)return;const{errorHandle:e}=this.utils,i="string"===typeof t?document.querySelector(t):t;e(i,`Can not find the mount point: ${t}`),this.append(i,this.template.$danmuku),this.template.$mount=i,this.reset()}destroy(){if(this.lifecycle.closed)return;this.lifecycle.close(),clearTimeout(this.timer),this.timer=null,this.emitting=!1,this.isLock=!1;const t=this.template.$danmuku;(null==t?void 0:t.parentElement)&&t.parentElement.removeChild(t)}}function et(t){return e=>{const i=new P(e,t);let n;try{n=e.isDestroy?void 0:new tt(e,i),!e.isDestroy&&i.option.heatmap&&N(e,i,i.option.heatmap)}catch(a){try{null==n||n.destroy()}catch(s){}try{i.destroy()}catch(s){}throw a}return{name:"artplayerPluginDanmuku",emit:i.emit.bind(i),load:i.load.bind(i),config:i.config.bind(i),hide:i.hide.bind(i),show:i.show.bind(i),reset:i.reset.bind(i),mount:n?n.mount.bind(n):()=>{},get option(){return i.option},get isHide(){return i.isHide},get isStop(){return i.isStop}}}}return et.icons=tt.icons,et}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDanmuku=e(),define(function(){return t.artplayerPluginDanmuku})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDanmuku=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.defineProperties,i=Object.getOwnPropertyDescriptors,n=Object.getOwnPropertySymbols,a=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,r=Math.pow,o=(e,i,n)=>i in e?t(e,i,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[i]=n,l=(t,e)=>{for(var i in e||(e={}))a.call(e,i)&&o(t,i,e[i]);if(n)for(var i of n(e))s.call(e,i)&&o(t,i,e[i]);return t},d=(t,n)=>e(t,i(n)),c=(t,e,i)=>new Promise((n,a)=>{var s=t=>{try{o(i.next(t))}catch(e){a(e)}},r=t=>{try{o(i.throw(t))}catch(e){a(e)}},o=t=>t.done?n(t.value):Promise.resolve(t.value).then(s,r);o((i=i.apply(t,e)).next())});function p(t){return t instanceof Promise||"[object Promise]"===Object.prototype.toString.call(t)}function h(t){switch(t){case 1:case 2:case 3:default:return 0;case 4:return 2;case 5:return 1}}function u(t){if("string"!==typeof t)return[];const e=new RegExp(']*?p="(?

[^"]+)"[^>]*>(?.*?)<\\/d>',"gs"),i=t.matchAll(e);return Array.from(i).map(t=>{const e=t.groups.p.split(",");if(e.length>=8){return{text:t.groups.text.trim().replaceAll(""",'"').replaceAll("'","'").replaceAll("<","<").replaceAll(">",">").replaceAll("&","&"),time:Number(e[0]),mode:h(Number(e[1])),fontSize:Number(e[2]),color:`#${Number(e[3]).toString(16)}`,timestamp:Number(e[4]),pool:Number(e[5]),userID:e[6],rowID:Number(e[7])}}return null}).filter(Boolean)}function m({data:t}){const{xml:e,id:i}=t,n=u(e);globalThis.postMessage({danmus:n,id:i})}let f=0;function g(t,{signal:e,onCancel:i}={}){return new Promise((n,a)=>{let s,r,o,l=!1;const d=++f,p=()=>y(void 0,function(t){if(void 0!==(null==t?void 0:t.reason))return t.reason;const e=new Error("Bilibili Danmu loading cancelled");return e.name="AbortError",e}(e));function g(){let t,e=!1;try{if(s){const t=s;s=void 0,t.onmessage=null,t.onerror=null,t.onmessageerror=null,t.terminate()}}catch(i){t=i,e=!0}if(void 0!==r){const n=r;r=void 0;try{URL.revokeObjectURL(n)}catch(i){e||(t=i),e=!0}}if(e)throw t}function y(t,i){if(l)return;l=!0;let s=arguments.length>1;null==e||e.removeEventListener("abort",p),null==o||o();try{g()}catch(r){s||(i=r),s=!0}s?a(i):n(t)}function v(t,e){if(!l){console.error("Error parsing Bilibili Danmu:",e);try{y(u(t))}catch(i){y(void 0,i)}}}(null==e?void 0:e.aborted)?p():(null==e||e.addEventListener("abort",p),o=null==i?void 0:i(p),l?null==o||o():function(){return c(this,null,function*(){const i=e?yield fetch(t,{signal:e}):yield fetch(t);if(l)return;const n=yield i.text();if(!l)try{const t=`\n ${h.toString()}\n ${u.toString()}\n onmessage = ${m.toString()}\n `;if(r=URL.createObjectURL(new Blob([t],{type:"application/javascript"})),l)return void g();if(s=new Worker(r),l)return void g();s.onmessage=t=>{var e;if(l||(null==(e=t.data)?void 0:e.id)!==d)return;const{danmus:i}=t.data;Array.isArray(i)?y(i):y(void 0,new Error("Invalid Bilibili Danmu worker response"))},s.onerror=t=>{var e;null==(e=t.preventDefault)||e.call(t),v(n,t.error||t)},s.onmessageerror=t=>v(n,t.error||t),s.postMessage({xml:n,id:d})}catch(a){v(n,a)}})}().catch(t=>y(void 0,t)))})}const y=new WeakMap,v=Symbol("cancelled danmuku input");function w(t){return y.has(t)||y.set(t,{closed:!1,tasks:new Set,emitting:[]}),y.get(t)}function k(t){const e=w(t),i=e.emitting[e.emitting.length-1];return!e.closed&&!t.art.isDestroy&&(!i||i.active())}function x(t){const e=w(t);e.closed=!0;for(const i of[...e.tasks])i.cancel()}function b(t,e,i){const n=t.states[e]||[];for(let a=0;a{const a=n.$ref.offsetTop,s=this.owner.getLeft(n.$ref)-i,r=n.$ref.clientHeight,o=n.$ref.clientWidth,l=s+o,d=e-l,c=l/n.$restTime,p={};p.top=a,p.left=s,p.height=r,p.width=o,p.right=d,p.speed=c,p.distance=l,p.time=n.$restTime,p.mode=n.mode,t.push(p)}),t}left(t){const e=t.getBoundingClientRect();return this.owner.isRotate?e.top:e.left}makeWait(t){this.owner.setState(t,"wait"),t.$ref&&(t.$ref.style.cssText=this.owner.constructor.cssText,t.$ref.style.visibility="hidden",t.$ref.style.marginLeft="0px",t.$ref.style.transform="translateX(0px)",t.$ref.style.transition="transform 0s linear 0s",this.owner.$refs.push(t.$ref),t.$ref=null)}resize(){const{clientWidth:t}=this.owner.$player;this.owner.filter("stop",e=>{if(0===e.mode)e.$ref.style.left=`${t}px`}),this.owner.filter("emit",e=>{switch(e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.left=`${t}px`,e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}})}continue(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("stop",e=>{switch(this.owner.setState(e,"emit"),e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}}),this}suspend(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("emit",e=>{switch(this.owner.setState(e,"stop"),e.mode){case 0:{const i=t-(this.owner.getLeft(e.$ref)-this.owner.getLeft(this.owner.$player));e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition="transform 0s linear 0s";break}}}),this}prepare(t,e){const i=this.owner,n=i.$ref;return e.danmu=t,e.ref=n,t.$ref=n,n.textContent=t.text,i.$danmuku.appendChild(n),n.style.opacity=i.option.opacity,n.style.fontSize=`${i.fontSize}px`,n.style.color=t.color,n.style.border=t.border?`1px solid ${t.color}`:null,n.style.backgroundColor=t.border?"rgb(0 0 0 / 50%)":null,i.utils.setStyles(n,t.style),n}place(t,e,i,n,a){switch(e.style.top=`${i}px`,e.style.visibility="visible",e.dataset.mode=t.mode,e.dataset.id=t.id||"",t.mode){case 0:e.style.left=`${a}px`,e.style.marginLeft="0px",e.style.transform=`translateX(${-n}px)`,e.style.transition=`transform ${t.$restTime}s linear 0s`;break;case 1:case 2:e.style.left="50%",e.style.marginLeft=`-${e.clientWidth/2}px`}}release(t){const{danmu:e,ref:i}=t;i&&e.$ref===i&&(i.style.cssText=this.owner.constructor.cssText,i.style.visibility="hidden",this.owner.$refs.push(i),e.$ref=null),t.ref=null}clear(){this.destroy(),this.owner.$refs=[],this.owner.$danmuku.textContent=""}destroy(){for(const t of this.nodes)t.parentElement&&t.parentElement.removeChild(t);for(const t of this.owner.queue)t.$ref=null;this.owner.$refs.length=0,this.nodes.clear()}}const M=Symbol("cancelled danmuku frame");class S{constructor(t){this.owner=t,this.generation=0,this.starts=0,this.frame=null,this.operation=null,this.closed=!1,this.running=!1,this.fault=!1,this.failedItems=new Set}active(t){const e=this.owner;return!this.closed&&!this.fault&&!e.art.isDestroy&&!e.isStop&&!e.isHide&&t.generation===this.generation}release(t){this.owner.renderer.release(t)}invalidate(){var t;if(this.generation++,null!==this.frame&&window.cancelAnimationFrame(this.frame),this.frame=null,this.owner.timer=null,this.operation){const t=this.operation;this.operation=null,t.cancel(),this.release(t)}null==(t=this.owner.workerClient)||t.cancel(),this.failedItems.clear()}report(t){try{this.owner.art.emit("artplayerPluginDanmuku:error",t)}catch(e){console.warn("Failed to report danmuku scheduling error:",e)}}fail(t){this.closed||this.fault||(this.fault=!0,this.invalidate(),this.report(t))}recover(){var t;if(!this.closed&&!this.owner.art.isDestroy&&(this.failedItems.clear(),this.fault))try{null==(t=this.owner.workerClient)||t.dispose(),this.owner.createWorker(),this.fault=!1}catch(e){this.report(e)}}schedule(){const t=this.owner;!this.running||this.closed||this.fault||t.art.isDestroy||t.isStop||null!==this.frame||this.operation||(this.frame=window.requestAnimationFrame(()=>{if(this.frame=null,t.timer=null,this.closed||this.fault||t.isStop||t.art.isDestroy)return;let e;const i=new Promise(t=>e=()=>t(M)),n={generation:this.generation,cancel:e,wait:t=>Promise.race([t,i]),ref:null};return this.operation=n,this.run(n).catch(t=>{this.active(n)&&this.fail(t)}).finally(()=>{this.release(n),this.operation===n&&(this.operation=null,this.schedule())})}),t.timer=this.frame)}run(t){return c(this,null,function*(){const e=this.owner;if(!e.art.playing||!this.active(t))return;e.filter("emit",t=>{const i=(Date.now()-t.$lastStartTime)/1e3;t.$restTime-=i,t.$lastStartTime=Date.now(),t.$restTime<=0&&e.makeWait(t)});const i=e.readys;for(const a of i){if(!this.active(t))return;if(this.failedItems.has(a))continue;let i;try{i=yield t.wait(e.option.beforeVisible(a))}catch(n){if(!this.active(t))return;this.failedItems.add(a),this.report(n);continue}if(!this.active(t))return;if(!i)continue;const{clientWidth:s,clientHeight:r}=e.$player,o=e.renderer.prepare(a,t);if(!this.active(t))return;a.$lastStartTime=Date.now(),a.$restTime=e.speed;const l=s+o.clientWidth,d=yield t.wait(e.postMessage({type:"getDanmuTop",target:{mode:a.mode,height:o.clientHeight,speed:l/a.$restTime},visibles:e.visibles,antiOverlap:e.option.antiOverlap,clientWidth:s,clientHeight:r,marginBottom:e.marginBottom,marginTop:e.marginTop})),c="symbol"===typeof d?void 0:d.result;if(!this.active(t)||a.$ref!==o)return;void 0!==c?(e.setState(a,"emit"),e.renderer.place(a,o,c,l,s),t.ref=null,e.art.emit("artplayerPluginDanmuku:visible",a)):(e.setState(a,"ready"),this.release(t))}})}destroy(){this.closed=!0,this.invalidate()}}let z=0;class E{constructor(t,e){this.pending=new Map,this.closed=!1,this.failed=!1,this.onFailure=e,this.worker=t();try{this.worker.onmessage=t=>{var e;const i=this.pending.get(null==(e=t.data)?void 0:e.id);i&&(this.pending.delete(t.data.id),i.resolve(t.data))},this.worker.onerror=t=>{var e;null==(e=t.preventDefault)||e.call(t),this.fail(t.error||t)},this.worker.onmessageerror=t=>this.fail(t.error||t)}catch(i){try{this.dispose()}catch(n){}throw i}}request(t){t.id=++z;const{id:e}=t;return this.closed||this.failed?Promise.resolve({id:e,result:void 0}):new Promise((i,n)=>{this.pending.set(e,{resolve:i,reject:n});try{this.worker.postMessage(t)}catch(a){this.fail(a)}})}fail(t){if(!this.closed&&!this.failed){this.failed=!0;for(const e of this.pending.values())e.reject(t);this.pending.clear();try{this.dispose()}catch(e){}this.onFailure(t)}}cancel(){for(const[t,e]of this.pending)e.resolve({id:t,result:void 0});this.pending.clear()}dispose(){if(this.closed)return;this.closed=!0,this.cancel();let t,e=!1;const i=i=>{try{i()}catch(n){e||(t=n),e=!0}};for(const n of["onmessage","onerror","onmessageerror"])i(()=>this.worker[n]=null);if(i(()=>this.worker.terminate()),e)throw t}}const F='/*!\n * artplayer-plugin-danmuku.js v5.3.0\n * Github: https://github.com/zhw2590582/ArtPlayer\n * (c) 2017-2026 Harvey Zhao\n * Released under the MIT License.\n */\nfunction t({target:t,visibles:e,clientWidth:i,clientHeight:h,marginBottom:n,marginTop:o,antiOverlap:s}){const r=h-n,p=e.filter(e=>e.mode===t.mode&&e.top<=r).sort((t,e)=>t.top-e.top);if(0===p.length)return 2===t.mode?r-t.height:o;if(p.unshift({type:"top",top:0,left:0,right:0,height:o,width:i,speed:0,distance:i}),p.push({type:"bottom",top:r,left:0,right:0,height:n,width:i,speed:0,distance:i}),2===t.mode)for(let g=p.length-2;g>=0;g-=1){const e=p[g],i=p[g+1],h=e.top+e.height;if(i.top-h>=t.height)return i.top-t.height}else for(let g=1;g=t.height)return h}const a=[];for(let g=1;g{const i=Math.min(...e.map(t=>t.right)),h=Math.min(...t.map(t=>t.right));return i*e.length-h*t.length});break;case 1:case 2:a.sort((t,e)=>{const i=Math.max(...e.map(t=>t.width));return Math.max(...t.map(t=>t.width))*t.length-i*e.length})}return a[0][0].top}switch(t.mode){case 0:{const e=a.find(e=>e.every(e=>{if(ie.time}));return e&&e[0]?e[0].top:void 0}case 1:case 2:return}}onmessage=e=>{const{data:i}=e;if(!i.id||!i.type)return;const h=(0,{getDanmuTop:t}[i.type])(i);globalThis.postMessage({result:h,id:i.id})};\n',C="undefined"!==typeof self&&self.Blob&&new Blob(["URL.revokeObjectURL(import.meta.url);",F],{type:"text/javascript;charset=utf-8"});function D(t){let e;try{if(e=C&&(self.URL||self.webkitURL).createObjectURL(C),!e)throw"";const i=new Worker(e,{type:"module",name:null==t?void 0:t.name});return i.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(e)}),i}catch(i){return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(F),{type:"module",name:null==t?void 0:t.name})}}class P{constructor(t,e){const{constructor:i,template:n}=t;this.utils=i.utils,this.validator=i.validator,this.$danmuku=n.$danmuku,this.$player=n.$player,this.art=t,this.queue=[],this.$refs=[],this.isStop=!1,this.isHide=!1,this.timer=null,this.index=0,this.worker=null,this.workerClient=null,this.renderer=new $(this),this.scheduler=new S(this),this.option=P.option,this.states={wait:[],ready:[],emit:[],stop:[]},this.start=this.start.bind(this),this.stop=this.stop.bind(this),this.reset=this.reset.bind(this),this.resize=this.resize.bind(this),this.destroy=this.destroy.bind(this),this.seek=this.seek.bind(this),k(this),t.on("destroy",this.destroy);try{if(this.config(e,!0),!k(this))return;this.createWorker()}catch(a){throw t.off("destroy",this.destroy),x(this),this.scheduler.destroy(),a}t.on("video:play",this.start),t.on("video:playing",this.start),t.on("video:pause",this.stop),t.on("video:waiting",this.stop),t.on("resize",this.resize),t.on("video:seeking",this.seek),this.load().catch(t=>console.warn("Failed to load initial danmuku:",t))}static get option(){return{danmuku:[],speed:5,margin:[10,"25%"],opacity:1,color:"#FFFFFF",mode:0,modes:[0,1,2],fontSize:25,antiOverlap:!0,synchronousPlayback:!1,mount:void 0,heatmap:!1,width:512,points:[],filter:()=>!0,beforeEmit:()=>!0,beforeVisible:()=>!0,visible:!0,emitter:!0,maxLength:200,lockTime:5,theme:"dark",OPACITY:{},FONT_SIZE:{},MARGIN:{},SPEED:{},COLOR:[]}}static get scheme(){return{danmuku:"array|function|string|promise",speed:"number",margin:"array",opacity:"number",color:"string",mode:"number",modes:"array",fontSize:"number|string",antiOverlap:"boolean",synchronousPlayback:"boolean",mount:"?htmldivelement|string",heatmap:"object|boolean",width:"number",points:"array",filter:"function",beforeEmit:"function",beforeVisible:"function",visible:"boolean",emitter:"boolean",maxLength:"number",lockTime:"number",theme:"string",OPACITY:"object",FONT_SIZE:"object",MARGIN:"object",SPEED:"object",COLOR:"array"}}static get cssText(){return'\n user-select: none;\n position: absolute;\n white-space: pre;\n pointer-events: none;\n perspective: 500px;\n display: inline-block;\n will-change: transform;\n font-weight: normal;\n line-height: 1.125;\n visibility: hidden;\n font-family: SimHei, "Microsoft JhengHei", Arial, Helvetica, sans-serif;\n text-shadow: rgb(0, 0, 0) 1px 0px 1px, rgb(0, 0, 0) 0px 1px 1px, rgb(0, 0, 0) 0px -1px 1px, rgb(0, 0, 0) -1px 0px 1px;\n '}get isRotate(){var t,e;return null==(e=null==(t=this.art.plugins)?void 0:t.autoOrientation)?void 0:e.state}get marginTop(){const{clamp:t}=this.utils,e=this.option.margin[0],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return P.option.margin[0]}get marginBottom(){const{clamp:t}=this.utils,e=this.option.margin[1],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return P.option.margin[1]}get fontSize(){const{clamp:t}=this.utils,{clientHeight:e}=this.$player,i=this.option.fontSize;if("number"===typeof i)return Math.round(t(i,12,e));if("string"===typeof i&&i.endsWith("%")){const n=Number.parseFloat(i)/100;return Math.round(t(e*n,12,e))}return P.option.fontSize}get $ref(){return this.renderer.acquire()}get readys(){return function(t){const{currentTime:e}=t.art,i=[];return b(t,"ready",t=>i.push(t)),b(t,"wait",t=>{e+.1>=t.time&&t.time>=e-.1&&i.push(t)}),i}(this)}get visibles(){return this.renderer.visibles}get speed(){return this.option.synchronousPlayback&&this.art.playbackRate?this.option.speed/Number(this.art.playbackRate):this.option.speed}load(t){return c(this,null,function*(){const{errorHandle:e}=this.utils,i=function(t,e){const i=w(t);if(e)for(const d of[...i.tasks])d.replace&&d.cancel();let n,a=i.closed||t.art.isDestroy;const s=new Promise(t=>n=t),r=new Set,o="function"===typeof AbortController?new AbortController:void 0,l={replace:e,signal:null==o?void 0:o.signal,active:()=>!a&&!i.closed&&!t.art.isDestroy,onCancel:t=>l.active()?(r.add(t),()=>r.delete(t)):(t(),()=>{}),cancel(){if(!a){a=!0,n(v),null==o||o.abort();for(const t of[...r])t();r.clear()}},wait:t=>Promise.race([t,s]),emit(e){i.emitting.push(l);try{return t.emit(e)}finally{i.emitting.pop()}},finish(){i.tasks.delete(l),r.clear()}};return a?n(v):i.tasks.add(l),l}(this,void 0===t);try{if(!i.active())return this;const n=function(t,e){return"function"===typeof t?{asynchronous:!0,value:t()}:p(t)?{asynchronous:!0,value:t}:"string"===typeof t?{asynchronous:!0,value:g(t,e)}:{asynchronous:!1,value:Array.isArray(t)?t:[]}}(t||this.option.danmuku,i),a=n.asynchronous?yield i.wait(n.value):n.value;if(!i.active())return this;e(Array.isArray(a),"Danmuku need return an array as result");const s=a;if(void 0===t){if(this.reset(),!i.active())return this;this.queue=[],this.states={wait:[],ready:[],emit:[],stop:[]},this.renderer.clear()}for(let t=0;t{const e=a[t],i=s[t];return"function"===typeof e||"function"===typeof i||p(e)||p(i)?e!==i:JSON.stringify(e)!==JSON.stringify(i)}))&&!e)return this;const r=function(t,e,{defaults:i,validate:n,clamp:a,mount:s}){const r=Object.assign({},i,t,e);return n(r),r.mode=a(r.mode,0,2),r.speed=a(r.speed,1,10),r.opacity=a(r.opacity,0,1),r.lockTime=a(r.lockTime,1,60),r.maxLength=a(r.maxLength,1,1e3),r.mount=r.mount||s,r}(this.option,t,{defaults:P.option,validate:t=>this.validator(t,P.scheme),clamp:i,mount:n});if(!k(this))return this;const o=this.option.beforeVisible!==r.beforeVisible;return this.option=r,o&&(this.scheduler.invalidate(),this.scheduler.schedule()),t.fontSize&&(this.reset(),!k(this))||(this.option.visible?this.show():this.hide(),k(this)&&this.art.emit("artplayerPluginDanmuku:config",this.option)),this}getLeft(t){return this.renderer.left(t)}postMessage(t={}){return this.workerClient.request(t)}createWorker(){this.workerClient=new E(()=>new D,t=>this.scheduler.fail(t)),this.worker=this.workerClient.worker}filter(t,e){return b(this,t,e)}setState(t,e){!function(t,e,i){t.states[e.$state]=t.states[e.$state].filter(t=>t!==e),e.$state=i,e.$ref&&(e.$ref.dataset.state=i),t.states[i].push(e)}(this,t,e)}makeWait(t){this.renderer.makeWait(t)}update(){return this.scheduler.running=!0,this.scheduler.schedule(),this}resize(){this.renderer.resize()}continue(){return this.renderer.continue(),this}suspend(){return this.renderer.suspend(),this}stop(){return this.isStop=!0,this.scheduler.running=!1,this.scheduler.invalidate(),this.suspend(),this.art.emit("artplayerPluginDanmuku:stop"),this}start(){if(this.scheduler.closed||this.art.isDestroy)return this;const t=++this.scheduler.starts,e=this.scheduler.generation;this.isStop=!1,this.scheduler.recover();return this.scheduler.closed||this.art.isDestroy||this.isStop||this.scheduler.fault||e!==this.scheduler.generation||t!==this.scheduler.starts||(this.continue(),this.update(),this.art.emit("artplayerPluginDanmuku:start")),this}reset(){return this.scheduler.invalidate(),this.queue.forEach(t=>this.makeWait(t)),this.art.emit("artplayerPluginDanmuku:reset"),this.scheduler.recover(),this.scheduler.schedule(),this}seek(){this.scheduler.invalidate(),this.scheduler.schedule()}show(){return this.isHide=!1,this.$danmuku.style.opacity=1,this.option.visible=!0,this.art.emit("artplayerPluginDanmuku:show"),this}hide(){return this.isHide=!0,this.scheduler.invalidate(),this.$danmuku.style.opacity=0,this.option.visible=!1,this.art.emit("artplayerPluginDanmuku:hide"),this.scheduler.schedule(),this}destroy(){var t;x(this),this.scheduler.destroy();try{try{this.stop()}finally{null==(t=this.workerClient)||t.dispose()}}finally{this.renderer.destroy(),this.art.off("video:play",this.start),this.art.off("video:playing",this.start),this.art.off("video:pause",this.stop),this.art.off("video:waiting",this.stop),this.art.off("resize",this.resize),this.art.off("video:seeking",this.seek),this.art.off("destroy",this.destroy),this.art.emit("artplayerPluginDanmuku:destroy")}}}const T=4294967295;function A(t,e){let i=0,n=t.length;for(;iNumber.isFinite(t)&&t>0))return null;const o={xMin:0,xMax:t,yMin:0,yMax:128,scale:.25,opacity:.2,minHeight:Math.floor(.05*e),sampling:Math.max(1,Math.floor(t/100)),smoothing:.2,flattening:.2},l=Object.assign({},"object"===typeof a?a:{}),d=Object.assign({},o,l);for(const r of Object.keys(o))Number.isFinite(d[r])||(d[r]=o[r]);if(d.sampling<=0&&(d.sampling=o.sampling),d.xMin===d.xMax||d.yMin===d.yMax)return null;const c=Array.isArray(s)&&s.length>0,p=c?[...s]:function(t,e,i,n){if(!Number.isFinite(e)||e<=0||!Number.isFinite(i)||i<=0||!Number.isFinite(n)||n<=0||Math.floor(e/n)+1>T)return[];const a=t.map(t=>t.time).filter(t=>"number"===typeof t&&!Number.isNaN(t)).sort((t,e)=>t-e),s=[],r=i/e;if(!Number.isFinite(r))return[];for(let o=0;o<=e;o+=n){if(s.length>=T||o+n===o)return[];const t=o*r,e=(o+n)*r;s.push([o,A(a,e)-A(a,t)])}return s}(n,t,i,d.sampling);if(!p.length||!p.every(t=>Array.isArray(t)&&Number.isFinite(t[0])&&Number.isFinite(t[1])))return null;const h=p[p.length-1];if(h[0]!==t){if(p.length===T)return null;p.push([t,h[1]])}let u=1/0,m=-1/0;for(const r of p)u=Math.min(u,r[1]),m=Math.max(m,r[1]);const f=(u+m)/2;for(const r of p){const t=r[1];r[1]=t*(t>f?1+d.scale:1-d.scale)+d.minHeight}const g=!c&&!Number.isFinite(l.yMin)&&!Number.isFinite(l.yMax);let y=0;for(const r of p)y=Math.max(y,r[1]);const v=g&&y>o.yMax/4;v&&(d.yMax=4*y);const w=t=>v?Math.min(e,Math.max(.75*e,t)):t,k=(t,e,i,n)=>{const a=function(t,e){const i=e[0]-t[0],n=e[1]-t[1];return{length:Math.sqrt(r(i,2)+r(n,2)),angle:Math.atan2(n,i)}}(e||t,i||t),s=L(Math.cos(a.angle)*d.flattening,0,1,1,0),o=a.angle*s+(n?Math.PI:0),l=a.length*d.smoothing;return[t[0]+Math.cos(o)*l,w(t[1]+Math.sin(o)*l)]},x=p.map(i=>[L(i[0],d.xMin,d.xMax,0,t),w(L(i[1],d.yMin,d.yMax,e,0))]);if(!x.every(t=>t.every(Number.isFinite)))return null;const b=x.map((t,i,n)=>{if(0===i)return`M ${n[n.length-1][0]},${e} L ${t[0]},${e} L ${t[0]},${t[1]}`;const a=k(n[i-1],n[i-2],t),s=k(t,n[i-1],n[i+1],!0);return`C ${a[0]},${a[1]} ${s[0]},${s[1]} ${t[0]},${t[1]}${i===n.length-1?" z":""}`}).join(" ");return/NaN|Infinity/u.test(b)?null:{path:b,opacity:d.opacity,width:t,height:e}}let H=0;function N(t,e,i){if(t.isDestroy)return;const{query:n}=t.constructor.utils;let a;const s=[];let r,o,l,d=!1;const c=()=>!d&&!t.isDestroy;function p(e,i){const n=i;s.push([e,n]),t.on(e,n)}function h(e){if(d)return;let i;d=!0,o=null,l=null;let n=!1;const a=t=>{try{t()}catch(e){n||(i=e),n=!0}};for(const[r,o]of s.splice(0))a(()=>t.off(r,o));if(e&&r&&t.controls.heatmap===r&&a(()=>t.controls.remove("heatmap")),r=void 0,n)throw i}function u(t){c()&&o&&l&&(o.setAttribute("offset",100*t+"%"),l.setAttribute("offset",100*t+"%"))}function m(s){try{!function(s=[]){if(!c())return;o=null,l=null;const d=r;if(d.innerHTML="",t.option.isLive)return;const p=O({width:d.offsetWidth,height:d.offsetHeight,duration:t.duration,queue:e.queue,option:i,points:s});if(!p||!c())return;const h=d.ownerDocument;for(a||(a=h?"heatmap-solids":"heatmap-solids-"+ ++H);null==h?void 0:h.getElementById(a);)a="heatmap-solids-"+ ++H;d.innerHTML=`\n \n \n \n \n \n \n \n \n \n \n \n `,o=n("#heatmap-start",d),l=n("#heatmap-stop",d),u(t.played)}(s)}catch(d){try{h(!0)}catch(p){}throw d}}p("destroy",()=>h(!0));try{t.controls.add({name:"heatmap",position:"top",html:"",style:{position:"absolute",top:"-100px",left:"0px",right:"0px",height:"100px",width:"100%",pointerEvents:"none"},beforeUnmount(){h(!1)},mounted(e){if(r=e,d)return t.controls.heatmap===e&&t.controls.remove("heatmap"),void(r=void 0);c()?(p("video:timeupdate",()=>u(t.played)),p("setBar",(t,e)=>{"played"===t&&u(e)}),p("ready",()=>m()),p("resize",()=>m()),p("artplayerPluginDanmuku:loaded",()=>m()),p("artplayerPluginDanmuku:points",t=>m(t))):h(!0)}})}catch(f){try{h(!0)}catch(g){}throw f}return()=>h(!0)}const V=new WeakMap;class j{constructor(t){this.art=t,this.closed=!1,this.disposers=new Set,this.cancelled=new Promise(t=>this.cancel=t)}get active(){return!this.closed&&!this.art.isDestroy}own(t){this.closed?t():this.disposers.add(t)}write(t,e,i,n=i=>t[e]=i){const a=t;if(!this.active)return;const s=()=>"function"===typeof a.getPropertyPriority?a.getPropertyPriority(e):void 0;let r=V.get(a);r||(r=new Map,V.set(a,r));let o=r.get(e);o||(o={original:a[e],present:e in a||void 0!==a[e],priority:s(),owners:new Map,apply:n},r.set(e,o)),o.owners.has(this)||this.own(()=>{const t=[...o.owners.keys()],i=t[t.length-1]===this;if(o.owners.delete(this),i&&a[e]===o.last&&s()===o.lastPriority){const t=[...o.owners.values()];t.length?(o.apply(t[t.length-1]),o.last=a[e],o.lastPriority=s()):void 0!==o.priority?a.setProperty(e,o.original,o.priority):o.present?o.apply(o.original):delete a[e]}o.owners.size||(r.delete(e),r.size||V.delete(a))}),o.owners.delete(this),o.owners.set(this,i),o.apply(i),o.last=a[e],o.lastPriority=s()}on(t,e){if(!this.active)return;const i=this;function n(...t){if(i.active)return e.apply(this,t)}const a=()=>this.art.off(t,n);this.own(a);try{this.art.on(t,n),this.active||a()}catch(s){throw this.release(a),s}}proxy(t,e,i){if(!this.active)return;const n=this;function a(...t){if(n.active)return i.apply(this,t)}let s;const r=()=>{var i,n;if("function"===typeof s){const t=this.art.events;"function"===typeof(null==t?void 0:t.remove)&&(null==(i=t.destroyEvents)?void 0:i.has(s))?t.remove(s):s()}else null==(n=t.removeEventListener)||n.call(t,e,a)};this.own(r);try{s=this.art.proxy(t,e,a),this.active||r()}catch(o){throw this.release(r),o}}wait(t){return Promise.race([t,this.cancelled])}release(t){try{t()}catch(e){console.warn("Failed to dispose danmuku setting resource:",e)}}close(){if(!this.closed){this.closed=!0,this.cancel();for(const t of this.disposers)this.release(t);this.disposers.clear()}}}function q({min:t,max:e,container:i,findIndex:n,onChange:a,steps:s=[]}){const{lifecycle:r}=this;if(!r.active)return{reset(){}};const{query:o,clamp:l,setStyle:d}=this.utils;d(i,"touch-action","none"),i.innerHTML=`\n

\n
\n ${s.map(()=>'
').join("")}\n
\n
\n
\n
\n
\n ${s.map(t=>t.hide?"":`
${t.name}
`).join("")}\n
\n `;const c=o(".apd-slider-dot",i),p=o(".apd-slider-progress",i);let h=!1;function u(i){if(!r.active)return;if(void 0===i&&(i=n()),ie)return;const o=(i-t)/(e-t);c.style.left=100*o+"%",0===s.length&&(p.style.width=c.style.left),a(i)}function m(n){const{top:a,height:s,left:r,width:o}=i.getBoundingClientRect();if(this.art.isRotate){const i=l(n.clientY-a,0,s);u(Math.round(i/s*(e-t)+t))}else{const i=l(n.clientX-r,0,o);u(Math.round(i/o*(e-t)+t))}}return r.own(()=>h=!1),r.proxy(i,"click",t=>{m.call(this,t)}),r.proxy(i,"pointerdown",t=>{h=0===t.button}),r.on("document:pointermove",t=>{h&&m.call(this,t)}),r.on("document:pointerup",t=>{h&&(h=!1,m.call(this,t))}),{reset:u}}const I='',R='',B='',Z='',W='',_='',U='',G='',X='',Y='',J='',K='';function Q(){const{option:t}=this;return`\n
\n ${J}${Y}\n
\n
\n ${B}\n
\n
\n
\n \u6309\u7c7b\u578b\u5c4f\u853d\n
\n
\n ${Z}${W}\n
\u6eda\u52a8
\n
\n
\n ${_}${U}\n
\u9876\u90e8
\n
\n
\n ${G}${X}\n
\u5e95\u90e8
\n
\n
\n
\n
\n
\n ${R}${I}\n \u9632\u6b62\u5f39\u5e55\u91cd\u53e0\n
\n
\n ${R}${I}\n \u540c\u6b65\u89c6\u9891\u901f\u5ea6\n
\n
\n
\n \u4e0d\u900f\u660e\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n \u663e\u793a\u533a\u57df\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u5b57\u53f7\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u901f\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n
\n
\n
\n
\n ${K}\n
\n
\n
\n \u6a21\u5f0f\n
\n
\n ${W}\n
\u6eda\u52a8
\n
\n
\n ${U}\n
\u9876\u90e8
\n
\n
\n ${X}\n
\u5e95\u90e8
\n
\n
\n
\n
\n \u989c\u8272\n
\n ${this.COLOR.map(t=>`
`).join("")}\n
\n
\n
\n
\n
\n \n
\u53d1\u9001
\n
\n `}if("undefined"!==typeof document){const t="artplayer-plugin-danmuku",e=Symbol.for("artplayer-plugin-danmuku.pending-style"),i=document;let n=document.getElementById(t)||i[e];if(!n)if(n=document.createElement("style"),n.id=t,"loading"===document.readyState){i[e]=n;const a=()=>{document.removeEventListener("DOMContentLoaded",a),i[e]===n&&delete i[e];const s=document.getElementById(t);s?s.textContent=n.textContent:(document.head||document.documentElement).appendChild(n)};document.addEventListener("DOMContentLoaded",a,{once:!0})}else(document.head||document.documentElement).appendChild(n);n.textContent='.artplayer-plugin-danmuku{display:flex;position:relative;z-index:99;align-items:center;justify-content:center;font-size:12px;height:32px;width:100%;color:#fff;font-weight:300;flex-shrink:0;gap:10px}.artplayer-plugin-danmuku .apd-icon{cursor:pointer;opacity:.75;transition:all .2s ease;fill:#fff}.artplayer-plugin-danmuku .apd-icon:hover{opacity:1}.artplayer-plugin-danmuku .apd-config{display:flex;position:relative}.artplayer-plugin-danmuku .apd-config .apd-config-panel{position:absolute;bottom:24px;left:0;width:320px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-config .apd-config-panel .apd-config-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-config:hover .apd-config-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-config-mode,.artplayer-plugin-danmuku .apd-config-slider,.artplayer-plugin-danmuku .apd-config-other,.artplayer-plugin-danmuku .apd-style-mode{margin-bottom:15px}.artplayer-plugin-danmuku .apd-modes{display:flex;align-items:center;margin-top:5px;gap:20px}.artplayer-plugin-danmuku .apd-modes .apd-mode{cursor:pointer;text-align:center}.artplayer-plugin-danmuku .apd-modes .apd-mode:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-config-slider{display:flex;align-items:center;gap:12px}.artplayer-plugin-danmuku .apd-config-slider .apd-value{width:32px;text-align:right}.artplayer-plugin-danmuku .apd-slider{position:relative;flex:1;display:flex;height:20px;align-items:center;justify-content:center;cursor:pointer}.artplayer-plugin-danmuku .apd-slider .apd-slider-line{position:relative;height:2px;width:100%;overflow:hidden;border-radius:3px;background-color:#ffffff40}.artplayer-plugin-danmuku .apd-slider .apd-slider-points{position:absolute;inset:0;display:flex;align-items:center;justify-content:space-between}.artplayer-plugin-danmuku .apd-slider .apd-slider-points .apd-slider-point{width:2px;height:2px;border-radius:50%;background-color:#ffffff80}.artplayer-plugin-danmuku .apd-slider .apd-slider-progress{width:0%;height:100%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-dot{position:absolute;transform:translate(-6px);left:0%;width:12px;height:12px;border-radius:50%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps{display:flex;align-items:center;justify-content:space-between;position:absolute;bottom:-12px;width:calc(100% + 32px);color:#777}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps .apd-slider-step{flex-shrink:0;width:36px;text-align:center;scale:.95}.artplayer-plugin-danmuku .apd-config-other{display:flex;align-items:center;gap:20px}.artplayer-plugin-danmuku .apd-config-other .apd-check-off,.artplayer-plugin-danmuku .apd-config-other .apd-check-on{width:16px;height:16px}.artplayer-plugin-danmuku .apd-config-other .apd-other{display:flex;align-items:center;cursor:pointer;gap:2px}.artplayer-plugin-danmuku .apd-config-other .apd-other:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-emitter{display:flex;flex:1;align-items:center;height:100%;background-color:#ffffff40;border-radius:5px}.artplayer-plugin-danmuku .apd-style{position:relative;display:flex;align-items:center;justify-content:center}.artplayer-plugin-danmuku .apd-style .apd-style-panel{position:absolute;bottom:24px;left:0;width:200px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-style .apd-style-panel .apd-style-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-style:hover .apd-style-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-colors{display:flex;flex-wrap:wrap;margin-top:5px;gap:8px}.artplayer-plugin-danmuku .apd-colors .apd-color{width:16px;height:16px;border-radius:2px;cursor:pointer}.artplayer-plugin-danmuku .apd-colors .apd-color.apd-active{border:1px solid black;box-shadow:0 0 0 1px #fff}.artplayer-plugin-danmuku .apd-input{outline:none;height:100%;flex:1;min-width:0;width:auto;border:none;line-height:1;color:#fff;background-color:transparent}.artplayer-plugin-danmuku .apd-input::placeholder{color:#ffffff80}.artplayer-plugin-danmuku .apd-send{display:flex;align-items:center;justify-content:center;height:100%;width:60px;flex-shrink:0;cursor:pointer;text-shadow:none;border-top-right-radius:5px;border-bottom-right-radius:5px;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-send.apd-lock{cursor:not-allowed;color:#666;background-color:#e7e7e7}.art-controls-center .apd-emitter{width:260px;flex:none}.art-fullscreen .artplayer-plugin-danmuku,.art-fullscreen-web .artplayer-plugin-danmuku{height:38px;gap:16px}.art-fullscreen .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen-web .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-on,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-on{width:28px;height:28px}.art-fullscreen .artplayer-plugin-danmuku .apd-emitter,.art-fullscreen-web .artplayer-plugin-danmuku .apd-emitter{width:400px;flex:none}.art-video-player>.artplayer-plugin-danmuku{position:absolute;left:0;right:0;bottom:-40px;padding:0 10px}.art-video-player:has(>.artplayer-plugin-danmuku){margin-bottom:40px}[data-danmuku-emitter=false] .apd-emitter{display:none!important}[data-danmuku-emitter=false] .art-controls-center .artplayer-plugin-danmuku{justify-content:flex-end;gap:18px}[data-danmuku-emitter=false].art-fullscreen .art-controls-center .artplayer-plugin-danmuku,[data-danmuku-emitter=false].art-fullscreen-web .art-controls-center .artplayer-plugin-danmuku{gap:24px}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-icon{fill:#333}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-emitter{background-color:#f1f2f3}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input{color:#000}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input::placeholder{color:#0000004d}[data-danmuku-visible=false] .apd-toggle-off{display:block}[data-danmuku-visible=false] .apd-toggle-on,[data-danmuku-visible=true] .apd-toggle-off{display:none}[data-danmuku-visible=true] .apd-toggle-on{display:block}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-on{display:none}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-off,[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-on{display:block}[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-off,[data-danmuku-sync-video=false] .apd-sync-video .apd-check-on{display:none}[data-danmuku-sync-video=false] .apd-sync-video .apd-check-off,[data-danmuku-sync-video=true] .apd-sync-video .apd-check-on{display:block}[data-danmuku-sync-video=true] .apd-sync-video .apd-check-off{display:none}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-off{display:block}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-on{display:none}[data-danmuku-mode0=false] .art-danmuku [data-mode="0"]{opacity:0!important}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-off{display:none}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-on{display:block}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"]{color:#00a1d6}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"] path{fill:#00a1d6}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-off{display:block}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-on{display:none}[data-danmuku-mode1=false] .art-danmuku [data-mode="1"]{opacity:0!important}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-off{display:none}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-on{display:block}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"]{color:#00a1d6}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"] path{fill:#00a1d6}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-off{display:block}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-on{display:none}[data-danmuku-mode2=false] .art-danmuku [data-mode="2"]{opacity:0!important}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-off{display:none}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-on{display:block}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"]{color:#00a1d6}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"] path{fill:#00a1d6}'}class tt{constructor(t,e){this.art=t,this.danmuku=e,this.utils=t.constructor.utils;const{setStyle:i}=this.utils,{$controlsCenter:n}=t.template;this.template={$controlsCenter:n,$mount:n,$danmuku:null,$toggle:null,$config:null,$configPanel:null,$configModes:null,$style:null,$stylePanel:null,$styleModes:null,$colors:null,$opacitySlider:null,$opacityValue:null,$marginSlider:null,$marginValue:null,$fontSizeSlider:null,$fontSizeValue:null,$speedSlider:null,$speedValue:null,$input:null,$send:null},this.slider={opacity:null,margin:null,fontSize:null,speed:null},this.emitting=!1,this.isLock=!1,this.timer=null,this.lifecycle=new j(t),this.destroy=this.destroy.bind(this),this.lifecycle.own(()=>t.off("destroy",this.destroy));try{if(t.on("destroy",this.destroy),!this.lifecycle.active)return;if(this.lifecycle.write(n.style,"display","flex",t=>i(n,"display",t)),this.createTemplate(),!this.lifecycle.active)return;if(this.createSliders(),!this.lifecycle.active)return;if(this.createEvents(),this.mount(this.option.mount),!this.lifecycle.active)return;this.lifecycle.on("resize",()=>this.resize()),this.lifecycle.on("fullscreen",t=>this.onFullscreen(t)),this.lifecycle.on("fullscreenWeb",t=>this.onFullscreen(t)),this.lifecycle.proxy(this.template.$config,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$config,$panel:this.template.$configPanel})}),this.lifecycle.proxy(this.template.$style,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$style,$panel:this.template.$stylePanel})})}catch(a){try{this.destroy()}catch(s){console.warn("Failed to roll back danmuku setting:",s)}throw a}}static get icons(){return{$on:J,$off:Y,$config:B,$style:K,$mode_0_off:Z,$mode_0_on:W,$mode_1_off:_,$mode_1_on:U,$mode_2_off:G,$mode_2_on:X,$check_on:R,$check_off:I}}get option(){return this.danmuku.option}get outside(){return this.template.$mount!==this.template.$controlsCenter}get TEMPLATE(){return Q.call(this)}get OPACITY(){return l({min:0,max:100,steps:[]},this.option.OPACITY)}get FONT_SIZE(){return l({min:12,max:120,steps:[]},this.option.FONT_SIZE)}get MARGIN(){return l({min:0,max:3,steps:[{name:"1/4",value:[10,"75%"]},{name:"\u534a\u5c4f",value:[10,"50%"]},{name:"3/4",value:[10,"25%"]},{name:"\u6ee1\u5c4f",value:[10,10]}]},this.option.MARGIN)}get SPEED(){return l({min:0,max:4,steps:[{name:"\u6781\u6162",value:10},{name:"\u8f83\u6162",value:7.5,hide:!0},{name:"\u9002\u4e2d",value:5},{name:"\u8f83\u5feb",value:2.5,hide:!0},{name:"\u6781\u5feb",value:1}]},this.option.SPEED)}get COLOR(){return this.option.COLOR.length?this.option.COLOR:["#FE0302","#FF7204","#FFAA02","#FFD302","#FFFF00","#A0EE00","#00CD00","#019899","#4266BE","#89D5FF","#CC0273","#222222","#9B9B9B","#FFFFFF"]}query(t){const{query:e}=this.utils,{$danmuku:i}=this.template;return e(t,i)}append(t,e){if(!this.lifecycle.active)return;const{append:i}=this.utils;[...t.children].includes(e)||i(t,e)}setData(t,e){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,{$mount:n}=this.template;this.lifecycle.write(i.dataset,t,e),this.outside&&this.lifecycle.write(n.dataset,t,e)}createTemplate(){const{createElement:t,tooltip:e}=this.utils,i=t("div");this.template.$danmuku=i,i.className="artplayer-plugin-danmuku",i.innerHTML=this.TEMPLATE,this.template.$toggle=this.query(".apd-toggle"),this.template.$config=this.query(".apd-config"),this.template.$configPanel=this.query(".apd-config-panel"),this.template.$configModes=this.query(".apd-config-mode .apd-modes"),this.template.$style=this.query(".apd-style"),this.template.$stylePanel=this.query(".apd-style-panel"),this.template.$styleModes=this.query(".apd-style-mode .apd-modes"),this.template.$colors=this.query(".apd-colors"),this.template.$antiOverlap=this.query(".apd-anti-overlap"),this.template.$syncVideo=this.query(".apd-sync-video"),this.template.$opacitySlider=this.query(".apd-config-opacity .apd-slider"),this.template.$opacityValue=this.query(".apd-config-opacity .apd-value"),this.template.$marginSlider=this.query(".apd-config-margin .apd-slider"),this.template.$marginValue=this.query(".apd-config-margin .apd-value"),this.template.$fontSizeSlider=this.query(".apd-config-fontSize .apd-slider"),this.template.$fontSizeValue=this.query(".apd-config-fontSize .apd-value"),this.template.$speedSlider=this.query(".apd-config-speed .apd-slider"),this.template.$speedValue=this.query(".apd-config-speed .apd-value"),this.template.$input=this.query(".apd-input"),this.template.$send=this.query(".apd-send");const{$toggle:n}=this.template;this.lifecycle.on("artplayerPluginDanmuku:show",()=>{e(n,"\u5173\u95ed\u5f39\u5e55")}),this.lifecycle.on("artplayerPluginDanmuku:hide",()=>{e(n,"\u6253\u5f00\u5f39\u5e55")})}createEvents(){const{$toggle:t,$configModes:e,$styleModes:i,$colors:n,$antiOverlap:a,$syncVideo:s,$send:r,$input:o}=this.template;this.lifecycle.proxy(t,"click",()=>{this.danmuku.config({visible:!this.option.visible}),this.reset()}),this.lifecycle.proxy(e,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.option.modes.includes(i)?this.danmuku.config({modes:this.option.modes.filter(t=>t!==i)}):this.danmuku.config({modes:[...this.option.modes,i]}),this.reset()}),this.lifecycle.proxy(a,"click",()=>{this.danmuku.config({antiOverlap:!this.option.antiOverlap}),this.reset()}),this.lifecycle.proxy(s,"click",()=>{this.danmuku.config({synchronousPlayback:!this.option.synchronousPlayback}),this.reset()}),this.lifecycle.proxy(i,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.danmuku.config({mode:i}),this.reset()}),this.lifecycle.proxy(n,"click",t=>{const e=t.target.closest(".apd-color");e&&(this.danmuku.config({color:e.dataset.color}),this.reset())}),this.lifecycle.proxy(r,"click",()=>this.emit()),this.lifecycle.proxy(o,"keypress",t=>{"Enter"===t.key&&(t.preventDefault(),this.emit())})}createSliders(){this.slider.opacity=this.createSlider(d(l({},this.OPACITY),{container:this.template.$opacitySlider,findIndex:()=>Math.round(100*this.option.opacity),onChange:t=>{const{$opacityValue:e}=this.template;e.textContent=`${t}%`,this.danmuku.config({opacity:t/100})}})),this.slider.margin=this.createSlider(d(l({},this.MARGIN),{container:this.template.$marginSlider,findIndex:()=>this.MARGIN.steps.findIndex(t=>t.value[0]===this.option.margin[0]&&t.value[1]===this.option.margin[1]),onChange:t=>{const e=this.MARGIN.steps[t];if(!e)return;const{$marginValue:i}=this.template;i.textContent=e.name,this.danmuku.config({margin:e.value})}})),this.slider.fontSize=this.createSlider(d(l({},this.FONT_SIZE),{container:this.template.$fontSizeSlider,findIndex:()=>this.danmuku.fontSize,onChange:t=>{const{$fontSizeValue:e}=this.template;e.textContent=`${t}px`,t!==this.danmuku.fontSize&&this.danmuku.config({fontSize:t})}})),this.slider.speed=this.createSlider(d(l({},this.SPEED),{container:this.template.$speedSlider,findIndex:()=>this.SPEED.steps.findIndex(t=>t.value===this.option.speed),onChange:t=>{const e=this.SPEED.steps[t];if(!e)return;const{$speedValue:i}=this.template;i.textContent=e.name,this.danmuku.config({speed:e.value})}}))}createSlider(t){return q.call(this,t)}onFullscreen(t){if(!this.lifecycle.active)return;const{$danmuku:e,$controlsCenter:i,$mount:n}=this.template;this.outside?t?this.append(i,e):this.append(n,e):this.append(i,e)}onMouseEnter({$control:t,$panel:e}){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,n=t.getBoundingClientRect(),a=e.getBoundingClientRect(),s=i.getBoundingClientRect(),r=a.width/2-n.width/2,o=s.left-(n.left-r),l=n.right+r-s.right;e.style.left=o>0?`${-r+o}px`:l>0?-r-l+"px":-r+"px"}emit(){return function(t){return c(this,null,function*(){const{lifecycle:e,template:{$input:i}}=t;if(!e.active)return;const n=i.value.trim();if(!n.length||t.isLock||t.emitting)return;const a={text:n,mode:t.option.mode,color:t.option.color,time:t.art.currentTime},s=t=>{e.active&&console.error("Error emitting danmuku:",t)};try{t.emitting=!0;const n=yield e.wait(t.option.beforeEmit(a));if(!e.active)return;if(t.emitting=!1,!0!==n)return;if(a.border=!0,delete a.time,Promise.resolve(t.danmuku.emit(a)).catch(s),!e.active)return;i.value="",t.lock()}catch(r){s(r),e.active&&(t.emitting=!1)}})}(this)}lock(){!function(t){if(!t.lifecycle.active)return;const{addClass:e}=t.utils,{$send:i}=t.template;clearTimeout(t.timer),t.isLock=!0;let n=t.option.lockTime;i.textContent=n,e(i,"apd-lock");const a=()=>{t.timer=setTimeout(()=>{t.lifecycle.active&&(0===n?t.unlock():(n-=1,i.textContent=n,a()))},1e3)};a()}(this)}unlock(){!function(t){if(clearTimeout(t.timer),t.timer=null,t.isLock=!1,!t.lifecycle.active)return;const{removeClass:e}=t.utils,{$send:i}=t.template;i.textContent="\u53d1\u9001",e(i,"apd-lock")}(this)}resize(){if(!this.lifecycle.active)return;if(this.outside)return;if(this.art.fullscreen)return;if(this.art.fullscreenWeb)return;const{$player:t,$controlsCenter:e}=this.art.template,{$danmuku:i}=this.template;this.art.widtht.dataset.color===this.option.color.toUpperCase());s&&t(s,"apd-active"),e(i,this.option.visible?"\u5173\u95ed\u5f39\u5e55":"\u6253\u5f00\u5f39\u5e55"),this.resize()}mount(t){if(!this.lifecycle.active)return;const{errorHandle:e}=this.utils,i="string"===typeof t?document.querySelector(t):t;e(i,`Can not find the mount point: ${t}`),this.append(i,this.template.$danmuku),this.template.$mount=i,this.reset()}destroy(){if(this.lifecycle.closed)return;this.lifecycle.close(),clearTimeout(this.timer),this.timer=null,this.emitting=!1,this.isLock=!1;const t=this.template.$danmuku;(null==t?void 0:t.parentElement)&&t.parentElement.removeChild(t)}}function et(t){return e=>{const i=new P(e,t);let n;try{n=e.isDestroy?void 0:new tt(e,i),!e.isDestroy&&i.option.heatmap&&N(e,i,i.option.heatmap)}catch(a){try{null==n||n.destroy()}catch(s){}try{i.destroy()}catch(s){}throw a}return{name:"artplayerPluginDanmuku",emit:i.emit.bind(i),load:i.load.bind(i),config:i.config.bind(i),hide:i.hide.bind(i),show:i.show.bind(i),reset:i.reset.bind(i),mount:n?n.mount.bind(n):()=>{},get option(){return i.option},get isHide(){return i.isHide},get isStop(){return i.isStop}}}}return et.icons=tt.icons,et}); diff --git a/docs/compiled/artplayer-plugin-danmuku.mjs b/docs/compiled/artplayer-plugin-danmuku.mjs index f92026e90..560c1e7e2 100644 --- a/docs/compiled/artplayer-plugin-danmuku.mjs +++ b/docs/compiled/artplayer-plugin-danmuku.mjs @@ -665,16 +665,18 @@ class Scheduler { danmu.$lastStartTime = Date.now(); danmu.$restTime = owner.speed; const distance = clientWidth + ref.clientWidth; - const { result: top } = await operation.wait(owner.postMessage({ + const reply = await operation.wait(owner.postMessage({ type: "getDanmuTop", target: { mode: danmu.mode, height: ref.clientHeight, speed: distance / danmu.$restTime }, visibles: owner.visibles, antiOverlap: owner.option.antiOverlap, clientWidth, clientHeight, + // Valid margins are numeric. Preserve the old raw fallback for unsupported strings. marginBottom: owner.marginBottom, marginTop: owner.marginTop })); + const top = typeof reply === "symbol" ? void 0 : reply.result; if (!this.active(operation) || danmu.$ref !== ref) return; if (top !== void 0) { @@ -948,6 +950,7 @@ class Danmuku { if (!task.active()) return this; errorHandle(Array.isArray(danmus), "Danmuku need return an array as result"); + const rows = danmus; if (danmuku === void 0) { this.reset(); if (!task.active()) @@ -956,10 +959,10 @@ class Danmuku { this.states = { wait: [], ready: [], emit: [], stop: [] }; this.renderer.clear(); } - for (let index = 0; index < danmus.length; index++) { + for (let index = 0; index < rows.length; index++) { if (!task.active()) return this; - const danmu = danmus[index]; + const danmu = rows[index]; await task.emit(danmu); } if (task.active()) @@ -1074,6 +1077,7 @@ class Danmuku { return this.renderer.left($ref); } // 复杂运算交给 Web Worker 处理 + // Keep the historical empty default; unknown message types receive no reply. postMessage(message = {}) { return this.workerClient.request(message); } @@ -1312,8 +1316,9 @@ function heatmap(art, danmuku, option) { let closed = false; const active = () => !closed && !art.isDestroy; function listen(name, callback) { - subscriptions.push([name, callback]); - art.on(name, callback); + const listener = callback; + subscriptions.push([name, listener]); + art.on(name, listener); } function dispose(removeControl) { if (closed) @@ -1351,18 +1356,19 @@ function heatmap(art, danmuku, option) { return; start = null; stop = null; - element.innerHTML = ""; + const target = element; + target.innerHTML = ""; if (art.option.isLive) return; - const shape = heatmapGeometry({ width: element.offsetWidth, height: element.offsetHeight, duration: art.duration, queue: danmuku.queue, option, points }); + const shape = heatmapGeometry({ width: target.offsetWidth, height: target.offsetHeight, duration: art.duration, queue: danmuku.queue, option, points }); if (!shape || !active()) return; - const document2 = element.ownerDocument; + const document2 = target.ownerDocument; if (!gradient) gradient = document2 ? "heatmap-solids" : `heatmap-solids-${++nextGradient}`; while (document2?.getElementById(gradient)) gradient = `heatmap-solids-${++nextGradient}`; - element.innerHTML = ` + target.innerHTML = ` @@ -1375,8 +1381,8 @@ function heatmap(art, danmuku, option) { `; - start = query("#heatmap-start", element); - stop = query("#heatmap-stop", element); + start = query("#heatmap-start", target); + stop = query("#heatmap-stop", target); progress(art.played); } function update(points) { @@ -1456,7 +1462,8 @@ class SettingLifecycle { dispose(); else this.disposers.add(dispose); } - write(target, key, value, apply = (value2) => target[key] = value2) { + write(host, key, value, apply = (value2) => host[key] = value2) { + const target = host; if (!this.active) return; const priority = () => typeof target.getPropertyPriority === "function" ? target.getPropertyPriority(key) : void 0; @@ -1847,16 +1854,17 @@ const style = ".artplayer-plugin-danmuku {\n display: flex;\n position: relati if (typeof document !== "undefined") { const id = "artplayer-plugin-danmuku"; const pending = /* @__PURE__ */ Symbol.for("artplayer-plugin-danmuku.pending-style"); - let $style2 = document.getElementById(id) || document[pending]; + const styleDocument = document; + let $style2 = document.getElementById(id) || styleDocument[pending]; if (!$style2) { $style2 = document.createElement("style"); $style2.id = id; if (document.readyState === "loading") { - document[pending] = $style2; + styleDocument[pending] = $style2; const ready = () => { document.removeEventListener("DOMContentLoaded", ready); - if (document[pending] === $style2) - delete document[pending]; + if (styleDocument[pending] === $style2) + delete styleDocument[pending]; const existing = document.getElementById(id); if (existing) existing.textContent = $style2.textContent; diff --git a/docs/uncompiled/artplayer-plugin-danmuku/index.js b/docs/uncompiled/artplayer-plugin-danmuku/index.js index e72a0e079..60777ec88 100644 --- a/docs/uncompiled/artplayer-plugin-danmuku/index.js +++ b/docs/uncompiled/artplayer-plugin-danmuku/index.js @@ -661,16 +661,18 @@ var artplayerPluginDanmuku = (function() { danmu.$lastStartTime = Date.now(); danmu.$restTime = owner.speed; const distance = clientWidth + ref.clientWidth; - const { result: top } = await operation.wait(owner.postMessage({ + const reply = await operation.wait(owner.postMessage({ type: "getDanmuTop", target: { mode: danmu.mode, height: ref.clientHeight, speed: distance / danmu.$restTime }, visibles: owner.visibles, antiOverlap: owner.option.antiOverlap, clientWidth, clientHeight, + // Valid margins are numeric. Preserve the old raw fallback for unsupported strings. marginBottom: owner.marginBottom, marginTop: owner.marginTop })); + const top = typeof reply === "symbol" ? void 0 : reply.result; if (!this.active(operation) || danmu.$ref !== ref) return; if (top !== void 0) { @@ -944,6 +946,7 @@ var artplayerPluginDanmuku = (function() { if (!task.active()) return this; errorHandle(Array.isArray(danmus), "Danmuku need return an array as result"); + const rows = danmus; if (danmuku === void 0) { this.reset(); if (!task.active()) @@ -952,10 +955,10 @@ var artplayerPluginDanmuku = (function() { this.states = { wait: [], ready: [], emit: [], stop: [] }; this.renderer.clear(); } - for (let index = 0; index < danmus.length; index++) { + for (let index = 0; index < rows.length; index++) { if (!task.active()) return this; - const danmu = danmus[index]; + const danmu = rows[index]; await task.emit(danmu); } if (task.active()) @@ -1070,6 +1073,7 @@ var artplayerPluginDanmuku = (function() { return this.renderer.left($ref); } // 复杂运算交给 Web Worker 处理 + // Keep the historical empty default; unknown message types receive no reply. postMessage(message = {}) { return this.workerClient.request(message); } @@ -1235,7 +1239,8 @@ var artplayerPluginDanmuku = (function() { smoothing: 0.2, flattening: 0.2 }; - const options = Object.assign({}, defaults, typeof option === "object" ? option : {}); + const configured = Object.assign({}, typeof option === "object" ? option : {}); + const options = Object.assign({}, defaults, configured); for (const key of Object.keys(defaults)) { if (!Number.isFinite(options[key])) options[key] = defaults[key]; @@ -1244,7 +1249,8 @@ var artplayerPluginDanmuku = (function() { options.sampling = defaults.sampling; if (options.xMin === options.xMax || options.yMin === options.yMax) return null; - const points = Array.isArray(input) && input.length ? [...input] : sampleHeatmap(queue, width, duration, options.sampling); + const hasCustomPoints = Array.isArray(input) && input.length > 0; + const points = hasCustomPoints ? [...input] : sampleHeatmap(queue, width, duration, options.sampling); if (!points.length || !points.every((point) => Array.isArray(point) && Number.isFinite(point[0]) && Number.isFinite(point[1]))) { return null; } @@ -1265,14 +1271,21 @@ var artplayerPluginDanmuku = (function() { const y = point[1]; point[1] = y * (y > yMid ? 1 + options.scale : 1 - options.scale) + options.minHeight; } + const automatic = !hasCustomPoints && !Number.isFinite(configured.yMin) && !Number.isFinite(configured.yMax); + let peak = 0; + for (const point of points) peak = Math.max(peak, point[1]); + const fitted = automatic && peak > defaults.yMax / 4; + if (fitted) + options.yMax = peak * 4; + const boundY = (value) => fitted ? Math.min(height, Math.max(height * 0.75, value)) : value; const controlPoint = (current, previous, next, reverse) => { const geometry = line(previous || current, next || current); const flat = map(Math.cos(geometry.angle) * options.flattening, 0, 1, 1, 0); const angle = geometry.angle * flat + (reverse ? Math.PI : 0); const length = geometry.length * options.smoothing; - return [current[0] + Math.cos(angle) * length, current[1] + Math.sin(angle) * length]; + return [current[0] + Math.cos(angle) * length, boundY(current[1] + Math.sin(angle) * length)]; }; - const positions = points.map((point) => [map(point[0], options.xMin, options.xMax, 0, width), map(point[1], options.yMin, options.yMax, height, 0)]); + const positions = points.map((point) => [map(point[0], options.xMin, options.xMax, 0, width), boundY(map(point[1], options.yMin, options.yMax, height, 0))]); if (!positions.every((point) => point.every(Number.isFinite))) return null; const path = positions.map((point, index, all) => { @@ -1299,8 +1312,9 @@ var artplayerPluginDanmuku = (function() { let closed = false; const active = () => !closed && !art.isDestroy; function listen(name, callback) { - subscriptions.push([name, callback]); - art.on(name, callback); + const listener = callback; + subscriptions.push([name, listener]); + art.on(name, listener); } function dispose(removeControl) { if (closed) @@ -1338,18 +1352,19 @@ var artplayerPluginDanmuku = (function() { return; start = null; stop = null; - element.innerHTML = ""; + const target = element; + target.innerHTML = ""; if (art.option.isLive) return; - const shape = heatmapGeometry({ width: element.offsetWidth, height: element.offsetHeight, duration: art.duration, queue: danmuku.queue, option, points }); + const shape = heatmapGeometry({ width: target.offsetWidth, height: target.offsetHeight, duration: art.duration, queue: danmuku.queue, option, points }); if (!shape || !active()) return; - const document2 = element.ownerDocument; + const document2 = target.ownerDocument; if (!gradient) gradient = document2 ? "heatmap-solids" : `heatmap-solids-${++nextGradient}`; while (document2?.getElementById(gradient)) gradient = `heatmap-solids-${++nextGradient}`; - element.innerHTML = ` + target.innerHTML = ` @@ -1362,8 +1377,8 @@ var artplayerPluginDanmuku = (function() { `; - start = query("#heatmap-start", element); - stop = query("#heatmap-stop", element); + start = query("#heatmap-start", target); + stop = query("#heatmap-stop", target); progress(art.played); } function update(points) { @@ -1443,7 +1458,8 @@ var artplayerPluginDanmuku = (function() { dispose(); else this.disposers.add(dispose); } - write(target, key, value, apply = (value2) => target[key] = value2) { + write(host, key, value, apply = (value2) => host[key] = value2) { + const target = host; if (!this.active) return; const priority = () => typeof target.getPropertyPriority === "function" ? target.getPropertyPriority(key) : void 0; @@ -1834,16 +1850,17 @@ var artplayerPluginDanmuku = (function() { if (typeof document !== "undefined") { const id = "artplayer-plugin-danmuku"; const pending = /* @__PURE__ */ Symbol.for("artplayer-plugin-danmuku.pending-style"); - let $style2 = document.getElementById(id) || document[pending]; + const styleDocument = document; + let $style2 = document.getElementById(id) || styleDocument[pending]; if (!$style2) { $style2 = document.createElement("style"); $style2.id = id; if (document.readyState === "loading") { - document[pending] = $style2; + styleDocument[pending] = $style2; const ready = () => { document.removeEventListener("DOMContentLoaded", ready); - if (document[pending] === $style2) - delete document[pending]; + if (styleDocument[pending] === $style2) + delete styleDocument[pending]; const existing = document.getElementById(id); if (existing) existing.textContent = $style2.textContent; diff --git a/package.json b/package.json index d8d78161a..d3859c8cc 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,9 @@ "test:chromecast": "node --test test/chromecast.test.js test/chromecast-failures.test.js test/chromecast-runtime.test.js", "test:chromecast-types-package": "node refactor/scripts/chromecast-package-types.mjs", "test:danmuku-mask": "node --test test/danmuku-mask-failures.test.js refactor/scripts/danmuku-mask-contract.test.mjs", - "test:danmuku": "node --test test/danmuku-setting.test.js test/danmuku-heatmap.test.js test/danmuku-renderer.test.js test/danmuku-scheduler.test.js test/danmuku-worker-client.test.js test/danmuku-input.test.js test/danmuku-parser.test.js test/danmuku-failures.test.js refactor/scripts/danmuku-contract.test.mjs" + "test:danmuku": "node --test test/danmuku-setting.test.js test/danmuku-heatmap.test.js test/danmuku-renderer.test.js test/danmuku-scheduler.test.js test/danmuku-worker-client.test.js test/danmuku-input.test.js test/danmuku-parser.test.js test/danmuku-failures.test.js refactor/scripts/danmuku-contract.test.mjs", + "test:danmuku-types": "node --test refactor/scripts/danmuku-types.test.mjs", + "test:danmuku-types-package": "node refactor/scripts/danmuku-package-types.mjs" }, "browserslist": "last 1 Chrome version", "devDependencies": { diff --git a/packages/artplayer-plugin-danmuku/.npmignore b/packages/artplayer-plugin-danmuku/.npmignore index 4439c6874..2b272d565 100644 --- a/packages/artplayer-plugin-danmuku/.npmignore +++ b/packages/artplayer-plugin-danmuku/.npmignore @@ -2,4 +2,5 @@ node_modules .DS_Store yarn.lock package-lock.json -src \ No newline at end of file +src +tsconfig.json diff --git a/packages/artplayer-plugin-danmuku/ARCHITECTURE.md b/packages/artplayer-plugin-danmuku/ARCHITECTURE.md index 8fe35671f..66631d0e5 100644 --- a/packages/artplayer-plugin-danmuku/ARCHITECTURE.md +++ b/packages/artplayer-plugin-danmuku/ARCHITECTURE.md @@ -2,40 +2,100 @@ The compatibility reference is the actual npm 5.3.0 package. Its archive, historical declarations, earlier exports and failure evidence are indexed in -`refactor/baselines/danmuku-release.json` and `danmuku-contract.md` at the -repository root. Do not infer runtime contracts from the old declaration file. +`refactor/baselines/danmuku-release.json` and +`refactor/baselines/danmuku-contract.md` relative to the repository root. Do not infer runtime contracts from the old declaration file. ## Module responsibilities | Module | Responsibility | | --- | --- | -| `src/index.js` | Plugin factory, facade getters, bound methods, setting and heatmap composition | -| `src/danmuku.js` | Internal instance, method return identity, configuration/load commits, event and lifecycle wiring | -| `src/config.js` | Fresh defaults, validation schema, configuration comparison and normalization | -| `src/input.js` | Input forms, replacement ownership, independent append operations, cancellation and synchronous callback reentry | -| `src/bilibili-parser.js` | Pure legacy-compatible XML fields, mode mapping and entity decoding | -| `src/bilibili.js` | Fetch and response text, one parser Worker per request, fallback, settlement and Blob URL cleanup | -| `src/scheduler.js` | One RAF or asynchronous frame, cancellation generations, preparation ownership and failure recovery | -| `src/renderer.js` | Owned node allocation, preparation, geometry snapshots, placement, pause/resume styling and disposal | -| `src/queue.js` | Ordered state pools, eligibility and state transitions | -| `src/worker-client.js` | Track Worker lifecycle, a shared response dispatcher, unique request IDs and pending requests | -| `src/worker.js` | Track placement Worker; a separate protocol from the XML parser Worker | -| `src/setting.js` | Settings coordinator, configuration controls, mount and fullscreen layout | -| `src/setting-template.js` | Existing template HTML and static icon values | -| `src/setting-slider.js` | Slider index, pointer and rotation behavior | -| `src/setting-send.js` | Pending send, original error outlet and lock countdown | -| `src/setting-lifecycle.js` | Exact subscription/proxy disposal, cancellation and conditional host-property restoration | -| `src/setting-style.js` | Shared document style and one pending DOMContentLoaded installation | -| `src/heatmap.js` | Owned control, progress stops, subscriptions and per-instance gradient | -| `src/heatmap-sampling.js` | Sorted-time bin counts with historical numeric boundaries | -| `src/heatmap-geometry.js` | Legacy point normalization, caller-visible point writes and SVG curve calculation | +| `src/index.ts` | Plugin factory, facade getters, bound methods, setting and heatmap composition | +| `src/danmuku.ts` | Internal instance, method return identity, configuration/load commits, event and lifecycle wiring | +| `src/config.ts` | Fresh defaults, validation schema, configuration comparison and normalization | +| `src/input.ts` | Input forms, replacement ownership, independent append operations, cancellation and synchronous callback reentry | +| `src/bilibili-parser.ts` | Pure legacy-compatible XML fields, mode mapping and entity decoding | +| `src/bilibili.ts` | Fetch and response text, one parser Worker per request, fallback, settlement and Blob URL cleanup | +| `src/scheduler.ts` | One RAF or asynchronous frame, cancellation generations, preparation ownership and failure recovery | +| `src/renderer.ts` | Owned node allocation, preparation, geometry snapshots, placement, pause/resume styling and disposal | +| `src/queue.ts` | Ordered state pools, eligibility and state transitions | +| `src/worker-client.ts` | Track Worker lifecycle, a shared response dispatcher, unique request IDs and pending requests | +| `src/worker.ts` | Track placement Worker; a separate protocol from the XML parser Worker | +| `src/setting.ts` | Settings coordinator, configuration controls, mount and fullscreen layout | +| `src/setting-template.ts` | Existing template HTML and static icon values | +| `src/setting-slider.ts` | Slider index, pointer and rotation behavior | +| `src/setting-send.ts` | Pending send, original error outlet and lock countdown | +| `src/setting-lifecycle.ts` | Exact subscription/proxy disposal, cancellation and conditional host-property restoration | +| `src/setting-style.ts` | Shared document style and one pending DOMContentLoaded installation | +| `src/heatmap.ts` | Owned control, progress stops, subscriptions and per-instance gradient | +| `src/heatmap-sampling.ts` | Sorted-time bin counts with historical numeric boundaries | +| `src/heatmap-geometry.ts` | Legacy point normalization, caller-visible point writes and SVG curve calculation | -The package is undergoing staged migration. PKG-DANMUKU-03 separates input and -configuration responsibilities in JavaScript; PKG-DANMUKU-04 separates scheduling -and track requests; PKG-DANMUKU-05 separates rendering and UI resources; -PKG-DANMUKU-06 converts all owned -modules and Worker messages to TypeScript and validates installed consumers. -This intermediate structure is not the final TypeScript or release acceptance. +All 20 owned runtime modules use TypeScript. Their responsibilities remain +separate from public declaration compatibility and from release acceptance. +The following files contain types only: + +| Module | Responsibility | +| --- | --- | +| `src/types.ts` | Reexports public runtime data types and defines the internal Artplayer host adapter | +| `src/worker-types.ts` | Placement requests, replies, visible rows and virtual boundary rows | +| `src/parser-types.ts` | XML parser results and its independent Worker protocol | +| `src/setting-types.ts` | Settings template slots and slider integration contracts | +| `src/worker-assets.d.ts` | Inline placement Worker module declaration | +| `types/runtime-shared.d.ts` | Single source for accurate option, callback, input, queue, facade, owner and event payload types | + +`src/types.ts` imports/reexports `types/runtime-shared.d.ts` with type-only +syntax. Keep shared option/data definitions there; do not create a second copy +in production source. Host, DOM and Worker adapter types remain internal. The +factory composes Danmuku, Setting and heatmap. Danmuku owns queue/state pools and +coordinates input, renderer and scheduler. Scheduler/renderer refer back to the +owner with type-only imports; these references add no runtime module cycle. +The XML parser and heatmap sampling/geometry remain independently testable. + +## Declaration entrypoints and type boundaries + +The root and `/legacy` declarations retain the exact actual npm 5.3.0 type +source, including historical inaccuracies and extraction behavior. Edit neither +to make runtime implementation checks pass. The additive `/runtime` entrypoint +selects the existing ESM or CommonJS factory with accurate declarations. Its +`runtime.d.mts` entry exposes ESM type exports; `runtime.d.cts` and the +`typesVersions` fallback `runtime.d.ts` expose the callable CommonJS factory +and its type namespace. CommonJS returns the function itself; importing it does +not add a `factory.default` property. + +The argument object is required, while its fields are optional. The registrar +returns `RuntimeResult` synchronously. Command methods resolve/return `Owner`, +which is a different object; `emit` is asynchronous even though its initial +insertion work is synchronous. The facade retains its three live getters and +requires a valid target for `mount`. Public data includes mutable point tuples, +typed static icons, normalized filter inputs and queue entries for visibility +callbacks. Filters and visibility callbacks accept truthy results; sending +requires strict `true`. Their `this` is the current normalized option. Loader +functions retain their receiver-free call. `EventMap` supplies named payload +tuples explicitly; importing `/runtime` does not augment old core event types. +It contains types only, with no new event bus or runtime export. + +Keep these narrow implementation assertions documented when editing them: + +- Native style/dataset writes retain their historical numeric and null values. + WebIDL performs the conversion; changing source assignments to satisfy DOM + declarations could change controlled hosts or observable setters. +- Emit/stop state pools own allocated nodes. A frame with a non-null reference + has its associated item, and release checks node identity. Non-null assertions + depend on those invariants; they do not replace lifecycle validation. +- The legacy public core type describes `constructor` as `Function`. The + factory's host adapter narrows it to the existing Artplayer static utilities + and validator. It introduces no new core capability or minimum core version. +- An omitted internal `postMessage` keeps its old empty-message behavior; + unsupported margin strings retain the raw historical fallback at the Worker + boundary. Placement types describe valid scheduler requests, with explicit + compatibility assertions for those exceptional paths. These internal message + helpers are not additional methods promised by the public `Owner` interface. + +The strict package config disables JavaScript input. Its `ES2021.String` type +library describes the existing XML `replaceAll` calls; it neither introduces a +new runtime dependency nor raises the existing browser requirement. Modern and +legacy build targets remain es2020 and es2015; syntax lowering does not polyfill +`replaceAll` or other browser APIs. ## Contracts to retain @@ -125,6 +185,25 @@ Late, unknown and duplicate replies do not settle a different request. ## Verification and remaining work +Run package commands from the repository root with the pinned Node version and +Yarn Classic 1.22.22: + +| Command | What it checks or produces | +| --- | --- | +| `yarn test:danmuku` | Historical and candidate input, parser, Worker, scheduling, rendering, settings and heatmap regressions | +| `yarn test:danmuku-types` | Frozen root declarations, current/legacy compiler consumers, negative cases, implementation assignability and editor types | +| `yarn test:danmuku-types-package` | Packed isolated installs, real entrypoints and consumer module/compiler modes; creates local evidence, does not publish | +| `yarn typecheck` | Strict production sources and the repository consumer type matrix | +| `yarn build artplayer-plugin-danmuku` | Normal package outputs and copied docs artifacts, including the inline TS Worker | +| `yarn dev artplayer-plugin-danmuku` | Local demo rebuild for the package | + +Edit `src/` for runtime changes, `types/runtime-shared.d.ts` for shared accurate +data contracts and the corresponding `runtime.d.*` entry for module shape. +Rebuild artifacts through the package script. The frozen root declaration and +historical test helpers are compatibility evidence, not generated repair targets. +Use `refactor/fixtures/implementation/danmuku.ts` when a source/declaration change must +prove assignability to the actual implementation. + `yarn test:danmuku` runs frozen historical contracts/failures and candidate input, parser, scheduler and Worker regressions. The historical helper and archives must remain frozen; candidate tests use `test/helpers/danmuku-candidate.js` and may select built @@ -143,9 +222,11 @@ three modes, strict dense geometry, one request per prepared item, native pause/seek/rate and visible-node reuse. Preserve both geometric and protocol assertions; unique IDs alone do not prove non-overlapping tracks. -Long-run native load behavior still requires PKG-DANMUKU-07. Check task evidence -and the risk register before changing completion or release status; short native -regressions are not package-wide stability acceptance. +Long-run native load behavior still requires PKG-DANMUKU-07; cross-plugin/core +combination coverage and final distribution acceptance remain PKG-DANMUKU-08/09. +Check task evidence and the risk register before changing completion or release +status. These commands and short native regressions do not establish npm release +readiness; final checks and release reviews remain separate gates. ## Rendering and UI resources diff --git a/packages/artplayer-plugin-danmuku/README.md b/packages/artplayer-plugin-danmuku/README.md index 327bd2120..6e1a06418 100644 --- a/packages/artplayer-plugin-danmuku/README.md +++ b/packages/artplayer-plugin-danmuku/README.md @@ -6,6 +6,38 @@ Danmuku plugin for ArtPlayer [https://artplayer.org](https://artplayer.org/?libs=./uncompiled/artplayer-plugin-danmuku/index.js&example=danmuku) +## TypeScript entrypoints + +Existing imports from `artplayer-plugin-danmuku` and `/legacy` retain the npm +5.3.0 declarations. Use `/runtime` for declarations that describe the actual +factory, asynchronous commands and returned internal owner: + +```ts +import danmuku from 'artplayer-plugin-danmuku/runtime' +import type { Point, RuntimeOption } from 'artplayer-plugin-danmuku/runtime' + +const option: RuntimeOption = { danmuku: [], heatmap: true } +const points: Point[] = [[0, 10], [200, 20]] +``` + +The option object is required; each field is optional. Registration returns the +plugin facade immediately. `await plugin.emit(row)` and `await plugin.load()` +return the internal owner, as do synchronous `config/hide/show/reset` commands. +That owner differs from the registered facade. `mount(target)` returns undefined +and requires a valid element or selector. The facade's option/state getters +remain live. + +The accurate entrypoint also exposes icon types, callback receivers and an +explicit `EventMap` of payload tuples. Filters and visibility callbacks receive +the current option as `this`; `beforeEmit` does too and accepts only strict +`true`. Importing these types does not automatically alter Artplayer's historical +event declarations. Custom points use mutable `[number, number]` tuples and +retain the existing inner-array modifications during rendering. + +ESM imports select the existing ESM factory; CommonJS +`require('artplayer-plugin-danmuku/runtime')` returns the existing callable +factory directly. No additional runtime implementation is loaded. + ## Loading and configuration `plugin.load()` reloads the configured input and replaces the queue after input @@ -66,7 +98,10 @@ finite `heatmap.yMin` or `heatmap.yMax`, and custom points events, retain their original coordinate mapping; use these when deliberately controlling the scale. See [ARCHITECTURE.md](./ARCHITECTURE.md) for module ownership, compatibility -constraints, tests and the remaining staged TypeScript migration. +constraints and editing/verification commands. All owned runtime modules now use +TypeScript, with shared public runtime data types maintained in +`types/runtime-shared.d.ts`. Long-duration load tests, combination/distribution +acceptance and release reviews remain separate unfinished stages. ## License diff --git a/packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.js b/packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.js index 57cc579f2..6ae4f81f1 100644 --- a/packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.js +++ b/packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDanmuku=e(),define(function(){return t.artplayerPluginDanmuku})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDanmuku=e()}(this,function(){"use strict";function t(t){return t instanceof Promise||"[object Promise]"===Object.prototype.toString.call(t)}function e(t){switch(t){case 1:case 2:case 3:default:return 0;case 4:return 2;case 5:return 1}}function i(t){if("string"!==typeof t)return[];const i=t.matchAll(/]*?p="(?

[^"]+)"[^>]*>(?.*?)<\/d>/gs);return Array.from(i).map(t=>{const i=t.groups.p.split(",");if(i.length>=8){return{text:t.groups.text.trim().replaceAll(""",'"').replaceAll("'","'").replaceAll("<","<").replaceAll(">",">").replaceAll("&","&"),time:Number(i[0]),mode:e(Number(i[1])),fontSize:Number(i[2]),color:`#${Number(i[3]).toString(16)}`,timestamp:Number(i[4]),pool:Number(i[5]),userID:i[6],rowID:Number(i[7])}}return null}).filter(Boolean)}function n({data:t}){const{xml:e,id:n}=t,a=i(e);globalThis.postMessage({danmus:a,id:n})}let a=0;function s(t,{signal:s,onCancel:r}={}){return new Promise((o,l)=>{let d,c,p,h=!1;const u=++a,m=()=>g(void 0,function(t){if(void 0!==t?.reason)return t.reason;const e=new Error("Bilibili Danmu loading cancelled");return e.name="AbortError",e}(s));function f(){let t,e=!1;try{if(d){const t=d;d=void 0,t.onmessage=null,t.onerror=null,t.onmessageerror=null,t.terminate()}}catch(i){t=i,e=!0}if(void 0!==c){const n=c;c=void 0;try{URL.revokeObjectURL(n)}catch(i){e||(t=i),e=!0}}if(e)throw t}function g(t,e){if(h)return;h=!0;let i=arguments.length>1;s?.removeEventListener("abort",m),p?.();try{f()}catch(n){i||(e=n),i=!0}i?l(e):o(t)}function y(t,e){if(!h){console.error("Error parsing Bilibili Danmu:",e);try{g(i(t))}catch(n){g(void 0,n)}}}s?.aborted?m():(s?.addEventListener("abort",m),p=r?.(m),h?p?.():async function(){const a=s?await fetch(t,{signal:s}):await fetch(t);if(h)return;const r=await a.text();if(!h)try{const t=`\n ${e.toString()}\n ${i.toString()}\n onmessage = ${n.toString()}\n `;if(c=URL.createObjectURL(new Blob([t],{type:"application/javascript"})),h)return void f();if(d=new Worker(c),h)return void f();d.onmessage=t=>{if(h||t.data?.id!==u)return;const{danmus:e}=t.data;Array.isArray(e)?g(e):g(void 0,new Error("Invalid Bilibili Danmu worker response"))},d.onerror=t=>{t.preventDefault?.(),y(r,t.error||t)},d.onmessageerror=t=>y(r,t.error||t),d.postMessage({xml:r,id:u})}catch(o){y(r,o)}}().catch(t=>g(void 0,t)))})}const r=new WeakMap,o=Symbol("cancelled danmuku input");function l(t){return r.has(t)||r.set(t,{closed:!1,tasks:new Set,emitting:[]}),r.get(t)}function d(t){const e=l(t),i=e.emitting[e.emitting.length-1];return!e.closed&&!t.art.isDestroy&&(!i||i.active())}function c(t){const e=l(t);e.closed=!0;for(const i of[...e.tasks])i.cancel()}function p(t,e,i){const n=t.states[e]||[];for(let a=0;a{const a=n.$ref.offsetTop,s=this.owner.getLeft(n.$ref)-i,r=n.$ref.clientHeight,o=n.$ref.clientWidth,l=s+o,d=e-l,c=l/n.$restTime,p={};p.top=a,p.left=s,p.height=r,p.width=o,p.right=d,p.speed=c,p.distance=l,p.time=n.$restTime,p.mode=n.mode,t.push(p)}),t}left(t){const e=t.getBoundingClientRect();return this.owner.isRotate?e.top:e.left}makeWait(t){this.owner.setState(t,"wait"),t.$ref&&(t.$ref.style.cssText=this.owner.constructor.cssText,t.$ref.style.visibility="hidden",t.$ref.style.marginLeft="0px",t.$ref.style.transform="translateX(0px)",t.$ref.style.transition="transform 0s linear 0s",this.owner.$refs.push(t.$ref),t.$ref=null)}resize(){const{clientWidth:t}=this.owner.$player;this.owner.filter("stop",e=>{if(0===e.mode)e.$ref.style.left=`${t}px`}),this.owner.filter("emit",e=>{switch(e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.left=`${t}px`,e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}})}continue(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("stop",e=>{switch(this.owner.setState(e,"emit"),e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}}),this}suspend(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("emit",e=>{switch(this.owner.setState(e,"stop"),e.mode){case 0:{const i=t-(this.owner.getLeft(e.$ref)-this.owner.getLeft(this.owner.$player));e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition="transform 0s linear 0s";break}}}),this}prepare(t,e){const i=this.owner,n=i.$ref;return e.danmu=t,e.ref=n,t.$ref=n,n.textContent=t.text,i.$danmuku.appendChild(n),n.style.opacity=i.option.opacity,n.style.fontSize=`${i.fontSize}px`,n.style.color=t.color,n.style.border=t.border?`1px solid ${t.color}`:null,n.style.backgroundColor=t.border?"rgb(0 0 0 / 50%)":null,i.utils.setStyles(n,t.style),n}place(t,e,i,n,a){switch(e.style.top=`${i}px`,e.style.visibility="visible",e.dataset.mode=t.mode,e.dataset.id=t.id||"",t.mode){case 0:e.style.left=`${a}px`,e.style.marginLeft="0px",e.style.transform=`translateX(${-n}px)`,e.style.transition=`transform ${t.$restTime}s linear 0s`;break;case 1:case 2:e.style.left="50%",e.style.marginLeft=`-${e.clientWidth/2}px`}}release(t){const{danmu:e,ref:i}=t;i&&e.$ref===i&&(i.style.cssText=this.owner.constructor.cssText,i.style.visibility="hidden",this.owner.$refs.push(i),e.$ref=null),t.ref=null}clear(){this.destroy(),this.owner.$refs=[],this.owner.$danmuku.textContent=""}destroy(){for(const t of this.nodes)t.parentElement&&t.parentElement.removeChild(t);for(const t of this.owner.queue)t.$ref=null;this.owner.$refs.length=0,this.nodes.clear()}}const u=Symbol("cancelled danmuku frame");class m{constructor(t){this.owner=t,this.generation=0,this.starts=0,this.frame=null,this.operation=null,this.closed=!1,this.running=!1,this.fault=!1,this.failedItems=new Set}active(t){const e=this.owner;return!this.closed&&!this.fault&&!e.art.isDestroy&&!e.isStop&&!e.isHide&&t.generation===this.generation}release(t){this.owner.renderer.release(t)}invalidate(){if(this.generation++,null!==this.frame&&window.cancelAnimationFrame(this.frame),this.frame=null,this.owner.timer=null,this.operation){const t=this.operation;this.operation=null,t.cancel(),this.release(t)}this.owner.workerClient?.cancel(),this.failedItems.clear()}report(t){try{this.owner.art.emit("artplayerPluginDanmuku:error",t)}catch(e){console.warn("Failed to report danmuku scheduling error:",e)}}fail(t){this.closed||this.fault||(this.fault=!0,this.invalidate(),this.report(t))}recover(){if(!this.closed&&!this.owner.art.isDestroy&&(this.failedItems.clear(),this.fault))try{this.owner.workerClient?.dispose(),this.owner.createWorker(),this.fault=!1}catch(t){this.report(t)}}schedule(){const t=this.owner;!this.running||this.closed||this.fault||t.art.isDestroy||t.isStop||null!==this.frame||this.operation||(this.frame=window.requestAnimationFrame(()=>{if(this.frame=null,t.timer=null,this.closed||this.fault||t.isStop||t.art.isDestroy)return;let e;const i=new Promise(t=>e=()=>t(u)),n={generation:this.generation,cancel:e,wait:t=>Promise.race([t,i]),ref:null};return this.operation=n,this.run(n).catch(t=>{this.active(n)&&this.fail(t)}).finally(()=>{this.release(n),this.operation===n&&(this.operation=null,this.schedule())})}),t.timer=this.frame)}async run(t){const e=this.owner;if(!e.art.playing||!this.active(t))return;e.filter("emit",t=>{const i=(Date.now()-t.$lastStartTime)/1e3;t.$restTime-=i,t.$lastStartTime=Date.now(),t.$restTime<=0&&e.makeWait(t)});const i=e.readys;for(const a of i){if(!this.active(t))return;if(this.failedItems.has(a))continue;let i;try{i=await t.wait(e.option.beforeVisible(a))}catch(n){if(!this.active(t))return;this.failedItems.add(a),this.report(n);continue}if(!this.active(t))return;if(!i)continue;const{clientWidth:s,clientHeight:r}=e.$player,o=e.renderer.prepare(a,t);if(!this.active(t))return;a.$lastStartTime=Date.now(),a.$restTime=e.speed;const l=s+o.clientWidth,{result:d}=await t.wait(e.postMessage({type:"getDanmuTop",target:{mode:a.mode,height:o.clientHeight,speed:l/a.$restTime},visibles:e.visibles,antiOverlap:e.option.antiOverlap,clientWidth:s,clientHeight:r,marginBottom:e.marginBottom,marginTop:e.marginTop}));if(!this.active(t)||a.$ref!==o)return;void 0!==d?(e.setState(a,"emit"),e.renderer.place(a,o,d,l,s),t.ref=null,e.art.emit("artplayerPluginDanmuku:visible",a)):(e.setState(a,"ready"),this.release(t))}}destroy(){this.closed=!0,this.invalidate()}}let f=0;class g{constructor(t,e){this.pending=new Map,this.closed=!1,this.failed=!1,this.onFailure=e,this.worker=t();try{this.worker.onmessage=t=>{const e=this.pending.get(t.data?.id);e&&(this.pending.delete(t.data.id),e.resolve(t.data))},this.worker.onerror=t=>{t.preventDefault?.(),this.fail(t.error||t)},this.worker.onmessageerror=t=>this.fail(t.error||t)}catch(i){try{this.dispose()}catch{}throw i}}request(t){t.id=++f;const{id:e}=t;return this.closed||this.failed?Promise.resolve({id:e,result:void 0}):new Promise((i,n)=>{this.pending.set(e,{resolve:i,reject:n});try{this.worker.postMessage(t)}catch(a){this.fail(a)}})}fail(t){if(!this.closed&&!this.failed){this.failed=!0;for(const e of this.pending.values())e.reject(t);this.pending.clear();try{this.dispose()}catch{}this.onFailure(t)}}cancel(){for(const[t,e]of this.pending)e.resolve({id:t,result:void 0});this.pending.clear()}dispose(){if(this.closed)return;this.closed=!0,this.cancel();let t,e=!1;const i=i=>{try{i()}catch(n){e||(t=n),e=!0}};for(const n of["onmessage","onerror","onmessageerror"])i(()=>this.worker[n]=null);if(i(()=>this.worker.terminate()),e)throw t}}const y='/*!\n * artplayer-plugin-danmuku.js v5.3.0\n * Github: https://github.com/zhw2590582/ArtPlayer\n * (c) 2017-2026 Harvey Zhao\n * Released under the MIT License.\n */\nfunction t({target:t,visibles:e,clientWidth:i,clientHeight:h,marginBottom:n,marginTop:o,antiOverlap:s}){const r=h-n,p=e.filter(e=>e.mode===t.mode&&e.top<=r).sort((t,e)=>t.top-e.top);if(0===p.length)return 2===t.mode?r-t.height:o;if(p.unshift({type:"top",top:0,left:0,right:0,height:o,width:i,speed:0,distance:i}),p.push({type:"bottom",top:r,left:0,right:0,height:n,width:i,speed:0,distance:i}),2===t.mode)for(let g=p.length-2;g>=0;g-=1){const e=p[g],i=p[g+1],h=e.top+e.height;if(i.top-h>=t.height)return i.top-t.height}else for(let g=1;g=t.height)return h}const a=[];for(let g=1;g{const i=Math.min(...e.map(t=>t.right)),h=Math.min(...t.map(t=>t.right));return i*e.length-h*t.length});break;case 1:case 2:a.sort((t,e)=>{const i=Math.max(...e.map(t=>t.width));return Math.max(...t.map(t=>t.width))*t.length-i*e.length})}return a[0][0].top}switch(t.mode){case 0:{const e=a.find(e=>e.every(e=>{if(ie.time}));return e&&e[0]?e[0].top:void 0}case 1:case 2:return}}onmessage=e=>{const{data:i}=e;if(!i.id||!i.type)return;const h=(0,{getDanmuTop:t}[i.type])(i);globalThis.postMessage({result:h,id:i.id})};\n',v="undefined"!==typeof self&&self.Blob&&new Blob(["URL.revokeObjectURL(import.meta.url);",y],{type:"text/javascript;charset=utf-8"});function w(t){let e;try{if(e=v&&(self.URL||self.webkitURL).createObjectURL(v),!e)throw"";const i=new Worker(e,{type:"module",name:t?.name});return i.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(e)}),i}catch(i){return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(y),{type:"module",name:t?.name})}}class k{constructor(t,e){const{constructor:i,template:n}=t;this.utils=i.utils,this.validator=i.validator,this.$danmuku=n.$danmuku,this.$player=n.$player,this.art=t,this.queue=[],this.$refs=[],this.isStop=!1,this.isHide=!1,this.timer=null,this.index=0,this.worker=null,this.workerClient=null,this.renderer=new h(this),this.scheduler=new m(this),this.option=k.option,this.states={wait:[],ready:[],emit:[],stop:[]},this.start=this.start.bind(this),this.stop=this.stop.bind(this),this.reset=this.reset.bind(this),this.resize=this.resize.bind(this),this.destroy=this.destroy.bind(this),this.seek=this.seek.bind(this),d(this),t.on("destroy",this.destroy);try{if(this.config(e,!0),!d(this))return;this.createWorker()}catch(a){throw t.off("destroy",this.destroy),c(this),this.scheduler.destroy(),a}t.on("video:play",this.start),t.on("video:playing",this.start),t.on("video:pause",this.stop),t.on("video:waiting",this.stop),t.on("resize",this.resize),t.on("video:seeking",this.seek),this.load().catch(t=>console.warn("Failed to load initial danmuku:",t))}static get option(){return{danmuku:[],speed:5,margin:[10,"25%"],opacity:1,color:"#FFFFFF",mode:0,modes:[0,1,2],fontSize:25,antiOverlap:!0,synchronousPlayback:!1,mount:void 0,heatmap:!1,width:512,points:[],filter:()=>!0,beforeEmit:()=>!0,beforeVisible:()=>!0,visible:!0,emitter:!0,maxLength:200,lockTime:5,theme:"dark",OPACITY:{},FONT_SIZE:{},MARGIN:{},SPEED:{},COLOR:[]}}static get scheme(){return{danmuku:"array|function|string|promise",speed:"number",margin:"array",opacity:"number",color:"string",mode:"number",modes:"array",fontSize:"number|string",antiOverlap:"boolean",synchronousPlayback:"boolean",mount:"?htmldivelement|string",heatmap:"object|boolean",width:"number",points:"array",filter:"function",beforeEmit:"function",beforeVisible:"function",visible:"boolean",emitter:"boolean",maxLength:"number",lockTime:"number",theme:"string",OPACITY:"object",FONT_SIZE:"object",MARGIN:"object",SPEED:"object",COLOR:"array"}}static get cssText(){return'\n user-select: none;\n position: absolute;\n white-space: pre;\n pointer-events: none;\n perspective: 500px;\n display: inline-block;\n will-change: transform;\n font-weight: normal;\n line-height: 1.125;\n visibility: hidden;\n font-family: SimHei, "Microsoft JhengHei", Arial, Helvetica, sans-serif;\n text-shadow: rgb(0, 0, 0) 1px 0px 1px, rgb(0, 0, 0) 0px 1px 1px, rgb(0, 0, 0) 0px -1px 1px, rgb(0, 0, 0) -1px 0px 1px;\n '}get isRotate(){return this.art.plugins?.autoOrientation?.state}get marginTop(){const{clamp:t}=this.utils,e=this.option.margin[0],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return k.option.margin[0]}get marginBottom(){const{clamp:t}=this.utils,e=this.option.margin[1],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return k.option.margin[1]}get fontSize(){const{clamp:t}=this.utils,{clientHeight:e}=this.$player,i=this.option.fontSize;if("number"===typeof i)return Math.round(t(i,12,e));if("string"===typeof i&&i.endsWith("%")){const n=Number.parseFloat(i)/100;return Math.round(t(e*n,12,e))}return k.option.fontSize}get $ref(){return this.renderer.acquire()}get readys(){return function(t){const{currentTime:e}=t.art,i=[];return p(t,"ready",t=>i.push(t)),p(t,"wait",t=>{e+.1>=t.time&&t.time>=e-.1&&i.push(t)}),i}(this)}get visibles(){return this.renderer.visibles}get speed(){return this.option.synchronousPlayback&&this.art.playbackRate?this.option.speed/Number(this.art.playbackRate):this.option.speed}async load(e){const{errorHandle:i}=this.utils,n=function(t,e){const i=l(t);if(e)for(const o of[...i.tasks])o.replace&&o.cancel();let n,a=i.closed||t.art.isDestroy;const s=new Promise(t=>n=t),r=new Set,d="function"===typeof AbortController?new AbortController:void 0,c={replace:e,signal:d?.signal,active:()=>!a&&!i.closed&&!t.art.isDestroy,onCancel:t=>c.active()?(r.add(t),()=>r.delete(t)):(t(),()=>{}),cancel(){if(!a){a=!0,n(o),d?.abort();for(const t of[...r])t();r.clear()}},wait:t=>Promise.race([t,s]),emit(e){i.emitting.push(c);try{return t.emit(e)}finally{i.emitting.pop()}},finish(){i.tasks.delete(c),r.clear()}};return a?n(o):i.tasks.add(c),c}(this,void 0===e);try{if(!n.active())return this;const a=function(e,i){return"function"===typeof e?{asynchronous:!0,value:e()}:t(e)?{asynchronous:!0,value:e}:"string"===typeof e?{asynchronous:!0,value:s(e,i)}:{asynchronous:!1,value:Array.isArray(e)?e:[]}}(e||this.option.danmuku,n),r=a.asynchronous?await n.wait(a.value):a.value;if(!n.active())return this;if(i(Array.isArray(r),"Danmuku need return an array as result"),void 0===e){if(this.reset(),!n.active())return this;this.queue=[],this.states={wait:[],ready:[],emit:[],stop:[]},this.renderer.clear()}for(let t=0;t{const i=s[e],n=r[e];return"function"===typeof i||"function"===typeof n||t(i)||t(n)?i!==n:JSON.stringify(i)!==JSON.stringify(n)}))&&!i)return this;const o=function(t,e,{defaults:i,validate:n,clamp:a,mount:s}){const r=Object.assign({},i,t,e);return n(r),r.mode=a(r.mode,0,2),r.speed=a(r.speed,1,10),r.opacity=a(r.opacity,0,1),r.lockTime=a(r.lockTime,1,60),r.maxLength=a(r.maxLength,1,1e3),r.mount=r.mount||s,r}(this.option,e,{defaults:k.option,validate:t=>this.validator(t,k.scheme),clamp:n,mount:a});if(!d(this))return this;const l=this.option.beforeVisible!==o.beforeVisible;return this.option=o,l&&(this.scheduler.invalidate(),this.scheduler.schedule()),e.fontSize&&(this.reset(),!d(this))||(this.option.visible?this.show():this.hide(),d(this)&&this.art.emit("artplayerPluginDanmuku:config",this.option)),this}getLeft(t){return this.renderer.left(t)}postMessage(t={}){return this.workerClient.request(t)}createWorker(){this.workerClient=new g(()=>new w,t=>this.scheduler.fail(t)),this.worker=this.workerClient.worker}filter(t,e){return p(this,t,e)}setState(t,e){!function(t,e,i){t.states[e.$state]=t.states[e.$state].filter(t=>t!==e),e.$state=i,e.$ref&&(e.$ref.dataset.state=i),t.states[i].push(e)}(this,t,e)}makeWait(t){this.renderer.makeWait(t)}update(){return this.scheduler.running=!0,this.scheduler.schedule(),this}resize(){this.renderer.resize()}continue(){return this.renderer.continue(),this}suspend(){return this.renderer.suspend(),this}stop(){return this.isStop=!0,this.scheduler.running=!1,this.scheduler.invalidate(),this.suspend(),this.art.emit("artplayerPluginDanmuku:stop"),this}start(){if(this.scheduler.closed||this.art.isDestroy)return this;const t=++this.scheduler.starts,e=this.scheduler.generation;this.isStop=!1,this.scheduler.recover();return this.scheduler.closed||this.art.isDestroy||this.isStop||this.scheduler.fault||e!==this.scheduler.generation||t!==this.scheduler.starts||(this.continue(),this.update(),this.art.emit("artplayerPluginDanmuku:start")),this}reset(){return this.scheduler.invalidate(),this.queue.forEach(t=>this.makeWait(t)),this.art.emit("artplayerPluginDanmuku:reset"),this.scheduler.recover(),this.scheduler.schedule(),this}seek(){this.scheduler.invalidate(),this.scheduler.schedule()}show(){return this.isHide=!1,this.$danmuku.style.opacity=1,this.option.visible=!0,this.art.emit("artplayerPluginDanmuku:show"),this}hide(){return this.isHide=!0,this.scheduler.invalidate(),this.$danmuku.style.opacity=0,this.option.visible=!1,this.art.emit("artplayerPluginDanmuku:hide"),this.scheduler.schedule(),this}destroy(){c(this),this.scheduler.destroy();try{try{this.stop()}finally{this.workerClient?.dispose()}}finally{this.renderer.destroy(),this.art.off("video:play",this.start),this.art.off("video:playing",this.start),this.art.off("video:pause",this.stop),this.art.off("video:waiting",this.stop),this.art.off("resize",this.resize),this.art.off("video:seeking",this.seek),this.art.off("destroy",this.destroy),this.art.emit("artplayerPluginDanmuku:destroy")}}}const x=4294967295;function $(t,e){let i=0,n=t.length;for(;iNumber.isFinite(t)&&t>0))return null;const r={xMin:0,xMax:t,yMin:0,yMax:128,scale:.25,opacity:.2,minHeight:Math.floor(.05*e),sampling:Math.max(1,Math.floor(t/100)),smoothing:.2,flattening:.2},o=Object.assign({},"object"===typeof a?a:{}),l=Object.assign({},r,o);for(const x of Object.keys(r))Number.isFinite(l[x])||(l[x]=r[x]);if(l.sampling<=0&&(l.sampling=r.sampling),l.xMin===l.xMax||l.yMin===l.yMax)return null;const d=Array.isArray(s)&&s.length>0,c=d?[...s]:function(t,e,i,n){if(!Number.isFinite(e)||e<=0||!Number.isFinite(i)||i<=0||!Number.isFinite(n)||n<=0||Math.floor(e/n)+1>x)return[];const a=t.map(t=>t.time).filter(t=>"number"===typeof t&&!Number.isNaN(t)).sort((t,e)=>t-e),s=[],r=i/e;if(!Number.isFinite(r))return[];for(let o=0;o<=e;o+=n){if(s.length>=x||o+n===o)return[];const t=o*r,e=(o+n)*r;s.push([o,$(a,e)-$(a,t)])}return s}(n,t,i,l.sampling);if(!c.length||!c.every(t=>Array.isArray(t)&&Number.isFinite(t[0])&&Number.isFinite(t[1])))return null;const p=c[c.length-1];if(p[0]!==t){if(c.length===x)return null;c.push([t,p[1]])}let h=1/0,u=-1/0;for(const x of c)h=Math.min(h,x[1]),u=Math.max(u,x[1]);const m=(h+u)/2;for(const x of c){const t=x[1];x[1]=t*(t>m?1+l.scale:1-l.scale)+l.minHeight}const f=!d&&!Number.isFinite(o.yMin)&&!Number.isFinite(o.yMax);let g=0;for(const x of c)g=Math.max(g,x[1]);const y=f&&g>r.yMax/4;y&&(l.yMax=4*g);const v=t=>y?Math.min(e,Math.max(.75*e,t)):t,w=(t,e,i,n)=>{const a=function(t,e){const i=e[0]-t[0],n=e[1]-t[1];return{length:Math.sqrt(i**2+n**2),angle:Math.atan2(n,i)}}(e||t,i||t),s=b(Math.cos(a.angle)*l.flattening,0,1,1,0),r=a.angle*s+(n?Math.PI:0),o=a.length*l.smoothing;return[t[0]+Math.cos(r)*o,v(t[1]+Math.sin(r)*o)]},k=c.map(i=>[b(i[0],l.xMin,l.xMax,0,t),v(b(i[1],l.yMin,l.yMax,e,0))]);if(!k.every(t=>t.every(Number.isFinite)))return null;const M=k.map((t,i,n)=>{if(0===i)return`M ${n[n.length-1][0]},${e} L ${t[0]},${e} L ${t[0]},${t[1]}`;const a=w(n[i-1],n[i-2],t),s=w(t,n[i-1],n[i+1],!0);return`C ${a[0]},${a[1]} ${s[0]},${s[1]} ${t[0]},${t[1]}${i===n.length-1?" z":""}`}).join(" ");return/NaN|Infinity/u.test(M)?null:{path:M,opacity:l.opacity,width:t,height:e}}let S=0;function z(t,e,i){if(t.isDestroy)return;const{query:n}=t.constructor.utils;let a;const s=[];let r,o,l,d=!1;const c=()=>!d&&!t.isDestroy;function p(e,i){s.push([e,i]),t.on(e,i)}function h(e){if(d)return;let i;d=!0,o=null,l=null;let n=!1;const a=t=>{try{t()}catch(e){n||(i=e),n=!0}};for(const[r,o]of s.splice(0))a(()=>t.off(r,o));if(e&&r&&t.controls.heatmap===r&&a(()=>t.controls.remove("heatmap")),r=void 0,n)throw i}function u(t){c()&&o&&l&&(o.setAttribute("offset",100*t+"%"),l.setAttribute("offset",100*t+"%"))}function m(s){try{!function(s=[]){if(!c())return;if(o=null,l=null,r.innerHTML="",t.option.isLive)return;const d=M({width:r.offsetWidth,height:r.offsetHeight,duration:t.duration,queue:e.queue,option:i,points:s});if(!d||!c())return;const p=r.ownerDocument;for(a||(a=p?"heatmap-solids":"heatmap-solids-"+ ++S);p?.getElementById(a);)a="heatmap-solids-"+ ++S;r.innerHTML=`\n \n \n \n \n \n \n \n \n \n \n \n `,o=n("#heatmap-start",r),l=n("#heatmap-stop",r),u(t.played)}(s)}catch(d){try{h(!0)}catch{}throw d}}p("destroy",()=>h(!0));try{t.controls.add({name:"heatmap",position:"top",html:"",style:{position:"absolute",top:"-100px",left:"0px",right:"0px",height:"100px",width:"100%",pointerEvents:"none"},beforeUnmount(){h(!1)},mounted(e){if(r=e,d)return t.controls.heatmap===e&&t.controls.remove("heatmap"),void(r=void 0);c()?(p("video:timeupdate",()=>u(t.played)),p("setBar",(t,e)=>{"played"===t&&u(e)}),p("ready",()=>m()),p("resize",()=>m()),p("artplayerPluginDanmuku:loaded",()=>m()),p("artplayerPluginDanmuku:points",t=>m(t))):h(!0)}})}catch(f){try{h(!0)}catch{}throw f}return()=>h(!0)}const E=new WeakMap;class F{constructor(t){this.art=t,this.closed=!1,this.disposers=new Set,this.cancelled=new Promise(t=>this.cancel=t)}get active(){return!this.closed&&!this.art.isDestroy}own(t){this.closed?t():this.disposers.add(t)}write(t,e,i,n=i=>t[e]=i){if(!this.active)return;const a=()=>"function"===typeof t.getPropertyPriority?t.getPropertyPriority(e):void 0;let s=E.get(t);s||(s=new Map,E.set(t,s));let r=s.get(e);r||(r={original:t[e],present:e in t||void 0!==t[e],priority:a(),owners:new Map,apply:n},s.set(e,r)),r.owners.has(this)||this.own(()=>{const i=[...r.owners.keys()],n=i[i.length-1]===this;if(r.owners.delete(this),n&&t[e]===r.last&&a()===r.lastPriority){const i=[...r.owners.values()];i.length?(r.apply(i[i.length-1]),r.last=t[e],r.lastPriority=a()):void 0!==r.priority?t.setProperty(e,r.original,r.priority):r.present?r.apply(r.original):delete t[e]}r.owners.size||(s.delete(e),s.size||E.delete(t))}),r.owners.delete(this),r.owners.set(this,i),r.apply(i),r.last=t[e],r.lastPriority=a()}on(t,e){if(!this.active)return;const i=this;function n(...t){if(i.active)return e.apply(this,t)}const a=()=>this.art.off(t,n);this.own(a);try{this.art.on(t,n),this.active||a()}catch(s){throw this.release(a),s}}proxy(t,e,i){if(!this.active)return;const n=this;function a(...t){if(n.active)return i.apply(this,t)}let s;const r=()=>{if("function"===typeof s){const t=this.art.events;"function"===typeof t?.remove&&t.destroyEvents?.has(s)?t.remove(s):s()}else t.removeEventListener?.(e,a)};this.own(r);try{s=this.art.proxy(t,e,a),this.active||r()}catch(o){throw this.release(r),o}}wait(t){return Promise.race([t,this.cancelled])}release(t){try{t()}catch(e){console.warn("Failed to dispose danmuku setting resource:",e)}}close(){if(!this.closed){this.closed=!0,this.cancel();for(const t of this.disposers)this.release(t);this.disposers.clear()}}}function C({min:t,max:e,container:i,findIndex:n,onChange:a,steps:s=[]}){const{lifecycle:r}=this;if(!r.active)return{reset(){}};const{query:o,clamp:l,setStyle:d}=this.utils;d(i,"touch-action","none"),i.innerHTML=`\n

\n
\n ${s.map(()=>'
').join("")}\n
\n
\n
\n
\n
\n ${s.map(t=>t.hide?"":`
${t.name}
`).join("")}\n
\n `;const c=o(".apd-slider-dot",i),p=o(".apd-slider-progress",i);let h=!1;function u(i){if(!r.active)return;if(void 0===i&&(i=n()),ie)return;const o=(i-t)/(e-t);c.style.left=100*o+"%",0===s.length&&(p.style.width=c.style.left),a(i)}function m(n){const{top:a,height:s,left:r,width:o}=i.getBoundingClientRect();if(this.art.isRotate){const i=l(n.clientY-a,0,s);u(Math.round(i/s*(e-t)+t))}else{const i=l(n.clientX-r,0,o);u(Math.round(i/o*(e-t)+t))}}return r.own(()=>h=!1),r.proxy(i,"click",t=>{m.call(this,t)}),r.proxy(i,"pointerdown",t=>{h=0===t.button}),r.on("document:pointermove",t=>{h&&m.call(this,t)}),r.on("document:pointerup",t=>{h&&(h=!1,m.call(this,t))}),{reset:u}}const D='',T='',P='',A='',L='',H='',O='',N='',V='',q='',I='',j='';function B(){const{option:t}=this;return`\n
\n ${I}${q}\n
\n
\n ${P}\n
\n
\n
\n \u6309\u7c7b\u578b\u5c4f\u853d\n
\n
\n ${A}${L}\n
\u6eda\u52a8
\n
\n
\n ${H}${O}\n
\u9876\u90e8
\n
\n
\n ${N}${V}\n
\u5e95\u90e8
\n
\n
\n
\n
\n
\n ${T}${D}\n \u9632\u6b62\u5f39\u5e55\u91cd\u53e0\n
\n
\n ${T}${D}\n \u540c\u6b65\u89c6\u9891\u901f\u5ea6\n
\n
\n
\n \u4e0d\u900f\u660e\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n \u663e\u793a\u533a\u57df\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u5b57\u53f7\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u901f\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n
\n
\n
\n
\n ${j}\n
\n
\n
\n \u6a21\u5f0f\n
\n
\n ${L}\n
\u6eda\u52a8
\n
\n
\n ${O}\n
\u9876\u90e8
\n
\n
\n ${V}\n
\u5e95\u90e8
\n
\n
\n
\n
\n \u989c\u8272\n
\n ${this.COLOR.map(t=>`
`).join("")}\n
\n
\n
\n
\n
\n \n
\u53d1\u9001
\n
\n `}if("undefined"!==typeof document){const t="artplayer-plugin-danmuku",e=Symbol.for("artplayer-plugin-danmuku.pending-style");let i=document.getElementById(t)||document[e];if(!i)if(i=document.createElement("style"),i.id=t,"loading"===document.readyState){document[e]=i;const n=()=>{document.removeEventListener("DOMContentLoaded",n),document[e]===i&&delete document[e];const a=document.getElementById(t);a?a.textContent=i.textContent:(document.head||document.documentElement).appendChild(i)};document.addEventListener("DOMContentLoaded",n,{once:!0})}else(document.head||document.documentElement).appendChild(i);i.textContent='.artplayer-plugin-danmuku{display:flex;position:relative;z-index:99;align-items:center;justify-content:center;font-size:12px;height:32px;width:100%;color:#fff;font-weight:300;flex-shrink:0;gap:10px}.artplayer-plugin-danmuku .apd-icon{cursor:pointer;opacity:.75;transition:all .2s ease;fill:#fff}.artplayer-plugin-danmuku .apd-icon:hover{opacity:1}.artplayer-plugin-danmuku .apd-config{display:flex;position:relative}.artplayer-plugin-danmuku .apd-config .apd-config-panel{position:absolute;bottom:24px;left:0;width:320px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-config .apd-config-panel .apd-config-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-config:hover .apd-config-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-config-mode,.artplayer-plugin-danmuku .apd-config-slider,.artplayer-plugin-danmuku .apd-config-other,.artplayer-plugin-danmuku .apd-style-mode{margin-bottom:15px}.artplayer-plugin-danmuku .apd-modes{display:flex;align-items:center;margin-top:5px;gap:20px}.artplayer-plugin-danmuku .apd-modes .apd-mode{cursor:pointer;text-align:center}.artplayer-plugin-danmuku .apd-modes .apd-mode:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-config-slider{display:flex;align-items:center;gap:12px}.artplayer-plugin-danmuku .apd-config-slider .apd-value{width:32px;text-align:right}.artplayer-plugin-danmuku .apd-slider{position:relative;flex:1;display:flex;height:20px;align-items:center;justify-content:center;cursor:pointer}.artplayer-plugin-danmuku .apd-slider .apd-slider-line{position:relative;height:2px;width:100%;overflow:hidden;border-radius:3px;background-color:#ffffff40}.artplayer-plugin-danmuku .apd-slider .apd-slider-points{position:absolute;inset:0;display:flex;align-items:center;justify-content:space-between}.artplayer-plugin-danmuku .apd-slider .apd-slider-points .apd-slider-point{width:2px;height:2px;border-radius:50%;background-color:#ffffff80}.artplayer-plugin-danmuku .apd-slider .apd-slider-progress{width:0%;height:100%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-dot{position:absolute;transform:translate(-6px);left:0%;width:12px;height:12px;border-radius:50%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps{display:flex;align-items:center;justify-content:space-between;position:absolute;bottom:-12px;width:calc(100% + 32px);color:#777}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps .apd-slider-step{flex-shrink:0;width:36px;text-align:center;scale:.95}.artplayer-plugin-danmuku .apd-config-other{display:flex;align-items:center;gap:20px}.artplayer-plugin-danmuku .apd-config-other .apd-check-off,.artplayer-plugin-danmuku .apd-config-other .apd-check-on{width:16px;height:16px}.artplayer-plugin-danmuku .apd-config-other .apd-other{display:flex;align-items:center;cursor:pointer;gap:2px}.artplayer-plugin-danmuku .apd-config-other .apd-other:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-emitter{display:flex;flex:1;align-items:center;height:100%;background-color:#ffffff40;border-radius:5px}.artplayer-plugin-danmuku .apd-style{position:relative;display:flex;align-items:center;justify-content:center}.artplayer-plugin-danmuku .apd-style .apd-style-panel{position:absolute;bottom:24px;left:0;width:200px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-style .apd-style-panel .apd-style-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-style:hover .apd-style-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-colors{display:flex;flex-wrap:wrap;margin-top:5px;gap:8px}.artplayer-plugin-danmuku .apd-colors .apd-color{width:16px;height:16px;border-radius:2px;cursor:pointer}.artplayer-plugin-danmuku .apd-colors .apd-color.apd-active{border:1px solid black;box-shadow:0 0 0 1px #fff}.artplayer-plugin-danmuku .apd-input{outline:none;height:100%;flex:1;min-width:0;width:auto;border:none;line-height:1;color:#fff;background-color:transparent}.artplayer-plugin-danmuku .apd-input::placeholder{color:#ffffff80}.artplayer-plugin-danmuku .apd-send{display:flex;align-items:center;justify-content:center;height:100%;width:60px;flex-shrink:0;cursor:pointer;text-shadow:none;border-top-right-radius:5px;border-bottom-right-radius:5px;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-send.apd-lock{cursor:not-allowed;color:#666;background-color:#e7e7e7}.art-controls-center .apd-emitter{width:260px;flex:none}.art-fullscreen .artplayer-plugin-danmuku,.art-fullscreen-web .artplayer-plugin-danmuku{height:38px;gap:16px}.art-fullscreen .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen-web .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-on,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-on{width:28px;height:28px}.art-fullscreen .artplayer-plugin-danmuku .apd-emitter,.art-fullscreen-web .artplayer-plugin-danmuku .apd-emitter{width:400px;flex:none}.art-video-player>.artplayer-plugin-danmuku{position:absolute;left:0;right:0;bottom:-40px;padding:0 10px}.art-video-player:has(>.artplayer-plugin-danmuku){margin-bottom:40px}[data-danmuku-emitter=false] .apd-emitter{display:none!important}[data-danmuku-emitter=false] .art-controls-center .artplayer-plugin-danmuku{justify-content:flex-end;gap:18px}[data-danmuku-emitter=false].art-fullscreen .art-controls-center .artplayer-plugin-danmuku,[data-danmuku-emitter=false].art-fullscreen-web .art-controls-center .artplayer-plugin-danmuku{gap:24px}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-icon{fill:#333}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-emitter{background-color:#f1f2f3}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input{color:#000}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input::placeholder{color:#0000004d}[data-danmuku-visible=false] .apd-toggle-off{display:block}[data-danmuku-visible=false] .apd-toggle-on,[data-danmuku-visible=true] .apd-toggle-off{display:none}[data-danmuku-visible=true] .apd-toggle-on{display:block}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-on{display:none}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-off,[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-on{display:block}[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-off,[data-danmuku-sync-video=false] .apd-sync-video .apd-check-on{display:none}[data-danmuku-sync-video=false] .apd-sync-video .apd-check-off,[data-danmuku-sync-video=true] .apd-sync-video .apd-check-on{display:block}[data-danmuku-sync-video=true] .apd-sync-video .apd-check-off{display:none}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-off{display:block}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-on{display:none}[data-danmuku-mode0=false] .art-danmuku [data-mode="0"]{opacity:0!important}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-off{display:none}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-on{display:block}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"]{color:#00a1d6}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"] path{fill:#00a1d6}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-off{display:block}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-on{display:none}[data-danmuku-mode1=false] .art-danmuku [data-mode="1"]{opacity:0!important}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-off{display:none}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-on{display:block}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"]{color:#00a1d6}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"] path{fill:#00a1d6}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-off{display:block}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-on{display:none}[data-danmuku-mode2=false] .art-danmuku [data-mode="2"]{opacity:0!important}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-off{display:none}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-on{display:block}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"]{color:#00a1d6}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"] path{fill:#00a1d6}'}class R{constructor(t,e){this.art=t,this.danmuku=e,this.utils=t.constructor.utils;const{setStyle:i}=this.utils,{$controlsCenter:n}=t.template;this.template={$controlsCenter:n,$mount:n,$danmuku:null,$toggle:null,$config:null,$configPanel:null,$configModes:null,$style:null,$stylePanel:null,$styleModes:null,$colors:null,$opacitySlider:null,$opacityValue:null,$marginSlider:null,$marginValue:null,$fontSizeSlider:null,$fontSizeValue:null,$speedSlider:null,$speedValue:null,$input:null,$send:null},this.slider={opacity:null,margin:null,fontSize:null,speed:null},this.emitting=!1,this.isLock=!1,this.timer=null,this.lifecycle=new F(t),this.destroy=this.destroy.bind(this),this.lifecycle.own(()=>t.off("destroy",this.destroy));try{if(t.on("destroy",this.destroy),!this.lifecycle.active)return;if(this.lifecycle.write(n.style,"display","flex",t=>i(n,"display",t)),this.createTemplate(),!this.lifecycle.active)return;if(this.createSliders(),!this.lifecycle.active)return;if(this.createEvents(),this.mount(this.option.mount),!this.lifecycle.active)return;this.lifecycle.on("resize",()=>this.resize()),this.lifecycle.on("fullscreen",t=>this.onFullscreen(t)),this.lifecycle.on("fullscreenWeb",t=>this.onFullscreen(t)),this.lifecycle.proxy(this.template.$config,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$config,$panel:this.template.$configPanel})}),this.lifecycle.proxy(this.template.$style,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$style,$panel:this.template.$stylePanel})})}catch(a){try{this.destroy()}catch(s){console.warn("Failed to roll back danmuku setting:",s)}throw a}}static get icons(){return{$on:I,$off:q,$config:P,$style:j,$mode_0_off:A,$mode_0_on:L,$mode_1_off:H,$mode_1_on:O,$mode_2_off:N,$mode_2_on:V,$check_on:T,$check_off:D}}get option(){return this.danmuku.option}get outside(){return this.template.$mount!==this.template.$controlsCenter}get TEMPLATE(){return B.call(this)}get OPACITY(){return{min:0,max:100,steps:[],...this.option.OPACITY}}get FONT_SIZE(){return{min:12,max:120,steps:[],...this.option.FONT_SIZE}}get MARGIN(){return{min:0,max:3,steps:[{name:"1/4",value:[10,"75%"]},{name:"\u534a\u5c4f",value:[10,"50%"]},{name:"3/4",value:[10,"25%"]},{name:"\u6ee1\u5c4f",value:[10,10]}],...this.option.MARGIN}}get SPEED(){return{min:0,max:4,steps:[{name:"\u6781\u6162",value:10},{name:"\u8f83\u6162",value:7.5,hide:!0},{name:"\u9002\u4e2d",value:5},{name:"\u8f83\u5feb",value:2.5,hide:!0},{name:"\u6781\u5feb",value:1}],...this.option.SPEED}}get COLOR(){return this.option.COLOR.length?this.option.COLOR:["#FE0302","#FF7204","#FFAA02","#FFD302","#FFFF00","#A0EE00","#00CD00","#019899","#4266BE","#89D5FF","#CC0273","#222222","#9B9B9B","#FFFFFF"]}query(t){const{query:e}=this.utils,{$danmuku:i}=this.template;return e(t,i)}append(t,e){if(!this.lifecycle.active)return;const{append:i}=this.utils;[...t.children].includes(e)||i(t,e)}setData(t,e){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,{$mount:n}=this.template;this.lifecycle.write(i.dataset,t,e),this.outside&&this.lifecycle.write(n.dataset,t,e)}createTemplate(){const{createElement:t,tooltip:e}=this.utils,i=t("div");this.template.$danmuku=i,i.className="artplayer-plugin-danmuku",i.innerHTML=this.TEMPLATE,this.template.$toggle=this.query(".apd-toggle"),this.template.$config=this.query(".apd-config"),this.template.$configPanel=this.query(".apd-config-panel"),this.template.$configModes=this.query(".apd-config-mode .apd-modes"),this.template.$style=this.query(".apd-style"),this.template.$stylePanel=this.query(".apd-style-panel"),this.template.$styleModes=this.query(".apd-style-mode .apd-modes"),this.template.$colors=this.query(".apd-colors"),this.template.$antiOverlap=this.query(".apd-anti-overlap"),this.template.$syncVideo=this.query(".apd-sync-video"),this.template.$opacitySlider=this.query(".apd-config-opacity .apd-slider"),this.template.$opacityValue=this.query(".apd-config-opacity .apd-value"),this.template.$marginSlider=this.query(".apd-config-margin .apd-slider"),this.template.$marginValue=this.query(".apd-config-margin .apd-value"),this.template.$fontSizeSlider=this.query(".apd-config-fontSize .apd-slider"),this.template.$fontSizeValue=this.query(".apd-config-fontSize .apd-value"),this.template.$speedSlider=this.query(".apd-config-speed .apd-slider"),this.template.$speedValue=this.query(".apd-config-speed .apd-value"),this.template.$input=this.query(".apd-input"),this.template.$send=this.query(".apd-send");const{$toggle:n}=this.template;this.lifecycle.on("artplayerPluginDanmuku:show",()=>{e(n,"\u5173\u95ed\u5f39\u5e55")}),this.lifecycle.on("artplayerPluginDanmuku:hide",()=>{e(n,"\u6253\u5f00\u5f39\u5e55")})}createEvents(){const{$toggle:t,$configModes:e,$styleModes:i,$colors:n,$antiOverlap:a,$syncVideo:s,$send:r,$input:o}=this.template;this.lifecycle.proxy(t,"click",()=>{this.danmuku.config({visible:!this.option.visible}),this.reset()}),this.lifecycle.proxy(e,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.option.modes.includes(i)?this.danmuku.config({modes:this.option.modes.filter(t=>t!==i)}):this.danmuku.config({modes:[...this.option.modes,i]}),this.reset()}),this.lifecycle.proxy(a,"click",()=>{this.danmuku.config({antiOverlap:!this.option.antiOverlap}),this.reset()}),this.lifecycle.proxy(s,"click",()=>{this.danmuku.config({synchronousPlayback:!this.option.synchronousPlayback}),this.reset()}),this.lifecycle.proxy(i,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.danmuku.config({mode:i}),this.reset()}),this.lifecycle.proxy(n,"click",t=>{const e=t.target.closest(".apd-color");e&&(this.danmuku.config({color:e.dataset.color}),this.reset())}),this.lifecycle.proxy(r,"click",()=>this.emit()),this.lifecycle.proxy(o,"keypress",t=>{"Enter"===t.key&&(t.preventDefault(),this.emit())})}createSliders(){this.slider.opacity=this.createSlider({...this.OPACITY,container:this.template.$opacitySlider,findIndex:()=>Math.round(100*this.option.opacity),onChange:t=>{const{$opacityValue:e}=this.template;e.textContent=`${t}%`,this.danmuku.config({opacity:t/100})}}),this.slider.margin=this.createSlider({...this.MARGIN,container:this.template.$marginSlider,findIndex:()=>this.MARGIN.steps.findIndex(t=>t.value[0]===this.option.margin[0]&&t.value[1]===this.option.margin[1]),onChange:t=>{const e=this.MARGIN.steps[t];if(!e)return;const{$marginValue:i}=this.template;i.textContent=e.name,this.danmuku.config({margin:e.value})}}),this.slider.fontSize=this.createSlider({...this.FONT_SIZE,container:this.template.$fontSizeSlider,findIndex:()=>this.danmuku.fontSize,onChange:t=>{const{$fontSizeValue:e}=this.template;e.textContent=`${t}px`,t!==this.danmuku.fontSize&&this.danmuku.config({fontSize:t})}}),this.slider.speed=this.createSlider({...this.SPEED,container:this.template.$speedSlider,findIndex:()=>this.SPEED.steps.findIndex(t=>t.value===this.option.speed),onChange:t=>{const e=this.SPEED.steps[t];if(!e)return;const{$speedValue:i}=this.template;i.textContent=e.name,this.danmuku.config({speed:e.value})}})}createSlider(t){return C.call(this,t)}onFullscreen(t){if(!this.lifecycle.active)return;const{$danmuku:e,$controlsCenter:i,$mount:n}=this.template;this.outside?t?this.append(i,e):this.append(n,e):this.append(i,e)}onMouseEnter({$control:t,$panel:e}){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,n=t.getBoundingClientRect(),a=e.getBoundingClientRect(),s=i.getBoundingClientRect(),r=a.width/2-n.width/2,o=s.left-(n.left-r),l=n.right+r-s.right;e.style.left=o>0?`${-r+o}px`:l>0?-r-l+"px":-r+"px"}emit(){return async function(t){const{lifecycle:e,template:{$input:i}}=t;if(!e.active)return;const n=i.value.trim();if(!n.length||t.isLock||t.emitting)return;const a={text:n,mode:t.option.mode,color:t.option.color,time:t.art.currentTime},s=t=>{e.active&&console.error("Error emitting danmuku:",t)};try{t.emitting=!0;const n=await e.wait(t.option.beforeEmit(a));if(!e.active)return;if(t.emitting=!1,!0!==n)return;if(a.border=!0,delete a.time,Promise.resolve(t.danmuku.emit(a)).catch(s),!e.active)return;i.value="",t.lock()}catch(r){s(r),e.active&&(t.emitting=!1)}}(this)}lock(){!function(t){if(!t.lifecycle.active)return;const{addClass:e}=t.utils,{$send:i}=t.template;clearTimeout(t.timer),t.isLock=!0;let n=t.option.lockTime;i.textContent=n,e(i,"apd-lock");const a=()=>{t.timer=setTimeout(()=>{t.lifecycle.active&&(0===n?t.unlock():(n-=1,i.textContent=n,a()))},1e3)};a()}(this)}unlock(){!function(t){if(clearTimeout(t.timer),t.timer=null,t.isLock=!1,!t.lifecycle.active)return;const{removeClass:e}=t.utils,{$send:i}=t.template;i.textContent="\u53d1\u9001",e(i,"apd-lock")}(this)}resize(){if(!this.lifecycle.active)return;if(this.outside)return;if(this.art.fullscreen)return;if(this.art.fullscreenWeb)return;const{$player:t,$controlsCenter:e}=this.art.template,{$danmuku:i}=this.template;this.art.widtht.dataset.color===this.option.color.toUpperCase());s&&t(s,"apd-active"),e(i,this.option.visible?"\u5173\u95ed\u5f39\u5e55":"\u6253\u5f00\u5f39\u5e55"),this.resize()}mount(t){if(!this.lifecycle.active)return;const{errorHandle:e}=this.utils,i="string"===typeof t?document.querySelector(t):t;e(i,`Can not find the mount point: ${t}`),this.append(i,this.template.$danmuku),this.template.$mount=i,this.reset()}destroy(){if(this.lifecycle.closed)return;this.lifecycle.close(),clearTimeout(this.timer),this.timer=null,this.emitting=!1,this.isLock=!1;const t=this.template.$danmuku;t?.parentElement&&t.parentElement.removeChild(t)}}function Z(t){return e=>{const i=new k(e,t);let n;try{n=e.isDestroy?void 0:new R(e,i),!e.isDestroy&&i.option.heatmap&&z(e,i,i.option.heatmap)}catch(a){try{n?.destroy()}catch{}try{i.destroy()}catch{}throw a}return{name:"artplayerPluginDanmuku",emit:i.emit.bind(i),load:i.load.bind(i),config:i.config.bind(i),hide:i.hide.bind(i),show:i.show.bind(i),reset:i.reset.bind(i),mount:n?n.mount.bind(n):()=>{},get option(){return i.option},get isHide(){return i.isHide},get isStop(){return i.isStop}}}}return Z.icons=R.icons,Z}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDanmuku=e(),define(function(){return t.artplayerPluginDanmuku})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDanmuku=e()}(this,function(){"use strict";function t(t){return t instanceof Promise||"[object Promise]"===Object.prototype.toString.call(t)}function e(t){switch(t){case 1:case 2:case 3:default:return 0;case 4:return 2;case 5:return 1}}function i(t){if("string"!==typeof t)return[];const i=t.matchAll(/]*?p="(?

[^"]+)"[^>]*>(?.*?)<\/d>/gs);return Array.from(i).map(t=>{const i=t.groups.p.split(",");if(i.length>=8){return{text:t.groups.text.trim().replaceAll(""",'"').replaceAll("'","'").replaceAll("<","<").replaceAll(">",">").replaceAll("&","&"),time:Number(i[0]),mode:e(Number(i[1])),fontSize:Number(i[2]),color:`#${Number(i[3]).toString(16)}`,timestamp:Number(i[4]),pool:Number(i[5]),userID:i[6],rowID:Number(i[7])}}return null}).filter(Boolean)}function n({data:t}){const{xml:e,id:n}=t,a=i(e);globalThis.postMessage({danmus:a,id:n})}let a=0;function s(t,{signal:s,onCancel:r}={}){return new Promise((o,l)=>{let d,c,p,h=!1;const u=++a,m=()=>g(void 0,function(t){if(void 0!==t?.reason)return t.reason;const e=new Error("Bilibili Danmu loading cancelled");return e.name="AbortError",e}(s));function f(){let t,e=!1;try{if(d){const t=d;d=void 0,t.onmessage=null,t.onerror=null,t.onmessageerror=null,t.terminate()}}catch(i){t=i,e=!0}if(void 0!==c){const n=c;c=void 0;try{URL.revokeObjectURL(n)}catch(i){e||(t=i),e=!0}}if(e)throw t}function g(t,e){if(h)return;h=!0;let i=arguments.length>1;s?.removeEventListener("abort",m),p?.();try{f()}catch(n){i||(e=n),i=!0}i?l(e):o(t)}function y(t,e){if(!h){console.error("Error parsing Bilibili Danmu:",e);try{g(i(t))}catch(n){g(void 0,n)}}}s?.aborted?m():(s?.addEventListener("abort",m),p=r?.(m),h?p?.():async function(){const a=s?await fetch(t,{signal:s}):await fetch(t);if(h)return;const r=await a.text();if(!h)try{const t=`\n ${e.toString()}\n ${i.toString()}\n onmessage = ${n.toString()}\n `;if(c=URL.createObjectURL(new Blob([t],{type:"application/javascript"})),h)return void f();if(d=new Worker(c),h)return void f();d.onmessage=t=>{if(h||t.data?.id!==u)return;const{danmus:e}=t.data;Array.isArray(e)?g(e):g(void 0,new Error("Invalid Bilibili Danmu worker response"))},d.onerror=t=>{t.preventDefault?.(),y(r,t.error||t)},d.onmessageerror=t=>y(r,t.error||t),d.postMessage({xml:r,id:u})}catch(o){y(r,o)}}().catch(t=>g(void 0,t)))})}const r=new WeakMap,o=Symbol("cancelled danmuku input");function l(t){return r.has(t)||r.set(t,{closed:!1,tasks:new Set,emitting:[]}),r.get(t)}function d(t){const e=l(t),i=e.emitting[e.emitting.length-1];return!e.closed&&!t.art.isDestroy&&(!i||i.active())}function c(t){const e=l(t);e.closed=!0;for(const i of[...e.tasks])i.cancel()}function p(t,e,i){const n=t.states[e]||[];for(let a=0;a{const a=n.$ref.offsetTop,s=this.owner.getLeft(n.$ref)-i,r=n.$ref.clientHeight,o=n.$ref.clientWidth,l=s+o,d=e-l,c=l/n.$restTime,p={};p.top=a,p.left=s,p.height=r,p.width=o,p.right=d,p.speed=c,p.distance=l,p.time=n.$restTime,p.mode=n.mode,t.push(p)}),t}left(t){const e=t.getBoundingClientRect();return this.owner.isRotate?e.top:e.left}makeWait(t){this.owner.setState(t,"wait"),t.$ref&&(t.$ref.style.cssText=this.owner.constructor.cssText,t.$ref.style.visibility="hidden",t.$ref.style.marginLeft="0px",t.$ref.style.transform="translateX(0px)",t.$ref.style.transition="transform 0s linear 0s",this.owner.$refs.push(t.$ref),t.$ref=null)}resize(){const{clientWidth:t}=this.owner.$player;this.owner.filter("stop",e=>{if(0===e.mode)e.$ref.style.left=`${t}px`}),this.owner.filter("emit",e=>{switch(e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.left=`${t}px`,e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}})}continue(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("stop",e=>{switch(this.owner.setState(e,"emit"),e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}}),this}suspend(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("emit",e=>{switch(this.owner.setState(e,"stop"),e.mode){case 0:{const i=t-(this.owner.getLeft(e.$ref)-this.owner.getLeft(this.owner.$player));e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition="transform 0s linear 0s";break}}}),this}prepare(t,e){const i=this.owner,n=i.$ref;return e.danmu=t,e.ref=n,t.$ref=n,n.textContent=t.text,i.$danmuku.appendChild(n),n.style.opacity=i.option.opacity,n.style.fontSize=`${i.fontSize}px`,n.style.color=t.color,n.style.border=t.border?`1px solid ${t.color}`:null,n.style.backgroundColor=t.border?"rgb(0 0 0 / 50%)":null,i.utils.setStyles(n,t.style),n}place(t,e,i,n,a){switch(e.style.top=`${i}px`,e.style.visibility="visible",e.dataset.mode=t.mode,e.dataset.id=t.id||"",t.mode){case 0:e.style.left=`${a}px`,e.style.marginLeft="0px",e.style.transform=`translateX(${-n}px)`,e.style.transition=`transform ${t.$restTime}s linear 0s`;break;case 1:case 2:e.style.left="50%",e.style.marginLeft=`-${e.clientWidth/2}px`}}release(t){const{danmu:e,ref:i}=t;i&&e.$ref===i&&(i.style.cssText=this.owner.constructor.cssText,i.style.visibility="hidden",this.owner.$refs.push(i),e.$ref=null),t.ref=null}clear(){this.destroy(),this.owner.$refs=[],this.owner.$danmuku.textContent=""}destroy(){for(const t of this.nodes)t.parentElement&&t.parentElement.removeChild(t);for(const t of this.owner.queue)t.$ref=null;this.owner.$refs.length=0,this.nodes.clear()}}const u=Symbol("cancelled danmuku frame");class m{constructor(t){this.owner=t,this.generation=0,this.starts=0,this.frame=null,this.operation=null,this.closed=!1,this.running=!1,this.fault=!1,this.failedItems=new Set}active(t){const e=this.owner;return!this.closed&&!this.fault&&!e.art.isDestroy&&!e.isStop&&!e.isHide&&t.generation===this.generation}release(t){this.owner.renderer.release(t)}invalidate(){if(this.generation++,null!==this.frame&&window.cancelAnimationFrame(this.frame),this.frame=null,this.owner.timer=null,this.operation){const t=this.operation;this.operation=null,t.cancel(),this.release(t)}this.owner.workerClient?.cancel(),this.failedItems.clear()}report(t){try{this.owner.art.emit("artplayerPluginDanmuku:error",t)}catch(e){console.warn("Failed to report danmuku scheduling error:",e)}}fail(t){this.closed||this.fault||(this.fault=!0,this.invalidate(),this.report(t))}recover(){if(!this.closed&&!this.owner.art.isDestroy&&(this.failedItems.clear(),this.fault))try{this.owner.workerClient?.dispose(),this.owner.createWorker(),this.fault=!1}catch(t){this.report(t)}}schedule(){const t=this.owner;!this.running||this.closed||this.fault||t.art.isDestroy||t.isStop||null!==this.frame||this.operation||(this.frame=window.requestAnimationFrame(()=>{if(this.frame=null,t.timer=null,this.closed||this.fault||t.isStop||t.art.isDestroy)return;let e;const i=new Promise(t=>e=()=>t(u)),n={generation:this.generation,cancel:e,wait:t=>Promise.race([t,i]),ref:null};return this.operation=n,this.run(n).catch(t=>{this.active(n)&&this.fail(t)}).finally(()=>{this.release(n),this.operation===n&&(this.operation=null,this.schedule())})}),t.timer=this.frame)}async run(t){const e=this.owner;if(!e.art.playing||!this.active(t))return;e.filter("emit",t=>{const i=(Date.now()-t.$lastStartTime)/1e3;t.$restTime-=i,t.$lastStartTime=Date.now(),t.$restTime<=0&&e.makeWait(t)});const i=e.readys;for(const a of i){if(!this.active(t))return;if(this.failedItems.has(a))continue;let i;try{i=await t.wait(e.option.beforeVisible(a))}catch(n){if(!this.active(t))return;this.failedItems.add(a),this.report(n);continue}if(!this.active(t))return;if(!i)continue;const{clientWidth:s,clientHeight:r}=e.$player,o=e.renderer.prepare(a,t);if(!this.active(t))return;a.$lastStartTime=Date.now(),a.$restTime=e.speed;const l=s+o.clientWidth,d=await t.wait(e.postMessage({type:"getDanmuTop",target:{mode:a.mode,height:o.clientHeight,speed:l/a.$restTime},visibles:e.visibles,antiOverlap:e.option.antiOverlap,clientWidth:s,clientHeight:r,marginBottom:e.marginBottom,marginTop:e.marginTop})),c="symbol"===typeof d?void 0:d.result;if(!this.active(t)||a.$ref!==o)return;void 0!==c?(e.setState(a,"emit"),e.renderer.place(a,o,c,l,s),t.ref=null,e.art.emit("artplayerPluginDanmuku:visible",a)):(e.setState(a,"ready"),this.release(t))}}destroy(){this.closed=!0,this.invalidate()}}let f=0;class g{constructor(t,e){this.pending=new Map,this.closed=!1,this.failed=!1,this.onFailure=e,this.worker=t();try{this.worker.onmessage=t=>{const e=this.pending.get(t.data?.id);e&&(this.pending.delete(t.data.id),e.resolve(t.data))},this.worker.onerror=t=>{t.preventDefault?.(),this.fail(t.error||t)},this.worker.onmessageerror=t=>this.fail(t.error||t)}catch(i){try{this.dispose()}catch{}throw i}}request(t){t.id=++f;const{id:e}=t;return this.closed||this.failed?Promise.resolve({id:e,result:void 0}):new Promise((i,n)=>{this.pending.set(e,{resolve:i,reject:n});try{this.worker.postMessage(t)}catch(a){this.fail(a)}})}fail(t){if(!this.closed&&!this.failed){this.failed=!0;for(const e of this.pending.values())e.reject(t);this.pending.clear();try{this.dispose()}catch{}this.onFailure(t)}}cancel(){for(const[t,e]of this.pending)e.resolve({id:t,result:void 0});this.pending.clear()}dispose(){if(this.closed)return;this.closed=!0,this.cancel();let t,e=!1;const i=i=>{try{i()}catch(n){e||(t=n),e=!0}};for(const n of["onmessage","onerror","onmessageerror"])i(()=>this.worker[n]=null);if(i(()=>this.worker.terminate()),e)throw t}}const y='/*!\n * artplayer-plugin-danmuku.js v5.3.0\n * Github: https://github.com/zhw2590582/ArtPlayer\n * (c) 2017-2026 Harvey Zhao\n * Released under the MIT License.\n */\nfunction t({target:t,visibles:e,clientWidth:i,clientHeight:h,marginBottom:n,marginTop:o,antiOverlap:s}){const r=h-n,p=e.filter(e=>e.mode===t.mode&&e.top<=r).sort((t,e)=>t.top-e.top);if(0===p.length)return 2===t.mode?r-t.height:o;if(p.unshift({type:"top",top:0,left:0,right:0,height:o,width:i,speed:0,distance:i}),p.push({type:"bottom",top:r,left:0,right:0,height:n,width:i,speed:0,distance:i}),2===t.mode)for(let g=p.length-2;g>=0;g-=1){const e=p[g],i=p[g+1],h=e.top+e.height;if(i.top-h>=t.height)return i.top-t.height}else for(let g=1;g=t.height)return h}const a=[];for(let g=1;g{const i=Math.min(...e.map(t=>t.right)),h=Math.min(...t.map(t=>t.right));return i*e.length-h*t.length});break;case 1:case 2:a.sort((t,e)=>{const i=Math.max(...e.map(t=>t.width));return Math.max(...t.map(t=>t.width))*t.length-i*e.length})}return a[0][0].top}switch(t.mode){case 0:{const e=a.find(e=>e.every(e=>{if(ie.time}));return e&&e[0]?e[0].top:void 0}case 1:case 2:return}}onmessage=e=>{const{data:i}=e;if(!i.id||!i.type)return;const h=(0,{getDanmuTop:t}[i.type])(i);globalThis.postMessage({result:h,id:i.id})};\n',v="undefined"!==typeof self&&self.Blob&&new Blob(["URL.revokeObjectURL(import.meta.url);",y],{type:"text/javascript;charset=utf-8"});function w(t){let e;try{if(e=v&&(self.URL||self.webkitURL).createObjectURL(v),!e)throw"";const i=new Worker(e,{type:"module",name:t?.name});return i.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(e)}),i}catch(i){return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(y),{type:"module",name:t?.name})}}class k{constructor(t,e){const{constructor:i,template:n}=t;this.utils=i.utils,this.validator=i.validator,this.$danmuku=n.$danmuku,this.$player=n.$player,this.art=t,this.queue=[],this.$refs=[],this.isStop=!1,this.isHide=!1,this.timer=null,this.index=0,this.worker=null,this.workerClient=null,this.renderer=new h(this),this.scheduler=new m(this),this.option=k.option,this.states={wait:[],ready:[],emit:[],stop:[]},this.start=this.start.bind(this),this.stop=this.stop.bind(this),this.reset=this.reset.bind(this),this.resize=this.resize.bind(this),this.destroy=this.destroy.bind(this),this.seek=this.seek.bind(this),d(this),t.on("destroy",this.destroy);try{if(this.config(e,!0),!d(this))return;this.createWorker()}catch(a){throw t.off("destroy",this.destroy),c(this),this.scheduler.destroy(),a}t.on("video:play",this.start),t.on("video:playing",this.start),t.on("video:pause",this.stop),t.on("video:waiting",this.stop),t.on("resize",this.resize),t.on("video:seeking",this.seek),this.load().catch(t=>console.warn("Failed to load initial danmuku:",t))}static get option(){return{danmuku:[],speed:5,margin:[10,"25%"],opacity:1,color:"#FFFFFF",mode:0,modes:[0,1,2],fontSize:25,antiOverlap:!0,synchronousPlayback:!1,mount:void 0,heatmap:!1,width:512,points:[],filter:()=>!0,beforeEmit:()=>!0,beforeVisible:()=>!0,visible:!0,emitter:!0,maxLength:200,lockTime:5,theme:"dark",OPACITY:{},FONT_SIZE:{},MARGIN:{},SPEED:{},COLOR:[]}}static get scheme(){return{danmuku:"array|function|string|promise",speed:"number",margin:"array",opacity:"number",color:"string",mode:"number",modes:"array",fontSize:"number|string",antiOverlap:"boolean",synchronousPlayback:"boolean",mount:"?htmldivelement|string",heatmap:"object|boolean",width:"number",points:"array",filter:"function",beforeEmit:"function",beforeVisible:"function",visible:"boolean",emitter:"boolean",maxLength:"number",lockTime:"number",theme:"string",OPACITY:"object",FONT_SIZE:"object",MARGIN:"object",SPEED:"object",COLOR:"array"}}static get cssText(){return'\n user-select: none;\n position: absolute;\n white-space: pre;\n pointer-events: none;\n perspective: 500px;\n display: inline-block;\n will-change: transform;\n font-weight: normal;\n line-height: 1.125;\n visibility: hidden;\n font-family: SimHei, "Microsoft JhengHei", Arial, Helvetica, sans-serif;\n text-shadow: rgb(0, 0, 0) 1px 0px 1px, rgb(0, 0, 0) 0px 1px 1px, rgb(0, 0, 0) 0px -1px 1px, rgb(0, 0, 0) -1px 0px 1px;\n '}get isRotate(){return this.art.plugins?.autoOrientation?.state}get marginTop(){const{clamp:t}=this.utils,e=this.option.margin[0],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return k.option.margin[0]}get marginBottom(){const{clamp:t}=this.utils,e=this.option.margin[1],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return k.option.margin[1]}get fontSize(){const{clamp:t}=this.utils,{clientHeight:e}=this.$player,i=this.option.fontSize;if("number"===typeof i)return Math.round(t(i,12,e));if("string"===typeof i&&i.endsWith("%")){const n=Number.parseFloat(i)/100;return Math.round(t(e*n,12,e))}return k.option.fontSize}get $ref(){return this.renderer.acquire()}get readys(){return function(t){const{currentTime:e}=t.art,i=[];return p(t,"ready",t=>i.push(t)),p(t,"wait",t=>{e+.1>=t.time&&t.time>=e-.1&&i.push(t)}),i}(this)}get visibles(){return this.renderer.visibles}get speed(){return this.option.synchronousPlayback&&this.art.playbackRate?this.option.speed/Number(this.art.playbackRate):this.option.speed}async load(e){const{errorHandle:i}=this.utils,n=function(t,e){const i=l(t);if(e)for(const o of[...i.tasks])o.replace&&o.cancel();let n,a=i.closed||t.art.isDestroy;const s=new Promise(t=>n=t),r=new Set,d="function"===typeof AbortController?new AbortController:void 0,c={replace:e,signal:d?.signal,active:()=>!a&&!i.closed&&!t.art.isDestroy,onCancel:t=>c.active()?(r.add(t),()=>r.delete(t)):(t(),()=>{}),cancel(){if(!a){a=!0,n(o),d?.abort();for(const t of[...r])t();r.clear()}},wait:t=>Promise.race([t,s]),emit(e){i.emitting.push(c);try{return t.emit(e)}finally{i.emitting.pop()}},finish(){i.tasks.delete(c),r.clear()}};return a?n(o):i.tasks.add(c),c}(this,void 0===e);try{if(!n.active())return this;const a=function(e,i){return"function"===typeof e?{asynchronous:!0,value:e()}:t(e)?{asynchronous:!0,value:e}:"string"===typeof e?{asynchronous:!0,value:s(e,i)}:{asynchronous:!1,value:Array.isArray(e)?e:[]}}(e||this.option.danmuku,n),r=a.asynchronous?await n.wait(a.value):a.value;if(!n.active())return this;i(Array.isArray(r),"Danmuku need return an array as result");const o=r;if(void 0===e){if(this.reset(),!n.active())return this;this.queue=[],this.states={wait:[],ready:[],emit:[],stop:[]},this.renderer.clear()}for(let t=0;t{const i=s[e],n=r[e];return"function"===typeof i||"function"===typeof n||t(i)||t(n)?i!==n:JSON.stringify(i)!==JSON.stringify(n)}))&&!i)return this;const o=function(t,e,{defaults:i,validate:n,clamp:a,mount:s}){const r=Object.assign({},i,t,e);return n(r),r.mode=a(r.mode,0,2),r.speed=a(r.speed,1,10),r.opacity=a(r.opacity,0,1),r.lockTime=a(r.lockTime,1,60),r.maxLength=a(r.maxLength,1,1e3),r.mount=r.mount||s,r}(this.option,e,{defaults:k.option,validate:t=>this.validator(t,k.scheme),clamp:n,mount:a});if(!d(this))return this;const l=this.option.beforeVisible!==o.beforeVisible;return this.option=o,l&&(this.scheduler.invalidate(),this.scheduler.schedule()),e.fontSize&&(this.reset(),!d(this))||(this.option.visible?this.show():this.hide(),d(this)&&this.art.emit("artplayerPluginDanmuku:config",this.option)),this}getLeft(t){return this.renderer.left(t)}postMessage(t={}){return this.workerClient.request(t)}createWorker(){this.workerClient=new g(()=>new w,t=>this.scheduler.fail(t)),this.worker=this.workerClient.worker}filter(t,e){return p(this,t,e)}setState(t,e){!function(t,e,i){t.states[e.$state]=t.states[e.$state].filter(t=>t!==e),e.$state=i,e.$ref&&(e.$ref.dataset.state=i),t.states[i].push(e)}(this,t,e)}makeWait(t){this.renderer.makeWait(t)}update(){return this.scheduler.running=!0,this.scheduler.schedule(),this}resize(){this.renderer.resize()}continue(){return this.renderer.continue(),this}suspend(){return this.renderer.suspend(),this}stop(){return this.isStop=!0,this.scheduler.running=!1,this.scheduler.invalidate(),this.suspend(),this.art.emit("artplayerPluginDanmuku:stop"),this}start(){if(this.scheduler.closed||this.art.isDestroy)return this;const t=++this.scheduler.starts,e=this.scheduler.generation;this.isStop=!1,this.scheduler.recover();return this.scheduler.closed||this.art.isDestroy||this.isStop||this.scheduler.fault||e!==this.scheduler.generation||t!==this.scheduler.starts||(this.continue(),this.update(),this.art.emit("artplayerPluginDanmuku:start")),this}reset(){return this.scheduler.invalidate(),this.queue.forEach(t=>this.makeWait(t)),this.art.emit("artplayerPluginDanmuku:reset"),this.scheduler.recover(),this.scheduler.schedule(),this}seek(){this.scheduler.invalidate(),this.scheduler.schedule()}show(){return this.isHide=!1,this.$danmuku.style.opacity=1,this.option.visible=!0,this.art.emit("artplayerPluginDanmuku:show"),this}hide(){return this.isHide=!0,this.scheduler.invalidate(),this.$danmuku.style.opacity=0,this.option.visible=!1,this.art.emit("artplayerPluginDanmuku:hide"),this.scheduler.schedule(),this}destroy(){c(this),this.scheduler.destroy();try{try{this.stop()}finally{this.workerClient?.dispose()}}finally{this.renderer.destroy(),this.art.off("video:play",this.start),this.art.off("video:playing",this.start),this.art.off("video:pause",this.stop),this.art.off("video:waiting",this.stop),this.art.off("resize",this.resize),this.art.off("video:seeking",this.seek),this.art.off("destroy",this.destroy),this.art.emit("artplayerPluginDanmuku:destroy")}}}const x=4294967295;function $(t,e){let i=0,n=t.length;for(;iNumber.isFinite(t)&&t>0))return null;const r={xMin:0,xMax:t,yMin:0,yMax:128,scale:.25,opacity:.2,minHeight:Math.floor(.05*e),sampling:Math.max(1,Math.floor(t/100)),smoothing:.2,flattening:.2},o=Object.assign({},"object"===typeof a?a:{}),l=Object.assign({},r,o);for(const x of Object.keys(r))Number.isFinite(l[x])||(l[x]=r[x]);if(l.sampling<=0&&(l.sampling=r.sampling),l.xMin===l.xMax||l.yMin===l.yMax)return null;const d=Array.isArray(s)&&s.length>0,c=d?[...s]:function(t,e,i,n){if(!Number.isFinite(e)||e<=0||!Number.isFinite(i)||i<=0||!Number.isFinite(n)||n<=0||Math.floor(e/n)+1>x)return[];const a=t.map(t=>t.time).filter(t=>"number"===typeof t&&!Number.isNaN(t)).sort((t,e)=>t-e),s=[],r=i/e;if(!Number.isFinite(r))return[];for(let o=0;o<=e;o+=n){if(s.length>=x||o+n===o)return[];const t=o*r,e=(o+n)*r;s.push([o,$(a,e)-$(a,t)])}return s}(n,t,i,l.sampling);if(!c.length||!c.every(t=>Array.isArray(t)&&Number.isFinite(t[0])&&Number.isFinite(t[1])))return null;const p=c[c.length-1];if(p[0]!==t){if(c.length===x)return null;c.push([t,p[1]])}let h=1/0,u=-1/0;for(const x of c)h=Math.min(h,x[1]),u=Math.max(u,x[1]);const m=(h+u)/2;for(const x of c){const t=x[1];x[1]=t*(t>m?1+l.scale:1-l.scale)+l.minHeight}const f=!d&&!Number.isFinite(o.yMin)&&!Number.isFinite(o.yMax);let g=0;for(const x of c)g=Math.max(g,x[1]);const y=f&&g>r.yMax/4;y&&(l.yMax=4*g);const v=t=>y?Math.min(e,Math.max(.75*e,t)):t,w=(t,e,i,n)=>{const a=function(t,e){const i=e[0]-t[0],n=e[1]-t[1];return{length:Math.sqrt(i**2+n**2),angle:Math.atan2(n,i)}}(e||t,i||t),s=b(Math.cos(a.angle)*l.flattening,0,1,1,0),r=a.angle*s+(n?Math.PI:0),o=a.length*l.smoothing;return[t[0]+Math.cos(r)*o,v(t[1]+Math.sin(r)*o)]},k=c.map(i=>[b(i[0],l.xMin,l.xMax,0,t),v(b(i[1],l.yMin,l.yMax,e,0))]);if(!k.every(t=>t.every(Number.isFinite)))return null;const M=k.map((t,i,n)=>{if(0===i)return`M ${n[n.length-1][0]},${e} L ${t[0]},${e} L ${t[0]},${t[1]}`;const a=w(n[i-1],n[i-2],t),s=w(t,n[i-1],n[i+1],!0);return`C ${a[0]},${a[1]} ${s[0]},${s[1]} ${t[0]},${t[1]}${i===n.length-1?" z":""}`}).join(" ");return/NaN|Infinity/u.test(M)?null:{path:M,opacity:l.opacity,width:t,height:e}}let S=0;function z(t,e,i){if(t.isDestroy)return;const{query:n}=t.constructor.utils;let a;const s=[];let r,o,l,d=!1;const c=()=>!d&&!t.isDestroy;function p(e,i){const n=i;s.push([e,n]),t.on(e,n)}function h(e){if(d)return;let i;d=!0,o=null,l=null;let n=!1;const a=t=>{try{t()}catch(e){n||(i=e),n=!0}};for(const[r,o]of s.splice(0))a(()=>t.off(r,o));if(e&&r&&t.controls.heatmap===r&&a(()=>t.controls.remove("heatmap")),r=void 0,n)throw i}function u(t){c()&&o&&l&&(o.setAttribute("offset",100*t+"%"),l.setAttribute("offset",100*t+"%"))}function m(s){try{!function(s=[]){if(!c())return;o=null,l=null;const d=r;if(d.innerHTML="",t.option.isLive)return;const p=M({width:d.offsetWidth,height:d.offsetHeight,duration:t.duration,queue:e.queue,option:i,points:s});if(!p||!c())return;const h=d.ownerDocument;for(a||(a=h?"heatmap-solids":"heatmap-solids-"+ ++S);h?.getElementById(a);)a="heatmap-solids-"+ ++S;d.innerHTML=`\n \n \n \n \n \n \n \n \n \n \n \n `,o=n("#heatmap-start",d),l=n("#heatmap-stop",d),u(t.played)}(s)}catch(d){try{h(!0)}catch{}throw d}}p("destroy",()=>h(!0));try{t.controls.add({name:"heatmap",position:"top",html:"",style:{position:"absolute",top:"-100px",left:"0px",right:"0px",height:"100px",width:"100%",pointerEvents:"none"},beforeUnmount(){h(!1)},mounted(e){if(r=e,d)return t.controls.heatmap===e&&t.controls.remove("heatmap"),void(r=void 0);c()?(p("video:timeupdate",()=>u(t.played)),p("setBar",(t,e)=>{"played"===t&&u(e)}),p("ready",()=>m()),p("resize",()=>m()),p("artplayerPluginDanmuku:loaded",()=>m()),p("artplayerPluginDanmuku:points",t=>m(t))):h(!0)}})}catch(f){try{h(!0)}catch{}throw f}return()=>h(!0)}const E=new WeakMap;class F{constructor(t){this.art=t,this.closed=!1,this.disposers=new Set,this.cancelled=new Promise(t=>this.cancel=t)}get active(){return!this.closed&&!this.art.isDestroy}own(t){this.closed?t():this.disposers.add(t)}write(t,e,i,n=i=>t[e]=i){const a=t;if(!this.active)return;const s=()=>"function"===typeof a.getPropertyPriority?a.getPropertyPriority(e):void 0;let r=E.get(a);r||(r=new Map,E.set(a,r));let o=r.get(e);o||(o={original:a[e],present:e in a||void 0!==a[e],priority:s(),owners:new Map,apply:n},r.set(e,o)),o.owners.has(this)||this.own(()=>{const t=[...o.owners.keys()],i=t[t.length-1]===this;if(o.owners.delete(this),i&&a[e]===o.last&&s()===o.lastPriority){const t=[...o.owners.values()];t.length?(o.apply(t[t.length-1]),o.last=a[e],o.lastPriority=s()):void 0!==o.priority?a.setProperty(e,o.original,o.priority):o.present?o.apply(o.original):delete a[e]}o.owners.size||(r.delete(e),r.size||E.delete(a))}),o.owners.delete(this),o.owners.set(this,i),o.apply(i),o.last=a[e],o.lastPriority=s()}on(t,e){if(!this.active)return;const i=this;function n(...t){if(i.active)return e.apply(this,t)}const a=()=>this.art.off(t,n);this.own(a);try{this.art.on(t,n),this.active||a()}catch(s){throw this.release(a),s}}proxy(t,e,i){if(!this.active)return;const n=this;function a(...t){if(n.active)return i.apply(this,t)}let s;const r=()=>{if("function"===typeof s){const t=this.art.events;"function"===typeof t?.remove&&t.destroyEvents?.has(s)?t.remove(s):s()}else t.removeEventListener?.(e,a)};this.own(r);try{s=this.art.proxy(t,e,a),this.active||r()}catch(o){throw this.release(r),o}}wait(t){return Promise.race([t,this.cancelled])}release(t){try{t()}catch(e){console.warn("Failed to dispose danmuku setting resource:",e)}}close(){if(!this.closed){this.closed=!0,this.cancel();for(const t of this.disposers)this.release(t);this.disposers.clear()}}}function C({min:t,max:e,container:i,findIndex:n,onChange:a,steps:s=[]}){const{lifecycle:r}=this;if(!r.active)return{reset(){}};const{query:o,clamp:l,setStyle:d}=this.utils;d(i,"touch-action","none"),i.innerHTML=`\n

\n
\n ${s.map(()=>'
').join("")}\n
\n
\n
\n
\n
\n ${s.map(t=>t.hide?"":`
${t.name}
`).join("")}\n
\n `;const c=o(".apd-slider-dot",i),p=o(".apd-slider-progress",i);let h=!1;function u(i){if(!r.active)return;if(void 0===i&&(i=n()),ie)return;const o=(i-t)/(e-t);c.style.left=100*o+"%",0===s.length&&(p.style.width=c.style.left),a(i)}function m(n){const{top:a,height:s,left:r,width:o}=i.getBoundingClientRect();if(this.art.isRotate){const i=l(n.clientY-a,0,s);u(Math.round(i/s*(e-t)+t))}else{const i=l(n.clientX-r,0,o);u(Math.round(i/o*(e-t)+t))}}return r.own(()=>h=!1),r.proxy(i,"click",t=>{m.call(this,t)}),r.proxy(i,"pointerdown",t=>{h=0===t.button}),r.on("document:pointermove",t=>{h&&m.call(this,t)}),r.on("document:pointerup",t=>{h&&(h=!1,m.call(this,t))}),{reset:u}}const D='',T='',P='',A='',L='',H='',O='',N='',V='',q='',I='',j='';function B(){const{option:t}=this;return`\n
\n ${I}${q}\n
\n
\n ${P}\n
\n
\n
\n \u6309\u7c7b\u578b\u5c4f\u853d\n
\n
\n ${A}${L}\n
\u6eda\u52a8
\n
\n
\n ${H}${O}\n
\u9876\u90e8
\n
\n
\n ${N}${V}\n
\u5e95\u90e8
\n
\n
\n
\n
\n
\n ${T}${D}\n \u9632\u6b62\u5f39\u5e55\u91cd\u53e0\n
\n
\n ${T}${D}\n \u540c\u6b65\u89c6\u9891\u901f\u5ea6\n
\n
\n
\n \u4e0d\u900f\u660e\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n \u663e\u793a\u533a\u57df\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u5b57\u53f7\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u901f\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n
\n
\n
\n
\n ${j}\n
\n
\n
\n \u6a21\u5f0f\n
\n
\n ${L}\n
\u6eda\u52a8
\n
\n
\n ${O}\n
\u9876\u90e8
\n
\n
\n ${V}\n
\u5e95\u90e8
\n
\n
\n
\n
\n \u989c\u8272\n
\n ${this.COLOR.map(t=>`
`).join("")}\n
\n
\n
\n
\n
\n \n
\u53d1\u9001
\n
\n `}if("undefined"!==typeof document){const t="artplayer-plugin-danmuku",e=Symbol.for("artplayer-plugin-danmuku.pending-style"),i=document;let n=document.getElementById(t)||i[e];if(!n)if(n=document.createElement("style"),n.id=t,"loading"===document.readyState){i[e]=n;const a=()=>{document.removeEventListener("DOMContentLoaded",a),i[e]===n&&delete i[e];const s=document.getElementById(t);s?s.textContent=n.textContent:(document.head||document.documentElement).appendChild(n)};document.addEventListener("DOMContentLoaded",a,{once:!0})}else(document.head||document.documentElement).appendChild(n);n.textContent='.artplayer-plugin-danmuku{display:flex;position:relative;z-index:99;align-items:center;justify-content:center;font-size:12px;height:32px;width:100%;color:#fff;font-weight:300;flex-shrink:0;gap:10px}.artplayer-plugin-danmuku .apd-icon{cursor:pointer;opacity:.75;transition:all .2s ease;fill:#fff}.artplayer-plugin-danmuku .apd-icon:hover{opacity:1}.artplayer-plugin-danmuku .apd-config{display:flex;position:relative}.artplayer-plugin-danmuku .apd-config .apd-config-panel{position:absolute;bottom:24px;left:0;width:320px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-config .apd-config-panel .apd-config-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-config:hover .apd-config-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-config-mode,.artplayer-plugin-danmuku .apd-config-slider,.artplayer-plugin-danmuku .apd-config-other,.artplayer-plugin-danmuku .apd-style-mode{margin-bottom:15px}.artplayer-plugin-danmuku .apd-modes{display:flex;align-items:center;margin-top:5px;gap:20px}.artplayer-plugin-danmuku .apd-modes .apd-mode{cursor:pointer;text-align:center}.artplayer-plugin-danmuku .apd-modes .apd-mode:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-config-slider{display:flex;align-items:center;gap:12px}.artplayer-plugin-danmuku .apd-config-slider .apd-value{width:32px;text-align:right}.artplayer-plugin-danmuku .apd-slider{position:relative;flex:1;display:flex;height:20px;align-items:center;justify-content:center;cursor:pointer}.artplayer-plugin-danmuku .apd-slider .apd-slider-line{position:relative;height:2px;width:100%;overflow:hidden;border-radius:3px;background-color:#ffffff40}.artplayer-plugin-danmuku .apd-slider .apd-slider-points{position:absolute;inset:0;display:flex;align-items:center;justify-content:space-between}.artplayer-plugin-danmuku .apd-slider .apd-slider-points .apd-slider-point{width:2px;height:2px;border-radius:50%;background-color:#ffffff80}.artplayer-plugin-danmuku .apd-slider .apd-slider-progress{width:0%;height:100%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-dot{position:absolute;transform:translate(-6px);left:0%;width:12px;height:12px;border-radius:50%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps{display:flex;align-items:center;justify-content:space-between;position:absolute;bottom:-12px;width:calc(100% + 32px);color:#777}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps .apd-slider-step{flex-shrink:0;width:36px;text-align:center;scale:.95}.artplayer-plugin-danmuku .apd-config-other{display:flex;align-items:center;gap:20px}.artplayer-plugin-danmuku .apd-config-other .apd-check-off,.artplayer-plugin-danmuku .apd-config-other .apd-check-on{width:16px;height:16px}.artplayer-plugin-danmuku .apd-config-other .apd-other{display:flex;align-items:center;cursor:pointer;gap:2px}.artplayer-plugin-danmuku .apd-config-other .apd-other:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-emitter{display:flex;flex:1;align-items:center;height:100%;background-color:#ffffff40;border-radius:5px}.artplayer-plugin-danmuku .apd-style{position:relative;display:flex;align-items:center;justify-content:center}.artplayer-plugin-danmuku .apd-style .apd-style-panel{position:absolute;bottom:24px;left:0;width:200px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-style .apd-style-panel .apd-style-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-style:hover .apd-style-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-colors{display:flex;flex-wrap:wrap;margin-top:5px;gap:8px}.artplayer-plugin-danmuku .apd-colors .apd-color{width:16px;height:16px;border-radius:2px;cursor:pointer}.artplayer-plugin-danmuku .apd-colors .apd-color.apd-active{border:1px solid black;box-shadow:0 0 0 1px #fff}.artplayer-plugin-danmuku .apd-input{outline:none;height:100%;flex:1;min-width:0;width:auto;border:none;line-height:1;color:#fff;background-color:transparent}.artplayer-plugin-danmuku .apd-input::placeholder{color:#ffffff80}.artplayer-plugin-danmuku .apd-send{display:flex;align-items:center;justify-content:center;height:100%;width:60px;flex-shrink:0;cursor:pointer;text-shadow:none;border-top-right-radius:5px;border-bottom-right-radius:5px;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-send.apd-lock{cursor:not-allowed;color:#666;background-color:#e7e7e7}.art-controls-center .apd-emitter{width:260px;flex:none}.art-fullscreen .artplayer-plugin-danmuku,.art-fullscreen-web .artplayer-plugin-danmuku{height:38px;gap:16px}.art-fullscreen .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen-web .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-on,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-on{width:28px;height:28px}.art-fullscreen .artplayer-plugin-danmuku .apd-emitter,.art-fullscreen-web .artplayer-plugin-danmuku .apd-emitter{width:400px;flex:none}.art-video-player>.artplayer-plugin-danmuku{position:absolute;left:0;right:0;bottom:-40px;padding:0 10px}.art-video-player:has(>.artplayer-plugin-danmuku){margin-bottom:40px}[data-danmuku-emitter=false] .apd-emitter{display:none!important}[data-danmuku-emitter=false] .art-controls-center .artplayer-plugin-danmuku{justify-content:flex-end;gap:18px}[data-danmuku-emitter=false].art-fullscreen .art-controls-center .artplayer-plugin-danmuku,[data-danmuku-emitter=false].art-fullscreen-web .art-controls-center .artplayer-plugin-danmuku{gap:24px}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-icon{fill:#333}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-emitter{background-color:#f1f2f3}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input{color:#000}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input::placeholder{color:#0000004d}[data-danmuku-visible=false] .apd-toggle-off{display:block}[data-danmuku-visible=false] .apd-toggle-on,[data-danmuku-visible=true] .apd-toggle-off{display:none}[data-danmuku-visible=true] .apd-toggle-on{display:block}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-on{display:none}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-off,[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-on{display:block}[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-off,[data-danmuku-sync-video=false] .apd-sync-video .apd-check-on{display:none}[data-danmuku-sync-video=false] .apd-sync-video .apd-check-off,[data-danmuku-sync-video=true] .apd-sync-video .apd-check-on{display:block}[data-danmuku-sync-video=true] .apd-sync-video .apd-check-off{display:none}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-off{display:block}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-on{display:none}[data-danmuku-mode0=false] .art-danmuku [data-mode="0"]{opacity:0!important}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-off{display:none}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-on{display:block}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"]{color:#00a1d6}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"] path{fill:#00a1d6}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-off{display:block}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-on{display:none}[data-danmuku-mode1=false] .art-danmuku [data-mode="1"]{opacity:0!important}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-off{display:none}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-on{display:block}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"]{color:#00a1d6}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"] path{fill:#00a1d6}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-off{display:block}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-on{display:none}[data-danmuku-mode2=false] .art-danmuku [data-mode="2"]{opacity:0!important}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-off{display:none}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-on{display:block}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"]{color:#00a1d6}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"] path{fill:#00a1d6}'}class R{constructor(t,e){this.art=t,this.danmuku=e,this.utils=t.constructor.utils;const{setStyle:i}=this.utils,{$controlsCenter:n}=t.template;this.template={$controlsCenter:n,$mount:n,$danmuku:null,$toggle:null,$config:null,$configPanel:null,$configModes:null,$style:null,$stylePanel:null,$styleModes:null,$colors:null,$opacitySlider:null,$opacityValue:null,$marginSlider:null,$marginValue:null,$fontSizeSlider:null,$fontSizeValue:null,$speedSlider:null,$speedValue:null,$input:null,$send:null},this.slider={opacity:null,margin:null,fontSize:null,speed:null},this.emitting=!1,this.isLock=!1,this.timer=null,this.lifecycle=new F(t),this.destroy=this.destroy.bind(this),this.lifecycle.own(()=>t.off("destroy",this.destroy));try{if(t.on("destroy",this.destroy),!this.lifecycle.active)return;if(this.lifecycle.write(n.style,"display","flex",t=>i(n,"display",t)),this.createTemplate(),!this.lifecycle.active)return;if(this.createSliders(),!this.lifecycle.active)return;if(this.createEvents(),this.mount(this.option.mount),!this.lifecycle.active)return;this.lifecycle.on("resize",()=>this.resize()),this.lifecycle.on("fullscreen",t=>this.onFullscreen(t)),this.lifecycle.on("fullscreenWeb",t=>this.onFullscreen(t)),this.lifecycle.proxy(this.template.$config,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$config,$panel:this.template.$configPanel})}),this.lifecycle.proxy(this.template.$style,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$style,$panel:this.template.$stylePanel})})}catch(a){try{this.destroy()}catch(s){console.warn("Failed to roll back danmuku setting:",s)}throw a}}static get icons(){return{$on:I,$off:q,$config:P,$style:j,$mode_0_off:A,$mode_0_on:L,$mode_1_off:H,$mode_1_on:O,$mode_2_off:N,$mode_2_on:V,$check_on:T,$check_off:D}}get option(){return this.danmuku.option}get outside(){return this.template.$mount!==this.template.$controlsCenter}get TEMPLATE(){return B.call(this)}get OPACITY(){return{min:0,max:100,steps:[],...this.option.OPACITY}}get FONT_SIZE(){return{min:12,max:120,steps:[],...this.option.FONT_SIZE}}get MARGIN(){return{min:0,max:3,steps:[{name:"1/4",value:[10,"75%"]},{name:"\u534a\u5c4f",value:[10,"50%"]},{name:"3/4",value:[10,"25%"]},{name:"\u6ee1\u5c4f",value:[10,10]}],...this.option.MARGIN}}get SPEED(){return{min:0,max:4,steps:[{name:"\u6781\u6162",value:10},{name:"\u8f83\u6162",value:7.5,hide:!0},{name:"\u9002\u4e2d",value:5},{name:"\u8f83\u5feb",value:2.5,hide:!0},{name:"\u6781\u5feb",value:1}],...this.option.SPEED}}get COLOR(){return this.option.COLOR.length?this.option.COLOR:["#FE0302","#FF7204","#FFAA02","#FFD302","#FFFF00","#A0EE00","#00CD00","#019899","#4266BE","#89D5FF","#CC0273","#222222","#9B9B9B","#FFFFFF"]}query(t){const{query:e}=this.utils,{$danmuku:i}=this.template;return e(t,i)}append(t,e){if(!this.lifecycle.active)return;const{append:i}=this.utils;[...t.children].includes(e)||i(t,e)}setData(t,e){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,{$mount:n}=this.template;this.lifecycle.write(i.dataset,t,e),this.outside&&this.lifecycle.write(n.dataset,t,e)}createTemplate(){const{createElement:t,tooltip:e}=this.utils,i=t("div");this.template.$danmuku=i,i.className="artplayer-plugin-danmuku",i.innerHTML=this.TEMPLATE,this.template.$toggle=this.query(".apd-toggle"),this.template.$config=this.query(".apd-config"),this.template.$configPanel=this.query(".apd-config-panel"),this.template.$configModes=this.query(".apd-config-mode .apd-modes"),this.template.$style=this.query(".apd-style"),this.template.$stylePanel=this.query(".apd-style-panel"),this.template.$styleModes=this.query(".apd-style-mode .apd-modes"),this.template.$colors=this.query(".apd-colors"),this.template.$antiOverlap=this.query(".apd-anti-overlap"),this.template.$syncVideo=this.query(".apd-sync-video"),this.template.$opacitySlider=this.query(".apd-config-opacity .apd-slider"),this.template.$opacityValue=this.query(".apd-config-opacity .apd-value"),this.template.$marginSlider=this.query(".apd-config-margin .apd-slider"),this.template.$marginValue=this.query(".apd-config-margin .apd-value"),this.template.$fontSizeSlider=this.query(".apd-config-fontSize .apd-slider"),this.template.$fontSizeValue=this.query(".apd-config-fontSize .apd-value"),this.template.$speedSlider=this.query(".apd-config-speed .apd-slider"),this.template.$speedValue=this.query(".apd-config-speed .apd-value"),this.template.$input=this.query(".apd-input"),this.template.$send=this.query(".apd-send");const{$toggle:n}=this.template;this.lifecycle.on("artplayerPluginDanmuku:show",()=>{e(n,"\u5173\u95ed\u5f39\u5e55")}),this.lifecycle.on("artplayerPluginDanmuku:hide",()=>{e(n,"\u6253\u5f00\u5f39\u5e55")})}createEvents(){const{$toggle:t,$configModes:e,$styleModes:i,$colors:n,$antiOverlap:a,$syncVideo:s,$send:r,$input:o}=this.template;this.lifecycle.proxy(t,"click",()=>{this.danmuku.config({visible:!this.option.visible}),this.reset()}),this.lifecycle.proxy(e,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.option.modes.includes(i)?this.danmuku.config({modes:this.option.modes.filter(t=>t!==i)}):this.danmuku.config({modes:[...this.option.modes,i]}),this.reset()}),this.lifecycle.proxy(a,"click",()=>{this.danmuku.config({antiOverlap:!this.option.antiOverlap}),this.reset()}),this.lifecycle.proxy(s,"click",()=>{this.danmuku.config({synchronousPlayback:!this.option.synchronousPlayback}),this.reset()}),this.lifecycle.proxy(i,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.danmuku.config({mode:i}),this.reset()}),this.lifecycle.proxy(n,"click",t=>{const e=t.target.closest(".apd-color");e&&(this.danmuku.config({color:e.dataset.color}),this.reset())}),this.lifecycle.proxy(r,"click",()=>this.emit()),this.lifecycle.proxy(o,"keypress",t=>{"Enter"===t.key&&(t.preventDefault(),this.emit())})}createSliders(){this.slider.opacity=this.createSlider({...this.OPACITY,container:this.template.$opacitySlider,findIndex:()=>Math.round(100*this.option.opacity),onChange:t=>{const{$opacityValue:e}=this.template;e.textContent=`${t}%`,this.danmuku.config({opacity:t/100})}}),this.slider.margin=this.createSlider({...this.MARGIN,container:this.template.$marginSlider,findIndex:()=>this.MARGIN.steps.findIndex(t=>t.value[0]===this.option.margin[0]&&t.value[1]===this.option.margin[1]),onChange:t=>{const e=this.MARGIN.steps[t];if(!e)return;const{$marginValue:i}=this.template;i.textContent=e.name,this.danmuku.config({margin:e.value})}}),this.slider.fontSize=this.createSlider({...this.FONT_SIZE,container:this.template.$fontSizeSlider,findIndex:()=>this.danmuku.fontSize,onChange:t=>{const{$fontSizeValue:e}=this.template;e.textContent=`${t}px`,t!==this.danmuku.fontSize&&this.danmuku.config({fontSize:t})}}),this.slider.speed=this.createSlider({...this.SPEED,container:this.template.$speedSlider,findIndex:()=>this.SPEED.steps.findIndex(t=>t.value===this.option.speed),onChange:t=>{const e=this.SPEED.steps[t];if(!e)return;const{$speedValue:i}=this.template;i.textContent=e.name,this.danmuku.config({speed:e.value})}})}createSlider(t){return C.call(this,t)}onFullscreen(t){if(!this.lifecycle.active)return;const{$danmuku:e,$controlsCenter:i,$mount:n}=this.template;this.outside?t?this.append(i,e):this.append(n,e):this.append(i,e)}onMouseEnter({$control:t,$panel:e}){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,n=t.getBoundingClientRect(),a=e.getBoundingClientRect(),s=i.getBoundingClientRect(),r=a.width/2-n.width/2,o=s.left-(n.left-r),l=n.right+r-s.right;e.style.left=o>0?`${-r+o}px`:l>0?-r-l+"px":-r+"px"}emit(){return async function(t){const{lifecycle:e,template:{$input:i}}=t;if(!e.active)return;const n=i.value.trim();if(!n.length||t.isLock||t.emitting)return;const a={text:n,mode:t.option.mode,color:t.option.color,time:t.art.currentTime},s=t=>{e.active&&console.error("Error emitting danmuku:",t)};try{t.emitting=!0;const n=await e.wait(t.option.beforeEmit(a));if(!e.active)return;if(t.emitting=!1,!0!==n)return;if(a.border=!0,delete a.time,Promise.resolve(t.danmuku.emit(a)).catch(s),!e.active)return;i.value="",t.lock()}catch(r){s(r),e.active&&(t.emitting=!1)}}(this)}lock(){!function(t){if(!t.lifecycle.active)return;const{addClass:e}=t.utils,{$send:i}=t.template;clearTimeout(t.timer),t.isLock=!0;let n=t.option.lockTime;i.textContent=n,e(i,"apd-lock");const a=()=>{t.timer=setTimeout(()=>{t.lifecycle.active&&(0===n?t.unlock():(n-=1,i.textContent=n,a()))},1e3)};a()}(this)}unlock(){!function(t){if(clearTimeout(t.timer),t.timer=null,t.isLock=!1,!t.lifecycle.active)return;const{removeClass:e}=t.utils,{$send:i}=t.template;i.textContent="\u53d1\u9001",e(i,"apd-lock")}(this)}resize(){if(!this.lifecycle.active)return;if(this.outside)return;if(this.art.fullscreen)return;if(this.art.fullscreenWeb)return;const{$player:t,$controlsCenter:e}=this.art.template,{$danmuku:i}=this.template;this.art.widtht.dataset.color===this.option.color.toUpperCase());s&&t(s,"apd-active"),e(i,this.option.visible?"\u5173\u95ed\u5f39\u5e55":"\u6253\u5f00\u5f39\u5e55"),this.resize()}mount(t){if(!this.lifecycle.active)return;const{errorHandle:e}=this.utils,i="string"===typeof t?document.querySelector(t):t;e(i,`Can not find the mount point: ${t}`),this.append(i,this.template.$danmuku),this.template.$mount=i,this.reset()}destroy(){if(this.lifecycle.closed)return;this.lifecycle.close(),clearTimeout(this.timer),this.timer=null,this.emitting=!1,this.isLock=!1;const t=this.template.$danmuku;t?.parentElement&&t.parentElement.removeChild(t)}}function Z(t){return e=>{const i=new k(e,t);let n;try{n=e.isDestroy?void 0:new R(e,i),!e.isDestroy&&i.option.heatmap&&z(e,i,i.option.heatmap)}catch(a){try{n?.destroy()}catch{}try{i.destroy()}catch{}throw a}return{name:"artplayerPluginDanmuku",emit:i.emit.bind(i),load:i.load.bind(i),config:i.config.bind(i),hide:i.hide.bind(i),show:i.show.bind(i),reset:i.reset.bind(i),mount:n?n.mount.bind(n):()=>{},get option(){return i.option},get isHide(){return i.isHide},get isStop(){return i.isStop}}}}return Z.icons=R.icons,Z}); diff --git a/packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.legacy.js b/packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.legacy.js index 8f98323ce..4811a82a9 100644 --- a/packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.legacy.js +++ b/packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.legacy.js @@ -4,4 +4,4 @@ * (c) 2017-2026 Harvey Zhao * Released under the MIT License. */ -!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDanmuku=e(),define(function(){return t.artplayerPluginDanmuku})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDanmuku=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.defineProperties,i=Object.getOwnPropertyDescriptors,n=Object.getOwnPropertySymbols,a=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,r=Math.pow,o=(e,i,n)=>i in e?t(e,i,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[i]=n,l=(t,e)=>{for(var i in e||(e={}))a.call(e,i)&&o(t,i,e[i]);if(n)for(var i of n(e))s.call(e,i)&&o(t,i,e[i]);return t},d=(t,n)=>e(t,i(n)),c=(t,e,i)=>new Promise((n,a)=>{var s=t=>{try{o(i.next(t))}catch(e){a(e)}},r=t=>{try{o(i.throw(t))}catch(e){a(e)}},o=t=>t.done?n(t.value):Promise.resolve(t.value).then(s,r);o((i=i.apply(t,e)).next())});function p(t){return t instanceof Promise||"[object Promise]"===Object.prototype.toString.call(t)}function h(t){switch(t){case 1:case 2:case 3:default:return 0;case 4:return 2;case 5:return 1}}function u(t){if("string"!==typeof t)return[];const e=new RegExp(']*?p="(?

[^"]+)"[^>]*>(?.*?)<\\/d>',"gs"),i=t.matchAll(e);return Array.from(i).map(t=>{const e=t.groups.p.split(",");if(e.length>=8){return{text:t.groups.text.trim().replaceAll(""",'"').replaceAll("'","'").replaceAll("<","<").replaceAll(">",">").replaceAll("&","&"),time:Number(e[0]),mode:h(Number(e[1])),fontSize:Number(e[2]),color:`#${Number(e[3]).toString(16)}`,timestamp:Number(e[4]),pool:Number(e[5]),userID:e[6],rowID:Number(e[7])}}return null}).filter(Boolean)}function m({data:t}){const{xml:e,id:i}=t,n=u(e);globalThis.postMessage({danmus:n,id:i})}let f=0;function g(t,{signal:e,onCancel:i}={}){return new Promise((n,a)=>{let s,r,o,l=!1;const d=++f,p=()=>y(void 0,function(t){if(void 0!==(null==t?void 0:t.reason))return t.reason;const e=new Error("Bilibili Danmu loading cancelled");return e.name="AbortError",e}(e));function g(){let t,e=!1;try{if(s){const t=s;s=void 0,t.onmessage=null,t.onerror=null,t.onmessageerror=null,t.terminate()}}catch(i){t=i,e=!0}if(void 0!==r){const n=r;r=void 0;try{URL.revokeObjectURL(n)}catch(i){e||(t=i),e=!0}}if(e)throw t}function y(t,i){if(l)return;l=!0;let s=arguments.length>1;null==e||e.removeEventListener("abort",p),null==o||o();try{g()}catch(r){s||(i=r),s=!0}s?a(i):n(t)}function v(t,e){if(!l){console.error("Error parsing Bilibili Danmu:",e);try{y(u(t))}catch(i){y(void 0,i)}}}(null==e?void 0:e.aborted)?p():(null==e||e.addEventListener("abort",p),o=null==i?void 0:i(p),l?null==o||o():function(){return c(this,null,function*(){const i=e?yield fetch(t,{signal:e}):yield fetch(t);if(l)return;const n=yield i.text();if(!l)try{const t=`\n ${h.toString()}\n ${u.toString()}\n onmessage = ${m.toString()}\n `;if(r=URL.createObjectURL(new Blob([t],{type:"application/javascript"})),l)return void g();if(s=new Worker(r),l)return void g();s.onmessage=t=>{var e;if(l||(null==(e=t.data)?void 0:e.id)!==d)return;const{danmus:i}=t.data;Array.isArray(i)?y(i):y(void 0,new Error("Invalid Bilibili Danmu worker response"))},s.onerror=t=>{var e;null==(e=t.preventDefault)||e.call(t),v(n,t.error||t)},s.onmessageerror=t=>v(n,t.error||t),s.postMessage({xml:n,id:d})}catch(a){v(n,a)}})}().catch(t=>y(void 0,t)))})}const y=new WeakMap,v=Symbol("cancelled danmuku input");function w(t){return y.has(t)||y.set(t,{closed:!1,tasks:new Set,emitting:[]}),y.get(t)}function k(t){const e=w(t),i=e.emitting[e.emitting.length-1];return!e.closed&&!t.art.isDestroy&&(!i||i.active())}function x(t){const e=w(t);e.closed=!0;for(const i of[...e.tasks])i.cancel()}function b(t,e,i){const n=t.states[e]||[];for(let a=0;a{const a=n.$ref.offsetTop,s=this.owner.getLeft(n.$ref)-i,r=n.$ref.clientHeight,o=n.$ref.clientWidth,l=s+o,d=e-l,c=l/n.$restTime,p={};p.top=a,p.left=s,p.height=r,p.width=o,p.right=d,p.speed=c,p.distance=l,p.time=n.$restTime,p.mode=n.mode,t.push(p)}),t}left(t){const e=t.getBoundingClientRect();return this.owner.isRotate?e.top:e.left}makeWait(t){this.owner.setState(t,"wait"),t.$ref&&(t.$ref.style.cssText=this.owner.constructor.cssText,t.$ref.style.visibility="hidden",t.$ref.style.marginLeft="0px",t.$ref.style.transform="translateX(0px)",t.$ref.style.transition="transform 0s linear 0s",this.owner.$refs.push(t.$ref),t.$ref=null)}resize(){const{clientWidth:t}=this.owner.$player;this.owner.filter("stop",e=>{if(0===e.mode)e.$ref.style.left=`${t}px`}),this.owner.filter("emit",e=>{switch(e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.left=`${t}px`,e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}})}continue(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("stop",e=>{switch(this.owner.setState(e,"emit"),e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}}),this}suspend(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("emit",e=>{switch(this.owner.setState(e,"stop"),e.mode){case 0:{const i=t-(this.owner.getLeft(e.$ref)-this.owner.getLeft(this.owner.$player));e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition="transform 0s linear 0s";break}}}),this}prepare(t,e){const i=this.owner,n=i.$ref;return e.danmu=t,e.ref=n,t.$ref=n,n.textContent=t.text,i.$danmuku.appendChild(n),n.style.opacity=i.option.opacity,n.style.fontSize=`${i.fontSize}px`,n.style.color=t.color,n.style.border=t.border?`1px solid ${t.color}`:null,n.style.backgroundColor=t.border?"rgb(0 0 0 / 50%)":null,i.utils.setStyles(n,t.style),n}place(t,e,i,n,a){switch(e.style.top=`${i}px`,e.style.visibility="visible",e.dataset.mode=t.mode,e.dataset.id=t.id||"",t.mode){case 0:e.style.left=`${a}px`,e.style.marginLeft="0px",e.style.transform=`translateX(${-n}px)`,e.style.transition=`transform ${t.$restTime}s linear 0s`;break;case 1:case 2:e.style.left="50%",e.style.marginLeft=`-${e.clientWidth/2}px`}}release(t){const{danmu:e,ref:i}=t;i&&e.$ref===i&&(i.style.cssText=this.owner.constructor.cssText,i.style.visibility="hidden",this.owner.$refs.push(i),e.$ref=null),t.ref=null}clear(){this.destroy(),this.owner.$refs=[],this.owner.$danmuku.textContent=""}destroy(){for(const t of this.nodes)t.parentElement&&t.parentElement.removeChild(t);for(const t of this.owner.queue)t.$ref=null;this.owner.$refs.length=0,this.nodes.clear()}}const M=Symbol("cancelled danmuku frame");class S{constructor(t){this.owner=t,this.generation=0,this.starts=0,this.frame=null,this.operation=null,this.closed=!1,this.running=!1,this.fault=!1,this.failedItems=new Set}active(t){const e=this.owner;return!this.closed&&!this.fault&&!e.art.isDestroy&&!e.isStop&&!e.isHide&&t.generation===this.generation}release(t){this.owner.renderer.release(t)}invalidate(){var t;if(this.generation++,null!==this.frame&&window.cancelAnimationFrame(this.frame),this.frame=null,this.owner.timer=null,this.operation){const t=this.operation;this.operation=null,t.cancel(),this.release(t)}null==(t=this.owner.workerClient)||t.cancel(),this.failedItems.clear()}report(t){try{this.owner.art.emit("artplayerPluginDanmuku:error",t)}catch(e){console.warn("Failed to report danmuku scheduling error:",e)}}fail(t){this.closed||this.fault||(this.fault=!0,this.invalidate(),this.report(t))}recover(){var t;if(!this.closed&&!this.owner.art.isDestroy&&(this.failedItems.clear(),this.fault))try{null==(t=this.owner.workerClient)||t.dispose(),this.owner.createWorker(),this.fault=!1}catch(e){this.report(e)}}schedule(){const t=this.owner;!this.running||this.closed||this.fault||t.art.isDestroy||t.isStop||null!==this.frame||this.operation||(this.frame=window.requestAnimationFrame(()=>{if(this.frame=null,t.timer=null,this.closed||this.fault||t.isStop||t.art.isDestroy)return;let e;const i=new Promise(t=>e=()=>t(M)),n={generation:this.generation,cancel:e,wait:t=>Promise.race([t,i]),ref:null};return this.operation=n,this.run(n).catch(t=>{this.active(n)&&this.fail(t)}).finally(()=>{this.release(n),this.operation===n&&(this.operation=null,this.schedule())})}),t.timer=this.frame)}run(t){return c(this,null,function*(){const e=this.owner;if(!e.art.playing||!this.active(t))return;e.filter("emit",t=>{const i=(Date.now()-t.$lastStartTime)/1e3;t.$restTime-=i,t.$lastStartTime=Date.now(),t.$restTime<=0&&e.makeWait(t)});const i=e.readys;for(const a of i){if(!this.active(t))return;if(this.failedItems.has(a))continue;let i;try{i=yield t.wait(e.option.beforeVisible(a))}catch(n){if(!this.active(t))return;this.failedItems.add(a),this.report(n);continue}if(!this.active(t))return;if(!i)continue;const{clientWidth:s,clientHeight:r}=e.$player,o=e.renderer.prepare(a,t);if(!this.active(t))return;a.$lastStartTime=Date.now(),a.$restTime=e.speed;const l=s+o.clientWidth,{result:d}=yield t.wait(e.postMessage({type:"getDanmuTop",target:{mode:a.mode,height:o.clientHeight,speed:l/a.$restTime},visibles:e.visibles,antiOverlap:e.option.antiOverlap,clientWidth:s,clientHeight:r,marginBottom:e.marginBottom,marginTop:e.marginTop}));if(!this.active(t)||a.$ref!==o)return;void 0!==d?(e.setState(a,"emit"),e.renderer.place(a,o,d,l,s),t.ref=null,e.art.emit("artplayerPluginDanmuku:visible",a)):(e.setState(a,"ready"),this.release(t))}})}destroy(){this.closed=!0,this.invalidate()}}let z=0;class E{constructor(t,e){this.pending=new Map,this.closed=!1,this.failed=!1,this.onFailure=e,this.worker=t();try{this.worker.onmessage=t=>{var e;const i=this.pending.get(null==(e=t.data)?void 0:e.id);i&&(this.pending.delete(t.data.id),i.resolve(t.data))},this.worker.onerror=t=>{var e;null==(e=t.preventDefault)||e.call(t),this.fail(t.error||t)},this.worker.onmessageerror=t=>this.fail(t.error||t)}catch(i){try{this.dispose()}catch(n){}throw i}}request(t){t.id=++z;const{id:e}=t;return this.closed||this.failed?Promise.resolve({id:e,result:void 0}):new Promise((i,n)=>{this.pending.set(e,{resolve:i,reject:n});try{this.worker.postMessage(t)}catch(a){this.fail(a)}})}fail(t){if(!this.closed&&!this.failed){this.failed=!0;for(const e of this.pending.values())e.reject(t);this.pending.clear();try{this.dispose()}catch(e){}this.onFailure(t)}}cancel(){for(const[t,e]of this.pending)e.resolve({id:t,result:void 0});this.pending.clear()}dispose(){if(this.closed)return;this.closed=!0,this.cancel();let t,e=!1;const i=i=>{try{i()}catch(n){e||(t=n),e=!0}};for(const n of["onmessage","onerror","onmessageerror"])i(()=>this.worker[n]=null);if(i(()=>this.worker.terminate()),e)throw t}}const F='/*!\n * artplayer-plugin-danmuku.js v5.3.0\n * Github: https://github.com/zhw2590582/ArtPlayer\n * (c) 2017-2026 Harvey Zhao\n * Released under the MIT License.\n */\nfunction t({target:t,visibles:e,clientWidth:i,clientHeight:h,marginBottom:n,marginTop:o,antiOverlap:s}){const r=h-n,p=e.filter(e=>e.mode===t.mode&&e.top<=r).sort((t,e)=>t.top-e.top);if(0===p.length)return 2===t.mode?r-t.height:o;if(p.unshift({type:"top",top:0,left:0,right:0,height:o,width:i,speed:0,distance:i}),p.push({type:"bottom",top:r,left:0,right:0,height:n,width:i,speed:0,distance:i}),2===t.mode)for(let g=p.length-2;g>=0;g-=1){const e=p[g],i=p[g+1],h=e.top+e.height;if(i.top-h>=t.height)return i.top-t.height}else for(let g=1;g=t.height)return h}const a=[];for(let g=1;g{const i=Math.min(...e.map(t=>t.right)),h=Math.min(...t.map(t=>t.right));return i*e.length-h*t.length});break;case 1:case 2:a.sort((t,e)=>{const i=Math.max(...e.map(t=>t.width));return Math.max(...t.map(t=>t.width))*t.length-i*e.length})}return a[0][0].top}switch(t.mode){case 0:{const e=a.find(e=>e.every(e=>{if(ie.time}));return e&&e[0]?e[0].top:void 0}case 1:case 2:return}}onmessage=e=>{const{data:i}=e;if(!i.id||!i.type)return;const h=(0,{getDanmuTop:t}[i.type])(i);globalThis.postMessage({result:h,id:i.id})};\n',C="undefined"!==typeof self&&self.Blob&&new Blob(["URL.revokeObjectURL(import.meta.url);",F],{type:"text/javascript;charset=utf-8"});function D(t){let e;try{if(e=C&&(self.URL||self.webkitURL).createObjectURL(C),!e)throw"";const i=new Worker(e,{type:"module",name:null==t?void 0:t.name});return i.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(e)}),i}catch(i){return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(F),{type:"module",name:null==t?void 0:t.name})}}class P{constructor(t,e){const{constructor:i,template:n}=t;this.utils=i.utils,this.validator=i.validator,this.$danmuku=n.$danmuku,this.$player=n.$player,this.art=t,this.queue=[],this.$refs=[],this.isStop=!1,this.isHide=!1,this.timer=null,this.index=0,this.worker=null,this.workerClient=null,this.renderer=new $(this),this.scheduler=new S(this),this.option=P.option,this.states={wait:[],ready:[],emit:[],stop:[]},this.start=this.start.bind(this),this.stop=this.stop.bind(this),this.reset=this.reset.bind(this),this.resize=this.resize.bind(this),this.destroy=this.destroy.bind(this),this.seek=this.seek.bind(this),k(this),t.on("destroy",this.destroy);try{if(this.config(e,!0),!k(this))return;this.createWorker()}catch(a){throw t.off("destroy",this.destroy),x(this),this.scheduler.destroy(),a}t.on("video:play",this.start),t.on("video:playing",this.start),t.on("video:pause",this.stop),t.on("video:waiting",this.stop),t.on("resize",this.resize),t.on("video:seeking",this.seek),this.load().catch(t=>console.warn("Failed to load initial danmuku:",t))}static get option(){return{danmuku:[],speed:5,margin:[10,"25%"],opacity:1,color:"#FFFFFF",mode:0,modes:[0,1,2],fontSize:25,antiOverlap:!0,synchronousPlayback:!1,mount:void 0,heatmap:!1,width:512,points:[],filter:()=>!0,beforeEmit:()=>!0,beforeVisible:()=>!0,visible:!0,emitter:!0,maxLength:200,lockTime:5,theme:"dark",OPACITY:{},FONT_SIZE:{},MARGIN:{},SPEED:{},COLOR:[]}}static get scheme(){return{danmuku:"array|function|string|promise",speed:"number",margin:"array",opacity:"number",color:"string",mode:"number",modes:"array",fontSize:"number|string",antiOverlap:"boolean",synchronousPlayback:"boolean",mount:"?htmldivelement|string",heatmap:"object|boolean",width:"number",points:"array",filter:"function",beforeEmit:"function",beforeVisible:"function",visible:"boolean",emitter:"boolean",maxLength:"number",lockTime:"number",theme:"string",OPACITY:"object",FONT_SIZE:"object",MARGIN:"object",SPEED:"object",COLOR:"array"}}static get cssText(){return'\n user-select: none;\n position: absolute;\n white-space: pre;\n pointer-events: none;\n perspective: 500px;\n display: inline-block;\n will-change: transform;\n font-weight: normal;\n line-height: 1.125;\n visibility: hidden;\n font-family: SimHei, "Microsoft JhengHei", Arial, Helvetica, sans-serif;\n text-shadow: rgb(0, 0, 0) 1px 0px 1px, rgb(0, 0, 0) 0px 1px 1px, rgb(0, 0, 0) 0px -1px 1px, rgb(0, 0, 0) -1px 0px 1px;\n '}get isRotate(){var t,e;return null==(e=null==(t=this.art.plugins)?void 0:t.autoOrientation)?void 0:e.state}get marginTop(){const{clamp:t}=this.utils,e=this.option.margin[0],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return P.option.margin[0]}get marginBottom(){const{clamp:t}=this.utils,e=this.option.margin[1],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return P.option.margin[1]}get fontSize(){const{clamp:t}=this.utils,{clientHeight:e}=this.$player,i=this.option.fontSize;if("number"===typeof i)return Math.round(t(i,12,e));if("string"===typeof i&&i.endsWith("%")){const n=Number.parseFloat(i)/100;return Math.round(t(e*n,12,e))}return P.option.fontSize}get $ref(){return this.renderer.acquire()}get readys(){return function(t){const{currentTime:e}=t.art,i=[];return b(t,"ready",t=>i.push(t)),b(t,"wait",t=>{e+.1>=t.time&&t.time>=e-.1&&i.push(t)}),i}(this)}get visibles(){return this.renderer.visibles}get speed(){return this.option.synchronousPlayback&&this.art.playbackRate?this.option.speed/Number(this.art.playbackRate):this.option.speed}load(t){return c(this,null,function*(){const{errorHandle:e}=this.utils,i=function(t,e){const i=w(t);if(e)for(const d of[...i.tasks])d.replace&&d.cancel();let n,a=i.closed||t.art.isDestroy;const s=new Promise(t=>n=t),r=new Set,o="function"===typeof AbortController?new AbortController:void 0,l={replace:e,signal:null==o?void 0:o.signal,active:()=>!a&&!i.closed&&!t.art.isDestroy,onCancel:t=>l.active()?(r.add(t),()=>r.delete(t)):(t(),()=>{}),cancel(){if(!a){a=!0,n(v),null==o||o.abort();for(const t of[...r])t();r.clear()}},wait:t=>Promise.race([t,s]),emit(e){i.emitting.push(l);try{return t.emit(e)}finally{i.emitting.pop()}},finish(){i.tasks.delete(l),r.clear()}};return a?n(v):i.tasks.add(l),l}(this,void 0===t);try{if(!i.active())return this;const n=function(t,e){return"function"===typeof t?{asynchronous:!0,value:t()}:p(t)?{asynchronous:!0,value:t}:"string"===typeof t?{asynchronous:!0,value:g(t,e)}:{asynchronous:!1,value:Array.isArray(t)?t:[]}}(t||this.option.danmuku,i),a=n.asynchronous?yield i.wait(n.value):n.value;if(!i.active())return this;if(e(Array.isArray(a),"Danmuku need return an array as result"),void 0===t){if(this.reset(),!i.active())return this;this.queue=[],this.states={wait:[],ready:[],emit:[],stop:[]},this.renderer.clear()}for(let t=0;t{const e=a[t],i=s[t];return"function"===typeof e||"function"===typeof i||p(e)||p(i)?e!==i:JSON.stringify(e)!==JSON.stringify(i)}))&&!e)return this;const r=function(t,e,{defaults:i,validate:n,clamp:a,mount:s}){const r=Object.assign({},i,t,e);return n(r),r.mode=a(r.mode,0,2),r.speed=a(r.speed,1,10),r.opacity=a(r.opacity,0,1),r.lockTime=a(r.lockTime,1,60),r.maxLength=a(r.maxLength,1,1e3),r.mount=r.mount||s,r}(this.option,t,{defaults:P.option,validate:t=>this.validator(t,P.scheme),clamp:i,mount:n});if(!k(this))return this;const o=this.option.beforeVisible!==r.beforeVisible;return this.option=r,o&&(this.scheduler.invalidate(),this.scheduler.schedule()),t.fontSize&&(this.reset(),!k(this))||(this.option.visible?this.show():this.hide(),k(this)&&this.art.emit("artplayerPluginDanmuku:config",this.option)),this}getLeft(t){return this.renderer.left(t)}postMessage(t={}){return this.workerClient.request(t)}createWorker(){this.workerClient=new E(()=>new D,t=>this.scheduler.fail(t)),this.worker=this.workerClient.worker}filter(t,e){return b(this,t,e)}setState(t,e){!function(t,e,i){t.states[e.$state]=t.states[e.$state].filter(t=>t!==e),e.$state=i,e.$ref&&(e.$ref.dataset.state=i),t.states[i].push(e)}(this,t,e)}makeWait(t){this.renderer.makeWait(t)}update(){return this.scheduler.running=!0,this.scheduler.schedule(),this}resize(){this.renderer.resize()}continue(){return this.renderer.continue(),this}suspend(){return this.renderer.suspend(),this}stop(){return this.isStop=!0,this.scheduler.running=!1,this.scheduler.invalidate(),this.suspend(),this.art.emit("artplayerPluginDanmuku:stop"),this}start(){if(this.scheduler.closed||this.art.isDestroy)return this;const t=++this.scheduler.starts,e=this.scheduler.generation;this.isStop=!1,this.scheduler.recover();return this.scheduler.closed||this.art.isDestroy||this.isStop||this.scheduler.fault||e!==this.scheduler.generation||t!==this.scheduler.starts||(this.continue(),this.update(),this.art.emit("artplayerPluginDanmuku:start")),this}reset(){return this.scheduler.invalidate(),this.queue.forEach(t=>this.makeWait(t)),this.art.emit("artplayerPluginDanmuku:reset"),this.scheduler.recover(),this.scheduler.schedule(),this}seek(){this.scheduler.invalidate(),this.scheduler.schedule()}show(){return this.isHide=!1,this.$danmuku.style.opacity=1,this.option.visible=!0,this.art.emit("artplayerPluginDanmuku:show"),this}hide(){return this.isHide=!0,this.scheduler.invalidate(),this.$danmuku.style.opacity=0,this.option.visible=!1,this.art.emit("artplayerPluginDanmuku:hide"),this.scheduler.schedule(),this}destroy(){var t;x(this),this.scheduler.destroy();try{try{this.stop()}finally{null==(t=this.workerClient)||t.dispose()}}finally{this.renderer.destroy(),this.art.off("video:play",this.start),this.art.off("video:playing",this.start),this.art.off("video:pause",this.stop),this.art.off("video:waiting",this.stop),this.art.off("resize",this.resize),this.art.off("video:seeking",this.seek),this.art.off("destroy",this.destroy),this.art.emit("artplayerPluginDanmuku:destroy")}}}const T=4294967295;function A(t,e){let i=0,n=t.length;for(;iNumber.isFinite(t)&&t>0))return null;const o={xMin:0,xMax:t,yMin:0,yMax:128,scale:.25,opacity:.2,minHeight:Math.floor(.05*e),sampling:Math.max(1,Math.floor(t/100)),smoothing:.2,flattening:.2},l=Object.assign({},"object"===typeof a?a:{}),d=Object.assign({},o,l);for(const r of Object.keys(o))Number.isFinite(d[r])||(d[r]=o[r]);if(d.sampling<=0&&(d.sampling=o.sampling),d.xMin===d.xMax||d.yMin===d.yMax)return null;const c=Array.isArray(s)&&s.length>0,p=c?[...s]:function(t,e,i,n){if(!Number.isFinite(e)||e<=0||!Number.isFinite(i)||i<=0||!Number.isFinite(n)||n<=0||Math.floor(e/n)+1>T)return[];const a=t.map(t=>t.time).filter(t=>"number"===typeof t&&!Number.isNaN(t)).sort((t,e)=>t-e),s=[],r=i/e;if(!Number.isFinite(r))return[];for(let o=0;o<=e;o+=n){if(s.length>=T||o+n===o)return[];const t=o*r,e=(o+n)*r;s.push([o,A(a,e)-A(a,t)])}return s}(n,t,i,d.sampling);if(!p.length||!p.every(t=>Array.isArray(t)&&Number.isFinite(t[0])&&Number.isFinite(t[1])))return null;const h=p[p.length-1];if(h[0]!==t){if(p.length===T)return null;p.push([t,h[1]])}let u=1/0,m=-1/0;for(const r of p)u=Math.min(u,r[1]),m=Math.max(m,r[1]);const f=(u+m)/2;for(const r of p){const t=r[1];r[1]=t*(t>f?1+d.scale:1-d.scale)+d.minHeight}const g=!c&&!Number.isFinite(l.yMin)&&!Number.isFinite(l.yMax);let y=0;for(const r of p)y=Math.max(y,r[1]);const v=g&&y>o.yMax/4;v&&(d.yMax=4*y);const w=t=>v?Math.min(e,Math.max(.75*e,t)):t,k=(t,e,i,n)=>{const a=function(t,e){const i=e[0]-t[0],n=e[1]-t[1];return{length:Math.sqrt(r(i,2)+r(n,2)),angle:Math.atan2(n,i)}}(e||t,i||t),s=L(Math.cos(a.angle)*d.flattening,0,1,1,0),o=a.angle*s+(n?Math.PI:0),l=a.length*d.smoothing;return[t[0]+Math.cos(o)*l,w(t[1]+Math.sin(o)*l)]},x=p.map(i=>[L(i[0],d.xMin,d.xMax,0,t),w(L(i[1],d.yMin,d.yMax,e,0))]);if(!x.every(t=>t.every(Number.isFinite)))return null;const b=x.map((t,i,n)=>{if(0===i)return`M ${n[n.length-1][0]},${e} L ${t[0]},${e} L ${t[0]},${t[1]}`;const a=k(n[i-1],n[i-2],t),s=k(t,n[i-1],n[i+1],!0);return`C ${a[0]},${a[1]} ${s[0]},${s[1]} ${t[0]},${t[1]}${i===n.length-1?" z":""}`}).join(" ");return/NaN|Infinity/u.test(b)?null:{path:b,opacity:d.opacity,width:t,height:e}}let H=0;function N(t,e,i){if(t.isDestroy)return;const{query:n}=t.constructor.utils;let a;const s=[];let r,o,l,d=!1;const c=()=>!d&&!t.isDestroy;function p(e,i){s.push([e,i]),t.on(e,i)}function h(e){if(d)return;let i;d=!0,o=null,l=null;let n=!1;const a=t=>{try{t()}catch(e){n||(i=e),n=!0}};for(const[r,o]of s.splice(0))a(()=>t.off(r,o));if(e&&r&&t.controls.heatmap===r&&a(()=>t.controls.remove("heatmap")),r=void 0,n)throw i}function u(t){c()&&o&&l&&(o.setAttribute("offset",100*t+"%"),l.setAttribute("offset",100*t+"%"))}function m(s){try{!function(s=[]){if(!c())return;if(o=null,l=null,r.innerHTML="",t.option.isLive)return;const d=O({width:r.offsetWidth,height:r.offsetHeight,duration:t.duration,queue:e.queue,option:i,points:s});if(!d||!c())return;const p=r.ownerDocument;for(a||(a=p?"heatmap-solids":"heatmap-solids-"+ ++H);null==p?void 0:p.getElementById(a);)a="heatmap-solids-"+ ++H;r.innerHTML=`\n \n \n \n \n \n \n \n \n \n \n \n `,o=n("#heatmap-start",r),l=n("#heatmap-stop",r),u(t.played)}(s)}catch(d){try{h(!0)}catch(p){}throw d}}p("destroy",()=>h(!0));try{t.controls.add({name:"heatmap",position:"top",html:"",style:{position:"absolute",top:"-100px",left:"0px",right:"0px",height:"100px",width:"100%",pointerEvents:"none"},beforeUnmount(){h(!1)},mounted(e){if(r=e,d)return t.controls.heatmap===e&&t.controls.remove("heatmap"),void(r=void 0);c()?(p("video:timeupdate",()=>u(t.played)),p("setBar",(t,e)=>{"played"===t&&u(e)}),p("ready",()=>m()),p("resize",()=>m()),p("artplayerPluginDanmuku:loaded",()=>m()),p("artplayerPluginDanmuku:points",t=>m(t))):h(!0)}})}catch(f){try{h(!0)}catch(g){}throw f}return()=>h(!0)}const V=new WeakMap;class j{constructor(t){this.art=t,this.closed=!1,this.disposers=new Set,this.cancelled=new Promise(t=>this.cancel=t)}get active(){return!this.closed&&!this.art.isDestroy}own(t){this.closed?t():this.disposers.add(t)}write(t,e,i,n=i=>t[e]=i){if(!this.active)return;const a=()=>"function"===typeof t.getPropertyPriority?t.getPropertyPriority(e):void 0;let s=V.get(t);s||(s=new Map,V.set(t,s));let r=s.get(e);r||(r={original:t[e],present:e in t||void 0!==t[e],priority:a(),owners:new Map,apply:n},s.set(e,r)),r.owners.has(this)||this.own(()=>{const i=[...r.owners.keys()],n=i[i.length-1]===this;if(r.owners.delete(this),n&&t[e]===r.last&&a()===r.lastPriority){const i=[...r.owners.values()];i.length?(r.apply(i[i.length-1]),r.last=t[e],r.lastPriority=a()):void 0!==r.priority?t.setProperty(e,r.original,r.priority):r.present?r.apply(r.original):delete t[e]}r.owners.size||(s.delete(e),s.size||V.delete(t))}),r.owners.delete(this),r.owners.set(this,i),r.apply(i),r.last=t[e],r.lastPriority=a()}on(t,e){if(!this.active)return;const i=this;function n(...t){if(i.active)return e.apply(this,t)}const a=()=>this.art.off(t,n);this.own(a);try{this.art.on(t,n),this.active||a()}catch(s){throw this.release(a),s}}proxy(t,e,i){if(!this.active)return;const n=this;function a(...t){if(n.active)return i.apply(this,t)}let s;const r=()=>{var i,n;if("function"===typeof s){const t=this.art.events;"function"===typeof(null==t?void 0:t.remove)&&(null==(i=t.destroyEvents)?void 0:i.has(s))?t.remove(s):s()}else null==(n=t.removeEventListener)||n.call(t,e,a)};this.own(r);try{s=this.art.proxy(t,e,a),this.active||r()}catch(o){throw this.release(r),o}}wait(t){return Promise.race([t,this.cancelled])}release(t){try{t()}catch(e){console.warn("Failed to dispose danmuku setting resource:",e)}}close(){if(!this.closed){this.closed=!0,this.cancel();for(const t of this.disposers)this.release(t);this.disposers.clear()}}}function q({min:t,max:e,container:i,findIndex:n,onChange:a,steps:s=[]}){const{lifecycle:r}=this;if(!r.active)return{reset(){}};const{query:o,clamp:l,setStyle:d}=this.utils;d(i,"touch-action","none"),i.innerHTML=`\n

\n
\n ${s.map(()=>'
').join("")}\n
\n
\n
\n
\n
\n ${s.map(t=>t.hide?"":`
${t.name}
`).join("")}\n
\n `;const c=o(".apd-slider-dot",i),p=o(".apd-slider-progress",i);let h=!1;function u(i){if(!r.active)return;if(void 0===i&&(i=n()),ie)return;const o=(i-t)/(e-t);c.style.left=100*o+"%",0===s.length&&(p.style.width=c.style.left),a(i)}function m(n){const{top:a,height:s,left:r,width:o}=i.getBoundingClientRect();if(this.art.isRotate){const i=l(n.clientY-a,0,s);u(Math.round(i/s*(e-t)+t))}else{const i=l(n.clientX-r,0,o);u(Math.round(i/o*(e-t)+t))}}return r.own(()=>h=!1),r.proxy(i,"click",t=>{m.call(this,t)}),r.proxy(i,"pointerdown",t=>{h=0===t.button}),r.on("document:pointermove",t=>{h&&m.call(this,t)}),r.on("document:pointerup",t=>{h&&(h=!1,m.call(this,t))}),{reset:u}}const I='',R='',B='',Z='',W='',_='',U='',G='',X='',Y='',J='',K='';function Q(){const{option:t}=this;return`\n
\n ${J}${Y}\n
\n
\n ${B}\n
\n
\n
\n \u6309\u7c7b\u578b\u5c4f\u853d\n
\n
\n ${Z}${W}\n
\u6eda\u52a8
\n
\n
\n ${_}${U}\n
\u9876\u90e8
\n
\n
\n ${G}${X}\n
\u5e95\u90e8
\n
\n
\n
\n
\n
\n ${R}${I}\n \u9632\u6b62\u5f39\u5e55\u91cd\u53e0\n
\n
\n ${R}${I}\n \u540c\u6b65\u89c6\u9891\u901f\u5ea6\n
\n
\n
\n \u4e0d\u900f\u660e\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n \u663e\u793a\u533a\u57df\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u5b57\u53f7\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u901f\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n
\n
\n
\n
\n ${K}\n
\n
\n
\n \u6a21\u5f0f\n
\n
\n ${W}\n
\u6eda\u52a8
\n
\n
\n ${U}\n
\u9876\u90e8
\n
\n
\n ${X}\n
\u5e95\u90e8
\n
\n
\n
\n
\n \u989c\u8272\n
\n ${this.COLOR.map(t=>`
`).join("")}\n
\n
\n
\n
\n
\n \n
\u53d1\u9001
\n
\n `}if("undefined"!==typeof document){const t="artplayer-plugin-danmuku",e=Symbol.for("artplayer-plugin-danmuku.pending-style");let i=document.getElementById(t)||document[e];if(!i)if(i=document.createElement("style"),i.id=t,"loading"===document.readyState){document[e]=i;const n=()=>{document.removeEventListener("DOMContentLoaded",n),document[e]===i&&delete document[e];const a=document.getElementById(t);a?a.textContent=i.textContent:(document.head||document.documentElement).appendChild(i)};document.addEventListener("DOMContentLoaded",n,{once:!0})}else(document.head||document.documentElement).appendChild(i);i.textContent='.artplayer-plugin-danmuku{display:flex;position:relative;z-index:99;align-items:center;justify-content:center;font-size:12px;height:32px;width:100%;color:#fff;font-weight:300;flex-shrink:0;gap:10px}.artplayer-plugin-danmuku .apd-icon{cursor:pointer;opacity:.75;transition:all .2s ease;fill:#fff}.artplayer-plugin-danmuku .apd-icon:hover{opacity:1}.artplayer-plugin-danmuku .apd-config{display:flex;position:relative}.artplayer-plugin-danmuku .apd-config .apd-config-panel{position:absolute;bottom:24px;left:0;width:320px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-config .apd-config-panel .apd-config-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-config:hover .apd-config-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-config-mode,.artplayer-plugin-danmuku .apd-config-slider,.artplayer-plugin-danmuku .apd-config-other,.artplayer-plugin-danmuku .apd-style-mode{margin-bottom:15px}.artplayer-plugin-danmuku .apd-modes{display:flex;align-items:center;margin-top:5px;gap:20px}.artplayer-plugin-danmuku .apd-modes .apd-mode{cursor:pointer;text-align:center}.artplayer-plugin-danmuku .apd-modes .apd-mode:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-config-slider{display:flex;align-items:center;gap:12px}.artplayer-plugin-danmuku .apd-config-slider .apd-value{width:32px;text-align:right}.artplayer-plugin-danmuku .apd-slider{position:relative;flex:1;display:flex;height:20px;align-items:center;justify-content:center;cursor:pointer}.artplayer-plugin-danmuku .apd-slider .apd-slider-line{position:relative;height:2px;width:100%;overflow:hidden;border-radius:3px;background-color:#ffffff40}.artplayer-plugin-danmuku .apd-slider .apd-slider-points{position:absolute;inset:0;display:flex;align-items:center;justify-content:space-between}.artplayer-plugin-danmuku .apd-slider .apd-slider-points .apd-slider-point{width:2px;height:2px;border-radius:50%;background-color:#ffffff80}.artplayer-plugin-danmuku .apd-slider .apd-slider-progress{width:0%;height:100%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-dot{position:absolute;transform:translate(-6px);left:0%;width:12px;height:12px;border-radius:50%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps{display:flex;align-items:center;justify-content:space-between;position:absolute;bottom:-12px;width:calc(100% + 32px);color:#777}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps .apd-slider-step{flex-shrink:0;width:36px;text-align:center;scale:.95}.artplayer-plugin-danmuku .apd-config-other{display:flex;align-items:center;gap:20px}.artplayer-plugin-danmuku .apd-config-other .apd-check-off,.artplayer-plugin-danmuku .apd-config-other .apd-check-on{width:16px;height:16px}.artplayer-plugin-danmuku .apd-config-other .apd-other{display:flex;align-items:center;cursor:pointer;gap:2px}.artplayer-plugin-danmuku .apd-config-other .apd-other:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-emitter{display:flex;flex:1;align-items:center;height:100%;background-color:#ffffff40;border-radius:5px}.artplayer-plugin-danmuku .apd-style{position:relative;display:flex;align-items:center;justify-content:center}.artplayer-plugin-danmuku .apd-style .apd-style-panel{position:absolute;bottom:24px;left:0;width:200px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-style .apd-style-panel .apd-style-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-style:hover .apd-style-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-colors{display:flex;flex-wrap:wrap;margin-top:5px;gap:8px}.artplayer-plugin-danmuku .apd-colors .apd-color{width:16px;height:16px;border-radius:2px;cursor:pointer}.artplayer-plugin-danmuku .apd-colors .apd-color.apd-active{border:1px solid black;box-shadow:0 0 0 1px #fff}.artplayer-plugin-danmuku .apd-input{outline:none;height:100%;flex:1;min-width:0;width:auto;border:none;line-height:1;color:#fff;background-color:transparent}.artplayer-plugin-danmuku .apd-input::placeholder{color:#ffffff80}.artplayer-plugin-danmuku .apd-send{display:flex;align-items:center;justify-content:center;height:100%;width:60px;flex-shrink:0;cursor:pointer;text-shadow:none;border-top-right-radius:5px;border-bottom-right-radius:5px;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-send.apd-lock{cursor:not-allowed;color:#666;background-color:#e7e7e7}.art-controls-center .apd-emitter{width:260px;flex:none}.art-fullscreen .artplayer-plugin-danmuku,.art-fullscreen-web .artplayer-plugin-danmuku{height:38px;gap:16px}.art-fullscreen .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen-web .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-on,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-on{width:28px;height:28px}.art-fullscreen .artplayer-plugin-danmuku .apd-emitter,.art-fullscreen-web .artplayer-plugin-danmuku .apd-emitter{width:400px;flex:none}.art-video-player>.artplayer-plugin-danmuku{position:absolute;left:0;right:0;bottom:-40px;padding:0 10px}.art-video-player:has(>.artplayer-plugin-danmuku){margin-bottom:40px}[data-danmuku-emitter=false] .apd-emitter{display:none!important}[data-danmuku-emitter=false] .art-controls-center .artplayer-plugin-danmuku{justify-content:flex-end;gap:18px}[data-danmuku-emitter=false].art-fullscreen .art-controls-center .artplayer-plugin-danmuku,[data-danmuku-emitter=false].art-fullscreen-web .art-controls-center .artplayer-plugin-danmuku{gap:24px}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-icon{fill:#333}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-emitter{background-color:#f1f2f3}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input{color:#000}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input::placeholder{color:#0000004d}[data-danmuku-visible=false] .apd-toggle-off{display:block}[data-danmuku-visible=false] .apd-toggle-on,[data-danmuku-visible=true] .apd-toggle-off{display:none}[data-danmuku-visible=true] .apd-toggle-on{display:block}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-on{display:none}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-off,[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-on{display:block}[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-off,[data-danmuku-sync-video=false] .apd-sync-video .apd-check-on{display:none}[data-danmuku-sync-video=false] .apd-sync-video .apd-check-off,[data-danmuku-sync-video=true] .apd-sync-video .apd-check-on{display:block}[data-danmuku-sync-video=true] .apd-sync-video .apd-check-off{display:none}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-off{display:block}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-on{display:none}[data-danmuku-mode0=false] .art-danmuku [data-mode="0"]{opacity:0!important}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-off{display:none}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-on{display:block}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"]{color:#00a1d6}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"] path{fill:#00a1d6}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-off{display:block}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-on{display:none}[data-danmuku-mode1=false] .art-danmuku [data-mode="1"]{opacity:0!important}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-off{display:none}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-on{display:block}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"]{color:#00a1d6}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"] path{fill:#00a1d6}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-off{display:block}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-on{display:none}[data-danmuku-mode2=false] .art-danmuku [data-mode="2"]{opacity:0!important}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-off{display:none}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-on{display:block}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"]{color:#00a1d6}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"] path{fill:#00a1d6}'}class tt{constructor(t,e){this.art=t,this.danmuku=e,this.utils=t.constructor.utils;const{setStyle:i}=this.utils,{$controlsCenter:n}=t.template;this.template={$controlsCenter:n,$mount:n,$danmuku:null,$toggle:null,$config:null,$configPanel:null,$configModes:null,$style:null,$stylePanel:null,$styleModes:null,$colors:null,$opacitySlider:null,$opacityValue:null,$marginSlider:null,$marginValue:null,$fontSizeSlider:null,$fontSizeValue:null,$speedSlider:null,$speedValue:null,$input:null,$send:null},this.slider={opacity:null,margin:null,fontSize:null,speed:null},this.emitting=!1,this.isLock=!1,this.timer=null,this.lifecycle=new j(t),this.destroy=this.destroy.bind(this),this.lifecycle.own(()=>t.off("destroy",this.destroy));try{if(t.on("destroy",this.destroy),!this.lifecycle.active)return;if(this.lifecycle.write(n.style,"display","flex",t=>i(n,"display",t)),this.createTemplate(),!this.lifecycle.active)return;if(this.createSliders(),!this.lifecycle.active)return;if(this.createEvents(),this.mount(this.option.mount),!this.lifecycle.active)return;this.lifecycle.on("resize",()=>this.resize()),this.lifecycle.on("fullscreen",t=>this.onFullscreen(t)),this.lifecycle.on("fullscreenWeb",t=>this.onFullscreen(t)),this.lifecycle.proxy(this.template.$config,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$config,$panel:this.template.$configPanel})}),this.lifecycle.proxy(this.template.$style,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$style,$panel:this.template.$stylePanel})})}catch(a){try{this.destroy()}catch(s){console.warn("Failed to roll back danmuku setting:",s)}throw a}}static get icons(){return{$on:J,$off:Y,$config:B,$style:K,$mode_0_off:Z,$mode_0_on:W,$mode_1_off:_,$mode_1_on:U,$mode_2_off:G,$mode_2_on:X,$check_on:R,$check_off:I}}get option(){return this.danmuku.option}get outside(){return this.template.$mount!==this.template.$controlsCenter}get TEMPLATE(){return Q.call(this)}get OPACITY(){return l({min:0,max:100,steps:[]},this.option.OPACITY)}get FONT_SIZE(){return l({min:12,max:120,steps:[]},this.option.FONT_SIZE)}get MARGIN(){return l({min:0,max:3,steps:[{name:"1/4",value:[10,"75%"]},{name:"\u534a\u5c4f",value:[10,"50%"]},{name:"3/4",value:[10,"25%"]},{name:"\u6ee1\u5c4f",value:[10,10]}]},this.option.MARGIN)}get SPEED(){return l({min:0,max:4,steps:[{name:"\u6781\u6162",value:10},{name:"\u8f83\u6162",value:7.5,hide:!0},{name:"\u9002\u4e2d",value:5},{name:"\u8f83\u5feb",value:2.5,hide:!0},{name:"\u6781\u5feb",value:1}]},this.option.SPEED)}get COLOR(){return this.option.COLOR.length?this.option.COLOR:["#FE0302","#FF7204","#FFAA02","#FFD302","#FFFF00","#A0EE00","#00CD00","#019899","#4266BE","#89D5FF","#CC0273","#222222","#9B9B9B","#FFFFFF"]}query(t){const{query:e}=this.utils,{$danmuku:i}=this.template;return e(t,i)}append(t,e){if(!this.lifecycle.active)return;const{append:i}=this.utils;[...t.children].includes(e)||i(t,e)}setData(t,e){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,{$mount:n}=this.template;this.lifecycle.write(i.dataset,t,e),this.outside&&this.lifecycle.write(n.dataset,t,e)}createTemplate(){const{createElement:t,tooltip:e}=this.utils,i=t("div");this.template.$danmuku=i,i.className="artplayer-plugin-danmuku",i.innerHTML=this.TEMPLATE,this.template.$toggle=this.query(".apd-toggle"),this.template.$config=this.query(".apd-config"),this.template.$configPanel=this.query(".apd-config-panel"),this.template.$configModes=this.query(".apd-config-mode .apd-modes"),this.template.$style=this.query(".apd-style"),this.template.$stylePanel=this.query(".apd-style-panel"),this.template.$styleModes=this.query(".apd-style-mode .apd-modes"),this.template.$colors=this.query(".apd-colors"),this.template.$antiOverlap=this.query(".apd-anti-overlap"),this.template.$syncVideo=this.query(".apd-sync-video"),this.template.$opacitySlider=this.query(".apd-config-opacity .apd-slider"),this.template.$opacityValue=this.query(".apd-config-opacity .apd-value"),this.template.$marginSlider=this.query(".apd-config-margin .apd-slider"),this.template.$marginValue=this.query(".apd-config-margin .apd-value"),this.template.$fontSizeSlider=this.query(".apd-config-fontSize .apd-slider"),this.template.$fontSizeValue=this.query(".apd-config-fontSize .apd-value"),this.template.$speedSlider=this.query(".apd-config-speed .apd-slider"),this.template.$speedValue=this.query(".apd-config-speed .apd-value"),this.template.$input=this.query(".apd-input"),this.template.$send=this.query(".apd-send");const{$toggle:n}=this.template;this.lifecycle.on("artplayerPluginDanmuku:show",()=>{e(n,"\u5173\u95ed\u5f39\u5e55")}),this.lifecycle.on("artplayerPluginDanmuku:hide",()=>{e(n,"\u6253\u5f00\u5f39\u5e55")})}createEvents(){const{$toggle:t,$configModes:e,$styleModes:i,$colors:n,$antiOverlap:a,$syncVideo:s,$send:r,$input:o}=this.template;this.lifecycle.proxy(t,"click",()=>{this.danmuku.config({visible:!this.option.visible}),this.reset()}),this.lifecycle.proxy(e,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.option.modes.includes(i)?this.danmuku.config({modes:this.option.modes.filter(t=>t!==i)}):this.danmuku.config({modes:[...this.option.modes,i]}),this.reset()}),this.lifecycle.proxy(a,"click",()=>{this.danmuku.config({antiOverlap:!this.option.antiOverlap}),this.reset()}),this.lifecycle.proxy(s,"click",()=>{this.danmuku.config({synchronousPlayback:!this.option.synchronousPlayback}),this.reset()}),this.lifecycle.proxy(i,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.danmuku.config({mode:i}),this.reset()}),this.lifecycle.proxy(n,"click",t=>{const e=t.target.closest(".apd-color");e&&(this.danmuku.config({color:e.dataset.color}),this.reset())}),this.lifecycle.proxy(r,"click",()=>this.emit()),this.lifecycle.proxy(o,"keypress",t=>{"Enter"===t.key&&(t.preventDefault(),this.emit())})}createSliders(){this.slider.opacity=this.createSlider(d(l({},this.OPACITY),{container:this.template.$opacitySlider,findIndex:()=>Math.round(100*this.option.opacity),onChange:t=>{const{$opacityValue:e}=this.template;e.textContent=`${t}%`,this.danmuku.config({opacity:t/100})}})),this.slider.margin=this.createSlider(d(l({},this.MARGIN),{container:this.template.$marginSlider,findIndex:()=>this.MARGIN.steps.findIndex(t=>t.value[0]===this.option.margin[0]&&t.value[1]===this.option.margin[1]),onChange:t=>{const e=this.MARGIN.steps[t];if(!e)return;const{$marginValue:i}=this.template;i.textContent=e.name,this.danmuku.config({margin:e.value})}})),this.slider.fontSize=this.createSlider(d(l({},this.FONT_SIZE),{container:this.template.$fontSizeSlider,findIndex:()=>this.danmuku.fontSize,onChange:t=>{const{$fontSizeValue:e}=this.template;e.textContent=`${t}px`,t!==this.danmuku.fontSize&&this.danmuku.config({fontSize:t})}})),this.slider.speed=this.createSlider(d(l({},this.SPEED),{container:this.template.$speedSlider,findIndex:()=>this.SPEED.steps.findIndex(t=>t.value===this.option.speed),onChange:t=>{const e=this.SPEED.steps[t];if(!e)return;const{$speedValue:i}=this.template;i.textContent=e.name,this.danmuku.config({speed:e.value})}}))}createSlider(t){return q.call(this,t)}onFullscreen(t){if(!this.lifecycle.active)return;const{$danmuku:e,$controlsCenter:i,$mount:n}=this.template;this.outside?t?this.append(i,e):this.append(n,e):this.append(i,e)}onMouseEnter({$control:t,$panel:e}){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,n=t.getBoundingClientRect(),a=e.getBoundingClientRect(),s=i.getBoundingClientRect(),r=a.width/2-n.width/2,o=s.left-(n.left-r),l=n.right+r-s.right;e.style.left=o>0?`${-r+o}px`:l>0?-r-l+"px":-r+"px"}emit(){return function(t){return c(this,null,function*(){const{lifecycle:e,template:{$input:i}}=t;if(!e.active)return;const n=i.value.trim();if(!n.length||t.isLock||t.emitting)return;const a={text:n,mode:t.option.mode,color:t.option.color,time:t.art.currentTime},s=t=>{e.active&&console.error("Error emitting danmuku:",t)};try{t.emitting=!0;const n=yield e.wait(t.option.beforeEmit(a));if(!e.active)return;if(t.emitting=!1,!0!==n)return;if(a.border=!0,delete a.time,Promise.resolve(t.danmuku.emit(a)).catch(s),!e.active)return;i.value="",t.lock()}catch(r){s(r),e.active&&(t.emitting=!1)}})}(this)}lock(){!function(t){if(!t.lifecycle.active)return;const{addClass:e}=t.utils,{$send:i}=t.template;clearTimeout(t.timer),t.isLock=!0;let n=t.option.lockTime;i.textContent=n,e(i,"apd-lock");const a=()=>{t.timer=setTimeout(()=>{t.lifecycle.active&&(0===n?t.unlock():(n-=1,i.textContent=n,a()))},1e3)};a()}(this)}unlock(){!function(t){if(clearTimeout(t.timer),t.timer=null,t.isLock=!1,!t.lifecycle.active)return;const{removeClass:e}=t.utils,{$send:i}=t.template;i.textContent="\u53d1\u9001",e(i,"apd-lock")}(this)}resize(){if(!this.lifecycle.active)return;if(this.outside)return;if(this.art.fullscreen)return;if(this.art.fullscreenWeb)return;const{$player:t,$controlsCenter:e}=this.art.template,{$danmuku:i}=this.template;this.art.widtht.dataset.color===this.option.color.toUpperCase());s&&t(s,"apd-active"),e(i,this.option.visible?"\u5173\u95ed\u5f39\u5e55":"\u6253\u5f00\u5f39\u5e55"),this.resize()}mount(t){if(!this.lifecycle.active)return;const{errorHandle:e}=this.utils,i="string"===typeof t?document.querySelector(t):t;e(i,`Can not find the mount point: ${t}`),this.append(i,this.template.$danmuku),this.template.$mount=i,this.reset()}destroy(){if(this.lifecycle.closed)return;this.lifecycle.close(),clearTimeout(this.timer),this.timer=null,this.emitting=!1,this.isLock=!1;const t=this.template.$danmuku;(null==t?void 0:t.parentElement)&&t.parentElement.removeChild(t)}}function et(t){return e=>{const i=new P(e,t);let n;try{n=e.isDestroy?void 0:new tt(e,i),!e.isDestroy&&i.option.heatmap&&N(e,i,i.option.heatmap)}catch(a){try{null==n||n.destroy()}catch(s){}try{i.destroy()}catch(s){}throw a}return{name:"artplayerPluginDanmuku",emit:i.emit.bind(i),load:i.load.bind(i),config:i.config.bind(i),hide:i.hide.bind(i),show:i.show.bind(i),reset:i.reset.bind(i),mount:n?n.mount.bind(n):()=>{},get option(){return i.option},get isHide(){return i.isHide},get isStop(){return i.isStop}}}}return et.icons=tt.icons,et}); +!function(t,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"==typeof define&&define.amd?(t.artplayerPluginDanmuku=e(),define(function(){return t.artplayerPluginDanmuku})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginDanmuku=e()}(this,function(){"use strict";var t=Object.defineProperty,e=Object.defineProperties,i=Object.getOwnPropertyDescriptors,n=Object.getOwnPropertySymbols,a=Object.prototype.hasOwnProperty,s=Object.prototype.propertyIsEnumerable,r=Math.pow,o=(e,i,n)=>i in e?t(e,i,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[i]=n,l=(t,e)=>{for(var i in e||(e={}))a.call(e,i)&&o(t,i,e[i]);if(n)for(var i of n(e))s.call(e,i)&&o(t,i,e[i]);return t},d=(t,n)=>e(t,i(n)),c=(t,e,i)=>new Promise((n,a)=>{var s=t=>{try{o(i.next(t))}catch(e){a(e)}},r=t=>{try{o(i.throw(t))}catch(e){a(e)}},o=t=>t.done?n(t.value):Promise.resolve(t.value).then(s,r);o((i=i.apply(t,e)).next())});function p(t){return t instanceof Promise||"[object Promise]"===Object.prototype.toString.call(t)}function h(t){switch(t){case 1:case 2:case 3:default:return 0;case 4:return 2;case 5:return 1}}function u(t){if("string"!==typeof t)return[];const e=new RegExp(']*?p="(?

[^"]+)"[^>]*>(?.*?)<\\/d>',"gs"),i=t.matchAll(e);return Array.from(i).map(t=>{const e=t.groups.p.split(",");if(e.length>=8){return{text:t.groups.text.trim().replaceAll(""",'"').replaceAll("'","'").replaceAll("<","<").replaceAll(">",">").replaceAll("&","&"),time:Number(e[0]),mode:h(Number(e[1])),fontSize:Number(e[2]),color:`#${Number(e[3]).toString(16)}`,timestamp:Number(e[4]),pool:Number(e[5]),userID:e[6],rowID:Number(e[7])}}return null}).filter(Boolean)}function m({data:t}){const{xml:e,id:i}=t,n=u(e);globalThis.postMessage({danmus:n,id:i})}let f=0;function g(t,{signal:e,onCancel:i}={}){return new Promise((n,a)=>{let s,r,o,l=!1;const d=++f,p=()=>y(void 0,function(t){if(void 0!==(null==t?void 0:t.reason))return t.reason;const e=new Error("Bilibili Danmu loading cancelled");return e.name="AbortError",e}(e));function g(){let t,e=!1;try{if(s){const t=s;s=void 0,t.onmessage=null,t.onerror=null,t.onmessageerror=null,t.terminate()}}catch(i){t=i,e=!0}if(void 0!==r){const n=r;r=void 0;try{URL.revokeObjectURL(n)}catch(i){e||(t=i),e=!0}}if(e)throw t}function y(t,i){if(l)return;l=!0;let s=arguments.length>1;null==e||e.removeEventListener("abort",p),null==o||o();try{g()}catch(r){s||(i=r),s=!0}s?a(i):n(t)}function v(t,e){if(!l){console.error("Error parsing Bilibili Danmu:",e);try{y(u(t))}catch(i){y(void 0,i)}}}(null==e?void 0:e.aborted)?p():(null==e||e.addEventListener("abort",p),o=null==i?void 0:i(p),l?null==o||o():function(){return c(this,null,function*(){const i=e?yield fetch(t,{signal:e}):yield fetch(t);if(l)return;const n=yield i.text();if(!l)try{const t=`\n ${h.toString()}\n ${u.toString()}\n onmessage = ${m.toString()}\n `;if(r=URL.createObjectURL(new Blob([t],{type:"application/javascript"})),l)return void g();if(s=new Worker(r),l)return void g();s.onmessage=t=>{var e;if(l||(null==(e=t.data)?void 0:e.id)!==d)return;const{danmus:i}=t.data;Array.isArray(i)?y(i):y(void 0,new Error("Invalid Bilibili Danmu worker response"))},s.onerror=t=>{var e;null==(e=t.preventDefault)||e.call(t),v(n,t.error||t)},s.onmessageerror=t=>v(n,t.error||t),s.postMessage({xml:n,id:d})}catch(a){v(n,a)}})}().catch(t=>y(void 0,t)))})}const y=new WeakMap,v=Symbol("cancelled danmuku input");function w(t){return y.has(t)||y.set(t,{closed:!1,tasks:new Set,emitting:[]}),y.get(t)}function k(t){const e=w(t),i=e.emitting[e.emitting.length-1];return!e.closed&&!t.art.isDestroy&&(!i||i.active())}function x(t){const e=w(t);e.closed=!0;for(const i of[...e.tasks])i.cancel()}function b(t,e,i){const n=t.states[e]||[];for(let a=0;a{const a=n.$ref.offsetTop,s=this.owner.getLeft(n.$ref)-i,r=n.$ref.clientHeight,o=n.$ref.clientWidth,l=s+o,d=e-l,c=l/n.$restTime,p={};p.top=a,p.left=s,p.height=r,p.width=o,p.right=d,p.speed=c,p.distance=l,p.time=n.$restTime,p.mode=n.mode,t.push(p)}),t}left(t){const e=t.getBoundingClientRect();return this.owner.isRotate?e.top:e.left}makeWait(t){this.owner.setState(t,"wait"),t.$ref&&(t.$ref.style.cssText=this.owner.constructor.cssText,t.$ref.style.visibility="hidden",t.$ref.style.marginLeft="0px",t.$ref.style.transform="translateX(0px)",t.$ref.style.transition="transform 0s linear 0s",this.owner.$refs.push(t.$ref),t.$ref=null)}resize(){const{clientWidth:t}=this.owner.$player;this.owner.filter("stop",e=>{if(0===e.mode)e.$ref.style.left=`${t}px`}),this.owner.filter("emit",e=>{switch(e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.left=`${t}px`,e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}})}continue(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("stop",e=>{switch(this.owner.setState(e,"emit"),e.$lastStartTime=Date.now(),e.mode){case 0:{const i=t+e.$ref.clientWidth;e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition=`transform ${e.$restTime}s linear 0s`;break}}}),this}suspend(){const{clientWidth:t}=this.owner.$player;return this.owner.filter("emit",e=>{switch(this.owner.setState(e,"stop"),e.mode){case 0:{const i=t-(this.owner.getLeft(e.$ref)-this.owner.getLeft(this.owner.$player));e.$ref.style.transform=`translateX(${-i}px)`,e.$ref.style.transition="transform 0s linear 0s";break}}}),this}prepare(t,e){const i=this.owner,n=i.$ref;return e.danmu=t,e.ref=n,t.$ref=n,n.textContent=t.text,i.$danmuku.appendChild(n),n.style.opacity=i.option.opacity,n.style.fontSize=`${i.fontSize}px`,n.style.color=t.color,n.style.border=t.border?`1px solid ${t.color}`:null,n.style.backgroundColor=t.border?"rgb(0 0 0 / 50%)":null,i.utils.setStyles(n,t.style),n}place(t,e,i,n,a){switch(e.style.top=`${i}px`,e.style.visibility="visible",e.dataset.mode=t.mode,e.dataset.id=t.id||"",t.mode){case 0:e.style.left=`${a}px`,e.style.marginLeft="0px",e.style.transform=`translateX(${-n}px)`,e.style.transition=`transform ${t.$restTime}s linear 0s`;break;case 1:case 2:e.style.left="50%",e.style.marginLeft=`-${e.clientWidth/2}px`}}release(t){const{danmu:e,ref:i}=t;i&&e.$ref===i&&(i.style.cssText=this.owner.constructor.cssText,i.style.visibility="hidden",this.owner.$refs.push(i),e.$ref=null),t.ref=null}clear(){this.destroy(),this.owner.$refs=[],this.owner.$danmuku.textContent=""}destroy(){for(const t of this.nodes)t.parentElement&&t.parentElement.removeChild(t);for(const t of this.owner.queue)t.$ref=null;this.owner.$refs.length=0,this.nodes.clear()}}const M=Symbol("cancelled danmuku frame");class S{constructor(t){this.owner=t,this.generation=0,this.starts=0,this.frame=null,this.operation=null,this.closed=!1,this.running=!1,this.fault=!1,this.failedItems=new Set}active(t){const e=this.owner;return!this.closed&&!this.fault&&!e.art.isDestroy&&!e.isStop&&!e.isHide&&t.generation===this.generation}release(t){this.owner.renderer.release(t)}invalidate(){var t;if(this.generation++,null!==this.frame&&window.cancelAnimationFrame(this.frame),this.frame=null,this.owner.timer=null,this.operation){const t=this.operation;this.operation=null,t.cancel(),this.release(t)}null==(t=this.owner.workerClient)||t.cancel(),this.failedItems.clear()}report(t){try{this.owner.art.emit("artplayerPluginDanmuku:error",t)}catch(e){console.warn("Failed to report danmuku scheduling error:",e)}}fail(t){this.closed||this.fault||(this.fault=!0,this.invalidate(),this.report(t))}recover(){var t;if(!this.closed&&!this.owner.art.isDestroy&&(this.failedItems.clear(),this.fault))try{null==(t=this.owner.workerClient)||t.dispose(),this.owner.createWorker(),this.fault=!1}catch(e){this.report(e)}}schedule(){const t=this.owner;!this.running||this.closed||this.fault||t.art.isDestroy||t.isStop||null!==this.frame||this.operation||(this.frame=window.requestAnimationFrame(()=>{if(this.frame=null,t.timer=null,this.closed||this.fault||t.isStop||t.art.isDestroy)return;let e;const i=new Promise(t=>e=()=>t(M)),n={generation:this.generation,cancel:e,wait:t=>Promise.race([t,i]),ref:null};return this.operation=n,this.run(n).catch(t=>{this.active(n)&&this.fail(t)}).finally(()=>{this.release(n),this.operation===n&&(this.operation=null,this.schedule())})}),t.timer=this.frame)}run(t){return c(this,null,function*(){const e=this.owner;if(!e.art.playing||!this.active(t))return;e.filter("emit",t=>{const i=(Date.now()-t.$lastStartTime)/1e3;t.$restTime-=i,t.$lastStartTime=Date.now(),t.$restTime<=0&&e.makeWait(t)});const i=e.readys;for(const a of i){if(!this.active(t))return;if(this.failedItems.has(a))continue;let i;try{i=yield t.wait(e.option.beforeVisible(a))}catch(n){if(!this.active(t))return;this.failedItems.add(a),this.report(n);continue}if(!this.active(t))return;if(!i)continue;const{clientWidth:s,clientHeight:r}=e.$player,o=e.renderer.prepare(a,t);if(!this.active(t))return;a.$lastStartTime=Date.now(),a.$restTime=e.speed;const l=s+o.clientWidth,d=yield t.wait(e.postMessage({type:"getDanmuTop",target:{mode:a.mode,height:o.clientHeight,speed:l/a.$restTime},visibles:e.visibles,antiOverlap:e.option.antiOverlap,clientWidth:s,clientHeight:r,marginBottom:e.marginBottom,marginTop:e.marginTop})),c="symbol"===typeof d?void 0:d.result;if(!this.active(t)||a.$ref!==o)return;void 0!==c?(e.setState(a,"emit"),e.renderer.place(a,o,c,l,s),t.ref=null,e.art.emit("artplayerPluginDanmuku:visible",a)):(e.setState(a,"ready"),this.release(t))}})}destroy(){this.closed=!0,this.invalidate()}}let z=0;class E{constructor(t,e){this.pending=new Map,this.closed=!1,this.failed=!1,this.onFailure=e,this.worker=t();try{this.worker.onmessage=t=>{var e;const i=this.pending.get(null==(e=t.data)?void 0:e.id);i&&(this.pending.delete(t.data.id),i.resolve(t.data))},this.worker.onerror=t=>{var e;null==(e=t.preventDefault)||e.call(t),this.fail(t.error||t)},this.worker.onmessageerror=t=>this.fail(t.error||t)}catch(i){try{this.dispose()}catch(n){}throw i}}request(t){t.id=++z;const{id:e}=t;return this.closed||this.failed?Promise.resolve({id:e,result:void 0}):new Promise((i,n)=>{this.pending.set(e,{resolve:i,reject:n});try{this.worker.postMessage(t)}catch(a){this.fail(a)}})}fail(t){if(!this.closed&&!this.failed){this.failed=!0;for(const e of this.pending.values())e.reject(t);this.pending.clear();try{this.dispose()}catch(e){}this.onFailure(t)}}cancel(){for(const[t,e]of this.pending)e.resolve({id:t,result:void 0});this.pending.clear()}dispose(){if(this.closed)return;this.closed=!0,this.cancel();let t,e=!1;const i=i=>{try{i()}catch(n){e||(t=n),e=!0}};for(const n of["onmessage","onerror","onmessageerror"])i(()=>this.worker[n]=null);if(i(()=>this.worker.terminate()),e)throw t}}const F='/*!\n * artplayer-plugin-danmuku.js v5.3.0\n * Github: https://github.com/zhw2590582/ArtPlayer\n * (c) 2017-2026 Harvey Zhao\n * Released under the MIT License.\n */\nfunction t({target:t,visibles:e,clientWidth:i,clientHeight:h,marginBottom:n,marginTop:o,antiOverlap:s}){const r=h-n,p=e.filter(e=>e.mode===t.mode&&e.top<=r).sort((t,e)=>t.top-e.top);if(0===p.length)return 2===t.mode?r-t.height:o;if(p.unshift({type:"top",top:0,left:0,right:0,height:o,width:i,speed:0,distance:i}),p.push({type:"bottom",top:r,left:0,right:0,height:n,width:i,speed:0,distance:i}),2===t.mode)for(let g=p.length-2;g>=0;g-=1){const e=p[g],i=p[g+1],h=e.top+e.height;if(i.top-h>=t.height)return i.top-t.height}else for(let g=1;g=t.height)return h}const a=[];for(let g=1;g{const i=Math.min(...e.map(t=>t.right)),h=Math.min(...t.map(t=>t.right));return i*e.length-h*t.length});break;case 1:case 2:a.sort((t,e)=>{const i=Math.max(...e.map(t=>t.width));return Math.max(...t.map(t=>t.width))*t.length-i*e.length})}return a[0][0].top}switch(t.mode){case 0:{const e=a.find(e=>e.every(e=>{if(ie.time}));return e&&e[0]?e[0].top:void 0}case 1:case 2:return}}onmessage=e=>{const{data:i}=e;if(!i.id||!i.type)return;const h=(0,{getDanmuTop:t}[i.type])(i);globalThis.postMessage({result:h,id:i.id})};\n',C="undefined"!==typeof self&&self.Blob&&new Blob(["URL.revokeObjectURL(import.meta.url);",F],{type:"text/javascript;charset=utf-8"});function D(t){let e;try{if(e=C&&(self.URL||self.webkitURL).createObjectURL(C),!e)throw"";const i=new Worker(e,{type:"module",name:null==t?void 0:t.name});return i.addEventListener("error",()=>{(self.URL||self.webkitURL).revokeObjectURL(e)}),i}catch(i){return new Worker("data:text/javascript;charset=utf-8,"+encodeURIComponent(F),{type:"module",name:null==t?void 0:t.name})}}class P{constructor(t,e){const{constructor:i,template:n}=t;this.utils=i.utils,this.validator=i.validator,this.$danmuku=n.$danmuku,this.$player=n.$player,this.art=t,this.queue=[],this.$refs=[],this.isStop=!1,this.isHide=!1,this.timer=null,this.index=0,this.worker=null,this.workerClient=null,this.renderer=new $(this),this.scheduler=new S(this),this.option=P.option,this.states={wait:[],ready:[],emit:[],stop:[]},this.start=this.start.bind(this),this.stop=this.stop.bind(this),this.reset=this.reset.bind(this),this.resize=this.resize.bind(this),this.destroy=this.destroy.bind(this),this.seek=this.seek.bind(this),k(this),t.on("destroy",this.destroy);try{if(this.config(e,!0),!k(this))return;this.createWorker()}catch(a){throw t.off("destroy",this.destroy),x(this),this.scheduler.destroy(),a}t.on("video:play",this.start),t.on("video:playing",this.start),t.on("video:pause",this.stop),t.on("video:waiting",this.stop),t.on("resize",this.resize),t.on("video:seeking",this.seek),this.load().catch(t=>console.warn("Failed to load initial danmuku:",t))}static get option(){return{danmuku:[],speed:5,margin:[10,"25%"],opacity:1,color:"#FFFFFF",mode:0,modes:[0,1,2],fontSize:25,antiOverlap:!0,synchronousPlayback:!1,mount:void 0,heatmap:!1,width:512,points:[],filter:()=>!0,beforeEmit:()=>!0,beforeVisible:()=>!0,visible:!0,emitter:!0,maxLength:200,lockTime:5,theme:"dark",OPACITY:{},FONT_SIZE:{},MARGIN:{},SPEED:{},COLOR:[]}}static get scheme(){return{danmuku:"array|function|string|promise",speed:"number",margin:"array",opacity:"number",color:"string",mode:"number",modes:"array",fontSize:"number|string",antiOverlap:"boolean",synchronousPlayback:"boolean",mount:"?htmldivelement|string",heatmap:"object|boolean",width:"number",points:"array",filter:"function",beforeEmit:"function",beforeVisible:"function",visible:"boolean",emitter:"boolean",maxLength:"number",lockTime:"number",theme:"string",OPACITY:"object",FONT_SIZE:"object",MARGIN:"object",SPEED:"object",COLOR:"array"}}static get cssText(){return'\n user-select: none;\n position: absolute;\n white-space: pre;\n pointer-events: none;\n perspective: 500px;\n display: inline-block;\n will-change: transform;\n font-weight: normal;\n line-height: 1.125;\n visibility: hidden;\n font-family: SimHei, "Microsoft JhengHei", Arial, Helvetica, sans-serif;\n text-shadow: rgb(0, 0, 0) 1px 0px 1px, rgb(0, 0, 0) 0px 1px 1px, rgb(0, 0, 0) 0px -1px 1px, rgb(0, 0, 0) -1px 0px 1px;\n '}get isRotate(){var t,e;return null==(e=null==(t=this.art.plugins)?void 0:t.autoOrientation)?void 0:e.state}get marginTop(){const{clamp:t}=this.utils,e=this.option.margin[0],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return P.option.margin[0]}get marginBottom(){const{clamp:t}=this.utils,e=this.option.margin[1],{clientHeight:i}=this.$player;if("number"===typeof e)return t(e,0,i);if("string"===typeof e&&e.endsWith("%")){return t(i*(Number.parseFloat(e)/100),0,i)}return P.option.margin[1]}get fontSize(){const{clamp:t}=this.utils,{clientHeight:e}=this.$player,i=this.option.fontSize;if("number"===typeof i)return Math.round(t(i,12,e));if("string"===typeof i&&i.endsWith("%")){const n=Number.parseFloat(i)/100;return Math.round(t(e*n,12,e))}return P.option.fontSize}get $ref(){return this.renderer.acquire()}get readys(){return function(t){const{currentTime:e}=t.art,i=[];return b(t,"ready",t=>i.push(t)),b(t,"wait",t=>{e+.1>=t.time&&t.time>=e-.1&&i.push(t)}),i}(this)}get visibles(){return this.renderer.visibles}get speed(){return this.option.synchronousPlayback&&this.art.playbackRate?this.option.speed/Number(this.art.playbackRate):this.option.speed}load(t){return c(this,null,function*(){const{errorHandle:e}=this.utils,i=function(t,e){const i=w(t);if(e)for(const d of[...i.tasks])d.replace&&d.cancel();let n,a=i.closed||t.art.isDestroy;const s=new Promise(t=>n=t),r=new Set,o="function"===typeof AbortController?new AbortController:void 0,l={replace:e,signal:null==o?void 0:o.signal,active:()=>!a&&!i.closed&&!t.art.isDestroy,onCancel:t=>l.active()?(r.add(t),()=>r.delete(t)):(t(),()=>{}),cancel(){if(!a){a=!0,n(v),null==o||o.abort();for(const t of[...r])t();r.clear()}},wait:t=>Promise.race([t,s]),emit(e){i.emitting.push(l);try{return t.emit(e)}finally{i.emitting.pop()}},finish(){i.tasks.delete(l),r.clear()}};return a?n(v):i.tasks.add(l),l}(this,void 0===t);try{if(!i.active())return this;const n=function(t,e){return"function"===typeof t?{asynchronous:!0,value:t()}:p(t)?{asynchronous:!0,value:t}:"string"===typeof t?{asynchronous:!0,value:g(t,e)}:{asynchronous:!1,value:Array.isArray(t)?t:[]}}(t||this.option.danmuku,i),a=n.asynchronous?yield i.wait(n.value):n.value;if(!i.active())return this;e(Array.isArray(a),"Danmuku need return an array as result");const s=a;if(void 0===t){if(this.reset(),!i.active())return this;this.queue=[],this.states={wait:[],ready:[],emit:[],stop:[]},this.renderer.clear()}for(let t=0;t{const e=a[t],i=s[t];return"function"===typeof e||"function"===typeof i||p(e)||p(i)?e!==i:JSON.stringify(e)!==JSON.stringify(i)}))&&!e)return this;const r=function(t,e,{defaults:i,validate:n,clamp:a,mount:s}){const r=Object.assign({},i,t,e);return n(r),r.mode=a(r.mode,0,2),r.speed=a(r.speed,1,10),r.opacity=a(r.opacity,0,1),r.lockTime=a(r.lockTime,1,60),r.maxLength=a(r.maxLength,1,1e3),r.mount=r.mount||s,r}(this.option,t,{defaults:P.option,validate:t=>this.validator(t,P.scheme),clamp:i,mount:n});if(!k(this))return this;const o=this.option.beforeVisible!==r.beforeVisible;return this.option=r,o&&(this.scheduler.invalidate(),this.scheduler.schedule()),t.fontSize&&(this.reset(),!k(this))||(this.option.visible?this.show():this.hide(),k(this)&&this.art.emit("artplayerPluginDanmuku:config",this.option)),this}getLeft(t){return this.renderer.left(t)}postMessage(t={}){return this.workerClient.request(t)}createWorker(){this.workerClient=new E(()=>new D,t=>this.scheduler.fail(t)),this.worker=this.workerClient.worker}filter(t,e){return b(this,t,e)}setState(t,e){!function(t,e,i){t.states[e.$state]=t.states[e.$state].filter(t=>t!==e),e.$state=i,e.$ref&&(e.$ref.dataset.state=i),t.states[i].push(e)}(this,t,e)}makeWait(t){this.renderer.makeWait(t)}update(){return this.scheduler.running=!0,this.scheduler.schedule(),this}resize(){this.renderer.resize()}continue(){return this.renderer.continue(),this}suspend(){return this.renderer.suspend(),this}stop(){return this.isStop=!0,this.scheduler.running=!1,this.scheduler.invalidate(),this.suspend(),this.art.emit("artplayerPluginDanmuku:stop"),this}start(){if(this.scheduler.closed||this.art.isDestroy)return this;const t=++this.scheduler.starts,e=this.scheduler.generation;this.isStop=!1,this.scheduler.recover();return this.scheduler.closed||this.art.isDestroy||this.isStop||this.scheduler.fault||e!==this.scheduler.generation||t!==this.scheduler.starts||(this.continue(),this.update(),this.art.emit("artplayerPluginDanmuku:start")),this}reset(){return this.scheduler.invalidate(),this.queue.forEach(t=>this.makeWait(t)),this.art.emit("artplayerPluginDanmuku:reset"),this.scheduler.recover(),this.scheduler.schedule(),this}seek(){this.scheduler.invalidate(),this.scheduler.schedule()}show(){return this.isHide=!1,this.$danmuku.style.opacity=1,this.option.visible=!0,this.art.emit("artplayerPluginDanmuku:show"),this}hide(){return this.isHide=!0,this.scheduler.invalidate(),this.$danmuku.style.opacity=0,this.option.visible=!1,this.art.emit("artplayerPluginDanmuku:hide"),this.scheduler.schedule(),this}destroy(){var t;x(this),this.scheduler.destroy();try{try{this.stop()}finally{null==(t=this.workerClient)||t.dispose()}}finally{this.renderer.destroy(),this.art.off("video:play",this.start),this.art.off("video:playing",this.start),this.art.off("video:pause",this.stop),this.art.off("video:waiting",this.stop),this.art.off("resize",this.resize),this.art.off("video:seeking",this.seek),this.art.off("destroy",this.destroy),this.art.emit("artplayerPluginDanmuku:destroy")}}}const T=4294967295;function A(t,e){let i=0,n=t.length;for(;iNumber.isFinite(t)&&t>0))return null;const o={xMin:0,xMax:t,yMin:0,yMax:128,scale:.25,opacity:.2,minHeight:Math.floor(.05*e),sampling:Math.max(1,Math.floor(t/100)),smoothing:.2,flattening:.2},l=Object.assign({},"object"===typeof a?a:{}),d=Object.assign({},o,l);for(const r of Object.keys(o))Number.isFinite(d[r])||(d[r]=o[r]);if(d.sampling<=0&&(d.sampling=o.sampling),d.xMin===d.xMax||d.yMin===d.yMax)return null;const c=Array.isArray(s)&&s.length>0,p=c?[...s]:function(t,e,i,n){if(!Number.isFinite(e)||e<=0||!Number.isFinite(i)||i<=0||!Number.isFinite(n)||n<=0||Math.floor(e/n)+1>T)return[];const a=t.map(t=>t.time).filter(t=>"number"===typeof t&&!Number.isNaN(t)).sort((t,e)=>t-e),s=[],r=i/e;if(!Number.isFinite(r))return[];for(let o=0;o<=e;o+=n){if(s.length>=T||o+n===o)return[];const t=o*r,e=(o+n)*r;s.push([o,A(a,e)-A(a,t)])}return s}(n,t,i,d.sampling);if(!p.length||!p.every(t=>Array.isArray(t)&&Number.isFinite(t[0])&&Number.isFinite(t[1])))return null;const h=p[p.length-1];if(h[0]!==t){if(p.length===T)return null;p.push([t,h[1]])}let u=1/0,m=-1/0;for(const r of p)u=Math.min(u,r[1]),m=Math.max(m,r[1]);const f=(u+m)/2;for(const r of p){const t=r[1];r[1]=t*(t>f?1+d.scale:1-d.scale)+d.minHeight}const g=!c&&!Number.isFinite(l.yMin)&&!Number.isFinite(l.yMax);let y=0;for(const r of p)y=Math.max(y,r[1]);const v=g&&y>o.yMax/4;v&&(d.yMax=4*y);const w=t=>v?Math.min(e,Math.max(.75*e,t)):t,k=(t,e,i,n)=>{const a=function(t,e){const i=e[0]-t[0],n=e[1]-t[1];return{length:Math.sqrt(r(i,2)+r(n,2)),angle:Math.atan2(n,i)}}(e||t,i||t),s=L(Math.cos(a.angle)*d.flattening,0,1,1,0),o=a.angle*s+(n?Math.PI:0),l=a.length*d.smoothing;return[t[0]+Math.cos(o)*l,w(t[1]+Math.sin(o)*l)]},x=p.map(i=>[L(i[0],d.xMin,d.xMax,0,t),w(L(i[1],d.yMin,d.yMax,e,0))]);if(!x.every(t=>t.every(Number.isFinite)))return null;const b=x.map((t,i,n)=>{if(0===i)return`M ${n[n.length-1][0]},${e} L ${t[0]},${e} L ${t[0]},${t[1]}`;const a=k(n[i-1],n[i-2],t),s=k(t,n[i-1],n[i+1],!0);return`C ${a[0]},${a[1]} ${s[0]},${s[1]} ${t[0]},${t[1]}${i===n.length-1?" z":""}`}).join(" ");return/NaN|Infinity/u.test(b)?null:{path:b,opacity:d.opacity,width:t,height:e}}let H=0;function N(t,e,i){if(t.isDestroy)return;const{query:n}=t.constructor.utils;let a;const s=[];let r,o,l,d=!1;const c=()=>!d&&!t.isDestroy;function p(e,i){const n=i;s.push([e,n]),t.on(e,n)}function h(e){if(d)return;let i;d=!0,o=null,l=null;let n=!1;const a=t=>{try{t()}catch(e){n||(i=e),n=!0}};for(const[r,o]of s.splice(0))a(()=>t.off(r,o));if(e&&r&&t.controls.heatmap===r&&a(()=>t.controls.remove("heatmap")),r=void 0,n)throw i}function u(t){c()&&o&&l&&(o.setAttribute("offset",100*t+"%"),l.setAttribute("offset",100*t+"%"))}function m(s){try{!function(s=[]){if(!c())return;o=null,l=null;const d=r;if(d.innerHTML="",t.option.isLive)return;const p=O({width:d.offsetWidth,height:d.offsetHeight,duration:t.duration,queue:e.queue,option:i,points:s});if(!p||!c())return;const h=d.ownerDocument;for(a||(a=h?"heatmap-solids":"heatmap-solids-"+ ++H);null==h?void 0:h.getElementById(a);)a="heatmap-solids-"+ ++H;d.innerHTML=`\n \n \n \n \n \n \n \n \n \n \n \n `,o=n("#heatmap-start",d),l=n("#heatmap-stop",d),u(t.played)}(s)}catch(d){try{h(!0)}catch(p){}throw d}}p("destroy",()=>h(!0));try{t.controls.add({name:"heatmap",position:"top",html:"",style:{position:"absolute",top:"-100px",left:"0px",right:"0px",height:"100px",width:"100%",pointerEvents:"none"},beforeUnmount(){h(!1)},mounted(e){if(r=e,d)return t.controls.heatmap===e&&t.controls.remove("heatmap"),void(r=void 0);c()?(p("video:timeupdate",()=>u(t.played)),p("setBar",(t,e)=>{"played"===t&&u(e)}),p("ready",()=>m()),p("resize",()=>m()),p("artplayerPluginDanmuku:loaded",()=>m()),p("artplayerPluginDanmuku:points",t=>m(t))):h(!0)}})}catch(f){try{h(!0)}catch(g){}throw f}return()=>h(!0)}const V=new WeakMap;class j{constructor(t){this.art=t,this.closed=!1,this.disposers=new Set,this.cancelled=new Promise(t=>this.cancel=t)}get active(){return!this.closed&&!this.art.isDestroy}own(t){this.closed?t():this.disposers.add(t)}write(t,e,i,n=i=>t[e]=i){const a=t;if(!this.active)return;const s=()=>"function"===typeof a.getPropertyPriority?a.getPropertyPriority(e):void 0;let r=V.get(a);r||(r=new Map,V.set(a,r));let o=r.get(e);o||(o={original:a[e],present:e in a||void 0!==a[e],priority:s(),owners:new Map,apply:n},r.set(e,o)),o.owners.has(this)||this.own(()=>{const t=[...o.owners.keys()],i=t[t.length-1]===this;if(o.owners.delete(this),i&&a[e]===o.last&&s()===o.lastPriority){const t=[...o.owners.values()];t.length?(o.apply(t[t.length-1]),o.last=a[e],o.lastPriority=s()):void 0!==o.priority?a.setProperty(e,o.original,o.priority):o.present?o.apply(o.original):delete a[e]}o.owners.size||(r.delete(e),r.size||V.delete(a))}),o.owners.delete(this),o.owners.set(this,i),o.apply(i),o.last=a[e],o.lastPriority=s()}on(t,e){if(!this.active)return;const i=this;function n(...t){if(i.active)return e.apply(this,t)}const a=()=>this.art.off(t,n);this.own(a);try{this.art.on(t,n),this.active||a()}catch(s){throw this.release(a),s}}proxy(t,e,i){if(!this.active)return;const n=this;function a(...t){if(n.active)return i.apply(this,t)}let s;const r=()=>{var i,n;if("function"===typeof s){const t=this.art.events;"function"===typeof(null==t?void 0:t.remove)&&(null==(i=t.destroyEvents)?void 0:i.has(s))?t.remove(s):s()}else null==(n=t.removeEventListener)||n.call(t,e,a)};this.own(r);try{s=this.art.proxy(t,e,a),this.active||r()}catch(o){throw this.release(r),o}}wait(t){return Promise.race([t,this.cancelled])}release(t){try{t()}catch(e){console.warn("Failed to dispose danmuku setting resource:",e)}}close(){if(!this.closed){this.closed=!0,this.cancel();for(const t of this.disposers)this.release(t);this.disposers.clear()}}}function q({min:t,max:e,container:i,findIndex:n,onChange:a,steps:s=[]}){const{lifecycle:r}=this;if(!r.active)return{reset(){}};const{query:o,clamp:l,setStyle:d}=this.utils;d(i,"touch-action","none"),i.innerHTML=`\n

\n
\n ${s.map(()=>'
').join("")}\n
\n
\n
\n
\n
\n ${s.map(t=>t.hide?"":`
${t.name}
`).join("")}\n
\n `;const c=o(".apd-slider-dot",i),p=o(".apd-slider-progress",i);let h=!1;function u(i){if(!r.active)return;if(void 0===i&&(i=n()),ie)return;const o=(i-t)/(e-t);c.style.left=100*o+"%",0===s.length&&(p.style.width=c.style.left),a(i)}function m(n){const{top:a,height:s,left:r,width:o}=i.getBoundingClientRect();if(this.art.isRotate){const i=l(n.clientY-a,0,s);u(Math.round(i/s*(e-t)+t))}else{const i=l(n.clientX-r,0,o);u(Math.round(i/o*(e-t)+t))}}return r.own(()=>h=!1),r.proxy(i,"click",t=>{m.call(this,t)}),r.proxy(i,"pointerdown",t=>{h=0===t.button}),r.on("document:pointermove",t=>{h&&m.call(this,t)}),r.on("document:pointerup",t=>{h&&(h=!1,m.call(this,t))}),{reset:u}}const I='',R='',B='',Z='',W='',_='',U='',G='',X='',Y='',J='',K='';function Q(){const{option:t}=this;return`\n
\n ${J}${Y}\n
\n
\n ${B}\n
\n
\n
\n \u6309\u7c7b\u578b\u5c4f\u853d\n
\n
\n ${Z}${W}\n
\u6eda\u52a8
\n
\n
\n ${_}${U}\n
\u9876\u90e8
\n
\n
\n ${G}${X}\n
\u5e95\u90e8
\n
\n
\n
\n
\n
\n ${R}${I}\n \u9632\u6b62\u5f39\u5e55\u91cd\u53e0\n
\n
\n ${R}${I}\n \u540c\u6b65\u89c6\u9891\u901f\u5ea6\n
\n
\n
\n \u4e0d\u900f\u660e\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n \u663e\u793a\u533a\u57df\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u5b57\u53f7\n
\n
\u672a\u77e5
\n
\n
\n \u5f39\u5e55\u901f\u5ea6\n
\n
\u672a\u77e5
\n
\n
\n
\n
\n
\n
\n ${K}\n
\n
\n
\n \u6a21\u5f0f\n
\n
\n ${W}\n
\u6eda\u52a8
\n
\n
\n ${U}\n
\u9876\u90e8
\n
\n
\n ${X}\n
\u5e95\u90e8
\n
\n
\n
\n
\n \u989c\u8272\n
\n ${this.COLOR.map(t=>`
`).join("")}\n
\n
\n
\n
\n
\n \n
\u53d1\u9001
\n
\n `}if("undefined"!==typeof document){const t="artplayer-plugin-danmuku",e=Symbol.for("artplayer-plugin-danmuku.pending-style"),i=document;let n=document.getElementById(t)||i[e];if(!n)if(n=document.createElement("style"),n.id=t,"loading"===document.readyState){i[e]=n;const a=()=>{document.removeEventListener("DOMContentLoaded",a),i[e]===n&&delete i[e];const s=document.getElementById(t);s?s.textContent=n.textContent:(document.head||document.documentElement).appendChild(n)};document.addEventListener("DOMContentLoaded",a,{once:!0})}else(document.head||document.documentElement).appendChild(n);n.textContent='.artplayer-plugin-danmuku{display:flex;position:relative;z-index:99;align-items:center;justify-content:center;font-size:12px;height:32px;width:100%;color:#fff;font-weight:300;flex-shrink:0;gap:10px}.artplayer-plugin-danmuku .apd-icon{cursor:pointer;opacity:.75;transition:all .2s ease;fill:#fff}.artplayer-plugin-danmuku .apd-icon:hover{opacity:1}.artplayer-plugin-danmuku .apd-config{display:flex;position:relative}.artplayer-plugin-danmuku .apd-config .apd-config-panel{position:absolute;bottom:24px;left:0;width:320px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-config .apd-config-panel .apd-config-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-config:hover .apd-config-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-config-mode,.artplayer-plugin-danmuku .apd-config-slider,.artplayer-plugin-danmuku .apd-config-other,.artplayer-plugin-danmuku .apd-style-mode{margin-bottom:15px}.artplayer-plugin-danmuku .apd-modes{display:flex;align-items:center;margin-top:5px;gap:20px}.artplayer-plugin-danmuku .apd-modes .apd-mode{cursor:pointer;text-align:center}.artplayer-plugin-danmuku .apd-modes .apd-mode:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-config-slider{display:flex;align-items:center;gap:12px}.artplayer-plugin-danmuku .apd-config-slider .apd-value{width:32px;text-align:right}.artplayer-plugin-danmuku .apd-slider{position:relative;flex:1;display:flex;height:20px;align-items:center;justify-content:center;cursor:pointer}.artplayer-plugin-danmuku .apd-slider .apd-slider-line{position:relative;height:2px;width:100%;overflow:hidden;border-radius:3px;background-color:#ffffff40}.artplayer-plugin-danmuku .apd-slider .apd-slider-points{position:absolute;inset:0;display:flex;align-items:center;justify-content:space-between}.artplayer-plugin-danmuku .apd-slider .apd-slider-points .apd-slider-point{width:2px;height:2px;border-radius:50%;background-color:#ffffff80}.artplayer-plugin-danmuku .apd-slider .apd-slider-progress{width:0%;height:100%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-dot{position:absolute;transform:translate(-6px);left:0%;width:12px;height:12px;border-radius:50%;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps{display:flex;align-items:center;justify-content:space-between;position:absolute;bottom:-12px;width:calc(100% + 32px);color:#777}.artplayer-plugin-danmuku .apd-slider .apd-slider-steps .apd-slider-step{flex-shrink:0;width:36px;text-align:center;scale:.95}.artplayer-plugin-danmuku .apd-config-other{display:flex;align-items:center;gap:20px}.artplayer-plugin-danmuku .apd-config-other .apd-check-off,.artplayer-plugin-danmuku .apd-config-other .apd-check-on{width:16px;height:16px}.artplayer-plugin-danmuku .apd-config-other .apd-other{display:flex;align-items:center;cursor:pointer;gap:2px}.artplayer-plugin-danmuku .apd-config-other .apd-other:hover{color:#00a1d6}.artplayer-plugin-danmuku .apd-emitter{display:flex;flex:1;align-items:center;height:100%;background-color:#ffffff40;border-radius:5px}.artplayer-plugin-danmuku .apd-style{position:relative;display:flex;align-items:center;justify-content:center}.artplayer-plugin-danmuku .apd-style .apd-style-panel{position:absolute;bottom:24px;left:0;width:200px;padding:10px;opacity:0;pointer-events:none}.artplayer-plugin-danmuku .apd-style .apd-style-panel .apd-style-panel-inner{width:100%;border-radius:3px;background-color:#000000d9;padding:10px}.artplayer-plugin-danmuku .apd-style:hover .apd-style-panel{opacity:100;pointer-events:all}.artplayer-plugin-danmuku .apd-colors{display:flex;flex-wrap:wrap;margin-top:5px;gap:8px}.artplayer-plugin-danmuku .apd-colors .apd-color{width:16px;height:16px;border-radius:2px;cursor:pointer}.artplayer-plugin-danmuku .apd-colors .apd-color.apd-active{border:1px solid black;box-shadow:0 0 0 1px #fff}.artplayer-plugin-danmuku .apd-input{outline:none;height:100%;flex:1;min-width:0;width:auto;border:none;line-height:1;color:#fff;background-color:transparent}.artplayer-plugin-danmuku .apd-input::placeholder{color:#ffffff80}.artplayer-plugin-danmuku .apd-send{display:flex;align-items:center;justify-content:center;height:100%;width:60px;flex-shrink:0;cursor:pointer;text-shadow:none;border-top-right-radius:5px;border-bottom-right-radius:5px;background-color:#00a1d6}.artplayer-plugin-danmuku .apd-send.apd-lock{cursor:not-allowed;color:#666;background-color:#e7e7e7}.art-controls-center .apd-emitter{width:260px;flex:none}.art-fullscreen .artplayer-plugin-danmuku,.art-fullscreen-web .artplayer-plugin-danmuku{height:38px;gap:16px}.art-fullscreen .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen-web .artplayer-plugin-danmuku .apd-config-icon,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-off,.art-fullscreen .artplayer-plugin-danmuku .apd-toggle-on,.art-fullscreen-web .artplayer-plugin-danmuku .apd-toggle-on{width:28px;height:28px}.art-fullscreen .artplayer-plugin-danmuku .apd-emitter,.art-fullscreen-web .artplayer-plugin-danmuku .apd-emitter{width:400px;flex:none}.art-video-player>.artplayer-plugin-danmuku{position:absolute;left:0;right:0;bottom:-40px;padding:0 10px}.art-video-player:has(>.artplayer-plugin-danmuku){margin-bottom:40px}[data-danmuku-emitter=false] .apd-emitter{display:none!important}[data-danmuku-emitter=false] .art-controls-center .artplayer-plugin-danmuku{justify-content:flex-end;gap:18px}[data-danmuku-emitter=false].art-fullscreen .art-controls-center .artplayer-plugin-danmuku,[data-danmuku-emitter=false].art-fullscreen-web .art-controls-center .artplayer-plugin-danmuku{gap:24px}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-icon{fill:#333}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-emitter{background-color:#f1f2f3}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input{color:#000}[data-danmuku-theme=light]>.artplayer-plugin-danmuku .apd-input::placeholder{color:#0000004d}[data-danmuku-visible=false] .apd-toggle-off{display:block}[data-danmuku-visible=false] .apd-toggle-on,[data-danmuku-visible=true] .apd-toggle-off{display:none}[data-danmuku-visible=true] .apd-toggle-on{display:block}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-on{display:none}[data-danmuku-anti-overlap=false] .apd-anti-overlap .apd-check-off,[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-on{display:block}[data-danmuku-anti-overlap=true] .apd-anti-overlap .apd-check-off,[data-danmuku-sync-video=false] .apd-sync-video .apd-check-on{display:none}[data-danmuku-sync-video=false] .apd-sync-video .apd-check-off,[data-danmuku-sync-video=true] .apd-sync-video .apd-check-on{display:block}[data-danmuku-sync-video=true] .apd-sync-video .apd-check-off{display:none}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-off{display:block}[data-danmuku-mode0=false] .apd-config-mode .apd-mode-0-on{display:none}[data-danmuku-mode0=false] .art-danmuku [data-mode="0"]{opacity:0!important}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-off{display:none}[data-danmuku-mode0=true] .apd-config-mode .apd-mode-0-on{display:block}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"]{color:#00a1d6}[data-danmuku-mode="0"] .apd-style-mode [data-mode="0"] path{fill:#00a1d6}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-off{display:block}[data-danmuku-mode1=false] .apd-config-mode .apd-mode-1-on{display:none}[data-danmuku-mode1=false] .art-danmuku [data-mode="1"]{opacity:0!important}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-off{display:none}[data-danmuku-mode1=true] .apd-config-mode .apd-mode-1-on{display:block}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"]{color:#00a1d6}[data-danmuku-mode="1"] .apd-style-mode [data-mode="1"] path{fill:#00a1d6}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-off{display:block}[data-danmuku-mode2=false] .apd-config-mode .apd-mode-2-on{display:none}[data-danmuku-mode2=false] .art-danmuku [data-mode="2"]{opacity:0!important}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-off{display:none}[data-danmuku-mode2=true] .apd-config-mode .apd-mode-2-on{display:block}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"]{color:#00a1d6}[data-danmuku-mode="2"] .apd-style-mode [data-mode="2"] path{fill:#00a1d6}'}class tt{constructor(t,e){this.art=t,this.danmuku=e,this.utils=t.constructor.utils;const{setStyle:i}=this.utils,{$controlsCenter:n}=t.template;this.template={$controlsCenter:n,$mount:n,$danmuku:null,$toggle:null,$config:null,$configPanel:null,$configModes:null,$style:null,$stylePanel:null,$styleModes:null,$colors:null,$opacitySlider:null,$opacityValue:null,$marginSlider:null,$marginValue:null,$fontSizeSlider:null,$fontSizeValue:null,$speedSlider:null,$speedValue:null,$input:null,$send:null},this.slider={opacity:null,margin:null,fontSize:null,speed:null},this.emitting=!1,this.isLock=!1,this.timer=null,this.lifecycle=new j(t),this.destroy=this.destroy.bind(this),this.lifecycle.own(()=>t.off("destroy",this.destroy));try{if(t.on("destroy",this.destroy),!this.lifecycle.active)return;if(this.lifecycle.write(n.style,"display","flex",t=>i(n,"display",t)),this.createTemplate(),!this.lifecycle.active)return;if(this.createSliders(),!this.lifecycle.active)return;if(this.createEvents(),this.mount(this.option.mount),!this.lifecycle.active)return;this.lifecycle.on("resize",()=>this.resize()),this.lifecycle.on("fullscreen",t=>this.onFullscreen(t)),this.lifecycle.on("fullscreenWeb",t=>this.onFullscreen(t)),this.lifecycle.proxy(this.template.$config,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$config,$panel:this.template.$configPanel})}),this.lifecycle.proxy(this.template.$style,"mouseenter",()=>{this.onMouseEnter({$control:this.template.$style,$panel:this.template.$stylePanel})})}catch(a){try{this.destroy()}catch(s){console.warn("Failed to roll back danmuku setting:",s)}throw a}}static get icons(){return{$on:J,$off:Y,$config:B,$style:K,$mode_0_off:Z,$mode_0_on:W,$mode_1_off:_,$mode_1_on:U,$mode_2_off:G,$mode_2_on:X,$check_on:R,$check_off:I}}get option(){return this.danmuku.option}get outside(){return this.template.$mount!==this.template.$controlsCenter}get TEMPLATE(){return Q.call(this)}get OPACITY(){return l({min:0,max:100,steps:[]},this.option.OPACITY)}get FONT_SIZE(){return l({min:12,max:120,steps:[]},this.option.FONT_SIZE)}get MARGIN(){return l({min:0,max:3,steps:[{name:"1/4",value:[10,"75%"]},{name:"\u534a\u5c4f",value:[10,"50%"]},{name:"3/4",value:[10,"25%"]},{name:"\u6ee1\u5c4f",value:[10,10]}]},this.option.MARGIN)}get SPEED(){return l({min:0,max:4,steps:[{name:"\u6781\u6162",value:10},{name:"\u8f83\u6162",value:7.5,hide:!0},{name:"\u9002\u4e2d",value:5},{name:"\u8f83\u5feb",value:2.5,hide:!0},{name:"\u6781\u5feb",value:1}]},this.option.SPEED)}get COLOR(){return this.option.COLOR.length?this.option.COLOR:["#FE0302","#FF7204","#FFAA02","#FFD302","#FFFF00","#A0EE00","#00CD00","#019899","#4266BE","#89D5FF","#CC0273","#222222","#9B9B9B","#FFFFFF"]}query(t){const{query:e}=this.utils,{$danmuku:i}=this.template;return e(t,i)}append(t,e){if(!this.lifecycle.active)return;const{append:i}=this.utils;[...t.children].includes(e)||i(t,e)}setData(t,e){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,{$mount:n}=this.template;this.lifecycle.write(i.dataset,t,e),this.outside&&this.lifecycle.write(n.dataset,t,e)}createTemplate(){const{createElement:t,tooltip:e}=this.utils,i=t("div");this.template.$danmuku=i,i.className="artplayer-plugin-danmuku",i.innerHTML=this.TEMPLATE,this.template.$toggle=this.query(".apd-toggle"),this.template.$config=this.query(".apd-config"),this.template.$configPanel=this.query(".apd-config-panel"),this.template.$configModes=this.query(".apd-config-mode .apd-modes"),this.template.$style=this.query(".apd-style"),this.template.$stylePanel=this.query(".apd-style-panel"),this.template.$styleModes=this.query(".apd-style-mode .apd-modes"),this.template.$colors=this.query(".apd-colors"),this.template.$antiOverlap=this.query(".apd-anti-overlap"),this.template.$syncVideo=this.query(".apd-sync-video"),this.template.$opacitySlider=this.query(".apd-config-opacity .apd-slider"),this.template.$opacityValue=this.query(".apd-config-opacity .apd-value"),this.template.$marginSlider=this.query(".apd-config-margin .apd-slider"),this.template.$marginValue=this.query(".apd-config-margin .apd-value"),this.template.$fontSizeSlider=this.query(".apd-config-fontSize .apd-slider"),this.template.$fontSizeValue=this.query(".apd-config-fontSize .apd-value"),this.template.$speedSlider=this.query(".apd-config-speed .apd-slider"),this.template.$speedValue=this.query(".apd-config-speed .apd-value"),this.template.$input=this.query(".apd-input"),this.template.$send=this.query(".apd-send");const{$toggle:n}=this.template;this.lifecycle.on("artplayerPluginDanmuku:show",()=>{e(n,"\u5173\u95ed\u5f39\u5e55")}),this.lifecycle.on("artplayerPluginDanmuku:hide",()=>{e(n,"\u6253\u5f00\u5f39\u5e55")})}createEvents(){const{$toggle:t,$configModes:e,$styleModes:i,$colors:n,$antiOverlap:a,$syncVideo:s,$send:r,$input:o}=this.template;this.lifecycle.proxy(t,"click",()=>{this.danmuku.config({visible:!this.option.visible}),this.reset()}),this.lifecycle.proxy(e,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.option.modes.includes(i)?this.danmuku.config({modes:this.option.modes.filter(t=>t!==i)}):this.danmuku.config({modes:[...this.option.modes,i]}),this.reset()}),this.lifecycle.proxy(a,"click",()=>{this.danmuku.config({antiOverlap:!this.option.antiOverlap}),this.reset()}),this.lifecycle.proxy(s,"click",()=>{this.danmuku.config({synchronousPlayback:!this.option.synchronousPlayback}),this.reset()}),this.lifecycle.proxy(i,"click",t=>{const e=t.target.closest(".apd-mode");if(!e)return;const i=Number(e.dataset.mode);this.danmuku.config({mode:i}),this.reset()}),this.lifecycle.proxy(n,"click",t=>{const e=t.target.closest(".apd-color");e&&(this.danmuku.config({color:e.dataset.color}),this.reset())}),this.lifecycle.proxy(r,"click",()=>this.emit()),this.lifecycle.proxy(o,"keypress",t=>{"Enter"===t.key&&(t.preventDefault(),this.emit())})}createSliders(){this.slider.opacity=this.createSlider(d(l({},this.OPACITY),{container:this.template.$opacitySlider,findIndex:()=>Math.round(100*this.option.opacity),onChange:t=>{const{$opacityValue:e}=this.template;e.textContent=`${t}%`,this.danmuku.config({opacity:t/100})}})),this.slider.margin=this.createSlider(d(l({},this.MARGIN),{container:this.template.$marginSlider,findIndex:()=>this.MARGIN.steps.findIndex(t=>t.value[0]===this.option.margin[0]&&t.value[1]===this.option.margin[1]),onChange:t=>{const e=this.MARGIN.steps[t];if(!e)return;const{$marginValue:i}=this.template;i.textContent=e.name,this.danmuku.config({margin:e.value})}})),this.slider.fontSize=this.createSlider(d(l({},this.FONT_SIZE),{container:this.template.$fontSizeSlider,findIndex:()=>this.danmuku.fontSize,onChange:t=>{const{$fontSizeValue:e}=this.template;e.textContent=`${t}px`,t!==this.danmuku.fontSize&&this.danmuku.config({fontSize:t})}})),this.slider.speed=this.createSlider(d(l({},this.SPEED),{container:this.template.$speedSlider,findIndex:()=>this.SPEED.steps.findIndex(t=>t.value===this.option.speed),onChange:t=>{const e=this.SPEED.steps[t];if(!e)return;const{$speedValue:i}=this.template;i.textContent=e.name,this.danmuku.config({speed:e.value})}}))}createSlider(t){return q.call(this,t)}onFullscreen(t){if(!this.lifecycle.active)return;const{$danmuku:e,$controlsCenter:i,$mount:n}=this.template;this.outside?t?this.append(i,e):this.append(n,e):this.append(i,e)}onMouseEnter({$control:t,$panel:e}){if(!this.lifecycle.active)return;const{$player:i}=this.art.template,n=t.getBoundingClientRect(),a=e.getBoundingClientRect(),s=i.getBoundingClientRect(),r=a.width/2-n.width/2,o=s.left-(n.left-r),l=n.right+r-s.right;e.style.left=o>0?`${-r+o}px`:l>0?-r-l+"px":-r+"px"}emit(){return function(t){return c(this,null,function*(){const{lifecycle:e,template:{$input:i}}=t;if(!e.active)return;const n=i.value.trim();if(!n.length||t.isLock||t.emitting)return;const a={text:n,mode:t.option.mode,color:t.option.color,time:t.art.currentTime},s=t=>{e.active&&console.error("Error emitting danmuku:",t)};try{t.emitting=!0;const n=yield e.wait(t.option.beforeEmit(a));if(!e.active)return;if(t.emitting=!1,!0!==n)return;if(a.border=!0,delete a.time,Promise.resolve(t.danmuku.emit(a)).catch(s),!e.active)return;i.value="",t.lock()}catch(r){s(r),e.active&&(t.emitting=!1)}})}(this)}lock(){!function(t){if(!t.lifecycle.active)return;const{addClass:e}=t.utils,{$send:i}=t.template;clearTimeout(t.timer),t.isLock=!0;let n=t.option.lockTime;i.textContent=n,e(i,"apd-lock");const a=()=>{t.timer=setTimeout(()=>{t.lifecycle.active&&(0===n?t.unlock():(n-=1,i.textContent=n,a()))},1e3)};a()}(this)}unlock(){!function(t){if(clearTimeout(t.timer),t.timer=null,t.isLock=!1,!t.lifecycle.active)return;const{removeClass:e}=t.utils,{$send:i}=t.template;i.textContent="\u53d1\u9001",e(i,"apd-lock")}(this)}resize(){if(!this.lifecycle.active)return;if(this.outside)return;if(this.art.fullscreen)return;if(this.art.fullscreenWeb)return;const{$player:t,$controlsCenter:e}=this.art.template,{$danmuku:i}=this.template;this.art.widtht.dataset.color===this.option.color.toUpperCase());s&&t(s,"apd-active"),e(i,this.option.visible?"\u5173\u95ed\u5f39\u5e55":"\u6253\u5f00\u5f39\u5e55"),this.resize()}mount(t){if(!this.lifecycle.active)return;const{errorHandle:e}=this.utils,i="string"===typeof t?document.querySelector(t):t;e(i,`Can not find the mount point: ${t}`),this.append(i,this.template.$danmuku),this.template.$mount=i,this.reset()}destroy(){if(this.lifecycle.closed)return;this.lifecycle.close(),clearTimeout(this.timer),this.timer=null,this.emitting=!1,this.isLock=!1;const t=this.template.$danmuku;(null==t?void 0:t.parentElement)&&t.parentElement.removeChild(t)}}function et(t){return e=>{const i=new P(e,t);let n;try{n=e.isDestroy?void 0:new tt(e,i),!e.isDestroy&&i.option.heatmap&&N(e,i,i.option.heatmap)}catch(a){try{null==n||n.destroy()}catch(s){}try{i.destroy()}catch(s){}throw a}return{name:"artplayerPluginDanmuku",emit:i.emit.bind(i),load:i.load.bind(i),config:i.config.bind(i),hide:i.hide.bind(i),show:i.show.bind(i),reset:i.reset.bind(i),mount:n?n.mount.bind(n):()=>{},get option(){return i.option},get isHide(){return i.isHide},get isStop(){return i.isStop}}}}return et.icons=tt.icons,et}); diff --git a/packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.mjs b/packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.mjs index f92026e90..560c1e7e2 100644 --- a/packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.mjs +++ b/packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.mjs @@ -665,16 +665,18 @@ class Scheduler { danmu.$lastStartTime = Date.now(); danmu.$restTime = owner.speed; const distance = clientWidth + ref.clientWidth; - const { result: top } = await operation.wait(owner.postMessage({ + const reply = await operation.wait(owner.postMessage({ type: "getDanmuTop", target: { mode: danmu.mode, height: ref.clientHeight, speed: distance / danmu.$restTime }, visibles: owner.visibles, antiOverlap: owner.option.antiOverlap, clientWidth, clientHeight, + // Valid margins are numeric. Preserve the old raw fallback for unsupported strings. marginBottom: owner.marginBottom, marginTop: owner.marginTop })); + const top = typeof reply === "symbol" ? void 0 : reply.result; if (!this.active(operation) || danmu.$ref !== ref) return; if (top !== void 0) { @@ -948,6 +950,7 @@ class Danmuku { if (!task.active()) return this; errorHandle(Array.isArray(danmus), "Danmuku need return an array as result"); + const rows = danmus; if (danmuku === void 0) { this.reset(); if (!task.active()) @@ -956,10 +959,10 @@ class Danmuku { this.states = { wait: [], ready: [], emit: [], stop: [] }; this.renderer.clear(); } - for (let index = 0; index < danmus.length; index++) { + for (let index = 0; index < rows.length; index++) { if (!task.active()) return this; - const danmu = danmus[index]; + const danmu = rows[index]; await task.emit(danmu); } if (task.active()) @@ -1074,6 +1077,7 @@ class Danmuku { return this.renderer.left($ref); } // 复杂运算交给 Web Worker 处理 + // Keep the historical empty default; unknown message types receive no reply. postMessage(message = {}) { return this.workerClient.request(message); } @@ -1312,8 +1316,9 @@ function heatmap(art, danmuku, option) { let closed = false; const active = () => !closed && !art.isDestroy; function listen(name, callback) { - subscriptions.push([name, callback]); - art.on(name, callback); + const listener = callback; + subscriptions.push([name, listener]); + art.on(name, listener); } function dispose(removeControl) { if (closed) @@ -1351,18 +1356,19 @@ function heatmap(art, danmuku, option) { return; start = null; stop = null; - element.innerHTML = ""; + const target = element; + target.innerHTML = ""; if (art.option.isLive) return; - const shape = heatmapGeometry({ width: element.offsetWidth, height: element.offsetHeight, duration: art.duration, queue: danmuku.queue, option, points }); + const shape = heatmapGeometry({ width: target.offsetWidth, height: target.offsetHeight, duration: art.duration, queue: danmuku.queue, option, points }); if (!shape || !active()) return; - const document2 = element.ownerDocument; + const document2 = target.ownerDocument; if (!gradient) gradient = document2 ? "heatmap-solids" : `heatmap-solids-${++nextGradient}`; while (document2?.getElementById(gradient)) gradient = `heatmap-solids-${++nextGradient}`; - element.innerHTML = ` + target.innerHTML = ` @@ -1375,8 +1381,8 @@ function heatmap(art, danmuku, option) { `; - start = query("#heatmap-start", element); - stop = query("#heatmap-stop", element); + start = query("#heatmap-start", target); + stop = query("#heatmap-stop", target); progress(art.played); } function update(points) { @@ -1456,7 +1462,8 @@ class SettingLifecycle { dispose(); else this.disposers.add(dispose); } - write(target, key, value, apply = (value2) => target[key] = value2) { + write(host, key, value, apply = (value2) => host[key] = value2) { + const target = host; if (!this.active) return; const priority = () => typeof target.getPropertyPriority === "function" ? target.getPropertyPriority(key) : void 0; @@ -1847,16 +1854,17 @@ const style = ".artplayer-plugin-danmuku {\n display: flex;\n position: relati if (typeof document !== "undefined") { const id = "artplayer-plugin-danmuku"; const pending = /* @__PURE__ */ Symbol.for("artplayer-plugin-danmuku.pending-style"); - let $style2 = document.getElementById(id) || document[pending]; + const styleDocument = document; + let $style2 = document.getElementById(id) || styleDocument[pending]; if (!$style2) { $style2 = document.createElement("style"); $style2.id = id; if (document.readyState === "loading") { - document[pending] = $style2; + styleDocument[pending] = $style2; const ready = () => { document.removeEventListener("DOMContentLoaded", ready); - if (document[pending] === $style2) - delete document[pending]; + if (styleDocument[pending] === $style2) + delete styleDocument[pending]; const existing = document.getElementById(id); if (existing) existing.textContent = $style2.textContent; diff --git a/packages/artplayer-plugin-danmuku/package.json b/packages/artplayer-plugin-danmuku/package.json index 07c075e8a..545cfa273 100644 --- a/packages/artplayer-plugin-danmuku/package.json +++ b/packages/artplayer-plugin-danmuku/package.json @@ -27,11 +27,31 @@ "types": "./types/artplayer-plugin-danmuku.d.ts", "import": "./dist/artplayer-plugin-danmuku.legacy.js", "require": "./dist/artplayer-plugin-danmuku.legacy.js" + }, + "./runtime": { + "import": { + "types": "./types/runtime.d.mts", + "default": "./dist/artplayer-plugin-danmuku.mjs" + }, + "require": { + "types": "./types/runtime.d.cts", + "default": "./dist/artplayer-plugin-danmuku.js" + } } }, "main": "./dist/artplayer-plugin-danmuku.js", "module": "./dist/artplayer-plugin-danmuku.mjs", "types": "./types/artplayer-plugin-danmuku.d.ts", "legacy": "./dist/artplayer-plugin-danmuku.legacy.js", - "browserslist": "last 1 Chrome version" + "browserslist": "last 1 Chrome version", + "typesVersions": { + "*": { + "legacy": [ + "types/artplayer-plugin-danmuku.d.ts" + ], + "runtime": [ + "types/runtime.d.ts" + ] + } + } } diff --git a/packages/artplayer-plugin-danmuku/src/bilibili-parser.js b/packages/artplayer-plugin-danmuku/src/bilibili-parser.ts similarity index 68% rename from packages/artplayer-plugin-danmuku/src/bilibili-parser.js rename to packages/artplayer-plugin-danmuku/src/bilibili-parser.ts index ac000e5f9..bf40db5fb 100644 --- a/packages/artplayer-plugin-danmuku/src/bilibili-parser.js +++ b/packages/artplayer-plugin-danmuku/src/bilibili-parser.ts @@ -1,4 +1,6 @@ -export function getMode(key) { +import type { ParsedDanmu, ParserReply, ParserRequest } from './parser-types' + +export function getMode(key: number): number { switch (key) { case 1: case 2: @@ -13,16 +15,16 @@ export function getMode(key) { } } -export function bilibiliDanmuParseFromXml(xmlString) { +export function bilibiliDanmuParseFromXml(xmlString: unknown): ParsedDanmu[] { if (typeof xmlString !== 'string') return [] const reg = /]*?p="(?

[^"]+)"[^>]*>(?.*?)<\/d>/gs const matches = xmlString.matchAll(reg) return Array.from(matches) .map((match) => { - const attr = match.groups.p.split(',') + const attr = match.groups!.p!.split(',') if (attr.length >= 8) { - const text = match.groups.text + const text = match.groups!.text! .trim() .replaceAll('"', '"') .replaceAll(''', '\'') @@ -38,7 +40,7 @@ export function bilibiliDanmuParseFromXml(xmlString) { color: `#${Number(attr[3]).toString(16)}`, timestamp: Number(attr[4]), pool: Number(attr[5]), - userID: attr[6], + userID: attr[6]!, rowID: Number(attr[7]), } } @@ -46,11 +48,11 @@ export function bilibiliDanmuParseFromXml(xmlString) { return null } }) - .filter(Boolean) + .filter(Boolean) as ParsedDanmu[] } -export function onmessage({ data }) { +export function onmessage({ data }: MessageEvent) { const { xml, id } = data const danmus = bilibiliDanmuParseFromXml(xml) - globalThis.postMessage({ danmus, id }) + globalThis.postMessage({ danmus, id } satisfies ParserReply) } diff --git a/packages/artplayer-plugin-danmuku/src/bilibili.js b/packages/artplayer-plugin-danmuku/src/bilibili.ts similarity index 77% rename from packages/artplayer-plugin-danmuku/src/bilibili.js rename to packages/artplayer-plugin-danmuku/src/bilibili.ts index 47be475fb..348633a38 100644 --- a/packages/artplayer-plugin-danmuku/src/bilibili.js +++ b/packages/artplayer-plugin-danmuku/src/bilibili.ts @@ -1,8 +1,9 @@ +import type { ParsedDanmu, ParserCancellation, ParserReply, ParserRequest } from './parser-types' import { bilibiliDanmuParseFromXml, getMode, onmessage } from './bilibili-parser' let nextRequest = 0 -function abortError(signal) { +function abortError(signal?: AbortSignal): unknown { if (signal?.reason !== undefined) return signal.reason const error = new Error('Bilibili Danmu loading cancelled') @@ -10,17 +11,17 @@ function abortError(signal) { return error } -export function bilibiliDanmuParseFromUrl(url, { signal, onCancel } = {}) { +export function bilibiliDanmuParseFromUrl(url: string, { signal, onCancel }: ParserCancellation = {}): Promise { return new Promise((resolve, reject) => { let settled = false - let worker - let workerUrl - let releaseCancel + let worker: Worker | undefined + let workerUrl: string | undefined + let releaseCancel: (() => void) | undefined const id = ++nextRequest const cancel = () => finish(undefined, abortError(signal)) function releaseWorker() { - let error + let error: unknown let failed = false try { if (worker) { @@ -52,7 +53,9 @@ export function bilibiliDanmuParseFromUrl(url, { signal, onCancel } = {}) { throw error } - function finish(value, error) { + function finish(value: ParsedDanmu[]): void + function finish(value: undefined, error: unknown): void + function finish(value: ParsedDanmu[] | undefined, error?: unknown) { if (settled) return settled = true @@ -69,10 +72,10 @@ export function bilibiliDanmuParseFromUrl(url, { signal, onCancel } = {}) { } if (failed) reject(error) - else resolve(value) + else resolve(value!) } - function fallback(xml, error) { + function fallback(xml: string, error: unknown) { if (settled) return console.error('Error parsing Bilibili Danmu:', error) @@ -107,7 +110,7 @@ export function bilibiliDanmuParseFromUrl(url, { signal, onCancel } = {}) { releaseWorker() return } - worker.onmessage = (event) => { + worker.onmessage = (event: MessageEvent) => { if (settled || event.data?.id !== id) return const { danmus } = event.data @@ -121,8 +124,8 @@ export function bilibiliDanmuParseFromUrl(url, { signal, onCancel } = {}) { event.preventDefault?.() fallback(xml, event.error || event) } - worker.onmessageerror = event => fallback(xml, event.error || event) - worker.postMessage({ xml, id }) + worker.onmessageerror = event => fallback(xml, (event as MessageEvent & { error?: unknown }).error || event) + worker.postMessage({ xml, id } satisfies ParserRequest) } catch (error) { fallback(xml, error) diff --git a/packages/artplayer-plugin-danmuku/src/config.js b/packages/artplayer-plugin-danmuku/src/config.ts similarity index 72% rename from packages/artplayer-plugin-danmuku/src/config.js rename to packages/artplayer-plugin-danmuku/src/config.ts index 25c0bda12..629eee464 100644 --- a/packages/artplayer-plugin-danmuku/src/config.js +++ b/packages/artplayer-plugin-danmuku/src/config.ts @@ -1,4 +1,6 @@ -export function defaultOption() { +import type { DanmukuOption, NormalizedOption } from './types' + +export function defaultOption(): NormalizedOption & { fontSize: number, margin: [number, string] } { return { danmuku: [], speed: 5, @@ -62,21 +64,21 @@ export function optionScheme() { } } -export function isPromiseInput(value) { +export function isPromiseInput(value: unknown): value is Promise { return value instanceof Promise || Object.prototype.toString.call(value) === '[object Promise]' } -export function optionChanged(previous, update) { +export function optionChanged(previous: NormalizedOption, update: DanmukuOption) { return Object.keys(update).some((key) => { - const before = previous[key] - const after = update[key] + const before = previous[key as keyof NormalizedOption] + const after = update[key as keyof NormalizedOption] if (typeof before === 'function' || typeof after === 'function' || isPromiseInput(before) || isPromiseInput(after)) return before !== after return JSON.stringify(before) !== JSON.stringify(after) }) } -export function normalizeOption(previous, update, { defaults, validate, clamp, mount }) { +export function normalizeOption(previous: NormalizedOption, update: DanmukuOption, { defaults, validate, clamp, mount }: { defaults: NormalizedOption, validate: (value: NormalizedOption) => unknown, clamp: (value: number, min: number, max: number) => number, mount: HTMLDivElement }): NormalizedOption { const next = Object.assign({}, defaults, previous, update) validate(next) next.mode = clamp(next.mode, 0, 2) diff --git a/packages/artplayer-plugin-danmuku/src/danmuku.js b/packages/artplayer-plugin-danmuku/src/danmuku.ts similarity index 85% rename from packages/artplayer-plugin-danmuku/src/danmuku.js rename to packages/artplayer-plugin-danmuku/src/danmuku.ts index 32e20afaa..cba6d6da4 100644 --- a/packages/artplayer-plugin-danmuku/src/danmuku.js +++ b/packages/artplayer-plugin-danmuku/src/danmuku.ts @@ -1,13 +1,34 @@ +import type { DanmuInput, DanmuItem, DanmukuArt, DanmukuInput, DanmukuOption, DanmuState, NormalizedDanmu, NormalizedOption } from './types' + import { defaultOption, normalizeOption, optionChanged, optionScheme } from './config' import { beginInput, cancelInputs, inputActive, readInput } from './input' import { filterState, readyItems, setItemState } from './queue' import Renderer from './renderer' import Scheduler from './scheduler' import WorkerClient from './worker-client' -import DanmuWorker from './worker.js?worker&inline' +import DanmuWorker from './worker.ts?worker&inline' export default class Danmuku { - constructor(art, option) { + declare ['constructor']: typeof Danmuku + declare art: DanmukuArt + declare utils: DanmukuArt['constructor']['utils'] + declare validator: DanmukuArt['constructor']['validator'] + declare $danmuku: HTMLDivElement + declare $player: HTMLDivElement + declare queue: DanmuItem[] + declare $refs: HTMLDivElement[] + declare isStop: boolean + declare isHide: boolean + declare timer: number | null + declare index: number + declare worker: Worker | null + declare workerClient: WorkerClient | null + declare renderer: Renderer + declare scheduler: Scheduler + declare option: NormalizedOption + declare states: Record + + constructor(art: DanmukuArt, option: DanmukuOption) { const { constructor, template } = art this.utils = constructor.utils // 工具库 @@ -99,8 +120,8 @@ export default class Danmuku { } // 是否在移动端使用了自动旋屏,会影响弹幕的left和top值 - get isRotate() { - return this.art.plugins?.autoOrientation?.state + get isRotate(): boolean | undefined { + return (this.art.plugins?.autoOrientation as { state?: boolean } | undefined)?.state } // 计算上空白边距 @@ -181,7 +202,7 @@ export default class Danmuku { } // 加载弹幕 - async load(danmuku) { + async load(danmuku?: DanmukuInput) { const { errorHandle } = this.utils const task = beginInput(this, danmuku === undefined) @@ -196,6 +217,8 @@ export default class Danmuku { return this errorHandle(Array.isArray(danmus), 'Danmuku need return an array as result') + // Active input and the runtime validator exclude the cancellation sentinel. + const rows = danmus as DanmuInput[] // 假如没有传入弹幕参数,则清空弹幕,否则追加弹幕 if (danmuku === undefined) { @@ -208,10 +231,10 @@ export default class Danmuku { } // 逐个验证原始弹幕并转换到弹幕队列 - for (let index = 0; index < danmus.length; index++) { + for (let index = 0; index < rows.length; index++) { if (!task.active()) return this - const danmu = danmus[index] + const danmu = rows[index]! await task.emit(danmu) } @@ -232,7 +255,7 @@ export default class Danmuku { } // 把原始弹幕转换到弹幕队列 - async emit(danmu) { + async emit(danmu: DanmuInput) { const { clamp } = this.utils if (!inputActive(this)) return this @@ -279,7 +302,8 @@ export default class Danmuku { return this // 自定义弹幕过滤函数 - if (!this.option.filter(danmu)) + // Validation/defaulting above fills these fields before the public callback. + if (!this.option.filter(danmu as NormalizedDanmu)) return this if (!inputActive(this)) return this @@ -287,7 +311,7 @@ export default class Danmuku { // 添加自定义属性 const item = { ...danmu, - $state: 'wait', // 弹幕初始状态 + $state: 'wait' as const, // 弹幕初始状态 $index: this.index++, // 弹幕索引 $ref: null, // 弹幕 DOM 节点 $restTime: 0, // 弹幕剩余时间 @@ -295,10 +319,10 @@ export default class Danmuku { } // 转换为wait状态 - this.setState(item, 'wait') + this.setState(item as DanmuItem, 'wait') // 添加到实际弹幕队列 - this.queue.push(item) + this.queue.push(item as DanmuItem) // 弹幕有四个状态: // - wait: 弹幕还未开始显示,没有被添加到 DOM 中 @@ -310,7 +334,7 @@ export default class Danmuku { } // 动态配置 - config(option, isInit = false) { + config(option: DanmukuOption, isInit = false) { const { clamp } = this.utils const { $controlsCenter } = this.art.template @@ -359,13 +383,14 @@ export default class Danmuku { } // 计算DOM的left值,受到旋屏影响 - getLeft($ref) { + getLeft($ref: HTMLElement): number { return this.renderer.left($ref) } // 复杂运算交给 Web Worker 处理 - postMessage(message = {}) { - return this.workerClient.request(message) + // Keep the historical empty default; unknown message types receive no reply. + postMessage(message: Parameters[0] = {} as Parameters[0]) { + return this.workerClient!.request(message) } createWorker() { @@ -374,17 +399,17 @@ export default class Danmuku { } // 根据状态获取弹幕 - filter(state, callback) { + filter(state: DanmuState, callback: (danmu: DanmuItem) => void) { return filterState(this, state, callback) } // 设置弹幕状态 - setState(danmu, state) { + setState(danmu: DanmuItem, state: DanmuState) { setItemState(this, danmu, state) } // 重置弹幕到wait状态,回收弹幕DOM节点 - makeWait(danmu) { + makeWait(danmu: DanmuItem) { this.renderer.makeWait(danmu) } @@ -454,7 +479,7 @@ export default class Danmuku { show() { this.isHide = false - this.$danmuku.style.opacity = 1 + this.$danmuku.style.opacity = 1 as unknown as string this.option.visible = true this.art.emit('artplayerPluginDanmuku:show') return this @@ -463,7 +488,7 @@ export default class Danmuku { hide() { this.isHide = true this.scheduler.invalidate() - this.$danmuku.style.opacity = 0 + this.$danmuku.style.opacity = 0 as unknown as string this.option.visible = false this.art.emit('artplayerPluginDanmuku:hide') this.scheduler.schedule() diff --git a/packages/artplayer-plugin-danmuku/src/heatmap-geometry.js b/packages/artplayer-plugin-danmuku/src/heatmap-geometry.ts similarity index 67% rename from packages/artplayer-plugin-danmuku/src/heatmap-geometry.js rename to packages/artplayer-plugin-danmuku/src/heatmap-geometry.ts index 846f3bab3..eef0fa984 100644 --- a/packages/artplayer-plugin-danmuku/src/heatmap-geometry.js +++ b/packages/artplayer-plugin-danmuku/src/heatmap-geometry.ts @@ -1,19 +1,36 @@ +import type { DanmuItem, HeatmapOptions, HeatmapPoint } from './types' import { MAX_HEATMAP_POINTS, sampleHeatmap } from './heatmap-sampling' -function map(value, inMin, inMax, outMin, outMax) { +function map(value: number, inMin: number, inMax: number, outMin: number, outMax: number): number { return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin } -function line(pointA, pointB) { +function line(pointA: HeatmapPoint, pointB: HeatmapPoint) { const lengthX = pointB[0] - pointA[0] const lengthY = pointB[1] - pointA[1] return { length: Math.sqrt(lengthX ** 2 + lengthY ** 2), angle: Math.atan2(lengthY, lengthX) } } -export function heatmapGeometry({ width, height, duration, queue, option, points: input = [] }) { +interface GeometryInput { + width: number + height: number + duration: number + queue: readonly Pick[] + option: boolean | HeatmapOptions + points?: HeatmapPoint[] +} + +export interface HeatmapGeometry { + path: string + opacity: number + width: number + height: number +} + +export function heatmapGeometry({ width, height, duration, queue, option, points: input = [] }: GeometryInput): HeatmapGeometry | null { if (![width, height, duration].every(value => Number.isFinite(value) && value > 0)) return null - const defaults = { + const defaults: Required = { xMin: 0, xMax: width, yMin: 0, @@ -25,9 +42,9 @@ export function heatmapGeometry({ width, height, duration, queue, option, points smoothing: 0.2, flattening: 0.2, } - const configured = Object.assign({}, typeof option === 'object' ? option : {}) + const configured: HeatmapOptions = Object.assign({}, typeof option === 'object' ? option : {}) const options = Object.assign({}, defaults, configured) - for (const key of Object.keys(defaults)) { + for (const key of Object.keys(defaults) as (keyof HeatmapOptions)[]) { if (!Number.isFinite(options[key])) options[key] = defaults[key] } @@ -39,7 +56,8 @@ export function heatmapGeometry({ width, height, duration, queue, option, points const points = hasCustomPoints ? [...input] : sampleHeatmap(queue, width, duration, options.sampling) if (!points.length || !points.every(point => Array.isArray(point) && Number.isFinite(point[0]) && Number.isFinite(point[1]))) { return null } - const lastPoint = points[points.length - 1] + // The nonempty check above guarantees both endpoints used below. + const lastPoint = points[points.length - 1]! if (lastPoint[0] !== width) { if (points.length === MAX_HEATMAP_POINTS) return null @@ -65,8 +83,8 @@ export function heatmapGeometry({ width, height, duration, queue, option, points const fitted = automatic && peak > defaults.yMax / 4 if (fitted) options.yMax = peak * 4 - const boundY = value => fitted ? Math.min(height, Math.max(height * 0.75, value)) : value - const controlPoint = (current, previous, next, reverse) => { + const boundY = (value: number) => fitted ? Math.min(height, Math.max(height * 0.75, value)) : value + const controlPoint = (current: HeatmapPoint, previous: HeatmapPoint | undefined, next: HeatmapPoint | undefined, reverse?: boolean): HeatmapPoint => { const geometry = line(previous || current, next || current) const flat = map(Math.cos(geometry.angle) * options.flattening, 0, 1, 1, 0) const angle = geometry.angle * flat + (reverse ? Math.PI : 0) @@ -74,13 +92,14 @@ export function heatmapGeometry({ width, height, duration, queue, option, points // A cubic stays inside the convex hull of its endpoints and controls. return [current[0] + Math.cos(angle) * length, boundY(current[1] + Math.sin(angle) * length)] } - const positions = points.map(point => [map(point[0], options.xMin, options.xMax, 0, width), boundY(map(point[1], options.yMin, options.yMax, height, 0))]) + const positions = points.map(point => [map(point[0], options.xMin, options.xMax, 0, width), boundY(map(point[1], options.yMin, options.yMax, height, 0))]) if (!positions.every(point => point.every(Number.isFinite))) return null const path = positions.map((point, index, all) => { if (index === 0) - return `M ${all[all.length - 1][0]},${height} L ${point[0]},${height} L ${point[0]},${point[1]}` - const start = controlPoint(all[index - 1], all[index - 2], point) + return `M ${all[all.length - 1]![0]},${height} L ${point[0]},${height} L ${point[0]},${point[1]}` + // Every noninitial point has its immediately preceding control point. + const start = controlPoint(all[index - 1]!, all[index - 2], point) const end = controlPoint(point, all[index - 1], all[index + 1], true) return `C ${start[0]},${start[1]} ${end[0]},${end[1]} ${point[0]},${point[1]}${index === all.length - 1 ? ' z' : ''}` }).join(' ') diff --git a/packages/artplayer-plugin-danmuku/src/heatmap-sampling.js b/packages/artplayer-plugin-danmuku/src/heatmap-sampling.ts similarity index 71% rename from packages/artplayer-plugin-danmuku/src/heatmap-sampling.js rename to packages/artplayer-plugin-danmuku/src/heatmap-sampling.ts index 805c0da66..d9f7232e7 100644 --- a/packages/artplayer-plugin-danmuku/src/heatmap-sampling.js +++ b/packages/artplayer-plugin-danmuku/src/heatmap-sampling.ts @@ -1,22 +1,25 @@ +import type { DanmuItem, HeatmapPoint } from './types' + export const MAX_HEATMAP_POINTS = 0xFFFFFFFF -function upperBound(values, value) { +function upperBound(values: readonly number[], value: number): number { let start = 0 let end = values.length while (start < end) { const middle = Math.floor((start + end) / 2) - if (values[middle] <= value) + // The binary-search interval guarantees middle is an existing index. + if (values[middle]! <= value) start = middle + 1 else end = middle } return start } -export function sampleHeatmap(queue, width, duration, sampling) { +export function sampleHeatmap(queue: readonly Pick[], width: number, duration: number, sampling: number): HeatmapPoint[] { if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(duration) || duration <= 0 || !Number.isFinite(sampling) || sampling <= 0 || Math.floor(width / sampling) + 1 > MAX_HEATMAP_POINTS) { return [] } const times = queue.map(item => item.time).filter(time => typeof time === 'number' && !Number.isNaN(time)).sort((a, b) => a - b) - const points = [] + const points: HeatmapPoint[] = [] const gap = duration / width if (!Number.isFinite(gap)) return [] diff --git a/packages/artplayer-plugin-danmuku/src/heatmap.js b/packages/artplayer-plugin-danmuku/src/heatmap.ts similarity index 63% rename from packages/artplayer-plugin-danmuku/src/heatmap.js rename to packages/artplayer-plugin-danmuku/src/heatmap.ts index 3e9f67348..436cefc29 100644 --- a/packages/artplayer-plugin-danmuku/src/heatmap.js +++ b/packages/artplayer-plugin-danmuku/src/heatmap.ts @@ -1,32 +1,47 @@ +import type { DanmuItem, DanmukuArt, HeatmapOptions, HeatmapPoint } from './types' import { heatmapGeometry } from './heatmap-geometry' let nextGradient = 0 -export default function heatmap(art, danmuku, option) { +type HeatmapArt = Pick +interface HeatmapEvents { + 'destroy': [] + 'video:timeupdate': [] + 'setBar': [type: string, percentage: number] + 'ready': [] + 'resize': [] + 'artplayerPluginDanmuku:loaded': [] + 'artplayerPluginDanmuku:points': [points: HeatmapPoint[]] +} +type HeatmapCallback = (...args: unknown[]) => unknown + +export default function heatmap(art: HeatmapArt, danmuku: { queue: readonly Pick[] }, option: boolean | HeatmapOptions): (() => void) | undefined { if (art.isDestroy) return const { query } = art.constructor.utils - let gradient - const subscriptions = [] - let element - let start - let stop + let gradient: string | undefined + const subscriptions: [keyof HeatmapEvents, HeatmapCallback][] = [] + let element: HTMLElement | undefined + let start: Element | null | undefined + let stop: Element | null | undefined let closed = false const active = () => !closed && !art.isDestroy - function listen(name, callback) { - subscriptions.push([name, callback]) - art.on(name, callback) + function listen(name: Name, callback: (...args: HeatmapEvents[Name]) => unknown) { + // Core's string-event overload erases payloads; this local map checks each subscription. + const listener = callback as HeatmapCallback + subscriptions.push([name, listener]) + art.on(name, listener) } - function dispose(removeControl) { + function dispose(removeControl: boolean) { if (closed) return closed = true start = null stop = null - let failure + let failure: unknown let failed = false - const attempt = (callback) => { + const attempt = (callback: () => unknown) => { try { callback() } @@ -44,29 +59,31 @@ export default function heatmap(art, danmuku, option) { if (failed) throw failure } - function progress(value) { + function progress(value: number) { if (active() && start && stop) { start.setAttribute('offset', `${value * 100}%`) stop.setAttribute('offset', `${value * 100}%`) } } - function draw(points = []) { + function draw(points: HeatmapPoint[] = []) { if (!active()) return start = null stop = null - element.innerHTML = '' + // Drawing listeners are registered only after mounted assigns element; dispose closes first. + const target = element! + target.innerHTML = '' if (art.option.isLive) return - const shape = heatmapGeometry({ width: element.offsetWidth, height: element.offsetHeight, duration: art.duration, queue: danmuku.queue, option, points }) + const shape = heatmapGeometry({ width: target.offsetWidth, height: target.offsetHeight, duration: art.duration, queue: danmuku.queue, option, points }) if (!shape || !active()) return - const document = element.ownerDocument + const document = target.ownerDocument if (!gradient) gradient = document ? 'heatmap-solids' : `heatmap-solids-${++nextGradient}` while (document?.getElementById(gradient)) gradient = `heatmap-solids-${++nextGradient}` - element.innerHTML = ` + target.innerHTML = ` @@ -79,11 +96,11 @@ export default function heatmap(art, danmuku, option) { ` - start = query('#heatmap-start', element) - stop = query('#heatmap-stop', element) + start = query('#heatmap-start', target) + stop = query('#heatmap-stop', target) progress(art.played) } - function update(points) { + function update(points?: HeatmapPoint[]) { try { draw(points) } diff --git a/packages/artplayer-plugin-danmuku/src/index.js b/packages/artplayer-plugin-danmuku/src/index.ts similarity index 60% rename from packages/artplayer-plugin-danmuku/src/index.js rename to packages/artplayer-plugin-danmuku/src/index.ts index f4e58131b..c05936e5f 100644 --- a/packages/artplayer-plugin-danmuku/src/index.js +++ b/packages/artplayer-plugin-danmuku/src/index.ts @@ -1,15 +1,19 @@ +import type Artplayer from 'artplayer' +import type { DanmukuArt, DanmukuOption } from './types' + import Danmuku from './danmuku' import heatmap from './heatmap' import Setting from './setting' -export default function artplayerPluginDanmuku(option) { - return (art) => { - const danmuku = new Danmuku(art, option) - let setting +export default function artplayerPluginDanmuku(option: DanmukuOption) { + return (art: Artplayer) => { + // Old declarations type instance.constructor as Function; both supported cores expose these statics. + const danmuku = new Danmuku(art as DanmukuArt, option) + let setting: Setting | undefined try { - setting = art.isDestroy ? undefined : new Setting(art, danmuku) + setting = art.isDestroy ? undefined : new Setting(art as DanmukuArt, danmuku) if (!art.isDestroy && danmuku.option.heatmap) { - heatmap(art, danmuku, danmuku.option.heatmap) + heatmap(art as DanmukuArt, danmuku, danmuku.option.heatmap) } } catch (error) { @@ -25,7 +29,7 @@ export default function artplayerPluginDanmuku(option) { } return { - name: 'artplayerPluginDanmuku', + name: 'artplayerPluginDanmuku' as const, emit: danmuku.emit.bind(danmuku), load: danmuku.load.bind(danmuku), config: danmuku.config.bind(danmuku), diff --git a/packages/artplayer-plugin-danmuku/src/input.js b/packages/artplayer-plugin-danmuku/src/input.ts similarity index 60% rename from packages/artplayer-plugin-danmuku/src/input.js rename to packages/artplayer-plugin-danmuku/src/input.ts index 83442a21a..f6ad9a06a 100644 --- a/packages/artplayer-plugin-danmuku/src/input.js +++ b/packages/artplayer-plugin-danmuku/src/input.ts @@ -1,22 +1,45 @@ +import type { DanmuInput, DanmukuInput } from './types' import { bilibiliDanmuParseFromUrl } from './bilibili' import { isPromiseInput } from './config' -const inputs = new WeakMap() -const cancelled = Symbol('cancelled danmuku input') - -function stateFor(owner) { - if (!inputs.has(owner)) - inputs.set(owner, { closed: false, tasks: new Set(), emitting: [] }) - return inputs.get(owner) +interface InputOwner { + art: { isDestroy: boolean } + emit: (danmu: DanmuInput) => unknown } -export function inputActive(owner) { +export interface InputTask { + replace: boolean + signal: AbortSignal | undefined + active: () => boolean + onCancel: (callback: () => void) => () => void + cancel: () => void + wait: (value: T) => Promise | typeof cancelled> + emit: (danmu: DanmuInput) => unknown + finish: () => void +} + +interface InputState { + closed: boolean + tasks: Set + emitting: InputTask[] +} + +const inputs = new WeakMap() +export const cancelled = Symbol('cancelled danmuku input') + +function stateFor(owner: InputOwner): InputState { + if (!inputs.has(owner)) + inputs.set(owner, { closed: false, tasks: new Set(), emitting: [] }) + return inputs.get(owner)! +} + +export function inputActive(owner: InputOwner) { const state = stateFor(owner) const task = state.emitting[state.emitting.length - 1] return !state.closed && !owner.art.isDestroy && (!task || task.active()) } -export function beginInput(owner, replace) { +export function beginInput(owner: InputOwner, replace: boolean): InputTask { const state = stateFor(owner) if (replace) { for (const task of [...state.tasks]) { @@ -25,11 +48,11 @@ export function beginInput(owner, replace) { } } let stopped = state.closed || owner.art.isDestroy - let resolveCancel - const cancellation = new Promise(resolve => resolveCancel = resolve) - const handlers = new Set() + let resolveCancel!: (value: typeof cancelled) => void + const cancellation = new Promise(resolve => resolveCancel = resolve) + const handlers = new Set<() => void>() const controller = typeof AbortController === 'function' ? new AbortController() : undefined - const task = { + const task: InputTask = { replace, signal: controller?.signal, active: () => !stopped && !state.closed && !owner.art.isDestroy, @@ -71,13 +94,15 @@ export function beginInput(owner, replace) { return task } -export function cancelInputs(owner) { +export function cancelInputs(owner: InputOwner) { const state = stateFor(owner) state.closed = true for (const task of [...state.tasks]) task.cancel() } -export function readInput(target, task) { +export function readInput(target: DanmukuInput, task: InputTask): + | { asynchronous: true, value: DanmuInput[] | PromiseLike } + | { asynchronous: false, value: DanmuInput[] } { if (typeof target === 'function') return { asynchronous: true, value: target() } if (isPromiseInput(target)) diff --git a/packages/artplayer-plugin-danmuku/src/parser-types.ts b/packages/artplayer-plugin-danmuku/src/parser-types.ts new file mode 100644 index 000000000..d1d9cf156 --- /dev/null +++ b/packages/artplayer-plugin-danmuku/src/parser-types.ts @@ -0,0 +1,20 @@ +import type { DanmuInput } from './types' + +export interface ParsedDanmu extends DanmuInput { + time: number + mode: number + fontSize: number + color: string + timestamp: number + pool: number + userID: string + rowID: number +} + +export interface ParserRequest { xml: unknown, id: number } +export interface ParserReply { danmus: ParsedDanmu[], id: number } + +export interface ParserCancellation { + signal?: AbortSignal + onCancel?: (callback: () => void) => () => void +} diff --git a/packages/artplayer-plugin-danmuku/src/queue.js b/packages/artplayer-plugin-danmuku/src/queue.ts similarity index 57% rename from packages/artplayer-plugin-danmuku/src/queue.js rename to packages/artplayer-plugin-danmuku/src/queue.ts index 083eea245..65e01fb13 100644 --- a/packages/artplayer-plugin-danmuku/src/queue.js +++ b/packages/artplayer-plugin-danmuku/src/queue.ts @@ -1,12 +1,16 @@ -export function filterState(owner, state, callback) { +import type { DanmuItem, DanmuState } from './types' + +interface QueueOwner { states: Record, art: { currentTime: number } } + +export function filterState(owner: QueueOwner, state: DanmuState, callback: (danmu: DanmuItem) => void) { const danmus = owner.states[state] || [] - for (let index = 0; index < danmus.length; index++) callback(danmus[index]) + for (let index = 0; index < danmus.length; index++) callback(danmus[index]!) return danmus } -export function readyItems(owner) { +export function readyItems(owner: QueueOwner) { const { currentTime } = owner.art - const result = [] + const result: DanmuItem[] = [] filterState(owner, 'ready', danmu => result.push(danmu)) filterState(owner, 'wait', (danmu) => { if (currentTime + 0.1 >= danmu.time && danmu.time >= currentTime - 0.1) @@ -15,7 +19,7 @@ export function readyItems(owner) { return result } -export function setItemState(owner, danmu, state) { +export function setItemState(owner: QueueOwner, danmu: DanmuItem, state: DanmuState) { owner.states[danmu.$state] = owner.states[danmu.$state].filter(item => item !== danmu) danmu.$state = state if (danmu.$ref) diff --git a/packages/artplayer-plugin-danmuku/src/renderer.js b/packages/artplayer-plugin-danmuku/src/renderer.ts similarity index 64% rename from packages/artplayer-plugin-danmuku/src/renderer.js rename to packages/artplayer-plugin-danmuku/src/renderer.ts index d11d3876f..4cbc86087 100644 --- a/packages/artplayer-plugin-danmuku/src/renderer.js +++ b/packages/artplayer-plugin-danmuku/src/renderer.ts @@ -1,5 +1,12 @@ +import type Danmuku from './danmuku' +import type { FrameOperation } from './scheduler' +import type { DanmuItem } from './types' + export default class Renderer { - constructor(owner) { + declare owner: Danmuku + declare nodes: Set + + constructor(owner: Danmuku) { this.owner = owner this.nodes = new Set() } @@ -14,21 +21,21 @@ export default class Renderer { return $ref } - get visibles() { - const result = [] + get visibles(): { top: number, left: number, height: number, width: number, right: number, speed: number, distance: number, time: number, mode: number }[] { + const result: Renderer['visibles'] = [] const { clientWidth } = this.owner.$player const clientLeft = this.owner.getLeft(this.owner.$player) this.owner.filter('emit', (danmu) => { - const top = danmu.$ref.offsetTop - const left = this.owner.getLeft(danmu.$ref) - clientLeft - const height = danmu.$ref.clientHeight - const width = danmu.$ref.clientWidth + const top = danmu.$ref!.offsetTop + const left = this.owner.getLeft(danmu.$ref!) - clientLeft + const height = danmu.$ref!.clientHeight + const width = danmu.$ref!.clientWidth const distance = left + width const right = clientWidth - distance const speed = distance / danmu.$restTime - const emit = {} + const emit = {} as Renderer['visibles'][number] emit.top = top emit.left = left emit.height = height @@ -45,21 +52,21 @@ export default class Renderer { return result } - left($ref) { + left($ref: HTMLElement): number { const rect = $ref.getBoundingClientRect() return this.owner.isRotate ? rect.top : rect.left } - makeWait(danmu) { + makeWait(danmu: DanmuItem) { this.owner.setState(danmu, 'wait') if (danmu.$ref) { - danmu.$ref.style.cssText = this.owner.constructor.cssText - danmu.$ref.style.visibility = 'hidden' - danmu.$ref.style.marginLeft = '0px' - danmu.$ref.style.transform = 'translateX(0px)' - danmu.$ref.style.transition = 'transform 0s linear 0s' + danmu.$ref!.style.cssText = this.owner.constructor.cssText + danmu.$ref!.style.visibility = 'hidden' + danmu.$ref!.style.marginLeft = '0px' + danmu.$ref!.style.transform = 'translateX(0px)' + danmu.$ref!.style.transition = 'transform 0s linear 0s' this.owner.$refs.push(danmu.$ref) - danmu.$ref = null + danmu!.$ref = null } } @@ -70,7 +77,7 @@ export default class Renderer { switch (danmu.mode) { // 滚动的弹幕 case 0: - danmu.$ref.style.left = `${clientWidth}px` + danmu.$ref!.style.left = `${clientWidth}px` break default: break @@ -82,10 +89,10 @@ export default class Renderer { switch (danmu.mode) { // 滚动的弹幕 case 0: { - const distance = clientWidth + danmu.$ref.clientWidth - danmu.$ref.style.left = `${clientWidth}px` - danmu.$ref.style.transform = `translateX(${-distance}px)` - danmu.$ref.style.transition = `transform ${danmu.$restTime}s linear 0s` + const distance = clientWidth + danmu.$ref!.clientWidth + danmu.$ref!.style.left = `${clientWidth}px` + danmu.$ref!.style.transform = `translateX(${-distance}px)` + danmu.$ref!.style.transition = `transform ${danmu.$restTime}s linear 0s` break } default: @@ -102,9 +109,9 @@ export default class Renderer { switch (danmu.mode) { // 继续滚动的弹幕 case 0: { - const distance = clientWidth + danmu.$ref.clientWidth - danmu.$ref.style.transform = `translateX(${-distance}px)` - danmu.$ref.style.transition = `transform ${danmu.$restTime}s linear 0s` + const distance = clientWidth + danmu.$ref!.clientWidth + danmu.$ref!.style.transform = `translateX(${-distance}px)` + danmu.$ref!.style.transition = `transform ${danmu.$restTime}s linear 0s` break } default: @@ -122,9 +129,9 @@ export default class Renderer { switch (danmu.mode) { // 停止滚动的弹幕 case 0: { - const translateX = clientWidth - (this.owner.getLeft(danmu.$ref) - this.owner.getLeft(this.owner.$player)) - danmu.$ref.style.transform = `translateX(${-translateX}px)` - danmu.$ref.style.transition = 'transform 0s linear 0s' + const translateX = clientWidth - (this.owner.getLeft(danmu.$ref!) - this.owner.getLeft(this.owner.$player)) + danmu.$ref!.style.transform = `translateX(${-translateX}px)` + danmu.$ref!.style.transition = 'transform 0s linear 0s' break } default: @@ -135,7 +142,7 @@ export default class Renderer { return this } - prepare(danmu, operation) { + prepare(danmu: DanmuItem, operation: FrameOperation) { const owner = this.owner const ref = owner.$ref operation.danmu = danmu @@ -143,19 +150,19 @@ export default class Renderer { danmu.$ref = ref ref.textContent = danmu.text owner.$danmuku.appendChild(ref) - ref.style.opacity = owner.option.opacity + ref.style.opacity = owner.option.opacity as unknown as string ref.style.fontSize = `${owner.fontSize}px` ref.style.color = danmu.color - ref.style.border = danmu.border ? `1px solid ${danmu.color}` : null - ref.style.backgroundColor = danmu.border ? 'rgb(0 0 0 / 50%)' : null + ref.style.border = danmu.border ? `1px solid ${danmu.color}` : null as unknown as string + ref.style.backgroundColor = danmu.border ? 'rgb(0 0 0 / 50%)' : null as unknown as string owner.utils.setStyles(ref, danmu.style) return ref } - place(danmu, ref, top, distance, clientWidth) { + place(danmu: DanmuItem, ref: HTMLDivElement, top: number, distance: number, clientWidth: number) { ref.style.top = `${top}px` ref.style.visibility = 'visible' - ref.dataset.mode = danmu.mode + ref.dataset.mode = danmu.mode as unknown as string ref.dataset.id = danmu.id || '' switch (danmu.mode) { case 0: @@ -174,13 +181,13 @@ export default class Renderer { } } - release(operation) { + release(operation: FrameOperation) { const { danmu, ref } = operation - if (ref && danmu.$ref === ref) { + if (ref && danmu!.$ref === ref) { ref.style.cssText = this.owner.constructor.cssText ref.style.visibility = 'hidden' this.owner.$refs.push(ref) - danmu.$ref = null + danmu!.$ref = null } operation.ref = null } diff --git a/packages/artplayer-plugin-danmuku/src/scheduler.js b/packages/artplayer-plugin-danmuku/src/scheduler.ts similarity index 76% rename from packages/artplayer-plugin-danmuku/src/scheduler.js rename to packages/artplayer-plugin-danmuku/src/scheduler.ts index 8425504c7..231c61cae 100644 --- a/packages/artplayer-plugin-danmuku/src/scheduler.js +++ b/packages/artplayer-plugin-danmuku/src/scheduler.ts @@ -1,7 +1,28 @@ +import type Danmuku from './danmuku' +import type { DanmuItem } from './types' + const cancelled = Symbol('cancelled danmuku frame') +export interface FrameOperation { + generation: number + cancel: () => void + wait: (value: T | PromiseLike) => Promise + danmu?: DanmuItem + ref: HTMLDivElement | null +} + export default class Scheduler { - constructor(owner) { + declare owner: Danmuku + declare generation: number + declare starts: number + declare frame: number | null + declare operation: FrameOperation | null + declare closed: boolean + declare running: boolean + declare fault: boolean + declare failedItems: Set + + constructor(owner: Danmuku) { this.owner = owner this.generation = 0 this.starts = 0 @@ -13,13 +34,13 @@ export default class Scheduler { this.failedItems = new Set() } - active(operation) { + active(operation: FrameOperation) { const owner = this.owner return !this.closed && !this.fault && !owner.art.isDestroy && !owner.isStop && !owner.isHide && operation.generation === this.generation } - release(operation) { + release(operation: FrameOperation) { this.owner.renderer.release(operation) } @@ -39,7 +60,7 @@ export default class Scheduler { this.failedItems.clear() } - report(error) { + report(error: unknown) { try { this.owner.art.emit('artplayerPluginDanmuku:error', error) } @@ -48,7 +69,7 @@ export default class Scheduler { } } - fail(error) { + fail(error: unknown) { if (this.closed || this.fault) return this.fault = true @@ -81,9 +102,9 @@ export default class Scheduler { owner.timer = null if (this.closed || this.fault || owner.isStop || owner.art.isDestroy) return - let cancel - const cancellation = new Promise(resolve => cancel = () => resolve(cancelled)) - const operation = { generation: this.generation, cancel, wait: value => Promise.race([value, cancellation]), ref: null } + let cancel!: () => void + const cancellation = new Promise(resolve => cancel = () => resolve(cancelled)) + const operation: FrameOperation = { generation: this.generation, cancel, wait: value => Promise.race([value, cancellation]), ref: null } this.operation = operation return this.run(operation).catch((error) => { if (this.active(operation)) @@ -99,7 +120,7 @@ export default class Scheduler { owner.timer = this.frame } - async run(operation) { + async run(operation: FrameOperation) { const owner = this.owner if (!owner.art.playing || !this.active(operation)) return @@ -139,16 +160,18 @@ export default class Scheduler { danmu.$lastStartTime = Date.now() danmu.$restTime = owner.speed const distance = clientWidth + ref.clientWidth - const { result: top } = await operation.wait(owner.postMessage({ + const reply = await operation.wait(owner.postMessage({ type: 'getDanmuTop', target: { mode: danmu.mode, height: ref.clientHeight, speed: distance / danmu.$restTime }, visibles: owner.visibles, antiOverlap: owner.option.antiOverlap, clientWidth, clientHeight, - marginBottom: owner.marginBottom, + // Valid margins are numeric. Preserve the old raw fallback for unsupported strings. + marginBottom: owner.marginBottom as number, marginTop: owner.marginTop, })) + const top = typeof reply === 'symbol' ? undefined : reply.result if (!this.active(operation) || danmu.$ref !== ref) return if (top !== undefined) { diff --git a/packages/artplayer-plugin-danmuku/src/setting-lifecycle.js b/packages/artplayer-plugin-danmuku/src/setting-lifecycle.ts similarity index 57% rename from packages/artplayer-plugin-danmuku/src/setting-lifecycle.js rename to packages/artplayer-plugin-danmuku/src/setting-lifecycle.ts index 83e86356f..fe56c7e8c 100644 --- a/packages/artplayer-plugin-danmuku/src/setting-lifecycle.js +++ b/packages/artplayer-plugin-danmuku/src/setting-lifecycle.ts @@ -1,7 +1,35 @@ -const properties = new WeakMap() +import type { DanmukuArt } from './types' + +type Disposer = () => unknown +type PropertyValue = string | number | boolean | undefined +interface PropertyEntry { + original: PropertyValue + present: boolean + priority: string | undefined + owners: Map + apply: (value: PropertyValue) => unknown + last?: PropertyValue + lastPriority?: string +} +interface SettingEvents { + 'resize': [] + 'fullscreen': [state: boolean] + 'fullscreenWeb': [state: boolean] + 'artplayerPluginDanmuku:show': [] + 'artplayerPluginDanmuku:hide': [] + 'document:pointermove': [event: PointerEvent] + 'document:pointerup': [event: PointerEvent] +} +const properties = new WeakMap>() export default class SettingLifecycle { - constructor(art) { + declare art: DanmukuArt + declare closed: boolean + declare disposers: Set + declare cancelled: Promise + declare cancel: () => void + + constructor(art: DanmukuArt) { this.art = art this.closed = false this.disposers = new Set() @@ -12,13 +40,15 @@ export default class SettingLifecycle { return !this.closed && !this.art.isDestroy } - own(dispose) { + own(dispose: Disposer) { if (this.closed) dispose() else this.disposers.add(dispose) } - write(target, key, value, apply = value => target[key] = value) { + write(host: DOMStringMap | CSSStyleDeclaration, key: string, value: PropertyValue, apply: (value: PropertyValue) => unknown = value => (host as unknown as Record)[key] = value) { + // This adapter retains native dataset/style assignment and its WebIDL coercion. + const target = host as unknown as Record & Partial> if (!this.active) return const priority = () => typeof target.getPropertyPriority === 'function' ? target.getPropertyPriority(key) : undefined @@ -52,7 +82,7 @@ export default class SettingLifecycle { entry.lastPriority = priority() } else if (entry.priority !== undefined) { - target.setProperty(key, entry.original, entry.priority) + target.setProperty!(key, entry.original as string, entry.priority) } else if (entry.present) { entry.apply(entry.original) @@ -75,13 +105,15 @@ export default class SettingLifecycle { entry.lastPriority = priority() } - on(name, callback) { + on(name: Name, callback: (this: unknown, ...args: SettingEvents[Name]) => unknown) { if (!this.active) return + // Keep the emitter/native callback receiver while consulting this owner. + // eslint-disable-next-line ts/no-this-alias const lifecycle = this - function listener(...args) { + function listener(this: unknown, ...args: unknown[]) { if (lifecycle.active) - return callback.apply(this, args) + return callback.apply(this, args as SettingEvents[Name]) } const dispose = () => this.art.off(name, listener) this.own(dispose) @@ -96,18 +128,20 @@ export default class SettingLifecycle { } } - proxy(target, name, callback) { + proxy(target: HTMLElement, name: Name, callback: (this: unknown, event: HTMLElementEventMap[Name]) => unknown) { if (!this.active) return + // Keep the emitter/native callback receiver while consulting this owner. + // eslint-disable-next-line ts/no-this-alias const lifecycle = this - function listener(...args) { + function listener(this: unknown, ...args: unknown[]) { if (lifecycle.active) - return callback.apply(this, args) + return callback.apply(this, args as [HTMLElementEventMap[Name]]) } - let remove + let remove: (() => void) | undefined const dispose = () => { if (typeof remove === 'function') { - const registry = this.art.events + const registry = this.art.events as DanmukuArt['events'] & { destroyEvents?: Set<() => void> } if (typeof registry?.remove === 'function' && registry.destroyEvents?.has(remove)) registry.remove(remove) else remove() @@ -128,11 +162,11 @@ export default class SettingLifecycle { } } - wait(value) { + wait(value: Value | PromiseLike) { return Promise.race([value, this.cancelled]) } - release(dispose) { + release(dispose: Disposer) { try { dispose() } diff --git a/packages/artplayer-plugin-danmuku/src/setting-send.js b/packages/artplayer-plugin-danmuku/src/setting-send.ts similarity index 76% rename from packages/artplayer-plugin-danmuku/src/setting-send.js rename to packages/artplayer-plugin-danmuku/src/setting-send.ts index 62ed5f876..1c59d3c61 100644 --- a/packages/artplayer-plugin-danmuku/src/setting-send.js +++ b/packages/artplayer-plugin-danmuku/src/setting-send.ts @@ -1,17 +1,20 @@ -export async function emitSetting(setting) { +import type Setting from './setting' +import type { DanmuInput } from './types' + +export async function emitSetting(setting: Setting) { const { lifecycle, template: { $input } } = setting if (!lifecycle.active) return const text = $input.value.trim() if (!text.length || setting.isLock || setting.emitting) return - const danmu = { + const danmu: DanmuInput = { text, mode: setting.option.mode, color: setting.option.color, time: setting.art.currentTime, } - const report = (error) => { + const report = (error: unknown) => { if (lifecycle.active) console.error('Error emitting danmuku:', error) } @@ -39,15 +42,15 @@ export async function emitSetting(setting) { } } -export function lockSetting(setting) { +export function lockSetting(setting: Setting) { if (!setting.lifecycle.active) return const { addClass } = setting.utils const { $send } = setting.template - clearTimeout(setting.timer) + clearTimeout(setting.timer as number | undefined) setting.isLock = true let time = setting.option.lockTime - $send.textContent = time + $send.textContent = time as unknown as string addClass($send, 'apd-lock') const loop = () => { setting.timer = setTimeout(() => { @@ -58,7 +61,7 @@ export function lockSetting(setting) { } else { time -= 1 - $send.textContent = time + $send.textContent = time as unknown as string loop() } }, 1000) @@ -66,8 +69,8 @@ export function lockSetting(setting) { loop() } -export function unlockSetting(setting) { - clearTimeout(setting.timer) +export function unlockSetting(setting: Setting) { + clearTimeout(setting.timer as number | undefined) setting.timer = null setting.isLock = false if (!setting.lifecycle.active) diff --git a/packages/artplayer-plugin-danmuku/src/setting-slider.js b/packages/artplayer-plugin-danmuku/src/setting-slider.ts similarity index 78% rename from packages/artplayer-plugin-danmuku/src/setting-slider.js rename to packages/artplayer-plugin-danmuku/src/setting-slider.ts index 65dab5573..3d05a2984 100644 --- a/packages/artplayer-plugin-danmuku/src/setting-slider.js +++ b/packages/artplayer-plugin-danmuku/src/setting-slider.ts @@ -1,10 +1,13 @@ -export default function createSlider({ min, max, container, findIndex, onChange, steps = [] }) { +import type Setting from './setting' +import type { SliderConfig } from './setting-types' + +export default function createSlider(this: Setting, { min, max, container, findIndex, onChange, steps = [] }: SliderConfig) { const { lifecycle } = this if (!lifecycle.active) return { reset() {} } const { query, clamp, setStyle } = this.utils - setStyle(container, 'touch-action', 'none') + setStyle(container, 'touch-action' as keyof CSSStyleDeclaration, 'none') container.innerHTML = `

@@ -19,14 +22,14 @@ export default function createSlider({ min, max, container, findIndex, onChange,
` - const $dot = query('.apd-slider-dot', container) - const $progress = query('.apd-slider-progress', container) + const $dot = query('.apd-slider-dot', container)! + const $progress = query('.apd-slider-progress', container)! let isDroging = false lifecycle.own(() => isDroging = false) - function reset(index) { + function reset(index?: number) { if (!lifecycle.active) return if (index === undefined) @@ -41,7 +44,7 @@ export default function createSlider({ min, max, container, findIndex, onChange, onChange(index) } - function updateLeft(event) { + function updateLeft(this: Setting, event: MouseEvent) { const { top, height, left, width } = container.getBoundingClientRect() if (this.art.isRotate) { const value = clamp(event.clientY - top, 0, height) diff --git a/packages/artplayer-plugin-danmuku/src/setting-style.js b/packages/artplayer-plugin-danmuku/src/setting-style.ts similarity index 68% rename from packages/artplayer-plugin-danmuku/src/setting-style.js rename to packages/artplayer-plugin-danmuku/src/setting-style.ts index 2d1aa68ae..4c095a60b 100644 --- a/packages/artplayer-plugin-danmuku/src/setting-style.js +++ b/packages/artplayer-plugin-danmuku/src/setting-style.ts @@ -3,26 +3,27 @@ import style from './style.less?inline' if (typeof document !== 'undefined') { const id = 'artplayer-plugin-danmuku' const pending = Symbol.for('artplayer-plugin-danmuku.pending-style') - let $style = document.getElementById(id) || document[pending] + const styleDocument = document as Document & { [key: symbol]: HTMLElement | undefined } + let $style = document.getElementById(id) || styleDocument[pending] if (!$style) { $style = document.createElement('style') $style.id = id if (document.readyState === 'loading') { - document[pending] = $style + styleDocument[pending] = $style const ready = () => { document.removeEventListener('DOMContentLoaded', ready) - if (document[pending] === $style) - delete document[pending] + if (styleDocument[pending] === $style) + delete styleDocument[pending] const existing = document.getElementById(id) if (existing) - existing.textContent = $style.textContent + existing.textContent = $style!.textContent else - (document.head || document.documentElement).appendChild($style) + (document.head || document.documentElement).appendChild($style!) } document.addEventListener('DOMContentLoaded', ready, { once: true }) } else { - (document.head || document.documentElement).appendChild($style) + (document.head || document.documentElement).appendChild($style!) } } $style.textContent = style diff --git a/packages/artplayer-plugin-danmuku/src/setting-template.js b/packages/artplayer-plugin-danmuku/src/setting-template.ts similarity index 98% rename from packages/artplayer-plugin-danmuku/src/setting-template.js rename to packages/artplayer-plugin-danmuku/src/setting-template.ts index b8597a03f..3649e3a4e 100644 --- a/packages/artplayer-plugin-danmuku/src/setting-template.js +++ b/packages/artplayer-plugin-danmuku/src/setting-template.ts @@ -1,3 +1,4 @@ +import type Setting from './setting' import $check_off from './img/check_off.svg?raw' import $check_on from './img/check_on.svg?raw' import $config from './img/config.svg?raw' @@ -28,7 +29,7 @@ export function settingIcons() { } } -export function settingTemplate() { +export function settingTemplate(this: Setting) { const { option } = this return `
diff --git a/packages/artplayer-plugin-danmuku/src/setting-types.ts b/packages/artplayer-plugin-danmuku/src/setting-types.ts new file mode 100644 index 000000000..76c1010fb --- /dev/null +++ b/packages/artplayer-plugin-danmuku/src/setting-types.ts @@ -0,0 +1,39 @@ +import type { SliderStep } from './types' + +// createTemplate/createSliders fill these slots before registering active UI callbacks. +// The initial null values and absent antiOverlap/syncVideo keys are kept for compatibility. +export interface SettingTemplate { + $controlsCenter: HTMLDivElement + $mount: HTMLElement + $danmuku: HTMLDivElement + $toggle: HTMLDivElement + $config: HTMLDivElement + $configPanel: HTMLDivElement + $configModes: HTMLDivElement + $style: HTMLDivElement + $stylePanel: HTMLDivElement + $styleModes: HTMLDivElement + $colors: HTMLDivElement + $antiOverlap?: HTMLDivElement + $syncVideo?: HTMLDivElement + $opacitySlider: HTMLDivElement + $opacityValue: HTMLDivElement + $marginSlider: HTMLDivElement + $marginValue: HTMLDivElement + $fontSizeSlider: HTMLDivElement + $fontSizeValue: HTMLDivElement + $speedSlider: HTMLDivElement + $speedValue: HTMLDivElement + $input: HTMLInputElement + $send: HTMLDivElement +} + +export interface SettingSlider { reset: (index?: number) => void } +export interface SliderConfig { + min: number + max: number + container: HTMLElement + findIndex: () => number + onChange: (index: number) => void + steps?: SliderStep[] +} diff --git a/packages/artplayer-plugin-danmuku/src/setting.js b/packages/artplayer-plugin-danmuku/src/setting.ts similarity index 81% rename from packages/artplayer-plugin-danmuku/src/setting.js rename to packages/artplayer-plugin-danmuku/src/setting.ts index 858ec8ee5..bd3bf02d0 100644 --- a/packages/artplayer-plugin-danmuku/src/setting.js +++ b/packages/artplayer-plugin-danmuku/src/setting.ts @@ -1,3 +1,6 @@ +import type Danmuku from './danmuku' +import type { SettingSlider, SettingTemplate, SliderConfig } from './setting-types' +import type { DanmukuArt, NormalizedOption, SliderOption } from './types' import SettingLifecycle from './setting-lifecycle' import { emitSetting, lockSetting, unlockSetting } from './setting-send' import createSlider from './setting-slider' @@ -5,7 +8,17 @@ import { settingIcons, settingTemplate } from './setting-template' import './setting-style' export default class Setting { - constructor(art, danmuku) { + declare art: DanmukuArt + declare danmuku: Danmuku + declare utils: DanmukuArt['constructor']['utils'] + declare template: SettingTemplate + declare slider: Record<'opacity' | 'margin' | 'fontSize' | 'speed', SettingSlider> + declare emitting: boolean + declare isLock: boolean + declare timer: ReturnType | null + declare lifecycle: SettingLifecycle + + constructor(art: DanmukuArt, danmuku: Danmuku) { this.art = art this.danmuku = danmuku this.utils = art.constructor.utils @@ -16,32 +29,32 @@ export default class Setting { this.template = { $controlsCenter, $mount: $controlsCenter, - $danmuku: null, - $toggle: null, - $config: null, - $configPanel: null, - $configModes: null, - $style: null, - $stylePanel: null, - $styleModes: null, - $colors: null, - $opacitySlider: null, - $opacityValue: null, - $marginSlider: null, - $marginValue: null, - $fontSizeSlider: null, - $fontSizeValue: null, - $speedSlider: null, - $speedValue: null, - $input: null, - $send: null, + $danmuku: null!, + $toggle: null!, + $config: null!, + $configPanel: null!, + $configModes: null!, + $style: null!, + $stylePanel: null!, + $styleModes: null!, + $colors: null!, + $opacitySlider: null!, + $opacityValue: null!, + $marginSlider: null!, + $marginValue: null!, + $fontSizeSlider: null!, + $fontSizeValue: null!, + $speedSlider: null!, + $speedValue: null!, + $input: null!, + $send: null!, } this.slider = { - opacity: null, - margin: null, - fontSize: null, - speed: null, + opacity: null!, + margin: null!, + fontSize: null!, + speed: null!, } this.emitting = false @@ -56,7 +69,7 @@ export default class Setting { art.on('destroy', this.destroy) if (!this.lifecycle.active) return - this.lifecycle.write($controlsCenter.style, 'display', 'flex', value => setStyle($controlsCenter, 'display', value)) + this.lifecycle.write($controlsCenter.style, 'display', 'flex', value => setStyle($controlsCenter, 'display', value as string)) this.createTemplate() if (!this.lifecycle.active) @@ -103,7 +116,7 @@ export default class Setting { return settingIcons() } - get option() { + get option(): NormalizedOption { return this.danmuku.option } @@ -115,7 +128,7 @@ export default class Setting { return settingTemplate.call(this) } - get OPACITY() { + get OPACITY(): Required { return { min: 0, max: 100, @@ -124,7 +137,7 @@ export default class Setting { } } - get FONT_SIZE() { + get FONT_SIZE(): Required { return { min: 12, max: 120, @@ -133,7 +146,7 @@ export default class Setting { } } - get MARGIN() { + get MARGIN(): Required> { return { min: 0, max: 3, @@ -159,7 +172,7 @@ export default class Setting { } } - get SPEED() { + get SPEED(): Required { return { min: 0, max: 4, @@ -212,13 +225,13 @@ export default class Setting { ] } - query(selector) { + query(selector: string): T { const { query } = this.utils const { $danmuku } = this.template - return query(selector, $danmuku) + return query(selector, $danmuku) as T } - append(el, target) { + append(el: HTMLElement, target: HTMLElement) { if (!this.lifecycle.active) return const { append } = this.utils @@ -228,7 +241,7 @@ export default class Setting { append(el, target) } - setData(key, value) { + setData(key: string, value: string | number | boolean) { if (!this.lifecycle.active) return const { $player } = this.art.template @@ -265,7 +278,7 @@ export default class Setting { this.template.$fontSizeValue = this.query('.apd-config-fontSize .apd-value') this.template.$speedSlider = this.query('.apd-config-speed .apd-slider') this.template.$speedValue = this.query('.apd-config-speed .apd-value') - this.template.$input = this.query('.apd-input') + this.template.$input = this.query('.apd-input') this.template.$send = this.query('.apd-send') const { $toggle } = this.template @@ -290,7 +303,7 @@ export default class Setting { }) this.lifecycle.proxy($configModes, 'click', (event) => { - const $mode = event.target.closest('.apd-mode') + const $mode = (event.target as HTMLElement).closest('.apd-mode') if (!$mode) return const mode = Number($mode.dataset.mode) @@ -307,14 +320,14 @@ export default class Setting { this.reset() }) - this.lifecycle.proxy($antiOverlap, 'click', () => { + this.lifecycle.proxy($antiOverlap!, 'click', () => { this.danmuku.config({ antiOverlap: !this.option.antiOverlap, }) this.reset() }) - this.lifecycle.proxy($syncVideo, 'click', () => { + this.lifecycle.proxy($syncVideo!, 'click', () => { this.danmuku.config({ synchronousPlayback: !this.option.synchronousPlayback, }) @@ -322,7 +335,7 @@ export default class Setting { }) this.lifecycle.proxy($styleModes, 'click', (event) => { - const $mode = event.target.closest('.apd-mode') + const $mode = (event.target as HTMLElement).closest('.apd-mode') if (!$mode) return const mode = Number($mode.dataset.mode) @@ -333,7 +346,7 @@ export default class Setting { }) this.lifecycle.proxy($colors, 'click', (event) => { - const $color = event.target.closest('.apd-color') + const $color = (event.target as HTMLElement).closest('.apd-color') if (!$color) return this.danmuku.config({ @@ -373,7 +386,7 @@ export default class Setting { container: this.template.$marginSlider, findIndex: () => { return this.MARGIN.steps.findIndex( - item => item.value[0] === this.option.margin[0] && item.value[1] === this.option.margin[1], + item => item.value![0] === this.option.margin[0] && item.value![1] === this.option.margin[1], ) }, onChange: (index) => { @@ -381,7 +394,7 @@ export default class Setting { if (!margin) return const { $marginValue } = this.template - $marginValue.textContent = margin.name + $marginValue.textContent = margin.name as string this.danmuku.config({ margin: margin.value, }) @@ -416,7 +429,7 @@ export default class Setting { if (!speed) return const { $speedValue } = this.template - $speedValue.textContent = speed.name + $speedValue.textContent = speed.name as string this.danmuku.config({ speed: speed.value, }) @@ -424,11 +437,11 @@ export default class Setting { }) } - createSlider(option) { + createSlider(option: SliderConfig) { return createSlider.call(this, option) } - onFullscreen(state) { + onFullscreen(state: boolean) { if (!this.lifecycle.active) return const { $danmuku, $controlsCenter, $mount } = this.template @@ -445,7 +458,7 @@ export default class Setting { } } - onMouseEnter({ $control, $panel }) { + onMouseEnter({ $control, $panel }: { $control: HTMLElement, $panel: HTMLElement }) { if (!this.lifecycle.active) return const { $player } = this.art.template @@ -522,7 +535,7 @@ export default class Setting { this.setData('danmukuTheme', this.option.theme) this.setData('danmukuEmitter', this.option.emitter) - const colors = $colors.children + const colors = $colors.children as HTMLCollectionOf const $color = Array.from(colors).find(item => item.dataset.color === this.option.color.toUpperCase()) $color && inverseClass($color, 'apd-active') @@ -531,14 +544,14 @@ export default class Setting { this.resize() } - mount(target) { + mount(target: NormalizedOption['mount']) { if (!this.lifecycle.active) return const { errorHandle } = this.utils - const $el = typeof target === 'string' ? document.querySelector(target) : target - errorHandle($el, `Can not find the mount point: ${target}`) - this.append($el, this.template.$danmuku) - this.template.$mount = $el + const $el = typeof target === 'string' ? document.querySelector(target) : target + errorHandle($el as unknown as boolean, `Can not find the mount point: ${target}`) + this.append($el!, this.template.$danmuku) + this.template.$mount = $el! this.reset() } @@ -546,7 +559,7 @@ export default class Setting { if (this.lifecycle.closed) return this.lifecycle.close() - clearTimeout(this.timer) + clearTimeout(this.timer as number | undefined) this.timer = null this.emitting = false this.isLock = false diff --git a/packages/artplayer-plugin-danmuku/src/types.ts b/packages/artplayer-plugin-danmuku/src/types.ts new file mode 100644 index 000000000..49555b9ff --- /dev/null +++ b/packages/artplayer-plugin-danmuku/src/types.ts @@ -0,0 +1,19 @@ +import type Artplayer from 'artplayer' + +// Public runtime data has one declaration source; host and Worker adapters stay internal. +export type { + Danmu as DanmuInput, + Item as DanmuItem, + Input as DanmukuInput, + RuntimeOption as DanmukuOption, + State as DanmuState, + Heatmap as HeatmapOptions, + Point as HeatmapPoint, + NormalizedDanmu, + NormalizedOption, + Slider as SliderOption, + SliderStep, +} from '../types/runtime-shared' + +export type DanmukuArt = Artplayer & { constructor: typeof Artplayer, isRotate?: boolean } +export type DanmuStyle = Partial diff --git a/packages/artplayer-plugin-danmuku/src/worker-assets.d.ts b/packages/artplayer-plugin-danmuku/src/worker-assets.d.ts new file mode 100644 index 000000000..2b7ab648f --- /dev/null +++ b/packages/artplayer-plugin-danmuku/src/worker-assets.d.ts @@ -0,0 +1,4 @@ +declare module '*?worker&inline' { + const WorkerConstructor: new (options?: WorkerOptions) => Worker + export default WorkerConstructor +} diff --git a/packages/artplayer-plugin-danmuku/src/worker-client.js b/packages/artplayer-plugin-danmuku/src/worker-client.ts similarity index 70% rename from packages/artplayer-plugin-danmuku/src/worker-client.js rename to packages/artplayer-plugin-danmuku/src/worker-client.ts index 317274240..01cccd254 100644 --- a/packages/artplayer-plugin-danmuku/src/worker-client.js +++ b/packages/artplayer-plugin-danmuku/src/worker-client.ts @@ -1,14 +1,22 @@ +import type { PlacementReply, PlacementRequest } from './worker-types' + let nextRequest = 0 export default class WorkerClient { - constructor(createWorker, onFailure) { + declare pending: Map void, reject: (error: unknown) => void }> + declare closed: boolean + declare failed: boolean + declare onFailure: (error: unknown) => void + declare worker: Worker + + constructor(createWorker: () => Worker, onFailure: (error: unknown) => void) { this.pending = new Map() this.closed = false this.failed = false this.onFailure = onFailure this.worker = createWorker() try { - this.worker.onmessage = (event) => { + this.worker.onmessage = (event: MessageEvent) => { const request = this.pending.get(event.data?.id) if (!request) return @@ -19,7 +27,7 @@ export default class WorkerClient { event.preventDefault?.() this.fail(event.error || event) } - this.worker.onmessageerror = event => this.fail(event.error || event) + this.worker.onmessageerror = event => this.fail((event as MessageEvent & { error?: unknown }).error || event) } catch (error) { try { @@ -30,7 +38,7 @@ export default class WorkerClient { } } - request(message) { + request(message: PlacementRequest): Promise { message.id = ++nextRequest const { id } = message if (this.closed || this.failed) @@ -46,7 +54,7 @@ export default class WorkerClient { }) } - fail(error) { + fail(error: unknown) { if (this.closed || this.failed) return this.failed = true @@ -70,8 +78,8 @@ export default class WorkerClient { this.closed = true this.cancel() let failed = false - let error - const attempt = (callback) => { + let error: unknown + const attempt = (callback: () => void) => { try { callback() } @@ -81,7 +89,7 @@ export default class WorkerClient { failed = true } } - for (const name of ['onmessage', 'onerror', 'onmessageerror']) { + for (const name of ['onmessage', 'onerror', 'onmessageerror'] as const) { attempt(() => this.worker[name] = null) } attempt(() => this.worker.terminate()) diff --git a/packages/artplayer-plugin-danmuku/src/worker-types.ts b/packages/artplayer-plugin-danmuku/src/worker-types.ts new file mode 100644 index 000000000..0d7fd51e8 --- /dev/null +++ b/packages/artplayer-plugin-danmuku/src/worker-types.ts @@ -0,0 +1,32 @@ +export interface VisibleDanmu { + mode: number + top: number + left: number + right: number + height: number + width: number + speed: number + distance: number + time: number +} + +export interface PlacementRequest { + id?: number + type: 'getDanmuTop' + target: { mode: number, height: number, speed: number } + visibles: VisibleDanmu[] + clientWidth: number + clientHeight: number + marginBottom: number + marginTop: number + antiOverlap: boolean +} + +export interface PlacementMessage extends PlacementRequest { id: number } +export interface PlacementReply { id: number, result: number | undefined } + +export interface PlacementBoundary extends Omit { + type: 'top' | 'bottom' +} + +export type PlacementRow = VisibleDanmu | PlacementBoundary diff --git a/packages/artplayer-plugin-danmuku/src/worker.js b/packages/artplayer-plugin-danmuku/src/worker.ts similarity index 81% rename from packages/artplayer-plugin-danmuku/src/worker.js rename to packages/artplayer-plugin-danmuku/src/worker.ts index 0d889ea6e..ba9b3ece8 100644 --- a/packages/artplayer-plugin-danmuku/src/worker.js +++ b/packages/artplayer-plugin-danmuku/src/worker.ts @@ -1,9 +1,11 @@ -function getDanmuTop({ target, visibles, clientWidth, clientHeight, marginBottom, marginTop, antiOverlap }) { +import type { PlacementMessage, PlacementReply, PlacementRequest, PlacementRow, VisibleDanmu } from './worker-types' + +function getDanmuTop({ target, visibles, clientWidth, clientHeight, marginBottom, marginTop, antiOverlap }: PlacementRequest): number | undefined { // 弹幕最大高度 const maxTop = clientHeight - marginBottom // 过滤同模式的弹幕,即每种模式各不影响 - const danmus = visibles + const danmus: PlacementRow[] = visibles .filter(item => item.mode === target.mode && item.top <= maxTop) .sort((prev, next) => prev.top - next.top) @@ -44,8 +46,8 @@ function getDanmuTop({ target, visibles, clientWidth, clientHeight, marginBottom if (target.mode === 2) { // 倒序查找 for (let index = danmus.length - 2; index >= 0; index -= 1) { - const item = danmus[index] - const prev = danmus[index + 1] + const item = danmus[index]! + const prev = danmus[index + 1]! const itemBottom = item.top + item.height const diff = prev.top - itemBottom if (diff >= target.height) { @@ -56,8 +58,8 @@ function getDanmuTop({ target, visibles, clientWidth, clientHeight, marginBottom else { // 顺序查找 for (let index = 1; index < danmus.length; index += 1) { - const item = danmus[index] - const prev = danmus[index - 1] + const item = danmus[index]! + const prev = danmus[index - 1]! const prevBottom = prev.top + prev.height const diff = item.top - prevBottom if (diff >= target.height) { @@ -66,12 +68,13 @@ function getDanmuTop({ target, visibles, clientWidth, clientHeight, marginBottom } } - const topMap = [] + const topMap: VisibleDanmu[][] = [] for (let index = 1; index < danmus.length - 1; index += 1) { - const item = danmus[index] + // The loop excludes the two virtual boundary rows inserted above. + const item = danmus[index] as VisibleDanmu if (topMap.length) { - const last = topMap[topMap.length - 1] - if (last[0].top === item.top) { + const last = topMap[topMap.length - 1]! + if (last[0]!.top === item.top) { last.push(item) } else { @@ -131,11 +134,11 @@ function getDanmuTop({ target, visibles, clientWidth, clientHeight, marginBottom break } - return topMap[0][0].top + return topMap[0]![0]!.top } } -onmessage = (event) => { +onmessage = (event: MessageEvent) => { const { data } = event if (!data.id || !data.type) return @@ -147,5 +150,5 @@ onmessage = (event) => { globalThis.postMessage({ result, id: data.id, - }) + } satisfies PlacementReply) } diff --git a/packages/artplayer-plugin-danmuku/tsconfig.json b/packages/artplayer-plugin-danmuku/tsconfig.json new file mode 100644 index 000000000..c90957472 --- /dev/null +++ b/packages/artplayer-plugin-danmuku/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "allowJs": false, + "lib": ["ES2020", "ES2021.String", "DOM", "DOM.Iterable"] + }, + "include": ["src/**/*.ts", "../../types/assets.d.ts", "../../types/option-validator.d.ts"] +} diff --git a/packages/artplayer-plugin-danmuku/types/runtime-shared.d.ts b/packages/artplayer-plugin-danmuku/types/runtime-shared.d.ts new file mode 100644 index 000000000..2c40b9001 --- /dev/null +++ b/packages/artplayer-plugin-danmuku/types/runtime-shared.d.ts @@ -0,0 +1,171 @@ +import type Artplayer from 'artplayer' + +export type Mode = 0 | 1 | 2 +export type State = 'wait' | 'ready' | 'emit' | 'stop' +export type Margin = [number | string, number | string] +/** Mutable tuples: historical custom heatmap rendering adjusts the second coordinate. */ +export type Point = [number, number] + +export interface Danmu { + text: string + id?: string + mode?: number + color?: string + time?: number + border?: boolean + style?: Partial + [extension: string]: unknown +} + +/** Input fields have been filled before filter runs; queue fields do not exist yet. */ +export interface NormalizedDanmu extends Danmu { + mode: number + color: string + time: number + style: Partial +} + +/** The actual mutable queue entry supplied to beforeVisible and visible/loaded events. */ +export interface Item extends NormalizedDanmu { + $state: State + $index: number + $ref: HTMLDivElement | null + $restTime: number + $lastStartTime: number +} + +/** Functions run without an option receiver. Arrays are also valid synchronous results. */ +export type Input = Danmu[] | string | Promise | (() => Danmu[] | Promise) +export interface SliderStep { name?: string, value?: Value, hide?: boolean, show?: boolean } +export interface Slider { min?: number, max?: number, steps?: SliderStep[] } +export interface Heatmap { + xMin?: number + xMax?: number + yMin?: number + yMax?: number + scale?: number + opacity?: number + minHeight?: number + sampling?: number + smoothing?: number + flattening?: number +} + +export interface NormalizedOption { + danmuku: Input + speed: number + margin: Margin + opacity: number + color: string + /** Numeric inputs are clamped; values other than 0, 1 and 2 are not emitted. */ + mode: number + modes: number[] + fontSize: number | string + antiOverlap: boolean + synchronousPlayback: boolean + mount: HTMLDivElement | string | undefined + heatmap: boolean | Heatmap + width: number + /** Stored configuration; send the points event to draw custom points. */ + points: Point[] + /** A truthy result accepts the row. */ + filter: (this: NormalizedOption, danmu: NormalizedDanmu) => unknown + /** Only a result equal to true sends the row; callbacks may be asynchronous. */ + beforeEmit: (this: NormalizedOption, danmu: Danmu) => unknown + /** A truthy result displays the queue entry; callbacks may be asynchronous. */ + beforeVisible: (this: NormalizedOption, danmu: Item) => unknown + visible: boolean + emitter: boolean + maxLength: number + lockTime: number + theme: string + OPACITY: Slider + FONT_SIZE: Slider + MARGIN: Slider + SPEED: Slider + COLOR: string[] +} + +/** The argument object is required; every option inside it is optional. */ +export type RuntimeOption = Partial + +/** The owner returned by commands is different from the registered plugin facade. */ +export interface Owner { + art: Artplayer + option: NormalizedOption + isHide: boolean + isStop: boolean + queue: Item[] + states: Record + readonly readys: Item[] + readonly speed: number + readonly fontSize: number + readonly marginTop: number + readonly marginBottom: number | string + readonly isRotate: boolean | undefined + emit: (danmu: Danmu) => Promise + /** No argument replaces the configured source; a provided argument appends rows. */ + load: (danmuku?: Input) => Promise + config: (option: RuntimeOption) => Owner + hide: () => Owner + show: () => Owner + reset: () => Owner + start: () => Owner + stop: () => Owner + continue: () => Owner + suspend: () => Owner + update: () => Owner + resize: () => void + seek: () => void + destroy: () => void +} + +export interface RuntimeResult { + name: 'artplayerPluginDanmuku' + emit: (danmu: Danmu) => Promise + load: (danmuku?: Input) => Promise + config: (option: RuntimeOption) => Owner + hide: () => Owner + show: () => Owner + reset: () => Owner + /** The live setting requires a valid mount; omitting it does not select a default. */ + mount: (target: HTMLDivElement | string) => void + readonly option: NormalizedOption + readonly isHide: boolean + readonly isStop: boolean +} + +export interface Icons { + $on: string + $off: string + $config: string + $style: string + $mode_0_off: string + $mode_0_on: string + $mode_1_off: string + $mode_1_on: string + $mode_2_off: string + $mode_2_on: string + $check_on: string + $check_off: string +} + +export interface RuntimeFactory { + (option: RuntimeOption): (art: Artplayer) => RuntimeResult + icons: Icons +} + +/** Explicit payload map; importing /runtime does not augment historical Artplayer events. */ +export interface EventMap { + 'artplayerPluginDanmuku:points': [points: Point[]] + 'artplayerPluginDanmuku:loaded': [queue: Item[]] + 'artplayerPluginDanmuku:visible': [item: Item] + 'artplayerPluginDanmuku:config': [option: NormalizedOption] + 'artplayerPluginDanmuku:error': [error: unknown] + 'artplayerPluginDanmuku:show': [] + 'artplayerPluginDanmuku:hide': [] + 'artplayerPluginDanmuku:start': [] + 'artplayerPluginDanmuku:stop': [] + 'artplayerPluginDanmuku:reset': [] + 'artplayerPluginDanmuku:destroy': [] +} diff --git a/packages/artplayer-plugin-danmuku/types/runtime.d.cts b/packages/artplayer-plugin-danmuku/types/runtime.d.cts new file mode 100644 index 000000000..51fc385f9 --- /dev/null +++ b/packages/artplayer-plugin-danmuku/types/runtime.d.cts @@ -0,0 +1,25 @@ +import type { RuntimeFactory } from './runtime-shared.js' + +declare const artplayerPluginDanmuku: RuntimeFactory +// eslint-disable-next-line ts/no-redeclare -- Ambient merge exposes types on the CommonJS factory. +declare namespace artplayerPluginDanmuku { + type Danmu = import('./runtime-shared.js').Danmu + type EventMap = import('./runtime-shared.js').EventMap + type Heatmap = import('./runtime-shared.js').Heatmap + type Icons = import('./runtime-shared.js').Icons + type Input = import('./runtime-shared.js').Input + type Item = import('./runtime-shared.js').Item + type Margin = import('./runtime-shared.js').Margin + type Mode = import('./runtime-shared.js').Mode + type NormalizedDanmu = import('./runtime-shared.js').NormalizedDanmu + type NormalizedOption = import('./runtime-shared.js').NormalizedOption + type Owner = import('./runtime-shared.js').Owner + type Point = import('./runtime-shared.js').Point + type RuntimeFactory = import('./runtime-shared.js').RuntimeFactory + type RuntimeOption = import('./runtime-shared.js').RuntimeOption + type RuntimeResult = import('./runtime-shared.js').RuntimeResult + type Slider = import('./runtime-shared.js').Slider + type SliderStep = import('./runtime-shared.js').SliderStep + type State = import('./runtime-shared.js').State +} +export = artplayerPluginDanmuku diff --git a/packages/artplayer-plugin-danmuku/types/runtime.d.mts b/packages/artplayer-plugin-danmuku/types/runtime.d.mts new file mode 100644 index 000000000..52840f429 --- /dev/null +++ b/packages/artplayer-plugin-danmuku/types/runtime.d.mts @@ -0,0 +1,5 @@ +import type { RuntimeFactory } from './runtime-shared.js' + +declare const artplayerPluginDanmuku: RuntimeFactory +export default artplayerPluginDanmuku +export type { Danmu, EventMap, Heatmap, Icons, Input, Item, Margin, Mode, NormalizedDanmu, NormalizedOption, Owner, Point, RuntimeFactory, RuntimeOption, RuntimeResult, Slider, SliderStep, State } from './runtime-shared.js' diff --git a/packages/artplayer-plugin-danmuku/types/runtime.d.ts b/packages/artplayer-plugin-danmuku/types/runtime.d.ts new file mode 100644 index 000000000..51fc385f9 --- /dev/null +++ b/packages/artplayer-plugin-danmuku/types/runtime.d.ts @@ -0,0 +1,25 @@ +import type { RuntimeFactory } from './runtime-shared.js' + +declare const artplayerPluginDanmuku: RuntimeFactory +// eslint-disable-next-line ts/no-redeclare -- Ambient merge exposes types on the CommonJS factory. +declare namespace artplayerPluginDanmuku { + type Danmu = import('./runtime-shared.js').Danmu + type EventMap = import('./runtime-shared.js').EventMap + type Heatmap = import('./runtime-shared.js').Heatmap + type Icons = import('./runtime-shared.js').Icons + type Input = import('./runtime-shared.js').Input + type Item = import('./runtime-shared.js').Item + type Margin = import('./runtime-shared.js').Margin + type Mode = import('./runtime-shared.js').Mode + type NormalizedDanmu = import('./runtime-shared.js').NormalizedDanmu + type NormalizedOption = import('./runtime-shared.js').NormalizedOption + type Owner = import('./runtime-shared.js').Owner + type Point = import('./runtime-shared.js').Point + type RuntimeFactory = import('./runtime-shared.js').RuntimeFactory + type RuntimeOption = import('./runtime-shared.js').RuntimeOption + type RuntimeResult = import('./runtime-shared.js').RuntimeResult + type Slider = import('./runtime-shared.js').Slider + type SliderStep = import('./runtime-shared.js').SliderStep + type State = import('./runtime-shared.js').State +} +export = artplayerPluginDanmuku diff --git a/refactor/baselines/danmuku-types-validation.json b/refactor/baselines/danmuku-types-validation.json new file mode 100644 index 000000000..f31aaa5f7 --- /dev/null +++ b/refactor/baselines/danmuku-types-validation.json @@ -0,0 +1,12147 @@ +{ + "schemaVersion": 1, + "task": "PKG-DANMUKU-06", + "status": "done", + "capturedAt": "2026-09-13T14:04:49.770Z", + "baselineCommit": "abc8008da79bdd0c5cd38ee884b4720a271f9bd5", + "scope": "20 runtime modules migrated to strict TypeScript, 5 internal type-only files, optional accurate runtime declarations; latest npm root unchanged. This does not complete stability, combination, distribution or release reviews.", + "units": [ + { + "file": "refactor/.cache/danmuku06-final-source-suite.log", + "sha256": "c2f7d59f14614a8df74a9bcd7165a806d4c6dc86b785b0b24275a812340a7bc4", + "tests": 250 + }, + { + "file": "refactor/.cache/danmuku06-corrected-main-unit.log", + "sha256": "7686358bf0696179f1efe7bee7aca3c3d44c43f47e3386af84c5d53a0adcfcdb", + "tests": 164 + }, + { + "file": "refactor/.cache/danmuku06-corrected-legacy-unit.log", + "sha256": "78e5f8f283c700ce18fb4b3c6fbd5d4948f17e8e68d8b9d0b2d592e1d1127a08", + "tests": 164 + } + ], + "unitScope": "Artifact runs include shared direct internal and frozen historical tests; selected integration tests and Worker geometry execute actual artifact bytes. No claim of 578 independent artifact cases.", + "types": { + "tests": { + "file": "refactor/.cache/danmuku06-final-types.log", + "sha256": "71fe768b451fd7f2cc70dcf362c2586f7ecf06894a0fb4112433af98af83cb70", + "tests": 4 + }, + "installedReport": { + "file": "refactor/.cache/danmuku-package-types-4Itmg2/report.json", + "sha256": "4918cf99daae82f6f310239b49089b4b48f2048abe300b0f2ce5b8394e8f15e9" + }, + "installed": { + "suite": "danmuku-isolated-package-types", + "introducedBy": "PKG-DANMUKU-06", + "scope": "Actual npm 5.3.0 and packed candidate outside workspace; offline and frozen reinstall, exact installed bytes, unchanged root declaration, full factory replacement and extraction. Old synchronous emit and object points remain root typing only. Runtime types are checked separately. NodeNext root namespace failures are compared by exact codes and line positions. This is not real playback or complete publication acceptance.", + "fixtures": { + "refactor/fixtures/consumers/danmuku-published.ts": "70e8a7583ebb4580af35bbb98d5224a0fdeccc5812c9f83455ddc0dbdf731c6e", + "test/types/danmuku-root.ts": "a5a4b55bed906d56c24e07ee4a478bb9edf6ad3c5c3ee0755996c8facd96a8d3", + "test/types/danmuku-runtime.ts": "5fde19071bf34d4ee0d431f1f707bc283947ce09988097fa0670af70bd70d5d5" + }, + "packages": [ + { + "name": "artplayer", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\danmuku-package-types-4Itmg2\\artplayer.tgz", + "sha256": "0c0ea52d41575261b836aefac68280733b119c75834ecad6d4bc78edb073f4c1", + "files": { + "package/ARCHITECTURE.md": "bee98fc9d93a3911d58e98ef15f2dbe07e12f2274c444d989094248a22079e46", + "package/README.md": "1e185e65f67fc2081b4182bbc6c4558ea05b6e3fa3acc5dd0c3ac7df1d542b09", + "package/THIRD_PARTY_NOTICES": "b77b5b46b79fa0cda850b0781f78c3b1112c4ed9672fa291956fe8eff53098e7", + "package/dist/artplayer.js": "6dc8ca6d6eb34aa8a056cbc31328efd634530b3c5c60f0014cb7ce719fabc335", + "package/dist/artplayer.legacy.js": "80f74a8a39df4baf64cc00c65d21cd57c011bfcdead0da38eea077fbc67c5c04", + "package/dist/artplayer.mjs": "432e2b0ad92009a09743e020bf7c928c8b8f18a52496622b16259ec9ab20059f", + "package/dist/i18n/ar.js": "bd0bef65f4f7dc3f8ed562904cc6fa9071ff4537fb64c1e0fc3d554141bcd3cb", + "package/dist/i18n/ar.mjs": "59310b7bbeea83bbb7d73aadc5ca82a6ec269c3530d19aa741c94d5c64fc5da8", + "package/dist/i18n/cs.js": "6e2c93daba24c2e48dfa206bcf4f2f2a8a02713b856e404d2e4e4ecd8309ca2f", + "package/dist/i18n/cs.mjs": "c73fba18797d62d7b2581e67fe7340c35fe84510b2250ff1ecaad253d9cabbac", + "package/dist/i18n/es.js": "f4133ac6ea06b69a474b33c4a1809a4066e2376bb9464a3f3aa8330f01f1944a", + "package/dist/i18n/es.mjs": "1390bca3def2be115e811de337df0a0c3f63998e51359f283023246245c552a7", + "package/dist/i18n/fa.js": "8d3ef2e0f0cc0ea480c2167b6fcb87cdf444e90759c3519e8daa139fec576da4", + "package/dist/i18n/fa.mjs": "ab441e8a2d79043b29ceca87c783bf06874bb9d3a149c1604bc167eb481be2a9", + "package/dist/i18n/fr.js": "89dc4194a6c0a962b9ce756b52c90401c1acf3b159f265f3bc0d7d6d7d987f03", + "package/dist/i18n/fr.mjs": "99491878430fb7b6f2b7bf93da0588a831704b412e01116f23ba1d9d45863816", + "package/dist/i18n/id.js": "4c6dc86db741bcc2857df55a9d037e82e91d375fa6fd22e0562809ff621b92db", + "package/dist/i18n/id.mjs": "383e5803a6b6686c6de4e867770241de75b6f517dcca2bdedc6f2004bf6e1fb3", + "package/dist/i18n/pl.js": "3a38828b1c3d719c0aca9906cf6a46c23555a8411f8d57645f0e0ce8147dc664", + "package/dist/i18n/pl.mjs": "079ed96732979035297af387c64a7b99519b5a43107fb86735a654ea4fe8e9b5", + "package/dist/i18n/ru.js": "62fac9dddb302eb16382e6e9301fc4b2c66f5f69d4a358a26b67026a5fa4e787", + "package/dist/i18n/ru.mjs": "a893aeaee1d79409db72e324fbff200e3864f33ed168f04d91d25f7cf2efa765", + "package/dist/i18n/tr.js": "1492c36b069a7d73db4f3913bd18085faff7afd8b3c158138fe264e31600e38c", + "package/dist/i18n/tr.mjs": "a90478cac078836b76c2a98261907610fee07f3e4b28671b8e7dbf533778f918", + "package/dist/i18n/vi.js": "c7216ef7e608a79b7e2c479a07bf310d4ab79577947ac4a62d676faaced0e9cf", + "package/dist/i18n/vi.mjs": "66270fdfa0b5da396322d1769169b7df37a030ba15ac18eb30bd87b3af8074ec", + "package/dist/i18n/zh-tw.js": "11c49857d6d40a5ffd224ffa59c9026e71621b345f63c5a086326db80a22371a", + "package/dist/i18n/zh-tw.mjs": "a38e630cb96de96c15d824686aec750e25cf6c8ada01285a292442a81566314f", + "package/package.json": "82e92699e7c29847f32ac6767ece0401290e7d7a12854722bdbcfd09b90b7954", + "package/types/COMPATIBILITY.md": "a7da9e102a116104b513df4f7451247b7759bc502d3375f30bd5b14a541826e0", + "package/types/README.md": "1bd9868e773610e6312fd48a1c5aa20772d29df0af40c3db66e75b7ab4a1aee0", + "package/types/artplayer.d.cts": "3685065e4263abcfd9d953176e461d5b3eb990b0363b54689a7f55deba770fef", + "package/types/artplayer.d.mts": "b664fec51ee5c5381f2ceab25d99d52aee56feab04a2ee7b4ea5b2145ea5fa0e", + "package/types/artplayer.d.ts": "5d0162adf7ac6d0f2bb9b5cfe361f28657c4d9c540cadbf36eba3cc954ce50f8", + "package/types/component.d.ts": "ae5bc1ddeaa1ee1ebbc62573e32423993727079205c64751a5a314170e588a32", + "package/types/config.d.ts": "36f9a84cd436f17701a0d11d3417fc1fe0414e0c52c80083b94a85dfc329e37e", + "package/types/cssVar.d.ts": "93217860fff2ebf750f606d95ddd22220a58837119e63bdf2f5f78fd7488458b", + "package/types/emitter.d.ts": "7e1f9a2a8ce65feb95734f1ed52d1334e57aa388bbef2a43d8bc53a9b6f6623c", + "package/types/events.d.ts": "f83624c9268a284477e8bd793fe3562a2363b72f2a44d5c64fe8c6a6cede8830", + "package/types/i18n.d.ts": "f4f2f05bdfda73c74fef34767cb18ee8e11b743120b1c4598e9714d7846018e5", + "package/types/icons.d.ts": "326a67ba7bdcf52862c5b2a269e540140aff98d05c7b96196485a77f84fe5960", + "package/types/language.d.cts": "f7928e108cf7b7814c2d75cb0a525333e95873fca6cd6d89a740450656b9c1cd", + "package/types/language.d.mts": "6619a26d604b7cccbe2f596243d87ae37cfaae33b8b52874ec071d9787446f9e", + "package/types/legacy/language.d.ts": "c7abda4d506fd933c350e9030290bf9e64347a921d683a66115da1995ebe0a81", + "package/types/option.d.ts": "ccf2c8ddc537dc65c9682d39f044726e23bff18f476b622512d7e6a4b949b5f6", + "package/types/playback.d.ts": "58705c273df6e76a77a6575e769a9ec6c013785c25080f2551aa22415788d870", + "package/types/player.d.ts": "92b00211b70ac71336e495899df972a0519dd138111846044fe4e0f967966177", + "package/types/plugin.d.ts": "fc3594a41c51d8248713e33c486fb1f6a518ac5c7ce31be6988a6cd8d052c5f3", + "package/types/quality.d.ts": "1ac1a48b4f74ff9ba1ce60443e1463d50d1c6a2e5d87d63dc7d2a8eb7f4c2c34", + "package/types/runtime.d.cts": "7ee988dfea9a0ac71a34a656f96f7b74e78aa92547cfdfab7ff7e1139a77d4a3", + "package/types/runtime.d.mts": "dcb85f59a8b7714918ced0c1d18c2bb24e8b80b56d8c594e019dcbdb5ad88b08", + "package/types/runtime.d.ts": "9c6ccd39a1be293f17ae46dae8ea077053e12635ee79d73a5e3b3a559230fcff", + "package/types/runtime/component.d.ts": "1b4c0cee44568649e80140fe778febfcae88af0ac85515f1cbeff78ab0c6d14f", + "package/types/runtime/construction.d.ts": "7a87bedf6f7ffff0c46078aa5f1551e0077d35574e286516121f5f935e2d9ce4", + "package/types/runtime/events.d.ts": "a1e2f9d387475453eaf95c2a5ee79e228636c0bb859bda3fcc46da46574a0c31", + "package/types/runtime/media.d.ts": "8fc89401be7fe25542c169d07dd5675f10916ed20be8493e19695966cf3864a8", + "package/types/runtime/option.d.ts": "6e334e1a76fdcc40b4665c02523bd31c3994d96078fadeb40e23422aa54c1a1d", + "package/types/runtime/player.d.ts": "8ac6383502e4872e83659838844856903031a81a0adc8e8535feb78453aa042c", + "package/types/runtime/plugin.d.ts": "8528f2b17843149f42f9cc16d6fd70baa238c9dc2c329b67d2c38096799c69bd", + "package/types/runtime/services.d.ts": "604c9f8e4b6c0da818f96576bf9f4b0e96085a9003927b2f7ba30027b1ba237e", + "package/types/runtime/setting.d.ts": "349c1fcb205ad312916bd3ee55e6bf241ea8a569fb2ab6c80822e69fc518f7d7", + "package/types/runtime/subtitle.d.ts": "269ff84678d327b3a9dc2f5bc6cc0d731d1e081b85422e474621d5d792efb153", + "package/types/runtime/template.d.ts": "7a62f968a38b4bf9109a4439557c02074eb1d62ba1f6e21a8eb194e91f0badfe", + "package/types/runtime/utils.d.ts": "6bf731705d86853e066dbaf3b8d4003b6548423fe615b7412b722bd0d3a6e48b", + "package/types/setting.d.ts": "08305a75dff96f06490430e099338b463df471b550c1d0dac3abc8995b39eb09", + "package/types/subtitle.d.ts": "b18314e2d64efdb6045830b6e12f615ae2ed585416130e562329ad3ea337f6ce", + "package/types/template.d.ts": "4792bc0c2cbcb7966e4375118bd34b326166e0831862e94ace4cf6b1bbe6a8c8", + "package/types/utils.d.ts": "fca0d3d814df94a25f9eb067c86fa0cf272e2791bac793b91229d57680797712" + } + }, + { + "name": "artplayer-plugin-danmuku", + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\danmuku-package-types-4Itmg2\\artplayer-plugin-danmuku.tgz", + "sha256": "c7307bea1b41eec1b39dbeaa95872173f10b9c3c995aaa133198851345973ffa", + "files": { + "package/ARCHITECTURE.md": "bb52ee9be8d44c3f69f5e1d322a66b07c09fcf0f6882bd0730d0452bc1795dd0", + "package/README.md": "d393b6862951242f258a579ea539e4a408599decf49925265c43dbee3bae3f37", + "package/dist/artplayer-plugin-danmuku.js": "17899cc03cc39c3a440e8d2db8b5ffbae05aff81f6c8426071df2a5eb1b7ce42", + "package/dist/artplayer-plugin-danmuku.legacy.js": "83da521eda70a111ac0d655028b7bdfb0f4d4167bcda2890adab4ad679b48d99", + "package/dist/artplayer-plugin-danmuku.mjs": "adfc131c6d4c524ce91098ce9fc16feb9e87142f37f17a3ff772e68734b0fd38", + "package/package.json": "563f92ac14666c7600b0af623bd77d7ff71f1faebb851a1d780207ac8952f0a5", + "package/types/artplayer-plugin-danmuku.d.ts": "3162fc3db91bff06f0709fa0e6ca78de05053e25436ca215846a6267d4680e53", + "package/types/runtime-shared.d.ts": "52556c8228c0947fb74a4335a204521378099a6a855d83e48e4f5aec1e34944d", + "package/types/runtime.d.cts": "9b5073ca622762c367e79161968f6c31e1f3f73b412d3e049a0d845cdac2c832", + "package/types/runtime.d.mts": "564a6ed25fbb363603b825e84ae0298cfcc69c3d007fd56c616dbd20cec4a0a9", + "package/types/runtime.d.ts": "9b5073ca622762c367e79161968f6c31e1f3f73b412d3e049a0d845cdac2c832" + } + } + ], + "published": { + "archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\releases\\artplayer-plugin-danmuku-5.3.0.tgz", + "sha256": "1abb76460790d85a26590b8b911eb1301fd720885f96c73432060416a38dcb0e" + }, + "matrix": [ + { + "plugin": "published-5.3.0", + "compiler": "5.9.3", + "mode": "node10-commonjs", + "direct": [], + "namespace": [], + "root": null, + "runtime": null, + "commonjs": [] + }, + { + "plugin": "published-5.3.0", + "compiler": "4.3.5", + "mode": "node10-commonjs", + "direct": [], + "namespace": [], + "root": null, + "runtime": null, + "commonjs": [] + }, + { + "plugin": "published-5.3.0", + "compiler": "5.9.3", + "mode": "nodenext-cjs", + "direct": [], + "namespace": [], + "root": null, + "runtime": null, + "commonjs": [] + }, + { + "plugin": "published-5.3.0", + "compiler": "5.9.3", + "mode": "nodenext-esm", + "direct": [ + { + "code": 2344, + "line": 8, + "message": "Type 'false' does not satisfy the constraint 'true'." + }, + { + "code": 2344, + "line": 8, + "message": "Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' does not satisfy the constraint '(...args: any) => any'.\n Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' provides no match for the signature '(...args: any): any'." + }, + { + "code": 2344, + "line": 9, + "message": "Type 'false' does not satisfy the constraint 'true'." + }, + { + "code": 2344, + "line": 9, + "message": "Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' does not satisfy the constraint '(...args: any) => any'.\n Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' provides no match for the signature '(...args: any): any'." + }, + { + "code": 2322, + "line": 18, + "message": "Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' is not assignable to type 'Factory'.\n Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' provides no match for the signature '(option: Option): (art: Artplayer) => Result'." + }, + { + "code": 2322, + "line": 19, + "message": "Type 'Factory' is not assignable to type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")'." + }, + { + "code": 2349, + "line": 21, + "message": "This expression is not callable.\n Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' has no call signatures." + } + ], + "namespace": [], + "root": null, + "runtime": null, + "commonjs": [] + }, + { + "plugin": "published-5.3.0", + "compiler": "5.9.3", + "mode": "bundler-esm", + "direct": [], + "namespace": [], + "root": null, + "runtime": null, + "commonjs": [] + }, + { + "plugin": "candidate", + "compiler": "5.9.3", + "mode": "node10-commonjs", + "direct": [], + "namespace": [], + "root": { + "diagnostics": [], + "invalid": [ + { + "code": 2554, + "line": 14, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 15, + "message": "Argument of type '{}' is not assignable to parameter of type 'Option'.\n Property 'danmuku' is missing in type '{}' but required in type 'Option'." + }, + { + "code": 2322, + "line": 16, + "message": "Type '[number, number]' is not assignable to type '{ time: number; value: number; }'." + }, + { + "code": 2739, + "line": 17, + "message": "Type 'Result' is missing the following properties from type 'Promise': then, catch, finally, [Symbol.toStringTag]" + } + ], + "negativeStatements": 4 + }, + "runtime": { + "diagnostics": [], + "invalid": [ + { + "code": 2322, + "line": 32, + "message": "Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2339, + "line": 77, + "message": "Property 'name' does not exist on type 'Owner'." + }, + { + "code": 2339, + "line": 78, + "message": "Property 'mount' does not exist on type 'Owner'." + }, + { + "code": 2554, + "line": 81, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 82, + "message": "Argument of type 'undefined' is not assignable to parameter of type 'Partial'." + }, + { + "code": 2345, + "line": 83, + "message": "Argument of type '{ id: string; }' is not assignable to parameter of type 'Danmu'.\n Property 'text' is missing in type '{ id: string; }' but required in type 'Danmu'." + }, + { + "code": 2353, + "line": 84, + "message": "Object literal may only specify known properties, and 'time' does not exist in type '[number, number]'." + }, + { + "code": 2322, + "line": 85, + "message": "Type 'number' is not assignable to type '[string | number, string | number]'." + }, + { + "code": 2554, + "line": 86, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2540, + "line": 87, + "message": "Cannot assign to 'option' because it is a read-only property." + }, + { + "code": 2540, + "line": 88, + "message": "Cannot assign to 'isHide' because it is a read-only property." + }, + { + "code": 2540, + "line": 89, + "message": "Cannot assign to 'isStop' because it is a read-only property." + }, + { + "code": 2339, + "line": 90, + "message": "Property 'hide' does not exist on type 'Promise'." + }, + { + "code": 2339, + "line": 91, + "message": "Property 'default' does not exist on type 'RuntimeFactory'." + } + ], + "negativeStatements": 14 + }, + "commonjs": [] + }, + { + "plugin": "candidate", + "compiler": "4.3.5", + "mode": "node10-commonjs", + "direct": [], + "namespace": [], + "root": { + "diagnostics": [], + "invalid": [ + { + "code": 2554, + "line": 14, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 15, + "message": "Argument of type '{}' is not assignable to parameter of type 'Option'.\n Property 'danmuku' is missing in type '{}' but required in type 'Option'." + }, + { + "code": 2322, + "line": 16, + "message": "Type '[number, number]' is not assignable to type '{ time: number; value: number; }'." + }, + { + "code": 2739, + "line": 17, + "message": "Type 'Result' is missing the following properties from type 'Promise': then, catch, finally, [Symbol.toStringTag]" + } + ], + "negativeStatements": 4 + }, + "runtime": { + "diagnostics": [], + "invalid": [ + { + "code": 2322, + "line": 32, + "message": "Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2339, + "line": 77, + "message": "Property 'name' does not exist on type 'Owner'." + }, + { + "code": 2339, + "line": 78, + "message": "Property 'mount' does not exist on type 'Owner'." + }, + { + "code": 2554, + "line": 81, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 82, + "message": "Argument of type 'undefined' is not assignable to parameter of type 'Partial'." + }, + { + "code": 2345, + "line": 83, + "message": "Argument of type '{ id: string; }' is not assignable to parameter of type 'Danmu'.\n Property 'text' is missing in type '{ id: string; }' but required in type 'Danmu'." + }, + { + "code": 2322, + "line": 84, + "message": "Type '{ time: number; value: number; }' is not assignable to type 'Point'.\n Object literal may only specify known properties, and 'time' does not exist in type '[number, number]'." + }, + { + "code": 2322, + "line": 85, + "message": "Type 'number' is not assignable to type 'Margin | undefined'." + }, + { + "code": 2554, + "line": 86, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2540, + "line": 87, + "message": "Cannot assign to 'option' because it is a read-only property." + }, + { + "code": 2540, + "line": 88, + "message": "Cannot assign to 'isHide' because it is a read-only property." + }, + { + "code": 2540, + "line": 89, + "message": "Cannot assign to 'isStop' because it is a read-only property." + }, + { + "code": 2339, + "line": 90, + "message": "Property 'hide' does not exist on type 'Promise'." + }, + { + "code": 2339, + "line": 91, + "message": "Property 'default' does not exist on type 'RuntimeFactory'." + } + ], + "negativeStatements": 14 + }, + "commonjs": [] + }, + { + "plugin": "candidate", + "compiler": "5.9.3", + "mode": "nodenext-cjs", + "direct": [], + "namespace": [], + "root": { + "diagnostics": [], + "invalid": [ + { + "code": 2554, + "line": 14, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 15, + "message": "Argument of type '{}' is not assignable to parameter of type 'Option'.\n Property 'danmuku' is missing in type '{}' but required in type 'Option'." + }, + { + "code": 2322, + "line": 16, + "message": "Type '[number, number]' is not assignable to type '{ time: number; value: number; }'." + }, + { + "code": 2739, + "line": 17, + "message": "Type 'Result' is missing the following properties from type 'Promise': then, catch, finally, [Symbol.toStringTag]" + } + ], + "negativeStatements": 4 + }, + "runtime": { + "diagnostics": [], + "invalid": [ + { + "code": 2322, + "line": 32, + "message": "Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2339, + "line": 77, + "message": "Property 'name' does not exist on type 'Owner'." + }, + { + "code": 2339, + "line": 78, + "message": "Property 'mount' does not exist on type 'Owner'." + }, + { + "code": 2554, + "line": 81, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 82, + "message": "Argument of type 'undefined' is not assignable to parameter of type 'Partial'." + }, + { + "code": 2345, + "line": 83, + "message": "Argument of type '{ id: string; }' is not assignable to parameter of type 'Danmu'.\n Property 'text' is missing in type '{ id: string; }' but required in type 'Danmu'." + }, + { + "code": 2353, + "line": 84, + "message": "Object literal may only specify known properties, and 'time' does not exist in type '[number, number]'." + }, + { + "code": 2322, + "line": 85, + "message": "Type 'number' is not assignable to type '[string | number, string | number]'." + }, + { + "code": 2554, + "line": 86, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2540, + "line": 87, + "message": "Cannot assign to 'option' because it is a read-only property." + }, + { + "code": 2540, + "line": 88, + "message": "Cannot assign to 'isHide' because it is a read-only property." + }, + { + "code": 2540, + "line": 89, + "message": "Cannot assign to 'isStop' because it is a read-only property." + }, + { + "code": 2339, + "line": 90, + "message": "Property 'hide' does not exist on type 'Promise'." + }, + { + "code": 2339, + "line": 91, + "message": "Property 'default' does not exist on type 'RuntimeFactory'." + } + ], + "negativeStatements": 14 + }, + "commonjs": [] + }, + { + "plugin": "candidate", + "compiler": "5.9.3", + "mode": "nodenext-esm", + "direct": [ + { + "code": 2344, + "line": 8, + "message": "Type 'false' does not satisfy the constraint 'true'." + }, + { + "code": 2344, + "line": 8, + "message": "Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' does not satisfy the constraint '(...args: any) => any'.\n Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' provides no match for the signature '(...args: any): any'." + }, + { + "code": 2344, + "line": 9, + "message": "Type 'false' does not satisfy the constraint 'true'." + }, + { + "code": 2344, + "line": 9, + "message": "Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' does not satisfy the constraint '(...args: any) => any'.\n Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' provides no match for the signature '(...args: any): any'." + }, + { + "code": 2322, + "line": 18, + "message": "Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' is not assignable to type 'Factory'.\n Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' provides no match for the signature '(option: Option): (art: Artplayer) => Result'." + }, + { + "code": 2322, + "line": 19, + "message": "Type 'Factory' is not assignable to type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")'." + }, + { + "code": 2349, + "line": 21, + "message": "This expression is not callable.\n Type 'typeof import(\"/node_modules/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku\")' has no call signatures." + } + ], + "namespace": [], + "root": { + "diagnostics": [], + "invalid": [ + { + "code": 2554, + "line": 16, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 17, + "message": "Argument of type '{}' is not assignable to parameter of type 'Option'.\n Property 'danmuku' is missing in type '{}' but required in type 'Option'." + }, + { + "code": 2322, + "line": 18, + "message": "Type '[number, number]' is not assignable to type '{ time: number; value: number; }'." + }, + { + "code": 2739, + "line": 19, + "message": "Type 'Result' is missing the following properties from type 'Promise': then, catch, finally, [Symbol.toStringTag]" + } + ], + "negativeStatements": 4 + }, + "runtime": { + "diagnostics": [], + "invalid": [ + { + "code": 2322, + "line": 32, + "message": "Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2339, + "line": 77, + "message": "Property 'name' does not exist on type 'Owner'." + }, + { + "code": 2339, + "line": 78, + "message": "Property 'mount' does not exist on type 'Owner'." + }, + { + "code": 2554, + "line": 81, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 82, + "message": "Argument of type 'undefined' is not assignable to parameter of type 'Partial'." + }, + { + "code": 2345, + "line": 83, + "message": "Argument of type '{ id: string; }' is not assignable to parameter of type 'Danmu'.\n Property 'text' is missing in type '{ id: string; }' but required in type 'Danmu'." + }, + { + "code": 2353, + "line": 84, + "message": "Object literal may only specify known properties, and 'time' does not exist in type '[number, number]'." + }, + { + "code": 2322, + "line": 85, + "message": "Type 'number' is not assignable to type '[string | number, string | number]'." + }, + { + "code": 2554, + "line": 86, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2540, + "line": 87, + "message": "Cannot assign to 'option' because it is a read-only property." + }, + { + "code": 2540, + "line": 88, + "message": "Cannot assign to 'isHide' because it is a read-only property." + }, + { + "code": 2540, + "line": 89, + "message": "Cannot assign to 'isStop' because it is a read-only property." + }, + { + "code": 2339, + "line": 90, + "message": "Property 'hide' does not exist on type 'Promise'." + }, + { + "code": 2339, + "line": 91, + "message": "Property 'default' does not exist on type 'RuntimeFactory'." + } + ], + "negativeStatements": 14 + }, + "commonjs": [] + }, + { + "plugin": "candidate", + "compiler": "5.9.3", + "mode": "bundler-esm", + "direct": [], + "namespace": [], + "root": { + "diagnostics": [], + "invalid": [ + { + "code": 2554, + "line": 14, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 15, + "message": "Argument of type '{}' is not assignable to parameter of type 'Option'.\n Property 'danmuku' is missing in type '{}' but required in type 'Option'." + }, + { + "code": 2322, + "line": 16, + "message": "Type '[number, number]' is not assignable to type '{ time: number; value: number; }'." + }, + { + "code": 2739, + "line": 17, + "message": "Type 'Result' is missing the following properties from type 'Promise': then, catch, finally, [Symbol.toStringTag]" + } + ], + "negativeStatements": 4 + }, + "runtime": { + "diagnostics": [], + "invalid": [ + { + "code": 2322, + "line": 32, + "message": "Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2339, + "line": 77, + "message": "Property 'name' does not exist on type 'Owner'." + }, + { + "code": 2339, + "line": 78, + "message": "Property 'mount' does not exist on type 'Owner'." + }, + { + "code": 2554, + "line": 81, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 82, + "message": "Argument of type 'undefined' is not assignable to parameter of type 'Partial'." + }, + { + "code": 2345, + "line": 83, + "message": "Argument of type '{ id: string; }' is not assignable to parameter of type 'Danmu'.\n Property 'text' is missing in type '{ id: string; }' but required in type 'Danmu'." + }, + { + "code": 2353, + "line": 84, + "message": "Object literal may only specify known properties, and 'time' does not exist in type '[number, number]'." + }, + { + "code": 2322, + "line": 85, + "message": "Type 'number' is not assignable to type '[string | number, string | number]'." + }, + { + "code": 2554, + "line": 86, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2540, + "line": 87, + "message": "Cannot assign to 'option' because it is a read-only property." + }, + { + "code": 2540, + "line": 88, + "message": "Cannot assign to 'isHide' because it is a read-only property." + }, + { + "code": 2540, + "line": 89, + "message": "Cannot assign to 'isStop' because it is a read-only property." + }, + { + "code": 2339, + "line": 90, + "message": "Property 'hide' does not exist on type 'Promise'." + }, + { + "code": 2339, + "line": 91, + "message": "Property 'default' does not exist on type 'RuntimeFactory'." + } + ], + "negativeStatements": 14 + }, + "commonjs": [] + }, + { + "plugin": "candidate", + "compiler": "5.9.3", + "mode": "node10-commonjs-no-interop", + "direct": [], + "namespace": [], + "root": { + "diagnostics": [], + "invalid": [ + { + "code": 2554, + "line": 14, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 15, + "message": "Argument of type '{}' is not assignable to parameter of type 'Option'.\n Property 'danmuku' is missing in type '{}' but required in type 'Option'." + }, + { + "code": 2322, + "line": 16, + "message": "Type '[number, number]' is not assignable to type '{ time: number; value: number; }'." + }, + { + "code": 2739, + "line": 17, + "message": "Type 'Result' is missing the following properties from type 'Promise': then, catch, finally, [Symbol.toStringTag]" + } + ], + "negativeStatements": 4 + }, + "runtime": { + "diagnostics": [], + "invalid": [ + { + "code": 2322, + "line": 32, + "message": "Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2339, + "line": 77, + "message": "Property 'name' does not exist on type 'Owner'." + }, + { + "code": 2339, + "line": 78, + "message": "Property 'mount' does not exist on type 'Owner'." + }, + { + "code": 2554, + "line": 81, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 82, + "message": "Argument of type 'undefined' is not assignable to parameter of type 'Partial'." + }, + { + "code": 2345, + "line": 83, + "message": "Argument of type '{ id: string; }' is not assignable to parameter of type 'Danmu'.\n Property 'text' is missing in type '{ id: string; }' but required in type 'Danmu'." + }, + { + "code": 2353, + "line": 84, + "message": "Object literal may only specify known properties, and 'time' does not exist in type '[number, number]'." + }, + { + "code": 2322, + "line": 85, + "message": "Type 'number' is not assignable to type '[string | number, string | number]'." + }, + { + "code": 2554, + "line": 86, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2540, + "line": 87, + "message": "Cannot assign to 'option' because it is a read-only property." + }, + { + "code": 2540, + "line": 88, + "message": "Cannot assign to 'isHide' because it is a read-only property." + }, + { + "code": 2540, + "line": 89, + "message": "Cannot assign to 'isStop' because it is a read-only property." + }, + { + "code": 2339, + "line": 90, + "message": "Property 'hide' does not exist on type 'Promise'." + }, + { + "code": 2339, + "line": 91, + "message": "Property 'default' does not exist on type 'RuntimeFactory'." + } + ], + "negativeStatements": 14 + }, + "commonjs": [] + }, + { + "plugin": "candidate", + "compiler": "4.3.5", + "mode": "node10-commonjs-no-interop", + "direct": [], + "namespace": [], + "root": { + "diagnostics": [], + "invalid": [ + { + "code": 2554, + "line": 14, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 15, + "message": "Argument of type '{}' is not assignable to parameter of type 'Option'.\n Property 'danmuku' is missing in type '{}' but required in type 'Option'." + }, + { + "code": 2322, + "line": 16, + "message": "Type '[number, number]' is not assignable to type '{ time: number; value: number; }'." + }, + { + "code": 2739, + "line": 17, + "message": "Type 'Result' is missing the following properties from type 'Promise': then, catch, finally, [Symbol.toStringTag]" + } + ], + "negativeStatements": 4 + }, + "runtime": { + "diagnostics": [], + "invalid": [ + { + "code": 2322, + "line": 32, + "message": "Type 'unknown' is not assignable to type 'string'." + }, + { + "code": 2339, + "line": 77, + "message": "Property 'name' does not exist on type 'Owner'." + }, + { + "code": 2339, + "line": 78, + "message": "Property 'mount' does not exist on type 'Owner'." + }, + { + "code": 2554, + "line": 81, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2345, + "line": 82, + "message": "Argument of type 'undefined' is not assignable to parameter of type 'Partial'." + }, + { + "code": 2345, + "line": 83, + "message": "Argument of type '{ id: string; }' is not assignable to parameter of type 'Danmu'.\n Property 'text' is missing in type '{ id: string; }' but required in type 'Danmu'." + }, + { + "code": 2322, + "line": 84, + "message": "Type '{ time: number; value: number; }' is not assignable to type 'Point'.\n Object literal may only specify known properties, and 'time' does not exist in type '[number, number]'." + }, + { + "code": 2322, + "line": 85, + "message": "Type 'number' is not assignable to type 'Margin | undefined'." + }, + { + "code": 2554, + "line": 86, + "message": "Expected 1 arguments, but got 0." + }, + { + "code": 2540, + "line": 87, + "message": "Cannot assign to 'option' because it is a read-only property." + }, + { + "code": 2540, + "line": 88, + "message": "Cannot assign to 'isHide' because it is a read-only property." + }, + { + "code": 2540, + "line": 89, + "message": "Cannot assign to 'isStop' because it is a read-only property." + }, + { + "code": 2339, + "line": 90, + "message": "Property 'hide' does not exist on type 'Promise'." + }, + { + "code": 2339, + "line": 91, + "message": "Property 'default' does not exist on type 'RuntimeFactory'." + } + ], + "negativeStatements": 14 + }, + "commonjs": [] + } + ] + }, + "productionFiles": 390, + "productionCheck": { + "file": "refactor/.cache/danmuku06-typecheck.log", + "sha256": "c292bf1f166b99a34bcf65d403e20dc247f21e73e120dd7d186f87b0d1a41a1b" + } + }, + "browsers": [ + { + "format": "source", + "file": "refactor/.cache/danmuku06-accepted-source-browser.json", + "sha256": "e5835ca17e9b8326e4e618934b8bd5ff97bc20be2af8fffbdf2b4c114c3e704b", + "log": { + "file": "refactor/.cache/danmuku06-accepted-source-browser.log", + "sha256": "004a6a579d8289d8dcb661a5250f2c47e374e16e0e2f5218effcbf293c55bf53" + }, + "stats": { + "startTime": "2026-09-13T13:54:51.156Z", + "duration": 166824.218, + "expected": 156, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "workers": 2, + "versions": { + "chromium": { + "version": "153.0.8010.12", + "platform": "win32" + }, + "firefox": { + "version": "155.0", + "platform": "win32" + }, + "webkit": { + "version": "26.6", + "platform": "win32" + } + }, + "tests": [ + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 3071, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "7c3ec36f5475c6329002f4ef0e231909f5740aefa8d000ffac2773d52a87e65c" + }, + { + "name": "browser-evidence", + "sha256": "9abfbfccccec4b8b62b0293ce24570222f152f51a8a552003e5962920ed266e7" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2350, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "70f4e9e78964ca657eeebf12f5fdfa3ae35db7347cc90240ce24647954c420eb" + }, + { + "name": "browser-evidence", + "sha256": "9abfbfccccec4b8b62b0293ce24570222f152f51a8a552003e5962920ed266e7" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2370, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "348b5e07d2d45555013c6aadf3a05353c60aa602dbe8ce96d7c73c56d5d22234" + }, + { + "name": "browser-evidence", + "sha256": "9abfbfccccec4b8b62b0293ce24570222f152f51a8a552003e5962920ed266e7" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 3075, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "4010f37ffc708afe81b2bd8704229e2f825567401b6f465af1f7850e82fbd391" + }, + { + "name": "browser-evidence", + "sha256": "0985be257051d444c5930670d01acc15e27b71036e9436311b7e3d904db57e1a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2356, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "3ed3925d0b137edb5f1731bf22ae3ba98da81a7663136782a46787282749abf0" + }, + { + "name": "browser-evidence", + "sha256": "0985be257051d444c5930670d01acc15e27b71036e9436311b7e3d904db57e1a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2320, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "3e86dc6d770dcaec2f69e6979896880ca44ab91316a94e81b4583553461cc35b" + }, + { + "name": "browser-evidence", + "sha256": "0985be257051d444c5930670d01acc15e27b71036e9436311b7e3d904db57e1a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 4134, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "8bc924dc62dc89cf3c87fa9cabf7269b35455734cf0f0d248fa694bda8932fbd" + }, + { + "name": "browser-evidence", + "sha256": "e2e51d88292c75949273ad25946c097e70ec8f6a01fbbd5b7c21cc18b4c2087a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 2492, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "2cfcd54f4a3147ff2e4c50db047243236cfcd94dd1c1d414d10c990f16aae263" + }, + { + "name": "browser-evidence", + "sha256": "e2e51d88292c75949273ad25946c097e70ec8f6a01fbbd5b7c21cc18b4c2087a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 2245, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "0050f061b24b488636005fc9101ca496c6f897d72f2233cc0afd14b63d47780f" + }, + { + "name": "browser-evidence", + "sha256": "e2e51d88292c75949273ad25946c097e70ec8f6a01fbbd5b7c21cc18b4c2087a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 3881, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "54c4219729f60d5fca8bef4b5d81fde09e2470f431c97318269d51fd8be4a8aa" + }, + { + "name": "browser-evidence", + "sha256": "9e5cdc1e55756fc764cce7383dcceffd3abf2734a295a4d13841bd6766e103ca" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 2303, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "89e4874637c2faf43c1bb4bfcd338a484c187039c9540073ac7172cf6db87fb9" + }, + { + "name": "browser-evidence", + "sha256": "aefd9f89aedfbc55f77b71c5c0177b4be0d29228feeb3990401b0865bc212f50" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 2270, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "1c0acd61e91a41683373ed4ae8f5390f15c69a90e977d3513cda6d27eb8a4559" + }, + { + "name": "browser-evidence", + "sha256": "aefd9f89aedfbc55f77b71c5c0177b4be0d29228feeb3990401b0865bc212f50" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3888, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "62b34b6b873fc7cc0f46a0c6ae77819f988083f5f0feadd6df339b5930356ae9" + }, + { + "name": "browser-evidence", + "sha256": "740fb71cc79b564e1d35e1c6d0377bb4eee660310954d6ad7537ecee54debccd" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3798, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "db324c1457d471de3934be92f044dfc68af5855af237918f03602c3f2282ceb0" + }, + { + "name": "browser-evidence", + "sha256": "740fb71cc79b564e1d35e1c6d0377bb4eee660310954d6ad7537ecee54debccd" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3958, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "a46839c063b0406ff5a2a3ed6b39713c138b531861cd371d4c3212deaea6138d" + }, + { + "name": "browser-evidence", + "sha256": "740fb71cc79b564e1d35e1c6d0377bb4eee660310954d6ad7537ecee54debccd" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3912, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "dc9ca9cab75990955ef303421041408a4b22feb8a9a43f2770c936be418fb924" + }, + { + "name": "browser-evidence", + "sha256": "c678b0b03f9aa3e0837bd195112862eb1b0435fea490ddda5b271bc744c77a9e" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3788, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "5033e510f974834aa3e5df41de6687a50165a75c7d258f7a67b8f3526bd0c654" + }, + { + "name": "browser-evidence", + "sha256": "c678b0b03f9aa3e0837bd195112862eb1b0435fea490ddda5b271bc744c77a9e" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3563, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "ee38d175ab29431ad4fb18a416105ba4122fc1c4ce07f9c85d5298e03ff7d19e" + }, + { + "name": "browser-evidence", + "sha256": "c678b0b03f9aa3e0837bd195112862eb1b0435fea490ddda5b271bc744c77a9e" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (published)", + "engine": "chromium", + "durationMs": 270, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "acf72a63d3cf7fe900dd2cf5837fc78b40a31d15be3b3c93bf9098991ec381a9" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "78ae5a551f64c6914fbf3d2ea36a2f3184a2ec6064261fa92ee131978070cf67" + }, + { + "name": "browser-evidence", + "sha256": "fe97591c2ec718049bc4df5b1b2dff395cac175fb6a40cf2b873c0b93046399c" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (published)", + "engine": "chromium", + "durationMs": 274, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "acf72a63d3cf7fe900dd2cf5837fc78b40a31d15be3b3c93bf9098991ec381a9" + }, + { + "name": "danmuku-native-parser", + "sha256": "aa8e93b8cd2860cccca992e3ebea9f2c3dcf5048c83c8d403d203108bfb76ee0" + }, + { + "name": "browser-evidence", + "sha256": "588ccbd1c2f62ad680992d8a83b4043b2701679538a0c91f34db764582018447" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (published)", + "engine": "chromium", + "durationMs": 266, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "acf72a63d3cf7fe900dd2cf5837fc78b40a31d15be3b3c93bf9098991ec381a9" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "6d5938cd03d88028eb4985aa6937a3558a96de48b2ec6afc7f1144dfb841eb45" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (published)", + "engine": "chromium", + "durationMs": 257, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "acf72a63d3cf7fe900dd2cf5837fc78b40a31d15be3b3c93bf9098991ec381a9" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "6d5938cd03d88028eb4985aa6937a3558a96de48b2ec6afc7f1144dfb841eb45" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (candidate)", + "engine": "chromium", + "durationMs": 297, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "21046732b76bade1aaf626424a2c31bdace5abd486ac5f6fad615e4363977138" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "d6bf0e48856c4c6133c420539d3e291bd8fd9a38d3ce1bf0b7a1da367ce57a20" + }, + { + "name": "browser-evidence", + "sha256": "17047a258ae97d9487d1dabb077a10bbe8aa9eb37da1b71e6e9bf812ed9dafe0" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (candidate)", + "engine": "chromium", + "durationMs": 311, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "21046732b76bade1aaf626424a2c31bdace5abd486ac5f6fad615e4363977138" + }, + { + "name": "danmuku-native-parser", + "sha256": "c7de82eb2c305c2ff4a5bd6fa0c3893748bf350f713ce821166b2e8aed463a12" + }, + { + "name": "browser-evidence", + "sha256": "8e2885bd8ad4c8c905f46496b6548bee9053c76286c2e5470334e853f99fc5b6" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (candidate)", + "engine": "chromium", + "durationMs": 315, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "21046732b76bade1aaf626424a2c31bdace5abd486ac5f6fad615e4363977138" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "73d86528d8480a4c7dea456216b5f1cb9e12b53cced6e3c98544021ce0cbee61" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (candidate)", + "engine": "chromium", + "durationMs": 278, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "21046732b76bade1aaf626424a2c31bdace5abd486ac5f6fad615e4363977138" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "73d86528d8480a4c7dea456216b5f1cb9e12b53cced6e3c98544021ce0cbee61" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (published)", + "engine": "firefox", + "durationMs": 602, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "acf72a63d3cf7fe900dd2cf5837fc78b40a31d15be3b3c93bf9098991ec381a9" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "daddc1c457503ac165dd091e0f5ca4f6dee5b30a07cbe00cf7726fcbe3e14cfe" + }, + { + "name": "browser-evidence", + "sha256": "c6ed5c2b14447cb634d85eb6d9378e5ae6ea0baa6d103c786a5c786e0f8bf6d1" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (published)", + "engine": "firefox", + "durationMs": 618, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "acf72a63d3cf7fe900dd2cf5837fc78b40a31d15be3b3c93bf9098991ec381a9" + }, + { + "name": "danmuku-native-parser", + "sha256": "b2b84a70a4da538f4e9d5954e3955b949c6250f15503d14f29803cbf1abfc7b6" + }, + { + "name": "browser-evidence", + "sha256": "d5097905dd4b15bd3deef86f14437f413c7651715bc40200527d427e6bf89786" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (published)", + "engine": "firefox", + "durationMs": 470, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "acf72a63d3cf7fe900dd2cf5837fc78b40a31d15be3b3c93bf9098991ec381a9" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (published)", + "engine": "firefox", + "durationMs": 433, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "acf72a63d3cf7fe900dd2cf5837fc78b40a31d15be3b3c93bf9098991ec381a9" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (candidate)", + "engine": "firefox", + "durationMs": 520, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "21046732b76bade1aaf626424a2c31bdace5abd486ac5f6fad615e4363977138" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "ad08cbf06c0adb8b2a6ea19e06fab78824029b0444c90b94b67208616f10939f" + }, + { + "name": "browser-evidence", + "sha256": "dc3f7faf36619fdf354ad91eb7c1a62cefbfdb9e62d225c82183d1b172b40981" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (candidate)", + "engine": "firefox", + "durationMs": 654, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "21046732b76bade1aaf626424a2c31bdace5abd486ac5f6fad615e4363977138" + }, + { + "name": "danmuku-native-parser", + "sha256": "05a1b2d5613c662f7363324885bc2a3472a7de4f7d1dbc3c6a894527a48695f6" + }, + { + "name": "browser-evidence", + "sha256": "8823e4ebd37f9cb544be40068f89f493b772863ea73bfe1398d2567f837c1297" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (candidate)", + "engine": "firefox", + "durationMs": 593, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "21046732b76bade1aaf626424a2c31bdace5abd486ac5f6fad615e4363977138" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (candidate)", + "engine": "firefox", + "durationMs": 452, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "21046732b76bade1aaf626424a2c31bdace5abd486ac5f6fad615e4363977138" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (published)", + "engine": "webkit", + "durationMs": 1128, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "acf72a63d3cf7fe900dd2cf5837fc78b40a31d15be3b3c93bf9098991ec381a9" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "35d786eaaac72bf96d460ae624c7fe122b4b9a79de2d20c9659e1ceb265134ba" + }, + { + "name": "browser-evidence", + "sha256": "5e4d385feb6418cb42d6d647582a1e65bbe0cc31e3505de76890fc88b1811392" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (published)", + "engine": "webkit", + "durationMs": 1327, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "acf72a63d3cf7fe900dd2cf5837fc78b40a31d15be3b3c93bf9098991ec381a9" + }, + { + "name": "danmuku-native-parser", + "sha256": "6b17b194996ac6003221946b32c13584dc90afc239397a9d3b1e48c16020dc39" + }, + { + "name": "browser-evidence", + "sha256": "39b9993b7bd639340152bf8fcc8f9ce8ec1e6e6ff3f3516d81edec225d6901c9" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (published)", + "engine": "webkit", + "durationMs": 1124, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "acf72a63d3cf7fe900dd2cf5837fc78b40a31d15be3b3c93bf9098991ec381a9" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (published)", + "engine": "webkit", + "durationMs": 1006, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "acf72a63d3cf7fe900dd2cf5837fc78b40a31d15be3b3c93bf9098991ec381a9" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (candidate)", + "engine": "webkit", + "durationMs": 1185, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "21046732b76bade1aaf626424a2c31bdace5abd486ac5f6fad615e4363977138" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "afdd54c3db16c761061f756302ab308653545e06fa9c92625d7f9f03cfd4c268" + }, + { + "name": "browser-evidence", + "sha256": "48d9c7b6558a5272f595472ba624c9e0cb30a713399614cd029ae1882d69c1e3" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (candidate)", + "engine": "webkit", + "durationMs": 1220, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "21046732b76bade1aaf626424a2c31bdace5abd486ac5f6fad615e4363977138" + }, + { + "name": "danmuku-native-parser", + "sha256": "37117aee7b7ad117b5d22d5dbc43190ee12a04cc7cb0882d79c5783b5ad31020" + }, + { + "name": "browser-evidence", + "sha256": "ef659c544a73dfe808900970bd58c03e19d2088add365af893775afca9f699c6" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (candidate)", + "engine": "webkit", + "durationMs": 1026, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "21046732b76bade1aaf626424a2c31bdace5abd486ac5f6fad615e4363977138" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (candidate)", + "engine": "webkit", + "durationMs": 1094, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "21046732b76bade1aaf626424a2c31bdace5abd486ac5f6fad615e4363977138" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: shared setting mount preserves the surviving instance and later user changes", + "engine": "chromium", + "durationMs": 316, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "96cc31e201ef60e560a9824e3be38eb34536b32db988f298d371093f72468365" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "chromium", + "durationMs": 340, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "7443bc0c9ae74105a1331def30a824533188b99262f227f0a5850c0a96fd7cae" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "chromium", + "durationMs": 453, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "6c2ce72b69cfdbb53954e48bdda7c647de8aba81b5f7ba1fe596ac59e2eee5dd" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "96cc31e201ef60e560a9824e3be38eb34536b32db988f298d371093f72468365" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy clears active send timer and leaves another mounted instance usable", + "engine": "chromium", + "durationMs": 579, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "7443bc0c9ae74105a1331def30a824533188b99262f227f0a5850c0a96fd7cae" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "chromium", + "durationMs": 434, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "cdd2fdafe2ecd9052eaf6fda86cbdf09b012b00f757fd177458159800acbf877" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "1624840c81ebf08857ddbd3d3b59f1df9ffba31be88bdcdb021a9b1e24b42bc9" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "chromium", + "durationMs": 508, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "96cc31e201ef60e560a9824e3be38eb34536b32db988f298d371093f72468365" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "chromium", + "durationMs": 474, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "96cc31e201ef60e560a9824e3be38eb34536b32db988f298d371093f72468365" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "chromium", + "durationMs": 505, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "08d95dfd0b903fcf0fc3f6248a5d85d80c4c50c5f8781915f3ec91ee19b3d41f" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "7443bc0c9ae74105a1331def30a824533188b99262f227f0a5850c0a96fd7cae" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: shared setting mount preserves the surviving instance and later user changes", + "engine": "chromium", + "durationMs": 316, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "326565edb6152c52b97bac465aeeed05d0abe7d1c63c8e3de4d2cb8a2535b781" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "chromium", + "durationMs": 377, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "ea0c1c023cf2826f5be109522b0b0a87d6043b295b7eb2bf335aa6dedb315b46" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "chromium", + "durationMs": 523, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "ccfacab12e5be4bdb038f813b97e0b446b937fdef73d7da4aa53b529e044118c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "326565edb6152c52b97bac465aeeed05d0abe7d1c63c8e3de4d2cb8a2535b781" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy clears active send timer and leaves another mounted instance usable", + "engine": "chromium", + "durationMs": 555, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "ea0c1c023cf2826f5be109522b0b0a87d6043b295b7eb2bf335aa6dedb315b46" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "chromium", + "durationMs": 442, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "d4651ffbe113acc1a6d7a905295a8bcb4c088e1e71dddb0fd1fb8278b6a71860" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "69de9f174219607024fd0ea29e663a04b966d5e77d7a527845a75f8765a7324f" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "chromium", + "durationMs": 486, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "326565edb6152c52b97bac465aeeed05d0abe7d1c63c8e3de4d2cb8a2535b781" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "chromium", + "durationMs": 504, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "326565edb6152c52b97bac465aeeed05d0abe7d1c63c8e3de4d2cb8a2535b781" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "chromium", + "durationMs": 510, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "77decb3724c407faf9445a5bcad129fefecd111d1223bc68f1d02d36862b8a85" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "ea0c1c023cf2826f5be109522b0b0a87d6043b295b7eb2bf335aa6dedb315b46" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: shared setting mount preserves the surviving instance and later user changes", + "engine": "firefox", + "durationMs": 515, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "firefox", + "durationMs": 689, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "firefox", + "durationMs": 1629, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "6c2ce72b69cfdbb53954e48bdda7c647de8aba81b5f7ba1fe596ac59e2eee5dd" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy clears active send timer and leaves another mounted instance usable", + "engine": "firefox", + "durationMs": 834, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "firefox", + "durationMs": 807, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "cdd2fdafe2ecd9052eaf6fda86cbdf09b012b00f757fd177458159800acbf877" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "a37e53602682ec1f0ad1eff163ef58dedffaa5658b8f55520b864f4b2a766e3e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "firefox", + "durationMs": 865, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "firefox", + "durationMs": 784, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "firefox", + "durationMs": 673, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "08d95dfd0b903fcf0fc3f6248a5d85d80c4c50c5f8781915f3ec91ee19b3d41f" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: shared setting mount preserves the surviving instance and later user changes", + "engine": "firefox", + "durationMs": 529, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "firefox", + "durationMs": 631, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "firefox", + "durationMs": 1500, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "ccfacab12e5be4bdb038f813b97e0b446b937fdef73d7da4aa53b529e044118c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy clears active send timer and leaves another mounted instance usable", + "engine": "firefox", + "durationMs": 834, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "firefox", + "durationMs": 840, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "d4651ffbe113acc1a6d7a905295a8bcb4c088e1e71dddb0fd1fb8278b6a71860" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "93533638ef2d44aab06038ae645023c766dad22d19767cee2d01d626ed9b9569" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "firefox", + "durationMs": 826, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "firefox", + "durationMs": 830, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "firefox", + "durationMs": 723, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "77decb3724c407faf9445a5bcad129fefecd111d1223bc68f1d02d36862b8a85" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: shared setting mount preserves the surviving instance and later user changes", + "engine": "webkit", + "durationMs": 1215, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "webkit", + "durationMs": 1407, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "webkit", + "durationMs": 1675, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "6c2ce72b69cfdbb53954e48bdda7c647de8aba81b5f7ba1fe596ac59e2eee5dd" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy clears active send timer and leaves another mounted instance usable", + "engine": "webkit", + "durationMs": 2506, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "webkit", + "durationMs": 1736, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "cdd2fdafe2ecd9052eaf6fda86cbdf09b012b00f757fd177458159800acbf877" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "b4e1558640dd3365fa32b01258a3b1ebc8cc340d3d9ee679c6e0650dab2d806e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "webkit", + "durationMs": 1630, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "webkit", + "durationMs": 1756, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "635ca30b04b5d9440e76f54447f89c6c7dbfa19c964b405dc693b96166f1969c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "webkit", + "durationMs": 1987, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "08d95dfd0b903fcf0fc3f6248a5d85d80c4c50c5f8781915f3ec91ee19b3d41f" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: shared setting mount preserves the surviving instance and later user changes", + "engine": "webkit", + "durationMs": 1486, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "webkit", + "durationMs": 1334, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "webkit", + "durationMs": 2200, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "ccfacab12e5be4bdb038f813b97e0b446b937fdef73d7da4aa53b529e044118c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy clears active send timer and leaves another mounted instance usable", + "engine": "webkit", + "durationMs": 2450, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "webkit", + "durationMs": 1635, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "d4651ffbe113acc1a6d7a905295a8bcb4c088e1e71dddb0fd1fb8278b6a71860" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "ab222de8427a05b26737bbbf2d35daab8db7a2c04db2a7600b5a5428d2ae8931" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "webkit", + "durationMs": 1565, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "webkit", + "durationMs": 1725, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3b7e50074cdad7ba25ed4807332a518447367483c1b5df1ef48eba0dd7047391" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "webkit", + "durationMs": 1934, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "77decb3724c407faf9445a5bcad129fefecd111d1223bc68f1d02d36862b8a85" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "chromium", + "durationMs": 2265, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "9021bf098617d2c9e2877d83e9da0ff8ee494bc33c5f8932ccfc36f07c5313f6" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "25fbe05d541a57101003b57baa05d0b6ef14d42164060ba4e55e3708a0c6ff58" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "chromium", + "durationMs": 484, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "06c412a3899ab39396f87f877f81741704cf6a134cfe6347bfa51b54f8f4e081" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "fef4e929fffb1043979163067fcca68c377b76ae661c8300d48956126934b4be" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "chromium", + "durationMs": 502, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "2f5db9dda1c8bea20879ba4487b63485c78e453f563bfbe4f75f0035b35a869c" + }, + { + "name": "danmuku-layout", + "sha256": "47d3ac80cef2f0a944730bac8b694b1017556790124353c1ceb4fdc4718ce212" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "ed6f6ecb8f54b86f408120ec92f8a8cbbafbc47bd95975a2a49e8595a65f4594" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2717, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "b6a6ba3c85c140377603186ded6caa4edffcfeba2e75d8b58cf0344796ee9005" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "4ffd177c7ed947080e6e3b9da66543bc27315d460157a8644db673de98c5bd75" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2374, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "d9b3eba4ee268c92a5ef6ec6f3be1e87080d2791ce575b6e892dab26816472d6" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "be1058bdb98f125b00fd8baf8b759a4a3aad3a5b883616e6ce575563d9821d6f" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2536, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "07e822835ed7e5c6faff4aa2c6a96502213e91e29885ab891de24228a4e43146" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "5a9a1ad8cc2f9164ee3f4bb3258a6bbd2d1b0e70cef25bbe3544960d9604a487" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2287, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "4344cd82ddeb79ccb48e0cace729293bbca434efac7c966858594a800b90c021" + }, + { + "name": "browser-evidence", + "sha256": "788cd2aa1d722d3fae8169c90dc925ee2ebedb65893a37aaccf897845ef9bed1" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "chromium", + "durationMs": 1330, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-layout", + "sha256": "d2eed0425bb7ec7561219f8e95af691d81ff4210bb3a50dafa994e5eec264b8e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "ec6e2cf680d321e073ed4464690a526f12b27b0e53e4f0f1735223127155225b" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "chromium", + "durationMs": 1514, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "1ca347e0f082bbe5b2bee4ff249b2c390511f3c292ad3a2aa9f99301a0cd0c7d" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "3bed33edc19a0ec4c497049bbe6ea50d80de931e4aceb00a760809a32732cc59" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "d08906d8f5ea53fcb888fe47859a62b1618ce0f84d9b0f303540b644f2ffbb66" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "chromium", + "durationMs": 2379, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "6c63a5bd1359fd7517962d18080ee83f38c8a4011c1fd5429da6b6b19635ef20" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "4b3a69200ce68cde1ce990c05b013d9429ca2978a056af2eefdc9b69c1dac208" + }, + { + "name": "danmuku-layout", + "sha256": "d1940cdd2560f6aa3c94ac6b311c811b9b5369cff28ea09773b4741983e87a37" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "bb3a65b51d7dc52fd8ab099fdaa23b69957d483cb40ddf326680bd6a29a56a29" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "chromium", + "durationMs": 4139, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "8843ef2a447a22e7fb39c0d153ebf0027d1da45b2ac11efc87edffb94e167994" + }, + { + "name": "danmuku-layout", + "sha256": "b80573c1bd08214afa0740a52c7c39409f06aa4a4a16eb3d2efd38208829e127" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "531c6483b25bc0f3bf5a886ec32ecb2d37d541336724d2b3c3d8f4d6bace9179" + }, + { + "name": "browser-evidence", + "sha256": "7c9f4a21af0011c3d15c3c352a7ff53739541aa6f6c3e93348d977271bc236f7" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "chromium", + "durationMs": 2313, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "136b5a4c8b432461dd22402d2658b4f3973372e45cedcff9cb1d3a93e87d14e9" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "dc43660203c69d3285b3d959883a0b5059d42437d6d47d7c8f70b24a26c08f8f" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "chromium", + "durationMs": 504, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "357c5eaab9af565bec5146454811e085f1bc723700f22f2cf6a787790e1233b6" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "642a1eb4bcf3b452db4ba147d230ddaca6f802f8aea8b10ea556f2f512340830" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "chromium", + "durationMs": 522, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "f50a5aeed13ba6a769acbad6e1f88a81ca65a20ef4956f7f29b106b61581a9c1" + }, + { + "name": "danmuku-layout", + "sha256": "bf683a0366afec9454af6c3aa3c57f4ff4c682e6d442b27119b2c868235fc817" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "a1273d106072503f3ffe0c88657339d5a62ae81b07c06ebdcd2e3ec419e457ad" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2656, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "84afe76b1c3ee83b87bdd56b08c480e63de99d6771e44bf6c4b52bb010988c92" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "c63c1e54915aa70bc4929136d2b4b8548b7866c47baca7899635519df39f812d" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2461, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "44834ff1bedbd9aed95437579df5997ad565728b84a8c8a45019635cfc7e061d" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "92e7896cda94c21a4ee80dcc4c16721501d071da2a7f9679bed5ff198f3e6353" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2452, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "af47678279a80a5869bc999f5ad46078e54b359b404f34d1b6b9ae3176f84568" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "d3e788c316cb7352564a3db95a60115b40f06651fc574f21943ba99f3171dea5" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2289, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "c4e98c02beb2c07547870c33e8f51ea5cb736b5c16ec849a67c40b9619b9aa39" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "chromium", + "durationMs": 1416, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-layout", + "sha256": "dc7c7155e94186069edb3e8d7127951b1552cf5b7ad1b5d0d3ee158ea71abbaf" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "29eec45809be3703e978c0eb6c4be0cadfe026a467e09ea3c4ee9a82c0848539" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "chromium", + "durationMs": 1530, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "752eb9e8858cab6b92b90196b36d19173bd99ddbb3f5d9cc5748aa540a226371" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "a206efeaf9ceaaae88ad10ec164f8d4a1fee3ba2ab0d2928abde99c7d828497c" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "834b88bd3896fe1f5f38d8c39304111f717754c1947fbb78620f9bd9b721c108" + }, + { + "name": "browser-evidence", + "sha256": "9dd6e666e6f908e2a5bcbedda78132ab3713311a939892280dd9dc3823d724d4" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "chromium", + "durationMs": 2413, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b4ba84bcbe92b28af16e4bfc9ced00329a12bdf9e91e07036cfc4fd55cb033ee" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "27eb782612372963273d25e917a55b65ade18dafdea4bcb8bdd0fe4f3174a2a9" + }, + { + "name": "danmuku-layout", + "sha256": "d3efbf707d350ce2fba7b4b638c2aba660ac1c2213ea0ee27a4f386a20673ab2" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "f01240768896fb8dd4ab091f293ae601897a4f50847e7f6db2c93122360e0105" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "chromium", + "durationMs": 4154, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "a2adecb587fbd148a79c1b0e75315c3f2e9598fd45951039f8efd864672c1ba7" + }, + { + "name": "danmuku-layout", + "sha256": "730ad5045958afb926cc667729396863da9fb13414b2575e66529c077f2140cc" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "e7127a7d46b2b561caf44bcb093a374b3febe3f7fec47a11eaed98e043f6ca47" + }, + { + "name": "browser-evidence", + "sha256": "71730b2e97b3f5935a1eca46fbd686ce72103fe9230088797dccd9e13d9c206e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "firefox", + "durationMs": 2547, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "21ced1da9c91883d6451166351ddd6fcbae8a6cbdf2e9d2d05dcc7307667412b" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "451cb1f1a35c75068149697112c71b9881c44053e7c68c6223d7ee308893921c" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "firefox", + "durationMs": 772, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "d6c22285ab81d89dfcfa457fe16ca6ec2f05ef208240ef3be3c1bf8bd7da3d80" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "4e079f6769eedfa829e76f71e1dbaa5825b40bce70446c1fb8ba69a1b93eb4b6" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "firefox", + "durationMs": 810, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "c5d91a5681299f8dde4c58e59e6d5cbb796fadabcc743f7f4d01bac3b64f8967" + }, + { + "name": "danmuku-layout", + "sha256": "66c85a51ada2feb2a44c968141303771037227b33496d4c627e4b3506353c5ce" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "6d6e3bbace50794bd25ec6a8f01e605c40d234e5fa11decee32ddea0c1d5c0dd" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2774, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "1c4910c47b0217368d3b6f2a2e17986a5190444890d3cea9019b33382848170a" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "0a710e75209bed549219b7b131158531bb799412a3a6e8d58e83e416f17fb715" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2969, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "f08e4a5957a52d33e85f6a8af721ce0dcbb411eba1d8da446224ed3c2a6dd95e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "b1d87f33bf03d304e7f25776171099475e8e5812515887f345bd37399f82009a" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2841, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "e28cc30af6ef3ef2923d9c208dcc2c6c00089cc791ad631e0553cd70450276ef" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "812691e8512ee03b85404c880566c03f7e57f0918f73f8cb6254a488a7dc3843" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2544, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "df5525971f7b179a54720daf3b0d5d3a21c13e5728ffe90be3044e434649927b" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "firefox", + "durationMs": 1542, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-layout", + "sha256": "c4d118d1143bd36d6df3e2b0780128750cfe6920db1fc3146e4c89bd46579453" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "69ee21a1be83381ea14befbad14699fc3856301d179a964c327dcfa56f6781db" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "firefox", + "durationMs": 1558, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "1ca347e0f082bbe5b2bee4ff249b2c390511f3c292ad3a2aa9f99301a0cd0c7d" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "31c074a20d67007a07919abddea7defaa160034113983e2945ea1f47fafd1484" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "7593b65b87207c7e58ce60ed654cdc2e9f569010e6a3703aa36322c5f9d908ee" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "firefox", + "durationMs": 2556, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "6c63a5bd1359fd7517962d18080ee83f38c8a4011c1fd5429da6b6b19635ef20" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "f92b7a7c5e0d7cc16b0a8c1194936ad03c746b5938f91ae419290914edc7e9c0" + }, + { + "name": "danmuku-layout", + "sha256": "c296cf6d76519246839c6728c451112e98c19b71c5994f751882c3d53d96f350" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "7c1a7b26a1fb8dc6758d3ecc0dc4ca64573fd09921b850e6366636b869898ee3" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "firefox", + "durationMs": 4710, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "8843ef2a447a22e7fb39c0d153ebf0027d1da45b2ac11efc87edffb94e167994" + }, + { + "name": "danmuku-layout", + "sha256": "bd9bce1ddd303a73e114f52bc855c26eccba5fa282d487f318262c7a312cf6ec" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "e7703cf8772b449b9bc12d7e843371e8279c936c8d17ff086713891357f6bb45" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "firefox", + "durationMs": 2659, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "21ced1da9c91883d6451166351ddd6fcbae8a6cbdf2e9d2d05dcc7307667412b" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "880671889f6c3ebff118f3fcdcb00833200b94c887f305df9458843548ec9aff" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "firefox", + "durationMs": 819, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "2428f04af9696eed3eae4072611962f30a1d605e383a0bb859f2897d99ad1e8e" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "3ce3cd86c1b852387a1baf2ca25a521929ecf22734a230518d63f6195280970f" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "firefox", + "durationMs": 831, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "1d1d92856cb43660406b94b35ad7847b89c1f8b378d98e75ab39fa3f5ab27b3a" + }, + { + "name": "danmuku-layout", + "sha256": "c73b45f89abee768165b681ea9aa51512977b6f846cae647f478dbaa4041f5d7" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "420b38dc05127c4506e4c90ba64659ae451404dc6e0d04fb1ed1a08ea3ae8427" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2876, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "7416e6e4baa7e249e28d4f931bc823525186a283330aab39ac88c4d84637c06a" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "c3f49bc32c66e06777a1a2e74bda883a66e412a772c5c9ec16f15a144a6d7ae3" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 3246, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "f08e4a5957a52d33e85f6a8af721ce0dcbb411eba1d8da446224ed3c2a6dd95e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "31323e4507f191b307b39b86295754627cdef7015654a6aafa4133f88c57e01c" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2783, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "cde8eeea4fa8a83eefd6af0c7d47bdc072e94e90f7694ba77f63d42c965d96ef" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "b1c26b0f1519da3655571cbec31810fc01239c22add1ca4fd2365b4b33df71fb" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2629, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "404f157e5eb8bd49117002604a4c109c6e49aa8064380d32d7be32923c586ba3" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "firefox", + "durationMs": 1597, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-layout", + "sha256": "fb5fc5323d8572801b26e830b989234ce46cb75b6dcdfb9d79dcfa4cfdb503f6" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "f3850c9a0f8c26fe32b380beab6f0b62eb93ef156ddb3165a196aea9f70d19a1" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "firefox", + "durationMs": 1594, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "752eb9e8858cab6b92b90196b36d19173bd99ddbb3f5d9cc5748aa540a226371" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "27e7fb3bfd87621de40f1fccb3892ec30520db490a3390c3f1475d7e2c7c4ab8" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "cba66b64006ce9ae507cea5b0293636c0c0310353587e9eab88da7558c8b9c42" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "firefox", + "durationMs": 2807, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b4ba84bcbe92b28af16e4bfc9ced00329a12bdf9e91e07036cfc4fd55cb033ee" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "f92b7a7c5e0d7cc16b0a8c1194936ad03c746b5938f91ae419290914edc7e9c0" + }, + { + "name": "danmuku-layout", + "sha256": "e1964d1084e1417a99b02eb5e1f621336ef90de62a82d5ea87405a94e8beb56b" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "5034bad1861c76c56d2b50431e46cd11b64e07eae3b38827160247eae129d082" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "firefox", + "durationMs": 4488, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "a2adecb587fbd148a79c1b0e75315c3f2e9598fd45951039f8efd864672c1ba7" + }, + { + "name": "danmuku-layout", + "sha256": "7e72053f1a2a0df171abffaf7d66d4f26d6955f2a641d47a72edbf48915e15ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "b6bc0387c779eff61cf7622a3694300d1e64f07d0c80803eaf66f55119219785" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "webkit", + "durationMs": 3648, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "8126de986bb9239b4f18c0b8bd966339ee97b666dff092a5332d5a4f502f275d" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "f0180e37d40277bb12c36818659ec7f8a80145202ae1cbb6e2ea3130df52e73e" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "webkit", + "durationMs": 1578, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "fa2b2401260e85f83a4c7a9d01ce5b2ea661d41973eebcdca2db0d2f8d484bfd" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "5ff8c78704b7566dce4e9957eebd88b9314d02791e9413e82090f35452186ff1" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "webkit", + "durationMs": 1685, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "39c70f034d1bf88742cfed7605ef81d62f92f48d383050d949acad5e11e9ca03" + }, + { + "name": "danmuku-layout", + "sha256": "e503bf6560316c4a0b887a3abf765c14d1b634af506f90978500b16eeb41c1b8" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "a81d93d0c17449d1abfa500ff6d53b62fdb5d0acf267e11455a5c8e34aeb02a6" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4635, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "fc209b900356bc0224562866b25df7ac8ef2e4f54b613027375346348560a144" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "bf0cea59ede2565480abb6ea087c1759ceecc3a21372008abba9d1f7f524d268" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 3921, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "ecd00bfb4f8fa3a0450d1d3e728d891e4a2eecbea1c30fafb8a4b86af3f0714a" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "14ca52974fd2ab3976b26c2cf01783daf3a5b337d94fdfb4c30ba399ddefbd51" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4230, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "7cf5c2590d66383db0b27e7e21bab820faed687f06f9121f5e3f21b852fbc95f" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "889a59d8d01797a5710436bcfd1b187c1ff22f9c8f3eacba7b205742345732eb" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 3644, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "5730b110475d191b257ad45f9737170335ec7609e2fe27018543df64459b2fdb" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "webkit", + "durationMs": 2378, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "fa4aa3405105d8db2e5698365d750091a6f0833ec7d0e327f4e56ac5f05418d5" + }, + { + "name": "danmuku-layout", + "sha256": "97b6befd1abe477af318fd39747ff83b266843411ca1fba32ee60e70969b79d2" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "c36c53bbaccfb10502fb75f9407c0109b7089706d4bf36406e20a0d7aab7e72d" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "webkit", + "durationMs": 2848, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "1ca347e0f082bbe5b2bee4ff249b2c390511f3c292ad3a2aa9f99301a0cd0c7d" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "5a52fdbf47a87812a774ed181f7679c125ee259cf2fb30a65bf0373ac86a6e14" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "3ffc93d0ffc9471a8af9386fc5696b3fcf7f909a27968d762de032b1af27c9e1" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "webkit", + "durationMs": 4179, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "6c63a5bd1359fd7517962d18080ee83f38c8a4011c1fd5429da6b6b19635ef20" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "2eaae2ef79d96e3e62653fb4bb08b5f475ec3df6dc4a1db40d8fe96a75436a6f" + }, + { + "name": "danmuku-layout", + "sha256": "2c6e8ed33d7086420f0e91080a8ea895a5bd13aeb0c27d47457755faa463000a" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "e71649028a2b3d95c95b3389f68b5318cfb09d789818ee51cddfdd58d4ef3cf8" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "webkit", + "durationMs": 7061, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "8843ef2a447a22e7fb39c0d153ebf0027d1da45b2ac11efc87edffb94e167994" + }, + { + "name": "danmuku-layout", + "sha256": "99ae29244ca98e404afc3ad9773a172731017f9431256056e5ecbb7913e46a59" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "4f26036aad8888ef502cab49ebfbccd2971910de5dacfadf2d97fb9079cbb872" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "webkit", + "durationMs": 3552, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "67dab3229b8337c8e2257b2e2e4a8dad8906cbd07e602aafacf90b0f509a4f26" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "0fc0cc902cabbea04d4fa3c935524975ef9b8ceafa34be0354f21dfa4c7265a3" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "webkit", + "durationMs": 1825, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "7e957b72ca7aa55b3cabee7cbb4e2d2b8918b6db6908bc724e98c55a788f4d3d" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "721697c74f85b4fddf502e4d70a68a1d456d7d812157020487522621547a0f1e" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "webkit", + "durationMs": 1943, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "9ceb0da2ff74c6a7b880b9dcd50d0b9e3bb986e9a3c439f5de0d12bc13832f9a" + }, + { + "name": "danmuku-layout", + "sha256": "7f2232cd6601699321e60309eb0036526f8e4e3a6799892425d402c923a14864" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "44ec27e9161b7fdd2b832287043f06f91966ce7d8f45de772659ca695882447c" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4686, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "d5ec2a3b7d2088cf65aeefe910e727ecf3c0880c44a214f38f20d7fca5f6a3cd" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "24d20b774cb3c837e3836c332f628a41a0d3c8dcca20eaa3b74ceadb33fcb407" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4000, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "b9fc5880ed1ccffddc819062fe5e6270d82ba0d590ed52afa8fc238896e93d91" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "45e4774828153ddf4256fe045b0d9a113182d264e63d22fce0cb3da510fa7735" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4189, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "271578cbd87f39cb7ddf4e396a017919b541999d4383d33cd4621a0bd814d71f" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "4868e3ba7b8291b742e1cd3481bb8c2060ecd1b4d0fede83ed39f289261460f6" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 3740, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "dbd35003a28df84f824605580990bbfdaa39df270cc8ef61364770bd48b289ec" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "webkit", + "durationMs": 2391, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b8de44afa3f643ca9c7a8083a6096a718e44ffd125958914c2c8b234a94ad225" + }, + { + "name": "danmuku-layout", + "sha256": "8a8918fd2f09945042d13bf9c715a4813997d009bd4c0fa08bf1d9b0e2481f56" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "fbdc7bc41af64a7b1b130316adb2da7a769880ffc5b789746c754a16d9e8564c" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "webkit", + "durationMs": 3026, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "752eb9e8858cab6b92b90196b36d19173bd99ddbb3f5d9cc5748aa540a226371" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "7fed96b1acb2ea2f1a5fa8d7a4a929559f5e8ca4844234345b99667d92f38a36" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "eacf9221e6c74e72355048438ab9ad85f92131a7a1fb4a6e2b7b0dd6d555c7f0" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "webkit", + "durationMs": 9318, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "b4ba84bcbe92b28af16e4bfc9ced00329a12bdf9e91e07036cfc4fd55cb033ee" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "240ccddd581b56658451c6f8ff97f0e8e280f4355764032c8b69364d72871c6b" + }, + { + "name": "danmuku-layout", + "sha256": "d632ba9cbdcce2509fe860bf9572476c91445a7f1c0020f259c7fd67b5833fac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "3dd4a7771c26bd2213c9a8d8ed624ec89a221983fc1e7397fec606fec431ef24" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "webkit", + "durationMs": 6812, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "a2adecb587fbd148a79c1b0e75315c3f2e9598fd45951039f8efd864672c1ba7" + }, + { + "name": "danmuku-layout", + "sha256": "7607a26afbaaa1a22d2f60af434946c0e1bac408f675dd27b62025b80d881cd1" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "555b9ba0952eb4a5579e0be6e3425bc3f8f192aa7bd9f6402bfd592cdf09821a" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + } + ] + }, + { + "format": "main", + "file": "refactor/.cache/danmuku06-accepted-main-browser.json", + "sha256": "aa70f23ec860609b6ded2862cd58e41d8db5b451a549156bfee0176f31d6617f", + "log": { + "file": "refactor/.cache/danmuku06-accepted-main-browser.log", + "sha256": "4273900bd98c5144dc5ef69ed21b6fd7f29a4299966f902129f1fa4bca8f6fd2" + }, + "stats": { + "startTime": "2026-09-13T13:57:39.047Z", + "duration": 156047.465, + "expected": 156, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "workers": 2, + "versions": { + "chromium": { + "version": "153.0.8010.12", + "platform": "win32" + }, + "firefox": { + "version": "155.0", + "platform": "win32" + }, + "webkit": { + "version": "26.6", + "platform": "win32" + } + }, + "tests": [ + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2392, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "d6a5f8fded2a4be45599459de87fb454bb67ba7a350a6bfc6653bd581d6fd1e9" + }, + { + "name": "browser-evidence", + "sha256": "9abfbfccccec4b8b62b0293ce24570222f152f51a8a552003e5962920ed266e7" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2543, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "268b12a395a053a918c7f2302178d7322d4c7b3e21312b832dedacbcf68e8a2b" + }, + { + "name": "browser-evidence", + "sha256": "9abfbfccccec4b8b62b0293ce24570222f152f51a8a552003e5962920ed266e7" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2357, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "f39ebcbd8759bc83ac55ef712a2d99f235da3a39d38d5e23b1696e64868f4429" + }, + { + "name": "browser-evidence", + "sha256": "9abfbfccccec4b8b62b0293ce24570222f152f51a8a552003e5962920ed266e7" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2470, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "5f31d71ec868b2cc2ff509258a3de4d589908f55becdcd365e530d1644b57c4d" + }, + { + "name": "browser-evidence", + "sha256": "0985be257051d444c5930670d01acc15e27b71036e9436311b7e3d904db57e1a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2484, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "f8180c893e1bba71a04ae1cdbcfe56a3c3a93d0fce2bfaee188d79d633ae5843" + }, + { + "name": "browser-evidence", + "sha256": "0985be257051d444c5930670d01acc15e27b71036e9436311b7e3d904db57e1a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2379, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "6c30bfb6cb96727a470c15679be95759f7a68d8037a04d5ceb150282e3bf00f1" + }, + { + "name": "browser-evidence", + "sha256": "0985be257051d444c5930670d01acc15e27b71036e9436311b7e3d904db57e1a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 3116, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "4fc7e8981daa01c803087e2d687eed74841f1cffdc089b8b8cad0d4bd1a40946" + }, + { + "name": "browser-evidence", + "sha256": "e2e51d88292c75949273ad25946c097e70ec8f6a01fbbd5b7c21cc18b4c2087a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 1960, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "fe1bc8348001794b33ad139d49f45a617bd265d16222925a1b71b71435dd5185" + }, + { + "name": "browser-evidence", + "sha256": "e2e51d88292c75949273ad25946c097e70ec8f6a01fbbd5b7c21cc18b4c2087a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 2285, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "20535ef0efa3a361230f3764a15c46c8b64b3838fcbea2f848a28a61e7af3e4a" + }, + { + "name": "browser-evidence", + "sha256": "e2e51d88292c75949273ad25946c097e70ec8f6a01fbbd5b7c21cc18b4c2087a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 3388, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "87d05f6221c9760641136ec53349e599562e7f1afaba97e50c90d6d492965424" + }, + { + "name": "browser-evidence", + "sha256": "aefd9f89aedfbc55f77b71c5c0177b4be0d29228feeb3990401b0865bc212f50" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 2486, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "08b33de09e59660faf2c4b20e94bcb50fc116b96fb5e71f545fd7c8bb43e3ad1" + }, + { + "name": "browser-evidence", + "sha256": "aefd9f89aedfbc55f77b71c5c0177b4be0d29228feeb3990401b0865bc212f50" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 2334, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "1dd72161ff7ed525b81287a0d3ae25db923fd1505fbf3583daac9329b2c55168" + }, + { + "name": "browser-evidence", + "sha256": "aefd9f89aedfbc55f77b71c5c0177b4be0d29228feeb3990401b0865bc212f50" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3604, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "c0813d98c36bfdf917b80a69bcedb6b1cf9ec35bdb09ded0ba43f4c24f0a44a2" + }, + { + "name": "browser-evidence", + "sha256": "740fb71cc79b564e1d35e1c6d0377bb4eee660310954d6ad7537ecee54debccd" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3459, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "c6dcac199a7aba74dacf1542074023046620d20d6015302a146dfbb74146f71f" + }, + { + "name": "browser-evidence", + "sha256": "740fb71cc79b564e1d35e1c6d0377bb4eee660310954d6ad7537ecee54debccd" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3574, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "f8a09e0058c7dc7240d41d9ef99d88c3c02ddaed8b531c2e226c03f93b7d3230" + }, + { + "name": "browser-evidence", + "sha256": "740fb71cc79b564e1d35e1c6d0377bb4eee660310954d6ad7537ecee54debccd" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3606, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "aa625449e3b34f0e6d4b57d3f9812422f4cf31b01ed40125d549d6f71463210e" + }, + { + "name": "browser-evidence", + "sha256": "c678b0b03f9aa3e0837bd195112862eb1b0435fea490ddda5b271bc744c77a9e" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3454, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "7d55ebf0120dd8054e1659117eed9a11cab305ae352d032e6d4eaf3e47342329" + }, + { + "name": "browser-evidence", + "sha256": "c678b0b03f9aa3e0837bd195112862eb1b0435fea490ddda5b271bc744c77a9e" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3813, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "f0702c3b69bc049e82bca344bc16833fd65b64b8ee980e0d1fb48eb47f3ab90b" + }, + { + "name": "browser-evidence", + "sha256": "c678b0b03f9aa3e0837bd195112862eb1b0435fea490ddda5b271bc744c77a9e" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (published)", + "engine": "chromium", + "durationMs": 261, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "54d59f4fdb2442c3e4fd61d81e56549925bb706fe219178c58504b9723b750fc" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "69f03e90159ee56e34645d2343c65e9f58fb8f2db60a5dcfdf1c87d2ace84686" + }, + { + "name": "browser-evidence", + "sha256": "fe97591c2ec718049bc4df5b1b2dff395cac175fb6a40cf2b873c0b93046399c" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (published)", + "engine": "chromium", + "durationMs": 275, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "54d59f4fdb2442c3e4fd61d81e56549925bb706fe219178c58504b9723b750fc" + }, + { + "name": "danmuku-native-parser", + "sha256": "de130501ba7b521adedd1b5474dbfa064012546c72051bdcd9f9d1294bcd365e" + }, + { + "name": "browser-evidence", + "sha256": "588ccbd1c2f62ad680992d8a83b4043b2701679538a0c91f34db764582018447" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (published)", + "engine": "chromium", + "durationMs": 261, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "54d59f4fdb2442c3e4fd61d81e56549925bb706fe219178c58504b9723b750fc" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "6d5938cd03d88028eb4985aa6937a3558a96de48b2ec6afc7f1144dfb841eb45" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (published)", + "engine": "chromium", + "durationMs": 247, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "54d59f4fdb2442c3e4fd61d81e56549925bb706fe219178c58504b9723b750fc" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "6d5938cd03d88028eb4985aa6937a3558a96de48b2ec6afc7f1144dfb841eb45" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (candidate)", + "engine": "chromium", + "durationMs": 304, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "118340bac48aec6f272b570077a4ed2c063ef3b6dd8313b4f5a1b8fe9229f0ab" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "d506723fc32f629c1a26bb0bc19a5979552b017e9c65dbe684cc5b5e10386ceb" + }, + { + "name": "browser-evidence", + "sha256": "17047a258ae97d9487d1dabb077a10bbe8aa9eb37da1b71e6e9bf812ed9dafe0" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (candidate)", + "engine": "chromium", + "durationMs": 280, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "118340bac48aec6f272b570077a4ed2c063ef3b6dd8313b4f5a1b8fe9229f0ab" + }, + { + "name": "danmuku-native-parser", + "sha256": "e7aaea2a84f716a5ef3b7c30e9aa02429fe44bed81aa9fad4adc15890e9409d3" + }, + { + "name": "browser-evidence", + "sha256": "8e2885bd8ad4c8c905f46496b6548bee9053c76286c2e5470334e853f99fc5b6" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (candidate)", + "engine": "chromium", + "durationMs": 264, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "118340bac48aec6f272b570077a4ed2c063ef3b6dd8313b4f5a1b8fe9229f0ab" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "73d86528d8480a4c7dea456216b5f1cb9e12b53cced6e3c98544021ce0cbee61" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (candidate)", + "engine": "chromium", + "durationMs": 274, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "118340bac48aec6f272b570077a4ed2c063ef3b6dd8313b4f5a1b8fe9229f0ab" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "73d86528d8480a4c7dea456216b5f1cb9e12b53cced6e3c98544021ce0cbee61" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (published)", + "engine": "firefox", + "durationMs": 441, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "54d59f4fdb2442c3e4fd61d81e56549925bb706fe219178c58504b9723b750fc" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "4a4dafb96ce26cac78c3be4273973afe3cecb92b699a2c73093c64eec6a9f866" + }, + { + "name": "browser-evidence", + "sha256": "c6ed5c2b14447cb634d85eb6d9378e5ae6ea0baa6d103c786a5c786e0f8bf6d1" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (published)", + "engine": "firefox", + "durationMs": 465, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "54d59f4fdb2442c3e4fd61d81e56549925bb706fe219178c58504b9723b750fc" + }, + { + "name": "danmuku-native-parser", + "sha256": "90c391646731ededb247da05764292a98ec951e6a1fa3caba9c5704b618049c7" + }, + { + "name": "browser-evidence", + "sha256": "d5097905dd4b15bd3deef86f14437f413c7651715bc40200527d427e6bf89786" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (published)", + "engine": "firefox", + "durationMs": 577, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "54d59f4fdb2442c3e4fd61d81e56549925bb706fe219178c58504b9723b750fc" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (published)", + "engine": "firefox", + "durationMs": 715, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "54d59f4fdb2442c3e4fd61d81e56549925bb706fe219178c58504b9723b750fc" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (candidate)", + "engine": "firefox", + "durationMs": 545, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "118340bac48aec6f272b570077a4ed2c063ef3b6dd8313b4f5a1b8fe9229f0ab" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "21a679281f47e025e45863162e39507baeb83cdd884b1c232a660133293f8e8a" + }, + { + "name": "browser-evidence", + "sha256": "dc3f7faf36619fdf354ad91eb7c1a62cefbfdb9e62d225c82183d1b172b40981" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (candidate)", + "engine": "firefox", + "durationMs": 595, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "118340bac48aec6f272b570077a4ed2c063ef3b6dd8313b4f5a1b8fe9229f0ab" + }, + { + "name": "danmuku-native-parser", + "sha256": "36e4282832c2b6841fd2469e4367e53969e6d3f3aa588d96a0c26dad0a189540" + }, + { + "name": "browser-evidence", + "sha256": "8823e4ebd37f9cb544be40068f89f493b772863ea73bfe1398d2567f837c1297" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (candidate)", + "engine": "firefox", + "durationMs": 447, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "118340bac48aec6f272b570077a4ed2c063ef3b6dd8313b4f5a1b8fe9229f0ab" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (candidate)", + "engine": "firefox", + "durationMs": 547, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "118340bac48aec6f272b570077a4ed2c063ef3b6dd8313b4f5a1b8fe9229f0ab" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (published)", + "engine": "webkit", + "durationMs": 1054, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "54d59f4fdb2442c3e4fd61d81e56549925bb706fe219178c58504b9723b750fc" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "1c042b1c4f9e6a7eb7c31ec7ee7049137dd8ea862c2d2a02d980862f2f03ba53" + }, + { + "name": "browser-evidence", + "sha256": "7903d4aa31399102ba58e481ecc5c176fa1e9bb3febb35031daa70fc0ab81547" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (published)", + "engine": "webkit", + "durationMs": 1137, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "54d59f4fdb2442c3e4fd61d81e56549925bb706fe219178c58504b9723b750fc" + }, + { + "name": "danmuku-native-parser", + "sha256": "2a337d72e50f9dc85b8fd8a52dab4eb5f7dd56028340c8a28aff4fb4227f6805" + }, + { + "name": "browser-evidence", + "sha256": "39b9993b7bd639340152bf8fcc8f9ce8ec1e6e6ff3f3516d81edec225d6901c9" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (published)", + "engine": "webkit", + "durationMs": 1113, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "54d59f4fdb2442c3e4fd61d81e56549925bb706fe219178c58504b9723b750fc" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (published)", + "engine": "webkit", + "durationMs": 1098, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "54d59f4fdb2442c3e4fd61d81e56549925bb706fe219178c58504b9723b750fc" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (candidate)", + "engine": "webkit", + "durationMs": 1227, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "118340bac48aec6f272b570077a4ed2c063ef3b6dd8313b4f5a1b8fe9229f0ab" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "d51c199fc637ee24f86228a05f75d25be8ef7d61fe74d05446a1e855839df98a" + }, + { + "name": "browser-evidence", + "sha256": "63453f9186c97bf9588dd540b6d821dfb82029573656e12d494683b3110d4716" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (candidate)", + "engine": "webkit", + "durationMs": 1262, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "118340bac48aec6f272b570077a4ed2c063ef3b6dd8313b4f5a1b8fe9229f0ab" + }, + { + "name": "danmuku-native-parser", + "sha256": "a5fdf5be0d331bbaebe01753249c6dc19d7390360fb5278f984d8b47c423962f" + }, + { + "name": "browser-evidence", + "sha256": "ef659c544a73dfe808900970bd58c03e19d2088add365af893775afca9f699c6" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (candidate)", + "engine": "webkit", + "durationMs": 1401, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "118340bac48aec6f272b570077a4ed2c063ef3b6dd8313b4f5a1b8fe9229f0ab" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (candidate)", + "engine": "webkit", + "durationMs": 1265, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "118340bac48aec6f272b570077a4ed2c063ef3b6dd8313b4f5a1b8fe9229f0ab" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: shared setting mount preserves the surviving instance and later user changes", + "engine": "chromium", + "durationMs": 293, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "96cc31e201ef60e560a9824e3be38eb34536b32db988f298d371093f72468365" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "chromium", + "durationMs": 349, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "7443bc0c9ae74105a1331def30a824533188b99262f227f0a5850c0a96fd7cae" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "chromium", + "durationMs": 400, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3fcc809d4c3e167d78c25ba4d1b59ade9f1c21983bef8a1bd20037f152e7b3e8" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "96cc31e201ef60e560a9824e3be38eb34536b32db988f298d371093f72468365" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy clears active send timer and leaves another mounted instance usable", + "engine": "chromium", + "durationMs": 447, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "7443bc0c9ae74105a1331def30a824533188b99262f227f0a5850c0a96fd7cae" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "chromium", + "durationMs": 385, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "bb3153c179326ff860535461cd01d3dfb74d40545e68d5b5e4e0b79b4bb47af1" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "1624840c81ebf08857ddbd3d3b59f1df9ffba31be88bdcdb021a9b1e24b42bc9" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "chromium", + "durationMs": 491, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "96cc31e201ef60e560a9824e3be38eb34536b32db988f298d371093f72468365" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "chromium", + "durationMs": 489, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "96cc31e201ef60e560a9824e3be38eb34536b32db988f298d371093f72468365" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "chromium", + "durationMs": 519, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a962a4e962825693f55a0ade3eaa242bf81e1fd1aa70fc6476f78ff5c7a033e3" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "7443bc0c9ae74105a1331def30a824533188b99262f227f0a5850c0a96fd7cae" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: shared setting mount preserves the surviving instance and later user changes", + "engine": "chromium", + "durationMs": 317, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "28265e57465d42c171f632b72c5cbf99ece1cfa4629f9feca1cd7e1223769cf0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "chromium", + "durationMs": 343, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "ea0c1c023cf2826f5be109522b0b0a87d6043b295b7eb2bf335aa6dedb315b46" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "chromium", + "durationMs": 388, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "944eaca9d81c072354f5fd553d3e119f7c3152f89ce6bae59be866f3da9571c5" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "326565edb6152c52b97bac465aeeed05d0abe7d1c63c8e3de4d2cb8a2535b781" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy clears active send timer and leaves another mounted instance usable", + "engine": "chromium", + "durationMs": 493, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "ea0c1c023cf2826f5be109522b0b0a87d6043b295b7eb2bf335aa6dedb315b46" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "chromium", + "durationMs": 407, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "90358b607b23375023c47ab11777a9f3dda565b215548ba6d39eb204182bfafc" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "69de9f174219607024fd0ea29e663a04b966d5e77d7a527845a75f8765a7324f" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "chromium", + "durationMs": 473, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "326565edb6152c52b97bac465aeeed05d0abe7d1c63c8e3de4d2cb8a2535b781" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "chromium", + "durationMs": 513, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "326565edb6152c52b97bac465aeeed05d0abe7d1c63c8e3de4d2cb8a2535b781" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "chromium", + "durationMs": 538, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "592a8dd0bf4bf0d0954ff36b962105592a6795bf573b5a5a1ee342004b1efd39" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "ea0c1c023cf2826f5be109522b0b0a87d6043b295b7eb2bf335aa6dedb315b46" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: shared setting mount preserves the surviving instance and later user changes", + "engine": "firefox", + "durationMs": 561, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "firefox", + "durationMs": 474, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "firefox", + "durationMs": 1398, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3fcc809d4c3e167d78c25ba4d1b59ade9f1c21983bef8a1bd20037f152e7b3e8" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy clears active send timer and leaves another mounted instance usable", + "engine": "firefox", + "durationMs": 921, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "firefox", + "durationMs": 684, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "bb3153c179326ff860535461cd01d3dfb74d40545e68d5b5e4e0b79b4bb47af1" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "a37e53602682ec1f0ad1eff163ef58dedffaa5658b8f55520b864f4b2a766e3e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "firefox", + "durationMs": 696, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "firefox", + "durationMs": 619, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "firefox", + "durationMs": 813, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a962a4e962825693f55a0ade3eaa242bf81e1fd1aa70fc6476f78ff5c7a033e3" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: shared setting mount preserves the surviving instance and later user changes", + "engine": "firefox", + "durationMs": 473, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "firefox", + "durationMs": 590, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "firefox", + "durationMs": 1553, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "944eaca9d81c072354f5fd553d3e119f7c3152f89ce6bae59be866f3da9571c5" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy clears active send timer and leaves another mounted instance usable", + "engine": "firefox", + "durationMs": 882, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "firefox", + "durationMs": 684, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "90358b607b23375023c47ab11777a9f3dda565b215548ba6d39eb204182bfafc" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "3304158b14c8094063d657cf3d57908559cb23ec3c7a9911fd19cbc57298035c" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "firefox", + "durationMs": 717, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "firefox", + "durationMs": 823, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "firefox", + "durationMs": 654, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "592a8dd0bf4bf0d0954ff36b962105592a6795bf573b5a5a1ee342004b1efd39" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: shared setting mount preserves the surviving instance and later user changes", + "engine": "webkit", + "durationMs": 1331, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "webkit", + "durationMs": 1428, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "webkit", + "durationMs": 1866, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3fcc809d4c3e167d78c25ba4d1b59ade9f1c21983bef8a1bd20037f152e7b3e8" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy clears active send timer and leaves another mounted instance usable", + "engine": "webkit", + "durationMs": 2455, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "webkit", + "durationMs": 1771, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "bb3153c179326ff860535461cd01d3dfb74d40545e68d5b5e4e0b79b4bb47af1" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "b4e1558640dd3365fa32b01258a3b1ebc8cc340d3d9ee679c6e0650dab2d806e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "webkit", + "durationMs": 1659, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "webkit", + "durationMs": 1869, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "8445276148db1a7796b3bafe46f2da8472f684e7739c17850704e2f70c2b3250" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "webkit", + "durationMs": 1966, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a962a4e962825693f55a0ade3eaa242bf81e1fd1aa70fc6476f78ff5c7a033e3" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: shared setting mount preserves the surviving instance and later user changes", + "engine": "webkit", + "durationMs": 1313, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "webkit", + "durationMs": 1337, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "webkit", + "durationMs": 1908, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "944eaca9d81c072354f5fd553d3e119f7c3152f89ce6bae59be866f3da9571c5" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy clears active send timer and leaves another mounted instance usable", + "engine": "webkit", + "durationMs": 2476, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "webkit", + "durationMs": 1686, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "90358b607b23375023c47ab11777a9f3dda565b215548ba6d39eb204182bfafc" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "ab222de8427a05b26737bbbf2d35daab8db7a2c04db2a7600b5a5428d2ae8931" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "webkit", + "durationMs": 1849, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "webkit", + "durationMs": 1796, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "a85e45c0ed5f3e8f9fc29bb14706311ccc5c88cc22201783cc10ab9243e7e602" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "webkit", + "durationMs": 1912, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "592a8dd0bf4bf0d0954ff36b962105592a6795bf573b5a5a1ee342004b1efd39" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "chromium", + "durationMs": 2300, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "b8ed3be6729e9a8981467ea10c0368b78a6d04d271be194b45bb871a7598f3c3" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "03c2e2552d0033b86f19bdee242cffad5ea9eb8f17fc0aea1d3dd47c0fa3d399" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "chromium", + "durationMs": 450, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "8af6f7e9aeb6fbf6b089c802434f44e86b495f464f257506d467250c84ab2597" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "b92902198913d42ca09c5a7ea4a0f3f9be31cfb988d21309945c9c7412034d7a" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "chromium", + "durationMs": 480, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "6985e558e0a871d2d9c5ff22f53a58a31b0e215c4e384c5ba040c3b181332e00" + }, + { + "name": "danmuku-layout", + "sha256": "af1a1f3b7213a08a9a4e302414a46151aac3d3a0b1f8654f1ab1c2305f4d0c0a" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "06e8963755ba0c5ed0c9231300bb37f0657cc15408a9332250e3196ec39ffa75" + }, + { + "name": "browser-evidence", + "sha256": "788cd2aa1d722d3fae8169c90dc925ee2ebedb65893a37aaccf897845ef9bed1" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2639, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "101766d41a02b6e99a7e1c432ba3429e469851304413b99f74b079c48ea27db8" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "e07cbc296a87ac5c676eeb52ae20e78eac92e73af74ae2cb534a571c55d98b11" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2459, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "6d20f29c9924ad2b766b5b2f3ee9b14435ea314ae76bc8cacdacca026e738d7c" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "8bab3597904c0cdfd71c403349cc4d22bb13722bddd789eba72e061147324eab" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2473, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "dc0dd513e7321d47fabc895a8e647e7650843e9eba09a2dc34165dfc08b94271" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "ba40df3c1e099d6479626b9e3e69bbea507a95dfdb477561075e97055a2a2e2b" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2320, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "07517cb990a57179e84c5b5d67ef47fb6f35470e14c5ceb68a0174aae568e4d0" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "chromium", + "durationMs": 1346, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-layout", + "sha256": "d4a692fe79061643666cde1da90cc9c6e2f0aaa71461f7e14f339900e55c83ea" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "644389ff54ca1a243b9830ae3d94723be40c193c1623abdabee316371839df53" + }, + { + "name": "browser-evidence", + "sha256": "788cd2aa1d722d3fae8169c90dc925ee2ebedb65893a37aaccf897845ef9bed1" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "chromium", + "durationMs": 1449, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "be69c7c5a44d09e3d916d6be03014f85570f8882ab216de681aba7a8dab45ee0" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "b1ba7348169201454b9f0285bc8eecd5644fc5d41ede4d176fa7f963db04fb96" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "720f91a73b1aeee02c5cd6402d8f4cf436b09fefaf1a4e918892b5f6e220d5ff" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "chromium", + "durationMs": 2327, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "d3ed0dd9bad1b9ae5c1c397076c69892b508d783644a5e275000e75802fef922" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "34a3af0c7a571f01e9ac4cb090a4077031d1d699ad76c5a50b74e158be4f2c7f" + }, + { + "name": "danmuku-layout", + "sha256": "69e4e8a7506f223fffe21ffa49c384d2b12424eeff8b6e3183f48c540bb94c4d" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "50845b2497d89c37e9d5b8a141fbf09c06ab4e85f3bf4714ee7883869ce9bd04" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "chromium", + "durationMs": 4036, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "61e968921688f9008de10229d6e27343c2f539264e566380e1ff3ab0b8e34ebc" + }, + { + "name": "danmuku-layout", + "sha256": "f50044093da8aa075b51591d7f77b07d23ef8be5720c52b0a8023a497fce3e41" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "3f5b5c0572ebcaf1106deb0e18df79174ca7b6648ab35c99623c1cddddf61c25" + }, + { + "name": "browser-evidence", + "sha256": "7c9f4a21af0011c3d15c3c352a7ff53739541aa6f6c3e93348d977271bc236f7" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "chromium", + "durationMs": 2304, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "766c773c698fedd145d9e1a3210e0866d66b5896d782b7e96ca46ed266c880ab" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "5941d2c582d776d52a64e80d630adf409d7db7626ed134a11fe8db3fbe17db3d" + }, + { + "name": "browser-evidence", + "sha256": "9dd6e666e6f908e2a5bcbedda78132ab3713311a939892280dd9dc3823d724d4" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "chromium", + "durationMs": 489, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "d96d5e6c759f0f7a4f9b6d95b183310d35c8714a7a31437a83e5b4f9d24547d4" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "228763997dbb885b7030bd5f7d41fec3c99a158ddce7ecf1465e79facaeb9640" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "chromium", + "durationMs": 491, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "64793c775bb38d50180b3049b8b23a6a1bc751e4411f8acc1b4b7d9b62f12cf1" + }, + { + "name": "danmuku-layout", + "sha256": "eea136f3f18bbe096be28a9773a2ac7c903e25a9a7925312eab2c1ed652534e5" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "e996b4dec3e07ef7ec15491c3913ac302f7e165cd7d0cea9712ff2483afe5450" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2663, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "49b76dff47dbe0940cfb5eeac642f013f8189c38d9cea8d19edee1c50fa96b04" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "6f64c5e132eee755db4caa9a0a71714fc54549ead262de2f365bd4692cb1f2e8" + }, + { + "name": "browser-evidence", + "sha256": "9dd6e666e6f908e2a5bcbedda78132ab3713311a939892280dd9dc3823d724d4" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2507, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "71af878cfa7df8d907ef606e90377bba2f7f6412e056c6e3f63b770dcadec07c" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "8a6ebb670fedf3c61d1c4d71e73eefb4752d4898ee9ce3cf18cdc2b1bc899a7b" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2400, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "f9dc828d55caa3af734fd80d72249f1331b7fe79e9164e40eac2b16ab83c1184" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "f26ec4bed47ee61481c9b9dc34e6ed0e0d6326b05cf82c7817ea02cd708e9824" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2333, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "6a138cce772170e2ce65a85ad4903927086179ad7e3ba8340ae6bf6dad3d3849" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "chromium", + "durationMs": 1375, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-layout", + "sha256": "81fccc296f9b34c031011714dbe8cbee994403250ad6b4e31e92017e55f1a5b9" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "acc58e92d819bb3deb9c3300c021dfe08f0ce38180f983ca4a6ca56ce8d7bc02" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "chromium", + "durationMs": 1483, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "a728d527a59889ea5e9a7b0da79ffc20e284cddd8ff894464046e5125078cff6" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "3d8c7698908b2394997ee548f2addde204542c0f81d36788c80ae2769fee8330" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "b0f45149b42a6a0eca720e65e11161dbb6fea106139a5a627fb3a136d889323c" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "chromium", + "durationMs": 2376, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "bab7cbd67624063726955401fced2d5935672e11b84b6fc2718deb5646405e1e" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "550a028e5dd7dcda04f041ec4cb6f029e5b43720baf9bac30a4b30f50fc28586" + }, + { + "name": "danmuku-layout", + "sha256": "d4803622a449bf798adfcd92142989ce06cbaeec659ff0f40d04bc0fcdc4648f" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "caf74da296e8c118c2211cd7cc20e0cf2ccc6f6a8a2ca16758ae5f8ee8efaaf3" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "chromium", + "durationMs": 4141, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "048d42bfeaf6ae8aef03481838e795c695ff76ac41676702a5e1ed3e30d8ac8c" + }, + { + "name": "danmuku-layout", + "sha256": "39f42d5a89ccadfdad29f2e1566df1ad6da3519fc83bffe47171033489e0e889" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "ebc8669e630f1424c4438e3e4a1875b4baacbd42d75cf73bd1cf586fc285ec68" + }, + { + "name": "browser-evidence", + "sha256": "71730b2e97b3f5935a1eca46fbd686ce72103fe9230088797dccd9e13d9c206e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "firefox", + "durationMs": 2552, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "21ced1da9c91883d6451166351ddd6fcbae8a6cbdf2e9d2d05dcc7307667412b" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "763bddee0b3729937f4e83ccdf06fa90180693092d7de4d045a4724cea28337c" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "firefox", + "durationMs": 758, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "021c1c7ca229918bf38201073af64726241a3c142b268af7b1803649c98f9d67" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "70c3b32117650fe52d44f78944694ae1042b68c5df19b16578717f04b5846fc4" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "firefox", + "durationMs": 691, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "bb1fc4cde38fd8e78417270dc2a5d3b8652b79003f50f0ffeb1d2418689fd6d4" + }, + { + "name": "danmuku-layout", + "sha256": "0870b9a897cf12911a49d41c7414bdcd67b04c2a917f3fa3f89ea6ce56fb7c13" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "cd11a3b23c188ab3eca4821951ead07d16e03d7e823732c9e213c34ba4e2c938" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2989, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "494a830a25e31bee7e355e2b6fc1de9cc21b35122939a77181a7c3551e445919" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "2db817e2969a3f13692681ccb63de4d3bbedac68641aae25808dd51828759d50" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 3205, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "da7090edd59b9dce2f58a8f93dd5f93d26f2dcc3bdc94165b6fb9ee84047ca82" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "e0a2a1d54ad10e423ee7ecbb6ab2a53543a773fc11e9f656e8ff17d5da4611f9" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2813, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "e28cc30af6ef3ef2923d9c208dcc2c6c00089cc791ad631e0553cd70450276ef" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "8994b9ab861129374fbd36c0bbbf770961c86e2d66e2b346778badf2c00b7f6d" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2469, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "2e9f36832612a09eee199e507f1a14e56429ce4dac3c2e8afcd5980b6fb35b7c" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "firefox", + "durationMs": 1619, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-layout", + "sha256": "edc037d9657428cc6c7eadb874ac793f314b972198e7be2abb1f6eb57e863af2" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "7048d0628200c43e705d6ebfd578a0bb70a60b882e24c74afca94cca33f1a36e" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "firefox", + "durationMs": 1566, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "be69c7c5a44d09e3d916d6be03014f85570f8882ab216de681aba7a8dab45ee0" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "453650b4ed4a2fa62011a19ef752cab5b3c74096eac40dca8145379e25ffa36a" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "91dea7126c9103024ece40c2306db5da944577a884d76cd421c6dc869b908d5b" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "firefox", + "durationMs": 2531, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "d3ed0dd9bad1b9ae5c1c397076c69892b508d783644a5e275000e75802fef922" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "fadd068fced67fe52eed938de49358f5f8492d8348618476eddf579cce1febd7" + }, + { + "name": "danmuku-layout", + "sha256": "3d3034a273c33c30d5e5a66df3020bd449e0f765fdd631a0ba59966570d18927" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "cc74828be8b37a1b0313e89b82adcec458c03f1796193ef900e3f67217337cea" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "firefox", + "durationMs": 4607, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "61e968921688f9008de10229d6e27343c2f539264e566380e1ff3ab0b8e34ebc" + }, + { + "name": "danmuku-layout", + "sha256": "166c27346cbe3d41cb9daa55ffaea060979abf9a5b5b6f99a6b586ca45ab823e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "17d6e7d2ba7d54f18bda688c74ed9d3b660a88de717da31cacaa7b27a46d2816" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "firefox", + "durationMs": 2526, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "318dc25f102e40047d3311beb3fc8a8236b2bf5648f9dd91b1c5f9546924f048" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "6edce1770f748ec3548d7b30d0ec1349e1e3e778f2777d9cc2357d2d75afe0f5" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "firefox", + "durationMs": 803, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "6c45c6108654bd5566483f41af5f2eb7c5866c2c97b615b1e014900588b98215" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "ba35f5b690142894820bef5824f1d9e8a688aa53d8c2657bc3944137de47233c" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "firefox", + "durationMs": 789, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "0e5f61eb7dd048f0817d70b8907cf2abb5e6730f39460245595afb7270739b1a" + }, + { + "name": "danmuku-layout", + "sha256": "3758ba0976fd0c20eddf3450b4ec591d4f443c1e57788ad050495f130343de3e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "65b5669ea309436855dbcee9089ba39fc2fc0dad24aba2014f765353a88556e1" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2929, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "7416e6e4baa7e249e28d4f931bc823525186a283330aab39ac88c4d84637c06a" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "6bbb0847b3d7472fad4025738672e892be87aa75791627c14920737eb096a27a" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2972, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "f08e4a5957a52d33e85f6a8af721ce0dcbb411eba1d8da446224ed3c2a6dd95e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "fc2f820f1e5ddd019d18b8a09bcf2fee49c829e07cc00bfcd43bc0c1f29cb71e" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2729, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "cde8eeea4fa8a83eefd6af0c7d47bdc072e94e90f7694ba77f63d42c965d96ef" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "4df6cb0b6a4c6d6e9a706263504735911bb5b5d85cde4f2cc96cc19e88a27930" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2684, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "6e6592f0e3db5c51767cf5b86f0cebba021f1b1b9769042b306ba138652b92ba" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "firefox", + "durationMs": 1535, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-layout", + "sha256": "b2e0bcdcb941b884e7ef30e74482f43555b9b227740492a9020023fb6ec7024e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "5ae89b88eee89164fcc3e2966fb4dfedcae9dcb4d708cc447a34c3de579f6b07" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "firefox", + "durationMs": 1588, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "a728d527a59889ea5e9a7b0da79ffc20e284cddd8ff894464046e5125078cff6" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "2840182ee639acbd374cf822daa0f37c345293213d2dba93db9db2592d55fabd" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "a05e18fbe7e6e3caf4d95f92af0c56c4e1d18cfc951b5135d5b891c6c573cdfa" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "firefox", + "durationMs": 2676, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "bab7cbd67624063726955401fced2d5935672e11b84b6fc2718deb5646405e1e" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "ff5837f0ddbf2afc0fa3367e7e78c0d67e148b56029aa1bfc41bd46398013504" + }, + { + "name": "danmuku-layout", + "sha256": "f10fc6020b11126ff5c631ec80ead6c1fe5934aa58222fcfcccdefb81f7fb093" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "689d2cd0291caadfe2668094c56fb94f8a5489ec069adf289ac05aec2eb37190" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "firefox", + "durationMs": 4670, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "048d42bfeaf6ae8aef03481838e795c695ff76ac41676702a5e1ed3e30d8ac8c" + }, + { + "name": "danmuku-layout", + "sha256": "abf22868c7cd38536ca73d24559c264801ad7d54d3740d065190dbc5355af1db" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "850058f5d81e9a19f274d274053908a5e321efe2d87101d31c32c185e3e67854" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "webkit", + "durationMs": 3624, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "9e7193d14663aa1b7fe1a386db86e0bed5fbccf05ce52cd04faa40093702afa3" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "c0738dcd599bfda624e2362036ad6e05c594e3b0b12b2029ecfb9107bc292a9c" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "webkit", + "durationMs": 1606, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "b0db2802cb67776dbc4cb3fbb5e948fc14aa9190f320eb164900c4f016e26b54" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "b6f47190831d5f7e21cd9ea9b25caa54d8a77ea1241b06f5acb4f5b53aaf2343" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "webkit", + "durationMs": 1754, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "2dac5a7ade3b88c29f18c165415977b5901bc80229f80cbe62b253050e773dc6" + }, + { + "name": "danmuku-layout", + "sha256": "f56e3b37969f33abdbf16d1c7c0cf101d7356b9a084fda8cdc3642a643e96d2a" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "dfbccb3ed94faae3be807a40d969a63abc2f2990da99fb8c8de09362382737c9" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4848, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "f07ecd67eaeef23ba4f6e8e0842e9cb5ba6de55a3806575540abd8838f8c643e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "6e7d008ca444dcd25ce0d7fa43fcda5593b582df434d2da8d9c226eab4c4126e" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 3980, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "10cf185bb65742abd9ce52721e04b747b858589917c73a3d5ea92942890a6274" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "79f43704964643bb95d71886b1805d3ca78d95c3d407b987411a4a9b5949a56f" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4450, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "e11ff2c60ec0280607f025be288462edfc41dd58a76657a3b59eea421a72595d" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "64c8f0a05837f3851b334e48bff86b172d70221d95b8cdad8960a05410af3cbb" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 3874, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "855cc7547bb04e1ebc012b76be4b6765c62db80f3d74a47391e0a783696d0dd6" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "webkit", + "durationMs": 2287, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "88bc6607b082b99cb98d1ea78cf41d1b1879e42612a87546e411ceb952964ce0" + }, + { + "name": "danmuku-layout", + "sha256": "b1ef30b4b18044f6225dc86e09c7d5a8a651ff835d37692fc12b8d5fd5f20a2d" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "f7723dcd9b6b6ec7d980731e3cc0578925ec7e062c69341c9e58695d21669635" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "webkit", + "durationMs": 3461, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "be69c7c5a44d09e3d916d6be03014f85570f8882ab216de681aba7a8dab45ee0" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "780f513df75db8ab2527e21c650fa7f6da52b10929d62e7762fbc8a529db2c9c" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "ae2a56ef76f3fed362fe2f8bef98609b688579e149c19eccf41325254a2efa78" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "webkit", + "durationMs": 4691, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "d3ed0dd9bad1b9ae5c1c397076c69892b508d783644a5e275000e75802fef922" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "951b6340b8efd4983ba217d2772d1f66e51d53dd3d94f02964629bc5c9abc660" + }, + { + "name": "danmuku-layout", + "sha256": "81587529c55657435009190d793da78b2d48b52012c9d82649f184d209ab3a8e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "b25a3b81fd44d8125e591793c5321e0e4aff73dbbf2d5f91a288a08d2361820a" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "webkit", + "durationMs": 6886, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "61e968921688f9008de10229d6e27343c2f539264e566380e1ff3ab0b8e34ebc" + }, + { + "name": "danmuku-layout", + "sha256": "42a8e0acc48af1cd555612b5bfed18189fb12c69cdd076cee3c2784b646c61fe" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "c424365f2192573540da7a9773e64db5e3e38df573549ffba362a95b2312bd52" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "webkit", + "durationMs": 3681, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "7c4eaa121c579ea79a4615a023ed31c600e798f9bb58ec91d67e27ae5090f961" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "155429ae4b03966a2e0317ea52edd5de2c94f104fea383d493abb7bf32b59b45" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "webkit", + "durationMs": 1652, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "cecc5828c1d415e003a146523bf958535614796bf9868854b6802c4ce3e7c130" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "96808dfd898bc1a9189f10f0a810fb04b29e8518dc1b360a1594910827308f52" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "webkit", + "durationMs": 1844, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "d199e01c894ddc6e0156e4f1f7cf04f35702f98bb96a42a9cc3d905f42b942e3" + }, + { + "name": "danmuku-layout", + "sha256": "8b48c7c2b0fa12a2f48f7381f37e4fd1d7f36caa67b70060313820b3b27a633e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "3c00d3ceb648fa897e1b751af8a0c36b22f43f6f1bdd86d93e9f880b437a17e3" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4554, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "0a24925e78c01508fef10e21869763986c657d4c390d7efb9e7f52128f11fe39" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "6f0f2c9bb3a2c0f75b09bb1a7dbce6762fdbc147b5fbe28dd931fb3833743050" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 3821, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "60bb3f87a01b7eacfdd93b79cc5abb4035ed6971a718936cea590d3b4e7d0305" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "a8022bcf4c937e151357c52d1b6e997c75e5478879c32c7168e40741e2b85c93" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4364, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "cff08dc87f5fec7957e6bcb7edcb4d0a63d6e401c2a656f5e03fd875d2b3aeb8" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "ff6e298aeaf634d7a16d95328494f76b10722d92aba9ef376efcea457ab48d3c" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 3705, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "39193ff5e1d28c686ba95b6f094a6530ef4892058796275c08126289e107e6a7" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "webkit", + "durationMs": 3173, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "2a4a4568509affd5160fda5de6a97076ef92c272171f4ad288866deb1756bc33" + }, + { + "name": "danmuku-layout", + "sha256": "d603b1402191e2a608bbe7a3aa8af07b1113ad1e04dc2ba3f95a60705096a64e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "0fd111e303e26fd1238d074a02b12ac9711602acd775a89b77ca6675daefe560" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "webkit", + "durationMs": 3146, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "a728d527a59889ea5e9a7b0da79ffc20e284cddd8ff894464046e5125078cff6" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "d70c16712c6150c913258729d64c282c2a90c13ef2ba27068a1968a809d35b7e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "c010b995129865a0078480c47c1da7cae71ebb6281f526d65c01c63da959a1f7" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "webkit", + "durationMs": 4263, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "bab7cbd67624063726955401fced2d5935672e11b84b6fc2718deb5646405e1e" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "2582aeb2fcc3691162920d40aae39d698a72bdf387714bfb119c292a0349302a" + }, + { + "name": "danmuku-layout", + "sha256": "a356ec7862712f45981c5e587bfeb71b2f3adc453024cd7c66bc8a8909840057" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "ed285bb21f7d12aa4c80eb58918ae12d3dca715b1ec5194aeff7181ac0b28189" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "webkit", + "durationMs": 6737, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "048d42bfeaf6ae8aef03481838e795c695ff76ac41676702a5e1ed3e30d8ac8c" + }, + { + "name": "danmuku-layout", + "sha256": "d744ac60a0c23fc3fbc85d8f4d1a4c04df94db39ba502485c53bee5cb65181d1" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "bf8c1287e088e5c6a0d4660b2d2d9f8a44957c8ccde4458b1b117873bf607d5c" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + } + ] + }, + { + "format": "legacy", + "file": "refactor/.cache/danmuku06-accepted-legacy-browser.json", + "sha256": "2c79c2a411b084c74bfd83ca42ffd2e36d26f1b35ce4b1f6f28ee9910f1434cc", + "log": { + "file": "refactor/.cache/danmuku06-accepted-legacy-browser.log", + "sha256": "00c1214aaffb282542c49211cce6442306fe75cb9cc397a746366daad7d2427d" + }, + "stats": { + "startTime": "2026-09-13T14:00:16.133Z", + "duration": 155236.379, + "expected": 156, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "workers": 2, + "versions": { + "chromium": { + "version": "153.0.8010.12", + "platform": "win32" + }, + "firefox": { + "version": "155.0", + "platform": "win32" + }, + "webkit": { + "version": "26.6", + "platform": "win32" + } + }, + "tests": [ + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2416, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "bc5e3ad219871dc44afe296ae9744fb5294abdc149fe90b409b10a79fe23b613" + }, + { + "name": "browser-evidence", + "sha256": "9abfbfccccec4b8b62b0293ce24570222f152f51a8a552003e5962920ed266e7" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2271, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "ae364f24b120c6104599bbc8d86b5f4b46564f6db721e3aafeb90e0a6cd516eb" + }, + { + "name": "browser-evidence", + "sha256": "9abfbfccccec4b8b62b0293ce24570222f152f51a8a552003e5962920ed266e7" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2288, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "4e87ac803a1159b75f2eea4b2957b04b464fe03bcfdd9766c05758ac6f48d517" + }, + { + "name": "browser-evidence", + "sha256": "9abfbfccccec4b8b62b0293ce24570222f152f51a8a552003e5962920ed266e7" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2449, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "7ab4336ece8f4f9f530ad875ba0c386911f8c4ff598de7e6889c8d5929c5ce41" + }, + { + "name": "browser-evidence", + "sha256": "0985be257051d444c5930670d01acc15e27b71036e9436311b7e3d904db57e1a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2305, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "bdcb594ca02bb8e46d1cb1215f8e3ae44600c4745a8ae18d6bde355589094868" + }, + { + "name": "browser-evidence", + "sha256": "0985be257051d444c5930670d01acc15e27b71036e9436311b7e3d904db57e1a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "chromium", + "durationMs": 2271, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "c48b0d0055e27f1d158e65b3029ef7468cae7a509f6eb73ead1f33cc3ff93fba" + }, + { + "name": "browser-evidence", + "sha256": "0985be257051d444c5930670d01acc15e27b71036e9436311b7e3d904db57e1a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 3309, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "3b256bac77fffee8ae83fddddfa7ee61c7af9e4ab1d6c1c86dde229965dc61e6" + }, + { + "name": "browser-evidence", + "sha256": "e2e51d88292c75949273ad25946c097e70ec8f6a01fbbd5b7c21cc18b4c2087a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 2069, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "7b2d4e7da501069c9db0f2f4075af026cf6a980b15722cd4d2c837b48ff3c4df" + }, + { + "name": "browser-evidence", + "sha256": "e2e51d88292c75949273ad25946c097e70ec8f6a01fbbd5b7c21cc18b4c2087a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 2250, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "8f04ec0f82b539d6d644e1d3598a4d9c2cf436b3a9d5191ae361db7c0777f913" + }, + { + "name": "browser-evidence", + "sha256": "e2e51d88292c75949273ad25946c097e70ec8f6a01fbbd5b7c21cc18b4c2087a" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 3415, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "72fc97e86df45d1bdcfe942f941f9e078a63a73432e2c868779a20c449db9e57" + }, + { + "name": "browser-evidence", + "sha256": "aefd9f89aedfbc55f77b71c5c0177b4be0d29228feeb3990401b0865bc212f50" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 2346, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "8cc2e368518b606948fe045f5f96e1e9438bf7c126a7685b5f3e8a29d00557b0" + }, + { + "name": "browser-evidence", + "sha256": "aefd9f89aedfbc55f77b71c5c0177b4be0d29228feeb3990401b0865bc212f50" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "firefox", + "durationMs": 2381, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "012595097d45d971b2da4a4e1b7be5796dc891ac4ec70b8d2d5c526c6ecf72d2" + }, + { + "name": "browser-evidence", + "sha256": "aefd9f89aedfbc55f77b71c5c0177b4be0d29228feeb3990401b0865bc212f50" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 4037, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "003b7a0efa1cab54e50a4553fd460f950211d4b8c0649bd1bb14eef88134bd2b" + }, + { + "name": "browser-evidence", + "sha256": "740fb71cc79b564e1d35e1c6d0377bb4eee660310954d6ad7537ecee54debccd" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3551, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "6d64991ac29e8d38ce369ca3d70fafc3b01875378c5349fd979cff1f55725c3a" + }, + { + "name": "browser-evidence", + "sha256": "740fb71cc79b564e1d35e1c6d0377bb4eee660310954d6ad7537ecee54debccd" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "published: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3918, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "a5ffc0f4ee4e13817feaa26358e6ccb5fae8dfff63f59888edc93c8818f249df" + }, + { + "name": "browser-evidence", + "sha256": "740fb71cc79b564e1d35e1c6d0377bb4eee660310954d6ad7537ecee54debccd" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 uniform 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3965, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "d042664fc682c99eb705bd2e3dfbdddc28950d553acd77d1ba59fb6ec571d35f" + }, + { + "name": "browser-evidence", + "sha256": "c678b0b03f9aa3e0837bd195112862eb1b0435fea490ddda5b271bc744c77a9e" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 clustered 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3624, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "4ca83ec6f65c00504f84bb626ec40fd54020b2682a19b743d14c90eeb1023e8b" + }, + { + "name": "browser-evidence", + "sha256": "c678b0b03f9aa3e0837bd195112862eb1b0435fea490ddda5b271bc744c77a9e" + } + ] + }, + { + "file": "danmuku-heatmap-density.spec.js", + "title": "candidate: #958 mixed 16000-row heatmap remains below 25px during playback and resize", + "engine": "webkit", + "durationMs": 3711, + "evidence": [ + { + "name": "heatmap-density-evidence", + "sha256": "f83288abf83663d5ac3a1b699d74ac43cdf88b431cb131c7f5348004ddd67d97" + }, + { + "name": "browser-evidence", + "sha256": "c678b0b03f9aa3e0837bd195112862eb1b0435fea490ddda5b271bc744c77a9e" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (published)", + "engine": "chromium", + "durationMs": 260, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "6b5e13b8a871f6d88386b654b22390c0c673a4a4d70ed87e84c38fead9cea80f" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "831d3c0cca12d9831655698e3cad1b89e642c41cab9847d12554b37602514752" + }, + { + "name": "browser-evidence", + "sha256": "fe97591c2ec718049bc4df5b1b2dff395cac175fb6a40cf2b873c0b93046399c" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (published)", + "engine": "chromium", + "durationMs": 309, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "6b5e13b8a871f6d88386b654b22390c0c673a4a4d70ed87e84c38fead9cea80f" + }, + { + "name": "danmuku-native-parser", + "sha256": "7094db992f215fa8cd351920dc143afc2e88e007fede11db22e6ada60279cf56" + }, + { + "name": "browser-evidence", + "sha256": "588ccbd1c2f62ad680992d8a83b4043b2701679538a0c91f34db764582018447" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (published)", + "engine": "chromium", + "durationMs": 254, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "6b5e13b8a871f6d88386b654b22390c0c673a4a4d70ed87e84c38fead9cea80f" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "ddf13c135b2f8461c36fea0c0bf98d5ca924bda36580231757ea7fc9722144d2" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (published)", + "engine": "chromium", + "durationMs": 241, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "6b5e13b8a871f6d88386b654b22390c0c673a4a4d70ed87e84c38fead9cea80f" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "6d5938cd03d88028eb4985aa6937a3558a96de48b2ec6afc7f1144dfb841eb45" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (candidate)", + "engine": "chromium", + "durationMs": 277, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "8dc4f77c3fea9a9f3ee26ca9c57a28626a1489533cfcd98ff12ed2a27da5cf10" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "7fb65bd494144d99892850bb8461ecd05ebbdf954b7f2e4eb328ab4304c64f47" + }, + { + "name": "browser-evidence", + "sha256": "17047a258ae97d9487d1dabb077a10bbe8aa9eb37da1b71e6e9bf812ed9dafe0" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (candidate)", + "engine": "chromium", + "durationMs": 306, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "8dc4f77c3fea9a9f3ee26ca9c57a28626a1489533cfcd98ff12ed2a27da5cf10" + }, + { + "name": "danmuku-native-parser", + "sha256": "6a0dd0d3f918f0bbbc67d6fd99b08853bd8e855b4951ed0599c9384e7ab30e57" + }, + { + "name": "browser-evidence", + "sha256": "8e2885bd8ad4c8c905f46496b6548bee9053c76286c2e5470334e853f99fc5b6" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (candidate)", + "engine": "chromium", + "durationMs": 284, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "8dc4f77c3fea9a9f3ee26ca9c57a28626a1489533cfcd98ff12ed2a27da5cf10" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "73d86528d8480a4c7dea456216b5f1cb9e12b53cced6e3c98544021ce0cbee61" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (candidate)", + "engine": "chromium", + "durationMs": 243, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "8dc4f77c3fea9a9f3ee26ca9c57a28626a1489533cfcd98ff12ed2a27da5cf10" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "73d86528d8480a4c7dea456216b5f1cb9e12b53cced6e3c98544021ce0cbee61" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (published)", + "engine": "firefox", + "durationMs": 516, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "6b5e13b8a871f6d88386b654b22390c0c673a4a4d70ed87e84c38fead9cea80f" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "869f62aa503565cc83405cfd4607179aac9568afab4c5d2cce540ffd50da0559" + }, + { + "name": "browser-evidence", + "sha256": "c6ed5c2b14447cb634d85eb6d9378e5ae6ea0baa6d103c786a5c786e0f8bf6d1" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (published)", + "engine": "firefox", + "durationMs": 551, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "6b5e13b8a871f6d88386b654b22390c0c673a4a4d70ed87e84c38fead9cea80f" + }, + { + "name": "danmuku-native-parser", + "sha256": "528a94e5a157c424ea88d1543c2c4fa8fcf8638c2d1e4795b5a75c06e6f114a4" + }, + { + "name": "browser-evidence", + "sha256": "d5097905dd4b15bd3deef86f14437f413c7651715bc40200527d427e6bf89786" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (published)", + "engine": "firefox", + "durationMs": 468, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "6b5e13b8a871f6d88386b654b22390c0c673a4a4d70ed87e84c38fead9cea80f" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (published)", + "engine": "firefox", + "durationMs": 471, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "6b5e13b8a871f6d88386b654b22390c0c673a4a4d70ed87e84c38fead9cea80f" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (candidate)", + "engine": "firefox", + "durationMs": 557, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "8dc4f77c3fea9a9f3ee26ca9c57a28626a1489533cfcd98ff12ed2a27da5cf10" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "317fa08dadc0e8adb4e34fdeca346cd896b607ddbfe0b36c33e54bb281757819" + }, + { + "name": "browser-evidence", + "sha256": "dc3f7faf36619fdf354ad91eb7c1a62cefbfdb9e62d225c82183d1b172b40981" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (candidate)", + "engine": "firefox", + "durationMs": 612, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "8dc4f77c3fea9a9f3ee26ca9c57a28626a1489533cfcd98ff12ed2a27da5cf10" + }, + { + "name": "danmuku-native-parser", + "sha256": "7b27006a6239556f4b994451ca133bafce61146b31e9bed0673be49ce534c2ba" + }, + { + "name": "browser-evidence", + "sha256": "8823e4ebd37f9cb544be40068f89f493b772863ea73bfe1398d2567f837c1297" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (candidate)", + "engine": "firefox", + "durationMs": 461, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "8dc4f77c3fea9a9f3ee26ca9c57a28626a1489533cfcd98ff12ed2a27da5cf10" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (candidate)", + "engine": "firefox", + "durationMs": 442, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "8dc4f77c3fea9a9f3ee26ca9c57a28626a1489533cfcd98ff12ed2a27da5cf10" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (published)", + "engine": "webkit", + "durationMs": 1068, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "6b5e13b8a871f6d88386b654b22390c0c673a4a4d70ed87e84c38fead9cea80f" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "5f242f2ac6b332892b4482f49c2c67e6c04b2da63d743497f01cca4a96bf4e48" + }, + { + "name": "browser-evidence", + "sha256": "fb61013c4668026191d7c5f3509118e1ff15b22f5cae85d74785f3789f6b5b1e" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (published)", + "engine": "webkit", + "durationMs": 1251, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "6b5e13b8a871f6d88386b654b22390c0c673a4a4d70ed87e84c38fead9cea80f" + }, + { + "name": "danmuku-native-parser", + "sha256": "930a2e093e1a5a75685cebadb4cdc356b140f5a6b72ed3844a1f5d3e042ff25a" + }, + { + "name": "browser-evidence", + "sha256": "39b9993b7bd639340152bf8fcc8f9ce8ec1e6e6ff3f3516d81edec225d6901c9" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (published)", + "engine": "webkit", + "durationMs": 995, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "6b5e13b8a871f6d88386b654b22390c0c673a4a4d70ed87e84c38fead9cea80f" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (published)", + "engine": "webkit", + "durationMs": 1018, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "6b5e13b8a871f6d88386b654b22390c0c673a4a4d70ed87e84c38fead9cea80f" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native Worker startup failure falls back locally (candidate)", + "engine": "webkit", + "durationMs": 1155, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "8dc4f77c3fea9a9f3ee26ca9c57a28626a1489533cfcd98ff12ed2a27da5cf10" + }, + { + "name": "danmuku-native-worker-fault", + "sha256": "f425584b4575ce0c71a8f7b71a20d7120cdd4c9bbdea62039aad4a81672fa267" + }, + { + "name": "browser-evidence", + "sha256": "9b7a8944513ab45a1e6c6e0bab2e0f37bc9dc69e056798ecbad536eb1b484d39" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input native parser cleanup and rejected fetch (candidate)", + "engine": "webkit", + "durationMs": 1210, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "8dc4f77c3fea9a9f3ee26ca9c57a28626a1489533cfcd98ff12ed2a27da5cf10" + }, + { + "name": "danmuku-native-parser", + "sha256": "64db69060576301b62ef92d0b1c826032ad0f289eea119b7035ee1530664659f" + }, + { + "name": "browser-evidence", + "sha256": "ef659c544a73dfe808900970bd58c03e19d2088add365af893775afca9f699c6" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku input replacement append and destruction (candidate)", + "engine": "webkit", + "durationMs": 1206, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "8dc4f77c3fea9a9f3ee26ca9c57a28626a1489533cfcd98ff12ed2a27da5cf10" + }, + { + "name": "danmuku-native-input-ownership", + "sha256": "485cf68c27a9ee93ee11cbd3f6c458f299df1ae3218cb2a5d1e0ec34c8fd9737" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-input.spec.js", + "title": "Danmuku config callback replacement and validation (candidate)", + "engine": "webkit", + "durationMs": 1057, + "evidence": [ + { + "name": "danmuku-input-build", + "sha256": "8dc4f77c3fea9a9f3ee26ca9c57a28626a1489533cfcd98ff12ed2a27da5cf10" + }, + { + "name": "danmuku-native-config", + "sha256": "cddd7273944da5e194015943fc618cae7a3e19a434d15b5d87dd7ea46615c76f" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: shared setting mount preserves the surviving instance and later user changes", + "engine": "chromium", + "durationMs": 291, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "96cc31e201ef60e560a9824e3be38eb34536b32db988f298d371093f72468365" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "chromium", + "durationMs": 359, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "6245aa932ba9a3803f25e1232986cc5f89913266243c8cd65fff96f7b5cf2438" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "chromium", + "durationMs": 383, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7d7b7c14c030291cfadce7d4dcf0291b9a1dcb1e581244f537664867f572d952" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "96cc31e201ef60e560a9824e3be38eb34536b32db988f298d371093f72468365" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy clears active send timer and leaves another mounted instance usable", + "engine": "chromium", + "durationMs": 466, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "6245aa932ba9a3803f25e1232986cc5f89913266243c8cd65fff96f7b5cf2438" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "chromium", + "durationMs": 382, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3aec0fa73b2af3a12bd4dead75b1b406ea93b2cda68b34861c3a4f82a176c088" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "1624840c81ebf08857ddbd3d3b59f1df9ffba31be88bdcdb021a9b1e24b42bc9" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "chromium", + "durationMs": 444, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "96cc31e201ef60e560a9824e3be38eb34536b32db988f298d371093f72468365" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "chromium", + "durationMs": 441, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "96cc31e201ef60e560a9824e3be38eb34536b32db988f298d371093f72468365" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "chromium", + "durationMs": 471, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7880f9b3897792a3c774040360d15242ab8de46a6e0a9221c97687842ea039bd" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "7443bc0c9ae74105a1331def30a824533188b99262f227f0a5850c0a96fd7cae" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: shared setting mount preserves the surviving instance and later user changes", + "engine": "chromium", + "durationMs": 302, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "326565edb6152c52b97bac465aeeed05d0abe7d1c63c8e3de4d2cb8a2535b781" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "chromium", + "durationMs": 389, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "ea0c1c023cf2826f5be109522b0b0a87d6043b295b7eb2bf335aa6dedb315b46" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "chromium", + "durationMs": 442, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "52d704a861a09d8d416b1c27c18061068a008364a68591a67d997a6407c1fc56" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "326565edb6152c52b97bac465aeeed05d0abe7d1c63c8e3de4d2cb8a2535b781" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy clears active send timer and leaves another mounted instance usable", + "engine": "chromium", + "durationMs": 473, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "ea0c1c023cf2826f5be109522b0b0a87d6043b295b7eb2bf335aa6dedb315b46" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "chromium", + "durationMs": 373, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "68630c02b5ca887811cfb87a13e57c2c293e5608b4b608d654cc096446d3c12c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "69de9f174219607024fd0ea29e663a04b966d5e77d7a527845a75f8765a7324f" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "chromium", + "durationMs": 467, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "326565edb6152c52b97bac465aeeed05d0abe7d1c63c8e3de4d2cb8a2535b781" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "chromium", + "durationMs": 480, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "326565edb6152c52b97bac465aeeed05d0abe7d1c63c8e3de4d2cb8a2535b781" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "chromium", + "durationMs": 544, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "9a67abdc9af7c64c1e5d35d031f8c0a1cb4f02586b6c5491f0c035a67f2d22b4" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "a5387e722fb81383234413c729bc4ffacdfbeb7922d0ed1c3f4745434e8b0e78" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: shared setting mount preserves the surviving instance and later user changes", + "engine": "firefox", + "durationMs": 726, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "firefox", + "durationMs": 584, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "firefox", + "durationMs": 1411, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7d7b7c14c030291cfadce7d4dcf0291b9a1dcb1e581244f537664867f572d952" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy clears active send timer and leaves another mounted instance usable", + "engine": "firefox", + "durationMs": 905, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "firefox", + "durationMs": 699, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3aec0fa73b2af3a12bd4dead75b1b406ea93b2cda68b34861c3a4f82a176c088" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "a37e53602682ec1f0ad1eff163ef58dedffaa5658b8f55520b864f4b2a766e3e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "firefox", + "durationMs": 774, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "firefox", + "durationMs": 531, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "firefox", + "durationMs": 699, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7880f9b3897792a3c774040360d15242ab8de46a6e0a9221c97687842ea039bd" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "10bbad4ed95b844c80ed949526938269bf9525db5704ec47241c2163f3130aba" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: shared setting mount preserves the surviving instance and later user changes", + "engine": "firefox", + "durationMs": 479, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "firefox", + "durationMs": 478, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "firefox", + "durationMs": 1440, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "52d704a861a09d8d416b1c27c18061068a008364a68591a67d997a6407c1fc56" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "39869f67f90b16f274003fd790d94edaedb2f1b15ded7eff21dae75003a09175" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy clears active send timer and leaves another mounted instance usable", + "engine": "firefox", + "durationMs": 762, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "firefox", + "durationMs": 696, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "68630c02b5ca887811cfb87a13e57c2c293e5608b4b608d654cc096446d3c12c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "93533638ef2d44aab06038ae645023c766dad22d19767cee2d01d626ed9b9569" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "firefox", + "durationMs": 791, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "firefox", + "durationMs": 718, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "firefox", + "durationMs": 669, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "9a67abdc9af7c64c1e5d35d031f8c0a1cb4f02586b6c5491f0c035a67f2d22b4" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "2513782f17a4573c1bcb7841e4193bd47e946b816a4c08ac7d3a7d534244e246" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: shared setting mount preserves the surviving instance and later user changes", + "engine": "webkit", + "durationMs": 1182, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "webkit", + "durationMs": 1409, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "webkit", + "durationMs": 1640, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7d7b7c14c030291cfadce7d4dcf0291b9a1dcb1e581244f537664867f572d952" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy clears active send timer and leaves another mounted instance usable", + "engine": "webkit", + "durationMs": 2223, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "webkit", + "durationMs": 1698, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "3aec0fa73b2af3a12bd4dead75b1b406ea93b2cda68b34861c3a4f82a176c088" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "b4e1558640dd3365fa32b01258a3b1ebc8cc340d3d9ee679c6e0650dab2d806e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "webkit", + "durationMs": 1623, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "webkit", + "durationMs": 1729, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7629f98964147781e566c7ce2bb86712da90d37eac7d48f02161ea65d4cd61f3" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "published: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "webkit", + "durationMs": 1942, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "7880f9b3897792a3c774040360d15242ab8de46a6e0a9221c97687842ea039bd" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "33f660c5e9a16589a8655cab35686367ff22f48e6b0db1d3773d85d9413ac93e" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: shared setting mount preserves the surviving instance and later user changes", + "engine": "webkit", + "durationMs": 1334, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: invalid setting mount rolls back plugin resources while preserving the original error", + "engine": "webkit", + "durationMs": 1619, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy cancels pending setting send without clearing retained input or starting a lock", + "engine": "webkit", + "durationMs": 1657, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "52d704a861a09d8d416b1c27c18061068a008364a68591a67d997a6407c1fc56" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "a4d0ca922b338a7bd56737b2df9c5fd8ae2f55560d435c2606616c3677ecfc0b" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy clears active send timer and leaves another mounted instance usable", + "engine": "webkit", + "durationMs": 2466, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "953acc6965318087be0e941333d2ff5f6151e6990b49a5292fffa482f6f16abb" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: rejected setting callback retains input and original console error without an unhandled rejection", + "engine": "webkit", + "durationMs": 1701, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "68630c02b5ca887811cfb87a13e57c2c293e5608b4b608d654cc096446d3c12c" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "fdf42db4c029d180bfad8d10a7439c2bd33ab8cb02701471785c9d88ebd92c2e" + }, + { + "name": "browser-evidence", + "sha256": "ab222de8427a05b26737bbbf2d35daab8db7a2c04db2a7600b5a5428d2ae8931" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: destroy false releases actual renderer nodes while retaining the player shell", + "engine": "webkit", + "durationMs": 1676, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "7ab2884e975329c0ec9206c89fefef5bf16cda152fe48da0c9bc8b2cb0a332d2" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: replacement load removes a moved renderer node and preserves unrelated body content", + "engine": "webkit", + "durationMs": 1789, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "2286405d70fcd95dde3db0713059c09f88346873fab8615126e4ed9ede4c0240" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "74ffabbf82fca049736a7119115b9c61d3509c389a90409f50a4de03e51ca27f" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-resources.spec.js", + "title": "candidate: narrow heatmap invalid sampling stays responsive and two instances have independent gradients", + "engine": "webkit", + "durationMs": 2047, + "evidence": [ + { + "name": "danmuku-resources-inputs", + "sha256": "9a67abdc9af7c64c1e5d35d031f8c0a1cb4f02586b6c5491f0c035a67f2d22b4" + }, + { + "name": "danmuku-resources-heatmap", + "sha256": "e9d120c12ba479403923b82047fe1d205bb52a2d66fd05b35a39210aeab0b669" + }, + { + "name": "danmuku-resources-evidence", + "sha256": "741777ff8e35768c0034b4cdd88ec7a52a86f26f7b4fd21b11b6afd5b2c4d5c6" + }, + { + "name": "browser-evidence", + "sha256": "8cd6ebb76447e2ab9c5e4029b0984eb3313373cd1387777cbb822ab44155add0" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "chromium", + "durationMs": 2279, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "3d1c2f69b4166e953edb968ecd839084f93ba557f3e801521b2fefd8a33d5784" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "5c3bdb0cc3ee65cc88aed0b47df27c4b1d34686dab8dead6bf2d7d19fc817b00" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "chromium", + "durationMs": 479, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "c86dcbec83403ae0e1f6ee05237759c884834a297ada5f740f38a43222fc8a5f" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "fb2633c34c1de3723020d743fab4eb78208c1d775f47bdd5e945c9fbdb6047f0" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "chromium", + "durationMs": 488, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "7b31e31233b5c342c5086605b85f9fef5be2ecbe97888110d3cfab867eed77d9" + }, + { + "name": "danmuku-layout", + "sha256": "b66304a914066dfd30b337652be59c9f420aa81f6a30f8808c75150708affc24" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "8978cfca4a497f9fdab7c59ed36cd1cfa56d1483970a22f85b8dc3345ca1113e" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2661, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "5af3560f1c959d422b95295a65e5c5d6e7e9db40766c452366fc3b9cc3bc4d84" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "94ec8fb35800dd612338026adc61eacaa55a23d952a6788511b07edbe5912eaa" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2438, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "d2d32702be65a0219cac520e8ad1221ac29809d04bd0f7b76af34ba88c8b12d8" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "a7b1994dadefff181e9e194d60d1ba88031417d2fcc5e82de8e32a4ea28d53d6" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2341, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "4affa8fb766054cfeb861fa7e82e6b7263adedeeb6e5b2d45a56e50def0a4320" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "410eeb5d756b2bbb65ac1640bd39b3f95bb05073a6e9197bb1da60f25d26b8ef" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2276, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "01f4febf1e97245cc398e2ba5cfaf4479bfbd8088aee749c7ff0a47727f36433" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "chromium", + "durationMs": 1338, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-layout", + "sha256": "8494f2609dfef3281f82b634d67e2a21e13a7156d8f06f8e8e76b4522ffa18ce" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "3df93f775b63f14b27e57095e94d51ac8d9fa3b4a0f167a26bcb8641097f3686" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "chromium", + "durationMs": 1407, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "c12743df46ab1eda805417a4bf1d8a8dac15da37ab195ff8259453ea8f4ba090" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "4a8fa959e3477d0ac519f7bb71847a02898ea2ce066f4406cb21c90cbd743a70" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "60cf2a6408d778599e65178b64460135ab5b465dd7d49d596031b620da2f1cda" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "chromium", + "durationMs": 2339, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "0fad1c81fa6ad31e9bf3f6b1af5d4d262e16ac06d8fb45ec8a5a782ff517328c" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "6dc5fce0a2ddb69ede4d84ea85a04a4ccd6391aaf093068bd6b251032ed5175d" + }, + { + "name": "danmuku-layout", + "sha256": "555d8bba755f12c77ec4749ab3dc1ca27937372a2018bfa79192ac424006f1ea" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "ef879f0668adf1b44cde56d3877f9e185d75c0530871be35a7ffe559c59fbff3" + }, + { + "name": "browser-evidence", + "sha256": "a8778dbf5f0c3853d92a5120635a9e520f3cf15d420ac5a555933d4ce0a4d09a" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "chromium", + "durationMs": 4062, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "bb1b28807f757c1fcadf08577f1dbfd1b358a7ff37ea3b1ece148abdf2357fb0" + }, + { + "name": "danmuku-layout", + "sha256": "b964ceb15cb0047c9500b29aa36c65ac7984cda867a00990c0cbb20cc9ac251f" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "a11de4e140f1cc0cf2ae2c6c173f42ecc1451a39c0ef79c1ebfca908a787b910" + }, + { + "name": "browser-evidence", + "sha256": "7c9f4a21af0011c3d15c3c352a7ff53739541aa6f6c3e93348d977271bc236f7" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "chromium", + "durationMs": 2287, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "bddbc94a30e04de211d21e4c53094ba4ab6e42fe34b347314cefe278325f03c4" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "8f74fb702f07f280eaa7a1bda06b72f395d2a540e87cb3a5356f87a8ae4bfd49" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "chromium", + "durationMs": 472, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "0dbb8658adc5f6200e5d8c8f1ef052ad9efcb9aa5c68e9de2d45dd1574f6c3b7" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "73c5c5ab7269d490eced591d81badbcdbb6bf3520452a3068d625991268dad22" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "chromium", + "durationMs": 491, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "cf625ad651a9bce3cfcc48d99e05dafcdfcb9af6b21ace9552b75d74ebc5204a" + }, + { + "name": "danmuku-layout", + "sha256": "de19a0b5785dd8099d30e0b4ab96a2ddd96ae567e893970c0762413f9630e25b" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "0025f96739c51e9ce840061562415ed00c3a31bbfc668204b6e18d00f6d96153" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2658, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "e6bf7f0f8d6733202057975a3b9de66e016d3e1d81c187e27666351f26eecf88" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "f35ce5bd35b87659c2feeecf02d5c205b56b6011d9afcd52fa579336be76e957" + }, + { + "name": "browser-evidence", + "sha256": "9dd6e666e6f908e2a5bcbedda78132ab3713311a939892280dd9dc3823d724d4" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2341, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "3f74f8f649f62f82eecdb64915efe4cb89ab26f2d1d6bfa6fd1a78510b78a150" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "48582883a50ed6e2436bda19b32f72c1f9e0b6112ec5b480401131a7272a39de" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2387, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "84165f8a84f1252a21169c9af3fc967c72f929540f8ee2056ffea49fe04dcbab" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "30c6e613d73e4885519ba50c1a84fa9b3286bce49e8502f08c08e324313f719e" + }, + { + "name": "browser-evidence", + "sha256": "9dd6e666e6f908e2a5bcbedda78132ab3713311a939892280dd9dc3823d724d4" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "chromium", + "durationMs": 2345, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "63aa353c812ef3ff85f11fe2e324f0de2346b394109e30b383b95f5b882e9591" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "chromium", + "durationMs": 1353, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-layout", + "sha256": "ce263836fb9cc1db250c9e552e9d3761ef78b13cf8397fc567c1ae9956170cd9" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "b980338c48724e4bd0db3e6957b1ddd3f64cd017e9531a1147cd5201d0d6c1b6" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "chromium", + "durationMs": 1454, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "1adca3026297ed9b855b217002981111c955e5441f66205929299a6a4c02415e" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "e536c784e71b24756c34417a2c3b58855a6f3f5ad912ac17d2d75ca5e0d79257" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "eb60394d1070b0e345f85cd370ce470e32ce06562e6d40de3f945592c564567c" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "chromium", + "durationMs": 2361, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "69ddf436eef41ccc1a1c59e51ab960893bdd95261196557262ec9d2502d71277" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "6165ca42b959cafa380b455a037a2fb2d5e0ddd46fbefc4d531575dd803c8010" + }, + { + "name": "danmuku-layout", + "sha256": "0c3c0f94b15c766c0e61c5b45c9d79daa332731e2f0b13275536ef01e7c80b0a" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "369bed73fa401c19140658d056c63355a5a44a721e30517d83eeccb0210e34df" + }, + { + "name": "browser-evidence", + "sha256": "9b3dc7b03ef4976984cbfe80d7dc7206a60d6df532f2f106de11005f1d3b2d02" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "chromium", + "durationMs": 4012, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "a0f910650ef2ed788e21cbb0f5e3d5a55866e17cb1c11e77a4839e8fd602e25f" + }, + { + "name": "danmuku-layout", + "sha256": "776bf3af3b2b6033c860eb5173758ce42536e10f2dc74b01feae3ec955454a5c" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "ea4d9cde62619c32c303373496845be7c4aa48ad6e85da1d7bfd4392d03506c8" + }, + { + "name": "browser-evidence", + "sha256": "71730b2e97b3f5935a1eca46fbd686ce72103fe9230088797dccd9e13d9c206e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "firefox", + "durationMs": 2650, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "9a311bafe7721abd5591bdff50816c05f885a65071aa136fafb54f0e7ac5ddd6" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "49dfebc58936a65e1e9748432a02cb4b820536e9d41c27dacdfdc15e8e9fb6c8" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "firefox", + "durationMs": 767, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "0c06f59c5dd86f4ed20440ca6d057974defdabc1f88d44cb4a74ddab292cffeb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "1cfc5d1b3dc3cf22917ddb7b29866a92114bf78a02c73dc713e381380f7610f9" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "firefox", + "durationMs": 822, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "9fdda12cc951988a0d10ae92b453d1b43df6748b86e8a5d0b449ae39e1c0cbdd" + }, + { + "name": "danmuku-layout", + "sha256": "9e96495c810e7279dddf820ee0423b437d23cb408ff9957595e29e5510b794a9" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "6766c9b19830ae7ce743dc44dde816736b95487d1a7300478412a7249fcbf8da" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2866, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "f6b3111a547b24cda10a2501c67b32ffead58bb4b00970d2d1f3378c51a5f164" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "e7031d8d74373fb85fe823da78ff7499e30927bda1bf53384f3f5d23beacc47d" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 3127, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "f08e4a5957a52d33e85f6a8af721ce0dcbb411eba1d8da446224ed3c2a6dd95e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "9ba2d00c1f9b443b85c91c65c012a0af63bd7dca0a82ee5c631f2483effb0eeb" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2819, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "e28cc30af6ef3ef2923d9c208dcc2c6c00089cc791ad631e0553cd70450276ef" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "a35e7f995a835b8b0b7346f7c20591cefb28530c8f6804e20dbd164a26f64c5f" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2608, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "2bc734bf2f3a4f0b70843694b4a05e353ff3be56d8dd54ddb2bd57a555c7706b" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "firefox", + "durationMs": 1648, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-layout", + "sha256": "0d2a09542cbc18b5d82826a54d410c37adbf81b5d8066061309ae8b3a8d852e1" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "8c8ac4aa97d1e8643786e73b07edd4a2144c0cb0b41c9bf78b1b3aa1d0edcac6" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "firefox", + "durationMs": 1533, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "c12743df46ab1eda805417a4bf1d8a8dac15da37ab195ff8259453ea8f4ba090" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "d8943eb24f19774c2904aa743399ced7147dc500541408478aa72514bb6e6ffa" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "7b9fde7ad22a762e1ad4b8499cdb49c470e5e060d08d5e8de0a880644b4103ad" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "firefox", + "durationMs": 2699, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "0fad1c81fa6ad31e9bf3f6b1af5d4d262e16ac06d8fb45ec8a5a782ff517328c" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "fadd068fced67fe52eed938de49358f5f8492d8348618476eddf579cce1febd7" + }, + { + "name": "danmuku-layout", + "sha256": "17cf241aedb4e8bd2e100b27eee8248d14f6283bb2f3d1459efc522b77a5ea45" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "571a9362603fcab0964ec3d7fd45ff25acb0ca5f270208e9226e67b3144dbcd4" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "firefox", + "durationMs": 4566, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "bb1b28807f757c1fcadf08577f1dbfd1b358a7ff37ea3b1ece148abdf2357fb0" + }, + { + "name": "danmuku-layout", + "sha256": "baf37468ba8383d16d587a78f57cc063a562808d311ac108cbb702cfc216d346" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "0a28ffe960e9be2c6d9c961a7bc3e2bb5cb6a191ae1b5fea066dce0e26129b77" + }, + { + "name": "browser-evidence", + "sha256": "ceb61e15f07d49244f154d15f77b0e6d1b5a5d59106c6fe06b81b601da007d5e" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "firefox", + "durationMs": 2654, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "3e80173eefe697c9b0985cfc73300cb5054e1cf0b89df57d3998459709529633" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "0dbb19a633ff40135f175800e8fe9ade5b2e9f2f2252a8e5fdba0af11616a7db" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "firefox", + "durationMs": 814, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "28d221cf7b884394a4b832c2d0fd78e372c306e201ef99a5519f981d164ad861" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "4ffe6b5e0365347afb7e7ae15c61e613efc0b7291c281ac3dc133521a8585778" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "firefox", + "durationMs": 767, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "db684f20736f4c158cee096d50d9934d4a8e24563fb3088d44bb858c04d42eae" + }, + { + "name": "danmuku-layout", + "sha256": "71e4ea359aa94da532fd619c5d0efdcb52c008e63cdf1fd74e0e03968fcd6d1f" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "0043448615272c71f9b6224fd081b0f66998832b5b779efb710c83fe65db222d" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2969, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "7416e6e4baa7e249e28d4f931bc823525186a283330aab39ac88c4d84637c06a" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "a4636e205da4da6027fb9e620487f14d5cfac2fde4bf05efc8b80a2bcefc7166" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2935, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "f08e4a5957a52d33e85f6a8af721ce0dcbb411eba1d8da446224ed3c2a6dd95e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "5b32ab2457d8ce5f46b1f4e415058fe9d963b01f1ce3c1245a0985090d9ca162" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2750, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "cde8eeea4fa8a83eefd6af0c7d47bdc072e94e90f7694ba77f63d42c965d96ef" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "5756a58d80d979ffbe49486cbba253c0cf0ec6a5ddecf7d8a82aac06ce0ccb04" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "firefox", + "durationMs": 2647, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "63341369130c4cb9b04efea6c159e2c7af863dcfc9c309d005b9ef5426f2b85c" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "firefox", + "durationMs": 1548, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-layout", + "sha256": "fb5fc5323d8572801b26e830b989234ce46cb75b6dcdfb9d79dcfa4cfdb503f6" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "50d590c4006caaabd272bd18a15b7df1ea889438b2687e3fdc3b907f51dd86f2" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "firefox", + "durationMs": 1539, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "1adca3026297ed9b855b217002981111c955e5441f66205929299a6a4c02415e" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "d8943eb24f19774c2904aa743399ced7147dc500541408478aa72514bb6e6ffa" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "c368a6c5c2c769a01acd90c9139e64c622842af9082e13f3afeadf561c26ae65" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "firefox", + "durationMs": 2609, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "69ddf436eef41ccc1a1c59e51ab960893bdd95261196557262ec9d2502d71277" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "a2fbad3d2a7c0c79049995789da778c1276a805ccd3910f52fb3adfbf9a5caab" + }, + { + "name": "danmuku-layout", + "sha256": "c6f7751f51b3f8e91f51a8e163335a2a1a62a7a41b94dcce54bf78480277a077" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "1ca15ba36ba7e2560f8bd20d13f9abe3265f201c888466888389f7cb70fed471" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "firefox", + "durationMs": 4601, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "a0f910650ef2ed788e21cbb0f5e3d5a55866e17cb1c11e77a4839e8fd602e25f" + }, + { + "name": "danmuku-layout", + "sha256": "011f843f591721f6d197795f981788c20a40aa1563ea4b6a2b403fedef3d2aab" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "e3fdd796ef6898b47c7b0c075244920d5cebcc775653526484f7c00fc56dae0a" + }, + { + "name": "browser-evidence", + "sha256": "6a9254d7e6c2bf16711aac5c58901fc497f80f9e6380bf71949cf19727dcca00" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "webkit", + "durationMs": 3754, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "897ac67ee49e4caca2f1d1335348803d568a74e5250f446e77905432f842047e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "300ff61dab11dc6bbd255b06743a90c92d0ca3f9173528fe6f552773897cec5d" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "webkit", + "durationMs": 1601, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "ad0a74fa15da174f9dc28a07059a707fa875f4d09230ae21ecfa75cc2ea49825" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "4119e1ee00a0f7c1926bf201359c27338ae03d18be73a6d5095e3294fc3aea6c" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "webkit", + "durationMs": 1750, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "a325a19b674b1a3ec40c95915a0271b45dac9507e589b7d95f78f655231008a8" + }, + { + "name": "danmuku-layout", + "sha256": "8c958e6bb5e03519f5e87800da2bda3deb4ba737cc49ee131fb5be9b97b48e74" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "363d57a6a60cc1731b3a0d133a2ad550a3ce1615f82812a0d4aa22d57b4367f7" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4705, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "1547df547aba8ea7587f7f904b0ba741d6e30678643f70df1c4b52424c349b34" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "8621ec5a48715afde3d554bdb3de2f5365da08af2a603e44b6122ee518afbc3a" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4022, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "deee517b762021065eb2a05a0744379cbaf0e3682b70b5247adb4538c899abf8" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "e7db186268a6f68f2f29487eb8f896e27dae81dff581ece01dead9acea1e126e" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4296, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "c5b1fb23d6508c1a216c0a1b8005b933b37266401b0952faf6331ee5ff630e24" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "a314da5ff137fdac73e2c3936d22f2fced077821e8310f885533068400bdd87d" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 3754, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "1bd9041a520199bdcfa3660757c4d3714772e4158d32c2a9bdaad5550adcdde8" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "webkit", + "durationMs": 2864, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "40b6587d23ecabb195e93bf1b8eb80fe501060409c596220d9d0250a14ddf4df" + }, + { + "name": "danmuku-layout", + "sha256": "c5a2decdad66593be783c8ebbf902488f340be44fd1815144770fe54d944e453" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "cbc44eb8bbdf5b26bff68b2cfa7b7df9a34a5f80a6fd6e6e1570df9d8057c599" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "webkit", + "durationMs": 3307, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "c12743df46ab1eda805417a4bf1d8a8dac15da37ab195ff8259453ea8f4ba090" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "7ba3d982f18fb7b53431d1d37089d44efaed846abd3b0fed29442f96a69be36e" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "e9202480e09796a48903d59277b794e6be6fc9b37db527ca53238e07c6ed85f4" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "webkit", + "durationMs": 4197, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "0fad1c81fa6ad31e9bf3f6b1af5d4d262e16ac06d8fb45ec8a5a782ff517328c" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "a8b3cbdff77e4107670734aeb2d9e2f87b4d5d5457f5598c928c8af256b4e719" + }, + { + "name": "danmuku-layout", + "sha256": "e09a30be085b82a998e0314c88d2fddffe17e5f5ba218f94fab3de03bed0cdfb" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "cc19a15371f2b310dfd81ef8b5e6fedf5dfae772ebd7de3204ea0c0a6dc34a0c" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "published / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "webkit", + "durationMs": 6890, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "bb1b28807f757c1fcadf08577f1dbfd1b358a7ff37ea3b1ece148abdf2357fb0" + }, + { + "name": "danmuku-layout", + "sha256": "6f6aac73b13749419168baaa25e8cf671d9c7e18ebb44ca078db268952965682" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "55c7cde0e369f5f2b2c18e82f418fde8833695edf9232111125d5d795769c3a5" + }, + { + "name": "browser-evidence", + "sha256": "7fbb0ea558f9e61c649b53590dfb271d8280e065f73152ef154e1dcbe60827af" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: accepted NaN timestamp keeps the historical current-time fallback", + "engine": "webkit", + "durationMs": 3634, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-numeric-compatibility", + "sha256": "47c472d101feb1370a6144bbf402eb2487b497e77bc1d680f6ab5024ddf5d38b" + }, + { + "name": "danmuku-layout", + "sha256": "9857ee970c6b2b48150a5a43dd265c71d3faa2520f38aaf1cea032b87daa5e95" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "70c0ea2675e571b229c1822d79d0f0ceed6e07fab6d0880697bc41bea014b8fa" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous destroy from the error listener", + "engine": "webkit", + "durationMs": 1822, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "21eb01744f41befe252ce3febe46770ed8c246f1fb7f109afda095baa6022c7f" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "8c94d06c3f33f169396ef77ced7d49b46887babeac8354e12a636c1315e2ad6a" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: failed recovery respects synchronous stop from the error listener", + "engine": "webkit", + "durationMs": 1970, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-recovery-reentry", + "sha256": "1063dd145855bf1869547ff3048c00f16dfef8312ea3ff6c9a86559416c727a8" + }, + { + "name": "danmuku-layout", + "sha256": "84b5a003b3a90f82394478100cfb35698b5f7eef8eeb8d99602f930bf5d23765" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "95726d97e4f8f5d594210f2e94f8e9d3d0147f1f73a835d3e6bff8cd0e25cb19" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4639, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-cancel", + "sha256": "111f80f14ca6f8a32268432370530dc9ba6d4b7da69914c59772d3b705d100ec" + }, + { + "name": "danmuku-layout", + "sha256": "ae1e2606e57af298d4231aadd93b5c2b20f6d4f4276cc32f8094eb5ae2d18726" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "cb2de4bac1a956674f1e78a0346e4553b3a19a373646f2999c3833ce31d19413" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native reset cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 3986, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-cancel", + "sha256": "7934631c50b47f87f4f94efe53bb6c994536d05e56d184ed0613af694a5c152f" + }, + { + "name": "danmuku-layout", + "sha256": "8bc757ae25dcc380afcffefb40428a881f5713f3b1199aa1b151c61721432ef0" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "a6b087df70ca3fc8a5d0605dccf7dd8750c4635a81861368d11369c2ea13b690" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native seek cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 4396, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a2ddb86491cbb358372f6b0e4eedf8fdea16ddaa8bda6aead23b158406e847b3" + }, + { + "name": "danmuku-layout", + "sha256": "929efcc98f3509b0f50034a39d3bfc8c407ac1a762864de9f6ac6a9bc7133a77" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "6eb08843659f0d17aca84edb72aa89b4967b0625cd0ed1d9ea8c623bafb4afb7" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native destroy cancels pending visibility without blocking later work", + "engine": "webkit", + "durationMs": 3834, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-native-cancel", + "sha256": "a09191cf0a3a3a23d57d7861df5525388589e126c18a0be7f1470ae8674fc1bb" + }, + { + "name": "danmuku-layout", + "sha256": "bd0171a3e7372948efc55265ac45a6f3e9446339095500dbdc447a6efdb999ac" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "1dc00f164ca2617e4a8881b84c8049f6be335cfb24ded68abcbb7d9222bb8824" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native Worker renders three modes with synchronous filter and asynchronous beforeVisible", + "engine": "webkit", + "durationMs": 2620, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "30e103ed660b8405d94d100ba0f700ff3a17b436b583e60f046cc7899c51c530" + }, + { + "name": "danmuku-layout", + "sha256": "5697a1cb1fb02c7003bb0944804b81276c8f061a22df408aa36357a0e7ebb96f" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "3eea051b32a76823f008305bc6734c2124002b78bfaf127bb0c3876e2fd75460" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: six simultaneous fixed comments preserve usable track geometry and input order", + "engine": "webkit", + "durationMs": 3167, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "1adca3026297ed9b855b217002981111c955e5441f66205929299a6a4c02415e" + }, + { + "name": "danmuku-dense-tracks", + "sha256": "f544ccc42ee2329e9591b5b24c667905d31d3b3c38aa69a2b5acea3d006fa0e8" + }, + { + "name": "danmuku-layout", + "sha256": "cec74c7c88300ef462f2202d7e3e3723a3e6b7c727e79a20bac31612223f7fbd" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "cccb1235259ca00b4bae82cb7bf90c4859534731f0a643e023ed5d91e2f21667" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: native pause seek rate and public reset/load retain historical pool semantics", + "engine": "webkit", + "durationMs": 4358, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "69ddf436eef41ccc1a1c59e51ab960893bdd95261196557262ec9d2502d71277" + }, + { + "name": "danmuku-pool-and-rate", + "sha256": "8be2a486b3633c912e8b8dea6b3fbf40057031c2bac5ca3c710b572256b2c10f" + }, + { + "name": "danmuku-layout", + "sha256": "fcfecdf1e67cee72d102c6a37cf74765b5b411338e08e5f59254da632478bd61" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "9bd88c9948dcb9431a5c63823cd30fc2fd4de7967cdcc23466b7a3f9a71dc30b" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + }, + { + "file": "danmuku-scheduler.spec.js", + "title": "candidate / Candidate Danmuku candidate: actual settings, asynchronous send lock, external fullscreen mount and two-player destruction", + "engine": "webkit", + "durationMs": 6983, + "evidence": [ + { + "name": "danmuku-browser-inputs", + "sha256": "a0f910650ef2ed788e21cbb0f5e3d5a55866e17cb1c11e77a4839e8fd602e25f" + }, + { + "name": "danmuku-layout", + "sha256": "045919c166b1911a8d11395c532870d9ddf2816ffe76a84cffb7590460023b09" + }, + { + "name": "danmuku-worker-and-lifecycle", + "sha256": "618306c7912c26063403bfa232c7001f3c3fe03706dd60f2a505b055f9694674" + }, + { + "name": "browser-evidence", + "sha256": "b0a3327e1cecd318c58a30cd7197943b85095b936c6d141abbcbdbf4fbdb519f" + } + ] + } + ] + } + ], + "browserScope": "Two cores and three desktop engines per format; Windows WebKit is not Safari hardware. Kept original assertions, 20s timeout and zero retries. No long-load acceptance is inferred.", + "diagnostics": { + "density": { + "file": "refactor/.cache/danmuku06-webkit-density-diagnostic.json", + "sha256": "755dfb51b5c0fa4f090e3cd3ae63a9b6774e3d6b2a3c292cd073ac06d50af3c0", + "stats": { + "startTime": "2026-09-13T13:53:49.464Z", + "duration": 25307.453, + "expected": 6, + "skipped": 0, + "unexpected": 0, + "flaky": 0 + }, + "workers": 1 + }, + "failedSource": { + "file": "refactor/.cache/danmuku06-verified-source-browser.json", + "sha256": "9d2292fa8f4633fd0a68fbe3f0271e08c63aa454230ee7f054d70d06eb19b9e6", + "stats": { + "startTime": "2026-09-13T13:40:39.400Z", + "duration": 202920.14, + "expected": 154, + "skipped": 0, + "unexpected": 2, + "flaky": 0 + }, + "reason": "WebKit elapsed time primarily before plugin installation; trace distinguishes client/browserside delay. Both failures remain failures." + }, + "failures": [ + { + "file": "refactor/.cache/danmuku06-main-unit.log", + "sha256": "5899cf6db813e0fef01517bc88b762e096ce4ebf28785a0555b02f52fd1e4d33" + }, + { + "file": "refactor/.cache/danmuku06-installed-types-pack-failure.log", + "sha256": "5a341a52238cb138eabfd77ca3db06b0097be20cace9b6500a1338bc3bf97308" + }, + { + "file": "refactor/.cache/danmuku06-source-browser-overlap.log", + "sha256": "e7cf5ef3108f8c4b7607b936747d207ff55abb7d23dd7312e0f0c0a41c50c404" + }, + { + "file": "refactor/.cache/danmuku06-main-browser-port-collision.log", + "sha256": "0cb4e4702d4911202fe245b32998163a8a602aefe45827e997cf88cf3c50c4ae" + } + ] + }, + "manualBrowser": { + "browser": "Codex in-app Chrome/152.0.0.0", + "url": "http://localhost:8082/?libs=./uncompiled/artplayer-plugin-danmuku/index.js&example=danmuku", + "mode": "normal yarn dev docs example, local media and XML", + "playing": { + "time": 11.711807, + "videoWidth": 640, + "paused": false + }, + "paused": { + "time": 43.744183, + "repeatedTime": 43.744183, + "videoWidth": 640, + "paused": true + }, + "sent": "TS migration browser check", + "inputAfterSend": "", + "consoleWarningsAndErrors": [], + "limitation": "Development smoke only. Browser.getVersion unsupported; userAgent read through supported CDP. Earlier stale tab focus timeout retained in change record." + }, + "checks": [ + { + "file": "refactor/.cache/danmuku06-final-build.log", + "sha256": "e3769d061d2a42a2312ee4f70b1981262b2eb414525bdc4fd04217675ddfad67" + }, + { + "file": "refactor/.cache/danmuku06-editor-build.log", + "sha256": "607e5ee81fffbb654dff8e6fe914eb2751e73d42470fb1129da8b63c3cf99ec8" + }, + { + "file": "refactor/.cache/danmuku06-frozen-install.log", + "sha256": "0563e4096d2118ac4345d89658fc9e43ccf766ab66a8971642c7d99001a4cc61" + }, + { + "file": "refactor/.cache/danmuku06-toolchain.log", + "sha256": "087808cfb19ff7e41faa2a54d3dd8384fd4a73e7422a9aa77902242e6ef6fb77" + }, + { + "file": "refactor/.cache/danmuku06-final-lint.log", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + ], + "fingerprints": [ + { + "file": "packages/artplayer-plugin-danmuku/src/bilibili-parser.ts", + "sha256": "a9d7668eda4ab10d076c319fb8847b2f4492f4499a47b003e30a8a647f1fce9f" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/bilibili.ts", + "sha256": "5ab32ff4b7aff266c0b4ca0967e7be712e0eb4451fb40d544d42df46460f47d2" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/config.ts", + "sha256": "a24b2e25f40bbe4bf0f326e67dee93501f756324cfae3e4a8dfc13789dd9fa82" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/danmuku.ts", + "sha256": "15ccf41b7bc94fce7fd1df64ee53de251736f0ec719de7a9c007b449e96003e5" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/heatmap-geometry.ts", + "sha256": "fc4a3073bdcfdeb09facf7dafce5bd10c2518ec6feb5ab6fccc008c869d01ee0" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/heatmap-sampling.ts", + "sha256": "7f4074a8ec3eb985599ca5c4def096030a11733285e72e9e1ba5cf23f81108a3" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/heatmap.ts", + "sha256": "032ec70f9594824fb6eedaed277e1125a6327219a8551bd22956f8a5ac162381" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/img/check_off.svg", + "sha256": "8cb8e31da1bf7b5be8f4065920b46e1988755910218fb121dd3d9649c50156cf" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/img/check_on.svg", + "sha256": "c5a34f56c56b0f696b4e3740c73e803076b5bfdfdee89714793118fc4c6baa84" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/img/config.svg", + "sha256": "bbd7c2952740ac8220f396477eaca3b01f6c249866d880ece38c565cc77c5e36" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/img/mode_0_off.svg", + "sha256": "fe9e5e1745e8206a4eda0d0ac30815f301c37acccec2564bbda51c1fa4bd1e1e" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/img/mode_0_on.svg", + "sha256": "96720a81f0315caf6c70664de934e49d22e6dd070a9f6ca1fec2a9a96af4d138" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/img/mode_1_off.svg", + "sha256": "3ccd22bf3648dc260cd1d0b6a60a9226a853764ae20efb8fe9c940cd856c81b2" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/img/mode_1_on.svg", + "sha256": "2238ca27e3c7c4c687d10de13b794d8be70ae1c5ba9225ebad27f251cfe5f12a" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/img/mode_2_off.svg", + "sha256": "31423e3d67c00839adc77dd871dc1da55f77c8b83b5a37b445e4b4c3f46c6d6d" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/img/mode_2_on.svg", + "sha256": "96b4c9db8d5055c8777fda74cb5e0f32b3e12e62a1e0bacbf75d2088f4505e2f" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/img/off.svg", + "sha256": "4e6743ee28002e16117caf47ea3bffb34ca75bcc4118744b4ee720829ae3b563" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/img/on.svg", + "sha256": "cc0fb7048bff6dc3f14a17ed549209c67e9bc34368cb1571e0abd8035c7b59b7" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/img/style.svg", + "sha256": "9034ea7b9a214a6d4c086c02cf85f42b1c4487cec0906e919fc91e99dfff529a" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/index.ts", + "sha256": "4146d0a23b59e39db26966b689437c27ff137a395f4ff0c4b94f37d0628ecf87" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/input.ts", + "sha256": "a9340c7031f486e22074a8def633fba47eb22c2a745fc5d6e6ddac988b9c6839" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/parser-types.ts", + "sha256": "f91cf9afc4947a12a79a44f51db7117a08d833db2cb2fca025935a3ac9358046" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/queue.ts", + "sha256": "74bb239ef0d7bfa8b754930f829ee9d76f0d15b33d1b7eb0f84364a9580b8f04" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/renderer.ts", + "sha256": "6ac4b1991c6cb683353a811f41b6ca025401029720cbcadaff120616ca936cdf" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/scheduler.ts", + "sha256": "9e343bd2528fd98ea61e4ba75db7e4db5f029619b54f4f9ce89670e957cda22b" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/setting-lifecycle.ts", + "sha256": "4580ce961a4b9ca9d6a481667306204778fdd46b404fbfa232e28db97ab74db0" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/setting-send.ts", + "sha256": "b6abbaf3a5e29d42cffb41de44e1ec3760fa3c3223e1b0476e9c8a57f94fd650" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/setting-slider.ts", + "sha256": "f06dd34babc0d369cc741d1eb98638aa90a516f59b317fa97a7a0cb2f343cb1d" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/setting-style.ts", + "sha256": "47a9a52fee7b9565febb91f8db0f7027d9e77ea5c5dff9220c8425803cca5057" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/setting-template.ts", + "sha256": "136609371f91b057d7c589fc32ae7510a0359edc794628c6f1ace33ac6a10406" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/setting-types.ts", + "sha256": "a09efe0f780940838e75ea0428a3e775bd75145799120e1b2f5ad38e9f8d08dd" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/setting.ts", + "sha256": "9273fd8869364efb12fb90e9f45674fb0d5f9e49675acb2c2500850f0386ee5a" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/style.less", + "sha256": "a497a7c79c9e8329874eec30f7da436ed35a2f80a35048df1eb7c8f1fe34d6c3" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/types.ts", + "sha256": "745fadee438b3bd232f759063e4d78059d52663b9c0be66af0c6a33676d51f86" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/worker-assets.d.ts", + "sha256": "3582ccd87626600d9d29c6fa82f812012ea389bc03b39e4edcb4345d02372751" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/worker-client.ts", + "sha256": "252b048c67123d296c277d5c1e92ff0f36d919e4c47588497e2da3ca7b4ca881" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/worker-types.ts", + "sha256": "6595241341a2ed7c0d01120addadf42dcca306fa1282098084d5d0b6c4e32608" + }, + { + "file": "packages/artplayer-plugin-danmuku/src/worker.ts", + "sha256": "9342c3ad2247edb79afb6834b9ca0ed2e026a3f95b66af7609feca234e81ef5b" + }, + { + "file": "packages/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku.d.ts", + "sha256": "3162fc3db91bff06f0709fa0e6ca78de05053e25436ca215846a6267d4680e53" + }, + { + "file": "packages/artplayer-plugin-danmuku/types/runtime-shared.d.ts", + "sha256": "52556c8228c0947fb74a4335a204521378099a6a855d83e48e4f5aec1e34944d" + }, + { + "file": "packages/artplayer-plugin-danmuku/types/runtime.d.cts", + "sha256": "9b5073ca622762c367e79161968f6c31e1f3f73b412d3e049a0d845cdac2c832" + }, + { + "file": "packages/artplayer-plugin-danmuku/types/runtime.d.mts", + "sha256": "564a6ed25fbb363603b825e84ae0298cfcc69c3d007fd56c616dbd20cec4a0a9" + }, + { + "file": "packages/artplayer-plugin-danmuku/types/runtime.d.ts", + "sha256": "9b5073ca622762c367e79161968f6c31e1f3f73b412d3e049a0d845cdac2c832" + }, + { + "file": "packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.js", + "sha256": "17899cc03cc39c3a440e8d2db8b5ffbae05aff81f6c8426071df2a5eb1b7ce42" + }, + { + "file": "packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.legacy.js", + "sha256": "83da521eda70a111ac0d655028b7bdfb0f4d4167bcda2890adab4ad679b48d99" + }, + { + "file": "packages/artplayer-plugin-danmuku/dist/artplayer-plugin-danmuku.mjs", + "sha256": "adfc131c6d4c524ce91098ce9fc16feb9e87142f37f17a3ff772e68734b0fd38" + }, + { + "file": "packages/artplayer-plugin-danmuku/package.json", + "sha256": "563f92ac14666c7600b0af623bd77d7ff71f1faebb851a1d780207ac8952f0a5" + }, + { + "file": "packages/artplayer-plugin-danmuku/tsconfig.json", + "sha256": "e77f21e3d898f815361f940427ac977adfcc40140972c320aace52a9038a9ea1" + }, + { + "file": "packages/artplayer-plugin-danmuku/README.md", + "sha256": "d393b6862951242f258a579ea539e4a408599decf49925265c43dbee3bae3f37" + }, + { + "file": "packages/artplayer-plugin-danmuku/ARCHITECTURE.md", + "sha256": "bb52ee9be8d44c3f69f5e1d322a66b07c09fcf0f6882bd0730d0452bc1795dd0" + }, + { + "file": "docs/assets/ts/artplayer-plugin-danmuku.d.ts", + "sha256": "e3421bdbdfc6bea1b0b633300350fff1a745d8bc21c1430b6154a23fa3ad0415" + }, + { + "file": "docs/uncompiled/artplayer-plugin-danmuku/index.js", + "sha256": "bfd1dfd78ed228e3ec81e19135a8af0538610dcaca91895be7d978bf8a1eaf9b" + }, + { + "file": "test/helpers/danmuku-candidate.js", + "sha256": "5df8bb0eb98331c5e8eae60dd8893e92a402d37741037feebc170a08a64bb64a" + }, + { + "file": "test/danmuku-scheduler.test.js", + "sha256": "8bede957b936e1a14c0b4e6830077b72ef54dfc9e6896b54b6d17b7691bab661" + }, + { + "file": "test/danmuku-worker-client.test.js", + "sha256": "8721a129cd8cb68d8289efa7e53a61e3ea07ced19788b80b9735d540ca4d943a" + }, + { + "file": "test/danmuku-renderer.test.js", + "sha256": "0f7d3218524ad724982d661c11591a7336638feb718212c014c57cfdde322bdd" + }, + { + "file": "test/types/danmuku-root.ts", + "sha256": "a5a4b55bed906d56c24e07ee4a478bb9edf6ad3c5c3ee0755996c8facd96a8d3" + }, + { + "file": "test/types/danmuku-runtime.ts", + "sha256": "5fde19071bf34d4ee0d431f1f707bc283947ce09988097fa0670af70bd70d5d5" + }, + { + "file": "refactor/fixtures/implementation/danmuku.ts", + "sha256": "7ffc8b313dc36bb4fea65e0a3c7401f55e929fdb5c30a0940bfc4b5bc38c4296" + }, + { + "file": "refactor/scripts/danmuku-types.test.mjs", + "sha256": "0fb962c65cc10cdfa07ca60c1bffe65b1fefd25716c9e45b54d4d11548b40242" + }, + { + "file": "refactor/scripts/danmuku-package-types.mjs", + "sha256": "859e96e989896f09a6bc225f3cbc4d40540808369e13a99d71976d3b87e1012c" + } + ], + "remaining": [ + "PKG-DANMUKU-07", + "PKG-DANMUKU-08", + "PKG-DANMUKU-09", + "DANMUKU-LOAD-01", + "All-package major preparation and release review gates" + ] +} diff --git a/refactor/changes/2026-09-13-PKG-DANMUKU-06-types.md b/refactor/changes/2026-09-13-PKG-DANMUKU-06-types.md new file mode 100644 index 000000000..138ed2a91 --- /dev/null +++ b/refactor/changes/2026-09-13-PKG-DANMUKU-06-types.md @@ -0,0 +1,99 @@ +# PKG-DANMUKU-06 自有 TypeScript 与兼容声明 + +本步将前面已拆分的20个自有运行模块迁移为严格TypeScript,明确输入、队列、 +Worker协议、UI与生命周期边界。任务完成状态以tasks.json为准。 + +## 实现与兼容边界 + +- 入口保持同步插件门面;Danmuku拥有配置与队列,scheduler负责时钟与有效操作, + renderer负责节点,WorkerClient负责请求身份与终止,worker只计算轨道。 + input、bilibili及parser保持请求/解析/取消分层;设置和热图继续使用05/10的模块边界。 +- 新增内部types、worker-types、parser-types、setting-types及Worker资源声明。 + 公开运行数据由types/runtime-shared.d.ts单一维护,内部通过type-only转发使用; + DOM宿主与Worker协议留在src。不存在新的运行时模块环或消费者依赖。 +- 类字段使用declare,不改变原初始化顺序或新增undefined自有属性。保留同步数组 + 加载前缀、Promise/owner返回身份、回调receiver、默认参数、事件与原DOM属性写入。 + 取消符号明确区分异步结果;没有为了通过类型检查改变轨道算法。 +- 受控断言用于旧核心constructor声明、已初始化的设置节点、已分配的活动弹幕节点、 + DOM WebIDL的原始数值/null赋值,以及历史无效margin字符串回退。它们不替代输入 + 验证,不引入全局any。旧postMessage空对象默认值仍保留,未知协议不产生回复。 +- 分包tsconfig禁用allowJs。ES2021.String仅描述既有parser.replaceAll,不新增该 + 运行时要求;现代es2020与legacy es2015构建目标不变。本包没有自有遗留JS或vendor。 + 模块地图、生命周期与维护指引见包内ARCHITECTURE.md。 + +## 公开类型与消费者 + +实际npm 5.3.0根声明完整保留,换行归一后SHA256为 +`006c3e2d22425b936d6d09bf5138b0ae13c4cfffd0a571d03722c17137dbe99c`。 +旧根/legacy的参数、返回提取和普通替代工厂关系不变;没有给工厂增加必填default。 +新增可选/runtime路径指向相同分发文件,只提供准确声明: + +- 工厂参数对象仍必传,但对象内字段可选。保留弹幕扩展字段;输入可为数组、URL、 + Promise或返回数组/Promise的函数,config可以传部分字段。 +- emit/load返回Promise,同步命令返回Owner,Owner与注册门面分别描述; + 门面的option/isHide/isStop只读getter不会使内部配置对象变为深只读。 +- filter接收填默认值后的数据,beforeVisible接收队列项;两者按truthy判断, + beforeEmit严格比较true。回调this为实际NormalizedOption。 +- mount需要有效目标。icons描述真实可写属性;Slider与margin元组、heatmap坐标和 + 配置保持实际格式。points配置目前只保存,自定义绘图由points事件触发,保留内层 + 元组mutation。EventMap提供明确payload,不全局改写旧核心事件声明。 +- 旧根的同步emit结果和对象points属于历史声明错误,切换/runtime后需按真实行为 + 使用await及元组;继续使用旧入口无需因本次迁移修改代码。实际运行并未被旧声明 + 所描述的行为替换,具体示例见README及type-compatibility-policy.md。 + +四项类型测试覆盖真实声明归档、正反例、实现赋值和语义编辑器生成。安装测试在 +仓库外实际Yarn pack/install并离线冻结重装,逐成员核对字节,检查CJS/ESM身份。 +12个编译器/解析模式组合包括TS5.9.3与4.3.5;候选每个适用模式有4个root和14个 +runtime负例,逐个去掉expect-error确认相应错误实际出现。 +旧根在NodeNext ESM默认namespace方式的7个历史诊断与候选逐代码/行号比较, +显式namespace适配通过;保留失败事实,不把它包装成全部旧TS用法均无错误。 + +build:ts把Danmuku纳入语义声明生成,更新docs编辑器资产;不手改生成声明。 +.npmignore排除实现tsconfig,公开类型仍随包分发。新增test:danmuku-types及 +test:danmuku-types-package脚本,前者被既有test:baseline自动发现。本步无新依赖, +唯一yarn.lock不变,版本递增仍由统一版本任务执行。 + +## 测试与证据 + +最终机器证据见[验收报告](../baselines/danmuku-types-validation.json)。 +完整源码250项单测通过,正式main和legacy各164项联合测试通过;严格类型检查覆盖 +全仓390个生产TS文件。源码/main/legacy最终各156项真实浏览器通过,共468项, +每轮2并发、零重试、零跳过;覆盖实际发布5.4.0与候选5.4.1核心以及Chromium、 +Firefox和Windows WebKit。正常构建、类型编辑器生成、目标lint、冻结安装及严格 +Node24.21.0/Yarn1.22.22工具链检查通过;构建产物与docs复制逐字节相同。 +源码单测、正式main/legacy联合测试包含共同内部模块与历史对照,不能把每一项都 +计为独立产物测试。Worker加载器会执行选中产物内嵌Blob或data URL的实际字节; +只有源码受控模式才单独编译worker.ts。冻结的历史helper没有修改。 + +内置浏览器用正常yarn dev打开8082现有danmuku示例,本地视频和XML实际加载。 +Chrome/152.0.0.0,视频播放至11.711807秒且解码宽640;发送 +`TS migration browser check`后输入清空;暂停在43.744183秒,再次读取保持同值。 +真实弹幕可见,页面warn/error记录为空。原生Browser.getVersion不支持,改用受支持 +CDP只读navigator.userAgent取版本;之前失效tab的CDP焦点超时不作为页面失败。 +这只是开发示例smoke,不替代发布包、Safari真机或07的持续负载验收。 + +## 失败处置与未完成范围 + +- 第一版实际pack发现实现tsconfig泄漏,修正.npmignore后重做完整隔离安装。 +- 实现赋值fixture最初放在根test/types,导致根配置缺少分包Worker资源和既有 + ES2021.String声明;移至refactor/fixtures/implementation,并用分包配置检查。 + 负例诊断行计算也修正了跨空白行匹配;保留失败日志,不弱化类型断言。 +- 最终独立复查更正架构文档仍指向旧位置的实现fixture,并对最终包内容重新执行 + 隔离打包安装;不使用修正文档前的归档作为最终候选证据。 +- 产物Worker测试首次只识别Blob,实际正式构建使用百分号编码data URL。增加精确 + data URL解码后执行内嵌算法,保持未知来源断言,不用当前源码冒充产物。 +- 一轮浏览器复测提前启动下一轮,共享结果目录被覆盖,导致最后两项trace清理失败; + 下一轮同时遇到端口占用。保留日志,改为一个串行命令,上一进程结束后归档再继续。 +- 随后source矩阵154通过、2个WebKit uniform场景超时。trace显示published首次 + Play客户端13.17秒而浏览器动作仅0.39秒;candidate启动/建页/导航合计16.23秒, + 都在插件安装前。16k加载分别0.995秒与2.96秒;published超时后几何断言实际通过, + 但整项仍记失败。证据支持宿主或自动化延迟,不能确认更具体的系统原因。 + 其他重型检查结束后,原断言/20秒超时不变,单worker WebKit诊断6项通过;完整 + 最终矩阵单独归档,不关闭历史负载疑点,也不以重跑抹去首次失败。 + +07继续承担持续负载、渲染/资源对比及Mask稳定依赖,08承担完整组合,09承担分发 +与文档最终验收。DANMUKU-LOAD-01仍保持待验证;本步不声称npm候选或全部重构完成。 +三轮发布复盘、设备/外部SDK证据、全包独立major以及远端CI仍按原计划。 + +回退:单独revert本任务提交,恢复05/10的JS实现及原类型分发;#958独立修复不回退。 +只提交本任务实现、测试、文档和状态,不推送或发布。 diff --git a/refactor/fixtures/consumers/danmuku-published.ts b/refactor/fixtures/consumers/danmuku-published.ts new file mode 100644 index 000000000..86ba485d3 --- /dev/null +++ b/refactor/fixtures/consumers/danmuku-published.ts @@ -0,0 +1,27 @@ +import type Artplayer from 'artplayer' +import type { Danmu, Option, Result } from 'artplayer-plugin-danmuku' +import danmuku from 'artplayer-plugin-danmuku' + +type Equal = (() => T extends A ? 1 : 2) extends (() => T extends B ? 1 : 2) ? true : false +type Assert = T +type Factory = (option: Option) => (art: Artplayer) => Result +type SameArguments = Assert, [option: Option]>> +type SameResult = Assert>, Result>> +type SameEmit = Assert, Result>> +type SameLoad = Assert, Promise>> +type SamePoints = Assert> +type SameDanmuKeys = Assert> + +declare const art: Artplayer +declare const result: Result +const replacement: Factory = (_option: Option) => (_art: Artplayer) => result +const toOld: Factory = danmuku +const fromOld: typeof danmuku = replacement +const option: Option = { danmuku: [], points: [{ time: 1, value: 2 }], margin: [0, '25%'], beforeEmit: async () => true } +const plugin: Result = danmuku(option)(art) +const synchronousOldEmit: Result = plugin.emit({ text: 'old declaration', mode: 1 }) +const loaded: Promise = plugin.load(Promise.resolve([{ text: 'reload' }])) +const configured: Result = plugin.config(option) +const mounted: void = plugin.mount('#external') +void [toOld, fromOld, synchronousOldEmit, loaded, configured, mounted] +export type { SameArguments, SameDanmuKeys, SameEmit, SameLoad, SamePoints, SameResult } diff --git a/refactor/fixtures/implementation/danmuku.ts b/refactor/fixtures/implementation/danmuku.ts new file mode 100644 index 000000000..43f2b8425 --- /dev/null +++ b/refactor/fixtures/implementation/danmuku.ts @@ -0,0 +1,6 @@ +import type { RuntimeFactory } from 'artplayer-plugin-danmuku/runtime' +import factory from '../../../packages/artplayer-plugin-danmuku/src' + +// Compile with the package configuration: the implementation imports Worker assets. +const implementation: RuntimeFactory = factory +export { implementation } diff --git a/refactor/plan.md b/refactor/plan.md index 21f5c5cae..ff02a27e7 100644 --- a/refactor/plan.md +++ b/refactor/plan.md @@ -4,7 +4,7 @@ 基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 229 项,范围 22 个包及工作区/示例。 -状态:todo 69 / doing 15 / blocked 0 / done 145 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 +状态:todo 68 / doing 15 / blocked 0 / done 146 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。 前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。 @@ -314,8 +314,8 @@ | PKG-DANMUKU-03 | artplayer-plugin-danmuku
整理加载、解析与配置 | PKG-DANMUKU-02, CORE-08 | bilibili/input/parser/config 的分层与取消 | 旧格式、回调、过滤、追加/替换语义保持 | M | done | | PKG-DANMUKU-04 | artplayer-plugin-danmuku
整理时钟、队列与轨道调度 | PKG-DANMUKU-03, CORE-10 | danmuku 调度器和确定性时钟测试 | seek/暂停/倍率/长时间运行无顺序和碰撞回归 | H | done | | PKG-DANMUKU-05 | artplayer-plugin-danmuku
整理 DOM 渲染、设置、热力图与 worker | PKG-DANMUKU-04, CORE-14, CORE-18 | renderer/setting/heatmap/worker 职责及资源归属 | mount/icons/设置和 worker 协议保持,销毁无后台工作 | H | done | -| PKG-DANMUKU-06 | artplayer-plugin-danmuku
迁移 TS 与公开声明 | PKG-DANMUKU-05, ENG-04, ENG-06 | 自有模块、worker 消息、option/item/result 类型 | 旧参数、扩展字段和事件消费通过,vendored 边界清楚 | H | doing | -| PKG-DANMUKU-07 | artplayer-plugin-danmuku
弹幕能力稳定性验收 | PKG-DANMUKU-06, PKG-DANMUKU-10 | 负载/渲染/资源对比及 mask 可依赖的稳定边界 | 在试点核心中旧插件 API 与关键帧路径通过 | H | todo | +| PKG-DANMUKU-06 | artplayer-plugin-danmuku
迁移 TS 与公开声明 | PKG-DANMUKU-05, ENG-04, ENG-06 | 自有模块、worker 消息、option/item/result 类型 | 旧参数、扩展字段和事件消费通过,vendored 边界清楚 | H | done | +| PKG-DANMUKU-07 | artplayer-plugin-danmuku
弹幕能力稳定性验收 | PKG-DANMUKU-06, PKG-DANMUKU-10 | 负载/渲染/资源对比及 mask 可依赖的稳定边界 | 在试点核心中旧插件 API 与关键帧路径通过 | H | doing | | PKG-DANMUKU-08 | artplayer-plugin-danmuku
完成新旧核心与组合验收 | PKG-DANMUKU-07, CORE-22 | 最终核心/旧核心、mask/fullscreen/PiP 组合报告 | 性能无未解释退化,反复装卸无累计资源 | H | todo | | PKG-DANMUKU-09 | artplayer-plugin-danmuku
完成分发、示例与文档 | PKG-DANMUKU-08, ENG-07 | danmuku.js、README、声明、worker 与产物 | tarball 和静态 icons 等旧调用通过,有独立回退版本 | H | todo | | PKG-DANMUKU-10 | artplayer-plugin-danmuku
修复 issue958 密集热力图过高与曲线裁平 | PKG-DANMUKU-05 | 自动密度缩放与曲线边界、历史自定义坐标兼容及真实浏览器红绿证据 | 16000条三种分布在两核心三引擎和source/main/legacy不遮挡;显式轴和自定义points保持旧解释,有单独commit | M | done | @@ -559,6 +559,7 @@ - PKG-DANMUKU-03: [记录](changes/2026-09-13-PKG-DANMUKU-03-input.md) [记录](baselines/danmuku-input-validation.json) - PKG-DANMUKU-04: [记录](changes/2026-09-13-PKG-DANMUKU-04-scheduler.md) [记录](baselines/danmuku-scheduler-validation.json) - PKG-DANMUKU-05: [记录](changes/2026-09-13-PKG-DANMUKU-05-resources.md) [记录](baselines/danmuku-resources-validation.json) +- PKG-DANMUKU-06: [记录](changes/2026-09-13-PKG-DANMUKU-06-types.md) [记录](baselines/danmuku-types-validation.json) - PKG-CANVAS-01: [记录](changes/2026-09-12-PKG-CANVAS-01-contract.md) [记录](baselines/canvas-contract.md) [记录](baselines/canvas-release.json) [记录](canvas-validation.md) [记录](baselines/canvas-contract-validation.json) - PKG-CANVAS-02: [记录](changes/2026-09-12-PKG-CANVAS-02-tests.md) [记录](baselines/canvas-behavior-validation.json) [记录](canvas-validation.md) - PKG-CANVAS-03: [记录](changes/2026-09-12-PKG-CANVAS-03-lifecycle.md) [记录](baselines/canvas-lifecycle-validation.json) [记录](canvas-validation.md) diff --git a/refactor/progress.md b/refactor/progress.md index f558980af..379588818 100644 --- a/refactor/progress.md +++ b/refactor/progress.md @@ -1,5 +1,20 @@ # 进度与证据 +## PKG-DANMUKU-06 自有 TS 与公开声明完成 + +20个自有运行模块完成严格TS,新增内部状态/Worker/parser/UI类型边界;公开运行 +数据单一维护。实际npm5.3.0根声明完整保留,新增同实现/runtime准确描述Promise、 +owner、getter、回调和数据;语义docs编辑器声明同步生成。包架构和维护命令已更新。 +完整源码250项、main/legacy各164项联合单测、4项类型检查、12组仓库外安装消费者 +通过;全仓390生产TS严格检查、冻结Yarn安装、工具链、构建和目标lint通过。 +三格式/两核心/三引擎最终468项浏览器通过,原断言/超时不变、无重试/跳过;内置 +Chrome152完成现有docs示例实际播放、发送与暂停。Worker data URL测试加载器、 +先前报告目录冲突和WebKit超时均保留原因与失败证据,未把重跑等同历史从未失败。 +关闭DANMUKU-TYPE-01/02;LOAD-01与稳定性/组合/分发及发布复盘继续保留。 +见[变更](changes/2026-09-13-PKG-DANMUKU-06-types.md)和[机器证据](baselines/danmuku-types-validation.json)。 +229项:146 done、15 doing、68 todo;07进入负载、渲染与资源稳定性验收。 +本地独立任务提交,不推送或发布。 + ## PKG-DANMUKU-10:issue #958 密集热力图修复 用户新增的16000条弹幕热图过高/裁平已独立复现并修复。自动密度拟合到底部四分之一, diff --git a/refactor/risk-table.md b/refactor/risk-table.md index 35b849a29..7dfa36278 100644 --- a/refactor/risk-table.md +++ b/refactor/risk-table.md @@ -227,11 +227,11 @@ | MASK-BACKEND-01 | open / 已复现 | Mask TensorFlow backend fallback does not establish MediaPipe fallback and failed initialization keeps scheduling | PKG-MASK-02, PKG-MASK-03, PKG-MASK-05 | | MASK-DOM-01 | open / 已复现 | Mask registration and frame processing assume Danmuku DOM, readable frames and a 2D context | PKG-MASK-02, PKG-MASK-03, PKG-MASK-05 | | MASK-NOTICE-01 | open / 源码/产物事实 | Published Mask bundles lack a separate third-party notice despite bundled SDK code | PKG-MASK-06 | -| DANMUKU-TYPE-01 | open / 已复现 | Danmuku historical declared results, optional mount and initial Promise input disagree with runtime | PKG-DANMUKU-03, PKG-DANMUKU-06 | +| DANMUKU-TYPE-01 | resolved / 已复现 | Danmuku historical declared results, optional mount and initial Promise input disagree with runtime | PKG-DANMUKU-03, PKG-DANMUKU-06 | | DANMUKU-INPUT-01 | resolved / 已复现 | Danmuku replaces time zero with current time plus 0.5 and mutates supplied input objects | PKG-DANMUKU-02, PKG-DANMUKU-03 | | DANMUKU-CONFIG-01 | resolved / 已复现 | Danmuku JSON configuration comparison ignores a replacement filter function | PKG-DANMUKU-02, PKG-DANMUKU-03 | | DANMUKU-CLEANUP-01 | resolved / 已复现 | Danmuku removes a different resize callback and retains the settings lock timer on destroy | PKG-DANMUKU-02, PKG-DANMUKU-05 | -| DANMUKU-TYPE-02 | open / 源码/产物事实 | Danmuku id, icons, Slider steps, heatmap points and partial configuration drift from declarations | PKG-DANMUKU-05, PKG-DANMUKU-06 | +| DANMUKU-TYPE-02 | resolved / 源码/产物事实 | Danmuku id, icons, Slider steps, heatmap points and partial configuration drift from declarations | PKG-DANMUKU-05, PKG-DANMUKU-06 | | DANMUKU-ASYNC-01 | resolved / 已复现 | Danmuku asynchronous input, worker replies and pending visibility have unverified failure and cancellation paths | PKG-DANMUKU-02, PKG-DANMUKU-03, PKG-DANMUKU-04, PKG-DANMUKU-05 | | DANMUKU-HEATMAP-01 | resolved / 已复现 | Danmuku heatmap sampling can become zero and points/instance ownership need browser validation | PKG-DANMUKU-02, PKG-DANMUKU-05 | | DANMUKU-SOURCE-01 | open / 已复现 | Danmuku registry Git associations differ from releases and static icon provenance remains incomplete | PKG-DANMUKU-09 | diff --git a/refactor/risks.json b/refactor/risks.json index cc5dde2b6..2c604ef43 100644 --- a/refactor/risks.json +++ b/refactor/risks.json @@ -5023,7 +5023,7 @@ "id": "DANMUKU-TYPE-01", "title": "Danmuku historical declared results, optional mount and initial Promise input disagree with runtime", "confirmation": "reproduced", - "status": "open", + "status": "resolved", "owners": [ "PKG-DANMUKU-03", "PKG-DANMUKU-06" @@ -5033,10 +5033,17 @@ "refactor/baselines/danmuku-contract-validation.json", "refactor/baselines/danmuku-release.json", "refactor/changes/2026-09-13-PKG-DANMUKU-03-input.md", - "refactor/baselines/danmuku-input-validation.json" + "refactor/baselines/danmuku-input-validation.json", + "refactor/changes/2026-09-13-PKG-DANMUKU-06-types.md", + "refactor/baselines/danmuku-types-validation.json" ], "compatibleResolution": "Preserve latest published complete root type contracts and actual legal JS return identities; expose accurate types separately.", - "closureCriteria": "Installed consumers verify latest root assignments and precise async/getter/mount contracts; initial Promise mismatch has an explicit tested resolution." + "closureCriteria": "Installed consumers verify latest root assignments and precise async/getter/mount contracts; initial Promise mismatch has an explicit tested resolution.", + "resolutionEvidence": [ + "refactor/changes/2026-09-13-PKG-DANMUKU-06-types.md", + "refactor/baselines/danmuku-types-validation.json" + ], + "resolutionRationale": "Preserved actual npm 5.3.0 complete root/legacy declaration and installed consumer behavior; optional runtime entry describes tested async owner, getter, mount, data, icon and callback contracts. Verified actual implementation assignability and packed/offline-installed current and historical compiler consumers." }, { "id": "DANMUKU-INPUT-01", @@ -5127,7 +5134,7 @@ "id": "DANMUKU-TYPE-02", "title": "Danmuku id, icons, Slider steps, heatmap points and partial configuration drift from declarations", "confirmation": "source-observed", - "status": "open", + "status": "resolved", "owners": [ "PKG-DANMUKU-05", "PKG-DANMUKU-06" @@ -5135,10 +5142,17 @@ "evidence": [ "refactor/baselines/danmuku-contract.md", "refactor/baselines/danmuku-contract-validation.json", - "refactor/baselines/danmuku-release.json" + "refactor/baselines/danmuku-release.json", + "refactor/changes/2026-09-13-PKG-DANMUKU-06-types.md", + "refactor/baselines/danmuku-types-validation.json" ], "compatibleResolution": "Validate actual accepted data and static properties, retain root compatibility and document effective fields.", - "closureCriteria": "Positive and negative installed consumers and source behavior tests cover these fields without widening away old factory contracts." + "closureCriteria": "Positive and negative installed consumers and source behavior tests cover these fields without widening away old factory contracts.", + "resolutionEvidence": [ + "refactor/changes/2026-09-13-PKG-DANMUKU-06-types.md", + "refactor/baselines/danmuku-types-validation.json" + ], + "resolutionRationale": "Preserved actual npm 5.3.0 complete root/legacy declaration and installed consumer behavior; optional runtime entry describes tested async owner, getter, mount, data, icon and callback contracts. Verified actual implementation assignability and packed/offline-installed current and historical compiler consumers." }, { "id": "DANMUKU-ASYNC-01", diff --git a/refactor/scripts/danmuku-package-types.mjs b/refactor/scripts/danmuku-package-types.mjs new file mode 100644 index 000000000..2e56c8328 --- /dev/null +++ b/refactor/scripts/danmuku-package-types.mjs @@ -0,0 +1,140 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import path from 'node:path' +import process from 'node:process' +import ts from 'typescript' +import compat from 'typescript-compat' +import { checkFiles, packedFiles } from '../../scripts/package-check.mjs' +import { consumerDirectory, removeConsumer, run, workspace, writeJson } from '../../scripts/package-consumer.mjs' +import { verifyDanmukuContract } from './danmuku-contract.mjs' +import { hash, readMember } from './releases.mjs' + +const publishedFixture = 'refactor/fixtures/consumers/danmuku-published.ts' +const rootFixture = 'test/types/danmuku-root.ts' +const runtimeFixture = 'test/types/danmuku-runtime.ts' + +async function main() { + assert.equal(process.env.npm_config_user_agent?.split(' ')[0], 'yarn/1.22.22', 'Use yarn test:danmuku-types-package') + const yarn = process.env.npm_execpath + assert(yarn && fs.existsSync(yarn)) + const output = fs.mkdtempSync(path.join(workspace, 'refactor/.cache/danmuku-package-types-')) + console.log(`Danmuku isolated package evidence: ${output}`) + const contract = await verifyDanmukuContract() + assert.equal(contract.baseline.release.version, '5.3.0') + const fixtures = Object.fromEntries([publishedFixture, rootFixture, runtimeFixture].map(file => [file, fs.readFileSync(path.join(workspace, file), 'utf8')])) + const packages = [] + for (const name of ['artplayer', 'artplayer-plugin-danmuku']) { + const archive = path.join(output, `${name}.tgz`) + fs.writeFileSync(path.join(output, `${name}-pack.log`), run([yarn, 'pack', '--filename', archive], path.join(workspace, 'packages', name))) + const manifest = JSON.parse(readMember(archive, 'package/package.json')) + const files = packedFiles(archive) + checkFiles(manifest, files, name === contract.baseline.release.name ? Object.keys(contract.baseline.release.files) : []) + packages.push({ name, archive, sha256: hash(fs.readFileSync(archive)), files: Object.fromEntries(files.map(member => [member, hash(readMember(archive, member))])) }) + } + const published = { ...contract.baseline.release, archive: contract.archives.get('5.3.0'), label: 'published-5.3.0' } + const declaration = 'package/types/artplayer-plugin-danmuku.d.ts' + assert.equal(readMember(packages[1].archive, declaration).toString().replaceAll('\r\n', '\n'), readMember(published.archive, declaration).toString().replaceAll('\r\n', '\n'), 'Preserve the actual npm 5.3.0 root declaration') + const matrix = [] + const historical = new Map() + for (const plugin of [published, { ...packages[1], label: 'candidate' }]) { + const consumer = consumerDirectory() + try { + writeJson(path.join(consumer, 'package.json'), { name: 'danmuku-isolated-consumer', private: true, dependencies: Object.fromEntries([packages[0], plugin].map(pkg => [pkg.name, `file:${pkg.archive.replaceAll('\\', '/')}`])) }) + fs.copyFileSync(path.join(workspace, 'yarn.lock'), path.join(consumer, 'yarn.lock')) + fs.writeFileSync(path.join(output, `${plugin.label}-install.log`), run([yarn, 'install', '--offline', '--ignore-scripts', '--non-interactive'], consumer)) + const lock = fs.readFileSync(path.join(consumer, 'yarn.lock')) + fs.writeFileSync(path.join(output, `${plugin.label}-frozen-install.log`), run([yarn, 'install', '--offline', '--frozen-lockfile', '--ignore-scripts', '--non-interactive'], consumer)) + assert.deepEqual(fs.readFileSync(path.join(consumer, 'yarn.lock')), lock) + fs.writeFileSync(path.join(output, `${plugin.label}-yarn.lock`), lock) + for (const pkg of [packages[0], plugin]) { + const root = path.join(consumer, 'node_modules', pkg.name) + assert.equal(fs.realpathSync(root), root, 'Installed package must not be a workspace link') + for (const [member, expected] of Object.entries(pkg.files)) + assert.equal(hash(fs.readFileSync(path.join(root, member.slice(8)))), expected, `Installed bytes differ: ${member}`) + } + const runtimeFile = path.join(consumer, 'exports.cjs') + fs.writeFileSync(runtimeFile, `const assert = require('node:assert/strict'); +const names = ${JSON.stringify(plugin.label === 'candidate' ? ['artplayer-plugin-danmuku', 'artplayer-plugin-danmuku/legacy', 'artplayer-plugin-danmuku/runtime'] : ['artplayer-plugin-danmuku', 'artplayer-plugin-danmuku/legacy'])}; +for (const name of names) { const factory = require(name); assert.equal(typeof factory, 'function'); assert.equal(typeof factory({danmuku: []}), 'function'); assert.equal(typeof factory.icons, 'object'); } +${plugin.label === 'candidate' ? 'assert.equal(require(\'artplayer-plugin-danmuku/runtime\'), require(\'artplayer-plugin-danmuku\'));' : ''} +Promise.all(names.map(name => import(name))).then(modules => { for (const value of modules) assert.equal(typeof value.default, 'function'); assert.equal(modules[1].default, require(names[1])); ${plugin.label === 'candidate' ? 'assert.equal(modules[2].default, modules[0].default);' : ''} }).catch(error => { console.error(error); process.exitCode = 1; });`) + fs.writeFileSync(path.join(output, `${plugin.label}-runtime.log`), run([runtimeFile], consumer)) + const modes = [[ts, 'node10-commonjs'], [compat, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm']] + if (plugin.label === 'candidate') + modes.push([ts, 'node10-commonjs-no-interop'], [compat, 'node10-commonjs-no-interop']) + for (const [compiler, mode] of modes) { + const next = mode.startsWith('nodenext') + const filename = path.join(consumer, `consumer.${next ? mode.endsWith('-cjs') ? 'cts' : 'mts' : 'ts'}`) + const options = { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: !mode.endsWith('-no-interop'), target: compiler.ScriptTarget.ES2020, lib: ['lib.es2020.d.ts', 'lib.dom.d.ts'], module: next ? compiler.ModuleKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleKind.ESNext : compiler.ModuleKind.CommonJS, moduleResolution: next ? compiler.ModuleResolutionKind.NodeNext : mode === 'bundler-esm' ? compiler.ModuleResolutionKind.Bundler : compiler.ModuleResolutionKind.NodeJs } + function compile(source) { + fs.writeFileSync(filename, source) + const program = compiler.createProgram([filename], options) + for (const file of program.getSourceFiles()) { + const actual = fs.realpathSync(file.fileName) + assert(actual.startsWith(fs.realpathSync(consumer) + path.sep) || (program.isSourceFileDefaultLibrary(file) && path.dirname(actual) === fs.realpathSync(path.dirname(compiler.sys.getExecutingFilePath()))), `Types escaped installed consumer: ${actual}`) + } + return compiler.getPreEmitDiagnostics(program).map(item => ({ code: item.code, line: item.file && item.start !== undefined ? item.file.getLineAndCharacterOfPosition(item.start).line + 1 : null, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n').replaceAll(consumer.replaceAll('\\', '/'), '') })) + } + function checkedFixture(source, label) { + const diagnostics = compile(source) + assert.deepEqual(diagnostics, [], `${label}: ${compiler.version} ${mode}`) + const lines = [] + let line = 1 + for (const text of source.split('\n')) { + if (/^[ \t]*\/\/ @ts-expect-error/.test(text)) + lines.push(line) + else line++ + } + assert.equal(lines.length, label === 'root/legacy' ? 4 : 14, `Keep all negative ${label} fixtures`) + const invalid = compile(source.replaceAll(/^[ \t]*\/\/ @ts-expect-error[^\n]*\n/gm, '')) + assert.deepEqual([...new Set(invalid.map(item => item.line))].sort((a, b) => a - b), lines, `Every ${label} negative statement must fail at its own line`) + return { diagnostics, invalid, negativeStatements: lines.length } + } + const direct = compile(fixtures[publishedFixture]) + const key = `${compiler.version}:${mode}` + if (plugin.label === 'published-5.3.0') { + if (mode === 'nodenext-esm') { + assert(direct.length > 0, 'Preserve evidence of the existing NodeNext default namespace mismatch') + assert(direct.every(item => [2322, 2344, 2349].includes(item.code)), 'Only the established callable namespace mismatch is expected') + } + else { + assert.deepEqual(direct, []) + } + historical.set(key, direct.map(({ code, line }) => ({ code, line }))) + } + else { + assert.deepEqual(direct.map(({ code, line }) => ({ code, line })), historical.get(key) ?? [], 'Candidate must preserve exact npm root diagnostic codes and positions') + } + const namespace = mode === 'nodenext-esm' ? compile(fixtures[publishedFixture].replace('import danmuku from \'artplayer-plugin-danmuku\'', 'import danmukuModule from \'artplayer-plugin-danmuku\'\nconst danmuku = danmukuModule.default')) : [] + assert.deepEqual(namespace, [], 'Explicit published NodeNext namespace remains valid') + let root = null + let runtime = null + let commonjs = [] + if (plugin.label === 'candidate') { + const rootSource = mode === 'nodenext-esm' + ? fixtures[rootFixture].replace('import danmuku from \'artplayer-plugin-danmuku\'', 'import danmukuModule from \'artplayer-plugin-danmuku\'\nconst danmuku = danmukuModule.default').replace('import legacy from \'artplayer-plugin-danmuku/legacy\'', 'import legacyModule from \'artplayer-plugin-danmuku/legacy\'\nconst legacy = legacyModule.default') + : fixtures[rootFixture] + root = checkedFixture(rootSource, 'root/legacy') + const runtimeSource = mode.endsWith('-no-interop') ? fixtures[runtimeFixture].replace('import runtime from \'artplayer-plugin-danmuku/runtime\'', 'import runtime = require(\'artplayer-plugin-danmuku/runtime\')') : fixtures[runtimeFixture] + runtime = checkedFixture(runtimeSource, 'runtime') + if (mode !== 'nodenext-esm' && mode !== 'bundler-esm') { + commonjs = compile(`import root = require('artplayer-plugin-danmuku'); import legacy = require('artplayer-plugin-danmuku/legacy'); import runtime = require('artplayer-plugin-danmuku/runtime');\nconst replacement: typeof root.default = (_option) => (_art) => { throw new Error('consumer'); }; const other: typeof legacy.default = replacement; root.default({danmuku: []}); legacy.default({danmuku: []}); runtime({danmuku: []}); void other;`) + assert.deepEqual(commonjs, [], 'Installed require declarations preserve old factories and callable runtime') + } + } + matrix.push({ plugin: plugin.label, compiler: compiler.version, mode, direct, namespace, root, runtime, commonjs }) + writeJson(path.join(output, 'matrix-progress.json'), matrix) + } + } + finally { + removeConsumer(consumer) + } + } + writeJson(path.join(output, 'report.json'), { suite: 'danmuku-isolated-package-types', introducedBy: 'PKG-DANMUKU-06', scope: 'Actual npm 5.3.0 and packed candidate outside workspace; offline and frozen reinstall, exact installed bytes, unchanged root declaration, full factory replacement and extraction. Old synchronous emit and object points remain root typing only. Runtime types are checked separately. NodeNext root namespace failures are compared by exact codes and line positions. This is not real playback or complete publication acceptance.', fixtures: Object.fromEntries(Object.entries(fixtures).map(([file, source]) => [file, hash(source)])), packages, published: { archive: published.archive, sha256: published.sha256 }, matrix }) + console.log(`Danmuku installed types passed: ${matrix.length} cases; ${output}`) +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/refactor/scripts/danmuku-types.test.mjs b/refactor/scripts/danmuku-types.test.mjs new file mode 100644 index 000000000..f9caa7da1 --- /dev/null +++ b/refactor/scripts/danmuku-types.test.mjs @@ -0,0 +1,64 @@ +import assert from 'node:assert/strict' +import fs from 'node:fs' +import path from 'node:path' +// eslint-disable-next-line test/no-import-node-test -- Public declaration compatibility uses Node. +import test from 'node:test' +import ts from 'typescript' +import compat from 'typescript-compat' +import { generateCoreEditorDeclaration } from '../../scripts/editor-types.mjs' +import { checkPluginEditorDeclaration, generatePluginEditorDeclaration } from '../../scripts/plugin-editor-types.mjs' +import { checkConsumer } from '../../scripts/typecheck.mjs' +import { ensureArchive, readMember } from './releases.mjs' + +const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']] +const read = file => fs.readFileSync(file, 'utf8').replaceAll('\r\n', '\n') +const declaration = 'packages/artplayer-plugin-danmuku/types/artplayer-plugin-danmuku.d.ts' + +test('Danmuku root declaration retains the exact latest npm 5.3.0 type source', async () => { + const { release } = JSON.parse(read('refactor/baselines/danmuku-release.json')) + const archive = await ensureArchive(release) + assert.equal(read(declaration), readMember(archive, 'package/types/artplayer-plugin-danmuku.d.ts').toString().replaceAll('\r\n', '\n')) +}) + +test('Danmuku root and runtime consumers retain extraction, ownership and meaningful negative checks', () => { + for (const [compiler, mode] of modes) { + for (const fixture of ['danmuku-root', 'danmuku-runtime']) { + let source = read(`test/types/${fixture}.ts`) + if (fixture === 'danmuku-root' && mode === 'nodenext-esm') { + source = source.replace('import danmuku from \'artplayer-plugin-danmuku\'', 'import danmukuModule from \'artplayer-plugin-danmuku\'\nconst danmuku = danmukuModule.default') + .replace('import legacy from \'artplayer-plugin-danmuku/legacy\'', 'import legacyModule from \'artplayer-plugin-danmuku/legacy\'\nconst legacy = legacyModule.default') + } + assert.deepEqual(checkConsumer(compiler, mode, source), [], `${fixture}: ${compiler.version}/${mode}`) + const expected = [] + let line = 1 + for (const text of source.split('\n')) { + if (/^\s*\/\/ @ts-expect-error/u.test(text)) + expected.push(line) + else line++ + } + assert.equal(expected.length, fixture === 'danmuku-root' ? 4 : 14) + const invalid = checkConsumer(compiler, mode, source.replaceAll(/^[\t ]*\/\/ @ts-expect-error[^\n]*\n/gmu, '')) + assert.deepEqual(invalid.map(item => item.line).sort((a, b) => a - b), expected, 'Every invalid consumer statement must fail at its own line') + } + } +}) + +test('Danmuku runtime declarations are assignable from the real strict TypeScript implementation', () => { + const filename = path.resolve('packages/artplayer-plugin-danmuku/tsconfig.json') + const config = ts.readConfigFile(filename, ts.sys.readFile) + assert.equal(config.error, undefined) + const parsed = ts.parseJsonConfigFileContent(config.config, ts.sys, path.dirname(filename)) + const program = ts.createProgram([...parsed.fileNames, path.resolve('refactor/fixtures/implementation/danmuku.ts')], parsed.options) + assert.deepEqual(ts.getPreEmitDiagnostics(program).map(item => ts.flattenDiagnosticMessageText(item.messageText, '\n')), []) +}) + +test('Danmuku editor declarations retain the callable global and exported historical names', () => { + const generated = generatePluginEditorDeclaration(read(declaration), 'artplayerPluginDanmuku') + const core = generateCoreEditorDeclaration() + const consumer = `const option: artplayerPluginDanmuku.Option = {danmuku: [], points: [{time: 1, value: 2}]}; +const art = new Artplayer({container: '#player', url: '/video.mp4'}); +const result: artplayerPluginDanmuku.Result = artplayerPluginDanmuku(option)(art); +const oldReturn: artplayerPluginDanmuku.Result = result.emit({text: 'legacy editor'}); void oldReturn;` + for (const compiler of [ts, compat]) + assert.deepEqual(checkPluginEditorDeclaration(generated, core, consumer, compiler), []) +}) diff --git a/refactor/tasks.json b/refactor/tasks.json index fb4d7825e..b6aab4542 100644 --- a/refactor/tasks.json +++ b/refactor/tasks.json @@ -3362,11 +3362,14 @@ "ENG-04", "ENG-06" ], - "status": "doing", + "status": "done", "risk": "H", "deliverable": "自有模块、worker 消息、option/item/result 类型", "acceptance": "旧参数、扩展字段和事件消费通过,vendored 边界清楚", - "evidence": [] + "evidence": [ + "changes/2026-09-13-PKG-DANMUKU-06-types.md", + "baselines/danmuku-types-validation.json" + ] }, { "id": "PKG-DANMUKU-07", @@ -3379,7 +3382,7 @@ "PKG-DANMUKU-06", "PKG-DANMUKU-10" ], - "status": "todo", + "status": "doing", "risk": "H", "deliverable": "负载/渲染/资源对比及 mask 可依赖的稳定边界", "acceptance": "在试点核心中旧插件 API 与关键帧路径通过", diff --git a/refactor/type-compatibility-policy.md b/refactor/type-compatibility-policy.md index 823b90edc..755cbafe1 100644 --- a/refactor/type-compatibility-policy.md +++ b/refactor/type-compatibility-policy.md @@ -28,3 +28,10 @@ Chromecast的PKG-CAST-04沿用此规则保留最新npm 1.1.0根声明,不引 破坏。1.0.0的export=与1.1.0的default export已有历史差异,分别保留测试证据; 准确异步注册、回调和查询类型通过新增/runtime提供。此处记录规则的应用, 不声称用户另行确认过Cast专属问题,也不扩大到未验证的运行时变化。 + +Danmuku的PKG-DANMUKU-06保留实际npm 5.3.0根声明的完整类型形状与字节(忽略 +换行格式),包括历史同步emit结果、必填danmuku、对象形points和NodeNext ESM的 +既有namespace限制。新增/runtime描述真实Promise/owner结果、可选配置字段、tuple +points、静态icons和回调receiver;根/legacy/runtime复用原分发实现,不添加不存在 +的factory.default。旧入口用户不需要因本次类型迁移修改代码。切换/runtime是可选 +的准确类型入口,历史声明与运行时的区别见包README;这里没有新增旧类型破坏授权。 diff --git a/scripts/build-ts.js b/scripts/build-ts.js index 58130c89c..d442e8823 100644 --- a/scripts/build-ts.js +++ b/scripts/build-ts.js @@ -53,7 +53,7 @@ console.log(`✨ Built ${artplayerTSoutput}`); const type = pluginsTS[index] const { name, file } = parsePluginInfo(type) const source = String(fs.readFileSync(type)) - const semanticPlugin = ['artplayerPluginChromecast', 'artplayerPluginAsr', 'artplayerPluginMultipleSubtitles', 'artplayerPluginVttThumbnail', 'artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl', 'artplayerPluginAds', 'artplayerPluginAmbilight', 'artplayerProxyCanvas', 'artplayerProxyMediabunny', 'artplayerPluginDocumentPip', 'ArtplayerToolIframe', 'ArtplayerToolThumbnail'].includes(name) + const semanticPlugin = ['artplayerPluginDanmuku', 'artplayerPluginChromecast', 'artplayerPluginAsr', 'artplayerPluginMultipleSubtitles', 'artplayerPluginVttThumbnail', 'artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl', 'artplayerPluginAds', 'artplayerPluginAmbilight', 'artplayerProxyCanvas', 'artplayerProxyMediabunny', 'artplayerPluginDocumentPip', 'ArtplayerToolIframe', 'ArtplayerToolThumbnail'].includes(name) const localTypes = name === 'artplayerProxyMediabunny' ? { './media': fs.readFileSync(path.join(path.dirname(type), 'media.d.ts'), 'utf8') } : {} const code = semanticPlugin ? generatePluginEditorDeclaration(source, name, localTypes) diff --git a/test/README.md b/test/README.md index 67f1e3af6..b291e340a 100644 --- a/test/README.md +++ b/test/README.md @@ -70,3 +70,15 @@ volume/rate, independent instances and cleanup. Published-only tests retain defe See [audio validation](../refactor/audio-validation.md) for actual media tests and limitations. Document PiP: `yarn test:dpip` runs 48 historical window/DOM/lifecycle cases. Native DOM iframe checks use `yarn test:browser test/browser/dpip.spec.js`; controlled window APIs do not establish native Document PiP support. See [validation notes](../refactor/dpip-validation.md). + +Danmuku: `yarn test:danmuku` covers historical behavior and current input, scheduling, +settings, rendering and heatmap boundaries. `ARTPLAYER_DANMUKU_ARTIFACT` selects a +built candidate for integration cases; direct internal tests remain source tests. +The candidate Worker helper executes the selected artifact's Blob or data URL bytes; +only source-mode controlled imports compile `worker.ts` separately. The frozen +published helper remains unchanged. `yarn test:danmuku-types` checks the unchanged +npm root, the optional accurate `/runtime` entry, implementation assignability and +semantic editor declarations. `yarn test:danmuku-types-package` packs and installs +the packages outside the workspace, verifies exact member bytes and frozen offline +reinstallation, and checks historical and current compiler consumers. These type +checks do not replace the native browser suite or final distribution acceptance. diff --git a/test/browser/README.md b/test/browser/README.md index 9f1845e5f..660b343ce 100644 --- a/test/browser/README.md +++ b/test/browser/README.md @@ -128,3 +128,14 @@ both response modes. A passing diagnostic only proves observation plus successfu playback after release; inspect its progressed/waiting flags before claiming buffering. These capability observation timeouts do not relax the integration assertions. Archive the report and results (including traces) before starting another browser suite. + +Danmuku input, scheduler, resources and heatmap-density specs accept +`ARTPLAYER_DANMUKU_ARTIFACT` for a built main/legacy file; unset it to compile the +current JS/TS entry. Run each format sequentially and wait for the command's final +exit before copying reports or launching the next format. A visible last test line +does not prove reporter/trace teardown is finished. These runs share the server port +and output directory, so simultaneous invocations can corrupt failure evidence. +For a timeout, inspect the retained trace before changing the test or implementation; +browser startup and pre-plugin playback delays are different from plugin execution. +The density regression retains 16000 rows, real playback and SVG/pixel geometry at +two widths. A diagnostic pass does not waive the full matrix or long-load acceptance. diff --git a/test/danmuku-renderer.test.js b/test/danmuku-renderer.test.js index 4183ca79e..5bf7a243d 100644 --- a/test/danmuku-renderer.test.js +++ b/test/danmuku-renderer.test.js @@ -2,7 +2,9 @@ import assert from 'node:assert/strict' // eslint-disable-next-line test/no-import-node-test -- Deterministic owned-node tests use the Node runner. import test from 'node:test' import { parseHTML } from 'linkedom' -import Renderer from '../packages/artplayer-plugin-danmuku/src/renderer.js' +import { loadModules } from './helpers/load.js' + +const { Renderer } = await loadModules({ Renderer: 'packages/artplayer-plugin-danmuku/src/renderer' }) function fixture(t) { const { document } = parseHTML('
') diff --git a/test/danmuku-scheduler.test.js b/test/danmuku-scheduler.test.js index 6ef37a364..bb0fbb918 100644 --- a/test/danmuku-scheduler.test.js +++ b/test/danmuku-scheduler.test.js @@ -1,9 +1,8 @@ import assert from 'node:assert/strict' -import fs from 'node:fs' // eslint-disable-next-line test/no-import-node-test -- Deterministic candidate scheduling uses the Node runner. import test from 'node:test' import vm from 'node:vm' -import { danmukuCandidate, danmukuCandidateEnvironment } from './helpers/danmuku-candidate.js' +import { danmukuCandidate, danmukuCandidateEnvironment, danmukuWorkerCode } from './helpers/danmuku-candidate.js' import { deferred } from './helpers/danmuku.js' const implementation = await danmukuCandidate() @@ -44,10 +43,7 @@ async function fixture(options = {}, { automatic = false } = {}) { return child } const worker = env.workers[0] - const actualBlob = env.urls.get(worker.url) - const workerCode = actualBlob - ? await actualBlob.text() - : fs.readFileSync(new URL('../packages/artplayer-plugin-danmuku/src/worker.js', import.meta.url), 'utf8') + const workerCode = await danmukuWorkerCode(implementation, env, worker) const responses = [] const workerContext = vm.createContext({ postMessage: data => responses.push(structuredClone(data)), URL: { revokeObjectURL() {} }, location: { href: worker.url } }) workerContext.self = workerContext diff --git a/test/danmuku-worker-client.test.js b/test/danmuku-worker-client.test.js index 00382ffeb..10d4e0a33 100644 --- a/test/danmuku-worker-client.test.js +++ b/test/danmuku-worker-client.test.js @@ -1,9 +1,8 @@ import assert from 'node:assert/strict' -import fs from 'node:fs' // eslint-disable-next-line test/no-import-node-test -- Controlled client faults and real worker geometry use the Node runner. import test from 'node:test' import vm from 'node:vm' -import { danmukuCandidate, danmukuCandidateEnvironment } from './helpers/danmuku-candidate.js' +import { danmukuCandidate, danmukuCandidateEnvironment, danmukuWorkerCode } from './helpers/danmuku-candidate.js' import { loadModules } from './helpers/load.js' const { WorkerClient } = await loadModules({ WorkerClient: 'packages/artplayer-plugin-danmuku/src/worker-client' }) @@ -223,13 +222,18 @@ test('danmuku worker client: independent clients never share pending ownership', second.client.dispose() }) -// Unlike the client double above, this executes the actual unmodified worker -// script. Numeric geometry inputs are controlled; no browser layout is claimed. -const workerCode = fs.readFileSync(new URL('../packages/artplayer-plugin-danmuku/src/worker.js', import.meta.url), 'utf8') +// Unlike the client double above, this executes the actual source or artifact +// worker. Numeric geometry inputs are controlled; no browser layout is claimed. +const workerEnvironment = danmukuCandidateEnvironment(implementation) +workerEnvironment.factory({ danmuku: [] })(workerEnvironment.art) +const actualWorker = workerEnvironment.workers[0] +const workerCode = await danmukuWorkerCode(implementation, workerEnvironment, actualWorker) +workerEnvironment.destroy() function calculateGeometry(input) { const replies = [] - const context = vm.createContext({ input, postMessage: data => replies.push(data) }) + const context = vm.createContext({ input, postMessage: data => replies.push(data), URL: { revokeObjectURL() {} }, location: { href: actualWorker.url } }) + context.self = context vm.runInContext(workerCode, context, { timeout: 1000 }) vm.runInContext('onmessage({ data: input })', context, { timeout: 1000 }) assert.equal(replies.length, 1) diff --git a/test/helpers/danmuku-candidate.js b/test/helpers/danmuku-candidate.js index 6e6c2ce43..16969edb6 100644 --- a/test/helpers/danmuku-candidate.js +++ b/test/helpers/danmuku-candidate.js @@ -1,3 +1,4 @@ +import assert from 'node:assert/strict' import fs from 'node:fs' import path from 'node:path' import process from 'node:process' @@ -6,8 +7,31 @@ import { build } from 'esbuild' import { ensureArchive, readMember, refactorDir } from '../../refactor/scripts/releases.mjs' import { getEntryFile } from '../../scripts/projects.js' import { danmukuEnvironment } from './danmuku.js' +import { resolveSource } from './load.js' const workspace = fileURLToPath(new URL('../../', import.meta.url)) +let sourceWorker + +export async function danmukuWorkerCode(implementation, env, worker) { + const blob = env.urls.get(worker.url) + if (blob) + return blob.text() + // Production worker-loader emits a percent-encoded data URL. Execute those + // exact shipped bytes instead of silently substituting the current source. + const dataPrefix = 'data:text/javascript;charset=utf-8,' + if (typeof worker.url === 'string' && worker.url.startsWith(dataPrefix)) + return decodeURIComponent(worker.url.slice(dataPrefix.length)) + assert.equal(implementation.name, 'candidate-source', 'Artifact Worker tests must execute the Worker embedded in that artifact') + sourceWorker ??= build({ + entryPoints: [resolveSource('packages/artplayer-plugin-danmuku/src/worker')], + bundle: true, + write: false, + format: 'iife', + platform: 'browser', + target: 'es2020', + }).then(result => result.outputFiles[0].text) + return sourceWorker +} export async function danmukuCandidate() { const coreRelease = JSON.parse(fs.readFileSync(path.join(refactorDir, 'baselines/releases.json'), 'utf8')).releases.find(item => item.name === 'artplayer') diff --git a/test/types/danmuku-root.ts b/test/types/danmuku-root.ts new file mode 100644 index 000000000..56b3c584f --- /dev/null +++ b/test/types/danmuku-root.ts @@ -0,0 +1,22 @@ +import type { Option, Result } from 'artplayer-plugin-danmuku' +import danmuku from 'artplayer-plugin-danmuku' +import legacy from 'artplayer-plugin-danmuku/legacy' + +const legacyFactory: typeof danmuku = legacy +const rootFactory: typeof legacy = danmuku +const option: Option = { danmuku: [], points: [{ time: 1, value: 2 }] } +danmuku(option) +legacy(option) +declare const result: Result +const synchronous: Result = result.emit({ text: 'historical declaration' }) +void [legacyFactory, rootFactory, synchronous] + +// @ts-expect-error The published root requires its option argument. +danmuku() +// @ts-expect-error The published root requires danmuku in Option. +danmuku({}) +// @ts-expect-error Preserve historical object points at the root. +danmuku({ danmuku: [], points: [[1, 2]] }) +// @ts-expect-error Root emit keeps its published synchronous Result shape. +const asynchronous: Promise = result.emit({ text: 'old emit' }) +void asynchronous diff --git a/test/types/danmuku-runtime.ts b/test/types/danmuku-runtime.ts new file mode 100644 index 000000000..4e58e4c0d --- /dev/null +++ b/test/types/danmuku-runtime.ts @@ -0,0 +1,108 @@ +import type { Danmu, EventMap, Icons, Input, Item, NormalizedDanmu, NormalizedOption, Owner, Point, RuntimeFactory, RuntimeOption, RuntimeResult, Slider } from 'artplayer-plugin-danmuku/runtime' +import Artplayer from 'artplayer' +import runtime from 'artplayer-plugin-danmuku/runtime' + +type Equal = (() => T extends A ? 1 : 2) extends (() => T extends B ? 1 : 2) ? true : false +type Assert = T +type FactoryArguments = Assert, [option: RuntimeOption]>> +type FactoryReturns = Assert>, RuntimeResult>> +type EmitReturns = Assert, Promise>> +type LoadReturns = Assert, Promise>> +type CommandReturns = Assert, Owner>> +type OwnerHasNoFacadeKeys = Assert, never>> +type VisiblePayload = Assert> +type LoadedPayload = Assert> + +function expectOption(_option: NormalizedOption) {} +const row: Danmu = { text: 'extended input', id: 'message-1', author: { name: 'test' }, style: { fontWeight: 'bold' } } +const input: Input = () => [row] +const asyncInput: Input = async () => [row] +const point: Point = [10, 5] +const margin: Slider<[number | string, number | string]> = { steps: [{ name: 'half', value: [10, '50%'], hide: true, show: false }] } +const options: RuntimeOption = { + danmuku: input, + points: [point], + MARGIN: margin, + SPEED: { steps: [{ name: 'fast', value: 2.5, hide: true }] }, + filter(danmu) { + expectOption(this) + const normalized: NormalizedDanmu = danmu + const time: number = danmu.time + const extension: unknown = danmu.author + // @ts-expect-error Queue allocation has not run when filter is invoked. + const state: string = danmu.$state + void [normalized, time, extension, state] + return true + }, + async beforeEmit(danmu) { + expectOption(this) + const text: string = danmu.text + void text + return true + }, + async beforeVisible(item) { + expectOption(this) + const state: 'wait' | 'ready' | 'emit' | 'stop' = item.$state + const index: number = item.$index + const node: HTMLDivElement | null = item.$ref + const extra: unknown = item.author + void [state, index, node, extra] + return true + }, +} +const art = new Artplayer({ container: '#player', url: '/video.mp4', plugins: [runtime(options)] }) +const factory: RuntimeFactory = runtime +const result = runtime({})(art) +const registered: 'artplayerPluginDanmuku' = result.name +const icon: string = runtime.icons.$check_on +const icons: Icons = runtime.icons +runtime.icons = icons +runtime.icons.$on = '' +const promise: Promise = result.emit(row) +const loading: Promise = result.load(asyncInput) +const replacing: Promise = result.load() +const owner: Owner = result.config({ opacity: 0.5 }) +const stopped: Owner = owner.stop().start().suspend().continue().reset() +const configured: NormalizedOption = result.option +result.option.visible = false +result.mount(document.createElement('div')) +result.mount('#danmuku-mount') +const loadedPayload: EventMap['artplayerPluginDanmuku:loaded'] = [owner.queue] +const pointsPayload: EventMap['artplayerPluginDanmuku:points'] = [[point]] +const failedPayload: EventMap['artplayerPluginDanmuku:error'] = [new Error('failed')] + +async function consume() { + const emitted = await result.emit({ text: 'await owner' }) + const rows: Item[] = emitted.queue + const next: Owner = emitted.hide().show() + // @ts-expect-error The resolved owner does not expose the facade name. + void emitted.name + // @ts-expect-error The resolved owner does not own setting.mount. + emitted.mount('#mount') + return [rows, next] +} +// @ts-expect-error The factory still requires its options object. +runtime() +// @ts-expect-error Undefined is not a valid options object. +runtime(undefined) +// @ts-expect-error An emitted row requires text. +result.emit({ id: 'missing-text' }) +// @ts-expect-error The runtime heatmap uses mutable pairs, not historical object points. +runtime({ points: [{ time: 1, value: 2 }] }) +// @ts-expect-error Margin sliders carry pairs rather than scalar numbers. +runtime({ MARGIN: { steps: [{ value: 10 }] } }) +// @ts-expect-error A live mount needs an explicit destination. +result.mount() +// @ts-expect-error Facade option is a getter, although the returned options object is mutable. +result.option = configured +// @ts-expect-error Facade isHide is a getter. +result.isHide = true +// @ts-expect-error Facade isStop is a getter. +result.isStop = true +// @ts-expect-error emit returns a Promise; synchronous chaining is not available. +result.emit(row).hide() +// @ts-expect-error The factory has icons but no default self-reference. +void runtime.default + +export type { CommandReturns, EmitReturns, FactoryArguments, FactoryReturns, LoadedPayload, LoadReturns, OwnerHasNoFacadeKeys, VisiblePayload } +void [factory, registered, icon, promise, loading, replacing, stopped, loadedPayload, pointsPayload, failedPayload, consume]