From 2e57d80959845e265a31af713859a2ee549428b9 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Mon, 13 Feb 2023 14:50:07 +0900 Subject: [PATCH 01/10] add show-global-env action --- .../@node-red/editor-client/locales/ja/editor.json | 3 ++- .../node_modules/@node-red/editor-client/src/js/ui/env-var.js | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json index 4878763a6..01a5faed9 100644 --- a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json @@ -1376,6 +1376,7 @@ "copy-item-url": "要素のURLをコピー", "copy-item-edit-url": "要素の編集URLをコピー", "move-flow-to-start": "フローを先頭に移動", - "move-flow-to-end": "フローを末尾に移動" + "move-flow-to-end": "フローを末尾に移動", + "show-global-env": "大域環境変数を表示" } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js b/packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js index ab071a1d4..db1a8e86f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/env-var.js @@ -166,6 +166,10 @@ RED.envVar = (function() { } } }); + + RED.actions.add("core:show-global-env", function() { + RED.userSettings.show('envvar'); + }); } return { From 96cd823fea2b7f6f042346484750f686bb02defe Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Tue, 21 Feb 2023 20:31:12 +0900 Subject: [PATCH 02/10] Update packages/node_modules/@node-red/editor-client/locales/ja/editor.json thank you! Co-authored-by: Nick O'Leary --- .../node_modules/@node-red/editor-client/locales/ja/editor.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json index 01a5faed9..b7351d985 100644 --- a/packages/node_modules/@node-red/editor-client/locales/ja/editor.json +++ b/packages/node_modules/@node-red/editor-client/locales/ja/editor.json @@ -1377,6 +1377,6 @@ "copy-item-edit-url": "要素の編集URLをコピー", "move-flow-to-start": "フローを先頭に移動", "move-flow-to-end": "フローを末尾に移動", - "show-global-env": "大域環境変数を表示" + "show-global-env": "大域環境変数を表示" } } From 363a8b858826fce6b24b38cc6b9219352a6ded44 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 23 Feb 2023 22:48:08 +0000 Subject: [PATCH 03/10] Add change icon to tabs --- .../@node-red/editor-client/src/js/history.js | 5 +- .../@node-red/editor-client/src/js/nodes.js | 148 +++++++++++++++++- .../editor-client/src/js/ui/common/tabs.js | 1 - .../editor-client/src/js/ui/contextMenu.js | 3 +- .../editor-client/src/js/ui/deploy.js | 37 ++++- .../editor-client/src/js/ui/group.js | 3 +- .../editor-client/src/js/ui/tab-config.js | 8 +- .../editor-client/src/js/ui/view-tools.js | 3 +- .../@node-red/editor-client/src/js/ui/view.js | 34 ++-- .../editor-client/src/js/ui/workspaces.js | 11 ++ .../editor-client/src/sass/workspace.scss | 9 ++ 11 files changed, 231 insertions(+), 31 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/history.js b/packages/node_modules/@node-red/editor-client/src/js/history.js index 977ecb187..a95042cc0 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/history.js +++ b/packages/node_modules/@node-red/editor-client/src/js/history.js @@ -421,6 +421,9 @@ RED.history = (function() { ev.node[i] = ev.changes[i]; } } + ev.node.dirty = true; + ev.node.changed = ev.changed; + var eventType; switch(ev.node.type) { case 'tab': eventType = "flows"; break; @@ -511,8 +514,6 @@ RED.history = (function() { inverseEv.links.push(ev.createdLinks[i]); } } - ev.node.dirty = true; - ev.node.changed = ev.changed; } else if (ev.t == "createSubflow") { inverseEv = { t: "deleteSubflow", 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 36bc2a7fa..0c8bc83b6 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 @@ -46,6 +46,9 @@ RED.nodes = (function() { function setDirty(d) { dirty = d; + if (!d) { + allNodes.clearState() + } RED.events.emit("workspace:dirty",{dirty:dirty}); } @@ -238,22 +241,36 @@ RED.nodes = (function() { // allNodes holds information about the Flow nodes. var allNodes = (function() { + // Map node.id -> node var nodes = {}; + // Map tab.id -> Array of nodes on that tab var tabMap = {}; + // Map tab.id -> Set of dirty object ids on that tab + var tabDirtyMap = {}; + // Map tab.id -> Set of object ids of things deleted from the tab that weren't otherwise dirty + var tabDeletedNodesMap = {}; + // Set of object ids of things added to a tab after initial import + var addedDirtyObjects = new Set() + var api = { addTab: function(id) { tabMap[id] = []; + tabDirtyMap[id] = new Set(); + tabDeletedNodesMap[id] = new Set(); }, hasTab: function(z) { return tabMap.hasOwnProperty(z) }, removeTab: function(id) { delete tabMap[id]; + delete tabDirtyMap[id]; + delete tabDeletedNodesMap[id]; }, addNode: function(n) { nodes[n.id] = n; if (tabMap.hasOwnProperty(n.z)) { tabMap[n.z].push(n); + api.addObjectToWorkspace(n.z, n.id, n.changed || n.moved) } else { console.warn("Node added to unknown tab/subflow:",n); tabMap["_"] = tabMap["_"] || []; @@ -267,8 +284,37 @@ RED.nodes = (function() { if (i > -1) { tabMap[n.z].splice(i,1); } + api.removeObjectFromWorkspace(n.z, n.id) } }, + /** + * Add an object to our dirty/clean tracking state + * @param {String} z + * @param {String} id + * @param {Boolean} isDirty + */ + addObjectToWorkspace: function (z, id, isDirty) { + if (isDirty) { + addedDirtyObjects.add(id) + } + if (tabDeletedNodesMap[z].has(id)) { + tabDeletedNodesMap[z].delete(id) + } + api.markNodeDirty(z, id, isDirty) + }, + /** + * Remove an object from our dirty/clean tracking state + * @param {String} z + * @param {String} id + */ + removeObjectFromWorkspace: function (z, id) { + if (!addedDirtyObjects.has(id)) { + tabDeletedNodesMap[z].add(id) + } else { + addedDirtyObjects.delete(id) + } + api.markNodeDirty(z, id, false) + }, hasNode: function(id) { return nodes.hasOwnProperty(id); }, @@ -433,6 +479,33 @@ RED.nodes = (function() { clear: function() { nodes = {}; tabMap = {}; + tabDirtyMap = {}; + tabDeletedNodesMap = {}; + addedDirtyObjects = new Set(); + }, + /** + * Clear all internal state on what is dirty. + */ + clearState: function () { + // Called when a deploy happens, we can forget about added/remove + // items as they have now been deployed. + addedDirtyObjects = new Set() + const flowsToCheck = new Set() + for (const [z, set] of Object.entries(tabDeletedNodesMap)) { + if (set.size > 0) { + set.clear() + flowsToCheck.add(z) + } + } + for (const [z, set] of Object.entries(tabDirtyMap)) { + if (set.size > 0) { + set.clear() + flowsToCheck.add(z) + } + } + for (const z of flowsToCheck) { + api.checkTabState(z) + } }, eachNode: function(cb) { var nodeList,i,j; @@ -510,6 +583,36 @@ RED.nodes = (function() { B._reordered = true; return orderMap[A.id] - orderMap[B.id]; }) + }, + /** + * Update our records if an object is dirty or not + * @param {String} z tab id + * @param {String} id object id + * @param {Boolean} dirty whether the object is dirty or not + */ + markNodeDirty: function(z, id, dirty) { + if (tabDirtyMap[z]) { + if (dirty) { + tabDirtyMap[z].add(id) + } else { + tabDirtyMap[z].delete(id) + } + api.checkTabState(z) + } + }, + /** + * Check if a tab should update its contentsChange flag + * @param {String} z tab id + */ + checkTabState: function (z) { + const ws = workspaces[z] + if (ws) { + const contentsChanged = tabDirtyMap[z].size > 0 || tabDeletedNodesMap[z].size > 0 + if (!!ws.contentsChanged !== contentsChanged) { + ws.contentsChanged = contentsChanged + RED.events.emit("flows:change", ws); + } + } } } return api; @@ -597,6 +700,11 @@ RED.nodes = (function() { throw new Error(`Cannot modified property '${prop}' of locked object '${node.type}:${node.id}'`) } } + if (node.z && (prop === 'changed' || prop === 'moved')) { + setTimeout(() => { + allNodes.markNodeDirty(node.z, node.id, node.changed || node.moved) + }, 0) + } node[prop] = value; return true } @@ -666,10 +774,16 @@ RED.nodes = (function() { } if (l.source.z === l.target.z && linkTabMap[l.source.z]) { linkTabMap[l.source.z].push(l); + allNodes.addObjectToWorkspace(l.source.z, getLinkId(l), true) } RED.events.emit("links:add",l); } + function getLinkId(link) { + return link.source.id + ':' + link.sourcePort + ':' + link.target.id + } + + function getNode(id) { if (id in configNodes) { return configNodes[id]; @@ -864,6 +978,7 @@ RED.nodes = (function() { if (index !== -1) { linkTabMap[l.source.z].splice(index,1) } + allNodes.removeObjectFromWorkspace(l.source.z, getLinkId(l)) } } RED.events.emit("links:remove",l); @@ -1688,6 +1803,7 @@ RED.nodes = (function() { * Options: * - generateIds - whether to replace all node ids * - addFlow - whether to import nodes to a new tab + * - markChanged - whether to set changed=true on all newly imported objects * - reimport - if node has a .z property, dont overwrite it * Only applicible when `generateIds` is false * - importMap - how to resolve any conflicts. @@ -1696,7 +1812,7 @@ RED.nodes = (function() { * - id:replace - import over the top of existing */ function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) { - const defOpts = { generateIds: false, addFlow: false, reimport: false, importMap: {} } + const defOpts = { generateIds: false, addFlow: false, markChanged: false, reimport: false, importMap: {} } options = Object.assign({}, defOpts, options) options.importMap = options.importMap || {} const createNewIds = options.generateIds; @@ -1722,7 +1838,7 @@ RED.nodes = (function() { newNodes = newNodesObj; } - if (!$.isArray(newNodes)) { + if (!Array.isArray(newNodes)) { newNodes = [newNodes]; } @@ -2020,6 +2136,9 @@ RED.nodes = (function() { if (!n.z) { delete configNode.z; } + if (options.markChanged) { + configNode.changed = true + } if (n.hasOwnProperty('d')) { configNode.d = n.d; } @@ -2082,6 +2201,9 @@ RED.nodes = (function() { if (n.hasOwnProperty('g')) { node.g = n.g; } + if (options.markChanged) { + node.changed = true + } if (createNewIds || options.importMap[n.id] === "copy") { if (subflow_denylist[n.z]) { continue; @@ -2595,6 +2717,7 @@ RED.nodes = (function() { groupsByZ[group.z] = groupsByZ[group.z] || []; groupsByZ[group.z].push(group); groups[group.id] = group; + allNodes.addObjectToWorkspace(group.z, group.id, group.changed || group.moved) RED.events.emit("groups:add",group); return group } @@ -2611,7 +2734,7 @@ RED.nodes = (function() { } } RED.group.markDirty(group); - + allNodes.removeObjectFromWorkspace(group.z, group.id) delete groups[group.id]; RED.events.emit("groups:remove",group); } @@ -2626,6 +2749,7 @@ RED.nodes = (function() { if (!nodeLinks[junction.id]) { nodeLinks[junction.id] = {in:[],out:[]}; } + allNodes.addObjectToWorkspace(junction.z, junction.id, junction.changed || junction.moved) RED.events.emit("junctions:add", junction) return junction } @@ -2637,6 +2761,7 @@ RED.nodes = (function() { } delete junctions[junction.id] delete nodeLinks[junction.id]; + allNodes.removeObjectFromWorkspace(junction.z, junction.id) RED.events.emit("junctions:remove", junction) var removedLinks = links.filter(function(l) { return (l.source === junction) || (l.target === junction); }); @@ -2874,6 +2999,9 @@ RED.nodes = (function() { RED.view.redraw(true); } }); + RED.events.on('deploy', function () { + allNodes.clearState() + }) }, registry:registry, setNodeList: registry.setNodeList, @@ -2976,6 +3104,20 @@ RED.nodes = (function() { } } }, + eachGroup: function(cb) { + for (var group of Object.values(groups)) { + if (cb(group) === false) { + break + } + } + }, + eachJunction: function(cb) { + for (var junction of Object.values(junctions)) { + if (cb(junction) === false) { + break + } + } + }, node: getNode, diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js index fd8c5c62d..abb76e622 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/tabs.js @@ -845,7 +845,6 @@ RED.tabs = (function() { var badges = $('').appendTo(li); if (options.onselect) { - $('').appendTo(badges); $('').appendTo(badges); } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js index 57d5bbcf9..a41ad8fd0 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js @@ -79,7 +79,8 @@ RED.contextMenu = (function () { w: 0, h: 0, outputs: 1, inputs: 1, - dirty: true + dirty: true, + moved: true } const historyEvent = { dirty: RED.nodes.dirty(), diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js index 32b604235..1df91dae3 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js @@ -557,12 +557,17 @@ RED.deploy = (function() { } else { RED.notify('

' + RED._("deploy.successfulDeploy") + '

', "success"); } - RED.nodes.eachNode(function (node) { - const flow = node.z && (RED.nodes.workspace(node.z) || RED.nodes.subflow(node.z) || null); + const flowsToLock = new Set() + function ensureUnlocked(id) { + const flow = id && (RED.nodes.workspace(id) || RED.nodes.subflow(id) || null); const isLocked = flow ? flow.locked : false; if (flow && isLocked) { flow.locked = false; + flowsToLock.add(flow) } + } + RED.nodes.eachNode(function (node) { + ensureUnlocked(node.z) if (node.changed) { node.dirty = true; node.changed = false; @@ -574,10 +579,32 @@ RED.deploy = (function() { if (node.credentials) { delete node.credentials; } - if (flow && isLocked) { - flow.locked = isLocked; - } }); + RED.nodes.eachGroup(function (node) { + ensureUnlocked(node.z) + if (node.changed) { + node.dirty = true; + node.changed = false; + } + if (node.moved) { + node.dirty = true; + node.moved = false; + } + }) + RED.nodes.eachJunction(function (node) { + ensureUnlocked(node.z) + if (node.changed) { + node.dirty = true; + node.changed = false; + } + if (node.moved) { + node.dirty = true; + node.moved = false; + } + }) + flowsToLock.forEach(flow => { + flow.locked = true + }) RED.nodes.eachConfig(function (confNode) { confNode.changed = false; if (confNode.credentials) { diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/group.js b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js index 399e5e36e..96fde0f0d 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/group.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js @@ -488,7 +488,8 @@ RED.group = (function() { y: Number.POSITIVE_INFINITY, w: 0, h: 0, - _def: RED.group.def + _def: RED.group.def, + changed: true } group.z = nodes[0].z; diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js index e69674b93..90ecf6093 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-config.js @@ -145,17 +145,19 @@ RED.sidebar.config = (function() { } else { var currentType = ""; nodes.forEach(function(node) { - var label = RED.utils.getNodeLabel(node,node.id); + var labelText = RED.utils.getNodeLabel(node,node.id); if (node.type != currentType) { $('
  • '+node.type+'
  • ').appendTo(list); currentType = node.type; } - + if (node.changed) { + labelText += "!!" + } var entry = $('
  • ').appendTo(list); var nodeDiv = $('
    ').appendTo(entry); entry.data('node',node.id); nodeDiv.data('node',node.id); - var label = $('
    ').text(label).appendTo(nodeDiv); + var label = $('
    ').text(labelText).appendTo(nodeDiv); if (node.d) { nodeDiv.addClass("red-ui-palette-node-config-disabled"); $('').prependTo(label); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js index a22f65f76..ea2ebc289 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js @@ -1191,7 +1191,8 @@ RED.view.tools = (function() { w: 0, h: 0, outputs: 1, inputs: 1, - dirty: true + dirty: true, + moved: true } links = links.filter(function(l) { return !removedLinks.has(l) }) if (links.length === 0) { 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 5078c8e8b..62c7f90a5 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 @@ -1267,7 +1267,8 @@ RED.view = (function() { w: 0, h: 0, outputs: 1, inputs: 1, - dirty: true + dirty: true, + moved: true } historyEvent = { t:'add', @@ -3307,7 +3308,7 @@ RED.view = (function() { console.log("Definition error: "+node.type+"."+((portType === PORT_TYPE_INPUT)?"inputLabels":"outputLabels"),err); result = null; } - } else if ($.isArray(portLabels)) { + } else if (Array.isArray(portLabels)) { result = portLabels[portIndex]; } return result; @@ -5699,7 +5700,7 @@ RED.view = (function() { if (mouse_mode === RED.state.SELECTING_NODE) { return; } - + const wasDirty = RED.nodes.dirty() var nodesToImport; if (typeof newNodesObj === "string") { if (newNodesObj === "") { @@ -5716,7 +5717,7 @@ RED.view = (function() { nodesToImport = newNodesObj; } - if (!$.isArray(nodesToImport)) { + if (!Array.isArray(nodesToImport)) { nodesToImport = [nodesToImport]; } if (options.generateDefaultNames) { @@ -5749,7 +5750,12 @@ RED.view = (function() { return (n.type === "global-config"); }); } - var result = RED.nodes.import(filteredNodesToImport,{generateIds:options.generateIds, addFlow: addNewFlow, importMap: options.importMap}); + var result = RED.nodes.import(filteredNodesToImport,{ + generateIds: options.generateIds, + addFlow: addNewFlow, + importMap: options.importMap, + markChanged: true + }); if (result) { var new_nodes = result.nodes; var new_links = result.links; @@ -5765,7 +5771,7 @@ RED.view = (function() { var new_ms = new_nodes.filter(function(n) { return n.hasOwnProperty("x") && n.hasOwnProperty("y") && n.z == RED.workspaces.active() }); new_ms = new_ms.concat(new_groups.filter(function(g) { return g.z === RED.workspaces.active()})) new_ms = new_ms.concat(new_junctions.filter(function(j) { return j.z === RED.workspaces.active()})) - var new_node_ids = new_nodes.map(function(n){ n.changed = true; return n.id; }); + var new_node_ids = new_nodes.map(function(n){ return n.id; }); clearSelection(); movingSet.clear(); @@ -5845,14 +5851,14 @@ RED.view = (function() { } var historyEvent = { - t:"add", - nodes:new_node_ids, - links:new_links, - groups:new_groups, + t: "add", + nodes: new_node_ids, + links: new_links, + groups: new_groups, junctions: new_junctions, - workspaces:new_workspaces, - subflows:new_subflows, - dirty:RED.nodes.dirty() + workspaces: new_workspaces, + subflows: new_subflows, + dirty: wasDirty }; if (movingSet.length() === 0) { RED.nodes.dirty(true); @@ -5861,7 +5867,7 @@ RED.view = (function() { var subflowRefresh = RED.subflow.refresh(true); if (subflowRefresh) { historyEvent.subflow = { - id:activeSubflow.id, + id: activeSubflow.id, changed: activeSubflowChanged, instances: subflowRefresh.instances } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js index 9077ca399..56fb1c79e 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js @@ -375,6 +375,12 @@ RED.workspaces = (function() { $("#red-ui-tab-"+(tab.id.replace(".","-"))).addClass('red-ui-workspace-locked'); } + const changeBadgeContainer = $('').appendTo("#red-ui-tab-"+(tab.id.replace(".","-"))) + const changeBadge = document.createElementNS("http://www.w3.org/2000/svg","circle"); + changeBadge.setAttribute("cx",5); + changeBadge.setAttribute("cy",5); + changeBadge.setAttribute("r",5); + changeBadgeContainer.append(changeBadge) RED.menu.setDisabled("menu-item-workspace-delete",activeWorkspace === 0 || workspaceTabCount <= 1); if (workspaceTabCount === 1) { @@ -637,6 +643,11 @@ RED.workspaces = (function() { RED.workspaces.show(viewStack[++viewStackPos],true); } }) + + RED.events.on("flows:change", (ws) => { + $("#red-ui-tab-"+(ws.id.replace(".","-"))).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed)); + }) + hideWorkspace(); } diff --git a/packages/node_modules/@node-red/editor-client/src/sass/workspace.scss b/packages/node_modules/@node-red/editor-client/src/sass/workspace.scss index 06ec5d4fc..e096c7cf3 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/workspace.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/workspace.scss @@ -105,6 +105,15 @@ } } } +.red-ui-tab:not(.red-ui-workspace-changed) .red-ui-flow-tab-changed { + display: none; +} +.red-ui-tab.red-ui-workspace-changed .red-ui-flow-tab-changed { + display: inline-block; + position: absolute; + top: 1px; + right: 1px; +} .red-ui-workspace-locked-icon { display: none; From 2ddbb44992cd570d57ebde7bf0b1e9015642f96a Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 23 Feb 2023 23:14:18 +0000 Subject: [PATCH 04/10] Add change annotation for newly added flow --- .../@node-red/editor-client/src/js/ui/deploy.js | 16 ++++++++++++---- .../editor-client/src/js/ui/workspaces.js | 10 ++++++---- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js index 1df91dae3..a09fdeb01 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js @@ -602,10 +602,10 @@ RED.deploy = (function() { node.moved = false; } }) - flowsToLock.forEach(flow => { - flow.locked = true - }) RED.nodes.eachConfig(function (confNode) { + if (confNode.z) { + ensureUnlocked(confNode.z) + } confNode.changed = false; if (confNode.credentials) { delete confNode.credentials; @@ -615,8 +615,16 @@ RED.deploy = (function() { subflow.changed = false; }); RED.nodes.eachWorkspace(function (ws) { - ws.changed = false; + if (ws.changed || ws.added) { + ensureUnlocked(ws.z) + ws.changed = false; + delete ws.added + RED.events.emit("flows:change", ws) + } }); + flowsToLock.forEach(flow => { + flow.locked = true + }) // Once deployed, cannot undo back to a clean state RED.history.markAllDirty(); RED.view.redraw(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js index 56fb1c79e..9b2b1d0d7 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js @@ -82,8 +82,11 @@ RED.workspaces = (function() { info: "", label: RED._('workspace.defaultName',{number:workspaceIndex}), env: [], - hideable: true + hideable: true, }; + if (!skipHistoryEntry) { + ws.added = true + } RED.nodes.addWorkspace(ws,targetIndex); workspace_tabs.addTab(ws,targetIndex); @@ -93,8 +96,7 @@ RED.workspaces = (function() { RED.nodes.dirty(true); } } - $("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label) - + $("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added)); RED.view.focus(); return ws; } @@ -645,7 +647,7 @@ RED.workspaces = (function() { }) RED.events.on("flows:change", (ws) => { - $("#red-ui-tab-"+(ws.id.replace(".","-"))).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed)); + $("#red-ui-tab-"+(ws.id.replace(".","-"))).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added)); }) hideWorkspace(); From 6abe66934ecb7915ed783fc5bc40a0616eb88853 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 23 Feb 2023 23:36:51 +0000 Subject: [PATCH 05/10] Prevent opening locked node's edit dialog --- .../editor-client/src/js/ui/clipboard.js | 8 +++---- .../editor-client/src/js/ui/contextMenu.js | 2 +- .../editor-client/src/js/ui/editor.js | 4 ++++ .../editor-client/src/js/ui/group.js | 18 +++++++------- .../editor-client/src/js/ui/subflow.js | 4 ++-- .../editor-client/src/js/ui/view-tools.js | 24 +++++++++---------- .../@node-red/editor-client/src/js/ui/view.js | 12 +++++----- .../editor-client/src/js/ui/workspaces.js | 7 +++--- 8 files changed, 42 insertions(+), 37 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js b/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js index f24205be1..dd1ca7074 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/clipboard.js @@ -503,7 +503,7 @@ RED.clipboard = (function() { $("#red-ui-clipboard-dialog-import-text").on("keyup", validateImport); $("#red-ui-clipboard-dialog-import-text").on('paste',function() { setTimeout(validateImport,10)}); - if (RED.workspaces.active() === 0 || RED.workspaces.isActiveLocked()) { + if (RED.workspaces.active() === 0 || RED.workspaces.isLocked()) { $("#red-ui-clipboard-dialog-import-opt-current").addClass('disabled').removeClass("selected"); $("#red-ui-clipboard-dialog-import-opt-new").addClass("selected"); } else { @@ -1278,7 +1278,7 @@ RED.clipboard = (function() { RED.keyboard.add("#red-ui-drop-target", "escape" ,hideDropTarget); $('#red-ui-workspace-chart').on("dragenter",function(event) { - if (!RED.workspaces.isActiveLocked() && ( + if (!RED.workspaces.isLocked() && ( $.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 || $.inArray("Files",event.originalEvent.dataTransfer.types) != -1)) { $("#red-ui-drop-target").css({display:'table'}).focus(); @@ -1288,7 +1288,7 @@ RED.clipboard = (function() { $('#red-ui-drop-target').on("dragover",function(event) { if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1 || $.inArray("Files",event.originalEvent.dataTransfer.types) != -1 || - RED.workspaces.isActiveLocked()) { + RED.workspaces.isLocked()) { event.preventDefault(); } }) @@ -1296,7 +1296,7 @@ RED.clipboard = (function() { hideDropTarget(); }) .on("drop",function(event) { - if (!RED.workspaces.isActiveLocked()) { + if (!RED.workspaces.isLocked()) { try { if ($.inArray("text/plain",event.originalEvent.dataTransfer.types) != -1) { var data = event.originalEvent.dataTransfer.getData("text/plain"); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js index 57d5bbcf9..30fc7fd3b 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/contextMenu.js @@ -28,7 +28,7 @@ RED.contextMenu = (function () { const isMultipleLinks = !hasSelection && hasLinks && wireLinks.length > 1 const canDelete = hasSelection || hasLinks const isGroup = hasSelection && selection.nodes.length === 1 && selection.nodes[0].type === 'group' - const canEdit = !RED.workspaces.isActiveLocked() + const canEdit = !RED.workspaces.isLocked() const canRemoveFromGroup = hasSelection && !!selection.nodes[0].g const isAllGroups = hasSelection && selection.nodes.filter(n => n.type !== 'group').length === 0 const hasGroup = hasSelection && selection.nodes.filter(n => n.type === 'group' ).length > 0 diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js index 6b5db326a..45f2068d9 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js @@ -860,6 +860,7 @@ RED.editor = (function() { function showEditDialog(node, defaultTab) { if (buildingEditDialog) { return } buildingEditDialog = true; + if (node.z && RED.workspaces.isLocked(node.z)) { return } var editing_node = node; var removeInfoEditorOnClose = false; var skipInfoRefreshOnClose = false; @@ -1155,6 +1156,8 @@ RED.editor = (function() { var editing_config_node = RED.nodes.node(id); var activeEditPanes = []; + if (editing_config_node && editing_config_node.z && RED.workspaces.isLocked(editing_config_node.z)) { return } + var configNodeScope = ""; // default to global var activeSubflow = RED.nodes.subflow(RED.workspaces.active()); if (activeSubflow) { @@ -1708,6 +1711,7 @@ RED.editor = (function() { function showEditGroupDialog(group, defaultTab) { if (buildingEditDialog) { return } buildingEditDialog = true; + if (group.z && RED.workspaces.isLocked(group.z)) { return } var editing_node = group; editStack.push(group); RED.view.state(RED.state.EDITING); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/group.js b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js index 399e5e36e..bee533f98 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/group.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/group.js @@ -188,7 +188,7 @@ RED.group = (function() { var activateMerge = false; var activateRemove = false; var singleGroupSelected = false; - var locked = RED.workspaces.isActiveLocked() + var locked = RED.workspaces.isLocked() if (activateGroup) { singleGroupSelected = selection.nodes.length === 1 && selection.nodes[0].type === 'group'; @@ -266,7 +266,7 @@ RED.group = (function() { } } function pasteGroupStyle() { - if (RED.workspaces.isActiveLocked()) { return } + if (RED.workspaces.isLocked()) { return } if (RED.view.state() !== RED.state.DEFAULT) { return } if (groupStyleClipboard) { var selection = RED.view.selection(); @@ -301,7 +301,7 @@ RED.group = (function() { } function groupSelection() { - if (RED.workspaces.isActiveLocked()) { return } + if (RED.workspaces.isLocked()) { return } if (RED.view.state() !== RED.state.DEFAULT) { return } var selection = RED.view.selection(); if (selection.nodes) { @@ -320,7 +320,7 @@ RED.group = (function() { } } function ungroupSelection() { - if (RED.workspaces.isActiveLocked()) { return } + if (RED.workspaces.isLocked()) { return } if (RED.view.state() !== RED.state.DEFAULT) { return } var selection = RED.view.selection(); if (selection.nodes) { @@ -344,7 +344,7 @@ RED.group = (function() { } function ungroup(g) { - if (RED.workspaces.isActiveLocked()) { return } + if (RED.workspaces.isLocked()) { return } var nodes = []; var parentGroup = RED.nodes.group(g.g); g.nodes.forEach(function(n) { @@ -371,7 +371,7 @@ RED.group = (function() { } function mergeSelection() { - if (RED.workspaces.isActiveLocked()) { return } + if (RED.workspaces.isLocked()) { return } if (RED.view.state() !== RED.state.DEFAULT) { return } var selection = RED.view.selection(); if (selection.nodes) { @@ -441,7 +441,7 @@ RED.group = (function() { } function removeSelection() { - if (RED.workspaces.isActiveLocked()) { return } + if (RED.workspaces.isLocked()) { return } if (RED.view.state() !== RED.state.DEFAULT) { return } var selection = RED.view.selection(); if (selection.nodes) { @@ -469,7 +469,7 @@ RED.group = (function() { } } function createGroup(nodes) { - if (RED.workspaces.isActiveLocked()) { return } + if (RED.workspaces.isLocked()) { return } if (nodes.length === 0) { return; } @@ -575,7 +575,7 @@ RED.group = (function() { markDirty(group); } function removeFromGroup(group, nodes, reparent) { - if (RED.workspaces.isActiveLocked()) { return } + if (RED.workspaces.isLocked()) { return } if (!Array.isArray(nodes)) { nodes = [nodes]; } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js index b798ba891..903686e42 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/subflow.js @@ -573,7 +573,7 @@ RED.subflow = (function() { } }); RED.events.on("view:selection-changed",function(selection) { - if (!selection.nodes || RED.workspaces.isActiveLocked()) { + if (!selection.nodes || RED.workspaces.isLocked()) { RED.menu.setDisabled("menu-item-subflow-convert",true); } else { RED.menu.setDisabled("menu-item-subflow-convert",false); @@ -636,7 +636,7 @@ RED.subflow = (function() { } function convertToSubflow() { - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { return } var selection = RED.view.selection(); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js index a22f65f76..5b2c95431 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js @@ -39,7 +39,7 @@ RED.view.tools = (function() { } function alignToGrid() { - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { return } var selection = RED.view.selection(); @@ -90,7 +90,7 @@ RED.view.tools = (function() { } function moveSelection(dx,dy) { - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { return } if (moving_set === null) { @@ -159,7 +159,7 @@ RED.view.tools = (function() { } function setSelectedNodeLabelState(labelShown) { - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { return } var selection = RED.view.selection(); @@ -448,7 +448,7 @@ RED.view.tools = (function() { } function alignSelectionToEdge(direction) { - // if (RED.workspaces.isActiveLocked()) { + // if (RED.workspaces.isLocked()) { // return // } var selection = RED.view.selection(); @@ -552,7 +552,7 @@ RED.view.tools = (function() { } function distributeSelection(direction) { - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { return } var selection = RED.view.selection(); @@ -713,7 +713,7 @@ RED.view.tools = (function() { } function reorderSelection(dir) { - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { return } var selection = RED.view.selection(); @@ -752,7 +752,7 @@ RED.view.tools = (function() { } function wireSeriesOfNodes() { - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { return } var selection = RED.view.selection(); @@ -795,7 +795,7 @@ RED.view.tools = (function() { } function wireNodeToMultiple() { - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { return } var selection = RED.view.selection(); @@ -841,7 +841,7 @@ RED.view.tools = (function() { } function wireMultipleToNode() { - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { return } var selection = RED.view.selection(); @@ -903,7 +903,7 @@ RED.view.tools = (function() { * @param {Object || Object[]} wires The wire(s) to split and replace with link-out, link-in nodes. */ function splitWiresWithLinkNodes(wires) { - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { return } let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link)); @@ -1074,7 +1074,7 @@ RED.view.tools = (function() { * @param {{ renameBlank: boolean, renameClash: boolean, generateHistory: boolean }} options Possible options are `renameBlank`, `renameClash` and `generateHistory` */ function generateNodeNames(node, options) { - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { return } options = Object.assign({ @@ -1147,7 +1147,7 @@ RED.view.tools = (function() { } function addJunctionsToWires(wires) { - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { return } let wiresToSplit = wires || (RED.view.selection().links && RED.view.selection().links.filter(e => !e.link)); 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 5078c8e8b..42bbe8e12 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 @@ -671,7 +671,7 @@ RED.view = (function() { RED.actions.add("core:copy-selection-to-internal-clipboard",copySelection); RED.actions.add("core:cut-selection-to-internal-clipboard",function(){copySelection(true);deleteSelection();}); RED.actions.add("core:paste-from-internal-clipboard",function(){ - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { return } importNodes(clipboard,{generateIds: clipboardSource === 'copy', generateDefaultNames: clipboardSource === 'copy'}); @@ -2549,7 +2549,7 @@ RED.view = (function() { } function editSelection() { - if (RED.workspaces.isActiveLocked()) { return } + if (RED.workspaces.isLocked()) { return } if (movingSet.length() > 0) { var node = movingSet.get(0).n; if (node.type === "subflow") { @@ -2883,7 +2883,7 @@ RED.view = (function() { function detachSelectedNodes() { - if (RED.workspaces.isActiveLocked()) { return } + if (RED.workspaces.isLocked()) { return } var selection = RED.view.selection(); if (selection.nodes) { const {newLinks, removedLinks} = RED.nodes.detachNodes(selection.nodes); @@ -3465,7 +3465,7 @@ RED.view = (function() { } if (dblClickPrimed && mousedown_node == d && clickElapsed > 0 && clickElapsed < dblClickInterval) { mouse_mode = RED.state.DEFAULT; - if (RED.workspaces.isActiveLocked()) { + if (RED.workspaces.isLocked()) { clickElapsed = 0; d3.event.stopPropagation(); return @@ -4009,7 +4009,7 @@ RED.view = (function() { if (RED.view.DEBUG) { console.warn("groupMouseUp", { mouse_mode, event: d3.event }); } - if (RED.workspaces.isActiveLocked()) { return } + if (RED.workspaces.isLocked()) { return } if (dblClickPrimed && mousedown_group == g && clickElapsed > 0 && clickElapsed < dblClickInterval) { mouse_mode = RED.state.DEFAULT; RED.editor.editGroup(g); @@ -4233,7 +4233,7 @@ RED.view = (function() { function showTouchMenu(obj,pos) { var mdn = mousedown_node; var options = []; - const isActiveLocked = RED.workspaces.isActiveLocked() + const isActiveLocked = RED.workspaces.isLocked() options.push({name:"delete",disabled:(isActiveLocked || movingSet.length()===0 && selectedLinks.length() === 0),onselect:function() {deleteSelection();}}); options.push({name:"cut",disabled:(isActiveLocked || movingSet.length()===0),onselect:function() {copySelection(true);deleteSelection();}}); options.push({name:"copy",disabled:(isActiveLocked || movingSet.length()===0),onselect:function() {copySelection();}}); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js index 9077ca399..ac457cf69 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/workspaces.js @@ -160,7 +160,7 @@ RED.workspaces = (function() { } }); - let isCurrentLocked = RED.workspaces.isActiveLocked() + let isCurrentLocked = RED.workspaces.isLocked() if (tab) { isCurrentLocked = tab.locked } @@ -833,8 +833,9 @@ RED.workspaces = (function() { active: function() { return activeWorkspace }, - isActiveLocked: function() { - var ws = RED.nodes.workspace(activeWorkspace) || RED.nodes.subflow(activeWorkspace) + isLocked: function(id) { + id = id || activeWorkspace + var ws = RED.nodes.workspace(id) || RED.nodes.subflow(id) return ws && ws.locked }, selection: function() { From bbc1a82c2a0adfdaf1f35db4e4b8fba49552b17d Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 23 Feb 2023 23:45:34 +0000 Subject: [PATCH 06/10] Fix linting on boolean check --- packages/node_modules/@node-red/editor-client/src/js/nodes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 0c8bc83b6..05256343b 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 @@ -608,7 +608,7 @@ RED.nodes = (function() { const ws = workspaces[z] if (ws) { const contentsChanged = tabDirtyMap[z].size > 0 || tabDeletedNodesMap[z].size > 0 - if (!!ws.contentsChanged !== contentsChanged) { + if (Boolean(ws.contentsChanged) !== contentsChanged) { ws.contentsChanged = contentsChanged RED.events.emit("flows:change", ws); } From 25b6e214dd6ae738187c5783c64c28bb47a8fa74 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Sat, 25 Feb 2023 00:54:15 +0900 Subject: [PATCH 07/10] fix align nodes on locked tab --- .../@node-red/editor-client/src/js/ui/view-tools.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js index 5b2c95431..a35f8e04f 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view-tools.js @@ -448,9 +448,9 @@ RED.view.tools = (function() { } function alignSelectionToEdge(direction) { - // if (RED.workspaces.isLocked()) { - // return - // } + if (RED.workspaces.isLocked()) { + return; + } var selection = RED.view.selection(); if (selection.nodes && selection.nodes.length > 1) { From 662b1a81005650f2eb1be64388e669d4605dd667 Mon Sep 17 00:00:00 2001 From: Hiroyasu Nishiyama Date: Mon, 27 Feb 2023 14:31:44 +0900 Subject: [PATCH 08/10] add cancel operation to editableList --- .../@node-red/editor-client/src/js/ui/common/editableList.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js index f308614d7..8ee1f0e29 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/editableList.js @@ -417,6 +417,9 @@ } else { return null; } + }, + cancel: function() { + this.element.sortable("cancel"); } }); })(jQuery); From 27e258b19b2870366eb5c1c0d9ba8fe4738e8c0e Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 2 Mar 2023 14:14:33 +0000 Subject: [PATCH 09/10] Fix importing connected link nodes into a subflow Fixes #4055 --- packages/node_modules/@node-red/editor-client/src/js/nodes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 36bc2a7fa..5f054782e 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 @@ -2302,7 +2302,7 @@ RED.nodes = (function() { // get added if (activeSubflow && /^link /.test(n.type) && n.links) { n.links = n.links.filter(function(id) { - var otherNode = RED.nodes.node(id); + const otherNode = node_map[id] || RED.nodes.node(id); return (otherNode && otherNode.z === activeWorkspace) }); } From 9e6d5010094e35ef54594f6ccfb7ad594e36c51f Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Thu, 2 Mar 2023 14:59:54 +0000 Subject: [PATCH 10/10] Join: ensure inflight status is cleared when in auto mode Fixes #4080 --- packages/node_modules/@node-red/nodes/core/sequence/17-split.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node_modules/@node-red/nodes/core/sequence/17-split.js b/packages/node_modules/@node-red/nodes/core/sequence/17-split.js index 4e71fe6c7..b2e11218a 100644 --- a/packages/node_modules/@node-red/nodes/core/sequence/17-split.js +++ b/packages/node_modules/@node-red/nodes/core/sequence/17-split.js @@ -478,7 +478,7 @@ module.exports = function(RED) { var completeSend = function(partId) { var group = inflight[partId]; if (group.timeout) { clearTimeout(group.timeout); } - if ((node.accumulate !== true) || group.msg.hasOwnProperty("complete")) { delete inflight[partId]; } + if (node.mode === 'auto' || node.accumulate !== true || group.msg.hasOwnProperty("complete")) { delete inflight[partId]; } if (group.type === 'array' && group.arrayLen > 1) { var newArray = []; group.payload.forEach(function(n) {