diff --git a/media/MetadataViewer/index.html b/media/MetadataViewer/index.html
new file mode 100644
index 00000000..342d0fae
--- /dev/null
+++ b/media/MetadataViewer/index.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
diff --git a/media/MetadataViewer/index.js b/media/MetadataViewer/index.js
new file mode 100644
index 00000000..9046b49c
--- /dev/null
+++ b/media/MetadataViewer/index.js
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+let currentConfigType = null;
+
+this.window.addEventListener('message', (event) => {
+ const message = event.data;
+ switch (message.command) {
+ case 'showMetadata':
+ updateMetadataInfo(message.metadata);
+ break;
+ default:
+ break;
+ }
+});
+
+// Update metadata information
+function updateMetadataInfo(metadata) {
+ const mainViewItemBox = document.createElement('div');
+ mainViewItemBox.classList.add('main-view-item-box');
+
+ const viewItemBox = document.createElement('div');
+ viewItemBox.classList.add('view-item-box');
+ viewItemBox.setAttribute('id', 'common-view-item-box');
+
+ const viewItemHeaderBox = document.createElement('div');
+ viewItemHeaderBox.classList.add('view-item-header-box');
+
+ const viewItemHeader = document.createElement('div');
+ viewItemHeader.classList.add('view-item-header');
+ viewItemHeader.innerText = 'Common';
+
+ const viewItemShowButton = document.createElement('div');
+ viewItemShowButton.setAttribute('id', 'common-view-item-show-button');
+ viewItemShowButton.classList.add('view-item-show-button', 'codicon-collapse-all', 'codicon');
+
+ const viewItemContentBox = document.createElement('div');
+ viewItemContentBox.setAttribute('id', 'common-view-content-box');
+ viewItemContentBox.classList.add('view-item-content-box');
+
+ document.body.appendChild(mainViewItemBox);
+ mainViewItemBox.appendChild(viewItemBox);
+ viewItemBox.append(viewItemHeaderBox, viewItemContentBox);
+ viewItemHeaderBox.append(viewItemHeader, viewItemShowButton);
+
+ // Pull out the main key.(File Name)
+ const mainFileName = Object.keys(metadata)[0];
+
+ // Store metadata information in variable
+ const metadataInfo = metadata[mainFileName];
+
+ for (const subKey in metadataInfo) {
+ if (subKey === 'operations') {
+ const viewItemBox = document.createElement('div');
+ viewItemBox.setAttribute('id', 'operations-view-item-box');
+ viewItemBox.classList.add('view-item-box');
+
+ // draw header
+ const viewItemHeader = document.createElement('div');
+ viewItemHeader.classList.add('view-item-header');
+ viewItemHeader.innerText = 'Operations';
+
+ // add show button image
+ const showButton = document.createElement('div');
+ showButton.classList.add('view-item-show-button', 'codicon-collapse-all', 'codicon');
+ showButton.setAttribute('id', 'operations-view-item-show-button');
+
+ // draw a header box to hold headers and showButton
+ const viewItemHeaderBox = document.createElement('div');
+ viewItemHeaderBox.classList.add('view-item-header-box');
+
+ viewItemHeaderBox.append(viewItemHeader, showButton);
+ viewItemBox.appendChild(viewItemHeaderBox);
+ mainViewItemBox.appendChild(viewItemBox);
+
+ // draw content box
+ const viewItemContentBox = document.createElement('div');
+ viewItemContentBox.classList.add('view-item-content-box');
+ viewItemContentBox.setAttribute('id', 'operations-view-content-box');
+ viewItemBox.appendChild(viewItemContentBox);
+
+
+ for (const operationsKey in metadataInfo[subKey]) {
+ metadataDivCreate(operationsKey, metadataInfo[subKey][operationsKey], 'operations');
+ }
+ } else if (subKey === 'cfg-settings') {
+ const viewItemBox = document.createElement('div');
+ viewItemBox.setAttribute('id', 'cfg-settings-view-item-box');
+ viewItemBox.classList.add('view-item-box');
+
+ const viewItemHeaderBox = document.createElement('div');
+ viewItemHeaderBox.classList.add('view-item-header-box');
+
+ const viewItemHeader = document.createElement('div');
+ viewItemHeader.classList.add('view-item-header');
+ viewItemHeader.innerText = 'Config';
+
+ const showButton = document.createElement('div');
+ showButton.setAttribute('id', 'cfg-settings-view-item-show-button');
+ showButton.classList.add('view-item-show-button', 'codicon-collapse-all', 'codicon');
+ // showButton.innerText = '-';
+
+ viewItemHeaderBox.append(viewItemHeader, showButton);
+ viewItemBox.appendChild(viewItemHeaderBox);
+ mainViewItemBox.appendChild(viewItemBox);
+
+ const viewItemContentBox = document.createElement('div');
+ viewItemContentBox.classList.add('view-item-content-box');
+ viewItemContentBox.setAttribute('id', 'cfg-settings-view-content-box');
+ viewItemBox.appendChild(viewItemContentBox);
+
+ const oneccInfo = metadataInfo[subKey];
+
+ // Store values that onecc`s value is true
+ const oneccInfoList = [];
+
+ for (const configKey in oneccInfo) {
+ const viewItemSubHeaderBox = document.createElement('div');
+ viewItemSubHeaderBox.classList.add('view-item-header-box');
+
+ const viewItemSubHeader = document.createElement('div');
+ viewItemSubHeader.innerText = `[${configKey}]`;
+ viewItemSubHeader.classList.add('view-item-sub-header');
+
+ const showButton = document.createElement('div');
+ showButton.innerText = `-`;
+ showButton.classList.add('view-item-show-button');
+ showButton.style.fontSize = '20px';
+ showButton.setAttribute('id', `${configKey}-view-item-show-button`);
+
+ viewItemSubHeaderBox.append(viewItemSubHeader, showButton);
+ viewItemContentBox.appendChild(viewItemSubHeaderBox);
+
+ const subContentBox = document.createElement('div');
+ subContentBox.classList.add('sub-view-item-content-box');
+ subContentBox.setAttribute('id', `${configKey}-sub-view-content-box`);
+
+ viewItemContentBox.appendChild(subContentBox);
+
+ // Save the current key to use as id
+ currentConfigType = configKey;
+
+ if (configKey === 'onecc') {
+ // Handle if no onecc information is available
+ const viewItemContent = document.createElement('div');
+ viewItemContent.innerText = 'There is no config information...';
+ viewItemBox.appendChild(viewItemContent);
+ viewItemContent.classList.add('view-item-info-content');
+
+ for (const oneccSubkey in oneccInfo[configKey]) {
+ if (oneccInfo[configKey][oneccSubkey]) {
+ oneccInfoList.push(oneccSubkey);
+
+ // If you have cfg information, delete the no info statement
+ viewItemContent ?.remove();
+ metadataDivCreate(oneccSubkey, oneccInfo[configKey][oneccSubkey], 'cfg-settings');
+ }
+ }
+ } else {
+ if (oneccInfoList.includes(configKey)) {
+ viewItemSubHeader.style.marginTop = '15px';
+ for (const oneccSubkey in oneccInfo[configKey]) {
+ metadataDivCreate(oneccSubkey, oneccInfo[configKey][oneccSubkey], 'cfg-settings');
+ }
+
+ } else {
+ viewItemSubHeader ?.remove();
+ }
+ }
+ }
+ } else {
+ // Other common metadata information
+ metadataDivCreate(subKey, metadataInfo[subKey], 'common');
+ }
+ }
+ showButtonClickEvent();
+}
+
+function metadataDivCreate(subKey, value, type) {
+ let viewItemContentBox = null;
+ let subContentBox = null;
+ if (type === 'common') {
+ viewItemContentBox = document.getElementById('common-view-content-box');
+ } else if (type === 'operations') {
+ viewItemContentBox = document.getElementById('operations-view-content-box');
+ } else if (type === 'cfg-settings') {
+ viewItemContentBox = document.getElementById('cfg-settings-view-content-box');
+ subContentBox = document.getElementById(`${currentConfigType}-sub-view-content-box`);
+ }
+
+
+ const viewItemContent = document.createElement('div');
+ viewItemContent.classList.add('view-item-content');
+ const viewItemName = document.createElement('div');
+ viewItemName.classList.add('view-item-name');
+ viewItemName.innerText = subKey;
+
+ // If the value of the key is the object structure again, turn the repetition door.
+ if (typeof value === 'object' && value !== null) {
+ const viewItemValueList = document.createElement('div');
+ viewItemValueList.classList.add('view-item-value-list');
+
+ for (const key in value) {
+ const viewItemValue = document.createElement('div');
+ viewItemValue.classList.add('view-item-value');
+ viewItemValue.innerText = `${value[key]}`;
+ // if the size of the viewer is smaller, Set items to be smaller in proportion
+ viewItemValue.style.width = 'auto';
+ viewItemValue.classList.add('margin-bottom-border-thin-gray');
+ viewItemValueList.appendChild(viewItemValue);
+ }
+
+ viewItemContent.append(viewItemName, viewItemValueList);
+ viewItemContent.classList.add('aline-items-baseline');
+
+ } else {
+ // If it's a simple string, it's shown on the screen right away.
+ const viewItemValue = document.createElement('div');
+ viewItemValue.classList.add('view-item-value');
+ viewItemValue.innerText = value;
+ viewItemContent.append(viewItemName, viewItemValue);
+ }
+
+ if (type === 'cfg-settings') {
+ viewItemContent.style.marginBottom = '1px';
+ subContentBox.appendChild(viewItemContent);
+
+ } else {
+ viewItemContentBox.appendChild(viewItemContent);
+ }
+}
+
+function showButtonClickEvent() {
+ const showButtons = document.getElementsByClassName('view-item-show-button');
+ let isSubButton = false;
+
+ for (let index = 0; index < showButtons.length; index++) {
+ const button = showButtons[index];
+ const id = button.getAttribute('id');
+
+ button.addEventListener('click', () => {
+ let contentBox = document.getElementById(`${id.split('-view')[0]}-view-content-box`);
+ isSubButton = false;
+ if (!contentBox) {
+ contentBox = document.getElementById(`${id.split('-view')[0]}-sub-view-content-box`);
+ isSubButton = true;
+ }
+
+ if(contentBox?.style.display === 'block' || contentBox.style.display === ''){
+ isSubButton ? (() => {
+ button.innerText = '+';
+ button.style.fontSize = '14px';
+ })() :
+ (() => {
+ button.classList.remove('codicon-collapse-all');
+ button.classList.add('codicon-unfold');
+ })();
+ contentBox.style.display = 'none';
+ } else {
+ isSubButton ? (() => {
+ button.innerText = '-';
+ button.style.fontSize = '20px';
+ })() :
+ (() => {
+ button.classList.remove('codicon-unfold');
+ button.classList.add('codicon-collapse-all');
+ })();
+ contentBox.style.display = 'block';
+ }
+ });
+ }
+}
diff --git a/media/MetadataViewer/style.css b/media/MetadataViewer/style.css
new file mode 100644
index 00000000..b7da4e9d
--- /dev/null
+++ b/media/MetadataViewer/style.css
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ body {
+ background-color: #1e1e1e;
+ max-width: 800px;
+ margin-left: 20px;
+}
+
+/* white테마일 때 처리 */
+body.vscode-light {
+ background-color: #e7e7e7;
+}
+
+body.vscode-light .view-item-header {
+ color: #444444;
+}
+
+body.vscode-light .view-item-name {
+ color: #616161;
+}
+
+body.vscode-light .view-item-sub-header {
+ color: #a31515;
+}
+
+body.vscode-light .view-item-value {
+ color: #444444;
+ background-color: white;
+}
+
+.view-item-header {
+ padding: 10px;
+ margin-top: 20px;
+ margin-bottom: 10px;
+ font-size: 20px;
+ font-weight: bold;
+ font-family: sans-serif;
+ color: #e7e7e7;
+}
+
+.view-item-content {
+ display: flex;
+ align-items: center;
+ justify-content: end;
+ margin-bottom: 10px;
+}
+
+
+.view-item-value {
+ margin-left: 10px;
+ background-color: #252526;
+ padding: 10px;
+ width: 650px;
+ border-radius: 3px;
+ font-weight: bold;
+}
+
+.view-item-value-list {
+ width: 680px;
+}
+
+.view-item-name {
+ color: #cccccc;
+ min-width: 100px;
+}
+
+.view-item-content {
+ color:#e7e7e7;
+}
+
+.view-item-sub-header {
+ margin-left: 10px;
+ font-size: 16px;
+ color: #d7ba7d;
+ margin-bottom: 10px;
+ font-weight: bold;
+}
+
+.view-item-info-content {
+ font-size: 20px;
+ margin-left: 20px;
+ margin-top: 10px;
+}
+
+.view-item-header-box {
+ display: flex;
+ align-items: baseline;
+ justify-content: space-between;
+}
+
+.view-item-show-button {
+ font-size: 30px;
+ cursor: pointer;
+}
+
+.view-item-show-button:hover {
+ color: gray;
+}
+
+#cfg-settings-view-item-box {
+ margin-bottom: 30px;
+}
+
+.view-item-content-box {
+ display: block;
+}
+
+.aline-items-baseline {
+ align-items: baseline;
+}
+
+.margin-bottom-border-thin-gray {
+ box-sizing: border-box;
+ border-bottom: solid rgba(0, 0, 0, 0.20);
+ border-width: 0.1px;
+}
diff --git a/media/RelationViewer/index.html b/media/RelationViewer/index.html
new file mode 100644
index 00000000..d167be2c
--- /dev/null
+++ b/media/RelationViewer/index.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Document
+
+
+
+
+
+
diff --git a/media/RelationViewer/index.js b/media/RelationViewer/index.js
new file mode 100644
index 00000000..48b7e378
--- /dev/null
+++ b/media/RelationViewer/index.js
@@ -0,0 +1,968 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// prevent right-clicks in a Web view
+document.addEventListener('contextmenu', event => event.preventDefault());
+
+const vscode = acquireVsCodeApi();
+// history saved List
+let historyList = vscode.getState() ?.historyList || [];
+
+// Save information for currently open files
+let currentFileInfo = null;
+
+// Save node path to the current context menu
+let selectedNodePath = null;
+
+window.addEventListener('message', (event) => {
+ const message = event.data;
+ const selected = message.payload['selected'];
+ const relationData = message.payload['relation-data'];
+
+ getSelectedFileInfo(selected, relationData);
+
+ switch (message.type) {
+ case 'create':
+ detachTree();
+ pushCurrentFileInfoObject(historyList);
+ attachTree(relationData);
+ console.log('create 메시지는: ', message);
+ break;
+ case 'update':
+ console.log('update 메시지는: ', message);
+ detachTree();
+ historyList = message.historyList;
+ pushCurrentFileInfoObject(historyList);
+ attachTree(relationData);
+ historyMainBoxDisplay(message.type, message.isOpenHistoryBox);
+ vscode.setState({historyList: historyList});
+ break;
+ case 'history':
+ console.log('history 메시지는: ', message);
+ detachTree();
+ attachTree(relationData);
+ historyList = message.historyList;
+ break;
+ default:
+ break;
+ }
+});
+
+// save node
+let node;
+
+function attachTree(relationData) {
+ const wheelInfoBox = document.createElement('div');
+ wheelInfoBox.innerText = '* (Ctrl or Shift) + Wheel : Zoom In, Zoom Out';
+ wheelInfoBox.classList.add('wheel-info-box');
+ document.body.appendChild(wheelInfoBox);
+
+ // assigns the data to a hierarchy using parent-child relationships
+ const treeData = d3.stratify().id(d => d.id).parentId(d => d.parent)(relationData);
+
+ let historyDivWidth = screen.width * 0.11;
+ const rectSizeWidth = 130;
+ const rectSizeHeight = 50;
+
+ const [maxWidthCount, maxHeightCount] = countMaxDataNum(relationData);
+
+ // set the dimensions and margins of the diagram
+ const margin = {top: 70, right: 0, bottom: 60, left: 0};
+ let width = (maxWidthCount + 5) * rectSizeWidth - margin.left - margin.right;
+ width = (width < screen.width * 0.7) ? screen.width * 0.7 : width;
+ let height = screen.height * 0.6 - margin.top - margin.bottom;
+ height = (rectSizeHeight * maxHeightCount < height) ? height : rectSizeHeight * maxHeightCount;
+ // declares a tree layout and assigns the size
+ const treemap = d3.tree().size([width, height]);
+
+ // maps the node data to the tree layout
+ const nodes = treemap(treeData);
+
+ // Main Box Create
+ const relationBox = document.createElement('div');
+ relationBox.classList.add('relation-box');
+ relationBox.setAttribute('id', 'relation-box');
+ relationBox.style.width = `${historyDivWidth + width}px`;
+
+ document.body.appendChild(relationBox);
+
+ // draw contextMenuBox
+ const contextMenuBox = document.createElement('div');
+ contextMenuBox.setAttribute('id', 'context-menu-box');
+ contextMenuBox.classList.add('plus-button-context-box');
+ contextMenuBox.style.width = `${width + historyDivWidth}px`;
+
+
+ // draw contextMenu
+ const contextMenu = document.createElement('div');
+ contextMenu.setAttribute('id', 'context-menu');
+ contextMenu.classList.add('context-menu');
+
+ document.body.append(contextMenu, contextMenuBox);
+
+ const contextMenuList = ['Show Metadata of this file', 'Open this file'];
+
+ for (let index = 0; index < contextMenuList.length; index++) {
+ // add menu a dividing line
+ if (index >= 1) {
+ const contextMenuInfoLine = document.createElement('div');
+ contextMenuInfoLine.classList.add('context-menu-line');
+ contextMenu.appendChild(contextMenuInfoLine);
+ }
+
+ const element = contextMenuList[index];
+ const contextMenuInfo = document.createElement('div');
+ contextMenuInfo.classList.add('context-menu-info');
+ contextMenuInfo.innerText = element;
+ contextMenu.appendChild(contextMenuInfo);
+
+ contextMenuInfo.addEventListener('click', () => {
+ contextMenuBox.style.display = 'none';
+ contextMenu.style.display = 'none';
+ switch (element) {
+ case 'Show Metadata of this file':
+ postMessage('showMetadata', {path: selectedNodePath});
+ break;
+ case 'Open this file':
+ postMessage('openFile', {path: selectedNodePath});
+ break;
+ default:
+ break;
+ }
+ });
+ }
+ // append left, right click
+ contextMenuBox.addEventListener('click', () => {
+ contextMenuBox.style.display = 'none';
+ contextMenu.style.display = 'none';
+ });
+ contextMenuBox.addEventListener('contextmenu', () => {
+ contextMenuBox.style.display = 'none';
+ contextMenu.style.display = 'none';
+ });
+
+ // append the svg obgect to the body of the page
+ // appends a 'group' element to 'svg'
+ // moves the 'group' element to the top left margin
+ const svg =
+ d3.select('.relation-box')
+ .append('svg')
+ .attr('preserveAspectRatio', 'xMidYMid meet')
+ .attr(
+ 'viewBox',
+ `0 0 ${width + margin.left + margin.right} ${height + margin.top + margin.bottom}`)
+ .attr('width', width + margin.left + margin.right)
+ .attr('height', height + margin.top + margin.bottom)
+ .style('background-color', '#545454'),
+ g = svg.append('g').attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+ // adds the links between the nodes
+ g.selectAll('.link')
+ .data(nodes.descendants().slice(1))
+ .enter()
+ .append('path')
+ .attr('class', 'link')
+ .attr('d', function(d) {
+ return 'M' + d.x + ',' + d.y + 'C' + d.x + ',' + (d.y + d.parent.y) / 2 + ' ' + d.parent.x +
+ ',' + (d.y + d.parent.y) / 2 + ' ' + d.parent.x + ',' + d.parent.y;
+ });
+
+ // adds each node as a group
+ node = g.selectAll('.node')
+ .data(nodes.descendants())
+ .enter()
+ .append('g')
+ .attr('class', d => 'node' + (d.children ? ' node--internal' : ' node--leaf'))
+ .attr('transform', d => 'translate(' + d.x + ',' + d.y + ')');
+
+ let waitForDouble = null;
+
+ // adds the rectangle to the node
+ drawRect('extension',rectSizeWidth,rectSizeHeight,waitForDouble);
+ drawRect('fileName',rectSizeWidth,rectSizeHeight,waitForDouble);
+ drawRect('versionInfo',rectSizeWidth,rectSizeHeight,waitForDouble);
+
+ // Create hover text_div to show File path
+ const hoverText = document.createElement('div');
+ document.body.appendChild(hoverText);
+ hoverText.classList.add('hover-text');
+
+ // draw node`s extension
+ setDrawInfoInNode('extension', node, rectSizeHeight, waitForDouble);
+ // draw node`s fileName
+ setDrawInfoInNode('name', node, rectSizeHeight, waitForDouble);
+
+ // draw version info
+ setDrawInfoInNode('toolchainVersion', node, rectSizeHeight, waitForDouble);
+ setDrawInfoInNode('oneccVersion', node, rectSizeHeight), waitForDouble;
+
+ // add a plus button
+ node._groups[0].forEach((g) => {
+ g.setAttribute('id', `${g.__data__.data.id}-g`);
+
+ if (g.__data__.data['data-list'].length >= 2) {
+ // draw plus_button_ctext_menu
+ const plusButtonContextMenu = document.createElement('div');
+ plusButtonContextMenu.classList.add('plus-button-context-menu');
+ plusButtonContextMenu.setAttribute('id', `${g.__data__.data.id}-plus-button-context-menu`);
+ document.body.appendChild(plusButtonContextMenu);
+
+ // draw plus_button_context_menu_header
+ const plusButtonContextMenuHeader = document.createElement('div');
+ plusButtonContextMenuHeader.classList.add('plus-button-context-menu-header');
+ plusButtonContextMenuHeader.innerText = 'Same Content File List';
+ plusButtonContextMenu.appendChild(plusButtonContextMenuHeader);
+
+ // Add data list information to the context menu
+ const dataList = g.__data__.data['data-list'];
+
+ for (let idx = 0; idx < dataList.length; idx++) {
+ if (idx !== g.__data__.data['represent-idx']) {
+ const data = dataList[idx];
+ for (const key in data) {
+ if (key === 'name') {
+ // add a dividing line
+ const plusButtonContextMenuLine = document.createElement('div');
+ plusButtonContextMenuLine.classList.add('context-menu-line');
+ plusButtonContextMenu.appendChild(plusButtonContextMenuLine);
+
+ // draw plus_button_context_menu_info
+ const plusButtonContextMenuInfo = document.createElement('div');
+ plusButtonContextMenuInfo.innerText = data[key];
+ plusButtonContextMenuInfo.classList.add('plus-button-context-menu-info');
+ plusButtonContextMenu.appendChild(plusButtonContextMenuInfo);
+
+ plusButtonContextMenuInfo.addEventListener('click', () => {
+ postMessage('update', {path: dataList[idx]['path'], historyList: historyList, isOpenHistoryBox:
+ document.getElementsByClassName('history-main-box')[0].style.display});
+ // hide context_menu
+ hidePlusButtonContextMenu(plusButtonContextBox);
+ });
+ }
+ }
+ }
+ }
+ }
+
+ // When there are two versions of the information,
+ // the height of the rect tag is increased.
+ let versionInfoCount = 0;
+ const nodeData = g.__data__.data['data-list'][g.__data__.data['represent-idx']];
+ for (const key in nodeData) {
+ if (key === 'onecc-version' || key === 'toolchain-version') {
+ versionInfoCount += 1;
+ }
+ }
+
+ // If there is no version information, sort the file name in the middle
+ const versionInfoRect = g.childNodes[2];
+ if (versionInfoCount === 0) {
+ const nameInfoRect = g.childNodes[1];
+ versionInfoRect.remove();
+
+ nameInfoRect.style.rx = 3;
+ } else if (versionInfoCount === 1){
+ versionInfoRect.style.height = `20px`;
+ }
+ });
+
+ // draw plusButtonContextBox
+ const plusButtonContextBox = document.createElement('div');
+ plusButtonContextBox.classList.add('plus-button-context-box');
+ plusButtonContextBox.setAttribute('id', `plus-button-context-box`);
+ plusButtonContextBox.style.width = `100%`;
+ document.body.appendChild(plusButtonContextBox);
+
+ // add hide_plus_button_Event
+ plusButtonContextBox.addEventListener('click', () => {
+ // hide context_menu
+ hidePlusButtonContextMenu(plusButtonContextBox);
+ });
+
+ // draw plus_button and minus_button
+ plusMinusButtonCreate('minus', rectSizeWidth, rectSizeHeight);
+ plusMinusButtonCreate('plus', rectSizeWidth, rectSizeHeight);
+
+ // add mouse drag and mouse wheel event
+ relationBox.addEventListener('mousedown', (e) => _mouseDownHandler(e));
+ relationBox.addEventListener('mousewheel', (e) => _wheelHandler(e), {passive: false});
+
+ const historyOpenDiv = document.createElement('div');
+ historyOpenDiv.style.width = `${historyDivWidth}px`;
+ historyOpenDiv.classList.add('history-open-box');
+ relationBox.appendChild(historyOpenDiv);
+
+ const historyOpenDivHeader = document.createElement('div');
+ historyOpenDivHeader.classList.add('plus-button-context-menu-header');
+ historyOpenDivHeader.innerText = 'History';
+ historyOpenDivHeader.style.fontSize = '20px';
+
+ const historyOpenDivHeaderOpenButton = document.createElement('div');
+ historyOpenDivHeaderOpenButton.classList.add(
+ 'history-open-button', 'codicon', 'codicon-fold-down');
+
+ historyOpenDivHeaderOpenButton.addEventListener('click', () => {
+ document.getElementsByClassName('history-main-box')[0].style.display = 'block';
+ historyOpenDiv.style.display = 'none';
+ });
+
+ historyOpenDivHeaderOpenButton.addEventListener('mouseover', () => {
+ const historyOpenButtonRect = historyOpenDivHeaderOpenButton.getBoundingClientRect();
+ hoverText.innerText = `"History Open"`;
+ hoverText.style.top = `${historyOpenButtonRect.top - historyOpenButtonRect.height - 12}px`;
+ hoverText.style.left = `${historyOpenButtonRect.left - historyOpenButtonRect.width - 20}px`;
+ hoverText.style.display = 'block';
+ hoverText.classList.add('font-bold');
+ });
+ historyOpenDivHeaderOpenButton.addEventListener('mouseout', () => {
+ hoverText.style.display = 'none';
+ hoverText.classList.remove('font-bold');
+ });
+
+ historyOpenDiv.append(historyOpenDivHeaderOpenButton, historyOpenDivHeader);
+
+ // draw hisory_area
+ const historyDiv = document.createElement('div');
+ historyDiv.style.width = `${historyDivWidth}px`;
+ historyDiv.style.height =
+ `${document.getElementsByTagName('svg')[0].getBoundingClientRect().height - 100}px`;
+ historyDiv.classList.add('history-main-box');
+ relationBox.appendChild(historyDiv);
+
+ // draw history_box
+ const historyDivHeaderBox = document.createElement('div');
+ historyDivHeaderBox.classList.add('plus-button-context-menu-header-box');
+ historyDiv.appendChild(historyDivHeaderBox);
+ // draw history_header
+ const historyDivHeader = document.createElement('div');
+ historyDivHeader.classList.add('plus-button-context-menu-header');
+ historyDivHeader.innerText = 'History';
+ historyDivHeader.style.fontSize = '20px';
+ // add clear button
+ const historyDivHeaderClearButton = document.createElement('div');
+ historyDivHeaderClearButton.classList.add(
+ 'plus-button-context-menu-header-clear-button', 'codicon', 'codicon-clear-all');
+
+ historyDivHeaderClearButton.addEventListener('click', () => {
+ historyList = [historyList[0]];
+ vscode.setState({historyList: historyList});
+
+ const historyDivChildNodes = [...historyDiv.childNodes];
+ for (let index = 0; index < historyDivChildNodes.length; index++) {
+ const element = historyDivChildNodes[index];
+ if (index >= 3) {
+ element.remove();
+ }
+ }
+ });
+
+ historyDivHeaderClearButton.addEventListener('mouseover', () => {
+ const historyClearButtonRect = historyDivHeaderClearButton.getBoundingClientRect();
+ hoverText.innerText = `"Clear history"`;
+ hoverText.style.top = `${historyClearButtonRect.top - historyClearButtonRect.height - 12}px`;
+ hoverText.style.left = `${historyClearButtonRect.left - historyClearButtonRect.width - 37}px`;
+ hoverText.style.display = 'block';
+ hoverText.classList.add('font-bold');
+ });
+
+ historyDivHeaderClearButton.addEventListener('mouseout', () => {
+ hoverText.style.display = 'none';
+ hoverText.classList.remove('font-bold');
+ });
+
+ const historyDivHeaderCloseButton = document.createElement('div');
+ historyDivHeaderCloseButton.classList.add(
+ 'history-close-button', 'codicon', 'codicon-fold-up',
+ 'plus-button-context-menu-header-clear-button');
+
+ historyDivHeaderCloseButton.addEventListener('click', () => {
+ document.getElementsByClassName('history-main-box')[0].style.display = 'none';
+ historyOpenDiv.style.display = 'flex';
+ });
+
+ historyDivHeaderCloseButton.addEventListener('mouseover', () => {
+ const historyCloseButtonRect = historyDivHeaderCloseButton.getBoundingClientRect();
+ hoverText.innerText = `"History Close"`;
+ hoverText.style.top = `${historyCloseButtonRect.top - historyCloseButtonRect.height - 12}px`;
+ hoverText.style.left = `${historyCloseButtonRect.left - historyCloseButtonRect.width - 20}px`;
+ hoverText.style.display = 'block';
+ hoverText.classList.add('font-bold');
+ });
+ historyDivHeaderCloseButton.addEventListener('mouseout', () => {
+ hoverText.style.display = 'none';
+ hoverText.classList.remove('font-bold');
+ });
+
+ historyDivHeaderBox.append(
+ historyDivHeaderCloseButton, historyDivHeader, historyDivHeaderClearButton);
+
+ // append history info
+ for (let index = 0; index < historyList.length; index++) {
+ // draw a dividing line
+ const historyInfoLine = document.createElement('div');
+ historyInfoLine.classList.add('context-menu-line');
+
+ const element = historyList[index];
+ const historyDivInfo = document.createElement('div');
+
+ let historyFileName = element.name;
+ // limit file_length
+ if (historyFileName.length > 35) {
+ historyFileName = historyFileName.substring(0, 35) + '...';
+ }
+ historyDivInfo.innerText = historyFileName;
+ historyDivInfo.id = element.path;
+ historyDivInfo.classList.add('plus-button-context-menu-info');
+
+ historyDivInfo.addEventListener('click', () => {
+ postMessage('history', {path: historyDivInfo.id, historyList: historyList});
+ });
+
+ if (index === 0) {
+ historyDivInfo.classList.add('current-history-color');
+ }
+ historyDiv.append(historyInfoLine, historyDivInfo);
+ }
+
+ // focus screen on the current_node
+ const currentNodeRect =
+ document.getElementsByClassName('current-node')[0].getBoundingClientRect();
+ const relationBoxRect =
+ document.getElementsByClassName('relation-box')[0].getBoundingClientRect();
+
+ relationBox.scrollTo({
+ left: currentNodeRect.left - relationBoxRect.width / 2 + currentNodeRect.width / 2,
+ top: currentNodeRect.top - relationBoxRect.height / 2 + currentNodeRect.height / 2,
+ behavior: 'auto'
+ });
+}
+
+function pushCurrentFileInfoObject(historyList) {
+ const currentFileInfoObject = {
+ name: currentFileInfo['data-list'][currentFileInfo['represent-idx']].name,
+ path: currentFileInfo['data-list'][currentFileInfo['represent-idx']].path
+ };
+ historyList.unshift(currentFileInfoObject);
+}
+let _zoom = 1;
+function _wheelHandler(e) {
+ if (e.shiftKey || e.ctrlKey) {
+ const delta =
+ -e.deltaY * (e.deltaMode === 1 ? 0.05 : e.deltaMode ? 1 : 0.002) * (e.ctrlKey ? 10 : 1);
+ _updateZoom(_zoom * Math.pow(2, delta), e);
+ e.preventDefault();
+ } else {
+ const container = document.getElementsByClassName('relation-box')[0];
+ _scrollLeft = container.scrollLeft;
+ _scrollTop = container.scrollTop;
+ }
+}
+
+
+let _scrollLeft = 0;
+let _scrollTop = 0;
+let _width = 0;
+let _height = 0;
+function _updateZoom(zoom, e) {
+ const canvas = document.getElementsByTagName('svg')[0];
+ _width = _width || canvas.width.animVal.value;
+ _height = _height || canvas.height.animVal.value;
+
+ const container = document.getElementsByClassName('relation-box')[0];
+
+ const limit = container.clientHeight / _height;
+ const min = Math.min(Math.max(limit, 0.15), 1);
+ zoom = Math.max(min, Math.min(zoom, 1.4));
+ const width = zoom * _width;
+ const height = zoom * _height;
+
+ canvas.style.width = width + 'px';
+ canvas.style.height = height + 'px';
+ const scrollLeft = container.scrollLeft || _scrollLeft;
+ const scrollTop = container.scrollTop || _scrollTop;
+ const x = e.pageX + scrollLeft;
+ const y = e.pageY + scrollTop;
+ _scrollLeft = Math.max(0, ((x * zoom) / _zoom) - (x - scrollLeft));
+ _scrollTop = Math.max(0, ((y * zoom) / _zoom) - (y - scrollTop));
+
+ container.scrollLeft = _scrollLeft;
+ container.scrollTop = _scrollTop;
+ _zoom = zoom;
+}
+
+
+function _mouseDownHandler(e) {
+ if (e.buttons === 1) {
+ const html = document.getElementsByTagName('html')[0];
+ html.style.cursor = 'grabbing';
+ const container = document.getElementsByClassName('relation-box')[0];
+ let _mousePosition =
+ {left: container.scrollLeft, top: container.scrollTop, x: e.clientX, y: e.clientY};
+ e.stopImmediatePropagation();
+
+ const mouseMoveHandler = (e) => {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ const dx = e.clientX - _mousePosition.x;
+ const dy = e.clientY - _mousePosition.y;
+ _mousePosition.moved = dx * dx + dy * dy > 0;
+
+ if (_mousePosition.moved) {
+ container.scrollLeft = _mousePosition.left - dx;
+ container.scrollTop = _mousePosition.top - dy;
+ _scrollLeft = container.scrollLeft;
+ _scrollTop = container.scrollTop;
+ }
+ };
+
+ const mouseUpHandler = () => {
+ html.style.cursor = null;
+ container.removeEventListener('mouseup', mouseUpHandler);
+ container.removeEventListener('mouseleave', mouseUpHandler);
+ container.removeEventListener('mousemove', mouseMoveHandler);
+ if (_mousePosition && _mousePosition.moved) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ _mousePosition = null;
+ }
+ };
+ container.addEventListener('mousemove', mouseMoveHandler);
+ container.addEventListener('mouseup', mouseUpHandler);
+ container.addEventListener('mouseleave', mouseUpHandler);
+ }
+}
+
+function postMessage(type, object) {
+ vscode.postMessage({type: type, ...object});
+}
+
+function detachTree() {
+ d3.select('svg') ?.remove();
+
+ while (document.body.hasChildNodes()) {
+ document.body.removeChild(document.body.firstChild);
+ }
+}
+
+function setDrawInfoInNode(type, node, rectSizeHeight, waitForDouble) {
+ let currentRect = '';
+
+ node.append('text')
+ .attr('x', -60)
+ .attr(
+ 'class',
+ d => {
+ let classList = '';
+ if (type === 'extension') {
+ classList += 'file-extension-custom file-extension ';
+ } else if (
+ type === 'name' && d.data['data-list'][d.data['represent-idx']]['is-deleted']) {
+ classList += 'deleted-text-decoration ';
+ } else if (type === 'name') {
+ classList += 'text-file-name';
+ }
+ return classList;
+ })
+ .attr(
+ 'y',
+ d => {
+ if (type === 'oneccVersion') {
+ return -rectSizeHeight + 75;
+ } else if (type === 'toolchainVersion') {
+ return -rectSizeHeight + 63;
+ } else if (type === 'extension') {
+ return -rectSizeHeight + 15;
+ } else if (type === 'name') {
+ const oneccVersion = d.data['data-list'][d.data['represent-idx']]['onecc-version'];
+ const toolchainVersion =
+ d.data['data-list'][d.data['represent-idx']]['toolchain-version'];
+
+ if (oneccVersion || toolchainVersion) {
+ return -rectSizeHeight + 42;
+ } else {
+ return -rectSizeHeight + 42;
+ }
+ }
+ })
+ .text(d => {
+ let versionInfo = '';
+ if (type === 'oneccVersion' &&
+ d.data['data-list'][d.data['represent-idx']]['onecc-version']) {
+ versionInfo = d.data['data-list'][d.data['represent-idx']]['onecc-version'];
+ versionInfo = `onecc-version: ${versionInfo}`;
+ } else if (
+ type === 'toolchainVersion' &&
+ d.data['data-list'][d.data['represent-idx']]['toolchain-version']) {
+ versionInfo = d.data['data-list'][d.data['represent-idx']]['toolchain-version'];
+ versionInfo = `toolchain-version: ${versionInfo}`;
+ } else if ( type === 'extension') {
+ versionInfo = d.data['data-list'][d.data['represent-idx']].name.split(
+ '.')[d.data['data-list'][d.data['represent-idx']].name.split('.').length - 1];
+ } else if (type === 'name') {
+ let fullName = d.data['data-list'][d.data['represent-idx']].name;
+ if (fullName.length > 22) {
+ fullName = fullName.substring(0, 22) + '...';
+ }
+
+ if (d.data['data-list'][d.data['represent-idx']]['is-deleted']) {
+ fullName = '(deleted)';
+ }
+ return fullName;
+ }
+ return versionInfo;
+ })
+ .on('dblclick',
+ () => {
+ if (waitForDouble !== null) {
+ clearTimeout(waitForDouble);
+ waitForDouble = null;
+ }
+ })
+ .on('click',
+ (_p, d) => {
+ if (waitForDouble === null) {
+ waitForDouble = setTimeout(() => {
+ postMessage('update', {
+ path: d.data['data-list'][d.data['represent-idx']].path,
+ historyList: historyList,
+ isOpenHistoryBox:
+ document.getElementsByClassName('history-main-box')[0].style.display
+ });
+ waitForDouble = null;
+ }, 300);
+ }
+ })
+ .on('contextmenu',
+ (mouse, d) => {
+ openContextMenu(mouse, d.data['data-list'][d.data['represent-idx']].path);
+ })
+ .on('mouseover',
+ (mouse, node) => {
+ if(type === 'name'){
+ const hoverText = document.getElementsByClassName('hover-text')[0];
+ const fileExtensionText = mouse.path[0].getBoundingClientRect();
+ hoverText.style.display = 'block';
+ hoverText.innerText = `${node.data['data-list'][node.data['represent-idx']].path}`;
+ hoverText.style.left = `${fileExtensionText.x - 8}px`;
+ hoverText.style.top = `${fileExtensionText.y - 24}px`;
+ if (node.data['data-list'][node.data['represent-idx']]['is-deleted']) {
+ hoverText.classList.add('deleted-text-decoration');
+ }
+ }
+
+ currentRect = mouse.fromElement;
+ if(mouse.fromElement?.tagName !== 'rect') {
+ currentRect = mouse.path[1].childNodes[2];
+ }
+ currentRect.classList.add('rect-hover-fill');
+
+ })
+ .on('mouseout', (_mouse, node) => {
+ if(type === 'name'){
+ const hoverText = document.getElementsByClassName('hover-text')[0];
+ hoverText.style.display = 'none';
+ if (node.data['data-list'][node.data['represent-idx']]['is-deleted']) {
+ hoverText.classList.remove('deleted-text-decoration');
+ }
+ }
+
+ currentRect.classList.remove('rect-hover-fill');
+
+ });
+}
+
+function hidePlusButtonContextMenu(plusButtonContextBox) {
+ if (plusButtonContextBox.style.display === 'block') {
+ plusButtonContextBox.style.display = 'none';
+ const plusButtonContextMenus = document.getElementsByClassName('plus-button-context-menu');
+ for (let index = 0; index < plusButtonContextMenus.length; index++) {
+ const element = plusButtonContextMenus[index];
+ element.style.display = 'none';
+ }
+ const plusButtons = document.getElementsByClassName('plus-button');
+ for (let index = 0; index < plusButtons.length; index++) {
+ const element = plusButtons[index];
+ element.style.display = 'block';
+ }
+ const minusButton = document.getElementsByClassName('minus-button');
+ for (let index = 0; index < minusButton.length; index++) {
+ const element = minusButton[index];
+ element.style.display = 'none';
+ }
+ }
+}
+
+function openContextMenu(mouse, path) {
+ const contextMenuBox = document.getElementById('context-menu-box');
+ const contextMenu = document.getElementById('context-menu');
+ const relationBox = document.getElementById('relation-box');
+ const relationBoxRect = relationBox.getBoundingClientRect();
+
+ contextMenuBox.style.display = 'block';
+ contextMenu.style.display = 'block';
+
+ contextMenu.style.top = `${mouse.y}px`;
+ contextMenu.style.left = `${mouse.x}px`;
+
+ const contextMenuRect = contextMenu.getBoundingClientRect();
+
+ // If the right of the context menu is longer than the size of the current container,
+ if (relationBoxRect.right - mouse.x < contextMenuRect.width) {
+ contextMenu.style.left = `${mouse.x - contextMenuRect.width}px`;
+ }
+
+ // If the bottom of the plus menu is outside the screen,
+ if (relationBoxRect.height - mouse.y < contextMenuRect.height) {
+ contextMenu.style.top = `${mouse.y - contextMenuRect.height}px`;
+ }
+
+
+ selectedNodePath = path;
+}
+
+function getSelectedFileInfo(selected, relationData) {
+ for (let idx = 0; idx < relationData.length; idx++) {
+ for (const key in relationData[idx]) {
+ if (key === 'id') {
+ if (relationData[idx]['id'] === selected) {
+ currentFileInfo = relationData[idx];
+ }
+ }
+ }
+ if (!currentFileInfo) {
+ break;
+ }
+ }
+}
+
+function plusMinusButtonCreate(type, rectSizeWidth, rectSizeHeight) {
+ node.append('text')
+ .attr('x', rectSizeWidth / 2 - 14)
+ .attr('y', -rectSizeHeight + 9)
+ .attr(
+ 'class',
+ () => {
+ return type === 'minus' ? 'minus-button' : 'plus-button';
+ })
+ .attr(
+ 'id',
+ node => {
+ return type === 'minus' ? `${node.data.id}-minus-button` :
+ `${node.data.id}-plus-button`;
+ })
+ .text(node => {
+ if (node.data['data-list'].length >= 2) {
+ return type === 'minus' ? `-` : '+';
+ }
+ })
+ .on('click',
+ (mouse, node) => {
+ const minusButton = document.getElementById(`${node.data.id}-minus-button`);
+ const plusButton = document.getElementById(`${node.data.id}-plus-button`);
+ const plusButtonContextBox = document.getElementById(`plus-button-context-box`);
+ const plusButtonContextMenu =
+ document.getElementById(`${node.data.id}-plus-button-context-menu`);
+ const relationBox = document.getElementById(`relation-box`);
+ const relationBoxRect = relationBox.getBoundingClientRect();
+
+ if (type === 'minus') {
+ minusButton.style.display = 'none';
+ plusButton.style.display = 'display';
+ plusButtonContextBox.style.display = 'none';
+ plusButtonContextMenu.style.display = 'none';
+ } else {
+ const plusButtonRect = mouse.path[0].getBoundingClientRect();
+ minusButton.style.display = 'block';
+ plusButton.style.display = 'none';
+ plusButtonContextBox.style.display = 'block';
+ plusButtonContextMenu.style.display = 'block';
+ plusButtonContextMenu.style.top = `${plusButtonRect.y}px`;
+ plusButtonContextMenu.style.left = `${plusButtonRect.right}px`;
+
+ const plusButtonContextMenuWidth =
+ plusButtonContextMenu.getBoundingClientRect().width;
+ const plusButtonContextMenuHeight =
+ plusButtonContextMenu.getBoundingClientRect().height;
+
+ // the area of the plus menu is outside the current screen
+ if (plusButtonRect.right + plusButtonContextMenuWidth >
+ relationBox.getBoundingClientRect().right) {
+ plusButtonContextMenu.style.left =
+ `${plusButtonRect.right - plusButtonContextMenuWidth}px`;
+ }
+ // If the bottom of the plus menu is outside the screen,
+ if (relationBoxRect.height - plusButtonRect.top < plusButtonContextMenuHeight) {
+ plusButtonContextMenu.style.top =
+ `${plusButtonRect.y - plusButtonContextMenuHeight}px`;
+ }
+ }
+ })
+ .on('mouseover',
+ (mouse) => {
+ const plusButton = mouse.path[0].getBoundingClientRect();
+ const hoverText = document.getElementsByClassName('hover-text')[0];
+ hoverText.style.display = 'block';
+ hoverText.innerText = `"Open the same content file list"`;
+ hoverText.style.left = `${plusButton.x - 80}px`;
+ hoverText.style.top = `${plusButton.y - 12}px`;
+ hoverText.classList.add('font-bold');
+
+ const hoverTextRect = hoverText.getBoundingClientRect();
+ const relationBoxRect = document.getElementById('relation-box').getBoundingClientRect();
+ // If the right of the hover text is longer than the size of the current container,
+ if (relationBoxRect.right - plusButton.x < hoverTextRect.width) {
+ hoverText.style.left = `${plusButton.x - hoverTextRect.width + 20}px`;
+ }
+
+ // If the top of the hover text is outside the screen,
+ if (plusButton.y < hoverTextRect.height) {
+ hoverText.style.top = `${plusButton.y + hoverTextRect.height + 10}px`;
+ }
+ })
+ .on('mouseout', () => {
+ const hoverText = document.getElementsByClassName('hover-text')[0];
+ hoverText.style.display = 'none';
+ hoverText.classList.remove('font-bold');
+ });
+}
+
+function countMaxDataNum(relationData) {
+ // d3 graph layer info
+ const d3ClassData = {};
+
+ // d3 graph number of nodes for layer
+ const d3NumData = {};
+
+ // Maximum Number in One Floor
+ let _maxWidthCount = 1;
+ // Total number of floors
+ let _maxHeightCount = 1;
+ for (const key in relationData) {
+ const data = relationData[key];
+
+ if (!d3ClassData[data.parent]) {
+ d3ClassData[data.id] = 1;
+ } else {
+ d3ClassData[data.id] = d3ClassData[data.parent] + 1;
+ }
+
+ d3NumData[d3ClassData[data.id]] =
+ (d3NumData[d3ClassData[data.id]] ? d3NumData[d3ClassData[data.id]] : 0) + 1;
+ if (_maxWidthCount < d3NumData[d3ClassData[data.id]]) {
+ _maxWidthCount = d3NumData[d3ClassData[data.id]];
+ }
+ }
+ _maxHeightCount = Object.keys(d3NumData).length;
+ return [_maxWidthCount, _maxHeightCount];
+}
+
+function historyMainBoxDisplay(type, isOpenHistoryBox) {
+ const historyMainBox = document.getElementsByClassName('history-main-box')[0];
+ const historyOpenBox = document.getElementsByClassName('history-open-box')[0];
+
+ if (type === 'update') {
+ console.log(isOpenHistoryBox);
+ if (isOpenHistoryBox === 'block' || isOpenHistoryBox === '') {
+ historyMainBox.style.display = 'block';
+ historyOpenBox.style.display = 'none';
+ } else {
+ historyMainBox.style.display = 'none';
+ historyOpenBox.style.display = 'flex';
+ }
+ } else {
+ historyMainBox.style.display = 'block';
+ historyOpenBox.style.display = 'none';
+ }
+}
+
+function drawRect(type,rectSizeWidth,rectSizeHeight,waitForDouble) {
+ node.append('rect')
+ .attr('x', -rectSizeWidth / 2)
+ .attr('y',() => {
+ switch (type) {
+ case 'extension':
+ return -rectSizeHeight;
+ case 'fileName':
+ return -rectSizeHeight + 25;
+ case 'versionInfo':
+ return -rectSizeHeight + 25 + 25;
+ default:
+ break;
+ }
+ })
+ .attr('rx',() => {
+ return type === 'extension' ? 3 : null;
+ })
+ .attr('ry',() => {
+ return type === 'versionInfo' ? 3 : null;
+ })
+ .attr('width', rectSizeWidth)
+ .attr('height', type === 'versionInfo' ? 30 : 25)
+ .attr(
+ 'class',
+ d => {
+ let classList = '';
+ if (d.data.id === currentFileInfo.id) {
+ classList += 'current-node ';
+ }
+ if (type === 'fileName'){
+ classList += 'rect-file-name';
+ }
+ return classList;
+ })
+ .style(
+ 'fill',
+ d => {
+ if(type === 'extension'){
+ const nodeData = d.data['data-list'][d.data['represent-idx']];
+ const nodeFileExtension =
+ nodeData['name'].split('.')[nodeData['name'].split('.').length - 1];
+ if (nodeFileExtension === 'log') {
+ return 'rgb(25, 40, 60, 1.0)';
+ } else if (nodeFileExtension === 'circle') {
+ return 'rgba(75, 27, 22, 1.0)';
+ } else {
+ return '#161515';
+ }
+ } else if(type === 'versionInfo'){
+ return "rgb(37 50 37)";
+ }
+ })
+ .on('dblclick',
+ () => {
+ if (waitForDouble !== null) {
+ clearTimeout(waitForDouble);
+ waitForDouble = null;
+ }
+ })
+ .on('click',
+ (_p, d) => {
+ if (waitForDouble === null) {
+ waitForDouble = setTimeout(() => {
+ postMessage('update', {
+ path: d.data['data-list'][d.data['represent-idx']].path,
+ historyList: historyList,
+ isOpenHistoryBox:
+ document.getElementsByClassName('history-main-box')[0].style.display
+ });
+ waitForDouble = null;
+ }, 300);
+ }
+ })
+ .on('contextmenu', (mouse, d) => {
+ openContextMenu(mouse, d.data['data-list'][d.data['represent-idx']].path);
+ });
+}
diff --git a/media/RelationViewer/style.css b/media/RelationViewer/style.css
new file mode 100644
index 00000000..9f12dc52
--- /dev/null
+++ b/media/RelationViewer/style.css
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /* light theme */
+ body.vscode-light {
+ background-color: #f8f8f8;
+ }
+
+ body.vscode-light svg {
+ background-color: #f8f8f8 !important;
+}
+
+body.vscode-light .wheel-info-box {
+ color: black;
+}
+
+body.vscode-light .history-main-box {
+ background-color: #e7e7e7;
+}
+
+body.vscode-light .history-open-box {
+ background-color: #e7e7e7;
+}
+
+body.vscode-light .context-menu-line {
+ border-bottom:solid rgba(0, 0, 0, 0.425);
+}
+
+body.vscode-light .plus-button-context-menu {
+ background-color: #ececec;
+}
+
+body.vscode-light .plus-button-context-menu-info {
+ color: black;
+}
+
+body.vscode-light .context-menu {
+ background-color: #ececec;
+}
+
+body.vscode-light .plus-button-context-menu-header-clear-button {
+ color: gray;
+}
+
+body.vscode-light .link {
+ stroke: #1e1e1e;
+}
+
+body.vscode-light .hover-text {
+ background-color: black;
+ color: white;
+}
+
+body.vscode-light .plus-button {
+ fill: #a69c9c;
+}
+
+body.vscode-light .minus-button {
+ fill: #a69c9c;
+}
+
+body.vscode-light .context-menu-info {
+ color: black;
+}
+
+body.vscode-light .plus-button-context-menu-header {
+ color: black;
+}
+
+body.vscode-light .context-menu-info:hover {
+ background-color: white;
+}
+
+body.vscode-light .plus-button-context-menu-info:hover {
+ background-color: white;
+}
+
+body.vscode-light .plus-button:hover {
+ fill: white;
+}
+
+body.vscode-light .plus-button-context-menu-header-clear-button:hover {
+ color: white;
+}
+
+body.vscode-light .current-history-color {
+ color: rgb(163, 21, 21);
+}
+
+body.vscode-light .node rect {
+ stroke: #a69c9c;
+}
+
+body.vscode-light .text-file-name {
+ fill: black !important;
+}
+
+body.vscode-light .rect-file-name {
+ fill: white !important;
+}
+
+body.vscode-light .node rect:hover {
+ fill : rgb(141, 141, 141, 0.8) !important;
+}
+
+body.vscode-light .rect-hover-fill {
+ fill : rgb(141, 141, 141, 0.8) !important;
+}
+
+body.vscode-light .current-node {
+ stroke: rgb(163, 21, 21) !important;
+ stroke-width: 2px !important;
+}
+
+html {
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+}
+
+body {
+ background-color: #545454;
+ padding: 0;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif, "PingFang SC";
+ /* prevent drag */
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+svg {
+ margin : 0 auto;
+ margin-top: 30px;
+ flex-shrink: 0;
+}
+
+.node rect {
+ fill: #2a2929;
+ stroke: black;
+ stroke-width: 1px;
+ cursor: pointer;
+}
+
+.node rect:hover {
+ fill: #525252 !important;
+}
+
+.text-hover {
+ opacity: 0.5;
+}
+
+.node text {
+ font: 11px sans-serif;
+ cursor: pointer;
+ fill : white;
+
+ /* default drag function */
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.text-shadow-white {
+ text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
+}
+
+.file-extension-custom {
+ fill: #e7e7e7;
+ font-size: 11px !important;
+ font-weight: bold !important;
+}
+
+.link {
+ fill: none;
+ stroke: #ccc;
+ stroke-width: 2px;
+}
+
+.relation-box {
+ width: 100% !important;
+ height: 100% !important;
+ display: flex;
+ overflow: auto;
+}
+
+.hover-text {
+ position: absolute;
+ background-color: white;
+ color: black;
+ padding: 3px 6px;
+ border-radius: 3px;
+ width: max-content;
+ display: none;
+}
+
+.minus-button {
+ position: absolute;
+ font-size: 35px !important;
+ font-weight: bold !important;
+ cursor: pointer !important;
+ fill: #ececec;
+ display: none;
+ z-index: 3;
+}
+
+.plus-button {
+ position: absolute;
+ font-size: 25px !important;
+ font-weight: bold !important;
+ cursor: pointer !important;
+ fill: #ececec;
+}
+
+.plus-button:hover {
+ fill: #888888;
+}
+
+.plus-button-context-box {
+ position: absolute;
+ top:0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: none;
+ z-index: 1;
+}
+
+.plus-button-context-menu {
+ display: none;
+ position: absolute;
+ width: 250px;
+ max-height: 300px;
+ border: 1px solid black;
+ z-index: 2;
+ box-sizing: border-box;
+ background-color: #1e1e1e;
+ border-radius: 10px;
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+
+.plus-button-context-menu-info {
+ font-size: 13px;
+ cursor: pointer;
+ padding: 4px 10px;
+ word-break: break-all;
+}
+
+.plus-button-context-menu-header-box {
+ display: flex;
+ justify-content: space-around;
+ align-items: flex-end;
+ padding: 5px;
+}
+
+.plus-button-context-menu-header {
+ font-size: 16px;
+ text-align: center;
+ font-weight: bold;
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ color: #e7e7e7;
+ flex-grow: 1;
+}
+
+.plus-button-context-menu-header-clear-button {
+ text-align: center;
+ padding-right: 5px;
+ cursor: pointer;
+ font-size: 10px;
+ color: gray;
+}
+
+.plus-button-context-menu-header-clear-button:hover {
+ color: #fff;
+}
+
+.plus-button-context-menu-info:hover {
+ background-color: #04395e;
+}
+
+.context-menu-line {
+ box-sizing: border-box;
+ border-bottom: solid rgba(255, 255, 255, 0.425);
+ border-width: 0.1px;
+ margin-top: 4px;
+ margin-bottom: 4px;
+}
+
+.current-node {
+ stroke: #d7ba7d !important;
+ stroke-width: 2px !important;
+}
+
+.context-menu {
+ display: none;
+ position: absolute;
+ max-height: 300px;
+ width: max-content;
+ border: 1px solid black;
+ z-index: 2;
+ box-sizing: border-box;
+ background-color: #1e1e1e;
+ border-radius: 10px;
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+
+.context-menu-info {
+ font-size: 13px;
+ cursor: pointer;
+ padding: 4px 20px;
+}
+
+.context-menu-info:hover {
+ background-color: #04395e;
+}
+
+.history-main-box {
+ background-color: #2a2929;
+ border-radius: 5px;
+ overflow: scroll;
+ position: absolute;
+ right: 30px;
+ top: 50px;
+ display: block;
+}
+
+/* scroll hide */
+.history-main-box::-webkit-scrollbar {
+ display: none;
+}
+
+.wheel-info-box {
+ position: absolute;
+ top:10px;
+ left: 10px;
+ color: white;
+}
+
+.deleted-text-decoration {
+ text-decoration: line-through !important;
+ font-style: italic !important;
+ fill: gray !important;
+ color: gray !important;
+}
+
+.font-bold {
+ font-weight: bold;
+}
+
+.history-open-box {
+ display: none;
+ align-items: flex-end;
+ background-color: #2a2929;
+ border-radius: 5px;
+ height: max-content;
+ position: absolute;
+ top: 50px;
+ right: 30px;
+ padding-top: 5px;
+ padding-bottom: 8px;
+ opacity: 0.7;
+}
+
+.history-close-button {
+ margin-left: 5px;
+ cursor: pointer;
+}
+
+.history-open-button {
+ margin-left: 10px;
+ margin-right: 0;
+ cursor: pointer;
+}
+
+.current-history-color {
+ color: #d7ba7d;
+}
+
+.rect-hover-fill {
+ fill: #525252 !important;
+}
+
+.text-file-name {
+ fill: white !important;
+}
+
+.rect-file-name {
+ fill: #2d2d2d !important;
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index d0f525ec..3b996e08 100644
--- a/package.json
+++ b/package.json
@@ -97,6 +97,26 @@
}
],
"priority": "option"
+ },
+ {
+ "viewType": "one.viewer.metadata",
+ "displayName": "Metadata Viewer",
+ "selector": [
+ {
+ "filenamePattern": "*"
+ }
+ ],
+ "priority": "option"
+ },
+ {
+ "viewType": "one.viewer.relation",
+ "displayName": "Relation Viewer",
+ "selector": [
+ {
+ "filenamePattern": "*"
+ }
+ ],
+ "priority": "option"
}
],
"commands": [
@@ -195,6 +215,26 @@
"command": "one.viewer.jsonTracer",
"title": "json tracer",
"category": "ONE"
+ },
+ {
+ "command": "one.viewer.metadata.showFromDefaultExplorer",
+ "title": "Show Metadata",
+ "category": "ONE"
+ },
+ {
+ "command": "one.viewer.metadata.showFromOneExplorer",
+ "title": "ONE: Show Metadata",
+ "category": "ONE"
+ },
+ {
+ "command": "one.viewer.relation.showFromDefaultExplorer",
+ "title": "Show Relation",
+ "category": "ONE"
+ },
+ {
+ "command": "one.viewer.relation.showFromOneExplorer",
+ "title": "ONE: Show Relation",
+ "category": "ONE"
}
],
"menus": {
@@ -275,6 +315,28 @@
"command": "one.toolchain.uninstall",
"when": "view == ToolchainView && viewItem =~ /toolchain/ && !one.job:running",
"group": "inline"
+ },
+ {
+ "command": "one.viewer.metadata.showFromOneExplorer",
+ "when": "view == OneExplorerView && viewItem =~ /baseModel|product/",
+ "group": "7_metadata@1"
+ },
+ {
+ "command": "one.viewer.relation.showFromOneExplorer",
+ "when": "view == OneExplorerView && viewItem =~ /baseModel|product/",
+ "group": "7_metadata@1"
+ }
+ ],
+ "explorer/context": [
+ {
+ "command": "one.viewer.metadata.showFromDefaultExplorer",
+ "when": "resourceExtname in one.metadata.supportedFiles && !explorerResourceIsFolder",
+ "group": "7_metadata@1"
+ },
+ {
+ "command": "one.viewer.relation.showFromDefaultExplorer",
+ "when": "resourceExtname in one.metadata.supportedFiles && !explorerResourceIsFolder",
+ "group": "7_metadata@1"
}
],
"commandPalette": [
@@ -341,6 +403,22 @@
{
"command": "one.viewer.jsonTracer",
"when": "false"
+ },
+ {
+ "command": "one.viewer.metadata.showFromDefaultExplorer",
+ "when": "false"
+ },
+ {
+ "command": "one.viewer.metadata.showFromOneExplorer",
+ "when": "false"
+ },
+ {
+ "command": "one.viewer.relation.showFromDefaultExplorer",
+ "when": "false"
+ },
+ {
+ "command": "one.viewer.relation.showFromOneExplorer",
+ "when": "false"
}
]
},
diff --git a/src/MetadataViewer/MetadataViewer.ts b/src/MetadataViewer/MetadataViewer.ts
new file mode 100644
index 00000000..c7dffa19
--- /dev/null
+++ b/src/MetadataViewer/MetadataViewer.ts
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as vscode from 'vscode';
+import {getNonce} from '../Utils/external/Nonce';
+
+/* istanbul ignore next */
+export class MetadataViewer {
+ private readonly _panel: vscode.WebviewPanel;
+ private _disposable: vscode.Disposable[];
+ protected readonly _webview: vscode.Webview;
+ protected readonly _extensionUri: vscode.Uri;
+
+ public constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
+ this._disposable = [];
+ this._webview = panel.webview;
+ this._panel = panel;
+ this._extensionUri = extensionUri;
+ }
+
+ public initWebView() {
+ this._webview.options = this.getWebviewOptions();
+
+ // Register for an event when you receive a message from a web view
+ this.registerEventHandlers();
+ }
+
+ private getWebviewOptions(): vscode.WebviewOptions&vscode.WebviewPanelOptions {
+ return {
+ // Enable javascript in the webview
+ enableScripts: true,
+ // to prevent view to reload after loosing focus
+ retainContextWhenHidden: true
+ };
+ }
+
+
+ public loadContent() {
+ this._getHtmlForWebview(this._extensionUri, this._panel);
+ }
+
+ private async _getHtmlForWebview(extensionUri: vscode.Uri, panel: vscode.WebviewPanel) {
+ panel.webview.options = {
+ enableScripts: true,
+ };
+
+ const nonce = getNonce();
+ const jsIndex = panel.webview.asWebviewUri(
+ vscode.Uri.joinPath(extensionUri, 'media', 'MetadataViewer', 'index.js'));
+ const cssIndex = panel.webview.asWebviewUri(
+ vscode.Uri.joinPath(extensionUri, 'media', 'MetadataViewer', 'style.css'));
+ const codiconsUri = panel.webview.asWebviewUri(vscode.Uri.joinPath(
+ extensionUri, 'node_modules', '@vscode/codicons', 'dist', 'codicon.css'));
+
+ const htmlUri = vscode.Uri.joinPath(extensionUri, 'media', 'MetadataViewer', 'index.html');
+
+ let html = Buffer.from(await vscode.workspace.fs.readFile(htmlUri)).toString();
+ html = html.replace(/\${nonce}/g, `${nonce}`);
+ html = html.replace(/\${webview.cspSource}/g, `${panel.webview.cspSource}`);
+ html = html.replace(/\${index.css}/g, `${cssIndex}`);
+ html = html.replace(/\${index.js}/g, `${jsIndex}`);
+ html = html.replace(/\${codicon.css}/g, `${codiconsUri}`);
+ panel.webview.html = html;
+ }
+
+ public owner(panel: vscode.WebviewPanel) {
+ return this._panel === panel;
+ }
+
+ private registerEventHandlers() {
+ // Handle messages from the webview
+ this._webview.onDidReceiveMessage(
+ _message => {
+
+ },
+ null, this._disposable);
+ }
+
+ public disposeMetadataView() {
+ while (this._disposable.length) {
+ const x = this._disposable.pop();
+ if (x) {
+ x.dispose();
+ }
+ }
+ }
+}
diff --git a/src/MetadataViewer/MetadataViewerProvider.ts b/src/MetadataViewer/MetadataViewerProvider.ts
new file mode 100644
index 00000000..692e8e5d
--- /dev/null
+++ b/src/MetadataViewer/MetadataViewerProvider.ts
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as vscode from 'vscode';
+import { Metadata } from '../MetadataManager/Metadata';
+import { PathToHash } from '../MetadataManager/PathToHash';
+import { MetadataViewer } from './MetadataViewer';
+
+export class RelationViewerDocument implements vscode.CustomDocument {
+ private readonly _uri: vscode.Uri;
+ private _metadataViwer: MetadataViewer[];
+
+
+ static async create(uri: vscode.Uri):
+ Promise> {
+ return new RelationViewerDocument(uri);
+ }
+
+ private constructor(uri: vscode.Uri) {
+ this._uri = uri;
+ this._metadataViwer = [];
+ }
+
+ public get uri() {
+ return this._uri;
+ }
+
+ // CustomDocument implements
+ dispose(): void {
+ // NOTE panel is closed before document and this is just for safety
+ this._metadataViwer.forEach((view) => {
+ while (this._metadataViwer.length) {
+ view.disposeMetadataView();
+ }
+ });
+ this._metadataViwer = [];
+ }
+
+ public openView(panel: vscode.WebviewPanel, extensionUri: vscode.Uri, fileUri: vscode.Uri) {
+ let view = new MetadataViewer(panel, extensionUri);
+
+ view.initWebView();
+ view.loadContent();
+ this._metadataViwer.push(view);
+
+ //메타데이터 정보를 가져오는 로직(Uri 인자를 이용하면 됨)
+ getMetadata(fileUri).then((metadata: any) => {
+ const payload: any = {};
+ payload[metadata['name']] = metadata;
+ console.log(metadata);
+ //패널 타이틀 변경(적용되지 않음)
+ //panel.title = `Metadata: ${this._getNameFromPath(fileUri.toString())}`;
+
+ //가져온 메타데이터를 웹뷰로 메세지를 보낸다.
+ panel.webview.postMessage({command:'showMetadata',metadata: payload});
+ });
+
+
+
+
+ panel.onDidDispose(() => {
+ // TODO make faster
+ this._metadataViwer.forEach((view, index) => {
+ if (view.owner(panel)) {
+ view.disposeMetadataView();
+ this._metadataViwer.splice(index, 1);
+ }
+ });
+ });
+
+ return view;
+ }
+}
+
+export class MetadataViewerProvider implements
+ vscode.CustomReadonlyEditorProvider {
+ public static readonly viewType = 'one.viewer.metadata';
+
+ private _context: vscode.ExtensionContext;
+
+ public static register(context: vscode.ExtensionContext): void {
+ const provider = new MetadataViewerProvider(context);
+
+ const registrations = [
+ vscode.window.registerCustomEditorProvider(MetadataViewerProvider.viewType, provider, {
+ webviewOptions: {
+ retainContextWhenHidden: true,
+ }
+ }),
+ vscode.commands.registerCommand(
+ 'one.viewer.metadata.showFromOneExplorer',
+ async (uri) => {
+ // If the method is executed in the ONE Explorer, change the uri instance.
+ const fileUri = uri.uri;
+
+ vscode.commands.executeCommand(
+ 'vscode.openWith', fileUri, MetadataViewerProvider.viewType);
+ }),
+ vscode.commands.registerCommand(
+ 'one.viewer.metadata.showFromDefaultExplorer',
+ async (uri) => {
+ const fileUri = uri;
+
+ vscode.commands.executeCommand(
+ 'vscode.openWith', fileUri, MetadataViewerProvider.viewType);
+ })
+ // Add command registration here
+ ];
+
+ // supported file extension to show relations context menu
+ vscode.commands.executeCommand(
+ 'setContext', 'one.metadata.supportedFiles',
+ ['.tflite', '.pb', '.onnx', '.circle', '.log']);
+
+ registrations.forEach(disposable => context.subscriptions.push(disposable));
+ }
+
+ constructor(private readonly context: vscode.ExtensionContext) {
+ this._context = context;
+ }
+
+ // CustomReadonlyEditorProvider implements
+ async openCustomDocument(
+ uri: vscode.Uri, _openContext: {backupId?: string},
+ _token: vscode.CancellationToken): Promise {
+ const document: RelationViewerDocument = await RelationViewerDocument.create(uri);
+ // NOTE as a readonly viewer, there is not much to do
+
+ return document;
+ }
+
+ // CustomReadonlyEditorProvider implements
+ async resolveCustomEditor(
+ document: RelationViewerDocument, webviewPanel: vscode.WebviewPanel,
+ _token: vscode.CancellationToken): Promise {
+ document.openView(webviewPanel, this._context.extensionUri, document.uri);
+ }
+}
+
+async function getMetadata(uri: vscode.Uri) {
+ const instance = await PathToHash.getInstance();
+ const hash = instance.getHash(uri);
+ const entry = await Metadata.getEntry(uri, hash);
+ return entry;
+}
diff --git a/src/MetadataViewer/example/MetadataExample.ts b/src/MetadataViewer/example/MetadataExample.ts
new file mode 100644
index 00000000..8639aca8
--- /dev/null
+++ b/src/MetadataViewer/example/MetadataExample.ts
@@ -0,0 +1,42 @@
+/*
+NOTE this is example logic is that you can get sample MetaData
+TODO Replace this sample logic into real logic
+*/
+export function getMetadata(path:any) {
+ return {
+ "test.log": {
+ "file-extension": "log",
+ "created-time": new Date().toLocaleString(),
+ "modified-time": new Date().toLocaleString(),
+ "is-deleted": false,
+
+ "toolchain-version": "toolchain v1.3.0",
+ "onecc-version": "1.20.0",
+ "operations": {
+ "op-total": 4,
+ "ops": ['conv2d', 'relu', 'conv', 'spp']
+ },
+ "cfg-settings": {
+ "onecc": {
+ "one-import-tf": true,
+ "one-import-tflite": false,
+ "one-import-onnx": false,
+ "one-quantize":true
+ },
+ "one-import-tf": {
+ "converter-version": "v2",
+ "input-array": "a",
+ "output-array": "a",
+ "input-shapes": "1,299,299"
+ },
+ "one-quantize":{
+ "quantized-dtype":'int16',
+ "input-data-format":'list',
+ "min-percentile":'11',
+ "max-percentile":'100',
+ "mode":'movingAvg',
+ }
+ }
+ }
+ };
+}
diff --git a/src/RelationViewer/RelationViewer.ts b/src/RelationViewer/RelationViewer.ts
new file mode 100644
index 00000000..bfb01185
--- /dev/null
+++ b/src/RelationViewer/RelationViewer.ts
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as vscode from 'vscode';
+import { MetadataViewerProvider } from '../MetadataViewer/MetadataViewerProvider';
+import {Balloon} from '../Utils/Balloon';
+import {getNonce} from '../Utils/external/Nonce';
+import {getUri} from '../Utils/external/Uri';
+import {obtainWorkspaceRoot} from '../Utils/Helpers';
+import {getRelationData} from './example/RelationExample';
+
+/* istanbul ignore next */
+export class RelationViewer {
+ private readonly _panel: vscode.WebviewPanel;
+ private _disposable: vscode.Disposable[];
+ protected readonly _webview: vscode.Webview;
+ protected readonly _extensionUri: vscode.Uri;
+
+ public constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
+ this._disposable = [];
+ this._webview = panel.webview;
+ this._panel = panel;
+ this._extensionUri = extensionUri;
+ }
+
+ public initWebview() {
+ this._webview.options = this.getWebviewOptions();
+
+ // Register for an event when you receive a message from a web view
+ this.registerEventHandlers(this._panel);
+ }
+
+ private getWebviewOptions(): vscode.WebviewOptions&vscode.WebviewPanelOptions {
+ return {
+ // Enable javascript in the webview
+ enableScripts: true,
+ // to prevent view to reload after loosing focus
+ retainContextWhenHidden: true
+ };
+ }
+
+ public loadContent() {
+ this._getHtmlForWebview(this._extensionUri, this._panel);
+ }
+
+ private async _getHtmlForWebview(extensionUri: vscode.Uri, panel: vscode.WebviewPanel) {
+ panel.webview.options = {
+ enableScripts: true,
+ };
+
+ const nonce = getNonce();
+ const scriptUri = getUri(panel.webview, extensionUri, ['media', 'RelationViewer', 'index.js']);
+ const styleUri = getUri(panel.webview, extensionUri, ['media', 'RelationViewer', 'style.css']);
+
+ const codiconsUri = panel.webview.asWebviewUri(vscode.Uri.joinPath(
+ extensionUri, 'node_modules', '@vscode/codicons', 'dist', 'codicon.css'));
+
+
+ const htmlUri = vscode.Uri.joinPath(extensionUri, 'media', 'RelationViewer', 'index.html');
+ let html = Buffer.from(await vscode.workspace.fs.readFile(htmlUri)).toString();
+ html = html.replace(/\${nonce}/g, `${nonce}`);
+ html = html.replace(/\${webview.cspSource}/g, `${panel.webview.cspSource}`);
+ html = html.replace(/\${codicon.css}/g, `${codiconsUri}`);
+ html = html.replace(/\${scriptUri}/g, `${scriptUri}`);
+ html = html.replace(/\${styleUri}/g, `${styleUri}`);
+ panel.webview.html = html;
+ }
+
+ public owner(panel: vscode.WebviewPanel) {
+ return this._panel === panel;
+ }
+
+ private registerEventHandlers(panel: vscode.WebviewPanel) {
+ // Handle messages from the webview
+ this._webview.onDidReceiveMessage(message => {
+ let payload;
+ let fileUri: vscode.Uri;
+ let viewType: string = 'default';
+ switch (message.type) {
+ case 'update':
+ // fileUri = vscode.Uri.file(obtainWorkspaceRoot() + '/' + message.path);
+ payload = getRelationData(message.path);
+ if (!payload) {
+ return Balloon.error('Invalid File Path, please check if file exists.', false);
+ }
+ panel.webview.postMessage({
+ type: 'update',
+ payload: payload,
+ historyList: message.historyList,
+ isOpenHistoryBox: message.isOpenHistoryBox
+ });
+ break;
+ case 'history':
+ // fileUri = vscode.Uri.file(obtainWorkspaceRoot() + '/' + message.path);
+ payload = getRelationData(message.path);
+ if (!payload) {
+ return Balloon.error('Invalid File Path, please check if file exists.', false);
+ }
+ panel.webview.postMessage(
+ {type: 'history', payload: payload, historyList: message.historyList});
+ break;
+ case 'showMetadata':
+ fileUri = vscode.Uri.file(obtainWorkspaceRoot() + '/' + message.path);
+ // This code opens the metadata viewer directly.
+ // It is necessary if the code of the metadata viewer is added.
+ vscode.commands.executeCommand('vscode.openWith', fileUri, MetadataViewerProvider.viewType);
+ break;
+ case 'openFile':
+ if (message.path.split('.').slice(-1)[0] === 'circle') {
+ viewType = `one.viewer.circle`;
+ }
+ fileUri = vscode.Uri.file(obtainWorkspaceRoot() + '/' + message.path);
+ vscode.commands.executeCommand('vscode.openWith', fileUri, viewType);
+ break;
+ default:
+ break;
+ }
+ }, null, this._disposable);
+ }
+
+ public disposeMetadataView() {
+ while (this._disposable.length) {
+ const x = this._disposable.pop();
+ if (x) {
+ x.dispose();
+ }
+ }
+ }
+}
diff --git a/src/RelationViewer/RelationViewerProvider.ts b/src/RelationViewer/RelationViewerProvider.ts
new file mode 100644
index 00000000..2cdfec55
--- /dev/null
+++ b/src/RelationViewer/RelationViewerProvider.ts
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as vscode from 'vscode';
+
+import {getRelationData} from './example/RelationExample';
+import {RelationViewer} from './RelationViewer';
+
+export class RelationViewerDocument implements vscode.CustomDocument {
+ private readonly _uri: vscode.Uri;
+ private _relationViewer: RelationViewer[];
+
+ static async create(uri: vscode.Uri):
+ Promise> {
+ return new RelationViewerDocument(uri);
+ }
+
+ private constructor(uri: vscode.Uri) {
+ this._uri = uri;
+ this._relationViewer = [];
+ }
+
+ public get uri() {
+ return this._uri;
+ }
+
+ // CustomDocument implements
+ dispose(): void {
+ // NOTE panel is closed before document and this is just for safety
+ this._relationViewer.forEach((view) => {
+ while (this._relationViewer.length) {
+ view.disposeMetadataView();
+ }
+ });
+ this._relationViewer = [];
+ }
+
+ public openView(panel: vscode.WebviewPanel, extensionUri: vscode.Uri, fileUri: vscode.Uri) {
+ let view = new RelationViewer(panel, extensionUri);
+
+ view.initWebview();
+ view.loadContent();
+ this._relationViewer.push(view);
+
+ const payload = getRelationData(fileUri);
+
+ // Send a message the relation data to the web view
+ panel.webview.postMessage({type: 'create', payload: payload});
+
+ panel.onDidDispose(() => {
+ // TODO make faster
+ this._relationViewer.forEach((view, index) => {
+ if (view.owner(panel)) {
+ view.disposeMetadataView();
+ this._relationViewer.splice(index, 1);
+ }
+ });
+ });
+
+ return view;
+ }
+}
+
+export class RelationViewerProvider implements
+ vscode.CustomReadonlyEditorProvider {
+ public static readonly viewType = 'one.viewer.relation';
+ private _context: vscode.ExtensionContext;
+
+ public static register(context: vscode.ExtensionContext): void {
+ const provider = new RelationViewerProvider(context);
+
+ const registrations = [
+ vscode.window.registerCustomEditorProvider(RelationViewerProvider.viewType, provider, {
+ webviewOptions: {
+ retainContextWhenHidden: true,
+ }
+ }),
+ vscode.commands.registerCommand(
+ 'one.viewer.relation.showFromDefaultExplorer',
+ async (uri) => {
+ const fileUri = uri;
+
+ vscode.commands.executeCommand(
+ 'vscode.openWith', fileUri, RelationViewerProvider.viewType);
+ }),
+ vscode.commands.registerCommand(
+ 'one.viewer.relation.showFromOneExplorer',
+ async (uri) => {
+ // If the method is executed in the ONE Explorer, change the uri instance.
+ const fileUri = uri.uri;
+
+ vscode.commands.executeCommand(
+ 'vscode.openWith', fileUri, RelationViewerProvider.viewType);
+ })
+ // Add command registration here
+ ];
+
+ // supported file extension to show relations context menu
+ vscode.commands.executeCommand(
+ 'setContext', 'one.relation.supportedFiles',
+ ['.tflite', '.pb', '.onnx', '.circle', '.log']);
+
+ registrations.forEach(disposable => context.subscriptions.push(disposable));
+ }
+
+ constructor(private readonly context: vscode.ExtensionContext) {
+ this._context = context;
+ }
+
+ // CustomReadonlyEditorProvider implements
+ async openCustomDocument(
+ uri: vscode.Uri, _openContext: {backupId?: string},
+ _token: vscode.CancellationToken): Promise {
+ const document: RelationViewerDocument = await RelationViewerDocument.create(uri);
+ // NOTE as a readonly viewer, there is not much to do
+
+ // TODO handle dispose
+ // TODO handle file change events
+ // TODO handle backup
+
+ return document;
+ }
+
+ // CustomReadonlyEditorProvider implements
+ async resolveCustomEditor(
+ document: RelationViewerDocument, webviewPanel: vscode.WebviewPanel,
+ _token: vscode.CancellationToken): Promise {
+ document.openView(webviewPanel, this._context.extensionUri, document.uri);
+ }
+}
diff --git a/src/RelationViewer/example/RelationExample.ts b/src/RelationViewer/example/RelationExample.ts
new file mode 100644
index 00000000..8be4ea37
--- /dev/null
+++ b/src/RelationViewer/example/RelationExample.ts
@@ -0,0 +1,48 @@
+/*
+NOTE this is example logic is that you can get sample relation data
+TODO Replace this sample logic into real logic
+*/
+export function getRelationData(path:any) {
+
+ const dummyData = {
+ "selected": "1",
+ "relation-data": [
+ {"id": "1", "parent": "", "represent-idx": 0, "data-list": [{"name": "baseModelTestTflite123123.tflite", "path": "baseModelTestTflite123123.tflite", "is-deleted":false},{"name": "model.tflite", "path": "model.tflite", "is-deleted":true},{"name": "c.tflite", "path": "c.tflite"},{"name": "d.tflite", "path": "d.tflite", "is-deleted":false}]}, // TODO: id, parentid: hashId
+ {"id": "2", "parent": "1", "represent-idx": 0, "data-list": [{"name": "test1.circle", "path": "src/hello/test1.circle", "onecc-version": "1.0.0", "toolchain-version": "1.0.0", "is-deleted":false}]},
+ {"id": "3", "parent": "2", "represent-idx": 0, "data-list": [{"name": "test2.circle", "path": "src/trudiv/model/test2.circle", "onecc-version": "1.0.0", "toolchain-version": "1.0.0", "is-deleted":false}]},
+ {"id": "4", "parent": "1", "represent-idx": 0, "data-list": [{"name": "test1.log", "path": "test1.log", "onecc-version": "1.0.0", "toolchain-version": "1.0.0", "is-deleted":false}]},
+ {"id": "5", "parent": "2", "represent-idx": 0, "data-list": [{"name": "test2.log", "path": "test2.log", "toolchain-version": "1.0.0", "is-deleted":false}]},
+ {"id": "6", "parent": "4", "represent-idx": 0, "data-list": [{"name": "baseModelTestCircle.circle", "path": "baseModelTestCircle.circle", "is-deleted":false}]},
+ {"id": "7", "parent": "6", "represent-idx": 0, "data-list": [{"name": "model.q8.circle", "path": "model.q8.circle", "onecc-version": "1.0.0", "toolchain-version": "1.0.0", "is-deleted":false}]},
+ {"id": "8", "parent": "6", "represent-idx": 0, "data-list": [{"name": "pbTestCircle1.log", "path": "pbTestCircle1.log", "onecc-version": "1.0.0", "toolchain-version": "1.0.0", "is-deleted":false}]},
+ {"id": "9", "parent": "7", "represent-idx": 0, "data-list": [{"name": "test_onnx.circle", "path": "hello/test_onnx.circle", "toolchain-version": "1.0.0", "is-deleted":false}]},
+ {"id": "10", "parent": "7", "represent-idx": 0, "data-list": [{"name": "while_000.circle", "path": "while/while_000.circle", "onecc-version": "1.0.0", "toolchain-version": "1.0.0", "is-deleted":false}]},
+ {"id": "11", "parent": "8", "represent-idx": 0, "data-list": [{"name": "e1.log", "path": "e1.circle", "onecc-version": "1.0.0", "toolchain-version": "1.0.0", "is-deleted":false}]},
+ {"id": "12", "parent": "8", "represent-idx": 0, "data-list": [{"name": "e2.log", "path": "e2.circle", "onecc-version": "1.0.0", "toolchain-version": "1.0.0", "is-deleted":true},{"name": "e3.circle", "path": "e3.circle", "onecc-version": "1.2.0", "toolchain-version": "1.0.0", "is-deleted":false}]}
+ ]
+ } as any;
+
+ for (const key in dummyData) {
+ if(key === 'relation-data'){
+ for (const idx in dummyData['relation-data']) {
+ for (const key2 in dummyData['relation-data'][idx]) {
+ if(key2 === 'data-list'){
+ for (let index = 0; index < dummyData['relation-data'][idx]['data-list'].length; index++) {
+ const element = dummyData['relation-data'][idx]['data-list'][index];
+ for (const key3 in element) {
+ if(key3 === 'path'){
+ if(element['path'] === path){
+ dummyData['relation-data'][idx]['represent-idx'] = index;
+ dummyData['selected'] = dummyData['relation-data'][idx]['id'];
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return dummyData;
+}
\ No newline at end of file
diff --git a/src/extension.ts b/src/extension.ts
index 9a34841d..c8e920f5 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -25,6 +25,8 @@ import {MondrianEditorProvider} from './Mondrian/MondrianEditor';
import {OneTreeDataProvider} from './OneExplorer/OneExplorer';
import {PartEditorProvider} from './PartEditor/PartEditor';
import {PartGraphSelPanel} from './PartEditor/PartGraphSelector';
+import {RelationViewerProvider} from './RelationViewer/RelationViewerProvider';
+import {MetadataViewerProvider} from './MetadataViewer/MetadataViewerProvider';
import {ToolchainProvider} from './Toolchain/ToolchainProvider';
import {Logger} from './Utils/Logger';
@@ -70,6 +72,10 @@ export function activate(context: vscode.ExtensionContext) {
CircleViewerProvider.register(context);
+ RelationViewerProvider.register(context);
+
+ MetadataViewerProvider.register(context);
+
// returning backend registration function that will be called by backend extensions
return backendRegistrationApi();
}