diff --git a/packages/node_modules/@node-red/editor-client/src/js/nodes.js b/packages/node_modules/@node-red/editor-client/src/js/nodes.js index 97ea4e992..e5a66ccaf 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/nodes.js +++ b/packages/node_modules/@node-red/editor-client/src/js/nodes.js @@ -1811,6 +1811,7 @@ RED.nodes = (function() { * - id:import - import as-is * - id:copy - import with new id * - id:replace - import over the top of existing + * - modules: map of module:version - hints for unknown nodes */ function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) { const defOpts = { generateIds: false, addFlow: false, markChanged: false, reimport: false, importMap: {} } @@ -1946,12 +1947,54 @@ RED.nodes = (function() { } if (!isInitialLoad && unknownTypes.length > 0) { - var typeList = $("
"+RED._("clipboard.importUnrecognised",{count:unknownTypes.length})+"
"+typeList,"error",false,10000); + const notificationOptions = { + type: "error", + fixed: false, + timeout: 10000, + } + let unknownNotification + if (options.modules) { + notificationOptions.fixed = true + delete notificationOptions.timeout + // We have module hint list from imported global-config + // Provide option to install missing modules + notificationOptions.buttons = [ + { + text: "Manage dependencies", + class:"primary", + click: function(e) { + unknownNotification.close(); + + RED.actions.invoke('core:manage-palette', { + view: 'install', + filter: '"' + Object.keys(options.modules).join('", "') + '"' + }) + } + } + ] + let moduleList = $(""+RED._("clipboard.importWithModuleInfo")+"
"+ + ""+RED._("clipboard.importWithModuleInfoDesc")+"
"+ + moduleList, + notificationOptions + ); + } else { + var typeList = $(""+RED._("clipboard.importUnrecognised",{count:unknownTypes.length})+"
"+typeList, + notificationOptions + ); + } } var activeWorkspace = RED.workspaces.active(); @@ -2319,6 +2362,9 @@ RED.nodes = (function() { delete node.z; } } + const unknownTypeDef = RED.nodes.getType('unknown') + node._def.oneditprepare = unknownTypeDef.oneditprepare + var orig = {}; for (var p in n) { if (n.hasOwnProperty(p) && p!="x" && p!="y" && p!="z" && p!="id" && p!="wires") { @@ -2328,6 +2374,10 @@ RED.nodes = (function() { node._orig = orig; node.name = n.type; node.type = "unknown"; + if (options.modules) { + // We have a module hint list. Attach to the unknown node so we can reference it later + node.modules = Object.keys(options.modules) + } } if (node._def.category != "config") { if (n.hasOwnProperty('inputs')) { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js index 3d949f8ca..44e6146c8 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/palette-editor.js @@ -625,8 +625,20 @@ RED.palette.editor = (function() { } }) - RED.actions.add("core:manage-palette",function() { + RED.actions.add("core:manage-palette", function(opts) { RED.userSettings.show('palette'); + if (opts) { + if (opts.view) { + editorTabs.activateTab(opts.view); + } + if (opts.filter) { + if (opts.view === 'install') { + searchInput.searchBox('value', opts.filter) + } else if (opts.view === 'nodes') { + filterInput.searchBox('value', opts.filter) + } + } + } }); RED.events.on('registry:module-updated', function(ns) { @@ -982,8 +994,35 @@ RED.palette.editor = (function() { change: function() { var searchTerm = $(this).val().trim().toLowerCase(); if (searchTerm.length > 0 || loadedList.length < 20) { + const searchTerms = [] + searchTerm.split(',').forEach(term => { + term = term.trim() + if (term) { + const isExact = term[0] === '"' && term[term.length-1] === '"' + searchTerms.push({ + exact: isExact, + term: isExact ? term.substring(1,term.length-1) : term + }) + } + }) filteredList = loadedList.filter(function(m) { - return (m.index.indexOf(searchTerm) > -1); + for (let i = 0; i < searchTerms.length; i++) { + const location = m.index.indexOf(searchTerms[i].term) + if ( + ( + searchTerms[i].exact && + ( + location === 0 && ( + m.index.length === searchTerms[i].term.length || + m.index[searchTerms[i].term.length] === ',' + ) + ) + ) || + (!searchTerms[i].exact && location > -1)) { + return true + } + } + return false }).map(function(f) { return {info:f}}); refreshFilteredItems(); searchInput.searchBox('count',filteredList.length+" / "+loadedList.length); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 14f79977a..5872c0f40 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -5664,27 +5664,29 @@ RED.view = (function() { activeSubflowChanged = activeSubflow.changed; } var filteredNodesToImport = nodesToImport; - var globalConfig = null; - var gconf = null; + var importedGlobalConfig = null; + var existingGlobalConfig = null; RED.nodes.eachConfig(function (conf) { if (conf.type === "global-config") { - gconf = conf; + existingGlobalConfig = conf; } }); - if (gconf) { + if (existingGlobalConfig) { filteredNodesToImport = nodesToImport.filter(function (n) { - return (n.type !== "global-config"); - }); - globalConfig = nodesToImport.find(function (n) { - return (n.type === "global-config"); + if (n.type === "global-config") { + importedGlobalConfig = n + return false + } + return true }); } var result = RED.nodes.import(filteredNodesToImport,{ generateIds: options.generateIds, addFlow: addNewFlow, importMap: options.importMap, - markChanged: true + markChanged: true, + modules: importedGlobalConfig ? (importedGlobalConfig.modules || {}) : {} }); if (result) { var new_nodes = result.nodes; @@ -5808,38 +5810,38 @@ RED.view = (function() { } } - if (globalConfig) { + if (importedGlobalConfig) { // merge global env to existing global-config - var env0 = gconf.env || []; - var env1 = globalConfig.env || [] - var newEnv = Array.from(env0); + var existingEnv = existingGlobalConfig.env || []; + var importedEnv = importedGlobalConfig.env || [] + var newEnv = Array.from(existingEnv); var changed = false; - env1.forEach(function (item1) { - var index = newEnv.findIndex(function (item0) { - return (item0.name === item1.name); + importedEnv.forEach(function (importedItem) { + var index = newEnv.findIndex(function (existingItem) { + return (existingItem.name === importedItem.name); }); if (index >= 0) { - var item0 = newEnv[index]; - if ((item0.type !== item1.type) || - (item0.value !== item1.value)) { - newEnv[index] = item1; + const existingItem = newEnv[index]; + if ((existingItem.type !== importedItem.type) || + (existingItem.value !== importedItem.value)) { + newEnv[index] = importedItem; changed = true; } } else { - newEnv.push(item1); + newEnv.push(importedItem); changed = true; } }); - if(changed) { - gconf.env = newEnv; + if (changed) { + existingGlobalConfig.env = newEnv; var replaceEvent = { t: "edit", - node: gconf, + node: existingGlobalConfig, changed: true, changes: { - env: env0 + env: existingEnv } }; historyEvent = { diff --git a/packages/node_modules/@node-red/nodes/core/common/98-unknown.html b/packages/node_modules/@node-red/nodes/core/common/98-unknown.html index 52071c30f..282ad3415 100644 --- a/packages/node_modules/@node-red/nodes/core/common/98-unknown.html +++ b/packages/node_modules/@node-red/nodes/core/common/98-unknown.html @@ -1,14 +1,22 @@