Add GitHub Pages docs deployment

This commit is contained in:
musistudio 2026-06-25 19:53:49 +08:00
parent 02319a257f
commit e7daa50692
6 changed files with 117 additions and 24 deletions

48
.github/workflows/docs.yml vendored Normal file
View file

@ -0,0 +1,48 @@
name: Docs
on:
push:
branches:
- main
paths:
- "docs/**"
- ".github/workflows/docs.yml"
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v6
- name: Build and upload docs
uses: withastro/action@v6
env:
ASTRO_SITE: https://musistudio.github.io
ASTRO_BASE: /claude-code-router
with:
path: docs
node-version: 24
deploy:
name: Deploy
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v5

View file

@ -13,6 +13,16 @@ npm run preview
The local development server runs from this `docs` directory.
## GitHub Pages
Docs are deployed from `.github/workflows/docs.yml` on pushes to `main` that change `docs/**` or the workflow itself. The default public URL is:
```text
https://musistudio.github.io/claude-code-router/
```
The Astro build reads `ASTRO_SITE` and `ASTRO_BASE`, defaulting to `https://musistudio.github.io` and `/claude-code-router`.
## Content
Docs pages are authored in Markdown:

View file

@ -1,6 +1,11 @@
import { defineConfig } from "astro/config";
const site = process.env.ASTRO_SITE ?? "https://musistudio.github.io";
const base = process.env.ASTRO_BASE ?? "/claude-code-router";
export default defineConfig({
site,
base,
output: "static",
markdown: {
shikiConfig: {

View file

@ -245,14 +245,14 @@ Bots forward agent messages to instant-messaging platforms and can hand off acti
CCR supports the following IM platforms for Bot relay. Each requires platform-specific setup described in its dedicated page:
- [DingTalk](/en/relay-agents-in-im-with-bots/dingtalk)
- [Discord](/en/relay-agents-in-im-with-bots/discord)
- [Feishu (Lark)](/en/relay-agents-in-im-with-bots/feishu)
- [LINE](/en/relay-agents-in-im-with-bots/line)
- [Slack](/en/relay-agents-in-im-with-bots/slack)
- [Telegram](/en/relay-agents-in-im-with-bots/telegram)
- [WeCom (Enterprise WeChat)](/en/relay-agents-in-im-with-bots/wecom)
- [Weixin (iLink)](/en/relay-agents-in-im-with-bots/weixin-ilink)
- [DingTalk](relay-agents-in-im-with-bots/dingtalk)
- [Discord](relay-agents-in-im-with-bots/discord)
- [Feishu (Lark)](relay-agents-in-im-with-bots/feishu)
- [LINE](relay-agents-in-im-with-bots/line)
- [Slack](relay-agents-in-im-with-bots/slack)
- [Telegram](relay-agents-in-im-with-bots/telegram)
- [WeCom (Enterprise WeChat)](relay-agents-in-im-with-bots/wecom)
- [Weixin (iLink)](relay-agents-in-im-with-bots/weixin-ilink)
Each platform page covers: required credentials, where to obtain them in the platform's developer console, and common troubleshooting steps.

View file

@ -307,14 +307,14 @@ Bot 能把 Agent 的消息转发到 IM还能在你空闲一段时间后
每个平台的完整步骤(平台后台怎么建应用、字段对照、排查 FAQ都有单独一篇
- [Slack](/bot-与-im-接力-agent/slack)
- [Discord](/bot-与-im-接力-agent/discord)
- [Telegram](/bot-与-im-接力-agent/telegram)
- [LINE](/bot-与-im-接力-agent/line)
- [微信](/bot-与-im-接力-agent/weixin-ilink)
- [企业微信](/bot-与-im-接力-agent/wecom)
- [飞书](/bot-与-im-接力-agent/feishu)
- [钉钉](/bot-与-im-接力-agent/dingtalk)
- [Slack](bot-与-im-接力-agent/slack)
- [Discord](bot-与-im-接力-agent/discord)
- [Telegram](bot-与-im-接力-agent/telegram)
- [LINE](bot-与-im-接力-agent/line)
- [微信](bot-与-im-接力-agent/weixin-ilink)
- [企业微信](bot-与-im-接力-agent/wecom)
- [飞书](bot-与-im-接力-agent/feishu)
- [钉钉](bot-与-im-接力-agent/dingtalk)
## 遇到问题时,照着这个查

View file

@ -41,7 +41,37 @@ const {
},
} = Astro.props;
const homeHref = locale === "en" ? "/en/" : "/";
const baseUrl = import.meta.env.BASE_URL ?? "/";
const absoluteUrlPattern = /^[a-z][a-z\d+.-]*:/i;
const withBase = (href) => {
if (!href || href.startsWith("#") || href.startsWith("//") || absoluteUrlPattern.test(href)) {
return href;
}
const basePath = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
if (href === "/") {
return basePath;
}
const normalizedHref = href.startsWith("/") ? href.slice(1) : href;
return `${basePath}${normalizedHref}`;
};
const absoluteHref = (href) => {
const resolvedHref = withBase(href);
if (absoluteUrlPattern.test(resolvedHref) || !Astro.site) {
return resolvedHref;
}
return new URL(resolvedHref, Astro.site).toString();
};
const resolvedLanguageOptions = languageOptions.map((option) => ({
...option,
href: withBase(option.href),
absoluteHref: absoluteHref(option.href),
}));
const homeHref = withBase(locale === "en" ? "/en/" : "/");
const faviconHref = withBase("/favicon.svg");
const logoSrc = withBase("/logo.png");
const sidebarIcons = {
rocket: Rocket,
book: BookOpen,
@ -81,13 +111,13 @@ const sidebarIcons = {
}
})();
</script>
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
<link rel="icon" href={faviconHref} type="image/svg+xml" />
{
languageOptions.map((option) => (
resolvedLanguageOptions.map((option) => (
<link
rel="alternate"
hreflang={option.locale === "zh" ? "zh-CN" : "en"}
href={option.href}
href={option.absoluteHref}
/>
))
}
@ -97,7 +127,7 @@ const sidebarIcons = {
<div class="shell">
<header class="topbar">
<a class="brand" href={homeHref} aria-label="Claude Code Router Docs">
<img src="/logo.png" alt="" class="brand-logo" />
<img src={logoSrc} alt="" class="brand-logo" />
<span>CCR docs</span>
</a>
@ -108,7 +138,7 @@ const sidebarIcons = {
</summary>
<div class="language-menu">
{
languageOptions.map((option) => (
resolvedLanguageOptions.map((option) => (
<a
class:list={{ active: option.locale === locale }}
href={option.href}
@ -192,7 +222,7 @@ const sidebarIcons = {
<ul class="sidebar-children">
{childItems.map((child) => (
<li>
<a class="sidebar-child-link" href={sidebarLinks[child] ?? "#"}>
<a class="sidebar-child-link" href={withBase(sidebarLinks[child] ?? "#")}>
{child}
</a>
</li>
@ -202,7 +232,7 @@ const sidebarIcons = {
) : (
<a
class:list={["sidebar-link", { active: group.active === item }]}
href={sidebarLinks[item] ?? "#"}
href={withBase(sidebarLinks[item] ?? "#")}
>
<span>{item}</span>
</a>