@node-red/editor-api 5.0.0-beta.1 → 5.0.0-beta.3
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 +124 -97
- package/package.json +6 -6
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
|
@@ -31,7 +31,7 @@ var defaultContext = {
|
|
|
31
31
|
},
|
|
32
32
|
header: {
|
|
33
33
|
title: "Node-RED",
|
|
34
|
-
image: "red/images/node-red.svg"
|
|
34
|
+
image: "red/images/node-red-icon.svg"
|
|
35
35
|
},
|
|
36
36
|
asset: {
|
|
37
37
|
red: "red/red.min.js",
|
|
@@ -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,119 @@ 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
|
+
|
|
202
|
+
// These settings are not exposed under `editorTheme`, so we don't have a merge strategy for them
|
|
203
|
+
// If they're defined in the theme plugin, they replace any settings.js values.
|
|
204
|
+
// But, this direct manipulation of `theme` is not ideal and relies on mutating a passed-in object
|
|
205
|
+
theme.codeEditor = theme.codeEditor || {}
|
|
206
|
+
theme.codeEditor.options = Object.assign({}, themePlugin.monacoOptions, theme.codeEditor.options);
|
|
207
|
+
theme.mermaid = Object.assign({}, themePlugin.mermaid, theme.mermaid)
|
|
208
|
+
}
|
|
209
|
+
activeThemeInitialised = true;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
94
213
|
module.exports = {
|
|
95
214
|
init: function(_settings, _runtimeAPI) {
|
|
96
215
|
settings = _settings;
|
|
@@ -232,6 +351,7 @@ module.exports = {
|
|
|
232
351
|
res.json(themeContext);
|
|
233
352
|
})
|
|
234
353
|
|
|
354
|
+
// Copy the settings that need passing to the editor into themeSettings.
|
|
235
355
|
if (theme.hasOwnProperty("menu")) {
|
|
236
356
|
themeSettings.menu = theme.menu;
|
|
237
357
|
}
|
|
@@ -263,104 +383,11 @@ module.exports = {
|
|
|
263
383
|
return themeApp;
|
|
264
384
|
},
|
|
265
385
|
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
|
-
}
|
|
386
|
+
await loadThemePlugin();
|
|
361
387
|
return themeContext;
|
|
362
388
|
},
|
|
363
|
-
settings: function() {
|
|
389
|
+
settings: async function() {
|
|
390
|
+
await loadThemePlugin();
|
|
364
391
|
return themeSettings;
|
|
365
392
|
},
|
|
366
393
|
serveFile: function(baseUrl,file) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@node-red/editor-api",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"repository": {
|
|
@@ -16,17 +16,17 @@
|
|
|
16
16
|
}
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@node-red/util": "5.0.0-beta.
|
|
20
|
-
"@node-red/editor-client": "5.0.0-beta.
|
|
21
|
-
"bcryptjs": "3.0.
|
|
22
|
-
"body-parser": "1.20.
|
|
19
|
+
"@node-red/util": "5.0.0-beta.3",
|
|
20
|
+
"@node-red/editor-client": "5.0.0-beta.3",
|
|
21
|
+
"bcryptjs": "3.0.3",
|
|
22
|
+
"body-parser": "1.20.4",
|
|
23
23
|
"clone": "2.1.2",
|
|
24
24
|
"cors": "2.8.5",
|
|
25
25
|
"express-session": "1.18.2",
|
|
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",
|