diff --git a/source/index.html b/source/index.html
index a9ecc67f6d..69a598dc57 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-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; }
@@ -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-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; }
@@ -334,6 +340,11 @@
+
diff --git a/source/view.js b/source/view.js
index 327e7862fb..2b1972efef 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,97 @@ 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';
+ defaultOption.style.fontSize = '12px';
+ defaultOption.textContent = 'All Types';
+ 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('div');
+ option.style.padding = '6px 12px';
+ option.style.cursor = 'pointer';
+ option.style.fontSize = '12px';
+ option.textContent = nodeType;
+ 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);
+ }
+
+ 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)) {
+ 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');
this._elements = [this._query, this._content];
this._query.setAttribute('id', 'search');
@@ -4359,6 +4473,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._updateDropdownSelection) {
+ this._updateDropdownSelection();
+ }
this._update();
this._host.event('open_sidebar', {
sidebar_identifier: this.identifier,