@aaqu/fromcubes-portal-react 0.1.0-alpha.7 → 0.1.0-alpha.8
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/README.md +46 -30
- package/examples/chart-portal-flow.json +74 -0
- package/examples/d3-poland-flow.json +80 -0
- package/examples/sensor-portal-flow.json +2 -2
- package/examples/threejs-portal-flow.json +61 -0
- package/nodes/portal-react.html +356 -87
- package/nodes/portal-react.js +302 -129
- package/package.json +7 -8
- package/nodes/vendor/react-19.production.min.js +0 -55
- package/scripts/bundle-react.js +0 -31
package/nodes/portal-react.html
CHANGED
|
@@ -26,7 +26,10 @@
|
|
|
26
26
|
// Idempotent — safe to call multiple times, only runs setup once.
|
|
27
27
|
function ensureJsxSetup() {
|
|
28
28
|
if (jsxSetupDone) {
|
|
29
|
-
console.log(
|
|
29
|
+
console.log(
|
|
30
|
+
PREFIX,
|
|
31
|
+
"ensureJsxSetup: already done, re-applying compiler+diag only",
|
|
32
|
+
);
|
|
30
33
|
// Always re-apply compiler/diag in case Node-RED reset them
|
|
31
34
|
applyCompilerAndDiag();
|
|
32
35
|
return;
|
|
@@ -36,8 +39,16 @@
|
|
|
36
39
|
var jsDef = monaco.typescript.javascriptDefaults;
|
|
37
40
|
|
|
38
41
|
// Log initial state
|
|
39
|
-
console.log(
|
|
40
|
-
|
|
42
|
+
console.log(
|
|
43
|
+
PREFIX,
|
|
44
|
+
"INITIAL compilerOptions:",
|
|
45
|
+
JSON.stringify(jsDef.getCompilerOptions()),
|
|
46
|
+
);
|
|
47
|
+
console.log(
|
|
48
|
+
PREFIX,
|
|
49
|
+
"INITIAL diagnosticsOptions:",
|
|
50
|
+
JSON.stringify(jsDef.getDiagnosticsOptions()),
|
|
51
|
+
);
|
|
41
52
|
|
|
42
53
|
applyCompilerAndDiag();
|
|
43
54
|
|
|
@@ -134,7 +145,17 @@
|
|
|
134
145
|
});
|
|
135
146
|
}
|
|
136
147
|
}
|
|
137
|
-
console.log(
|
|
148
|
+
console.log(
|
|
149
|
+
PREFIX,
|
|
150
|
+
"TW completion: ctx=" +
|
|
151
|
+
(isClassName ? "className" : "string") +
|
|
152
|
+
" word='" +
|
|
153
|
+
word +
|
|
154
|
+
"', matched=" +
|
|
155
|
+
suggestions.length +
|
|
156
|
+
"/" +
|
|
157
|
+
classes.length,
|
|
158
|
+
);
|
|
138
159
|
return { suggestions: suggestions };
|
|
139
160
|
},
|
|
140
161
|
});
|
|
@@ -142,19 +163,82 @@
|
|
|
142
163
|
// JSX/HTML tag completion — type tag name, Tab → <tag>|</tag>
|
|
143
164
|
console.log(PREFIX, "registering JSX tag completion provider");
|
|
144
165
|
var HTML_TAGS = [
|
|
145
|
-
"div",
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"
|
|
152
|
-
"
|
|
153
|
-
"
|
|
154
|
-
"
|
|
155
|
-
"
|
|
166
|
+
"div",
|
|
167
|
+
"span",
|
|
168
|
+
"p",
|
|
169
|
+
"a",
|
|
170
|
+
"button",
|
|
171
|
+
"input",
|
|
172
|
+
"img",
|
|
173
|
+
"form",
|
|
174
|
+
"label",
|
|
175
|
+
"h1",
|
|
176
|
+
"h2",
|
|
177
|
+
"h3",
|
|
178
|
+
"h4",
|
|
179
|
+
"h5",
|
|
180
|
+
"h6",
|
|
181
|
+
"ul",
|
|
182
|
+
"ol",
|
|
183
|
+
"li",
|
|
184
|
+
"dl",
|
|
185
|
+
"dt",
|
|
186
|
+
"dd",
|
|
187
|
+
"table",
|
|
188
|
+
"thead",
|
|
189
|
+
"tbody",
|
|
190
|
+
"tfoot",
|
|
191
|
+
"tr",
|
|
192
|
+
"th",
|
|
193
|
+
"td",
|
|
194
|
+
"section",
|
|
195
|
+
"article",
|
|
196
|
+
"aside",
|
|
197
|
+
"header",
|
|
198
|
+
"footer",
|
|
199
|
+
"nav",
|
|
200
|
+
"main",
|
|
201
|
+
"strong",
|
|
202
|
+
"em",
|
|
203
|
+
"b",
|
|
204
|
+
"i",
|
|
205
|
+
"u",
|
|
206
|
+
"s",
|
|
207
|
+
"small",
|
|
208
|
+
"mark",
|
|
209
|
+
"code",
|
|
210
|
+
"pre",
|
|
211
|
+
"blockquote",
|
|
212
|
+
"br",
|
|
213
|
+
"hr",
|
|
214
|
+
"wbr",
|
|
215
|
+
"select",
|
|
216
|
+
"option",
|
|
217
|
+
"optgroup",
|
|
218
|
+
"textarea",
|
|
219
|
+
"fieldset",
|
|
220
|
+
"legend",
|
|
221
|
+
"details",
|
|
222
|
+
"summary",
|
|
223
|
+
"dialog",
|
|
224
|
+
"canvas",
|
|
225
|
+
"video",
|
|
226
|
+
"audio",
|
|
227
|
+
"source",
|
|
228
|
+
"picture",
|
|
229
|
+
"figure",
|
|
230
|
+
"figcaption",
|
|
231
|
+
"svg",
|
|
232
|
+
"path",
|
|
233
|
+
"circle",
|
|
234
|
+
"rect",
|
|
235
|
+
"line",
|
|
236
|
+
"g",
|
|
237
|
+
"defs",
|
|
238
|
+
"use",
|
|
239
|
+
"text",
|
|
156
240
|
];
|
|
157
|
-
var VOID_TAGS = new Set(["br","hr","img","input","wbr","source"]);
|
|
241
|
+
var VOID_TAGS = new Set(["br", "hr", "img", "input", "wbr", "source"]);
|
|
158
242
|
|
|
159
243
|
monaco.languages.registerCompletionItemProvider("javascript", {
|
|
160
244
|
triggerCharacters: ["<"],
|
|
@@ -165,12 +249,15 @@
|
|
|
165
249
|
// After "<" — suggest tags
|
|
166
250
|
var afterBracket = before.match(/<([a-zA-Z]*)$/);
|
|
167
251
|
// Bare word at line start or after whitespace/{ — Emmet-like (lower or upper)
|
|
168
|
-
var bareWord =
|
|
252
|
+
var bareWord =
|
|
253
|
+
!afterBracket &&
|
|
254
|
+
before.match(/(?:^|[\s{(,])([a-zA-Z][a-zA-Z0-9]*)$/);
|
|
169
255
|
|
|
170
256
|
if (!afterBracket && !bareWord) return { suggestions: [] };
|
|
171
257
|
|
|
172
258
|
var typed = (afterBracket ? afterBracket[1] : bareWord[1]) || "";
|
|
173
|
-
var replaceStart =
|
|
259
|
+
var replaceStart =
|
|
260
|
+
position.column - typed.length - (afterBracket ? 1 : 0);
|
|
174
261
|
|
|
175
262
|
var range = {
|
|
176
263
|
startLineNumber: position.lineNumber,
|
|
@@ -192,7 +279,8 @@
|
|
|
192
279
|
label: tag,
|
|
193
280
|
kind: monaco.languages.CompletionItemKind.Property,
|
|
194
281
|
insertText: "<" + tag + " $0/>",
|
|
195
|
-
insertTextRules:
|
|
282
|
+
insertTextRules:
|
|
283
|
+
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
196
284
|
range: range,
|
|
197
285
|
detail: "<" + tag + " />",
|
|
198
286
|
sortText: "0" + tag,
|
|
@@ -202,7 +290,8 @@
|
|
|
202
290
|
label: tag,
|
|
203
291
|
kind: monaco.languages.CompletionItemKind.Property,
|
|
204
292
|
insertText: "<" + tag + ">$0</" + tag + ">",
|
|
205
|
-
insertTextRules:
|
|
293
|
+
insertTextRules:
|
|
294
|
+
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
206
295
|
range: range,
|
|
207
296
|
detail: "<" + tag + ">…</" + tag + ">",
|
|
208
297
|
sortText: "0" + tag + "a",
|
|
@@ -211,7 +300,8 @@
|
|
|
211
300
|
label: tag,
|
|
212
301
|
kind: monaco.languages.CompletionItemKind.Property,
|
|
213
302
|
insertText: "<" + tag + " $0/>",
|
|
214
|
-
insertTextRules:
|
|
303
|
+
insertTextRules:
|
|
304
|
+
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
215
305
|
range: range,
|
|
216
306
|
detail: "<" + tag + " />",
|
|
217
307
|
sortText: "0" + tag + "b",
|
|
@@ -220,9 +310,15 @@
|
|
|
220
310
|
}
|
|
221
311
|
|
|
222
312
|
// Custom components from registry (PascalCase)
|
|
223
|
-
var upperMatch =
|
|
224
|
-
|
|
225
|
-
var
|
|
313
|
+
var upperMatch =
|
|
314
|
+
afterBracket && before.match(/<([A-Z][a-zA-Z0-9]*)$/);
|
|
315
|
+
var bareUpper =
|
|
316
|
+
!afterBracket && before.match(/(?:^|[\s{(,])([A-Z][a-zA-Z0-9]*)$/);
|
|
317
|
+
var compTyped = upperMatch
|
|
318
|
+
? upperMatch[1]
|
|
319
|
+
: bareUpper
|
|
320
|
+
? bareUpper[1]
|
|
321
|
+
: "";
|
|
226
322
|
|
|
227
323
|
if (compTyped || (!typed && afterBracket)) {
|
|
228
324
|
// Fetch component names from registry (cached on window)
|
|
@@ -231,13 +327,19 @@
|
|
|
231
327
|
var name = reg[c];
|
|
232
328
|
if (seen.has(name)) continue;
|
|
233
329
|
if (compTyped && name.indexOf(compTyped) !== 0) continue;
|
|
234
|
-
if (
|
|
330
|
+
if (
|
|
331
|
+
!compTyped &&
|
|
332
|
+
typed &&
|
|
333
|
+
name.toLowerCase().indexOf(typed) !== 0
|
|
334
|
+
)
|
|
335
|
+
continue;
|
|
235
336
|
seen.add(name);
|
|
236
337
|
suggestions.push({
|
|
237
338
|
label: name,
|
|
238
339
|
kind: monaco.languages.CompletionItemKind.Class,
|
|
239
340
|
insertText: "<" + name + ">$0</" + name + ">",
|
|
240
|
-
insertTextRules:
|
|
341
|
+
insertTextRules:
|
|
342
|
+
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
241
343
|
range: range,
|
|
242
344
|
detail: "<" + name + ">…</" + name + ">",
|
|
243
345
|
sortText: "0" + name + "a",
|
|
@@ -246,7 +348,8 @@
|
|
|
246
348
|
label: name,
|
|
247
349
|
kind: monaco.languages.CompletionItemKind.Class,
|
|
248
350
|
insertText: "<" + name + " $0/>",
|
|
249
|
-
insertTextRules:
|
|
351
|
+
insertTextRules:
|
|
352
|
+
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
250
353
|
range: range,
|
|
251
354
|
detail: "<" + name + " />",
|
|
252
355
|
sortText: "0" + name + "b",
|
|
@@ -258,7 +361,8 @@
|
|
|
258
361
|
label: compTyped,
|
|
259
362
|
kind: monaco.languages.CompletionItemKind.Class,
|
|
260
363
|
insertText: "<" + compTyped + ">$0</" + compTyped + ">",
|
|
261
|
-
insertTextRules:
|
|
364
|
+
insertTextRules:
|
|
365
|
+
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
262
366
|
range: range,
|
|
263
367
|
detail: "<" + compTyped + ">…</" + compTyped + ">",
|
|
264
368
|
sortText: "0" + compTyped + "a",
|
|
@@ -267,7 +371,8 @@
|
|
|
267
371
|
label: compTyped,
|
|
268
372
|
kind: monaco.languages.CompletionItemKind.Class,
|
|
269
373
|
insertText: "<" + compTyped + " $0/>",
|
|
270
|
-
insertTextRules:
|
|
374
|
+
insertTextRules:
|
|
375
|
+
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
|
271
376
|
range: range,
|
|
272
377
|
detail: "<" + compTyped + " />",
|
|
273
378
|
sortText: "0" + compTyped + "b",
|
|
@@ -275,7 +380,13 @@
|
|
|
275
380
|
}
|
|
276
381
|
}
|
|
277
382
|
|
|
278
|
-
console.log(
|
|
383
|
+
console.log(
|
|
384
|
+
PREFIX,
|
|
385
|
+
"JSX tag completion: typed='" +
|
|
386
|
+
typed +
|
|
387
|
+
"', suggestions=" +
|
|
388
|
+
suggestions.length,
|
|
389
|
+
);
|
|
279
390
|
return { suggestions: suggestions };
|
|
280
391
|
},
|
|
281
392
|
});
|
|
@@ -296,12 +407,14 @@
|
|
|
296
407
|
var m = line.match(/^(.*)<([a-zA-Z][a-zA-Z0-9]*)>\/<\/\2>(.*)$/);
|
|
297
408
|
var matchStr, tag, prefix;
|
|
298
409
|
if (m) {
|
|
299
|
-
prefix = m[1];
|
|
410
|
+
prefix = m[1];
|
|
411
|
+
tag = m[2];
|
|
300
412
|
matchStr = "<" + tag + ">/</" + tag + ">";
|
|
301
413
|
} else {
|
|
302
414
|
m = line.match(/^(.*)<([a-zA-Z][a-zA-Z0-9]*)\/><\/\2>(.*)$/);
|
|
303
415
|
if (!m) return;
|
|
304
|
-
prefix = m[1];
|
|
416
|
+
prefix = m[1];
|
|
417
|
+
tag = m[2];
|
|
305
418
|
matchStr = "<" + tag + "/></" + tag + ">";
|
|
306
419
|
}
|
|
307
420
|
var startCol = prefix.length + 1;
|
|
@@ -309,15 +422,17 @@
|
|
|
309
422
|
var replacement = "<" + tag + " />";
|
|
310
423
|
var cursorCol = startCol + replacement.length - 2;
|
|
311
424
|
setTimeout(function () {
|
|
312
|
-
editor.executeEdits("self-close-collapse", [
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
425
|
+
editor.executeEdits("self-close-collapse", [
|
|
426
|
+
{
|
|
427
|
+
range: {
|
|
428
|
+
startLineNumber: lineNum,
|
|
429
|
+
endLineNumber: lineNum,
|
|
430
|
+
startColumn: startCol,
|
|
431
|
+
endColumn: endCol,
|
|
432
|
+
},
|
|
433
|
+
text: replacement,
|
|
318
434
|
},
|
|
319
|
-
|
|
320
|
-
}]);
|
|
435
|
+
]);
|
|
321
436
|
editor.setPosition({ lineNumber: lineNum, column: cursorCol });
|
|
322
437
|
}, 0);
|
|
323
438
|
});
|
|
@@ -328,7 +443,11 @@
|
|
|
328
443
|
function refreshComponentNames() {
|
|
329
444
|
$.getJSON("portal-react/registry", function (reg) {
|
|
330
445
|
window.__fcComponentNames = Object.keys(reg);
|
|
331
|
-
console.log(
|
|
446
|
+
console.log(
|
|
447
|
+
PREFIX,
|
|
448
|
+
"component names loaded:",
|
|
449
|
+
window.__fcComponentNames,
|
|
450
|
+
);
|
|
332
451
|
}).fail(function () {
|
|
333
452
|
window.__fcComponentNames = [];
|
|
334
453
|
});
|
|
@@ -343,11 +462,19 @@
|
|
|
343
462
|
console.group(PREFIX + " MARKERS on " + uri.toString());
|
|
344
463
|
markers.forEach(function (m) {
|
|
345
464
|
console.log(
|
|
346
|
-
" code=" +
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
465
|
+
" code=" +
|
|
466
|
+
(m.code || "?") +
|
|
467
|
+
" severity=" +
|
|
468
|
+
m.severity +
|
|
469
|
+
" source=" +
|
|
470
|
+
(m.source || "?") +
|
|
471
|
+
" msg=" +
|
|
472
|
+
m.message +
|
|
473
|
+
" [L" +
|
|
474
|
+
m.startLineNumber +
|
|
475
|
+
":" +
|
|
476
|
+
m.startColumn +
|
|
477
|
+
"]",
|
|
351
478
|
);
|
|
352
479
|
});
|
|
353
480
|
console.groupEnd();
|
|
@@ -379,8 +506,16 @@
|
|
|
379
506
|
console.log(PREFIX, "setDiagnosticsOptions:", JSON.stringify(diagOpts));
|
|
380
507
|
jsDef.setDiagnosticsOptions(diagOpts);
|
|
381
508
|
|
|
382
|
-
console.log(
|
|
383
|
-
|
|
509
|
+
console.log(
|
|
510
|
+
PREFIX,
|
|
511
|
+
"readback compilerOptions:",
|
|
512
|
+
JSON.stringify(jsDef.getCompilerOptions()),
|
|
513
|
+
);
|
|
514
|
+
console.log(
|
|
515
|
+
PREFIX,
|
|
516
|
+
"readback diagnosticsOptions:",
|
|
517
|
+
JSON.stringify(jsDef.getDiagnosticsOptions()),
|
|
518
|
+
);
|
|
384
519
|
}
|
|
385
520
|
|
|
386
521
|
// Exported for oneditprepare to call before model creation
|
|
@@ -391,7 +526,10 @@
|
|
|
391
526
|
window.__fcLoadMonaco = function (cb) {
|
|
392
527
|
console.log(PREFIX, "loadMonaco called, monaco exists:", !!window.monaco);
|
|
393
528
|
if (window.monaco) {
|
|
394
|
-
console.log(
|
|
529
|
+
console.log(
|
|
530
|
+
PREFIX,
|
|
531
|
+
"Monaco already loaded (by Node-RED?), running setup inline",
|
|
532
|
+
);
|
|
395
533
|
ensureJsxSetup();
|
|
396
534
|
cb();
|
|
397
535
|
return;
|
|
@@ -510,22 +648,32 @@
|
|
|
510
648
|
if (!window.__fcTwClasses) {
|
|
511
649
|
console.log("[FC-Monaco] loading tw-classes...");
|
|
512
650
|
$.getJSON("portal-react/tw-classes", function (classes) {
|
|
513
|
-
console.log(
|
|
651
|
+
console.log(
|
|
652
|
+
"[FC-Monaco] tw-classes loaded, count=" + classes.length,
|
|
653
|
+
);
|
|
514
654
|
window.__fcTwClasses = classes;
|
|
515
655
|
}).fail(function (xhr) {
|
|
516
|
-
console.error(
|
|
656
|
+
console.error(
|
|
657
|
+
"[FC-Monaco] tw-classes FAILED:",
|
|
658
|
+
xhr.status,
|
|
659
|
+
xhr.statusText,
|
|
660
|
+
);
|
|
517
661
|
});
|
|
518
662
|
}
|
|
519
663
|
|
|
520
664
|
window.__fcLoadMonaco(function (failed) {
|
|
521
665
|
if (failed) {
|
|
522
|
-
console.error(
|
|
666
|
+
console.error(
|
|
667
|
+
"[FC-Monaco] COMP: Monaco load failed, using fallback textarea",
|
|
668
|
+
);
|
|
523
669
|
$("#fcc-monaco").hide();
|
|
524
670
|
$("#fcc-fallback").show().val(code);
|
|
525
671
|
return;
|
|
526
672
|
}
|
|
527
673
|
|
|
528
|
-
console.log(
|
|
674
|
+
console.log(
|
|
675
|
+
"[FC-Monaco] COMP: applying JSX defaults before model creation",
|
|
676
|
+
);
|
|
529
677
|
window.__fcApplyJsxDefaults();
|
|
530
678
|
|
|
531
679
|
var compUri = monaco.Uri.parse("file:///fc-comp-" + node.id + ".jsx");
|
|
@@ -535,8 +683,15 @@
|
|
|
535
683
|
console.log("[FC-Monaco] COMP: disposing existing model");
|
|
536
684
|
existingModel.dispose();
|
|
537
685
|
}
|
|
538
|
-
var compModel = monaco.editor.createModel(
|
|
539
|
-
|
|
686
|
+
var compModel = monaco.editor.createModel(
|
|
687
|
+
code,
|
|
688
|
+
"javascript",
|
|
689
|
+
compUri,
|
|
690
|
+
);
|
|
691
|
+
console.log(
|
|
692
|
+
"[FC-Monaco] COMP: model created, language=" +
|
|
693
|
+
compModel.getLanguageId(),
|
|
694
|
+
);
|
|
540
695
|
|
|
541
696
|
compEditorInstance = monaco.editor.create(
|
|
542
697
|
document.getElementById("fcc-monaco"),
|
|
@@ -547,13 +702,27 @@
|
|
|
547
702
|
// Log markers after a short delay (diagnostics are async)
|
|
548
703
|
setTimeout(function () {
|
|
549
704
|
var markers = monaco.editor.getModelMarkers({ resource: compUri });
|
|
550
|
-
console.log(
|
|
705
|
+
console.log(
|
|
706
|
+
"[FC-Monaco] COMP: markers after 500ms, count=" + markers.length,
|
|
707
|
+
);
|
|
551
708
|
markers.forEach(function (m) {
|
|
552
|
-
console.log(
|
|
709
|
+
console.log(
|
|
710
|
+
"[FC-Monaco] COMP marker: code=" + m.code + " msg=" + m.message,
|
|
711
|
+
);
|
|
553
712
|
});
|
|
554
713
|
// Also log current compiler options state
|
|
555
|
-
console.log(
|
|
556
|
-
|
|
714
|
+
console.log(
|
|
715
|
+
"[FC-Monaco] COMP: current compilerOptions:",
|
|
716
|
+
JSON.stringify(
|
|
717
|
+
monaco.typescript.javascriptDefaults.getCompilerOptions(),
|
|
718
|
+
),
|
|
719
|
+
);
|
|
720
|
+
console.log(
|
|
721
|
+
"[FC-Monaco] COMP: current diagnosticsOptions:",
|
|
722
|
+
JSON.stringify(
|
|
723
|
+
monaco.typescript.javascriptDefaults.getDiagnosticsOptions(),
|
|
724
|
+
),
|
|
725
|
+
);
|
|
557
726
|
}, 500);
|
|
558
727
|
});
|
|
559
728
|
},
|
|
@@ -641,7 +810,7 @@
|
|
|
641
810
|
<label for="node-input-endpoint"
|
|
642
811
|
><i class="fa fa-globe"></i> Endpoint</label
|
|
643
812
|
>
|
|
644
|
-
<input type="text" id="node-input-endpoint" placeholder="/
|
|
813
|
+
<input type="text" id="node-input-endpoint" placeholder="/fromcubes" />
|
|
645
814
|
<div
|
|
646
815
|
style="font-size:11px;opacity:.5;margin-top:2px;margin-left:105px;"
|
|
647
816
|
id="fc-url-hint"
|
|
@@ -653,6 +822,29 @@
|
|
|
653
822
|
>
|
|
654
823
|
<input type="text" id="node-input-pageTitle" placeholder="Portal" />
|
|
655
824
|
</div>
|
|
825
|
+
<div class="form-row node-input-libs-container-row">
|
|
826
|
+
<label style="width:auto;"><i class="fa fa-archive"></i> Modules</label>
|
|
827
|
+
<ol id="node-input-libs-container"></ol>
|
|
828
|
+
<div
|
|
829
|
+
style="font-size:11px;opacity:.5;margin-top:4px;margin-left:4px;"
|
|
830
|
+
>
|
|
831
|
+
npm packages to bundle. Node-RED auto-installs them at deploy time.
|
|
832
|
+
</div>
|
|
833
|
+
</div>
|
|
834
|
+
<div class="form-row" style="margin-top:12px;">
|
|
835
|
+
<label style="width:auto;">
|
|
836
|
+
<input
|
|
837
|
+
type="checkbox"
|
|
838
|
+
id="node-input-showWsStatus"
|
|
839
|
+
style="width:auto;margin:0 8px 0 0;vertical-align:middle;"
|
|
840
|
+
/>
|
|
841
|
+
Show WebSocket status indicator
|
|
842
|
+
</label>
|
|
843
|
+
<div style="font-size:11px;opacity:.5;margin-top:4px;margin-left:4px;">
|
|
844
|
+
Displays a small <em>fromcubes</em> badge in the bottom-right corner
|
|
845
|
+
showing connection state.
|
|
846
|
+
</div>
|
|
847
|
+
</div>
|
|
656
848
|
</div>
|
|
657
849
|
|
|
658
850
|
<!-- ── Tab: JSX ── -->
|
|
@@ -725,14 +917,17 @@
|
|
|
725
917
|
</div>
|
|
726
918
|
<div class="form-row" style="padding-left:4px;">
|
|
727
919
|
<div style="font-size:11px;opacity:.6;line-height:1.5;">
|
|
728
|
-
Read <code>X-Portal-*</code> headers set by Nginx proxy and expose
|
|
729
|
-
via <code>useNodeRed()</code>.<br
|
|
730
|
-
When enabled:<br
|
|
731
|
-
• <code>useNodeRed()</code> returns
|
|
732
|
-
<code>user</code>
|
|
733
|
-
<code>
|
|
734
|
-
|
|
735
|
-
|
|
920
|
+
Read <code>X-Portal-*</code> headers set by Nginx proxy and expose
|
|
921
|
+
user data via <code>useNodeRed()</code>.<br /><br />
|
|
922
|
+
When enabled:<br />
|
|
923
|
+
• <code>useNodeRed()</code> returns
|
|
924
|
+
<code>{ data, send, user }</code> where <code>user</code> contains
|
|
925
|
+
<code>userId</code>, <code>userName</code>, <code>username</code>,
|
|
926
|
+
<code>email</code>, <code>role</code>, <code>groups</code><br />
|
|
927
|
+
• Messages from WebSocket include <code>msg._client</code> with
|
|
928
|
+
user data<br /><br />
|
|
929
|
+
Requires Nginx (or similar reverse proxy) to set
|
|
930
|
+
<code>X-Portal-*</code> headers.
|
|
736
931
|
</div>
|
|
737
932
|
</div>
|
|
738
933
|
</div>
|
|
@@ -759,7 +954,7 @@
|
|
|
759
954
|
"",
|
|
760
955
|
" return (",
|
|
761
956
|
' <div className="min-h-screen bg-zinc-950 p-8">',
|
|
762
|
-
' <h1 className="text-2xl font-light text-cyan-400 mb-6">
|
|
957
|
+
' <h1 className="text-2xl font-light text-cyan-400 mb-6">fromcubes</h1>',
|
|
763
958
|
' <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">',
|
|
764
959
|
' <StatusCard label="Value" value={d.value ?? "—"} unit="" />',
|
|
765
960
|
" </div>",
|
|
@@ -779,11 +974,13 @@
|
|
|
779
974
|
color: "#61dafb",
|
|
780
975
|
defaults: {
|
|
781
976
|
name: { value: "" },
|
|
782
|
-
endpoint: { value: "/
|
|
783
|
-
pageTitle: { value: "
|
|
977
|
+
endpoint: { value: "/fromcubes", required: true },
|
|
978
|
+
pageTitle: { value: "fromcubes" },
|
|
784
979
|
componentCode: { value: STARTER },
|
|
785
980
|
customHead: { value: "" },
|
|
786
981
|
portalAuth: { value: false },
|
|
982
|
+
showWsStatus: { value: false },
|
|
983
|
+
libs: { value: [] },
|
|
787
984
|
},
|
|
788
985
|
inputs: 1,
|
|
789
986
|
outputs: 1,
|
|
@@ -802,10 +999,16 @@
|
|
|
802
999
|
if (!window.__fcTwClasses) {
|
|
803
1000
|
console.log("[FC-Monaco] loading tw-classes...");
|
|
804
1001
|
$.getJSON("portal-react/tw-classes", function (classes) {
|
|
805
|
-
console.log(
|
|
1002
|
+
console.log(
|
|
1003
|
+
"[FC-Monaco] tw-classes loaded, count=" + classes.length,
|
|
1004
|
+
);
|
|
806
1005
|
window.__fcTwClasses = classes;
|
|
807
1006
|
}).fail(function (xhr) {
|
|
808
|
-
console.error(
|
|
1007
|
+
console.error(
|
|
1008
|
+
"[FC-Monaco] tw-classes FAILED:",
|
|
1009
|
+
xhr.status,
|
|
1010
|
+
xhr.statusText,
|
|
1011
|
+
);
|
|
809
1012
|
});
|
|
810
1013
|
}
|
|
811
1014
|
|
|
@@ -829,16 +1032,38 @@
|
|
|
829
1032
|
|
|
830
1033
|
// URL hint
|
|
831
1034
|
function updateHint() {
|
|
832
|
-
var ep = $("#node-input-endpoint").val() || "/
|
|
1035
|
+
var ep = $("#node-input-endpoint").val() || "/fromcubes";
|
|
833
1036
|
$("#fc-url-hint").text("Page served at: http://<host>:1880" + ep);
|
|
834
1037
|
}
|
|
835
1038
|
$("#node-input-endpoint").on("input", updateHint);
|
|
836
1039
|
updateHint();
|
|
837
1040
|
|
|
1041
|
+
// Modules editableList (like function node's libs)
|
|
1042
|
+
var libsList = node.libs || [];
|
|
1043
|
+
$("#node-input-libs-container").css("min-height","68px").editableList({
|
|
1044
|
+
addItem: function(container, i, opt) {
|
|
1045
|
+
var lib = opt || {};
|
|
1046
|
+
var row = $('<div/>',{style:"display:flex;gap:8px;align-items:center;"}).appendTo(container);
|
|
1047
|
+
var modInput = $('<input/>',{type:"text",placeholder:"e.g. chart.js/auto@^4.4.0",style:"flex:1;"}).appendTo(row);
|
|
1048
|
+
var varInput = $('<input/>',{type:"text",placeholder:"Import as (e.g. Chart)",style:"width:140px;"}).appendTo(row);
|
|
1049
|
+
modInput.val(lib.module || "");
|
|
1050
|
+
varInput.val(lib.var || "");
|
|
1051
|
+
container.data("mod", modInput);
|
|
1052
|
+
container.data("var", varInput);
|
|
1053
|
+
},
|
|
1054
|
+
removable: true,
|
|
1055
|
+
sortable: true
|
|
1056
|
+
});
|
|
1057
|
+
libsList.forEach(function(lib) {
|
|
1058
|
+
$("#node-input-libs-container").editableList("addItem", lib);
|
|
1059
|
+
});
|
|
1060
|
+
|
|
838
1061
|
// Monaco
|
|
839
1062
|
window.__fcLoadMonaco(function (failed) {
|
|
840
1063
|
if (failed) {
|
|
841
|
-
console.error(
|
|
1064
|
+
console.error(
|
|
1065
|
+
"[FC-Monaco] PORTAL: Monaco load failed, using fallback textarea",
|
|
1066
|
+
);
|
|
842
1067
|
$("#fc-monaco").hide();
|
|
843
1068
|
$("#fc-fallback").show().val(code);
|
|
844
1069
|
$("#fc-head-monaco").hide();
|
|
@@ -846,11 +1071,15 @@
|
|
|
846
1071
|
return;
|
|
847
1072
|
}
|
|
848
1073
|
|
|
849
|
-
console.log(
|
|
1074
|
+
console.log(
|
|
1075
|
+
"[FC-Monaco] PORTAL: applying JSX defaults before model creation",
|
|
1076
|
+
);
|
|
850
1077
|
window.__fcApplyJsxDefaults();
|
|
851
1078
|
var opts = window.__fcEditorOpts;
|
|
852
1079
|
|
|
853
|
-
var jsxUri = monaco.Uri.parse(
|
|
1080
|
+
var jsxUri = monaco.Uri.parse(
|
|
1081
|
+
"file:///fc-portal-" + node.id + ".jsx",
|
|
1082
|
+
);
|
|
854
1083
|
console.log("[FC-Monaco] PORTAL: JSX model URI=" + jsxUri.toString());
|
|
855
1084
|
var existingJsx = monaco.editor.getModel(jsxUri);
|
|
856
1085
|
if (existingJsx) {
|
|
@@ -858,15 +1087,21 @@
|
|
|
858
1087
|
existingJsx.dispose();
|
|
859
1088
|
}
|
|
860
1089
|
var jsxModel = monaco.editor.createModel(code, "javascript", jsxUri);
|
|
861
|
-
console.log(
|
|
1090
|
+
console.log(
|
|
1091
|
+
"[FC-Monaco] PORTAL: JSX model created, language=" +
|
|
1092
|
+
jsxModel.getLanguageId(),
|
|
1093
|
+
);
|
|
862
1094
|
editorInstance = monaco.editor.create(
|
|
863
1095
|
document.getElementById("fc-monaco"),
|
|
864
1096
|
Object.assign({ model: jsxModel }, opts),
|
|
865
1097
|
);
|
|
866
1098
|
console.log("[FC-Monaco] PORTAL: JSX editor created");
|
|
867
|
-
if (window.__fcAttachSelfClose)
|
|
1099
|
+
if (window.__fcAttachSelfClose)
|
|
1100
|
+
window.__fcAttachSelfClose(editorInstance);
|
|
868
1101
|
|
|
869
|
-
var headUri = monaco.Uri.parse(
|
|
1102
|
+
var headUri = monaco.Uri.parse(
|
|
1103
|
+
"file:///fc-head-" + node.id + ".html",
|
|
1104
|
+
);
|
|
870
1105
|
var existingHead = monaco.editor.getModel(headUri);
|
|
871
1106
|
if (existingHead) existingHead.dispose();
|
|
872
1107
|
var headModel = monaco.editor.createModel(headCode, "html", headUri);
|
|
@@ -879,12 +1114,32 @@
|
|
|
879
1114
|
// Log markers after a short delay (diagnostics are async)
|
|
880
1115
|
setTimeout(function () {
|
|
881
1116
|
var markers = monaco.editor.getModelMarkers({ resource: jsxUri });
|
|
882
|
-
console.log(
|
|
1117
|
+
console.log(
|
|
1118
|
+
"[FC-Monaco] PORTAL: markers after 500ms, count=" +
|
|
1119
|
+
markers.length,
|
|
1120
|
+
);
|
|
883
1121
|
markers.forEach(function (m) {
|
|
884
|
-
console.log(
|
|
1122
|
+
console.log(
|
|
1123
|
+
"[FC-Monaco] PORTAL marker: code=" +
|
|
1124
|
+
m.code +
|
|
1125
|
+
" severity=" +
|
|
1126
|
+
m.severity +
|
|
1127
|
+
" msg=" +
|
|
1128
|
+
m.message,
|
|
1129
|
+
);
|
|
885
1130
|
});
|
|
886
|
-
console.log(
|
|
887
|
-
|
|
1131
|
+
console.log(
|
|
1132
|
+
"[FC-Monaco] PORTAL: current compilerOptions:",
|
|
1133
|
+
JSON.stringify(
|
|
1134
|
+
monaco.typescript.javascriptDefaults.getCompilerOptions(),
|
|
1135
|
+
),
|
|
1136
|
+
);
|
|
1137
|
+
console.log(
|
|
1138
|
+
"[FC-Monaco] PORTAL: current diagnosticsOptions:",
|
|
1139
|
+
JSON.stringify(
|
|
1140
|
+
monaco.typescript.javascriptDefaults.getDiagnosticsOptions(),
|
|
1141
|
+
),
|
|
1142
|
+
);
|
|
888
1143
|
}, 500);
|
|
889
1144
|
});
|
|
890
1145
|
|
|
@@ -958,6 +1213,19 @@
|
|
|
958
1213
|
|
|
959
1214
|
oneditsave: function () {
|
|
960
1215
|
console.log("[FC-Monaco] PORTAL oneditsave");
|
|
1216
|
+
|
|
1217
|
+
// Collect libs from editableList
|
|
1218
|
+
var libs = [];
|
|
1219
|
+
var items = $("#node-input-libs-container").editableList("items");
|
|
1220
|
+
items.each(function() {
|
|
1221
|
+
var mod = $(this).data("mod").val().trim();
|
|
1222
|
+
var v = $(this).data("var").val().trim();
|
|
1223
|
+
if (mod) {
|
|
1224
|
+
libs.push({ module: mod, var: v });
|
|
1225
|
+
}
|
|
1226
|
+
});
|
|
1227
|
+
this.libs = libs;
|
|
1228
|
+
|
|
961
1229
|
var code = editorInstance
|
|
962
1230
|
? editorInstance.getValue()
|
|
963
1231
|
: $("#fc-fallback").val();
|
|
@@ -1078,12 +1346,13 @@ const { data, send } = useNodeRed();
|
|
|
1078
1346
|
<h3>Portal Auth</h3>
|
|
1079
1347
|
<p>
|
|
1080
1348
|
When enabled in the Auth tab, reads <code>X-Portal-*</code> headers set by
|
|
1081
|
-
|
|
1082
|
-
exposes user data:
|
|
1349
|
+
a reverse proxy (e.g. Nginx) and exposes user data:
|
|
1083
1350
|
</p>
|
|
1084
1351
|
<ul>
|
|
1085
1352
|
<li><code>useNodeRed()</code> returns <code>{ data, send, user }</code></li>
|
|
1086
|
-
<li>
|
|
1353
|
+
<li>
|
|
1354
|
+
Messages from WebSocket include <code>msg._client</code> with user info
|
|
1355
|
+
</li>
|
|
1087
1356
|
</ul>
|
|
1088
1357
|
|
|
1089
1358
|
<h3>Custom Head HTML</h3>
|