From 0aad81743f6b61f6ad79771d4f5422b40ed3200d Mon Sep 17 00:00:00 2001 From: tnzl Date: Fri, 2 Jan 2026 22:06:15 +0530 Subject: [PATCH 1/3] Add node type filter for ONNX models in search sidebar - Add dropdown filter to search sidebar for filtering nodes by type (Conv, MatMul, Add, etc.) - Filter only appears when viewing ONNX models - Collects unique node types from the model automatically - Minimal design that integrates seamlessly with search bar - Supports filtering nodes by type in combination with text search --- source/index.html | 8 ++++++- source/view.js | 55 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/source/index.html b/source/index.html index a9ecc67f6d..939c77d793 100644 --- a/source/index.html +++ b/source/index.html @@ -246,7 +246,10 @@ } .sidebar-separator { margin-bottom: 20px; } .sidebar-find-search { display: flex; align-items: center; background: #fff; border-radius: 16px; margin: 0px 20px 8px 20px; padding: 0px 8px 0px 8px; } -.sidebar-find-query { width: 100vw; background: none; font-family: inherit; font-size: 13px; padding: 8px 8px 8px 8px; border: 0; outline: 0; } +.sidebar-find-query { flex: 1; background: none; font-family: inherit; font-size: 13px; padding: 8px 8px 8px 8px; border: 0; outline: 0; } +.sidebar-find-nodetype-filter { font-family: inherit; font-size: 11px; padding: 0 16px 0 0; margin-left: 6px; margin-right: 0; border: 0; background: transparent; outline: 0; cursor: pointer; color: #999; appearance: none; -webkit-appearance: none; -moz-appearance: none; background-image: linear-gradient(45deg, transparent 50%, #bbb 50%), linear-gradient(135deg, #bbb 50%, transparent 50%); background-position: calc(100% - 6px) calc(50%), calc(100% - 2px) calc(50%); background-size: 3px 3px, 3px 3px; background-repeat: no-repeat; min-width: 60px; } +.sidebar-find-nodetype-filter:hover { color: #666; background-image: linear-gradient(45deg, transparent 50%, #888 50%), linear-gradient(135deg, #888 50%, transparent 50%); } +.sidebar-find-nodetype-filter:focus { color: #666; outline: 0; } .sidebar-find-toggle { margin-left: auto; margin-right: 2px; width: 16px; height: 16px; cursor: pointer; } .sidebar-find-toggle input[type="checkbox"] { display: none; } .sidebar-find-toggle input[type="checkbox"]:not(:checked) + svg { fill: #ccc; stroke: #ccc; } @@ -290,6 +293,9 @@ .sidebar-documentation code { background-color: #1e1e1e; } .sidebar-documentation pre { background-color: #1e1e1e; } .sidebar-find-search { background: #383838; color: #dfdfdf; border-color: #424242; } + .sidebar-find-nodetype-filter { background: transparent; color: #666; background-image: linear-gradient(45deg, transparent 50%, #777 50%), linear-gradient(135deg, #777 50%, transparent 50%); } + .sidebar-find-nodetype-filter:hover { color: #aaa; background-image: linear-gradient(45deg, transparent 50%, #999 50%), linear-gradient(135deg, #999 50%, transparent 50%); } + .sidebar-find-nodetype-filter:focus { color: #aaa; outline: 0; } .sidebar-find-toggle input[type="checkbox"]:not(:checked) + svg { fill: #555; stroke: #555; } .sidebar-find-toggle input[type="checkbox"]:checked + svg { fill: #aaa; stroke: #aaa; } .sidebar-find-content li { color: #aaaaaa; } diff --git a/source/view.js b/source/view.js index 327e7862fb..1a3d5e5c86 100644 --- a/source/view.js +++ b/source/view.js @@ -4072,13 +4072,15 @@ view.FindSidebar = class extends view.Control { query: '', node: true, connection: true, - weight: true + weight: true, + nodeType: '' }; this._toggles = { node: { hide: 'Hide Nodes', show: 'Show Nodes' }, connection: { hide: 'Hide Connections', show: 'Show Connections' }, weight: { hide: 'Hide Weights', show: 'Show Weights' } }; + this._isOnnxModel = this._view._model && this._view._model.format && this._view._model.format.startsWith('ONNX'); } get identifier() { @@ -4194,6 +4196,13 @@ view.FindSidebar = class extends view.Control { const name = node.name; const type = node.type.name; const identifier = node.identifier; + // Apply node type filter for ONNX models + if (this._isOnnxModel && this._state.nodeType && this._state.nodeType !== '') { + const nodeTypeName = type.split('.').pop(); // Get base type name (e.g., "Conv" from "ai.onnx.Conv") + if (nodeTypeName.toLowerCase() !== this._state.nodeType.toLowerCase()) { + return; // Skip nodes that don't match the selected type + } + } if ((name && this._term(name)) || (type && this._term(type)) || (identifier && this._term(identifier))) { const content = `${name || `[${type}]`}`; this._add(node, content, 'node'); @@ -4259,6 +4268,20 @@ view.FindSidebar = class extends view.Control { } } + _collectNodeTypes() { + if (!this._isOnnxModel || !this._target || !this._target.nodes) { + return []; + } + const nodeTypes = new Set(); + for (const node of this._target.nodes) { + if (node.type && node.type.name) { + const typeName = node.type.name.split('.').pop(); // Get base type name (e.g., "Conv" from "ai.onnx.Conv") + nodeTypes.add(typeName); + } + } + return Array.from(nodeTypes).sort(); + } + _update() { try { this._reset(); @@ -4295,6 +4318,33 @@ view.FindSidebar = class extends view.Control { this._search = this.createElement('div', 'sidebar-find-search'); this._query = this.createElement('input', 'sidebar-find-query'); this._search.appendChild(this._query); + + // Add node type filter dropdown for ONNX models + if (this._isOnnxModel) { + this._nodeTypeSelect = this.createElement('select', 'sidebar-find-nodetype-filter'); + this._nodeTypeSelect.setAttribute('title', 'Filter by Node Type'); + const defaultOption = this.createElement('option'); + defaultOption.value = ''; + defaultOption.textContent = 'All Types'; + this._nodeTypeSelect.appendChild(defaultOption); + + const nodeTypes = this._collectNodeTypes(); + for (const nodeType of nodeTypes) { + const option = this.createElement('option'); + option.value = nodeType; + option.textContent = nodeType; + this._nodeTypeSelect.appendChild(option); + } + + this._nodeTypeSelect.value = this._state.nodeType || ''; + this._nodeTypeSelect.addEventListener('change', (e) => { + this._state.nodeType = e.target.value; + this.emit('state-changed', this._state); + this._update(); + }); + this._search.appendChild(this._nodeTypeSelect); + } + this._content = this.createElement('ol', 'sidebar-find-content'); this._elements = [this._query, this._content]; this._query.setAttribute('id', 'search'); @@ -4359,6 +4409,9 @@ view.FindSidebar = class extends view.Control { toggle.checkbox.checked = this._state[name]; toggle.element.setAttribute('title', this._state[name] ? toggle.hide : toggle.show); } + if (this._isOnnxModel && this._nodeTypeSelect) { + this._nodeTypeSelect.value = this._state.nodeType || ''; + } this._update(); this._host.event('open_sidebar', { sidebar_identifier: this.identifier, From 8112e195dd5e65ed59a146b05af8efb4613b08e2 Mon Sep 17 00:00:00 2001 From: tnzl Date: Fri, 2 Jan 2026 22:25:35 +0530 Subject: [PATCH 2/3] Replace dropdown with filter icon button for node type filter - Replace select dropdown with filter icon button matching toggle buttons style - Add filter icon SVG symbol (three horizontal lines) - Implement dropdown menu that appears on icon click - Update CSS for dropdown menu with dark mode support - Filter icon matches the theme of existing search bar buttons --- source/index.html | 17 +++++--- source/view.js | 98 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 92 insertions(+), 23 deletions(-) diff --git a/source/index.html b/source/index.html index 939c77d793..69a598dc57 100644 --- a/source/index.html +++ b/source/index.html @@ -247,9 +247,9 @@ .sidebar-separator { margin-bottom: 20px; } .sidebar-find-search { display: flex; align-items: center; background: #fff; border-radius: 16px; margin: 0px 20px 8px 20px; padding: 0px 8px 0px 8px; } .sidebar-find-query { flex: 1; background: none; font-family: inherit; font-size: 13px; padding: 8px 8px 8px 8px; border: 0; outline: 0; } -.sidebar-find-nodetype-filter { font-family: inherit; font-size: 11px; padding: 0 16px 0 0; margin-left: 6px; margin-right: 0; border: 0; background: transparent; outline: 0; cursor: pointer; color: #999; appearance: none; -webkit-appearance: none; -moz-appearance: none; background-image: linear-gradient(45deg, transparent 50%, #bbb 50%), linear-gradient(135deg, #bbb 50%, transparent 50%); background-position: calc(100% - 6px) calc(50%), calc(100% - 2px) calc(50%); background-size: 3px 3px, 3px 3px; background-repeat: no-repeat; min-width: 60px; } -.sidebar-find-nodetype-filter:hover { color: #666; background-image: linear-gradient(45deg, transparent 50%, #888 50%), linear-gradient(135deg, #888 50%, transparent 50%); } -.sidebar-find-nodetype-filter:focus { color: #666; outline: 0; } +.sidebar-find-nodetype-dropdown { position: absolute; right: 0; top: 100%; margin-top: 4px; background: #fff; border: 1px solid #ddd; border-radius: 4px; box-shadow: 0 2px 8px rgba(0,0,0,0.15); z-index: 1000; min-width: 120px; max-height: 200px; overflow-y: auto; } +.sidebar-find-nodetype-dropdown > div { padding: 6px 12px; cursor: pointer; font-size: 12px; color: #333; } +.sidebar-find-nodetype-dropdown > div:hover { background: #f5f5f5; } .sidebar-find-toggle { margin-left: auto; margin-right: 2px; width: 16px; height: 16px; cursor: pointer; } .sidebar-find-toggle input[type="checkbox"] { display: none; } .sidebar-find-toggle input[type="checkbox"]:not(:checked) + svg { fill: #ccc; stroke: #ccc; } @@ -293,9 +293,9 @@ .sidebar-documentation code { background-color: #1e1e1e; } .sidebar-documentation pre { background-color: #1e1e1e; } .sidebar-find-search { background: #383838; color: #dfdfdf; border-color: #424242; } - .sidebar-find-nodetype-filter { background: transparent; color: #666; background-image: linear-gradient(45deg, transparent 50%, #777 50%), linear-gradient(135deg, #777 50%, transparent 50%); } - .sidebar-find-nodetype-filter:hover { color: #aaa; background-image: linear-gradient(45deg, transparent 50%, #999 50%), linear-gradient(135deg, #999 50%, transparent 50%); } - .sidebar-find-nodetype-filter:focus { color: #aaa; outline: 0; } + .sidebar-find-nodetype-dropdown { background: #383838; border-color: #555; } + .sidebar-find-nodetype-dropdown > div { color: #dfdfdf; } + .sidebar-find-nodetype-dropdown > div:hover { background: #424242; } .sidebar-find-toggle input[type="checkbox"]:not(:checked) + svg { fill: #555; stroke: #555; } .sidebar-find-toggle input[type="checkbox"]:checked + svg { fill: #aaa; stroke: #aaa; } .sidebar-find-content li { color: #aaaaaa; } @@ -340,6 +340,11 @@

+ + + + + diff --git a/source/view.js b/source/view.js index 1a3d5e5c86..da9beb0bc5 100644 --- a/source/view.js +++ b/source/view.js @@ -4319,30 +4319,94 @@ view.FindSidebar = class extends view.Control { this._query = this.createElement('input', 'sidebar-find-query'); this._search.appendChild(this._query); - // Add node type filter dropdown for ONNX models + // Add node type filter button for ONNX models if (this._isOnnxModel) { - this._nodeTypeSelect = this.createElement('select', 'sidebar-find-nodetype-filter'); - this._nodeTypeSelect.setAttribute('title', 'Filter by Node Type'); - const defaultOption = this.createElement('option'); - defaultOption.value = ''; + const container = this.createElement('div'); + container.style.position = 'relative'; + + this._nodeTypeFilterButton = this.createElement('label', 'sidebar-find-toggle'); + this._nodeTypeFilterButton.innerHTML = ``; + this._nodeTypeFilterButton.setAttribute('title', 'Filter by Node Type'); + + // Create dropdown menu (initially hidden) + this._nodeTypeDropdown = this.createElement('div', 'sidebar-find-nodetype-dropdown'); + this._nodeTypeDropdown.style.display = 'none'; + + const defaultOption = this.createElement('div'); + defaultOption.style.padding = '6px 12px'; + defaultOption.style.cursor = 'pointer'; + defaultOption.style.fontSize = '12px'; defaultOption.textContent = 'All Types'; - this._nodeTypeSelect.appendChild(defaultOption); + defaultOption.addEventListener('click', () => { + this._state.nodeType = ''; + this._updateDropdownSelection(); + this.emit('state-changed', this._state); + this._update(); + this._nodeTypeDropdown.style.display = 'none'; + }); + this._nodeTypeDropdown.appendChild(defaultOption); const nodeTypes = this._collectNodeTypes(); for (const nodeType of nodeTypes) { - const option = this.createElement('option'); - option.value = nodeType; + const option = this.createElement('div'); + option.style.padding = '6px 12px'; + option.style.cursor = 'pointer'; + option.style.fontSize = '12px'; option.textContent = nodeType; - this._nodeTypeSelect.appendChild(option); + option.addEventListener('click', () => { + this._state.nodeType = nodeType; + this._updateDropdownSelection(); + this.emit('state-changed', this._state); + this._update(); + this._nodeTypeDropdown.style.display = 'none'; + }); + option.addEventListener('mouseenter', () => { + if (this._state.nodeType !== nodeType) { + option.style.background = '#f5f5f5'; + } + }); + option.addEventListener('mouseleave', () => { + if (this._state.nodeType !== nodeType) { + option.style.background = 'transparent'; + } + }); + this._nodeTypeDropdown.appendChild(option); } - this._nodeTypeSelect.value = this._state.nodeType || ''; - this._nodeTypeSelect.addEventListener('change', (e) => { - this._state.nodeType = e.target.value; - this.emit('state-changed', this._state); - this._update(); + container.appendChild(this._nodeTypeFilterButton); + container.appendChild(this._nodeTypeDropdown); + + this._nodeTypeFilterButton.addEventListener('click', (e) => { + e.stopPropagation(); + const isVisible = this._nodeTypeDropdown.style.display !== 'none'; + this._nodeTypeDropdown.style.display = isVisible ? 'none' : 'block'; }); - this._search.appendChild(this._nodeTypeSelect); + + // Close dropdown when clicking outside + const closeDropdown = (e) => { + if (container && !container.contains(e.target)) { + this._nodeTypeDropdown.style.display = 'none'; + } + }; + this._host.document.addEventListener('click', closeDropdown); + + const nodeTypesList = nodeTypes; + this._updateDropdownSelection = () => { + const options = this._nodeTypeDropdown.children; + for (let i = 0; i < options.length; i++) { + const option = options[i]; + const isSelected = (i === 0 && this._state.nodeType === '') || + (i > 0 && this._state.nodeType === nodeTypesList[i - 1]); + option.style.color = isSelected ? '#2e6bd2' : ''; + option.style.fontWeight = isSelected ? 'bold' : 'normal'; + if (!isSelected) { + option.style.background = 'transparent'; + } + } + }; + + this._search.appendChild(container); + this._updateDropdownSelection(); } this._content = this.createElement('ol', 'sidebar-find-content'); @@ -4409,8 +4473,8 @@ view.FindSidebar = class extends view.Control { toggle.checkbox.checked = this._state[name]; toggle.element.setAttribute('title', this._state[name] ? toggle.hide : toggle.show); } - if (this._isOnnxModel && this._nodeTypeSelect) { - this._nodeTypeSelect.value = this._state.nodeType || ''; + if (this._isOnnxModel && this._updateDropdownSelection) { + this._updateDropdownSelection(); } this._update(); this._host.event('open_sidebar', { From 77d799b7b2ca17f0dc121ab3157f49740a70a549 Mon Sep 17 00:00:00 2001 From: tnzl Date: Fri, 2 Jan 2026 22:44:43 +0530 Subject: [PATCH 3/3] Fix linting errors: remove trailing spaces --- source/view.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/source/view.js b/source/view.js index da9beb0bc5..2b1972efef 100644 --- a/source/view.js +++ b/source/view.js @@ -4318,20 +4318,20 @@ view.FindSidebar = class extends view.Control { this._search = this.createElement('div', 'sidebar-find-search'); this._query = this.createElement('input', 'sidebar-find-query'); this._search.appendChild(this._query); - + // Add node type filter button for ONNX models if (this._isOnnxModel) { const container = this.createElement('div'); container.style.position = 'relative'; - + this._nodeTypeFilterButton = this.createElement('label', 'sidebar-find-toggle'); this._nodeTypeFilterButton.innerHTML = ``; this._nodeTypeFilterButton.setAttribute('title', 'Filter by Node Type'); - + // Create dropdown menu (initially hidden) this._nodeTypeDropdown = this.createElement('div', 'sidebar-find-nodetype-dropdown'); this._nodeTypeDropdown.style.display = 'none'; - + const defaultOption = this.createElement('div'); defaultOption.style.padding = '6px 12px'; defaultOption.style.cursor = 'pointer'; @@ -4345,7 +4345,7 @@ view.FindSidebar = class extends view.Control { this._nodeTypeDropdown.style.display = 'none'; }); this._nodeTypeDropdown.appendChild(defaultOption); - + const nodeTypes = this._collectNodeTypes(); for (const nodeType of nodeTypes) { const option = this.createElement('div'); @@ -4372,16 +4372,16 @@ view.FindSidebar = class extends view.Control { }); this._nodeTypeDropdown.appendChild(option); } - + container.appendChild(this._nodeTypeFilterButton); container.appendChild(this._nodeTypeDropdown); - + this._nodeTypeFilterButton.addEventListener('click', (e) => { e.stopPropagation(); const isVisible = this._nodeTypeDropdown.style.display !== 'none'; this._nodeTypeDropdown.style.display = isVisible ? 'none' : 'block'; }); - + // Close dropdown when clicking outside const closeDropdown = (e) => { if (container && !container.contains(e.target)) { @@ -4389,13 +4389,13 @@ view.FindSidebar = class extends view.Control { } }; this._host.document.addEventListener('click', closeDropdown); - + const nodeTypesList = nodeTypes; this._updateDropdownSelection = () => { const options = this._nodeTypeDropdown.children; for (let i = 0; i < options.length; i++) { const option = options[i]; - const isSelected = (i === 0 && this._state.nodeType === '') || + const isSelected = (i === 0 && this._state.nodeType === '') || (i > 0 && this._state.nodeType === nodeTypesList[i - 1]); option.style.color = isSelected ? '#2e6bd2' : ''; option.style.fontWeight = isSelected ? 'bold' : 'normal'; @@ -4404,11 +4404,11 @@ view.FindSidebar = class extends view.Control { } } }; - + this._search.appendChild(container); this._updateDropdownSelection(); } - + this._content = this.createElement('ol', 'sidebar-find-content'); this._elements = [this._query, this._content]; this._query.setAttribute('id', 'search');