Compare commits

..

2 Commits

Author SHA1 Message Date
Nick O'Leary
37524e05bb Do not select parent group after completing quick-add inside group 2023-03-03 15:58:38 +00:00
Nick O'Leary
3fbfd47089 Show quick-add dialog when draggin wire to empty space 2023-03-03 15:52:59 +00:00
23 changed files with 53 additions and 226 deletions

View File

@@ -19,9 +19,9 @@ jobs:
matrix:
node-version: [16, 18]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies

View File

@@ -1,55 +1,6 @@
#### 3.1.0-beta.2: Beta Release
Editor
- NEW: Add change icon to tabs (#4068) @knolleary
- NEW: Complete overhaul of Group UX (#4079) @knolleary
- NEW: Add link to node help in node edit dialog footer (#4065) @knolleary
- NEW: Added editor feature for connecting multiple nodes to single node (#4051) @sonntam
- NEW: Increase workspace size to 8000x8000 (#4094) @knolleary
- Ensure node buttons are redrawn when flow lock state is changed (#4091) @knolleary
- Prevent loops being created with junction nodes (#4087) @knolleary
- Prevent opening locked node's edit dialog (#4069) @knolleary
- Reverse direction of tab scroll to expected direction (#4064) @knolleary
- Add cancel operation to editableList (#4077) @HiroyasuNishiyama
- Apply Mermaid diagram for project settings UI (#4054) @kazuhitoyokoi
- Add tooltip for show/hide button on info sidebar (#4050) @kazuhitoyokoi
- Fix align nodes on locked tab (#4072) @HiroyasuNishiyama
- Fix importing connected link nodes into a subflow (#4082) @knolleary
- Fix to add empty marker to empty group (#4060) @HiroyasuNishiyama
- Fix image URLs for v3.0 tour (#4053) @kazuhitoyokoi
- Show scrollbar in notification dialog only when needed (#4048) @kazuhitoyokoi
- Update-monaco-and-typings (#4089) @Steve-Mcl
- Update jquery UI (#4088) @knolleary
- Support i18n of lock/unlock buttons in flow property UI (#4049) @kazuhitoyokoi
- Translation kr (#3895) @hae-iotplatform
- Translation zhcn (请懂中文的帮忙review) (#3952) @cliyr
- Add French translation of nodes (#3964) @GogoVega
- Add French translation (#3962) @GogoVega
- Portuguese Brazilian (pt-BR) translation (#3804) @FabsMuller
Runtime
- NEW: Generate stable ids for subflow instance internal nodes (#4093) @knolleary
- NEW: Change default file name to flows.json in project feature (#4073) @kazuhitoyokoi
- NEW: Deprecate synchronous access to jsonata (#4090) @knolleary
- Add Node 18 to test matrix (#4084) @knolleary
- Bump minimum nodejs version supported to match documented value (#4086) @knolleary
- Update monaco docs link in settings.js (#4075) @Steve-Mcl
- Remove duplicated messages in the message catalog (#4066) @kazuhitoyokoi
- Ensure errors in preDeliver callback are handled (#3911) @knolleary
- Fix "EADDRINUSE" error (#4046) @bggbr
Nodes
- Link Call: Clear link-call timeouts when node is closed (#4085) @knolleary
- Join: ensure inflight status is cleared when in auto mode (#4083) @knolleary
- File Out: Fix extra newline append for multipart file write (#3915) @dceejay
- Add validators for complete and link call nodes (#4056) @kazuhitoyokoi
#### 3.1.0-beta.1: Beta Release
Editor
- NEW: Locking Flows (#3938) @knolleary

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "3.1.0-beta.2",
"version": "3.1.0-beta.1",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-api",
"version": "3.1.0-beta.2",
"version": "3.1.0-beta.1",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,8 +16,8 @@
}
],
"dependencies": {
"@node-red/util": "3.1.0-beta.2",
"@node-red/editor-client": "3.1.0-beta.2",
"@node-red/util": "3.1.0-beta.1",
"@node-red/editor-client": "3.1.0-beta.1",
"bcryptjs": "2.4.3",
"body-parser": "1.20.2",
"clone": "2.1.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/editor-client",
"version": "3.1.0-beta.2",
"version": "3.1.0-beta.1",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -747,14 +747,14 @@ RED.projects = (function() {
var row = $('<div class="form-row"></div>').appendTo(body);
$('<label for="red-ui-projects-dialog-screen-create-project-file">'+RED._("projects.default-files.flow-file")+'</label>').appendTo(row);
var subrow = $('<div style="position:relative;"></div>').appendTo(row);
var defaultFlowFile = (createProjectOptions.files &&createProjectOptions.files.flow) || (RED.settings.files && RED.settings.files.flow) || "flows.json";
var defaultFlowFile = (createProjectOptions.files &&createProjectOptions.files.flow) || (RED.settings.files && RED.settings.files.flow)||"flow.json";
projectFlowFileInput = $('<input id="red-ui-projects-dialog-screen-create-project-file" type="text">').val(defaultFlowFile)
.on("change keyup paste",validateForm)
.appendTo(subrow);
$('<div class="red-ui-projects-dialog-screen-input-status"></div>').appendTo(subrow);
$('<label class="red-ui-projects-edit-form-sublabel"><small>*.json</small></label>').appendTo(row);
var defaultCredentialsFile = (createProjectOptions.files &&createProjectOptions.files.credentials) || (RED.settings.files && RED.settings.files.credentials) || "flows_cred.json";
var defaultCredentialsFile = (createProjectOptions.files &&createProjectOptions.files.credentials) || (RED.settings.files && RED.settings.files.credentials)||"flow_cred.json";
row = $('<div class="form-row"></div>').appendTo(body);
$('<label for="red-ui-projects-dialog-screen-create-project-credfile">'+RED._("projects.default-files.credentials-file")+'</label>').appendTo(row);
subrow = $('<div style="position:relative;"></div>').appendTo(row);
@@ -1257,7 +1257,7 @@ RED.projects = (function() {
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-empty"></div>').appendTo(container);
$('<label for="red-ui-projects-dialog-screen-create-project-file">'+RED._("projects.create.flow-file")+'</label>').appendTo(row);
subrow = $('<div style="position:relative;"></div>').appendTo(row);
projectFlowFileInput = $('<input id="red-ui-projects-dialog-screen-create-project-file" type="text">').val("flows.json")
projectFlowFileInput = $('<input id="red-ui-projects-dialog-screen-create-project-file" type="text">').val("flow.json")
.on("change keyup paste",validateForm)
.appendTo(subrow);
$('<div class="red-ui-projects-dialog-screen-input-status"></div>').appendTo(subrow);

View File

@@ -17,9 +17,9 @@
RED.view.navigator = (function() {
var nav_scale = 50;
var nav_width = 8000/nav_scale;
var nav_height = 8000/nav_scale;
var nav_scale = 25;
var nav_width = 5000/nav_scale;
var nav_height = 5000/nav_scale;
var navContainer;
var navBox;

View File

@@ -30,8 +30,8 @@
*/
RED.view = (function() {
var space_width = 8000,
space_height = 8000,
var space_width = 5000,
space_height = 5000,
lineCurveScale = 0.75,
scaleFactor = 1,
node_width = 100,
@@ -1455,9 +1455,6 @@ RED.view = (function() {
// auto select dropped node - so info shows (if visible)
clearSelection();
nn.selected = true;
if (targetGroup) {
selectGroup(targetGroup,false);
}
movingSet.add(nn);
updateActiveNodes();
updateSelection();
@@ -1905,6 +1902,7 @@ RED.view = (function() {
}
showQuickAddDialog({ position: point, group: clickedGroup });
}
hideDragLines();
}
if (lasso) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,71 +1,15 @@
export default {
version: "3.1.0-beta.2",
version: "3.1.0-beta.1",
steps: [
{
titleIcon: "fa fa-map-o",
title: {
"en-US": "Welcome to Node-RED 3.1 Beta 2!",
"en-US": "Welcome to Node-RED 3.1 Beta 1!",
"ja": "Node-RED 3.1 ベータ1へようこそ!"
},
description: {
"en-US": "<p>This is the second beta release for 3.1.0 and we have a few new features to tell you about.</p>",
// "ja": "<p>これは3.1.0の最初のベータリリースです。いくつかの新機能について説明します。</p>"
}
},
{
title: {
"en-US": "New ways to work with groups",
},
description: {
"en-US": `<p>We have changed how you interact with groups in the editor.</p>
<ul>
<li>They don't get in the way when clicking on a node</li>
<li>They can be reordered using the Moving Forwards and Move Backwards actions</li>
<li>Multiple nodes can be dragged into a group in one go</li>
<li>Holding <code>Alt</code> when dragging a node will *remove* it from its group</li>
</ul>`
}
},
{
title: {
"en-US": "Change notification on tabs",
},
image: 'images/tab-changes.png',
description: {
"en-US": `<p>When a tab contains undeployed changes it now shows the
same style of change icon used by nodes.</p>
<p>This will make it much easier to track down changes when you're
working across multiple flows.</p>`
}
},
{
title: {
"en-US": "A bigger canvas to work with",
},
description: {
"en-US": `<p>The default canvas size has been increased so you can fit more
into one flow.</p>
<p>We still recommend using tools such as subflows and Link Nodes to help
keep things organised, but now you have more room to work in.</p>`
}
},
{
title: {
"en-US": "Finding help",
},
image: 'images/node-help.png',
description: {
"en-US": `<p>All node edit dialogs now include a link to that node's help
in the footer.</p>
<p>Clicking it will open up the Help sidebar showing the help for that node.</p>`
}
},
{
title: {
"en-US": "And lots more...",
},
description: {
"en-US": `<p>Of course we have everything from 3.1.0-beta.1 as well....</p>`
"en-US": "<p>This is the first beta release for 3.1.0 and we have a few new features to tell you about.</p>",
"ja": "<p>これは3.1.0の最初のベータリリースです。いくつかの新機能について説明します。</p>"
}
},
{

View File

@@ -117,9 +117,7 @@ module.exports = function(RED) {
}
if (typeof data === "boolean") { data = data.toString(); }
if (typeof data === "number") { data = data.toString(); }
var aflg = true;
if (msg.hasOwnProperty("parts") && msg.parts.type === "string" && (msg.parts.count === msg.parts.index + 1)) { aflg = false; }
if ((node.appendNewline) && (!Buffer.isBuffer(data)) && aflg) { data += os.EOL; }
if ((node.appendNewline) && (!Buffer.isBuffer(data))) { data += os.EOL; }
var buf;
if (node.encoding === "setbymsg") {
buf = encode(data, msg.encoding || "none");
@@ -316,6 +314,7 @@ module.exports = function(RED) {
});
filename = filename || "";
var fullFilename = filename;
var filePath = "";
if (filename && RED.settings.fileWorkingDirectory && !path.isAbsolute(filename)) {
fullFilename = path.resolve(path.join(RED.settings.fileWorkingDirectory,filename));
}

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/nodes",
"version": "3.1.0-beta.2",
"version": "3.1.0-beta.1",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/registry",
"version": "3.1.0-beta.2",
"version": "3.1.0-beta.1",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,7 +16,7 @@
}
],
"dependencies": {
"@node-red/util": "3.1.0-beta.2",
"@node-red/util": "3.1.0-beta.1",
"clone": "2.1.2",
"fs-extra": "10.1.0",
"semver": "7.3.8",

View File

@@ -818,16 +818,6 @@ function handlePreRoute(flow, sendEvent, reportError) {
})
}
function deliverMessageToDestination(sendEvent) {
if (sendEvent?.destination?.node) {
try {
sendEvent.destination.node.receive(sendEvent.msg);
} catch(err) {
Log.error(`Error delivering message to node:${sendEvent.destination.node._path} [${sendEvent.destination.node.type}]`)
Log.error(err.stack)
}
}
}
function handlePreDeliver(flow,sendEvent, reportError) {
// preDeliver - the local router has identified the node it is going to send to. At this point, the message has been cloned if needed.
hooks.trigger("preDeliver",sendEvent,(err) => {
@@ -837,10 +827,15 @@ function handlePreDeliver(flow,sendEvent, reportError) {
} else if (err !== false) {
if (asyncMessageDelivery) {
setImmediate(function() {
deliverMessageToDestination(sendEvent)
if (sendEvent.destination.node) {
sendEvent.destination.node.receive(sendEvent.msg);
}
})
} else {
deliverMessageToDestination(sendEvent)
if (sendEvent.destination.node) {
sendEvent.destination.node.receive(sendEvent.msg);
}
}
// postDeliver - the message has been dispatched to be delivered asynchronously (unless the sync delivery flag is set, in which case it would be continue as synchronous delivery)
hooks.trigger("postDeliver", sendEvent, function(err) {

View File

@@ -474,7 +474,7 @@ class Subflow extends Flow {
*/
function createNodeInSubflow(subflowInstanceId, def) {
let node = clone(def);
let nid = `${subflowInstanceId}-${node.id}` //redUtil.generateId();
let nid = redUtil.generateId();
// console.log("Create Node In subflow",node._alias, "--->",nid, "(",node.type,")")
// node_map[node.id] = node;
node._alias = node.id;

View File

@@ -589,28 +589,17 @@ function deleteContext(id,flowId) {
* If flowConfig is undefined, all flow/node contexts will be removed
**/
function clean(flowConfig) {
flowConfig = flowConfig || { allNodes: {}, subflows: {} };
const knownNodes = new Set(Object.keys(flowConfig.allNodes))
// We need to alias all of the subflow instance contents
for (const subflow of Object.values(flowConfig.subflows || {})) {
subflow.instances.forEach(instance => {
for (const nodeId of Object.keys(subflow.nodes || {})) {
knownNodes.add(`${instance.id}-${nodeId}`)
}
for (const nodeId of Object.keys(subflow.configs || {})) {
knownNodes.add(`${instance.id}-${nodeId}`)
}
})
}
flowConfig = flowConfig || { allNodes: {} };
var promises = [];
for (const store of Object.values(stores)){
promises.push(store.clean(Array.from(knownNodes)));
for(var plugin in stores){
if(stores.hasOwnProperty(plugin)){
promises.push(stores[plugin].clean(Object.keys(flowConfig.allNodes)));
}
}
for (const id of Object.keys(contexts)) {
if (id !== "global") {
for (var id in contexts) {
if (contexts.hasOwnProperty(id) && id !== "global") {
var idParts = id.split(":");
if (!knownNodes.has(idParts[0])) {
if (!flowConfig.allNodes.hasOwnProperty(idParts[0])) {
delete contexts[id];
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/runtime",
"version": "3.1.0-beta.2",
"version": "3.1.0-beta.1",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,8 +16,8 @@
}
],
"dependencies": {
"@node-red/registry": "3.1.0-beta.2",
"@node-red/util": "3.1.0-beta.2",
"@node-red/registry": "3.1.0-beta.1",
"@node-red/util": "3.1.0-beta.1",
"async-mutex": "0.4.0",
"clone": "2.1.2",
"express": "4.18.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/util",
"version": "3.1.0-beta.2",
"version": "3.1.0-beta.1",
"license": "Apache-2.0",
"repository": {
"type": "git",

View File

@@ -1,6 +1,6 @@
{
"name": "node-red",
"version": "3.1.0-beta.2",
"version": "3.1.0-beta.1",
"description": "Low-code programming for event-driven applications",
"homepage": "http://nodered.org",
"license": "Apache-2.0",
@@ -31,10 +31,10 @@
"flow"
],
"dependencies": {
"@node-red/editor-api": "3.1.0-beta.2",
"@node-red/runtime": "3.1.0-beta.2",
"@node-red/util": "3.1.0-beta.2",
"@node-red/nodes": "3.1.0-beta.2",
"@node-red/editor-api": "3.1.0-beta.1",
"@node-red/runtime": "3.1.0-beta.1",
"@node-red/util": "3.1.0-beta.1",
"@node-red/nodes": "3.1.0-beta.1",
"basic-auth": "2.0.1",
"bcryptjs": "2.4.3",
"express": "4.18.2",

View File

@@ -458,7 +458,7 @@ httpsPromise.then(function(startupHttps) {
RED.start().then(function() {
if (settings.httpAdminRoot !== false || settings.httpNodeRoot !== false || settings.httpStatic) {
server.on('error', function(err) {
if (err.code === "EADDRINUSE") {
if (err.errno === "EADDRINUSE") {
RED.log.error(RED.log._("server.unable-to-listen", {listenpath:getListenPath()}));
RED.log.error(RED.log._("server.port-in-use"));
} else {

View File

@@ -416,7 +416,7 @@ module.exports = {
*/
// theme: "vs",
/** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc.
* for the full list, see https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IStandaloneEditorConstructionOptions.html
* for the full list, see https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneEditorConstructionOptions.html
*/
//fontSize: 14,
//fontFamily: "Cascadia Code, Fira Code, Consolas, 'Courier New', monospace",

View File

@@ -194,55 +194,6 @@ describe('file Nodes', function() {
});
});
it('should append to a file and add newline, except last line of multipart input', function(done) {
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":false, wires: [["helperNode1"]]},
{id:"helperNode1", type:"helper"}];
try {
fs.unlinkSync(fileToTest);
} catch(err) {
}
helper.load(fileNode, flow, function() {
var n1 = helper.getNode("fileNode1");
var n2 = helper.getNode("helperNode1");
var count = 0;
//var data = ["Line1", "Line2"];
n2.on("input", function (msg) {
try {
msg.should.have.property("payload");
//data.should.containDeep([msg.payload]);
if (count === 3) {
var f = fs.readFileSync(fileToTest).toString();
if (os.type() !== "Windows_NT") {
f.should.have.length(23);
f.should.equal("Line1\nLine2\nLine3\nLine4");
}
else {
f.should.have.length(23);
f.should.equal("Line1\r\nLine2\r\nLine3\r\nLine4");
}
done();
}
count++;
}
catch (e) {
done(e);
}
});
n1.receive({payload:"Line1",parts:{index:0,type:"string"}}); // string
setTimeout(function() {
n1.receive({payload:"Line2",parts:{index:1,type:"string"}}); // string
},30);
setTimeout(function() {
n1.receive({payload:"Line3",parts:{index:2,type:"string"}}); // string
},60);
setTimeout(function() {
n1.receive({payload:"Line4",parts:{index:3,type:"string",count:4}}); // string
},90);
});
});
it('should append to a file after it has been deleted ', function(done) {
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":false, wires: [["helperNode1"]]},
{id:"helperNode1", type:"helper"}];