Compare commits

...

36 Commits

Author SHA1 Message Date
Nick O'Leary
bbc1a82c2a Fix linting on boolean check 2023-02-23 23:45:34 +00:00
Nick O'Leary
2ddbb44992 Add change annotation for newly added flow 2023-02-23 23:14:18 +00:00
Nick O'Leary
363a8b8588 Add change icon to tabs 2023-02-23 22:48:08 +00:00
Nick O'Leary
196a9ae43a Merge pull request #4065 from node-red/node-help-link
Add link to node help in node edit dialog footer
2023-02-21 14:25:40 +00:00
Nick O'Leary
196773d6ba Merge branch 'master' into dev 2023-02-20 18:17:16 +00:00
Nick O'Leary
d8dbe68425 Merge pull request #4064 from node-red/reverse-tab-scroll
Reverse direction of tab scroll to expected direction
2023-02-20 18:15:04 +00:00
Nick O'Leary
286c0b000f Add link to node help in node edit dialog footer 2023-02-20 18:13:02 +00:00
Nick O'Leary
dec3164fdd Reverse direction of tab scroll to expected direction 2023-02-20 17:30:16 +00:00
Nick O'Leary
c2772e5038 Merge pull request #4051 from sonntam/feature-editor-multipleToNode
Added editor feature for connecting multiple nodes to single node
2023-02-20 17:00:14 +00:00
Nick O'Leary
43e4d09f72 Merge pull request #4049 from kazuhitoyokoi/dev-addjpn
Support i18n of lock/unlock buttons in flow property UI
2023-02-20 16:51:55 +00:00
Nick O'Leary
06e35baeaa Merge pull request #4060 from node-red-hitachi/fix-empty-group-on-outliner
Fix to add empty marker to empty group
2023-02-20 16:51:17 +00:00
Nick O'Leary
c9d72d7a1d Merge pull request #4056 from kazuhitoyokoi/master-validator
Add validators for complete and link call nodes
2023-02-20 16:50:01 +00:00
Nick O'Leary
7e0a783220 Merge pull request #4054 from kazuhitoyokoi/dev-fixmermaid
Apply Mermaid diagram for project settings UI
2023-02-20 16:46:08 +00:00
Nick O'Leary
f853c9f1c3 Merge pull request #4053 from kazuhitoyokoi/dev-fixtour
Fix image URLs for v3.0 tour
2023-02-20 16:45:44 +00:00
Nick O'Leary
62e9572070 Merge pull request #4048 from kazuhitoyokoi/master-notification
Show scrollbar in notification dialog only when needed
2023-02-20 16:45:10 +00:00
Nick O'Leary
fea68c8acc Merge pull request #4050 from kazuhitoyokoi/master-addtooltip
Add tooltip for show/hide button on info sidebar
2023-02-20 16:44:38 +00:00
Hiroyasu Nishiyama
929f0e90ac add empty marker to group 2023-02-14 20:49:43 +09:00
Kazuhito Yokoi
81331e68d2 Add validator for link call node 2023-02-10 18:55:10 +09:00
Kazuhito Yokoi
4fda59a585 Add validator for complete node 2023-02-10 16:49:29 +09:00
Kazuhito Yokoi
4477b9ac18 Apply mermaid diagram for project settings UI 2023-02-06 00:53:22 +09:00
Kazuhito Yokoi
78f93dc11a Fix image URLs for v3.0 tour 2023-02-05 18:58:58 +09:00
sonntam
4c7c855f2c fix code style 2023-02-04 21:31:49 +01:00
Marcus Sonntag
8740ec5570 Added editor feature for connecting multiple nodes to single node 2023-02-04 21:03:30 +01:00
Kazuhito Yokoi
ede3ac4282 Add tooltip for show/hide button on info sidebar 2023-02-04 20:55:34 +09:00
Kazuhito Yokoi
3e4cad3a79 Add Japanese translations for the action list 2023-02-04 20:38:08 +09:00
Kazuhito Yokoi
bd0b0077a3 Support i18n of lock/unlock button in flow property UI 2023-02-04 20:28:08 +09:00
Kazuhito Yokoi
16f8b78b39 Show scrollbar in notification dialog only when needed 2023-02-04 11:16:28 +09:00
Nick O'Leary
90d1bb0ae4 Merge branch 'master' into dev 2023-02-03 09:11:46 +00:00
Nick O'Leary
adfc5b3e98 Merge pull request #4043 from node-red-hitachi/fix-group-undo-position
fix group position after undo
2023-02-03 09:11:20 +00:00
Hiroyasu Nishiyama
f3d7016ab2 fix group position after undo 2023-02-03 17:25:58 +09:00
Nick O'Leary
ae776547ce Merge pull request #4042 from kazuhitoyokoi/dev-jpn
Add Japanese translations for welcome tour of 3.1.0 beta.1
2023-02-02 15:19:09 +00:00
Kazuhito Yokoi
da6885be62 Add Japanese translations for welcome tour of 3.1.0 beta.1 2023-02-03 00:08:50 +09:00
Nick O'Leary
c0fa4a077f Merge pull request #4041 from node-red/310-tour
Add welcome tour for 3.1.0 beta.1
2023-02-02 14:53:06 +00:00
Nick O'Leary
19139a4dce Merge pull request #4040 from kazuhitoyokoi/dev-jpn
Add Japanese translations for locking flow
2023-02-02 13:42:22 +00:00
Kazuhito Yokoi
7d1c3133b3 Fix i18n of tooltips for locking flow 2023-02-02 22:37:00 +09:00
Kazuhito Yokoi
da9c0af854 Add Japanese translations for locking flow 2023-02-02 22:35:07 +09:00
25 changed files with 515 additions and 90 deletions

View File

@@ -23,7 +23,11 @@
"position": "Position",
"enable": "Enable",
"disable": "Disable",
"upload": "Upload"
"upload": "Upload",
"lock": "Lock",
"unlock": "Unlock",
"locked": "Locked",
"unlocked": "Unlocked"
},
"type": {
"string": "string",
@@ -695,7 +699,10 @@
"triggerAction": "Trigger action",
"find": "Find in workspace",
"copyItemUrl": "Copy item url",
"copyURL2Clipboard": "Copied url to clipboard"
"copyURL2Clipboard": "Copied url to clipboard",
"showFlow": "Show",
"hideFlow": "Hide",
"find": "Find in workspace"
},
"help": {
"name": "Help",

View File

@@ -23,7 +23,11 @@
"position": "配置",
"enable": "有効",
"disable": "無効",
"upload": "アップロード"
"upload": "アップロード",
"lock": "固定",
"unlock": "固定を解除",
"locked": "固定済み",
"unlocked": "固定なし"
},
"type": {
"string": "文字列",
@@ -73,6 +77,8 @@
"selectNodes": "ノードをクリックして選択",
"enableFlow": "フローを有効化",
"disableFlow": "フローを無効化",
"lockFlow": "フローを固定",
"unlockFlow": "フローの固定を解除",
"moveToStart": "フローを先頭へ移動",
"moveToEnd": "フローを最後へ移動"
},
@@ -107,6 +113,7 @@
"displayStatus": "ノードのステータスを表示",
"displayConfig": "設定ノード",
"import": "読み込み",
"importExample": "フロー例を読み込み",
"export": "書き出し",
"search": "ノードを検索",
"searchInput": "ノードを検索",
@@ -503,6 +510,7 @@
"addRemoveNode": "ノードの選択、選択解除",
"editSelected": "選択したノードを編集",
"deleteSelected": "選択したノードや接続を削除",
"deleteReconnect": "削除と再接続",
"importNode": "フローの読み込み",
"exportNode": "フローの書き出し",
"nudgeNode": "選択したノードを移動(移動量小)",
@@ -691,7 +699,10 @@
"triggerAction": "アクションを実行",
"find": "ワークスペース内を検索",
"copyItemUrl": "要素のURLをコピー",
"copyURL2Clipboard": "URLをクリップボードにコピーしました"
"copyURL2Clipboard": "URLをクリップボードにコピーしました",
"showFlow": "表示",
"hideFlow": "非表示",
"find": "ワークスペース内を検索"
},
"help": {
"name": "ヘルプ",
@@ -1218,6 +1229,11 @@
"junction": "分岐点",
"linkNodes": "Linkード"
},
"env-var": {
"environment": "環境変数",
"header": "大域環境変数",
"revert": "破棄"
},
"action-list": {
"toggle-show-tips": "ヒント表示切替",
"show-about": "Node-REDの説明を表示",
@@ -1365,11 +1381,9 @@
"copy-item-url": "要素のURLをコピー",
"copy-item-edit-url": "要素の編集URLをコピー",
"move-flow-to-start": "フローを先頭に移動",
"move-flow-to-end": "フローを末尾に移動"
},
"env-var": {
"environment": "環境変数",
"header": "大域環境変数",
"revert": "破棄"
"move-flow-to-end": "フローを末尾に移動",
"lock-flow": "フローを固定",
"unlock-flow": "フローの固定を解除",
"show-node-help": "ノードのヘルプを表示"
}
}

View File

@@ -421,6 +421,9 @@ RED.history = (function() {
ev.node[i] = ev.changes[i];
}
}
ev.node.dirty = true;
ev.node.changed = ev.changed;
var eventType;
switch(ev.node.type) {
case 'tab': eventType = "flows"; break;
@@ -511,8 +514,6 @@ RED.history = (function() {
inverseEv.links.push(ev.createdLinks[i]);
}
}
ev.node.dirty = true;
ev.node.changed = ev.changed;
} else if (ev.t == "createSubflow") {
inverseEv = {
t: "deleteSubflow",

View File

@@ -46,6 +46,9 @@ RED.nodes = (function() {
function setDirty(d) {
dirty = d;
if (!d) {
allNodes.clearState()
}
RED.events.emit("workspace:dirty",{dirty:dirty});
}
@@ -238,22 +241,36 @@ RED.nodes = (function() {
// allNodes holds information about the Flow nodes.
var allNodes = (function() {
// Map node.id -> node
var nodes = {};
// Map tab.id -> Array of nodes on that tab
var tabMap = {};
// Map tab.id -> Set of dirty object ids on that tab
var tabDirtyMap = {};
// Map tab.id -> Set of object ids of things deleted from the tab that weren't otherwise dirty
var tabDeletedNodesMap = {};
// Set of object ids of things added to a tab after initial import
var addedDirtyObjects = new Set()
var api = {
addTab: function(id) {
tabMap[id] = [];
tabDirtyMap[id] = new Set();
tabDeletedNodesMap[id] = new Set();
},
hasTab: function(z) {
return tabMap.hasOwnProperty(z)
},
removeTab: function(id) {
delete tabMap[id];
delete tabDirtyMap[id];
delete tabDeletedNodesMap[id];
},
addNode: function(n) {
nodes[n.id] = n;
if (tabMap.hasOwnProperty(n.z)) {
tabMap[n.z].push(n);
api.addObjectToWorkspace(n.z, n.id, n.changed || n.moved)
} else {
console.warn("Node added to unknown tab/subflow:",n);
tabMap["_"] = tabMap["_"] || [];
@@ -267,8 +284,37 @@ RED.nodes = (function() {
if (i > -1) {
tabMap[n.z].splice(i,1);
}
api.removeObjectFromWorkspace(n.z, n.id)
}
},
/**
* Add an object to our dirty/clean tracking state
* @param {String} z
* @param {String} id
* @param {Boolean} isDirty
*/
addObjectToWorkspace: function (z, id, isDirty) {
if (isDirty) {
addedDirtyObjects.add(id)
}
if (tabDeletedNodesMap[z].has(id)) {
tabDeletedNodesMap[z].delete(id)
}
api.markNodeDirty(z, id, isDirty)
},
/**
* Remove an object from our dirty/clean tracking state
* @param {String} z
* @param {String} id
*/
removeObjectFromWorkspace: function (z, id) {
if (!addedDirtyObjects.has(id)) {
tabDeletedNodesMap[z].add(id)
} else {
addedDirtyObjects.delete(id)
}
api.markNodeDirty(z, id, false)
},
hasNode: function(id) {
return nodes.hasOwnProperty(id);
},
@@ -433,6 +479,33 @@ RED.nodes = (function() {
clear: function() {
nodes = {};
tabMap = {};
tabDirtyMap = {};
tabDeletedNodesMap = {};
addedDirtyObjects = new Set();
},
/**
* Clear all internal state on what is dirty.
*/
clearState: function () {
// Called when a deploy happens, we can forget about added/remove
// items as they have now been deployed.
addedDirtyObjects = new Set()
const flowsToCheck = new Set()
for (const [z, set] of Object.entries(tabDeletedNodesMap)) {
if (set.size > 0) {
set.clear()
flowsToCheck.add(z)
}
}
for (const [z, set] of Object.entries(tabDirtyMap)) {
if (set.size > 0) {
set.clear()
flowsToCheck.add(z)
}
}
for (const z of flowsToCheck) {
api.checkTabState(z)
}
},
eachNode: function(cb) {
var nodeList,i,j;
@@ -510,6 +583,36 @@ RED.nodes = (function() {
B._reordered = true;
return orderMap[A.id] - orderMap[B.id];
})
},
/**
* Update our records if an object is dirty or not
* @param {String} z tab id
* @param {String} id object id
* @param {Boolean} dirty whether the object is dirty or not
*/
markNodeDirty: function(z, id, dirty) {
if (tabDirtyMap[z]) {
if (dirty) {
tabDirtyMap[z].add(id)
} else {
tabDirtyMap[z].delete(id)
}
api.checkTabState(z)
}
},
/**
* Check if a tab should update its contentsChange flag
* @param {String} z tab id
*/
checkTabState: function (z) {
const ws = workspaces[z]
if (ws) {
const contentsChanged = tabDirtyMap[z].size > 0 || tabDeletedNodesMap[z].size > 0
if (Boolean(ws.contentsChanged) !== contentsChanged) {
ws.contentsChanged = contentsChanged
RED.events.emit("flows:change", ws);
}
}
}
}
return api;
@@ -597,6 +700,11 @@ RED.nodes = (function() {
throw new Error(`Cannot modified property '${prop}' of locked object '${node.type}:${node.id}'`)
}
}
if (node.z && (prop === 'changed' || prop === 'moved')) {
setTimeout(() => {
allNodes.markNodeDirty(node.z, node.id, node.changed || node.moved)
}, 0)
}
node[prop] = value;
return true
}
@@ -666,10 +774,16 @@ RED.nodes = (function() {
}
if (l.source.z === l.target.z && linkTabMap[l.source.z]) {
linkTabMap[l.source.z].push(l);
allNodes.addObjectToWorkspace(l.source.z, getLinkId(l), true)
}
RED.events.emit("links:add",l);
}
function getLinkId(link) {
return link.source.id + ':' + link.sourcePort + ':' + link.target.id
}
function getNode(id) {
if (id in configNodes) {
return configNodes[id];
@@ -864,6 +978,7 @@ RED.nodes = (function() {
if (index !== -1) {
linkTabMap[l.source.z].splice(index,1)
}
allNodes.removeObjectFromWorkspace(l.source.z, getLinkId(l))
}
}
RED.events.emit("links:remove",l);
@@ -1688,6 +1803,7 @@ RED.nodes = (function() {
* Options:
* - generateIds - whether to replace all node ids
* - addFlow - whether to import nodes to a new tab
* - markChanged - whether to set changed=true on all newly imported objects
* - reimport - if node has a .z property, dont overwrite it
* Only applicible when `generateIds` is false
* - importMap - how to resolve any conflicts.
@@ -1696,7 +1812,7 @@ RED.nodes = (function() {
* - id:replace - import over the top of existing
*/
function importNodes(newNodesObj,options) { // createNewIds,createMissingWorkspace) {
const defOpts = { generateIds: false, addFlow: false, reimport: false, importMap: {} }
const defOpts = { generateIds: false, addFlow: false, markChanged: false, reimport: false, importMap: {} }
options = Object.assign({}, defOpts, options)
options.importMap = options.importMap || {}
const createNewIds = options.generateIds;
@@ -1722,7 +1838,7 @@ RED.nodes = (function() {
newNodes = newNodesObj;
}
if (!$.isArray(newNodes)) {
if (!Array.isArray(newNodes)) {
newNodes = [newNodes];
}
@@ -2020,6 +2136,9 @@ RED.nodes = (function() {
if (!n.z) {
delete configNode.z;
}
if (options.markChanged) {
configNode.changed = true
}
if (n.hasOwnProperty('d')) {
configNode.d = n.d;
}
@@ -2082,6 +2201,9 @@ RED.nodes = (function() {
if (n.hasOwnProperty('g')) {
node.g = n.g;
}
if (options.markChanged) {
node.changed = true
}
if (createNewIds || options.importMap[n.id] === "copy") {
if (subflow_denylist[n.z]) {
continue;
@@ -2595,6 +2717,7 @@ RED.nodes = (function() {
groupsByZ[group.z] = groupsByZ[group.z] || [];
groupsByZ[group.z].push(group);
groups[group.id] = group;
allNodes.addObjectToWorkspace(group.z, group.id, group.changed || group.moved)
RED.events.emit("groups:add",group);
return group
}
@@ -2611,7 +2734,7 @@ RED.nodes = (function() {
}
}
RED.group.markDirty(group);
allNodes.removeObjectFromWorkspace(group.z, group.id)
delete groups[group.id];
RED.events.emit("groups:remove",group);
}
@@ -2626,6 +2749,7 @@ RED.nodes = (function() {
if (!nodeLinks[junction.id]) {
nodeLinks[junction.id] = {in:[],out:[]};
}
allNodes.addObjectToWorkspace(junction.z, junction.id, junction.changed || junction.moved)
RED.events.emit("junctions:add", junction)
return junction
}
@@ -2637,6 +2761,7 @@ RED.nodes = (function() {
}
delete junctions[junction.id]
delete nodeLinks[junction.id];
allNodes.removeObjectFromWorkspace(junction.z, junction.id)
RED.events.emit("junctions:remove", junction)
var removedLinks = links.filter(function(l) { return (l.source === junction) || (l.target === junction); });
@@ -2874,6 +2999,9 @@ RED.nodes = (function() {
RED.view.redraw(true);
}
});
RED.events.on('deploy', function () {
allNodes.clearState()
})
},
registry:registry,
setNodeList: registry.setNodeList,
@@ -2976,6 +3104,20 @@ RED.nodes = (function() {
}
}
},
eachGroup: function(cb) {
for (var group of Object.values(groups)) {
if (cb(group) === false) {
break
}
}
},
eachJunction: function(cb) {
for (var junction of Object.values(junctions)) {
if (cb(junction) === false) {
break
}
}
},
node: getNode,

View File

@@ -183,7 +183,7 @@ RED.tabs = (function() {
// Assume this is wheel event which might not trigger
// the scroll event, so do things manually
var sl = scrollContainer.scrollLeft();
sl -= evt.originalEvent.deltaY;
sl += evt.originalEvent.deltaY;
scrollContainer.scrollLeft(sl);
}
})
@@ -845,7 +845,6 @@ RED.tabs = (function() {
var badges = $('<span class="red-ui-tabs-badges"></span>').appendTo(li);
if (options.onselect) {
$('<i class="red-ui-tabs-badge-changed fa fa-circle"></i>').appendTo(badges);
$('<i class="red-ui-tabs-badge-selected fa fa-check-circle"></i>').appendTo(badges);
}

View File

@@ -79,7 +79,8 @@ RED.contextMenu = (function () {
w: 0, h: 0,
outputs: 1,
inputs: 1,
dirty: true
dirty: true,
moved: true
}
const historyEvent = {
dirty: RED.nodes.dirty(),

View File

@@ -557,12 +557,17 @@ RED.deploy = (function() {
} else {
RED.notify('<p>' + RED._("deploy.successfulDeploy") + '</p>', "success");
}
RED.nodes.eachNode(function (node) {
const flow = node.z && (RED.nodes.workspace(node.z) || RED.nodes.subflow(node.z) || null);
const flowsToLock = new Set()
function ensureUnlocked(id) {
const flow = id && (RED.nodes.workspace(id) || RED.nodes.subflow(id) || null);
const isLocked = flow ? flow.locked : false;
if (flow && isLocked) {
flow.locked = false;
flowsToLock.add(flow)
}
}
RED.nodes.eachNode(function (node) {
ensureUnlocked(node.z)
if (node.changed) {
node.dirty = true;
node.changed = false;
@@ -574,11 +579,33 @@ RED.deploy = (function() {
if (node.credentials) {
delete node.credentials;
}
if (flow && isLocked) {
flow.locked = isLocked;
}
});
RED.nodes.eachGroup(function (node) {
ensureUnlocked(node.z)
if (node.changed) {
node.dirty = true;
node.changed = false;
}
if (node.moved) {
node.dirty = true;
node.moved = false;
}
})
RED.nodes.eachJunction(function (node) {
ensureUnlocked(node.z)
if (node.changed) {
node.dirty = true;
node.changed = false;
}
if (node.moved) {
node.dirty = true;
node.moved = false;
}
})
RED.nodes.eachConfig(function (confNode) {
if (confNode.z) {
ensureUnlocked(confNode.z)
}
confNode.changed = false;
if (confNode.credentials) {
delete confNode.credentials;
@@ -588,8 +615,16 @@ RED.deploy = (function() {
subflow.changed = false;
});
RED.nodes.eachWorkspace(function (ws) {
ws.changed = false;
if (ws.changed || ws.added) {
ensureUnlocked(ws.z)
ws.changed = false;
delete ws.added
RED.events.emit("flows:change", ws)
}
});
flowsToLock.forEach(flow => {
flow.locked = true
})
// Once deployed, cannot undo back to a clean state
RED.history.markAllDirty();
RED.view.redraw();

View File

@@ -1045,6 +1045,13 @@ RED.editor = (function() {
var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)
var helpButton = $('<button type="button" class="red-ui-button"><i class="fa fa-book"></button>').on("click", function(evt) {
evt.preventDefault();
evt.stopPropagation();
RED.sidebar.help.show(editing_node.type);
}).appendTo(trayFooterLeft);
RED.popover.tooltip(helpButton, RED._("sidebar.help.showHelp"));
$('<input id="node-input-node-disabled" type="checkbox">').prop("checked",!!node.d).appendTo(trayFooterLeft).toggleButton({
enabledIcon: "fa-circle-thin",
disabledIcon: "fa-ban",
@@ -1190,6 +1197,13 @@ RED.editor = (function() {
var trayFooterLeft = $('<div class="red-ui-tray-footer-left"></div>').appendTo(trayFooter)
var helpButton = $('<button type="button" class="red-ui-button"><i class="fa fa-book"></button>').on("click", function(evt) {
evt.preventDefault();
evt.stopPropagation();
RED.sidebar.help.show(editing_config_node.type);
}).appendTo(trayFooterLeft);
RED.popover.tooltip(helpButton, RED._("sidebar.help.showHelp"));
$('<input id="node-config-input-node-disabled" type="checkbox">').prop("checked",!!editing_config_node.d).appendTo(trayFooterLeft).toggleButton({
enabledIcon: "fa-circle-thin",
disabledIcon: "fa-ban",
@@ -1922,9 +1936,9 @@ RED.editor = (function() {
workspace.locked = false;
}
$('<input id="node-input-locked" type="checkbox">').prop("checked",workspace.locked).appendTo(trayFooterRight).toggleButton({
enabledLabel: 'Unlocked',
enabledLabel: RED._("common.label.unlocked"),
enabledIcon: "fa-unlock-alt",
disabledLabel: 'Locked',
disabledLabel: RED._("common.label.locked"),
disabledIcon: "fa-lock",
invertState: true
})

View File

@@ -488,7 +488,8 @@ RED.group = (function() {
y: Number.POSITIVE_INFINITY,
w: 0,
h: 0,
_def: RED.group.def
_def: RED.group.def,
changed: true
}
group.z = nodes[0].z;

View File

@@ -165,6 +165,9 @@ RED.projects.settings = (function() {
}
var description = addTargetToExternalLinks($('<span class="red-ui-text-bidi-aware" dir=\"'+RED.text.bidi.resolveBaseTextDir(desc)+'">'+desc+'</span>')).appendTo(container);
description.find(".red-ui-text-bidi-aware").contents().filter(function() { return this.nodeType === 3 && this.textContent.trim() !== "" }).wrap( "<span></span>" );
setTimeout(function () {
mermaid.init();
}, 200);
}
function editSummary(activeProject, summary, container, version, versionContainer) {

View File

@@ -145,17 +145,19 @@ RED.sidebar.config = (function() {
} else {
var currentType = "";
nodes.forEach(function(node) {
var label = RED.utils.getNodeLabel(node,node.id);
var labelText = RED.utils.getNodeLabel(node,node.id);
if (node.type != currentType) {
$('<li class="red-ui-palette-node-config-type">'+node.type+'</li>').appendTo(list);
currentType = node.type;
}
if (node.changed) {
labelText += "!!"
}
var entry = $('<li class="red-ui-palette-node_id_'+node.id.replace(/\./g,"-")+'"></li>').appendTo(list);
var nodeDiv = $('<div class="red-ui-palette-node-config red-ui-palette-node"></div>').appendTo(entry);
entry.data('node',node.id);
nodeDiv.data('node',node.id);
var label = $('<div class="red-ui-palette-label"></div>').text(label).appendTo(nodeDiv);
var label = $('<div class="red-ui-palette-label"></div>').text(labelText).appendTo(nodeDiv);
if (node.d) {
nodeDiv.addClass("red-ui-palette-node-config-disabled");
$('<i class="fa fa-ban"></i>').prependTo(label);

View File

@@ -135,6 +135,10 @@ RED.sidebar.info.outliner = (function() {
RED.workspaces.show(n.id, null, true);
}
});
RED.popover.tooltip(toggleVisibleButton, function () {
var isHidden = !div.hasClass("red-ui-info-outline-item-hidden");
return RED._("sidebar.info." + (isHidden ? "hideFlow" : "showFlow"));
});
}
if (n.type !== 'subflow') {
var toggleButton = $('<button type="button" class="red-ui-info-outline-item-control-disable red-ui-button red-ui-button-small"><i class="fa fa-circle-thin"></i><i class="fa fa-ban"></i></button>').appendTo(controls).on("click",function(evt) {
@@ -633,6 +637,9 @@ RED.sidebar.info.outliner = (function() {
objects[n.id].children = missingParents[n.id];
delete missingParents[n.id]
}
if (objects[n.id].children.length === 0) {
objects[n.id].children.push(getEmptyItem(n.id));
}
}
var parent = n.g||n.z||"__global__";

View File

@@ -840,6 +840,64 @@ RED.view.tools = (function() {
}
}
function wireMultipleToNode() {
if (RED.workspaces.isActiveLocked()) {
return
}
var selection = RED.view.selection();
if (selection.nodes) {
if (selection.nodes.length > 1) {
var targetNode = selection.nodes[selection.nodes.length - 1];
if (targetNode.inputs === 0) {
return;
}
var i = 0;
var newLinks = [];
for (i = 0; i < selection.nodes.length - 1; i++) {
var sourceNode = selection.nodes[i];
if (sourceNode.outputs > 0) {
// Wire the first output to the target that has no link to the target yet.
// This allows for connecting all combinations of inputs/outputs.
// The user may then delete links quickly that aren't needed.
var sourceConnectedOutports = RED.nodes.filterLinks({
source: sourceNode,
target: targetNode
});
// Get outport indices that have no link yet
var sourceOutportIndices = Array.from({ length: sourceNode.outputs }, (_, i) => i);
var sourceConnectedOutportIndices = sourceConnectedOutports.map( x => x.sourcePort );
var sourceFreeOutportIndices = sourceOutportIndices.filter(x => !sourceConnectedOutportIndices.includes(x));
// Does an unconnected source port exist?
if (sourceFreeOutportIndices.length == 0) {
continue;
}
// Connect the first free outport to the target
var newLink = {
source: sourceNode,
target: targetNode,
sourcePort: sourceFreeOutportIndices[0]
}
RED.nodes.addLink(newLink);
newLinks.push(newLink);
}
}
if (newLinks.length > 0) {
RED.history.push({
t: 'add',
links: newLinks,
dirty: RED.nodes.dirty()
})
RED.nodes.dirty(true);
RED.view.redraw(true);
}
}
}
}
/**
* Splits selected wires and re-joins them with link-out+link-in
* @param {Object || Object[]} wires The wire(s) to split and replace with link-out, link-in nodes.
@@ -1133,7 +1191,8 @@ RED.view.tools = (function() {
w: 0, h: 0,
outputs: 1,
inputs: 1,
dirty: true
dirty: true,
moved: true
}
links = links.filter(function(l) { return !removedLinks.has(l) })
if (links.length === 0) {
@@ -1307,6 +1366,7 @@ RED.view.tools = (function() {
RED.actions.add("core:wire-series-of-nodes", function() { wireSeriesOfNodes() })
RED.actions.add("core:wire-node-to-multiple", function() { wireNodeToMultiple() })
RED.actions.add("core:wire-multiple-to-node", function() { wireMultipleToNode() })
RED.actions.add("core:split-wire-with-link-nodes", function () { splitWiresWithLinkNodes() });
RED.actions.add("core:split-wires-with-junctions", function () { addJunctionsToWires() });

View File

@@ -609,11 +609,28 @@ RED.view = (function() {
var group = $(ui.helper).data("group");
if (group) {
var oldX = group.x;
var oldY = group.y;
RED.group.addToGroup(group, nn);
var moveEvent = null;
if ((group.x !== oldX) ||
(group.y !== oldY)) {
moveEvent = {
t: "move",
nodes: [{n: group,
ox: oldX, oy: oldY,
dx: group.x -oldX,
dy: group.y -oldY}],
dirty: true
};
}
historyEvent = {
t: 'multi',
events: [historyEvent],
};
if (moveEvent) {
historyEvent.events.push(moveEvent)
}
historyEvent.events.push({
t: "addToGroup",
@@ -1250,7 +1267,8 @@ RED.view = (function() {
w: 0, h: 0,
outputs: 1,
inputs: 1,
dirty: true
dirty: true,
moved: true
}
historyEvent = {
t:'add',
@@ -1385,19 +1403,35 @@ RED.view = (function() {
RED.editor.validateNode(nn);
if (targetGroup) {
var oldX = targetGroup.x;
var oldY = targetGroup.y;
RED.group.addToGroup(targetGroup, nn);
var moveEvent = null;
if ((targetGroup.x !== oldX) ||
(targetGroup.y !== oldY)) {
moveEvent = {
t: "move",
nodes: [{n: targetGroup,
ox: oldX, oy: oldY,
dx: targetGroup.x -oldX,
dy: targetGroup.y -oldY}],
dirty: true
};
}
if (historyEvent.t !== "multi") {
historyEvent = {
t:'multi',
events: [historyEvent]
}
};
}
historyEvent.events.push({
t: "addToGroup",
group: targetGroup,
nodes: nn
})
});
if (moveEvent) {
historyEvent.events.push(moveEvent);
}
}
if (spliceLink) {
@@ -1735,6 +1769,7 @@ RED.view = (function() {
// Check link splice or group-add
if (movingSet.length() === 1 && movingSet.get(0).n.type !== "group") {
//}{//NIS
node = movingSet.get(0);
if (spliceActive) {
if (!spliceTimer) {
@@ -2094,11 +2129,25 @@ RED.view = (function() {
if (mouse_mode == RED.state.MOVING_ACTIVE) {
if (movingSet.length() > 0) {
var addedToGroup = null;
var moveEvent = null;
if (activeHoverGroup) {
var oldX = activeHoverGroup.x;
var oldY = activeHoverGroup.y;
for (var j=0;j<movingSet.length();j++) {
var n = movingSet.get(j);
RED.group.addToGroup(activeHoverGroup,n.n);
}
if ((activeHoverGroup.x !== oldX) ||
(activeHoverGroup.y !== oldY)) {
moveEvent = {
t: "move",
nodes: [{n: activeHoverGroup,
ox: oldX, oy: oldY,
dx: activeHoverGroup.x -oldX,
dy: activeHoverGroup.y -oldY}],
dirty: true
};
}
addedToGroup = activeHoverGroup;
activeHoverGroup.hovered = false;
@@ -2144,6 +2193,12 @@ RED.view = (function() {
historyEvent.addToGroup = addedToGroup;
}
RED.nodes.dirty(true);
if (moveEvent) {
historyEvent = {
t: "multi",
events: [moveEvent, historyEvent]
};
}
RED.history.push(historyEvent);
}
}
@@ -3253,7 +3308,7 @@ RED.view = (function() {
console.log("Definition error: "+node.type+"."+((portType === PORT_TYPE_INPUT)?"inputLabels":"outputLabels"),err);
result = null;
}
} else if ($.isArray(portLabels)) {
} else if (Array.isArray(portLabels)) {
result = portLabels[portIndex];
}
return result;
@@ -3513,11 +3568,25 @@ RED.view = (function() {
updateActiveNodes();
}
var moveEvent = null;
if (activeHoverGroup) {
var oldX = activeHoverGroup.x;
var oldY = activeHoverGroup.y;
for (var j=0;j<movingSet.length();j++) {
var n = movingSet.get(j);
RED.group.addToGroup(activeHoverGroup,n.n);
}
if ((activeHoverGroup.x !== oldX) ||
(activeHoverGroup.y !== oldY)) {
moveEvent = {
t: "move",
nodes: [{n: activeHoverGroup,
ox: oldX, oy: oldY,
dx: activeHoverGroup.x -oldX,
dy: activeHoverGroup.y -oldY}],
dirty: true
};
}
historyEvent.addedToGroup = activeHoverGroup;
activeHoverGroup.hovered = false;
@@ -3526,7 +3595,6 @@ RED.view = (function() {
activeGroup.selected = true;
activeHoverGroup = null;
}
if (mouse_mode == RED.state.DETACHED_DRAGGING) {
var ns = [];
for (var j=0;j<movingSet.length();j++) {
@@ -3537,7 +3605,15 @@ RED.view = (function() {
n.n.moved = true;
}
}
RED.history.replace({t:"multi",events:[historyEvent,{t:"move",nodes:ns}],dirty: historyEvent.dirty})
var event = {t:"multi",events:[historyEvent,{t:"move",nodes:ns}],dirty: historyEvent.dirty};
if (moveEvent) {
event.events.push(moveEvent);
}
RED.history.replace(event)
}
else if(moveEvent) {
var event = {t:"multi", events:[historyEvent, moveEvent], dirty: true};
RED.history.replace(event);
}
updateSelection();
@@ -5624,7 +5700,7 @@ RED.view = (function() {
if (mouse_mode === RED.state.SELECTING_NODE) {
return;
}
const wasDirty = RED.nodes.dirty()
var nodesToImport;
if (typeof newNodesObj === "string") {
if (newNodesObj === "") {
@@ -5641,7 +5717,7 @@ RED.view = (function() {
nodesToImport = newNodesObj;
}
if (!$.isArray(nodesToImport)) {
if (!Array.isArray(nodesToImport)) {
nodesToImport = [nodesToImport];
}
if (options.generateDefaultNames) {
@@ -5674,7 +5750,12 @@ RED.view = (function() {
return (n.type === "global-config");
});
}
var result = RED.nodes.import(filteredNodesToImport,{generateIds:options.generateIds, addFlow: addNewFlow, importMap: options.importMap});
var result = RED.nodes.import(filteredNodesToImport,{
generateIds: options.generateIds,
addFlow: addNewFlow,
importMap: options.importMap,
markChanged: true
});
if (result) {
var new_nodes = result.nodes;
var new_links = result.links;
@@ -5690,7 +5771,7 @@ RED.view = (function() {
var new_ms = new_nodes.filter(function(n) { return n.hasOwnProperty("x") && n.hasOwnProperty("y") && n.z == RED.workspaces.active() });
new_ms = new_ms.concat(new_groups.filter(function(g) { return g.z === RED.workspaces.active()}))
new_ms = new_ms.concat(new_junctions.filter(function(j) { return j.z === RED.workspaces.active()}))
var new_node_ids = new_nodes.map(function(n){ n.changed = true; return n.id; });
var new_node_ids = new_nodes.map(function(n){ return n.id; });
clearSelection();
movingSet.clear();
@@ -5770,14 +5851,14 @@ RED.view = (function() {
}
var historyEvent = {
t:"add",
nodes:new_node_ids,
links:new_links,
groups:new_groups,
t: "add",
nodes: new_node_ids,
links: new_links,
groups: new_groups,
junctions: new_junctions,
workspaces:new_workspaces,
subflows:new_subflows,
dirty:RED.nodes.dirty()
workspaces: new_workspaces,
subflows: new_subflows,
dirty: wasDirty
};
if (movingSet.length() === 0) {
RED.nodes.dirty(true);
@@ -5786,7 +5867,7 @@ RED.view = (function() {
var subflowRefresh = RED.subflow.refresh(true);
if (subflowRefresh) {
historyEvent.subflow = {
id:activeSubflow.id,
id: activeSubflow.id,
changed: activeSubflowChanged,
instances: subflowRefresh.instances
}

View File

@@ -82,8 +82,11 @@ RED.workspaces = (function() {
info: "",
label: RED._('workspace.defaultName',{number:workspaceIndex}),
env: [],
hideable: true
hideable: true,
};
if (!skipHistoryEntry) {
ws.added = true
}
RED.nodes.addWorkspace(ws,targetIndex);
workspace_tabs.addTab(ws,targetIndex);
@@ -93,8 +96,7 @@ RED.workspaces = (function() {
RED.nodes.dirty(true);
}
}
$("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label)
$("#red-ui-tab-"+(ws.id.replace(".","-"))).attr("flowname",ws.label).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added));
RED.view.focus();
return ws;
}
@@ -375,6 +377,12 @@ RED.workspaces = (function() {
$("#red-ui-tab-"+(tab.id.replace(".","-"))).addClass('red-ui-workspace-locked');
}
const changeBadgeContainer = $('<svg class="red-ui-flow-tab-changed red-ui-flow-node-changed" width="10" height="10" viewBox="-1 -1 12 12"></svg>').appendTo("#red-ui-tab-"+(tab.id.replace(".","-")))
const changeBadge = document.createElementNS("http://www.w3.org/2000/svg","circle");
changeBadge.setAttribute("cx",5);
changeBadge.setAttribute("cy",5);
changeBadge.setAttribute("r",5);
changeBadgeContainer.append(changeBadge)
RED.menu.setDisabled("menu-item-workspace-delete",activeWorkspace === 0 || workspaceTabCount <= 1);
if (workspaceTabCount === 1) {
@@ -637,6 +645,11 @@ RED.workspaces = (function() {
RED.workspaces.show(viewStack[++viewStackPos],true);
}
})
RED.events.on("flows:change", (ws) => {
$("#red-ui-tab-"+(ws.id.replace(".","-"))).toggleClass('red-ui-workspace-changed',!!(ws.contentsChanged || ws.changed || ws.added));
})
hideWorkspace();
}

View File

@@ -87,16 +87,18 @@
padding: 0px 8px;
height: 26px;
line-height: 26px;
&.toggle:not(.selected) {
&.toggle.selected {
color: var(--red-ui-workspace-button-color-selected) !important;
background: var(--red-ui-workspace-button-background-active);
background: var(--red-ui-workspace-button-background) !important;
}
}
.red-ui-tray-footer-left {
display:inline-block;
margin-right: 20px;
float:left;
& :not(:first-child) {
margin-left: 5px
}
}
.red-ui-tray-footer-right {
float: right;

View File

@@ -32,7 +32,7 @@
color: var(--red-ui-primary-text-color);
border: 1px solid var(--red-ui-notification-border-default);
border-left-width: 16px;
overflow: scroll;
overflow: auto;
max-height: 80vh;
.ui-dialog-buttonset {
margin-top: 20px;

View File

@@ -105,6 +105,15 @@
}
}
}
.red-ui-tab:not(.red-ui-workspace-changed) .red-ui-flow-tab-changed {
display: none;
}
.red-ui-tab.red-ui-workspace-changed .red-ui-flow-tab-changed {
display: inline-block;
position: absolute;
top: 1px;
right: 1px;
}
.red-ui-workspace-locked-icon {
display: none;

View File

@@ -17,7 +17,7 @@ export default {
"en-US": "Context Menu",
"ja": "コンテキストメニュー"
},
image: 'images/context-menu.png',
image: '3.0/images/context-menu.png',
description: {
"en-US": `<p>The editor now has its own context menu when you
right-click in the workspace.</p>
@@ -32,7 +32,7 @@ export default {
"en-US": "Wire Junctions",
"ja": "分岐点をワイヤーに追加"
},
image: 'images/junction-slice.gif',
image: '3.0/images/junction-slice.gif',
description: {
"en-US": `<p>To make it easier to route wires around your flows,
it is now possible to add junction nodes that give
@@ -48,7 +48,7 @@ export default {
"en-US": "Wire Junctions",
"ja": "分岐点をワイヤーに追加"
},
image: 'images/junction-quick-add.png',
image: '3.0/images/junction-quick-add.png',
description: {
"en-US": `<p>Junctions can also be added using the quick-add dialog.</p>
<p>The dialog is opened by holding the Ctrl (or Cmd) key when
@@ -62,7 +62,7 @@ export default {
"en-US": "Debug Path Tooltip",
"ja": "デバッグパスのツールチップ"
},
image: 'images/debug-path-tooltip.png',
image: '3.0/images/debug-path-tooltip.png',
description: {
"en-US": `<p>When hovering over a node name in the Debug sidebar, a
new tooltip shows the full location of the node.</p>
@@ -81,7 +81,7 @@ export default {
"en-US": "Continuous Search",
"ja": "連続した検索"
},
image: 'images/continuous-search.png',
image: '3.0/images/continuous-search.png',
description: {
"en-US": `<p>When searching for things in the editor, a new toolbar in
the workspace provides options to quickly jump between
@@ -94,7 +94,7 @@ export default {
"en-US": "New wiring actions",
"ja": "新しいワイヤー操作"
},
image: "images/split-wire-with-links.gif",
image: "3.0/images/split-wire-with-links.gif",
description: {
"en-US": `<p>A new action has been added that will replace a wire with a pair of connected Link nodes:</p>
<ul>

View File

@@ -5,17 +5,17 @@ export default {
titleIcon: "fa fa-map-o",
title: {
"en-US": "Welcome to Node-RED 3.1 Beta 1!",
// "ja": "Node-RED 3.1へようこそ!"
"ja": "Node-RED 3.1 ベータ1へようこそ!"
},
description: {
"en-US": "<p>This is the first beta release for 3.1.0 and we have a few new features to tell you about.</p>",
// "ja": ""
"ja": "<p>これは3.1.0の最初のベータリリースです。いくつかの新機能について説明します。</p>"
}
},
{
title: {
"en-US": "Improved Context Menu",
// "ja": ""
"ja": "コンテキストメニューの改善"
},
image: 'images/context-menu.png',
description: {
@@ -25,26 +25,29 @@ export default {
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": ``
"ja": `<p>より多くの組み込み動作を利用できるように、エディタのコンテキストメニューが拡張されました。</p>
<p>ノードの追加、グループの操作、その他の便利なツールをクリックするだけで実行できるようになりました。</p>
<p>フローのタブバーには、フローの操作をより簡単にする独自のコンテキストメニューもあります。</p>`
}
},
{
title: {
"en-US": "Hiding Flows",
// "ja": ""
"ja": "フローを非表示"
},
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": ``
"ja": `<p>フローを非表示にする機能は、フローのコンテキストメニューから実行するようになりました。</p>
<p>これまでのリリースでタブに存在していた「非表示」ボタンは、よく誤ってクリックされていたため、削除されました。</p>`
},
},
{
title: {
"en-US": "Locking Flows",
// "ja": ""
"ja": "フローを固定"
},
image: 'images/locking-flows.png',
description: {
@@ -52,44 +55,49 @@ export default {
<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": ``
"ja": `<p>誤ってフローに変更が加えられてしまうのを防ぐために、フローを固定できるようになりました。</p>
<p>固定されている時は、ノードを修正することはできません。</p>
<p>フローのコンテキストメニューと、情報サイドバーのエクスプローラには、フローの固定や解除をするためのオプションが用意されています。</p>`
},
},
{
title: {
"en-US": "Adding Images to node/flow descriptions",
// "ja": ""
"ja": "ノードやフローの説明へ画像を追加"
},
// 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.`,
// "ja": ``
<p>When the description is shown in the Info sidebar, the image will be displayed.</p>`,
"ja": `<p>ノードまたはフローの説明に、画像を追加できるようになりました。</p>
<p>画像をテキストエディタにドラッグするだけで、行内に埋め込まれます。</p>
<p>情報サイドバーの説明を開くと、その画像が表示されます。</p>`
},
},
{
title: {
"en-US": "Adding Mermaid Diagrams",
// "ja": ""
"ja": "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": ``
"ja": `<p>ノードやフローの説明に、<a href="https://github.com/mermaid-js/mermaid">Mermaid</a>の図を直接追加することもできます。</p>
<p>これによって、フローを説明する文書作成の選択肢がより多くなります。</p>`
},
},
{
title: {
"en-US": "Managing Global Environment Variables",
// "ja": ""
"ja": "グローバル環境変数の管理"
},
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": ``
"ja": `<p>ユーザ設定に新しく追加された「大域環境変数」のセクションで、全てのノードとフローに適用される環境変数を登録できます。</p>`
},
},
{
@@ -101,7 +109,7 @@ export default {
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": ``
"ja": `<p>コアノードにマイナーな修正、ドキュメント更新、小規模な拡張が数多く追加されています。全ての一覧は、ヘルプサイドバーの全ての更新履歴を確認してください。</p>`
}
}
]

View File

@@ -18,7 +18,16 @@
color:"#c0edc0",
defaults: {
name: {value:""},
scope: {value:[], type:"*[]"},
scope: {
value: [],
type: "*[]",
validate: function (v, opt) {
if (v.length > 0) {
return true;
}
return RED._("node-red:complete.errors.scopeUndefined");
}
},
uncaught: {value:false}
},
inputs:0,

View File

@@ -1,4 +1,3 @@
<script type="text/html" data-template-name="link in">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
@@ -272,7 +271,17 @@
color:"#ddd",//"#87D8CF",
defaults: {
name: { value: "" },
links: { value: [], type:"link in[]" },
links: {
value: [],
type: "link in[]",
validate: function (v, opt) {
if ((this.linkType === "static" && v.length > 0)
|| this.linkType === "dynamic") {
return true;
}
return RED._("node-red:link.errors.linkUndefined");
}
},
linkType: { value:"static" },
timeout: {
value: "30",

View File

@@ -164,10 +164,10 @@ module.exports = function(RED) {
if (returnNode && returnNode.returnLinkMessage) {
returnNode.returnLinkMessage(messageEvent.id, msg);
} else {
node.warn(RED._("link.error.missingReturn"))
node.warn(RED._("link.errors.missingReturn"));
}
} else {
node.warn(RED._("link.error.missingReturn"))
node.warn(RED._("link.errors.missingReturn"));
}
done();
} else if (mode === "link") {

View File

@@ -119,7 +119,10 @@
}
},
"complete": {
"completeNodes": "complete: __number__"
"completeNodes": "complete: __number__",
"errors": {
"scopeUndefined": "scope undefined"
}
},
"debug": {
"output": "Output",
@@ -181,8 +184,9 @@
"staticLinkCall": "Fixed target",
"dynamicLinkCall": "Dynamic target (msg.target)",
"dynamicLinkLabel": "Dynamic",
"error": {
"missingReturn": "Missing return node information"
"errors": {
"missingReturn": "Missing return node information",
"linkUndefined": "link undefined"
}
},
"tls": {

View File

@@ -119,7 +119,10 @@
}
},
"complete": {
"completeNodes": "complete: __number__"
"completeNodes": "complete: __number__",
"errors": {
"scopeUndefined": "スコープが未定義"
}
},
"debug": {
"output": "対象",
@@ -181,8 +184,9 @@
"staticLinkCall": "対象を固定で指定",
"dynamicLinkCall": "対象を動的に指定 (msg.target)",
"dynamicLinkLabel": "動的",
"error": {
"missingReturn": "返却するノードの情報が存在しません"
"errors": {
"missingReturn": "返却するノードの情報が存在しません",
"linkUndefined": "リンクが未定義"
}
},
"tls": {