@flowfuse/nr-assistant 0.2.2-480c08b-202506180948.0 → 0.3.0

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 (3) hide show
  1. package/README.md +10 -5
  2. package/index.html +207 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -16,17 +16,22 @@ This plugin is designed to assist users of the FlowFuse platform by providing to
16
16
  * Function node Code Lens
17
17
  * JSON generation in all typed inputs and JSON editors (like the inject node, change node, template node, etc)
18
18
  * Flows Explainer
19
+ * HTML, VUE, and CSS generation in FlowFuse Dashboard ui-template nodes
19
20
 
20
21
  ### Function Builder
21
- ![flowfuse-assistant-function builder](https://github.com/user-attachments/assets/5efc4c24-a4e6-4d0c-b836-aebf97d3ca02)
22
-
22
+ ![flowfuse-assistant-assistant-builder](https://github.com/user-attachments/assets/6520eeaf-83f5-466e-ad32-6b4ae1d62954)
23
23
 
24
24
  ### JSON generator
25
- ![flowfuse-assistant-json-generator](https://github.com/user-attachments/assets/d3271f64-a733-48dd-924b-0a946b9a6e7f)
26
-
25
+ ![flowfuse-assistant-json-generator](https://github.com/user-attachments/assets/9d4bf3ef-7ea8-4e72-9e04-73712d5323e3)
27
26
 
28
27
  ### Flows Explainer
29
- ![flowfuse-assistant-flow-explainer](https://github.com/user-attachments/assets/3a8c17bb-066b-4264-9ad1-3a031bd2afee)
28
+ ![flowfuse-assistant-flow-explainer](https://github.com/user-attachments/assets/20f5490f-469f-4f95-b63c-cdf216139bd0)
29
+
30
+ ### FlowFuse Dashboard UI Template Assistant
31
+ ![flowfuse-assistant-ui-template](https://github.com/user-attachments/assets/c6810553-40c0-429e-aa6b-039317b1dc30)
32
+
33
+ ### FlowFuse Dashboard UI CSS Assistant
34
+ ![flowfuse-assistant-css](https://github.com/user-attachments/assets/fea87030-294e-4bce-a9ce-146249ee0459)
30
35
 
31
36
 
32
37
  ## Installation
package/index.html CHANGED
@@ -80,6 +80,8 @@
80
80
 
81
81
  const funcCommandId = 'nr-assistant-fn-inline'
82
82
  const jsonCommandId = 'nr-assistant-json-inline'
83
+ const cssCommandId = 'nr-assistant-css-inline'
84
+ const db2uiTemplateCommandId = 'nr-assistant-html-dashboard2-template-inline'
83
85
 
84
86
  debug('registering code lens providers...')
85
87
 
@@ -175,6 +177,87 @@
175
177
  }
176
178
  })
177
179
 
180
+ monaco.languages.registerCodeLensProvider('css', {
181
+ provideCodeLenses: function (model, token) {
182
+ debug('CSS CodeLens provider called', model, token)
183
+ const thisEditor = getMonacoEditorForModel(model)
184
+ const node = RED.view.selection()?.nodes?.[0]
185
+ if (!thisEditor || !node) {
186
+ return
187
+ }
188
+
189
+ return {
190
+ lenses: [
191
+ {
192
+ range: {
193
+ startLineNumber: 1,
194
+ startColumn: 1,
195
+ endLineNumber: 2,
196
+ endColumn: 1
197
+ },
198
+ id: cssCommandId
199
+ }
200
+ ],
201
+ dispose: () => { }
202
+ }
203
+ },
204
+ resolveCodeLens: function (model, codeLens, token) {
205
+ debug('CSS CodeLens resolve called', model, codeLens, token)
206
+ if (codeLens.id !== cssCommandId) {
207
+ return codeLens
208
+ }
209
+ codeLens.command = {
210
+ id: codeLens.id,
211
+ title: 'Ask the FlowFuse Assistant 🪄',
212
+ tooltip: 'Click to ask FlowFuse Assistant for help with CSS',
213
+ arguments: [model, codeLens, token]
214
+ }
215
+ return codeLens
216
+ }
217
+ })
218
+
219
+ monaco.languages.registerCodeLensProvider('html', {
220
+ provideCodeLenses: function (model, token) {
221
+ debug('HTML CodeLens provider called', model, token)
222
+ const thisEditor = getMonacoEditorForModel(model)
223
+ if (!thisEditor) {
224
+ return
225
+ }
226
+ const node = RED.view.selection()?.nodes?.[0]
227
+ // only support dashboard2 ui-template nodes for now
228
+ if (!node || node.type !== 'ui-template' || node._def?.set?.id !== '@flowfuse/node-red-dashboard/ui-template') {
229
+ return
230
+ }
231
+ return {
232
+ lenses: [
233
+ {
234
+ range: {
235
+ startLineNumber: 1,
236
+ startColumn: 1,
237
+ endLineNumber: 2,
238
+ endColumn: 1
239
+ },
240
+ id: db2uiTemplateCommandId
241
+ }
242
+ ],
243
+ dispose: () => { }
244
+ }
245
+ },
246
+ resolveCodeLens: function (model, codeLens, token) {
247
+ debug('HTML CodeLens resolve called', model, codeLens, token)
248
+ if (codeLens.id !== db2uiTemplateCommandId) {
249
+ return codeLens
250
+ }
251
+ codeLens.command = {
252
+ id: codeLens.id,
253
+ title: 'Ask the FlowFuse Assistant 🪄',
254
+ tooltip: 'Click to ask FlowFuse Assistant for help with VUE or HTML',
255
+ arguments: [model, codeLens, token]
256
+ }
257
+ return codeLens
258
+ }
259
+ })
260
+
178
261
  debug('registering commands...')
179
262
 
180
263
  monaco.editor.registerCommand(funcCommandId, function (accessor, model, codeLens, token) {
@@ -368,6 +451,130 @@
368
451
  }
369
452
  })
370
453
 
454
+ monaco.editor.registerCommand(cssCommandId, function (accessor, model, codeLens, token) {
455
+ debug('running command', cssCommandId)
456
+ const node = RED.view.selection()?.nodes?.[0]
457
+ if (!node) {
458
+ console.warn('No node selected') // should not happen
459
+ return
460
+ }
461
+ if (!assistantOptions.enabled) {
462
+ RED.notify(plugin._('errors.assistant-not-enabled'), 'warning')
463
+ return
464
+ }
465
+ const thisEditor = getMonacoEditorForModel(model)
466
+ if (thisEditor) {
467
+ if (!document.body.contains(thisEditor.getDomNode())) {
468
+ console.warn('Editor is no longer in the DOM, cannot proceed.')
469
+ return
470
+ }
471
+
472
+ // FUTURE: for including selected text in the context for features like "fix my code", "refactor this", "what is this?" etc
473
+ // const userSelection = triggeredEditor.getSelection()
474
+ // const selectedText = model.getValueInRange(userSelection)
475
+ /** @type {PromptOptions} */
476
+ const promptOptions = {
477
+ method: 'css',
478
+ lang: 'css',
479
+ type: node.type
480
+ // selectedText: model.getValueInRange(userSelection)
481
+ }
482
+ /** @type {PromptUIOptions} */
483
+ const uiOptions = {
484
+ title: 'FlowFuse Assistant : CSS',
485
+ explanation: 'The FlowFuse Assistant can help you write CSS.',
486
+ description: 'Enter a short description of what you want it to do.'
487
+ }
488
+ doPrompt(node, thisEditor, promptOptions, uiOptions, (error, response) => {
489
+ if (error) {
490
+ console.warn('Error processing request', error)
491
+ return
492
+ }
493
+ debug('css response', response)
494
+ const responseData = response?.data
495
+ if (responseData && responseData.css) {
496
+ // ensure the editor is still present in the DOM
497
+ if (!document.body.contains(thisEditor.getDomNode())) {
498
+ console.warn('Editor is no longer in the DOM')
499
+ return
500
+ }
501
+ thisEditor.focus()
502
+ const currentSelection = thisEditor.getSelection()
503
+ thisEditor.executeEdits('', [
504
+ {
505
+ range: new monaco.Range(currentSelection.startLineNumber, currentSelection.startColumn, currentSelection.endLineNumber, currentSelection.endColumn),
506
+ text: responseData.css
507
+ }
508
+ ])
509
+ }
510
+ })
511
+ } else {
512
+ console.warn('Could not find editor for model', model.uri.toString())
513
+ }
514
+ })
515
+
516
+ monaco.editor.registerCommand(db2uiTemplateCommandId, function (accessor, model, codeLens, token) {
517
+ debug('running command', db2uiTemplateCommandId)
518
+ const node = RED.view.selection()?.nodes?.[0]
519
+ if (!node) {
520
+ console.warn('No node selected') // should not happen
521
+ return
522
+ }
523
+ if (!assistantOptions.enabled) {
524
+ RED.notify(plugin._('errors.assistant-not-enabled'), 'warning')
525
+ return
526
+ }
527
+ const thisEditor = getMonacoEditorForModel(model)
528
+ if (thisEditor) {
529
+ if (!document.body.contains(thisEditor.getDomNode())) {
530
+ console.warn('Editor is no longer in the DOM, cannot proceed.')
531
+ return
532
+ }
533
+
534
+ // FUTURE: for including selected text in the context for features like "fix my code", "refactor this", "what is this?" etc
535
+ // const userSelection = triggeredEditor.getSelection()
536
+ // const selectedText = model.getValueInRange(userSelection)
537
+ /** @type {PromptOptions} */
538
+ const promptOptions = {
539
+ method: 'dashboard2-template',
540
+ lang: 'html',
541
+ type: node.type
542
+ // selectedText: model.getValueInRange(userSelection)
543
+ }
544
+ /** @type {PromptUIOptions} */
545
+ const uiOptions = {
546
+ title: 'FlowFuse Assistant : Dashboard 2 UI Template',
547
+ explanation: 'The FlowFuse Assistant can help you write HTML, VUE and JavaScript.',
548
+ description: 'Enter a short description of what you want it to do.'
549
+ }
550
+ doPrompt(node, thisEditor, promptOptions, uiOptions, (error, response) => {
551
+ if (error) {
552
+ console.warn('Error processing request', error)
553
+ return
554
+ }
555
+ debug('html response', response)
556
+ const responseData = response?.data
557
+ if (responseData && responseData.html) {
558
+ // ensure the editor is still present in the DOM
559
+ if (!document.body.contains(thisEditor.getDomNode())) {
560
+ console.warn('Editor is no longer in the DOM')
561
+ return
562
+ }
563
+ thisEditor.focus()
564
+ const currentSelection = thisEditor.getSelection()
565
+ thisEditor.executeEdits('', [
566
+ {
567
+ range: new monaco.Range(currentSelection.startLineNumber, currentSelection.startColumn, currentSelection.endLineNumber, currentSelection.endColumn),
568
+ text: responseData.html
569
+ }
570
+ ])
571
+ }
572
+ })
573
+ } else {
574
+ console.warn('Could not find editor for model', model.uri.toString())
575
+ }
576
+ })
577
+
371
578
  const toolbarMenuButton = $('<li><a id="red-ui-header-button-ff-ai" class="button" href="#"></a></li>')
372
579
  const toolbarMenuButtonAnchor = toolbarMenuButton.find('a')
373
580
  const deployButtonLi = $('#red-ui-header-button-deploy').closest('li')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowfuse/nr-assistant",
3
- "version": "0.2.2-480c08b-202506180948.0",
3
+ "version": "0.3.0",
4
4
  "description": "FlowFuse Node-RED assistant plugin",
5
5
  "main": "index.js",
6
6
  "scripts": {