feat(device model component): add device model component to create model file
|
@ -1,4 +1,4 @@
|
|||
out
|
||||
node_modules
|
||||
dist
|
||||
.vscode-test
|
||||
.vscode-test
|
||||
cache
|
|
@ -0,0 +1,43 @@
|
|||
const callbackStack = [];
|
||||
const vscode = acquireVsCodeApi();
|
||||
|
||||
function command(cmd, callback) {
|
||||
if (!cmd) {
|
||||
return;
|
||||
}
|
||||
let args = Array.from(arguments);
|
||||
if (typeof args[args.length - 1] === 'function') {
|
||||
callback = args[args.length - 1];
|
||||
args.length = args.length - 1;
|
||||
} else {
|
||||
callback = undefined;
|
||||
}
|
||||
args.shift();
|
||||
const messageId = new Date().getTime() + Math.random();
|
||||
|
||||
callbackStack.push({
|
||||
messageId,
|
||||
callback
|
||||
});
|
||||
|
||||
vscode.postMessage({
|
||||
messageId,
|
||||
command: cmd,
|
||||
parameter: args
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('message', event => {
|
||||
const message = event.data;
|
||||
|
||||
for (let index = 0; index < callbackStack.length; index++) {
|
||||
const callbackItem = callbackStack[index];
|
||||
if (callbackItem.messageId === message.messageId) {
|
||||
if (callbackItem.callback) {
|
||||
callbackItem.callback(message.payload);
|
||||
}
|
||||
callbackStack.splice(index, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 12 12" style="enable-background:new 0 0 12 12;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#333333;}
|
||||
</style>
|
||||
<title>96dpiClear</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g>
|
||||
<path class="st0" d="M0.1,3.9l0.7-0.7L6,8.4l5.1-5.1l0.7,0.7L6,9.8L0.1,3.9z"/>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 541 B |
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 18 18" style="enable-background:new 0 0 18 18;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#C8C8C8;}
|
||||
</style>
|
||||
<title>96dpiSuccess info</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g>
|
||||
<path class="st0" d="M16.9,1.1v15.8H1.1V1.1H16.9 M18,0H0v18h18V0L18,0z"/>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 544 B |
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 18 18" style="enable-background:new 0 0 18 18;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#0078D7;}
|
||||
.st1{enable-background:new ;}
|
||||
.st2{fill:#FFFFFF;}
|
||||
</style>
|
||||
<polyline class="st0" points="16.9,1.1 16.9,16.9 1.1,16.9 1.1,1.1 16.9,1.1 "/>
|
||||
<title>96dpiSuccess info</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Success-info">
|
||||
<g class="st1">
|
||||
<path class="st2" d="M13.1,5.2L13.8,6l-6.6,6.6L4,9.4l0.8-0.8L7.2,11L13.1,5.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M17,1v16H1V1H17 M18,0H0v18h18V0L18,0z"/>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 799 B |
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 18 18" style="enable-background:new 0 0 18 18;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{enable-background:new ;}
|
||||
.st1{fill:#C8C8C8;}
|
||||
</style>
|
||||
<title>96dpiSuccess info</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Success-info">
|
||||
<g class="st0">
|
||||
<path class="st1" d="M13.1,5.2L13.8,6l-6.6,6.6L4,9.4l0.8-0.8L7.2,11L13.1,5.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M16.9,1.1v15.8H1.1V1.1H16.9 M18,0H0v18h18V0L18,0z"/>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 711 B |
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 12 12" style="enable-background:new 0 0 12 12;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{enable-background:new ;}
|
||||
.st1{fill:#333333;}
|
||||
</style>
|
||||
<title>96dpiClear</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Clear">
|
||||
<g class="st0">
|
||||
<path class="st1" d="M6.5,6l5.4,5.4l-0.5,0.5L6,6.5l-5.4,5.4l-0.5-0.5L5.5,6L0.1,0.6l0.5-0.5L6,5.5l5.4-5.4l0.5,0.5L6.5,6z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 655 B |
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#333">
|
||||
<path d="M10.711 3L15 7.289V16H5v-3H1V0h5.711l3 3h1zM2 12h3V3h3.29l-2-2H2v11zm4 3h8V8h-4V4H6v11zm5-10.289V7h2.289L11 4.711z"></path>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 268 B |
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{enable-background:new ;}
|
||||
.st1{fill:#333333;}
|
||||
</style>
|
||||
<title>96dpicreate</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="create">
|
||||
<g class="st0">
|
||||
<path class="st1" d="M16,7.5v1H8.5V16h-1V8.5H0v-1h7.5V0h1v7.5H16z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 603 B |
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#333333;}
|
||||
</style>
|
||||
<title>96dpiDelete</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g>
|
||||
<rect x="6" y="5" class="st0" width="1" height="8"/>
|
||||
<path class="st0" d="M11,2V1c0-0.6-0.4-1-1-1H7C6.4,0,6,0.4,6,1v1H2v1h1v11v0.5v0.1C3,15.4,3.6,16,4.4,16h0.1H5h7h0.5h0.1
|
||||
c0.8,0,1.4-0.6,1.4-1.4v-0.1V14V3h1V2H11z M10,1v1H7V1H10z M4.5,15c-0.1,0-0.3,0-0.4-0.1C4,14.8,4,14.6,4,14.5V3h9v11.5
|
||||
c0,0.1,0,0.3-0.1,0.4C12.8,15,12.6,15,12.5,15H4.5z"/>
|
||||
<rect x="8" y="5" class="st0" width="1" height="8"/>
|
||||
<rect x="10" y="5" class="st0" width="1" height="8"/>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 919 B |
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{enable-background:new ;}
|
||||
.st1{fill:#333333;}
|
||||
</style>
|
||||
<title>96dpiDownload</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Download">
|
||||
<g class="st0">
|
||||
<path class="st1" d="M13.4,8.4l-4.9,4.9L3.6,8.4l0.7-0.7L8,11.3V0h1v11.3l3.6-3.7L13.4,8.4z M4,16v-1h9v1H4z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 647 B |
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#A1260D;}
|
||||
</style>
|
||||
<title>96dpiError info</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<path class="st0" d="M11.5,5.2L8.7,8l2.8,2.8l-0.7,0.7L8,8.7l-2.8,2.8l-0.7-0.7L7.3,8L4.5,5.2l0.7-0.7L8,7.3l2.8-2.8L11.5,5.2z M8,1
|
||||
c3.9,0,7,3.1,7,7s-3.1,7-7,7s-7-3.1-7-7S4.1,1,8,1 M8,0C3.6,0,0,3.6,0,8s3.6,8,8,8s8-3.6,8-8S12.4,0,8,0L8,0z"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 697 B |
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{enable-background:new ;}
|
||||
.st1{fill:#333333;}
|
||||
</style>
|
||||
<title>96dpiFilter</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Filter">
|
||||
<g class="st0">
|
||||
<path class="st1" d="M0,1h16v1.7l-6,6V15H6V8.7l-6-6V1z M15,2.3V2H1v0.3l6,6V14h2V8.3L15,2.3z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 629 B |
|
@ -0,0 +1,14 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#00529C;}
|
||||
</style>
|
||||
<path class="st0" d="M7.5,1C11.1,1,14,3.9,14,7.5S11.1,14,7.5,14S1,11.1,1,7.5S3.9,1,7.5,1 M7.5,0C3.4,0,0,3.4,0,7.5S3.4,15,7.5,15
|
||||
S15,11.6,15,7.5S11.6,0,7.5,0L7.5,0z"/>
|
||||
<title>96dpiInfo</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<rect x="7" y="4" class="st0" width="1" height="1"/>
|
||||
<rect x="7" y="6" class="st0" width="1" height="5"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 726 B |
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 4" style="enable-background:new 0 0 16 4;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{opacity:0.3;fill:#333333;}
|
||||
</style>
|
||||
<title>96dpiShow more</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<circle class="st0" cx="2" cy="2" r="1"/>
|
||||
<circle class="st0" cx="8" cy="2" r="1"/>
|
||||
<circle class="st0" cx="14" cy="2" r="1"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 594 B |
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{enable-background:new ;}
|
||||
.st1{fill:#333333;}
|
||||
</style>
|
||||
<title>96dpiPublish</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Publish">
|
||||
<g class="st0">
|
||||
<path class="st1" d="M15,4.3V16H2V0h8.7L15,4.3z M14,15V5h-4V1H3v14H14z M11,4h2.3L11,1.7V4z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M12,9.5c-0.8,0-1.5,0.7-1.5,1.5v1.5c0,0.3-0.2,0.5-0.5,0.5H9.5v1H10c0.8,0,1.5-0.7,1.5-1.5V11
|
||||
c0-0.3,0.2-0.5,0.5-0.5h0.5v-1H12z"/>
|
||||
<path class="st1" d="M10,6c0.8,0,1.5,0.7,1.5,1.5V9c0,0.3,0.2,0.5,0.5,0.5h0.5v1H12c-0.8,0-1.5-0.7-1.5-1.5V7.5
|
||||
C10.5,7.2,10.3,7,10,7H9.5V6H10z"/>
|
||||
<path class="st1" d="M5,9.5c0.8,0,1.5,0.7,1.5,1.5v1.5C6.5,12.8,6.7,13,7,13h0.5v1H7c-0.8,0-1.5-0.7-1.5-1.5V11
|
||||
c0-0.3-0.2-0.5-0.5-0.5H4.5v-1H5z"/>
|
||||
<path class="st1" d="M7,6C6.2,6,5.5,6.7,5.5,7.5V9c0,0.3-0.2,0.5-0.5,0.5H4.5v1H5c0.8,0,1.5-0.7,1.5-1.5V7.5C6.5,7.2,6.7,7,7,7
|
||||
h0.5V6H7z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 1.2 KiB |
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 18 18" style="enable-background:new 0 0 18 18;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#C8C8C8;}
|
||||
</style>
|
||||
<title>96dpiSuccess info</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<path class="st0" d="M9,0C4,0,0,4,0,9s4,9,9,9s9-4,9-9S14,0,9,0z M9,16.9c-4.3,0-7.9-3.5-7.9-7.9S4.7,1.1,9,1.1s7.9,3.5,7.9,7.9
|
||||
S13.3,16.9,9,16.9z"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 608 B |
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 18 18" style="enable-background:new 0 0 18 18;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#0078D7;}
|
||||
</style>
|
||||
<title>96dpiSuccess info</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<path class="st0" d="M9,0C4,0,0,4,0,9s4,9,9,9s9-4,9-9S14,0,9,0z M9,16.9c-4.3,0-7.9-3.5-7.9-7.9S4.7,1.1,9,1.1s7.9,3.5,7.9,7.9
|
||||
S13.3,16.9,9,16.9z"/>
|
||||
<circle class="st0" cx="9" cy="9" r="5.9"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 652 B |
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 18 18" style="enable-background:new 0 0 18 18;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#C8C8C8;}
|
||||
</style>
|
||||
<title>96dpiSuccess info</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<path class="st0" d="M9,0C4,0,0,4,0,9s4,9,9,9s9-4,9-9S14,0,9,0z M9,16.9c-4.3,0-7.9-3.5-7.9-7.9S4.7,1.1,9,1.1s7.9,3.5,7.9,7.9
|
||||
S13.3,16.9,9,16.9z"/>
|
||||
<circle class="st0" cx="9" cy="9" r="5.9"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 652 B |
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#333333;}
|
||||
</style>
|
||||
<title>96dpiRefresh</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<path class="st0" d="M10.1,0.3l-0.3,1C12.8,2,15,4.8,15,8c0,3.9-3.1,7-7,7s-7-3.1-7-7c0-2.8,1.6-5.2,4-6.3l0,0V4h1V0H2v1h2.1l0,0
|
||||
C1.7,2.4,0,5,0,8c0,4.4,3.6,8,8,8s8-3.6,8-8C16,4.3,13.5,1.2,10.1,0.3z"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 654 B |
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#333333;}
|
||||
</style>
|
||||
<title>96dpiSearch</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<path class="st0" d="M10.5,0C7.5,0,5,2.5,5,5.5C5,6.8,5.5,8.1,6.3,9l-6.1,6.1c-0.2,0.2-0.2,0.5,0,0.7C0.2,16,0.4,16,0.5,16
|
||||
s0.3,0,0.4-0.1L7,9.7c1,0.8,2.2,1.3,3.5,1.3c3,0,5.5-2.5,5.5-5.5S13.5,0,10.5,0z M10.5,10C8,10,6,8,6,5.5S8,1,10.5,1S15,3,15,5.5
|
||||
S13,10,10.5,10z"/>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 720 B |
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{enable-background:new ;}
|
||||
.st1{fill:#388A34;}
|
||||
</style>
|
||||
<title>96dpiSuccess info</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Success-info">
|
||||
<g class="st0">
|
||||
<path class="st1" d="M11.6,4.6l0.7,0.7l-5.9,5.9L3.6,8.4l0.7-0.7l2.1,2.1L11.6,4.6z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M8,1c3.9,0,7,3.1,7,7s-3.1,7-7,7s-7-3.1-7-7S4.1,1,8,1 M8,0C3.6,0,0,3.6,0,8s3.6,8,8,8s8-3.6,8-8S12.4,0,8,0
|
||||
L8,0z"/>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 778 B |
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{enable-background:new ;}
|
||||
.st1{fill:#D73B02;}
|
||||
</style>
|
||||
<title>96dpiWarning info</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Warning-info">
|
||||
<g class="st0">
|
||||
<path class="st1" d="M7.5,0L15,15H0L7.5,0z M7.5,2.2L1.6,14h11.8L7.5,2.2z M7,6h1v5H7V6z M7,13v-1h1v1H7z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 652 B |
|
@ -0,0 +1,178 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script src="vue.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main" v-bind:class="{'show-search-bar': showSearchBar, 'show-search-result': searchKeywords !== ''}" v-cloak>
|
||||
|
||||
<header v-bind:class="{'show-status-selector': showStatusSelector, 'show-tag-selector': showTagSelector}">
|
||||
<h1 class="company-name">{{ companyName }}</h1>
|
||||
<div class="digital-twin-file-type-tab">
|
||||
<span v-bind:class="{active: type.value === 'CapabilityModel'}" v-on:click="type.value = 'CapabilityModel'">Device Capability Models</span>
|
||||
<span v-bind:class="{active: type.value === 'Interface'}" v-on:click="type.value = 'Interface'">Interfaces</span>
|
||||
</div>
|
||||
<div class="action-bar">
|
||||
<button class="with-icon new" v-if="!publicRepository" v-on:click="createDigitalTwinFile">New</button>
|
||||
<button class="with-icon download" v-on:click="editDigitalTwinFiles" v-bind:disabled="type.value === 'Interface' && !this.selectedInterfaces.value.length || type.value === 'CapabilityModel' && !this.selectedCapabilityModels.value.length">Download</button>
|
||||
<button class="with-icon publish" v-if="false" v-on:click="publishDigitalTwinFiles" v-bind:disabled="hasNoItemToPublish()">Publish</button>
|
||||
<button class="with-icon delete" v-if="!publicRepository" v-on:click="deleteDigitalTwinFiles" v-bind:disabled="type.value === 'Interface' && !this.selectedInterfaces.value.length || type.value === 'CapabilityModel' && !this.selectedCapabilityModels.value.length">Delete</button>
|
||||
<button class="with-icon refresh right" v-on:click="refreshDigitalTwinFileList"></button>
|
||||
<button class="with-icon filter right" v-on:click="showHideSearchBar"></button>
|
||||
</div>
|
||||
<div class="with-icon search-bar">
|
||||
<input type="text" placeholder="Filter by keyword" v-model="filterKeywords">
|
||||
<button class="with-icon clear right" v-on:click="clearFilter"></button>
|
||||
<button class="dropdown tags right" v-on:click="showHideTagSelector">{{ filterTags.length ? (filterTags.length > 1 ? (filterTags.length + ' Tags') : '1 Tag') : 'Tags' }}</button>
|
||||
<button class="dropdown status right" v-on:click="showHideStatusSelector">{{ filterStatus === 'All' ? 'Status' : filterStatus }}</button>
|
||||
</div>
|
||||
<div class="tag-selector">
|
||||
<div class="tag-list">
|
||||
<div class="tag-list-item" v-for="tag in searchTags(allTags.value)">
|
||||
<label><input type="checkbox" v-bind:value="tag" v-model="filterTags">{{ tag }}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tag-selector-search-bar">
|
||||
<div class="with-icon tag-search-bar">
|
||||
<input type="text" placeholder="Search" v-model="filterTagsKeywords">
|
||||
</div>
|
||||
<div class="tag-search-action">
|
||||
<div class="tag-and-or">
|
||||
<input type="radio" name="tag-and-or" value="or" id="tag-or" v-model="filterTagsOrAnd"><label for="tag-or">or</label>
|
||||
<input type="radio" name="tag-and-or" value="and" id="tag-and" v-model="filterTagsOrAnd"><label for="tag-and">and</label>
|
||||
</div>
|
||||
<div class="tag-clear" v-on:click="filterTags = []" v-bind:class="{disabled: !filterTags.length}">Clear</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-selector">
|
||||
<div class="status-item" v-on:click="selectFilterStatus('All')">All</div>
|
||||
<div class="status-item" v-on:click="selectFilterStatus('Saved')">Saved</div>
|
||||
<div class="status-item" v-on:click="selectFilterStatus('Published')">Published</div>
|
||||
</div>
|
||||
</header>
|
||||
<div class="content">
|
||||
<div class="search_result" v-if="searchKeywords !== ''">Search results with keywords: <strong>{{searchKeywords}}</strong>. Click <span class="link" v-on:click="clearKeywords">here</span> to clear keywords.</div>
|
||||
<table v-show="type.value === 'Interface'">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>ID</th>
|
||||
<th>Version</th>
|
||||
<th>Publisher</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-on:wheel="onScrollTable" v-on:touchmove="onScrollTable" id="interfaceListTable">
|
||||
<tr v-show="filterKeywords && filterKeywords !== searchKeywords" v-on:click="searchOnServer">
|
||||
<td colspan="5" style="width: 100%"><span class="info_icon"></span>Results below are filtered from local cache. Click here to search on server.</td>
|
||||
</tr>
|
||||
<tr v-for="interface in filterItems(interfaceList.value)" v-on:click="addRemoveInterface(interface.urnId)">
|
||||
<td><input type="checkbox" v-bind:value="interface.urnId" v-model="selectedInterfaces.value"></td>
|
||||
<td v-bind:title="interface.displayName"><div class="td_inner" v-html="highlight(interface.displayName)"></div></td>
|
||||
<td v-bind:title="interface.urnId"><div class="td_inner" v-html="highlight(interface.urnId)"></div><span class="copy_icon" title="Copy" v-on:click.stop="copy($event, interface.urnId)"></span></td>
|
||||
<td v-bind:title="interface.version"><div class="td_inner">{{ interface.version }}</div></td>
|
||||
<td>{{ interface.publisherName }}</td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinInterfaces.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinInterfaces.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinInterfaces.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinInterfaces.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinInterfaces.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinInterfaces.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinInterfaces.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinInterfaces.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinInterfaces.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinInterfaces.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr v-show="!loadingDigitalTwinInterfaces.value && interfaceNextToken.value">
|
||||
<td colspan="5"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table v-show="type.value === 'CapabilityModel'">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>ID</th>
|
||||
<th>Version</th>
|
||||
<th>Publisher</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-on:wheel="onScrollTable" v-on:touchmove="onScrollTable" id="capabilityListTable">
|
||||
<tr v-show="filterKeywords && filterKeywords !== searchKeywords" v-on:click="searchOnServer">
|
||||
<td colspan="5" style="width: 100%"><span class="info_icon"></span>Results below are filtered from local cache. Click here to search on server.</td>
|
||||
</tr>
|
||||
<tr v-for="capability in filterItems(capabilityList.value)" v-on:click="addRemoveCapability(capability.urnId)">
|
||||
<td><input type="checkbox" v-bind:value="capability.urnId" v-model="selectedCapabilityModels.value"></td>
|
||||
<td v-bind:title="capability.displayName"><div class="td_inner" v-html="highlight(capability.displayName)"></div></td>
|
||||
<td v-bind:title="capability.urnId"><div class="td_inner" v-html="highlight(capability.urnId)"></div><span class="copy_icon" title="Copy" v-on:click.stop="copy($event, capability.urnId)"></span></td>
|
||||
<td v-bind:title="capability.version"><div class="td_inner">{{ capability.version }}</div></td>
|
||||
<td>{{ capability.publisherName }}</td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinCapabilityModels.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinCapabilityModels.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinCapabilityModels.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinCapabilityModels.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinCapabilityModels.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinCapabilityModels.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinCapabilityModels.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinCapabilityModels.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinCapabilityModels.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr class="loading-digital-twin-files" v-show="loadingDigitalTwinCapabilityModels.value">
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
<tr v-show="!loadingDigitalTwinCapabilityModels.value && capabilityNextToken.value">
|
||||
<td colspan="5"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script src="command.js"></script>
|
||||
<script src="main.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,403 @@
|
|||
var repository = new Vue({
|
||||
el: "#main",
|
||||
data: {
|
||||
companyName:
|
||||
_location.search === "?public"
|
||||
? "Public repository"
|
||||
: "Company repository",
|
||||
selectedInterfaces: {
|
||||
value: []
|
||||
},
|
||||
interfaceList: {
|
||||
value: []
|
||||
},
|
||||
selectedCapabilityModels: {
|
||||
value: []
|
||||
},
|
||||
capabilityList: {
|
||||
value: []
|
||||
},
|
||||
type: {
|
||||
value: "CapabilityModel"
|
||||
},
|
||||
interfaceNextToken: {
|
||||
value: ""
|
||||
},
|
||||
capabilityNextToken: {
|
||||
value: ""
|
||||
},
|
||||
showSearchBar: false,
|
||||
showStatusSelector: false,
|
||||
showTagSelector: false,
|
||||
filterStatus: "All",
|
||||
filterTags: [],
|
||||
filterKeywords: "",
|
||||
searchKeywords: "",
|
||||
filterTagsOrAnd: "and",
|
||||
loadingDigitalTwinInterfaces: {
|
||||
value: true
|
||||
},
|
||||
loadingDigitalTwinCapabilityModels: {
|
||||
value: true
|
||||
},
|
||||
allTags: {
|
||||
value: [
|
||||
"tag1",
|
||||
"tag2",
|
||||
"tag3",
|
||||
"tag11",
|
||||
"tag12",
|
||||
"tag13",
|
||||
"tag21",
|
||||
"tag22",
|
||||
"tag23",
|
||||
"tag31",
|
||||
"tag32",
|
||||
"tag33"
|
||||
]
|
||||
},
|
||||
filterTagsKeywords: "",
|
||||
nextPageLoadingCounter: null,
|
||||
publicRepository: _location.search === "?public"
|
||||
},
|
||||
methods: {
|
||||
command,
|
||||
highlight: function(value) {
|
||||
value = encodeHTML(value);
|
||||
const filterKeywords = encodeHTML(this.filterKeywords.trim());
|
||||
if (!filterKeywords) {
|
||||
return value;
|
||||
}
|
||||
const filterReg = new RegExp(`(${filterKeywords})`, "ig");
|
||||
return value.replace(filterReg, "<em>$1</em>");
|
||||
},
|
||||
createDigitalTwinFile,
|
||||
deleteDigitalTwinFiles,
|
||||
editDigitalTwinFiles,
|
||||
getNextPageDigitalTwinFiles,
|
||||
refreshDigitalTwinFileList,
|
||||
showHideSearchBar,
|
||||
showHideStatusSelector,
|
||||
showHideTagSelector,
|
||||
clearFilter,
|
||||
selectFilterStatus,
|
||||
addRemoveInterface,
|
||||
addRemoveCapability,
|
||||
filterItems,
|
||||
onScrollTable,
|
||||
searchTags,
|
||||
copy,
|
||||
hasNoItemToPublish,
|
||||
searchOnServer,
|
||||
clearKeywords
|
||||
},
|
||||
created: function() {
|
||||
getNextPageDigitalTwinFiles.call(this, "Interface");
|
||||
getNextPageDigitalTwinFiles.call(this, "CapabilityModel");
|
||||
}
|
||||
});
|
||||
|
||||
function encodeHTML(value) {
|
||||
let div = document.createElement("div");
|
||||
div.innerText = value;
|
||||
const html = div.innerHTML;
|
||||
div = undefined;
|
||||
return html;
|
||||
}
|
||||
|
||||
function deleteDigitalTwinFiles() {
|
||||
const fileIds =
|
||||
this.type.value === "Interface"
|
||||
? this.selectedInterfaces.value
|
||||
: this.selectedCapabilityModels.value;
|
||||
command(
|
||||
"iotworkbench.deleteMetamodelFiles",
|
||||
fileIds,
|
||||
this.type.value,
|
||||
refreshDigitalTwinFileList.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
function editDigitalTwinFiles() {
|
||||
const fileIds =
|
||||
this.type.value === "Interface"
|
||||
? this.selectedInterfaces.value
|
||||
: this.selectedCapabilityModels.value;
|
||||
command(
|
||||
"iotworkbench.editMetamodelFiles",
|
||||
fileIds,
|
||||
this.type.value,
|
||||
this.publicRepository,
|
||||
refreshDigitalTwinFileList.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
function createDigitalTwinFile() {
|
||||
const commandName =
|
||||
this.type.value === "Interface"
|
||||
? "iotworkbench.iotPnPCreateInterface"
|
||||
: "iotworkbench.iotPnPCreateCapabilityModel";
|
||||
command(commandName);
|
||||
}
|
||||
|
||||
function getNextPageDigitalTwinFiles(fileType) {
|
||||
fileType = typeof fileType === "string" ? fileType : this.type.value;
|
||||
let commandName, fileList, nextToken, loadingDigitalTwinFiles;
|
||||
|
||||
if (fileType === "Interface") {
|
||||
commandName = "iotworkbench.getInterfaces";
|
||||
fileList = this.interfaceList;
|
||||
nextToken = this.interfaceNextToken;
|
||||
loadingDigitalTwinFiles = this.loadingDigitalTwinInterfaces;
|
||||
tableId = "interfaceListTable";
|
||||
} else {
|
||||
commandName = "iotworkbench.getCapabilityModels";
|
||||
fileList = this.capabilityList;
|
||||
nextToken = this.capabilityNextToken;
|
||||
loadingDigitalTwinFiles = this.loadingDigitalTwinCapabilityModels;
|
||||
tableId = "capabilityListTable";
|
||||
}
|
||||
|
||||
loadingDigitalTwinFiles.value = true;
|
||||
|
||||
command(
|
||||
commandName,
|
||||
this.searchKeywords,
|
||||
this.publicRepository,
|
||||
50,
|
||||
nextToken.value,
|
||||
res => {
|
||||
Vue.set(fileList, "value", fileList.value.concat(res.result.results));
|
||||
Vue.set(nextToken, "value", res.result.continuationToken);
|
||||
Vue.set(loadingDigitalTwinFiles, "value", false);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function refreshDigitalTwinFileList() {
|
||||
let nextToken, selectedList;
|
||||
if (this.type.value === "Interface") {
|
||||
fileList = this.interfaceList;
|
||||
nextToken = this.interfaceNextToken;
|
||||
selectedList = this.selectedInterfaces;
|
||||
loadingDigitalTwinFiles = this.loadingDigitalTwinInterfaces;
|
||||
} else {
|
||||
fileList = this.capabilityList;
|
||||
nextToken = this.capabilityNextToken;
|
||||
selectedList = this.selectedCapabilityModels;
|
||||
loadingDigitalTwinFiles = this.loadingDigitalTwinCapabilityModels;
|
||||
}
|
||||
|
||||
Vue.set(fileList, "value", []);
|
||||
Vue.set(nextToken, "value", "");
|
||||
Vue.set(selectedList, "value", []);
|
||||
Vue.set(loadingDigitalTwinFiles, "value", true);
|
||||
setTimeout(getNextPageDigitalTwinFiles.bind(this), 1000); // wait for server refresh
|
||||
}
|
||||
|
||||
function showHideSearchBar() {
|
||||
this.showSearchBar = !this.showSearchBar;
|
||||
this.showStatusSelector = false;
|
||||
this.showTagSelector = false;
|
||||
}
|
||||
|
||||
function showHideStatusSelector() {
|
||||
this.showStatusSelector = !this.showStatusSelector;
|
||||
this.showTagSelector = false;
|
||||
}
|
||||
|
||||
function showHideTagSelector() {
|
||||
this.showTagSelector = !this.showTagSelector;
|
||||
this.showStatusSelector = false;
|
||||
}
|
||||
|
||||
function clearFilter() {
|
||||
this.filterTags = [];
|
||||
this.filterStatus = "All";
|
||||
this.showStatusSelector = false;
|
||||
this.filterKeywords = "";
|
||||
this.showTagSelector = false;
|
||||
}
|
||||
|
||||
function selectFilterStatus(status) {
|
||||
this.filterStatus = status;
|
||||
this.showStatusSelector = false;
|
||||
}
|
||||
|
||||
function addRemoveInterface(id) {
|
||||
const index = this.selectedInterfaces.value.indexOf(id);
|
||||
if (index !== -1) {
|
||||
this.selectedInterfaces.value.splice(index, 1);
|
||||
} else {
|
||||
this.selectedInterfaces.value.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
function addRemoveCapability(id) {
|
||||
const index = this.selectedCapabilityModels.value.indexOf(id);
|
||||
if (index !== -1) {
|
||||
this.selectedCapabilityModels.value.splice(index, 1);
|
||||
} else {
|
||||
this.selectedCapabilityModels.value.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
function searchTags(tags) {
|
||||
const filterTagsKeywords = this.filterTagsKeywords.trim();
|
||||
if (!filterTagsKeywords) {
|
||||
return tags;
|
||||
}
|
||||
|
||||
return tags.filter(tag => {
|
||||
return tag.toLowerCase().indexOf(filterTagsKeywords.toLowerCase()) !== -1;
|
||||
});
|
||||
}
|
||||
|
||||
function filterItems(list) {
|
||||
if (!this.showSearchBar) {
|
||||
return list;
|
||||
}
|
||||
|
||||
const filterKeywords = this.filterKeywords.trim();
|
||||
const filterStatus = this.filterStatus;
|
||||
const filterTags = this.filterTags;
|
||||
const filterTagsOrAnd = this.filterTagsOrAnd;
|
||||
|
||||
return list.filter(item => {
|
||||
if (filterStatus !== "All") {
|
||||
if (item.published && filterStatus !== "Published") {
|
||||
return false;
|
||||
}
|
||||
if (!item.published && filterStatus !== "Saved") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isMatchTags(item.tags, filterTags, filterTagsOrAnd)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!item.displayName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const displayName = item.displayName;
|
||||
const urnId = item.urnId;
|
||||
if (
|
||||
filterKeywords &&
|
||||
displayName.toLowerCase().indexOf(filterKeywords.toLowerCase()) === -1 &&
|
||||
urnId.toLowerCase().indexOf(filterKeywords.toLowerCase()) === -1
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function isMatchTags(tags, selectedTags, orAnd) {
|
||||
if ((!tags || !tags.length) && selectedTags.length) {
|
||||
return false;
|
||||
}
|
||||
if (!selectedTags.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (tag of selectedTags) {
|
||||
const index = tags.indexOf(tag);
|
||||
if (index === -1 && orAnd === "and") {
|
||||
return false;
|
||||
}
|
||||
if (index !== -1 && orAnd === "or") {
|
||||
return true;
|
||||
}
|
||||
if (index !== -1 && orAnd === "and") {
|
||||
tags.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (orAnd === "or") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (orAnd === "and" && !tags.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function onScrollTable(event) {
|
||||
if (this.filterKeywords) {
|
||||
return;
|
||||
}
|
||||
const nextToken =
|
||||
this.type.value === "Interface"
|
||||
? this.interfaceNextToken.value
|
||||
: this.capabilityNextToken.value;
|
||||
const loadingDigitalTwinFiles =
|
||||
this.type.value === "Interface"
|
||||
? this.loadingDigitalTwinInterfaces
|
||||
: this.loadingDigitalTwinCapabilityModels;
|
||||
if (
|
||||
!nextToken ||
|
||||
this.nextPageLoadingCounter ||
|
||||
loadingDigitalTwinFiles.value
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.nextPageLoadingCounter = setTimeout(() => {
|
||||
const totalHeight = event.target.scrollHeight;
|
||||
const heightOffset = event.target.scrollTop;
|
||||
const viewHeight = event.target.offsetHeight;
|
||||
if (viewHeight + heightOffset >= totalHeight) {
|
||||
this.getNextPageDigitalTwinFiles(this.type);
|
||||
}
|
||||
this.nextPageLoadingCounter = null;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function copy(event, content) {
|
||||
const copyTextBox = document.createElement("input");
|
||||
copyTextBox.className = "copy-text-box";
|
||||
copyTextBox.value = content;
|
||||
document.body.appendChild(copyTextBox);
|
||||
copyTextBox.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(copyTextBox);
|
||||
event.target.className = "copy_icon copied";
|
||||
setTimeout(() => {
|
||||
event.target.className = "copy_icon";
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function hasNoItemToPublish() {
|
||||
let selectedItemList, fullItemList;
|
||||
if (this.type.value === "Interface") {
|
||||
fullItemList = this.interfaceList.value;
|
||||
selectedItemList = this.selectedInterfaces.value;
|
||||
} else {
|
||||
fullItemList = this.capabilityList.value;
|
||||
selectedItemList = this.selectedCapabilityModels.value;
|
||||
}
|
||||
|
||||
for (let i = 0; i < selectedItemList.length; i++) {
|
||||
const item = fullItemList.find(item => item.id === selectedItemList[i]);
|
||||
if (item && !item.published) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function searchOnServer() {
|
||||
this.searchKeywords = this.filterKeywords;
|
||||
this.filterKeywords = "";
|
||||
this.refreshDigitalTwinFileList();
|
||||
}
|
||||
|
||||
function clearKeywords() {
|
||||
this.searchKeywords = "";
|
||||
this.filterKeywords = "";
|
||||
this.refreshDigitalTwinFileList();
|
||||
}
|
|
@ -0,0 +1,895 @@
|
|||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
min-width: 600px;
|
||||
margin: 0;
|
||||
padding: 0 20px;
|
||||
color: #333;
|
||||
font-family: "Segoe UI", SegoeUI, "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
background: white;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
em {
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
background: rgba(0, 120, 215, 0.2);
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
width: 20px;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
margin-right: 8px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input[type=checkbox]::after {
|
||||
content: "";
|
||||
background: url(image/Checkbox.svg) no-repeat center;
|
||||
background-size: 18px 18px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -14px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
input[type=checkbox]:hover::after {
|
||||
background-image: url(image/CheckboxHover.svg);
|
||||
}
|
||||
|
||||
input[type=checkbox]:checked::after {
|
||||
background-image: url(image/CheckboxChecked.svg);
|
||||
}
|
||||
|
||||
input[type=radio] {
|
||||
width: 20px;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
margin-right: 8px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
input[type=radio]::after {
|
||||
content: "";
|
||||
background: url(image/Radio.svg) no-repeat center;
|
||||
background-size: 18px 18px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: -14px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
input[type=radio]:hover::after {
|
||||
background-image: url(image/RadioHover.svg);
|
||||
}
|
||||
|
||||
input[type=radio]:checked::after {
|
||||
background-image: url(image/RadioChecked.svg);
|
||||
}
|
||||
|
||||
.right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
button {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button:not(:disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:not(:disabled):hover {
|
||||
background: rgba(0, 120, 215, 0.1);
|
||||
}
|
||||
|
||||
button:not(:disabled):active {
|
||||
background: rgba(0, 120, 215, 0.2)!important;
|
||||
}
|
||||
button:disabled {
|
||||
color: rgba(102, 102, 102, 0.3);
|
||||
cursor: default!important;
|
||||
}
|
||||
|
||||
button:disabled::before {
|
||||
background-color: rgba(102, 102, 102, 0.3);
|
||||
}
|
||||
|
||||
.with-icon::before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
box-sizing: border-box;
|
||||
background-color: #333;
|
||||
-webkit-mask-position: center;
|
||||
mask-position: center;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-size: contain;
|
||||
mask-size: contain;
|
||||
}
|
||||
|
||||
#main {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
header {
|
||||
padding-top: 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
header h1.company-name {
|
||||
font-size: 20px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-bottom: 20px;
|
||||
display: block;
|
||||
line-height: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
header .digital-twin-file-type-tab {
|
||||
font-size: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
header .digital-twin-file-type-tab span {
|
||||
margin-right: 20px;
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
font-weight: bold;
|
||||
border-bottom: solid 2px transparent;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
header .digital-twin-file-type-tab span.active {
|
||||
border-color: #0078d7;
|
||||
}
|
||||
|
||||
header .action-bar {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
header .action-bar::after {
|
||||
content: "";
|
||||
clear: both;
|
||||
}
|
||||
|
||||
header .action-bar button {
|
||||
padding: 8px;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
background: transparent;
|
||||
height: 32px;
|
||||
line-height: 16px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
header .action-bar button.right::before {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
header .action-bar button.new::before {
|
||||
-webkit-mask-image: url(image/Create.svg);
|
||||
mask-image: url(image/Create.svg);
|
||||
}
|
||||
|
||||
header .action-bar button.download::before {
|
||||
-webkit-mask-image: url(image/Download.svg);
|
||||
mask-image: url(image/Download.svg);
|
||||
}
|
||||
|
||||
header .action-bar button.publish::before {
|
||||
-webkit-mask-image: url(image/Publish.svg);
|
||||
mask-image: url(image/Publish.svg);
|
||||
}
|
||||
|
||||
header .action-bar button.delete::before {
|
||||
-webkit-mask-image: url(image/Delete.svg);
|
||||
mask-image: url(image/Delete.svg);
|
||||
}
|
||||
|
||||
header .action-bar button.filter::before {
|
||||
-webkit-mask-image: url(image/Filter.svg);
|
||||
mask-image: url(image/Filter.svg);
|
||||
}
|
||||
|
||||
header .action-bar button.refresh::before {
|
||||
-webkit-mask-image: url(image/Refresh.svg);
|
||||
mask-image: url(image/Refresh.svg);
|
||||
}
|
||||
|
||||
#main.show-search-bar header .action-bar button.filter {
|
||||
background: rgba(0, 120, 215, 0.2);
|
||||
}
|
||||
|
||||
header .search-bar {
|
||||
border: 1px solid rgba(102, 102, 102, 0.3);
|
||||
box-sizing: border-box;
|
||||
height: 32px;
|
||||
margin: 0 8px 5px 8px;
|
||||
font-size: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#main.show-search-bar header .search-bar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
header .search-bar:focus-within {
|
||||
border-color: rgba(0, 120, 215, 0.6);
|
||||
box-shadow: 0 0 1px rgba(0, 120, 215, 0.6);
|
||||
}
|
||||
|
||||
header .search-bar::before {
|
||||
margin: 7px;
|
||||
-webkit-mask-image: url(image/Search.svg);
|
||||
mask-image: url(image/Search.svg);
|
||||
}
|
||||
|
||||
header .search-bar input[type=text] {
|
||||
border: none;
|
||||
height: 30px;
|
||||
width: calc(100% - 65px);
|
||||
line-height: 30px;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
background: transparent;
|
||||
vertical-align: top;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
header .search-bar input[type=text]::placeholder {
|
||||
color: #333;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
header .search-bar::after {
|
||||
content: "";
|
||||
clear: both;
|
||||
}
|
||||
|
||||
header .search-bar button {
|
||||
height: 30px;
|
||||
background: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
header .search-bar button.clear {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
header .search-bar button.clear::before {
|
||||
margin: 7px;
|
||||
-webkit-mask-image: url(image/Clear.svg);
|
||||
mask-image: url(image/Clear.svg);
|
||||
}
|
||||
|
||||
header .search-bar button.dropdown {
|
||||
width: 80px;
|
||||
padding: 0 5px;
|
||||
line-height: 30px;
|
||||
position: relative;
|
||||
margin-left: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
header .search-bar button.dropdown::after {
|
||||
content: "";
|
||||
float: right;
|
||||
vertical-align: top;
|
||||
margin: 9px 0 9px 8px;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: #333;
|
||||
-webkit-mask-position: center;
|
||||
mask-position: center;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-size: contain;
|
||||
mask-size: contain;
|
||||
-webkit-mask-image: url(image/Arrow.svg);
|
||||
mask-image: url(image/Arrow.svg);
|
||||
}
|
||||
|
||||
header .tag-selector {
|
||||
position: absolute;
|
||||
top: 184px;
|
||||
right: 58px;
|
||||
border-top: 65px solid white;
|
||||
box-sizing: border-box;
|
||||
max-height: calc(100% - 190px);
|
||||
width: 200px;
|
||||
background: white;
|
||||
box-shadow: 0px 0px 5px 0px rgba(102, 102, 102, 0.3);
|
||||
display: none;
|
||||
overflow-y: auto;
|
||||
font-size: 14px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
header .tag-selector::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
header .tag-selector::-webkit-scrollbar-thumb {
|
||||
background-color: #ccc;
|
||||
border: 2px solid #eee;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
header .tag-selector .tag-list {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
header .tag-selector .tag-list-item:hover {
|
||||
background: rgba(0, 120, 215, 0.1);
|
||||
}
|
||||
|
||||
header .tag-selector .tag-list-item:active {
|
||||
background: rgba(0, 120, 215, 0.2);
|
||||
}
|
||||
|
||||
header .tag-selector .tag-list-item label {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 10px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
}
|
||||
|
||||
header .tag-selector-search-bar {
|
||||
position: absolute;
|
||||
top: 184px;
|
||||
right: 58px;
|
||||
width: 200px;
|
||||
z-index: 200;
|
||||
display: none;
|
||||
}
|
||||
|
||||
header .tag-selector-search-bar .tag-search-bar {
|
||||
height: 32px;
|
||||
font-size: 0;
|
||||
border-bottom: 1px solid rgba(102, 102, 102, 0.1);
|
||||
}
|
||||
|
||||
header .tag-selector-search-bar .tag-search-bar::before {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
-webkit-mask-image: url(image/Search.svg);
|
||||
mask-image: url(image/Search.svg);
|
||||
}
|
||||
|
||||
header .tag-selector-search-bar .tag-search-bar input[type=text] {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 0 5px;
|
||||
height: 32px;
|
||||
width: calc(100% - 32px);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
header .tag-selector-search-bar .tag-search-bar input[type=text]::placeholder {
|
||||
color: #333;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
header .tag-selector-search-bar .tag-and-or {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
margin: 6px 0;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
header .tag-selector-search-bar .tag-and-or label {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
header .tag-selector-search-bar .tag-clear {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-size: 14px;
|
||||
border-left: 1px solid rgba(102, 102, 102, 0.1);
|
||||
padding: 0 8px;
|
||||
margin: 6px 0;
|
||||
float: right;
|
||||
color: #0078d7;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
header .tag-selector-search-bar .tag-clear:not(.disabled):hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
header .tag-selector-search-bar .tag-clear.disabled {
|
||||
color: rgba(0, 120, 215, 0.3);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
header .tag-selector-search-bar .tag-and-or input[type=radio] {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
header .tag-selector-search-bar .tag-and-or input[type=radio]:not(:first-child) {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
header .tag-selector-search-bar .tag-list-item input[type=checkbox] {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
header .status-selector {
|
||||
position: absolute;
|
||||
top: 184px;
|
||||
right: 128px;
|
||||
width: 110px;
|
||||
background: white;
|
||||
box-shadow: 0px 0px 5px 0px rgba(102, 102, 102, 0.3);
|
||||
z-index: 100;
|
||||
display: none;
|
||||
}
|
||||
|
||||
header .status-selector .status-item {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
padding: 0 15px;
|
||||
font-size: 14px;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
header .status-selector .status-item:hover {
|
||||
background: rgba(0, 120, 215, 0.1);
|
||||
}
|
||||
|
||||
header .status-selector .status-item:active {
|
||||
background: rgba(0, 120, 215, 0.2);
|
||||
}
|
||||
|
||||
header .search-bar button.dropdown.status {
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
#main.show-search-bar header.show-tag-selector .tag-selector,
|
||||
#main.show-search-bar header.show-tag-selector .tag-selector-search-bar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#main.show-search-bar header.show-tag-selector .search-bar .tags {
|
||||
background: rgba(0, 120, 215, 0.1);
|
||||
}
|
||||
|
||||
#main.show-search-bar header.show-status-selector .status-selector {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#main.show-search-bar header.show-status-selector .search-bar .status {
|
||||
background: rgba(0, 120, 215, 0.1);
|
||||
}
|
||||
|
||||
button.tags,
|
||||
button.status {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 0 18px 0 8px;
|
||||
height: calc(100% - 152px);
|
||||
}
|
||||
|
||||
#main.show-search-bar:not(.show-search-result) .content {
|
||||
height: calc(100% - 189px);
|
||||
}
|
||||
|
||||
#main.show-search-result:not(.show-search-bar) .content {
|
||||
height: calc(100% - 184px);
|
||||
}
|
||||
|
||||
#main.show-search-bar.show-search-result .content {
|
||||
height: calc(100% - 221px);
|
||||
}
|
||||
|
||||
.content table {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-collapse: collapse;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.content table thead,
|
||||
.content table tbody {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.content table tr {
|
||||
display: block;
|
||||
height: 32px;
|
||||
border-bottom: 1px solid rgba(102, 102, 102, 0.1);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.content table tr:not(:last-child):not(.loading-digital-twin-files) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.content table th {
|
||||
color: #666;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.content table th,
|
||||
.content table td {
|
||||
height: 31px;
|
||||
line-height: 31px;
|
||||
padding: 0 10px;
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.content table th,
|
||||
.content table td .td_inner {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content table td .td_inner {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content table th:nth-child(1),
|
||||
.content table td:nth-child(1) {
|
||||
width: 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.content table th:nth-child(2),
|
||||
.content table td:nth-child(2) {
|
||||
/* width: calc(23% - 52px);
|
||||
max-width: 74px; */
|
||||
width: calc(20% - 20px);
|
||||
}
|
||||
|
||||
.content table th:nth-child(3),
|
||||
.content table td:nth-child(3) {
|
||||
/* width: calc(46% - 104px);
|
||||
max-width: 150px; */
|
||||
width: calc(50% - 50px);
|
||||
}
|
||||
|
||||
.content table th:nth-child(4),
|
||||
.content table td:nth-child(4) {
|
||||
/* width: 120px; */
|
||||
width: calc(15% - 15px);
|
||||
/* text-align: right; */
|
||||
}
|
||||
|
||||
.content table th:nth-child(5),
|
||||
.content table td:nth-child(5) {
|
||||
/* min-width: calc(30% - 70px);
|
||||
width: calc(100% - 464px); */
|
||||
width: calc(15% + 65px);
|
||||
}
|
||||
|
||||
.content table tbody {
|
||||
max-height: calc(100% - 32px);
|
||||
margin-right: -10px;
|
||||
margin-top: -1px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.content table tbody tr:last-child {
|
||||
height: 12px;
|
||||
background: url(image/More.svg) no-repeat center;
|
||||
background-size: 16px 16px;
|
||||
}
|
||||
|
||||
.content table tbody tr:last-child td {
|
||||
height: 11px;
|
||||
}
|
||||
|
||||
.content table tbody::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.content table tbody::-webkit-scrollbar-thumb {
|
||||
background-color: #ccc;
|
||||
border: 2px solid #eee;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.content table tbody tr:not(:last-child):not(.loading-digital-twin-files):hover {
|
||||
background: rgba(0, 120, 215, 0.1);
|
||||
}
|
||||
|
||||
.content table tbody tr:not(:last-child):not(.loading-digital-twin-files):active {
|
||||
background: rgba(0, 120, 215, 0.2);
|
||||
}
|
||||
|
||||
.content table tr.loading-digital-twin-files {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.content table tr.loading-digital-twin-files td::before {
|
||||
content: "";
|
||||
display: block;
|
||||
height: 8px;
|
||||
background: rgba(102, 102, 102, 0.2);
|
||||
bottom: 11px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.content table tr.loading-digital-twin-files td:nth-child(1)::before {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
border: 1px solid #c8c8c8;
|
||||
background: none;
|
||||
bottom: 6px;
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
.content table tr.loading-digital-twin-files td:nth-child(2)::before {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.content table tr.loading-digital-twin-files td:nth-child(3)::before {
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.content table tr.loading-digital-twin-files td:nth-child(4)::before {
|
||||
width: 70px;
|
||||
/* right: 10px; */
|
||||
}
|
||||
|
||||
.content table tr.loading-digital-twin-files td:nth-child(5)::before {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.content table tr.loading-digital-twin-files td:nth-child(6)::before {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.copy_icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
display: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.copy_icon::after {
|
||||
content: "";
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: block;
|
||||
-webkit-mask-image: url(image/Copy.svg);
|
||||
mask-image: url(image/Copy.svg);
|
||||
-webkit-mask-size: 16px 16px;
|
||||
mask-size: 16px 16px;
|
||||
background-color: #333;
|
||||
left: 8px;
|
||||
top: 8px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
:hover > .copy_icon {
|
||||
display: block;
|
||||
}
|
||||
.copy_icon:hover {
|
||||
background-color: rgba(0, 120, 215, 0.2);
|
||||
}
|
||||
|
||||
.copy_icon.copied::before {
|
||||
content: "Copied";
|
||||
display: block;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: white;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
width: 50px;
|
||||
position: absolute;
|
||||
right: -55px;
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
/* .copy_icon.copied::after {
|
||||
content: "";
|
||||
display: block;
|
||||
border-right: 5px solid rgba(0, 0, 0, 0.5);
|
||||
border-top: 5px solid transparent;
|
||||
border-bottom: 5px solid transparent;
|
||||
position: absolute;
|
||||
right: -5px;
|
||||
top: 11px;
|
||||
} */
|
||||
|
||||
.copy-text-box {
|
||||
position: absolute;
|
||||
top: -1000px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.info_icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
-webkit-mask-image: url(image/Info.svg);
|
||||
mask-image: url(image/Info.svg);
|
||||
background-color: #333;
|
||||
vertical-align: text-bottom;
|
||||
margin: 0 0.6em;
|
||||
}
|
||||
|
||||
.search_result {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
}
|
||||
|
||||
.search_result .link {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
body.vscode-dark {
|
||||
color: #ccc;
|
||||
background: #1e1e1e;
|
||||
}
|
||||
|
||||
body.vscode-dark em {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
body.vscode-dark .with-icon::before {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
body.vscode-dark button:not(:disabled):hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
body.vscode-dark button:not(:disabled):active {
|
||||
background: rgba(255, 255, 255, 0.2)!important;
|
||||
}
|
||||
body.vscode-dark button:disabled {
|
||||
color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
body.vscode-dark button:disabled::before {
|
||||
background-color: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
body.vscode-dark button {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
body.vscode-dark .content table th {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
body.vscode-dark #main.show-search-bar header .action-bar button.filter {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
body.vscode-dark #main.show-search-bar header.show-status-selector .search-bar .status {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
body.vscode-dark #main.show-search-bar header.show-tag-selector .search-bar .tags {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
body.vscode-dark header .search-bar input[type=text] {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
body.vscode-dark header .search-bar button.dropdown::after {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
body.vscode-dark header .status-selector {
|
||||
background: #333;
|
||||
}
|
||||
|
||||
body.vscode-dark header .tag-selector {
|
||||
border-top-color: #333;
|
||||
background: #333;
|
||||
}
|
||||
|
||||
body.vscode-dark header .tag-selector .tag-list-item:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
body.vscode-dark header .tag-selector .tag-list-item:active {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
body.vscode-dark header .status-selector .status-item:hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
body.vscode-dark header .status-selector .status-item:active {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
body.vscode-dark header .tag-selector-search-bar .tag-search-bar input[type=text] {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
body.vscode-dark .content table tr.loading-digital-twin-files td:not(:first-child)::before {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
body.vscode-dark .content table tr {
|
||||
border-bottom-color: rgba(255, 255, 255, 0.1)
|
||||
}
|
||||
|
||||
body.vscode-dark .content table tbody tr:not(:last-child):not(.loading-digital-twin-files):hover {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
body.vscode-dark .content table tbody tr:not(:last-child):not(.loading-digital-twin-files):active {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
body.vscode-dark .content table tbody::-webkit-scrollbar {
|
||||
background: #333;
|
||||
}
|
||||
|
||||
body.vscode-dark .content table tbody::-webkit-scrollbar-thumb {
|
||||
background-color: #1e1e1e;
|
||||
border-color: #333;
|
||||
}
|
||||
|
||||
body.vscode-dark .copy_icon::after {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
body.vscode-dark .copy_icon:hover {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
body.vscode-dark .info_icon {
|
||||
background-color: #ccc;
|
||||
}
|
После Ширина: | Высота: | Размер: 6.9 KiB |
87
package.json
|
@ -1,28 +1,78 @@
|
|||
{
|
||||
"name": "iot-pnp",
|
||||
"displayName": "iot-pnp",
|
||||
"description": "IoT Plug and Play",
|
||||
"name": "azure-digital-twins",
|
||||
"displayName": "IoT Plug and Play",
|
||||
"description": "Author IoT Plug and Play models, publish and manage with Model Repository",
|
||||
"version": "0.0.1",
|
||||
"publisher": "vsciot-vscode",
|
||||
"aiKey": "5b869bc6-ca93-4f24-aa87-92871a3a616e",
|
||||
"icon": "logo.png",
|
||||
"license": "SEE LICENSE IN LICENSE.txt",
|
||||
"engines": {
|
||||
"vscode": "^1.36.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Microsoft/vscode-azure-digital-twins.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/Microsoft/vscode-azure-digital-twins/issues"
|
||||
},
|
||||
"homepage": "https://github.com/Microsoft/vscode-azure-digital-twins/blob/master/README.md",
|
||||
"categories": [
|
||||
"Azure",
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onCommand:extension.helloWorld"
|
||||
"onCommand:azure-digital-twins.createInterface",
|
||||
"onCommand:azure-digital-twins.createCapabilityModel",
|
||||
"onCommand:azure-digital-twins.openRepository",
|
||||
"onCommand:azure-digital-twins.signOutRepository",
|
||||
"onCommand:azure-digital-twins.submitFiles"
|
||||
],
|
||||
"main": "./out/extension.js",
|
||||
"main": "./out/extension",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "extension.helloWorld",
|
||||
"title": "Hello World"
|
||||
"command": "azure-digital-twins.createInterface",
|
||||
"title": "Create Interface",
|
||||
"category": "IoT Plug and Play"
|
||||
},
|
||||
{
|
||||
"command": "azure-digital-twins.createCapabilityModel",
|
||||
"title": "Create Capability Model",
|
||||
"category": "IoT Plug and Play"
|
||||
},
|
||||
{
|
||||
"command": "azure-digital-twins.openRepository",
|
||||
"title": "Open Model Repository",
|
||||
"category": "IoT Plug and Play"
|
||||
},
|
||||
{
|
||||
"command": "azure-digital-twins.signOutRepository",
|
||||
"title": "Sign out Model Repository",
|
||||
"category": "IoT Plug and Play"
|
||||
},
|
||||
{
|
||||
"command": "azure-digital-twins.submitFiles",
|
||||
"title": "Submit Files to Model Repository",
|
||||
"category": "IoT Plug and Play"
|
||||
}
|
||||
],
|
||||
"configuration": [
|
||||
{
|
||||
"title": "IoT Plug and Play Configuration",
|
||||
"properties": {
|
||||
"iot-pnp.publicRepositoryUrl": {
|
||||
"type": "string",
|
||||
"default": "https://repo.azureiotrepository.com",
|
||||
"description": "Set the public model repository url."
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"languages": [
|
||||
{
|
||||
"id": "pnp-channel",
|
||||
"id": "colorized-channel",
|
||||
"mimetypes": [
|
||||
"text/x-code-output"
|
||||
]
|
||||
|
@ -30,9 +80,9 @@
|
|||
],
|
||||
"grammars": [
|
||||
{
|
||||
"language": "pnp-channel",
|
||||
"scopeName": "text.channel.pnp",
|
||||
"path": "./syntaxes/pnp.channel.tmLanguage"
|
||||
"language": "colorized-channel",
|
||||
"scopeName": "text.channel.colorized",
|
||||
"path": "./syntaxes/colorized.channel.tmLanguage"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -40,18 +90,29 @@
|
|||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"watch": "tsc -watch -p ./",
|
||||
"pretest": "npm run compile",
|
||||
"tslint": "tslint -t verbose src/**/*.ts",
|
||||
"test": "node ./out/test/runTest.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^7.0.0",
|
||||
"@types/glob": "^7.1.1",
|
||||
"@types/mocha": "^5.2.6",
|
||||
"@types/node": "^10.12.21",
|
||||
"@types/vscode": "^1.36.0",
|
||||
"cz-conventional-changelog": "^3.0.2",
|
||||
"glob": "^7.1.4",
|
||||
"mocha": "^6.1.4",
|
||||
"typescript": "^3.3.1",
|
||||
"tslint": "^5.12.1",
|
||||
"typescript": "^3.3.1",
|
||||
"vscode-test": "^1.0.0-next.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-extension-telemetry": "^0.1.0",
|
||||
"fs-extra": "^7.0.1"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"@id": "{DigitalTwinIdentifier}",
|
||||
"@type": "CapabilityModel",
|
||||
"displayName": "mycapabilitymodel",
|
||||
"implements": [
|
||||
{
|
||||
"schema": "urn:azureiot:DeviceManagement:DeviceInformation:1",
|
||||
"name": "deviceInfo"
|
||||
},
|
||||
{
|
||||
"schema": "urn:azureiot:ModelDiscovery:ModelDefinition:1",
|
||||
"name": "modelDefinition"
|
||||
}
|
||||
],
|
||||
"@context": "http://azureiot.com/v1/contexts/IoTModel.json"
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
{
|
||||
"@id": "{DigitalTwinIdentifier}",
|
||||
"@type": "Interface",
|
||||
"displayName": "myinterface",
|
||||
"contents": [
|
||||
{
|
||||
"@type": "Property",
|
||||
"displayName": "Device Name",
|
||||
"description": "The name of the device.",
|
||||
"name": "name",
|
||||
"schema": "string",
|
||||
"writable": false
|
||||
},
|
||||
{
|
||||
"@type": "Property",
|
||||
"name": "fanSpeed",
|
||||
"displayName": "Fan Speed",
|
||||
"writable": true,
|
||||
"schema": "double"
|
||||
},
|
||||
{
|
||||
"@type": "Telemetry",
|
||||
"comment": "This shows an event that contains a single value (temperature).",
|
||||
"name": "temperature",
|
||||
"schema": "double"
|
||||
},
|
||||
{
|
||||
"@type": "Telemetry",
|
||||
"name": "magnetometer",
|
||||
"displayName": "Magnetometer",
|
||||
"comment": "This shows a complex telemetry that contains a magnetometer reading.",
|
||||
"schema": {
|
||||
"@type": "Object",
|
||||
"fields": [
|
||||
{
|
||||
"name": "x",
|
||||
"schema": "integer"
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"schema": "integer"
|
||||
},
|
||||
{
|
||||
"name": "z",
|
||||
"schema": "integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"@type": "Command",
|
||||
"description": "This command will begin blinking the LED for given time interval.",
|
||||
"name": "blink",
|
||||
"commandType": "synchronous",
|
||||
"request": {
|
||||
"name": "interval",
|
||||
"schema": "long"
|
||||
},
|
||||
"response": {
|
||||
"name": "blinkResponse",
|
||||
"schema": {
|
||||
"@type": "Object",
|
||||
"fields": [
|
||||
{
|
||||
"name": "description",
|
||||
"schema": "string"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"@context": "http://azureiot.com/v1/contexts/IoTModel.json"
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as vscode from "vscode";
|
||||
|
||||
export class ColorizedChannel {
|
||||
private static buildTag(name: string | undefined): string {
|
||||
return name ? `[${name}]` : "";
|
||||
}
|
||||
|
||||
private channel: vscode.OutputChannel;
|
||||
|
||||
constructor(name: string) {
|
||||
this.channel = vscode.window.createOutputChannel(name);
|
||||
}
|
||||
|
||||
public start(message: string, component?: string): void {
|
||||
const tag = ColorizedChannel.buildTag(component);
|
||||
this.channel.appendLine(`[Start]${tag} ${message}`);
|
||||
}
|
||||
|
||||
public end(message: string, component?: string): void {
|
||||
const tag = ColorizedChannel.buildTag(component);
|
||||
this.channel.appendLine(`[Done]${tag} ${message}`);
|
||||
}
|
||||
|
||||
public warn(message: string, component?: string): void {
|
||||
const tag = ColorizedChannel.buildTag(component);
|
||||
this.channel.appendLine(`[Warn]${tag} ${message}`);
|
||||
}
|
||||
|
||||
public error(message: string, component?: string): void {
|
||||
const tag = ColorizedChannel.buildTag(component);
|
||||
this.channel.appendLine(`[Error]${tag} ${message}`);
|
||||
}
|
||||
|
||||
public info(message: string): void {
|
||||
this.channel.appendLine(message);
|
||||
}
|
||||
|
||||
public show(): void {
|
||||
this.channel.show();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
export class Command {
|
||||
public static readonly CREATE_INTERFACE = new Command("azure-digital-twins.createInterface", "Create Interface");
|
||||
public static readonly CREATE_CAPABILITY_MODEL = new Command(
|
||||
"azure-digital-twins.createCapabilityModel",
|
||||
"Create Capability Model",
|
||||
);
|
||||
public static readonly OPEN_REPOSITORY = new Command("azure-digital-twins.openRepository", "Open Model Repository");
|
||||
public static readonly SIGN_OUT_REPOSITORY = new Command(
|
||||
"azure-digital-twins.signOutRepository",
|
||||
"Sign out Model Repository",
|
||||
);
|
||||
public static readonly SUBMIT_FILES = new Command(
|
||||
"azure-digital-twins.submitFiles",
|
||||
"Submit Files to Model Repository",
|
||||
);
|
||||
|
||||
private constructor(public readonly id: string, public readonly description: string) {}
|
||||
}
|
|
@ -1,4 +1,22 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
export const EXTENSION_NAME = "IoT Plug and Play";
|
||||
export class Constants {
|
||||
public static readonly CHANNEL_NAME = "IoT Plug and Play";
|
||||
|
||||
public static readonly UTF8 = "utf8";
|
||||
public static readonly RESOURCE_FOLDER = "resources";
|
||||
public static readonly TEMPLATE_FOLDER = "templates";
|
||||
public static readonly SAMPLE_FILENAME = "sample";
|
||||
|
||||
public static readonly DEVICE_MODEL_COMPONENT = "Device Model";
|
||||
public static readonly MODEL_REPOSITORY_COMPONENT = "Model Repository";
|
||||
|
||||
public static readonly EXTENSION_ACTIVATED_MSG = "extensionActivated";
|
||||
|
||||
public static readonly NSAT_SURVEY_URL = "https://aka.ms/vscode-azure-digital-twins-survey";
|
||||
|
||||
public static readonly MODEL_NAME_REGEX = new RegExp("^[a-zA-Z_][a-zA-Z0-9_]*$");
|
||||
public static readonly MODEL_NAME_REGEX_DESCRIPTION = "alphanumeric and underscore, not start with number";
|
||||
public static readonly DIGITAL_TWIN_ID_PLACEHOLDER = "{DigitalTwinIdentifier}";
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { commands, ExtensionContext, Memento, Uri, window } from "vscode";
|
||||
import { TelemetryClient } from "./telemetryClient";
|
||||
|
||||
const PROBABILITY = 1;
|
||||
const SESSION_COUNT_THRESHOLD = 2;
|
||||
const UNKNOWN = "unknown";
|
||||
const PACKAGE_JSON_PATH = "./package.json";
|
||||
const SESSION_COUNT_KEY = "nsat/sessionCount";
|
||||
const LAST_SESSION_DATE_KEY = "nsat/lastSessionDate";
|
||||
const TAKE_SURVEY_DATE_KEY = "nsat/takeSurveyDate";
|
||||
const DONT_SHOW_DATE_KEY = "nsat/dontShowDate";
|
||||
const SKIP_VERSION_KEY = "nsat/skipVersion";
|
||||
const IS_CANDIDATE_KEY = "nsat/isCandidate";
|
||||
|
||||
export class NSAT {
|
||||
constructor(private readonly surveyUrl: string, private readonly telemetryClient: TelemetryClient) {}
|
||||
|
||||
public async takeSurvey(context: ExtensionContext) {
|
||||
const packageJSON = require(context.asAbsolutePath(PACKAGE_JSON_PATH));
|
||||
if (!packageJSON) {
|
||||
return;
|
||||
}
|
||||
|
||||
const globalState: Memento = context.globalState;
|
||||
if (!globalState) {
|
||||
return;
|
||||
}
|
||||
|
||||
const skipVersion: string = globalState.get(SKIP_VERSION_KEY, "");
|
||||
if (skipVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
const date: string = new Date().toDateString();
|
||||
const lastSessionDate: string = globalState.get(LAST_SESSION_DATE_KEY, new Date(0).toDateString());
|
||||
if (date === lastSessionDate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sessionCount: number = globalState.get(SESSION_COUNT_KEY, 0) + 1;
|
||||
await globalState.update(LAST_SESSION_DATE_KEY, date);
|
||||
await globalState.update(SESSION_COUNT_KEY, sessionCount);
|
||||
if (sessionCount < SESSION_COUNT_THRESHOLD) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isCandidate: boolean = globalState.get(IS_CANDIDATE_KEY, false) || Math.random() < PROBABILITY;
|
||||
await globalState.update(IS_CANDIDATE_KEY, isCandidate);
|
||||
const extensionVersion: string = packageJSON.version || UNKNOWN;
|
||||
if (!isCandidate) {
|
||||
await globalState.update(SKIP_VERSION_KEY, extensionVersion);
|
||||
return;
|
||||
}
|
||||
|
||||
const take = {
|
||||
title: "Take Survey",
|
||||
run: async () => {
|
||||
this.telemetryClient.sendEvent("nsat.survey/takeShortSurvey");
|
||||
commands.executeCommand(
|
||||
"vscode.open",
|
||||
Uri.parse(
|
||||
`${this.surveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(extensionVersion)}`,
|
||||
),
|
||||
);
|
||||
await globalState.update(IS_CANDIDATE_KEY, false);
|
||||
await globalState.update(SKIP_VERSION_KEY, extensionVersion);
|
||||
await globalState.update(TAKE_SURVEY_DATE_KEY, date);
|
||||
},
|
||||
};
|
||||
const remind = {
|
||||
title: "Remind Me Later",
|
||||
run: async () => {
|
||||
this.telemetryClient.sendEvent("nsat.survey/remindMeLater");
|
||||
await globalState.update(SESSION_COUNT_KEY, 0);
|
||||
},
|
||||
};
|
||||
const never = {
|
||||
title: "Don't Show Again",
|
||||
run: async () => {
|
||||
this.telemetryClient.sendEvent("nsat.survey/dontShowAgain");
|
||||
await globalState.update(IS_CANDIDATE_KEY, false);
|
||||
await globalState.update(SKIP_VERSION_KEY, extensionVersion);
|
||||
await globalState.update(DONT_SHOW_DATE_KEY, date);
|
||||
},
|
||||
};
|
||||
|
||||
this.telemetryClient.sendEvent("nsat.survey/userAsked");
|
||||
const button = await window.showInformationMessage(
|
||||
"Do you mind taking a quick feedback survey about the Azure IoT Edge Extension for VS Code?",
|
||||
take,
|
||||
remind,
|
||||
never,
|
||||
);
|
||||
await (button || remind).run();
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as vscode from "vscode";
|
||||
|
||||
import * as constants from "./constants";
|
||||
|
||||
export const pnpChannel = {
|
||||
channel: vscode.window.createOutputChannel(constants.EXTENSION_NAME),
|
||||
|
||||
start(component: string, message: string) {
|
||||
this.channel.appendLine(`[Start][${component}] ${message}`);
|
||||
},
|
||||
|
||||
end(component: string, message: string) {
|
||||
this.channel.appendLine(`[Complete][${component}] ${message}`);
|
||||
},
|
||||
|
||||
warn(component: string, message: string) {
|
||||
this.channel.appendLine(`[Warn][${component}] ${message}`);
|
||||
},
|
||||
|
||||
error(component: string, message: string) {
|
||||
this.channel.appendLine(`[Error][${component}] ${message}`);
|
||||
},
|
||||
|
||||
info(message: string) {
|
||||
this.channel.appendLine(message);
|
||||
},
|
||||
|
||||
show() {
|
||||
this.channel.show();
|
||||
},
|
||||
|
||||
hide() {
|
||||
this.channel.hide();
|
||||
}
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
export class ProcessError extends Error {
|
||||
constructor(message: string, public readonly component: string) {
|
||||
super(message);
|
||||
this.name = "ProcessError";
|
||||
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, ProcessError);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as vscode from "vscode";
|
||||
import TelemetryReporter from "vscode-extension-telemetry";
|
||||
|
||||
const MILLISECOND = 1000;
|
||||
const PACKAGE_JSON_PATH = "./package.json";
|
||||
const INTERNAL_USER_DOMAIN = "microsoft.com";
|
||||
|
||||
export interface TelemetryContext {
|
||||
start: number;
|
||||
properties: { [key: string]: string };
|
||||
measurements: { [key: string]: number };
|
||||
}
|
||||
|
||||
export enum TelemetryResult {
|
||||
Succeeded = "Succeeded",
|
||||
Failed = "Failed",
|
||||
Cancelled = "Cancelled",
|
||||
}
|
||||
|
||||
export class TelemetryClient {
|
||||
private static validatePackageJSON(packageJSON: any): boolean {
|
||||
return packageJSON.name && packageJSON.publisher && packageJSON.version && packageJSON.aiKey;
|
||||
}
|
||||
|
||||
private static isInternalUser(): boolean {
|
||||
const userDomain = process.env.USERDNSDOMAIN ? process.env.USERDNSDOMAIN.toLowerCase() : "";
|
||||
return userDomain.endsWith(INTERNAL_USER_DOMAIN);
|
||||
}
|
||||
|
||||
private client: TelemetryReporter | undefined;
|
||||
private isInternal: boolean = false;
|
||||
|
||||
constructor(context: vscode.ExtensionContext) {
|
||||
const packageJSON = require(context.asAbsolutePath(PACKAGE_JSON_PATH));
|
||||
if (!packageJSON) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TelemetryClient.validatePackageJSON(packageJSON)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.client = new TelemetryReporter(
|
||||
`${packageJSON.publisher}.${packageJSON.name}`,
|
||||
packageJSON.version,
|
||||
packageJSON.aiKey,
|
||||
);
|
||||
this.isInternal = TelemetryClient.isInternalUser();
|
||||
}
|
||||
|
||||
public sendEvent(eventName: string, telemetryContext?: TelemetryContext): void {
|
||||
if (!this.client) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (telemetryContext) {
|
||||
this.client.sendTelemetryEvent(eventName, telemetryContext.properties, telemetryContext.measurements);
|
||||
} else {
|
||||
this.client.sendTelemetryEvent(eventName);
|
||||
}
|
||||
}
|
||||
|
||||
public createContext(): TelemetryContext {
|
||||
const context: TelemetryContext = { start: Date.now(), properties: {}, measurements: {} };
|
||||
context.properties.isInternal = this.isInternal.toString();
|
||||
context.properties.result = TelemetryResult.Succeeded;
|
||||
return context;
|
||||
}
|
||||
|
||||
public setErrorContext(context: TelemetryContext, error: Error): void {
|
||||
context.properties.result = TelemetryResult.Failed;
|
||||
context.properties.error = error.name;
|
||||
context.properties.errorMessage = error.message;
|
||||
}
|
||||
|
||||
public setCancelContext(context: TelemetryContext): void {
|
||||
context.properties.result = TelemetryResult.Cancelled;
|
||||
}
|
||||
|
||||
public closeContext(context: TelemetryContext) {
|
||||
context.measurements.duration = (Date.now() - context.start) / MILLISECOND;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
export class UserCancelledError extends Error {
|
||||
constructor(operation?: string) {
|
||||
const message = operation ? ` on [${operation}]` : "";
|
||||
super("User cancelled the operation" + message);
|
||||
this.name = "UserCancelledError";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as fs from "fs-extra";
|
||||
import * as path from "path";
|
||||
import { DeviceModelManager, ModelType } from "../deviceModel/deviceModelManager";
|
||||
import { Constants } from "./constants";
|
||||
|
||||
export class Utility {
|
||||
public static async createFileFromTemplate(
|
||||
templatePath: string,
|
||||
filePath: string,
|
||||
replacement: Map<string, string>,
|
||||
): Promise<void> {
|
||||
const template: string = await fs.readFile(templatePath, Constants.UTF8);
|
||||
const content: string = Utility.replaceAll(template, replacement);
|
||||
await fs.writeFile(filePath, content, { encoding: Constants.UTF8 });
|
||||
}
|
||||
|
||||
public static replaceAll(str: string, replacement: Map<string, string>, caseInsensitive: boolean = false): string {
|
||||
const flag: string = caseInsensitive ? "ig" : "g";
|
||||
const keys = Array.from(replacement.keys());
|
||||
const pattern: RegExp = new RegExp(keys.join("|"), flag);
|
||||
return str.replace(pattern, (matched) => {
|
||||
const value: string | undefined = replacement.get(matched);
|
||||
return value ? value : matched;
|
||||
});
|
||||
}
|
||||
|
||||
public static async validateModelName(name: string, type: ModelType, folder?: string): Promise<string | undefined> {
|
||||
if (!name || name.trim() === "") {
|
||||
return "Name could not be empty";
|
||||
}
|
||||
if (!Constants.MODEL_NAME_REGEX.test(name)) {
|
||||
return `Name can only contain ${Constants.MODEL_NAME_REGEX_DESCRIPTION}`;
|
||||
}
|
||||
|
||||
if (folder) {
|
||||
const filename = DeviceModelManager.generateModelFilename(name, type);
|
||||
if (await fs.pathExists(path.join(folder, filename))) {
|
||||
return `${type} ${name} already exists in folder ${folder}`;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as path from "path";
|
||||
import * as vscode from "vscode";
|
||||
import { ColorizedChannel } from "../common/colorizedChannel";
|
||||
import { Constants } from "../common/constants";
|
||||
import { ProcessError } from "../common/processError";
|
||||
import { Utility } from "../common/utility";
|
||||
import { MessageType, UI } from "../views/ui";
|
||||
import { UIConstants } from "../views/uiConstants";
|
||||
|
||||
export enum ModelType {
|
||||
Interface = "Interface",
|
||||
CapabilityModel = "Capability Model",
|
||||
}
|
||||
|
||||
export class DeviceModelManager {
|
||||
public static generateModelId(name: string): string {
|
||||
return `urn:{your name}:${name}:1`;
|
||||
}
|
||||
|
||||
public static generateModelFilename(name: string, type: ModelType): string {
|
||||
const fileType: string = type.replace(/\s+/g, "").toLowerCase();
|
||||
return `${name}.${fileType}.json`;
|
||||
}
|
||||
|
||||
public static getTemplateFilename(type: ModelType): string {
|
||||
return DeviceModelManager.generateModelFilename(Constants.SAMPLE_FILENAME, type);
|
||||
}
|
||||
|
||||
constructor(private readonly context: vscode.ExtensionContext, private readonly outputChannel: ColorizedChannel) {}
|
||||
|
||||
public async createModel(type: ModelType): Promise<void> {
|
||||
const folder: string = await UI.selectRootFolder(UIConstants.SELECT_ROOT_FOLDER_LABEL);
|
||||
const name: string = await UI.inputModelName(UIConstants.INPUT_MODEL_NAME_LABEL, type, folder);
|
||||
const model: string = `${type} ${name}`;
|
||||
|
||||
this.outputChannel.start(`Create ${model} in folder ${folder}`, Constants.DEVICE_MODEL_COMPONENT);
|
||||
let filePath: string;
|
||||
try {
|
||||
filePath = await this.doCreateModel(type, folder, name);
|
||||
} catch (error) {
|
||||
const errorMessage = `Fail to create ${model}. Error: ${error.message}`;
|
||||
throw new ProcessError(errorMessage, Constants.DEVICE_MODEL_COMPONENT);
|
||||
}
|
||||
|
||||
const message = `${model} is created successfully`;
|
||||
await UI.openAndShowTextDocument(filePath);
|
||||
UI.showNotification(MessageType.Info, message);
|
||||
this.outputChannel.end(message, Constants.DEVICE_MODEL_COMPONENT);
|
||||
}
|
||||
|
||||
private async doCreateModel(type: ModelType, folder: string, name: string): Promise<string> {
|
||||
const modelId = DeviceModelManager.generateModelId(name);
|
||||
const filePath = path.join(folder, DeviceModelManager.generateModelFilename(name, type));
|
||||
const templatePath = this.context.asAbsolutePath(
|
||||
path.join(Constants.RESOURCE_FOLDER, Constants.TEMPLATE_FOLDER, DeviceModelManager.getTemplateFilename(type)),
|
||||
);
|
||||
const replacement = new Map<string, string>();
|
||||
replacement.set(Constants.DIGITAL_TWIN_ID_PLACEHOLDER, modelId);
|
||||
|
||||
await Utility.createFileFromTemplate(templatePath, filePath, replacement);
|
||||
return filePath;
|
||||
}
|
||||
}
|
109
src/extension.ts
|
@ -1,39 +1,92 @@
|
|||
// The module 'vscode' contains the VS Code extensibility API
|
||||
// Import the module and reference it with the alias vscode in your code below
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as vscode from "vscode";
|
||||
import { ColorizedChannel } from "./common/colorizedChannel";
|
||||
import { Command } from "./common/command";
|
||||
import { Constants } from "./common/constants";
|
||||
import { NSAT } from "./common/nsat";
|
||||
import { ProcessError } from "./common/processError";
|
||||
import { TelemetryClient, TelemetryContext } from "./common/telemetryClient";
|
||||
import { UserCancelledError } from "./common/userCancelledError";
|
||||
import { DeviceModelManager, ModelType } from "./deviceModel/deviceModelManager";
|
||||
import { MessageType, UI } from "./views/ui";
|
||||
|
||||
import * as constants from "./common/constants";
|
||||
|
||||
import { pnpChannel } from "./common/outputChannel";
|
||||
|
||||
// this method is called when your extension is activated
|
||||
// your extension is activated the very first time the command is executed
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
// Use the console to output diagnostic information (console.log) and errors (console.error)
|
||||
// This line of code will only be executed once when your extension is activated
|
||||
console.log('Congratulations, your extension "iot-pnp" is now active!');
|
||||
const outputChannel = new ColorizedChannel(Constants.CHANNEL_NAME);
|
||||
const telemetryClient = new TelemetryClient(context);
|
||||
const nsat = new NSAT(Constants.NSAT_SURVEY_URL, telemetryClient);
|
||||
const deviceModelManager = new DeviceModelManager(context, outputChannel);
|
||||
|
||||
// The command has been defined in the package.json file
|
||||
// Now provide the implementation of the command with registerCommand
|
||||
// The commandId parameter must match the command field in package.json
|
||||
let disposable = vscode.commands.registerCommand(
|
||||
"extension.helloWorld",
|
||||
() => {
|
||||
// The code you place here will be executed every time your command is executed
|
||||
telemetryClient.sendEvent(Constants.EXTENSION_ACTIVATED_MSG);
|
||||
|
||||
// Display a message box to the user
|
||||
let message = "Hello World!";
|
||||
pnpChannel.start(constants.EXTENSION_NAME, message);
|
||||
pnpChannel.end(constants.EXTENSION_NAME, message);
|
||||
pnpChannel.info(message);
|
||||
pnpChannel.warn(constants.EXTENSION_NAME, message);
|
||||
pnpChannel.error(constants.EXTENSION_NAME, message);
|
||||
pnpChannel.show();
|
||||
}
|
||||
initCommand(
|
||||
context,
|
||||
telemetryClient,
|
||||
outputChannel,
|
||||
nsat,
|
||||
true,
|
||||
Command.CREATE_INTERFACE,
|
||||
(): Promise<void> => {
|
||||
return deviceModelManager.createModel(ModelType.Interface);
|
||||
},
|
||||
);
|
||||
|
||||
context.subscriptions.push(disposable);
|
||||
initCommand(
|
||||
context,
|
||||
telemetryClient,
|
||||
outputChannel,
|
||||
nsat,
|
||||
true,
|
||||
Command.CREATE_CAPABILITY_MODEL,
|
||||
(): Promise<void> => {
|
||||
return deviceModelManager.createModel(ModelType.CapabilityModel);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// this method is called when your extension is deactivated
|
||||
export function deactivate() {}
|
||||
|
||||
function initCommand(
|
||||
context: vscode.ExtensionContext,
|
||||
telemetryClient: TelemetryClient,
|
||||
outputChannel: ColorizedChannel,
|
||||
nsat: NSAT,
|
||||
enableSurvey: boolean,
|
||||
command: Command,
|
||||
callback: (...args: any[]) => Promise<any>,
|
||||
): void {
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand(command.id, async (...args: any[]) => {
|
||||
const telemetryContext: TelemetryContext = telemetryClient.createContext();
|
||||
telemetryClient.sendEvent(`${command.id}.start`);
|
||||
outputChannel.show();
|
||||
outputChannel.start(`Trigger command ${command.description}`);
|
||||
|
||||
try {
|
||||
return await callback(...args);
|
||||
} catch (error) {
|
||||
if (error instanceof UserCancelledError) {
|
||||
outputChannel.warn(error.message);
|
||||
} else {
|
||||
telemetryClient.setErrorContext(telemetryContext, error);
|
||||
UI.showNotification(MessageType.Error, error.message);
|
||||
if (error instanceof ProcessError) {
|
||||
const message = `${error.message}\nStack: ${error.stack}`;
|
||||
outputChannel.error(message, error.component);
|
||||
} else {
|
||||
outputChannel.error(error.message);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
telemetryClient.closeContext(telemetryContext);
|
||||
telemetryClient.sendEvent(`${command.id}.end`, telemetryContext);
|
||||
outputChannel.end(`Complete command ${command.description}`);
|
||||
if (enableSurvey) {
|
||||
nsat.takeSurvey(context);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
import * as path from 'path';
|
||||
import * as path from "path";
|
||||
|
||||
import { runTests } from 'vscode-test';
|
||||
import { runTests } from "vscode-test";
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
// The folder containing the Extension Manifest package.json
|
||||
// Passed to `--extensionDevelopmentPath`
|
||||
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
|
||||
try {
|
||||
// The folder containing the Extension Manifest package.json
|
||||
// Passed to `--extensionDevelopmentPath`
|
||||
const extensionDevelopmentPath = path.resolve(__dirname, "../../");
|
||||
|
||||
// The path to test runner
|
||||
// Passed to --extensionTestsPath
|
||||
const extensionTestsPath = path.resolve(__dirname, './suite/index');
|
||||
// The path to test runner
|
||||
// Passed to --extensionTestsPath
|
||||
const extensionTestsPath = path.resolve(__dirname, "./suite/index");
|
||||
|
||||
// Download VS Code, unzip it and run the integration test
|
||||
await runTests({ extensionDevelopmentPath, extensionTestsPath });
|
||||
} catch (err) {
|
||||
console.error('Failed to run tests');
|
||||
process.exit(1);
|
||||
}
|
||||
// Download VS Code, unzip it and run the integration test
|
||||
await runTests({ extensionDevelopmentPath, extensionTestsPath });
|
||||
} catch (err) {
|
||||
console.error("Failed to run tests");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import * as assert from 'assert';
|
||||
import { before } from 'mocha';
|
||||
import * as assert from "assert";
|
||||
import { before } from "mocha";
|
||||
|
||||
// You can import and use all API from the 'vscode' module
|
||||
// as well as import your extension to test it
|
||||
import * as vscode from 'vscode';
|
||||
import * as vscode from "vscode";
|
||||
// import * as myExtension from '../extension';
|
||||
|
||||
suite('Extension Test Suite', () => {
|
||||
before(() => {
|
||||
vscode.window.showInformationMessage('Start all tests.');
|
||||
});
|
||||
suite("Extension Test Suite", () => {
|
||||
before(() => {
|
||||
vscode.window.showInformationMessage("Start all tests.");
|
||||
});
|
||||
|
||||
test('Sample test', () => {
|
||||
assert.equal(-1, [1, 2, 3].indexOf(5));
|
||||
assert.equal(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
test("Sample test", () => {
|
||||
assert.equal(-1, [1, 2, 3].indexOf(5));
|
||||
assert.equal(-1, [1, 2, 3].indexOf(0));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,37 +1,37 @@
|
|||
import * as path from 'path';
|
||||
import * as Mocha from 'mocha';
|
||||
import * as glob from 'glob';
|
||||
import * as glob from "glob";
|
||||
import * as Mocha from "mocha";
|
||||
import * as path from "path";
|
||||
|
||||
export function run(): Promise<void> {
|
||||
// Create the mocha test
|
||||
const mocha = new Mocha({
|
||||
ui: 'tdd',
|
||||
});
|
||||
mocha.useColors(true);
|
||||
// Create the mocha test
|
||||
const mocha = new Mocha({
|
||||
ui: "tdd",
|
||||
});
|
||||
mocha.useColors(true);
|
||||
|
||||
const testsRoot = path.resolve(__dirname, '..');
|
||||
const testsRoot = path.resolve(__dirname, "..");
|
||||
|
||||
return new Promise((c, e) => {
|
||||
glob('**/**.test.js', { cwd: testsRoot }, (err, files) => {
|
||||
if (err) {
|
||||
return e(err);
|
||||
}
|
||||
return new Promise((c, e) => {
|
||||
glob("**/**.test.js", { cwd: testsRoot }, (err, files) => {
|
||||
if (err) {
|
||||
return e(err);
|
||||
}
|
||||
|
||||
// Add files to the test suite
|
||||
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
|
||||
// Add files to the test suite
|
||||
files.forEach((f) => mocha.addFile(path.resolve(testsRoot, f)));
|
||||
|
||||
try {
|
||||
// Run the mocha test
|
||||
mocha.run(failures => {
|
||||
if (failures > 0) {
|
||||
e(new Error(`${failures} tests failed.`));
|
||||
} else {
|
||||
c();
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
e(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
try {
|
||||
// Run the mocha test
|
||||
mocha.run((failures) => {
|
||||
if (failures > 0) {
|
||||
e(new Error(`${failures} tests failed.`));
|
||||
} else {
|
||||
c();
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
e(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as path from "path";
|
||||
import * as vscode from "vscode";
|
||||
import { UserCancelledError } from "../common/userCancelledError";
|
||||
import { Utility } from "../common/utility";
|
||||
import { ModelType } from "../deviceModel/deviceModelManager";
|
||||
|
||||
export enum MessageType {
|
||||
Info,
|
||||
Warn,
|
||||
Error,
|
||||
}
|
||||
|
||||
export class UI {
|
||||
public static async openAndShowTextDocument(filePath: string): Promise<void> {
|
||||
const folder: string = path.dirname(filePath);
|
||||
await vscode.commands.executeCommand("vscode.openFolder", vscode.Uri.file(folder), false);
|
||||
await vscode.window.showTextDocument(vscode.Uri.file(filePath));
|
||||
}
|
||||
|
||||
public static showNotification(type: MessageType, message: string): void {
|
||||
switch (type) {
|
||||
case MessageType.Info:
|
||||
vscode.window.showInformationMessage(message);
|
||||
break;
|
||||
case MessageType.Warn:
|
||||
vscode.window.showWarningMessage(message);
|
||||
break;
|
||||
case MessageType.Error:
|
||||
vscode.window.showErrorMessage(message);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
public static async selectRootFolder(label: string): Promise<string> {
|
||||
const worksapceFolders = vscode.workspace.workspaceFolders;
|
||||
// use the only workspace as default
|
||||
if (worksapceFolders && worksapceFolders.length === 1) {
|
||||
return worksapceFolders[0].uri.fsPath;
|
||||
}
|
||||
|
||||
// select workspace or open specified folder
|
||||
let items: vscode.QuickPickItem[] = [];
|
||||
if (worksapceFolders) {
|
||||
items = worksapceFolders.map((f: vscode.WorkspaceFolder) => {
|
||||
const fsPath = f.uri.fsPath;
|
||||
return {
|
||||
label: path.basename(fsPath),
|
||||
description: fsPath,
|
||||
};
|
||||
});
|
||||
}
|
||||
items.push({ label: "Browse...", description: undefined });
|
||||
const selected: vscode.QuickPickItem = await UI.showQuickPick(items, label);
|
||||
|
||||
// browse to open folder
|
||||
return selected.description ? selected.description : await UI.showOpenDialog(label);
|
||||
}
|
||||
|
||||
public static async showQuickPick(items: vscode.QuickPickItem[], label: string): Promise<vscode.QuickPickItem> {
|
||||
const options: vscode.QuickPickOptions = {
|
||||
placeHolder: label,
|
||||
ignoreFocusOut: true,
|
||||
};
|
||||
|
||||
const result: vscode.QuickPickItem | undefined = await vscode.window.showQuickPick(items, options);
|
||||
if (!result) {
|
||||
throw new UserCancelledError(label);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static async showOpenDialog(label: string, defaultUri?: vscode.Uri): Promise<string> {
|
||||
const options: vscode.OpenDialogOptions = {
|
||||
openLabel: label,
|
||||
defaultUri,
|
||||
canSelectFiles: false,
|
||||
canSelectFolders: true,
|
||||
canSelectMany: false,
|
||||
};
|
||||
|
||||
const result: vscode.Uri[] | undefined = await vscode.window.showOpenDialog(options);
|
||||
if (!result || result.length === 0) {
|
||||
throw new UserCancelledError(label);
|
||||
}
|
||||
return result[0].fsPath;
|
||||
}
|
||||
|
||||
public static async inputModelName(label: string, type: ModelType, folder: string): Promise<string> {
|
||||
const placeHolder = `${type} name`;
|
||||
const validateInput = async (name: string): Promise<string | undefined> => {
|
||||
return await Utility.validateModelName(name, type, folder);
|
||||
};
|
||||
return await UI.showInputBox(label, placeHolder, validateInput);
|
||||
}
|
||||
|
||||
public static async showInputBox(
|
||||
label: string,
|
||||
placeHolder: string,
|
||||
validateInput?: (s: string) => Promise<string | undefined>,
|
||||
value?: string,
|
||||
ignoreFocusOut: boolean = true,
|
||||
): Promise<string> {
|
||||
const options: vscode.InputBoxOptions = {
|
||||
prompt: label,
|
||||
placeHolder,
|
||||
validateInput,
|
||||
value,
|
||||
ignoreFocusOut,
|
||||
};
|
||||
|
||||
const result: string | undefined = await vscode.window.showInputBox(options);
|
||||
if (!result) {
|
||||
throw new UserCancelledError(label);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
export class UIConstants {
|
||||
public static readonly SELECT_ROOT_FOLDER_LABEL = "Select folder";
|
||||
public static readonly INPUT_MODEL_NAME_LABEL = "Input device model name";
|
||||
}
|
|
@ -3,16 +3,16 @@
|
|||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>pnpChannel</string>
|
||||
<string>colorizedChannel</string>
|
||||
<key>scopeName</key>
|
||||
<string>text.channel.pnp</string>
|
||||
<string>text.channel.colorized</string>
|
||||
<key>patterns</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>channel.pnp.info</string>
|
||||
<string>channel.colorized.info</string>
|
||||
<key>match</key>
|
||||
<string>(\[Start\]|\[Complete\])(.*)</string>
|
||||
<string>(\[Start\]|\[Done\])(.*)</string>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
|
@ -24,9 +24,9 @@
|
|||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>channle.pnp.warn</string>
|
||||
<string>channel.colorized.warn</string>
|
||||
<key>match</key>
|
||||
<string>(\[Warn\])(.*)</string>
|
||||
<string>(\[Warn\])(.+)</string>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
||||
|
@ -38,9 +38,9 @@
|
|||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>channel.pnp.error</string>
|
||||
<string>channel.colorized.error</string>
|
||||
<key>match</key>
|
||||
<string>(\[Error\])(.*)</string>
|
||||
<string>(\[Error\])(.+)</string>
|
||||
<key>captures</key>
|
||||
<dict>
|
||||
<key>0</key>
|
21
tslint.json
|
@ -1,15 +1,10 @@
|
|||
{
|
||||
"rules": {
|
||||
"no-string-throw": true,
|
||||
"no-unused-expression": true,
|
||||
"no-duplicate-variable": true,
|
||||
"curly": true,
|
||||
"class-name": true,
|
||||
"semicolon": [
|
||||
true,
|
||||
"always"
|
||||
],
|
||||
"triple-equals": true
|
||||
},
|
||||
"defaultSeverity": "warning"
|
||||
"extends": "tslint:recommended",
|
||||
"rules": {
|
||||
"variable-name": [true, "ban-keywords", "check-format"],
|
||||
"interface-name": [true, "never-prefix"],
|
||||
"max-line-length": [true, 120],
|
||||
"no-empty": false,
|
||||
"object-literal-sort-keys": false
|
||||
}
|
||||
}
|
||||
|
|