-
Notifications
You must be signed in to change notification settings - Fork 18
[4973] Migrate /integrations/ to Nuxt #5050
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
n-lark
wants to merge
4
commits into
4972-rework-install
Choose a base branch
from
4973-nuxt-migration
base: 4972-rework-install
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
447377b
Migrate /integrations/ and /integrations/{id}/ from Eleventy to Nuxt …
n-lark aa29b16
Merge remote-tracking branch 'origin/4972-rework-install' into 4973-n…
n-lark c2a96e2
Anchor integrations cache on cwd, not import.meta.url
n-lark 69f61de
Merge remote-tracking branch 'origin/4972-rework-install' into 4973-n…
n-lark File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| <script setup> | ||
| defineProps({ | ||
| // Null while the catalog fetch is in flight; resolves to a number after. | ||
| count: { validator: v => v === null || typeof v === 'number', default: null }, | ||
| pressed: { type: Boolean, required: true } | ||
| }) | ||
| defineEmits(['toggle']) | ||
| </script> | ||
|
|
||
| <template> | ||
| <section class="certified-hero mb-8"> | ||
| <div class="container m-auto md:max-w-6xl"> | ||
| <div class="certified-hero--card rounded-lg border-[3px] border-indigo-200 p-6 md:p-10 grid md:grid-cols-12 gap-8 md:gap-10 items-start"> | ||
| <div class="md:col-span-5"> | ||
| <span class="certified-eyebrow"> | ||
| <IntegrationsCertifiedIcon /> | ||
| <span>FlowFuse Certified</span> | ||
| </span> | ||
| <h2 class="certified-hero--title text-balance mb-8"> | ||
| Certified Nodes, backed by their authors and supported long-term | ||
| </h2> | ||
| <p class="certified-hero--lede"> | ||
| Choosing a Node-RED node for production raises questions you can't always answer from a README. Is it actively maintained? Is it secure? Will the maintainer still be around in two years? Certified Nodes answer those questions. | ||
| </p> | ||
| <div class="certified-hero--actions mt-8"> | ||
| <button | ||
| id="certified-pill-toggle" | ||
| type="button" | ||
| class="ff-btn ff-btn--primary uppercase certified-toggle" | ||
| :aria-pressed="pressed ? 'true' : 'false'" | ||
| @click="$emit('toggle')" | ||
| > | ||
| <IntegrationsCertifiedIcon /> | ||
| <span>Show only Certified</span> | ||
| <span v-if="count !== null" class="certified-toggle--count"> ({{ count }})</span> | ||
| </button> | ||
| <!-- TODO: repoint to a proper FlowFuse-owned Certified Nodes explainer page when one exists. A year-old blog post is not the long-term destination. --> | ||
| <a | ||
| class="certified-hero--link inline-flex items-center gap-1 uppercase" | ||
| href="/blog/2025/07/certified-nodes-v2/" | ||
| > | ||
| Learn more | ||
| <svg | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| fill="none" | ||
| viewBox="0 0 24 24" | ||
| stroke-width="1.5" | ||
| stroke="currentColor" | ||
| class="w-4 h-4" | ||
| > | ||
| <path | ||
| stroke-linecap="round" | ||
| stroke-linejoin="round" | ||
| d="M13.5 4.5 21 12m0 0-7.5 7.5M21 12H3" | ||
| /> | ||
| </svg> | ||
| </a> | ||
| </div> | ||
| </div> | ||
| <ul class="certified-pillars md:col-span-7"> | ||
| <li class="certified-pillar"> | ||
| <span class="certified-pillar--icon" aria-hidden="true"> | ||
| <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="100%" height="100%"> | ||
| <path stroke-linecap="round" stroke-linejoin="round" d="M15 19.128a9.38 9.38 0 0 0 2.625.372 9.337 9.337 0 0 0 4.121-.952 4.125 4.125 0 0 0-7.533-2.493M15 19.128v-.003c0-1.113-.285-2.16-.786-3.07M15 19.128v.106A12.318 12.318 0 0 1 8.624 21c-2.331 0-4.512-.645-6.374-1.766l-.001-.109a6.375 6.375 0 0 1 11.964-3.07M12 6.375a3.375 3.375 0 1 1-6.75 0 3.375 3.375 0 0 1 6.75 0Zm8.25 2.25a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Z" /> | ||
| </svg> | ||
| </span> | ||
| <div> | ||
| <h3 class="certified-pillar--title">Vetted authors</h3> | ||
| <p class="certified-pillar--body">Every Certified Node comes from a developer with a track record in their domain — not an anonymous npm publisher.</p> | ||
| </div> | ||
| </li> | ||
| <li class="certified-pillar"> | ||
| <span class="certified-pillar--icon" aria-hidden="true"> | ||
| <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="100%" height="100%"> | ||
| <path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75m-3-7.036A11.959 11.959 0 0 1 3.598 6 11.99 11.99 0 0 0 3 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285Z" /> | ||
| </svg> | ||
| </span> | ||
| <div> | ||
| <h3 class="certified-pillar--title">Supported through production</h3> | ||
| <p class="certified-pillar--body">FlowFuse stands behind every Certified Node after launch — patching CVEs on our own timeline. Each node is vetted for reliability, security posture, and current documentation before shipping.</p> | ||
| </div> | ||
| </li> | ||
| <li class="certified-pillar"> | ||
| <span class="certified-pillar--icon" aria-hidden="true"> | ||
| <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" width="100%" height="100%"> | ||
| <path stroke-linecap="round" stroke-linejoin="round" d="m6.75 7.5 3 2.25-3 2.25m4.5 0h3m-9 8.25h13.5A2.25 2.25 0 0 0 21 18V6a2.25 2.25 0 0 0-2.25-2.25H5.25A2.25 2.25 0 0 0 3 6v12a2.25 2.25 0 0 0 2.25 2.25Z" /> | ||
| </svg> | ||
| </span> | ||
| <div> | ||
| <h3 class="certified-pillar--title">Free or commercial, same bar</h3> | ||
| <p class="certified-pillar--body">Some Certified Nodes are free and open; others target specific enterprise needs. The certification standard is the same.</p> | ||
| </div> | ||
| </li> | ||
| </ul> | ||
| </div> | ||
| </div> | ||
| </section> | ||
| </template> | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| <template> | ||
| <svg | ||
| class="certified-icon" | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| viewBox="0 0 24 24" | ||
| fill="currentColor" | ||
| aria-hidden="true" | ||
| > | ||
| <path | ||
| fill-rule="evenodd" | ||
| clip-rule="evenodd" | ||
| d="M8.603 3.799A4.49 4.49 0 0 1 12 2.25c1.357 0 2.573.6 3.397 1.549a4.49 4.49 0 0 1 3.498 1.307 4.491 4.491 0 0 1 1.307 3.497A4.49 4.49 0 0 1 21.75 12a4.49 4.49 0 0 1-1.549 3.397 4.491 4.491 0 0 1-1.307 3.497 4.491 4.491 0 0 1-3.497 1.307A4.49 4.49 0 0 1 12 21.75a4.49 4.49 0 0 1-3.397-1.549 4.49 4.49 0 0 1-3.498-1.306 4.491 4.491 0 0 1-1.307-3.498A4.49 4.49 0 0 1 2.25 12c0-1.357.6-2.573 1.549-3.397a4.49 4.49 0 0 1 1.307-3.497 4.49 4.49 0 0 1 3.497-1.307Zm7.007 6.387a.75.75 0 1 0-1.22-.872l-3.236 4.53L9.53 12.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.14-.094l3.75-5.25Z" | ||
| /> | ||
| </svg> | ||
| </template> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| <script setup> | ||
| const props = defineProps({ | ||
| // Already-sanitised flow JSON string (escaped at build time in the composable). | ||
| flow: { type: String, required: true }, | ||
| // Index used for the underlying DOM id, mirroring the Eleventy template. | ||
| index: { type: Number, required: true } | ||
| }) | ||
|
|
||
| const container = ref(null) | ||
| let cleanedUp = false | ||
|
|
||
| async function loadAndRender () { | ||
| if (!container.value || cleanedUp) return | ||
| try { | ||
| // Dynamic import of the global JS lib bundled into Nuxt's public/js/ by build:js:nuxt. | ||
| // The /* @vite-ignore */ tells Vite to treat this as a runtime URL, not a bundled module. | ||
| const flowrendererUrl = '/js/flowrenderer.min.js' | ||
| const mod = await import(/* @vite-ignore */ flowrendererUrl) | ||
| const Renderer = mod.default ?? mod | ||
| const renderer = new Renderer() | ||
| // The flow string was JSON.stringify'd in the composable; parse it back here. | ||
| const parsed = JSON.parse(props.flow) | ||
| renderer.renderFlows(parsed, { | ||
| container: container.value, | ||
| direction: 'LR', | ||
| gridLines: true, | ||
| zoom: true, | ||
| labels: true, | ||
| autoZoom: true | ||
| }) | ||
| } catch (err) { | ||
| console.error('FlowRenderer failed:', err) | ||
| } | ||
| } | ||
|
|
||
| onMounted(() => { void loadAndRender() }) | ||
| onBeforeUnmount(() => { cleanedUp = true }) | ||
| </script> | ||
|
|
||
| <template> | ||
| <div :id="`flow-renderer-${index}`" ref="container" class="flow-renderer-container" style="height: 400px;"></div> | ||
| </template> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| <script setup> | ||
| const installGifUrl = '/img/installing-node-red-node.gif' | ||
| const installPngUrl = '/images/integrations/palette-manager-install.png' | ||
| </script> | ||
|
|
||
| <template> | ||
| <div class="bg-white border border-gray-200 rounded-lg p-6 shadow-sm overflow-hidden"> | ||
| <h3 class="text-lg font-bold mb-3 flex items-center gap-2"> | ||
| <svg | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| fill="none" | ||
| viewBox="0 0 24 24" | ||
| stroke-width="1.5" | ||
| stroke="currentColor" | ||
| class="w-5 h-5" | ||
| > | ||
| <path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5" /> | ||
| </svg> | ||
| Installation | ||
| </h3> | ||
| <p class="text-sm text-gray-600 mb-3"> | ||
| Install in Node-RED via the | ||
| <a | ||
| href="https://flowfuse.com/node-red/getting-started/library/#using-the-palette-manager" | ||
| class="text-indigo-600 hover:text-indigo-800 font-semibold" | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| >palette manager</a>. | ||
| </p> | ||
| <img | ||
| :src="installGifUrl" | ||
| alt="Animation of the Node-RED palette manager: open Manage Palette, search for the node, then click Install." | ||
| loading="lazy" | ||
| width="1326" | ||
| height="720" | ||
| class="motion-reduce:hidden block w-full rounded border border-gray-200" | ||
| /> | ||
| <img | ||
| :src="installPngUrl" | ||
| alt="Node-RED Palette Manager dialog with the node selected and the Install button visible." | ||
| loading="lazy" | ||
| width="707" | ||
| height="376" | ||
| class="motion-safe:hidden block w-full rounded border border-gray-200" | ||
| /> | ||
| </div> | ||
| </template> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| <script setup> | ||
| const props = defineProps({ | ||
| node: { type: Object, required: true }, | ||
| // Set of node IDs that have a generated detail page on this site. | ||
| generatedIds: { type: Set, required: true } | ||
| }) | ||
|
|
||
| const hasGeneratedPage = computed(() => props.generatedIds.has(props.node._id)) | ||
| const href = computed(() => | ||
| hasGeneratedPage.value | ||
| ? `/integrations/${props.node._id}/` | ||
| : `https://flows.nodered.org/node/${props.node._id}` | ||
| ) | ||
| const externalAttrs = computed(() => | ||
| hasGeneratedPage.value | ||
| ? {} | ||
| : { target: '_blank', rel: 'noopener noreferrer' } | ||
| ) | ||
| const scope = computed(() => props.node.npmScope || props.node.npmOwners?.[0] || '') | ||
| const shortDescription = computed(() => { | ||
| if (!props.node.description) return '' | ||
| const words = props.node.description.split(' ') | ||
| return words.length > 15 | ||
| ? words.slice(0, 15).join(' ') + '...' | ||
| : props.node.description | ||
| }) | ||
| </script> | ||
|
|
||
| <template> | ||
| <li class="integration-card group border border-gray-300 rounded-xl bg-white drop-shadow-md"> | ||
| <a :href="href" v-bind="externalAttrs" class="h-48 flex flex-col"> | ||
| <div class="integration-card--details p-3 grow min-h-0"> | ||
| <div class="flex justify-between text-sm items-center gap-2"> | ||
| <span class="truncate"> | ||
| @{{ scope }} | ||
| <svg | ||
| v-if="!hasGeneratedPage" | ||
| xmlns="http://www.w3.org/2000/svg" | ||
| fill="none" | ||
| viewBox="0 0 24 24" | ||
| stroke-width="1.5" | ||
| stroke="currentColor" | ||
| class="w-4 h-4 inline-block ml-1 opacity-60" | ||
| > | ||
| <path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" /> | ||
| </svg> | ||
| </span> | ||
| <span v-if="node.ffCertified" class="certified-pill" title="FlowFuse Certified"> | ||
| <IntegrationsCertifiedIcon /> | ||
| <span>Certified</span> | ||
| </span> | ||
| </div> | ||
| <label class="group-hover:text-indigo-600 cursor-pointer">{{ node.name }}</label> | ||
| <p class="text-sm my-2 leading-5">{{ shortDescription }}</p> | ||
| </div> | ||
| <div class="integration-card--meta flex justify-between bg-indigo-50/50 group-hover:bg-indigo-50 p-3 text-sm"> | ||
| <div class="integration-card--stats"> | ||
| <span>v{{ node.version }}<span class="ff-helper left-0 after:left-1/4">Version Number</span></span> | ||
| <span class="flex items-center gap-1"> | ||
| <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"> | ||
| <path stroke-linecap="round" stroke-linejoin="round" d="M12 9.75v6.75m0 0-3-3m3 3 3-3m-8.25 6a4.5 4.5 0 0 1-1.41-8.775 5.25 5.25 0 0 1 10.233-2.33 3 3 0 0 1 3.758 3.848A3.752 3.752 0 0 1 18 19.5H6.75Z" /> | ||
| </svg> | ||
| {{ node.downloads.week }} | ||
| <span class="ff-helper right-0 after:left-3/4">Weekly Downloads</span> | ||
| </span> | ||
| </div> | ||
| </div> | ||
| </a> | ||
| </li> | ||
| </template> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| <script setup></script> | ||
|
|
||
| <template> | ||
| <li class="integration-card pointer-events-none border border-gray-300 rounded-xl bg-white drop-shadow-md"> | ||
| <span class="h-48 flex flex-col"> | ||
| <div class="integration-card--details p-3 grow min-h-0"> | ||
| <span class="placeholder-bar block w-1/2 h-5 mt-1 mb-3"><span class="placeholder-gradient"></span></span> | ||
| <span class="placeholder-bar block w-full h-16 opacity-50"><span class="placeholder-gradient"></span></span> | ||
| </div> | ||
| <div class="integration-card--meta flex justify-between bg-indigo-50/50 p-3 text-sm"> | ||
| <div class="integration-card--stats"> | ||
| <span class="placeholder-bar block h-5 w-5"><span class="placeholder-gradient"></span></span> | ||
| <span class="placeholder-bar block h-5 w-5"><span class="placeholder-gradient"></span></span> | ||
| </div> | ||
| </div> | ||
| </span> | ||
| </li> | ||
| </template> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update this file based on changes in #5036 - updated for first round of feedback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also may need to migrate the cookies banner to nuxt before this can merge - TBD