Skip to content

Commit 670e027

Browse files
新增了无人机相关教程:系统的讲解无人机从控制到轨迹生成再到轨迹优化的完整流程,包括比较难以理解的微分平坦性,SE3控制器,minimumsnap轨迹优化等内容,包含12个可运行的简单易懂案例,不用复杂的环境,不用复杂的代码,助你从零入门无人机。
Add GitHub Pages static ebook build
2 parents 089d3cb + 07cf1d8 commit 670e027

8 files changed

Lines changed: 1031 additions & 670 deletions

File tree

.github/workflows/pages.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Deploy Pages
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: read
11+
pages: write
12+
id-token: write
13+
14+
concurrency:
15+
group: pages
16+
cancel-in-progress: true
17+
18+
jobs:
19+
build:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@v4
24+
25+
- name: Setup Node
26+
uses: actions/setup-node@v4
27+
with:
28+
node-version: 20
29+
cache: npm
30+
31+
- name: Install dependencies
32+
run: npm ci
33+
34+
- name: Build static site
35+
run: npm run build:pages
36+
37+
- name: Upload Pages artifact
38+
uses: actions/upload-pages-artifact@v3
39+
with:
40+
path: dist
41+
42+
deploy:
43+
runs-on: ubuntu-latest
44+
needs: build
45+
environment:
46+
name: github-pages
47+
url: ${{ steps.deployment.outputs.page_url }}
48+
steps:
49+
- name: Deploy to GitHub Pages
50+
id: deployment
51+
uses: actions/deploy-pages@v4

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,7 @@ datawhale_eai_pnp_language
199199
# Local ebook web app dependencies
200200
node_modules/
201201
artifacts/
202+
203+
# Keep ebook web shared logic tracked
204+
!web/lib/
205+
!web/lib/*.js

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"version": "1.0.0",
44
"private": true,
55
"scripts": {
6-
"start": "node web/server.js"
6+
"start": "node web/server.js",
7+
"build:pages": "node scripts/build-pages.js"
78
},
89
"dependencies": {
910
"cheerio": "^1.1.2",

scripts/build-pages.js

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
"use strict";
2+
3+
const fs = require("fs/promises");
4+
const fsSync = require("fs");
5+
const path = require("path");
6+
const {
7+
repoRoot,
8+
projectAssetMap,
9+
fullPathFromRel,
10+
collectCatalog,
11+
buildDocPayload,
12+
fetchGithubRepo,
13+
} = require("../web/lib/content");
14+
15+
const distRoot = path.join(repoRoot, "dist");
16+
const siteRoot = path.join(distRoot, "zh-cn");
17+
const publicRoot = path.join(repoRoot, "web", "public");
18+
const katexRoot = path.join(repoRoot, "node_modules", "katex", "dist");
19+
20+
async function ensureDir(dirPath) {
21+
await fs.mkdir(dirPath, { recursive: true });
22+
}
23+
24+
async function emptyDir(dirPath) {
25+
await fs.rm(dirPath, { recursive: true, force: true });
26+
await ensureDir(dirPath);
27+
}
28+
29+
async function copyFilePreserveDir(fromPath, toPath) {
30+
await ensureDir(path.dirname(toPath));
31+
await fs.copyFile(fromPath, toPath);
32+
}
33+
34+
async function copyDirectory(source, target) {
35+
await ensureDir(target);
36+
const entries = await fs.readdir(source, { withFileTypes: true });
37+
for (const entry of entries) {
38+
const fromPath = path.join(source, entry.name);
39+
const toPath = path.join(target, entry.name);
40+
if (entry.isDirectory()) {
41+
await copyDirectory(fromPath, toPath);
42+
} else if (entry.isFile()) {
43+
await copyFilePreserveDir(fromPath, toPath);
44+
}
45+
}
46+
}
47+
48+
function collectFileRefs(value, bucket) {
49+
const matches = String(value).match(/files\/([^"'()\s?#]+(?:\?[^"'()\s#]*)?)/g) || [];
50+
for (const match of matches) {
51+
const relPath = decodeURIComponent(
52+
match.replace(/^files\//, "").replace(/\?.*$/, ""),
53+
);
54+
bucket.add(relPath);
55+
}
56+
}
57+
58+
function collectHomeAssetRefs() {
59+
const appSource = fsSync.readFileSync(path.join(publicRoot, "app.js"), "utf8");
60+
const refs = new Set();
61+
const matches = appSource.matchAll(/\b(?:media|image):\s*"([^"]+)"/g);
62+
for (const match of matches) {
63+
refs.add(match[1]);
64+
}
65+
return refs;
66+
}
67+
68+
async function buildSite() {
69+
await emptyDir(distRoot);
70+
await ensureDir(siteRoot);
71+
72+
await copyDirectory(publicRoot, siteRoot);
73+
await copyDirectory(katexRoot, path.join(siteRoot, "vendor", "katex"));
74+
75+
const catalog = await collectCatalog({ withVersion: true });
76+
const github = await fetchGithubRepo().catch(() => ({
77+
stars: null,
78+
forks: null,
79+
updatedAt: null,
80+
}));
81+
82+
const dataRoot = path.join(siteRoot, "data");
83+
const docsRoot = path.join(dataRoot, "docs");
84+
await ensureDir(docsRoot);
85+
86+
await fs.writeFile(
87+
path.join(dataRoot, "catalog.json"),
88+
JSON.stringify(catalog, null, 2),
89+
"utf8",
90+
);
91+
await fs.writeFile(
92+
path.join(dataRoot, "github.json"),
93+
JSON.stringify(github, null, 2),
94+
"utf8",
95+
);
96+
97+
const referencedRepoFiles = collectHomeAssetRefs();
98+
Object.values(projectAssetMap).forEach((relPath) => {
99+
if (relPath && fsSync.existsSync(fullPathFromRel(relPath))) {
100+
referencedRepoFiles.add(relPath);
101+
}
102+
});
103+
104+
for (const doc of catalog.docs) {
105+
const payload = await buildDocPayload(doc.relPath, catalog, { withVersion: true });
106+
collectFileRefs(payload.html, referencedRepoFiles);
107+
await fs.writeFile(
108+
path.join(docsRoot, `${doc.id}.json`),
109+
JSON.stringify(payload),
110+
"utf8",
111+
);
112+
}
113+
114+
for (const relPath of referencedRepoFiles) {
115+
const sourcePath = fullPathFromRel(relPath);
116+
if (!fsSync.existsSync(sourcePath)) continue;
117+
const stat = fsSync.statSync(sourcePath);
118+
if (!stat.isFile()) continue;
119+
const toPath = path.join(siteRoot, "files", ...relPath.split("/"));
120+
await copyFilePreserveDir(sourcePath, toPath);
121+
}
122+
123+
const faviconRel = projectAssetMap.favicon;
124+
if (faviconRel && fsSync.existsSync(fullPathFromRel(faviconRel))) {
125+
await copyFilePreserveDir(fullPathFromRel(faviconRel), path.join(siteRoot, "favicon.png"));
126+
}
127+
128+
const rootRedirect = `<!doctype html>
129+
<html lang="zh-CN">
130+
<head>
131+
<meta charset="utf-8" />
132+
<meta http-equiv="refresh" content="0; url=./zh-cn/" />
133+
<meta name="viewport" content="width=device-width, initial-scale=1" />
134+
<title>Every-Embodied</title>
135+
</head>
136+
<body>
137+
<p>Redirecting to <a href="./zh-cn/">./zh-cn/</a>...</p>
138+
</body>
139+
</html>
140+
`;
141+
142+
const noJekyllPath = path.join(distRoot, ".nojekyll");
143+
await fs.writeFile(path.join(distRoot, "index.html"), rootRedirect, "utf8");
144+
await fs.writeFile(noJekyllPath, "", "utf8");
145+
146+
const siteIndex = await fs.readFile(path.join(siteRoot, "index.html"), "utf8");
147+
await fs.writeFile(path.join(siteRoot, "404.html"), siteIndex, "utf8");
148+
}
149+
150+
buildSite().catch((error) => {
151+
console.error(error);
152+
process.exitCode = 1;
153+
});

0 commit comments

Comments
 (0)