Merge branch 'dev' into dev
31
CHANGELOG.md
@ -1,3 +1,34 @@
|
|||||||
|
#### 4.0.0-beta.1: Beta Release
|
||||||
|
|
||||||
|
Editor
|
||||||
|
|
||||||
|
- Click on id in debug panel highlights node or flow (#4439) @ralphwetzel
|
||||||
|
- Support config selection in a subflow env var (#4587) @Steve-Mcl
|
||||||
|
- Add timestamp formatting options to TypedInput (#4468) @knolleary
|
||||||
|
- Allow RED.view.select to select links (#4553) @lgrkvst
|
||||||
|
- Add auto-complete to flow/global/env typedInput types (#4480) @knolleary
|
||||||
|
- Improve the appearance of the Node-RED primary header (#4598) @joepavitt
|
||||||
|
|
||||||
|
Runtime
|
||||||
|
|
||||||
|
- let settings.httpNodeAuth accept single middleware or array of middlewares (#4572) @kevinGodell
|
||||||
|
- Upgrade to JSONata 2.x (#4590) @knolleary
|
||||||
|
- Bump minimum version to node 18 (#4571) @knolleary
|
||||||
|
- npm: Remove production flag on npm invocation (#4347) @ZJvandeWeg
|
||||||
|
- Timer testing fix (#4367) @hlovdal
|
||||||
|
- Bump to 4.0.0-dev (#4322) @knolleary
|
||||||
|
|
||||||
|
Nodes
|
||||||
|
|
||||||
|
- TCP node - when resetting, if no payload, stay disconnected @dceejay
|
||||||
|
- HTML node: add option for collecting attributes and content (#4513) @gorenje
|
||||||
|
- let split node specify property to split on, and join auto join correctly (#4386) @dceejay
|
||||||
|
- Add RFC4180 compliant mode to CSV node (#4540) @Steve-Mcl
|
||||||
|
- Fix change node to return boolean if asked (#4525) @dceejay
|
||||||
|
- Let msg.reset reset Tcp request node connection when in stay connected mode (#4406) @dceejay
|
||||||
|
- Let debug node status msg length be settable via settings (#4402) @dceejay
|
||||||
|
- Feat: Add ability to set headers for WebSocket client (#4436) @marcus-j-davies
|
||||||
|
|
||||||
#### 3.1.7: Maintenance Release
|
#### 3.1.7: Maintenance Release
|
||||||
|
|
||||||
- Add Japanese translation for v3.1.6 (#4603) @kazuhitoyokoi
|
- Add Japanese translation for v3.1.6 (#4603) @kazuhitoyokoi
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-red",
|
"name": "node-red",
|
||||||
"version": "4.0.0-dev",
|
"version": "4.0.0-beta.1",
|
||||||
"description": "Low-code programming for event-driven applications",
|
"description": "Low-code programming for event-driven applications",
|
||||||
"homepage": "https://nodered.org",
|
"homepage": "https://nodered.org",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/editor-api",
|
"name": "@node-red/editor-api",
|
||||||
"version": "4.0.0-dev",
|
"version": "4.0.0-beta.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -16,8 +16,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/util": "4.0.0-dev",
|
"@node-red/util": "4.0.0-beta.1",
|
||||||
"@node-red/editor-client": "4.0.0-dev",
|
"@node-red/editor-client": "4.0.0-beta.1",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"body-parser": "1.20.2",
|
"body-parser": "1.20.2",
|
||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
|
@ -924,7 +924,8 @@
|
|||||||
"date": "timestamp",
|
"date": "timestamp",
|
||||||
"jsonata": "expression",
|
"jsonata": "expression",
|
||||||
"env": "env variable",
|
"env": "env variable",
|
||||||
"cred": "credential"
|
"cred": "credential",
|
||||||
|
"conf-types": "config node"
|
||||||
},
|
},
|
||||||
"date": {
|
"date": {
|
||||||
"format": {
|
"format": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/editor-client",
|
"name": "@node-red/editor-client",
|
||||||
"version": "4.0.0-dev",
|
"version": "4.0.0-beta.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -91,6 +91,31 @@ RED.nodes = (function() {
|
|||||||
getNodeTypes: function() {
|
getNodeTypes: function() {
|
||||||
return Object.keys(nodeDefinitions);
|
return Object.keys(nodeDefinitions);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* Get an array of node definitions
|
||||||
|
* @param {Object} options - options object
|
||||||
|
* @param {boolean} [options.configOnly] - if true, only return config nodes
|
||||||
|
* @param {function} [options.filter] - a filter function to apply to the list of nodes
|
||||||
|
* @returns array of node definitions
|
||||||
|
*/
|
||||||
|
getNodeDefinitions: function(options) {
|
||||||
|
const result = []
|
||||||
|
const configOnly = (options && options.configOnly)
|
||||||
|
const filter = (options && options.filter)
|
||||||
|
const keys = Object.keys(nodeDefinitions)
|
||||||
|
for (const key of keys) {
|
||||||
|
const def = nodeDefinitions[key]
|
||||||
|
if(!def) { continue }
|
||||||
|
if (configOnly && def.category !== "config") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (filter && !filter(nodeDefinitions[key])) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result.push(nodeDefinitions[key])
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
},
|
||||||
setNodeList: function(list) {
|
setNodeList: function(list) {
|
||||||
nodeList = [];
|
nodeList = [];
|
||||||
for(var i=0;i<list.length;i++) {
|
for(var i=0;i<list.length;i++) {
|
||||||
|
@ -174,12 +174,24 @@
|
|||||||
this.uiContainer.width(m[1]);
|
this.uiContainer.width(m[1]);
|
||||||
}
|
}
|
||||||
if (this.options.sortable) {
|
if (this.options.sortable) {
|
||||||
|
var isCanceled = false; // Flag to track if an item has been canceled from being dropped into a different list
|
||||||
|
var noDrop = false; // Flag to track if an item is being dragged into a different list
|
||||||
var handle = (typeof this.options.sortable === 'string')?
|
var handle = (typeof this.options.sortable === 'string')?
|
||||||
this.options.sortable :
|
this.options.sortable :
|
||||||
".red-ui-editableList-item-handle";
|
".red-ui-editableList-item-handle";
|
||||||
var sortOptions = {
|
var sortOptions = {
|
||||||
axis: "y",
|
axis: "y",
|
||||||
update: function( event, ui ) {
|
update: function( event, ui ) {
|
||||||
|
// dont trigger update if the item is being canceled
|
||||||
|
const targetList = $(event.target);
|
||||||
|
const draggedItem = ui.item;
|
||||||
|
const draggedItemParent = draggedItem.parent();
|
||||||
|
if (!targetList.is(draggedItemParent) && draggedItem.hasClass("red-ui-editableList-item-constrained")) {
|
||||||
|
noDrop = true;
|
||||||
|
}
|
||||||
|
if (isCanceled || noDrop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (that.options.sortItems) {
|
if (that.options.sortItems) {
|
||||||
that.options.sortItems(that.items());
|
that.options.sortItems(that.items());
|
||||||
}
|
}
|
||||||
@ -189,8 +201,32 @@
|
|||||||
tolerance: "pointer",
|
tolerance: "pointer",
|
||||||
forcePlaceholderSize:true,
|
forcePlaceholderSize:true,
|
||||||
placeholder: "red-ui-editabelList-item-placeholder",
|
placeholder: "red-ui-editabelList-item-placeholder",
|
||||||
start: function(e, ui){
|
start: function (event, ui) {
|
||||||
ui.placeholder.height(ui.item.height()-4);
|
isCanceled = false;
|
||||||
|
ui.placeholder.height(ui.item.height() - 4);
|
||||||
|
ui.item.css('cursor', 'grabbing'); // TODO: this doesn't seem to work, use a class instead?
|
||||||
|
},
|
||||||
|
stop: function (event, ui) {
|
||||||
|
ui.item.css('cursor', 'auto');
|
||||||
|
},
|
||||||
|
receive: function (event, ui) {
|
||||||
|
if (ui.item.hasClass("red-ui-editableList-item-constrained")) {
|
||||||
|
isCanceled = true;
|
||||||
|
$(ui.sender).sortable('cancel');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
over: function (event, ui) {
|
||||||
|
// if the dragged item is constrained, prevent it from being dropped into a different list
|
||||||
|
const targetList = $(event.target);
|
||||||
|
const draggedItem = ui.item;
|
||||||
|
const draggedItemParent = draggedItem.parent();
|
||||||
|
if (!targetList.is(draggedItemParent) && draggedItem.hasClass("red-ui-editableList-item-constrained")) {
|
||||||
|
noDrop = true;
|
||||||
|
draggedItem.css('cursor', 'no-drop'); // TODO: this doesn't seem to work, use a class instead?
|
||||||
|
} else {
|
||||||
|
noDrop = false;
|
||||||
|
draggedItem.css('cursor', 'grabbing'); // TODO: this doesn't seem to work, use a class instead?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (this.options.connectWith) {
|
if (this.options.connectWith) {
|
||||||
|
@ -596,18 +596,75 @@
|
|||||||
eyeButton.show();
|
eyeButton.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'conf-types': {
|
||||||
|
value: "conf-types",
|
||||||
|
label: "config",
|
||||||
|
icon: "fa fa-cog",
|
||||||
|
// hasValue: false,
|
||||||
|
valueLabel: function (container, value) {
|
||||||
|
// get the selected option (for access to the "name" and "module" properties)
|
||||||
|
const _options = this._optionsCache || this.typeList.find(opt => opt.value === value)?.options || []
|
||||||
|
const selectedOption = _options.find(opt => opt.value === value) || {
|
||||||
|
title: '',
|
||||||
|
name: '',
|
||||||
|
module: ''
|
||||||
|
}
|
||||||
|
container.attr("title", selectedOption.title) // set tooltip to the full path/id of the module/node
|
||||||
|
container.text(selectedOption.name) // apply the "name" of the selected option
|
||||||
|
// set "line-height" such as to make the "name" appear further up, giving room for the "module" to be displayed below the value
|
||||||
|
container.css("line-height", "1.4em")
|
||||||
|
// add the module name in smaller, lighter font below the value
|
||||||
|
$('<div></div>').text(selectedOption.module).css({
|
||||||
|
// "font-family": "var(--red-ui-monospace-font)",
|
||||||
|
color: "var(--red-ui-tertiary-text-color)",
|
||||||
|
"font-size": "0.8em",
|
||||||
|
"line-height": "1em",
|
||||||
|
opacity: 0.8
|
||||||
|
}).appendTo(container);
|
||||||
|
},
|
||||||
|
// hasValue: false,
|
||||||
|
options: function () {
|
||||||
|
if (this._optionsCache) {
|
||||||
|
return this._optionsCache
|
||||||
|
}
|
||||||
|
const configNodes = RED.nodes.registry.getNodeDefinitions({configOnly: true, filter: (def) => def.type !== "global-config"}).map((def) => {
|
||||||
|
// create a container with with 2 rows (row 1 for the name, row 2 for the module name in smaller, lighter font)
|
||||||
|
const container = $('<div style="display: flex; flex-direction: column; justify-content: space-between; row-gap: 1px;">')
|
||||||
|
const row1Name = $('<div>').text(def.type)
|
||||||
|
const row2Module = $('<div style="font-size: 0.8em; color: var(--red-ui-tertiary-text-color);">').text(def.set.module)
|
||||||
|
container.append(row1Name, row2Module)
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: def.type,
|
||||||
|
name: def.type,
|
||||||
|
enabled: def.set.enabled ?? true,
|
||||||
|
local: def.set.local,
|
||||||
|
title: def.set.id, // tooltip e.g. "node-red-contrib-foo/bar"
|
||||||
|
module: def.set.module,
|
||||||
|
icon: container[0].outerHTML.trim(), // the typeInput will interpret this as html text and render it in the anchor
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this._optionsCache = configNodes
|
||||||
|
return configNodes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// For a type with options, check value is a valid selection
|
// For a type with options, check value is a valid selection
|
||||||
// If !opt.multiple, returns the valid option object
|
// If !opt.multiple, returns the valid option object
|
||||||
// if opt.multiple, returns an array of valid option objects
|
// if opt.multiple, returns an array of valid option objects
|
||||||
// If not valid, returns null;
|
// If not valid, returns null;
|
||||||
|
|
||||||
function isOptionValueValid(opt, currentVal) {
|
function isOptionValueValid(opt, currentVal) {
|
||||||
|
let _options = opt.options
|
||||||
|
if (typeof _options === "function") {
|
||||||
|
_options = _options.call(this)
|
||||||
|
}
|
||||||
if (!opt.multiple) {
|
if (!opt.multiple) {
|
||||||
for (var i=0;i<opt.options.length;i++) {
|
for (var i=0;i<_options.length;i++) {
|
||||||
op = opt.options[i];
|
op = _options[i];
|
||||||
if (typeof op === "string" && op === currentVal) {
|
if (typeof op === "string" && op === currentVal) {
|
||||||
return {value:currentVal}
|
return {value:currentVal}
|
||||||
} else if (op.value === currentVal) {
|
} else if (op.value === currentVal) {
|
||||||
@ -624,8 +681,8 @@
|
|||||||
currentValues[v] = true;
|
currentValues[v] = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
for (var i=0;i<opt.options.length;i++) {
|
for (var i=0;i<_options.length;i++) {
|
||||||
op = opt.options[i];
|
op = _options[i];
|
||||||
var val = typeof op === "string" ? op : op.value;
|
var val = typeof op === "string" ? op : op.value;
|
||||||
if (currentValues.hasOwnProperty(val)) {
|
if (currentValues.hasOwnProperty(val)) {
|
||||||
delete currentValues[val];
|
delete currentValues[val];
|
||||||
@ -1056,7 +1113,9 @@
|
|||||||
if (this.optionMenu) {
|
if (this.optionMenu) {
|
||||||
this.optionMenu.remove();
|
this.optionMenu.remove();
|
||||||
}
|
}
|
||||||
|
if (this.menu) {
|
||||||
this.menu.remove();
|
this.menu.remove();
|
||||||
|
}
|
||||||
this.uiSelect.remove();
|
this.uiSelect.remove();
|
||||||
},
|
},
|
||||||
types: function(types) {
|
types: function(types) {
|
||||||
@ -1089,7 +1148,7 @@
|
|||||||
this.menu = this._createMenu(this.typeList,{},function(v) { that.type(v) });
|
this.menu = this._createMenu(this.typeList,{},function(v) { that.type(v) });
|
||||||
if (currentType && !this.typeMap.hasOwnProperty(currentType)) {
|
if (currentType && !this.typeMap.hasOwnProperty(currentType)) {
|
||||||
if (!firstCall) {
|
if (!firstCall) {
|
||||||
this.type(this.typeList[0].value);
|
this.type(this.typeList[0]?.value || ""); // permit empty typeList
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.propertyType = null;
|
this.propertyType = null;
|
||||||
@ -1126,6 +1185,11 @@
|
|||||||
var selectedOption = [];
|
var selectedOption = [];
|
||||||
var valueToCheck = value;
|
var valueToCheck = value;
|
||||||
if (opt.options) {
|
if (opt.options) {
|
||||||
|
let _options = opt.options
|
||||||
|
if (typeof opt.options === "function") {
|
||||||
|
_options = opt.options.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
if (opt.hasValue && opt.parse) {
|
if (opt.hasValue && opt.parse) {
|
||||||
var parts = opt.parse(value);
|
var parts = opt.parse(value);
|
||||||
if (this.options.debug) { console.log(this.identifier,"new parse",parts) }
|
if (this.options.debug) { console.log(this.identifier,"new parse",parts) }
|
||||||
@ -1139,8 +1203,8 @@
|
|||||||
checkValues = valueToCheck.split(",");
|
checkValues = valueToCheck.split(",");
|
||||||
}
|
}
|
||||||
checkValues.forEach(function(valueToCheck) {
|
checkValues.forEach(function(valueToCheck) {
|
||||||
for (var i=0;i<opt.options.length;i++) {
|
for (var i=0;i<_options.length;i++) {
|
||||||
var op = opt.options[i];
|
var op = _options[i];
|
||||||
if (typeof op === "string") {
|
if (typeof op === "string") {
|
||||||
if (op === valueToCheck || op === ""+valueToCheck) {
|
if (op === valueToCheck || op === ""+valueToCheck) {
|
||||||
selectedOption.push(that.activeOptions[op]);
|
selectedOption.push(that.activeOptions[op]);
|
||||||
@ -1175,7 +1239,7 @@
|
|||||||
},
|
},
|
||||||
type: function(type) {
|
type: function(type) {
|
||||||
if (!arguments.length) {
|
if (!arguments.length) {
|
||||||
return this.propertyType;
|
return this.propertyType || this.options?.default || '';
|
||||||
} else {
|
} else {
|
||||||
var that = this;
|
var that = this;
|
||||||
if (this.options.debug) { console.log(this.identifier,"----- SET TYPE -----",type) }
|
if (this.options.debug) { console.log(this.identifier,"----- SET TYPE -----",type) }
|
||||||
@ -1276,6 +1340,10 @@
|
|||||||
this.optionMenu = null;
|
this.optionMenu = null;
|
||||||
}
|
}
|
||||||
if (opt.options) {
|
if (opt.options) {
|
||||||
|
let _options = opt.options
|
||||||
|
if (typeof _options === "function") {
|
||||||
|
_options = opt.options.call(this);
|
||||||
|
}
|
||||||
if (this.optionExpandButton) {
|
if (this.optionExpandButton) {
|
||||||
this.optionExpandButton.hide();
|
this.optionExpandButton.hide();
|
||||||
this.optionExpandButton.shown = false;
|
this.optionExpandButton.shown = false;
|
||||||
@ -1292,7 +1360,7 @@
|
|||||||
this.valueLabelContainer.hide();
|
this.valueLabelContainer.hide();
|
||||||
}
|
}
|
||||||
this.activeOptions = {};
|
this.activeOptions = {};
|
||||||
opt.options.forEach(function(o) {
|
_options.forEach(function(o) {
|
||||||
if (typeof o === 'string') {
|
if (typeof o === 'string') {
|
||||||
that.activeOptions[o] = {label:o,value:o};
|
that.activeOptions[o] = {label:o,value:o};
|
||||||
} else {
|
} else {
|
||||||
@ -1312,7 +1380,7 @@
|
|||||||
if (validValues) {
|
if (validValues) {
|
||||||
that._updateOptionSelectLabel(validValues)
|
that._updateOptionSelectLabel(validValues)
|
||||||
} else {
|
} else {
|
||||||
op = opt.options[0];
|
op = _options[0] || {value:""}; // permit zero options
|
||||||
if (typeof op === "string") {
|
if (typeof op === "string") {
|
||||||
this.value(op);
|
this.value(op);
|
||||||
that._updateOptionSelectLabel({value:op});
|
that._updateOptionSelectLabel({value:op});
|
||||||
@ -1331,7 +1399,7 @@
|
|||||||
that._updateOptionSelectLabel(validValues);
|
that._updateOptionSelectLabel(validValues);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var selectedOption = this.optionValue||opt.options[0];
|
var selectedOption = this.optionValue||_options[0];
|
||||||
if (opt.parse) {
|
if (opt.parse) {
|
||||||
var selectedOptionObj = typeof selectedOption === "string"?{value:selectedOption}:selectedOption
|
var selectedOptionObj = typeof selectedOption === "string"?{value:selectedOption}:selectedOption
|
||||||
var parts = opt.parse(this.input.val(),selectedOptionObj);
|
var parts = opt.parse(this.input.val(),selectedOptionObj);
|
||||||
@ -1375,7 +1443,7 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.optionMenu = this._createMenu(opt.options,opt,function(v){
|
this.optionMenu = this._createMenu(_options,opt,function(v){
|
||||||
if (!opt.multiple) {
|
if (!opt.multiple) {
|
||||||
that._updateOptionSelectLabel(that.activeOptions[v]);
|
that._updateOptionSelectLabel(that.activeOptions[v]);
|
||||||
if (!opt.hasValue) {
|
if (!opt.hasValue) {
|
||||||
|
@ -326,47 +326,78 @@ RED.editor = (function() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a config-node select box for this property
|
* Create a config-node select box for this property
|
||||||
* @param node - the node being edited
|
* @param {Object} node - the node being edited
|
||||||
* @param property - the name of the field
|
* @param {String} property - the name of the node property
|
||||||
* @param type - the type of the config-node
|
* @param {String} type - the type of the config-node
|
||||||
|
* @param {"node-config-input"|"node-input"|"node-input-subflow-env"} prefix - the prefix to use in the input element ids
|
||||||
|
* @param {Function} [filter] - a function to filter the list of config nodes
|
||||||
|
* @param {Object} [env] - the environment variable object (only used for subflow env vars)
|
||||||
*/
|
*/
|
||||||
function prepareConfigNodeSelect(node,property,type,prefix,filter) {
|
function prepareConfigNodeSelect(node, property, type, prefix, filter, env) {
|
||||||
var input = $("#"+prefix+"-"+property);
|
let nodeValue
|
||||||
if (input.length === 0 ) {
|
if (prefix === 'node-input-subflow-env') {
|
||||||
|
nodeValue = env?.value
|
||||||
|
} else {
|
||||||
|
nodeValue = node[property]
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttonId = `${prefix}-lookup-${property}`
|
||||||
|
const selectId = prefix + '-' + property
|
||||||
|
const input = $(`#${selectId}`);
|
||||||
|
if (input.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var newWidth = input.width();
|
const attrStyle = input.attr('style');
|
||||||
var attrStyle = input.attr('style');
|
let newWidth;
|
||||||
var m;
|
let m;
|
||||||
if ((m = /(^|\s|;)width\s*:\s*([^;]+)/i.exec(attrStyle)) !== null) {
|
if ((m = /(^|\s|;)width\s*:\s*([^;]+)/i.exec(attrStyle)) !== null) {
|
||||||
newWidth = m[2].trim();
|
newWidth = m[2].trim();
|
||||||
} else {
|
} else {
|
||||||
newWidth = "70%";
|
newWidth = "70%";
|
||||||
}
|
}
|
||||||
var outerWrap = $("<div></div>").css({
|
const outerWrap = $("<div></div>").css({
|
||||||
width: newWidth,
|
width: newWidth,
|
||||||
display:'inline-flex'
|
display: 'inline-flex'
|
||||||
});
|
});
|
||||||
var select = $('<select id="'+prefix+'-'+property+'"></select>').appendTo(outerWrap);
|
const select = $('<select id="' + selectId + '"></select>').appendTo(outerWrap);
|
||||||
input.replaceWith(outerWrap);
|
input.replaceWith(outerWrap);
|
||||||
// set the style attr directly - using width() on FF causes a value of 114%...
|
// set the style attr directly - using width() on FF causes a value of 114%...
|
||||||
select.css({
|
select.css({
|
||||||
'flex-grow': 1
|
'flex-grow': 1
|
||||||
});
|
});
|
||||||
updateConfigNodeSelect(property,type,node[property],prefix,filter);
|
updateConfigNodeSelect(property, type, nodeValue, prefix, filter);
|
||||||
$('<a id="'+prefix+'-lookup-'+property+'" class="red-ui-button"><i class="fa fa-pencil"></i></a>')
|
const disableButton = function(disabled) {
|
||||||
.css({"margin-left":"10px"})
|
btn.prop( "disabled", !!disabled)
|
||||||
|
btn.toggleClass("disabled", !!disabled)
|
||||||
|
}
|
||||||
|
// create the edit button
|
||||||
|
const btn = $('<a id="' + buttonId + '" class="red-ui-button"><i class="fa fa-pencil"></i></a>')
|
||||||
|
.css({ "margin-left": "10px" })
|
||||||
.appendTo(outerWrap);
|
.appendTo(outerWrap);
|
||||||
$('#'+prefix+'-lookup-'+property).on("click", function(e) {
|
|
||||||
showEditConfigNodeDialog(property,type,select.find(":selected").val(),prefix,node);
|
// add the click handler
|
||||||
|
btn.on("click", function (e) {
|
||||||
|
const selectedOpt = select.find(":selected")
|
||||||
|
if (selectedOpt.data('env')) { return } // don't show the dialog for env vars items (MVP. Future enhancement: lookup the env, if present, show the associated edit dialog)
|
||||||
|
if (btn.prop("disabled")) { return }
|
||||||
|
showEditConfigNodeDialog(property, type, selectedOpt.val(), prefix, node);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// dont permit the user to click the button if the selected option is an env var
|
||||||
|
select.on("change", function () {
|
||||||
|
const selectedOpt = select.find(":selected")
|
||||||
|
if (selectedOpt?.data('env')) {
|
||||||
|
disableButton(true)
|
||||||
|
} else {
|
||||||
|
disableButton(false)
|
||||||
|
}
|
||||||
|
});
|
||||||
var label = "";
|
var label = "";
|
||||||
var configNode = RED.nodes.node(node[property]);
|
var configNode = RED.nodes.node(nodeValue);
|
||||||
var node_def = RED.nodes.getType(type);
|
|
||||||
|
|
||||||
if (configNode) {
|
if (configNode) {
|
||||||
label = RED.utils.getNodeLabel(configNode,configNode.id);
|
label = RED.utils.getNodeLabel(configNode, configNode.id);
|
||||||
}
|
}
|
||||||
input.val(label);
|
input.val(label);
|
||||||
}
|
}
|
||||||
@ -768,12 +799,9 @@ RED.editor = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function defaultConfigNodeSort(A,B) {
|
function defaultConfigNodeSort(A,B) {
|
||||||
if (A.__label__ < B.__label__) {
|
// sort case insensitive so that `[env] node-name` items are at the top and
|
||||||
return -1;
|
// not mixed inbetween the the lower and upper case items
|
||||||
} else if (A.__label__ > B.__label__) {
|
return (A.__label__ || '').localeCompare((B.__label__ || ''), undefined, {sensitivity: 'base'})
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateConfigNodeSelect(name,type,value,prefix,filter) {
|
function updateConfigNodeSelect(name,type,value,prefix,filter) {
|
||||||
@ -788,7 +816,7 @@ RED.editor = (function() {
|
|||||||
}
|
}
|
||||||
$("#"+prefix+"-"+name).val(value);
|
$("#"+prefix+"-"+name).val(value);
|
||||||
} else {
|
} else {
|
||||||
|
let inclSubflowEnvvars = false
|
||||||
var select = $("#"+prefix+"-"+name);
|
var select = $("#"+prefix+"-"+name);
|
||||||
var node_def = RED.nodes.getType(type);
|
var node_def = RED.nodes.getType(type);
|
||||||
select.children().remove();
|
select.children().remove();
|
||||||
@ -796,6 +824,7 @@ RED.editor = (function() {
|
|||||||
var activeWorkspace = RED.nodes.workspace(RED.workspaces.active());
|
var activeWorkspace = RED.nodes.workspace(RED.workspaces.active());
|
||||||
if (!activeWorkspace) {
|
if (!activeWorkspace) {
|
||||||
activeWorkspace = RED.nodes.subflow(RED.workspaces.active());
|
activeWorkspace = RED.nodes.subflow(RED.workspaces.active());
|
||||||
|
inclSubflowEnvvars = true
|
||||||
}
|
}
|
||||||
|
|
||||||
var configNodes = [];
|
var configNodes = [];
|
||||||
@ -811,6 +840,31 @@ RED.editor = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// as includeSubflowEnvvars is true, this is a subflow.
|
||||||
|
// include any 'conf-types' env vars as a list of avaiable configs
|
||||||
|
// in the config dropdown as `[env] node-name`
|
||||||
|
if (inclSubflowEnvvars && activeWorkspace.env) {
|
||||||
|
const parentEnv = activeWorkspace.env.filter(env => env.ui?.type === 'conf-types' && env.type === type)
|
||||||
|
if (parentEnv && parentEnv.length > 0) {
|
||||||
|
const locale = RED.i18n.lang()
|
||||||
|
for (let i = 0; i < parentEnv.length; i++) {
|
||||||
|
const tenv = parentEnv[i]
|
||||||
|
const ui = tenv.ui || {}
|
||||||
|
const labels = ui.label || {}
|
||||||
|
const labelText = RED.editor.envVarList.lookupLabel(labels, labels["en-US"] || tenv.name, locale)
|
||||||
|
const config = {
|
||||||
|
env: tenv,
|
||||||
|
id: '${' + parentEnv[0].name + '}',
|
||||||
|
type: type,
|
||||||
|
label: labelText,
|
||||||
|
__label__: `[env] ${labelText}`
|
||||||
|
}
|
||||||
|
configNodes.push(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var configSortFn = defaultConfigNodeSort;
|
var configSortFn = defaultConfigNodeSort;
|
||||||
if (typeof node_def.sort == "function") {
|
if (typeof node_def.sort == "function") {
|
||||||
configSortFn = node_def.sort;
|
configSortFn = node_def.sort;
|
||||||
@ -822,7 +876,10 @@ RED.editor = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
configNodes.forEach(function(cn) {
|
configNodes.forEach(function(cn) {
|
||||||
$('<option value="'+cn.id+'"'+(value==cn.id?" selected":"")+'></option>').text(RED.text.bidi.enforceTextDirectionWithUCC(cn.__label__)).appendTo(select);
|
const option = $('<option value="'+cn.id+'"'+(value==cn.id?" selected":"")+'></option>').text(RED.text.bidi.enforceTextDirectionWithUCC(cn.__label__)).appendTo(select);
|
||||||
|
if (cn.env) {
|
||||||
|
option.data('env', cn.env) // set a data attribute to indicate this is an env var (to inhibit the edit button)
|
||||||
|
}
|
||||||
delete cn.__label__;
|
delete cn.__label__;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1483,11 +1540,18 @@ RED.editor = (function() {
|
|||||||
}
|
}
|
||||||
RED.tray.close(function() {
|
RED.tray.close(function() {
|
||||||
var filter = null;
|
var filter = null;
|
||||||
if (editContext && typeof editContext._def.defaults[configProperty].filter === 'function') {
|
// when editing a config via subflow edit panel, the `configProperty` will not
|
||||||
|
// necessarily be a property of the editContext._def.defaults object
|
||||||
|
// Also, when editing via dashboard sidebar, editContext can be null
|
||||||
|
// so we need to guard both scenarios
|
||||||
|
if (editContext?._def) {
|
||||||
|
const isSubflow = (editContext._def.type === 'subflow' || /subflow:.*/.test(editContext._def.type))
|
||||||
|
if (editContext && !isSubflow && typeof editContext._def.defaults?.[configProperty]?.filter === 'function') {
|
||||||
filter = function(n) {
|
filter = function(n) {
|
||||||
return editContext._def.defaults[configProperty].filter.call(editContext,n);
|
return editContext._def.defaults[configProperty].filter.call(editContext,n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
updateConfigNodeSelect(configProperty,configType,editing_config_node.id,prefix,filter);
|
updateConfigNodeSelect(configProperty,configType,editing_config_node.id,prefix,filter);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1546,7 +1610,7 @@ RED.editor = (function() {
|
|||||||
RED.history.push(historyEvent);
|
RED.history.push(historyEvent);
|
||||||
RED.tray.close(function() {
|
RED.tray.close(function() {
|
||||||
var filter = null;
|
var filter = null;
|
||||||
if (editContext && typeof editContext._def.defaults[configProperty].filter === 'function') {
|
if (editContext && typeof editContext._def.defaults[configProperty]?.filter === 'function') {
|
||||||
filter = function(n) {
|
filter = function(n) {
|
||||||
return editContext._def.defaults[configProperty].filter.call(editContext,n);
|
return editContext._def.defaults[configProperty].filter.call(editContext,n);
|
||||||
}
|
}
|
||||||
@ -2132,6 +2196,7 @@ RED.editor = (function() {
|
|||||||
filteredEditPanes[type] = filter
|
filteredEditPanes[type] = filter
|
||||||
}
|
}
|
||||||
editPanes[type] = definition;
|
editPanes[type] = definition;
|
||||||
}
|
},
|
||||||
|
prepareConfigNodeSelect: prepareConfigNodeSelect,
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
RED.editor.envVarList = (function() {
|
RED.editor.envVarList = (function() {
|
||||||
|
|
||||||
var currentLocale = 'en-US';
|
var currentLocale = 'en-US';
|
||||||
var DEFAULT_ENV_TYPE_LIST = ['str','num','bool','json','bin','env'];
|
const DEFAULT_ENV_TYPE_LIST = ['str','num','bool','json','bin','env'];
|
||||||
var DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred','jsonata'];
|
const DEFAULT_ENV_TYPE_LIST_INC_CONFTYPES = ['str','num','bool','json','bin','env','conf-types'];
|
||||||
|
const DEFAULT_ENV_TYPE_LIST_INC_CRED = ['str','num','bool','json','bin','env','cred','jsonata'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create env var edit interface
|
* Create env var edit interface
|
||||||
@ -10,8 +11,8 @@ RED.editor.envVarList = (function() {
|
|||||||
* @param node - subflow node
|
* @param node - subflow node
|
||||||
*/
|
*/
|
||||||
function buildPropertiesList(envContainer, node) {
|
function buildPropertiesList(envContainer, node) {
|
||||||
|
if(RED.editor.envVarList.debug) { console.log('envVarList: buildPropertiesList', envContainer, node) }
|
||||||
var isTemplateNode = (node.type === "subflow");
|
const isTemplateNode = (node.type === "subflow");
|
||||||
|
|
||||||
envContainer
|
envContainer
|
||||||
.css({
|
.css({
|
||||||
@ -83,7 +84,14 @@ RED.editor.envVarList = (function() {
|
|||||||
// if `opt.ui` does not exist, then apply defaults. If these
|
// if `opt.ui` does not exist, then apply defaults. If these
|
||||||
// defaults do not change then they will get stripped off
|
// defaults do not change then they will get stripped off
|
||||||
// before saving.
|
// before saving.
|
||||||
if (opt.type === 'cred') {
|
if (opt.type === 'conf-types') {
|
||||||
|
opt.ui = opt.ui || {
|
||||||
|
icon: "fa fa-cog",
|
||||||
|
type: "conf-types",
|
||||||
|
opts: {opts:[]}
|
||||||
|
}
|
||||||
|
opt.ui.type = "conf-types";
|
||||||
|
} else if (opt.type === 'cred') {
|
||||||
opt.ui = opt.ui || {
|
opt.ui = opt.ui || {
|
||||||
icon: "",
|
icon: "",
|
||||||
type: "cred"
|
type: "cred"
|
||||||
@ -119,7 +127,7 @@ RED.editor.envVarList = (function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
buildEnvEditRow(uiRow, opt.ui, nameField, valueField);
|
buildEnvEditRow(uiRow, opt, nameField, valueField);
|
||||||
nameField.trigger('change');
|
nameField.trigger('change');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -181,18 +189,20 @@ RED.editor.envVarList = (function() {
|
|||||||
* @param nameField - name field of env var
|
* @param nameField - name field of env var
|
||||||
* @param valueField - value field of env var
|
* @param valueField - value field of env var
|
||||||
*/
|
*/
|
||||||
function buildEnvEditRow(container, ui, nameField, valueField) {
|
function buildEnvEditRow(container, opt, nameField, valueField) {
|
||||||
|
const ui = opt.ui
|
||||||
|
if(RED.editor.envVarList.debug) { console.log('envVarList: buildEnvEditRow', container, ui, nameField, valueField) }
|
||||||
container.addClass("red-ui-editor-subflow-env-ui-row")
|
container.addClass("red-ui-editor-subflow-env-ui-row")
|
||||||
var topRow = $('<div></div>').appendTo(container);
|
var topRow = $('<div></div>').appendTo(container);
|
||||||
$('<div></div>').appendTo(topRow);
|
$('<div></div>').appendTo(topRow);
|
||||||
$('<div>').text(RED._("editor.icon")).appendTo(topRow);
|
$('<div>').text(RED._("editor.icon")).appendTo(topRow);
|
||||||
$('<div>').text(RED._("editor.label")).appendTo(topRow);
|
$('<div>').text(RED._("editor.label")).appendTo(topRow);
|
||||||
$('<div>').text(RED._("editor.inputType")).appendTo(topRow);
|
$('<div class="red-env-ui-input-type-col">').text(RED._("editor.inputType")).appendTo(topRow);
|
||||||
|
|
||||||
var row = $('<div></div>').appendTo(container);
|
var row = $('<div></div>').appendTo(container);
|
||||||
$('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row);
|
$('<div><i class="red-ui-editableList-item-handle fa fa-bars"></i></div>').appendTo(row);
|
||||||
var typeOptions = {
|
var typeOptions = {
|
||||||
'input': {types:DEFAULT_ENV_TYPE_LIST},
|
'input': {types:DEFAULT_ENV_TYPE_LIST_INC_CONFTYPES},
|
||||||
'select': {opts:[]},
|
'select': {opts:[]},
|
||||||
'spinner': {},
|
'spinner': {},
|
||||||
'cred': {}
|
'cred': {}
|
||||||
@ -260,15 +270,16 @@ RED.editor.envVarList = (function() {
|
|||||||
labelInput.attr("placeholder",$(this).val())
|
labelInput.attr("placeholder",$(this).val())
|
||||||
});
|
});
|
||||||
|
|
||||||
var inputCell = $('<div></div>').appendTo(row);
|
var inputCell = $('<div class="red-env-ui-input-type-col"></div>').appendTo(row);
|
||||||
var inputCellInput = $('<input type="text">').css("width","100%").appendTo(inputCell);
|
var uiInputTypeInput = $('<input type="text">').css("width","100%").appendTo(inputCell);
|
||||||
if (ui.type === "input") {
|
if (ui.type === "input") {
|
||||||
inputCellInput.val(ui.opts.types.join(","));
|
uiInputTypeInput.val(ui.opts.types.join(","));
|
||||||
}
|
}
|
||||||
var checkbox;
|
var checkbox;
|
||||||
var selectBox;
|
var selectBox;
|
||||||
|
|
||||||
inputCellInput.typedInput({
|
// the options presented in the UI section for an "input" type selection
|
||||||
|
uiInputTypeInput.typedInput({
|
||||||
types: [
|
types: [
|
||||||
{
|
{
|
||||||
value:"input",
|
value:"input",
|
||||||
@ -429,7 +440,7 @@ RED.editor.envVarList = (function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.opts.opts = vals;
|
ui.opts.opts = vals;
|
||||||
inputCellInput.typedInput('value',Date.now())
|
uiInputTypeInput.typedInput('value',Date.now())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -496,12 +507,13 @@ RED.editor.envVarList = (function() {
|
|||||||
} else {
|
} else {
|
||||||
delete ui.opts.max;
|
delete ui.opts.max;
|
||||||
}
|
}
|
||||||
inputCellInput.typedInput('value',Date.now())
|
uiInputTypeInput.typedInput('value',Date.now())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'conf-types',
|
||||||
{
|
{
|
||||||
value:"none",
|
value:"none",
|
||||||
label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false
|
label:RED._("editor.inputs.none"), icon:"fa fa-times",hasValue:false
|
||||||
@ -519,14 +531,20 @@ RED.editor.envVarList = (function() {
|
|||||||
// In the case of 'input' type, the typedInput uses the multiple-option
|
// In the case of 'input' type, the typedInput uses the multiple-option
|
||||||
// mode. Its value needs to be set to a comma-separately list of the
|
// mode. Its value needs to be set to a comma-separately list of the
|
||||||
// selected options.
|
// selected options.
|
||||||
inputCellInput.typedInput('value',ui.opts.types.join(","))
|
uiInputTypeInput.typedInput('value',ui.opts.types.join(","))
|
||||||
|
} else if (ui.type === 'conf-types') {
|
||||||
|
// In the case of 'conf-types' type, the typedInput will be populated
|
||||||
|
// with a list of all config nodes types installed.
|
||||||
|
// Restore the value to the last selected type
|
||||||
|
uiInputTypeInput.typedInput('value', opt.type)
|
||||||
} else {
|
} else {
|
||||||
// No other type cares about `value`, but doing this will
|
// No other type cares about `value`, but doing this will
|
||||||
// force a refresh of the label now that `ui.opts` has
|
// force a refresh of the label now that `ui.opts` has
|
||||||
// been updated.
|
// been updated.
|
||||||
inputCellInput.typedInput('value',Date.now())
|
uiInputTypeInput.typedInput('value',Date.now())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(RED.editor.envVarList.debug) { console.log('envVarList: inputCellInput on:typedinputtypechange. ui.type = ' + ui.type) }
|
||||||
switch (ui.type) {
|
switch (ui.type) {
|
||||||
case 'input':
|
case 'input':
|
||||||
valueField.typedInput('types',ui.opts.types);
|
valueField.typedInput('types',ui.opts.types);
|
||||||
@ -544,7 +562,7 @@ RED.editor.envVarList = (function() {
|
|||||||
valueField.typedInput('types',['cred']);
|
valueField.typedInput('types',['cred']);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
valueField.typedInput('types',DEFAULT_ENV_TYPE_LIST)
|
valueField.typedInput('types', DEFAULT_ENV_TYPE_LIST);
|
||||||
}
|
}
|
||||||
if (ui.type === 'checkbox') {
|
if (ui.type === 'checkbox') {
|
||||||
valueField.typedInput('type','bool');
|
valueField.typedInput('type','bool');
|
||||||
@ -556,8 +574,46 @@ RED.editor.envVarList = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}).on("change", function(evt,type) {
|
}).on("change", function(evt,type) {
|
||||||
if (ui.type === 'input') {
|
const selectedType = $(this).typedInput('type') // the UI typedInput type
|
||||||
var types = inputCellInput.typedInput('value');
|
if(RED.editor.envVarList.debug) { console.log('envVarList: inputCellInput on:change. selectedType = ' + selectedType) }
|
||||||
|
if (selectedType === 'conf-types') {
|
||||||
|
const selectedConfigType = $(this).typedInput('value') || opt.type
|
||||||
|
let activeWorkspace = RED.nodes.workspace(RED.workspaces.active());
|
||||||
|
if (!activeWorkspace) {
|
||||||
|
activeWorkspace = RED.nodes.subflow(RED.workspaces.active());
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a list of all config nodes matching the selectedValue
|
||||||
|
const configNodes = [];
|
||||||
|
RED.nodes.eachConfig(function(config) {
|
||||||
|
if (config.type == selectedConfigType && (!config.z || config.z === activeWorkspace.id)) {
|
||||||
|
const modulePath = config._def?.set?.id || ''
|
||||||
|
let label = RED.utils.getNodeLabel(config, config.id) || config.id;
|
||||||
|
label += config.d ? ' ['+RED._('workspace.disabled')+']' : '';
|
||||||
|
const _config = {
|
||||||
|
_type: selectedConfigType,
|
||||||
|
value: config.id,
|
||||||
|
label: label,
|
||||||
|
title: modulePath ? modulePath + ' - ' + label : label,
|
||||||
|
enabled: config.d !== true,
|
||||||
|
disabled: config.d === true,
|
||||||
|
}
|
||||||
|
configNodes.push(_config);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const tiTypes = {
|
||||||
|
value: selectedConfigType,
|
||||||
|
label: "config",
|
||||||
|
icon: "fa fa-cog",
|
||||||
|
options: configNodes,
|
||||||
|
}
|
||||||
|
valueField.typedInput('types', [tiTypes]);
|
||||||
|
valueField.typedInput('type', selectedConfigType);
|
||||||
|
valueField.typedInput('value', opt.value);
|
||||||
|
|
||||||
|
|
||||||
|
} else if (ui.type === 'input') {
|
||||||
|
var types = uiInputTypeInput.typedInput('value');
|
||||||
ui.opts.types = (types === "") ? ["str"] : types.split(",");
|
ui.opts.types = (types === "") ? ["str"] : types.split(",");
|
||||||
valueField.typedInput('types',ui.opts.types);
|
valueField.typedInput('types',ui.opts.types);
|
||||||
}
|
}
|
||||||
@ -569,7 +625,7 @@ RED.editor.envVarList = (function() {
|
|||||||
})
|
})
|
||||||
// Set the input to the right type. This will trigger the 'typedinputtypechange'
|
// Set the input to the right type. This will trigger the 'typedinputtypechange'
|
||||||
// event handler (just above ^^) to update the value if needed
|
// event handler (just above ^^) to update the value if needed
|
||||||
inputCellInput.typedInput('type',ui.type)
|
uiInputTypeInput.typedInput('type',ui.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
function setLocale(l, list) {
|
function setLocale(l, list) {
|
||||||
|
@ -909,17 +909,19 @@ RED.subflow = (function() {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create interface for controlling env var UI definition
|
* Build the edit dialog for a subflow template (creating/modifying a subflow template)
|
||||||
|
* @param {Object} uiContainer - the jQuery container for the environment variable list
|
||||||
|
* @param {Object} node - the subflow template node
|
||||||
*/
|
*/
|
||||||
function buildEnvControl(envList,node) {
|
function buildEnvControl(uiContainer,node) {
|
||||||
var tabs = RED.tabs.create({
|
var tabs = RED.tabs.create({
|
||||||
id: "subflow-env-tabs",
|
id: "subflow-env-tabs",
|
||||||
onchange: function(tab) {
|
onchange: function(tab) {
|
||||||
if (tab.id === "subflow-env-tab-preview") {
|
if (tab.id === "subflow-env-tab-preview") {
|
||||||
var inputContainer = $("#subflow-input-ui");
|
var inputContainer = $("#subflow-input-ui");
|
||||||
var list = envList.editableList("items");
|
var list = uiContainer.editableList("items");
|
||||||
var exportedEnv = exportEnvList(list, true);
|
var exportedEnv = exportEnvList(list, true);
|
||||||
buildEnvUI(inputContainer, exportedEnv,node);
|
buildEnvUI(inputContainer, exportedEnv, node);
|
||||||
}
|
}
|
||||||
$("#subflow-env-tabs-content").children().hide();
|
$("#subflow-env-tabs-content").children().hide();
|
||||||
$("#" + tab.id).show();
|
$("#" + tab.id).show();
|
||||||
@ -957,12 +959,33 @@ RED.subflow = (function() {
|
|||||||
RED.editor.envVarList.setLocale(locale);
|
RED.editor.envVarList.setLocale(locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
function buildEnvUIRow(row, tenv, ui, node) {
|
* Build a UI row for a subflow instance environment variable
|
||||||
|
* Also used to build the UI row for subflow template preview
|
||||||
|
* @param {JQuery} row - A form row element
|
||||||
|
* @param {Object} tenv - A template environment variable
|
||||||
|
* @param {String} tenv.name - The name of the environment variable
|
||||||
|
* @param {String} tenv.type - The type of the environment variable
|
||||||
|
* @param {String} tenv.value - The value set for this environment variable
|
||||||
|
* @param {Object} tenv.parent - The parent environment variable
|
||||||
|
* @param {String} tenv.parent.value - The value set for the parent environment variable
|
||||||
|
* @param {String} tenv.parent.type - The type of the parent environment variable
|
||||||
|
* @param {Object} tenv.ui - The UI configuration for the environment variable
|
||||||
|
* @param {String} tenv.ui.icon - The icon for the environment variable
|
||||||
|
* @param {Object} tenv.ui.label - The label for the environment variable
|
||||||
|
* @param {String} tenv.ui.type - The type of the UI control for the environment variable
|
||||||
|
* @param {Object} node - The subflow instance node
|
||||||
|
*/
|
||||||
|
function buildEnvUIRow(row, tenv, node) {
|
||||||
|
if(RED.subflow.debug) { console.log("buildEnvUIRow", tenv) }
|
||||||
|
const ui = tenv.ui || {}
|
||||||
ui.label = ui.label||{};
|
ui.label = ui.label||{};
|
||||||
if ((tenv.type === "cred" || (tenv.parent && tenv.parent.type === "cred")) && !ui.type) {
|
if ((tenv.type === "cred" || (tenv.parent && tenv.parent.type === "cred")) && !ui.type) {
|
||||||
ui.type = "cred";
|
ui.type = "cred";
|
||||||
ui.opts = {};
|
ui.opts = {};
|
||||||
|
} else if (tenv.type === "conf-types") {
|
||||||
|
ui.type = "conf-types"
|
||||||
|
ui.opts = { types: ['conf-types'] }
|
||||||
} else if (!ui.type) {
|
} else if (!ui.type) {
|
||||||
ui.type = "input";
|
ui.type = "input";
|
||||||
ui.opts = { types: RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST }
|
ui.opts = { types: RED.editor.envVarList.DEFAULT_ENV_TYPE_LIST }
|
||||||
@ -1006,9 +1029,10 @@ RED.subflow = (function() {
|
|||||||
if (tenv.hasOwnProperty('type')) {
|
if (tenv.hasOwnProperty('type')) {
|
||||||
val.type = tenv.type;
|
val.type = tenv.type;
|
||||||
}
|
}
|
||||||
|
const elId = getSubflowEnvPropertyName(tenv.name)
|
||||||
switch(ui.type) {
|
switch(ui.type) {
|
||||||
case "input":
|
case "input":
|
||||||
input = $('<input type="text">').css('width','70%').appendTo(row);
|
input = $('<input type="text">').css('width','70%').attr('id', elId).appendTo(row);
|
||||||
if (ui.opts.types && ui.opts.types.length > 0) {
|
if (ui.opts.types && ui.opts.types.length > 0) {
|
||||||
var inputType = val.type;
|
var inputType = val.type;
|
||||||
if (ui.opts.types.indexOf(inputType) === -1) {
|
if (ui.opts.types.indexOf(inputType) === -1) {
|
||||||
@ -1035,7 +1059,7 @@ RED.subflow = (function() {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "select":
|
case "select":
|
||||||
input = $('<select>').css('width','70%').appendTo(row);
|
input = $('<select>').css('width','70%').attr('id', elId).appendTo(row);
|
||||||
if (ui.opts.opts) {
|
if (ui.opts.opts) {
|
||||||
ui.opts.opts.forEach(function(o) {
|
ui.opts.opts.forEach(function(o) {
|
||||||
$('<option>').val(o.v).text(RED.editor.envVarList.lookupLabel(o.l, o.l['en-US']||o.v, locale)).appendTo(input);
|
$('<option>').val(o.v).text(RED.editor.envVarList.lookupLabel(o.l, o.l['en-US']||o.v, locale)).appendTo(input);
|
||||||
@ -1046,7 +1070,7 @@ RED.subflow = (function() {
|
|||||||
case "checkbox":
|
case "checkbox":
|
||||||
label.css("cursor","default");
|
label.css("cursor","default");
|
||||||
var cblabel = $('<label>').css('width','70%').appendTo(row);
|
var cblabel = $('<label>').css('width','70%').appendTo(row);
|
||||||
input = $('<input type="checkbox">').css({
|
input = $('<input type="checkbox">').attr('id', elId).css({
|
||||||
marginTop: 0,
|
marginTop: 0,
|
||||||
width: 'auto',
|
width: 'auto',
|
||||||
height: '34px'
|
height: '34px'
|
||||||
@ -1064,7 +1088,7 @@ RED.subflow = (function() {
|
|||||||
input.prop("checked",boolVal);
|
input.prop("checked",boolVal);
|
||||||
break;
|
break;
|
||||||
case "spinner":
|
case "spinner":
|
||||||
input = $('<input>').css('width','70%').appendTo(row);
|
input = $('<input>').css('width','70%').attr('id', elId).appendTo(row);
|
||||||
var spinnerOpts = {};
|
var spinnerOpts = {};
|
||||||
if (ui.opts.hasOwnProperty('min')) {
|
if (ui.opts.hasOwnProperty('min')) {
|
||||||
spinnerOpts.min = ui.opts.min;
|
spinnerOpts.min = ui.opts.min;
|
||||||
@ -1093,18 +1117,25 @@ RED.subflow = (function() {
|
|||||||
default: 'cred'
|
default: 'cred'
|
||||||
})
|
})
|
||||||
break;
|
break;
|
||||||
}
|
case "conf-types":
|
||||||
if (input) {
|
// let clsId = 'config-node-input-' + val.type + '-' + val.value + '-' + Math.floor(Math.random() * 100000);
|
||||||
input.attr('id',getSubflowEnvPropertyName(tenv.name))
|
// clsId = clsId.replace(/\W/g, '-');
|
||||||
|
// input = $('<input>').css('width','70%').addClass(clsId).attr('id', elId).appendTo(row);
|
||||||
|
input = $('<input>').css('width','70%').attr('id', elId).appendTo(row);
|
||||||
|
const _type = tenv.parent?.type || tenv.type;
|
||||||
|
RED.editor.prepareConfigNodeSelect(node, tenv.name, _type, 'node-input-subflow-env', null, tenv);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create environment variable input UI
|
* Build the edit form for a subflow instance
|
||||||
|
* Also used to build the preview form in the subflow template edit dialog
|
||||||
* @param uiContainer - container for UI
|
* @param uiContainer - container for UI
|
||||||
* @param envList - env var definitions of template
|
* @param envList - env var definitions of template
|
||||||
*/
|
*/
|
||||||
function buildEnvUI(uiContainer, envList, node) {
|
function buildEnvUI(uiContainer, envList, node) {
|
||||||
|
if(RED.subflow.debug) { console.log("buildEnvUI",envList) }
|
||||||
uiContainer.empty();
|
uiContainer.empty();
|
||||||
for (var i = 0; i < envList.length; i++) {
|
for (var i = 0; i < envList.length; i++) {
|
||||||
var tenv = envList[i];
|
var tenv = envList[i];
|
||||||
@ -1112,7 +1143,7 @@ RED.subflow = (function() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var row = $("<div/>", { class: "form-row" }).appendTo(uiContainer);
|
var row = $("<div/>", { class: "form-row" }).appendTo(uiContainer);
|
||||||
buildEnvUIRow(row,tenv, tenv.ui || {}, node);
|
buildEnvUIRow(row, tenv, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// buildEnvUI
|
// buildEnvUI
|
||||||
@ -1185,6 +1216,9 @@ RED.subflow = (function() {
|
|||||||
delete ui.opts
|
delete ui.opts
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "conf-types":
|
||||||
|
delete ui.opts;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
delete ui.opts;
|
delete ui.opts;
|
||||||
}
|
}
|
||||||
@ -1207,8 +1241,9 @@ RED.subflow = (function() {
|
|||||||
if (/^subflow:/.test(node.type)) {
|
if (/^subflow:/.test(node.type)) {
|
||||||
var subflowDef = RED.nodes.subflow(node.type.substring(8));
|
var subflowDef = RED.nodes.subflow(node.type.substring(8));
|
||||||
if (subflowDef.env) {
|
if (subflowDef.env) {
|
||||||
subflowDef.env.forEach(function(env) {
|
subflowDef.env.forEach(function(env, i) {
|
||||||
var item = {
|
var item = {
|
||||||
|
index: i,
|
||||||
name:env.name,
|
name:env.name,
|
||||||
parent: {
|
parent: {
|
||||||
type: env.type,
|
type: env.type,
|
||||||
@ -1273,6 +1308,7 @@ RED.subflow = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function exportSubflowInstanceEnv(node) {
|
function exportSubflowInstanceEnv(node) {
|
||||||
|
if(RED.subflow.debug) { console.log("exportSubflowInstanceEnv",node) }
|
||||||
var env = [];
|
var env = [];
|
||||||
// First, get the values for the SubflowTemplate defined properties
|
// First, get the values for the SubflowTemplate defined properties
|
||||||
// - these are the ones with custom UI elements
|
// - these are the ones with custom UI elements
|
||||||
@ -1319,6 +1355,9 @@ RED.subflow = (function() {
|
|||||||
item.type = 'bool';
|
item.type = 'bool';
|
||||||
item.value = ""+input.prop("checked");
|
item.value = ""+input.prop("checked");
|
||||||
break;
|
break;
|
||||||
|
case "conf-types":
|
||||||
|
item.value = input.val()
|
||||||
|
item.type = data.parent.value;
|
||||||
}
|
}
|
||||||
if (ui.type === "cred" || item.type !== data.parent.type || item.value !== data.parent.value) {
|
if (ui.type === "cred" || item.type !== data.parent.type || item.value !== data.parent.value) {
|
||||||
env.push(item);
|
env.push(item);
|
||||||
@ -1332,8 +1371,15 @@ RED.subflow = (function() {
|
|||||||
return 'node-input-subflow-env-'+name.replace(/[^a-z0-9-_]/ig,"_");
|
return 'node-input-subflow-env-'+name.replace(/[^a-z0-9-_]/ig,"_");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called by subflow.oneditprepare for both instances and templates
|
|
||||||
|
/**
|
||||||
|
* Build the subflow edit form
|
||||||
|
* Called by subflow.oneditprepare for both instances and templates
|
||||||
|
* @param {"subflow"|"subflow-template"} type - the type of subflow being edited
|
||||||
|
* @param {Object} node - the node being edited
|
||||||
|
*/
|
||||||
function buildEditForm(type,node) {
|
function buildEditForm(type,node) {
|
||||||
|
if(RED.subflow.debug) { console.log("buildEditForm",type,node) }
|
||||||
if (type === "subflow-template") {
|
if (type === "subflow-template") {
|
||||||
// This is the tabbed UI that offers the env list - with UI options
|
// This is the tabbed UI that offers the env list - with UI options
|
||||||
// plus the preview tab
|
// plus the preview tab
|
||||||
|
@ -435,10 +435,15 @@ RED.tourGuide = (function() {
|
|||||||
|
|
||||||
function listTour() {
|
function listTour() {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
id: "4_0",
|
||||||
|
label: "4.0",
|
||||||
|
path: "./tours/welcome.js"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "3_1",
|
id: "3_1",
|
||||||
label: "3.1",
|
label: "3.1",
|
||||||
path: "./tours/welcome.js"
|
path: "./tours/3.1/welcome.js"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "3_0",
|
id: "3_0",
|
||||||
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 189 KiB After Width: | Height: | Size: 189 KiB |
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
231
packages/node_modules/@node-red/editor-client/src/tours/3.1/welcome.js
vendored
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
export default {
|
||||||
|
version: "3.1.0",
|
||||||
|
steps: [
|
||||||
|
{
|
||||||
|
titleIcon: "fa fa-map-o",
|
||||||
|
title: {
|
||||||
|
"en-US": "Welcome to Node-RED 3.1!",
|
||||||
|
"ja": "Node-RED 3.1へようこそ!",
|
||||||
|
"fr": "Bienvenue dans Node-RED 3.1!"
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
"en-US": "<p>Let's take a moment to discover the new features in this release.</p>",
|
||||||
|
"ja": "<p>本リリースの新機能を見つけてみましょう。</p>",
|
||||||
|
"fr": "<p>Prenons un moment pour découvrir les nouvelles fonctionnalités de cette version.</p>"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
"en-US": "New ways to work with groups",
|
||||||
|
"ja": "グループの新たな操作方法",
|
||||||
|
"fr": "De nouvelles façons de travailler avec les groupes"
|
||||||
|
},
|
||||||
|
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>`,
|
||||||
|
"ja": `<p>エディタ上のグループの操作が変更されました。</p>
|
||||||
|
<ul>
|
||||||
|
<li>グループ内のノードをクリックする時に、グループが邪魔をすることが無くなりました。</li>
|
||||||
|
<li>「前面へ移動」と「背面へ移動」の動作を用いて、複数のグループの表示順序を変えることができます。</li>
|
||||||
|
<li>グループ内へ一度に複数のノードをドラッグできるようになりました。</li>
|
||||||
|
<li><code>Alt</code> を押したまま、グループ内のノードをドラッグすると、そのグループから *除く* ことができます。</li>
|
||||||
|
</ul>`,
|
||||||
|
"fr": `<p>Nous avons modifié la façon dont vous interagissez avec les groupes dans l'éditeur.</p>
|
||||||
|
<ul>
|
||||||
|
<li>Ils ne gênent plus lorsque vous cliquez sur un noeud</li>
|
||||||
|
<li>Ils peuvent être réorganisés à l'aide des actions Avancer et Reculer</li>
|
||||||
|
<li>Plusieurs noeuds peuvent être glissés dans un groupe en une seule fois</li>
|
||||||
|
<li>Maintenir <code>Alt</code> lors du déplacement d'un noeud le *supprimera* de son groupe</li>
|
||||||
|
</ul>`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
"en-US": "Change notification on tabs",
|
||||||
|
"ja": "タブ上の変更通知",
|
||||||
|
"fr": "Notification de changement sur les onglets"
|
||||||
|
},
|
||||||
|
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>`,
|
||||||
|
"ja": `<p>タブ内にデプロイされていない変更が存在する時は、ノードと同じスタイルで変更の印が表示されるようになりました。</p>
|
||||||
|
<p>これによって複数のフローを編集している時に、変更を見つけるのが簡単になりました。</p>`,
|
||||||
|
"fr": `<p>Lorsqu'un onglet contient des modifications non déployées, il affiche désormais le
|
||||||
|
même style d'icône de changement utilisé par les noeuds.</p>
|
||||||
|
<p>Cela facilitera grandement le suivi des modifications lorsque vous
|
||||||
|
travaillez sur plusieurs flux.</p>`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
"en-US": "A bigger canvas to work with",
|
||||||
|
"ja": "より広くなった作業キャンバス",
|
||||||
|
"fr": "Un canevas plus grand pour travailler"
|
||||||
|
},
|
||||||
|
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>`,
|
||||||
|
"ja": `<p>標準のキャンバスが広くなったため、1つのフローに沢山のものを含めることができるようになりました。</p>
|
||||||
|
<p>引き続き、サブフローやリンクノードなどの方法を用いて整理することをお勧めしますが、作業できる場所が増えました。</p>`,
|
||||||
|
"fr": `<p>La taille par défaut du canevas a été augmentée pour que vous puissiez en mettre plus
|
||||||
|
sur un seul flux.</p>
|
||||||
|
<p>Nous recommandons toujours d'utiliser des outils tels que les sous-flux et les noeuds de lien pour vous aider
|
||||||
|
à garder les choses organisées, mais vous avez maintenant plus d'espace pour travailler.</p>`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
"en-US": "Finding help",
|
||||||
|
"ja": "ヘルプを見つける",
|
||||||
|
"fr": "Trouver de l'aide"
|
||||||
|
},
|
||||||
|
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>`,
|
||||||
|
"ja": `<p>全てのノードの編集ダイアログの下に、ノードのヘルプへのリンクが追加されました。</p>
|
||||||
|
<p>これをクリックすると、ノードのヘルプサイドバーが表示されます。</p>`,
|
||||||
|
"fr": `<p>Toutes les boîtes de dialogue d'édition de noeud incluent désormais un lien vers l'aide de ce noeud
|
||||||
|
dans le pied de page.</p>
|
||||||
|
<p>Cliquer dessus ouvrira la barre latérale d'aide affichant l'aide pour ce noeud.</p>`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
"en-US": "Improved Context Menu",
|
||||||
|
"ja": "コンテキストメニューの改善",
|
||||||
|
"fr": "Menu contextuel amélioré"
|
||||||
|
},
|
||||||
|
image: 'images/context-menu.png',
|
||||||
|
description: {
|
||||||
|
"en-US": `<p>The editor's context menu has been expanded to make lots more of
|
||||||
|
the built-in actions available.</p>
|
||||||
|
<p>Adding nodes, working with groups and plenty
|
||||||
|
of other useful tools are now just a click away.</p>
|
||||||
|
<p>The flow tab bar also has its own context menu to make working
|
||||||
|
with your flows much easier.</p>`,
|
||||||
|
"ja": `<p>より多くの組み込み動作を利用できるように、エディタのコンテキストメニューが拡張されました。</p>
|
||||||
|
<p>ノードの追加、グループの操作、その他の便利なツールをクリックするだけで実行できるようになりました。</p>
|
||||||
|
<p>フローのタブバーには、フローの操作をより簡単にする独自のコンテキストメニューもあります。</p>`,
|
||||||
|
"fr": `<p>Le menu contextuel de l'éditeur a été étendu pour faire beaucoup plus d'actions intégrées disponibles.</p>
|
||||||
|
<p>Ajouter des noeuds, travailler avec des groupes et beaucoup d'autres outils utiles sont désormais à portée de clic.</p>
|
||||||
|
<p>La barre d'onglets de flux possède également son propre menu contextuel pour faciliter l'utilisation de vos flux.</p>`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
"en-US": "Hiding Flows",
|
||||||
|
"ja": "フローを非表示",
|
||||||
|
"fr": "Masquage de flux"
|
||||||
|
},
|
||||||
|
image: 'images/hiding-flows.png',
|
||||||
|
description: {
|
||||||
|
"en-US": `<p>Hiding flows is now done through the flow context menu.</p>
|
||||||
|
<p>The 'hide' button in previous releases has been removed from the tabs
|
||||||
|
as they were being clicked accidentally too often.</p>`,
|
||||||
|
"ja": `<p>フローを非表示にする機能は、フローのコンテキストメニューから実行するようになりました。</p>
|
||||||
|
<p>これまでのリリースでタブに存在していた「非表示」ボタンは、よく誤ってクリックされていたため、削除されました。</p>`,
|
||||||
|
"fr": `<p>Le masquage des flux s'effectue désormais via le menu contextuel du flux.</p>
|
||||||
|
<p>Le bouton "Masquer" des versions précédentes a été supprimé des onglets
|
||||||
|
car il était cliqué accidentellement trop souvent.</p>`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
"en-US": "Locking Flows",
|
||||||
|
"ja": "フローを固定",
|
||||||
|
"fr": "Verrouillage de flux"
|
||||||
|
},
|
||||||
|
image: 'images/locking-flows.png',
|
||||||
|
description: {
|
||||||
|
"en-US": `<p>Flows can now be locked to prevent accidental changes being made.</p>
|
||||||
|
<p>When locked you cannot modify the nodes in any way.</p>
|
||||||
|
<p>The flow context menu provides the options to lock and unlock flows,
|
||||||
|
as well as in the Info sidebar explorer.</p>`,
|
||||||
|
"ja": `<p>誤ってフローに変更が加えられてしまうのを防ぐために、フローを固定できるようになりました。</p>
|
||||||
|
<p>固定されている時は、ノードを修正することはできません。</p>
|
||||||
|
<p>フローのコンテキストメニューと、情報サイドバーのエクスプローラには、フローの固定や解除をするためのオプションが用意されています。</p>`,
|
||||||
|
"fr": `<p>Les flux peuvent désormais être verrouillés pour éviter toute modification accidentelle.</p>
|
||||||
|
<p>Lorsqu'il est verrouillé, vous ne pouvez en aucun cas modifier les noeuds.</p>
|
||||||
|
<p>Le menu contextuel du flux fournit les options pour verrouiller et déverrouiller les flux,
|
||||||
|
ainsi que dans l'explorateur de la barre latérale d'informations.</p>`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
"en-US": "Adding Images to node/flow descriptions",
|
||||||
|
"ja": "ノードやフローの説明へ画像を追加",
|
||||||
|
"fr": "Ajout d'images aux descriptions de noeud/flux"
|
||||||
|
},
|
||||||
|
// image: 'images/debug-path-tooltip.png',
|
||||||
|
description: {
|
||||||
|
"en-US": `<p>You can now add images to a node's or flows's description.</p>
|
||||||
|
<p>Simply drag the image into the text editor and it will get added inline.</p>
|
||||||
|
<p>When the description is shown in the Info sidebar, the image will be displayed.</p>`,
|
||||||
|
"ja": `<p>ノードまたはフローの説明に、画像を追加できるようになりました。</p>
|
||||||
|
<p>画像をテキストエディタにドラッグするだけで、行内に埋め込まれます。</p>
|
||||||
|
<p>情報サイドバーの説明を開くと、その画像が表示されます。</p>`,
|
||||||
|
"fr": `<p>Vous pouvez désormais ajouter des images à la description d'un noeud ou d'un flux.</p>
|
||||||
|
<p>Faites simplement glisser l'image dans l'éditeur de texte et elle sera ajoutée en ligne.</p>
|
||||||
|
<p>Lorsque la description s'affiche dans la barre latérale d'informations, l'image s'affiche.</p>`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
"en-US": "Adding Mermaid Diagrams",
|
||||||
|
"ja": "Mermaid図を追加",
|
||||||
|
"fr": "Ajout de diagrammes Mermaid"
|
||||||
|
},
|
||||||
|
image: 'images/mermaid.png',
|
||||||
|
description: {
|
||||||
|
"en-US": `<p>You can also add <a href="https://github.com/mermaid-js/mermaid">Mermaid</a> diagrams directly into your node or flow descriptions.</p>
|
||||||
|
<p>This gives you much richer options for documenting your flows.</p>`,
|
||||||
|
"ja": `<p>ノードやフローの説明に、<a href="https://github.com/mermaid-js/mermaid">Mermaid</a>図を直接追加することもできます。</p>
|
||||||
|
<p>これによって、フローを説明する文書作成の選択肢がより多くなります。</p>`,
|
||||||
|
"fr": `<p>Vous pouvez également ajouter des diagrammes <a href="https://github.com/mermaid-js/mermaid">Mermaid</a> directement dans vos descriptions de noeud ou de flux.</p>
|
||||||
|
<p>Cela vous offre des options beaucoup plus riches pour documenter vos flux.</p>`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
"en-US": "Managing Global Environment Variables",
|
||||||
|
"ja": "グローバル環境変数の管理",
|
||||||
|
"fr": "Gestion des variables d'environnement globales"
|
||||||
|
},
|
||||||
|
image: 'images/global-env-vars.png',
|
||||||
|
description: {
|
||||||
|
"en-US": `<p>You can set environment variables that apply to all nodes and flows in the new
|
||||||
|
'Global Environment Variables' section of User Settings.</p>`,
|
||||||
|
"ja": `<p>ユーザ設定に新しく追加された「大域環境変数」のセクションで、全てのノードとフローに適用される環境変数を登録できます。</p>`,
|
||||||
|
"fr": `<p>Vous pouvez définir des variables d'environnement qui s'appliquent à tous les noeuds et flux dans la nouvelle
|
||||||
|
section "Global Environment Variables" des paramètres utilisateur.</p>`
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
"en-US": "Node Updates",
|
||||||
|
"ja": "ノードの更新",
|
||||||
|
"fr": "Mises à jour des noeuds"
|
||||||
|
},
|
||||||
|
// image: "images/",
|
||||||
|
description: {
|
||||||
|
"en-US": `<p>The core nodes have received lots of minor fixes, documentation updates and
|
||||||
|
small enhancements. Check the full changelog in the Help sidebar for a full list.</p>`,
|
||||||
|
"ja": `<p>コアノードにマイナーな修正、ドキュメント更新、小規模な拡張が数多く追加されています。全ての一覧は、ヘルプサイドバーの全ての更新履歴を確認してください。</p>`,
|
||||||
|
"fr": `<p>Les noeuds principaux ont reçu de nombreux correctifs mineurs, mises à jour de la documentation et
|
||||||
|
petites améliorations. Consulter le journal des modifications complet dans la barre latérale d'aide.</p>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
packages/node_modules/@node-red/editor-client/src/tours/images/nr4-auto-complete.png
vendored
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
packages/node_modules/@node-red/editor-client/src/tours/images/nr4-sf-config.png
vendored
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
packages/node_modules/@node-red/editor-client/src/tours/images/nr4-timestamp-formatting.png
vendored
Normal file
After Width: | Height: | Size: 15 KiB |
@ -1,12 +1,12 @@
|
|||||||
export default {
|
export default {
|
||||||
version: "3.1.0",
|
version: "4.0.0-beta.1",
|
||||||
steps: [
|
steps: [
|
||||||
{
|
{
|
||||||
titleIcon: "fa fa-map-o",
|
titleIcon: "fa fa-map-o",
|
||||||
title: {
|
title: {
|
||||||
"en-US": "Welcome to Node-RED 3.1!",
|
"en-US": "Welcome to Node-RED 4.0 Beta 1!",
|
||||||
"ja": "Node-RED 3.1へようこそ!",
|
"ja": "Node-RED 4.0 Beta 0へようこそ!",
|
||||||
"fr": "Bienvenue dans Node-RED 3.1!"
|
"fr": "Bienvenue dans Node-RED 4.0 Beta 1!"
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
"en-US": "<p>Let's take a moment to discover the new features in this release.</p>",
|
"en-US": "<p>Let's take a moment to discover the new features in this release.</p>",
|
||||||
@ -16,202 +16,49 @@ export default {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: {
|
title: {
|
||||||
"en-US": "New ways to work with groups",
|
"en-US": "Timestamp formatting options",
|
||||||
"ja": "グループの新たな操作方法",
|
// "ja": ""
|
||||||
"fr": "De nouvelles façons de travailler avec les groupes"
|
|
||||||
},
|
},
|
||||||
|
image: 'images/nr4-timestamp-formatting.png',
|
||||||
description: {
|
description: {
|
||||||
"en-US": `<p>We have changed how you interact with groups in the editor.</p>
|
"en-US": `<p>Nodes that let you set a timestamp now have options on what format that timestamp should be in.</p>
|
||||||
|
<p>We're keeping it simple to begin with by providing three options:<p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>They don't get in the way when clicking on a node</li>
|
<li>Milliseconds since epoch - this is existing behaviour of the timestamp option</li>
|
||||||
<li>They can be reordered using the Moving Forwards and Move Backwards actions</li>
|
<li>ISO 8601 - a common format used by many systems</li>
|
||||||
<li>Multiple nodes can be dragged into a group in one go</li>
|
<li>JavaScript Data Object</li>
|
||||||
<li>Holding <code>Alt</code> when dragging a node will *remove* it from its group</li>
|
|
||||||
</ul>`,
|
</ul>`,
|
||||||
"ja": `<p>エディタ上のグループの操作が変更されました。</p>
|
// "ja": ``
|
||||||
<ul>
|
|
||||||
<li>グループ内のノードをクリックする時に、グループが邪魔をすることが無くなりました。</li>
|
|
||||||
<li>「前面へ移動」と「背面へ移動」の動作を用いて、複数のグループの表示順序を変えることができます。</li>
|
|
||||||
<li>グループ内へ一度に複数のノードをドラッグできるようになりました。</li>
|
|
||||||
<li><code>Alt</code> を押したまま、グループ内のノードをドラッグすると、そのグループから *除く* ことができます。</li>
|
|
||||||
</ul>`,
|
|
||||||
"fr": `<p>Nous avons modifié la façon dont vous interagissez avec les groupes dans l'éditeur.</p>
|
|
||||||
<ul>
|
|
||||||
<li>Ils ne gênent plus lorsque vous cliquez sur un noeud</li>
|
|
||||||
<li>Ils peuvent être réorganisés à l'aide des actions Avancer et Reculer</li>
|
|
||||||
<li>Plusieurs noeuds peuvent être glissés dans un groupe en une seule fois</li>
|
|
||||||
<li>Maintenir <code>Alt</code> lors du déplacement d'un noeud le *supprimera* de son groupe</li>
|
|
||||||
</ul>`
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: {
|
title: {
|
||||||
"en-US": "Change notification on tabs",
|
"en-US": "Auto-complete of flow/global and env types",
|
||||||
"ja": "タブ上の変更通知",
|
// "ja": ""
|
||||||
"fr": "Notification de changement sur les onglets"
|
|
||||||
},
|
},
|
||||||
image: 'images/tab-changes.png',
|
image: 'images/nr4-auto-complete.png',
|
||||||
description: {
|
description: {
|
||||||
"en-US": `<p>When a tab contains undeployed changes it now shows the
|
"en-US": `<p>The <code>flow</code>/<code>global</code> context inputs and the <code>env</code> input
|
||||||
same style of change icon used by nodes.</p>
|
now all include auto-complete suggestions based on the live state of your flows.</p>
|
||||||
<p>This will make it much easier to track down changes when you're
|
`,
|
||||||
working across multiple flows.</p>`,
|
// "ja": ``
|
||||||
"ja": `<p>タブ内にデプロイされていない変更が存在する時は、ノードと同じスタイルで変更の印が表示されるようになりました。</p>
|
|
||||||
<p>これによって複数のフローを編集している時に、変更を見つけるのが簡単になりました。</p>`,
|
|
||||||
"fr": `<p>Lorsqu'un onglet contient des modifications non déployées, il affiche désormais le
|
|
||||||
même style d'icône de changement utilisé par les noeuds.</p>
|
|
||||||
<p>Cela facilitera grandement le suivi des modifications lorsque vous
|
|
||||||
travaillez sur plusieurs flux.</p>`
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: {
|
title: {
|
||||||
"en-US": "A bigger canvas to work with",
|
"en-US": "Config node customisation in Subflows",
|
||||||
"ja": "より広くなった作業キャンバス",
|
// "ja": ""
|
||||||
"fr": "Un canevas plus grand pour travailler"
|
|
||||||
},
|
},
|
||||||
|
image: 'images/nr4-sf-config.png',
|
||||||
description: {
|
description: {
|
||||||
"en-US": `<p>The default canvas size has been increased so you can fit more
|
"en-US": `<p>Subflows can now be customised to allow each instance to use a different
|
||||||
into one flow.</p>
|
config node of a selected type.</p>
|
||||||
<p>We still recommend using tools such as subflows and Link Nodes to help
|
<p>For example, each instance of a subflow that connects to an MQTT Broker and does some post-processing
|
||||||
keep things organised, but now you have more room to work in.</p>`,
|
of the messages received can be pointed at a different broker.</p>
|
||||||
"ja": `<p>標準のキャンバスが広くなったため、1つのフローに沢山のものを含めることができるようになりました。</p>
|
`,
|
||||||
<p>引き続き、サブフローやリンクノードなどの方法を用いて整理することをお勧めしますが、作業できる場所が増えました。</p>`,
|
// "ja": ``
|
||||||
"fr": `<p>La taille par défaut du canevas a été augmentée pour que vous puissiez en mettre plus
|
|
||||||
sur un seul flux.</p>
|
|
||||||
<p>Nous recommandons toujours d'utiliser des outils tels que les sous-flux et les noeuds de lien pour vous aider
|
|
||||||
à garder les choses organisées, mais vous avez maintenant plus d'espace pour travailler.</p>`
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: {
|
|
||||||
"en-US": "Finding help",
|
|
||||||
"ja": "ヘルプを見つける",
|
|
||||||
"fr": "Trouver de l'aide"
|
|
||||||
},
|
|
||||||
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>`,
|
|
||||||
"ja": `<p>全てのノードの編集ダイアログの下に、ノードのヘルプへのリンクが追加されました。</p>
|
|
||||||
<p>これをクリックすると、ノードのヘルプサイドバーが表示されます。</p>`,
|
|
||||||
"fr": `<p>Toutes les boîtes de dialogue d'édition de noeud incluent désormais un lien vers l'aide de ce noeud
|
|
||||||
dans le pied de page.</p>
|
|
||||||
<p>Cliquer dessus ouvrira la barre latérale d'aide affichant l'aide pour ce noeud.</p>`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
"en-US": "Improved Context Menu",
|
|
||||||
"ja": "コンテキストメニューの改善",
|
|
||||||
"fr": "Menu contextuel amélioré"
|
|
||||||
},
|
|
||||||
image: 'images/context-menu.png',
|
|
||||||
description: {
|
|
||||||
"en-US": `<p>The editor's context menu has been expanded to make lots more of
|
|
||||||
the built-in actions available.</p>
|
|
||||||
<p>Adding nodes, working with groups and plenty
|
|
||||||
of other useful tools are now just a click away.</p>
|
|
||||||
<p>The flow tab bar also has its own context menu to make working
|
|
||||||
with your flows much easier.</p>`,
|
|
||||||
"ja": `<p>より多くの組み込み動作を利用できるように、エディタのコンテキストメニューが拡張されました。</p>
|
|
||||||
<p>ノードの追加、グループの操作、その他の便利なツールをクリックするだけで実行できるようになりました。</p>
|
|
||||||
<p>フローのタブバーには、フローの操作をより簡単にする独自のコンテキストメニューもあります。</p>`,
|
|
||||||
"fr": `<p>Le menu contextuel de l'éditeur a été étendu pour faire beaucoup plus d'actions intégrées disponibles.</p>
|
|
||||||
<p>Ajouter des noeuds, travailler avec des groupes et beaucoup d'autres outils utiles sont désormais à portée de clic.</p>
|
|
||||||
<p>La barre d'onglets de flux possède également son propre menu contextuel pour faciliter l'utilisation de vos flux.</p>`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
"en-US": "Hiding Flows",
|
|
||||||
"ja": "フローを非表示",
|
|
||||||
"fr": "Masquage de flux"
|
|
||||||
},
|
|
||||||
image: 'images/hiding-flows.png',
|
|
||||||
description: {
|
|
||||||
"en-US": `<p>Hiding flows is now done through the flow context menu.</p>
|
|
||||||
<p>The 'hide' button in previous releases has been removed from the tabs
|
|
||||||
as they were being clicked accidentally too often.</p>`,
|
|
||||||
"ja": `<p>フローを非表示にする機能は、フローのコンテキストメニューから実行するようになりました。</p>
|
|
||||||
<p>これまでのリリースでタブに存在していた「非表示」ボタンは、よく誤ってクリックされていたため、削除されました。</p>`,
|
|
||||||
"fr": `<p>Le masquage des flux s'effectue désormais via le menu contextuel du flux.</p>
|
|
||||||
<p>Le bouton "Masquer" des versions précédentes a été supprimé des onglets
|
|
||||||
car il était cliqué accidentellement trop souvent.</p>`
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
"en-US": "Locking Flows",
|
|
||||||
"ja": "フローを固定",
|
|
||||||
"fr": "Verrouillage de flux"
|
|
||||||
},
|
|
||||||
image: 'images/locking-flows.png',
|
|
||||||
description: {
|
|
||||||
"en-US": `<p>Flows can now be locked to prevent accidental changes being made.</p>
|
|
||||||
<p>When locked you cannot modify the nodes in any way.</p>
|
|
||||||
<p>The flow context menu provides the options to lock and unlock flows,
|
|
||||||
as well as in the Info sidebar explorer.</p>`,
|
|
||||||
"ja": `<p>誤ってフローに変更が加えられてしまうのを防ぐために、フローを固定できるようになりました。</p>
|
|
||||||
<p>固定されている時は、ノードを修正することはできません。</p>
|
|
||||||
<p>フローのコンテキストメニューと、情報サイドバーのエクスプローラには、フローの固定や解除をするためのオプションが用意されています。</p>`,
|
|
||||||
"fr": `<p>Les flux peuvent désormais être verrouillés pour éviter toute modification accidentelle.</p>
|
|
||||||
<p>Lorsqu'il est verrouillé, vous ne pouvez en aucun cas modifier les noeuds.</p>
|
|
||||||
<p>Le menu contextuel du flux fournit les options pour verrouiller et déverrouiller les flux,
|
|
||||||
ainsi que dans l'explorateur de la barre latérale d'informations.</p>`
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
"en-US": "Adding Images to node/flow descriptions",
|
|
||||||
"ja": "ノードやフローの説明へ画像を追加",
|
|
||||||
"fr": "Ajout d'images aux descriptions de noeud/flux"
|
|
||||||
},
|
|
||||||
// image: 'images/debug-path-tooltip.png',
|
|
||||||
description: {
|
|
||||||
"en-US": `<p>You can now add images to a node's or flows's description.</p>
|
|
||||||
<p>Simply drag the image into the text editor and it will get added inline.</p>
|
|
||||||
<p>When the description is shown in the Info sidebar, the image will be displayed.</p>`,
|
|
||||||
"ja": `<p>ノードまたはフローの説明に、画像を追加できるようになりました。</p>
|
|
||||||
<p>画像をテキストエディタにドラッグするだけで、行内に埋め込まれます。</p>
|
|
||||||
<p>情報サイドバーの説明を開くと、その画像が表示されます。</p>`,
|
|
||||||
"fr": `<p>Vous pouvez désormais ajouter des images à la description d'un noeud ou d'un flux.</p>
|
|
||||||
<p>Faites simplement glisser l'image dans l'éditeur de texte et elle sera ajoutée en ligne.</p>
|
|
||||||
<p>Lorsque la description s'affiche dans la barre latérale d'informations, l'image s'affiche.</p>`
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
"en-US": "Adding Mermaid Diagrams",
|
|
||||||
"ja": "Mermaid図を追加",
|
|
||||||
"fr": "Ajout de diagrammes Mermaid"
|
|
||||||
},
|
|
||||||
image: 'images/mermaid.png',
|
|
||||||
description: {
|
|
||||||
"en-US": `<p>You can also add <a href="https://github.com/mermaid-js/mermaid">Mermaid</a> diagrams directly into your node or flow descriptions.</p>
|
|
||||||
<p>This gives you much richer options for documenting your flows.</p>`,
|
|
||||||
"ja": `<p>ノードやフローの説明に、<a href="https://github.com/mermaid-js/mermaid">Mermaid</a>図を直接追加することもできます。</p>
|
|
||||||
<p>これによって、フローを説明する文書作成の選択肢がより多くなります。</p>`,
|
|
||||||
"fr": `<p>Vous pouvez également ajouter des diagrammes <a href="https://github.com/mermaid-js/mermaid">Mermaid</a> directement dans vos descriptions de noeud ou de flux.</p>
|
|
||||||
<p>Cela vous offre des options beaucoup plus riches pour documenter vos flux.</p>`
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: {
|
|
||||||
"en-US": "Managing Global Environment Variables",
|
|
||||||
"ja": "グローバル環境変数の管理",
|
|
||||||
"fr": "Gestion des variables d'environnement globales"
|
|
||||||
},
|
|
||||||
image: 'images/global-env-vars.png',
|
|
||||||
description: {
|
|
||||||
"en-US": `<p>You can set environment variables that apply to all nodes and flows in the new
|
|
||||||
'Global Environment Variables' section of User Settings.</p>`,
|
|
||||||
"ja": `<p>ユーザ設定に新しく追加された「大域環境変数」のセクションで、全てのノードとフローに適用される環境変数を登録できます。</p>`,
|
|
||||||
"fr": `<p>Vous pouvez définir des variables d'environnement qui s'appliquent à tous les noeuds et flux dans la nouvelle
|
|
||||||
section "Global Environment Variables" des paramètres utilisateur.</p>`
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: {
|
title: {
|
||||||
"en-US": "Node Updates",
|
"en-US": "Node Updates",
|
||||||
@ -221,10 +68,13 @@ export default {
|
|||||||
// image: "images/",
|
// image: "images/",
|
||||||
description: {
|
description: {
|
||||||
"en-US": `<p>The core nodes have received lots of minor fixes, documentation updates and
|
"en-US": `<p>The core nodes have received lots of minor fixes, documentation updates and
|
||||||
small enhancements. Check the full changelog in the Help sidebar for a full list.</p>`,
|
small enhancements. Check the full changelog in the Help sidebar for a full list.</p>
|
||||||
"ja": `<p>コアノードにマイナーな修正、ドキュメント更新、小規模な拡張が数多く追加されています。全ての一覧は、ヘルプサイドバーの全ての更新履歴を確認してください。</p>`,
|
<ul>
|
||||||
"fr": `<p>Les noeuds principaux ont reçu de nombreux correctifs mineurs, mises à jour de la documentation et
|
<li>A fully RFC4180 compliant CSV mode</li>
|
||||||
petites améliorations. Consulter le journal des modifications complet dans la barre latérale d'aide.</p>`
|
<li>Customisable headers on the WebSocket node</li>
|
||||||
|
<li>Split node now can operate on any message property</li>
|
||||||
|
<li>and lots more...</li>
|
||||||
|
</ul>`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/nodes",
|
"name": "@node-red/nodes",
|
||||||
"version": "4.0.0-dev",
|
"version": "4.0.0-beta.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/registry",
|
"name": "@node-red/registry",
|
||||||
"version": "4.0.0-dev",
|
"version": "4.0.0-beta.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -16,7 +16,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/util": "4.0.0-dev",
|
"@node-red/util": "4.0.0-beta.1",
|
||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
"fs-extra": "11.1.1",
|
"fs-extra": "11.1.1",
|
||||||
"semver": "7.5.4",
|
"semver": "7.5.4",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/runtime",
|
"name": "@node-red/runtime",
|
||||||
"version": "4.0.0-dev",
|
"version": "4.0.0-beta.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"main": "./lib/index.js",
|
"main": "./lib/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@ -16,8 +16,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/registry": "4.0.0-dev",
|
"@node-red/registry": "4.0.0-beta.1",
|
||||||
"@node-red/util": "4.0.0-dev",
|
"@node-red/util": "4.0.0-beta.1",
|
||||||
"async-mutex": "0.4.0",
|
"async-mutex": "0.4.0",
|
||||||
"clone": "2.1.2",
|
"clone": "2.1.2",
|
||||||
"express": "4.18.2",
|
"express": "4.18.2",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@node-red/util",
|
"name": "@node-red/util",
|
||||||
"version": "4.0.0-dev",
|
"version": "4.0.0-beta.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
10
packages/node_modules/node-red/package.json
vendored
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-red",
|
"name": "node-red",
|
||||||
"version": "4.0.0-dev",
|
"version": "4.0.0-beta.1",
|
||||||
"description": "Low-code programming for event-driven applications",
|
"description": "Low-code programming for event-driven applications",
|
||||||
"homepage": "https://nodered.org",
|
"homepage": "https://nodered.org",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@ -31,10 +31,10 @@
|
|||||||
"flow"
|
"flow"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-red/editor-api": "4.0.0-dev",
|
"@node-red/editor-api": "4.0.0-beta.1",
|
||||||
"@node-red/runtime": "4.0.0-dev",
|
"@node-red/runtime": "4.0.0-beta.1",
|
||||||
"@node-red/util": "4.0.0-dev",
|
"@node-red/util": "4.0.0-beta.1",
|
||||||
"@node-red/nodes": "4.0.0-dev",
|
"@node-red/nodes": "4.0.0-beta.1",
|
||||||
"basic-auth": "2.0.1",
|
"basic-auth": "2.0.1",
|
||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"express": "4.18.2",
|
"express": "4.18.2",
|
||||||
|
@ -4,7 +4,7 @@ const path = require("path");
|
|||||||
const fs = require("fs-extra");
|
const fs = require("fs-extra");
|
||||||
const should = require("should");
|
const should = require("should");
|
||||||
|
|
||||||
const LATEST = "3";
|
const LATEST = "4";
|
||||||
|
|
||||||
function generateScript() {
|
function generateScript() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|