node-red-contrib-markdown-note 1.0.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.
- package/LICENSE +21 -0
- package/README.md +53 -0
- package/markdown.html +361 -0
- package/markdown.js +7 -0
- package/package.json +40 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ted Lanham
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Node-RED Markdown Note
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/node-red-contrib-markdown-note)
|
|
4
|
+
[](https://www.npmjs.com/package/node-red-contrib-markdown-note)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
A Node-RED node for adding **Markdown-formatted notes** directly on the flow canvas.
|
|
8
|
+
Designed for inline documentation, design notes, and contextual explanations that remain visible while editing or reviewing flows.
|
|
9
|
+
|
|
10
|
+

|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Why use Markdown Note?
|
|
15
|
+
|
|
16
|
+
The standard Comment node hides content by default. Markdown Note keeps your notes **always visible**, making it easier to:
|
|
17
|
+
|
|
18
|
+
- Document flow logic inline
|
|
19
|
+
- Highlight important information
|
|
20
|
+
- Include structured content with headings, lists, and code blocks
|
|
21
|
+
|
|
22
|
+
| Feature | Comment Node | Markdown Note |
|
|
23
|
+
|---------|-------------|---------------|
|
|
24
|
+
| Visibility | Collapsed by default | Always visible |
|
|
25
|
+
| Formatting | Plain text | Markdown (headers, lists, code, blockquotes) |
|
|
26
|
+
| Structure | Minimal | Suitable for detailed documentation |
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- **Always-visible notes** – No need to click to expand.
|
|
33
|
+
- **Markdown rendering** – Support for headers, lists, emphasis, code blocks, quotes.
|
|
34
|
+
- **Task lists** – Track TODOs or action items inline.
|
|
35
|
+
- **Resizable & readable layout** – Automatically adjusts to content size.
|
|
36
|
+
- **Developer-focused** – Document payload formats, API contracts, assumptions, or edge cases directly on the flow.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## Known Issues
|
|
42
|
+
|
|
43
|
+
- **Links are not clickable**: Due to Node-RED's security restrictions on SVG content in the flow editor, hyperlinks rendered in the markdown cannot be clicked.
|
|
44
|
+
- **Visual jump on edit**: When closing the edit dialog, the node may appear slightly displaced or "jump" until you click away (deselect the node). This is a rendering artifact of the flow editor's redraw cycle.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Installation
|
|
49
|
+
|
|
50
|
+
Run this in your Node-RED user directory (`~/.node-red`):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install node-red-contrib-markdown-note
|
package/markdown.html
ADDED
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType("note", {
|
|
3
|
+
category: "common",
|
|
4
|
+
color: "#F3E5AB",
|
|
5
|
+
defaults: {
|
|
6
|
+
content: { value: "" },
|
|
7
|
+
},
|
|
8
|
+
inputs: 0,
|
|
9
|
+
outputs: 0,
|
|
10
|
+
icon: "font-awesome/fa-file-text-o",
|
|
11
|
+
label: function () {
|
|
12
|
+
// Convert actual newlines to literal \n that Node-RED interprets as line breaks
|
|
13
|
+
if (this.content) {
|
|
14
|
+
return this.content.replace(/\n/g, "\\n ");
|
|
15
|
+
}
|
|
16
|
+
return "note";
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
oneditprepare: function () {
|
|
20
|
+
var content = this.content || "";
|
|
21
|
+
|
|
22
|
+
// Capture top-left anchor position before editing
|
|
23
|
+
// This ensures the node stays in place after edits
|
|
24
|
+
if (
|
|
25
|
+
this._noteTopY === undefined &&
|
|
26
|
+
this.y !== undefined &&
|
|
27
|
+
this.h !== undefined
|
|
28
|
+
) {
|
|
29
|
+
this._noteTopY = this.y - this.h / 2;
|
|
30
|
+
this._noteLastY = this.y;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Create the ACE editor with markdown mode
|
|
34
|
+
this.editor = RED.editor.createEditor({
|
|
35
|
+
id: "node-input-content-editor",
|
|
36
|
+
mode: "ace/mode/markdown",
|
|
37
|
+
value: content,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
this.editor.focus();
|
|
41
|
+
},
|
|
42
|
+
oneditsave: function () {
|
|
43
|
+
this.content = this.editor.getValue();
|
|
44
|
+
$("#node-input-content").val(this.content);
|
|
45
|
+
this.editor.destroy();
|
|
46
|
+
delete this.editor;
|
|
47
|
+
|
|
48
|
+
// Reposition node after Node-RED finishes its layout
|
|
49
|
+
var node = this;
|
|
50
|
+
if (node._noteTopY !== undefined) {
|
|
51
|
+
setTimeout(function () {
|
|
52
|
+
// Re-render to get correct height
|
|
53
|
+
renderNoteNode(node);
|
|
54
|
+
|
|
55
|
+
// Anchor top edge
|
|
56
|
+
if (node.h !== undefined) {
|
|
57
|
+
node.y = node._noteTopY + node.h / 2;
|
|
58
|
+
node._noteLastY = node.y;
|
|
59
|
+
RED.view.redraw(true);
|
|
60
|
+
}
|
|
61
|
+
}, 0);
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
oneditcancel: function () {
|
|
65
|
+
this.editor.destroy();
|
|
66
|
+
delete this.editor;
|
|
67
|
+
},
|
|
68
|
+
oneditresize: function (size) {
|
|
69
|
+
var rows = $("#dialog-form>div:not(.node-text-editor-row)");
|
|
70
|
+
var height = $("#dialog-form").height();
|
|
71
|
+
for (var i = 0; i < rows.length; i++) {
|
|
72
|
+
height -= $(rows[i]).outerHeight(true);
|
|
73
|
+
}
|
|
74
|
+
var editorRow = $("#dialog-form>div.node-text-editor-row");
|
|
75
|
+
height -=
|
|
76
|
+
parseInt(editorRow.css("marginTop")) +
|
|
77
|
+
parseInt(editorRow.css("marginBottom"));
|
|
78
|
+
$(".node-text-editor").css("height", height + "px");
|
|
79
|
+
this.editor.resize();
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Render markdown content inside nodes on the workspace
|
|
84
|
+
RED.events.on("nodes:add", function (node) {
|
|
85
|
+
if (node.type === "note") {
|
|
86
|
+
setTimeout(function () {
|
|
87
|
+
renderNoteNode(node);
|
|
88
|
+
}, 100);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
RED.events.on("nodes:change", function (node) {
|
|
93
|
+
if (node.type === "note") {
|
|
94
|
+
// Delay to allow Node-RED to recalculate size
|
|
95
|
+
setTimeout(function () {
|
|
96
|
+
renderNoteNode(node);
|
|
97
|
+
}, 100);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
RED.events.on("view:redraw", function () {
|
|
102
|
+
// Single redraw after view settles
|
|
103
|
+
setTimeout(function () {
|
|
104
|
+
RED.nodes.eachNode(function (node) {
|
|
105
|
+
if (node.type === "note") {
|
|
106
|
+
renderNoteNode(node);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}, 100);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
function renderNoteNode(node) {
|
|
113
|
+
var nodeEl = document.getElementById(node.id);
|
|
114
|
+
if (!nodeEl) return;
|
|
115
|
+
|
|
116
|
+
var g = d3.select(nodeEl);
|
|
117
|
+
|
|
118
|
+
// Clear any previous custom content FIRST
|
|
119
|
+
g.selectAll(".note-overlay").remove();
|
|
120
|
+
|
|
121
|
+
// CSS-BASED HIDING: Apply class to parent group
|
|
122
|
+
g.classed("note-hidden-content", true);
|
|
123
|
+
|
|
124
|
+
// Get the node's main rect to determine size
|
|
125
|
+
var rect = g.select("rect.node");
|
|
126
|
+
// Fallback if class .node doesn't exist
|
|
127
|
+
if (rect.empty()) rect = g.select("rect");
|
|
128
|
+
if (rect.empty()) return;
|
|
129
|
+
|
|
130
|
+
var bbox = rect.node().getBBox();
|
|
131
|
+
var w = bbox.width;
|
|
132
|
+
var h = bbox.height;
|
|
133
|
+
|
|
134
|
+
// INSET: Render overlay slightly smaller than rect to reveal the border stroke
|
|
135
|
+
// Node-RED standard nodes have rounded corners.
|
|
136
|
+
// By insetting 1px, we sit "inside" the stroke.
|
|
137
|
+
var inset = 1;
|
|
138
|
+
|
|
139
|
+
// Pre-process content: convert single newlines to markdown line breaks
|
|
140
|
+
// In markdown, single \n doesn't create a line break - need " \n" (two spaces + newline)
|
|
141
|
+
//
|
|
142
|
+
// We also need to preserve blank lines intentionally entered.
|
|
143
|
+
// Standard markdown often collapses consecutive blank lines.
|
|
144
|
+
// We replace empty or whitespace-only lines with a Non-Breaking Space (\u00A0).
|
|
145
|
+
// This forces the renderer to draw a line of height, which in turn
|
|
146
|
+
// allows the node to resize correctly for "visual" blank space.
|
|
147
|
+
|
|
148
|
+
var rawContent = node.content || "";
|
|
149
|
+
|
|
150
|
+
// Sanitize FIRST so we don't accidentally escape our injection or leave an opening for injection
|
|
151
|
+
var sanitized = RED.utils.sanitize(rawContent);
|
|
152
|
+
|
|
153
|
+
// Split into lines
|
|
154
|
+
var lines = sanitized.split("\n");
|
|
155
|
+
|
|
156
|
+
// Process each line: if empty/whitespace, replace with (unicode \u00A0)
|
|
157
|
+
var processedLines = lines.map(function (line) {
|
|
158
|
+
if (!line.trim()) {
|
|
159
|
+
return "\u00A0";
|
|
160
|
+
}
|
|
161
|
+
return line;
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Join back with markdown hard break (space space newline)
|
|
165
|
+
var finalMarkdown = processedLines.join(" \n");
|
|
166
|
+
|
|
167
|
+
// Render markdown to HTML
|
|
168
|
+
var html = RED.utils.renderMarkdown(finalMarkdown);
|
|
169
|
+
|
|
170
|
+
// Create foreignObject to hold the HTML content
|
|
171
|
+
var fo = g
|
|
172
|
+
.append("foreignObject")
|
|
173
|
+
.attr("class", "note-overlay")
|
|
174
|
+
.attr("x", inset)
|
|
175
|
+
.attr("y", inset)
|
|
176
|
+
.attr("width", w - inset * 2)
|
|
177
|
+
.attr("height", h - inset * 2)
|
|
178
|
+
.attr("pointer-events", "none");
|
|
179
|
+
|
|
180
|
+
var contentDiv = fo
|
|
181
|
+
.append("xhtml:div")
|
|
182
|
+
.attr("class", "note-node-content")
|
|
183
|
+
.attr("xmlns", "http://www.w3.org/1999/xhtml")
|
|
184
|
+
.style("width", w - inset * 2 + "px")
|
|
185
|
+
.style("overflow", "hidden")
|
|
186
|
+
.html(html);
|
|
187
|
+
|
|
188
|
+
// Measure actual content height and resize node to fit
|
|
189
|
+
var contentNode = contentDiv.node();
|
|
190
|
+
if (contentNode) {
|
|
191
|
+
// Minimum height of one line (14px font * 1.5 line-height + padding)
|
|
192
|
+
var minHeight = 28;
|
|
193
|
+
var actualHeight = Math.max(
|
|
194
|
+
contentNode.scrollHeight + inset * 2,
|
|
195
|
+
minHeight,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
// Use the smaller of Node-RED's allocation or our measured height
|
|
199
|
+
// SNAP TO GRID: Round up to nearest 40px (double grid size)
|
|
200
|
+
// Node-RED nodes align to center.
|
|
201
|
+
// Height 20px (1 unit) -> top edge is at -10px (mid-grid).
|
|
202
|
+
// Height 40px (2 units) -> top edge is at -20px (on-grid).
|
|
203
|
+
// To ensure consistent edge alignment, we enforce 40px increments.
|
|
204
|
+
var snappedHeight = Math.ceil(actualHeight / 40) * 40;
|
|
205
|
+
|
|
206
|
+
// We use snappedHeight directly to allow expansion beyond initial 'h'
|
|
207
|
+
var finalHeight = Math.max(snappedHeight, Math.ceil(minHeight / 40) * 40);
|
|
208
|
+
|
|
209
|
+
// Track top edge position to anchor the node
|
|
210
|
+
// If this is first render OR user has moved the node, recalculate anchor
|
|
211
|
+
var currentTopY = node.y - h / 2;
|
|
212
|
+
if (node._noteTopY === undefined || node._noteLastY === undefined) {
|
|
213
|
+
// First time: store current top edge as anchor
|
|
214
|
+
node._noteTopY = currentTopY;
|
|
215
|
+
} else if (Math.abs(node.y - node._noteLastY) > 1) {
|
|
216
|
+
// User moved the node - update anchor
|
|
217
|
+
node._noteTopY = currentTopY;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Set y so top edge stays at _noteTopY
|
|
221
|
+
var newY = node._noteTopY + finalHeight / 2;
|
|
222
|
+
node.y = newY;
|
|
223
|
+
node._noteLastY = newY;
|
|
224
|
+
|
|
225
|
+
rect.attr("height", finalHeight);
|
|
226
|
+
fo.attr("height", finalHeight - inset * 2);
|
|
227
|
+
contentDiv.style("height", finalHeight - inset * 2 + "px");
|
|
228
|
+
|
|
229
|
+
// Update Node-RED's internal dimensions
|
|
230
|
+
node.h = finalHeight;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
</script>
|
|
234
|
+
|
|
235
|
+
<script type="text/html" data-template-name="note">
|
|
236
|
+
<div class="form-row node-text-editor-row">
|
|
237
|
+
<input type="hidden" id="node-input-content" />
|
|
238
|
+
<div
|
|
239
|
+
style="height: 250px; min-height:150px;"
|
|
240
|
+
class="node-text-editor"
|
|
241
|
+
id="node-input-content-editor"
|
|
242
|
+
></div>
|
|
243
|
+
</div>
|
|
244
|
+
</script>
|
|
245
|
+
|
|
246
|
+
<script type="text/html" data-help-name="note">
|
|
247
|
+
<p>A node for displaying markdown-formatted notes in your flow.</p>
|
|
248
|
+
<h3>Usage</h3>
|
|
249
|
+
<p>
|
|
250
|
+
Use this node to add documentation, instructions, or annotations directly in
|
|
251
|
+
your Node-RED flows.
|
|
252
|
+
</p>
|
|
253
|
+
<p>
|
|
254
|
+
Double-click to edit. Use the toolbar buttons or type markdown syntax
|
|
255
|
+
directly:
|
|
256
|
+
</p>
|
|
257
|
+
<ul>
|
|
258
|
+
<li><b>Headers:</b> # H1, ## H2, ### H3</li>
|
|
259
|
+
<li><b>Bold:</b> **text**</li>
|
|
260
|
+
<li><b>Italic:</b> _text_</li>
|
|
261
|
+
<li><b>Code:</b> `code`</li>
|
|
262
|
+
<li><b>Lists:</b> - item or 1. item</li>
|
|
263
|
+
<li><b>Links:</b> [text](url)</li>
|
|
264
|
+
<li><b>Quote:</b> > text</li>
|
|
265
|
+
</ul>
|
|
266
|
+
|
|
267
|
+
</script>
|
|
268
|
+
|
|
269
|
+
<style>
|
|
270
|
+
/* Markdown content rendered inside the node */
|
|
271
|
+
.note-node-content {
|
|
272
|
+
font-size: 14px;
|
|
273
|
+
line-height: 1.5;
|
|
274
|
+
color: #333;
|
|
275
|
+
font-family:
|
|
276
|
+
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
277
|
+
padding: 0 8px;
|
|
278
|
+
background: #f3e5ab;
|
|
279
|
+
border-radius: 4px;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.note-node-content h1 {
|
|
283
|
+
font-size: 18px;
|
|
284
|
+
font-weight: bold;
|
|
285
|
+
margin: 0 0 8px 0;
|
|
286
|
+
border-bottom: 1px solid #ccc;
|
|
287
|
+
padding-bottom: 4px;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.note-node-content h2 {
|
|
291
|
+
font-size: 16px;
|
|
292
|
+
font-weight: bold;
|
|
293
|
+
margin: 8px 0 6px 0;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.note-node-content h3 {
|
|
297
|
+
font-size: 15px;
|
|
298
|
+
font-weight: bold;
|
|
299
|
+
margin: 6px 0 4px 0;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.note-node-content p {
|
|
303
|
+
margin: 4px 0;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.note-node-content ul,
|
|
307
|
+
.note-node-content ol {
|
|
308
|
+
margin: 4px 0;
|
|
309
|
+
padding-left: 18px;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.note-node-content li {
|
|
313
|
+
margin: 2px 0;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.note-node-content code {
|
|
317
|
+
background: rgba(0, 0, 0, 0.08);
|
|
318
|
+
padding: 2px 5px;
|
|
319
|
+
border-radius: 2px;
|
|
320
|
+
font-family: monospace;
|
|
321
|
+
font-size: 13px;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.note-node-content pre {
|
|
325
|
+
background: rgba(0, 0, 0, 0.08);
|
|
326
|
+
padding: 3px;
|
|
327
|
+
border-radius: 2px;
|
|
328
|
+
overflow-x: auto;
|
|
329
|
+
margin: 2px 0;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
.note-node-content pre code {
|
|
333
|
+
background: none;
|
|
334
|
+
padding: 0;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.note-node-content a {
|
|
338
|
+
color: #0066cc;
|
|
339
|
+
text-decoration: underline;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.note-node-content blockquote {
|
|
343
|
+
border-left: 2px solid #ccc;
|
|
344
|
+
margin: 2px 0;
|
|
345
|
+
padding-left: 6px;
|
|
346
|
+
color: #666;
|
|
347
|
+
font-style: italic;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/*
|
|
351
|
+
CSS Hiding Logic for Sticky Note
|
|
352
|
+
Hide Node-RED's default label text, icons, etc.
|
|
353
|
+
*/
|
|
354
|
+
g.note-hidden-content text,
|
|
355
|
+
g.note-hidden-content .node_icon_group,
|
|
356
|
+
g.note-hidden-content .node_icon,
|
|
357
|
+
g.note-hidden-content image {
|
|
358
|
+
display: none !important;
|
|
359
|
+
visibility: hidden !important;
|
|
360
|
+
}
|
|
361
|
+
</style>
|
package/markdown.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "node-red-contrib-markdown-note",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A Node-RED node for displaying markdown notes as annotations",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"node-red",
|
|
7
|
+
"markdown",
|
|
8
|
+
"note",
|
|
9
|
+
"comment",
|
|
10
|
+
"annotation",
|
|
11
|
+
"documentation"
|
|
12
|
+
],
|
|
13
|
+
"author": "Ted Lanham <tedlanham@gmail.com>",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"homepage": "https://github.com/Backroads4Me/node-red-contrib-markdown-note",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/Backroads4Me/node-red-contrib-markdown-note/issues"
|
|
18
|
+
},
|
|
19
|
+
"main": "markdown.js",
|
|
20
|
+
"files": [
|
|
21
|
+
"markdown.js",
|
|
22
|
+
"markdown.html",
|
|
23
|
+
"package.json",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"node-red": {
|
|
28
|
+
"version": ">=3.0.0",
|
|
29
|
+
"nodes": {
|
|
30
|
+
"note": "markdown.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/Backroads4Me/node-red-contrib-markdown-note.git"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=14.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|