Skip to content

Commit dbce923

Browse files
committed
Merge remote-tracking branch 'origin/main' into 4973-nuxt-migration
2 parents eeeb28f + e22a403 commit dbce923

476 files changed

Lines changed: 13262 additions & 7356 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/CLAUDE.md

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,45 @@
11
# FlowFuse Website — Codebase Guide
22

3-
## Stack
3+
## Architecture (Nuxt-first, 11ty being phased out)
44

5-
- **SSG**: Eleventy (11ty) v3, source in `src/`, output to `_site/`
6-
- **CSS**: Tailwind v3 via PostCSS → `_site/css/style.css`
7-
- **Templates**: Nunjucks (`.njk`) + Markdown
5+
The site is migrating from Eleventy (11ty) to Nuxt 3. Nuxt is the primary framework going forward; 11ty is being phased out section by section using a strangler-fig pattern.
6+
7+
- **Primary framework**: Nuxt 3 (`nuxt/`) with `@nuxt/content` v3 for content-driven pages
8+
- **Legacy SSG**: Eleventy (11ty) v3, source in `src/`, output to `_site/` — being phased out
9+
- **Strategy**: Nuxt is the front door. In dev, Nuxt proxies un-migrated routes to 11ty (port 8080). In production, `nuxt generate` produces the final output.
10+
- **CSS**: Tailwind v3 via PostCSS → `_site/css/style.css` (shared between both)
11+
- **Templates (legacy)**: Nunjucks (`.njk`) + Markdown (11ty only)
812
- **Search**: Algolia (`scripts/index-algolia.js`)
9-
- **Hosting**: Netlify; publish dir = `_site`
10-
- **Nuxt migration**: parallel Nuxt 3 project lives in `nuxt/` (see [nuxt/CLAUDE.md](nuxt/CLAUDE.md))
13+
- **Hosting**: Netlify; final output from `nuxt generate`
14+
15+
### Migration status
16+
17+
| Section | Status |
18+
|---------|--------|
19+
| `/handbook/**` | **Migrated** — served by Nuxt (`nuxt/content/handbook/`) |
20+
| All other routes | Still on 11ty, proxied through Nuxt in dev |
21+
22+
### Production build order
23+
24+
```
25+
clean:nuxt → build:js:nuxt → prod:postcss-nuxt → prod:eleventy-nuxt → prod:nuxt
26+
```
27+
28+
11ty outputs to `nuxt/public/` so Nuxt can serve 11ty-generated assets. `nuxt/public/` is gitignored (fully build-generated).
1129

1230
## Dev commands
1331

1432
```bash
1533
npm start # all watchers in parallel (11ty + nuxt + postcss + docs + blueprints)
1634
npm run dev # eleventy + postcss + nuxt only
17-
npm run dev:eleventy # 11ty only, port 8080
18-
npm run dev:nuxt # Nuxt only, port 3000/3001
35+
npm run dev:eleventy # 11ty only, port 8080 (legacy; most work doesn't need this)
36+
npm run dev:nuxt # Nuxt only, port 3000 — use this for handbook and migrated pages
1937
npm run docs # sync docs from external source once
2038
npm run build # production build
2139
```
2240

41+
> When working on the handbook or other migrated sections, `npm run dev:nuxt` is sufficient. `npm start` is only needed when also touching 11ty-served pages.
42+
2343
## Directory layout
2444

2545
```
@@ -104,19 +124,26 @@ Each year has a `src/changelog/YYYY/YYYY.json` that tags the collection.
104124

105125
### Handbook pages
106126

107-
**Source:** `src/handbook/{department}/{slug}.md`
127+
**Source:** `nuxt/content/handbook/{department}/{slug}.md` ← edit here
108128
**URL:** `/handbook/{department}/{slug}/`
109-
**Layout:** `layouts/documentation.njk` (shared with docs)
129+
**Rendered by:** Nuxt — `nuxt/pages/handbook/[...slug].vue` + `HandbookLeftNav` component
110130

111131
```yaml
112132
---
113-
navTitle: "Title shown in sidebar nav"
114-
navGroup: "Optional group heading"
133+
title: "Page title (shown in browser tab and sidebar nav)"
134+
navigation: # optional — only needed on top-level section index.md files
135+
group: "Company" # groups this section under a heading in the left nav
115136
---
116137
```
117138

139+
**Fields:**
140+
- `title` — required; used as the page title and sidebar nav label
141+
- `navigation.group` — only set on top-level section `index.md` files to group sections in the left nav (e.g. "Company", "Engineering & Design Practices", "Internal Operations", "Sales department")
142+
143+
**Nav grouping:** `nuxt/composables/useHandbookNav.ts` reads all pages via `queryCollection('handbook').all()` and builds the sidebar tree. The `navigation.group` on each section's `index.md` controls which sidebar group the section appears under.
144+
118145
Department folders: `company/`, `design/`, `engineering/`, `marketing/`, `operations/`, `peopleops/`, `sales/`
119-
Collection config: `src/handbook/handbook.json`
146+
Collection config: `nuxt/content.config.ts` (defines the `handbook` collection)
120147

121148
---
122149

.claude/launch.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"version": "0.0.1",
3+
"configurations": [
4+
{
5+
"name": "eleventy",
6+
"runtimeExecutable": "npm",
7+
"runtimeArgs": ["run", "dev:eleventy"],
8+
"port": 8080
9+
}
10+
]
11+
}

.eleventy.js

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ module.exports = function(eleventyConfig) {
221221
eleventyConfig.addLayoutAlias('page', 'layouts/page.njk');
222222
eleventyConfig.addLayoutAlias('nohero', 'layouts/nohero.njk');
223223
eleventyConfig.addLayoutAlias('solution', 'layouts/solution.njk');
224+
eleventyConfig.addLayoutAlias('use-case', 'layouts/use-case.njk');
224225
eleventyConfig.addLayoutAlias('catalog', 'layouts/catalog.njk');
225226
eleventyConfig.addLayoutAlias('redirect', 'layouts/redirect.njk');
226227

@@ -531,12 +532,13 @@ module.exports = function(eleventyConfig) {
531532
return new URL(url, site.baseURL).href;
532533
})
533534

535+
534536
eleventyConfig.addFilter("handbookBreadcrumbs", (url) => {
535537
let parts = url.split("/").filter(e => e !== '');
536538
if (parts[parts.length-1] === "index") {
537539
parts.pop();
538540
}
539-
541+
540542
let path = "";
541543
return "/"+parts.map(p => {
542544
let url = `${path}/${p}`;
@@ -546,24 +548,16 @@ module.exports = function(eleventyConfig) {
546548
});
547549

548550
eleventyConfig.addFilter("rewriteHandbookLinks", (str, page) => {
549-
// If page.inputPath looks like: ./src/handbook/abc/def.md
550-
// then the url of the page will be `/handbook/abc/def/`
551-
// links of the form `./` or `[^/]` must be prepended with `../`
552-
// to ensure it links to the right place
553-
554551
const isIndexPage = /(README.md|index.md)$/i.test(page.inputPath)
555552

556553
const matcher = /((href|src)="([^"]*))"/g
557554
let match
558555
while ((match = matcher.exec(str)) !== null) {
559556
let url = match[3]
560557
if (/^(http|#|mailto:)/.test(url)) {
561-
// Do not rewrite absolute urls, in-page anchors or emails
562558
continue
563559
}
564-
// */abc.md#anchor => */abc/#anchor
565560
url = url.replace(/.md(#.*)?$/, '$1')
566-
// */README#anchor => */#anchor
567561
url = url.replace(/README(#.*)?$/, '$1')
568562
if (url[0] !== '/' && !isIndexPage) {
569563
url = '../'+url
@@ -922,7 +916,7 @@ module.exports = function(eleventyConfig) {
922916
// Inject tier badges into docs pages: parent feature after H1, subfeatures after their headings
923917
eleventyConfig.addTransform("docsFeatureBadges", function(content) {
924918
if (!this.page.outputPath || !this.page.outputPath.endsWith(".html")) return content;
925-
if (!this.page.url || !/^(\/docs\/|\/node-red\/|\/handbook\/)/.test(this.page.url)) return content;
919+
if (!this.page.url || !/^(\/docs\/|\/node-red\/)/.test(this.page.url)) return content;
926920

927921
const parentFeature = findFeatureByDocsLink(this.page.url);
928922
const subfeatures = findSubfeaturesForDocsPage(this.page.url);
@@ -1109,7 +1103,6 @@ module.exports = function(eleventyConfig) {
11091103
eleventyConfig.addCollection('nav', function(collection) {
11101104
let nav = {}
11111105

1112-
createNav('handbook')
11131106
createNav('docs')
11141107

11151108
function createNav(tag) {
@@ -1140,7 +1133,7 @@ module.exports = function(eleventyConfig) {
11401133
// recursively parse the folder hierarchy and created our collection object
11411134
// pass nav = {} as the first accumulator - build up hierarchy map of TOC
11421135
hierarchy.reduce((accumulator, currentValue, i) => {
1143-
// create a nested object detailing the full handbook hierarchy
1136+
// create a nested object detailing the full docs hierarchy
11441137
if (!accumulator[currentValue]) {
11451138
accumulator[currentValue] = {
11461139
'name': currentValue,
@@ -1191,7 +1184,6 @@ module.exports = function(eleventyConfig) {
11911184
}
11921185
}
11931186

1194-
// not req'd to have handbook in Website build, so this may be empty
11951187
if (nav[tag]) {
11961188
for (child of nav[tag].children) {
11971189
if (child.group) {
@@ -1205,7 +1197,7 @@ module.exports = function(eleventyConfig) {
12051197
}
12061198
groups[group].children.push(child)
12071199
} else {
1208-
// capture & flag top-level handbook docs, that haven't had a group assigned
1200+
// capture & flag top-level docs that haven't had a group assigned
12091201
groups['Other'].children.push(child)
12101202
}
12111203
}

.github/workflows/build.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ jobs:
1313
runs-on: ubuntu-latest
1414
steps:
1515
- name: Check out website repository
16-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
16+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
1717
with:
1818
path: 'website'
1919
- name: Check out FlowFuse/flowfuse repository (to access the docs)
20-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
20+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
2121
with:
2222
repository: 'FlowFuse/flowfuse'
2323
ref: main
@@ -30,7 +30,7 @@ jobs:
3030
private-key: ${{ secrets.GH_BOT_APP_KEY }}
3131
owner: ${{ github.repository_owner }}
3232
- name: Check out FlowFuse/blueprint-library repository (to access the blueprints)
33-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
33+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
3434
with:
3535
repository: 'FlowFuse/blueprint-library'
3636
ref: main

.github/workflows/calibreapp-image-actions.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
if: github.repository == 'FlowFuse/website' && github.event.pull_request.head.repo.full_name == github.repository
1717
steps:
1818
- name: Checkout Branch
19-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
19+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
2020
- name: Compress Images
2121
id: calibre
2222
uses: calibreapp/image-actions@9d037c06280028c110ff61c433ad4dc7d33c3c43 # 1.5.0
@@ -25,3 +25,4 @@ jobs:
2525
# For non-Pull Requests, run in compressOnly mode and we'll PR after.
2626
compressOnly: ${{ github.event_name != 'pull_request' }}
2727
pngQuality: '100'
28+
ignorePaths: 'nuxt/**'

.github/workflows/handbook-changes-notify.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
handbook_has_been_updated: ${{ steps.changed-files.outputs.handbook_changed }}
1818
steps:
1919
- name: Checkout
20-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
20+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
2121
with:
2222
fetch-depth: 0
2323
- name: Find changed files
@@ -35,7 +35,7 @@ jobs:
3535

3636
steps:
3737
- name: Checkout
38-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
38+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
3939
with:
4040
fetch-depth: 0
4141
- name: Generate URLs

.github/workflows/test.yml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,19 @@ jobs:
1818
flowfuse
1919
blueprint-library
2020
- name: Check out website repository
21-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
21+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
2222
with:
2323
path: 'website'
2424
token: ${{ steps.generate_token.outputs.token }}
2525
- name: Check out FlowFuse/flowfuse repository (to access the docs)
26-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
26+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
2727
with:
2828
repository: 'FlowFuse/flowfuse'
2929
ref: main
3030
path: 'flowfuse'
3131
token: ${{ steps.generate_token.outputs.token }}
3232
- name: Check out FlowFuse/blueprint-library repository (to access the blueprints)
33-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
33+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
3434
with:
3535
repository: 'FlowFuse/blueprint-library'
3636
ref: main
@@ -51,3 +51,7 @@ jobs:
5151
- name: Build the forge
5252
run: npm run build:nuxt:skip-images
5353
working-directory: 'website'
54+
- name: Check links
55+
uses: untitaker/hyperlink@fb5bb9c5011a3d143a54b4b30aedc30ec5bc0f89 # 0.2.0
56+
with:
57+
args: website/nuxt/dist/ --check-anchors --sources website/src

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ This starts three watchers concurrently:
6767

6868
**Note**: the first time running this, 11ty may take a little while to process all images in the `/docs` and `/handbook` folders.
6969

70+
**Note**: if you have previously run `npm run build:nuxt`, clean the generated directories before starting dev or you will get a `spawn EBADF` error:
71+
72+
```bash
73+
npm run clean:nuxt
74+
```
75+
7076
### Legacy-only mode
7177

7278
To run just the legacy 11ty stack (equivalent to the old `npm start`):

netlify.toml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[[headers]]
22
for = "/*"
33
[headers.values]
4-
X-Frame-Options = "DENY"
5-
Content-Security-Policy = "frame-ancestors 'none';"
4+
X-Frame-Options = "SAMEORIGIN"
5+
Content-Security-Policy = "frame-ancestors 'self';"
66

77
[[redirects]]
88
from = "http://flowforge.com/*"
@@ -66,7 +66,15 @@ autoLaunch = false
6666

6767
[build]
6868
command = "npm run build:nuxt"
69-
publish = "nuxt/.output/public"
69+
publish = "nuxt/dist"
70+
71+
[functions]
72+
directory = "nuxt/.netlify/functions-internal"
73+
74+
[[redirects]]
75+
from = "/*"
76+
to = "/.netlify/functions/server"
77+
status = 200
7078

7179
[context.deploy-preview]
7280
command = "npm run build:nuxt"

nuxt/.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Nuxt Studio — GitHub OAuth
2+
# Create at: GitHub → Settings → Developer settings → OAuth Apps
3+
# Callback URL: https://your-domain.com/_studio/auth/github/callback
4+
STUDIO_GITHUB_CLIENT_ID=
5+
STUDIO_GITHUB_CLIENT_SECRET=

0 commit comments

Comments
 (0)