mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
refactor(ambilight): [PKG-AMBILIGHT-04] preserve published types and dual CommonJS exports
This commit is contained in:
1 parent
6caee44dbf
commit
959cccf515
30 files changed
+877
-69
No files matched your search
+32
-17
@@ -1,20 +1,35 @@
|
||||
interface Option {
|
||||
blur?: string
|
||||
opacity?: number
|
||||
frequency?: number
|
||||
zIndex?: number
|
||||
duration?: number
|
||||
// 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 artplayerPluginAmbilight {
|
||||
interface Option {
|
||||
/** CSS blur radius. @default '50px' */
|
||||
blur?: string
|
||||
/** Grid cell opacity. @default 0.5 */
|
||||
opacity?: number
|
||||
/** Maximum sampling frequency in frames per second. @default 10 */
|
||||
frequency?: number
|
||||
/** Historical input retained for compatibility; runtime uses a fixed z-index of 9. */
|
||||
zIndex?: number
|
||||
/** Background color transition duration in seconds. @default 0.3 */
|
||||
duration?: number
|
||||
}
|
||||
interface Result {
|
||||
name: 'artplayerPluginAmbilight'
|
||||
/** Start sampling; does nothing after the player is destroyed. */
|
||||
start: () => void
|
||||
/** Stop sampling while retaining the last colors. */
|
||||
stop: () => void
|
||||
}
|
||||
interface Callable {
|
||||
(option?: Option): (art: Artplayer) => Result
|
||||
/** Keep Parameters extraction identical to the published 1.1.0 signature. */
|
||||
(option: Option): (art: Artplayer) => Result
|
||||
}
|
||||
interface Factory extends Callable {
|
||||
/** Same function, including historical require(package).default calls. */
|
||||
readonly default: Factory
|
||||
}
|
||||
}
|
||||
|
||||
interface Result {
|
||||
name: 'artplayerPluginAmbilight'
|
||||
start: () => void
|
||||
stop: () => void
|
||||
}
|
||||
|
||||
declare const artplayerPluginAmbilight: (option: Option) => (art: Artplayer) => Result
|
||||
|
||||
export default artplayerPluginAmbilight
|
||||
|
||||
declare const artplayerPluginAmbilight: artplayerPluginAmbilight.Factory
|
||||
export = artplayerPluginAmbilight
|
||||
export as namespace artplayerPluginAmbilight;
|
||||
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(t.artplayerPluginAmbilight=n(),define(function(){return t.artplayerPluginAmbilight})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAmbilight=n()}(this,function(){"use strict";function t(t,n,e){let r="stopped",o=null,i=0,l=!1,u=0;function c(){const s=i,f=()=>"running"===r&&s===i;l=!0;try{const r=performance.now();if(r-u<1e3/t||!n()||!f())return;u=r,e(f)}finally{l=!1,"running"===r&&null===o&&function(){const t=i;try{o=requestAnimationFrame(()=>{"running"===r&&t===i&&(o=null,c())})}catch(n){throw r="stopped",i++,n}}()}}return{start:function(){"stopped"===r&&(r="running",i++,l||c())},stop:function(){if("destroyed"===r)return;r="stopped",i++;const t=o;o=null,null!==t&&cancelAnimationFrame(t)},destroy:function(){if("destroyed"===r)return;r="destroyed",i++;const t=o;o=null,null!==t&&cancelAnimationFrame(t)}}}return function(n={}){return e=>{let r=e,o=e.isDestroy,i=null,l=null,u=null;const c=[],s={name:"artplayerPluginAmbilight",start(){o||u?.start()},stop(){o||u?.stop()}};if(o)return r=null,s;function f(){const t=[u?.destroy,l?.destroy,i?.destroy];u=null,l=null,i=null;const n=[];for(const r of t)try{r?.()}catch(e){n.push(e)}if(n.length)throw n[0]}function a(){if(o)return;o=!0;const t=r;r=null;const n=[];for(const[r,o]of c.splice(0))try{t?.off(r,o)}catch(e){n.push(e)}try{f()}catch(e){n.push(e)}if(n.length)throw n[0]}function d(t,n){c.push([t,n]),r?.on(t,n)}try{if(d("destroy",a),o)return s;const{$video:c}=e.template,h=e.constructor.utils,{blur:y="50px",opacity:p=.5,frequency:g=10,duration:m=.3}=n;if(o)return s;if(i=function(t){const n=t.createElement("div");n.innerHTML=Array.from({length:9}).fill("<div></div>").join("");const e=Array.from(n.children);return{mount:function(r,o,i){if(i()&&(t.addClass(n,"artplayer-plugin-ambilight"),i()&&(r.parentNode.insertBefore(n,r),i()))){t.setStyles(n,{position:"absolute",top:0,left:0,zIndex:9,inset:0,display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gridTemplateRows:"1fr 1fr 1fr"});for(const n of e){if(!i())return;t.setStyles(n,{opacity:o.opacity,filter:`blur(${o.blur})`,transition:`background-color ${o.duration}s ease`})}}},render:function(t,n){for(let r=0;r<t.length;r++){if(!n())return;const o=e[r],i=t[r];o&&void 0!==i&&(o.style.backgroundColor=i)}},destroy:function(){try{n.parentNode?.removeChild(n)}finally{e.length=0}}}}(h),o||i.mount(c,{blur:y,opacity:p,duration:m},()=>!o),o)return f(),s;const b=h.createElement("canvas");if(o)return b.width=0,b.height=0,s;if(l=function(t,n,e){let r;try{r=t.getContext("2d"),t.width=3,t.height=3}catch(l){throw t.width=0,t.height=0,l}let o=!1,i=!1;return{read:function(l){if(o||!l())return null;const u=n.videoWidth,c=n.videoHeight;if(!Number.isFinite(u)||!Number.isFinite(c)||u<=0||c<=0)return null;const s=u/3,f=c/3,a=[];try{if(i){const n=e();if(o||!l())return n.width=0,n.height=0,null;if(r=null,r=(t=n).getContext("2d"),o||!l())return t.width=0,t.height=0,null;t.width=3,t.height=3,i=!1}if(!r)return null;for(let t=0;t<3;t++)for(let e=0;e<3;e++){if(o||!l())return null;if(r.drawImage(n,e*s,t*f,s,f,0,0,1,1),o||!l())return null;const[i,u,c]=r.getImageData(0,0,1,1).data;a.push(`rgb(${i}, ${u}, ${c})`)}return a}catch{return o||(i=!0,t.width=0,t.height=0),null}},destroy:function(){o||(o=!0,t.width=0,t.height=0)}}}(b,c,()=>h.createElement("canvas")),o)return f(),s;u=t(g,()=>Boolean(r?.playing),t=>{const n=l?.read(t);n&&t()&&i?.render(n,t)}),d("ready",s.start),o&&f()}catch(h){try{a()}catch{}try{f()}catch{}throw h}return s}}});
|
||||
!function(t,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(t.artplayerPluginAmbilight=n(),define(function(){return t.artplayerPluginAmbilight})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAmbilight=n()}(this,function(){"use strict";function t(t,n,e){let r="stopped",i=null,o=0,l=!1,u=0;function c(){const s=o,f=()=>"running"===r&&s===o;l=!0;try{const r=performance.now();if(r-u<1e3/t||!n()||!f())return;u=r,e(f)}finally{l=!1,"running"===r&&null===i&&function(){const t=o;try{i=requestAnimationFrame(()=>{"running"===r&&t===o&&(i=null,c())})}catch(n){throw r="stopped",o++,n}}()}}return{start:function(){"stopped"===r&&(r="running",o++,l||c())},stop:function(){if("destroyed"===r)return;r="stopped",o++;const t=i;i=null,null!==t&&cancelAnimationFrame(t)},destroy:function(){if("destroyed"===r)return;r="destroyed",o++;const t=i;i=null,null!==t&&cancelAnimationFrame(t)}}}function n(n={}){return e=>{let r=e,i=e.isDestroy,o=null,l=null,u=null;const c=[],s={name:"artplayerPluginAmbilight",start(){i||u?.start()},stop(){i||u?.stop()}};if(i)return r=null,s;function f(){const t=[u?.destroy,l?.destroy,o?.destroy];u=null,l=null,o=null;const n=[];for(const r of t)try{r?.()}catch(e){n.push(e)}if(n.length)throw n[0]}function a(){if(i)return;i=!0;const t=r;r=null;const n=[];for(const[r,i]of c.splice(0))try{t?.off(r,i)}catch(e){n.push(e)}try{f()}catch(e){n.push(e)}if(n.length)throw n[0]}function d(t,n){c.push([t,n]),r?.on(t,n)}try{if(d("destroy",a),i)return s;const{$video:c}=e.template,h=e.constructor.utils,{blur:y="50px",opacity:p=.5,frequency:g=10,duration:m=.3}=n;if(i)return s;if(o=function(t){const n=t.createElement("div");n.innerHTML=Array.from({length:9}).fill("<div></div>").join("");const e=Array.from(n.children);return{mount:function(r,i,o){if(o()&&(t.addClass(n,"artplayer-plugin-ambilight"),o()&&(r.parentNode.insertBefore(n,r),o()))){t.setStyles(n,{position:"absolute",top:0,left:0,zIndex:9,inset:0,display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gridTemplateRows:"1fr 1fr 1fr"});for(const n of e){if(!o())return;t.setStyles(n,{opacity:i.opacity,filter:`blur(${i.blur})`,transition:`background-color ${i.duration}s ease`})}}},render:function(t,n){for(let r=0;r<t.length;r++){if(!n())return;const i=e[r],o=t[r];i&&void 0!==o&&(i.style.backgroundColor=o)}},destroy:function(){try{n.parentNode?.removeChild(n)}finally{e.length=0}}}}(h),i||o.mount(c,{blur:y,opacity:p,duration:m},()=>!i),i)return f(),s;const b=h.createElement("canvas");if(i)return b.width=0,b.height=0,s;if(l=function(t,n,e){let r;try{r=t.getContext("2d"),t.width=3,t.height=3}catch(l){throw t.width=0,t.height=0,l}let i=!1,o=!1;return{read:function(l){if(i||!l())return null;const u=n.videoWidth,c=n.videoHeight;if(!Number.isFinite(u)||!Number.isFinite(c)||u<=0||c<=0)return null;const s=u/3,f=c/3,a=[];try{if(o){const n=e();if(i||!l())return n.width=0,n.height=0,null;if(r=null,r=(t=n).getContext("2d"),i||!l())return t.width=0,t.height=0,null;t.width=3,t.height=3,o=!1}if(!r)return null;for(let t=0;t<3;t++)for(let e=0;e<3;e++){if(i||!l())return null;if(r.drawImage(n,e*s,t*f,s,f,0,0,1,1),i||!l())return null;const[o,u,c]=r.getImageData(0,0,1,1).data;a.push(`rgb(${o}, ${u}, ${c})`)}return a}catch{return i||(o=!0,t.width=0,t.height=0),null}},destroy:function(){i||(i=!0,t.width=0,t.height=0)}}}(b,c,()=>h.createElement("canvas")),i)return f(),s;u=t(g,()=>Boolean(r?.playing),t=>{const n=l?.read(t);n&&t()&&o?.render(n,t)}),d("ready",s.start),i&&f()}catch(h){try{a()}catch{}try{f()}catch{}throw h}return s}}return Object.assign(n,{default:n})});
|
||||
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(t.artplayerPluginAmbilight=n(),define(function(){return t.artplayerPluginAmbilight})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAmbilight=n()}(this,function(){"use strict";function t(t,n,e){let r="stopped",o=null,i=0,l=!1,u=0;function c(){const s=i,f=()=>"running"===r&&s===i;l=!0;try{const r=performance.now();if(r-u<1e3/t||!n()||!f())return;u=r,e(f)}finally{l=!1,"running"===r&&null===o&&function(){const t=i;try{o=requestAnimationFrame(()=>{"running"===r&&t===i&&(o=null,c())})}catch(n){throw r="stopped",i++,n}}()}}return{start:function(){"stopped"===r&&(r="running",i++,l||c())},stop:function(){if("destroyed"===r)return;r="stopped",i++;const t=o;o=null,null!==t&&cancelAnimationFrame(t)},destroy:function(){if("destroyed"===r)return;r="destroyed",i++;const t=o;o=null,null!==t&&cancelAnimationFrame(t)}}}return function(n={}){return e=>{let r=e,o=e.isDestroy,i=null,l=null,u=null;const c=[],s={name:"artplayerPluginAmbilight",start(){o||null==u||u.start()},stop(){o||null==u||u.stop()}};if(o)return r=null,s;function f(){const t=[null==u?void 0:u.destroy,null==l?void 0:l.destroy,null==i?void 0:i.destroy];u=null,l=null,i=null;const n=[];for(const r of t)try{null==r||r()}catch(e){n.push(e)}if(n.length)throw n[0]}function a(){if(o)return;o=!0;const t=r;r=null;const n=[];for(const[r,o]of c.splice(0))try{null==t||t.off(r,o)}catch(e){n.push(e)}try{f()}catch(e){n.push(e)}if(n.length)throw n[0]}function d(t,n){c.push([t,n]),null==r||r.on(t,n)}try{if(d("destroy",a),o)return s;const{$video:c}=e.template,h=e.constructor.utils,{blur:y="50px",opacity:p=.5,frequency:g=10,duration:m=.3}=n;if(o)return s;if(i=function(t){const n=t.createElement("div");n.innerHTML=Array.from({length:9}).fill("<div></div>").join("");const e=Array.from(n.children);return{mount:function(r,o,i){if(i()&&(t.addClass(n,"artplayer-plugin-ambilight"),i()&&(r.parentNode.insertBefore(n,r),i()))){t.setStyles(n,{position:"absolute",top:0,left:0,zIndex:9,inset:0,display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gridTemplateRows:"1fr 1fr 1fr"});for(const n of e){if(!i())return;t.setStyles(n,{opacity:o.opacity,filter:`blur(${o.blur})`,transition:`background-color ${o.duration}s ease`})}}},render:function(t,n){for(let r=0;r<t.length;r++){if(!n())return;const o=e[r],i=t[r];o&&void 0!==i&&(o.style.backgroundColor=i)}},destroy:function(){var t;try{null==(t=n.parentNode)||t.removeChild(n)}finally{e.length=0}}}}(h),o||i.mount(c,{blur:y,opacity:p,duration:m},()=>!o),o)return f(),s;const b=h.createElement("canvas");if(o)return b.width=0,b.height=0,s;if(l=function(t,n,e){let r;try{r=t.getContext("2d"),t.width=3,t.height=3}catch(l){throw t.width=0,t.height=0,l}let o=!1,i=!1;return{read:function(l){if(o||!l())return null;const u=n.videoWidth,c=n.videoHeight;if(!Number.isFinite(u)||!Number.isFinite(c)||u<=0||c<=0)return null;const s=u/3,f=c/3,a=[];try{if(i){const n=e();if(o||!l())return n.width=0,n.height=0,null;if(r=null,r=(t=n).getContext("2d"),o||!l())return t.width=0,t.height=0,null;t.width=3,t.height=3,i=!1}if(!r)return null;for(let t=0;t<3;t++)for(let e=0;e<3;e++){if(o||!l())return null;if(r.drawImage(n,e*s,t*f,s,f,0,0,1,1),o||!l())return null;const[i,u,c]=r.getImageData(0,0,1,1).data;a.push(`rgb(${i}, ${u}, ${c})`)}return a}catch(d){return o||(i=!0,t.width=0,t.height=0),null}},destroy:function(){o||(o=!0,t.width=0,t.height=0)}}}(b,c,()=>h.createElement("canvas")),o)return f(),s;u=t(g,()=>Boolean(null==r?void 0:r.playing),t=>{const n=null==l?void 0:l.read(t);n&&t()&&(null==i||i.render(n,t))}),d("ready",s.start),o&&f()}catch(h){try{a()}catch(y){}try{f()}catch(y){}throw h}return s}}});
|
||||
!function(t,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(t.artplayerPluginAmbilight=n(),define(function(){return t.artplayerPluginAmbilight})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAmbilight=n()}(this,function(){"use strict";function t(t,n,e){let r="stopped",i=null,o=0,l=!1,u=0;function c(){const s=o,f=()=>"running"===r&&s===o;l=!0;try{const r=performance.now();if(r-u<1e3/t||!n()||!f())return;u=r,e(f)}finally{l=!1,"running"===r&&null===i&&function(){const t=o;try{i=requestAnimationFrame(()=>{"running"===r&&t===o&&(i=null,c())})}catch(n){throw r="stopped",o++,n}}()}}return{start:function(){"stopped"===r&&(r="running",o++,l||c())},stop:function(){if("destroyed"===r)return;r="stopped",o++;const t=i;i=null,null!==t&&cancelAnimationFrame(t)},destroy:function(){if("destroyed"===r)return;r="destroyed",o++;const t=i;i=null,null!==t&&cancelAnimationFrame(t)}}}function n(n={}){return e=>{let r=e,i=e.isDestroy,o=null,l=null,u=null;const c=[],s={name:"artplayerPluginAmbilight",start(){i||null==u||u.start()},stop(){i||null==u||u.stop()}};if(i)return r=null,s;function f(){const t=[null==u?void 0:u.destroy,null==l?void 0:l.destroy,null==o?void 0:o.destroy];u=null,l=null,o=null;const n=[];for(const r of t)try{null==r||r()}catch(e){n.push(e)}if(n.length)throw n[0]}function a(){if(i)return;i=!0;const t=r;r=null;const n=[];for(const[r,i]of c.splice(0))try{null==t||t.off(r,i)}catch(e){n.push(e)}try{f()}catch(e){n.push(e)}if(n.length)throw n[0]}function d(t,n){c.push([t,n]),null==r||r.on(t,n)}try{if(d("destroy",a),i)return s;const{$video:c}=e.template,h=e.constructor.utils,{blur:y="50px",opacity:p=.5,frequency:g=10,duration:m=.3}=n;if(i)return s;if(o=function(t){const n=t.createElement("div");n.innerHTML=Array.from({length:9}).fill("<div></div>").join("");const e=Array.from(n.children);return{mount:function(r,i,o){if(o()&&(t.addClass(n,"artplayer-plugin-ambilight"),o()&&(r.parentNode.insertBefore(n,r),o()))){t.setStyles(n,{position:"absolute",top:0,left:0,zIndex:9,inset:0,display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gridTemplateRows:"1fr 1fr 1fr"});for(const n of e){if(!o())return;t.setStyles(n,{opacity:i.opacity,filter:`blur(${i.blur})`,transition:`background-color ${i.duration}s ease`})}}},render:function(t,n){for(let r=0;r<t.length;r++){if(!n())return;const i=e[r],o=t[r];i&&void 0!==o&&(i.style.backgroundColor=o)}},destroy:function(){var t;try{null==(t=n.parentNode)||t.removeChild(n)}finally{e.length=0}}}}(h),i||o.mount(c,{blur:y,opacity:p,duration:m},()=>!i),i)return f(),s;const b=h.createElement("canvas");if(i)return b.width=0,b.height=0,s;if(l=function(t,n,e){let r;try{r=t.getContext("2d"),t.width=3,t.height=3}catch(l){throw t.width=0,t.height=0,l}let i=!1,o=!1;return{read:function(l){if(i||!l())return null;const u=n.videoWidth,c=n.videoHeight;if(!Number.isFinite(u)||!Number.isFinite(c)||u<=0||c<=0)return null;const s=u/3,f=c/3,a=[];try{if(o){const n=e();if(i||!l())return n.width=0,n.height=0,null;if(r=null,r=(t=n).getContext("2d"),i||!l())return t.width=0,t.height=0,null;t.width=3,t.height=3,o=!1}if(!r)return null;for(let t=0;t<3;t++)for(let e=0;e<3;e++){if(i||!l())return null;if(r.drawImage(n,e*s,t*f,s,f,0,0,1,1),i||!l())return null;const[o,u,c]=r.getImageData(0,0,1,1).data;a.push(`rgb(${o}, ${u}, ${c})`)}return a}catch(d){return i||(o=!0,t.width=0,t.height=0),null}},destroy:function(){i||(i=!0,t.width=0,t.height=0)}}}(b,c,()=>h.createElement("canvas")),i)return f(),s;u=t(g,()=>Boolean(null==r?void 0:r.playing),t=>{const n=null==l?void 0:l.read(t);n&&t()&&(null==o||o.render(n,t))}),d("ready",s.start),i&&f()}catch(h){try{a()}catch(y){}try{f()}catch(y){}throw h}return s}}return Object.assign(n,{default:n})});
|
||||
@@ -167,11 +167,11 @@ function createAmbilightView(utils) {
|
||||
}
|
||||
}
|
||||
function render(colors, active) {
|
||||
for (let index = 0; index < colors.length; index++) {
|
||||
for (let index2 = 0; index2 < colors.length; index2++) {
|
||||
if (!active())
|
||||
return;
|
||||
const item = items[index];
|
||||
const color = colors[index];
|
||||
const item = items[index2];
|
||||
const color = colors[index2];
|
||||
if (item && color !== void 0)
|
||||
item.style.backgroundColor = color;
|
||||
}
|
||||
@@ -299,6 +299,7 @@ function artplayerPluginAmbilight(option = {}) {
|
||||
return result;
|
||||
};
|
||||
}
|
||||
const index = Object.assign(artplayerPluginAmbilight, { default: artplayerPluginAmbilight });
|
||||
export {
|
||||
artplayerPluginAmbilight as default
|
||||
index as default
|
||||
};
|
||||
+2
-1
@@ -70,7 +70,8 @@
|
||||
"check:impact": "node refactor/scripts/impact.mjs",
|
||||
"check:contracts": "node refactor/scripts/contracts.mjs",
|
||||
"test:contracts": "node refactor/scripts/run-contracts.mjs",
|
||||
"test:ambilight": "node --test test/ambilight.test.js test/ambilight-lifecycle.test.js"
|
||||
"test:ambilight": "node --test test/ambilight.test.js test/ambilight-lifecycle.test.js",
|
||||
"test:ambilight-types-package": "node refactor/scripts/ambilight-package-types.mjs"
|
||||
},
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -2,4 +2,5 @@ node_modules
|
||||
.DS_Store
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
src
|
||||
src
|
||||
tsconfig.json
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
Read the frozen published contracts in `refactor/baselines/ambilight-contract.md`
|
||||
before changing the public factory or distribution. Source is strict TypeScript;
|
||||
the existing public declaration remains the input contract until task 04.
|
||||
`types/artplayer-plugin-ambilight.d.ts` owns the public options and result contract.
|
||||
The runtime derives its option type from that declaration; do not duplicate it.
|
||||
|
||||
| Module | Responsibility |
|
||||
| --- | --- |
|
||||
@@ -42,5 +43,22 @@ and `yarn test:browser test/browser/ambilight-lifecycle.spec.js`. Historical tes
|
||||
in `test/ambilight.test.js` and `test/browser/ambilight.spec.js` intentionally retain
|
||||
the old failures. Candidate tests must remain separate. The browser suite runs real
|
||||
video/canvas with a lexical native RAF observer across published cores 5.1.7/5.4.0
|
||||
and candidate core in Chromium/Firefox/WebKit. Proxy/device, packed consumer and
|
||||
public type acceptance remain tasks 04-06; see `refactor/ambilight-validation.md`.
|
||||
and candidate core in Chromium/Firefox/WebKit. Proxy/device and complete packed
|
||||
browser/demo acceptance remain tasks 05-06; see `refactor/ambilight-validation.md`.
|
||||
|
||||
Public types merge a callable value with a type-only namespace. An optional first
|
||||
overload permits omitted/undefined options; the required final overload preserves
|
||||
the published 1.1.0 `Parameters` extraction. Do not reorder these signatures. The
|
||||
runtime factory has a self-referencing `default` property to support both historical
|
||||
CommonJS forms. `.d.mts`/`.d.cts` forward to the same declaration for the actual ESM/
|
||||
CommonJS entry kinds; `typesVersions` supplies `/legacy` to older resolvers. The
|
||||
editor global is generated by `yarn build:ts` through the semantic plugin generator.
|
||||
|
||||
Run `node --test refactor/scripts/ambilight-types.test.mjs` for public/editor/format
|
||||
contracts and `yarn test:ambilight-types-package` for offline tarball installations
|
||||
outside the workspace. The latter verifies two actual published plugin archives and
|
||||
candidate against the same packed candidate core, five compiler modes plus two
|
||||
candidate modes with interop disabled, and ten negative uses. Published 1.1.0 fails
|
||||
NodeNext ESM with its old declarations; candidate must not inherit that failure.
|
||||
Published 1.0.0 required all option fields; 1.1.0 already made them optional. Current
|
||||
inference follows 1.1.0 while retaining valid calls from both generations.
|
||||
@@ -9,6 +9,23 @@ failures are skipped and retried, allowing a later readable source to recover.
|
||||
|
||||
Internal modules and compatibility tests are documented in [ARCHITECTURE.md](ARCHITECTURE.md).
|
||||
|
||||
## Usage and TypeScript
|
||||
|
||||
```ts
|
||||
import type { Option } from 'artplayer-plugin-ambilight'
|
||||
import artplayerPluginAmbilight from 'artplayer-plugin-ambilight'
|
||||
|
||||
const option: Option = { blur: '50px', opacity: 0.5, frequency: 10, duration: 0.3 }
|
||||
// Add to ArtPlayer's plugins option:
|
||||
const plugins = [artplayerPluginAmbilight(option)]
|
||||
```
|
||||
|
||||
Options can be omitted. `require('artplayer-plugin-ambilight')` and the historical
|
||||
`require('artplayer-plugin-ambilight').default` refer to the same factory. The
|
||||
`/legacy` entry remains available. The historical `zIndex` input is accepted but
|
||||
ignored; the grid uses 9. Option fields stay optional as in version 1.1.0; code
|
||||
extracting their types should account for `undefined`.
|
||||
|
||||
## Demo
|
||||
|
||||
[https://artplayer.org](https://artplayer.org/?libs=./uncompiled/artplayer-plugin-ambilight/index.js&example=ambilight)
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(t.artplayerPluginAmbilight=n(),define(function(){return t.artplayerPluginAmbilight})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAmbilight=n()}(this,function(){"use strict";function t(t,n,e){let r="stopped",o=null,i=0,l=!1,u=0;function c(){const s=i,f=()=>"running"===r&&s===i;l=!0;try{const r=performance.now();if(r-u<1e3/t||!n()||!f())return;u=r,e(f)}finally{l=!1,"running"===r&&null===o&&function(){const t=i;try{o=requestAnimationFrame(()=>{"running"===r&&t===i&&(o=null,c())})}catch(n){throw r="stopped",i++,n}}()}}return{start:function(){"stopped"===r&&(r="running",i++,l||c())},stop:function(){if("destroyed"===r)return;r="stopped",i++;const t=o;o=null,null!==t&&cancelAnimationFrame(t)},destroy:function(){if("destroyed"===r)return;r="destroyed",i++;const t=o;o=null,null!==t&&cancelAnimationFrame(t)}}}return function(n={}){return e=>{let r=e,o=e.isDestroy,i=null,l=null,u=null;const c=[],s={name:"artplayerPluginAmbilight",start(){o||u?.start()},stop(){o||u?.stop()}};if(o)return r=null,s;function f(){const t=[u?.destroy,l?.destroy,i?.destroy];u=null,l=null,i=null;const n=[];for(const r of t)try{r?.()}catch(e){n.push(e)}if(n.length)throw n[0]}function a(){if(o)return;o=!0;const t=r;r=null;const n=[];for(const[r,o]of c.splice(0))try{t?.off(r,o)}catch(e){n.push(e)}try{f()}catch(e){n.push(e)}if(n.length)throw n[0]}function d(t,n){c.push([t,n]),r?.on(t,n)}try{if(d("destroy",a),o)return s;const{$video:c}=e.template,h=e.constructor.utils,{blur:y="50px",opacity:p=.5,frequency:g=10,duration:m=.3}=n;if(o)return s;if(i=function(t){const n=t.createElement("div");n.innerHTML=Array.from({length:9}).fill("<div></div>").join("");const e=Array.from(n.children);return{mount:function(r,o,i){if(i()&&(t.addClass(n,"artplayer-plugin-ambilight"),i()&&(r.parentNode.insertBefore(n,r),i()))){t.setStyles(n,{position:"absolute",top:0,left:0,zIndex:9,inset:0,display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gridTemplateRows:"1fr 1fr 1fr"});for(const n of e){if(!i())return;t.setStyles(n,{opacity:o.opacity,filter:`blur(${o.blur})`,transition:`background-color ${o.duration}s ease`})}}},render:function(t,n){for(let r=0;r<t.length;r++){if(!n())return;const o=e[r],i=t[r];o&&void 0!==i&&(o.style.backgroundColor=i)}},destroy:function(){try{n.parentNode?.removeChild(n)}finally{e.length=0}}}}(h),o||i.mount(c,{blur:y,opacity:p,duration:m},()=>!o),o)return f(),s;const b=h.createElement("canvas");if(o)return b.width=0,b.height=0,s;if(l=function(t,n,e){let r;try{r=t.getContext("2d"),t.width=3,t.height=3}catch(l){throw t.width=0,t.height=0,l}let o=!1,i=!1;return{read:function(l){if(o||!l())return null;const u=n.videoWidth,c=n.videoHeight;if(!Number.isFinite(u)||!Number.isFinite(c)||u<=0||c<=0)return null;const s=u/3,f=c/3,a=[];try{if(i){const n=e();if(o||!l())return n.width=0,n.height=0,null;if(r=null,r=(t=n).getContext("2d"),o||!l())return t.width=0,t.height=0,null;t.width=3,t.height=3,i=!1}if(!r)return null;for(let t=0;t<3;t++)for(let e=0;e<3;e++){if(o||!l())return null;if(r.drawImage(n,e*s,t*f,s,f,0,0,1,1),o||!l())return null;const[i,u,c]=r.getImageData(0,0,1,1).data;a.push(`rgb(${i}, ${u}, ${c})`)}return a}catch{return o||(i=!0,t.width=0,t.height=0),null}},destroy:function(){o||(o=!0,t.width=0,t.height=0)}}}(b,c,()=>h.createElement("canvas")),o)return f(),s;u=t(g,()=>Boolean(r?.playing),t=>{const n=l?.read(t);n&&t()&&i?.render(n,t)}),d("ready",s.start),o&&f()}catch(h){try{a()}catch{}try{f()}catch{}throw h}return s}}});
|
||||
!function(t,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(t.artplayerPluginAmbilight=n(),define(function(){return t.artplayerPluginAmbilight})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAmbilight=n()}(this,function(){"use strict";function t(t,n,e){let r="stopped",i=null,o=0,l=!1,u=0;function c(){const s=o,f=()=>"running"===r&&s===o;l=!0;try{const r=performance.now();if(r-u<1e3/t||!n()||!f())return;u=r,e(f)}finally{l=!1,"running"===r&&null===i&&function(){const t=o;try{i=requestAnimationFrame(()=>{"running"===r&&t===o&&(i=null,c())})}catch(n){throw r="stopped",o++,n}}()}}return{start:function(){"stopped"===r&&(r="running",o++,l||c())},stop:function(){if("destroyed"===r)return;r="stopped",o++;const t=i;i=null,null!==t&&cancelAnimationFrame(t)},destroy:function(){if("destroyed"===r)return;r="destroyed",o++;const t=i;i=null,null!==t&&cancelAnimationFrame(t)}}}function n(n={}){return e=>{let r=e,i=e.isDestroy,o=null,l=null,u=null;const c=[],s={name:"artplayerPluginAmbilight",start(){i||u?.start()},stop(){i||u?.stop()}};if(i)return r=null,s;function f(){const t=[u?.destroy,l?.destroy,o?.destroy];u=null,l=null,o=null;const n=[];for(const r of t)try{r?.()}catch(e){n.push(e)}if(n.length)throw n[0]}function a(){if(i)return;i=!0;const t=r;r=null;const n=[];for(const[r,i]of c.splice(0))try{t?.off(r,i)}catch(e){n.push(e)}try{f()}catch(e){n.push(e)}if(n.length)throw n[0]}function d(t,n){c.push([t,n]),r?.on(t,n)}try{if(d("destroy",a),i)return s;const{$video:c}=e.template,h=e.constructor.utils,{blur:y="50px",opacity:p=.5,frequency:g=10,duration:m=.3}=n;if(i)return s;if(o=function(t){const n=t.createElement("div");n.innerHTML=Array.from({length:9}).fill("<div></div>").join("");const e=Array.from(n.children);return{mount:function(r,i,o){if(o()&&(t.addClass(n,"artplayer-plugin-ambilight"),o()&&(r.parentNode.insertBefore(n,r),o()))){t.setStyles(n,{position:"absolute",top:0,left:0,zIndex:9,inset:0,display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gridTemplateRows:"1fr 1fr 1fr"});for(const n of e){if(!o())return;t.setStyles(n,{opacity:i.opacity,filter:`blur(${i.blur})`,transition:`background-color ${i.duration}s ease`})}}},render:function(t,n){for(let r=0;r<t.length;r++){if(!n())return;const i=e[r],o=t[r];i&&void 0!==o&&(i.style.backgroundColor=o)}},destroy:function(){try{n.parentNode?.removeChild(n)}finally{e.length=0}}}}(h),i||o.mount(c,{blur:y,opacity:p,duration:m},()=>!i),i)return f(),s;const b=h.createElement("canvas");if(i)return b.width=0,b.height=0,s;if(l=function(t,n,e){let r;try{r=t.getContext("2d"),t.width=3,t.height=3}catch(l){throw t.width=0,t.height=0,l}let i=!1,o=!1;return{read:function(l){if(i||!l())return null;const u=n.videoWidth,c=n.videoHeight;if(!Number.isFinite(u)||!Number.isFinite(c)||u<=0||c<=0)return null;const s=u/3,f=c/3,a=[];try{if(o){const n=e();if(i||!l())return n.width=0,n.height=0,null;if(r=null,r=(t=n).getContext("2d"),i||!l())return t.width=0,t.height=0,null;t.width=3,t.height=3,o=!1}if(!r)return null;for(let t=0;t<3;t++)for(let e=0;e<3;e++){if(i||!l())return null;if(r.drawImage(n,e*s,t*f,s,f,0,0,1,1),i||!l())return null;const[o,u,c]=r.getImageData(0,0,1,1).data;a.push(`rgb(${o}, ${u}, ${c})`)}return a}catch{return i||(o=!0,t.width=0,t.height=0),null}},destroy:function(){i||(i=!0,t.width=0,t.height=0)}}}(b,c,()=>h.createElement("canvas")),i)return f(),s;u=t(g,()=>Boolean(r?.playing),t=>{const n=l?.read(t);n&&t()&&o?.render(n,t)}),d("ready",s.start),i&&f()}catch(h){try{a()}catch{}try{f()}catch{}throw h}return s}}return Object.assign(n,{default:n})});
|
||||
+1
-1
@@ -4,4 +4,4 @@
|
||||
* (c) 2017-2026 Harvey Zhao
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
!function(t,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(t.artplayerPluginAmbilight=n(),define(function(){return t.artplayerPluginAmbilight})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAmbilight=n()}(this,function(){"use strict";function t(t,n,e){let r="stopped",o=null,i=0,l=!1,u=0;function c(){const s=i,f=()=>"running"===r&&s===i;l=!0;try{const r=performance.now();if(r-u<1e3/t||!n()||!f())return;u=r,e(f)}finally{l=!1,"running"===r&&null===o&&function(){const t=i;try{o=requestAnimationFrame(()=>{"running"===r&&t===i&&(o=null,c())})}catch(n){throw r="stopped",i++,n}}()}}return{start:function(){"stopped"===r&&(r="running",i++,l||c())},stop:function(){if("destroyed"===r)return;r="stopped",i++;const t=o;o=null,null!==t&&cancelAnimationFrame(t)},destroy:function(){if("destroyed"===r)return;r="destroyed",i++;const t=o;o=null,null!==t&&cancelAnimationFrame(t)}}}return function(n={}){return e=>{let r=e,o=e.isDestroy,i=null,l=null,u=null;const c=[],s={name:"artplayerPluginAmbilight",start(){o||null==u||u.start()},stop(){o||null==u||u.stop()}};if(o)return r=null,s;function f(){const t=[null==u?void 0:u.destroy,null==l?void 0:l.destroy,null==i?void 0:i.destroy];u=null,l=null,i=null;const n=[];for(const r of t)try{null==r||r()}catch(e){n.push(e)}if(n.length)throw n[0]}function a(){if(o)return;o=!0;const t=r;r=null;const n=[];for(const[r,o]of c.splice(0))try{null==t||t.off(r,o)}catch(e){n.push(e)}try{f()}catch(e){n.push(e)}if(n.length)throw n[0]}function d(t,n){c.push([t,n]),null==r||r.on(t,n)}try{if(d("destroy",a),o)return s;const{$video:c}=e.template,h=e.constructor.utils,{blur:y="50px",opacity:p=.5,frequency:g=10,duration:m=.3}=n;if(o)return s;if(i=function(t){const n=t.createElement("div");n.innerHTML=Array.from({length:9}).fill("<div></div>").join("");const e=Array.from(n.children);return{mount:function(r,o,i){if(i()&&(t.addClass(n,"artplayer-plugin-ambilight"),i()&&(r.parentNode.insertBefore(n,r),i()))){t.setStyles(n,{position:"absolute",top:0,left:0,zIndex:9,inset:0,display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gridTemplateRows:"1fr 1fr 1fr"});for(const n of e){if(!i())return;t.setStyles(n,{opacity:o.opacity,filter:`blur(${o.blur})`,transition:`background-color ${o.duration}s ease`})}}},render:function(t,n){for(let r=0;r<t.length;r++){if(!n())return;const o=e[r],i=t[r];o&&void 0!==i&&(o.style.backgroundColor=i)}},destroy:function(){var t;try{null==(t=n.parentNode)||t.removeChild(n)}finally{e.length=0}}}}(h),o||i.mount(c,{blur:y,opacity:p,duration:m},()=>!o),o)return f(),s;const b=h.createElement("canvas");if(o)return b.width=0,b.height=0,s;if(l=function(t,n,e){let r;try{r=t.getContext("2d"),t.width=3,t.height=3}catch(l){throw t.width=0,t.height=0,l}let o=!1,i=!1;return{read:function(l){if(o||!l())return null;const u=n.videoWidth,c=n.videoHeight;if(!Number.isFinite(u)||!Number.isFinite(c)||u<=0||c<=0)return null;const s=u/3,f=c/3,a=[];try{if(i){const n=e();if(o||!l())return n.width=0,n.height=0,null;if(r=null,r=(t=n).getContext("2d"),o||!l())return t.width=0,t.height=0,null;t.width=3,t.height=3,i=!1}if(!r)return null;for(let t=0;t<3;t++)for(let e=0;e<3;e++){if(o||!l())return null;if(r.drawImage(n,e*s,t*f,s,f,0,0,1,1),o||!l())return null;const[i,u,c]=r.getImageData(0,0,1,1).data;a.push(`rgb(${i}, ${u}, ${c})`)}return a}catch(d){return o||(i=!0,t.width=0,t.height=0),null}},destroy:function(){o||(o=!0,t.width=0,t.height=0)}}}(b,c,()=>h.createElement("canvas")),o)return f(),s;u=t(g,()=>Boolean(null==r?void 0:r.playing),t=>{const n=null==l?void 0:l.read(t);n&&t()&&(null==i||i.render(n,t))}),d("ready",s.start),o&&f()}catch(h){try{a()}catch(y){}try{f()}catch(y){}throw h}return s}}});
|
||||
!function(t,n){"object"===typeof exports&&"undefined"!==typeof module?module.exports=n():"function"==typeof define&&define.amd?(t.artplayerPluginAmbilight=n(),define(function(){return t.artplayerPluginAmbilight})):(t="undefined"!==typeof globalThis?globalThis:t||self).artplayerPluginAmbilight=n()}(this,function(){"use strict";function t(t,n,e){let r="stopped",i=null,o=0,l=!1,u=0;function c(){const s=o,f=()=>"running"===r&&s===o;l=!0;try{const r=performance.now();if(r-u<1e3/t||!n()||!f())return;u=r,e(f)}finally{l=!1,"running"===r&&null===i&&function(){const t=o;try{i=requestAnimationFrame(()=>{"running"===r&&t===o&&(i=null,c())})}catch(n){throw r="stopped",o++,n}}()}}return{start:function(){"stopped"===r&&(r="running",o++,l||c())},stop:function(){if("destroyed"===r)return;r="stopped",o++;const t=i;i=null,null!==t&&cancelAnimationFrame(t)},destroy:function(){if("destroyed"===r)return;r="destroyed",o++;const t=i;i=null,null!==t&&cancelAnimationFrame(t)}}}function n(n={}){return e=>{let r=e,i=e.isDestroy,o=null,l=null,u=null;const c=[],s={name:"artplayerPluginAmbilight",start(){i||null==u||u.start()},stop(){i||null==u||u.stop()}};if(i)return r=null,s;function f(){const t=[null==u?void 0:u.destroy,null==l?void 0:l.destroy,null==o?void 0:o.destroy];u=null,l=null,o=null;const n=[];for(const r of t)try{null==r||r()}catch(e){n.push(e)}if(n.length)throw n[0]}function a(){if(i)return;i=!0;const t=r;r=null;const n=[];for(const[r,i]of c.splice(0))try{null==t||t.off(r,i)}catch(e){n.push(e)}try{f()}catch(e){n.push(e)}if(n.length)throw n[0]}function d(t,n){c.push([t,n]),null==r||r.on(t,n)}try{if(d("destroy",a),i)return s;const{$video:c}=e.template,h=e.constructor.utils,{blur:y="50px",opacity:p=.5,frequency:g=10,duration:m=.3}=n;if(i)return s;if(o=function(t){const n=t.createElement("div");n.innerHTML=Array.from({length:9}).fill("<div></div>").join("");const e=Array.from(n.children);return{mount:function(r,i,o){if(o()&&(t.addClass(n,"artplayer-plugin-ambilight"),o()&&(r.parentNode.insertBefore(n,r),o()))){t.setStyles(n,{position:"absolute",top:0,left:0,zIndex:9,inset:0,display:"grid",gridTemplateColumns:"1fr 1fr 1fr",gridTemplateRows:"1fr 1fr 1fr"});for(const n of e){if(!o())return;t.setStyles(n,{opacity:i.opacity,filter:`blur(${i.blur})`,transition:`background-color ${i.duration}s ease`})}}},render:function(t,n){for(let r=0;r<t.length;r++){if(!n())return;const i=e[r],o=t[r];i&&void 0!==o&&(i.style.backgroundColor=o)}},destroy:function(){var t;try{null==(t=n.parentNode)||t.removeChild(n)}finally{e.length=0}}}}(h),i||o.mount(c,{blur:y,opacity:p,duration:m},()=>!i),i)return f(),s;const b=h.createElement("canvas");if(i)return b.width=0,b.height=0,s;if(l=function(t,n,e){let r;try{r=t.getContext("2d"),t.width=3,t.height=3}catch(l){throw t.width=0,t.height=0,l}let i=!1,o=!1;return{read:function(l){if(i||!l())return null;const u=n.videoWidth,c=n.videoHeight;if(!Number.isFinite(u)||!Number.isFinite(c)||u<=0||c<=0)return null;const s=u/3,f=c/3,a=[];try{if(o){const n=e();if(i||!l())return n.width=0,n.height=0,null;if(r=null,r=(t=n).getContext("2d"),i||!l())return t.width=0,t.height=0,null;t.width=3,t.height=3,o=!1}if(!r)return null;for(let t=0;t<3;t++)for(let e=0;e<3;e++){if(i||!l())return null;if(r.drawImage(n,e*s,t*f,s,f,0,0,1,1),i||!l())return null;const[o,u,c]=r.getImageData(0,0,1,1).data;a.push(`rgb(${o}, ${u}, ${c})`)}return a}catch(d){return i||(o=!0,t.width=0,t.height=0),null}},destroy:function(){i||(i=!0,t.width=0,t.height=0)}}}(b,c,()=>h.createElement("canvas")),i)return f(),s;u=t(g,()=>Boolean(null==r?void 0:r.playing),t=>{const n=null==l?void 0:l.read(t);n&&t()&&(null==o||o.render(n,t))}),d("ready",s.start),i&&f()}catch(h){try{a()}catch(y){}try{f()}catch(y){}throw h}return s}}return Object.assign(n,{default:n})});
|
||||
@@ -167,11 +167,11 @@ function createAmbilightView(utils) {
|
||||
}
|
||||
}
|
||||
function render(colors, active) {
|
||||
for (let index = 0; index < colors.length; index++) {
|
||||
for (let index2 = 0; index2 < colors.length; index2++) {
|
||||
if (!active())
|
||||
return;
|
||||
const item = items[index];
|
||||
const color = colors[index];
|
||||
const item = items[index2];
|
||||
const color = colors[index2];
|
||||
if (item && color !== void 0)
|
||||
item.style.backgroundColor = color;
|
||||
}
|
||||
@@ -299,6 +299,7 @@ function artplayerPluginAmbilight(option = {}) {
|
||||
return result;
|
||||
};
|
||||
}
|
||||
const index = Object.assign(artplayerPluginAmbilight, { default: artplayerPluginAmbilight });
|
||||
export {
|
||||
artplayerPluginAmbilight as default
|
||||
index as default
|
||||
};
|
||||
@@ -19,19 +19,36 @@
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.ts",
|
||||
"import": "./dist/artplayer-plugin-ambilight.mjs",
|
||||
"require": "./dist/artplayer-plugin-ambilight.js"
|
||||
"import": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.mts",
|
||||
"default": "./dist/artplayer-plugin-ambilight.mjs"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.cts",
|
||||
"default": "./dist/artplayer-plugin-ambilight.js"
|
||||
}
|
||||
},
|
||||
"./legacy": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.ts",
|
||||
"import": "./dist/artplayer-plugin-ambilight.legacy.js",
|
||||
"require": "./dist/artplayer-plugin-ambilight.legacy.js"
|
||||
"import": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.cts",
|
||||
"default": "./dist/artplayer-plugin-ambilight.legacy.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./types/artplayer-plugin-ambilight.d.cts",
|
||||
"default": "./dist/artplayer-plugin-ambilight.legacy.js"
|
||||
}
|
||||
}
|
||||
},
|
||||
"main": "./dist/artplayer-plugin-ambilight.js",
|
||||
"module": "./dist/artplayer-plugin-ambilight.mjs",
|
||||
"types": "./types/artplayer-plugin-ambilight.d.ts",
|
||||
"legacy": "./dist/artplayer-plugin-ambilight.legacy.js",
|
||||
"browserslist": "last 1 Chrome version"
|
||||
"browserslist": "last 1 Chrome version",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"legacy": [
|
||||
"types/artplayer-plugin-ambilight.d.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import { createAmbilightView } from './view'
|
||||
type Option = Parameters<typeof PublicPlugin>[0]
|
||||
type Subscription = ['ready' | 'destroy', () => void]
|
||||
|
||||
export default function artplayerPluginAmbilight(option: Option = {}) {
|
||||
function artplayerPluginAmbilight(option: Option = {}) {
|
||||
return (art: Artplayer) => {
|
||||
let host: Artplayer | null = art
|
||||
let closed = art.isDestroy
|
||||
@@ -123,3 +123,5 @@ export default function artplayerPluginAmbilight(option: Option = {}) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
export default Object.assign(artplayerPluginAmbilight, { default: artplayerPluginAmbilight })
|
||||
@@ -0,0 +1,4 @@
|
||||
// eslint-disable-next-line ts/no-require-imports -- Preserve the CommonJS export-assignment namespace.
|
||||
import artplayerPluginAmbilight = require('./artplayer-plugin-ambilight.js')
|
||||
|
||||
export = artplayerPluginAmbilight
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default } from './artplayer-plugin-ambilight.js'
|
||||
export type { Callable, Factory, Option, Result } from './artplayer-plugin-ambilight.js'
|
||||
+36
-15
@@ -1,19 +1,40 @@
|
||||
/* eslint-disable ts/no-redeclare -- Callable and public type namespace intentionally merge. */
|
||||
import type Artplayer from 'artplayer'
|
||||
|
||||
interface Option {
|
||||
blur?: string
|
||||
opacity?: number
|
||||
frequency?: number
|
||||
zIndex?: number
|
||||
duration?: number
|
||||
declare namespace artplayerPluginAmbilight {
|
||||
interface Option {
|
||||
/** CSS blur radius. @default '50px' */
|
||||
blur?: string
|
||||
/** Grid cell opacity. @default 0.5 */
|
||||
opacity?: number
|
||||
/** Maximum sampling frequency in frames per second. @default 10 */
|
||||
frequency?: number
|
||||
/** Historical input retained for compatibility; runtime uses a fixed z-index of 9. */
|
||||
zIndex?: number
|
||||
/** Background color transition duration in seconds. @default 0.3 */
|
||||
duration?: number
|
||||
}
|
||||
|
||||
interface Result {
|
||||
name: 'artplayerPluginAmbilight'
|
||||
/** Start sampling; does nothing after the player is destroyed. */
|
||||
start: () => void
|
||||
/** Stop sampling while retaining the last colors. */
|
||||
stop: () => void
|
||||
}
|
||||
|
||||
interface Callable {
|
||||
(option?: Option): (art: Artplayer) => Result
|
||||
/** Keep Parameters extraction identical to the published 1.1.0 signature. */
|
||||
(option: Option): (art: Artplayer) => Result
|
||||
}
|
||||
|
||||
interface Factory extends Callable {
|
||||
/** Same function, including historical require(package).default calls. */
|
||||
readonly default: Factory
|
||||
}
|
||||
}
|
||||
|
||||
interface Result {
|
||||
name: 'artplayerPluginAmbilight'
|
||||
start: () => void
|
||||
stop: () => void
|
||||
}
|
||||
|
||||
declare const artplayerPluginAmbilight: (option: Option) => (art: Artplayer) => Result
|
||||
|
||||
export default artplayerPluginAmbilight
|
||||
declare const artplayerPluginAmbilight: artplayerPluginAmbilight.Factory
|
||||
export = artplayerPluginAmbilight
|
||||
export as namespace artplayerPluginAmbilight
|
||||
@@ -46,3 +46,27 @@ candidate是当前源码;以browser-evidence的manifest和实际version为准
|
||||
03证据见 [生命周期验证](baselines/ambilight-lifecycle-validation.json) 和
|
||||
[实施记录](changes/2026-09-12-PKG-AMBILIGHT-03-lifecycle.md)。04公开声明及CJS.default、
|
||||
05代理/实际设备、06安装包和8082 demo仍未完成;三浏览器通过不能代替这些验收。
|
||||
|
||||
## 04公开类型与安装消费者
|
||||
|
||||
`node --test refactor/scripts/ambilight-types.test.mjs`覆盖五种TS模式、10项非法用法、
|
||||
实际1.0.0/1.1.0声明对照、编辑器语义生成及正式main/legacy/global/ESM导出。已接入
|
||||
test:baseline和根typecheck。CommonJS自default别名兼容两代调用;可选参数重载放在前,
|
||||
必填Option签名必须放最后以保护Parameters提取。新的d.mts/d.cts为相同公共声明提供
|
||||
正确模块身份,typesVersions补齐旧解析器的legacy子路径。
|
||||
|
||||
`yarn test:ambilight-types-package`打包当前核心和插件,在仓库外分别安装两个真实
|
||||
发布插件和候选,离线/frozen安装及逐文件哈希检查防止误用workspace链接。共17个
|
||||
编译场景:候选7个(含TS5.9.3/4.3.5关闭interop)全部零诊断且各拒绝10项非法用法;
|
||||
旧包10个场景中1.1.0 NodeNext ESM精确保留4个既有错误,其余零诊断。已发表的类型
|
||||
不一致与候选失败严格分开,不把旧错误写成候选白名单。
|
||||
|
||||
1.0.0字段必填变为1.1.0可选是历史差异,本候选没有继续扩大推导。新测试在03提交上
|
||||
3失败/1通过,实装额外复现旧TS legacy缺映射、tsconfig打包泄漏和旧NodeNext问题。
|
||||
05代理/设备、06完整分发和8082 demo继续;当前统一test:package默认scope仍仅core/
|
||||
chapter,专项安装命令的存在不代表全部包已加入该门槛。
|
||||
|
||||
04最终四组专项、975项完整CI及18项三引擎媒体回归通过;安装消费者证据、包哈希、
|
||||
初始失败及明确限制见 [类型验证证据](baselines/ambilight-types-validation.json) 和
|
||||
[实施记录](changes/2026-09-12-PKG-AMBILIGHT-04-types.md)。类型与两代导出具体风险已
|
||||
关闭;当前待做范围为05/06,旧阶段记录保留其当时状态。
|
||||
@@ -0,0 +1,367 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"task": "PKG-AMBILIGHT-04",
|
||||
"status": "done",
|
||||
"parentCommit": "6caee44dbf23073b5b307fe083633172679a4e07",
|
||||
"checks": [
|
||||
{
|
||||
"command": "node --test refactor/scripts/ambilight-types.test.mjs",
|
||||
"result": "pass in final ci:check",
|
||||
"tests": 4,
|
||||
"scope": "five compiler modes, ten invalid uses, two frozen declarations, editor globals, actual production CJS/global/ESM"
|
||||
},
|
||||
{
|
||||
"command": "yarn test:ambilight-types-package",
|
||||
"result": "pass with exact historical failure observation",
|
||||
"cases": 17,
|
||||
"candidateZeroDiagnosticCases": 7,
|
||||
"historicalZeroDiagnosticCases": 9,
|
||||
"historicalExpectedFailureCases": 1,
|
||||
"invalidUsesRejectedPerCandidateCase": 10,
|
||||
"report": "refactor/.cache/ambilight-package-types-NRiHcI/report.json",
|
||||
"sha256": "77d85ca46927b85e801747cfbd3e905604c31f21414b54ee8b7eda6849f74300",
|
||||
"packages": [
|
||||
{
|
||||
"name": "artplayer",
|
||||
"archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\ambilight-package-types-NRiHcI\\artplayer.tgz",
|
||||
"sha256": "2139af017670a4f30f667bf5fdc1aa680f3e03b9c8d3cb767fc92d6345aedff5"
|
||||
},
|
||||
{
|
||||
"name": "artplayer-plugin-ambilight",
|
||||
"archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\ambilight-package-types-NRiHcI\\artplayer-plugin-ambilight.tgz",
|
||||
"sha256": "f79ad847970a8e4e46ab254d6654ce500ceed799eb8a4004bea5fa4ff7ff243a"
|
||||
}
|
||||
],
|
||||
"published": [
|
||||
{
|
||||
"label": "published-1.0.0",
|
||||
"archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\releases\\artplayer-plugin-ambilight-1.0.0.tgz",
|
||||
"sha256": "a95aa8c232ab48493e6b4d46b36edc8091230cbde5d18d7ec41912a4fa2b8956"
|
||||
},
|
||||
{
|
||||
"label": "published-1.1.0",
|
||||
"archive": "D:\\github\\ArtPlayer\\refactor\\.cache\\releases\\artplayer-plugin-ambilight-1.1.0.tgz",
|
||||
"sha256": "e95bd60fd7bce52142cf0041bad811452305029cbe1129c7d89246eb260c5c86"
|
||||
}
|
||||
],
|
||||
"matrix": [
|
||||
{
|
||||
"plugin": "published-1.0.0",
|
||||
"compiler": "5.9.3",
|
||||
"mode": "node10-commonjs",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 0
|
||||
},
|
||||
{
|
||||
"plugin": "published-1.0.0",
|
||||
"compiler": "5.9.3",
|
||||
"mode": "nodenext-cjs",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 0
|
||||
},
|
||||
{
|
||||
"plugin": "published-1.0.0",
|
||||
"compiler": "5.9.3",
|
||||
"mode": "nodenext-esm",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 0
|
||||
},
|
||||
{
|
||||
"plugin": "published-1.0.0",
|
||||
"compiler": "5.9.3",
|
||||
"mode": "bundler-esm",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 0
|
||||
},
|
||||
{
|
||||
"plugin": "published-1.0.0",
|
||||
"compiler": "4.3.5",
|
||||
"mode": "node10-commonjs",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 0
|
||||
},
|
||||
{
|
||||
"plugin": "published-1.1.0",
|
||||
"compiler": "5.9.3",
|
||||
"mode": "node10-commonjs",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 0
|
||||
},
|
||||
{
|
||||
"plugin": "published-1.1.0",
|
||||
"compiler": "5.9.3",
|
||||
"mode": "nodenext-cjs",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 0
|
||||
},
|
||||
{
|
||||
"plugin": "published-1.1.0",
|
||||
"compiler": "5.9.3",
|
||||
"mode": "nodenext-esm",
|
||||
"historicalFailure": true,
|
||||
"diagnosticCodes": [
|
||||
2349,
|
||||
2349,
|
||||
2322,
|
||||
2344
|
||||
],
|
||||
"invalidCount": 0
|
||||
},
|
||||
{
|
||||
"plugin": "published-1.1.0",
|
||||
"compiler": "5.9.3",
|
||||
"mode": "bundler-esm",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 0
|
||||
},
|
||||
{
|
||||
"plugin": "published-1.1.0",
|
||||
"compiler": "4.3.5",
|
||||
"mode": "node10-commonjs",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 0
|
||||
},
|
||||
{
|
||||
"plugin": "candidate",
|
||||
"compiler": "5.9.3",
|
||||
"mode": "node10-commonjs",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 10
|
||||
},
|
||||
{
|
||||
"plugin": "candidate",
|
||||
"compiler": "5.9.3",
|
||||
"mode": "nodenext-cjs",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 10
|
||||
},
|
||||
{
|
||||
"plugin": "candidate",
|
||||
"compiler": "5.9.3",
|
||||
"mode": "nodenext-esm",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 10
|
||||
},
|
||||
{
|
||||
"plugin": "candidate",
|
||||
"compiler": "5.9.3",
|
||||
"mode": "bundler-esm",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 10
|
||||
},
|
||||
{
|
||||
"plugin": "candidate",
|
||||
"compiler": "4.3.5",
|
||||
"mode": "node10-commonjs",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 10
|
||||
},
|
||||
{
|
||||
"plugin": "candidate",
|
||||
"compiler": "5.9.3",
|
||||
"mode": "node10-commonjs-no-interop",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 10
|
||||
},
|
||||
{
|
||||
"plugin": "candidate",
|
||||
"compiler": "4.3.5",
|
||||
"mode": "node10-commonjs-no-interop",
|
||||
"historicalFailure": false,
|
||||
"diagnosticCodes": [],
|
||||
"invalidCount": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"command": "yarn build artplayer-plugin-ambilight && yarn build:ts",
|
||||
"result": "pass",
|
||||
"scope": "main/legacy/ESM, docs/compiled copies and generated editor declarations"
|
||||
},
|
||||
{
|
||||
"command": "focused eslint --no-fix",
|
||||
"result": "pass"
|
||||
},
|
||||
{
|
||||
"command": "yarn ci:check",
|
||||
"result": "pass",
|
||||
"tests": {
|
||||
"unit": 878,
|
||||
"engineering": 14,
|
||||
"baseline": 83,
|
||||
"mainTotal": 975,
|
||||
"repeatedContractExecutions": 44
|
||||
},
|
||||
"strictProductionTypeScriptFiles": 278
|
||||
},
|
||||
{
|
||||
"command": "playwright test test/browser/ambilight-lifecycle.spec.js",
|
||||
"result": "pass",
|
||||
"tests": 18,
|
||||
"stats": {
|
||||
"startTime": "2026-09-12T03:46:06.215Z",
|
||||
"duration": 29911.739,
|
||||
"expected": 18,
|
||||
"skipped": 0,
|
||||
"unexpected": 0,
|
||||
"flaky": 0
|
||||
},
|
||||
"report": "refactor/.cache/ambilight04-final-results/report.json",
|
||||
"archivedResults": "refactor/.cache/ambilight04-final-results/results",
|
||||
"originalResultsPrefix": "refactor/.cache/browser/results",
|
||||
"scope": "same native canvas/media candidate suite, npm cores 5.1.7/5.4.0 and candidate, Chromium/Firefox/WebKit"
|
||||
}
|
||||
],
|
||||
"reproducedThenFixed": [
|
||||
"New tests on task 03 state: three failures, one historical-call test passes.",
|
||||
"Old Node10 resolver could not find /legacy declarations: added typesVersions.",
|
||||
"Real pack included implementation tsconfig: excluded it via package .npmignore.",
|
||||
"Actual npm 1.1.0 NodeNext ESM declaration errors 2349,2349,2322,2344 retained as historical evidence; candidate proper declaration entry modes pass."
|
||||
],
|
||||
"inferenceBoundary": "Candidate preserves published 1.1.0 optional fields and required final Parameters input. Required fields in 1.0.0 had already become optional in 1.1.0; no new widening or borrowed Ads approval.",
|
||||
"inputs": [
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ambilight/src/index.ts",
|
||||
"sha256LF": "9db25144798ee667bc420b3e1f0b00ef6855185d26cefca45e05271354bf8591"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ambilight/types/artplayer-plugin-ambilight.d.cts",
|
||||
"sha256LF": "4e3e0ed19f41bf27afa1a8abc9fad55169080c0c0f514171446881e87d36941d"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ambilight/types/artplayer-plugin-ambilight.d.mts",
|
||||
"sha256LF": "0454444c80bedb1b32df54642256440329f78a748a811296420e05f6ae1d6563"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ambilight/types/artplayer-plugin-ambilight.d.ts",
|
||||
"sha256LF": "c4a42edb8e30ad851febd15d1e61012019dd9aeb774c3b5d9bf5c59cbec9a671"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.js",
|
||||
"sha256LF": "e0487dabba876392c76e91864857fd3fe7d62f50d227f3ca0b4cc1361b34d9ea"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.legacy.js",
|
||||
"sha256LF": "f8f2909dbb63a6b8fa8297295f8084528c336fed10dbad3d6a676fd1c629a909"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.mjs",
|
||||
"sha256LF": "b40ed26e30d8215b4bad7eb209fd89201b4e87baca291b899cb60598ad9e38d3"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ambilight/package.json",
|
||||
"sha256LF": "f7d9c4d7e22d5b72db159342f954cf3becda33376d12130fbd15526da40296cb"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ambilight/.npmignore",
|
||||
"sha256LF": "145c5fa0341195e21cf13df3423a057282f8ea374e9a1b3065e2280e1ce1c5c1"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ambilight/README.md",
|
||||
"sha256LF": "d7933f5d5d4ef7815d14d39878968680a6a623417140a7cb8c8bc864d0e69e76"
|
||||
},
|
||||
{
|
||||
"file": "packages/artplayer-plugin-ambilight/ARCHITECTURE.md",
|
||||
"sha256LF": "2a0ef231cfe5d87888a4fc6f6c38ae038e1e049189ca0d8bbfae5bb62f109a50"
|
||||
},
|
||||
{
|
||||
"file": "scripts/build-ts.js",
|
||||
"sha256LF": "1cb6badbce9b974cca0ad7d307aae7bf0008369de78f429590dea4e0323d69e6"
|
||||
},
|
||||
{
|
||||
"file": "scripts/typecheck.mjs",
|
||||
"sha256LF": "0bef8f8c6a1cdf5082fdcc3f4bfdd54f0d8bc30954a0e798db3f27b069ba9295"
|
||||
},
|
||||
{
|
||||
"file": "refactor/scripts/ambilight-types.test.mjs",
|
||||
"sha256LF": "7abfe50e32b899fd15ee248ac07878211533c569afe85ae26a6555bca19d0665"
|
||||
},
|
||||
{
|
||||
"file": "refactor/scripts/ambilight-package-types.mjs",
|
||||
"sha256LF": "8df883909fb4db92e59043265a92e4c7d01c2408b91c2a5da6e8abdccd963e3f"
|
||||
},
|
||||
{
|
||||
"file": "refactor/fixtures/consumers/ambilight-published.ts",
|
||||
"sha256LF": "fb440616e3eec4b3bfd0f6063b63fc9590a46ca471c5f1ffefacf97cff3c547a"
|
||||
},
|
||||
{
|
||||
"file": "test/types/ambilight.ts",
|
||||
"sha256LF": "aa2e4ebf4eb54a3713acf0c7a89be5eca88f7b54f75615435bd9cac664a0574a"
|
||||
},
|
||||
{
|
||||
"file": "test/browser/ambilight-lifecycle.spec.js",
|
||||
"sha256LF": "321693a24d33f0c7aee02fd4137b55175386562a645ba138e15c3a51d6348d2c"
|
||||
},
|
||||
{
|
||||
"file": "docs/assets/ts/artplayer-plugin-ambilight.d.ts",
|
||||
"sha256LF": "162bf7139e8c1e453b0aaf7290eb08ff698d3db19fae8594e9787b07efa40612"
|
||||
},
|
||||
{
|
||||
"file": "package.json",
|
||||
"sha256LF": "1c0a6f5b206cb4ecdaec8025369099b4ae8df9d7b18d987e03f91164ed96fcae"
|
||||
}
|
||||
],
|
||||
"logs": [
|
||||
{
|
||||
"file": "refactor/.cache/ambilight04-before.log",
|
||||
"sha256": "c2d01f5ac9fbfd9be61187b0eaf5d27fc61e0b5019828df621bad745ce5d17f0"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ambilight04-types.log",
|
||||
"sha256": "cf385247c78a3e95acb5738356fbdb456f993c16c0e963fd5b532573efaf00ab"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ambilight04-package.log",
|
||||
"sha256": "68abe529928ae4138be893c1a5b3ef5ffa24c70a404f99d833b405b2246733bd"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ambilight04-package-second.log",
|
||||
"sha256": "b29dca1e79f9549f3fa7ef05700f246ee7a6e006476f1b5e02f6b28897b2ffd0"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ambilight04-package-final.log",
|
||||
"sha256": "ef927b871eb509e9c62b305539dfca8b35035a504fe6a3cf0073e61a7f542093"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ambilight04-build.log",
|
||||
"sha256": "2c8fa2a92b824975142d6506c13ef626d8b1fa2b97571a924e0b4e2c8658c2ff"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ambilight04-editor-build.log",
|
||||
"sha256": "f3304ba150de716716b67d4ca7d9995826b48ae2eee15169e79c764d7942de05"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ambilight04-lint-final.log",
|
||||
"sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ambilight04-ci.log",
|
||||
"sha256": "6b2bbfe06464a281b83c9d877b486f02a5520f9b99a89352e400d766b86d6a90"
|
||||
},
|
||||
{
|
||||
"file": "refactor/.cache/ambilight04-browser.log",
|
||||
"sha256": "e7688c4721ba67d0fb1b99350571b3679c04b4f85f823d730b314d1f4ff47147"
|
||||
}
|
||||
],
|
||||
"limitations": [
|
||||
"Installed matrix uses the same packed candidate core for all plugin declaration versions; it does not establish a historical runtime core support range.",
|
||||
"Proxy and device validation is task 05. Complete distribution, actual installed-browser and 8082 demo/editor interaction remain task 06.",
|
||||
"The generic test:package still covers core/chapter; the dedicated Ambilight installation script does not imply whole-ecosystem acceptance.",
|
||||
"No push, publish, release version change or remote workflow run."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
# PKG-AMBILIGHT-04 公开类型与历史导出兼容
|
||||
|
||||
03已完成自有生产源码的严格TS拆分;04补齐公共声明、Node解析模式和真实安装消费者。
|
||||
最终验证结果见 [类型与安装证据](../baselines/ambilight-types-validation.json)。
|
||||
|
||||
## 兼容门面
|
||||
|
||||
工厂增加指向自身的default属性:同时支持1.0.0的require(pkg).default、1.1.0的直接
|
||||
require调用和ESM默认导入。公共声明使用callable与类型namespace合并;Option/Result/
|
||||
Callable/Factory可显式导入,start/stop仍为void、name仍是原字面量。可选调用重载补齐
|
||||
省略参数及undefined,最后一个必填Option重载保持Parameters<typeof factory>[0]不增加
|
||||
顶层undefined。zIndex保留但清楚标注运行时忽略。
|
||||
|
||||
1.0.0所有字段必填,1.1.0已经改成可选;旧的“读取字段一定是string/number”推导差异
|
||||
是已发布历史变化。本候选保持1.1.0推导,不能声称恢复1.0.0的必填字段推导,也没有
|
||||
套用Ads的专属批准。两版实际声明和合法调用分别编译对照,精确断言已有差异。
|
||||
|
||||
main/module/legacy/types旧路径保留;import和require分别增加d.mts/d.cts转发,旧TS
|
||||
通过typesVersions解析legacy。编辑器改用已有语义生成器,重新运行build:ts,不手改
|
||||
生成声明。包排除tsconfig.json,修复真实pack发现的实现配置泄漏。
|
||||
|
||||
## 证据范围
|
||||
|
||||
新增类型/产物测试先在03状态运行:4组中3失败,历史调用对照通过。工作区测试覆盖
|
||||
TS5.9.3的Node10/NodeNext CJS、ESM/Bundler及TS4.3.5,10项非法用法去掉expect-error后
|
||||
必须逐项报错。编辑器消费亦有负向断言;Node执行正式main/legacy/global/ESM产物。
|
||||
|
||||
新脚本test:ambilight-types-package在仓库外安装两个真实npm发布及候选tarball,搭配
|
||||
同一打包候选核心;核对每个已安装文件哈希、非workspace链接、离线及frozen二次安装。
|
||||
候选额外验证两个TS版本关闭esModuleInterop仍可默认导入。旧npm1.1.0在NodeNext ESM
|
||||
实装中暴露默认导出不可调用的既有错误,按精确4个诊断保存,候选该模式必须零诊断。
|
||||
不修改历史包或把该错误当作候选容许失败。
|
||||
|
||||
本步没有新依赖;只添加专项脚本和消费者。代理/实际设备归05,完整分发/8082 demo及
|
||||
安装包浏览器归06;本步包内README/架构说明同步更新,未推送或发布。
|
||||
|
||||
## 最终验收
|
||||
|
||||
四组专项检查通过;真实安装17个场景中候选7个、历史9个零诊断,另1个历史场景按
|
||||
固定4条诊断核验。候选每个场景去掉expect-error后均拒绝10项非法输入。正式三格式
|
||||
与编辑器生成通过;完整CI975项通过(878单元+14工程+83基线),另44项重复契约观察,
|
||||
278个生产TS文件严格检查。18项三核心/三引擎原生Canvas媒体回归通过,无skip。
|
||||
|
||||
AMBILIGHT-TYPE-01及AMBILIGHT-DIST-01对应的已复现类型/导出问题关闭,本任务独立
|
||||
本地提交并审计自身。关闭这两条具体问题不等于05/06或npm发布验收完成;VAST专属
|
||||
VPN外部脚本例外没有用于本任务。
|
||||
@@ -0,0 +1,10 @@
|
||||
import Artplayer from 'artplayer'
|
||||
import ambilight from 'artplayer-plugin-ambilight'
|
||||
|
||||
const option = { blur: '50px', opacity: 0.5, frequency: 10, zIndex: 9, duration: 0.3 }
|
||||
const art = new Artplayer({ container: '#player', url: 'video.mp4', plugins: [ambilight(option)] })
|
||||
const result = ambilight(option)(art)
|
||||
const name: 'artplayerPluginAmbilight' = result.name
|
||||
const calls: void[] = [result.start(), result.stop()]
|
||||
const accepted: Parameters<typeof ambilight>[0] = option
|
||||
void [name, calls, accepted]
|
||||
+3
-2
@@ -4,7 +4,7 @@
|
||||
|
||||
基线:`40fcda6a37d0049d42e49c1e64e70d4fd9ba5f7f`。总任务 220 项,范围 22 个包及工作区/示例。
|
||||
|
||||
状态:todo 127 / doing 6 / blocked 0 / done 87 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
状态:todo 126 / doing 6 / blocked 0 / done 88 / deferred 0。风险 L/M/H 表示兼容风险,不表示工期。
|
||||
|
||||
前置依赖是启动条件;验收是完成条件。任务可以继续拆分,但不能复用或悄悄删除旧 ID。
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
| PKG-AMBILIGHT-01 | artplayer-plugin-ambilight<br>核对包契约与历史用法 | BASE-05 | blur/opacity/frequency/duration、start/stop 与挂载样式 | 源码/声明/README/demo/发布包差异已登记;公开形状和版本范围冻结 | M | done |
|
||||
| PKG-AMBILIGHT-02 | artplayer-plugin-ambilight<br>建立特有行为与错误测试 | PKG-AMBILIGHT-01, ENG-03, ENG-05 | 跨域 canvas 失败、零尺寸、暂停、重复 start/stop 和 destroy | 旧版本行为可重跑,成功/失败/切源/销毁有必要断言 | M | done |
|
||||
| PKG-AMBILIGHT-03 | artplayer-plugin-ambilight<br>整理内部职责与资源 | PKG-AMBILIGHT-02, CORE-03, CORE-12 | 取色计算与绘帧分离,统一 RAF 和 DOM 清理 | 结构变化和缺陷修复分开记录;原 API/事件/资源生命周期通过 | M | done |
|
||||
| PKG-AMBILIGHT-04 | artplayer-plugin-ambilight<br>迁移自有源码和公开类型 | PKG-AMBILIGHT-03, ENG-04, ENG-06, CORE-07 | canvas 上下文、参数及 start/stop 的明确类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | M | todo |
|
||||
| PKG-AMBILIGHT-04 | artplayer-plugin-ambilight<br>迁移自有源码和公开类型 | PKG-AMBILIGHT-03, ENG-04, ENG-06, CORE-07 | canvas 上下文、参数及 start/stop 的明确类型 | 严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容 | M | done |
|
||||
| PKG-AMBILIGHT-05 | artplayer-plugin-ambilight<br>验证新旧核心和组合 | PKG-AMBILIGHT-04, CORE-22 | 原生 video、canvas proxy 的能力边界及销毁无帧循环 | 最终核心与原支持范围核心分别通过;设备/SDK 缺证据不能标完成 | M | todo |
|
||||
| PKG-AMBILIGHT-06 | artplayer-plugin-ambilight<br>验证分发并同步文档 | PKG-AMBILIGHT-05, ENG-07 | ambilight.js 示例及样式/分发验证 | tarball 入口/资源、类型、8082 demo 和 README 一致,有回退记录 | M | todo |
|
||||
|
||||
@@ -479,6 +479,7 @@
|
||||
- PKG-AMBILIGHT-01: [记录](changes/2026-09-12-PKG-AMBILIGHT-01-contract.md) [记录](baselines/ambilight-contract.md) [记录](baselines/ambilight-release.json) [记录](baselines/ambilight-contract-validation.json)
|
||||
- PKG-AMBILIGHT-02: [记录](changes/2026-09-12-PKG-AMBILIGHT-02-tests.md) [记录](baselines/ambilight-behavior-validation.json) [记录](ambilight-validation.md)
|
||||
- PKG-AMBILIGHT-03: [记录](changes/2026-09-12-PKG-AMBILIGHT-03-lifecycle.md) [记录](ambilight-validation.md) [记录](baselines/ambilight-lifecycle-validation.json)
|
||||
- PKG-AMBILIGHT-04: [记录](changes/2026-09-12-PKG-AMBILIGHT-04-types.md) [记录](ambilight-validation.md) [记录](baselines/ambilight-types-validation.json)
|
||||
- PKG-AUDIO-01: [记录](changes/2026-09-12-PKG-AUDIO-01-contract.md) [记录](baselines/audio-track-contract.md) [记录](baselines/audio-track-release.json)
|
||||
- PKG-AUDIO-02: [记录](changes/2026-09-12-PKG-AUDIO-02-tests.md) [记录](audio-validation.md) [记录](baselines/audio-validation.json)
|
||||
- PKG-AUDIO-03: [记录](changes/2026-09-12-PKG-AUDIO-03-lifecycle.md) [记录](baselines/audio-lifecycle-validation.json)
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# 进度与证据
|
||||
|
||||
## 最新完成:Ambilight-04 公开类型与两代导出
|
||||
|
||||
公共声明补齐可选调用,保留1.1.0的Parameters推导;CJS直接调用与历史.default自别名
|
||||
同时可用,ESM/CJS声明身份、旧TS legacy映射、编辑器生成和打包排除已同步。
|
||||
仓库外安装两版真实发布及候选,共17个场景(候选7个/历史9个零诊断,另1个历史
|
||||
NodeNext ESM场景按4个精确诊断保存),候选各拒绝10项非法用法。四组专项、完整
|
||||
CI975项及18项三核心/三引擎浏览器通过,278个生产TS文件严格检查;类型/两代导出
|
||||
具体风险关闭。当前220项:88 done、6 doing、126 todo。下一步05代理/设备组合,
|
||||
06完整分发/demo仍未完成。见 [实施记录](changes/2026-09-12-PKG-AMBILIGHT-04-types.md)
|
||||
和 [验证证据](baselines/ambilight-types-validation.json)。独立本地提交,无推送/发布。
|
||||
|
||||
## 最新完成:Ambilight-03 TS模块与生命周期修复
|
||||
|
||||
生产源码已拆成入口、视图、取色、帧调度4个严格TS模块,修复销毁残留、重入旧帧、
|
||||
|
||||
@@ -152,5 +152,5 @@
|
||||
| VAST-LIFE-01 | open / 已复现 | VAST SDK and container lack core destruction ownership and pending work cancellation | PKG-VAST-02, PKG-VAST-03, PKG-VAST-05 |
|
||||
| VAST-DIST-01 | open / 已复现 | Published VAST CommonJS default namespace differs from the current callable distribution | PKG-VAST-04, PKG-VAST-06 |
|
||||
| AMBILIGHT-LIFE-01 | open / 已复现 | Ambilight sampling and RAF/DOM ownership lack terminal/error handling | PKG-AMBILIGHT-02, PKG-AMBILIGHT-03, PKG-AMBILIGHT-05 |
|
||||
| AMBILIGHT-TYPE-01 | open / 源码/产物事实 | Ambilight declarations disagree with optional factory argument and differ across published generations | PKG-AMBILIGHT-04, PKG-AMBILIGHT-06 |
|
||||
| AMBILIGHT-DIST-01 | open / 已复现 | Ambilight 1.0.0 CommonJS default namespace differs from callable 1.1.0 | PKG-AMBILIGHT-04, PKG-AMBILIGHT-06 |
|
||||
| AMBILIGHT-TYPE-01 | resolved / 已复现 | Ambilight declarations disagree with optional factory argument and differ across published generations | PKG-AMBILIGHT-04, PKG-AMBILIGHT-06 |
|
||||
| AMBILIGHT-DIST-01 | resolved / 已复现 | Ambilight 1.0.0 CommonJS default namespace differs from callable 1.1.0 | PKG-AMBILIGHT-04, PKG-AMBILIGHT-06 |
|
||||
+15
-5
@@ -3279,19 +3279,24 @@
|
||||
{
|
||||
"id": "AMBILIGHT-TYPE-01",
|
||||
"title": "Ambilight declarations disagree with optional factory argument and differ across published generations",
|
||||
"confirmation": "source-observed",
|
||||
"confirmation": "reproduced",
|
||||
"owners": [
|
||||
"PKG-AMBILIGHT-04",
|
||||
"PKG-AMBILIGHT-06"
|
||||
],
|
||||
"compatibleResolution": "Preserve legal old/new calls and ignored zIndex, validate export= and default consumers without narrowing inferred options.",
|
||||
"closureCriteria": "Installed old/current compiler consumers and documented historical limitations verified.",
|
||||
"status": "open",
|
||||
"status": "resolved",
|
||||
"evidence": [
|
||||
"refactor/baselines/ambilight-contract.md",
|
||||
"refactor/baselines/ambilight-release.json",
|
||||
"refactor/changes/2026-09-12-PKG-AMBILIGHT-01-contract.md"
|
||||
]
|
||||
],
|
||||
"resolutionEvidence": [
|
||||
"refactor/changes/2026-09-12-PKG-AMBILIGHT-04-types.md",
|
||||
"refactor/baselines/ambilight-types-validation.json"
|
||||
],
|
||||
"resolutionRationale": "Packed candidate passes seven old/current compiler configurations, including no-interop default imports; optional factory overload preserves published 1.1.0 Parameters input. Both exact published declarations and prior 1.0.0-to-1.1.0 field optionality difference are verified. Candidate corrects the actual 1.1.0 NodeNext ESM failure; this is not full device or distribution acceptance."
|
||||
},
|
||||
{
|
||||
"id": "AMBILIGHT-DIST-01",
|
||||
@@ -3303,12 +3308,17 @@
|
||||
],
|
||||
"compatibleResolution": "Keep callable factory with compatible self default alias.",
|
||||
"closureCriteria": "Packed root/legacy/global/ESM and old namespace consumers pass.",
|
||||
"status": "open",
|
||||
"status": "resolved",
|
||||
"evidence": [
|
||||
"refactor/baselines/ambilight-contract.md",
|
||||
"refactor/baselines/ambilight-release.json",
|
||||
"refactor/changes/2026-09-12-PKG-AMBILIGHT-01-contract.md"
|
||||
]
|
||||
],
|
||||
"resolutionEvidence": [
|
||||
"refactor/changes/2026-09-12-PKG-AMBILIGHT-04-types.md",
|
||||
"refactor/baselines/ambilight-types-validation.json"
|
||||
],
|
||||
"resolutionRationale": "Actual production and installed root/legacy callable factories have self default identity, native ESM default is callable and script globals match. Public default/import= consumers pass. Broader deep-import/demo/package-browser review remains task 06."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
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 { verifyAmbilightContract } from './ambilight-contract.mjs'
|
||||
import { hash, readMember } from './releases.mjs'
|
||||
|
||||
async function main() {
|
||||
assert.equal(process.env.npm_config_user_agent?.split(' ')[0], 'yarn/1.22.22', 'Use yarn test:ambilight-types-package')
|
||||
const yarn = process.env.npm_execpath
|
||||
assert(yarn && fs.existsSync(yarn))
|
||||
const output = fs.mkdtempSync(path.join(workspace, 'refactor/.cache/ambilight-package-types-'))
|
||||
console.log(`Ambilight isolated package evidence: ${output}`)
|
||||
const contract = await verifyAmbilightContract()
|
||||
const packages = []
|
||||
for (const name of ['artplayer', 'artplayer-plugin-ambilight']) {
|
||||
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 matrix = []
|
||||
const core = packages[0]
|
||||
const candidates = [
|
||||
...[contract.baseline.previous[0], contract.baseline.release].map(release => ({ ...release, archive: contract.archives.get(release.version), label: `published-${release.version}` })),
|
||||
{ ...packages[1], label: 'candidate' },
|
||||
]
|
||||
for (const plugin of candidates) {
|
||||
const consumer = consumerDirectory()
|
||||
try {
|
||||
writeJson(path.join(consumer, 'package.json'), { name: 'ambilight-isolated-consumer', private: true, dependencies: Object.fromEntries([core, 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 [core, 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, plugin.label === 'candidate'
|
||||
? `const assert = require('node:assert/strict');
|
||||
for (const name of ['artplayer-plugin-ambilight', 'artplayer-plugin-ambilight/legacy']) { const factory = require(name); assert.equal(typeof factory, 'function'); assert.equal(factory.default, factory); assert.equal(typeof factory(), 'function'); }
|
||||
import('artplayer-plugin-ambilight').then(module => { assert.deepEqual(Object.keys(module), ['default']); assert.equal(module.default.default, module.default); }).catch(error => { console.error(error); process.exitCode = 1; });`
|
||||
: `const assert = require('node:assert/strict'); const value = require('artplayer-plugin-ambilight'); assert.equal(typeof value, '${plugin.version === '1.0.0' ? 'object' : 'function'}'); assert.equal(typeof (value.default || value)({}), 'function');`)
|
||||
fs.writeFileSync(path.join(output, `${plugin.label}-runtime.log`), run([runtimeFile], consumer))
|
||||
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
|
||||
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 extension = next ? mode.endsWith('-cjs') ? 'cts' : 'mts' : 'ts'
|
||||
const source = fs.readFileSync(path.join(workspace, plugin.label === 'candidate' ? 'test/types/ambilight.ts' : 'refactor/fixtures/consumers/ambilight-published.ts'), 'utf8')
|
||||
const filename = path.join(consumer, `consumer.${extension}`)
|
||||
const historical = path.join(consumer, `historical.${extension}`)
|
||||
fs.copyFileSync(path.join(workspace, 'refactor/fixtures/consumers/ambilight-published.ts'), historical)
|
||||
const files = plugin.label === 'candidate' ? [filename, historical] : [filename]
|
||||
if (mode === 'nodenext-cjs' && plugin.label !== 'published-1.1.0') {
|
||||
const commonjs = path.join(consumer, 'commonjs.cts')
|
||||
fs.writeFileSync(commonjs, `import ambilight = require('artplayer-plugin-ambilight'); ambilight({ blur: '50px', opacity: 0.5, frequency: 10, zIndex: 9, duration: 0.3 }); ${plugin.label === 'candidate' ? 'ambilight.default();' : ''}`)
|
||||
files.push(commonjs)
|
||||
}
|
||||
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(code) {
|
||||
fs.writeFileSync(filename, code)
|
||||
const program = compiler.createProgram(files, 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, message: compiler.flattenDiagnosticMessageText(item.messageText, '\n') }))
|
||||
}
|
||||
const diagnostics = compile(source)
|
||||
const historicalFailure = plugin.label === 'published-1.1.0' && mode === 'nodenext-esm'
|
||||
assert.deepEqual(diagnostics.map(item => item.code), historicalFailure ? [2349, 2349, 2322, 2344] : [], `${plugin.label} ${compiler.version} ${mode}`)
|
||||
const invalid = plugin.label === 'candidate' ? compile(source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')) : []
|
||||
if (plugin.label === 'candidate')
|
||||
assert.equal(invalid.length, 10, 'Installed declarations must reject all invalid uses')
|
||||
matrix.push({ plugin: plugin.label, compiler: compiler.version, mode, historicalFailure, diagnostics, invalid })
|
||||
}
|
||||
}
|
||||
finally {
|
||||
removeConsumer(consumer)
|
||||
}
|
||||
}
|
||||
writeJson(path.join(output, 'report.json'), { suite: 'ambilight-isolated-package-types', introducedBy: 'PKG-AMBILIGHT-04', scope: 'Two actual published plugins and packed candidate installed outside workspace with packed candidate core; offline and frozen reinstall, exact bytes, compiler/default/import= contracts. Published 1.0.0 required option fields became optional in 1.1.0; candidate retains 1.1.0 extraction. This is not proxy, device or complete distribution acceptance.', packages, published: candidates.slice(0, 2).map(pkg => ({ label: pkg.label, archive: pkg.archive, sha256: pkg.sha256 })), matrix })
|
||||
console.log(`Ambilight installed types passed: ${matrix.length} cases; ${output}`)
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error)
|
||||
process.exitCode = 1
|
||||
})
|
||||
@@ -0,0 +1,90 @@
|
||||
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 -- Node baseline runner.
|
||||
import test from 'node:test'
|
||||
import vm from 'node:vm'
|
||||
import { ESLint } from 'eslint'
|
||||
import ts from 'typescript'
|
||||
import compat from 'typescript-compat'
|
||||
import { checkPluginEditorDeclaration, generatePluginEditorDeclaration } from '../../scripts/plugin-editor-types.mjs'
|
||||
import { checkConsumer } from '../../scripts/typecheck.mjs'
|
||||
import { verifyAmbilightContract } from './ambilight-contract.mjs'
|
||||
import { readMember } from './releases.mjs'
|
||||
|
||||
const modes = [[ts, 'node10-commonjs'], [ts, 'nodenext-cjs'], [ts, 'nodenext-esm'], [ts, 'bundler-esm'], [compat, 'node10-commonjs']]
|
||||
|
||||
test('Ambilight current types retain inference, optional runtime arguments and ten invalid-use rejections', () => {
|
||||
const source = fs.readFileSync('test/types/ambilight.ts', 'utf8')
|
||||
for (const [compiler, mode] of modes) {
|
||||
assert.deepEqual(checkConsumer(compiler, mode, source), [], `${compiler.version} ${mode}`)
|
||||
assert.equal(checkConsumer(compiler, mode, source.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, '')).length, 10)
|
||||
}
|
||||
assert.deepEqual(checkConsumer(ts, 'nodenext-cjs', `import ambilight = require('artplayer-plugin-ambilight'); import legacy = require('artplayer-plugin-ambilight/legacy');
|
||||
const option: ambilight.Option = {}; ambilight(option); ambilight.default(option); legacy(); legacy.default(option);`), [])
|
||||
})
|
||||
|
||||
test('Ambilight published calls compile against both exact historical declarations and candidate without new inference widening', async () => {
|
||||
const contract = await verifyAmbilightContract()
|
||||
const source = fs.readFileSync('refactor/fixtures/consumers/ambilight-published.ts', 'utf8')
|
||||
for (const version of ['1.0.0', '1.1.0']) {
|
||||
const old = readMember(contract.archives.get(version), 'package/types/artplayer-plugin-ambilight.d.ts').toString()
|
||||
const required = `declare const input: Parameters<typeof ambilight>[0]; const blur: string = input.blur; const opacity: number = input.opacity; void [blur, opacity];`
|
||||
for (const compiler of [ts, compat]) {
|
||||
const filename = path.resolve('test/types/ambilight-history.ts')
|
||||
const declaration = path.resolve('test/types/ambilight-history.d.ts')
|
||||
function historical(code) {
|
||||
const host = compiler.createCompilerHost({})
|
||||
const original = host.getSourceFile.bind(host)
|
||||
host.getSourceFile = (file, language, ...rest) => path.resolve(file) === filename
|
||||
? compiler.createSourceFile(file, code.replace('from \'artplayer-plugin-ambilight\'', 'from \'./ambilight-history\''), language, true)
|
||||
: path.resolve(file) === declaration ? compiler.createSourceFile(file, old, language, true) : original(file, language, ...rest)
|
||||
const exists = host.fileExists.bind(host)
|
||||
host.fileExists = file => path.resolve(file) === declaration || exists(file)
|
||||
const program = compiler.createProgram([filename, declaration], { strict: true, noEmit: true, skipLibCheck: false, types: [], esModuleInterop: true, module: compiler.ModuleKind.CommonJS, moduleResolution: compiler.ModuleResolutionKind.NodeJs, target: compiler.ScriptTarget.ES2020, lib: ['lib.es2020.d.ts', 'lib.dom.d.ts'] }, host)
|
||||
assert(program.getSourceFile(declaration))
|
||||
return compiler.getPreEmitDiagnostics(program).map(item => item.code)
|
||||
}
|
||||
assert.deepEqual(historical(source), [])
|
||||
assert.deepEqual(historical(`${source}\n${required}`), version === '1.0.0' ? [] : [2322, 2322])
|
||||
assert.deepEqual(checkConsumer(compiler, 'node10-commonjs', source), [])
|
||||
assert.deepEqual(checkConsumer(compiler, 'node10-commonjs', `${source}\n${required}`).map(item => item.code), [2322, 2322], 'Optional fields were introduced in published 1.1.0; no new difference from that baseline')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
test('Ambilight editor globals are generated from the public namespace and retain negative checks', async () => {
|
||||
const source = fs.readFileSync('packages/artplayer-plugin-ambilight/types/artplayer-plugin-ambilight.d.ts', 'utf8')
|
||||
const generated = generatePluginEditorDeclaration(source, 'artplayerPluginAmbilight')
|
||||
const file = 'docs/assets/ts/artplayer-plugin-ambilight.d.ts'
|
||||
const [formatted] = await new ESLint({ fix: true, fixTypes: ['layout'] }).lintText(generated, { filePath: file })
|
||||
assert.equal(formatted.errorCount, 0)
|
||||
assert.equal(fs.readFileSync(file, 'utf8').replaceAll('\r\n', '\n'), formatted.output || generated)
|
||||
const core = fs.readFileSync('docs/assets/ts/artplayer.d.ts', 'utf8')
|
||||
const consumer = `const option: artplayerPluginAmbilight.Option = {}; artplayerPluginAmbilight(); artplayerPluginAmbilight.default(option);
|
||||
// @ts-expect-error Invalid blur.
|
||||
artplayerPluginAmbilight({ blur: 2 });`
|
||||
for (const compiler of [ts, compat]) {
|
||||
assert.deepEqual(checkPluginEditorDeclaration(generated, core, consumer, compiler), [])
|
||||
assert.equal(checkPluginEditorDeclaration(generated, core, consumer.replaceAll(/\/\/ @ts-expect-error[^\n]*\n/g, ''), compiler).length, 1)
|
||||
}
|
||||
})
|
||||
|
||||
test('Ambilight production main, legacy, global and native ESM preserve callable and default identities', async () => {
|
||||
for (const suffix of ['js', 'legacy.js']) {
|
||||
const source = fs.readFileSync(`packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.${suffix}`, 'utf8')
|
||||
const module = { exports: {} }
|
||||
vm.runInNewContext(source, { module, exports: module.exports, window: {} })
|
||||
assert.equal(typeof module.exports, 'function')
|
||||
assert.equal(module.exports.default, module.exports)
|
||||
assert.equal(typeof module.exports.default(), 'function')
|
||||
const global = { window: {} }
|
||||
vm.runInNewContext(source, global)
|
||||
const factory = global.artplayerPluginAmbilight || global.window.artplayerPluginAmbilight
|
||||
assert.equal(factory.default, factory)
|
||||
}
|
||||
const source = fs.readFileSync('packages/artplayer-plugin-ambilight/dist/artplayer-plugin-ambilight.mjs', 'utf8')
|
||||
const module = await import(`data:text/javascript,${encodeURIComponent(source)}`)
|
||||
assert.deepEqual(Object.keys(module), ['default'])
|
||||
assert.equal(module.default.default, module.default)
|
||||
})
|
||||
+6
-2
@@ -1526,11 +1526,15 @@
|
||||
"ENG-06",
|
||||
"CORE-07"
|
||||
],
|
||||
"status": "todo",
|
||||
"status": "done",
|
||||
"risk": "M",
|
||||
"deliverable": "canvas 上下文、参数及 start/stop 的明确类型",
|
||||
"acceptance": "严格类型检查、旧消费样例通过;声明路径/导出和同步异步兼容",
|
||||
"evidence": []
|
||||
"evidence": [
|
||||
"changes/2026-09-12-PKG-AMBILIGHT-04-types.md",
|
||||
"ambilight-validation.md",
|
||||
"baselines/ambilight-types-validation.json"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "PKG-AMBILIGHT-05",
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ console.log(`✨ Built ${artplayerTSoutput}`);
|
||||
const type = pluginsTS[index]
|
||||
const { name, file } = parsePluginInfo(type)
|
||||
const source = String(fs.readFileSync(type))
|
||||
const semanticPlugin = ['artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl', 'artplayerPluginAds'].includes(name)
|
||||
const semanticPlugin = ['artplayerPluginHlsControl', 'artplayerPluginAudioTrack', 'artplayerPluginDashControl', 'artplayerPluginAds', 'artplayerPluginAmbilight'].includes(name)
|
||||
const code = semanticPlugin
|
||||
? generatePluginEditorDeclaration(source, name)
|
||||
: `${source.replace(reg, '')}\nexport = ${name};\nexport as namespace ${name};\n`
|
||||
|
||||
@@ -76,6 +76,8 @@ export function runTypechecks() {
|
||||
for (const fixture of ['test/types/public.ts', 'test/types/declaration-inputs.ts', 'test/types/declaration-legacy.ts', 'test/types/plugins-public.ts', 'test/types/playback-public.ts', 'test/types/chapter-options.ts', 'test/types/chapter-exports.ts', 'test/types/hls-control.ts', 'test/types/dash-control.ts', 'test/types/ads.ts', 'refactor/fixtures/consumers/ads-published.ts', 'refactor/fixtures/consumers/ads-workspace.ts', 'refactor/fixtures/consumers/dash-control-legacy.ts', 'test/types/audio-track.ts', 'test/types/language-value.ts', 'refactor/fixtures/consumers/language.ts', 'refactor/fixtures/consumers/legacy-plugin.ts']) {
|
||||
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, fixture), 'utf8')), [], `Consumer failed: TS ${compiler.version} ${mode} ${fixture}`)
|
||||
}
|
||||
for (const fixture of ['test/types/ambilight.ts', 'refactor/fixtures/consumers/ambilight-published.ts'])
|
||||
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, fixture), 'utf8')), [], `Consumer failed: TS ${compiler.version} ${mode} ${fixture}`)
|
||||
if (mode === 'nodenext-cjs')
|
||||
assert.deepEqual(checkConsumer(compiler, mode, fs.readFileSync(path.join(root, 'test/types/commonjs.cts'), 'utf8')), [], 'CommonJS export assignment consumer failed')
|
||||
console.log(`Consumers passed: TS ${compiler.version} ${mode}`)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import type { Factory, Option, Result } from 'artplayer-plugin-ambilight'
|
||||
import Artplayer from 'artplayer'
|
||||
import ambilight from 'artplayer-plugin-ambilight'
|
||||
import legacy from 'artplayer-plugin-ambilight/legacy'
|
||||
|
||||
const option: Option = { blur: '40px', opacity: 0.6, frequency: 10, duration: 0.2, zIndex: 3 }
|
||||
const art = new Artplayer({ container: '#player', url: 'video.mp4', plugins: [ambilight(), ambilight(option), legacy({})] })
|
||||
ambilight(undefined)
|
||||
ambilight.default(option)
|
||||
legacy.default()
|
||||
declare const optional: Option | undefined
|
||||
ambilight(optional)
|
||||
const inferred: Parameters<typeof ambilight>[0] = option
|
||||
const blur: string | undefined = inferred.blur
|
||||
const factory: Factory = ambilight
|
||||
const result: Result = factory(option)(art)
|
||||
const name: 'artplayerPluginAmbilight' = result.name
|
||||
const calls: void[] = [result.start(), result.stop()]
|
||||
const registration: (art: Artplayer) => Result = ambilight()
|
||||
// @ts-expect-error Keep required last overload for existing Parameters extraction.
|
||||
const missing: Parameters<typeof ambilight>[0] = undefined
|
||||
// @ts-expect-error Blur is a CSS string, not a number.
|
||||
ambilight({ blur: 50 })
|
||||
// @ts-expect-error Opacity remains numeric.
|
||||
ambilight({ opacity: '0.5' })
|
||||
// @ts-expect-error Frequency remains numeric.
|
||||
ambilight({ frequency: '10' })
|
||||
// @ts-expect-error Duration remains numeric.
|
||||
ambilight({ duration: false })
|
||||
// @ts-expect-error The ignored historical zIndex input still has its declared type.
|
||||
ambilight({ zIndex: '9' })
|
||||
// @ts-expect-error Null was never accepted by the public types.
|
||||
ambilight(null)
|
||||
// @ts-expect-error Unknown options do not silently enter the contract.
|
||||
ambilight({ speed: 10 })
|
||||
// @ts-expect-error Methods are synchronous.
|
||||
const promise: Promise<void> = result.start()
|
||||
// @ts-expect-error No public plugin destroy method is introduced.
|
||||
result.destroy()
|
||||
void [blur, name, calls, registration, missing, promise]
|
||||
Reference in new issue
Block a user