Skip to content

docs - Documentation Site

Purpose: VitePress documentation site for Swiss AI Hub. User-facing docs, arc42 ADRs, and auto-synced code deep dives. Automated EN→DE translation, auto-generated sidebar, GitHub Pages deployment.

Folder Structure

docs/
├── .vitepress/
│   ├── config.mts              # Site config (locales, rewrites, sidebar, plugins)
│   ├── sidebar-logic.mjs       # Auto-generates sidebar from folder structure
│   ├── components/             # GradientBackground.vue, NavigationBoxes.vue
│   └── theme/                  # Layout.vue, custom.css (brand red, Outfit font)
├── docs/                       # All documentation content
│   ├── 1_vision_and_positioning/
│   ├── 2_platform/
│   ├── 3_sdk/
│   ├── 4_ecosystem/
│   ├── 5_references/
│   └── 6_code_deep_dive/      # AUTO-SYNCED from package READMEs — NEVER edit
├── arc42/decisions/            # ADRs (YYYY_MM_DD_short-summary.md)
├── media/                      # Images organized by topic (architecture/, sdk/, etc.)
├── public/                     # Mostly GENERATED by generate-llm-files.mjs — NEVER edit the generated files
│   ├── CNAME                   # Static: GitHub Pages custom domain (committed, hand-edited)
│   └── robots.txt              # Static: crawler rules (committed, hand-edited)
├── index.en.md                 # Homepage (English — source of truth)
├── index.de.md                 # Homepage (German — auto-generated)
├── sync-docs.sh                # Syncs README files from monorepo packages
├── translate-docs.sh           # EN→DE translation via LLM
├── translate-prompt.md         # Translation system prompt and glossary
└── generate-llm-files.mjs     # Generates LLM-optimized doc files in public/

What NOT to Edit

Three categories of generated files — edits will be overwritten:

  • index.de.md files: Overwritten by translate-docs.sh. Edit index.en.md instead.
  • docs/6_code_deep_dive/: Overwritten by sync-docs.sh. Edit the source README.md in each package.
  • public/ generated files: index.md, llms.txt, llms-full.txt, changelog.md, licenses.md, and the docs/ and de/ subtrees are rebuilt by generate-llm-files.mjs — never edit these. The committed static files CNAME and robots.txt are the exception: they are hand-edited and survive the generator (its cleanPublicDirectory only removes the generated paths). VitePress copies everything in public/ to the deployed site root.

Page Structure

Every page lives in its own directory as index.en.md. Numbered folders control sidebar order:

docs/
├── 1_vision_and_positioning/   # First in sidebar
│   └── index.en.md
├── 2_platform/                 # Second in sidebar
│   ├── index.en.md             # Section overview
│   └── 1_getting_started/      # Nested page (ordered by prefix)
│       └── index.en.md
  • No order: frontmatter — the numeric prefix in the directory name is the sole ordering mechanism
  • Sidebar is 100% auto-generated by sidebar-logic.mjs — never manually configure it
  • Title derived from frontmatter title field, or directory name if missing

Minimal frontmatter:

yaml
---
title: My Feature Title
description: Brief explanation (optional)
---

Translation Pipeline

Automated English→German translation using gemini-2.5-flash via the llm CLI.

  • Change detection: SHA-256 hash of index.en.md stored as source_sha in index.de.md frontmatter. If hashes match, translation is skipped.
  • Prompt rules: Defined in translate-prompt.md — preserves code blocks, rewrites absolute links to /de/..., keeps technical terms, uses formal "Sie" German.
  • CI skips translation: docs:build:ci does not run translation. German files must be pre-committed to git.
  • Run: pnpm run docs:translate (requires pipx install llm)

The source_sha field is load-bearing: if you manually edit index.de.md without changing the sha, the next translate run silently skips re-translation. Always re-run translation after editing English source.

Sync Pipeline

pnpm run docs:sync pulls README.md files from monorepo packages into docs/6_code_deep_dive/ as index.en.md. Deletes and rebuilds the entire 6_code_deep_dive/ content fresh each run. German .de.md copies are verbatim (no translation for synced READMEs). To update synced docs: edit the source README in the package, then re-run sync.

Commands

CommandWhat it does
pnpm run docs:devDev server at localhost:5173
pnpm run docs:syncSync README files from monorepo
pnpm run docs:translateGenerate German translations (requires llm CLI)
pnpm run docs:buildFull build: sync + LLM files + translate + VitePress build
pnpm run docs:build:ciCI build: sync + LLM files + VitePress build (no translation)
pnpm run docs:previewPreview production build at localhost:4173

Writing Documentation

  1. Create docs/N_section/M_topic/index.en.md with title frontmatter
  2. Run pnpm run docs:translate for German version
  3. Preview: pnpm run docs:dev
  4. Commit both .en.md and .de.md files

VitePress containers (used throughout):

markdown
::: tip
Helpful information.
:::

::: warning
Important caveat.
:::

::: details Click to expand
Collapsible content (JSON payloads, HTTP examples).
:::

Images: reference with relative paths to media/ (../../../../media/architecture/high_level/tier_1.svg). Add data-zoomable attribute for lightbox behavior.

Architecture diagrams: the eight media/architecture/{high_level,low_level}/tier_*.svg files are rendered from media/architecture/architecture.drawio by pnpm run docs:render-diagrams (a Docker-based step that exports each drawio page using the page name as the output filename). Run it after editing the .drawio and commit the regenerated SVGs alongside the source.

Mermaid diagrams: inline fenced code blocks with mermaid language tag.

Custom Vue components available in any .md file: <NavigationBoxes />.

Media Organization

Images in topic-specific subdirectories under media/:

  • media/architecture/.drawio source files + .svg exports (high_level/, low_level/), regenerated via pnpm run docs:render-diagrams
  • media/sdk/ — tutorial screenshots and videos
  • media/platform/ — demo videos
  • media/evaluation/, media/knowledge/, media/open_webui/ — UI screenshots

Architecture diagrams (LikeC4)

The platform's C4 model lives in docs/likec4/ and is rendered as interactive web components in any docs page via <likec4-view view-id="...">. Source files:

  • docs/likec4/specification.c4 — element kinds, colors, tags
  • docs/likec4/model/*.c4 — actors, containers, components, relationships (one file per C4 level)
  • docs/likec4/views/*.c4 — view definitions (one file per view family: context, containers, centered, dynamic, …)

The web component bundle is regenerated automatically by pnpm run docs:dev (prepended via likec4:codegen script). Output: docs/.vitepress/theme/likec4-webcomponent.js (gitignored). It's side-effect-imported in docs/.vitepress/theme/index.js; the Vue compiler is told likec4-* tags are custom elements in docs/.vitepress/config.mts.

When creating or editing views

Apply the design principles from the likec4-dsl skill, especially:

  • Less per view, more views: target ≤10 boxes per view; split when over budget. For central containers (e.g. API Gateway) split inbound / outbound into separate views.
  • Multiple parallel view families: tier views answer "what's in this layer?"; package-centered views answer "what does my package touch?"; dynamic views answer "how does scenario X flow?". Same model, different projections.
  • Code-first for connectivity: when adding or editing edges, verify against source — READMEs and docker-compose.yml miss runtime integrations (e.g. custom OpenWebUI pipelines, NATS RPC patterns).
  • Honest self-criticism: open the rendered diagram in the dev server before declaring a change done. If labels are squashed or edges tangled, fix it.
  • Icons + tier colours: every container carries a tech/bootstrap: icon and a tier colour (app = bbv red, data = slate, llm = secondary (renders blue), eventing = amber, identity/edge = indigo, observability = green, utility = gray, external = muted). See the skill's "Icons, colour, and relationship styling" section for the full convention. Note: the <likec4-view> webcomponent can't render a notation legend, so the encoding is conveyed by icons/colours/shapes themselves rather than an on-page key.

Connectivity research subagent

For deep-dive connectivity mapping of a single application container (packages/api, packages/agent, etc.), use the connectivity-researcher subagent (.claude/agents/connectivity-researcher.md). It reads code, not docker-compose, and returns structured edge inventories.

MCP server

The likec4 MCP server (.claude/mcp/mcp-likec4.sh) exposes the model for runtime introspection — read-project-summary, read-view, query-graph, find-relationship-paths. Useful for sanity-checking the model without re-reading the .c4 files.

ADRs

  • Location: arc42/decisions/
  • Format: YYYY_MM_DD_short-kebab-summary.md
  • Template: arc42/decisions/0000_00_00_template.md (Context → Decision Drivers → Decision → Consequences)
  • See root CLAUDE.md for when to create an ADR

Theme & Branding

  • Brand color: red (CSS variable overrides in .vitepress/theme/custom.css)
  • Fonts: Outfit (text), JetBrains Mono (code)
  • Dark mode: pure black background (#000000)
  • darkModeSelector: '.dark' — class-based dark mode

Deployment

  • Automatic via GitHub Pages on release (repository_dispatch: release-ready)
  • Manual: workflow_dispatch on .github/workflows/deploy-docs.yml
  • Base path: / (served from a custom domain; public/CNAME holds docs.ai-hub.bbv.ch)
  • Live URL: https://docs.ai-hub.bbv.ch/

Essential Files

  • VitePress config: .vitepress/config.mts
  • Sidebar logic: .vitepress/sidebar-logic.mjs
  • Custom theme: .vitepress/theme/ (Layout.vue, custom.css)
  • Translation script: translate-docs.sh
  • Translation prompt: translate-prompt.md
  • Sync script: sync-docs.sh
  • LLM file generator: generate-llm-files.mjs
  • ADR template: arc42/decisions/0000_00_00_template.md
  • Deployment: .github/workflows/deploy-docs.yml

Built with ❤️ in Switzerland 🇨🇭