diff --git a/CHANGELOG.md b/CHANGELOG.md index d09c2acd7..4e77657c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,29 @@ +#### 4.0.9: Maintenance Release + + Editor + + - Add details for the dynamic subscription to match the English docs (#5050) @aikitori + - Fix tooltip snapping based on `typedInput` type (#5051) @GogoVega + - Prevent symbol usage warning in monaco (#5049) @Steve-Mcl + - Show subflow flow context under node section of sidebar (#5025) @knolleary + - feat: Add custom label for default deploy button in settings.editorTheme (#5030) @matiseni51 + - Handle long auto-complete suggests (#5042) @knolleary + - Handle undefined username when generating user icon (#5043) @knolleary + - Handle dragging node into group and splicing link at same time (#5027) @knolleary + - Remember context sidebar tree state when refreshing (#5021) @knolleary + - Update sf instance env vars when removed from template (#5023) @knolleary + - Do not select group when triggering quick-add within it (#5022) @knolleary + - Fix library icon handling within library browser component (#5017) @knolleary + +Runtime + - Allow env var access to context (#5016) @knolleary + - fix debug status reporting if null (#5018) @dceejay + - Fix grunt dev via better ndoemon ignore rules (#5015) @knolleary + - Fix typo in CHANGELOG (4.0.7-->4.0.8) (#5007) @natcl + +Nodes + - Switch: Avoid exceeding call stack when draining message group in Switch (#5014) @knolleary + #### 4.0.8: Maintenance Release Editor diff --git a/package.json b/package.json index 185c73fe4..d2fb401b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-red", - "version": "4.0.8", + "version": "4.0.9", "description": "Low-code programming for event-driven applications", "homepage": "https://nodered.org", "license": "Apache-2.0", diff --git a/packages/node_modules/@node-red/editor-api/package.json b/packages/node_modules/@node-red/editor-api/package.json index f7a14b766..62298a003 100644 --- a/packages/node_modules/@node-red/editor-api/package.json +++ b/packages/node_modules/@node-red/editor-api/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-api", - "version": "4.0.8", + "version": "4.0.9", "license": "Apache-2.0", "main": "./lib/index.js", "repository": { @@ -16,8 +16,8 @@ } ], "dependencies": { - "@node-red/util": "4.0.8", - "@node-red/editor-client": "4.0.8", + "@node-red/util": "4.0.9", + "@node-red/editor-client": "4.0.9", "bcryptjs": "2.4.3", "body-parser": "1.20.3", "clone": "2.1.2", diff --git a/packages/node_modules/@node-red/editor-client/package.json b/packages/node_modules/@node-red/editor-client/package.json index 60e9bb5e4..ef2600a54 100644 --- a/packages/node_modules/@node-red/editor-client/package.json +++ b/packages/node_modules/@node-red/editor-client/package.json @@ -1,6 +1,6 @@ { "name": "@node-red/editor-client", - "version": "4.0.8", + "version": "4.0.9", "license": "Apache-2.0", "repository": { "type": "git", diff --git a/packages/node_modules/@node-red/editor-client/src/js/multiplayer.js b/packages/node_modules/@node-red/editor-client/src/js/multiplayer.js index b37d90fcb..fed5f518a 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/multiplayer.js +++ b/packages/node_modules/@node-red/editor-client/src/js/multiplayer.js @@ -398,14 +398,13 @@ RED.multiplayer = (function () { anonIconBody.setAttribute("d",`M ${radius/2} ${radius/2 + 5} h -2.5 c -2 1 -2 -5 0.5 -4.5 c 2 1 2 1 4 0 c 2.5 -0.5 2.5 5.5 0 4.5 z`); group.appendChild(anonIconBody) } else { - const labelText = user.username ? user.username.substring(0,2) : user const label = document.createElementNS("http://www.w3.org/2000/svg","text"); - if (user.username) { + if (user.username || user.email) { label.setAttribute("class","red-ui-multiplayer-annotation-label"); - label.textContent = user.username.substring(0,2) + label.textContent = (user.username || user.email).substring(0,2) } else { label.setAttribute("class","red-ui-multiplayer-annotation-label red-ui-multiplayer-user-count") - label.textContent = user + label.textContent = 'nr' } label.setAttribute("text-anchor", "middle") label.setAttribute("x",radius/2); diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js index a3ce0bcd1..a4de3f2a9 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/autoComplete.js @@ -61,7 +61,7 @@ } this.menu = RED.popover.menu({ tabSelect: true, - width: 300, + width: Math.max(300, this.element.width()), maxHeight: 200, class: "red-ui-autoComplete-container", options: completions, diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js index 47355565f..525cc3d1c 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/common/typedInput.js @@ -63,6 +63,7 @@ pre: value.substring(0,idx), match: value.substring(idx,idx+len), post: value.substring(idx+len), + exact: idx === 0 && value.length === searchValue.length } } function generateSpans(match) { @@ -83,7 +84,7 @@ const srcMatch = getMatch(optSrc, val); if (valMatch.found || srcMatch.found) { const element = $('
',{style: "display: flex"}); - const valEl = $('
',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"}); + const valEl = $('
',{ class: "red-ui-autoComplete-completion" }); valEl.append(generateSpans(valMatch)); valEl.appendTo(element); if (optSrc) { @@ -159,7 +160,7 @@ if (valMatch.found) { const optSrc = envVarsMap[v] const element = $('
',{style: "display: flex"}); - const valEl = $('
',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"}); + const valEl = $('
',{ class: "red-ui-autoComplete-completion" }); valEl.append(generateSpans(valMatch)) valEl.appendTo(element) @@ -201,7 +202,7 @@ const that = this const getContextKeysFromRuntime = function(scope, store, searchKey, done) { contextKnownKeys[scope] = contextKnownKeys[scope] || {} - contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Set() + contextKnownKeys[scope][store] = contextKnownKeys[scope][store] || new Map() if (searchKey.length > 0) { try { RED.utils.normalisePropertyExpression(searchKey) @@ -223,11 +224,12 @@ const result = data[store] || {} const keys = result.keys || [] const keyPrefix = searchKey + (searchKey.length > 0 ? '.' : '') - keys.forEach(key => { + keys.forEach(keyInfo => { + const key = keyInfo.key if (/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(key)) { - contextKnownKeys[scope][store].add(keyPrefix + key) + contextKnownKeys[scope][store].set(keyPrefix + key, keyInfo) } else { - contextKnownKeys[scope][store].add(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]") + contextKnownKeys[scope][store].set(searchKey + "[\""+key.replace(/"/,"\\\"")+"\"]", keyInfo) } }) done() @@ -242,14 +244,14 @@ // Get the flow id of the node we're editing const editStack = RED.editor.getEditStack() if (editStack.length === 0) { - done([]) + done(new Map()) return } const editingNode = editStack.pop() if (editingNode.z) { scope = `${scope}/${editingNode.z}` } else { - done([]) + done(new Map()) return } } @@ -269,17 +271,29 @@ return function(val, done) { getContextKeys(val, function (keys) { const matches = [] - keys.forEach(v => { + keys.forEach((keyInfo, v) => { let optVal = v let valMatch = getMatch(optVal, val); - if (!valMatch.found && val.length > 0 && val.endsWith('.')) { - // Search key ends in '.' - but doesn't match. Check again - // with [" at the end instead so we match bracket notation - valMatch = getMatch(optVal, val.substring(0, val.length - 1) + '["') + if (!valMatch.found && val.length > 0) { + if (val.endsWith('.')) { + // Search key ends in '.' - but doesn't match. Check again + // with [" at the end instead so we match bracket notation + valMatch = getMatch(optVal, val.substring(0, val.length - 1) + '["') + // } else if (val.endsWith('[') && /^array/.test(keyInfo.format)) { + // console.log('this case') + } } if (valMatch.found) { const element = $('
',{style: "display: flex"}); - const valEl = $('
',{style:"font-family: var(--red-ui-monospace-font); white-space:nowrap; overflow: hidden; flex-grow:1"}); + const valEl = $('
',{ class: "red-ui-autoComplete-completion" }); + // if (keyInfo.format) { + // valMatch.post += ' ' + keyInfo.format + // } + if (valMatch.exact && /^array/.test(keyInfo.format)) { + valMatch.post += `[0-${keyInfo.length}]` + optVal += '[' + + } valEl.append(generateSpans(valMatch)) valEl.appendTo(element) matches.push({ @@ -1567,7 +1581,8 @@ if (tooltip) { tooltip.setContent(valid); } else { - tooltip = RED.popover.tooltip(this.elementDiv, valid); + const target = this.typeMap[type]?.options ? this.optionSelectLabel : this.elementDiv; + tooltip = RED.popover.tooltip(target, valid); this.element.data("tooltip", tooltip); } } diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js index ed503bcfe..d77a4dba2 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/deploy.js @@ -44,6 +44,7 @@ RED.deploy = (function() { /** * options: * type: "default" - Button with drop-down options - no further customisation available + * label: the text to display - default: "Deploy" * type: "simple" - Button without dropdown. Customisations: * label: the text to display - default: "Deploy" * icon : the icon to use. Null removes the icon. default: "red/images/deploy-full-o.svg" @@ -51,13 +52,14 @@ RED.deploy = (function() { function init(options) { options = options || {}; var type = options.type || "default"; + var label = options.label || RED._("deploy.deploy"); if (type == "default") { $('
  • '+ ''+ ''+ ' '+ - ''+RED._("deploy.deploy")+''+ + ''+label+''+ ''+ ''+ ''+ @@ -78,7 +80,6 @@ RED.deploy = (function() { mainMenuItems.push({id:"deploymenu-item-reload", icon:"red/images/deploy-reload.svg",label:RED._("deploy.restartFlows"),sublabel:RED._("deploy.restartFlowsDesc"),onselect:"core:restart-flows"}) RED.menu.init({id:"red-ui-header-button-deploy-options", options: mainMenuItems }); } else if (type == "simple") { - var label = options.label || RED._("deploy.deploy"); var icon = 'red/images/deploy-full-o.svg'; if (options.hasOwnProperty('icon')) { icon = options.icon; diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js index b9f586944..b35e60d50 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/editors/code-editors/monaco.js @@ -691,6 +691,7 @@ RED.editor.codeEditor.monaco = (function() { 2322, //Type 'unknown' is not assignable to type 'string' 2339, //property does not exist on 2345, //Argument of type xxx is not assignable to parameter of type 'DateTimeFormatOptions' + 2538, //Ignore symbols as index property error. 7043, //i forget what this one is, 80001, //Convert to ES6 module 80004, //JSDoc types may be moved to TypeScript types. diff --git a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js index 92d4593c6..198af0850 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/ui/view.js +++ b/packages/node_modules/@node-red/editor-client/src/js/ui/view.js @@ -2170,19 +2170,24 @@ RED.view = (function() { n.n.moved = true; } } - - // Check to see if we need to splice a link + // If a node has moved and ends up being spliced into a link, keep + // track of which historyEvent to add the splice info to + let targetSpliceEvent = null if (moveEvent.nodes.length > 0) { historyEvent.events.push(moveEvent) - if (activeSpliceLink) { - var linkToSplice = d3.select(activeSpliceLink).data()[0]; - spliceLink(linkToSplice, movingSet.get(0).n, moveEvent) - } + targetSpliceEvent = moveEvent } if (moveAndChangedGroupEvent.nodes.length > 0) { historyEvent.events.push(moveAndChangedGroupEvent) + targetSpliceEvent = moveAndChangedGroupEvent } - + // activeSpliceLink will only be set if the movingSet has a single + // node that is able to splice. + if (targetSpliceEvent && activeSpliceLink) { + var linkToSplice = d3.select(activeSpliceLink).data()[0]; + spliceLink(linkToSplice, movingSet.get(0).n, targetSpliceEvent) + } + // Only continue if something has moved if (historyEvent.events.length > 0) { RED.nodes.dirty(true); diff --git a/packages/node_modules/@node-red/editor-client/src/js/user.js b/packages/node_modules/@node-red/editor-client/src/js/user.js index e2c3cb577..e626f4ec0 100644 --- a/packages/node_modules/@node-red/editor-client/src/js/user.js +++ b/packages/node_modules/@node-red/editor-client/src/js/user.js @@ -351,10 +351,10 @@ RED.user = (function() { userIcon.css({ backgroundImage: "url("+user.image+")", }) - } else if (user.anonymous) { + } else if (user.anonymous || (!user.username && !user.email)) { $('').appendTo(userIcon); } else { - $('').text(user.username.substring(0,2)).appendTo(userIcon); + $('').text((user.username || user.email).substring(0,2)).appendTo(userIcon); } if (user.profileColor !== undefined) { userIcon.addClass('red-ui-user-profile-color-' + user.profileColor) diff --git a/packages/node_modules/@node-red/editor-client/src/sass/ui/common/autoComplete.scss b/packages/node_modules/@node-red/editor-client/src/sass/ui/common/autoComplete.scss index 0501bb6a2..85cb4f1db 100644 --- a/packages/node_modules/@node-red/editor-client/src/sass/ui/common/autoComplete.scss +++ b/packages/node_modules/@node-red/editor-client/src/sass/ui/common/autoComplete.scss @@ -2,4 +2,15 @@ &.red-ui-popover-panel { border-top: none; } + + +} +.red-ui-autoComplete-completion { + font-family: var(--red-ui-monospace-font); + white-space: nowrap; + overflow: hidden; + flex-grow: 1; + text-overflow: ellipsis; + direction: rtl; + text-align: left; } diff --git a/packages/node_modules/@node-red/nodes/locales/de/network/10-mqtt.html b/packages/node_modules/@node-red/nodes/locales/de/network/10-mqtt.html index 2aa31c081..e5c7b5fb5 100644 --- a/packages/node_modules/@node-red/nodes/locales/de/network/10-mqtt.html +++ b/packages/node_modules/@node-red/nodes/locales/de/network/10-mqtt.html @@ -39,10 +39,36 @@
    MQTTv5: Ablaufzeit der Nachricht in Sekunden.

    Details

    -

    Das abonnierte Topic darf MQTT-Platzhalterzeichen (wildcards) enthalten (+ für eine Ebene und # für mehrere Ebenen).

    -

    Dieser Node erfordert eine Verbindung zu einem MQTT-Broker, der über die Auswahlliste selektiert werden kann. - Eine neue Verbindung wird durch Klicken auf das Stiftsymbol erstellt.

    +

    Das abonnierte Topic darf MQTT-Platzhalterzeichen (wildcards) enthalten (+ für eine Ebene und # für mehrere Ebenen).

    +

    Diese Node erfordert eine Verbindung zu einem MQTT-Broker, der über die Auswahlliste selektiert werden kann. Eine neue Verbindung wird durch Klicken auf das Stiftsymbol erstellt.

    Mehrere MQTT-Nodes (in oder out) können bei Bedarf dieselbe Broker-Verbindung nutzen.

    +

    Dynamische Steuerung

    + Die von der Node genutzte Verbindung kann dynamisch gesteuert werden, wenn die MQTT-Node eine der folgenden Nachrichten erhält. Die Payload dieser Nachrichten werden nicht veröffentlicht. +

    Eingangsdaten

    +

    Nur Verfügbar, wenn die Node für dynamische Abonnements konfiguriert wurde.

    +
    +
    action string
    +
    Der Name der Aktion, die die MQTT-Node ausführen soll. Verfügbare Aktionen sind: "connect", "disconnect", "getSubscriptions", "subscribe" und "unsubscribe".
    +
    topic string|object|array
    +
    Bei den Aktionen "subscribe" und "unsubscribe" gibt diese Eigenschaft die MQTT-Topic an. Dabei kann es sich um Folgendes handeln: +
      +
    • eine Zeichenfolge, die den Topic-Filter enthält
    • +
    • ein Objekt mit den Eigenschaften topic und qos
    • +
    • ein Array aus Zeichenfolgen oder Objekten, um mehrere Topics gleichzeitig zu verwalten
    • +
    +
    +
    broker broker
    +
    Für die Aktion "connect" kann diese Eigenschaft jede der einzelnen Broker-Konfigurationseinstellungen überschreiben, einschließlich:
      +
    • broker
    • +
    • port
    • +
    • url - überschreibt Broker/Port, um eine vollständige Verbindungs-URL bereitzustellen
    • +
    • username
    • +
    • password
    • +
    +

    Wenn diese Eigenschaft gesetzt ist und der Broker bereits verbunden ist, wird ein Fehler protokolliert, es sei denn, die Eigenschaft force gesetzt - in diesem Fall wird die Verbindung zum Broker getrennt, die neuen Einstellungen angewendet und erneut verbunden.

    +
    +
    +