@node-red/editor-api 4.1.5 → 4.1.7
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/lib/admin/settings.js +2 -2
- package/lib/editor/theme.js +127 -96
- package/package.json +5 -5
package/lib/admin/settings.js
CHANGED
|
@@ -53,10 +53,10 @@ module.exports = {
|
|
|
53
53
|
var opts = {
|
|
54
54
|
user: req.user
|
|
55
55
|
}
|
|
56
|
-
runtimeAPI.settings.getRuntimeSettings(opts).then(function(result) {
|
|
56
|
+
runtimeAPI.settings.getRuntimeSettings(opts).then(async function(result) {
|
|
57
57
|
if (!settings.disableEditor) {
|
|
58
58
|
result.editorTheme = result.editorTheme||{};
|
|
59
|
-
|
|
59
|
+
const themeSettings = await theme.settings();
|
|
60
60
|
if (themeSettings) {
|
|
61
61
|
// result.editorTheme may already exist with the palette
|
|
62
62
|
// disabled. Need to merge that into the receive settings
|
package/lib/editor/theme.js
CHANGED
|
@@ -42,7 +42,13 @@ var defaultContext = {
|
|
|
42
42
|
var settings;
|
|
43
43
|
|
|
44
44
|
var theme = null;
|
|
45
|
+
/**
|
|
46
|
+
* themeContext is an object passed to the mustache template to generate the editor index.html.
|
|
47
|
+
*/
|
|
45
48
|
var themeContext = clone(defaultContext);
|
|
49
|
+
/**
|
|
50
|
+
* themeSettings is an object passed to the editor client as the "editorTheme" property of the settings object
|
|
51
|
+
*/
|
|
46
52
|
var themeSettings = null;
|
|
47
53
|
|
|
48
54
|
var activeTheme = null;
|
|
@@ -91,6 +97,123 @@ function serveFilesFromTheme(themeValue, themeApp, directory, baseDirectory) {
|
|
|
91
97
|
return result
|
|
92
98
|
}
|
|
93
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Check if a theme is enabled and load its settings.
|
|
102
|
+
* This is done lazily as it has to happen after the plugins have been loaded, but before the editor is served.
|
|
103
|
+
*/
|
|
104
|
+
async function loadThemePlugin () {
|
|
105
|
+
if (activeTheme && !activeThemeInitialised) {
|
|
106
|
+
const themePlugin = await runtimeAPI.plugins.getPlugin({
|
|
107
|
+
id:activeTheme
|
|
108
|
+
});
|
|
109
|
+
if (themePlugin) {
|
|
110
|
+
if (themePlugin.css) {
|
|
111
|
+
const cssFiles = serveFilesFromTheme(
|
|
112
|
+
themePlugin.css,
|
|
113
|
+
themeApp,
|
|
114
|
+
"/css/",
|
|
115
|
+
themePlugin.path
|
|
116
|
+
);
|
|
117
|
+
themeContext.page.css = cssFiles.concat(themeContext.page.css || [])
|
|
118
|
+
// Mutating `theme` is not ideal, but currently necessary as debug (packages/node_modules/@node-red/nodes/core/common/21-debug.js)
|
|
119
|
+
// accesses RED.settings.editorTheme.page._.css directly to apply theme to the debug pop-out window.
|
|
120
|
+
theme.page = theme.page || {_:{}}
|
|
121
|
+
theme.page._.css = cssFiles.concat(theme.page._.css || [])
|
|
122
|
+
}
|
|
123
|
+
if (themePlugin.scripts) {
|
|
124
|
+
const scriptFiles = serveFilesFromTheme(
|
|
125
|
+
themePlugin.scripts,
|
|
126
|
+
themeApp,
|
|
127
|
+
"/scripts/",
|
|
128
|
+
themePlugin.path
|
|
129
|
+
)
|
|
130
|
+
themeContext.page.scripts = scriptFiles.concat(themeContext.page.scripts || [])
|
|
131
|
+
theme.page = theme.page || {_:{}}
|
|
132
|
+
theme.page._.scripts = scriptFiles.concat(theme.page._.scripts || [])
|
|
133
|
+
}
|
|
134
|
+
// check and load page settings from theme
|
|
135
|
+
if (themePlugin.page) {
|
|
136
|
+
if (themePlugin.page.favicon && !theme.page.favicon) {
|
|
137
|
+
const result = serveFilesFromTheme(
|
|
138
|
+
[themePlugin.page.favicon],
|
|
139
|
+
themeApp,
|
|
140
|
+
"/",
|
|
141
|
+
themePlugin.path
|
|
142
|
+
)
|
|
143
|
+
if(result && result.length > 0) {
|
|
144
|
+
// update themeContext page favicon
|
|
145
|
+
themeContext.page.favicon = result[0]
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (themePlugin.page.tabicon && themePlugin.page.tabicon.icon && !theme.page.tabicon) {
|
|
149
|
+
const result = serveFilesFromTheme(
|
|
150
|
+
[themePlugin.page.tabicon.icon],
|
|
151
|
+
themeApp,
|
|
152
|
+
"/page/",
|
|
153
|
+
themePlugin.path
|
|
154
|
+
)
|
|
155
|
+
if(result && result.length > 0) {
|
|
156
|
+
// update themeContext page tabicon
|
|
157
|
+
themeContext.page.tabicon.icon = result[0]
|
|
158
|
+
themeContext.page.tabicon.colour = themeContext.page.tabicon.colour || themeContext.page.tabicon.colour
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// if the plugin has a title AND the users settings.js does NOT
|
|
162
|
+
if (themePlugin.page.title && !theme.page.title) {
|
|
163
|
+
themeContext.page.title = themePlugin.page.title || themeContext.page.title
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// check and load header settings from theme
|
|
167
|
+
if (themePlugin.header) {
|
|
168
|
+
if (themePlugin.header.image && !theme.header.image) {
|
|
169
|
+
const result = serveFilesFromTheme(
|
|
170
|
+
[themePlugin.header.image],
|
|
171
|
+
themeApp,
|
|
172
|
+
"/header/",
|
|
173
|
+
themePlugin.path
|
|
174
|
+
)
|
|
175
|
+
if(result && result.length > 0) {
|
|
176
|
+
// update themeContext header image
|
|
177
|
+
themeContext.header.image = result[0]
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// if the plugin has a title AND the users settings.js does NOT have a title
|
|
181
|
+
if (themePlugin.header.title && !theme.header.title) {
|
|
182
|
+
themeContext.header.title = themePlugin.header.title || themeContext.header.title
|
|
183
|
+
}
|
|
184
|
+
// if the plugin has a header url AND the users settings.js does NOT
|
|
185
|
+
if (themePlugin.header.url && !theme.header.url) {
|
|
186
|
+
themeContext.header.url = themePlugin.header.url || themeContext.header.url
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (Array.isArray(themePlugin.palette?.theme)) {
|
|
191
|
+
themeSettings.palette = themeSettings.palette || {};
|
|
192
|
+
themeSettings.palette.theme = themePlugin.palette.theme;
|
|
193
|
+
// The theme is providing its own palette theme. It *might* include icons that need namespacing
|
|
194
|
+
// to the theme plugin module.
|
|
195
|
+
themePlugin.palette.theme.forEach(themeRule => {
|
|
196
|
+
if (themeRule.icon && themeRule.icon.indexOf("/") === -1) {
|
|
197
|
+
themeRule.icon = `${themePlugin.module}/${themeRule.icon}`;
|
|
198
|
+
}
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
if (Array.isArray(themePlugin.palette?.categories)) {
|
|
202
|
+
themeSettings.palette = themeSettings.palette || {};
|
|
203
|
+
themeSettings.palette.categories = themePlugin.palette.categories;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// These settings are not exposed under `editorTheme`, so we don't have a merge strategy for them
|
|
207
|
+
// If they're defined in the theme plugin, they replace any settings.js values.
|
|
208
|
+
// But, this direct manipulation of `theme` is not ideal and relies on mutating a passed-in object
|
|
209
|
+
theme.codeEditor = theme.codeEditor || {}
|
|
210
|
+
theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options);
|
|
211
|
+
theme.mermaid = Object.assign({}, themePlugin.mermaid, theme.mermaid)
|
|
212
|
+
}
|
|
213
|
+
activeThemeInitialised = true;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
94
217
|
module.exports = {
|
|
95
218
|
init: function(_settings, _runtimeAPI) {
|
|
96
219
|
settings = _settings;
|
|
@@ -232,6 +355,7 @@ module.exports = {
|
|
|
232
355
|
res.json(themeContext);
|
|
233
356
|
})
|
|
234
357
|
|
|
358
|
+
// Copy the settings that need passing to the editor into themeSettings.
|
|
235
359
|
if (theme.hasOwnProperty("menu")) {
|
|
236
360
|
themeSettings.menu = theme.menu;
|
|
237
361
|
}
|
|
@@ -263,104 +387,11 @@ module.exports = {
|
|
|
263
387
|
return themeApp;
|
|
264
388
|
},
|
|
265
389
|
context: async function() {
|
|
266
|
-
|
|
267
|
-
const themePlugin = await runtimeAPI.plugins.getPlugin({
|
|
268
|
-
id:activeTheme
|
|
269
|
-
});
|
|
270
|
-
if (themePlugin) {
|
|
271
|
-
if (themePlugin.css) {
|
|
272
|
-
const cssFiles = serveFilesFromTheme(
|
|
273
|
-
themePlugin.css,
|
|
274
|
-
themeApp,
|
|
275
|
-
"/css/",
|
|
276
|
-
themePlugin.path
|
|
277
|
-
);
|
|
278
|
-
themeContext.page.css = cssFiles.concat(themeContext.page.css || [])
|
|
279
|
-
theme.page = theme.page || {_:{}}
|
|
280
|
-
theme.page._.css = cssFiles.concat(theme.page._.css || [])
|
|
281
|
-
}
|
|
282
|
-
if (themePlugin.scripts) {
|
|
283
|
-
const scriptFiles = serveFilesFromTheme(
|
|
284
|
-
themePlugin.scripts,
|
|
285
|
-
themeApp,
|
|
286
|
-
"/scripts/",
|
|
287
|
-
themePlugin.path
|
|
288
|
-
)
|
|
289
|
-
themeContext.page.scripts = scriptFiles.concat(themeContext.page.scripts || [])
|
|
290
|
-
theme.page = theme.page || {_:{}}
|
|
291
|
-
theme.page._.scripts = scriptFiles.concat(theme.page._.scripts || [])
|
|
292
|
-
}
|
|
293
|
-
// check and load page settings from theme
|
|
294
|
-
if (themePlugin.page) {
|
|
295
|
-
if (themePlugin.page.favicon && !theme.page.favicon) {
|
|
296
|
-
const result = serveFilesFromTheme(
|
|
297
|
-
[themePlugin.page.favicon],
|
|
298
|
-
themeApp,
|
|
299
|
-
"/",
|
|
300
|
-
themePlugin.path
|
|
301
|
-
)
|
|
302
|
-
if(result && result.length > 0) {
|
|
303
|
-
// update themeContext page favicon
|
|
304
|
-
themeContext.page.favicon = result[0]
|
|
305
|
-
theme.page = theme.page || {_:{}}
|
|
306
|
-
theme.page._.favicon = result[0]
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
if (themePlugin.page.tabicon && themePlugin.page.tabicon.icon && !theme.page.tabicon) {
|
|
310
|
-
const result = serveFilesFromTheme(
|
|
311
|
-
[themePlugin.page.tabicon.icon],
|
|
312
|
-
themeApp,
|
|
313
|
-
"/page/",
|
|
314
|
-
themePlugin.path
|
|
315
|
-
)
|
|
316
|
-
if(result && result.length > 0) {
|
|
317
|
-
// update themeContext page tabicon
|
|
318
|
-
themeContext.page.tabicon.icon = result[0]
|
|
319
|
-
themeContext.page.tabicon.colour = themeContext.page.tabicon.colour || themeContext.page.tabicon.colour
|
|
320
|
-
theme.page = theme.page || {_:{}}
|
|
321
|
-
theme.page._.tabicon = theme.page._.tabicon || {}
|
|
322
|
-
theme.page._.tabicon.icon = themeContext.page.tabicon.icon
|
|
323
|
-
theme.page._.tabicon.colour = themeContext.page.tabicon.colour
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
// if the plugin has a title AND the users settings.js does NOT
|
|
327
|
-
if (themePlugin.page.title && !theme.page.title) {
|
|
328
|
-
themeContext.page.title = themePlugin.page.title || themeContext.page.title
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
// check and load header settings from theme
|
|
332
|
-
if (themePlugin.header) {
|
|
333
|
-
if (themePlugin.header.image && !theme.header.image) {
|
|
334
|
-
const result = serveFilesFromTheme(
|
|
335
|
-
[themePlugin.header.image],
|
|
336
|
-
themeApp,
|
|
337
|
-
"/header/",
|
|
338
|
-
themePlugin.path
|
|
339
|
-
)
|
|
340
|
-
if(result && result.length > 0) {
|
|
341
|
-
// update themeContext header image
|
|
342
|
-
themeContext.header.image = result[0]
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
// if the plugin has a title AND the users settings.js does NOT have a title
|
|
346
|
-
if (themePlugin.header.title && !theme.header.title) {
|
|
347
|
-
themeContext.header.title = themePlugin.header.title || themeContext.header.title
|
|
348
|
-
}
|
|
349
|
-
// if the plugin has a header url AND the users settings.js does NOT
|
|
350
|
-
if (themePlugin.header.url && !theme.header.url) {
|
|
351
|
-
themeContext.header.url = themePlugin.header.url || themeContext.header.url
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
theme.codeEditor = theme.codeEditor || {}
|
|
355
|
-
theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options);
|
|
356
|
-
|
|
357
|
-
theme.mermaid = Object.assign({}, themePlugin.mermaid, theme.mermaid)
|
|
358
|
-
}
|
|
359
|
-
activeThemeInitialised = true;
|
|
360
|
-
}
|
|
390
|
+
await loadThemePlugin();
|
|
361
391
|
return themeContext;
|
|
362
392
|
},
|
|
363
|
-
settings: function() {
|
|
393
|
+
settings: async function() {
|
|
394
|
+
await loadThemePlugin();
|
|
364
395
|
return themeSettings;
|
|
365
396
|
},
|
|
366
397
|
serveFile: function(baseUrl,file) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node-red/editor-api",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.7",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
}
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@node-red/util": "4.1.
|
|
20
|
-
"@node-red/editor-client": "4.1.
|
|
21
|
-
"bcryptjs": "3.0.
|
|
19
|
+
"@node-red/util": "4.1.7",
|
|
20
|
+
"@node-red/editor-client": "4.1.7",
|
|
21
|
+
"bcryptjs": "3.0.3",
|
|
22
22
|
"body-parser": "1.20.4",
|
|
23
23
|
"clone": "2.1.2",
|
|
24
24
|
"cors": "2.8.5",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"express": "4.22.1",
|
|
27
27
|
"memorystore": "1.6.7",
|
|
28
28
|
"mime": "3.0.0",
|
|
29
|
-
"multer": "2.
|
|
29
|
+
"multer": "2.1.1",
|
|
30
30
|
"mustache": "4.2.0",
|
|
31
31
|
"oauth2orize": "1.12.0",
|
|
32
32
|
"passport-http-bearer": "1.0.1",
|