@node-red/editor-client 4.1.0-beta.2 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1278,7 +1278,7 @@
1278
1278
  "label": "Notifications de mise à jour",
1279
1279
  "settingsTitle": "Activer les notifications de mise à jour",
1280
1280
  "settingsDescription": "<p>Node-RED peut vous avertir de la disponibilité d'une nouvelle version. Vous êtes ainsi informé des dernières fonctionnalités et correctifs.</p><p>Cela nécessite d'envoyer des données anonymes à l'équipe Node-RED. Elles n'incluent aucun détail sur vos flux ou vos utilisateurs.</p><p>Pour plus d'informations sur les informations collectées et leur utilisation, veuillez consulter la <a href=\"https://nodered.org/docs/telemetry\" target=\"_blank\">documentation</a>.</p>",
1281
- "settingsDescription2": "<p>Vous pouvez modifier ce paramètre à tout moment dans les paramètres de l'éditeur.</p>",
1281
+ "settingsDescription2": "<p>Vous pouvez modifier ce paramètre à tout moment dans les paramètres de l'utilisateur.</p>",
1282
1282
  "enableLabel": "Oui, activer les notifications",
1283
1283
  "disableLabel": "Non, ne pas activer les notifications",
1284
1284
  "updateAvailable": "Mise(s) à jour disponible(s)",
@@ -573,6 +573,7 @@
573
573
  "filter": "ノードを検索",
574
574
  "search": "ノードを検索",
575
575
  "addCategory": "新規追加...",
576
+ "loadingSuggestions": "提案を読み込み中...",
576
577
  "label": {
577
578
  "subflows": "サブフロー",
578
579
  "network": "ネットワーク",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-red/editor-client",
3
- "version": "4.1.0-beta.2",
3
+ "version": "4.1.0",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
package/public/red/about CHANGED
@@ -1,3 +1,10 @@
1
+ #### 4.1.0: Milestone Release
2
+
3
+ - Fix: multipart form data upload issue (#5228) @debadutta98
4
+ - Update help document of filter node (#5210) @kazuhitoyokoi
5
+ - Fix inject node validation to support binary and hexadecimal numbers (#5212) @ZJvandeWeg
6
+ - Do not select a nearest node if move is active (#5199) @GogoVega
7
+
1
8
  #### 4.1.0-beta.2: Beta Release
2
9
 
3
10
  Editor
package/public/red/red.js CHANGED
@@ -6341,6 +6341,7 @@ RED.nodes = (function() {
6341
6341
  * - id:replace - import over the top of existing
6342
6342
  * - modules: map of module:version - hints for unknown nodes
6343
6343
  * - applyNodeDefaults - whether to apply default values to the imported nodes (default: false)
6344
+ * - eventContext - context to include in the `nodes:add` event
6344
6345
  */
6345
6346
  function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) {
6346
6347
  const defOpts = {
@@ -6349,7 +6350,8 @@ RED.nodes = (function() {
6349
6350
  markChanged: false,
6350
6351
  reimport: false,
6351
6352
  importMap: {},
6352
- applyNodeDefaults: false
6353
+ applyNodeDefaults: false,
6354
+ eventContext: null
6353
6355
  }
6354
6356
  options = Object.assign({}, defOpts, options)
6355
6357
  options.importMap = options.importMap || {}
@@ -7225,7 +7227,7 @@ RED.nodes = (function() {
7225
7227
 
7226
7228
  // Now the nodes have been fully updated, add them.
7227
7229
  for (i=0;i<new_nodes.length;i++) {
7228
- new_nodes[i] = addNode(new_nodes[i])
7230
+ new_nodes[i] = addNode(new_nodes[i], options.eventContext)
7229
7231
  node_map[new_nodes[i].id] = new_nodes[i]
7230
7232
  }
7231
7233
 
@@ -21009,23 +21011,27 @@ RED.keyboard = (function() {
21009
21011
  return resolveKeyEvent(evt);
21010
21012
  }
21011
21013
  }
21012
- d3.select(window).on("keydown",function() {
21014
+ d3.select(window).on("keydown", () => {
21015
+ handleEvent(d3.event)
21016
+ })
21017
+
21018
+ function handleEvent (evt) {
21013
21019
  if (!handlersActive) {
21014
21020
  return;
21015
21021
  }
21016
- if (metaKeyCodes[d3.event.keyCode]) {
21022
+ if (metaKeyCodes[evt]) {
21017
21023
  return;
21018
21024
  }
21019
- var handler = resolveKeyEvent(d3.event);
21025
+ var handler = resolveKeyEvent(evt);
21020
21026
  if (handler && handler.ondown) {
21021
21027
  if (typeof handler.ondown === "string") {
21022
21028
  RED.actions.invoke(handler.ondown);
21023
21029
  } else {
21024
21030
  handler.ondown();
21025
21031
  }
21026
- d3.event.preventDefault();
21027
- }
21028
- });
21032
+ evt.preventDefault();
21033
+ }
21034
+ }
21029
21035
 
21030
21036
  function addHandler(scope,key,modifiers,ondown) {
21031
21037
  var mod = modifiers;
@@ -21407,7 +21413,8 @@ RED.keyboard = (function() {
21407
21413
  formatKey: formatKey,
21408
21414
  validateKey: validateKey,
21409
21415
  disable: disable,
21410
- enable: enable
21416
+ enable: enable,
21417
+ handle: handleEvent
21411
21418
  }
21412
21419
 
21413
21420
  })();
@@ -23414,7 +23421,11 @@ RED.view = (function() {
23414
23421
  if (RED.workspaces.isLocked()) {
23415
23422
  return
23416
23423
  }
23417
- importNodes(clipboard,{generateIds: clipboardSource === 'copy', generateDefaultNames: clipboardSource === 'copy'});
23424
+ importNodes(clipboard, {
23425
+ generateIds: clipboardSource === 'copy',
23426
+ generateDefaultNames: clipboardSource === 'copy',
23427
+ eventContext: { source: 'clipboard' }
23428
+ });
23418
23429
  });
23419
23430
 
23420
23431
  RED.actions.add("core:detach-selected-nodes", function() { detachSelectedNodes() })
@@ -24086,7 +24097,8 @@ RED.view = (function() {
24086
24097
  touchImport: true,
24087
24098
  notify: false,
24088
24099
  // Ensure the node gets all of its defaults applied
24089
- applyNodeDefaults: true
24100
+ applyNodeDefaults: true,
24101
+ eventContext: { source: 'typeSearch' }
24090
24102
  })
24091
24103
  quickAddActive = false;
24092
24104
  ghostNode.remove();
@@ -24136,6 +24148,7 @@ RED.view = (function() {
24136
24148
  }
24137
24149
  var nn;
24138
24150
  var historyEvent;
24151
+ let addHistoryEvent;
24139
24152
  if (/^_action_:/.test(type)) {
24140
24153
  const actionName = type.substring(9)
24141
24154
  quickAddActive = false;
@@ -24169,6 +24182,7 @@ RED.view = (function() {
24169
24182
  nn = result.node;
24170
24183
  historyEvent = result.historyEvent;
24171
24184
  }
24185
+ addHistoryEvent = historyEvent;
24172
24186
  if (keepAdding) {
24173
24187
  mouse_mode = RED.state.QUICK_JOINING;
24174
24188
  }
@@ -24323,7 +24337,8 @@ RED.view = (function() {
24323
24337
 
24324
24338
  if (linkToSplice) {
24325
24339
  resetMouseVars();
24326
- spliceLink(linkToSplice, nn, historyEvent)
24340
+ // Add any history event data to the original add event
24341
+ spliceLink(linkToSplice, nn, addHistoryEvent)
24327
24342
  }
24328
24343
  RED.history.push(historyEvent);
24329
24344
  RED.nodes.dirty(true);
@@ -24367,6 +24382,7 @@ RED.view = (function() {
24367
24382
  }
24368
24383
  },
24369
24384
  suggest: function (suggestion) {
24385
+ // TypeSearch only provides one suggestion at a time
24370
24386
  if (suggestion?.nodes?.length > 0) {
24371
24387
  // Reposition the suggestion relative to the existing ghost node position
24372
24388
  const deltaX = (suggestion.nodes[0].x || 0) - point[0]
@@ -28471,7 +28487,8 @@ RED.view = (function() {
28471
28487
  generateIds: false,
28472
28488
  generateDefaultNames: false,
28473
28489
  notify: true,
28474
- applyNodeDefaults: false
28490
+ applyNodeDefaults: false,
28491
+ eventContext: null
28475
28492
  }
28476
28493
  const addNewFlow = options.addFlow
28477
28494
  const touchImport = options.touchImport;
@@ -28561,7 +28578,8 @@ RED.view = (function() {
28561
28578
  importMap: options.importMap,
28562
28579
  markChanged: true,
28563
28580
  modules: modules,
28564
- applyNodeDefaults: applyNodeDefaults
28581
+ applyNodeDefaults: applyNodeDefaults,
28582
+ eventContext: options.eventContext
28565
28583
  });
28566
28584
  if (importResult) {
28567
28585
  var new_nodes = importResult.nodes;
@@ -29133,6 +29151,8 @@ RED.view = (function() {
29133
29151
  */
29134
29152
  function setSuggestedFlow (suggestion) {
29135
29153
  $(window).off('keydown.suggestedFlow')
29154
+ $(window).off('mousedown.suggestedFlow');
29155
+ RED.keyboard.enable()
29136
29156
  if (!currentSuggestion && !suggestion) {
29137
29157
  // Avoid unnecessary redraws
29138
29158
  return
@@ -29140,7 +29160,28 @@ RED.view = (function() {
29140
29160
  // Clear up any existing suggestion state
29141
29161
  clearSuggestedFlow()
29142
29162
  currentSuggestion = suggestion
29143
- if (suggestion?.nodes?.length > 0) {
29163
+ if (suggestion && suggestion.nodes) {
29164
+ // If suggestion.nodes is an array of arrays, then there are multiple suggestions being provided
29165
+ // Normalise the shape of the suggestion.nodes to be an array of arrays
29166
+ if (!Array.isArray(suggestion.nodes[0])) {
29167
+ suggestion.nodes = [suggestion.nodes]
29168
+ }
29169
+ suggestion.count = suggestion.nodes.length
29170
+ suggestion.currentIndex = 0
29171
+ suggestion.current = suggestion.nodes[suggestion.currentIndex]
29172
+ refreshSuggestedFlow()
29173
+ } else {
29174
+ redraw()
29175
+ }
29176
+ }
29177
+
29178
+ function refreshSuggestedFlow () {
29179
+ const suggestion = currentSuggestion
29180
+ suggestedNodes = []
29181
+ suggestedLinks = []
29182
+
29183
+ currentSuggestion.current = currentSuggestion.nodes[currentSuggestion.currentIndex]
29184
+ if (suggestion.current.length > 0) {
29144
29185
  const nodeMap = {}
29145
29186
  const links = []
29146
29187
  const positionOffset = { x: 0, y: 0 }
@@ -29156,12 +29197,11 @@ RED.view = (function() {
29156
29197
  targetX += gridOffset.x
29157
29198
  }
29158
29199
 
29159
- positionOffset.x = targetX - (suggestion.nodes[0].x || 0)
29160
- positionOffset.y = targetY - (suggestion.nodes[0].y || 0)
29200
+ positionOffset.x = targetX - (suggestion.current[0].x || 0)
29201
+ positionOffset.y = targetY - (suggestion.current[0].y || 0)
29161
29202
  }
29162
29203
 
29163
-
29164
- suggestion.nodes.forEach(nodeConfig => {
29204
+ suggestion.current.forEach(nodeConfig => {
29165
29205
  if (!nodeConfig.type || nodeConfig.type === 'group' || nodeConfig.type === 'subflow' || nodeConfig.type === 'tab') {
29166
29206
  // A node type we don't support previewing
29167
29207
  return
@@ -29258,12 +29298,38 @@ RED.view = (function() {
29258
29298
  })
29259
29299
  }
29260
29300
  if (!RED.typeSearch.isVisible()) {
29301
+ // Disable the core keyboard handling so we get priority.
29302
+ // Ideally we'd be able to do this via actions, but we can't currently scope
29303
+ // actions finely enough to only be handled when the suggested flow is active.
29304
+ RED.keyboard.disable()
29261
29305
  $(window).on('keydown.suggestedFlow', function (evt) {
29262
- if (evt.keyCode === 9) { // tab
29306
+ $(window).off('keydown.suggestedFlow')
29307
+ RED.keyboard.enable()
29308
+ if (evt.keyCode === 9) { // tab; apply suggestion
29309
+ evt.stopPropagation();
29310
+ evt.preventDefault();
29263
29311
  applySuggestedFlow();
29264
- } else {
29312
+ } else if (evt.keyCode === 38 && currentSuggestion.count > 1) { // up arrow
29313
+ evt.stopPropagation();
29314
+ evt.preventDefault();
29315
+ currentSuggestion.currentIndex--
29316
+ if (currentSuggestion.currentIndex < 0) {
29317
+ currentSuggestion.currentIndex = currentSuggestion.count - 1
29318
+ }
29319
+ refreshSuggestedFlow();
29320
+ } else if (evt.keyCode === 40 && currentSuggestion.count > 1) { // down arrow
29321
+ evt.stopPropagation();
29322
+ evt.preventDefault();
29323
+ currentSuggestion.currentIndex++
29324
+ if (currentSuggestion.currentIndex === currentSuggestion.count) {
29325
+ currentSuggestion.currentIndex = 0
29326
+ }
29327
+ refreshSuggestedFlow();
29328
+ } else { // Anything else; clear the suggestion
29265
29329
  clearSuggestedFlow();
29266
29330
  RED.view.redraw(true);
29331
+ // manually push the event to the keyboard handler
29332
+ RED.keyboard.handle(evt)
29267
29333
  }
29268
29334
  });
29269
29335
  }
@@ -29280,20 +29346,27 @@ RED.view = (function() {
29280
29346
  ghostNode.style('opacity', 1)
29281
29347
  }
29282
29348
  }
29283
- redraw();
29349
+ if (currentSuggestion.count > 1 && suggestedNodes.length > 0) {
29350
+ suggestedNodes[0].status = {
29351
+ text: `${currentSuggestion.currentIndex + 1} / ${currentSuggestion.count}`,
29352
+ }
29353
+ suggestedNodes[0].dirtyStatus = true
29354
+ }
29355
+ redraw()
29284
29356
  }
29285
29357
 
29286
29358
  function clearSuggestedFlow () {
29287
29359
  $(window).off('mousedown.suggestedFlow');
29288
29360
  $(window).off('keydown.suggestedFlow')
29361
+ RED.keyboard.enable()
29289
29362
  currentSuggestion = null
29290
29363
  suggestedNodes = []
29291
29364
  suggestedLinks = []
29292
29365
  }
29293
29366
 
29294
29367
  function applySuggestedFlow () {
29295
- if (currentSuggestion && currentSuggestion.nodes) {
29296
- const nodesToImport = currentSuggestion.nodes
29368
+ if (currentSuggestion && currentSuggestion.current) {
29369
+ const nodesToImport = currentSuggestion.current
29297
29370
  const sourceNode = currentSuggestion.source
29298
29371
  const sourcePort = currentSuggestion.sourcePort || 0
29299
29372
  setSuggestedFlow(null)
@@ -29302,7 +29375,8 @@ RED.view = (function() {
29302
29375
  touchImport: true,
29303
29376
  notify: false,
29304
29377
  // Ensure the node gets all of its defaults applied
29305
- applyNodeDefaults: true
29378
+ applyNodeDefaults: true,
29379
+ eventContext: { source: 'suggestion' }
29306
29380
  })
29307
29381
  if (sourceNode) {
29308
29382
  const firstNode = result.nodeMap[nodesToImport[0].id]
@@ -30307,6 +30381,8 @@ RED.view.tools = (function() {
30307
30381
 
30308
30382
 
30309
30383
  function gotoNearestNode(direction) {
30384
+ // Do not select a nearest node if move is active
30385
+ if (RED.view.state() === RED.state.MOVING_ACTIVE) { return }
30310
30386
  var selection = RED.view.selection();
30311
30387
  if (selection.nodes && selection.nodes.length === 1) {
30312
30388
  var origin = selection.nodes[0];
@@ -45147,6 +45223,15 @@ RED.editor.codeEditor.monaco = (function() {
45147
45223
  ed.gotoLine(row, col);
45148
45224
  }
45149
45225
  ed.type = type;
45226
+
45227
+ ed.onKeyDown((event) => {
45228
+ if (event.keyCode === monaco.KeyCode.Escape) {
45229
+ if (monacoWidgetsAreOpen(ed)) {
45230
+ event.preventDefault();
45231
+ }
45232
+ }
45233
+ })
45234
+
45150
45235
  return ed;
45151
45236
  }
45152
45237
 
@@ -45156,6 +45241,16 @@ RED.editor.codeEditor.monaco = (function() {
45156
45241
  return true;
45157
45242
  }
45158
45243
 
45244
+ function monacoWidgetsAreOpen(editor) {
45245
+ /** @type {HTMLElement} */
45246
+ const editorDomNode = editor.getDomNode()
45247
+ const suggestVisible = !!editorDomNode.querySelector('.monaco-editor .suggest-widget.visible');
45248
+ const parameterHintsVisible = !!editorDomNode.querySelector('.monaco-editor .parameter-hints-widget.visible');
45249
+ const findWidgetVisible = !!editorDomNode.querySelector('.monaco-editor .find-widget.visible');
45250
+ const renameInputVisible = !!editorDomNode.querySelector('.monaco-editor .rename-box');
45251
+ return suggestVisible || parameterHintsVisible || findWidgetVisible || renameInputVisible
45252
+ }
45253
+
45159
45254
  return {
45160
45255
  /**
45161
45256
  * Editor type
@@ -49749,6 +49844,14 @@ RED.actionList = (function() {
49749
49844
  }
49750
49845
  }
49751
49846
  function confirm(def) {
49847
+ if (!activeSuggestion) {
49848
+ // The user has hit Enter without selecting an entry in the list.
49849
+ // This means no suggestion has been shown yet - and the position recalculation
49850
+ // has not been done.
49851
+ // Trigger an update of the suggestion to get the position right before
49852
+ // applying.
49853
+ updateSuggestion(def)
49854
+ }
49752
49855
  hide();
49753
49856
  if (!def.nodes && !/^_action_:/.test(def.type)) {
49754
49857
  typesUsed[def.type] = Date.now();