diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 4a6b086..1f7672f 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -24,11 +24,17 @@ jobs: build: runs-on: ubuntu-latest steps: + # 1. 注入私钥(支持多行) + - uses: webfactory/ssh-agent@v0.8.0 + with: + ssh-private-key: ${{ secrets.SUBMODULE_SSH_KEY }} + + # 3. 现在 checkout(含递归子模块) - name: Checkout repository (with submodules) uses: actions/checkout@v4 with: - fetch-depth: 0 # 推荐完整历史以支持 lastUpdated 等 - submodules: recursive # 检出子模块(递归) + fetch-depth: 0 + submodules: false - name: Ensure submodules are up-to-date (pull latest from their remote) # 这一步会把子模块更新到各自远端的最新分支(通常是子模块配置的上游) run: | diff --git a/.github/workflows/deploy-github.yml b/.github/workflows/deploy-github.yml index 05bbb18..04c22e9 100644 --- a/.github/workflows/deploy-github.yml +++ b/.github/workflows/deploy-github.yml @@ -26,11 +26,14 @@ jobs: build: runs-on: ubuntu-latest steps: + - uses: webfactory/ssh-agent@v0.8.0 + with: + ssh-private-key: ${{ secrets.SUBMODULE_SSH_KEY }} - name: Checkout repository (with submodules) uses: actions/checkout@v4 with: fetch-depth: 0 # 推荐完整历史以支持 lastUpdated 等 - submodules: recursive # 检出子模块(递归) + submodules: false # 检出子模块(递归) - name: Ensure submodules are up-to-date (pull latest from their remote) # 这一步会把子模块更新到各自远端的最新分支(通常是子模块配置的上游) diff --git a/.github/workflows/deploy-obs.yml b/.github/workflows/deploy-obs.yml index b601b08..938f1e0 100644 --- a/.github/workflows/deploy-obs.yml +++ b/.github/workflows/deploy-obs.yml @@ -36,6 +36,9 @@ jobs: outputs: version: ${{ steps.ver.outputs.value }} steps: + - uses: webfactory/ssh-agent@v0.8.0 + with: + ssh-private-key: ${{ secrets.SUBMODULE_SSH_KEY }} - name: Checkout repository (with submodules) uses: actions/checkout@v4 with: diff --git a/.gitmodules b/.gitmodules index 1b1b182..2eb5325 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "tiny-engine"] path = tiny-engine url = git@github.com:opentiny/tiny-engine.git +[submodule "genui-sdk"] + path = genui-sdk + url = git@github.com:opentiny/genui-sdk.git diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 2e80732..2fbae10 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -39,11 +39,12 @@ export default defineConfig({ // 忽略死链检查(true 会跳过 VitePress 的 dead link 报错) ignoreDeadLinks: true, outDir: 'dist', - srcExclude: [ - '**/README*.md', '**/develop-demo-en.md', '**/theme-en.md', - 'tiny-vue/packages/**/*.md', 'tiny-engine/packages/**/*.md', - 'tiny-robot/packages/**/*.md', 'next-sdk/packages/**/*.md', - ], + srcExclude: [ + '**/README*.md', '**/develop-demo-en.md', '**/theme-en.md', + 'tiny-vue/packages/**/*.md', 'tiny-engine/packages/**/*.md', + 'tiny-robot/packages/**/*.md', 'next-sdk/packages/**/*.md', + 'genui-sdk/packages/**/*.md', + ], base: process.env.VITEPRESS_BASE || '/', head: [['link', { rel: 'icon', href: '/images/logo-mini.svg' }]], vite: { @@ -75,6 +76,7 @@ export default defineConfig({ 'tiny-robot/docs/src/:section(guide|components|tools)/:path*': 'tiny-robot/guide/:path*', 'tiny-robot/docs/src/examples/:path*': 'tiny-robot/examples/:path*', 'next-sdk/docs/:path*': 'next-sdk/:path*', + 'genui-sdk/docs/src/:section(guide|api)/:path*': 'genui-sdk/guide/:path*', 'tiny-vue/examples/sites/demos/pc/webdoc/:path*': 'tiny-vue/guide/:path*', 'tiny-engine/docs/:section(getting-started|basic-features|advanced-features|tutorials)/:path*': 'tiny-engine/guide/:path*', @@ -213,6 +215,22 @@ export default defineConfig({ ] } ], + '/genui-sdk/guide/': [ + { + text: '介绍', + base: '/genui-sdk/guide/', + items: [ + { text: '安装指南', link: 'installation' }, + ] + }, + { + text: 'API文档', + base: '/genui-sdk/guide/', + items: [ + { text: 'API文档', link: 'api' } + ] + } + ], '/tiny-vue/guide/': [ { text: '指南', diff --git a/.vitepress/theme/Layout.vue b/.vitepress/theme/Layout.vue index 168f111..680d0b5 100644 --- a/.vitepress/theme/Layout.vue +++ b/.vitepress/theme/Layout.vue @@ -85,6 +85,10 @@ const redirectMap = [ { patterns: ['/next-sdk.html', '/next-sdk/'], target: '/next-sdk/guide' + }, + { + patterns: ['/genui-sdk.html', '/genui-sdk/', '/genui-sdk/guide.html', '/genui-sdk/guide/'], + target: '/genui-sdk/guide/installation' } ]; diff --git a/.vitepress/theme/components/CustomHeader.vue b/.vitepress/theme/components/CustomHeader.vue index bfea04b..2c82012 100644 --- a/.vitepress/theme/components/CustomHeader.vue +++ b/.vitepress/theme/components/CustomHeader.vue @@ -23,7 +23,7 @@ :tabs="productTabs" :activeTab="activeProductTab" @tab-change="handleProductTabChange" - style="width: 500px" + style="width: 610px" /> @@ -286,6 +286,7 @@ import { computed, ref, onMounted, watch } from "vue"; import { useData, useRoute, useRouter } from "vitepress"; import TabNavigation from "./TabNavigation.vue"; import { normalizeLink, isActiveRoute, isHomePage } from "../utils/router"; +import { NavTabFactory } from "../entity"; // 获取 VitePress 数据 const { site, theme } = useData(); @@ -312,37 +313,8 @@ const getModalTabClasses = (tab: TabItem) => ({ // 转换导航配置为TabNavigation所需格式 const navigationTabs = computed(() => { - if ( - activeProductTab.value === "next-sdk" && - route.path.includes("/next-sdk/") - ) { - return [{ key: "guide", name: "使用文档", link: "/next-sdk/guide/" }]; - } else if ( - activeProductTab.value === "tiny-vue" && - route.path.includes("/tiny-vue/") - ) { - return [{ key: "guide", name: "使用文档", link: "/tiny-vue/guide/introduce" }]; - } else { - let configNav: configNavItem[] = []; - if ( - activeProductTab.value === "tiny-engine" && - route.path.includes("/tiny-engine/") - ) { - configNav = themeConfig.value.engineNav || []; - } else { - configNav = themeConfig.value.nav || []; - } - return ( - configNav?.map((item: configNavItem) => ({ - key: - getConfigKey(item.link) || - item.text.toLowerCase().replace(/\s+/g, "-"), - name: item.text, - link: `${prefix}${item.link.slice(1)}`, - disabled: false, - })) || [] - ); - } + const navTab = NavTabFactory(activeProductTab.value, route, site, themeConfig.value); + return navTab.getTabs(); }); interface TabItem { @@ -459,6 +431,14 @@ const productTabs = computed(() => [ activeProductTab.value === "next-sdk" ? "active" : "normal" }-next-sdk.svg`, }, + { + key: "genui-sdk", + name: "GenUI-SDK", + link: `${prefix}genui-sdk/guide/installation`, + src: `${prefix}images/logo-${ + activeProductTab.value === "genui-sdk" ? "active" : "normal" + }-genui-sdk.svg`, + }, { key: "tiny-robot", name: "TinyRobot", @@ -502,6 +482,8 @@ watch( const path = route.path.replace(new RegExp(`^${site.value.base}`), "/"); if (path.includes("/next-sdk/")) { activeProductTab.value = "next-sdk"; + } else if (path.includes("/genui-sdk/")) { + activeProductTab.value = "genui-sdk"; } else if (path.includes("/tiny-robot/")) { activeProductTab.value = "tiny-robot"; } else if (path.includes("/tiny-vue/")) { @@ -753,6 +735,7 @@ watch( align-items: center; padding-left: 3rem; padding-right: 3rem; + width: fit-content; } /* 响应式设计 */ diff --git a/.vitepress/theme/entity/index.ts b/.vitepress/theme/entity/index.ts new file mode 100644 index 0000000..ca0f42b --- /dev/null +++ b/.vitepress/theme/entity/index.ts @@ -0,0 +1 @@ +export * from "./nav-tab"; \ No newline at end of file diff --git a/.vitepress/theme/entity/nav-tab.ts b/.vitepress/theme/entity/nav-tab.ts new file mode 100644 index 0000000..c691d0d --- /dev/null +++ b/.vitepress/theme/entity/nav-tab.ts @@ -0,0 +1,127 @@ +interface TabItem { + key: string + name: string + link: string +} + +interface configNavItem { + text: string + link: string + activeMatch?: string +} + + +type NavTabConstructor = new ( + activeProductTab: string, + site: any, + themeConfig?: any +) => NavTab + +class NavTab { + activeProductTab: string + site: any + themeConfig?: any + + constructor(activeProductTab: string, site: any, themeConfig?: any) { + this.activeProductTab = activeProductTab + this.site = site + this.themeConfig = themeConfig + } + + getConfigKey(link: any) { + return link.replace(/\/$/, '').split('/')[2] + } + + getTabs(): TabItem[] { + const prefix = this.site.value.base || '/' + const { nav } = this.themeConfig + + return ( + (nav || []).map((item: configNavItem) => ({ + key: this.getConfigKey(item.link) || item.text.toLowerCase().replace(/\s+/g, '-'), + name: item.text, + link: `${prefix}${item.link.slice(1)}`, + disabled: false + })) || [] + ) + } +} + +class NextSdkNavTab extends NavTab { + constructor(activeProductTab: string, site: any, themeConfig?: any) { + super(activeProductTab, site, themeConfig) + } + + getTabs(): TabItem[] { + return [{ key: 'guide', name: '使用文档', link: '/next-sdk/guide/' }] + } +} + +class TinyVueNavTab extends NavTab { + constructor(activeProductTab: string, site: any, themeConfig?: any) { + super(activeProductTab, site, themeConfig) + } + + getTabs(): TabItem[] { + return [{ key: 'guide', name: '使用文档', link: '/tiny-vue/guide/introduce' }] + } +} + +class TinyEngineNavTab extends NavTab { + constructor(activeProductTab: string, site: any, themeConfig?: any) { + super(activeProductTab, site, themeConfig) + } + + getTabs(): TabItem[] { + const prefix = this.site.value.base || '/' + const { engineNav } = this.themeConfig + + return ( + (engineNav || []).map((item: configNavItem) => ({ + key: this.getConfigKey(item.link) || item.text.toLowerCase().replace(/\s+/g, '-'), + name: item.text, + link: `${prefix}${item.link.slice(1)}`, + disabled: false + })) || [] + ) + } +} + +class GenuiSdkNavTab extends NavTab { + constructor(activeProductTab: string, site: any, themeConfig?: any) { + super(activeProductTab, site, themeConfig) + } + + getTabs(): TabItem[] { + return [{ key: 'guide', name: '使用文档', link: '/genui-sdk/guide/installation' }] + } +} + +const navTabClassMap: Record = { + 'next-sdk': NextSdkNavTab, + 'tiny-vue': TinyVueNavTab, + 'tiny-engine': TinyEngineNavTab, + 'genui-sdk': GenuiSdkNavTab, +} + +const navPathMap: Record = { + 'next-sdk': '/next-sdk/', + 'tiny-vue': '/tiny-vue/', + 'tiny-engine': '/tiny-engine/', + 'genui-sdk': '/genui-sdk/', +} + +const NavTabFactory = (activeProductTab: string, route: any, site: any, themeConfig: any) => { + const isCurrentPath = (path: string) => route.path.includes(path) + + const navPath = navPathMap[activeProductTab] + const NavTabClass = navTabClassMap[activeProductTab] + + if (NavTabClass && isCurrentPath(navPath)) { + return new NavTabClass(activeProductTab, site, themeConfig) + } + + return new NavTab(activeProductTab, site, themeConfig) +} + +export { NavTabFactory } diff --git a/genui-sdk b/genui-sdk new file mode 160000 index 0000000..faa7786 --- /dev/null +++ b/genui-sdk @@ -0,0 +1 @@ +Subproject commit faa77860e8fe926cb6e1d4f2f6d6022209224481 diff --git a/public/images/logo-active-genui-sdk.svg b/public/images/logo-active-genui-sdk.svg new file mode 100644 index 0000000..9cdd5d2 --- /dev/null +++ b/public/images/logo-active-genui-sdk.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/logo-normal-genui-sdk.svg b/public/images/logo-normal-genui-sdk.svg new file mode 100644 index 0000000..bdf702a --- /dev/null +++ b/public/images/logo-normal-genui-sdk.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +