diff --git a/nuxt/components/HiwFit.vue b/nuxt/components/HiwFit.vue new file mode 100644 index 0000000000..bed21ecd3a --- /dev/null +++ b/nuxt/components/HiwFit.vue @@ -0,0 +1,199 @@ + + + + + diff --git a/nuxt/components/HiwFlow.vue b/nuxt/components/HiwFlow.vue new file mode 100644 index 0000000000..5cffea00a9 --- /dev/null +++ b/nuxt/components/HiwFlow.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/nuxt/components/HiwGoDeeper.vue b/nuxt/components/HiwGoDeeper.vue new file mode 100644 index 0000000000..9438c9098c --- /dev/null +++ b/nuxt/components/HiwGoDeeper.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/nuxt/components/HiwLeftNav.vue b/nuxt/components/HiwLeftNav.vue new file mode 100644 index 0000000000..81a24227f0 --- /dev/null +++ b/nuxt/components/HiwLeftNav.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/nuxt/components/HiwProgress.vue b/nuxt/components/HiwProgress.vue new file mode 100644 index 0000000000..8f1e30dc77 --- /dev/null +++ b/nuxt/components/HiwProgress.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/nuxt/components/HiwSectionShare.vue b/nuxt/components/HiwSectionShare.vue new file mode 100644 index 0000000000..2cc2e08f79 --- /dev/null +++ b/nuxt/components/HiwSectionShare.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/nuxt/components/HiwShare.vue b/nuxt/components/HiwShare.vue new file mode 100644 index 0000000000..1d6fb4cb66 --- /dev/null +++ b/nuxt/components/HiwShare.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/nuxt/components/HiwStories.vue b/nuxt/components/HiwStories.vue new file mode 100644 index 0000000000..a594c2149f --- /dev/null +++ b/nuxt/components/HiwStories.vue @@ -0,0 +1,45 @@ + + + + + diff --git a/nuxt/layouts/getting-started.vue b/nuxt/layouts/getting-started.vue new file mode 100644 index 0000000000..fdfbfb62a6 --- /dev/null +++ b/nuxt/layouts/getting-started.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/nuxt/nuxt.config.ts b/nuxt/nuxt.config.ts index 76ef3c2709..f093ec4546 100644 --- a/nuxt/nuxt.config.ts +++ b/nuxt/nuxt.config.ts @@ -40,17 +40,17 @@ function collectChangelogRoutes(dir: string, basePath: string): { routes: string return { routes, entryCount } } -// The Application Guide pages are markdown (`applicationGuideDoc`, a `page` collection). -// Their routes are largely discoverable by @nuxt/content, but we keep an explicit prerender -// list for the section index + each page. File names are .md and match the `slug` field. +// The Application Guide pages are a `data` collection (see content.config.ts), so their routes +// are not discoverable from @nuxt/content page paths. Derive them from the file names, which +// are NN-.yml and match each file's `slug` field. function collectApplicationGuideRoutes(dir: string): string[] { const routes = ['/application-guide/'] for (const guide of readdirSync(dir)) { const guideDir = join(dir, guide) if (!statSync(guideDir).isDirectory()) continue for (const file of readdirSync(guideDir)) { - if (!file.endsWith('.md')) continue - routes.push(`/application-guide/${guide}/${basename(file, '.md').replace(/^\d+-/, '')}/`) + if (!file.endsWith('.yml')) continue + routes.push(`/application-guide/${guide}/${basename(file, '.yml').replace(/^\d+-/, '')}/`) } } return routes @@ -171,16 +171,6 @@ export default defineNuxtConfig({ notes: [ 'This file only covers pages served by the Nuxt frontend. Some sections of flowfuse.com are still served by a legacy Eleventy site not represented here.', ], - - // /raw/.md, the per-page markdown endpoint the links below point at, is served - // by @nuxt/content's own llms feature rather than by nuxt-llms, and it searches every - // page-type collection by default. That included the handbook, so the exclusion above - // only held for llms.txt while /raw/handbook/company.md answered with the same content - // in markdown. Same reasoning applies to both surfaces. - contentRawMarkdown: { - excludeCollections: ['handbook'], - }, - sections: [ { title: 'Documentation', @@ -322,17 +312,11 @@ export default defineNuxtConfig({ routeRules: { '/terms': { robots: false }, '/privacy-policy': { robots: false }, - '/thank-you/**': { robots: false }, ...redirects, }, nitro: { preset: 'netlify', - // Nitro emits a .mjs.map next to every server chunk, so the Netlify functions bundle - // ships one map file per chunk. Skipping them keeps the bundle smaller and the - // server build a little shorter. The cost is that a server-side stack trace in the - // function logs no longer resolves back to the original source. - sourceMap: false, serverAssets: [ { baseName: 'analytics', @@ -351,9 +335,9 @@ export default defineNuxtConfig({ ], prerender: { routes: (() => { - // The changelog listing is a single page now (grouped by release, revealed - // as you scroll), so there are no /changelog// pages to enumerate. const changelog = collectChangelogRoutes(join(__dirname, '../src/changelog'), '/changelog') + const changelogPageCount = Math.max(1, Math.ceil(changelog.entryCount / 19)) + const changelogListingRoutes = ['/changelog/', ...Array.from({ length: changelogPageCount - 1 }, (_, i) => `/changelog/${i + 2}/`)] const blogListingRoutes = paginatedListingRoutes('/blog', blogFiles.length) const blogTagRoutes = BLOG_TAGS.flatMap(tag => @@ -366,6 +350,11 @@ export default defineNuxtConfig({ '/integrations', '/pricing', '/product', + // Static hub page under /product/. collectProductRoutes only enumerates + // the tier pages (/product/{edge,hub,fleet}/) from content/products/*.yml, + // and crawlLinks is off, so this file-based page must be listed explicitly + // or it never prerenders and the nav link to it 404s site-wide. + '/product/how-it-works/', ...collectProductRoutes(join(__dirname, 'content/products')), // Without this, @nuxtjs/sitemap only bakes /sitemap.xml statically when // isNuxtGenerate() is true, which checks for nitro.static/preset "static" - @@ -376,10 +365,6 @@ export default defineNuxtConfig({ // content-urls.get.ts) silently resolves to undefined. Explicitly listing // it here bakes it at build time instead, inside the git checkout. '/sitemap.xml', - '/contact-us', - '/book-demo', - '/support', - '/professional-services', '/ebooks/beginner-guide-to-a-professional-nodered/', '/ebooks/ultimate-guide-to-building-applications-with-flowfuse-dashboard-for-node-red/', '/whitepaper/uns-decoupling-data-producers-and-consumers/', @@ -389,7 +374,7 @@ export default defineNuxtConfig({ '/resources/publications/', ...collectApplicationGuideRoutes(join(__dirname, 'content/application-guide')), '/changelog/index.xml', - '/changelog/', + ...changelogListingRoutes, ...changelog.routes, '/blog/index.xml', ...blogListingRoutes, diff --git a/nuxt/pages/product/how-it-works/index.vue b/nuxt/pages/product/how-it-works/index.vue new file mode 100644 index 0000000000..eb93886215 --- /dev/null +++ b/nuxt/pages/product/how-it-works/index.vue @@ -0,0 +1,292 @@ + + + diff --git a/nuxt/pages/product/index.vue b/nuxt/pages/product/index.vue index 91e2df81fa..b0c859a17f 100644 --- a/nuxt/pages/product/index.vue +++ b/nuxt/pages/product/index.vue @@ -40,6 +40,24 @@ const TIERS = [ }, ] +const DIFFERENTIATORS = [ + { + heading: 'Vendor-Free Open Source', + description: 'Every factory is different, so why deploy the same solution as everyone else? Build your way with ready-made blueprints, your own app store, or anything in between — we never dictate your tools.', + icon: 'i-lucide-puzzle', + }, + { + heading: 'Flexible and Secure', + description: 'From whole-factory rollouts to last-mile fixes across any industry. Build your solution, then secure it with granular RBAC, auditing, version control, and traceability.', + icon: 'i-lucide-shield-check', + }, + { + heading: 'Seamless Collaboration', + description: 'OT teams prototype and deploy fast while IT keeps the governance, security, and auditability they need — no trade-offs.', + icon: 'i-lucide-handshake', + }, +] + const CAPABILITIES = [ { label: 'Industrial AI', to: '/ai/' }, { label: 'IT/OT Middleware', to: '/use-cases/it-ot-middleware/' }, @@ -122,7 +140,17 @@ onUnmounted(() => {

The needs of modern industry requires modern solutions

- +
+
+ +

{{ diff.heading }}

+

+

+
@@ -168,6 +196,8 @@ onUnmounted(() => {

The FlowFuse platform is the foundation for all of your IT/OT applications

Leveraging enterprise-grade development, deployment, and governance solutions, you can build faster, scale more efficiently, and secure deployments of any size.

+

Every application is assembled from the same core pieces — managed Node-RED instances running in the cloud or at the edge, a built-in Team Broker for your Unified Namespace, shared Tables for operational data, operator dashboards, and fleet-wide device management. The same building blocks arrange into the IT, OT, and IIoT architectures that fit how you run — wherever the work happens.

+ Learn how it works →
Integrations