|
24 | 24 | const modulesAllowed = RED.settings.functionExternalModules !== false |
25 | 25 | const assistantOptions = { |
26 | 26 | enabled: false, |
| 27 | + tablesEnabled: false, |
27 | 28 | requestTimeout: AI_TIMEOUT |
28 | 29 | } |
29 | 30 | let assistantInitialised = false |
|
42 | 43 | if (topic === 'nr-assistant/initialise') { |
43 | 44 | assistantOptions.enabled = !!msg?.enabled |
44 | 45 | assistantOptions.requestTimeout = msg?.requestTimeout || AI_TIMEOUT |
| 46 | + assistantOptions.tablesEnabled = msg?.tablesEnabled === true |
45 | 47 | initAssistant(msg) |
46 | 48 | RED.actions.add('flowfuse-nr-assistant:function-builder', showFunctionBuilderPrompt, { label: '@flowfuse/nr-assistant/flowfuse-nr-assistant:function-builder.action.label' }) |
47 | 49 | setMenuShortcutKey('ff-assistant-function-builder', 'red-ui-workspace', 'ctrl-alt-f', 'flowfuse-nr-assistant:function-builder') |
|
69 | 71 | } |
70 | 72 | RED.plugins.registerPlugin('flowfuse-nr-assistant', plugin) |
71 | 73 |
|
72 | | - function initAssistant () { |
| 74 | + function initAssistant (options) { |
73 | 75 | if (assistantInitialised) { |
74 | 76 | return |
75 | 77 | } |
|
88 | 90 | const jsonCommandId = 'nr-assistant-json-inline' |
89 | 91 | const cssCommandId = 'nr-assistant-css-inline' |
90 | 92 | const db2uiTemplateCommandId = 'nr-assistant-html-dashboard2-template-inline' |
| 93 | + const ffTablesNodeCommandId = 'nr-assistant-ff-tables-node-inline' |
91 | 94 |
|
92 | 95 | debug('registering code lens providers...') |
93 | 96 |
|
|
264 | 267 | } |
265 | 268 | }) |
266 | 269 |
|
| 270 | + assistantOptions.tablesEnabled && monaco.languages.registerCodeLensProvider('sql', { |
| 271 | + provideCodeLenses: function (model, token) { |
| 272 | + debug('SQL CodeLens provider called', model, token) |
| 273 | + const thisEditor = getMonacoEditorForModel(model) |
| 274 | + if (!thisEditor) { |
| 275 | + return |
| 276 | + } |
| 277 | + const node = RED.view.selection()?.nodes?.[0] |
| 278 | + // only support tables query nodes for now |
| 279 | + if (!node || node.type !== 'tables-query' || node._def?.set?.id !== '@flowfuse/nr-tables-nodes/tables-query') { |
| 280 | + return |
| 281 | + } |
| 282 | + return { |
| 283 | + lenses: [ |
| 284 | + { |
| 285 | + range: { |
| 286 | + startLineNumber: 1, |
| 287 | + startColumn: 1, |
| 288 | + endLineNumber: 2, |
| 289 | + endColumn: 1 |
| 290 | + }, |
| 291 | + id: ffTablesNodeCommandId |
| 292 | + } |
| 293 | + ], |
| 294 | + dispose: () => { } |
| 295 | + } |
| 296 | + }, |
| 297 | + resolveCodeLens: function (model, codeLens, token) { |
| 298 | + debug('SQL CodeLens resolve called', model, codeLens, token) |
| 299 | + if (codeLens.id !== ffTablesNodeCommandId) { |
| 300 | + return codeLens |
| 301 | + } |
| 302 | + codeLens.command = { |
| 303 | + id: codeLens.id, |
| 304 | + title: 'Ask the FlowFuse Assistant 🪄', |
| 305 | + tooltip: 'Click to ask FlowFuse Assistant for help with PostgreSQL', |
| 306 | + arguments: [model, codeLens, token] |
| 307 | + } |
| 308 | + return codeLens |
| 309 | + } |
| 310 | + }) |
| 311 | + |
267 | 312 | debug('registering commands...') |
268 | 313 |
|
269 | 314 | monaco.editor.registerCommand(funcCommandId, function (accessor, model, codeLens, token) { |
|
581 | 626 | } |
582 | 627 | }) |
583 | 628 |
|
| 629 | + assistantOptions.tablesEnabled && monaco.editor.registerCommand(ffTablesNodeCommandId, function (accessor, model, codeLens, token) { |
| 630 | + debug('running command', ffTablesNodeCommandId) |
| 631 | + const node = RED.view.selection()?.nodes?.[0] |
| 632 | + if (!node) { |
| 633 | + console.warn('No node selected') // should not happen |
| 634 | + return |
| 635 | + } |
| 636 | + if (!assistantOptions.enabled) { |
| 637 | + RED.notify(plugin._('errors.assistant-not-enabled'), 'warning') |
| 638 | + return |
| 639 | + } |
| 640 | + const thisEditor = getMonacoEditorForModel(model) |
| 641 | + if (thisEditor) { |
| 642 | + if (!document.body.contains(thisEditor.getDomNode())) { |
| 643 | + console.warn('Editor is no longer in the DOM, cannot proceed.') |
| 644 | + return |
| 645 | + } |
| 646 | + |
| 647 | + // FUTURE: for including selected text in the context for features like "fix my code", "refactor this", "what is this?" etc |
| 648 | + // const userSelection = triggeredEditor.getSelection() |
| 649 | + // const selectedText = model.getValueInRange(userSelection) |
| 650 | + /** @type {PromptOptions} */ |
| 651 | + const promptOptions = { |
| 652 | + method: 'flowfuse-tables-query', |
| 653 | + lang: 'sql', |
| 654 | + dialect: 'g', |
| 655 | + type: node.type |
| 656 | + // selectedText: model.getValueInRange(userSelection) |
| 657 | + } |
| 658 | + /** @type {PromptUIOptions} */ |
| 659 | + const uiOptions = { |
| 660 | + title: 'FlowFuse Assistant : FlowFuse Query', |
| 661 | + explanation: 'The FlowFuse Assistant can help you write SQL queries.', |
| 662 | + description: 'Enter a short description of what you want it to do.' |
| 663 | + } |
| 664 | + doPrompt(node, thisEditor, promptOptions, uiOptions, (error, response) => { |
| 665 | + if (error) { |
| 666 | + console.warn('Error processing request', error) |
| 667 | + return |
| 668 | + } |
| 669 | + debug('sql response', response) |
| 670 | + const responseData = response?.data |
| 671 | + if (responseData && responseData.sql) { |
| 672 | + // ensure the editor is still present in the DOM |
| 673 | + if (!document.body.contains(thisEditor.getDomNode())) { |
| 674 | + console.warn('Editor is no longer in the DOM') |
| 675 | + return |
| 676 | + } |
| 677 | + thisEditor.focus() |
| 678 | + const currentSelection = thisEditor.getSelection() |
| 679 | + thisEditor.executeEdits('', [ |
| 680 | + { |
| 681 | + range: new monaco.Range(currentSelection.startLineNumber, currentSelection.startColumn, currentSelection.endLineNumber, currentSelection.endColumn), |
| 682 | + text: responseData.sql |
| 683 | + } |
| 684 | + ]) |
| 685 | + } |
| 686 | + }) |
| 687 | + } else { |
| 688 | + console.warn('Could not find editor for model', model.uri.toString()) |
| 689 | + } |
| 690 | + }) |
| 691 | + |
584 | 692 | const toolbarMenuButton = $('<li><a id="red-ui-header-button-ff-ai" class="button" href="#"></a></li>') |
585 | 693 | const toolbarMenuButtonAnchor = toolbarMenuButton.find('a') |
586 | 694 | const deployButtonLi = $('#red-ui-header-button-deploy').closest('li') |
|
0 commit comments