diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/.gitignore b/plugins/tensorboard-plugins/tb_graph_ascend/.gitignore index 70f4e767811d0d93c25fbb8ce2d2b29c4ba3b6e6..2ae952e7aced2c5fe1b74606444d92498196c7d2 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/.gitignore +++ b/plugins/tensorboard-plugins/tb_graph_ascend/.gitignore @@ -5,7 +5,7 @@ dist/ build/ tb_graph_ascend.egg-info/ __pycache__/ -/server/static/index.html +**/server/static/index.html report.html assets/ /htmlcov/ \ No newline at end of file diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/README.md b/plugins/tensorboard-plugins/tb_graph_ascend/README.md index cd578e3f4d7d597229854e1362afe05753600f03..7cd935265554d493dc47472811cebbf1619c9a83 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/README.md +++ b/plugins/tensorboard-plugins/tb_graph_ascend/README.md @@ -2,13 +2,13 @@ ## 一、 介绍 -此工具是将模型结构进行分级可视化展示的 Tensorboard 插件。可将模型的层级关系、精度性能数据进行可视化,并支持将调试模型和标杆模型进行分视图展示和关联比对,方便用户快速定位精度问题。 +此工具是将模型结构进行分级可视化展示的 Tensorboard 插件。可将模型的层级关系、精度数据进行可视化,并支持将调试模型和标杆模型进行分视图展示和关联比对,方便用户快速定位精度问题。 ## 二、快速安装 ### 1. 相关依赖 -`python >= 3.7 ,tensorboard >= 2.11.2,numpy <= 1.26.3` +`python >= 3.7 ,tensorboard >= 2.11.2 ### 2. 安装方式 @@ -160,7 +160,7 @@ 打开本工具时,本工具会对 logdir 目录下的 vis 文件以及其父目录进行安全检查,如果存在安全风险,本工具会展示如下提示信息,询问用户是否继续执行,用户选择继续执行后,可以操作未通过安全检查的文件和目录,用户需要自行承担操作风险。如果用户选择不继续执行,则用户只能操作通过安全检查的文件。 -![输入图片说明](./doc/images/saFe_warning.png) +![输入图片说明](./doc/images/safe_warning.png) #### 4.1.2 TensorBoard 版本说明 diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/common/constant.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/common/constant.ts index 145aa943805e35341a4453b44b7ae6495c27fb64..986d12c65fb4265a0f06bb3534862e977d656b02 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/common/constant.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/common/constant.ts @@ -20,6 +20,9 @@ export const BENCH_PREFIX = 'B___'; // 未匹配节点颜色 export const UNMATCHED_COLOR = '#C7C7C7'; +export const JSON_TYPE = 'json' +export const DB_TYPE = 'db' + // 双图下单个图形的最小宽度 export const MIN_GRAPG_WIDTH = 200; @@ -44,7 +47,7 @@ export enum NODE_TYPE { // 渲染信息 export const DURATION_TIME = 160; // 动画时间 export const SELECTED_STROKE_COLOR = 'rgb(31, 63, 207)'; // 选中节点颜色 -export const BENCH_NODE_COLOR = 'rgb(236, 235, 235)'; // 基准模型节点颜色 +export const BENCH_NODE_COLOR = 'rgba(255, 255, 255, 1)'; // 基准模型节点颜色 export const BENCH_STROKE_COLOR = 'rgb(161, 161, 161)'; // 基准模型边框颜色 export const NO_MATCHED_NODE_COLOR = 'rgb(199, 199, 199)'; // 未匹配节点颜色 export const BASE_NODE_COLOR = 'rgb(255, 255, 255)'; // 基准节点颜色,没有精度信息、API、FUSION的填充色 diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/common/i18n.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/common/i18n.ts index 1f0e5ca5a3940526b0ac32b7950fd2343487fce9..87fabb2ff4a75a547f5ae17448455348ca1e8110 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/common/i18n.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/common/i18n.ts @@ -25,7 +25,7 @@ i18next translation: { fit: "Fit Screen", settings: "Settings", - match: 'Matching', + function: 'function', show_debug_minimap: "show debug minimap", show_bench_minimap: "show bench minimap", run: "Run", @@ -48,15 +48,15 @@ i18next }, node_match: "Node Match", select_match_config_file: "Select Match Config File", - select_match_config_file_desc: "Select the corresponding configuration file, read the matching node information, and match the corresponding node." - + select_match_config_file_desc: "Select the corresponding configuration file, read the matching node information, and match the corresponding node.", + node_search: "Node Search" } }, 'zh-CN': { translation: { fit: "自适应屏幕", settings: "设置", - match: '匹配', + function: "功能", show_debug_minimap: "调试侧缩略图", show_bench_minimap: "标杆侧缩略图", run: "目录", @@ -70,8 +70,8 @@ i18next accuracy_error: "精度误差", overflow: "精度溢出", match_accuracy_error: "符合精度误差节点", - overflow_filter_node: "溢出筛选节点", - no_matching_nodes: "无匹配节点11", + overflow_filter_node: "符合溢出筛选节点", + no_matching_nodes: "无匹配节点", precision_desc: { "summary": "节点中调试侧和标杆侧输出的统计量相对误差,值越大精度差距越大,颜色标记越深,相对误差指标(RelativeErr):| (调试值 - 标杆值) / 标杆值 |", "all": "节点中所有输入的最小双千指标和所有输出的最小双千分之一指标的差值,反映了双千指标的下降情况,值越大精度差距越大,颜色标记越深,双千分之一精度指标(One Thousandth Err Ratio):Tensor中的元素逐个与对应的标杆数据对比,相对误差小于千分之一的比例占总元素个数的比例,比例越接近1越好", @@ -79,7 +79,8 @@ i18next }, node_match: "节点匹配", select_match_config_file: "选择匹配配置文件", - select_match_config_file_desc: "选择对应配置文件,会读取匹配节点信息,并将对应节点进行匹配。" + select_match_config_file_desc: "选择对应配置文件,会读取匹配节点信息,并将对应节点进行匹配。", + node_search: "节点搜索" } } }, diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/index.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/index.ts index 3d895cea6fcf654bad4d5967636a103f42148b94..b085bee4647da0d1445d88f06077e6c9e2466973 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/index.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/index.ts @@ -27,8 +27,8 @@ import '../graph_controls_board/index'; import '../common/graph-board-layout'; import '@vaadin/confirm-dialog' import { Notification } from '@vaadin/notification'; - -import type { SelectionType, ProgressType, GraphConfigType, GraphAllNodeType, NodeListType, UnmatchedNodeType } from './type'; +import request from '../utils/request'; +import type { SelectedItemType, SelectionType, ProgressType, GraphConfigType, GraphAllNodeType, NodeListType, UnmatchedNodeType } from './type'; @customElement('graph-ascend') class TfGraphDashboard extends LegacyElementMixin(PolymerElement) { @@ -51,19 +51,22 @@ class TfGraphDashboard extends LegacyElementMixin(PolymerElement) { colors="{{colors}}" colorset="[[colorset]]" overflowcheck="[[overflowcheck]]" + steps="[[steps]]" + ranks="[[ranks]]" microsteps="[[microsteps]]" npu-match-nodes="[[npuMatchNodes]]" bench-match-nodes="[[benchMatchNodes]]" matched-config-files="[[matchedConfigFiles]]" nodelist="[[nodelist]]" unmatched="[[unmatched]]" - matchedlist="[[matchedlist]]" minimap-vis="{{minimapVis}}" is-sync-expand="{{isSyncExpand}}" is-single-graph="{{isSingleGraph}}" task="[[task]]" is-overflow-filter="{{isOverflowFilter}}" on-fit-tap="onFitTap" + load-all-node-list="{{loadGraphAllNodeList}}" + need-load-all-node-list="{{needLoadAllNodeList}}" >
@@ -160,10 +163,10 @@ class TfGraphDashboard extends LegacyElementMixin(PolymerElement) { selection: SelectionType | null = null; @property({ type: Object, notify: true }) - nodelist: any; + nodelist: NodeListType = {} as NodeListType; @property({ type: Object, notify: true }) - unmatched: any; + unmatched: UnmatchedNodeType = {} as UnmatchedNodeType; @property({ type: Object, notify: true }) matchedlist: any; @@ -186,11 +189,18 @@ class TfGraphDashboard extends LegacyElementMixin(PolymerElement) { @property({ type: Boolean }) isSingleGraph: boolean = false; - @property({ type: Object }) - microsteps: any; + @property({ type: Array }) + microsteps: number[] = []; + + @property({ type: Array }) + steps: Array = [{ value: 0, label: '0' }]; + + @property({ type: Array }) + ranks: Array = [{ value: 0, label: '0' }]; + @property({ type: Array }) - overflowcheck; + overflowcheck: boolean = false; @property({ type: Object }) tooltips: object = {}; @@ -215,6 +225,9 @@ class TfGraphDashboard extends LegacyElementMixin(PolymerElement) { @property({ type: Array }) fileListError: Array = []; + @property({ type: Object }) + needLoadAllNodeList: boolean = true; + private currentSelection: SelectionType | null = null; private useGraphAscend = useGraphAscend(); private eventSource: EventSource | null = null; @@ -224,12 +237,30 @@ class TfGraphDashboard extends LegacyElementMixin(PolymerElement) { if (!this.selection?.run || !this.selection?.tag) { return; } - if (this.currentSelection?.run !== this.selection?.run || this.currentSelection?.tag !== this.selection?.tag) { - this.loadGraphData(this.selection); - } else if (this.currentSelection?.microStep !== this.selection?.microStep) { + const isFileChange = this.currentSelection?.run !== this.selection?.run || this.currentSelection?.tag !== this.selection?.tag; + const isDBChange = this.currentSelection?.rank !== this.selection?.rank || this.currentSelection?.step !== this.selection?.step; + if (isFileChange) { + switch (this.selection?.type) { + case 'json': + this.loadJSONGraphData(this.selection); + break; + case 'db': + this.loadDBGraphData(this.selection, true); + break; + default: + break; + } + } + else if (isDBChange) { + this.loadDBGraphData(this.selection, false); + } + else if (this.currentSelection?.microStep !== this.selection?.microStep) { this.initGraphBoard(); // 只改变microsteps时,不重新加载图数据 - this.loadGraphAllNodeList(this.selection); } + else { + return + } + this.set('needLoadAllNodeList', true); this.currentSelection = this.selection; }; @@ -262,13 +293,25 @@ class TfGraphDashboard extends LegacyElementMixin(PolymerElement) { } this.set('metaDir', data); } + loadDBGraphData = async (metaData: SelectionType, isInitDB: boolean = false) => { + if (isInitDB) { + this.progreesLoading('正在初始化数据库', '请稍后', { progress: 10, progressValue: 10, done: false }); + await request({ url: 'loadGraphData', method: 'GET', params: metaData }); + await this.loadGraphConfig(metaData) + } + this.progreesLoading('正在初始化图', '请稍后', { progress: 90, progressValue: 90, done: false }); + this.initGraphBoard(); // 先读取配置,再加载图,顺序很重要 + this.progreesLoading('初始化完成', '请稍后', { progress: 100, progressValue: 100, done: true }); + } + - loadGraphData = (metaData: SelectionType) => { + loadJSONGraphData = async (metaData: SelectionType) => { if (this.eventSource) { this.eventSource.close(); this.eventSource = null; } - this.eventSource = new EventSource(`loadGraphData?run=${metaData.run}&tag=${metaData.tag}`); + + this.eventSource = new EventSource(`loadGraphData?run=${metaData.run}&tag=${metaData.tag}&type=${metaData.type}`); this.eventSource.onmessage = async (e) => { const data = safeJSONParse(e.data); if (data?.error) { @@ -282,10 +325,7 @@ class TfGraphDashboard extends LegacyElementMixin(PolymerElement) { this.eventSource?.close(); this.eventSource = null; try { - await Promise.all([ - this.loadGraphConfig(metaData), - this.loadGraphAllNodeList(metaData), - ]); + await this.loadGraphConfig(metaData) this.initGraphBoard(); // 先读取配置,再加载图,顺序很重要 this.progreesLoading('初始化完成', '请稍后', data); } catch (error) { @@ -303,7 +343,7 @@ class TfGraphDashboard extends LegacyElementMixin(PolymerElement) { } this.eventSource?.close(); }; - }; + } loadGraphConfig = async (metaData) => { const { success, data, error } = await this.useGraphAscend.loadGraphConfig(metaData); @@ -317,14 +357,28 @@ class TfGraphDashboard extends LegacyElementMixin(PolymerElement) { this.set('task', config.task); this.set('matchedConfigFiles', ['未选择', ...config.matchedConfigFiles]); const microstepsCount = Number(config.microSteps); + const ranks = config.ranks || [0]; + const steps = config.steps || [0]; if (microstepsCount) { const microstepsArray = Array.from({ length: microstepsCount + 1 }, (_, index) => ({ label: index === 0 ? 'ALL' : String(index - 1), value: index - 1, })); this.set('microsteps', microstepsArray); - } else { - this.set('microsteps', []); + } + if (ranks.length > 0) { + const ranksArray = ranks.map((rank) => ({ + label: rank, + value: rank, + })) + this.set('ranks', ranksArray); + } + if (steps.length > 0) { + const stepsArray = steps.map((step) => ({ + label: step, + value: step, + })) + this.set('steps', stepsArray); } } else { Notification.show(`图配置加载失败:${error}`, { diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/type/index.d.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/type/index.d.ts index 04066670d861cb2c4f9ece15ce037617afe5b121..dc9b7455e37f692a940c49f2fc79a3c4e7998982 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/type/index.d.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/type/index.d.ts @@ -21,13 +21,18 @@ export interface ProgressType { done?: boolean; } +export interface SelectedItemType { + value: number; + label: string; +} + export interface SelectionType { run: string; tag: string; - type: string; + type: 'json' | 'db'; microStep?: number; - step?: string; - rank?: string; + step?: number; + rank?: number; } export interface GraphConfigType { @@ -41,6 +46,8 @@ export interface GraphConfigType { isSingleGraph: boolean; matchedConfigFiles: string[]; task: string; + ranks: number[]; + steps: number[] } export interface GraphAllNodeType { diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/useGraphAscend.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/useGraphAscend.ts index b83d74219a19b6440e00f9a2412c2dd67338e84f..d340d8a6fb9562de017ff6301f7497291f13c38f 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/useGraphAscend.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/useGraphAscend.ts @@ -38,12 +38,12 @@ const useGraphAscend = () => { } }; const loadGraphConfig = async (metaData: SelectionType): Promise => { - const result = await request({ url: 'loadGraphConfigInfo', method: 'POST', data: { metaData }}); // 获取异步的 ArrayBuffer + const result = await request({ url: 'loadGraphConfigInfo', method: 'POST', data: { metaData } }); // 获取异步的 ArrayBuffer return result; }; const loadGraphAllNodeList = async (metaData: SelectionType): Promise => { - const result = await request({ url: 'loadGraphAllNodeList', method: 'POST', data: { metaData }}); // 获取异步的 ArrayBuffer + const result = await request({ url: 'loadGraphAllNodeList', method: 'POST', data: { metaData } }); // 获取异步的 ArrayBuffer return result; }; diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_board/components/hierarchy/index.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_board/components/hierarchy/index.ts index 692c4d0893533389d2181e40be9068c77062b782..111ea4f6b16b368d0b110db346ff4358169cf905 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_board/components/hierarchy/index.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_board/components/hierarchy/index.ts @@ -38,6 +38,7 @@ import { Notification } from '@vaadin/notification'; import type { UseGraphType } from '../../type'; import type { HierarchyNodeType, ContextMenuItem, PreProcessDataConfigType, GraphType } from '../../type'; import type { ContextMenuItemSelectedEvent } from '@vaadin/context-menu'; +import type { SelectionType } from '../../../graph_ascend/type'; const EXPAND_MATCHED_NODE = 1; const DATA_COMMUNICATION = 2; @@ -131,7 +132,7 @@ class Hierarchy extends PolymerElement { isOverflowFilter: boolean = false; @property({ type: Object }) - selection = {}; + selection: SelectionType = {} as SelectionType; @property({ type: Boolean, notify: true }) selectedNode = ''; @@ -356,7 +357,7 @@ class Hierarchy extends PolymerElement { bindUpdateHierarchyDataEvent() { const onUpdateHierarchyDataEvent = async () => { this.set('loading', true); - const { success, data, error } = await this.useGraph.updateHierarchyData(this.graphType); + const { success, data, error } = await this.useGraph.updateHierarchyData(this.graphType, this.selection); this.set('loading', false); if (success) { const hierarchyObject = data; @@ -435,12 +436,13 @@ class Hierarchy extends PolymerElement { if (target.tagName.toLowerCase() !== 'rect' && target.tagName.toLowerCase() !== 'text') { event.stopPropagation(); } else { - const contextMenuItems: Array = [ - { + const contextMenuItems: Array = []; + if (this.graphType != 'Single') { + contextMenuItems.push({ text: '展开对应侧节点', type: EXPAND_MATCHED_NODE, - }, - ]; + }) + } const selectedNode = target.getAttribute('name'); const nodeName = selectedNode?.replace(new RegExp(`^(${NPU_PREFIX}|${BENCH_PREFIX})`), '') ?? ''; const nodeData = this.hierarchyObject[nodeName]; @@ -534,6 +536,7 @@ class Hierarchy extends PolymerElement { this.dispatchEvent(changeMatchNodeExpandState); } const transform = this.changeNodeCenter(nodeName); + this.set('needChangeNodeCenter', true); this.renderGraph(this.hierarchyData, this.hightLightNodeName, transform); }; const onDoubleClickGraphEvent = (event) => { diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_board/components/hierarchy/useGraph.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_board/components/hierarchy/useGraph.ts index 415eac9a993ead40c1e2864e7d21b9b50014b3a7..0a874861e08774901475bf98b4096699030dcd2e 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_board/components/hierarchy/useGraph.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_board/components/hierarchy/useGraph.ts @@ -18,7 +18,7 @@ import { maybeTruncateString, darkenColor, safeJSONParse } from '../../../utils/ import request from '../../../utils/request'; import { isEmpty } from 'lodash'; import { HierarchyNodeType, PreProcessDataConfigType, GraphType } from '../../type'; - +import { SelectionType } from '../../../graph_ascend/type'; import { UseGraphType } from '../../type'; import { DURATION_TIME, @@ -99,11 +99,16 @@ const useGraph = (): UseGraphType => { if (isEmpty(node.matchedNodeLink)) { return Object.keys(colors).find((color) => colors[color].value === '无匹配节点') ?? NO_MATCHED_NODE_COLOR; } + if (graphType === 'Bench') { + return BENCH_NODE_COLOR; + } + const precisionValue = parseFloat(node.precisionIndex); return calcClolorByPrecision(precisionValue, colors); }; const calcClolorByPrecision = (precisionValue: number, colors: PreProcessDataConfigType['colors']) => { + if (isNaN(precisionValue)) { return BASE_NODE_COLOR; // 默认返回灰色 } @@ -275,7 +280,7 @@ const useGraph = (): UseGraphType => { texts.order(); }; - const changeNodeExpandState: UseGraphType['changeNodeExpandState'] = async (nodeInfo: any, metaData: any): Promise => { + const changeNodeExpandState: UseGraphType['changeNodeExpandState'] = async (nodeInfo: any, metaData: SelectionType): Promise => { try { const metaDataSafe = safeJSONParse(JSON.stringify(metaData)); const params = { @@ -297,10 +302,14 @@ const useGraph = (): UseGraphType => { } }; - const updateHierarchyData = async (graphType: string): Promise => { - const params = { graphType }; + const updateHierarchyData = async (graphType: string, metaData: SelectionType): Promise => { + try { - const result = await request({ url: 'updateHierarchyData', method: 'GET', params: params }); + const params = { + metaData, + graphType + }; + const result = await request({ url: 'updateHierarchyData', method: 'POST', data: params }); return result; } catch (err) { return { diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_board/type/index.d.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_board/type/index.d.ts index 47db174ac24e1c03e886ee563ea1e730ed115343..299a7c4fd81c992c4bae41d86680c896daff1ae8 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_board/type/index.d.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_board/type/index.d.ts @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import { SelectionType } from "../../graph_ascend/type"; export interface HierarchyNodeType { x: number; y: number; @@ -59,9 +61,9 @@ export interface UseGraphType { config: PreProcessDataConfigType, transform: { x: number; y: number; scale: number }, ) => Array; - changeNodeExpandState: (nodeInfo: any, metaData: any) => Promise; + changeNodeExpandState: (nodeInfo: any, metaData: SelectionType) => Promise; createComponent: (text, precision, colors: PreProcessDataConfigType['colors']) => any; - updateHierarchyData: (graphType: string) => Promise; + updateHierarchyData: (graphType: string, metaData: SelectionType) => Promise; } export interface TransformType { diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/components/tf_color_select/index.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/components/tf_color_select/index.ts index 07601d2c6dcd86cb8ce114f3dedb575d83e7be88..1bb53b352ad9959955f6531147ce19349ac8c7e3 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/components/tf_color_select/index.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/components/tf_color_select/index.ts @@ -20,7 +20,6 @@ import * as _ from 'lodash'; import { PolymerElement, html } from '@polymer/polymer'; import { Notification } from '@vaadin/notification'; import { customElement, property, observe } from '@polymer/decorators'; -import { fetchPbTxt, safeJSONParse } from '../../../utils'; import { NPU_PREFIX, UNMATCHED_COLOR, defaultColorSetting, defaultColorSelects } from '../../../common/constant'; import request from '../../../utils/request'; import { DarkModeMixin } from '../../../polymer/dark_mode_mixin'; @@ -246,7 +245,7 @@ class Legend extends LegacyElementMixin(DarkModeMixin(PolymerElement)) {
- ([[precisionmenu.length]]) + ([[overflowmenu.length]])