@node-red/editor-api 5.0.0-beta.2 → 5.0.0-beta.4
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 +138 -99
- 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;
|
|
@@ -102,9 +225,17 @@ module.exports = {
|
|
|
102
225
|
}
|
|
103
226
|
themeSettings = null;
|
|
104
227
|
theme = settings.editorTheme || {};
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
228
|
+
theme.codeEditor = theme.codeEditor || {}
|
|
229
|
+
theme.codeEditor.lib = theme.codeEditor.lib || 'monaco' // default to monaco if no code editor lib specified
|
|
230
|
+
themeContext.asset.vendorAce = ''
|
|
231
|
+
themeContext.asset.vendorMonaco = ''
|
|
232
|
+
if (theme.codeEditor.lib === 'monaco') {
|
|
233
|
+
themeContext.asset.vendorMonaco = "vendor/monaco/monaco-bootstrap.js"
|
|
234
|
+
} else if (theme.codeEditor.lib === 'ace') {
|
|
235
|
+
themeContext.asset.vendorAce = 'vendor/ace/ace-bootstrap.js'
|
|
236
|
+
} else {
|
|
237
|
+
// default to basic if no valid code editor lib specified
|
|
238
|
+
theme.codeEditor.lib = 'basic'
|
|
108
239
|
}
|
|
109
240
|
activeTheme = theme.theme;
|
|
110
241
|
},
|
|
@@ -232,6 +363,7 @@ module.exports = {
|
|
|
232
363
|
res.json(themeContext);
|
|
233
364
|
})
|
|
234
365
|
|
|
366
|
+
// Copy the settings that need passing to the editor into themeSettings.
|
|
235
367
|
if (theme.hasOwnProperty("menu")) {
|
|
236
368
|
themeSettings.menu = theme.menu;
|
|
237
369
|
}
|
|
@@ -263,104 +395,11 @@ module.exports = {
|
|
|
263
395
|
return themeApp;
|
|
264
396
|
},
|
|
265
397
|
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
|
-
}
|
|
398
|
+
await loadThemePlugin();
|
|
361
399
|
return themeContext;
|
|
362
400
|
},
|
|
363
|
-
settings: function() {
|
|
401
|
+
settings: async function() {
|
|
402
|
+
await loadThemePlugin();
|
|
364
403
|
return themeSettings;
|
|
365
404
|
},
|
|
366
405
|
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.4",
|
|
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": "5.0.0-beta.
|
|
20
|
-
"@node-red/editor-client": "5.0.0-beta.
|
|
21
|
-
"bcryptjs": "3.0.
|
|
19
|
+
"@node-red/util": "5.0.0-beta.4",
|
|
20
|
+
"@node-red/editor-client": "5.0.0-beta.4",
|
|
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",
|