diff --git a/Gruntfile.js b/Gruntfile.js
index 261f0c72b..a5a43d053 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -199,7 +199,8 @@ module.exports = function(grunt) {
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/projectUserSettings.js",
"packages/node_modules/@node-red/editor-client/src/js/ui/projects/tab-versionControl.js",
- "packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js"
+ "packages/node_modules/@node-red/editor-client/src/js/ui/touch/radialMenu.js",
+ "packages/node_modules/@node-red/editor-client/src/js/ui/tour/*.js"
],
dest: "packages/node_modules/@node-red/editor-client/public/red/red.js"
},
@@ -326,6 +327,12 @@ module.exports = function(grunt) {
],
tasks: ['jsonlint:keymaps','copy:build']
},
+ tours: {
+ files: [
+ 'packages/node_modules/@node-red/editor-client/src/tours/**/*.js'
+ ],
+ tasks: ['copy:build']
+ },
misc: {
files: [
'CHANGELOG.md'
@@ -423,6 +430,12 @@ module.exports = function(grunt) {
src: '**',
expand: true,
dest: 'packages/node_modules/@node-red/editor-client/public/vendor/ace/'
+ },
+ {
+ cwd: 'packages/node_modules/@node-red/editor-client/src/tours',
+ src: '**',
+ expand: true,
+ dest: 'packages/node_modules/@node-red/editor-client/public/red/tours/'
}
]
}
diff --git a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
index 2a1e16f1d..95c803216 100755
--- a/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
+++ b/packages/node_modules/@node-red/editor-client/locales/en-US/editor.json
@@ -112,6 +112,7 @@
"editPalette":"Manage palette",
"other": "Other",
"showTips": "Show tips",
+ "showWelcomeTours": "Show guided tours for new versions",
"help": "Node-RED website",
"projects": "Projects",
"projects-new": "New",
@@ -1132,6 +1133,10 @@
"preview": "UI Preview",
"defaultValue": "Default value"
},
+ "tourGuide": {
+ "start": "Start",
+ "next": "Next"
+ },
"languages" : {
"de": "German",
"en-US": "English",
diff --git a/packages/node_modules/@node-red/editor-client/src/js/red.js b/packages/node_modules/@node-red/editor-client/src/js/red.js
index ad68ea612..d9ef01021 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/red.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/red.js
@@ -535,19 +535,18 @@ var RED = (function() {
setTimeout(function() {
loader.end();
+ checkFirstRun();
},100);
}
- function showAbout() {
- $.get('red/about', function(data) {
- // data will be strictly markdown. Any HTML should be escaped.
- data = RED.utils.sanitize(data);
- var aboutHeader = '
').appendTo(div);
if (size !== "default") {
div.addClass("red-ui-popover-size-"+size);
}
@@ -67,71 +67,23 @@ RED.popover = (function() {
return;
}
if (typeof result === 'string') {
- div.text(result);
+ contentDiv.text(result);
} else {
- div.append(result);
+ contentDiv.append(result);
}
} else {
- div.html(content);
- }
- if (width !== "auto") {
- div.width(width);
+ contentDiv.html(content);
}
div.appendTo("body");
- var targetPos = target.offset();
- var targetWidth = target.outerWidth();
- var targetHeight = target.outerHeight();
- var divHeight = div.height();
- var divWidth = div.width();
- var paddingRight = 10;
+ movePopup({target,direction,width,maxWidth});
- var viewportTop = $(window).scrollTop();
- var viewportLeft = $(window).scrollLeft();
- var viewportBottom = viewportTop + $(window).height();
- var viewportRight = viewportLeft + $(window).width();
- var top = 0;
- var left = 0;
- var d = direction;
- if (d === 'right') {
- top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top;
- left = targetPos.left+targetWidth+deltaSizes[size].leftRight;
- } else if (d === 'left') {
- top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top;
- left = targetPos.left-deltaSizes[size].leftLeft-divWidth;
- } else if (d === 'bottom') {
- top = targetPos.top+targetHeight+deltaSizes[size].top;
- left = targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftBottom;
- if (left < 0) {
- d = "right";
- top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top;
- left = targetPos.left+targetWidth+deltaSizes[size].leftRight;
- } else if (left+divWidth+paddingRight > viewportRight) {
- d = "left";
- top = targetPos.top+targetHeight/2-divHeight/2-deltaSizes[size].top;
- left = targetPos.left-deltaSizes[size].leftLeft-divWidth;
- if (top+divHeight+targetHeight/2 + 5 > viewportBottom) {
- top -= (top+divHeight+targetHeight/2 - viewportBottom + 5)
- }
- } else if (top+divHeight > viewportBottom) {
- d = 'top';
- top = targetPos.top-deltaSizes[size].topTop-divHeight;
- left = targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftTop;
- }
- } else if (d === 'top') {
- top = targetPos.top-deltaSizes[size].topTop-divHeight;
- left = targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftTop;
- if (top < 0) {
- d = 'bottom';
- top = targetPos.top+targetHeight+deltaSizes[size].top;
- left = targetPos.left+targetWidth/2-divWidth/2 - deltaSizes[size].leftBottom;
- }
- }
- div.addClass('red-ui-popover-'+d).css({top: top, left: left});
if (existingPopover) {
existingPopover.close(true);
}
- target.data("red-ui-popover",res)
+ if (options.trigger !== 'manual') {
+ target.data("red-ui-popover",res)
+ }
if (options.tooltip) {
div.on("mousedown", function(evt) {
closePopup(true);
@@ -161,6 +113,102 @@ RED.popover = (function() {
}
}
}
+ var movePopup = function(options) {
+ target = options.target || target;
+ direction = options.direction || direction || "right";
+ popupOffset = options.offset || popupOffset;
+ var transition = options.transition;
+
+ var width = options.width||"auto";
+ div.width(width);
+ if (options.maxWidth) {
+ div.css("max-width",options.maxWidth)
+ }
+
+ var targetPos = target[0].getBoundingClientRect();
+ var targetHeight = targetPos.height;
+ var targetWidth = targetPos.width;
+
+ var divHeight = div.outerHeight();
+ var divWidth = div.outerWidth();
+ var paddingRight = 10;
+
+ var viewportTop = $(window).scrollTop();
+ var viewportLeft = $(window).scrollLeft();
+ var viewportBottom = viewportTop + $(window).height();
+ var viewportRight = viewportLeft + $(window).width();
+ var top = 0;
+ var left = 0;
+ if (direction === 'right') {
+ top = targetPos.top+targetHeight/2-divHeight/2;
+ left = targetPos.left+targetWidth+deltaSizes[size].x+popupOffset;
+ } else if (direction === 'left') {
+ top = targetPos.top+targetHeight/2-divHeight/2;
+ left = targetPos.left-deltaSizes[size].x-divWidth-popupOffset;
+ } else if (direction === 'bottom') {
+ top = targetPos.top+targetHeight+deltaSizes[size].y+popupOffset;
+ left = targetPos.left+targetWidth/2-divWidth/2;
+ if (left < 0) {
+ direction = "right";
+ top = targetPos.top+targetHeight/2-divHeight/2;
+ left = targetPos.left+targetWidth+deltaSizes[size].x+popupOffset;
+ } else if (left+divWidth+paddingRight > viewportRight) {
+ direction = "left";
+ top = targetPos.top+targetHeight/2-divHeight/2;
+ left = targetPos.left-deltaSizes[size].x-divWidth-popupOffset;
+ if (top+divHeight+targetHeight/2 + 5 > viewportBottom) {
+ top -= (top+divHeight+targetHeight/2 - viewportBottom + 5)
+ }
+ } else if (top+divHeight > viewportBottom) {
+ direction = 'top';
+ top = targetPos.top-deltaSizes[size].y-divHeight-popupOffset;
+ left = targetPos.left+targetWidth/2-divWidth/2;
+ }
+ } else if (direction === 'top') {
+ top = targetPos.top-deltaSizes[size].y-divHeight-popupOffset;
+ left = targetPos.left+targetWidth/2-divWidth/2;
+ if (top < 0) {
+ direction = 'bottom';
+ top = targetPos.top+targetHeight+deltaSizes[size].y+popupOffset;
+ left = targetPos.left+targetWidth/2-divWidth/2;
+ }
+ } else if (/inset/.test(direction)) {
+ top = targetPos.top + targetHeight/2 - divHeight/2;
+ left = targetPos.left + targetWidth/2 - divWidth/2;
+
+ if (/bottom/.test(direction)) {
+ top = targetPos.top + targetHeight - divHeight-popupOffset;
+ }
+ if (/top/.test(direction)) {
+ top = targetPos.top+popupOffset;
+ }
+ if (/left/.test(direction)) {
+ left = targetPos.left+popupOffset;
+ }
+ if (/right/.test(direction)) {
+ left = targetPos.left + targetWidth - divWidth-popupOffset;
+ }
+ }
+ if (currentStyle) {
+ div.removeClass(currentStyle);
+ }
+ if (transition) {
+ div.css({
+ "transition": "0.6s ease",
+ "transition-property": "top,left,right,bottom"
+ })
+ }
+ currentStyle = 'red-ui-popover-'+direction;
+ div.addClass(currentStyle).css({top: top, left: left});
+ if (transition) {
+ setTimeout(function() {
+ div.css({
+ "transition": "none"
+ });
+ },600);
+ }
+
+ }
var closePopup = function(instant) {
$(document).off('mousedown.red-ui-popover');
if (!active) {
@@ -236,8 +284,10 @@ RED.popover = (function() {
},autoClose);
}
var res = {
+ get element() { return div },
setContent: function(_content) {
content = _content;
+
return res;
},
open: function (instant) {
@@ -249,6 +299,10 @@ RED.popover = (function() {
active = false;
closePopup(instant);
return res;
+ },
+ move: function(options) {
+ movePopup(options);
+ return
}
}
return res;
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js
index 27be8db63..b6c11e5f3 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editor.js
@@ -1409,7 +1409,7 @@ RED.editor = (function() {
RED.tray.show(trayOptions);
}
- function showEditSubflowDialog(subflow) {
+ function showEditSubflowDialog(subflow, defaultTab) {
if (buildingEditDialog) { return }
buildingEditDialog = true;
var editing_node = subflow;
@@ -1549,7 +1549,7 @@ RED.editor = (function() {
'editor-tab-appearance'
];
- prepareEditDialog(trayBody, nodeEditPanes, subflow, subflow._def, "node-input", null, function(_activeEditPanes) {
+ prepareEditDialog(trayBody, nodeEditPanes, subflow, subflow._def, "node-input", defaultTab, function(_activeEditPanes) {
activeEditPanes = _activeEditPanes;
$("#subflow-input-name").val(subflow.name);
RED.text.bidi.prepareInput($("#subflow-input-name"));
@@ -1579,7 +1579,7 @@ RED.editor = (function() {
RED.tray.show(trayOptions);
}
- function showEditGroupDialog(group) {
+ function showEditGroupDialog(group, defaultTab) {
if (buildingEditDialog) { return }
buildingEditDialog = true;
var editing_node = group;
@@ -1661,7 +1661,7 @@ RED.editor = (function() {
'editor-tab-envProperties',
'editor-tab-description'
];
- prepareEditDialog(trayBody, nodeEditPanes, group,group._def,"node-input", null, function(_activeEditPanes) {
+ prepareEditDialog(trayBody, nodeEditPanes, group,group._def,"node-input", defaultTab, function(_activeEditPanes) {
activeEditPanes = _activeEditPanes;
trayBody.i18n();
buildingEditDialog = false;
@@ -1692,7 +1692,7 @@ RED.editor = (function() {
RED.tray.show(trayOptions);
}
- function showEditFlowDialog(workspace) {
+ function showEditFlowDialog(workspace, defaultTab) {
if (buildingEditDialog) { return }
buildingEditDialog = true;
var activeEditPanes = [];
@@ -1796,8 +1796,7 @@ RED.editor = (function() {
disabledIcon: "fa-ban",
invertState: true
})
-
- prepareEditDialog(trayBody, nodeEditPanes, workspace, {}, "node-input", null, function(_activeEditPanes) {
+ prepareEditDialog(trayBody, nodeEditPanes, workspace, {}, "node-input", defaultTab, function(_activeEditPanes) {
activeEditPanes = _activeEditPanes;
trayBody.i18n();
trayFooter.i18n();
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js
index 6add6abe9..ed891c114 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tab-help.js
@@ -25,7 +25,6 @@ RED.sidebar.help = (function() {
var tocPanel;
var helpIndex = {};
-
function resizeStack() {
var h = $(content).parent().height() - toolbar.outerHeight();
panels.resize(h)
@@ -93,9 +92,28 @@ RED.sidebar.help = (function() {
$('
'+RED._("sidebar.help.noHelp")+' ').appendTo(helpSection);
treeList = $("
").css({width: "100%"}).appendTo(tocPanel).treeList({data: []})
+ var pendingContentLoad;
treeList.on('treelistselect', function(e,item) {
+ pendingContentLoad = item;
if (item.nodeType) {
- showHelp(item.nodeType);
+ showNodeTypeHelp(item.nodeType);
+ } else if (item.content) {
+ helpSection.empty();
+ if (typeof item.content === "string") {
+ setInfoText(item.label, item.content);
+ } else if (typeof item.content === "function") {
+ if (item.content.length === 0) {
+ setInfoText(item.label, item.content());
+ } else {
+ setInfoText(item.label, '
',helpSection)
+ item.content(function(content) {
+ if (pendingContentLoad === item) {
+ helpSection.empty();
+ setInfoText(item.label, content);
+ }
+ })
+ }
+ }
}
})
@@ -174,21 +192,28 @@ RED.sidebar.help = (function() {
var moduleNames = Object.keys(modules);
moduleNames.sort();
- var helpData = [{
+ var nodeHelp = {
label: RED._("sidebar.help.nodeHelp"),
children: [],
expanded: true
- }]
-
+ }
+ var helpData = [
+ {
+ id: 'changelog',
+ label: "Node-RED v"+RED.settings.version,
+ content: getChangelog
+ },
+ nodeHelp
+ ]
var subflows = RED.nodes.registry.getNodeTypes().filter(function(t) {return /subflow/.test(t)});
if (subflows.length > 0) {
- helpData[0].children.push({
+ nodeHelp.children.push({
label: RED._("menu.label.subflows"),
children: []
})
subflows.forEach(function(nodeType) {
var sf = RED.nodes.getType(nodeType);
- helpData[0].children[0].children.push({
+ nodeHelp.children[0].children.push({
id:"node-type:"+nodeType,
nodeType: nodeType,
subflowLabel: sf.label().toLowerCase(),
@@ -218,7 +243,7 @@ RED.sidebar.help = (function() {
nodeTypes.sort(function(A,B) {
return A.nodeType.localeCompare(B.nodeType)
})
- helpData[0].children.push({
+ nodeHelp.children.push({
id: moduleName,
icon: "fa fa-cube",
label: moduleName,
@@ -244,7 +269,7 @@ RED.sidebar.help = (function() {
return div;
}
- function showHelp(nodeType) {
+ function showNodeTypeHelp(nodeType) {
helpSection.empty();
var helpText;
var title;
@@ -265,7 +290,7 @@ RED.sidebar.help = (function() {
}
}
}
- setInfoText(title, helpText, helpSection);
+ setInfoText(title, helpText);
var ratio = panels.ratio();
if (ratio > 0.7) {
@@ -282,7 +307,7 @@ RED.sidebar.help = (function() {
}
if (type) {
// hideTOC();
- showHelp(type);
+ showNodeTypeHelp(type);
}
resizeStack();
}
@@ -298,11 +323,12 @@ RED.sidebar.help = (function() {
return el;
}
- function setInfoText(title, infoText,target) {
+ function setInfoText(title, infoText) {
+ helpSection.empty();
if (title) {
- $("
",{class:"red-ui-help-title"}).text(title).appendTo(target);
+ $("",{class:"red-ui-help-title"}).text(title).appendTo(helpSection);
}
- var info = addTargetToExternalLinks($(' '+infoText+'
')).appendTo(target);
+ var info = addTargetToExternalLinks($(''+infoText+'
')).appendTo(helpSection);
info.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( " " );
var foldingHeader = "H3";
info.find(foldingHeader).wrapInner(' ')
@@ -316,12 +342,12 @@ RED.sidebar.help = (function() {
}
$(this).toggleClass('expanded',!isExpanded);
})
- target.parent().scrollTop(0);
+ helpSection.parent().scrollTop(0);
}
function set(html,title) {
$(helpSection).empty();
- setInfoText(title,html,helpSection);
+ setInfoText(title,html);
hideTOC();
show();
}
@@ -336,13 +362,83 @@ RED.sidebar.help = (function() {
if (node.type === "subflow" && node.direction) {
// ignore subflow virtual ports
} else if (node.type !== 'group'){
- showHelp(node.type);
+ showNodeTypeHelp(node.type);
}
}
}
}
RED.events.on("view:selection-changed",refreshSelection);
+ function getChangelog(done) {
+ $.get('red/about', function(data) {
+ // data will be strictly markdown. Any HTML should be escaped.
+ data = RED.utils.sanitize(data);
+ RED.tourGuide.load("./tours/welcome.js", function(err, tour) {
+ var tourHeader = '';
+ if (tour) {
+ var currentVersionParts = RED.settings.version.split(".");
+ var tourVersionParts = tour.version.split(".");
+ if (tourVersionParts[0] === currentVersionParts[0] && tourVersionParts[1] === currentVersionParts[1]) {
+ tourHeader = 'Take a tour
'
+ }
+ }
+ var aboutHeader = ''+tourHeader+'
'
+ done(aboutHeader+RED.utils.renderMarkdown(data))
+ });
+
+ });
+ }
+ function showAbout() {
+ treeList.treeList("show","changelog")
+ treeList.treeList("select","changelog");
+ show();
+ }
+ function showWelcomeTour(lastSeenVersion) {
+ RED.tourGuide.load("./tours/welcome.js", function(err, tour) {
+ if (err) {
+ console.warn("Failed to load welcome tour",err);
+ return;
+ }
+ var currentVersionParts = RED.settings.version.split(".");
+ var tourVersionParts = tour.version.split(".");
+
+ // Only display the tour if its MAJ.MIN versions the current version
+ // This means if we update MAJ/MIN without updating the tour, the old tour won't get shown
+ if (tourVersionParts[0] !== currentVersionParts[0] || tourVersionParts[1] !== currentVersionParts[1]) {
+ return;
+ }
+
+ if (lastSeenVersion) {
+ // Previously displayed a welcome tour.
+ if (lastSeenVersion === RED.settings.version) {
+ // Exact match - don't show the tour
+ return;
+ }
+ var lastSeenParts = lastSeenVersion.split(".");
+ if (currentVersionParts[0] < lastSeenParts[0] || (currentVersionParts[0] === lastSeenParts[0] && currentVersionParts[1] < lastSeenParts[1])) {
+ // Running an *older* version than last displayed tour.
+ return;
+ }
+ if (currentVersionParts[0] === lastSeenParts[0] && currentVersionParts[1] === lastSeenParts[1]) {
+ if (lastSeenParts.length === 3 && currentVersionParts.length === 3) {
+ // Matching non-beta MAJ.MIN - don't repeat tour
+ return;
+ }
+ if (currentVersionParts.length === 4 && (lastSeenParts.length === 3 || currentVersionParts[3] < lastSeenParts[3])) {
+ // Running an *older* beta than last displayed tour.
+ return
+ }
+ }
+ }
+ RED.tourGuide.run("./tours/welcome.js", function(err) {
+ RED.settings.set("editor.tours.welcome", RED.settings.version)
+ })
+ })
+
+ }
+ RED.actions.add("core:show-about", showAbout);
+ RED.actions.add("core:show-welcome-tour", showWelcomeTour);
+
return {
init: init,
show: show,
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/tour/tourGuide.js b/packages/node_modules/@node-red/editor-client/src/js/ui/tour/tourGuide.js
new file mode 100644
index 000000000..e6933f01c
--- /dev/null
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/tour/tourGuide.js
@@ -0,0 +1,410 @@
+RED.tourGuide = (function() {
+ var activeListeners = [];
+ var shade;
+ var focus;
+ var popover;
+ var stepContent;
+ var targetElement;
+ var fullscreen;
+
+ var tourCache = {};
+
+ function run(tourPath, done) {
+ done = done || function(err) {
+ if (err) {
+ console.error(err);
+ }
+ };
+ loadTour(tourPath, function(err, tour) {
+ if (err) {
+ console.warn("Error loading tour:",err);
+ return;
+ }
+ runTour(tour, done);
+ })
+
+ }
+
+ function loadTour(tourPath, done) {
+ if (tourCache[tourPath]) {
+ done(null, tourCache[tourPath]);
+ } else {
+ /* jshint ignore:start */
+ // jshint<2.13 doesn't support dynamic imports. Once grunt-contrib-jshint
+ // has been updated with the new jshint, we can stop ignoring this block
+ import(tourPath).then(function(module) {
+ tourCache[tourPath] = module.default;
+ done(null, tourCache[tourPath]);
+ }).catch(function(err) {
+ done(err);
+ })
+ /* jshint ignore:end */
+ }
+ }
+
+ function repositionFocus() {
+ if (targetElement) {
+ var pos = targetElement[0].getBoundingClientRect();
+ var dimension = Math.max(50, Math.max(pos.width,pos.height)*1.5);
+ if (!fullscreen) {
+ focus.css({
+ left: (pos.left+pos.width/2)+"px",
+ top: (pos.top+pos.height/2)+"px",
+ width: (2*dimension)+"px",
+ height: (2*dimension)+"px"
+ })
+ var flush = focus[0].offsetHeight; // Flush CSS changes
+ focus.addClass("transition");
+ focus.css({
+ width: dimension+"px",
+ height: dimension+"px"
+ })
+ } else {
+ focus.css({
+ left: ($(window).width()/2)+"px",
+ top: ($(window).height()/2)+"px",
+ width: "0px",
+ height: "0px"
+ })
+ }
+ if (popover) {
+ popover.move({
+ target: targetElement,
+ })
+ }
+ }
+ }
+ function runTour(tour, done) {
+
+ shade = $('
').appendTo(document.body);
+ focus = $('
').appendTo(shade);
+
+ // var resizeTimer;
+ //
+ $(window).on("resize.red-ui-tourGuide", function() {
+ repositionFocus();
+ })
+
+
+
+ var i = 0;
+ var state = {
+ index: 0,
+ count: tour.steps.length
+ };
+
+ function endTour(err) {
+ $(window).off("resize.red-ui-tourGuide");
+ $(document).off('keydown.red-ui-tourGuide');
+ if (popover) {
+ popover.close();
+ }
+ stepContent = null;
+ popover = null;
+ shade.remove();
+ shade = null;
+ done(err);
+ }
+ function runStep(carryOn) {
+ if (carryOn === false) {
+ endTour(false);
+ return;
+ }
+ if (i === tour.steps.length) {
+ endTour();
+ return
+ }
+ state.index = i;
+ // console.log("TOUR STEP",i+1,"OF",tour.steps.length)
+ try {
+ runTourStep(tour.steps[i++], state, runStep)
+ } catch(err) {
+ endTour(err);
+ return;
+ }
+ }
+ runStep();
+ }
+
+ function clearListeners() {
+ activeListeners.forEach(function(listener) {
+ if (listener.type === "dom-event") {
+ listener.target[0].removeEventListener(listener.event,listener.listener,listener.opts);
+ } else if (listener.type === "nr-event") {
+ RED.events.off(listener.event, listener.listener)
+ }
+ })
+ activeListeners = [];
+ }
+
+ function prepareStep(step, state, done) {
+ if (step.prepare) {
+ if (step.prepare.length === 0) {
+ step.prepare.call(state);
+ } else {
+ step.prepare.call(state, done)
+ return;
+ }
+ }
+ done();
+ }
+ function completeStep(step, state, done) {
+ function finish() {
+ clearListeners();
+ setTimeout(function() {
+ done();
+ },0)
+ }
+ if (step.complete) {
+ if (step.complete.length === 0) {
+ step.complete.call(state);
+ } else {
+ step.complete.call(state, finish)
+ return;
+ }
+ }
+ finish();
+
+ }
+ function getLocaleText(property) {
+ if (typeof property === 'string') {
+ return property;
+ }
+ var currentLang = RED.i18n.lang() || 'en-US';
+ var availableLangs = Object.keys(property);
+ return property[currentLang]||property['en-US']||property[availableLangs[0]]
+
+ }
+ function runTourStep(step, state, done) {
+ shade.fadeIn();
+ prepareStep(step, state, function() {
+ var zIndex;
+ var direction = step.direction || "bottom";
+ fullscreen = false;
+
+ if (typeof step.element === "string") {
+ targetElement = $(step.element)
+ } else if (typeof step.element === "function") {
+ targetElement = step.element.call(state);
+ } else if (!step.element) {
+ targetElement = $(".red-ui-editor")
+ fullscreen = true;
+ direction = "inset";
+ } else {
+ targetElement = step.element;
+ }
+
+ if (targetElement.length === 0) {
+ targetElement = null;
+ shade.hide();
+ throw new Error("Element not found")
+ }
+ if ($(window).width() < 400) {
+ targetElement = $(".red-ui-editor");
+ fullscreen = true;
+ direction = "inset";
+ }
+
+ zIndex = targetElement.css("z-index");
+ if (!fullscreen) {
+ targetElement.css("z-index",2002);
+ }
+ repositionFocus();
+ focus.toggleClass("disableInteraction", step.interactive === false)
+
+ if (!stepContent) {
+ stepContent = $('
');
+ } else {
+ stepContent.empty();
+ }
+ $(' ').appendTo(stepContent).click(function(evt) {
+ evt.preventDefault();
+ completeStep(step, state, function() {
+ done(false);
+ });
+ })
+
+ var stepDescription = $('
').appendTo(stepContent);
+ if (step.titleIcon) {
+ $(' ').appendTo(stepDescription);
+ }
+ if (step.title) {
+ $('').text(getLocaleText(step.title)).appendTo(stepDescription);
+ }
+ $('').css("text-align","left").html(getLocaleText(step.description)).appendTo(stepDescription);
+
+ var stepToolbar = $('
',{class:"red-ui-tourGuide-toolbar"}).appendTo(stepContent);
+
+ // var breadcrumbs = $('
',{class:"red-ui-tourGuide-breadcrumbs"}).appendTo(stepToolbar);
+ // var bcStart = Math.max(0,state.index - 3);
+ // var bcEnd = Math.min(state.count, bcStart + 7);
+ // if (bcEnd === state.count) {
+ // bcStart = Math.max(0,bcEnd - 7);
+ // }
+ // for (var i = bcStart; i < bcEnd; i++) {
+ // var bullet = $('
').addClass(i===state.index ? "fa-circle":"fa-circle-o").appendTo(breadcrumbs);
+ // if (i === bcStart) {
+ // if (i > 1) {
+ // bullet.css("font-size", "3px");
+ // } else if (i === 1) {
+ // bullet.css("font-size", "4px");
+ // }
+ // } else if (i === bcStart + 1) {
+ // if (i > 2) {
+ // bullet.css("font-size", "4px");
+ // }
+ // }
+ // if (i === bcEnd - 1) {
+ // if (i < state.count - 2) {
+ // bullet.css("font-size", "3px");
+ // } else if (i === state.count - 2) {
+ // bullet.css("font-size", "4px");
+ // }
+ // } else if (i === bcEnd - 2) {
+ // if (i < state.count - 3) {
+ // bullet.css("font-size", "4px");
+ // }
+ // }
+ // // if (i === bcEnd - 1) {
+ // // if (i < state.count - 2) {
+ // // bullet.css("font-size", "3px");
+ // // } else if (i === state.count - 2) {
+ // // bullet.css("font-size", "4px");
+ // // }
+ // // }
+ // }
+
+ $('
').text((state.index+1)+"/"+state.count).appendTo(stepToolbar)
+ var nextButton;
+ if (fullscreen || !step.wait) {
+ nextButton = $(' ').appendTo(stepToolbar).one('click',function(evt) {
+ evt.preventDefault();
+ stepEventListener();
+ });
+ if (state.index === state.count - 1) {
+ $(' ').text(RED._("common.label.close")).appendTo(nextButton);
+ } else if (state.index === 0) {
+ $('start ').text(RED._("tourGuide.start")).appendTo(nextButton);
+ } else if (state.index < state.count-1) {
+ $(' ').text(RED._("tourGuide.next")).appendTo(nextButton);
+ $(' ').appendTo(nextButton);
+ }
+ }
+
+ var width = step.width;
+ if (fullscreen) {
+ width = 500;
+ }
+ var maxWidth = Math.min($(window).width()-10,Math.max(width || 0, 300));
+ if (!popover) {
+ popover = RED.popover.create({
+ target: targetElement,
+ width: width || "auto",
+ maxWidth: maxWidth+"px",
+ direction: direction,
+ class: "red-ui-tourGuide-popover"+(fullscreen?" ":""),
+ trigger: "manual",
+ content: stepContent
+ }).open();
+ }
+ $(document).off('keydown.red-ui-tourGuide');
+ $(document).on('keydown.red-ui-tourGuide', function(evt) {
+ if (evt.key === "Escape" || evt.key === "Esc") {
+ evt.preventDefault();
+ evt.stopPropagation();
+ completeStep(step, state, function() {
+ done(false);
+ });
+ }
+ })
+ popover.element.toggleClass("red-ui-tourGuide-popover-full",!!fullscreen);
+ popover.move({
+ target: targetElement,
+ width: width || "auto",
+ maxWidth: maxWidth+"px",
+ direction: direction,
+ })
+
+ if (nextButton) {
+ setTimeout(function() {
+ nextButton.focus();
+ },50);
+ }
+
+ var isSVG = targetElement[0] instanceof SVGElement;
+ if (step.fallback) {
+ focus.one("mouseenter", function(evt) {
+ setTimeout(function() {
+ focus.css({
+ width: (4*dimension)+"px",
+ height: (4*dimension)+"px"
+ })
+ shade.fadeOut();
+ popover.move({
+ target: $(".red-ui-editor"),
+ direction: step.fallback,
+ offset: 10,
+ transition: true
+ })
+ // popover.element.addClass('red-ui-tourGuide-popover-bounce');
+ },isSVG?0:500);
+ })
+ }
+
+ var stepEventListener = function() {
+ focus.removeClass("transition");
+ targetElement.css("z-index",zIndex);
+ completeStep(step, state, done);
+ }
+
+ if (step.wait) {
+ if (step.wait.type === "dom-event") {
+ var eventTarget = targetElement;
+ if (step.wait.element) {
+ if (typeof step.wait.element === "string") {
+ eventTarget = $(step.wait.element);
+ } else if (typeof step.wait.element === "function") {
+ eventTarget = step.wait.element.call(state);
+ }
+ }
+ var listener = {
+ type: step.wait.type,
+ target: eventTarget,
+ event: step.wait.event,
+ listener: function() {
+ stepEventListener();
+ },
+ opts: { once: true }
+ }
+ activeListeners.push(listener)
+ eventTarget[0].addEventListener(listener.event,listener.listener,listener.opts)
+ } else if (step.wait.type === "nr-event") {
+ var listener = {
+ type: step.wait.type,
+ event: step.wait.event,
+ listener: function() {
+ if (step.wait.filter) {
+ if (!step.wait.filter.apply(state,arguments)) {
+ return;
+ }
+ }
+ stepEventListener();
+ }
+ }
+ activeListeners.push(listener);
+ RED.events.on(listener.event,listener.listener);
+ }
+ }
+ })
+ }
+
+ return {
+ load: loadTour,
+ run: run,
+ reset: function() {
+ RED.settings.set("editor.tours.welcome",'');
+ }
+ }
+
+
+})();
diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js b/packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js
index 2bdae4b13..3c1e295d2 100644
--- a/packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js
+++ b/packages/node_modules/@node-red/editor-client/src/js/ui/userSettings.js
@@ -139,7 +139,8 @@ RED.userSettings = (function() {
{
title: "menu.label.other",
options: [
- {setting:"view-show-tips",oldSettings:"menu-menu-item-show-tips",label:"menu.label.showTips",toggle:true,default:true,onchange:"core:toggle-show-tips"}
+ {setting:"view-show-tips",oldSettings:"menu-menu-item-show-tips",label:"menu.label.showTips",toggle:true,default:true,onchange:"core:toggle-show-tips"},
+ {setting:"view-show-welcome-tours",label:"menu.label.showWelcomeTours",toggle:true,default:true}
]
}
];
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/colors.scss b/packages/node_modules/@node-red/editor-client/src/sass/colors.scss
index 4b7301542..eb0582ba1 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/colors.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/colors.scss
@@ -140,8 +140,8 @@ $workspace-button-color-focus-outline: $form-input-focus-color;
$shade-color: rgba(160,160,160,0.5);
-
$popover-background: #333;
+$popover-border: $popover-background;
$popover-color: #eee;
$popover-button-border-color: #bbb;
$popover-button-border-color-hover: #666;
@@ -295,6 +295,10 @@ $group-default-stroke: #999;
$group-default-stroke-opacity: 1;
$group-default-label-color: #a4a4a4;
+$tourGuide-shade: $shade-color;
+$tourGuide-border: #a22222;
+$tourGuide-heading-color: #a22222;
+
// Deprecated
$text-color-green: $text-color-success;
$info-text-code-color: $text-color-code;
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/dropdownMenu.scss b/packages/node_modules/@node-red/editor-client/src/sass/dropdownMenu.scss
index e2ef1bc91..98ab3bd3b 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/dropdownMenu.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/dropdownMenu.scss
@@ -132,6 +132,7 @@
.red-ui-menu-dropdown > li > a:hover,
+.red-ui-menu-dropdown > li.open > a,
.red-ui-menu-dropdown > li > a:focus,
.red-ui-menu-dropdown-submenu:hover > a,
.red-ui-menu-dropdown-submenu:focus > a {
@@ -148,6 +149,7 @@
margin-top: -6px;
margin-left: -1px;
}
+ &.open > .red-ui-menu-dropdown,
&:hover > .red-ui-menu-dropdown {
display: block;
}
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/header.scss b/packages/node_modules/@node-red/editor-client/src/sass/header.scss
index e638506b4..bd1e78a81 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/header.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/header.scss
@@ -224,6 +224,7 @@
}
}
> li > a:hover,
+ > li.open > a,
> li > a:focus,
> li:hover > a,
> li:focus > a {
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/popover.scss b/packages/node_modules/@node-red/editor-client/src/sass/popover.scss
index 872f32024..e6def9efc 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/popover.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/popover.scss
@@ -19,19 +19,23 @@
display: none;
position: absolute;
width: auto;
- padding: 10px;
+ padding: 2px;
height: auto;
- background: $popover-background;
- color: $popover-color;
+ background: var(--red-ui-popover-border);
+ color: var(--red-ui-popover-color);
border-radius: 4px;
z-index: 1000;
font-family: $primary-font;
font-size: 14px;
line-height: 1.4em;
@include component-shadow;
- border-color: $popover-background;
+ border-color: var(--red-ui-popover-border);
+}
+.red-ui-popover-content {
+ padding: 8px;
+ border-radius: 2px;
+ background: var(--red-ui-popover-background);
}
-
.red-ui-popover:after, .red-ui-popover:before {
border: solid transparent;
content: " ";
@@ -61,26 +65,26 @@
.red-ui-popover.red-ui-popover-right:after {
border-color: transparent;
- border-right-color: $popover-background;
+ border-right-color: var(--red-ui-popover-border);
border-width: 10px;
margin-top: -10px;
}
.red-ui-popover.red-ui-popover-right:before {
border-color: transparent;
- border-right-color: $popover-background;
+ border-right-color: var(--red-ui-popover-border);
border-width: 11px;
margin-top: -11px;
}
.red-ui-popover.red-ui-popover-left:after {
border-color: transparent;
- border-left-color: $popover-background;
+ border-left-color: var(--red-ui-popover-border);
border-width: 10px;
margin-top: -10px;
}
.red-ui-popover.red-ui-popover-left:before {
border-color: transparent;
- border-left-color: $popover-background;
+ border-left-color: var(--red-ui-popover-border);
border-width: 11px;
margin-top: -11px;
}
@@ -88,26 +92,26 @@
.red-ui-popover.red-ui-popover-bottom:after {
border-color: transparent;
- border-bottom-color: $popover-background;
+ border-bottom-color: var(--red-ui-popover-border);
border-width: 10px;
margin-left: -10px;
}
.red-ui-popover.red-ui-popover-bottom:before {
border-color: transparent;
- border-bottom-color: $popover-background;
+ border-bottom-color: var(--red-ui-popover-border);
border-width: 11px;
margin-left: -11px;
}
.red-ui-popover.red-ui-popover-top:after {
border-color: transparent;
- border-top-color: $popover-background;
+ border-top-color: var(--red-ui-popover-border);
border-width: 10px;
margin-left: -10px;
}
.red-ui-popover.red-ui-popover-top:before {
border-color: transparent;
- border-top-color: $popover-background;
+ border-top-color: var(--red-ui-popover-border);
border-width: 11px;
margin-left: -11px;
}
@@ -116,9 +120,10 @@
.red-ui-popover-size-small {
font-size: 12px;
- padding: 5px 7px;
line-height: 1.8em;
-
+ .red-ui-popover-content {
+ padding: 1px 4px;
+ }
&.red-ui-popover-right:after, &.red-ui-popover-left:after {
border-width: 7px;
margin-top: -7px;
@@ -143,7 +148,7 @@
font-size: 11px;
font-family: $monospace-font;
margin-left: 3px;
- border: 1px solid $popover-color;
+ border: 1px solid var(--red-ui-popover-color);
border-radius:3px;
padding: 1px 2px;
}
@@ -152,8 +157,8 @@
.red-ui-popover button.red-ui-button {
&:not(.primary) {
border-color: $popover-button-border-color;
- background: $popover-background;
- color: $popover-color !important;
+ background: var(--red-ui-popover-background);
+ color: var(--red-ui-popover-color) !important;
}
&:not(.primary):not(.disabled):not(.ui-button-disabled):hover {
border-color: $popover-button-border-color-hover;
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/style.scss b/packages/node_modules/@node-red/editor-client/src/sass/style.scss
index 8901d1305..084cda954 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/style.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/style.scss
@@ -69,3 +69,5 @@
@import "debug";
@import "radialMenu";
+
+@import "tourGuide";
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/tourGuide.scss b/packages/node_modules/@node-red/editor-client/src/sass/tourGuide.scss
new file mode 100644
index 000000000..53c920222
--- /dev/null
+++ b/packages/node_modules/@node-red/editor-client/src/sass/tourGuide.scss
@@ -0,0 +1,122 @@
+.red-ui-tourGuide-shade {
+ position: absolute;
+ top:0;
+ left:0;
+ bottom:0;
+ right:0;
+ z-index: 2000;
+ overflow: hidden;
+}
+
+.red-ui-tourGuide-shade-focus {
+ display: block;
+ width: 100px;
+ height: 100px;
+ position: absolute;
+ z-index: 2001;
+ transform: translate(-50%, -50%);
+ border-radius: 50%;
+ border: 2px solid $tourGuide-border;
+
+ &.transition {
+ transition: 0.4s ease;
+ transition-property: width,height;
+ }
+
+ &.disableInteraction {
+ pointer-events: none;
+ }
+
+ &::before {
+ content: '';
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ border-radius: 50%;
+ border: solid 6000px $tourGuide-shade;
+ margin-left: -6000px;
+ margin-top: -6000px;
+ pointer-events: none;
+ }
+}
+.red-ui-popover.red-ui-tourGuide-popover {
+ z-index: 2003;
+ --red-ui-popover-background: #{$secondary-background};
+ --red-ui-popover-border: #{$tourGuide-border};
+ --red-ui-popover-color: #{$primary-text-color};
+
+ .red-ui-popover-content {
+ h2 {
+ text-align: center;
+ margin-top: 0px;
+ color: #a22222;
+ i.fa {
+ font-size: 1.5em
+ }
+ }
+ }
+
+}
+
+.red-ui-tourGuide-toolbar {
+ min-height: 36px;
+ position: relative;
+ display: flex;
+ align-items: flex-end;
+}
+.red-ui-tourGuide-breadcrumbs {
+ flex-grow: 1;
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 6px;
+ & > div {
+ display: inline-block;
+ }
+ i {
+ line-height: 16px;
+ margin: 0 3px;
+ }
+}
+.red-ui-tourGuide-popover-description {
+ padding: 10px 20px 5px;
+}
+.red-ui-tourGuide-popover-full {
+ .red-ui-tourGuide-popover-description {
+ padding: 20px 40px 10px;
+ text-align: center;
+ }
+}
+.red-ui-popover.red-ui-tourGuide-popover button.red-ui-button {
+ &:not(.primary) {
+ border-color: transparent;
+ background: $secondary-background;
+ color: $primary-text-color !important;
+ }
+ &:not(.primary):not(.disabled):not(.ui-button-disabled):hover {
+ border-color: $popover-button-border-color-hover;
+ }
+}
+
+
+// .red-ui-tourGuide-popover-bounce {
+// animation: 10s ease-in 5s infinite both red-ui-tourGuide-popover-bounce;
+// }
+// // @keyframes *must* be on multiple lines so build-custom-theme can filter them out
+// @keyframes red-ui-tourGuide-popover-bounce {
+// 0%,
+// 10%,
+// 100% {
+// -webkit-transform: translateY(0);
+// transform: translateY(0);
+// }
+// 2%,8% {
+// -webkit-transform: translateY(-5px);
+// transform: translateY(-5px);
+// }
+// 5% {
+// -webkit-transform: translateY(5px);
+// transform: translateY(5px);
+// }
+// }
diff --git a/packages/node_modules/@node-red/editor-client/src/sass/variables.scss b/packages/node_modules/@node-red/editor-client/src/sass/variables.scss
index 72b573a2e..1ddf08411 100644
--- a/packages/node_modules/@node-red/editor-client/src/sass/variables.scss
+++ b/packages/node_modules/@node-red/editor-client/src/sass/variables.scss
@@ -81,12 +81,15 @@
--red-ui-node-status-changed-border: #{$node-status-changed-border};
--red-ui-node-status-changed-background: #{$node-status-changed-background};
-
-
--red-ui-node-border: #{$node-border};
--red-ui-node-port-background:#{$node-port-background};
--red-ui-node-label-color: #{$node-label-color};
--red-ui-node-selected-color: #{$node-selected-color};
--red-ui-port-selected-color: #{$port-selected-color};
+
+ --red-ui-popover-background: #{$popover-background};
+ --red-ui-popover-border: #{$popover-border};
+ --red-ui-popover-color: #{$popover-color};
+
}
diff --git a/packages/node_modules/@node-red/editor-client/src/tours/first-flow.js b/packages/node_modules/@node-red/editor-client/src/tours/first-flow.js
new file mode 100644
index 000000000..e7eed1d0f
--- /dev/null
+++ b/packages/node_modules/@node-red/editor-client/src/tours/first-flow.js
@@ -0,0 +1,80 @@
+export default {
+ steps: [
+ {
+ title: "Create your first flow",
+ width: 400,
+ description: 'This tutorial will guide you through creating your first flow',
+ nextButton: 'start'
+ },
+ {
+ element: "#red-ui-workspace .red-ui-tab-button.red-ui-tabs-add",
+ description: 'To add a new tab, click the button',
+ wait: {
+ type: "dom-event",
+ event: "click",
+ element: "#red-ui-workspace .red-ui-tab-button.red-ui-tabs-add a"
+ },
+ },
+ {
+ element: '.red-ui-palette-node[data-palette-type="inject"]',
+ direction: 'right',
+ description: 'The palette lists all of the nodes available to use. Drag a new Inject node into the workspace.',
+ fallback: 'inset-bottom-right',
+ wait: {
+ type: "nr-event",
+ event: "nodes:add",
+ filter: function(event) {
+ if (event.type === "inject") {
+ this.injectNode = event;
+ return true;
+ }
+ return false
+ }
+ },
+ complete: function() {
+ $('.red-ui-palette-node[data-palette-type="inject"]').css("z-index","auto");
+ }
+ },
+ {
+ element: '.red-ui-palette-node[data-palette-type="debug"]',
+ direction: 'right',
+ description: 'Next, drag a new Debug node into the workspace.',
+ fallback: 'inset-bottom-right',
+ wait: {
+ type: "nr-event",
+ event: "nodes:add",
+ filter: function(event) {
+ if (event.type === "debug") {
+ this.debugNode = event;
+ return true;
+ }
+ return false
+ }
+ },
+ complete: function() {
+ $('.red-ui-palette-node[data-palette-type="debug"]').css("z-index","auto");
+ },
+ },
+ {
+ element: function() { return $("#"+this.injectNode.id+" .red-ui-flow-port") },
+ description: 'Add a wire from the output of the Inject node to the input of the Debug node',
+ fallback: 'inset-bottom-right',
+ wait: {
+ type: "nr-event",
+ event: "links:add",
+ filter: function(event) {
+ return event.source.id === this.injectNode.id && event.target.id === this.debugNode.id;
+ }
+ },
+ },
+ {
+ element: "#red-ui-header-button-deploy",
+ description: 'Deploy your changes so the flow is active in the runtime',
+ width: 200,
+ wait: {
+ type: "dom-event",
+ event: "click"
+ },
+ }
+ ]
+}
diff --git a/packages/node_modules/@node-red/editor-client/src/tours/welcome.js b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js
new file mode 100644
index 000000000..f425a3f1e
--- /dev/null
+++ b/packages/node_modules/@node-red/editor-client/src/tours/welcome.js
@@ -0,0 +1,70 @@
+export default {
+ version: "2.1.0",
+ steps: [
+ {
+ titleIcon: "fa fa-map-o",
+ title: { "en-US": "Welcome to Node-RED 2.1!" },
+ description: { "en-US": "Let's take a moment to discover the new features in this release." }
+ },
+ {
+ title: { "en-US": "A new Tour Guide" },
+ description: { "en-US": " First, as you've already found, we now have this tour of new features. We'll only show the tour the first time you open the editor for each new version of Node-RED.
"+
+ "
You can choose not to see this tour in the future by disabling it under the View tab of User Settings.
" }
+ },
+ {
+ prepare() {
+ $("#red-ui-header-button-sidemenu").trigger("click");
+ $("#menu-item-edit-menu").parent().addClass("open")
+ },
+ complete() {
+ $("#menu-item-edit-menu").parent().removeClass("open")
+ },
+ element: "#menu-item-edit-menu-submenu",
+ interactive: false,
+ direction: "left",
+ title: { "en-US": "New Edit menu" },
+ description: { "en-US": "
The main menu has been updated with a new 'Edit' section. This includes all of the familar options, like cut/paste and undo/redo.
"+
+ "
The menu now displays keyboard shortcuts for the options.
" }
+
+ },
+ {
+ prepare() {
+ $("#red-ui-header-button-sidemenu").trigger("click");
+ $("#menu-item-arrange-menu").parent().addClass("open")
+ },
+ complete() {
+ $("#menu-item-arrange-menu").parent().removeClass("open")
+ },
+ element: "#menu-item-arrange-menu-submenu",
+ interactive: false,
+ direction: "left",
+ title: { "en-US": "Arranging nodes" },
+ description: { "en-US": "
The new 'Arrange' section of the menu provides new options to help arrange your nodes. You can align them to a common edge, spread them out evenly or change their order.
" },
+ },
+ {
+ element: "#red-ui-workspace-tabs > li:first-child",
+ title: { "en-US": "Flow and Group level environment variables" },
+ description: { "en-US": "
Flows and Groups can now have their own environment variables that can be referenced by nodes inside them.
" },
+ },
+ {
+ prepare(done) {
+ RED.editor.editFlow(RED.nodes.workspace(RED.workspaces.active()),"editor-tab-envProperties");
+ setTimeout(done,800);
+ },
+ element: "#red-ui-tab-editor-tab-envProperties-link-button",
+ description: { "en-US": "
Flows and Groups now have an Environment Variables section in their edit dialog.
" },
+ },
+ {
+ element: ".node-input-env-container-row .red-ui-editableList-addButton",
+ direction: "top",
+ description: { "en-US": '
The environment variables are listed in this table and new ones can be added by clicking the button.
' },
+ complete() {
+ $("#node-dialog-cancel").trigger("click");
+ }
+ },
+ {
+ title: { "en-US": "And that's not all..." },
+ description: { "en-US": "
There's more still to come before 2.1.0 is released. Watch this space!
" }
+ },
+ ]
+}