From 5793f6e8a58b3ba828f6aa2eee46918e8a6fc063 Mon Sep 17 00:00:00 2001 From: Hector Martinez Date: Wed, 5 Aug 2026 16:46:32 +0200 Subject: [PATCH] refactor(docs): switch to VitePress Plus theme with multi-version builds Replace the default VitePress theme with @lando/vitepress-theme-default-plus and enable multi-version documentation builds via mvb. Adopt the Lando defineConfig wrapper, add multiVersionBuild/sidebarEnder configuration, derive the sidebar version label from VPL_MVB_VERSION, simplify Vite resolve aliases with import.meta.resolve, and update CI to fetch tags for version discovery. Rework custom CSS for the new theme and preserve -webkit- vendor prefixes for Safari compatibility. Signed-off-by: Hector Martinez Co-Authored-By: Claude Opus 4.6 Signed-off-by: Hector Martinez --- .github/workflows/site-build.yml | 58 +- .gitmodules | 2 +- .prettierignore | 5 +- .stylelintrc.json | 3 +- docs/.vitepress/config.ts | 43 +- docs/.vitepress/lando-theme.d.ts | 23 + docs/.vitepress/search.d.ts | 2 +- docs/.vitepress/theme/components/Mermaid.vue | 72 +- .../theme/components/ReadingProgress.vue | 18 +- docs/.vitepress/theme/custom.css | 67 +- docs/.vitepress/theme/index.ts | 31 +- docs/doc-site.md | 47 +- docs/v/index.md | 33 + package-lock.json | 1601 ++++++++++++++++- package.json | 7 +- 15 files changed, 1791 insertions(+), 221 deletions(-) create mode 100644 docs/.vitepress/lando-theme.d.ts create mode 100644 docs/v/index.md diff --git a/.github/workflows/site-build.yml b/.github/workflows/site-build.yml index 2f44a0ab9f..bb3410a83d 100644 --- a/.github/workflows/site-build.yml +++ b/.github/workflows/site-build.yml @@ -36,12 +36,14 @@ concurrency: jobs: build: + timeout-minutes: 10 runs-on: ubuntu-24.04 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} - submodules: true + fetch-tags: true + fetch-depth: 0 - uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0 with: @@ -52,8 +54,38 @@ jobs: - name: Install JS dependencies run: npm ci + # Caches are immutable: save is a no-op if the primary key already exists. + # Unique time-based key + restore-keys is the documented way to persist an + # updated tree; restore-keys then takes the most recently created prefix + # match (the last main save, which has the most tagged versions). + # Save only on main, and only when mvb wrote new per-tag artifacts. + - name: Compute mvb cache key + id: mvb-key + run: echo "key=lando-mvb-$(date -u +%Y%m%d%H%M%S)" >> "$GITHUB_OUTPUT" + + - uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + key: ${{ steps.mvb-key.outputs.key }} + restore-keys: | + lando-mvb- + path: docs/.vitepress/cache/@lando/mvb + + - name: Snapshot restored mvb cache + id: mvb-before + env: + MVB_CACHE: docs/.vitepress/cache/@lando/mvb + run: | + set -euo pipefail + mkdir -p "$MVB_CACHE" + hash=$(find "$MVB_CACHE" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort | sha256sum | awk '{print $1}') + echo "hash=${hash}" >> "$GITHUB_OUTPUT" + - name: Build documentation site - run: npm run docs:build + run: | + git submodule update --init + npx mvb docs + env: + VPL_MVB_BRANCH: ${{ github.event.pull_request.head.sha || github.sha }} - name: Prepare deploy bundle run: | @@ -72,3 +104,25 @@ jobs: name: site path: _bundle/ retention-days: 5 + + - name: Detect new mvb version artifacts + id: mvb-after + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + env: + MVB_CACHE: docs/.vitepress/cache/@lando/mvb + BEFORE: ${{ steps.mvb-before.outputs.hash }} + run: | + set -euo pipefail + mkdir -p "$MVB_CACHE" + hash=$(find "$MVB_CACHE" -mindepth 1 -maxdepth 1 -printf '%f\n' | sort | sha256sum | awk '{print $1}') + if [[ "$hash" != "$BEFORE" ]]; then + echo "changed=true" >> "$GITHUB_OUTPUT" + else + echo "changed=false" >> "$GITHUB_OUTPUT" + fi + + - uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + if: github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.mvb-after.outputs.changed == 'true' + with: + key: ${{ steps.mvb-key.outputs.key }} + path: docs/.vitepress/cache/@lando/mvb diff --git a/.gitmodules b/.gitmodules index dac09e8ea3..e3773c004b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "experiments"] path = experiments - url = git@github.com:fullsend-ai/experiments.git + url = https://github.com/fullsend-ai/experiments.git branch = main [submodule "eval/.agent-eval-harness"] path = eval/.agent-eval-harness diff --git a/.prettierignore b/.prettierignore index dd7ce72488..70e00dbb40 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,5 +8,8 @@ cloudflare_site/ *.py hack/ internal/ -docs/ +docs/* +!docs/.vitepress/ +docs/.vitepress/dist/ +docs/.vitepress/cache/ web/public/ diff --git a/.stylelintrc.json b/.stylelintrc.json index 16f9d61154..4f33b33368 100644 --- a/.stylelintrc.json +++ b/.stylelintrc.json @@ -2,6 +2,7 @@ "extends": ["stylelint-config-standard", "stylelint-config-html/vue"], "rules": { "custom-property-pattern": null, - "selector-class-pattern": null + "selector-class-pattern": null, + "property-no-vendor-prefix": [true, { "ignoreProperties": ["-webkit-background-clip", "-webkit-backdrop-filter"] }] } } diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 9b9155ff18..fd22103fac 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,4 +1,4 @@ -import { defineConfig } from "vitepress"; +import { defineConfig } from "@lando/vitepress-theme-default-plus/config"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; @@ -15,6 +15,10 @@ import { const __dirname = path.dirname(fileURLToPath(import.meta.url)); const docsDir = path.resolve(__dirname, ".."); +const version = + JSON.parse(fs.readFileSync(path.resolve(__dirname, "..", "..", "package.json"), "utf-8")) + .version ?? "dev"; + function getMarkdownFiles(dir: string, base: string): { text: string; link: string }[] { const fullDir = path.resolve(docsDir, dir); if (!fs.existsSync(fullDir)) return []; @@ -188,7 +192,6 @@ export default defineConfig({ }, srcExclude: ["**/agents/icons/**", "**/testing/**"], - ignoreDeadLinks: true, themeConfig: { @@ -196,6 +199,11 @@ export default defineConfig({ logoLink: { link: "https://fullsend.sh", target: "_self" }, siteTitle: "Fullsend", + multiVersionBuild: { + satisfies: ">=0.37.0", + build: "stable", + }, + nav: [ { text: "Docs", link: "/guides/getting-started/", activeMatch: "^/(?!cli/)" }, { text: "CLI Reference", link: "/cli/", activeMatch: "/cli/" }, @@ -382,6 +390,26 @@ export default defineConfig({ ], }, + sidebarEnder: { + text: version, + collapsed: true, + items: [ + { + text: "Other Doc Versions", + items: [ + { rel: "mvb", text: "stable", target: "_blank", link: "/stable/" }, + { rel: "mvb", text: "edge", target: "_blank", link: "/edge/" }, + { rel: "mvb", text: "dev", target: "_blank", link: "/dev/" }, + { text: "see all versions", link: "/v/" }, + ], + }, + { + text: "Other Releases", + link: "https://github.com/fullsend-ai/fullsend/releases", + }, + ], + }, + socialLinks: [{ icon: "github", link: "https://github.com/fullsend-ai/fullsend" }], editLink: { @@ -393,7 +421,10 @@ export default defineConfig({ provider: "local", options: { scopes: [ - { label: "Guides", prefixes: ["/docs/guides/", "/docs/agents/", "/docs/cli/", "/docs/runtimes"] }, + { + label: "Guides", + prefixes: ["/docs/guides/", "/docs/agents/", "/docs/cli/", "/docs/runtimes"], + }, { label: "Design Docs", prefixes: ["/docs/problems/", "/docs/ADRs/", "/docs/normative/", "/docs/spikes/"], @@ -458,15 +489,15 @@ export default defineConfig({ shikiSetup: async (shiki) => { await shiki.loadLanguage("toml"); }, + preConfig: (md) => { const defaultParse = md.parse.bind(md); md.parse = (src: string, env: Record) => { + const rel = (env?.relativePath as string) ?? ""; + if (rel === "v/index.md") return defaultParse(src, env); return defaultParse(escapeVueSyntax(src), env); }; }, - // Auto-add v-pre to inline code so `{{ }}` inside backticks is safe. - // Recommended by VitePress maintainer brc-dd: - // https://github.com/vuejs/vitepress/discussions/3724 config: (md) => { const defaultCodeInline = md.renderer.rules.code_inline!; md.renderer.rules.code_inline = (tokens, idx, options, env, self) => { diff --git a/docs/.vitepress/lando-theme.d.ts b/docs/.vitepress/lando-theme.d.ts new file mode 100644 index 0000000000..b29ee9ff52 --- /dev/null +++ b/docs/.vitepress/lando-theme.d.ts @@ -0,0 +1,23 @@ +declare module "@lando/vitepress-theme-default-plus/config" { + import type { UserConfig } from "vitepress"; + + interface VPLThemeConfig { + sidebarEnder?: unknown; + multiVersionBuild?: unknown; + [key: string]: unknown; + } + + export function defineConfig(config: UserConfig): UserConfig; +} + +declare module "@lando/vitepress-theme-default-plus" { + import type { Theme } from "vitepress"; + const theme: Theme; + export default theme; +} + +declare module "*.vue" { + import type { DefineComponent } from "vue"; + const component: DefineComponent; + export default component; +} diff --git a/docs/.vitepress/search.d.ts b/docs/.vitepress/search.d.ts index ccb9e53954..9f2f559987 100644 --- a/docs/.vitepress/search.d.ts +++ b/docs/.vitepress/search.d.ts @@ -3,7 +3,7 @@ import "vitepress"; declare module "vitepress" { namespace DefaultTheme { interface LocalSearchOptions { - scopes?: { label: string; prefixes: string[] }[]; + scopes?: { label: string; prefixes: string[]; others?: boolean }[]; } } } diff --git a/docs/.vitepress/theme/components/Mermaid.vue b/docs/.vitepress/theme/components/Mermaid.vue index 50b3f189d0..52395119cb 100644 --- a/docs/.vitepress/theme/components/Mermaid.vue +++ b/docs/.vitepress/theme/components/Mermaid.vue @@ -1,20 +1,20 @@