Skip to content

feat: Vue and Vuetify editing in the ui-template code editor - #2290

Merged
n-lark merged 2 commits into
mainfrom
feat/ui-template-vue-script
Oct 21, 2026
Merged

n-lark merged 2 commits into
mainfrom
feat/ui-template-vue-script

Conversation

@Steve-Mcl

Copy link
Copy Markdown
Contributor

Description

Related to node-red/nr-monaco-build#9 and node-red/node-red#5994

Summary

People regularly ask for better editing in ui-template (e.g. https://discourse.nodered.org/t/weather-card-struggling-with-methods/85434/6). Today the editor treats a Vue template as plain HTML: no Vue syntax and no help with Vuetify components.

This adds a vue-sfc language to Node-RED's Monaco editor, shipped with the Dashboard as an editor plugin. Nothing is added to Node-RED core's editor bundle, which was the concern raised in node-red/nr-monaco-build#9. With it, the ui-template editor gets:

  • Vue highlighting: directives, bindings (:prop), events (@click), slots (#activator) and {{ }} expressions highlighted as JavaScript. <script> and <style> blocks are highlighted too, including lang="ts" / lang="scss" / lang="less".
  • Vuetify completion and hover:
    • Typing <v- lists the components.
    • Inside a tag you get its props, bound props (:), events (@), Vuetify directives, and, on a <template> inside a component, that component's slots (#).
    • Props with a fixed set of values (e.g. variant) suggest those values.
    • Hover shows the type, default, description and a link to the Vuetify API docs.
  • Vue's own directives and components (v-if, v-for, <transition>, ...), with links to the Vue docs.
  • Plain HTML completion, hover and formatting, as before, from Monaco's html worker.

It works offline, and works with any Node-RED that uses Monaco. Editors set to ACE keep the plain HTML mode.

How it works

  • nodes/plugins/vue-editor.html: a small editor plugin (registered under node-red.plugins in package.json). At startup it only registers the vue-sfc language and Monaco's HTML language service for it. Everything else loads the first time a ui-template editor opens.
  • resources/vue-editor/vue-sfc.mjs: loaded on first use. The tokenizer, which is adapted from Monaco's HTML tokenizer (MIT, credited in the file), plus the Vue/Vuetify completion and hover providers. Only public Monaco APIs are used.
  • scripts/build-vue-editor-data.js: generates resources/vue-editor/vuetify.json from vuetify/dist/json/web-types.json, so the editor always matches the Vuetify version the Dashboard bundles. It runs as part of npm run build (and build:dev / dev), and the output is gitignored. 192 components, 401 KB raw / 66 KB gzipped, with descriptions de-duplicated into a string table.
  • nodes/widgets/ui_template.html: uses vue-sfc when the editor is Monaco and the plugin is loaded, html otherwise. The expand editor uses the same mode.
  • package.json: adds resources/* to files, the build step, and the plugin entry.

Choices worth flagging:

  • A new language id (vue-sfc) rather than vue. Node-RED maps the vue mode to html, while other ids pass through, so this needs no change in core.
  • Vuetify completion runs on the main thread, not through the html worker's custom data. The HTML service filters attribute completions on the typed text, so :col, @cli or #act would never match color etc.
  • A worker mapping in the plugin. Monaco asks for a language's worker by its language id, and Node-RED's Monaco only knows its built-in ids. The plugin maps vue-sfc to the html worker. feat: add JavaScript and CSS support to script and style blocks in HT… node-red/node-red#5994 does this in core for newer versions.

Package size: about 434 KB more unpacked (the plugin 3.6 KB, vue-sfc.mjs 29 KB, vuetify.json 401 KB) - BUT - it is only loaded when a ui-template editor opens!

What's next

node-red/node-red#5994 adds JavaScript and CSS support to <script> and <style> blocks in Node-RED's HTML editors, with an API that languages like this one can opt into. A follow-up PR builds on it so that, on Node-RED versions that have it, ui-template also gets JavaScript completion and error checking in <script>, with this typed for the component (this.send(), this.msg, data/computed/methods), and CSS completion in <style>. This PR doesn't depend on it.

Testing

  • New unit tests in test/resources/vue-editor.spec.js (20) for cursor context (including skipping script/style/comments/interpolation), parent tag for slots, completions, values and hover. The full suite passes.
  • Lint clean on new/changed files.
  • Checked in the browser with Playwright on Node-RED 5 (Monaco 0.57) and Node-RED 4.1.13 (Monaco 0.52.2):
    • inline and expand editors use vue-sfc;
    • bound props, events, tags, prop values, slots in <template #> and hover all work;
    • HTML completion comes from the html worker;
    • switching the template type to the CSS scopes uses css, and back again uses vue-sfc;
    • Done saves with noerr 0;
    • ACE stays on HTML;
    • no console errors.

Notes

  • FlowFuse Expert: nr-assistant registers its ui-template CodeLens and inline completions for the html language. With ui-template on vue-sfc, those won't show in the template part until nr-assistant also accepts vue-sfc. That's a small change in nr-assistant (CodeLens registration, supportedInlineCompletions, and isInsideComment). I'll PR that as well.
  • The docs (docs/en/nodes/widgets/ui-template.md) get a short "Code Editor" section. I've only done the English page.

Related Issue(s)

Checklist

  • I have read the contribution guidelines
  • Suitable unit/system level tests have been added and they pass
  • Documentation has been updated
    • Upgrade instructions
    • Configuration details
    • Concepts
  • Changes flowforge.yml?
    • Issue/PR raised on FlowFuse/helm to update ConfigMap Template
    • Issue/PR raised on FlowFuse/CloudProject to update values for Staging/Production
  • Link to Changelog Entry PR, or note why one is not needed.

Labels

  • Includes a DB migration? -> add the area:migration label

@Steve-Mcl

Copy link
Copy Markdown
Contributor Author
  • FlowFuse Expert: nr-assistant registers its ui-template CodeLens and inline completions for the html language. With ui-template on vue-sfc, those won't show in the template part until nr-assistant also accepts vue-sfc. That's a small change in nr-assistant (CodeLens registration, supportedInlineCompletions, and isInsideComment)

I've looked at nr-assistant and the compatibility is something a bit more than first thought!

Monaco picks inline-completion and CodeLens providers by the model's language. Up to and including 0.20.0, nr-assistant only registers them for html, javascript, css and sql. Once ui-template switches to vue-sfc, none of them run, so the ui-template editor loses FIM completions everywhere (template, <script> and <style>) and the "Ask the FlowFuse Expert" CodeLens no longer appears at the top.

proposal

I'll add vue-sfc support to nr-assistant as a feat:, so it releases as 0.21.0. It keeps the html registration, so 0.21.0 still works with older dashboard versions. Installs already on 0.20.0 or earlier can't be fixed from nr-assistant, though. Anyone who updates dashboard first would lose Expert completions in ui-template.

To cover that, this PR will only use vue-sfc when the installed nr-assistant can handle it:

// nr-assistant before 0.21.0 only provides Expert support for html
function assistantSupportsVueSfc () {
    const version = RED.plugins.getModule('@flowfuse/nr-assistant')?.version
    if (!version) return true
    const [major, minor] = version.split('.').map(n => parseInt(n, 10))
    return major > 0 || minor >= 21
}

const htmlMode = RED.editor.codeEditor?.editor?.type === 'monaco' && assistantSupportsVueSfc() && vueEditor?.ensure?.() ? 'ace/mode/vue-sfc' : 'ace/mode/html'
nr-assistant ui-template language Expert completions
not installed vue-sfc n/a
0.20.0 or earlier html yes
0.21.0 or later vue-sfc yes

Users on an older nr-assistant miss the Vue IntelliSense until it updates, but they keep FIM and the CodeLens. The FIM backend will accept vue-sfc as a language before any of this ships.

WDYT @n-lark

@Steve-Mcl

Copy link
Copy Markdown
Contributor Author

Associated PR for nr-assistant: FlowFuse/nr-assistant#426

@n-lark n-lark left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good thank you for adding this! Here is everything I tested:

Tested on: Node-RED 5.0.4 (Monaco 0.55, without node-red#5994) and 5.0.8 (main, Monaco 0.57, with #5994).

Build:

  • Every build regenerates vuetify.json from the installed Vuetify (3.11.3, 192 components)
  • Rebuilds produce identical output
  • The npm package includes vuetify.json
  • The release workflow builds before publishing

Both versions:

  • Dashboard renders the template, mounted runs, Send reaches Node-RED
  • Edit, Done and Deploy work
  • Inline and expand editors use vue-sfc
  • Completion: Vuetify tags, props, bound props, events, slots, prop values, Vue directives
  • Hover shows prop types, defaults and docs
  • CSS scopes switch the editor to css and back
  • Editor files load only when a ui-template editor opens
  • No console errors

5.0.4 only:

  • ACE stays on HTML
  • Editor still works if vuetify.json is missing

5.0.8 only:

  • <script> and <style> get highlighting only, with no JS/CSS completion (#2291 adds that)

@n-lark

n-lark commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

Hey @Steve-Mcl the proposal and nr-assistant FlowFuse/nr-assistant#426 looks good to me.

One nit - I'm pretty sure RED.plugins.getModule doesn't exist before Node-RED 4.0, and Dashboard supports >=3.0.0, so it needs RED.plugins.getModule?.(...) to avoid throwing.

@Steve-Mcl

Copy link
Copy Markdown
Contributor Author

thanks Noley. I am gonna adjust the detection method by adding an explicit flag in nr-assistant and pick that up here (rather than rely on version numbers and support for getModule)

@Steve-Mcl

Copy link
Copy Markdown
Contributor Author

I've dropped the version check and gone with a capability flag instead:

  • nr-assistant 0.21.0 sets supportsVueSfc: true on its flowfuse-nr-assistant plugin.
  • The Dashboard vue-editor plugin reads it with RED.plugins.getPlugin, which has been in the editor since Node-RED 1.3.
  • No nr-assistant means vue-sfc. An nr-assistant without the flag keeps html. An nr-assistant with the flag gets vue-sfc.

The check now lives in nodes/plugins/vue-editor.html as useVueSfc(), so ui-template just calls vueEditor?.useVueSfc?.(). I tested all three cases in the editor.

@Steve-Mcl
Steve-Mcl requested a review from n-lark October 14, 2026 18:15
@Steve-Mcl
Steve-Mcl force-pushed the feat/ui-template-vue-script branch from ec1839c to c7540f4 Compare October 15, 2026 12:58
@Steve-Mcl

Copy link
Copy Markdown
Contributor Author

Hey @n-lark FYI, the related work in Node-RED is merged so this new goodness will be available really soon (I think there may be a release today 🤞)

Once merged and new Node-RED is released, a final test run of dashboard main would be a good call in case I missed anyting - me thinks - i've logged that in me noodle as a TODO.

@n-lark n-lark left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work

@Steve-Mcl
Steve-Mcl force-pushed the feat/ui-template-vue-script branch 2 times, most recently from f1ac76a to 3eb30a7 Compare October 17, 2026 08:00
@n-lark

n-lark commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

Did another round of testing on this with the latest node-red release 5.0.9 - everything looks good to go.

#2290: Vue / Vuetify editing

  • Inline and expand editors use vue-sfc
  • Editor files load only when a ui-template editor opens
  • Completion: <v- tags, props, :color, @click, variant values, #activator slot, Vue directives, <transition>, HTML tags
  • Hover on a prop shows type, default, docs link
  • CSS scopes switch to css and back
  • nr-assistant: none → vue-sfc, without supportsVueSfc → html, with it → vue-sfc (stubbed plugin)
  • ACE stays on HTML, no editor files load

#2291: <script> / <style>

  • this. lists data/computed/methods first, then Dashboard members (msg, send, $socket, ...)
  • Raw (non-component) scripts get the same this
  • Types and signatures: this.count is number, this.send( shows its signature
  • Script and CSS errors flagged on the right line
  • No false error for a comment after export default {}
  • Done with errors marks the node invalid; fixing clears it
  • <style> CSS completion
  • Hidden models cleaned up on Done and Cancel
  • Typing stays fast on a 148 KB template

Runtime

  • Template renders, scoped style applies
  • mounted and button send() reach the flow
  • No console errors

@Steve-Mcl

Copy link
Copy Markdown
Contributor Author

Thank you thank you thank you 🙇

@Steve-Mcl

Copy link
Copy Markdown
Contributor Author

@n-lark feel free to merge and release at your leisure. (or let me know if you want me to)

The ui-template editor treats a Vue template as plain HTML: no Vue syntax, no help
with Vuetify components. This adds a `vue-sfc` language for Node-RED's Monaco editor,
shipped as an editor plugin with the Dashboard, so nothing is added to Node-RED core.

- Vue highlighting: directives, bindings, events, slots and {{ }} as JavaScript;
  <script> and <style> blocks (TS, SCSS and Less with lang=)
- Completion and hover for Vuetify components, props, bound props, events, slots,
  prop values and directives, plus Vue's own directives and components
- Vuetify data is generated at build time from the Vuetify version the Dashboard
  bundles (scripts/build-vue-editor-data.js), and loaded only when a ui-template
  editor is first opened
- Plain HTML completion, hover and formatting from Monaco's html worker
- ui-template uses the new language with Monaco; ACE keeps the HTML mode
nr-assistant before 0.21.0 only registers its Expert completions and
CodeLens for html, so switching ui-template to vue-sfc would drop them.
nr-assistant 0.21.0 sets supportsVueSfc on its plugin. Read that with
RED.plugins.getPlugin, which exists back to Node-RED 1.3, rather than
RED.plugins.getModule, which only arrived in 4.0.

- vue-sfc when nr-assistant isn't installed or sets supportsVueSfc
- html when an older nr-assistant is installed
- the check lives in the vue-editor plugin as useVueSfc()
@n-lark
n-lark force-pushed the feat/ui-template-vue-script branch from 3eb30a7 to f0ecfae Compare October 21, 2026 15:04
@n-lark
n-lark merged commit c0be053 into main Oct 21, 2026
5 checks passed
@n-lark
n-lark deleted the feat/ui-template-vue-script branch October 21, 2026 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants