Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: docs

# The documentation site (docs/site, VitePress) from the repository's Markdown: built on every
# pull request that touches the docs, published to GitHub Pages from main once Pages is enabled
# (Settings -> Pages -> Source: GitHub Actions); until then the build only reports it.

on:
push:
branches: [main]
paths: ["**.md", "docs/**", "src/**/config/**", ".github/workflows/docs.yml"]
pull_request:
paths: ["**.md", "docs/**", "src/**/config/**", ".github/workflows/docs.yml"]
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: docs-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-24.04
outputs:
deploy: ${{ steps.pages.outcome == 'success' }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: docs/site/package-lock.json
- name: Python for generated pages
if: hashFiles('docs/site/requirements.txt') != ''
run: pip install -r docs/site/requirements.txt
- name: GitHub Pages
id: pages
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
continue-on-error: true # not enabled yet: build only
uses: actions/configure-pages@v5
- name: Build the site
working-directory: docs/site
env:
DOCS_BASE: ${{ steps.pages.outputs.base_path && format('{0}/', steps.pages.outputs.base_path) || format('/{0}/', github.event.repository.name) }}
run: |
npm ci --no-audit --no-fund
npm run build
- name: Pages not enabled
if: steps.pages.outcome == 'failure'
run: echo "::notice title=GitHub Pages::not enabled, the site was built but not published (Settings -> Pages -> Source -> GitHub Actions)"
- uses: actions/upload-pages-artifact@v3
if: steps.pages.outcome == 'success'
with:
path: docs/site/.vitepress/dist

deploy:
needs: build
if: needs.build.outputs.deploy == 'true'
runs-on: ubuntu-24.04
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ repos:
hooks:
- id: prettier
types_or: [yaml, markdown, json]
exclude: ^docs/site/package-lock\.json$ # written by npm
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.10.0.1
hooks:
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ versions follow [SemVer](https://semver.org).

### Added

- Documentation site: `docs/site` (the kit from the Dharma template, VitePress) builds the
README, the docs, the package READMEs and the protocols into a site with search and dark mode,
published to GitHub Pages by `.github/workflows/docs.yml`; the Markdown stays the one source.
- One clock for every stamp: `scripts/setup.sh install timesync ptp` (`scripts/timesync.sh`, from
the Dharma template). chrony makes the clock follow `TIME_SERVERS` and serves the robot's
other computers (`TIME_ALLOW`, e.g. the Lite3's network); `ptp4l` as PTP master on the
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ the AprilTags that anchor it, a path finder between points of interest, tours wi
and interaction with visitors — driven from a handheld and a web dashboard.**

[![CI](https://github.com/DarmawanRobotics/Molly/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/DarmawanRobotics/Molly/actions/workflows/ci.yml)
[![Docs](https://img.shields.io/badge/docs-site-c94f33)](https://darmawanrobotics.github.io/Molly/)
[![ROS 2](https://img.shields.io/badge/ROS%202-Humble-22314E?logo=ros&logoColor=white)](https://docs.ros.org/en/humble/)
[![Dharma](https://img.shields.io/badge/stack-Dharma%200.2-4A78C2)](molly.repos)
[![Ubuntu](https://img.shields.io/badge/Ubuntu-22.04-E95420?logo=ubuntu&logoColor=white)](https://releases.ubuntu.com/22.04/)
Expand All @@ -18,7 +19,7 @@ and interaction with visitors — driven from a handheld and a web dashboard.**
[![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)

[Quick start](#quick-start) · [Features](#features) · [System](#system) · [Architecture](#architecture) · [Install](#install) · [Workflow](#workflow) ·
[Operations](#operations) · [Develop](#develop) · [Docs](docs/README.md)
[Operations](#operations) · [Develop](#develop) · [Docs](docs/README.md) · [Docs site](https://darmawanrobotics.github.io/Molly/)

</div>

Expand Down
5 changes: 4 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Molly docs

Start with the [README](../README.md) (quick start, install, workflow, operations).
Start with the [README](../README.md) (quick start, install, workflow, operations). The same pages,
with search, are the documentation site:
[darmawanrobotics.github.io/Molly](https://darmawanrobotics.github.io/Molly/) (built from
[site/](site/README.md)).

## Reference

Expand Down
4 changes: 4 additions & 0 deletions docs/site/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
content/
.vitepress/cache/
.vitepress/dist/
101 changes: 101 additions & 0 deletions docs/site/.vitepress/config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// The site of this repository's documentation, generic: everything specific is in
// site.config.mjs. Pages come from scripts/sync.mjs (content/), which `npm run build` runs first.
import { defineConfig } from 'vitepress'

import site from '../site.config.mjs'

const base = process.env.DOCS_BASE || '/'
const branch = site.branch ?? 'main'
const repoUrl = `https://github.com/${site.repo}`
const link = (dest: string) => '/' + dest.replace(/\.md$/, '')
const brand = site.brand ?? { light: '#3451b2', dark: '#a8b1ff' }

export default defineConfig({
title: site.title,
description: site.description,
lang: 'en-US',
base,
srcDir: 'content',
cleanUrls: true,
lastUpdated: false,
ignoreDeadLinks: 'localhostLinks', // the dev container's VNC and the like
head: [
['link', { rel: 'icon', href: base + site.logo }],
['meta', { name: 'theme-color', content: brand.light }],
['meta', { property: 'og:title', content: site.title }],
['meta', { property: 'og:description', content: site.description }],
[
'style',
{},
`:root{--vp-c-brand-1:${brand.light};--vp-c-brand-2:${brand.light2 ?? brand.light};` +
`--vp-c-brand-3:${brand.light};--vp-c-brand-soft:${brand.soft ?? 'rgba(100,108,255,.14)'}}` +
`.dark{--vp-c-brand-1:${brand.dark};--vp-c-brand-2:${brand.dark2 ?? brand.dark};` +
`--vp-c-brand-3:${brand.dark}}`,
],
],
markdown: {
config(md) {
// inline code may hold {{ }} (Vue would compile it)
const code = md.renderer.rules.code_inline!
md.renderer.rules.code_inline = (tokens, idx, options, env, self) =>
code(tokens, idx, options, env, self).replace(/^<code/, '<code v-pre')
// a paragraph holding only an image is a figure, its alt text the caption (as in the PDFs)
md.core.ruler.push('figures', (state) => {
const t = state.tokens
for (let i = 0; i + 2 < t.length; i++) {
if (t[i].type !== 'paragraph_open' || t[i + 1].type !== 'inline') continue
const kids = (t[i + 1].children ?? []).filter((k) => k.type !== 'text' || k.content.trim())
if (kids.length !== 1 || kids[0].type !== 'image' || !kids[0].content) continue
t[i].tag = t[i + 2].tag = 'figure'
const caption = new state.Token('html_inline', '', 0)
caption.content = `<figcaption>${md.utils.escapeHtml(kids[0].content)}</figcaption>`
t[i + 1].children!.push(caption)
}
})
},
},
themeConfig: {
logo: '/' + site.logo,
siteTitle: site.title,
nav: [
...site.sections
.filter((section) => section.nav !== false)
.map((section) => ({
text: section.text,
link: link(section.items[0].dest),
activeMatch: `^/${section.items[0].dest.split('/')[0]}/`,
})),
...(site.files?.some((f) => f.text)
? [
{
text: 'PDF',
items: site.files
.filter((f) => f.text)
.map((f) => ({ text: f.text, link: `${base}files/${f.as}`, target: '_blank' })),
},
]
: []),
],
sidebar: site.sections.map((section) => ({
text: section.text,
collapsed: section.collapsed ?? false,
items: section.items.map((item) => ({ text: item.text, link: link(item.dest) })),
})),
outline: { level: [2, 3], label: 'On this page' },
socialLinks: [{ icon: 'github', link: repoUrl }],
search: { provider: 'local' },
editLink: {
// serialized for the browser without its closure: the values are written into it
pattern: new Function(
'page',
`return page.frontmatter.source ? ${JSON.stringify(`${repoUrl}/edit/${branch}/`)} + ` +
`page.frontmatter.source : ${JSON.stringify(repoUrl)}`
) as (page: { frontmatter: Record<string, string> }) => string,
text: 'Edit this page on GitHub',
},
footer: {
message: site.footer ?? `The pages are the Markdown of <a href="${repoUrl}">${site.repo}</a>.`,
copyright: site.copyright,
},
},
})
5 changes: 5 additions & 0 deletions docs/site/.vitepress/theme/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import DefaultTheme from 'vitepress/theme'

import './style.css'

export default DefaultTheme
109 changes: 109 additions & 0 deletions docs/site/.vitepress/theme/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/* Shared look of the documentation sites (brand colours come from site.config.mjs). */

:root {
--vp-home-hero-name-color: transparent;
--vp-home-hero-name-background: linear-gradient(120deg, var(--vp-c-brand-1) 30%, var(--vp-c-brand-2));
--vp-home-hero-image-background-image: linear-gradient(-45deg, var(--vp-c-brand-soft) 50%, transparent 50%);
--vp-home-hero-image-filter: blur(56px);
}

.VPHero .image-src {
max-width: 260px;
max-height: 260px;
}

/* figures: a paragraph holding only an image, its alt text as caption */
.vp-doc figure {
margin: 20px 0;
text-align: center;
}
.vp-doc figure img {
margin: 0 auto;
border-radius: 8px;
box-shadow: var(--vp-shadow-2);
}
.vp-doc figcaption {
margin-top: 8px;
font-size: 13px;
color: var(--vp-c-text-2);
}

/* wide tables scroll instead of squeezing their columns */
.vp-doc table {
display: block;
overflow-x: auto;
}
.vp-doc td code,
.vp-doc th code {
white-space: nowrap;
}

/* the title block of a versioned guide */
.vp-doc h1.doc-title {
margin-bottom: 4px;
}
.vp-doc p.doc-subtitle {
margin-top: 0;
font-size: 18px;
color: var(--vp-c-text-2);
}

/* guides numbered like their PDF: chapters (h1) 1, 2, ...; sections (h2) 1.1, 1.2, ... */
.numbered .vp-doc {
counter-reset: chapter;
}
.numbered .vp-doc h1:not(.doc-title):not(.unnumbered) {
counter-increment: chapter;
counter-reset: section;
margin-top: 56px;
padding-top: 24px;
border-top: 1px solid var(--vp-c-divider);
}
.numbered .vp-doc h2:not(.unnumbered) {
counter-increment: section;
}
.numbered .vp-doc h1:not(.doc-title):not(.unnumbered)::before {
content: counter(chapter) ' ';
color: var(--vp-c-brand-1);
}
.numbered .vp-doc h2:not(.unnumbered)::before {
content: counter(chapter) '.' counter(section) ' ';
color: var(--vp-c-brand-1);
}

/* cards of the home page's document list */
.docs-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 16px;
margin: 16px 0 32px;
}
.docs-cards p {
margin: 0;
}
.docs-cards a {
display: block;
height: 100%;
padding: 20px;
border: 1px solid var(--vp-c-bg-soft);
border-radius: 12px;
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
text-decoration: none;
transition: border-color 0.25s;
}
.docs-cards a:hover {
border-color: var(--vp-c-brand-1);
}
.docs-cards a span {
display: block;
margin-top: 8px;
font-size: 14px;
line-height: 1.5;
color: var(--vp-c-text-2);
}
.VPHome .vp-doc {
max-width: 1152px;
margin: 0 auto;
padding: 0 24px 48px;
}
28 changes: 28 additions & 0 deletions docs/site/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Documentation site

The repository's Markdown as a website (VitePress): search, dark mode, the PDFs to download.
The Markdown stays the one source; the site is built from it and published to GitHub Pages by
[`.github/workflows/docs.yml`](../../.github/workflows/docs.yml).

```bash
cd docs/site
npm ci
npm run dev # http://localhost:5173, rebuilt on save (run `npm run sync` after editing a source page)
npm run build # .vitepress/dist; DOCS_BASE=/<repo>/ for the Pages path
```

| File | Holds |
| ---------------------------- | ---------------------------------------------------------------------------------------------------- |
| `site.config.mjs` | this repository's site: title, colours, the sections and which Markdown file is which page, the PDFs |
| `index.md`, `public/` | the home page and its logo |
| `scripts/sync.mjs` | copies the listed pages into `content/` (git-ignored), fixing links, images and admonitions |
| `.vitepress/config.mts` | the VitePress configuration built from `site.config.mjs` (generic) |
| `.vitepress/theme/style.css` | the shared look: figures, numbered guides, home cards |

`scripts/sync.mjs`, `.vitepress/` and the workflow are the same in every product made from this
template; a product changes `site.config.mjs`, `index.md` and `public/`. Links between listed
pages become site links, links to other files of the repository go to GitHub, and a page edited
on the site opens its source file.

**Publishing.** Once per repository: Settings → Pages → Source: **GitHub Actions**. Until then the
workflow builds the site on every docs change and only reports that Pages is off.
Loading
Loading