@bonsae/nrg 0.6.1 → 0.6.2

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.
Files changed (84) hide show
  1. package/README.md +5 -5
  2. package/package.json +13 -77
  3. package/{src/core/client → shims}/components.d.ts +2 -0
  4. package/{src/tsconfig → tsconfig}/client.json +3 -3
  5. package/types/client.d.ts +37 -0
  6. package/types/index.d.ts +211 -0
  7. package/types/server.d.ts +2293 -0
  8. package/types/vite.d.ts +12 -0
  9. package/build/vite/utils.js +0 -56
  10. package/src/core/client/app.vue +0 -185
  11. package/src/core/client/components/node-red-config-input.vue +0 -79
  12. package/src/core/client/components/node-red-editor-input.vue +0 -307
  13. package/src/core/client/components/node-red-input-label.vue +0 -53
  14. package/src/core/client/components/node-red-input.vue +0 -93
  15. package/src/core/client/components/node-red-json-schema-form.vue +0 -444
  16. package/src/core/client/components/node-red-select-input.vue +0 -108
  17. package/src/core/client/components/node-red-toggle.vue +0 -115
  18. package/src/core/client/components/node-red-typed-input.vue +0 -158
  19. package/src/core/client/index.ts +0 -500
  20. package/src/core/client/tsconfig.json +0 -18
  21. package/src/core/constants.ts +0 -18
  22. package/src/core/errors.ts +0 -9
  23. package/src/core/server/api/index.ts +0 -1
  24. package/src/core/server/api/serve-nrg-resources.ts +0 -54
  25. package/src/core/server/index.ts +0 -191
  26. package/src/core/server/nodes/config-node.ts +0 -67
  27. package/src/core/server/nodes/factories.ts +0 -136
  28. package/src/core/server/nodes/index.ts +0 -5
  29. package/src/core/server/nodes/io-node.ts +0 -179
  30. package/src/core/server/nodes/node.ts +0 -259
  31. package/src/core/server/nodes/types/config-node.ts +0 -28
  32. package/src/core/server/nodes/types/factories.ts +0 -130
  33. package/src/core/server/nodes/types/index.ts +0 -4
  34. package/src/core/server/nodes/types/io-node.ts +0 -40
  35. package/src/core/server/nodes/types/node.ts +0 -41
  36. package/src/core/server/nodes/utils.ts +0 -106
  37. package/src/core/server/schemas/base.ts +0 -66
  38. package/src/core/server/schemas/index.ts +0 -3
  39. package/src/core/server/schemas/type.ts +0 -95
  40. package/src/core/server/schemas/types/index.ts +0 -82
  41. package/src/core/server/tsconfig.json +0 -17
  42. package/src/core/server/types/index.ts +0 -220
  43. package/src/core/server/utils.ts +0 -56
  44. package/src/core/server/validator.ts +0 -36
  45. package/src/core/validator.ts +0 -222
  46. package/src/index.ts +0 -2
  47. package/src/types.ts +0 -189
  48. package/src/utils.ts +0 -20
  49. package/src/vite/async-utils.ts +0 -61
  50. package/src/vite/client/build.ts +0 -227
  51. package/src/vite/client/index.ts +0 -1
  52. package/src/vite/client/plugins/html-generator.ts +0 -75
  53. package/src/vite/client/plugins/index.ts +0 -5
  54. package/src/vite/client/plugins/locales-generator.ts +0 -126
  55. package/src/vite/client/plugins/minifier.ts +0 -23
  56. package/src/vite/client/plugins/node-definitions-inliner.ts +0 -275
  57. package/src/vite/client/plugins/static-copy.ts +0 -43
  58. package/src/vite/defaults.ts +0 -77
  59. package/src/vite/errors.ts +0 -37
  60. package/src/vite/index.ts +0 -2
  61. package/src/vite/logger.ts +0 -94
  62. package/src/vite/node-red-launcher.ts +0 -344
  63. package/src/vite/plugin.ts +0 -61
  64. package/src/vite/plugins/build.ts +0 -85
  65. package/src/vite/plugins/index.ts +0 -2
  66. package/src/vite/plugins/server.ts +0 -267
  67. package/src/vite/server/build.ts +0 -124
  68. package/src/vite/server/index.ts +0 -1
  69. package/src/vite/server/plugins/index.ts +0 -3
  70. package/src/vite/server/plugins/output-wrapper.ts +0 -109
  71. package/src/vite/server/plugins/package-json-generator.ts +0 -203
  72. package/src/vite/server/plugins/type-generator.ts +0 -442
  73. package/src/vite/types.ts +0 -174
  74. package/src/vite/utils.ts +0 -72
  75. /package/{build/index.js → index.js} +0 -0
  76. /package/{build/server → server}/index.cjs +0 -0
  77. /package/{build/server → server}/resources/nrg-client.js +0 -0
  78. /package/{build/server → server}/resources/vue.esm-browser.js +0 -0
  79. /package/{build/server → server}/resources/vue.esm-browser.prod.js +0 -0
  80. /package/{src/core/client → shims}/globals.d.ts +0 -0
  81. /package/{src/core/client → shims}/shims-vue.d.ts +0 -0
  82. /package/{src/tsconfig → tsconfig}/base.json +0 -0
  83. /package/{src/tsconfig → tsconfig}/server.json +0 -0
  84. /package/{build/vite → vite}/index.js +0 -0
@@ -0,0 +1,12 @@
1
+
2
+ import type { Plugin } from "vite";
3
+
4
+ export interface NodeRedPluginOptions {
5
+ outDir?: string;
6
+ serverBuildOptions?: Record<string, any>;
7
+ clientBuildOptions?: Record<string, any>;
8
+ nodeRedLauncherOptions?: Record<string, any>;
9
+ extraFilesCopyTargets?: Array<{ src: string; dest: string }>;
10
+ }
11
+
12
+ export declare function nodeRed(options?: NodeRedPluginOptions): Plugin[];
@@ -1,56 +0,0 @@
1
- // src/vite/utils.ts
2
- import fs from "fs";
3
- import path from "path";
4
- function cleanDir(dir) {
5
- if (fs.existsSync(dir)) {
6
- fs.rmSync(dir, { recursive: true });
7
- }
8
- fs.mkdirSync(dir, { recursive: true });
9
- }
10
- function copyFiles(targets, outDir) {
11
- for (const { src, dest } of targets) {
12
- const srcPath = path.resolve(src);
13
- const destPath = path.join(outDir, dest);
14
- if (!fs.existsSync(srcPath)) {
15
- continue;
16
- }
17
- const stat = fs.statSync(srcPath);
18
- if (stat.isDirectory()) {
19
- fs.cpSync(srcPath, destPath, { recursive: true });
20
- } else {
21
- fs.mkdirSync(path.dirname(destPath), { recursive: true });
22
- fs.copyFileSync(srcPath, destPath);
23
- }
24
- }
25
- }
26
- function getPackageName() {
27
- const pkgPath = path.resolve("./package.json");
28
- if (fs.existsSync(pkgPath)) {
29
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
30
- return pkg.name;
31
- }
32
- return "node-red-nodes";
33
- }
34
- function mergeOptions(defaults, overrides) {
35
- if (!overrides) return { ...defaults };
36
- const result = { ...defaults };
37
- for (const key of Object.keys(overrides)) {
38
- const overrideVal = overrides[key];
39
- const defaultVal = defaults[key];
40
- if (overrideVal !== void 0 && !Array.isArray(overrideVal) && !Array.isArray(defaultVal) && typeof overrideVal === "object" && typeof defaultVal === "object" && overrideVal !== null && defaultVal !== null) {
41
- result[key] = mergeOptions(
42
- defaultVal,
43
- overrideVal
44
- );
45
- } else if (overrideVal !== void 0) {
46
- result[key] = overrideVal;
47
- }
48
- }
49
- return result;
50
- }
51
- export {
52
- cleanDir,
53
- copyFiles,
54
- getPackageName,
55
- mergeOptions
56
- };
@@ -1,185 +0,0 @@
1
- <template>
2
- <div
3
- v-if="features.hasInputSchema || features.hasOutputSchema"
4
- class="form-row"
5
- style="display: flex; align-items: center; gap: 12px"
6
- >
7
- <NodeRedToggle
8
- v-if="features.hasInputSchema"
9
- :model-value="localNode.validateInput"
10
- label="Validate Input"
11
- style="flex: 1"
12
- @update:model-value="localNode.validateInput = $event"
13
- />
14
- <NodeRedToggle
15
- v-if="features.hasOutputSchema"
16
- :model-value="localNode.validateOutput"
17
- label="Validate Output"
18
- style="flex: 1"
19
- @update:model-value="localNode.validateOutput = $event"
20
- />
21
- </div>
22
- <div style="width: 100%; padding-bottom: 12px">
23
- <NodeRedNodeForm
24
- :node="localNode"
25
- :schema="schema"
26
- :errors="errors"
27
- style="width: 100%"
28
- />
29
- </div>
30
- </template>
31
-
32
- <script lang="ts">
33
- import jsonpointer from "jsonpointer";
34
- import { type JSONSchemaType } from "ajv";
35
- import type { PropType } from "vue";
36
- import { defineComponent } from "vue";
37
- import { validator } from "../validator";
38
-
39
- export default defineComponent({
40
- name: "NodeRedVueApp",
41
- props: {
42
- node: {
43
- type: Object,
44
- required: true,
45
- },
46
- schema: {
47
- type: Object as PropType<JSONSchemaType<any>>,
48
- required: true,
49
- },
50
- features: {
51
- type: Object as PropType<{
52
- hasInputSchema: boolean;
53
- hasOutputSchema: boolean;
54
- }>,
55
- required: true,
56
- },
57
- },
58
- data() {
59
- return {
60
- localNode: this.node,
61
- errors: {},
62
- };
63
- },
64
- beforeMount() {
65
- // Normalize array-typed properties to actual arrays. Nodes saved with an
66
- // older version of the code may have stored array values as comma-separated
67
- // strings; this ensures validation and future saves always see real arrays.
68
- if (this.schema?.properties) {
69
- for (const [prop, propSchema] of Object.entries(this.schema.properties)) {
70
- if (
71
- (propSchema as any).type === "array" &&
72
- !Array.isArray(this.localNode[prop])
73
- ) {
74
- const val = this.localNode[prop];
75
- this.localNode[prop] = val
76
- ? String(val).split(",").filter(Boolean)
77
- : [];
78
- }
79
- }
80
- }
81
-
82
- // Set __PWD__ for existing password fields before the first validation so
83
- // the password-skip logic in validate() sees the sentinel value correctly.
84
- if (this.localNode._def.credentials) {
85
- Object.keys(this.localNode._def.credentials).forEach((prop) => {
86
- if (
87
- this.localNode._def.credentials[prop].type === "password" &&
88
- this.localNode.credentials[`has_${prop}`]
89
- ) {
90
- this.localNode.credentials[prop] = "__PWD__";
91
- }
92
- });
93
- }
94
-
95
- this.validate();
96
-
97
- if (this.localNode._def.defaults) {
98
- Object.keys(this.localNode._def.defaults).forEach((prop) => {
99
- this.$watch(
100
- () => this.localNode[prop],
101
- () => {
102
- this.validate();
103
- },
104
- { deep: true },
105
- );
106
- });
107
- }
108
-
109
- if (this.localNode._def.credentials) {
110
- Object.keys(this.localNode._def.credentials).forEach((prop) => {
111
- this.$watch(
112
- () => this.localNode.credentials[prop],
113
- (newVal, oldVal) => {
114
- this.validate();
115
-
116
- if (
117
- this.localNode._def.credentials[prop].type === "password" &&
118
- newVal !== oldVal
119
- ) {
120
- this.localNode.credentials[`has_${prop}`] = !!newVal;
121
- }
122
- },
123
- { deep: true },
124
- );
125
- });
126
- }
127
- },
128
- beforeUnmount() {
129
- // NOTE: must set credentials prop to undefined to avoid updating it to __PWD__ in the server
130
- if (this.localNode._def.credentials) {
131
- Object.keys(this.localNode._def.credentials).forEach((prop) => {
132
- if (
133
- this.localNode._def.credentials[prop].type === "password" &&
134
- this.localNode.credentials?.[`has_${prop}`] &&
135
- this.localNode.credentials?.[prop] === "__PWD__"
136
- ) {
137
- this.localNode.credentials[prop] = undefined;
138
- }
139
- });
140
- }
141
- },
142
- methods: {
143
- validate() {
144
- const result = validator.validate(this.localNode, this.schema, {
145
- cacheKey: `node-schema-${this.node.type}`,
146
- });
147
-
148
- if (!result.valid) {
149
- this.errors = result.errors.reduce((acc, error) => {
150
- const errorValue = jsonpointer.get(
151
- this.localNode,
152
- error.instancePath,
153
- );
154
- if (
155
- error.parentSchema?.format === "password" &&
156
- errorValue === "__PWD__"
157
- ) {
158
- return acc;
159
- } else {
160
- const key = `node${error.instancePath.replaceAll("/", ".")}`;
161
- acc[key] = error.message;
162
- return acc;
163
- }
164
- }, {});
165
- } else {
166
- this.errors = {};
167
- }
168
- },
169
- },
170
- });
171
- </script>
172
-
173
- <style scoped>
174
- :deep(.node-red-vue-input-error-message) {
175
- color: var(--red-ui-text-color-error);
176
- }
177
-
178
- :deep(.form-row input[type="text"]),
179
- :deep(.form-row input[type="number"]),
180
- :deep(.form-row input[type="password"]) {
181
- height: 34px;
182
- padding: 0 8px;
183
- box-sizing: border-box;
184
- }
185
- </style>
@@ -1,79 +0,0 @@
1
- <template>
2
- <div style="display: flex; flex-direction: column; width: 100%">
3
- <slot name="label">
4
- <NodeRedInputLabel
5
- v-if="label"
6
- :label="label"
7
- :icon="icon"
8
- :required="required"
9
- />
10
- </slot>
11
- <input :id="inputId" type="text" style="width: 100%" />
12
- <div v-if="error" class="node-red-vue-input-error-message">
13
- {{ error }}
14
- </div>
15
- </div>
16
- </template>
17
-
18
- <script lang="ts">
19
- import { defineComponent } from "vue";
20
- import NodeRedInputLabel from "./node-red-input-label.vue";
21
- export default defineComponent({
22
- components: { NodeRedInputLabel },
23
- props: {
24
- value: {
25
- type: String,
26
- default: "",
27
- },
28
- type: {
29
- type: String,
30
- required: true,
31
- },
32
- node: {
33
- type: Object,
34
- required: true,
35
- },
36
- propName: {
37
- type: String,
38
- required: true,
39
- },
40
- label: {
41
- type: String,
42
- default: "",
43
- },
44
- icon: {
45
- type: String,
46
- default: "",
47
- },
48
- required: {
49
- type: Boolean,
50
- default: false,
51
- },
52
- error: {
53
- type: String,
54
- default: "",
55
- },
56
- },
57
- emits: ["update:value"],
58
- computed: {
59
- inputId() {
60
- return "node-input-" + this.propName;
61
- },
62
- },
63
- mounted() {
64
- RED.editor.prepareConfigNodeSelect(
65
- this.node,
66
- this.propName,
67
- this.type,
68
- "node-input",
69
- );
70
-
71
- const input = $("#" + this.inputId);
72
- input.on("change", () => {
73
- this.$emit("update:value", input.val());
74
- });
75
-
76
- input.val(this.value || "_ADD_");
77
- },
78
- });
79
- </script>
@@ -1,307 +0,0 @@
1
- <template>
2
- <div ref="container" class="container">
3
- <slot name="label">
4
- <NodeRedInputLabel
5
- v-if="label"
6
- :label="label"
7
- :icon="icon"
8
- :required="required"
9
- />
10
- </slot>
11
- <div class="editor-wrapper">
12
- <button
13
- ref="expand-button"
14
- class="red-ui-button red-ui-button-small expand-button"
15
- @click="onClickExpand"
16
- >
17
- <i class="fa fa-expand"></i>
18
- </button>
19
- <div :id="editorId" ref="editor"></div>
20
- </div>
21
- <div v-show="error" class="node-red-vue-input-error-message">
22
- {{ error }}
23
- </div>
24
- </div>
25
- </template>
26
-
27
- <script lang="ts">
28
- // TODO: expose editor apis
29
- import { defineComponent } from "vue";
30
- import NodeRedInputLabel from "./node-red-input-label.vue";
31
- export default defineComponent({
32
- components: { NodeRedInputLabel },
33
- props: {
34
- value: {
35
- type: String,
36
- default: "",
37
- },
38
- language: {
39
- type: String,
40
- default: "json",
41
- validator: function (value) {
42
- const allowedLanguages = [
43
- "abap",
44
- "apex",
45
- "azcli",
46
- "bat",
47
- "bicep",
48
- "cameligo",
49
- "clojure",
50
- "coffee",
51
- "cpp",
52
- "csharp",
53
- "csp",
54
- "css",
55
- "cypher",
56
- "dart",
57
- "dockerfile",
58
- "ecl",
59
- "elixir",
60
- "flow9",
61
- "freemarker2",
62
- "fsharp",
63
- "go",
64
- "graphql",
65
- "handlebars",
66
- "hcl",
67
- "html",
68
- "ini",
69
- "java",
70
- "javascript",
71
- "json",
72
- "julia",
73
- "kotlin",
74
- "less",
75
- "lexon",
76
- "liquid",
77
- "lua",
78
- "m3",
79
- "markdown",
80
- "mdx",
81
- "mips",
82
- "msdax",
83
- "mysql",
84
- "objective-c",
85
- "pascal",
86
- "pascaligo",
87
- "perl",
88
- "pgsql",
89
- "php",
90
- "pla",
91
- "postiats",
92
- "powerquery",
93
- "powershell",
94
- "protobuf",
95
- "pub",
96
- "python",
97
- "qsharp",
98
- "r",
99
- "razor",
100
- "redis",
101
- "redshift",
102
- "restructuredtext",
103
- "ruby",
104
- "rust",
105
- "sb",
106
- "scala",
107
- "scheme",
108
- "scss",
109
- "shell",
110
- "solidity",
111
- "sophia",
112
- "sparql",
113
- "sql",
114
- "st",
115
- "swift",
116
- "systemverilog",
117
- "tcl",
118
- "twig",
119
- "typescript",
120
- "typespec",
121
- "vb",
122
- "wgsl",
123
- "xml",
124
- "yaml",
125
- ];
126
- const isValid = allowedLanguages.includes(value);
127
- if (!isValid) {
128
- console.warn(
129
- `[WARN]: Invalid value for 'type' property: "${value}". ` +
130
- `Expected one of: ${allowedLanguages.join(", ")}`,
131
- );
132
- }
133
- return isValid;
134
- },
135
- },
136
- label: {
137
- type: String,
138
- default: "",
139
- },
140
- icon: {
141
- type: String,
142
- default: "",
143
- },
144
- required: {
145
- type: Boolean,
146
- default: false,
147
- },
148
- error: {
149
- type: String,
150
- default: "",
151
- },
152
- },
153
- emits: ["update:value"],
154
- editor: null,
155
- data() {
156
- const stateId = Math.random().toString(36).substring(2, 9);
157
- return {
158
- editorId: "node-red-editor-" + stateId,
159
- stateId,
160
- };
161
- },
162
- mounted() {
163
- // NOTE: jquery wrapper is used because RED.popover.tooltip needs it
164
- const expandButton = $(this.$refs["expand-button"]);
165
- RED.popover.tooltip(expandButton, RED._("node-red:common.label.expand"));
166
- this.mountEditor();
167
- this.createExpandeEditorTray();
168
- },
169
- beforeUnmount() {
170
- if (this.editorInstance) {
171
- try {
172
- this.editorInstance.destroy();
173
- } catch (err) {
174
- console.error(`Error destroying editor for ID ${this.editorId}:`, err);
175
- }
176
- this.editorInstance = null;
177
- }
178
- },
179
- methods: {
180
- mountEditor() {
181
- this.$nextTick(() => {
182
- const containerEl = this.$refs.container;
183
- const editorEl = this.$refs.editor;
184
-
185
- if (containerEl && editorEl) {
186
- try {
187
- const inlineHeight = containerEl.style.height;
188
- const inlineWidth = containerEl.style.width;
189
- if (inlineHeight) {
190
- editorEl.style.height = inlineHeight;
191
- } else {
192
- editorEl.style.height = "200px";
193
- }
194
-
195
- if (inlineWidth) {
196
- editorEl.style.width = inlineWidth;
197
- } else {
198
- editorEl.style.width = "100%";
199
- }
200
-
201
- this.createEditorInstance();
202
- } catch (e) {
203
- console.error(
204
- "[NodeRedEditorInput] Error setting initial editor style:",
205
- e,
206
- );
207
- this.createEditorInstance();
208
- }
209
- } else {
210
- console.error(
211
- "[NodeRedEditorInput] Container or Editor div refs not found on mount.",
212
- );
213
- }
214
- });
215
- },
216
- createEditorInstance() {
217
- this.editorInstance = RED.editor.createEditor({
218
- id: this.editorId,
219
- mode: this.language,
220
- value: this.value,
221
- });
222
- this.editorInstance.getSession().on("change", () => {
223
- const currentValue = this.editorInstance.getValue();
224
- if (currentValue !== this.value) {
225
- this.$emit("update:value", currentValue);
226
- }
227
- });
228
- },
229
- createExpandeEditorTray() {
230
- let expandedEditor;
231
-
232
- const onCancel = () => {
233
- setTimeout(() => {
234
- this.editorInstance.focus();
235
- }, 250);
236
- RED.tray.close();
237
- };
238
-
239
- const onDone = () => {
240
- expandedEditor.saveView();
241
- this.editorInstance.setValue(expandedEditor.getValue(), -1);
242
- setTimeout(() => {
243
- this.editorInstance.restoreView();
244
- this.editorInstance.focus();
245
- }, 250);
246
- RED.tray.close();
247
- };
248
-
249
- this.expandedEditorTray = {
250
- title: "Editor",
251
- focusElement: true,
252
- width: "Infinity",
253
- buttons: [
254
- {
255
- id: "node-dialog-cancel",
256
- text: RED._("common.label.cancel"),
257
- click: onCancel,
258
- },
259
- {
260
- id: "node-dialog-ok",
261
- text: RED._("common.label.done"),
262
- class: "primary",
263
- click: onDone,
264
- },
265
- ],
266
- open: (tray) => {
267
- const dialogForm = $(
268
- '<form id="dialog-form" class="form-horizontal" autocomplete="off"></form>',
269
- ).appendTo(tray.find(".red-ui-tray-body"));
270
- dialogForm.html(
271
- '<div id="expanded-editor-input" style="height: 100%"></div>',
272
- );
273
-
274
- expandedEditor = RED.editor.createEditor({
275
- id: "expanded-editor-input",
276
- stateId: this.stateId,
277
- mode: this.language,
278
- focus: true,
279
- value: this.value,
280
- });
281
- dialogForm.i18n();
282
- },
283
- close: function () {
284
- expandedEditor.destroy();
285
- },
286
- };
287
- },
288
- onClickExpand() {
289
- RED.tray.show(this.expandedEditorTray);
290
- },
291
- },
292
- });
293
- </script>
294
- <style scoped>
295
- .editor-wrapper {
296
- position: relative;
297
- }
298
-
299
- .expand-button {
300
- position: absolute;
301
- top: -23px;
302
- right: 0px;
303
- z-index: 10;
304
- transition: color 0.3s ease;
305
- cursor: pointer;
306
- }
307
- </style>
@@ -1,53 +0,0 @@
1
- <template>
2
- <span class="nrg-label">
3
- <i v-if="icon" :class="iconClass"></i>
4
- <slot>{{ label }}</slot>
5
- <span v-if="required" class="nrg-required">*</span>
6
- </span>
7
- </template>
8
-
9
- <script lang="ts">
10
- import { defineComponent } from "vue";
11
-
12
- export default defineComponent({
13
- name: "NodeRedInputLabel",
14
- props: {
15
- label: {
16
- type: String,
17
- default: "",
18
- },
19
- icon: {
20
- type: String,
21
- default: "",
22
- },
23
- required: {
24
- type: Boolean,
25
- default: false,
26
- },
27
- },
28
- computed: {
29
- iconClass(): string {
30
- if (!this.icon) return "";
31
- const name = this.icon.startsWith("fa-") ? this.icon : `fa-${this.icon}`;
32
- return `fa ${name}`;
33
- },
34
- },
35
- });
36
- </script>
37
-
38
- <style scoped>
39
- .nrg-label {
40
- display: inline-block;
41
- width: 100%;
42
- cursor: default;
43
- }
44
-
45
- .nrg-label i {
46
- margin-right: 5px;
47
- }
48
-
49
- .nrg-required {
50
- color: var(--red-ui-text-color-error);
51
- margin-left: 2px;
52
- }
53
- </style>