更新说明
1、.jpg .png .gif解码功能使用taskpool实现 2、 修复了内存缓存张数设置为1时gif图片消失的问题 Signed-off-by: 明月清风 <qiufeihu1@h-partners.com>
This commit is contained in:
parent
db7f176084
commit
2006641248
|
@ -1,3 +1,9 @@
|
||||||
|
## 2.1.1-rc.5
|
||||||
|
- .jpg .png .gif解码功能使用taskpool实现
|
||||||
|
- 修复了内存缓存张数设置为1时gif图片消失的问题
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 2.1.1-rc.4
|
## 2.1.1-rc.4
|
||||||
|
|
||||||
- 删除pako源码依赖,使用ohpm依赖
|
- 删除pako源码依赖,使用ohpm依赖
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import router from '@system.router';
|
import router from '@ohos.router';
|
||||||
import {
|
import {
|
||||||
ImageKnifeComponent,
|
ImageKnifeComponent,
|
||||||
ImageKnifeOption,
|
ImageKnifeOption,
|
||||||
|
@ -67,7 +67,6 @@ struct IndexFunctionDemo {
|
||||||
.onClick(() => {
|
.onClick(() => {
|
||||||
this.imageKnifeOption2 = {
|
this.imageKnifeOption2 = {
|
||||||
loadSrc: 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658',
|
loadSrc: 'https://gd-hbimg.huaban.com/e0a25a7cab0d7c2431978726971d61720732728a315ae-57EskW_fw658',
|
||||||
|
|
||||||
placeholderSrc: $r('app.media.icon_loading'),
|
placeholderSrc: $r('app.media.icon_loading'),
|
||||||
errorholderSrc: $r('app.media.icon_failed'),
|
errorholderSrc: $r('app.media.icon_failed'),
|
||||||
displayProgress:true,
|
displayProgress:true,
|
||||||
|
@ -80,7 +79,7 @@ struct IndexFunctionDemo {
|
||||||
Button("ImageKnife测试目录页面")
|
Button("ImageKnife测试目录页面")
|
||||||
.onClick(() => {
|
.onClick(() => {
|
||||||
console.log("pages/imageknifeTestCaseIndex 页面跳转")
|
console.log("pages/imageknifeTestCaseIndex 页面跳转")
|
||||||
router.push({ uri: "pages/imageknifeTestCaseIndex" });
|
router.replaceUrl({ url: "pages/imageknifeTestCaseIndex" });
|
||||||
}).margin({ top: 15 })
|
}).margin({ top: 15 })
|
||||||
}.width('100%').height(60).backgroundColor(Color.Pink)
|
}.width('100%').height(60).backgroundColor(Color.Pink)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
*/
|
*/
|
||||||
import hilog from '@ohos.hilog';
|
import hilog from '@ohos.hilog';
|
||||||
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'
|
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'
|
||||||
import {ImageKnife,ImageKnifeDrawFactory,ImageKnifeGlobal} from '@ohos/libraryimageknife'
|
import {ImageKnife,ImageKnifeDrawFactory,ImageKnifeGlobal} from '@ohos/imageknife'
|
||||||
|
|
||||||
export default function ImageKnifeTest() {
|
export default function ImageKnifeTest() {
|
||||||
describe('ImageKnifeTest', ()=> {
|
describe('ImageKnifeTest', ()=> {
|
||||||
|
|
|
@ -14,12 +14,12 @@
|
||||||
"main": "index.ets",
|
"main": "index.ets",
|
||||||
"repository": "https://gitee.com/openharmony-tpc/ImageKnife",
|
"repository": "https://gitee.com/openharmony-tpc/ImageKnife",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "2.1.1-rc.4",
|
"version": "2.1.1-rc.5",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"pako": "^2.1.0",
|
||||||
"@ohos/disklrucache": "^2.0.2-rc.0",
|
"@ohos/disklrucache": "^2.0.2-rc.0",
|
||||||
"@ohos/svg": "^2.1.1-rc.0",
|
"@ohos/svg": "^2.1.1-rc.0",
|
||||||
"@ohos/gpu_transform": "^1.0.0",
|
"@ohos/gpu_transform": "^1.0.0"
|
||||||
"pako": "^2.1.0"
|
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
"ImageCache",
|
"ImageCache",
|
||||||
|
|
|
@ -330,12 +330,6 @@ export class ImageKnife {
|
||||||
|
|
||||||
let signature = request.signature;
|
let signature = request.signature;
|
||||||
|
|
||||||
if (signature != undefined) {
|
|
||||||
console.log("唯一标识:" + signature.getKey())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cacheKey = factories.generateMemoryCacheKey(loadKey,size,transformed,dontAnimateFlag,signature);
|
cacheKey = factories.generateMemoryCacheKey(loadKey,size,transformed,dontAnimateFlag,signature);
|
||||||
|
|
||||||
// 生成磁盘缓存变换后数据key 变换后数据保存在磁盘
|
// 生成磁盘缓存变换后数据key 变换后数据保存在磁盘
|
||||||
|
|
|
@ -85,6 +85,9 @@ export struct ImageKnifeComponent {
|
||||||
|
|
||||||
private detachFromLayout:DetachFromLayout|undefined = undefined;
|
private detachFromLayout:DetachFromLayout|undefined = undefined;
|
||||||
|
|
||||||
|
private detachFromLayoutGIF :DetachFromLayout|undefined = undefined;
|
||||||
|
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
Canvas(this.context)
|
Canvas(this.context)
|
||||||
.width('100%')
|
.width('100%')
|
||||||
|
@ -519,9 +522,13 @@ export struct ImageKnifeComponent {
|
||||||
LogUtil.log('ImageKnifeComponent default drawMainSource end!')
|
LogUtil.log('ImageKnifeComponent default drawMainSource end!')
|
||||||
})
|
})
|
||||||
} else if (data.isGIFFrame()) {
|
} else if (data.isGIFFrame()) {
|
||||||
|
if(data.drawGIFFrame != undefined) {
|
||||||
|
data.drawGIFFrame.isShowOnComponent = true
|
||||||
|
this.detachFromLayoutGIF = data.drawGIFFrame.detachFromLayoutGIF
|
||||||
this.drawGIFFrame(context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId)
|
this.drawGIFFrame(context, data, imageKnifeOption, compWidth, compHeight, setGifTimeId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
drawRetryholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
drawRetryholder(context: CanvasRenderingContext2D, data: ImageKnifeData, imageKnifeOption: ImageKnifeOption, compWidth: number, compHeight: number, setGifTimeId?: (timeId: number) => void) {
|
||||||
LogUtil.log('ImageKnifeComponent default drawRetryholder start!')
|
LogUtil.log('ImageKnifeComponent default drawRetryholder start!')
|
||||||
|
@ -604,7 +611,9 @@ export struct ImageKnifeComponent {
|
||||||
if(this.detachFromLayout != undefined){
|
if(this.detachFromLayout != undefined){
|
||||||
this.detachFromLayout.detach();
|
this.detachFromLayout.detach();
|
||||||
}
|
}
|
||||||
|
if(this.detachFromLayoutGIF != undefined){
|
||||||
|
this.detachFromLayoutGIF.detach();
|
||||||
|
}
|
||||||
this.resetGifData();
|
this.resetGifData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -750,23 +759,7 @@ export struct ImageKnifeComponent {
|
||||||
LogUtil.log('ImageKnifeComponent canvasDrawPixelMap index=' + index)
|
LogUtil.log('ImageKnifeComponent canvasDrawPixelMap index=' + index)
|
||||||
let frame = frames[index];
|
let frame = frames[index];
|
||||||
let pixelmap = frame['drawPixelMap']
|
let pixelmap = frame['drawPixelMap']
|
||||||
let disposal = 0
|
|
||||||
// disposal value is from preFrame
|
|
||||||
if (index >= 1) {
|
|
||||||
let preFrame = frames[index-1]
|
|
||||||
disposal = preFrame.disposalType
|
|
||||||
|
|
||||||
if (disposal === FrameDisposalType.DISPOSE_RestoreBackground) {
|
|
||||||
let left:number = preFrame.dims.left;
|
|
||||||
let top:number = preFrame.dims.top
|
|
||||||
context.clearRect(left, top, compWidth, compHeight);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (disposal === FrameDisposalType.DISPOSE_RestoreBackground) {
|
|
||||||
context.clearRect(0, 0, compWidth, compHeight)
|
context.clearRect(0, 0, compWidth, compHeight)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let scaleType = (typeof this.imageKnifeOption.mainScaleType == 'number') ? this.imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER
|
let scaleType = (typeof this.imageKnifeOption.mainScaleType == 'number') ? this.imageKnifeOption.mainScaleType : ScaleType.FIT_CENTER
|
||||||
context.save();
|
context.save();
|
||||||
let frameW = frames[0].dims.left + frames[0].dims.width
|
let frameW = frames[0].dims.left + frames[0].dims.width
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { GIFFrame } from './utils/gif/GIFFrame'
|
import { GIFFrame } from './utils/gif/GIFFrame'
|
||||||
|
import { DetachFromLayout } from './RequestOption'
|
||||||
|
|
||||||
export enum ImageKnifeType {
|
export enum ImageKnifeType {
|
||||||
PIXELMAP = 'PixelMap',
|
PIXELMAP = 'PixelMap',
|
||||||
|
@ -35,6 +36,26 @@ export class DrawResource {
|
||||||
|
|
||||||
export class DrawGIFFrame {
|
export class DrawGIFFrame {
|
||||||
imageGIFFrames: GIFFrame[] | undefined = undefined
|
imageGIFFrames: GIFFrame[] | undefined = undefined
|
||||||
|
isShowOnComponent:boolean = false;//gif是否显示在组件上 true:显示在组件上 false:不显示在组件上
|
||||||
|
isLruCacheRelease:boolean = false;//当前lru是否释放gif资源,true的就释放了gif资源 false就是没有释放
|
||||||
|
|
||||||
|
detachFromLayoutGIF :DetachFromLayout = {
|
||||||
|
detach:()=> {
|
||||||
|
this.isShowOnComponent = false
|
||||||
|
if(this.isLruCacheRelease) {
|
||||||
|
let gifFrames = this.imageGIFFrames;
|
||||||
|
if (gifFrames != undefined) {
|
||||||
|
for (let i = 0; i < gifFrames.length; i++) {
|
||||||
|
let tempFrame = gifFrames[i];
|
||||||
|
if (tempFrame.drawPixelMap != undefined) {
|
||||||
|
tempFrame.drawPixelMap.release()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.imageGIFFrames = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ImageKnifeData {
|
export class ImageKnifeData {
|
||||||
|
@ -98,6 +119,11 @@ export class ImageKnifeData {
|
||||||
}
|
}
|
||||||
if(this.isGIFFrame()){
|
if(this.isGIFFrame()){
|
||||||
if (this.drawGIFFrame != undefined) {
|
if (this.drawGIFFrame != undefined) {
|
||||||
|
this.drawGIFFrame.isLruCacheRelease = true;
|
||||||
|
|
||||||
|
if(this.drawGIFFrame.isShowOnComponent){
|
||||||
|
return;
|
||||||
|
}else {
|
||||||
let gifFrames = this.drawGIFFrame.imageGIFFrames;
|
let gifFrames = this.drawGIFFrame.imageGIFFrames;
|
||||||
if (gifFrames != undefined) {
|
if (gifFrames != undefined) {
|
||||||
for (let i = 0; i < gifFrames.length; i++) {
|
for (let i = 0; i < gifFrames.length; i++) {
|
||||||
|
@ -108,6 +134,8 @@ export class ImageKnifeData {
|
||||||
}
|
}
|
||||||
this.drawGIFFrame.imageGIFFrames = undefined
|
this.drawGIFFrame.imageGIFFrames = undefined
|
||||||
}
|
}
|
||||||
|
this.drawGIFFrame.imageGIFFrames = undefined
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
import { IParseImage } from '../interface/IParseImage'
|
import { IParseImage } from '../interface/IParseImage'
|
||||||
import image from '@ohos.multimedia.image';
|
import image from '@ohos.multimedia.image';
|
||||||
import { BusinessError } from '@ohos.base'
|
import { BusinessError } from '@ohos.base'
|
||||||
|
import taskpool from '@ohos.taskpool';
|
||||||
|
import { LogUtil } from './LogUtil';
|
||||||
|
|
||||||
export class ParseImageUtil implements IParseImage<PixelMap> {
|
export class ParseImageUtil implements IParseImage<PixelMap> {
|
||||||
parseImage(imageinfo: ArrayBuffer, onCompleteFunction: (value: PixelMap) => void | PromiseLike<PixelMap>, onErrorFunction: (reason?: BusinessError | string) => void) {
|
parseImage(imageinfo: ArrayBuffer, onCompleteFunction: (value: PixelMap) => void | PromiseLike<PixelMap>, onErrorFunction: (reason?: BusinessError | string) => void) {
|
||||||
this.parseImageThumbnail(1, imageinfo, onCompleteFunction, onErrorFunction)
|
this.parseImageThumbnail(1, imageinfo, onCompleteFunction, onErrorFunction)
|
||||||
|
@ -23,34 +26,41 @@ export class ParseImageUtil implements IParseImage<PixelMap> {
|
||||||
|
|
||||||
// scale(0,1)
|
// scale(0,1)
|
||||||
parseImageThumbnail(scale: number, imageinfo: ArrayBuffer, onCompleteFunction: (value: PixelMap) => void | PromiseLike<PixelMap>, onErrorFunction: (reason?: BusinessError | string) => void) {
|
parseImageThumbnail(scale: number, imageinfo: ArrayBuffer, onCompleteFunction: (value: PixelMap) => void | PromiseLike<PixelMap>, onErrorFunction: (reason?: BusinessError | string) => void) {
|
||||||
let imageSource:image.ImageSource = image.createImageSource(imageinfo); // 步骤一:文件转为pixelMap 然后变换 给Image组件
|
taskPoolExecutePixelMap(imageinfo,scale,onCompleteFunction,onErrorFunction);
|
||||||
imageSource.getImageInfo((err, value) => {
|
|
||||||
if (err) {
|
|
||||||
onErrorFunction(err);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// let TAG:string = "ParseImageUtil_TASK"
|
||||||
|
|
||||||
|
@Concurrent
|
||||||
|
async function taskParseImage(arrayBuffer: ArrayBuffer,scale: number): Promise<PixelMap> {
|
||||||
|
let imageSource: image.ImageSource = image.createImageSource(arrayBuffer); // 步骤一:文件转为pixelMap 然后变换 给Image组件
|
||||||
|
let value = await imageSource.getImageInfo();
|
||||||
let hValue = Math.round(value.size.height * scale);
|
let hValue = Math.round(value.size.height * scale);
|
||||||
let wValue = Math.round(value.size.width * scale);
|
let wValue = Math.round(value.size.width * scale);
|
||||||
let defaultSize: image.Size = {
|
let defaultSize: image.Size = {
|
||||||
height: hValue,
|
height: hValue,
|
||||||
width: wValue
|
width: wValue
|
||||||
};
|
};
|
||||||
|
|
||||||
let opts: image.DecodingOptions = {
|
let opts: image.DecodingOptions = {
|
||||||
editable: true,
|
editable: true,
|
||||||
desiredSize: defaultSize
|
desiredSize: defaultSize
|
||||||
};
|
};
|
||||||
|
let pixelMap = await imageSource.createPixelMap(opts)
|
||||||
|
LogUtil.log( "ceshi321 : Succeeded in creating pixelmap taskpool " + pixelMap.getPixelBytesNumber())
|
||||||
imageSource.createPixelMap(opts, (err, pixelmap) => {
|
|
||||||
if (err) {
|
|
||||||
onErrorFunction(err);
|
|
||||||
} else {
|
|
||||||
onCompleteFunction(pixelmap);
|
|
||||||
}
|
|
||||||
imageSource.release()
|
imageSource.release()
|
||||||
})
|
return pixelMap;
|
||||||
|
}
|
||||||
|
|
||||||
})
|
function taskPoolExecutePixelMap(arrayBuffer: ArrayBuffer, scale: number, onCompleteFunction: (value: PixelMap) => void | PromiseLike<PixelMap>, onErrorFunction: (reason?: BusinessError | string) => void) {
|
||||||
}
|
LogUtil.log("ceshi321 : arrayBuffer长度" + arrayBuffer.byteLength)
|
||||||
|
let task = new taskpool.Task(taskParseImage, arrayBuffer,scale)
|
||||||
|
taskpool.execute(task).then((pixelmap: image.PixelMap) => {
|
||||||
|
LogUtil.log('ceshi321 : Succeeded in creating pixelmap Ui .' + pixelmap.getPixelBytesNumber())
|
||||||
|
onCompleteFunction(pixelmap);
|
||||||
|
}).catch((err: string) => {
|
||||||
|
LogUtil.log("ceshi321 : test occur error: " + err)
|
||||||
|
onErrorFunction(err);
|
||||||
|
});
|
||||||
}
|
}
|
|
@ -17,6 +17,8 @@ import { Dims, GIFFrame } from './GIFFrame'
|
||||||
import image from '@ohos.multimedia.image'
|
import image from '@ohos.multimedia.image'
|
||||||
import { BusinessError } from '@ohos.base'
|
import { BusinessError } from '@ohos.base'
|
||||||
import worker, { ErrorEvent, MessageEvents } from '@ohos.worker';
|
import worker, { ErrorEvent, MessageEvents } from '@ohos.worker';
|
||||||
|
import taskpool from '@ohos.taskpool'
|
||||||
|
import { LogUtil } from '../LogUtil'
|
||||||
|
|
||||||
export interface senderData {
|
export interface senderData {
|
||||||
type: string,
|
type: string,
|
||||||
|
@ -30,23 +32,27 @@ export interface gifBackData{
|
||||||
patch: Uint8ClampedArray[],
|
patch: Uint8ClampedArray[],
|
||||||
transparentIndex: number[]
|
transparentIndex: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GIFParseImpl implements IParseGif {
|
export class GIFParseImpl implements IParseGif {
|
||||||
//
|
parseGifs(imageinfo: ArrayBuffer, callback: (data?: GIFFrame[], err?: BusinessError | string) => void) {
|
||||||
parseGifs(imageinfo: ArrayBuffer, callback: (data?:GIFFrame[], err?:BusinessError|string) => void, worker?:worker.ThreadWorker,runMainThread?:boolean) {
|
taskPoolExecutePixelMapList(imageinfo,callback);
|
||||||
// 硬解码流程
|
}
|
||||||
let imageSource = image.createImageSource(imageinfo);
|
}
|
||||||
|
|
||||||
|
@Concurrent
|
||||||
|
async function taskParseGif(arrayBuffer: ArrayBuffer): Promise<GIFFrame[]> {
|
||||||
|
let imageSource = image.createImageSource(arrayBuffer);
|
||||||
|
let data: GIFFrame[] = [];
|
||||||
let decodeOpts: image.DecodingOptions = {
|
let decodeOpts: image.DecodingOptions = {
|
||||||
sampleSize: 1,
|
sampleSize: 1,
|
||||||
editable: true,
|
editable: true,
|
||||||
rotate: 0
|
rotate: 0
|
||||||
}
|
}
|
||||||
let data:GIFFrame[] = [];
|
let pixelList = await imageSource.createPixelMapList(decodeOpts);
|
||||||
imageSource.createPixelMapList(decodeOpts).then((pixelList: Array<PixelMap>) => {
|
|
||||||
//sdk的api接口发生变更:从.getDelayTime() 变为.getDelayTimeList()
|
|
||||||
imageSource.getDelayTimeList().then(delayTimes => {
|
|
||||||
if (pixelList.length > 0) {
|
if (pixelList.length > 0) {
|
||||||
let pixelmap1 = pixelList[0];
|
let pixelmap1 = pixelList[0];
|
||||||
pixelmap1.getImageInfo().then(imageInfo => {
|
let imageInfo = await pixelmap1.getImageInfo();
|
||||||
|
let delayTimes = await imageSource.getDelayTimeList();
|
||||||
for (let i = 0; i < pixelList.length; i++) {
|
for (let i = 0; i < pixelList.length; i++) {
|
||||||
let frame = new GIFFrame();
|
let frame = new GIFFrame();
|
||||||
frame.drawPixelMap = pixelList[i];
|
frame.drawPixelMap = pixelList[i];
|
||||||
|
@ -58,22 +64,18 @@ export class GIFParseImpl implements IParseGif {
|
||||||
}
|
}
|
||||||
data.push(frame)
|
data.push(frame)
|
||||||
}
|
}
|
||||||
callback(data,undefined)
|
|
||||||
imageSource.release();
|
|
||||||
}).catch((err: string) => {
|
|
||||||
imageSource.release();
|
|
||||||
callback(undefined,err)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}).catch((err: string) => {
|
return data;
|
||||||
imageSource.release();
|
|
||||||
callback(undefined,err)
|
|
||||||
})
|
|
||||||
}).catch((err: string) => {
|
|
||||||
imageSource.release();
|
|
||||||
callback(undefined,err)
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function taskPoolExecutePixelMapList(arrayBuffer: ArrayBuffer, callback: (data?: GIFFrame[], err?: BusinessError | string) => void) {
|
||||||
|
LogUtil.log("ceshi321 : arrayBuffer长度" + arrayBuffer.byteLength)
|
||||||
|
let task = new taskpool.Task(taskParseGif, arrayBuffer)
|
||||||
|
taskpool.execute(task).then((imageFrames: GIFFrame[]) => {
|
||||||
|
// LogUtil.log('ceshi321 : Succeeded in creating pixelmap Ui .' + imageFrames.getPixelBytesNumber())
|
||||||
|
callback(imageFrames,undefined)
|
||||||
|
}).catch((err: string) => {
|
||||||
|
LogUtil.log("ceshi321 : test occur error: " + err)
|
||||||
|
callback(undefined,err);
|
||||||
|
});
|
||||||
}
|
}
|
|
@ -6,6 +6,6 @@
|
||||||
"name": "imageknife",
|
"name": "imageknife",
|
||||||
"description": "example description",
|
"description": "example description",
|
||||||
"repository": {},
|
"repository": {},
|
||||||
"version": "2.1.1-rc.4",
|
"version": "2.1.1-rc.5",
|
||||||
"dependencies": {}
|
"dependencies": {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue